Skip to content

Kitchen Sink release verification gate (#9) - #219

Draft
sethwebster wants to merge 118 commits into
mainfrom
seth/kitchen-sink-gate
Draft

sethwebster wants to merge 118 commits into
mainfrom
seth/kitchen-sink-gate

Conversation

@sethwebster

@sethwebster sethwebster commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Release verification gate for the Spark Kitchen Sink. Tracking: #9.

Status: in progress (draft). Each task lands here through its own reviewed PR into seth/kitchen-sink-gate (code review, merge-result typecheck/test/e2e lint, green CI, and screenshot review for UI changes). This PR collects them for one review into main.

Integrated (117 PRs, oldest first)

In progress

Remaining open work (ready)

Blocked here

Open decisions for Seth

  • Release harness guard requires reports on all 5 macOS arm64 targets, including both reference machines and older-macOS VMs: narrow it or provide the machines/images ([KS] Release harness: install signed artifacts on clean machines/VMs (#55) #281).
  • Should a release require a clean account/machine (environment.clean)?
  • Notarization and a notarized, quarantined driver launch are unverified until run with Seth's Apple credentials.

Decisions

  • Visual verification uses in-app driver capture (no Screen Recording/Accessibility permission needed). Driver mode never shows windows. White-box passes are accepted by the gate and labelled; the black-box AX/CGEvent backend ([KS][INFRA] macOS black-box backend (AX API + CGEvent) and OS-state commands #50) needs Seth's one-time TCC grant and will add per-tag requireBlackBox.
  • build: dev flows live in e2e/dev-flows/<area>/; the gate doesn't run them, coverage counts them.
  • [KS][CTRL] API: native control primitives #91: an explicit variant="default" keeps its portable behavior. [KS][MW] API: multiwindow architecture #178: multiwindow folded into /windows with a shared JS runtime by default; the legend-apps navigator API is unchanged. RTL left/right keep React Native's iOS swap.
  • No agent touches real devices/services (printers, accounts) and no VMs run on the shared Mac. Notarization stays "not verified" until run with Seth's Apple credentials.
  • Driver code and SPARK_SIMULATE_CRASH are compiled out of consumer release builds ([KS][INFRA] Keep the in-app test driver out of consumer release builds #340); spark build fails a release that contains driver strings without the opt-in.
  • Release license notices: first-party/private workspace packages excluded; unlicensed third-party packages listed UNKNOWN with a warning (strict only in the Kitchen Sink audit) so consumer builds don't break.
  • Reference machines: M1 MacBook Air 8 GB (budgets), base 14" MacBook Pro (120 Hz). Windows 10 is out of scope.

How to run

  • Release-build audits: bun run e2e:audit [--artifact <app>] (bundle hygiene); security flows in e2e/flows/security/ (entitlements, code signing, load paths). Accessibility audit flows: e2e/flows/accessibility/.
  • Driver in release builds: only with macos.testDriverInRelease (Kitchen Sink sets it); private CG event synthesis only with macos.testDriverPrivateEvents.
  • bun run gate (fails closed); one screen: bun run ks:capture <area>/<screen> [--appearance dark] [--locale ar].

Visual verification

Gallery added when the gate is complete.

- Command catalog (scripts/e2e/format/commands.ts) is the single source:
  flow/registry/gate JSON Schemas and docs/e2e-flow-commands.md are
  generated from it (bun run e2e:schema); a test fails on drift.
- Parser/validator: ajv over the generated schema, errors mapped to
  line:column; canonical FlowCommand AST incl. nested commands and when.
- Linter: unknown commands, sleeps, missing checks, check ID format,
  build: dev in gate flows, unanchored point selectors.
- CLI: bun run e2e:validate / e2e:lint [paths].
- Driver commands marked, incl. setAppAppearance and in-process
  takeScreenshot/assertScreenshot.
- VS Code yaml.schemas mapping, agent docs, spec examples as fixtures.
… (#220)

- maximumDeltas: signed Sparkle deltas; each release keeps, references and
  reports only its own deltas, stale ones removed from dist/updates
- events carry display version and build; downloading reports delta,
  skipped reports major; no error event after notAvailable
- status: skippedBuild, skippedMajorBuild, lastCheckedAt, persisted prefs
  before start; clearSkippedUpdate; getUpdateAvailability
- intervals under 3600 s rejected instead of clamped by Sparkle
- test:sparkle runs a headless SPUUpdater over signed feeds: delta
  selection/fallback, older-only feed, skip/skip-major/clear
- Kitchen Sink updates screen; UPD check registry; Windows unsupported
  rationale (development-only target)

Refs #148
…s screens (#56) (#221)

* feat(kitchen-sink): app shell with area catalog and spark-ks:// deep links

- Catalog of the 37 release-gate areas; screens/<area>/index.tsx registers
  each area's screens without editing shared files.
- spark-ks://<area>/<screen> routing with a not-found page; scheme in
  desktop.config.json.
- Registry renders every screen inside <area>-<screen>-root.
- App-wide controller (started from index.ts) owns the Document menu,
  Cmd+Shift+K, quit/close unsaved-changes prompts and deep links.
- Shell, menu and prompts localized (en, ar); Arabic lays out RTL.
- Desktop checks testIDs follow <area>-<screen>-<element>.
- build/package scripts match shipping apps; rebuild scripts build the
  custom dev runtime the URL scheme requires.

* feat(kitchen-sink): port legend-apps test-kitchen-sink screens into their areas

- native-controls: sidebar (data items, dynamic heights, React rows), split
  view, glass, SF Symbols, search field
- keyboard: keyboard monitor
- windows: window manager (with /windows/macos blur), window controls
- future-facing-capabilities: AI command and tool runners

Public @legendapp/spark entry points only; shared Panel.tsx. Anchor the
native-project .gitignore entries so screens/windows is not ignored.
- Registry checks declare `covers:` (SDK surface IDs: ./subpath,
  ./subpath#export, ./subpath#getXAvailability(arg).flag); schema
  regenerated from schemas.ts.
- scripts/api-surface.ts derives the SDK surface (package.json exports +
  type checker, availability flags from signatures); the public-surface
  snapshot test now uses it.
- Coverage engine: no-flow, unregistered-check, duplicate-check,
  unknown-cover with file:line, plus uncovered subpaths/exports/flags.
- CLI: bun run e2e:coverage [paths] [--json]; exit 1 until coverage is
  complete. Not wired into bun run test.
- Fixture tests for the engine, surface derivation and CLI; docs.
…facts, reports (#225)

Executor over the parsed FlowAst with implicit waits (5s default, per-command
timeout), zero retries, when/matrix/env, runFlow/repeat/extendedWaitUntil,
LIFO OS-state restoration on every exit, failure artifacts per run directory,
JSON/JUnit/HTML reports, --agent JSON lines, and a typed backend dispatch
table. No production backend yet (#50, #52).
…#217)

* feat(menus): alternates, open/close events, Help search, mixed state

- alternate/mixed gated in shared validator via MenuSupport; declared on MenuWireItem
- alternates must follow a visible non-alternate command with same key, different modifiers
- help target is a cross-platform root; AppKit Help search on macOS only
- getMenuAvailability(feature); Windows reports host-restriction except hiddenItems

* feat(kitchen-sink): menus audit screen, MENU checks and flows
…229)

Metro source map paths are relative to its server root, which is the
workspace root for in-repo apps. Anchor that root on the entry module and
match packages by installed root or node_modules name, so workspace and
installed SDK packages select the same native modules.

Release packaging fixes surfaced by the Kitchen Sink build:
- pass native-selection exclusions to Expo's RN autolinker via --exclude
  (it ignores an app-level ios: null when a package declares iOS)
- give Xcode's bundle phase an absolute ENTRY_FILE
* feat(ui): native control primitives and AppKit button styles (#91)

Checkbox (mixed), RadioGroup, Switch, Slider, Stepper, ComboBox, TokenField,
PathControl, Progress, LevelIndicator, DisclosureTriangle in four sizes on macOS;
typed unsupported elsewhere (WinUI follow-up #228).

Controlled values use event-count acknowledgment, so in-flight edits survive
stale props. An omitted Button variant is a plain push button; only explicit
variant="default" gets the Return key on macOS and stays portable elsewhere.

* feat(kitchen-sink): control primitive gallery screens (#91)

Toggles, Value inputs, Indicators and Button styles under native-controls,
registered through defineScreens. Theme classes, en/ar strings.

* test(e2e): native-controls checks and flows for control primitives (#91)
…ate (#54) (#233)

- e2e/gate.yaml: Kitchen Sink target matrix, include globs, blocking policy,
  retries 0, 45m time budget, coverage, sign-offs and report paths
- e2e/budgets.yaml: baselines per reference machine (M1 Air 8 GB, base 14"
  MacBook Pro); all not-measured, so blocking budgets fail closed
- bun run gate [--target] [--dry-run]: lint, coverage, build + signature,
  flows through the runner, budgets, manual sign-offs, time budget; one
  JSON + HTML report, PASS only with no reasons; fails with no backend
- schemas for budgets and sign-offs; gate schema adds timeBudget, signoffs,
  targets[].machine, arm64|x64, and drops unimplemented install/cleanUser/junit
- runner: context.metric(), FlowResult.metrics, run deadline, runSuite()
- docs: Release gate section in e2e-flows.md, pointer in releases.md
* feat(windows): window types, singletons and per-window services in createWindowsNavigator

- Existing contract unchanged: open() resolves WindowInfo and rejects
  E_ALREADY_EXISTS while an id entry is open; close/show/getId/prefetch,
  spark.window.<id> roots and direct root props as before.
- Added: openInstance() handle (singletons focus the live window), entries
  without id (generated IDs), get/list/getKeyWindowId/subscribe/remove.
- Per-window undo stack, menus following the key window (one update per
  tick), window-scoped hotkeys, macOS toolbar, error boundary.
- native-menu is an optional peer, loaded only for entries with menus;
  missing module rejects E_MODULE_UNAVAILABLE.
- Opt-in isolated runtime per entry; runtimes reserved before opening and
  destroyed after their last window across navigators.
- Closure tracked per native instance, not the ID-only windowClosed event;
  singleton opens wait for post-reload adoption and never replace it.
- createWindowState: instance-keyed window scope with promotion to app
  scope; closed windows throw E_CLOSED.
- Remove the /multiwindow subpath, its router and deep links; docs folded
  into api-window-contract.md.

* feat(kitchen-sink): multiwindow screen, checks and flows

Note windows, a single Settings window, a shared live document, per-window
stars with promotion, undo/menus per window, error isolation and an
isolated-runtime window. MW checks with covers for the new /windows surface.
… thumbnails, xattrs, free space (#103) (#231)

* feat(files): bookmarks, Full Disk Access, coordination, open-with, icons, thumbnails, xattrs, quarantine, disk space (#103)

- macOS: security-scoped bookmarks (app-scope entitlement checked when
  sandboxed), FDA probe (indeterminate when sandboxed), NSFileCoordinator
  option, open-with with bundle validation, icons/thumbnails/Quick Look,
  xattrs, quarantine, free space
- Windows: open-with via registered handlers, Mark of the Web, free space,
  icons/thumbnails via IShellItemImageFactory + WIC (not compiled here)
- E_READ_ONLY / E_NO_SPACE typed errors on both targets
- withBookmarkAccess bounds access to a callback
- Native test runs sandboxed and checks LaunchServices drops quarantine URLs
- Windows previews tracked in #223

* test(kitchen-sink): file system API screen, checks and #103 verification

* fix(files): sandboxed Full Disk Access is indeterminate for any probe failure

Sandbox denials vary by macOS release (EPERM, or ENOENT outside the
container), so only a successful probe proves anything when sandboxed.

* test(files): sandboxed Full Disk Access may be granted or indeterminate, never denied

CI runners carry broad privacy grants, so a sandboxed probe can open.
* fix(cli): run pod install when a native source file is added or removed

* feat(driver): in-app test driver with in-process window capture (#52)

Inert unless launched by the gate runner: private run directory, one-time
0600 token, 0600 Unix socket, constant-time auth. Commands: ping, navigate,
waitFor, setAppAppearance, capture, quit. Driven apps never activate or show
windows. Adds scripts/e2e/driver.ts client and bun run ks:capture.

* fix(driver): terminate without quit guards when the runner session ends

Runner teardown is not a user quit; running beforeQuit guards left driven
apps alive past quit. Adds verification captures (light, dark, Arabic RTL,
a navigated screen).
…238)

* fix(ui): render DisclosureTriangle as image-only AppKit disclosure button

Empty title + NSImageOnly + push-on/push-off; label becomes AX text on the button cell.
Size to fit at leading edge; square default frame.

* test(e2e): #235 toggles captures (light, dark, ar)
* feat(e2e): visual golden pipeline: store, perceptual diff, assertScreenshot, e2e:golden approve (#53)

* test(e2e): approve infra shell catalog goldens, light and dark (en) (#53)
… safe mode (#60) (#241)

* feat(app): socket-forwarded single instance, relaunch, safe mode (#60)

- macOS: flock + Unix-socket handoff replaces distributed notifications;
  setup lock closes the acquire/listen race; second process forwards
  arguments, working directory, files and URLs, exits after the owner acks
- relaunch(options?) spawns the new instance first, runs the quit
  coordinator, kills it on veto; approved child waits for parent exit
- safe mode: --spark-safe-mode or Shift at launch; skips native frame,
  startup-window and split-view restoration without overwriting it
- getAppContext().launch: safeMode, relaunched, instance
- Windows: forwarded cwd, files/URLs in secondInstance, relaunch via
  mutex handoff, safe mode; pure helpers in launch.inc (not compiled here)
- native tests run real processes for lock, forwarding, crash, relaunch

* test(kitchen-sink): app-lifecycle launch screen, checks and flows (#60)

Launch & single instance screen (en/ar), LIFE checks with flows for
forwarding, relaunch, veto and safe mode, light/dark/ar captures.

* fix(app): refuse forwarded launches from peers running as another user
RN macOS 0.81.7 Fabric flips the scroll content container in RTL but not
the scroll view, so content renders mirrored. The config plugin now patches
RCTScrollViewComponentView: no flip on macOS, no RTL offset conversion, and
RTL content anchored to its right end. Adds an i18n scroll-direction screen,
an AppKit harness test, captures, and re-approves the en catalog goldens
(9 of 37 areas).
* fix(test-driver): waitFor frames from the captured area's top-left; setAppAppearance after first render

waitFor reported AppKit window coordinates (y up), which do not map onto
capture pixels. setAppAppearance sent during launch did not last: the first
render came out in the OS appearance.

* feat(e2e): in-app driver runner backend (#226)

BACKENDS.driver runs flows in-process on the Kitchen Sink: launchApp (+openUrl
via navigate), id lookups via waitFor, matrix appearance/locale, screenshots
via capture, stop via quit. Everything else fails as unsupported.
ks:capture and the backend share scripts/e2e/kitchen-sink.ts. Visibility
checks use the driver when no black-box backend is active.
Adds e2e/flows/infra/catalog-driver.yaml and INFRA checks.
* feat(driver): commands registered by optional modules (#174)

SparkTestDriverRegisterCommand lets a module the driver cannot link add
driver commands; built-in and duplicate names are refused. The client
and the runner's driver backend gain setClock/advanceClock for the
motion clock.

* feat(motion): core animation engine, @legendapp/spark/motion (#174)

Portable C++ solver (closed-form springs, timing, keyframes, decay,
sequences, repeat/reverse, timelines, interruption with exact velocity)
baked into 120 Hz CAKeyframeAnimations the window server plays.
TS builders with macOS (SwiftUI) presets and response/bounce conversion,
animate/stagger/timeline, set/stop/state, native scroll links, frame
telemetry and MotionView. Validation in TS and C++; typed SparkError,
no fallbacks. Requests made before their MotionView mounts wait for it.
Windows typed unsupported (#237). Virtual clock registered as driver
commands setClock/advanceClock.

* feat(kitchen-sink): motion-system screens (#174)

Springs and interruption, keyframes and timelines, scroll link and a
500 ms JavaScript block with an on-screen frame report. en/ar strings,
tracks follow reading direction. Captures in e2e/verification/174.

* test(e2e): motion-system checks and flows (#174)
… panel (#108) (#249)

* feat(dialogs): accessory controls, prompts, About panel, per-feature availability

- open/save accessory: data-driven checkboxes and pop-ups, values return with the result
- showPrompt: plain/secure text prompt, value or distinct cancel
- showAboutPanel: standard About panel with name/version/build/credits/copyright/icon overrides
- confirm: localized labels
- getFileDialogAvailability/getMessageDialogAvailability(feature); Windows typed unsupported (#247)
- native AppKit harness builds and renders panels without presenting them

* feat(kitchen-sink): dialogs screen, DLG checks and flows, #108 verification

* test(dialogs): skip AppKit's accessory helper window in the not-presented check

Its placement is AppKit's choice and overlaps a screen on CI runners.
RN macOS 0.81.7 Fabric passes natural textAlign to TextKit, which resolves
it from the user's language, not the Yoga direction, so text in an RTL
subtree aligns left. The config plugin now patches RCTAttributedTextUtils
to resolve natural alignment from the layout direction; left/right keep
the upstream (iOS) swap. Adds an i18n text-alignment screen, a TextKit
harness test, Arabic catalog goldens and an ar catalog flow run.
* feat(windows): window options audit: aspect ratio, opacity, content protection, click-through, Spaces, simple fullscreen, traffic-light position, subtitle, edited dot, vibrancy (#65)

Typed validation, getWindowAvailability(option), Windows rejects with
E_UNSUPPORTED_OPTION (#243). Audit table in docs/api-window-contract.md.
Native harness asserts each NSWindow property at open and on update.

* feat(kitchen-sink): windows/window-options screen, WIN checks and flows (#65)

* fix(windows): test driver mode never shows windows; child windows follow app appearance; represented URL updates keep the title (#65)

* test(e2e): #65 window options captures (light, dark, ar; child window)
…reporting (#79) (#261)

* feat(shortcuts): user-editable shortcut map with persistence and conflict reporting

- createShortcutMap in /shortcuts/commands: commands reuse HotkeyDefinition plus
  scope (application/window/view/global); rebind/unbind/reset persist overrides in
  Spark settings (version 2, migrates createHotkeyStore version 1 files)
- typed conflicts with commands, registerShortcut, registerGlobalShortcut, menu key
  equivalents and OS warnings; change events; useShortcutMap observing hook
- atomic rebinding: router consumes old+new until one JS commit, gated global
  hotkeys, attached menus republished in between; HotkeyRegistration.setBindings,
  used by useRoutedHotkeys
- getReservedShortcuts (macOS/Windows), getShortcutMapAvailability; Windows local
  scopes typed unsupported (#259)

Refs #79

* feat(kitchen-sink): Shortcut map screen, KEY checks and flows

Refs #79
…227) (#260)

One visual.yaml flow per area opens each screen by deep link, asserts its root and
compares light/dark, en/ar captures with reviewed goldens (shell sidebar masked).
Screens with visual bugs assert the root only (#252, #253, #254, #255); split view,
search field and AI tool runner skip Arabic until #256.
A completed turn was discarded when the app-server exited before the run
accepted it (process exit cleared all turn state), and exit handling could
race the stdout reader and drop the final reply.

- end a process generation by finishing its turns instead of clearing them;
  key turns by generation so restarts don't touch other runs' state
- handle exit only after stdout drains; SIGTERM leftover group members first
- keep received responses across restarts; each waiter removes its own
- tests: accept-after-exit hook replaces the 100 ms delay, stream-ordered
  late-orphan check replaces the 400 ms sleep, fake exits right after
  writing, header compile test gets a 20 s timeout from CI timings

Refs #258
)

* fix(ui): configure Sidebar on a null-oldProps mount (#252)

react-native-macos 0.81 mounts inserted views with updateProps:oldProps:nullptr;
RNSidebar dereferenced it and crashed on mount. With no previous props it now
applies every prop. Native harnesses mount every Spark Fabric view that way.

* test(e2e): open Sidebar screens in the native-controls visual flow (#252)
…246) (#263)

* feat(test-driver): openFile command; driver mode never restores or saves window state

openFile delivers an existing absolute path as a file URL through
application:openURLs:, after the first render settles; navigate keeps
rejecting file URLs. Driven launches skip main-window frame autosave,
child window bounds, restorable windows and split-view chrome, so a frame
saved by an earlier launch can no longer change captures.

* feat(e2e): driver backend launchApp clearState, arguments, env, openFile (#246)

clearState deletes the app's defaults domain and the Application Support,
Caches, HTTPStorages, WebKit, Saved Application State and temp paths
derived from its bundle id and Spark namespace, refusing unexpected or
sandboxed apps and running instances. arguments/env pass through; the
driver's own keys are reserved. Kitchen Sink infra/launch-state screen
and e2e/flows/infra/launch-state.yaml check it end to end.

* fix(e2e): clearState refuses non-absolute or root home/temp directories

* test(test-driver): window-state expectations come from AppKit on the running screen

The control (outside driver mode) asserted the seeded 1040x720 frame
exactly; on CI's small display AppKit constrains it (1024x674). Expect
the saved frame applied and constrained to the screen, and in driver
mode the configured window as AppKit creates it.
Host React children in a flipped content view, in React order, on both
the NSGlassEffectView and plain container paths. Adds a native harness
test and Glass goldens to the native-controls visual flow.
…oggle (#277)

Middle crumb shrinks to an ellipsis first, then the current crumb; root keeps its
width. Breadcrumb clips instead of overlapping, and carries the full path as
tooltip and accessibility label. Separator is one bidi-mirrored glyph with an
explicit writing direction. Adds AI: command runner goldens (light/dark, en/ar).

Refs #255
…ink Documents screen (#289)

- createDocumentSession: untitled numbering, edited state bound to the window's
  edited dot and proxy icon, conflict-checked Save/Save As inside a coordinated
  write claim, autosave in place, Duplicate, Rename, Move To, Revert, locking,
  external-change reload, one session per file (findDocumentSession)
- listDocumentVersions/addDocumentVersion/restoreDocumentVersion (NSFileVersion)
- files: writeTextIfUnchanged accepts { coordinated: true }
- Windows: typed unsupported (#280)
- Kitchen Sink Document model > Documents screen, DOC checks and flows
- Audit table in docs/links-and-documents.md

Refs #112
* feat(printing): print panel, page setup and headless PDF export (#111)

- @legendapp/spark/printing: print, showPageSetup, getDefaultPageSetup,
  exportPDF, getPrintingAvailability(feature), paperSizes
- content: text, HTML, PDF, a window or a View by ref; text/HTML reflow,
  PDFs scale per sheet, windows/views print a cacheDisplayInRect render
- PageSetup is plain JSON (portrait sheet, orientation, margins in points)
- exportPDF draws the print layout straight to Core Graphics PDF: no UI,
  never the print system (a save-job disposition can fall back to a printer)
- canceled is a result, failures reject typed; Windows typed unsupported (#275)
- native harness: page setup round trips, configured print operations,
  real exports parsed back with PDFKit

* feat(kitchen-sink): document-model Printing screen, DOC checks and flows (#111)

Print a report, the window or a receipt View; page setup kept in app
settings; export through a save panel. --ks-print-export <folder> exports
on launch for the driver flow. en/ar, light/dark captures and rendered
first pages of the app's exported PDFs in e2e/verification/111.

* docs(printing): how a canceled print panel is told from a failed job

* test(printing): assert no job reaches the CUPS queue; keep the print panel flow manual

Printing flows take a printing- prefix beside the Documents flows; the
Printing screen joins Documents in document-model.

* test(e2e): re-approve infra-shell-catalog goldens for the Document model screens

Diff: the areas-with-screens count and the Document model row (Documents
from #289 and Printing).

* fix(printing): page setup round-trips the same with or without printers

Without a printer, NSPrintInfo snaps a sheet to its generic paper list
(A4 becomes 595 x 842), so CI read back a different sheet. Set the
orientation, then the oriented sheet, without relying on AppKit's swap;
read the orientation from the sheet's shape and keep the caller's sheet
when AppKit's is within a point of it.
…hdog wakes late (#388)

A watchdog thread that woke late and found its ping already answered never
compared the round trip with the threshold, so a real hang could go
unreported. The round trip now decides on every path, once per ping. A hang
that was over before the watchdog looked is reported with an empty stack and
the new endedBeforeSampling flag; so is one answered between the wait timing
out and the main thread being suspended.

Adds a native test build with a test-only hook that holds the watchdog
thread between a ping and its wait until a hang is over.

Refs #386
Concurrent dynamic imports of the same vi.mock'd module from one importer
share a vitest callstack; the second import sees the first's mock id on it
and loads the real module. StrictMode double effects in settings-ui hit
that race. Share one import so the module is requested once.
* feat(motion): Reduce Motion alternates, frame rate, hidden windows, close cancellation, cues (#177)

Engine: designed per-step alternates or a fade/instant/play plan alternate
under Reduce Motion, cue times scaled with the alternate; hidden views
complete finite tracks or pause (loops, whenHidden: pause) and resume
shifted; bakes at the display's rate. macOS glue: 120 Hz max, 60 Hz in Low
Power Mode via preferredFrameRateRange, re-bake on power/screen changes,
occlusion and window-close handling, cues on display-link frames with a
one-shot timer until two frames before. Driver-only: simulated Reduce
Motion/Low Power/refresh rate through SparkTestDriverDidSimulateState,
simulateOcclusion, recorded cues.

* feat(motion): getMotionEnvironment and plan options for a11y, power, hidden windows, cues (#177)

* feat(e2e): driver assertPaused (windowHidden/occluded), crossfade with jumps, cues in captureMotion (#177)

* feat(kitchen-sink): Motion system > Accessibility, power and lifecycle screen, checks and flows (#177)

Environment readout (Reduce Motion, Low Power Mode, frame rate, window
visibility) re-read on system events; slide/shake/instant Reduce Motion
alternates; hidden-window spinner and slide; cued spring; unmount
cancellation. MOT-REDUCE/POWER/HIDDEN/CANCEL/CUES-01 with a driver flow,
the screen in the visual, audit and Windows flows, and its goldens.

* fix(motion): a driver run pins Reduce Motion and Low Power Mode off (#177)

CI's macOS runner has Reduce Motion on, so motion played alternates in the
coordinator test and would in flows. Simulated state still overrides.

* fix(motion): read window occlusion directly; move cues when an operation resumes a paused view (#177)

Outside driver runs windowHidden: reads the window's occlusion state, so a
window already occluded when its first view mounts counts as hidden and no
cache outlives the observers; simulated visibility stays driver-only. The
engine reports how far resumed views' tracks moved (takeResumeShifts), and
the coordinator moves their cues after every operation, not only on show.

* feat(motion): layout and shared-element hand-offs honor Reduce Motion and hidden windows (#177)

Engine::handoff takes the user's Reduce Motion setting and plays the fade
alternate; the coordinator syncs the host's window visibility first, so a
hand-off in a hidden window completes at once, and moves resumed cues.
…#400)

* feat(biometrics): Touch ID prompts, fallback policy and biometry-gated Keychain secrets (#142)

- @legendapp/spark/biometrics: getBiometricAvailability, getBiometricStatus,
  authenticateWithBiometrics, setBiometricSecret, getBiometricSecret and
  deleteBiometricSecret
- fallback defaults to none (Touch ID only); the login password only with
  fallback password or a biometryOrPassword secret; app fallback ends with
  userFallback
- the person's outcomes resolve; LAError and Keychain causes map explicitly to
  a typed BiometricError (E_UNAVAILABLE, E_ABORTED, E_CLOSED); unnamed codes
  are E_NATIVE; one prompt at a time (E_BUSY); AbortSignal invalidates the
  LAContext
- secrets in the data protection keychain with biometry access control;
  storing and deleting never prompt; Keychain reads run off the main thread;
  results are delivered once on the main thread
- driver mode answers from simulated state (#if SPARK_TEST_DRIVER):
  simulateBiometrics and testBiometrics driver commands and flow commands
- Windows typed unsupported (#389)

* feat(kitchen-sink): Biometrics screen, DATA-BIO checks, flows and goldens (#142)

Storage & data > Biometrics shows availability and status, a prompt per
fallback policy with cancel, and store/read/delete per secret access. Driver
flows cover outcomes, typed errors, abort, busy and secrets via the
simulation; the real Touch ID flow is manual. Goldens for light/dark x en/ar;
infra-shell-catalog goldens re-approved for the new area count.
* Release probe: fail a release build whose JavaScript contains the network inspector (DEVT-REL-04)

* Network inspector for fetch, XMLHttpRequest and WebSocket in React Native DevTools

Development bundles on macOS observe React Native's networking modules in every
JavaScript runtime and send CDP Network events through spark dev's
/inspector/network relay while DevTools is attached. Authorization, Cookie and
Set-Cookie are redacted and bodies capped. Adds the public API in
@legendapp/spark/devtools, the Kitchen Sink Network screen, flows, goldens and
a headless script against loopback fixtures.
…ler (#125) (#398)

* feat(sharing): share picker, Services provider, Core Spotlight and default URL handler (#125)

- @legendapp/spark/sharing: getSharingAvailability, share (text, web URL and
  file items, anchored to a view; resolves the chosen service or null),
  addServiceListener (Info.plist NSServices with NSMessage sparkService;
  services with NSReturnTypes reject E_UNSUPPORTED_OPTION), indexSpotlightItems,
  deleteSpotlightItems and addSpotlightListener (openings before the first
  listener are held and delivered once)
- /links: getDefaultURLHandlerAvailability, isDefaultURLHandler and
  setDefaultURLHandler for schemes the app declares
- host app delegate continues user activities: universal links become url
  events, Spotlight openings reach the sharing listener; SparkFindReactView is
  shared with printing
- driver mode simulates everything in memory and never shows a picker, sets the
  Services provider, writes the Spotlight index or touches LaunchServices;
  simulateSharing/testSharing driver commands and the simulateSharing and
  assertSharing flow commands
- Windows typed unsupported (#392); App Intents and Shortcuts tracked in #393

* feat(kitchen-sink): Sharing & Spotlight screen, SYS-SHARE checks and flows (#125)

System integration > Sharing & Spotlight shares text, a link and a file,
listens for a service, indexes and deletes sample recipes, logs Spotlight
openings and universal links, and sets the default spark-ks handler. Driver
flows use simulateSharing and assertSharing; the real picker, Spotlight and
default handler flow is manual. Goldens for light/dark x en/ar and
verification captures.
* feat(windows): special window types (#190)

Command bar (global hotkey), HUD, Quick Look-style preview, picture in
picture with corner snapping, menu bar/tray/notch popovers, edge drawer,
click-through overlay, desktop widget and presenter mode as controllers
over ordinary windows in @legendapp/spark/windows, with
getSpecialWindowAvailability. Windows reports unsupported-platform (#394).

New macOS window options: panelStyle "hud", level "desktop",
fullScreenAuxiliary, stationary, ignoresCycle, cornerSnapping; non-activating
panels take key focus without activating and stay shown while inactive.
Tray.getBounds() anchors popovers. Driver: simulateGlobalShortcut, and
simulated displays now drive window geometry.

* feat(kitchen-sink): multiwindow/special-windows screen, MW-SPECIAL checks and flows (#190)

* test(e2e): multiwindow/special-windows golden in the multiwindow visual flow (#190)
* Serve the Runtimes worker entry to driver launches of the Kitchen Sink

Dev builds request /.threaded-runtime/entry.bundle when an isolated runtime starts;
the driver's server answered 404, so no worker could run in a flow. Bundle the
generated entry in the background after the app's bundle and serve it.

* [KS] Plugin system scaffold v1: @legendapp/spark/plugins (#211)

Versioned manifests, declared bundled entries, capability grants and per-plugin
isolated Runtimes; contributions (commands, panels, file types, menu items) are
validated before registration. Kitchen Sink Plugins screen with load, register,
sandbox-violation, version, isolation and unload flows, goldens and docs.

* Bundle the Runtimes worker entry on its first request

Most driver runs never start an isolated runtime; build the entry lazily, share
one build between concurrent requests, retry after a failure, and keep close()
waiting for a build in progress.

* Allow the first plugin load to wait for the worker entry's bundling

The driver bundles the Runtimes worker entry on its first request, which takes
about 5s with a warm cache, the default assertion timeout.
* Motion: visual effects, text transitions and symbol effects (#176)

Engine properties for OKLCH background colors, corner radius, shadow,
blur, masks and 3D rotation with perspective, each baked onto its own
layer key path. Background, corners and shadow follow the view's style
until motion first drives them. Adds motionPath, MotionNumber,
MotionText, and MotionSymbol with SF Symbol effects gated by SDK and
macOS version, all with Reduce Motion alternates.

* Kitchen Sink: Visual effects and Text and symbols screens, flows and goldens (#176)
* Add video and speech APIs with Kitchen Sink screens (#133)

@legendapp/spark/video: AVFoundation players with exact seek, rate, volume,
status events, VideoView, fullscreen and picture in picture.
@legendapp/spark/speech: AVSpeechSynthesizer with word events, pause, resume
and stop; SFSpeechRecognizer recognition that never prompts.
Permissions gain speechRecognition. Driver mode simulates fullscreen, PiP,
speech output and recognition. Windows is typed unsupported (#403).

* Give the permissions native test's dropped-prompt timeout room on slow runners

* PermissionKind gains speechRecognition: show it on the Privacy permissions screen and note it in the docs

* Harden media teardown, fullscreen and driver mode

- Speech screen cancels its recognition and stops speech on unmount.
- A reloading runtime stops its own utterances; completions after
  invalidation no longer reach the runtime.
- Fullscreen: closing the window or a failed exit settles the change,
  and a transition macOS never finishes times out after five seconds.
- Driver mode: simulated players stay muted underneath and only load
  local files.
- Recognition: no retain cycle through the task's handler; an audio
  configuration change ends it with E_UNAVAILABLE.
- Speech screen placeholders are distinct.
…ine (#272) (#410)

* Release probe: fail a release build whose JavaScript or native code contains the profiler (DEVT-REL-05)

assertNoDebugSurface now searches every Mach-O file of the app, as well as its
JavaScript, for the profiler's marker, flags profiler menu items, and reports a
missing bundle or missing native code alongside the other findings.

* Profiling: Hermes sampling profiler for every runtime, frame timeline as a Chrome trace

Development bundles on macOS get startProfiling/stopProfiling and
getProfilerAvailability in @legendapp/spark/devtools, and Developer menu
Start Profiling / Stop Profiling… items. A recording samples every JavaScript
runtime with Hermes's sampling profiler and records the frame timeline (main
thread display-link frames and MotionView animations), then writes a
source-mapped .cpuprofile per runtime and one Chrome trace. The test driver's
frame counter records on the same timeline, and assertFrames writes it as a
trace artifact. Release builds compile none of it (RCT_DEV and __DEV__).

Adds the Kitchen Sink Profiling screen, checks, flows, goldens and a headless
script covering React Native DevTools' per-runtime profiler, tracing and heap
snapshots.

* Profiling: keep answering runtimes, write off the main thread, follow the recording

- stopProfiling keeps every runtime that answered and lists the rest in
  ProfilingResult.missing (E_TIMEOUT, E_UNAVAILABLE, E_INVALID_DATA) instead
  of failing the whole recording; invalid UTF-8 is a typed error.
- Profiles are encoded and written on a background queue (SparkProfilerFiles.mm),
  with a compile-and-run native test of the writer.
- File names carry milliseconds, deduplicate sanitized runtime names, and retry
  other names when taken, so a collision cannot lose a recording.
- sourceMaps is "applied" only when a frame was mapped.
- The frame timeline polls motion only when startFrames asks for it
  (assertFrames trace: true); plain frame budgets count frames only.
- isProfiling() and addProfilingListener(); the Profiling screen follows the
  real recording state.
- Documents the shared Hermes sampler with React Native DevTools (#409).
- Profiling screen aligned to the leading edge; screen titles can be localized
  in the catalog and breadcrumb.
* [KS] API: App Intents and Shortcuts (#393)

- @legendapp/spark/app-intents: getAppIntentsAvailability, getAppIntents,
  addAppIntentHandler (text or no result, typed failures, invocations wait
  for their handler, each completes once within 30 s) and donateAppIntent
- macos.appIntents (id, title, description, string/number/integer/boolean
  parameters, returnsText, openApp, App Shortcut phrases): the config plugin
  generates the AppIntent structs, AppShortcutsProvider and donation class
  into the app target and lists the intents in Info.plist; the
  RNDesktopAppIntents pod is a module exposing a Foundation-only bridge
- spark build fails when the product lacks Metadata.appintents for a
  declared intent; the Spark Runner reports host-restriction, Windows
  unsupported-platform (#415)
- driver mode simulates declarations, invocations and donations; the
  simulateAppIntents/testAppIntents driver commands and flow commands
- Kitchen Sink: System integration > App Intents & Shortcuts, SYS-INTENTS
  checks, driver flows, goldens (light/dark x en/ar), a manual Shortcuts flow

Refs #393

* Keep App Intents out of driver clones, escape control characters, buffer invocations until registered (#393)
…421)

* feat(windows): window pools with prewarm and memory budgets, background render throttling (#187)

Pooled navigator entries keep hidden, mounted windows: prewarm creates them,
an open hands the root its props natively and shows the window once the
content commits, and closing parks the window (new instance token) to be
reused. Warm windows are bounded by max and a per-entry memory budget
measured from the process footprint, and are released for real under
memory pressure, on trimPool and on remove. throttle: true suspends a
window's content while it is hidden, minimized or occluded. New
occlusionChanged window event and getWindowPerformanceAvailability.
macOS only; Windows rejects E_UNSUPPORTED_OPTION (#414).

* feat(kitchen-sink): multiwindow/performance screen, MW-PERF checks, flows and goldens (#187)

* fix(windows): pool only plain opens, close changed windows, pair app window events per use (#187)

- Windows opened with options a warm window cannot take, or restored with a
  saved ID, close for real instead of joining the pool.
- A pooled window whose options changed during a use closes for real.
- windowOpened/windowClosed pair per use: none for warm windows, opened on
  reuse, closed on park, nothing more when a parked window is released.
- Memory budget uses the median of recent costs; prewarm and trimPool reset it.
- Warm reuse falls back to the primary display when its display is gone.
- Menus are removed even if pool cleanup fails; throttling and Windows docs.

* fix(kitchen-sink): localized title and leading-aligned layout on multiwindow/performance (#187)
* Kitchen Sink: move the isolated-runtime panel to a top-level file

spark dev discovers threaded components only in top-level files (#418), so the
multiwindow isolated-runtime window rendered nothing in development sessions.
The isolated-runtime flow asserts the increment button's label, which carries
its count.

* React DevTools: Components and Profiler per runtime, Select element across windows

React Native DevTools' Components and Profiler panels reach each JavaScript
runtime's React DevTools backend. In development bundles on macOS,
runtime-entry ends Select element in every window of the runtime after a click
in one (React Native ended it only in the clicked window). The release-safety
probe fails a release build whose JavaScript contains React DevTools' backend
or the fix (DEVT-REL-06). Adds the Kitchen Sink React DevTools screen, checks,
flows, goldens, docs, and a headless script that speaks the React DevTools
bridge over CDP.
* feat(authentication): Sign in with Apple and passkeys (#146)

- @legendapp/spark/apple-authentication: getAppleAuthenticationAvailability,
  signInWithApple, getAppleIDCredentialState
- @legendapp/spark/passkeys: getPasskeyAvailability, createPasskey, getPasskey
  with WebAuthn's base64url values
- availability reads the signed entitlements and reports missing-entitlement;
  requests without them reject missingEntitlement before a controller exists
- ASAuthorizationController per request, presented over windowId's window or
  the key window; one at a time (E_BUSY); the person's cancel resolves; named
  ASAuthorizationErrors reject a typed AuthorizationError; AbortSignal cancels
  the controller (E_ABORTED appCancel); completion once, on the main thread
- macos.authentication opts in to com.apple.developer.applesignin and
  webcredentials associated domains; nothing is added otherwise
- driver mode answers from simulated state (#if SPARK_TEST_DRIVER):
  simulateAuthorization and testAuthorization driver and flow commands
- Windows typed unsupported (#423)

* feat(kitchen-sink): Sign in with Apple and passkeys screen, AUTH checks, flows and goldens (#146)

Authentication > Sign in with Apple and passkeys shows availability, signs in,
checks the credential state, creates and uses a passkey, and cancels. Driver
flows cover availability, credentials, typed errors, busy and abort via the
simulation; the real sheets are a manual flow on a team-signed build. Goldens
for light/dark x en/ar; infra-shell-catalog goldens re-approved for the new
area count.
* [KS] Motion: animatable backdrop materials (#407)

MotionMaterial: a MotionView showing an NSVisualEffectView material or Liquid
Glass. A material change crossfades on the engine (materialCrossfade, interruptible,
covered materials removed); materialIntensity and materialTintOpacity are motion
properties baked onto the material's and the tint's layers. getMaterialAvailability
checks SDK and runtime; plain MotionViews reject the material properties with
E_UNSUPPORTED_OPTION. Reduce Motion plays crossfades as the 200 ms fade;
getMotionEnvironment reports reduceTransparency (pinned off and simulated in driver
runs). Windows is typed unsupported (#237).

Adds the Kitchen Sink Backdrop materials screen (localized title), checks, flows
and at-rest goldens.

* [KS] Motion materials: keep the tint out of intensity, cap overlapping crossfades (#407)

materialIntensity drives a container of the material views under the tint, so
the tint stays at intensity 0. Overlapping crossfades keep at most three
materials. Docs say Liquid Glass takes a new corner radius when the animation
starts. The Glass button is always shown and reports E_UNAVAILABLE where Liquid
Glass is missing, so goldens do not depend on the macOS version.
Driver runs now receive only simulateSystemEvent's events, so the System
state screen and its golden no longer depend on the host's network, power,
displays or settings. The sleep, wake, lock, unlock, appearance, displays and
power sources move into SparkSystemStateObserver, so every OS source is
installed per event type behind one driver check. A native test covers both
modes.

Refs #431
@legendapp/spark/capture: microphone recording to M4A with levels,
the camera with a CameraView preview and JPEG photos, and ScreenCaptureKit
sources, screenshots and screen recording (macOS 15). Capture never prompts:
each call rejects E_PERMISSION_DENIED unless its permission is granted.
The camera and microphone entitlements are opt-in through macos.capture,
which requires the usage descriptions; a hardened-runtime build without
one reports missing-entitlement. Interruptions end a session, and
recordings are finished when the app quits.
Driver mode simulates every device, permission and entitlement with
synthetic levels and frames and never opens AVCaptureDevice or
ScreenCaptureKit (simulateCapture, assertSimulatedCapture). Kitchen Sink
Capture screen with flows and goldens; real devices are checked by a
manual flow. Windows is typed unsupported (#425).
* feat(windows): window choreography (#189)

Zoom a window out of a source rectangle (display or window content) and back
into it on close, animated frame changes, close transitions that hand key
focus to the window the content returns to, and attached windows (palettes,
inspectors) that dock magnetically and move with their parent. A native
animator steps frames and opacity on the main thread, follows Reduce Motion,
and in driver runs follows motion's virtual clock through a new driver clock
hook. Interruption resolves finished: false; an interrupted close resolves
{ closed: false, reason: "interrupted" }. setWindowBounds' macos.durationMs and
corner snapping run on the same animator. macOS only; Windows reports
unsupported-platform (#434).

* feat(kitchen-sink): multiwindow/choreography screen, MW-CHOREO checks, flows and goldens (#189)

* fix(windows): choreography review fixes (#189)

- Close transitions with to: null no longer throw when picking the focus target.
- An interrupted animated setWindowBounds settles nothing, and a new animation
  stops every running one first, so no snap or dock animation is orphaned.
- Every advance of motion's virtual clock (waitForMotion too) reaches window
  choreography.
- Attaching a window that has a parent restores that parent when the
  attachment ends.
- openWindowFrom documents when it resolves; an ignored finished promise
  raises no unhandled rejection.
- The Kitchen Sink screen clears close transitions before closing its windows.
* [KS] Motion: shape morph and stroke drawing (#406)

MotionShape: a MotionView drawing a CAShapeLayer. morph moves it between its
paths (SVG path data, parsed strictly; paths with the same subpaths are
subdivided to one cubic structure, others are E_UNSUPPORTED_OPTION), and
strokeStart, strokeEnd, strokeWidth, fillColor and strokeColor (OKLCH) are
engine properties baked onto the shape layer. Bounded at 16 paths of 128
segments. Plain MotionViews reject the shape properties with
E_UNSUPPORTED_OPTION. Under Reduce Motion the morph and stroke ends jump and
the shape fades in. motionPath shares the path parser. Windows is typed
unsupported (#237).

Adds the Kitchen Sink Shapes screen (localized title), checks, flows and
goldens at rest and at frozen frames 0/100.

* [KS] Motion shapes: bounded path bakes, stricter path data (#406)

The morph bakes at most 60 samples a second and 240 per part (docs give the
worst-case memory). MotionShape memoizes its parsed paths. The path parser
reports token positions, rejects commas outside number pairs and numbers after
Z, and treats Z Z as a no-op; motionPath's input changes are documented and
tested. Native config range-checks segment counts; adds morph interior-pair
and extrapolation tests.
* feat(privileged-helper): privileged helper, admin prompt and XPC (#138)

- @legendapp/spark/privileged-helper: availability, status, register,
  unregister, open Login Items, requestAdminAuthorization and
  sendPrivilegedHelperMessage, with typed PrivilegedHelperError reasons
- SMAppService launch daemons; requiresApproval is a state, not an error
- admin prompt via Authorization Services; the rights travel to the helper,
  which checks them itself without UI
- XPC both ways checked by code signature (team + app identifier on the
  helper's listener, team on the app's connection); the app sends nothing
  until the helper's verification reply passes its requirement
- helper side (helper/SparkPrivilegedHelperService.m) for app helpers
- unsigned/ad-hoc builds report unsigned and stop before ServiceManagement
- macos.privilegedHelpers opts helpers in; spark build writes their
  LaunchDaemons plists, nothing otherwise
- driver mode simulates registration, approval, the prompt and the
  connection: simulatePrivilegedHelper / testPrivilegedHelper
- Windows typed unsupported (#440); threat model in docs/privileged-helper.md

* feat(kitchen-sink): privileged helper screen, PROC checks, flows and goldens (#138)

Processes and IPC > Privileged helper shows availability and status,
registers and unregisters, opens Login Items, requests and releases admin
rights and sends whoami. Driver flows cover availability, approval states,
the prompt and the connection via the simulation; the real path is a manual
flow on a team-signed build with the Kitchen Sink helper. Goldens for
light/dark x en/ar; infra-shell-catalog goldens re-approved for the area count.
MotionShader: a MotionView with a built-in ripple (CITorusLensDistortion),
distortion (CIBumpDistortion), specular sweep (gradient overlay) or dissolve
(a precompiled Metal noise-threshold mask rendered on display frames). Each
effect is one engine property, installed only while it shows or animates.
The Metal library is compiled ahead of time (bun run motion:shaders, Metal
2.0) into a header that records the source and library hashes and the
compiler; no user-supplied shader source. Availability builds the pipeline;
devices added, removed or lost make it check again. Display frames are
skipped rather than waiting for a drawable, and the link follows one
animating view, made again when it stops or moves.

MotionEmitter with startParticles/stopParticles: a CAEmitterLayer with a
1,000-particle budget (checked in JavaScript and natively), laid out on
resize, paused in hidden windows, stopped on unmount and when Reduce Motion
turns on (unless started with play). Reduce Motion: ripple, distortion and
sweep jump (fade alternate), dissolve fades, particles do not emit. Windows
is typed unsupported (#237). MotionShape, MotionMaterial, MotionShader and
MotionEmitter pass their content to useMotionView the same way.

Adds the Kitchen Sink Shaders and particles screen (localized title and
summary), checks, flows, at-rest goldens, and CARenderer-rendered frozen
virtual-clock frames in the native coordinator test.
…store (#444) (#447)

* Drive media-native playback checks by the player's clock and events

Under load the restart seek lands after play() returns, so the status still read 3s; the timed drains also raced late playback starts. Wait on the player's time and reported events instead, and check the playback rate on the item's timebase.

* Keep a note's scroll offset set before its details first report

A scroll offset set after the details view mounted but before it was sized was ignored, and the first report (0) overwrote it, so the saved session sometimes held 0. Treat it as pending like a restored offset, and assert the note reports it before the app quits.
…umb (#412) (#438)

* Localize Kitchen Sink screen titles and summaries in Arabic

* Re-approve Arabic goldens for localized screen titles
Under the test driver, the displays observer, motion's power, screen and
occlusion observers and the window manager's screen observer install no
host sources. getSystemInfo, getNetworkStatus and getAppearancePreferences
report fixed driver values for fields no simulation sets; launch arguments
pin Reduce Motion and the appearance setting. Motion pins 60 Hz. Native
tests cover both modes.
… for (#446) (#452)

AppKit silently withholds a menu key equivalent while another menu-bar item
with a different action has it, so Ctrl+Cmd+S lost to the template's
View > Show Sidebar. A published shortcut now takes over from a host item
(restored on removal); a clash with another published item rejects E_BUSY.
* feat(windows): window workspaces (#191)

createWindowWorkspaces saves a navigator's session and the connected displays
as named workspaces in the app's data directory (versioned workspaces.json,
validated on every read; a corrupt file rejects E_INVALID_DATA until clear()).
restore() checks the saved session, closes the navigator's windows through
their guards and reopens the workspace adapted to the displays connected now
(exact, moved or scaled), and puts fullscreen windows back into fullscreen.
An optional Window > Workspaces menu saves and restores with shortcuts.
macOS only; Windows reports unsupported-platform (#445). Spaces and Stage
Manager have no public API; the contract says what is and isn't possible.

* feat(kitchen-sink): multiwindow/workspaces screen, MW-WORKSPACE checks, flows and goldens (#191)

* fix(windows): workspace review fixes (#191)

- restoreSession releases parked pooled windows that hold saved IDs.
- Restore skips closed windows, counts natively gone ones as closed, and
  reopens what it closed when it stops or fails (typed failed result).
- Every operation re-reads the file; writes use compare and swap.
- Load limits: file size, workspace and window counts, duplicate fullscreen
  IDs, negative savedAt.
- Menu item IDs are per controller and per name; setMenuLabels relabels.
- Displays without persistent IDs match by display ID.
- A navigator instance close on a natively gone window ends it (E_CLOSED).

* fix(kitchen-sink): localized workspace menu labels, isolated shortcuts in Arabic (#191)

* fix(windows): workspaces publish their own menu instead of retitling Window (#191)

A location-targeted overlay sets the merged menu's title, which replaced the
system's localized Window title (#449). Re-approved the workspace goldens:
Arabic shortcuts are in left-to-right isolates.
MotionView sharedAcrossWindows: a shared element flies between windows in a
borderless flight window, driven by the destination's hand-off on the motion
engine. It arrives as its window is shown and flies back as the window starts
to leave, following the window module's showing and departure signals.
Kitchen Sink screen motion-system/shared-windows with flows, checks and goldens.
Launch the driven process with TZ=UTC so Date and Intl match the fixed
timeZone; mask the architecture row in state-visual; document that a
simulated powerChanged leaves batteryLevel null; assert the driver state
exists before simulateSystemEvent writes it; fix a doc reference.
…449, #450) (#455)

* fix(menus): keep a native root's localized title when a menu targets it (#449)

A root with target { menu } merged into an existing native menu and set its
title to the label, replacing the system's localized title (ar, fr). The label
now names only a root Spark creates.

* fix(menus): publish Shift+letter shortcuts as uppercase key equivalents (#450)

AppKit matches a lowercase key equivalent to a key event made with
keyEventWithType: (the test driver's) by its characters, ignoring Shift, so a
published Cmd+Shift+N ran on Cmd+N and not on Cmd+Shift+N. The uppercase form
matches only the shifted press, for driver and hardware events.
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