Skip to content

feat(desk98): Windows 98 desktop compositor demo on the gpui backend - #302

Merged
doodlewind merged 2 commits into
mainfrom
desk98
Aug 20, 2026
Merged

feat(desk98): Windows 98 desktop compositor demo on the gpui backend#302
doodlewind merged 2 commits into
mainfrom
desk98

Conversation

@doodlewind

@doodlewind doodlewind commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

A complete Windows 98 desktop as one PocketJS guest on the gpui macOS host, styled after sheru's win98 theme — business logic written in Vue Vapor SFCs over the same DrawList contract the consoles boot.

desktop

bun run macos desk98

The desktop (apps/desk98)

  • Vue Vapor throughout: app.vue is the compositor (<script setup>), the chrome and programs are 17 SFC components — keyed v-for windows (block reuse, no teardown), per-window state in vue refs (state.ts), pocket.json declares framework: "vue-vapor" so the manifest build picks it up everywhere. The pure modules (wm.ts, mines.ts, notepad.ts) are framework-free and unit-tested.
  • A real compositor in the guest: the app owns all input — it hit-tests the raw svc pointer stream against its own window geometry (wm.ts). Drag by caption, resize on any edge/corner, double-click caption to maximize, caption buttons (now flush, no close gap), focus follows raises, minimize to the taskbar. Window moves ride paint-only translateX/Y, raises ride zIndex — a drag never relayouts.
  • Notepad selection: anchor+caret model in the Doc (notepad.ts), drag-select, double-click word select, shift+click/shift+arrows extend, navy-segment rendering, and clipboard round-trips — ⌘C/⌘X/⌘V/⌘A, an Edit menu and a right-click context menu (Cut/Copy grayed without a selection). Menus build their items at open, so disabled states track live state.
  • macOS-style shortcuts (the Win98 chords are gone): ⌘W close, ⌘M minimize, ⌘` cycle windows, ⌘N new Notepad, ⌘Esc Start menu, Esc dismiss, F2 (Minesweeper), F5 (time/date). ⌘Q quits the host, ⌘V pastes host-side.
  • Chrome: the Start button carries the PocketJS favicon mark (solid-color pixel art), the Start-menu banner is a plain gradient (wordmark removed), Programs/Documents flyouts, desktop icons, context menus, taskbar buttons + live clock.

The desk companion (hosts/macos)

The note dialect's input lines, extended when the plan's companions name desk: right-button mouse lines (b:2), alt/ctl modifiers, F1–F12, cmd-flagged ⌘ chord lines (⌘Q/⌘V stay host-side), a boot epoch in the hello, {t:"cursor"} pointer-shape intents, and a {t:"paste-req"} guest intent the host answers with a paste line (menu-driven Paste). Scripted acceptance: --mouse X,Y[,d|u|r]@TICK, --key [cmd+][alt+][ctl+]NAME@TICK, --type.

Typing double-delivery fixed: with an IME input handler registered, gpui hands the unhandled key event to the input context, so insertText: always reaches replace_text_in_range — and on_key_down's key_char emission delivered every keypress twice. Plain typing is now single-sourced through the input handler (the note dialect shared the latent double path; scripted --type bypassed it, which is why it never showed in acceptance).

W95FA, baked per-app (slots 19–21)

Unchanged pipeline (grid snap → density-2 AA exactly 0.00%, nonzero-winding fill, GDI-smear bold) plus one addition: the font ships no backtick, so gen-assets.ts synthesizes one — the apostrophe's ink mirrored around its own bounds stays on the pixel grid and reads as the leaning mark.

Verified

  • Desk-companion journey in the sim (tests/desk98-sim.test.ts): mock svc ops installed before eval drive the whole dialect headless — typing (ch per keypress), double-click word select then ⌘C (copy intent payload asserted: "HiWelcome"), drag selection (prefix payload + the selected run splits into its own tree node), right-click context menu → Paste → paste-req on the wire → paste line lands, ⌘Esc/⌘N/⌘W, the epoch clock.
  • Scripted acceptance on the live host: drag-select then --key cmd+c round-tripped the selected text into the real system clipboard.
  • bun run test: 12/12 stages green (new desk98 sim stage builds the vue-vapor bundle; pure wm/mines/notepad+selection tests stay in the unit stage); pocket check clean over the 24-module app graph; host + gpui crates clippy/fmt clean.
  • Screenshot above is the portable pipeline's byte-deterministic render of the same DrawList at density 2 (the gpui host paints the identical list).

🤖 Generated with Claude Code

doodlewind and others added 2 commits August 19, 2026 13:39
PocketJS 98 (apps/desk98): a full Win98 desktop as one PocketJS guest on
the macos-app target — draggable/resizable/z-ordered windows, taskbar,
Start menu with flyouts, desktop icons, right-click context menus,
Notepad (typing/IME/caret/scroll), a playable Minesweeper, folder views,
About and Shut Down dialogs, and the classic shortcuts (Alt+Tab, Alt+F4,
Ctrl+Esc, F2/F5, double-click-to-maximize). Styling follows sheru's win98
theme through the engine's bevel rings; window moves ride paint-only
translate props and raises ride zIndex, so drags never relayout and the
idle desktop keeps the demand-render governor at a few frames a second.

The host grows the DESK companion dialect (input forwarding when the
plan's companions name "desk"): right-button mouse lines (b:2), alt/ctl
key modifiers, F1..F12, a boot epoch in the hello for the taskbar clock,
a {t:"cursor"} pointer-shape intent, and script flags for acceptance runs
(--mouse drags/right-clicks, --key chords). Also fixes --press: script
buttons were composed but never OR'd into guest.frame().

W95FA (OFL, vendored) bakes per-app into slots 19-21 via pak.json +
gen-assets.ts, grid-snapped so density-2 coverage is exactly bi-level
(AA 0.00%); the repo slot table 0-18 never moves. bake-font's scanline
fill switches from even-odd pairing to nonzero winding — the TrueType/CFF
rule — because W95FA's edge-sharing stroke rectangles legitimately cancel
under even-odd ('h' baked identical to 'n'); ASCII Inter/JetBrains bakes
are byte-identical across the change, and tests/font-bake.test.ts pins
the regression.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…icon start logo

- Business logic ported from Solid JSX to Vue Vapor SFCs: keyed v-for
  windows, per-window vue refs (state.ts), pure modules unchanged; the
  manifest declares framework vue-vapor so bun run macos desk98 builds it.
- Host fix: plain typing is single-sourced through the IME input handler —
  gpui hands the unhandled key event to the input context, so insertText
  always reaches replace_text_in_range and the on_key_down key_char
  emission delivered every keypress twice (the note dialect shared the
  latent double path).
- Desk dialect: cmd-flagged ⌘ chord key lines (⌘Q quits, ⌘V pastes
  host-side), {t:"paste-req"} guest intent answered with a paste line,
  --key cmd+ scripting.
- macOS-style shortcuts replace the Win98 chords: ⌘W close, ⌘M minimize,
  ⌘` cycle, ⌘N new Notepad, ⌘Esc Start menu, ⌘A/C/X in Notepad.
- Notepad selection: anchor+caret Doc model (notepad.ts, unit-tested),
  drag/double-click-word/shift extend, navy segment rendering, Cut/Copy/
  Paste/Select All in the Edit menu and a right-click context menu over the
  system clipboard.
- Chrome: caption buttons flush (close gap removed), Start button carries
  the PocketJS favicon mark (solid color), Start-menu banner wordmark
  removed.
- W95FA: synthetic backtick (the font ships none — the apostrophe mirrored
  around its ink bounds stays on the pixel grid).
- Tests: desk companion journey in the sim (mock svc ops asserting copy/
  paste-req payloads on the wire), desk98 sim stage in tools/test.ts and
  the macos CI lane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@doodlewind
doodlewind marked this pull request as ready for review August 20, 2026 07:21
@doodlewind
doodlewind merged commit 8188d16 into main Aug 20, 2026
1 check passed
@doodlewind
doodlewind deleted the desk98 branch August 20, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant