From 60523214c341c566247c50ec41a1ccd059de86a8 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Tue, 15 Sep 2026 13:18:26 -0600 Subject: [PATCH 1/2] Make the probe page usable on narrow and short screens --- apps/probe-viewer/src/App.css | 24 ++++++++++++++++++++++++ apps/probe-viewer/src/index.css | 13 +++++++++++++ 2 files changed, 37 insertions(+) diff --git a/apps/probe-viewer/src/App.css b/apps/probe-viewer/src/App.css index c57495d..8ad1abd 100644 --- a/apps/probe-viewer/src/App.css +++ b/apps/probe-viewer/src/App.css @@ -540,6 +540,7 @@ width: 100%; height: 100%; cursor: grab; + touch-action: none; /* a touch drag pans the probe instead of scrolling the page */ } .viewer-canvas canvas:active { @@ -740,11 +741,34 @@ color: #dc2626; } +/* Too narrow for the sidebar beside the viewer, or too short for the whole + viewer: the document scrolls instead of being locked to the viewport. */ +@media (max-width: 960px), (max-height: 680px) { + .app-shell { + height: auto; + overflow: visible; + } + + .sidebar { + height: auto; + } + + .sidebar-list { + flex: none; + max-height: 40dvh; + } +} + @media (max-width: 960px) { .app-shell { flex-direction: column; } + .viewer-header, + .viewer-header-actions { + flex-wrap: wrap; + } + .app-sidebar { width: 100%; border-right: none; diff --git a/apps/probe-viewer/src/index.css b/apps/probe-viewer/src/index.css index 4d29991..19b22b3 100644 --- a/apps/probe-viewer/src/index.css +++ b/apps/probe-viewer/src/index.css @@ -33,3 +33,16 @@ a { #root { height: 100%; } + +/* When the layout does not fit the viewport (narrow screens stack the sidebar + above the viewer; short screens cannot fit the viewer), the document scrolls. */ +@media (max-width: 960px), (max-height: 680px) { + body { + height: auto; + overflow: auto; + } + + #root { + height: auto; + } +} From d3f137fcfd94ad7f6ca33da341a62a8dd8153f82 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Tue, 15 Sep 2026 13:21:47 -0600 Subject: [PATCH 2/2] Rename probe page classes and components to domain names --- apps/probe-viewer/src/App.css | 211 +++++++++--------- apps/probe-viewer/src/App.tsx | 8 +- ...Canvas.tsx => DoubleSidedProbeDrawing.tsx} | 10 +- .../{ProbeCanvas.tsx => ProbeDrawing.tsx} | 10 +- .../src/components/ProbeIndex.tsx | 4 +- .../{ProbeOverview.tsx => ProbeMinimap.tsx} | 8 +- .../{Sidebar.tsx => ProbeSelectionMenu.tsx} | 56 ++--- .../src/components/ProbeViewer.tsx | 70 +++--- .../src/components/PythonSnippet.tsx | 18 +- .../src/hooks/useProbeViewport.ts | 2 +- apps/probe-viewer/src/index.css | 5 +- 11 files changed, 202 insertions(+), 200 deletions(-) rename apps/probe-viewer/src/components/{DoubleSidedProbeCanvas.tsx => DoubleSidedProbeDrawing.tsx} (96%) rename apps/probe-viewer/src/components/{ProbeCanvas.tsx => ProbeDrawing.tsx} (96%) rename apps/probe-viewer/src/components/{ProbeOverview.tsx => ProbeMinimap.tsx} (98%) rename apps/probe-viewer/src/components/{Sidebar.tsx => ProbeSelectionMenu.tsx} (85%) diff --git a/apps/probe-viewer/src/App.css b/apps/probe-viewer/src/App.css index 8ad1abd..3029cc0 100644 --- a/apps/probe-viewer/src/App.css +++ b/apps/probe-viewer/src/App.css @@ -28,14 +28,14 @@ background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%); } -.app-sidebar { +.probe-selection-menu { width: 320px; border-right: 1px solid var(--line); background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(6px); } -.app-main { +.probe-display { flex: 1; min-width: 0; /* allow the main area to shrink to the frame instead of overflowing */ min-height: 0; @@ -45,7 +45,7 @@ justify-content: center; } -.sidebar { +.probe-selection-menu-content { display: flex; flex-direction: column; gap: 1.25rem; @@ -54,13 +54,13 @@ color: var(--ink); } -.sidebar-header { +.probe-selection-menu-header { display: flex; flex-direction: column; gap: 0.5rem; } -.sidebar-title { +.probe-selection-menu-title { font-size: 1.4rem; margin: 0; font-weight: 700; @@ -68,26 +68,26 @@ color: var(--ink); } -.sidebar-subtitle { +.probe-selection-menu-subtitle { margin: 0; color: var(--ink-2); font-size: 0.95rem; } -.sidebar-control { +.probe-filter { display: flex; flex-direction: column; gap: 0.5rem; } -.sidebar-label { +.probe-filter-label { font-size: 0.85rem; font-weight: 600; color: var(--ink-2); } -.sidebar select, -.sidebar input { +.probe-selection-menu-content select, +.probe-selection-menu-content input { font: inherit; padding: 0.5rem 0.75rem; border-radius: var(--radius); @@ -96,20 +96,20 @@ transition: border-color 0.2s ease, box-shadow 0.2s ease; } -.sidebar select:focus, -.sidebar input:focus { +.probe-selection-menu-content select:focus, +.probe-selection-menu-content input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: none; } -.sidebar select:disabled, -.sidebar input:disabled { +.probe-selection-menu-content select:disabled, +.probe-selection-menu-content input:disabled { opacity: 0.6; cursor: not-allowed; } -.sidebar-list { +.probe-list { flex: 1; min-height: 0; /* let the list shrink to its slot so overflow-y: auto can scroll it */ overflow-y: auto; @@ -119,7 +119,7 @@ padding-right: 0.25rem; } -.sidebar-item { +.probe-list-item { display: flex; flex-direction: column; align-items: flex-start; @@ -133,37 +133,37 @@ transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; } -.sidebar-item:hover { +.probe-list-item:hover { border-color: var(--line-strong); background: var(--accent-soft); } /* Active probe: a graphite left bar (inset shadow, so no layout shift) plus a darker border. Monochrome, distinct from the lighter hover fill. */ -.sidebar-item--active { +.probe-list-item--active { border-color: var(--accent-strong); background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent-strong), var(--shadow-sm); } -.sidebar-item-name { +.probe-list-item-name { font-weight: 600; color: var(--ink); } -.sidebar-item-meta { +.probe-list-item-meta { font-size: 0.8rem; color: var(--ink-2); } /* Neuropixels hierarchy grouping */ -.sidebar-group { +.probe-list-group { display: flex; flex-direction: column; gap: 0.4rem; } -.sidebar-group-header { +.probe-list-group-header { display: flex; align-items: center; gap: 0.5rem; @@ -178,21 +178,21 @@ border-bottom: 1px solid var(--line); } -.sidebar-group-header:hover { +.probe-list-group-header:hover { color: var(--accent-strong); } -.sidebar-group-caret { +.probe-list-group-caret { width: 1rem; color: var(--muted); } -.sidebar-group-title { +.probe-list-group-title { flex: 1; text-align: left; } -.sidebar-group-count { +.probe-list-group-count { font-size: 0.75rem; font-weight: 600; color: var(--ink-2); @@ -201,14 +201,14 @@ padding: 0.05rem 0.5rem; } -.sidebar-subgroup { +.probe-list-subgroup { display: flex; flex-direction: column; gap: 0.35rem; padding-left: 0.6rem; } -.sidebar-subgroup-header { +.probe-list-subgroup-header { display: flex; align-items: center; gap: 0.4rem; @@ -219,11 +219,11 @@ cursor: pointer; } -.sidebar-subgroup-header:hover .sidebar-subgroup-title { +.probe-list-subgroup-header:hover .probe-list-subgroup-title { color: var(--accent-strong); } -.sidebar-subgroup-title { +.probe-list-subgroup-title { flex: 1; text-align: left; font-size: 0.74rem; @@ -233,14 +233,14 @@ color: var(--muted); } -.sidebar-subdivision { +.probe-list-subdivision { display: flex; flex-direction: column; gap: 0.35rem; } /* Static (non-collapsible) length divider inside the Non-human-primate family. */ -.sidebar-subgroup-divider { +.probe-list-subgroup-divider { margin: 0.3rem 0 0.05rem 0.9rem; font-size: 0.68rem; font-weight: 600; @@ -249,20 +249,20 @@ color: var(--muted); } -.sidebar-hint { +.probe-list-hint { font-size: 0.9rem; color: var(--muted); margin: 0.5rem 0; } -.sidebar-error { +.probe-list-error { font-size: 0.9rem; color: #dc2626; font-weight: 600; margin: 0.5rem 0; } -.viewer-panel { +.probe-view { flex: 1; display: flex; flex-direction: column; @@ -276,14 +276,14 @@ box-shadow: var(--shadow); } -.viewer-header { +.probe-view-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; } -.viewer-title { +.probe-view-title { margin: 0; font-size: 1.7rem; font-weight: 700; @@ -294,7 +294,7 @@ /* "JSON" link inline in the subtitle metadata line. It uses the blue link color and a persistent underline (on the word, not the icon) so it clearly reads as a clickable link rather than just emphasized text. */ -.viewer-json-link { +.probe-view-json-link { display: inline-flex; align-items: center; gap: 0.25rem; @@ -305,16 +305,16 @@ transition: color 0.2s ease; } -.viewer-json-link-text { +.probe-view-json-link-text { text-decoration: underline; text-underline-offset: 2px; } -.viewer-json-link:hover { +.probe-view-json-link:hover { color: var(--accent-strong); } -.viewer-json-download { +.probe-view-json-download { display: inline-flex; align-items: center; vertical-align: middle; @@ -325,17 +325,17 @@ transition: color 0.2s ease, background 0.2s ease; } -.viewer-json-download svg { +.probe-view-json-download svg { width: 14px; height: 14px; } -.viewer-json-download:hover { +.probe-view-json-download:hover { color: var(--accent-strong); background: var(--accent-soft); } -.viewer-subtitle { +.probe-view-subtitle { margin: 0.25rem 0 0; color: #475569; font-size: 0.95rem; @@ -343,7 +343,7 @@ /* Map-style overlay clusters floating in the canvas corners, so the probe owns the frame and the chrome sits out of the way until you reach for it. */ -.canvas-controls { +.probe-drawing-controls { position: absolute; top: 0.75rem; z-index: 4; @@ -352,11 +352,11 @@ gap: 0.4rem; } -.canvas-controls--nav { +.probe-drawing-controls--zoom { left: 0.75rem; } -.canvas-controls button { +.probe-drawing-controls button { display: inline-flex; align-items: center; justify-content: center; @@ -373,7 +373,7 @@ transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease; } -.canvas-controls button:hover { +.probe-drawing-controls button:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-soft); @@ -382,14 +382,14 @@ /* Control bands above (framing) and below (display preferences) the canvas, so the canvas itself carries only the zoom overlay and nothing occludes the probe. */ -.viewer-toolbar { +.probe-view-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } -.viewer-toolbar button { +.probe-view-toolbar button { display: inline-flex; align-items: center; justify-content: center; @@ -405,7 +405,7 @@ transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease; } -.viewer-toolbar button:hover { +.probe-view-toolbar button:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-soft); @@ -413,7 +413,7 @@ /* Toggles styled as part of the button family: an outline chip that fills with the accent when on, so they sit alongside the action buttons consistently. */ -.viewer-toggle { +.probe-view-toggle { display: inline-flex; align-items: center; gap: 0.45rem; @@ -428,26 +428,26 @@ transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease; } -.viewer-toggle:hover { +.probe-view-toggle:hover { border-color: var(--accent); color: var(--accent-strong); } -.viewer-toggle:has(input:checked) { +.probe-view-toggle:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong); } /* Keyboard focus ring on the chip, since the real checkbox is visually hidden. */ -.viewer-toggle:has(input:focus-visible) { +.probe-view-toggle:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; } /* The checkbox stays in the DOM (and keyboard-focusable) for accessibility, but is visually hidden: the chip's pressed fill is what conveys on/off. */ -.viewer-toggle input { +.probe-view-toggle input { position: absolute; width: 1px; height: 1px; @@ -460,13 +460,13 @@ } /* Double-sided probe layout controls */ -.viewer-controls-sides { +.probe-view-sides-control { display: flex; align-items: center; gap: 0.6rem; } -.viewer-controls-label { +.probe-view-sides-label { font-size: 0.9rem; font-weight: 600; color: #1e293b; @@ -474,52 +474,52 @@ /* Segmented button group: adjacent buttons read as one control, the active segment highlighted. */ -.viewer-segmented { +.probe-view-segmented { display: inline-flex; gap: 0; } -.viewer-segmented button { +.probe-view-segmented button { border-radius: 0; text-transform: capitalize; } -.viewer-segmented button:first-child { +.probe-view-segmented button:first-child { border-top-left-radius: 0.75rem; border-bottom-left-radius: 0.75rem; } -.viewer-segmented button:last-child { +.probe-view-segmented button:last-child { border-top-right-radius: 0.75rem; border-bottom-right-radius: 0.75rem; } -.viewer-segmented button:not(:first-child) { +.probe-view-segmented button:not(:first-child) { border-left: none; } -.viewer-segmented button.is-active { +.probe-view-segmented button.is-active { background: #2563eb; color: #fff; } /* Color key matching the canvas: gold front, steel-blue back. */ -.viewer-side-swatch { +.probe-view-side-swatch { width: 0.7rem; height: 0.7rem; border-radius: 50%; display: inline-block; } -.viewer-side-swatch--front { +.probe-view-side-swatch--front { background: rgb(212, 175, 55); } -.viewer-side-swatch--back { +.probe-view-side-swatch--back { background: rgb(70, 130, 180); } -.viewer-canvas { +.probe-drawing-window { position: relative; min-height: 360px; border-radius: 1.1rem; @@ -528,7 +528,7 @@ background: #f8f9fa; } -.viewer-canvas-surface { +.probe-drawing-area { width: 100%; height: 100%; display: flex; @@ -536,18 +536,18 @@ justify-content: stretch; } -.viewer-canvas canvas { +.probe-drawing-window canvas { width: 100%; height: 100%; cursor: grab; touch-action: none; /* a touch drag pans the probe instead of scrolling the page */ } -.viewer-canvas canvas:active { +.probe-drawing-window canvas:active { cursor: grabbing; } -.probe-overview { +.probe-minimap { position: absolute; bottom: 12px; right: 12px; @@ -557,11 +557,11 @@ transition: box-shadow 0.2s ease; } -.probe-overview:hover { +.probe-minimap:hover { box-shadow: 0 6px 16px rgba(15, 23, 42, 0.25); } -.viewer-download { +.probe-view-action { display: inline-flex; align-items: center; gap: 0.35rem; @@ -576,26 +576,26 @@ cursor: pointer; } -.viewer-header-actions { +.probe-view-header-actions { display: flex; gap: 0.5rem; align-items: center; } -.viewer-download:hover { +.probe-view-action:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-soft); } -.viewer-snippet { +.probe-view-snippet { position: relative; display: inline-flex; vertical-align: middle; } /* Looks like the JSON link, with a chevron to say it opens something. */ -.viewer-snippet-toggle { +.probe-view-snippet-toggle { display: inline-flex; align-items: center; gap: 0.15rem; @@ -608,11 +608,11 @@ cursor: pointer; } -.viewer-snippet-toggle:hover { +.probe-view-snippet-toggle:hover { color: var(--accent-strong); } -.viewer-snippet-popover { +.probe-view-snippet-popover { position: absolute; top: calc(100% + 0.4rem); left: 0; @@ -628,7 +628,7 @@ } /* Language label on the left, copy action on the right, as in docs code blocks. */ -.viewer-snippet-header { +.probe-view-snippet-header { display: flex; align-items: center; justify-content: space-between; @@ -637,7 +637,7 @@ background: var(--bg); } -.viewer-snippet-language { +.probe-view-snippet-language { font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em; @@ -645,7 +645,7 @@ color: var(--muted); } -.viewer-snippet-copy { +.probe-view-snippet-copy { display: inline-flex; align-items: center; gap: 0.3rem; @@ -659,12 +659,12 @@ cursor: pointer; } -.viewer-snippet-copy:hover { +.probe-view-snippet-copy:hover { color: var(--accent-strong); background: var(--accent-soft); } -.viewer-snippet-code { +.probe-view-snippet-code { display: block; padding: 0.8rem 1rem; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; @@ -676,20 +676,20 @@ } /* Syntax highlighting kept monochrome: the chrome carries no hue (see :root). */ -.viewer-snippet-token--keyword { +.probe-view-snippet-token--keyword { font-weight: 700; color: var(--accent-strong); } -.viewer-snippet-token--function { +.probe-view-snippet-token--function { color: var(--ink); } -.viewer-snippet-token--string { +.probe-view-snippet-token--string { color: var(--muted); } -.viewer-canvas-placeholder { +.probe-drawing-placeholder { height: 360px; border-radius: 1rem; background: repeating-linear-gradient( @@ -708,12 +708,12 @@ padding: 1rem; } -.viewer-issue-link { +.probe-view-issue-link { text-align: right; font-size: 0.85rem; } -.viewer-issue-link a { +.probe-view-issue-link a { color: #6b7280; text-decoration: none; display: inline-flex; @@ -721,12 +721,12 @@ gap: 0.35rem; } -.viewer-issue-link a:hover { +.probe-view-issue-link a:hover { color: var(--accent-strong); text-decoration: underline; } -.viewer-placeholder { +.probe-view-placeholder { flex: 1; display: flex; align-items: center; @@ -737,23 +737,24 @@ box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.2); } -.viewer-placeholder--error { +.probe-view-placeholder--error { color: #dc2626; } -/* Too narrow for the sidebar beside the viewer, or too short for the whole - viewer: the document scrolls instead of being locked to the viewport. */ +/* Too narrow for the probe selection menu beside the probe display, or too short + for the whole probe view: the document scrolls instead of being locked to the + viewport. */ @media (max-width: 960px), (max-height: 680px) { .app-shell { height: auto; overflow: visible; } - .sidebar { + .probe-selection-menu-content { height: auto; } - .sidebar-list { + .probe-list { flex: none; max-height: 40dvh; } @@ -764,18 +765,18 @@ flex-direction: column; } - .viewer-header, - .viewer-header-actions { + .probe-view-header, + .probe-view-header-actions { flex-wrap: wrap; } - .app-sidebar { + .probe-selection-menu { width: 100%; border-right: none; border-bottom: 1px solid rgba(15, 23, 42, 0.08); } - .app-main { + .probe-display { padding: 1rem; } } @@ -968,7 +969,7 @@ color: #64748b; } -.sidebar-home { +.probe-selection-menu-home { display: inline-flex; align-items: center; gap: 0.35rem; @@ -985,7 +986,7 @@ transition: color 0.15s ease; } -.sidebar-home:hover { +.probe-selection-menu-home:hover { color: var(--ink); } @@ -1062,7 +1063,7 @@ } /* Cold /local empty state. */ -.viewer-panel--empty { +.probe-view--empty { align-items: center; justify-content: center; } diff --git a/apps/probe-viewer/src/App.tsx b/apps/probe-viewer/src/App.tsx index 0cb1608..13aabee 100644 --- a/apps/probe-viewer/src/App.tsx +++ b/apps/probe-viewer/src/App.tsx @@ -3,7 +3,7 @@ import { useLocation, useParams } from "react-router-dom"; import { ProbeIndex } from "./components/ProbeIndex"; import { ProbeViewer } from "./components/ProbeViewer"; -import { Sidebar } from "./components/Sidebar"; +import { ProbeSelectionMenu } from "./components/ProbeSelectionMenu"; import { useAppStore } from "./state/useAppStore"; import { useProbeRouteSync } from "./state/useProbeRouteSync"; import { useRestoreCameraFromUrl } from "./state/useRestoreCameraFromUrl"; @@ -43,10 +43,10 @@ function App() { return (
-
diff --git a/apps/probe-viewer/src/components/DoubleSidedProbeCanvas.tsx b/apps/probe-viewer/src/components/DoubleSidedProbeDrawing.tsx similarity index 96% rename from apps/probe-viewer/src/components/DoubleSidedProbeCanvas.tsx rename to apps/probe-viewer/src/components/DoubleSidedProbeDrawing.tsx index 0464daa..b6341a1 100644 --- a/apps/probe-viewer/src/components/DoubleSidedProbeCanvas.tsx +++ b/apps/probe-viewer/src/components/DoubleSidedProbeDrawing.tsx @@ -12,7 +12,7 @@ import { } from "../geometry/draw"; import type { ManifestEntry, ProbeInterfaceFile, ProbeViewerCamera } from "../types/probe"; -interface DoubleSidedProbeCanvasProps { +interface DoubleSidedProbeDrawingProps { entry: ManifestEntry; probeData: ProbeInterfaceFile; camera: ProbeViewerCamera; @@ -27,7 +27,7 @@ function colorForSide(side: string | undefined) { return side === "back" ? CONTACT_COLORS.back : CONTACT_COLORS.front; } -export function DoubleSidedProbeCanvas({ +export function DoubleSidedProbeDrawing({ entry, probeData, camera, @@ -35,7 +35,7 @@ export function DoubleSidedProbeCanvas({ overlaySide, onViewCenterChange, onZoom, -}: DoubleSidedProbeCanvasProps) { +}: DoubleSidedProbeDrawingProps) { const { zoom, centerX, centerY } = camera; const { ref: containerRef, size } = useResizeObserver(); const lastCanvasSizeRef = useRef({ w: 0, h: 0, dpr: 0 }); @@ -146,7 +146,7 @@ export function DoubleSidedProbeCanvas({ }, [canvasRef, entry.id, geometry, getProjection, labelInfo, overlaySide, probe, showScaleBar, size.height, size.width, zoom, centerX, centerY]); return ( -
+
{geometry && probe ? ( ) : ( -
+

No planar geometry available for this probe.

)} diff --git a/apps/probe-viewer/src/components/ProbeCanvas.tsx b/apps/probe-viewer/src/components/ProbeDrawing.tsx similarity index 96% rename from apps/probe-viewer/src/components/ProbeCanvas.tsx rename to apps/probe-viewer/src/components/ProbeDrawing.tsx index 159c213..dc3ddec 100644 --- a/apps/probe-viewer/src/components/ProbeCanvas.tsx +++ b/apps/probe-viewer/src/components/ProbeDrawing.tsx @@ -16,7 +16,7 @@ import type { ProbeViewerCamera, } from "../types/probe"; -interface ProbeCanvasProps { +interface ProbeDrawingProps { entry: ManifestEntry; probeData: ProbeInterfaceFile; camera: ProbeViewerCamera; @@ -27,7 +27,7 @@ interface ProbeCanvasProps { onZoom: (zoom: number) => void; } -export function ProbeCanvas({ +export function ProbeDrawing({ entry, probeData, camera, @@ -36,7 +36,7 @@ export function ProbeCanvas({ showScaleBar, onViewCenterChange, onZoom, -}: ProbeCanvasProps) { +}: ProbeDrawingProps) { const { zoom, centerX, centerY } = camera; const { ref: containerRef, size } = useResizeObserver(); // Track the last applied canvas backing-store size so we only reallocate (an @@ -178,7 +178,7 @@ export function ProbeCanvas({ ]); return ( -
+
{geometry && probe ? ( ) : ( -
+

No planar geometry available for this probe.

)} diff --git a/apps/probe-viewer/src/components/ProbeIndex.tsx b/apps/probe-viewer/src/components/ProbeIndex.tsx index 2ec00f2..94f7a0f 100644 --- a/apps/probe-viewer/src/components/ProbeIndex.tsx +++ b/apps/probe-viewer/src/components/ProbeIndex.tsx @@ -39,7 +39,7 @@ function ManufacturerMedia({ groupKey, label }: { groupKey: string; label: strin ); } -// Mirrors the Sidebar's friendly names. +// Mirrors the ProbeSelectionMenu's friendly names. const MANUFACTURER_DISPLAY_NAMES: Record = { cambridgeneurotech: "Cambridge NeuroTech", diagnosticbiochips: "Diagnostic Biochips", @@ -73,7 +73,7 @@ const UploadIcon = ( ); // Landing page: one card per manufacturer. Selecting a card enters the existing -// probe view (sidebar + viewer) on that manufacturer's first probe. +// probe page (probe selection menu + probe display) on that manufacturer's first probe. export function ProbeIndex() { const manifest = useAppStore((state) => state.manifest); const manifestStatus = useAppStore((state) => state.manifestStatus); diff --git a/apps/probe-viewer/src/components/ProbeOverview.tsx b/apps/probe-viewer/src/components/ProbeMinimap.tsx similarity index 98% rename from apps/probe-viewer/src/components/ProbeOverview.tsx rename to apps/probe-viewer/src/components/ProbeMinimap.tsx index d83a86b..4fdab1c 100644 --- a/apps/probe-viewer/src/components/ProbeOverview.tsx +++ b/apps/probe-viewer/src/components/ProbeMinimap.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useMemo } from "react"; import type { ProbeInterfaceFile, ProbeViewerCamera } from "../types/probe"; -interface ProbeOverviewProps { +interface ProbeMinimapProps { probeData: ProbeInterfaceFile; camera: ProbeViewerCamera; /** Main canvas dimensions */ @@ -49,13 +49,13 @@ function computeGeometrySummary(probeData: ProbeInterfaceFile): GeometrySummary return { minX, maxX, minY, maxY, width, height, centerX, centerY }; } -export function ProbeOverview({ +export function ProbeMinimap({ probeData, camera, mainWidth, mainHeight, onViewCenterChange, -}: ProbeOverviewProps) { +}: ProbeMinimapProps) { const { zoom, centerX, centerY } = camera; const canvasRef = useRef(null); const geometry = useMemo(() => computeGeometrySummary(probeData), [probeData]); @@ -234,7 +234,7 @@ export function ProbeOverview({ return ( diff --git a/apps/probe-viewer/src/components/Sidebar.tsx b/apps/probe-viewer/src/components/ProbeSelectionMenu.tsx similarity index 85% rename from apps/probe-viewer/src/components/Sidebar.tsx rename to apps/probe-viewer/src/components/ProbeSelectionMenu.tsx index 9afac9d..b8c25db 100644 --- a/apps/probe-viewer/src/components/Sidebar.tsx +++ b/apps/probe-viewer/src/components/ProbeSelectionMenu.tsx @@ -16,7 +16,7 @@ const MANUFACTURER_DISPLAY_NAMES: Record = { "sinaps-research-platform": "SINAPS", }; -export function Sidebar() { +export function ProbeSelectionMenu() { const manifest = useAppStore((state) => state.manifest); const manifestStatus = useAppStore((state) => state.manifestStatus); const selectedManufacturer = useAppStore((state) => state.selectedManufacturer); @@ -135,13 +135,13 @@ export function Sidebar() { type="button" className={ entry.id === selectedProbeId - ? "sidebar-item sidebar-item--active" - : "sidebar-item" + ? "probe-list-item probe-list-item--active" + : "probe-list-item" } onClick={() => selectProbe(entry.id)} > - {entry.displayName} - + {entry.displayName} + {entry.contactCount} contacts · {entry.shankCount} shanks {multiSideManufacturers.has(entry.manufacturer) && ` · ${entry.numSides} ${entry.numSides === 1 ? "side" : "sides"}`} @@ -158,33 +158,33 @@ export function Sidebar() { const open = !node.collapsible || isSearching || expanded.has(key); const wrapClass = depth === 0 - ? "sidebar-group" + ? "probe-list-group" : depth === 1 - ? "sidebar-subgroup" - : "sidebar-subdivision"; + ? "probe-list-subgroup" + : "probe-list-subdivision"; return (
{node.collapsible ? ( ) : ( -

{node.label}

+

{node.label}

)} {open && (node.children @@ -195,11 +195,11 @@ export function Sidebar() { }; return ( -
-
+
+
-

Probe Catalog

-

+

Probe Catalog

+

Browse available probe layouts and inspect their geometry.

-
-