rnxsim 0.0.0 → 0.1.312
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 +21 -0
- package/README.md +512 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +39 -0
- package/detox/expectations.ts +537 -0
- package/detox/gestures.ts +480 -0
- package/detox/index.ts +2118 -0
- package/detox/jest-preset.cjs +51 -0
- package/detox/jest-setup-after-env.cjs +18 -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.ts +43 -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-cli/bin.js +15 -0
- package/dist-cli/chunks/agent-NXVDDCFG.js +48 -0
- package/dist-cli/chunks/agent-wrapper-DXJCFKL3.js +17 -0
- package/dist-cli/chunks/app-fonts-W5AJZQJF.js +4 -0
- package/dist-cli/chunks/assert-3LOJEUDT.js +49 -0
- package/dist-cli/chunks/auth-MFTYYPNX.js +12 -0
- package/dist-cli/chunks/beta-GT3NN67B.js +4 -0
- package/dist-cli/chunks/camera-6EAK7CUL.js +22 -0
- package/dist-cli/chunks/chunk-274UD5FD.js +4 -0
- package/dist-cli/chunks/chunk-2SA2VTGP.js +5 -0
- package/dist-cli/chunks/chunk-33FKQH54.js +5 -0
- package/dist-cli/chunks/chunk-37I5AIS7.js +3 -0
- package/dist-cli/chunks/chunk-3D57IBPC.js +4 -0
- package/dist-cli/chunks/chunk-4BXCZHAU.js +4 -0
- package/dist-cli/chunks/chunk-5OX7ANRA.js +5 -0
- package/dist-cli/chunks/chunk-66NF2KNG.js +9 -0
- package/dist-cli/chunks/chunk-6UKJB2VC.js +125 -0
- package/dist-cli/chunks/chunk-7INBNA3U.js +26 -0
- package/dist-cli/chunks/chunk-7VWOSTD5.js +5 -0
- package/dist-cli/chunks/chunk-AY47WKS5.js +23 -0
- package/dist-cli/chunks/chunk-B7KMNHX7.js +4 -0
- package/dist-cli/chunks/chunk-BKNOLP3J.js +4 -0
- package/dist-cli/chunks/chunk-BRIZOR4K.js +28 -0
- package/dist-cli/chunks/chunk-DM6KUAOZ.js +4 -0
- package/dist-cli/chunks/chunk-EUAVBTKU.js +4 -0
- package/dist-cli/chunks/chunk-FTC4M26Q.js +4 -0
- package/dist-cli/chunks/chunk-FWYYCUAZ.js +4 -0
- package/dist-cli/chunks/chunk-G74YIEVS.js +7 -0
- package/dist-cli/chunks/chunk-GZDHEONW.js +507 -0
- package/dist-cli/chunks/chunk-HG7ORJMT.js +5 -0
- package/dist-cli/chunks/chunk-HMDOLCKG.js +15 -0
- package/dist-cli/chunks/chunk-JG6S4RSH.js +4 -0
- package/dist-cli/chunks/chunk-KR5BGMBZ.js +4 -0
- package/dist-cli/chunks/chunk-KZW5WBGU.js +983 -0
- package/dist-cli/chunks/chunk-L24Z3CRS.js +30 -0
- package/dist-cli/chunks/chunk-LDQ7HVCW.js +4 -0
- package/dist-cli/chunks/chunk-MENV7VLD.js +4 -0
- package/dist-cli/chunks/chunk-MNGQ42QE.js +568 -0
- package/dist-cli/chunks/chunk-N6A3SXMR.js +4 -0
- package/dist-cli/chunks/chunk-NLEMQ4PY.js +6 -0
- package/dist-cli/chunks/chunk-NPVGMH2N.js +101 -0
- package/dist-cli/chunks/chunk-OR3XTXEB.js +107 -0
- package/dist-cli/chunks/chunk-PEPUGJVI.js +4 -0
- package/dist-cli/chunks/chunk-Q3RGLERJ.js +142 -0
- package/dist-cli/chunks/chunk-SI3BUTQR.js +4 -0
- package/dist-cli/chunks/chunk-T4FT6CM7.js +4 -0
- package/dist-cli/chunks/chunk-TBMVBI26.js +7 -0
- package/dist-cli/chunks/chunk-TCWQJILS.js +5 -0
- package/dist-cli/chunks/chunk-TGQNQ6XT.js +766 -0
- package/dist-cli/chunks/chunk-TYE3WBSR.js +5 -0
- package/dist-cli/chunks/chunk-U4PN6JOT.js +6 -0
- package/dist-cli/chunks/chunk-VUFRB347.js +4 -0
- package/dist-cli/chunks/chunk-VZBLFMYC.js +10 -0
- package/dist-cli/chunks/chunk-WBT32R6N.js +25 -0
- package/dist-cli/chunks/chunk-XRGEEPJ5.js +38 -0
- package/dist-cli/chunks/chunk-Y2FSZSO7.js +71 -0
- package/dist-cli/chunks/chunk-YRV7HR3P.js +74 -0
- package/dist-cli/chunks/chunk-YZRFF4YA.js +14 -0
- package/dist-cli/chunks/chunk-ZMZTHU7V.js +4 -0
- package/dist-cli/chunks/cleanup-H3GWQB63.js +25 -0
- package/dist-cli/chunks/cli-version-3QPAJ3LC.js +4 -0
- package/dist-cli/chunks/compat-5WG2XJV7.js +20 -0
- package/dist-cli/chunks/config-ANMBVVLL.js +13 -0
- package/dist-cli/chunks/control-NSGNQFJG.js +4 -0
- package/dist-cli/chunks/daemon-JHF4BJ3U.js +4 -0
- package/dist-cli/chunks/debug-CHV2O2KF.js +200 -0
- package/dist-cli/chunks/desktop-FIPIK4CH.js +31 -0
- package/dist-cli/chunks/detox-CVUJP7K6.js +52 -0
- package/dist-cli/chunks/device-6WCUM4W6.js +18 -0
- package/dist-cli/chunks/diagnose-I6EMWEII.js +43 -0
- package/dist-cli/chunks/disk-cleanup-P27CYFTN.js +4 -0
- package/dist-cli/chunks/drivers-HQ6AKZT6.js +4 -0
- package/dist-cli/chunks/film-UOQFFYNF.js +74 -0
- package/dist-cli/chunks/flow-N27MRRNL.js +4 -0
- package/dist-cli/chunks/help-SF6FEGEE.js +4 -0
- package/dist-cli/chunks/hidden-runtime-alias-NJYFSJKZ.js +4 -0
- package/dist-cli/chunks/home-paths-XOV44JEF.js +4 -0
- package/dist-cli/chunks/inspect-CWFRFQTY.js +4 -0
- package/dist-cli/chunks/install-desktop-KLQDNXZQ.js +4 -0
- package/dist-cli/chunks/login-BPFQENCX.js +28 -0
- package/dist-cli/chunks/logout-5A45ZGY7.js +4 -0
- package/dist-cli/chunks/maestro-E4CSDMLM.js +104 -0
- package/dist-cli/chunks/maestro-generate-CKMOJQX2.js +36 -0
- package/dist-cli/chunks/mode-SZYNMIQF.js +26 -0
- package/dist-cli/chunks/optional-demo-registry-FRH4SFVG.js +4 -0
- package/dist-cli/chunks/perf-VXU2XJAX.js +48 -0
- package/dist-cli/chunks/permissions-IRIAOVND.js +13 -0
- package/dist-cli/chunks/record-RY7FPLLH.js +72 -0
- package/dist-cli/chunks/report-issue-KNWXOE53.js +29 -0
- package/dist-cli/chunks/runtime-AR3GJTGK.js +4 -0
- package/dist-cli/chunks/screenshot-command-QLALR6GY.js +159 -0
- package/dist-cli/chunks/screenshot-layers-OQFMFVRL.js +20 -0
- package/dist-cli/chunks/screenshots-capture-6TSRJFGB.js +42 -0
- package/dist-cli/chunks/serve-F54J2NQO.js +44 -0
- package/dist-cli/chunks/setup-7V6UKX4U.js +32 -0
- package/dist-cli/chunks/skills-QQBOSETH.js +50 -0
- package/dist-cli/chunks/storage-TAPMKK5O.js +37 -0
- package/dist-cli/chunks/store-7NX3S43H.js +4 -0
- package/dist-cli/chunks/telemetry-E7BUK6RA.js +4 -0
- package/dist-cli/chunks/timeline-IAM564NA.js +24 -0
- package/dist-cli/chunks/upgrade-CD55QXIX.js +7 -0
- package/dist-cli/chunks/upload-GZ6LIMYM.js +4 -0
- package/dist-cli/chunks/version-3DYJ5AS3.js +6 -0
- package/dist-cli/chunks/web-6NW6YLPI.js +4 -0
- package/dist-cli/chunks/what-happened-CZZDOOVG.js +25 -0
- package/dist-lib/agent-daemon-client.cjs +468 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +725 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +195 -0
- package/dist-lib/backend-origin.cjs +70 -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/cli-constants.cjs +32 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +2863 -0
- package/dist-lib/detox/jest-preset.cjs +51 -0
- package/dist-lib/dev-bundle-resolution.cjs +223 -0
- package/dist-lib/home-paths.cjs +421 -0
- package/dist-lib/host/bridge-host.cjs +5600 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +373 -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/websocket-proxy.cjs +214 -0
- package/dist-lib/index.cjs +513 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/menu.cjs +620 -0
- package/dist-lib/menu.mjs +581 -0
- package/dist-lib/metro.cjs +297 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1034 -0
- package/dist-lib/sdk.cjs +2333 -0
- package/dist-lib/sdk.mjs +2240 -0
- package/dist-lib/skills.cjs +23911 -0
- package/dist-lib/vite.cjs +358 -0
- package/package.json +189 -18
- package/scripts/dev-server-scanner.ts +941 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/scripts/postinstall.cjs +3 -0
- package/scripts/preuninstall.cjs +118 -0
- package/skills/contrast/SKILL.md +171 -0
- package/skills/rnx-debug/SKILL.md +425 -0
- package/skills/rnx-perf/SKILL.md +295 -0
- package/skills/rnx-setup/SKILL.md +160 -0
- package/skills/rnx-test/SKILL.md +292 -0
- package/skills/rnx-visual/SKILL.md +122 -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 +573 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +204 -0
- package/src/backend-origin.ts +49 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +171 -0
- package/src/browser-cache.ts +16 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +32 -0
- package/src/codex-client.ts +215 -0
- package/src/config.ts +134 -0
- package/src/dev-bundle-resolution.ts +236 -0
- package/src/disk-cleanup.ts +466 -0
- package/src/home-paths.ts +552 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2626 -0
- package/src/host/fetch-proxy-handler.ts +397 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/websocket-proxy.ts +210 -0
- package/src/index.ts +12 -705
- package/src/jump-to-source-babel.ts +135 -0
- package/src/menu.ts +734 -0
- package/src/metro-plugin.ts +139 -0
- package/src/native-dev-bundle-url.ts +96 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +92 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/render-mode.ts +27 -0
- package/src/runtime-assets.ts +228 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +454 -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 +7 -0
- package/src/settings-bundle.ts +418 -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/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +190 -0
- package/src/vite-plugin.ts +1750 -0
- package/src/worklets-babel.ts +134 -0
|
@@ -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
|
+
// `sootsim 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
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { devices, type DeviceModel } from 'sootsim-engine/settings'
|
|
4
|
+
import yaml from 'yaml'
|
|
5
|
+
import {
|
|
6
|
+
cloneBackgroundSpec,
|
|
7
|
+
DEFAULT_FRAME_SHADOW,
|
|
8
|
+
DEFAULT_FRAME_STYLE,
|
|
9
|
+
resolveBackgroundPreset,
|
|
10
|
+
resolveCanvasPreset,
|
|
11
|
+
toAssetKey,
|
|
12
|
+
type BackgroundPresetName,
|
|
13
|
+
type BackgroundSpec,
|
|
14
|
+
type CanvasPreset,
|
|
15
|
+
type FrameComposeSpec,
|
|
16
|
+
type PosePresetName,
|
|
17
|
+
type TextPresetName,
|
|
18
|
+
} from './registry'
|
|
19
|
+
|
|
20
|
+
export type CaptureMode = 'raw' | 'framed' | 'raw+framed'
|
|
21
|
+
export type CapturePathMode = 'auto' | 'plan' | 'flow'
|
|
22
|
+
|
|
23
|
+
export interface NormalizedScreenshotSlide {
|
|
24
|
+
id: string
|
|
25
|
+
assetKey: string
|
|
26
|
+
screenshot: string
|
|
27
|
+
headline: string
|
|
28
|
+
subheadline: string
|
|
29
|
+
eyebrow: string
|
|
30
|
+
pose?: PosePresetName
|
|
31
|
+
scale?: number
|
|
32
|
+
offsetY?: number
|
|
33
|
+
background?: BackgroundSpec
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface NormalizedCapturePlan {
|
|
37
|
+
flowPath: string | null
|
|
38
|
+
fromDir: string | null
|
|
39
|
+
outDir: string
|
|
40
|
+
rawDir: string
|
|
41
|
+
framedDir: string
|
|
42
|
+
mode: CaptureMode
|
|
43
|
+
pathMode: CapturePathMode
|
|
44
|
+
simId: string | null
|
|
45
|
+
openInNewSim: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface NormalizedComposePlan {
|
|
49
|
+
outDir: string
|
|
50
|
+
locale: string
|
|
51
|
+
canvases: CanvasPreset[]
|
|
52
|
+
frame: FrameComposeSpec
|
|
53
|
+
background: BackgroundSpec
|
|
54
|
+
text: {
|
|
55
|
+
preset: TextPresetName
|
|
56
|
+
color: string
|
|
57
|
+
subColor: string
|
|
58
|
+
eyebrowColor: string
|
|
59
|
+
}
|
|
60
|
+
slides: NormalizedScreenshotSlide[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface NormalizedScreenshotsPlan {
|
|
64
|
+
planPath: string
|
|
65
|
+
planDir: string
|
|
66
|
+
appTarget: string | null
|
|
67
|
+
deviceModel: DeviceModel | null
|
|
68
|
+
capture: NormalizedCapturePlan
|
|
69
|
+
compose: NormalizedComposePlan
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
73
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function expectRecord(value: unknown, label: string): Record<string, unknown> {
|
|
77
|
+
if (!isRecord(value)) throw new Error(`${label} must be an object`)
|
|
78
|
+
return value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readString(value: unknown, fallback = ''): string {
|
|
82
|
+
return typeof value === 'string' ? value : fallback
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function readOptionalString(value: unknown): string | null {
|
|
86
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readBoolean(value: unknown, fallback: boolean): boolean {
|
|
90
|
+
return typeof value === 'boolean' ? value : fallback
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readNumber(value: unknown, fallback: number): number {
|
|
94
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readDeviceModel(
|
|
98
|
+
value: unknown,
|
|
99
|
+
fallback: DeviceModel | null,
|
|
100
|
+
): DeviceModel | null {
|
|
101
|
+
if (typeof value !== 'string' || value.trim().length === 0) return fallback
|
|
102
|
+
return Object.prototype.hasOwnProperty.call(devices, value)
|
|
103
|
+
? (value as DeviceModel)
|
|
104
|
+
: null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeBackground(value: unknown, fallback: BackgroundSpec): BackgroundSpec {
|
|
108
|
+
if (typeof value === 'string') {
|
|
109
|
+
const preset = resolveBackgroundPreset(value)
|
|
110
|
+
if (!preset) throw new Error(`unknown background preset: ${value}`)
|
|
111
|
+
return preset
|
|
112
|
+
}
|
|
113
|
+
if (!isRecord(value)) return cloneBackgroundSpec(fallback)
|
|
114
|
+
const type =
|
|
115
|
+
value.type === 'solid' || value.type === 'gradient' ? value.type : fallback.type
|
|
116
|
+
if (type === 'solid') {
|
|
117
|
+
return {
|
|
118
|
+
type,
|
|
119
|
+
color: readString(value.color, fallback.color ?? '#000000'),
|
|
120
|
+
glow: readOptionalString(value.glow) ?? fallback.glow,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const stops = Array.isArray(value.stops)
|
|
124
|
+
? value.stops
|
|
125
|
+
.map((stop) => {
|
|
126
|
+
if (!isRecord(stop)) return null
|
|
127
|
+
const offset = readNumber(stop.offset, Number.NaN)
|
|
128
|
+
const color = readOptionalString(stop.color)
|
|
129
|
+
if (!Number.isFinite(offset) || !color) return null
|
|
130
|
+
return { offset, color }
|
|
131
|
+
})
|
|
132
|
+
.filter((stop): stop is { offset: number; color: string } => !!stop)
|
|
133
|
+
: (fallback.stops?.map((stop) => ({ ...stop })) ?? [])
|
|
134
|
+
return {
|
|
135
|
+
type,
|
|
136
|
+
direction: readNumber(value.direction, fallback.direction ?? 180),
|
|
137
|
+
glow: readOptionalString(value.glow) ?? fallback.glow,
|
|
138
|
+
stops:
|
|
139
|
+
stops.length > 0 ? stops : (fallback.stops?.map((stop) => ({ ...stop })) ?? []),
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizeCanvasList(value: unknown): CanvasPreset[] {
|
|
144
|
+
const names = Array.isArray(value) ? value : ['iphone-6-9']
|
|
145
|
+
const presets = names.map((entry) => {
|
|
146
|
+
if (typeof entry !== 'string')
|
|
147
|
+
throw new Error('compose.canvases entries must be strings')
|
|
148
|
+
const preset = resolveCanvasPreset(entry)
|
|
149
|
+
if (!preset) throw new Error(`unknown canvas preset: ${entry}`)
|
|
150
|
+
return preset
|
|
151
|
+
})
|
|
152
|
+
if (presets.length === 0)
|
|
153
|
+
throw new Error('compose.canvases must include at least one preset')
|
|
154
|
+
return presets
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function normalizeTextPreset(value: unknown): TextPresetName {
|
|
158
|
+
switch (value) {
|
|
159
|
+
case 'editorial-left':
|
|
160
|
+
case 'minimal-bottom':
|
|
161
|
+
case 'none':
|
|
162
|
+
case 'bold-top':
|
|
163
|
+
return value
|
|
164
|
+
default:
|
|
165
|
+
return 'bold-top'
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function normalizeCapturePathMode(value: unknown): CapturePathMode {
|
|
170
|
+
switch (value) {
|
|
171
|
+
case 'plan':
|
|
172
|
+
case 'flow':
|
|
173
|
+
case 'auto':
|
|
174
|
+
return value
|
|
175
|
+
default:
|
|
176
|
+
return 'auto'
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function normalizePose(value: unknown): PosePresetName | undefined {
|
|
181
|
+
switch (value) {
|
|
182
|
+
case 'straight':
|
|
183
|
+
case 'tilted-left':
|
|
184
|
+
case 'tilted-right':
|
|
185
|
+
case 'cut-bottom':
|
|
186
|
+
case 'cut-top':
|
|
187
|
+
return value
|
|
188
|
+
default:
|
|
189
|
+
return undefined
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function resolvePlanPath(planDir: string, value: string): string {
|
|
194
|
+
return path.isAbsolute(value) ? value : path.resolve(planDir, value)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function normalizeSlides(
|
|
198
|
+
value: unknown,
|
|
199
|
+
fallbackBackground: BackgroundSpec,
|
|
200
|
+
): NormalizedScreenshotSlide[] {
|
|
201
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
202
|
+
throw new Error('compose.slides must be a non-empty array')
|
|
203
|
+
}
|
|
204
|
+
return value.map((entry, index) => {
|
|
205
|
+
const slide = expectRecord(entry, `compose.slides[${index}]`)
|
|
206
|
+
const screenshot = readOptionalString(slide.screenshot)
|
|
207
|
+
if (!screenshot) {
|
|
208
|
+
throw new Error(`compose.slides[${index}].screenshot is required`)
|
|
209
|
+
}
|
|
210
|
+
const explicitId = readOptionalString(slide.id)
|
|
211
|
+
const assetKey = toAssetKey(explicitId || screenshot)
|
|
212
|
+
return {
|
|
213
|
+
id: explicitId || assetKey,
|
|
214
|
+
assetKey,
|
|
215
|
+
screenshot,
|
|
216
|
+
headline: readString(slide.headline),
|
|
217
|
+
subheadline: readString(slide.subheadline),
|
|
218
|
+
eyebrow: readString(slide.eyebrow),
|
|
219
|
+
pose: normalizePose(slide.pose),
|
|
220
|
+
scale:
|
|
221
|
+
typeof slide.scale === 'number' && Number.isFinite(slide.scale)
|
|
222
|
+
? slide.scale
|
|
223
|
+
: undefined,
|
|
224
|
+
offsetY:
|
|
225
|
+
typeof slide.offsetY === 'number' && Number.isFinite(slide.offsetY)
|
|
226
|
+
? slide.offsetY
|
|
227
|
+
: undefined,
|
|
228
|
+
background:
|
|
229
|
+
slide.theme || slide.background
|
|
230
|
+
? normalizeBackground(slide.theme ?? slide.background, fallbackBackground)
|
|
231
|
+
: undefined,
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function loadScreenshotsPlan(planPath: string): NormalizedScreenshotsPlan {
|
|
237
|
+
const resolvedPlanPath = path.resolve(planPath)
|
|
238
|
+
const planDir = path.dirname(resolvedPlanPath)
|
|
239
|
+
const raw = yaml.parse(readFileSync(resolvedPlanPath, 'utf8'))
|
|
240
|
+
const root = expectRecord(raw, 'screenshots plan')
|
|
241
|
+
const capture = expectRecord(root.capture ?? {}, 'capture')
|
|
242
|
+
const compose = expectRecord(root.compose ?? {}, 'compose')
|
|
243
|
+
|
|
244
|
+
const appTarget =
|
|
245
|
+
typeof root.app === 'number' ? String(root.app) : readOptionalString(root.app)
|
|
246
|
+
const deviceModel = readDeviceModel(root.device, null)
|
|
247
|
+
const captureOutDir = resolvePlanPath(
|
|
248
|
+
planDir,
|
|
249
|
+
readString(capture.out, path.join('.sootsim', 'screenshots', 'capture')),
|
|
250
|
+
)
|
|
251
|
+
const captureFrom = readOptionalString(capture.from)
|
|
252
|
+
const flowPath = readOptionalString(capture.flow)
|
|
253
|
+
if (!captureFrom && !flowPath) {
|
|
254
|
+
throw new Error('capture.flow or capture.from is required')
|
|
255
|
+
}
|
|
256
|
+
const captureMode =
|
|
257
|
+
capture.mode === 'raw' || capture.mode === 'framed' || capture.mode === 'raw+framed'
|
|
258
|
+
? capture.mode
|
|
259
|
+
: ('raw+framed' as CaptureMode)
|
|
260
|
+
const capturePathMode = normalizeCapturePathMode(capture.pathMode)
|
|
261
|
+
const defaultBackground = normalizeBackground(
|
|
262
|
+
compose.background ?? ('cyan' as BackgroundPresetName),
|
|
263
|
+
resolveBackgroundPreset('cyan')!,
|
|
264
|
+
)
|
|
265
|
+
const frameConfig: FrameComposeSpec = {
|
|
266
|
+
show: readBoolean(
|
|
267
|
+
compose.frame && isRecord(compose.frame) ? compose.frame.show : undefined,
|
|
268
|
+
true,
|
|
269
|
+
),
|
|
270
|
+
style:
|
|
271
|
+
readDeviceModel(
|
|
272
|
+
compose.frame && isRecord(compose.frame) ? compose.frame.style : null,
|
|
273
|
+
null,
|
|
274
|
+
) ||
|
|
275
|
+
deviceModel ||
|
|
276
|
+
DEFAULT_FRAME_STYLE,
|
|
277
|
+
pose:
|
|
278
|
+
normalizePose(
|
|
279
|
+
compose.frame && isRecord(compose.frame) ? compose.frame.pose : undefined,
|
|
280
|
+
) || 'straight',
|
|
281
|
+
scale: readNumber(
|
|
282
|
+
compose.frame && isRecord(compose.frame) ? compose.frame.scale : undefined,
|
|
283
|
+
1,
|
|
284
|
+
),
|
|
285
|
+
offsetY: readNumber(
|
|
286
|
+
compose.frame && isRecord(compose.frame) ? compose.frame.offsetY : undefined,
|
|
287
|
+
0,
|
|
288
|
+
),
|
|
289
|
+
shadow: {
|
|
290
|
+
color: readString(
|
|
291
|
+
compose.frame && isRecord(compose.frame) && isRecord(compose.frame.shadow)
|
|
292
|
+
? compose.frame.shadow.color
|
|
293
|
+
: undefined,
|
|
294
|
+
DEFAULT_FRAME_SHADOW.color,
|
|
295
|
+
),
|
|
296
|
+
blur: readNumber(
|
|
297
|
+
compose.frame && isRecord(compose.frame) && isRecord(compose.frame.shadow)
|
|
298
|
+
? compose.frame.shadow.blur
|
|
299
|
+
: undefined,
|
|
300
|
+
DEFAULT_FRAME_SHADOW.blur,
|
|
301
|
+
),
|
|
302
|
+
spread: readNumber(
|
|
303
|
+
compose.frame && isRecord(compose.frame) && isRecord(compose.frame.shadow)
|
|
304
|
+
? compose.frame.shadow.spread
|
|
305
|
+
: undefined,
|
|
306
|
+
DEFAULT_FRAME_SHADOW.spread,
|
|
307
|
+
),
|
|
308
|
+
opacity: readNumber(
|
|
309
|
+
compose.frame && isRecord(compose.frame) && isRecord(compose.frame.shadow)
|
|
310
|
+
? compose.frame.shadow.opacity
|
|
311
|
+
: undefined,
|
|
312
|
+
DEFAULT_FRAME_SHADOW.opacity,
|
|
313
|
+
),
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
planPath: resolvedPlanPath,
|
|
319
|
+
planDir,
|
|
320
|
+
appTarget,
|
|
321
|
+
deviceModel,
|
|
322
|
+
capture: {
|
|
323
|
+
flowPath: flowPath ? resolvePlanPath(planDir, flowPath) : null,
|
|
324
|
+
fromDir: captureFrom ? resolvePlanPath(planDir, captureFrom) : null,
|
|
325
|
+
outDir: captureOutDir,
|
|
326
|
+
rawDir: captureFrom
|
|
327
|
+
? resolvePlanPath(planDir, captureFrom)
|
|
328
|
+
: path.join(captureOutDir, 'raw'),
|
|
329
|
+
framedDir: path.join(captureOutDir, 'framed'),
|
|
330
|
+
mode: captureMode,
|
|
331
|
+
pathMode: capturePathMode,
|
|
332
|
+
simId: readOptionalString(capture.sim),
|
|
333
|
+
openInNewSim: readBoolean(capture.new, false),
|
|
334
|
+
},
|
|
335
|
+
compose: {
|
|
336
|
+
outDir: resolvePlanPath(
|
|
337
|
+
planDir,
|
|
338
|
+
readString(compose.out, path.join('.sootsim', 'screenshots', 'exports')),
|
|
339
|
+
),
|
|
340
|
+
locale: readString(compose.locale, 'en'),
|
|
341
|
+
canvases: normalizeCanvasList(compose.canvases),
|
|
342
|
+
frame: frameConfig,
|
|
343
|
+
background: defaultBackground,
|
|
344
|
+
text: {
|
|
345
|
+
preset: normalizeTextPreset(
|
|
346
|
+
compose.text && isRecord(compose.text) ? compose.text.preset : undefined,
|
|
347
|
+
),
|
|
348
|
+
color: readString(
|
|
349
|
+
compose.text && isRecord(compose.text) ? compose.text.color : undefined,
|
|
350
|
+
'#ffffff',
|
|
351
|
+
),
|
|
352
|
+
subColor: readString(
|
|
353
|
+
compose.text && isRecord(compose.text) ? compose.text.subColor : undefined,
|
|
354
|
+
'rgba(232,240,247,0.86)',
|
|
355
|
+
),
|
|
356
|
+
eyebrowColor: readString(
|
|
357
|
+
compose.text && isRecord(compose.text) ? compose.text.eyebrowColor : undefined,
|
|
358
|
+
'rgba(229,242,255,0.72)',
|
|
359
|
+
),
|
|
360
|
+
},
|
|
361
|
+
slides: normalizeSlides(compose.slides, defaultBackground),
|
|
362
|
+
},
|
|
363
|
+
}
|
|
364
|
+
}
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from '../cli/commands/inspect/core'
|
|
2
|
+
export * from '../cli/commands/inspect/actions'
|
|
3
|
+
export { waitForSootsimIdle } from '../cli/commands/inspect/settling'
|
|
4
|
+
export type {
|
|
5
|
+
WaitForSootsimIdleOptions,
|
|
6
|
+
WaitForSootsimIdleResult,
|
|
7
|
+
} from '../cli/commands/inspect/settling'
|