|
41 | 41 | <div class="facade-loader__text">Loading...</div> |
42 | 42 | </div> |
43 | 43 | </div> |
44 | | -<script type="module"> |
45 | | - const STORAGE_KEY = '@wippy_token_info'; |
46 | | - const appEl = document.getElementById('app'); |
| 44 | +<script> |
| 45 | + (async function () { |
| 46 | + var STORAGE_KEY = '@wippy_token_info'; |
| 47 | + var appEl = document.getElementById('app'); |
47 | 48 |
|
48 | | - function showError(title, detail) { |
49 | | - appEl.innerHTML = |
50 | | - '<div class="facade-error">' + |
51 | | - '<div class="facade-error__title">' + title + '</div>' + |
52 | | - (detail ? '<div class="facade-error__detail">' + detail + '</div>' : '') + |
53 | | - '<button class="facade-error__retry" onclick="location.reload()">Retry</button>' + |
54 | | - '</div>'; |
55 | | - } |
56 | | - |
57 | | - try { |
58 | | - const res = await fetch('/api/public/facade/config'); |
59 | | - if (!res.ok) { |
60 | | - showError('Failed to load configuration', 'Server returned ' + res.status + '. Check that the backend is running.'); |
61 | | - throw new Error('config fetch failed: ' + res.status); |
| 49 | + function showError(title, detail) { |
| 50 | + appEl.innerHTML = |
| 51 | + '<div class="facade-error">' + |
| 52 | + '<div class="facade-error__title">' + title + '</div>' + |
| 53 | + (detail ? '<div class="facade-error__detail">' + detail + '</div>' : '') + |
| 54 | + '<button class="facade-error__retry" onclick="location.reload()">Retry</button>' + |
| 55 | + '</div>'; |
62 | 56 | } |
63 | | - const cfg = await res.json(); |
64 | 57 |
|
65 | | - const stored = localStorage.getItem(STORAGE_KEY); |
66 | | - if (!stored) { window.location.href = cfg.login_path || '/login.html'; throw new Error('no token'); } |
| 58 | + try { |
| 59 | + var res = await fetch('/api/public/facade/config'); |
| 60 | + if (!res.ok) { |
| 61 | + showError('Failed to load configuration', 'Server returned ' + res.status + '. Check that the backend is running.'); |
| 62 | + throw new Error('config fetch failed: ' + res.status); |
| 63 | + } |
| 64 | + var cfg = await res.json(); |
67 | 65 |
|
68 | | - let token; |
69 | | - try { token = JSON.parse(stored).token; } |
70 | | - catch { window.location.href = cfg.login_path || '/login.html'; throw new Error('bad token'); } |
| 66 | + var stored = localStorage.getItem(STORAGE_KEY); |
| 67 | + if (!stored) { window.location.href = cfg.login_path || '/login.html'; throw new Error('no token'); } |
71 | 68 |
|
72 | | - const apiUrl = cfg.api_url || window.location.origin; |
73 | | - const wsProto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
74 | | - const wsUrl = cfg.ws_url || (wsProto + '//' + window.location.host); |
| 69 | + var token; |
| 70 | + try { token = JSON.parse(stored).token; } |
| 71 | + catch (e) { window.location.href = cfg.login_path || '/login.html'; throw new Error('bad token'); } |
75 | 72 |
|
76 | | - let mod; |
77 | | - try { |
78 | | - mod = await import(cfg.facade_url + '/module.js'); |
79 | | - } catch (importErr) { |
80 | | - showError('Failed to load frontend bundle', 'Could not load ' + cfg.facade_url + '/module.js — ' + importErr.message); |
81 | | - throw importErr; |
82 | | - } |
| 73 | + var apiUrl = cfg.api_url || window.location.origin; |
| 74 | + var wsProto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
| 75 | + var wsUrl = cfg.ws_url || (wsProto + '//' + window.location.host); |
83 | 76 |
|
84 | | - if (typeof window.initWippyApp !== 'function') { |
85 | | - showError('Frontend bundle error', 'initWippyApp not found after loading module. The bundle may be incompatible.'); |
86 | | - throw new Error('initWippyApp not found'); |
87 | | - } |
| 77 | + // Inject import map BEFORE any module loading |
| 78 | + try { |
| 79 | + var mapRes = await fetch(cfg.facade_url + '/import-map.json'); |
| 80 | + if (mapRes.ok) { |
| 81 | + var mapData = await mapRes.json(); |
| 82 | + var mapScript = document.createElement('script'); |
| 83 | + mapScript.type = 'importmap'; |
| 84 | + mapScript.textContent = JSON.stringify(mapData); |
| 85 | + document.head.appendChild(mapScript); |
| 86 | + } |
| 87 | + } catch (e) { |
| 88 | + console.warn('Failed to load import map:', e); |
| 89 | + } |
| 90 | + |
| 91 | + // Now safe to load modules — import map is already in the DOM |
| 92 | + try { |
| 93 | + await import(cfg.facade_url + '/module.js'); |
| 94 | + } catch (importErr) { |
| 95 | + showError('Failed to load frontend bundle', 'Could not load ' + cfg.facade_url + '/module.js — ' + importErr.message); |
| 96 | + throw importErr; |
| 97 | + } |
88 | 98 |
|
89 | | - const events = window.initWippyApp({ |
90 | | - auth: { token, expiresAt: new Date(Date.now() + 86400000).toISOString() }, |
91 | | - feature: { |
92 | | - session: { type: cfg.feature.session_type }, |
93 | | - history: cfg.feature.history_mode, |
94 | | - env: { APP_API_URL: apiUrl, APP_WEBSOCKET_URL: wsUrl }, |
95 | | - routePrefix: apiUrl, |
96 | | - showAdmin: cfg.feature.show_admin, |
97 | | - allowSelectModel: cfg.feature.allow_select_model, |
98 | | - startNavOpen: cfg.feature.start_nav_open, |
99 | | - hideNavBar: cfg.feature.hide_nav_bar, |
100 | | - disableRightPanel: cfg.feature.disable_right_panel, |
101 | | - }, |
102 | | - customization: { |
103 | | - customCSS: cfg.customization.custom_css || '', |
104 | | - cssVariables: cfg.customization.css_variables || {}, |
105 | | - i18n: cfg.customization.i18n || {}, |
106 | | - icons: cfg.customization.icons || {}, |
107 | | - }, |
108 | | - }, '#app'); |
| 99 | + if (typeof window.initWippyApp !== 'function') { |
| 100 | + showError('Frontend bundle error', 'initWippyApp not found after loading module. The bundle may be incompatible.'); |
| 101 | + throw new Error('initWippyApp not found'); |
| 102 | + } |
109 | 103 |
|
110 | | - const loginPath = cfg.login_path || '/login.html'; |
| 104 | + var events = window.initWippyApp({ |
| 105 | + auth: { token: token, expiresAt: new Date(Date.now() + 86400000).toISOString() }, |
| 106 | + feature: { |
| 107 | + session: { type: cfg.feature.session_type }, |
| 108 | + history: cfg.feature.history_mode, |
| 109 | + env: { APP_API_URL: apiUrl, APP_WEBSOCKET_URL: wsUrl }, |
| 110 | + routePrefix: apiUrl, |
| 111 | + showAdmin: cfg.feature.show_admin, |
| 112 | + allowSelectModel: cfg.feature.allow_select_model, |
| 113 | + startNavOpen: cfg.feature.start_nav_open, |
| 114 | + hideNavBar: cfg.feature.hide_nav_bar, |
| 115 | + disableRightPanel: cfg.feature.disable_right_panel, |
| 116 | + }, |
| 117 | + customization: { |
| 118 | + customCSS: cfg.customization.custom_css || '', |
| 119 | + cssVariables: cfg.customization.css_variables || {}, |
| 120 | + i18n: cfg.customization.i18n || {}, |
| 121 | + icons: cfg.customization.icons || {}, |
| 122 | + }, |
| 123 | + }, '#app'); |
111 | 124 |
|
112 | | - events.on('authExpired', () => { |
113 | | - localStorage.removeItem(STORAGE_KEY); |
114 | | - window.location.href = loginPath; |
115 | | - }); |
| 125 | + var loginPath = cfg.login_path || '/login.html'; |
116 | 126 |
|
117 | | - events.on('error', (err) => { |
118 | | - console.error('Wippy critical error:', err); |
119 | | - localStorage.removeItem(STORAGE_KEY); |
120 | | - window.location.href = loginPath; |
121 | | - }); |
122 | | - } catch (err) { |
123 | | - console.error('Facade initialization failed:', err); |
124 | | - } |
| 127 | + events.on('authExpired', function () { |
| 128 | + localStorage.removeItem(STORAGE_KEY); |
| 129 | + window.location.href = loginPath; |
| 130 | + }); |
| 131 | + |
| 132 | + events.on('error', function (err) { |
| 133 | + console.error('Wippy critical error:', err); |
| 134 | + localStorage.removeItem(STORAGE_KEY); |
| 135 | + window.location.href = loginPath; |
| 136 | + }); |
| 137 | + } catch (err) { |
| 138 | + console.error('Facade initialization failed:', err); |
| 139 | + } |
| 140 | + })(); |
125 | 141 | </script> |
126 | 142 | </body> |
127 | 143 | </html> |
0 commit comments