mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Format entire codebase with Prettier (mechanical, no behavior change)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user