diff --git a/selfdrive/carrot/README.md b/selfdrive/carrot/README.md index de2193342d..7a9b5583f4 100644 --- a/selfdrive/carrot/README.md +++ b/selfdrive/carrot/README.md @@ -11,6 +11,7 @@ Anyone can modify. Refer to the structure below. ``` server/ +├── __init__.py ├── app.py composition root: middleware, lifecycle, make_app() ├── config.py constants (paths, URLs, tmux session, etc.) ├── live_runtime/ cereal SubMaster broker for /api/live_runtime @@ -30,6 +31,7 @@ server/ │ ├── time_sync.py browser → system time sync │ ├── device_info.py focused calibration + network helpers for Device tab │ ├── setting_favorites.py CarrotPilot setting favorites state +│ ├── setting_profiles.py CarrotPilot setting profile CRUD + import/export │ ├── web_settings.py device/server-backed Web Settings state │ └── tmux.py tmux session helpers └── features/ HTTP entry points (one feature per file/folder) @@ -39,6 +41,7 @@ server/ ├── settings.py /api/settings ├── params.py /api/params_*, /download/params_backup.json ├── setting_favorites.py /api/setting_favorites + ├── setting_profiles.py /api/setting_profiles, profile import/export ├── web_settings.py /api/web_settings ├── ssh_keys.py /api/ssh_keys ├── cars.py /api/cars @@ -83,12 +86,20 @@ web/ │ ├── layout.css page container, swipe, headings, sections │ ├── components.css dialog, toast, buttons, setting items, transitions │ ├── responsive.css desktop + mobile media queries (loads last) +│ ├── vendor/ +│ │ └── plyr.css Plyr video player styles │ └── pages/ │ ├── logs.css Logs/Dashcam page │ ├── drive.css WebRTC video + Carrot stage -│ ├── settings.css Settings page styles (includes device tab) │ ├── terminal.css Terminal page styles -│ └── tools.css Tools page styles +│ ├── settings/ Settings page styles, split for readability +│ │ ├── base.css page base, car entry, FAB menu (open/close anim) +│ │ ├── panels.css search panel, group list, profile sections, toolbar +│ │ └── device.css Device tab, settings-diff dialog, subnav, responsive +│ └── tools/ Tools page styles, split by feature +│ ├── base.css page base, meta/lang, Web Settings dialog +│ ├── qr.css QR code dialog +│ └── main.css groups, progress, notifications/console, responsive └── js/ ├── app.js bootstrap: popstate, initial showPage() ├── shared/ cross-page modules @@ -97,6 +108,7 @@ web/ │ ├── utils.js escapeHtml, clamp, copyToClipboard, quick link │ ├── i18n.js bootstrapped LANG, getUIText, renderUIText, setWebLanguage │ ├── api.js bulkGet, setParam, postJson, getJson, waitMs + │ ├── setting_diff.js setting-diff dialog helpers (used by settings + tools) │ ├── activity.js cross-page activity badges + beforeunload guard │ └── ui/ │ ├── dialog.js appAlert/Confirm/Prompt + toast @@ -112,17 +124,28 @@ web/ │ ├── setting_device_actions.js Device action/dialog handlers │ ├── setting_device.js Device tab coordinator and state │ ├── tools_web_settings.js server-backed Web Settings dialog + │ ├── tools_notifications.js Tools-tab notification preview/composer + │ ├── tools_settings_qr.js Settings QR import/export │ ├── tools.js tools page + initToolsPage + action runners │ ├── branch.js branch picker modal + Branch page - │ ├── logs.js Dashcam + Screen Recording lists - │ └── terminal.js tmux WebSocket client + │ ├── logs/ Logs page, split by tab + │ │ ├── shared.js tab state, scroll persistence, lazy-image observer, + │ │ │ video player, bind/init + │ │ ├── dashcam.js Dashcam tab: virtual route+segment list, upload subsystem + │ │ └── screenrecord.js Screen Recording tab: virtual list, lazy thumbs + │ ├── terminal.js tmux WebSocket client + │ └── vision_background.js static background for non-realtime pages ├── translations/ ko/en/zh/ja/fr + registry.js - └── realtime - app_realtime.js live runtime/raw stream wiring + HUD payload bridge - home_drive.js Carrot Vision renderer and overlay canvas - hud_card.js adaptive driving HUD card - raw_capnp.js raw capnp decoders for HUD/overlay state - raw_capnp_worker.js worker entry for raw capnp decoding + ├── realtime/ realtime stream stack (loaded together) + │ ├── hud_card.js adaptive driving HUD card + │ ├── raw_capnp.js raw capnp decoders for HUD/overlay state + │ ├── raw_capnp_worker.js worker entry for raw capnp decoding + │ ├── vision_state.js shared vision/HUD state + │ ├── vision_rtc.js WebRTC vision stream client + │ ├── vision_raw.js raw WebSocket vision client + decoder worker bridge + │ ├── app_realtime.js live runtime/raw stream wiring + HUD payload bridge + │ └── home_drive.js Carrot Vision renderer and overlay canvas + └── vendor/ third-party libraries (Plyr, jsQR, qrcode-generator) ``` ### Settings page tab structure @@ -137,8 +160,457 @@ The Setting page has two top-level tabs: Device tab adapts to hardware via `DeviceType` param (`tici`/`mici`/`tizi`). Load order (set in `index.html`): -`tokens → layout_tokens → hud_card → base → layout → pages/* → components → responsive` -then JS: -`translations → hud_card → shared/* → shared/ui/* → pages/* → realtime/* → app.js` -CSS files merge byte-identical with the previous single `app.css` if concatenated in the order above. JS scripts share the same global realm — top-level `let`/`const` are visible across files. +CSS: +``` +tokens → layout_tokens → hud_card → base → layout → components + → pages/logs → pages/terminal + → pages/settings/base → pages/settings/panels → pages/settings/device + → pages/tools/base → pages/tools/qr → pages/tools/main + → pages/drive → responsive → vendor/plyr +``` + +JS: +``` +vendor/* → translations → shared/* → shared/ui/* → pages/* → pages/logs/* → realtime/* → app +``` + +CSS files merge byte-identical with the previous single `settings.css` and `tools.css` if concatenated in the order above. JS scripts share the same global realm — top-level `let`/`const` are visible across files (so the logs split files all see the shared `logsActiveTab`, `dashcamState`, `screenrecordState`, etc.). + +### Recovery server (standalone) + +``` +recovery/ +├── __init__.py +└── server.py port 6999, minimal self-contained recovery UI +``` + +--- + +## Design System Reference + +Everything below is a working contract. **When in doubt, copy the pattern.** +Don't invent new motion durations, shadow stacks, z-index numbers, or focus +ring colors — use the tokens. New components should compose existing +primitives before adding their own. + +All tokens live in [css/tokens.css](web/css/tokens.css) with usage comments next to each group. + +### Color (Material 3 dark) + +#### Surface & text + +| Token | Use for | +|---|---| +| `--md-surface` | page background | +| `--md-surface-cont` | cards, list rows | +| `--md-surface-cont-l` | slightly recessed (input field background) | +| `--md-surface-cont-h` | raised surfaces (dialog sheet, popover) | +| `--md-surface-cont-hh` | nested raised (chip on a card) | +| `--md-surface-bright` | highlight surface (selected row hover) | +| `--md-on-surface` | primary text | +| `--md-on-surface-var` | secondary text, captions | +| `--md-outline` / `--md-outline-var` | borders, dividers | + +#### Brand & state surfaces + +| Token | Use for | +|---|---| +| `--md-primary` | accent (Carrot orange) | +| `--md-on-primary` | text on a primary-filled surface | +| `--md-action-filled` / `--md-on-action-filled` | primary action button (filled variant) | +| `--md-primary-state-soft` / `-state` / `-state-strong` | hover/pressed surfaces tinted by primary | + +#### Semantic status + +Use these — don't hardcode greens, ambers, blues. Each family has a base color, a `-strong` accent, a `-cont` (container surface for chips/badges), and an `-on-*-cont` (text on that container). + +| Family | Base | When | +|---|---|---| +| Success | `--md-success` (`#8fdc9b`) | confirmation, "OK", restored states | +| Warning | `--md-warning` (`#ffc94a`) | non-critical alerts, slow network, "may take a while" | +| Info | `--md-info` (`#7dd3fc`) | informational hints, "did you know" | +| Error | `--md-error` (`#ff9d94`) | soft errors — form validation, failed toast | +| Danger | `--md-danger` (`#ff8a80`) | alarm-level — active hazard, irreversible destructive | + +**Example — semantic chip:** +```html +Saved +Slow network +Recording +``` + +`prefers-contrast: more` shifts surface and outline tokens automatically — don't override per-component. + +### Motion + +| Duration | Value | Use for | +|---|---|---| +| `--motion-instant` | 80ms | state flicks (toggle on/off colour) | +| `--motion-quick` | 140ms | hover, small togglers, taps | +| `--motion-base` | 180ms | default for most things | +| `--motion-medium` | 240ms | FAB menus, sheets entering | +| `--motion-long` | 380ms | page transitions, large slides | + +| Easing | Use for | +|---|---| +| `--ease-standard` | symmetric in/out (default) | +| `--ease-emphasized` | enter / open / expand (decelerates onto place) | +| `--ease-emphasized-accelerate` | exit / close / dismiss (accelerates away) | +| `--ease-linear` | crossfades, progress bars only | + +**Pattern — asymmetric open/close (preferred):** +```css +.menu { transition: opacity var(--motion-quick) var(--ease-emphasized-accelerate); } +.menu.is-open { transition: opacity var(--motion-medium) var(--ease-emphasized); } +``` + +For a worked example see the Setting FAB menu in [css/pages/settings/base.css](web/css/pages/settings/base.css) (`.setting-fab-actions`). + +### State layer (Material 3) + +```css +--state-hover: 0.08; +--state-focus: 0.12; +--state-pressed: 0.12; +--state-dragged: 0.16; +``` + +**Pattern A — use the `.state-layer` helper:** position any interactive element relative, add the class, and it overlays a primary-tinted layer that intensifies on hover/focus/press. +```html + +``` + +**Pattern B — manual color-mix** (when you need control over which color tints): +```css +.row:hover { + background: color-mix(in srgb, var(--md-primary) var(--state-hover-pct), transparent); +} +``` + +### Elevation + +5 levels, dark-tuned, picked smallest-first. + +| Token | Use for | +|---|---| +| `--shadow-1` | hovered/lifted controls | +| `--shadow-2` | default cards, FAB | +| `--shadow-3` | popovers, dropdowns | +| `--shadow-4` | dialogs, sheets | +| `--shadow-5` | fullscreen modals, video player, pickers | + +For brand-tinted elevation (orange FAB) compose with the base shadow rather than re-encoding a coloured shadow inline: +```css +box-shadow: var(--shadow-3), 0 0 0 1px var(--md-primary); +``` + +### Z-index scale + +Use these tokens for cross-component layering. Local stacking inside one component (1/2/3) can stay as raw numbers. + +| Token | Value | Use for | +|---|---|---| +| `--z-base` | 1 | in-flow content | +| `--z-sticky` | 50 | sticky headers, subnav | +| `--z-rail` | 100 | side nav rail (landscape) | +| `--z-nav` | 120 | bottom nav bar | +| `--z-fab` | 130 | FAB / FAB menus | +| `--z-popover` | 150 | dropdowns, tooltips | +| `--z-modal` | 170 | dialogs, sheets, pickers | +| `--z-toast` | 200 | transient toast layer | +| `--z-overlay` | 220 | fullscreen overlays | + +### Focus & reduced motion (global) + +[base.css](web/css/base.css) sets: +- One global `:focus-visible` ring using `--focus-ring-*` tokens — covers every interactive element. Override only when shape requires it. +- `@media (prefers-reduced-motion: reduce)` collapses every animation/transition to 0.001ms so things still *snap* into state without movement. + +Both rules are intentionally broad. Don't recreate them per component. + +### Shared primitives ([components.css](web/css/components.css)) + +These exist so pages don't reinvent the same chip / icon button / loading +skeleton / empty state over and over. Compose them before writing new CSS. + +| Class | Variants | Use for | +|---|---|---| +| `.btn` | `--filled`, `--danger`, `.smallBtn` | text buttons | +| `.icon-btn` | `--circle`, `--ghost`, `--sm`, `--lg` | icon-only buttons (36×36 default) | +| `.chip` | `--accent`, `--danger`, `--success`, `--warning`, `--info` | status tags, counts, labels | +| `.skeleton` | `--circle` | loading placeholders (shimmer) | +| `.empty-state` | `__title`, `__message`, `__action` | "no items" cards in lists | +| `.state-layer` | — | M3 state overlay on any interactive surface | +| `.ui-stagger-item` | (uses CSS var `--i`) | sequenced list reveal animation | +| `.ui-dropdown-menu` | `__button`, `__panel`, `__item`, `--primary`, `--danger` | dropdown menus | +| `.ui-action-grid` | `--quick` | button grids (Tools quick actions) | +| `.app-dialog` | `__sheet`, `__title`, `__body`, `__actions` | dialogs (use the JS API instead) | +| `.app-toast` | `is-error`, `is-success`, `is-hint` | toasts (use `showAppToast` instead) | +| `.visually-hidden` | — | screen-reader-only text | + +#### Worked examples + +**Icon-only button** — picks up the global focus ring automatically: +```html + +``` + +**Status chip:** +```html +3 segments +Connected +Recording +``` + +**Loading skeleton** — animates a shimmer; respects reduced motion: +```html +
+ +``` + +**Empty state:** +```html +