djangobuilder5 — Milestone 1: redesigned UI vertical slice (React + Vite + Tailwind + shadcn) - #263
Open
mmcardle wants to merge 63 commits into
Open
djangobuilder5 — Milestone 1: redesigned UI vertical slice (React + Vite + Tailwind + shadcn)#263mmcardle wants to merge 63 commits into
mmcardle wants to merge 63 commits into
Conversation
Design doc for a new React + Vite + Tailwind + shadcn/ui front-end package (djangobuilder5) replacing the dated Vuetify UI. Milestone 1 is the look-locking vertical slice: design system (dark-first, emerald accent), app shell, hero splash with live code preview, and the three-pane IDE model builder. Uses @djangobuilder/core unchanged; local state, Firebase deferred. Also gitignore .superpowers/ (visual-companion brainstorm output). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
14-task TDD plan for the look-locking vertical slice: package scaffold, Tailwind v4 design tokens (dark+light), shadcn foundation, theme toggle, core domain adapter (@djangobuilder/core), generation + tar, CodeBlock with token-driven highlighting + copy, Zustand store, app shell, hero splash, and the three-pane IDE model builder with live regeneration and .tar download. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Adds djangobuilder5 dev/build/lint/test scripts and folds lint_v5/test_v5 into the aggregate lint/test. Leaves the deploy-oriented build (io+v4) unchanged; db5 is not deployed in Milestone 1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
…uard - buildCoreProject: pass 2 now wires relationships onto the core model produced in pass 1 by identity (paired list) instead of re-looking up the owner by name, so duplicate model names can no longer mis-wire a relationship. Removes two non-null assertions. Adds a regression test. - CodeBlock.copy: guard missing navigator.clipboard and wrap writeText in try/catch so copy fails silently on non-secure origins (e.g. LAN IP) instead of throwing an unhandled rejection. Both from the Milestone 1 final code review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
- Hero column is now top-aligned (items-start) instead of vertically centered against the tall code preview, so "Design your models." starts at the top. - Wider container (max-w-7xl) with a capped text column (~28rem) and a code column that grows to fill the remaining space (minmax-based grid), so the generated-code panel is wider and responsive — long lines like the ForeignKey no longer get clipped. Addresses layout feedback on the Milestone 1 splash. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
The nav Docs link now uses target="_blank" + rel="noopener noreferrer" and shows a small external-link icon so it's visually clear it leaves the app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Mirrors how djangobuilder4 is served under /db4/:
- db5 build scripts now use --base=/db5/ (+ development/staging/build-only
variants), and the router uses BrowserRouter basename=import.meta.env.BASE_URL
so client routing works under the subpath ("/" in dev).
- Root aggregate build/build_development/build_staging/build_production now
include build_v5(_development/_staging/_production).
- firebase.json rewrites db5/** -> /db5/index.html.
- script/deploy.sh and the Makefile deploy target build db5 and copy its dist
into the db5/ subdir of the deploy root.
Verified: built index.html emits /db5/assets/..., and the bundle boots and
client-routes correctly when served under /db5/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
The code pane was a fixed w-[380px] while the editor took all remaining space. Give it flex-1 (with a 360px floor) so it and the editor split the space ~50/50 and both scale with the viewport — the generated code is no longer cramped and the editor no longer hogs the width. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Design for M2: share the live Firebase backend + 5-collection owner-stamped schema (via a pure flatten/re-nest mapper), auto-save write-through builder, full email auth + anonymous, and an A+C projects dashboard (code-thumbnail cards). Keeps MAX_PROJECTS=3, drops the local-only postgres field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
11-task TDD plan for auth + Firestore persistence + dashboard: firebase init, pure renest/summary mapper, auth/data/write services (owner-stamped, cascade), auth store + firestore-backed write-through project store, builder wiring (route :id, debounced edits, anon try-it), auth screens, A+C dashboard with MAX_PROJECTS, and auth-gated routing. Service layer tested with mocked firebase; dev project is the manual e2e gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
…on try-it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
… auto-select From the Milestone 2 code review + live e2e findings: - DebouncedInput: cancel the pending commit on unmount (no updateDoc against a deleted doc) and only adopt an external value when not focused (no clobbering in-progress keystrokes). - projectStore: route every write-through op through guardWrite/reportWriteError so failed saves log instead of becoming silent unhandled rejections; createProject/deleteProject catch too. - BuilderPage: auto-select the first model once a project's collections load (openProject can run before the models snapshot arrives), and add a short grace period so a freshly-created project shows Loading rather than flashing "Project not found". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Deploy db5 under /db5/ to the development Firebase project, after fixing the subpath auth continue-URL + favicon and the four deferred M2 review minors (snapshot-handler log-not-throw, all-collections-loaded guard, unverified reload button, atomic batched addModel). Inline execution; staging/prod later. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
- Email action-code continue URL uses import.meta.env.BASE_URL so verified/reset users return to /db5/login under the deployed subpath (not the root app). - Add an inline SVG favicon (kills the 404). - snapshotErrorHandler logs instead of throwing into onSnapshot's async machinery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
subscribeAll now reports allLoaded (true once every collection's initial snapshot has arrived); the store sets dataLoaded from it. Fixes the cascade-delete race window, the brief "Project not found" flash, and makes first-model auto-select reliable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Add reloadUser() and an "I've verified — refresh" button that reloads the user from the server, pushes it into the auth store, and navigates to /projects when verified — no manual page reload needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
addModel now writes the model doc, the app-map link, and the two default DateTimeField fields (created/last_updated) in a single writeBatch — atomic and one round-trip instead of four sequential writes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
…t, responsive Closes the five parity gaps vs the current system: - Project files: CodePane now renders the full project file tree (renderer.asTree) — project/app/model files, click to view — not just the five app files. Adds projectFileTree + renderNodeByPath to domain/generate; exports DjangoProjectFileResource from core. - Description + settings: new ProjectHeader (name, description, version/HTMX/ Channels chips) + ProjectSettingsDialog (edit name/description/version/flags write-through, delete project). Adds store setDescription. - About: new /about route + AboutView + TopNav link. - Responsive: new BuilderShell — 3-pane on lg+, tree drawer + Edit/Code tab switcher below lg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Replace the four-column builder (models tree | editor | file tree | code) with a two-column tabbed workspace: a persistent models tree plus a main area that tabs between Design (model editor) and Code (file tree + code). Only one content pane shows at a time, at every width, so nothing is cramped. Makes the existing mobile tab pattern the layout at all breakpoints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Collapse the builder to a single navigation tree (the generated file tree) plus the code view; move all model editing into a per-app modal. - Remove the models panel (TreePane) and the Design/Code tabs (EditorPane, CodePane deleted). BuilderShell is now: file tree (persistent lg / drawer below lg) + CodeView, with a per-app ModelsModal. - ModelsModal lists every model in an app (ModelEditor each: name, abstract, fields, relationships, delete), plus Add model and Delete app. Opened from the tree's edit control on models.py or the code header's 'Edit models'. - FileTree gains a '+ App' input and a per-models.py edit control. - Add app-level creation to the tree; add store updateModel + removeApp (writes.removeApp cascade); drop the now-unused select/selectedModelId. - Promote DebouncedInput to a shared ui component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
- core: add DJANGO6 = 6.0 to the DjangoVersion enum (additive; db4 unaffected). - db5: DjangoVersionNumber 3|4|5|6; toVersionNumber(6)->6.0; fromVersion default 6; buildCoreProject maps 6 -> DJANGO6; LocalProject/ProjectSummary version types. - dialogs: add 'Django 6' option, preselected by default, in New-project + Settings. Note: the numeric enum renders the pin as 'Django==6' (pip resolves to 6.0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
…de, built-in targets, consent/analytics
Closes the High + Medium gaps vs djangobuilder.io:
- Import from models.py: core ModelImporter wrapped in domain/import.ts;
writes.importModels batches parsed models+fields+rels; ImportModelsDialog
(paste -> parse -> select -> add) opened from the models modal.
- Model inheritance/parents: LocalModel.parents in the shared Firestore format
({class,type:django} | {app,model,type:user}); mapper reads + filters dangling;
buildCoreProject resolves django + user parents; ModelEditor 'Inherits from' UI.
- Move model between apps: writes.moveModel re-parents via the app maps; a
'Move to…' select in the model editor.
- Anonymous -> registered upgrade: auth.upgradeAnonymous (linkWithCredential),
UpgradeAccountDialog, 'Save your account' in the nav — same uid, data kept.
- Extra built-in targets: auth.AbstractUser/AbstractBaseUser/Group as relationship
targets; buildCoreProject resolves any built-in; shared builtInTargets.
- Analytics + consent + privacy: consentStore, gated gtag loader, ConsentSnackbar,
/privacy PrivacyPolicy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
At >=1536px the per-app models editor renders as a right-rail side panel next to the live code (edit + watch models.py regenerate together) instead of a centered overlay; below that width it stays the modal. - extract ModelsPanel (the editor content) from ModelsModal; ModelsModal is now a thin overlay wrapper around it. - add a matchMedia-based useMediaQuery hook (+ jsdom mock in test setup). - BuilderShell docks ModelsPanel (right rail) when the 2xl query matches, else renders the ModelsModal overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
The relationship row had a fixed-width name input plus two intrinsic-width selects (type + target) and no flex-1 absorber, so its min content width (~548px) exceeded the narrow docked panel (~469px) and spilled past the edge. Make both selects min-w-0 flex-1 so they share the row and shrink (like the field row's args input). Verified live: row now fits its container exactly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
On >=1536px the docked models editor is now a permanent right rail — no close/Done — that edits the app of the file you're viewing (follows tree navigation, keeps the last app on project-level files, re-points if its app is deleted). 'Edit models'/tree ✎ on large screens navigate to that app's models.py instead of opening anything. Below the breakpoint it stays the closable modal. - ModelsPanel onClose is now optional (absent = non-closable: hides ✕/Done). - BuilderShell derives a dockedAppId and always renders the panel when wide. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Field/relationship arguments were crammed into a shrinking inline input (and relationship args weren't editable at all). Give args their own full-width line below the controls, as a wrapping, auto-growing textarea (DebouncedTextarea): long values wrap into view instead of clipping, and relationship args (on_delete, related_name, …) are now editable. Enter is suppressed / newlines stripped so args stay a single logical line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Shorter controls (h-8), a smaller args textarea, and tighter section/row spacing bring each field block from ~78px to ~66px. Args still wrap when long. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
On large screens the models panel is always docked, so the tree's ✎ control and the code header's '✎ Edit models' button are redundant. Gate both on the docking breakpoint (BuilderShell passes onEditModels to FileTree/CodeView only below it); selecting a models.py row still switches the docked panel. Below the breakpoint both remain and open the modal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RakQgqBxYPYUY37gvxrtG7
Pre-deploy pass over djangobuilder5, driven by a feature diff against the production .io app. Data integrity: - Chunk every unbounded batch at 450 writes. importModels, removeModel, removeApp and deleteProjectCascade each built a single WriteBatch, so a ~40-model import or a large project delete blew Firestore's 500-op cap and failed outright. Write order is now deliberate — children before the link that reveals them, descendants before the parent — so a failed chunk leaves recoverable state. - Cascade relationship targets. `to` is a denormalised "app.Model" string, so renaming an app or model, moving a model, or deleting either one stranded every relationship pointing at it. domain/relationshipIntegrity.ts computes the repoints (rename/move) and the inbound deletes, applied in the same run. - Tolerate an unresolved target in buildCoreProject instead of throwing. The five collection listeners deliver snapshots independently, so a renamed app can land a frame before its repointed relationship — and BuilderShell renders the file tree unguarded, which took the whole page down. Found only by exercising a rename against live Firestore. Parity gaps: - Apps can now be renamed (previously delete-and-recreate, losing every model). - Anonymous sign-in from /login, not just the splash CTAs. - Signing out as a guest warns before stranding the data, and offers deleting it; previously it signed out silently and orphaned the docs forever. - Name validation on everything that becomes a Python identifier — rejects spaces, leading digits, punctuation and keywords rather than generating unimportable code. - MAX_MODELS_PER_APP (25), matching .io. - Failed snapshot listeners surface a banner; they never recover, so the page was silently showing stale data. - Import accepts .py uploads and routes each parsed model to a chosen app. Also: the wordmark links home rather than to /projects, and the splash drops "Live demo", which fired the identical handler as the primary CTA and landed users on an empty dashboard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new account landed on "Your projects / 0 of 3", an empty grid and a modal behind a button — nothing about what the product does. With no projects, /projects now renders inline (no dialog): a short hero, the create form, and the real generated models.py / admin.py / serializers.py / views.py / urls.py for a starter blog app, tabbed and highlighted. The project name flows live into the preview as it is typed, and a three-step strip spells out the workflow. The hints are derived, not hardcoded: countProjectFiles and generatedFileNames walk the actual rendered tree, and the output chips are a curated list filtered against what the renderer really emits — so the page can't promise a file that is no longer produced. Toggling HTMX or Channels does not change the file tree (only contents and requirements), so the copy doesn't claim it does. Extracts ProjectForm, shared with NewProjectDialog, so the inline and modal paths can't drift on validation or create-then-navigate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI had jobs for core, .io and the CLI but none for db5, so its suite (now 192 tests) only ran locally. Adds v5_tests and v5_type_check alongside io_tests, plus the db5 dist artifact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Introduces a new front-end package
packages/djangobuilder5— a slick, modern, dark-first redesign of Django Builder — as a look-locking Milestone 1 vertical slice. Nothing indjangobuilder.ioordjangobuilder4is touched; this is purely additive.class-variance-authority+clsx+tailwind-merge).#3ECF8Eaccent, and a coding palette exposed as first-class tokens (highlight.js theme is driven by those tokens, so the code colours follow the theme switch).models.pypreview with a Copy helper..tar. Editing regenerates the code live..tarcome from@djangobuilder/core, unchanged, isolated behind a singlesrc/domain/adapter. State is local (Zustand + immer +localStorage) — no Firebase in M1.dev5/build_v5/lint_v5/test_v5and foldslint_v5/test_v5into the aggregatelint/test.Design artifacts:
docs/superpowers/specs/2026-07-05-djangobuilder5-ui-redesign-design.mdanddocs/superpowers/plans/2026-07-05-djangobuilder5-milestone-1.md.Deliberate choices / deviations
<select>for M1 (Radix Select can replace later).renderAppFile) rather than the fullasTreeexplorer (deferred).Out of scope (later milestones)
/db5/base + hosting), Radix-backed Select/Dialog/Toast.Test Plan
bun install && bun run test_v5→ 30 tests pass (real generator + real store + a live-edit integration test).bun run build_v5→tsc --noEmit+vite buildsucceed.bun run lint && bun run test(aggregate, all packages) pass.bun run dev5→ splash shows livemodels.py+ Copy;/buildthree-pane editor; edit a field → right-pane code regenerates; Download .tar downloads the project; theme toggle flips dark/light and the code palette follows.🤖 Generated with Claude Code
https://claude.ai/code/session_011jUxCijreThyiaGzxyieYo