@cou.sh/nuxt 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +84 -0
  2. package/dist/module.d.mts +49 -0
  3. package/dist/module.json +12 -0
  4. package/dist/module.mjs +126 -0
  5. package/dist/runtime/components/ActivityTimeline.d.vue.ts +17 -0
  6. package/dist/runtime/components/ActivityTimeline.vue +108 -0
  7. package/dist/runtime/components/ActivityTimeline.vue.d.ts +17 -0
  8. package/dist/runtime/components/AuthForm.d.vue.ts +20 -0
  9. package/dist/runtime/components/AuthForm.vue +78 -0
  10. package/dist/runtime/components/AuthForm.vue.d.ts +20 -0
  11. package/dist/runtime/components/CommentThread.d.vue.ts +10 -0
  12. package/dist/runtime/components/CommentThread.vue +149 -0
  13. package/dist/runtime/components/CommentThread.vue.d.ts +10 -0
  14. package/dist/runtime/components/ConnectionStatus.d.vue.ts +13 -0
  15. package/dist/runtime/components/ConnectionStatus.vue +27 -0
  16. package/dist/runtime/components/ConnectionStatus.vue.d.ts +13 -0
  17. package/dist/runtime/components/DocumentBreadcrumb.d.vue.ts +13 -0
  18. package/dist/runtime/components/DocumentBreadcrumb.vue +38 -0
  19. package/dist/runtime/components/DocumentBreadcrumb.vue.d.ts +13 -0
  20. package/dist/runtime/components/DocumentCard.d.vue.ts +30 -0
  21. package/dist/runtime/components/DocumentCard.vue +88 -0
  22. package/dist/runtime/components/DocumentCard.vue.d.ts +30 -0
  23. package/dist/runtime/components/DocumentSearchPalette.d.vue.ts +22 -0
  24. package/dist/runtime/components/DocumentSearchPalette.vue +99 -0
  25. package/dist/runtime/components/DocumentSearchPalette.vue.d.ts +22 -0
  26. package/dist/runtime/components/DocumentTree.d.vue.ts +32 -0
  27. package/dist/runtime/components/DocumentTree.vue +497 -0
  28. package/dist/runtime/components/DocumentTree.vue.d.ts +32 -0
  29. package/dist/runtime/components/Editor.d.vue.ts +11 -0
  30. package/dist/runtime/components/Editor.vue +328 -0
  31. package/dist/runtime/components/Editor.vue.d.ts +11 -0
  32. package/dist/runtime/components/ExportMenu.d.vue.ts +9 -0
  33. package/dist/runtime/components/ExportMenu.vue +55 -0
  34. package/dist/runtime/components/ExportMenu.vue.d.ts +9 -0
  35. package/dist/runtime/components/FileUploader.d.vue.ts +22 -0
  36. package/dist/runtime/components/FileUploader.vue +148 -0
  37. package/dist/runtime/components/FileUploader.vue.d.ts +22 -0
  38. package/dist/runtime/components/MemberManager.d.vue.ts +9 -0
  39. package/dist/runtime/components/MemberManager.vue +153 -0
  40. package/dist/runtime/components/MemberManager.vue.d.ts +9 -0
  41. package/dist/runtime/components/OfflineIndicator.d.vue.ts +9 -0
  42. package/dist/runtime/components/OfflineIndicator.vue +44 -0
  43. package/dist/runtime/components/OfflineIndicator.vue.d.ts +9 -0
  44. package/dist/runtime/components/PermissionGuard.d.vue.ts +25 -0
  45. package/dist/runtime/components/PermissionGuard.vue +16 -0
  46. package/dist/runtime/components/PermissionGuard.vue.d.ts +25 -0
  47. package/dist/runtime/components/RoleBadge.d.vue.ts +11 -0
  48. package/dist/runtime/components/RoleBadge.vue +30 -0
  49. package/dist/runtime/components/RoleBadge.vue.d.ts +11 -0
  50. package/dist/runtime/components/ShareDialog.d.vue.ts +15 -0
  51. package/dist/runtime/components/ShareDialog.vue +119 -0
  52. package/dist/runtime/components/ShareDialog.vue.d.ts +15 -0
  53. package/dist/runtime/components/SnapshotTimeline.d.vue.ts +15 -0
  54. package/dist/runtime/components/SnapshotTimeline.vue +118 -0
  55. package/dist/runtime/components/SnapshotTimeline.vue.d.ts +15 -0
  56. package/dist/runtime/components/SpaceSettings.d.vue.ts +7 -0
  57. package/dist/runtime/components/SpaceSettings.vue +105 -0
  58. package/dist/runtime/components/SpaceSettings.vue.d.ts +7 -0
  59. package/dist/runtime/components/SpaceSwitcher.d.vue.ts +18 -0
  60. package/dist/runtime/components/SpaceSwitcher.vue +104 -0
  61. package/dist/runtime/components/SpaceSwitcher.vue.d.ts +18 -0
  62. package/dist/runtime/components/TrashBin.d.vue.ts +7 -0
  63. package/dist/runtime/components/TrashBin.vue +159 -0
  64. package/dist/runtime/components/TrashBin.vue.d.ts +7 -0
  65. package/dist/runtime/components/UserMenu.d.vue.ts +13 -0
  66. package/dist/runtime/components/UserMenu.vue +50 -0
  67. package/dist/runtime/components/UserMenu.vue.d.ts +13 -0
  68. package/dist/runtime/components/UserPresence.d.vue.ts +13 -0
  69. package/dist/runtime/components/UserPresence.vue +34 -0
  70. package/dist/runtime/components/UserPresence.vue.d.ts +13 -0
  71. package/dist/runtime/components/editor/CollaborationUsers.d.vue.ts +7 -0
  72. package/dist/runtime/components/editor/CollaborationUsers.vue +31 -0
  73. package/dist/runtime/components/editor/CollaborationUsers.vue.d.ts +7 -0
  74. package/dist/runtime/components/editor/CompletionExtension.d.ts +43 -0
  75. package/dist/runtime/components/editor/CompletionExtension.js +133 -0
  76. package/dist/runtime/components/editor/ImageUploadExtension.d.ts +10 -0
  77. package/dist/runtime/components/editor/ImageUploadExtension.js +31 -0
  78. package/dist/runtime/components/editor/ImageUploadNode.d.vue.ts +4 -0
  79. package/dist/runtime/components/editor/ImageUploadNode.vue +59 -0
  80. package/dist/runtime/components/editor/ImageUploadNode.vue.d.ts +4 -0
  81. package/dist/runtime/components/editor/LinkPopover.d.vue.ts +8 -0
  82. package/dist/runtime/components/editor/LinkPopover.vue +131 -0
  83. package/dist/runtime/components/editor/LinkPopover.vue.d.ts +8 -0
  84. package/dist/runtime/components/widgets/ButtonWidget.d.vue.ts +4 -0
  85. package/dist/runtime/components/widgets/ButtonWidget.vue +32 -0
  86. package/dist/runtime/components/widgets/ButtonWidget.vue.d.ts +4 -0
  87. package/dist/runtime/components/widgets/CalendarWidget.d.vue.ts +4 -0
  88. package/dist/runtime/components/widgets/CalendarWidget.vue +36 -0
  89. package/dist/runtime/components/widgets/CalendarWidget.vue.d.ts +4 -0
  90. package/dist/runtime/components/widgets/CheckboxWidget.d.vue.ts +4 -0
  91. package/dist/runtime/components/widgets/CheckboxWidget.vue +32 -0
  92. package/dist/runtime/components/widgets/CheckboxWidget.vue.d.ts +4 -0
  93. package/dist/runtime/components/widgets/ColorPickerWidget.d.vue.ts +4 -0
  94. package/dist/runtime/components/widgets/ColorPickerWidget.vue +30 -0
  95. package/dist/runtime/components/widgets/ColorPickerWidget.vue.d.ts +4 -0
  96. package/dist/runtime/components/widgets/IconWidget.d.vue.ts +4 -0
  97. package/dist/runtime/components/widgets/IconWidget.vue +102 -0
  98. package/dist/runtime/components/widgets/IconWidget.vue.d.ts +4 -0
  99. package/dist/runtime/components/widgets/InputDateWidget.d.vue.ts +4 -0
  100. package/dist/runtime/components/widgets/InputDateWidget.vue +38 -0
  101. package/dist/runtime/components/widgets/InputDateWidget.vue.d.ts +4 -0
  102. package/dist/runtime/components/widgets/ScrollAreaWidget.d.vue.ts +4 -0
  103. package/dist/runtime/components/widgets/ScrollAreaWidget.vue +41 -0
  104. package/dist/runtime/components/widgets/ScrollAreaWidget.vue.d.ts +4 -0
  105. package/dist/runtime/components/widgets/SliderWidget.d.vue.ts +4 -0
  106. package/dist/runtime/components/widgets/SliderWidget.vue +42 -0
  107. package/dist/runtime/components/widgets/SliderWidget.vue.d.ts +4 -0
  108. package/dist/runtime/components/widgets/SwitchWidget.d.vue.ts +4 -0
  109. package/dist/runtime/components/widgets/SwitchWidget.vue +35 -0
  110. package/dist/runtime/components/widgets/SwitchWidget.vue.d.ts +4 -0
  111. package/dist/runtime/components/widgets/TabPanelWidget.d.vue.ts +4 -0
  112. package/dist/runtime/components/widgets/TabPanelWidget.vue +22 -0
  113. package/dist/runtime/components/widgets/TabPanelWidget.vue.d.ts +4 -0
  114. package/dist/runtime/components/widgets/TabsWidget.d.vue.ts +4 -0
  115. package/dist/runtime/components/widgets/TabsWidget.vue +109 -0
  116. package/dist/runtime/components/widgets/TabsWidget.vue.d.ts +4 -0
  117. package/dist/runtime/components/widgets/index.d.ts +13 -0
  118. package/dist/runtime/components/widgets/index.js +278 -0
  119. package/dist/runtime/composables/index.d.ts +14 -0
  120. package/dist/runtime/composables/index.js +13 -0
  121. package/dist/runtime/composables/useCollabStatus.d.ts +10 -0
  122. package/dist/runtime/composables/useCollabStatus.js +23 -0
  123. package/dist/runtime/composables/useDocumentPermissions.d.ts +11 -0
  124. package/dist/runtime/composables/useDocumentPermissions.js +44 -0
  125. package/dist/runtime/composables/useEditorCollaboration.d.ts +25 -0
  126. package/dist/runtime/composables/useEditorCollaboration.js +141 -0
  127. package/dist/runtime/composables/useEditorCompletion.d.ts +60 -0
  128. package/dist/runtime/composables/useEditorCompletion.js +42 -0
  129. package/dist/runtime/composables/useEditorDragHandle.d.ts +10 -0
  130. package/dist/runtime/composables/useEditorDragHandle.js +130 -0
  131. package/dist/runtime/composables/useEditorEmojis.d.ts +4 -0
  132. package/dist/runtime/composables/useEditorEmojis.js +9 -0
  133. package/dist/runtime/composables/useEditorMentions.d.ts +4 -0
  134. package/dist/runtime/composables/useEditorMentions.js +27 -0
  135. package/dist/runtime/composables/useEditorSuggestions.d.ts +149 -0
  136. package/dist/runtime/composables/useEditorSuggestions.js +114 -0
  137. package/dist/runtime/composables/useEditorToolbar.d.ts +12 -0
  138. package/dist/runtime/composables/useEditorToolbar.js +194 -0
  139. package/dist/runtime/composables/useLucideIcons.d.ts +4 -0
  140. package/dist/runtime/composables/useLucideIcons.js +16 -0
  141. package/dist/runtime/composables/useOnlineStatus.d.ts +6 -0
  142. package/dist/runtime/composables/useOnlineStatus.js +44 -0
  143. package/dist/runtime/composables/useSpaces.d.ts +36 -0
  144. package/dist/runtime/composables/useSpaces.js +29 -0
  145. package/dist/runtime/middleware/collab-auth.d.ts +2 -0
  146. package/dist/runtime/middleware/collab-auth.js +18 -0
  147. package/dist/runtime/plugin.d.ts +7 -0
  148. package/dist/runtime/plugin.js +39 -0
  149. package/dist/runtime/server/tsconfig.json +3 -0
  150. package/dist/types.d.mts +3 -0
  151. package/package.json +66 -0
@@ -0,0 +1,497 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ spaceId: { type: String, required: false },
4
+ selectedId: { type: String, required: false },
5
+ editable: { type: Boolean, required: false, default: true },
6
+ multiSpace: { type: Boolean, required: false, default: false }
7
+ });
8
+ const emit = defineEmits(["select", "selectSpace", "move", "create", "delete"]);
9
+ const client = useCollabClient();
10
+ const toast = useToast();
11
+ const spaces = ref([]);
12
+ const spacesLoading = ref(false);
13
+ const rawTree = ref([]);
14
+ const loading = ref(true);
15
+ const nodeMap = /* @__PURE__ */ new Map();
16
+ const docSpaceMap = /* @__PURE__ */ new Map();
17
+ function buildNodeMap(nodes, spaceId) {
18
+ for (const node of nodes) {
19
+ nodeMap.set(node.id, node);
20
+ if (spaceId) docSpaceMap.set(node.id, spaceId);
21
+ if (node.children?.length) buildNodeMap(node.children, spaceId);
22
+ }
23
+ }
24
+ const expandedIds = ref(/* @__PURE__ */ new Set());
25
+ function toggleExpand(id) {
26
+ if (expandedIds.value.has(id)) {
27
+ expandedIds.value.delete(id);
28
+ } else {
29
+ expandedIds.value.add(id);
30
+ }
31
+ expandedIds.value = new Set(expandedIds.value);
32
+ }
33
+ async function loadSpaces() {
34
+ spacesLoading.value = true;
35
+ try {
36
+ const result = await client.spaces.list();
37
+ const list = Array.isArray(result) ? result : result.data ?? [];
38
+ spaces.value = list.map((s) => ({
39
+ id: s.id,
40
+ name: s.name || s.slug || s.id,
41
+ tree: [],
42
+ loaded: false,
43
+ loading: false
44
+ }));
45
+ const first = spaces.value[0];
46
+ if (first) {
47
+ const firstId = `space:${first.id}`;
48
+ expandedIds.value.add(firstId);
49
+ expandedIds.value = new Set(expandedIds.value);
50
+ await loadSpaceTree(first);
51
+ }
52
+ } catch {
53
+ toast.add({ title: "Failed to load spaces", color: "error" });
54
+ } finally {
55
+ spacesLoading.value = false;
56
+ }
57
+ }
58
+ async function loadSpaceTree(space) {
59
+ if (space.loaded || space.loading) return;
60
+ space.loading = true;
61
+ try {
62
+ let autoExpand = function(nodes) {
63
+ for (const node of nodes) {
64
+ if (node.depth < 2 && node.children?.length) {
65
+ expandedIds.value.add(node.id);
66
+ }
67
+ if (node.children?.length) autoExpand(node.children);
68
+ }
69
+ };
70
+ const tree = await client.documents.tree(space.id);
71
+ space.tree = tree;
72
+ space.loaded = true;
73
+ buildNodeMap(tree, space.id);
74
+ autoExpand(tree);
75
+ expandedIds.value = new Set(expandedIds.value);
76
+ } catch {
77
+ toast.add({ title: `Failed to load documents for ${space.name}`, color: "error" });
78
+ } finally {
79
+ space.loading = false;
80
+ }
81
+ }
82
+ async function toggleSpace(space) {
83
+ const key = `space:${space.id}`;
84
+ toggleExpand(key);
85
+ if (expandedIds.value.has(key) && !space.loaded) {
86
+ await loadSpaceTree(space);
87
+ }
88
+ emit("selectSpace", space.id);
89
+ }
90
+ async function loadTree() {
91
+ if (!props.spaceId) return;
92
+ loading.value = true;
93
+ try {
94
+ rawTree.value = await client.documents.tree(props.spaceId);
95
+ nodeMap.clear();
96
+ buildNodeMap(rawTree.value);
97
+ } finally {
98
+ loading.value = false;
99
+ }
100
+ }
101
+ function flattenNodes(nodes, spaceId, depthOffset) {
102
+ const result = [];
103
+ function walk(nodes2) {
104
+ for (const node of nodes2) {
105
+ const hasChildren = !!node.children?.length;
106
+ const expanded = expandedIds.value.has(node.id);
107
+ result.push({
108
+ id: node.id,
109
+ name: node.name || "Untitled",
110
+ depth: node.depth + depthOffset,
111
+ hasChildren,
112
+ expanded,
113
+ type: "document",
114
+ spaceId
115
+ });
116
+ if (hasChildren && expanded) {
117
+ walk(node.children);
118
+ }
119
+ }
120
+ }
121
+ walk(nodes);
122
+ return result;
123
+ }
124
+ const multiSpaceFlatItems = computed(() => {
125
+ const result = [];
126
+ for (const space of spaces.value) {
127
+ const spaceKey = `space:${space.id}`;
128
+ const expanded = expandedIds.value.has(spaceKey);
129
+ const hasChildren = space.tree.length > 0 || !space.loaded;
130
+ result.push({
131
+ id: spaceKey,
132
+ name: space.name,
133
+ depth: 0,
134
+ hasChildren,
135
+ expanded,
136
+ type: "space",
137
+ spaceId: space.id
138
+ });
139
+ if (expanded && space.loaded) {
140
+ result.push(...flattenNodes(space.tree, space.id, 1));
141
+ }
142
+ }
143
+ return result;
144
+ });
145
+ const singleSpaceFlatItems = computed(() => {
146
+ if (!props.spaceId) return [];
147
+ return flattenNodes(rawTree.value, props.spaceId, 0);
148
+ });
149
+ const flatItems = computed(
150
+ () => props.multiSpace ? multiSpaceFlatItems.value : singleSpaceFlatItems.value
151
+ );
152
+ watch(rawTree, () => {
153
+ function autoExpand(nodes) {
154
+ for (const node of nodes) {
155
+ if (node.depth < 2 && node.children?.length) {
156
+ expandedIds.value.add(node.id);
157
+ }
158
+ if (node.children?.length) autoExpand(node.children);
159
+ }
160
+ }
161
+ autoExpand(rawTree.value);
162
+ expandedIds.value = new Set(expandedIds.value);
163
+ }, { immediate: true });
164
+ const contextDocId = ref(null);
165
+ const contextSpaceId = ref(null);
166
+ const contextMenuItems = computed(() => {
167
+ if (!props.editable) return [];
168
+ return [
169
+ [
170
+ {
171
+ label: "New child document",
172
+ icon: "i-lucide-file-plus",
173
+ onSelect: () => createChild(contextDocId.value)
174
+ },
175
+ {
176
+ label: "Rename",
177
+ icon: "i-lucide-pencil",
178
+ onSelect: () => startRename(contextDocId.value)
179
+ }
180
+ ],
181
+ [
182
+ {
183
+ label: "Delete",
184
+ icon: "i-lucide-trash-2",
185
+ color: "error",
186
+ onSelect: () => deleteDoc(contextDocId.value)
187
+ }
188
+ ]
189
+ ];
190
+ });
191
+ function resolveSpaceId(docId) {
192
+ if (props.multiSpace) {
193
+ if (docId) return docSpaceMap.get(docId);
194
+ return contextSpaceId.value ?? void 0;
195
+ }
196
+ return props.spaceId;
197
+ }
198
+ async function createChild(parentId) {
199
+ const sid = resolveSpaceId(parentId);
200
+ if (!sid) return;
201
+ try {
202
+ const doc = await client.documents.create(sid, {
203
+ name: "Untitled",
204
+ parentId: parentId ?? void 0
205
+ });
206
+ emit("create", doc);
207
+ if (props.multiSpace) {
208
+ const space = spaces.value.find((s) => s.id === sid);
209
+ if (space) {
210
+ space.loaded = false;
211
+ await loadSpaceTree(space);
212
+ }
213
+ } else {
214
+ await loadTree();
215
+ }
216
+ } catch {
217
+ toast.add({ title: "Failed to create document", color: "error" });
218
+ }
219
+ }
220
+ const renameId = ref(null);
221
+ const renameValue = ref("");
222
+ function startRename(docId) {
223
+ if (!docId) return;
224
+ const node = nodeMap.get(docId);
225
+ if (!node) return;
226
+ renameId.value = docId;
227
+ renameValue.value = node.name || "Untitled";
228
+ }
229
+ async function commitRename() {
230
+ if (!renameId.value || !renameValue.value.trim()) {
231
+ renameId.value = null;
232
+ return;
233
+ }
234
+ const sid = resolveSpaceId(renameId.value);
235
+ if (!sid) return;
236
+ try {
237
+ await client.documents.update(sid, renameId.value, {
238
+ name: renameValue.value.trim()
239
+ });
240
+ if (props.multiSpace) {
241
+ const space = spaces.value.find((s) => s.id === sid);
242
+ if (space) {
243
+ space.loaded = false;
244
+ await loadSpaceTree(space);
245
+ }
246
+ } else {
247
+ await loadTree();
248
+ }
249
+ } catch {
250
+ toast.add({ title: "Failed to rename", color: "error" });
251
+ } finally {
252
+ renameId.value = null;
253
+ }
254
+ }
255
+ async function deleteDoc(docId) {
256
+ if (!docId) return;
257
+ const sid = resolveSpaceId(docId);
258
+ if (!sid) return;
259
+ try {
260
+ await client.documents.delete(sid, docId);
261
+ emit("delete", docId);
262
+ if (props.multiSpace) {
263
+ const space = spaces.value.find((s) => s.id === sid);
264
+ if (space) {
265
+ space.loaded = false;
266
+ await loadSpaceTree(space);
267
+ }
268
+ } else {
269
+ await loadTree();
270
+ }
271
+ } catch {
272
+ toast.add({ title: "Failed to delete document", color: "error" });
273
+ }
274
+ }
275
+ const dragDocId = ref(null);
276
+ function onDragStart(e, docId) {
277
+ if (!props.editable) return;
278
+ dragDocId.value = docId;
279
+ if (e.dataTransfer) {
280
+ e.dataTransfer.effectAllowed = "move";
281
+ e.dataTransfer.setData("text/plain", docId);
282
+ }
283
+ }
284
+ function onDragOver(e) {
285
+ e.preventDefault();
286
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
287
+ }
288
+ async function onDrop(e, targetDocId) {
289
+ e.preventDefault();
290
+ if (!dragDocId.value || dragDocId.value === targetDocId) return;
291
+ const sid = resolveSpaceId(dragDocId.value);
292
+ if (!sid) return;
293
+ try {
294
+ await client.documents.move(sid, dragDocId.value, targetDocId, 0);
295
+ emit("move", dragDocId.value, targetDocId, 0);
296
+ if (props.multiSpace) {
297
+ const space = spaces.value.find((s) => s.id === sid);
298
+ if (space) {
299
+ space.loaded = false;
300
+ await loadSpaceTree(space);
301
+ }
302
+ } else {
303
+ await loadTree();
304
+ }
305
+ } catch {
306
+ toast.add({ title: "Failed to move document", color: "error" });
307
+ } finally {
308
+ dragDocId.value = null;
309
+ }
310
+ }
311
+ async function createRootDocument(spaceId) {
312
+ const sid = spaceId || props.spaceId;
313
+ if (!sid) return;
314
+ try {
315
+ const doc = await client.documents.create(sid, { name: "Untitled" });
316
+ emit("create", doc);
317
+ if (props.multiSpace) {
318
+ const space = spaces.value.find((s) => s.id === sid);
319
+ if (space) {
320
+ space.loaded = false;
321
+ await loadSpaceTree(space);
322
+ }
323
+ } else {
324
+ await loadTree();
325
+ }
326
+ } catch {
327
+ toast.add({ title: "Failed to create document", color: "error" });
328
+ }
329
+ }
330
+ function onDocumentClick(item) {
331
+ if (props.multiSpace) {
332
+ emit("selectSpace", item.spaceId);
333
+ }
334
+ const node = nodeMap.get(item.id);
335
+ if (node) emit("select", node);
336
+ }
337
+ if (!props.multiSpace) {
338
+ watch(() => props.spaceId, loadTree);
339
+ onMounted(loadTree);
340
+ } else {
341
+ onMounted(loadSpaces);
342
+ }
343
+ async function refresh() {
344
+ if (props.multiSpace) {
345
+ for (const space of spaces.value) {
346
+ space.loaded = false;
347
+ }
348
+ await loadSpaces();
349
+ } else {
350
+ await loadTree();
351
+ }
352
+ }
353
+ defineExpose({ refresh });
354
+ </script>
355
+
356
+ <template>
357
+ <div>
358
+ <!-- Single-space header -->
359
+ <div v-if="editable && !multiSpace" class="flex items-center justify-between px-2 py-1.5">
360
+ <span class="text-xs font-medium text-(--ui-text-muted) uppercase tracking-wide">Documents</span>
361
+ <UButton
362
+ icon="i-lucide-plus"
363
+ variant="ghost"
364
+ color="neutral"
365
+ size="xs"
366
+ @click="createRootDocument()"
367
+ />
368
+ </div>
369
+
370
+ <USkeleton v-if="multiSpace ? spacesLoading : loading" class="h-32 mx-2" />
371
+
372
+ <UEmpty
373
+ v-else-if="flatItems.length === 0"
374
+ icon="i-lucide-file-text"
375
+ :title="multiSpace ? 'No spaces' : 'No documents'"
376
+ :description="multiSpace ? 'No spaces available.' : 'Create your first document.'"
377
+ size="sm"
378
+ />
379
+
380
+ <div v-else class="space-y-px px-1">
381
+ <template v-for="item in flatItems" :key="item.id">
382
+ <!-- Space header row (multi-space mode) -->
383
+ <button
384
+ v-if="item.type === 'space'"
385
+ class="flex items-center gap-1.5 w-full px-2 py-1.5 rounded-(--ui-radius) text-xs cursor-pointer select-none hover:bg-(--ui-bg-elevated) group"
386
+ @click="toggleSpace(spaces.find((s) => s.id === item.spaceId))"
387
+ >
388
+ <UIcon
389
+ :name="item.expanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
390
+ class="size-3.5 shrink-0"
391
+ />
392
+ <UIcon name="i-lucide-layout-grid" class="size-3.5 shrink-0 text-(--ui-text-muted)" />
393
+ <span class="font-semibold uppercase tracking-wide text-(--ui-text-muted) flex-1 truncate text-left">{{ item.name }}</span>
394
+ <UIcon
395
+ v-if="spaces.find((s) => s.id === item.spaceId)?.loading"
396
+ name="i-lucide-loader"
397
+ class="size-3.5 animate-spin text-(--ui-text-muted)"
398
+ />
399
+ <UButton
400
+ v-if="editable"
401
+ icon="i-lucide-plus"
402
+ variant="ghost"
403
+ color="neutral"
404
+ size="xs"
405
+ class="opacity-0 group-hover:opacity-100"
406
+ @click.stop="createRootDocument(item.spaceId)"
407
+ />
408
+ </button>
409
+
410
+ <!-- Document row (editable with context menu) -->
411
+ <UContextMenu
412
+ v-else-if="item.type === 'document' && editable"
413
+ :items="contextMenuItems"
414
+ @update:open="(open) => {
415
+ if (open) {
416
+ contextDocId = item.id;
417
+ contextSpaceId = item.spaceId;
418
+ }
419
+ }"
420
+ >
421
+ <button
422
+ class="flex items-center gap-1.5 w-full px-2 py-1.5 rounded-(--ui-radius) text-sm cursor-pointer select-none"
423
+ :class="[
424
+ selectedId === item.id ? 'bg-(--ui-bg-elevated) text-(--ui-text-highlighted) font-medium' : 'hover:bg-(--ui-bg-elevated) text-(--ui-text-dimmed)'
425
+ ]"
426
+ :style="{ paddingLeft: `${item.depth * 16 + 8}px` }"
427
+ :draggable="editable"
428
+ @click="onDocumentClick(item)"
429
+ @dragstart="onDragStart($event, item.id)"
430
+ @dragover="onDragOver"
431
+ @drop="onDrop($event, item.id)"
432
+ >
433
+ <span
434
+ v-if="item.hasChildren"
435
+ class="flex items-center justify-center size-5 shrink-0"
436
+ @click.stop="toggleExpand(item.id)"
437
+ >
438
+ <UIcon
439
+ :name="item.expanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
440
+ class="size-3.5"
441
+ />
442
+ </span>
443
+ <span v-else class="size-5 shrink-0" />
444
+
445
+ <UIcon
446
+ :name="item.hasChildren ? item.expanded ? 'i-lucide-folder-open' : 'i-lucide-folder' : 'i-lucide-file-text'"
447
+ class="size-4 shrink-0 text-(--ui-text-muted)"
448
+ />
449
+
450
+ <UInput
451
+ v-if="renameId === item.id"
452
+ v-model="renameValue"
453
+ size="xs"
454
+ variant="none"
455
+ class="flex-1 -my-1"
456
+ autofocus
457
+ @keydown.enter="commitRename"
458
+ @keydown.escape="renameId = null"
459
+ @blur="commitRename"
460
+ @click.stop
461
+ />
462
+ <span v-else class="truncate flex-1 text-left">{{ item.name }}</span>
463
+ </button>
464
+ </UContextMenu>
465
+
466
+ <!-- Document row (non-editable) -->
467
+ <button
468
+ v-else-if="item.type === 'document' && !editable"
469
+ class="flex items-center gap-1.5 w-full px-2 py-1.5 rounded-(--ui-radius) text-sm cursor-pointer select-none"
470
+ :class="[
471
+ selectedId === item.id ? 'bg-(--ui-bg-elevated) text-(--ui-text-highlighted) font-medium' : 'hover:bg-(--ui-bg-elevated) text-(--ui-text-dimmed)'
472
+ ]"
473
+ :style="{ paddingLeft: `${item.depth * 16 + 8}px` }"
474
+ @click="onDocumentClick(item)"
475
+ >
476
+ <span
477
+ v-if="item.hasChildren"
478
+ class="flex items-center justify-center size-5 shrink-0"
479
+ @click.stop="toggleExpand(item.id)"
480
+ >
481
+ <UIcon
482
+ :name="item.expanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
483
+ class="size-3.5"
484
+ />
485
+ </span>
486
+ <span v-else class="size-5 shrink-0" />
487
+
488
+ <UIcon
489
+ :name="item.hasChildren ? item.expanded ? 'i-lucide-folder-open' : 'i-lucide-folder' : 'i-lucide-file-text'"
490
+ class="size-4 shrink-0 text-(--ui-text-muted)"
491
+ />
492
+ <span class="truncate flex-1 text-left">{{ item.name }}</span>
493
+ </button>
494
+ </template>
495
+ </div>
496
+ </div>
497
+ </template>
@@ -0,0 +1,32 @@
1
+ import type { Document, DocumentTreeNode } from "@rgby/collab-core";
2
+ type __VLS_Props = {
3
+ /** Space ID to load document tree for (required in single-space mode) */
4
+ spaceId?: string;
5
+ /** Currently selected document ID */
6
+ selectedId?: string;
7
+ /** Allow editing (rename, delete, move) */
8
+ editable?: boolean;
9
+ /** Multi-space mode: loads all spaces as collapsible groups */
10
+ multiSpace?: boolean;
11
+ };
12
+ declare function refresh(): Promise<void>;
13
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
14
+ refresh: typeof refresh;
15
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ select: (doc: DocumentTreeNode) => any;
17
+ delete: (docId: string) => any;
18
+ selectSpace: (spaceId: string) => any;
19
+ move: (docId: string, parentId: string | null, position: number) => any;
20
+ create: (doc: Document) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onSelect?: ((doc: DocumentTreeNode) => any) | undefined;
23
+ onDelete?: ((docId: string) => any) | undefined;
24
+ onSelectSpace?: ((spaceId: string) => any) | undefined;
25
+ onMove?: ((docId: string, parentId: string | null, position: number) => any) | undefined;
26
+ onCreate?: ((doc: Document) => any) | undefined;
27
+ }>, {
28
+ editable: boolean;
29
+ multiSpace: boolean;
30
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
@@ -0,0 +1,11 @@
1
+ type __VLS_Props = {
2
+ spaceId: string;
3
+ documentId: string;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ ready: (provider: any) => any;
7
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
+ onReady?: ((provider: any) => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
+ declare const _default: typeof __VLS_export;
11
+ export default _default;