@arbidocs/blocks 0.4.97 → 0.4.98

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.
Files changed (31) hide show
  1. package/dist/{chunk-RSUOOMPT.js → chunk-6B2AWE3I.js} +3 -3
  2. package/dist/{chunk-RSUOOMPT.js.map → chunk-6B2AWE3I.js.map} +1 -1
  3. package/dist/{chunk-QIEEFBZ5.js → chunk-FIEAWH4K.js} +354 -69
  4. package/dist/chunk-FIEAWH4K.js.map +1 -0
  5. package/dist/{chunk-YH6SSJDW.js → chunk-LZ5AFAWC.js} +3 -3
  6. package/dist/{chunk-YH6SSJDW.js.map → chunk-LZ5AFAWC.js.map} +1 -1
  7. package/dist/{chunk-HGJGALLN.cjs → chunk-MIMVRMKC.cjs} +359 -67
  8. package/dist/chunk-MIMVRMKC.cjs.map +1 -0
  9. package/dist/{chunk-GVOTXXZG.cjs → chunk-MUYZP5FB.cjs} +5 -5
  10. package/dist/{chunk-GVOTXXZG.cjs.map → chunk-MUYZP5FB.cjs.map} +1 -1
  11. package/dist/{chunk-EHB7J6SR.cjs → chunk-VJO66VVF.cjs} +5 -5
  12. package/dist/{chunk-EHB7J6SR.cjs.map → chunk-VJO66VVF.cjs.map} +1 -1
  13. package/dist/connect/docx.cjs +6 -6
  14. package/dist/connect/docx.js +4 -4
  15. package/dist/connect/index.cjs +3 -3
  16. package/dist/connect/index.js +2 -2
  17. package/dist/docx/NativeDocxEditor.d.ts +7 -0
  18. package/dist/docx/RibbonMenu.d.ts +3 -1
  19. package/dist/docx/index.cjs +52 -24
  20. package/dist/docx/index.d.ts +2 -0
  21. package/dist/docx/index.js +3 -3
  22. package/dist/docx/template/HouseTemplate.d.ts +64 -0
  23. package/dist/docx/template/HouseTemplatePanel.d.ts +6 -0
  24. package/dist/docx/template/QuickParts.d.ts +7 -0
  25. package/dist/docx/wordStyles.d.ts +3 -0
  26. package/dist/index.cjs +41 -41
  27. package/dist/index.js +2 -2
  28. package/package.json +4 -4
  29. package/src/docx/styles.css +102 -0
  30. package/dist/chunk-HGJGALLN.cjs.map +0 -1
  31. package/dist/chunk-QIEEFBZ5.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { NativeDocxEditor } from './chunk-QIEEFBZ5.js';
2
+ import { NativeDocxEditor } from './chunk-FIEAWH4K.js';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
5
5
  function StreamedWordArtifact({
@@ -37,5 +37,5 @@ function StreamedWordArtifact({
37
37
  }
38
38
 
39
39
  export { StreamedWordArtifact };
40
- //# sourceMappingURL=chunk-RSUOOMPT.js.map
41
- //# sourceMappingURL=chunk-RSUOOMPT.js.map
40
+ //# sourceMappingURL=chunk-6B2AWE3I.js.map
41
+ //# sourceMappingURL=chunk-6B2AWE3I.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/docx/StreamedWordArtifact.tsx"],"names":[],"mappings":";;;AAmBO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAA8B;AAC5B,EAAA,IAAI,CAAC,QAAQ,UAAA,EAAY;AACvB,IAAA,uBACE,GAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QAEC,OAAA;AAAA,QACA,UAAA;AAAA,QACA,KAAA;AAAA,QACA;AAAA,OAAA;AAAA,MAJK;AAAA,KAKP;AAAA,EAEJ;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MAEC,OAAA;AAAA,MACA,UAAA,EAAW,gBAAA;AAAA,MACX,KAAA;AAAA,MACA,YAAA,EAAa,EAAA;AAAA,MACb,MAAA,EAAQ,EAAE,OAAA,EAAS,IAAA,EAAM,KAAA;AAAM,KAAA;AAAA,IAL3B;AAAA,GAMN;AAEJ","file":"chunk-RSUOOMPT.js","sourcesContent":["import { NativeDocxEditor, type NativeDocxAdapter } from './NativeDocxEditor'\n\nexport interface StreamedWordArtifactProps {\n adapter: NativeDocxAdapter\n /** Streamed Markdown, shown as a read-only preview while the draft is generated. */\n content: string\n title: string\n busy: boolean\n error?: string | null\n /** The DOCX draft the server stored for this generation (`doc-…`), once known. */\n documentId?: string\n onSaved?: (documentId: string) => void\n}\n\n/**\n * One drafting surface for an AI document draft. The server stores every draft as DOCX, so\n * this component never creates a document: it previews the streamed Markdown until generation\n * ends and the stored DOCX is known, then opens that DOCX in the live collaborative editor.\n */\nexport function StreamedWordArtifact({\n adapter,\n content,\n title,\n busy,\n error,\n documentId,\n onSaved,\n}: StreamedWordArtifactProps) {\n if (!busy && documentId) {\n return (\n <NativeDocxEditor\n key={documentId}\n adapter={adapter}\n documentId={documentId}\n title={title}\n onSaved={onSaved}\n />\n )\n }\n return (\n <NativeDocxEditor\n key=\"stream-preview\"\n adapter={adapter}\n documentId=\"stream-preview\"\n title={title}\n draftContent=\"\"\n stream={{ content, busy, error }}\n />\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/docx/StreamedWordArtifact.tsx"],"names":[],"mappings":";;;AAmBO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAA8B;AAC5B,EAAA,IAAI,CAAC,QAAQ,UAAA,EAAY;AACvB,IAAA,uBACE,GAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QAEC,OAAA;AAAA,QACA,UAAA;AAAA,QACA,KAAA;AAAA,QACA;AAAA,OAAA;AAAA,MAJK;AAAA,KAKP;AAAA,EAEJ;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MAEC,OAAA;AAAA,MACA,UAAA,EAAW,gBAAA;AAAA,MACX,KAAA;AAAA,MACA,YAAA,EAAa,EAAA;AAAA,MACb,MAAA,EAAQ,EAAE,OAAA,EAAS,IAAA,EAAM,KAAA;AAAM,KAAA;AAAA,IAL3B;AAAA,GAMN;AAEJ","file":"chunk-6B2AWE3I.js","sourcesContent":["import { NativeDocxEditor, type NativeDocxAdapter } from './NativeDocxEditor'\n\nexport interface StreamedWordArtifactProps {\n adapter: NativeDocxAdapter\n /** Streamed Markdown, shown as a read-only preview while the draft is generated. */\n content: string\n title: string\n busy: boolean\n error?: string | null\n /** The DOCX draft the server stored for this generation (`doc-…`), once known. */\n documentId?: string\n onSaved?: (documentId: string) => void\n}\n\n/**\n * One drafting surface for an AI document draft. The server stores every draft as DOCX, so\n * this component never creates a document: it previews the streamed Markdown until generation\n * ends and the stored DOCX is known, then opens that DOCX in the live collaborative editor.\n */\nexport function StreamedWordArtifact({\n adapter,\n content,\n title,\n busy,\n error,\n documentId,\n onSaved,\n}: StreamedWordArtifactProps) {\n if (!busy && documentId) {\n return (\n <NativeDocxEditor\n key={documentId}\n adapter={adapter}\n documentId={documentId}\n title={title}\n onSaved={onSaved}\n />\n )\n }\n return (\n <NativeDocxEditor\n key=\"stream-preview\"\n adapter={adapter}\n documentId=\"stream-preview\"\n title={title}\n draftContent=\"\"\n stream={{ content, busy, error }}\n />\n )\n}\n"]}
@@ -6,9 +6,10 @@ import { collectReviewItems, revisionItemsOf } from '@docx-editor.dev/core/store
6
6
  import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from 'react';
7
7
  import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, useEditorCommand } from '@docx-editor.dev/react';
8
8
  import { AiMarkdown, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Button, DialogFooter, cn, splitMarkdownBlocks } from '@arbidocs/react/ui';
9
- import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Type, Ellipsis, RefreshCw, WifiOff, Download, Save, BookA, ListRestart, Superscript, NotebookText } from 'lucide-react';
9
+ import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Type, Ellipsis, RefreshCw, WifiOff, Download, Save, BookA, ListRestart, Blocks, Superscript, NotebookText } from 'lucide-react';
10
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
11
  import { createPortal } from 'react-dom';
12
+ import { defaultLegalTemplate, listBuildingBlocks, attachedTemplateOf, linkedLevelStyles, styleDivergence, buildingBlockFragment, fragmentClipboardHtml, newFromTemplate } from '@arbidocs/coedit/template';
12
13
  import { renderToStaticMarkup } from 'react-dom/server';
13
14
  import * as Y from 'yjs';
14
15
  import { DOCUMENT_COLLABORATION_VERSIONS, collaborationModule } from '@arbidocs/coedit';
@@ -2122,6 +2123,130 @@ function DefinedTermHover({
2122
2123
  document.body
2123
2124
  );
2124
2125
  }
2126
+ var BUILTIN_TEMPLATE_NAME = "ARBI legal template";
2127
+ var templateFileName = (t) => /\.(dotx|dotm|docx|docm)$/i.test(t.name) ? t.name : `${t.name}.dotx`;
2128
+ var builtin;
2129
+ function builtinHouseTemplate() {
2130
+ builtin ??= { bytes: defaultLegalTemplate(), name: BUILTIN_TEMPLATE_NAME, source: "builtin" };
2131
+ return builtin;
2132
+ }
2133
+ async function resolveHouseTemplate(host) {
2134
+ if (!host) return builtinHouseTemplate();
2135
+ try {
2136
+ return await host.load();
2137
+ } catch {
2138
+ return builtinHouseTemplate();
2139
+ }
2140
+ }
2141
+ var HouseTemplateContext = createContext(null);
2142
+ var useHouseTemplate = () => useContext(HouseTemplateContext);
2143
+ function HouseTemplateProvider({
2144
+ host,
2145
+ documentId,
2146
+ editable,
2147
+ notify,
2148
+ enabled = true,
2149
+ children
2150
+ }) {
2151
+ const editor = useDocxEditor();
2152
+ const [template, setTemplate] = useState(builtinHouseTemplate);
2153
+ const [divergence, setDivergence] = useState(null);
2154
+ const [basedOnTemplate, setBasedOnTemplate] = useState(false);
2155
+ const [levels, setLevels] = useState({});
2156
+ const [generation, setGeneration] = useState(0);
2157
+ useEffect(() => {
2158
+ let live = true;
2159
+ void resolveHouseTemplate(host).then((t) => live && setTemplate(t));
2160
+ return () => {
2161
+ live = false;
2162
+ };
2163
+ }, [host]);
2164
+ const blocks = useMemo(() => {
2165
+ try {
2166
+ return listBuildingBlocks(template.bytes);
2167
+ } catch {
2168
+ return [];
2169
+ }
2170
+ }, [template]);
2171
+ useEffect(() => {
2172
+ if (!editor || !enabled) return;
2173
+ let live = true;
2174
+ void editor.save().then((saved) => {
2175
+ if (!live || !saved) return;
2176
+ const bytes = new Uint8Array(saved);
2177
+ setBasedOnTemplate(attachedTemplateOf(bytes) === templateFileName(template));
2178
+ setLevels(linkedLevelStyles(bytes));
2179
+ setDivergence(styleDivergence(bytes, template.bytes));
2180
+ }).catch(() => live && setDivergence(null));
2181
+ return () => {
2182
+ live = false;
2183
+ };
2184
+ }, [editor, template, generation, enabled]);
2185
+ const refresh = useCallback(() => setGeneration((g) => g + 1), []);
2186
+ const insertBlock = useCallback(
2187
+ (name) => {
2188
+ if (!editor || !editable) return false;
2189
+ const fragment = buildingBlockFragment(template.bytes, name);
2190
+ if (!fragment) return false;
2191
+ try {
2192
+ const result = withDirectEditing(
2193
+ editor,
2194
+ () => editor.exec({ type: "paste", text: "", html: fragmentClipboardHtml(fragment) })
2195
+ );
2196
+ if (!result.ok) throw new Error(result.reason ?? "The block could not be inserted here.");
2197
+ refresh();
2198
+ return true;
2199
+ } catch (failure) {
2200
+ notify?.(
2201
+ failure instanceof Error ? failure.message : "Could not insert the block.",
2202
+ "error"
2203
+ );
2204
+ return false;
2205
+ }
2206
+ },
2207
+ [editor, editable, template, notify, refresh]
2208
+ );
2209
+ const setAsHouseTemplate = useMemo(
2210
+ () => host?.setFromDocument && documentId ? async () => {
2211
+ try {
2212
+ await host.setFromDocument(documentId);
2213
+ setTemplate(await resolveHouseTemplate(host));
2214
+ notify?.("This document is now the house template for new documents.", "success");
2215
+ } catch (failure) {
2216
+ notify?.(
2217
+ failure instanceof Error ? failure.message : "Could not set the house template.",
2218
+ "error"
2219
+ );
2220
+ }
2221
+ } : void 0,
2222
+ [host, documentId, notify]
2223
+ );
2224
+ const value = useMemo(
2225
+ () => ({
2226
+ template,
2227
+ blocks,
2228
+ divergence,
2229
+ basedOnTemplate,
2230
+ levels,
2231
+ insertBlock,
2232
+ refresh,
2233
+ setAsHouseTemplate,
2234
+ editable
2235
+ }),
2236
+ [
2237
+ template,
2238
+ blocks,
2239
+ divergence,
2240
+ basedOnTemplate,
2241
+ levels,
2242
+ insertBlock,
2243
+ refresh,
2244
+ setAsHouseTemplate,
2245
+ editable
2246
+ ]
2247
+ );
2248
+ return /* @__PURE__ */ jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
2249
+ }
2125
2250
  function wordDraftHtml(markdown) {
2126
2251
  const template = document.createElement("template");
2127
2252
  template.innerHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(AiMarkdown, { children: markdown }));
@@ -2214,7 +2339,8 @@ function RibbonMenu({
2214
2339
  icon,
2215
2340
  children,
2216
2341
  className,
2217
- testId
2342
+ testId,
2343
+ title
2218
2344
  }) {
2219
2345
  const [open, setOpen] = useState(false);
2220
2346
  const element = useRef(null);
@@ -2240,7 +2366,7 @@ function RibbonMenu({
2240
2366
  type: "button",
2241
2367
  className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
2242
2368
  "aria-label": label,
2243
- title: label,
2369
+ title: title ?? label,
2244
2370
  "aria-expanded": open,
2245
2371
  "data-testid": testId,
2246
2372
  onMouseDown: (event) => event.preventDefault(),
@@ -2297,6 +2423,17 @@ function wordShortcut(event) {
2297
2423
  if (event.shiftKey && !event.altKey && event.code === "KeyN") return "Normal";
2298
2424
  return null;
2299
2425
  }
2426
+ function divergenceSummary(divergence) {
2427
+ if (!divergence) return null;
2428
+ const changed = divergence.styles.filter((s) => s.kind === "changed").map((s) => styleLabel(s.name));
2429
+ const missing = divergence.styles.filter((s) => s.kind === "missing").map((s) => styleLabel(s.name));
2430
+ const parts = [
2431
+ divergence.docDefaults ? "default text formatting" : "",
2432
+ changed.length ? `${changed.length === 1 ? "style" : "styles"} ${changed.slice(0, 6).join(", ")}${changed.length > 6 ? "\u2026" : ""}` : "",
2433
+ missing.length ? `${missing.length} house ${missing.length === 1 ? "style" : "styles"} missing` : ""
2434
+ ].filter(Boolean);
2435
+ return parts.length ? `Differs from the house template: ${parts.join("; ")}.` : null;
2436
+ }
2300
2437
  var FILTER_FROM = 8;
2301
2438
  function previewStyle(option) {
2302
2439
  const { fontFamily, fontSizePt, bold, italic, color } = option.preview;
@@ -2312,6 +2449,10 @@ function previewStyle(option) {
2312
2449
  function Gallery({ close }) {
2313
2450
  const { value, setValue, options, isEnabled } = useParagraphStyle();
2314
2451
  const editor = useDocxEditor();
2452
+ const house = useHouseTemplate();
2453
+ const changed = new Set(
2454
+ house?.basedOnTemplate ? house.divergence?.styles.filter((s) => s.kind === "changed").map((s) => s.styleId) : []
2455
+ );
2315
2456
  const [query, setQuery] = useState("");
2316
2457
  const quick = useMemo(
2317
2458
  () => QUICK_STYLES.map((name) => findStyle(options, name)).filter(
@@ -2374,7 +2515,7 @@ function Gallery({ close }) {
2374
2515
  id: nav.listboxId,
2375
2516
  "aria-label": "Document styles",
2376
2517
  children: [
2377
- shown.map((option, index) => /* @__PURE__ */ jsx(
2518
+ shown.map((option, index) => /* @__PURE__ */ jsxs(
2378
2519
  "li",
2379
2520
  {
2380
2521
  id: listOptionId(nav.listboxId, index),
@@ -2385,7 +2526,17 @@ function Gallery({ close }) {
2385
2526
  "data-style-id": option.styleId,
2386
2527
  onMouseEnter: () => nav.setSelectedIndex(index),
2387
2528
  onClick: () => isEnabled && apply(option),
2388
- children: /* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) })
2529
+ children: [
2530
+ /* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) }),
2531
+ changed.has(option.styleId) && /* @__PURE__ */ jsx(
2532
+ "span",
2533
+ {
2534
+ className: "native-docx-style-diverged",
2535
+ title: "Defined differently from the house template",
2536
+ "aria-label": "Defined differently from the house template"
2537
+ }
2538
+ )
2539
+ ]
2389
2540
  },
2390
2541
  option.styleId
2391
2542
  )),
@@ -2399,14 +2550,25 @@ function StyleGallery() {
2399
2550
  const { value, options } = useParagraphStyle();
2400
2551
  const current = options.find((o) => o.styleId === value);
2401
2552
  const name = current ? styleLabel(current.name) : "Styles";
2553
+ const house = useHouseTemplate();
2554
+ const diverged = house?.basedOnTemplate ? divergenceSummary(house.divergence) : null;
2402
2555
  return /* @__PURE__ */ jsx(
2403
2556
  RibbonMenu,
2404
2557
  {
2405
2558
  label: "Styles",
2559
+ title: diverged ? `Styles. ${diverged}` : void 0,
2406
2560
  className: "native-docx-style-trigger",
2407
2561
  testId: "docx-style-trigger",
2408
2562
  icon: /* @__PURE__ */ jsxs(Fragment, { children: [
2409
2563
  /* @__PURE__ */ jsx("span", { className: "native-docx-style-current", "data-testid": "docx-current-style", children: name }),
2564
+ diverged && /* @__PURE__ */ jsx(
2565
+ "span",
2566
+ {
2567
+ className: "native-docx-style-diverged",
2568
+ "data-testid": "docx-style-diverged",
2569
+ "aria-label": diverged
2570
+ }
2571
+ ),
2410
2572
  /* @__PURE__ */ jsx(ChevronDown, { size: 12, "aria-hidden": true })
2411
2573
  ] }),
2412
2574
  children: (close) => /* @__PURE__ */ jsx(Gallery, { close })
@@ -2475,12 +2637,18 @@ function WordShortcuts({ scope }) {
2475
2637
  const endnote = useInsertNote("endnote");
2476
2638
  const styles = useParagraphStyle();
2477
2639
  const editor = useDocxEditor();
2478
- const latest = useRef({ footnote, endnote, styles, editor });
2479
- latest.current = { footnote, endnote, styles, editor };
2640
+ const levels = useHouseTemplate()?.levels;
2641
+ const latest = useRef({ footnote, endnote, styles, editor, levels });
2642
+ latest.current = { footnote, endnote, styles, editor, levels };
2480
2643
  useEffect(() => {
2481
2644
  const element = scope.current;
2482
2645
  if (!element) return;
2483
2646
  const onKeyDown = (event) => {
2647
+ if (outlineTab(event)) {
2648
+ event.preventDefault();
2649
+ event.stopPropagation();
2650
+ return;
2651
+ }
2484
2652
  const action = wordShortcut(event);
2485
2653
  if (!action) return;
2486
2654
  const { footnote: footnote2, endnote: endnote2, styles: styles2, editor: editor2 } = latest.current;
@@ -2495,11 +2663,110 @@ function WordShortcuts({ scope }) {
2495
2663
  event.stopPropagation();
2496
2664
  editor2?.surface?.focus();
2497
2665
  };
2666
+ const outlineTab = (event) => {
2667
+ if (event.key !== "Tab" || event.ctrlKey || event.metaKey || event.altKey) return false;
2668
+ const { editor: editor2, levels: levels2, styles: styles2 } = latest.current;
2669
+ const current = editor2?.getSelectionFormatting()?.styleId;
2670
+ const link = current ? levels2?.[current] : void 0;
2671
+ const target = event.shiftKey ? link?.previous : link?.next;
2672
+ if (!target || !editor2 || editorPort(editor2).caret()?.offset !== 0) return false;
2673
+ styles2.setValue(target);
2674
+ return true;
2675
+ };
2498
2676
  element.addEventListener("keydown", onKeyDown, true);
2499
2677
  return () => element.removeEventListener("keydown", onKeyDown, true);
2500
2678
  }, [scope]);
2501
2679
  return null;
2502
2680
  }
2681
+ function QuickParts() {
2682
+ const house = useHouseTemplate();
2683
+ if (!house || house.blocks.length === 0) return null;
2684
+ const categories = [...new Set(house.blocks.map((b) => b.category))];
2685
+ return /* @__PURE__ */ jsx(RibbonMenu, { label: "Quick Parts", icon: /* @__PURE__ */ jsx(Blocks, { size: 16 }), testId: "docx-quick-parts", children: (close) => /* @__PURE__ */ jsxs("div", { className: "native-docx-quick-parts", "data-testid": "docx-quick-parts-panel", children: [
2686
+ categories.map((category) => /* @__PURE__ */ jsxs("section", { "aria-label": category, children: [
2687
+ /* @__PURE__ */ jsx("h3", { children: category }),
2688
+ /* @__PURE__ */ jsx("ul", { children: house.blocks.filter((b) => b.category === category).map((block) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
2689
+ "button",
2690
+ {
2691
+ type: "button",
2692
+ disabled: !house.editable,
2693
+ title: block.description || block.name,
2694
+ onClick: () => {
2695
+ close();
2696
+ house.insertBlock(block.name);
2697
+ },
2698
+ children: [
2699
+ /* @__PURE__ */ jsx("span", { className: "native-docx-quick-part-name", children: block.name }),
2700
+ /* @__PURE__ */ jsx("span", { className: "native-docx-quick-part-preview", children: block.text })
2701
+ ]
2702
+ }
2703
+ ) }, block.name)) })
2704
+ ] }, category)),
2705
+ /* @__PURE__ */ jsxs("p", { className: "native-docx-quick-parts-source", children: [
2706
+ "From ",
2707
+ house.template.name
2708
+ ] })
2709
+ ] }) });
2710
+ }
2711
+ var SOURCE = {
2712
+ workspace: "This workspace's house template",
2713
+ deployment: "The house template for all workspaces",
2714
+ builtin: "ARBI\u2019s built-in template (no house template has been set)"
2715
+ };
2716
+ function download(bytes, name) {
2717
+ const isTemplate = !/\.docx$/i.test(name);
2718
+ const url = URL.createObjectURL(
2719
+ new Blob([bytes], {
2720
+ type: isTemplate ? "application/vnd.openxmlformats-officedocument.wordprocessingml.template" : "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
2721
+ })
2722
+ );
2723
+ const a = document.createElement("a");
2724
+ a.href = url;
2725
+ a.download = /\.(dotx|docx)$/i.test(name) ? name : `${name}.dotx`;
2726
+ a.click();
2727
+ setTimeout(() => URL.revokeObjectURL(url), 5e3);
2728
+ }
2729
+ function HouseTemplatePanel() {
2730
+ const house = useHouseTemplate();
2731
+ if (!house) return null;
2732
+ const summary = divergenceSummary(house.divergence);
2733
+ return /* @__PURE__ */ jsx(
2734
+ RibbonMenu,
2735
+ {
2736
+ label: "House template",
2737
+ className: "native-docx-style-trigger",
2738
+ testId: "docx-house-template",
2739
+ icon: /* @__PURE__ */ jsxs(Fragment, { children: [
2740
+ /* @__PURE__ */ jsx("span", { className: "native-docx-style-current", children: "House template" }),
2741
+ /* @__PURE__ */ jsx(ChevronDown, { size: 12, "aria-hidden": true })
2742
+ ] }),
2743
+ children: /* @__PURE__ */ jsxs("div", { className: "native-docx-house-template", "data-testid": "docx-house-template-panel", children: [
2744
+ /* @__PURE__ */ jsxs("p", { children: [
2745
+ /* @__PURE__ */ jsx("strong", { children: house.template.name }),
2746
+ /* @__PURE__ */ jsx("br", {}),
2747
+ SOURCE[house.template.source],
2748
+ ". New documents start from it."
2749
+ ] }),
2750
+ /* @__PURE__ */ jsx("p", { "data-testid": "docx-house-template-status", children: house.divergence === null ? "Comparing this document\u2019s styles\u2026" : !house.basedOnTemplate ? "This document was not started from the house template." : summary ?? "This document\u2019s styles match the house template." }),
2751
+ /* @__PURE__ */ jsxs("div", { className: "native-docx-house-template-actions", children: [
2752
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => download(house.template.bytes, house.template.name), children: [
2753
+ /* @__PURE__ */ jsx(Download, { size: 14 }),
2754
+ " Download template"
2755
+ ] }),
2756
+ house.setAsHouseTemplate && /* @__PURE__ */ jsx(
2757
+ "button",
2758
+ {
2759
+ type: "button",
2760
+ "data-testid": "docx-set-house-template",
2761
+ onClick: () => void house.setAsHouseTemplate(),
2762
+ children: "Use this document as the house template"
2763
+ }
2764
+ )
2765
+ ] })
2766
+ ] })
2767
+ }
2768
+ );
2769
+ }
2503
2770
  function contentPath(documentId) {
2504
2771
  const match = /^(doc-[A-Za-z0-9]+)@v([1-9][0-9]*)$/.exec(documentId);
2505
2772
  return match ? `/${encodeURIComponent(match[1])}/content?version=${match[2]}` : `/${encodeURIComponent(documentId)}/content`;
@@ -2589,6 +2856,7 @@ function DocxRibbon({
2589
2856
  /* @__PURE__ */ jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
2590
2857
  /* @__PURE__ */ jsx("option", { children: "Home" }),
2591
2858
  /* @__PURE__ */ jsx("option", { children: "Insert" }),
2859
+ /* @__PURE__ */ jsx("option", { children: "Design" }),
2592
2860
  /* @__PURE__ */ jsx("option", { children: "References" }),
2593
2861
  /* @__PURE__ */ jsx("option", { children: "Review" })
2594
2862
  ] }),
@@ -2622,8 +2890,10 @@ function DocxRibbon({
2622
2890
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableInsert, {}),
2623
2891
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageInsert, {}),
2624
2892
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {}),
2625
- /* @__PURE__ */ jsx(DefinedTermButtons, {})
2893
+ /* @__PURE__ */ jsx(DefinedTermButtons, {}),
2894
+ /* @__PURE__ */ jsx(QuickParts, {})
2626
2895
  ] }),
2896
+ tab === "Design" && /* @__PURE__ */ jsx(HouseTemplatePanel, {}),
2627
2897
  tab === "References" && /* @__PURE__ */ jsx(NoteButtons, {}),
2628
2898
  tab === "Review" && /* @__PURE__ */ jsxs(Fragment, { children: [
2629
2899
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.EditingMode, {}),
@@ -2698,11 +2968,16 @@ function NativeDocxEditor({
2698
2968
  async function start() {
2699
2969
  const fonts = await adapter.loadFonts();
2700
2970
  if (isDraft || snapshot) {
2701
- const data = isDraft ? !streamRef.current && drafts.get(documentId) || await (await adapter.request("/prepare", {
2971
+ const data = isDraft ? !streamRef.current && drafts.get(documentId) || (draftContent === "" && adapter.houseTemplate ? await (async () => {
2972
+ const template = await resolveHouseTemplate(adapter.houseTemplate);
2973
+ return newFromTemplate(template.bytes, {
2974
+ attachedTemplate: templateFileName(template)
2975
+ });
2976
+ })() : await (await adapter.request("/prepare", {
2702
2977
  method: "POST",
2703
2978
  headers: { "Content-Type": "application/json" },
2704
2979
  body: JSON.stringify({ content: draftContent })
2705
- })).arrayBuffer() : await (await adapter.request(contentPath(documentId))).arrayBuffer();
2980
+ })).arrayBuffer()) : await (await adapter.request(contentPath(documentId))).arrayBuffer();
2706
2981
  if (!cancelled) {
2707
2982
  setReady({
2708
2983
  document: new Uint8Array(data),
@@ -2964,68 +3239,78 @@ function NativeDocxEditor({
2964
3239
  if (change.source !== "load") changed();
2965
3240
  },
2966
3241
  onFontError: (e) => setError(e.message),
2967
- children: /* @__PURE__ */ jsxs(
3242
+ children: /* @__PURE__ */ jsx(
2968
3243
  DefinedTermsProvider,
2969
3244
  {
2970
3245
  load: adapter.definitions,
2971
3246
  editable: !stream && !snapshot && ready.canEdit,
2972
3247
  notify: adapter.notify,
2973
- children: [
2974
- /* @__PURE__ */ jsx(
2975
- DocxEditor.RevisionMarkup,
2976
- {
2977
- insertions: { mark: "underline", color: "blue" },
2978
- deletions: { mark: "strikethrough", color: "red" },
2979
- movedFrom: { mark: "doubleStrikethrough", color: "green" },
2980
- movedTo: { mark: "doubleUnderline", color: "green" },
2981
- trackMoves: true
2982
- }
2983
- ),
2984
- !stream && /* @__PURE__ */ jsx(
2985
- DocxRibbon,
2986
- {
2987
- onSave: () => void saveVersion(),
2988
- badge: (
2989
- // Says the document is shared live, in place of a title row above the ribbon.
2990
- ready.room && ready.canEdit && !snapshot ? /* @__PURE__ */ jsx("span", { title: "Edits sync live with everyone who has this document open", children: "Co-edit mode" }) : void 0
3248
+ children: /* @__PURE__ */ jsxs(
3249
+ HouseTemplateProvider,
3250
+ {
3251
+ enabled: !stream,
3252
+ host: adapter.houseTemplate,
3253
+ documentId: isDraft || snapshot ? void 0 : documentId,
3254
+ editable: !stream && !snapshot && ready.canEdit,
3255
+ notify: adapter.notify,
3256
+ children: [
3257
+ /* @__PURE__ */ jsx(
3258
+ DocxEditor.RevisionMarkup,
3259
+ {
3260
+ insertions: { mark: "underline", color: "blue" },
3261
+ deletions: { mark: "strikethrough", color: "red" },
3262
+ movedFrom: { mark: "doubleStrikethrough", color: "green" },
3263
+ movedTo: { mark: "doubleUnderline", color: "green" },
3264
+ trackMoves: true
3265
+ }
3266
+ ),
3267
+ !stream && /* @__PURE__ */ jsx(
3268
+ DocxRibbon,
3269
+ {
3270
+ onSave: () => void saveVersion(),
3271
+ badge: (
3272
+ // Says the document is shared live, in place of a title row above the ribbon.
3273
+ ready.room && ready.canEdit && !snapshot ? /* @__PURE__ */ jsx("span", { title: "Edits sync live with everyone who has this document open", children: "Co-edit mode" }) : void 0
3274
+ )
3275
+ }
3276
+ ),
3277
+ !stream && /* @__PURE__ */ jsx(WordShortcuts, { scope: stageRef }),
3278
+ /* @__PURE__ */ jsxs(
3279
+ "div",
3280
+ {
3281
+ className: "native-docx-stage",
3282
+ ref: stageRef,
3283
+ onMouseDownCapture: (event) => {
3284
+ if (event.button === 2 && ref.current?.getSelectionPlacement())
3285
+ event.preventDefault();
3286
+ },
3287
+ children: [
3288
+ /* @__PURE__ */ jsxs(DocxEditor.Viewport, { className: "native-docx-viewport", children: [
3289
+ /* @__PURE__ */ jsx(DocxEditor.Content, {}),
3290
+ /* @__PURE__ */ jsx(
3291
+ DocxMentions,
3292
+ {
3293
+ initialCommentId,
3294
+ mentions: adapter.mentions,
3295
+ notify: adapter.notify,
3296
+ getAgentStatus: adapter.getAgentStatus,
3297
+ requestAgent: adapter.requestAgent ? (request) => adapter.requestAgent(documentId, request) : void 0,
3298
+ children: /* @__PURE__ */ jsx(DocxEditorReview, { children: () => /* @__PURE__ */ jsx(DocxEditorReview.Card, { children: /* @__PURE__ */ jsx(DocxCommentActivity, {}) }) })
3299
+ }
3300
+ ),
3301
+ /* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
3302
+ /* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
3303
+ /* @__PURE__ */ jsx(DocxEditor.Equation, {}),
3304
+ /* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
3305
+ /* @__PURE__ */ jsx(DocxEditor.ContextMenu, {})
3306
+ ] }),
3307
+ /* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
3308
+ ]
3309
+ }
2991
3310
  )
2992
- }
2993
- ),
2994
- !stream && /* @__PURE__ */ jsx(WordShortcuts, { scope: stageRef }),
2995
- /* @__PURE__ */ jsxs(
2996
- "div",
2997
- {
2998
- className: "native-docx-stage",
2999
- ref: stageRef,
3000
- onMouseDownCapture: (event) => {
3001
- if (event.button === 2 && ref.current?.getSelectionPlacement())
3002
- event.preventDefault();
3003
- },
3004
- children: [
3005
- /* @__PURE__ */ jsxs(DocxEditor.Viewport, { className: "native-docx-viewport", children: [
3006
- /* @__PURE__ */ jsx(DocxEditor.Content, {}),
3007
- /* @__PURE__ */ jsx(
3008
- DocxMentions,
3009
- {
3010
- initialCommentId,
3011
- mentions: adapter.mentions,
3012
- notify: adapter.notify,
3013
- getAgentStatus: adapter.getAgentStatus,
3014
- requestAgent: adapter.requestAgent ? (request) => adapter.requestAgent(documentId, request) : void 0,
3015
- children: /* @__PURE__ */ jsx(DocxEditorReview, { children: () => /* @__PURE__ */ jsx(DocxEditorReview.Card, { children: /* @__PURE__ */ jsx(DocxCommentActivity, {}) }) })
3016
- }
3017
- ),
3018
- /* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
3019
- /* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
3020
- /* @__PURE__ */ jsx(DocxEditor.Equation, {}),
3021
- /* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
3022
- /* @__PURE__ */ jsx(DocxEditor.ContextMenu, {})
3023
- ] }),
3024
- /* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
3025
- ]
3026
- }
3027
- )
3028
- ]
3311
+ ]
3312
+ }
3313
+ )
3029
3314
  }
3030
3315
  )
3031
3316
  }
@@ -3122,6 +3407,6 @@ async function loadFonts(baseUrl) {
3122
3407
  };
3123
3408
  }
3124
3409
 
3125
- export { DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, NativeDocxEditor, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, reviewModule, updateDefinitions, useDefinedTerms, useReview, useReviewItem, withDirectEditing };
3126
- //# sourceMappingURL=chunk-QIEEFBZ5.js.map
3127
- //# sourceMappingURL=chunk-QIEEFBZ5.js.map
3410
+ export { BUILTIN_TEMPLATE_NAME, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, HouseTemplateProvider, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, resolveHouseTemplate, reviewModule, templateFileName, updateDefinitions, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, withDirectEditing };
3411
+ //# sourceMappingURL=chunk-FIEAWH4K.js.map
3412
+ //# sourceMappingURL=chunk-FIEAWH4K.js.map