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:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user