autokap 1.0.7 → 1.0.8

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 (278) hide show
  1. package/assets/cursors/macos.svg +4 -0
  2. package/assets/cursors/windows.svg +15 -0
  3. package/assets/skill/OPCODE-REFERENCE.md +607 -0
  4. package/assets/skill/README.md +39 -0
  5. package/assets/skill/SKILL.md +453 -468
  6. package/assets/skill/STUDIO-SKILL.md +476 -0
  7. package/assets/skill/references/examples.md +104 -0
  8. package/assets/skill/references/interactive-demo.md +225 -0
  9. package/assets/skill/references/mock-data.md +178 -0
  10. package/dist/action-verifier.d.ts +29 -0
  11. package/dist/action-verifier.js +133 -0
  12. package/dist/agent-action-recovery.d.ts +45 -0
  13. package/dist/agent-action-recovery.js +370 -0
  14. package/dist/agent-message-utils.d.ts +21 -0
  15. package/dist/agent-message-utils.js +77 -0
  16. package/dist/agent-url-utils.d.ts +30 -0
  17. package/dist/agent-url-utils.js +138 -0
  18. package/dist/agent.d.ts +92 -8
  19. package/dist/agent.js +2936 -781
  20. package/dist/ak-tree.d.ts +39 -0
  21. package/dist/ak-tree.js +368 -0
  22. package/dist/alt-text.d.ts +26 -0
  23. package/dist/alt-text.js +55 -0
  24. package/dist/auth-capture.d.ts +17 -0
  25. package/dist/auth-capture.js +164 -0
  26. package/dist/benchmark.d.ts +59 -0
  27. package/dist/benchmark.js +135 -0
  28. package/dist/browser-bar.d.ts +14 -6
  29. package/dist/browser-bar.js +145 -8
  30. package/dist/browser-pool.d.ts +7 -0
  31. package/dist/browser-pool.js +15 -5
  32. package/dist/browser-utils.d.ts +31 -0
  33. package/dist/browser-utils.js +97 -0
  34. package/dist/browser.d.ts +51 -1
  35. package/dist/browser.js +1481 -31
  36. package/dist/capture-alt-text.js +2 -1
  37. package/dist/capture-language-preflight.js +14 -0
  38. package/dist/capture-llm-page-identity.js +22 -10
  39. package/dist/capture-page-identity.d.ts +5 -7
  40. package/dist/capture-page-identity.js +211 -78
  41. package/dist/capture-preset-credentials.d.ts +50 -0
  42. package/dist/capture-preset-credentials.js +127 -0
  43. package/dist/capture-request-plan.d.ts +2 -2
  44. package/dist/capture-request-plan.js +64 -16
  45. package/dist/capture-run-optimizer.js +48 -33
  46. package/dist/capture-selector-memory.d.ts +5 -0
  47. package/dist/capture-selector-memory.js +18 -0
  48. package/dist/capture-strategy.d.ts +36 -0
  49. package/dist/capture-strategy.js +95 -0
  50. package/dist/capture-studio-sync.d.ts +1 -0
  51. package/dist/capture-studio-sync.js +9 -3
  52. package/dist/capture-surface-contract.d.ts +36 -0
  53. package/dist/capture-surface-contract.js +299 -0
  54. package/dist/capture-transition-engine.d.ts +28 -0
  55. package/dist/capture-transition-engine.js +292 -0
  56. package/dist/capture-variant-state.d.ts +2 -0
  57. package/dist/capture-variant-state.js +26 -0
  58. package/dist/capture-verification.d.ts +35 -0
  59. package/dist/capture-verification.js +95 -0
  60. package/dist/capture-viewport-lock.d.ts +48 -0
  61. package/dist/capture-viewport-lock.js +74 -0
  62. package/dist/circuit-breaker.d.ts +42 -0
  63. package/dist/circuit-breaker.js +119 -0
  64. package/dist/cli-config.d.ts +8 -1
  65. package/dist/cli-config.js +62 -6
  66. package/dist/cli-contract.d.ts +15 -0
  67. package/dist/cli-contract.js +167 -0
  68. package/dist/cli-runner-local.d.ts +12 -0
  69. package/dist/cli-runner-local.js +102 -0
  70. package/dist/cli-runner.d.ts +34 -0
  71. package/dist/cli-runner.js +433 -0
  72. package/dist/cli-utils.d.ts +0 -1
  73. package/dist/cli-utils.js +2 -5
  74. package/dist/cli.js +1005 -267
  75. package/dist/clip-orchestrator.js +9 -2
  76. package/dist/clip-postprocess.js +25 -16
  77. package/dist/cookie-dismiss.d.ts +2 -0
  78. package/dist/cookie-dismiss.js +48 -13
  79. package/dist/cost-logging.d.ts +8 -0
  80. package/dist/cost-logging.js +160 -46
  81. package/dist/cost-resolution-monitor.d.ts +16 -0
  82. package/dist/cost-resolution-monitor.js +34 -0
  83. package/dist/credential-templates.js +2 -2
  84. package/dist/cursor-overlay-script.d.ts +6 -0
  85. package/dist/cursor-overlay-script.js +169 -0
  86. package/dist/dom-css-purger.d.ts +65 -0
  87. package/dist/dom-css-purger.js +333 -0
  88. package/dist/dom-font-inliner.d.ts +45 -0
  89. package/dist/dom-font-inliner.js +148 -0
  90. package/dist/dom-patch-resolver.d.ts +52 -0
  91. package/dist/dom-patch-resolver.js +242 -0
  92. package/dist/dom-serializer.d.ts +82 -0
  93. package/dist/dom-serializer.js +378 -0
  94. package/dist/element-capture.d.ts +1 -41
  95. package/dist/element-capture.js +202 -446
  96. package/dist/env-validation.d.ts +5 -0
  97. package/dist/env-validation.js +29 -0
  98. package/dist/execution-schema.d.ts +4423 -0
  99. package/dist/execution-schema.js +507 -0
  100. package/dist/execution-types.d.ts +886 -0
  101. package/dist/execution-types.js +65 -0
  102. package/dist/fonts-loader.d.ts +14 -0
  103. package/dist/fonts-loader.js +55 -0
  104. package/dist/hybrid-navigator.js +12 -12
  105. package/dist/index.d.ts +9 -6
  106. package/dist/index.js +10 -4
  107. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  108. package/dist/legacy/agent-action-recovery.js +370 -0
  109. package/dist/legacy/agent-message-utils.d.ts +21 -0
  110. package/dist/legacy/agent-message-utils.js +77 -0
  111. package/dist/legacy/agent-url-utils.d.ts +30 -0
  112. package/dist/legacy/agent-url-utils.js +138 -0
  113. package/dist/legacy/agent.d.ts +226 -0
  114. package/dist/legacy/agent.js +6666 -0
  115. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  116. package/dist/legacy/clip-orchestrator.js +957 -0
  117. package/dist/legacy/credential-templates.d.ts +5 -0
  118. package/dist/legacy/credential-templates.js +60 -0
  119. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  120. package/dist/legacy/hybrid-navigator.js +468 -0
  121. package/dist/legacy/llm-usage.d.ts +17 -0
  122. package/dist/legacy/llm-usage.js +45 -0
  123. package/dist/legacy/prompt-cache.d.ts +10 -0
  124. package/dist/legacy/prompt-cache.js +24 -0
  125. package/dist/legacy/prompts.d.ts +175 -0
  126. package/dist/legacy/prompts.js +1038 -0
  127. package/dist/legacy/tools.d.ts +4 -0
  128. package/dist/legacy/tools.js +216 -0
  129. package/dist/legacy/video-agent.d.ts +143 -0
  130. package/dist/legacy/video-agent.js +4788 -0
  131. package/dist/legacy/video-observation.d.ts +36 -0
  132. package/dist/legacy/video-observation.js +192 -0
  133. package/dist/legacy/video-planner.d.ts +12 -0
  134. package/dist/legacy/video-planner.js +501 -0
  135. package/dist/legacy/video-prompts.d.ts +37 -0
  136. package/dist/legacy/video-prompts.js +569 -0
  137. package/dist/legacy/video-tools.d.ts +3 -0
  138. package/dist/legacy/video-tools.js +59 -0
  139. package/dist/legacy/video-variant-state.d.ts +29 -0
  140. package/dist/legacy/video-variant-state.js +80 -0
  141. package/dist/legacy/vision-model.d.ts +17 -0
  142. package/dist/legacy/vision-model.js +74 -0
  143. package/dist/llm-healer.d.ts +63 -0
  144. package/dist/llm-healer.js +166 -0
  145. package/dist/llm-provider.d.ts +29 -0
  146. package/dist/llm-provider.js +80 -0
  147. package/dist/logger.d.ts +6 -2
  148. package/dist/logger.js +15 -1
  149. package/dist/mockup-html.js +35 -25
  150. package/dist/mockup.d.ts +95 -2
  151. package/dist/mockup.js +427 -166
  152. package/dist/mouse-animation.d.ts +2 -2
  153. package/dist/mouse-animation.js +34 -20
  154. package/dist/opcode-actions.d.ts +42 -0
  155. package/dist/opcode-actions.js +511 -0
  156. package/dist/opcode-runner.d.ts +51 -0
  157. package/dist/opcode-runner.js +770 -0
  158. package/dist/openrouter-client.d.ts +40 -0
  159. package/dist/openrouter-client.js +16 -0
  160. package/dist/overlay-engine.d.ts +24 -0
  161. package/dist/overlay-engine.js +176 -0
  162. package/dist/postcondition.d.ts +16 -0
  163. package/dist/postcondition.js +269 -0
  164. package/dist/program-patcher.d.ts +25 -0
  165. package/dist/program-patcher.js +44 -0
  166. package/dist/prompts.d.ts +13 -5
  167. package/dist/prompts.js +224 -351
  168. package/dist/provider-config.d.ts +12 -0
  169. package/dist/provider-config.js +15 -0
  170. package/dist/recovery-chain.d.ts +37 -0
  171. package/dist/recovery-chain.js +350 -0
  172. package/dist/remote-browser.d.ts +28 -4
  173. package/dist/remote-browser.js +60 -5
  174. package/dist/safari-browser-bar.d.ts +15 -0
  175. package/dist/safari-browser-bar.js +95 -0
  176. package/dist/safari-toolbar-asset.d.ts +15 -0
  177. package/dist/safari-toolbar-asset.js +12 -0
  178. package/dist/security.d.ts +2 -1
  179. package/dist/security.js +49 -10
  180. package/dist/selector-resolver.d.ts +34 -0
  181. package/dist/selector-resolver.js +181 -0
  182. package/dist/semantic-resolver.d.ts +35 -0
  183. package/dist/semantic-resolver.js +161 -0
  184. package/dist/server-capture-runtime.d.ts +5 -3
  185. package/dist/server-capture-runtime.js +42 -95
  186. package/dist/server-credit-usage.d.ts +2 -2
  187. package/dist/server-project-webhooks.d.ts +15 -1
  188. package/dist/server-project-webhooks.js +34 -8
  189. package/dist/server-screenshot-watermark.js +27 -5
  190. package/dist/session-profile.js +164 -1
  191. package/dist/sf-pro-symbols.d.ts +1 -0
  192. package/dist/sf-pro-symbols.js +55 -0
  193. package/dist/skill-packaging.d.ts +28 -0
  194. package/dist/skill-packaging.js +169 -0
  195. package/dist/smart-wait.d.ts +27 -0
  196. package/dist/smart-wait.js +81 -0
  197. package/dist/status-bar-render.d.ts +20 -0
  198. package/dist/status-bar-render.js +410 -0
  199. package/dist/status-bar.d.ts +9 -0
  200. package/dist/status-bar.js +298 -14
  201. package/dist/svg-browser-bar.d.ts +33 -0
  202. package/dist/svg-browser-bar.js +206 -0
  203. package/dist/svg-status-bar.d.ts +36 -0
  204. package/dist/svg-status-bar.js +597 -0
  205. package/dist/svg-text.d.ts +61 -0
  206. package/dist/svg-text.js +118 -0
  207. package/dist/tools.js +89 -451
  208. package/dist/types.d.ts +240 -5
  209. package/dist/types.js +23 -1
  210. package/dist/v2/action-verifier.d.ts +29 -0
  211. package/dist/v2/action-verifier.js +133 -0
  212. package/dist/v2/alt-text.d.ts +26 -0
  213. package/dist/v2/alt-text.js +55 -0
  214. package/dist/v2/benchmark.d.ts +59 -0
  215. package/dist/v2/benchmark.js +135 -0
  216. package/dist/v2/capture-strategy.d.ts +30 -0
  217. package/dist/v2/capture-strategy.js +67 -0
  218. package/dist/v2/capture-verification.d.ts +35 -0
  219. package/dist/v2/capture-verification.js +95 -0
  220. package/dist/v2/circuit-breaker.d.ts +42 -0
  221. package/dist/v2/circuit-breaker.js +119 -0
  222. package/dist/v2/cli-runner-local.d.ts +11 -0
  223. package/dist/v2/cli-runner-local.js +91 -0
  224. package/dist/v2/cli-runner.d.ts +34 -0
  225. package/dist/v2/cli-runner.js +300 -0
  226. package/dist/v2/compiler-prompts.d.ts +27 -0
  227. package/dist/v2/compiler-prompts.js +123 -0
  228. package/dist/v2/compiler.d.ts +37 -0
  229. package/dist/v2/compiler.js +147 -0
  230. package/dist/v2/explorer.d.ts +41 -0
  231. package/dist/v2/explorer.js +56 -0
  232. package/dist/v2/index.d.ts +37 -0
  233. package/dist/v2/index.js +31 -0
  234. package/dist/v2/llm-healer.d.ts +62 -0
  235. package/dist/v2/llm-healer.js +166 -0
  236. package/dist/v2/llm-provider.d.ts +29 -0
  237. package/dist/v2/llm-provider.js +80 -0
  238. package/dist/v2/opcode-runner.d.ts +47 -0
  239. package/dist/v2/opcode-runner.js +634 -0
  240. package/dist/v2/overlay-engine.d.ts +24 -0
  241. package/dist/v2/overlay-engine.js +150 -0
  242. package/dist/v2/postcondition.d.ts +16 -0
  243. package/dist/v2/postcondition.js +249 -0
  244. package/dist/v2/program-patcher.d.ts +25 -0
  245. package/dist/v2/program-patcher.js +44 -0
  246. package/dist/v2/recovery-chain.d.ts +30 -0
  247. package/dist/v2/recovery-chain.js +368 -0
  248. package/dist/v2/schema.d.ts +2580 -0
  249. package/dist/v2/schema.js +295 -0
  250. package/dist/v2/selector-resolver.d.ts +34 -0
  251. package/dist/v2/selector-resolver.js +181 -0
  252. package/dist/v2/semantic-resolver.d.ts +35 -0
  253. package/dist/v2/semantic-resolver.js +161 -0
  254. package/dist/v2/smart-wait.d.ts +27 -0
  255. package/dist/v2/smart-wait.js +81 -0
  256. package/dist/v2/types.d.ts +444 -0
  257. package/dist/v2/types.js +19 -0
  258. package/dist/v2/web-playwright-local.d.ts +69 -0
  259. package/dist/v2/web-playwright-local.js +392 -0
  260. package/dist/version.d.ts +1 -0
  261. package/dist/version.js +5 -0
  262. package/dist/video-agent.js +18 -13
  263. package/dist/video-planner.js +2 -1
  264. package/dist/video-prompts.js +3 -3
  265. package/dist/web-playwright-local.d.ts +126 -0
  266. package/dist/web-playwright-local.js +819 -0
  267. package/dist/ws-auth.js +4 -1
  268. package/dist/ws-broadcast.d.ts +34 -0
  269. package/dist/ws-broadcast.js +85 -0
  270. package/dist/ws-connection-limits.d.ts +12 -0
  271. package/dist/ws-connection-limits.js +44 -0
  272. package/dist/ws-handler-utils.d.ts +32 -0
  273. package/dist/ws-handler-utils.js +139 -0
  274. package/dist/ws-handler.js +294 -164
  275. package/dist/ws-metrics-server.d.ts +9 -0
  276. package/dist/ws-metrics-server.js +31 -0
  277. package/dist/ws-server.js +41 -1
  278. package/package.json +51 -34
@@ -37,21 +37,148 @@ export function decryptPresetCredentials(value) {
37
37
  }
38
38
  return normalizePresetCredentials(value);
39
39
  }
40
+ export function normalizePresetAuthCookies(value) {
41
+ if (!Array.isArray(value))
42
+ return undefined;
43
+ const cleaned = [];
44
+ for (const entry of value) {
45
+ if (!entry || typeof entry !== 'object')
46
+ continue;
47
+ const candidate = entry;
48
+ const name = typeof candidate.name === 'string' ? candidate.name.trim() : '';
49
+ const cookieValue = typeof candidate.value === 'string' ? candidate.value : '';
50
+ const domain = typeof candidate.domain === 'string' ? candidate.domain.trim().toLowerCase() : '';
51
+ if (!name || !cookieValue || !domain)
52
+ continue;
53
+ const path = typeof candidate.path === 'string' && candidate.path.trim()
54
+ ? candidate.path.trim()
55
+ : undefined;
56
+ cleaned.push({
57
+ name,
58
+ value: cookieValue,
59
+ domain,
60
+ ...(path ? { path } : {}),
61
+ });
62
+ }
63
+ return cleaned.length > 0 ? cleaned : undefined;
64
+ }
65
+ export function encryptPresetAuthCookies(cookies) {
66
+ return encrypt(JSON.stringify(cookies), getSecret());
67
+ }
68
+ export function decryptPresetAuthCookies(value) {
69
+ if (!value)
70
+ return undefined;
71
+ if (isEncryptedEnvelope(value)) {
72
+ const decrypted = decrypt(value, getSecret());
73
+ return normalizePresetAuthCookies(JSON.parse(decrypted));
74
+ }
75
+ return normalizePresetAuthCookies(value);
76
+ }
77
+ export function hasStoredAuthCookies(raw) {
78
+ if (!raw || typeof raw !== 'object')
79
+ return false;
80
+ return raw.authCookies != null;
81
+ }
82
+ export function buildAuthCookiesMeta(cookies) {
83
+ const domainSet = new Set();
84
+ for (const cookie of cookies) {
85
+ if (cookie.domain)
86
+ domainSet.add(cookie.domain);
87
+ }
88
+ return {
89
+ count: cookies.length,
90
+ domains: Array.from(domainSet),
91
+ };
92
+ }
93
+ /**
94
+ * Normalize a Playwright storageState payload, dropping anything that doesn't
95
+ * match the expected shape. Returns undefined if the payload is empty (no
96
+ * cookies and no origin data).
97
+ */
98
+ export function normalizeStorageState(value) {
99
+ if (!value || typeof value !== 'object')
100
+ return undefined;
101
+ const candidate = value;
102
+ const cookies = Array.isArray(candidate.cookies)
103
+ ? candidate.cookies
104
+ .filter((c) => !!c && typeof c === 'object' && typeof c.name === 'string' && typeof c.value === 'string' && typeof c.domain === 'string')
105
+ .map((c) => ({
106
+ name: c.name,
107
+ value: c.value,
108
+ domain: c.domain,
109
+ path: typeof c.path === 'string' ? c.path : '/',
110
+ expires: typeof c.expires === 'number' ? c.expires : -1,
111
+ httpOnly: !!c.httpOnly,
112
+ secure: !!c.secure,
113
+ sameSite: c.sameSite === 'Strict' || c.sameSite === 'Lax' || c.sameSite === 'None'
114
+ ? c.sameSite
115
+ : 'Lax',
116
+ }))
117
+ : [];
118
+ const origins = Array.isArray(candidate.origins)
119
+ ? candidate.origins
120
+ .filter((o) => !!o && typeof o === 'object' && typeof o.origin === 'string')
121
+ .map((o) => ({
122
+ origin: o.origin,
123
+ localStorage: Array.isArray(o.localStorage)
124
+ ? o.localStorage
125
+ .filter((entry) => entry && typeof entry === 'object' && typeof entry.name === 'string')
126
+ .map((entry) => ({ name: entry.name, value: typeof entry.value === 'string' ? entry.value : '' }))
127
+ : [],
128
+ }))
129
+ : [];
130
+ if (cookies.length === 0 && origins.length === 0)
131
+ return undefined;
132
+ return { cookies, origins };
133
+ }
134
+ export function encryptStorageState(state) {
135
+ return encrypt(JSON.stringify(state), getSecret());
136
+ }
137
+ export function decryptStorageState(value) {
138
+ if (!value)
139
+ return undefined;
140
+ if (isEncryptedEnvelope(value)) {
141
+ const decrypted = decrypt(value, getSecret());
142
+ return normalizeStorageState(JSON.parse(decrypted));
143
+ }
144
+ return normalizeStorageState(value);
145
+ }
146
+ export function summarizeStorageState(state) {
147
+ const domainSet = new Set();
148
+ for (const cookie of state.cookies) {
149
+ if (cookie.domain)
150
+ domainSet.add(cookie.domain);
151
+ }
152
+ return {
153
+ cookieCount: state.cookies.length,
154
+ originCount: state.origins.length,
155
+ domains: Array.from(domainSet),
156
+ };
157
+ }
40
158
  export function preparePresetConfigForStorage(config) {
41
159
  const credentials = normalizePresetCredentials(config.credentials);
160
+ const authCookies = normalizePresetAuthCookies(config.authCookies);
42
161
  return {
43
162
  ...config,
44
163
  ...(credentials
45
164
  ? { credentials: encryptPresetCredentials(credentials) }
46
165
  : { credentials: undefined }),
166
+ ...(authCookies
167
+ ? {
168
+ authCookies: encryptPresetAuthCookies(authCookies),
169
+ authCookiesMeta: buildAuthCookiesMeta(authCookies),
170
+ }
171
+ : { authCookies: undefined, authCookiesMeta: undefined }),
47
172
  };
48
173
  }
49
174
  export function hydratePresetConfigFromStorage(raw) {
50
175
  const config = (raw && typeof raw === 'object' ? raw : {});
51
176
  const credentials = decryptPresetCredentials(config.credentials);
177
+ const authCookies = decryptPresetAuthCookies(config.authCookies);
52
178
  return {
53
179
  ...config,
54
180
  ...(credentials ? { credentials } : { credentials: undefined }),
181
+ ...(authCookies ? { authCookies } : { authCookies: undefined }),
55
182
  };
56
183
  }
57
184
  //# sourceMappingURL=capture-preset-credentials.js.map
@@ -1,5 +1,4 @@
1
1
  import { type ScreenshotPageRunInput, type ScreenshotVariantExecution } from "./capture-run-optimizer.js";
2
- import { findVariantPageDefinitionIssues } from "./capture-page-identity.js";
3
2
  export type CaptureTheme = "light" | "dark";
4
3
  export interface CaptureViewport {
5
4
  width: number;
@@ -40,9 +39,10 @@ export interface CaptureRequestPlan<TTarget extends CaptureTargetLike = CaptureT
40
39
  elements: TElement[];
41
40
  pageRuns: ScreenshotPageRunInput[];
42
41
  variantPlan: ScreenshotVariantExecution[];
43
- pageDefinitionIssues: ReturnType<typeof findVariantPageDefinitionIssues>;
44
42
  requestedCaptureCount: number;
45
43
  totalCaptures: number;
44
+ /** Warnings about non-fatal issues (e.g. unknown device frames). */
45
+ warnings: string[];
46
46
  }
47
47
  export declare function hasNonEmptyCapturePrompt(prompt: string | null | undefined): boolean;
48
48
  export declare function getCapturePromptValidationError(input: Pick<CaptureRequestPlanInput, "url" | "prompt" | "pages">): string | null;
@@ -1,11 +1,34 @@
1
1
  import { resolveDeviceFrameDescriptor, } from "./mockup.js";
2
2
  import { buildScreenshotVariantPlan, } from "./capture-run-optimizer.js";
3
- import { findVariantPageDefinitionIssues } from "./capture-page-identity.js";
4
3
  function normalizeViewportDimension(value) {
5
4
  if (!Number.isFinite(value))
6
5
  return 1;
7
6
  return Math.max(1, Math.round(value));
8
7
  }
8
+ function viewportAspectRatio(viewport) {
9
+ return viewport.width / Math.max(1, viewport.height);
10
+ }
11
+ function shouldPreserveStoredDeviceViewport(storedViewport, resolvedViewport) {
12
+ const sameOrientation = (storedViewport.width >= storedViewport.height)
13
+ === (resolvedViewport.width >= resolvedViewport.height);
14
+ if (!sameOrientation) {
15
+ return false;
16
+ }
17
+ if (storedViewport.width > resolvedViewport.width
18
+ || storedViewport.height > resolvedViewport.height) {
19
+ return false;
20
+ }
21
+ const widthDelta = Math.abs(storedViewport.width - resolvedViewport.width);
22
+ const heightDelta = Math.abs(storedViewport.height - resolvedViewport.height);
23
+ const differsMeaningfully = widthDelta > 24 || heightDelta > 24;
24
+ if (!differsMeaningfully) {
25
+ return false;
26
+ }
27
+ const resolvedAspect = viewportAspectRatio(resolvedViewport);
28
+ const storedAspect = viewportAspectRatio(storedViewport);
29
+ const aspectDelta = Math.abs(storedAspect - resolvedAspect) / resolvedAspect;
30
+ return aspectDelta <= 0.015;
31
+ }
9
32
  function getRequestedOrientation(target) {
10
33
  const orientation = target.mockupOptions?.orientation;
11
34
  return orientation === "portrait" || orientation === "landscape"
@@ -18,17 +41,18 @@ async function resolveDeviceViewport(deviceId, orientation) {
18
41
  });
19
42
  if (!descriptor)
20
43
  return null;
21
- return {
22
- label: descriptor.name,
23
- viewport: {
24
- width: normalizeViewportDimension(descriptor.screen.logicalWidth
25
- - (descriptor.safeArea.left ?? 0)
26
- - (descriptor.safeArea.right ?? 0)),
27
- height: normalizeViewportDimension(descriptor.screen.logicalHeight
28
- - descriptor.safeArea.top
29
- - descriptor.safeArea.bottom),
30
- },
44
+ // Compute viewport from screen dimensions minus safe areas.
45
+ // The device config's `viewport` field is NOT reliable (Supabase configs may
46
+ // store stale/wrong values like 400x740). Always compute from logicalWidth/Height.
47
+ const viewport = {
48
+ width: normalizeViewportDimension(descriptor.screen.logicalWidth
49
+ - (descriptor.safeArea.left ?? 0)
50
+ - (descriptor.safeArea.right ?? 0)),
51
+ height: normalizeViewportDimension(descriptor.screen.logicalHeight
52
+ - descriptor.safeArea.top
53
+ - descriptor.safeArea.bottom),
31
54
  };
55
+ return { label: descriptor.name, viewport };
32
56
  }
33
57
  export function hasNonEmptyCapturePrompt(prompt) {
34
58
  return typeof prompt === "string" && prompt.trim().length > 0;
@@ -122,9 +146,19 @@ export async function normalizeCaptureTargets(input) {
122
146
  }
123
147
  const deviceViewportCache = new Map();
124
148
  const resolvedTargets = await Promise.all(targets.map(async (target) => {
125
- if (!target.deviceFrame)
149
+ if (!target.deviceFrame) {
150
+ console.log(`[capture-plan] target ${target.id}: no deviceFrame, keeping stored viewport ${target.viewport.width}x${target.viewport.height}`);
126
151
  return target;
127
- const orientation = getRequestedOrientation(target);
152
+ }
153
+ let orientation = getRequestedOrientation(target);
154
+ // Fallback: infer orientation from stored viewport aspect ratio when
155
+ // mockupOptions.orientation is missing (e.g. legacy presets).
156
+ if (!orientation && target.viewport) {
157
+ orientation = target.viewport.height > target.viewport.width
158
+ ? "portrait"
159
+ : "landscape";
160
+ console.log(`[capture-plan] target ${target.id}: mockupOptions.orientation missing (mockupOptions=${JSON.stringify(target.mockupOptions)}), inferred "${orientation}" from stored viewport ${target.viewport.width}x${target.viewport.height}`);
161
+ }
128
162
  const cacheKey = `${target.deviceFrame}:${orientation ?? "default"}`;
129
163
  let pending = deviceViewportCache.get(cacheKey);
130
164
  if (!pending) {
@@ -132,8 +166,15 @@ export async function normalizeCaptureTargets(input) {
132
166
  deviceViewportCache.set(cacheKey, pending);
133
167
  }
134
168
  const resolved = await pending;
135
- if (!resolved)
169
+ if (!resolved) {
170
+ console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): device NOT FOUND, keeping stored viewport ${target.viewport.width}x${target.viewport.height}`);
171
+ return { ...target, _deviceNotFound: true };
172
+ }
173
+ if (shouldPreserveStoredDeviceViewport(target.viewport, resolved.viewport)) {
174
+ console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): keeping custom viewport ${target.viewport.width}x${target.viewport.height} instead of native ${resolved.viewport.width}x${resolved.viewport.height}`);
136
175
  return target;
176
+ }
177
+ console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): resolved viewport ${resolved.viewport.width}x${resolved.viewport.height} (was ${target.viewport.width}x${target.viewport.height})`);
137
178
  return {
138
179
  ...target,
139
180
  viewport: resolved.viewport,
@@ -197,9 +238,16 @@ export async function resolveCaptureRequestPlan(input) {
197
238
  themes,
198
239
  pages: pageRuns,
199
240
  });
200
- const pageDefinitionIssues = findVariantPageDefinitionIssues(pageRuns);
201
241
  const requestedCaptureCount = pageRuns.length * targets.length * langs.length * themes.length
202
242
  + elements.length * langs.length * themes.length;
243
+ // Collect warnings for device frames that could not be resolved
244
+ const warnings = [];
245
+ for (const target of targets) {
246
+ if (target._deviceNotFound) {
247
+ warnings.push(`Device frame "${target.deviceFrame}" not found — using stored viewport ${target.viewport.width}x${target.viewport.height} as fallback.`);
248
+ delete target._deviceNotFound;
249
+ }
250
+ }
203
251
  return {
204
252
  targets,
205
253
  outputScale,
@@ -208,9 +256,9 @@ export async function resolveCaptureRequestPlan(input) {
208
256
  elements,
209
257
  pageRuns,
210
258
  variantPlan,
211
- pageDefinitionIssues,
212
259
  requestedCaptureCount,
213
260
  totalCaptures: requestedCaptureCount,
261
+ warnings,
214
262
  };
215
263
  }
216
264
  //# sourceMappingURL=capture-request-plan.js.map
@@ -50,27 +50,27 @@ const ELEMENT_ASSIGNMENT_STOP_WORDS = new Set([
50
50
  ]);
51
51
  const ELEMENT_DOMAIN_HINTS = [
52
52
  {
53
- element: /\b(gallery|screenshot|screenshots|capture|captures|thumbnail|download all|filter by preset|filtrer par preset)\b/i,
54
- page: /\b(gallery|screenshot|screenshots|capture|captures)\b/i,
53
+ element: /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|thumbnail|thumbnails)\b/i,
54
+ page: /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?)\b/i,
55
55
  score: 5,
56
56
  },
57
57
  {
58
- element: /\b(preset|presets|template|templates|mockup|iphone|instructions|editor|configuration|settings)\b/i,
59
- page: /\b(preset|presets|template|templates|mockup|editor|configuration|settings)\b/i,
58
+ element: /\b(edit|editor|editing|configuration|configure|settings?|preferences|details?|detail|instructions?|form|compose|customi[sz]e)\b/i,
59
+ page: /\b(edit|editor|editing|configuration|configure|settings?|preferences|details?|detail|instructions?|form|compose|customi[sz]e)\b/i,
60
60
  score: 5,
61
61
  },
62
62
  {
63
- element: /\b(assistant|chat|conversation|prompt ia|ai assistant)\b/i,
64
- page: /\b(assistant|chat|conversation)\b/i,
63
+ element: /\b(assistant|chat|conversation|message|messages|inbox|thread)\b/i,
64
+ page: /\b(assistant|chat|conversation|message|messages|inbox|thread)\b/i,
65
65
  score: 5,
66
66
  },
67
67
  {
68
- element: /\b(home|homepage|dashboard|accueil)\b/i,
69
- page: /\b(home|homepage|dashboard|accueil)\b/i,
68
+ element: /\b(home|homepage|landing|dashboard|welcome|overview|accueil)\b/i,
69
+ page: /\b(home|homepage|landing|dashboard|welcome|overview|accueil)\b/i,
70
70
  score: 4,
71
71
  },
72
72
  ];
73
- const MODAL_CAPTURE_RE = /\b(modal|dialog|popup|drawer|template|picker|gallery|selection|select|choose)\b/i;
73
+ const MODAL_CAPTURE_RE = /\b(modal|dialog|popup|drawer|overlay|selection|select|choose|pick|picker)\b/i;
74
74
  const CONFIG_CAPTURE_RE = /\b(edit|editor|config|configuration|configure|settings|instruction|instructions|textarea|form|detail|details|modify|modifier|edition)\b/i;
75
75
  const MODAL_OPENER_RE = /\b(new|nouveau|create|creer|cr[eé]er|add|ajouter|open|ouvrir|plus)\b/i;
76
76
  function uniqueInOrder(values) {
@@ -140,24 +140,24 @@ function inferCaptureIntent(values) {
140
140
  tokens: normalizeNavigationTokens(values),
141
141
  modalSelection: MODAL_CAPTURE_RE.test(joined),
142
142
  configuration: CONFIG_CAPTURE_RE.test(joined),
143
- domain: inferCaptureDomain(joined),
143
+ structure: inferCaptureStructure(joined),
144
144
  };
145
145
  }
146
- function inferCaptureDomain(value) {
146
+ function inferCaptureStructure(value) {
147
147
  if (/\b(assistant|chat|conversation|copilot|prompt ia|ai assistant)\b/i.test(value)) {
148
- return "assistant";
148
+ return "conversation";
149
149
  }
150
- if (/\b(gallery|screenshots?|captures?|thumbnail|download all|filter by preset)\b/i.test(value)) {
151
- return "gallery";
150
+ if (/\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|screenshots?|captures?|thumbnail)\b/i.test(value)) {
151
+ return "overview";
152
152
  }
153
- if (/\b(preset|template|editor|mockup|instructions?|configuration|configure)\b/i.test(value)) {
154
- return "preset";
153
+ if (/\b(edit|editor|detail|details|instructions?|configuration|configure|form|compose|customi[sz]e)\b/i.test(value)) {
154
+ return "detail";
155
155
  }
156
156
  if (/\b(settings|preferences|appearance|language|theme|param[eè]tres)\b/i.test(value)) {
157
- return "settings";
157
+ return "preferences";
158
158
  }
159
- if (/\b(home|homepage|dashboard|accueil)\b/i.test(value)) {
160
- return "home";
159
+ if (/\b(home|homepage|landing|dashboard|welcome|accueil)\b/i.test(value)) {
160
+ return "landing";
161
161
  }
162
162
  return "unknown";
163
163
  }
@@ -212,9 +212,14 @@ export function urlsShareOrigin(left, right) {
212
212
  }
213
213
  export function resolveCaptureResumeUrl(params) {
214
214
  const normalizedPageId = params.pageId ?? "main";
215
- const checkpoint = [...params.checkpoints]
216
- .reverse()
217
- .find((entry) => entry.pageId === normalizedPageId);
215
+ const checkpoints = [...params.checkpoints].reverse();
216
+ const checkpoint = checkpoints.find((entry) => entry.pageId === normalizedPageId);
217
+ const sameOriginCheckpoint = checkpoints.find((entry) => entry.pageId !== normalizedPageId
218
+ && [
219
+ entry.resumeUrl,
220
+ entry.url,
221
+ entry.canonicalUrl,
222
+ ].some((candidate) => urlsShareOrigin(candidate, params.pageUrl)));
218
223
  const candidates = [
219
224
  params.cursorResumeUrl,
220
225
  checkpoint?.resumeUrl,
@@ -223,7 +228,11 @@ export function resolveCaptureResumeUrl(params) {
223
228
  params.currentUrl,
224
229
  params.sessionProfile?.lastKnownUrl,
225
230
  params.sessionProfile?.validatedStartUrl,
226
- params.pageUrl,
231
+ ];
232
+ const sameOriginCheckpointCandidates = [
233
+ sameOriginCheckpoint?.resumeUrl,
234
+ sameOriginCheckpoint?.url,
235
+ sameOriginCheckpoint?.canonicalUrl,
227
236
  ];
228
237
  for (const candidate of candidates) {
229
238
  if (!candidate)
@@ -232,6 +241,12 @@ export function resolveCaptureResumeUrl(params) {
232
241
  return candidate;
233
242
  }
234
243
  if (params.pageIndex > 0) {
244
+ for (const candidate of sameOriginCheckpointCandidates) {
245
+ if (!candidate)
246
+ continue;
247
+ if (urlsShareOrigin(candidate, params.pageUrl))
248
+ return candidate;
249
+ }
235
250
  for (const candidate of candidates) {
236
251
  if (!candidate)
237
252
  continue;
@@ -344,7 +359,7 @@ export function shouldReuseLivePageState(params) {
344
359
  if (!currentOrigin || !pageOrigin || currentOrigin !== pageOrigin) {
345
360
  return false;
346
361
  }
347
- // Only skip the reset when the page uses the preset root URL.
362
+ // Only skip the reset when the page uses the shared root URL for the flow.
348
363
  // Explicit per-page URLs still navigate directly for determinism.
349
364
  return params.pageUrl === params.presetRootUrl;
350
365
  }
@@ -507,10 +522,10 @@ export function buildVisibleControlNavigationPlan(params) {
507
522
  if (targetIntent.modalSelection && MODAL_OPENER_RE.test(normalizedLabel)) {
508
523
  score += 5;
509
524
  }
510
- if (/\bpreset|template\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\bpreset|template\b/i.test(normalizedLabel)) {
525
+ if (/\b(edit|editor|detail|details|configuration|configure|settings?|preferences|form)\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\b(edit|editor|detail|details|configuration|configure|settings?|preferences|form)\b/i.test(normalizedLabel)) {
511
526
  score += 4;
512
527
  }
513
- if (/\bgallery|capture|captures|screenshot|screenshots\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\bgallery|capture|captures|screenshot|screenshots\b/i.test(normalizedLabel)) {
528
+ if (/\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|capture|captures|screenshot|screenshots)\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|capture|captures|screenshot|screenshots)\b/i.test(normalizedLabel)) {
514
529
  score += 4;
515
530
  }
516
531
  if (element.visibilityState === "full")
@@ -562,13 +577,13 @@ export function shouldAttemptSequentialReadyCheck(params) {
562
577
  }
563
578
  const intentOverlap = countOverlap(previousIntent.tokens, targetIntent.tokens);
564
579
  if (countOverlap(currentPathTokens, targetPathTokens) > 0) {
565
- // Many AutoKap captures share the same base route (for example /home) while
566
- // representing materially different UI states. Do not use the fast ready
567
- // check on same-path transitions unless the target is clearly a close
568
- // continuation of the previous page.
569
- if (previousIntent.domain !== "unknown"
570
- && targetIntent.domain !== "unknown"
571
- && previousIntent.domain !== targetIntent.domain) {
580
+ // Many apps reuse the same base route while representing materially
581
+ // different UI states. Do not use the fast ready check on same-path
582
+ // transitions unless the target is clearly a close continuation of the
583
+ // previous page.
584
+ if (previousIntent.structure !== "unknown"
585
+ && targetIntent.structure !== "unknown"
586
+ && previousIntent.structure !== targetIntent.structure) {
572
587
  return false;
573
588
  }
574
589
  return intentOverlap >= 2;
@@ -5,12 +5,16 @@ export interface ScreenshotSelectorMemoryUpdate {
5
5
  source: 'agent' | 'deterministic' | 'manual';
6
6
  success: boolean;
7
7
  }
8
+ /** Map a viewport width to a device category for selector memory scoping. */
9
+ export declare function getViewportCategory(width: number): 'mobile' | 'tablet' | 'desktop';
8
10
  export declare function loadScreenshotSelectorMemory(supabase: SupabaseClient, params: {
9
11
  projectId: string | null;
10
12
  presetId?: string | null;
11
13
  domain: string;
12
14
  lang: string;
13
15
  theme: 'light' | 'dark';
16
+ /** Optional viewport width — when provided, filters selectors by viewport category. */
17
+ viewportWidth?: number | null;
14
18
  }): Promise<Record<string, string[]>>;
15
19
  export declare function persistScreenshotSelectorMemoryUpdates(supabase: SupabaseClient, params: {
16
20
  projectId: string | null;
@@ -18,6 +22,7 @@ export declare function persistScreenshotSelectorMemoryUpdates(supabase: Supabas
18
22
  domain: string;
19
23
  lang: string;
20
24
  theme: 'light' | 'dark';
25
+ viewportWidth?: number | null;
21
26
  }, updates: ScreenshotSelectorMemoryUpdate[]): Promise<void>;
22
27
  export declare function extractSelectorUpdates(actions: Array<{
23
28
  action: string;
@@ -59,6 +59,14 @@ function extractPathTokens(rawUrl) {
59
59
  return normalizeNavigationTokens(rawUrl.split('/'));
60
60
  }
61
61
  }
62
+ /** Map a viewport width to a device category for selector memory scoping. */
63
+ export function getViewportCategory(width) {
64
+ if (width < 768)
65
+ return 'mobile';
66
+ if (width < 1024)
67
+ return 'tablet';
68
+ return 'desktop';
69
+ }
62
70
  export async function loadScreenshotSelectorMemory(supabase, params) {
63
71
  const selectFields = 'step_signature, selector, confidence, last_success_at';
64
72
  const grouped = {};
@@ -75,6 +83,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
75
83
  }
76
84
  }
77
85
  };
86
+ const viewportCategory = params.viewportWidth
87
+ ? getViewportCategory(params.viewportWidth)
88
+ : null;
78
89
  if (params.presetId) {
79
90
  let query = supabase
80
91
  .from('screenshot_step_memory')
@@ -89,6 +100,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
89
100
  if (params.projectId) {
90
101
  query = query.eq('project_id', params.projectId);
91
102
  }
103
+ if (viewportCategory) {
104
+ query = query.or(`viewport_category.eq.${viewportCategory},viewport_category.is.null`);
105
+ }
92
106
  const { data } = await query;
93
107
  if (data && data.length > 0) {
94
108
  appendRows(data);
@@ -105,6 +119,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
105
119
  if (params.projectId) {
106
120
  fallbackQuery = fallbackQuery.eq('project_id', params.projectId);
107
121
  }
122
+ if (viewportCategory) {
123
+ fallbackQuery = fallbackQuery.or(`viewport_category.eq.${viewportCategory},viewport_category.is.null`);
124
+ }
108
125
  const { data: fallbackData } = await fallbackQuery;
109
126
  if (fallbackData) {
110
127
  appendRows(fallbackData);
@@ -145,6 +162,7 @@ export async function persistScreenshotSelectorMemoryUpdates(supabase, params, u
145
162
  domain: params.domain,
146
163
  lang: params.lang,
147
164
  theme: params.theme,
165
+ viewport_category: params.viewportWidth ? getViewportCategory(params.viewportWidth) : null,
148
166
  step_signature: update.stepSignature,
149
167
  selector: update.selector,
150
168
  source: update.source,
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Capture Agent — Capture Strategy
3
+ *
4
+ * Abstraction over the supported media modes: screenshot and clip.
5
+ * The opcode runner is identical for all three — only the capture
6
+ * opcodes dispatch to different strategies.
7
+ */
8
+ import type { ArtifactSpec, ArtifactResult, RuntimeAdapter, MediaMode } from './execution-types.js';
9
+ export interface CaptureStrategy {
10
+ readonly mediaMode: MediaMode;
11
+ /** Prepare the adapter for capture (e.g. lock viewport, inject cursor overlay) */
12
+ prepare(adapter: RuntimeAdapter, spec: ArtifactSpec): Promise<void>;
13
+ /** Perform the capture and return the raw artifact */
14
+ capture(adapter: RuntimeAdapter, spec: ArtifactSpec): Promise<ArtifactResult>;
15
+ /** Post-process the artifact (mockup frame, status bar, format conversion) */
16
+ postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
17
+ }
18
+ export declare class ScreenshotStrategy implements CaptureStrategy {
19
+ readonly mediaMode: "screenshot";
20
+ prepare(_adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
21
+ capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
22
+ postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
23
+ }
24
+ export declare class ClipStrategy implements CaptureStrategy {
25
+ readonly mediaMode: "clip";
26
+ prepare(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
27
+ capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
28
+ postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
29
+ }
30
+ export declare class DomStrategy implements CaptureStrategy {
31
+ readonly mediaMode: "dom";
32
+ prepare(_adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
33
+ capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
34
+ postProcess(artifact: ArtifactResult, _spec: ArtifactSpec): Promise<ArtifactResult>;
35
+ }
36
+ export declare function createCaptureStrategy(mediaMode: MediaMode): CaptureStrategy;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Capture Agent — Capture Strategy
3
+ *
4
+ * Abstraction over the supported media modes: screenshot and clip.
5
+ * The opcode runner is identical for all three — only the capture
6
+ * opcodes dispatch to different strategies.
7
+ */
8
+ // ── Screenshot strategy ─────────────────────────────────────────────
9
+ export class ScreenshotStrategy {
10
+ mediaMode = 'screenshot';
11
+ async prepare(_adapter, _spec) {
12
+ // Viewport locking is handled externally by the CLI runner
13
+ // when creating the browser context per variant.
14
+ }
15
+ async capture(adapter, _spec) {
16
+ const buffer = await adapter.takeScreenshot();
17
+ return {
18
+ mediaMode: 'screenshot',
19
+ buffer,
20
+ mimeType: 'image/png',
21
+ };
22
+ }
23
+ async postProcess(artifact, spec) {
24
+ // Post-processing (mockup frame, status bar) will be applied
25
+ // by the CLI runner using existing utilities:
26
+ // - applyDeviceFrame() from src/mockup.ts
27
+ // - generateStatusBarHtml() from src/status-bar.ts
28
+ // For now, return as-is. Phase 4 CLI runner handles the orchestration.
29
+ return artifact;
30
+ }
31
+ }
32
+ // ── Clip strategy ───────────────────────────────────────────────────
33
+ export class ClipStrategy {
34
+ mediaMode = 'clip';
35
+ async prepare(adapter, _spec) {
36
+ // Clip recording preparation:
37
+ // - The browser context is created with video recording enabled
38
+ // - Cursor overlay is injected by Browser.forVideoRecording()
39
+ }
40
+ async capture(adapter, _spec) {
41
+ // Clip capture is bounded by BEGIN_CLIP / END_CLIP opcodes.
42
+ // The opcode runner calls adapter.beginRecording() and adapter.endRecording().
43
+ // This method is called for the final artifact assembly.
44
+ const recording = await adapter.endRecording();
45
+ return {
46
+ mediaMode: 'clip',
47
+ buffer: recording.buffer,
48
+ mimeType: recording.mimeType,
49
+ };
50
+ }
51
+ async postProcess(artifact, spec) {
52
+ // Post-processing for clips:
53
+ // - Convert WebM to GIF/MP4 via src/clip-postprocess.ts (if it exists)
54
+ // - Trim start/end
55
+ // - Apply cursor theme
56
+ // Handled by CLI runner using existing video processing utilities.
57
+ return artifact;
58
+ }
59
+ }
60
+ // ── DOM strategy (Interactive Demos — AUT-121) ──────────────────────
61
+ export class DomStrategy {
62
+ mediaMode = 'dom';
63
+ async prepare(_adapter, _spec) {
64
+ // Nothing to prepare — DOM serialization happens inline in the
65
+ // CAPTURE_DOM opcode handler in opcode-runner.ts.
66
+ }
67
+ async capture(adapter, _spec) {
68
+ if (!adapter.serializeDom) {
69
+ throw new Error('DOM capture requires an adapter that implements serializeDom()');
70
+ }
71
+ const result = await adapter.serializeDom();
72
+ return {
73
+ mediaMode: 'dom',
74
+ buffer: Buffer.from(result.html, 'utf8'),
75
+ mimeType: 'text/html; charset=utf-8',
76
+ domHtml: result.html,
77
+ domAssetUrls: result.assetUrls,
78
+ domHtmlBytes: result.html.length,
79
+ dimensions: result.viewport,
80
+ };
81
+ }
82
+ async postProcess(artifact, _spec) {
83
+ // Phase 3 will add asset extraction (CAS), PurgeCSS and Brotli here.
84
+ return artifact;
85
+ }
86
+ }
87
+ // ── Factory ─────────────────────────────────────────────────────────
88
+ export function createCaptureStrategy(mediaMode) {
89
+ switch (mediaMode) {
90
+ case 'screenshot': return new ScreenshotStrategy();
91
+ case 'clip': return new ClipStrategy();
92
+ case 'dom': return new DomStrategy();
93
+ }
94
+ }
95
+ //# sourceMappingURL=capture-strategy.js.map