Skip to content

feat(desktop): add windows-app stock target - #419

Open
jnhu76 wants to merge 4 commits into
pocket-stack:mainfrom
jnhu76:feat/windows-desktop-parity
Open

feat(desktop): add windows-app stock target#419
jnhu76 wants to merge 4 commits into
pocket-stack:mainfrom
jnhu76:feat/windows-desktop-parity

Conversation

@jnhu76

@jnhu76 jnhu76 commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR completes Windows stock-target admission for the existing portable hosts/desktop host by adding windows-app alongside macos-app and linux-app.

It does not add a second Windows desktop architecture, renderer, guest ABI, framework contract, or Windows-specific UI stack.

windows-app uses the existing portable desktop path:

PocketJS guest
    ↓
QuickJS / runtime worker
    ↓
PocketJS core / DrawList
    ↓
hosts/desktop
    ├─ winit       window + input
    ├─ wgpu        rendering / presentation
    ├─ OffloadWorker
    └─ pocket-text

The scope rule used throughout this change is:

A capability that PocketJS advertises on windows-app must have its required execution closure working truthfully on Windows. Unrelated device, site, and development tooling does not automatically become Windows-supported merely because a Windows stock target exists.

This is therefore a target-admission and portable-host parity change, not a repository-wide Windows portability campaign.


What this adds

windows-app stock target

contracts/spec/platforms.ts now registers windows-app with:

  • hostAbi: 4
  • platform: "windows"
  • form: "window"
  • physical viewport: 1440 × 960
  • default logical viewport: 720 × 480
  • dynamic logical viewport: 240 × 180 … 4096 × 4096
  • fixed viewports accepted
  • rasterDensity: 2

Advertised capabilities:

  • input.buttons
  • display.viewport.live
  • text.glyphs.baked
  • io.offload
  • text.layout.offload

System-UI role:

  • ui.compositor-surfaces

The profile follows the existing portable desktop contract rather than introducing Windows-specific semantics.


Portable desktop host admission

hosts/desktop/src/plan.rs now admits Windows as the third stock target of the same host family:

macOS   → macos-app
Linux   → linux-app
Windows → windows-app

The existing host-plan identity check remains authoritative. A plan whose target id or ABI does not match the compiled host identity is rejected.

No Windows-specific renderer, guest runtime, service protocol, or alternate host architecture is introduced.


Why hostAbi: 4 is correct

Windows uses the same host ABI generation already consumed by the portable macOS/Linux desktop host.

This PR adds no:

  • guest operations;
  • DrawList commands;
  • service wire formats;
  • manifest semantics;
  • Windows-only native APIs exposed to the guest.

The change is host admission, not protocol expansion.


Capability closure

The Windows profile does not merely copy capability names. Each advertised capability was traced through its implementation path:

Capability Windows execution path
input.buttons winit keyboard event → existing desktop button mapping → guest input
display.viewport.live winit resize → live scale/viewport calculation → guest relayout
text.glyphs.baked existing pocket-text path → desktop DrawList rendering
io.offload hosts/desktoppocket-ui-surface::OffloadWorker
text.layout.offload desktop text_workerOffloadWorkerpocket-text::Engine
ui.compositor-surfaces existing desktop supervisor / surface composition path under the System-UI role

There is no separate Windows implementation of these semantics.


Why text-offload is built in Windows CI

windows-app advertises both io.offload and text.layout.offload.

A registry entry alone would not prove those capabilities participate in a real Windows build. The Windows workflow therefore resolves and builds apps/text-offload against windows-app; that app explicitly requires both capabilities.

A validated Windows plan contains:

target:              windows-app
hostAbi:             4
rasterDensity:       2
io.offload:          true
text.layout.offload: true

The ordinary note guest is also resolved and built against the same target.


Stock target vs pocket build backend

windows-app becoming a stock target does not mean this PR adds a pocket build target backend.

Today tools/pocket.ts has backend dispatch entries for psp, vita, and pocketbook. Desktop targets are not uniformly dispatched through that table: macos-app has its own desktop tooling, while linux-app, windows-app, and web-app currently have no pocket build backend entry.

So this PR establishes:

  • stock-target registration and resolution;
  • truthful capability admission;
  • production guest compilation through tools/build.ts;
  • the portable native desktop host on Windows;
  • Windows CI for that execution closure.

It intentionally does not add a new tools/pocket.ts backend for Windows. The existing documentation in site/content/docs/platform-contracts.md calls this split out explicitly.

This boundary matters because stock target admission and CLI backend dispatch are separate concerns in the current repository.


Windows CI

This PR adds .github/workflows/desktop-windows.yml on windows-latest.

It validates:

  • stock-target registry admission;
  • relevant System/service contracts;
  • note resolution and compilation against windows-app;
  • text-offload resolution and compilation against windows-app;
  • hosts/desktop fmt, clippy, and unit tests;
  • pocket-ui-surface tests;
  • pocket-text tests;
  • release build of the Windows desktop host.

Workflow permissions are limited to:

permissions:
  contents: read

CI trigger boundary

The workflow path filters intentionally cover the code it actually consumes, including target/contracts, manifest/compiler code, tools/build.ts, the two guest fixtures, desktop/native crates, workspace manifests/locks, and the workflow itself.

This avoids a misleading state where Windows CI exists but a change to the Windows build/compiler closure does not trigger it.

The workflow still does not claim that unrelated PSP/Vita/Symbian/site tooling works on Windows.


Native Windows acceptance

The production windows-app path was exercised on real Windows hardware.

Environment:

Windows 11 Pro 10.0.26200.9445 x64
rustc 1.98.1 (x86_64-pc-windows-msvc)
Bun 1.2.8
AMD Radeon integrated GPU
wgpu backend: Vulkan
surface format: Bgra8Unorm

Observed using the production desktop host and production note guest:

  • a normal decorated Windows window was created;
  • the production guest loaded successfully;
  • the first frame was presented;
  • real OS pointer input reached the winit event loop;
  • real keyboard input reached the guest and rendered back to pixels;
  • maximizing the real window triggered live resize and guest relayout;
  • native close generated CloseRequested;
  • the process exited cleanly.

The real-GPU ignored desktop test retained_targets_bound_leases_and_survive_resize was also executed locally and passed.

Later commits after physical acceptance changed only CI/workflow policy, not production/runtime/contracts/docs semantics, so that physical acceptance remains applicable to the current implementation.


Why GitHub Windows CI does not run the ignored GPU test

This PR deliberately does not add Windows-specific CI graphics infrastructure such as WARP, Mesa, DXC setup, Agility SDK setup, or a software-GPU acceptance layer merely to run the GPU-dependent ignored test on windows-latest.

The evidence boundary is explicit:

GitHub Windows CI
    → target contracts
    → guest builds
    → native unit tests
    → release host build

physical Windows hardware
    → native window
    → wgpu/Vulkan
    → presentation
    → real input
    → live resize
    → GPU acceptance test

The GitHub workflow does not claim GPU presentation acceptance.


Windows filesystem-path audit

While validating this work, existing repository code using patterns such as:

new URL(..., import.meta.url).pathname

was observed. On Windows, treating a file-URL pathname directly as a filesystem path can produce forms such as /C:/Users/... and fail when passed to native filesystem consumers.

This PR intentionally does not turn that observation into a repository-wide cleanup.

Instead, the execution closure required by windows-app was traced:

manifest resolution
→ build-plan creation
→ tools/build.ts
→ compiler paths consumed by that build
→ guest bundle/package
→ hosts/desktop
→ pocket-ui-surface / text worker
→ native runtime

No blocking URL.pathname → filesystem path defect was found inside that required closure.

In particular:

  • tools/build.ts converts its repository file URL with fileURLToPath(...);
  • manifest resolution does not depend on the problematic conversion;
  • native desktop plan/asset resolution uses Rust filesystem semantics such as canonicalize;
  • the relevant compiler URL.pathname value remains in URL-pathname space for prefix comparison, while Bun.file receives the URL itself rather than that pathname string.

Both note and text-offload successfully resolve and compile as windows-app on Windows through this production path.

Why unrelated path bugs are not fixed here

Other existing pathname assumptions remain in device/site tooling, including historical paths associated with PSP/Symbian launchers and site/build utilities.

The rule used here is:

If a Windows path defect is required by a capability newly advertised by windows-app, it is a blocker for this PR.

If it belongs only to an unrelated device/site/tooling path, it does not enter this PR merely because a Windows stock target now exists.

This is not justified by the weaker argument that “base and branch fail the same way.” A pre-existing defect would still block this PR if windows-app depended on it.


Why some registry tests are scoped on Windows

The complete registry-oriented test files also execute unrelated device/tooling paths. Some currently encounter the pre-existing Windows pathname assumptions described above, while local core.autocrlf=true checkouts also expose byte-exact fixture differences.

Rather than weakening those tests or repairing unrelated tooling, the Windows workflow executes the target-inventory blocks changed by this PR and separately runs the portable services that windows-app actually advertises. Production Windows guest builds provide an additional end-to-end check of the target/compiler boundary.

This PR does not change .gitattributes, repository line-ending policy, unrelated fixtures, or historical device tooling.


CRLF policy

The local Windows validation checkout used core.autocrlf=true, which exposed existing byte-exact fixture differences in some full test files.

No CRLF-only changes were committed. The branch diff remains LF-normalized.

This PR intentionally does not introduce repository-wide line-ending policy as part of Windows target admission.


rasterDensity: 2 is not a claim that Windows always runs at 200% DPI

rasterDensity is a PocketJS target-owned rendering fact.

For the stock Windows profile:

logical:  720 × 480
physical: 1440 × 960
density:  2

This does not mean every Windows monitor has scale factor 2. The desktop host continues to consume the live window scale reported by winit for runtime window/input/resize behavior. Profile raster density and live OS window scale are separate concepts.

This PR does not claim or implement Per-Monitor DPI V2, cross-monitor DPI migration, dynamic density rebaking, or a Windows DPI application-manifest policy.


Scope extraction

The implementation contains only the PocketJS semantics needed for Windows desktop admission:

windows-app target contract
+ desktop HOST_ID admission
+ target contract tests
+ documentation
+ Windows CI

It does not carry image-viewer-specific or experimental image-resource work. It adds no native image-resource APIs, image-specific guest ABI, WIC, large-image semantics, additional DrawList opcodes, or Windows-specific compositor code.


Validation

Physical/runtime validation was performed against production semantics at 4082abcb3a0302553be8f87241c787e1c196fc16.

The current head adds only the CI trigger-boundary corrective; it does not change production/runtime/contracts/docs semantics.

Local closeout results before opening this PR:

platform registry                         4 pass / 0 fail
E7 production-registry exclusion          1 pass / 0 fail
pocket-system + portable service tests   11 pass / 0 fail
hosts/desktop                            5 pass / 0 fail / 1 ignored
pocket-ui-surface                        8 pass / 0 fail
pocket-text                              3 pass / 0 fail
real-GPU ignored desktop test            1 pass / 0 fail
release Windows host build               pass
note@windows-app                          pass
text-offload@windows-app                  pass

The PR remains Draft until the current head's windows-latest workflow completes successfully upstream.


What this PR intentionally does not do

This PR does not:

  • make the complete repository Windows-authorable;
  • add a pocket build Windows backend;
  • repair every historical URL.pathname filesystem assumption;
  • change repository CRLF policy or add .gitattributes;
  • add path linting/helper infrastructure;
  • add a Windows-specific renderer or second desktop host;
  • add a new guest ABI or DrawList opcode;
  • add new Windows dependencies;
  • add image-resource/WIC work;
  • add PerMonitorV2 support;
  • claim PSP/Vita/Symbian/site tooling works from Windows;
  • claim the GitHub Windows runner provides GPU presentation acceptance.

Those are separate changes if and when their own consumers require them.


Review focus

  1. Is windows-app a truthful stock profile for the existing portable desktop host?
  2. Does every capability advertised by that profile have a real Windows execution path?
  3. Is hostAbi: 4 the correct identity for that unchanged portable ABI?
  4. Does the Windows workflow cover the code it actually consumes and test the claimed target closure without implying repository-wide Windows support?
  5. Is the boundary around existing Windows path/tooling defects drawn at the correct execution boundary?
  6. Is the distinction between stock-target admission and pocket build backend dispatch clear and truthful?
  7. Is any Windows-specific mechanism being added where reuse of the existing portable desktop host should remain sufficient?

The updated Windows workflow is green on the current head; this PR is ready for maintainer review.

jnhu76 and others added 4 commits September 12, 2026 21:28
Admit Windows as the third stock target of the portable desktop host:

- contracts/spec/platforms.ts: register windows-app at hostAbi 4,
  platform windows, form window, rasterDensity 2, dynamic viewport
  240x180..4096x4096 with acceptsFixed, and the portable desktop
  capability set (input.buttons, display.viewport.live,
  text.glyphs.baked, io.offload, text.layout.offload; the systemUI
  role adds ui.compositor-surfaces)
- hosts/desktop/src/plan.rs: map target_os = "windows" to the
  windows-app HOST_ID and extend the compile-time platform guard
- tests/platform-contracts.test.ts, tests/symbian-runtime.test.ts:
  include windows-app in the stock-target inventories and pin the
  profile
- .github/workflows/desktop-windows.yml: windows-latest CI that runs
  the target-registry contract tests, resolves and builds the note
  against windows-app, and runs fmt, clippy, tests and the release
  host build. The two registry suites run scoped to the
  target-registry blocks: the unscoped files carry pre-existing
  Windows path defects (file:// URL pathnames concatenated into
  /C:/... paths, CRLF byte-exact fixture compares) that fail on every
  Windows machine; recorded, not repaired here.

Local Windows 11 acceptance on the release host (AMD Radeon iGPU):
winit creates a real window, wgpu initializes Vulkan with a Bgra8Unorm
surface, the note guest reaches the first presented frame, real
pointer and keyboard events enter the native event loop, a maximize
relayouts live, and CloseRequested exits 0.

docs/BACKENDS.md and site/content/docs/platform-contracts.md enumerate
windows-app. The platform-contracts target table now reads its
capability cells from the registry: the macos-app and web-app rows
named text.layout.native and omitted the offload ids.
@jnhu76
jnhu76 marked this pull request as ready for review September 12, 2026 15:23
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