@nwire/studio 0.9.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.
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/components.json +19 -0
- package/index.html +12 -0
- package/package.json +66 -0
- package/src/App.vue +305 -0
- package/src/components/EmptyState.stories.ts +53 -0
- package/src/components/EmptyState.vue +28 -0
- package/src/components/ErrorBoundary.vue +60 -0
- package/src/components/FilterInput.stories.ts +32 -0
- package/src/components/FilterInput.vue +33 -0
- package/src/components/JsonView.stories.ts +38 -0
- package/src/components/JsonView.vue +34 -0
- package/src/components/KindBadge.stories.ts +72 -0
- package/src/components/KindBadge.vue +59 -0
- package/src/components/ListRow.stories.ts +56 -0
- package/src/components/ListRow.vue +48 -0
- package/src/components/MasterDetail.stories.ts +74 -0
- package/src/components/MasterDetail.vue +35 -0
- package/src/components/MonacoViewer.vue +143 -0
- package/src/components/PageHeader.stories.ts +45 -0
- package/src/components/PageHeader.vue +46 -0
- package/src/components/SchemaNode.vue +208 -0
- package/src/components/SchemaTree.vue +65 -0
- package/src/components/SourceDrawer.vue +136 -0
- package/src/components/SourcePill.vue +103 -0
- package/src/components/__tests__/EmptyState.test.ts +28 -0
- package/src/components/__tests__/ErrorBoundary.test.ts +52 -0
- package/src/components/__tests__/FilterInput.test.ts +38 -0
- package/src/components/__tests__/JsonView.test.ts +33 -0
- package/src/components/__tests__/KindBadge.test.ts +39 -0
- package/src/components/__tests__/ListRow.test.ts +39 -0
- package/src/components/__tests__/MasterDetail.test.ts +40 -0
- package/src/components/__tests__/PageHeader.test.ts +42 -0
- package/src/components/index.ts +17 -0
- package/src/components/ui/badge/Badge.vue +17 -0
- package/src/components/ui/badge/index.ts +25 -0
- package/src/components/ui/button/Button.vue +28 -0
- package/src/components/ui/button/index.ts +34 -0
- package/src/components/ui/card/Card.vue +14 -0
- package/src/components/ui/card/CardContent.vue +14 -0
- package/src/components/ui/card/CardDescription.vue +14 -0
- package/src/components/ui/card/CardFooter.vue +14 -0
- package/src/components/ui/card/CardHeader.vue +14 -0
- package/src/components/ui/card/CardTitle.vue +14 -0
- package/src/components/ui/card/index.ts +6 -0
- package/src/components/ui/dialog/Dialog.vue +15 -0
- package/src/components/ui/dialog/DialogClose.vue +12 -0
- package/src/components/ui/dialog/DialogContent.vue +47 -0
- package/src/components/ui/dialog/DialogDescription.vue +22 -0
- package/src/components/ui/dialog/DialogFooter.vue +12 -0
- package/src/components/ui/dialog/DialogHeader.vue +14 -0
- package/src/components/ui/dialog/DialogScrollContent.vue +60 -0
- package/src/components/ui/dialog/DialogTitle.vue +22 -0
- package/src/components/ui/dialog/DialogTrigger.vue +12 -0
- package/src/components/ui/dialog/index.ts +9 -0
- package/src/components/ui/input/Input.vue +32 -0
- package/src/components/ui/input/index.ts +1 -0
- package/src/components/ui/scroll-area/ScrollArea.vue +22 -0
- package/src/components/ui/scroll-area/ScrollBar.vue +32 -0
- package/src/components/ui/scroll-area/index.ts +2 -0
- package/src/components/ui/separator/Separator.vue +27 -0
- package/src/components/ui/separator/index.ts +1 -0
- package/src/components/ui/skeleton/Skeleton.vue +14 -0
- package/src/components/ui/skeleton/index.ts +1 -0
- package/src/components/ui/tabs/Tabs.vue +15 -0
- package/src/components/ui/tabs/TabsContent.vue +25 -0
- package/src/components/ui/tabs/TabsList.vue +25 -0
- package/src/components/ui/tabs/TabsTrigger.vue +29 -0
- package/src/components/ui/tabs/index.ts +4 -0
- package/src/components/ui/tooltip/Tooltip.vue +15 -0
- package/src/components/ui/tooltip/TooltipContent.vue +40 -0
- package/src/components/ui/tooltip/TooltipProvider.vue +12 -0
- package/src/components/ui/tooltip/TooltipTrigger.vue +12 -0
- package/src/components/ui/tooltip/index.ts +4 -0
- package/src/composables/useCopy.ts +31 -0
- package/src/lib/__tests__/normalize-cache.test.ts +104 -0
- package/src/lib/cache.ts +334 -0
- package/src/lib/normalize-cache.ts +92 -0
- package/src/lib/project-catalog.ts +125 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.ts +112 -0
- package/src/pages/Actions.vue +180 -0
- package/src/pages/Commands.vue +262 -0
- package/src/pages/Dispatch.vue +431 -0
- package/src/pages/Events.vue +166 -0
- package/src/pages/Home.stories.ts +47 -0
- package/src/pages/Home.vue +485 -0
- package/src/pages/Hooks.vue +297 -0
- package/src/pages/Live.vue +249 -0
- package/src/pages/Modules.vue +174 -0
- package/src/pages/Overview.vue +159 -0
- package/src/pages/Plugins.stories.ts +44 -0
- package/src/pages/Plugins.vue +403 -0
- package/src/pages/Projects.vue +272 -0
- package/src/pages/Run.vue +479 -0
- package/src/pages/Topology.vue +164 -0
- package/src/pages/Trace.vue +511 -0
- package/src/pages/TraceNode.vue +166 -0
- package/src/pages/Workflows.vue +191 -0
- package/src/pages/__tests__/Actions.test.ts +98 -0
- package/src/pages/__tests__/Home.test.ts +98 -0
- package/src/pages/__tests__/Hooks.test.ts +119 -0
- package/src/pages/__tests__/Plugins.test.ts +80 -0
- package/src/style.css +40 -0
- package/tsconfig.json +20 -0
- package/vite.config.ts +892 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { nextTick, onMounted, ref, watch } from "vue";
|
|
3
|
+
import { useRoute, useRouter } from "vue-router";
|
|
4
|
+
import { useCache } from "@/lib/cache";
|
|
5
|
+
import { Boxes, ArrowDownRight, ArrowUpRight } from "lucide-vue-next";
|
|
6
|
+
|
|
7
|
+
const route = useRoute();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const { cache } = useCache();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Preselect — /modules?name=<moduleName>. The page is grid-of-cards (no
|
|
13
|
+
* master/detail), so "preselect" means: highlight the card + scroll it
|
|
14
|
+
* into view. First module with the matching name wins (modules are unique
|
|
15
|
+
* within an app, and module-name collisions across apps are rare in
|
|
16
|
+
* practice — if it ever happens the operator can disambiguate visually).
|
|
17
|
+
*/
|
|
18
|
+
const selected = ref<string | null>(null);
|
|
19
|
+
|
|
20
|
+
function applyQueryPreselect(): Promise<void> | void {
|
|
21
|
+
const name = route.query.name;
|
|
22
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
23
|
+
selected.value = null;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const found = cache.value?.modules.find((m) => m.name === name);
|
|
27
|
+
if (!found) return;
|
|
28
|
+
const key = `${found.app}::${found.name}`;
|
|
29
|
+
selected.value = key;
|
|
30
|
+
return nextTick(() => {
|
|
31
|
+
const el = document.querySelector(`[data-module-key="${key}"]`);
|
|
32
|
+
if (el instanceof HTMLElement) {
|
|
33
|
+
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
void applyQueryPreselect();
|
|
40
|
+
});
|
|
41
|
+
watch(
|
|
42
|
+
() => route.query.name,
|
|
43
|
+
() => {
|
|
44
|
+
void applyQueryPreselect();
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
watch(
|
|
48
|
+
() => cache.value,
|
|
49
|
+
() => {
|
|
50
|
+
void applyQueryPreselect();
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const moduleKey = (m: { app: string; name: string }) => `${m.app}::${m.name}`;
|
|
55
|
+
|
|
56
|
+
function openEvent(name: string): void {
|
|
57
|
+
void router.push({ path: "/events", query: { name } });
|
|
58
|
+
}
|
|
59
|
+
function openAction(name: string): void {
|
|
60
|
+
void router.push({ path: "/actions", query: { name } });
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<div v-if="cache" class="p-6 space-y-3">
|
|
66
|
+
<div>
|
|
67
|
+
<h1 class="text-2xl font-semibold tracking-tight">Modules</h1>
|
|
68
|
+
<p class="text-sm text-zinc-500 mt-1">Bounded contexts and their dependency graph</p>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
|
|
72
|
+
<div
|
|
73
|
+
v-for="m in cache.modules"
|
|
74
|
+
:key="moduleKey(m)"
|
|
75
|
+
:data-module-key="moduleKey(m)"
|
|
76
|
+
:data-testid="`module-card-${m.name}`"
|
|
77
|
+
class="rounded-lg border bg-zinc-900/30 px-4 py-3 space-y-2 transition-colors"
|
|
78
|
+
:class="
|
|
79
|
+
selected === moduleKey(m)
|
|
80
|
+
? 'border-emerald-500/70 ring-1 ring-emerald-500/40'
|
|
81
|
+
: 'border-zinc-800'
|
|
82
|
+
"
|
|
83
|
+
>
|
|
84
|
+
<div class="flex items-center justify-between">
|
|
85
|
+
<div class="flex items-center gap-2">
|
|
86
|
+
<Boxes class="w-4 h-4 text-emerald-400" />
|
|
87
|
+
<span class="font-mono font-medium">{{ m.name }}</span>
|
|
88
|
+
<span
|
|
89
|
+
class="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded bg-zinc-800 text-zinc-400"
|
|
90
|
+
>
|
|
91
|
+
{{ m.app }}
|
|
92
|
+
</span>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="text-xs text-zinc-500 tabular-nums" data-testid="module-counts">
|
|
95
|
+
{{ m.counts.actions }}A · {{ m.counts.events }}E · {{ m.counts.actors }}@ ·
|
|
96
|
+
{{ m.counts.projections }}P · {{ m.counts.queries }}Q · {{ m.counts.workflows }}W
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div v-if="m.provides.events.length || m.provides.actions.length" class="text-xs">
|
|
101
|
+
<div class="flex items-center gap-1 text-zinc-500 mb-1">
|
|
102
|
+
<ArrowUpRight class="w-3 h-3 text-emerald-400" />
|
|
103
|
+
Provides
|
|
104
|
+
</div>
|
|
105
|
+
<div class="flex flex-wrap gap-1 pl-4">
|
|
106
|
+
<button
|
|
107
|
+
v-for="e in m.provides.events"
|
|
108
|
+
:key="`p-e-${e}`"
|
|
109
|
+
type="button"
|
|
110
|
+
class="font-mono text-[10px] px-1.5 py-0.5 rounded bg-purple-950/30 border border-purple-900/50 text-purple-300 hover:bg-purple-900/40"
|
|
111
|
+
:data-testid="`event-link-${e}`"
|
|
112
|
+
@click="openEvent(e)"
|
|
113
|
+
>
|
|
114
|
+
{{ e }}
|
|
115
|
+
</button>
|
|
116
|
+
<button
|
|
117
|
+
v-for="a in m.provides.actions"
|
|
118
|
+
:key="`p-a-${a}`"
|
|
119
|
+
type="button"
|
|
120
|
+
class="font-mono text-[10px] px-1.5 py-0.5 rounded bg-amber-950/30 border border-amber-900/50 text-amber-300 hover:bg-amber-900/40"
|
|
121
|
+
:data-testid="`action-link-${a}`"
|
|
122
|
+
@click="openAction(a)"
|
|
123
|
+
>
|
|
124
|
+
{{ a }}
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div
|
|
130
|
+
v-if="m.needs.events.length || m.needs.externalEvents.length || m.needs.actions.length"
|
|
131
|
+
class="text-xs"
|
|
132
|
+
>
|
|
133
|
+
<div class="flex items-center gap-1 text-zinc-500 mb-1">
|
|
134
|
+
<ArrowDownRight class="w-3 h-3 text-blue-400" />
|
|
135
|
+
Needs
|
|
136
|
+
</div>
|
|
137
|
+
<div class="flex flex-wrap gap-1 pl-4">
|
|
138
|
+
<button
|
|
139
|
+
v-for="e in m.needs.events"
|
|
140
|
+
:key="`n-e-${e}`"
|
|
141
|
+
type="button"
|
|
142
|
+
class="font-mono text-[10px] px-1.5 py-0.5 rounded bg-zinc-900 border border-zinc-700 text-zinc-300 hover:bg-zinc-800"
|
|
143
|
+
:data-testid="`event-link-${e}`"
|
|
144
|
+
@click="openEvent(e)"
|
|
145
|
+
>
|
|
146
|
+
{{ e }}
|
|
147
|
+
</button>
|
|
148
|
+
<button
|
|
149
|
+
v-for="e in m.needs.externalEvents"
|
|
150
|
+
:key="`n-x-${e}`"
|
|
151
|
+
type="button"
|
|
152
|
+
class="font-mono text-[10px] px-1.5 py-0.5 rounded bg-blue-950/30 border border-blue-900/50 text-blue-300 hover:bg-blue-900/40"
|
|
153
|
+
:data-testid="`event-link-${e}`"
|
|
154
|
+
title="cross-service"
|
|
155
|
+
@click="openEvent(e)"
|
|
156
|
+
>
|
|
157
|
+
{{ e }} ⨯
|
|
158
|
+
</button>
|
|
159
|
+
<button
|
|
160
|
+
v-for="a in m.needs.actions"
|
|
161
|
+
:key="`n-a-${a}`"
|
|
162
|
+
type="button"
|
|
163
|
+
class="font-mono text-[10px] px-1.5 py-0.5 rounded bg-zinc-900 border border-zinc-700 text-zinc-300 hover:bg-zinc-800"
|
|
164
|
+
:data-testid="`action-link-${a}`"
|
|
165
|
+
@click="openAction(a)"
|
|
166
|
+
>
|
|
167
|
+
{{ a }}
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</template>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { RouterLink } from "vue-router";
|
|
4
|
+
import { useCache } from "@/lib/cache";
|
|
5
|
+
import {
|
|
6
|
+
Boxes,
|
|
7
|
+
Zap,
|
|
8
|
+
Radio,
|
|
9
|
+
Layers,
|
|
10
|
+
Database,
|
|
11
|
+
GitBranch,
|
|
12
|
+
Network,
|
|
13
|
+
LayoutDashboard,
|
|
14
|
+
Waves,
|
|
15
|
+
Send,
|
|
16
|
+
Play,
|
|
17
|
+
Workflow,
|
|
18
|
+
} from "lucide-vue-next";
|
|
19
|
+
import { PageHeader, KindBadge } from "@/components";
|
|
20
|
+
|
|
21
|
+
const { cache } = useCache();
|
|
22
|
+
|
|
23
|
+
// "Daily" shortcuts — the high-frequency entry points.
|
|
24
|
+
const quickActions = [
|
|
25
|
+
{
|
|
26
|
+
to: "/live",
|
|
27
|
+
label: "Trace",
|
|
28
|
+
desc: "Watch actions / events / workflows fire in real time",
|
|
29
|
+
icon: Waves,
|
|
30
|
+
color: "text-cyan-400",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
to: "/dispatch",
|
|
34
|
+
label: "Try",
|
|
35
|
+
desc: "Dispatch any action with a form generated from its schema",
|
|
36
|
+
icon: Send,
|
|
37
|
+
color: "text-amber-400",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
to: "/run",
|
|
41
|
+
label: "Processes",
|
|
42
|
+
desc: "Start, stop, and watch logs for your wires",
|
|
43
|
+
icon: Play,
|
|
44
|
+
color: "text-emerald-400",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
to: "/eventstorm",
|
|
48
|
+
label: "Flow",
|
|
49
|
+
desc: "See the causal graph: action → event → workflow → action",
|
|
50
|
+
icon: Workflow,
|
|
51
|
+
color: "text-violet-400",
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const hasData = computed(() => !!cache.value && cache.value.apps.length > 0);
|
|
56
|
+
|
|
57
|
+
const stats = computed(() => {
|
|
58
|
+
if (!cache.value) return [];
|
|
59
|
+
return [
|
|
60
|
+
{ label: "Apps", value: cache.value.apps.length, icon: Network, color: "text-blue-400" },
|
|
61
|
+
{ label: "Modules", value: cache.value.modules.length, icon: Boxes, color: "text-emerald-400" },
|
|
62
|
+
{ label: "Actions", value: cache.value.actions.length, icon: Zap, color: "text-amber-400" },
|
|
63
|
+
{ label: "Events", value: cache.value.events.length, icon: Radio, color: "text-purple-400" },
|
|
64
|
+
{ label: "Actors", value: cache.value.actors.length, icon: Layers, color: "text-pink-400" },
|
|
65
|
+
{
|
|
66
|
+
label: "Projections",
|
|
67
|
+
value: cache.value.projections.length,
|
|
68
|
+
icon: Database,
|
|
69
|
+
color: "text-cyan-400",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: "Workflows",
|
|
73
|
+
value: cache.value.workflows.length,
|
|
74
|
+
icon: GitBranch,
|
|
75
|
+
color: "text-violet-400",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: "Resolvers",
|
|
79
|
+
value: cache.value.resolvers.length,
|
|
80
|
+
icon: Network,
|
|
81
|
+
color: "text-rose-400",
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
});
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<template>
|
|
88
|
+
<div v-if="cache" class="p-6 space-y-6" data-testid="overview-page">
|
|
89
|
+
<PageHeader
|
|
90
|
+
title="Overview"
|
|
91
|
+
:icon="LayoutDashboard"
|
|
92
|
+
icon-color="text-emerald-400"
|
|
93
|
+
:subtitle="
|
|
94
|
+
hasData
|
|
95
|
+
? 'Snapshot from .nwire/manifest.json'
|
|
96
|
+
: 'No apps discovered yet — try the shortcuts below'
|
|
97
|
+
"
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<!-- Quick actions — what you actually do here daily. -->
|
|
101
|
+
<div>
|
|
102
|
+
<h2 class="text-sm font-medium text-zinc-300 uppercase tracking-wide mb-3">Start here</h2>
|
|
103
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
|
104
|
+
<RouterLink
|
|
105
|
+
v-for="a in quickActions"
|
|
106
|
+
:key="a.to"
|
|
107
|
+
:to="a.to"
|
|
108
|
+
class="rounded-lg border border-zinc-800 bg-zinc-900/50 px-4 py-3 hover:bg-zinc-900 hover:border-zinc-700 transition-colors block"
|
|
109
|
+
>
|
|
110
|
+
<div class="flex items-center gap-2 mb-1">
|
|
111
|
+
<component :is="a.icon" class="w-4 h-4" :class="a.color" />
|
|
112
|
+
<span class="font-medium text-zinc-100">{{ a.label }}</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="text-xs text-zinc-400 leading-relaxed">{{ a.desc }}</div>
|
|
115
|
+
</RouterLink>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div v-if="hasData" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-8 gap-3">
|
|
120
|
+
<div
|
|
121
|
+
v-for="s in stats"
|
|
122
|
+
:key="s.label"
|
|
123
|
+
class="rounded-lg border border-zinc-800 bg-zinc-900/50 px-4 py-3"
|
|
124
|
+
data-testid="overview-stat"
|
|
125
|
+
>
|
|
126
|
+
<div class="flex items-center justify-between mb-1">
|
|
127
|
+
<span class="text-xs text-zinc-500 uppercase tracking-wide">{{ s.label }}</span>
|
|
128
|
+
<component :is="s.icon" class="w-3.5 h-3.5" :class="s.color" />
|
|
129
|
+
</div>
|
|
130
|
+
<div class="text-2xl font-semibold tabular-nums">{{ s.value }}</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div v-if="hasData">
|
|
135
|
+
<h2 class="text-sm font-medium text-zinc-300 uppercase tracking-wide mb-3">Apps</h2>
|
|
136
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
137
|
+
<div
|
|
138
|
+
v-for="app in cache.apps"
|
|
139
|
+
:key="app.name"
|
|
140
|
+
class="rounded-lg border border-zinc-800 bg-zinc-900/50 px-4 py-3"
|
|
141
|
+
data-testid="overview-app-card"
|
|
142
|
+
>
|
|
143
|
+
<div class="flex items-center justify-between">
|
|
144
|
+
<span class="font-medium">{{ app.name }}</span>
|
|
145
|
+
<span class="text-xs text-zinc-500 tabular-nums"> {{ app.modules.length }} BCs </span>
|
|
146
|
+
</div>
|
|
147
|
+
<div v-if="app.description" class="text-sm text-zinc-400 mt-1">
|
|
148
|
+
{{ app.description }}
|
|
149
|
+
</div>
|
|
150
|
+
<div class="mt-2 flex flex-wrap gap-1">
|
|
151
|
+
<KindBadge v-for="m in app.modules" :key="m" variant="neutral">
|
|
152
|
+
{{ m }}
|
|
153
|
+
</KindBadge>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</template>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/vue3-vite";
|
|
2
|
+
import { createRouter, createMemoryHistory } from "vue-router";
|
|
3
|
+
import Plugins from "./Plugins.vue";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Storybook for the Plugins page. The manifest fetch + SSE will fail
|
|
7
|
+
* inside Storybook, so the page degrades to its empty / no-live state —
|
|
8
|
+
* which is exactly the surface we want to document.
|
|
9
|
+
*/
|
|
10
|
+
const router = createRouter({
|
|
11
|
+
history: createMemoryHistory(),
|
|
12
|
+
routes: [
|
|
13
|
+
{ path: "/plugins", name: "plugins", component: Plugins },
|
|
14
|
+
{ path: "/hooks", name: "hooks", component: { template: "<div/>" } },
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof Plugins> = {
|
|
19
|
+
title: "Pages/Plugins",
|
|
20
|
+
component: Plugins,
|
|
21
|
+
decorators: [
|
|
22
|
+
(story) => ({
|
|
23
|
+
components: { story },
|
|
24
|
+
template: '<div class="h-screen bg-zinc-950 text-zinc-100"><story /></div>',
|
|
25
|
+
}),
|
|
26
|
+
],
|
|
27
|
+
parameters: {
|
|
28
|
+
layout: "fullscreen",
|
|
29
|
+
vueRouter: { router },
|
|
30
|
+
},
|
|
31
|
+
render: () => ({
|
|
32
|
+
components: { Plugins },
|
|
33
|
+
setup() {
|
|
34
|
+
return {};
|
|
35
|
+
},
|
|
36
|
+
template: "<Plugins />",
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
export default meta;
|
|
40
|
+
|
|
41
|
+
type Story = StoryObj<typeof Plugins>;
|
|
42
|
+
|
|
43
|
+
/** Default: no wire running — empty cache + stream errored. */
|
|
44
|
+
export const NoLiveData: Story = {};
|