@harbour-enterprises/superdoc 0.23.0-next.26 → 0.23.0-next.27

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.
@@ -71989,7 +71989,7 @@ const fileHalfDashedIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="
71989
71989
  const commentIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z"/></svg>';
71990
71990
  const circleIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/></svg>';
71991
71991
  const checkIconSvg$1 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>';
71992
- const xmarkIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';
71992
+ const xMarkIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';
71993
71993
  const upRightFromSquareIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-128c0-17.7-14.3-32-32-32L352 0zM80 32C35.8 32 0 67.8 0 112L0 432c0 44.2 35.8 80 80 80l320 0c44.2 0 80-35.8 80-80l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 112c0 8.8-7.2 16-16 16L80 448c-8.8 0-16-7.2-16-16l0-320c0-8.8 7.2-16 16-16l112 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L80 32z"/></svg>';
71994
71994
  const ellipsisVerticalIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z"/></svg>';
71995
71995
  const caretUpIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z"/></svg>';
@@ -72044,7 +72044,7 @@ const toolbarIcons = {
72044
72044
  colorOption: circleIconSvg,
72045
72045
  colorOptionCheck: checkIconSvg$1,
72046
72046
  linkInput: linkIconSvg,
72047
- removeLink: xmarkIconSvg,
72047
+ removeLink: xMarkIconSvg,
72048
72048
  openLink: upRightFromSquareIconSvg,
72049
72049
  overflow: ellipsisVerticalIconSvg,
72050
72050
  dropdownCaretUp: caretUpIconSvg,
@@ -85883,7 +85883,9 @@ const ICONS = {
85883
85883
  copy: copyIconSvg,
85884
85884
  paste: pasteIconSvg,
85885
85885
  addDocumentSection: plusIconSvg,
85886
- removeDocumentSection: trashIconSvg
85886
+ removeDocumentSection: trashIconSvg,
85887
+ trackChangesAccept: checkIconSvg$1,
85888
+ trackChangesReject: xMarkIconSvg
85887
85889
  };
85888
85890
  const TEXTS = {
85889
85891
  addRowBefore: "Insert row above",
@@ -85906,7 +85908,9 @@ const TEXTS = {
85906
85908
  copy: "Copy",
85907
85909
  paste: "Paste",
85908
85910
  removeDocumentSection: "Remove section",
85909
- createDocumentSection: "Create section"
85911
+ createDocumentSection: "Create section",
85912
+ trackChangesAccept: "Accept change",
85913
+ trackChangesReject: "Reject change"
85910
85914
  };
85911
85915
  const tableActionsOptions = [
85912
85916
  {
@@ -86110,34 +86114,37 @@ async function getEditorContext(editor, event) {
86110
86114
  const isInSectionNode = structureFromResolvedPos?.isInSectionNode ?? selectionHasNodeOrMark(state2, "documentSection", { requireEnds: true });
86111
86115
  const currentNodeType = node?.type?.name || null;
86112
86116
  const activeMarks = [];
86117
+ let trackedChangeId = null;
86113
86118
  if (event && pos !== null) {
86114
86119
  const $pos = state2.doc.resolve(pos);
86115
- if ($pos.marks && typeof $pos.marks === "function") {
86116
- $pos.marks().forEach((mark) => activeMarks.push(mark.type.name));
86120
+ const processMark = (mark) => {
86121
+ if (!activeMarks.includes(mark.type.name)) {
86122
+ activeMarks.push(mark.type.name);
86123
+ }
86124
+ if (!trackedChangeId && (mark.type.name === "trackInsert" || mark.type.name === "trackDelete" || mark.type.name === "trackFormat")) {
86125
+ trackedChangeId = mark.attrs.id;
86126
+ }
86127
+ };
86128
+ for (let depth = 0; depth <= $pos.depth; depth++) {
86129
+ const nodeAtDepth = $pos.node(depth);
86130
+ if (nodeAtDepth && nodeAtDepth.marks) {
86131
+ nodeAtDepth.marks.forEach(processMark);
86132
+ }
86117
86133
  }
86118
- if (node && node.marks) {
86119
- node.marks.forEach((mark) => activeMarks.push(mark.type.name));
86134
+ if (state2.storedMarks) {
86135
+ state2.storedMarks.forEach(processMark);
86120
86136
  }
86121
86137
  } else {
86122
86138
  state2.storedMarks?.forEach((mark) => activeMarks.push(mark.type.name));
86123
86139
  state2.selection.$head.marks().forEach((mark) => activeMarks.push(mark.type.name));
86124
86140
  }
86125
- const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete");
86126
- let trackedChangeId = null;
86127
- if (isTrackedChange && event && pos !== null) {
86128
- const $pos = state2.doc.resolve(pos);
86129
- const marksAtPos = $pos.marks();
86130
- const trackedMark = marksAtPos.find((mark) => mark.type.name === "trackInsert" || mark.type.name === "trackDelete");
86131
- if (trackedMark) {
86132
- trackedChangeId = trackedMark.attrs.id;
86133
- }
86134
- }
86141
+ const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete") || activeMarks.includes("trackFormat");
86135
86142
  const cursorCoords = pos ? view.coordsAtPos(pos) : null;
86136
86143
  const cursorPosition = cursorCoords ? {
86137
86144
  x: cursorCoords.left,
86138
86145
  y: cursorCoords.top
86139
86146
  } : null;
86140
- return {
86147
+ const context = {
86141
86148
  // Selection info
86142
86149
  selectedText,
86143
86150
  hasSelection: !empty2,
@@ -86163,9 +86170,11 @@ async function getEditorContext(editor, event) {
86163
86170
  pos,
86164
86171
  node,
86165
86172
  event,
86173
+ trigger: event ? "click" : "slash",
86166
86174
  // Editor reference for advanced use cases
86167
86175
  editor
86168
86176
  };
86177
+ return context;
86169
86178
  }
86170
86179
  function computeCanUndo(editor, state2) {
86171
86180
  if (typeof editor?.can === "function") {
@@ -86246,160 +86255,209 @@ const isModuleEnabled = (editorOptions, moduleName) => {
86246
86255
  switch (moduleName) {
86247
86256
  case "ai":
86248
86257
  return !!editorOptions?.isAiEnabled;
86249
- // Example for future use cases
86250
- // case 'comments':
86251
- // return !!editorOptions?.isCommentsEnabled;
86252
86258
  default:
86253
86259
  return true;
86254
86260
  }
86255
86261
  };
86256
- function applyCustomMenuConfiguration(defaultSections, context) {
86257
- const { editor } = context;
86258
- const slashMenuConfig = editor.options?.slashMenuConfig;
86259
- if (!slashMenuConfig) {
86260
- return defaultSections;
86261
- }
86262
- let sections = [];
86263
- if (slashMenuConfig.includeDefaultItems !== false) {
86264
- sections = [...defaultSections];
86265
- }
86266
- if (slashMenuConfig.customItems && Array.isArray(slashMenuConfig.customItems)) {
86267
- sections = [...sections, ...slashMenuConfig.customItems];
86268
- }
86269
- if (typeof slashMenuConfig.menuProvider === "function") {
86262
+ const shouldShowItem = (item, context) => {
86263
+ if (typeof item.showWhen === "function") {
86270
86264
  try {
86271
- sections = slashMenuConfig.menuProvider(context, sections) || sections;
86265
+ return item.showWhen(context);
86272
86266
  } catch (error) {
86273
- console.warn("[SlashMenu] Error in custom menuProvider:", error);
86267
+ console.warn("[SlashMenu] showWhen error for item", item.id, ":", error);
86268
+ return false;
86274
86269
  }
86275
86270
  }
86276
- return sections;
86277
- }
86278
- function filterCustomItems(sections, context) {
86279
- return sections.map((section) => {
86280
- const filteredItems = section.items.filter((item) => {
86281
- if (typeof item.showWhen === "function") {
86282
- try {
86283
- return item.showWhen(context);
86284
- } catch (error) {
86285
- console.warn(`[SlashMenu] Error in showWhen for item ${item.id}:`, error);
86286
- return false;
86287
- }
86288
- }
86289
- return true;
86290
- });
86291
- return {
86292
- ...section,
86293
- items: filteredItems
86294
- };
86295
- }).filter((section) => section.items.length > 0);
86296
- }
86297
- function getItems(context) {
86298
- const { editor, selectedText, trigger: trigger2, clipboardContent } = context;
86299
- const clipboardHasContent = Boolean(
86300
- clipboardContent?.hasContent || clipboardContent?.html || clipboardContent?.text || typeof clipboardContent?.size === "number" && clipboardContent.size > 0 || clipboardContent && typeof clipboardContent?.content?.size === "number" && clipboardContent.content.size > 0 || clipboardContent?.raw && typeof clipboardContent.raw.size === "number" && clipboardContent.raw.size > 0 || clipboardContent?.raw && typeof clipboardContent.raw?.content?.size === "number" && clipboardContent.raw.content.size > 0
86301
- );
86302
- const isInTable2 = selectionHasNodeOrMark(editor.view.state, "table", { requireEnds: true });
86303
- const isInSectionNode = selectionHasNodeOrMark(editor.view.state, "documentSection", { requireEnds: true });
86304
- const sections = [
86271
+ };
86272
+ function getItems(context, customItems = [], includeDefaultItems = true) {
86273
+ const { selectedText, editor } = context;
86274
+ if (arguments.length === 1 && editor?.options?.slashMenuConfig) {
86275
+ customItems = editor.options.slashMenuConfig.items || editor.options.slashMenuConfig.customItems || [];
86276
+ includeDefaultItems = editor.options.slashMenuConfig.includeDefaultItems !== false;
86277
+ }
86278
+ const enhancedContext = {
86279
+ ...context,
86280
+ isInTable: context.isInTable ?? false,
86281
+ isInSectionNode: context.isInSectionNode ?? false,
86282
+ isTrackedChange: context.isTrackedChange ?? false,
86283
+ clipboardContent: context.clipboardContent ?? { hasContent: false },
86284
+ selectedText: context.selectedText ?? "",
86285
+ hasSelection: context.hasSelection ?? Boolean(context.selectedText)
86286
+ };
86287
+ const defaultSections = [
86305
86288
  {
86306
86289
  id: "ai-content",
86290
+ isDefault: true,
86307
86291
  items: [
86308
86292
  {
86309
86293
  id: "insert-text",
86310
86294
  label: selectedText ? TEXTS.replaceText : TEXTS.insertText,
86311
86295
  icon: ICONS.ai,
86312
86296
  component: AIWriter,
86297
+ isDefault: true,
86313
86298
  action: (editor2) => {
86314
86299
  if (editor2?.commands && typeof editor2.commands?.insertAiMark === "function") {
86315
86300
  editor2.commands.insertAiMark();
86316
86301
  }
86317
86302
  },
86318
- allowedTriggers: [TRIGGERS.slash, TRIGGERS.click],
86319
- requiresModule: "ai"
86303
+ showWhen: (context2) => {
86304
+ const { trigger: trigger2 } = context2;
86305
+ const allowedTriggers = [TRIGGERS.slash, TRIGGERS.click];
86306
+ return allowedTriggers.includes(trigger2) && isModuleEnabled(context2.editor?.options, "ai");
86307
+ }
86308
+ }
86309
+ ]
86310
+ },
86311
+ {
86312
+ id: "track-changes",
86313
+ isDefault: true,
86314
+ items: [
86315
+ {
86316
+ id: "track-changes-accept",
86317
+ icon: ICONS.trackChangesAccept,
86318
+ label: TEXTS.trackChangesAccept,
86319
+ isDefault: true,
86320
+ action: (editor2, context2) => {
86321
+ if (context2?.trackedChangeId) {
86322
+ editor2.commands.acceptTrackedChangeById(context2.trackedChangeId);
86323
+ } else {
86324
+ editor2.commands.acceptTrackedChangeBySelection();
86325
+ }
86326
+ },
86327
+ showWhen: (context2) => {
86328
+ const { trigger: trigger2, isTrackedChange } = context2;
86329
+ return trigger2 === TRIGGERS.click && isTrackedChange;
86330
+ }
86331
+ },
86332
+ {
86333
+ id: "track-changes-reject",
86334
+ label: TEXTS.trackChangesReject,
86335
+ icon: ICONS.trackChangesReject,
86336
+ isDefault: true,
86337
+ action: (editor2, context2) => {
86338
+ if (context2?.trackedChangeId) {
86339
+ editor2.commands.rejectTrackedChangeById(context2.trackedChangeId);
86340
+ } else {
86341
+ editor2.commands.rejectTrackedChangeOnSelection();
86342
+ }
86343
+ },
86344
+ showWhen: (context2) => {
86345
+ const { trigger: trigger2, isTrackedChange } = context2;
86346
+ return trigger2 === TRIGGERS.click && isTrackedChange;
86347
+ }
86320
86348
  }
86321
86349
  ]
86322
86350
  },
86323
86351
  {
86324
86352
  id: "document-sections",
86353
+ isDefault: true,
86325
86354
  items: [
86326
86355
  {
86327
86356
  id: "insert-document-section",
86328
86357
  label: TEXTS.createDocumentSection,
86329
86358
  icon: ICONS.addDocumentSection,
86359
+ isDefault: true,
86330
86360
  action: (editor2) => {
86331
86361
  editor2.commands.createDocumentSection();
86332
86362
  },
86333
- allowedTriggers: [TRIGGERS.click]
86363
+ showWhen: (context2) => {
86364
+ const { trigger: trigger2 } = context2;
86365
+ return trigger2 === TRIGGERS.click;
86366
+ }
86334
86367
  },
86335
86368
  {
86336
86369
  id: "remove-section",
86337
86370
  label: TEXTS.removeDocumentSection,
86338
86371
  icon: ICONS.removeDocumentSection,
86372
+ isDefault: true,
86339
86373
  action: (editor2) => {
86340
86374
  editor2.commands.removeSectionAtSelection();
86341
86375
  },
86342
- allowedTriggers: [TRIGGERS.click],
86343
- requiresSectionParent: true
86376
+ showWhen: (context2) => {
86377
+ const { trigger: trigger2, isInSectionNode } = context2;
86378
+ return trigger2 === TRIGGERS.click && isInSectionNode;
86379
+ }
86344
86380
  }
86345
86381
  ]
86346
86382
  },
86347
86383
  {
86348
86384
  id: "general",
86385
+ isDefault: true,
86349
86386
  items: [
86350
86387
  {
86351
86388
  id: "insert-link",
86352
86389
  label: TEXTS.insertLink,
86353
86390
  icon: ICONS.link,
86354
86391
  component: LinkInput,
86355
- allowedTriggers: [TRIGGERS.click]
86392
+ isDefault: true,
86393
+ showWhen: (context2) => {
86394
+ const { trigger: trigger2 } = context2;
86395
+ return trigger2 === TRIGGERS.click;
86396
+ }
86356
86397
  },
86357
86398
  {
86358
86399
  id: "insert-table",
86359
86400
  label: TEXTS.insertTable,
86360
86401
  icon: ICONS.table,
86361
86402
  component: TableGrid,
86362
- allowedTriggers: [TRIGGERS.slash, TRIGGERS.click]
86403
+ isDefault: true,
86404
+ showWhen: (context2) => {
86405
+ const { trigger: trigger2, isInTable: isInTable2 } = context2;
86406
+ const allowedTriggers = [TRIGGERS.slash, TRIGGERS.click];
86407
+ return allowedTriggers.includes(trigger2) && !isInTable2;
86408
+ }
86363
86409
  },
86364
86410
  {
86365
86411
  id: "edit-table",
86366
86412
  label: TEXTS.editTable,
86367
86413
  icon: ICONS.table,
86368
86414
  component: TableActions,
86369
- allowedTriggers: [TRIGGERS.slash, TRIGGERS.click],
86370
- requiresTableParent: true
86415
+ isDefault: true,
86416
+ showWhen: (context2) => {
86417
+ const { trigger: trigger2, isInTable: isInTable2 } = context2;
86418
+ const allowedTriggers = [TRIGGERS.slash, TRIGGERS.click];
86419
+ return allowedTriggers.includes(trigger2) && isInTable2;
86420
+ }
86371
86421
  }
86372
86422
  ]
86373
86423
  },
86374
86424
  {
86375
86425
  id: "clipboard",
86426
+ isDefault: true,
86376
86427
  items: [
86377
86428
  {
86378
86429
  id: "cut",
86379
86430
  label: TEXTS.cut,
86380
86431
  icon: ICONS.cut,
86432
+ isDefault: true,
86381
86433
  action: (editor2) => {
86382
86434
  editor2.view.focus();
86383
86435
  document.execCommand("cut");
86384
86436
  },
86385
- allowedTriggers: [TRIGGERS.click],
86386
- requiresSelection: true
86437
+ showWhen: (context2) => {
86438
+ const { trigger: trigger2, selectedText: selectedText2 } = context2;
86439
+ return trigger2 === TRIGGERS.click && selectedText2;
86440
+ }
86387
86441
  },
86388
86442
  {
86389
86443
  id: "copy",
86390
86444
  label: TEXTS.copy,
86391
86445
  icon: ICONS.copy,
86446
+ isDefault: true,
86392
86447
  action: (editor2) => {
86393
86448
  editor2.view.focus();
86394
86449
  document.execCommand("copy");
86395
86450
  },
86396
- allowedTriggers: [TRIGGERS.click],
86397
- requiresSelection: true
86451
+ showWhen: (context2) => {
86452
+ const { trigger: trigger2, selectedText: selectedText2 } = context2;
86453
+ return trigger2 === TRIGGERS.click && selectedText2;
86454
+ }
86398
86455
  },
86399
86456
  {
86400
86457
  id: "paste",
86401
86458
  label: TEXTS.paste,
86402
86459
  icon: ICONS.paste,
86460
+ isDefault: true,
86403
86461
  action: async (editor2) => {
86404
86462
  try {
86405
86463
  const clipboardItems = await navigator.clipboard.read();
@@ -86413,7 +86471,7 @@ function getItems(context) {
86413
86471
  text = await (await item.getType("text/plain")).text();
86414
86472
  }
86415
86473
  }
86416
- const handled = handleClipboardPaste({ editor: editor2, view: editor2.view }, html, text);
86474
+ const handled = handleClipboardPaste({ editor: editor2, view: editor2.view }, html);
86417
86475
  if (!handled) {
86418
86476
  const dataTransfer = new DataTransfer();
86419
86477
  if (html) dataTransfer.setData("text/html", html);
@@ -86429,30 +86487,52 @@ function getItems(context) {
86429
86487
  console.warn("Failed to paste:", error);
86430
86488
  }
86431
86489
  },
86432
- allowedTriggers: [TRIGGERS.click, TRIGGERS.slash],
86433
- requiresClipboard: true
86490
+ showWhen: (context2) => {
86491
+ const { trigger: trigger2, clipboardContent } = context2;
86492
+ const allowedTriggers = [TRIGGERS.click, TRIGGERS.slash];
86493
+ const hasContent = clipboardContent?.hasContent || clipboardContent?.size > 0 || clipboardContent?.content?.size > 0;
86494
+ return allowedTriggers.includes(trigger2) && hasContent;
86495
+ }
86434
86496
  }
86435
86497
  ]
86436
86498
  }
86437
86499
  ];
86438
- let allSections = applyCustomMenuConfiguration(sections, context);
86439
- const filteredSections = allSections.map((section) => {
86440
- const filteredItems = section.items.filter((item) => {
86441
- if (item.requiresModule && !isModuleEnabled(editor?.options, item.requiresModule)) return false;
86442
- if (item.requiresSelection && !selectedText) return false;
86443
- if (!item.allowedTriggers.includes(trigger2)) return false;
86444
- if (item.requiresClipboard && !clipboardHasContent) return false;
86445
- if (item.requiresTableParent && !isInTable2 || item.id === "insert-table" && isInTable2) return false;
86446
- if (item.requiresSectionParent && !isInSectionNode) return false;
86447
- return true;
86500
+ let allSections = [];
86501
+ if (includeDefaultItems) {
86502
+ allSections = [...defaultSections];
86503
+ }
86504
+ if (customItems.length > 0) {
86505
+ customItems.forEach((customSection) => {
86506
+ const existingSectionIndex = allSections.findIndex((section) => section.id === customSection.id);
86507
+ if (existingSectionIndex !== -1) {
86508
+ allSections[existingSectionIndex].items = [
86509
+ ...allSections[existingSectionIndex].items,
86510
+ ...customSection.items.map((item) => ({ ...item, isDefault: false }))
86511
+ ];
86512
+ } else {
86513
+ allSections.push({
86514
+ ...customSection,
86515
+ isDefault: false,
86516
+ items: customSection.items.map((item) => ({ ...item, isDefault: false }))
86517
+ });
86518
+ }
86448
86519
  });
86520
+ }
86521
+ if (editor?.options?.slashMenuConfig?.menuProvider) {
86522
+ try {
86523
+ allSections = editor.options.slashMenuConfig.menuProvider(enhancedContext, allSections) || allSections;
86524
+ } catch (error) {
86525
+ console.warn("[SlashMenu] menuProvider error:", error);
86526
+ }
86527
+ }
86528
+ const filteredSections = allSections.map((section) => {
86529
+ const filteredItems = section.items.filter((item) => shouldShowItem(item, enhancedContext));
86449
86530
  return {
86450
86531
  ...section,
86451
86532
  items: filteredItems
86452
86533
  };
86453
86534
  }).filter((section) => section.items.length > 0);
86454
- const finalSections = filterCustomItems(filteredSections, context);
86455
- return finalSections;
86535
+ return filteredSections;
86456
86536
  }
86457
86537
  const _hoisted_1$3 = { class: "slash-menu-items" };
86458
86538
  const _hoisted_2$1 = {
@@ -86535,13 +86615,28 @@ const _sfc_main$4 = {
86535
86615
  });
86536
86616
  const customItemRefs = /* @__PURE__ */ new Map();
86537
86617
  const setCustomItemRef = (el, item) => {
86538
- if (el && item.render) {
86618
+ if (el) {
86539
86619
  customItemRefs.set(item.id, { element: el, item });
86540
86620
  vue.nextTick(() => {
86541
86621
  renderCustomItem(item.id);
86542
86622
  });
86543
86623
  }
86544
86624
  };
86625
+ const defaultRender = (context) => {
86626
+ const item = context.item || context.currentItem;
86627
+ const container = document.createElement("div");
86628
+ container.className = "slash-menu-default-content";
86629
+ if (item.icon) {
86630
+ const iconSpan = document.createElement("span");
86631
+ iconSpan.className = "slash-menu-item-icon";
86632
+ iconSpan.innerHTML = item.icon;
86633
+ container.appendChild(iconSpan);
86634
+ }
86635
+ const labelSpan = document.createElement("span");
86636
+ labelSpan.textContent = item.label;
86637
+ container.appendChild(labelSpan);
86638
+ return container;
86639
+ };
86545
86640
  const renderCustomItem = async (itemId) => {
86546
86641
  const refData = customItemRefs.get(itemId);
86547
86642
  if (!refData || refData.element.hasCustomContent) return;
@@ -86550,8 +86645,9 @@ const _sfc_main$4 = {
86550
86645
  if (!currentContext.value) {
86551
86646
  currentContext.value = await getEditorContext(props.editor);
86552
86647
  }
86553
- const context = currentContext.value;
86554
- const customElement = item.render(context);
86648
+ const contextWithItem = { ...currentContext.value, currentItem: item };
86649
+ const renderFunction = item.render || defaultRender;
86650
+ const customElement = renderFunction(contextWithItem);
86555
86651
  if (customElement instanceof HTMLElement) {
86556
86652
  element.innerHTML = "";
86557
86653
  element.appendChild(customElement);
@@ -86559,7 +86655,9 @@ const _sfc_main$4 = {
86559
86655
  }
86560
86656
  } catch (error) {
86561
86657
  console.warn(`[SlashMenu] Error rendering custom item ${itemId}:`, error);
86562
- element.innerHTML = `<span>${item.label || "Custom Item"}</span>`;
86658
+ const fallbackElement = defaultRender({ ...currentContext.value || {}, currentItem: item });
86659
+ element.innerHTML = "";
86660
+ element.appendChild(fallbackElement);
86563
86661
  element.hasCustomContent = true;
86564
86662
  }
86565
86663
  };
@@ -86754,19 +86852,20 @@ const _sfc_main$4 = {
86754
86852
  class: vue.normalizeClass(["slash-menu-item", { "is-selected": item.id === selectedId.value }]),
86755
86853
  onClick: ($event) => executeCommand(item)
86756
86854
  }, [
86757
- item.render ? (vue.openBlock(), vue.createElementBlock("div", {
86758
- key: 0,
86855
+ vue.createBaseVNode("div", {
86759
86856
  ref_for: true,
86760
86857
  ref: (el) => setCustomItemRef(el, item),
86761
86858
  class: "slash-menu-custom-item"
86762
- }, null, 512)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
86763
- item.icon ? (vue.openBlock(), vue.createElementBlock("span", {
86764
- key: 0,
86765
- class: "slash-menu-item-icon",
86766
- innerHTML: item.icon
86767
- }, null, 8, _hoisted_4)) : vue.createCommentVNode("", true),
86768
- vue.createBaseVNode("span", null, vue.toDisplayString(item.label), 1)
86769
- ], 64))
86859
+ }, [
86860
+ !item.render ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
86861
+ item.icon ? (vue.openBlock(), vue.createElementBlock("span", {
86862
+ key: 0,
86863
+ class: "slash-menu-item-icon",
86864
+ innerHTML: item.icon
86865
+ }, null, 8, _hoisted_4)) : vue.createCommentVNode("", true),
86866
+ vue.createBaseVNode("span", null, vue.toDisplayString(item.label), 1)
86867
+ ], 64)) : vue.createCommentVNode("", true)
86868
+ ], 512)
86770
86869
  ], 10, _hoisted_3$1);
86771
86870
  }), 128))
86772
86871
  ], 64);
package/dist/style.css CHANGED
@@ -2080,7 +2080,7 @@ on the right if it is inside shape textbox.
2080
2080
  .slash-menu {
2081
2081
  position: absolute;
2082
2082
  z-index: 50;
2083
- width: 175px;
2083
+ width: 180px;
2084
2084
  color: #47484a;
2085
2085
  background: white;
2086
2086
  box-shadow:
@@ -2153,6 +2153,11 @@ on the right if it is inside shape textbox.
2153
2153
  align-items: center;
2154
2154
  width: 100%;
2155
2155
  }
2156
+ .slash-menu-default-content {
2157
+ display: flex;
2158
+ align-items: center;
2159
+ width: 100%;
2160
+ }
2156
2161
  .popover {
2157
2162
  background: white;
2158
2163
  border-radius: 6px;
@@ -72,7 +72,7 @@ const fileHalfDashedIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="
72
72
  const commentIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z"/></svg>';
73
73
  const circleIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/></svg>';
74
74
  const checkIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>';
75
- const xmarkIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';
75
+ const xMarkIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';
76
76
  const upRightFromSquareIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-128c0-17.7-14.3-32-32-32L352 0zM80 32C35.8 32 0 67.8 0 112L0 432c0 44.2 35.8 80 80 80l320 0c44.2 0 80-35.8 80-80l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 112c0 8.8-7.2 16-16 16L80 448c-8.8 0-16-7.2-16-16l0-320c0-8.8 7.2-16 16-16l112 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L80 32z"/></svg>';
77
77
  const ellipsisVerticalIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z"/></svg>';
78
78
  const caretUpIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z"/></svg>';
@@ -127,7 +127,7 @@ const toolbarIcons = {
127
127
  colorOption: circleIconSvg,
128
128
  colorOptionCheck: checkIconSvg,
129
129
  linkInput: linkIconSvg,
130
- removeLink: xmarkIconSvg,
130
+ removeLink: xMarkIconSvg,
131
131
  openLink: upRightFromSquareIconSvg,
132
132
  overflow: ellipsisVerticalIconSvg,
133
133
  dropdownCaretUp: caretUpIconSvg,
@@ -10161,18 +10161,20 @@ export {
10161
10161
  NSkeleton as N,
10162
10162
  Toolbar as T,
10163
10163
  trashIconSvg as a,
10164
- tableIconSvg as b,
10165
- scissorsIconSvg as c,
10166
- copyIconSvg as d,
10167
- pasteIconSvg as e,
10168
- borderNoneIconSvg as f,
10169
- arrowsToDotIconSvg as g,
10170
- arrowsLeftRightIconSvg as h,
10164
+ borderNoneIconSvg as b,
10165
+ arrowsToDotIconSvg as c,
10166
+ arrowsLeftRightIconSvg as d,
10167
+ checkIconSvg as e,
10168
+ tableIconSvg as f,
10169
+ scissorsIconSvg as g,
10170
+ copyIconSvg as h,
10171
+ pasteIconSvg as i,
10171
10172
  linkIconSvg as l,
10172
10173
  magicWandIcon as m,
10173
10174
  plusIconSvg as p,
10174
10175
  sanitizeNumber as s,
10175
10176
  toolbarIcons as t,
10176
10177
  useMessage as u,
10177
- wrenchIconSvg as w
10178
+ wrenchIconSvg as w,
10179
+ xMarkIconSvg as x
10178
10180
  };
@@ -1706,7 +1706,7 @@ on the right if it is inside shape textbox.
1706
1706
  .slash-menu {
1707
1707
  position: absolute;
1708
1708
  z-index: 50;
1709
- width: 175px;
1709
+ width: 180px;
1710
1710
  color: #47484a;
1711
1711
  background: white;
1712
1712
  box-shadow:
@@ -1779,6 +1779,11 @@ on the right if it is inside shape textbox.
1779
1779
  align-items: center;
1780
1780
  width: 100%;
1781
1781
  }
1782
+ .slash-menu-default-content {
1783
+ display: flex;
1784
+ align-items: center;
1785
+ width: 100%;
1786
+ }
1782
1787
  .popover {
1783
1788
  background: white;
1784
1789
  border-radius: 6px;
@@ -18,6 +18,8 @@ export namespace ICONS {
18
18
  export { pasteIconSvg as paste };
19
19
  export { plusIconSvg as addDocumentSection };
20
20
  export { trashIconSvg as removeDocumentSection };
21
+ export { checkIconSvg as trackChangesAccept };
22
+ export { xMarkIconSvg as trackChangesReject };
21
23
  }
22
24
  export namespace TEXTS {
23
25
  let addRowBefore: string;
@@ -41,6 +43,8 @@ export namespace TEXTS {
41
43
  let paste: string;
42
44
  let removeDocumentSection: string;
43
45
  let createDocumentSection: string;
46
+ let trackChangesAccept: string;
47
+ let trackChangesReject: string;
44
48
  }
45
49
  export const tableActionsOptions: ({
46
50
  label: string;