UNPKG

spaps

Version:

Sweet Potato Authentication & Payment Service CLI - Docker Compose orchestrator for local Python/FastAPI SPAPS server with built-in admin middleware

518 lines (457 loc) 16.1 kB
/** * Design System Registry (DSR) surface for the generated browser starters. * * The scaffolded React starter renders the SPAPS "Authentication reference" * page. Its visual layer is registry-owned: tokens come from the pinned DSR * theme payload, and the page composes shadcn semantic tokens only. Nothing * here invents a local palette, radius scale, or shadow system. * * Adoption is recorded in the generated `design-system.registry.json`, and the * repo-level decision lives in `docs/DSR-DECISION.md`. */ const DSR_THEME = { item: 'vectorsight-theme', version: '0.1.0', channel: 'pilot', family: 'vectorsight', title: 'VectorSight', registryUrl: 'https://registry.buildooor.com/r', payloadUrl: 'https://registry.buildooor.com/r/vectorsight-theme.json', installCommand: 'npx shadcn@latest add @design-system/vectorsight-theme', sourceRepo: 'https://github.com/build000r/design-system-registry', sourcePayload: 'registry/vectorsight/themes/vectorsight-theme.json', // Every entry below carries the DSR cross-family `ds-*` token contract, so // switching the active item is a one-line change in the theme adapter. switchableItems: ['vectorsight-theme', 'nocturne-theme', 'molten-pop-theme'], }; const DSR_DEPENDENCIES = { 'class-variance-authority': '^0.7.1', clsx: '^2.1.1', 'tailwind-merge': '^3.3.1', }; const DSR_DEV_DEPENDENCIES = { '@tailwindcss/vite': '^4.1.14', tailwindcss: '^4.1.14', }; function provenanceHeader(commentOpen, commentLine, commentClose) { const lines = [ 'Design System Registry - ' + DSR_THEME.item + '@' + DSR_THEME.version + ' (channel: ' + DSR_THEME.channel + ')', 'Upstream: ' + DSR_THEME.sourceRepo, 'Payload: ' + DSR_THEME.payloadUrl, 'Refresh: ' + DSR_THEME.installCommand, '', 'Registry-owned visual decisions. Pinned in design-system.registry.json.', 'Re-pull the item instead of hand-editing these values.', ]; return commentOpen + '\n' + lines.map((line) => (line ? commentLine + ' ' + line : commentLine)).join('\n') + '\n' + commentClose + '\n'; } /** * Tailwind v4 entry stylesheet carrying the pinned DSR theme payload. * * `:root` and `.dark` are identical on purpose: VectorSight is a void-only * system (pure black ground in both schemes) per its upstream payload. */ function reactStylesSource() { return provenanceHeader('/*', ' *', ' */') + ` @import "tailwindcss"; @custom-variant dark (&:is(.dark *)); :root, .dark { --radius: 3px; --background: #000000; --foreground: #00ff41; --card: #0a0a0a; --card-foreground: #00ff41; --popover: #0a0a0a; --popover-foreground: #00ff41; --primary: #c8ff00; --primary-foreground: #000000; --secondary: #00ff41; --secondary-foreground: #000000; --muted: #0a0a0a; --muted-foreground: rgba(0, 255, 65, 0.6); --accent: #c8ff00; --accent-foreground: #000000; --destructive: #ff3333; --destructive-foreground: #000000; --border: rgba(0, 255, 65, 0.3); --input: rgba(0, 255, 65, 0.3); --ring: #c8ff00; /* DSR cross-family token contract */ --ds-radius-sm: 2px; --ds-radius-md: 3px; --ds-radius-lg: 4px; --ds-radius-card: 3px; --ds-radius-full: 9999px; --ds-font-display: var(--vs-font-display); --ds-font-body: var(--vs-font-mono); --ds-shadow-card: 0 0 8px rgba(0, 255, 65, 0.15); --ds-shadow-elevated: 0 0 16px rgba(0, 255, 65, 0.2); --ds-border-weight: 1px; --ds-border-style: solid; /* vectorsight family tokens */ --vs-font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace; --vs-font-display: "Inter", "Helvetica Neue", ui-sans-serif, sans-serif; --vs-mesh-green: #00ff41; --vs-mesh-green-dim: rgba(0, 255, 65, 0.5); --vs-mesh-green-faint: rgba(0, 255, 65, 0.15); --vs-focal: #c8ff00; --vs-focal-glow: rgba(200, 255, 0, 0.25); --vs-scanline: rgba(0, 255, 65, 0.12); --vs-text-bright: #ffffff; --vs-incident-amber: #ffaa00; --vs-incident-red: #ff3333; } @theme inline { --font-sans: var(--ds-font-body); --font-mono: var(--vs-font-mono); --font-display: var(--ds-font-display); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-signal: var(--vs-mesh-green); --color-signal-faint: var(--vs-mesh-green-faint); --color-caution: var(--vs-incident-amber); --radius-sm: var(--ds-radius-sm); --radius-md: var(--ds-radius-md); --radius-lg: var(--ds-radius-lg); --radius-xl: var(--ds-radius-lg); --shadow-card: var(--ds-shadow-card); --shadow-elevated: var(--ds-shadow-elevated); } @layer base { * { border-color: var(--color-border); } html { color-scheme: dark; } body { margin: 0; min-height: 100svh; background-color: var(--color-background); color: var(--color-foreground); font-family: var(--font-sans); -webkit-font-smoothing: antialiased; } ::selection { background: var(--vs-focal); color: var(--primary-foreground); } :focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; } } @layer components { /* Scanline ground: the family's signature wireframe field. */ .vs-scanfield { background-image: repeating-linear-gradient( to bottom, var(--vs-scanline) 0px, var(--vs-scanline) 1px, transparent 1px, transparent 5px ); } /* Single-pixel mesh divider between console sections. */ .vs-rule { height: var(--ds-border-weight); background: linear-gradient(to right, var(--vs-mesh-green), transparent); opacity: 0.5; } .vs-glow { text-shadow: 0 0 10px var(--vs-focal-glow); } .vs-eyebrow { font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase; color: var(--vs-focal); } } `; } function utilsSource() { return `import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } `; } /** * Theme adapter: the single switch point between approved registry items. * Product components consume semantic tokens only and never read these names. */ function themeAdapterSource() { const switchable = DSR_THEME.switchableItems.map((name) => " '" + name + "'").join(',\n'); return provenanceHeader('/*', ' *', ' */') + ` export const REGISTRY_ITEMS = [ ${switchable}, ] as const; export type RegistryItem = (typeof REGISTRY_ITEMS)[number]; /** The item whose payload is currently vendored into src/app.css. */ export const ACTIVE_REGISTRY_ITEM: RegistryItem = '${DSR_THEME.item}'; export const ACTIVE_REGISTRY_VERSION = '${DSR_THEME.version}'; /** * Swap the active item by re-pulling its payload and updating both * ACTIVE_REGISTRY_ITEM above and activeItem in design-system.registry.json: * * npx shadcn@latest add @design-system/<item> */ export function registryInstallCommand(item: RegistryItem): string { return 'npx shadcn@latest add @design-system/' + item; } `; } function componentsJsonSource() { const config = { $schema: 'https://ui.shadcn.com/schema.json', style: 'new-york', rsc: false, tsx: true, tailwind: { config: '', css: 'src/app.css', baseColor: 'neutral', cssVariables: true, prefix: '', }, aliases: { components: '@/components', ui: '@/components/ui', lib: '@/lib', utils: '@/lib/utils', hooks: '@/hooks', }, iconLibrary: 'lucide', registries: { '@design-system': DSR_THEME.registryUrl + '/{name}.json', }, }; return JSON.stringify(config, null, 2) + '\n'; } function designSystemManifestSource({ installedAt }) { const manifest = { $schema: 'https://design-system-registry.local/schemas/design-system.registry.schema.json', manifestVersion: 1, upstream: { name: 'design-system-registry', registryUrl: DSR_THEME.registryUrl, sourceRepo: DSR_THEME.sourceRepo, }, activeItem: { name: DSR_THEME.item, version: DSR_THEME.version, payloadUrl: DSR_THEME.payloadUrl, installCommand: DSR_THEME.installCommand, installedAt, provenance: 'Payload vendored into src/app.css by the spaps CLI scaffolder; values match ' + DSR_THEME.sourcePayload + ' at meta.registry.version ' + DSR_THEME.version + '.', }, adapter: { family: 'web-shadcn-theme', path: 'src/design-system/theme-adapter.ts', tokenProjection: 'source', switchableItems: DSR_THEME.switchableItems, surfaces: [ { route: '/', role: 'authentication reference console', proofMethod: 'visual-check', }, ], notes: 'Generated by `spaps create --template react`. The page composes shadcn semantic tokens only; no local palette, radius scale, or shadow system.', }, verification: { command: 'npm run typecheck && npm run build', visualChecks: ['authentication reference console at http://localhost:5173'], }, driftPolicy: { updateMode: 'manual', forkAllowed: false, }, }; return JSON.stringify(manifest, null, 2) + '\n'; } /* ------------------------------------------------------------------ */ /* shadcn primitives (upstream shadcn/ui shapes, driven by DSR tokens) */ /* ------------------------------------------------------------------ */ function buttonSource() { return `import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const buttonVariants = cva( 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-xs font-medium uppercase tracking-[0.12em] transition-[background,box-shadow,color] disabled:pointer-events-none disabled:opacity-40', { variants: { variant: { default: 'bg-primary text-primary-foreground shadow-[0_0_12px_var(--vs-focal-glow)] hover:brightness-110', outline: 'border border-border bg-transparent text-foreground hover:bg-signal-faint hover:border-primary', ghost: 'bg-transparent text-muted-foreground hover:text-foreground hover:bg-signal-faint', destructive: 'bg-destructive text-destructive-foreground hover:brightness-110', }, size: { default: 'h-10 px-4', sm: 'h-8 px-3 text-[0.6875rem]', lg: 'h-11 px-6', }, }, defaultVariants: { variant: 'outline', size: 'default' }, } ); export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {} export function Button({ className, variant, size, ...props }: ButtonProps) { return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />; } export { buttonVariants }; `; } function cardSource() { return `import * as React from 'react'; import { cn } from '@/lib/utils'; type DivProps = React.HTMLAttributes<HTMLDivElement>; export function Card({ className, ...props }: DivProps) { return ( <div className={cn( 'flex flex-col gap-4 rounded-md border border-border bg-card p-5 text-card-foreground shadow-card', className )} {...props} /> ); } export function CardHeader({ className, ...props }: DivProps) { return <div className={cn('flex flex-col gap-1', className)} {...props} />; } export function CardTitle({ className, ...props }: DivProps) { return ( <h2 className={cn('text-sm font-medium uppercase tracking-[0.18em] text-foreground', className)} {...props} /> ); } export function CardDescription({ className, ...props }: DivProps) { return <p className={cn('text-xs leading-relaxed text-muted-foreground', className)} {...props} />; } export function CardContent({ className, ...props }: DivProps) { return <div className={cn('flex flex-col gap-3', className)} {...props} />; } export function CardFooter({ className, ...props }: DivProps) { return <div className={cn('flex flex-wrap gap-2', className)} {...props} />; } `; } function fieldSource() { return `import * as React from 'react'; import { cn } from '@/lib/utils'; export function FieldGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { return <div className={cn('flex flex-col gap-3', className)} {...props} />; } export function Field({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { return <div className={cn('flex flex-col gap-1.5', className)} {...props} />; } export function FieldLabel({ className, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) { return ( <label className={cn( 'text-[0.6875rem] font-medium uppercase tracking-[0.18em] text-muted-foreground', className )} {...props} /> ); } export function FieldDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) { return <p className={cn('text-xs leading-relaxed text-muted-foreground', className)} {...props} />; } `; } function inputSource() { return `import * as React from 'react'; import { cn } from '@/lib/utils'; const control = 'w-full rounded-sm border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:border-ring disabled:opacity-40'; export function Input({ className, ...props }: React.InputHTMLAttributes<HTMLInputElement>) { return <input className={cn(control, 'h-10', className)} {...props} />; } export function Textarea({ className, ...props }: React.TextareaHTMLAttributes<HTMLTextAreaElement>) { return <textarea className={cn(control, 'min-h-20 resize-y font-mono text-xs', className)} {...props} />; } export function Select({ className, ...props }: React.SelectHTMLAttributes<HTMLSelectElement>) { return <select className={cn(control, 'h-10', className)} {...props} />; } `; } function badgeSource() { return `import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const badgeVariants = cva( 'inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-[0.6875rem] uppercase tracking-[0.14em]', { variants: { variant: { default: 'border-border bg-signal-faint text-foreground', muted: 'border-border/40 bg-transparent text-muted-foreground', focal: 'border-primary bg-transparent text-primary', danger: 'border-destructive bg-transparent text-destructive', }, }, defaultVariants: { variant: 'default' }, } ); export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {} export function Badge({ className, variant, ...props }: BadgeProps) { return <span className={cn(badgeVariants({ variant }), className)} {...props} />; } `; } module.exports = { DSR_THEME, DSR_DEPENDENCIES, DSR_DEV_DEPENDENCIES, reactStylesSource, utilsSource, themeAdapterSource, componentsJsonSource, designSystemManifestSource, uiPrimitiveSources: () => ({ 'src/components/ui/badge.tsx': badgeSource(), 'src/components/ui/button.tsx': buttonSource(), 'src/components/ui/card.tsx': cardSource(), 'src/components/ui/field.tsx': fieldSource(), 'src/components/ui/input.tsx': inputSource(), }), };