@braincrew-lab/langchain-canvas 0.1.0 → 0.1.2

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
@@ -1513,9 +1513,27 @@ var TEMPLATES = {
1513
1513
  </section>`
1514
1514
  }
1515
1515
  };
1516
+ var SLIDE_W = 1280;
1517
+ function useSlideFit(ratio, boxRef) {
1518
+ const [scale, setScale] = useState(1);
1519
+ const [rw, rh] = (ratio ?? "16:9").split(/[:x/]/).map(Number);
1520
+ const height = rw && rh ? Math.round(SLIDE_W * rh / rw) : 720;
1521
+ useEffect(() => {
1522
+ if (!ratio) return;
1523
+ const el = boxRef.current;
1524
+ if (!el) return;
1525
+ const fit = () => setScale(Math.min(1, (el.clientWidth - 40) / SLIDE_W));
1526
+ fit();
1527
+ const ro = new ResizeObserver(fit);
1528
+ ro.observe(el);
1529
+ return () => ro.disconnect();
1530
+ }, [ratio, height]);
1531
+ return { scale, width: SLIDE_W, height };
1532
+ }
1516
1533
  function HtmlRenderer({ artifact: artifact2 }) {
1517
1534
  const iframeRef = useRef(null);
1518
1535
  const imgFileRef = useRef(null);
1536
+ const stageRef = useRef(null);
1519
1537
  const api = useCanvasStoreApi();
1520
1538
  const setSelections = useCanvasStore((s) => s.setSelections);
1521
1539
  const applyEvent = useCanvasStore((s) => s.applyUserEvent);
@@ -1587,6 +1605,8 @@ function HtmlRenderer({ artifact: artifact2 }) {
1587
1605
  reader.onload = () => sendIframeCommand({ artifactId: artifact2.id, type: "set_src", cid: single.cid, value: String(reader.result) });
1588
1606
  reader.readAsDataURL(file);
1589
1607
  };
1608
+ const ratio = artifact2.meta?.ratio;
1609
+ const slide = useSlideFit(ratio, stageRef);
1590
1610
  return /* @__PURE__ */ jsxs("div", { className: "cv-html-wrap", children: [
1591
1611
  /* @__PURE__ */ jsx("input", { ref: imgFileRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => {
1592
1612
  onImgFile(e.target.files?.[0]);
@@ -1594,8 +1614,10 @@ function HtmlRenderer({ artifact: artifact2 }) {
1594
1614
  } }),
1595
1615
  /* @__PURE__ */ jsxs("div", { className: "cv-html-bar cv-chrome", children: [
1596
1616
  mode === "design" && /* @__PURE__ */ jsxs(Fragment, { children: [
1597
- /* @__PURE__ */ jsx("div", { className: "cv-html-seg", role: "group", "aria-label": "Preview width", children: DEVICES.map((d) => /* @__PURE__ */ jsx("button", { className: device === d.id ? "is-on" : "", onClick: () => setDevice(d.id), children: d.label }, d.id)) }),
1598
- /* @__PURE__ */ jsx("span", { className: "cv-html-bar__sep" }),
1617
+ !ratio && /* @__PURE__ */ jsxs(Fragment, { children: [
1618
+ /* @__PURE__ */ jsx("div", { className: "cv-html-seg", role: "group", "aria-label": "Preview width", children: DEVICES.map((d) => /* @__PURE__ */ jsx("button", { className: device === d.id ? "is-on" : "", onClick: () => setDevice(d.id), children: d.label }, d.id)) }),
1619
+ /* @__PURE__ */ jsx("span", { className: "cv-html-bar__sep" })
1620
+ ] }),
1599
1621
  /* @__PURE__ */ jsx("span", { className: "cv-html-bar__label", children: "Add" }),
1600
1622
  BLOCKS.map((b) => /* @__PURE__ */ jsx("button", { className: "cv-html-add", onClick: () => command("insert", { block: b.tag }), children: b.label }, b.tag)),
1601
1623
  /* @__PURE__ */ jsxs(
@@ -1656,7 +1678,22 @@ function HtmlRenderer({ artifact: artifact2 }) {
1656
1678
  /* @__PURE__ */ jsx("button", { className: mode === "code" ? "is-on" : "", onClick: () => setMode("code"), children: "Code" })
1657
1679
  ] })
1658
1680
  ] }),
1659
- mode === "design" ? /* @__PURE__ */ jsx("div", { className: "cv-html-stage", children: /* @__PURE__ */ jsx(
1681
+ mode === "design" ? /* @__PURE__ */ jsx("div", { className: `cv-html-stage${ratio ? " cv-html-stage--slide" : ""}`, ref: stageRef, children: ratio ? (
1682
+ // Scaled slide: the iframe lays out at its full design size, then a
1683
+ // transform shrinks it to fit; a sized wrapper reserves the scaled box
1684
+ // so the stage scrolls correctly. Clicks still hit the right elements.
1685
+ /* @__PURE__ */ jsx("div", { style: { width: slide.width * slide.scale, height: slide.height * slide.scale, flex: "0 0 auto" }, children: /* @__PURE__ */ jsx(
1686
+ "iframe",
1687
+ {
1688
+ ref: iframeRef,
1689
+ className: "cv-html",
1690
+ title: artifact2.title,
1691
+ srcDoc,
1692
+ sandbox: "allow-scripts allow-popups allow-modals",
1693
+ style: { width: slide.width, height: slide.height, transform: `scale(${slide.scale})`, transformOrigin: "top left" }
1694
+ }
1695
+ ) })
1696
+ ) : /* @__PURE__ */ jsx(
1660
1697
  "iframe",
1661
1698
  {
1662
1699
  ref: iframeRef,
package/dist/styles.css CHANGED
@@ -788,6 +788,14 @@
788
788
  flex: 1; min-height: 0; overflow: auto; display: flex; justify-content: center;
789
789
  padding: 10px; background: var(--cv-surface); border-radius: 10px;
790
790
  }
791
+ /* Fixed-aspect slide stage — the scaled iframe box sits centered at the top,
792
+ with a card-like frame around it. Still fully editable. */
793
+ .cv-html-stage--slide { align-items: flex-start; padding: 16px; }
794
+ .cv-html-stage--slide > div {
795
+ overflow: hidden; background: #fff; border-radius: 8px;
796
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.14);
797
+ }
798
+ .cv-html-stage--slide iframe { display: block; border: 0; }
791
799
  .cv-html-code {
792
800
  flex: 1; min-height: 0; width: 100%; resize: none;
793
801
  padding: 14px 16px; border: 1px solid var(--cv-border); border-radius: 10px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@braincrew-lab/langchain-canvas",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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",