Skip to main content

max / makenotwork

3.4 KB · 79 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
35 /**
36 * Legacy bridge — the not-yet-migrated `static/*.js` files call these by their
37 * historical global names. `mnw.js` exposed them as classic-script globals
38 * (function declarations / `window.*`); the module re-exposes them so the
39 * cutover is behavior-preserving. Retire each entry as its consumers move into
40 * frontend/src.
41 */
42 function installLegacyBridge(): void {
43 const w = window as unknown as Record<string, unknown>;
44 w.escapeHtml = escapeHtml;
45 w.escapeAttr = escapeAttr;
46 w.csrfHeaders = csrfHeaders;
47 w.showToast = showToast;
48 w.safeStorageGet = safeGet;
49 w.safeStorageSet = safeSet;
50 w.setActiveTab = setActiveTab;
51 w.toggleShortcutsHelp = toggleShortcutsHelp;
52 w.copyWithFeedback = copyWithFeedback;
53 w.resolveHtmxLoadingButton = resolveHtmxLoadingButton;
54 w.withLoadingState = withLoadingState;
55 // Historical name; wraps apiError so the (response, fallback) signature and
56 // the Promise<string> return match the old global exactly.
57 w.apiErrorMessage = (response: Response | null | undefined, fallback?: string) => apiError(response, fallback);
58 }
59
60 function initCore(): void {
61 installLegacyBridge();
62 initToasts();
63 initHtmxGlue();
64 initActionDispatcher();
65 initHxCallbackDispatcher();
66 initCopyLink();
67 initKeyboard();
68 initTabs();
69 initRestartBanner();
70 }
71
72 // A module script is deferred, so the DOM is already parsed by the time this
73 // runs; the readyState guard is belt-and-suspenders.
74 if (document.readyState === 'loading') {
75 document.addEventListener('DOMContentLoaded', initCore);
76 } else {
77 initCore();
78 }
79