@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.
- package/dist/module.d.mts +46 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +95 -2
- package/dist/runtime/assets/editor.css +1 -0
- package/dist/runtime/components/ACommandPalette.vue +4 -1
- package/dist/runtime/components/ADocRenderer.d.vue.ts +29 -0
- package/dist/runtime/components/ADocRenderer.vue +99 -0
- package/dist/runtime/components/ADocRenderer.vue.d.ts +29 -0
- package/dist/runtime/components/ADocTypeSelect.vue +4 -1
- package/dist/runtime/components/ADocumentTree.vue +78 -19
- package/dist/runtime/components/AEditor.d.vue.ts +9 -4
- package/dist/runtime/components/AEditor.vue +102 -7
- package/dist/runtime/components/AEditor.vue.d.ts +9 -4
- package/dist/runtime/components/AIconPicker.vue +8 -2
- package/dist/runtime/components/ANodePanel.vue +100 -61
- package/dist/runtime/components/ANotifications.vue +35 -8
- package/dist/runtime/components/APermissionGuard.vue +3 -1
- package/dist/runtime/components/APresence.vue +14 -3
- package/dist/runtime/components/AProvider.vue +7 -1
- package/dist/runtime/components/AVoiceBar.vue +57 -15
- package/dist/runtime/components/AVoiceTile.vue +4 -1
- package/dist/runtime/components/aware/AArea.vue +1 -1
- package/dist/runtime/components/aware/AAvatar.vue +85 -16
- package/dist/runtime/components/aware/AButton.vue +5 -1
- package/dist/runtime/components/aware/ACursorLabel.vue +5 -1
- package/dist/runtime/components/aware/ADocBadge.vue +4 -1
- package/dist/runtime/components/aware/AFacepile.vue +13 -3
- package/dist/runtime/components/aware/AInput.vue +5 -1
- package/dist/runtime/components/aware/ATextarea.vue +5 -1
- package/dist/runtime/components/aware/AUserList.vue +8 -2
- package/dist/runtime/components/renderers/ACalendarRenderer.d.vue.ts +12 -1
- package/dist/runtime/components/renderers/ACalendarRenderer.vue +388 -114
- package/dist/runtime/components/renderers/ACalendarRenderer.vue.d.ts +12 -1
- package/dist/runtime/components/renderers/ACallRenderer.d.vue.ts +13 -0
- package/dist/runtime/components/renderers/ACallRenderer.vue +169 -0
- package/dist/runtime/components/renderers/ACallRenderer.vue.d.ts +13 -0
- package/dist/runtime/components/renderers/AChecklistRenderer.d.vue.ts +19 -0
- package/dist/runtime/components/renderers/AChecklistRenderer.vue +581 -0
- package/dist/runtime/components/renderers/AChecklistRenderer.vue.d.ts +19 -0
- package/dist/runtime/components/renderers/ADashboardRenderer.d.vue.ts +19 -0
- package/dist/runtime/components/renderers/ADashboardRenderer.vue +1372 -0
- package/dist/runtime/components/renderers/ADashboardRenderer.vue.d.ts +19 -0
- package/dist/runtime/components/renderers/AGalleryCoverImage.d.vue.ts +8 -0
- package/dist/runtime/components/renderers/AGalleryCoverImage.vue +60 -0
- package/dist/runtime/components/renderers/AGalleryCoverImage.vue.d.ts +8 -0
- package/dist/runtime/components/renderers/AGalleryRenderer.d.vue.ts +12 -1
- package/dist/runtime/components/renderers/AGalleryRenderer.vue +221 -55
- package/dist/runtime/components/renderers/AGalleryRenderer.vue.d.ts +12 -1
- package/dist/runtime/components/renderers/AGraphRenderer.d.vue.ts +19 -0
- package/dist/runtime/components/renderers/AGraphRenderer.vue +1027 -0
- package/dist/runtime/components/renderers/AGraphRenderer.vue.d.ts +19 -0
- package/dist/runtime/components/renderers/AKanbanRenderer.d.vue.ts +13 -1
- package/dist/runtime/components/renderers/AKanbanRenderer.vue +474 -140
- package/dist/runtime/components/renderers/AKanbanRenderer.vue.d.ts +13 -1
- package/dist/runtime/components/renderers/AMapRenderer.d.vue.ts +19 -0
- package/dist/runtime/components/renderers/AMapRenderer.vue +1622 -0
- package/dist/runtime/components/renderers/AMapRenderer.vue.d.ts +19 -0
- package/dist/runtime/components/renderers/AOutlineRenderer.d.vue.ts +12 -1
- package/dist/runtime/components/renderers/AOutlineRenderer.vue +294 -134
- package/dist/runtime/components/renderers/AOutlineRenderer.vue.d.ts +12 -1
- package/dist/runtime/components/renderers/ATableRenderer.d.vue.ts +12 -1
- package/dist/runtime/components/renderers/ATableRenderer.vue +437 -145
- package/dist/runtime/components/renderers/ATableRenderer.vue.d.ts +12 -1
- package/dist/runtime/components/renderers/ATimelineRenderer.d.vue.ts +19 -0
- package/dist/runtime/components/renderers/ATimelineRenderer.vue +446 -0
- package/dist/runtime/components/renderers/ATimelineRenderer.vue.d.ts +19 -0
- package/dist/runtime/composables/useAwareness.js +5 -0
- package/dist/runtime/composables/useBroadcastSync.d.ts +18 -0
- package/dist/runtime/composables/useBroadcastSync.js +26 -0
- package/dist/runtime/composables/useChat.js +4 -2
- package/dist/runtime/composables/useChatUsers.js +2 -1
- package/dist/runtime/composables/useCommandPalette.js +62 -3
- package/dist/runtime/composables/useConnectionStatus.js +7 -0
- package/dist/runtime/composables/useDevicePairing.d.ts +58 -0
- package/dist/runtime/composables/useDevicePairing.js +108 -0
- package/dist/runtime/composables/useDocExport.d.ts +5 -0
- package/dist/runtime/composables/useDocExport.js +2 -2
- package/dist/runtime/composables/useDocImport.js +4 -3
- package/dist/runtime/composables/useDocSeo.d.ts +20 -0
- package/dist/runtime/composables/useDocSeo.js +44 -0
- package/dist/runtime/composables/useDocSlugs.d.ts +7 -0
- package/dist/runtime/composables/useDocSlugs.js +20 -0
- package/dist/runtime/composables/useDocTree.d.ts +34 -0
- package/dist/runtime/composables/useDocTree.js +35 -0
- package/dist/runtime/composables/useEditorDragHandle.js +2 -1
- package/dist/runtime/composables/useEditorMentions.js +4 -2
- package/dist/runtime/composables/useEditorSuggestions.d.ts +1 -0
- package/dist/runtime/composables/useEditorSuggestions.js +9 -2
- package/dist/runtime/composables/useEditorToolbar.js +2 -1
- package/dist/runtime/composables/useFileIndex.js +2 -1
- package/dist/runtime/composables/useFileTransfer.d.ts +112 -0
- package/dist/runtime/composables/useFileTransfer.js +171 -0
- package/dist/runtime/composables/useFollowUser.js +2 -1
- package/dist/runtime/composables/useInvites.d.ts +56 -0
- package/dist/runtime/composables/useInvites.js +77 -0
- package/dist/runtime/composables/useNodePanel.d.ts +14 -0
- package/dist/runtime/composables/useNodePanel.js +52 -0
- package/dist/runtime/composables/useNotifications.js +4 -2
- package/dist/runtime/composables/usePasskeyAccounts.js +4 -2
- package/dist/runtime/composables/useSearchIndex.d.ts +1 -0
- package/dist/runtime/composables/useSearchIndex.js +13 -5
- package/dist/runtime/composables/useServerInfo.d.ts +31 -0
- package/dist/runtime/composables/useServerInfo.js +80 -0
- package/dist/runtime/composables/useSlugRoute.d.ts +6 -0
- package/dist/runtime/composables/useSlugRoute.js +19 -0
- package/dist/runtime/composables/useSpaces.d.ts +37 -0
- package/dist/runtime/composables/useSpaces.js +83 -0
- package/dist/runtime/composables/useTouchDrag.d.ts +34 -0
- package/dist/runtime/composables/useTouchDrag.js +191 -0
- package/dist/runtime/composables/useTrash.d.ts +1 -1
- package/dist/runtime/composables/useTrash.js +6 -3
- package/dist/runtime/composables/useWebRTC.d.ts +50 -0
- package/dist/runtime/composables/useWebRTC.js +177 -0
- package/dist/runtime/extensions/meta-field.d.ts +4 -1
- package/dist/runtime/extensions/steps.js +1 -1
- package/dist/runtime/extensions/views/AccordionItemView.vue +13 -3
- package/dist/runtime/extensions/views/AccordionView.vue +4 -1
- package/dist/runtime/extensions/views/BadgeView.vue +11 -2
- package/dist/runtime/extensions/views/CalloutView.vue +4 -1
- package/dist/runtime/extensions/views/CardGroupView.vue +4 -1
- package/dist/runtime/extensions/views/CardView.vue +17 -3
- package/dist/runtime/extensions/views/CodeGroupView.vue +4 -1
- package/dist/runtime/extensions/views/CollapsibleView.vue +8 -2
- package/dist/runtime/extensions/views/FileNodeView.vue +32 -8
- package/dist/runtime/extensions/views/KbdView.vue +8 -2
- package/dist/runtime/extensions/views/MetaFieldView.vue +208 -46
- package/dist/runtime/extensions/views/ProseIconView.vue +8 -2
- package/dist/runtime/extensions/views/TabsView.vue +17 -4
- package/dist/runtime/locale.d.ts +71 -0
- package/dist/runtime/locale.js +71 -0
- package/dist/runtime/plugin-abracadabra.client.js +29 -3
- package/dist/runtime/plugin-abracadabra.server.js +2 -0
- package/dist/runtime/server/api/_abracadabra/render/[docId].get.d.ts +1 -1
- package/dist/runtime/server/api/_abracadabra/render/[docId].get.js +29 -4
- package/dist/runtime/server/api/_abracadabra/resolve/[...slug].get.d.ts +2 -0
- package/dist/runtime/server/api/_abracadabra/resolve/[...slug].get.js +43 -0
- package/dist/runtime/server/api/_abracadabra/slugs.get.d.ts +2 -0
- package/dist/runtime/server/api/_abracadabra/slugs.get.js +7 -0
- package/dist/runtime/server/plugins/abracadabra-service.js +10 -5
- package/dist/runtime/server/runners/doc-tree-cache.js +4 -0
- package/dist/runtime/server/utils/slugMap.d.ts +32 -0
- package/dist/runtime/server/utils/slugMap.js +58 -0
- package/dist/runtime/types.d.ts +1 -0
- package/dist/runtime/utils/docTypes.d.ts +29 -1
- package/dist/runtime/utils/docTypes.js +129 -1
- package/dist/runtime/utils/markdownToYjs.js +2 -2
- package/dist/runtime/utils/sdkRef.d.ts +2 -0
- package/dist/runtime/utils/sdkRef.js +7 -0
- package/dist/runtime/utils/slugify.d.ts +40 -0
- package/dist/runtime/utils/slugify.js +36 -0
- package/dist/types.d.mts +6 -0
- package/package.json +32 -19
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { ref, shallowRef } from "vue";
|
|
2
|
+
const inboundTransfers = shallowRef(/* @__PURE__ */ new Map());
|
|
3
|
+
const outboundTransfers = shallowRef(/* @__PURE__ */ new Map());
|
|
4
|
+
const completedFiles = ref([]);
|
|
5
|
+
const _unbinds = [];
|
|
6
|
+
export function _wireFileTransferEvents(rtc) {
|
|
7
|
+
_cleanupFileTransfer();
|
|
8
|
+
const on = (event, handler) => {
|
|
9
|
+
rtc.on(event, handler);
|
|
10
|
+
_unbinds.push(() => rtc.off(event, handler));
|
|
11
|
+
};
|
|
12
|
+
on("fileReceiveStart", (data) => {
|
|
13
|
+
const next = new Map(inboundTransfers.value);
|
|
14
|
+
next.set(data.transferId, {
|
|
15
|
+
transferId: data.transferId,
|
|
16
|
+
peerId: data.peerId,
|
|
17
|
+
filename: data.filename ?? "unknown",
|
|
18
|
+
mimeType: data.mimeType ?? "application/octet-stream",
|
|
19
|
+
totalSize: data.totalSize ?? 0,
|
|
20
|
+
progress: 0,
|
|
21
|
+
status: "receiving"
|
|
22
|
+
});
|
|
23
|
+
inboundTransfers.value = next;
|
|
24
|
+
});
|
|
25
|
+
on("fileReceiveProgress", (data) => {
|
|
26
|
+
const existing = inboundTransfers.value.get(data.transferId);
|
|
27
|
+
if (existing) {
|
|
28
|
+
const next = new Map(inboundTransfers.value);
|
|
29
|
+
next.set(data.transferId, {
|
|
30
|
+
...existing,
|
|
31
|
+
progress: data.total > 0 ? data.received / data.total : 0
|
|
32
|
+
});
|
|
33
|
+
inboundTransfers.value = next;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
on("fileReceiveComplete", (data) => {
|
|
37
|
+
const next = new Map(inboundTransfers.value);
|
|
38
|
+
next.delete(data.transferId);
|
|
39
|
+
inboundTransfers.value = next;
|
|
40
|
+
const objectUrl = URL.createObjectURL(data.blob);
|
|
41
|
+
completedFiles.value = [
|
|
42
|
+
...completedFiles.value,
|
|
43
|
+
{
|
|
44
|
+
transferId: data.transferId,
|
|
45
|
+
peerId: data.peerId ?? "",
|
|
46
|
+
filename: data.filename ?? "unknown",
|
|
47
|
+
mimeType: data.mimeType ?? "application/octet-stream",
|
|
48
|
+
size: data.blob?.size ?? 0,
|
|
49
|
+
blob: data.blob,
|
|
50
|
+
objectUrl,
|
|
51
|
+
receivedAt: Date.now()
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
});
|
|
55
|
+
on("fileReceiveError", (data) => {
|
|
56
|
+
const existing = inboundTransfers.value.get(data.transferId);
|
|
57
|
+
if (existing) {
|
|
58
|
+
const next = new Map(inboundTransfers.value);
|
|
59
|
+
next.set(data.transferId, { ...existing, status: "error" });
|
|
60
|
+
inboundTransfers.value = next;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
on("fileReceiveCancelled", (data) => {
|
|
64
|
+
const next = new Map(inboundTransfers.value);
|
|
65
|
+
next.delete(data.transferId);
|
|
66
|
+
inboundTransfers.value = next;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function _cleanupFileTransfer() {
|
|
70
|
+
for (const unsub of _unbinds) unsub();
|
|
71
|
+
_unbinds.length = 0;
|
|
72
|
+
}
|
|
73
|
+
export function _destroyFileTransfer() {
|
|
74
|
+
_cleanupFileTransfer();
|
|
75
|
+
inboundTransfers.value = /* @__PURE__ */ new Map();
|
|
76
|
+
outboundTransfers.value = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const file of completedFiles.value) {
|
|
78
|
+
try {
|
|
79
|
+
URL.revokeObjectURL(file.objectUrl);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
completedFiles.value = [];
|
|
84
|
+
}
|
|
85
|
+
async function sendFile(peerId, file, filename) {
|
|
86
|
+
const { sendFile: rtcSendFile } = useWebRTC();
|
|
87
|
+
const transferId = crypto.randomUUID();
|
|
88
|
+
const next = new Map(outboundTransfers.value);
|
|
89
|
+
next.set(transferId, {
|
|
90
|
+
transferId,
|
|
91
|
+
peerId,
|
|
92
|
+
filename,
|
|
93
|
+
mimeType: file.type || "application/octet-stream",
|
|
94
|
+
totalSize: file.size,
|
|
95
|
+
progress: 0,
|
|
96
|
+
status: "sending"
|
|
97
|
+
});
|
|
98
|
+
outboundTransfers.value = next;
|
|
99
|
+
try {
|
|
100
|
+
const handle = await rtcSendFile(peerId, file, filename);
|
|
101
|
+
if (handle) {
|
|
102
|
+
handle.on?.("progress", (p) => {
|
|
103
|
+
const existing = outboundTransfers.value.get(transferId);
|
|
104
|
+
if (existing) {
|
|
105
|
+
const updated = new Map(outboundTransfers.value);
|
|
106
|
+
updated.set(transferId, { ...existing, progress: p });
|
|
107
|
+
outboundTransfers.value = updated;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
handle.on?.("complete", () => {
|
|
111
|
+
const updated = new Map(outboundTransfers.value);
|
|
112
|
+
const existing = updated.get(transferId);
|
|
113
|
+
if (existing) updated.set(transferId, { ...existing, status: "complete", progress: 1 });
|
|
114
|
+
outboundTransfers.value = updated;
|
|
115
|
+
});
|
|
116
|
+
handle.on?.("error", () => {
|
|
117
|
+
const updated = new Map(outboundTransfers.value);
|
|
118
|
+
const existing = updated.get(transferId);
|
|
119
|
+
if (existing) updated.set(transferId, { ...existing, status: "error" });
|
|
120
|
+
outboundTransfers.value = updated;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
const updated = new Map(outboundTransfers.value);
|
|
125
|
+
const existing = updated.get(transferId);
|
|
126
|
+
if (existing) updated.set(transferId, { ...existing, status: "error" });
|
|
127
|
+
outboundTransfers.value = updated;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function downloadFile(transferId) {
|
|
131
|
+
const file = completedFiles.value.find((f) => f.transferId === transferId);
|
|
132
|
+
if (!file) return;
|
|
133
|
+
const a = document.createElement("a");
|
|
134
|
+
a.href = file.objectUrl;
|
|
135
|
+
a.download = file.filename;
|
|
136
|
+
document.body.appendChild(a);
|
|
137
|
+
a.click();
|
|
138
|
+
document.body.removeChild(a);
|
|
139
|
+
}
|
|
140
|
+
function clearCompleted() {
|
|
141
|
+
for (const file of completedFiles.value) {
|
|
142
|
+
try {
|
|
143
|
+
URL.revokeObjectURL(file.objectUrl);
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
completedFiles.value = [];
|
|
148
|
+
const next = /* @__PURE__ */ new Map();
|
|
149
|
+
for (const [id, transfer] of outboundTransfers.value) {
|
|
150
|
+
if (transfer.status !== "complete" && transfer.status !== "error") {
|
|
151
|
+
next.set(id, transfer);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
outboundTransfers.value = next;
|
|
155
|
+
}
|
|
156
|
+
export function useFileTransfer() {
|
|
157
|
+
return {
|
|
158
|
+
/** Active inbound file transfers. */
|
|
159
|
+
inboundTransfers,
|
|
160
|
+
/** Active outbound file transfers. */
|
|
161
|
+
outboundTransfers,
|
|
162
|
+
/** Completed received files (with object URLs for download). */
|
|
163
|
+
completedFiles,
|
|
164
|
+
/** Send a file to a specific peer. */
|
|
165
|
+
sendFile,
|
|
166
|
+
/** Trigger browser download for a completed file. */
|
|
167
|
+
downloadFile,
|
|
168
|
+
/** Clear all completed transfers and revoke object URLs. */
|
|
169
|
+
clearCompleted
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -3,6 +3,7 @@ const followingUserKey = ref(null);
|
|
|
3
3
|
let cleanup = null;
|
|
4
4
|
let navLock = false;
|
|
5
5
|
export function useFollowUser() {
|
|
6
|
+
const { getDocUrl } = useDocSlugs();
|
|
6
7
|
function setFollowing(key, provider, getCurrentDocId) {
|
|
7
8
|
cleanup?.();
|
|
8
9
|
cleanup = null;
|
|
@@ -18,7 +19,7 @@ export function useFollowUser() {
|
|
|
18
19
|
const targetDocId = state.docId;
|
|
19
20
|
if (targetDocId && targetDocId !== getCurrentDocId()) {
|
|
20
21
|
navLock = true;
|
|
21
|
-
navigateTo(
|
|
22
|
+
navigateTo(getDocUrl(targetDocId));
|
|
22
23
|
setTimeout(() => {
|
|
23
24
|
navLock = false;
|
|
24
25
|
}, 1e3);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useInvites
|
|
3
|
+
*
|
|
4
|
+
* Focused composable for invite management. Wraps the invite methods
|
|
5
|
+
* from useAbracadabra() with reactive state for the invite list.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { invites, create, revoke, redeem, refresh } = useInvites()
|
|
9
|
+
*/
|
|
10
|
+
export interface Invite {
|
|
11
|
+
code: string;
|
|
12
|
+
role: string;
|
|
13
|
+
uses: number;
|
|
14
|
+
max_uses: number | null;
|
|
15
|
+
expires_at: number | null;
|
|
16
|
+
created_at: number;
|
|
17
|
+
}
|
|
18
|
+
declare function refresh(): Promise<void>;
|
|
19
|
+
declare function create(opts?: {
|
|
20
|
+
role?: string;
|
|
21
|
+
maxUses?: number;
|
|
22
|
+
expiresIn?: number;
|
|
23
|
+
}): Promise<Invite>;
|
|
24
|
+
declare function revoke(code: string): Promise<void>;
|
|
25
|
+
declare function redeem(code: string): Promise<void>;
|
|
26
|
+
export declare function useInvites(): {
|
|
27
|
+
/** Reactive list of invites. */
|
|
28
|
+
invites: import("vue").Ref<{
|
|
29
|
+
code: string;
|
|
30
|
+
role: string;
|
|
31
|
+
uses: number;
|
|
32
|
+
max_uses: number | null;
|
|
33
|
+
expires_at: number | null;
|
|
34
|
+
created_at: number;
|
|
35
|
+
}[], Invite[] | {
|
|
36
|
+
code: string;
|
|
37
|
+
role: string;
|
|
38
|
+
uses: number;
|
|
39
|
+
max_uses: number | null;
|
|
40
|
+
expires_at: number | null;
|
|
41
|
+
created_at: number;
|
|
42
|
+
}[]>;
|
|
43
|
+
/** Whether invite list is loading. */
|
|
44
|
+
loading: import("vue").Ref<boolean, boolean>;
|
|
45
|
+
/** Last error message. */
|
|
46
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
47
|
+
/** Refresh the invite list from server. */
|
|
48
|
+
refresh: typeof refresh;
|
|
49
|
+
/** Create a new invite. */
|
|
50
|
+
create: typeof create;
|
|
51
|
+
/** Revoke an existing invite by code. */
|
|
52
|
+
revoke: typeof revoke;
|
|
53
|
+
/** Redeem an invite code. */
|
|
54
|
+
redeem: typeof redeem;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
const invites = ref([]);
|
|
3
|
+
const loading = ref(false);
|
|
4
|
+
const error = ref(null);
|
|
5
|
+
let _fetched = false;
|
|
6
|
+
async function refresh() {
|
|
7
|
+
const abra = useAbracadabra();
|
|
8
|
+
if (!abra.client.value) return;
|
|
9
|
+
loading.value = true;
|
|
10
|
+
error.value = null;
|
|
11
|
+
try {
|
|
12
|
+
const result = await abra.listInvites();
|
|
13
|
+
invites.value = result ?? [];
|
|
14
|
+
_fetched = true;
|
|
15
|
+
} catch (e) {
|
|
16
|
+
error.value = e.message ?? "Failed to fetch invites";
|
|
17
|
+
} finally {
|
|
18
|
+
loading.value = false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function create(opts) {
|
|
22
|
+
const abra = useAbracadabra();
|
|
23
|
+
error.value = null;
|
|
24
|
+
try {
|
|
25
|
+
const result = await abra.createInvite(opts);
|
|
26
|
+
await refresh();
|
|
27
|
+
return result;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
error.value = e.message ?? "Failed to create invite";
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function revoke(code) {
|
|
34
|
+
const abra = useAbracadabra();
|
|
35
|
+
error.value = null;
|
|
36
|
+
try {
|
|
37
|
+
await abra.revokeInvite(code);
|
|
38
|
+
invites.value = invites.value.filter((i) => i.code !== code);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
error.value = e.message ?? "Failed to revoke invite";
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function redeem(code) {
|
|
45
|
+
const abra = useAbracadabra();
|
|
46
|
+
error.value = null;
|
|
47
|
+
try {
|
|
48
|
+
await abra.redeemInvite(code);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
error.value = e.message ?? "Failed to redeem invite";
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function useInvites() {
|
|
55
|
+
if (!_fetched && !loading.value) {
|
|
56
|
+
const abra = useAbracadabra();
|
|
57
|
+
if (abra.client.value) {
|
|
58
|
+
refresh();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
/** Reactive list of invites. */
|
|
63
|
+
invites,
|
|
64
|
+
/** Whether invite list is loading. */
|
|
65
|
+
loading,
|
|
66
|
+
/** Last error message. */
|
|
67
|
+
error,
|
|
68
|
+
/** Refresh the invite list from server. */
|
|
69
|
+
refresh,
|
|
70
|
+
/** Create a new invite. */
|
|
71
|
+
create,
|
|
72
|
+
/** Revoke an existing invite by code. */
|
|
73
|
+
revoke,
|
|
74
|
+
/** Redeem an invite code. */
|
|
75
|
+
redeem
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ShallowRef } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Manages the state for opening a child document in a slide-over panel.
|
|
4
|
+
* Loads the child provider, waits for sync, and primes the Y.XmlFragment
|
|
5
|
+
* so TipTap can initialise without errors.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useNodePanel(parentProvider: ShallowRef<any>): {
|
|
8
|
+
openNodeId: import("vue").Ref<string | null, string | null>;
|
|
9
|
+
openNodeLabel: import("vue").Ref<string, string>;
|
|
10
|
+
openNodeProvider: import("vue").Ref<any, any>;
|
|
11
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
12
|
+
openNode: (nodeId: string, label: string) => Promise<void>;
|
|
13
|
+
closePanel: () => void;
|
|
14
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export function useNodePanel(parentProvider) {
|
|
3
|
+
const openNodeId = ref(null);
|
|
4
|
+
const openNodeLabel = ref("");
|
|
5
|
+
const openNodeProvider = ref(null);
|
|
6
|
+
const isLoading = ref(false);
|
|
7
|
+
async function openNode(nodeId, label) {
|
|
8
|
+
if (!parentProvider.value) return;
|
|
9
|
+
isLoading.value = true;
|
|
10
|
+
try {
|
|
11
|
+
const childProv = await parentProvider.value.loadChild(nodeId);
|
|
12
|
+
if (!childProv.isSynced) {
|
|
13
|
+
await new Promise((resolve) => {
|
|
14
|
+
const done = () => {
|
|
15
|
+
childProv.off("synced", done);
|
|
16
|
+
resolve();
|
|
17
|
+
};
|
|
18
|
+
childProv.on("synced", done);
|
|
19
|
+
setTimeout(resolve, 6e3);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const doc = childProv.document;
|
|
23
|
+
const frag = doc.getXmlFragment("default");
|
|
24
|
+
if (!frag._item) {
|
|
25
|
+
doc.transact(() => {
|
|
26
|
+
void frag.length;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
openNodeProvider.value = childProv;
|
|
30
|
+
openNodeLabel.value = label;
|
|
31
|
+
openNodeId.value = nodeId;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error("Failed to load node:", e);
|
|
34
|
+
} finally {
|
|
35
|
+
isLoading.value = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function closePanel() {
|
|
39
|
+
openNodeId.value = null;
|
|
40
|
+
openNodeLabel.value = "";
|
|
41
|
+
openNodeProvider.value = null;
|
|
42
|
+
isLoading.value = false;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
openNodeId,
|
|
46
|
+
openNodeLabel,
|
|
47
|
+
openNodeProvider,
|
|
48
|
+
isLoading,
|
|
49
|
+
openNode,
|
|
50
|
+
closePanel
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -71,7 +71,8 @@ export function _handleStatelessNotification(payload) {
|
|
|
71
71
|
break;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
} catch {
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (import.meta.dev) console.warn("[abracadabra] notifications: failed to handle stateless message:", e);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
function _scheduleToast(notif) {
|
|
@@ -117,7 +118,8 @@ function _flushToasts() {
|
|
|
117
118
|
duration: 5e3
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
|
-
}).catch(() => {
|
|
121
|
+
}).catch((e) => {
|
|
122
|
+
if (import.meta.dev) console.debug("[abracadabra] notifications: toast display failed:", e);
|
|
121
123
|
});
|
|
122
124
|
}
|
|
123
125
|
function fetchNotifications(opts) {
|
|
@@ -4,7 +4,8 @@ function loadAccounts() {
|
|
|
4
4
|
try {
|
|
5
5
|
const raw = localStorage.getItem(ACCOUNTS_KEY);
|
|
6
6
|
return raw ? JSON.parse(raw) : [];
|
|
7
|
-
} catch {
|
|
7
|
+
} catch (e) {
|
|
8
|
+
if (import.meta.dev) console.debug("[abracadabra] passkey: failed to load accounts from localStorage:", e);
|
|
8
9
|
return [];
|
|
9
10
|
}
|
|
10
11
|
}
|
|
@@ -18,7 +19,8 @@ export function usePasskeyAccounts() {
|
|
|
18
19
|
function persist() {
|
|
19
20
|
try {
|
|
20
21
|
localStorage.setItem(ACCOUNTS_KEY, JSON.stringify(accounts.value));
|
|
21
|
-
} catch {
|
|
22
|
+
} catch (e) {
|
|
23
|
+
if (import.meta.dev) console.debug("[abracadabra] passkey: failed to persist accounts:", e);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
function registerAccount(account) {
|
|
@@ -16,5 +16,6 @@ export declare function useSearchIndex(): {
|
|
|
16
16
|
search: (query: string, limit?: number) => Promise<SearchResult[]>;
|
|
17
17
|
isReady: import("vue").ComputedRef<boolean>;
|
|
18
18
|
indexedCount: import("vue").ComputedRef<number>;
|
|
19
|
+
error: import("vue").ComputedRef<string | null>;
|
|
19
20
|
};
|
|
20
21
|
export {};
|
|
@@ -4,6 +4,7 @@ let _searchIndex = null;
|
|
|
4
4
|
const _indexedVersions = /* @__PURE__ */ new Map();
|
|
5
5
|
const _isReady = ref(false);
|
|
6
6
|
const _indexedCount = ref(0);
|
|
7
|
+
const _error = ref(null);
|
|
7
8
|
let _stopWatcher = null;
|
|
8
9
|
function walkXmlForText(node, texts) {
|
|
9
10
|
for (const child of node.toArray()) {
|
|
@@ -38,7 +39,8 @@ function extractDocTexts(ydoc, label, meta) {
|
|
|
38
39
|
try {
|
|
39
40
|
const fragment = ydoc.getXmlFragment("default");
|
|
40
41
|
walkXmlForText(fragment, texts);
|
|
41
|
-
} catch {
|
|
42
|
+
} catch (e) {
|
|
43
|
+
if (import.meta.dev) console.warn("[abracadabra] search: failed to extract doc texts:", e);
|
|
42
44
|
}
|
|
43
45
|
return texts;
|
|
44
46
|
}
|
|
@@ -49,7 +51,9 @@ export function _initSearchIndex(serverOrigin) {
|
|
|
49
51
|
_indexedVersions.clear();
|
|
50
52
|
_indexedCount.value = 0;
|
|
51
53
|
_startWatcher();
|
|
52
|
-
}).catch(() => {
|
|
54
|
+
}).catch((e) => {
|
|
55
|
+
_error.value = e?.message ?? "Failed to load SearchIndex";
|
|
56
|
+
if (import.meta.dev) console.warn("[abracadabra] search: failed to load SearchIndex:", e);
|
|
53
57
|
});
|
|
54
58
|
}
|
|
55
59
|
export function _destroySearchIndex() {
|
|
@@ -60,6 +64,7 @@ export function _destroySearchIndex() {
|
|
|
60
64
|
_isReady.value = false;
|
|
61
65
|
_indexedVersions.clear();
|
|
62
66
|
_indexedCount.value = 0;
|
|
67
|
+
_error.value = null;
|
|
63
68
|
}
|
|
64
69
|
function _startWatcher() {
|
|
65
70
|
const { syncStates } = useBackgroundSync();
|
|
@@ -82,7 +87,8 @@ function _startWatcher() {
|
|
|
82
87
|
await _searchIndex.index(docId, texts);
|
|
83
88
|
_indexedVersions.set(docId, lastSynced);
|
|
84
89
|
_indexedCount.value = _indexedVersions.size;
|
|
85
|
-
} catch {
|
|
90
|
+
} catch (e) {
|
|
91
|
+
if (import.meta.dev) console.warn(`[abracadabra] search: failed to index doc ${docId}:`, e);
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
}, { deep: false });
|
|
@@ -94,11 +100,13 @@ export function useSearchIndex() {
|
|
|
94
100
|
if (!_searchIndex) return [];
|
|
95
101
|
try {
|
|
96
102
|
return await _searchIndex.search(query, limit);
|
|
97
|
-
} catch {
|
|
103
|
+
} catch (e) {
|
|
104
|
+
if (import.meta.dev) console.warn("[abracadabra] search: query failed:", e);
|
|
98
105
|
return [];
|
|
99
106
|
}
|
|
100
107
|
},
|
|
101
108
|
isReady: computed(() => _isReady.value),
|
|
102
|
-
indexedCount: computed(() => _indexedCount.value)
|
|
109
|
+
indexedCount: computed(() => _indexedCount.value),
|
|
110
|
+
error: computed(() => _error.value)
|
|
103
111
|
};
|
|
104
112
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useServerInfo
|
|
3
|
+
*
|
|
4
|
+
* Fetches and exposes server health, metadata, and ICE server configuration.
|
|
5
|
+
* Automatically fetches on first use when client is available.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { serverName, serverVersion, healthy, iceServers, refresh } = useServerInfo()
|
|
9
|
+
*/
|
|
10
|
+
export declare function _initServerInfo(client: any): void;
|
|
11
|
+
export declare function _destroyServerInfo(): void;
|
|
12
|
+
declare function refresh(): Promise<void>;
|
|
13
|
+
export declare function useServerInfo(): {
|
|
14
|
+
/** Server display name. */
|
|
15
|
+
serverName: import("vue").Ref<string | null, string | null>;
|
|
16
|
+
/** Server version string. */
|
|
17
|
+
serverVersion: import("vue").Ref<string | null, string | null>;
|
|
18
|
+
/** Root/index document ID from server. */
|
|
19
|
+
indexDocId: import("vue").Ref<string | null, string | null>;
|
|
20
|
+
/** Whether the server is healthy. */
|
|
21
|
+
healthy: import("vue").Ref<boolean, boolean>;
|
|
22
|
+
/** ICE servers from server (STUN/TURN). */
|
|
23
|
+
iceServers: import("vue").ShallowRef<RTCIceServer[], RTCIceServer[]>;
|
|
24
|
+
/** Whether a fetch is in progress. */
|
|
25
|
+
loading: import("vue").Ref<boolean, boolean>;
|
|
26
|
+
/** Last error message. */
|
|
27
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
28
|
+
/** Re-fetch server info. */
|
|
29
|
+
refresh: typeof refresh;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ref, shallowRef } from "vue";
|
|
2
|
+
const serverName = ref(null);
|
|
3
|
+
const serverVersion = ref(null);
|
|
4
|
+
const indexDocId = ref(null);
|
|
5
|
+
const healthy = ref(false);
|
|
6
|
+
const iceServers = shallowRef([]);
|
|
7
|
+
const loading = ref(false);
|
|
8
|
+
const error = ref(null);
|
|
9
|
+
let _fetched = false;
|
|
10
|
+
let _client = null;
|
|
11
|
+
export function _initServerInfo(client) {
|
|
12
|
+
_client = client;
|
|
13
|
+
_fetched = false;
|
|
14
|
+
_fetchAll();
|
|
15
|
+
}
|
|
16
|
+
export function _destroyServerInfo() {
|
|
17
|
+
_client = null;
|
|
18
|
+
_fetched = false;
|
|
19
|
+
serverName.value = null;
|
|
20
|
+
serverVersion.value = null;
|
|
21
|
+
indexDocId.value = null;
|
|
22
|
+
healthy.value = false;
|
|
23
|
+
iceServers.value = [];
|
|
24
|
+
error.value = null;
|
|
25
|
+
}
|
|
26
|
+
async function _fetchAll() {
|
|
27
|
+
if (!_client || loading.value) return;
|
|
28
|
+
loading.value = true;
|
|
29
|
+
error.value = null;
|
|
30
|
+
try {
|
|
31
|
+
const [healthResult, infoResult, iceResult] = await Promise.allSettled([
|
|
32
|
+
_client.health(),
|
|
33
|
+
_client.serverInfo(),
|
|
34
|
+
_client.getIceServers()
|
|
35
|
+
]);
|
|
36
|
+
if (healthResult.status === "fulfilled") {
|
|
37
|
+
healthy.value = healthResult.value?.status === "ok" || !!healthResult.value;
|
|
38
|
+
}
|
|
39
|
+
if (infoResult.status === "fulfilled") {
|
|
40
|
+
const info = infoResult.value;
|
|
41
|
+
serverName.value = info?.name ?? null;
|
|
42
|
+
serverVersion.value = info?.version ?? null;
|
|
43
|
+
indexDocId.value = info?.index_doc_id ?? null;
|
|
44
|
+
}
|
|
45
|
+
if (iceResult.status === "fulfilled") {
|
|
46
|
+
iceServers.value = iceResult.value ?? [];
|
|
47
|
+
}
|
|
48
|
+
_fetched = true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
error.value = e.message ?? "Failed to fetch server info";
|
|
51
|
+
} finally {
|
|
52
|
+
loading.value = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function refresh() {
|
|
56
|
+
await _fetchAll();
|
|
57
|
+
}
|
|
58
|
+
export function useServerInfo() {
|
|
59
|
+
if (!_fetched && _client && !loading.value) {
|
|
60
|
+
_fetchAll();
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
/** Server display name. */
|
|
64
|
+
serverName,
|
|
65
|
+
/** Server version string. */
|
|
66
|
+
serverVersion,
|
|
67
|
+
/** Root/index document ID from server. */
|
|
68
|
+
indexDocId,
|
|
69
|
+
/** Whether the server is healthy. */
|
|
70
|
+
healthy,
|
|
71
|
+
/** ICE servers from server (STUN/TURN). */
|
|
72
|
+
iceServers,
|
|
73
|
+
/** Whether a fetch is in progress. */
|
|
74
|
+
loading,
|
|
75
|
+
/** Last error message. */
|
|
76
|
+
error,
|
|
77
|
+
/** Re-fetch server info. */
|
|
78
|
+
refresh
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { isUUID } from "../utils/slugify.js";
|
|
3
|
+
export function useSlugRoute() {
|
|
4
|
+
const route = useRoute();
|
|
5
|
+
const { getDocId, slugMap } = useDocSlugs();
|
|
6
|
+
const slugPath = computed(() => {
|
|
7
|
+
const params = route.params.slug;
|
|
8
|
+
return Array.isArray(params) ? params.join("/") : params ?? "";
|
|
9
|
+
});
|
|
10
|
+
const docId = computed(() => {
|
|
11
|
+
const s = slugPath.value;
|
|
12
|
+
if (isUUID(s)) return s;
|
|
13
|
+
return getDocId(s);
|
|
14
|
+
});
|
|
15
|
+
const slugMapReady = computed(() => Object.keys(slugMap.value.slugToId).length > 0);
|
|
16
|
+
const isLoading = computed(() => !isUUID(slugPath.value) && !slugMapReady.value);
|
|
17
|
+
const notFound = computed(() => !docId.value && slugMapReady.value);
|
|
18
|
+
return { slugPath, docId, isLoading, notFound };
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useSpaces
|
|
3
|
+
*
|
|
4
|
+
* Focused composable for space management. Wraps the space methods
|
|
5
|
+
* from useAbracadabra() with a cleaner API surface.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { spaces, currentSpace, create, switchTo, refresh } = useSpaces()
|
|
9
|
+
*/
|
|
10
|
+
import type { SpaceMeta } from '../types.js';
|
|
11
|
+
export declare function useSpaces(): {
|
|
12
|
+
/** All spaces on the current server. */
|
|
13
|
+
spaces: import("vue").ComputedRef<SpaceMeta[]>;
|
|
14
|
+
/** Whether the current server supports spaces. */
|
|
15
|
+
spacesEnabled: import("vue").ComputedRef<boolean>;
|
|
16
|
+
/** The currently active space. */
|
|
17
|
+
currentSpace: import("vue").ComputedRef<SpaceMeta | null>;
|
|
18
|
+
/** Whether space operations are in progress. */
|
|
19
|
+
loading: import("vue").Ref<boolean, boolean>;
|
|
20
|
+
/** Last error message. */
|
|
21
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
22
|
+
/** Refresh spaces list from server. */
|
|
23
|
+
refresh: () => Promise<void>;
|
|
24
|
+
/** Create a new space. */
|
|
25
|
+
create: (opts: {
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
visibility?: SpaceMeta["visibility"];
|
|
29
|
+
id?: string;
|
|
30
|
+
}) => Promise<SpaceMeta>;
|
|
31
|
+
/** Update an existing space. */
|
|
32
|
+
update: (id: string, opts: Partial<Pick<SpaceMeta, "name" | "description" | "visibility" | "is_hub">>) => Promise<SpaceMeta>;
|
|
33
|
+
/** Delete a space. */
|
|
34
|
+
remove: (id: string) => Promise<void>;
|
|
35
|
+
/** Switch to a different space. */
|
|
36
|
+
switchTo: (spaceDocId: string) => Promise<void>;
|
|
37
|
+
};
|