Skip to main content

max / makenotwork

3.6 KB · 82 lines History Blame Raw
1 // MNW frontend core, entry point.
2 //
3 // Loaded once, globally, as a `<script type="module">` in place of the retired
4 // `static/mnw.js`. It installs every delegated listener the old god-file did
5 // (toasts, HTMX glue, the data-action + hx-callback dispatchers, copy-link,
6 // keyboard, tabs, restart banner) and bridges the shared primitives onto
7 // `window` so the not-yet-migrated `static/*.js` files keep resolving them.
8 //
9 // As feature files migrate into frontend/src, drop their entries from the
10 // bridge and register their handlers via `dispatch.register()` instead of a
11 // global; the `frontend_globals` seal ratchets the legacy count down.
12
13 import { escapeHtml, escapeAttr } from './dom.ts';
14 import { csrfHeaders, apiError } from './net.ts';
15 import { showToast, initToasts } from './toast.ts';
16 import { safeGet, safeSet } from './storage.ts';
17 import { copyWithFeedback, initCopyLink } from './clipboard.ts';
18 import { resolveHtmxLoadingButton, withLoadingState } from './loading.ts';
19 import { initActionDispatcher, initHxCallbackDispatcher } from './dispatch.ts';
20 import { setActiveTab, initTabs } from './tabs.ts';
21 import { toggleShortcutsHelp, initKeyboard } from './keyboard.ts';
22 import { initRestartBanner } from './restart-banner.ts';
23 import { initHtmxGlue } from './htmx-glue.ts';
24
25 // Common, lightweight islands are registered globally here (side-effect import
26 // defines the custom element). Heavy or page-specific islands (media player,
27 // uploader) load per-page instead. Custom elements auto-upgrade on connect, so
28 // carousels in HTMX-swapped content wire themselves with no extra glue.
29 import '../islands/carousel.ts';
30 // Image uploader appears in HTMX-swapped wizard steps, so it's registered
31 // globally (auto-upgrades on swap). The heavier upload flows (item audio/version
32 // queue, gallery, bundle) still use the legacy static/upload.js until migrated.
33 import '../islands/uploader/image-uploader.ts';
34 // SyncKit dashboard: the keys-endpoint secret action. Registers a dispatcher
35 // verb (no window global) and is inert on pages without the button.
36 import '../islands/synckit-keys-secret.ts';
37
38 /**
39 * Legacy bridge, the not-yet-migrated `static/*.js` files call these by their
40 * historical global names. `mnw.js` exposed them as classic-script globals
41 * (function declarations / `window.*`); the module re-exposes them so the
42 * cutover is behavior-preserving. Retire each entry as its consumers move into
43 * frontend/src.
44 */
45 function installLegacyBridge(): void {
46 const w = window as unknown as Record<string, unknown>;
47 w.escapeHtml = escapeHtml;
48 w.escapeAttr = escapeAttr;
49 w.csrfHeaders = csrfHeaders;
50 w.showToast = showToast;
51 w.safeStorageGet = safeGet;
52 w.safeStorageSet = safeSet;
53 w.setActiveTab = setActiveTab;
54 w.toggleShortcutsHelp = toggleShortcutsHelp;
55 w.copyWithFeedback = copyWithFeedback;
56 w.resolveHtmxLoadingButton = resolveHtmxLoadingButton;
57 w.withLoadingState = withLoadingState;
58 // Historical name; wraps apiError so the (response, fallback) signature and
59 // the Promise<string> return match the old global exactly.
60 w.apiErrorMessage = (response: Response | null | undefined, fallback?: string) => apiError(response, fallback);
61 }
62
63 function initCore(): void {
64 installLegacyBridge();
65 initToasts();
66 initHtmxGlue();
67 initActionDispatcher();
68 initHxCallbackDispatcher();
69 initCopyLink();
70 initKeyboard();
71 initTabs();
72 initRestartBanner();
73 }
74
75 // A module script is deferred, so the DOM is already parsed by the time this
76 // runs; the readyState guard is belt-and-suspenders.
77 if (document.readyState === 'loading') {
78 document.addEventListener('DOMContentLoaded', initCore);
79 } else {
80 initCore();
81 }
82