@wildwinter/app-shell 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -0
- package/dist/chunk-4JQWNC6Z.js +319 -0
- package/dist/chunk-4JQWNC6Z.js.map +1 -0
- package/dist/chunk-ICYPXUNA.js +8 -0
- package/dist/chunk-ICYPXUNA.js.map +1 -0
- package/dist/{chunk-CPNTROWD.js → chunk-OZQ2BYXH.js} +13 -4
- package/dist/chunk-OZQ2BYXH.js.map +1 -0
- package/dist/index.cjs +330 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tool-window.cjs +11 -2
- package/dist/tool-window.cjs.map +1 -1
- package/dist/tool-window.d.cts +10 -4
- package/dist/tool-window.d.ts +10 -4
- package/dist/tool-window.js +1 -1
- package/dist/updater.cjs.map +1 -1
- package/dist/updater.js +1 -1
- package/dist/view-client.cjs +172 -0
- package/dist/view-client.cjs.map +1 -0
- package/dist/view-client.d.cts +46 -0
- package/dist/view-client.d.ts +46 -0
- package/dist/view-client.js +165 -0
- package/dist/view-client.js.map +1 -0
- package/dist/view-host-web.cjs +327 -0
- package/dist/view-host-web.cjs.map +1 -0
- package/dist/view-host-web.d.cts +61 -0
- package/dist/view-host-web.d.ts +61 -0
- package/dist/view-host-web.js +4 -0
- package/dist/view-host-web.js.map +1 -0
- package/dist/view-host.cjs +532 -0
- package/dist/view-host.cjs.map +1 -0
- package/dist/view-host.d.cts +178 -0
- package/dist/view-host.d.ts +178 -0
- package/dist/view-host.js +426 -0
- package/dist/view-host.js.map +1 -0
- package/dist/view-preload.cjs +16 -0
- package/dist/view-preload.cjs.map +1 -0
- package/dist/view-preload.js +8 -0
- package/dist/view-preload.js.map +1 -0
- package/dist/view-protocol.cjs +12 -0
- package/dist/view-protocol.cjs.map +1 -0
- package/dist/view-protocol.d.cts +257 -0
- package/dist/view-protocol.d.ts +257 -0
- package/dist/view-protocol.js +3 -0
- package/dist/view-protocol.js.map +1 -0
- package/package.json +25 -4
- package/dist/chunk-CPNTROWD.js.map +0 -1
|
@@ -124,6 +124,9 @@ function openToolWindow(existing, opts) {
|
|
|
124
124
|
existing.focus();
|
|
125
125
|
return existing;
|
|
126
126
|
}
|
|
127
|
+
if (opts.url === void 0 && (opts.page === void 0 || opts.rendererDir === void 0)) {
|
|
128
|
+
throw new Error("openToolWindow needs a url, or a page and its rendererDir");
|
|
129
|
+
}
|
|
127
130
|
const w = new BrowserWindow({
|
|
128
131
|
...opts.rect,
|
|
129
132
|
...opts.min !== void 0 ? { minWidth: opts.min.width, minHeight: opts.min.height } : {},
|
|
@@ -134,14 +137,20 @@ function openToolWindow(existing, opts) {
|
|
|
134
137
|
contextIsolation: true,
|
|
135
138
|
nodeIntegration: false,
|
|
136
139
|
sandbox: true,
|
|
137
|
-
|
|
140
|
+
// Electron's defaults, said out loud: no webview tag, and a dropped file never navigates the window.
|
|
141
|
+
webviewTag: false,
|
|
142
|
+
navigateOnDragDrop: false,
|
|
143
|
+
preload: opts.preload,
|
|
144
|
+
...opts.partition !== void 0 ? { partition: opts.partition } : {},
|
|
145
|
+
...opts.devTools !== void 0 ? { devTools: opts.devTools } : {}
|
|
138
146
|
}
|
|
139
147
|
});
|
|
140
148
|
if (opts.pinTo !== void 0) pinToolWindow(w, opts.pinTo(), opts.pinned ?? false);
|
|
141
149
|
if (opts.remember) rememberBounds(w, opts.remember);
|
|
142
150
|
w.once("ready-to-show", () => w.show());
|
|
143
151
|
if (opts.onClosed) w.on("closed", opts.onClosed);
|
|
144
|
-
if (
|
|
152
|
+
if (opts.url !== void 0) void w.loadURL(opts.url);
|
|
153
|
+
else if (process.env["ELECTRON_RENDERER_URL"]) void w.loadURL(`${process.env["ELECTRON_RENDERER_URL"]}/${opts.page}`);
|
|
145
154
|
else void w.loadFile(join(opts.rendererDir, opts.page));
|
|
146
155
|
return w;
|
|
147
156
|
}
|
|
@@ -209,5 +218,5 @@ function defineToolWindows(specs, env) {
|
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
export { centeredOnPrimary, defineToolWindows, frontForModal, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
|
212
|
-
//# sourceMappingURL=chunk-
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
221
|
+
//# sourceMappingURL=chunk-OZQ2BYXH.js.map
|
|
222
|
+
//# sourceMappingURL=chunk-OZQ2BYXH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tool-window.ts"],"names":[],"mappings":"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","file":"chunk-OZQ2BYXH.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The tool-window kit, MAIN-PROCESS half: the detached helper-window\n// machinery both apps had grown four copies of (Patterpad's Play + Search +\n// Coverage, Storyletter's Board + Find). Remembered bounds with a\n// disconnected-monitor guard, debounced persistence, the Reset View rescue,\n// and the pinnable window factory they compose into.\n//\n// This module imports electron, so it ships as its own subpath\n// (@wildwinter/app-shell/tool-window) - never import it from renderer code.\n// The renderer half (pin button, .swin drag-bar grammar) lives in the root\n// export + tool-window.css.\n//\n// Two deliberate corrections over the Patterpad originals, for when it\n// migrates here: rescueToolWindow re-pins EVERY window it rescues (Patterpad\n// re-pinned only Play, so its store lied about Search + Coverage), and the\n// factory always wires closed-cleanup through an identity guard.\n// ---------------------------------------------------------------------------\n\nimport { BrowserWindow, app, screen } from \"electron\";\nimport { join } from \"node:path\";\nimport type { Satellite } from \"./session.js\";\n\nexport interface ToolWindowSize {\n width: number;\n height: number;\n}\n\nexport interface ToolWindowBounds {\n x?: number;\n y?: number;\n width: number;\n height: number;\n}\n\n/** The per-window store slice every app remembers: where it was, whether it\n * floats. (Patterpad's PlayWindowState, generalised.) */\nexport interface ToolWindowState {\n bounds?: ToolWindowBounds;\n pinned: boolean;\n}\n\n/** Use a remembered rect only if it still intersects a live display (a window\n * saved on a now-disconnected monitor must not open offscreen). Falls back\n * to the default size; a remembered size is kept even when the position is\n * dropped (Electron then centres it). */\nexport function savedWindowRect(\n saved: ToolWindowBounds | undefined,\n def: ToolWindowSize,\n min: ToolWindowSize,\n): ToolWindowBounds {\n if (!saved) return { ...def };\n const w = Math.max(min.width, saved.width), h = Math.max(min.height, saved.height);\n if (saved.x != null && saved.y != null) {\n const onScreen = screen.getAllDisplays().some((d) => {\n const a = d.workArea;\n return saved.x! + w > a.x + 40 && saved.x! < a.x + a.width - 40 && saved.y! + h > a.y + 20 && saved.y! < a.y + a.height - 20;\n });\n if (onScreen) return { x: saved.x, y: saved.y, width: w, height: h };\n }\n return { width: w, height: h };\n}\n\n/** Persist a helper window's bounds as the user moves / resizes / closes it\n * (debounced 400ms; close flushes synchronously). */\nexport function rememberBounds(w: BrowserWindow, write: (bounds: ToolWindowBounds) => void): void {\n const saveBounds = (): void => { if (!w.isDestroyed()) write(w.getBounds()); };\n let boundsTimer: ReturnType<typeof setTimeout> | undefined;\n const queueSave = (): void => { clearTimeout(boundsTimer); boundsTimer = setTimeout(saveBounds, 400); };\n w.on(\"resize\", queueSave);\n w.on(\"move\", queueSave);\n w.on(\"close\", () => { clearTimeout(boundsTimer); saveBounds(); });\n}\n\n/** Centre a rect on the primary display's work area. */\nexport function centeredOnPrimary(size: ToolWindowSize): { x: number; y: number } {\n const a = screen.getPrimaryDisplay().workArea;\n return { x: Math.round(a.x + (a.width - size.width) / 2), y: Math.round(a.y + (a.height - size.height) / 2) };\n}\n\n/** Reset View's per-window half: un-minimise, re-pin (always - the store\n * says pinned:true after a rescue, so the window must agree), default size,\n * centred on the primary display, shown. The host clears its remembered\n * bounds itself (that's a store concern). No focus steal: helpers show\n * behind the editor. */\nexport function rescueToolWindow(w: BrowserWindow | undefined | null, def: ToolWindowSize): void {\n if (!w || w.isDestroyed()) return;\n if (w.isMinimized()) w.restore();\n w.setBounds({ ...def, ...centeredOnPrimary(def) });\n w.show();\n // Raise within OUR stack. This used to setAlwaysOnTop(true), which on macOS\n // and Windows floats the window above every other APPLICATION - and stuck\n // that way, since nothing ever unset it.\n w.moveTop();\n}\n\n/** macOS pinned windows, for the activation toggle below. */\nconst macPinned = new Set<BrowserWindow>();\nlet macActivationWired = false;\nfunction wireMacActivation(): void {\n if (macActivationWired) return;\n macActivationWired = true;\n // Floating is a GLOBAL window level: left on while another app is front,\n // the pinned window would sit over that app too - the exact 2026-08-25\n // complaint. Scoping it to our app's active state keeps the pin meaning\n // \"above my editor\" and nothing more.\n // Not while a modal has the pins held (frontForModal): the editor is in\n // front for it, and coming back to the app must not cover it again.\n app.on(\"did-become-active\", () => { if (pinsHeld > 0) return; for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, \"floating\"); });\n app.on(\"did-resign-active\", () => { for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false); });\n}\n\n/**\n * The pin: keep a tool window above THIS APP'S MAIN WINDOW, not above the\n * world. Two findings shaped the mechanism, one per platform family:\n *\n * - Bare `alwaysOnTop` (the first implementation) floats the window over\n * every other application on macOS and Windows, so a pinned Board meant\n * nothing else on the machine could come to the front (a Storyletter user\n * report, 2026-08-25).\n * - A CHILD window (the second implementation) is position-coupled to its\n * parent on macOS: dragging the editor dragged the Board with it (a\n * Storyletter user report, 2026-08-28). Windows and Linux owned windows do\n * not move with their owner, so the child mechanism stays right for them.\n *\n * So: on macOS the pin is ACTIVATION-SCOPED alwaysOnTop - floating while\n * this app is active, dropped the moment it resigns - which is above the\n * editor without following its drags and without covering anyone else. On\n * Windows and Linux the pin is a child window, which stays over its parent\n * and stacks normally against everything else. Each branch clears the other\n * mechanism first, which also heals a window an older build pinned the old\n * way. `platform` is a seam for the tests; leave it defaulted.\n */\nexport function pinToolWindow(\n w: BrowserWindow | undefined | null, parent: BrowserWindow | undefined | null, on: boolean,\n platform: NodeJS.Platform = process.platform,\n): void {\n if (!w || w.isDestroyed()) return;\n if (platform === \"darwin\") {\n w.setParentWindow(null);\n if (on) {\n if (!macPinned.has(w)) {\n macPinned.add(w);\n w.once(\"closed\", () => macPinned.delete(w));\n }\n wireMacActivation();\n // Raise only when the app is active now and no modal has the pins\n // held; otherwise the next did-become-active, or the release, raises it.\n w.setAlwaysOnTop(pinsHeld === 0 && BrowserWindow.getFocusedWindow() !== null, \"floating\");\n } else {\n macPinned.delete(w);\n w.setAlwaysOnTop(false);\n }\n return;\n }\n w.setAlwaysOnTop(false);\n const owner = on && parent && !parent.isDestroyed() ? parent : null;\n // While a modal has the pins held, remember the choice and apply it at the\n // release, so the editor stays in front until the modal closes.\n if (pinsHeld > 0) {\n if (owner) heldChildren.set(w, owner); else heldChildren.delete(w);\n w.setParentWindow(null);\n return;\n }\n w.setParentWindow(owner);\n}\n\n// ---------------------------------------------------------------------------\n// Fronting the main window for a modal. A pinned tool window rides above the\n// editor by design, so a modal the EDITOR shows (the updater's prompts) opened\n// underneath one: the editor blurred behind its scrim and the prompt was\n// hidden, with nothing on screen to say why (both apps, 2026-10-02, the Play\n// window over the editor). While such a modal is up the pins step aside and\n// the editor comes forward; when it closes, every pin goes back as it was.\n// ---------------------------------------------------------------------------\n\n/** How many modals are holding the pins down. The pins return at zero. */\nlet pinsHeld = 0;\n/** Windows and Linux: the tool windows unparented by the hold, and the window\n * each goes back to. */\nconst heldChildren = new Map<BrowserWindow, BrowserWindow>();\n\n/**\n * Bring `main` in front of every tool window, pinned ones included, for as\n * long as a modal it shows is open: un-minimise, show, focus, with the pins\n * set aside. Returns the release, which puts the pins back (idempotent; holds\n * nest, and the pins return when the last is released). Call it only while\n * the app is active: focusing a window of an inactive app is the focus steal\n * the platforms forbid. `platform` is a seam for the tests.\n */\nexport function frontForModal(main: BrowserWindow, platform: NodeJS.Platform = process.platform): () => void {\n if (main.isDestroyed()) return () => {};\n pinsHeld++;\n if (platform === \"darwin\") {\n for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false);\n } else {\n for (const w of BrowserWindow.getAllWindows()) {\n if (w === main || w.isDestroyed()) continue;\n const owner = w.getParentWindow();\n if (owner !== main) continue;\n heldChildren.set(w, main);\n w.setParentWindow(null);\n }\n }\n if (main.isMinimized()) main.restore();\n main.show();\n main.focus();\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--pinsHeld > 0) return;\n if (platform === \"darwin\") {\n // Only float again if the app is active; otherwise did-become-active does.\n const active = BrowserWindow.getFocusedWindow() !== null;\n if (active) for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, \"floating\");\n } else {\n for (const [w, owner] of heldChildren) {\n if (!w.isDestroyed() && !owner.isDestroyed()) w.setParentWindow(owner);\n }\n heldChildren.clear();\n }\n };\n}\n\nexport interface ToolWindowOptions {\n title: string;\n /** Renderer entry, relative to rendererDir (e.g. \"table.html\", \"search/index.html\"). Not used with `url`. */\n page?: string;\n /** Absolute path of the built renderer dir (join(import.meta.dirname, \"../renderer\")). Not used with `url`. */\n rendererDir?: string;\n /** Load this URL instead of a page from the renderer dir (a view window's `patter-view://...`). */\n url?: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n /** A session partition of the window's own (a view's in-memory `view-<id>`); the default session otherwise. */\n partition?: string;\n /** Whether the window may open devtools; Electron's default (yes) when absent. */\n devTools?: boolean;\n rect: ToolWindowBounds;\n min?: ToolWindowSize;\n /** false = frameless: the renderer draws its own slim drag bar (.swin-head). */\n frame?: boolean;\n /** The window to PIN ABOVE: the app's main window, resolved at open time (a\n * getter, because the main window can be recreated). With `pinned` true\n * the new tool window rides above it - on macOS via activation-scoped\n * alwaysOnTop (a child window there follows the editor's drags), on\n * Windows and Linux as a child window (bare alwaysOnTop there floats over\n * the whole machine). See pinToolWindow for both findings. */\n pinTo?: () => BrowserWindow | undefined;\n /** Start pinned (the host remembers); flip later with `pinToolWindow`. */\n pinned?: boolean;\n /** Wire debounced bounds persistence into the host's store slice. */\n remember?: (bounds: ToolWindowBounds) => void;\n /** Fired when THIS window closes (identity-guarded; a stale close never\n * clears a newer window). Null the host's handle here. */\n onClosed?: () => void;\n}\n\n/** The pinnable detached-window factory (hardened webPreferences, one\n * preload, electron-vite's dev-URL/file split). Focuses and returns the\n * existing window when there is one; otherwise creates, wires, and loads. */\nexport function openToolWindow(existing: BrowserWindow | undefined | null, opts: ToolWindowOptions): BrowserWindow {\n if (existing && !existing.isDestroyed()) { existing.focus(); return existing; }\n if (opts.url === undefined && (opts.page === undefined || opts.rendererDir === undefined)) {\n throw new Error(\"openToolWindow needs a url, or a page and its rendererDir\");\n }\n const w = new BrowserWindow({\n ...opts.rect,\n ...(opts.min !== undefined ? { minWidth: opts.min.width, minHeight: opts.min.height } : {}),\n show: false,\n title: opts.title,\n ...(opts.frame !== undefined ? { frame: opts.frame } : {}),\n webPreferences: {\n contextIsolation: true, nodeIntegration: false, sandbox: true,\n // Electron's defaults, said out loud: no webview tag, and a dropped file never navigates the window.\n webviewTag: false, navigateOnDragDrop: false,\n preload: opts.preload,\n ...(opts.partition !== undefined ? { partition: opts.partition } : {}),\n ...(opts.devTools !== undefined ? { devTools: opts.devTools } : {}),\n },\n });\n if (opts.pinTo !== undefined) pinToolWindow(w, opts.pinTo(), opts.pinned ?? false);\n if (opts.remember) rememberBounds(w, opts.remember);\n w.once(\"ready-to-show\", () => w.show());\n if (opts.onClosed) w.on(\"closed\", opts.onClosed);\n if (opts.url !== undefined) void w.loadURL(opts.url);\n else if (process.env[\"ELECTRON_RENDERER_URL\"]) void w.loadURL(`${process.env[\"ELECTRON_RENDERER_URL\"]}/${opts.page}`);\n else void w.loadFile(join(opts.rendererDir!, opts.page!));\n return w;\n}\n\n// ---------------------------------------------------------------------------\n// The tool windows AS DATA: one row per window, and everything that walks the\n// windows walks the rows. Lifted from Storyletter's TOOL_WINDOWS table +\n// openToolWindowFor + rescueWindows (studio/src/main/index.ts), where it had\n// replaced four near-identical openX() functions whose differences were all\n// mistakes (only one forwarded its console; Reset View rescued two of four).\n// Patterpad's three hand copies (createPlayWindow / createSearchWindow /\n// createCoverageWindow and a four-branch rescueWindows) become three rows.\n//\n// Two things the table makes structurally impossible: forgetting to register\n// a window as a satellite for the project-changed nudge (every row is\n// registered when a session is given), and Reset View rescuing a subset (it\n// walks the rows).\n// ---------------------------------------------------------------------------\n\n\nexport interface ToolWindowSpec<N extends string = string> {\n /** The window's key: the store slice it remembers under, the dev-console\n * prefix, and what `open(name)` takes. */\n name: N;\n title: string;\n /** Renderer entry, relative to the env's rendererDir. */\n page: string;\n def: ToolWindowSize;\n min: ToolWindowSize;\n /** The remembered rect, read at open time (a store read-through). */\n bounds: () => ToolWindowBounds | undefined;\n /** Persist a moved / resized rect (debounced by the factory). */\n remember: (bounds: ToolWindowBounds) => void;\n /** Whether it starts pinned above the editor (both apps default true). */\n pinned: () => boolean;\n /** Frameless (the family's shape: the renderer draws `toolWindowHead`).\n * Default false = frameless; pass `frame: true` for an OS title bar. */\n frame?: boolean;\n /** The project-changed channel for THIS window when it differs from the\n * env's (Storyletter's Links window listens on \"links:focus\"). */\n channel?: string;\n /** Drop what main caches for the outgoing project on this window's behalf\n * (a coverage report, a search anchor). Runs whether the window is open\n * or not: see session.ts. */\n clear?: () => void;\n}\n\nexport interface ToolWindowEnv {\n /** Absolute path of the built renderer dir. */\n rendererDir: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n /** The window to pin above: the app's main window, resolved at open time. */\n pinTo: () => BrowserWindow | undefined;\n /** Register every row as a satellite of the project session, so a new\n * project cannot open underneath a tool window that still shows the old\n * one. `channel` is the nudge each window listens on (a row may override). */\n session?: { addSatellite: (satellite: Satellite) => () => void; channel: string };\n /** Reset View's store half, run first: forget every remembered rect and\n * set pinned true (`resetWindows(store)` from app-store, or the app's own). */\n resetStore?: () => void;\n /** The channel a rescued window's renderer hears its re-pin on, so its pin\n * button agrees with the store (default \"state:pinned\"). */\n pinChannel?: string;\n /** Runs once per window created: forward its console in dev, say. */\n onOpened?: (w: BrowserWindow, name: string) => void;\n}\n\nexport interface ToolWindows<N extends string> {\n /** Open (or focus) one window by name. */\n open(name: N): BrowserWindow;\n /** The live window, if open. */\n get(name: N): BrowserWindow | undefined;\n /** Reset View's window half, over the table: store reset, then every open\n * window restored, default size, centred, re-pinned, and told so. */\n rescue(): void;\n /** Every window that is open right now. */\n all(): BrowserWindow[];\n readonly names: readonly N[];\n}\n\n/** Build the table. The windows are owned here: a host reads them through\n * `get(name)` rather than holding module variables of its own. */\nexport function defineToolWindows<N extends string>(specs: ToolWindowSpec<N>[], env: ToolWindowEnv): ToolWindows<N> {\n const live = new Map<N, BrowserWindow>();\n const byName = (name: N): ToolWindowSpec<N> => {\n const found = specs.find((s) => s.name === name);\n if (!found) throw new Error(`no tool window \"${name}\"`);\n return found;\n };\n const get = (name: N): BrowserWindow | undefined => {\n const w = live.get(name);\n return w && !w.isDestroyed() ? w : undefined;\n };\n if (env.session) {\n for (const spec of specs) {\n env.session.addSatellite({\n window: () => get(spec.name),\n channel: spec.channel ?? env.session.channel,\n ...(spec.clear ? { clear: spec.clear } : {}),\n });\n }\n }\n const open = (name: N): BrowserWindow => {\n const spec = byName(name);\n const existing = get(name);\n const opened = openToolWindow(existing, {\n title: spec.title, page: spec.page, frame: spec.frame ?? false,\n rendererDir: env.rendererDir, preload: env.preload,\n rect: savedWindowRect(spec.bounds(), spec.def, spec.min),\n min: spec.min,\n pinTo: env.pinTo,\n pinned: spec.pinned(),\n remember: spec.remember,\n });\n if (opened !== existing) {\n live.set(name, opened);\n // Identity-guarded: a stale close never clears a newer window.\n opened.on(\"closed\", () => { if (live.get(name) === opened) live.delete(name); });\n env.onOpened?.(opened, name);\n }\n return opened;\n };\n const rescue = (): void => {\n env.resetStore?.();\n for (const spec of specs) rescueToolWindow(get(spec.name), spec.def);\n // ACTUALLY pin them, then tell them. `rescueToolWindow` restores, resizes,\n // centres and raises but never pins; the store now says pinned, so the\n // window and its button have to agree before the claim is made.\n for (const spec of specs) {\n const w = get(spec.name);\n if (!w) continue;\n pinToolWindow(w, env.pinTo(), true);\n w.webContents.send(env.pinChannel ?? \"state:pinned\", true);\n }\n };\n return {\n open, get, rescue,\n all: () => specs.map((s) => get(s.name)).filter((w): w is BrowserWindow => w !== undefined),\n names: specs.map((s) => s.name),\n };\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1262,10 +1262,10 @@ function mountSettingsDialog(opts) {
|
|
|
1262
1262
|
for (const s of opts.sections) {
|
|
1263
1263
|
const found = handles.get(s.id)?.firstInvalid?.();
|
|
1264
1264
|
if (!found) continue;
|
|
1265
|
-
const
|
|
1266
|
-
const message = (found instanceof HTMLElement ? void 0 : found.message) ??
|
|
1265
|
+
const bad2 = found instanceof HTMLElement ? found : found.el;
|
|
1266
|
+
const message = (found instanceof HTMLElement ? void 0 : found.message) ?? bad2.dataset["tip"];
|
|
1267
1267
|
showTab(s.id);
|
|
1268
|
-
|
|
1268
|
+
bad2.focus();
|
|
1269
1269
|
error.textContent = message || "Fix the highlighted fields first.";
|
|
1270
1270
|
error.hidden = false;
|
|
1271
1271
|
return;
|
|
@@ -3838,6 +3838,325 @@ function historyNav(onBack, onForward) {
|
|
|
3838
3838
|
};
|
|
3839
3839
|
}
|
|
3840
3840
|
|
|
3841
|
+
// src/view-protocol.ts
|
|
3842
|
+
var VIEW_API = 1;
|
|
3843
|
+
var VIEW_PORT_CHANNEL = "wildwinter-view:port";
|
|
3844
|
+
var VIEW_PORT_MESSAGE = "wildwinter-view:port";
|
|
3845
|
+
|
|
3846
|
+
// src/view-host-web.ts
|
|
3847
|
+
var PALETTE_TOKENS = [
|
|
3848
|
+
"--bg",
|
|
3849
|
+
"--surface",
|
|
3850
|
+
"--card",
|
|
3851
|
+
"--ink",
|
|
3852
|
+
"--muted",
|
|
3853
|
+
"--line",
|
|
3854
|
+
"--line-soft",
|
|
3855
|
+
"--accent",
|
|
3856
|
+
"--accent-soft",
|
|
3857
|
+
"--on-accent",
|
|
3858
|
+
"--danger",
|
|
3859
|
+
"--warn",
|
|
3860
|
+
"--ok",
|
|
3861
|
+
"--font-ui",
|
|
3862
|
+
"--font-read",
|
|
3863
|
+
"--font-mono",
|
|
3864
|
+
...Array.from({ length: 12 }, (_, i) => `--char-${i}`)
|
|
3865
|
+
];
|
|
3866
|
+
var GRAMMAR_TOKENS = [
|
|
3867
|
+
"--color-scheme",
|
|
3868
|
+
"--chrome-size",
|
|
3869
|
+
"--scrim",
|
|
3870
|
+
"--shadow-1",
|
|
3871
|
+
"--shadow-2",
|
|
3872
|
+
"--shadow-3",
|
|
3873
|
+
"--ease-standard",
|
|
3874
|
+
"--dur-quick",
|
|
3875
|
+
"--dur-base",
|
|
3876
|
+
"--dur-settle",
|
|
3877
|
+
"--dur-slow",
|
|
3878
|
+
"--dur-panel",
|
|
3879
|
+
"--dur-exit",
|
|
3880
|
+
"--radius-sm",
|
|
3881
|
+
"--radius-md",
|
|
3882
|
+
"--radius-lg",
|
|
3883
|
+
"--radius-pill",
|
|
3884
|
+
"--scroll-thumb",
|
|
3885
|
+
"--scroll-thumb-hover"
|
|
3886
|
+
];
|
|
3887
|
+
var VIEW_THEME_TOKENS = [...PALETTE_TOKENS, ...GRAMMAR_TOKENS];
|
|
3888
|
+
function readTheme(root = document.documentElement, tokens = VIEW_THEME_TOKENS) {
|
|
3889
|
+
const style = getComputedStyle(root);
|
|
3890
|
+
const out = {};
|
|
3891
|
+
for (const name of tokens) {
|
|
3892
|
+
const value = style.getPropertyValue(name).trim();
|
|
3893
|
+
if (value !== "") out[name] = value;
|
|
3894
|
+
}
|
|
3895
|
+
const declared = (style.getPropertyValue("color-scheme") || style.colorScheme || "").trim();
|
|
3896
|
+
const words = declared.split(/\s+/);
|
|
3897
|
+
let scheme = "light";
|
|
3898
|
+
if (declared === "dark") scheme = "dark";
|
|
3899
|
+
else if (words.includes("light") && words.includes("dark")) {
|
|
3900
|
+
scheme = typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
3901
|
+
}
|
|
3902
|
+
return { scheme, tokens: out };
|
|
3903
|
+
}
|
|
3904
|
+
function onViewPort(fn, target = window, closed) {
|
|
3905
|
+
const listener = (e) => {
|
|
3906
|
+
if (e.source !== target) return;
|
|
3907
|
+
const data = e.data;
|
|
3908
|
+
if (!data || typeof data !== "object" || data.type !== VIEW_PORT_MESSAGE) return;
|
|
3909
|
+
const port = e.ports?.[0];
|
|
3910
|
+
const meta = data.meta;
|
|
3911
|
+
if (!meta || typeof meta.window !== "number" || typeof meta.extension !== "string" || typeof meta.view !== "string") return;
|
|
3912
|
+
if (meta.closed === true) {
|
|
3913
|
+
closed?.(meta);
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
if (!port) return;
|
|
3917
|
+
fn(port, meta);
|
|
3918
|
+
};
|
|
3919
|
+
target.addEventListener("message", listener);
|
|
3920
|
+
return () => target.removeEventListener("message", listener);
|
|
3921
|
+
}
|
|
3922
|
+
var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3923
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3924
|
+
var isId = (v) => typeof v === "string" && v !== "";
|
|
3925
|
+
function isJson(v, depth = 0) {
|
|
3926
|
+
if (depth > 64) return false;
|
|
3927
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return true;
|
|
3928
|
+
if (typeof v === "number") return Number.isFinite(v);
|
|
3929
|
+
if (Array.isArray(v)) return v.every((x) => isJson(x, depth + 1));
|
|
3930
|
+
if (isObj(v)) return Object.getPrototypeOf(v) === Object.prototype && Object.values(v).every((x) => isJson(x, depth + 1));
|
|
3931
|
+
return false;
|
|
3932
|
+
}
|
|
3933
|
+
var bad = (message) => ({ code: "bad-request", message });
|
|
3934
|
+
function argsProblem(op, args) {
|
|
3935
|
+
switch (op) {
|
|
3936
|
+
case "project":
|
|
3937
|
+
case "selection":
|
|
3938
|
+
return null;
|
|
3939
|
+
case "node":
|
|
3940
|
+
if (!isId(args["id"])) return "A node request names the node by its id.";
|
|
3941
|
+
if (args["children"] !== void 0 && typeof args["children"] !== "boolean") return "A node request's children is true or false.";
|
|
3942
|
+
return null;
|
|
3943
|
+
case "media":
|
|
3944
|
+
if (!Array.isArray(args["ids"]) || !args["ids"].every(isId)) return "A media request lists the lines' ids.";
|
|
3945
|
+
return null;
|
|
3946
|
+
case "select":
|
|
3947
|
+
return isId(args["id"]) ? null : "A select request names the node by its id.";
|
|
3948
|
+
case "write": {
|
|
3949
|
+
if (typeof args["label"] !== "string" || args["label"].trim() === "") return "A write has a label, for the undo step.";
|
|
3950
|
+
const edits = args["edits"];
|
|
3951
|
+
if (!Array.isArray(edits)) return "A write lists its edits.";
|
|
3952
|
+
for (const e of edits) {
|
|
3953
|
+
if (!isObj(e) || !isId(e["node"])) return "Each edit names its node by id.";
|
|
3954
|
+
if (typeof e["field"] !== "string" || !NAME.test(e["field"])) return "Each edit names one of the extension's fields, never a path inside one.";
|
|
3955
|
+
if ("value" in e && e["value"] !== void 0 && !isJson(e["value"])) return `The value for ${e["field"]} isn't plain JSON.`;
|
|
3956
|
+
}
|
|
3957
|
+
return null;
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
var OPS = ["project", "node", "selection", "media", "write", "select"];
|
|
3962
|
+
function hostView(port, adapter, opts) {
|
|
3963
|
+
const { meta } = opts;
|
|
3964
|
+
const ext = meta.extension;
|
|
3965
|
+
const allowed = /* @__PURE__ */ new Set([ext, ...opts.reads]);
|
|
3966
|
+
const theme = opts.theme ?? (() => readTheme());
|
|
3967
|
+
const debounceMs = opts.debounceMs ?? 100;
|
|
3968
|
+
let welcomed = false;
|
|
3969
|
+
let closed = false;
|
|
3970
|
+
let pending = null;
|
|
3971
|
+
let timer3;
|
|
3972
|
+
const post = (msg) => {
|
|
3973
|
+
if (!closed) port.postMessage(msg);
|
|
3974
|
+
};
|
|
3975
|
+
const emit = (name, value) => {
|
|
3976
|
+
if (welcomed) post({ t: "event", name, value });
|
|
3977
|
+
};
|
|
3978
|
+
const filterNode = (node2) => {
|
|
3979
|
+
const keptExt = {};
|
|
3980
|
+
for (const [id, data] of Object.entries(node2.ext ?? {})) if (allowed.has(id)) keptExt[id] = data;
|
|
3981
|
+
const base = { id: node2.id, kind: node2.kind, parent: node2.parent, label: node2.label, host: node2.host ?? {}, ext: keptExt };
|
|
3982
|
+
const out = typeof node2.held === "string" ? { ...base, held: node2.held } : base;
|
|
3983
|
+
return node2.children ? { ...out, children: node2.children.map(filterNode) } : out;
|
|
3984
|
+
};
|
|
3985
|
+
const answer = async (op, args) => {
|
|
3986
|
+
switch (op) {
|
|
3987
|
+
case "project":
|
|
3988
|
+
return { value: filterNode(await adapter.project()) };
|
|
3989
|
+
case "node": {
|
|
3990
|
+
const node2 = await adapter.node(args["id"], args["children"] === true);
|
|
3991
|
+
return { value: node2 ? filterNode(node2) : null };
|
|
3992
|
+
}
|
|
3993
|
+
case "selection":
|
|
3994
|
+
return { value: await adapter.selection() };
|
|
3995
|
+
case "media":
|
|
3996
|
+
if (!adapter.media) return { refusal: { code: "unsupported", message: "This app has no recordings or timings for lines." } };
|
|
3997
|
+
return { value: await adapter.media(args["ids"]) };
|
|
3998
|
+
case "select":
|
|
3999
|
+
return { value: await adapter.select(args["id"]) };
|
|
4000
|
+
case "write": {
|
|
4001
|
+
const writable = adapter.writable(ext);
|
|
4002
|
+
if (writable.readOnly) return { refusal: { code: "read-only", message: writable.why ?? "This project is read-only just now." } };
|
|
4003
|
+
const edits = args["edits"].map((e) => "value" in e && e.value !== void 0 ? { node: e.node, field: e.field, value: e.value } : { node: e.node, field: e.field });
|
|
4004
|
+
if (edits.length === 0) return { value: { written: 0 } };
|
|
4005
|
+
if (edits.length > 1) {
|
|
4006
|
+
const projectId = (await adapter.project()).id;
|
|
4007
|
+
const onProject = edits.filter((e) => e.node === projectId).length;
|
|
4008
|
+
if (onProject > 0 && onProject < edits.length) {
|
|
4009
|
+
return { refusal: bad("A write changes the project or other nodes, not both: they undo separately. Make two writes.") };
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
const refused = await adapter.write(ext, args["label"], edits);
|
|
4013
|
+
if (refused) return { refusal: refused };
|
|
4014
|
+
return { value: { written: edits.length } };
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
};
|
|
4018
|
+
const request = async (msg) => {
|
|
4019
|
+
const id = msg["id"];
|
|
4020
|
+
if (typeof id !== "number" || !Number.isInteger(id)) return;
|
|
4021
|
+
const reply = (r) => post("refusal" in r ? { t: "reply", id, ok: false, refusal: r.refusal } : { t: "reply", id, ok: true, value: r.value });
|
|
4022
|
+
const op = msg["op"];
|
|
4023
|
+
if (typeof op !== "string" || !OPS.includes(op)) {
|
|
4024
|
+
reply({ refusal: bad(`There's no request called ${JSON.stringify(op)}.`) });
|
|
4025
|
+
return;
|
|
4026
|
+
}
|
|
4027
|
+
const args = msg["args"] ?? {};
|
|
4028
|
+
if (!isObj(args)) {
|
|
4029
|
+
reply({ refusal: bad("A request's args are an object.") });
|
|
4030
|
+
return;
|
|
4031
|
+
}
|
|
4032
|
+
const problem = argsProblem(op, args);
|
|
4033
|
+
if (problem) {
|
|
4034
|
+
reply({ refusal: bad(problem) });
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
try {
|
|
4038
|
+
reply(await answer(op, args));
|
|
4039
|
+
} catch (e) {
|
|
4040
|
+
reply({ refusal: { code: "failed", message: `The app couldn't do that: ${e instanceof Error ? e.message : String(e)}` } });
|
|
4041
|
+
}
|
|
4042
|
+
};
|
|
4043
|
+
const hello = (msg) => {
|
|
4044
|
+
const api = msg["api"];
|
|
4045
|
+
if (api !== VIEW_API) {
|
|
4046
|
+
const who = `${opts.names?.extension ?? ext}'s ${opts.names?.view ?? meta.view}`;
|
|
4047
|
+
const message = typeof api === "number" ? `${who} was built for view API ${api}; this app serves ${VIEW_API}.` : `${who} didn't say which view API it was built for; this app serves ${VIEW_API}.`;
|
|
4048
|
+
post({ t: "refused", message });
|
|
4049
|
+
close(false);
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
4052
|
+
const welcome = {
|
|
4053
|
+
t: "welcome",
|
|
4054
|
+
api: VIEW_API,
|
|
4055
|
+
host: adapter.host,
|
|
4056
|
+
extension: { id: ext, version: meta.version, ...meta.pinned !== void 0 ? { pinned: meta.pinned } : {} },
|
|
4057
|
+
view: meta.view,
|
|
4058
|
+
settings: adapter.settings(ext),
|
|
4059
|
+
theme: theme(),
|
|
4060
|
+
writable: adapter.writable(ext)
|
|
4061
|
+
};
|
|
4062
|
+
welcomed = true;
|
|
4063
|
+
post(welcome);
|
|
4064
|
+
};
|
|
4065
|
+
const onMessage = (e) => {
|
|
4066
|
+
const msg = e.data;
|
|
4067
|
+
if (closed || !isObj(msg)) return;
|
|
4068
|
+
if (!welcomed) {
|
|
4069
|
+
if (msg["t"] === "hello") hello(msg);
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
4072
|
+
if (msg["t"] === "request") void request(msg);
|
|
4073
|
+
else if (msg["t"] === "key" && typeof msg["combo"] === "string") adapter.key?.(msg["combo"]);
|
|
4074
|
+
};
|
|
4075
|
+
port.addEventListener("message", onMessage);
|
|
4076
|
+
port.start();
|
|
4077
|
+
const flush = () => {
|
|
4078
|
+
timer3 = void 0;
|
|
4079
|
+
const nodes = pending;
|
|
4080
|
+
pending = null;
|
|
4081
|
+
if (nodes === null) return;
|
|
4082
|
+
emit("changed", { nodes: nodes === "all" ? "all" : [...nodes] });
|
|
4083
|
+
};
|
|
4084
|
+
function close(sayClosing = true) {
|
|
4085
|
+
if (closed) return;
|
|
4086
|
+
if (sayClosing) emit("closing", {});
|
|
4087
|
+
clearTimeout(timer3);
|
|
4088
|
+
closed = true;
|
|
4089
|
+
port.removeEventListener("message", onMessage);
|
|
4090
|
+
port.close();
|
|
4091
|
+
}
|
|
4092
|
+
return {
|
|
4093
|
+
changed(nodes) {
|
|
4094
|
+
if (closed || !welcomed) return;
|
|
4095
|
+
if (nodes === "all" || pending === "all") pending = "all";
|
|
4096
|
+
else {
|
|
4097
|
+
pending ??= /* @__PURE__ */ new Set();
|
|
4098
|
+
for (const n of nodes) pending.add(n);
|
|
4099
|
+
}
|
|
4100
|
+
if (timer3 === void 0) timer3 = setTimeout(flush, debounceMs);
|
|
4101
|
+
},
|
|
4102
|
+
selection() {
|
|
4103
|
+
if (closed || !welcomed) return;
|
|
4104
|
+
void Promise.resolve().then(() => adapter.selection()).then((s) => emit("selection", s), () => {
|
|
4105
|
+
});
|
|
4106
|
+
},
|
|
4107
|
+
theme() {
|
|
4108
|
+
if (!closed) emit("theme", theme());
|
|
4109
|
+
},
|
|
4110
|
+
writable() {
|
|
4111
|
+
if (!closed) emit("writable", adapter.writable(ext));
|
|
4112
|
+
},
|
|
4113
|
+
close: () => close(true)
|
|
4114
|
+
};
|
|
4115
|
+
}
|
|
4116
|
+
function hostViews(setup, opts = {}) {
|
|
4117
|
+
const handles = /* @__PURE__ */ new Map();
|
|
4118
|
+
const drop = (meta) => {
|
|
4119
|
+
handles.get(meta.window)?.close();
|
|
4120
|
+
handles.delete(meta.window);
|
|
4121
|
+
};
|
|
4122
|
+
const stop = onViewPort((port, meta) => {
|
|
4123
|
+
drop(meta);
|
|
4124
|
+
const s = setup(meta);
|
|
4125
|
+
if (!s) {
|
|
4126
|
+
port.close();
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
handles.set(meta.window, hostView(port, s.adapter, {
|
|
4130
|
+
meta,
|
|
4131
|
+
reads: s.reads,
|
|
4132
|
+
...s.names !== void 0 ? { names: s.names } : {},
|
|
4133
|
+
...opts.theme !== void 0 ? { theme: opts.theme } : {},
|
|
4134
|
+
...opts.debounceMs !== void 0 ? { debounceMs: opts.debounceMs } : {}
|
|
4135
|
+
}));
|
|
4136
|
+
}, opts.target ?? window, drop);
|
|
4137
|
+
const each = (f) => {
|
|
4138
|
+
for (const h of handles.values()) f(h);
|
|
4139
|
+
};
|
|
4140
|
+
return {
|
|
4141
|
+
get size() {
|
|
4142
|
+
return handles.size;
|
|
4143
|
+
},
|
|
4144
|
+
changed: (nodes) => each((h) => h.changed(nodes)),
|
|
4145
|
+
selection: () => each((h) => h.selection()),
|
|
4146
|
+
theme: () => each((h) => h.theme()),
|
|
4147
|
+
writable: () => each((h) => h.writable()),
|
|
4148
|
+
close: () => {
|
|
4149
|
+
each((h) => h.close());
|
|
4150
|
+
handles.clear();
|
|
4151
|
+
},
|
|
4152
|
+
dispose: () => {
|
|
4153
|
+
stop();
|
|
4154
|
+
each((h) => h.close());
|
|
4155
|
+
handles.clear();
|
|
4156
|
+
}
|
|
4157
|
+
};
|
|
4158
|
+
}
|
|
4159
|
+
|
|
3841
4160
|
exports.EDIT_MENU = EDIT_MENU;
|
|
3842
4161
|
exports.FILE_MENU = FILE_MENU;
|
|
3843
4162
|
exports.GO_MENU = GO_MENU;
|
|
@@ -3853,7 +4172,11 @@ exports.PROPERTY_TYPES = PROPERTY_TYPES;
|
|
|
3853
4172
|
exports.PUBLISH_MENU = PUBLISH_MENU;
|
|
3854
4173
|
exports.RESERVED_PROPERTY_NAMES = RESERVED_PROPERTY_NAMES;
|
|
3855
4174
|
exports.REVIEW_MENU = REVIEW_MENU;
|
|
4175
|
+
exports.VIEW_API = VIEW_API;
|
|
3856
4176
|
exports.VIEW_MENU = VIEW_MENU;
|
|
4177
|
+
exports.VIEW_PORT_CHANNEL = VIEW_PORT_CHANNEL;
|
|
4178
|
+
exports.VIEW_PORT_MESSAGE = VIEW_PORT_MESSAGE;
|
|
4179
|
+
exports.VIEW_THEME_TOKENS = VIEW_THEME_TOKENS;
|
|
3857
4180
|
exports.askIdentity = askIdentity;
|
|
3858
4181
|
exports.bindPropertyName = bindPropertyName;
|
|
3859
4182
|
exports.bindPropertyRef = bindPropertyRef;
|
|
@@ -3891,6 +4214,8 @@ exports.gameIdify = gameIdify;
|
|
|
3891
4214
|
exports.hideTip = hideTip;
|
|
3892
4215
|
exports.hintBar = hintBar;
|
|
3893
4216
|
exports.historyNav = historyNav;
|
|
4217
|
+
exports.hostView = hostView;
|
|
4218
|
+
exports.hostViews = hostViews;
|
|
3894
4219
|
exports.icon = icon;
|
|
3895
4220
|
exports.iconBtn = iconBtn;
|
|
3896
4221
|
exports.iconHtml = iconHtml;
|
|
@@ -3925,6 +4250,7 @@ exports.mountPropertyList = mountPropertyList;
|
|
|
3925
4250
|
exports.mountSettingsDialog = mountSettingsDialog;
|
|
3926
4251
|
exports.mountWelcome = mountWelcome;
|
|
3927
4252
|
exports.moveItem = moveItem;
|
|
4253
|
+
exports.onViewPort = onViewPort;
|
|
3928
4254
|
exports.openAnchoredPanel = openAnchoredPanel;
|
|
3929
4255
|
exports.openComments = openComments;
|
|
3930
4256
|
exports.openContextMenu = openContextMenu;
|
|
@@ -3941,6 +4267,7 @@ exports.problemName = problemName;
|
|
|
3941
4267
|
exports.propertyNameProblem = propertyNameProblem;
|
|
3942
4268
|
exports.propertyNameify = propertyNameify;
|
|
3943
4269
|
exports.propertyRefProblem = propertyRefProblem;
|
|
4270
|
+
exports.readTheme = readTheme;
|
|
3944
4271
|
exports.relativeTime = relativeTime;
|
|
3945
4272
|
exports.renderStepperBar = renderStepperBar;
|
|
3946
4273
|
exports.revalidatePropertyRefs = revalidatePropertyRefs;
|