@braincrew-lab/langchain-canvas 0.7.5 → 0.7.6

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.
@@ -141,6 +141,107 @@ function docxStats(root) {
141
141
  substitutedFonts: substitutedFonts(root)
142
142
  };
143
143
  }
144
+
145
+ // src/io/symbolBullets.ts
146
+ var SYMBOL_FONT_BULLETS = {
147
+ symbol: {
148
+ 61623: "\u2022"
149
+ // • bullet — Word's default list bullet
150
+ },
151
+ wingdings: {
152
+ 61548: "\u25CF",
153
+ // ● black circle
154
+ 61549: "\u274D",
155
+ // ❍ shadowed white circle
156
+ 61550: "\u25A0",
157
+ // ■ black square
158
+ 61551: "\u25A1",
159
+ // □ white square
160
+ 61553: "\u2751",
161
+ // ❑ lower-right shadowed white square
162
+ 61557: "\u25C6",
163
+ // ◆ black diamond
164
+ 61607: "\u25AA",
165
+ // ▪ black small square
166
+ 61656: "\u27A2",
167
+ // ➢ three-d top-lighted right arrowhead
168
+ 61692: "\u2714",
169
+ // ✔ heavy check mark
170
+ 61694: "\u2611"
171
+ // ☑ ballot box with check
172
+ }
173
+ };
174
+ function isPrivateUse(codePoint) {
175
+ return codePoint >= 57344 && codePoint <= 63743;
176
+ }
177
+ function primaryFont(fontFamily) {
178
+ const first = fontFamily.split(",")[0] ?? "";
179
+ return first.trim().replace(/^['"]|['"]$/g, "").toLowerCase();
180
+ }
181
+ function standardBullet(codePoint, fontFamily) {
182
+ const table = SYMBOL_FONT_BULLETS[primaryFont(fontFamily)];
183
+ return table?.[codePoint] ?? null;
184
+ }
185
+ function drawnOnPage(root, selectorText) {
186
+ return selectorText.split(",").some((part) => {
187
+ const base = part.trim().replace(/::?(before|after)\s*$/i, "").trim();
188
+ if (!base) return false;
189
+ try {
190
+ return root.querySelector(base) !== null;
191
+ } catch {
192
+ return false;
193
+ }
194
+ });
195
+ }
196
+ function styleRules(sheet) {
197
+ const found = [];
198
+ const stack = [...sheet.cssRules];
199
+ while (stack.length) {
200
+ const rule = stack.pop();
201
+ if (rule.cssRules?.length) {
202
+ stack.push(...rule.cssRules);
203
+ continue;
204
+ }
205
+ if (rule.style && rule.selectorText) found.push(rule);
206
+ }
207
+ return found;
208
+ }
209
+ function restoreSymbolBullets(root) {
210
+ const swaps = [];
211
+ for (const style of Array.from(root.querySelectorAll("style"))) {
212
+ let rules;
213
+ try {
214
+ rules = style.sheet ? styleRules(style.sheet) : [];
215
+ } catch {
216
+ continue;
217
+ }
218
+ for (const rule of rules) {
219
+ const content = rule.style.getPropertyValue("content");
220
+ if (!content) continue;
221
+ const font = rule.style.getPropertyValue("font-family");
222
+ if (!font) continue;
223
+ if (!drawnOnPage(root, rule.selectorText)) continue;
224
+ let next = "";
225
+ let changed = false;
226
+ for (const character of content) {
227
+ const code = character.codePointAt(0) ?? 0;
228
+ const standard = isPrivateUse(code) ? standardBullet(code, font) : null;
229
+ if (standard === null) {
230
+ next += character;
231
+ continue;
232
+ }
233
+ next += standard;
234
+ changed = true;
235
+ swaps.push({ from: character, to: standard, font: primaryFont(font) });
236
+ }
237
+ if (changed) rule.style.setProperty("content", next);
238
+ }
239
+ }
240
+ return swaps;
241
+ }
242
+ function redrawnFonts(swaps) {
243
+ return [...new Set(swaps.map((swap) => swap.font))];
244
+ }
144
245
  var BANNER = "Preview only \u2014 to change it, ask in chat or select some text.";
145
246
  function DocxPreview({
146
247
  artifactId,
@@ -151,6 +252,7 @@ function DocxPreview({
151
252
  const hostRef = useRef(null);
152
253
  const [status, setStatus] = useState("loading");
153
254
  const [stats, setStats] = useState(null);
255
+ const [redrawn, setRedrawn] = useState([]);
154
256
  const [picked, setPicked] = useState(null);
155
257
  const setSelections = useCanvasStore((s) => s.setSelections);
156
258
  useEffect(() => {
@@ -160,6 +262,7 @@ function DocxPreview({
160
262
  if (!host) return;
161
263
  setStatus("loading");
162
264
  setStats(null);
265
+ setRedrawn([]);
163
266
  setPicked(null);
164
267
  (async () => {
165
268
  const { renderAsync } = await loadOptional(
@@ -181,6 +284,7 @@ function DocxPreview({
181
284
  });
182
285
  if (!live) return;
183
286
  stampDocxAddresses(host);
287
+ setRedrawn(redrawnFonts(restoreSymbolBullets(host)));
184
288
  setStats(docxStats(host));
185
289
  setStatus("ready");
186
290
  let fittedFor = host.clientWidth;
@@ -262,7 +366,18 @@ function DocxPreview({
262
366
  stats.substitutedFonts.length > 0 && /* @__PURE__ */ jsxs("span", { className: "cv-docx__fonts", children: [
263
367
  "substituted: ",
264
368
  stats.substitutedFonts.join(", ")
265
- ] })
369
+ ] }),
370
+ redrawn.length > 0 && /* @__PURE__ */ jsxs(
371
+ "span",
372
+ {
373
+ className: "cv-docx__redrawn",
374
+ title: "This document writes its list bullets as characters in a symbol font's own private area, which no other font can draw. They are shown here as the standard characters that mean the same mark; the stored file is unchanged.",
375
+ children: [
376
+ "bullets redrawn: ",
377
+ redrawn.join(", ")
378
+ ]
379
+ }
380
+ )
266
381
  ]
267
382
  }
268
383
  )
@@ -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-QK4K5427.js').then((m) => ({ default: m.DocxPreview }))
13
+ () => import('./DocxPreview-NGT3Z5KW.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-WPNFFBCP.js').then((m) => ({ default: m.FileRenderer })));
2267
+ var FileRenderer = lazy(() => import('./FileRenderer-TZSZVVLY.js').then((m) => ({ default: m.FileRenderer })));
2268
2268
  var builtinRenderers = {
2269
2269
  html: HtmlRenderer,
2270
2270
  document: DocumentRenderer,
package/dist/styles.css CHANGED
@@ -1309,6 +1309,7 @@
1309
1309
  }
1310
1310
  .cv-docx__picked { color: var(--cv-accent); }
1311
1311
  .cv-docx__fonts { color: var(--cv-warn, #b45309); }
1312
+ .cv-docx__redrawn { color: var(--cv-warn, #b45309); }
1312
1313
 
1313
1314
  /* Tablet / narrow desktop: tighten paddings, let toolbars wrap. */
1314
1315
  @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.5",
3
+ "version": "0.7.6",
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)",