@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
package/dist/runtime/locale.d.ts
CHANGED
|
@@ -129,6 +129,77 @@ export interface AbracadabraLocale {
|
|
|
129
129
|
untitled: string;
|
|
130
130
|
noItems: string;
|
|
131
131
|
};
|
|
132
|
+
checklist: {
|
|
133
|
+
addTask: string;
|
|
134
|
+
empty: string;
|
|
135
|
+
done: string;
|
|
136
|
+
active: string;
|
|
137
|
+
all: string;
|
|
138
|
+
sortManual: string;
|
|
139
|
+
sortPriority: string;
|
|
140
|
+
sortDue: string;
|
|
141
|
+
markComplete: string;
|
|
142
|
+
markIncomplete: string;
|
|
143
|
+
addSubTask: string;
|
|
144
|
+
};
|
|
145
|
+
timeline: {
|
|
146
|
+
addEpic: string;
|
|
147
|
+
addTask: string;
|
|
148
|
+
untitled: string;
|
|
149
|
+
empty: string;
|
|
150
|
+
week: string;
|
|
151
|
+
month: string;
|
|
152
|
+
quarter: string;
|
|
153
|
+
};
|
|
154
|
+
call: {
|
|
155
|
+
noStreams: string;
|
|
156
|
+
turnOnCamera: string;
|
|
157
|
+
};
|
|
158
|
+
graph: {
|
|
159
|
+
addNode: string;
|
|
160
|
+
empty: string;
|
|
161
|
+
pin: string;
|
|
162
|
+
unpin: string;
|
|
163
|
+
openAsDoc: string;
|
|
164
|
+
delete: string;
|
|
165
|
+
};
|
|
166
|
+
dashboard: {
|
|
167
|
+
addItem: string;
|
|
168
|
+
empty: string;
|
|
169
|
+
modeIcon: string;
|
|
170
|
+
modeWidgetSm: string;
|
|
171
|
+
modeWidgetLg: string;
|
|
172
|
+
cleanUp: string;
|
|
173
|
+
open: string;
|
|
174
|
+
rename: string;
|
|
175
|
+
duplicate: string;
|
|
176
|
+
delete: string;
|
|
177
|
+
confirmDelete: string;
|
|
178
|
+
confirmDeleteDesc: string;
|
|
179
|
+
cancel: string;
|
|
180
|
+
};
|
|
181
|
+
map: {
|
|
182
|
+
addMarker: string;
|
|
183
|
+
addLine: string;
|
|
184
|
+
addMeasure: string;
|
|
185
|
+
empty: string;
|
|
186
|
+
pan: string;
|
|
187
|
+
ping: string;
|
|
188
|
+
finish: string;
|
|
189
|
+
save: string;
|
|
190
|
+
points: string;
|
|
191
|
+
follow: string;
|
|
192
|
+
unfollow: string;
|
|
193
|
+
following: string;
|
|
194
|
+
showLabels: string;
|
|
195
|
+
hideLabels: string;
|
|
196
|
+
openInPanel: string;
|
|
197
|
+
hintMarker: string;
|
|
198
|
+
hintPing: string;
|
|
199
|
+
hintLine: string;
|
|
200
|
+
hintMeasure: string;
|
|
201
|
+
hintMeasureAdd: string;
|
|
202
|
+
};
|
|
132
203
|
};
|
|
133
204
|
}
|
|
134
205
|
export declare const DEFAULT_LOCALE: AbracadabraLocale;
|
package/dist/runtime/locale.js
CHANGED
|
@@ -114,6 +114,77 @@ export const DEFAULT_LOCALE = {
|
|
|
114
114
|
addItem: "Add item",
|
|
115
115
|
untitled: "Untitled",
|
|
116
116
|
noItems: "No items yet"
|
|
117
|
+
},
|
|
118
|
+
checklist: {
|
|
119
|
+
addTask: "Add task",
|
|
120
|
+
empty: "No tasks yet",
|
|
121
|
+
done: "done",
|
|
122
|
+
active: "Active",
|
|
123
|
+
all: "All",
|
|
124
|
+
sortManual: "Manual",
|
|
125
|
+
sortPriority: "Priority",
|
|
126
|
+
sortDue: "Due date",
|
|
127
|
+
markComplete: "Mark complete",
|
|
128
|
+
markIncomplete: "Mark incomplete",
|
|
129
|
+
addSubTask: "Add sub-task"
|
|
130
|
+
},
|
|
131
|
+
timeline: {
|
|
132
|
+
addEpic: "Add epic",
|
|
133
|
+
addTask: "Add task",
|
|
134
|
+
untitled: "Untitled",
|
|
135
|
+
empty: "No epics yet",
|
|
136
|
+
week: "Week",
|
|
137
|
+
month: "Month",
|
|
138
|
+
quarter: "Quarter"
|
|
139
|
+
},
|
|
140
|
+
call: {
|
|
141
|
+
noStreams: "No video streams",
|
|
142
|
+
turnOnCamera: "Turn on your camera to get started"
|
|
143
|
+
},
|
|
144
|
+
graph: {
|
|
145
|
+
addNode: "Add node",
|
|
146
|
+
empty: "No nodes yet",
|
|
147
|
+
pin: "Pin position",
|
|
148
|
+
unpin: "Unpin",
|
|
149
|
+
openAsDoc: "Open as document",
|
|
150
|
+
delete: "Delete"
|
|
151
|
+
},
|
|
152
|
+
dashboard: {
|
|
153
|
+
addItem: "Add item",
|
|
154
|
+
empty: "No items yet",
|
|
155
|
+
modeIcon: "Icon",
|
|
156
|
+
modeWidgetSm: "Small widget",
|
|
157
|
+
modeWidgetLg: "Large widget",
|
|
158
|
+
cleanUp: "Clean up",
|
|
159
|
+
open: "Open",
|
|
160
|
+
rename: "Rename",
|
|
161
|
+
duplicate: "Duplicate",
|
|
162
|
+
delete: "Delete",
|
|
163
|
+
confirmDelete: "Delete item?",
|
|
164
|
+
confirmDeleteDesc: "This action cannot be undone.",
|
|
165
|
+
cancel: "Cancel"
|
|
166
|
+
},
|
|
167
|
+
map: {
|
|
168
|
+
addMarker: "Place marker",
|
|
169
|
+
addLine: "Draw line",
|
|
170
|
+
addMeasure: "Measure distance",
|
|
171
|
+
empty: "No markers yet",
|
|
172
|
+
pan: "Pan",
|
|
173
|
+
ping: "Send ping",
|
|
174
|
+
finish: "Finish",
|
|
175
|
+
save: "Save",
|
|
176
|
+
points: "points",
|
|
177
|
+
follow: "Follow",
|
|
178
|
+
unfollow: "Unfollow",
|
|
179
|
+
following: "Following",
|
|
180
|
+
showLabels: "Show map labels",
|
|
181
|
+
hideLabels: "Hide map labels",
|
|
182
|
+
openInPanel: "Open in sidebar",
|
|
183
|
+
hintMarker: "Click to place marker",
|
|
184
|
+
hintPing: "Click to send ping",
|
|
185
|
+
hintLine: "Click to start drawing a line",
|
|
186
|
+
hintMeasure: "Click to start measuring",
|
|
187
|
+
hintMeasureAdd: "Click to add points"
|
|
117
188
|
}
|
|
118
189
|
}
|
|
119
190
|
};
|
|
@@ -34,6 +34,19 @@ import {
|
|
|
34
34
|
_initFileIndex,
|
|
35
35
|
_destroyFileIndex
|
|
36
36
|
} from "./composables/useFileIndex.js";
|
|
37
|
+
import {
|
|
38
|
+
_initBroadcastSync,
|
|
39
|
+
_destroyBroadcastSync,
|
|
40
|
+
useBroadcastSync
|
|
41
|
+
} from "./composables/useBroadcastSync.js";
|
|
42
|
+
import {
|
|
43
|
+
_destroyWebRTC
|
|
44
|
+
} from "./composables/useWebRTC.js";
|
|
45
|
+
import {
|
|
46
|
+
_initServerInfo,
|
|
47
|
+
_destroyServerInfo
|
|
48
|
+
} from "./composables/useServerInfo.js";
|
|
49
|
+
import { setSdkModule } from "./utils/sdkRef.js";
|
|
37
50
|
const NUXT_UI_PRIMARY_COLORS = /* @__PURE__ */ new Set([
|
|
38
51
|
"red",
|
|
39
52
|
"orange",
|
|
@@ -365,6 +378,9 @@ export default defineNuxtPlugin({
|
|
|
365
378
|
_destroyNotifications();
|
|
366
379
|
_destroyChat();
|
|
367
380
|
_destroyFileIndex();
|
|
381
|
+
_destroyBroadcastSync();
|
|
382
|
+
_destroyWebRTC();
|
|
383
|
+
_destroyServerInfo();
|
|
368
384
|
}
|
|
369
385
|
async function switchServer(url) {
|
|
370
386
|
if (url === currentServerUrl.value) return;
|
|
@@ -530,7 +546,7 @@ export default defineNuxtPlugin({
|
|
|
530
546
|
}
|
|
531
547
|
try {
|
|
532
548
|
const [
|
|
533
|
-
|
|
549
|
+
sdkModule,
|
|
534
550
|
ed,
|
|
535
551
|
{ sha512 }
|
|
536
552
|
] = await Promise.all([
|
|
@@ -538,7 +554,9 @@ export default defineNuxtPlugin({
|
|
|
538
554
|
import("@noble/ed25519"),
|
|
539
555
|
import("@noble/hashes/sha512")
|
|
540
556
|
]);
|
|
541
|
-
|
|
557
|
+
setSdkModule(sdkModule);
|
|
558
|
+
const { AbracadabraClient, AbracadabraProvider, HocuspocusProviderWebsocket, CryptoIdentityKeystore } = sdkModule;
|
|
559
|
+
ed.hashes.sha512 = (m) => sha512(m);
|
|
542
560
|
const ks = new CryptoIdentityKeystore();
|
|
543
561
|
keystore.value = ks;
|
|
544
562
|
let privKey = null;
|
|
@@ -662,7 +680,7 @@ export default defineNuxtPlugin({
|
|
|
662
680
|
}
|
|
663
681
|
}
|
|
664
682
|
const server = savedServers.value.find((s) => s.url === serverUrl);
|
|
665
|
-
|
|
683
|
+
const docId = configEntryDocId ?? server?.hubDocId ?? spacesInfo?.hubDocId ?? server?.cachedSpaces?.[0]?.doc_id ?? spacesInfo?.spaces?.[0]?.doc_id ?? server?.entryDocId ?? info.index_doc_id ?? void 0;
|
|
666
684
|
if (!docId) {
|
|
667
685
|
connectionError.value = "No entry document found. Configure entryDocId or ensure the server has spaces or an index document.";
|
|
668
686
|
addLog("No entry document \u2014 cannot connect", "system");
|
|
@@ -736,6 +754,12 @@ export default defineNuxtPlugin({
|
|
|
736
754
|
}).catch(() => {
|
|
737
755
|
});
|
|
738
756
|
}
|
|
757
|
+
if (features.broadcastSync !== false) {
|
|
758
|
+
const { BroadcastChannelSync } = sdkModule ?? {};
|
|
759
|
+
if (BroadcastChannelSync && _provider && _doc) {
|
|
760
|
+
_initBroadcastSync(BroadcastChannelSync, _doc, docId, _provider.awareness);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
739
763
|
try {
|
|
740
764
|
_provider.sendStateless?.(JSON.stringify({ type: "notify:fetch", limit: 20 }));
|
|
741
765
|
} catch {
|
|
@@ -818,6 +842,7 @@ export default defineNuxtPlugin({
|
|
|
818
842
|
_initFileBlobStore(blobStore);
|
|
819
843
|
} catch {
|
|
820
844
|
}
|
|
845
|
+
_initServerInfo(_client);
|
|
821
846
|
_initNotifications(publicKeyB64);
|
|
822
847
|
_initChat(publicKeyB64, userName);
|
|
823
848
|
if (_provider.onStateless !== void 0) {
|
|
@@ -890,6 +915,7 @@ export default defineNuxtPlugin({
|
|
|
890
915
|
createInvite,
|
|
891
916
|
listInvites,
|
|
892
917
|
revokeInvite,
|
|
918
|
+
broadcastSyncActive: useBroadcastSync().isActive,
|
|
893
919
|
init
|
|
894
920
|
};
|
|
895
921
|
nuxtApp.provide("abracadabra", abra);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Usage in a Nuxt page:
|
|
14
14
|
* const { data } = await useFetch(`/api/_abracadabra/render/${docId}`)
|
|
15
|
-
* // data.value: { html, title, type, meta, docId, source }
|
|
15
|
+
* // data.value: { html, title, type, meta, description, ogImage, docId, source }
|
|
16
16
|
*/
|
|
17
17
|
declare const _default: any;
|
|
18
18
|
export default _default;
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
function extractFirstParagraph(html) {
|
|
2
|
+
if (!html) return "";
|
|
3
|
+
const pMatch = html.match(/<p[^>]*>(.*?)<\/p>/is);
|
|
4
|
+
const raw = pMatch?.[1] ?? html;
|
|
5
|
+
const text = raw.replace(/<[^>]+>/g, "").replace(/&[a-z]+;/gi, " ").trim();
|
|
6
|
+
if (!text) return "";
|
|
7
|
+
return text.length > 160 ? text.slice(0, 157) + "..." : text;
|
|
8
|
+
}
|
|
9
|
+
function extractFirstImage(html) {
|
|
10
|
+
if (!html) return "";
|
|
11
|
+
const match = html.match(/<img[^>]+src=["']([^"']+)["']/i);
|
|
12
|
+
return match?.[1] ?? "";
|
|
13
|
+
}
|
|
14
|
+
let _maxAge = 60;
|
|
15
|
+
try {
|
|
16
|
+
_maxAge = useRuntimeConfig().public?.abracadabra?.server?.cacheMaxAge ?? 60;
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
1
19
|
export default defineCachedEventHandler(async (event) => {
|
|
2
20
|
const config = useRuntimeConfig();
|
|
3
21
|
const baseUrl = config.public?.abracadabra?.url;
|
|
@@ -15,12 +33,15 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
15
33
|
]);
|
|
16
34
|
if (cachedHtml !== null) {
|
|
17
35
|
const treeEntry = cachedJson?.["doc-tree"]?.[docId] ?? {};
|
|
36
|
+
const html2 = cachedHtml ?? "";
|
|
18
37
|
return {
|
|
19
38
|
docId,
|
|
20
|
-
html:
|
|
39
|
+
html: html2,
|
|
21
40
|
title: treeEntry.label ?? "",
|
|
22
41
|
type: treeEntry.type ?? "doc",
|
|
23
42
|
meta: treeEntry.meta ?? {},
|
|
43
|
+
description: treeEntry.meta?.note ?? treeEntry.meta?.description ?? extractFirstParagraph(html2),
|
|
44
|
+
ogImage: treeEntry.meta?.coverImage ?? extractFirstImage(html2),
|
|
24
45
|
source: "crdt-cache"
|
|
25
46
|
};
|
|
26
47
|
}
|
|
@@ -37,15 +58,19 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
37
58
|
{ headers }
|
|
38
59
|
).catch(() => null)
|
|
39
60
|
]);
|
|
61
|
+
const html = exportRes?.html ?? "";
|
|
62
|
+
const meta = metaRes?.meta ?? {};
|
|
40
63
|
return {
|
|
41
64
|
docId,
|
|
42
|
-
html
|
|
65
|
+
html,
|
|
43
66
|
title: metaRes?.label ?? "",
|
|
44
67
|
type: metaRes?.type ?? "doc",
|
|
45
|
-
meta
|
|
68
|
+
meta,
|
|
69
|
+
description: meta?.note ?? meta?.description ?? extractFirstParagraph(html),
|
|
70
|
+
ogImage: meta?.coverImage ?? extractFirstImage(html),
|
|
46
71
|
source: "rest"
|
|
47
72
|
};
|
|
48
73
|
}, {
|
|
49
|
-
maxAge:
|
|
74
|
+
maxAge: _maxAge,
|
|
50
75
|
getKey: (event) => `abra:render:${getRouterParam(event, "docId")}`
|
|
51
76
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getDocIdFromSlug } from "../../../utils/slugMap.js";
|
|
2
|
+
import { getCachedHTML, getCachedJSON } from "../../../utils/docCache.js";
|
|
3
|
+
function extractFirstParagraph(html) {
|
|
4
|
+
if (!html) return "";
|
|
5
|
+
const pMatch = html.match(/<p[^>]*>(.*?)<\/p>/is);
|
|
6
|
+
const raw = pMatch?.[1] ?? html;
|
|
7
|
+
const text = raw.replace(/<[^>]+>/g, "").replace(/&[a-z]+;/gi, " ").trim();
|
|
8
|
+
if (!text) return "";
|
|
9
|
+
return text.length > 160 ? text.slice(0, 157) + "..." : text;
|
|
10
|
+
}
|
|
11
|
+
function extractFirstImage(html) {
|
|
12
|
+
if (!html) return "";
|
|
13
|
+
const match = html.match(/<img[^>]+src=["']([^"']+)["']/i);
|
|
14
|
+
return match?.[1] ?? "";
|
|
15
|
+
}
|
|
16
|
+
export default defineEventHandler(async (event) => {
|
|
17
|
+
const slugParam = getRouterParam(event, "slug");
|
|
18
|
+
if (!slugParam) {
|
|
19
|
+
throw createError({ statusCode: 400, message: "slug is required" });
|
|
20
|
+
}
|
|
21
|
+
const slug = slugParam;
|
|
22
|
+
const docId = getDocIdFromSlug(slug);
|
|
23
|
+
if (!docId) {
|
|
24
|
+
throw createError({ statusCode: 404, message: `Document not found for slug: ${slug}` });
|
|
25
|
+
}
|
|
26
|
+
const [cachedHtml, cachedJson] = await Promise.all([
|
|
27
|
+
getCachedHTML(docId),
|
|
28
|
+
getCachedJSON(docId)
|
|
29
|
+
]);
|
|
30
|
+
const html = cachedHtml ?? "";
|
|
31
|
+
const treeEntry = cachedJson?.["doc-tree"]?.[docId] ?? {};
|
|
32
|
+
return {
|
|
33
|
+
docId,
|
|
34
|
+
slug,
|
|
35
|
+
html,
|
|
36
|
+
title: treeEntry.label ?? "",
|
|
37
|
+
type: treeEntry.type ?? "doc",
|
|
38
|
+
meta: treeEntry.meta ?? {},
|
|
39
|
+
description: treeEntry.meta?.note ?? treeEntry.meta?.description ?? extractFirstParagraph(html),
|
|
40
|
+
ogImage: treeEntry.meta?.coverImage ?? extractFirstImage(html),
|
|
41
|
+
source: cachedHtml !== null ? "crdt-cache" : "none"
|
|
42
|
+
};
|
|
43
|
+
});
|
|
@@ -4,6 +4,7 @@ import { useStorage } from "nitropack/runtime/storage";
|
|
|
4
4
|
import { registerServerPlugin, bootRunners, shutdownAllRunners } from "../utils/serverRunner.js";
|
|
5
5
|
import { createDocCacheAPI } from "../utils/docCache.js";
|
|
6
6
|
import { docTreeCacheRunner } from "../runners/doc-tree-cache.js";
|
|
7
|
+
import { initSlugMap, loadPersistedSlugMap } from "../utils/slugMap.js";
|
|
7
8
|
function fromBase64Url(str) {
|
|
8
9
|
const b = atob(str.replace(/-/g, "+").replace(/_/g, "/").padEnd(str.length + (4 - str.length % 4) % 4, "="));
|
|
9
10
|
const a = new Uint8Array(b.length);
|
|
@@ -18,6 +19,9 @@ function toBase64Url(bytes) {
|
|
|
18
19
|
export default defineNitroPlugin(async (nitroApp) => {
|
|
19
20
|
const config = useRuntimeConfig();
|
|
20
21
|
const abraConfig = config.abracadabra;
|
|
22
|
+
const storage = useStorage();
|
|
23
|
+
initSlugMap(storage);
|
|
24
|
+
await loadPersistedSlugMap();
|
|
21
25
|
const pubKeyB64 = abraConfig?.servicePublicKey ?? "";
|
|
22
26
|
const privKeyB64 = abraConfig?.servicePrivateKey ?? "";
|
|
23
27
|
const rootDocIdOverride = abraConfig?.serviceRootDocId ?? "";
|
|
@@ -38,15 +42,16 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
38
42
|
import("@noble/hashes/sha2.js"),
|
|
39
43
|
import("yjs")
|
|
40
44
|
]);
|
|
41
|
-
ed.etc
|
|
42
|
-
|
|
45
|
+
if (ed.etc && !ed.etc.sha512Sync) {
|
|
46
|
+
ed.etc.sha512Sync = (m) => sha512(m);
|
|
47
|
+
}
|
|
43
48
|
const privKey = fromBase64Url(privKeyB64);
|
|
44
49
|
const client = new AbracadabraClient({
|
|
45
50
|
url: config.public?.abracadabra?.url,
|
|
46
51
|
persistAuth: false
|
|
47
52
|
});
|
|
48
53
|
await client.loginWithKey(pubKeyB64, async (challenge) => {
|
|
49
|
-
const sig = await ed.
|
|
54
|
+
const sig = await ed.signAsync(fromBase64Url(challenge), privKey);
|
|
50
55
|
return toBase64Url(sig);
|
|
51
56
|
});
|
|
52
57
|
console.log("[abracadabra-service] Authenticated as service role");
|
|
@@ -87,13 +92,13 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
87
92
|
new Promise((_, reject) => setTimeout(() => reject(new Error("sync timeout")), 3e4))
|
|
88
93
|
]);
|
|
89
94
|
console.log("[abracadabra-service] Root doc synced");
|
|
90
|
-
const
|
|
95
|
+
const storage2 = useStorage();
|
|
91
96
|
const ctx = {
|
|
92
97
|
client,
|
|
93
98
|
wsp,
|
|
94
99
|
rootDoc,
|
|
95
100
|
rootProvider,
|
|
96
|
-
storage,
|
|
101
|
+
storage: storage2,
|
|
97
102
|
docCache: createDocCacheAPI(rootDoc)
|
|
98
103
|
};
|
|
99
104
|
const publicConfig = config.public?.abracadabra;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { initDocCache, observeDoc, unobserveDoc } from "../utils/docCache.js";
|
|
2
2
|
import { getServerPlugins } from "../utils/serverRunner.js";
|
|
3
|
+
import { initSlugMap, rebuildSlugMapSync, rebuildSlugMap } from "../utils/slugMap.js";
|
|
3
4
|
export const docTreeCacheRunner = {
|
|
4
5
|
name: "abracadabra:doc-tree-cache",
|
|
5
6
|
async start(ctx) {
|
|
@@ -9,6 +10,7 @@ export const docTreeCacheRunner = {
|
|
|
9
10
|
const pendingDocs = /* @__PURE__ */ new Set();
|
|
10
11
|
const serverExts = getServerPlugins().flatMap((p) => p.serverExtensions?.() ?? []);
|
|
11
12
|
initDocCache(storage, serverExts);
|
|
13
|
+
initSlugMap(storage);
|
|
12
14
|
async function trackDoc(docId) {
|
|
13
15
|
if (loadedDocs.has(docId) || pendingDocs.has(docId)) return;
|
|
14
16
|
pendingDocs.add(docId);
|
|
@@ -42,6 +44,7 @@ export const docTreeCacheRunner = {
|
|
|
42
44
|
treeMap.forEach((_val, docId) => {
|
|
43
45
|
trackDoc(docId);
|
|
44
46
|
});
|
|
47
|
+
rebuildSlugMapSync(treeMap);
|
|
45
48
|
const observer = (event) => {
|
|
46
49
|
event.keysChanged.forEach((docId) => {
|
|
47
50
|
if (treeMap.has(docId)) {
|
|
@@ -52,6 +55,7 @@ export const docTreeCacheRunner = {
|
|
|
52
55
|
console.log(`[abracadabra:doc-tree-cache] Untracked: ${docId}`);
|
|
53
56
|
}
|
|
54
57
|
});
|
|
58
|
+
rebuildSlugMap(treeMap);
|
|
55
59
|
};
|
|
56
60
|
treeMap.observe(observer);
|
|
57
61
|
return () => {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side slug map singleton.
|
|
3
|
+
*
|
|
4
|
+
* Maintains bidirectional slug↔docId maps, persists them to Nitro storage,
|
|
5
|
+
* and loads them on startup so slug routes work even before CRDT sync.
|
|
6
|
+
*/
|
|
7
|
+
import { type SlugMap } from '../../utils/slugify.js';
|
|
8
|
+
/**
|
|
9
|
+
* Initialize slug map with a Nitro storage handle.
|
|
10
|
+
* Call early in the service plugin lifecycle.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initSlugMap(storage: any): void;
|
|
13
|
+
/**
|
|
14
|
+
* Load persisted slug map from storage.
|
|
15
|
+
* Call on startup before CRDT connects so slug routes work immediately.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadPersistedSlugMap(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Rebuild the slug map from a Y.Map tree (debounced).
|
|
20
|
+
* Called by the doc-tree-cache runner when the tree changes.
|
|
21
|
+
*/
|
|
22
|
+
export declare function rebuildSlugMap(treeMap: any): void;
|
|
23
|
+
/**
|
|
24
|
+
* Rebuild immediately (used for initial load).
|
|
25
|
+
*/
|
|
26
|
+
export declare function rebuildSlugMapSync(treeMap: any): void;
|
|
27
|
+
/** Resolve a slug path to a docId. Returns null if not found. */
|
|
28
|
+
export declare function getDocIdFromSlug(slug: string): string | null;
|
|
29
|
+
/** Get the slug for a docId. Returns null if not found. */
|
|
30
|
+
export declare function getSlugFromDocId(docId: string): string | null;
|
|
31
|
+
/** Get the full current slug map (for the slugs API endpoint). */
|
|
32
|
+
export declare function getSlugMapData(): SlugMap;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { buildSlugMap } from "../../utils/slugify.js";
|
|
2
|
+
let _slugMap = { slugToId: {}, idToSlug: {} };
|
|
3
|
+
let _storage = null;
|
|
4
|
+
let _rebuildTimer = null;
|
|
5
|
+
const SLUG_STORAGE_KEY = "doc-cache:slug-map";
|
|
6
|
+
const REBUILD_DEBOUNCE_MS = 2e3;
|
|
7
|
+
export function initSlugMap(storage) {
|
|
8
|
+
_storage = storage;
|
|
9
|
+
}
|
|
10
|
+
export async function loadPersistedSlugMap() {
|
|
11
|
+
if (!_storage) return;
|
|
12
|
+
try {
|
|
13
|
+
const stored = await _storage.getItem(SLUG_STORAGE_KEY);
|
|
14
|
+
if (stored && typeof stored === "object" && stored.slugToId && stored.idToSlug) {
|
|
15
|
+
_slugMap = stored;
|
|
16
|
+
console.log(`[abracadabra:slug-map] Loaded ${Object.keys(_slugMap.slugToId).length} slugs from cache`);
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.warn("[abracadabra:slug-map] Failed to load persisted slug map:", e);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function rebuildSlugMap(treeMap) {
|
|
23
|
+
if (_rebuildTimer) clearTimeout(_rebuildTimer);
|
|
24
|
+
_rebuildTimer = setTimeout(() => {
|
|
25
|
+
_rebuildTimer = null;
|
|
26
|
+
rebuildSlugMapSync(treeMap);
|
|
27
|
+
}, REBUILD_DEBOUNCE_MS);
|
|
28
|
+
}
|
|
29
|
+
export function rebuildSlugMapSync(treeMap) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = treeMap.toJSON();
|
|
32
|
+
const entries = Object.entries(raw).map(([id, entry]) => ({
|
|
33
|
+
id,
|
|
34
|
+
label: entry.label ?? "",
|
|
35
|
+
parentId: entry.parentId ?? null,
|
|
36
|
+
order: entry.order ?? 0,
|
|
37
|
+
trashed: entry.trashed ?? false
|
|
38
|
+
}));
|
|
39
|
+
_slugMap = buildSlugMap(entries);
|
|
40
|
+
if (_storage) {
|
|
41
|
+
_storage.setItem(SLUG_STORAGE_KEY, _slugMap).catch((e) => {
|
|
42
|
+
console.warn("[abracadabra:slug-map] Failed to persist:", e);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
console.log(`[abracadabra:slug-map] Rebuilt: ${Object.keys(_slugMap.slugToId).length} slugs`);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.warn("[abracadabra:slug-map] Rebuild failed:", e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function getDocIdFromSlug(slug) {
|
|
51
|
+
return _slugMap.slugToId[slug] ?? null;
|
|
52
|
+
}
|
|
53
|
+
export function getSlugFromDocId(docId) {
|
|
54
|
+
return _slugMap.idToSlug[docId] ?? null;
|
|
55
|
+
}
|
|
56
|
+
export function getSlugMapData() {
|
|
57
|
+
return _slugMap;
|
|
58
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -97,7 +97,30 @@ export interface TagsField {
|
|
|
97
97
|
options?: string[];
|
|
98
98
|
label?: string;
|
|
99
99
|
}
|
|
100
|
-
export
|
|
100
|
+
export interface TimeRangeField {
|
|
101
|
+
type: 'timerange';
|
|
102
|
+
startKey: string;
|
|
103
|
+
endKey: string;
|
|
104
|
+
label?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface TimeField {
|
|
107
|
+
type: 'time';
|
|
108
|
+
key: string;
|
|
109
|
+
label?: string;
|
|
110
|
+
}
|
|
111
|
+
export interface LocationField {
|
|
112
|
+
type: 'location';
|
|
113
|
+
latKey: string;
|
|
114
|
+
lngKey: string;
|
|
115
|
+
label?: string;
|
|
116
|
+
}
|
|
117
|
+
export interface IconField {
|
|
118
|
+
type: 'icon';
|
|
119
|
+
key: string;
|
|
120
|
+
options: string[];
|
|
121
|
+
label?: string;
|
|
122
|
+
}
|
|
123
|
+
export type MetaField = DatetimeRangeField | DateRangeField | TimeRangeField | DatetimeField | DateField | TimeField | SliderField | ColorPresetField | ColorPickerField | LocationField | IconField | TextareaField | NumberField | ToggleField | SelectField | MultiSelectField | UrlField | RatingField | TagsField;
|
|
101
124
|
export interface DocTypeDefinition {
|
|
102
125
|
key: string;
|
|
103
126
|
label: string;
|
|
@@ -121,6 +144,11 @@ export interface DocTypeDefinition {
|
|
|
121
144
|
/** Metadata fields shown/edited in the document properties panel. */
|
|
122
145
|
metaSchema?: MetaField[];
|
|
123
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Per-geoType meta schemas. Used by AEditor to show the right fields
|
|
149
|
+
* regardless of the parent doc's type (handles nested marker/line/measure).
|
|
150
|
+
*/
|
|
151
|
+
export declare const GEO_TYPE_META_SCHEMAS: Record<string, MetaField[]>;
|
|
124
152
|
export declare const DOC_TYPES: Record<string, DocTypeDefinition>;
|
|
125
153
|
export declare const DEFAULT_DOC_TYPE: DocTypeDefinition;
|
|
126
154
|
/** Resolve a type key to a DocTypeDefinition. Falls back to plugin types, then to 'doc'. */
|