Add views, import/duplicate, DRC, jumps toggle, multi-wire select, auto-route, delete view tabs

- Roadmap #22: File→Import JSON to restore a diagram (drag-drop or file picker)
- Roadmap #23: Duplicate diagram from diagram list
- Roadmap #24: DRC panel with collapsible categories (unconnected pins, dup refs, no length, no P/N)
- Multi-view tabs: named views per diagram with independent device/wire layouts; snapshot-on-entry prevents bleed
- Keyboard arrow nudge for selected device(s)
- Undo for move, duplicate, label/property changes
- Wire jump arcs toggle (⌒ Jumps button in toolbar)
- Shift+click multi-wire selection; right-click bulk clear waypoints or auto-route
- Auto-route: obstacle-avoiding ortho routing for single or bulk selected wires
- Delete view tab with × button; right-click context menu for rename/delete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 19:18:39 -04:00
parent f43cd3409c
commit e4a80dc0e6
11 changed files with 823 additions and 25 deletions
+11
View File
@@ -16,6 +16,17 @@ const api = {
get: (id) => apiFetch(`/diagrams/${id}`),
update: (id, data) => apiFetch(`/diagrams/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/diagrams/${id}`, { method: "DELETE" }),
duplicate: (id) => apiFetch(`/diagrams/${id}/duplicate`, { method: "POST" }),
import: (data) => apiFetch("/diagrams/import", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
},
views: {
listForDiagram: (diagramId) => apiFetch(`/views/diagram/${diagramId}`),
create: (data) => apiFetch("/views/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/views/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/views/${id}`, { method: "DELETE" }),
getLayout: (id) => apiFetch(`/views/${id}/layout`),
updateDevicePos: (viewId, deviceId, data) => apiFetch(`/views/${viewId}/devices/${deviceId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
updateWireWaypoints: (viewId, wireId, data) => apiFetch(`/views/${viewId}/wires/${wireId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
},
devices: {
create: (data) => apiFetch("/devices/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),