Skip to content

Latest commit

 

History

History
158 lines (127 loc) · 7.44 KB

README.md

File metadata and controls

158 lines (127 loc) · 7.44 KB

Frontend Architecture

This directory contains the React / TypeScript frontend for HardwareVisualizer. The frontend talks to the Rust backend through Tauri commands generated by tauri-specta.

Directory Layout

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.

Layer Boundaries

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*.tsx only mounts App.
  • 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 useAtomValue and 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.

Backend IPC

  • Use generated commands from src/rspc/bindings.ts.
  • Do not edit src/rspc/bindings.ts by hand.
  • To expose a new backend command, add/register the Rust command in src-tauri, run npm run tauri:dev, then consume the regenerated binding.
  • Command results use the typed Result shape from src/types/result.ts; check for errors before reading data.

Backend error events are emitted as error_event. The frontend listener lives in src/hooks/tauri/useTauriEventListener.ts and displays the error dialog.

State And Persistence

Use the existing boundary when choosing where state lives:

  • User-facing application preferences belong in settings.json. Add typed Rust setter commands in src-tauri/src/commands/settings.rs, persist through the Rust settings service, regenerate bindings, and update src/store/settings.ts and src/hooks/settings/useSettingsAtom.ts.
  • UI-local or transient state can use Tauri Store through src/lib/tauriStore.ts and src/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, and src/store/ when the state is genuinely shared. Group a feature's atoms by subject (for example liveUsage, 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 a WeakMap keyed by the Jotai store. Components read atoms with useAtomValue; every write is a named function returned by a hook.

Do not write user-facing preferences directly from the frontend with Tauri Store.

Internationalization

Translations use i18next and react-i18next.

Current language files live in src/lang/:

  • en.json
  • ja.json
  • ru.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.

Add A New Language

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.

Styling And UI

  • 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-i18next for 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.

Testing

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.tsx holds 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.