@braincrew-lab/langchain-canvas 0.6.1 → 0.7.0

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.
@@ -1,5 +1,5 @@
1
- import { useArtifactPatch } from './chunk-H74O2AEA.js';
2
- import './chunk-AHDFQGCX.js';
1
+ import { useArtifactPatch } from './chunk-N5JW2BKI.js';
2
+ import './chunk-RAPTQ6VH.js';
3
3
  import { useState, useRef, useMemo, useEffect } from 'react';
4
4
  import * as echarts from 'echarts/core';
5
5
  import { BarChart, LineChart, PieChart } from 'echarts/charts';
@@ -1,7 +1,7 @@
1
- import { useAssetUrl } from './chunk-W4W5Q7HF.js';
1
+ import { useAssetUrl } from './chunk-HCF2DXCR.js';
2
2
  import './chunk-7T5DRR3F.js';
3
- import { useArtifactPatch } from './chunk-H74O2AEA.js';
4
- import './chunk-AHDFQGCX.js';
3
+ import { useArtifactPatch } from './chunk-N5JW2BKI.js';
4
+ import './chunk-RAPTQ6VH.js';
5
5
  import { useState, useRef } from 'react';
6
6
  import ReactMarkdown, { defaultUrlTransform } from 'react-markdown';
7
7
  import remarkGfm from 'remark-gfm';
@@ -1,5 +1,5 @@
1
1
  import { isAssetReference, resolveAssetUrl } from './chunk-7T5DRR3F.js';
2
- import { useCanvasStore } from './chunk-AHDFQGCX.js';
2
+ import { useCanvasStore } from './chunk-RAPTQ6VH.js';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
5
5
  function formatSize(size) {
@@ -1,8 +1,8 @@
1
- import { deckPage, pageAspect, DEFAULT_SLIDE_PAGE_IN, resolveElements, fontScaleFor } from './chunk-SE6AP3A7.js';
2
- import { useAssetUrl } from './chunk-W4W5Q7HF.js';
1
+ import { deckPage, pageAspect, DEFAULT_SLIDE_PAGE_IN, resolveElements, fontScaleFor } from './chunk-GXCUFFEL.js';
2
+ import { useAssetUrl } from './chunk-HCF2DXCR.js';
3
3
  import './chunk-7T5DRR3F.js';
4
- import { useArtifactPatch } from './chunk-H74O2AEA.js';
5
- import './chunk-AHDFQGCX.js';
4
+ import { useArtifactPatch } from './chunk-N5JW2BKI.js';
5
+ import './chunk-RAPTQ6VH.js';
6
6
  import { useState, useRef, useEffect } from 'react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
 
@@ -329,7 +329,7 @@ function SlidesRenderer({ artifact }) {
329
329
  };
330
330
  const duplicateSlide = () => {
331
331
  const next = [...slides];
332
- next.splice(at + 1, 0, { ...slide, elements: resolveElements(slide).map((e) => ({ ...e })) });
332
+ next.splice(at + 1, 0, { ...slide, elements: resolveElements(slide, page).map((e) => ({ ...e })) });
333
333
  setSlides(next);
334
334
  setIndex(at + 1);
335
335
  };
@@ -354,7 +354,7 @@ function SlidesRenderer({ artifact }) {
354
354
  setSlides(next);
355
355
  setIndex(to);
356
356
  };
357
- const addElement = (el) => update({ elements: [...resolveElements(slide), { ...el, id: newElementId() }] });
357
+ const addElement = (el) => update({ elements: [...resolveElements(slide, page), { ...el, id: newElementId() }] });
358
358
  const addTextEl = () => addElement({ type: "text", x: 12, y: 16, w: 45, h: 16, text: "Text", fontSize: 24 });
359
359
  const addImageEl = (file) => {
360
360
  if (!file) return;
@@ -392,7 +392,7 @@ function SlidesRenderer({ artifact }) {
392
392
  onDragEnd: () => setDragIndex(null),
393
393
  children: /* @__PURE__ */ jsxs("button", { className: "cv-deck__thumb", onClick: () => setIndex(i), children: [
394
394
  /* @__PURE__ */ jsx("span", { className: "cv-deck__thumb-n", children: i + 1 }),
395
- /* @__PURE__ */ jsx("div", { className: "cv-deck__thumb-slide", style: { aspectRatio: aspect, ...s.background ? { background: s.background } : {} }, children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: `${s.padding ?? 0}%` }, children: resolveElements(s).map(
395
+ /* @__PURE__ */ jsx("div", { className: "cv-deck__thumb-slide", style: { aspectRatio: aspect, ...s.background ? { background: s.background } : {} }, children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: `${s.padding ?? 0}%` }, children: resolveElements(s, page).map(
396
396
  (el) => el.type === "text" ? /* @__PURE__ */ jsx(
397
397
  "span",
398
398
  {
@@ -476,7 +476,7 @@ function SlidesRenderer({ artifact }) {
476
476
  /* @__PURE__ */ jsx("button", { onClick: duplicateSlide, title: "Duplicate slide", children: "\u29C9" }),
477
477
  /* @__PURE__ */ jsx("button", { onClick: deleteSlide, title: "Delete slide", disabled: slides.length === 1, children: "\u{1F5D1}" })
478
478
  ] }),
479
- /* @__PURE__ */ jsx("div", { className: "cv-slide cv-slide--blank", ref: editBox.ref, style: { aspectRatio: aspect, ...slideStyle }, children: /* @__PURE__ */ jsx(FreeSlide, { elements: resolveElements(slide), onChange: (elements) => update({ elements }), padding: slide.padding, fontScale: editBox.scale }) }),
479
+ /* @__PURE__ */ jsx("div", { className: "cv-slide cv-slide--blank", ref: editBox.ref, style: { aspectRatio: aspect, ...slideStyle }, children: /* @__PURE__ */ jsx(FreeSlide, { elements: resolveElements(slide, page), onChange: (elements) => update({ elements }), padding: slide.padding, fontScale: editBox.scale }) }),
480
480
  /* @__PURE__ */ jsxs("div", { className: "cv-deck__nav cv-chrome", children: [
481
481
  /* @__PURE__ */ jsx("button", { disabled: at === 0, onClick: () => setIndex(at - 1), "aria-label": "Previous slide", children: "\u2039" }),
482
482
  /* @__PURE__ */ jsxs("span", { children: [
@@ -497,7 +497,7 @@ function SlidesRenderer({ artifact }) {
497
497
  )
498
498
  ] }),
499
499
  presenting && /* @__PURE__ */ jsxs("div", { className: "cv-present", onClick: () => setIndex(Math.min(at + 1, slides.length - 1)), children: [
500
- /* @__PURE__ */ jsx("div", { ref: presentBox.ref, className: "cv-present__slide cv-present__fade cv-slide cv-slide--blank", style: { aspectRatio: aspect, ...slideStyle }, children: /* @__PURE__ */ jsx("div", { className: "cv-free", style: slide.padding ? { inset: `${slide.padding}%` } : void 0, children: resolveElements(slide).map(
500
+ /* @__PURE__ */ jsx("div", { ref: presentBox.ref, className: "cv-present__slide cv-present__fade cv-slide cv-slide--blank", style: { aspectRatio: aspect, ...slideStyle }, children: /* @__PURE__ */ jsx("div", { className: "cv-free", style: slide.padding ? { inset: `${slide.padding}%` } : void 0, children: resolveElements(slide, page).map(
501
501
  (el) => el.type === "text" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("div", { className: "cv-free__text", style: { fontSize: (el.fontSize ?? 24) * presentBox.scale, fontWeight: el.bold ? 700 : 400, color: el.color, textAlign: el.align ?? "left", whiteSpace: "pre-wrap" }, children: el.text }) }, el.id) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: slide.textColor }, children: /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) }, el.id) : /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: assetUrl(el.src), alt: "" }) }, el.id)
502
502
  ) }) }, at),
503
503
  slide.notes ? /* @__PURE__ */ jsx("div", { className: "cv-present__notes", onClick: (e) => e.stopPropagation(), children: slide.notes }) : null,
@@ -1,7 +1,7 @@
1
1
  import { mergeRowsIntoSheet } from './chunk-IFNRLN4Y.js';
2
2
  import { computeFormulas } from './chunk-SGOPRUQ4.js';
3
3
  import './chunk-YZZSJJMQ.js';
4
- import { useCanvasStore } from './chunk-AHDFQGCX.js';
4
+ import { useCanvasStore } from './chunk-RAPTQ6VH.js';
5
5
  import { lazy, useMemo, useState, useRef, useEffect, useCallback, Suspense } from 'react';
6
6
  import '@fortune-sheet/react/dist/index.css';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -0,0 +1,157 @@
1
+ // src/client/slidePage.ts
2
+ var DEFAULT_SLIDE_PAGE_IN = { widthIn: 10, heightIn: 5.625 };
3
+ var PAGE_DPI = 96;
4
+ function deckPage(data) {
5
+ const page = data.page;
6
+ if (!page) return DEFAULT_SLIDE_PAGE_IN;
7
+ const { widthIn, heightIn } = page;
8
+ if (!Number.isFinite(widthIn) || !Number.isFinite(heightIn) || widthIn <= 0 || heightIn <= 0) {
9
+ return DEFAULT_SLIDE_PAGE_IN;
10
+ }
11
+ return { widthIn, heightIn };
12
+ }
13
+ function pageAspect(page) {
14
+ return `${page.widthIn} / ${page.heightIn}`;
15
+ }
16
+ function fontScaleFor(boxWidthPx, page) {
17
+ if (!Number.isFinite(boxWidthPx) || boxWidthPx <= 0) return 1;
18
+ return boxWidthPx / (page.widthIn * PAGE_DPI);
19
+ }
20
+
21
+ // src/client/slideElements.ts
22
+ var FONT_DISPLAY = 48;
23
+ var FONT_TITLE = 38;
24
+ var BODY_RAMP = [30, 24, 19];
25
+ var LINE_HEIGHT = 1.35;
26
+ var GAP_MIN = 0.35;
27
+ var GAP_MAX = 3;
28
+ var BODY_TOP = 28;
29
+ var BODY_BOTTOM = 88;
30
+ var BODY_LEFT = 8;
31
+ var BODY_WIDTH = 84;
32
+ var TITLE_TOP = 7;
33
+ var TITLE_LEFT = 6;
34
+ var TITLE_WIDTH = 88;
35
+ var COLUMN_WIDTH = 42;
36
+ var COLUMN_RIGHT_LEFT = 52;
37
+ var BULLET = "\u2022 ";
38
+ var WIDE_CHAR_FLOOR = 11903;
39
+ function pagePx(page) {
40
+ const fallback = [DEFAULT_SLIDE_PAGE_IN.widthIn * PAGE_DPI, DEFAULT_SLIDE_PAGE_IN.heightIn * PAGE_DPI];
41
+ if (!page) return fallback;
42
+ const { widthIn, heightIn } = page;
43
+ if (!(widthIn > 0 && heightIn > 0)) return fallback;
44
+ return [widthIn * PAGE_DPI, heightIn * PAGE_DPI];
45
+ }
46
+ function displayWidth(text) {
47
+ let width = 0;
48
+ for (const ch of text) width += (ch.codePointAt(0) ?? 0) > WIDE_CHAR_FLOOR ? 2 : 1;
49
+ return width;
50
+ }
51
+ function linePercent(fontPx, pageHeightPx) {
52
+ return fontPx * LINE_HEIGHT / pageHeightPx * 100;
53
+ }
54
+ function lineCount(text, widthPercent, fontPx, pageWidthPx) {
55
+ if (!text) return 1;
56
+ const perLine = widthPercent / 100 * pageWidthPx / (fontPx / 2);
57
+ if (perLine < 1) return 1;
58
+ return Math.max(1, Math.ceil(displayWidth(text) / perLine));
59
+ }
60
+ function fit(texts, width, band, [pageW, pageH]) {
61
+ let size = BODY_RAMP[BODY_RAMP.length - 1];
62
+ let counts = [];
63
+ for (const candidate of BODY_RAMP) {
64
+ size = candidate;
65
+ counts = texts.map((t) => lineCount(t, width, candidate, pageW));
66
+ const line = linePercent(candidate, pageH);
67
+ const ink = counts.reduce((a, b) => a + b, 0) * line;
68
+ const floorGap = GAP_MIN * line * Math.max(0, texts.length - 1);
69
+ if (ink + floorGap <= band) return [candidate, counts];
70
+ }
71
+ return [size, counts];
72
+ }
73
+ function place(counts, size, o) {
74
+ const n = counts.length;
75
+ if (n === 0) return [];
76
+ const band = o.bottom - o.top;
77
+ const line = linePercent(size, o.pageHeightPx);
78
+ const ink = counts.reduce((a, b) => a + b, 0) * line;
79
+ if (ink > band) {
80
+ const pitch = band / n;
81
+ return counts.map((_, i) => ({ x: o.left, y: o.top + i * pitch, w: o.width, h: pitch, fontSize: size }));
82
+ }
83
+ const gap = n > 1 ? Math.min(Math.max((band - ink) / (n - 1), 0), GAP_MAX * line) : 0;
84
+ const boxes = [];
85
+ let y = o.top + Math.max(band - (ink + gap * (n - 1)), 0) / 2;
86
+ for (const count of counts) {
87
+ const h = count * line;
88
+ boxes.push({ x: o.left, y, w: o.width, h, fontSize: size });
89
+ y += h + gap;
90
+ }
91
+ return boxes;
92
+ }
93
+ function bullets(texts, o) {
94
+ if (!texts.length) return [];
95
+ const lines = texts.map((t) => BULLET + t);
96
+ const [size, counts] = o.size === void 0 ? fit(lines, o.width, o.bottom - o.top, o.pagePx) : [o.size, lines.map((t) => lineCount(t, o.width, o.size, o.pagePx[0]))];
97
+ return place(counts, size, { top: o.top, bottom: o.bottom, left: o.left, width: o.width, pageHeightPx: o.pagePx[1] });
98
+ }
99
+ function toElements(s, page) {
100
+ const px = pagePx(page);
101
+ const [pageW, pageH] = px;
102
+ const layout = s.layout ?? "content";
103
+ const els = [];
104
+ const push = (id, e) => els.push({ color: s.textColor, ...e, id });
105
+ if (layout === "title" || layout === "section") {
106
+ const titleH = s.title ? lineCount(s.title, TITLE_WIDTH, FONT_DISPLAY, pageW) * linePercent(FONT_DISPLAY, pageH) : 0;
107
+ const subH = s.subtitle ? lineCount(s.subtitle, TITLE_WIDTH, BODY_RAMP[0], pageW) * linePercent(BODY_RAMP[0], pageH) : 0;
108
+ const spacer = titleH && subH ? linePercent(BODY_RAMP[0], pageH) : 0;
109
+ let y = (100 - (titleH + spacer + subH)) / 2;
110
+ if (s.title) {
111
+ push("title", { type: "text", x: TITLE_LEFT, y, w: TITLE_WIDTH, h: titleH, text: s.title, fontSize: FONT_DISPLAY, bold: true, align: "center" });
112
+ y += titleH + spacer;
113
+ }
114
+ if (s.subtitle) {
115
+ push("subtitle", { type: "text", x: TITLE_LEFT, y, w: TITLE_WIDTH, h: subH, text: s.subtitle, fontSize: BODY_RAMP[0], align: "center" });
116
+ }
117
+ return els;
118
+ }
119
+ let bodyTop = BODY_TOP;
120
+ if (s.title) {
121
+ const h = lineCount(s.title, TITLE_WIDTH, FONT_TITLE, pageW) * linePercent(FONT_TITLE, pageH);
122
+ push("title", { type: "text", x: TITLE_LEFT, y: TITLE_TOP, w: TITLE_WIDTH, h, text: s.title, fontSize: FONT_TITLE, bold: true });
123
+ bodyTop = Math.max(bodyTop, TITLE_TOP + h + linePercent(FONT_TITLE, pageH) * 0.9);
124
+ }
125
+ if (layout === "image") {
126
+ if (s.image) els.push({ id: "img", type: "image", x: 14, y: bodyTop, w: 72, h: Math.max(BODY_BOTTOM - bodyTop, 0), src: s.image });
127
+ return els;
128
+ }
129
+ if (s.subtitle) {
130
+ const h = lineCount(s.subtitle, TITLE_WIDTH, BODY_RAMP[0], pageW) * linePercent(BODY_RAMP[0], pageH);
131
+ push("subtitle", { type: "text", x: TITLE_LEFT, y: bodyTop, w: TITLE_WIDTH, h, text: s.subtitle, fontSize: BODY_RAMP[0] });
132
+ bodyTop = bodyTop + h + linePercent(BODY_RAMP[0], pageH) * 0.9;
133
+ }
134
+ if (layout === "two-column") {
135
+ const band = BODY_BOTTOM - bodyTop;
136
+ const left = s.bullets ?? [];
137
+ const right = s.bullets2 ?? [];
138
+ const size = Math.min(
139
+ fit(left.map((t) => BULLET + t), COLUMN_WIDTH, band, px)[0],
140
+ fit(right.map((t) => BULLET + t), COLUMN_WIDTH, band, px)[0]
141
+ );
142
+ for (const [prefix, texts2, x] of [["bul", left, BODY_LEFT - 2], ["bul2", right, COLUMN_RIGHT_LEFT]]) {
143
+ bullets(texts2, { top: bodyTop, bottom: BODY_BOTTOM, left: x, width: COLUMN_WIDTH, pagePx: px, size }).forEach(
144
+ (b, i) => push(`${prefix}_${i}`, { type: "text", x: b.x, y: b.y, w: b.w, h: b.h, text: BULLET + texts2[i], fontSize: b.fontSize })
145
+ );
146
+ }
147
+ return els;
148
+ }
149
+ const texts = s.bullets ?? [];
150
+ bullets(texts, { top: bodyTop, bottom: BODY_BOTTOM, left: BODY_LEFT, width: BODY_WIDTH, pagePx: px }).forEach(
151
+ (b, i) => push(`bul_${i}`, { type: "text", x: b.x, y: b.y, w: b.w, h: b.h, text: BULLET + texts[i], fontSize: b.fontSize })
152
+ );
153
+ return els;
154
+ }
155
+ var resolveElements = (s, page) => s.elements?.length ? s.elements : toElements(s, page);
156
+
157
+ export { DEFAULT_SLIDE_PAGE_IN, deckPage, fontScaleFor, pageAspect, resolveElements };
@@ -1,5 +1,5 @@
1
1
  import { isAssetReference, resolveAssetUrl } from './chunk-7T5DRR3F.js';
2
- import { useCanvasStore } from './chunk-AHDFQGCX.js';
2
+ import { useCanvasStore } from './chunk-RAPTQ6VH.js';
3
3
  import { useCallback } from 'react';
4
4
 
5
5
  function useAssetUrl() {
@@ -1,4 +1,4 @@
1
- import { useCanvasStore } from './chunk-AHDFQGCX.js';
1
+ import { useCanvasStore } from './chunk-RAPTQ6VH.js';
2
2
  import { useCallback } from 'react';
3
3
 
4
4
  function useArtifactPatch(id) {
@@ -58,7 +58,9 @@ function reduceCanvas(state, event) {
58
58
  ...event.revision ? { revision: event.revision } : {}
59
59
  }
60
60
  };
61
- const keep = event.amends && versions.length > 1 ? -2 : -1;
61
+ const tail = versions[versions.length - 1];
62
+ const working = tail && typeof tail.meta?.commitDescription !== "string";
63
+ const keep = event.amends && working && versions.length > 1 ? -2 : -1;
62
64
  return {
63
65
  ...state,
64
66
  artifacts: { ...state.artifacts, [event.id]: committed },
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
  import { projectSheetIntoRows, mergeRowsIntoSheet } from './chunk-IFNRLN4Y.js';
3
3
  import { loadOptional } from './chunk-YZZSJJMQ.js';
4
- import { deckPage, resolveElements } from './chunk-SE6AP3A7.js';
5
- export { useAssetUrl } from './chunk-W4W5Q7HF.js';
4
+ import { deckPage, resolveElements } from './chunk-GXCUFFEL.js';
5
+ export { useAssetUrl } from './chunk-HCF2DXCR.js';
6
6
  import { inlineArtifactAssets, inlineHtmlAssets } from './chunk-7T5DRR3F.js';
7
7
  export { ASSET_REFERENCE_PREFIXES, fetchAssetDataUri, inlineArtifactAssets, inlineHtmlAssets, isAssetReference, normalizeAssetReference, resolveAssetUrl } from './chunk-7T5DRR3F.js';
8
- export { useArtifactPatch } from './chunk-H74O2AEA.js';
9
- import { useCanvasStoreApi, useCanvasStore } from './chunk-AHDFQGCX.js';
10
- export { CanvasProvider, createCanvasStore, emptyCanvasState, isCanvasEvent, isChatEvent, mergePatch, reduceCanvas, useCanvasStore, useCanvasStoreApi } from './chunk-AHDFQGCX.js';
8
+ export { useArtifactPatch } from './chunk-N5JW2BKI.js';
9
+ import { useCanvasStoreApi, useCanvasStore } from './chunk-RAPTQ6VH.js';
10
+ export { CanvasProvider, createCanvasStore, emptyCanvasState, isCanvasEvent, isChatEvent, mergePatch, reduceCanvas, useCanvasStore, useCanvasStoreApi } from './chunk-RAPTQ6VH.js';
11
11
  import { createContext, lazy, useMemo, useRef, useCallback, useEffect, useContext, useState, Suspense, Component } from 'react';
12
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
13
 
@@ -2255,11 +2255,11 @@ function HtmlRenderer({ artifact: artifact2 }) {
2255
2255
  }
2256
2256
 
2257
2257
  // src/components/renderers/index.ts
2258
- var ChartRenderer = lazy(() => import('./ChartRenderer-KQ25Q3YG.js').then((m) => ({ default: m.ChartRenderer })));
2259
- var DocumentRenderer = lazy(() => import('./DocumentRenderer-VSVQSVJE.js').then((m) => ({ default: m.DocumentRenderer })));
2260
- var TableRenderer = lazy(() => import('./TableRenderer-C4RZRE52.js').then((m) => ({ default: m.TableRenderer })));
2261
- var SlidesRenderer = lazy(() => import('./SlidesRenderer-SZO3OVXV.js').then((m) => ({ default: m.SlidesRenderer })));
2262
- var FileRenderer = lazy(() => import('./FileRenderer-RW6JR5FL.js').then((m) => ({ default: m.FileRenderer })));
2258
+ var ChartRenderer = lazy(() => import('./ChartRenderer-TBTWNPV7.js').then((m) => ({ default: m.ChartRenderer })));
2259
+ var DocumentRenderer = lazy(() => import('./DocumentRenderer-ER5TSNA6.js').then((m) => ({ default: m.DocumentRenderer })));
2260
+ var TableRenderer = lazy(() => import('./TableRenderer-YTOF2ZIC.js').then((m) => ({ default: m.TableRenderer })));
2261
+ var SlidesRenderer = lazy(() => import('./SlidesRenderer-YLO3NLEY.js').then((m) => ({ default: m.SlidesRenderer })));
2262
+ var FileRenderer = lazy(() => import('./FileRenderer-2CDJYZ7I.js').then((m) => ({ default: m.FileRenderer })));
2263
2263
  var builtinRenderers = {
2264
2264
  html: HtmlRenderer,
2265
2265
  document: DocumentRenderer,
@@ -2423,7 +2423,7 @@ async function slidesToPptx(data, _title) {
2423
2423
  const tc = slide.textColor ? slide.textColor.replace("#", "") : void 0;
2424
2424
  const pad = (slide.padding ?? 0) / 100;
2425
2425
  const inset = (v) => pad + v / 100 * (1 - 2 * pad);
2426
- for (const el of resolveElements(slide)) {
2426
+ for (const el of resolveElements(slide, { widthIn: W, heightIn: H })) {
2427
2427
  const box = { x: inset(el.x) * W, y: inset(el.y) * H, w: el.w / 100 * (1 - 2 * pad) * W, h: el.h / 100 * (1 - 2 * pad) * H };
2428
2428
  if (el.type === "text") {
2429
2429
  const color = el.color ? el.color.replace("#", "") : tc;
@@ -2451,7 +2451,7 @@ function slidesToPrintHtml(data, title) {
2451
2451
  const pages = slides2.map((slide) => {
2452
2452
  const bg = slide.background ?? "#ffffff";
2453
2453
  const fg = slide.textColor ?? "#1f2328";
2454
- const els = resolveElements(slide).map((el) => {
2454
+ const els = resolveElements(slide, page).map((el) => {
2455
2455
  const box = `left:${el.x}%;top:${el.y}%;width:${el.w}%;height:${el.h}%`;
2456
2456
  if (el.type === "text") {
2457
2457
  const style = `${box};font-size:${(el.fontSize ?? 24) / 7.2}vw;font-weight:${el.bold ? 700 : 400};color:${escapeAttr(el.color ?? fg)};text-align:${escapeAttr(el.align ?? "left")};white-space:pre-wrap`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@braincrew-lab/langchain-canvas",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "A live canvas for LangChain agents — stream documents, charts, and rich artifacts into a React panel.",
5
5
  "license": "MIT",
6
6
  "author": "Brain Crew (https://github.com/braincrew-lab)",
@@ -1,44 +0,0 @@
1
- // src/client/slideElements.ts
2
- function toElements(s) {
3
- const layout = s.layout ?? "content";
4
- const els = [];
5
- const push = (id, e) => els.push({ color: s.textColor, ...e, id });
6
- if (layout === "title" || layout === "section") {
7
- if (s.title) push("title", { type: "text", x: 10, y: 34, w: 80, h: 18, text: s.title, fontSize: layout === "title" ? 54 : 40, bold: true, align: "center" });
8
- if (s.subtitle) push("subtitle", { type: "text", x: 10, y: 58, w: 80, h: 8, text: s.subtitle, fontSize: 24, align: "center" });
9
- } else if (layout === "image") {
10
- if (s.title) push("title", { type: "text", x: 6, y: 6, w: 88, h: 10, text: s.title, fontSize: 28, bold: true });
11
- if (s.image) els.push({ id: "img", type: "image", x: 14, y: 20, w: 72, h: 66, src: s.image });
12
- } else if (layout === "two-column") {
13
- if (s.title) push("title", { type: "text", x: 6, y: 6, w: 88, h: 10, text: s.title, fontSize: 28, bold: true });
14
- (s.bullets ?? []).forEach((b, i) => push(`bul_${i}`, { type: "text", x: 6, y: 24 + i * 8, w: 42, h: 7, text: `\u2022 ${b}`, fontSize: 18 }));
15
- (s.bullets2 ?? []).forEach((b, i) => push(`bul2_${i}`, { type: "text", x: 52, y: 24 + i * 8, w: 42, h: 7, text: `\u2022 ${b}`, fontSize: 18 }));
16
- } else {
17
- if (s.title) push("title", { type: "text", x: 6, y: 8, w: 88, h: 10, text: s.title, fontSize: 32, bold: true });
18
- (s.bullets ?? []).forEach((b, i) => push(`bul_${i}`, { type: "text", x: 8, y: 28 + i * 9, w: 84, h: 8, text: `\u2022 ${b}`, fontSize: 20 }));
19
- }
20
- return els;
21
- }
22
- var resolveElements = (s) => s.elements?.length ? s.elements : toElements(s);
23
-
24
- // src/client/slidePage.ts
25
- var DEFAULT_SLIDE_PAGE_IN = { widthIn: 10, heightIn: 5.625 };
26
- var PAGE_DPI = 96;
27
- function deckPage(data) {
28
- const page = data.page;
29
- if (!page) return DEFAULT_SLIDE_PAGE_IN;
30
- const { widthIn, heightIn } = page;
31
- if (!Number.isFinite(widthIn) || !Number.isFinite(heightIn) || widthIn <= 0 || heightIn <= 0) {
32
- return DEFAULT_SLIDE_PAGE_IN;
33
- }
34
- return { widthIn, heightIn };
35
- }
36
- function pageAspect(page) {
37
- return `${page.widthIn} / ${page.heightIn}`;
38
- }
39
- function fontScaleFor(boxWidthPx, page) {
40
- if (!Number.isFinite(boxWidthPx) || boxWidthPx <= 0) return 1;
41
- return boxWidthPx / (page.widthIn * PAGE_DPI);
42
- }
43
-
44
- export { DEFAULT_SLIDE_PAGE_IN, deckPage, fontScaleFor, pageAspect, resolveElements };