@braincrew-lab/langchain-canvas 0.1.10 → 0.1.12
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.
|
@@ -67,6 +67,16 @@ function ChartRenderer({ artifact }) {
|
|
|
67
67
|
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: !!options?.stacked, onChange: (e) => patch({ options: { ...options, stacked: e.target.checked } }) }),
|
|
68
68
|
"Stacked"
|
|
69
69
|
] }),
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
"input",
|
|
72
|
+
{
|
|
73
|
+
className: "cv-chart__title-input",
|
|
74
|
+
value: options?.title ?? "",
|
|
75
|
+
placeholder: "Chart title\u2026",
|
|
76
|
+
onChange: (e) => patch({ options: { ...options, title: e.target.value || void 0 } }),
|
|
77
|
+
title: "Chart title"
|
|
78
|
+
}
|
|
79
|
+
),
|
|
70
80
|
/* @__PURE__ */ jsx("span", { className: "cv-chart__spacer" }),
|
|
71
81
|
/* @__PURE__ */ jsx("button", { className: `cv-edit-btn ${editing ? "is-primary" : ""}`, onClick: () => setEditing((v) => !v), children: editing ? "Done" : "Edit data" })
|
|
72
82
|
] }),
|
|
@@ -128,9 +138,12 @@ function DataGrid({
|
|
|
128
138
|
}
|
|
129
139
|
function toEChartsOption(chart, rows, xKey, series, options) {
|
|
130
140
|
const AXIS = "#9aa4b2";
|
|
141
|
+
const titleBlock = options?.title ? { title: { text: options.title, left: "center", top: 2, textStyle: { color: AXIS, fontSize: 15, fontWeight: 600 } } } : {};
|
|
142
|
+
const hasTitle = !!options?.title;
|
|
131
143
|
if (chart === "pie") {
|
|
132
144
|
const valueKey = series[0]?.key ?? "value";
|
|
133
145
|
return {
|
|
146
|
+
...titleBlock,
|
|
134
147
|
tooltip: { trigger: "item" },
|
|
135
148
|
legend: { bottom: 0, textStyle: { color: AXIS } },
|
|
136
149
|
series: [
|
|
@@ -151,9 +164,10 @@ function toEChartsOption(chart, rows, xKey, series, options) {
|
|
|
151
164
|
}
|
|
152
165
|
const stacked = (chart === "bar" || chart === "area") && !!options?.stacked;
|
|
153
166
|
return {
|
|
167
|
+
...titleBlock,
|
|
154
168
|
tooltip: { trigger: "axis" },
|
|
155
169
|
legend: { bottom: 0, textStyle: { color: AXIS } },
|
|
156
|
-
grid: { left: 8, right: 16, top: 24, bottom: 40, containLabel: true },
|
|
170
|
+
grid: { left: 8, right: 16, top: hasTitle ? 44 : 24, bottom: 40, containLabel: true },
|
|
157
171
|
xAxis: {
|
|
158
172
|
type: "category",
|
|
159
173
|
data: rows.map((r) => String(r[xKey] ?? "")),
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { useArtifactPatch } from './chunk-TERWLUW3.js';
|
|
2
|
+
import './chunk-KKLWKR5G.js';
|
|
3
|
+
import { useState, useRef } from 'react';
|
|
4
|
+
import ReactMarkdown from 'react-markdown';
|
|
5
|
+
import remarkGfm from 'remark-gfm';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function wordStats(text) {
|
|
9
|
+
const words = text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
10
|
+
return { words, minutes: Math.max(1, Math.round(words / 200)) };
|
|
11
|
+
}
|
|
12
|
+
function DocumentRenderer({ artifact }) {
|
|
13
|
+
const { content } = artifact.data;
|
|
14
|
+
const patch = useArtifactPatch(artifact.id);
|
|
15
|
+
const [draft, setDraft] = useState(null);
|
|
16
|
+
const taRef = useRef(null);
|
|
17
|
+
const editing = draft !== null;
|
|
18
|
+
const commit = () => {
|
|
19
|
+
if (draft !== null && draft !== content) patch({ content: draft });
|
|
20
|
+
setDraft(null);
|
|
21
|
+
};
|
|
22
|
+
const apply = (fn) => {
|
|
23
|
+
const ta = taRef.current;
|
|
24
|
+
if (!ta || draft === null) return;
|
|
25
|
+
const { text, a, b } = fn(draft, ta.selectionStart, ta.selectionEnd);
|
|
26
|
+
setDraft(text);
|
|
27
|
+
requestAnimationFrame(() => {
|
|
28
|
+
ta.focus();
|
|
29
|
+
ta.setSelectionRange(a, b);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const wrap = (mark) => apply((full, s, e) => ({ text: full.slice(0, s) + mark + full.slice(s, e) + mark + full.slice(e), a: s + mark.length, b: e + mark.length }));
|
|
33
|
+
const link = () => apply((full, s, e) => {
|
|
34
|
+
const label = full.slice(s, e) || "link";
|
|
35
|
+
const ins = `[${label}](https://)`;
|
|
36
|
+
const urlAt = s + label.length + 3;
|
|
37
|
+
return { text: full.slice(0, s) + ins + full.slice(e), a: urlAt, b: urlAt + 8 };
|
|
38
|
+
});
|
|
39
|
+
const prefixLines = (prefix) => apply((full, s, e) => {
|
|
40
|
+
const start = full.lastIndexOf("\n", s - 1) + 1;
|
|
41
|
+
const nl = full.indexOf("\n", e);
|
|
42
|
+
const end = nl === -1 ? full.length : nl;
|
|
43
|
+
const block = full.slice(start, end).split("\n").map((l) => prefix + l).join("\n");
|
|
44
|
+
return { text: full.slice(0, start) + block + full.slice(end), a: start, b: start + block.length };
|
|
45
|
+
});
|
|
46
|
+
const { words, minutes } = wordStats(editing ? draft ?? "" : content);
|
|
47
|
+
return /* @__PURE__ */ jsxs("div", { className: "cv-word", children: [
|
|
48
|
+
editing && /* @__PURE__ */ jsxs("div", { className: "cv-doc-bar cv-chrome", onMouseDown: (e) => e.preventDefault(), children: [
|
|
49
|
+
/* @__PURE__ */ jsx("button", { title: "Bold", onMouseDown: (e) => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
wrap("**");
|
|
52
|
+
}, children: /* @__PURE__ */ jsx("b", { children: "B" }) }),
|
|
53
|
+
/* @__PURE__ */ jsx("button", { title: "Italic", onMouseDown: (e) => {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
wrap("*");
|
|
56
|
+
}, children: /* @__PURE__ */ jsx("i", { children: "I" }) }),
|
|
57
|
+
/* @__PURE__ */ jsx("button", { title: "Inline code", onMouseDown: (e) => {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
wrap("`");
|
|
60
|
+
}, children: "<>" }),
|
|
61
|
+
/* @__PURE__ */ jsx("span", { className: "cv-doc-bar__sep" }),
|
|
62
|
+
/* @__PURE__ */ jsx("button", { title: "Heading 1", onMouseDown: (e) => {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
prefixLines("# ");
|
|
65
|
+
}, children: "H1" }),
|
|
66
|
+
/* @__PURE__ */ jsx("button", { title: "Heading 2", onMouseDown: (e) => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
prefixLines("## ");
|
|
69
|
+
}, children: "H2" }),
|
|
70
|
+
/* @__PURE__ */ jsx("button", { title: "Bullet list", onMouseDown: (e) => {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
prefixLines("- ");
|
|
73
|
+
}, children: "\u2022" }),
|
|
74
|
+
/* @__PURE__ */ jsx("button", { title: "Numbered list", onMouseDown: (e) => {
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
prefixLines("1. ");
|
|
77
|
+
}, children: "1." }),
|
|
78
|
+
/* @__PURE__ */ jsx("button", { title: "Quote", onMouseDown: (e) => {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
prefixLines("> ");
|
|
81
|
+
}, children: "\u275D" }),
|
|
82
|
+
/* @__PURE__ */ jsx("button", { title: "Link", onMouseDown: (e) => {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
link();
|
|
85
|
+
}, children: "\u{1F517}" }),
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "cv-doc-bar__spacer" }),
|
|
87
|
+
/* @__PURE__ */ jsxs("span", { className: "cv-doc-bar__count", children: [
|
|
88
|
+
words,
|
|
89
|
+
" words \xB7 ",
|
|
90
|
+
minutes,
|
|
91
|
+
" min read"
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsx("button", { className: "cv-doc-bar__done", onMouseDown: (e) => {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
commit();
|
|
96
|
+
}, children: "Done" })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsx("div", { className: "cv-word__page", children: editing ? /* @__PURE__ */ jsx(
|
|
99
|
+
"textarea",
|
|
100
|
+
{
|
|
101
|
+
ref: taRef,
|
|
102
|
+
className: "cv-doc-editor",
|
|
103
|
+
value: draft,
|
|
104
|
+
autoFocus: true,
|
|
105
|
+
onBlur: commit,
|
|
106
|
+
onChange: (e) => setDraft(e.target.value)
|
|
107
|
+
}
|
|
108
|
+
) : /* @__PURE__ */ jsxs(
|
|
109
|
+
"article",
|
|
110
|
+
{
|
|
111
|
+
className: "cv-doc",
|
|
112
|
+
title: "Click to edit",
|
|
113
|
+
onClick: () => artifact.status !== "streaming" && setDraft(content),
|
|
114
|
+
children: [
|
|
115
|
+
/* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: content }),
|
|
116
|
+
artifact.status === "streaming" && /* @__PURE__ */ jsx("span", { className: "cv-caret", "aria-hidden": true })
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
) })
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { DocumentRenderer };
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ interface ChartSeries {
|
|
|
46
46
|
interface ChartOptions {
|
|
47
47
|
stacked?: boolean;
|
|
48
48
|
yLabel?: string;
|
|
49
|
+
/** Chart title shown above the plot. */
|
|
50
|
+
title?: string;
|
|
49
51
|
/** Per-slice colors for pie charts, index-aligned to `rows`. */
|
|
50
52
|
colors?: string[];
|
|
51
53
|
}
|
|
@@ -374,7 +376,7 @@ interface ChatMessage {
|
|
|
374
376
|
interface IframeCommand {
|
|
375
377
|
artifactId: string;
|
|
376
378
|
/** style · structure (duplicate/delete/move/insert/insert_html) · group/ungroup · set_src · set_slide_style · clear. */
|
|
377
|
-
type: "set_style" | "style_persist" | "commit" | "clear" | "set_src" | "set_slide_style" | "duplicate" | "delete" | "move_up" | "move_down" | "insert" | "insert_html" | "group" | "ungroup";
|
|
379
|
+
type: "set_style" | "style_persist" | "commit" | "clear" | "set_src" | "set_slide_style" | "scroll_to" | "duplicate" | "delete" | "move_up" | "move_down" | "insert" | "insert_html" | "group" | "ungroup";
|
|
378
380
|
/** Target element (omitted for document-level inserts with no selection). */
|
|
379
381
|
cid?: string;
|
|
380
382
|
/** Members to wrap for `group`. */
|
|
@@ -383,6 +385,8 @@ interface IframeCommand {
|
|
|
383
385
|
value?: string;
|
|
384
386
|
/** Style map to apply to the slide root for `set_slide_style` (e.g. background, color). */
|
|
385
387
|
style?: Record<string, string>;
|
|
388
|
+
/** Heading index to scroll into view for `scroll_to`. */
|
|
389
|
+
index?: number;
|
|
386
390
|
/** Tag/block to insert for `insert` (e.g. "h2", "p", "button", "img", "hr", "section"). */
|
|
387
391
|
block?: string;
|
|
388
392
|
/** HTML fragment to insert for `insert_html` (a built-in section template). */
|
package/dist/index.js
CHANGED
|
@@ -568,6 +568,11 @@ var INSPECTOR_SCRIPT = `
|
|
|
568
568
|
var d = e.data;
|
|
569
569
|
if (!d || d.source !== MARK) return;
|
|
570
570
|
if (d.type === "clear") { clearSelected(); return; }
|
|
571
|
+
if (d.type === "scroll_to") {
|
|
572
|
+
var hs = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
|
|
573
|
+
if (hs[d.index]) hs[d.index].scrollIntoView({ behavior: "smooth", block: "start" });
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
571
576
|
if (d.type === "set_style") { var el = byCid(d.cid); if (el) el.style[d.prop] = d.value; return; }
|
|
572
577
|
if (d.type === "style_persist") { var elp = byCid(d.cid); if (elp) { elp.style[d.prop] = d.value; emitEdit(elp); } return; }
|
|
573
578
|
if (d.type === "set_slide_style") {
|
|
@@ -1610,6 +1615,14 @@ var TEMPLATES = {
|
|
|
1610
1615
|
</footer>`
|
|
1611
1616
|
}
|
|
1612
1617
|
};
|
|
1618
|
+
function pageDoc(title, sections) {
|
|
1619
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title></head><body style="margin:0;font-family:'Segoe UI',system-ui,'Apple SD Gothic Neo','Noto Sans KR',sans-serif;background:#0b1020;color:#e6e8ef">${sections.join("\n")}</body></html>`;
|
|
1620
|
+
}
|
|
1621
|
+
var STARTERS = {
|
|
1622
|
+
landing: { label: "Landing page", build: () => pageDoc("Landing", [TEMPLATES.navbar.html, TEMPLATES.hero.html, TEMPLATES.features.html, TEMPLATES.pricing.html, TEMPLATES.cta.html, TEMPLATES.footer.html]) },
|
|
1623
|
+
saas: { label: "SaaS", build: () => pageDoc("SaaS", [TEMPLATES.navbar.html, TEMPLATES.hero.html, TEMPLATES.stats.html, TEMPLATES.features.html, TEMPLATES.testimonial.html, TEMPLATES.faq.html, TEMPLATES.cta.html, TEMPLATES.footer.html]) },
|
|
1624
|
+
portfolio: { label: "Portfolio", build: () => pageDoc("Portfolio", [TEMPLATES.navbar.html, TEMPLATES.hero.html, TEMPLATES.gallery.html, TEMPLATES.contact.html, TEMPLATES.footer.html]) }
|
|
1625
|
+
};
|
|
1613
1626
|
var SLIDE_TEMPLATES = {
|
|
1614
1627
|
title: {
|
|
1615
1628
|
label: "Title",
|
|
@@ -1709,6 +1722,27 @@ var SLIDE_FONTS = {
|
|
|
1709
1722
|
rounded: { label: "Rounded", stack: "'Trebuchet MS', 'Segoe UI', 'Apple SD Gothic Neo', sans-serif" },
|
|
1710
1723
|
condensed: { label: "Condensed", stack: "'Arial Narrow', 'Helvetica Neue', 'Apple SD Gothic Neo', sans-serif" }
|
|
1711
1724
|
};
|
|
1725
|
+
function checkA11y(html) {
|
|
1726
|
+
if (typeof DOMParser === "undefined") return [];
|
|
1727
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
1728
|
+
const issues = [];
|
|
1729
|
+
const count = (sel, keep) => Array.from(doc.querySelectorAll(sel)).filter(keep).length;
|
|
1730
|
+
const imgs = count("img", (el) => !el.getAttribute("alt"));
|
|
1731
|
+
if (imgs) issues.push(`${imgs} image${imgs > 1 ? "s" : ""} missing alt text`);
|
|
1732
|
+
const links = count("a", (el) => !el.textContent?.trim() && !el.getAttribute("aria-label"));
|
|
1733
|
+
if (links) issues.push(`${links} link${links > 1 ? "s" : ""} with no text or aria-label`);
|
|
1734
|
+
const btns = count("button", (el) => !el.textContent?.trim() && !el.getAttribute("aria-label"));
|
|
1735
|
+
if (btns) issues.push(`${btns} button${btns > 1 ? "s" : ""} with no accessible label`);
|
|
1736
|
+
const fields = count("input, textarea, select", (el) => {
|
|
1737
|
+
const id = el.getAttribute("id");
|
|
1738
|
+
const labelled = id && doc.querySelector(`label[for="${CSS.escape(id)}"]`);
|
|
1739
|
+
return !labelled && !el.getAttribute("aria-label") && !el.getAttribute("placeholder");
|
|
1740
|
+
});
|
|
1741
|
+
if (fields) issues.push(`${fields} form field${fields > 1 ? "s" : ""} with no label`);
|
|
1742
|
+
if (!doc.documentElement.getAttribute("lang")) issues.push("Missing a <html lang> attribute");
|
|
1743
|
+
if (!doc.querySelector("h1")) issues.push("No <h1> \u2014 every page needs one top-level heading");
|
|
1744
|
+
return issues;
|
|
1745
|
+
}
|
|
1712
1746
|
var SLIDE_W = 1280;
|
|
1713
1747
|
var SCROLL_FIX = "<style>html,body{overflow:auto!important;height:auto!important;min-height:100%!important}</style>";
|
|
1714
1748
|
function withScrollableBody(html) {
|
|
@@ -1748,6 +1782,7 @@ function HtmlRenderer({ artifact: artifact2 }) {
|
|
|
1748
1782
|
const iframeCommand = useCanvasStore((s) => s.iframeCommand);
|
|
1749
1783
|
const [device, setDevice] = useState("desktop");
|
|
1750
1784
|
const [mode, setMode] = useState("design");
|
|
1785
|
+
const [a11y, setA11y] = useState(null);
|
|
1751
1786
|
const lastSelfHtml = useRef(null);
|
|
1752
1787
|
const srcDocRef = useRef("");
|
|
1753
1788
|
const isFixedSlide = Boolean(artifact2.meta?.ratio);
|
|
@@ -1760,6 +1795,14 @@ function HtmlRenderer({ artifact: artifact2 }) {
|
|
|
1760
1795
|
}, [artifact2.data.html, mode, isFixedSlide]);
|
|
1761
1796
|
const selected = selections.filter((s) => s.artifactId === artifact2.id);
|
|
1762
1797
|
const single = selected.length === 1 ? selected[0] : null;
|
|
1798
|
+
const outline = useMemo(() => {
|
|
1799
|
+
if (typeof DOMParser === "undefined") return [];
|
|
1800
|
+
const doc = new DOMParser().parseFromString(artifact2.data.html, "text/html");
|
|
1801
|
+
return Array.from(doc.querySelectorAll("h1,h2,h3,h4,h5,h6")).map((h) => ({
|
|
1802
|
+
level: Number(h.tagName[1]),
|
|
1803
|
+
text: (h.textContent ?? "").trim().slice(0, 48) || "(untitled)"
|
|
1804
|
+
}));
|
|
1805
|
+
}, [artifact2.data.html]);
|
|
1763
1806
|
useEffect(() => {
|
|
1764
1807
|
function onMessage(event) {
|
|
1765
1808
|
if (event.source !== iframeRef.current?.contentWindow) return;
|
|
@@ -1844,23 +1887,60 @@ function HtmlRenderer({ artifact: artifact2 }) {
|
|
|
1844
1887
|
] }),
|
|
1845
1888
|
/* @__PURE__ */ jsx("span", { className: "cv-html-bar__label", children: "Add" }),
|
|
1846
1889
|
(ratio ? BLOCKS.filter((b) => SLIDE_BLOCK_TAGS.has(b.tag)) : BLOCKS).map((b) => /* @__PURE__ */ jsx("button", { className: "cv-html-add", onClick: () => command("insert", { block: b.tag }), children: b.label }, b.tag)),
|
|
1847
|
-
!ratio && /* @__PURE__ */ jsxs(
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1890
|
+
!ratio && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1891
|
+
/* @__PURE__ */ jsxs(
|
|
1892
|
+
"select",
|
|
1893
|
+
{
|
|
1894
|
+
className: "cv-html-tpl",
|
|
1895
|
+
value: "",
|
|
1896
|
+
title: "Start from a full page template (replaces the page)",
|
|
1897
|
+
onChange: (e) => {
|
|
1898
|
+
const s = STARTERS[e.target.value];
|
|
1899
|
+
if (s) applyEvent({ type: "canvas.patch", id: artifact2.id, patch: { html: s.build() } });
|
|
1900
|
+
e.currentTarget.value = "";
|
|
1901
|
+
},
|
|
1902
|
+
children: [
|
|
1903
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Page\u2026" }),
|
|
1904
|
+
Object.entries(STARTERS).map(([k, v]) => /* @__PURE__ */ jsx("option", { value: k, children: v.label }, k))
|
|
1905
|
+
]
|
|
1906
|
+
}
|
|
1907
|
+
),
|
|
1908
|
+
/* @__PURE__ */ jsxs(
|
|
1909
|
+
"select",
|
|
1910
|
+
{
|
|
1911
|
+
className: "cv-html-tpl",
|
|
1912
|
+
value: "",
|
|
1913
|
+
title: "Insert a section template",
|
|
1914
|
+
onChange: (e) => {
|
|
1915
|
+
const t = TEMPLATES[e.target.value];
|
|
1916
|
+
if (t) sendIframeCommand({ artifactId: artifact2.id, type: "insert_html", cid: single?.cid, html: t.html });
|
|
1917
|
+
e.currentTarget.value = "";
|
|
1918
|
+
},
|
|
1919
|
+
children: [
|
|
1920
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Section\u2026" }),
|
|
1921
|
+
Object.entries(TEMPLATES).map(([k, v]) => /* @__PURE__ */ jsx("option", { value: k, children: v.label }, k))
|
|
1922
|
+
]
|
|
1923
|
+
}
|
|
1924
|
+
),
|
|
1925
|
+
outline.length > 0 && /* @__PURE__ */ jsxs(
|
|
1926
|
+
"select",
|
|
1927
|
+
{
|
|
1928
|
+
className: "cv-html-tpl",
|
|
1929
|
+
value: "",
|
|
1930
|
+
title: "Jump to a heading",
|
|
1931
|
+
onChange: (e) => {
|
|
1932
|
+
const idx = Number(e.target.value);
|
|
1933
|
+
if (!Number.isNaN(idx)) sendIframeCommand({ artifactId: artifact2.id, type: "scroll_to", index: idx });
|
|
1934
|
+
e.currentTarget.value = "";
|
|
1935
|
+
},
|
|
1936
|
+
children: [
|
|
1937
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Outline\u2026" }),
|
|
1938
|
+
outline.map((h, i) => /* @__PURE__ */ jsx("option", { value: i, children: "\xA0".repeat((h.level - 1) * 2) + h.text }, i))
|
|
1939
|
+
]
|
|
1940
|
+
}
|
|
1941
|
+
),
|
|
1942
|
+
/* @__PURE__ */ jsx("button", { className: "cv-html-add", title: "Accessibility check", onClick: () => setA11y(checkA11y(artifact2.data.html)), children: "\u267F Check" })
|
|
1943
|
+
] }),
|
|
1864
1944
|
ratio && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1865
1945
|
/* @__PURE__ */ jsxs(
|
|
1866
1946
|
"select",
|
|
@@ -1979,6 +2059,18 @@ function HtmlRenderer({ artifact: artifact2 }) {
|
|
|
1979
2059
|
/* @__PURE__ */ jsx("button", { className: mode === "code" ? "is-on" : "", onClick: () => setMode("code"), children: "Code" })
|
|
1980
2060
|
] })
|
|
1981
2061
|
] }),
|
|
2062
|
+
a11y !== null && /* @__PURE__ */ jsxs("div", { className: "cv-a11y", role: "status", children: [
|
|
2063
|
+
/* @__PURE__ */ jsx("button", { className: "cv-a11y__close", onClick: () => setA11y(null), "aria-label": "Dismiss", children: "\xD7" }),
|
|
2064
|
+
a11y.length === 0 ? /* @__PURE__ */ jsx("span", { className: "cv-a11y__ok", children: "\u267F No accessibility issues found" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2065
|
+
/* @__PURE__ */ jsxs("b", { children: [
|
|
2066
|
+
"\u267F ",
|
|
2067
|
+
a11y.length,
|
|
2068
|
+
" accessibility issue",
|
|
2069
|
+
a11y.length > 1 ? "s" : ""
|
|
2070
|
+
] }),
|
|
2071
|
+
/* @__PURE__ */ jsx("ul", { children: a11y.map((m, i) => /* @__PURE__ */ jsx("li", { children: m }, i)) })
|
|
2072
|
+
] })
|
|
2073
|
+
] }),
|
|
1982
2074
|
mode === "design" ? /* @__PURE__ */ jsx("div", { className: `cv-html-stage${ratio ? " cv-html-stage--slide" : ""}`, ref: stageRef, children: ratio ? (
|
|
1983
2075
|
// Scaled slide: the iframe lays out at its full design size, then a
|
|
1984
2076
|
// transform shrinks it to fit; a sized wrapper reserves the scaled box
|
|
@@ -2019,8 +2111,8 @@ function HtmlRenderer({ artifact: artifact2 }) {
|
|
|
2019
2111
|
}
|
|
2020
2112
|
|
|
2021
2113
|
// src/components/renderers/index.ts
|
|
2022
|
-
var ChartRenderer = lazy(() => import('./ChartRenderer-
|
|
2023
|
-
var DocumentRenderer = lazy(() => import('./DocumentRenderer-
|
|
2114
|
+
var ChartRenderer = lazy(() => import('./ChartRenderer-CWV2OX3S.js').then((m) => ({ default: m.ChartRenderer })));
|
|
2115
|
+
var DocumentRenderer = lazy(() => import('./DocumentRenderer-KJ6FTGKH.js').then((m) => ({ default: m.DocumentRenderer })));
|
|
2024
2116
|
var TableRenderer = lazy(() => import('./TableRenderer-ZF7MVA7I.js').then((m) => ({ default: m.TableRenderer })));
|
|
2025
2117
|
var SlidesRenderer = lazy(() => import('./SlidesRenderer-7ICJQHYT.js').then((m) => ({ default: m.SlidesRenderer })));
|
|
2026
2118
|
var builtinRenderers = {
|
package/dist/styles.css
CHANGED
|
@@ -295,6 +295,21 @@
|
|
|
295
295
|
|
|
296
296
|
/* --- Word viewer (document page) ---------------------------------------------- */
|
|
297
297
|
|
|
298
|
+
.cv-doc-bar {
|
|
299
|
+
position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 4px;
|
|
300
|
+
max-width: 816px; margin: 0 auto 12px; padding: 6px 8px;
|
|
301
|
+
border: 1px solid var(--cv-border); border-radius: 10px; background: var(--cv-surface);
|
|
302
|
+
flex-wrap: wrap;
|
|
303
|
+
}
|
|
304
|
+
.cv-doc-bar button {
|
|
305
|
+
border: 0; background: transparent; color: var(--cv-text); font: inherit; font-size: 13px;
|
|
306
|
+
min-width: 28px; height: 28px; padding: 0 8px; border-radius: 6px; cursor: pointer;
|
|
307
|
+
}
|
|
308
|
+
.cv-doc-bar button:hover { background: var(--cv-surface-2, rgba(120,120,140,0.12)); }
|
|
309
|
+
.cv-doc-bar__sep { width: 1px; height: 18px; background: var(--cv-border); margin: 0 4px; }
|
|
310
|
+
.cv-doc-bar__spacer { flex: 1; }
|
|
311
|
+
.cv-doc-bar__count { font-size: 12px; color: var(--cv-muted); white-space: nowrap; }
|
|
312
|
+
.cv-doc-bar__done { background: var(--cv-accent) !important; color: #fff; font-weight: 600; }
|
|
298
313
|
.cv-word { background: #edeef1; padding: 28px 16px; min-height: 100%; }
|
|
299
314
|
.cv-word .cv-edit-toolbar { max-width: 816px; margin: 0 auto 12px; }
|
|
300
315
|
.cv-word__page {
|
|
@@ -690,6 +705,12 @@
|
|
|
690
705
|
.cv-chart__types .cv-edit-btn { text-transform: capitalize; }
|
|
691
706
|
.cv-chart__stack { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--cv-muted); cursor: pointer; }
|
|
692
707
|
.cv-chart__spacer { flex: 1; }
|
|
708
|
+
.cv-chart__title-input {
|
|
709
|
+
font: inherit; font-size: 13px; padding: 5px 10px; border-radius: 7px;
|
|
710
|
+
border: 1px solid var(--cv-border); background: var(--cv-bg); color: var(--cv-text);
|
|
711
|
+
min-width: 120px; max-width: 220px;
|
|
712
|
+
}
|
|
713
|
+
.cv-chart__title-input:focus { outline: none; border-color: var(--cv-accent); }
|
|
693
714
|
|
|
694
715
|
/* --- chart editor ------------------------------------------------------------- */
|
|
695
716
|
.cv-chart__editor {
|
|
@@ -761,6 +782,17 @@
|
|
|
761
782
|
/* --- html renderer (base substrate) ------------------------------------------- */
|
|
762
783
|
|
|
763
784
|
.cv-html-wrap { display: flex; flex-direction: column; height: 100%; min-height: 0; gap: 10px; }
|
|
785
|
+
.cv-a11y {
|
|
786
|
+
position: relative; flex: none; padding: 12px 34px 12px 16px;
|
|
787
|
+
border: 1px solid var(--cv-border); border-radius: 10px; background: var(--cv-surface);
|
|
788
|
+
font-size: 13px; color: var(--cv-text);
|
|
789
|
+
}
|
|
790
|
+
.cv-a11y ul { margin: 8px 0 0; padding-left: 18px; color: var(--cv-muted); line-height: 1.7; }
|
|
791
|
+
.cv-a11y__ok { color: var(--cv-good, #1f9d6b); font-weight: 600; }
|
|
792
|
+
.cv-a11y__close {
|
|
793
|
+
position: absolute; top: 6px; right: 8px; border: 0; background: transparent;
|
|
794
|
+
color: var(--cv-muted); font-size: 18px; cursor: pointer; line-height: 1;
|
|
795
|
+
}
|
|
764
796
|
.cv-html-bar {
|
|
765
797
|
display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
|
|
766
798
|
padding: 7px 9px; border: 1px solid var(--cv-border); border-radius: 9px; background: var(--cv-surface);
|
package/package.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useArtifactPatch } from './chunk-TERWLUW3.js';
|
|
2
|
-
import './chunk-KKLWKR5G.js';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import ReactMarkdown from 'react-markdown';
|
|
5
|
-
import remarkGfm from 'remark-gfm';
|
|
6
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
-
|
|
8
|
-
function DocumentRenderer({ artifact }) {
|
|
9
|
-
const { content } = artifact.data;
|
|
10
|
-
const patch = useArtifactPatch(artifact.id);
|
|
11
|
-
const [draft, setDraft] = useState(null);
|
|
12
|
-
const editing = draft !== null;
|
|
13
|
-
const commit = () => {
|
|
14
|
-
if (draft !== null && draft !== content) patch({ content: draft });
|
|
15
|
-
setDraft(null);
|
|
16
|
-
};
|
|
17
|
-
return /* @__PURE__ */ jsx("div", { className: "cv-word", children: /* @__PURE__ */ jsx("div", { className: "cv-word__page", children: editing ? /* @__PURE__ */ jsx(
|
|
18
|
-
"textarea",
|
|
19
|
-
{
|
|
20
|
-
className: "cv-doc-editor",
|
|
21
|
-
value: draft,
|
|
22
|
-
autoFocus: true,
|
|
23
|
-
onBlur: commit,
|
|
24
|
-
onChange: (e) => setDraft(e.target.value)
|
|
25
|
-
}
|
|
26
|
-
) : /* @__PURE__ */ jsxs(
|
|
27
|
-
"article",
|
|
28
|
-
{
|
|
29
|
-
className: "cv-doc",
|
|
30
|
-
title: "Click to edit",
|
|
31
|
-
onClick: () => artifact.status !== "streaming" && setDraft(content),
|
|
32
|
-
children: [
|
|
33
|
-
/* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: content }),
|
|
34
|
-
artifact.status === "streaming" && /* @__PURE__ */ jsx("span", { className: "cv-caret", "aria-hidden": true })
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
) }) });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { DocumentRenderer };
|