@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,328 @@
1
+ <script setup>
2
+ import { Emoji } from "@tiptap/extension-emoji";
3
+ import { TaskList, TaskItem } from "@tiptap/extension-list";
4
+ import { TableKit } from "@tiptap/extension-table";
5
+ import { CellSelection } from "@tiptap/pm/tables";
6
+ import { CodeBlockShiki } from "tiptap-extension-code-block-shiki";
7
+ import { ImageUpload } from "./editor/ImageUploadExtension";
8
+ import { AllWidgets } from "./widgets";
9
+ const props = defineProps({
10
+ spaceId: { type: String, required: true },
11
+ documentId: { type: String, required: true }
12
+ });
13
+ const emit = defineEmits(["ready"]);
14
+ const client = useCollabClient();
15
+ const { user: authUser } = useAuth(client);
16
+ const user = computed(() => {
17
+ if (!authUser.value) {
18
+ return {
19
+ name: "Anonymous",
20
+ color: getRandomColor(),
21
+ avatar: void 0
22
+ };
23
+ }
24
+ const seed = authUser.value.id || authUser.value.name || "anonymous";
25
+ const colorKeys = Object.keys(COLORS);
26
+ const hash = seed.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
27
+ const color = COLORS[colorKeys[hash % colorKeys.length]];
28
+ return {
29
+ name: authUser.value.name || authUser.value.email || "Unknown",
30
+ color,
31
+ avatar: authUser.value.image
32
+ };
33
+ });
34
+ const editorRef = useTemplateRef("editorRef");
35
+ const { extension: Completion, handlers: aiHandlers, isLoading: aiLoading } = useEditorCompletion(editorRef);
36
+ const {
37
+ enabled: collaborationEnabled,
38
+ ready: collaborationReady,
39
+ extensions: collaborationExtensions,
40
+ connectedUsers,
41
+ status: connectionStatus,
42
+ doc,
43
+ provider,
44
+ synced,
45
+ updateUser
46
+ } = useEditorCollaboration({
47
+ spaceId: props.spaceId,
48
+ documentId: props.documentId,
49
+ user: {
50
+ name: user.value.name,
51
+ color: user.value.color
52
+ }
53
+ });
54
+ watch([collaborationReady, provider], ([ready, prov]) => {
55
+ if (ready && prov) {
56
+ emit("ready", prov);
57
+ }
58
+ }, { immediate: true });
59
+ watch(user, (newUser) => {
60
+ updateUser({
61
+ name: newUser.name,
62
+ color: newUser.color,
63
+ avatar: newUser.avatar
64
+ // Pass avatar if supported by CollaborationUser
65
+ });
66
+ }, { deep: true });
67
+ const customHandlers = {
68
+ imageUpload: {
69
+ canExecute: (editor) => editor.can().insertContent({ type: "imageUpload" }),
70
+ execute: (editor) => editor.chain().focus().insertContent({ type: "imageUpload" }),
71
+ isActive: (editor) => editor.isActive("imageUpload"),
72
+ isDisabled: void 0
73
+ },
74
+ table: {
75
+ canExecute: (editor) => editor.can().insertTable({ rows: 3, cols: 3, withHeaderRow: true }),
76
+ execute: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }),
77
+ isActive: (editor) => editor.isActive("table"),
78
+ isDisabled: void 0
79
+ },
80
+ switchWidget: {
81
+ canExecute: (editor) => editor.can().insertContent({ type: "switchWidget" }),
82
+ execute: (editor) => editor.chain().focus().insertContent({ type: "switchWidget" }),
83
+ isActive: (editor) => editor.isActive("switchWidget"),
84
+ isDisabled: void 0
85
+ },
86
+ checkboxWidget: {
87
+ canExecute: (editor) => editor.can().insertContent({ type: "checkboxWidget" }),
88
+ execute: (editor) => editor.chain().focus().insertContent({ type: "checkboxWidget" }),
89
+ isActive: (editor) => editor.isActive("checkboxWidget"),
90
+ isDisabled: void 0
91
+ },
92
+ buttonWidget: {
93
+ canExecute: (editor) => editor.can().insertContent({ type: "buttonWidget" }),
94
+ execute: (editor) => editor.chain().focus().insertContent({ type: "buttonWidget" }),
95
+ isActive: (editor) => editor.isActive("buttonWidget"),
96
+ isDisabled: void 0
97
+ },
98
+ sliderWidget: {
99
+ canExecute: (editor) => editor.can().insertContent({ type: "sliderWidget" }),
100
+ execute: (editor) => editor.chain().focus().insertContent({ type: "sliderWidget" }),
101
+ isActive: (editor) => editor.isActive("sliderWidget"),
102
+ isDisabled: void 0
103
+ },
104
+ colorPickerWidget: {
105
+ canExecute: (editor) => editor.can().insertContent({ type: "colorPickerWidget" }),
106
+ execute: (editor) => editor.chain().focus().insertContent({ type: "colorPickerWidget" }),
107
+ isActive: (editor) => editor.isActive("colorPickerWidget"),
108
+ isDisabled: void 0
109
+ },
110
+ calendarWidget: {
111
+ canExecute: (editor) => editor.can().insertContent({ type: "calendarWidget" }),
112
+ execute: (editor) => editor.chain().focus().insertContent({ type: "calendarWidget" }),
113
+ isActive: (editor) => editor.isActive("calendarWidget"),
114
+ isDisabled: void 0
115
+ },
116
+ inputDateWidget: {
117
+ canExecute: (editor) => editor.can().insertContent({ type: "inputDateWidget" }),
118
+ execute: (editor) => editor.chain().focus().insertContent({ type: "inputDateWidget" }),
119
+ isActive: (editor) => editor.isActive("inputDateWidget"),
120
+ isDisabled: void 0
121
+ },
122
+ scrollAreaWidget: {
123
+ canExecute: (editor) => editor.can().insertContent({ type: "scrollAreaWidget", content: [{ type: "paragraph" }] }),
124
+ execute: (editor) => editor.chain().focus().insertContent({ type: "scrollAreaWidget", content: [{ type: "paragraph" }] }),
125
+ isActive: (editor) => editor.isActive("scrollAreaWidget"),
126
+ isDisabled: void 0
127
+ },
128
+ iconWidget: {
129
+ canExecute: (editor) => editor.can().insertContent({ type: "iconWidget" }),
130
+ execute: (editor) => editor.chain().focus().insertContent({ type: "iconWidget" }),
131
+ isActive: (editor) => editor.isActive("iconWidget"),
132
+ isDisabled: void 0
133
+ },
134
+ tabsWidget: {
135
+ canExecute: (editor) => editor.can().insertContent({ type: "tabsWidget" }),
136
+ execute: (editor) => editor.chain().focus().insertContent({
137
+ type: "tabsWidget",
138
+ content: [
139
+ { type: "tabPanel", attrs: { label: "Tab 1" }, content: [{ type: "paragraph" }] },
140
+ { type: "tabPanel", attrs: { label: "Tab 2" }, content: [{ type: "paragraph" }] }
141
+ ]
142
+ }),
143
+ isActive: (editor) => editor.isActive("tabsWidget"),
144
+ isDisabled: void 0
145
+ },
146
+ ...aiHandlers
147
+ };
148
+ const { items: emojiItems } = useEditorEmojis();
149
+ const { items: mentionItems } = useEditorMentions(connectedUsers);
150
+ const { items: suggestionItems } = useEditorSuggestions(customHandlers);
151
+ const { getItems: getDragHandleItems, onNodeChange } = useEditorDragHandle(customHandlers);
152
+ const { toolbarItems, bubbleToolbarItems, getImageToolbarItems, getTableToolbarItems } = useEditorToolbar(customHandlers, { aiLoading });
153
+ const metadata = useSyncedMap(doc, "metadata");
154
+ const {
155
+ label: statusLabel,
156
+ color: statusColor,
157
+ icon: statusIcon
158
+ } = useCollabStatus(connectionStatus, synced);
159
+ function onCreate({ editor }) {
160
+ if (!collaborationEnabled) return;
161
+ const storageKey = `editor-initialized-${props.spaceId}-${props.documentId}`;
162
+ if (sessionStorage.getItem(storageKey)) return;
163
+ sessionStorage.setItem(storageKey, "true");
164
+ }
165
+ const extensions = computed(() => [
166
+ CodeBlockShiki.configure({
167
+ defaultTheme: "material-theme",
168
+ themes: {
169
+ light: "material-theme-lighter",
170
+ dark: "material-theme-palenight"
171
+ }
172
+ }),
173
+ Completion,
174
+ Emoji,
175
+ ImageUpload,
176
+ TableKit,
177
+ TaskList,
178
+ TaskItem,
179
+ ...AllWidgets,
180
+ ...collaborationExtensions.value
181
+ ]);
182
+ </script>
183
+
184
+ <template>
185
+ <UDashboardPanel>
186
+ <template #header>
187
+ <UDashboardNavbar>
188
+ <template #leading>
189
+ <UDashboardSidebarCollapse />
190
+ </template>
191
+
192
+ <template #left>
193
+ <UButton
194
+ icon="i-lucide-arrow-left"
195
+ variant="ghost"
196
+ color="neutral"
197
+ to="/"
198
+ />
199
+ <UInput
200
+ v-if="metadata"
201
+ :model-value="metadata.data.title || props.documentId"
202
+ placeholder="Untitled"
203
+ variant="none"
204
+ class="font-semibold text-lg"
205
+ @update:model-value="metadata.set('title', String($event))"
206
+ />
207
+ </template>
208
+
209
+ <template #right>
210
+ <CollabUserPresence :provider="provider" />
211
+
212
+ <UBadge :color="statusColor" variant="subtle" size="sm">
213
+ <UIcon :name="statusIcon" class="size-3.5" />
214
+ {{ statusLabel }}
215
+ </UBadge>
216
+ </template>
217
+ </UDashboardNavbar>
218
+ </template>
219
+
220
+ <template #body>
221
+ <UEditorToolbar
222
+ v-if="editorRef?.editor"
223
+ :editor="editorRef.editor"
224
+ :items="toolbarItems"
225
+ />
226
+
227
+ <UEditor
228
+ v-if="collaborationReady"
229
+ ref="editorRef"
230
+ v-slot="{ editor, handlers }"
231
+ content-type="markdown"
232
+ :extensions="extensions"
233
+ :starter-kit="{ history: false, undoRedo: false, codeBlock: false }"
234
+ :handlers="customHandlers"
235
+ autofocus
236
+ placeholder="Write, type '/' for commands..."
237
+ class="min-h-screen"
238
+ @create="onCreate"
239
+ >
240
+ <UEditorToolbar
241
+ :editor="editor"
242
+ :items="bubbleToolbarItems"
243
+ layout="bubble"
244
+ :should-show="({ editor: editor2, view, state }) => {
245
+ if (editor2.isActive('imageUpload') || editor2.isActive('image') || state.selection instanceof CellSelection) {
246
+ return false;
247
+ }
248
+ const { selection } = state;
249
+ return view.hasFocus() && !selection.empty;
250
+ }"
251
+ >
252
+ <template #link>
253
+ <CollabEditorLinkPopover :editor="editor" />
254
+ </template>
255
+ </UEditorToolbar>
256
+
257
+ <UEditorToolbar
258
+ :editor="editor"
259
+ :items="getImageToolbarItems(editor)"
260
+ layout="bubble"
261
+ :should-show="({ editor: editor2, view }) => {
262
+ return editor2.isActive('image') && view.hasFocus();
263
+ }"
264
+ />
265
+
266
+ <UEditorToolbar
267
+ :editor="editor"
268
+ :items="getTableToolbarItems(editor)"
269
+ layout="bubble"
270
+ :should-show="({ editor: editor2, view }) => {
271
+ return editor2.state.selection instanceof CellSelection && view.hasFocus();
272
+ }"
273
+ />
274
+
275
+ <UEditorDragHandle
276
+ v-slot="{ ui, onClick }"
277
+ :editor="editor"
278
+ @node-change="onNodeChange"
279
+ >
280
+ <UButton
281
+ icon="i-lucide-plus"
282
+ color="neutral"
283
+ variant="ghost"
284
+ size="sm"
285
+ :class="ui.handle()"
286
+ @click="(e) => {
287
+ e.stopPropagation();
288
+ const node = onClick();
289
+ handlers.suggestion?.execute(editor, { pos: node?.pos }).run();
290
+ }"
291
+ />
292
+
293
+ <UDropdownMenu
294
+ v-slot="{ open }"
295
+ :modal="false"
296
+ :items="getDragHandleItems(editor)"
297
+ :content="{ side: 'left' }"
298
+ :ui="{ content: 'w-48', label: 'text-xs' }"
299
+ @update:open="editor.chain().setMeta('lockDragHandle', $event).run()"
300
+ >
301
+ <UButton
302
+ color="neutral"
303
+ variant="ghost"
304
+ active-variant="soft"
305
+ size="sm"
306
+ icon="i-lucide-grip-vertical"
307
+ :active="open"
308
+ :class="ui.handle()"
309
+ />
310
+ </UDropdownMenu>
311
+ </UEditorDragHandle>
312
+
313
+ <UEditorEmojiMenu
314
+ :editor="editor"
315
+ :items="emojiItems"
316
+ />
317
+ <UEditorMentionMenu
318
+ :editor="editor"
319
+ :items="mentionItems"
320
+ />
321
+ <UEditorSuggestionMenu
322
+ :editor="editor"
323
+ :items="suggestionItems"
324
+ />
325
+ </UEditor>
326
+ </template>
327
+ </UDashboardPanel>
328
+ </template>
@@ -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;
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ /** Space ID */
3
+ spaceId: string;
4
+ /** Document ID */
5
+ documentId: string;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,55 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ spaceId: { type: String, required: true },
4
+ documentId: { type: String, required: true }
5
+ });
6
+ const client = useCollabClient();
7
+ const toast = useToast();
8
+ const exporting = ref(null);
9
+ const formats = [
10
+ { label: "HTML", value: "html", icon: "i-lucide-code" },
11
+ { label: "JSON", value: "json", icon: "i-lucide-braces" }
12
+ ];
13
+ const menuItems = computed(() => [
14
+ formats.map((fmt) => ({
15
+ label: fmt.label,
16
+ icon: fmt.icon,
17
+ disabled: exporting.value === fmt.value,
18
+ onSelect: () => exportDoc(fmt.value)
19
+ }))
20
+ ]);
21
+ async function exportDoc(format) {
22
+ exporting.value = format;
23
+ try {
24
+ const content = await client.documents.export(props.spaceId, props.documentId, format);
25
+ const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
26
+ const mimeType = format === "html" ? "text/html" : "application/json";
27
+ const ext = format === "html" ? "html" : "json";
28
+ const blob = new Blob([text], { type: mimeType });
29
+ const url = URL.createObjectURL(blob);
30
+ const a = document.createElement("a");
31
+ a.href = url;
32
+ a.download = `document.${ext}`;
33
+ a.click();
34
+ URL.revokeObjectURL(url);
35
+ toast.add({ title: `Exported as ${format.toUpperCase()}`, color: "success", icon: "i-lucide-download" });
36
+ } catch {
37
+ toast.add({ title: "Export failed", color: "error" });
38
+ } finally {
39
+ exporting.value = null;
40
+ }
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <UDropdownMenu :items="menuItems">
46
+ <UButton
47
+ icon="i-lucide-download"
48
+ label="Export"
49
+ variant="ghost"
50
+ color="neutral"
51
+ size="sm"
52
+ :loading="!!exporting"
53
+ />
54
+ </UDropdownMenu>
55
+ </template>
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ /** Space ID */
3
+ spaceId: string;
4
+ /** Document ID */
5
+ documentId: string;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import type { FileUpload as CollabFile } from "@rgby/collab-core";
2
+ type __VLS_Props = {
3
+ spaceId: string;
4
+ documentId?: string;
5
+ accept?: string;
6
+ multiple?: boolean;
7
+ };
8
+ declare function loadFiles(): Promise<void>;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
10
+ refresh: typeof loadFiles;
11
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ delete: (fileId: string) => any;
13
+ upload: (file: CollabFile) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onDelete?: ((fileId: string) => any) | undefined;
16
+ onUpload?: ((file: CollabFile) => any) | undefined;
17
+ }>, {
18
+ multiple: boolean;
19
+ accept: string;
20
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,148 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ spaceId: { type: String, required: true },
4
+ documentId: { type: String, required: false },
5
+ accept: { type: String, required: false, default: "*" },
6
+ multiple: { type: Boolean, required: false, default: true }
7
+ });
8
+ const emit = defineEmits(["upload", "delete"]);
9
+ const client = useCollabClient();
10
+ const toast = useToast();
11
+ const runtimeConfig = useRuntimeConfig();
12
+ const baseUrl = runtimeConfig.public.collab?.baseUrl || "";
13
+ const uploadedFiles = ref([]);
14
+ const loading = ref(true);
15
+ const pendingCount = ref(0);
16
+ const localFiles = ref([]);
17
+ async function loadFiles() {
18
+ loading.value = true;
19
+ try {
20
+ const result = await client.uploads.list(props.spaceId, props.documentId);
21
+ uploadedFiles.value = result.items;
22
+ } finally {
23
+ loading.value = false;
24
+ }
25
+ }
26
+ watch(localFiles, async (files) => {
27
+ if (!files || files.length === 0) return;
28
+ for (const file of files) {
29
+ try {
30
+ const uploaded = await client.uploads.upload(props.spaceId, file, props.documentId);
31
+ uploadedFiles.value.unshift(uploaded);
32
+ emit("upload", uploaded);
33
+ } catch {
34
+ toast.add({ title: `Failed to upload ${file.name}`, color: "error" });
35
+ }
36
+ }
37
+ localFiles.value = [];
38
+ });
39
+ async function deleteFile(fileId) {
40
+ try {
41
+ await client.uploads.delete(props.spaceId, fileId);
42
+ uploadedFiles.value = uploadedFiles.value.filter((f) => f.id !== fileId);
43
+ emit("delete", fileId);
44
+ } catch {
45
+ toast.add({ title: "Failed to delete file", color: "error" });
46
+ }
47
+ }
48
+ async function checkPending() {
49
+ try {
50
+ pendingCount.value = await client.getPendingUploadsCount();
51
+ } catch {
52
+ }
53
+ }
54
+ function formatSize(bytes) {
55
+ if (bytes < 1024) return `${bytes} B`;
56
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
57
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
58
+ }
59
+ function isImage(mimeType) {
60
+ return mimeType.startsWith("image/");
61
+ }
62
+ function resolveUrl(url) {
63
+ if (!url) return "";
64
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:")) return url;
65
+ return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
66
+ }
67
+ onMounted(() => {
68
+ loadFiles();
69
+ checkPending();
70
+ });
71
+ defineExpose({ refresh: loadFiles });
72
+ </script>
73
+
74
+ <template>
75
+ <div class="space-y-4">
76
+ <div class="flex items-center justify-between">
77
+ <h3 class="text-sm font-medium">Files</h3>
78
+ <UBadge
79
+ v-if="pendingCount > 0"
80
+ :label="`${pendingCount} pending`"
81
+ color="warning"
82
+ variant="subtle"
83
+ size="xs"
84
+ icon="i-lucide-cloud-off"
85
+ />
86
+ </div>
87
+
88
+ <UFileUpload
89
+ v-model="localFiles"
90
+ :accept="accept"
91
+ :multiple="multiple"
92
+ >
93
+ <template #actions="{ open }">
94
+ <UButton
95
+ label="Upload files"
96
+ icon="i-lucide-upload"
97
+ color="neutral"
98
+ variant="outline"
99
+ @click="open()"
100
+ />
101
+ </template>
102
+ </UFileUpload>
103
+
104
+ <USkeleton v-if="loading" class="h-24" />
105
+
106
+ <UEmpty
107
+ v-else-if="uploadedFiles.length === 0"
108
+ icon="i-lucide-file"
109
+ title="No files uploaded"
110
+ size="sm"
111
+ />
112
+
113
+ <div v-else class="grid grid-cols-2 sm:grid-cols-3 gap-2">
114
+ <div
115
+ v-for="file in uploadedFiles"
116
+ :key="file.id"
117
+ class="group relative rounded-(--ui-radius) border border-(--ui-border) overflow-hidden"
118
+ >
119
+ <img
120
+ v-if="isImage(file.mimeType)"
121
+ :src="resolveUrl(file.url)"
122
+ :alt="file.filename"
123
+ class="w-full h-24 object-cover"
124
+ />
125
+ <div
126
+ v-else
127
+ class="w-full h-24 flex items-center justify-center bg-(--ui-bg-elevated)"
128
+ >
129
+ <UIcon name="i-lucide-file" class="size-8 text-(--ui-text-muted)" />
130
+ </div>
131
+
132
+ <div class="p-1.5">
133
+ <div class="text-xs truncate">{{ file.filename }}</div>
134
+ <div class="text-xs text-(--ui-text-muted)">{{ formatSize(file.size) }}</div>
135
+ </div>
136
+
137
+ <UButton
138
+ icon="i-lucide-x"
139
+ variant="soft"
140
+ color="error"
141
+ size="2xs"
142
+ class="absolute top-1 right-1 opacity-0 group-hover:opacity-100"
143
+ @click="deleteFile(file.id)"
144
+ />
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </template>
@@ -0,0 +1,22 @@
1
+ import type { FileUpload as CollabFile } from "@rgby/collab-core";
2
+ type __VLS_Props = {
3
+ spaceId: string;
4
+ documentId?: string;
5
+ accept?: string;
6
+ multiple?: boolean;
7
+ };
8
+ declare function loadFiles(): Promise<void>;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
10
+ refresh: typeof loadFiles;
11
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ delete: (fileId: string) => any;
13
+ upload: (file: CollabFile) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onDelete?: ((fileId: string) => any) | undefined;
16
+ onUpload?: ((file: CollabFile) => any) | undefined;
17
+ }>, {
18
+ multiple: boolean;
19
+ accept: string;
20
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ spaceId: string;
3
+ currentUserRole?: string;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
6
+ currentUserRole: string;
7
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;