@abraca/nuxt 0.1.1 → 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/AFloatingWindow.vue +1 -1
- 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/AWindowLayer.vue +1 -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
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { shallowRef, ref, watch, watchEffect, computed, nextTick } from "vue";
|
|
3
|
+
import { Fragment } from "@tiptap/pm/model";
|
|
4
|
+
import { resolveDocType, GEO_TYPE_META_SCHEMAS } from "../utils/docTypes";
|
|
5
|
+
import { schemaFieldToAttrs, userFieldToAttrs } from "../extensions/meta-field";
|
|
3
6
|
import { useEditor } from "../composables/useEditor";
|
|
4
7
|
import { useEditorToolbar } from "../composables/useEditorToolbar";
|
|
5
8
|
import { useEditorSuggestions } from "../composables/useEditorSuggestions";
|
|
@@ -13,7 +16,9 @@ const props = defineProps({
|
|
|
13
16
|
showToolbar: { type: Boolean, required: false, default: true },
|
|
14
17
|
showSuggestionMenu: { type: Boolean, required: false, default: true },
|
|
15
18
|
showDragHandle: { type: Boolean, required: false, default: true },
|
|
16
|
-
docMeta: { type: Object, required: false }
|
|
19
|
+
docMeta: { type: Object, required: false },
|
|
20
|
+
parentType: { type: String, required: false },
|
|
21
|
+
metaSchema: { type: Array, required: false }
|
|
17
22
|
});
|
|
18
23
|
const emit = defineEmits(["ready", "update", "rename", "updateMeta"]);
|
|
19
24
|
const model = defineModel({ type: null });
|
|
@@ -37,7 +42,35 @@ watch(ready, (val) => {
|
|
|
37
42
|
});
|
|
38
43
|
const editorRef = ref(null);
|
|
39
44
|
const { items: toolbarItems } = useEditorToolbar({ docId: props.docId });
|
|
40
|
-
const
|
|
45
|
+
const resolvedMetaSchema = computed(() => {
|
|
46
|
+
if (props.metaSchema) return props.metaSchema;
|
|
47
|
+
const geoType = props.docMeta?.geoType;
|
|
48
|
+
if (geoType && geoType in GEO_TYPE_META_SCHEMAS)
|
|
49
|
+
return GEO_TYPE_META_SCHEMAS[geoType];
|
|
50
|
+
return resolveDocType(props.parentType, registry).metaSchema ?? [];
|
|
51
|
+
});
|
|
52
|
+
const { items: allSuggestionItems, propertiesOnlyItems } = useEditorSuggestions({ docId: props.docId });
|
|
53
|
+
const isInDocumentMeta = ref(false);
|
|
54
|
+
watchEffect((onCleanup) => {
|
|
55
|
+
const ed = editorRef.value?.editor;
|
|
56
|
+
if (!ed || !ready.value) return;
|
|
57
|
+
const check = () => {
|
|
58
|
+
const { $head } = ed.state.selection;
|
|
59
|
+
let inMeta = false;
|
|
60
|
+
for (let d = $head.depth; d >= 0; d--) {
|
|
61
|
+
if ($head.node(d).type.name === "documentMeta") {
|
|
62
|
+
inMeta = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
isInDocumentMeta.value = inMeta;
|
|
67
|
+
};
|
|
68
|
+
ed.on("selectionUpdate", check);
|
|
69
|
+
onCleanup(() => ed.off("selectionUpdate", check));
|
|
70
|
+
});
|
|
71
|
+
const suggestionItems = computed(
|
|
72
|
+
() => isInDocumentMeta.value ? propertiesOnlyItems.value : allSuggestionItems.value
|
|
73
|
+
);
|
|
41
74
|
const dragHandle = useEditorDragHandle();
|
|
42
75
|
watchEffect(() => {
|
|
43
76
|
const isReady = ready.value;
|
|
@@ -49,6 +82,57 @@ watchEffect(() => {
|
|
|
49
82
|
storage.pageMeta = docMeta ?? null;
|
|
50
83
|
storage.updateMeta = (patch) => emit("updateMeta", patch);
|
|
51
84
|
});
|
|
85
|
+
let metaInitDone = false;
|
|
86
|
+
function handleMetaUpdate(patch) {
|
|
87
|
+
emit("updateMeta", patch);
|
|
88
|
+
}
|
|
89
|
+
function initDocumentMeta(ed) {
|
|
90
|
+
if (metaInitDone) return;
|
|
91
|
+
const doc2 = ed.state.doc;
|
|
92
|
+
let metaPos = -1;
|
|
93
|
+
let metaNode = null;
|
|
94
|
+
doc2.forEach((node, offset) => {
|
|
95
|
+
if (node.type.name === "documentMeta") {
|
|
96
|
+
metaPos = offset;
|
|
97
|
+
metaNode = node;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (metaPos === -1 || !metaNode) return;
|
|
101
|
+
if (metaNode.childCount > 0) {
|
|
102
|
+
metaInitDone = true;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const docMeta = props.docMeta;
|
|
106
|
+
if (!docMeta) return;
|
|
107
|
+
const schema = resolvedMetaSchema.value;
|
|
108
|
+
const userFields = docMeta._metaFields ?? [];
|
|
109
|
+
const wasAlreadyInitialized = !!docMeta._metaInitialized;
|
|
110
|
+
if (wasAlreadyInitialized && !schema.length) {
|
|
111
|
+
metaInitDone = true;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!wasAlreadyInitialized) {
|
|
115
|
+
handleMetaUpdate({ _metaInitialized: true });
|
|
116
|
+
}
|
|
117
|
+
if (!schema.length && !userFields.length) {
|
|
118
|
+
metaInitDone = true;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const metaFieldType = ed.schema.nodes.metaField;
|
|
122
|
+
if (!metaFieldType) return;
|
|
123
|
+
const nodesToInsert = wasAlreadyInitialized ? schema.map((f) => metaFieldType.create(schemaFieldToAttrs(f))) : [
|
|
124
|
+
...schema.map((f) => metaFieldType.create(schemaFieldToAttrs(f))),
|
|
125
|
+
...userFields.map((f) => metaFieldType.create(userFieldToAttrs(f)))
|
|
126
|
+
];
|
|
127
|
+
const { tr } = ed.state;
|
|
128
|
+
const insertPos = metaPos + 1;
|
|
129
|
+
tr.insert(insertPos, Fragment.fromArray(nodesToInsert));
|
|
130
|
+
ed.view.dispatch(tr);
|
|
131
|
+
if (!wasAlreadyInitialized && userFields.length) {
|
|
132
|
+
handleMetaUpdate({ _metaFields: void 0 });
|
|
133
|
+
}
|
|
134
|
+
metaInitDone = true;
|
|
135
|
+
}
|
|
52
136
|
let syncingFromEditor = false;
|
|
53
137
|
const _lastEmittedHeader = ref("");
|
|
54
138
|
function getHeaderText(ed) {
|
|
@@ -106,8 +190,12 @@ watchEffect((onCleanup) => {
|
|
|
106
190
|
}
|
|
107
191
|
function onUpdate() {
|
|
108
192
|
syncHeaderToTree(ed);
|
|
193
|
+
initDocumentMeta(ed);
|
|
109
194
|
}
|
|
110
|
-
nextTick(() =>
|
|
195
|
+
nextTick(() => {
|
|
196
|
+
doInitialSync();
|
|
197
|
+
initDocumentMeta(ed);
|
|
198
|
+
});
|
|
111
199
|
ed.on("update", onUpdate);
|
|
112
200
|
onCleanup(() => ed.off("update", onUpdate));
|
|
113
201
|
});
|
|
@@ -141,7 +229,10 @@ function onPlusClick(e, onClick) {
|
|
|
141
229
|
<template>
|
|
142
230
|
<ClientOnly>
|
|
143
231
|
<!-- Loading skeleton while extensions and child provider are loading -->
|
|
144
|
-
<div
|
|
232
|
+
<div
|
|
233
|
+
v-if="!extensions.length"
|
|
234
|
+
class="p-6 space-y-3"
|
|
235
|
+
>
|
|
145
236
|
<div class="h-8 w-64 animate-pulse rounded bg-elevated" />
|
|
146
237
|
<div class="h-4 w-full animate-pulse rounded bg-elevated" />
|
|
147
238
|
<div class="h-4 w-3/4 animate-pulse rounded bg-elevated" />
|
|
@@ -151,6 +242,7 @@ function onPlusClick(e, onClick) {
|
|
|
151
242
|
<UEditor
|
|
152
243
|
v-else
|
|
153
244
|
ref="editorRef"
|
|
245
|
+
v-slot="{ editor }"
|
|
154
246
|
v-model="model"
|
|
155
247
|
:content-type="contentType"
|
|
156
248
|
:editable="editable"
|
|
@@ -158,11 +250,14 @@ function onPlusClick(e, onClick) {
|
|
|
158
250
|
:starter-kit="{ undoRedo: false, codeBlock: false, document: false }"
|
|
159
251
|
:handlers="editorHandlers"
|
|
160
252
|
:placeholder="placeholder"
|
|
161
|
-
v-slot="{ editor }"
|
|
162
253
|
@update:model-value="emit('update', $event)"
|
|
163
254
|
>
|
|
164
255
|
<!-- Default slot: app can override entire editor content -->
|
|
165
|
-
<slot
|
|
256
|
+
<slot
|
|
257
|
+
:editor="editor"
|
|
258
|
+
:connected-users="connectedUsers"
|
|
259
|
+
:ready="ready"
|
|
260
|
+
>
|
|
166
261
|
<!-- ── Bubble toolbar (appears when text is selected) ─────────────── -->
|
|
167
262
|
<UEditorToolbar
|
|
168
263
|
v-if="showToolbar"
|
|
@@ -182,8 +277,8 @@ function onPlusClick(e, onClick) {
|
|
|
182
277
|
<!-- ── Drag handle — plus button + grip dropdown ───────────────────── -->
|
|
183
278
|
<UEditorDragHandle
|
|
184
279
|
v-if="showDragHandle"
|
|
185
|
-
:editor="editor"
|
|
186
280
|
v-slot="{ ui, onClick }"
|
|
281
|
+
:editor="editor"
|
|
187
282
|
@node-change="dragHandle.onNodeChange"
|
|
188
283
|
>
|
|
189
284
|
<!-- Plus: insert block via slash menu -->
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DocPageMeta } from '../types.js';
|
|
2
|
+
import type { MetaField } from '../utils/docTypes.js';
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
/** Y.Doc ID to load as a child document */
|
|
4
5
|
docId: string;
|
|
@@ -18,6 +19,10 @@ type __VLS_Props = {
|
|
|
18
19
|
showDragHandle?: boolean;
|
|
19
20
|
/** Current document metadata from the doc-tree — written into MetaField storage */
|
|
20
21
|
docMeta?: DocPageMeta;
|
|
22
|
+
/** Parent document's type key — used to resolve metaSchema for auto-inserting chips */
|
|
23
|
+
parentType?: string;
|
|
24
|
+
/** Override metaSchema directly instead of resolving from parentType */
|
|
25
|
+
metaSchema?: MetaField[];
|
|
21
26
|
};
|
|
22
27
|
type __VLS_ModelProps = {
|
|
23
28
|
modelValue?: any;
|
|
@@ -39,15 +44,15 @@ type __VLS_Slots = {} & {
|
|
|
39
44
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
40
45
|
"update:modelValue": (value: any) => any;
|
|
41
46
|
rename: (label: string) => any;
|
|
42
|
-
ready: () => any;
|
|
43
|
-
update: (content: any) => any;
|
|
44
47
|
updateMeta: (patch: Partial<DocPageMeta>) => any;
|
|
48
|
+
update: (content: any) => any;
|
|
49
|
+
ready: () => any;
|
|
45
50
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
46
51
|
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
47
52
|
onRename?: ((label: string) => any) | undefined;
|
|
48
|
-
onReady?: (() => any) | undefined;
|
|
49
|
-
onUpdate?: ((content: any) => any) | undefined;
|
|
50
53
|
onUpdateMeta?: ((patch: Partial<DocPageMeta>) => any) | undefined;
|
|
54
|
+
onUpdate?: ((content: any) => any) | undefined;
|
|
55
|
+
onReady?: (() => any) | undefined;
|
|
51
56
|
}>, {
|
|
52
57
|
contentType: "json" | "html" | "markdown";
|
|
53
58
|
editable: boolean;
|
|
@@ -19,7 +19,7 @@ const {
|
|
|
19
19
|
moveWindow,
|
|
20
20
|
resizeWindow
|
|
21
21
|
} = useWindowManager();
|
|
22
|
-
const win = computed(() => windows.
|
|
22
|
+
const win = computed(() => windows.get(props.windowId));
|
|
23
23
|
const isAnimatingOut = ref(false);
|
|
24
24
|
const animStyle = ref({});
|
|
25
25
|
watch(() => win.value?.minimized, (minimized, wasMinimized) => {
|
|
@@ -207,7 +207,10 @@ function onOpen(v) {
|
|
|
207
207
|
</script>
|
|
208
208
|
|
|
209
209
|
<template>
|
|
210
|
-
<UPopover
|
|
210
|
+
<UPopover
|
|
211
|
+
v-model:open="open"
|
|
212
|
+
@update:open="onOpen"
|
|
213
|
+
>
|
|
211
214
|
<button
|
|
212
215
|
type="button"
|
|
213
216
|
class="flex items-center gap-2 h-8 px-2 rounded-md border border-default hover:bg-elevated/60 transition-colors"
|
|
@@ -241,7 +244,10 @@ function onOpen(v) {
|
|
|
241
244
|
:title="name"
|
|
242
245
|
@click="select(name)"
|
|
243
246
|
>
|
|
244
|
-
<UIcon
|
|
247
|
+
<UIcon
|
|
248
|
+
:name="`i-lucide-${name}`"
|
|
249
|
+
class="size-4"
|
|
250
|
+
/>
|
|
245
251
|
</button>
|
|
246
252
|
</div>
|
|
247
253
|
<button
|
|
@@ -23,17 +23,16 @@ const tree = computed(() => {
|
|
|
23
23
|
if (!props.nodeId) return null;
|
|
24
24
|
return useChildTree(rootDoc, props.nodeId);
|
|
25
25
|
});
|
|
26
|
+
const treeMap = useSyncedMap(rootDoc, "doc-tree");
|
|
26
27
|
const entry = computed(() => {
|
|
27
28
|
if (!props.nodeId) return null;
|
|
28
|
-
|
|
29
|
-
return treeMap?.get(props.nodeId) ?? null;
|
|
29
|
+
return treeMap.data[props.nodeId] ?? null;
|
|
30
30
|
});
|
|
31
31
|
const meta = computed(() => entry.value?.meta ?? {});
|
|
32
32
|
const currentType = computed(() => entry.value?.type ?? "doc");
|
|
33
33
|
function patchMeta(patch) {
|
|
34
|
-
if (!props.nodeId
|
|
35
|
-
const
|
|
36
|
-
const e = treeMap.get(props.nodeId);
|
|
34
|
+
if (!props.nodeId) return;
|
|
35
|
+
const e = treeMap.data[props.nodeId];
|
|
37
36
|
if (e) {
|
|
38
37
|
treeMap.set(props.nodeId, {
|
|
39
38
|
...e,
|
|
@@ -43,32 +42,11 @@ function patchMeta(patch) {
|
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
function patchType(type) {
|
|
46
|
-
if (!props.nodeId
|
|
47
|
-
const
|
|
48
|
-
const e = treeMap.get(props.nodeId);
|
|
45
|
+
if (!props.nodeId) return;
|
|
46
|
+
const e = treeMap.data[props.nodeId];
|
|
49
47
|
if (e) treeMap.set(props.nodeId, { ...e, type, updatedAt: Date.now() });
|
|
50
48
|
}
|
|
51
49
|
const activeTab = ref("editor");
|
|
52
|
-
const tabs = computed(() => {
|
|
53
|
-
const base = [
|
|
54
|
-
{ key: "editor", label: locale.value.editorTab, icon: "i-lucide-file-text" },
|
|
55
|
-
{ key: "properties", label: locale.value.propertiesTab, icon: "i-lucide-sliders-horizontal" }
|
|
56
|
-
];
|
|
57
|
-
const pluginSlots2 = registry.getAllNodePanelSlots().filter((slot) => {
|
|
58
|
-
if (!props.nodeId || !tree.value) return false;
|
|
59
|
-
const e = tree.value.entries.value.find((en) => en.id === props.nodeId);
|
|
60
|
-
if (!e) return false;
|
|
61
|
-
return slot.when({
|
|
62
|
-
childId: props.nodeId,
|
|
63
|
-
childDoc: props.childProvider?.document ?? null,
|
|
64
|
-
parentDocId: props.nodeId,
|
|
65
|
-
parentType: props.docType,
|
|
66
|
-
meta: meta.value,
|
|
67
|
-
tree: tree.value
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
return [...base, ...pluginSlots2.map((s) => ({ key: s.id, label: s.label, icon: s.icon }))];
|
|
71
|
-
});
|
|
72
50
|
const pluginSlots = computed(() => {
|
|
73
51
|
return registry.getAllNodePanelSlots().filter((slot) => {
|
|
74
52
|
if (!props.nodeId || !tree.value) return false;
|
|
@@ -129,19 +107,19 @@ function setCustomFieldValue(field, value) {
|
|
|
129
107
|
patchMeta({ [field.key]: value });
|
|
130
108
|
}
|
|
131
109
|
const statusOptions = [
|
|
132
|
-
{ label: "None", value: "" },
|
|
110
|
+
{ label: "None", value: "none" },
|
|
133
111
|
{ label: "Todo", value: "todo" },
|
|
134
112
|
{ label: "In progress", value: "in-progress" },
|
|
135
113
|
{ label: "Done", value: "done" },
|
|
136
114
|
{ label: "Archived", value: "archived" }
|
|
137
115
|
];
|
|
138
116
|
const priorityOptions = [
|
|
139
|
-
{ label: "None", value:
|
|
140
|
-
{ label: "1 \u2014 Low", value: 1 },
|
|
141
|
-
{ label: "2", value: 2 },
|
|
142
|
-
{ label: "3 \u2014 Medium", value: 3 },
|
|
143
|
-
{ label: "4", value: 4 },
|
|
144
|
-
{ label: "5 \u2014 High", value: 5 }
|
|
117
|
+
{ label: "None", value: "none" },
|
|
118
|
+
{ label: "1 \u2014 Low", value: "1" },
|
|
119
|
+
{ label: "2", value: "2" },
|
|
120
|
+
{ label: "3 \u2014 Medium", value: "3" },
|
|
121
|
+
{ label: "4", value: "4" },
|
|
122
|
+
{ label: "5 \u2014 High", value: "5" }
|
|
145
123
|
];
|
|
146
124
|
const addFieldMenuItems = computed(
|
|
147
125
|
() => META_FIELD_DEFINITIONS.map((def) => ({
|
|
@@ -157,43 +135,85 @@ const addFieldMenuItems = computed(
|
|
|
157
135
|
v-model:open="open"
|
|
158
136
|
side="right"
|
|
159
137
|
:title="nodeLabel || 'Document'"
|
|
160
|
-
:ui="{ width: 'max-w-
|
|
138
|
+
:ui="{ width: 'sm:max-w-2xl' }"
|
|
161
139
|
@update:open="(v) => !v && emit('close')"
|
|
162
140
|
>
|
|
163
141
|
<template #header>
|
|
164
|
-
<div class="flex items-center gap-2 min-w-0">
|
|
142
|
+
<div class="flex items-center gap-2 min-w-0 flex-1">
|
|
165
143
|
<UIcon
|
|
166
144
|
:name="meta.icon ?? 'i-lucide-file-text'"
|
|
167
145
|
class="size-4 shrink-0"
|
|
168
146
|
:style="meta.color ? `color: ${meta.color}` : ''"
|
|
169
147
|
:class="meta.color ? '' : 'text-muted'"
|
|
170
148
|
/>
|
|
171
|
-
<span class="font-medium truncate">{{ nodeLabel || "Document" }}</span>
|
|
149
|
+
<span class="font-medium truncate flex-1">{{ nodeLabel || "Document" }}</span>
|
|
150
|
+
<UTooltip
|
|
151
|
+
text="Open as full page"
|
|
152
|
+
:content="{ side: 'bottom' }"
|
|
153
|
+
>
|
|
154
|
+
<UButton
|
|
155
|
+
icon="i-lucide-expand"
|
|
156
|
+
size="xs"
|
|
157
|
+
variant="ghost"
|
|
158
|
+
color="neutral"
|
|
159
|
+
@click="navigateTo(`/app/${nodeId}`)"
|
|
160
|
+
/>
|
|
161
|
+
</UTooltip>
|
|
172
162
|
</div>
|
|
173
163
|
</template>
|
|
174
164
|
|
|
175
165
|
<template #body>
|
|
176
|
-
<!--
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
166
|
+
<!-- Tab bar -->
|
|
167
|
+
<div class="flex items-center gap-1 border-b border-(--ui-border) mb-4 -mt-2">
|
|
168
|
+
<UButton
|
|
169
|
+
icon="i-lucide-file-text"
|
|
170
|
+
:label="locale.editorTab"
|
|
171
|
+
size="sm"
|
|
172
|
+
:color="activeTab === 'editor' ? 'primary' : 'neutral'"
|
|
173
|
+
:variant="activeTab === 'editor' ? 'soft' : 'ghost'"
|
|
174
|
+
@click="activeTab = 'editor'"
|
|
175
|
+
/>
|
|
176
|
+
<UButton
|
|
177
|
+
icon="i-lucide-sliders-horizontal"
|
|
178
|
+
:label="locale.propertiesTab"
|
|
179
|
+
size="sm"
|
|
180
|
+
:color="activeTab === 'properties' ? 'primary' : 'neutral'"
|
|
181
|
+
:variant="activeTab === 'properties' ? 'soft' : 'ghost'"
|
|
182
|
+
@click="activeTab = 'properties'"
|
|
183
|
+
/>
|
|
184
|
+
<UButton
|
|
185
|
+
v-for="slot in pluginSlots"
|
|
186
|
+
:key="slot.id"
|
|
187
|
+
:icon="slot.icon"
|
|
188
|
+
:label="slot.label"
|
|
189
|
+
size="sm"
|
|
190
|
+
:color="activeTab === slot.id ? 'primary' : 'neutral'"
|
|
191
|
+
:variant="activeTab === slot.id ? 'soft' : 'ghost'"
|
|
192
|
+
@click="activeTab = slot.id"
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
183
195
|
|
|
184
196
|
<!-- Editor tab -->
|
|
185
|
-
<div
|
|
197
|
+
<div
|
|
198
|
+
v-show="activeTab === 'editor'"
|
|
199
|
+
class="min-h-[60vh]"
|
|
200
|
+
>
|
|
186
201
|
<AEditor
|
|
187
202
|
v-if="nodeId"
|
|
188
203
|
:doc-id="nodeId"
|
|
189
204
|
:child-provider="childProvider"
|
|
205
|
+
:parent-type="docType"
|
|
206
|
+
:doc-meta="meta"
|
|
190
207
|
class="h-full overflow-auto"
|
|
208
|
+
@update-meta="patchMeta"
|
|
191
209
|
/>
|
|
192
210
|
</div>
|
|
193
211
|
|
|
194
212
|
<!-- Properties tab -->
|
|
195
|
-
<div
|
|
196
|
-
|
|
213
|
+
<div
|
|
214
|
+
v-show="activeTab === 'properties'"
|
|
215
|
+
class="space-y-4"
|
|
216
|
+
>
|
|
197
217
|
<!-- Document type -->
|
|
198
218
|
<div class="space-y-1">
|
|
199
219
|
<label class="text-xs font-medium text-muted">{{ locale.docType }}</label>
|
|
@@ -225,23 +245,24 @@ const addFieldMenuItems = computed(
|
|
|
225
245
|
<div class="grid grid-cols-2 gap-3">
|
|
226
246
|
<div class="space-y-1">
|
|
227
247
|
<label class="text-xs font-medium text-muted">{{ locale.status }}</label>
|
|
228
|
-
<
|
|
229
|
-
:model-value="meta.status
|
|
248
|
+
<USelect
|
|
249
|
+
:model-value="meta.status || 'none'"
|
|
230
250
|
:items="statusOptions"
|
|
231
251
|
value-key="value"
|
|
232
|
-
|
|
252
|
+
label-key="label"
|
|
233
253
|
size="sm"
|
|
234
|
-
@update:model-value="patchMeta({ status: $event
|
|
254
|
+
@update:model-value="patchMeta({ status: $event === 'none' ? void 0 : $event })"
|
|
235
255
|
/>
|
|
236
256
|
</div>
|
|
237
257
|
<div class="space-y-1">
|
|
238
258
|
<label class="text-xs font-medium text-muted">{{ locale.priority }}</label>
|
|
239
|
-
<
|
|
240
|
-
:model-value="meta.priority
|
|
259
|
+
<USelect
|
|
260
|
+
:model-value="meta.priority ? String(meta.priority) : 'none'"
|
|
241
261
|
:items="priorityOptions"
|
|
242
262
|
value-key="value"
|
|
263
|
+
label-key="label"
|
|
243
264
|
size="sm"
|
|
244
|
-
@update:model-value="patchMeta({ priority: $event
|
|
265
|
+
@update:model-value="patchMeta({ priority: $event === 'none' ? void 0 : Number($event) })"
|
|
245
266
|
/>
|
|
246
267
|
</div>
|
|
247
268
|
</div>
|
|
@@ -269,7 +290,10 @@ const addFieldMenuItems = computed(
|
|
|
269
290
|
class="ml-1 text-xs text-muted hover:text-default"
|
|
270
291
|
@click="patchMeta({ rating: void 0 })"
|
|
271
292
|
>
|
|
272
|
-
<UIcon
|
|
293
|
+
<UIcon
|
|
294
|
+
name="i-lucide-x"
|
|
295
|
+
class="size-3"
|
|
296
|
+
/>
|
|
273
297
|
</button>
|
|
274
298
|
</div>
|
|
275
299
|
</div>
|
|
@@ -317,7 +341,10 @@ const addFieldMenuItems = computed(
|
|
|
317
341
|
@click="removeTag(tag)"
|
|
318
342
|
>
|
|
319
343
|
{{ tag }}
|
|
320
|
-
<UIcon
|
|
344
|
+
<UIcon
|
|
345
|
+
name="i-lucide-x"
|
|
346
|
+
class="size-3 ml-1"
|
|
347
|
+
/>
|
|
321
348
|
</UBadge>
|
|
322
349
|
</div>
|
|
323
350
|
<UInput
|
|
@@ -330,10 +357,16 @@ const addFieldMenuItems = computed(
|
|
|
330
357
|
</div>
|
|
331
358
|
|
|
332
359
|
<!-- Cover image -->
|
|
333
|
-
<div
|
|
360
|
+
<div
|
|
361
|
+
v-if="meta.coverUploadId || meta.coverDocId"
|
|
362
|
+
class="space-y-1"
|
|
363
|
+
>
|
|
334
364
|
<label class="text-xs font-medium text-muted">{{ locale.cover }}</label>
|
|
335
365
|
<div class="flex items-center gap-2 text-xs text-muted">
|
|
336
|
-
<UIcon
|
|
366
|
+
<UIcon
|
|
367
|
+
name="i-lucide-image"
|
|
368
|
+
class="size-4 shrink-0"
|
|
369
|
+
/>
|
|
337
370
|
<span class="truncate">{{ meta.coverUploadId ?? meta.coverDocId }}</span>
|
|
338
371
|
<UButton
|
|
339
372
|
icon="i-lucide-x"
|
|
@@ -488,7 +521,10 @@ const addFieldMenuItems = computed(
|
|
|
488
521
|
class="cursor-pointer"
|
|
489
522
|
@click="setCustomFieldValue(field, getCustomFieldValue(field).filter((x) => x !== t))"
|
|
490
523
|
>
|
|
491
|
-
{{ t }}<UIcon
|
|
524
|
+
{{ t }}<UIcon
|
|
525
|
+
name="i-lucide-x"
|
|
526
|
+
class="size-3 ml-1"
|
|
527
|
+
/>
|
|
492
528
|
</UBadge>
|
|
493
529
|
</div>
|
|
494
530
|
</template>
|
|
@@ -522,7 +558,10 @@ const addFieldMenuItems = computed(
|
|
|
522
558
|
</div>
|
|
523
559
|
|
|
524
560
|
<!-- Plugin slots -->
|
|
525
|
-
<template
|
|
561
|
+
<template
|
|
562
|
+
v-for="slot in pluginSlots"
|
|
563
|
+
:key="slot.id"
|
|
564
|
+
>
|
|
526
565
|
<div v-show="activeTab === slot.id">
|
|
527
566
|
<component
|
|
528
567
|
:is="slot.component"
|
|
@@ -25,12 +25,24 @@ function openAndMarkFetched() {
|
|
|
25
25
|
:aria-label="`Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ''}`"
|
|
26
26
|
@click="openAndMarkFetched"
|
|
27
27
|
>
|
|
28
|
-
<template
|
|
29
|
-
|
|
28
|
+
<template
|
|
29
|
+
v-if="unreadCount > 0"
|
|
30
|
+
#trailing
|
|
31
|
+
>
|
|
32
|
+
<UChip
|
|
33
|
+
:text="unreadCount > 9 ? '9+' : String(unreadCount)"
|
|
34
|
+
color="error"
|
|
35
|
+
size="xs"
|
|
36
|
+
/>
|
|
30
37
|
</template>
|
|
31
38
|
</UButton>
|
|
32
39
|
|
|
33
|
-
<USlideover
|
|
40
|
+
<USlideover
|
|
41
|
+
v-model:open="isOpen"
|
|
42
|
+
:title="locale.title"
|
|
43
|
+
side="right"
|
|
44
|
+
:ui="{ width: 'max-w-sm' }"
|
|
45
|
+
>
|
|
34
46
|
<template #header>
|
|
35
47
|
<div class="flex items-center justify-between w-full">
|
|
36
48
|
<span class="font-semibold">{{ locale.title }}</span>
|
|
@@ -56,15 +68,30 @@ function openAndMarkFetched() {
|
|
|
56
68
|
]"
|
|
57
69
|
@click="markRead(n.id)"
|
|
58
70
|
>
|
|
59
|
-
<UIcon
|
|
71
|
+
<UIcon
|
|
72
|
+
:name="n.icon ?? 'i-lucide-bell'"
|
|
73
|
+
class="size-4 shrink-0 mt-0.5 text-muted"
|
|
74
|
+
/>
|
|
60
75
|
<div class="flex-1 min-w-0">
|
|
61
|
-
<div class="text-sm font-medium truncate">
|
|
62
|
-
|
|
76
|
+
<div class="text-sm font-medium truncate">
|
|
77
|
+
{{ n.title }}
|
|
78
|
+
</div>
|
|
79
|
+
<div class="text-xs text-muted line-clamp-2">
|
|
80
|
+
{{ n.body }}
|
|
81
|
+
</div>
|
|
63
82
|
</div>
|
|
64
|
-
<UChip
|
|
83
|
+
<UChip
|
|
84
|
+
v-if="!n.read"
|
|
85
|
+
color="primary"
|
|
86
|
+
size="xs"
|
|
87
|
+
class="shrink-0 mt-1"
|
|
88
|
+
/>
|
|
65
89
|
</div>
|
|
66
90
|
|
|
67
|
-
<div
|
|
91
|
+
<div
|
|
92
|
+
v-if="!notifications.length"
|
|
93
|
+
class="text-center py-12 text-muted text-sm"
|
|
94
|
+
>
|
|
68
95
|
{{ locale.empty }}
|
|
69
96
|
</div>
|
|
70
97
|
</div>
|
|
@@ -27,10 +27,21 @@ function userAvatarStyle(user) {
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
|
-
<slot
|
|
30
|
+
<slot
|
|
31
|
+
:users="users"
|
|
32
|
+
:overflow="overflow"
|
|
33
|
+
:current-user="currentUser"
|
|
34
|
+
:avatar-style="userAvatarStyle"
|
|
35
|
+
>
|
|
31
36
|
<!-- Default: render nothing, slot is the API -->
|
|
32
|
-
<template
|
|
33
|
-
|
|
37
|
+
<template
|
|
38
|
+
v-for="user in users"
|
|
39
|
+
:key="user.clientId"
|
|
40
|
+
>
|
|
41
|
+
<slot
|
|
42
|
+
name="user"
|
|
43
|
+
:user="user"
|
|
44
|
+
/>
|
|
34
45
|
</template>
|
|
35
46
|
</slot>
|
|
36
47
|
</template>
|
|
@@ -37,6 +37,12 @@ provide(ABRA_DOC_KEY, {
|
|
|
37
37
|
|
|
38
38
|
<template>
|
|
39
39
|
<component :is="tag">
|
|
40
|
-
<slot
|
|
40
|
+
<slot
|
|
41
|
+
:provider="childProvider"
|
|
42
|
+
:doc="childDoc"
|
|
43
|
+
:synced="synced"
|
|
44
|
+
:doc-id="docId"
|
|
45
|
+
:error="error"
|
|
46
|
+
/>
|
|
41
47
|
</component>
|
|
42
48
|
</template>
|