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.
- package/LICENSE +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- 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
|
+
}
|