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
@@ -13908,6 +13908,10 @@
13908
13908
  const id = sdtPr?.elements?.find((el) => el.name === "w:id");
13909
13909
  const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
13910
13910
  const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
13911
+ const lockTag = sdtPr?.elements?.find((el) => el.name === "w:lock");
13912
+ const lockValue = lockTag?.attributes?.["w:val"];
13913
+ const validModes = ["unlocked", "sdtLocked", "contentLocked", "sdtContentLocked"];
13914
+ const lockMode = validModes.includes(lockValue) ? lockValue : "unlocked";
13911
13915
  if (!sdtContent) {
13912
13916
  return null;
13913
13917
  }
@@ -13929,6 +13933,7 @@
13929
13933
  id: id?.attributes?.["w:val"] || null,
13930
13934
  tag: tag?.attributes?.["w:val"] || null,
13931
13935
  alias: alias?.attributes?.["w:val"] || null,
13936
+ lockMode,
13932
13937
  sdtPr
13933
13938
  }
13934
13939
  };
@@ -30649,13 +30654,19 @@ Please report this to https://github.com/markedjs/marked.`, e) {
30649
30654
  type: "element",
30650
30655
  attributes: { "w:val": attrs.tag }
30651
30656
  };
30657
+ const lock = {
30658
+ name: "w:lock",
30659
+ type: "element",
30660
+ attributes: { "w:val": attrs.lockMode }
30661
+ };
30652
30662
  const resultElements = [];
30653
30663
  if (attrs.id) resultElements.push(id);
30654
30664
  if (attrs.alias) resultElements.push(alias);
30655
30665
  if (attrs.tag) resultElements.push(tag);
30666
+ if (attrs.lockMode && attrs.lockMode !== "unlocked") resultElements.push(lock);
30656
30667
  if (attrs.sdtPr) {
30657
30668
  const elements = attrs.sdtPr.elements || [];
30658
- const elementsToExclude = ["w:id", "w:alias", "w:tag"];
30669
+ const elementsToExclude = ["w:id", "w:alias", "w:tag", "w:lock"];
30659
30670
  const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
30660
30671
  const result2 = {
30661
30672
  name: "w:sdtPr",
@@ -34367,7 +34378,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
34367
34378
  const COMMUNITY_LICENSE_KEY = "community-and-eval-agplv3";
34368
34379
  function getSuperdocVersion() {
34369
34380
  try {
34370
- return true ? "1.12.0-next.11" : "unknown";
34381
+ return true ? "1.12.0-next.13" : "unknown";
34371
34382
  } catch {
34372
34383
  return "unknown";
34373
34384
  }
@@ -36643,7 +36654,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
36643
36654
  }
36644
36655
  #replaceSpecialCharacters(text2) {
36645
36656
  if (text2 === void 0 || text2 === null) return text2;
36646
- return String(text2).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
36657
+ return String(text2).replace(/&(?!#\d+;|#x[0-9a-fA-F]+;|(?:amp|lt|gt|quot|apos);)/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
36647
36658
  }
36648
36659
  /**
36649
36660
  * Recursively generates XML string representation from a JSON node structure.
@@ -37639,7 +37650,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
37639
37650
  static getStoredSuperdocVersion(docx) {
37640
37651
  return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
37641
37652
  }
37642
- static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.12.0-next.11") {
37653
+ static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.12.0-next.13") {
37643
37654
  return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
37644
37655
  }
37645
37656
  /**
@@ -65116,7 +65127,7 @@ ${err.toString()}`);
65116
65127
  return false;
65117
65128
  }
65118
65129
  };
65119
- const summaryVersion = "1.12.0-next.11";
65130
+ const summaryVersion = "1.12.0-next.13";
65120
65131
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
65121
65132
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
65122
65133
  function mapAttributes(attrs) {
@@ -67531,8 +67542,7 @@ ${err.toString()}`);
67531
67542
  "docProps/custom.xml": String(customXml),
67532
67543
  "word/_rels/document.xml.rels": String(rels),
67533
67544
  "word/numbering.xml": String(numbering),
67534
- // Replace & with &amp; in styles.xml as DOCX viewers can't handle it
67535
- "word/styles.xml": String(styles).replace(/&/gi, "&amp;"),
67545
+ "word/styles.xml": String(styles),
67536
67546
  ...updatedHeadersFooters,
67537
67547
  ...coreXml ? { "docProps/core.xml": String(coreXml) } : {}
67538
67548
  };
@@ -67922,7 +67932,7 @@ ${err.toString()}`);
67922
67932
  * Process collaboration migrations
67923
67933
  */
67924
67934
  processCollaborationMigrations() {
67925
- console.debug("[checkVersionMigrations] Current editor version", "1.12.0-next.11");
67935
+ console.debug("[checkVersionMigrations] Current editor version", "1.12.0-next.13");
67926
67936
  if (!this.options.ydoc) return;
67927
67937
  const metaMap = this.options.ydoc.getMap("meta");
67928
67938
  let docVersion = metaMap.get("version");
@@ -68100,6 +68110,300 @@ ${err.toString()}`);
68100
68110
  this.#renderer?.initDevTools?.(this);
68101
68111
  }
68102
68112
  }
68113
+ function getEditorSurfaceElement(editor) {
68114
+ if (!editor) return null;
68115
+ if (typeof editor.hitTest === "function" && editor.element instanceof HTMLElement) {
68116
+ return editor.element;
68117
+ }
68118
+ return editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element ?? null;
68119
+ }
68120
+ function getSurfaceRelativePoint(editor, eventLocation = {}) {
68121
+ const surface = getEditorSurfaceElement(editor);
68122
+ if (!surface) return null;
68123
+ const rect = surface.getBoundingClientRect();
68124
+ let left2;
68125
+ let top2;
68126
+ if (typeof eventLocation.clientX === "number" && typeof eventLocation.clientY === "number") {
68127
+ left2 = eventLocation.clientX - rect.left;
68128
+ top2 = eventLocation.clientY - rect.top;
68129
+ } else if (editor?.state?.selection) {
68130
+ const selectionFrom = editor.state.selection.from;
68131
+ const coords = editor.coordsAtPos?.(selectionFrom);
68132
+ if (coords) {
68133
+ left2 = coords.left - rect.left;
68134
+ top2 = coords.top - rect.top;
68135
+ }
68136
+ }
68137
+ if (!Number.isFinite(left2) || !Number.isFinite(top2)) {
68138
+ return null;
68139
+ }
68140
+ return { left: left2, top: top2 };
68141
+ }
68142
+ function findContainingBlockAncestor(element2) {
68143
+ if (!element2) return null;
68144
+ let current = element2.parentElement;
68145
+ while (current && current !== document.body && current !== document.documentElement) {
68146
+ try {
68147
+ const style2 = window.getComputedStyle(current);
68148
+ const transform = style2.transform;
68149
+ const filter = style2.filter;
68150
+ const backdropFilter = style2.backdropFilter || style2.webkitBackdropFilter;
68151
+ const perspective = style2.perspective;
68152
+ const willChange = style2.willChange;
68153
+ const contain = style2.contain;
68154
+ if (transform && transform !== "none") {
68155
+ return current;
68156
+ }
68157
+ if (filter && filter !== "none") {
68158
+ return current;
68159
+ }
68160
+ if (backdropFilter && backdropFilter !== "none") {
68161
+ return current;
68162
+ }
68163
+ if (perspective && perspective !== "none") {
68164
+ return current;
68165
+ }
68166
+ if (willChange && willChange !== "auto") {
68167
+ const values = willChange.split(",").map((v2) => v2.trim());
68168
+ if (values.includes("transform") || values.includes("perspective")) {
68169
+ return current;
68170
+ }
68171
+ }
68172
+ if (contain && /paint|layout|strict|content/.test(contain)) {
68173
+ return current;
68174
+ }
68175
+ } catch (error) {
68176
+ console.warn("SlashMenu: Failed to get computed style for element", current, error);
68177
+ }
68178
+ current = current.parentElement;
68179
+ }
68180
+ return null;
68181
+ }
68182
+ const SlashMenuPluginKey = new PluginKey("slashMenu");
68183
+ const MENU_OFFSET_X = 100;
68184
+ const MENU_OFFSET_Y = 28;
68185
+ const CONTEXT_MENU_OFFSET_X = 10;
68186
+ const CONTEXT_MENU_OFFSET_Y = 10;
68187
+ const SLASH_COOLDOWN_MS = 5e3;
68188
+ const SlashMenu = Extension.create({
68189
+ name: "slashMenu",
68190
+ /**
68191
+ * Initialize default options for the SlashMenu extension
68192
+ * @returns {SlashMenuOptions} Empty options object (configuration is inherited from editor options)
68193
+ */
68194
+ addOptions() {
68195
+ return {};
68196
+ },
68197
+ addPmPlugins() {
68198
+ const editor = this.editor;
68199
+ if (editor.options?.isHeadless) {
68200
+ return [];
68201
+ }
68202
+ let slashCooldown = false;
68203
+ let slashCooldownTimeout = null;
68204
+ const isMenuDisabled = () => Boolean(editor.options?.disableContextMenu);
68205
+ const ensureStateShape = (value = {}) => ({
68206
+ open: false,
68207
+ selected: null,
68208
+ anchorPos: null,
68209
+ menuPosition: null,
68210
+ disabled: isMenuDisabled(),
68211
+ ...value
68212
+ });
68213
+ const slashMenuPlugin = new Plugin({
68214
+ key: SlashMenuPluginKey,
68215
+ state: {
68216
+ init: () => ensureStateShape(),
68217
+ /**
68218
+ * Apply transaction to update plugin state
68219
+ * Handles state transitions based on transaction metadata:
68220
+ * - 'open': Opens menu at specified position or cursor location
68221
+ * - 'select': Updates the selected menu item
68222
+ * - 'close': Closes the menu and clears anchor position
68223
+ * - 'updatePosition': Triggers menu position recalculation (no-op in apply)
68224
+ *
68225
+ * @param {import('prosemirror-state').Transaction} tr - The transaction
68226
+ * @param {SlashMenuState} value - Previous plugin state
68227
+ * @returns {SlashMenuState} New plugin state
68228
+ */
68229
+ apply(tr, value) {
68230
+ const meta2 = tr.getMeta(SlashMenuPluginKey);
68231
+ const disabled = isMenuDisabled();
68232
+ if (disabled) {
68233
+ if (value.open) {
68234
+ editor.emit("slashMenu:close");
68235
+ }
68236
+ return ensureStateShape({ disabled: true });
68237
+ }
68238
+ if (!meta2) {
68239
+ if (value.disabled !== disabled) {
68240
+ return ensureStateShape({ ...value, disabled });
68241
+ }
68242
+ return value;
68243
+ }
68244
+ switch (meta2.type) {
68245
+ case "open": {
68246
+ if (typeof meta2.pos !== "number" || meta2.pos < 0 || meta2.pos > tr.doc.content.size) {
68247
+ console.warn("SlashMenu: Invalid position", meta2.pos);
68248
+ return ensureStateShape(value);
68249
+ }
68250
+ let left2 = 0;
68251
+ let top2 = 0;
68252
+ let isContextMenu = false;
68253
+ if (typeof meta2.clientX === "number" && typeof meta2.clientY === "number") {
68254
+ left2 = meta2.clientX;
68255
+ top2 = meta2.clientY;
68256
+ isContextMenu = true;
68257
+ } else {
68258
+ const relativePoint = getSurfaceRelativePoint(editor, meta2);
68259
+ if (relativePoint) {
68260
+ const surface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
68261
+ if (surface) {
68262
+ try {
68263
+ const rect = surface.getBoundingClientRect();
68264
+ left2 = rect.left + relativePoint.left;
68265
+ top2 = rect.top + relativePoint.top;
68266
+ } catch (error) {
68267
+ console.warn("SlashMenu: Failed to get surface bounds", error);
68268
+ return ensureStateShape(value);
68269
+ }
68270
+ }
68271
+ }
68272
+ }
68273
+ const menuSurface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
68274
+ const containingBlock = findContainingBlockAncestor(menuSurface);
68275
+ if (containingBlock) {
68276
+ try {
68277
+ const cbRect = containingBlock.getBoundingClientRect();
68278
+ left2 -= cbRect.left;
68279
+ top2 -= cbRect.top;
68280
+ left2 += containingBlock.scrollLeft || 0;
68281
+ top2 += containingBlock.scrollTop || 0;
68282
+ } catch (error) {
68283
+ console.warn("SlashMenu: Failed to adjust for containing block", error);
68284
+ }
68285
+ }
68286
+ const offsetX = isContextMenu ? CONTEXT_MENU_OFFSET_X : MENU_OFFSET_X;
68287
+ const offsetY = isContextMenu ? CONTEXT_MENU_OFFSET_Y : MENU_OFFSET_Y;
68288
+ const menuPosition = {
68289
+ left: `${left2 + offsetX}px`,
68290
+ top: `${top2 + offsetY}px`
68291
+ };
68292
+ const newState = {
68293
+ ...value,
68294
+ open: true,
68295
+ anchorPos: meta2.pos,
68296
+ menuPosition
68297
+ };
68298
+ editor.emit("slashMenu:open", { menuPosition });
68299
+ return ensureStateShape(newState);
68300
+ }
68301
+ case "select": {
68302
+ return ensureStateShape({ ...value, selected: meta2.id });
68303
+ }
68304
+ case "close": {
68305
+ editor.emit("slashMenu:close");
68306
+ return ensureStateShape({ ...value, open: false, anchorPos: null });
68307
+ }
68308
+ default:
68309
+ return ensureStateShape({ ...value, disabled });
68310
+ }
68311
+ }
68312
+ },
68313
+ /**
68314
+ * Create view plugin to handle window event listeners
68315
+ * @param {import('prosemirror-view').EditorView} editorView - The ProseMirror editor view
68316
+ * @returns {Object} View plugin with destroy method
68317
+ */
68318
+ view(editorView) {
68319
+ const updatePosition2 = () => {
68320
+ if (isMenuDisabled()) return;
68321
+ const state = SlashMenuPluginKey.getState(editorView.state);
68322
+ if (state.open) {
68323
+ editorView.dispatch(
68324
+ editorView.state.tr.setMeta(SlashMenuPluginKey, {
68325
+ type: "updatePosition"
68326
+ })
68327
+ );
68328
+ }
68329
+ };
68330
+ window.addEventListener("scroll", updatePosition2, true);
68331
+ window.addEventListener("resize", updatePosition2);
68332
+ return {
68333
+ destroy() {
68334
+ window.removeEventListener("scroll", updatePosition2, true);
68335
+ window.removeEventListener("resize", updatePosition2);
68336
+ if (slashCooldownTimeout) {
68337
+ clearTimeout(slashCooldownTimeout);
68338
+ slashCooldownTimeout = null;
68339
+ }
68340
+ }
68341
+ };
68342
+ },
68343
+ props: {
68344
+ /**
68345
+ * Handle keyboard events to open/close the slash menu
68346
+ * - '/': Opens menu at cursor if conditions are met (in paragraph, after space/start)
68347
+ * - 'Escape' or 'ArrowLeft': Closes menu and restores cursor position
68348
+ *
68349
+ * @param {import('prosemirror-view').EditorView} view - The ProseMirror editor view
68350
+ * @param {KeyboardEvent} event - The keyboard event
68351
+ * @returns {boolean} True if the event was handled, false otherwise
68352
+ */
68353
+ handleKeyDown(view, event) {
68354
+ if (isMenuDisabled()) {
68355
+ return false;
68356
+ }
68357
+ const pluginState = this.getState(view.state);
68358
+ if (event.key === "/" && slashCooldown) {
68359
+ return false;
68360
+ }
68361
+ if (event.key === "/" && !pluginState.open) {
68362
+ const { $cursor } = view.state.selection;
68363
+ if (!$cursor) return false;
68364
+ const isParagraph = $cursor.parent.type.name === "paragraph";
68365
+ if (!isParagraph) return false;
68366
+ const textBefore = $cursor.parent.textContent.slice(0, $cursor.parentOffset);
68367
+ const isEmptyOrAfterSpace = !textBefore || textBefore.endsWith(" ");
68368
+ if (!isEmptyOrAfterSpace) return false;
68369
+ event.preventDefault();
68370
+ slashCooldown = true;
68371
+ if (slashCooldownTimeout) clearTimeout(slashCooldownTimeout);
68372
+ slashCooldownTimeout = setTimeout(() => {
68373
+ slashCooldown = false;
68374
+ slashCooldownTimeout = null;
68375
+ }, SLASH_COOLDOWN_MS);
68376
+ view.dispatch(
68377
+ view.state.tr.setMeta(SlashMenuPluginKey, {
68378
+ type: "open",
68379
+ pos: $cursor.pos
68380
+ })
68381
+ );
68382
+ return true;
68383
+ }
68384
+ if (pluginState.open && (event.key === "Escape" || event.key === "ArrowLeft")) {
68385
+ const { anchorPos } = pluginState;
68386
+ view.dispatch(
68387
+ view.state.tr.setMeta(SlashMenuPluginKey, {
68388
+ type: "close"
68389
+ })
68390
+ );
68391
+ if (anchorPos !== null) {
68392
+ const tr = view.state.tr.setSelection(
68393
+ view.state.selection.constructor.near(view.state.doc.resolve(anchorPos))
68394
+ );
68395
+ view.dispatch(tr);
68396
+ view.focus();
68397
+ }
68398
+ return true;
68399
+ }
68400
+ return false;
68401
+ }
68402
+ }
68403
+ });
68404
+ return [slashMenuPlugin];
68405
+ }
68406
+ });
68103
68407
  let readFromCache;
68104
68408
  let addToCache;
68105
68409
  if (typeof WeakMap != "undefined") {
@@ -73133,21 +73437,21 @@ ${o}
73133
73437
 
73134
73438
  /* Structured content drag handle/label - positioned above */
73135
73439
  .superdoc-structured-content__label {
73136
- font-size: 10px;
73440
+ font-size: 11px;
73137
73441
  align-items: center;
73138
73442
  justify-content: center;
73139
73443
  position: absolute;
73140
73444
  left: 2px;
73141
73445
  top: -19px;
73142
73446
  width: calc(100% - 4px);
73143
- max-width: 110px;
73447
+ max-width: 130px;
73144
73448
  min-width: 0;
73145
73449
  height: 18px;
73146
73450
  padding: 0 4px;
73147
73451
  border: 1px solid #629be7;
73148
73452
  border-bottom: none;
73149
73453
  border-radius: 6px 6px 0 0;
73150
- background-color: #629be7dd;
73454
+ background-color: #629be7ee;
73151
73455
  box-sizing: border-box;
73152
73456
  z-index: 10;
73153
73457
  display: none;
@@ -73161,7 +73465,9 @@ ${o}
73161
73465
  text-overflow: ellipsis;
73162
73466
  }
73163
73467
 
73164
- .superdoc-structured-content-block:hover .superdoc-structured-content__label {
73468
+ /* Hover effect for block structured content (via event delegation class).
73469
+ * Shows label on hover — border reveal is handled by the lock-mode hover rule below. */
73470
+ .superdoc-structured-content-block.sdt-hover .superdoc-structured-content__label {
73165
73471
  display: inline-flex;
73166
73472
  }
73167
73473
 
@@ -73190,6 +73496,13 @@ ${o}
73190
73496
  border-bottom: none;
73191
73497
  }
73192
73498
 
73499
+ /* Collapse double borders between adjacent SDT blocks */
73500
+ .superdoc-structured-content-block + .superdoc-structured-content-block {
73501
+ border-top: none;
73502
+ border-top-left-radius: 0;
73503
+ border-top-right-radius: 0;
73504
+ }
73505
+
73193
73506
  /* Structured Content Inline - Inline wrapper with blue border */
73194
73507
  .superdoc-structured-content-inline {
73195
73508
  padding: 1px;
@@ -73201,21 +73514,15 @@ ${o}
73201
73514
  z-index: 10;
73202
73515
  }
73203
73516
 
73204
- /* Hover effect for inline structured content */
73205
- .superdoc-structured-content-inline:hover {
73206
- background-color: rgba(98, 155, 231, 0.15);
73207
- border-color: #4a8ad9;
73208
- }
73209
-
73210
73517
  /* Inline structured content label - shown on hover */
73211
73518
  .superdoc-structured-content-inline__label {
73212
73519
  position: absolute;
73213
73520
  bottom: calc(100% + 2px);
73214
73521
  left: 50%;
73215
73522
  transform: translateX(-50%);
73216
- font-size: 10px;
73217
- padding: 2px 6px;
73218
- background-color: #629be7dd;
73523
+ font-size: 11px;
73524
+ padding: 0 4px;
73525
+ background-color: #629be7ee;
73219
73526
  color: white;
73220
73527
  border-radius: 4px;
73221
73528
  white-space: nowrap;
@@ -73228,6 +73535,17 @@ ${o}
73228
73535
  display: block;
73229
73536
  }
73230
73537
 
73538
+ /* Hover highlight for SDT containers.
73539
+ * Blue border is always visible (set in base rules above).
73540
+ * Hover adds background highlight and z-index boost.
73541
+ * Block SDTs use .sdt-hover class (event delegation for multi-fragment coordination).
73542
+ * Inline SDTs use :hover (single element, no coordination needed). */
73543
+ .superdoc-structured-content-block[data-lock-mode].sdt-hover,
73544
+ .superdoc-structured-content-inline[data-lock-mode]:hover {
73545
+ background-color: rgba(98, 155, 231, 0.08);
73546
+ z-index: 9999999;
73547
+ }
73548
+
73231
73549
  /* Viewing mode: remove structured content affordances */
73232
73550
  .presentation-editor--viewing .superdoc-structured-content-block,
73233
73551
  .presentation-editor--viewing .superdoc-structured-content-inline {
@@ -73273,7 +73591,7 @@ ${o}
73273
73591
  .superdoc-layout .annotation *::selection {
73274
73592
  background: transparent;
73275
73593
  }
73276
-
73594
+
73277
73595
  .superdoc-layout .annotation::-moz-selection,
73278
73596
  .superdoc-layout .annotation *::-moz-selection {
73279
73597
  background: transparent;
@@ -73425,7 +73743,16 @@ ${o}
73425
73743
  * within this wrapper should be used for character-level positioning, not the
73426
73744
  * wrapper itself.
73427
73745
  */
73428
- INLINE_SDT_WRAPPER: "superdoc-structured-content-inline"
73746
+ INLINE_SDT_WRAPPER: "superdoc-structured-content-inline",
73747
+ /**
73748
+ * Class name for block-level structured content containers.
73749
+ */
73750
+ BLOCK_SDT: "superdoc-structured-content-block",
73751
+ /**
73752
+ * Class name added to block SDT fragments on hover via event delegation.
73753
+ * Applied/removed by SdtGroupedHover to highlight all fragments of the same SDT.
73754
+ */
73755
+ SDT_HOVER: "sdt-hover"
73429
73756
  };
73430
73757
  var define_process_default$8 = { env: {} };
73431
73758
  const DEFAULT_ALLOWED_PROTOCOLS = ["http", "https", "mailto", "tel", "sms"];
@@ -73831,6 +74158,11 @@ ${o}
73831
74158
  container.dataset.sdtContainerStart = String(isStart);
73832
74159
  container.dataset.sdtContainerEnd = String(isEnd);
73833
74160
  container.style.overflow = "visible";
74161
+ if (isStructuredContentMetadata(sdt)) {
74162
+ container.dataset.lockMode = sdt.lockMode || "unlocked";
74163
+ } else if (isStructuredContentMetadata(containerSdt)) {
74164
+ container.dataset.lockMode = containerSdt.lockMode || "unlocked";
74165
+ }
73834
74166
  if (boundaryOptions?.widthOverride != null) {
73835
74167
  container.style.width = `${boundaryOptions.widthOverride}px`;
73836
74168
  }
@@ -73843,6 +74175,19 @@ ${o}
73843
74175
  container.appendChild(labelEl);
73844
74176
  }
73845
74177
  }
74178
+ function shouldRebuildForSdtBoundary(element2, boundary) {
74179
+ if (!boundary) {
74180
+ return element2.dataset.sdtContainerStart !== void 0;
74181
+ }
74182
+ const startAttr = element2.dataset.sdtContainerStart;
74183
+ const endAttr = element2.dataset.sdtContainerEnd;
74184
+ const expectedStart = String(boundary.isStart ?? true);
74185
+ const expectedEnd = String(boundary.isEnd ?? true);
74186
+ if (startAttr === void 0 || endAttr === void 0) {
74187
+ return true;
74188
+ }
74189
+ return startAttr !== expectedStart || endAttr !== expectedEnd;
74190
+ }
73846
74191
  const TWIPS_PER_INCH$4 = 1440;
73847
74192
  const PX_PER_INCH$3 = 96;
73848
74193
  const PX_PER_PT = 96 / 72;
@@ -75322,6 +75667,65 @@ ${o}
75322
75667
  const hasPmEnd = fragment.pmEnd != null;
75323
75668
  globalValidationStats.record(hasPmStart, hasPmEnd);
75324
75669
  }
75670
+ const SDT_BLOCK_SELECTOR = `.${DOM_CLASS_NAMES.BLOCK_SDT}[data-sdt-id]`;
75671
+ const HOVER_CLASS = DOM_CLASS_NAMES.SDT_HOVER;
75672
+ function sdtElementsById(root2, sdtId) {
75673
+ return root2.querySelectorAll(`.${DOM_CLASS_NAMES.BLOCK_SDT}[data-sdt-id="${sdtId}"]`);
75674
+ }
75675
+ class SdtGroupedHover {
75676
+ constructor() {
75677
+ this.hoveredSdtId = null;
75678
+ this.mount = null;
75679
+ this.onMouseOver = null;
75680
+ this.onMouseLeave = null;
75681
+ }
75682
+ /** Attach hover listeners to the mount element. Safe to call again on remount. */
75683
+ bind(mount2) {
75684
+ this.destroy();
75685
+ this.mount = mount2;
75686
+ this.onMouseOver = (e) => {
75687
+ const target = e.target.closest?.(SDT_BLOCK_SELECTOR);
75688
+ const sdtId = target?.dataset.sdtId ?? null;
75689
+ if (sdtId === this.hoveredSdtId) return;
75690
+ if (this.hoveredSdtId) {
75691
+ sdtElementsById(mount2, this.hoveredSdtId).forEach((el) => el.classList.remove(HOVER_CLASS));
75692
+ }
75693
+ this.hoveredSdtId = sdtId;
75694
+ if (sdtId) {
75695
+ sdtElementsById(mount2, sdtId).forEach((el) => el.classList.add(HOVER_CLASS));
75696
+ }
75697
+ };
75698
+ this.onMouseLeave = () => {
75699
+ if (this.hoveredSdtId) {
75700
+ sdtElementsById(mount2, this.hoveredSdtId).forEach((el) => el.classList.remove(HOVER_CLASS));
75701
+ this.hoveredSdtId = null;
75702
+ }
75703
+ };
75704
+ mount2.addEventListener("mouseover", this.onMouseOver);
75705
+ mount2.addEventListener("mouseleave", this.onMouseLeave);
75706
+ }
75707
+ /** Re-apply hover class after render. New/rebuilt elements lose the class. */
75708
+ reapply() {
75709
+ if (this.hoveredSdtId && this.mount) {
75710
+ sdtElementsById(this.mount, this.hoveredSdtId).forEach((el) => el.classList.add(HOVER_CLASS));
75711
+ }
75712
+ }
75713
+ /** Remove listeners and reset state. */
75714
+ destroy() {
75715
+ if (this.mount) {
75716
+ if (this.onMouseOver) {
75717
+ this.mount.removeEventListener("mouseover", this.onMouseOver);
75718
+ }
75719
+ if (this.onMouseLeave) {
75720
+ this.mount.removeEventListener("mouseleave", this.onMouseLeave);
75721
+ }
75722
+ }
75723
+ this.mount = null;
75724
+ this.onMouseOver = null;
75725
+ this.onMouseLeave = null;
75726
+ this.hoveredSdtId = null;
75727
+ }
75728
+ }
75325
75729
  const DEFAULT_PPI = 96;
75326
75730
  const DEFAULT_RULER_HEIGHT = 25;
75327
75731
  const TICK_SPACING_PX = DEFAULT_PPI / 8;
@@ -76030,6 +76434,7 @@ ${o}
76030
76434
  this.onScrollHandler = null;
76031
76435
  this.onWindowScrollHandler = null;
76032
76436
  this.onResizeHandler = null;
76437
+ this.sdtHover = new SdtGroupedHover();
76033
76438
  this.activeCommentId = null;
76034
76439
  this.options = options;
76035
76440
  this.layoutMode = options.layoutMode ?? "vertical";
@@ -76295,6 +76700,7 @@ ${o}
76295
76700
  };
76296
76701
  win.addEventListener("resize", this.onResizeHandler);
76297
76702
  }
76703
+ this.sdtHover.bind(mount2);
76298
76704
  }
76299
76705
  computeVirtualMetrics() {
76300
76706
  if (!this.currentLayout) return;
@@ -76437,6 +76843,7 @@ ${o}
76437
76843
  }
76438
76844
  this.changedBlocks.clear();
76439
76845
  this.processedLayoutVersion = this.layoutVersion;
76846
+ this.sdtHover.reapply();
76440
76847
  }
76441
76848
  updateSpacers(start2, end2) {
76442
76849
  if (!this.topSpacerEl || !this.bottomSpacerEl) return;
@@ -76721,6 +77128,7 @@ ${o}
76721
77128
  this.onScrollHandler = null;
76722
77129
  this.onWindowScrollHandler = null;
76723
77130
  this.onResizeHandler = null;
77131
+ this.sdtHover.destroy();
76724
77132
  this.layoutVersion = 0;
76725
77133
  this.processedLayoutVersion = -1;
76726
77134
  }
@@ -76782,7 +77190,9 @@ ${o}
76782
77190
  if (current) {
76783
77191
  existing.delete(key2);
76784
77192
  const sdtBoundaryMismatch = shouldRebuildForSdtBoundary(current.element, sdtBoundary);
76785
- const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup) || sdtBoundaryMismatch;
77193
+ const newPmStart = fragment.pmStart;
77194
+ const mappingUnreliable = this.currentMapping != null && newPmStart != null && current.element.dataset.pmStart != null && this.currentMapping.map(Number(current.element.dataset.pmStart)) !== newPmStart;
77195
+ const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup) || sdtBoundaryMismatch || mappingUnreliable;
76786
77196
  if (needsRebuild) {
76787
77197
  const replacement = this.renderFragment(fragment, contextBase, sdtBoundary);
76788
77198
  pageEl.replaceChild(replacement, current.element);
@@ -78868,6 +79278,43 @@ ${o}
78868
79278
  }
78869
79279
  return void 0;
78870
79280
  };
79281
+ let geoSdtWrapper = null;
79282
+ let geoSdtId = null;
79283
+ let geoSdtWrapperLeft = 0;
79284
+ let geoSdtMaxRight = 0;
79285
+ const closeGeoSdtWrapper = () => {
79286
+ if (geoSdtWrapper) {
79287
+ geoSdtWrapper.style.width = `${geoSdtMaxRight - geoSdtWrapperLeft}px`;
79288
+ el.appendChild(geoSdtWrapper);
79289
+ geoSdtWrapper = null;
79290
+ geoSdtId = null;
79291
+ }
79292
+ };
79293
+ const appendToLineGeo = (elem, runForSdt, elemLeftPx, elemWidthPx) => {
79294
+ const resolved = this.resolveRunSdtId(runForSdt);
79295
+ const thisRunSdtId = resolved?.sdtId ?? null;
79296
+ if (thisRunSdtId !== geoSdtId) {
79297
+ closeGeoSdtWrapper();
79298
+ }
79299
+ if (resolved && this.doc) {
79300
+ if (!geoSdtWrapper) {
79301
+ geoSdtWrapper = this.createInlineSdtWrapper(resolved.sdt);
79302
+ geoSdtId = thisRunSdtId;
79303
+ geoSdtWrapperLeft = elemLeftPx;
79304
+ geoSdtMaxRight = elemLeftPx;
79305
+ geoSdtWrapper.style.position = "absolute";
79306
+ geoSdtWrapper.style.left = `${elemLeftPx}px`;
79307
+ geoSdtWrapper.style.top = "0px";
79308
+ geoSdtWrapper.style.height = `${line.lineHeight}px`;
79309
+ }
79310
+ elem.style.left = `${elemLeftPx - geoSdtWrapperLeft}px`;
79311
+ geoSdtMaxRight = Math.max(geoSdtMaxRight, elemLeftPx + elemWidthPx);
79312
+ this.expandSdtWrapperPmRange(geoSdtWrapper, runForSdt.pmStart, runForSdt.pmEnd);
79313
+ geoSdtWrapper.appendChild(elem);
79314
+ } else {
79315
+ el.appendChild(elem);
79316
+ }
79317
+ };
78871
79318
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
78872
79319
  const baseRun = block.runs[runIndex];
78873
79320
  if (!baseRun) continue;
@@ -78899,7 +79346,7 @@ ${o}
78899
79346
  if (baseRun.pmStart != null) tabEl.dataset.pmStart = String(baseRun.pmStart);
78900
79347
  if (baseRun.pmEnd != null) tabEl.dataset.pmEnd = String(baseRun.pmEnd);
78901
79348
  tabEl.dataset.layoutEpoch = String(this.layoutEpoch);
78902
- el.appendChild(tabEl);
79349
+ appendToLineGeo(tabEl, baseRun, tabStartX + indentOffset, actualTabWidth);
78903
79350
  cumulativeX = tabEndX;
78904
79351
  continue;
78905
79352
  }
@@ -78915,7 +79362,7 @@ ${o}
78915
79362
  const segWidth = (runSegments2 && runSegments2[0]?.width !== void 0 ? runSegments2[0].width : elem.offsetWidth) ?? 0;
78916
79363
  elem.style.position = "absolute";
78917
79364
  elem.style.left = `${segX}px`;
78918
- el.appendChild(elem);
79365
+ appendToLineGeo(elem, baseRun, segX, segWidth);
78919
79366
  cumulativeX = baseSegX + segWidth;
78920
79367
  }
78921
79368
  continue;
@@ -78938,7 +79385,7 @@ ${o}
78938
79385
  const segWidth = (runSegments2 && runSegments2[0]?.width !== void 0 ? runSegments2[0].width : 0) ?? 0;
78939
79386
  elem.style.position = "absolute";
78940
79387
  elem.style.left = `${segX}px`;
78941
- el.appendChild(elem);
79388
+ appendToLineGeo(elem, baseRun, segX, segWidth);
78942
79389
  cumulativeX = baseSegX + segWidth;
78943
79390
  }
78944
79391
  continue;
@@ -78973,7 +79420,7 @@ ${o}
78973
79420
  const xPos = baseX + indentOffset;
78974
79421
  elem.style.position = "absolute";
78975
79422
  elem.style.left = `${xPos}px`;
78976
- el.appendChild(elem);
79423
+ appendToLineGeo(elem, segmentRun, xPos, segment.width ?? 0);
78977
79424
  let width = segment.width ?? 0;
78978
79425
  if (width <= 0 && this.doc) {
78979
79426
  const measureEl = elem.cloneNode(true);
@@ -78985,9 +79432,13 @@ ${o}
78985
79432
  this.doc.body.removeChild(measureEl);
78986
79433
  }
78987
79434
  cumulativeX = baseX + width;
79435
+ if (geoSdtWrapper) {
79436
+ geoSdtMaxRight = Math.max(geoSdtMaxRight, xPos + width);
79437
+ }
78988
79438
  }
78989
79439
  });
78990
79440
  }
79441
+ closeGeoSdtWrapper();
78991
79442
  } else {
78992
79443
  let currentInlineSdtWrapper = null;
78993
79444
  let currentInlineSdtId = null;
@@ -78999,12 +79450,12 @@ ${o}
78999
79450
  }
79000
79451
  };
79001
79452
  runsForLine.forEach((run2) => {
79002
- const runSdt = run2.sdt;
79003
- const isInlineSdt = runSdt?.type === "structuredContent" && runSdt?.scope === "inline";
79004
- const runSdtId = isInlineSdt && runSdt?.id ? String(runSdt.id) : null;
79453
+ const resolved = this.resolveRunSdtId(run2);
79454
+ const runSdtId = resolved?.sdtId ?? null;
79005
79455
  if (runSdtId !== currentInlineSdtId) {
79006
79456
  closeCurrentWrapper();
79007
79457
  }
79458
+ let elem = null;
79008
79459
  if (run2.kind === "tab") {
79009
79460
  const tabEl = this.doc.createElement("span");
79010
79461
  tabEl.classList.add("superdoc-tab");
@@ -79025,39 +79476,20 @@ ${o}
79025
79476
  if (run2.pmStart != null) tabEl.dataset.pmStart = String(run2.pmStart);
79026
79477
  if (run2.pmEnd != null) tabEl.dataset.pmEnd = String(run2.pmEnd);
79027
79478
  tabEl.dataset.layoutEpoch = String(this.layoutEpoch);
79028
- el.appendChild(tabEl);
79029
- return;
79479
+ elem = tabEl;
79480
+ } else {
79481
+ elem = this.renderRun(run2, context, trackedConfig);
79030
79482
  }
79031
- const elem = this.renderRun(run2, context, trackedConfig);
79032
79483
  if (elem) {
79033
79484
  if (styleId) {
79034
79485
  elem.setAttribute("styleid", styleId);
79035
79486
  }
79036
- if (isInlineSdt && runSdtId && this.doc) {
79487
+ if (resolved && this.doc) {
79037
79488
  if (!currentInlineSdtWrapper) {
79038
- currentInlineSdtWrapper = this.doc.createElement("span");
79039
- currentInlineSdtWrapper.className = DOM_CLASS_NAMES.INLINE_SDT_WRAPPER;
79040
- currentInlineSdtWrapper.dataset.layoutEpoch = String(this.layoutEpoch);
79489
+ currentInlineSdtWrapper = this.createInlineSdtWrapper(resolved.sdt);
79041
79490
  currentInlineSdtId = runSdtId;
79042
- this.applySdtDataset(currentInlineSdtWrapper, runSdt);
79043
- const alias = runSdt?.alias || "Inline content";
79044
- const labelEl = this.doc.createElement("span");
79045
- labelEl.className = `${DOM_CLASS_NAMES.INLINE_SDT_WRAPPER}__label`;
79046
- labelEl.textContent = alias;
79047
- currentInlineSdtWrapper.appendChild(labelEl);
79048
- }
79049
- const wrapperPmStart = currentInlineSdtWrapper.dataset.pmStart;
79050
- const wrapperPmEnd = currentInlineSdtWrapper.dataset.pmEnd;
79051
- if (run2.pmStart != null) {
79052
- if (!wrapperPmStart || run2.pmStart < parseInt(wrapperPmStart, 10)) {
79053
- currentInlineSdtWrapper.dataset.pmStart = String(run2.pmStart);
79054
- }
79055
- }
79056
- if (run2.pmEnd != null) {
79057
- if (!wrapperPmEnd || run2.pmEnd > parseInt(wrapperPmEnd, 10)) {
79058
- currentInlineSdtWrapper.dataset.pmEnd = String(run2.pmEnd);
79059
- }
79060
79491
  }
79492
+ this.expandSdtWrapperPmRange(currentInlineSdtWrapper, run2.pmStart, run2.pmEnd);
79061
79493
  currentInlineSdtWrapper.appendChild(elem);
79062
79494
  } else {
79063
79495
  el.appendChild(elem);
@@ -79234,6 +79666,7 @@ ${o}
79234
79666
  "sdtScope",
79235
79667
  "sdtTag",
79236
79668
  "sdtAlias",
79669
+ "lockMode",
79237
79670
  "sdtSectionTitle",
79238
79671
  "sdtSectionType",
79239
79672
  "sdtSectionLocked",
@@ -79258,6 +79691,49 @@ ${o}
79258
79691
  el.dataset[key2] = String(value);
79259
79692
  }
79260
79693
  }
79694
+ /**
79695
+ * Resolve the inline SDT id from a run, or null if the run is not inside an inline SDT.
79696
+ */
79697
+ resolveRunSdtId(run2) {
79698
+ const sdt = run2.sdt;
79699
+ if (sdt?.type === "structuredContent" && sdt?.scope === "inline" && sdt?.id) {
79700
+ return { sdtId: String(sdt.id), sdt };
79701
+ }
79702
+ return null;
79703
+ }
79704
+ /**
79705
+ * Create an inline SDT wrapper `<span>` with className, layoutEpoch, dataset, and label.
79706
+ * Shared by both the geometry and run-based rendering paths.
79707
+ */
79708
+ createInlineSdtWrapper(sdt) {
79709
+ const wrapper = this.doc.createElement("span");
79710
+ wrapper.className = DOM_CLASS_NAMES.INLINE_SDT_WRAPPER;
79711
+ wrapper.dataset.layoutEpoch = String(this.layoutEpoch);
79712
+ this.applySdtDataset(wrapper, sdt);
79713
+ const alias = sdt?.alias || "Inline content";
79714
+ const labelEl = this.doc.createElement("span");
79715
+ labelEl.className = `${DOM_CLASS_NAMES.INLINE_SDT_WRAPPER}__label`;
79716
+ labelEl.textContent = alias;
79717
+ wrapper.appendChild(labelEl);
79718
+ return wrapper;
79719
+ }
79720
+ /**
79721
+ * Expand the PM position range tracked on an SDT wrapper to include a new run's range.
79722
+ */
79723
+ expandSdtWrapperPmRange(wrapper, pmStart, pmEnd) {
79724
+ if (pmStart != null) {
79725
+ const cur = wrapper.dataset.pmStart;
79726
+ if (!cur || pmStart < parseInt(cur, 10)) {
79727
+ wrapper.dataset.pmStart = String(pmStart);
79728
+ }
79729
+ }
79730
+ if (pmEnd != null) {
79731
+ const cur = wrapper.dataset.pmEnd;
79732
+ if (!cur || pmEnd > parseInt(cur, 10)) {
79733
+ wrapper.dataset.pmEnd = String(pmEnd);
79734
+ }
79735
+ }
79736
+ }
79261
79737
  /**
79262
79738
  * Applies SDT (Structured Document Tag) metadata to an element's dataset as data-sdt-* attributes.
79263
79739
  * Supports field annotations, structured content, document sections, and doc parts.
@@ -79285,6 +79761,7 @@ ${o}
79285
79761
  this.setDatasetString(el, "sdtScope", metadata.scope);
79286
79762
  this.setDatasetString(el, "sdtTag", metadata.tag);
79287
79763
  this.setDatasetString(el, "sdtAlias", metadata.alias);
79764
+ this.setDatasetString(el, "lockMode", metadata.lockMode || "unlocked");
79288
79765
  } else if (metadata.type === "documentSection") {
79289
79766
  this.setDatasetString(el, "sdtSectionTitle", metadata.title);
79290
79767
  this.setDatasetString(el, "sdtSectionType", metadata.sectionType);
@@ -79373,17 +79850,6 @@ ${o}
79373
79850
  }
79374
79851
  return boundaries;
79375
79852
  };
79376
- const shouldRebuildForSdtBoundary = (element2, boundary) => {
79377
- if (!boundary) return false;
79378
- const startAttr = element2.dataset.sdtContainerStart;
79379
- const endAttr = element2.dataset.sdtContainerEnd;
79380
- const expectedStart = String(boundary.isStart ?? true);
79381
- const expectedEnd = String(boundary.isEnd ?? true);
79382
- if (startAttr === void 0 || endAttr === void 0) {
79383
- return true;
79384
- }
79385
- return startAttr !== expectedStart || endAttr !== expectedEnd;
79386
- };
79387
79853
  const fragmentKey = (fragment) => {
79388
79854
  if (fragment.kind === "para") {
79389
79855
  return `para:${fragment.blockId}:${fragment.fromLine}:${fragment.toLine}`;
@@ -88779,6 +89245,7 @@ ${o}
88779
89245
  id: toNullableString(attrs.id),
88780
89246
  tag: toOptionalString(attrs.tag),
88781
89247
  alias: toOptionalString(attrs.alias),
89248
+ lockMode: attrs.lockMode,
88782
89249
  sdtPr: attrs.sdtPr
88783
89250
  };
88784
89251
  }
@@ -89603,7 +90070,8 @@ ${o}
89603
90070
  positions,
89604
90071
  tabOrdinal,
89605
90072
  paragraphAttrs,
89606
- inheritedMarks
90073
+ inheritedMarks,
90074
+ sdtMetadata
89607
90075
  }) {
89608
90076
  const pos = positions.get(node2);
89609
90077
  if (!pos) return null;
@@ -89619,6 +90087,9 @@ ${o}
89619
90087
  indent: indent2,
89620
90088
  leader: node2.attrs?.leader ?? null
89621
90089
  };
90090
+ if (sdtMetadata) {
90091
+ run2.sdt = sdtMetadata;
90092
+ }
89622
90093
  const marks = [...node2.marks ?? [], ...inheritedMarks ?? []];
89623
90094
  if (marks.length > 0) {
89624
90095
  applyMarksToRun(run2, marks);
@@ -112184,7 +112655,8 @@ ${o}
112184
112655
  }
112185
112656
  const activeEditor = this.getActiveEditor();
112186
112657
  const hasFocus = activeEditor?.view?.hasFocus?.() ?? false;
112187
- if (!hasFocus) {
112658
+ const slashMenuOpen = activeEditor?.state ? !!SlashMenuPluginKey.getState(activeEditor.state)?.open : false;
112659
+ if (!hasFocus && !slashMenuOpen) {
112188
112660
  try {
112189
112661
  this.#clearSelectedFieldAnnotationClass();
112190
112662
  this.#localSelectionLayer.innerHTML = "";
@@ -114406,300 +114878,6 @@ ${o}
114406
114878
  });
114407
114879
  return { decorations };
114408
114880
  };
114409
- function getEditorSurfaceElement(editor) {
114410
- if (!editor) return null;
114411
- if (typeof editor.hitTest === "function" && editor.element instanceof HTMLElement) {
114412
- return editor.element;
114413
- }
114414
- return editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element ?? null;
114415
- }
114416
- function getSurfaceRelativePoint(editor, eventLocation = {}) {
114417
- const surface = getEditorSurfaceElement(editor);
114418
- if (!surface) return null;
114419
- const rect = surface.getBoundingClientRect();
114420
- let left2;
114421
- let top2;
114422
- if (typeof eventLocation.clientX === "number" && typeof eventLocation.clientY === "number") {
114423
- left2 = eventLocation.clientX - rect.left;
114424
- top2 = eventLocation.clientY - rect.top;
114425
- } else if (editor?.state?.selection) {
114426
- const selectionFrom = editor.state.selection.from;
114427
- const coords = editor.coordsAtPos?.(selectionFrom);
114428
- if (coords) {
114429
- left2 = coords.left - rect.left;
114430
- top2 = coords.top - rect.top;
114431
- }
114432
- }
114433
- if (!Number.isFinite(left2) || !Number.isFinite(top2)) {
114434
- return null;
114435
- }
114436
- return { left: left2, top: top2 };
114437
- }
114438
- function findContainingBlockAncestor(element2) {
114439
- if (!element2) return null;
114440
- let current = element2.parentElement;
114441
- while (current && current !== document.body && current !== document.documentElement) {
114442
- try {
114443
- const style2 = window.getComputedStyle(current);
114444
- const transform = style2.transform;
114445
- const filter = style2.filter;
114446
- const backdropFilter = style2.backdropFilter || style2.webkitBackdropFilter;
114447
- const perspective = style2.perspective;
114448
- const willChange = style2.willChange;
114449
- const contain = style2.contain;
114450
- if (transform && transform !== "none") {
114451
- return current;
114452
- }
114453
- if (filter && filter !== "none") {
114454
- return current;
114455
- }
114456
- if (backdropFilter && backdropFilter !== "none") {
114457
- return current;
114458
- }
114459
- if (perspective && perspective !== "none") {
114460
- return current;
114461
- }
114462
- if (willChange && willChange !== "auto") {
114463
- const values = willChange.split(",").map((v2) => v2.trim());
114464
- if (values.includes("transform") || values.includes("perspective")) {
114465
- return current;
114466
- }
114467
- }
114468
- if (contain && /paint|layout|strict|content/.test(contain)) {
114469
- return current;
114470
- }
114471
- } catch (error) {
114472
- console.warn("SlashMenu: Failed to get computed style for element", current, error);
114473
- }
114474
- current = current.parentElement;
114475
- }
114476
- return null;
114477
- }
114478
- const SlashMenuPluginKey = new PluginKey("slashMenu");
114479
- const MENU_OFFSET_X = 100;
114480
- const MENU_OFFSET_Y = 28;
114481
- const CONTEXT_MENU_OFFSET_X = 10;
114482
- const CONTEXT_MENU_OFFSET_Y = 10;
114483
- const SLASH_COOLDOWN_MS = 5e3;
114484
- const SlashMenu = Extension.create({
114485
- name: "slashMenu",
114486
- /**
114487
- * Initialize default options for the SlashMenu extension
114488
- * @returns {SlashMenuOptions} Empty options object (configuration is inherited from editor options)
114489
- */
114490
- addOptions() {
114491
- return {};
114492
- },
114493
- addPmPlugins() {
114494
- const editor = this.editor;
114495
- if (editor.options?.isHeadless) {
114496
- return [];
114497
- }
114498
- let slashCooldown = false;
114499
- let slashCooldownTimeout = null;
114500
- const isMenuDisabled = () => Boolean(editor.options?.disableContextMenu);
114501
- const ensureStateShape = (value = {}) => ({
114502
- open: false,
114503
- selected: null,
114504
- anchorPos: null,
114505
- menuPosition: null,
114506
- disabled: isMenuDisabled(),
114507
- ...value
114508
- });
114509
- const slashMenuPlugin = new Plugin({
114510
- key: SlashMenuPluginKey,
114511
- state: {
114512
- init: () => ensureStateShape(),
114513
- /**
114514
- * Apply transaction to update plugin state
114515
- * Handles state transitions based on transaction metadata:
114516
- * - 'open': Opens menu at specified position or cursor location
114517
- * - 'select': Updates the selected menu item
114518
- * - 'close': Closes the menu and clears anchor position
114519
- * - 'updatePosition': Triggers menu position recalculation (no-op in apply)
114520
- *
114521
- * @param {import('prosemirror-state').Transaction} tr - The transaction
114522
- * @param {SlashMenuState} value - Previous plugin state
114523
- * @returns {SlashMenuState} New plugin state
114524
- */
114525
- apply(tr, value) {
114526
- const meta2 = tr.getMeta(SlashMenuPluginKey);
114527
- const disabled = isMenuDisabled();
114528
- if (disabled) {
114529
- if (value.open) {
114530
- editor.emit("slashMenu:close");
114531
- }
114532
- return ensureStateShape({ disabled: true });
114533
- }
114534
- if (!meta2) {
114535
- if (value.disabled !== disabled) {
114536
- return ensureStateShape({ ...value, disabled });
114537
- }
114538
- return value;
114539
- }
114540
- switch (meta2.type) {
114541
- case "open": {
114542
- if (typeof meta2.pos !== "number" || meta2.pos < 0 || meta2.pos > tr.doc.content.size) {
114543
- console.warn("SlashMenu: Invalid position", meta2.pos);
114544
- return ensureStateShape(value);
114545
- }
114546
- let left2 = 0;
114547
- let top2 = 0;
114548
- let isContextMenu = false;
114549
- if (typeof meta2.clientX === "number" && typeof meta2.clientY === "number") {
114550
- left2 = meta2.clientX;
114551
- top2 = meta2.clientY;
114552
- isContextMenu = true;
114553
- } else {
114554
- const relativePoint = getSurfaceRelativePoint(editor, meta2);
114555
- if (relativePoint) {
114556
- const surface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
114557
- if (surface) {
114558
- try {
114559
- const rect = surface.getBoundingClientRect();
114560
- left2 = rect.left + relativePoint.left;
114561
- top2 = rect.top + relativePoint.top;
114562
- } catch (error) {
114563
- console.warn("SlashMenu: Failed to get surface bounds", error);
114564
- return ensureStateShape(value);
114565
- }
114566
- }
114567
- }
114568
- }
114569
- const menuSurface = editor.presentationEditor?.element ?? editor.view?.dom ?? editor.options?.element;
114570
- const containingBlock = findContainingBlockAncestor(menuSurface);
114571
- if (containingBlock) {
114572
- try {
114573
- const cbRect = containingBlock.getBoundingClientRect();
114574
- left2 -= cbRect.left;
114575
- top2 -= cbRect.top;
114576
- left2 += containingBlock.scrollLeft || 0;
114577
- top2 += containingBlock.scrollTop || 0;
114578
- } catch (error) {
114579
- console.warn("SlashMenu: Failed to adjust for containing block", error);
114580
- }
114581
- }
114582
- const offsetX = isContextMenu ? CONTEXT_MENU_OFFSET_X : MENU_OFFSET_X;
114583
- const offsetY = isContextMenu ? CONTEXT_MENU_OFFSET_Y : MENU_OFFSET_Y;
114584
- const menuPosition = {
114585
- left: `${left2 + offsetX}px`,
114586
- top: `${top2 + offsetY}px`
114587
- };
114588
- const newState = {
114589
- ...value,
114590
- open: true,
114591
- anchorPos: meta2.pos,
114592
- menuPosition
114593
- };
114594
- editor.emit("slashMenu:open", { menuPosition });
114595
- return ensureStateShape(newState);
114596
- }
114597
- case "select": {
114598
- return ensureStateShape({ ...value, selected: meta2.id });
114599
- }
114600
- case "close": {
114601
- editor.emit("slashMenu:close");
114602
- return ensureStateShape({ ...value, open: false, anchorPos: null });
114603
- }
114604
- default:
114605
- return ensureStateShape({ ...value, disabled });
114606
- }
114607
- }
114608
- },
114609
- /**
114610
- * Create view plugin to handle window event listeners
114611
- * @param {import('prosemirror-view').EditorView} editorView - The ProseMirror editor view
114612
- * @returns {Object} View plugin with destroy method
114613
- */
114614
- view(editorView) {
114615
- const updatePosition2 = () => {
114616
- if (isMenuDisabled()) return;
114617
- const state = SlashMenuPluginKey.getState(editorView.state);
114618
- if (state.open) {
114619
- editorView.dispatch(
114620
- editorView.state.tr.setMeta(SlashMenuPluginKey, {
114621
- type: "updatePosition"
114622
- })
114623
- );
114624
- }
114625
- };
114626
- window.addEventListener("scroll", updatePosition2, true);
114627
- window.addEventListener("resize", updatePosition2);
114628
- return {
114629
- destroy() {
114630
- window.removeEventListener("scroll", updatePosition2, true);
114631
- window.removeEventListener("resize", updatePosition2);
114632
- if (slashCooldownTimeout) {
114633
- clearTimeout(slashCooldownTimeout);
114634
- slashCooldownTimeout = null;
114635
- }
114636
- }
114637
- };
114638
- },
114639
- props: {
114640
- /**
114641
- * Handle keyboard events to open/close the slash menu
114642
- * - '/': Opens menu at cursor if conditions are met (in paragraph, after space/start)
114643
- * - 'Escape' or 'ArrowLeft': Closes menu and restores cursor position
114644
- *
114645
- * @param {import('prosemirror-view').EditorView} view - The ProseMirror editor view
114646
- * @param {KeyboardEvent} event - The keyboard event
114647
- * @returns {boolean} True if the event was handled, false otherwise
114648
- */
114649
- handleKeyDown(view, event) {
114650
- if (isMenuDisabled()) {
114651
- return false;
114652
- }
114653
- const pluginState = this.getState(view.state);
114654
- if (event.key === "/" && slashCooldown) {
114655
- return false;
114656
- }
114657
- if (event.key === "/" && !pluginState.open) {
114658
- const { $cursor } = view.state.selection;
114659
- if (!$cursor) return false;
114660
- const isParagraph = $cursor.parent.type.name === "paragraph";
114661
- if (!isParagraph) return false;
114662
- const textBefore = $cursor.parent.textContent.slice(0, $cursor.parentOffset);
114663
- const isEmptyOrAfterSpace = !textBefore || textBefore.endsWith(" ");
114664
- if (!isEmptyOrAfterSpace) return false;
114665
- event.preventDefault();
114666
- slashCooldown = true;
114667
- if (slashCooldownTimeout) clearTimeout(slashCooldownTimeout);
114668
- slashCooldownTimeout = setTimeout(() => {
114669
- slashCooldown = false;
114670
- slashCooldownTimeout = null;
114671
- }, SLASH_COOLDOWN_MS);
114672
- view.dispatch(
114673
- view.state.tr.setMeta(SlashMenuPluginKey, {
114674
- type: "open",
114675
- pos: $cursor.pos
114676
- })
114677
- );
114678
- return true;
114679
- }
114680
- if (pluginState.open && (event.key === "Escape" || event.key === "ArrowLeft")) {
114681
- const { anchorPos } = pluginState;
114682
- view.dispatch(
114683
- view.state.tr.setMeta(SlashMenuPluginKey, {
114684
- type: "close"
114685
- })
114686
- );
114687
- if (anchorPos !== null) {
114688
- const tr = view.state.tr.setSelection(
114689
- view.state.selection.constructor.near(view.state.doc.resolve(anchorPos))
114690
- );
114691
- view.dispatch(tr);
114692
- view.focus();
114693
- }
114694
- return true;
114695
- }
114696
- return false;
114697
- }
114698
- }
114699
- });
114700
- return [slashMenuPlugin];
114701
- }
114702
- });
114703
114881
  class StructuredContentViewBase {
114704
114882
  node;
114705
114883
  view;
@@ -114839,6 +115017,20 @@ ${o}
114839
115017
  dragHandle.append(textElement);
114840
115018
  return dragHandle;
114841
115019
  }
115020
+ isContentLocked() {
115021
+ const lockMode = this.node.attrs.lockMode;
115022
+ return lockMode === "contentLocked" || lockMode === "sdtContentLocked";
115023
+ }
115024
+ isSdtLocked() {
115025
+ const lockMode = this.node.attrs.lockMode;
115026
+ return lockMode === "sdtLocked" || lockMode === "sdtContentLocked";
115027
+ }
115028
+ updateContentEditability() {
115029
+ if (this.dom) {
115030
+ this.dom.classList.toggle("sd-structured-content--content-locked", this.isContentLocked());
115031
+ this.dom.classList.toggle("sd-structured-content--sdt-locked", this.isSdtLocked());
115032
+ }
115033
+ }
114842
115034
  onDragStart(event) {
114843
115035
  const { view } = this.editor;
114844
115036
  const target = event.target;
@@ -114894,10 +115086,12 @@ ${o}
114894
115086
  element2.prepend(dragHandle);
114895
115087
  element2.addEventListener("dragstart", (e) => this.onDragStart(e));
114896
115088
  this.root = element2;
115089
+ this.updateContentEditability();
114897
115090
  }
114898
115091
  updateView() {
114899
115092
  const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
114900
115093
  updateDOMAttributes(this.dom, { ...domAttrs });
115094
+ this.updateContentEditability();
114901
115095
  }
114902
115096
  update(node2, decorations, innerDecorations) {
114903
115097
  const result = super.update(node2, decorations, innerDecorations);
@@ -114906,6 +115100,157 @@ ${o}
114906
115100
  return true;
114907
115101
  }
114908
115102
  }
115103
+ const STRUCTURED_CONTENT_LOCK_KEY = new PluginKey("structuredContentLock");
115104
+ function collectSDTNodes(doc2) {
115105
+ const sdtNodes = [];
115106
+ doc2.descendants((node2, pos) => {
115107
+ if (node2.type.name === "structuredContent" || node2.type.name === "structuredContentBlock") {
115108
+ sdtNodes.push({
115109
+ type: node2.type.name,
115110
+ lockMode: node2.attrs.lockMode,
115111
+ pos,
115112
+ end: pos + node2.nodeSize
115113
+ });
115114
+ }
115115
+ });
115116
+ return sdtNodes;
115117
+ }
115118
+ function checkLockViolation(sdtNodes, from2, to) {
115119
+ for (const sdt of sdtNodes) {
115120
+ const overlaps = from2 < sdt.end && to > sdt.pos;
115121
+ if (!overlaps) continue;
115122
+ const containsSDT = from2 <= sdt.pos && to >= sdt.end;
115123
+ const insideSDT = from2 >= sdt.pos && to <= sdt.end;
115124
+ const crossesStart = from2 < sdt.pos && to > sdt.pos && to < sdt.end;
115125
+ const crossesEnd = from2 > sdt.pos && from2 < sdt.end && to > sdt.end;
115126
+ const wouldDamageWrapper = containsSDT || crossesStart || crossesEnd;
115127
+ const wouldModifyContent = insideSDT && !containsSDT;
115128
+ const isSdtLocked = sdt.lockMode === "sdtLocked" || sdt.lockMode === "sdtContentLocked";
115129
+ const isContentLocked = sdt.lockMode === "contentLocked" || sdt.lockMode === "sdtContentLocked";
115130
+ if (isSdtLocked && wouldDamageWrapper) {
115131
+ return { blocked: true, reason: `Cannot delete SDT wrapper (${sdt.lockMode})` };
115132
+ }
115133
+ if (isContentLocked && wouldModifyContent) {
115134
+ return { blocked: true, reason: `Cannot modify content (${sdt.lockMode})` };
115135
+ }
115136
+ }
115137
+ return { blocked: false };
115138
+ }
115139
+ function createStructuredContentLockPlugin() {
115140
+ return new Plugin({
115141
+ key: STRUCTURED_CONTENT_LOCK_KEY,
115142
+ state: {
115143
+ init(_2, editorState) {
115144
+ return collectSDTNodes(editorState.doc);
115145
+ },
115146
+ apply(tr, cachedSDTNodes, _oldState, newState) {
115147
+ if (!tr.docChanged) return cachedSDTNodes;
115148
+ return collectSDTNodes(newState.doc);
115149
+ }
115150
+ },
115151
+ props: {
115152
+ /**
115153
+ * Intercept key events BEFORE any transaction is created.
115154
+ * This prevents the browser selection from getting out of sync.
115155
+ */
115156
+ handleKeyDown(view, event) {
115157
+ const { state } = view;
115158
+ const { selection } = state;
115159
+ const { from: from2, to } = selection;
115160
+ const isDelete = event.key === "Delete";
115161
+ const isBackspace = event.key === "Backspace";
115162
+ const isCut = (event.metaKey || event.ctrlKey) && event.key === "x";
115163
+ if (!isDelete && !isBackspace && !isCut) {
115164
+ return false;
115165
+ }
115166
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(state);
115167
+ if (sdtNodes.length === 0) {
115168
+ return false;
115169
+ }
115170
+ let affectedFrom = from2;
115171
+ let affectedTo = to;
115172
+ if (from2 === to) {
115173
+ if (isBackspace && from2 > 0) {
115174
+ affectedFrom = from2 - 1;
115175
+ } else if (isDelete && to < state.doc.content.size) {
115176
+ affectedTo = to + 1;
115177
+ }
115178
+ }
115179
+ const result = checkLockViolation(sdtNodes, affectedFrom, affectedTo);
115180
+ if (result.blocked) {
115181
+ event.preventDefault();
115182
+ return true;
115183
+ }
115184
+ return false;
115185
+ },
115186
+ /**
115187
+ * Handle text input (typing) for content-locked nodes
115188
+ */
115189
+ handleTextInput(view, from2, to, _text) {
115190
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(view.state);
115191
+ if (sdtNodes.length === 0) {
115192
+ return false;
115193
+ }
115194
+ const result = checkLockViolation(sdtNodes, from2, to);
115195
+ if (result.blocked) {
115196
+ return true;
115197
+ }
115198
+ return false;
115199
+ }
115200
+ },
115201
+ /**
115202
+ * Safety net: filter transactions that slip through
115203
+ * (e.g., programmatic changes, paste, drag-drop)
115204
+ */
115205
+ filterTransaction(tr, state) {
115206
+ if (!tr.docChanged) {
115207
+ return true;
115208
+ }
115209
+ const sdtNodes = STRUCTURED_CONTENT_LOCK_KEY.getState(state);
115210
+ if (sdtNodes.length === 0) {
115211
+ return true;
115212
+ }
115213
+ for (const step of tr.steps) {
115214
+ if (step.from === void 0 || step.to === void 0) {
115215
+ continue;
115216
+ }
115217
+ const result = checkLockViolation(sdtNodes, step.from, step.to);
115218
+ if (result.blocked) {
115219
+ return false;
115220
+ }
115221
+ }
115222
+ return true;
115223
+ }
115224
+ });
115225
+ }
115226
+ function createStructuredContentSelectPlugin() {
115227
+ return new Plugin({
115228
+ appendTransaction(transactions, oldState, newState) {
115229
+ const { selection } = newState;
115230
+ if (!selection.empty) return null;
115231
+ if (oldState.selection.eq(newState.selection)) return null;
115232
+ if (transactions.some((tr) => tr.docChanged)) return null;
115233
+ const $pos = selection.$from;
115234
+ for (let d2 = $pos.depth; d2 > 0; d2--) {
115235
+ const node2 = $pos.node(d2);
115236
+ if (node2.type.name === "structuredContent") {
115237
+ const sdtStart = $pos.before(d2);
115238
+ const contentFrom = $pos.start(d2);
115239
+ const contentTo = $pos.end(d2);
115240
+ if (contentFrom === contentTo) return null;
115241
+ const old$pos = oldState.selection.$from;
115242
+ for (let od = old$pos.depth; od > 0; od--) {
115243
+ if (old$pos.node(od).type.name === "structuredContent" && old$pos.before(od) === sdtStart) {
115244
+ return null;
115245
+ }
115246
+ }
115247
+ return newState.tr.setSelection(TextSelection$1.create(newState.doc, contentFrom, contentTo));
115248
+ }
115249
+ }
115250
+ return null;
115251
+ }
115252
+ });
115253
+ }
114909
115254
  const structuredContentClass = "sd-structured-content";
114910
115255
  const structuredContentInnerClass = "sd-structured-content__content";
114911
115256
  const StructuredContent = Node$3.create({
@@ -114951,6 +115296,14 @@ ${o}
114951
115296
  return { "data-alias": attrs.alias };
114952
115297
  }
114953
115298
  },
115299
+ lockMode: {
115300
+ default: "unlocked",
115301
+ parseDOM: (elem) => elem.getAttribute("data-lock-mode") || "unlocked",
115302
+ renderDOM: (attrs) => {
115303
+ if (!attrs.lockMode || attrs.lockMode === "unlocked") return {};
115304
+ return { "data-lock-mode": attrs.lockMode };
115305
+ }
115306
+ },
114954
115307
  sdtPr: {
114955
115308
  rendered: false
114956
115309
  }
@@ -114968,6 +115321,9 @@ ${o}
114968
115321
  0
114969
115322
  ];
114970
115323
  },
115324
+ addPmPlugins() {
115325
+ return [createStructuredContentLockPlugin(), createStructuredContentSelectPlugin()];
115326
+ },
114971
115327
  addNodeView() {
114972
115328
  return (props) => {
114973
115329
  return new StructuredContentInlineView({ ...props });
@@ -115002,10 +115358,12 @@ ${o}
115002
115358
  element2.prepend(dragHandle);
115003
115359
  element2.addEventListener("dragstart", (e) => this.onDragStart(e));
115004
115360
  this.root = element2;
115361
+ this.updateContentEditability();
115005
115362
  }
115006
115363
  updateView() {
115007
115364
  const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
115008
115365
  updateDOMAttributes(this.dom, { ...domAttrs });
115366
+ this.updateContentEditability();
115009
115367
  }
115010
115368
  update(node2, decorations, innerDecorations) {
115011
115369
  const result = super.update(node2, decorations, innerDecorations);
@@ -115058,6 +115416,14 @@ ${o}
115058
115416
  return { "data-alias": attrs.alias };
115059
115417
  }
115060
115418
  },
115419
+ lockMode: {
115420
+ default: "unlocked",
115421
+ parseDOM: (elem) => elem.getAttribute("data-lock-mode") || "unlocked",
115422
+ renderDOM: (attrs) => {
115423
+ if (!attrs.lockMode || attrs.lockMode === "unlocked") return {};
115424
+ return { "data-lock-mode": attrs.lockMode };
115425
+ }
115426
+ },
115061
115427
  sdtPr: {
115062
115428
  rendered: false
115063
115429
  }
@@ -134384,18 +134750,25 @@ ${o}
134384
134750
  };
134385
134751
  const _hoisted_4$9 = {
134386
134752
  key: 3,
134387
- class: "link-input-wrapper"
134753
+ class: "link-title"
134388
134754
  };
134389
- const _hoisted_5$5 = { class: "input-row text-input-row" };
134390
- const _hoisted_6$3 = ["onKeydown"];
134391
- const _hoisted_7$3 = { class: "input-row url-input-row" };
134392
- const _hoisted_8$3 = ["innerHTML"];
134393
- const _hoisted_9$2 = ["onKeydown"];
134394
- const _hoisted_10$1 = ["innerHTML"];
134395
- const _hoisted_11$1 = { class: "input-row link-buttons" };
134396
- const _hoisted_12$1 = ["innerHTML"];
134397
- const _hoisted_13$1 = {
134755
+ const _hoisted_5$5 = {
134398
134756
  key: 4,
134757
+ class: "link-input-wrapper"
134758
+ };
134759
+ const _hoisted_6$3 = { class: "input-row text-input-row" };
134760
+ const _hoisted_7$3 = ["readonly"];
134761
+ const _hoisted_8$3 = { class: "input-row url-input-row" };
134762
+ const _hoisted_9$2 = ["innerHTML"];
134763
+ const _hoisted_10$1 = ["readonly", "onKeydown"];
134764
+ const _hoisted_11$1 = ["innerHTML"];
134765
+ const _hoisted_12$1 = {
134766
+ key: 0,
134767
+ class: "input-row link-buttons"
134768
+ };
134769
+ const _hoisted_13$1 = ["innerHTML"];
134770
+ const _hoisted_14$1 = {
134771
+ key: 5,
134399
134772
  class: "input-row go-to-anchor clickable"
134400
134773
  };
134401
134774
  const _sfc_main$D = {
@@ -134489,6 +134862,7 @@ ${o}
134489
134862
  });
134490
134863
  const isEditing = computed(() => !isAnchor.value && !!getLinkHrefAtSelection());
134491
134864
  const isDisabled2 = computed(() => !validUrl.value);
134865
+ const isViewingMode = computed(() => props.editor?.options?.documentMode === "viewing");
134492
134866
  const openLink = () => {
134493
134867
  window.open(url.value, "_blank");
134494
134868
  };
@@ -134514,6 +134888,7 @@ ${o}
134514
134888
  if (props.showInput) focusInput();
134515
134889
  });
134516
134890
  const handleSubmit = () => {
134891
+ if (isViewingMode.value) return;
134517
134892
  const editor = props.editor;
134518
134893
  if (!editor) return;
134519
134894
  if (!rawUrl.value) {
@@ -134544,36 +134919,38 @@ ${o}
134544
134919
  return openBlock(), createElementBlock("div", {
134545
134920
  class: normalizeClass(["link-input-ctn", { "high-contrast": unref(isHighContrastMode2) }])
134546
134921
  }, [
134547
- isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_1$y, "Page anchor")) : isEditing.value ? (openBlock(), createElementBlock("div", _hoisted_2$l, "Edit link")) : (openBlock(), createElementBlock("div", _hoisted_3$g, "Add link")),
134548
- __props.showInput && !isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_4$9, [
134549
- createBaseVNode("div", _hoisted_5$5, [
134550
- _cache[4] || (_cache[4] = createBaseVNode("div", { class: "input-icon text-input-icon" }, "T", -1)),
134922
+ isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_1$y, "Page anchor")) : isViewingMode.value ? (openBlock(), createElementBlock("div", _hoisted_2$l, "Link details")) : isEditing.value ? (openBlock(), createElementBlock("div", _hoisted_3$g, "Edit link")) : (openBlock(), createElementBlock("div", _hoisted_4$9, "Add link")),
134923
+ __props.showInput && !isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_5$5, [
134924
+ createBaseVNode("div", _hoisted_6$3, [
134925
+ _cache[5] || (_cache[5] = createBaseVNode("div", { class: "input-icon text-input-icon" }, "T", -1)),
134551
134926
  withDirectives(createBaseVNode("input", {
134552
134927
  type: "text",
134553
134928
  name: "text",
134554
134929
  placeholder: "Text",
134555
134930
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => text2.value = $event),
134556
- onKeydown: withKeys(withModifiers(handleSubmit, ["stop", "prevent"]), ["enter"])
134557
- }, null, 40, _hoisted_6$3), [
134931
+ readonly: isViewingMode.value,
134932
+ onKeydown: _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => !isViewingMode.value && handleSubmit, ["stop", "prevent"]), ["enter"]))
134933
+ }, null, 40, _hoisted_7$3), [
134558
134934
  [vModelText, text2.value]
134559
134935
  ])
134560
134936
  ]),
134561
- createBaseVNode("div", _hoisted_7$3, [
134937
+ createBaseVNode("div", _hoisted_8$3, [
134562
134938
  createBaseVNode("div", {
134563
134939
  class: "input-icon",
134564
134940
  innerHTML: unref(toolbarIcons).linkInput
134565
- }, null, 8, _hoisted_8$3),
134941
+ }, null, 8, _hoisted_9$2),
134566
134942
  withDirectives(createBaseVNode("input", {
134567
134943
  type: "text",
134568
134944
  name: "link",
134569
134945
  placeholder: "Type or paste a link",
134570
134946
  class: normalizeClass({ error: urlError.value }),
134571
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => rawUrl.value = $event),
134947
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => rawUrl.value = $event),
134948
+ readonly: isViewingMode.value,
134572
134949
  onKeydown: [
134573
134950
  withKeys(withModifiers(handleSubmit, ["stop", "prevent"]), ["enter"]),
134574
- _cache[2] || (_cache[2] = ($event) => urlError.value = false)
134951
+ _cache[3] || (_cache[3] = ($event) => urlError.value = false)
134575
134952
  ]
134576
- }, null, 42, _hoisted_9$2), [
134953
+ }, null, 42, _hoisted_10$1), [
134577
134954
  [vModelText, rawUrl.value]
134578
134955
  ]),
134579
134956
  createBaseVNode("div", {
@@ -134581,9 +134958,9 @@ ${o}
134581
134958
  innerHTML: unref(toolbarIcons).openLink,
134582
134959
  onClick: openLink,
134583
134960
  "data-item": "btn-link-open"
134584
- }, null, 10, _hoisted_10$1)
134961
+ }, null, 10, _hoisted_11$1)
134585
134962
  ]),
134586
- createBaseVNode("div", _hoisted_11$1, [
134963
+ !isViewingMode.value ? (openBlock(), createElementBlock("div", _hoisted_12$1, [
134587
134964
  isEditing.value ? (openBlock(), createElementBlock("button", {
134588
134965
  key: 0,
134589
134966
  class: "remove-btn",
@@ -134593,25 +134970,25 @@ ${o}
134593
134970
  createBaseVNode("div", {
134594
134971
  class: "remove-btn__icon",
134595
134972
  innerHTML: unref(toolbarIcons).removeLink
134596
- }, null, 8, _hoisted_12$1),
134597
- _cache[5] || (_cache[5] = createTextVNode(" Remove ", -1))
134973
+ }, null, 8, _hoisted_13$1),
134974
+ _cache[6] || (_cache[6] = createTextVNode(" Remove ", -1))
134598
134975
  ])) : createCommentVNode("", true),
134599
134976
  createBaseVNode("button", {
134600
134977
  class: normalizeClass(["submit-btn", { "disable-btn": isDisabled2.value }]),
134601
134978
  onClick: handleSubmit,
134602
134979
  "data-item": "btn-link-apply"
134603
134980
  }, " Apply ", 2)
134604
- ])
134605
- ])) : isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_13$1, [
134981
+ ])) : createCommentVNode("", true)
134982
+ ])) : isAnchor.value ? (openBlock(), createElementBlock("div", _hoisted_14$1, [
134606
134983
  createBaseVNode("a", {
134607
- onClick: _cache[3] || (_cache[3] = withModifiers((...args) => __props.goToAnchor && __props.goToAnchor(...args), ["stop", "prevent"]))
134984
+ onClick: _cache[4] || (_cache[4] = withModifiers((...args) => __props.goToAnchor && __props.goToAnchor(...args), ["stop", "prevent"]))
134608
134985
  }, "Go to " + toDisplayString(rawUrl.value.startsWith("#_") ? rawUrl.value.substring(2) : rawUrl.value), 1)
134609
134986
  ])) : createCommentVNode("", true)
134610
134987
  ], 2);
134611
134988
  };
134612
134989
  }
134613
134990
  };
134614
- const LinkInput = /* @__PURE__ */ _export_sfc(_sfc_main$D, [["__scopeId", "data-v-f64d35e8"]]);
134991
+ const LinkInput = /* @__PURE__ */ _export_sfc(_sfc_main$D, [["__scopeId", "data-v-198fc596"]]);
134615
134992
  const _hoisted_1$x = ["aria-label", "onClick", "onKeydown"];
134616
134993
  const _hoisted_2$k = ["innerHTML"];
134617
134994
  const _hoisted_3$f = ["innerHTML"];
@@ -171448,7 +171825,7 @@ js: import "konva/skia-backend";
171448
171825
  this.config.colors = shuffleArray(this.config.colors);
171449
171826
  this.userColorMap = /* @__PURE__ */ new Map();
171450
171827
  this.colorIndex = 0;
171451
- this.version = "1.12.0-next.11";
171828
+ this.version = "1.12.0-next.13";
171452
171829
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
171453
171830
  this.superdocId = config2.superdocId || v4();
171454
171831
  this.colors = this.config.colors;