@abraca/nuxt 0.2.0 → 0.3.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 (152) hide show
  1. package/dist/module.d.mts +46 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +95 -2
  4. package/dist/runtime/assets/editor.css +1 -0
  5. package/dist/runtime/components/ACommandPalette.vue +4 -1
  6. package/dist/runtime/components/ADocRenderer.d.vue.ts +29 -0
  7. package/dist/runtime/components/ADocRenderer.vue +99 -0
  8. package/dist/runtime/components/ADocRenderer.vue.d.ts +29 -0
  9. package/dist/runtime/components/ADocTypeSelect.vue +4 -1
  10. package/dist/runtime/components/ADocumentTree.vue +78 -19
  11. package/dist/runtime/components/AEditor.d.vue.ts +9 -4
  12. package/dist/runtime/components/AEditor.vue +102 -7
  13. package/dist/runtime/components/AEditor.vue.d.ts +9 -4
  14. package/dist/runtime/components/AIconPicker.vue +8 -2
  15. package/dist/runtime/components/ANodePanel.vue +100 -61
  16. package/dist/runtime/components/ANotifications.vue +35 -8
  17. package/dist/runtime/components/APermissionGuard.vue +3 -1
  18. package/dist/runtime/components/APresence.vue +14 -3
  19. package/dist/runtime/components/AProvider.vue +7 -1
  20. package/dist/runtime/components/AVoiceBar.vue +57 -15
  21. package/dist/runtime/components/AVoiceTile.vue +4 -1
  22. package/dist/runtime/components/aware/AArea.vue +1 -1
  23. package/dist/runtime/components/aware/AAvatar.vue +85 -16
  24. package/dist/runtime/components/aware/AButton.vue +5 -1
  25. package/dist/runtime/components/aware/ACursorLabel.vue +5 -1
  26. package/dist/runtime/components/aware/ADocBadge.vue +4 -1
  27. package/dist/runtime/components/aware/AFacepile.vue +13 -3
  28. package/dist/runtime/components/aware/AInput.vue +5 -1
  29. package/dist/runtime/components/aware/ATextarea.vue +5 -1
  30. package/dist/runtime/components/aware/AUserList.vue +8 -2
  31. package/dist/runtime/components/renderers/ACalendarRenderer.d.vue.ts +12 -1
  32. package/dist/runtime/components/renderers/ACalendarRenderer.vue +388 -114
  33. package/dist/runtime/components/renderers/ACalendarRenderer.vue.d.ts +12 -1
  34. package/dist/runtime/components/renderers/ACallRenderer.d.vue.ts +13 -0
  35. package/dist/runtime/components/renderers/ACallRenderer.vue +169 -0
  36. package/dist/runtime/components/renderers/ACallRenderer.vue.d.ts +13 -0
  37. package/dist/runtime/components/renderers/AChecklistRenderer.d.vue.ts +19 -0
  38. package/dist/runtime/components/renderers/AChecklistRenderer.vue +581 -0
  39. package/dist/runtime/components/renderers/AChecklistRenderer.vue.d.ts +19 -0
  40. package/dist/runtime/components/renderers/ADashboardRenderer.d.vue.ts +19 -0
  41. package/dist/runtime/components/renderers/ADashboardRenderer.vue +1372 -0
  42. package/dist/runtime/components/renderers/ADashboardRenderer.vue.d.ts +19 -0
  43. package/dist/runtime/components/renderers/AGalleryCoverImage.d.vue.ts +8 -0
  44. package/dist/runtime/components/renderers/AGalleryCoverImage.vue +60 -0
  45. package/dist/runtime/components/renderers/AGalleryCoverImage.vue.d.ts +8 -0
  46. package/dist/runtime/components/renderers/AGalleryRenderer.d.vue.ts +12 -1
  47. package/dist/runtime/components/renderers/AGalleryRenderer.vue +221 -55
  48. package/dist/runtime/components/renderers/AGalleryRenderer.vue.d.ts +12 -1
  49. package/dist/runtime/components/renderers/AGraphRenderer.d.vue.ts +19 -0
  50. package/dist/runtime/components/renderers/AGraphRenderer.vue +1027 -0
  51. package/dist/runtime/components/renderers/AGraphRenderer.vue.d.ts +19 -0
  52. package/dist/runtime/components/renderers/AKanbanRenderer.d.vue.ts +13 -1
  53. package/dist/runtime/components/renderers/AKanbanRenderer.vue +474 -140
  54. package/dist/runtime/components/renderers/AKanbanRenderer.vue.d.ts +13 -1
  55. package/dist/runtime/components/renderers/AMapRenderer.d.vue.ts +19 -0
  56. package/dist/runtime/components/renderers/AMapRenderer.vue +1622 -0
  57. package/dist/runtime/components/renderers/AMapRenderer.vue.d.ts +19 -0
  58. package/dist/runtime/components/renderers/AOutlineRenderer.d.vue.ts +12 -1
  59. package/dist/runtime/components/renderers/AOutlineRenderer.vue +294 -134
  60. package/dist/runtime/components/renderers/AOutlineRenderer.vue.d.ts +12 -1
  61. package/dist/runtime/components/renderers/ATableRenderer.d.vue.ts +12 -1
  62. package/dist/runtime/components/renderers/ATableRenderer.vue +437 -145
  63. package/dist/runtime/components/renderers/ATableRenderer.vue.d.ts +12 -1
  64. package/dist/runtime/components/renderers/ATimelineRenderer.d.vue.ts +19 -0
  65. package/dist/runtime/components/renderers/ATimelineRenderer.vue +446 -0
  66. package/dist/runtime/components/renderers/ATimelineRenderer.vue.d.ts +19 -0
  67. package/dist/runtime/composables/useAwareness.js +5 -0
  68. package/dist/runtime/composables/useBroadcastSync.d.ts +18 -0
  69. package/dist/runtime/composables/useBroadcastSync.js +26 -0
  70. package/dist/runtime/composables/useChat.js +4 -2
  71. package/dist/runtime/composables/useChatUsers.js +2 -1
  72. package/dist/runtime/composables/useCommandPalette.js +62 -3
  73. package/dist/runtime/composables/useConnectionStatus.js +7 -0
  74. package/dist/runtime/composables/useDevicePairing.d.ts +58 -0
  75. package/dist/runtime/composables/useDevicePairing.js +108 -0
  76. package/dist/runtime/composables/useDocExport.d.ts +5 -0
  77. package/dist/runtime/composables/useDocExport.js +2 -2
  78. package/dist/runtime/composables/useDocImport.js +4 -3
  79. package/dist/runtime/composables/useDocSeo.d.ts +20 -0
  80. package/dist/runtime/composables/useDocSeo.js +44 -0
  81. package/dist/runtime/composables/useDocSlugs.d.ts +7 -0
  82. package/dist/runtime/composables/useDocSlugs.js +20 -0
  83. package/dist/runtime/composables/useDocTree.d.ts +34 -0
  84. package/dist/runtime/composables/useDocTree.js +35 -0
  85. package/dist/runtime/composables/useEditorDragHandle.js +2 -1
  86. package/dist/runtime/composables/useEditorMentions.js +4 -2
  87. package/dist/runtime/composables/useEditorSuggestions.d.ts +1 -0
  88. package/dist/runtime/composables/useEditorSuggestions.js +9 -2
  89. package/dist/runtime/composables/useEditorToolbar.js +2 -1
  90. package/dist/runtime/composables/useFileIndex.js +2 -1
  91. package/dist/runtime/composables/useFileTransfer.d.ts +112 -0
  92. package/dist/runtime/composables/useFileTransfer.js +171 -0
  93. package/dist/runtime/composables/useFollowUser.js +2 -1
  94. package/dist/runtime/composables/useInvites.d.ts +56 -0
  95. package/dist/runtime/composables/useInvites.js +77 -0
  96. package/dist/runtime/composables/useNodePanel.d.ts +14 -0
  97. package/dist/runtime/composables/useNodePanel.js +52 -0
  98. package/dist/runtime/composables/useNotifications.js +4 -2
  99. package/dist/runtime/composables/usePasskeyAccounts.js +4 -2
  100. package/dist/runtime/composables/useSearchIndex.d.ts +1 -0
  101. package/dist/runtime/composables/useSearchIndex.js +13 -5
  102. package/dist/runtime/composables/useServerInfo.d.ts +31 -0
  103. package/dist/runtime/composables/useServerInfo.js +80 -0
  104. package/dist/runtime/composables/useSlugRoute.d.ts +6 -0
  105. package/dist/runtime/composables/useSlugRoute.js +19 -0
  106. package/dist/runtime/composables/useSpaces.d.ts +37 -0
  107. package/dist/runtime/composables/useSpaces.js +83 -0
  108. package/dist/runtime/composables/useTouchDrag.d.ts +34 -0
  109. package/dist/runtime/composables/useTouchDrag.js +191 -0
  110. package/dist/runtime/composables/useTrash.d.ts +1 -1
  111. package/dist/runtime/composables/useTrash.js +6 -3
  112. package/dist/runtime/composables/useWebRTC.d.ts +50 -0
  113. package/dist/runtime/composables/useWebRTC.js +177 -0
  114. package/dist/runtime/extensions/meta-field.d.ts +4 -1
  115. package/dist/runtime/extensions/steps.js +1 -1
  116. package/dist/runtime/extensions/views/AccordionItemView.vue +13 -3
  117. package/dist/runtime/extensions/views/AccordionView.vue +4 -1
  118. package/dist/runtime/extensions/views/BadgeView.vue +11 -2
  119. package/dist/runtime/extensions/views/CalloutView.vue +4 -1
  120. package/dist/runtime/extensions/views/CardGroupView.vue +4 -1
  121. package/dist/runtime/extensions/views/CardView.vue +17 -3
  122. package/dist/runtime/extensions/views/CodeGroupView.vue +4 -1
  123. package/dist/runtime/extensions/views/CollapsibleView.vue +8 -2
  124. package/dist/runtime/extensions/views/FileNodeView.vue +32 -8
  125. package/dist/runtime/extensions/views/KbdView.vue +8 -2
  126. package/dist/runtime/extensions/views/MetaFieldView.vue +208 -46
  127. package/dist/runtime/extensions/views/ProseIconView.vue +8 -2
  128. package/dist/runtime/extensions/views/TabsView.vue +17 -4
  129. package/dist/runtime/locale.d.ts +71 -0
  130. package/dist/runtime/locale.js +71 -0
  131. package/dist/runtime/plugin-abracadabra.client.js +29 -3
  132. package/dist/runtime/plugin-abracadabra.server.js +2 -0
  133. package/dist/runtime/server/api/_abracadabra/render/[docId].get.d.ts +1 -1
  134. package/dist/runtime/server/api/_abracadabra/render/[docId].get.js +29 -4
  135. package/dist/runtime/server/api/_abracadabra/resolve/[...slug].get.d.ts +2 -0
  136. package/dist/runtime/server/api/_abracadabra/resolve/[...slug].get.js +43 -0
  137. package/dist/runtime/server/api/_abracadabra/slugs.get.d.ts +2 -0
  138. package/dist/runtime/server/api/_abracadabra/slugs.get.js +7 -0
  139. package/dist/runtime/server/plugins/abracadabra-service.js +10 -5
  140. package/dist/runtime/server/runners/doc-tree-cache.js +4 -0
  141. package/dist/runtime/server/utils/slugMap.d.ts +32 -0
  142. package/dist/runtime/server/utils/slugMap.js +58 -0
  143. package/dist/runtime/types.d.ts +1 -0
  144. package/dist/runtime/utils/docTypes.d.ts +29 -1
  145. package/dist/runtime/utils/docTypes.js +129 -1
  146. package/dist/runtime/utils/markdownToYjs.js +2 -2
  147. package/dist/runtime/utils/sdkRef.d.ts +2 -0
  148. package/dist/runtime/utils/sdkRef.js +7 -0
  149. package/dist/runtime/utils/slugify.d.ts +40 -0
  150. package/dist/runtime/utils/slugify.js +36 -0
  151. package/dist/types.d.mts +6 -0
  152. package/package.json +32 -19
@@ -0,0 +1,58 @@
1
+ /**
2
+ * useDevicePairing
3
+ *
4
+ * Wraps ManualSignaling from @abraca/dabra for serverless device-to-device
5
+ * WebRTC pairing via blob exchange (QR code, copy-paste, NFC, etc.).
6
+ *
7
+ * Standalone composable — not auto-managed by the plugin. Users create and
8
+ * cancel pairings explicitly.
9
+ *
10
+ * Usage:
11
+ * const { createOffer, acceptOffer, acceptAnswer, cancelPairing, offerBlob, pairingStatus } = useDevicePairing()
12
+ *
13
+ * // Initiator flow:
14
+ * const blob = await createOffer() // show QR / copy
15
+ * await acceptAnswer(answerBlob) // complete connection
16
+ *
17
+ * // Responder flow:
18
+ * const answer = await acceptOffer(offerBlob) // show QR / copy
19
+ */
20
+ /**
21
+ * Initiator: create an offer blob to share with the other device.
22
+ * Returns the offer blob (also available as offerBlob ref).
23
+ */
24
+ declare function createOffer(): Promise<any>;
25
+ /**
26
+ * Responder: accept an offer blob from the initiator.
27
+ * Returns the answer blob to share back with the initiator.
28
+ */
29
+ declare function acceptOffer(blob: any): Promise<any>;
30
+ /**
31
+ * Initiator: accept the answer blob from the responder to complete the connection.
32
+ */
33
+ declare function acceptAnswer(blob: any): Promise<void>;
34
+ /**
35
+ * Cancel the current pairing attempt and clean up resources.
36
+ */
37
+ declare function cancelPairing(): void;
38
+ export declare function useDevicePairing(): {
39
+ /** Current role in the pairing flow. */
40
+ pairingRole: import("vue").Ref<"idle" | "initiator" | "responder", "idle" | "initiator" | "responder">;
41
+ /** Current status of the pairing attempt. */
42
+ pairingStatus: import("vue").Ref<"idle" | "connected" | "error" | "waiting-for-answer" | "waiting-for-offer", "idle" | "connected" | "error" | "waiting-for-answer" | "waiting-for-offer">;
43
+ /** The offer blob to display/share (initiator side). */
44
+ offerBlob: import("vue").ShallowRef<any, any>;
45
+ /** The answer blob to display/share (responder side). */
46
+ answerBlob: import("vue").ShallowRef<any, any>;
47
+ /** Error message if pairing failed. */
48
+ pairingError: import("vue").Ref<string | null, string | null>;
49
+ /** Initiator: create an offer blob. */
50
+ createOffer: typeof createOffer;
51
+ /** Responder: accept an offer blob and return the answer blob. */
52
+ acceptOffer: typeof acceptOffer;
53
+ /** Initiator: accept the answer blob to complete the connection. */
54
+ acceptAnswer: typeof acceptAnswer;
55
+ /** Cancel the current pairing attempt. */
56
+ cancelPairing: typeof cancelPairing;
57
+ };
58
+ export {};
@@ -0,0 +1,108 @@
1
+ import { ref, shallowRef } from "vue";
2
+ import { getSdkModule } from "../utils/sdkRef.js";
3
+ const pairingRole = ref("idle");
4
+ const pairingStatus = ref("idle");
5
+ const offerBlob = shallowRef(null);
6
+ const answerBlob = shallowRef(null);
7
+ const pairingError = ref(null);
8
+ let _manualSignaling = null;
9
+ async function _resolveIceServers() {
10
+ const config = useRuntimeConfig();
11
+ const webrtcConfig = config.public.abracadabra?.webrtc ?? {};
12
+ if (webrtcConfig.iceServers) return webrtcConfig.iceServers;
13
+ try {
14
+ const { client } = useAbracadabra();
15
+ if (client.value?.getIceServers) {
16
+ return await client.value.getIceServers();
17
+ }
18
+ } catch (e) {
19
+ if (import.meta.dev) console.debug("[abracadabra] pairing: failed to fetch ICE servers from server:", e);
20
+ }
21
+ return [{ urls: "stun:stun.l.google.com:19302" }];
22
+ }
23
+ async function createOffer() {
24
+ cancelPairing();
25
+ const sdk = getSdkModule();
26
+ if (!sdk?.ManualSignaling) throw new Error("SDK not loaded \u2014 cannot create pairing");
27
+ const { ManualSignaling } = sdk;
28
+ const iceServers = await _resolveIceServers();
29
+ _manualSignaling = new ManualSignaling(iceServers);
30
+ pairingRole.value = "initiator";
31
+ pairingStatus.value = "waiting-for-answer";
32
+ pairingError.value = null;
33
+ try {
34
+ const blob = await _manualSignaling.createOfferBlob();
35
+ offerBlob.value = blob;
36
+ return blob;
37
+ } catch (e) {
38
+ pairingError.value = e.message;
39
+ pairingStatus.value = "error";
40
+ throw e;
41
+ }
42
+ }
43
+ async function acceptOffer(blob) {
44
+ cancelPairing();
45
+ const sdk = getSdkModule();
46
+ if (!sdk?.ManualSignaling) throw new Error("SDK not loaded \u2014 cannot create pairing");
47
+ const { ManualSignaling } = sdk;
48
+ const iceServers = await _resolveIceServers();
49
+ _manualSignaling = new ManualSignaling(iceServers);
50
+ pairingRole.value = "responder";
51
+ pairingError.value = null;
52
+ try {
53
+ const answer = await _manualSignaling.acceptOffer(blob);
54
+ answerBlob.value = answer;
55
+ pairingStatus.value = "connected";
56
+ return answer;
57
+ } catch (e) {
58
+ pairingError.value = e.message;
59
+ pairingStatus.value = "error";
60
+ throw e;
61
+ }
62
+ }
63
+ async function acceptAnswer(blob) {
64
+ if (!_manualSignaling) {
65
+ throw new Error("No active pairing \u2014 call createOffer() first");
66
+ }
67
+ try {
68
+ await _manualSignaling.acceptAnswer(blob);
69
+ pairingStatus.value = "connected";
70
+ } catch (e) {
71
+ pairingError.value = e.message;
72
+ pairingStatus.value = "error";
73
+ throw e;
74
+ }
75
+ }
76
+ function cancelPairing() {
77
+ if (_manualSignaling) {
78
+ _manualSignaling.destroy?.();
79
+ _manualSignaling = null;
80
+ }
81
+ pairingRole.value = "idle";
82
+ pairingStatus.value = "idle";
83
+ offerBlob.value = null;
84
+ answerBlob.value = null;
85
+ pairingError.value = null;
86
+ }
87
+ export function useDevicePairing() {
88
+ return {
89
+ /** Current role in the pairing flow. */
90
+ pairingRole,
91
+ /** Current status of the pairing attempt. */
92
+ pairingStatus,
93
+ /** The offer blob to display/share (initiator side). */
94
+ offerBlob,
95
+ /** The answer blob to display/share (responder side). */
96
+ answerBlob,
97
+ /** Error message if pairing failed. */
98
+ pairingError,
99
+ /** Initiator: create an offer blob. */
100
+ createOffer,
101
+ /** Responder: accept an offer blob and return the answer blob. */
102
+ acceptOffer,
103
+ /** Initiator: accept the answer blob to complete the connection. */
104
+ acceptAnswer,
105
+ /** Cancel the current pairing attempt. */
106
+ cancelPairing
107
+ };
108
+ }
@@ -1,3 +1,8 @@
1
+ export declare function labelToFilename(label: string): string;
2
+ export declare function replaceFileMarkers(content: string, docZipPath: string, filePathMap: Map<string, {
3
+ zipPath: string;
4
+ mimeType: string;
5
+ }>, format: 'md' | 'html'): string;
1
6
  export declare function useDocExport(): {
2
7
  exportDoc: (id: string, format: "markdown" | "html" | "zip") => Promise<void>;
3
8
  exportDocs: (ids: string[], format?: "md" | "html") => Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import * as Y from "yjs";
2
2
  import { yjsToMarkdown, yjsToHtml } from "../utils/yjsConvert.js";
3
- function labelToFilename(label) {
3
+ export function labelToFilename(label) {
4
4
  return label.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "untitled";
5
5
  }
6
6
  function triggerDownload(blob, filename) {
@@ -27,7 +27,7 @@ function collectFileBlocks(el, sourceDocId) {
27
27
  }
28
28
  return files;
29
29
  }
30
- function replaceFileMarkers(content, docZipPath, filePathMap, format) {
30
+ export function replaceFileMarkers(content, docZipPath, filePathMap, format) {
31
31
  return content.replace(/<!--fileblock:([^:]+):(.+?)-->/g, (_, uploadId, filename) => {
32
32
  const entry = filePathMap.get(uploadId);
33
33
  if (!entry) return `<!-- file: ${filename} -->`;
@@ -6,6 +6,7 @@ export function useDocImport() {
6
6
  const toast = useToast();
7
7
  const { provider, client, isReady } = useAbracadabra();
8
8
  const { enqueue } = useOfflineUploadQueue();
9
+ const { getDocUrl } = useDocSlugs();
9
10
  const externalDragActive = ref(false);
10
11
  let externalDragCounter = 0;
11
12
  function getTreeMap() {
@@ -46,7 +47,7 @@ export function useDocImport() {
46
47
  const id = await importEntry(entry, parentId);
47
48
  if (firstId === null && id !== null) firstId = id;
48
49
  }
49
- if (firstId) navigateTo(`${useRuntimeConfig().public.abracadabra?.docBasePath ?? "/doc"}/${firstId}`);
50
+ if (firstId) navigateTo(getDocUrl(firstId));
50
51
  }
51
52
  function importFiles(parentId = null) {
52
53
  const input = document.createElement("input");
@@ -59,7 +60,7 @@ export function useDocImport() {
59
60
  const id = await importFile(file, parentId);
60
61
  if (firstId === null) firstId = id;
61
62
  }
62
- if (firstId) navigateTo(`${useRuntimeConfig().public.abracadabra?.docBasePath ?? "/doc"}/${firstId}`);
63
+ if (firstId) navigateTo(getDocUrl(firstId));
63
64
  };
64
65
  input.click();
65
66
  }
@@ -96,7 +97,7 @@ export function useDocImport() {
96
97
  const id = await importFile(file, currentParent);
97
98
  if (firstId === null) firstId = id;
98
99
  }
99
- if (firstId) navigateTo(`${useRuntimeConfig().public.abracadabra?.docBasePath ?? "/doc"}/${firstId}`);
100
+ if (firstId) navigateTo(getDocUrl(firstId));
100
101
  };
101
102
  input.click();
102
103
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * useDocSeo — reactive SEO meta for an Abracadabra document.
3
+ *
4
+ * Sets useHead() title and useSeoMeta() og/twitter tags reactively
5
+ * from the doc-tree metadata and optional SSR render data.
6
+ */
7
+ import { type MaybeRef } from 'vue';
8
+ export interface DocSeoOptions {
9
+ /** Site name for og:site_name */
10
+ siteName?: string;
11
+ /** Fallback description when none can be extracted */
12
+ fallbackDescription?: string;
13
+ /** Title template, e.g. '%s - My Site'. Uses Nuxt's titleTemplate. */
14
+ titleTemplate?: string | ((title?: string) => string);
15
+ }
16
+ export declare function useDocSeo(docId: MaybeRef<string | null>, options?: DocSeoOptions): {
17
+ title: import("vue").ComputedRef<any>;
18
+ description: import("vue").ComputedRef<any>;
19
+ ogImage: import("vue").ComputedRef<any>;
20
+ };
@@ -0,0 +1,44 @@
1
+ import { computed, unref } from "vue";
2
+ function extractFirstParagraph(html) {
3
+ if (!html) return void 0;
4
+ const pMatch = html.match(/<p[^>]*>(.*?)<\/p>/is);
5
+ const raw = pMatch?.[1] ?? html;
6
+ const text = raw.replace(/<[^>]+>/g, "").replace(/&[a-z]+;/gi, " ").trim();
7
+ if (!text) return void 0;
8
+ return text.length > 160 ? text.slice(0, 157) + "..." : text;
9
+ }
10
+ function extractFirstImage(html) {
11
+ if (!html) return void 0;
12
+ const match = html.match(/<img[^>]+src=["']([^"']+)["']/i);
13
+ return match?.[1] ?? void 0;
14
+ }
15
+ export function useDocSeo(docId, options) {
16
+ const abra = useAbracadabra();
17
+ const treeMap = useSyncedMap(abra.doc, "doc-tree");
18
+ const resolvedId = computed(() => unref(docId) ?? "");
19
+ const entry = computed(() => resolvedId.value ? treeMap.data[resolvedId.value] : void 0);
20
+ const { data: renderData } = useFetch(
21
+ () => resolvedId.value ? `/api/_abracadabra/render/${resolvedId.value}` : null,
22
+ { key: `seo-${resolvedId.value}`, server: true, lazy: true }
23
+ );
24
+ const title = computed(() => entry.value?.label ?? "Untitled");
25
+ const description = computed(
26
+ () => entry.value?.meta?.note ?? entry.value?.meta?.description ?? renderData.value?.description ?? extractFirstParagraph(renderData.value?.html) ?? options?.fallbackDescription ?? ""
27
+ );
28
+ const ogImage = computed(
29
+ () => entry.value?.meta?.coverImage ?? renderData.value?.ogImage ?? extractFirstImage(renderData.value?.html) ?? void 0
30
+ );
31
+ useHead({
32
+ title,
33
+ ...options?.titleTemplate ? { titleTemplate: options.titleTemplate } : {}
34
+ });
35
+ useSeoMeta({
36
+ ogTitle: title,
37
+ ogDescription: description,
38
+ ...options?.siteName ? { ogSiteName: options.siteName } : {},
39
+ ogType: "article",
40
+ twitterCard: "summary",
41
+ ...ogImage.value ? { ogImage } : {}
42
+ });
43
+ return { title, description, ogImage };
44
+ }
@@ -0,0 +1,7 @@
1
+ import { type SlugMap } from '../utils/slugify.js';
2
+ export declare function useDocSlugs(): {
3
+ slugMap: import("vue").ComputedRef<SlugMap>;
4
+ getSlug: (docId: string) => string;
5
+ getDocId: (slug: string) => string | null;
6
+ getDocUrl: (docId: string) => string;
7
+ };
@@ -0,0 +1,20 @@
1
+ import { computed } from "vue";
2
+ import { buildSlugMap } from "../utils/slugify.js";
3
+ export function useDocSlugs() {
4
+ const abra = useAbracadabra();
5
+ const config = useRuntimeConfig();
6
+ const docBasePath = config.public?.abracadabra?.docBasePath ?? "/doc";
7
+ const treeMap = useSyncedMap(abra.doc, "doc-tree");
8
+ const entries = computed(
9
+ () => Object.entries(treeMap.data).map(([id, entry]) => ({ id, ...entry }))
10
+ );
11
+ const slugMap = computed(() => buildSlugMap(entries.value));
12
+ const getSlug = (docId) => slugMap.value.idToSlug[docId] ?? docId;
13
+ const getDocId = (slug) => slugMap.value.slugToId[slug] ?? null;
14
+ const getDocUrl = (docId) => {
15
+ const slug = slugMap.value.idToSlug[docId];
16
+ const path = slug ?? docId;
17
+ return docBasePath ? `${docBasePath}/${path}` : `/${path}`;
18
+ };
19
+ return { slugMap, getSlug, getDocId, getDocUrl };
20
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * useDocTree — reactive access to the Abracadabra document tree.
3
+ *
4
+ * Wraps useSyncedMap on the root Y.Doc's 'doc-tree' map and provides
5
+ * computed navigation helpers: topLevelDocs, firstDocId, navigationItems.
6
+ * Navigation items use human-readable slug URLs when available.
7
+ */
8
+ import { type ComputedRef } from 'vue';
9
+ import type { TreeEntry } from '../types.js';
10
+ export interface DocTreeReturn {
11
+ /** All tree entries with `id` populated from map keys */
12
+ entries: ComputedRef<(TreeEntry & {
13
+ trashed?: boolean;
14
+ })[]>;
15
+ /** Top-level docs (no parent, not trashed), sorted by order */
16
+ topLevelDocs: ComputedRef<(TreeEntry & {
17
+ trashed?: boolean;
18
+ })[]>;
19
+ /** ID of the first top-level document, or null */
20
+ firstDocId: ComputedRef<string | null>;
21
+ /** Look up a single tree entry by ID */
22
+ getEntry: (id: string) => TreeEntry | undefined;
23
+ /** Navigation items with resolved doc-type icons and slug URLs */
24
+ navigationItems: ComputedRef<{
25
+ id: string;
26
+ label: string;
27
+ to: string;
28
+ icon: string;
29
+ type: string;
30
+ }[]>;
31
+ /** Raw useSyncedMap return for advanced usage */
32
+ raw: ReturnType<typeof useSyncedMap>;
33
+ }
34
+ export declare function useDocTree(): DocTreeReturn;
@@ -0,0 +1,35 @@
1
+ import { computed } from "vue";
2
+ import { resolveDocType } from "../utils/docTypes.js";
3
+ import { buildSlugMap } from "../utils/slugify.js";
4
+ export function useDocTree() {
5
+ const abra = useAbracadabra();
6
+ const config = useRuntimeConfig();
7
+ const docBasePath = config.public?.abracadabra?.docBasePath ?? "/doc";
8
+ const treeMap = useSyncedMap(abra.doc, "doc-tree");
9
+ const entries = computed(
10
+ () => Object.entries(treeMap.data).map(([id, entry]) => ({ id, ...entry }))
11
+ );
12
+ const topLevelDocs = computed(
13
+ () => entries.value.filter((e) => !e.parentId && !e.trashed).sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
14
+ );
15
+ const firstDocId = computed(() => topLevelDocs.value[0]?.id ?? null);
16
+ const getEntry = (id) => {
17
+ const raw = treeMap.data[id];
18
+ return raw ? { id, ...raw } : void 0;
19
+ };
20
+ const slugMap = computed(() => buildSlugMap(entries.value));
21
+ const navigationItems = computed(
22
+ () => topLevelDocs.value.map((e) => {
23
+ const slug = slugMap.value.idToSlug[e.id];
24
+ const path = slug ?? e.id;
25
+ return {
26
+ id: e.id,
27
+ label: e.label || "Untitled",
28
+ to: docBasePath ? `${docBasePath}/${path}` : `/${path}`,
29
+ icon: resolveDocType(e.type).icon,
30
+ type: e.type ?? "doc"
31
+ };
32
+ })
33
+ );
34
+ return { entries, topLevelDocs, firstDocId, getEntry, navigationItems, raw: treeMap };
35
+ }
@@ -55,7 +55,8 @@ export function useEditorDragHandle(pageConfig) {
55
55
  a.href = url;
56
56
  a.download = filename;
57
57
  a.click();
58
- } catch {
58
+ } catch (e) {
59
+ if (import.meta.dev) console.warn("[abracadabra] drag-handle: failed to download file:", e);
59
60
  }
60
61
  }
61
62
  }
@@ -19,7 +19,8 @@ export function useEditorMentions(options = {}) {
19
19
  });
20
20
  }
21
21
  }
22
- } catch {
22
+ } catch (e) {
23
+ if (import.meta.dev) console.warn("[abracadabra] mentions: failed to read awareness states:", e);
23
24
  }
24
25
  }
25
26
  try {
@@ -29,7 +30,8 @@ export function useEditorMentions(options = {}) {
29
30
  result.push(...p.staticItems);
30
31
  }
31
32
  }
32
- } catch {
33
+ } catch (e) {
34
+ if (import.meta.dev) console.warn("[abracadabra] mentions: failed to load plugin mention providers:", e);
33
35
  }
34
36
  if (options.extraItems?.length) {
35
37
  result.push(...options.extraItems);
@@ -15,4 +15,5 @@ export interface UseEditorSuggestionsOptions {
15
15
  }
16
16
  export declare function useEditorSuggestions(options?: UseEditorSuggestionsOptions): {
17
17
  items: any;
18
+ propertiesOnlyItems: any;
18
19
  };
@@ -47,9 +47,16 @@ export function useEditorSuggestions(options = {}) {
47
47
  for (const group of pluginGroups) {
48
48
  if (group.items?.length) base.push(group.items);
49
49
  }
50
- } catch {
50
+ } catch (e) {
51
+ if (import.meta.dev) console.warn("[abracadabra] suggestions: failed to load plugin suggestions:", e);
51
52
  }
52
53
  return base;
53
54
  });
54
- return { items };
55
+ const propertiesOnlyItems = computed(() => {
56
+ const group = items.value.find(
57
+ (g) => g[0]?.type === "label" && g[0]?.label === "Properties"
58
+ );
59
+ return group ? [group] : [];
60
+ });
61
+ return { items, propertiesOnlyItems };
55
62
  }
@@ -49,7 +49,8 @@ export function useEditorToolbar(options = {}) {
49
49
  base.push(group.items);
50
50
  }
51
51
  }
52
- } catch {
52
+ } catch (e) {
53
+ if (import.meta.dev) console.warn("[abracadabra] toolbar: failed to load plugin toolbar items:", e);
53
54
  }
54
55
  if (options.extraItems?.length) {
55
56
  base.push(...options.extraItems);
@@ -43,7 +43,8 @@ function _startWatcher() {
43
43
  if (val.docId === docId) _fileEntries.delete(key);
44
44
  }
45
45
  for (const f of files) _fileEntries.set(f.uploadId, f);
46
- } catch {
46
+ } catch (e) {
47
+ if (import.meta.dev) console.warn(`[abracadabra] file-index: failed to index doc ${docId}:`, e);
47
48
  }
48
49
  }
49
50
  }, { deep: false });
@@ -0,0 +1,112 @@
1
+ /**
2
+ * useFileTransfer
3
+ *
4
+ * Tracks inbound and outbound P2P file transfers over WebRTC data channels.
5
+ * Events are wired from AbracadabraWebRTC when useWebRTC connects.
6
+ *
7
+ * Usage:
8
+ * const { inboundTransfers, completedFiles, sendFile, downloadFile } = useFileTransfer()
9
+ */
10
+ export interface FileTransferMeta {
11
+ transferId: string;
12
+ peerId: string;
13
+ filename: string;
14
+ mimeType: string;
15
+ totalSize: number;
16
+ progress: number;
17
+ status: 'pending' | 'receiving' | 'sending' | 'complete' | 'error' | 'cancelled';
18
+ }
19
+ export interface CompletedFile {
20
+ transferId: string;
21
+ peerId: string;
22
+ filename: string;
23
+ mimeType: string;
24
+ size: number;
25
+ blob: Blob;
26
+ objectUrl: string;
27
+ receivedAt: number;
28
+ }
29
+ export declare function _wireFileTransferEvents(rtc: any): void;
30
+ export declare function _destroyFileTransfer(): void;
31
+ declare function sendFile(peerId: string, file: File | Blob, filename: string): Promise<void>;
32
+ declare function downloadFile(transferId: string): void;
33
+ declare function clearCompleted(): void;
34
+ export declare function useFileTransfer(): {
35
+ /** Active inbound file transfers. */
36
+ inboundTransfers: import("vue").ShallowRef<Map<string, FileTransferMeta>, Map<string, FileTransferMeta>>;
37
+ /** Active outbound file transfers. */
38
+ outboundTransfers: import("vue").ShallowRef<Map<string, FileTransferMeta>, Map<string, FileTransferMeta>>;
39
+ /** Completed received files (with object URLs for download). */
40
+ completedFiles: import("vue").Ref<{
41
+ transferId: string;
42
+ peerId: string;
43
+ filename: string;
44
+ mimeType: string;
45
+ size: number;
46
+ blob: {
47
+ readonly size: number;
48
+ readonly type: string;
49
+ arrayBuffer: {
50
+ (): Promise<ArrayBuffer>;
51
+ (): Promise<ArrayBuffer>;
52
+ };
53
+ bytes: {
54
+ (): Promise<Uint8Array<ArrayBuffer>>;
55
+ (): Promise<Uint8Array<ArrayBuffer>>;
56
+ };
57
+ slice: {
58
+ (start?: number, end?: number, contentType?: string): Blob;
59
+ (start?: number, end?: number, contentType?: string): Blob;
60
+ };
61
+ stream: {
62
+ (): ReadableStream<Uint8Array<ArrayBuffer>>;
63
+ (): ReadableStream<Uint8Array<ArrayBuffer>>;
64
+ };
65
+ text: {
66
+ (): Promise<string>;
67
+ (): Promise<string>;
68
+ };
69
+ };
70
+ objectUrl: string;
71
+ receivedAt: number;
72
+ }[], CompletedFile[] | {
73
+ transferId: string;
74
+ peerId: string;
75
+ filename: string;
76
+ mimeType: string;
77
+ size: number;
78
+ blob: {
79
+ readonly size: number;
80
+ readonly type: string;
81
+ arrayBuffer: {
82
+ (): Promise<ArrayBuffer>;
83
+ (): Promise<ArrayBuffer>;
84
+ };
85
+ bytes: {
86
+ (): Promise<Uint8Array<ArrayBuffer>>;
87
+ (): Promise<Uint8Array<ArrayBuffer>>;
88
+ };
89
+ slice: {
90
+ (start?: number, end?: number, contentType?: string): Blob;
91
+ (start?: number, end?: number, contentType?: string): Blob;
92
+ };
93
+ stream: {
94
+ (): ReadableStream<Uint8Array<ArrayBuffer>>;
95
+ (): ReadableStream<Uint8Array<ArrayBuffer>>;
96
+ };
97
+ text: {
98
+ (): Promise<string>;
99
+ (): Promise<string>;
100
+ };
101
+ };
102
+ objectUrl: string;
103
+ receivedAt: number;
104
+ }[]>;
105
+ /** Send a file to a specific peer. */
106
+ sendFile: typeof sendFile;
107
+ /** Trigger browser download for a completed file. */
108
+ downloadFile: typeof downloadFile;
109
+ /** Clear all completed transfers and revoke object URLs. */
110
+ clearCompleted: typeof clearCompleted;
111
+ };
112
+ export {};