The frontend is rebuilt screen by screen from the handoff: warm cream ground, terracotta + sage accents, Caprasimo over Figtree, every control a pill. Same React/Vite/TanStack stack and the same lib/ data layer; the presentation is new. - Tokens: web/src/styles/index.css declares the handoff's styles.css variables through Tailwind's @theme under the same names; dark mode is those variables overridden on <html> by the handoff's pansy-theme.js, inlined in index.html so it runs before first paint. Lucide glyphs at stroke 2.75; a small pill kit (Button, Dialog, Field, Seg, Toggle, Tag, toast). - Login / Register: the centered column over soft accent circles; OIDC button and signup footer still follow /auth/providers. - Gardens: cards with a real SVG plot thumbnail (objects + plant-colored dots from /full), a `plan` tag for "<name> — <year>" copies, shares line, Open + share/copy/edit/delete; New garden / Share / Plan-a-season dialogs. - Plants: monogram markers derived from the name (collision-resolved across the catalog — replaces emoji icons), category chips, expandable lot cards, the scan-packet flow as a two-step dialog that never auto-creates. - Settings: Appearance (theme seg), Who gets in (read-only sign-in config), Garden assistant (self-saving toggle + chat/vision model fields), You. - Editor: a new canvas with the prototype's pointer model (wheel-to-cursor, pinch about the centroid, 3″ snap, one PATCH per drop, semantic-zoom monograms/labels), plus corner resize handles; desktop three-card workspace (toolkit | plan | rail with Plot/Journal/History/Assistant) and, below 760px of container width, the phone chrome (header, peek panel, tool strip, mode bar). Seasons as a segmented control over the years with data plus plan copies; Undo re-reads history before reverting the newest step. - Public read-only view and the register page restyled to match. - GET /settings gains a read-only `auth` view (registration mode, local auth, OIDC issuer) so the Settings page can show what's in force. - README / DESIGN.md / CLAUDE.md updated; @use-gesture/react dropped. Co-Authored-By: Claude Fable 5 <[email protected]>
50 lines
1.1 KiB
TypeScript
50 lines
1.1 KiB
TypeScript
import { cn } from '@/lib/cn'
|
|
|
|
export interface SegOption<T extends string> {
|
|
value: T
|
|
label: string
|
|
title?: string
|
|
}
|
|
|
|
/**
|
|
* The segmented pill control: options on a neutral-200 track, the active one
|
|
* lifted on a neutral-100 pill with a soft shadow. Used for seasons, the theme,
|
|
* units, and read-only status displays (pass `disabled`).
|
|
*/
|
|
export function Seg<T extends string>({
|
|
options,
|
|
value,
|
|
onChange,
|
|
disabled,
|
|
className,
|
|
optionClassName,
|
|
label,
|
|
}: {
|
|
options: SegOption<T>[]
|
|
value: T
|
|
onChange?: (v: T) => void
|
|
disabled?: boolean
|
|
className?: string
|
|
optionClassName?: string
|
|
/** Accessible group name. */
|
|
label?: string
|
|
}) {
|
|
return (
|
|
<span className={cn('seg', className)} role="group" aria-label={label}>
|
|
{options.map((o) => (
|
|
<button
|
|
key={o.value}
|
|
type="button"
|
|
className={cn('seg-opt', optionClassName)}
|
|
aria-pressed={o.value === value}
|
|
disabled={disabled}
|
|
title={o.title}
|
|
onClick={() => onChange?.(o.value)}
|
|
>
|
|
{o.label}
|
|
</button>
|
|
))}
|
|
</span>
|
|
)
|
|
}
|