Skip to content

feat: Builder car-configurator wizard#241

Open
johnleider wants to merge 34 commits into
masterfrom
feat/builder
Open

feat: Builder car-configurator wizard#241
johnleider wants to merge 34 commits into
masterfrom
feat/builder

Conversation

@johnleider
Copy link
Copy Markdown
Member

Summary

Builds out apps/builder/ into a car-configurator-style wizard for assembling a v0-based Vue app from selectable plugins + components.

  • 13 per-plugin config screens at /builder/<slug> (Theme, Breakpoints, Locale, Rtl, Storage, Hydration, Logger, Stack, Features, Permissions, Date, Notifications, Rules) — each with its own form body component and defaults.ts. Hybrid PluginConfigShell wrapper handles chrome (Prev / Skip / Save & Next) so the body components only declare fields.
  • Smart-filter components page at /builder/components — recommended subset derived from selected plugins, full catalog grouped by category. Reads the component list from packages/0/src/maturity.json.
  • Dual outputs on /builder/review:
    • "Open in Playground" — encodes the manifest as a fflate-compressed hash and opens play.vuetifyjs.com#….
    • "Download starter (.zip)" — generates a runnable starter project (package.json, vite.config.ts, tsconfig.*, uno.config.ts, index.html, src/main.ts with all selected plugins wired with their saved configs).
    • One generateMainTs() source-of-truth feeds both outputs.
  • Persisted state via v0 useStorage under namespace builder.v1 (Set ↔ array conversion at the boundary). Resume across reloads.
  • Route guards in apps/builder/src/router/guards.ts for /builder/configure redirect-to-first-selected and rejecting orphan plugin slugs.

Adapter handling in generated main.ts

useDate, useLogger, useNotifications, useFeatures save their adapter as a string in the form (e.g. 'V0DateAdapter'). The generator strips that field, injects new V0DateAdapter() as raw TS, and adds the class to the v0 import list. 'none' omits the field; 'custom' for Date emits a // TODO: implement placeholder.

Scope cuts (v1)

  • Locale messages editor and Rules custom-rule editor are JSON textareas — tree/visual editors are their own projects.
  • Per-component config screens out of scope (slot reserved in the store for future).
  • Cross-device share URL deferred.
  • Only component-library intent supported.

What's not in this PR

  • No new tests (project policy is no tests unless asked).
  • No deploy / repo-creation integrations.
  • No global AppBar — progress lives inside the shell.

johnleider added 30 commits May 20, 2026 12:58
Bootstrap apps/builder workspace package modeled on apps/playground but
without the playground-specific deps (vue-repl, shiki, markdown). Same
tsconfig + vite config patterns so the oxc transformer resolves tsconfig
cleanly.
Carried forward from the deleted feature/framework-builder branch via
docs/builder-seed/:
- data/features.ts: curated catalog with name, summary, useCases, tags,
  icon over the generated dep graph
- data/dependencies.json: generated by build/generate-dependencies.ts
  on every `pnpm generate` (runs before vite via the dev script)
- data/questions.ts: 11 plugins across 5 categories (appearance, i18n,
  infrastructure, access, utilities) - the only surface upfront config
  needs to ask about
- engine/resolve.ts: pure function that walks the dep graph and reports
  { selected, autoIncluded, reasons, warnings }
- engine/manifest.ts: encodes selection into a fflate-compressed hash
  for handoff to apps/playground
- main.ts wires Pinia + router + v0 plugins (hydration, breakpoints,
  storage, theme) with light/dark color tokens
- pages/index.vue lands users with a one-paragraph explainer and a
  single CTA to /wizard
- pages/wizard.vue runs the wizard in two phases via a plain
  shallowRef<'plugins' | 'review'>:
  - plugins phase renders the questions.ts taxonomy as a category-
    grouped toggle grid
  - review phase shows selected + auto-included dependencies + any
    warnings, pulled from the engine/resolve output
- stores/builder.ts holds the selection Set and exposes resolved as a
  toRef derived from the dep graph

Deliberately skipped from the previous branch: IntentCard + multi-
intent flow (only component-library exists), mode cards, the stepper
integration, breadcrumb sync, Free Pick and AI pages.
Add generateMainTs() and generatePluginCalls() to manifest.ts. The
generator iterates a FACTORY map covering all 13 plugins and emits
`app.use(createXPlugin(config))` calls. When the stored config is
empty/absent, the call is no-arg. The useDate special-case always
emits `{ adapter: new V0DateAdapter() }` since the plugin throws
when the adapter is missing.
New engine/zip.ts exports generateZip(manifest) and downloadZip(manifest)
producing a downloadable starter project. The archive includes
package.json, vite.config.ts, tsconfig.json, uno.config.ts, index.html,
README.md, .gitignore, App.vue, src/pages/index.vue, and main.ts
generated by manifest.ts so per-plugin config is threaded through.
Replace the Wave 1 placeholder with the real review screen. Shows
selected plugins with customized/defaults/(no config) status, selected
components, auto-included deps, and any resolver warnings. Two action
buttons: "Open in Playground" (encodes a FrameworkManifest via
toPlaygroundUrl) and "Download starter (.zip)" (calls downloadZip).
Footer offers Reset all and Back.
Date / Logger / Notifications / Features plugins save the adapter as a
string in their config (e.g. 'V0DateAdapter', 'KnockNotificationsAdapter').
The generator was JSON-stringifying the whole config object, producing
broken TypeScript like `createDatePlugin({ adapter: "V0DateAdapter" })`
where the plugin expects a class INSTANCE.

The existing useDate sentinel trick also didn't work because the spread
order overwrote the placeholder.

Introduce a per-plugin ADAPTER_HANDLERS table that:
- Maps each adapter string to a raw TS expression (e.g. `new V0DateAdapter()`)
- Returns the v0 class names that need to be added to the import list
- Returns `null` to omit the adapter field entirely (e.g. 'none' for
  Notifications / Features)

Imports are now collected per-plugin and merged into the @vuetify/v0
import list so adapter classes always travel alongside their factory.

useDate always emits an adapter (it's required); 'custom' emits a
TODO-flagged `new CustomDateAdapter()` for the user to fill in.
- Add apps/builder workspace entry to knip.json
- Drop dead Feature/FeatureMeta types and the unused features.ts seed catalog (components page uses maturity.json directly)
- Un-export PLUGIN_TO_COMPONENTS, generatePluginCalls, encodeHash, generateZip — internal-only
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 20, 2026

Open in StackBlitz

commit: dd0e4b7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant