Skip to content

Commit d765770

Browse files
committed
docs: add an interactive Getting Started wizard to the guide
Adds a checkbox-based questionnaire (data source, target environments, data availability, frontend approach, agent support, and other requirements) that recommends a tailored reading list from the guide, adapters, frameworks, and plugins docs. Selections persist to localStorage so a reader can leave and come back to the page.
1 parent af0a5e7 commit d765770

4 files changed

Lines changed: 287 additions & 0 deletions

File tree

‎docs/app/app.config.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export default defineAppConfig({
7474
title: 'Introduction',
7575
items: [
7676
'/guide',
77+
'/guide/getting-started',
7778
'/guide/tutorial-server-data-inspector',
7879
],
7980
},
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
<script setup lang="ts">
2+
/**
3+
* Interactive "what should I read" wizard for the Getting Started guide.
4+
*
5+
* Every question is a checkbox group (multiple answers allowed per question,
6+
* since a real devtool usually spans more than one answer — e.g. it reads
7+
* from both the node side and the user's web app). Selections persist to
8+
* `localStorage` so a reader can leave the page and pick up where they left
9+
* off; the recommended reading list at the bottom recomputes from whatever
10+
* is currently checked.
11+
*/
12+
13+
interface WizardItem {
14+
value: string
15+
label: string
16+
description?: string
17+
}
18+
19+
interface WizardSection {
20+
key: string
21+
legend: string
22+
items: WizardItem[]
23+
}
24+
25+
interface DocEntry {
26+
title: string
27+
description: string
28+
icon: string
29+
}
30+
31+
const STORAGE_KEY = 'devframe-docs:getting-started'
32+
33+
const sections: WizardSection[] = [
34+
{
35+
key: 'dataSource',
36+
legend: 'Where do you want to visualize data from?',
37+
items: [
38+
{ value: 'node', label: 'The node side', description: 'Server state, build output, the filesystem, child processes' },
39+
{ value: 'browser', label: 'The user\'s web app', description: 'State living in the page you\'re developing' },
40+
],
41+
},
42+
{
43+
key: 'environments',
44+
legend: 'What do you expect your tool to work with?',
45+
items: [
46+
{ value: 'standalone', label: 'Standalone', description: 'A CLI or dev server with no host framework' },
47+
{ value: 'vite', label: 'Vite' },
48+
{ value: 'next', label: 'Next.js' },
49+
{ value: 'framework', label: 'A specific framework', description: 'Nuxt, or a host framework the kits don\'t cover yet' },
50+
{ value: 'all', label: 'All frameworks', description: 'Anything that speaks a Web Standard Request/Response' },
51+
],
52+
},
53+
{
54+
key: 'availability',
55+
legend: 'When is the data available?',
56+
items: [
57+
{ value: 'dev', label: 'Development time', description: 'Live, over a running dev server' },
58+
{ value: 'build', label: 'Production build time' },
59+
{ value: 'static', label: 'Statically available', description: 'Local filesystem, a static dump, etc.' },
60+
{ value: 'remote', label: 'Remotely', description: 'Over the web, not on localhost' },
61+
],
62+
},
63+
{
64+
key: 'frontend',
65+
legend: 'How do you want to build the frontend view?',
66+
items: [
67+
{ value: 'framework', label: 'A preferred framework', description: 'Vue, React, Svelte, Solid...' },
68+
{ value: 'webcomponents', label: 'Web Components' },
69+
{ value: 'nodeside', label: 'Build the frontend on the node side', description: 'Describe the UI as data instead of shipping a bundle' },
70+
],
71+
},
72+
{
73+
key: 'agent',
74+
legend: 'Should it also work with a coding agent?',
75+
items: [
76+
{ value: 'agent', label: 'Yes, expose it to a coding agent', description: 'Same RPC functions, resources, and state, over MCP' },
77+
],
78+
},
79+
{
80+
key: 'requirements',
81+
legend: 'Any specific requirements?',
82+
items: [
83+
{ value: 'streaming', label: 'Streaming data' },
84+
{ value: 'overlay', label: 'An overlay on the user\'s web app' },
85+
{ value: 'hub', label: 'Composing with other devtools', description: 'One UI, many devframes' },
86+
{ value: 'security', label: 'Authentication' },
87+
{ value: 'deep-linking', label: 'Deep linking', description: 'Shareable URLs into a specific view' },
88+
{ value: 'terminal', label: 'Terminal / process access' },
89+
],
90+
},
91+
]
92+
93+
/** Every doc a recommendation can point at, keyed by its route. */
94+
const DOC_CATALOG: Record<string, DocEntry> = {
95+
'/guide': { title: 'Introduction', description: 'What devframe is and who it\'s for.', icon: 'i-lucide-book-open' },
96+
'/guide/devframe-definition': { title: 'Devframe Definition', description: 'One defineDevframe() call returns a portable definition every adapter consumes.', icon: 'i-lucide-package' },
97+
'/guide/tutorial-server-data-inspector': { title: 'Tutorial: Build a Server Data Inspector', description: 'Build a devtool end to end, then ship it as a hub dock, a static build, a dev server, and a CLI.', icon: 'i-lucide-graduation-cap' },
98+
'/guide/rpc': { title: 'RPC', description: 'Type-safe, bidirectional calls between the node side and the browser side.', icon: 'i-lucide-cable' },
99+
'/guide/shared-state': { title: 'Shared State', description: 'Observable state synced between the node side and every RPC client.', icon: 'i-lucide-refresh-cw' },
100+
'/guide/streaming': { title: 'Streaming', description: 'Push chunk-style data from the node side to the browser side.', icon: 'i-lucide-radio' },
101+
'/guide/client-assets': { title: 'Client Assets', description: 'Where a devframe\'s built SPA lives — a local directory or an npm package.', icon: 'i-lucide-folder-tree' },
102+
'/guide/client': { title: 'Client', description: 'Connects any surface to a devframe\'s node side with RPC and shared state.', icon: 'i-lucide-plug' },
103+
'/guide/transports': { title: 'Transports', description: 'Live RPC over WebSocket or SSE, transparent to your RPC code.', icon: 'i-lucide-waypoints' },
104+
'/guide/security': { title: 'Security', description: 'Localhost binding and a trust handshake before a browser can call RPC.', icon: 'i-lucide-shield-check' },
105+
'/guide/agent-native': { title: 'Agent-Native Devframe', description: 'Expose RPC functions, resources, and shared state to coding agents over MCP.', icon: 'i-lucide-bot' },
106+
'/guide/hub': { title: 'Hub', description: 'Orchestrate many devtools sharing one UI — docks, terminals, messages, commands.', icon: 'i-lucide-layout-dashboard' },
107+
'/guide/client-context': { title: 'Client Scripts & Client Context', description: 'How a dock client script runs a devframe\'s code inside the host page.', icon: 'i-lucide-code-2' },
108+
'/guide/hub-initiate': { title: 'Serve a Hub Anywhere', description: 'initHub() serves a whole multi-devframe install from one handler.', icon: 'i-lucide-server-cog' },
109+
'/guide/services': { title: 'Cross-Devframe Services', description: 'Expose a typed, namespaced capability to every devframe in a hub.', icon: 'i-lucide-share-2' },
110+
'/guide/deep-linking': { title: 'Deep Linking', description: 'Send a user to a specific view inside a devframe from a URL or an agent.', icon: 'i-lucide-link' },
111+
'/guide/json-render': { title: 'JSON-Render', description: 'Describe a UI as data — a serializable component spec any frontend renders.', icon: 'i-lucide-braces' },
112+
'/guide/build-your-own-json-render-frontend': { title: 'Build Your Own JSON-Render Frontend', description: 'Implement the renderer contract in your own framework instead of the reference one.', icon: 'i-lucide-component' },
113+
'/guide/build-your-own-hub-ui': { title: 'Build Your Own Hub UI', description: 'The two contracts a hub UI provider implements — node side and browser side.', icon: 'i-lucide-layout-panel-left' },
114+
'/guide/standalone-cli': { title: 'Standalone CLI with Devframe', description: 'npx my-tool starts a dev server serving your SPA over type-safe RPC.', icon: 'i-lucide-terminal' },
115+
'/helpers/interactive-auth': { title: 'Interactive Auth', description: 'An OTP auth layer over devframe\'s node-side primitives.', icon: 'i-lucide-key-round' },
116+
'/helpers/utilities': { title: 'Utilities', description: 'Small, stable helpers bundled into devframe — no npm install.', icon: 'i-lucide-wrench' },
117+
'/adapters': { title: 'Adapters', description: 'Every path from a DevframeDefinition to a running devframe.', icon: 'i-lucide-shuffle' },
118+
'/adapters/initiate': { title: 'The Standard Handler', description: 'initDevframe() turns a definition into a Web Standard Request → Response handler.', icon: 'i-lucide-server' },
119+
'/adapters/cac': { title: 'CLI (cac)', description: 'A cac CLI around a DevframeDefinition with dev, build, and mcp commands.', icon: 'i-lucide-square-terminal' },
120+
'/adapters/build': { title: 'Build', description: 'Produces a static deploy of a devframe.', icon: 'i-lucide-hammer' },
121+
'/adapters/vite': { title: 'Vite (adapter)', description: 'Wraps a definition so Vite DevTools\' plugin-scan picks it up.', icon: 'i-lucide-zap' },
122+
'/adapters/embedded': { title: 'Embedded', description: 'Register a devframe into an already-running context at runtime.', icon: 'i-lucide-plug-zap' },
123+
'/adapters/mcp': { title: 'MCP', description: 'Exposes a devframe\'s agent-facing API as a Model Context Protocol server.', icon: 'i-lucide-bot' },
124+
'/frameworks': { title: 'Frameworks', description: 'Framework kits that integrate devframe with a meta-framework\'s dev server.', icon: 'i-lucide-blocks' },
125+
'/frameworks/vite': { title: 'Vite', description: 'Author one devframe\'s SPA, or mount a whole hub, from a Vite plugin.', icon: 'i-simple-icons-vite' },
126+
'/frameworks/next': { title: 'Next', description: 'Host devframes from a Next.js App Router app via a route handler.', icon: 'i-simple-icons-nextdotjs' },
127+
'/frameworks/nuxt': { title: 'Nuxt', description: 'A Nuxt module split into authoring one devframe or mounting a hub.', icon: 'i-simple-icons-nuxtdotjs' },
128+
'/plugins/a11y': { title: 'Accessibility Inspector', description: 'Runs axe-core against the user app and highlights violations in the page.', icon: 'i-lucide-accessibility' },
129+
'/plugins/terminals': { title: 'Terminals', description: 'A terminal panel built on xterm.js.', icon: 'i-lucide-square-terminal' },
130+
}
131+
132+
/** Always worth reading, regardless of what's checked above. */
133+
const BASE_DOCS = ['/guide', '/guide/devframe-definition', '/guide/tutorial-server-data-inspector']
134+
135+
/** `${section.key}:${item.value}` -> doc routes that answer is worth reading. */
136+
const RECOMMENDATIONS: Record<string, string[]> = {
137+
'dataSource:node': ['/guide/rpc', '/guide/shared-state', '/helpers/utilities'],
138+
'dataSource:browser': ['/guide/client-context', '/guide/deep-linking', '/plugins/a11y'],
139+
140+
'environments:standalone': ['/guide/standalone-cli', '/adapters/cac', '/adapters/build'],
141+
'environments:vite': ['/frameworks/vite', '/adapters/vite'],
142+
'environments:next': ['/frameworks/next'],
143+
'environments:framework': ['/frameworks/nuxt', '/adapters/embedded'],
144+
'environments:all': ['/adapters/initiate', '/adapters', '/guide/devframe-definition'],
145+
146+
'availability:dev': ['/guide/rpc', '/guide/transports'],
147+
'availability:build': ['/adapters/build', '/guide/client-assets'],
148+
'availability:static': ['/adapters/build', '/helpers/utilities'],
149+
'availability:remote': ['/guide/transports', '/guide/security'],
150+
151+
'frontend:framework': ['/guide/client-assets', '/guide/client'],
152+
'frontend:webcomponents': ['/guide/hub', '/guide/build-your-own-hub-ui'],
153+
'frontend:nodeside': ['/guide/json-render', '/guide/build-your-own-json-render-frontend'],
154+
155+
'agent:agent': ['/guide/agent-native', '/adapters/mcp'],
156+
157+
'requirements:streaming': ['/guide/streaming'],
158+
'requirements:overlay': ['/guide/client-context', '/plugins/a11y'],
159+
'requirements:hub': ['/guide/hub', '/guide/hub-initiate', '/guide/services'],
160+
'requirements:security': ['/guide/security', '/helpers/interactive-auth'],
161+
'requirements:deep-linking': ['/guide/deep-linking'],
162+
'requirements:terminal': ['/plugins/terminals'],
163+
}
164+
165+
const selections = reactive<Record<string, string[]>>(
166+
Object.fromEntries(sections.map(section => [section.key, [] as string[]])),
167+
)
168+
169+
onMounted(() => {
170+
if (!import.meta.client)
171+
return
172+
try {
173+
const raw = localStorage.getItem(STORAGE_KEY)
174+
if (!raw)
175+
return
176+
const saved = JSON.parse(raw) as Record<string, unknown>
177+
for (const section of sections) {
178+
const values = saved[section.key]
179+
if (!Array.isArray(values))
180+
continue
181+
const known = new Set(section.items.map(item => item.value))
182+
selections[section.key] = values.filter((value): value is string => typeof value === 'string' && known.has(value))
183+
}
184+
}
185+
catch {
186+
// Corrupt or inaccessible storage - fall back to a clean slate.
187+
}
188+
})
189+
190+
watch(selections, (value) => {
191+
if (!import.meta.client)
192+
return
193+
localStorage.setItem(STORAGE_KEY, JSON.stringify(value))
194+
}, { deep: true })
195+
196+
const hasSelections = computed(() => sections.some(section => selections[section.key].length > 0))
197+
198+
const recommendedDocs = computed(() => {
199+
const paths = new Set(BASE_DOCS)
200+
for (const section of sections) {
201+
for (const value of selections[section.key]) {
202+
for (const path of RECOMMENDATIONS[`${section.key}:${value}`] ?? [])
203+
paths.add(path)
204+
}
205+
}
206+
return [...paths]
207+
.filter(path => path in DOC_CATALOG)
208+
.map(path => ({ path, ...DOC_CATALOG[path]! }))
209+
})
210+
211+
function reset() {
212+
for (const section of sections) selections[section.key] = []
213+
}
214+
</script>
215+
216+
<template>
217+
<div class="not-prose rounded-xl border border-default divide-y divide-default overflow-hidden">
218+
<div class="flex items-center justify-between gap-4 px-5 py-4 bg-muted">
219+
<div>
220+
<p class="font-medium text-highlighted">
221+
What kind of devtool do you want to build?
222+
</p>
223+
<p class="text-sm text-muted mt-0.5">
224+
Check whatever applies — answers save in your browser.
225+
</p>
226+
</div>
227+
<UButton
228+
label="Reset"
229+
icon="i-lucide-rotate-ccw"
230+
color="neutral"
231+
variant="ghost"
232+
size="xs"
233+
:disabled="!hasSelections"
234+
class="cursor-pointer shrink-0"
235+
@click="reset"
236+
/>
237+
</div>
238+
239+
<div class="grid grid-cols-1 md:grid-cols-2 gap-px bg-default">
240+
<div
241+
v-for="section in sections"
242+
:key="section.key"
243+
class="bg-default p-5"
244+
>
245+
<UCheckboxGroup
246+
v-model="selections[section.key]"
247+
:legend="section.legend"
248+
:items="section.items"
249+
:ui="{ legend: 'text-base font-medium text-highlighted mb-3' }"
250+
/>
251+
</div>
252+
</div>
253+
254+
<div class="p-5 bg-muted">
255+
<p class="font-medium text-highlighted mb-3">
256+
{{ hasSelections ? 'Recommended docs, based on your answers' : 'Start here' }}
257+
</p>
258+
<UPageList divide>
259+
<UPageCard
260+
v-for="doc in recommendedDocs"
261+
:key="doc.path"
262+
:to="doc.path"
263+
:icon="doc.icon"
264+
:title="doc.title"
265+
:description="doc.description"
266+
orientation="horizontal"
267+
variant="ghost"
268+
:ui="{ container: 'p-3 sm:p-3', leadingIcon: 'size-5' }"
269+
/>
270+
</UPageList>
271+
</div>
272+
</div>
273+
</template>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: 'Getting Started'
3+
description: 'Answer a few questions about the devtool you want to build and get a reading list tailored to it.'
4+
---
5+
6+
Every devframe capability lives in its own guide page, so it helps to know which ones apply to what you're building before diving in. Check whatever describes your devtool below, and the list at the bottom updates with the docs worth reading first — your answers are saved in your browser, so you can come back to this page later.
7+
8+
::getting-started-wizard
9+
::
10+
11+
None of this is a required reading order. [`defineDevframe()`](/guide/devframe-definition) and [the tutorial](/guide/tutorial-server-data-inspector) are worth reading regardless of your answers above — everything else is additive.

‎docs/content/1.guide/index.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Devframe moves that boundary. A capability is defined once against a stable inte
1919

2020
With a coding agent to scaffold the boilerplate, Devframe is also a fast foundation for standing up a bespoke, specific-need, or even one-off devtool.
2121

22+
New here? [Answer a few questions about your devtool](/guide/getting-started) and get a reading list tailored to it.
23+
2224
## One definition, one standard handler
2325

2426
Every devframe starts with [`defineDevframe()`](/guide/devframe-definition), pairing a tool's identity with its capabilities.

0 commit comments

Comments
 (0)