@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/assets/applab-bundle-icon.png +0 -0
- package/assets/icons/icons8-claude-150.png +0 -0
- package/assets/icons/icons8-claude-500.png +0 -0
- package/dist/{chunk-HFN6BTVO.js → chunk-JAA53ES7.js} +1 -1
- package/dist/{chunk-XKX6NBHF.js → chunk-TWRWARU4.js} +52 -2
- package/dist/{chunk-IVX2OSOJ.js → chunk-XDUFCPOC.js} +198 -69
- package/dist/{chunk-5AISGCS4.js → chunk-YFF3M76J.js} +162 -33
- package/dist/cli.js +29 -18
- package/dist/export/infographic-template.html +392 -128
- package/dist/index.d.ts +30 -6
- package/dist/index.html +75 -21
- package/dist/index.js +4 -4
- package/dist/{infographic-GQAHEOAA.js → infographic-OSDIJM5M.js} +119 -17
- package/dist/mcpb/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/better_sqlite3.node.d +1 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d +133 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/deps/locate_sqlite3.stamp.d +1 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d +4 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/test_extension/deps/test_extension.o.d +7 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/sqlite3.a.d +1 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/test_extension.node.d +1 -0
- package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/ba23eeee118cd63e16015df367567cb043fed872.intermediate.d +1 -0
- package/dist/{server-W3JQ5RG7.js → server-GXNAKM4H.js} +1 -1
- package/dist/{server-OVOACIOJ.js → server-WN6DCCUA.js} +1 -1
- package/dist/{setup-F7MGEFIM.js → setup-SMN7FJNZ.js} +2 -2
- package/dist/{tools-VYFNRUS4.js → tools-6BTUMR3G.js} +3 -3
- package/package.json +8 -2
|
@@ -14,8 +14,9 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
createErrorResult,
|
|
16
16
|
createJsonResult,
|
|
17
|
-
createTextResult
|
|
18
|
-
|
|
17
|
+
createTextResult,
|
|
18
|
+
mcpServer
|
|
19
|
+
} from "./chunk-TWRWARU4.js";
|
|
19
20
|
import {
|
|
20
21
|
getAvailableTemplates,
|
|
21
22
|
isTemplatesInstalled
|
|
@@ -3788,29 +3789,25 @@ var exportInfographicTool = {
|
|
|
3788
3789
|
try {
|
|
3789
3790
|
const { projects: projectsTable, frames: framesTable, FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
|
|
3790
3791
|
const { eq: eq4 } = await import("drizzle-orm");
|
|
3791
|
-
const {
|
|
3792
|
+
const { buildInfographicData, generateInfographicHtml, resolveInfographicFrameInputs } = await import("./infographic-OSDIJM5M.js");
|
|
3792
3793
|
const db = getDatabase();
|
|
3793
3794
|
const [project] = await db.select().from(projectsTable).where(eq4(projectsTable.id, params.projectId)).limit(1);
|
|
3794
3795
|
if (!project) return createErrorResult(`Project not found: ${params.projectId}`);
|
|
3795
3796
|
const dbFrames = await db.select().from(framesTable).where(eq4(framesTable.projectId, project.id)).orderBy(framesTable.frameNumber).limit(20);
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
PROJECTS_DIR3,
|
|
3806
|
-
project.id
|
|
3807
|
-
);
|
|
3808
|
-
frameOcr = frameFiles.map(() => ({ ocrText: null }));
|
|
3797
|
+
const resolvedFrames = resolveInfographicFrameInputs(
|
|
3798
|
+
dbFrames,
|
|
3799
|
+
path5.join(FRAMES_DIR, project.id),
|
|
3800
|
+
project.videoPath,
|
|
3801
|
+
PROJECTS_DIR3,
|
|
3802
|
+
project.id
|
|
3803
|
+
);
|
|
3804
|
+
if (resolvedFrames.frameFiles.length === 0) {
|
|
3805
|
+
return createErrorResult(`No readable frames found for infographic export. Checked ${resolvedFrames.candidateCount} candidate(s) from ${resolvedFrames.source}.`);
|
|
3809
3806
|
}
|
|
3810
|
-
|
|
3811
|
-
|
|
3807
|
+
const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
|
|
3808
|
+
if (data.frames.length === 0) {
|
|
3809
|
+
return createErrorResult("Infographic export produced no embeddable frames.");
|
|
3812
3810
|
}
|
|
3813
|
-
const data = buildInfographicData(project, frameFiles, frameOcr);
|
|
3814
3811
|
const slug = (project.marketingTitle || project.name || project.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
3815
3812
|
const outputFilePath = params.outputPath ? path5.join(params.outputPath, `${slug}-infographic.html`) : path5.join(EXPORTS_DIR, `${slug}-infographic.html`);
|
|
3816
3813
|
const result = generateInfographicHtml(data, outputFilePath);
|
|
@@ -6919,6 +6916,102 @@ var templateTools = [
|
|
|
6919
6916
|
// src/mcp/tools/knowledge.ts
|
|
6920
6917
|
import { z as z12 } from "zod";
|
|
6921
6918
|
import { desc as desc2 } from "drizzle-orm";
|
|
6919
|
+
var KNOWLEDGE_VIEWER_RESOURCE_URI = "ui://discoverylab/knowledge-viewer.html";
|
|
6920
|
+
var KNOWLEDGE_VIEWER_MIME_TYPE = "text/html;profile=mcp-app";
|
|
6921
|
+
function buildViewerPlaceholderHtml(message) {
|
|
6922
|
+
const safeMessage = JSON.stringify(message);
|
|
6923
|
+
return `<!DOCTYPE html>
|
|
6924
|
+
<html lang="en">
|
|
6925
|
+
<head>
|
|
6926
|
+
<meta charset="UTF-8" />
|
|
6927
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6928
|
+
<title>DiscoveryLab Flow Viewer</title>
|
|
6929
|
+
<style>
|
|
6930
|
+
:root {
|
|
6931
|
+
color-scheme: dark;
|
|
6932
|
+
--bg: #0b1117;
|
|
6933
|
+
--panel: rgba(255, 255, 255, 0.04);
|
|
6934
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
6935
|
+
--text: #eff5fb;
|
|
6936
|
+
--muted: #9fb0c2;
|
|
6937
|
+
--accent: #2dd4bf;
|
|
6938
|
+
}
|
|
6939
|
+
|
|
6940
|
+
* { box-sizing: border-box; }
|
|
6941
|
+
|
|
6942
|
+
body {
|
|
6943
|
+
margin: 0;
|
|
6944
|
+
min-height: 100vh;
|
|
6945
|
+
display: grid;
|
|
6946
|
+
place-items: center;
|
|
6947
|
+
padding: 24px;
|
|
6948
|
+
background:
|
|
6949
|
+
radial-gradient(circle at top left, rgba(45, 212, 191, 0.18), transparent 28%),
|
|
6950
|
+
linear-gradient(180deg, #101826, var(--bg));
|
|
6951
|
+
color: var(--text);
|
|
6952
|
+
font: 14px/1.5 "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
|
|
6953
|
+
}
|
|
6954
|
+
|
|
6955
|
+
.card {
|
|
6956
|
+
width: min(720px, 100%);
|
|
6957
|
+
border: 1px solid var(--border);
|
|
6958
|
+
border-radius: 24px;
|
|
6959
|
+
padding: 24px;
|
|
6960
|
+
background: var(--panel);
|
|
6961
|
+
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
|
|
6962
|
+
}
|
|
6963
|
+
|
|
6964
|
+
.eyebrow {
|
|
6965
|
+
display: inline-flex;
|
|
6966
|
+
align-items: center;
|
|
6967
|
+
gap: 8px;
|
|
6968
|
+
border: 1px solid var(--border);
|
|
6969
|
+
border-radius: 999px;
|
|
6970
|
+
padding: 6px 12px;
|
|
6971
|
+
color: var(--muted);
|
|
6972
|
+
font-size: 12px;
|
|
6973
|
+
text-transform: uppercase;
|
|
6974
|
+
letter-spacing: 0.03em;
|
|
6975
|
+
}
|
|
6976
|
+
|
|
6977
|
+
h1 {
|
|
6978
|
+
margin: 16px 0 8px;
|
|
6979
|
+
font: 700 32px/1.02 "Space Grotesk", "Avenir Next", system-ui, sans-serif;
|
|
6980
|
+
letter-spacing: -0.03em;
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
p {
|
|
6984
|
+
margin: 0;
|
|
6985
|
+
color: var(--muted);
|
|
6986
|
+
}
|
|
6987
|
+
</style>
|
|
6988
|
+
</head>
|
|
6989
|
+
<body>
|
|
6990
|
+
<main class="card">
|
|
6991
|
+
<div class="eyebrow">DiscoveryLab Local Viewer</div>
|
|
6992
|
+
<h1>Open a flow in Claude Desktop</h1>
|
|
6993
|
+
<p id="message"></p>
|
|
6994
|
+
</main>
|
|
6995
|
+
<script>
|
|
6996
|
+
document.getElementById('message').textContent = ${safeMessage};
|
|
6997
|
+
</script>
|
|
6998
|
+
</body>
|
|
6999
|
+
</html>`;
|
|
7000
|
+
}
|
|
7001
|
+
mcpServer.registerResource({
|
|
7002
|
+
uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
|
|
7003
|
+
name: "discoverylab-knowledge-viewer",
|
|
7004
|
+
title: "DiscoveryLab Flow Viewer",
|
|
7005
|
+
description: "Interactive local flow viewer for DiscoveryLab projects.",
|
|
7006
|
+
mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
|
|
7007
|
+
contents: [
|
|
7008
|
+
{
|
|
7009
|
+
uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
|
|
7010
|
+
mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
|
|
7011
|
+
text: buildViewerPlaceholderHtml("Use dlab.knowledge.open to load a local project into the canvas.")
|
|
7012
|
+
}
|
|
7013
|
+
]
|
|
7014
|
+
});
|
|
6922
7015
|
var knowledgeSearchTool = {
|
|
6923
7016
|
name: "dlab.knowledge.search",
|
|
6924
7017
|
description: `Search across all DiscoveryLab projects for app flows, UI elements, screens, and behaviors. Use this when the user asks about how an app works, what a specific screen looks like, or any question about captured app flows. Returns relevant projects with their analysis, OCR text, and context.`,
|
|
@@ -7093,6 +7186,11 @@ var knowledgeOpenTool = {
|
|
|
7093
7186
|
query: z12.string().optional().describe('Search query to find the project (e.g. "login flow", "onboarding")'),
|
|
7094
7187
|
projectId: z12.string().optional().describe("Direct project ID if known")
|
|
7095
7188
|
}),
|
|
7189
|
+
_meta: {
|
|
7190
|
+
ui: {
|
|
7191
|
+
resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
|
|
7192
|
+
}
|
|
7193
|
+
},
|
|
7096
7194
|
handler: async (params) => {
|
|
7097
7195
|
try {
|
|
7098
7196
|
const db = getDatabase();
|
|
@@ -7129,26 +7227,57 @@ var knowledgeOpenTool = {
|
|
|
7129
7227
|
const { FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
|
|
7130
7228
|
const { join: join12 } = await import("path");
|
|
7131
7229
|
const dbFrames = await db.select().from(frames).where(eq4(frames.projectId, project.id)).orderBy(frames.frameNumber).limit(15);
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7230
|
+
const { resolveInfographicFrameInputs, buildInfographicData, generateInfographicHtmlString } = await import("./infographic-OSDIJM5M.js");
|
|
7231
|
+
const resolvedFrames = resolveInfographicFrameInputs(
|
|
7232
|
+
dbFrames,
|
|
7233
|
+
join12(FRAMES_DIR, project.id),
|
|
7234
|
+
project.videoPath,
|
|
7235
|
+
PROJECTS_DIR3,
|
|
7236
|
+
project.id
|
|
7237
|
+
);
|
|
7238
|
+
if (resolvedFrames.frameFiles.length === 0) {
|
|
7239
|
+
return createTextResult(`Project "${project.marketingTitle || project.name}" has no readable frames. Run the analyzer again, then try again.`);
|
|
7141
7240
|
}
|
|
7142
|
-
|
|
7143
|
-
|
|
7241
|
+
const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
|
|
7242
|
+
if (data.frames.length === 0) {
|
|
7243
|
+
return createErrorResult(`Project "${project.marketingTitle || project.name}" did not produce embeddable infographic frames.`);
|
|
7144
7244
|
}
|
|
7145
|
-
const { buildInfographicData, generateInfographicHtmlString } = await import("./infographic-GQAHEOAA.js");
|
|
7146
|
-
const data = buildInfographicData(project, frameFiles, frameOcr);
|
|
7147
7245
|
const html = generateInfographicHtmlString(data);
|
|
7148
7246
|
if (!html) {
|
|
7149
7247
|
return createErrorResult("Failed to generate infographic HTML (template not found)");
|
|
7150
7248
|
}
|
|
7151
|
-
|
|
7249
|
+
mcpServer.upsertResourceContents(KNOWLEDGE_VIEWER_RESOURCE_URI, {
|
|
7250
|
+
name: "discoverylab-knowledge-viewer",
|
|
7251
|
+
title: `DiscoveryLab Flow Viewer \xB7 ${project.marketingTitle || project.name}`,
|
|
7252
|
+
description: "Interactive local flow viewer for DiscoveryLab projects.",
|
|
7253
|
+
mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
|
|
7254
|
+
contents: [
|
|
7255
|
+
{
|
|
7256
|
+
uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
|
|
7257
|
+
mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
|
|
7258
|
+
text: html
|
|
7259
|
+
}
|
|
7260
|
+
]
|
|
7261
|
+
});
|
|
7262
|
+
return {
|
|
7263
|
+
content: [
|
|
7264
|
+
{
|
|
7265
|
+
type: "text",
|
|
7266
|
+
text: `Opened the local DiscoveryLab flow viewer for ${project.marketingTitle || project.name}.`
|
|
7267
|
+
}
|
|
7268
|
+
],
|
|
7269
|
+
structuredContent: {
|
|
7270
|
+
projectId: project.id,
|
|
7271
|
+
name: project.marketingTitle || project.name,
|
|
7272
|
+
frameCount: resolvedFrames.frameFiles.length,
|
|
7273
|
+
platform: project.platform || "unknown"
|
|
7274
|
+
},
|
|
7275
|
+
_meta: {
|
|
7276
|
+
ui: {
|
|
7277
|
+
resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
|
|
7278
|
+
}
|
|
7279
|
+
}
|
|
7280
|
+
};
|
|
7152
7281
|
} catch (error) {
|
|
7153
7282
|
return createErrorResult(`Failed to open flow: ${error instanceof Error ? error.message : String(error)}`);
|
|
7154
7283
|
}
|
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import "./chunk-R5U7XKVJ.js";
|
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
import open from "open";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
14
15
|
var program = new Command();
|
|
15
16
|
var binName = process.argv[1]?.replace(/.*[\\/]/, "").replace(/\.[^.]+$/, "") === "applab" ? "applab" : "discoverylab";
|
|
16
17
|
program.name(binName).description("AI-powered app testing & evidence generator - Claude Code Plugin").version(APP_VERSION);
|
|
@@ -389,7 +390,7 @@ program.command("serve").alias("server").description("Start the DiscoveryLab web
|
|
|
389
390
|
console.log(chalk.cyan("\n DiscoveryLab"));
|
|
390
391
|
console.log(chalk.gray(" AI-powered app testing & evidence generator\n"));
|
|
391
392
|
try {
|
|
392
|
-
const { startServer } = await import("./server-
|
|
393
|
+
const { startServer } = await import("./server-GXNAKM4H.js");
|
|
393
394
|
await startServer(port);
|
|
394
395
|
console.log(chalk.green(` Server running at http://localhost:${port}`));
|
|
395
396
|
console.log(chalk.gray(" Press Ctrl+C to stop\n"));
|
|
@@ -404,7 +405,7 @@ program.command("serve").alias("server").description("Start the DiscoveryLab web
|
|
|
404
405
|
program.command("setup").description("Check and configure DiscoveryLab dependencies").action(async () => {
|
|
405
406
|
console.log(chalk.cyan("\n DiscoveryLab Setup\n"));
|
|
406
407
|
try {
|
|
407
|
-
const { setupStatusTool } = await import("./setup-
|
|
408
|
+
const { setupStatusTool } = await import("./setup-SMN7FJNZ.js");
|
|
408
409
|
const result = await setupStatusTool.handler({});
|
|
409
410
|
if (result.isError) {
|
|
410
411
|
console.error(chalk.red(" Setup check failed"));
|
|
@@ -459,7 +460,11 @@ program.command("install").description("Install DiscoveryLab as MCP server for C
|
|
|
459
460
|
const { existsSync, readFileSync, writeFileSync, mkdirSync } = await import("fs");
|
|
460
461
|
const { join, dirname } = await import("path");
|
|
461
462
|
const home = homedir();
|
|
462
|
-
const
|
|
463
|
+
const localMcpEntrypoint = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
464
|
+
const mcpEntry = existsSync(localMcpEntrypoint) ? {
|
|
465
|
+
command: process.execPath,
|
|
466
|
+
args: [localMcpEntrypoint]
|
|
467
|
+
} : {
|
|
463
468
|
command: "npx",
|
|
464
469
|
args: ["-y", "@veolab/discoverylab@latest", "mcp"]
|
|
465
470
|
};
|
|
@@ -531,7 +536,7 @@ program.command("mcp").description("Run as MCP server (for Claude Code integrati
|
|
|
531
536
|
try {
|
|
532
537
|
const { getDatabase } = await import("./db-5ECN3O7F.js");
|
|
533
538
|
getDatabase();
|
|
534
|
-
const { mcpServer } = await import("./server-
|
|
539
|
+
const { mcpServer } = await import("./server-WN6DCCUA.js");
|
|
535
540
|
const {
|
|
536
541
|
uiTools,
|
|
537
542
|
projectTools,
|
|
@@ -545,7 +550,7 @@ program.command("mcp").description("Run as MCP server (for Claude Code integrati
|
|
|
545
550
|
taskHubTools,
|
|
546
551
|
esvpTools,
|
|
547
552
|
knowledgeTools
|
|
548
|
-
} = await import("./tools-
|
|
553
|
+
} = await import("./tools-6BTUMR3G.js");
|
|
549
554
|
mcpServer.registerTools([
|
|
550
555
|
...uiTools,
|
|
551
556
|
...projectTools,
|
|
@@ -592,7 +597,7 @@ program.command("export").description("Export project in various formats").argum
|
|
|
592
597
|
const { join: pathJoin } = await import("path");
|
|
593
598
|
const { getDatabase, projects, frames: framesTable, FRAMES_DIR, EXPORTS_DIR, PROJECTS_DIR } = await import("./db-5ECN3O7F.js");
|
|
594
599
|
const { eq } = await import("drizzle-orm");
|
|
595
|
-
const {
|
|
600
|
+
const { buildInfographicData, generateInfographicHtml, resolveInfographicFrameInputs } = await import("./infographic-OSDIJM5M.js");
|
|
596
601
|
const db = getDatabase();
|
|
597
602
|
const allProjects = await db.select().from(projects);
|
|
598
603
|
const project = allProjects.find((p) => p.id === projectId || p.id.startsWith(projectId) || p.name.toLowerCase().includes(projectId.toLowerCase()));
|
|
@@ -606,21 +611,27 @@ program.command("export").description("Export project in various formats").argum
|
|
|
606
611
|
}
|
|
607
612
|
console.log(chalk.green(` \u2714 Found project: ${project.marketingTitle || project.name}`));
|
|
608
613
|
const dbFrames = await db.select().from(framesTable).where(eq(framesTable.projectId, project.id)).orderBy(framesTable.frameNumber).limit(20);
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
614
|
+
const resolvedFrames = resolveInfographicFrameInputs(
|
|
615
|
+
dbFrames,
|
|
616
|
+
pathJoin(FRAMES_DIR, project.id),
|
|
617
|
+
project.videoPath,
|
|
618
|
+
PROJECTS_DIR,
|
|
619
|
+
project.id
|
|
620
|
+
);
|
|
621
|
+
console.log(
|
|
622
|
+
chalk.green(
|
|
623
|
+
` \u2714 ${resolvedFrames.frameFiles.length} readable frames found` + (resolvedFrames.candidateCount > 0 ? ` (from ${resolvedFrames.candidateCount} candidates via ${resolvedFrames.source})` : "")
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
if (resolvedFrames.frameFiles.length === 0) {
|
|
627
|
+
console.log(chalk.red(" No readable frames found. Run analyzer first or refresh project screenshots."));
|
|
628
|
+
return;
|
|
617
629
|
}
|
|
618
|
-
|
|
619
|
-
if (
|
|
620
|
-
console.log(chalk.red("
|
|
630
|
+
const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
|
|
631
|
+
if (data.frames.length === 0) {
|
|
632
|
+
console.log(chalk.red(" Export failed: infographic payload did not produce embeddable frames."));
|
|
621
633
|
return;
|
|
622
634
|
}
|
|
623
|
-
const data = buildInfographicData(project, frameFiles, frameOcr);
|
|
624
635
|
console.log(chalk.green(` \u2714 ${project.aiSummary ? "AI analysis loaded" : "No analysis (basic labels)"}`));
|
|
625
636
|
const slug = (project.marketingTitle || project.name || project.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
626
637
|
const outputPath = opts.output ? pathJoin(opts.output, `${slug}-infographic.html`) : pathJoin(EXPORTS_DIR, `${slug}-infographic.html`);
|