chore(builder): complete framework-creation flow, current with v0 master - #241
Draft
johnleider wants to merge 108 commits into
Draft
chore(builder): complete framework-creation flow, current with v0 master#241johnleider wants to merge 108 commits into
johnleider wants to merge 108 commits into
Conversation
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.
Every selectable card gains an About affordance opening a popover with the maturity.json description, level chip, the direct composables it is built on, the honest resolved-module count, and a verified docs link. Draft cards get a roadmap line. The decorative rec chip hides below sm so names read in full on phones; recommendation stays in sort order and accessible names.
A fresh theme config opens on whichever mode the builder itself is in — the header toggle is the user's most recent answer to that question — while saved configs always keep their stored default. The preview aside's sticky offset now matches the header's real 65px rendered height (64px row + 1px border), eliminating the one-pixel jump on the first scrolled pixel.
Ran pnpm --filter @paper/genesis build once: dist/style.css is a build artifact not present in fresh worktrees.
GnDotGrid's default color resolves var(--v0-on-background), the palette never defined it, and the convention matches docs/playground.
- F1 (must): customized() now resolves plugin meta via the exported getPluginById helper and short-circuits to "stock" for plugins with hasConfig: false (useHydration was stuck "customized" forever since it has no defaults.ts to compare against) - (c) free: saved === undefined -> isUndefined(saved) - F3 (low): index.vue's local meta() now delegates to getPluginById instead of reimplementing the PLUGINS.find lookup - F2 (must): chip rows carry a visually hidden stock/customized state text alongside the color-only chip-on/chip-quiet styling - F4 (low): manifest panel gets role="region" + an aria-label naming it - F5 (low): source switches from toRef to computed, since generateMainTs is expensive and was re-running on every read
…lock, visible dots - Gate the MiniFrame mock app AND the customized-chip row on route === '/builder' (step 1 only); config routes that fall back to PreviewSummary (e.g. /builder/hydration) keep both, unchanged. - On step 1, the manifest panel flex-fills all remaining sticky-aside height instead of sizing to content, with the code block absorbing the fill and scrolling internally — no dead space under a fixed-height cap. Peek is dropped on step 1 (fights the fill layout): the block always fills and scrolls there. Threaded a route+breakpoint `fill` flag through AppShell -> PreviewPane -> PreviewSummary (each computed locally, per the existing per-component convention) so this only engages on the desktop sticky aside; the mobile bottom sheet has no flex ancestor with a definite height to fill into, so it keeps the capped (24rem) + peek block instead. - Add an opt-in fill/maxHeight mode to GnDocsExampleCode so the content area scrolls internally instead of growing unbounded; the peek toggle stays outside the scroll region so it never scrolls out of view. - Restructure the manifest panel so GnDotGrid only layers the body (invite line + code), not the "src/main.ts ... N lines" header bar. - Make the dot grid actually read as a background: the code block's own surface (var(--v0-pre, var(--v0-surface))) is fully opaque, which hid the dots behind it in any populated state. Override --v0-pre to a translucent color-mix from the builder app (packages/genesis untouched), raise GnDotGrid's coverage to 30 (from the default 15) and turn on its `lines` prop so the pattern reads as an obvious blueprint grid rather than a handful of faint dots — visible at a glance in both empty and populated states, light and dark.
- Add shiki, @shikijs/langs, @shikijs/themes (catalog:) and wire up useHighlighter.ts / useHighlightCode.ts / constants/shiki.ts, ported from apps/docs with the vue lang swapped for typescript and the docs-only API transformer + idle-callback path stripped (builder wants eager highlighting, not the idle-prefetch used for docs example panes). `prehighlight` stays unexported here — nothing outside the module calls it, unlike docs' multi-file example prewarming. - PreviewSummary.vue drives GnDocsExampleCode's default slot with useHighlightCode(source), falling back to a plain <pre> until the highlighter loads. No packages/genesis changes — the slot seam already existed. - Global .shiki override in App.vue: shiki (defaultColor: false, dual themes) only emits --shiki-light/--shiki-dark as custom properties on each token span — nothing consumes them as `color` without a rule, so every fallback attempt at reusing apps/docs' override rendered monochrome text. apps/docs' version also assumes a `.dark` ancestor class; this app only sets [data-theme] on <html> (createThemePlugin, target: 'html'), so the added rule keys off that instead. Background is forced transparent (not shiki's own light/dark bg) so the manifest's translucent --v0-pre surface — and GnDotGrid behind it — still shows through the code.
Contributor
|
Removes the top "← Back" button and "STEP N · X" eyebrow across the wizard pages, replacing them with a chevron-left icon button next to the primary action and a linear progress hairline, both in the fixed bottom bar. Frees up vertical space so the h1 is the first element on each step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds
apps/builder/into a complete car-configurator wizard for assembling a v0-based Vue app from selectable plugins and components — now current with v0 master and able to produce a working framework start to finish.The flow: landing → select plugins (15, incl. new ReducedMotion + Tooltip) → per-plugin config screens with a live preview aside (every configurable plugin has a dedicated preview module) → component picker (maturity-aware: draft components are visibly gated) → review build sheet → outputs: a zip starter that installs and builds green, and a playground URL carrying the full plugin configuration.
What's in this update
foreground).Record<KnownAlias, string>), explicit plugin install order with storage auto-included when anything persists, undefined config values survive, draft components/composables filtered from emission.pnpm install && pnpm run buildgreen, styled via an emitted uno.config mapping theme tokens tovar(--v0-*)with fallbacks, demos rewritten on v0 compounds (zero native form controls).src/main.tsso the user's configuration installs in the sandbox;framework.tsbinds through a namespace (vue-repl cannot resolve re-exported bindings).Toggle.Rootwith real pressed state, every field labeled, CTAs announce their visible text, SVG indicators only.main.ts, segmented wizard progress, build-sheet review page, dark mode persisted, AA contrast verified.apps/builderwired into the vitest workspace — 153 tests including drift guards (every config screen must have a factory emission; every known alias must have a predicate).Verification
Three full Playwright walkthroughs (desktop/tablet/mobile, light/dark): complete flow, zero console errors, zip built and served, playground rendered with configured theme, state persistence across reload.
Known limitations
uno.config.ts, so a deliberately partial palette can render unstyled borders there (zip output has fallbacks).