superdoc 2.18.1-next.5 → 2.19.0-next.2

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 (42) hide show
  1. package/dist/chunks/{create-super-doc-ui-C3HDE1IE.es.js → create-super-doc-ui-BFJ9Y0YV.cjs} +1396 -1
  2. package/dist/chunks/{create-super-doc-ui-Gxk1m05U.cjs → create-super-doc-ui-Dfw1b6h_.es.js} +1318 -66
  3. package/dist/collaboration-upgrade-engine.cjs +1 -1
  4. package/dist/collaboration-upgrade-engine.es.js +1 -1
  5. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  6. package/dist/document-api/src/contract/operation-definitions.d.ts +9 -0
  7. package/dist/document-api/src/index.d.ts +1 -0
  8. package/dist/document-api/src/types/track-changes.types.d.ts +35 -2
  9. package/dist/document-api/src/validation-primitives.d.ts +1 -1
  10. package/dist/document-api/src/watermarks/watermarks.d.ts +4 -1
  11. package/dist/document-api/src/watermarks/watermarks.types.d.ts +34 -0
  12. package/dist/layout-engine/painters/dom/src/index.d.ts +2 -0
  13. package/dist/public/ui-react.cjs +1 -1
  14. package/dist/public/ui-react.es.js +1 -1
  15. package/dist/public/ui-vue.cjs +1 -1
  16. package/dist/public/ui-vue.es.js +1 -1
  17. package/dist/public/ui.cjs +1 -1
  18. package/dist/public/ui.es.js +1 -1
  19. package/dist/style.css +596 -80
  20. package/dist/style.layered.css +597 -81
  21. package/dist/superdoc/src/components/surfaces/WatermarkSurface.vue.d.ts +16 -0
  22. package/dist/superdoc/src/components/surfaces/watermark-surface-runtime.d.ts +8 -0
  23. package/dist/superdoc/src/core/SuperDoc.d.ts +8 -7
  24. package/dist/superdoc/src/core/types/index.d.ts +36 -18
  25. package/dist/superdoc/src/internal/toolbar/built-in/OverflowMenu.vue.d.ts +93 -1
  26. package/dist/superdoc/src/internal/toolbar/built-in/Toolbar.vue.d.ts +3 -1
  27. package/dist/superdoc/src/internal/toolbar/built-in/ToolbarDropdown.vue.d.ts +1 -0
  28. package/dist/superdoc/src/internal/toolbar/built-in/toolbarIcons.d.ts +1 -0
  29. package/dist/superdoc/src/internal/toolbar/built-in/toolbarTexts.d.ts +1 -0
  30. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +3 -0
  31. package/dist/superdoc/src/public/ui/types.d.ts +10 -1
  32. package/dist/superdoc/src/public/ui/watermark-controller.d.ts +32 -0
  33. package/dist/superdoc/src/public/ui/watermark-dialog-state.d.ts +66 -0
  34. package/dist/superdoc/src/public/ui/watermark-draft.d.ts +6 -0
  35. package/dist/superdoc/src/public/ui.d.cts +12 -0
  36. package/dist/superdoc/src/public/ui.d.ts +1 -1
  37. package/dist/superdoc.cjs +110 -174
  38. package/dist/superdoc.es.js +108 -172
  39. package/dist-cdn/style.layered.css +1 -1
  40. package/dist-cdn/superdoc.min.css +1 -1
  41. package/dist-cdn/superdoc.min.js +38 -37
  42. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { i as __toESM$2 } from "./chunks/rolldown-runtime-0pSA04fp.es.js";
2
2
  import { t as blank_default } from "./chunks/blank-docx-DzQccOlW.es.js";
3
- import { a as applyV2CommentInvalidationFromMutation, c as DOM_CLASS_NAMES, d as recordInteraction, l as closeInteractionHistory, n as executeFirstPartyCommandAsync, o as getV2TrackedChangeMutationImpact, r as registerFirstPartyCommandMutation, t as createSuperDocUI, u as createInteractionHistory } from "./chunks/create-super-doc-ui-C3HDE1IE.es.js";
3
+ import { a as applyV2CommentInvalidationFromMutation, c as resolveImageSrc, d as closeInteractionHistory, f as createInteractionHistory, n as executeFirstPartyCommandAsync, o as getV2TrackedChangeMutationImpact, p as recordInteraction, r as registerFirstPartyCommandMutation, s as createImageFilePicker, t as createSuperDocUI, u as DOM_CLASS_NAMES } from "./chunks/create-super-doc-ui-Dfw1b6h_.es.js";
4
4
  import { t as import_eventemitter3 } from "./chunks/eventemitter3-DEIiXiH2.es.js";
5
5
  import { t as v4 } from "./chunks/uuid-H0Xcmmhy.es.js";
6
6
  import { a as init_dist$1, i as global, n as init_dist$2, o as Buffer, r as process$1, s as init_dist, t as require_jszip_min } from "./chunks/jszip-BzJ3CyxR.es.js";
@@ -315,7 +315,7 @@ var shuffleArray = (array) => {
315
315
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
316
316
  function getSuperdocVersion() {
317
317
  try {
318
- return "2.18.1-next.5";
318
+ return "2.19.0-next.2";
319
319
  } catch {
320
320
  return "unknown";
321
321
  }
@@ -5455,13 +5455,13 @@ function resolveProviderExtension(candidate) {
5455
5455
  if (!adapterId) return {
5456
5456
  ok: false,
5457
5457
  reason: "invalid-adapter-id",
5458
- message: "SuperDoc v2 provider extensions require a non-empty v2Collaboration.adapterId."
5458
+ message: "SuperDoc v2 provider extensions require a non-empty collaboration.adapterId."
5459
5459
  };
5460
5460
  const documentId = normalizeNonEmptyString(candidate.documentId);
5461
5461
  if (!documentId) return {
5462
5462
  ok: false,
5463
5463
  reason: "invalid-document-id",
5464
- message: "SuperDoc v2 provider extensions require a non-empty v2Collaboration.documentId."
5464
+ message: "SuperDoc v2 provider extensions require a non-empty collaboration.documentId."
5465
5465
  };
5466
5466
  const roomMode = normalizeRoomMode(candidate.roomMode);
5467
5467
  if (!roomMode) return invalidRoomMode();
@@ -18712,6 +18712,7 @@ var toolbarIcons = {
18712
18712
  link: link_solid_default,
18713
18713
  image: image_solid_default,
18714
18714
  tableOfContents: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"96 96 448 448\"><!--!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M96 160C96 124.7 124.7 96 160 96L480 96C515.3 96 544 124.7 544 160L544 480C544 515.3 515.3 544 480 544L160 544C124.7 544 96 515.3 96 480L96 160zM160 160L160 224L224 224L224 160L160 160zM480 160L288 160L288 224L480 224L480 160zM160 288L160 352L224 352L224 288L160 288zM480 288L288 288L288 352L480 352L480 288zM160 416L160 480L224 480L224 416L160 416zM480 416L288 416L288 480L480 480L480 416z\"></path></svg>\n",
18715
+ watermark: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"16\" viewBox=\"0 0 384 512\"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM109.2 254.3L150.8 230.3L312.1 349.7L267.0 375.7L236.9 351.5L169.4 390.5L175.2 428.7L130.2 454.7ZM162.6 344.8L200.7 322.8L153.6 285.3Z\"/></svg>\n",
18715
18716
  alignLeft: align_left_solid_default,
18716
18717
  alignRight: align_right_solid_default,
18717
18718
  alignCenter: align_center_solid_default,
@@ -21423,7 +21424,9 @@ var SurfaceDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21423
21424
  }
21424
21425
  }
21425
21426
  function handleBackdropClick(event) {
21426
- if (event.target === event.currentTarget && props.surface.request.closeOnBackdrop !== false) emit("close");
21427
+ if (event.target !== event.currentTarget) return;
21428
+ event.preventDefault();
21429
+ if (props.surface.request.closeOnBackdrop !== false) emit("close");
21427
21430
  }
21428
21431
  const shellProps = computed(() => ({
21429
21432
  surfaceId: props.surface.id,
@@ -21499,7 +21502,7 @@ var SurfaceDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21499
21502
  ])) : __props.surface.component ? (openBlock(), createBlock(resolveDynamicComponent(__props.surface.component), normalizeProps(mergeProps({ key: 1 }, mergedComponentProps.value)), null, 16)) : createCommentVNode("", true)])], 12, _hoisted_1$18)], 32);
21500
21503
  };
21501
21504
  }
21502
- }, [["__scopeId", "data-v-20bf8e4b"]]);
21505
+ }, [["__scopeId", "data-v-582b6534"]]);
21503
21506
  //#endregion
21504
21507
  //#region src/components/surfaces/SurfaceFloating.vue
21505
21508
  var _hoisted_1$17 = ["aria-labelledby", "aria-label"];
@@ -21667,10 +21670,9 @@ var SurfaceHost_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21667
21670
  * Reads the SurfaceManager's reactive state (activeDialog / activeFloating)
21668
21671
  * and renders the appropriate surface shell for each slot.
21669
21672
  *
21670
- * Uses position:fixed with bounds dynamically computed as the intersection
21671
- * of .superdoc__layers (the document viewport) with the browser viewport.
21672
- * This keeps dialogs centered and floating surfaces pinned to the visible
21673
- * area regardless of document scroll position.
21673
+ * Floating surfaces are clipped to the visible document viewport. Modal
21674
+ * dialogs are siblings of that clipped host so their backdrop covers the
21675
+ * browser viewport, including toolbars and surrounding application content.
21674
21676
  *
21675
21677
  * Floating Escape and outside-pointer-down handling live here (document-level)
21676
21678
  * because floating surfaces are non-modal — focus can be anywhere in the
@@ -21868,15 +21870,15 @@ var SurfaceHost_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21868
21870
  surface: floating.value,
21869
21871
  ref_key: "floatingRef",
21870
21872
  ref: floatingRef
21871
- }, null, 8, ["surface"])) : createCommentVNode("", true), dialog.value ? (openBlock(), createBlock(SurfaceDialog_default, {
21873
+ }, null, 8, ["surface"])) : createCommentVNode("", true)], 4)) : createCommentVNode("", true), dialog.value ? (openBlock(), createBlock(SurfaceDialog_default, {
21872
21874
  key: dialog.value.id,
21873
21875
  surface: dialog.value,
21874
21876
  "scroll-lock-target": unref(geometryTarget),
21875
21877
  onClose: handleDialogClose
21876
- }, null, 8, ["surface", "scroll-lock-target"])) : createCommentVNode("", true)], 4)) : createCommentVNode("", true)]);
21878
+ }, null, 8, ["surface", "scroll-lock-target"])) : createCommentVNode("", true)]);
21877
21879
  };
21878
21880
  }
21879
- }, [["__scopeId", "data-v-c6a3b75f"]]);
21881
+ }, [["__scopeId", "data-v-dad99b67"]]);
21880
21882
  //#endregion
21881
21883
  //#region src/SuperDoc.vue
21882
21884
  var _hoisted_1$16 = ["innerHTML"];
@@ -38169,6 +38171,7 @@ function addCustomItemRegions(regions, customItems) {
38169
38171
  return result;
38170
38172
  }
38171
38173
  var TOOLBAR_OPTIONAL_ITEM_REGIONS = Object.freeze({
38174
+ watermark: "center",
38172
38175
  "formatting-marks": "right",
38173
38176
  "table-of-contents": "center"
38174
38177
  });
@@ -38279,7 +38282,8 @@ function normalizeToolbarOptions(options) {
38279
38282
  fonts: options.fontOptions === void 0 ? options.fonts : mapToolbarFontOptions(options.fontOptions),
38280
38283
  customButtons: options.customItems === void 0 ? options.customButtons : mapToolbarCustomItems(options.customItems),
38281
38284
  showFormattingMarksButton: includeItems === null && !itemIds.has("formatting-marks") ? options.showFormattingMarksButton : itemIds.has("formatting-marks") || includeItems?.has("formatting-marks"),
38282
- showTableOfContentsButton: includeItems === null && !itemIds.has("table-of-contents") ? options.showTableOfContentsButton : itemIds.has("table-of-contents") || includeItems?.has("table-of-contents")
38285
+ showTableOfContentsButton: includeItems === null && !itemIds.has("table-of-contents") ? options.showTableOfContentsButton : itemIds.has("table-of-contents") || includeItems?.has("table-of-contents"),
38286
+ showWatermarkButton: itemIds.has("watermark") || includeItems?.has("watermark") || false
38283
38287
  });
38284
38288
  }
38285
38289
  /**
@@ -38809,6 +38813,14 @@ var BUILT_IN_TOOLBAR_CATALOG = [
38809
38813
  disposition: "controller-routed",
38810
38814
  group: "center"
38811
38815
  },
38816
+ {
38817
+ name: "watermark",
38818
+ commandId: null,
38819
+ instanceMethod: "ui.watermark.open",
38820
+ disposition: "host-routed",
38821
+ group: "center",
38822
+ note: "opens the shared Watermark dialog through superdoc.ui.watermark.open; the workflow owns scope, edit permissions, and apply"
38823
+ },
38812
38824
  {
38813
38825
  name: "table",
38814
38826
  commandId: "table-insert",
@@ -38934,153 +38946,6 @@ function resolveToolbarCommandId(name) {
38934
38946
  return entry?.disposition === "controller-routed" ? entry.commandId : null;
38935
38947
  }
38936
38948
  //#endregion
38937
- //#region src/internal/toolbar/image-upload.js
38938
- /**
38939
- * Interactive image acquisition for the built-in toolbar (SD-3567).
38940
- *
38941
- * The toolbar image button routes through the shared controller command
38942
- * `image` → `create.image`, which requires a base64 PNG/JPEG data-URI `src`.
38943
- * This module owns the browser file picker and the File → data-URI
38944
- * conversion; the toolbar dispatches the resulting payload through the
38945
- * normal controller command path.
38946
- */
38947
- /** Accept filter for the picker: PNG and JPEG only (V1 parity; OOXML-native formats). */
38948
- var IMAGE_PICKER_ACCEPT = ".png,.jpg,.jpeg,image/png,image/jpeg";
38949
- var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
38950
- "image/png",
38951
- "image/jpeg",
38952
- "image/jpg"
38953
- ]);
38954
- var SUPPORTED_IMAGE_EXTENSIONS = /\.(png|jpe?g)$/i;
38955
- /**
38956
- * Whether a picked/dropped file is an insertable image (PNG/JPEG). Falls back
38957
- * to the file extension when the browser reports no MIME type.
38958
- * @param {File | null | undefined} file
38959
- * @returns {boolean}
38960
- */
38961
- function isSupportedImageFile(file) {
38962
- if (!file) return false;
38963
- const type = typeof file.type === "string" ? file.type.toLowerCase() : "";
38964
- if (type) return SUPPORTED_IMAGE_MIME_TYPES.has(type);
38965
- return SUPPORTED_IMAGE_EXTENSIONS.test(String(file.name ?? ""));
38966
- }
38967
- /**
38968
- * Read a File/Blob into a base64 data URI.
38969
- *
38970
- * Some browsers/OSes deliver picked files with an empty `type`; the picker
38971
- * accepts those by extension, but `readAsDataURL` would then produce an
38972
- * `application/octet-stream` data URI that `create.image` rejects. Rewrite
38973
- * the media type from the filename so extension-accepted files still insert.
38974
- * @param {Blob & { name?: string }} file
38975
- * @returns {Promise<string>}
38976
- */
38977
- function fileToDataUri(file) {
38978
- return new Promise((resolve, reject) => {
38979
- const reader = new FileReader();
38980
- reader.onload = () => resolve(normalizeImageDataUri(String(reader.result), file));
38981
- reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("image-read-failed"));
38982
- reader.readAsDataURL(file);
38983
- });
38984
- }
38985
- /**
38986
- * Ensure a data URI read from a supported file carries a PNG/JPEG media type.
38987
- * Leaves URIs already typed as a supported image (or uninferable ones) alone.
38988
- * @param {string} dataUri
38989
- * @param {(Blob & { name?: string }) | null | undefined} file
38990
- * @returns {string}
38991
- */
38992
- function normalizeImageDataUri(dataUri, file) {
38993
- const match = /^data:([^;,]*);base64,/.exec(dataUri);
38994
- if (!match) return dataUri;
38995
- const mime = match[1].toLowerCase();
38996
- if (SUPPORTED_IMAGE_MIME_TYPES.has(mime)) return dataUri;
38997
- const name = String(file?.name ?? "");
38998
- const inferred = /\.jpe?g$/i.test(name) ? "image/jpeg" : /\.png$/i.test(name) ? "image/png" : null;
38999
- if (!inferred) return dataUri;
39000
- return `data:${inferred};base64,${dataUri.slice(match[0].length)}`;
39001
- }
39002
- /**
39003
- * Resolve the `create.image` src for a picked file. Uses the consumer's
39004
- * `handleImageUpload` callback when configured (V1 contract:
39005
- * `(file: File) => Promise<string>`). A returned non-data URL is fetched and
39006
- * embedded, because `create.image` requires an embedded base64 data URI.
39007
- * @param {File} file
39008
- * @param {((file: File) => Promise<string>) | null | undefined} handleImageUpload
39009
- * @returns {Promise<string>}
39010
- */
39011
- async function resolveImageSrc(file, handleImageUpload) {
39012
- if (typeof handleImageUpload === "function") {
39013
- const result = await handleImageUpload(file);
39014
- if (typeof result !== "string" || result.trim() === "") throw new Error("handleImageUpload returned an empty result");
39015
- if (result.startsWith("data:")) return result;
39016
- return embedRemoteImage(result, file);
39017
- }
39018
- return fileToDataUri(file);
39019
- }
39020
- /**
39021
- * Fetch a remote image URL and embed it as a data URI. Object-store /
39022
- * presigned URLs often respond `application/octet-stream`, so keep a filename
39023
- * hint (URL path, falling back to the originally picked file) for the media
39024
- * type normalization in `fileToDataUri`.
39025
- * @param {string} url
39026
- * @param {File | null | undefined} pickedFile
39027
- * @returns {Promise<string>}
39028
- */
39029
- async function embedRemoteImage(url, pickedFile) {
39030
- const response = await fetch(url);
39031
- if (!response.ok) throw new Error(`image fetch failed: ${response.status}`);
39032
- const blob = await response.blob();
39033
- const name = (/\.(?:png|jpe?g)(?:$|[?#])/i.test(url) ? url.split(/[?#]/)[0] : "") || pickedFile?.name || "";
39034
- return fileToDataUri(name ? Object.assign(blob, { name }) : blob);
39035
- }
39036
- /**
39037
- * Hidden `<input type=file>` picker bound to one toolbar instance. The input
39038
- * stays attached (hidden) so automation and tests can drive it; `destroy()`
39039
- * removes it. Picking a file calls `onPick(file)`; an unsupported type or a
39040
- * failed pick routes through `onError(error)`.
39041
- *
39042
- * @param {{
39043
- * ownerDocument?: Document,
39044
- * onPick?: (file: File) => unknown,
39045
- * onError?: (error: Error) => void,
39046
- * }} options
39047
- * @returns {{ input: HTMLInputElement, open: () => void, destroy: () => void }}
39048
- */
39049
- function createImageFilePicker({ ownerDocument = document, onPick, onError } = {}) {
39050
- const input = ownerDocument.createElement("input");
39051
- input.type = "file";
39052
- input.accept = IMAGE_PICKER_ACCEPT;
39053
- input.hidden = true;
39054
- input.tabIndex = -1;
39055
- input.setAttribute("data-superdoc-image-picker", "true");
39056
- input.setAttribute("aria-hidden", "true");
39057
- input.addEventListener("change", () => {
39058
- const file = input.files && input.files[0];
39059
- input.value = "";
39060
- if (!file) return;
39061
- if (!isSupportedImageFile(file)) {
39062
- onError?.(/* @__PURE__ */ new Error(`unsupported image type: ${file.type || file.name}`));
39063
- return;
39064
- }
39065
- try {
39066
- const result = onPick?.(file);
39067
- if (result && typeof result.then === "function") result.catch((error) => onError?.(error));
39068
- } catch (error) {
39069
- onError?.(error);
39070
- }
39071
- });
39072
- ownerDocument.body?.appendChild(input);
39073
- return {
39074
- input,
39075
- open() {
39076
- input.click();
39077
- },
39078
- destroy() {
39079
- input.remove();
39080
- }
39081
- };
39082
- }
39083
- //#endregion
39084
38949
  //#region src/internal/toolbar/built-in/helpers.js
39085
38950
  var sanitizeNumber = (value, defaultNumber) => {
39086
38951
  let sanitized = value.replace(/[^0-9.]/g, "");
@@ -39270,6 +39135,7 @@ var ToolbarButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39270
39135
  handleClick();
39271
39136
  };
39272
39137
  const onEnterKeydown = (event) => {
39138
+ event.preventDefault();
39273
39139
  if (!props.allowEnterPropagation) event.stopPropagation();
39274
39140
  handleOuterEnter(event);
39275
39141
  };
@@ -39461,7 +39327,7 @@ var ToolbarButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39461
39327
  ], 10, _hoisted_2$10)], 46, _hoisted_1$14);
39462
39328
  };
39463
39329
  }
39464
- }, [["__scopeId", "data-v-22817fa7"]]);
39330
+ }, [["__scopeId", "data-v-96796456"]]);
39465
39331
  //#endregion
39466
39332
  //#region src/internal/toolbar/built-in/ToolbarSeparator.vue
39467
39333
  var _hoisted_1$13 = {
@@ -39541,7 +39407,7 @@ var ToolbarDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39541
39407
  }
39542
39408
  },
39543
39409
  emits: ["update:show", "select"],
39544
- setup(__props, { emit: __emit }) {
39410
+ setup(__props, { expose: __expose, emit: __emit }) {
39545
39411
  const props = __props;
39546
39412
  const emit = __emit;
39547
39413
  const slots = useSlots();
@@ -39776,6 +39642,7 @@ var ToolbarDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39776
39642
  trigger.setAttribute("tabindex", "-1");
39777
39643
  trigger.focus();
39778
39644
  };
39645
+ __expose({ focusTrigger });
39779
39646
  const handleKeyDown = (event) => {
39780
39647
  if (!isOpen.value) return;
39781
39648
  const { key } = event;
@@ -39887,7 +39754,7 @@ var ToolbarDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39887
39754
  })], 8, ["to"]))]);
39888
39755
  };
39889
39756
  }
39890
- }, [["__scopeId", "data-v-764d5fd7"]]);
39757
+ }, [["__scopeId", "data-v-fa3f6b69"]]);
39891
39758
  //#endregion
39892
39759
  //#region src/internal/toolbar/built-in/toolbar-focus-helpers.js
39893
39760
  var preventEditorFocusTransfer = (event) => {
@@ -39933,6 +39800,7 @@ var OverflowMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39933
39800
  setup(__props, { emit: __emit }) {
39934
39801
  const emit = __emit;
39935
39802
  const props = __props;
39803
+ const dropdown = ref(null);
39936
39804
  const isOverflowMenuOpened = computed(() => props.toolbarItem.expand.value);
39937
39805
  const hasOpenDropdown = computed(() => props.overflowItems.some((item) => item.type === "dropdown" && unref(item.expand)));
39938
39806
  const overflowToolbarItem = computed(() => ({
@@ -39948,6 +39816,10 @@ var OverflowMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39948
39816
  emit("close");
39949
39817
  };
39950
39818
  const handleCommand = ({ item, argument, option }) => {
39819
+ if (item.name.value === "watermark") {
39820
+ emit("close");
39821
+ dropdown.value?.focusTrigger();
39822
+ }
39951
39823
  emit("command", {
39952
39824
  item,
39953
39825
  argument,
@@ -39956,6 +39828,8 @@ var OverflowMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39956
39828
  };
39957
39829
  return (_ctx, _cache) => {
39958
39830
  return openBlock(), createBlock(ToolbarDropdown_default, {
39831
+ ref_key: "dropdown",
39832
+ ref: dropdown,
39959
39833
  class: "overflow-menu",
39960
39834
  "close-on-escape": !hasOpenDropdown.value,
39961
39835
  "has-open-child": hasOpenDropdown.value,
@@ -39973,7 +39847,10 @@ var OverflowMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39973
39847
  class: ["overflow-menu_items", "sd-toolbar-overflow-menu"]
39974
39848
  })
39975
39849
  }, {
39976
- trigger: withCtx(() => [createVNode(ToolbarButton_default, { "toolbar-item": overflowToolbarItem.value }, null, 8, ["toolbar-item"])]),
39850
+ trigger: withCtx(() => [createVNode(ToolbarButton_default, {
39851
+ "toolbar-item": overflowToolbarItem.value,
39852
+ "allow-enter-propagation": true
39853
+ }, null, 8, ["toolbar-item"])]),
39977
39854
  menu: withCtx(() => [createVNode(ButtonGroup_default, {
39978
39855
  class: "superdoc-toolbar-overflow",
39979
39856
  "toolbar-items": __props.overflowItems,
@@ -39995,7 +39872,7 @@ var OverflowMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
39995
39872
  ]);
39996
39873
  };
39997
39874
  }
39998
- }, [["__scopeId", "data-v-5f8ee279"]]);
39875
+ }, [["__scopeId", "data-v-1227fcf9"]]);
39999
39876
  //#endregion
40000
39877
  //#region src/internal/toolbar/built-in/font-typeahead.js
40001
39878
  /**
@@ -41630,10 +41507,12 @@ var Toolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
41630
41507
  setup(__props, { emit: __emit }) {
41631
41508
  const { proxy } = getCurrentInstance();
41632
41509
  let toolbarKey = ref(1);
41510
+ const toolbarRoot = ref(null);
41633
41511
  const toolbarStateVersion = ref(0);
41634
41512
  const compactSideGroups = ref(false);
41635
41513
  let containerResizeObserver = null;
41636
41514
  let pendingSelectionCapture = null;
41515
+ let renderedWidth = proxy.$toolbar.getAvailableWidth();
41637
41516
  /**
41638
41517
  * Computed property that determines the font-family to use for toolbar UI surfaces.
41639
41518
  * Retrieves the configured font from the toolbar instance's config and validates it.
@@ -41654,10 +41533,41 @@ var Toolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
41654
41533
  const updateCompactSideGroups = () => {
41655
41534
  compactSideGroups.value = proxy.$toolbar.getAvailableWidth() <= RESPONSIVE_BREAKPOINTS.lg;
41656
41535
  };
41536
+ const captureFocusedControl = () => {
41537
+ const root = toolbarRoot.value;
41538
+ const ownerDocument = root?.ownerDocument;
41539
+ const focused = ownerDocument?.activeElement;
41540
+ if (!root?.contains(focused)) return null;
41541
+ const control = focused.closest("[data-sd-part=\"toolbar-item\"]") ?? focused.querySelector("[data-sd-part=\"toolbar-item\"]");
41542
+ if (!control || focused !== control && !focused.contains(control)) return null;
41543
+ const itemId = control.querySelector("[data-item]")?.getAttribute("data-item");
41544
+ if (!itemId) return null;
41545
+ return () => {
41546
+ const currentRoot = toolbarRoot.value;
41547
+ if (!currentRoot?.isConnected || focused.isConnected || ownerDocument.activeElement !== ownerDocument.body) return;
41548
+ const markers = [...currentRoot.querySelectorAll("[data-item]")];
41549
+ for (const id of [itemId, "btn-overflow"]) {
41550
+ const target = markers.find((marker) => marker.getAttribute("data-item") === id)?.closest("[data-sd-part=\"toolbar-item\"]");
41551
+ if (target && target.getAttribute("aria-disabled") !== "true") {
41552
+ target.focus({ preventScroll: true });
41553
+ return;
41554
+ }
41555
+ }
41556
+ };
41557
+ };
41558
+ const renderToolbar = async (restoreFocus) => {
41559
+ toolbarKey.value += 1;
41560
+ await nextTick();
41561
+ restoreFocus?.();
41562
+ };
41657
41563
  const onWindowResized = async () => {
41564
+ const width = proxy.$toolbar.getAvailableWidth();
41565
+ if (width === renderedWidth) return;
41566
+ renderedWidth = width;
41567
+ const restoreFocus = captureFocusedControl();
41658
41568
  await proxy.$toolbar.onToolbarResize();
41659
41569
  updateCompactSideGroups();
41660
- toolbarKey.value += 1;
41570
+ await renderToolbar(restoreFocus);
41661
41571
  };
41662
41572
  const onResizeThrottled = throttle(onWindowResized, 300);
41663
41573
  /**
@@ -41667,12 +41577,16 @@ var Toolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
41667
41577
  * SuperToolbar emits `toolbar-items-changed` on rebuild; bumping the key re-reads the new items into the DOM.
41668
41578
  */
41669
41579
  const onToolbarItemsChanged = () => {
41670
- toolbarKey.value += 1;
41580
+ const restoreFocus = captureFocusedControl();
41581
+ renderedWidth = proxy.$toolbar.getAvailableWidth();
41582
+ updateCompactSideGroups();
41583
+ renderToolbar(restoreFocus);
41671
41584
  };
41672
41585
  const onToolbarStateChanged = () => {
41673
41586
  toolbarStateVersion.value += 1;
41674
41587
  };
41675
41588
  function teardownListeners() {
41589
+ onResizeThrottled.cancel();
41676
41590
  window.removeEventListener("resize", onResizeThrottled);
41677
41591
  proxy.$toolbar.off?.("toolbar-items-changed", onToolbarItemsChanged);
41678
41592
  proxy.$toolbar.off?.("toolbar-state-change", onToolbarStateChanged);
@@ -41730,6 +41644,8 @@ var Toolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
41730
41644
  return (_ctx, _cache) => {
41731
41645
  return openBlock(), createElementBlock("div", {
41732
41646
  class: "superdoc-toolbar",
41647
+ ref_key: "toolbarRoot",
41648
+ ref: toolbarRoot,
41733
41649
  key: unref(toolbarKey),
41734
41650
  role: "toolbar",
41735
41651
  "aria-label": "Toolbar",
@@ -41786,7 +41702,7 @@ var Toolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
41786
41702
  ], 32);
41787
41703
  };
41788
41704
  }
41789
- }, [["__scopeId", "data-v-ed19716b"]]);
41705
+ }, [["__scopeId", "data-v-9418cc7d"]]);
41790
41706
  //#endregion
41791
41707
  //#region src/internal/toolbar/built-in/font-options.js
41792
41708
  /**
@@ -43383,6 +43299,15 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons, toolbarTexts, toolbarFonts
43383
43299
  disabled: false,
43384
43300
  attributes: { ariaLabel: "Table of contents" }
43385
43301
  });
43302
+ const watermark = useToolbarItem({
43303
+ type: "button",
43304
+ name: "watermark",
43305
+ icon: toolbarIcons.watermark,
43306
+ tooltip: toolbarTexts.watermark,
43307
+ allowWithoutEditor: true,
43308
+ disabled: true,
43309
+ attributes: { ariaLabel: toolbarTexts.watermark }
43310
+ });
43386
43311
  const tableItem = useToolbarItem({
43387
43312
  type: "dropdown",
43388
43313
  name: "table",
@@ -44088,6 +44013,7 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons, toolbarTexts, toolbarFonts
44088
44013
  link,
44089
44014
  image,
44090
44015
  ...shouldIncludeTableOfContents ? [tableOfContents] : [],
44016
+ ...superToolbar.config?.showWatermarkButton || configuredItemNames?.has("watermark") ? [watermark] : [],
44091
44017
  tableItem,
44092
44018
  tableActionsItem,
44093
44019
  makeSeparator(),
@@ -44194,6 +44120,7 @@ var toolbarTexts = {
44194
44120
  link: "Link",
44195
44121
  image: "Image",
44196
44122
  tableOfContents: "Table of contents",
44123
+ watermark: "Watermark",
44197
44124
  table: "Insert table",
44198
44125
  tableActions: "Table options",
44199
44126
  addRowBefore: "Insert row above",
@@ -44393,7 +44320,8 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44393
44320
  groups: null,
44394
44321
  customButtons: [],
44395
44322
  showFormattingMarksButton: false,
44396
- showTableOfContentsButton: false
44323
+ showTableOfContentsButton: false,
44324
+ showWatermarkButton: false
44397
44325
  };
44398
44326
  toolbarItems = [];
44399
44327
  overflowItems = [];
@@ -44727,6 +44655,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44727
44655
  return false;
44728
44656
  }
44729
44657
  #canRunHostAction(name) {
44658
+ if (name === "watermark") return this.ui?.document?.getSnapshot?.().ready === true && typeof this.ui?.watermark?.open === "function";
44730
44659
  if (name === "ruler") return typeof this.superdoc?.toggleRuler === "function";
44731
44660
  if (name === "formattingMarks") return typeof this.superdoc?.toggleFormattingMarks === "function" || typeof this.superdoc?.setShowFormattingMarks === "function";
44732
44661
  return false;
@@ -44905,6 +44834,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44905
44834
  item.resetDisabled();
44906
44835
  this.#applyHeadlessState(item);
44907
44836
  });
44837
+ this.getToolbarItemByName("overflow")?.setDisabled(!this.overflowItems.some((item) => item.type !== "separator" && !item.disabled.value));
44908
44838
  const copyFormatItem = this.getToolbarItemByName("copyFormat");
44909
44839
  if (copyFormatItem) if (this.snapshot?.copyFormatActive) copyFormatItem.activate();
44910
44840
  else copyFormatItem.deactivate();
@@ -44919,6 +44849,12 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44919
44849
  }
44920
44850
  #runHostAction(name, argument) {
44921
44851
  const superdoc = this.superdoc;
44852
+ if (name === "watermark") {
44853
+ if (!this.#canRunHostAction(name)) return false;
44854
+ const result = this.ui.watermark.open();
44855
+ if (!result.ok) throw new Error(`[superdoc toolbar] Watermark dialog unavailable: ${result.reason}`);
44856
+ return true;
44857
+ }
44922
44858
  if (name === "ruler") {
44923
44859
  if (typeof superdoc?.toggleRuler === "function") {
44924
44860
  superdoc.toggleRuler();
@@ -45681,7 +45617,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45681
45617
  this.config.colors = shuffleArray(this.config.colors);
45682
45618
  this.userColorMap = /* @__PURE__ */ new Map();
45683
45619
  this.colorIndex = 0;
45684
- this.version = "2.18.1-next.5";
45620
+ this.version = "2.19.0-next.2";
45685
45621
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
45686
45622
  this.superdocId = config.superdocId || v4();
45687
45623
  this.colors = this.config.colors ?? [];
@@ -47073,7 +47009,7 @@ var SuperDoc = class extends import_eventemitter3.default {
47073
47009
  {
47074
47010
  feature: "shell.collaboration",
47075
47011
  status: "supported",
47076
- reason: "v2 single-doc y-websocket collaboration is wired through document.v2Collaboration, including collaboration-ready, awareness-update, and locked event bridges; arbitrary external { ydoc, provider } adapters remain unsupported"
47012
+ reason: "v2 single-doc collaboration (hocuspocus, y-websocket, liveblocks, and registered provider extensions) is wired through document.collaboration, including collaboration-ready, awareness-update, and locked event bridges; external { ydoc, provider } instances remain unsupported"
47077
47013
  },
47078
47014
  {
47079
47015
  feature: "shell.context-menu",