@abraca/nuxt 2.10.0 → 2.13.0

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 (127) hide show
  1. package/dist/module.d.mts +14 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +9 -0
  4. package/dist/runtime/assets/editor.css +1 -1
  5. package/dist/runtime/components/AConnectionBadge.d.vue.ts +29 -0
  6. package/dist/runtime/components/AConnectionBadge.vue +79 -0
  7. package/dist/runtime/components/AConnectionBadge.vue.d.ts +29 -0
  8. package/dist/runtime/components/ADocPickerModal.d.vue.ts +31 -0
  9. package/dist/runtime/components/ADocPickerModal.vue +191 -0
  10. package/dist/runtime/components/ADocPickerModal.vue.d.ts +31 -0
  11. package/dist/runtime/components/ADocumentTree.vue +65 -0
  12. package/dist/runtime/components/AEditor.d.vue.ts +19 -12
  13. package/dist/runtime/components/AEditor.vue +243 -165
  14. package/dist/runtime/components/AEditor.vue.d.ts +19 -12
  15. package/dist/runtime/components/AEncryptionModePicker.d.vue.ts +33 -0
  16. package/dist/runtime/components/AEncryptionModePicker.vue +211 -0
  17. package/dist/runtime/components/AEncryptionModePicker.vue.d.ts +33 -0
  18. package/dist/runtime/components/AModalShell.d.vue.ts +48 -0
  19. package/dist/runtime/components/AModalShell.vue +105 -0
  20. package/dist/runtime/components/AModalShell.vue.d.ts +48 -0
  21. package/dist/runtime/components/ANodePanel.d.vue.ts +17 -7
  22. package/dist/runtime/components/ANodePanel.vue +550 -451
  23. package/dist/runtime/components/ANodePanel.vue.d.ts +17 -7
  24. package/dist/runtime/components/ANodePanelHeader.d.vue.ts +20 -10
  25. package/dist/runtime/components/ANodePanelHeader.vue +17 -3
  26. package/dist/runtime/components/ANodePanelHeader.vue.d.ts +20 -10
  27. package/dist/runtime/components/ANodeSettingsPanel.d.vue.ts +2 -0
  28. package/dist/runtime/components/ANodeSettingsPanel.vue +21 -1
  29. package/dist/runtime/components/ANodeSettingsPanel.vue.d.ts +2 -0
  30. package/dist/runtime/components/ASnapshotPreviewModal.d.vue.ts +33 -0
  31. package/dist/runtime/components/ASnapshotPreviewModal.vue +430 -0
  32. package/dist/runtime/components/ASnapshotPreviewModal.vue.d.ts +33 -0
  33. package/dist/runtime/components/ATagsEditor.d.vue.ts +19 -0
  34. package/dist/runtime/components/ATagsEditor.vue +60 -0
  35. package/dist/runtime/components/ATagsEditor.vue.d.ts +19 -0
  36. package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
  37. package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
  38. package/dist/runtime/components/chat/AChatInput.d.vue.ts +11 -6
  39. package/dist/runtime/components/chat/AChatInput.vue +33 -2
  40. package/dist/runtime/components/chat/AChatInput.vue.d.ts +11 -6
  41. package/dist/runtime/components/chat/AChatList.d.vue.ts +12 -0
  42. package/dist/runtime/components/chat/AChatList.vue +76 -32
  43. package/dist/runtime/components/chat/AChatList.vue.d.ts +12 -0
  44. package/dist/runtime/components/chat/AChatMessages.d.vue.ts +4 -0
  45. package/dist/runtime/components/chat/AChatMessages.vue +57 -4
  46. package/dist/runtime/components/chat/AChatMessages.vue.d.ts +4 -0
  47. package/dist/runtime/components/chat/AChatPanel.d.vue.ts +6 -2
  48. package/dist/runtime/components/chat/AChatPanel.vue +17 -1
  49. package/dist/runtime/components/chat/AChatPanel.vue.d.ts +6 -2
  50. package/dist/runtime/components/chat/ANodeChatPanel.vue +1 -1
  51. package/dist/runtime/components/docs/ADocsSearch.d.vue.ts +1 -1
  52. package/dist/runtime/components/docs/ADocsSearch.vue.d.ts +1 -1
  53. package/dist/runtime/components/editor/ALocationPickerPopover.vue +28 -7
  54. package/dist/runtime/components/registry/APluginDetail.d.vue.ts +2 -2
  55. package/dist/runtime/components/registry/APluginDetail.vue.d.ts +2 -2
  56. package/dist/runtime/components/renderers/AChartRenderer.client.d.vue.ts +17 -0
  57. package/dist/runtime/components/renderers/AChartRenderer.client.vue +622 -0
  58. package/dist/runtime/components/renderers/AChartRenderer.client.vue.d.ts +17 -0
  59. package/dist/runtime/components/renderers/AGraphRenderer.vue +64 -15
  60. package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +2 -2
  61. package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +2 -2
  62. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.d.vue.ts +2 -2
  63. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.vue.d.ts +2 -2
  64. package/dist/runtime/components/renderers/media/MediaTransportBar.d.vue.ts +2 -2
  65. package/dist/runtime/components/renderers/media/MediaTransportBar.vue.d.ts +2 -2
  66. package/dist/runtime/components/renderers/sheets/ASheetsGrid.d.vue.ts +2 -2
  67. package/dist/runtime/components/renderers/sheets/ASheetsGrid.vue.d.ts +2 -2
  68. package/dist/runtime/components/settings/ASettingsAppearancePanel.d.vue.ts +3 -0
  69. package/dist/runtime/components/settings/ASettingsAppearancePanel.vue +67 -0
  70. package/dist/runtime/components/settings/ASettingsAppearancePanel.vue.d.ts +3 -0
  71. package/dist/runtime/components/settings/ASettingsGroup.d.vue.ts +24 -0
  72. package/dist/runtime/components/settings/ASettingsGroup.vue +31 -0
  73. package/dist/runtime/components/settings/ASettingsGroup.vue.d.ts +24 -0
  74. package/dist/runtime/components/settings/ASettingsModal.vue +84 -53
  75. package/dist/runtime/components/settings/ASettingsPlaceholder.d.vue.ts +20 -0
  76. package/dist/runtime/components/settings/ASettingsPlaceholder.vue +32 -0
  77. package/dist/runtime/components/settings/ASettingsPlaceholder.vue.d.ts +20 -0
  78. package/dist/runtime/components/settings/ASettingsRow.d.vue.ts +34 -0
  79. package/dist/runtime/components/settings/ASettingsRow.vue +34 -0
  80. package/dist/runtime/components/settings/ASettingsRow.vue.d.ts +34 -0
  81. package/dist/runtime/components/settings/sections.d.ts +37 -0
  82. package/dist/runtime/components/settings/sections.js +45 -0
  83. package/dist/runtime/components/shell/ABreadcrumbForDoc.d.vue.ts +6 -0
  84. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue +75 -3
  85. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue.d.ts +6 -0
  86. package/dist/runtime/components/shell/ADocPanelServerSettings.d.vue.ts +17 -0
  87. package/dist/runtime/components/shell/ADocPanelServerSettings.vue +253 -0
  88. package/dist/runtime/components/shell/ADocPanelServerSettings.vue.d.ts +17 -0
  89. package/dist/runtime/components/shell/ADocPanelSettings.d.vue.ts +2 -0
  90. package/dist/runtime/components/shell/ADocPanelSettings.vue +15 -4
  91. package/dist/runtime/components/shell/ADocPanelSettings.vue.d.ts +2 -0
  92. package/dist/runtime/components/shell/AUserProfilePopover.d.vue.ts +1 -1
  93. package/dist/runtime/components/shell/AUserProfilePopover.vue.d.ts +1 -1
  94. package/dist/runtime/composables/useChat.d.ts +22 -1
  95. package/dist/runtime/composables/useChat.js +79 -8
  96. package/dist/runtime/composables/useDocBreadcrumb.d.ts +17 -2
  97. package/dist/runtime/composables/useDocBreadcrumb.js +17 -3
  98. package/dist/runtime/composables/useDocSnapshots.d.ts +2 -1
  99. package/dist/runtime/composables/useDocSnapshots.js +5 -0
  100. package/dist/runtime/composables/useEditor.d.ts +1 -1
  101. package/dist/runtime/composables/useEditor.js +120 -0
  102. package/dist/runtime/composables/useEditorToolbar.d.ts +12 -4
  103. package/dist/runtime/composables/useEditorToolbar.js +78 -56
  104. package/dist/runtime/composables/useNodeContextMenu.d.ts +14 -0
  105. package/dist/runtime/composables/useNodeContextMenu.js +59 -1
  106. package/dist/runtime/composables/useSettingsModal.d.ts +1 -1
  107. package/dist/runtime/composables/useSwipeGesture.d.ts +48 -0
  108. package/dist/runtime/composables/useSwipeGesture.js +140 -0
  109. package/dist/runtime/extensions/document-header.js +16 -6
  110. package/dist/runtime/extensions/document-meta.js +344 -19
  111. package/dist/runtime/extensions/meta-field.js +42 -0
  112. package/dist/runtime/extensions/views/DocumentMetaView.vue +33 -7
  113. package/dist/runtime/extensions/views/FieldView.vue +51 -19
  114. package/dist/runtime/extensions/views/MetaFieldView.vue +30 -4
  115. package/dist/runtime/locale.d.ts +8 -0
  116. package/dist/runtime/locale.js +9 -1
  117. package/dist/runtime/middleware/abracadabra-auth.d.ts +1 -1
  118. package/dist/runtime/plugin-abracadabra.client.d.ts +1 -1
  119. package/dist/runtime/plugin-abracadabra.client.js +12 -2
  120. package/dist/runtime/plugin-abracadabra.server.d.ts +1 -1
  121. package/dist/runtime/plugin-shared-globals.client.d.ts +1 -1
  122. package/dist/runtime/utils/chatContent.d.ts +20 -2
  123. package/dist/runtime/utils/chatContent.js +20 -1
  124. package/dist/runtime/utils/docTypes.js +43 -0
  125. package/dist/runtime/utils/titleSync.d.ts +130 -0
  126. package/dist/runtime/utils/titleSync.js +53 -0
  127. package/package.json +11 -4
@@ -5,7 +5,11 @@ import { Fragment } from "@tiptap/pm/model";
5
5
  import { useSyncedMap } from "../composables/useYDoc";
6
6
  import { resolveDocType, GEO_TYPE_META_SCHEMAS } from "../utils/docTypes";
7
7
  import { schemaFieldToAttrs, userFieldToAttrs } from "../extensions/meta-field";
8
+ import { decideTitleSync, setHeaderText as applyHeaderText, UNTITLED } from "../utils/titleSync";
8
9
  import { useEditor } from "../composables/useEditor";
10
+ import { useDocTree } from "../composables/useDocTree";
11
+ import { useDocBreadcrumb } from "../composables/useDocBreadcrumb";
12
+ import { useDocSlugs } from "../composables/useDocSlugs";
9
13
  import { useEditorToolbar } from "../composables/useEditorToolbar";
10
14
  import { useEditorSuggestions } from "../composables/useEditorSuggestions";
11
15
  import { useEditorDragHandle } from "../composables/useEditorDragHandle";
@@ -27,7 +31,8 @@ const props = defineProps({
27
31
  parentType: { type: String, required: false },
28
32
  metaSchema: { type: Array, required: false },
29
33
  variant: { type: String, required: false, default: "doc" },
30
- showSourceToggle: { type: Boolean, required: false, default: false }
34
+ showSourceToggle: { type: Boolean, required: false, default: false },
35
+ showBreadcrumb: { type: Boolean, required: false, default: true }
31
36
  });
32
37
  const viewMode = ref("rich");
33
38
  const emit = defineEmits(["ready", "update", "rename", "updateMeta"]);
@@ -57,12 +62,21 @@ watch(ready, (val) => {
57
62
  const editorRef = ref(null);
58
63
  const tiptapEditor = computed(() => editorRef.value?.editor ?? null);
59
64
  defineExpose({ editor: tiptapEditor });
60
- const { items: toolbarItems } = useEditorToolbar({ docId: props.docId });
65
+ const { bubbleItems: toolbarItems } = useEditorToolbar({ docId: props.docId });
66
+ const docTree = useDocTree();
61
67
  const resolvedMetaSchema = computed(() => {
62
68
  if (props.metaSchema) return props.metaSchema;
63
69
  const geoType = props.docMeta?.geoType;
64
70
  if (geoType && geoType in GEO_TYPE_META_SCHEMAS)
65
71
  return GEO_TYPE_META_SCHEMAS[geoType];
72
+ let pid = docTree.getEntry(props.docId)?.parentId ?? void 0;
73
+ while (pid) {
74
+ const entry = docTree.getEntry(pid);
75
+ if (!entry) break;
76
+ const schema = resolveDocType(entry.type, registry).metaSchema;
77
+ if (schema && schema.length > 0) return schema;
78
+ pid = entry.parentId ?? void 0;
79
+ }
66
80
  return resolveDocType(props.parentType, registry).metaSchema ?? [];
67
81
  });
68
82
  const { items: allSuggestionItems, propertiesOnlyItems } = useEditorSuggestions({ docId: props.docId });
@@ -157,79 +171,93 @@ function initDocumentMeta(ed) {
157
171
  metaInitDone = true;
158
172
  }
159
173
  let syncingFromEditor = false;
160
- const _lastEmittedHeader = ref("");
174
+ let lastSyncedLabel = null;
161
175
  function getHeaderText(ed) {
162
176
  const first = ed.state.doc.firstChild;
163
177
  if (!first || first.type.name !== "documentHeader") return "";
164
178
  return first.textContent;
165
179
  }
166
- function setHeaderText(ed, text) {
167
- const first = ed.state.doc.firstChild;
168
- if (!first || first.type.name !== "documentHeader") return;
169
- if (first.textContent === text) return;
170
- const { tr, schema } = ed.state;
171
- const to = 1 + first.content.size;
172
- if (text) {
173
- tr.replaceWith(1, to, schema.text(text));
174
- } else {
175
- tr.delete(1, to);
176
- }
177
- ed.view.dispatch(tr);
180
+ function currentTreeLabel() {
181
+ return _treeMap.data[props.docId]?.label ?? props.docLabel ?? "";
178
182
  }
179
- function syncHeaderToTree(ed) {
180
- const text = getHeaderText(ed);
181
- if (text === _lastEmittedHeader.value) return;
182
- _lastEmittedHeader.value = text;
183
+ function writeTreeLabel(label) {
184
+ const e = _treeMap.data[props.docId];
185
+ const next = label || UNTITLED;
186
+ if (!e || e.label === next) return;
183
187
  syncingFromEditor = true;
184
- emit("rename", text || "Untitled");
188
+ lastSyncedLabel = next;
189
+ _treeMap.set(props.docId, { ...e, label: next, updatedAt: Date.now() });
190
+ emit("rename", next);
185
191
  nextTick(() => {
186
192
  syncingFromEditor = false;
187
193
  });
188
194
  }
195
+ function syncHeader(ed, isRemoteUpdate, initialSyncDone) {
196
+ const action = decideTitleSync({
197
+ headerText: getHeaderText(ed),
198
+ treeLabel: currentTreeLabel(),
199
+ isRemoteUpdate,
200
+ initialSyncDone
201
+ });
202
+ if (action.kind === "noop") return;
203
+ if (action.kind === "header-from-tree") {
204
+ applyHeaderText(ed, action.text);
205
+ return;
206
+ }
207
+ writeTreeLabel(action.label);
208
+ }
189
209
  watchEffect((onCleanup) => {
190
210
  const ed = editorRef.value?.editor;
191
211
  if (!ed || !ready.value) return;
192
212
  let initialSyncDone = false;
193
- function doInitialSync() {
194
- if (initialSyncDone) return;
195
- initialSyncDone = true;
196
- const headerText = getHeaderText(ed);
197
- const treeLabel = props.docLabel ?? "";
198
- if (headerText !== treeLabel) {
199
- const treeMeansEmpty = !treeLabel || treeLabel === "Untitled";
200
- const headerMeansEmpty = !headerText;
201
- if (treeMeansEmpty && headerMeansEmpty) return;
202
- if (!treeMeansEmpty && headerMeansEmpty) {
203
- setHeaderText(ed, treeLabel);
204
- return;
213
+ let headerTimer = null;
214
+ const runSync = (isRemote) => {
215
+ const wasInitial = !initialSyncDone;
216
+ if (wasInitial) initialSyncDone = true;
217
+ syncHeader(ed, isRemote, !wasInitial);
218
+ };
219
+ function onUpdate({ transaction }) {
220
+ const isRemote = !!transaction?.getMeta?.("y-sync$")?.isChangeOrigin;
221
+ initDocumentMeta(ed);
222
+ if (isRemote || !initialSyncDone) {
223
+ if (headerTimer) {
224
+ clearTimeout(headerTimer);
225
+ headerTimer = null;
205
226
  }
206
- _lastEmittedHeader.value = headerText;
207
- syncingFromEditor = true;
208
- emit("rename", headerText);
209
- nextTick(() => {
210
- syncingFromEditor = false;
211
- });
227
+ runSync(isRemote);
228
+ } else {
229
+ if (headerTimer) clearTimeout(headerTimer);
230
+ headerTimer = setTimeout(() => {
231
+ headerTimer = null;
232
+ runSync(false);
233
+ }, 200);
212
234
  }
213
235
  }
214
- function onUpdate() {
215
- syncHeaderToTree(ed);
216
- initDocumentMeta(ed);
217
- }
218
236
  nextTick(() => {
219
- doInitialSync();
237
+ runSync(false);
220
238
  initDocumentMeta(ed);
221
239
  });
222
240
  ed.on("update", onUpdate);
223
- onCleanup(() => ed.off("update", onUpdate));
224
- });
225
- watch(() => props.docLabel, (newLabel) => {
226
- if (syncingFromEditor) return;
227
- if (newLabel === _lastEmittedHeader.value) return;
228
- if (!_treeMap.lastUpdateLocal.value) return;
229
- const ed = editorRef.value?.editor;
230
- if (!ed || !ready.value) return;
231
- setHeaderText(ed, newLabel || "");
241
+ onCleanup(() => {
242
+ if (headerTimer) clearTimeout(headerTimer);
243
+ ed.off("update", onUpdate);
244
+ });
232
245
  });
246
+ watch(
247
+ () => _treeMap.data[props.docId]?.label ?? props.docLabel,
248
+ (newLabel) => {
249
+ if (syncingFromEditor) return;
250
+ if (lastSyncedLabel !== null && newLabel === lastSyncedLabel) {
251
+ lastSyncedLabel = null;
252
+ return;
253
+ }
254
+ lastSyncedLabel = null;
255
+ if (newLabel === void 0 || newLabel === "" || newLabel === UNTITLED) return;
256
+ const ed = editorRef.value?.editor;
257
+ if (!ed || !ready.value) return;
258
+ applyHeaderText(ed, newLabel);
259
+ }
260
+ );
233
261
  function insertNode(editor, type, attrs, content) {
234
262
  const node = { type, attrs };
235
263
  if (content) node.content = content;
@@ -347,6 +375,11 @@ const editorHandlers = {
347
375
  const _mentionItems = computed(
348
376
  () => registry.getAllMentionProviders().flatMap((p) => p.label ? [p] : [])
349
377
  );
378
+ const { items: _breadcrumbItems } = useDocBreadcrumb(() => props.docId);
379
+ const { getDocUrl } = useDocSlugs();
380
+ const ancestorChain = computed(
381
+ () => _breadcrumbItems.value.slice(0, -1).map((a) => ({ ...a, to: getDocUrl(a.id) }))
382
+ );
350
383
  function onPlusClick(e, onClick) {
351
384
  e.stopPropagation();
352
385
  onClick();
@@ -377,7 +410,10 @@ function onPlusClick(e, onClick) {
377
410
  >
378
411
  <div class="aeditor-source-toolbar">
379
412
  <span class="aeditor-source-toolbar__label">
380
- <UIcon name="i-lucide-code-xml" class="size-3.5" />
413
+ <UIcon
414
+ name="i-lucide-code-xml"
415
+ class="size-3.5"
416
+ />
381
417
  XML source — read-only mirror of <code>getXmlFragment('default')</code>
382
418
  </span>
383
419
  <UButton
@@ -398,136 +434,178 @@ function onPlusClick(e, onClick) {
398
434
  />
399
435
  </div>
400
436
 
401
- <UEditor
437
+ <!-- Editor canvas: ancestor breadcrumb (cou-sh parity) + the TipTap editor,
438
+ stacked in one scroll container (the parent's overflow class falls
439
+ through to this wrapper). -->
440
+ <div
402
441
  v-else
403
- :class="{ 'prose-variant': variant === 'prose' }"
404
- ref="editorRef"
405
- v-slot="{ editor }"
406
- v-model="model"
407
- :content-type="contentType"
408
- :editable="editable"
409
- :extensions="extensions"
410
- :starter-kit="{ undoRedo: false, codeBlock: false, document: false }"
411
- :handlers="editorHandlers"
412
- :placeholder="placeholder"
413
- @update:model-value="emit('update', $event)"
442
+ class="aeditor-canvas"
414
443
  >
415
- <!-- Floating source-view toggle (opt-in via :show-source-toggle="true") -->
416
- <UButton
417
- v-if="showSourceToggle"
418
- class="aeditor-source-toggle"
419
- icon="i-lucide-code-xml"
420
- size="xs"
421
- variant="ghost"
422
- color="neutral"
423
- :title="'View XML source'"
424
- @click="viewMode = 'source'"
425
- />
426
- <!-- Default slot: app can override entire editor content -->
427
- <slot
428
- :editor="editor"
429
- :connected-users="connectedUsers"
430
- :ready="ready"
444
+ <nav
445
+ v-if="showBreadcrumb && ancestorChain.length"
446
+ aria-label="Breadcrumb"
447
+ class="aeditor-breadcrumb"
431
448
  >
432
- <!-- ── Bubble toolbar (appears when text is selected) ─────────────── -->
433
- <!-- 4 named slots are forwarded to consumers:
434
- #link / #doc-link defaulted to ALinkPopover / ADocLinkPopover
435
- #create-child-doc / #send-to-chat — empty by default; app-defined -->
436
- <UEditorToolbar
437
- v-if="showToolbar"
438
- :editor="editor"
439
- :items="toolbarItems"
440
- layout="bubble"
441
- :should-show="({ view, state }) => view.hasFocus() && !state.selection.empty"
442
- >
443
- <template #link>
444
- <slot
445
- name="link"
446
- :editor="editor"
447
- >
448
- <ALinkPopover :editor="editor" />
449
- </slot>
450
- </template>
451
- <template #doc-link>
452
- <slot
453
- name="doc-link"
454
- :editor="editor"
449
+ <ol class="flex items-center gap-0.5 min-w-0">
450
+ <template
451
+ v-for="(ancestor, idx) in ancestorChain"
452
+ :key="ancestor.id"
453
+ >
454
+ <li
455
+ v-if="idx > 0"
456
+ class="flex shrink-0"
455
457
  >
456
- <ADocLinkPopover :editor="editor" />
457
- </slot>
458
- </template>
459
- <template #create-child-doc>
460
- <slot
461
- name="create-child-doc"
462
- :editor="editor"
463
- />
458
+ <UIcon
459
+ name="i-lucide-chevron-right"
460
+ class="size-4 text-(--ui-text-dimmed)"
461
+ />
462
+ </li>
463
+ <li class="flex min-w-0">
464
+ <ULink
465
+ :to="ancestor.to"
466
+ class="flex items-center gap-1.5 px-1.5 py-0.5 rounded-md text-sm text-(--ui-text-muted) font-medium hover:bg-(--ui-bg-elevated)/60 hover:text-(--ui-text) transition-colors min-w-0 select-none"
467
+ >
468
+ <UIcon
469
+ :name="ancestor.icon"
470
+ class="size-4 shrink-0"
471
+ />
472
+ <span class="truncate">{{ ancestor.label }}</span>
473
+ </ULink>
474
+ </li>
464
475
  </template>
465
- <template #send-to-chat>
466
- <slot
467
- name="send-to-chat"
468
- :editor="editor"
469
- />
470
- </template>
471
- </UEditorToolbar>
476
+ </ol>
477
+ </nav>
472
478
 
473
- <!-- ── Slash command menu ──────────────────────────────────────────── -->
474
- <UEditorSuggestionMenu
475
- v-if="showSuggestionMenu"
476
- :editor="editor"
477
- :items="suggestionItems"
479
+ <UEditor
480
+ ref="editorRef"
481
+ v-slot="{ editor }"
482
+ v-model="model"
483
+ :class="{ 'prose-variant': variant === 'prose' }"
484
+ :content-type="contentType"
485
+ :editable="editable"
486
+ :extensions="extensions"
487
+ :starter-kit="{ undoRedo: false, codeBlock: false, document: false }"
488
+ :handlers="editorHandlers"
489
+ :placeholder="placeholder"
490
+ @update:model-value="emit('update', $event)"
491
+ >
492
+ <!-- Floating source-view toggle (opt-in via :show-source-toggle="true") -->
493
+ <UButton
494
+ v-if="showSourceToggle"
495
+ class="aeditor-source-toggle"
496
+ icon="i-lucide-code-xml"
497
+ size="xs"
498
+ variant="ghost"
499
+ color="neutral"
500
+ :title="'View XML source'"
501
+ @click="viewMode = 'source'"
478
502
  />
479
-
480
- <!-- ── Emoji menu (`:` trigger) ───────────────────────────────────── -->
481
- <UEditorEmojiMenu
503
+ <!-- Default slot: app can override entire editor content -->
504
+ <slot
482
505
  :editor="editor"
483
- :items="emojiItems"
484
- />
506
+ :connected-users="connectedUsers"
507
+ :ready="ready"
508
+ >
509
+ <!-- ── Bubble toolbar (appears when text is selected) ─────────────── -->
510
+ <!-- 4 named slots are forwarded to consumers:
511
+ #link / #doc-link — defaulted to ALinkPopover / ADocLinkPopover
512
+ #create-child-doc / #send-to-chat — empty by default; app-defined -->
513
+ <UEditorToolbar
514
+ v-if="showToolbar"
515
+ :editor="editor"
516
+ :items="toolbarItems"
517
+ layout="bubble"
518
+ :should-show="({ view, state }) => view.hasFocus() && !state.selection.empty"
519
+ >
520
+ <template #link>
521
+ <slot
522
+ name="link"
523
+ :editor="editor"
524
+ >
525
+ <ALinkPopover :editor="editor" />
526
+ </slot>
527
+ </template>
528
+ <template #doc-link>
529
+ <slot
530
+ name="doc-link"
531
+ :editor="editor"
532
+ >
533
+ <ADocLinkPopover :editor="editor" />
534
+ </slot>
535
+ </template>
536
+ <template #create-child-doc>
537
+ <slot
538
+ name="create-child-doc"
539
+ :editor="editor"
540
+ />
541
+ </template>
542
+ <template #send-to-chat>
543
+ <slot
544
+ name="send-to-chat"
545
+ :editor="editor"
546
+ />
547
+ </template>
548
+ </UEditorToolbar>
485
549
 
486
- <!-- ── Mention menu (`@` trigger) — users + docs + plugin providers ── -->
487
- <UEditorMentionMenu
488
- :editor="editor"
489
- :items="mentionItems"
490
- />
550
+ <!-- ── Slash command menu ──────────────────────────────────────────── -->
551
+ <UEditorSuggestionMenu
552
+ v-if="showSuggestionMenu"
553
+ :editor="editor"
554
+ :items="suggestionItems"
555
+ />
491
556
 
492
- <!-- ── Drag handle plus button + grip dropdown ───────────────────── -->
493
- <UEditorDragHandle
494
- v-if="showDragHandle"
495
- v-slot="{ ui, onClick }"
496
- :editor="editor"
497
- @node-change="dragHandle.onNodeChange"
498
- >
499
- <!-- Plus: insert block via slash menu -->
500
- <UButton
501
- icon="i-lucide-plus"
502
- color="neutral"
503
- variant="ghost"
504
- size="sm"
505
- :class="ui.handle()"
506
- @click="(e) => onPlusClick(e, onClick)"
557
+ <!-- ── Emoji menu (`:` trigger) ───────────────────────────────────── -->
558
+ <UEditorEmojiMenu
559
+ :editor="editor"
560
+ :items="emojiItems"
561
+ />
562
+
563
+ <!-- ── Mention menu (`@` trigger) — users + docs + plugin providers ── -->
564
+ <UEditorMentionMenu
565
+ :editor="editor"
566
+ :items="mentionItems"
507
567
  />
508
568
 
509
- <!-- Grip: block context menu -->
510
- <UDropdownMenu
511
- v-slot="{ open }"
512
- :modal="false"
513
- :items="dragHandle.getItems(editor)"
514
- :content="{ side: 'left' }"
515
- :ui="{ content: 'w-48', label: 'text-xs' }"
516
- @update:open="(v) => editor.chain().setMeta('lockDragHandle', v).run()"
569
+ <!-- ── Drag handle plus button + grip dropdown ───────────────────── -->
570
+ <UEditorDragHandle
571
+ v-if="showDragHandle"
572
+ v-slot="{ ui, onClick }"
573
+ :editor="editor"
574
+ @node-change="dragHandle.onNodeChange"
517
575
  >
576
+ <!-- Plus: insert block via slash menu -->
518
577
  <UButton
578
+ icon="i-lucide-plus"
519
579
  color="neutral"
520
580
  variant="ghost"
521
- :active-variant="'soft'"
522
581
  size="sm"
523
- icon="i-lucide-grip-vertical"
524
- :active="open"
525
582
  :class="ui.handle()"
583
+ @click="(e) => onPlusClick(e, onClick)"
526
584
  />
527
- </UDropdownMenu>
528
- </UEditorDragHandle>
529
- </slot>
530
- </UEditor>
585
+
586
+ <!-- Grip: block context menu -->
587
+ <UDropdownMenu
588
+ v-slot="{ open }"
589
+ :modal="false"
590
+ :items="dragHandle.getItems(editor)"
591
+ :content="{ side: 'left' }"
592
+ :ui="{ content: 'w-48', label: 'text-xs' }"
593
+ @update:open="(v) => editor.chain().setMeta('lockDragHandle', v).run()"
594
+ >
595
+ <UButton
596
+ color="neutral"
597
+ variant="ghost"
598
+ :active-variant="'soft'"
599
+ size="sm"
600
+ icon="i-lucide-grip-vertical"
601
+ :active="open"
602
+ :class="ui.handle()"
603
+ />
604
+ </UDropdownMenu>
605
+ </UEditorDragHandle>
606
+ </slot>
607
+ </UEditor>
608
+ </div>
531
609
 
532
610
  <template #fallback>
533
611
  <div
@@ -543,5 +621,5 @@ function onPlusClick(e, onClick) {
543
621
  </template>
544
622
 
545
623
  <style scoped>
546
- .prose-variant :deep(.tiptap){color:var(--ui-text);font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;font-size:1.0625rem;line-height:1.75}.prose-variant :deep(.tiptap p){margin-bottom:1em;margin-top:0}.prose-variant :deep(.tiptap h1),.prose-variant :deep(.tiptap h2),.prose-variant :deep(.tiptap h3),.prose-variant :deep(.tiptap h4){font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;letter-spacing:-.01em;line-height:1.25;margin-bottom:.5em;margin-top:1.75em}.prose-variant :deep(.tiptap h1){font-size:2rem;font-weight:700}.prose-variant :deep(.tiptap h2){font-size:1.5rem;font-weight:700}.prose-variant :deep(.tiptap h3){font-size:1.25rem;font-weight:600}.prose-variant :deep(.tiptap blockquote){border-left:3px solid var(--ui-border);color:var(--ui-text-muted);font-style:italic;margin-left:0;padding-left:1em}.prose-variant :deep(.tiptap ol),.prose-variant :deep(.tiptap ul){margin-bottom:1em;padding-left:1.5em}.prose-variant :deep(.tiptap .document-header){font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;font-size:2.75rem;font-weight:700;letter-spacing:-.02em;line-height:1.15;margin-bottom:1.5rem}.aeditor-source-wrap{display:flex;flex:1 1 0;flex-direction:column;min-height:0}.aeditor-source-toolbar{align-items:center;background:var(--ui-bg);border-bottom:1px solid var(--ui-border);display:flex;flex-shrink:0;justify-content:space-between;padding:.375rem .75rem}.aeditor-source-toolbar__label{align-items:center;color:var(--ui-text-muted);display:inline-flex;font-size:.75rem;gap:.375rem}.aeditor-source-toolbar__label code{background:var(--ui-bg-elevated);border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-family:ui-monospace,SF Mono,Menlo,Monaco,Consolas,monospace;font-size:.7rem;padding:.05rem .3rem}.aeditor-source-toggle{opacity:.6;position:absolute;right:.375rem;top:.375rem;transition:opacity .12s ease;z-index:5}.aeditor-source-toggle:hover{opacity:1}
624
+ .aeditor-canvas{display:flex;flex-direction:column;min-height:0}.aeditor-breadcrumb{flex-shrink:0;padding:.75rem 1rem 0}@media (min-width:640px){.aeditor-breadcrumb{padding-left:1.5rem;padding-right:1.5rem}}.prose-variant :deep(.tiptap){color:var(--ui-text);font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;font-size:1.0625rem;line-height:1.75}.prose-variant :deep(.tiptap p){margin-bottom:1em;margin-top:0}.prose-variant :deep(.tiptap h1),.prose-variant :deep(.tiptap h2),.prose-variant :deep(.tiptap h3),.prose-variant :deep(.tiptap h4){font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;letter-spacing:-.01em;line-height:1.25;margin-bottom:.5em;margin-top:1.75em}.prose-variant :deep(.tiptap h1){font-size:2rem;font-weight:700}.prose-variant :deep(.tiptap h2){font-size:1.5rem;font-weight:700}.prose-variant :deep(.tiptap h3){font-size:1.25rem;font-weight:600}.prose-variant :deep(.tiptap blockquote){border-left:3px solid var(--ui-border);color:var(--ui-text-muted);font-style:italic;margin-left:0;padding-left:1em}.prose-variant :deep(.tiptap ol),.prose-variant :deep(.tiptap ul){margin-bottom:1em;padding-left:1.5em}.prose-variant :deep(.tiptap .document-header){font-family:ui-serif,Georgia,Cambria,Times New Roman,Times,serif;font-size:2.75rem;font-weight:700;letter-spacing:-.02em;line-height:1.15;margin-bottom:1.5rem}.aeditor-source-wrap{display:flex;flex:1 1 0;flex-direction:column;min-height:0}.aeditor-source-toolbar{align-items:center;background:var(--ui-bg);border-bottom:1px solid var(--ui-border);display:flex;flex-shrink:0;justify-content:space-between;padding:.375rem .75rem}.aeditor-source-toolbar__label{align-items:center;color:var(--ui-text-muted);display:inline-flex;font-size:.75rem;gap:.375rem}.aeditor-source-toolbar__label code{background:var(--ui-bg-elevated);border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-family:ui-monospace,SF Mono,Menlo,Monaco,Consolas,monospace;font-size:.7rem;padding:.05rem .3rem}.aeditor-source-toggle{opacity:.6;position:absolute;right:.375rem;top:.375rem;transition:opacity .12s ease;z-index:5}.aeditor-source-toggle:hover{opacity:1}
547
625
  </style>
@@ -35,12 +35,18 @@ type __VLS_Props = {
35
35
  * Useful for inspecting CRDT state during development. Default: false.
36
36
  */
37
37
  showSourceToggle?: boolean;
38
+ /**
39
+ * Show the in-canvas ancestor breadcrumb above the document (matching
40
+ * cou-sh's DocRenderer). Only renders when the doc actually has ancestors,
41
+ * so root/space docs show nothing. Set false for embeds/previews.
42
+ */
43
+ showBreadcrumb?: boolean;
38
44
  };
39
45
  type __VLS_ModelProps = {
40
46
  modelValue?: any;
41
47
  };
42
48
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
43
- declare var __VLS_42: {
49
+ declare var __VLS_58: {
44
50
  editor: any;
45
51
  connectedUsers: {
46
52
  name: string;
@@ -49,40 +55,40 @@ declare var __VLS_42: {
49
55
  docId?: string | undefined;
50
56
  }[];
51
57
  ready: boolean;
52
- }, __VLS_51: {
58
+ }, __VLS_67: {
53
59
  editor: any;
54
- }, __VLS_59: {
60
+ }, __VLS_75: {
55
61
  editor: any;
56
- }, __VLS_67: {
62
+ }, __VLS_83: {
57
63
  editor: any;
58
- }, __VLS_70: {
64
+ }, __VLS_86: {
59
65
  editor: any;
60
66
  };
61
67
  type __VLS_Slots = {} & {
62
- default?: (props: typeof __VLS_42) => any;
68
+ default?: (props: typeof __VLS_58) => any;
63
69
  } & {
64
- link?: (props: typeof __VLS_51) => any;
70
+ link?: (props: typeof __VLS_67) => any;
65
71
  } & {
66
- 'doc-link'?: (props: typeof __VLS_59) => any;
72
+ 'doc-link'?: (props: typeof __VLS_75) => any;
67
73
  } & {
68
- 'create-child-doc'?: (props: typeof __VLS_67) => any;
74
+ 'create-child-doc'?: (props: typeof __VLS_83) => any;
69
75
  } & {
70
- 'send-to-chat'?: (props: typeof __VLS_70) => any;
76
+ 'send-to-chat'?: (props: typeof __VLS_86) => any;
71
77
  };
72
78
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
73
79
  editor: import("vue").ComputedRef<any>;
74
80
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
75
81
  rename: (label: string) => any;
82
+ ready: () => any;
76
83
  update: (content: any) => any;
77
84
  "update:modelValue": (value: any) => any;
78
85
  updateMeta: (patch: Partial<DocPageMeta>) => any;
79
- ready: () => any;
80
86
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
81
87
  onRename?: ((label: string) => any) | undefined;
88
+ onReady?: (() => any) | undefined;
82
89
  onUpdate?: ((content: any) => any) | undefined;
83
90
  "onUpdate:modelValue"?: ((value: any) => any) | undefined;
84
91
  onUpdateMeta?: ((patch: Partial<DocPageMeta>) => any) | undefined;
85
- onReady?: (() => any) | undefined;
86
92
  }>, {
87
93
  contentType: "json" | "html" | "markdown";
88
94
  editable: boolean;
@@ -91,6 +97,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
91
97
  showDragHandle: boolean;
92
98
  variant: "doc" | "prose";
93
99
  showSourceToggle: boolean;
100
+ showBreadcrumb: boolean;
94
101
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
95
102
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
96
103
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,33 @@
1
+ import type { DocEncryptionInfo } from '@abraca/dabra';
2
+ type __VLS_Props = {
3
+ docId: string;
4
+ /** Override any displayed string. */
5
+ labels?: Partial<typeof DEFAULTS>;
6
+ };
7
+ declare const DEFAULTS: {
8
+ noneTitle: string;
9
+ noneDesc: string;
10
+ cseTitle: string;
11
+ cseDesc: string;
12
+ e2eTitle: string;
13
+ e2eDesc: string;
14
+ cannotDowngrade: string;
15
+ loadFailed: string;
16
+ retry: string;
17
+ enableE2ETitle: string;
18
+ cannotBeUndone: string;
19
+ warningServerCantRead: string;
20
+ warningNoSearch: string;
21
+ warningNoOffline: string;
22
+ warningKeysRequired: string;
23
+ warningCannotUndo: string;
24
+ cancel: string;
25
+ enableE2E: string;
26
+ };
27
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
28
+ updated: (args_0: DocEncryptionInfo) => any;
29
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
+ onUpdated?: ((args_0: DocEncryptionInfo) => any) | undefined;
31
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: typeof __VLS_export;
33
+ export default _default;