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

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 (52) hide show
  1. package/dist/chunks/{create-super-doc-ui-C3HDE1IE.es.js → create-super-doc-ui-CSZSAgxc.cjs} +1454 -1
  2. package/dist/chunks/{create-super-doc-ui-Gxk1m05U.cjs → create-super-doc-ui-DMe-GQEe.es.js} +1376 -66
  3. package/dist/chunks/pdf.worker.min-BgWCk_Cj.cjs +4 -0
  4. package/dist/chunks/pdf.worker.min-t-CYmkGJ.es.js +4 -0
  5. package/dist/collaboration-upgrade-engine.cjs +1 -1
  6. package/dist/collaboration-upgrade-engine.es.js +1 -1
  7. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  8. package/dist/document-api/src/contract/metadata-types.d.ts +1 -1
  9. package/dist/document-api/src/contract/operation-definitions.d.ts +18 -0
  10. package/dist/document-api/src/diff/diff.types.d.ts +9 -1
  11. package/dist/document-api/src/diff/index.d.ts +1 -1
  12. package/dist/document-api/src/index.d.ts +4 -3
  13. package/dist/document-api/src/selection/selection.d.ts +6 -2
  14. package/dist/document-api/src/selection/selection.types.d.ts +75 -0
  15. package/dist/document-api/src/types/track-changes.types.d.ts +35 -2
  16. package/dist/document-api/src/validation-primitives.d.ts +1 -1
  17. package/dist/document-api/src/watermarks/watermarks.d.ts +4 -1
  18. package/dist/document-api/src/watermarks/watermarks.types.d.ts +34 -0
  19. package/dist/layout-engine/contracts/src/index.d.ts +15 -0
  20. package/dist/layout-engine/contracts/src/resolved-layout.d.ts +3 -1
  21. package/dist/layout-engine/painters/dom/src/index.d.ts +2 -0
  22. package/dist/layout-engine/painters/dom/src/runs/tab-run.d.ts +1 -1
  23. package/dist/public/ui-react.cjs +1 -1
  24. package/dist/public/ui-react.es.js +1 -1
  25. package/dist/public/ui-vue.cjs +1 -1
  26. package/dist/public/ui-vue.es.js +1 -1
  27. package/dist/public/ui.cjs +1 -1
  28. package/dist/public/ui.es.js +1 -1
  29. package/dist/style.css +618 -102
  30. package/dist/style.layered.css +619 -103
  31. package/dist/superdoc/src/components/surfaces/WatermarkSurface.vue.d.ts +16 -0
  32. package/dist/superdoc/src/components/surfaces/watermark-surface-runtime.d.ts +8 -0
  33. package/dist/superdoc/src/core/SuperDoc.d.ts +8 -7
  34. package/dist/superdoc/src/core/types/index.d.ts +36 -18
  35. package/dist/superdoc/src/internal/toolbar/built-in/OverflowMenu.vue.d.ts +93 -1
  36. package/dist/superdoc/src/internal/toolbar/built-in/Toolbar.vue.d.ts +3 -1
  37. package/dist/superdoc/src/internal/toolbar/built-in/ToolbarDropdown.vue.d.ts +1 -0
  38. package/dist/superdoc/src/internal/toolbar/built-in/toolbarIcons.d.ts +1 -0
  39. package/dist/superdoc/src/internal/toolbar/built-in/toolbarTexts.d.ts +1 -0
  40. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +3 -0
  41. package/dist/superdoc/src/public/ui/types.d.ts +10 -1
  42. package/dist/superdoc/src/public/ui/watermark-controller.d.ts +32 -0
  43. package/dist/superdoc/src/public/ui/watermark-dialog-state.d.ts +66 -0
  44. package/dist/superdoc/src/public/ui/watermark-draft.d.ts +6 -0
  45. package/dist/superdoc/src/public/ui.d.cts +12 -0
  46. package/dist/superdoc/src/public/ui.d.ts +1 -1
  47. package/dist/superdoc.cjs +116 -176
  48. package/dist/superdoc.es.js +114 -174
  49. package/dist-cdn/style.layered.css +1 -1
  50. package/dist-cdn/superdoc.min.css +1 -1
  51. package/dist-cdn/superdoc.min.js +38 -37
  52. package/package.json +3 -2
@@ -1,3 +1,5 @@
1
+ const require__plugin_vue_export_helper = require("./_plugin-vue_export-helper-SDR04tiH.cjs");
2
+ let vue = require("vue");
1
3
  //#region src/internal/diagnostics/interaction-history.ts
2
4
  var recorders = /* @__PURE__ */ new WeakMap();
3
5
  var nextSession = 0;
@@ -534,6 +536,1311 @@ function shallowEqual(a, b) {
534
536
  return true;
535
537
  }
536
538
  //#endregion
539
+ //#region src/internal/toolbar/image-upload.js
540
+ /**
541
+ * Interactive image acquisition for the built-in toolbar (SD-3567).
542
+ *
543
+ * The toolbar image button routes through the shared controller command
544
+ * `image` → `create.image`, which requires a base64 PNG/JPEG data-URI `src`.
545
+ * This module owns the browser file picker and the File → data-URI
546
+ * conversion; the toolbar dispatches the resulting payload through the
547
+ * normal controller command path.
548
+ */
549
+ /** Accept filter for the picker: PNG and JPEG only (V1 parity; OOXML-native formats). */
550
+ var IMAGE_PICKER_ACCEPT = ".png,.jpg,.jpeg,image/png,image/jpeg";
551
+ var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
552
+ "image/png",
553
+ "image/jpeg",
554
+ "image/jpg"
555
+ ]);
556
+ var SUPPORTED_IMAGE_EXTENSIONS = /\.(png|jpe?g)$/i;
557
+ /**
558
+ * Whether a picked/dropped file is an insertable image (PNG/JPEG). Falls back
559
+ * to the file extension when the browser reports no MIME type.
560
+ * @param {File | null | undefined} file
561
+ * @returns {boolean}
562
+ */
563
+ function isSupportedImageFile(file) {
564
+ if (!file) return false;
565
+ const type = typeof file.type === "string" ? file.type.toLowerCase() : "";
566
+ if (type) return SUPPORTED_IMAGE_MIME_TYPES.has(type);
567
+ return SUPPORTED_IMAGE_EXTENSIONS.test(String(file.name ?? ""));
568
+ }
569
+ /**
570
+ * Read a File/Blob into a base64 data URI.
571
+ *
572
+ * Some browsers/OSes deliver picked files with an empty `type`; the picker
573
+ * accepts those by extension, but `readAsDataURL` would then produce an
574
+ * `application/octet-stream` data URI that `create.image` rejects. Rewrite
575
+ * the media type from the filename so extension-accepted files still insert.
576
+ * @param {Blob & { name?: string }} file
577
+ * @returns {Promise<string>}
578
+ */
579
+ function fileToDataUri(file) {
580
+ return new Promise((resolve, reject) => {
581
+ const reader = new FileReader();
582
+ reader.onload = () => resolve(normalizeImageDataUri(String(reader.result), file));
583
+ reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("image-read-failed"));
584
+ reader.readAsDataURL(file);
585
+ });
586
+ }
587
+ /**
588
+ * Ensure a data URI read from a supported file carries a PNG/JPEG media type.
589
+ * Leaves URIs already typed as a supported image (or uninferable ones) alone.
590
+ * @param {string} dataUri
591
+ * @param {(Blob & { name?: string }) | null | undefined} file
592
+ * @returns {string}
593
+ */
594
+ function normalizeImageDataUri(dataUri, file) {
595
+ const match = /^data:([^;,]*);base64,/.exec(dataUri);
596
+ if (!match) return dataUri;
597
+ const mime = match[1].toLowerCase();
598
+ if (SUPPORTED_IMAGE_MIME_TYPES.has(mime)) return dataUri;
599
+ const name = String(file?.name ?? "");
600
+ const inferred = /\.jpe?g$/i.test(name) ? "image/jpeg" : /\.png$/i.test(name) ? "image/png" : null;
601
+ if (!inferred) return dataUri;
602
+ return `data:${inferred};base64,${dataUri.slice(match[0].length)}`;
603
+ }
604
+ /**
605
+ * Resolve the `create.image` src for a picked file. Uses the consumer's
606
+ * `handleImageUpload` callback when configured (V1 contract:
607
+ * `(file: File) => Promise<string>`). A returned non-data URL is fetched and
608
+ * embedded, because `create.image` requires an embedded base64 data URI.
609
+ * @param {File} file
610
+ * @param {((file: File) => Promise<string>) | null | undefined} handleImageUpload
611
+ * @returns {Promise<string>}
612
+ */
613
+ async function resolveImageSrc(file, handleImageUpload) {
614
+ if (typeof handleImageUpload === "function") {
615
+ const result = await handleImageUpload(file);
616
+ if (typeof result !== "string" || result.trim() === "") throw new Error("handleImageUpload returned an empty result");
617
+ if (result.startsWith("data:")) return result;
618
+ return embedRemoteImage(result, file);
619
+ }
620
+ return fileToDataUri(file);
621
+ }
622
+ /**
623
+ * Fetch a remote image URL and embed it as a data URI. Object-store /
624
+ * presigned URLs often respond `application/octet-stream`, so keep a filename
625
+ * hint (URL path, falling back to the originally picked file) for the media
626
+ * type normalization in `fileToDataUri`.
627
+ * @param {string} url
628
+ * @param {File | null | undefined} pickedFile
629
+ * @returns {Promise<string>}
630
+ */
631
+ async function embedRemoteImage(url, pickedFile) {
632
+ const response = await fetch(url);
633
+ if (!response.ok) throw new Error(`image fetch failed: ${response.status}`);
634
+ const blob = await response.blob();
635
+ const name = (/\.(?:png|jpe?g)(?:$|[?#])/i.test(url) ? url.split(/[?#]/)[0] : "") || pickedFile?.name || "";
636
+ return fileToDataUri(name ? Object.assign(blob, { name }) : blob);
637
+ }
638
+ /**
639
+ * Hidden `<input type=file>` picker bound to one toolbar instance. The input
640
+ * stays attached (hidden) so automation and tests can drive it; `destroy()`
641
+ * removes it. Picking a file calls `onPick(file)`; an unsupported type or a
642
+ * failed pick routes through `onError(error)`.
643
+ *
644
+ * @param {{
645
+ * ownerDocument?: Document,
646
+ * onPick?: (file: File) => unknown,
647
+ * onError?: (error: Error) => void,
648
+ * }} options
649
+ * @returns {{ input: HTMLInputElement, open: () => void, destroy: () => void }}
650
+ */
651
+ function createImageFilePicker({ ownerDocument = document, onPick, onError } = {}) {
652
+ const input = ownerDocument.createElement("input");
653
+ input.type = "file";
654
+ input.accept = IMAGE_PICKER_ACCEPT;
655
+ input.hidden = true;
656
+ input.tabIndex = -1;
657
+ input.setAttribute("data-superdoc-image-picker", "true");
658
+ input.setAttribute("aria-hidden", "true");
659
+ input.addEventListener("change", () => {
660
+ const file = input.files && input.files[0];
661
+ input.value = "";
662
+ if (!file) return;
663
+ if (!isSupportedImageFile(file)) {
664
+ onError?.(/* @__PURE__ */ new Error(`unsupported image type: ${file.type || file.name}`));
665
+ return;
666
+ }
667
+ try {
668
+ const result = onPick?.(file);
669
+ if (result && typeof result.then === "function") result.catch((error) => onError?.(error));
670
+ } catch (error) {
671
+ onError?.(error);
672
+ }
673
+ });
674
+ ownerDocument.body?.appendChild(input);
675
+ return {
676
+ input,
677
+ open() {
678
+ input.click();
679
+ },
680
+ destroy() {
681
+ input.remove();
682
+ }
683
+ };
684
+ }
685
+ //#endregion
686
+ //#region src/components/surfaces/WatermarkSurface.vue?vue&type=script&setup=true&lang.ts
687
+ var _hoisted_1 = { class: "sd-watermark__body" };
688
+ var _hoisted_2 = { class: "sd-watermark__settings" };
689
+ var _hoisted_3 = ["disabled"];
690
+ var _hoisted_4 = { class: "sd-watermark__field" };
691
+ var _hoisted_5 = ["value"];
692
+ var _hoisted_6 = ["value"];
693
+ var _hoisted_7 = { class: "sd-watermark__field" };
694
+ var _hoisted_8 = ["value"];
695
+ var _hoisted_9 = ["disabled"];
696
+ var _hoisted_10 = { class: "sd-watermark__selection-list" };
697
+ var _hoisted_11 = [
698
+ "value",
699
+ "checked",
700
+ "onChange"
701
+ ];
702
+ var _hoisted_12 = ["disabled"];
703
+ var _hoisted_13 = {
704
+ key: 0,
705
+ class: "sd-watermark__notice"
706
+ };
707
+ var _hoisted_14 = {
708
+ key: 1,
709
+ class: "sd-watermark__notice"
710
+ };
711
+ var _hoisted_15 = ["disabled"];
712
+ var _hoisted_16 = { class: "sd-watermark__types" };
713
+ var _hoisted_17 = [
714
+ "name",
715
+ "value",
716
+ "checked",
717
+ "onChange"
718
+ ];
719
+ var _hoisted_18 = { class: "sd-watermark__field" };
720
+ var _hoisted_19 = ["value"];
721
+ var _hoisted_20 = {
722
+ class: "sd-watermark__presets",
723
+ "aria-label": "Text presets"
724
+ };
725
+ var _hoisted_21 = ["onClick"];
726
+ var _hoisted_22 = { class: "sd-watermark__font-row" };
727
+ var _hoisted_23 = { class: "sd-watermark__field" };
728
+ var _hoisted_24 = ["value"];
729
+ var _hoisted_25 = ["value"];
730
+ var _hoisted_26 = { class: "sd-watermark__field sd-watermark__size" };
731
+ var _hoisted_27 = ["list", "value"];
732
+ var _hoisted_28 = ["id"];
733
+ var _hoisted_29 = ["value"];
734
+ var _hoisted_30 = { class: "sd-watermark__styles" };
735
+ var _hoisted_31 = ["aria-pressed"];
736
+ var _hoisted_32 = ["aria-pressed"];
737
+ var _hoisted_33 = { class: "sd-watermark__field sd-watermark__font-color" };
738
+ var _hoisted_34 = { class: "sd-watermark__color" };
739
+ var _hoisted_35 = ["value"];
740
+ var _hoisted_36 = ["value"];
741
+ var _hoisted_37 = { class: "sd-watermark__picture" };
742
+ var _hoisted_38 = ["src"];
743
+ var _hoisted_39 = ["accept"];
744
+ var _hoisted_40 = { class: "sd-watermark__field" };
745
+ var _hoisted_41 = ["value"];
746
+ var _hoisted_42 = {
747
+ key: 0,
748
+ value: "custom",
749
+ disabled: ""
750
+ };
751
+ var _hoisted_43 = ["value"];
752
+ var _hoisted_44 = { class: "sd-watermark__check" };
753
+ var _hoisted_45 = ["checked"];
754
+ var _hoisted_46 = { class: "sd-watermark__check" };
755
+ var _hoisted_47 = ["checked"];
756
+ var _hoisted_48 = {
757
+ key: 2,
758
+ class: "sd-watermark__field sd-watermark__transparency"
759
+ };
760
+ var _hoisted_49 = ["for"];
761
+ var _hoisted_50 = { class: "sd-watermark__range" };
762
+ var _hoisted_51 = ["id", "value"];
763
+ var _hoisted_52 = ["value"];
764
+ var _hoisted_53 = {
765
+ key: 3,
766
+ class: "sd-watermark__orientation"
767
+ };
768
+ var _hoisted_54 = [
769
+ "name",
770
+ "value",
771
+ "checked",
772
+ "onChange"
773
+ ];
774
+ var _hoisted_55 = {
775
+ key: 4,
776
+ class: "sd-watermark__notice"
777
+ };
778
+ var _hoisted_56 = {
779
+ key: 5,
780
+ class: "sd-watermark__notice"
781
+ };
782
+ var _hoisted_57 = ["aria-busy"];
783
+ var _hoisted_58 = { class: "sd-watermark__page" };
784
+ var _hoisted_59 = [
785
+ "src",
786
+ "width",
787
+ "height"
788
+ ];
789
+ var _hoisted_60 = { key: 1 };
790
+ var _hoisted_61 = { class: "sd-watermark__impact" };
791
+ var _hoisted_62 = {
792
+ key: 0,
793
+ class: "sd-watermark__impact-details"
794
+ };
795
+ var _hoisted_63 = {
796
+ key: 0,
797
+ class: "sd-watermark__error",
798
+ role: "alert"
799
+ };
800
+ var _hoisted_64 = ["disabled"];
801
+ var _hoisted_65 = { class: "sd-watermark__footer sd-dialog-footer" };
802
+ var _hoisted_66 = {
803
+ role: "status",
804
+ "aria-live": "polite"
805
+ };
806
+ var _hoisted_67 = { class: "sd-watermark__actions" };
807
+ var _hoisted_68 = ["disabled"];
808
+ var WatermarkSurface_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.defineComponent)({
809
+ __name: "WatermarkSurface",
810
+ props: {
811
+ model: {},
812
+ surfaceId: { default: "" },
813
+ mode: {},
814
+ request: {},
815
+ resolve: {},
816
+ close: {
817
+ type: Function,
818
+ default: () => {}
819
+ }
820
+ },
821
+ setup(__props) {
822
+ const props = __props;
823
+ const snapshot = (0, vue.shallowRef)(props.model.getSnapshot());
824
+ const fileInput = (0, vue.ref)(null);
825
+ const filePending = (0, vue.ref)(false);
826
+ const localError = (0, vue.ref)("");
827
+ let fileGeneration = 0;
828
+ let unsubscribe;
829
+ const presets = [
830
+ "DRAFT",
831
+ "CONFIDENTIAL",
832
+ "DO NOT COPY"
833
+ ];
834
+ const types = [
835
+ {
836
+ value: "none",
837
+ label: "No watermark"
838
+ },
839
+ {
840
+ value: "text",
841
+ label: "Text"
842
+ },
843
+ {
844
+ value: "picture",
845
+ label: "Picture"
846
+ }
847
+ ];
848
+ const busy = (0, vue.computed)(() => snapshot.value.phase === "loading" || snapshot.value.phase === "applying");
849
+ const editable = (0, vue.computed)(() => !busy.value && !snapshot.value.readonlyReason);
850
+ const canApply = (0, vue.computed)(() => snapshot.value.canApply && !busy.value && !filePending.value && !snapshot.value.stale);
851
+ const error = (0, vue.computed)(() => localError.value || snapshot.value.error);
852
+ const status = (0, vue.computed)(() => {
853
+ if (filePending.value) return "Preparing picture…";
854
+ if (snapshot.value.phase === "loading") return "Loading watermarks…";
855
+ if (snapshot.value.phase === "applying") return "Applying changes; closing will not cancel them.";
856
+ if (snapshot.value.phase === "previewing") return "Updating preview…";
857
+ return snapshot.value.dirty ? "Changes apply when you choose Apply" : "No changes";
858
+ });
859
+ const fontOptions = (0, vue.computed)(() => {
860
+ const { fonts, draft } = snapshot.value;
861
+ return fonts.some((font) => font.value === draft.fontFamily) ? fonts : [{
862
+ value: draft.fontFamily,
863
+ label: draft.fontFamily
864
+ }, ...fonts];
865
+ });
866
+ function cancelPreparation() {
867
+ fileGeneration += 1;
868
+ filePending.value = false;
869
+ }
870
+ function patch(patch) {
871
+ if (!editable.value) return;
872
+ localError.value = "";
873
+ props.model.patchDraft(patch);
874
+ }
875
+ function changeScope(event, field) {
876
+ const value = event.target.value;
877
+ cancelPreparation();
878
+ props.model.setScope(field === "section" ? value || null : snapshot.value.sectionId, field === "variant" ? value : snapshot.value.variant);
879
+ }
880
+ function selectWatermark(id, event) {
881
+ cancelPreparation();
882
+ const selected = new Set(snapshot.value.selectedIds);
883
+ if (event.target.checked) selected.add(id);
884
+ else selected.delete(id);
885
+ props.model.selectWatermarks([...selected]);
886
+ }
887
+ async function choosePicture(event) {
888
+ const input = event.target;
889
+ const file = input.files?.[0];
890
+ input.value = "";
891
+ if (!file || !editable.value) return;
892
+ cancelPreparation();
893
+ localError.value = "";
894
+ if (!isSupportedImageFile(file)) {
895
+ localError.value = "Choose a PNG or JPEG picture.";
896
+ return;
897
+ }
898
+ const generation = fileGeneration;
899
+ filePending.value = true;
900
+ try {
901
+ const src = await fileToDataUri(file);
902
+ if (generation !== fileGeneration) return;
903
+ patch({
904
+ src,
905
+ pictureName: file.name,
906
+ kind: "picture"
907
+ });
908
+ } catch {
909
+ if (generation === fileGeneration) localError.value = "The picture could not be read. Choose it again.";
910
+ } finally {
911
+ if (generation === fileGeneration) filePending.value = false;
912
+ }
913
+ }
914
+ async function apply() {
915
+ if (!canApply.value) return;
916
+ try {
917
+ await props.model.apply();
918
+ } catch {
919
+ localError.value = "The watermark could not be applied. Try again.";
920
+ }
921
+ }
922
+ async function reload() {
923
+ cancelPreparation();
924
+ localError.value = "";
925
+ try {
926
+ await props.model.reload();
927
+ } catch {
928
+ localError.value = "Watermarks could not be loaded. Try again.";
929
+ }
930
+ }
931
+ function close() {
932
+ cancelPreparation();
933
+ props.close("user-cancelled");
934
+ }
935
+ (0, vue.watch)([
936
+ () => snapshot.value.draft.kind,
937
+ () => snapshot.value.sectionId,
938
+ () => snapshot.value.variant,
939
+ () => snapshot.value.selectedIds.join("\0")
940
+ ], cancelPreparation);
941
+ (0, vue.onMounted)(() => {
942
+ unsubscribe = props.model.subscribe((value) => {
943
+ snapshot.value = value;
944
+ });
945
+ snapshot.value = props.model.getSnapshot();
946
+ });
947
+ (0, vue.onBeforeUnmount)(() => {
948
+ cancelPreparation();
949
+ unsubscribe?.();
950
+ });
951
+ return (_ctx, _cache) => {
952
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("form", {
953
+ class: "sd-watermark sd-dialog-content",
954
+ onSubmit: (0, vue.withModifiers)(apply, ["prevent"])
955
+ }, [
956
+ (0, vue.createElementVNode)("header", { class: "sd-watermark__header sd-dialog-header" }, [_cache[17] || (_cache[17] = (0, vue.createElementVNode)("h2", null, "Watermark", -1)), (0, vue.createElementVNode)("button", {
957
+ type: "button",
958
+ class: "sd-dialog-close",
959
+ "aria-label": "Close watermark dialog",
960
+ onClick: close
961
+ }, [..._cache[16] || (_cache[16] = [(0, vue.createElementVNode)("svg", {
962
+ width: "16",
963
+ height: "16",
964
+ viewBox: "0 0 16 16",
965
+ "aria-hidden": "true"
966
+ }, [(0, vue.createElementVNode)("path", {
967
+ d: "m4 4 8 8m0-8-8 8",
968
+ fill: "none",
969
+ stroke: "currentColor",
970
+ "stroke-width": "1.5",
971
+ "stroke-linecap": "round"
972
+ })], -1)])])]),
973
+ (0, vue.createElementVNode)("div", _hoisted_1, [(0, vue.createElementVNode)("div", _hoisted_2, [
974
+ (0, vue.createElementVNode)("fieldset", {
975
+ class: "sd-watermark__scope",
976
+ disabled: busy.value
977
+ }, [
978
+ _cache[22] || (_cache[22] = (0, vue.createElementVNode)("legend", { class: "sd-watermark__sr-only" }, "Watermark scope", -1)),
979
+ (0, vue.createElementVNode)("label", _hoisted_4, [_cache[19] || (_cache[19] = (0, vue.createElementVNode)("span", null, "Apply to", -1)), (0, vue.createElementVNode)("select", {
980
+ "aria-label": "Apply to",
981
+ value: snapshot.value.sectionId ?? "",
982
+ onChange: _cache[0] || (_cache[0] = ($event) => changeScope($event, "section"))
983
+ }, [_cache[18] || (_cache[18] = (0, vue.createElementVNode)("option", { value: "" }, "Entire document", -1)), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(snapshot.value.sections, (section) => {
984
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
985
+ key: section.id,
986
+ value: section.id
987
+ }, (0, vue.toDisplayString)(section.label), 9, _hoisted_6);
988
+ }), 128))], 40, _hoisted_5)]),
989
+ (0, vue.createElementVNode)("label", _hoisted_7, [_cache[21] || (_cache[21] = (0, vue.createElementVNode)("span", null, "Page type", -1)), (0, vue.createElementVNode)("select", {
990
+ "aria-label": "Page type",
991
+ value: snapshot.value.variant,
992
+ onChange: _cache[1] || (_cache[1] = ($event) => changeScope($event, "variant"))
993
+ }, [..._cache[20] || (_cache[20] = [
994
+ (0, vue.createElementVNode)("option", { value: "all" }, "All applicable", -1),
995
+ (0, vue.createElementVNode)("option", { value: "default" }, "Default", -1),
996
+ (0, vue.createElementVNode)("option", { value: "first" }, "First", -1),
997
+ (0, vue.createElementVNode)("option", { value: "even" }, "Even", -1)
998
+ ])], 40, _hoisted_8)])
999
+ ], 8, _hoisted_3),
1000
+ snapshot.value.items.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("fieldset", {
1001
+ key: 0,
1002
+ class: "sd-watermark__selection",
1003
+ disabled: busy.value
1004
+ }, [
1005
+ _cache[23] || (_cache[23] = (0, vue.createElementVNode)("legend", null, "Watermarks to edit", -1)),
1006
+ (0, vue.createElementVNode)("div", _hoisted_10, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(snapshot.value.items, (item) => {
1007
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("label", {
1008
+ key: item.id,
1009
+ class: "sd-watermark__check"
1010
+ }, [(0, vue.createElementVNode)("input", {
1011
+ type: "checkbox",
1012
+ value: item.id,
1013
+ checked: snapshot.value.selectedIds.includes(item.id),
1014
+ onChange: ($event) => selectWatermark(item.id, $event)
1015
+ }, null, 40, _hoisted_11), (0, vue.createElementVNode)("span", null, [(0, vue.createTextVNode)((0, vue.toDisplayString)(item.label), 1), (0, vue.createElementVNode)("small", null, (0, vue.toDisplayString)(item.locations), 1)])]);
1016
+ }), 128))]),
1017
+ (0, vue.createElementVNode)("button", {
1018
+ type: "button",
1019
+ disabled: !editable.value,
1020
+ onClick: _cache[2] || (_cache[2] = ($event) => __props.model.selectWatermarks([]))
1021
+ }, "Add watermark", 8, _hoisted_12),
1022
+ snapshot.value.selectedIds.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_13, " Applying uses the settings below for all " + (0, vue.toDisplayString)(snapshot.value.selectedIds.length) + " selected copies. ", 1)) : (0, vue.createCommentVNode)("", true)
1023
+ ], 8, _hoisted_9)) : (0, vue.createCommentVNode)("", true),
1024
+ snapshot.value.readonlyReason ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_14, (0, vue.toDisplayString)(snapshot.value.readonlyReason), 1)) : (0, vue.createCommentVNode)("", true),
1025
+ (0, vue.createElementVNode)("fieldset", {
1026
+ class: (0, vue.normalizeClass)(["sd-watermark__draft", { "sd-watermark__draft--text": snapshot.value.draft.kind === "text" }]),
1027
+ disabled: !editable.value
1028
+ }, [
1029
+ _cache[40] || (_cache[40] = (0, vue.createElementVNode)("legend", { class: "sd-watermark__sr-only" }, "Watermark settings", -1)),
1030
+ (0, vue.createElementVNode)("fieldset", _hoisted_16, [_cache[24] || (_cache[24] = (0, vue.createElementVNode)("legend", { class: "sd-watermark__sr-only" }, "Watermark type", -1)), ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(types, (type) => {
1031
+ return (0, vue.createElementVNode)("label", {
1032
+ key: type.value,
1033
+ class: (0, vue.normalizeClass)({ "is-selected": snapshot.value.draft.kind === type.value })
1034
+ }, [(0, vue.createElementVNode)("input", {
1035
+ type: "radio",
1036
+ name: `watermark-type-${__props.surfaceId}`,
1037
+ value: type.value,
1038
+ checked: snapshot.value.draft.kind === type.value,
1039
+ onChange: ($event) => patch({ kind: type.value })
1040
+ }, null, 40, _hoisted_17), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(type.value === "none" && snapshot.value.items.length > 1 && snapshot.value.selectedIds.length ? "Remove selected" : type.label), 1)], 2);
1041
+ }), 64))]),
1042
+ snapshot.value.draft.kind === "text" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [
1043
+ (0, vue.createElementVNode)("label", _hoisted_18, [_cache[25] || (_cache[25] = (0, vue.createElementVNode)("span", null, "Text", -1)), (0, vue.createElementVNode)("input", {
1044
+ "aria-label": "Watermark text",
1045
+ type: "text",
1046
+ value: snapshot.value.draft.text,
1047
+ onInput: _cache[3] || (_cache[3] = ($event) => patch({ text: $event.target.value }))
1048
+ }, null, 40, _hoisted_19)]),
1049
+ (0, vue.createElementVNode)("div", _hoisted_20, [((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(presets, (preset) => {
1050
+ return (0, vue.createElementVNode)("button", {
1051
+ key: preset,
1052
+ type: "button",
1053
+ onClick: ($event) => patch({ text: preset })
1054
+ }, (0, vue.toDisplayString)(preset), 9, _hoisted_21);
1055
+ }), 64))]),
1056
+ (0, vue.createElementVNode)("div", _hoisted_22, [
1057
+ (0, vue.createElementVNode)("label", _hoisted_23, [_cache[26] || (_cache[26] = (0, vue.createElementVNode)("span", null, "Font", -1)), (0, vue.createElementVNode)("select", {
1058
+ "aria-label": "Font",
1059
+ value: snapshot.value.draft.fontFamily,
1060
+ onChange: _cache[4] || (_cache[4] = ($event) => patch({ fontFamily: $event.target.value }))
1061
+ }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(fontOptions.value, (font) => {
1062
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
1063
+ key: font.value,
1064
+ value: font.value
1065
+ }, (0, vue.toDisplayString)(font.label), 9, _hoisted_25);
1066
+ }), 128))], 40, _hoisted_24)]),
1067
+ (0, vue.createElementVNode)("label", _hoisted_26, [
1068
+ _cache[28] || (_cache[28] = (0, vue.createElementVNode)("span", null, "Size (pt)", -1)),
1069
+ (0, vue.createElementVNode)("input", {
1070
+ "aria-label": "Font size",
1071
+ list: `watermark-sizes-${__props.surfaceId}`,
1072
+ value: snapshot.value.draft.fontSize === "auto" ? "Auto" : snapshot.value.draft.fontSize,
1073
+ onChange: _cache[5] || (_cache[5] = ($event) => patch({ fontSize: $event.target.value.toLowerCase() === "auto" ? "auto" : Number($event.target.value) }))
1074
+ }, null, 40, _hoisted_27),
1075
+ (0, vue.createElementVNode)("datalist", { id: `watermark-sizes-${__props.surfaceId}` }, [_cache[27] || (_cache[27] = (0, vue.createElementVNode)("option", { value: "Auto" }, null, -1)), ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)([
1076
+ 24,
1077
+ 36,
1078
+ 48,
1079
+ 72,
1080
+ 96
1081
+ ], (size) => {
1082
+ return (0, vue.createElementVNode)("option", {
1083
+ key: size,
1084
+ value: size
1085
+ }, null, 8, _hoisted_29);
1086
+ }), 64))], 8, _hoisted_28)
1087
+ ]),
1088
+ (0, vue.createElementVNode)("div", _hoisted_30, [(0, vue.createElementVNode)("button", {
1089
+ type: "button",
1090
+ "aria-label": "Bold",
1091
+ "aria-pressed": snapshot.value.draft.bold,
1092
+ onClick: _cache[6] || (_cache[6] = ($event) => patch({ bold: !snapshot.value.draft.bold }))
1093
+ }, [..._cache[29] || (_cache[29] = [(0, vue.createElementVNode)("b", null, "B", -1)])], 8, _hoisted_31), (0, vue.createElementVNode)("button", {
1094
+ type: "button",
1095
+ "aria-label": "Italic",
1096
+ "aria-pressed": snapshot.value.draft.italic,
1097
+ onClick: _cache[7] || (_cache[7] = ($event) => patch({ italic: !snapshot.value.draft.italic }))
1098
+ }, [..._cache[30] || (_cache[30] = [(0, vue.createElementVNode)("i", null, "I", -1)])], 8, _hoisted_32)])
1099
+ ]),
1100
+ (0, vue.createElementVNode)("label", _hoisted_33, [_cache[31] || (_cache[31] = (0, vue.createElementVNode)("span", null, "Font color", -1)), (0, vue.createElementVNode)("span", _hoisted_34, [(0, vue.createElementVNode)("input", {
1101
+ type: "color",
1102
+ "aria-label": "Choose font color",
1103
+ value: snapshot.value.draft.color,
1104
+ onInput: _cache[8] || (_cache[8] = ($event) => patch({ color: $event.target.value }))
1105
+ }, null, 40, _hoisted_35), (0, vue.createElementVNode)("input", {
1106
+ type: "text",
1107
+ "aria-label": "Font color",
1108
+ value: snapshot.value.draft.color,
1109
+ spellcheck: "false",
1110
+ onInput: _cache[9] || (_cache[9] = ($event) => patch({ color: $event.target.value }))
1111
+ }, null, 40, _hoisted_36)])])
1112
+ ], 64)) : (0, vue.createCommentVNode)("", true),
1113
+ snapshot.value.draft.kind === "picture" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
1114
+ (0, vue.createElementVNode)("div", _hoisted_37, [
1115
+ snapshot.value.draft.src ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
1116
+ key: 0,
1117
+ src: snapshot.value.draft.src,
1118
+ alt: "Selected picture"
1119
+ }, null, 8, _hoisted_38)) : (0, vue.createCommentVNode)("", true),
1120
+ (0, vue.createElementVNode)("div", null, [(0, vue.createElementVNode)("strong", null, (0, vue.toDisplayString)(snapshot.value.draft.pictureName || "No picture selected"), 1), _cache[32] || (_cache[32] = (0, vue.createElementVNode)("small", null, "PNG or JPEG", -1))]),
1121
+ (0, vue.createElementVNode)("button", {
1122
+ type: "button",
1123
+ onClick: _cache[10] || (_cache[10] = ($event) => fileInput.value?.click())
1124
+ }, (0, vue.toDisplayString)(snapshot.value.draft.pictureName ? "Replace picture" : "Choose picture"), 1),
1125
+ (0, vue.createElementVNode)("input", {
1126
+ ref_key: "fileInput",
1127
+ ref: fileInput,
1128
+ type: "file",
1129
+ accept: (0, vue.unref)(IMAGE_PICKER_ACCEPT),
1130
+ "aria-label": "Choose watermark picture",
1131
+ hidden: "",
1132
+ onChange: choosePicture
1133
+ }, null, 40, _hoisted_39)
1134
+ ]),
1135
+ (0, vue.createElementVNode)("label", _hoisted_40, [_cache[34] || (_cache[34] = (0, vue.createElementVNode)("span", null, "Scale", -1)), (0, vue.createElementVNode)("select", {
1136
+ "aria-label": "Picture scale",
1137
+ value: snapshot.value.draft.customSize ? "custom" : snapshot.value.draft.scalePercent,
1138
+ onChange: _cache[11] || (_cache[11] = ($event) => patch({ scalePercent: $event.target.value === "auto" ? "auto" : Number($event.target.value) }))
1139
+ }, [
1140
+ snapshot.value.draft.customSize ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_42, "Custom size retained")) : (0, vue.createCommentVNode)("", true),
1141
+ _cache[33] || (_cache[33] = (0, vue.createElementVNode)("option", { value: "auto" }, "Auto", -1)),
1142
+ ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)([
1143
+ 50,
1144
+ 100,
1145
+ 150,
1146
+ 200,
1147
+ 500
1148
+ ], (scale) => {
1149
+ return (0, vue.createElementVNode)("option", {
1150
+ key: scale,
1151
+ value: scale
1152
+ }, (0, vue.toDisplayString)(scale) + "%", 9, _hoisted_43);
1153
+ }), 64))
1154
+ ], 40, _hoisted_41)]),
1155
+ (0, vue.createElementVNode)("label", _hoisted_44, [(0, vue.createElementVNode)("input", {
1156
+ type: "checkbox",
1157
+ checked: snapshot.value.draft.lockAspectRatio,
1158
+ onChange: _cache[12] || (_cache[12] = ($event) => patch({ lockAspectRatio: $event.target.checked }))
1159
+ }, null, 40, _hoisted_45), _cache[35] || (_cache[35] = (0, vue.createElementVNode)("span", null, "Preserve aspect ratio", -1))]),
1160
+ (0, vue.createElementVNode)("label", _hoisted_46, [(0, vue.createElementVNode)("input", {
1161
+ type: "checkbox",
1162
+ checked: snapshot.value.draft.washout,
1163
+ onChange: _cache[13] || (_cache[13] = ($event) => patch({ washout: $event.target.checked }))
1164
+ }, null, 40, _hoisted_47), _cache[36] || (_cache[36] = (0, vue.createElementVNode)("span", null, [(0, vue.createTextVNode)("Washout"), (0, vue.createElementVNode)("small", null, "Lighten the picture's colors.")], -1))])
1165
+ ], 64)) : (0, vue.createCommentVNode)("", true),
1166
+ snapshot.value.draft.kind !== "none" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [
1167
+ (0, vue.createElementVNode)("label", { for: `watermark-transparency-${__props.surfaceId}` }, "Transparency", 8, _hoisted_49),
1168
+ (0, vue.createElementVNode)("div", _hoisted_50, [(0, vue.createElementVNode)("input", {
1169
+ id: `watermark-transparency-${__props.surfaceId}`,
1170
+ type: "range",
1171
+ min: "0",
1172
+ max: "100",
1173
+ step: "1",
1174
+ "aria-label": "Transparency",
1175
+ value: snapshot.value.draft.transparency,
1176
+ onInput: _cache[14] || (_cache[14] = ($event) => patch({ transparency: Number($event.target.value) }))
1177
+ }, null, 40, _hoisted_51), (0, vue.createElementVNode)("label", null, [(0, vue.createElementVNode)("input", {
1178
+ type: "number",
1179
+ min: "0",
1180
+ max: "100",
1181
+ step: "any",
1182
+ "aria-label": "Transparency percentage",
1183
+ value: snapshot.value.draft.transparency,
1184
+ onInput: _cache[15] || (_cache[15] = ($event) => patch({ transparency: Number($event.target.value) }))
1185
+ }, null, 40, _hoisted_52), _cache[37] || (_cache[37] = (0, vue.createElementVNode)("span", null, "%", -1))])]),
1186
+ _cache[38] || (_cache[38] = (0, vue.createElementVNode)("small", null, "0% opaque, 100% transparent", -1))
1187
+ ])) : (0, vue.createCommentVNode)("", true),
1188
+ snapshot.value.draft.kind !== "none" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("fieldset", _hoisted_53, [_cache[39] || (_cache[39] = (0, vue.createElementVNode)("legend", null, "Orientation", -1)), ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(["horizontal", "diagonal"], (orientation) => {
1189
+ return (0, vue.createElementVNode)("label", { key: orientation }, [(0, vue.createElementVNode)("input", {
1190
+ type: "radio",
1191
+ name: `watermark-orientation-${__props.surfaceId}`,
1192
+ value: orientation,
1193
+ checked: !snapshot.value.draft.customOrientation && snapshot.value.draft.orientation === orientation,
1194
+ onChange: ($event) => patch({ orientation })
1195
+ }, null, 40, _hoisted_54), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(orientation === "horizontal" ? "Horizontal" : "Diagonal"), 1)]);
1196
+ }), 64))])) : (0, vue.createCommentVNode)("", true),
1197
+ snapshot.value.draft.customPlacement ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_55, "Custom placement retained.")) : (0, vue.createCommentVNode)("", true),
1198
+ snapshot.value.draft.kind === "none" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_56, (0, vue.toDisplayString)(snapshot.value.selectedIds.length ? "The selected watermark will be removed when you apply." : "Choose Text or Picture to add a watermark."), 1)) : (0, vue.createCommentVNode)("", true)
1199
+ ], 10, _hoisted_15)
1200
+ ]), (0, vue.createElementVNode)("aside", {
1201
+ class: "sd-watermark__preview",
1202
+ "aria-label": "Watermark preview",
1203
+ "aria-busy": snapshot.value.phase === "previewing"
1204
+ }, [
1205
+ _cache[41] || (_cache[41] = (0, vue.createElementVNode)("h3", null, "Preview", -1)),
1206
+ (0, vue.createElementVNode)("div", _hoisted_58, [snapshot.value.preview ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
1207
+ key: 0,
1208
+ src: snapshot.value.preview.url,
1209
+ width: snapshot.value.preview.width,
1210
+ height: snapshot.value.preview.height,
1211
+ alt: "Watermark on a sample page"
1212
+ }, null, 8, _hoisted_59)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_60, (0, vue.toDisplayString)(snapshot.value.phase === "loading" || snapshot.value.phase === "previewing" ? "Preparing preview…" : snapshot.value.draft.kind === "none" ? "No watermark in this preview" : "Preview unavailable"), 1))]),
1213
+ _cache[42] || (_cache[42] = (0, vue.createElementVNode)("p", { class: "sd-watermark__caption" }, "Preview at the selected page size", -1)),
1214
+ (0, vue.createElementVNode)("p", _hoisted_61, (0, vue.toDisplayString)(snapshot.value.impact), 1),
1215
+ snapshot.value.impactDetails?.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("details", _hoisted_62, [(0, vue.createElementVNode)("summary", null, "View " + (0, vue.toDisplayString)(snapshot.value.impactDetails.length) + " affected locations", 1), (0, vue.createElementVNode)("ul", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(snapshot.value.impactDetails, (location) => {
1216
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("li", { key: location }, (0, vue.toDisplayString)(location), 1);
1217
+ }), 128))])])) : (0, vue.createCommentVNode)("", true)
1218
+ ], 8, _hoisted_57)]),
1219
+ error.value || snapshot.value.stale ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_63, [(0, vue.createElementVNode)("p", null, (0, vue.toDisplayString)(error.value || "The document changed. Review the latest state before applying."), 1), snapshot.value.stale || snapshot.value.error ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1220
+ key: 0,
1221
+ type: "button",
1222
+ disabled: busy.value,
1223
+ onClick: reload
1224
+ }, " Review latest state ", 8, _hoisted_64)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true),
1225
+ (0, vue.createElementVNode)("footer", _hoisted_65, [(0, vue.createElementVNode)("p", _hoisted_66, (0, vue.toDisplayString)(status.value), 1), (0, vue.createElementVNode)("div", _hoisted_67, [(0, vue.createElementVNode)("button", {
1226
+ type: "button",
1227
+ class: "sd-dialog-secondary",
1228
+ onClick: close
1229
+ }, "Cancel"), (0, vue.createElementVNode)("button", {
1230
+ type: "submit",
1231
+ class: "sd-watermark__apply sd-dialog-primary",
1232
+ disabled: !canApply.value
1233
+ }, (0, vue.toDisplayString)(snapshot.value.phase === "applying" ? "Applying…" : snapshot.value.draft.kind === "none" ? snapshot.value.items.length > 1 ? "Remove selected watermark" : "Remove watermark" : "Apply watermark"), 9, _hoisted_68)])])
1234
+ ], 32);
1235
+ };
1236
+ }
1237
+ });
1238
+ //#endregion
1239
+ //#region src/components/surfaces/WatermarkSurface.vue
1240
+ var WatermarkSurface_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(WatermarkSurface_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-df56c82c"]]);
1241
+ //#endregion
1242
+ //#region src/components/surfaces/watermark-surface-runtime.js
1243
+ /**
1244
+ * Keeps Vue rendering and post-dismissal announcements outside the public type graph.
1245
+ * @param {(() => HTMLElement | null) | undefined} getContainer
1246
+ */
1247
+ function createWatermarkSurfaceRuntime(getContainer) {
1248
+ /** @type {HTMLDivElement | null} */
1249
+ let status = null;
1250
+ let disposed = false;
1251
+ return {
1252
+ component: WatermarkSurface_default,
1253
+ captureFocusReturn() {
1254
+ const document = getContainer?.()?.ownerDocument;
1255
+ const opener = document?.activeElement;
1256
+ const item = opener?.closest("[data-sd-part=\"toolbar-item\"]") ?? opener?.querySelector("[data-sd-part=\"toolbar-item\"]");
1257
+ const container = (item?.closest("[data-sd-part=\"toolbar\"]"))?.parentElement;
1258
+ const isWatermarkEntry = item?.querySelector("[data-item=\"btn-watermark\"], [data-item=\"btn-overflow\"]");
1259
+ /** @param {() => boolean} isCurrent */
1260
+ return async (isCurrent) => {
1261
+ await (0, vue.nextTick)();
1262
+ if (disposed || !isCurrent() || !isWatermarkEntry || opener?.isConnected || !container?.isConnected) return;
1263
+ if (document?.activeElement !== document?.body) return;
1264
+ const currentToolbar = container.querySelector("[data-sd-part=\"toolbar\"]");
1265
+ for (const name of ["watermark", "overflow"]) {
1266
+ const target = currentToolbar?.querySelector(`[data-item="btn-${name}"]`)?.closest("[data-sd-part=\"toolbar-item\"]");
1267
+ if (target instanceof HTMLElement && target.getClientRects().length && target.getAttribute("aria-disabled") !== "true") {
1268
+ target.focus({ preventScroll: true });
1269
+ return;
1270
+ }
1271
+ }
1272
+ };
1273
+ },
1274
+ mount() {
1275
+ const container = getContainer?.();
1276
+ if (disposed || status || !container?.ownerDocument) return;
1277
+ status = container.ownerDocument.createElement("div");
1278
+ status.setAttribute("role", "status");
1279
+ status.setAttribute("aria-live", "polite");
1280
+ status.setAttribute("aria-atomic", "true");
1281
+ Object.assign(status.style, {
1282
+ position: "absolute",
1283
+ width: "1px",
1284
+ height: "1px",
1285
+ overflow: "hidden",
1286
+ clipPath: "inset(50%)",
1287
+ whiteSpace: "nowrap"
1288
+ });
1289
+ container.appendChild(status);
1290
+ },
1291
+ /** @param {string} message */
1292
+ announce(message) {
1293
+ if (!disposed && status) status.textContent = message;
1294
+ },
1295
+ destroy() {
1296
+ disposed = true;
1297
+ status?.remove();
1298
+ status = null;
1299
+ }
1300
+ };
1301
+ }
1302
+ //#endregion
1303
+ //#region src/public/ui/watermark-draft.ts
1304
+ function placementState(placement) {
1305
+ const customOrientation = placement?.rotationDegrees != null && ![0, 315].includes(placement.rotationDegrees);
1306
+ return {
1307
+ customOrientation,
1308
+ customPlacement: Boolean(customOrientation || placement?.behindText === false || placement?.horizontal && (placement.horizontal.alignment !== "center" || placement.horizontal.offsetPt) || placement?.vertical && (placement.vertical.alignment !== "center" || placement.vertical.offsetPt))
1309
+ };
1310
+ }
1311
+ function createWatermarkDraft(item) {
1312
+ const value = item?.watermark;
1313
+ const placement = value?.placement ? structuredClone(value.placement) : void 0;
1314
+ const picture = value?.kind === "picture" ? value : void 0;
1315
+ const text = value?.kind === "text" ? value : void 0;
1316
+ const rotation = placement?.rotationDegrees ?? (picture ? 0 : text?.orientation === "horizontal" ? 0 : 315);
1317
+ return {
1318
+ kind: value?.kind ?? "none",
1319
+ geometryKind: value?.kind ?? null,
1320
+ text: text?.text ?? "DRAFT",
1321
+ fontFamily: text?.fontFamily ?? "Calibri",
1322
+ fontSize: text?.fontSizePt ?? "auto",
1323
+ bold: text?.bold ?? false,
1324
+ italic: text?.italic ?? false,
1325
+ color: text?.color ?? "#C0C0C0",
1326
+ transparency: (1 - (value?.opacity ?? .5)) * 100,
1327
+ orientation: rotation === 0 ? "horizontal" : "diagonal",
1328
+ scalePercent: picture?.scalePercent ?? "auto",
1329
+ washout: picture?.washout ?? true,
1330
+ lockAspectRatio: picture?.lockAspectRatio ?? true,
1331
+ pictureName: picture ? "Image in this document" : "",
1332
+ sourceWatermarkId: picture ? item?.watermarkId : void 0,
1333
+ widthPt: picture?.widthPt,
1334
+ heightPt: picture?.heightPt,
1335
+ placement,
1336
+ ...placementState(placement),
1337
+ customSize: Boolean(picture && (placement?.widthPt != null || picture.widthPt != null))
1338
+ };
1339
+ }
1340
+ function patchWatermarkDraft(draft, patch) {
1341
+ const next = {
1342
+ ...draft,
1343
+ ...patch
1344
+ };
1345
+ if (patch.kind && patch.kind !== "none") {
1346
+ if (patch.kind !== draft.geometryKind) {
1347
+ next.placement = void 0;
1348
+ next.widthPt = void 0;
1349
+ next.heightPt = void 0;
1350
+ next.customSize = false;
1351
+ next.orientation = patch.kind === "picture" ? "horizontal" : "diagonal";
1352
+ }
1353
+ next.geometryKind = patch.kind;
1354
+ }
1355
+ if (patch.orientation !== void 0) next.placement = {
1356
+ ...next.placement,
1357
+ rotationDegrees: patch.orientation === "diagonal" ? 315 : 0
1358
+ };
1359
+ if (patch.scalePercent !== void 0 || patch.src !== void 0) {
1360
+ const { widthPt: _width, heightPt: _height, ...placement } = next.placement ?? {};
1361
+ next.placement = Object.keys(placement).length ? placement : void 0;
1362
+ next.widthPt = void 0;
1363
+ next.heightPt = void 0;
1364
+ next.customSize = false;
1365
+ }
1366
+ return {
1367
+ ...next,
1368
+ ...placementState(next.placement)
1369
+ };
1370
+ }
1371
+ function getWatermarkDraftError(draft) {
1372
+ if (draft.kind === "none") return null;
1373
+ if (!Number.isFinite(draft.transparency) || draft.transparency < 0 || draft.transparency > 100) return "Transparency must be between 0 and 100%.";
1374
+ if (draft.kind === "picture") return draft.src || draft.sourceWatermarkId ? null : "Choose a picture.";
1375
+ if (!draft.text.trim()) return "Enter watermark text.";
1376
+ if (draft.fontSize !== "auto" && (!Number.isFinite(draft.fontSize) || draft.fontSize <= 0)) return "Enter a positive font size or choose Auto.";
1377
+ if (!/^#[\da-f]{6}$/i.test(draft.color)) return "Enter a six-digit hex color.";
1378
+ if (!draft.fontFamily.trim()) return "Choose a font.";
1379
+ return null;
1380
+ }
1381
+ function toWatermarkInput(draft) {
1382
+ const shared = {
1383
+ opacity: 1 - draft.transparency / 100,
1384
+ ...draft.placement ? { placement: draft.placement } : {}
1385
+ };
1386
+ if (draft.kind === "text") return {
1387
+ kind: "text",
1388
+ text: draft.text,
1389
+ fontFamily: draft.fontFamily,
1390
+ fontSizePt: draft.fontSize,
1391
+ bold: draft.bold,
1392
+ italic: draft.italic,
1393
+ color: draft.color,
1394
+ orientation: draft.orientation,
1395
+ ...shared
1396
+ };
1397
+ if (draft.kind !== "picture") throw new Error("A removal has no watermark input.");
1398
+ return {
1399
+ kind: "picture",
1400
+ ...draft.src ? { src: draft.src } : { source: {
1401
+ kind: "existing",
1402
+ watermarkId: draft.sourceWatermarkId
1403
+ } },
1404
+ scalePercent: draft.scalePercent,
1405
+ washout: draft.washout,
1406
+ lockAspectRatio: draft.lockAspectRatio,
1407
+ ...draft.widthPt != null ? { widthPt: draft.widthPt } : {},
1408
+ ...draft.heightPt != null ? { heightPt: draft.heightPt } : {},
1409
+ ...shared
1410
+ };
1411
+ }
1412
+ //#endregion
1413
+ //#region src/public/ui/watermark-controller.ts
1414
+ var PAGE_TYPES = {
1415
+ default: "Default pages",
1416
+ first: "First page",
1417
+ even: "Even pages"
1418
+ };
1419
+ var STALE_MESSAGE = "The document changed while this dialog was open. Review the latest watermark before applying.";
1420
+ function scopeSlots(sections, sectionId, variant) {
1421
+ return sections.filter((section) => sectionId == null || section.address.sectionId === sectionId).flatMap((section) => {
1422
+ return (variant === "all" ? [
1423
+ "default",
1424
+ ...section.titlePage ? ["first"] : [],
1425
+ ...section.oddEvenHeadersFooters ? ["even"] : []
1426
+ ] : [variant]).map((variant) => ({
1427
+ kind: "headerFooterSlot",
1428
+ section: section.address,
1429
+ headerFooterKind: "header",
1430
+ variant
1431
+ }));
1432
+ });
1433
+ }
1434
+ function slotKey(slot) {
1435
+ return `${slot.section.sectionId}:${slot.variant}`;
1436
+ }
1437
+ function failureMessage(error) {
1438
+ return error instanceof Error ? error.message : "The watermark could not be updated. Try again.";
1439
+ }
1440
+ function createWatermarkController(options) {
1441
+ const surface = createWatermarkSurfaceRuntime(options.getContainer);
1442
+ const workflowOptions = {
1443
+ ...options,
1444
+ announce: (message) => {
1445
+ surface.announce(message);
1446
+ options.announce?.(message);
1447
+ }
1448
+ };
1449
+ let active = null;
1450
+ let destroyed = false;
1451
+ let openGeneration = 0;
1452
+ function close() {
1453
+ const previous = active;
1454
+ active = null;
1455
+ previous?.dispose();
1456
+ previous?.handle.close();
1457
+ }
1458
+ function open() {
1459
+ if (destroyed) return {
1460
+ ok: false,
1461
+ reason: "not-ready"
1462
+ };
1463
+ const editor = options.getEditor();
1464
+ if (!editor) return {
1465
+ ok: false,
1466
+ reason: "not-ready"
1467
+ };
1468
+ if (!editor.doc?.watermarks?.list || !editor.doc.sections?.list) return {
1469
+ ok: false,
1470
+ reason: "document-api-unavailable"
1471
+ };
1472
+ if (!options.openSurface || !editor.doc.watermarks.apply || !editor.host?.watermarkPreview) return {
1473
+ ok: false,
1474
+ reason: "operation-unavailable"
1475
+ };
1476
+ const generation = ++openGeneration;
1477
+ const restoreFocus = surface.captureFocusReturn();
1478
+ close();
1479
+ surface.mount();
1480
+ const workflow = createDialogWorkflow(editor, workflowOptions, () => {
1481
+ if (active?.handle === handle) close();
1482
+ });
1483
+ let handle;
1484
+ try {
1485
+ handle = options.openSurface({
1486
+ mode: "dialog",
1487
+ ariaLabel: "Watermark",
1488
+ component: surface.component,
1489
+ props: { model: workflow.model },
1490
+ dialog: { maxWidth: 700 }
1491
+ });
1492
+ } catch {
1493
+ workflow.dispose();
1494
+ return {
1495
+ ok: false,
1496
+ reason: "operation-unavailable"
1497
+ };
1498
+ }
1499
+ active = {
1500
+ handle,
1501
+ dispose: workflow.dispose
1502
+ };
1503
+ handle.result.then((outcome) => {
1504
+ workflow.dispose();
1505
+ if (active?.handle === handle) active = null;
1506
+ if (outcome.status === "closed" || outcome.status === "submitted") restoreFocus(() => !destroyed && generation === openGeneration && active === null);
1507
+ });
1508
+ workflow.model.reload();
1509
+ return { ok: true };
1510
+ }
1511
+ return {
1512
+ open,
1513
+ close,
1514
+ destroy() {
1515
+ destroyed = true;
1516
+ close();
1517
+ surface.destroy();
1518
+ }
1519
+ };
1520
+ }
1521
+ function createDialogWorkflow(editor, options, onSuccess) {
1522
+ const listeners = /* @__PURE__ */ new Set();
1523
+ let alive = true;
1524
+ let generation = 0;
1525
+ let timer;
1526
+ let catalog = [];
1527
+ let sections = [];
1528
+ let revision = "";
1529
+ let originalDraft = JSON.stringify(createWatermarkDraft());
1530
+ let preparedInput = null;
1531
+ let snapshot = {
1532
+ phase: "loading",
1533
+ draft: createWatermarkDraft(),
1534
+ sections: [],
1535
+ sectionId: null,
1536
+ variant: "all",
1537
+ items: [],
1538
+ selectedIds: [],
1539
+ fonts: [...options.getFonts()],
1540
+ dirty: false,
1541
+ canApply: false,
1542
+ error: null,
1543
+ stale: false,
1544
+ readonlyReason: null,
1545
+ impact: "",
1546
+ impactDetails: [],
1547
+ preview: null
1548
+ };
1549
+ const current = (token) => alive && token === generation && options.getEditor() === editor;
1550
+ const editable = () => options.getMode() === "editing";
1551
+ function publish(patch) {
1552
+ if (!alive) return;
1553
+ snapshot = {
1554
+ ...snapshot,
1555
+ ...patch
1556
+ };
1557
+ for (const listener of listeners) listener(snapshot);
1558
+ }
1559
+ function invalidate() {
1560
+ clearTimeout(timer);
1561
+ preparedInput = null;
1562
+ return ++generation;
1563
+ }
1564
+ function location(slot) {
1565
+ const section = sections.find((item) => item.address.sectionId === slot.section.sectionId);
1566
+ const inactive = slot.variant === "first" ? !section?.titlePage : slot.variant === "even" ? !section?.oddEvenHeadersFooters : false;
1567
+ return `Section ${(section?.index ?? 0) + 1}, ${PAGE_TYPES[slot.variant].toLowerCase()}${inactive ? " (inactive)" : ""}`;
1568
+ }
1569
+ function scopedItems() {
1570
+ if (snapshot.sectionId == null && snapshot.variant === "all") return catalog;
1571
+ const keys = new Set(scopeSlots(sections, snapshot.sectionId, snapshot.variant).map(slotKey));
1572
+ return catalog.filter((item) => item.effectiveIn.some((slot) => keys.has(slotKey(slot))));
1573
+ }
1574
+ function publishCatalog() {
1575
+ publish({ items: scopedItems().map((item) => ({
1576
+ id: item.watermarkId,
1577
+ label: item.watermark.kind === "text" ? item.watermark.text : "Picture watermark",
1578
+ locations: item.effectiveIn.map(location).join("; ")
1579
+ })) });
1580
+ }
1581
+ function buildInput() {
1582
+ const target = snapshot.sectionId == null && snapshot.variant === "all" ? { kind: "document" } : {
1583
+ kind: "headerFooterSlots",
1584
+ slots: scopeSlots(sections, snapshot.sectionId, snapshot.variant)
1585
+ };
1586
+ if (snapshot.draft.kind === "none") return {
1587
+ target,
1588
+ action: "remove",
1589
+ watermarkIds: snapshot.selectedIds
1590
+ };
1591
+ const watermark = toWatermarkInput(snapshot.draft);
1592
+ if (snapshot.selectedIds.length) return {
1593
+ target,
1594
+ action: "replace",
1595
+ watermarkIds: snapshot.selectedIds,
1596
+ watermark
1597
+ };
1598
+ if ("source" in watermark) throw new Error("Select the existing picture watermark in this scope, or choose a new picture.");
1599
+ return {
1600
+ target,
1601
+ action: "insert",
1602
+ watermark
1603
+ };
1604
+ }
1605
+ function rejectReceipt(result) {
1606
+ if (result.success) return false;
1607
+ const stale = result.failure?.code === "REVISION_MISMATCH";
1608
+ publish({
1609
+ stale,
1610
+ phase: "editing",
1611
+ canApply: false,
1612
+ error: stale ? STALE_MESSAGE : result.failure?.message ?? "The watermark could not be updated."
1613
+ });
1614
+ return true;
1615
+ }
1616
+ async function prepare(token) {
1617
+ try {
1618
+ const error = getWatermarkDraftError(snapshot.draft);
1619
+ if (error) {
1620
+ publish({
1621
+ phase: "editing",
1622
+ error,
1623
+ canApply: false,
1624
+ preview: null
1625
+ });
1626
+ return;
1627
+ }
1628
+ const input = snapshot.draft.kind === "none" && !snapshot.selectedIds.length ? null : buildInput();
1629
+ const preview = snapshot.draft.kind === "none" ? null : await editor.host.watermarkPreview({
1630
+ watermark: toWatermarkInput(snapshot.draft),
1631
+ sectionId: snapshot.sectionId ?? sections[0]?.address.sectionId,
1632
+ expectedRevision: revision
1633
+ });
1634
+ if (!current(token)) return;
1635
+ if (!input || !snapshot.dirty || !editable()) {
1636
+ publish({
1637
+ phase: "editing",
1638
+ preview,
1639
+ canApply: false,
1640
+ error: null
1641
+ });
1642
+ return;
1643
+ }
1644
+ const receipt = await editor.doc.watermarks.apply(input, {
1645
+ expectedRevision: revision,
1646
+ dryRun: true
1647
+ });
1648
+ if (!current(token)) return;
1649
+ if (!receipt.success) {
1650
+ rejectReceipt(receipt);
1651
+ return;
1652
+ }
1653
+ preparedInput = input;
1654
+ const affected = receipt.affectedSlots.map(location);
1655
+ publish({
1656
+ phase: "editing",
1657
+ preview,
1658
+ error: null,
1659
+ canApply: affected.length > 0 && !snapshot.stale && editable(),
1660
+ impact: affected.length ? `Changes: ${affected.length > 3 ? `${affected.length} page locations` : affected.join("; ")}. Other locations stay as they are.` : "No matching watermark will change.",
1661
+ impactDetails: affected.length > 3 ? affected : []
1662
+ });
1663
+ } catch (error) {
1664
+ if (current(token)) publish({
1665
+ phase: "editing",
1666
+ error: failureMessage(error),
1667
+ canApply: false,
1668
+ preview: null
1669
+ });
1670
+ }
1671
+ }
1672
+ function schedulePreview() {
1673
+ const token = invalidate();
1674
+ publish({
1675
+ phase: "previewing",
1676
+ canApply: false,
1677
+ error: null,
1678
+ impact: "",
1679
+ impactDetails: [],
1680
+ preview: null
1681
+ });
1682
+ timer = setTimeout(() => {
1683
+ prepare(token);
1684
+ }, 120);
1685
+ }
1686
+ const model = {
1687
+ getSnapshot: () => snapshot,
1688
+ subscribe(listener) {
1689
+ listeners.add(listener);
1690
+ listener(snapshot);
1691
+ return () => {
1692
+ listeners.delete(listener);
1693
+ };
1694
+ },
1695
+ async reload() {
1696
+ const token = invalidate();
1697
+ publish({
1698
+ phase: "loading",
1699
+ canApply: false,
1700
+ stale: false,
1701
+ error: null,
1702
+ impactDetails: [],
1703
+ preview: null
1704
+ });
1705
+ try {
1706
+ const [marks, sectionList] = await Promise.all([editor.doc.watermarks.list(), editor.doc.sections.list()]);
1707
+ if (!current(token)) return;
1708
+ if (marks.items.length !== marks.total || sectionList.items.length !== sectionList.total) throw new Error("The complete watermark scope could not be loaded.");
1709
+ if (sectionList.evaluatedRevision && sectionList.evaluatedRevision !== marks.evaluatedRevision) throw new Error(STALE_MESSAGE);
1710
+ catalog = marks.items;
1711
+ sections = sectionList.items;
1712
+ revision = marks.evaluatedRevision;
1713
+ const available = scopedItems();
1714
+ const selected = available.length === 1 ? available[0] : void 0;
1715
+ const draft = createWatermarkDraft(selected);
1716
+ originalDraft = JSON.stringify(draft);
1717
+ publish({
1718
+ draft,
1719
+ dirty: false,
1720
+ selectedIds: selected ? [selected.watermarkId] : [],
1721
+ sections: sections.map((item) => ({
1722
+ id: item.address.sectionId,
1723
+ label: `Section ${item.index + 1}`
1724
+ })),
1725
+ readonlyReason: editable() ? null : options.getMode() === "suggesting" ? "Watermarks cannot be changed in Suggesting mode." : "This document is read-only.",
1726
+ impact: available.length > 1 ? "Select the watermark copies you want to edit, or add a new watermark." : ""
1727
+ });
1728
+ publishCatalog();
1729
+ await prepare(token);
1730
+ } catch (error) {
1731
+ if (current(token)) publish({
1732
+ phase: "editing",
1733
+ error: failureMessage(error),
1734
+ canApply: false
1735
+ });
1736
+ }
1737
+ },
1738
+ patchDraft(patch) {
1739
+ if (!alive || snapshot.phase === "loading" || snapshot.phase === "applying" || !editable()) return;
1740
+ const draft = patchWatermarkDraft(snapshot.draft, patch);
1741
+ publish({
1742
+ draft,
1743
+ dirty: JSON.stringify(draft) !== originalDraft
1744
+ });
1745
+ schedulePreview();
1746
+ },
1747
+ setScope(sectionId, variant) {
1748
+ if (!alive || snapshot.phase === "loading" || snapshot.phase === "applying") return;
1749
+ if (sectionId != null && !sections.some((item) => item.address.sectionId === sectionId)) return;
1750
+ publish({
1751
+ sectionId,
1752
+ variant
1753
+ });
1754
+ publishCatalog();
1755
+ const available = scopedItems();
1756
+ const visible = new Set(available.map((item) => item.watermarkId));
1757
+ let selectedIds = snapshot.selectedIds.filter((id) => visible.has(id));
1758
+ if (snapshot.dirty && snapshot.draft.kind === "none" && !selectedIds.length && available.length === 1) selectedIds = [available[0].watermarkId];
1759
+ publish({ selectedIds });
1760
+ if (!snapshot.dirty) model.selectWatermarks(available.length === 1 ? [available[0].watermarkId] : []);
1761
+ else schedulePreview();
1762
+ },
1763
+ selectWatermarks(ids) {
1764
+ if (!alive || snapshot.phase === "loading" || snapshot.phase === "applying") return;
1765
+ const available = scopedItems();
1766
+ const selectedIds = [...new Set(ids)].filter((id) => available.some((item) => item.watermarkId === id));
1767
+ const draft = createWatermarkDraft(available.find((item) => item.watermarkId === selectedIds[0]));
1768
+ originalDraft = JSON.stringify(draft);
1769
+ publish({
1770
+ selectedIds,
1771
+ draft,
1772
+ dirty: false
1773
+ });
1774
+ schedulePreview();
1775
+ },
1776
+ async apply() {
1777
+ if (!alive || !snapshot.canApply || !preparedInput || !editable() || options.getEditor() !== editor) return;
1778
+ const input = preparedInput;
1779
+ const token = invalidate();
1780
+ publish({
1781
+ phase: "applying",
1782
+ canApply: false,
1783
+ error: null
1784
+ });
1785
+ options.announce?.("Applying watermark changes. Closing the dialog will not cancel them.");
1786
+ try {
1787
+ const receipt = await editor.doc.watermarks.apply(input, { expectedRevision: revision });
1788
+ if (!current(token)) {
1789
+ options.announce?.(receipt.success ? "Watermark updated." : "The watermark could not be updated.");
1790
+ return;
1791
+ }
1792
+ if (rejectReceipt(receipt)) return;
1793
+ options.announce?.(input.action === "remove" ? "Watermark removed." : "Watermark updated.");
1794
+ onSuccess();
1795
+ } catch (error) {
1796
+ if (current(token)) publish({
1797
+ phase: "editing",
1798
+ error: failureMessage(error),
1799
+ canApply: false
1800
+ });
1801
+ else options.announce?.(failureMessage(error));
1802
+ }
1803
+ }
1804
+ };
1805
+ const stopEvents = editor.host?.events?.subscribe((event) => {
1806
+ if (!alive || snapshot.phase === "applying") return;
1807
+ if (event.type === "collaboration:document-replaced") {
1808
+ invalidate();
1809
+ publish({
1810
+ phase: "editing",
1811
+ stale: true,
1812
+ canApply: false,
1813
+ error: STALE_MESSAGE
1814
+ });
1815
+ return;
1816
+ }
1817
+ if (![
1818
+ "document:mutated",
1819
+ "mutation:committed",
1820
+ "collaboration:remote-changed"
1821
+ ].includes(event.type)) return;
1822
+ invalidate();
1823
+ if (snapshot.dirty) publish({
1824
+ phase: "editing",
1825
+ stale: true,
1826
+ canApply: false,
1827
+ error: STALE_MESSAGE
1828
+ });
1829
+ else timer = setTimeout(() => {
1830
+ model.reload();
1831
+ }, 120);
1832
+ });
1833
+ return {
1834
+ model,
1835
+ dispose() {
1836
+ alive = false;
1837
+ invalidate();
1838
+ stopEvents?.();
1839
+ listeners.clear();
1840
+ }
1841
+ };
1842
+ }
1843
+ //#endregion
537
1844
  //#region src/public/ui/format-painter-helpers.js
538
1845
  /**
539
1846
  * Maps SDRunProps field names to InlineRunPatch field names.
@@ -7591,6 +8898,7 @@ function createSuperDocUI(options) {
7591
8898
  syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(readHostReviewSource()));
7592
8899
  state = computeState("initial");
7593
8900
  lastOptimisticInlineSelectionSignature = selectionInlineValueSignature(state.selection);
8901
+ let scheduleActiveSearchRefresh = () => {};
7594
8902
  const syncHostEventsSubscription = () => {
7595
8903
  const host = getEditor()?.host;
7596
8904
  const events = host?.events;
@@ -7610,6 +8918,7 @@ function createSuperDocUI(options) {
7610
8918
  const off = next.subscribe((event) => {
7611
8919
  const type = event?.type;
7612
8920
  const isStandaloneDocumentMutation = type === "document:mutated" && event.hasCommitEvent !== true;
8921
+ if (isStandaloneDocumentMutation || type === "mutation:committed" && mutationCommittedEventChangedDocument(event) || type === "collaboration:remote-changed" && Array.isArray(event.changedStoryIds) && event.changedStoryIds.length > 0) scheduleActiveSearchRefresh();
7613
8922
  if ((type === "document:mutated" || type === "mutation:committed" && mutationCommittedEventChangedDocument(event)) && host && !dirtyHosts.has(host)) {
7614
8923
  dirtyHosts.add(host);
7615
8924
  recompute("document-dirty");
@@ -11011,6 +12320,12 @@ function createSuperDocUI(options) {
11011
12320
  * session is the one that says how to close it.
11012
12321
  */
11013
12322
  let releaseActiveSearchSession = null;
12323
+ let releaseRefreshShellSearch = null;
12324
+ const cancelRefreshShellSearch = () => {
12325
+ const release = releaseRefreshShellSearch;
12326
+ releaseRefreshShellSearch = null;
12327
+ if (release) safeCall(() => release(), null);
12328
+ };
11014
12329
  /** Release whatever session is open, then forget it. */
11015
12330
  const releaseSearchSession = () => {
11016
12331
  const release = releaseActiveSearchSession;
@@ -11042,6 +12357,7 @@ function createSuperDocUI(options) {
11042
12357
  searchRequestGeneration += 1;
11043
12358
  pendingFallbackGeneration = null;
11044
12359
  shellFallbackOutstanding = false;
12360
+ cancelRefreshShellSearch();
11045
12361
  releaseSearchSession();
11046
12362
  const available = searchIsAvailable();
11047
12363
  searchState = {
@@ -11223,6 +12539,7 @@ function createSuperDocUI(options) {
11223
12539
  },
11224
12540
  close: () => {
11225
12541
  searchRequestGeneration += 1;
12542
+ cancelRefreshShellSearch();
11226
12543
  if (releaseActiveSearchSession) releaseSearchSession();
11227
12544
  else {
11228
12545
  const host = getHostSearch();
@@ -11246,6 +12563,7 @@ function createSuperDocUI(options) {
11246
12563
  const includeDeletedText = (options?.includeTrackedDeletions ?? options?.includeDeletedText) === true;
11247
12564
  const regex = options?.regex === true;
11248
12565
  const generation = ++searchRequestGeneration;
12566
+ cancelRefreshShellSearch();
11249
12567
  cancelOutstandingShellFallback();
11250
12568
  if (!host || typeof host.setSession !== "function") {
11251
12569
  const editSearch = getEditCommandSearch();
@@ -11434,6 +12752,7 @@ function createSuperDocUI(options) {
11434
12752
  },
11435
12753
  clear: () => {
11436
12754
  searchRequestGeneration += 1;
12755
+ cancelRefreshShellSearch();
11437
12756
  cancelOutstandingShellFallback();
11438
12757
  const host = getHostSearch();
11439
12758
  if (host && typeof host.clear === "function") safeCall(() => host.clear(), null);
@@ -11549,6 +12868,49 @@ function createSuperDocUI(options) {
11549
12868
  return mapHostReplaceResult(result);
11550
12869
  }
11551
12870
  };
12871
+ let searchRefreshTimer = null;
12872
+ scheduleActiveSearchRefresh = () => {
12873
+ if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
12874
+ if (!searchState.open || !searchState.query) return;
12875
+ const scheduledGeneration = searchRequestGeneration;
12876
+ searchRefreshTimer = setTimeout(() => {
12877
+ searchRefreshTimer = null;
12878
+ if (disposed || scheduledGeneration !== searchRequestGeneration || !searchState.open || !searchState.query) return;
12879
+ const input = {
12880
+ query: searchState.query,
12881
+ caseSensitive: searchState.caseSensitive,
12882
+ includeDeletedText: searchState.includeDeletedText,
12883
+ regex: searchState.regex
12884
+ };
12885
+ const editSearch = getEditCommandSearch();
12886
+ const host = getHostSearch();
12887
+ if (typeof editSearch?.refresh !== "function" && typeof host?.refresh !== "function") return;
12888
+ const generation = ++searchRequestGeneration;
12889
+ cancelOutstandingShellFallback();
12890
+ if (typeof editSearch?.refresh === "function" && typeof editSearch.query === "function") {
12891
+ const capturedSearch = editSearch;
12892
+ releaseRefreshShellSearch = () => {
12893
+ capturedSearch.query({ query: "" });
12894
+ };
12895
+ }
12896
+ const result = typeof editSearch?.refresh === "function" ? safeCall(() => editSearch.refresh(input), null) : safeCall(() => host.refresh(), null);
12897
+ if (isPromiseLike(result)) {
12898
+ pendingFallbackGeneration = generation;
12899
+ Promise.resolve(result).then((resolved) => {
12900
+ if (generation !== searchRequestGeneration || disposed) return;
12901
+ pendingFallbackGeneration = null;
12902
+ applyHostSearchResult(resolved, readEditCommandCanReplace(), readEditCommandCanReplaceAll());
12903
+ }, () => {
12904
+ if (generation !== searchRequestGeneration || disposed) return;
12905
+ pendingFallbackGeneration = null;
12906
+ setSearchState({
12907
+ available: false,
12908
+ reason: SUPERDOC_UI_REASONS.searchUnavailable
12909
+ });
12910
+ });
12911
+ } else if (result !== null) applyHostSearchResult(result);
12912
+ }, 120);
12913
+ };
11552
12914
  const stylesSub = sliceHandle((s) => s.styles);
11553
12915
  const stylesSnap = snapshotHandle(stylesSub);
11554
12916
  const styles = {
@@ -11563,6 +12925,15 @@ function createSuperDocUI(options) {
11563
12925
  getQuickGallery: () => stylesSub.get().quickGallery,
11564
12926
  getActiveParagraphStyle: () => computeActiveParagraphStyle(state.selection, getStyleCatalog().cache).style
11565
12927
  };
12928
+ const watermark = createWatermarkController({
12929
+ getEditor,
12930
+ getMode: readDocumentMode,
12931
+ getFonts: () => fonts.getFamilyOptions(),
12932
+ getContainer: () => superdoc.element ?? null,
12933
+ openSurface: typeof superdoc.openSurface === "function" ? (request) => superdoc.openSurface(request) : null
12934
+ });
12935
+ documentResetHooks.push(watermark.close);
12936
+ documentModeChangeHooks.push(watermark.close);
11566
12937
  const createScope = () => {
11567
12938
  const scopeUnsubs = [];
11568
12939
  return {
@@ -11584,8 +12955,12 @@ function createSuperDocUI(options) {
11584
12955
  };
11585
12956
  const destroy = () => {
11586
12957
  if (disposed) return;
12958
+ watermark.destroy();
11587
12959
  clearContentControlHighlight();
11588
12960
  disposed = true;
12961
+ if (searchRefreshTimer !== null) clearTimeout(searchRefreshTimer);
12962
+ searchRefreshTimer = null;
12963
+ cancelRefreshShellSearch();
11589
12964
  releaseSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost, uiTrackedChangesCatalogState);
11590
12965
  if (foregroundAsyncRetryTimer) {
11591
12966
  clearTimeout(foregroundAsyncRetryTimer);
@@ -11666,6 +13041,7 @@ function createSuperDocUI(options) {
11666
13041
  tables,
11667
13042
  search,
11668
13043
  contextMenu,
13044
+ watermark,
11669
13045
  styles,
11670
13046
  formatPainter: {
11671
13047
  setPointerSelecting(flag) {
@@ -11878,4 +13254,81 @@ function commandIsActive(descriptor, selection) {
11878
13254
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
11879
13255
  }
11880
13256
  //#endregion
11881
- export { applyV2CommentInvalidationFromMutation as a, DOM_CLASS_NAMES as c, recordInteraction as d, BUILT_IN_COMMAND_IDS as i, closeInteractionHistory as l, executeFirstPartyCommandAsync as n, getV2TrackedChangeMutationImpact as o, registerFirstPartyCommandMutation as r, shallowEqual as s, createSuperDocUI as t, createInteractionHistory as u };
13257
+ Object.defineProperty(exports, "BUILT_IN_COMMAND_IDS", {
13258
+ enumerable: true,
13259
+ get: function() {
13260
+ return BUILT_IN_COMMAND_IDS;
13261
+ }
13262
+ });
13263
+ Object.defineProperty(exports, "DOM_CLASS_NAMES", {
13264
+ enumerable: true,
13265
+ get: function() {
13266
+ return DOM_CLASS_NAMES;
13267
+ }
13268
+ });
13269
+ Object.defineProperty(exports, "applyV2CommentInvalidationFromMutation", {
13270
+ enumerable: true,
13271
+ get: function() {
13272
+ return applyV2CommentInvalidationFromMutation;
13273
+ }
13274
+ });
13275
+ Object.defineProperty(exports, "closeInteractionHistory", {
13276
+ enumerable: true,
13277
+ get: function() {
13278
+ return closeInteractionHistory;
13279
+ }
13280
+ });
13281
+ Object.defineProperty(exports, "createImageFilePicker", {
13282
+ enumerable: true,
13283
+ get: function() {
13284
+ return createImageFilePicker;
13285
+ }
13286
+ });
13287
+ Object.defineProperty(exports, "createInteractionHistory", {
13288
+ enumerable: true,
13289
+ get: function() {
13290
+ return createInteractionHistory;
13291
+ }
13292
+ });
13293
+ Object.defineProperty(exports, "createSuperDocUI", {
13294
+ enumerable: true,
13295
+ get: function() {
13296
+ return createSuperDocUI;
13297
+ }
13298
+ });
13299
+ Object.defineProperty(exports, "executeFirstPartyCommandAsync", {
13300
+ enumerable: true,
13301
+ get: function() {
13302
+ return executeFirstPartyCommandAsync;
13303
+ }
13304
+ });
13305
+ Object.defineProperty(exports, "getV2TrackedChangeMutationImpact", {
13306
+ enumerable: true,
13307
+ get: function() {
13308
+ return getV2TrackedChangeMutationImpact;
13309
+ }
13310
+ });
13311
+ Object.defineProperty(exports, "recordInteraction", {
13312
+ enumerable: true,
13313
+ get: function() {
13314
+ return recordInteraction;
13315
+ }
13316
+ });
13317
+ Object.defineProperty(exports, "registerFirstPartyCommandMutation", {
13318
+ enumerable: true,
13319
+ get: function() {
13320
+ return registerFirstPartyCommandMutation;
13321
+ }
13322
+ });
13323
+ Object.defineProperty(exports, "resolveImageSrc", {
13324
+ enumerable: true,
13325
+ get: function() {
13326
+ return resolveImageSrc;
13327
+ }
13328
+ });
13329
+ Object.defineProperty(exports, "shallowEqual", {
13330
+ enumerable: true,
13331
+ get: function() {
13332
+ return shallowEqual;
13333
+ }
13334
+ });