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>
8.3 KiB
I have all I need. Here is the map.
Instructor App — Home (Meus cenários) + Running Simulation UI
Base path: /Users/viniciusrosa/Documents/GitHub/technomar/instructor/src/renderer/src
(a) Home / "Meus cenários" — scenario list
Layout & flow
app/Home/index.tsx— root flex layout, full viewport (100vh). LeftSidebar+ main content switched byactiveSidebarMenu(only'Scenario'→<MyScenarios />is wired today). HoldsopenSettingsboolean → rendersSettingsModaloverlay.app/Home/MyScenarios/index.tsx— the scenario grid. ScrollableContentContainerwith infinite scroll (scrollHandlerloadsnextPageat 80% scroll viafetchScenariosPage). RendersTopBar, a state component, the grid ofCardButtons, a loading ring, an end-of-list message, and theReplayModal(in anOverlay).app/Home/NewScenario/index.tsx— "Novo cenário" route (/new-scenario):Header mode="NewScenario", full-screenMapwithAreaRender, andNewScenarioPanel(area picker + form). Submitting creates a scenario then navigates to/edit-scenario/:id.
Components
- Sidebar (
features/home/components/Sidebar): brandHeaderwithActionMenudropdown (open ticket, view manual, settings, exit — last 3 areconsole.infostubs), middle menu list (single "Meus cenários" map item, also a stubonClick),Footerwith Settings button. Active state via$isActive. - TopBar (
features/home/myScenarios/components/TopBar): left title toggles "Meus cenários" ↔ "Resultados da pesquisa"; right side: debounced (500ms) searchInput(search icon), grid/list view toggle buttons (grid/list— visual only, no handlers), "Carregar Replay" button (opens ReplayModal), "Novo cenário" accent button → navigates/new-scenario. - CardButton (
features/home/myScenarios/components/CardButton): scenario card. Has loading skeleton variant. TopIconsRowcounts (ownships/targets/tugs/anchors/lines with ship/target/ship-side/anchor/line icons). "Ownships" section (first 2 names). "Condições ambientais" section: 2×2 grid of wind/current/waves/swell formatted asdir° speedknts,Hm Ps dir°(JetBrains Mono font). Footer: name + area name + UTC datetime (dd/mm/yyyy hh:mm). Hover/menu revealsActionsContainer: "Abrir" button + dotsActionMenu(Duplicar/Renomear/Excluir). Card click →setSelectedScenarioId+ navigate/edit-scenario/:id?name=. - ModalInput — rename/duplicate dialog (pre-filled name, e.g. duplicate uses
"<name> (Copy)"). Delete usesConfirmationDialog(danger). - States (
features/home/myScenarios/components/States/index.ts): keyed map{ loading, error, empty, emptySearch, default }.EmptyStateshowsno-scenariosicon + header/description + "Novo cenário" button;errorhasonRetry;loadingusesRingAnimation.
State (MyScenarios)
scenarios, scenariosFiltered, state (StateKey), nextPage, searchValue, replayModalVisible; canReqNextPage ref guards pagination. Selected scenario id lives in useScenarioStore (zustand). Search > 0 chars switches the rendered list to filtered results (fetched with limit: 999).
(b) Running simulation UI
Entry: app/RuntimeSimulation/index.tsx (route renders during simulation; Header mode="SimulatingScenario").
Layout
Container→ topHeader(toolbar),ContentArea(map + panels), bottom-centeredSimulationContainer(transport controls).HeaderdynamicProps expose the running toolbar: move tools (move/bodies/vessels/buoys/SAR), free/fixed camera toggle, measurement, and panel toggles — Information (onOpenInformation), Environment (onOpenEnvironment), Settings (onOpenSettings), Search & Rescue (onOpenSar), pluscurrentTimeclock. Ship/Bollard/Line buttons are stubs (console.info('Temp')).ContentArea:RuntimeMap(MapLibre viareact-map-gl) rendered once loaded;AnimatePresence/framer-motion fade betweenLoadingScenarioand content. Right side showsPanelContainerplus aZoomContainerwithCompassControl+ZoomControl. Layout depends ongetPanelVariant()—'Sheet'variant wraps both in aRightRail; otherwise zoom controls sit left of the panel.
Panels, sheets, windows
- Docked right sheets (toggled open/closed via
usePanel, position'right'):ScenarioInformationsheet (sheets/ScenarioInformation) — "Informações gerais", info icon.ScenarioInfoCard+SimulationInfoCard(maneuver-save flags, evaluate participants, debriefing notes). Refreshes onenvironment.general.update.EnvironmentalConditionssheet (sheets/EnvironmentalConditions) — "Condições ambientais", cloud icon. Tabs: "Geral", "Correnteza, vento e ondas", "Visibilidade".Settingssheet (sheets/Settings).
- Vessel control panels (
features/simulationScenario/panels/*, rendered insidePanelContainer):Ownship—Paneltitled "Ownship" + vessel name; tabs: Geral, AIS/COLREG, Lights/Shapes/Sounds, Morse Signals, Alarmes/Falhas, MCA, Lines, Anchor. Left actions: focus map, camera dropdown (Front/Bridge/Back/Starboard/Portside view), collision sensor window, "Controle do ownship" (opens control window). Right action menu: "Mover". Livebody.upd.positionupdates.Tug—Paneltitled "Tug" + name; tabs: Geral, AIS/COLREG, Lights/Shapes/Sounds, Morse Signals. Left actions: focus map + "Controle do Rebocador". Right menu: Mover.Target,Sarpanels also present.
- Detached control windows (opened via
window.instructorAPI.openNewWindow, own routes):windows/OwnshipControl(/ownship-control?bodyId=, 850×620) — "Controle do Ownship".RudderCard(Leme) + twoAzimuthalCards (azimuthal-left/right) + twoTunnelCards +EngineCard.windows/TugControl(/tug-control?bodyId=, 478×635) — tabs Control + Information.windows/CollisionAndBathymetry.- Control cards live in
windows/cards/*:EngineCard(EOT "Potência" telegraph buttons viaeotOptions, verticalSlider0–10, per-step % inputs, power checkbox, "RPM Máximo",thruster-idpopover),RudderCard,AzimuthalCard,TunnelCard,ForceCard,LinkModeCard,IDsCard,InformationCard. All use react-hook-form + zod, push viawindow.instructorAPI.upd*and refresh onbody.vessel.upd.thruster.*events.
Transport controls
features/editScenario/components/SimulationControl/index.tsx (reused): bottom bar with Timer (elapsed currentTime, HH:MM:SS), Play/Pause toggled by EditorState (Playing→pause, Paused→play), and Stop (danger) → opens EndSimulationHandler (end-simulation/evaluation/student-evaluation modals).
Telemetry / live data
RuntimeSimulation subscribes to: simulation.upd.time (clock), body.upd.collision and body.upd.bathymetry.collision → fetch collided vessel name, show error Alerts ("Colisão detectada!"). State: cameraMode, isLoading, currentTime, points (area bounds), collision, bathymetryCollision, vesselCollided. Tool state via useEditScenarioStore (currentTool, currentMovingBodyId, newBody, previewHeading, currentMapType). Selection modals gated on currentTool: VesselSelection (selectVessel), BollardSelection (selectBollard), SarSelection (selectSar).
Key Portuguese strings (config/intl/translations-pt-BR.json)
- my-scenarios "Meus cenários" · new-scenario "Novo cenário" · search-for-scenario "Pesquisar cenário" · search-results "Resultados da pesquisa" · load-replay "Carregar Replay" · reach-end "Você chegou ao fim" · ownships "Ownships" · environmental-conditions "Condições ambientais"
- general "Geral" · general-information "Informações gerais" · current-wind-waves "Correnteza, vento e ondas" · visibility "Visibilidade"
- ownship-control "Controle do Ownship" · control-ownship "Controle do ownship" · tug-control "Controle do Rebocador" · engine "Máquina" · power "Potência" · rudder "Leme" · thruster-id "ID do Thruster" · max-rpm "RPM Máximo"
- focus-map "Focar no mapa" · camera-control "Controle da Câmera" · front-view "Visão frontal" · bridge-view "Visão da ponte" · move "Mover" · collision-detected-title "Colisão detectada!"
Notable stubs / non-wired UI
- Sidebar: open-ticket / view-manual / exit are
console.infostubs; menu item onClick is a stub. - Header (SimulatingScenario): Ship/Bollard/Line buttons log
'Temp'. - TopBar grid/list view toggles have no handlers.