|
174 | 174 | #mock-args-fields input { flex: 1; min-width: 0; } |
175 | 175 | #mock-footer { display: flex; justify-content: flex-end; gap: 8px; } |
176 | 176 |
|
| 177 | + /* Error modal */ |
| 178 | + #error-overlay { |
| 179 | + position: fixed; inset: 0; background: rgba(0,0,0,0.55); |
| 180 | + display: flex; align-items: center; justify-content: center; z-index: 200; |
| 181 | + } |
| 182 | + #error-overlay.hidden { display: none; } |
| 183 | + #error-modal { |
| 184 | + background: var(--modal-bg); border: 1px solid var(--border); border-radius: 6px; |
| 185 | + padding: 20px; min-width: 420px; max-width: 640px; |
| 186 | + display: flex; flex-direction: column; gap: 14px; |
| 187 | + box-shadow: 0 8px 28px rgba(0,0,0,0.45); |
| 188 | + } |
| 189 | + #error-modal h3 { |
| 190 | + font-size: 14px; font-weight: 600; color: #f48771; |
| 191 | + display: flex; align-items: center; gap: 8px; |
| 192 | + } |
| 193 | + #error-modal-msg { |
| 194 | + font-size: 12px; line-height: 1.5; color: var(--fg); |
| 195 | + white-space: pre-wrap; word-break: break-word; |
| 196 | + max-height: 40vh; overflow-y: auto; |
| 197 | + } |
| 198 | + #error-footer { display: flex; justify-content: flex-end; } |
| 199 | + |
177 | 200 | /* Dockview tab styling — VS Code style */ |
178 | 201 | #dockview-container .dv-tabs-and-actions-container { |
179 | 202 | background-color: var(--panel-bg) !important; |
|
208 | 231 | <div id="toolbar"> |
209 | 232 | <button class="icon-btn" id="btn-pause-continue" title="Continue"><i class="codicon codicon-debug-continue" style="color:var(--icon-green)"></i></button> |
210 | 233 | <button class="icon-btn" id="btn-reset" title="Reset"><i class="codicon codicon-debug-restart"></i></button> |
211 | | - <button class="icon-btn" id="btn-choicepoint-filter" title="Configure choicepoint filter"><span class="svg-icon" style="mask-image:url(/icons/settings-gear.svg);-webkit-mask-image:url(/icons/settings-gear.svg)"></span></button> |
| 234 | + <button class="icon-btn" id="btn-choicepoint-filter" title="Configure choicepoint filter"><span class="svg-icon" style="mask-image:url(/icons/settings-choicepoint.svg);-webkit-mask-image:url(/icons/settings-choicepoint.svg)"></span></button> |
212 | 235 | <button class="icon-btn" id="btn-rewind-choicepoint" title="Rewind to first choicepoint"><span class="svg-icon" style="mask-image:url(/icons/debug-rewind-back-choicepoint.svg);-webkit-mask-image:url(/icons/debug-rewind-back-choicepoint.svg)"></span></button> |
213 | 236 | <button class="icon-btn" id="btn-step-back-choicepoint" title="Step back to choicepoint"><span class="svg-icon" style="mask-image:url(/icons/debug-step-back-choicepoint.svg);-webkit-mask-image:url(/icons/debug-step-back-choicepoint.svg)"></span></button> |
214 | 237 | <button class="icon-btn" id="btn-step-back" title="Step Back"><i class="codicon codicon-debug-step-back"></i></button> |
|
220 | 243 | </div> |
221 | 244 | <div id="mocks-bar"></div> |
222 | 245 | <div id="dockview-container" class="dockview-theme-abyss"></div> |
| 246 | +<div id="error-overlay" class="hidden"> |
| 247 | + <div id="error-modal"> |
| 248 | + <h3><i class="codicon codicon-error"></i> Error</h3> |
| 249 | + <div id="error-modal-msg"></div> |
| 250 | + <div id="error-footer"> |
| 251 | + <button id="btn-error-ok">OK</button> |
| 252 | + </div> |
| 253 | + </div> |
| 254 | +</div> |
223 | 255 | <div id="mock-overlay" class="hidden"> |
224 | 256 | <div id="mock-modal"> |
225 | 257 | <h3>Add Mock</h3> |
@@ -323,7 +355,25 @@ <h3>Add Mock</h3> |
323 | 355 | applyTheme(currentTheme === 'dark' ? 'light' : 'dark'); |
324 | 356 | }); |
325 | 357 |
|
326 | | -function setStatus(msg) { console.log('[status]', msg); } |
| 358 | +function showError(msg) { |
| 359 | + document.getElementById('error-modal-msg').textContent = msg; |
| 360 | + document.getElementById('error-overlay').classList.remove('hidden'); |
| 361 | +} |
| 362 | +function hideError() { |
| 363 | + document.getElementById('error-overlay').classList.add('hidden'); |
| 364 | +} |
| 365 | +document.getElementById('btn-error-ok').addEventListener('click', hideError); |
| 366 | +document.getElementById('error-overlay').addEventListener('click', e => { |
| 367 | + if (e.target.id === 'error-overlay') hideError(); |
| 368 | +}); |
| 369 | +document.addEventListener('keydown', e => { |
| 370 | + if (e.key === 'Escape' && !document.getElementById('error-overlay').classList.contains('hidden')) hideError(); |
| 371 | +}); |
| 372 | + |
| 373 | +function setStatus(msg) { |
| 374 | + console.log('[status]', msg); |
| 375 | + if (msg.startsWith('Error') || msg.startsWith('Invalid')) showError(msg); |
| 376 | +} |
327 | 377 |
|
328 | 378 | function updateButtons() { |
329 | 379 | ['btn-step', 'btn-step-back', 'btn-step-back-choicepoint', 'btn-rewind-choicepoint', 'btn-reset', 'btn-mock', 'btn-predict'].forEach(id => { |
@@ -906,7 +956,7 @@ <h3>Add Mock</h3> |
906 | 956 | setStatus('Running…'); |
907 | 957 | try { |
908 | 958 | const r = await fetch(url, { method: 'POST' }); |
909 | | - if (!r.ok) throw new Error('HTTP ' + r.status); |
| 959 | + if (!r.ok) throw new Error(await r.text() || ('HTTP ' + r.status)); |
910 | 960 | const graph = await r.json(); |
911 | 961 | renderGraph(graph); |
912 | 962 | const [source] = await Promise.all([fetchSource(), fetchMocks()]); |
@@ -982,7 +1032,7 @@ <h3>Add Mock</h3> |
982 | 1032 | setStatus('Running…'); |
983 | 1033 | try { |
984 | 1034 | const r = await fetch(url, { method: 'POST' }); |
985 | | - if (!r.ok) throw new Error(`No nodes matching "${choicePointFilterRegex}" could be found in the past!`); |
| 1035 | + if (!r.ok) throw new Error(await r.text() || ('HTTP ' + r.status)); |
986 | 1036 | const graph = await r.json(); |
987 | 1037 | renderGraph(graph); |
988 | 1038 | const [source] = await Promise.all([fetchSource(), fetchMocks()]); |
|
0 commit comments