From b5212a33d021d0a5697139c564e859781e899990 Mon Sep 17 00:00:00 2001 From: MrCoder Date: Thu, 11 Jun 2026 14:01:39 +1000 Subject: [PATCH 01/33] =?UTF-8?q?fix(web/hub):=20caret=20half=20of=20split?= =?UTF-8?q?=20New=20button=20was=20h-8=20vs=20Button=20md=20h-9=20?= =?UTF-8?q?=E2=80=94=20derive=20it=20from=20buttonClassName(primary)=20so?= =?UTF-8?q?=20the=20two=20segments=20can't=20drift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/home/HomeView.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/web/src/components/home/HomeView.tsx b/web/src/components/home/HomeView.tsx index 4de7d56a..a274792c 100644 --- a/web/src/components/home/HomeView.tsx +++ b/web/src/components/home/HomeView.tsx @@ -3,6 +3,7 @@ import type { Item, Folder, AppUser } from '../../domain/types'; import { SearchInput, Button, + buttonClassName, Select, SelectTrigger, SelectContent, @@ -187,12 +188,10 @@ export function HomeView({
- {TEMPLATE_STUBS.map((t) => ( + {QUICK_TEMPLATES.map((t) => ( @@ -430,11 +438,16 @@ function BrandIcon() { ); } -// Stub template cards — clicking any opens the full template picker (CreateNewModal). -const TEMPLATE_STUBS = [ - { id: 'blank', label: 'Blank', icon: '—' }, - { id: 'api', label: 'REST API', icon: 'API' }, - { id: 'auth', label: 'Auth flow', icon: '🔑' }, - { id: 'microservices', label: 'Microservices', icon: '⬡' }, - { id: 'ecommerce', label: 'Checkout', icon: '🛒' }, +// Quick-pick cards for the "Start something new" row. These are the REAL templates +// (the same inventory the CreateNewModal offers) so a single click creates the chosen +// diagram and opens it — no second selection. "Browse templates" still opens the full +// picker for users who want to preview the styled looks before committing. +const QUICK_TEMPLATES: { id: string; label: string; glyph: string; item: Partial }[] = [ + { id: 'blank', label: 'Blank', glyph: '—', item: blankTemplate() }, + ...TEMPLATES.map((t) => ({ + id: t.id, + label: t.title, + glyph: t.id === 'basic' ? 'A→B' : 'Aa', + item: t.item, + })), ]; From 1b02156d61469aea7244e581d00f742ec79ef5bb Mon Sep 17 00:00:00 2001 From: MrCoder Date: Sat, 13 Jun 2026 19:59:08 +1000 Subject: [PATCH 12/33] =?UTF-8?q?fix(web/brand):=20use=20the=20official=20?= =?UTF-8?q?ZenUML=20logo=20in=20hub=20+=20editor=20headers=20=E2=80=94=20r?= =?UTF-8?q?eplace=20the=20placeholder=20two-participant=20glyph=20with=20t?= =?UTF-8?q?he=20#outline-zenuml=20artwork=20(shared=20BrandLogo=20componen?= =?UTF-8?q?t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/header/AppMenu.tsx | 22 ++------------ web/src/components/home/HomeView.tsx | 23 +++----------- web/src/ui/BrandLogo.tsx | 44 +++++++++++++++++++++++++++ web/src/ui/index.ts | 1 + 4 files changed, 51 insertions(+), 39 deletions(-) create mode 100644 web/src/ui/BrandLogo.tsx diff --git a/web/src/components/header/AppMenu.tsx b/web/src/components/header/AppMenu.tsx index 49c5fa19..b6aa8e80 100644 --- a/web/src/components/header/AppMenu.tsx +++ b/web/src/components/header/AppMenu.tsx @@ -5,6 +5,7 @@ import { MenuContent, MenuItem, MenuSeparator, + BrandLogo, cn, } from '../../ui'; @@ -26,16 +27,6 @@ export interface AppMenuProps { // The "ZenUML" mark from the design's `.brand` tile — a small cobalt-gradient // square holding the column/arrow glyph. Used as the visual of the app-menu // trigger so the logo itself opens the document/app menu (Figma/Docs pattern). -function BrandGlyph() { - return ( - - ); -} function Chevron({ className }: { className?: string }) { return ( @@ -90,16 +81,7 @@ export function AppMenu({ 'hover:bg-white/5 ring-draft', )} > - - - - - + diff --git a/web/src/components/home/HomeView.tsx b/web/src/components/home/HomeView.tsx index 2421dacf..5333bb76 100644 --- a/web/src/components/home/HomeView.tsx +++ b/web/src/components/home/HomeView.tsx @@ -4,6 +4,7 @@ import { SearchInput, Button, buttonClassName, + BrandLogo, Select, SelectTrigger, SelectContent, @@ -416,26 +417,10 @@ export function HomeView({ ); } -// ZenUML brand icon — cobalt rounded square with the two-participant sequence glyph. +// ZenUML brand mark — the official logo (self-contained blue rounded square with the +// Zen/UML wordmark). Sized to the prior 30px footprint so the header layout is unchanged. function BrandIcon() { - return ( - - ); + return ; } // Quick-pick cards for the "Start something new" row. These are the REAL templates diff --git a/web/src/ui/BrandLogo.tsx b/web/src/ui/BrandLogo.tsx new file mode 100644 index 00000000..06f3bbc0 --- /dev/null +++ b/web/src/ui/BrandLogo.tsx @@ -0,0 +1,44 @@ +// The official ZenUML logo — ported verbatim from the legacy app's inline SVG sprite +// (`src/components/Icons.jsx`, ``). A #2E94D4 rounded square +// with "Zen" (white) above "UML" (knocked out of a white bar). The artwork is fully +// self-contained at a 300×300 viewBox — it carries its own rounded background, so render +// it directly at the target size; do NOT wrap it in another colored square. +interface BrandLogoProps { + className?: string; +} + +export function BrandLogo({ className }: BrandLogoProps) { + return ( + + + + + + + + + + + ); +} diff --git a/web/src/ui/index.ts b/web/src/ui/index.ts index 4996a3ff..7ed2c2e4 100644 --- a/web/src/ui/index.ts +++ b/web/src/ui/index.ts @@ -3,6 +3,7 @@ // docs/superpowers/specs/2026-06-07-design-system.md. export { cn } from './cn'; export { Button, buttonClassName, type ButtonProps } from './Button'; +export { BrandLogo } from './BrandLogo'; export { IconButton, type IconButtonProps } from './IconButton'; export { Dialog, DialogTrigger, DialogClose, DialogContent } from './Dialog'; export { TextInput, type TextInputProps } from './TextInput'; From 73ab92c1304d3a483ce030a111c0c8ccfd0ede57 Mon Sep 17 00:00:00 2001 From: MrCoder Date: Sat, 13 Jun 2026 20:19:10 +1000 Subject: [PATCH 13/33] =?UTF-8?q?fix(web/theme):=20convert=20all=20modals?= =?UTF-8?q?=20+=20renderer=20header=20to=20the=20dark=20ink=20surface=20(P?= =?UTF-8?q?3/P4)=20and=20dismiss=20the=20login=20dialog=20on=20auth=20succ?= =?UTF-8?q?ess=20(P5)=20=E2=80=94=20DialogContent=20shell,=2014=20modals'?= =?UTF-8?q?=20inner=20tokens,=20Select=20dropdown,=20RendererHeader/page-t?= =?UTF-8?q?abs=20all=20flip=20onlight=E2=86=92ondark/paper=E2=86=92ink;=20?= =?UTF-8?q?diagram=20canvas=20+=20template=20previews=20stay=20paper;=20Ap?= =?UTF-8?q?pRoot=20effect=20closes=20login=20modal=20on=20null=E2=86=92use?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/app/AppRoot.tsx | 10 ++++- web/src/components/auth/LoginModal.tsx | 12 +++--- .../components/modals/AskToImportModal.tsx | 4 +- .../modals/AtomicCssSettingsModal.tsx | 4 +- web/src/components/modals/CheatSheetModal.tsx | 14 +++---- web/src/components/modals/ConfirmDialog.tsx | 6 +-- web/src/components/modals/CreateNewModal.tsx | 12 +++--- web/src/components/modals/HelpModal.tsx | 10 ++--- .../modals/KeyboardShortcutsModal.tsx | 8 ++-- web/src/components/modals/OnboardingModal.tsx | 8 ++-- web/src/components/modals/SettingsModal.tsx | 15 +++---- .../components/modals/ShareErrorNotice.tsx | 2 +- .../components/modals/SupportPledgeModal.tsx | 4 +- web/src/components/preview/RendererHeader.tsx | 14 +++---- .../subscription/LimitReachedNotice.tsx | 6 +-- .../components/subscription/PricingModal.tsx | 40 +++++++++---------- web/src/ui/Dialog.tsx | 17 ++++---- web/src/ui/Select.tsx | 17 ++++---- 18 files changed, 108 insertions(+), 95 deletions(-) diff --git a/web/src/app/AppRoot.tsx b/web/src/app/AppRoot.tsx index b0316e3d..c16c63e5 100644 --- a/web/src/app/AppRoot.tsx +++ b/web/src/app/AppRoot.tsx @@ -140,6 +140,14 @@ export function AppRoot() { const loginModalOpen = useUiStore((s) => s.loginModalOpen); const setLoginModalOpen = useUiStore((s) => s.setLoginModalOpen); + // P5: dismiss the sign-in sheet once auth succeeds. The OAuth round-trip resolves + // asynchronously (popup → authStore.user), and nothing else closes the modal — so a + // signed-in user would otherwise stay stranded on the login dialog. Closing on the + // null→user transition covers every entry point (share-gate, save-gate, header). + useEffect(() => { + if (user && loginModalOpen) setLoginModalOpen(false); + }, [user, loginModalOpen, setLoginModalOpen]); + // M04: subscription (load + derive plan on auth) — `loading` gates the plan-limit // race guard (§1). analytics.track binds the current userId. paddle = checkout. const { subscription, planType, subscribed, loading: subLoading, reload: reloadSubscription } = @@ -1283,7 +1291,7 @@ export function AppRoot() { onPresent={toggleFullscreen} pageTabs={ onLogin(id)} @@ -93,7 +93,7 @@ export function LoginModal({ open, onOpenChange, onLogin, lastProvider, error }: data-chip={chip ? 'true' : undefined} className={ chip - ? 'flex shrink-0 items-center justify-center rounded-[3px] bg-white p-0.5 text-onlight-strong' + ? 'flex shrink-0 items-center justify-center rounded-[3px] bg-white p-0.5 text-ondark-strong' : 'shrink-0' } > @@ -113,9 +113,9 @@ export function LoginModal({ open, onOpenChange, onLogin, lastProvider, error }: ); } - // Eyelabel (.ret) section headers — onlight-muted keeps them AA-legible on paper - // (onlight-faint fails AA for small text per the contrast audit). - const eyelabel = 'font-mono text-[10px] uppercase tracking-[0.12em] text-onlight-muted'; + // Eyelabel (.ret) section headers — ondark-muted keeps them AA-legible on paper + // (ondark-faint fails AA for small text per the contrast audit). + const eyelabel = 'font-mono text-[10px] uppercase tracking-[0.12em] text-ondark-muted'; return ( @@ -127,7 +127,7 @@ export function LoginModal({ open, onOpenChange, onLogin, lastProvider, error }:

{error}

diff --git a/web/src/components/modals/AskToImportModal.tsx b/web/src/components/modals/AskToImportModal.tsx index 82224a34..485c9f3a 100644 --- a/web/src/components/modals/AskToImportModal.tsx +++ b/web/src/components/modals/AskToImportModal.tsx @@ -24,7 +24,7 @@ export function AskToImportModal({