From d765770f6f3e8823103c8d4c86abe58ca8c1d752 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Thu, 27 Aug 2026 02:22:25 +0000 Subject: [PATCH 1/7] 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. --- docs/app/app.config.ts | 1 + .../global/GettingStartedWizard.vue | 273 ++++++++++++++++++ docs/content/1.guide/0.getting-started.md | 11 + docs/content/1.guide/index.md | 2 + 4 files changed, 287 insertions(+) create mode 100644 docs/app/components/global/GettingStartedWizard.vue create mode 100644 docs/content/1.guide/0.getting-started.md diff --git a/docs/app/app.config.ts b/docs/app/app.config.ts index 62a33626..f42ff721 100644 --- a/docs/app/app.config.ts +++ b/docs/app/app.config.ts @@ -74,6 +74,7 @@ export default defineAppConfig({ title: 'Introduction', items: [ '/guide', + '/guide/getting-started', '/guide/tutorial-server-data-inspector', ], }, diff --git a/docs/app/components/global/GettingStartedWizard.vue b/docs/app/components/global/GettingStartedWizard.vue new file mode 100644 index 00000000..384ed625 --- /dev/null +++ b/docs/app/components/global/GettingStartedWizard.vue @@ -0,0 +1,273 @@ + + + diff --git a/docs/content/1.guide/0.getting-started.md b/docs/content/1.guide/0.getting-started.md new file mode 100644 index 00000000..a1a7ebef --- /dev/null +++ b/docs/content/1.guide/0.getting-started.md @@ -0,0 +1,11 @@ +--- +title: 'Getting Started' +description: 'Answer a few questions about the devtool you want to build and get a reading list tailored to it.' +--- + +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. + +::getting-started-wizard +:: + +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. diff --git a/docs/content/1.guide/index.md b/docs/content/1.guide/index.md index 28065319..afd39123 100644 --- a/docs/content/1.guide/index.md +++ b/docs/content/1.guide/index.md @@ -19,6 +19,8 @@ Devframe moves that boundary. A capability is defined once against a stable inte 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. +New here? [Answer a few questions about your devtool](/guide/getting-started) and get a reading list tailored to it. + ## One definition, one standard handler Every devframe starts with [`defineDevframe()`](/guide/devframe-definition), pairing a tool's identity with its capabilities. From 99b7d07934597083c07f8ecb13dd4ce3f9c4f03a Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Thu, 27 Aug 2026 02:44:22 +0000 Subject: [PATCH 2/7] docs: redesign the getting-started wizard as icon cards Replace the plain checkbox list with a grid of selectable cards - each with an icon badge, title, and description - matching the card-picker pattern used elsewhere for this kind of preference UI. Selected cards get a highlighted border, tinted icon badge, and a check mark. --- .../global/GettingStartedWizard.vue | 157 ++++++++++++------ 1 file changed, 105 insertions(+), 52 deletions(-) diff --git a/docs/app/components/global/GettingStartedWizard.vue b/docs/app/components/global/GettingStartedWizard.vue index 384ed625..f3a9ca29 100644 --- a/docs/app/components/global/GettingStartedWizard.vue +++ b/docs/app/components/global/GettingStartedWizard.vue @@ -2,23 +2,25 @@ /** * Interactive "what should I read" wizard for the Getting Started guide. * - * Every question is a checkbox group (multiple answers allowed per question, - * since a real devtool usually spans more than one answer — e.g. it reads - * from both the node side and the user's web app). Selections persist to - * `localStorage` so a reader can leave the page and pick up where they left - * off; the recommended reading list at the bottom recomputes from whatever - * is currently checked. + * Every question is a grid of selectable cards (multiple answers allowed per + * question, since a real devtool usually spans more than one answer — e.g. + * it reads from both the node side and the user's web app). Selections + * persist to `localStorage` so a reader can leave the page and pick up where + * they left off; the recommended reading list at the bottom recomputes from + * whatever is currently checked. */ interface WizardItem { value: string label: string + icon: string description?: string } interface WizardSection { key: string - legend: string + title: string + hint: string items: WizardItem[] } @@ -33,59 +35,65 @@ const STORAGE_KEY = 'devframe-docs:getting-started' const sections: WizardSection[] = [ { key: 'dataSource', - legend: 'Where do you want to visualize data from?', + title: 'Data source', + hint: 'Where do you want to visualize data from?', items: [ - { value: 'node', label: 'The node side', description: 'Server state, build output, the filesystem, child processes' }, - { value: 'browser', label: 'The user\'s web app', description: 'State living in the page you\'re developing' }, + { value: 'node', label: 'The node side', icon: 'i-lucide-server', description: 'Server state, build output, the filesystem, child processes' }, + { value: 'browser', label: 'The user\'s web app', icon: 'i-lucide-app-window', description: 'State living in the page you\'re developing' }, ], }, { key: 'environments', - legend: 'What do you expect your tool to work with?', + title: 'Target environments', + hint: 'What do you expect your tool to work with?', items: [ - { value: 'standalone', label: 'Standalone', description: 'A CLI or dev server with no host framework' }, - { value: 'vite', label: 'Vite' }, - { value: 'next', label: 'Next.js' }, - { value: 'framework', label: 'A specific framework', description: 'Nuxt, or a host framework the kits don\'t cover yet' }, - { value: 'all', label: 'All frameworks', description: 'Anything that speaks a Web Standard Request/Response' }, + { value: 'standalone', label: 'Standalone', icon: 'i-lucide-terminal', description: 'A CLI or dev server with no host framework' }, + { value: 'vite', label: 'Vite', icon: 'i-simple-icons-vite' }, + { value: 'next', label: 'Next.js', icon: 'i-simple-icons-nextdotjs' }, + { value: 'framework', label: 'A specific framework', icon: 'i-lucide-puzzle', description: 'Nuxt, or a host framework the kits don\'t cover yet' }, + { value: 'all', label: 'All frameworks', icon: 'i-lucide-infinity', description: 'Anything that speaks a Web Standard Request/Response' }, ], }, { key: 'availability', - legend: 'When is the data available?', + title: 'Data availability', + hint: 'When is the data available?', items: [ - { value: 'dev', label: 'Development time', description: 'Live, over a running dev server' }, - { value: 'build', label: 'Production build time' }, - { value: 'static', label: 'Statically available', description: 'Local filesystem, a static dump, etc.' }, - { value: 'remote', label: 'Remotely', description: 'Over the web, not on localhost' }, + { value: 'dev', label: 'Development time', icon: 'i-lucide-code', description: 'Live, over a running dev server' }, + { value: 'build', label: 'Production build time', icon: 'i-lucide-hammer' }, + { value: 'static', label: 'Statically available', icon: 'i-lucide-hard-drive', description: 'Local filesystem, a static dump, etc.' }, + { value: 'remote', label: 'Remotely', icon: 'i-lucide-cloud', description: 'Over the web, not on localhost' }, ], }, { key: 'frontend', - legend: 'How do you want to build the frontend view?', + title: 'Frontend approach', + hint: 'How do you want to build the frontend view?', items: [ - { value: 'framework', label: 'A preferred framework', description: 'Vue, React, Svelte, Solid...' }, - { value: 'webcomponents', label: 'Web Components' }, - { value: 'nodeside', label: 'Build the frontend on the node side', description: 'Describe the UI as data instead of shipping a bundle' }, + { value: 'framework', label: 'A preferred framework', icon: 'i-lucide-component', description: 'Vue, React, Svelte, Solid...' }, + { value: 'webcomponents', label: 'Web Components', icon: 'i-lucide-box' }, + { value: 'nodeside', label: 'Build it on the node side', icon: 'i-lucide-braces', description: 'Describe the UI as data instead of shipping a bundle' }, ], }, { key: 'agent', - legend: 'Should it also work with a coding agent?', + title: 'Agent support', + hint: 'Should it also work with a coding agent?', items: [ - { value: 'agent', label: 'Yes, expose it to a coding agent', description: 'Same RPC functions, resources, and state, over MCP' }, + { value: 'agent', label: 'Yes, expose it to a coding agent', icon: 'i-lucide-bot', description: 'Same RPC functions, resources, and state, over MCP' }, ], }, { key: 'requirements', - legend: 'Any specific requirements?', + title: 'Other requirements', + hint: 'Any specific requirements?', items: [ - { value: 'streaming', label: 'Streaming data' }, - { value: 'overlay', label: 'An overlay on the user\'s web app' }, - { value: 'hub', label: 'Composing with other devtools', description: 'One UI, many devframes' }, - { value: 'security', label: 'Authentication' }, - { value: 'deep-linking', label: 'Deep linking', description: 'Shareable URLs into a specific view' }, - { value: 'terminal', label: 'Terminal / process access' }, + { value: 'streaming', label: 'Streaming data', icon: 'i-lucide-radio' }, + { value: 'overlay', label: 'An overlay on the user\'s web app', icon: 'i-lucide-layers' }, + { value: 'hub', label: 'Composing with other devtools', icon: 'i-lucide-layout-dashboard', description: 'One UI, many devframes' }, + { value: 'security', label: 'Authentication', icon: 'i-lucide-shield-check' }, + { value: 'deep-linking', label: 'Deep linking', icon: 'i-lucide-link', description: 'Shareable URLs into a specific view' }, + { value: 'terminal', label: 'Terminal / process access', icon: 'i-lucide-square-terminal' }, ], }, ] @@ -104,7 +112,7 @@ const DOC_CATALOG: Record = { '/guide/security': { title: 'Security', description: 'Localhost binding and a trust handshake before a browser can call RPC.', icon: 'i-lucide-shield-check' }, '/guide/agent-native': { title: 'Agent-Native Devframe', description: 'Expose RPC functions, resources, and shared state to coding agents over MCP.', icon: 'i-lucide-bot' }, '/guide/hub': { title: 'Hub', description: 'Orchestrate many devtools sharing one UI — docks, terminals, messages, commands.', icon: 'i-lucide-layout-dashboard' }, - '/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' }, + '/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' }, '/guide/hub-initiate': { title: 'Serve a Hub Anywhere', description: 'initHub() serves a whole multi-devframe install from one handler.', icon: 'i-lucide-server-cog' }, '/guide/services': { title: 'Cross-Devframe Services', description: 'Expose a typed, namespaced capability to every devframe in a hub.', icon: 'i-lucide-share-2' }, '/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' }, @@ -193,12 +201,25 @@ watch(selections, (value) => { localStorage.setItem(STORAGE_KEY, JSON.stringify(value)) }, { deep: true }) -const hasSelections = computed(() => sections.some(section => selections[section.key].length > 0)) +function isChecked(sectionKey: string, value: string): boolean { + return selections[sectionKey]!.includes(value) +} + +function toggle(sectionKey: string, value: string): void { + const current = selections[sectionKey]! + const index = current.indexOf(value) + if (index === -1) + current.push(value) + else + current.splice(index, 1) +} + +const hasSelections = computed(() => sections.some(section => selections[section.key]!.length > 0)) const recommendedDocs = computed(() => { const paths = new Set(BASE_DOCS) for (const section of sections) { - for (const value of selections[section.key]) { + for (const value of selections[section.key]!) { for (const path of RECOMMENDATIONS[`${section.key}:${value}`] ?? []) paths.add(path) } @@ -208,14 +229,14 @@ const recommendedDocs = computed(() => { .map(path => ({ path, ...DOC_CATALOG[path]! })) }) -function reset() { +function reset(): void { for (const section of sections) selections[section.key] = [] }