Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 124 additions & 99 deletions apps/probe-viewer/src/App.css

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions apps/probe-viewer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -43,10 +43,10 @@ function App() {

return (
<div className="app-shell">
<aside className="app-sidebar">
<Sidebar />
<aside className="probe-selection-menu">
<ProbeSelectionMenu />
</aside>
<main className="app-main">
<main className="probe-display">
<ProbeViewer />
</main>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,15 +27,15 @@ function colorForSide(side: string | undefined) {
return side === "back" ? CONTACT_COLORS.back : CONTACT_COLORS.front;
}

export function DoubleSidedProbeCanvas({
export function DoubleSidedProbeDrawing({
entry,
probeData,
camera,
showScaleBar,
overlaySide,
onViewCenterChange,
onZoom,
}: DoubleSidedProbeCanvasProps) {
}: DoubleSidedProbeDrawingProps) {
const { zoom, centerX, centerY } = camera;
const { ref: containerRef, size } = useResizeObserver<HTMLDivElement>();
const lastCanvasSizeRef = useRef({ w: 0, h: 0, dpr: 0 });
Expand Down Expand Up @@ -146,7 +146,7 @@ export function DoubleSidedProbeCanvas({
}, [canvasRef, entry.id, geometry, getProjection, labelInfo, overlaySide, probe, showScaleBar, size.height, size.width, zoom, centerX, centerY]);

return (
<div ref={containerRef} className="viewer-canvas-surface">
<div ref={containerRef} className="probe-drawing-area">
{geometry && probe ? (
<canvas
ref={canvasRef}
Expand All @@ -162,7 +162,7 @@ export function DoubleSidedProbeCanvas({
onDoubleClick={handleDoubleClick}
/>
) : (
<div className="viewer-placeholder">
<div className="probe-view-placeholder">
<p>No planar geometry available for this probe.</p>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
ProbeViewerCamera,
} from "../types/probe";

interface ProbeCanvasProps {
interface ProbeDrawingProps {
entry: ManifestEntry;
probeData: ProbeInterfaceFile;
camera: ProbeViewerCamera;
Expand All @@ -27,7 +27,7 @@ interface ProbeCanvasProps {
onZoom: (zoom: number) => void;
}

export function ProbeCanvas({
export function ProbeDrawing({
entry,
probeData,
camera,
Expand All @@ -36,7 +36,7 @@ export function ProbeCanvas({
showScaleBar,
onViewCenterChange,
onZoom,
}: ProbeCanvasProps) {
}: ProbeDrawingProps) {
const { zoom, centerX, centerY } = camera;
const { ref: containerRef, size } = useResizeObserver<HTMLDivElement>();
// Track the last applied canvas backing-store size so we only reallocate (an
Expand Down Expand Up @@ -178,7 +178,7 @@ export function ProbeCanvas({
]);

return (
<div ref={containerRef} className="viewer-canvas-surface">
<div ref={containerRef} className="probe-drawing-area">
{geometry && probe ? (
<canvas
ref={canvasRef}
Expand All @@ -197,7 +197,7 @@ export function ProbeCanvas({
onDoubleClick={handleDoubleClick}
/>
) : (
<div className="viewer-placeholder">
<div className="probe-view-placeholder">
<p>No planar geometry available for this probe.</p>
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions apps/probe-viewer/src/components/ProbeIndex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string> = {
cambridgeneurotech: "Cambridge NeuroTech",
diagnosticbiochips: "Diagnostic Biochips",
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 */
Expand Down Expand Up @@ -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<HTMLCanvasElement | null>(null);
const geometry = useMemo(() => computeGeometrySummary(probeData), [probeData]);
Expand Down Expand Up @@ -234,7 +234,7 @@ export function ProbeOverview({
return (
<canvas
ref={canvasRef}
className="probe-overview"
className="probe-minimap"
onClick={handleClick}
title="Click to navigate"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const MANUFACTURER_DISPLAY_NAMES: Record<string, string> = {
"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);
Expand Down Expand Up @@ -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)}
>
<span className="sidebar-item-name">{entry.displayName}</span>
<span className="sidebar-item-meta">
<span className="probe-list-item-name">{entry.displayName}</span>
<span className="probe-list-item-meta">
{entry.contactCount} contacts · {entry.shankCount} shanks
{multiSideManufacturers.has(entry.manufacturer) &&
` · ${entry.numSides} ${entry.numSides === 1 ? "side" : "sides"}`}
Expand All @@ -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 (
<div className={wrapClass} key={key}>
{node.collapsible ? (
<button
type="button"
className={
depth === 0 ? "sidebar-group-header" : "sidebar-subgroup-header"
depth === 0 ? "probe-list-group-header" : "probe-list-subgroup-header"
}
aria-expanded={open}
onClick={() => toggle(key)}
>
<span className="sidebar-group-caret">{open ? "▾" : "▸"}</span>
<span className="probe-list-group-caret">{open ? "▾" : "▸"}</span>
<span
className={
depth === 0 ? "sidebar-group-title" : "sidebar-subgroup-title"
depth === 0 ? "probe-list-group-title" : "probe-list-subgroup-title"
}
>
{node.label}
</span>
<span className="sidebar-group-count">{node.count}</span>
<span className="probe-list-group-count">{node.count}</span>
</button>
) : (
<p className="sidebar-subgroup-divider">{node.label}</p>
<p className="probe-list-subgroup-divider">{node.label}</p>
)}
{open &&
(node.children
Expand All @@ -195,11 +195,11 @@ export function Sidebar() {
};

return (
<div className="sidebar">
<header className="sidebar-header">
<div className="probe-selection-menu-content">
<header className="probe-selection-menu-header">
<button
type="button"
className="sidebar-home"
className="probe-selection-menu-home"
onClick={() => {
selectProbe(undefined);
clearLocalProbe();
Expand All @@ -223,14 +223,14 @@ export function Sidebar() {
</svg>
Home
</button>
<h1 className="sidebar-title">Probe Catalog</h1>
<p className="sidebar-subtitle">
<h1 className="probe-selection-menu-title">Probe Catalog</h1>
<p className="probe-selection-menu-subtitle">
Browse available probe layouts and inspect their geometry.
</p>
</header>

<div className="sidebar-control">
<label className="sidebar-label" htmlFor="manufacturer-select">
<div className="probe-filter">
<label className="probe-filter-label" htmlFor="manufacturer-select">
Manufacturer
</label>
<select
Expand All @@ -247,8 +247,8 @@ export function Sidebar() {
</select>
</div>

<div className="sidebar-control">
<label className="sidebar-label" htmlFor="probe-search">
<div className="probe-filter">
<label className="probe-filter-label" htmlFor="probe-search">
Search by model
</label>
<input
Expand All @@ -262,8 +262,8 @@ export function Sidebar() {
</div>

{showSideFilter && (
<div className="sidebar-control">
<label className="sidebar-label" htmlFor="side-select">
<div className="probe-filter">
<label className="probe-filter-label" htmlFor="side-select">
Number of sides
</label>
<select
Expand All @@ -286,15 +286,15 @@ export function Sidebar() {
</div>
)}

<div className="sidebar-list" role="list">
<div className="probe-list" role="list">
{manifestStatus === "loading" && (
<p className="sidebar-hint">Loading manifest…</p>
<p className="probe-list-hint">Loading manifest…</p>
)}
{manifestStatus === "error" && (
<p className="sidebar-error">Failed to load manifest.</p>
<p className="probe-list-error">Failed to load manifest.</p>
)}
{manifestStatus === "success" && filteredEntries.length === 0 && (
<p className="sidebar-hint">No probes match the current filters.</p>
<p className="probe-list-hint">No probes match the current filters.</p>
)}

{manifestStatus === "success" &&
Expand Down
Loading
Loading