superdoc 1.12.0-next.11 → 1.12.0-next.13

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 (33) hide show
  1. package/dist/chunks/{PdfViewer-1H_e-blH.cjs → PdfViewer-BM8GbrHb.cjs} +2 -2
  2. package/dist/chunks/{PdfViewer-2GmHPcuP.es.js → PdfViewer-D9duMPBd.es.js} +2 -2
  3. package/dist/chunks/{SuperConverter-DKC3eAnw.cjs → SuperConverter-BMjvGr5r.cjs} +14 -3
  4. package/dist/chunks/{SuperConverter-Cyd0kGmJ.es.js → SuperConverter-Dx95dNlr.es.js} +14 -3
  5. package/dist/chunks/{index-BKXPlsQF.cjs → index-BF7DfWLs.cjs} +756 -390
  6. package/dist/chunks/{index-BlzZBR33.es.js → index-BTrpAvox.es.js} +4 -4
  7. package/dist/chunks/{index-C22k0MUW.es.js → index-C-t9SDCr.es.js} +756 -390
  8. package/dist/chunks/{index-CpgpR-lf.cjs → index-DcUDN9u9.cjs} +4 -4
  9. package/dist/style.css +39 -29
  10. package/dist/super-editor/converter.cjs +1 -1
  11. package/dist/super-editor/converter.es.js +1 -1
  12. package/dist/super-editor/core/Editor.d.ts.map +1 -1
  13. package/dist/super-editor/core/presentation-editor/PresentationEditor.d.ts.map +1 -1
  14. package/dist/super-editor/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.d.ts.map +1 -1
  15. package/dist/super-editor/extensions/structured-content/StructuredContentBlockView.d.ts.map +1 -1
  16. package/dist/super-editor/extensions/structured-content/StructuredContentInlineView.d.ts.map +1 -1
  17. package/dist/super-editor/extensions/structured-content/StructuredContentViewBase.d.ts +3 -0
  18. package/dist/super-editor/extensions/structured-content/StructuredContentViewBase.d.ts.map +1 -1
  19. package/dist/super-editor/extensions/structured-content/structured-content-block.d.ts.map +1 -1
  20. package/dist/super-editor/extensions/structured-content/structured-content-lock-plugin.d.ts +5 -0
  21. package/dist/super-editor/extensions/structured-content/structured-content-lock-plugin.d.ts.map +1 -0
  22. package/dist/super-editor/extensions/structured-content/structured-content-select-plugin.d.ts +14 -0
  23. package/dist/super-editor/extensions/structured-content/structured-content-select-plugin.d.ts.map +1 -0
  24. package/dist/super-editor/extensions/structured-content/structured-content.d.ts.map +1 -1
  25. package/dist/super-editor/extensions/types/node-attributes.d.ts +4 -0
  26. package/dist/super-editor/extensions/types/node-attributes.d.ts.map +1 -1
  27. package/dist/super-editor.cjs +2 -2
  28. package/dist/super-editor.es.js +3 -3
  29. package/dist/superdoc.cjs +3 -3
  30. package/dist/superdoc.es.js +3 -3
  31. package/dist/superdoc.umd.js +770 -393
  32. package/dist/superdoc.umd.js.map +1 -1
  33. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const superEditor_converter = require("./SuperConverter-DKC3eAnw.cjs");
2
+ const superEditor_converter = require("./SuperConverter-BMjvGr5r.cjs");
3
3
  const helpers$1 = require("./helpers-OFep8CYR.cjs");
4
4
  const vue = require("./vue-BkoOkkmT.cjs");
5
5
  require("./jszip.min-oAFpNMh5.cjs");
@@ -993,7 +993,7 @@ const DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
993
993
  const COMMUNITY_LICENSE_KEY = "community-and-eval-agplv3";
994
994
  function getSuperdocVersion() {
995
995
  try {
996
- return true ? "1.12.0-next.11" : "unknown";
996
+ return true ? "1.12.0-next.13" : "unknown";
997
997
  } catch {
998
998
  return "unknown";
999
999
  }
@@ -17068,7 +17068,7 @@ const canUseDOM = () => {
17068
17068
  return false;
17069
17069
  }
17070
17070
  };
17071
- const summaryVersion = "1.12.0-next.11";
17071
+ const summaryVersion = "1.12.0-next.13";
17072
17072
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
17073
17073
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
17074
17074
  function mapAttributes(attrs) {
@@ -19482,8 +19482,7 @@ class Editor extends EventEmitter {
19482
19482
  "docProps/custom.xml": String(customXml),
19483
19483
  "word/_rels/document.xml.rels": String(rels),
19484
19484
  "word/numbering.xml": String(numbering),
19485
- // Replace & with & in styles.xml as DOCX viewers can't handle it
19486
- "word/styles.xml": String(styles).replace(/&/gi, "&"),
19485
+ "word/styles.xml": String(styles),
19487
19486
  ...updatedHeadersFooters,
19488
19487
  ...coreXml ? { "docProps/core.xml": String(coreXml) } : {}
19489
19488
  };
@@ -19873,7 +19872,7 @@ class Editor extends EventEmitter {
19873
19872
  * Process collaboration migrations
19874
19873
  */
19875
19874
  processCollaborationMigrations() {
19876
- console.debug("[checkVersionMigrations] Current editor version", "1.12.0-next.11");
19875
+ console.debug("[checkVersionMigrations] Current editor version", "1.12.0-next.13");
19877
19876
  if (!this.options.ydoc) return;
19878
19877
  const metaMap = this.options.ydoc.getMap("meta");
19879
19878
  let docVersion = metaMap.get("version");
@@ -20051,6 +20050,300 @@ class Editor extends EventEmitter {
20051
20050
  this.#renderer?.initDevTools?.(this);
20052
20051
  }
20053
20052
  }
20053
+ function getEditorSurfaceElement(editor) {
20054
+ if (!editor) return null;
20055
+ if (typeof editor.hitTest === "function" && editor.element instanceof HTMLElement) {
20056
+ return editor.element;
20057
+ }
20058
+ return editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element ?? null;
20059
+ }
20060
+ function getSurfaceRelativePoint(editor, eventLocation = {}) {
20061
+ const surface = getEditorSurfaceElement(editor);
20062
+ if (!surface) return null;
20063
+ const rect = surface.getBoundingClientRect();
20064
+ let left2;
20065
+ let top2;
20066
+ if (typeof eventLocation.clientX === "number" && typeof eventLocation.clientY === "number") {
20067
+ left2 = eventLocation.clientX - rect.left;
20068
+ top2 = eventLocation.clientY - rect.top;
20069
+ } else if (editor?.state?.selection) {
20070
+ const selectionFrom = editor.state.selection.from;
20071
+ const coords = editor.coordsAtPos?.(selectionFrom);
20072
+ if (coords) {
20073
+ left2 = coords.left - rect.left;
20074
+ top2 = coords.top - rect.top;
20075
+ }
20076
+ }
20077
+ if (!Number.isFinite(left2) || !Number.isFinite(top2)) {
20078
+ return null;
20079
+ }
20080
+ return { left: left2, top: top2 };
20081
+ }
20082
+ function findContainingBlockAncestor(element) {
20083
+ if (!element) return null;
20084
+ let current = element.parentElement;
20085
+ while (current && current !== document.body && current !== document.documentElement) {
20086
+ try {
20087
+ const style2 = window.getComputedStyle(current);
20088
+ const transform = style2.transform;
20089
+ const filter = style2.filter;
20090
+ const backdropFilter = style2.backdropFilter || style2.webkitBackdropFilter;
20091
+ const perspective = style2.perspective;
20092
+ const willChange = style2.willChange;
20093
+ const contain = style2.contain;
20094
+ if (transform && transform !== "none") {
20095
+ return current;
20096
+ }
20097
+ if (filter && filter !== "none") {
20098
+ return current;
20099
+ }
20100
+ if (backdropFilter && backdropFilter !== "none") {
20101
+ return current;
20102
+ }
20103
+ if (perspective && perspective !== "none") {
20104
+ return current;
20105
+ }
20106
+ if (willChange && willChange !== "auto") {
20107
+ const values = willChange.split(",").map((v) => v.trim());
20108
+ if (values.includes("transform") || values.includes("perspective")) {
20109
+ return current;
20110
+ }
20111
+ }
20112
+ if (contain && /paint|layout|strict|content/.test(contain)) {
20113
+ return current;
20114
+ }
20115
+ } catch (error) {
20116
+ console.warn("SlashMenu: Failed to get computed style for element", current, error);
20117
+ }
20118
+ current = current.parentElement;
20119
+ }
20120
+ return null;
20121
+ }
20122
+ const SlashMenuPluginKey = new superEditor_converter.PluginKey("slashMenu");
20123
+ const MENU_OFFSET_X = 100;
20124
+ const MENU_OFFSET_Y = 28;
20125
+ const CONTEXT_MENU_OFFSET_X = 10;
20126
+ const CONTEXT_MENU_OFFSET_Y = 10;
20127
+ const SLASH_COOLDOWN_MS = 5e3;
20128
+ const SlashMenu = Extension.create({
20129
+ name: "slashMenu",
20130
+ /**
20131
+ * Initialize default options for the SlashMenu extension
20132
+ * @returns {SlashMenuOptions} Empty options object (configuration is inherited from editor options)
20133
+ */
20134
+ addOptions() {
20135
+ return {};
20136
+ },
20137
+ addPmPlugins() {
20138
+ const editor = this.editor;
20139
+ if (editor.options?.isHeadless) {
20140
+ return [];
20141
+ }
20142
+ let slashCooldown = false;
20143
+ let slashCooldownTimeout = null;
20144
+ const isMenuDisabled = () => Boolean(editor.options?.disableContextMenu);
20145
+ const ensureStateShape = (value = {}) => ({
20146
+ open: false,
20147
+ selected: null,
20148
+ anchorPos: null,
20149
+ menuPosition: null,
20150
+ disabled: isMenuDisabled(),
20151
+ ...value
20152
+ });
20153
+ const slashMenuPlugin = new superEditor_converter.Plugin({
20154
+ key: SlashMenuPluginKey,
20155
+ state: {
20156
+ init: () => ensureStateShape(),
20157
+ /**
20158
+ * Apply transaction to update plugin state
20159
+ * Handles state transitions based on transaction metadata:
20160
+ * - 'open': Opens menu at specified position or cursor location
20161
+ * - 'select': Updates the selected menu item
20162
+ * - 'close': Closes the menu and clears anchor position
20163
+ * - 'updatePosition': Triggers menu position recalculation (no-op in apply)
20164
+ *
20165
+ * @param {import('prosemirror-state').Transaction} tr - The transaction
20166
+ * @param {SlashMenuState} value - Previous plugin state
20167
+ * @returns {SlashMenuState} New plugin state
20168
+ */
20169
+ apply(tr, value) {
20170
+ const meta = tr.getMeta(SlashMenuPluginKey);
20171
+ const disabled = isMenuDisabled();
20172
+ if (disabled) {
20173
+ if (value.open) {
20174
+ editor.emit("slashMenu:close");
20175
+ }
20176
+ return ensureStateShape({ disabled: true });
20177
+ }
20178
+ if (!meta) {
20179
+ if (value.disabled !== disabled) {
20180
+ return ensureStateShape({ ...value, disabled });
20181
+ }
20182
+ return value;
20183
+ }
20184
+ switch (meta.type) {
20185
+ case "open": {
20186
+ if (typeof meta.pos !== "number" || meta.pos < 0 || meta.pos > tr.doc.content.size) {
20187
+ console.warn("SlashMenu: Invalid position", meta.pos);
20188
+ return ensureStateShape(value);
20189
+ }
20190
+ let left2 = 0;
20191
+ let top2 = 0;
20192
+ let isContextMenu = false;
20193
+ if (typeof meta.clientX === "number" && typeof meta.clientY === "number") {
20194
+ left2 = meta.clientX;
20195
+ top2 = meta.clientY;
20196
+ isContextMenu = true;
20197
+ } else {
20198
+ const relativePoint = getSurfaceRelativePoint(editor, meta);
20199
+ if (relativePoint) {
20200
+ const surface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
20201
+ if (surface) {
20202
+ try {
20203
+ const rect = surface.getBoundingClientRect();
20204
+ left2 = rect.left + relativePoint.left;
20205
+ top2 = rect.top + relativePoint.top;
20206
+ } catch (error) {
20207
+ console.warn("SlashMenu: Failed to get surface bounds", error);
20208
+ return ensureStateShape(value);
20209
+ }
20210
+ }
20211
+ }
20212
+ }
20213
+ const menuSurface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
20214
+ const containingBlock = findContainingBlockAncestor(menuSurface);
20215
+ if (containingBlock) {
20216
+ try {
20217
+ const cbRect = containingBlock.getBoundingClientRect();
20218
+ left2 -= cbRect.left;
20219
+ top2 -= cbRect.top;
20220
+ left2 += containingBlock.scrollLeft || 0;
20221
+ top2 += containingBlock.scrollTop || 0;
20222
+ } catch (error) {
20223
+ console.warn("SlashMenu: Failed to adjust for containing block", error);
20224
+ }
20225
+ }
20226
+ const offsetX = isContextMenu ? CONTEXT_MENU_OFFSET_X : MENU_OFFSET_X;
20227
+ const offsetY = isContextMenu ? CONTEXT_MENU_OFFSET_Y : MENU_OFFSET_Y;
20228
+ const menuPosition = {
20229
+ left: `${left2 + offsetX}px`,
20230
+ top: `${top2 + offsetY}px`
20231
+ };
20232
+ const newState = {
20233
+ ...value,
20234
+ open: true,
20235
+ anchorPos: meta.pos,
20236
+ menuPosition
20237
+ };
20238
+ editor.emit("slashMenu:open", { menuPosition });
20239
+ return ensureStateShape(newState);
20240
+ }
20241
+ case "select": {
20242
+ return ensureStateShape({ ...value, selected: meta.id });
20243
+ }
20244
+ case "close": {
20245
+ editor.emit("slashMenu:close");
20246
+ return ensureStateShape({ ...value, open: false, anchorPos: null });
20247
+ }
20248
+ default:
20249
+ return ensureStateShape({ ...value, disabled });
20250
+ }
20251
+ }
20252
+ },
20253
+ /**
20254
+ * Create view plugin to handle window event listeners
20255
+ * @param {import('prosemirror-view').EditorView} editorView - The ProseMirror editor view
20256
+ * @returns {Object} View plugin with destroy method
20257
+ */
20258
+ view(editorView) {
20259
+ const updatePosition2 = () => {
20260
+ if (isMenuDisabled()) return;
20261
+ const state = SlashMenuPluginKey.getState(editorView.state);
20262
+ if (state.open) {
20263
+ editorView.dispatch(
20264
+ editorView.state.tr.setMeta(SlashMenuPluginKey, {
20265
+ type: "updatePosition"
20266
+ })
20267
+ );
20268
+ }
20269
+ };
20270
+ window.addEventListener("scroll", updatePosition2, true);
20271
+ window.addEventListener("resize", updatePosition2);
20272
+ return {
20273
+ destroy() {
20274
+ window.removeEventListener("scroll", updatePosition2, true);
20275
+ window.removeEventListener("resize", updatePosition2);
20276
+ if (slashCooldownTimeout) {
20277
+ clearTimeout(slashCooldownTimeout);
20278
+ slashCooldownTimeout = null;
20279
+ }
20280
+ }
20281
+ };
20282
+ },
20283
+ props: {
20284
+ /**
20285
+ * Handle keyboard events to open/close the slash menu
20286
+ * - '/': Opens menu at cursor if conditions are met (in paragraph, after space/start)
20287
+ * - 'Escape' or 'ArrowLeft': Closes menu and restores cursor position
20288
+ *
20289
+ * @param {import('prosemirror-view').EditorView} view - The ProseMirror editor view
20290
+ * @param {KeyboardEvent} event - The keyboard event
20291
+ * @returns {boolean} True if the event was handled, false otherwise
20292
+ */
20293
+ handleKeyDown(view, event) {
20294
+ if (isMenuDisabled()) {
20295
+ return false;
20296
+ }
20297
+ const pluginState = this.getState(view.state);
20298
+ if (event.key === "/" && slashCooldown) {
20299
+ return false;
20300
+ }
20301
+ if (event.key === "/" && !pluginState.open) {
20302
+ const { $cursor } = view.state.selection;
20303
+ if (!$cursor) return false;
20304
+ const isParagraph = $cursor.parent.type.name === "paragraph";
20305
+ if (!isParagraph) return false;
20306
+ const textBefore = $cursor.parent.textContent.slice(0, $cursor.parentOffset);
20307
+ const isEmptyOrAfterSpace = !textBefore || textBefore.endsWith(" ");
20308
+ if (!isEmptyOrAfterSpace) return false;
20309
+ event.preventDefault();
20310
+ slashCooldown = true;
20311
+ if (slashCooldownTimeout) clearTimeout(slashCooldownTimeout);
20312
+ slashCooldownTimeout = setTimeout(() => {
20313
+ slashCooldown = false;
20314
+ slashCooldownTimeout = null;
20315
+ }, SLASH_COOLDOWN_MS);
20316
+ view.dispatch(
20317
+ view.state.tr.setMeta(SlashMenuPluginKey, {
20318
+ type: "open",
20319
+ pos: $cursor.pos
20320
+ })
20321
+ );
20322
+ return true;
20323
+ }
20324
+ if (pluginState.open && (event.key === "Escape" || event.key === "ArrowLeft")) {
20325
+ const { anchorPos } = pluginState;
20326
+ view.dispatch(
20327
+ view.state.tr.setMeta(SlashMenuPluginKey, {
20328
+ type: "close"
20329
+ })
20330
+ );
20331
+ if (anchorPos !== null) {
20332
+ const tr = view.state.tr.setSelection(
20333
+ view.state.selection.constructor.near(view.state.doc.resolve(anchorPos))
20334
+ );
20335
+ view.dispatch(tr);
20336
+ view.focus();
20337
+ }
20338
+ return true;
20339
+ }
20340
+ return false;
20341
+ }
20342
+ }
20343
+ });
20344
+ return [slashMenuPlugin];
20345
+ }
20346
+ });
20054
20347
  let readFromCache;
20055
20348
  let addToCache;
20056
20349
  if (typeof WeakMap != "undefined") {
@@ -25084,21 +25377,21 @@ const SDT_CONTAINER_STYLES = `
25084
25377
 
25085
25378
  /* Structured content drag handle/label - positioned above */
25086
25379
  .superdoc-structured-content__label {
25087
- font-size: 10px;
25380
+ font-size: 11px;
25088
25381
  align-items: center;
25089
25382
  justify-content: center;
25090
25383
  position: absolute;
25091
25384
  left: 2px;
25092
25385
  top: -19px;
25093
25386
  width: calc(100% - 4px);
25094
- max-width: 110px;
25387
+ max-width: 130px;
25095
25388
  min-width: 0;
25096
25389
  height: 18px;
25097
25390
  padding: 0 4px;
25098
25391
  border: 1px solid #629be7;
25099
25392
  border-bottom: none;
25100
25393
  border-radius: 6px 6px 0 0;
25101
- background-color: #629be7dd;
25394
+ background-color: #629be7ee;
25102
25395
  box-sizing: border-box;
25103
25396
  z-index: 10;
25104
25397
  display: none;
@@ -25112,7 +25405,9 @@ const SDT_CONTAINER_STYLES = `
25112
25405
  text-overflow: ellipsis;
25113
25406
  }
25114
25407
 
25115
- .superdoc-structured-content-block:hover .superdoc-structured-content__label {
25408
+ /* Hover effect for block structured content (via event delegation class).
25409
+ * Shows label on hover — border reveal is handled by the lock-mode hover rule below. */
25410
+ .superdoc-structured-content-block.sdt-hover .superdoc-structured-content__label {
25116
25411
  display: inline-flex;
25117
25412
  }
25118
25413
 
@@ -25141,6 +25436,13 @@ const SDT_CONTAINER_STYLES = `
25141
25436
  border-bottom: none;
25142
25437
  }
25143
25438
 
25439
+ /* Collapse double borders between adjacent SDT blocks */
25440
+ .superdoc-structured-content-block + .superdoc-structured-content-block {
25441
+ border-top: none;
25442
+ border-top-left-radius: 0;
25443
+ border-top-right-radius: 0;
25444
+ }
25445
+
25144
25446
  /* Structured Content Inline - Inline wrapper with blue border */
25145
25447
  .superdoc-structured-content-inline {
25146
25448
  padding: 1px;
@@ -25152,21 +25454,15 @@ const SDT_CONTAINER_STYLES = `
25152
25454
  z-index: 10;
25153
25455
  }
25154
25456
 
25155
- /* Hover effect for inline structured content */
25156
- .superdoc-structured-content-inline:hover {
25157
- background-color: rgba(98, 155, 231, 0.15);
25158
- border-color: #4a8ad9;
25159
- }
25160
-
25161
25457
  /* Inline structured content label - shown on hover */
25162
25458
  .superdoc-structured-content-inline__label {
25163
25459
  position: absolute;
25164
25460
  bottom: calc(100% + 2px);
25165
25461
  left: 50%;
25166
25462
  transform: translateX(-50%);
25167
- font-size: 10px;
25168
- padding: 2px 6px;
25169
- background-color: #629be7dd;
25463
+ font-size: 11px;
25464
+ padding: 0 4px;
25465
+ background-color: #629be7ee;
25170
25466
  color: white;
25171
25467
  border-radius: 4px;
25172
25468
  white-space: nowrap;
@@ -25179,6 +25475,17 @@ const SDT_CONTAINER_STYLES = `
25179
25475
  display: block;
25180
25476
  }
25181
25477
 
25478
+ /* Hover highlight for SDT containers.
25479
+ * Blue border is always visible (set in base rules above).
25480
+ * Hover adds background highlight and z-index boost.
25481
+ * Block SDTs use .sdt-hover class (event delegation for multi-fragment coordination).
25482
+ * Inline SDTs use :hover (single element, no coordination needed). */
25483
+ .superdoc-structured-content-block[data-lock-mode].sdt-hover,
25484
+ .superdoc-structured-content-inline[data-lock-mode]:hover {
25485
+ background-color: rgba(98, 155, 231, 0.08);
25486
+ z-index: 9999999;
25487
+ }
25488
+
25182
25489
  /* Viewing mode: remove structured content affordances */
25183
25490
  .presentation-editor--viewing .superdoc-structured-content-block,
25184
25491
  .presentation-editor--viewing .superdoc-structured-content-inline {
@@ -25224,7 +25531,7 @@ const FIELD_ANNOTATION_STYLES = `
25224
25531
  .superdoc-layout .annotation *::selection {
25225
25532
  background: transparent;
25226
25533
  }
25227
-
25534
+
25228
25535
  .superdoc-layout .annotation::-moz-selection,
25229
25536
  .superdoc-layout .annotation *::-moz-selection {
25230
25537
  background: transparent;
@@ -25376,7 +25683,16 @@ const DOM_CLASS_NAMES = {
25376
25683
  * within this wrapper should be used for character-level positioning, not the
25377
25684
  * wrapper itself.
25378
25685
  */
25379
- INLINE_SDT_WRAPPER: "superdoc-structured-content-inline"
25686
+ INLINE_SDT_WRAPPER: "superdoc-structured-content-inline",
25687
+ /**
25688
+ * Class name for block-level structured content containers.
25689
+ */
25690
+ BLOCK_SDT: "superdoc-structured-content-block",
25691
+ /**
25692
+ * Class name added to block SDT fragments on hover via event delegation.
25693
+ * Applied/removed by SdtGroupedHover to highlight all fragments of the same SDT.
25694
+ */
25695
+ SDT_HOVER: "sdt-hover"
25380
25696
  };
25381
25697
  const DEFAULT_ALLOWED_PROTOCOLS = ["http", "https", "mailto", "tel", "sms"];
25382
25698
  const OPTIONAL_PROTOCOLS = ["ftp", "sftp", "irc"];
@@ -25781,6 +26097,11 @@ function applySdtContainerStyling(doc2, container, sdt, containerSdt, boundaryOp
25781
26097
  container.dataset.sdtContainerStart = String(isStart);
25782
26098
  container.dataset.sdtContainerEnd = String(isEnd);
25783
26099
  container.style.overflow = "visible";
26100
+ if (isStructuredContentMetadata(sdt)) {
26101
+ container.dataset.lockMode = sdt.lockMode || "unlocked";
26102
+ } else if (isStructuredContentMetadata(containerSdt)) {
26103
+ container.dataset.lockMode = containerSdt.lockMode || "unlocked";
26104
+ }
25784
26105
  if (boundaryOptions?.widthOverride != null) {
25785
26106
  container.style.width = `${boundaryOptions.widthOverride}px`;
25786
26107
  }
@@ -25793,6 +26114,19 @@ function applySdtContainerStyling(doc2, container, sdt, containerSdt, boundaryOp
25793
26114
  container.appendChild(labelEl);
25794
26115
  }
25795
26116
  }
26117
+ function shouldRebuildForSdtBoundary(element, boundary) {
26118
+ if (!boundary) {
26119
+ return element.dataset.sdtContainerStart !== void 0;
26120
+ }
26121
+ const startAttr = element.dataset.sdtContainerStart;
26122
+ const endAttr = element.dataset.sdtContainerEnd;
26123
+ const expectedStart = String(boundary.isStart ?? true);
26124
+ const expectedEnd = String(boundary.isEnd ?? true);
26125
+ if (startAttr === void 0 || endAttr === void 0) {
26126
+ return true;
26127
+ }
26128
+ return startAttr !== expectedStart || endAttr !== expectedEnd;
26129
+ }
25796
26130
  const TWIPS_PER_INCH$4 = 1440;
25797
26131
  const PX_PER_INCH$3 = 96;
25798
26132
  const PX_PER_PT = 96 / 72;
@@ -27271,6 +27605,65 @@ function assertFragmentPmPositions(fragment, _context) {
27271
27605
  const hasPmEnd = fragment.pmEnd != null;
27272
27606
  globalValidationStats.record(hasPmStart, hasPmEnd);
27273
27607
  }
27608
+ const SDT_BLOCK_SELECTOR = `.${DOM_CLASS_NAMES.BLOCK_SDT}[data-sdt-id]`;
27609
+ const HOVER_CLASS = DOM_CLASS_NAMES.SDT_HOVER;
27610
+ function sdtElementsById(root2, sdtId) {
27611
+ return root2.querySelectorAll(`.${DOM_CLASS_NAMES.BLOCK_SDT}[data-sdt-id="${sdtId}"]`);
27612
+ }
27613
+ class SdtGroupedHover {
27614
+ constructor() {
27615
+ this.hoveredSdtId = null;
27616
+ this.mount = null;
27617
+ this.onMouseOver = null;
27618
+ this.onMouseLeave = null;
27619
+ }
27620
+ /** Attach hover listeners to the mount element. Safe to call again on remount. */
27621
+ bind(mount2) {
27622
+ this.destroy();
27623
+ this.mount = mount2;
27624
+ this.onMouseOver = (e) => {
27625
+ const target = e.target.closest?.(SDT_BLOCK_SELECTOR);
27626
+ const sdtId = target?.dataset.sdtId ?? null;
27627
+ if (sdtId === this.hoveredSdtId) return;
27628
+ if (this.hoveredSdtId) {
27629
+ sdtElementsById(mount2, this.hoveredSdtId).forEach((el) => el.classList.remove(HOVER_CLASS));
27630
+ }
27631
+ this.hoveredSdtId = sdtId;
27632
+ if (sdtId) {
27633
+ sdtElementsById(mount2, sdtId).forEach((el) => el.classList.add(HOVER_CLASS));
27634
+ }
27635
+ };
27636
+ this.onMouseLeave = () => {
27637
+ if (this.hoveredSdtId) {
27638
+ sdtElementsById(mount2, this.hoveredSdtId).forEach((el) => el.classList.remove(HOVER_CLASS));
27639
+ this.hoveredSdtId = null;
27640
+ }
27641
+ };
27642
+ mount2.addEventListener("mouseover", this.onMouseOver);
27643
+ mount2.addEventListener("mouseleave", this.onMouseLeave);
27644
+ }
27645
+ /** Re-apply hover class after render. New/rebuilt elements lose the class. */
27646
+ reapply() {
27647
+ if (this.hoveredSdtId && this.mount) {
27648
+ sdtElementsById(this.mount, this.hoveredSdtId).forEach((el) => el.classList.add(HOVER_CLASS));
27649
+ }
27650
+ }
27651
+ /** Remove listeners and reset state. */
27652
+ destroy() {
27653
+ if (this.mount) {
27654
+ if (this.onMouseOver) {
27655
+ this.mount.removeEventListener("mouseover", this.onMouseOver);
27656
+ }
27657
+ if (this.onMouseLeave) {
27658
+ this.mount.removeEventListener("mouseleave", this.onMouseLeave);
27659
+ }
27660
+ }
27661
+ this.mount = null;
27662
+ this.onMouseOver = null;
27663
+ this.onMouseLeave = null;
27664
+ this.hoveredSdtId = null;
27665
+ }
27666
+ }
27274
27667
  const DEFAULT_PPI = 96;
27275
27668
  const DEFAULT_RULER_HEIGHT = 25;
27276
27669
  const TICK_SPACING_PX = DEFAULT_PPI / 8;
@@ -27979,6 +28372,7 @@ class DomPainter {
27979
28372
  this.onScrollHandler = null;
27980
28373
  this.onWindowScrollHandler = null;
27981
28374
  this.onResizeHandler = null;
28375
+ this.sdtHover = new SdtGroupedHover();
27982
28376
  this.activeCommentId = null;
27983
28377
  this.options = options;
27984
28378
  this.layoutMode = options.layoutMode ?? "vertical";
@@ -28244,6 +28638,7 @@ class DomPainter {
28244
28638
  };
28245
28639
  win.addEventListener("resize", this.onResizeHandler);
28246
28640
  }
28641
+ this.sdtHover.bind(mount2);
28247
28642
  }
28248
28643
  computeVirtualMetrics() {
28249
28644
  if (!this.currentLayout) return;
@@ -28386,6 +28781,7 @@ class DomPainter {
28386
28781
  }
28387
28782
  this.changedBlocks.clear();
28388
28783
  this.processedLayoutVersion = this.layoutVersion;
28784
+ this.sdtHover.reapply();
28389
28785
  }
28390
28786
  updateSpacers(start2, end2) {
28391
28787
  if (!this.topSpacerEl || !this.bottomSpacerEl) return;
@@ -28670,6 +29066,7 @@ class DomPainter {
28670
29066
  this.onScrollHandler = null;
28671
29067
  this.onWindowScrollHandler = null;
28672
29068
  this.onResizeHandler = null;
29069
+ this.sdtHover.destroy();
28673
29070
  this.layoutVersion = 0;
28674
29071
  this.processedLayoutVersion = -1;
28675
29072
  }
@@ -28731,7 +29128,9 @@ class DomPainter {
28731
29128
  if (current) {
28732
29129
  existing.delete(key2);
28733
29130
  const sdtBoundaryMismatch = shouldRebuildForSdtBoundary(current.element, sdtBoundary);
28734
- const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup) || sdtBoundaryMismatch;
29131
+ const newPmStart = fragment.pmStart;
29132
+ const mappingUnreliable = this.currentMapping != null && newPmStart != null && current.element.dataset.pmStart != null && this.currentMapping.map(Number(current.element.dataset.pmStart)) !== newPmStart;
29133
+ const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup) || sdtBoundaryMismatch || mappingUnreliable;
28735
29134
  if (needsRebuild) {
28736
29135
  const replacement = this.renderFragment(fragment, contextBase, sdtBoundary);
28737
29136
  pageEl.replaceChild(replacement, current.element);
@@ -30817,6 +31216,43 @@ class DomPainter {
30817
31216
  }
30818
31217
  return void 0;
30819
31218
  };
31219
+ let geoSdtWrapper = null;
31220
+ let geoSdtId = null;
31221
+ let geoSdtWrapperLeft = 0;
31222
+ let geoSdtMaxRight = 0;
31223
+ const closeGeoSdtWrapper = () => {
31224
+ if (geoSdtWrapper) {
31225
+ geoSdtWrapper.style.width = `${geoSdtMaxRight - geoSdtWrapperLeft}px`;
31226
+ el.appendChild(geoSdtWrapper);
31227
+ geoSdtWrapper = null;
31228
+ geoSdtId = null;
31229
+ }
31230
+ };
31231
+ const appendToLineGeo = (elem, runForSdt, elemLeftPx, elemWidthPx) => {
31232
+ const resolved = this.resolveRunSdtId(runForSdt);
31233
+ const thisRunSdtId = resolved?.sdtId ?? null;
31234
+ if (thisRunSdtId !== geoSdtId) {
31235
+ closeGeoSdtWrapper();
31236
+ }
31237
+ if (resolved && this.doc) {
31238
+ if (!geoSdtWrapper) {
31239
+ geoSdtWrapper = this.createInlineSdtWrapper(resolved.sdt);
31240
+ geoSdtId = thisRunSdtId;
31241
+ geoSdtWrapperLeft = elemLeftPx;
31242
+ geoSdtMaxRight = elemLeftPx;
31243
+ geoSdtWrapper.style.position = "absolute";
31244
+ geoSdtWrapper.style.left = `${elemLeftPx}px`;
31245
+ geoSdtWrapper.style.top = "0px";
31246
+ geoSdtWrapper.style.height = `${line.lineHeight}px`;
31247
+ }
31248
+ elem.style.left = `${elemLeftPx - geoSdtWrapperLeft}px`;
31249
+ geoSdtMaxRight = Math.max(geoSdtMaxRight, elemLeftPx + elemWidthPx);
31250
+ this.expandSdtWrapperPmRange(geoSdtWrapper, runForSdt.pmStart, runForSdt.pmEnd);
31251
+ geoSdtWrapper.appendChild(elem);
31252
+ } else {
31253
+ el.appendChild(elem);
31254
+ }
31255
+ };
30820
31256
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
30821
31257
  const baseRun = block.runs[runIndex];
30822
31258
  if (!baseRun) continue;
@@ -30848,7 +31284,7 @@ class DomPainter {
30848
31284
  if (baseRun.pmStart != null) tabEl.dataset.pmStart = String(baseRun.pmStart);
30849
31285
  if (baseRun.pmEnd != null) tabEl.dataset.pmEnd = String(baseRun.pmEnd);
30850
31286
  tabEl.dataset.layoutEpoch = String(this.layoutEpoch);
30851
- el.appendChild(tabEl);
31287
+ appendToLineGeo(tabEl, baseRun, tabStartX + indentOffset, actualTabWidth);
30852
31288
  cumulativeX = tabEndX;
30853
31289
  continue;
30854
31290
  }
@@ -30864,7 +31300,7 @@ class DomPainter {
30864
31300
  const segWidth = (runSegments2 && runSegments2[0]?.width !== void 0 ? runSegments2[0].width : elem.offsetWidth) ?? 0;
30865
31301
  elem.style.position = "absolute";
30866
31302
  elem.style.left = `${segX}px`;
30867
- el.appendChild(elem);
31303
+ appendToLineGeo(elem, baseRun, segX, segWidth);
30868
31304
  cumulativeX = baseSegX + segWidth;
30869
31305
  }
30870
31306
  continue;
@@ -30887,7 +31323,7 @@ class DomPainter {
30887
31323
  const segWidth = (runSegments2 && runSegments2[0]?.width !== void 0 ? runSegments2[0].width : 0) ?? 0;
30888
31324
  elem.style.position = "absolute";
30889
31325
  elem.style.left = `${segX}px`;
30890
- el.appendChild(elem);
31326
+ appendToLineGeo(elem, baseRun, segX, segWidth);
30891
31327
  cumulativeX = baseSegX + segWidth;
30892
31328
  }
30893
31329
  continue;
@@ -30922,7 +31358,7 @@ class DomPainter {
30922
31358
  const xPos = baseX + indentOffset;
30923
31359
  elem.style.position = "absolute";
30924
31360
  elem.style.left = `${xPos}px`;
30925
- el.appendChild(elem);
31361
+ appendToLineGeo(elem, segmentRun, xPos, segment.width ?? 0);
30926
31362
  let width = segment.width ?? 0;
30927
31363
  if (width <= 0 && this.doc) {
30928
31364
  const measureEl = elem.cloneNode(true);
@@ -30934,9 +31370,13 @@ class DomPainter {
30934
31370
  this.doc.body.removeChild(measureEl);
30935
31371
  }
30936
31372
  cumulativeX = baseX + width;
31373
+ if (geoSdtWrapper) {
31374
+ geoSdtMaxRight = Math.max(geoSdtMaxRight, xPos + width);
31375
+ }
30937
31376
  }
30938
31377
  });
30939
31378
  }
31379
+ closeGeoSdtWrapper();
30940
31380
  } else {
30941
31381
  let currentInlineSdtWrapper = null;
30942
31382
  let currentInlineSdtId = null;
@@ -30948,12 +31388,12 @@ class DomPainter {
30948
31388
  }
30949
31389
  };
30950
31390
  runsForLine.forEach((run) => {
30951
- const runSdt = run.sdt;
30952
- const isInlineSdt = runSdt?.type === "structuredContent" && runSdt?.scope === "inline";
30953
- const runSdtId = isInlineSdt && runSdt?.id ? String(runSdt.id) : null;
31391
+ const resolved = this.resolveRunSdtId(run);
31392
+ const runSdtId = resolved?.sdtId ?? null;
30954
31393
  if (runSdtId !== currentInlineSdtId) {
30955
31394
  closeCurrentWrapper();
30956
31395
  }
31396
+ let elem = null;
30957
31397
  if (run.kind === "tab") {
30958
31398
  const tabEl = this.doc.createElement("span");
30959
31399
  tabEl.classList.add("superdoc-tab");
@@ -30974,39 +31414,20 @@ class DomPainter {
30974
31414
  if (run.pmStart != null) tabEl.dataset.pmStart = String(run.pmStart);
30975
31415
  if (run.pmEnd != null) tabEl.dataset.pmEnd = String(run.pmEnd);
30976
31416
  tabEl.dataset.layoutEpoch = String(this.layoutEpoch);
30977
- el.appendChild(tabEl);
30978
- return;
31417
+ elem = tabEl;
31418
+ } else {
31419
+ elem = this.renderRun(run, context, trackedConfig);
30979
31420
  }
30980
- const elem = this.renderRun(run, context, trackedConfig);
30981
31421
  if (elem) {
30982
31422
  if (styleId) {
30983
31423
  elem.setAttribute("styleid", styleId);
30984
31424
  }
30985
- if (isInlineSdt && runSdtId && this.doc) {
31425
+ if (resolved && this.doc) {
30986
31426
  if (!currentInlineSdtWrapper) {
30987
- currentInlineSdtWrapper = this.doc.createElement("span");
30988
- currentInlineSdtWrapper.className = DOM_CLASS_NAMES.INLINE_SDT_WRAPPER;
30989
- currentInlineSdtWrapper.dataset.layoutEpoch = String(this.layoutEpoch);
31427
+ currentInlineSdtWrapper = this.createInlineSdtWrapper(resolved.sdt);
30990
31428
  currentInlineSdtId = runSdtId;
30991
- this.applySdtDataset(currentInlineSdtWrapper, runSdt);
30992
- const alias = runSdt?.alias || "Inline content";
30993
- const labelEl = this.doc.createElement("span");
30994
- labelEl.className = `${DOM_CLASS_NAMES.INLINE_SDT_WRAPPER}__label`;
30995
- labelEl.textContent = alias;
30996
- currentInlineSdtWrapper.appendChild(labelEl);
30997
- }
30998
- const wrapperPmStart = currentInlineSdtWrapper.dataset.pmStart;
30999
- const wrapperPmEnd = currentInlineSdtWrapper.dataset.pmEnd;
31000
- if (run.pmStart != null) {
31001
- if (!wrapperPmStart || run.pmStart < parseInt(wrapperPmStart, 10)) {
31002
- currentInlineSdtWrapper.dataset.pmStart = String(run.pmStart);
31003
- }
31004
- }
31005
- if (run.pmEnd != null) {
31006
- if (!wrapperPmEnd || run.pmEnd > parseInt(wrapperPmEnd, 10)) {
31007
- currentInlineSdtWrapper.dataset.pmEnd = String(run.pmEnd);
31008
- }
31009
31429
  }
31430
+ this.expandSdtWrapperPmRange(currentInlineSdtWrapper, run.pmStart, run.pmEnd);
31010
31431
  currentInlineSdtWrapper.appendChild(elem);
31011
31432
  } else {
31012
31433
  el.appendChild(elem);
@@ -31183,6 +31604,7 @@ class DomPainter {
31183
31604
  "sdtScope",
31184
31605
  "sdtTag",
31185
31606
  "sdtAlias",
31607
+ "lockMode",
31186
31608
  "sdtSectionTitle",
31187
31609
  "sdtSectionType",
31188
31610
  "sdtSectionLocked",
@@ -31207,6 +31629,49 @@ class DomPainter {
31207
31629
  el.dataset[key2] = String(value);
31208
31630
  }
31209
31631
  }
31632
+ /**
31633
+ * Resolve the inline SDT id from a run, or null if the run is not inside an inline SDT.
31634
+ */
31635
+ resolveRunSdtId(run) {
31636
+ const sdt = run.sdt;
31637
+ if (sdt?.type === "structuredContent" && sdt?.scope === "inline" && sdt?.id) {
31638
+ return { sdtId: String(sdt.id), sdt };
31639
+ }
31640
+ return null;
31641
+ }
31642
+ /**
31643
+ * Create an inline SDT wrapper `<span>` with className, layoutEpoch, dataset, and label.
31644
+ * Shared by both the geometry and run-based rendering paths.
31645
+ */
31646
+ createInlineSdtWrapper(sdt) {
31647
+ const wrapper = this.doc.createElement("span");
31648
+ wrapper.className = DOM_CLASS_NAMES.INLINE_SDT_WRAPPER;
31649
+ wrapper.dataset.layoutEpoch = String(this.layoutEpoch);
31650
+ this.applySdtDataset(wrapper, sdt);
31651
+ const alias = sdt?.alias || "Inline content";
31652
+ const labelEl = this.doc.createElement("span");
31653
+ labelEl.className = `${DOM_CLASS_NAMES.INLINE_SDT_WRAPPER}__label`;
31654
+ labelEl.textContent = alias;
31655
+ wrapper.appendChild(labelEl);
31656
+ return wrapper;
31657
+ }
31658
+ /**
31659
+ * Expand the PM position range tracked on an SDT wrapper to include a new run's range.
31660
+ */
31661
+ expandSdtWrapperPmRange(wrapper, pmStart, pmEnd) {
31662
+ if (pmStart != null) {
31663
+ const cur = wrapper.dataset.pmStart;
31664
+ if (!cur || pmStart < parseInt(cur, 10)) {
31665
+ wrapper.dataset.pmStart = String(pmStart);
31666
+ }
31667
+ }
31668
+ if (pmEnd != null) {
31669
+ const cur = wrapper.dataset.pmEnd;
31670
+ if (!cur || pmEnd > parseInt(cur, 10)) {
31671
+ wrapper.dataset.pmEnd = String(pmEnd);
31672
+ }
31673
+ }
31674
+ }
31210
31675
  /**
31211
31676
  * Applies SDT (Structured Document Tag) metadata to an element's dataset as data-sdt-* attributes.
31212
31677
  * Supports field annotations, structured content, document sections, and doc parts.
@@ -31234,6 +31699,7 @@ class DomPainter {
31234
31699
  this.setDatasetString(el, "sdtScope", metadata.scope);
31235
31700
  this.setDatasetString(el, "sdtTag", metadata.tag);
31236
31701
  this.setDatasetString(el, "sdtAlias", metadata.alias);
31702
+ this.setDatasetString(el, "lockMode", metadata.lockMode || "unlocked");
31237
31703
  } else if (metadata.type === "documentSection") {
31238
31704
  this.setDatasetString(el, "sdtSectionTitle", metadata.title);
31239
31705
  this.setDatasetString(el, "sdtSectionType", metadata.sectionType);
@@ -31322,17 +31788,6 @@ const computeSdtBoundaries = (fragments, blockLookup) => {
31322
31788
  }
31323
31789
  return boundaries;
31324
31790
  };
31325
- const shouldRebuildForSdtBoundary = (element, boundary) => {
31326
- if (!boundary) return false;
31327
- const startAttr = element.dataset.sdtContainerStart;
31328
- const endAttr = element.dataset.sdtContainerEnd;
31329
- const expectedStart = String(boundary.isStart ?? true);
31330
- const expectedEnd = String(boundary.isEnd ?? true);
31331
- if (startAttr === void 0 || endAttr === void 0) {
31332
- return true;
31333
- }
31334
- return startAttr !== expectedStart || endAttr !== expectedEnd;
31335
- };
31336
31791
  const fragmentKey = (fragment) => {
31337
31792
  if (fragment.kind === "para") {
31338
31793
  return `para:${fragment.blockId}:${fragment.fromLine}:${fragment.toLine}`;
@@ -40777,6 +41232,7 @@ function normalizeStructuredContentMetadata(nodeType, attrs) {
40777
41232
  id: toNullableString(attrs.id),
40778
41233
  tag: toOptionalString(attrs.tag),
40779
41234
  alias: toOptionalString(attrs.alias),
41235
+ lockMode: attrs.lockMode,
40780
41236
  sdtPr: attrs.sdtPr
40781
41237
  };
40782
41238
  }
@@ -41601,7 +42057,8 @@ function tabNodeToRun({
41601
42057
  positions,
41602
42058
  tabOrdinal,
41603
42059
  paragraphAttrs,
41604
- inheritedMarks
42060
+ inheritedMarks,
42061
+ sdtMetadata
41605
42062
  }) {
41606
42063
  const pos = positions.get(node);
41607
42064
  if (!pos) return null;
@@ -41617,6 +42074,9 @@ function tabNodeToRun({
41617
42074
  indent,
41618
42075
  leader: node.attrs?.leader ?? null
41619
42076
  };
42077
+ if (sdtMetadata) {
42078
+ run.sdt = sdtMetadata;
42079
+ }
41620
42080
  const marks = [...node.marks ?? [], ...inheritedMarks ?? []];
41621
42081
  if (marks.length > 0) {
41622
42082
  applyMarksToRun(run, marks);
@@ -64201,7 +64661,8 @@ class PresentationEditor extends EventEmitter {
64201
64661
  }
64202
64662
  const activeEditor = this.getActiveEditor();
64203
64663
  const hasFocus = activeEditor?.view?.hasFocus?.() ?? false;
64204
- if (!hasFocus) {
64664
+ const slashMenuOpen = activeEditor?.state ? !!SlashMenuPluginKey.getState(activeEditor.state)?.open : false;
64665
+ if (!hasFocus && !slashMenuOpen) {
64205
64666
  try {
64206
64667
  this.#clearSelectedFieldAnnotationClass();
64207
64668
  this.#localSelectionLayer.innerHTML = "";
@@ -66438,300 +66899,6 @@ const processAiHighlights = (editor, doc2, highlightColor, customClass = null) =
66438
66899
  });
66439
66900
  return { decorations };
66440
66901
  };
66441
- function getEditorSurfaceElement(editor) {
66442
- if (!editor) return null;
66443
- if (typeof editor.hitTest === "function" && editor.element instanceof HTMLElement) {
66444
- return editor.element;
66445
- }
66446
- return editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element ?? null;
66447
- }
66448
- function getSurfaceRelativePoint(editor, eventLocation = {}) {
66449
- const surface = getEditorSurfaceElement(editor);
66450
- if (!surface) return null;
66451
- const rect = surface.getBoundingClientRect();
66452
- let left2;
66453
- let top2;
66454
- if (typeof eventLocation.clientX === "number" && typeof eventLocation.clientY === "number") {
66455
- left2 = eventLocation.clientX - rect.left;
66456
- top2 = eventLocation.clientY - rect.top;
66457
- } else if (editor?.state?.selection) {
66458
- const selectionFrom = editor.state.selection.from;
66459
- const coords = editor.coordsAtPos?.(selectionFrom);
66460
- if (coords) {
66461
- left2 = coords.left - rect.left;
66462
- top2 = coords.top - rect.top;
66463
- }
66464
- }
66465
- if (!Number.isFinite(left2) || !Number.isFinite(top2)) {
66466
- return null;
66467
- }
66468
- return { left: left2, top: top2 };
66469
- }
66470
- function findContainingBlockAncestor(element) {
66471
- if (!element) return null;
66472
- let current = element.parentElement;
66473
- while (current && current !== document.body && current !== document.documentElement) {
66474
- try {
66475
- const style2 = window.getComputedStyle(current);
66476
- const transform = style2.transform;
66477
- const filter = style2.filter;
66478
- const backdropFilter = style2.backdropFilter || style2.webkitBackdropFilter;
66479
- const perspective = style2.perspective;
66480
- const willChange = style2.willChange;
66481
- const contain = style2.contain;
66482
- if (transform && transform !== "none") {
66483
- return current;
66484
- }
66485
- if (filter && filter !== "none") {
66486
- return current;
66487
- }
66488
- if (backdropFilter && backdropFilter !== "none") {
66489
- return current;
66490
- }
66491
- if (perspective && perspective !== "none") {
66492
- return current;
66493
- }
66494
- if (willChange && willChange !== "auto") {
66495
- const values = willChange.split(",").map((v) => v.trim());
66496
- if (values.includes("transform") || values.includes("perspective")) {
66497
- return current;
66498
- }
66499
- }
66500
- if (contain && /paint|layout|strict|content/.test(contain)) {
66501
- return current;
66502
- }
66503
- } catch (error) {
66504
- console.warn("SlashMenu: Failed to get computed style for element", current, error);
66505
- }
66506
- current = current.parentElement;
66507
- }
66508
- return null;
66509
- }
66510
- const SlashMenuPluginKey = new superEditor_converter.PluginKey("slashMenu");
66511
- const MENU_OFFSET_X = 100;
66512
- const MENU_OFFSET_Y = 28;
66513
- const CONTEXT_MENU_OFFSET_X = 10;
66514
- const CONTEXT_MENU_OFFSET_Y = 10;
66515
- const SLASH_COOLDOWN_MS = 5e3;
66516
- const SlashMenu = Extension.create({
66517
- name: "slashMenu",
66518
- /**
66519
- * Initialize default options for the SlashMenu extension
66520
- * @returns {SlashMenuOptions} Empty options object (configuration is inherited from editor options)
66521
- */
66522
- addOptions() {
66523
- return {};
66524
- },
66525
- addPmPlugins() {
66526
- const editor = this.editor;
66527
- if (editor.options?.isHeadless) {
66528
- return [];
66529
- }
66530
- let slashCooldown = false;
66531
- let slashCooldownTimeout = null;
66532
- const isMenuDisabled = () => Boolean(editor.options?.disableContextMenu);
66533
- const ensureStateShape = (value = {}) => ({
66534
- open: false,
66535
- selected: null,
66536
- anchorPos: null,
66537
- menuPosition: null,
66538
- disabled: isMenuDisabled(),
66539
- ...value
66540
- });
66541
- const slashMenuPlugin = new superEditor_converter.Plugin({
66542
- key: SlashMenuPluginKey,
66543
- state: {
66544
- init: () => ensureStateShape(),
66545
- /**
66546
- * Apply transaction to update plugin state
66547
- * Handles state transitions based on transaction metadata:
66548
- * - 'open': Opens menu at specified position or cursor location
66549
- * - 'select': Updates the selected menu item
66550
- * - 'close': Closes the menu and clears anchor position
66551
- * - 'updatePosition': Triggers menu position recalculation (no-op in apply)
66552
- *
66553
- * @param {import('prosemirror-state').Transaction} tr - The transaction
66554
- * @param {SlashMenuState} value - Previous plugin state
66555
- * @returns {SlashMenuState} New plugin state
66556
- */
66557
- apply(tr, value) {
66558
- const meta = tr.getMeta(SlashMenuPluginKey);
66559
- const disabled = isMenuDisabled();
66560
- if (disabled) {
66561
- if (value.open) {
66562
- editor.emit("slashMenu:close");
66563
- }
66564
- return ensureStateShape({ disabled: true });
66565
- }
66566
- if (!meta) {
66567
- if (value.disabled !== disabled) {
66568
- return ensureStateShape({ ...value, disabled });
66569
- }
66570
- return value;
66571
- }
66572
- switch (meta.type) {
66573
- case "open": {
66574
- if (typeof meta.pos !== "number" || meta.pos < 0 || meta.pos > tr.doc.content.size) {
66575
- console.warn("SlashMenu: Invalid position", meta.pos);
66576
- return ensureStateShape(value);
66577
- }
66578
- let left2 = 0;
66579
- let top2 = 0;
66580
- let isContextMenu = false;
66581
- if (typeof meta.clientX === "number" && typeof meta.clientY === "number") {
66582
- left2 = meta.clientX;
66583
- top2 = meta.clientY;
66584
- isContextMenu = true;
66585
- } else {
66586
- const relativePoint = getSurfaceRelativePoint(editor, meta);
66587
- if (relativePoint) {
66588
- const surface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
66589
- if (surface) {
66590
- try {
66591
- const rect = surface.getBoundingClientRect();
66592
- left2 = rect.left + relativePoint.left;
66593
- top2 = rect.top + relativePoint.top;
66594
- } catch (error) {
66595
- console.warn("SlashMenu: Failed to get surface bounds", error);
66596
- return ensureStateShape(value);
66597
- }
66598
- }
66599
- }
66600
- }
66601
- const menuSurface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
66602
- const containingBlock = findContainingBlockAncestor(menuSurface);
66603
- if (containingBlock) {
66604
- try {
66605
- const cbRect = containingBlock.getBoundingClientRect();
66606
- left2 -= cbRect.left;
66607
- top2 -= cbRect.top;
66608
- left2 += containingBlock.scrollLeft || 0;
66609
- top2 += containingBlock.scrollTop || 0;
66610
- } catch (error) {
66611
- console.warn("SlashMenu: Failed to adjust for containing block", error);
66612
- }
66613
- }
66614
- const offsetX = isContextMenu ? CONTEXT_MENU_OFFSET_X : MENU_OFFSET_X;
66615
- const offsetY = isContextMenu ? CONTEXT_MENU_OFFSET_Y : MENU_OFFSET_Y;
66616
- const menuPosition = {
66617
- left: `${left2 + offsetX}px`,
66618
- top: `${top2 + offsetY}px`
66619
- };
66620
- const newState = {
66621
- ...value,
66622
- open: true,
66623
- anchorPos: meta.pos,
66624
- menuPosition
66625
- };
66626
- editor.emit("slashMenu:open", { menuPosition });
66627
- return ensureStateShape(newState);
66628
- }
66629
- case "select": {
66630
- return ensureStateShape({ ...value, selected: meta.id });
66631
- }
66632
- case "close": {
66633
- editor.emit("slashMenu:close");
66634
- return ensureStateShape({ ...value, open: false, anchorPos: null });
66635
- }
66636
- default:
66637
- return ensureStateShape({ ...value, disabled });
66638
- }
66639
- }
66640
- },
66641
- /**
66642
- * Create view plugin to handle window event listeners
66643
- * @param {import('prosemirror-view').EditorView} editorView - The ProseMirror editor view
66644
- * @returns {Object} View plugin with destroy method
66645
- */
66646
- view(editorView) {
66647
- const updatePosition2 = () => {
66648
- if (isMenuDisabled()) return;
66649
- const state = SlashMenuPluginKey.getState(editorView.state);
66650
- if (state.open) {
66651
- editorView.dispatch(
66652
- editorView.state.tr.setMeta(SlashMenuPluginKey, {
66653
- type: "updatePosition"
66654
- })
66655
- );
66656
- }
66657
- };
66658
- window.addEventListener("scroll", updatePosition2, true);
66659
- window.addEventListener("resize", updatePosition2);
66660
- return {
66661
- destroy() {
66662
- window.removeEventListener("scroll", updatePosition2, true);
66663
- window.removeEventListener("resize", updatePosition2);
66664
- if (slashCooldownTimeout) {
66665
- clearTimeout(slashCooldownTimeout);
66666
- slashCooldownTimeout = null;
66667
- }
66668
- }
66669
- };
66670
- },
66671
- props: {
66672
- /**
66673
- * Handle keyboard events to open/close the slash menu
66674
- * - '/': Opens menu at cursor if conditions are met (in paragraph, after space/start)
66675
- * - 'Escape' or 'ArrowLeft': Closes menu and restores cursor position
66676
- *
66677
- * @param {import('prosemirror-view').EditorView} view - The ProseMirror editor view
66678
- * @param {KeyboardEvent} event - The keyboard event
66679
- * @returns {boolean} True if the event was handled, false otherwise
66680
- */
66681
- handleKeyDown(view, event) {
66682
- if (isMenuDisabled()) {
66683
- return false;
66684
- }
66685
- const pluginState = this.getState(view.state);
66686
- if (event.key === "/" && slashCooldown) {
66687
- return false;
66688
- }
66689
- if (event.key === "/" && !pluginState.open) {
66690
- const { $cursor } = view.state.selection;
66691
- if (!$cursor) return false;
66692
- const isParagraph = $cursor.parent.type.name === "paragraph";
66693
- if (!isParagraph) return false;
66694
- const textBefore = $cursor.parent.textContent.slice(0, $cursor.parentOffset);
66695
- const isEmptyOrAfterSpace = !textBefore || textBefore.endsWith(" ");
66696
- if (!isEmptyOrAfterSpace) return false;
66697
- event.preventDefault();
66698
- slashCooldown = true;
66699
- if (slashCooldownTimeout) clearTimeout(slashCooldownTimeout);
66700
- slashCooldownTimeout = setTimeout(() => {
66701
- slashCooldown = false;
66702
- slashCooldownTimeout = null;
66703
- }, SLASH_COOLDOWN_MS);
66704
- view.dispatch(
66705
- view.state.tr.setMeta(SlashMenuPluginKey, {
66706
- type: "open",
66707
- pos: $cursor.pos
66708
- })
66709
- );
66710
- return true;
66711
- }
66712
- if (pluginState.open && (event.key === "Escape" || event.key === "ArrowLeft")) {
66713
- const { anchorPos } = pluginState;
66714
- view.dispatch(
66715
- view.state.tr.setMeta(SlashMenuPluginKey, {
66716
- type: "close"
66717
- })
66718
- );
66719
- if (anchorPos !== null) {
66720
- const tr = view.state.tr.setSelection(
66721
- view.state.selection.constructor.near(view.state.doc.resolve(anchorPos))
66722
- );
66723
- view.dispatch(tr);
66724
- view.focus();
66725
- }
66726
- return true;
66727
- }
66728
- return false;
66729
- }
66730
- }
66731
- });
66732
- return [slashMenuPlugin];
66733
- }
66734
- });
66735
66902
  class StructuredContentViewBase {
66736
66903
  node;
66737
66904
  view;
@@ -66871,6 +67038,20 @@ class StructuredContentViewBase {
66871
67038
  dragHandle.append(textElement);
66872
67039
  return dragHandle;
66873
67040
  }
67041
+ isContentLocked() {
67042
+ const lockMode = this.node.attrs.lockMode;
67043
+ return lockMode === "contentLocked" || lockMode === "sdtContentLocked";
67044
+ }
67045
+ isSdtLocked() {
67046
+ const lockMode = this.node.attrs.lockMode;
67047
+ return lockMode === "sdtLocked" || lockMode === "sdtContentLocked";
67048
+ }
67049
+ updateContentEditability() {
67050
+ if (this.dom) {
67051
+ this.dom.classList.toggle("sd-structured-content--content-locked", this.isContentLocked());
67052
+ this.dom.classList.toggle("sd-structured-content--sdt-locked", this.isSdtLocked());
67053
+ }
67054
+ }
66874
67055
  onDragStart(event) {
66875
67056
  const { view } = this.editor;
66876
67057
  const target = event.target;
@@ -66926,10 +67107,12 @@ class StructuredContentInlineView extends StructuredContentViewBase {
66926
67107
  element.prepend(dragHandle);
66927
67108
  element.addEventListener("dragstart", (e) => this.onDragStart(e));
66928
67109
  this.root = element;
67110
+ this.updateContentEditability();
66929
67111
  }
66930
67112
  updateView() {
66931
67113
  const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
66932
67114
  updateDOMAttributes(this.dom, { ...domAttrs });
67115
+ this.updateContentEditability();
66933
67116
  }
66934
67117
  update(node, decorations, innerDecorations) {
66935
67118
  const result = super.update(node, decorations, innerDecorations);
@@ -66938,6 +67121,157 @@ class StructuredContentInlineView extends StructuredContentViewBase {
66938
67121
  return true;
66939
67122
  }
66940
67123
  }
67124
+ const STRUCTURED_CONTENT_LOCK_KEY = new superEditor_converter.PluginKey("structuredContentLock");
67125
+ function collectSDTNodes(doc2) {
67126
+ const sdtNodes = [];
67127
+ doc2.descendants((node, pos) => {
67128
+ if (node.type.name === "structuredContent" || node.type.name === "structuredContentBlock") {
67129
+ sdtNodes.push({
67130
+ type: node.type.name,
67131
+ lockMode: node.attrs.lockMode,
67132
+ pos,
67133
+ end: pos + node.nodeSize
67134
+ });
67135
+ }
67136
+ });
67137
+ return sdtNodes;
67138
+ }
67139
+ function checkLockViolation(sdtNodes, from3, to) {
67140
+ for (const sdt of sdtNodes) {
67141
+ const overlaps = from3 < sdt.end && to > sdt.pos;
67142
+ if (!overlaps) continue;
67143
+ const containsSDT = from3 <= sdt.pos && to >= sdt.end;
67144
+ const insideSDT = from3 >= sdt.pos && to <= sdt.end;
67145
+ const crossesStart = from3 < sdt.pos && to > sdt.pos && to < sdt.end;
67146
+ const crossesEnd = from3 > sdt.pos && from3 < sdt.end && to > sdt.end;
67147
+ const wouldDamageWrapper = containsSDT || crossesStart || crossesEnd;
67148
+ const wouldModifyContent = insideSDT && !containsSDT;
67149
+ const isSdtLocked = sdt.lockMode === "sdtLocked" || sdt.lockMode === "sdtContentLocked";
67150
+ const isContentLocked = sdt.lockMode === "contentLocked" || sdt.lockMode === "sdtContentLocked";
67151
+ if (isSdtLocked && wouldDamageWrapper) {
67152
+ return { blocked: true, reason: `Cannot delete SDT wrapper (${sdt.lockMode})` };
67153
+ }
67154
+ if (isContentLocked && wouldModifyContent) {
67155
+ return { blocked: true, reason: `Cannot modify content (${sdt.lockMode})` };
67156
+ }
67157
+ }
67158
+ return { blocked: false };
67159
+ }
67160
+ function createStructuredContentLockPlugin() {
67161
+ return new superEditor_converter.Plugin({
67162
+ key: STRUCTURED_CONTENT_LOCK_KEY,
67163
+ state: {
67164
+ init(_2, editorState) {
67165
+ return collectSDTNodes(editorState.doc);
67166
+ },
67167
+ apply(tr, cachedSDTNodes, _oldState, newState) {
67168
+ if (!tr.docChanged) return cachedSDTNodes;
67169
+ return collectSDTNodes(newState.doc);
67170
+ }
67171
+ },
67172
+ props: {
67173
+ /**
67174
+ * Intercept key events BEFORE any transaction is created.
67175
+ * This prevents the browser selection from getting out of sync.
67176
+ */
67177
+ handleKeyDown(view, event) {
67178
+ const { state } = view;
67179
+ const { selection } = state;
67180
+ const { from: from3, to } = selection;
67181
+ const isDelete = event.key === "Delete";
67182
+ const isBackspace = event.key === "Backspace";
67183
+ const isCut = (event.metaKey || event.ctrlKey) && event.key === "x";
67184
+ if (!isDelete && !isBackspace && !isCut) {
67185
+ return false;
67186
+ }
67187
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(state);
67188
+ if (sdtNodes.length === 0) {
67189
+ return false;
67190
+ }
67191
+ let affectedFrom = from3;
67192
+ let affectedTo = to;
67193
+ if (from3 === to) {
67194
+ if (isBackspace && from3 > 0) {
67195
+ affectedFrom = from3 - 1;
67196
+ } else if (isDelete && to < state.doc.content.size) {
67197
+ affectedTo = to + 1;
67198
+ }
67199
+ }
67200
+ const result = checkLockViolation(sdtNodes, affectedFrom, affectedTo);
67201
+ if (result.blocked) {
67202
+ event.preventDefault();
67203
+ return true;
67204
+ }
67205
+ return false;
67206
+ },
67207
+ /**
67208
+ * Handle text input (typing) for content-locked nodes
67209
+ */
67210
+ handleTextInput(view, from3, to, _text) {
67211
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(view.state);
67212
+ if (sdtNodes.length === 0) {
67213
+ return false;
67214
+ }
67215
+ const result = checkLockViolation(sdtNodes, from3, to);
67216
+ if (result.blocked) {
67217
+ return true;
67218
+ }
67219
+ return false;
67220
+ }
67221
+ },
67222
+ /**
67223
+ * Safety net: filter transactions that slip through
67224
+ * (e.g., programmatic changes, paste, drag-drop)
67225
+ */
67226
+ filterTransaction(tr, state) {
67227
+ if (!tr.docChanged) {
67228
+ return true;
67229
+ }
67230
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(state);
67231
+ if (sdtNodes.length === 0) {
67232
+ return true;
67233
+ }
67234
+ for (const step of tr.steps) {
67235
+ if (step.from === void 0 || step.to === void 0) {
67236
+ continue;
67237
+ }
67238
+ const result = checkLockViolation(sdtNodes, step.from, step.to);
67239
+ if (result.blocked) {
67240
+ return false;
67241
+ }
67242
+ }
67243
+ return true;
67244
+ }
67245
+ });
67246
+ }
67247
+ function createStructuredContentSelectPlugin() {
67248
+ return new superEditor_converter.Plugin({
67249
+ appendTransaction(transactions, oldState, newState) {
67250
+ const { selection } = newState;
67251
+ if (!selection.empty) return null;
67252
+ if (oldState.selection.eq(newState.selection)) return null;
67253
+ if (transactions.some((tr) => tr.docChanged)) return null;
67254
+ const $pos = selection.$from;
67255
+ for (let d = $pos.depth; d > 0; d--) {
67256
+ const node = $pos.node(d);
67257
+ if (node.type.name === "structuredContent") {
67258
+ const sdtStart = $pos.before(d);
67259
+ const contentFrom = $pos.start(d);
67260
+ const contentTo = $pos.end(d);
67261
+ if (contentFrom === contentTo) return null;
67262
+ const old$pos = oldState.selection.$from;
67263
+ for (let od = old$pos.depth; od > 0; od--) {
67264
+ if (old$pos.node(od).type.name === "structuredContent" && old$pos.before(od) === sdtStart) {
67265
+ return null;
67266
+ }
67267
+ }
67268
+ return newState.tr.setSelection(superEditor_converter.TextSelection.create(newState.doc, contentFrom, contentTo));
67269
+ }
67270
+ }
67271
+ return null;
67272
+ }
67273
+ });
67274
+ }
66941
67275
  const structuredContentClass = "sd-structured-content";
66942
67276
  const structuredContentInnerClass = "sd-structured-content__content";
66943
67277
  const StructuredContent = Node$1.create({
@@ -66983,6 +67317,14 @@ const StructuredContent = Node$1.create({
66983
67317
  return { "data-alias": attrs.alias };
66984
67318
  }
66985
67319
  },
67320
+ lockMode: {
67321
+ default: "unlocked",
67322
+ parseDOM: (elem) => elem.getAttribute("data-lock-mode") || "unlocked",
67323
+ renderDOM: (attrs) => {
67324
+ if (!attrs.lockMode || attrs.lockMode === "unlocked") return {};
67325
+ return { "data-lock-mode": attrs.lockMode };
67326
+ }
67327
+ },
66986
67328
  sdtPr: {
66987
67329
  rendered: false
66988
67330
  }
@@ -67000,6 +67342,9 @@ const StructuredContent = Node$1.create({
67000
67342
  0
67001
67343
  ];
67002
67344
  },
67345
+ addPmPlugins() {
67346
+ return [createStructuredContentLockPlugin(), createStructuredContentSelectPlugin()];
67347
+ },
67003
67348
  addNodeView() {
67004
67349
  return (props) => {
67005
67350
  return new StructuredContentInlineView({ ...props });
@@ -67034,10 +67379,12 @@ class StructuredContentBlockView extends StructuredContentViewBase {
67034
67379
  element.prepend(dragHandle);
67035
67380
  element.addEventListener("dragstart", (e) => this.onDragStart(e));
67036
67381
  this.root = element;
67382
+ this.updateContentEditability();
67037
67383
  }
67038
67384
  updateView() {
67039
67385
  const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
67040
67386
  updateDOMAttributes(this.dom, { ...domAttrs });
67387
+ this.updateContentEditability();
67041
67388
  }
67042
67389
  update(node, decorations, innerDecorations) {
67043
67390
  const result = super.update(node, decorations, innerDecorations);
@@ -67090,6 +67437,14 @@ const StructuredContentBlock = Node$1.create({
67090
67437
  return { "data-alias": attrs.alias };
67091
67438
  }
67092
67439
  },
67440
+ lockMode: {
67441
+ default: "unlocked",
67442
+ parseDOM: (elem) => elem.getAttribute("data-lock-mode") || "unlocked",
67443
+ renderDOM: (attrs) => {
67444
+ if (!attrs.lockMode || attrs.lockMode === "unlocked") return {};
67445
+ return { "data-lock-mode": attrs.lockMode };
67446
+ }
67447
+ },
67093
67448
  sdtPr: {
67094
67449
  rendered: false
67095
67450
  }
@@ -86366,18 +86721,25 @@ const _hoisted_3$8 = {
86366
86721
  };
86367
86722
  const _hoisted_4$4 = {
86368
86723
  key: 3,
86369
- class: "link-input-wrapper"
86724
+ class: "link-title"
86370
86725
  };
86371
- const _hoisted_5$2 = { class: "input-row text-input-row" };
86372
- const _hoisted_6$1 = ["onKeydown"];
86373
- const _hoisted_7$1 = { class: "input-row url-input-row" };
86374
- const _hoisted_8$1 = ["innerHTML"];
86375
- const _hoisted_9 = ["onKeydown"];
86376
- const _hoisted_10 = ["innerHTML"];
86377
- const _hoisted_11 = { class: "input-row link-buttons" };
86378
- const _hoisted_12 = ["innerHTML"];
86379
- const _hoisted_13 = {
86726
+ const _hoisted_5$2 = {
86380
86727
  key: 4,
86728
+ class: "link-input-wrapper"
86729
+ };
86730
+ const _hoisted_6$1 = { class: "input-row text-input-row" };
86731
+ const _hoisted_7$1 = ["readonly"];
86732
+ const _hoisted_8$1 = { class: "input-row url-input-row" };
86733
+ const _hoisted_9 = ["innerHTML"];
86734
+ const _hoisted_10 = ["readonly", "onKeydown"];
86735
+ const _hoisted_11 = ["innerHTML"];
86736
+ const _hoisted_12 = {
86737
+ key: 0,
86738
+ class: "input-row link-buttons"
86739
+ };
86740
+ const _hoisted_13 = ["innerHTML"];
86741
+ const _hoisted_14 = {
86742
+ key: 5,
86381
86743
  class: "input-row go-to-anchor clickable"
86382
86744
  };
86383
86745
  const _sfc_main$j = {
@@ -86471,6 +86833,7 @@ const _sfc_main$j = {
86471
86833
  });
86472
86834
  const isEditing = vue.computed(() => !isAnchor.value && !!getLinkHrefAtSelection());
86473
86835
  const isDisabled2 = vue.computed(() => !validUrl.value);
86836
+ const isViewingMode = vue.computed(() => props.editor?.options?.documentMode === "viewing");
86474
86837
  const openLink = () => {
86475
86838
  window.open(url.value, "_blank");
86476
86839
  };
@@ -86496,6 +86859,7 @@ const _sfc_main$j = {
86496
86859
  if (props.showInput) focusInput();
86497
86860
  });
86498
86861
  const handleSubmit = () => {
86862
+ if (isViewingMode.value) return;
86499
86863
  const editor = props.editor;
86500
86864
  if (!editor) return;
86501
86865
  if (!rawUrl.value) {
@@ -86526,36 +86890,38 @@ const _sfc_main$j = {
86526
86890
  return vue.openBlock(), vue.createElementBlock("div", {
86527
86891
  class: vue.normalizeClass(["link-input-ctn", { "high-contrast": vue.unref(isHighContrastMode2) }])
86528
86892
  }, [
86529
- isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$g, "Page anchor")) : isEditing.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$b, "Edit link")) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$8, "Add link")),
86530
- __props.showInput && !isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$4, [
86531
- vue.createBaseVNode("div", _hoisted_5$2, [
86532
- _cache[4] || (_cache[4] = vue.createBaseVNode("div", { class: "input-icon text-input-icon" }, "T", -1)),
86893
+ isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$g, "Page anchor")) : isViewingMode.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$b, "Link details")) : isEditing.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$8, "Edit link")) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$4, "Add link")),
86894
+ __props.showInput && !isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$2, [
86895
+ vue.createBaseVNode("div", _hoisted_6$1, [
86896
+ _cache[5] || (_cache[5] = vue.createBaseVNode("div", { class: "input-icon text-input-icon" }, "T", -1)),
86533
86897
  vue.withDirectives(vue.createBaseVNode("input", {
86534
86898
  type: "text",
86535
86899
  name: "text",
86536
86900
  placeholder: "Text",
86537
86901
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => text.value = $event),
86538
- onKeydown: vue.withKeys(vue.withModifiers(handleSubmit, ["stop", "prevent"]), ["enter"])
86539
- }, null, 40, _hoisted_6$1), [
86902
+ readonly: isViewingMode.value,
86903
+ onKeydown: _cache[1] || (_cache[1] = vue.withKeys(vue.withModifiers(($event) => !isViewingMode.value && handleSubmit, ["stop", "prevent"]), ["enter"]))
86904
+ }, null, 40, _hoisted_7$1), [
86540
86905
  [vue.vModelText, text.value]
86541
86906
  ])
86542
86907
  ]),
86543
- vue.createBaseVNode("div", _hoisted_7$1, [
86908
+ vue.createBaseVNode("div", _hoisted_8$1, [
86544
86909
  vue.createBaseVNode("div", {
86545
86910
  class: "input-icon",
86546
86911
  innerHTML: vue.unref(toolbarIcons).linkInput
86547
- }, null, 8, _hoisted_8$1),
86912
+ }, null, 8, _hoisted_9),
86548
86913
  vue.withDirectives(vue.createBaseVNode("input", {
86549
86914
  type: "text",
86550
86915
  name: "link",
86551
86916
  placeholder: "Type or paste a link",
86552
86917
  class: vue.normalizeClass({ error: urlError.value }),
86553
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => rawUrl.value = $event),
86918
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => rawUrl.value = $event),
86919
+ readonly: isViewingMode.value,
86554
86920
  onKeydown: [
86555
86921
  vue.withKeys(vue.withModifiers(handleSubmit, ["stop", "prevent"]), ["enter"]),
86556
- _cache[2] || (_cache[2] = ($event) => urlError.value = false)
86922
+ _cache[3] || (_cache[3] = ($event) => urlError.value = false)
86557
86923
  ]
86558
- }, null, 42, _hoisted_9), [
86924
+ }, null, 42, _hoisted_10), [
86559
86925
  [vue.vModelText, rawUrl.value]
86560
86926
  ]),
86561
86927
  vue.createBaseVNode("div", {
@@ -86563,9 +86929,9 @@ const _sfc_main$j = {
86563
86929
  innerHTML: vue.unref(toolbarIcons).openLink,
86564
86930
  onClick: openLink,
86565
86931
  "data-item": "btn-link-open"
86566
- }, null, 10, _hoisted_10)
86932
+ }, null, 10, _hoisted_11)
86567
86933
  ]),
86568
- vue.createBaseVNode("div", _hoisted_11, [
86934
+ !isViewingMode.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12, [
86569
86935
  isEditing.value ? (vue.openBlock(), vue.createElementBlock("button", {
86570
86936
  key: 0,
86571
86937
  class: "remove-btn",
@@ -86575,25 +86941,25 @@ const _sfc_main$j = {
86575
86941
  vue.createBaseVNode("div", {
86576
86942
  class: "remove-btn__icon",
86577
86943
  innerHTML: vue.unref(toolbarIcons).removeLink
86578
- }, null, 8, _hoisted_12),
86579
- _cache[5] || (_cache[5] = vue.createTextVNode(" Remove ", -1))
86944
+ }, null, 8, _hoisted_13),
86945
+ _cache[6] || (_cache[6] = vue.createTextVNode(" Remove ", -1))
86580
86946
  ])) : vue.createCommentVNode("", true),
86581
86947
  vue.createBaseVNode("button", {
86582
86948
  class: vue.normalizeClass(["submit-btn", { "disable-btn": isDisabled2.value }]),
86583
86949
  onClick: handleSubmit,
86584
86950
  "data-item": "btn-link-apply"
86585
86951
  }, " Apply ", 2)
86586
- ])
86587
- ])) : isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_13, [
86952
+ ])) : vue.createCommentVNode("", true)
86953
+ ])) : isAnchor.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_14, [
86588
86954
  vue.createBaseVNode("a", {
86589
- onClick: _cache[3] || (_cache[3] = vue.withModifiers((...args) => __props.goToAnchor && __props.goToAnchor(...args), ["stop", "prevent"]))
86955
+ onClick: _cache[4] || (_cache[4] = vue.withModifiers((...args) => __props.goToAnchor && __props.goToAnchor(...args), ["stop", "prevent"]))
86590
86956
  }, "Go to " + vue.toDisplayString(rawUrl.value.startsWith("#_") ? rawUrl.value.substring(2) : rawUrl.value), 1)
86591
86957
  ])) : vue.createCommentVNode("", true)
86592
86958
  ], 2);
86593
86959
  };
86594
86960
  }
86595
86961
  };
86596
- const LinkInput = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["__scopeId", "data-v-f64d35e8"]]);
86962
+ const LinkInput = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["__scopeId", "data-v-198fc596"]]);
86597
86963
  const _hoisted_1$f = ["aria-label", "onClick", "onKeydown"];
86598
86964
  const _hoisted_2$a = ["innerHTML"];
86599
86965
  const _hoisted_3$7 = ["innerHTML"];