Introduce FrameDiffWriter that stores the previous frame's output and
compares line-by-line on each render. Only lines that actually differ
are written to the terminal, reducing I/O by ~94% for mostly-static UI.
- Extract output line building from WindowGroup.renderScene into
FrameDiffWriter.buildOutputLines() pure function
- WindowGroup.renderScene now returns FrameBuffer instead of writing
directly to terminal
- RenderLoop (now final class) owns FrameDiffWriter for state tracking
- Content and status bar are diffed independently
- SIGWINCH invalidates diff cache for full repaint on resize
- SignalManager gains separate consumeResizeFlag() for resize detection
- 13 new tests for diff computation and output line building
Replace mixed block characters (▇/■) with a single ● for all track
positions. Visual distinction comes purely from opacity fade. Extends
trail to 6 steps for a smoother motion effect.
Radically simplify the Spinner public API from 6 parameters to 3.
Remove SpinnerSpeed, BouncingTrackWidth, and BouncingTrailLength enums.
Hardcode calibrated intervals (dots 110ms, line 140ms, bouncing 100ms),
fixed track width (9), and trail opacities with edge overshoot for
smooth fade-in/fade-out at track edges.
Auto-animating loading indicator with three visual styles:
- Dots (braille rotation), Line (ASCII rotation), Bouncing (Larson scanner
with fade trail). Time-based frame calculation ensures each spinner runs
at its own speed independent of render triggers.
Configurable: SpinnerSpeed (.slow/.regular/.fast), BouncingTrackWidth
(.minimum/.default/.maximum/.fixed(Int)), BouncingTrailLength
(.short/.regular/.long), custom color.
Run loop upgraded from 100ms to 40ms polling (~25 FPS) to support
smooth animations.
Implement SwiftUI-style structural identity so @State values survive
full view-tree reconstruction on every render pass. State is keyed by
ViewIdentity (path-based) and property index in a central StateStorage.
- Add ViewIdentity (path-based structural identity for views)
- Add StateStorage with GC and branch invalidation
- Refactor @State to self-hydrate from StateStorage during init
- Set root hydration context in RenderLoop before app.body evaluation
- Extend RenderContext with identity propagation helpers
- Add ConditionalView branch invalidation on switch
- Remove scene cache from RenderLoop (app.body evaluated fresh per frame)
- Enhance Example App pages with @State (ButtonsPage, OverlaysPage, ContainersPage)
- Update DocC articles (StateManagement, RenderCycle)
- Add 12 tests for state storage identity
- New 'update-badge' job runs after build on main pushes
- Counts @Test and @Suite annotations in Swift test files
- Updates badge, project structure, and developer notes in README.md
- Uses [skip ci] to prevent recursive workflow triggers
- Only commits if counts actually changed
- HeroTerminal: all timers now flow through scheduleTimer/pendingTimersRef,
eliminating seekTimeoutRef/zoomTimerRef/powerOffTimerRef entirely;
poweringOnRef guards against rapid double-click race condition
- TerminalScreen: trim lineRefsRef to ROWS in pushLine to prevent
unbounded stale DOM ref accumulation
- Remove ~1.1MB unused audio duplicates (mp3 copies of m4a files,
unreferenced seek2 in both formats)
- Remove unused favicon-16.png
Performance & memory:
- HeroTerminal: fix setInterval one-time random (now recursive setTimeout),
reuse single Howl for seeks, track all timers via pendingTimersRef
- RainOverlay: cap at 30fps, skip on prefers-reduced-motion
- SpinnerLights: pre-compute boxShadow at spawn, nextId in useRef
- Remove eager audio preloads from <head> (Howler loads lazily)
- CloudBackground: remove unnecessary 'use client' (now Server Component)
Type safety & correctness:
- Import BootStep/SchoolStep/JoshuaStep/TerminalEntry from terminal-parser
instead of duplicating interfaces; typed terminal-data.ts export
- ThemeProvider: isTheme() type guard replaces unsafe 'as Theme' casts,
instanceof guard for event.target, try/catch on localStorage.setItem
- useCopyToClipboard: cancel previous timer on rapid clicks, try/catch
around clipboard API, cleanup on unmount
- TerminalScreen: use config.initialCursorDelay instead of magic 18000,
type predicate for filter(), track glitch reset timer for cleanup
Accessibility (WCAG):
- prefers-reduced-motion: global CSS rule + JS skip in Rain/Spinner/Glitch
- aria-hidden on decorative elements (Rain, Spinner, Clouds)
- focus-visible ring on ThemeSwitcher, CodePreview, PackageBadge buttons
- aria-pressed on active theme button, nav aria-label, main tabIndex
Code quality:
- Extract useCopyToClipboard hook (CodePreview + PackageBadge)
- bg-frosted-glass CSS utility replaces 3x inline color-mix
- hover:bg-white/* → hover:bg-foreground/* for theme consistency
- Remove dead zoomed prop, stale eslint-disable, move lineRefsRef up
- import type for ReactNode in FeatureCard
- Add SF Symbol icons to nav links (book for Docs, </> for GitHub)
- Fix theme button onClick broken by hydration mismatch: start with
null on both server and client, sync via useEffect after mount
- Active theme dot pulses with 3s ease-in-out glow animation
- Stronger glow on active theme indicator (triple-layer box-shadow)
- COLS truncation now counts visible characters only (ignores HTML tags),
preventing broken markup when tags get cut off mid-pair
- typeSystem() types content between paired tags char-by-char while tags
appear instantly as complete open/close pairs
- Empty lines render with non-breaking space for correct row height,
fixing premature scrolling when blank lines consumed ROWS budget
- Stable line keys (monotonic counter) prevent React DOM thrashing
- terminal-parser bold regex uses negative lookahead to skip ***
Parser now supports basic Markdown syntax in all text fields:
- **bold** → <strong> with bold font weight
- __underline__ → <span> with underline decoration
- ~~strikethrough~~ → <span> with line-through
- *italic* → <em> with italic style
Implementation:
- Regex-based parser converts Markdown to HTML-like tags
- TerminalScreen component parses tags and renders as React nodes
- Styling via Tailwind classes (font-bold, underline, etc.)
- Works in all sequences: Boot, School, Joshua, UNIX commands
Examples added to terminal-script.md:
- **BIOS** in boot sequence
- __AUTHORIZED USE ONLY__ message
Build-time processing ensures no runtime overhead.
- Add preload links for all 5 audio files in layout head
- Browser downloads sounds during page load
- Eliminates delay when power button is clicked
- Improves user experience with immediate sound response
Files preloaded:
- power-on.mp3 (beep)
- hard-drive-boot.m4a
- hard-drive-spin.m4a
- hard-drive-seek1.m4a
- hard-drive-power-off.m4a
Lint Fixes:
- Fix setState in useEffect warnings by using lazy initialization and useLayoutEffect
- Remove unused parameters and variables (patterns, zoomed)
- Add eslint-disable comments for legitimate patterns (hydration, cleanup)
- Fix ref mutation warnings in cleanup functions
Code Optimizations:
- Remove dead Patterns interface and regex objects from CodePreview
- Simplify tokenizeLine to use combined regex directly
- Use lazy state initialization in ThemeProvider to avoid SSR mismatch
- Split audio preload and mount state into separate effects
- Use useLayoutEffect for synchronous DOM updates (terminalOpacity reset)
- Improve cleanup by copying ref values before use
- Add proper cancelAnimationFrame cleanup for fade animation
Result: ✓ All lint errors fixed, production build successful
- Remove 'as const' from generated terminal-data.ts
- Add type assertion for INTERACTIONS array to fix TypeScript error
- Fixes production build failure with readonly type mismatch
- Create terminal-parser.ts to parse terminal-script.md
- Add build script (generate-terminal-data.ts) to convert markdown to TypeScript
- Refactor TerminalScreen.tsx to use generated data instead of hardcoded arrays
- Add prebuild script to package.json for automatic data generation
- Install tsx and @types/howler dev dependencies
- Fix parser to preserve spacing in COUNTER prefix (Memory Test: )
- Update terminal-script.md documentation with [INLINE] tag explanation
Now terminal content can be edited in markdown without touching TypeScript:
1. Edit terminal-script.md
2. Run: npx tsx scripts/generate-terminal-data.ts
3. Reload browser - changes are live
Adds realistic CRT monitor scanline sweep effect to terminal:
**Visual Effect:**
- Sharp bottom edge (cathode ray current position)
- Trailing phosphor glow upward (150px trail)
- Gradient: transparent → 0.02 → 0.04 → 0.08 → 0.1
- Sharp edge: 0.08 opacity, no blur
- Trail: blur(3px) for smooth phosphor decay
**Animation:**
- 15s linear infinite
- Moves from top to bottom within terminal area only
- Minimal pause between cycles
- Only active when terminal is powered on
**Implementation:**
- Container (150px) with gradient + sharp 2px bottom edge
- Uses transform: translateY() for smooth animation
- CSS @keyframes crt-scanline-move
- Positioned within CRT glow layer (z-index 3)
Inspired by real CRT electron beam behavior where the
cathode ray sweeps the screen with phosphor persistence
trailing behind.
- Increased scanline height from 3px to 8px
- Set animation directly in style instead of via Tailwind class
to ensure 30s duration is applied correctly
The old 'background-position' animation was still present and
overriding the new slower 'translateY' version. Now only the
30s animation remains active.
- Removed AudioContext setup and management
- Removed playCrtPowerOnSound() function (95 lines)
- Removed audio unlock listeners
- Cleaned up handlePowerOn comment
Audio caused issues with browser autoplay policies and added
unnecessary complexity. Terminal animation works fine without it.
- Make playCrtPowerOnSound() async with proper AudioContext.resume()
- Add AudioContext unlock on first user interaction (click/touchstart)
- Add error handling for audio failures
- Ensures sound works in all browsers with autoplay restrictions
- Remove SN Pro Google Font import from globals.css
- Add Nunito font import with weights 400, 500, 600, 700
- Update CSS variables to use --font-nunito
- Apply Nunito as primary font family for body text
- Keep Geist Mono for monospace elements
- Add robots.txt and sitemap.xml (static files for export mode)
- Add OG image (1200x630) with terminal icon + branding
- Add Twitter Card meta tags (summary_large_image)
- Add canonical URL via metadataBase
- Add JSON-LD structured data (SoftwareSourceCode schema)
- Add <main> landmark and skip-navigation link for accessibility
- Add preconnect hints for Google Fonts, Google Static, and Umami
- Add web app manifest (site.webmanifest)
- Add meta keywords for secondary search engines
- Add aria-hidden on decorative SF Symbol icons