|
41 | 41 | <div class="facade-loader__text">Loading...</div> |
42 | 42 | </div> |
43 | 43 | </div> |
44 | | -<script> |
45 | | - (async function () { |
46 | | - var STORAGE_KEY = '@wippy_token_info'; |
47 | | - var appEl = document.getElementById('app'); |
| 44 | +<script type="module"> |
| 45 | + const STORAGE_KEY = '@wippy_token_info'; |
| 46 | + const appEl = document.getElementById('app'); |
48 | 47 |
|
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>'; |
56 | | - } |
57 | | - |
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(); |
| 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 | + } |
65 | 56 |
|
66 | | - var stored = localStorage.getItem(STORAGE_KEY); |
67 | | - if (!stored) { window.location.href = cfg.login_path || '/login.html'; throw new Error('no token'); } |
| 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); |
| 62 | + } |
| 63 | + const cfg = await res.json(); |
68 | 64 |
|
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'); } |
| 65 | + const stored = localStorage.getItem(STORAGE_KEY); |
| 66 | + if (!stored) { window.location.href = cfg.login_path || '/login.html'; throw new Error('no token'); } |
72 | 67 |
|
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); |
| 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'); } |
76 | 71 |
|
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 | | - } |
| 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); |
90 | 75 |
|
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 | | - } |
| 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 | + } |
98 | 83 |
|
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 | | - } |
| 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 | + } |
103 | 88 |
|
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'); |
| 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'); |
124 | 109 |
|
125 | | - var loginPath = cfg.login_path || '/login.html'; |
| 110 | + const loginPath = cfg.login_path || '/login.html'; |
126 | 111 |
|
127 | | - events.on('authExpired', function () { |
128 | | - localStorage.removeItem(STORAGE_KEY); |
129 | | - window.location.href = loginPath; |
130 | | - }); |
| 112 | + events.on('authExpired', () => { |
| 113 | + localStorage.removeItem(STORAGE_KEY); |
| 114 | + window.location.href = loginPath; |
| 115 | + }); |
131 | 116 |
|
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 | | - })(); |
| 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 | + } |
141 | 125 | </script> |
142 | 126 | </body> |
143 | 127 | </html> |
0 commit comments