peachsim 0.1.527

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,256 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'fs'
2
+ import { tmpdir } from 'os'
3
+ import path from 'path'
4
+ import { launchReapedChrome } from '../../../../scripts/lib/reap-browser'
5
+ import { runFlowPlayback } from '../../cli/commands/flow'
6
+ import {
7
+ composeMarketingScreenshots,
8
+ type MarketingBrowserLike,
9
+ type ComposeResult,
10
+ type ComposeSlideSource,
11
+ } from './compose'
12
+ import { createFramedScreenshotComposer } from './frame-compose'
13
+ import { applyOrgDeckToPlan, type OrgDeckFrame } from './org-deck'
14
+ import { loadScreenshotsPlan, type NormalizedScreenshotsPlan } from './schema'
15
+
16
+ export interface RunScreenshotsOverrides {
17
+ appTarget?: string | null
18
+ deviceModel?: string | null
19
+ simId?: string | null
20
+ captureOnly?: boolean
21
+ composeOnly?: boolean
22
+ // copy/colors pulled from the team's org deck to overlay onto the plan's
23
+ // compose slides (rnx screenshot appstore --org … --repo …).
24
+ orgDeckFrames?: OrgDeckFrame[] | null
25
+ }
26
+
27
+ export interface CaptureResult {
28
+ manifestPath: string
29
+ rawDir: string
30
+ framedDir: string
31
+ rawFiles: string[]
32
+ framedFiles: string[]
33
+ }
34
+
35
+ export interface ScreenshotsRunResult {
36
+ plan: NormalizedScreenshotsPlan
37
+ capture: CaptureResult
38
+ compose: ComposeResult | null
39
+ }
40
+
41
+ function resolveCapturePathMode(plan: NormalizedScreenshotsPlan): 'plan' | 'flow' {
42
+ if (plan.capture.pathMode === 'plan' || plan.capture.pathMode === 'flow') {
43
+ return plan.capture.pathMode
44
+ }
45
+ return plan.capture.fromDir ? 'flow' : 'plan'
46
+ }
47
+
48
+ export function buildCaptureFlowArgs(
49
+ plan: NormalizedScreenshotsPlan,
50
+ overrides: RunScreenshotsOverrides,
51
+ ): string[] {
52
+ if (!plan.capture.flowPath) {
53
+ throw new Error('capture flow path is required to build flow args')
54
+ }
55
+ const args = [plan.capture.flowPath]
56
+ const pathMode = resolveCapturePathMode(plan)
57
+ args.push('--screenshots', plan.capture.rawDir)
58
+ if (pathMode === 'flow') {
59
+ args.push('--screenshot-paths', 'flow')
60
+ }
61
+ const appTarget = overrides.appTarget ?? plan.appTarget
62
+ const deviceModel = overrides.deviceModel ?? plan.deviceModel
63
+ const simId = overrides.simId ?? plan.capture.simId
64
+ if (appTarget) args.push('--url', appTarget)
65
+ if (deviceModel) args.push('--device', deviceModel)
66
+ if (simId) args.push('--sim', simId)
67
+ if (plan.capture.openInNewSim) args.push('--new')
68
+ return args
69
+ }
70
+
71
+ function resolveSlideRawPath(
72
+ plan: NormalizedScreenshotsPlan,
73
+ screenshot: string,
74
+ ): string {
75
+ return path.isAbsolute(screenshot)
76
+ ? screenshot
77
+ : path.join(plan.capture.rawDir, screenshot)
78
+ }
79
+
80
+ function resolveCaptureManifestPath(plan: NormalizedScreenshotsPlan): string {
81
+ return path.join(plan.capture.outDir, 'manifest.json')
82
+ }
83
+
84
+ async function runCaptureStage(
85
+ plan: NormalizedScreenshotsPlan,
86
+ overrides: RunScreenshotsOverrides,
87
+ ): Promise<void> {
88
+ if (!plan.capture.flowPath) return
89
+ mkdirSync(plan.capture.rawDir, { recursive: true })
90
+ const args = buildCaptureFlowArgs(plan, overrides)
91
+ const exitCode = await runFlowPlayback(args)
92
+ if (exitCode !== 0) {
93
+ throw new Error(`flow capture failed with exit code ${exitCode}`)
94
+ }
95
+ }
96
+
97
+ async function buildFramedSources(
98
+ plan: NormalizedScreenshotsPlan,
99
+ browserOverride?: MarketingBrowserLike,
100
+ ): Promise<Map<string, string>> {
101
+ const sources = new Map<string, string>()
102
+ mkdirSync(plan.capture.framedDir, { recursive: true })
103
+ const browser =
104
+ browserOverride ??
105
+ (await (async () => {
106
+ const { chromium } = await import('playwright')
107
+ return launchReapedChrome((options) => chromium.launch(options), {
108
+ headless: true,
109
+ })
110
+ })())
111
+ try {
112
+ const composer = await createFramedScreenshotComposer(browser)
113
+ try {
114
+ for (const slide of plan.compose.slides) {
115
+ const rawPath = resolveSlideRawPath(plan, slide.screenshot)
116
+ const rawBuffer = readFileSync(rawPath)
117
+ const framedBuffer = await composer.compose(rawBuffer, plan.compose.frame.style)
118
+ const framedPath = path.join(plan.capture.framedDir, `${slide.assetKey}.png`)
119
+ writeFileSync(framedPath, framedBuffer)
120
+ sources.set(slide.id, framedPath)
121
+ }
122
+ } finally {
123
+ await composer.close()
124
+ }
125
+ } finally {
126
+ if (!browserOverride && typeof browser.close === 'function') {
127
+ await browser.close()
128
+ }
129
+ }
130
+ return sources
131
+ }
132
+
133
+ function writeCaptureManifest(
134
+ plan: NormalizedScreenshotsPlan,
135
+ framedSources: Map<string, string>,
136
+ effectiveDeviceModel: string | null,
137
+ ): CaptureResult {
138
+ const rawFiles: string[] = []
139
+ const framedFiles: string[] = []
140
+ mkdirSync(plan.capture.outDir, { recursive: true })
141
+ const manifestPath = resolveCaptureManifestPath(plan)
142
+ const slides = plan.compose.slides.map((slide) => {
143
+ const rawPath = resolveSlideRawPath(plan, slide.screenshot)
144
+ const framedPath = framedSources.get(slide.id) ?? null
145
+ rawFiles.push(rawPath)
146
+ if (framedPath) framedFiles.push(framedPath)
147
+ return {
148
+ id: slide.id,
149
+ screenshot: slide.screenshot,
150
+ rawPath,
151
+ framedPath,
152
+ }
153
+ })
154
+ writeFileSync(
155
+ manifestPath,
156
+ JSON.stringify(
157
+ {
158
+ generatedAt: new Date().toISOString(),
159
+ deviceModel: effectiveDeviceModel,
160
+ mode: plan.capture.mode,
161
+ rawDir: plan.capture.rawDir,
162
+ framedDir: plan.capture.framedDir,
163
+ slides,
164
+ },
165
+ null,
166
+ 2,
167
+ ),
168
+ )
169
+ return {
170
+ manifestPath,
171
+ rawDir: plan.capture.rawDir,
172
+ framedDir: plan.capture.framedDir,
173
+ rawFiles,
174
+ framedFiles,
175
+ }
176
+ }
177
+
178
+ function resolveComposeSources(
179
+ plan: NormalizedScreenshotsPlan,
180
+ framedSources: Map<string, string>,
181
+ ): ComposeSlideSource[] {
182
+ return plan.compose.slides.map((slide) => ({
183
+ slide,
184
+ imagePath:
185
+ plan.compose.frame.show === true
186
+ ? (framedSources.get(slide.id) ?? resolveSlideRawPath(plan, slide.screenshot))
187
+ : resolveSlideRawPath(plan, slide.screenshot),
188
+ }))
189
+ }
190
+
191
+ export async function runScreenshotsPlan(
192
+ planPath: string,
193
+ overrides: RunScreenshotsOverrides = {},
194
+ ): Promise<ScreenshotsRunResult> {
195
+ const plan = loadScreenshotsPlan(planPath)
196
+ if (overrides.orgDeckFrames && overrides.orgDeckFrames.length > 0) {
197
+ applyOrgDeckToPlan(plan, overrides.orgDeckFrames)
198
+ }
199
+ const effectiveDeviceModel = overrides.deviceModel ?? plan.deviceModel
200
+ if (!overrides.composeOnly) {
201
+ await runCaptureStage(plan, overrides)
202
+ }
203
+ const needsFramedSources =
204
+ plan.capture.mode !== 'raw' ||
205
+ (!overrides.captureOnly && plan.compose.frame.show === true)
206
+ const needsSharedBrowser = needsFramedSources || !overrides.captureOnly
207
+ const sharedBrowser = needsSharedBrowser
208
+ ? await (async () => {
209
+ const { chromium } = await import('playwright')
210
+ return launchReapedChrome((options) => chromium.launch(options), {
211
+ headless: true,
212
+ })
213
+ })()
214
+ : null
215
+ try {
216
+ const framedSources = needsFramedSources
217
+ ? await buildFramedSources(plan, sharedBrowser ?? undefined)
218
+ : new Map()
219
+ const capture = writeCaptureManifest(plan, framedSources, effectiveDeviceModel)
220
+
221
+ if (overrides.captureOnly) {
222
+ return { plan, capture, compose: null }
223
+ }
224
+
225
+ const composeSources = resolveComposeSources(plan, framedSources)
226
+ const compose = await composeMarketingScreenshots(
227
+ plan.compose,
228
+ composeSources,
229
+ sharedBrowser ?? undefined,
230
+ )
231
+ return { plan, capture, compose }
232
+ } finally {
233
+ if (sharedBrowser && typeof sharedBrowser.close === 'function') {
234
+ await sharedBrowser.close()
235
+ }
236
+ }
237
+ }
238
+
239
+ export async function runScreenshotsPlanFromExistingRaw(
240
+ planPath: string,
241
+ rawDir: string,
242
+ ): Promise<ScreenshotsRunResult> {
243
+ const plan = loadScreenshotsPlan(planPath)
244
+ plan.capture.rawDir = rawDir
245
+ plan.capture.flowPath = null
246
+ plan.capture.fromDir = rawDir
247
+ const framedDir = path.join(tmpdir(), 'rnx-screenshots-framed')
248
+ plan.capture.framedDir = framedDir
249
+ const framedSources = await buildFramedSources(plan)
250
+ const capture = writeCaptureManifest(plan, framedSources, plan.deviceModel)
251
+ const compose = await composeMarketingScreenshots(
252
+ plan.compose,
253
+ resolveComposeSources(plan, framedSources),
254
+ )
255
+ return { plan, capture, compose }
256
+ }
@@ -0,0 +1,91 @@
1
+ // fetch the team's screenshot deck (the copy / colors founders set up in the
2
+ // org dashboard) and overlay it onto a screenshots plan, so the programmatic
3
+ // `rnx screenshot appstore --plan … --org … --repo …` flow renders the org's
4
+ // words + brand gradient instead of the local yaml's. the plan still owns
5
+ // which screen each slide captures and the device frame; the deck owns copy +
6
+ // colors. 3d scenes don't translate to the cli's 2d composer, so only copy and
7
+ // colors carry over (the 3d hero look stays the in-browser screenshot mode).
8
+
9
+ import { resolveBgColorValue } from 'sootsim-engine/screenshots/tokens'
10
+ import type { BackgroundSpec } from './registry'
11
+ import type { NormalizedScreenshotsPlan } from './schema'
12
+
13
+ export interface OrgDeckFetchOptions {
14
+ org: string
15
+ repo: string
16
+ origin: string
17
+ authHeader: string
18
+ }
19
+
20
+ export interface OrgDeckFrame {
21
+ title?: string
22
+ subtitle?: string
23
+ bgTop?: string
24
+ bgBottom?: string
25
+ titleColor?: string
26
+ }
27
+
28
+ function readString(value: unknown): string | undefined {
29
+ return typeof value === 'string' && value.length > 0 ? value : undefined
30
+ }
31
+
32
+ // GET the team deck and return its first deck's frames, or null when the team
33
+ // has no deck yet. throws on auth / network failure so the caller can surface a
34
+ // clear message rather than silently falling back to the local copy.
35
+ export async function fetchOrgDeckFrames(
36
+ opts: OrgDeckFetchOptions,
37
+ ): Promise<OrgDeckFrame[] | null> {
38
+ const base = opts.origin.replace(/\/$/, '')
39
+ const url = `${base}/api/sootsim/screenshot-decks?org=${encodeURIComponent(
40
+ opts.org,
41
+ )}&repo=${encodeURIComponent(opts.repo)}`
42
+ const res = await fetch(url, { headers: { Authorization: opts.authHeader } })
43
+ if (!res.ok) {
44
+ const detail = await res.text().catch(() => '')
45
+ throw new Error(
46
+ `org deck fetch failed (${res.status} ${res.statusText}) for ${opts.org}/${opts.repo}${
47
+ detail ? ` — ${detail.slice(0, 200)}` : ''
48
+ }`,
49
+ )
50
+ }
51
+ const body = (await res.json()) as { decks?: Array<{ config?: unknown }> }
52
+ const deck = body.decks?.[0]
53
+ if (!deck || !deck.config || typeof deck.config !== 'object') return null
54
+ const frames = (deck.config as { frames?: unknown }).frames
55
+ if (!Array.isArray(frames)) return null
56
+ return frames.map((entry) => {
57
+ const frame = (entry ?? {}) as Record<string, unknown>
58
+ return {
59
+ title: readString(frame.title),
60
+ subtitle: readString(frame.subtitle),
61
+ bgTop: readString(frame.bgTop),
62
+ bgBottom: readString(frame.bgBottom),
63
+ titleColor: readString(frame.titleColor),
64
+ }
65
+ })
66
+ }
67
+
68
+ // overlay deck frames onto the plan's compose slides, matched by order. a deck
69
+ // with fewer frames than the plan leaves the trailing slides on their yaml copy.
70
+ export function applyOrgDeckToPlan(
71
+ plan: NormalizedScreenshotsPlan,
72
+ frames: OrgDeckFrame[],
73
+ ): void {
74
+ plan.compose.slides.forEach((slide, index) => {
75
+ const frame = frames[index]
76
+ if (!frame) return
77
+ if (frame.title != null) slide.headline = frame.title
78
+ if (frame.subtitle != null) slide.subheadline = frame.subtitle
79
+ if (frame.bgTop && frame.bgBottom) {
80
+ const background: BackgroundSpec = {
81
+ type: 'gradient',
82
+ direction: 180,
83
+ stops: [
84
+ { offset: 0, color: resolveBgColorValue(frame.bgTop) },
85
+ { offset: 1, color: resolveBgColorValue(frame.bgBottom) },
86
+ ],
87
+ }
88
+ slide.background = background
89
+ }
90
+ })
91
+ }
@@ -0,0 +1,58 @@
1
+ // composer-facing registry. shared tokens live in
2
+ // `sootsim-engine/src/screenshots/tokens.ts` so the live browser
3
+ // screenshot mode and the post-process composer render from the same
4
+ // canvas / background / text / pose / animation / clamp vocabulary.
5
+ //
6
+ // this module re-exports those tokens plus the composer-specific
7
+ // `FrameComposeSpec` shape.
8
+
9
+ import type { DeviceModel } from 'sootsim-engine/settings'
10
+
11
+ export {
12
+ BACKGROUND_PRESETS,
13
+ DEFAULT_FRAME_SHADOW,
14
+ DEFAULT_FRAME_STYLE,
15
+ SCREENSHOT_CANVASES,
16
+ SCREENSHOT_MODE_ANIM,
17
+ SCREENSHOT_TEXT_CLAMP,
18
+ SCREENSHOT_TEXT_SAFE_AREA,
19
+ cloneBackgroundSpec,
20
+ resolveBackgroundPreset,
21
+ resolveCanvasPreset,
22
+ } from 'sootsim-engine/screenshots/tokens'
23
+
24
+ export type {
25
+ BackgroundPresetName,
26
+ BackgroundSpec,
27
+ CanvasPreset,
28
+ FrameShadowSpec,
29
+ GradientStop,
30
+ PosePresetName,
31
+ ScreenshotCanvasName,
32
+ TextPresetName,
33
+ } from 'sootsim-engine/screenshots/tokens'
34
+
35
+ import type { FrameShadowSpec, PosePresetName } from 'sootsim-engine/screenshots/tokens'
36
+
37
+ export interface FrameComposeSpec {
38
+ show: boolean
39
+ style: DeviceModel
40
+ pose: PosePresetName
41
+ scale: number
42
+ offsetY: number
43
+ shadow: FrameShadowSpec
44
+ }
45
+
46
+ export function toAssetKey(value: string): string {
47
+ const normalized = value
48
+ .replace(/\\/g, '/')
49
+ .replace(/\.png$/i, '')
50
+ .trim()
51
+ .split('/')
52
+ .filter(Boolean)
53
+ .join('--')
54
+ .replace(/[^A-Za-z0-9-]+/g, '-')
55
+ .replace(/-+/g, '-')
56
+ .replace(/^-|-$/g, '')
57
+ return normalized || 'slide'
58
+ }