Format entire codebase with Prettier (mechanical, no behavior change)

This commit is contained in:
2026-06-05 01:43:28 +03:00
parent 939950b136
commit 0c7297624e
32 changed files with 1597 additions and 832 deletions
+29 -18
View File
@@ -15,7 +15,7 @@ not components" architecture, and it carries no build-time magic. The principles
are the durable part — they would survive a change of library.
**Lineage (why React + Zustand).** The UI began on Preact + `@preact/signals` and migrated to
**React + Zustand** at M0, before feature work. The driver was *React-ecosystem friction*
**React + Zustand** at M0, before feature work. The driver was _React-ecosystem friction_
real-React-only libraries not cooperating with `preact/compat`**not** the signals model.
Switching framework while nothing was implemented yet was also the one cheap moment to pick
the lowest-migration-risk state library, so signals gave way to Zustand. A bonus: borrowing
@@ -58,7 +58,7 @@ Three ways to touch a store:
- **`set(partial)`** — update state (shallow-merges). Inside actions, the only place
that mutates state.
- **`get()`** — read current state inside actions without subscribing.
- **the hook `useAppStore(selector)`** — read state *in a React component*, subscribing
- **the hook `useAppStore(selector)`** — read state _in a React component_, subscribing
to exactly what the selector returns.
### Reading in components — always select narrowly
@@ -93,9 +93,11 @@ Services, orchestration, infrastructure, and tests use the store object directly
no React involved. This is the property that lets our logic live outside components:
```ts
openModal('settings'); // via the modal coordinator (doc 03)
const theme = useAppStore.getState().uiTheme; // snapshot read
const unsub = useAppStore.subscribe((s, prev) => { /* react to changes */ });
openModal('settings'); // via the modal coordinator (doc 03)
const theme = useAppStore.getState().uiTheme; // snapshot read
const unsub = useAppStore.subscribe((s, prev) => {
/* react to changes */
});
```
> Rule: in components, **select narrowly** (and `useShallow` for object/array
@@ -106,7 +108,7 @@ const unsub = useAppStore.subscribe((s, prev) => { /* react to changes */ });
## 2. One Source of Truth per Fact — Derive, Don't Duplicate
Every fact lives in exactly one state field. Anything that can be *calculated*
Every fact lives in exactly one state field. Anything that can be _calculated_
from other state is computed **in a selector at read time**, never stored as a
second field you keep in sync by hand.
@@ -121,8 +123,8 @@ read, drift is structurally impossible.
// activeSnippetId: string | null
// Derive in the component's selector — not a stored field:
const activeSnippet = useSnippetStore((s) =>
s.snippets.find((x) => x.id === s.activeSnippetId) ?? null,
const activeSnippet = useSnippetStore(
(s) => s.snippets.find((x) => x.id === s.activeSnippetId) ?? null,
);
const snippetCount = useSnippetStore((s) => s.snippets.length);
```
@@ -141,13 +143,13 @@ const active = useSnippetStore(selectActiveSnippet);
```
> Rule: if you can compute it, do not store it. Add a new state field only for a
> value that is *input* the app receives, not output it derives.
> value that is _input_ the app receives, not output it derives.
---
## 3. Where State Lives: Central vs. Per-Feature Stores
Each store is its own `create()` module. We split by *concern*, not by component
Each store is its own `create()` module. We split by _concern_, not by component
tree.
### Per-feature stores
@@ -162,16 +164,16 @@ Each cohesive feature owns a store holding its durable domain state.
### The central `useAppStore`
`useAppStore` holds only *cross-cutting, ephemeral UI state* that no single
`useAppStore` holds only _cross-cutting, ephemeral UI state_ that no single
feature owns — which modal is open, the runtime theme, transient render flags.
### How to decide
| Put it in a **feature store** when… | Put it in **`useAppStore`** when… |
| --------------------------------------------- | --------------------------------------------- |
| It's domain data (snippets, datasets, specs) | It's transient UI chrome (open modal, theme) |
| It outlives a single interaction | It belongs to no single feature |
| It gets persisted | Multiple unrelated features read/write it |
| Put it in a **feature store** when… | Put it in **`useAppStore`** when… |
| -------------------------------------------- | -------------------------------------------- |
| It's domain data (snippets, datasets, specs) | It's transient UI chrome (open modal, theme) |
| It outlives a single interaction | It belongs to no single feature |
| It gets persisted | Multiple unrelated features read/write it |
> Rule: keep `useAppStore` small. When a chunk of it only ever serves one feature,
> that's the signal to extract a feature store. A bloated central store is the
@@ -253,7 +255,14 @@ export function SnippetList() {
{snippets.map((s) => (
<li key={s.id} aria-current={s.id === activeSnippetId} onClick={() => select(s.id)}>
{s.name}
<button onClick={(e) => { e.stopPropagation(); remove(s.id); }}></button>
<button
onClick={(e) => {
e.stopPropagation();
remove(s.id);
}}
>
</button>
</li>
))}
</ul>
@@ -310,7 +319,9 @@ Subscribers read state and write to `src/app/infrastructure/` adapters (IndexedD
```ts
// src/main.tsx
const applyTheme = (t: string) => { document.documentElement.dataset.theme = t; };
const applyTheme = (t: string) => {
document.documentElement.dataset.theme = t;
};
applyTheme(useAppStore.getState().uiTheme);
useAppStore.subscribe((s, prev) => {
if (s.uiTheme !== prev.uiTheme) applyTheme(s.uiTheme);