@braincrew-lab/langchain-canvas 0.1.2 → 0.1.4

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/dist/index.js CHANGED
@@ -392,6 +392,14 @@ var INSPECTOR_SCRIPT = `
392
392
  }, "*");
393
393
  }, true);
394
394
 
395
+ // Block the browser's native drag (images and links start an HTML5 drag on
396
+ // mousemove by default, which hijacks our free-drag \u2014 the image "ghost" gets
397
+ // dragged instead of the element moving). Suppress it for any editable node.
398
+ document.addEventListener("dragstart", function (e) {
399
+ var t = e.target;
400
+ if (t instanceof Element && t.closest("[data-cid]") && !t.hasAttribute("data-lcx")) e.preventDefault();
401
+ }, true);
402
+
395
403
  // --- drag: freely move an element anywhere, or marquee-select empty space ----
396
404
  document.addEventListener("mousedown", function (e) {
397
405
  suppressClick = false; // a fresh press: never carry a stale suppress
@@ -1545,10 +1553,11 @@ function HtmlRenderer({ artifact: artifact2 }) {
1545
1553
  const lastSelfHtml = useRef(null);
1546
1554
  const srcDocRef = useRef("");
1547
1555
  const srcDoc = useMemo(() => {
1548
- if (artifact2.data.html === lastSelfHtml.current) return srcDocRef.current;
1556
+ if (mode === "design" && artifact2.data.html === lastSelfHtml.current) return srcDocRef.current;
1549
1557
  srcDocRef.current = withInspector(artifact2.data.html);
1558
+ lastSelfHtml.current = null;
1550
1559
  return srcDocRef.current;
1551
- }, [artifact2.data.html]);
1560
+ }, [artifact2.data.html, mode]);
1552
1561
  const selected = selections.filter((s) => s.artifactId === artifact2.id);
1553
1562
  const single = selected.length === 1 ? selected[0] : null;
1554
1563
  useEffect(() => {
@@ -1768,8 +1777,7 @@ var dataExporters = {
1768
1777
  { label: "JSON", extension: "json", mime: MIME.json, build: (a) => JSON.stringify(a.data, null, 2) }
1769
1778
  ],
1770
1779
  slides: [
1771
- { label: "PowerPoint", extension: "pptx", mime: MIME.pptx, build: (a) => slidesToPptx(a.data, a.title) },
1772
- { label: "Figma (JSON)", extension: "json", mime: MIME.json, build: (a) => slidesToFigmaJson(a.data) }
1780
+ { label: "PowerPoint", extension: "pptx", mime: MIME.pptx, build: (a) => slidesToPptx(a.data, a.title) }
1773
1781
  ]
1774
1782
  };
1775
1783
  function toStandaloneHtml(title, renderedHtml) {
@@ -1876,52 +1884,6 @@ async function slidesToPptx(data, _title) {
1876
1884
  }
1877
1885
  return await pptx.write({ outputType: "blob" });
1878
1886
  }
1879
- function slidesToFigmaJson(data) {
1880
- const W = 1280;
1881
- const H = 720;
1882
- const GAP = 64;
1883
- const slides2 = data.slides.length ? data.slides : [{ title: "Empty deck" }];
1884
- const textNode = (x, y, width, characters, fontSize, fontWeight, align, color = "#1F2328") => ({ type: "text", x, y, width, characters, fontSize, fontWeight, align, color });
1885
- const frames = slides2.map((slide, i) => {
1886
- const nodes = [];
1887
- for (const el of resolveElements(slide)) {
1888
- if (el.type !== "text") continue;
1889
- nodes.push(
1890
- textNode(el.x / 100 * W, el.y / 100 * H, el.w / 100 * W, el.text ?? "", el.fontSize ?? 24, el.bold ? 700 : 400, el.align === "center" ? "CENTER" : "LEFT", el.color ?? slide.textColor ?? "#1F2328")
1891
- );
1892
- }
1893
- return { name: `Slide ${i + 1}`, x: 0, y: i * (H + GAP), width: W, height: H, fill: slide.background ?? "#FFFFFF", nodes };
1894
- });
1895
- return JSON.stringify({ type: "langchain-canvas/figma-deck", version: 1, frames }, null, 2);
1896
- }
1897
- function slidesToSvg(data) {
1898
- const W = 1280;
1899
- const H = 720;
1900
- const GAP = 64;
1901
- const slides2 = data.slides.length ? data.slides : [{ title: "Empty deck" }];
1902
- const totalH = slides2.length * H + (slides2.length - 1) * GAP;
1903
- const text = (x, y, value, size, anchor, weight, fill = "#1f2328") => `<text x="${x}" y="${y}" font-family="Inter, Arial, sans-serif" font-size="${size}" font-weight="${weight}" text-anchor="${anchor}" fill="${fill}">${escapeXml(value)}</text>`;
1904
- const frames = slides2.map((slide, i) => {
1905
- const y = i * (H + GAP);
1906
- const bg = slide.background ?? "#ffffff";
1907
- let body = `<rect width="${W}" height="${H}" rx="8" fill="${bg}" stroke="#e5e7eb"/>`;
1908
- for (const el of resolveElements(slide)) {
1909
- const ex = el.x / 100 * W;
1910
- const ey = el.y / 100 * H;
1911
- const ew = el.w / 100 * W;
1912
- if (el.type === "text") {
1913
- const anchor = el.align === "center" ? "middle" : el.align === "right" ? "end" : "start";
1914
- const ax = el.align === "center" ? ex + ew / 2 : el.align === "right" ? ex + ew : ex;
1915
- const fs = el.fontSize ?? 24;
1916
- body += text(ax, ey + fs, el.text ?? "", fs, anchor, el.bold ? 700 : 400, el.color ?? slide.textColor ?? "#1f2328");
1917
- } else if (el.src) {
1918
- body += `<image href="${el.src}" x="${ex}" y="${ey}" width="${ew}" height="${el.h / 100 * H}" preserveAspectRatio="xMidYMid meet"/>`;
1919
- }
1920
- }
1921
- return `<g transform="translate(0 ${y})">${body}</g>`;
1922
- }).join("");
1923
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${totalH}" viewBox="0 0 ${W} ${totalH}">${frames}</svg>`;
1924
- }
1925
1887
  function slidesToPrintHtml(data, title) {
1926
1888
  const slides2 = data.slides.length ? data.slides : [{ title: "Empty deck" }];
1927
1889
  const pages = slides2.map((slide) => {
@@ -2035,14 +1997,6 @@ function ExportMenu({ artifact: artifact2, getRenderedHtml }) {
2035
1997
  }
2036
1998
  setOpen(false);
2037
1999
  };
2038
- const copyToFigma = async () => {
2039
- const svg = slidesToSvg(artifact2.data);
2040
- try {
2041
- await navigator.clipboard.writeText(svg);
2042
- } catch {
2043
- }
2044
- setOpen(false);
2045
- };
2046
2000
  return /* @__PURE__ */ jsxs("div", { className: "cv-export", children: [
2047
2001
  /* @__PURE__ */ jsx(
2048
2002
  "button",
@@ -2065,10 +2019,6 @@ function ExportMenu({ artifact: artifact2, getRenderedHtml }) {
2065
2019
  "PDF ",
2066
2020
  /* @__PURE__ */ jsx("span", { className: "cv-export__ext", children: ".pdf" })
2067
2021
  ] }),
2068
- artifact2.type === "slides" && /* @__PURE__ */ jsxs("button", { role: "menuitem", onClick: copyToFigma, children: [
2069
- "Copy to Figma ",
2070
- /* @__PURE__ */ jsx("span", { className: "cv-export__ext", children: "paste \u2318V" })
2071
- ] }),
2072
2022
  dataOptions.map((option) => /* @__PURE__ */ jsxs("button", { role: "menuitem", onClick: () => exportData(option), children: [
2073
2023
  option.label,
2074
2024
  " ",
@@ -2579,18 +2529,34 @@ function EmptyState({ onOpenFiles }) {
2579
2529
  ] })
2580
2530
  ] });
2581
2531
  }
2582
- var META = {
2532
+ var TYPE_META = {
2583
2533
  html: { icon: "\u{1F310}", label: "Web page" },
2584
2534
  document: { icon: "\u{1F4C4}", label: "Word document" },
2585
2535
  chart: { icon: "\u{1F4CA}", label: "Chart" },
2586
2536
  table: { icon: "\u{1F522}", label: "Excel sheet" },
2587
2537
  slides: { icon: "\u{1F4FD}\uFE0F", label: "PowerPoint deck" }
2588
2538
  };
2539
+ var KIND_META = {
2540
+ web: TYPE_META.html,
2541
+ html: TYPE_META.html,
2542
+ document: TYPE_META.document,
2543
+ doc: TYPE_META.document,
2544
+ chart: TYPE_META.chart,
2545
+ table: TYPE_META.table,
2546
+ sheet: TYPE_META.table,
2547
+ slide: TYPE_META.slides,
2548
+ slides: TYPE_META.slides
2549
+ };
2550
+ function resolveCardMeta(artifact2) {
2551
+ const kind = typeof artifact2.meta?.kind === "string" ? artifact2.meta.kind : void 0;
2552
+ const byKind = kind ? KIND_META[kind] : void 0;
2553
+ return byKind ?? TYPE_META[artifact2.type] ?? { icon: "\u{1F4CE}", label: artifact2.type };
2554
+ }
2589
2555
  function ArtifactCard({ artifactId }) {
2590
2556
  const artifact2 = useCanvasStore((s) => s.canvas.artifacts[artifactId]);
2591
2557
  const setActive = useCanvasStore((s) => s.setActiveArtifact);
2592
2558
  if (!artifact2) return null;
2593
- const meta = META[artifact2.type] ?? { icon: "\u{1F4CE}", label: artifact2.type };
2559
+ const meta = resolveCardMeta(artifact2);
2594
2560
  return /* @__PURE__ */ jsxs("button", { className: "cv-card", onClick: () => setActive(artifact2.id), title: "Open on the canvas", children: [
2595
2561
  /* @__PURE__ */ jsx("span", { className: "cv-card__icon", children: meta.icon }),
2596
2562
  /* @__PURE__ */ jsxs("span", { className: "cv-card__meta", children: [
package/dist/styles.css CHANGED
@@ -208,7 +208,7 @@
208
208
 
209
209
  /* --- body --------------------------------------------------------------------- */
210
210
 
211
- .cv-body { flex: 1; overflow: auto; padding: 24px; }
211
+ .cv-body { flex: 1; min-height: 0; overflow: auto; padding: 24px; }
212
212
  .cv-body--flush { padding: 0; overflow: hidden; }
213
213
  .cv-fallback { color: var(--cv-muted); font-size: 14px; }
214
214
 
@@ -750,7 +750,7 @@
750
750
 
751
751
  /* --- html renderer (base substrate) ------------------------------------------- */
752
752
 
753
- .cv-html-wrap { display: flex; flex-direction: column; height: 100%; gap: 10px; }
753
+ .cv-html-wrap { display: flex; flex-direction: column; height: 100%; min-height: 0; gap: 10px; }
754
754
  .cv-html-bar {
755
755
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
756
756
  padding: 7px 9px; border: 1px solid var(--cv-border); border-radius: 9px; background: var(--cv-surface);
@@ -807,7 +807,10 @@
807
807
  .cv-html {
808
808
  width: 100%;
809
809
  height: 100%;
810
- min-height: 70vh;
810
+ /* Fill the (now height-bounded) stage exactly so the sandboxed page scrolls
811
+ inside the iframe. A forced min-height would push the iframe taller than the
812
+ stage and defeat the stage's overflow, so the panel couldn't scroll at all. */
813
+ min-height: 0;
811
814
  border: 0;
812
815
  border-radius: 10px;
813
816
  background: #fff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@braincrew-lab/langchain-canvas",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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
  "type": "module",
@@ -18,17 +18,29 @@
18
18
  "LICENSE"
19
19
  ],
20
20
  "comment": "Dev resolves to source (no build needed; Next transpiles via transpilePackages). publishConfig swaps to dist for npm.",
21
- "main": "./dist/index.js",
22
- "types": "./dist/index.d.ts",
21
+ "main": "./src/index.ts",
22
+ "types": "./src/index.ts",
23
23
  "exports": {
24
- ".": {
25
- "types": "./dist/index.d.ts",
26
- "import": "./dist/index.js"
27
- },
28
- "./styles.css": "./dist/styles.css"
24
+ ".": "./src/index.ts",
25
+ "./styles.css": "./src/styles/canvas.css"
29
26
  },
30
27
  "publishConfig": {
31
- "access": "public"
28
+ "access": "public",
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js"
35
+ },
36
+ "./styles.css": "./dist/styles.css"
37
+ }
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "typecheck": "tsc --noEmit",
43
+ "test": "vitest run"
32
44
  },
33
45
  "peerDependencies": {
34
46
  "react": "^18 || ^19",
@@ -53,11 +65,5 @@
53
65
  "tsup": "^8.2.4",
54
66
  "typescript": "^5.5.4",
55
67
  "vitest": "^2"
56
- },
57
- "scripts": {
58
- "build": "tsup",
59
- "dev": "tsup --watch",
60
- "typecheck": "tsc --noEmit",
61
- "test": "vitest run"
62
68
  }
63
- }
69
+ }