diff --git a/docs/SCHEMATIC_DIFF.md b/docs/SCHEMATIC_DIFF.md index f0427cf..8a3e56f 100644 --- a/docs/SCHEMATIC_DIFF.md +++ b/docs/SCHEMATIC_DIFF.md @@ -85,3 +85,5 @@ API_KEY="$API_KEY" .venv/bin/python scripts/compare-smoke.py original.SchDoc mod Tests cover record reordering, component UID matching/renumbering, values, pure movement, combined movement/property edits, additions/removals, reversed wire vertices, duplicate objects, labels/net-name scope, embedded raster replacement, nonmutation, original SVG geometry, clip-ID separation, upload/authentication/CORS, per-file/combined limits, partial storage failures, a real OLE same-file comparison, private result/SVG access, failed processing and expiry cleanup. Frontend camera adapter tests cover pane offsets, synchronized world cameras, pinch, change focus and detached listeners. Synthetic unit circuits never appear as application data. The actual local `Sheet1-before.SchDoc` / `Sheet1-after.SchDoc` pair (205/206 source records) produced 1 moved component, 2 modified wires and 1 added junction. The 1,942-record CPU sheet compared with itself produced zero changes. These are structural integration checks, not a trusted Altium visual comparison or browser UI acceptance test. No proprietary samples are redistributed. Browser interaction and visual validation require a connected browser; absence of browser QA must be reported rather than replaced by camera unit tests. + +Implementation verification: 115 Python tests and 6 frontend tests passed with the native fixtures enabled. The Docker image built successfully, the Caddy configuration adapted successfully, and real HTTP comparison plus single-file smoke checks passed against PostgreSQL with two worker replicas. The comparison result and SVG routes enforced private read tokens. A connected browser was unavailable, so interactive workspace appearance and browser behavior are not claimed as verified. diff --git a/frontend/compare.css b/frontend/compare.css index a1139ac..5db7ce6 100644 --- a/frontend/compare.css +++ b/frontend/compare.css @@ -1,6 +1,6 @@ body { display: flex; flex-direction: column; font-family: system-ui,sans-serif; color: #233431; background: #f4f6f5; } * { box-sizing: border-box; } -header { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; padding: 12px 16px; border-bottom: 1px solid #d6dfda; } +header { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; min-height: 60px; padding: 12px 16px 12px 60px; border-bottom: 1px solid #d6dfda; } header > a { color: #244d49; font-size: 15px; font-weight: 650; text-decoration: none; } #summary, .controls { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; font-size: 12px; } .controls { margin-left: auto; } @@ -8,7 +8,11 @@ label { display: inline-flex; gap: 4px; align-items: center; } button, select { border: 1px solid #cbd5ce; border-radius: 4px; padding: 5px 9px; background: white; color: inherit; font: inherit; cursor: pointer; } button:focus-visible, select:focus-visible, .surface:focus-visible { outline: 2px solid #2676c5; outline-offset: -2px; } #scope { margin: 0; padding: 7px 16px; font-size: 11px; color: #60716b; border-bottom: 1px solid #d6dfda; } -.workspace { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr) 270px; } +.workspace { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); grid-template-rows: minmax(0,1fr); } +.menu-open .workspace { grid-template-columns: minmax(0,1fr) minmax(0,1fr) 270px; } +body:not(.menu-open) header, body:not(.menu-open) #scope, body:not(.menu-open) aside, body:not(.menu-open) .pane h2 { display: none; } +#menu-toggle { position: fixed; top: 12px; left: 12px; z-index: 10; width: 36px; height: 36px; display: grid; place-items: center; padding: 6px; box-shadow: 0 2px 6px #00000014; } +#menu-toggle[aria-expanded="true"] { background: #e5eee9; } .pane { min-width: 0; min-height: 0; display: flex; flex-direction: column; border-right: 1px solid #d6dfda; } h2 { margin: 0; padding: 9px 12px; font-size: 12px; font-weight: 650; background: #edf1ee; display: flex; gap: 10px; } h2 span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: normal; color: #61746b; } @@ -28,7 +32,7 @@ aside { display: flex; flex-direction: column; min-height: 0; overflow: hidden; [data-diff-hidden] { display: none; } .no-highlights [data-diff-box] { display: none; } [data-diff-box][data-selected] { stroke-width: 3; fill-opacity: .18; } -@media (max-width: 900px) { .workspace { grid-template-columns: minmax(0,1fr) minmax(0,1fr); grid-template-rows: minmax(0,1fr) 180px; } aside { grid-column: 1/-1; } #details { max-height: 40%; } } -@media (max-width: 550px) { .workspace { grid-template-columns: 1fr; grid-template-rows: minmax(0,1fr) minmax(0,1fr) 140px; } aside { grid-column: auto; } header { gap: 8px; }.controls { margin-left: 0; } } +@media (max-width: 900px) { .menu-open .workspace { grid-template-columns: minmax(0,1fr) minmax(0,1fr); grid-template-rows: minmax(0,1fr) 180px; } aside { grid-column: 1/-1; } #details { max-height: 40%; } } +@media (max-width: 550px) { .workspace { grid-template-columns: 1fr; grid-template-rows: minmax(0,1fr) minmax(0,1fr); } .menu-open .workspace { grid-template-columns: 1fr; grid-template-rows: minmax(0,1fr) minmax(0,1fr) 140px; } aside { grid-column: auto; } header { gap: 8px; }.controls { margin-left: 0; } } #changes button[hidden] { display: none; } diff --git a/frontend/compare.html b/frontend/compare.html index e790ade..50ac205 100644 --- a/frontend/compare.html +++ b/frontend/compare.html @@ -1,6 +1,7 @@ Schematic comparison -
Schematic Diff
+ +
Schematic Diff

Native object comparison · Connection graphics, not a compiled electrical netlist.

-

Original

Modified

+

Original

Modified

Loading comparison… diff --git a/frontend/compare.js b/frontend/compare.js index c9d8d2f..9f64476 100644 --- a/frontend/compare.js +++ b/frontend/compare.js @@ -1,4 +1,17 @@ import { bindCamera } from './interactions.js'; +const menuToggle=document.querySelector('#menu-toggle'); +function setMenu(open){ + document.body.classList.toggle('menu-open',open); + menuToggle.setAttribute('aria-expanded',String(open)); + const label=open?'Hide menu':'Show menu'; + menuToggle.setAttribute('aria-label',label);menuToggle.title=label; +} +menuToggle.addEventListener('click',()=>setMenu(!document.body.classList.contains('menu-open'))); +addEventListener('keydown',event=>{ + if(event.key==='Escape'&&document.body.classList.contains('menu-open')){ + setMenu(false);menuToggle.focus(); + } +}); const state=document.querySelector('#state'),id=location.pathname.split('/').pop(); const token=new URLSearchParams(location.hash.slice(1)).get('token')||sessionStorage.getItem(`comparison:${id}`); if(token)sessionStorage.setItem(`comparison:${id}`,token);