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
+39 -38
View File
@@ -20,13 +20,13 @@ that tree.
## Stack delta (read this first — it changes how directly we can borrow)
| | vega/editor | Astrolabe |
|---|---|---|
| UI framework | **React** | **React** (moved off Preact before build start) |
| State | Redux-ish single `State` in React context (`useState`) | Zustand **stores***not* Redux |
| Monaco | `@monaco-editor/react` + `@monaco-editor/loader` (CDN-loaded Monaco, **workers auto-wired**) | **raw `monaco-editor`** via Vite (**we must wire workers ourselves**) |
| Rendering | **hand-rolled** `vegaLite.compile``vega.parse``new vega.View().runAsync()` | **`vegaEmbed()`** (wraps that same pipeline) |
| Schema validation | Monaco JSON worker **+** standalone `ajv ^8` (two independent layers) | same two-layer model planned |
| | vega/editor | Astrolabe |
| ----------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| UI framework | **React** | **React** (moved off Preact before build start) |
| State | Redux-ish single `State` in React context (`useState`) | Zustand **stores**_not_ Redux |
| Monaco | `@monaco-editor/react` + `@monaco-editor/loader` (CDN-loaded Monaco, **workers auto-wired**) | **raw `monaco-editor`** via Vite (**we must wire workers ourselves**) |
| Rendering | **hand-rolled** `vegaLite.compile``vega.parse``new vega.View().runAsync()` | **`vegaEmbed()`** (wraps that same pipeline) |
| Schema validation | Monaco JSON worker **+** standalone `ajv ^8` (two independent layers) | same two-layer model planned |
Because both apps are now React, vega/editor's **component lifecycle patterns port more or
less directly** — the friction is only in (a) state (their Redux-flat-state → our Zustand
@@ -48,14 +48,15 @@ assets are hashed files in `dist/` that Workbox precaches automatically; (2) **p
third-party fetch on load contradicts SOUL's "the only outbound requests are user-created
URL-dataset fetches"; (3) **determinism** — npm + `package-lock` is integrity-pinned and
reproducible, a runtime CDN resolve is not. This axis is not a close call; vega/editor's CDN
choice is right *for an online hosted tool* and wrong for an offline, installable, private app.
choice is right _for an online hosted tool_ and wrong for an offline, installable, private app.
**Axis B — React integration: raw API, not `@monaco-editor/react`. (A lean, not forced.)**
The wrapper helps with the easy 80% (mount a JSON editor, lifecycle) and adds nothing to the
load-bearing 20% this app needs:
- **Workers** are still ours — the wrapper never manages `MonacoEnvironment` (see §1 gotcha).
- The **M2 schema service** (`jsonDefaults.setDiagnosticsOptions`, `fileMatch`) is namespace-level;
you reach *through* the wrapper via `onMount`, so it saves nothing there.
you reach _through_ the wrapper via `onMount`, so it saves nothing there.
- Its headline **`value`/`onChange` controlled-input model is a hazard**: driving Monaco's
content from React state causes cursor jumps and undo-stack churn, against §10's "typing
stays fluid" — you end up using it uncontrolled, i.e. the raw pattern anyway.
@@ -77,11 +78,11 @@ price of offline, paid in any non-CDN setup, and the wrapper would not remove it
Self-hosting raw Monaco forces a choice of ESM entry point, and the granularity matters:
| Import | What you get | Use? |
|---|---|---|
| `monaco-editor` (barrel) | All features **+ every basic language** (sql, abap, solidity, …) | ❌ language bloat (~20 dead chunks) |
| `esm/vs/editor/editor.api` | The API surface only — **zero feature contributions** | ❌ a text box: no folding, suggest widget, `Cmd+Backspace`, find, bracket colorization |
| `esm/vs/editor/edcore.main` | `editor.all` (all 59 feature contributions) + API, **no languages** | ✅ full editor UX, JSON-only weight |
| Import | What you get | Use? |
| --------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `monaco-editor` (barrel) | All features **+ every basic language** (sql, abap, solidity, …) | ❌ language bloat (~20 dead chunks) |
| `esm/vs/editor/editor.api` | The API surface only — **zero feature contributions** | ❌ a text box: no folding, suggest widget, `Cmd+Backspace`, find, bracket colorization |
| `esm/vs/editor/edcore.main` | `editor.all` (all 59 feature contributions) + API, **no languages** | ✅ full editor UX, JSON-only weight |
Import **`edcore.main`** and add only the JSON language service
(`esm/vs/language/json/monaco.contribution`). `edcore.main` ships no `.d.ts` of its own —
@@ -100,7 +101,7 @@ JSON strings, where Monaco disables auto-suggest by default).
> **The single biggest surprise:** vega/editor does **not** use `vega-embed` for its live
> preview. It builds the compile→parse→View pipeline by hand; `vega-embed` is imported only
> for types and the exported standalone-HTML snippet. This is *good news* — `vega-embed` is
> for types and the exported standalone-HTML snippet. This is _good news_ — `vega-embed` is
> exactly the wrapper they wrote by hand, so we get it for free. But their hand-rolled
> version (`src/components/renderer/renderer.tsx`) is the best available documentation of the
> lifecycle/cleanup discipline `vega-embed` still expects from us.
@@ -180,7 +181,7 @@ vega/editor's hand-rolled renderer (`src/components/renderer/renderer.tsx`) reve
**Gotchas / where we improve:**
- ⚠️ **Finalize before re-embed, or leak.** Every spec change must `view.finalize()` the old
view *and* clear the container before mounting the new one (`renderer.tsx:218-226`). `vegaEmbed`
view _and_ clear the container before mounting the new one (`renderer.tsx:218-226`). `vegaEmbed`
returns `{ view, finalize }` — call `finalize()` before the next embed and on unmount. This is
already a Do-rule in doc 05; vega/editor confirms how easy it is to leak otherwise.
- ⚠️ **Race on rapid edits.** `runAsync` is async; a stale render can resolve after a newer one
@@ -201,7 +202,7 @@ sorts errors into two tiers. Both are worth copying.
1. **Monaco JSON worker** → inline **squiggles, hovers, autocomplete** in the editor.
2. **`ajv ^8`** (`src/utils/validate.ts`) → runs at parse time, feeds the **error/log pane**.
It does *not* create editor markers.
It does _not_ create editor markers.
**The two error tiers (keep them separate):**
@@ -215,7 +216,7 @@ The orchestration is `app.tsx:188-291`: `parseJSONCOrThrow` → `$schema` semver
`validateVegaLite` (ajv, warn) → `vegaLite.compile` (throw=fatal) → render (`renderer.tsx`,
throw=fatal).
**ajv setup specifics that *will* bite a from-scratch impl** (`validate.ts:9-17`):
**ajv setup specifics that _will_ bite a from-scratch impl** (`validate.ts:9-17`):
- `new Ajv({ strict: false })` — the VL/Vega schemas fail ajv strict-mode at **compile** time
otherwise.
@@ -227,15 +228,15 @@ throw=fatal).
per keystroke is a perf killer.
**Where we improve:** ajv errors are shown as JSON-pointer text (e.g. `/encoding/x`) with **no
editor position** — vega/editor does not map them to markers. Only JSON *syntax* errors get a
editor position** — vega/editor does not map them to markers. Only JSON _syntax_ errors get a
line/col (via jsonc-parser's visitor, `utils/jsonc-parser.ts:3-17`). If our §03E wants inline
ajv markers, we map `instancePath` → editor offsets ourselves via jsonc-parser's node tree —
something the reference does *not* do.
something the reference does _not_ do.
## 4 · Data flow & debouncing (M1/M2 — translate to Zustand stores)
vega/editor keeps **`editorString` (the text) as the single source of truth**; the parsed spec
and compiled Vega spec are *derived* and recomputed by a subscriber when text/mode/config change
and compiled Vega spec are _derived_ and recomputed by a subscriber when text/mode/config change
(`app.tsx:338-365`). Errors don't clobber the last-good derived specs.
**The Zustand-store translation (this is the shape to build):**
@@ -251,18 +252,18 @@ text (store field, debounced writer on editor change)
- **Debounce only at edit→state**, not state→render. vega/editor debounces the editor at
**1200 ms** (`spec-editor/renderer.tsx:66`) and guards the render with a `deepEqual` prop
diff (`renderer.tsx:340-349`). (1200 ms is *their* number; tune ours — our settings expose a
diff (`renderer.tsx:340-349`). (1200 ms is _their_ number; tune ours — our settings expose a
render-debounce preference.)
- **A manual-parse escape hatch** (Ctrl/Cmd+S re-parses without waiting) maps to a future
live-vs-manual preview toggle (`renderer.tsx:89-111`).
- **`LocalLogger` pattern** (`utils/logger.ts`): a logger that buffers `errors/warns/infos/debugs`
into arrays instead of writing to console. This lets a **pure** `src/core` compile/validate
step *return* structured diagnostics with zero browser coupling — e.g.
step _return_ structured diagnostics with zero browser coupling — e.g.
`validateSpec(spec) → { errors, warns }`. Ideal core-first fit.
- **`json-stringify-pretty-compact`** for the format action and prettify-on-load — much nicer
than `JSON.stringify(…, null, 2)` for VL specs.
**Persistence note:** vega/editor snapshots its whole state to localStorage on *every* change,
**Persistence note:** vega/editor snapshots its whole state to localStorage on _every_ change,
stripping non-serializable fields (`view`, `runtime`, editor refs) and restoring via
`{ ...DEFAULT_STATE, ...parsed }` (`context/app-context.tsx`). Our **debounced auto-save to
IndexedDB** (doc 01/02) is the better pattern — but the "strip non-serializable, restore with
@@ -272,20 +273,20 @@ defaults-spread" discipline is worth keeping.
## Borrow list (where each lands)
| Technique | Lands in | Milestone |
|---|---|---|
| Bundle VL schema from package `build/`; `setDiagnosticsOptions` | `src/app/infrastructure/` Monaco setup | M2 |
| `markdownDescription` patch + compact formatter | Monaco setup | M2 |
| Explicit Vite worker wiring (`MonacoEnvironment.getWorker`) | Monaco setup | M2 |
| `fileMatch` schema binding (improvement over `$schema`-only) | Monaco setup | M2 |
| jsonc-parser tolerant parse + line/col syntax errors | `src/core/` | M1/M2 |
| ajv wrapper (`strict:false`, draft-06, color-hex, compile-once) → structured diagnostics | `src/core/` | M2 |
| `LocalLogger`-style buffered diagnostics from pure compile | `src/core/` | M2 |
| Fatal-vs-advisory two-tier error model | rendering/store contract | M1/M2 |
| `"container"` sizing + `ResizeObserver` → `view.resize()` | `rendering.ts` + LivePreview | M2 |
| `finalize()`-before-reembed + **render-generation guard** | LivePreview | M1 |
| theme = `vega-themes` config merged into `vegaEmbed` | preview + settings | M5 |
| `json-stringify-pretty-compact` format action | editor | M2 |
| Technique | Lands in | Milestone |
| ---------------------------------------------------------------------------------------- | -------------------------------------- | --------- |
| Bundle VL schema from package `build/`; `setDiagnosticsOptions` | `src/app/infrastructure/` Monaco setup | M2 |
| `markdownDescription` patch + compact formatter | Monaco setup | M2 |
| Explicit Vite worker wiring (`MonacoEnvironment.getWorker`) | Monaco setup | M2 |
| `fileMatch` schema binding (improvement over `$schema`-only) | Monaco setup | M2 |
| jsonc-parser tolerant parse + line/col syntax errors | `src/core/` | M1/M2 |
| ajv wrapper (`strict:false`, draft-06, color-hex, compile-once) → structured diagnostics | `src/core/` | M2 |
| `LocalLogger`-style buffered diagnostics from pure compile | `src/core/` | M2 |
| Fatal-vs-advisory two-tier error model | rendering/store contract | M1/M2 |
| `"container"` sizing + `ResizeObserver` → `view.resize()` | `rendering.ts` + LivePreview | M2 |
| `finalize()`-before-reembed + **render-generation guard** | LivePreview | M1 |
| theme = `vega-themes` config merged into `vegaEmbed` | preview + settings | M5 |
| `json-stringify-pretty-compact` format action | editor | M2 |
## Where we deliberately do better than the reference