Repository navigation
Kitchen Sink release verification gate (#9) - #219
Draft
sethwebster wants to merge 118 commits into
Draft
sethwebster wants to merge 118 commits into
sethwebster wants to merge 118 commits into
Conversation
- 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).
… 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.
…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.
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.
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.
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 intomain.Integrated (117 PRs, oldest first)
e2e/checks/<area>.yaml) + coverage enforcement #48) ([KS] Check registry coverage enforcement (#48) #224)e2e/gate.yaml): matrix, tags, budgets, blocking policy #54) ([KS] Release gate: e2e/gate.yaml, budgets, sign-offs, bun run gate (#54) #233)In progress
Remaining open work (ready)
Blocked here
Open decisions for Seth
environment.clean)?Decisions
requireBlackBox.build: devflows live ine2e/dev-flows/<area>/; the gate doesn't run them, coverage counts them.variant="default"keeps its portable behavior. [KS][MW] API: multiwindow architecture #178: multiwindow folded into/windowswith a shared JS runtime by default; the legend-apps navigator API is unchanged. RTLleft/rightkeep React Native's iOS swap.spark buildfails a release that contains driver strings without the opt-in.How to run
bun run e2e:audit [--artifact <app>](bundle hygiene); security flows ine2e/flows/security/(entitlements, code signing, load paths). Accessibility audit flows:e2e/flows/accessibility/.macos.testDriverInRelease(Kitchen Sink sets it); private CG event synthesis only withmacos.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.