@abraca/nuxt 2.10.0 → 2.13.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.
Files changed (127) hide show
  1. package/dist/module.d.mts +14 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +9 -0
  4. package/dist/runtime/assets/editor.css +1 -1
  5. package/dist/runtime/components/AConnectionBadge.d.vue.ts +29 -0
  6. package/dist/runtime/components/AConnectionBadge.vue +79 -0
  7. package/dist/runtime/components/AConnectionBadge.vue.d.ts +29 -0
  8. package/dist/runtime/components/ADocPickerModal.d.vue.ts +31 -0
  9. package/dist/runtime/components/ADocPickerModal.vue +191 -0
  10. package/dist/runtime/components/ADocPickerModal.vue.d.ts +31 -0
  11. package/dist/runtime/components/ADocumentTree.vue +65 -0
  12. package/dist/runtime/components/AEditor.d.vue.ts +19 -12
  13. package/dist/runtime/components/AEditor.vue +243 -165
  14. package/dist/runtime/components/AEditor.vue.d.ts +19 -12
  15. package/dist/runtime/components/AEncryptionModePicker.d.vue.ts +33 -0
  16. package/dist/runtime/components/AEncryptionModePicker.vue +211 -0
  17. package/dist/runtime/components/AEncryptionModePicker.vue.d.ts +33 -0
  18. package/dist/runtime/components/AModalShell.d.vue.ts +48 -0
  19. package/dist/runtime/components/AModalShell.vue +105 -0
  20. package/dist/runtime/components/AModalShell.vue.d.ts +48 -0
  21. package/dist/runtime/components/ANodePanel.d.vue.ts +17 -7
  22. package/dist/runtime/components/ANodePanel.vue +550 -451
  23. package/dist/runtime/components/ANodePanel.vue.d.ts +17 -7
  24. package/dist/runtime/components/ANodePanelHeader.d.vue.ts +20 -10
  25. package/dist/runtime/components/ANodePanelHeader.vue +17 -3
  26. package/dist/runtime/components/ANodePanelHeader.vue.d.ts +20 -10
  27. package/dist/runtime/components/ANodeSettingsPanel.d.vue.ts +2 -0
  28. package/dist/runtime/components/ANodeSettingsPanel.vue +21 -1
  29. package/dist/runtime/components/ANodeSettingsPanel.vue.d.ts +2 -0
  30. package/dist/runtime/components/ASnapshotPreviewModal.d.vue.ts +33 -0
  31. package/dist/runtime/components/ASnapshotPreviewModal.vue +430 -0
  32. package/dist/runtime/components/ASnapshotPreviewModal.vue.d.ts +33 -0
  33. package/dist/runtime/components/ATagsEditor.d.vue.ts +19 -0
  34. package/dist/runtime/components/ATagsEditor.vue +60 -0
  35. package/dist/runtime/components/ATagsEditor.vue.d.ts +19 -0
  36. package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
  37. package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
  38. package/dist/runtime/components/chat/AChatInput.d.vue.ts +11 -6
  39. package/dist/runtime/components/chat/AChatInput.vue +33 -2
  40. package/dist/runtime/components/chat/AChatInput.vue.d.ts +11 -6
  41. package/dist/runtime/components/chat/AChatList.d.vue.ts +12 -0
  42. package/dist/runtime/components/chat/AChatList.vue +76 -32
  43. package/dist/runtime/components/chat/AChatList.vue.d.ts +12 -0
  44. package/dist/runtime/components/chat/AChatMessages.d.vue.ts +4 -0
  45. package/dist/runtime/components/chat/AChatMessages.vue +57 -4
  46. package/dist/runtime/components/chat/AChatMessages.vue.d.ts +4 -0
  47. package/dist/runtime/components/chat/AChatPanel.d.vue.ts +6 -2
  48. package/dist/runtime/components/chat/AChatPanel.vue +17 -1
  49. package/dist/runtime/components/chat/AChatPanel.vue.d.ts +6 -2
  50. package/dist/runtime/components/chat/ANodeChatPanel.vue +1 -1
  51. package/dist/runtime/components/docs/ADocsSearch.d.vue.ts +1 -1
  52. package/dist/runtime/components/docs/ADocsSearch.vue.d.ts +1 -1
  53. package/dist/runtime/components/editor/ALocationPickerPopover.vue +28 -7
  54. package/dist/runtime/components/registry/APluginDetail.d.vue.ts +2 -2
  55. package/dist/runtime/components/registry/APluginDetail.vue.d.ts +2 -2
  56. package/dist/runtime/components/renderers/AChartRenderer.client.d.vue.ts +17 -0
  57. package/dist/runtime/components/renderers/AChartRenderer.client.vue +622 -0
  58. package/dist/runtime/components/renderers/AChartRenderer.client.vue.d.ts +17 -0
  59. package/dist/runtime/components/renderers/AGraphRenderer.vue +64 -15
  60. package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +2 -2
  61. package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +2 -2
  62. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.d.vue.ts +2 -2
  63. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.vue.d.ts +2 -2
  64. package/dist/runtime/components/renderers/media/MediaTransportBar.d.vue.ts +2 -2
  65. package/dist/runtime/components/renderers/media/MediaTransportBar.vue.d.ts +2 -2
  66. package/dist/runtime/components/renderers/sheets/ASheetsGrid.d.vue.ts +2 -2
  67. package/dist/runtime/components/renderers/sheets/ASheetsGrid.vue.d.ts +2 -2
  68. package/dist/runtime/components/settings/ASettingsAppearancePanel.d.vue.ts +3 -0
  69. package/dist/runtime/components/settings/ASettingsAppearancePanel.vue +67 -0
  70. package/dist/runtime/components/settings/ASettingsAppearancePanel.vue.d.ts +3 -0
  71. package/dist/runtime/components/settings/ASettingsGroup.d.vue.ts +24 -0
  72. package/dist/runtime/components/settings/ASettingsGroup.vue +31 -0
  73. package/dist/runtime/components/settings/ASettingsGroup.vue.d.ts +24 -0
  74. package/dist/runtime/components/settings/ASettingsModal.vue +84 -53
  75. package/dist/runtime/components/settings/ASettingsPlaceholder.d.vue.ts +20 -0
  76. package/dist/runtime/components/settings/ASettingsPlaceholder.vue +32 -0
  77. package/dist/runtime/components/settings/ASettingsPlaceholder.vue.d.ts +20 -0
  78. package/dist/runtime/components/settings/ASettingsRow.d.vue.ts +34 -0
  79. package/dist/runtime/components/settings/ASettingsRow.vue +34 -0
  80. package/dist/runtime/components/settings/ASettingsRow.vue.d.ts +34 -0
  81. package/dist/runtime/components/settings/sections.d.ts +37 -0
  82. package/dist/runtime/components/settings/sections.js +45 -0
  83. package/dist/runtime/components/shell/ABreadcrumbForDoc.d.vue.ts +6 -0
  84. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue +75 -3
  85. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue.d.ts +6 -0
  86. package/dist/runtime/components/shell/ADocPanelServerSettings.d.vue.ts +17 -0
  87. package/dist/runtime/components/shell/ADocPanelServerSettings.vue +253 -0
  88. package/dist/runtime/components/shell/ADocPanelServerSettings.vue.d.ts +17 -0
  89. package/dist/runtime/components/shell/ADocPanelSettings.d.vue.ts +2 -0
  90. package/dist/runtime/components/shell/ADocPanelSettings.vue +15 -4
  91. package/dist/runtime/components/shell/ADocPanelSettings.vue.d.ts +2 -0
  92. package/dist/runtime/components/shell/AUserProfilePopover.d.vue.ts +1 -1
  93. package/dist/runtime/components/shell/AUserProfilePopover.vue.d.ts +1 -1
  94. package/dist/runtime/composables/useChat.d.ts +22 -1
  95. package/dist/runtime/composables/useChat.js +79 -8
  96. package/dist/runtime/composables/useDocBreadcrumb.d.ts +17 -2
  97. package/dist/runtime/composables/useDocBreadcrumb.js +17 -3
  98. package/dist/runtime/composables/useDocSnapshots.d.ts +2 -1
  99. package/dist/runtime/composables/useDocSnapshots.js +5 -0
  100. package/dist/runtime/composables/useEditor.d.ts +1 -1
  101. package/dist/runtime/composables/useEditor.js +120 -0
  102. package/dist/runtime/composables/useEditorToolbar.d.ts +12 -4
  103. package/dist/runtime/composables/useEditorToolbar.js +78 -56
  104. package/dist/runtime/composables/useNodeContextMenu.d.ts +14 -0
  105. package/dist/runtime/composables/useNodeContextMenu.js +59 -1
  106. package/dist/runtime/composables/useSettingsModal.d.ts +1 -1
  107. package/dist/runtime/composables/useSwipeGesture.d.ts +48 -0
  108. package/dist/runtime/composables/useSwipeGesture.js +140 -0
  109. package/dist/runtime/extensions/document-header.js +16 -6
  110. package/dist/runtime/extensions/document-meta.js +344 -19
  111. package/dist/runtime/extensions/meta-field.js +42 -0
  112. package/dist/runtime/extensions/views/DocumentMetaView.vue +33 -7
  113. package/dist/runtime/extensions/views/FieldView.vue +51 -19
  114. package/dist/runtime/extensions/views/MetaFieldView.vue +30 -4
  115. package/dist/runtime/locale.d.ts +8 -0
  116. package/dist/runtime/locale.js +9 -1
  117. package/dist/runtime/middleware/abracadabra-auth.d.ts +1 -1
  118. package/dist/runtime/plugin-abracadabra.client.d.ts +1 -1
  119. package/dist/runtime/plugin-abracadabra.client.js +12 -2
  120. package/dist/runtime/plugin-abracadabra.server.d.ts +1 -1
  121. package/dist/runtime/plugin-shared-globals.client.d.ts +1 -1
  122. package/dist/runtime/utils/chatContent.d.ts +20 -2
  123. package/dist/runtime/utils/chatContent.js +20 -1
  124. package/dist/runtime/utils/docTypes.js +43 -0
  125. package/dist/runtime/utils/titleSync.d.ts +130 -0
  126. package/dist/runtime/utils/titleSync.js +53 -0
  127. package/package.json +11 -4
@@ -0,0 +1,32 @@
1
+ <script setup>
2
+ defineProps({
3
+ icon: { type: String, required: true },
4
+ title: { type: String, required: true },
5
+ description: { type: String, required: true },
6
+ badge: { type: String, required: false, default: void 0 }
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <div class="flex flex-col items-center justify-center text-center py-16 px-4 gap-3">
12
+ <div class="size-12 rounded-full bg-(--ui-bg-elevated) flex items-center justify-center">
13
+ <UIcon
14
+ :name="icon"
15
+ class="size-6 text-(--ui-text-muted)"
16
+ />
17
+ </div>
18
+ <h3 class="text-sm font-semibold text-(--ui-text-highlighted)">
19
+ {{ title }}
20
+ </h3>
21
+ <p class="text-xs text-(--ui-text-muted) max-w-sm leading-relaxed">
22
+ {{ description }}
23
+ </p>
24
+ <UBadge
25
+ v-if="badge"
26
+ color="neutral"
27
+ variant="subtle"
28
+ size="sm"
29
+ :label="badge"
30
+ />
31
+ </div>
32
+ </template>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * <ASettingsPlaceholder>
3
+ *
4
+ * Centered empty-state card for a not-yet-implemented (or empty) settings
5
+ * section: icon bubble + title + description, with an optional badge.
6
+ * Ported from cou-sh SettingsV2/Placeholder.vue — the badge is configurable
7
+ * here (cou-sh hardcoded "Coming next"); omit `badge` to hide it.
8
+ */
9
+ type __VLS_Props = {
10
+ icon: string;
11
+ title: string;
12
+ description: string;
13
+ /** Optional badge label (e.g. "Coming next"). Omit to hide the badge. */
14
+ badge?: string;
15
+ };
16
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
17
+ badge: string;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * <ASettingsRow>
3
+ *
4
+ * One labelled setting row: label + optional description on the left, the
5
+ * control (default slot) on the right. Bottom-bordered so rows stack into a
6
+ * clean list inside an <ASettingsGroup>. Use `stacked` for wide controls
7
+ * (color grids, lists) that need their own row beneath the label.
8
+ *
9
+ * Shared building block for settings panels — ported from cou-sh
10
+ * SettingsV2/Row.vue so module panels share one consistent row look.
11
+ */
12
+ type __VLS_Props = {
13
+ label: string;
14
+ description?: string;
15
+ /** Stack label + description above a full-width control row (wide widgets). */
16
+ stacked?: boolean;
17
+ /** Vertical alignment of the control against the label. */
18
+ align?: 'center' | 'start';
19
+ };
20
+ declare var __VLS_1: {};
21
+ type __VLS_Slots = {} & {
22
+ default?: (props: typeof __VLS_1) => any;
23
+ };
24
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
25
+ align: "center" | "start";
26
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ defineProps({
3
+ label: { type: String, required: true },
4
+ description: { type: String, required: false },
5
+ stacked: { type: Boolean, required: false },
6
+ align: { type: String, required: false, default: "center" }
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ class="flex gap-4 py-3.5 border-b border-(--ui-border) last:border-b-0"
13
+ :class="[
14
+ stacked ? 'flex-col' : 'flex-col sm:flex-row sm:justify-between',
15
+ !stacked && align === 'center' ? 'sm:items-center' : 'sm:items-start'
16
+ ]"
17
+ >
18
+ <div class="flex flex-col gap-0.5 min-w-0">
19
+ <span class="text-sm text-(--ui-text-highlighted)">{{ label }}</span>
20
+ <span
21
+ v-if="description"
22
+ class="text-xs text-(--ui-text-muted) leading-relaxed"
23
+ >
24
+ {{ description }}
25
+ </span>
26
+ </div>
27
+ <div
28
+ class="flex items-center gap-2 shrink-0"
29
+ :class="stacked && 'w-full justify-start'"
30
+ >
31
+ <slot />
32
+ </div>
33
+ </div>
34
+ </template>
@@ -0,0 +1,34 @@
1
+ /**
2
+ * <ASettingsRow>
3
+ *
4
+ * One labelled setting row: label + optional description on the left, the
5
+ * control (default slot) on the right. Bottom-bordered so rows stack into a
6
+ * clean list inside an <ASettingsGroup>. Use `stacked` for wide controls
7
+ * (color grids, lists) that need their own row beneath the label.
8
+ *
9
+ * Shared building block for settings panels — ported from cou-sh
10
+ * SettingsV2/Row.vue so module panels share one consistent row look.
11
+ */
12
+ type __VLS_Props = {
13
+ label: string;
14
+ description?: string;
15
+ /** Stack label + description above a full-width control row (wide widgets). */
16
+ stacked?: boolean;
17
+ /** Vertical alignment of the control against the label. */
18
+ align?: 'center' | 'start';
19
+ };
20
+ declare var __VLS_1: {};
21
+ type __VLS_Slots = {} & {
22
+ default?: (props: typeof __VLS_1) => any;
23
+ };
24
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
25
+ align: "center" | "start";
26
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Settings section registry.
3
+ *
4
+ * The single source of truth for which settings sections exist, how they're
5
+ * labelled/grouped/searched, and which component renders each. <ASettingsModal>
6
+ * builds its nav + content from this list instead of hard-coding them, so a new
7
+ * section is one entry here (no template edits), and the nav gets search +
8
+ * grouping for free.
9
+ *
10
+ * Grouping maps onto the shell's two modes:
11
+ * - 'user' → "User settings"
12
+ * - 'workspace' → "Administration" (workspace-level)
13
+ * - 'admin' → "Administration" (admin/service only; `adminOnly`)
14
+ */
15
+ import { type Component } from 'vue';
16
+ import type { SettingsTab } from '../../composables/useSettingsModal.js';
17
+ export type SettingsGroup = 'user' | 'workspace' | 'admin';
18
+ export interface SettingsSectionDef {
19
+ key: SettingsTab;
20
+ label: string;
21
+ icon: string;
22
+ group: SettingsGroup;
23
+ /** Extra terms matched by the nav search box (besides the label). */
24
+ keywords?: string[];
25
+ /** Only shown to admin/service roles. */
26
+ adminOnly?: boolean;
27
+ /** The panel component to render for this section. */
28
+ component: Component;
29
+ }
30
+ /** Which shell "mode" each group belongs to. */
31
+ export declare const GROUP_MODE: Record<SettingsGroup, 'user' | 'admin'>;
32
+ export declare const GROUP_LABELS: Record<SettingsGroup, string>;
33
+ export declare const BUILTIN_SETTINGS_SECTIONS: SettingsSectionDef[];
34
+ /** Filter to the sections visible for the current role. */
35
+ export declare function visibleSections(sections: SettingsSectionDef[], isAdmin: boolean): SettingsSectionDef[];
36
+ /** Case-insensitive search over label + keywords. */
37
+ export declare function searchSections(query: string, sections: SettingsSectionDef[]): SettingsSectionDef[];
@@ -0,0 +1,45 @@
1
+ import { markRaw } from "vue";
2
+ import ASettingsProfilePanel from "./ASettingsProfilePanel.vue";
3
+ import ASettingsAppearancePanel from "./ASettingsAppearancePanel.vue";
4
+ import ASettingsSecurityPanel from "./ASettingsSecurityPanel.vue";
5
+ import ASettingsOfflinePanel from "./ASettingsOfflinePanel.vue";
6
+ import ASettingsSpacesPanel from "./ASettingsSpacesPanel.vue";
7
+ import ASettingsMembersPanel from "./ASettingsMembersPanel.vue";
8
+ import ASettingsConnectionPanel from "./ASettingsConnectionPanel.vue";
9
+ import ASettingsTrashPanel from "./ASettingsTrashPanel.vue";
10
+ import ASettingsPluginsPanel from "./ASettingsPluginsPanel.vue";
11
+ import ASettingsInvitesPanel from "./ASettingsInvitesPanel.vue";
12
+ import ASettingsAdminPanel from "./ASettingsAdminPanel.vue";
13
+ export const GROUP_MODE = {
14
+ user: "user",
15
+ workspace: "admin",
16
+ admin: "admin"
17
+ };
18
+ export const GROUP_LABELS = {
19
+ user: "Account",
20
+ workspace: "Workspace",
21
+ admin: "Administration"
22
+ };
23
+ export const BUILTIN_SETTINGS_SECTIONS = [
24
+ { key: "profile", label: "Profile", icon: "i-lucide-user", group: "user", keywords: ["name", "avatar", "identity", "colour", "color"], component: markRaw(ASettingsProfilePanel) },
25
+ { key: "appearance", label: "Appearance", icon: "i-lucide-palette", group: "user", keywords: ["theme", "dark", "light", "motion", "font"], component: markRaw(ASettingsAppearancePanel) },
26
+ { key: "security", label: "Security", icon: "i-lucide-shield", group: "user", keywords: ["passkey", "password", "account", "logout", "wipe"], component: markRaw(ASettingsSecurityPanel) },
27
+ { key: "offline", label: "Offline", icon: "i-lucide-database", group: "user", keywords: ["sync", "storage", "cache"], component: markRaw(ASettingsOfflinePanel) },
28
+ { key: "spaces", label: "Spaces", icon: "i-lucide-layers", group: "workspace", keywords: ["workspace", "create", "switch"], component: markRaw(ASettingsSpacesPanel) },
29
+ { key: "members", label: "Members", icon: "i-lucide-users", group: "workspace", keywords: ["people", "roles", "online"], component: markRaw(ASettingsMembersPanel) },
30
+ { key: "connection", label: "Connection", icon: "i-lucide-wifi", group: "workspace", keywords: ["server", "p2p", "webrtc", "reconnect"], component: markRaw(ASettingsConnectionPanel) },
31
+ { key: "trash", label: "Trash", icon: "i-lucide-trash-2", group: "workspace", keywords: ["deleted", "restore", "purge"], component: markRaw(ASettingsTrashPanel) },
32
+ { key: "plugins", label: "Plugins", icon: "i-lucide-plug", group: "workspace", keywords: ["extensions", "page types"], component: markRaw(ASettingsPluginsPanel) },
33
+ { key: "invites", label: "Invites", icon: "i-lucide-ticket", group: "admin", adminOnly: true, keywords: ["invite", "code", "join"], component: markRaw(ASettingsInvitesPanel) },
34
+ { key: "admin", label: "Admin", icon: "i-lucide-shield-alert", group: "admin", adminOnly: true, keywords: ["users", "server", "manage"], component: markRaw(ASettingsAdminPanel) }
35
+ ];
36
+ export function visibleSections(sections, isAdmin) {
37
+ return sections.filter((s) => !s.adminOnly || isAdmin);
38
+ }
39
+ export function searchSections(query, sections) {
40
+ const q = query.trim().toLowerCase();
41
+ if (!q) return sections;
42
+ return sections.filter(
43
+ (s) => s.label.toLowerCase().includes(q) || s.keywords?.some((k) => k.toLowerCase().includes(q))
44
+ );
45
+ }
@@ -3,9 +3,15 @@ type __VLS_Props = {
3
3
  docId: string | null | undefined;
4
4
  /** Maximum ancestors to walk (default 8) */
5
5
  maxDepth?: number;
6
+ /**
7
+ * Collapse the middle into a "…" overflow menu once the trail exceeds this
8
+ * many crumbs. 0 (default) renders the full trail with no collapsing.
9
+ */
10
+ maxVisible?: number;
6
11
  };
7
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
13
  maxDepth: number;
14
+ maxVisible: number;
9
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
16
  declare const _default: typeof __VLS_export;
11
17
  export default _default;
@@ -1,15 +1,87 @@
1
1
  <script setup>
2
+ import { computed } from "vue";
2
3
  import { useDocBreadcrumb } from "../../composables/useDocBreadcrumb";
3
4
  const props = defineProps({
4
5
  docId: { type: null, required: true },
5
- maxDepth: { type: Number, required: false, default: 8 }
6
+ maxDepth: { type: Number, required: false, default: 8 },
7
+ maxVisible: { type: Number, required: false, default: 0 }
6
8
  });
7
- const { items } = useDocBreadcrumb(() => props.docId, { maxDepth: props.maxDepth });
9
+ const { items, collapsed } = useDocBreadcrumb(() => props.docId, {
10
+ maxDepth: props.maxDepth,
11
+ maxVisible: props.maxVisible
12
+ });
13
+ const overflowItems = computed(
14
+ () => collapsed.value.hidden.map((a) => ({ label: a.label, icon: a.icon, to: a.to }))
15
+ );
8
16
  </script>
9
17
 
10
18
  <template>
19
+ <!-- Collapsed single-line variant (overflow menu for the hidden middle) -->
20
+ <nav
21
+ v-if="collapsed.overflowed"
22
+ aria-label="Breadcrumb"
23
+ class="flex items-center gap-0.5 min-w-0 text-(--ui-text-muted)"
24
+ >
25
+ <template
26
+ v-for="ancestor in collapsed.head"
27
+ :key="ancestor.id"
28
+ >
29
+ <ULink
30
+ :to="ancestor.to"
31
+ class="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs hover:bg-(--ui-bg-elevated)/60 hover:text-(--ui-text) transition-colors min-w-0"
32
+ >
33
+ <UIcon
34
+ :name="ancestor.icon"
35
+ class="size-3.5 shrink-0"
36
+ />
37
+ <span class="truncate max-w-[10ch]">{{ ancestor.label }}</span>
38
+ </ULink>
39
+ <UIcon
40
+ name="i-lucide-chevron-right"
41
+ class="size-3.5 shrink-0 opacity-60"
42
+ />
43
+ </template>
44
+
45
+ <UDropdownMenu :items="overflowItems">
46
+ <button
47
+ type="button"
48
+ class="flex items-center px-1 py-0.5 rounded text-xs hover:bg-(--ui-bg-elevated)/60 hover:text-(--ui-text) transition-colors"
49
+ :aria-label="`Show ${collapsed.hidden.length} hidden`"
50
+ >
51
+
52
+ </button>
53
+ </UDropdownMenu>
54
+ <UIcon
55
+ name="i-lucide-chevron-right"
56
+ class="size-3.5 shrink-0 opacity-60"
57
+ />
58
+
59
+ <template
60
+ v-for="(ancestor, idx) in collapsed.tail"
61
+ :key="ancestor.id"
62
+ >
63
+ <ULink
64
+ :to="ancestor.to"
65
+ class="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs hover:bg-(--ui-bg-elevated)/60 hover:text-(--ui-text) transition-colors min-w-0"
66
+ :class="ancestor.to ? '' : 'pointer-events-none text-(--ui-text)'"
67
+ >
68
+ <UIcon
69
+ :name="ancestor.icon"
70
+ class="size-3.5 shrink-0"
71
+ />
72
+ <span class="truncate max-w-[10ch]">{{ ancestor.label }}</span>
73
+ </ULink>
74
+ <UIcon
75
+ v-if="idx < collapsed.tail.length - 1"
76
+ name="i-lucide-chevron-right"
77
+ class="size-3.5 shrink-0 opacity-60"
78
+ />
79
+ </template>
80
+ </nav>
81
+
82
+ <!-- Full-trail variant -->
11
83
  <UBreadcrumb
12
- v-if="items.length > 0"
84
+ v-else-if="items.length > 0"
13
85
  :items="items"
14
86
  :ui="{ root: 'min-w-0', list: 'min-w-0 flex-nowrap', item: 'truncate', link: 'truncate' }"
15
87
  />
@@ -3,9 +3,15 @@ type __VLS_Props = {
3
3
  docId: string | null | undefined;
4
4
  /** Maximum ancestors to walk (default 8) */
5
5
  maxDepth?: number;
6
+ /**
7
+ * Collapse the middle into a "…" overflow menu once the trail exceeds this
8
+ * many crumbs. 0 (default) renders the full trail with no collapsing.
9
+ */
10
+ maxVisible?: number;
6
11
  };
7
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
13
  maxDepth: number;
14
+ maxVisible: number;
9
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
16
  declare const _default: typeof __VLS_export;
11
17
  export default _default;
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ /** Hub document id (informational — the panel reflects the active server). */
3
+ docId?: string;
4
+ };
5
+ declare var __VLS_96: "storage" | "users" | "members" | "invites", __VLS_97: {};
6
+ type __VLS_Slots = {} & {
7
+ [K in NonNullable<typeof __VLS_96>]?: (props: typeof __VLS_97) => any;
8
+ };
9
+ declare const __VLS_base: 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>;
10
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
11
+ declare const _default: typeof __VLS_export;
12
+ export default _default;
13
+ type __VLS_WithSlots<T, S> = T & {
14
+ new (): {
15
+ $slots: S;
16
+ };
17
+ };
@@ -0,0 +1,253 @@
1
+ <script setup>
2
+ import { computed, onMounted, reactive, ref, useSlots, watch } from "vue";
3
+ import { useAbracadabra } from "../../composables/useAbracadabra";
4
+ defineProps({
5
+ docId: { type: String, required: false }
6
+ });
7
+ const slots = useSlots();
8
+ const { client, status, synced, effectiveRole, currentServerUrl, reconnect } = useAbracadabra();
9
+ const ROLE_LEVELS = {
10
+ observer: 0,
11
+ viewer: 1,
12
+ editor: 2,
13
+ owner: 3,
14
+ admin: 4,
15
+ service: 5
16
+ };
17
+ const roleLevel = computed(() => ROLE_LEVELS[effectiveRole.value ?? ""] ?? 0);
18
+ const canEdit = computed(() => roleLevel.value >= 2);
19
+ const isAdmin = computed(() => roleLevel.value >= 4);
20
+ const roleBadgeColor = computed(() => {
21
+ const r = effectiveRole.value;
22
+ if (r === "admin" || r === "service") return "warning";
23
+ if (r === "owner") return "primary";
24
+ if (r === "editor") return "success";
25
+ return "neutral";
26
+ });
27
+ const statusDotClass = computed(() => {
28
+ if (status.value === "connected") return "bg-(--ui-success)";
29
+ if (status.value === "connecting") return "bg-(--ui-warning)";
30
+ return "bg-(--ui-error)";
31
+ });
32
+ const serverInfo = ref(null);
33
+ async function loadServerInfo() {
34
+ if (!client.value) return;
35
+ try {
36
+ serverInfo.value = await client.value.serverInfo();
37
+ } catch {
38
+ }
39
+ }
40
+ watch(client, loadServerInfo, { immediate: true });
41
+ const healthData = ref(null);
42
+ const healthLoading = ref(false);
43
+ async function checkHealth() {
44
+ if (!client.value) return;
45
+ healthLoading.value = true;
46
+ try {
47
+ healthData.value = await client.value.health();
48
+ } catch {
49
+ } finally {
50
+ healthLoading.value = false;
51
+ }
52
+ }
53
+ const expanded = reactive({ members: false, users: false, invites: false, storage: false });
54
+ const adminSections = computed(() => [
55
+ { key: "members", label: "Members", icon: "i-lucide-users", show: canEdit.value && !!slots.members },
56
+ { key: "users", label: "Users", icon: "i-lucide-shield-alert", show: isAdmin.value && !!slots.users },
57
+ { key: "invites", label: "Invites", icon: "i-lucide-ticket", show: isAdmin.value && !!slots.invites },
58
+ { key: "storage", label: "Storage", icon: "i-lucide-hard-drive", show: isAdmin.value && !!slots.storage }
59
+ ].filter((s) => s.show));
60
+ onMounted(loadServerInfo);
61
+ </script>
62
+
63
+ <template>
64
+ <div class="flex-1 overflow-y-auto px-4 py-3 space-y-3">
65
+ <!-- Connection — read-only URL (single-server) -->
66
+ <div class="rounded-lg bg-(--ui-bg-elevated) overflow-hidden">
67
+ <div class="flex items-center gap-1.5 px-3 py-2 border-b border-(--ui-border)">
68
+ <UIcon
69
+ name="i-lucide-plug"
70
+ class="size-3.5 text-(--ui-text-muted) shrink-0"
71
+ />
72
+ <span class="text-xs font-semibold uppercase tracking-wider text-(--ui-text-muted)">Connection</span>
73
+ </div>
74
+ <div class="px-3 py-3 space-y-1.5">
75
+ <div class="flex items-center gap-2">
76
+ <UIcon
77
+ name="i-lucide-server"
78
+ class="size-3.5 text-(--ui-text-dimmed) shrink-0"
79
+ />
80
+ <span class="text-sm truncate flex-1 min-w-0 text-(--ui-text)">{{ currentServerUrl || "\u2014" }}</span>
81
+ <UButton
82
+ v-if="status !== 'connected'"
83
+ icon="i-lucide-refresh-cw"
84
+ size="xs"
85
+ variant="ghost"
86
+ color="primary"
87
+ @click="reconnect"
88
+ />
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <!-- Status -->
94
+ <div class="rounded-lg bg-(--ui-bg-elevated) overflow-hidden">
95
+ <div class="flex items-center gap-1.5 px-3 py-2 border-b border-(--ui-border)">
96
+ <UIcon
97
+ name="i-lucide-info"
98
+ class="size-3.5 text-(--ui-text-muted) shrink-0"
99
+ />
100
+ <span class="text-xs font-semibold uppercase tracking-wider text-(--ui-text-muted)">Status</span>
101
+ </div>
102
+ <div class="divide-y divide-(--ui-border)">
103
+ <div class="flex items-center justify-between gap-2 px-3 py-2">
104
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
105
+ <UIcon
106
+ name="i-lucide-activity"
107
+ class="size-3.5 shrink-0"
108
+ />
109
+ <span class="truncate">Connection</span>
110
+ </div>
111
+ <div class="flex items-center gap-1.5 shrink-0">
112
+ <span :class="['size-1.5 rounded-full', statusDotClass]" />
113
+ <span class="text-sm capitalize">{{ status }}</span>
114
+ </div>
115
+ </div>
116
+ <div class="flex items-center justify-between gap-2 px-3 py-2">
117
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
118
+ <UIcon
119
+ name="i-lucide-shield"
120
+ class="size-3.5 shrink-0"
121
+ />
122
+ <span class="truncate">Your role</span>
123
+ </div>
124
+ <UBadge
125
+ v-if="effectiveRole"
126
+ :color="roleBadgeColor"
127
+ variant="subtle"
128
+ :label="effectiveRole"
129
+ size="sm"
130
+ class="capitalize shrink-0"
131
+ />
132
+ <span
133
+ v-else
134
+ class="text-sm text-(--ui-text-dimmed)"
135
+ >—</span>
136
+ </div>
137
+ <div class="flex items-center justify-between gap-2 px-3 py-2">
138
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
139
+ <UIcon
140
+ name="i-lucide-refresh-ccw"
141
+ class="size-3.5 shrink-0"
142
+ />
143
+ <span class="truncate">Synced</span>
144
+ </div>
145
+ <UBadge
146
+ :color="synced ? 'success' : 'neutral'"
147
+ variant="subtle"
148
+ :label="synced ? 'Yes' : 'No'"
149
+ :icon="synced ? 'i-lucide-check' : 'i-lucide-circle'"
150
+ size="sm"
151
+ class="shrink-0"
152
+ />
153
+ </div>
154
+ <div
155
+ v-if="serverInfo?.version"
156
+ class="flex items-center justify-between gap-2 px-3 py-2"
157
+ >
158
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
159
+ <UIcon
160
+ name="i-lucide-tag"
161
+ class="size-3.5 shrink-0"
162
+ />
163
+ <span class="truncate">Version</span>
164
+ </div>
165
+ <div class="flex items-center gap-1 shrink-0">
166
+ <UBadge
167
+ color="neutral"
168
+ variant="subtle"
169
+ :label="`v${serverInfo.version}`"
170
+ size="sm"
171
+ />
172
+ <UBadge
173
+ v-if="serverInfo.protocol_version"
174
+ color="neutral"
175
+ variant="outline"
176
+ :label="`p${serverInfo.protocol_version}`"
177
+ size="sm"
178
+ />
179
+ </div>
180
+ </div>
181
+ <div
182
+ v-if="serverInfo?.invite_only !== void 0"
183
+ class="flex items-center justify-between gap-2 px-3 py-2"
184
+ >
185
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
186
+ <UIcon
187
+ :name="serverInfo.invite_only ? 'i-lucide-lock' : 'i-lucide-globe'"
188
+ class="size-3.5 shrink-0"
189
+ />
190
+ <span class="truncate">Invite only</span>
191
+ </div>
192
+ <UBadge
193
+ :color="serverInfo.invite_only ? 'warning' : 'success'"
194
+ variant="subtle"
195
+ :label="serverInfo.invite_only ? 'Yes' : 'No'"
196
+ size="sm"
197
+ class="shrink-0"
198
+ />
199
+ </div>
200
+ <div class="flex items-center justify-between gap-2 px-3 py-2">
201
+ <div class="flex items-center gap-2 text-sm text-(--ui-text-muted) min-w-0">
202
+ <UIcon
203
+ name="i-lucide-heart-pulse"
204
+ class="size-3.5 shrink-0"
205
+ />
206
+ <span class="truncate">Health</span>
207
+ </div>
208
+ <UButton
209
+ :label="healthData ? `v${healthData.version}` : 'Check'"
210
+ color="neutral"
211
+ variant="ghost"
212
+ size="xs"
213
+ class="shrink-0"
214
+ :loading="healthLoading"
215
+ @click="checkHealth"
216
+ />
217
+ </div>
218
+ </div>
219
+ </div>
220
+
221
+ <!-- Admin surfaces — opt-in via slots, gated by role -->
222
+ <div
223
+ v-for="section in adminSections"
224
+ :key="section.key"
225
+ class="rounded-lg bg-(--ui-bg-elevated) overflow-hidden"
226
+ >
227
+ <button
228
+ type="button"
229
+ class="w-full flex items-center gap-1.5 px-3 py-2 border-b border-(--ui-border) hover:bg-(--ui-bg-accented)/40 transition-colors"
230
+ :class="{ 'border-b-0': !expanded[section.key] }"
231
+ @click="expanded[section.key] = !expanded[section.key]"
232
+ >
233
+ <UIcon
234
+ :name="section.icon"
235
+ class="size-3.5 text-(--ui-text-muted) shrink-0"
236
+ />
237
+ <span class="text-xs font-semibold uppercase tracking-wider text-(--ui-text-muted) flex-1 text-left">
238
+ {{ section.label }}
239
+ </span>
240
+ <UIcon
241
+ :name="expanded[section.key] ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
242
+ class="size-3.5 text-(--ui-text-dimmed)"
243
+ />
244
+ </button>
245
+ <div
246
+ v-if="expanded[section.key]"
247
+ class="px-3 py-3"
248
+ >
249
+ <slot :name="section.key" />
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </template>
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ /** Hub document id (informational — the panel reflects the active server). */
3
+ docId?: string;
4
+ };
5
+ declare var __VLS_96: "storage" | "users" | "members" | "invites", __VLS_97: {};
6
+ type __VLS_Slots = {} & {
7
+ [K in NonNullable<typeof __VLS_96>]?: (props: typeof __VLS_97) => any;
8
+ };
9
+ declare const __VLS_base: 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>;
10
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
11
+ declare const _default: typeof __VLS_export;
12
+ export default _default;
13
+ type __VLS_WithSlots<T, S> = T & {
14
+ new (): {
15
+ $slots: S;
16
+ };
17
+ };