feat(editor): F4.3 the editor's frame and surface; a switch's cloud read and written

packages/editor (React 18, Vite) draws a document as Java FILIUS does: Java's
default hardware icons, the device button of JSidebarButton, cables as
JCablePanel's quadratic curve between the buttons' middles, wireless cables
dashed, documentation items below. The surface moves with mouse and finger
(the pointer captured only once it moved) and zooms with Ctrl and the
wheel, a trackpad pinch, two fingers and the zoom bar; the first view shows
all. German and English catalogs with Java's device names, the dashboard's
colour tokens, the bridge with its origin check. packages/doc gets the
drawing's light view of a document.

Java's Switch.cloud (the Internet in the examples) was lost on reading; it
is now read, kept in the model and written, with FlsOracle and the corpus,
written and exported checks renewed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 09:05:25 +00:00
co-authored by Claude Opus 5.5
parent 0fd42f10e2
commit 63273916b5
49 changed files with 2442 additions and 8 deletions
@@ -0,0 +1,50 @@
// The bridge to the dashboard (plan §9; the pattern of Scratch's
// lib/bridge.js and its twin dashboard-bridge.js): the editor runs in an
// iframe on another host, and between the two there is only postMessage.
// This file holds names, a version and the origin check, nothing that could
// drift apart from its twin in the dashboard (lib/bridge.js, F6). Every
// message is checked against the expected origin, both ways.
export const BRIDGE_VERSION = 1;
/** Dashboard → editor. */
export const COMMANDS = {
/** `{ locale: 'de' | 'en' }` */
SET_LOCALE: 'setLocale',
/** `{ theme: 'light' | 'dark', tokens?: { [name]: value } }`: the dashboard's resolved colours. */
SET_THEME: 'setTheme',
} as const;
/** Editor → dashboard. */
export const EVENTS = {
/** The editor listens. */
READY: 'ready',
} as const;
export interface BridgeHandlers {
readonly onLocale: (locale: string) => void;
readonly onTheme: (theme: 'light' | 'dark', tokens: Readonly<Record<string, string>>) => void;
}
interface Window {
readonly parent: { postMessage(message: unknown, targetOrigin: string): void };
addEventListener(type: 'message', listener: (event: { readonly origin: string; readonly data: unknown }) => void): void;
removeEventListener(type: 'message', listener: (event: { readonly origin: string; readonly data: unknown }) => void): void;
}
/** Listens to the dashboard at `parentOrigin` and tells it the editor is ready; returns how to stop. */
export function startBridge(window: Window, parentOrigin: string, handlers: BridgeHandlers): () => void {
const listener = (event: { readonly origin: string; readonly data: unknown }) => {
if (event.origin !== parentOrigin) return;
const data = event.data as { v?: unknown; type?: unknown; locale?: unknown; theme?: unknown; tokens?: unknown } | null;
if (data === null || typeof data !== 'object' || data.v !== BRIDGE_VERSION) return;
if (data.type === COMMANDS.SET_LOCALE && typeof data.locale === 'string') handlers.onLocale(data.locale);
if (data.type === COMMANDS.SET_THEME && (data.theme === 'light' || data.theme === 'dark')) {
const tokens = data.tokens !== null && typeof data.tokens === 'object' ? (data.tokens as Record<string, unknown>) : {};
handlers.onTheme(data.theme, Object.fromEntries(Object.entries(tokens).filter((entry): entry is [string, string] => entry[0].startsWith('--') && typeof entry[1] === 'string')));
}
};
window.addEventListener('message', listener);
window.parent.postMessage({ v: BRIDGE_VERSION, type: EVENTS.READY }, parentOrigin);
return () => window.removeEventListener('message', listener);
}