wave 1 (platform stream): background-observability accuracy + owner-driven follow-ups

- observability: liveness-gated stale-job recovery, extraction run registry on globalThis with
  explicit 'embedding' phase, /api/jobs force-dynamic, client refresh nudges at every seam
- headless: providerSafePng canonicalizer (multi-IDAT Chrome PNGs → single-IDAT) fixing qwen 400s
- composer: Enter routes through pure enterAction — accepts a pending plan verdict (owner decision)
- markdown chrome: session-scoped mermaid overlay + md-blocks component set + pan-zoom math,
  corner-copy (clipboard grant in electron main), math/table/code block fixes, heading weight 900

543/543 tests; typecheck/lint/build clean
This commit is contained in:
amosblomqvist
2026-09-19 13:16:34 +03:00
parent cbecb8df03
commit 487f5d4cea
27 changed files with 1754 additions and 111 deletions
+55
View File
@@ -0,0 +1,55 @@
/**
* Shared pan/zoom viewport math — the "fit floor" semantics harvested from
* the plan popup (plan-graph rounds 2–4) so the session overlays (the plan
* focus, the mermaid focus) run the SAME geometry: fully zoomed out IS the
* fit (whichever axis binds), panning locks at the floor (nothing to move),
* wheel zooms to the cursor. Pure on purpose: the overlays are render-tested
* in static markup (no live pointer events), so the math lives here where
* tests can pin it (tests/pan-zoom.test.ts).
*/
/** Zoom ceiling for every surface that uses this math (raised 2×→4× for
* reading node titles closely, owner, plan-graph round 3). */
export const ZOOM_MAX = 4;
/** Viewport breathing room subtracted by the fit. */
export const FIT_PAD = 16;
/** The fit never zooms IN past this — a tiny diagram must not blow up. */
export const FIT_ZOOM_MAX = 1.5;
/** A pan/zoom viewport state (translate + scale; origin top-left). */
export interface Viewport {
zoom: number;
x: number;
y: number;
}
/** The fit zoom for a box in a viewport: never below a true fit (the old
* 0.15 floor could sit ABOVE the fit for wide graphs — the epsilon only
* guards degenerate viewports), never above FIT_ZOOM_MAX. Pure. */
export function fitZoomFor(box: { w: number; h: number }, viewport: { w: number; h: number }): number {
if (!viewport.w || !viewport.h || box.w === 0) return 1;
return Math.max(0.02, Math.min((viewport.w - FIT_PAD) / box.w, (viewport.h - FIT_PAD) / box.h, FIT_ZOOM_MAX));
}
/** Centers a box (at the given zoom) in the viewport. Pure. */
export function fitGraphView(box: { w: number; h: number }, viewport: { w: number; h: number }, zoom: number): Viewport {
return { zoom, x: (viewport.w - box.w * zoom) / 2, y: (viewport.h - box.h * zoom) / 2 };
}
/** Zoom by `factor` keeping `focus` (viewport coordinates) visually fixed,
* clamped to [floor, ZOOM_MAX]. At the floor the view re-fits + RE-CENTERS
* (the fit IS the floor — a locked view there has nothing to move). Pure. */
export function zoomedGraphView(
view: Viewport,
factor: number,
floor: number,
focus: { x: number; y: number },
box: { w: number; h: number },
viewport: { w: number; h: number },
): Viewport {
const zoom = Math.min(Math.max(view.zoom * factor, floor), ZOOM_MAX);
if (zoom === view.zoom) return view;
if (zoom <= floor) return fitGraphView(box, viewport, floor);
const k = zoom / view.zoom;
return { zoom, x: focus.x - (focus.x - view.x) * k, y: focus.y - (focus.y - view.y) * k };
}