Phase 1 of the Figma "[INSTRUTOR]" port plus hardening found during testing. Design system - theme/tokens.ts + global.css: Figma palette, type scale, spacing, radii, shadows; injected as CSS vars at boot. - ui/: 11 themed primitives (Button, IconButton, TopBar, Panel, Tabs, Card, Slider, Field, Modal/AlertDialog, SimulationControl, MapActions), each tested. App shell (Layout do instrutor) - features/shell/AppShell.tsx: TopBar (logo + breadcrumb, gated center actions, Sair/Salvar), full-bleed map, bottom-center Build/Play/Stop pill, bottom-right zoom, no-ownship AlertDialog. Replaces ScenarioCreatorPage. - stores/mapStore.ts exposes the Leaflet instance so the zoom pill can drive it; AreaMapView registers it and drops Leaflet's default zoom control. Physics truth - Worker reports which engine loaded (real WASM vs JS mock); surfaced as a status badge/warning so the instructor never mistakes the mock for real dynamics. SimulationControls is now a status-only strip (actions moved to the pill). Tests - 229 passing, coverage gate green. New: tokens, all 11 primitives, AppShell, VesselPanel, vesselPanelStore. Fixed GeralTab to read draft by instanceId. Docs - docs/specs/features/instructor-ui/: extracted screen specs, reference maps, phased PLAN.md, and DYNAMICS-GAP.md (MATLAB↔Ymir physics parity). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.9 KiB
9.9 KiB
I have enough to write the inventory.
Instructor Design-System Inventory
Architecture overview
- Stack: React 19 + TypeScript, styled-components 6, Radix UI primitives, framer-motion for panel/tab animations, react-hook-form + zod for forms, i18next for i18n.
- Two layers:
@technomar/react-components(v2.0.15) — the shared design system. Ships the theme (defaultTheme/darkTheme, typeTechnomarTheme),GlobalStyle,IconProvider, toast system, and base primitives (Button, Input, Select, Modal, Sheet, Tabs, Checkbox, Radio, Switch, Tooltip, Alert, Uploader, etc.).- Local
src/renderer/src/components/*— app-specific wrappers and compositions built on top of the library + Radix, all themed via styled-componentstheme.
- Wrapper pattern: local components either (a) thinly re-export a library component widening props (
Button,Toogleextends@technomarButton), or (b) compose Radix primitives styled with theme tokens (RadioGroup,Tabs). Local styled files use the$transientPropconvention (e.g.$active,$fullHeight,$horizontal).
Provider / app wiring (App.tsx)
Nesting order: MapProvider → Theme → TooltipProvider → Language → UnitProvider → ErrorBoundary → HashRouter → WebsocketProvider → Root, with ToastContainer (position bottom-right) and <AppStyles/> siblings.
- Theme (
providers/Theme.tsx):styled-componentsThemeProviderfeddefaultTheme(light) ordarkTheme, selected fromuseParameterizationzustand store (theme='light' | 'dark'). - TooltipProvider: Radix
TooltipProvider delayDuration={0}. - Language: calls
i18n.changeLanguagereactively from store. styles/styled.d.tsaugments styled-componentsDefaultTheme extends TechnomarTheme, sotheme.*is fully typed in every styled block.
Styling approach
- styled-components with template literals; colors/spacing always pulled from
theme.colors.tokens.*— almost no hard-coded colors (shadows are the exception, inlinergba). Example token paths used:theme.colors.tokens.surface.default.main.{primary,secondary,active},...border.default.main.{primary,active},...content.default.main.{primary,sutil}. AppStyles.tsximports Inter (400/500/600/700) + JetBrains Mono fonts via@fontsource, libraryGlobalStyle, the toastify CSS, andglobal.css(only override:div[data-radix-popper-content-wrapper] { z-index: 999 }).- Spacing in styled files is mostly literal px multiples of 4/8 (8, 16, 24); the theme also exposes a
sizesscale (rem-based, key→value e.g.4 → 1rem/16px).
Theme tokens (TechnomarTheme)
- colors.background:
primary,secondary. - colors.text:
primary,secondary. - colors.scrollbar.
- colors.palette: ramps
primary,green,lime,red,yellow,grayeach50…1000; plusblack(amount)/white(amount)functions (0–1 → hex). - colors.tokens (semantic, the primary API):
surface,border,content— each with intentsdefault | negative | positive | warning | accent, each aToken<{main, highContrast?}>. Sub-fields seen in use: surface.main.{primary,secondary,active}, border.main.{primary,active}, content.main.{primary,sutil}. - colors.components: per-component theme slices (button, input, checkbox, radio, select, sheet, switch, tabs, tooltip, modal, alert, icons…). E.g.
ButtonThemehasbackground.{default,danger,success}.{fill,border}state sets +text.*. - sizes: numeric-key spacing scale
0.5 → 96mapping to rem/px (2px…384px). - Typography: Inter (UI) + JetBrains Mono (numeric/mono); component
fontprop typeInputFontType = 'JetBrains Mono' | 'Inter'. Font sizes are literal px in styles (12 sub/label, 14 body/title).
Reusable components (name → purpose → key props/variants)
Library primitives (@technomar/react-components):
- Button → actions.
variant: primary|secondary|outlined|text,type: default|danger|success|accent,size: small|regular|large,icon+iconPosition: leading|trailing,fullWidth,loading,enableRipple,buttonType. - Input → text field.
size,type: regular|error,label,required,helperText,leadingIcon/trailingIcon(+*IconProps),trailingAction,fullWidth,wrapperStyle. - TextArea →
size,type: regular|error,label,helperText,fullWidth,required. - Select / Combobox →
Root+Optioncompound;size,type: default|error,optionsPosition,helperText, controlledvalue/onChange. - Checkbox / CheckboxGroup, Radio / RadioGroup (library's own group), Switch (
leftLabel/rightLabel). - Modal (compound
Root/Header/Content/Footer,width,closeModal), Sheet (compound), Tooltip (label,side,align,sideOffset,delayDuration). - Alert (
type/text/title/icon) +AlertProvider/useAlert, ConfirmationDialog (mainAction/secondaryAction,type: default|success|danger). - Uploader, IconProvider (
icon,size,color), toast + ToastContainer, GlobalStyle.
Local components (components/*):
- PanelContainer → renders docked left/right panels from
usePanelhook with framer-motion slide/fade (spring, stiffness 300/damping 50). No props. - Panel → docked panel shell:
title,name,leftActions,rightActions,children,fullHeight=true,onClose,showClose. Styled container 432px wide, 8px radius, surface bg,backdrop-filter: blur(25px), close via library Buttonvariant="text" icon="close". - Tabs (local) → animated horizontal tabs with overflow "more" dropdown (framer-motion).
tabs: {id,label,content}[],defaultActiveTab,showList,headerSpacing,contentTabSpacing,style/contentStyle. Active underline + fade gradient on overflow. - Card → compound
{ Root, Header, Body }; styled variants via$direction: row|column,$backgroundColor: primary|secondary, 4px radius, bordered. - FormStepper → generic multi-step modal wizard
<Item>:open,steps: FormStep<Item>[],initialContext,onFinish,onClose. Each step:title|content(value or render-fn with{ctx,setCtx,goNext}),icon,width/height,showFooter,onNext/onBack,isNextEnabled. Built on libraryModal/Overlay+ localButton. - ActionButton → split button + dropdown (
ActionMenu= styled Radix dropdown).items: {icon,onSelect,labelKey,separatorBefore,isDisabled}[],buttonIcon,isActive/onToggleActive,tooltipKey,t(passes i18nTFunctionin). Uses libraryTooltip+IconProvider. - EditorInput → inline edit-on-focus wrapper over local
Input:value,onUpdate,required,hasError,disabled+ allInputProps. Commits on Enter, reverts on Escape/blur-with-error. - RadioGroup (local) → styled Radix
RadioGroup.Root, prophorizontal($horizontaltoggles flex-direction), forwardsvalue/defaultValue/onValueChange. - Toogle (note misspelling) → toggle button extending library
Button; controlledactiveor uncontrolleddefaultActive,onToggle(active). Styled with active surface/border tokens + inset shadow, min-height 63px. - Button (local) → thin pass-through of library
Button. - Other locals: Accordion, ActionMenu, Collapsible, ColorInput, CircularSlider, DateTimeInput, DisplayField(/Form), Form (+ InputForm/SelectForm/SwitchForm/TextAreaForm/MultiSelectForm — react-hook-form bindings), Header, Label, Map, Popover, Sheet, Spinner, ThreeDotsAnimation, TooltipSpan, RequiredAsterisk, ButtonToggleText.
- Local
InputPropsextends library with extra app fields:unit,labelSize,font,inputSize,horizontal,autoFocusOnMount,isDirty,leadingIcon/trailingIconasReactNode.
i18n setup (config/intl/)
i18n.ts:i18next+react-i18next(initReactI18next).fallbackLng: 'en',resources: { en: enUS, pt: ptBR }(translation namespace),interpolation.escapeValue: false,saveMissingin dev with a console.warn missing-key handler.- Translation files:
translations-en-US.json(~39KB),translations-pt-BR.json(~41KB), plustranslations.json(~8KB). Note: code/commits in English, but per project memory all docs/PRD artifacts are Portuguese. - Language switched reactively via
providers/Language.tsxfrom theuseParameterizationzustand store; components consumeuseTranslation()(t), and some acceptt/labelKeyprops (e.g. ActionButton) so translation is injected.
Guidance for building matching primitives (Ymir web)
- Mirror the semantic token model:
surface/border/content× intent ×{main,highContrast}, plus palette ramps50…1000andblack()/white()helpers; expose asizesrem scale. Wire via styled-componentsThemeProvider+ aDefaultThemeaugmentation. - Reuse the prop vocabulary:
variant,type(default|danger|success|accent),size(small|regular|large), compound components for Card/Modal/Sheet/Select,$transientstyled props. - Fonts: Inter (UI) + JetBrains Mono (numeric). Radius 4px (cards/inputs) / 8px (panels). Standard gaps 8/16/24px.
- Animations via framer-motion springs (stiffness ~300, damping ~50) for panels/tabs.
- i18n through
react-i18nextwithen/ptresources,escapeValue:false, store-driven language switching.
Key reference paths (all absolute):
/Users/viniciusrosa/Documents/GitHub/technomar/instructor/node_modules/@technomar/react-components/dist/index.d.ts(theme + all component prop types;TechnomarThemeat line 805, tokens 291-313, Button 175, Input 494)/Users/viniciusrosa/Documents/GitHub/technomar/instructor/src/renderer/src/styles/{AppStyles.tsx,styled.d.ts,global.css}/Users/viniciusrosa/Documents/GitHub/technomar/instructor/src/renderer/src/providers/{Theme,Language,TooltipProvider}.tsx/Users/viniciusrosa/Documents/GitHub/technomar/instructor/src/renderer/src/config/intl/i18n.ts/Users/viniciusrosa/Documents/GitHub/technomar/instructor/src/renderer/src/components/{Panel,PanelContainer,Tabs,Card,FormStepper,ActionButton,EditorInput,RadioGroup,Toogle,Button}/