@abraca/nuxt 2.0.10 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/module.d.mts +68 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +99 -4
  4. package/dist/runtime/components/ACodeEditor.d.vue.ts +26 -0
  5. package/dist/runtime/components/ACodeEditor.vue +268 -0
  6. package/dist/runtime/components/ACodeEditor.vue.d.ts +26 -0
  7. package/dist/runtime/components/ADocumentTree.vue +52 -20
  8. package/dist/runtime/components/AEditor.d.vue.ts +20 -13
  9. package/dist/runtime/components/AEditor.vue +55 -2
  10. package/dist/runtime/components/AEditor.vue.d.ts +20 -13
  11. package/dist/runtime/components/ANodePanel.vue +64 -60
  12. package/dist/runtime/components/ANotificationBell.d.vue.ts +1 -1
  13. package/dist/runtime/components/ANotificationBell.vue.d.ts +1 -1
  14. package/dist/runtime/components/ASpaceFormModal.d.vue.ts +2 -2
  15. package/dist/runtime/components/ASpaceFormModal.vue.d.ts +2 -2
  16. package/dist/runtime/components/aware/APresenceBlobs.d.vue.ts +29 -1
  17. package/dist/runtime/components/aware/APresenceBlobs.vue +54 -8
  18. package/dist/runtime/components/aware/APresenceBlobs.vue.d.ts +29 -1
  19. package/dist/runtime/components/aware/APresenceCursors.d.vue.ts +11 -0
  20. package/dist/runtime/components/aware/APresenceCursors.vue +74 -9
  21. package/dist/runtime/components/aware/APresenceCursors.vue.d.ts +11 -0
  22. package/dist/runtime/components/aware/AToggleGroup.d.vue.ts +28 -13
  23. package/dist/runtime/components/aware/AToggleGroup.vue +56 -20
  24. package/dist/runtime/components/aware/AToggleGroup.vue.d.ts +28 -13
  25. package/dist/runtime/components/docs/ADocsNavigation.d.vue.ts +1 -1
  26. package/dist/runtime/components/docs/ADocsNavigation.vue.d.ts +1 -1
  27. package/dist/runtime/components/docs/ADocsSearchButton.d.vue.ts +1 -1
  28. package/dist/runtime/components/docs/ADocsSearchButton.vue.d.ts +1 -1
  29. package/dist/runtime/components/docs/ADocsToc.d.vue.ts +2 -2
  30. package/dist/runtime/components/docs/ADocsToc.vue.d.ts +2 -2
  31. package/dist/runtime/components/editor/AEditorRedoButton.d.vue.ts +1 -1
  32. package/dist/runtime/components/editor/AEditorRedoButton.vue.d.ts +1 -1
  33. package/dist/runtime/components/editor/AEditorUndoButton.d.vue.ts +1 -1
  34. package/dist/runtime/components/editor/AEditorUndoButton.vue.d.ts +1 -1
  35. package/dist/runtime/components/editor/ANodeInlineLabel.d.vue.ts +1 -1
  36. package/dist/runtime/components/editor/ANodeInlineLabel.vue.d.ts +1 -1
  37. package/dist/runtime/components/registry/APluginBrowser.d.vue.ts +23 -0
  38. package/dist/runtime/components/registry/APluginBrowser.vue +155 -0
  39. package/dist/runtime/components/registry/APluginBrowser.vue.d.ts +23 -0
  40. package/dist/runtime/components/registry/APluginCapabilityDialog.d.vue.ts +17 -0
  41. package/dist/runtime/components/registry/APluginCapabilityDialog.vue +159 -0
  42. package/dist/runtime/components/registry/APluginCapabilityDialog.vue.d.ts +17 -0
  43. package/dist/runtime/components/registry/APluginCard.d.vue.ts +20 -0
  44. package/dist/runtime/components/registry/APluginCard.vue +91 -0
  45. package/dist/runtime/components/registry/APluginCard.vue.d.ts +20 -0
  46. package/dist/runtime/components/registry/APluginDetail.d.vue.ts +18 -0
  47. package/dist/runtime/components/registry/APluginDetail.vue +252 -0
  48. package/dist/runtime/components/registry/APluginDetail.vue.d.ts +18 -0
  49. package/dist/runtime/components/renderers/ACodeRenderer.d.vue.ts +15 -0
  50. package/dist/runtime/components/renderers/ACodeRenderer.vue +68 -0
  51. package/dist/runtime/components/renderers/ACodeRenderer.vue.d.ts +15 -0
  52. package/dist/runtime/components/renderers/AGraphRenderer.vue +416 -120
  53. package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +2 -2
  54. package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +2 -2
  55. package/dist/runtime/components/shell/ABreadcrumbForDoc.d.vue.ts +11 -0
  56. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue +16 -0
  57. package/dist/runtime/components/shell/ABreadcrumbForDoc.vue.d.ts +11 -0
  58. package/dist/runtime/components/shell/ASettingsSection.d.vue.ts +35 -0
  59. package/dist/runtime/components/shell/ASettingsSection.vue +26 -0
  60. package/dist/runtime/components/shell/ASettingsSection.vue.d.ts +35 -0
  61. package/dist/runtime/components/shell/ASidebar.d.vue.ts +1 -1
  62. package/dist/runtime/components/shell/ASidebar.vue.d.ts +1 -1
  63. package/dist/runtime/components/shell/AUserMenu.d.vue.ts +3 -0
  64. package/dist/runtime/components/shell/AUserMenu.vue +4 -0
  65. package/dist/runtime/components/shell/AUserMenu.vue.d.ts +3 -0
  66. package/dist/runtime/composables/useAbracadabraSchema.d.ts +83 -0
  67. package/dist/runtime/composables/useAbracadabraSchema.js +52 -0
  68. package/dist/runtime/composables/useAggregatedPresence.d.ts +1 -6
  69. package/dist/runtime/composables/useCalendarView.d.ts +1 -1
  70. package/dist/runtime/composables/useChat.js +1 -0
  71. package/dist/runtime/composables/useDocBreadcrumb.d.ts +21 -0
  72. package/dist/runtime/composables/useDocBreadcrumb.js +33 -0
  73. package/dist/runtime/composables/useDocEntryTyped.d.ts +60 -0
  74. package/dist/runtime/composables/useDocEntryTyped.js +70 -0
  75. package/dist/runtime/composables/useEditorDragHandle.js +18 -0
  76. package/dist/runtime/composables/useEditorSuggestions.js +2 -1
  77. package/dist/runtime/composables/useInstalledPlugins.d.ts +3 -21
  78. package/dist/runtime/composables/useInstalledPlugins.js +2 -12
  79. package/dist/runtime/composables/useMetaMenuItems.d.ts +21 -0
  80. package/dist/runtime/composables/useMetaMenuItems.js +115 -0
  81. package/dist/runtime/composables/useMetaValidator.d.ts +27 -0
  82. package/dist/runtime/composables/useMetaValidator.js +10 -0
  83. package/dist/runtime/composables/usePluginCatalog.d.ts +161 -0
  84. package/dist/runtime/composables/usePluginCatalog.js +234 -0
  85. package/dist/runtime/composables/useQuery.d.ts +79 -0
  86. package/dist/runtime/composables/useQuery.js +97 -0
  87. package/dist/runtime/composables/useSpaces.js +4 -5
  88. package/dist/runtime/composables/useTableView.d.ts +3 -3
  89. package/dist/runtime/composables/useTypedDoc.d.ts +97 -0
  90. package/dist/runtime/composables/useTypedDoc.js +114 -0
  91. package/dist/runtime/composables/useWebRTC.js +44 -5
  92. package/dist/runtime/extensions/document-meta.js +5 -0
  93. package/dist/runtime/extensions/timeline.d.ts +11 -0
  94. package/dist/runtime/extensions/timeline.js +52 -0
  95. package/dist/runtime/extensions/views/DocumentMetaView.d.vue.ts +4 -0
  96. package/dist/runtime/extensions/views/DocumentMetaView.vue +63 -0
  97. package/dist/runtime/extensions/views/DocumentMetaView.vue.d.ts +4 -0
  98. package/dist/runtime/extensions/views/TimelineItemView.d.vue.ts +4 -0
  99. package/dist/runtime/extensions/views/TimelineItemView.vue +131 -0
  100. package/dist/runtime/extensions/views/TimelineItemView.vue.d.ts +4 -0
  101. package/dist/runtime/extensions/views/TimelineView.d.vue.ts +9 -0
  102. package/dist/runtime/extensions/views/TimelineView.vue +29 -0
  103. package/dist/runtime/extensions/views/TimelineView.vue.d.ts +9 -0
  104. package/dist/runtime/locale.d.ts +2 -0
  105. package/dist/runtime/locale.js +2 -0
  106. package/dist/runtime/plugin-abracadabra.client.js +107 -6
  107. package/dist/runtime/plugin-registry.d.ts +11 -30
  108. package/dist/runtime/plugin-registry.js +2 -82
  109. package/dist/runtime/plugins/core.plugin.js +10 -4
  110. package/dist/runtime/server/api/_abracadabra/spaces.get.d.ts +1 -1
  111. package/dist/runtime/server/plugins/abracadabra-service.js +28 -0
  112. package/dist/runtime/server/utils/docCache.js +24 -3
  113. package/dist/runtime/server/utils/schemaServerSupport.d.ts +52 -0
  114. package/dist/runtime/server/utils/schemaServerSupport.js +51 -0
  115. package/dist/runtime/types.d.ts +63 -46
  116. package/dist/runtime/utils/docTypes.d.ts +15 -0
  117. package/dist/runtime/utils/docTypes.js +20 -0
  118. package/dist/runtime/utils/loadCodeMirror.d.ts +32 -0
  119. package/dist/runtime/utils/loadCodeMirror.js +65 -0
  120. package/dist/runtime/utils/markdownToYjs.d.ts +1 -23
  121. package/dist/runtime/utils/markdownToYjs.js +5 -440
  122. package/dist/runtime/utils/schemaSupport.d.ts +60 -0
  123. package/dist/runtime/utils/schemaSupport.js +40 -0
  124. package/dist/runtime/utils/yjsConvert.d.ts +1 -14
  125. package/dist/runtime/utils/yjsConvert.js +5 -331
  126. package/package.json +84 -23
@@ -17,8 +17,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
17
17
  onEditEnd?: (() => any) | undefined;
18
18
  }>, {
19
19
  disabled: boolean;
20
- variant: "label" | "title" | "name";
21
20
  placeholder: string;
21
+ variant: "label" | "title" | "name";
22
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
23
  declare const _default: typeof __VLS_export;
24
24
  export default _default;
@@ -0,0 +1,23 @@
1
+ import { type CatalogPlugin, type CatalogVersionDetail } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ /** Initial search query passed to the registry. */
4
+ initialSearch?: string;
5
+ /** Initial category filter. */
6
+ initialCategory?: string;
7
+ /**
8
+ * Abracadabra server URL — `<APluginBrowser>` fetches `/plugins/policy`
9
+ * from this on mount and renders policy-aware install affordances.
10
+ * When omitted, the policy is left at `null` and the UI behaves
11
+ * permissively (every plugin shows Install).
12
+ */
13
+ serverUrl?: string;
14
+ };
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
16
+ open: (plugin: CatalogPlugin) => any;
17
+ installed: (detail: CatalogVersionDetail) => any;
18
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ onOpen?: ((plugin: CatalogPlugin) => any) | undefined;
20
+ onInstalled?: ((detail: CatalogVersionDetail) => any) | undefined;
21
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,155 @@
1
+ <script setup>
2
+ import { computed, onMounted, ref, watch } from "vue";
3
+ import { useRuntimeConfig, useToast } from "#imports";
4
+ import { usePluginCatalog } from "../../composables/usePluginCatalog";
5
+ const props = defineProps({
6
+ initialSearch: { type: String, required: false },
7
+ initialCategory: { type: String, required: false },
8
+ serverUrl: { type: String, required: false }
9
+ });
10
+ const emit = defineEmits(["open", "installed"]);
11
+ const registry = usePluginCatalog();
12
+ const search = ref(props.initialSearch ?? "");
13
+ const category = ref(props.initialCategory ?? "");
14
+ const showDialog = ref(false);
15
+ const pendingDetail = ref(null);
16
+ const toast = useToast();
17
+ const cfg = useRuntimeConfig();
18
+ const resolvedServerUrl = computed(() => {
19
+ if (props.serverUrl) return props.serverUrl;
20
+ const moduleUrl = cfg.public.abracadabra?.url;
21
+ const flatUrl = cfg.public.abracadabraUrl;
22
+ return moduleUrl ?? flatUrl ?? null;
23
+ });
24
+ async function refresh() {
25
+ try {
26
+ await registry.list({ search: search.value || void 0, category: category.value || void 0 });
27
+ } catch (e) {
28
+ toast.add({ title: "Could not load plugin registry", description: e.message, color: "error" });
29
+ }
30
+ }
31
+ async function startInstall(id) {
32
+ try {
33
+ pendingDetail.value = await registry.getLatest(id);
34
+ showDialog.value = true;
35
+ } catch (e) {
36
+ toast.add({ title: `Could not load plugin ${id}`, description: e.message, color: "error" });
37
+ }
38
+ }
39
+ async function confirmInstall(detail) {
40
+ try {
41
+ await registry.install(detail.plugin_id);
42
+ toast.add({
43
+ title: `${detail.manifest.name ?? detail.plugin_id} installed`,
44
+ description: `v${detail.version} \u2014 reload to activate`,
45
+ color: "success"
46
+ });
47
+ emit("installed", detail);
48
+ } catch (e) {
49
+ toast.add({ title: "Install failed", description: e.message, color: "error" });
50
+ }
51
+ }
52
+ async function refreshPolicy() {
53
+ if (!resolvedServerUrl.value) return;
54
+ try {
55
+ await registry.loadPolicy(resolvedServerUrl.value);
56
+ } catch {
57
+ }
58
+ }
59
+ onMounted(() => {
60
+ refresh();
61
+ refreshPolicy();
62
+ });
63
+ watch([search, category], refresh);
64
+ watch(resolvedServerUrl, refreshPolicy);
65
+ </script>
66
+
67
+ <template>
68
+ <div class="flex h-full flex-col gap-4">
69
+ <div
70
+ v-if="registry.policy.value && !registry.policy.value.allow_user_install"
71
+ class="flex items-center gap-2 rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-sm text-warning"
72
+ >
73
+ <UIcon name="i-lucide-shield-alert" class="shrink-0" />
74
+ <span>
75
+ Your server restricts installs to its allowlist
76
+ ({{ registry.policy.value.allowlist.length }} plugins).
77
+ Other plugins are visible for browsing but cannot be installed.
78
+ </span>
79
+ </div>
80
+
81
+ <div class="flex flex-wrap items-center gap-2">
82
+ <UInput
83
+ v-model="search"
84
+ placeholder="Search plugins…"
85
+ icon="i-lucide-search"
86
+ class="flex-1 min-w-[12rem]"
87
+ size="md"
88
+ />
89
+ <USelect
90
+ v-if="registry.categories.value.length > 0"
91
+ v-model="category"
92
+ :items="['', ...registry.categories.value].map((c) => ({ label: c || 'All categories', value: c }))"
93
+ size="md"
94
+ class="w-48"
95
+ />
96
+ <UButton
97
+ icon="i-lucide-refresh-cw"
98
+ size="md"
99
+ variant="ghost"
100
+ color="neutral"
101
+ :loading="registry.isLoading.value"
102
+ @click="refresh"
103
+ />
104
+ </div>
105
+
106
+ <div
107
+ v-if="registry.isLoading.value && registry.plugins.value.length === 0"
108
+ class="flex flex-1 items-center justify-center text-sm text-muted"
109
+ >
110
+ Loading registry…
111
+ </div>
112
+
113
+ <div
114
+ v-else-if="registry.error.value && registry.plugins.value.length === 0"
115
+ class="flex flex-1 flex-col items-center justify-center gap-2 text-center"
116
+ >
117
+ <UIcon name="i-lucide-wifi-off" class="text-2xl text-muted" />
118
+ <p class="text-sm text-muted">
119
+ Couldn't reach the plugin registry.
120
+ </p>
121
+ <p class="font-mono text-xs text-muted">
122
+ {{ registry.error.value.message }}
123
+ </p>
124
+ <UButton size="sm" variant="soft" label="Retry" @click="refresh" />
125
+ </div>
126
+
127
+ <div
128
+ v-else-if="registry.plugins.value.length === 0"
129
+ class="flex flex-1 items-center justify-center text-sm text-muted"
130
+ >
131
+ No plugins match.
132
+ </div>
133
+
134
+ <div
135
+ v-else
136
+ class="grid flex-1 auto-rows-min gap-3 overflow-auto sm:grid-cols-2 lg:grid-cols-3"
137
+ >
138
+ <APluginCard
139
+ v-for="plugin in registry.plugins.value"
140
+ :key="plugin.id"
141
+ :plugin="plugin"
142
+ :installed="registry.isInstalled(plugin.id)"
143
+ :decision="registry.policyDecisionFor(plugin.id)"
144
+ @open="emit('open', plugin)"
145
+ @install="startInstall"
146
+ />
147
+ </div>
148
+
149
+ <APluginCapabilityDialog
150
+ v-model="showDialog"
151
+ :detail="pendingDetail"
152
+ @confirm="confirmInstall"
153
+ />
154
+ </div>
155
+ </template>
@@ -0,0 +1,23 @@
1
+ import { type CatalogPlugin, type CatalogVersionDetail } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ /** Initial search query passed to the registry. */
4
+ initialSearch?: string;
5
+ /** Initial category filter. */
6
+ initialCategory?: string;
7
+ /**
8
+ * Abracadabra server URL — `<APluginBrowser>` fetches `/plugins/policy`
9
+ * from this on mount and renders policy-aware install affordances.
10
+ * When omitted, the policy is left at `null` and the UI behaves
11
+ * permissively (every plugin shows Install).
12
+ */
13
+ serverUrl?: string;
14
+ };
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
16
+ open: (plugin: CatalogPlugin) => any;
17
+ installed: (detail: CatalogVersionDetail) => any;
18
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ onOpen?: ((plugin: CatalogPlugin) => any) | undefined;
20
+ onInstalled?: ((detail: CatalogVersionDetail) => any) | undefined;
21
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import type { CatalogVersionDetail } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ modelValue: boolean;
4
+ /** The version whose capabilities are being disclosed. */
5
+ detail: CatalogVersionDetail | null;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
8
+ cancel: () => any;
9
+ "update:modelValue": (open: boolean) => any;
10
+ confirm: (detail: CatalogVersionDetail) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ onCancel?: (() => any) | undefined;
13
+ "onUpdate:modelValue"?: ((open: boolean) => any) | undefined;
14
+ onConfirm?: ((detail: CatalogVersionDetail) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,159 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ const props = defineProps({
4
+ modelValue: { type: Boolean, required: true },
5
+ detail: { type: [Object, null], required: true }
6
+ });
7
+ const emit = defineEmits(["update:modelValue", "confirm", "cancel"]);
8
+ const isOpen = computed({
9
+ get: () => props.modelValue,
10
+ set: (v) => emit("update:modelValue", v)
11
+ });
12
+ const required = computed(
13
+ () => [...props.detail?.manifest.capabilities.required ?? []]
14
+ );
15
+ const optional = computed(
16
+ () => [...props.detail?.manifest.capabilities.optional ?? []]
17
+ );
18
+ const title = computed(() => props.detail?.manifest.name ?? props.detail?.manifest.id ?? "");
19
+ function describe(cap) {
20
+ if (cap === "network" || cap.startsWith("network:")) {
21
+ const host = cap === "network" ? "any host" : cap.slice("network:".length);
22
+ return { label: `Outbound network \u2014 ${host}`, icon: "i-lucide-globe", severity: "high" };
23
+ }
24
+ if (cap === "fs:read") return { label: "Read local filesystem", icon: "i-lucide-folder-open", severity: "high" };
25
+ if (cap === "fs:write") return { label: "Write local filesystem", icon: "i-lucide-folder-edit", severity: "high" };
26
+ if (cap === "clipboard:read") return { label: "Read clipboard", icon: "i-lucide-clipboard", severity: "medium" };
27
+ if (cap === "clipboard:write") return { label: "Write clipboard", icon: "i-lucide-clipboard-paste", severity: "medium" };
28
+ if (cap === "doc-write") return { label: "Modify documents", icon: "i-lucide-pencil", severity: "medium" };
29
+ if (cap === "doc-read") return { label: "Read documents", icon: "i-lucide-eye", severity: "low" };
30
+ if (cap === "awareness") return { label: "Broadcast presence", icon: "i-lucide-users", severity: "low" };
31
+ if (cap === "server-runner") return { label: "Run server-side background tasks", icon: "i-lucide-server", severity: "high" };
32
+ if (cap === "chat:send") return { label: "Send chat messages", icon: "i-lucide-message-circle", severity: "medium" };
33
+ if (cap === "chat:read") return { label: "Read chat history", icon: "i-lucide-message-square", severity: "low" };
34
+ if (cap === "notifications:show") return { label: "Show notifications", icon: "i-lucide-bell", severity: "low" };
35
+ if (cap === "command-palette") return { label: "Add command-palette entries", icon: "i-lucide-command", severity: "low" };
36
+ if (cap === "toolbar") return { label: "Add editor toolbar items", icon: "i-lucide-bold", severity: "low" };
37
+ if (cap === "bubble-menu") return { label: "Add bubble-menu items", icon: "i-lucide-square-dot", severity: "low" };
38
+ if (cap === "slash-command") return { label: "Add slash commands", icon: "i-lucide-slash", severity: "low" };
39
+ if (cap === "drag-handle") return { label: "Add drag-handle actions", icon: "i-lucide-grip-vertical", severity: "low" };
40
+ if (cap.startsWith("page-type:")) {
41
+ return { label: `Register page type "${cap.slice("page-type:".length)}"`, icon: "i-lucide-layout", severity: "low" };
42
+ }
43
+ return { label: cap, icon: "i-lucide-shield", severity: "low" };
44
+ }
45
+ const severityOrder = { high: 0, medium: 1, low: 2 };
46
+ const requiredSorted = computed(
47
+ () => [...required.value].map((c) => ({ cap: c, ...describe(c) })).sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity])
48
+ );
49
+ const optionalSorted = computed(
50
+ () => [...optional.value].map((c) => ({ cap: c, ...describe(c) })).sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity])
51
+ );
52
+ function badgeColor(s) {
53
+ return s === "high" ? "error" : s === "medium" ? "warning" : "neutral";
54
+ }
55
+ function confirm() {
56
+ if (props.detail) emit("confirm", props.detail);
57
+ isOpen.value = false;
58
+ }
59
+ function cancel() {
60
+ emit("cancel");
61
+ isOpen.value = false;
62
+ }
63
+ </script>
64
+
65
+ <template>
66
+ <UModal v-model:open="isOpen" :title="`Install ${title}?`" :ui="{ content: 'max-w-2xl' }">
67
+ <template #body>
68
+ <div v-if="!detail" class="py-6 text-center text-sm text-muted">
69
+ Loading capability disclosure…
70
+ </div>
71
+ <div v-else class="space-y-5">
72
+ <div class="rounded-md border border-default bg-elevated p-3 text-sm">
73
+ <div class="flex items-center gap-2">
74
+ <UIcon name="i-lucide-package" class="text-muted" />
75
+ <span class="font-mono text-xs text-muted">
76
+ {{ detail.plugin_id }}@{{ detail.version }}
77
+ </span>
78
+ </div>
79
+ <p class="mt-2 text-sm text-default">
80
+ {{ detail.manifest.description }}
81
+ </p>
82
+ <div class="mt-2 flex items-center gap-1.5 text-xs text-muted">
83
+ <UIcon name="i-lucide-shield-check" />
84
+ <span class="font-mono">{{ detail.integrity.slice(0, 24) }}…</span>
85
+ </div>
86
+ </div>
87
+
88
+ <div>
89
+ <h4 class="mb-2 text-sm font-semibold text-highlighted">
90
+ Required capabilities
91
+ </h4>
92
+ <p class="mb-3 text-xs text-muted">
93
+ By installing this plugin, you grant it the permissions listed below.
94
+ High-severity items are highlighted.
95
+ </p>
96
+ <ul class="space-y-1.5">
97
+ <li
98
+ v-for="item in requiredSorted"
99
+ :key="item.cap"
100
+ class="flex items-center gap-2 rounded-md border border-default px-3 py-2"
101
+ >
102
+ <UIcon :name="item.icon" class="text-muted" />
103
+ <span class="text-sm text-default">{{ item.label }}</span>
104
+ <UBadge
105
+ size="xs"
106
+ variant="subtle"
107
+ :color="badgeColor(item.severity)"
108
+ class="ml-auto capitalize"
109
+ >
110
+ {{ item.severity }}
111
+ </UBadge>
112
+ </li>
113
+ </ul>
114
+ </div>
115
+
116
+ <div v-if="optionalSorted.length > 0">
117
+ <h4 class="mb-2 text-sm font-semibold text-highlighted">
118
+ Optional capabilities
119
+ </h4>
120
+ <p class="mb-3 text-xs text-muted">
121
+ These are requested but not required. You can grant them later from
122
+ the plugin's settings.
123
+ </p>
124
+ <ul class="space-y-1.5">
125
+ <li
126
+ v-for="item in optionalSorted"
127
+ :key="item.cap"
128
+ class="flex items-center gap-2 rounded-md border border-default px-3 py-2"
129
+ >
130
+ <UIcon :name="item.icon" class="text-muted" />
131
+ <span class="text-sm text-default">{{ item.label }}</span>
132
+ <UBadge
133
+ size="xs"
134
+ variant="subtle"
135
+ :color="badgeColor(item.severity)"
136
+ class="ml-auto capitalize"
137
+ >
138
+ {{ item.severity }}
139
+ </UBadge>
140
+ </li>
141
+ </ul>
142
+ </div>
143
+ </div>
144
+ </template>
145
+
146
+ <template #footer>
147
+ <div class="flex w-full items-center justify-end gap-2">
148
+ <UButton variant="ghost" color="neutral" label="Cancel" @click="cancel" />
149
+ <UButton
150
+ color="primary"
151
+ icon="i-lucide-download"
152
+ label="Install"
153
+ :disabled="!detail"
154
+ @click="confirm"
155
+ />
156
+ </div>
157
+ </template>
158
+ </UModal>
159
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { CatalogVersionDetail } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ modelValue: boolean;
4
+ /** The version whose capabilities are being disclosed. */
5
+ detail: CatalogVersionDetail | null;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
8
+ cancel: () => any;
9
+ "update:modelValue": (open: boolean) => any;
10
+ confirm: (detail: CatalogVersionDetail) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ onCancel?: (() => any) | undefined;
13
+ "onUpdate:modelValue"?: ((open: boolean) => any) | undefined;
14
+ onConfirm?: ((detail: CatalogVersionDetail) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,20 @@
1
+ import type { CatalogPlugin, PolicyDecision } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ plugin: CatalogPlugin;
4
+ installed?: boolean;
5
+ /**
6
+ * Server policy decision for this plugin. Drives the Install button's
7
+ * visibility + label + tooltip. When omitted, defaults to permissive
8
+ * (policy hasn't been fetched yet) — same UX as today.
9
+ */
10
+ decision?: PolicyDecision;
11
+ };
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
13
+ open: (id: string) => any;
14
+ install: (id: string) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onOpen?: ((id: string) => any) | undefined;
17
+ onInstall?: ((id: string) => any) | undefined;
18
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,91 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ const props = defineProps({
4
+ plugin: { type: Object, required: true },
5
+ installed: { type: Boolean, required: false },
6
+ decision: { type: Object, required: false }
7
+ });
8
+ const emit = defineEmits(["open", "install"]);
9
+ const decisionState = computed(() => props.decision?.state ?? "unknown");
10
+ const title = computed(() => props.plugin.name ?? props.plugin.id);
11
+ const pricing = computed(() => {
12
+ if (props.plugin.pricing === "paid") return { label: "Paid", color: "warning" };
13
+ if (props.plugin.pricing === "optional") return { label: "Optional payments", color: "info" };
14
+ return { label: "Free", color: "neutral" };
15
+ });
16
+ </script>
17
+
18
+ <template>
19
+ <div
20
+ class="group flex h-full cursor-pointer flex-col gap-3 rounded-lg border border-default bg-default p-4 transition-colors hover:border-primary-300 dark:hover:border-primary-700"
21
+ role="button"
22
+ tabindex="0"
23
+ @click="emit('open', plugin.id)"
24
+ @keydown.enter="emit('open', plugin.id)"
25
+ >
26
+ <div class="flex items-start justify-between gap-2">
27
+ <div class="min-w-0 flex-1">
28
+ <div class="flex items-center gap-2">
29
+ <h3 class="truncate text-base font-semibold text-highlighted">
30
+ {{ title }}
31
+ </h3>
32
+ <UBadge size="xs" :color="pricing.color" variant="subtle">
33
+ {{ pricing.label }}
34
+ </UBadge>
35
+ </div>
36
+ <p class="mt-0.5 truncate text-xs text-muted">
37
+ {{ plugin.id }}<span v-if="plugin.latest_version"> · v{{ plugin.latest_version }}</span>
38
+ </p>
39
+ </div>
40
+ <UButton
41
+ v-if="installed"
42
+ size="xs"
43
+ variant="soft"
44
+ color="success"
45
+ icon="i-lucide-check"
46
+ disabled
47
+ label="Installed"
48
+ />
49
+ <UBadge
50
+ v-else-if="decisionState === 'blocked'"
51
+ size="xs"
52
+ variant="subtle"
53
+ color="warning"
54
+ icon="i-lucide-shield-alert"
55
+ :title="decision?.reason"
56
+ label="Not allowed"
57
+ />
58
+ <UButton
59
+ v-else
60
+ size="xs"
61
+ variant="solid"
62
+ :color="decisionState === 'allowed' ? 'success' : 'primary'"
63
+ :icon="decisionState === 'allowed' ? 'i-lucide-shield-check' : 'i-lucide-plus'"
64
+ :label="decisionState === 'allowed' ? 'Approved' : 'Install'"
65
+ @click.stop="emit('install', plugin.id)"
66
+ />
67
+ </div>
68
+
69
+ <p class="line-clamp-3 text-sm text-default">
70
+ {{ plugin.description }}
71
+ </p>
72
+
73
+ <div class="mt-auto flex flex-wrap items-center gap-1.5">
74
+ <UBadge
75
+ v-for="cat in plugin.categories.slice(0, 3)"
76
+ :key="cat"
77
+ size="xs"
78
+ variant="outline"
79
+ color="neutral"
80
+ >
81
+ {{ cat }}
82
+ </UBadge>
83
+ <span
84
+ v-if="plugin.owner_login"
85
+ class="ml-auto text-xs text-muted"
86
+ >
87
+ by {{ plugin.owner_login }}
88
+ </span>
89
+ </div>
90
+ </div>
91
+ </template>
@@ -0,0 +1,20 @@
1
+ import type { CatalogPlugin, PolicyDecision } from '../../composables/usePluginCatalog.js';
2
+ type __VLS_Props = {
3
+ plugin: CatalogPlugin;
4
+ installed?: boolean;
5
+ /**
6
+ * Server policy decision for this plugin. Drives the Install button's
7
+ * visibility + label + tooltip. When omitted, defaults to permissive
8
+ * (policy hasn't been fetched yet) — same UX as today.
9
+ */
10
+ decision?: PolicyDecision;
11
+ };
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
13
+ open: (id: string) => any;
14
+ install: (id: string) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onOpen?: ((id: string) => any) | undefined;
17
+ onInstall?: ((id: string) => any) | undefined;
18
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { type CatalogVersionDetail } from '../../composables/usePluginCatalog.js';
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
4
+ declare const __VLS_export: import("vue").DefineComponent<{
5
+ id: string;
6
+ /** See `<APluginBrowser>` — same fallback chain. */
7
+ serverUrl?: string;
8
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
9
+ installed: (detail: CatalogVersionDetail) => any;
10
+ back: () => any;
11
+ }, string, import("vue").PublicProps, Readonly<{
12
+ id: string;
13
+ /** See `<APluginBrowser>` — same fallback chain. */
14
+ serverUrl?: string;
15
+ }> & Readonly<{
16
+ onInstalled?: ((detail: CatalogVersionDetail) => any) | undefined;
17
+ onBack?: (() => any) | undefined;
18
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;