Site-wide Layout Redesign Master Plan
v2 Reassessment (2026-08-02): After cross-checking vision / ADR-0012 / backend capability inventory, wave order has changed (see "Waves" section). Core correction: capability exposure and shell removal take priority over presentation-layer density polish; Pro zero-out drops from "wave-driven metric" to "cleanup hygiene". Full capability matrix: product-capability-map.md.
Audience: implementers executing UI slices (Auto). Answers one question: how to raise layout quality across all existing pages to first-class product feel, and in what order. Constraints: antd 5 + umi (ADR-0005 / ADR-0014); Pro Strangler peel-only, no deepening; visual tokens single source of truth
frontend/src/theme/tokens.ts+theme/css-vars.less(see ui-home-model-redesign.md §Visual direction — this doc does not redefine the palette); landing page dark brand facade stays; Home is a light workspace. Relationship: this doc is the site-wide master plan; Home/model page block-level IA details remain inui-home-model-redesign.md; this doc references only, does not duplicate.
One System, Two Exposures
| World | Pages | Visual |
|---|---|---|
| Facade (dark brand) | Landing /, login/register | Syne/IBM Plex type + dark composition, brand-first |
| Workspace (light system) | Home, project list, designer chrome, version/settings/account, data sources, share, 404 | Light token system; antd Layout/Table/Form as skeleton |
Bridge across worlds: login/register left half uses landing dark brand panel, right half white form — one dark→light transition stitching "facade" and "workspace" on one screen.
Layout Pattern Catalog (only these three shells site-wide)
- Brand Shell: no nav chrome; content centered/split-column. Used for landing, login/register, 404/403.
- Workspace Shell: 64px header (
--erd-chrome-header-h; logo + primary nav + right user area) + content areamax-width: 1200pxcentered + one-line footer copyright. Used for Home, dataModels, project/*, databaseConfig, account/settings. Implementation =HomeLayout+ sharedlayouts/erd-chrome.less(GroupLayout same chrome; no full-page Watermark). - Designer Shell: 64px header (same chrome) + left tree 320px + tabs ~24px (
--erd-tabs-h, was 40→28→24, ADR-0016 density) + flex canvas. Used for/design/*(including version / import / export / setting tabs). Implementation =DesignLayout(antd Layout, W1 peeled Pro; version number under "More", no watermark clutter).
Do not invent a fourth shell; do not wrap any page in ProLayout/PageContainer again (ADR-0014).
Density Spec (site-wide unified)
- 4pt grid; workspace shell outer gutter Home/Group: shell/content 12×16, body 12×16 (inner page adds 8×12); forbid outer gutter 24/20 stacked with inner double-loose gutter; card padding follows each
.densify class - Tables:
size="middle", row height controlled by antd token, forbid custom row-height magic numbers - Forms:
layout="vertical"(settings) orlabelCol 6inside modals; labels 13px, required asterisk brand color - List row height follows
.project-list-page/ data source table densify (~row pad 4×8); tree row height 22 - Type scale follows token brief: 12/13/14/16/20/28/40; stat numbers always ink-900, no rainbow colors
Navigation Patterns
- Global: workspace shell header primary nav (Home / Projects / Data Sources / Account), current item brand underline 2px, hover ink-900
- Inside designer (single chrome): left logo→
/home+ project name ▾ (aria-label=Project menu: All projects → Recent projects (max 5, current item ✓, click other item switches/design/table/model?projectId=) → import·export·settings overlay,itemsAPI + submenus externalized, submenu click and only one open at a time); center main tabs Model | Version only (version includes sider sub-nav); right SaveStatus / Save version / presence / share / My work orders·Pending approvals·Notifications (/design/table/version/order|approval,/project/notice) /⋯(WeChat·GitHub·version number) / user. Import·export·settings pages remain deep-linkable, not in header - Hierarchy consolidation: project list card whole-card
<Link>straight to designer; switch project inside designer via ▾ "Recent projects"; return to list via "All projects" / logo, no new "Back" button
Empty State Spec (one sentence + one primary CTA per page)
| Page | Empty state |
|---|---|
| Home project area | Illustration + "New model" primary button + "Start from example" text link |
| dataModels (each tab) | "No projects yet" + "New project" primary button; team tab adds "Create a team" text link |
| databaseConfig | "No data sources yet" + "New data source" primary button |
| Designer canvas | ER silhouette illustration + "Create first table" primary button + "Import DBML" secondary button + "Reverse from data source" text link (empty state hides MiniMap); after import opens relation diagram and fitView |
| version list | "No versions yet" + "Save first version" primary button (same action as header entry) |
| share (invalid link) | AuthBrandShell + "Open demo" primary button + "Back to Home"; empty module/no model → ER silhouette + same CTA |
| 404 / 403 | AuthBrandShell + "Open demo" primary button + "Back to Home" (same shape as share invalid gate) |
Use brand shell / ER silhouette empty states uniformly; reuse existing illustration assets; empty-state CTA must be reachable (getByRole('button', { name })). Forbid bare antd Result as brand gate.
Per-Page Plan
Landing / (Brand Shell · composition unchanged)
- Keep dark full-bleed brand composition and
landing-hero.jpg(roadmap P5 decided) - ✅ Landing page token alignment (2026-08-03):
pages/landing/index.lesscleared@ink/@accent/#4aa3c8magic colors →--erd-*+color-mix; primary CTA changed to brand red; three pillars use success/brand/warning accents; type family via--erd-font-* - Footer one-line copyright; links grouped under "Resources" column
Login / Register (Brand Shell · W5)
- ✅ W5 slice 4 (2026-08-03):
AuthBrandShellleft 40% dark brand panel (--erd-ink-900gradient + logo/narrative/ErdEmptyDiagramthumbnail + "Open demo" text link) + right 60% white antdForm; clearedbg2.png/#1677FFhardcodes; register page same shape;redirectloop unchanged
Home /home (Workspace Shell · W2, deletion-first)
- Block-level IA see
ui-home-model-redesign.md§Home information architecture (hero CTA cluster + secondary horizontal links + full-width 3-column project grid; no quick-action wall; announcements hideable by freshness); W2 deletion only: duplicate stat cards ("Project overview"), slogan rotation,components/Radar/,_mock.ts, unrenderedPieconfig and dead@ant-design/chartsimport - ✅ S2 Home hero (2026-08-02): primary CTA "Continue last modeling" straight to recent project canvas + quiet three metrics; quick links retokenized (not W5 presentation layer)
- Grid hover elevation and other density details are opportunistic, not standalone slices; forbid extra visual design hours for Home (S3 grid is separate slice)
- Verification anchors:
getByRole('button', { name: 'Continue last modeling' }),home-link-*testId
Project list dataModels + project/* (Workspace Shell · W4)
- Current:
ProList(dataModels / project/person / project/recent / project/group) card list,avatar:'/logo.svg'placeholder, action buttons stacked inactions - Target: antd
List+ inline row layout: left logo 32px + project name (16 strong, whole row<Link>straight to designer) + type Tag (personal ink / team teal) + one-line truncated description + right "Updated x ago" + hover-reveal row-end actions (rename/copy/settings/delete inDropdown) - Tabs (recent/personal/team) use antd
Tabsat content header; sortSelectright-aligned; pagination unchanged - Peel all
ProList; delete project confirmation unchanged (existing) - project/group subpages (members/permissions/settings) same wave:
PageContainer→ workspace shell content area + antdCardsections
Data source config databaseConfig (Workspace Shell · W4)
- Current:
PageContainer+ProTable, functionality complete (status Badge / ping / Drawer form / batch delete) - Target: peel
PageContainer→ workspace shell;ProTable→ antdTable(columns ported, toolbar becomes title row + right "New data source" primary button + searchInput.Search); Drawer formProForm*deferred to W4 unified peel - Column width uses
Tabledefault adaptive, forbid fixed px total width
Designer chrome + model (Designer Shell · W1 ✅ / W2 cleanup ✅ / header IA ✅)
- W1: DesignLayout peel ProLayout → antd Layout (✅, see CHANGELOG 2026-08-02); header keeps save/share/presence/
homeRightContent/project menu - ✅ W2 slice 3 (2026-08-02): removed main area nested
Splitter/DataTable(left tree only = sider); removed sider footer; sider 400→320;CommonTabsbar 40px; designer shellcalc(100vh-*)→ flex fill; tree header "New" (design-tree-add) always visible - ✅ W2 slice 4 (2026-08-02): cleared designer residual
calc(100vh)— fixedEmptyStateAnimationcontent height chain break;QueryTree/version/ ReactFlow → flex/height:100%(metric "post-W1 designer 0" closed) - ✅ Header IA consolidation (2026-08-02): removed "Project ▾" and horizontal five-tab dual navigation; project name switcher + Model|Version + right North Star CTA +
⋯overflow (see "Navigation Patterns") - ✅ Project ▾ recent switch (2026-08-02): menu "Recent projects" max 5 (
recentProject); current item ✓; click other item switches/design/table/model?projectId=; "All projects"/import·export·settings retained
Version version / Import import / Export export / Settings setting (Designer Shell tabs · W3/W4)
- Current:
design/versionusesProListdeep hack (token brief marks "pending peel"); import/export/setting dialogs and panels heavyProForm*(ReverseDatabase, ExportDDL, DatabaseSetUp, DefaultSetUp, etc.) - Target:
- version list → antd
List/Table: version number (strong) + tag chips + truncated description + time + row-end action Dropdown (rename/diff/export/revert/approval); diff/approval subpages same shell - Cross-version diff export is new capability this wave (not port): diff panel adds "Export" action, reuses existing export pipeline
- All
ProForm*→ antdForm+Form.Item: fields, validation,onFinishported; modal width unified 520/720 two tiers - Form button area: primary button right brand, cancel left text button; destructive ops (rebuild version) confirmation unchanged
- version list → antd
- Principle: peel one dialog at a time, port without behavior change; run that journey E2E after each peel
Account account/settings (Workspace Shell · W4)
- Current: Pro settings page skeleton (
account/settings+components/baseetc.ProForm) - Target: antd
Tabs(left vertical: Basic profile / Change password) +Form layout="vertical"; avatar upload unchanged; peel all ProForm; delete "Geographic/Phone" etc. fields with no backend (grep first to confirm zero refs) - ✅ Authorization type density alignment (2026-08-03):
identificationremoved bareResult/#DE2910; density status panel (13/22 +--erd-brand);account-settings-identification - ✅ Export / Home·Group icon token (2026-08-03):
ExportCommoniconcurrentColor→--erd-brand; Home/Group_defaultPropsaligned DesignLayouterdColors.brand - ✅ dataTypeDomains tree icon token (2026-08-03):
getDataTypeTreebrandFill = erdColors.brand; forbid bare#DE2910; settings page/setting/dataTypemounted
Share share (Brand Shell-lite · W5)
- Current: read-only canvas + header, fork entry wired (P3a ✅)
- ✅ W5 slice 2 (2026-08-02): invalid/expired token →
Result403 + "Back to Home" + "Open demo" (same shape as 404/403) - ✅ W5 slice 3 (2026-08-03): success state header aligned designer shell —
erd-chrome-header64px, logo→landing, project name + "Read-only" Tag, "Copy to my projects" primary button, logged-out "Login/Register" text link (with autofork redirect); removed Card/Alertthick shell, canvas is facade body - ✅ W5 slice 4 (2026-08-03): login/register brand shell — left dark panel + right Form; see section above
- ✅ Share invalid/empty state brand alignment (2026-08-03): invalid gate →
AuthBrandShell(removed bare 403 Result); no model/no table →ShareEmptyState(ErdEmptyDiagram+ primary CTA);--erd-*same language - Share page is product facade for strangers; keep light chrome, no workspace navigation
404 / 403 (Brand Shell · W5)
- ✅ W5 slice 1 (2026-08-02): removed
antd/dist/reset.css;Resultstandard status icons (removedno-found.svg/no-access.svg); extra "Back to Home" + "Open demo" →/demo; 403 same shape - ✅ 404/403 brand alignment (2026-08-03): →
AuthBrandShell(removed bare Result); primary CTA "Open demo" + "Back to Home"; same language as share invalid gate - ✅ 404/403 shell keyboard (2026-08-03): Skip "Skip to main action" →
#exception-main-cta; focus-visible brand;/403deep link reachable; same shape as landing/login - ✅ Share invalid gate keyboard (2026-08-03): same Skip→
#exception-main-cta(share-invalid-gate); focus-visible brand;share.speckeyboard green - ✅
/comparecompetitor comparison page keyboard (2026-08-03): same shell Skip→#landing-main-cta; demo→self-host→back to Home; surface focus-visible;compare.speckeyboard green - ✅ Home workspace keyboard (2026-08-03): Skip "Skip to main content" →
#home-main-content; CTA/secondary entry/project card tab order; brand focus-visible;home-keyboard.specgreen - ✅ GroupLayout shell keyboard (2026-08-03): Skip "Skip to main content" →
#group-main-content; bypass header+sidebar; basic settings form tab order; brand focus-visible;group-keyboard.specgreen - ✅ Project list row keyboard (2026-08-03): personal/recent/team stretched link removes dead card; Enter opens designer; Tab row actions; brand focus-visible;
project-list-keyboard.specgreen - ✅ Account settings shell keyboard (2026-08-03):
/account/settingsSkip "Skip to main form" →#account-settings-form; bypass header+sidebar; email→phone→save; brand focus-visible;account-settings-keyboard.specgreen - ✅ Project action modal keyboard (2026-08-03): create/edit first focus field; delete confirm first focus "Yes"; Esc returns; Tab trap;
project-action-modals-keyboard.specgreen - ✅ Import/export overlay keyboard (2026-08-03): DBML import first focus text / export first focus model; Esc returns empty-state CTA/project menu; Tab trap;
import-export-keyboard.specgreen - ✅ Version action modal keyboard (2026-08-03): add/edit first focus version number (non-latest→description); delete/revert Popconfirm→Modal first focus "Yes"; Esc returns; Tab trap;
version-action-modals-keyboard.specgreen - ✅ Version compare/detail diff overlay keyboard (2026-08-03): compare first focus "Initial version"; detail first focus "Export change list"; Esc returns; Tab trap;
version-diff-keyboard.specgreen - ✅ Sync config/rebuild version overlay keyboard (2026-08-03): sync config first focus "Field incremental"; rebuild version first focus "Version number"; Esc returns; Tab trap;
version-sync-rebuild-keyboard.specgreen - ✅ Init baseline overlay keyboard (2026-08-03): first focus "Version number"; Esc returns; Tab trap;
version-init-keyboard.specgreen - ✅ Duplicate overlay keyboard (2026-08-03): first focus "Project name"; Esc returns; Tab trap;
project-copy-keyboard.specgreen - ✅ Data source settings overlay keyboard (2026-08-03): first focus "Add data source"; Esc returns "Project menu"; Tab trap;
database-setup-keyboard.specgreen - ✅ Default items settings overlay keyboard (2026-08-03): first focus "Default fields" Tab; Esc returns "Project menu"; Tab trap;
default-setup-keyboard.specgreen - ✅ Data source reverse parse overlay keyboard (2026-08-03): first focus "Data source" Select; Esc returns "Project menu"; Tab trap;
reverse-database-keyboard.specgreen - ✅ Export DDL overlay keyboard (2026-08-03): first focus "Data source" Select; Esc returns "Project menu"; Tab trap;
export-ddl-keyboard.specgreen - ✅ Parse ERD file overlay keyboard (2026-08-03): first focus upload area "Select ERD file"; Esc returns "Project menu"; Tab trap;
reverse-erd-keyboard.specgreen - ✅ Parse PdMan file overlay keyboard (2026-08-03): first focus upload area "Select PdMan file"; Esc returns "Project menu"; Tab trap;
reverse-pdman-keyboard.specgreen - ✅ Change password overlay keyboard (2026-08-03): first focus "Password"; Esc returns trigger; Tab trap;
reset-password-keyboard.specgreen - ✅ Submit SQL approval overlay keyboard (2026-08-03): first focus "Approver"; Esc returns trigger (parent detail still open); Tab trap;
sql-approval-keyboard.specgreen - ✅ Add member overlay keyboard (2026-08-03): first focus "Select user"; Esc returns trigger; Tab trap;
add-user-keyboard.specgreen - ✅ Read-only share overlay keyboard (2026-08-03): first focus "Share link"; Esc returns trigger; Tab trap;
share-project-keyboard.specgreen - ✅ EntityModal overlay keyboard (2026-08-03): empty state "Add model" first focus "Name"; Esc returns trigger; Tab trap;
entity-modal-keyboard.specgreen - ✅ Canvas delete table confirm overlay keyboard (2026-08-03): selected table Delete first focus "Delete"; Esc returns without delete; Tab trap;
canvas-delete-table-keyboard.specgreen - ✅ Canvas delete edge/frame confirm overlay keyboard (2026-08-03): selected edge/frame Delete first focus "Delete"; Esc returns without delete; Tab trap;
canvas-delete-edge-frame-keyboard.specgreen - ✅ Canvas delete field confirm overlay keyboard (2026-08-03): field browser × first focus "Delete"; Esc returns without delete; Tab trap;
canvas-delete-field-keyboard.specgreen - ✅ Table design delete index confirm overlay keyboard (2026-08-03): index tab "Delete index" first focus "Delete"; Esc returns without delete; Tab trap;
table-index-delete-keyboard.specgreen - ✅ JExcel toolbar delete row confirm overlay keyboard (2026-08-03): "Delete selected rows" first focus "Delete"; Esc returns without delete; Tab trap;
jexcel-toolbar-delete-keyboard.specgreen - ✅ Left tree delete model/table/relation diagram confirm overlay keyboard (2026-08-03): shared
confirmDestructive; first focus "Delete"; Esc returns without delete; Tab trap;tree-delete-keyboard.specgreen - ✅ Data source settings delete confirm overlay keyboard (2026-08-03):
Popconfirm→confirmDestructive; first focus "Delete"; Esc returns without delete; Tab trap;database-setup-delete-keyboard.specgreen - ✅ Workspace databaseConfig delete/batch delete confirm overlay keyboard (2026-08-03):
confirmDestructive; first focus "Delete"; Esc returns without delete; Tab trap;database-config-delete-keyboard.specgreen - ✅ Read-only share revoke confirm overlay keyboard (2026-08-03):
confirmDestructive; first focus "Revoke"; Esc returns without revoke; outer share window still open; Tab trap;share-revoke-keyboard.specgreen - ✅ Team project delete confirm overlay keyboard (2026-08-03):
RemoveGroupProjectPopconfirm→confirmDestructive; first focus "Delete"; Esc returns without delete; Tab trap;group-project-delete-keyboard.specgreen - ✅ Team member remove confirm overlay keyboard (2026-08-03):
GroupUserPopconfirm→confirmDestructive; first focus "Remove"; Esc returns without remove; Tab trap;group-user-remove-keyboard.specgreen - ✅ Approval action confirm overlay keyboard (2026-08-03): Pass/Refuse/Cancel/Repeat Popconfirm→
confirmDestructive; first focus semantic OK; Esc returns without persist; Tab trap; dead codeCopyVersionremoved;approval-action-keyboard.specgreen - ✅ Bare Modal.confirm → confirmDestructive zero-out (2026-08-03): version rebuild baseline/sync×2, reverse overwrite, canvas delete table·edge·frame·field, edge chip, JExcel delete row, table index delete; rebuild confirm first focus "Rebuild" + Esc returns rebuild button;
version-rebuild-confirm-keyboard.specgreen - ✅ Cmd+K command palette keyboard polish (2026-08-03): aria-modal + combobox/
aria-activedescendant; ↑↓ scroll selection; no match "No results" layered empty state; Esc returns trigger; Tab trapped in search;relation.spec"command palette" green - ✅ Tab header density re-compress (2026-08-03): CommonTabs
--erd-tabs-h24 + table design tab header ~24; no clip labels/close buttons; focus-visible;model-design-ux"table design three tabs" green; next cut →left tree toolbar tighten / chrome secondary density✅ - ✅ Left tree toolbar/secondary density (2026-08-03):
QueryTreetoolbar controls 24 + pad 4; sider-inner secondary density; forbid clip icons; focus-visible;model-design-ux"model tree" green; next cut →version list second pass / chrome stray colors✅ - ✅ Version list second density / chrome stray colors (2026-08-03): toolbar controls 24; token colors; forbid clip; focus-visible;
version.spec"version list row density" green; next cut →version work order/approval list density✅ - ✅ Version work order/approval list density (2026-08-03):
.approval-workorder-pagetitle bar ~24 + row pad 4×8; forbid clip; focus-visible;approval.spec"work order/approval list row density" green; next cut →designer secondary pane table density / chrome✅ - ✅ Designer secondary pane table density / chrome (2026-08-03): JExcel toolbar ~24 + header/row pad 4×8 (over datatables); version diff entity row token colors; forbid clip;
model-design-ux"table design JExcel row density" +version.specdiff green; next cut →metadata app sub-tabs / CodeTab chrome✅ - ✅ Metadata app sub-tabs / CodeTab chrome (2026-08-03): CodeTab/DbTab tab bar ~24; forbid clip; focus-visible + Cmd+1/2/3;
model-design-ux"metadata app sub-tabs" green; next cut →table design inner tabs (fields/index) explicit ~24✅ - ✅ Table design inner tab bar density (2026-08-03):
#tableNav--erd-inner-tabs-h24; forbid clip; focus-visible + Cmd+1/2/3;model-design-ux"table design inner tabs" green; next cut →context menu density✅ - ✅ Context/tree action menu density (2026-08-03): shared
.erd-dense-menu(tree actions / tab context / new / project menu); items ~28; forbid clip; menuitem keyboard;model-design-ux"context/tree action menu density" green; next cut →empty table design guidance✅ - ✅ Empty table design / empty table fields guidance (2026-08-03): fields tab Empty "Add first field"; canvas
canvas-fields-emptybrand CTA;table-field-emptygreen; next cut →tab body content secondary density✅ - ✅ Table design tab body content secondary density (2026-08-03): tab body pad 6/4 + hint/empty/metadata tip ~24; workspace gutter 6; forbid clip JExcel;
model-design-ux"table design tab body content secondary density" green; next cut →designer Empty giant marginTop / secondary loose gutter✅ - ✅ Designer empty state secondary density (2026-08-03): fallback forbid marginTop:100; fields/index Empty compress marginXL + pad flush tab-body; retain CTA;
model-design-ux"designer empty state secondary density" green; next cut →welcome empty✅.erd-welcome-emptyinner pad 32 - ✅ Welcome empty state secondary density (2026-08-03):
.erd-welcome-empty__innerpad 32×24 (later tighter 20×16); title 20/mt14→18/mt12·lh22; hero 176; retain reverse link + left tree add model;model-design-ux"welcome empty state secondary density" green; next cut →AuthBrandShell invalid/login gate secondary density✅ - ✅ AuthBrandShell secondary density (2026-08-03): brand 32×28/gap14 + form pad32 + header mb16 (later tighter brand/form 20×16 + gap12 + header mb12 + form body 12/28); login/register/invalid/404·403 same source; forbid weaken brand type size/Skip·Tab;
smoke+share+sessiondensify green; next cut →LandingChrome / compare secondary density✅ - ✅ LandingChrome /
/comparesecondary density (2026-08-03): secondary section 2.75 / comparison row 0.5 / nav·footer tighten; compare hero padT 1.5; hero brand-level + full-bleed unchanged;landing+comparedensify green; next cut →share success state meta/table list secondary density✅ - ✅ Share success state meta / table list secondary density (2026-08-03): stage 6×10 + meta gap2 / hint 12·16; table list pad 6×10·title 12; overlay
.erd-io-modal; keyboard/revoke not weakened;demo+share-project-keyboarddensify green; next cut →Home hero CTA cluster secondary density✅ - ✅ Home hero CTA cluster secondary density (2026-08-03): hero gap24/mb·pb16; actions gap8; secondary 4×10; primary CTA large + Skip/Tab not weakened;
home-keyboarddensify green; next cut →Home empty state/announcement area secondary density✅ - ✅ Home empty state/announcement secondary density (2026-08-03): empty state pad 24×12; secondary entry mb16; project area mb20; announcement pt4 / row pad4·gap10 / title 13; retain empty state CTA;
home-keyboardempty/announce densify green; next cut →designer secondary pane fragment density✅ - ✅ Designer secondary pane fragment density (2026-08-03):
.erd-secondary-panereverse/ERD·PdMan/advanced DDL;ReverseTablemeta;SyncConfig→io-modal; settings hint mb8;designer-secondary-panedensify green; next cut →import overlay Steps alignment✅ - ✅ Import overlay Steps alignment (2026-08-03):
.erd-io-modal__stepsmt/mb ≤10/12 · title 12; same tier as secondary pane;reverse-database-keyboard+export-ddl-keyboarddensify green; next cut →command palette empty pad densify✅ - ✅ Cmd+K no-match empty state / list gutter secondary density (2026-08-03): empty pad ≤8×8 / gap ≤2, list pad ≤2; forbid 16×12 / 4; Trap / aria-activedescendant / Esc return not weakened;
relation.spec"command palette" green; next cut →shortcut cheat sheet card density✅ - ✅ Shortcut cheat sheet (
?) density (2026-08-03): header 6×10 · list 2×4 · row 3×4/gap8 · footer 4×8 · maxH 360; close button focus-visible; forbid 6×8 gutter + padY 10; Esc / Cmd+K mutual exclusion not weakened;relation.spec"shortcut cheat sheet" green; next cut →modeling silent failure / unclear CTA✅ (autosave failure retryable → reverse parse failure readable+retry) - ✅ Reverse parse failure readable + retry (2026-08-03): forbid toast
[object Object]; failure area details + "Re-parse";reverse-parse-failuregreen; next cut →add member invite failure silent close✅ - ✅ Add member invite failure no close (2026-08-03): non-200 no close;
requesttoast no stack on modal;add-user-invite-failuregreen; next cut →dbsync / version save edge silent failure✅ - ✅ Version save/rebuild failure no fake success (2026-08-03):
initSaveonly code===200; InitVersion failure no close; dbsync clear syncing dead state;version-save-failuregreen; next cut →read-only share create failure dead affordance✅ - ✅ Read-only share create failure retryable (2026-08-03): failure no stack; primary button "Regenerate"; forbid disabled dead affordance;
share-create-failuregreen; next cut →change password failure silent close✅ - ✅ Change password failure no close (2026-08-03): only
code===200closes; failure toast + retryable; keyboard loop retained;reset-password-failuregreen; next cut →SyncConfig fake success✅ - ✅ Sync config failure no close (2026-08-03):
setUpgradeTypeonly writes store onsaveProjectcode===200; failure toast + retryable;sync-config-failuregreen; next cut →DefaultSetUp fake success✅ - ✅ Default items settings failure no close (2026-08-03):
updateProfileonly writes store onsaveProjectcode===200; failure toast + retryable; keyboard loop retained;default-setup-failuregreen; next cut →data source settings OK fake success✅ - ✅ Data source settings OK failure no close (2026-08-03):
updateDbsreturns boolean; OK flush only success toast/close; forbid unconditional "Saved successfully";database-setup-failuregreen; next cut →EntityModal/module tree local success vs autosave✅ - ✅ EntityModal persist failure no close (2026-08-03):
addModuleetc.persist:truesaveProjectfirst then write store; only code===200 toast/close;entity-modal-failuregreen; keyboard loop retained; next cut →canvas createDiagram same shape✅ - ✅ Canvas relation diagram overlay persist failure no close (2026-08-03):
ReactFlowRelationdiagram Modalpersist:true; only code===200 closes;diagram-modal-failuregreen; next cut →canvas table header rename fake success✅ - ✅ Canvas table header rename persist failure no exit edit (2026-08-03):
renameEntitypersist:true; only code===200 exits edit;table-rename-failuregreen; next cut →canvas create table/field inline fake success✅ - ✅ Canvas create table/inline add field persist failure retryable (2026-08-03):
addEntity/updateEntityFieldspersist:true; only code===200 adds to canvas/exits new edit; failure toast + retryable; empty name toast / empty field CTA retained;canvas-create-field-failuregreen; next cut →field rename/delete field fake success✅ - ✅ Canvas field rename/delete field persist failure retryable (2026-08-03): existing field
commit/removeFieldpersist:true; only code===200 exits edit/removes row; delete field confirmation retained, failure window stays open;canvas-field-rename-delete-failuregreen; next cut →field meta (type/PK/hidden) instant fake success✅ - ✅ Canvas field meta persist failure retryable (2026-08-03): type/PK/NN/AI/hidden/browse PK
persist:true; failure draft rollback or row remains;canvas-field-meta-failuregreen; next cut →table design JExcel field meta fake success✅ - ✅ Table design JExcel field meta persist failure retryable (2026-08-03):
TableInfoEditpersist:true; failure re-mount grid rollback;jexcel-field-meta-failuregreen; next cut →table design index tab fake success✅ - ✅ Table design index tab persist failure retryable (2026-08-03):
updateEntityIndex/TableIndexEditpersist:true; failure empty state/re-mount rollback;jexcel-index-failuregreen; next cut →default field fake success✅ - ✅ Default field persist failure retryable (2026-08-03):
updateDefaultFields/DefaultFieldpersist:true; failure re-mount rollback; dead codemoveFieldremoved;default-field-failuregreen; next cut →workspace shell outer gutter densify✅ - ✅ Workspace shell (Home/Group) outer gutter secondary density (2026-08-03): shell/content 12×16, body 12×16, list empty state 12×8; forbid 24/20 double-loose gutter;
layout-outletdensify green; next cut →account BaseView gap✅ - ✅ Account BaseView left-right column secondary density (2026-08-03): gap 24→16 (narrow 12); forbid 24;
account-settingsdensify green; next cut →header✅erd-chrome-actionsgap16 - ✅ Header
erd-chrome-actionssecondary density (2026-08-03): gap 16→12 (Design still 8);data-testid;layout-outletdensify green; next cut →header pad20 / brand–nav gap16✅ - ✅ Header
erd-chrome-headersecondary density (2026-08-03): padX 20→16 + brand–nav gap 16→12; Home/Group override aligned; Design still gap8;data-testid;layout-outletdensify green; next cut →Home horizontal nav Menu item horizontal loose spacing✅ - ✅ Home horizontal nav Menu item secondary density (2026-08-03): padX 16→12 (8–12 family); item height 64 / hit width ≥44;
testid=home-layout-menu;layout-outletdensify green; next cut →Group sidebar nav row spacing✅ - ✅ Group sidebar nav row spacing secondary density (2026-08-03): item height 40→28 / padX 24·16→12 / marginY 4→2 / type 12;
testid=group-layout-sider-menu;layout-outletdensify +group-keyboardgreen; next cut →project list toolbar fragment spacing✅ - ✅ Project list toolbar fragment spacing (2026-08-03): Search height 32→28 + Space
size={8}+ button padX 8; toolbar height ≤32;testid=project-list-toolbar;project-surfacedensify +project-list-keyboardgreen; next cut →team member toolbar fragment spacing✅ - ✅ Team member toolbar fragment spacing (2026-08-03):
GroupUsermb16→8 + Search 32→28 + Space gap8 + button padX8;testid=group-user-toolbar;group-layout-navdensify +group-keyboard/add-user-keyboardgreen; next cut →Group user group Title/tab fragment spacing✅ - ✅ Group user group Title/left role tab fragment spacing (2026-08-03): title 20→13/22·mb8; removed Space large + br; left tab padX24→12·height38→28·type12;
testid=group-setting-page;group-layout-navdensify +group-keyboard/add-user-keyboardgreen; next cut →Group basic settings Title level4✅ - ✅ Group basic settings page header fragment spacing (2026-08-03): title 20→13/22·mt0·mb8; same file "Delete project" same tier;
testid=basic-setting-page;group-basic-settingdensify +group-layout-nav/group-keyboardgreen; next cut →Group basic settings Form item spacing/controls 28✅ - ✅ Group basic settings Form fragment spacing (2026-08-03): item mb24→12 / Input·Select·button 32→28 / label 12; aligned
.setting-common-form;group-basic-settingdensify +group-layout-nav/group-keyboardgreen; next cut →Group basic settings delete zone fragments (Divider/Space/secondary text)✅ - ✅ Group basic settings delete zone fragments (2026-08-03): Divider 24→12 + removed Space stacked title mb + secondary text 14→12/18;
testid=basic-setting-delete-zone;group-basic-settingdensify +group-project-delete-keyboardgreen; next cut →welcome empty state title 20/mt14 fragment spacing✅ - ✅ Welcome empty state title fragment spacing (2026-08-03): title 20/mt14·lh≈26 → 18/mt12·lh22 (flush 8–12 / page-title 13/22); pad/hero unchanged;
model-design-uxdensify green; next cut →welcome empty state inner gutter pad32✅ - ✅ Welcome empty state inner gutter fragment spacing (2026-08-03):
.erd-welcome-empty__innerpad 32×24 → 20×16 (flush shell 8–12 / content 12×16); title/hero unchanged;testid=designer-welcome-empty-inner;model-design-uxdensify green; next cut →AuthBrandShell brand/form pad32 second pass (align 20 gutter)✅ - ✅ AuthBrandShell brand/form gutter fragment spacing second pass (2026-08-03): brand/form pad 32→20×16; gap14/header/type size/~40% unchanged;
testid=auth-form-panel;smoke+share+sessiondensify + login shell keyboard green; next cut →AuthBrandShell header mb16 / brand gap14 third pass