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
package/dist/superdoc.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("./chunks/rolldown-runtime-74VwNCz7.cjs");
3
3
  const require_blank_docx = require("./chunks/blank-docx-BuFAbRjs.cjs");
4
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-Gxk1m05U.cjs");
4
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BFJ9Y0YV.cjs");
5
5
  const require_eventemitter3 = require("./chunks/eventemitter3-C_TAnXOl.cjs");
6
6
  const require_uuid = require("./chunks/uuid-BhG0ngwk.cjs");
7
7
  const require_jszip = require("./chunks/jszip-D8mAFF-r.cjs");
@@ -316,7 +316,7 @@ var shuffleArray = (array) => {
316
316
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
317
317
  function getSuperdocVersion() {
318
318
  try {
319
- return "2.18.1-next.5";
319
+ return "2.19.0-next.2";
320
320
  } catch {
321
321
  return "unknown";
322
322
  }
@@ -5456,13 +5456,13 @@ function resolveProviderExtension(candidate) {
5456
5456
  if (!adapterId) return {
5457
5457
  ok: false,
5458
5458
  reason: "invalid-adapter-id",
5459
- message: "SuperDoc v2 provider extensions require a non-empty v2Collaboration.adapterId."
5459
+ message: "SuperDoc v2 provider extensions require a non-empty collaboration.adapterId."
5460
5460
  };
5461
5461
  const documentId = normalizeNonEmptyString(candidate.documentId);
5462
5462
  if (!documentId) return {
5463
5463
  ok: false,
5464
5464
  reason: "invalid-document-id",
5465
- message: "SuperDoc v2 provider extensions require a non-empty v2Collaboration.documentId."
5465
+ message: "SuperDoc v2 provider extensions require a non-empty collaboration.documentId."
5466
5466
  };
5467
5467
  const roomMode = normalizeRoomMode(candidate.roomMode);
5468
5468
  if (!roomMode) return invalidRoomMode();
@@ -18727,6 +18727,7 @@ var toolbarIcons = {
18727
18727
  link: link_solid_default,
18728
18728
  image: image_solid_default,
18729
18729
  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",
18730
+ 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",
18730
18731
  alignLeft: align_left_solid_default,
18731
18732
  alignRight: align_right_solid_default,
18732
18733
  alignCenter: align_center_solid_default,
@@ -21458,7 +21459,9 @@ var _sfc_main$23 = {
21458
21459
  }
21459
21460
  }
21460
21461
  function handleBackdropClick(event) {
21461
- if (event.target === event.currentTarget && props.surface.request.closeOnBackdrop !== false) emit("close");
21462
+ if (event.target !== event.currentTarget) return;
21463
+ event.preventDefault();
21464
+ if (props.surface.request.closeOnBackdrop !== false) emit("close");
21462
21465
  }
21463
21466
  const shellProps = (0, vue.computed)(() => ({
21464
21467
  surfaceId: props.surface.id,
@@ -21535,7 +21538,7 @@ var _sfc_main$23 = {
21535
21538
  };
21536
21539
  }
21537
21540
  };
21538
- var SurfaceDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$23, [["__scopeId", "data-v-20bf8e4b"]]);
21541
+ var SurfaceDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$23, [["__scopeId", "data-v-582b6534"]]);
21539
21542
  //#endregion
21540
21543
  //#region src/components/surfaces/SurfaceFloating.vue
21541
21544
  var _hoisted_1$17 = ["aria-labelledby", "aria-label"];
@@ -21706,10 +21709,9 @@ var _sfc_main$21 = {
21706
21709
  * Reads the SurfaceManager's reactive state (activeDialog / activeFloating)
21707
21710
  * and renders the appropriate surface shell for each slot.
21708
21711
  *
21709
- * Uses position:fixed with bounds dynamically computed as the intersection
21710
- * of .superdoc__layers (the document viewport) with the browser viewport.
21711
- * This keeps dialogs centered and floating surfaces pinned to the visible
21712
- * area regardless of document scroll position.
21712
+ * Floating surfaces are clipped to the visible document viewport. Modal
21713
+ * dialogs are siblings of that clipped host so their backdrop covers the
21714
+ * browser viewport, including toolbars and surrounding application content.
21713
21715
  *
21714
21716
  * Floating Escape and outside-pointer-down handling live here (document-level)
21715
21717
  * because floating surfaces are non-modal — focus can be anywhere in the
@@ -21907,16 +21909,16 @@ var _sfc_main$21 = {
21907
21909
  surface: floating.value,
21908
21910
  ref_key: "floatingRef",
21909
21911
  ref: floatingRef
21910
- }, null, 8, ["surface"])) : (0, vue.createCommentVNode)("", true), dialog.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(SurfaceDialog_default, {
21912
+ }, null, 8, ["surface"])) : (0, vue.createCommentVNode)("", true)], 4)) : (0, vue.createCommentVNode)("", true), dialog.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(SurfaceDialog_default, {
21911
21913
  key: dialog.value.id,
21912
21914
  surface: dialog.value,
21913
21915
  "scroll-lock-target": (0, vue.unref)(geometryTarget),
21914
21916
  onClose: handleDialogClose
21915
- }, null, 8, ["surface", "scroll-lock-target"])) : (0, vue.createCommentVNode)("", true)], 4)) : (0, vue.createCommentVNode)("", true)]);
21917
+ }, null, 8, ["surface", "scroll-lock-target"])) : (0, vue.createCommentVNode)("", true)]);
21916
21918
  };
21917
21919
  }
21918
21920
  };
21919
- var SurfaceHost_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$21, [["__scopeId", "data-v-c6a3b75f"]]);
21921
+ var SurfaceHost_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$21, [["__scopeId", "data-v-dad99b67"]]);
21920
21922
  //#endregion
21921
21923
  //#region src/SuperDoc.vue
21922
21924
  var _hoisted_1$16 = ["innerHTML"];
@@ -38209,6 +38211,7 @@ function addCustomItemRegions(regions, customItems) {
38209
38211
  return result;
38210
38212
  }
38211
38213
  var TOOLBAR_OPTIONAL_ITEM_REGIONS = Object.freeze({
38214
+ watermark: "center",
38212
38215
  "formatting-marks": "right",
38213
38216
  "table-of-contents": "center"
38214
38217
  });
@@ -38319,7 +38322,8 @@ function normalizeToolbarOptions(options) {
38319
38322
  fonts: options.fontOptions === void 0 ? options.fonts : mapToolbarFontOptions(options.fontOptions),
38320
38323
  customButtons: options.customItems === void 0 ? options.customButtons : mapToolbarCustomItems(options.customItems),
38321
38324
  showFormattingMarksButton: includeItems === null && !itemIds.has("formatting-marks") ? options.showFormattingMarksButton : itemIds.has("formatting-marks") || includeItems?.has("formatting-marks"),
38322
- showTableOfContentsButton: includeItems === null && !itemIds.has("table-of-contents") ? options.showTableOfContentsButton : itemIds.has("table-of-contents") || includeItems?.has("table-of-contents")
38325
+ showTableOfContentsButton: includeItems === null && !itemIds.has("table-of-contents") ? options.showTableOfContentsButton : itemIds.has("table-of-contents") || includeItems?.has("table-of-contents"),
38326
+ showWatermarkButton: itemIds.has("watermark") || includeItems?.has("watermark") || false
38323
38327
  });
38324
38328
  }
38325
38329
  /**
@@ -38849,6 +38853,14 @@ var BUILT_IN_TOOLBAR_CATALOG = [
38849
38853
  disposition: "controller-routed",
38850
38854
  group: "center"
38851
38855
  },
38856
+ {
38857
+ name: "watermark",
38858
+ commandId: null,
38859
+ instanceMethod: "ui.watermark.open",
38860
+ disposition: "host-routed",
38861
+ group: "center",
38862
+ note: "opens the shared Watermark dialog through superdoc.ui.watermark.open; the workflow owns scope, edit permissions, and apply"
38863
+ },
38852
38864
  {
38853
38865
  name: "table",
38854
38866
  commandId: "table-insert",
@@ -38974,153 +38986,6 @@ function resolveToolbarCommandId(name) {
38974
38986
  return entry?.disposition === "controller-routed" ? entry.commandId : null;
38975
38987
  }
38976
38988
  //#endregion
38977
- //#region src/internal/toolbar/image-upload.js
38978
- /**
38979
- * Interactive image acquisition for the built-in toolbar (SD-3567).
38980
- *
38981
- * The toolbar image button routes through the shared controller command
38982
- * `image` → `create.image`, which requires a base64 PNG/JPEG data-URI `src`.
38983
- * This module owns the browser file picker and the File → data-URI
38984
- * conversion; the toolbar dispatches the resulting payload through the
38985
- * normal controller command path.
38986
- */
38987
- /** Accept filter for the picker: PNG and JPEG only (V1 parity; OOXML-native formats). */
38988
- var IMAGE_PICKER_ACCEPT = ".png,.jpg,.jpeg,image/png,image/jpeg";
38989
- var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
38990
- "image/png",
38991
- "image/jpeg",
38992
- "image/jpg"
38993
- ]);
38994
- var SUPPORTED_IMAGE_EXTENSIONS = /\.(png|jpe?g)$/i;
38995
- /**
38996
- * Whether a picked/dropped file is an insertable image (PNG/JPEG). Falls back
38997
- * to the file extension when the browser reports no MIME type.
38998
- * @param {File | null | undefined} file
38999
- * @returns {boolean}
39000
- */
39001
- function isSupportedImageFile(file) {
39002
- if (!file) return false;
39003
- const type = typeof file.type === "string" ? file.type.toLowerCase() : "";
39004
- if (type) return SUPPORTED_IMAGE_MIME_TYPES.has(type);
39005
- return SUPPORTED_IMAGE_EXTENSIONS.test(String(file.name ?? ""));
39006
- }
39007
- /**
39008
- * Read a File/Blob into a base64 data URI.
39009
- *
39010
- * Some browsers/OSes deliver picked files with an empty `type`; the picker
39011
- * accepts those by extension, but `readAsDataURL` would then produce an
39012
- * `application/octet-stream` data URI that `create.image` rejects. Rewrite
39013
- * the media type from the filename so extension-accepted files still insert.
39014
- * @param {Blob & { name?: string }} file
39015
- * @returns {Promise<string>}
39016
- */
39017
- function fileToDataUri(file) {
39018
- return new Promise((resolve, reject) => {
39019
- const reader = new FileReader();
39020
- reader.onload = () => resolve(normalizeImageDataUri(String(reader.result), file));
39021
- reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("image-read-failed"));
39022
- reader.readAsDataURL(file);
39023
- });
39024
- }
39025
- /**
39026
- * Ensure a data URI read from a supported file carries a PNG/JPEG media type.
39027
- * Leaves URIs already typed as a supported image (or uninferable ones) alone.
39028
- * @param {string} dataUri
39029
- * @param {(Blob & { name?: string }) | null | undefined} file
39030
- * @returns {string}
39031
- */
39032
- function normalizeImageDataUri(dataUri, file) {
39033
- const match = /^data:([^;,]*);base64,/.exec(dataUri);
39034
- if (!match) return dataUri;
39035
- const mime = match[1].toLowerCase();
39036
- if (SUPPORTED_IMAGE_MIME_TYPES.has(mime)) return dataUri;
39037
- const name = String(file?.name ?? "");
39038
- const inferred = /\.jpe?g$/i.test(name) ? "image/jpeg" : /\.png$/i.test(name) ? "image/png" : null;
39039
- if (!inferred) return dataUri;
39040
- return `data:${inferred};base64,${dataUri.slice(match[0].length)}`;
39041
- }
39042
- /**
39043
- * Resolve the `create.image` src for a picked file. Uses the consumer's
39044
- * `handleImageUpload` callback when configured (V1 contract:
39045
- * `(file: File) => Promise<string>`). A returned non-data URL is fetched and
39046
- * embedded, because `create.image` requires an embedded base64 data URI.
39047
- * @param {File} file
39048
- * @param {((file: File) => Promise<string>) | null | undefined} handleImageUpload
39049
- * @returns {Promise<string>}
39050
- */
39051
- async function resolveImageSrc(file, handleImageUpload) {
39052
- if (typeof handleImageUpload === "function") {
39053
- const result = await handleImageUpload(file);
39054
- if (typeof result !== "string" || result.trim() === "") throw new Error("handleImageUpload returned an empty result");
39055
- if (result.startsWith("data:")) return result;
39056
- return embedRemoteImage(result, file);
39057
- }
39058
- return fileToDataUri(file);
39059
- }
39060
- /**
39061
- * Fetch a remote image URL and embed it as a data URI. Object-store /
39062
- * presigned URLs often respond `application/octet-stream`, so keep a filename
39063
- * hint (URL path, falling back to the originally picked file) for the media
39064
- * type normalization in `fileToDataUri`.
39065
- * @param {string} url
39066
- * @param {File | null | undefined} pickedFile
39067
- * @returns {Promise<string>}
39068
- */
39069
- async function embedRemoteImage(url, pickedFile) {
39070
- const response = await fetch(url);
39071
- if (!response.ok) throw new Error(`image fetch failed: ${response.status}`);
39072
- const blob = await response.blob();
39073
- const name = (/\.(?:png|jpe?g)(?:$|[?#])/i.test(url) ? url.split(/[?#]/)[0] : "") || pickedFile?.name || "";
39074
- return fileToDataUri(name ? Object.assign(blob, { name }) : blob);
39075
- }
39076
- /**
39077
- * Hidden `<input type=file>` picker bound to one toolbar instance. The input
39078
- * stays attached (hidden) so automation and tests can drive it; `destroy()`
39079
- * removes it. Picking a file calls `onPick(file)`; an unsupported type or a
39080
- * failed pick routes through `onError(error)`.
39081
- *
39082
- * @param {{
39083
- * ownerDocument?: Document,
39084
- * onPick?: (file: File) => unknown,
39085
- * onError?: (error: Error) => void,
39086
- * }} options
39087
- * @returns {{ input: HTMLInputElement, open: () => void, destroy: () => void }}
39088
- */
39089
- function createImageFilePicker({ ownerDocument = document, onPick, onError } = {}) {
39090
- const input = ownerDocument.createElement("input");
39091
- input.type = "file";
39092
- input.accept = IMAGE_PICKER_ACCEPT;
39093
- input.hidden = true;
39094
- input.tabIndex = -1;
39095
- input.setAttribute("data-superdoc-image-picker", "true");
39096
- input.setAttribute("aria-hidden", "true");
39097
- input.addEventListener("change", () => {
39098
- const file = input.files && input.files[0];
39099
- input.value = "";
39100
- if (!file) return;
39101
- if (!isSupportedImageFile(file)) {
39102
- onError?.(/* @__PURE__ */ new Error(`unsupported image type: ${file.type || file.name}`));
39103
- return;
39104
- }
39105
- try {
39106
- const result = onPick?.(file);
39107
- if (result && typeof result.then === "function") result.catch((error) => onError?.(error));
39108
- } catch (error) {
39109
- onError?.(error);
39110
- }
39111
- });
39112
- ownerDocument.body?.appendChild(input);
39113
- return {
39114
- input,
39115
- open() {
39116
- input.click();
39117
- },
39118
- destroy() {
39119
- input.remove();
39120
- }
39121
- };
39122
- }
39123
- //#endregion
39124
38989
  //#region src/internal/toolbar/built-in/helpers.js
39125
38990
  var sanitizeNumber = (value, defaultNumber) => {
39126
38991
  let sanitized = value.replace(/[^0-9.]/g, "");
@@ -39311,6 +39176,7 @@ var _sfc_main$18 = {
39311
39176
  handleClick();
39312
39177
  };
39313
39178
  const onEnterKeydown = (event) => {
39179
+ event.preventDefault();
39314
39180
  if (!props.allowEnterPropagation) event.stopPropagation();
39315
39181
  handleOuterEnter(event);
39316
39182
  };
@@ -39503,7 +39369,7 @@ var _sfc_main$18 = {
39503
39369
  };
39504
39370
  }
39505
39371
  };
39506
- var ToolbarButton_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$18, [["__scopeId", "data-v-22817fa7"]]);
39372
+ var ToolbarButton_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$18, [["__scopeId", "data-v-96796456"]]);
39507
39373
  //#endregion
39508
39374
  //#region src/internal/toolbar/built-in/ToolbarSeparator.vue
39509
39375
  var _hoisted_1$13 = {
@@ -39584,7 +39450,7 @@ var _sfc_main$16 = {
39584
39450
  }
39585
39451
  },
39586
39452
  emits: ["update:show", "select"],
39587
- setup(__props, { emit: __emit }) {
39453
+ setup(__props, { expose: __expose, emit: __emit }) {
39588
39454
  const props = __props;
39589
39455
  const emit = __emit;
39590
39456
  const slots = (0, vue.useSlots)();
@@ -39819,6 +39685,7 @@ var _sfc_main$16 = {
39819
39685
  trigger.setAttribute("tabindex", "-1");
39820
39686
  trigger.focus();
39821
39687
  };
39688
+ __expose({ focusTrigger });
39822
39689
  const handleKeyDown = (event) => {
39823
39690
  if (!isOpen.value) return;
39824
39691
  const { key } = event;
@@ -39931,7 +39798,7 @@ var _sfc_main$16 = {
39931
39798
  };
39932
39799
  }
39933
39800
  };
39934
- var ToolbarDropdown_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$16, [["__scopeId", "data-v-764d5fd7"]]);
39801
+ var ToolbarDropdown_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$16, [["__scopeId", "data-v-fa3f6b69"]]);
39935
39802
  //#endregion
39936
39803
  //#region src/internal/toolbar/built-in/toolbar-focus-helpers.js
39937
39804
  var preventEditorFocusTransfer = (event) => {
@@ -39979,6 +39846,7 @@ var _sfc_main$15 = {
39979
39846
  setup(__props, { emit: __emit }) {
39980
39847
  const emit = __emit;
39981
39848
  const props = __props;
39849
+ const dropdown = (0, vue.ref)(null);
39982
39850
  const isOverflowMenuOpened = (0, vue.computed)(() => props.toolbarItem.expand.value);
39983
39851
  const hasOpenDropdown = (0, vue.computed)(() => props.overflowItems.some((item) => item.type === "dropdown" && (0, vue.unref)(item.expand)));
39984
39852
  const overflowToolbarItem = (0, vue.computed)(() => ({
@@ -39994,6 +39862,10 @@ var _sfc_main$15 = {
39994
39862
  emit("close");
39995
39863
  };
39996
39864
  const handleCommand = ({ item, argument, option }) => {
39865
+ if (item.name.value === "watermark") {
39866
+ emit("close");
39867
+ dropdown.value?.focusTrigger();
39868
+ }
39997
39869
  emit("command", {
39998
39870
  item,
39999
39871
  argument,
@@ -40002,6 +39874,8 @@ var _sfc_main$15 = {
40002
39874
  };
40003
39875
  return (_ctx, _cache) => {
40004
39876
  return (0, vue.openBlock)(), (0, vue.createBlock)(ToolbarDropdown_default, {
39877
+ ref_key: "dropdown",
39878
+ ref: dropdown,
40005
39879
  class: "overflow-menu",
40006
39880
  "close-on-escape": !hasOpenDropdown.value,
40007
39881
  "has-open-child": hasOpenDropdown.value,
@@ -40019,7 +39893,10 @@ var _sfc_main$15 = {
40019
39893
  class: ["overflow-menu_items", "sd-toolbar-overflow-menu"]
40020
39894
  })
40021
39895
  }, {
40022
- trigger: (0, vue.withCtx)(() => [(0, vue.createVNode)(ToolbarButton_default, { "toolbar-item": overflowToolbarItem.value }, null, 8, ["toolbar-item"])]),
39896
+ trigger: (0, vue.withCtx)(() => [(0, vue.createVNode)(ToolbarButton_default, {
39897
+ "toolbar-item": overflowToolbarItem.value,
39898
+ "allow-enter-propagation": true
39899
+ }, null, 8, ["toolbar-item"])]),
40023
39900
  menu: (0, vue.withCtx)(() => [(0, vue.createVNode)(ButtonGroup_default, {
40024
39901
  class: "superdoc-toolbar-overflow",
40025
39902
  "toolbar-items": __props.overflowItems,
@@ -40042,7 +39919,7 @@ var _sfc_main$15 = {
40042
39919
  };
40043
39920
  }
40044
39921
  };
40045
- var OverflowMenu_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$15, [["__scopeId", "data-v-5f8ee279"]]);
39922
+ var OverflowMenu_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$15, [["__scopeId", "data-v-1227fcf9"]]);
40046
39923
  //#endregion
40047
39924
  //#region src/internal/toolbar/built-in/font-typeahead.js
40048
39925
  /**
@@ -41680,10 +41557,12 @@ var Toolbar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vu
41680
41557
  setup(__props, { emit: __emit }) {
41681
41558
  const { proxy } = (0, vue.getCurrentInstance)();
41682
41559
  let toolbarKey = (0, vue.ref)(1);
41560
+ const toolbarRoot = (0, vue.ref)(null);
41683
41561
  const toolbarStateVersion = (0, vue.ref)(0);
41684
41562
  const compactSideGroups = (0, vue.ref)(false);
41685
41563
  let containerResizeObserver = null;
41686
41564
  let pendingSelectionCapture = null;
41565
+ let renderedWidth = proxy.$toolbar.getAvailableWidth();
41687
41566
  /**
41688
41567
  * Computed property that determines the font-family to use for toolbar UI surfaces.
41689
41568
  * Retrieves the configured font from the toolbar instance's config and validates it.
@@ -41704,10 +41583,41 @@ var Toolbar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vu
41704
41583
  const updateCompactSideGroups = () => {
41705
41584
  compactSideGroups.value = proxy.$toolbar.getAvailableWidth() <= RESPONSIVE_BREAKPOINTS.lg;
41706
41585
  };
41586
+ const captureFocusedControl = () => {
41587
+ const root = toolbarRoot.value;
41588
+ const ownerDocument = root?.ownerDocument;
41589
+ const focused = ownerDocument?.activeElement;
41590
+ if (!root?.contains(focused)) return null;
41591
+ const control = focused.closest("[data-sd-part=\"toolbar-item\"]") ?? focused.querySelector("[data-sd-part=\"toolbar-item\"]");
41592
+ if (!control || focused !== control && !focused.contains(control)) return null;
41593
+ const itemId = control.querySelector("[data-item]")?.getAttribute("data-item");
41594
+ if (!itemId) return null;
41595
+ return () => {
41596
+ const currentRoot = toolbarRoot.value;
41597
+ if (!currentRoot?.isConnected || focused.isConnected || ownerDocument.activeElement !== ownerDocument.body) return;
41598
+ const markers = [...currentRoot.querySelectorAll("[data-item]")];
41599
+ for (const id of [itemId, "btn-overflow"]) {
41600
+ const target = markers.find((marker) => marker.getAttribute("data-item") === id)?.closest("[data-sd-part=\"toolbar-item\"]");
41601
+ if (target && target.getAttribute("aria-disabled") !== "true") {
41602
+ target.focus({ preventScroll: true });
41603
+ return;
41604
+ }
41605
+ }
41606
+ };
41607
+ };
41608
+ const renderToolbar = async (restoreFocus) => {
41609
+ toolbarKey.value += 1;
41610
+ await (0, vue.nextTick)();
41611
+ restoreFocus?.();
41612
+ };
41707
41613
  const onWindowResized = async () => {
41614
+ const width = proxy.$toolbar.getAvailableWidth();
41615
+ if (width === renderedWidth) return;
41616
+ renderedWidth = width;
41617
+ const restoreFocus = captureFocusedControl();
41708
41618
  await proxy.$toolbar.onToolbarResize();
41709
41619
  updateCompactSideGroups();
41710
- toolbarKey.value += 1;
41620
+ await renderToolbar(restoreFocus);
41711
41621
  };
41712
41622
  const onResizeThrottled = throttle(onWindowResized, 300);
41713
41623
  /**
@@ -41717,12 +41627,16 @@ var Toolbar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vu
41717
41627
  * SuperToolbar emits `toolbar-items-changed` on rebuild; bumping the key re-reads the new items into the DOM.
41718
41628
  */
41719
41629
  const onToolbarItemsChanged = () => {
41720
- toolbarKey.value += 1;
41630
+ const restoreFocus = captureFocusedControl();
41631
+ renderedWidth = proxy.$toolbar.getAvailableWidth();
41632
+ updateCompactSideGroups();
41633
+ renderToolbar(restoreFocus);
41721
41634
  };
41722
41635
  const onToolbarStateChanged = () => {
41723
41636
  toolbarStateVersion.value += 1;
41724
41637
  };
41725
41638
  function teardownListeners() {
41639
+ onResizeThrottled.cancel();
41726
41640
  window.removeEventListener("resize", onResizeThrottled);
41727
41641
  proxy.$toolbar.off?.("toolbar-items-changed", onToolbarItemsChanged);
41728
41642
  proxy.$toolbar.off?.("toolbar-state-change", onToolbarStateChanged);
@@ -41780,6 +41694,8 @@ var Toolbar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vu
41780
41694
  return (_ctx, _cache) => {
41781
41695
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
41782
41696
  class: "superdoc-toolbar",
41697
+ ref_key: "toolbarRoot",
41698
+ ref: toolbarRoot,
41783
41699
  key: (0, vue.unref)(toolbarKey),
41784
41700
  role: "toolbar",
41785
41701
  "aria-label": "Toolbar",
@@ -41836,7 +41752,7 @@ var Toolbar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vu
41836
41752
  ], 32);
41837
41753
  };
41838
41754
  }
41839
- }, [["__scopeId", "data-v-ed19716b"]]);
41755
+ }, [["__scopeId", "data-v-9418cc7d"]]);
41840
41756
  //#endregion
41841
41757
  //#region src/internal/toolbar/built-in/font-options.js
41842
41758
  /**
@@ -43452,6 +43368,15 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons, toolbarTexts, toolbarFonts
43452
43368
  disabled: false,
43453
43369
  attributes: { ariaLabel: "Table of contents" }
43454
43370
  });
43371
+ const watermark = useToolbarItem({
43372
+ type: "button",
43373
+ name: "watermark",
43374
+ icon: toolbarIcons.watermark,
43375
+ tooltip: toolbarTexts.watermark,
43376
+ allowWithoutEditor: true,
43377
+ disabled: true,
43378
+ attributes: { ariaLabel: toolbarTexts.watermark }
43379
+ });
43455
43380
  const tableItem = useToolbarItem({
43456
43381
  type: "dropdown",
43457
43382
  name: "table",
@@ -44157,6 +44082,7 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons, toolbarTexts, toolbarFonts
44157
44082
  link,
44158
44083
  image,
44159
44084
  ...shouldIncludeTableOfContents ? [tableOfContents] : [],
44085
+ ...superToolbar.config?.showWatermarkButton || configuredItemNames?.has("watermark") ? [watermark] : [],
44160
44086
  tableItem,
44161
44087
  tableActionsItem,
44162
44088
  makeSeparator(),
@@ -44263,6 +44189,7 @@ var toolbarTexts = {
44263
44189
  link: "Link",
44264
44190
  image: "Image",
44265
44191
  tableOfContents: "Table of contents",
44192
+ watermark: "Watermark",
44266
44193
  table: "Insert table",
44267
44194
  tableActions: "Table options",
44268
44195
  addRowBefore: "Insert row above",
@@ -44462,7 +44389,8 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
44462
44389
  groups: null,
44463
44390
  customButtons: [],
44464
44391
  showFormattingMarksButton: false,
44465
- showTableOfContentsButton: false
44392
+ showTableOfContentsButton: false,
44393
+ showWatermarkButton: false
44466
44394
  };
44467
44395
  toolbarItems = [];
44468
44396
  overflowItems = [];
@@ -44796,6 +44724,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
44796
44724
  return false;
44797
44725
  }
44798
44726
  #canRunHostAction(name) {
44727
+ if (name === "watermark") return this.ui?.document?.getSnapshot?.().ready === true && typeof this.ui?.watermark?.open === "function";
44799
44728
  if (name === "ruler") return typeof this.superdoc?.toggleRuler === "function";
44800
44729
  if (name === "formattingMarks") return typeof this.superdoc?.toggleFormattingMarks === "function" || typeof this.superdoc?.setShowFormattingMarks === "function";
44801
44730
  return false;
@@ -44974,6 +44903,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
44974
44903
  item.resetDisabled();
44975
44904
  this.#applyHeadlessState(item);
44976
44905
  });
44906
+ this.getToolbarItemByName("overflow")?.setDisabled(!this.overflowItems.some((item) => item.type !== "separator" && !item.disabled.value));
44977
44907
  const copyFormatItem = this.getToolbarItemByName("copyFormat");
44978
44908
  if (copyFormatItem) if (this.snapshot?.copyFormatActive) copyFormatItem.activate();
44979
44909
  else copyFormatItem.deactivate();
@@ -44988,6 +44918,12 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
44988
44918
  }
44989
44919
  #runHostAction(name, argument) {
44990
44920
  const superdoc = this.superdoc;
44921
+ if (name === "watermark") {
44922
+ if (!this.#canRunHostAction(name)) return false;
44923
+ const result = this.ui.watermark.open();
44924
+ if (!result.ok) throw new Error(`[superdoc toolbar] Watermark dialog unavailable: ${result.reason}`);
44925
+ return true;
44926
+ }
44991
44927
  if (name === "ruler") {
44992
44928
  if (typeof superdoc?.toggleRuler === "function") {
44993
44929
  superdoc.toggleRuler();
@@ -45042,10 +44978,10 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45042
44978
  */
45043
44979
  #startImagePick() {
45044
44980
  if ((this.snapshot?.commands?.["image"])?.disabled) return true;
45045
- if (!this.#imagePicker) this.#imagePicker = createImageFilePicker({
44981
+ if (!this.#imagePicker) this.#imagePicker = require_create_super_doc_ui.createImageFilePicker({
45046
44982
  ownerDocument: this.toolbarContainer?.ownerDocument ?? document,
45047
44983
  onPick: async (file) => {
45048
- const src = await resolveImageSrc(file, this.superdoc?.config?.handleImageUpload);
44984
+ const src = await require_create_super_doc_ui.resolveImageSrc(file, this.superdoc?.config?.handleImageUpload);
45049
44985
  executeCommand(this.ui, "image", { src }, {
45050
44986
  firstParty: true,
45051
44987
  onSettled: () => this.updateToolbarState(),
@@ -45750,7 +45686,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45750
45686
  this.config.colors = shuffleArray(this.config.colors);
45751
45687
  this.userColorMap = /* @__PURE__ */ new Map();
45752
45688
  this.colorIndex = 0;
45753
- this.version = "2.18.1-next.5";
45689
+ this.version = "2.19.0-next.2";
45754
45690
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
45755
45691
  this.superdocId = config.superdocId || require_uuid.v4();
45756
45692
  this.colors = this.config.colors ?? [];
@@ -47142,7 +47078,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
47142
47078
  {
47143
47079
  feature: "shell.collaboration",
47144
47080
  status: "supported",
47145
- 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"
47081
+ 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"
47146
47082
  },
47147
47083
  {
47148
47084
  feature: "shell.context-menu",