This directory contains the React / TypeScript frontend for HardwareVisualizer.
The frontend talks to the Rust backend through Tauri commands generated by
tauri-specta.
src/
├── app/ # App layer: composition root that may import anything
│ ├── App.tsx # App shell, settings load, language/theme setup
│ ├── lazyScreens.ts # Lazy screen loading and prefetch
│ ├── *ErrorFallback.tsx # Screen and root error boundary fallbacks
│ ├── FullScreenButton.tsx # Full screen toggle in the app shell
│ └── *Dialog.tsx, *Notice.tsx # App-wide startup dialogs and notices
├── main.tsx # React entry point
├── components/ # Reusable UI, charts, shared components
│ ├── ui/ # Radix/Tailwind primitives (shadcn, kebab-case files)
│ ├── icons/ # Icon components
│ ├── charts/ # Recharts wrappers
│ └── *.tsx # Shared components: ActionFailure, BurnInShift, InfoTable, LoadFailure, NeedRestart, SettingWriteFailure
├── features/ # Feature-owned UI, hooks, stores, and types
│ ├── hardware/ # Dashboard, usage graphs, insights
│ ├── settings/ # Settings screen and settings state
│ ├── menu/ # Sidebar navigation
│ ├── tray/ # Tray widget flyout UI
│ └── updater/ # App update UI
├── hooks/ # Cross-feature React hooks, grouped by concern
│ ├── appearance/ # Color theme, background image, burn-in shift, title icon
│ ├── navigation/ # Display target and navigation focus requests
│ ├── settings/ # Settings atom, tray, elevation, restart-required
│ ├── tauri/ # Tauri dialog, event listener, and store wrappers
│ └── window/ # Full screen, visibility, window size, input, sticky observer
├── lang/ # i18next translation JSON files
├── lib/ # Framework-agnostic helpers and Tauri wrappers
├── rspc/bindings.ts # Generated Tauri command bindings; do not edit
├── store/ # Cross-feature Jotai atoms
└── types/ # Shared frontend-only types
Keep feature-specific code under features/<feature>/ when the code is not
meaningfully reused elsewhere. Put reusable primitives in components/,
cross-feature hooks in hooks/, and pure helpers in lib/. The root
directories are the shared layer, organized by kind; group by concern in
sub-folders inside them, not with new top-level directories.
File names match their export: components are PascalCase .tsx, every other
module is camelCase .ts. components/ui/ keeps shadcn's kebab-case names, and
a test file may use either case. npm run lint:ci enforces this with
useFilenamingConvention.
Imports point only downward:
foundation (rspc, types, consts, lib) -> store -> hooks -> components/ui
-> components/{charts,*.tsx} -> features/<feature> -> app
src/app/is the app layer. It composes features and app-wide dialogs and notices, and may import anything.src/main*.tsxonly mountsApp.- Lower layers never import
@/app/**. A feature that needs something from the app layer (for example screen prefetch) receives it as a prop. - A feature never imports another feature.
- Cross-directory imports use
@/. - Atoms are declared only in
store/modules. - Components read atoms with
useAtomValueand write through hooks.
npm run lint:ci enforces these rules through
.config/biome/frontend-boundaries.jsonc. See
ADR 0027
and the Design Doc. No pending
exceptions remain; a new one is a design discussion, not a config edit.
- Use generated commands from
src/rspc/bindings.ts. - Do not edit
src/rspc/bindings.tsby hand. - To expose a new backend command, add/register the Rust command in
src-tauri, runnpm run tauri:dev, then consume the regenerated binding. - Command results use the typed
Resultshape fromsrc/types/result.ts; check for errors before readingdata.
Backend error events are emitted as error_event. The frontend listener lives
in src/hooks/tauri/useTauriEventListener.ts and displays the error dialog.
Use the existing boundary when choosing where state lives:
- User-facing application preferences belong in
settings.json. Add typed Rust setter commands insrc-tauri/src/commands/settings.rs, persist through the Rust settings service, regenerate bindings, and updatesrc/store/settings.tsandsrc/hooks/settings/useSettingsAtom.ts. - UI-local or transient state can use Tauri Store through
src/lib/tauriStore.tsandsrc/hooks/tauri/useTauriStore.ts. Examples include ephemeral selections, cached UI choices, or view state that can be reset without losing an explicit user configuration. - Declare Jotai atoms in a
store/module:src/features/<feature>/store/for feature state, andsrc/store/when the state is genuinely shared. Group a feature's atoms by subject (for exampleliveUsage,gpu,selection) so a subject can change without touching the others. - Per-store mutable state (counters, coalescing flags) also lives in a
store/module, as aWeakMapkeyed by the Jotai store. Components read atoms withuseAtomValue; every write is a named function returned by a hook.
Do not write user-facing preferences directly from the frontend with Tauri Store.
Translations use i18next and react-i18next.
Current language files live in src/lang/:
en.jsonja.jsonru.json
src/lib/i18n.ts imports these files and registers them as i18next resources.
src/types/i18next.d.ts keeps translation resources typed. The Settings
language selector derives its options from registered i18next resources, while
the Rust language service controls the default language fallback for first run.
New officially supported languages must be registered in both frontend i18next
resources and Rust SUPPORTED_LANGUAGES.
See docs/development/add-language.md
for the full checklist.
- Tailwind CSS is the styling layer.
- Reuse components from
src/components/ui/before adding new primitives. - Use feature-owned components for screen-specific UI.
- Use
react-i18nextfor display strings instead of hardcoded user-facing text. - Keep chart-specific wrappers in
src/components/charts/unless the chart is tightly owned by one feature.
Frontend tests are co-located with the code they cover:
- components:
Component.test.tsx - hooks:
useThing.test.ts - pure helpers:
helper.test.ts - feature workflows: under the owning
features/<feature>/tree - render fan-out:
src/features/hardware/renderFanout.test.tsxholds per-screen upper bounds on what one 1 Hz update re-renders; see Live metrics subscriptions
Use Vitest and Testing Library patterns already present in the repository. Prefer focused tests around behavior, command result handling, formatting, and state transitions.