Commit Graph
342 Commits
Author SHA1 Message Date
phranck dc34b0fa55 Refactor: Line-level diff rendering — only write changed terminal lines
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
2026-02-02 21:33:43 +01:00
phranck 45222115cd Merge pull request #61 from phranck/feat/spinner-view
Feat: Add Spinner view with dots, line, and bouncing styles
2026-02-02 20:59:59 +01:00
phranck 1c3057df54 Fix: Align last trail opacity with inactive dot opacity (0.15) 2026-02-02 20:59:34 +01:00
phranck 020afa75b0 Refactor: Use uniform dot character (●) for bouncing spinner with 6-step fade trail
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.
2026-02-02 20:53:12 +01:00
phranck 2cdb0dd3db Refactor: Simplify Spinner API — remove speed, track width, and trail length enums
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.
2026-02-02 20:25:38 +01:00
phranck c6ee32f02d Fix: Replace CFAbsoluteTimeGetCurrent with Date for CI portability 2026-02-02 20:10:12 +01:00
phranck dec8c7c29f Feat: Add Spinner view with dots, line, and bouncing styles
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.
2026-02-02 19:57:58 +01:00
phranck 40e78ebdf3 Chore: Update test count badge to 424 tests [skip ci] 2026-02-02 17:46:21 +00:00
phranck 62e83598f9 Merge pull request #60 from phranck/refactor/state-storage-identity
Feat: Structural identity for @State
2026-02-02 18:44:27 +01:00
phranck bb6b23f0da Fix: Rename @State var count to countValue to avoid SwiftLint empty_count false positive 2026-02-02 18:36:59 +01:00
phranck 50825d61da Feat: Structural identity for @State — persistent state across view reconstruction
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
2026-02-02 18:31:53 +01:00
phranck e2f654423a Fix: Cache scene in RenderLoop to preserve @State across render passes 2026-02-02 17:41:58 +01:00
phranck 6b6d4e8945 Merge pull request #59 from phranck/refactor/eliminate-appstate-singleton
Refactor: Eliminate AppState singleton via RenderNotifier and constructor injection
2026-02-02 17:37:58 +01:00
phranck 1877ae65e6 Refactor: Eliminate AppState singleton via RenderNotifier and constructor injection 2026-02-02 17:31:52 +01:00
phranck 45e00e43db Merge pull request #58 from phranck/fix/flaky-state-test
Fix: Isolate AppState tests to prevent race condition on Linux
2026-02-02 17:12:04 +01:00
phranck baa7912874 Fix: Isolate AppState tests to prevent race condition on Linux 2026-02-02 17:09:58 +01:00
phranck 7884949a17 Merge pull request #57 from phranck/chore/weak-test-rewrites
Test: Strengthen weak ANSI assertions with specific escape codes
2026-02-02 17:01:04 +01:00
phranck 52fe174142 Test: Strengthen weak ANSI assertions with specific escape codes 2026-02-02 16:58:19 +01:00
phranck 88f632b8ed Chore: Use personal git identity for CI badge commits 2026-02-02 16:57:23 +01:00
phranck 63b6513046 Fix: Include workflow_dispatch in update-badge trigger condition 2026-02-02 16:36:28 +01:00
phranck b1983e57f3 Feat: Auto-update test count badge in README via CI
- 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
2026-02-02 16:34:22 +01:00
phranck 9f5f24d02c Feat: CRT power-on/off raster animations + reduce initial cursor delay
- Feat: CRT shutdown animation — vertical collapse → horizontal collapse → phosphor afterglow dot
- Feat: CRT boot animation — horizontal line expands vertically (deflection warm-up)
- Fix: No content flash during zoom-back (powered=false before shutdownPhase clears)
- Fix: Edge glow only visible when powered on
- Chore: Reduce initial cursor delay from 18s to 1.5s (BIOS appears almost immediately)
- Chore: Terminal script tweaks (login name, drive detection, grade spacing)
2026-02-02 16:18:17 +01:00
phranck bdb6d0c3f9 Chore: Fourth-pass optimization — bug fix, auto test counts, ESLint clean, constant extraction
- Fix: parseTerminalFormatting used undefined 'key' instead of 'partKey' (TS error)
- Fix: ESLint setState-in-effect errors (4→0) via cleanup patterns and hydration-sync
- Feat: Auto test/suite counts from Swift project at build-time (prebuild script + env vars)
- Refactor: ArchHighlight sub-component eliminates 4× duplicated pattern in page.tsx
- Refactor: BTN_PRIMARY/BTN_SECONDARY constants replace duplicated button classes
- Refactor: CRT geometry object centralizes repeated calc() layer positions
- Refactor: 13 magic numbers → named constants in TerminalScreen (timings, delays)
- Refactor: Icon component extended with custom SVG support (copy icon)
- Refactor: cloudGradient() helper replaces 6× duplicated radial-gradient strings
- Refactor: HIGHLIGHT constants object for syntax highlighter colors
- Refactor: text-glow CSS utility replaces duplicated textShadow inline styles
2026-02-02 15:38:33 +01:00
phranck 85dd84aee8 Chore: Third-pass cleanup — timer safety, dead assets, ref hygiene
- 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
2026-02-02 15:10:59 +01:00
phranck 67ed7e25e1 Fix: Preload power-on beep for instant first-click playback 2026-02-02 14:53:54 +01:00
phranck d549869645 Refactor: Deep optimization pass across landing page codebase
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
2026-02-02 14:21:03 +01:00
phranck aecc111f3c Feat: Nav icons, theme switcher hydration fix, and pulse animation
- 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)
2026-02-02 13:48:42 +01:00
phranck 414555db6a Fix: Resolve HTML tag rendering bugs in CRT terminal
- 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 ***
2026-02-02 12:53:43 +01:00
phranck 4f3578be91 Feat: Add Markdown formatting support to terminal script parser
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.
2026-02-02 11:36:05 +01:00
phranck 617586c1f2 Feat: Preload terminal audio files for instant playback
- 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
2026-02-02 11:24:20 +01:00
phranck 2f44f45c3c chore: Trigger CI workflow 2026-02-04 00:40:55 +01:00
phranck b38fc7064c Fix: Resolve all ESLint errors and optimize TypeScript
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
2026-02-02 11:17:06 +01:00
phranck 33f993e834 Fix: Remove readonly constraint from terminal data
- 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
2026-02-02 11:12:03 +01:00
phranck fbfbdc3420 Feat: Add terminal script parser and markdown-based terminal content
- 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
2026-02-02 11:08:12 +01:00
phranck 6ae898c2c2 Feat: Add complete WarGames terminal animation with hard drive sounds
- Install Howler.js for gapless audio looping
- Add authentic hard drive sounds (boot, spin, seek, power-on/off)
- Implement power-on beep (0.3 volume)
- Boot sound: 20.18s with seamless transition to spin loop
- Spin sound: gapless loop with Howler.js (0.6 volume)
- Random seek sounds every 15-25s with 30% double-seek chance (0.4 volume)
- Add school computer grade change scene (WarGames film authentic)
  - Login as DABNEY/PENCIL
  - Change David's and Jennifer's Biology grades from F/C to A
  - Inline input format (prompt and input on same line)
  - Grade reports displayed as table
- Add complete Joshua/WOPR dialogue sequence
- Sequence flow: Boot → 12s UNIX → School → 12s UNIX → Joshua → loop
- Reduce CRT scanline opacity for subtler effect
- Add 6-second fade-in for entire terminal output
- Create terminal-script.md documentation with full script
2026-02-02 10:33:52 +01:00
phranck 8cf54ca6a8 Feat: Implement CRT cathode ray scanline effect
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.
2026-02-02 01:16:26 +01:00
phranck 6069dbf46c Fix: Make CRT scanline more visible and ensure 30s animation
- Increased scanline height from 3px to 8px
- Set animation directly in style instead of via Tailwind class
  to ensure 30s duration is applied correctly
2026-02-02 00:42:16 +01:00
phranck f8c5ea3b6e Fix: Remove duplicate crt-scanline keyframes (old fast version)
The old 'background-position' animation was still present and
overriding the new slower 'translateY' version. Now only the
30s animation remains active.
2026-02-02 00:41:13 +01:00
phranck 8193335039 Fix: Slow down CRT scanline animation from 8s to 30s 2026-02-02 00:39:30 +01:00
phranck 3cbcce9b88 Feat: Add CRT monitor visual effects to terminal
Inspired by cool-retro-term, implements vintage CRT display effects:

**New Component: CRTEffects.tsx**
- Scanlines (horizontal raster lines)
- Screen curvature simulation (vignette)
- Glow/bloom effect
- Subtle flicker animation
- Animated noise overlay (Canvas API)
- Moving scanline sweep

**CSS Animations (globals.css)**
- @keyframes crt-flicker: Subtle brightness variation (0.15s loop)
- @keyframes crt-scanline: Horizontal line sweep (8s loop)

**Integration (HeroTerminal.tsx)**
- CRT effects only active when terminal is powered+zoomed
- Optimized parameters:
  - scanlineIntensity: 0.12
  - curvature: 0.08
  - glowIntensity: 0.25
  - flickerIntensity: 0.02
  - noiseIntensity: 0.04

Effects layer at z-index 200 with mix-blend-mode screen.
All effects are pointer-events-none to preserve interactivity.
2026-02-02 00:37:35 +01:00
phranck e7d68fd4cc Remove: CRT power-on sound completely
- 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.
2026-02-02 00:33:59 +01:00
phranck ae552adfce Fix: CRT power-on sound blocked by browser autoplay policy
- 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
2026-02-02 00:28:01 +01:00
phranck 9b0a82ab15 Merge pull request #56 from phranck/feat/landing-page-nunito-font
Replace SN Pro with Nunito font on landing page
2026-02-02 00:23:13 +01:00
phranck dc2f0992f4 Feat: Replace SN Pro with Nunito font on landing page
- 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
2026-02-01 22:15:03 +01:00
phranck f4141d0ecf Test: Strengthen 17 weak test assertions
Replace vague assertions with concrete, specific checks:

**Pattern 1: !buffer.isEmpty → Specific content checks**
- ViewTests.swift (1): Check bold ANSI code + content
- StatusBarTests.swift (4): Check item labels present
- ModifierTests.swift (1): Check border characters
- ButtonTests.swift (3): Check height + border characters
- ComponentViewTests.swift (4): Check titles + content
- ContainerViewTests.swift (1): Check height + content
- FrameBufferTests.swift (2): Check overlay composition

**Pattern 2: Weak contains() → Exact ANSI codes**
- ANSIRendererFullTests.swift (1): contains("1") → contains("\u{1B}[1m")

**Summary:**
- 17 tests strengthened across 8 files
- All tests now assert observable behavior
- No property readback, no empty checks
- 412 tests passing

Addresses to-dos.md line 34: "Weak Test Rewrites"
2026-02-01 22:00:22 +01:00
phranck b8918ed6b8 Merge pull request #55 from phranck/test/audit-cleanup
Test: Remove 78 worthless tests
2026-02-01 04:25:40 +01:00
phranck 0e7dd858f8 Docs: Update test count badge and references (498/181 → 412) 2026-02-01 04:22:44 +01:00
phranck 32f06bcd98 Test: Remove 78 worthless tests (property readback, tautologies, redundancies) 2026-02-01 04:20:46 +01:00
phranck 07ad15320c Merge pull request #54 from phranck/feat/seo-improvements
Feat: Comprehensive SEO improvements for landing page
2026-02-01 04:04:49 +01:00
phranck 42b0597075 Feat: Comprehensive SEO improvements for landing page
- 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
2026-02-01 04:00:02 +01:00