feat(desk98): Windows 98 desktop compositor demo on the gpui backend - #302
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
The desktop (apps/desk98)
app.vueis the compositor (<script setup>), the chrome and programs are 17 SFC components — keyedv-forwindows (block reuse, no teardown), per-window state in vue refs (state.ts),pocket.jsondeclaresframework: "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.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-onlytranslateX/Y, raises ridezIndex— a drag never relayouts.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.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/ctlmodifiers, 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 reachesreplace_text_in_range— andon_key_down'skey_charemission delivered every keypress twice. Plain typing is now single-sourced through the input handler (the note dialect shared the latent double path; scripted--typebypassed 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.tssynthesizes one — the apostrophe's ink mirrored around its own bounds stays on the pixel grid and reads as the leaning mark.Verified
tests/desk98-sim.test.ts): mock svc ops installed before eval drive the whole dialect headless — typing (chper 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.--key cmd+cround-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 checkclean over the 24-module app graph; host + gpui crates clippy/fmt clean.🤖 Generated with Claude Code