diff --git a/client/main.ts b/client/main.ts index e5d6c0a..4888113 100644 --- a/client/main.ts +++ b/client/main.ts @@ -18,6 +18,10 @@ // (voice/STT plumbing) // ============================================================ +// Migrated modules — each side-effect import installs its window-global +// shim for legacy consumers and self-registers any DOM listeners. +import './ui-state'; + console.log('[client] TypeScript bundle loaded.'); // Marker so anything else that wants to know if the bundle reached diff --git a/client/ui-state.ts b/client/ui-state.ts new file mode 100644 index 0000000..5f01cf1 --- /dev/null +++ b/client/ui-state.ts @@ -0,0 +1,37 @@ +// ============================================================ +// client/ui-state.ts — sub-pill / sub-tab persistence across browser restart. +// +// First file migrated out of public/js/ as ES module + TypeScript. +// Tab-level selection (ped_last_tab) is handled in app.js (still legacy). +// This file persists finer-grained choices that would otherwise reset +// to defaults when the user closes the browser, signs out / back in, +// or reloads the page — because _componentCache is in-memory only. +// +// All keys live under "ped_ui/" in localStorage. Reads and writes are +// wrapped in try/catch — localStorage can throw in some contexts +// (Safari private mode, storage quotas), and a persistence failure +// must never break the UI. +// +// Compat: window.UIState is still assigned for the 10+ legacy consumers +// (app.js, sub-nav.js, several IIFE modules). Once those are migrated +// to ES module imports, the window assignment can be removed. +// ============================================================ + +const PREFIX = 'ped_ui/'; + +export function get(key: string): string | null { + try { return localStorage.getItem(PREFIX + key); } catch (e) { return null; } +} + +export function set(key: string, value: string): void { + try { localStorage.setItem(PREFIX + key, value); } catch (e) { /* swallow — see header */ } +} + +export function del(key: string): void { + try { localStorage.removeItem(PREFIX + key); } catch (e) { /* swallow */ } +} + +export const UIState = { get, set, del }; + +// Legacy bridge — kept until every consumer is migrated to ES imports. +(window as any).UIState = UIState; diff --git a/public/index.html b/public/index.html index 78e51d6..c34906a 100644 --- a/public/index.html +++ b/public/index.html @@ -452,7 +452,7 @@ - + diff --git a/public/js/ui-state.js b/public/js/ui-state.js deleted file mode 100644 index 9dc9d40..0000000 --- a/public/js/ui-state.js +++ /dev/null @@ -1,35 +0,0 @@ -// ============================================================ -// UI STATE — sub-pill / sub-tab persistence across browser restart -// ============================================================ -// Tab-level selection (ped_last_tab) is handled in app.js. This file -// persists the finer-grained choices that would otherwise reset to -// defaults when the user closes the browser, signs out / back in, or -// reloads the page — because _componentCache is in-memory and cleared -// on reload. -// -// All keys live under the "ped_ui/" namespace in localStorage so they -// are easy to enumerate and clear. Reads and writes are wrapped in -// try/catch — localStorage can throw in some contexts (Safari private -// mode, storage quotas), and a persistence failure must never break -// the UI. -// ============================================================ - -(function () { - var PREFIX = 'ped_ui/'; - - function get(key) { - try { return localStorage.getItem(PREFIX + key); } catch (e) { return null; } - } - function set(key, value) { - try { localStorage.setItem(PREFIX + key, value); } catch (e) {} - } - function del(key) { - try { localStorage.removeItem(PREFIX + key); } catch (e) {} - } - - window.UIState = { - get: get, - set: set, - del: del, - }; -})(); diff --git a/scripts/build-client.mjs b/scripts/build-client.mjs index 1ac54c2..4c2ebb4 100644 --- a/scripts/build-client.mjs +++ b/scripts/build-client.mjs @@ -31,17 +31,22 @@ if (!existsSync(SRC)) { } mkdirSync(OUT, { recursive: true }); -// Each top-level .ts/.tsx file in client/ is a separate entry point. -// Sub-directories are imported by the entries via TS imports — they -// are NOT separate entries, they get bundled into their parent. -const entryFiles = readdirSync(SRC, { withFileTypes: true }) - .filter(d => d.isFile() && /\.tsx?$/.test(d.name)) - .map(d => resolve(SRC, d.name)); +// Explicit entry list — every other .ts file in client/ is a sub-module +// imported by an entry. Add new entries here when a feature truly needs +// its own separate bundle (different DOM lifecycle, lazy-loaded route, +// etc.). For most modernization work, just import into main.ts. +const ENTRIES = [ + 'main.ts', +]; -if (entryFiles.length === 0) { - console.error(`[build-client] No .ts entry points found in ${SRC}`); - process.exit(1); -} +const entryFiles = ENTRIES.map(name => { + const p = resolve(SRC, name); + if (!existsSync(p)) { + console.error(`[build-client] Entry file does not exist: ${p}`); + process.exit(1); + } + return p; +}); const buildOpts = { entryPoints: entryFiles,