@braincrew-lab/langchain-canvas 0.7.6 → 0.7.8
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.
|
@@ -142,6 +142,153 @@ function docxStats(root) {
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// src/io/docxAlignment.ts
|
|
146
|
+
var PLACEMENT = {
|
|
147
|
+
// [margin-left, margin-right] — an empty string leaves the side alone.
|
|
148
|
+
center: ["auto", "auto"],
|
|
149
|
+
right: ["auto", ""]
|
|
150
|
+
};
|
|
151
|
+
function ownRows(table) {
|
|
152
|
+
return Array.from(table.rows ?? []);
|
|
153
|
+
}
|
|
154
|
+
function takeBlockAlignment(element) {
|
|
155
|
+
const stated = element.style.textAlign;
|
|
156
|
+
if (stated) element.style.removeProperty("text-align");
|
|
157
|
+
return stated;
|
|
158
|
+
}
|
|
159
|
+
function placed(table) {
|
|
160
|
+
return Boolean(table.style.marginLeft || table.style.marginRight);
|
|
161
|
+
}
|
|
162
|
+
function agreedAlignment(rows) {
|
|
163
|
+
if (rows.length === 0) return "";
|
|
164
|
+
const first = rows[0];
|
|
165
|
+
return rows.every((value) => value === first) ? first : "";
|
|
166
|
+
}
|
|
167
|
+
function separateBlockAlignment(root) {
|
|
168
|
+
const fix = { rows: 0, placed: 0 };
|
|
169
|
+
for (const table of Array.from(root.querySelectorAll("table"))) {
|
|
170
|
+
takeBlockAlignment(table);
|
|
171
|
+
const rows = ownRows(table);
|
|
172
|
+
const stated = rows.map((row) => takeBlockAlignment(row)).filter((value) => value !== "");
|
|
173
|
+
fix.rows += stated.length;
|
|
174
|
+
if (stated.length !== rows.length || placed(table)) continue;
|
|
175
|
+
const margins = PLACEMENT[agreedAlignment(stated)];
|
|
176
|
+
if (!margins) continue;
|
|
177
|
+
if (margins[0]) table.style.marginLeft = margins[0];
|
|
178
|
+
if (margins[1]) table.style.marginRight = margins[1];
|
|
179
|
+
fix.placed += 1;
|
|
180
|
+
}
|
|
181
|
+
return fix;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/io/docxShapes.ts
|
|
185
|
+
var VML_NS = "urn:schemas-microsoft-com:vml";
|
|
186
|
+
var VML_SHAPES = /* @__PURE__ */ new Set([
|
|
187
|
+
"oval",
|
|
188
|
+
"rect",
|
|
189
|
+
"roundrect",
|
|
190
|
+
"line",
|
|
191
|
+
"polyline",
|
|
192
|
+
"arc",
|
|
193
|
+
"curve",
|
|
194
|
+
"shape",
|
|
195
|
+
"group"
|
|
196
|
+
]);
|
|
197
|
+
var PAINTABLE = "ellipse,rect,circle,path,line,polygon,polyline,image,foreignObject";
|
|
198
|
+
function isVml(node) {
|
|
199
|
+
return node.namespaceURI === VML_NS || node.tagName.startsWith("v:");
|
|
200
|
+
}
|
|
201
|
+
function alphaOf(paint) {
|
|
202
|
+
const inside = /^rgba?\(([^)]*)\)$/.exec(paint);
|
|
203
|
+
if (!inside) return null;
|
|
204
|
+
const [channels, slashAlpha] = inside[1].split("/");
|
|
205
|
+
const raw = slashAlpha !== void 0 ? slashAlpha : (() => {
|
|
206
|
+
const parts = channels.split(",").map((part) => part.trim());
|
|
207
|
+
return parts.length === 4 ? parts[3] : void 0;
|
|
208
|
+
})();
|
|
209
|
+
if (raw === void 0) return null;
|
|
210
|
+
const value = raw.trim();
|
|
211
|
+
const number = value.endsWith("%") ? Number(value.slice(0, -1)) / 100 : Number(value);
|
|
212
|
+
return Number.isFinite(number) ? number : null;
|
|
213
|
+
}
|
|
214
|
+
function invisible(paint) {
|
|
215
|
+
const value = paint.trim();
|
|
216
|
+
if (!value || value === "none" || value === "transparent") return true;
|
|
217
|
+
return alphaOf(value) === 0;
|
|
218
|
+
}
|
|
219
|
+
function paints(element) {
|
|
220
|
+
const tag = element.tagName.toLowerCase();
|
|
221
|
+
if (tag === "image" || tag === "foreignobject") return true;
|
|
222
|
+
const style = element.ownerDocument.defaultView?.getComputedStyle(element);
|
|
223
|
+
const resolved = (name) => style?.getPropertyValue(name) || element.getAttribute(name) || "";
|
|
224
|
+
return !invisible(resolved("fill")) || !invisible(resolved("stroke"));
|
|
225
|
+
}
|
|
226
|
+
function drawsNothing(shape) {
|
|
227
|
+
return shape.getAttribute("filled") === "f" && shape.getAttribute("stroked") === "f";
|
|
228
|
+
}
|
|
229
|
+
function vmlShapeIn(node) {
|
|
230
|
+
if (isVml(node) && VML_SHAPES.has(node.localName)) return node;
|
|
231
|
+
for (const child of Array.from(node.children)) {
|
|
232
|
+
const found = vmlShapeIn(child);
|
|
233
|
+
if (found) return found;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function drawingIsShape(drawing) {
|
|
238
|
+
const data = drawing.getElementsByTagName("*");
|
|
239
|
+
for (const node of Array.from(data)) {
|
|
240
|
+
if (node.localName === "graphicData") {
|
|
241
|
+
return !Array.from(node.children).some((child) => child.localName === "pic");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
function insideAlternateContent(node) {
|
|
247
|
+
let parent = node.parentElement;
|
|
248
|
+
while (parent) {
|
|
249
|
+
if (parent.localName === "AlternateContent") return true;
|
|
250
|
+
parent = parent.parentElement;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
function shapesAskedFor(xml) {
|
|
255
|
+
let count = 0;
|
|
256
|
+
const seenTwins = /* @__PURE__ */ new Set();
|
|
257
|
+
for (const node of Array.from(xml.getElementsByTagName("*"))) {
|
|
258
|
+
if (node.localName !== "AlternateContent") continue;
|
|
259
|
+
const twin = vmlShapeIn(node);
|
|
260
|
+
const drawing = Array.from(node.getElementsByTagName("*")).find(
|
|
261
|
+
(child) => child.localName === "drawing"
|
|
262
|
+
);
|
|
263
|
+
const isShape = twin !== null || (drawing ? drawingIsShape(drawing) : false);
|
|
264
|
+
if (isShape && !(twin && drawsNothing(twin))) count += 1;
|
|
265
|
+
seenTwins.add(node);
|
|
266
|
+
}
|
|
267
|
+
for (const node of Array.from(xml.getElementsByTagName("*"))) {
|
|
268
|
+
if (insideAlternateContent(node)) continue;
|
|
269
|
+
if (isVml(node) && VML_SHAPES.has(node.localName)) {
|
|
270
|
+
if (node.parentElement && vmlShapeIn(node.parentElement) === node.parentElement) continue;
|
|
271
|
+
if (!drawsNothing(node)) count += 1;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (node.localName === "drawing" && drawingIsShape(node)) count += 1;
|
|
275
|
+
}
|
|
276
|
+
return count;
|
|
277
|
+
}
|
|
278
|
+
function shapesShown(root) {
|
|
279
|
+
let count = 0;
|
|
280
|
+
for (const svg of Array.from(root.querySelectorAll("svg"))) {
|
|
281
|
+
if (Array.from(svg.querySelectorAll(PAINTABLE)).some(paints)) count += 1;
|
|
282
|
+
}
|
|
283
|
+
return count;
|
|
284
|
+
}
|
|
285
|
+
function tallyShapes(root, xmls) {
|
|
286
|
+
if (xmls.length === 0) return { askedFor: 0, shown: 0, missing: 0 };
|
|
287
|
+
const askedFor = xmls.reduce((total, xml) => total + shapesAskedFor(xml), 0);
|
|
288
|
+
const shown = shapesShown(root);
|
|
289
|
+
return { askedFor, shown, missing: Math.max(0, askedFor - shown) };
|
|
290
|
+
}
|
|
291
|
+
|
|
145
292
|
// src/io/symbolBullets.ts
|
|
146
293
|
var SYMBOL_FONT_BULLETS = {
|
|
147
294
|
symbol: {
|
|
@@ -243,6 +390,12 @@ function redrawnFonts(swaps) {
|
|
|
243
390
|
return [...new Set(swaps.map((swap) => swap.font))];
|
|
244
391
|
}
|
|
245
392
|
var BANNER = "Preview only \u2014 to change it, ask in chat or select some text.";
|
|
393
|
+
var CONTENT_PART = /^word\/(document|header\d*|footer\d*)\.xml$/;
|
|
394
|
+
function storedParts(parsed) {
|
|
395
|
+
const parts = parsed?.parts;
|
|
396
|
+
if (!Array.isArray(parts)) return [];
|
|
397
|
+
return parts.filter((part) => typeof part?.path === "string" && CONTENT_PART.test(part.path)).map((part) => part._xmlDocument).filter((xml) => Boolean(xml));
|
|
398
|
+
}
|
|
246
399
|
function DocxPreview({
|
|
247
400
|
artifactId,
|
|
248
401
|
href,
|
|
@@ -253,6 +406,7 @@ function DocxPreview({
|
|
|
253
406
|
const [status, setStatus] = useState("loading");
|
|
254
407
|
const [stats, setStats] = useState(null);
|
|
255
408
|
const [redrawn, setRedrawn] = useState([]);
|
|
409
|
+
const [missingShapes, setMissingShapes] = useState(0);
|
|
256
410
|
const [picked, setPicked] = useState(null);
|
|
257
411
|
const setSelections = useCanvasStore((s) => s.setSelections);
|
|
258
412
|
useEffect(() => {
|
|
@@ -263,6 +417,7 @@ function DocxPreview({
|
|
|
263
417
|
setStatus("loading");
|
|
264
418
|
setStats(null);
|
|
265
419
|
setRedrawn([]);
|
|
420
|
+
setMissingShapes(0);
|
|
266
421
|
setPicked(null);
|
|
267
422
|
(async () => {
|
|
268
423
|
const { renderAsync } = await loadOptional(
|
|
@@ -275,16 +430,19 @@ function DocxPreview({
|
|
|
275
430
|
const data = await response.arrayBuffer();
|
|
276
431
|
if (!live) return;
|
|
277
432
|
host.replaceChildren();
|
|
278
|
-
await renderAsync(data, host, void 0, {
|
|
433
|
+
const parsed = await renderAsync(data, host, void 0, {
|
|
279
434
|
inWrapper: true,
|
|
280
435
|
breakPages: true,
|
|
281
436
|
renderHeaders: true,
|
|
282
437
|
renderFooters: true,
|
|
283
|
-
useBase64URL: true
|
|
438
|
+
useBase64URL: true,
|
|
439
|
+
keepOrigin: true
|
|
284
440
|
});
|
|
285
441
|
if (!live) return;
|
|
442
|
+
separateBlockAlignment(host);
|
|
286
443
|
stampDocxAddresses(host);
|
|
287
444
|
setRedrawn(redrawnFonts(restoreSymbolBullets(host)));
|
|
445
|
+
setMissingShapes(tallyShapes(host, storedParts(parsed)).missing);
|
|
288
446
|
setStats(docxStats(host));
|
|
289
447
|
setStatus("ready");
|
|
290
448
|
let fittedFor = host.clientWidth;
|
|
@@ -367,6 +525,20 @@ function DocxPreview({
|
|
|
367
525
|
"substituted: ",
|
|
368
526
|
stats.substitutedFonts.join(", ")
|
|
369
527
|
] }),
|
|
528
|
+
missingShapes > 0 && /* @__PURE__ */ jsxs(
|
|
529
|
+
"span",
|
|
530
|
+
{
|
|
531
|
+
className: "cv-docx__missing",
|
|
532
|
+
title: "This document draws shapes the preview cannot show. They are in the file \u2014 a download opens with them in place.",
|
|
533
|
+
children: [
|
|
534
|
+
missingShapes,
|
|
535
|
+
" shape",
|
|
536
|
+
missingShapes === 1 ? "" : "s",
|
|
537
|
+
" not shown \u2014 download the file to see ",
|
|
538
|
+
missingShapes === 1 ? "it" : "them"
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
),
|
|
370
542
|
redrawn.length > 0 && /* @__PURE__ */ jsxs(
|
|
371
543
|
"span",
|
|
372
544
|
{
|
|
@@ -10,7 +10,7 @@ function formatSize(size) {
|
|
|
10
10
|
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
11
11
|
}
|
|
12
12
|
var DocxPreview = lazy(
|
|
13
|
-
() => import('./DocxPreview-
|
|
13
|
+
() => import('./DocxPreview-MAS4GRDG.js').then((m) => ({ default: m.DocxPreview }))
|
|
14
14
|
);
|
|
15
15
|
function iconFor(mediaType, name) {
|
|
16
16
|
if (mediaType?.startsWith("image/")) return "\u{1F5BC}\uFE0F";
|
package/dist/index.js
CHANGED
|
@@ -2264,7 +2264,7 @@ var ChartRenderer = lazy(() => import('./ChartRenderer-TBTWNPV7.js').then((m) =>
|
|
|
2264
2264
|
var DocumentRenderer = lazy(() => import('./DocumentRenderer-ZGJXDVKG.js').then((m) => ({ default: m.DocumentRenderer })));
|
|
2265
2265
|
var TableRenderer = lazy(() => import('./TableRenderer-YTOF2ZIC.js').then((m) => ({ default: m.TableRenderer })));
|
|
2266
2266
|
var SlidesRenderer = lazy(() => import('./SlidesRenderer-4QWZVWWD.js').then((m) => ({ default: m.SlidesRenderer })));
|
|
2267
|
-
var FileRenderer = lazy(() => import('./FileRenderer-
|
|
2267
|
+
var FileRenderer = lazy(() => import('./FileRenderer-66GPPGMK.js').then((m) => ({ default: m.FileRenderer })));
|
|
2268
2268
|
var builtinRenderers = {
|
|
2269
2269
|
html: HtmlRenderer,
|
|
2270
2270
|
document: DocumentRenderer,
|
package/dist/styles.css
CHANGED
|
@@ -1310,6 +1310,7 @@
|
|
|
1310
1310
|
.cv-docx__picked { color: var(--cv-accent); }
|
|
1311
1311
|
.cv-docx__fonts { color: var(--cv-warn, #b45309); }
|
|
1312
1312
|
.cv-docx__redrawn { color: var(--cv-warn, #b45309); }
|
|
1313
|
+
.cv-docx__missing { color: var(--cv-warn, #b45309); }
|
|
1313
1314
|
|
|
1314
1315
|
/* Tablet / narrow desktop: tighten paddings, let toolbars wrap. */
|
|
1315
1316
|
@media (max-width: 900px) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braincrew-lab/langchain-canvas",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
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)",
|