@arbidocs/blocks 0.4.99 → 0.4.100

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 (54) hide show
  1. package/dist/agents/index.cjs +207 -207
  2. package/dist/agents/index.js +2 -2
  3. package/dist/{chunk-C5RF47LD.js → chunk-6HZNFOHW.js} +3 -3
  4. package/dist/{chunk-C5RF47LD.js.map → chunk-6HZNFOHW.js.map} +1 -1
  5. package/dist/{chunk-4ZTIMQ7Z.js → chunk-72PQ42YI.js} +933 -64
  6. package/dist/chunk-72PQ42YI.js.map +1 -0
  7. package/dist/{chunk-44TPPPRJ.cjs → chunk-B7P73VHX.cjs} +930 -60
  8. package/dist/chunk-B7P73VHX.cjs.map +1 -0
  9. package/dist/{chunk-3ICWR3BK.js → chunk-E2QWWLK5.js} +3 -3
  10. package/dist/{chunk-3ICWR3BK.js.map → chunk-E2QWWLK5.js.map} +1 -1
  11. package/dist/{chunk-QBEUCOC7.cjs → chunk-IE55TWIG.cjs} +5 -5
  12. package/dist/{chunk-QBEUCOC7.cjs.map → chunk-IE55TWIG.cjs.map} +1 -1
  13. package/dist/{chunk-5NQIBJPP.js → chunk-NJX774HB.js} +3 -3
  14. package/dist/{chunk-5NQIBJPP.js.map → chunk-NJX774HB.js.map} +1 -1
  15. package/dist/{chunk-DDYXFALG.cjs → chunk-NW3LFDZL.cjs} +3 -3
  16. package/dist/{chunk-DDYXFALG.cjs.map → chunk-NW3LFDZL.cjs.map} +1 -1
  17. package/dist/{chunk-MABEOI4S.cjs → chunk-O24WPHLD.cjs} +5 -5
  18. package/dist/{chunk-MABEOI4S.cjs.map → chunk-O24WPHLD.cjs.map} +1 -1
  19. package/dist/connect/docx.cjs +6 -6
  20. package/dist/connect/docx.js +4 -4
  21. package/dist/connect/index.cjs +3 -3
  22. package/dist/connect/index.js +2 -2
  23. package/dist/docx/NativeDocxEditor.d.ts +6 -1
  24. package/dist/docx/index.cjs +39 -35
  25. package/dist/docx/index.d.ts +1 -0
  26. package/dist/docx/index.js +3 -3
  27. package/dist/docx/layout/GenerateImage.d.ts +50 -0
  28. package/dist/docx/layout/LayoutRibbon.d.ts +32 -0
  29. package/dist/docx/layout/LayoutTabs.d.ts +20 -0
  30. package/dist/docx/layout/PictureRibbon.d.ts +23 -0
  31. package/dist/docx/layout/TableKeys.d.ts +25 -0
  32. package/dist/docx/layout/TableRibbon.d.ts +11 -0
  33. package/dist/docx/layout/caption.d.ts +38 -0
  34. package/dist/docx/layout/presets.d.ts +40 -0
  35. package/dist/facts/index.cjs +1 -1
  36. package/dist/facts/index.js +1 -1
  37. package/dist/files/index.cjs +1 -1
  38. package/dist/files/index.js +1 -1
  39. package/dist/home/index.cjs +1 -1
  40. package/dist/home/index.js +1 -1
  41. package/dist/index.cjs +258 -258
  42. package/dist/index.js +4 -4
  43. package/dist/shell/index.cjs +1 -1
  44. package/dist/shell/index.js +1 -1
  45. package/dist/studio/index.cjs +1 -1
  46. package/dist/studio/index.js +1 -1
  47. package/dist/workflow-canvas/index.cjs +9 -9
  48. package/dist/workflow-canvas/index.js +2 -2
  49. package/package.json +5 -4
  50. package/src/docx/README.md +22 -0
  51. package/src/docx/layout/layout.css +120 -0
  52. package/src/docx/styles.css +2 -0
  53. package/dist/chunk-44TPPPRJ.cjs.map +0 -1
  54. package/dist/chunk-4ZTIMQ7Z.js.map +0 -1
@@ -3,13 +3,14 @@ import { stylesXml, definitionBookmark, DEFINITION_BOOKMARK_PREFIX, compareTerms
3
3
  import { filterMentionCandidates, MentionMenu, findMentionQuery, listOptionId, extractMentions, useListKeyNav } from './chunk-AA3PT6KR.js';
4
4
  import { unzipSync, strFromU8, strToU8, zipSync } from 'fflate';
5
5
  import { collectReviewItems, revisionItemsOf } from '@docx-editor.dev/core/store';
6
- import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from 'react';
7
- import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, useEditorCommand } from '@docx-editor.dev/react';
6
+ import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo, useSyncExternalStore } from 'react';
7
+ import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useEditorState, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, normalizeImageBytes, usePageSetup, 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, Blocks, Superscript, NotebookText } from 'lucide-react';
9
+ import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Loader2, Sparkles, Type, Ellipsis, RefreshCw, WifiOff, Download, Save, BookA, ListRestart, Blocks, Superscript, NotebookText, Columns2, Settings2, BetweenHorizontalStart, BetweenHorizontalEnd, BetweenVerticalStart, BetweenVerticalEnd, Merge, Split, Heading, AlignVerticalJustifyStart, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, RotateCw, FlipHorizontal2, FlipVertical2, Scaling, RectangleVertical, RectangleHorizontal, FileText, SeparatorHorizontal, Captions } from 'lucide-react';
10
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
11
  import { createPortal } from 'react-dom';
12
12
  import { defaultLegalTemplate, listBuildingBlocks, attachedTemplateOf, linkedLevelStyles, styleDivergence, buildingBlockFragment, fragmentClipboardHtml, trackInsertion, newFromTemplate } from '@arbidocs/coedit/template';
13
+ import { executeImageCommand } from '@docx-editor.dev/core/editor';
13
14
  import { renderToStaticMarkup } from 'react-dom/server';
14
15
  import * as Y from 'yjs';
15
16
  import { DOCUMENT_COLLABORATION_VERSIONS, collaborationModule } from '@arbidocs/coedit';
@@ -2330,6 +2331,241 @@ function HouseTemplateProvider({
2330
2331
  );
2331
2332
  return /* @__PURE__ */ jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
2332
2333
  }
2334
+ function RibbonMenu({
2335
+ label,
2336
+ icon,
2337
+ children,
2338
+ className,
2339
+ testId,
2340
+ title
2341
+ }) {
2342
+ const [open, setOpen] = useState(false);
2343
+ const element = useRef(null);
2344
+ useEffect(() => {
2345
+ if (!open) return;
2346
+ const dismiss = (event) => {
2347
+ if (!element.current?.contains(event.target)) setOpen(false);
2348
+ };
2349
+ const escape = (event) => {
2350
+ if (event.key === "Escape") setOpen(false);
2351
+ };
2352
+ document.addEventListener("pointerdown", dismiss);
2353
+ document.addEventListener("keydown", escape);
2354
+ return () => {
2355
+ document.removeEventListener("pointerdown", dismiss);
2356
+ document.removeEventListener("keydown", escape);
2357
+ };
2358
+ }, [open]);
2359
+ return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
2360
+ /* @__PURE__ */ jsx(
2361
+ "button",
2362
+ {
2363
+ type: "button",
2364
+ className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
2365
+ "aria-label": label,
2366
+ title: title ?? label,
2367
+ "aria-expanded": open,
2368
+ "data-testid": testId,
2369
+ onMouseDown: (event) => event.preventDefault(),
2370
+ onClick: () => setOpen((value) => !value),
2371
+ children: icon
2372
+ }
2373
+ ),
2374
+ open && /* @__PURE__ */ jsx(
2375
+ "div",
2376
+ {
2377
+ className: "native-docx-ribbon-popover",
2378
+ role: "group",
2379
+ "aria-label": label,
2380
+ onMouseDown: (event) => {
2381
+ if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
2382
+ event.preventDefault();
2383
+ },
2384
+ children: typeof children === "function" ? children(() => setOpen(false)) : children
2385
+ }
2386
+ )
2387
+ ] });
2388
+ }
2389
+ var titleOf = (prompt) => {
2390
+ const line = prompt.trim().split("\n")[0];
2391
+ return line.length > 80 ? `${line.slice(0, 79)}\u2026` : line;
2392
+ };
2393
+ function rememberCaret(editor) {
2394
+ const surface = editor.surface;
2395
+ const head = surface?.state().selection?.head;
2396
+ if (!surface || !head) return null;
2397
+ return { paraId: surface.session.paraIdOf(head.paragraphId), ...head };
2398
+ }
2399
+ var mounted = [];
2400
+ var running = 0;
2401
+ var listeners = /* @__PURE__ */ new Set();
2402
+ var setRunning = (delta) => {
2403
+ running += delta;
2404
+ listeners.forEach((listener) => listener());
2405
+ };
2406
+ var subscribe = (listener) => {
2407
+ listeners.add(listener);
2408
+ return () => void listeners.delete(listener);
2409
+ };
2410
+ function insertionTarget(origin, caret, editors = mounted) {
2411
+ if (editors.includes(origin) && origin.surface) return origin;
2412
+ const paraId = caret?.paraId;
2413
+ if (!paraId) return null;
2414
+ for (let i = editors.length - 1; i >= 0; i--) {
2415
+ const session = editors[i].surface?.session;
2416
+ if (session?.nodeIdOf(paraId)) return editors[i];
2417
+ }
2418
+ return null;
2419
+ }
2420
+ function restoreCaret(editor, caret) {
2421
+ const surface = editor.surface;
2422
+ if (!surface || !caret) return;
2423
+ const paragraphId = caret.paraId && surface.session.nodeIdOf(caret.paraId) || caret.paragraphId;
2424
+ if (!surface.session.paragraphIds().includes(paragraphId)) return;
2425
+ const point = { paragraphId, offset: caret.offset };
2426
+ surface.setSelection({ anchor: point, head: point });
2427
+ }
2428
+ async function insertGeneratedImage(editor, bytes, prompt, caret) {
2429
+ const image = normalizeImageBytes(bytes);
2430
+ if (!image.ok) throw new Error("The generated image is not in a format Word can embed.");
2431
+ let result;
2432
+ for (let attempt = 0; attempt < 3 && !result?.ok; attempt++) {
2433
+ editor.focus();
2434
+ restoreCaret(editor, caret);
2435
+ result = await executeImageCommand(editor, {
2436
+ type: "insertImage",
2437
+ data: image.bytes,
2438
+ mime: image.mime,
2439
+ widthPoints: image.widthPoints,
2440
+ heightPoints: image.heightPoints,
2441
+ description: prompt.trim(),
2442
+ title: titleOf(prompt)
2443
+ });
2444
+ if (!result.ok && result.reason !== "stale drawing selection") break;
2445
+ }
2446
+ if (!result?.ok) throw new Error(result?.reason || "The picture could not be inserted here.");
2447
+ }
2448
+ var AUTO = "";
2449
+ function useGenerateImageTarget() {
2450
+ const editor = useDocxEditor();
2451
+ const loaded2 = useEditorState((snapshot) => !snapshot.isLoading);
2452
+ useEffect(() => {
2453
+ if (!editor || !loaded2) return;
2454
+ mounted.push(editor);
2455
+ return () => void mounted.splice(mounted.indexOf(editor), 1);
2456
+ }, [editor, loaded2]);
2457
+ }
2458
+ function GenerateImageButton({ generation }) {
2459
+ const editor = useDocxEditor();
2460
+ const [prompt, setPrompt] = useState("");
2461
+ const [aspectRatio, setAspectRatio] = useState(AUTO);
2462
+ const [busy, setBusy] = useState(false);
2463
+ const [error, setError] = useState("");
2464
+ const abort = useRef(null);
2465
+ const close = useRef(() => {
2466
+ });
2467
+ const editable = useEditorState((snapshot) => snapshot.editable);
2468
+ const generating = useSyncExternalStore(subscribe, () => running > 0);
2469
+ async function run() {
2470
+ if (!editor || !prompt.trim() || busy) return;
2471
+ const caret = rememberCaret(editor);
2472
+ const controller = new AbortController();
2473
+ abort.current = controller;
2474
+ setBusy(true);
2475
+ setRunning(1);
2476
+ setError("");
2477
+ try {
2478
+ const bytes = await generation.generate(prompt.trim(), {
2479
+ aspectRatio: aspectRatio || void 0,
2480
+ signal: controller.signal
2481
+ });
2482
+ if (controller.signal.aborted) return;
2483
+ const target = insertionTarget(editor, caret);
2484
+ if (!target) throw new Error("The document was closed before the generated image was ready.");
2485
+ await insertGeneratedImage(target, bytes, prompt, caret);
2486
+ setPrompt("");
2487
+ close.current();
2488
+ generation.notify?.("Generated image inserted", "success");
2489
+ } catch (failure) {
2490
+ if (controller.signal.aborted) return;
2491
+ const message = failure instanceof Error ? failure.message : String(failure);
2492
+ setError(message);
2493
+ generation.notify?.(message, "error");
2494
+ } finally {
2495
+ abort.current = null;
2496
+ setRunning(-1);
2497
+ setBusy(false);
2498
+ }
2499
+ }
2500
+ if (!editable && !generating) return null;
2501
+ return /* @__PURE__ */ jsx(
2502
+ RibbonMenu,
2503
+ {
2504
+ label: generating ? "Generating image\u2026" : "Generate image",
2505
+ icon: generating ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "native-docx-spin" }) : /* @__PURE__ */ jsx(Sparkles, { size: 16 }),
2506
+ testId: "docx-generate-image",
2507
+ children: (dismiss) => {
2508
+ close.current = dismiss;
2509
+ return /* @__PURE__ */ jsxs(
2510
+ "form",
2511
+ {
2512
+ className: "native-docx-form native-docx-generate-image",
2513
+ onSubmit: (event) => {
2514
+ event.preventDefault();
2515
+ void run();
2516
+ },
2517
+ children: [
2518
+ /* @__PURE__ */ jsxs("label", { children: [
2519
+ "Describe the image",
2520
+ /* @__PURE__ */ jsx(
2521
+ "textarea",
2522
+ {
2523
+ autoFocus: true,
2524
+ rows: 3,
2525
+ value: prompt,
2526
+ disabled: busy,
2527
+ placeholder: "A watercolour of a lighthouse at dawn",
2528
+ "data-testid": "docx-generate-image-prompt",
2529
+ onChange: (event) => setPrompt(event.target.value),
2530
+ onKeyDown: (event) => {
2531
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) void run();
2532
+ }
2533
+ }
2534
+ )
2535
+ ] }),
2536
+ generation.aspectRatios && generation.aspectRatios.length > 0 && /* @__PURE__ */ jsxs("label", { children: [
2537
+ "Aspect ratio",
2538
+ /* @__PURE__ */ jsxs(
2539
+ "select",
2540
+ {
2541
+ value: aspectRatio,
2542
+ disabled: busy,
2543
+ onChange: (event) => setAspectRatio(event.target.value),
2544
+ children: [
2545
+ /* @__PURE__ */ jsx("option", { value: AUTO, children: "Auto" }),
2546
+ generation.aspectRatios.map((ratio) => /* @__PURE__ */ jsx("option", { value: ratio.value, children: ratio.label }, ratio.value))
2547
+ ]
2548
+ }
2549
+ )
2550
+ ] }),
2551
+ busy && /* @__PURE__ */ jsx("p", { role: "status", "data-testid": "docx-generate-image-status", children: "Generating\u2026 the picture is inserted where your cursor was. You can keep writing." }),
2552
+ error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
2553
+ /* @__PURE__ */ jsx("div", { className: "native-docx-form-actions", children: busy ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => abort.current?.abort(), children: "Cancel" }) : /* @__PURE__ */ jsx(
2554
+ "button",
2555
+ {
2556
+ type: "submit",
2557
+ disabled: !prompt.trim(),
2558
+ "data-testid": "docx-generate-image-submit",
2559
+ children: "Generate"
2560
+ }
2561
+ ) })
2562
+ ]
2563
+ }
2564
+ );
2565
+ }
2566
+ }
2567
+ );
2568
+ }
2333
2569
  function wordDraftHtml(markdown) {
2334
2570
  const template = document.createElement("template");
2335
2571
  template.innerHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(AiMarkdown, { children: markdown }));
@@ -2417,61 +2653,6 @@ var WordDraftStream = class {
2417
2653
  }
2418
2654
  }
2419
2655
  };
2420
- function RibbonMenu({
2421
- label,
2422
- icon,
2423
- children,
2424
- className,
2425
- testId,
2426
- title
2427
- }) {
2428
- const [open, setOpen] = useState(false);
2429
- const element = useRef(null);
2430
- useEffect(() => {
2431
- if (!open) return;
2432
- const dismiss = (event) => {
2433
- if (!element.current?.contains(event.target)) setOpen(false);
2434
- };
2435
- const escape = (event) => {
2436
- if (event.key === "Escape") setOpen(false);
2437
- };
2438
- document.addEventListener("pointerdown", dismiss);
2439
- document.addEventListener("keydown", escape);
2440
- return () => {
2441
- document.removeEventListener("pointerdown", dismiss);
2442
- document.removeEventListener("keydown", escape);
2443
- };
2444
- }, [open]);
2445
- return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
2446
- /* @__PURE__ */ jsx(
2447
- "button",
2448
- {
2449
- type: "button",
2450
- className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
2451
- "aria-label": label,
2452
- title: title ?? label,
2453
- "aria-expanded": open,
2454
- "data-testid": testId,
2455
- onMouseDown: (event) => event.preventDefault(),
2456
- onClick: () => setOpen((value) => !value),
2457
- children: icon
2458
- }
2459
- ),
2460
- open && /* @__PURE__ */ jsx(
2461
- "div",
2462
- {
2463
- className: "native-docx-ribbon-popover",
2464
- role: "group",
2465
- "aria-label": label,
2466
- onMouseDown: (event) => {
2467
- if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
2468
- event.preventDefault();
2469
- },
2470
- children: typeof children === "function" ? children(() => setOpen(false)) : children
2471
- }
2472
- )
2473
- ] });
2474
- }
2475
2656
 
2476
2657
  // src/docx/wordStyles.ts
2477
2658
  var QUICK_STYLES = ["Normal", "Title", "heading 1", "heading 2", "heading 3"];
@@ -2761,6 +2942,679 @@ function WordShortcuts({ scope }) {
2761
2942
  }, [scope]);
2762
2943
  return null;
2763
2944
  }
2945
+
2946
+ // src/docx/layout/presets.ts
2947
+ var TWIPS_PER_INCH = 1440;
2948
+ var MARGIN_PRESETS = [
2949
+ { id: "normal", label: "Normal", top: 1440, right: 1440, bottom: 1440, left: 1440 },
2950
+ { id: "narrow", label: "Narrow", top: 720, right: 720, bottom: 720, left: 720 },
2951
+ { id: "moderate", label: "Moderate", top: 1440, right: 1080, bottom: 1440, left: 1080 },
2952
+ { id: "wide", label: "Wide", top: 1440, right: 2880, bottom: 1440, left: 2880 }
2953
+ ];
2954
+ var PAPER_SIZES = [
2955
+ { id: "letter", label: "Letter", width: 12240, height: 15840 },
2956
+ { id: "legal", label: "Legal", width: 12240, height: 20160 },
2957
+ { id: "a4", label: "A4", width: 11906, height: 16838 },
2958
+ { id: "a5", label: "A5", width: 8391, height: 11906 },
2959
+ { id: "executive", label: "Executive", width: 10440, height: 15120 }
2960
+ ];
2961
+ var TOLERANCE = 20;
2962
+ var near = (a, b) => Math.abs(a - b) <= TOLERANCE;
2963
+ function matchMargins(setup) {
2964
+ if (!setup) return null;
2965
+ const m = setup.marginsTwips;
2966
+ return MARGIN_PRESETS.find(
2967
+ (p) => near(p.top, m.top) && near(p.right, m.right) && near(p.bottom, m.bottom) && near(p.left, m.left)
2968
+ )?.id ?? null;
2969
+ }
2970
+ function matchPaper(setup) {
2971
+ if (!setup) return null;
2972
+ const short = Math.min(setup.pageWidthTwips, setup.pageHeightTwips);
2973
+ const long = Math.max(setup.pageWidthTwips, setup.pageHeightTwips);
2974
+ return PAPER_SIZES.find((p) => near(p.width, short) && near(p.height, long))?.id ?? null;
2975
+ }
2976
+ function orientedSize(paper, orientation) {
2977
+ return orientation === "landscape" ? { pageWidthTwips: paper.height, pageHeightTwips: paper.width } : { pageWidthTwips: paper.width, pageHeightTwips: paper.height };
2978
+ }
2979
+ function inches(twips) {
2980
+ return `${Number((twips / TWIPS_PER_INCH).toFixed(2))}"`;
2981
+ }
2982
+ function paperDescription(paper) {
2983
+ return `${inches(paper.width)} \xD7 ${inches(paper.height)}`;
2984
+ }
2985
+ var COLUMNS_UNSUPPORTED = "Text columns are not available in this editor yet. Documents that already use columns display and save them unchanged.";
2986
+ function RibbonGallery({
2987
+ label,
2988
+ choices,
2989
+ current,
2990
+ onPick,
2991
+ close,
2992
+ testId
2993
+ }) {
2994
+ return /* @__PURE__ */ jsx("div", { className: "native-docx-gallery", role: "menu", "aria-label": label, "data-testid": testId, children: choices.map((choice) => /* @__PURE__ */ jsxs(
2995
+ "button",
2996
+ {
2997
+ type: "button",
2998
+ role: "menuitemradio",
2999
+ "aria-checked": current === choice.id,
3000
+ disabled: choice.disabled,
3001
+ title: choice.title,
3002
+ "data-choice": choice.id,
3003
+ onClick: () => {
3004
+ onPick(choice.id);
3005
+ close();
3006
+ },
3007
+ children: [
3008
+ choice.icon,
3009
+ /* @__PURE__ */ jsxs("span", { className: "native-docx-gallery-text", children: [
3010
+ /* @__PURE__ */ jsx("span", { className: "native-docx-gallery-label", children: choice.label }),
3011
+ choice.detail && /* @__PURE__ */ jsx("span", { className: "native-docx-gallery-detail", children: choice.detail })
3012
+ ] })
3013
+ ]
3014
+ },
3015
+ choice.id
3016
+ )) });
3017
+ }
3018
+ var marginDetail = (p) => `Top ${inches(p.top)} \xB7 Bottom ${inches(p.bottom)} \xB7 Left ${inches(p.left)} \xB7 Right ${inches(p.right)}`;
3019
+ function Margins({ setup, apply, openDialog }) {
3020
+ return /* @__PURE__ */ jsx(RibbonMenu, { label: "Margins", icon: /* @__PURE__ */ jsx(Scaling, { size: 16 }), testId: "docx-layout-margins", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
3021
+ /* @__PURE__ */ jsx(
3022
+ RibbonGallery,
3023
+ {
3024
+ label: "Margins",
3025
+ current: matchMargins(setup),
3026
+ close,
3027
+ choices: MARGIN_PRESETS.map((p) => ({
3028
+ id: p.id,
3029
+ label: p.label,
3030
+ detail: marginDetail(p)
3031
+ })),
3032
+ onPick: (id) => {
3033
+ const p = MARGIN_PRESETS.find((m) => m.id === id);
3034
+ apply({
3035
+ marginTopTwips: p.top,
3036
+ marginRightTwips: p.right,
3037
+ marginBottomTwips: p.bottom,
3038
+ marginLeftTwips: p.left,
3039
+ scope: "section"
3040
+ });
3041
+ }
3042
+ }
3043
+ ),
3044
+ /* @__PURE__ */ jsx(
3045
+ "button",
3046
+ {
3047
+ type: "button",
3048
+ className: "native-docx-gallery-more",
3049
+ onClick: () => {
3050
+ close();
3051
+ openDialog();
3052
+ },
3053
+ children: "Custom margins\u2026"
3054
+ }
3055
+ )
3056
+ ] }) });
3057
+ }
3058
+ function Orientation({ setup, apply }) {
3059
+ return /* @__PURE__ */ jsx(
3060
+ RibbonMenu,
3061
+ {
3062
+ label: "Orientation",
3063
+ icon: setup?.orientation === "landscape" ? /* @__PURE__ */ jsx(RectangleHorizontal, { size: 16 }) : /* @__PURE__ */ jsx(RectangleVertical, { size: 16 }),
3064
+ testId: "docx-layout-orientation",
3065
+ children: (close) => /* @__PURE__ */ jsx(
3066
+ RibbonGallery,
3067
+ {
3068
+ label: "Orientation",
3069
+ current: setup?.orientation,
3070
+ close,
3071
+ choices: [
3072
+ { id: "portrait", label: "Portrait", icon: /* @__PURE__ */ jsx(RectangleVertical, { size: 16 }) },
3073
+ { id: "landscape", label: "Landscape", icon: /* @__PURE__ */ jsx(RectangleHorizontal, { size: 16 }) }
3074
+ ],
3075
+ onPick: (id) => apply({ orientation: id, scope: "section" })
3076
+ }
3077
+ )
3078
+ }
3079
+ );
3080
+ }
3081
+ function Size({ setup, apply, openDialog }) {
3082
+ return /* @__PURE__ */ jsx(RibbonMenu, { label: "Size", icon: /* @__PURE__ */ jsx(FileText, { size: 16 }), testId: "docx-layout-size", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
3083
+ /* @__PURE__ */ jsx(
3084
+ RibbonGallery,
3085
+ {
3086
+ label: "Size",
3087
+ current: matchPaper(setup),
3088
+ close,
3089
+ choices: PAPER_SIZES.map((p) => ({
3090
+ id: p.id,
3091
+ label: p.label,
3092
+ detail: paperDescription(p)
3093
+ })),
3094
+ onPick: (id) => apply({
3095
+ ...orientedSize(
3096
+ PAPER_SIZES.find((p) => p.id === id),
3097
+ setup?.orientation
3098
+ ),
3099
+ scope: "section"
3100
+ })
3101
+ }
3102
+ ),
3103
+ /* @__PURE__ */ jsx(
3104
+ "button",
3105
+ {
3106
+ type: "button",
3107
+ className: "native-docx-gallery-more",
3108
+ onClick: () => {
3109
+ close();
3110
+ openDialog();
3111
+ },
3112
+ children: "More paper sizes\u2026"
3113
+ }
3114
+ )
3115
+ ] }) });
3116
+ }
3117
+ var COLUMN_BREAK = { type: "insertBreak", kind: "column" };
3118
+ function Breaks() {
3119
+ const page = useEditorCommand("insert.pageBreak");
3120
+ const nextPage = useEditorCommand("insert.sectionBreakNextPage");
3121
+ const continuous = useEditorCommand("insert.sectionBreakContinuous");
3122
+ const column = useEditorCommand(COLUMN_BREAK);
3123
+ const rows = [
3124
+ { id: "page", label: "Page", detail: "Start the next page here", command: page },
3125
+ { id: "column", label: "Column", detail: "Start the next column here", command: column },
3126
+ {
3127
+ id: "nextPage",
3128
+ label: "Next Page",
3129
+ detail: "Section break: new section on the next page",
3130
+ command: nextPage
3131
+ },
3132
+ {
3133
+ id: "continuous",
3134
+ label: "Continuous",
3135
+ detail: "Section break: new section on the same page",
3136
+ command: continuous
3137
+ }
3138
+ ];
3139
+ return /* @__PURE__ */ jsx(RibbonMenu, { label: "Breaks", icon: /* @__PURE__ */ jsx(SeparatorHorizontal, { size: 16 }), testId: "docx-layout-breaks", children: (close) => /* @__PURE__ */ jsx(
3140
+ RibbonGallery,
3141
+ {
3142
+ label: "Breaks",
3143
+ close,
3144
+ choices: rows.map((row) => ({
3145
+ id: row.id,
3146
+ label: row.label,
3147
+ detail: row.detail,
3148
+ disabled: !row.command.isEnabled,
3149
+ title: row.command.disabledReason ?? void 0
3150
+ })),
3151
+ onPick: (id) => void rows.find((row) => row.id === id)?.command.execute()
3152
+ }
3153
+ ) });
3154
+ }
3155
+ function LayoutButtons() {
3156
+ const { pageSetup, apply } = usePageSetup();
3157
+ const [dialog, setDialog] = useState(false);
3158
+ const props = { setup: pageSetup, apply, openDialog: () => setDialog(true) };
3159
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3160
+ /* @__PURE__ */ jsx(Margins, { ...props }),
3161
+ /* @__PURE__ */ jsx(Orientation, { ...props }),
3162
+ /* @__PURE__ */ jsx(Size, { ...props }),
3163
+ /* @__PURE__ */ jsx(
3164
+ DocxEditor.Toolbar.Action,
3165
+ {
3166
+ label: "Columns",
3167
+ icon: /* @__PURE__ */ jsx(Columns2, { size: 16 }),
3168
+ disabled: true,
3169
+ disabledReason: COLUMNS_UNSUPPORTED,
3170
+ className: "native-docx-layout-columns"
3171
+ }
3172
+ ),
3173
+ /* @__PURE__ */ jsx(Breaks, {}),
3174
+ /* @__PURE__ */ jsx(
3175
+ DocxEditor.Toolbar.Action,
3176
+ {
3177
+ label: "Page Setup\u2026",
3178
+ icon: /* @__PURE__ */ jsx(Settings2, { size: 16 }),
3179
+ onSelect: () => setDialog(true),
3180
+ className: "native-docx-layout-page-setup"
3181
+ }
3182
+ ),
3183
+ /* @__PURE__ */ jsx(DocxEditor.PageSetupDialog, { open: dialog, onClose: () => setDialog(false) })
3184
+ ] });
3185
+ }
3186
+ var ROW_ABOVE = { type: "insertRow", where: "above" };
3187
+ var ROW_BELOW = { type: "insertRow", where: "below" };
3188
+ var COLUMN_LEFT = { type: "insertColumn", where: "left" };
3189
+ var COLUMN_RIGHT = { type: "insertColumn", where: "right" };
3190
+ var MERGE = { type: "mergeCells" };
3191
+ var SPLIT = { type: "splitCell", rows: 1, cols: 2 };
3192
+ var HEADER_ROW = { type: "toggleHeaderRow" };
3193
+ var DELETE_ROW = { type: "deleteRow" };
3194
+ var DELETE_COLUMN = { type: "deleteColumn" };
3195
+ var DELETE_TABLE = { type: "deleteTable" };
3196
+ var ALIGN_TOP = { type: "setTableCellVerticalAlignment", alignment: "top" };
3197
+ var ALIGN_CENTER = { type: "setTableCellVerticalAlignment", alignment: "center" };
3198
+ var ALIGN_BOTTOM = { type: "setTableCellVerticalAlignment", alignment: "bottom" };
3199
+ function useRows(rows) {
3200
+ return rows.map((row) => ({ ...row, state: useEditorCommand(row.command) }));
3201
+ }
3202
+ function CommandGallery({
3203
+ label,
3204
+ icon,
3205
+ testId,
3206
+ rows
3207
+ }) {
3208
+ const bound = useRows(rows);
3209
+ return /* @__PURE__ */ jsx(RibbonMenu, { label, icon, testId, children: (close) => /* @__PURE__ */ jsx(
3210
+ RibbonGallery,
3211
+ {
3212
+ label,
3213
+ close,
3214
+ choices: bound.map((row) => ({
3215
+ id: row.id,
3216
+ label: row.label,
3217
+ icon: row.icon,
3218
+ disabled: !row.state.isEnabled,
3219
+ title: row.state.disabledReason ?? void 0
3220
+ })),
3221
+ onPick: (id) => void bound.find((row) => row.id === id)?.state.execute()
3222
+ }
3223
+ ) });
3224
+ }
3225
+ function CommandAction({
3226
+ command,
3227
+ label,
3228
+ icon,
3229
+ testId
3230
+ }) {
3231
+ const state = useEditorCommand(command);
3232
+ return /* @__PURE__ */ jsx(
3233
+ DocxEditor.Toolbar.Action,
3234
+ {
3235
+ label,
3236
+ icon,
3237
+ active: state.isActive,
3238
+ disabled: !state.isEnabled,
3239
+ disabledReason: state.disabledReason ?? void 0,
3240
+ onSelect: () => void state.execute(),
3241
+ className: `native-docx-table-${testId}`
3242
+ }
3243
+ );
3244
+ }
3245
+ function TableButtons() {
3246
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3247
+ /* @__PURE__ */ jsx(
3248
+ CommandAction,
3249
+ {
3250
+ command: ROW_ABOVE,
3251
+ label: "Insert row above",
3252
+ icon: /* @__PURE__ */ jsx(BetweenHorizontalStart, { size: 16 }),
3253
+ testId: "row-above"
3254
+ }
3255
+ ),
3256
+ /* @__PURE__ */ jsx(
3257
+ CommandAction,
3258
+ {
3259
+ command: ROW_BELOW,
3260
+ label: "Insert row below",
3261
+ icon: /* @__PURE__ */ jsx(BetweenHorizontalEnd, { size: 16 }),
3262
+ testId: "row-below"
3263
+ }
3264
+ ),
3265
+ /* @__PURE__ */ jsx(
3266
+ CommandAction,
3267
+ {
3268
+ command: COLUMN_LEFT,
3269
+ label: "Insert column left",
3270
+ icon: /* @__PURE__ */ jsx(BetweenVerticalStart, { size: 16 }),
3271
+ testId: "column-left"
3272
+ }
3273
+ ),
3274
+ /* @__PURE__ */ jsx(
3275
+ CommandAction,
3276
+ {
3277
+ command: COLUMN_RIGHT,
3278
+ label: "Insert column right",
3279
+ icon: /* @__PURE__ */ jsx(BetweenVerticalEnd, { size: 16 }),
3280
+ testId: "column-right"
3281
+ }
3282
+ ),
3283
+ /* @__PURE__ */ jsx(
3284
+ CommandGallery,
3285
+ {
3286
+ label: "Delete",
3287
+ icon: /* @__PURE__ */ jsx(Trash2, { size: 16 }),
3288
+ testId: "docx-table-delete",
3289
+ rows: [
3290
+ { id: "row", label: "Delete row", command: DELETE_ROW },
3291
+ { id: "column", label: "Delete column", command: DELETE_COLUMN },
3292
+ { id: "table", label: "Delete table", command: DELETE_TABLE }
3293
+ ]
3294
+ }
3295
+ ),
3296
+ /* @__PURE__ */ jsx(
3297
+ CommandAction,
3298
+ {
3299
+ command: MERGE,
3300
+ label: "Merge cells",
3301
+ icon: /* @__PURE__ */ jsx(Merge, { size: 16 }),
3302
+ testId: "merge"
3303
+ }
3304
+ ),
3305
+ /* @__PURE__ */ jsx(CommandAction, { command: SPLIT, label: "Split cell", icon: /* @__PURE__ */ jsx(Split, { size: 16 }), testId: "split" }),
3306
+ /* @__PURE__ */ jsx(
3307
+ CommandAction,
3308
+ {
3309
+ command: HEADER_ROW,
3310
+ label: "Repeat header row",
3311
+ icon: /* @__PURE__ */ jsx(Heading, { size: 16 }),
3312
+ testId: "header-row"
3313
+ }
3314
+ ),
3315
+ /* @__PURE__ */ jsx(
3316
+ CommandGallery,
3317
+ {
3318
+ label: "Cell alignment",
3319
+ icon: /* @__PURE__ */ jsx(AlignVerticalJustifyCenter, { size: 16 }),
3320
+ testId: "docx-table-align",
3321
+ rows: [
3322
+ {
3323
+ id: "top",
3324
+ label: "Top",
3325
+ icon: /* @__PURE__ */ jsx(AlignVerticalJustifyStart, { size: 16 }),
3326
+ command: ALIGN_TOP
3327
+ },
3328
+ {
3329
+ id: "center",
3330
+ label: "Center",
3331
+ icon: /* @__PURE__ */ jsx(AlignVerticalJustifyCenter, { size: 16 }),
3332
+ command: ALIGN_CENTER
3333
+ },
3334
+ {
3335
+ id: "bottom",
3336
+ label: "Bottom",
3337
+ icon: /* @__PURE__ */ jsx(AlignVerticalJustifyEnd, { size: 16 }),
3338
+ command: ALIGN_BOTTOM
3339
+ }
3340
+ ]
3341
+ }
3342
+ ),
3343
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderTarget, {}),
3344
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderStyle, {}),
3345
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderWidth, {}),
3346
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderColor, {}),
3347
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableCellFill, {})
3348
+ ] });
3349
+ }
3350
+ var CAPTION_LABELS = ["Figure", "Table", "Equation"];
3351
+ var CAPTION_STYLES = stylesXml(
3352
+ "",
3353
+ '<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style><w:style w:type="paragraph" w:styleId="Caption"><w:name w:val="caption"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:uiPriority w:val="35"/><w:unhideWhenUsed/><w:qFormat/><w:pPr><w:spacing w:after="200" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:i/><w:iCs/><w:color w:val="44546A"/><w:sz w:val="18"/><w:szCs w:val="18"/></w:rPr></w:style>'
3354
+ );
3355
+ function seqIdentifier(instruction) {
3356
+ const match = /^\s*SEQ\s+("[^"]+"|\S+)/i.exec(instruction);
3357
+ return match ? match[1].replace(/"/g, "") : null;
3358
+ }
3359
+ function countSeqFields(root, label, throughId) {
3360
+ let count = 0;
3361
+ let done = false;
3362
+ const target = label.toLowerCase();
3363
+ const visit = (node) => {
3364
+ if (done) return;
3365
+ if (node.localName === "fldSimple") {
3366
+ const instr = node.attributes?.find((a) => a.localName === "instr")?.value ?? "";
3367
+ if (seqIdentifier(instr)?.toLowerCase() === target) count++;
3368
+ } else if (node.localName === "instrText") {
3369
+ const text = (node.children ?? []).map((c) => c.value ?? "").join("");
3370
+ if (seqIdentifier(text)?.toLowerCase() === target) count++;
3371
+ }
3372
+ node.children?.forEach(visit);
3373
+ if (throughId && node.id === throughId) done = true;
3374
+ };
3375
+ visit(root);
3376
+ return count;
3377
+ }
3378
+ function captionParagraphXml(label, number, text) {
3379
+ const run = (value) => `<w:r><w:t xml:space="preserve">${esc(value)}</w:t></w:r>`;
3380
+ const rest = text.trim() ? `: ${text.trim()}` : "";
3381
+ return `<w:p><w:pPr><w:pStyle w:val="Caption"/></w:pPr>${run(`${label} `)}<w:fldSimple w:instr=" SEQ ${esc(label)} \\* ARABIC "><w:r><w:t>${number}</w:t></w:r></w:fldSimple>${rest ? run(rest) : ""}</w:p>`;
3382
+ }
3383
+ function base642(bytes) {
3384
+ let binary = "";
3385
+ for (let i = 0; i < bytes.length; i += 32768)
3386
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
3387
+ return btoa(binary);
3388
+ }
3389
+ function captionFragmentHtml(label, number, text, revision) {
3390
+ const untracked = docxPackage({
3391
+ document: documentXml(captionParagraphXml(label, number, text)),
3392
+ styles: CAPTION_STYLES,
3393
+ title: "Caption"
3394
+ });
3395
+ const bytes = revision ? trackInsertion(untracked, revision, true) : untracked;
3396
+ return `<div data-docx-fragment="${base642(bytes)}" data-docx-fragment-end="covered"></div>`;
3397
+ }
3398
+ var TRANSFORMS = Object.fromEntries(
3399
+ ["rotateCW", "rotateCCW", "flipH", "flipV"].map((action) => [
3400
+ action,
3401
+ { type: "transformImage", action }
3402
+ ])
3403
+ );
3404
+ function TransformAction({
3405
+ action,
3406
+ label,
3407
+ icon
3408
+ }) {
3409
+ const state = useEditorCommand(TRANSFORMS[action]);
3410
+ return /* @__PURE__ */ jsx(
3411
+ DocxEditor.Toolbar.Action,
3412
+ {
3413
+ label,
3414
+ icon,
3415
+ disabled: !state.isEnabled,
3416
+ disabledReason: state.disabledReason ?? void 0,
3417
+ onSelect: () => void state.execute(),
3418
+ className: `native-docx-picture-${action}`
3419
+ }
3420
+ );
3421
+ }
3422
+ function insertCaption(editor, label, text, track) {
3423
+ const surface = editor.surface;
3424
+ const selection = surface?.state().selection;
3425
+ if (!surface || !selection) return null;
3426
+ const { paragraphId } = selection.head;
3427
+ const number = countSeqFields(
3428
+ surface.session.part().root,
3429
+ label,
3430
+ paragraphId
3431
+ ) + 1;
3432
+ const ids = surface.session.paragraphIds();
3433
+ const next = ids[ids.indexOf(paragraphId) + 1];
3434
+ const caption = `${label} ${number}${text.trim() ? `: ${text.trim()}` : ""}`;
3435
+ const outcome = track({
3436
+ label: `Inserted caption: ${caption}`,
3437
+ inserted: (inserted) => inserted !== null,
3438
+ insert: (revision) => {
3439
+ if (next) {
3440
+ surface.setSelection({
3441
+ anchor: { paragraphId: next, offset: 0 },
3442
+ head: { paragraphId: next, offset: 0 }
3443
+ });
3444
+ } else {
3445
+ editor.exec({ type: "selectAll" });
3446
+ const end = surface.state().selection?.head;
3447
+ if (!end) return null;
3448
+ surface.setSelection({ anchor: end, head: end });
3449
+ surface.splitParagraph();
3450
+ }
3451
+ const ok2 = editor.exec({
3452
+ type: "paste",
3453
+ text: caption,
3454
+ html: captionFragmentHtml(label, number, text, revision)
3455
+ }).ok;
3456
+ return ok2 ? number : null;
3457
+ }
3458
+ });
3459
+ return outcome?.result ?? null;
3460
+ }
3461
+ function CaptionButton({ defaultLabel = "Figure" }) {
3462
+ const editor = useDocxEditor();
3463
+ const track = useTrackedOrNoted();
3464
+ const [label, setLabel] = useState(defaultLabel);
3465
+ const [text, setText] = useState("");
3466
+ const [error, setError] = useState("");
3467
+ return /* @__PURE__ */ jsx(RibbonMenu, { label: "Insert caption", icon: /* @__PURE__ */ jsx(Captions, { size: 16 }), testId: "docx-insert-caption", children: (close) => /* @__PURE__ */ jsxs(
3468
+ "form",
3469
+ {
3470
+ className: "native-docx-form",
3471
+ onSubmit: (event) => {
3472
+ event.preventDefault();
3473
+ if (!editor) return;
3474
+ try {
3475
+ if (insertCaption(editor, label, text, track) === null)
3476
+ return setError("The caption could not be inserted here.");
3477
+ setText("");
3478
+ setError("");
3479
+ close();
3480
+ editor.focus();
3481
+ } catch (failure) {
3482
+ setError(failure instanceof Error ? failure.message : String(failure));
3483
+ }
3484
+ },
3485
+ children: [
3486
+ /* @__PURE__ */ jsxs("label", { children: [
3487
+ "Label",
3488
+ /* @__PURE__ */ jsx(
3489
+ "select",
3490
+ {
3491
+ value: label,
3492
+ onChange: (event) => setLabel(event.target.value),
3493
+ children: CAPTION_LABELS.map((value) => /* @__PURE__ */ jsx("option", { children: value }, value))
3494
+ }
3495
+ )
3496
+ ] }),
3497
+ /* @__PURE__ */ jsxs("label", { children: [
3498
+ "Caption",
3499
+ /* @__PURE__ */ jsx(
3500
+ "input",
3501
+ {
3502
+ autoFocus: true,
3503
+ value: text,
3504
+ placeholder: "Optional text after the number",
3505
+ "data-testid": "docx-caption-text",
3506
+ onChange: (event) => setText(event.target.value)
3507
+ }
3508
+ )
3509
+ ] }),
3510
+ error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
3511
+ /* @__PURE__ */ jsx("button", { type: "submit", "data-testid": "docx-caption-insert", children: "Insert" })
3512
+ ]
3513
+ }
3514
+ ) });
3515
+ }
3516
+ function PictureButtons() {
3517
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3518
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageWrap, {}),
3519
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageProperties, {}),
3520
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageAltText, {}),
3521
+ /* @__PURE__ */ jsx(TransformAction, { action: "rotateCW", label: "Rotate right 90\xB0", icon: /* @__PURE__ */ jsx(RotateCw, { size: 16 }) }),
3522
+ /* @__PURE__ */ jsx(TransformAction, { action: "rotateCCW", label: "Rotate left 90\xB0", icon: /* @__PURE__ */ jsx(RotateCcw, { size: 16 }) }),
3523
+ /* @__PURE__ */ jsx(
3524
+ TransformAction,
3525
+ {
3526
+ action: "flipH",
3527
+ label: "Flip horizontal",
3528
+ icon: /* @__PURE__ */ jsx(FlipHorizontal2, { size: 16 })
3529
+ }
3530
+ ),
3531
+ /* @__PURE__ */ jsx(TransformAction, { action: "flipV", label: "Flip vertical", icon: /* @__PURE__ */ jsx(FlipVertical2, { size: 16 }) }),
3532
+ /* @__PURE__ */ jsx(CaptionButton, {})
3533
+ ] });
3534
+ }
3535
+ var LAYOUT_TAB = "Layout";
3536
+ var TABLE_TAB = "Table";
3537
+ var PICTURE_TAB = "Picture";
3538
+ function useContextualTabs(tab) {
3539
+ const inTable = useEditorState((snapshot) => snapshot.table !== null);
3540
+ const onPicture = useEditorState((snapshot) => snapshot.image !== null);
3541
+ const tabs = [...inTable ? [TABLE_TAB] : [], ...onPicture ? [PICTURE_TAB] : []];
3542
+ const contextual = tab === TABLE_TAB || tab === PICTURE_TAB;
3543
+ return { tabs, shown: contextual && !tabs.includes(tab) ? "Home" : tab };
3544
+ }
3545
+ function LayoutTabContent({ tab }) {
3546
+ if (tab === LAYOUT_TAB) return /* @__PURE__ */ jsx(LayoutButtons, {});
3547
+ if (tab === TABLE_TAB) return /* @__PURE__ */ jsx(TableButtons, {});
3548
+ if (tab === PICTURE_TAB) return /* @__PURE__ */ jsx(PictureButtons, {});
3549
+ return null;
3550
+ }
3551
+ var contains = (node, id) => node.id === id || (node.children ?? []).some((child) => contains(child, id));
3552
+ function firstParagraph(node) {
3553
+ for (const child of node.children ?? []) {
3554
+ if (child.kind === "paragraph" && child.id) return child.id;
3555
+ if (child.localName === "tbl") continue;
3556
+ const nested = firstParagraph(child);
3557
+ if (nested) return nested;
3558
+ }
3559
+ return null;
3560
+ }
3561
+ function tableCells(root, paragraphId) {
3562
+ let table = null;
3563
+ const find = (node) => {
3564
+ if (node.localName === "tbl" && contains(node, paragraphId)) table = node;
3565
+ for (const child of node.children ?? []) if (contains(child, paragraphId)) find(child);
3566
+ };
3567
+ find(root);
3568
+ if (!table) return null;
3569
+ const cells = [];
3570
+ let index = -1;
3571
+ const rows = (node) => (node.children ?? []).flatMap(
3572
+ (child) => child.localName === "tr" ? [child] : child.localName === "tbl" ? [] : rows(child)
3573
+ );
3574
+ for (const row of rows(table))
3575
+ for (const cell of (row.children ?? []).filter((child) => child.localName === "tc")) {
3576
+ const first = firstParagraph(cell);
3577
+ if (!first) continue;
3578
+ if (contains(cell, paragraphId)) index = cells.length;
3579
+ cells.push(first);
3580
+ }
3581
+ return index < 0 ? null : { cells, index };
3582
+ }
3583
+ function TableTabKeys({ scope }) {
3584
+ const editor = useDocxEditor();
3585
+ const latest = useRef(editor);
3586
+ latest.current = editor;
3587
+ useEffect(() => {
3588
+ const element = scope.current;
3589
+ if (!element) return;
3590
+ const onKeyDown = (event) => {
3591
+ if (event.key !== "Tab" || event.ctrlKey || event.metaKey || event.altKey) return;
3592
+ const editor2 = latest.current;
3593
+ const surface = editor2?.surface;
3594
+ const head = surface?.state().selection?.head;
3595
+ if (!editor2 || !surface || !head) return;
3596
+ const root = () => surface.session.part().root;
3597
+ const table = tableCells(root(), head.paragraphId);
3598
+ if (!table) return;
3599
+ event.preventDefault();
3600
+ event.stopPropagation();
3601
+ let target = table.cells[table.index + (event.shiftKey ? -1 : 1)];
3602
+ if (!target && !event.shiftKey && editor2.exec({ type: "insertRow", where: "below" }).ok) {
3603
+ const grown = tableCells(
3604
+ root(),
3605
+ surface.state().selection?.head.paragraphId ?? head.paragraphId
3606
+ );
3607
+ target = grown?.cells[table.cells.length] ?? "";
3608
+ }
3609
+ if (!target) return;
3610
+ const point = { paragraphId: target, offset: 0 };
3611
+ surface.setSelection({ anchor: point, head: point });
3612
+ };
3613
+ element.addEventListener("keydown", onKeyDown, true);
3614
+ return () => element.removeEventListener("keydown", onKeyDown, true);
3615
+ }, [scope]);
3616
+ return null;
3617
+ }
2764
3618
  function QuickParts() {
2765
3619
  const house = useHouseTemplate();
2766
3620
  if (!house || house.blocks.length === 0) return null;
@@ -2931,17 +3785,23 @@ function DefinedTermButtons() {
2931
3785
  }
2932
3786
  function DocxRibbon({
2933
3787
  onSave,
2934
- badge
3788
+ badge,
3789
+ imageGeneration
2935
3790
  }) {
2936
- const [tab, setTab] = useState("Home");
3791
+ const [selected, setTab] = useState("Home");
3792
+ const contextual = useContextualTabs(selected);
3793
+ useGenerateImageTarget();
3794
+ const tab = contextual.shown;
2937
3795
  const t = useChromeTranslate();
2938
3796
  return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon", children: [
2939
3797
  /* @__PURE__ */ jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
2940
3798
  /* @__PURE__ */ jsx("option", { children: "Home" }),
2941
3799
  /* @__PURE__ */ jsx("option", { children: "Insert" }),
2942
3800
  /* @__PURE__ */ jsx("option", { children: "Design" }),
3801
+ /* @__PURE__ */ jsx("option", { children: LAYOUT_TAB }),
2943
3802
  /* @__PURE__ */ jsx("option", { children: "References" }),
2944
- /* @__PURE__ */ jsx("option", { children: "Review" })
3803
+ /* @__PURE__ */ jsx("option", { children: "Review" }),
3804
+ contextual.tabs.map((name) => /* @__PURE__ */ jsx("option", { children: name }, name))
2945
3805
  ] }),
2946
3806
  /* @__PURE__ */ jsxs(DocxEditor.Toolbar, { t, preset: false, onSave, children: [
2947
3807
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.Undo, {}),
@@ -2972,10 +3832,14 @@ function DocxRibbon({
2972
3832
  tab === "Insert" && /* @__PURE__ */ jsxs(Fragment, { children: [
2973
3833
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableInsert, {}),
2974
3834
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageInsert, {}),
3835
+ imageGeneration && /* @__PURE__ */ jsx(GenerateImageButton, { generation: imageGeneration }),
3836
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.Button, { slotId: "insert.pageBreak" }),
2975
3837
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {}),
2976
3838
  /* @__PURE__ */ jsx(DefinedTermButtons, {}),
2977
3839
  /* @__PURE__ */ jsx(QuickParts, {})
2978
3840
  ] }),
3841
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableCellFill, { hidden: true }),
3842
+ /* @__PURE__ */ jsx(LayoutTabContent, { tab }),
2979
3843
  tab === "Design" && /* @__PURE__ */ jsx(HouseTemplatePanel, {}),
2980
3844
  tab === "References" && /* @__PURE__ */ jsx(NoteButtons, {}),
2981
3845
  tab === "Review" && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -3351,6 +4215,10 @@ function NativeDocxEditor({
3351
4215
  DocxRibbon,
3352
4216
  {
3353
4217
  onSave: () => void saveVersion(),
4218
+ imageGeneration: adapter.imageGeneration && {
4219
+ notify: adapter.notify,
4220
+ ...adapter.imageGeneration
4221
+ },
3354
4222
  badge: (
3355
4223
  // Says the document is shared live, in place of a title row above the ribbon.
3356
4224
  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
@@ -3358,6 +4226,7 @@ function NativeDocxEditor({
3358
4226
  }
3359
4227
  ),
3360
4228
  !stream && /* @__PURE__ */ jsx(WordShortcuts, { scope: stageRef }),
4229
+ !stream && /* @__PURE__ */ jsx(TableTabKeys, { scope: stageRef }),
3361
4230
  /* @__PURE__ */ jsxs(
3362
4231
  "div",
3363
4232
  {
@@ -3490,6 +4359,6 @@ async function loadFonts(baseUrl) {
3490
4359
  };
3491
4360
  }
3492
4361
 
3493
- export { BUILTIN_TEMPLATE_NAME, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxAuthorContext, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, HouseTemplateProvider, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, resolveHouseTemplate, reviewModule, structuralPort, templateFileName, trackedOrNoted, updateDefinitions, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, useTrackedOrNoted, withDirectEditing };
3494
- //# sourceMappingURL=chunk-4ZTIMQ7Z.js.map
3495
- //# sourceMappingURL=chunk-4ZTIMQ7Z.js.map
4362
+ export { BUILTIN_TEMPLATE_NAME, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxAuthorContext, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, GenerateImageButton, HouseTemplateProvider, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, resolveHouseTemplate, reviewModule, structuralPort, templateFileName, trackedOrNoted, updateDefinitions, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, useTrackedOrNoted, withDirectEditing };
4363
+ //# sourceMappingURL=chunk-72PQ42YI.js.map
4364
+ //# sourceMappingURL=chunk-72PQ42YI.js.map