OR-1 dataflow CPU sketch

feat: add error panel display and parse error overlay

Orual 4520b798 6f0233b3

+117 -2
+59 -2
dfgraph/frontend/index.html
··· 11 11 #toolbar h1 { font-size: 14px; font-weight: 600; } 12 12 #view-toggle { padding: 6px 12px; background: #5c6bc0; color: #eee; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500; } 13 13 #view-toggle:hover { background: #3f51b5; } 14 - #graph { flex: 1; } 14 + #graph { flex: 1; position: relative; } 15 + #error-panel { 16 + max-height: 200px; 17 + overflow-y: auto; 18 + background: #1a1a2e; 19 + border-top: 2px solid #e53935; 20 + color: #eee; 21 + font-family: monospace; 22 + font-size: 12px; 23 + display: none; 24 + } 25 + #error-panel.visible { display: block; } 26 + #error-panel .error-header { 27 + padding: 6px 16px; 28 + background: #e53935; 29 + color: #fff; 30 + font-weight: 600; 31 + cursor: pointer; 32 + display: flex; 33 + justify-content: space-between; 34 + } 35 + #error-panel .error-list { padding: 0; margin: 0; list-style: none; } 36 + #error-panel .error-item { 37 + padding: 4px 16px; 38 + border-bottom: 1px solid rgba(255,255,255,0.05); 39 + cursor: pointer; 40 + } 41 + #error-panel .error-item:hover { background: rgba(255,255,255,0.05); } 42 + #error-panel .error-line { color: #ff9800; margin-right: 8px; } 43 + #error-panel .error-category { color: #9e9e9e; margin-right: 8px; } 44 + #error-panel .error-suggestion { color: #4caf50; font-style: italic; display: block; padding-left: 16px; } 45 + #parse-error-overlay { 46 + position: absolute; 47 + top: 50%; 48 + left: 50%; 49 + transform: translate(-50%, -50%); 50 + background: rgba(26, 26, 46, 0.95); 51 + padding: 24px 32px; 52 + border-radius: 8px; 53 + border: 2px solid #e53935; 54 + color: #eee; 55 + font-family: monospace; 56 + font-size: 13px; 57 + max-width: 600px; 58 + white-space: pre-wrap; 59 + display: none; 60 + z-index: 10; 61 + } 62 + #parse-error-overlay.visible { display: block; } 15 63 </style> 16 64 </head> 17 65 <body> ··· 19 67 <h1>dfgraph</h1> 20 68 <button id="view-toggle">Physical View</button> 21 69 </div> 22 - <div id="graph"></div> 70 + <div id="graph"> 71 + <div id="parse-error-overlay" aria-live="polite"></div> 72 + </div> 73 + <div id="error-panel"> 74 + <div class="error-header"> 75 + <span id="error-count"></span> 76 + <span>▼</span> 77 + </div> 78 + <ul class="error-list" id="error-list"></ul> 79 + </div> 23 80 <script type="module" src="dist/bundle.js"></script> 24 81 </body> 25 82 </html>
+58
dfgraph/frontend/src/main.ts
··· 153 153 return elements; 154 154 } 155 155 156 + function updateErrorPanel(update: GraphUpdate): void { 157 + const panel = document.getElementById("error-panel")!; 158 + const list = document.getElementById("error-list")!; 159 + const count = document.getElementById("error-count")!; 160 + const overlay = document.getElementById("parse-error-overlay")!; 161 + 162 + // Handle parse error (AC5.5) 163 + if (update.parse_error) { 164 + overlay.textContent = update.parse_error; 165 + overlay.classList.add("visible"); 166 + panel.classList.remove("visible"); 167 + return; 168 + } 169 + overlay.classList.remove("visible"); 170 + 171 + // Handle pipeline errors 172 + if (update.errors.length === 0) { 173 + panel.classList.remove("visible"); 174 + list.innerHTML = ""; 175 + return; 176 + } 177 + 178 + count.textContent = `${update.errors.length} error${update.errors.length > 1 ? "s" : ""}`; 179 + list.innerHTML = ""; 180 + 181 + for (const error of update.errors) { 182 + const li = document.createElement("li"); 183 + li.className = "error-item"; 184 + li.innerHTML = `<span class="error-line">L${error.line}:${error.column}</span>` 185 + + `<span class="error-category">[${error.category}]</span>` 186 + + error.message; 187 + 188 + if (error.suggestions.length > 0) { 189 + for (const suggestion of error.suggestions) { 190 + const span = document.createElement("span"); 191 + span.className = "error-suggestion"; 192 + span.textContent = `→ ${suggestion}`; 193 + li.appendChild(span); 194 + } 195 + } 196 + 197 + li.addEventListener("click", () => { 198 + // Highlight related nodes in the graph 199 + cy.nodes().unselect(); 200 + cy.nodes().forEach((node) => { 201 + if (node.hasClass("error")) { 202 + node.select(); 203 + } 204 + }); 205 + }); 206 + 207 + list.appendChild(li); 208 + } 209 + 210 + panel.classList.add("visible"); 211 + } 212 + 156 213 type ViewMode = "logical" | "physical"; 157 214 let currentView: ViewMode = "logical"; 158 215 let latestUpdate: GraphUpdate | null = null; ··· 189 246 } else { 190 247 renderPhysical(update); 191 248 } 249 + updateErrorPanel(update); 192 250 } 193 251 194 252 function setupToggleButton(): void {