@veolab/discoverylab 1.3.4 → 1.4.1

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 (37) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/chunk-34KRJWZL.js +477 -0
  4. package/dist/chunk-4VNS5WPM.js +42 -0
  5. package/dist/{chunk-FIL7IWEL.js → chunk-DGXAP477.js} +1 -1
  6. package/dist/{chunk-HGWEHWKJ.js → chunk-DKAX5RCX.js} +1 -1
  7. package/dist/{chunk-7EDIUVIO.js → chunk-EU63HPKT.js} +1 -1
  8. package/dist/chunk-QMUEC6B5.js +288 -0
  9. package/dist/{chunk-FNUN7EPB.js → chunk-RCY26WEK.js} +2 -2
  10. package/dist/{chunk-ZLHIHMSL.js → chunk-SWZIBO2R.js} +1 -1
  11. package/dist/chunk-VYYAP5G5.js +265 -0
  12. package/dist/{chunk-VVIOB362.js → chunk-XAMA3JJG.js} +18 -1
  13. package/dist/{chunk-BE7BFMYC.js → chunk-XWBFSSNB.js} +10224 -393
  14. package/dist/{chunk-AHVBE25Y.js → chunk-YNLUOZSZ.js} +274 -667
  15. package/dist/cli.js +33 -31
  16. package/dist/{db-6WLEVKUV.js → db-745LC5YC.js} +2 -2
  17. package/dist/document-AE4XI2CP.js +104 -0
  18. package/dist/{esvp-KVOWYW6G.js → esvp-4LIAU76K.js} +3 -3
  19. package/dist/{esvp-mobile-GZ5EMYPG.js → esvp-mobile-FKFHDS5Q.js} +4 -4
  20. package/dist/frames-RCNLSDD6.js +24 -0
  21. package/dist/{gridCompositor-M3K3LCLZ.js → gridCompositor-VUWBZXYL.js} +262 -3
  22. package/dist/index.d.ts +32 -0
  23. package/dist/index.html +1197 -9
  24. package/dist/index.js +15 -10
  25. package/dist/notion-api-OXSWOJPZ.js +190 -0
  26. package/dist/{ocr-QDYNCSPE.js → ocr-FXRLEP66.js} +1 -1
  27. package/dist/{playwright-VZ7PXDC5.js → playwright-GYKUH34L.js} +3 -3
  28. package/dist/renderer-D22GCMMD.js +17 -0
  29. package/dist/{server-6N3KIEGP.js → server-NTT2XGCC.js} +1 -1
  30. package/dist/server-TKYRIYJ6.js +24 -0
  31. package/dist/{setup-2SQC5UHJ.js → setup-O6WQQAGP.js} +3 -3
  32. package/dist/templates/bundle/bundle.js +4 -2
  33. package/dist/{tools-YGM5HRIB.js → tools-FVVWKEGC.js} +15 -7
  34. package/package.json +2 -2
  35. package/skills/knowledge-brain/SKILL.md +81 -0
  36. package/dist/chunk-MLKGABMK.js +0 -9
  37. package/dist/server-QKZXPZRC.js +0 -22
@@ -1,4 +1,4 @@
1
- import "./chunk-MLKGABMK.js";
1
+ import "./chunk-4VNS5WPM.js";
2
2
 
3
3
  // src/core/canvas/gridCompositor.ts
4
4
  import { createCanvas, loadImage } from "canvas";
@@ -157,6 +157,62 @@ function calculateCells(layout, canvasWidth, canvasHeight, padding, cellPadding,
157
157
  }
158
158
  break;
159
159
  }
160
+ case "flow-horizontal": {
161
+ const arrowGap = 30;
162
+ const count = Math.min(imageCount, 5);
163
+ const totalArrowGap = arrowGap * (count - 1);
164
+ const annotationSpace = 32;
165
+ const cellWidth = (contentWidth - totalArrowGap) / count;
166
+ const cellHeight = contentHeight - annotationSpace;
167
+ for (let i = 0; i < count; i++) {
168
+ cells.push({
169
+ x: padding + i * (cellWidth + arrowGap),
170
+ y: padding,
171
+ width: cellWidth,
172
+ height: cellHeight
173
+ });
174
+ }
175
+ break;
176
+ }
177
+ case "flow-vertical": {
178
+ const arrowGap = 30;
179
+ const count = Math.min(imageCount, 5);
180
+ const totalArrowGap = arrowGap * (count - 1);
181
+ const annotationSpace = 28;
182
+ const cellHeight = (contentHeight - totalArrowGap - annotationSpace * count) / count;
183
+ for (let i = 0; i < count; i++) {
184
+ const yOffset = i * (cellHeight + arrowGap + annotationSpace);
185
+ cells.push({
186
+ x: padding + contentWidth * 0.15,
187
+ y: padding + yOffset,
188
+ width: contentWidth * 0.7,
189
+ height: cellHeight
190
+ });
191
+ }
192
+ break;
193
+ }
194
+ case "infographic": {
195
+ const headerH = contentHeight * 0.08;
196
+ const footerH = contentHeight * 0.06;
197
+ const annotationH = 28;
198
+ const bodyH = contentHeight - headerH - footerH;
199
+ const count = Math.min(imageCount, 6);
200
+ const cols = count <= 2 ? count : count <= 4 ? 2 : 3;
201
+ const rows = Math.ceil(count / cols);
202
+ const cellW = (contentWidth - cellPadding * (cols - 1)) / cols;
203
+ const cellH = (bodyH - cellPadding * (rows - 1) - annotationH * rows) / rows;
204
+ for (let i = 0; i < count; i++) {
205
+ const col = i % cols;
206
+ const row = Math.floor(i / cols);
207
+ cells.push({
208
+ x: padding + col * (cellW + cellPadding),
209
+ y: padding + headerH + row * (cellH + cellPadding + annotationH),
210
+ width: cellW,
211
+ height: cellH
212
+ });
213
+ }
214
+ break;
215
+ }
160
216
  default:
161
217
  cells.push({
162
218
  x: padding,
@@ -290,6 +346,205 @@ function drawImageInCell(ctx, image, cell, cornerRadius, imageFit = "cover", sha
290
346
  );
291
347
  ctx.restore();
292
348
  }
349
+ function drawStepBadge(ctx, cell, stepNumber, scale = 1) {
350
+ const h = 20 * scale;
351
+ const w = 20 * scale;
352
+ const x = cell.x - 2 * scale;
353
+ const y = cell.y - 2 * scale;
354
+ ctx.save();
355
+ ctx.beginPath();
356
+ ctx.arc(x + w / 2, y + h / 2, w / 2 + 1 * scale, 0, Math.PI * 2);
357
+ ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
358
+ ctx.fill();
359
+ ctx.beginPath();
360
+ ctx.arc(x + w / 2, y + h / 2, w / 2, 0, Math.PI * 2);
361
+ ctx.fillStyle = "#6366f1";
362
+ ctx.fill();
363
+ ctx.fillStyle = "#fff";
364
+ ctx.font = `bold ${11 * scale}px -apple-system, BlinkMacSystemFont, sans-serif`;
365
+ ctx.textAlign = "center";
366
+ ctx.textBaseline = "middle";
367
+ ctx.fillText(String(stepNumber), x + w / 2, y + h / 2 + 0.5 * scale);
368
+ ctx.restore();
369
+ }
370
+ function drawFlowArrow(ctx, fromCell, toCell, direction, scale = 1) {
371
+ ctx.save();
372
+ const arrowSize = 6 * scale;
373
+ const lineColor = "rgba(255, 255, 255, 0.3)";
374
+ if (direction === "right") {
375
+ const startX = fromCell.x + fromCell.width + 2 * scale;
376
+ const endX = toCell.x - 2 * scale;
377
+ const y = fromCell.y + fromCell.height / 2;
378
+ const midX = (startX + endX) / 2;
379
+ ctx.strokeStyle = lineColor;
380
+ ctx.lineWidth = 1.5 * scale;
381
+ ctx.setLineDash([4 * scale, 3 * scale]);
382
+ ctx.beginPath();
383
+ ctx.moveTo(startX, y);
384
+ ctx.lineTo(endX - arrowSize, y);
385
+ ctx.stroke();
386
+ ctx.setLineDash([]);
387
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
388
+ ctx.lineWidth = 2 * scale;
389
+ ctx.lineCap = "round";
390
+ ctx.lineJoin = "round";
391
+ ctx.beginPath();
392
+ ctx.moveTo(endX - arrowSize, y - arrowSize);
393
+ ctx.lineTo(endX, y);
394
+ ctx.lineTo(endX - arrowSize, y + arrowSize);
395
+ ctx.stroke();
396
+ } else {
397
+ const x = fromCell.x + fromCell.width / 2;
398
+ const startY = fromCell.y + fromCell.height + 2 * scale;
399
+ const endY = toCell.y - 2 * scale;
400
+ ctx.strokeStyle = lineColor;
401
+ ctx.lineWidth = 1.5 * scale;
402
+ ctx.setLineDash([4 * scale, 3 * scale]);
403
+ ctx.beginPath();
404
+ ctx.moveTo(x, startY);
405
+ ctx.lineTo(x, endY - arrowSize);
406
+ ctx.stroke();
407
+ ctx.setLineDash([]);
408
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
409
+ ctx.lineWidth = 2 * scale;
410
+ ctx.lineCap = "round";
411
+ ctx.lineJoin = "round";
412
+ ctx.beginPath();
413
+ ctx.moveTo(x - arrowSize, endY - arrowSize);
414
+ ctx.lineTo(x, endY);
415
+ ctx.lineTo(x + arrowSize, endY - arrowSize);
416
+ ctx.stroke();
417
+ }
418
+ ctx.restore();
419
+ }
420
+ function drawAnnotation(ctx, cell, text, position, scale = 1) {
421
+ ctx.save();
422
+ const fontSize = 11 * scale;
423
+ ctx.font = `500 ${fontSize}px -apple-system, BlinkMacSystemFont, sans-serif`;
424
+ ctx.textAlign = "center";
425
+ const maxWidth = cell.width + 20 * scale;
426
+ let displayText = text;
427
+ if (ctx.measureText(text).width > maxWidth) {
428
+ while (ctx.measureText(displayText + "...").width > maxWidth && displayText.length > 0) {
429
+ displayText = displayText.slice(0, -1);
430
+ }
431
+ displayText += "...";
432
+ }
433
+ const textWidth = ctx.measureText(displayText).width;
434
+ const cx = cell.x + cell.width / 2;
435
+ const cy = position === "below" ? cell.y + cell.height + 14 * scale : cell.y - 10 * scale;
436
+ const pillPadX = 8 * scale;
437
+ const pillPadY = 3 * scale;
438
+ const pillW = textWidth + pillPadX * 2;
439
+ const pillH = fontSize + pillPadY * 2;
440
+ ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
441
+ drawRoundedRect(ctx, cx - pillW / 2, cy - pillH / 2, pillW, pillH, pillH / 2);
442
+ ctx.fill();
443
+ ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
444
+ ctx.textBaseline = "middle";
445
+ ctx.fillText(displayText, cx, cy);
446
+ ctx.restore();
447
+ }
448
+ function drawInfoHeader(ctx, title, subtitle, canvasWidth, padding, headerHeight, scale = 1) {
449
+ ctx.save();
450
+ ctx.fillStyle = "#ffffff";
451
+ ctx.font = `bold ${20 * scale}px -apple-system, BlinkMacSystemFont, sans-serif`;
452
+ ctx.textBaseline = "top";
453
+ ctx.fillText(title, padding, padding + 6 * scale);
454
+ const titleWidth = Math.min(ctx.measureText(title).width, canvasWidth * 0.3);
455
+ ctx.strokeStyle = "rgba(99, 102, 241, 0.5)";
456
+ ctx.lineWidth = 2 * scale;
457
+ ctx.beginPath();
458
+ ctx.moveTo(padding, padding + 30 * scale);
459
+ ctx.lineTo(padding + titleWidth, padding + 30 * scale);
460
+ ctx.stroke();
461
+ ctx.restore();
462
+ }
463
+ function drawInfoFooter(ctx, text, canvasWidth, canvasHeight, padding, scale = 1) {
464
+ ctx.save();
465
+ ctx.fillStyle = "rgba(255, 255, 255, 0.25)";
466
+ ctx.font = `${10 * scale}px -apple-system, BlinkMacSystemFont, sans-serif`;
467
+ ctx.textAlign = "right";
468
+ ctx.textBaseline = "bottom";
469
+ ctx.fillText(text, canvasWidth - padding, canvasHeight - padding / 2);
470
+ ctx.restore();
471
+ }
472
+ async function composeInfographic(images, config, outputPath) {
473
+ const gridConfig = {
474
+ layout: config.layout,
475
+ aspectRatio: config.aspectRatio || (config.layout === "flow-horizontal" ? "16:9" : "9:16"),
476
+ background: config.background || {
477
+ type: "gradient",
478
+ gradientStart: "#0f0f23",
479
+ gradientEnd: "#1a1a3e",
480
+ gradientAngle: 160
481
+ },
482
+ outputWidth: config.outputWidth || 1920,
483
+ padding: 60,
484
+ cellPadding: 20,
485
+ cornerRadius: 12,
486
+ shadowEnabled: true,
487
+ shadowBlur: 16,
488
+ imageFit: "contain"
489
+ };
490
+ const cfg = { ...DEFAULT_CONFIG, ...gridConfig };
491
+ const aspectDef = ASPECT_RATIOS[cfg.aspectRatio];
492
+ const scale = cfg.outputWidth / aspectDef.width;
493
+ const canvasWidth = Math.round(aspectDef.width * scale);
494
+ const canvasHeight = Math.round(aspectDef.height * scale);
495
+ const canvas = createCanvas(canvasWidth, canvasHeight);
496
+ const ctx = canvas.getContext("2d");
497
+ let bgImage;
498
+ if (cfg.background.type === "image" && cfg.background.imagePath && existsSync(cfg.background.imagePath)) {
499
+ bgImage = await loadImage(cfg.background.imagePath);
500
+ }
501
+ drawBackground(ctx, canvasWidth, canvasHeight, cfg.background, bgImage);
502
+ if (cfg.background.type === "image" && bgImage) {
503
+ ctx.save();
504
+ ctx.fillStyle = "rgba(0, 0, 0, 0.55)";
505
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
506
+ ctx.restore();
507
+ }
508
+ const headerHeight = canvasHeight * 0.12;
509
+ if (config.layout === "infographic") {
510
+ drawInfoHeader(ctx, config.title, config.subtitle || "", canvasWidth, cfg.padding, headerHeight, scale);
511
+ }
512
+ const cells = calculateCells(cfg.layout, canvasWidth, canvasHeight, cfg.padding, cfg.cellPadding, images.length);
513
+ for (let i = 0; i < Math.min(images.length, cells.length); i++) {
514
+ const cell = cells[i];
515
+ const imageCell = images[i];
516
+ if (!existsSync(imageCell.imagePath)) continue;
517
+ const image = await loadImage(imageCell.imagePath);
518
+ if (cfg.shadowEnabled && cfg.imageFit === "cover") {
519
+ drawShadow(ctx, cell.x, cell.y, cell.width, cell.height, cfg.cornerRadius, cfg.shadowBlur);
520
+ }
521
+ drawImageInCell(ctx, image, cell, cfg.cornerRadius, cfg.imageFit, cfg.shadowEnabled, cfg.shadowBlur);
522
+ if (imageCell.stepNumber !== void 0) {
523
+ drawStepBadge(ctx, cell, imageCell.stepNumber, scale);
524
+ }
525
+ if (imageCell.annotation) {
526
+ drawAnnotation(ctx, cell, imageCell.annotation, "below", scale);
527
+ }
528
+ if (imageCell.flowArrow && imageCell.flowArrow !== "none" && i < cells.length - 1) {
529
+ drawFlowArrow(ctx, cell, cells[i + 1], imageCell.flowArrow, scale);
530
+ }
531
+ }
532
+ if (config.footerText || config.layout === "infographic") {
533
+ const footerText = config.footerText || `Generated by DiscoveryLab \u2022 ${images.length} screens`;
534
+ drawInfoFooter(ctx, footerText, canvasWidth, canvasHeight, cfg.padding, scale);
535
+ }
536
+ try {
537
+ const buffer = canvas.toBuffer("image/png");
538
+ const outputDir = outputPath.substring(0, outputPath.lastIndexOf("/"));
539
+ if (outputDir && !existsSync(outputDir)) {
540
+ mkdirSync(outputDir, { recursive: true });
541
+ }
542
+ writeFileSync(outputPath, buffer);
543
+ return { success: true, outputPath, width: canvasWidth, height: canvasHeight };
544
+ } catch (error) {
545
+ return { success: false, error: error instanceof Error ? error.message : "Infographic export failed" };
546
+ }
547
+ }
293
548
  async function composeGrid(images, config = {}, outputPath) {
294
549
  try {
295
550
  const cfg = { ...DEFAULT_CONFIG, ...config };
@@ -363,12 +618,15 @@ function getLayoutInfo(layout) {
363
618
  "featured": { name: "Featured", maxImages: 3, description: "One large + two small" },
364
619
  "masonry": { name: "Masonry", maxImages: 6, description: "Pinterest-style columns" },
365
620
  "carousel": { name: "Carousel", maxImages: 3, description: "Overlapping cards" },
366
- "stacked": { name: "3D Stack", maxImages: 4, description: "Perspective stack effect" }
621
+ "stacked": { name: "3D Stack", maxImages: 4, description: "Perspective stack effect" },
622
+ "flow-horizontal": { name: "Flow \u2192", maxImages: 5, description: "Steps left to right with arrows" },
623
+ "flow-vertical": { name: "Flow \u2193", maxImages: 5, description: "Steps top to bottom with arrows" },
624
+ "infographic": { name: "Infographic", maxImages: 6, description: "Magazine-style with header" }
367
625
  };
368
626
  return layouts[layout];
369
627
  }
370
628
  function getAllLayouts() {
371
- return ["single", "duo-h", "duo-v", "trio-h", "trio-v", "quad", "featured", "masonry", "carousel", "stacked"];
629
+ return ["single", "duo-h", "duo-v", "trio-h", "trio-v", "quad", "featured", "masonry", "carousel", "stacked", "flow-horizontal", "flow-vertical", "infographic"];
372
630
  }
373
631
  function getAvailableBackgrounds(backgroundsDir) {
374
632
  if (!existsSync(backgroundsDir)) {
@@ -402,6 +660,7 @@ export {
402
660
  PRESET_GRADIENTS,
403
661
  PRESET_SOLID_COLORS,
404
662
  composeGrid,
663
+ composeInfographic,
405
664
  getAllLayouts,
406
665
  getAvailableBackgrounds,
407
666
  getLayoutInfo,
package/dist/index.d.ts CHANGED
@@ -382,6 +382,38 @@ declare const projects: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
382
382
  baseColumn: never;
383
383
  generated: undefined;
384
384
  }, object>;
385
+ marketingTitle: drizzle_orm_sqlite_core.SQLiteColumn<{
386
+ name: "marketing_title";
387
+ tableName: "projects";
388
+ dataType: "string";
389
+ columnType: "SQLiteText";
390
+ data: string;
391
+ driverParam: string;
392
+ notNull: false;
393
+ hasDefault: false;
394
+ isPrimaryKey: false;
395
+ isAutoincrement: false;
396
+ hasRuntimeDefault: false;
397
+ enumValues: [string, ...string[]];
398
+ baseColumn: never;
399
+ generated: undefined;
400
+ }, object>;
401
+ marketingDescription: drizzle_orm_sqlite_core.SQLiteColumn<{
402
+ name: "marketing_description";
403
+ tableName: "projects";
404
+ dataType: "string";
405
+ columnType: "SQLiteText";
406
+ data: string;
407
+ driverParam: string;
408
+ notNull: false;
409
+ hasDefault: false;
410
+ isPrimaryKey: false;
411
+ isAutoincrement: false;
412
+ hasRuntimeDefault: false;
413
+ enumValues: [string, ...string[]];
414
+ baseColumn: never;
415
+ generated: undefined;
416
+ }, object>;
385
417
  status: drizzle_orm_sqlite_core.SQLiteColumn<{
386
418
  name: "status";
387
419
  tableName: "projects";