@veolab/discoverylab 1.6.3 → 1.6.6

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 (29) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/assets/applab-bundle-icon.png +0 -0
  4. package/assets/icons/icons8-claude-150.png +0 -0
  5. package/assets/icons/icons8-claude-500.png +0 -0
  6. package/dist/{chunk-HFN6BTVO.js → chunk-JAA53ES7.js} +1 -1
  7. package/dist/{chunk-XKX6NBHF.js → chunk-TWRWARU4.js} +52 -2
  8. package/dist/{chunk-IVX2OSOJ.js → chunk-XDUFCPOC.js} +198 -69
  9. package/dist/{chunk-5AISGCS4.js → chunk-YFF3M76J.js} +162 -33
  10. package/dist/cli.js +29 -18
  11. package/dist/export/infographic-template.html +392 -128
  12. package/dist/index.d.ts +30 -6
  13. package/dist/index.html +75 -21
  14. package/dist/index.js +4 -4
  15. package/dist/{infographic-GQAHEOAA.js → infographic-OSDIJM5M.js} +119 -17
  16. package/dist/mcpb/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
  17. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/better_sqlite3.node.d +1 -0
  18. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d +133 -0
  19. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/deps/locate_sqlite3.stamp.d +1 -0
  20. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d +4 -0
  21. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/test_extension/deps/test_extension.o.d +7 -0
  22. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/sqlite3.a.d +1 -0
  23. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/test_extension.node.d +1 -0
  24. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/ba23eeee118cd63e16015df367567cb043fed872.intermediate.d +1 -0
  25. package/dist/{server-W3JQ5RG7.js → server-GXNAKM4H.js} +1 -1
  26. package/dist/{server-OVOACIOJ.js → server-WN6DCCUA.js} +1 -1
  27. package/dist/{setup-F7MGEFIM.js → setup-SMN7FJNZ.js} +2 -2
  28. package/dist/{tools-VYFNRUS4.js → tools-6BTUMR3G.js} +3 -3
  29. package/package.json +8 -2
package/dist/index.d.ts CHANGED
@@ -13,16 +13,35 @@ interface MCPTool {
13
13
  name: string;
14
14
  description: string;
15
15
  inputSchema: z.ZodType<any>;
16
+ _meta?: Record<string, any>;
16
17
  handler: (params: any) => Promise<MCPToolResult>;
17
18
  }
19
+ interface MCPToolContent {
20
+ type: 'text' | 'image';
21
+ text?: string;
22
+ data?: string;
23
+ mimeType?: string;
24
+ }
18
25
  interface MCPToolResult {
19
- content: Array<{
20
- type: 'text' | 'image';
21
- text?: string;
22
- data?: string;
23
- mimeType?: string;
24
- }>;
26
+ content: MCPToolContent[];
25
27
  isError?: boolean;
28
+ structuredContent?: Record<string, any>;
29
+ _meta?: Record<string, any>;
30
+ }
31
+ interface MCPResourceContents {
32
+ uri: string;
33
+ mimeType: string;
34
+ text?: string;
35
+ blob?: string;
36
+ _meta?: Record<string, any>;
37
+ }
38
+ interface MCPResource {
39
+ uri: string;
40
+ name: string;
41
+ title?: string;
42
+ description?: string;
43
+ mimeType: string;
44
+ contents?: MCPResourceContents[];
26
45
  }
27
46
  interface MCPRequest {
28
47
  jsonrpc: '2.0';
@@ -42,12 +61,17 @@ interface MCPResponse {
42
61
  }
43
62
  declare class MCPServer {
44
63
  private tools;
64
+ private resources;
45
65
  private serverInfo;
46
66
  registerTool(tool: MCPTool): void;
47
67
  registerTools(tools: MCPTool[]): void;
68
+ registerResource(resource: MCPResource): void;
69
+ upsertResourceContents(uri: string, resource: Omit<MCPResource, 'uri'>): void;
48
70
  handleRequest(request: MCPRequest): Promise<MCPResponse>;
49
71
  private handleInitialize;
50
72
  private handleToolsList;
73
+ private handleResourcesList;
74
+ private handleResourcesRead;
51
75
  private handleToolCall;
52
76
  private zodToJsonSchema;
53
77
  runStdio(): Promise<void>;
package/dist/index.html CHANGED
@@ -927,7 +927,7 @@
927
927
  .project-claude-action {
928
928
  position: absolute;
929
929
  top: 8px;
930
- left: 8px;
930
+ right: 8px;
931
931
  display: inline-flex;
932
932
  align-items: center;
933
933
  justify-content: center;
@@ -944,13 +944,20 @@
944
944
  backdrop-filter: blur(10px);
945
945
  }
946
946
 
947
+ .project-claude-action.secondary {
948
+ background: rgba(18, 24, 34, 0.6);
949
+ border-color: rgba(255, 255, 255, 0.1);
950
+ color: var(--text-secondary);
951
+ }
952
+
947
953
  .project-claude-action:hover {
948
954
  transform: translateY(-1px);
949
955
  border-color: rgba(245, 240, 230, 0.5);
950
956
  background: rgba(17, 24, 32, 0.92);
951
957
  }
952
958
 
953
- .project-claude-action svg {
959
+ .project-claude-action svg,
960
+ .project-claude-action img {
954
961
  width: 16px;
955
962
  height: 16px;
956
963
  }
@@ -1092,10 +1099,12 @@
1092
1099
  transform: none;
1093
1100
  }
1094
1101
 
1095
- .claude-action-btn svg {
1102
+ .claude-action-btn svg,
1103
+ .claude-action-btn img {
1096
1104
  width: 14px;
1097
1105
  height: 14px;
1098
1106
  flex-shrink: 0;
1107
+ object-fit: contain;
1099
1108
  }
1100
1109
 
1101
1110
  .project-meta {
@@ -8235,14 +8244,19 @@
8235
8244
 
8236
8245
  function getClaudeIconSvg() {
8237
8246
  return `
8238
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
8239
- <path d="M12 3.5c-2.1 0-3.8 1-4.9 2.8C4.9 6.5 3.5 8.4 3.5 11c0 3.9 2.9 6.8 7.1 8.7a3 3 0 0 0 2.8 0c4.1-1.9 7.1-4.8 7.1-8.7 0-2.6-1.4-4.5-3.6-4.7C15.8 4.5 14.1 3.5 12 3.5Z" stroke="currentColor" stroke-width="1.5"/>
8240
- <path d="M8.5 10.5c.9-1 2.1-1.5 3.5-1.5s2.6.5 3.5 1.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
8241
- <path d="M9.2 13.8c.8.7 1.7 1.1 2.8 1.1 1 0 2-.4 2.8-1.1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
8242
- </svg>
8247
+ <img src="/assets/icons/icons8-claude-150.png" alt="" aria-hidden="true" />
8243
8248
  `;
8244
8249
  }
8245
8250
 
8251
+ function projectHasRenderableThumb(project) {
8252
+ if (!project) return false;
8253
+ if (project.thumbnailPath) return true;
8254
+ if (!project.videoPath) return false;
8255
+
8256
+ const ext = String(project.videoPath).toLowerCase().split('.').pop();
8257
+ return ['png', 'jpg', 'jpeg', 'gif', 'webp', 'mp4', 'mov', 'webm'].includes(ext);
8258
+ }
8259
+
8246
8260
  function isProjectReadyForClaude(project) {
8247
8261
  if (!project) return false;
8248
8262
  if (isProjectAnalyzing(project.status || '')) return false;
@@ -8265,14 +8279,22 @@
8265
8279
  }
8266
8280
 
8267
8281
  function renderClaudeProjectQuickAction(project) {
8268
- if (!claudeDesktopStatus.ready || !isProjectReadyForClaude(project)) {
8282
+ if (!isProjectReadyForClaude(project) || !claudeDesktopStatus.launcherSupported || !projectHasRenderableThumb(project)) {
8269
8283
  return '';
8270
8284
  }
8271
8285
 
8272
- const promptTitle = `Copy prompt and open ${project.name} in Claude Desktop`;
8286
+ const setupNeeded = claudeDesktopStatus.appDetected && !claudeDesktopStatus.mcpConfigured;
8287
+ const ready = claudeDesktopStatus.ready;
8288
+ const promptTitle = ready
8289
+ ? `Copy prompt and open ${project.name} in Claude Desktop`
8290
+ : setupNeeded
8291
+ ? `Copy the ${project.name} prompt and open Claude Desktop`
8292
+ : `Copy the ${project.name} prompt and check Claude Desktop`;
8293
+ const buttonClass = ready ? 'project-claude-action' : 'project-claude-action secondary';
8294
+
8273
8295
  return `
8274
8296
  <button
8275
- class="project-claude-action"
8297
+ class="${buttonClass}"
8276
8298
  type="button"
8277
8299
  data-project-id="${escapeAttr(project.id)}"
8278
8300
  title="${escapeAttr(promptTitle)}"
@@ -8298,7 +8320,7 @@
8298
8320
  : !projectReady
8299
8321
  ? 'Analyze first'
8300
8322
  : setupNeeded
8301
- ? 'Copy install command'
8323
+ ? 'Copy prompt'
8302
8324
  : 'Reload';
8303
8325
  const buttonClass = ready ? 'claude-action-btn' : 'claude-action-btn secondary';
8304
8326
  const action = ready
@@ -8306,7 +8328,7 @@
8306
8328
  : !projectReady
8307
8329
  ? ''
8308
8330
  : setupNeeded
8309
- ? 'copyClaudeDesktopInstallCommand()'
8331
+ ? 'copyCurrentProjectClaudePrompt()'
8310
8332
  : 'refreshClaudeDesktopStatus()';
8311
8333
 
8312
8334
  return `
@@ -8383,6 +8405,30 @@
8383
8405
  });
8384
8406
  }
8385
8407
 
8408
+ async function copyClaudeProjectPrompt(project) {
8409
+ if (!project) return false;
8410
+
8411
+ const copied = await copyToClipboard(buildClaudeDesktopPrompt(project), { silent: true });
8412
+ if (!copied) {
8413
+ showToast('Failed to copy Claude Desktop prompt', 'error');
8414
+ return false;
8415
+ }
8416
+
8417
+ return true;
8418
+ }
8419
+
8420
+ async function copyCurrentProjectClaudePrompt() {
8421
+ if (!currentProject?.id) {
8422
+ showToast('Select a project first', 'warning');
8423
+ return;
8424
+ }
8425
+
8426
+ const copied = await copyClaudeProjectPrompt(currentProject);
8427
+ if (!copied) return;
8428
+
8429
+ showToast('Claude Desktop prompt copied', 'success');
8430
+ }
8431
+
8386
8432
  async function launchClaudeDesktop() {
8387
8433
  const response = await fetch('/api/integrations/claude-desktop/launch', { method: 'POST' });
8388
8434
  const data = await response.json();
@@ -8394,9 +8440,8 @@
8394
8440
  async function copyPromptAndLaunchClaude(project) {
8395
8441
  if (!project) return;
8396
8442
 
8397
- const copied = await copyToClipboard(buildClaudeDesktopPrompt(project), { silent: true });
8443
+ const copied = await copyClaudeProjectPrompt(project);
8398
8444
  if (!copied) {
8399
- showToast('Failed to copy Claude Desktop prompt', 'error');
8400
8445
  return;
8401
8446
  }
8402
8447
 
@@ -8417,11 +8462,17 @@
8417
8462
 
8418
8463
  if (!claudeDesktopStatus.ready) {
8419
8464
  if (claudeDesktopStatus.appDetected && !claudeDesktopStatus.mcpConfigured) {
8420
- copyClaudeDesktopInstallCommand();
8465
+ const copied = await copyClaudeProjectPrompt(project);
8466
+ if (copied) {
8467
+ showToast('Project prompt copied. Enable the DiscoveryLab extension in Claude Desktop to open it there.', 'warning');
8468
+ }
8421
8469
  return;
8422
8470
  }
8423
8471
 
8424
- showToast(claudeDesktopStatus.message || 'Claude Desktop is not ready yet.', 'warning');
8472
+ const copied = await copyClaudeProjectPrompt(project);
8473
+ if (copied) {
8474
+ showToast(claudeDesktopStatus.message || 'Project prompt copied. Claude Desktop is not ready yet.', 'warning');
8475
+ }
8425
8476
  return;
8426
8477
  }
8427
8478
 
@@ -22371,8 +22422,8 @@ appId: ${platform === 'ios' ? 'com.apple.Preferences' : 'com.android.settings'}
22371
22422
  jpg: 'Exports the current capture as JPEG images.',
22372
22423
  mp4: 'Exports the current capture as an MP4 video.',
22373
22424
  gif: 'Exports the current capture as an animated GIF.',
22374
- applab: 'Packages everything App Lab has locally for this project: media, recording session, test script, network trace, OCR, app intelligence, Task Hub data, and prior export artifacts like grids.',
22375
- esvp: 'Packages the full local project plus the attached ESVP session snapshot when available, so you can share trace, scripts, media, OCR, and app intelligence together.',
22425
+ applab: 'Packages a Claude-friendly App Lab bundle: analyzed frames, thumbnail, OCR, app intelligence, network trace, ESVP snapshot, and Task Hub metadata without heavy renders or long recordings.',
22426
+ esvp: 'Packages a lightweight ESVP-ready bundle with analyzed frames, OCR, app intelligence, network trace, and the attached ESVP snapshot when available.',
22376
22427
  };
22377
22428
 
22378
22429
  help.textContent = descriptions[select.value] || '';
@@ -22454,10 +22505,13 @@ appId: ${platform === 'ios' ? 'com.apple.Preferences' : 'com.android.settings'}
22454
22505
  const sizeKb = ((data.size || 0) / 1024).toFixed(1);
22455
22506
  showToast(`Infographic exported (${sizeKb}KB, ${data.frameCount} frames)`, 'success');
22456
22507
  } else {
22457
- showToast(data.error || 'Export failed', 'error');
22508
+ const debugHint = data.debug && Number.isFinite(data.debug.frameCandidates) && Number.isFinite(data.debug.validFrames)
22509
+ ? ` (${data.debug.validFrames}/${data.debug.frameCandidates} readable frames${data.debug.source ? `, ${data.debug.source}` : ''})`
22510
+ : '';
22511
+ showToast((data.error || 'Export failed') + debugHint, 'error');
22458
22512
  }
22459
22513
  } catch (err) {
22460
- showToast('Export failed', 'error');
22514
+ showToast(err?.message || 'Export failed', 'error');
22461
22515
  }
22462
22516
  }
22463
22517
 
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  startServer,
4
4
  stopServer
5
- } from "./chunk-IVX2OSOJ.js";
5
+ } from "./chunk-XDUFCPOC.js";
6
6
  import {
7
7
  analyzeTools,
8
8
  canvasTools,
@@ -16,15 +16,15 @@ import {
16
16
  templateTools,
17
17
  testingTools,
18
18
  uiTools
19
- } from "./chunk-5AISGCS4.js";
19
+ } from "./chunk-YFF3M76J.js";
20
20
  import "./chunk-34GGYFXX.js";
21
21
  import "./chunk-PMCXEA7J.js";
22
22
  import {
23
23
  setupTools
24
- } from "./chunk-HFN6BTVO.js";
24
+ } from "./chunk-JAA53ES7.js";
25
25
  import {
26
26
  mcpServer
27
- } from "./chunk-XKX6NBHF.js";
27
+ } from "./chunk-TWRWARU4.js";
28
28
  import "./chunk-6GK5K6CS.js";
29
29
  import "./chunk-7R5YNOXE.js";
30
30
  import "./chunk-3ERJNXYM.js";
@@ -18,18 +18,22 @@ function findTemplate() {
18
18
  return null;
19
19
  }
20
20
  function imageToBase64(imagePath) {
21
- if (!existsSync(imagePath)) return "";
22
- const buffer = readFileSync(imagePath);
23
- const ext = extname(imagePath).toLowerCase();
24
- const mimeTypes = {
25
- ".png": "image/png",
26
- ".jpg": "image/jpeg",
27
- ".jpeg": "image/jpeg",
28
- ".webp": "image/webp",
29
- ".gif": "image/gif"
30
- };
31
- const mime = mimeTypes[ext] || "image/png";
32
- return `data:${mime};base64,${buffer.toString("base64")}`;
21
+ try {
22
+ if (!existsSync(imagePath) || !statSync(imagePath).isFile()) return "";
23
+ const buffer = readFileSync(imagePath);
24
+ const ext = extname(imagePath).toLowerCase();
25
+ const mimeTypes = {
26
+ ".png": "image/png",
27
+ ".jpg": "image/jpeg",
28
+ ".jpeg": "image/jpeg",
29
+ ".webp": "image/webp",
30
+ ".gif": "image/gif"
31
+ };
32
+ const mime = mimeTypes[ext] || "image/png";
33
+ return `data:${mime};base64,${buffer.toString("base64")}`;
34
+ } catch {
35
+ return "";
36
+ }
33
37
  }
34
38
  function stripMarkdown(text) {
35
39
  return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/__(.+?)__/g, "$1").replace(/_(.+?)_/g, "$1").replace(/`(.+?)`/g, "$1").replace(/^#{1,3}\s+/gm, "").replace(/^-\s+/gm, "").replace(/^\d+\.\s+/gm, "").replace(/\[(.+?)\]\(.*?\)/g, "$1").trim();
@@ -51,7 +55,92 @@ function collectFrameImages(framesDir, videoPath, projectsDir, projectId) {
51
55
  }
52
56
  return [];
53
57
  }
58
+ function validateFrameInputs(frameFiles, frameOcr) {
59
+ const validFrameFiles = [];
60
+ const validFrameOcr = [];
61
+ const dataUrls = [];
62
+ const invalidFrames = [];
63
+ frameFiles.forEach((filePath, index) => {
64
+ if (!filePath || !existsSync(filePath)) {
65
+ invalidFrames.push({ path: filePath || `frame-${index + 1}`, reason: "missing" });
66
+ return;
67
+ }
68
+ let stats;
69
+ try {
70
+ stats = statSync(filePath);
71
+ } catch {
72
+ invalidFrames.push({ path: filePath, reason: "unreadable" });
73
+ return;
74
+ }
75
+ if (!stats.isFile()) {
76
+ invalidFrames.push({ path: filePath, reason: "not_file" });
77
+ return;
78
+ }
79
+ const dataUrl = imageToBase64(filePath);
80
+ if (!dataUrl) {
81
+ invalidFrames.push({ path: filePath, reason: "unreadable" });
82
+ return;
83
+ }
84
+ validFrameFiles.push(filePath);
85
+ validFrameOcr.push(frameOcr[index] || { ocrText: null });
86
+ dataUrls.push(dataUrl);
87
+ });
88
+ return {
89
+ frameFiles: validFrameFiles,
90
+ frameOcr: validFrameOcr,
91
+ dataUrls,
92
+ invalidFrames
93
+ };
94
+ }
95
+ function resolveInfographicFrameInputs(dbFrames, framesDir, videoPath, projectsDir, projectId) {
96
+ if (dbFrames.length > 0) {
97
+ const dbValidation = validateFrameInputs(
98
+ dbFrames.map((frame) => frame.imagePath),
99
+ dbFrames.map((frame) => ({ ocrText: frame.ocrText ?? null }))
100
+ );
101
+ if (dbValidation.frameFiles.length > 0) {
102
+ return {
103
+ ...dbValidation,
104
+ source: "db",
105
+ candidateCount: dbFrames.length
106
+ };
107
+ }
108
+ const fallbackFrameFiles2 = collectFrameImages(framesDir, videoPath, projectsDir, projectId);
109
+ const fallbackValidation2 = validateFrameInputs(
110
+ fallbackFrameFiles2,
111
+ fallbackFrameFiles2.map(() => ({ ocrText: null }))
112
+ );
113
+ if (fallbackValidation2.frameFiles.length > 0) {
114
+ return {
115
+ ...fallbackValidation2,
116
+ invalidFrames: [...dbValidation.invalidFrames, ...fallbackValidation2.invalidFrames],
117
+ source: "filesystem",
118
+ candidateCount: dbFrames.length + fallbackFrameFiles2.length
119
+ };
120
+ }
121
+ return {
122
+ ...fallbackValidation2,
123
+ invalidFrames: [...dbValidation.invalidFrames, ...fallbackValidation2.invalidFrames],
124
+ source: "none",
125
+ candidateCount: dbFrames.length + fallbackFrameFiles2.length
126
+ };
127
+ }
128
+ const fallbackFrameFiles = collectFrameImages(framesDir, videoPath, projectsDir, projectId);
129
+ const fallbackValidation = validateFrameInputs(
130
+ fallbackFrameFiles,
131
+ fallbackFrameFiles.map(() => ({ ocrText: null }))
132
+ );
133
+ return {
134
+ ...fallbackValidation,
135
+ source: fallbackValidation.frameFiles.length > 0 ? "filesystem" : "none",
136
+ candidateCount: fallbackFrameFiles.length
137
+ };
138
+ }
54
139
  function buildInfographicData(project, frameFiles, frameOcr, annotations) {
140
+ const validatedFrames = validateFrameInputs(frameFiles, frameOcr);
141
+ const usableFrameFiles = validatedFrames.frameFiles;
142
+ const usableFrameOcr = validatedFrames.frameOcr;
143
+ const dataUrls = validatedFrames.dataUrls;
55
144
  let flowSteps = [];
56
145
  let uiElements = [];
57
146
  if (project.aiSummary) {
@@ -64,7 +153,18 @@ function buildInfographicData(project, frameFiles, frameOcr, annotations) {
64
153
  uiElements = (uiMatch[1].match(/^-\s+(.+)$/gm) || []).map((s) => s.replace(/^-\s+/, ""));
65
154
  }
66
155
  }
67
- const elementsPerFrame = Math.max(1, Math.ceil(uiElements.length / Math.max(frameFiles.length, 1)));
156
+ let overview = "";
157
+ if (project.aiSummary) {
158
+ const overviewMatch = project.aiSummary.match(/## (?:App Overview|Page \/ App Overview|Overview|Summary)\n([\s\S]*?)(?=\n##|\n$|$)/);
159
+ if (overviewMatch) {
160
+ const overviewBody = overviewMatch[1].split(/\n\s*\n/).map((chunk) => chunk.trim()).filter(Boolean)[0] || overviewMatch[1];
161
+ overview = stripMarkdown(overviewBody).slice(0, 240);
162
+ }
163
+ }
164
+ if (!overview && project.marketingDescription) {
165
+ overview = stripMarkdown(project.marketingDescription).slice(0, 240);
166
+ }
167
+ const elementsPerFrame = Math.max(1, Math.ceil(uiElements.length / Math.max(usableFrameFiles.length, 1)));
68
168
  const hotspotColors = ["#818CF8", "#34D399", "#F59E0B", "#EC4899", "#06B6D4", "#8B5CF6"];
69
169
  const hotspotPositions = [
70
170
  { x: 50, y: 8 },
@@ -80,10 +180,10 @@ function buildInfographicData(project, frameFiles, frameOcr, annotations) {
80
180
  { x: 15, y: 70 }
81
181
  // bottom left
82
182
  ];
83
- const frames = frameFiles.map((filePath, i) => {
183
+ const frames = usableFrameFiles.map((filePath, i) => {
84
184
  const rawStepName = annotations?.[i]?.label || flowSteps[i] || `Step ${i + 1}`;
85
185
  const stepName = stripMarkdown(rawStepName);
86
- const ocr = frameOcr[i]?.ocrText || "";
186
+ const ocr = usableFrameOcr[i]?.ocrText || "";
87
187
  const rawDesc = flowSteps[i] || ocr.slice(0, 100) || `Screen ${i + 1}`;
88
188
  const description = stripMarkdown(rawDesc);
89
189
  const hotspots = [];
@@ -121,7 +221,7 @@ function buildInfographicData(project, frameFiles, frameOcr, annotations) {
121
221
  id: `frame-${i}`,
122
222
  step_name: stepName,
123
223
  description,
124
- base64: imageToBase64(filePath),
224
+ base64: dataUrls[i] || imageToBase64(filePath),
125
225
  baseline_status: "not_validated",
126
226
  hotspots
127
227
  };
@@ -130,6 +230,7 @@ function buildInfographicData(project, frameFiles, frameOcr, annotations) {
130
230
  name: project.marketingTitle || project.name,
131
231
  platform: project.platform || "unknown",
132
232
  recorded_at: project.createdAt instanceof Date ? project.createdAt.toISOString() : typeof project.createdAt === "string" ? project.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
233
+ overview,
133
234
  frames
134
235
  };
135
236
  }
@@ -179,5 +280,6 @@ export {
179
280
  buildInfographicData,
180
281
  collectFrameImages,
181
282
  generateInfographicHtml,
182
- generateInfographicHtmlString
283
+ generateInfographicHtmlString,
284
+ resolveInfographicFrameInputs
183
285
  };
@@ -0,0 +1,4 @@
1
+ File generated from our OpenAPI spec by Stainless.
2
+
3
+ This directory can be used to store custom files to expand the SDK.
4
+ It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
@@ -0,0 +1 @@
1
+ cmd_Release/better_sqlite3.node := c++ -bundle -undefined dynamic_lookup -Wl,-search_paths_first -Wl,-dead_strip -mmacosx-version-min=10.7 -arch arm64 -L./Release -stdlib=libc++ -o Release/better_sqlite3.node Release/obj.target/better_sqlite3/src/better_sqlite3.o Release/sqlite3.a
@@ -0,0 +1,133 @@
1
+ cmd_Release/obj.target/better_sqlite3/src/better_sqlite3.o := c++ -o Release/obj.target/better_sqlite3/src/better_sqlite3.o ../src/better_sqlite3.cpp '-DNODE_GYP_MODULE_NAME=better_sqlite3' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DBUILDING_NODE_EXTENSION' '-DNDEBUG' -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/src -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/openssl/config -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/openssl/openssl/include -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/uv/include -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/zlib -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/v8/include -I./Release/obj/gen/sqlite3 -O3 -fno-strict-aliasing -mmacosx-version-min=10.7 -arch arm64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++20 -stdlib=libc++ -fno-rtti -fno-exceptions -fvisibility-inlines-hidden -std=c++20 -stdlib=libc++ -MMD -MF ./Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d.raw -c
2
+ Release/obj.target/better_sqlite3/src/better_sqlite3.o: \
3
+ ../src/better_sqlite3.cpp ../src/better_sqlite3.hpp \
4
+ Release/obj/gen/sqlite3/sqlite3.h \
5
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node.h \
6
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8.h \
7
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/common.h \
8
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8config.h \
9
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-array-buffer.h \
10
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-local-handle.h \
11
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-handle-base.h \
12
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-internal.h \
13
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-memory-span.h \
14
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-object.h \
15
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-maybe.h \
16
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/internal/conditional-stack-allocated.h \
17
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/macros.h \
18
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/internal/compiler-specific.h \
19
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/type-traits.h \
20
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-persistent-handle.h \
21
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-weak-callback-info.h \
22
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-primitive.h \
23
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-data.h \
24
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-value.h \
25
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-sandbox.h \
26
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-traced-handle.h \
27
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-platform.h \
28
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-source-location.h \
29
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-container.h \
30
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-context.h \
31
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-snapshot.h \
32
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-isolate.h \
33
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-callbacks.h \
34
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-promise.h \
35
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-debug.h \
36
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-script.h \
37
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-message.h \
38
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-embedder-heap.h \
39
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-exception.h \
40
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-function-callback.h \
41
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-microtask.h \
42
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-statistics.h \
43
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-unwinder.h \
44
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-embedder-state-scope.h \
45
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-date.h \
46
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-extension.h \
47
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-external.h \
48
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-function.h \
49
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-template.h \
50
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-initialization.h \
51
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-json.h \
52
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-locker.h \
53
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-microtask-queue.h \
54
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-primitive-object.h \
55
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-proxy.h \
56
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-regexp.h \
57
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-typed-array.h \
58
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-value-serializer.h \
59
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-version.h \
60
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-wasm.h \
61
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_version.h \
62
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_api.h \
63
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/js_native_api.h \
64
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/js_native_api_types.h \
65
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_api_types.h \
66
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_object_wrap.h \
67
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_buffer.h
68
+ ../src/better_sqlite3.cpp:
69
+ ../src/better_sqlite3.hpp:
70
+ Release/obj/gen/sqlite3/sqlite3.h:
71
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node.h:
72
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8.h:
73
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/common.h:
74
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8config.h:
75
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-array-buffer.h:
76
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-local-handle.h:
77
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-handle-base.h:
78
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-internal.h:
79
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-memory-span.h:
80
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-object.h:
81
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-maybe.h:
82
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/internal/conditional-stack-allocated.h:
83
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/macros.h:
84
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/internal/compiler-specific.h:
85
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/cppgc/type-traits.h:
86
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-persistent-handle.h:
87
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-weak-callback-info.h:
88
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-primitive.h:
89
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-data.h:
90
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-value.h:
91
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-sandbox.h:
92
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-traced-handle.h:
93
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-platform.h:
94
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-source-location.h:
95
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-container.h:
96
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-context.h:
97
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-snapshot.h:
98
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-isolate.h:
99
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-callbacks.h:
100
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-promise.h:
101
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-debug.h:
102
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-script.h:
103
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-message.h:
104
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-embedder-heap.h:
105
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-exception.h:
106
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-function-callback.h:
107
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-microtask.h:
108
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-statistics.h:
109
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-unwinder.h:
110
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-embedder-state-scope.h:
111
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-date.h:
112
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-extension.h:
113
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-external.h:
114
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-function.h:
115
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-template.h:
116
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-initialization.h:
117
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-json.h:
118
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-locker.h:
119
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-microtask-queue.h:
120
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-primitive-object.h:
121
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-proxy.h:
122
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-regexp.h:
123
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-typed-array.h:
124
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-value-serializer.h:
125
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-version.h:
126
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/v8-wasm.h:
127
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_version.h:
128
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_api.h:
129
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/js_native_api.h:
130
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/js_native_api_types.h:
131
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_api_types.h:
132
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_object_wrap.h:
133
+ /Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node/node_buffer.h:
@@ -0,0 +1 @@
1
+ cmd_Release/obj.target/deps/locate_sqlite3.stamp := touch Release/obj.target/deps/locate_sqlite3.stamp
@@ -0,0 +1,4 @@
1
+ cmd_Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o := cc -o Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o Release/obj/gen/sqlite3/sqlite3.c '-DNODE_GYP_MODULE_NAME=sqlite3' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DHAVE_INT16_T=1' '-DHAVE_INT32_T=1' '-DHAVE_INT8_T=1' '-DHAVE_STDINT_H=1' '-DHAVE_UINT16_T=1' '-DHAVE_UINT32_T=1' '-DHAVE_UINT8_T=1' '-DHAVE_USLEEP=1' '-DSQLITE_DEFAULT_CACHE_SIZE=-16000' '-DSQLITE_DEFAULT_FOREIGN_KEYS=1' '-DSQLITE_DEFAULT_MEMSTATUS=0' '-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1' '-DSQLITE_DQS=0' '-DSQLITE_ENABLE_COLUMN_METADATA' '-DSQLITE_ENABLE_DBSTAT_VTAB' '-DSQLITE_ENABLE_DESERIALIZE' '-DSQLITE_ENABLE_FTS3' '-DSQLITE_ENABLE_FTS3_PARENTHESIS' '-DSQLITE_ENABLE_FTS4' '-DSQLITE_ENABLE_FTS5' '-DSQLITE_ENABLE_GEOPOLY' '-DSQLITE_ENABLE_JSON1' '-DSQLITE_ENABLE_MATH_FUNCTIONS' '-DSQLITE_ENABLE_RTREE' '-DSQLITE_ENABLE_STAT4' '-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT' '-DSQLITE_LIKE_DOESNT_MATCH_BLOBS' '-DSQLITE_OMIT_DEPRECATED' '-DSQLITE_OMIT_PROGRESS_CALLBACK' '-DSQLITE_OMIT_SHARED_CACHE' '-DSQLITE_OMIT_TCL_VARIABLE' '-DSQLITE_SOUNDEX' '-DSQLITE_THREADSAFE=2' '-DSQLITE_TRACE_SIZE_LIMIT=32' '-DSQLITE_USE_URI=0' '-DNDEBUG' -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/include/node -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/src -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/openssl/config -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/openssl/openssl/include -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/uv/include -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/zlib -I/Users/andersonmelo/Library/Caches/node-gyp/24.11.1/deps/v8/include -I./Release/obj/gen/sqlite3 -O3 -fno-strict-aliasing -mmacosx-version-min=10.7 -arch arm64 -Wall -Wendif-labels -W -Wno-unused-parameter -w -std=c99 -MMD -MF ./Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d.raw -c
2
+ Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o: \
3
+ Release/obj/gen/sqlite3/sqlite3.c
4
+ Release/obj/gen/sqlite3/sqlite3.c: