diff --git a/frontend/e2e/__screenshots__/darwin/view-controls.png b/frontend/e2e/__screenshots__/darwin/view-controls.png index e1c16db..59d9571 100644 Binary files a/frontend/e2e/__screenshots__/darwin/view-controls.png and b/frontend/e2e/__screenshots__/darwin/view-controls.png differ diff --git a/frontend/e2e/viewer.spec.ts b/frontend/e2e/viewer.spec.ts index 54d8418..192c5a8 100644 --- a/frontend/e2e/viewer.spec.ts +++ b/frontend/e2e/viewer.spec.ts @@ -13,6 +13,12 @@ const pqInputFixture = path.join(repositoryRoot, "examples/periodic-boundary.in" const crossingFixture = path.join(repositoryRoot, "examples/periodic-crossing.extxyz"); const acofFixture = path.join(repositoryRoot, "examples/acof-triclinic.xyz"); const waterFixture = path.join(repositoryRoot, "examples/water.xyz"); +const naclFixture = path.join( + repositoryRoot, + "docs/assets/sources/nacl.extxyz", +); +const polyhedraRequirement = + "Requires a supported center with 3+ bonded ligands"; test.afterEach(async ({ request }) => { const response = await request.post("/api/open", { @@ -78,6 +84,21 @@ function changedPixelCount(bytes: Buffer): number { return changed; } +function differentPixelCount(leftBytes: Buffer, rightBytes: Buffer): number { + const left = PNG.sync.read(leftBytes); + const right = PNG.sync.read(rightBytes); + expect([right.width, right.height]).toEqual([left.width, left.height]); + let changed = 0; + for (let index = 0; index < left.data.length; index += 4) { + const difference = Math.abs(left.data[index] - right.data[index]) + + Math.abs(left.data[index + 1] - right.data[index + 1]) + + Math.abs(left.data[index + 2] - right.data[index + 2]) + + Math.abs(left.data[index + 3] - right.data[index + 3]); + if (difference > 24) changed += 1; + } + return changed; +} + function littleEndianTiffTag(bytes: Buffer, tag: number): { type: number; count: number; @@ -98,7 +119,9 @@ function littleEndianTiffTag(bytes: Buffer, tag: number): { return null; } -test("restores a figure recipe and exports an exact TIFF", async ({ page }) => { +test("restores a figure recipe, rejects unsupported polyhedra, and exports an exact TIFF", async ({ + page, +}) => { const errors = collectBrowserErrors(page); await openPeriodicFixture(page); @@ -227,6 +250,34 @@ test("restores a figure recipe and exports an exact TIFF", async ({ page }) => { .toHaveAttribute("aria-pressed", "true"); await page.getByRole("button", { name: "Close", exact: true }).click(); + const unsupportedPolyhedraRecipe = structuredClone(firstRecipe); + unsupportedPolyhedraRecipe.scene.presentation.mode = "polyhedra"; + await page.getByRole("button", { name: "Show display controls" }).click(); + await page.getByRole("button", { name: "Lines", exact: true }).click(); + await page.getByRole("button", { name: "Close", exact: true }).click(); + const unsupportedPolyhedraChooser = page.waitForEvent("filechooser"); + await page.getByRole("button", { name: "Search commands" }).click(); + await page.getByRole("combobox", { name: "Search commands" }).fill( + "open figure recipe", + ); + await page.keyboard.press("Enter"); + await (await unsupportedPolyhedraChooser).setFiles({ + name: "unsupported-polyhedra.pqfigure.json", + mimeType: "application/json", + buffer: Buffer.from(JSON.stringify(unsupportedPolyhedraRecipe)), + }); + await expect(page.locator(".notice")).toContainText( + `Polyhedra unavailable · ${polyhedraRequirement}`, + ); + await page.getByRole("button", { name: "Show display controls" }).click(); + await expect(page.getByRole("button", { name: "Lines", exact: true })) + .toHaveAttribute("aria-pressed", "true"); + await expect(page.getByRole("button", { + name: "Polyhedra", + exact: true, + })).toBeDisabled(); + await page.getByRole("button", { name: "Close", exact: true }).click(); + const invalidLabelRecipe = structuredClone(firstRecipe); invalidLabelRecipe.annotations[0].atom.atom = 99; const invalidLabelChooser = page.waitForEvent("filechooser"); @@ -711,6 +762,74 @@ test("opens display controls and exports a publication PNG", async ({ page }) => expect(errors).toEqual([]); }); +test("disables unsupported polyhedra with an explicit reason", async ({ page }) => { + const errors = collectBrowserErrors(page); + await openPeriodicFixture(page); + + await page.getByRole("button", { name: "Show display controls" }).click(); + const polyhedra = page.getByRole("button", { + name: "Polyhedra", + exact: true, + }); + await expect(polyhedra).toBeDisabled(); + await expect(polyhedra).toHaveAttribute( + "aria-describedby", + "polyhedra-requirement", + ); + await expect(page.locator("#polyhedra-requirement")).toHaveText( + `Polyhedra · ${polyhedraRequirement}`, + ); + await page.getByRole("button", { name: "Close", exact: true }).click(); + + await page.getByRole("button", { name: "Search commands" }).click(); + await page.getByRole("combobox", { name: "Search commands" }).fill( + "polyhedra", + ); + const command = page.getByRole("option", { + name: /Representation · Polyhedra/, + }); + await expect(command).toBeVisible(); + await expect(command).toHaveAttribute("aria-disabled", "true"); + await expect(command).toContainText(polyhedraRequirement); + expect(errors).toEqual([]); +}); + +test("renders and exports supported NaCl polyhedra", async ({ page }) => { + const errors = collectBrowserErrors(page); + await openPeriodicFixture(page); + await openFixture(page, naclFixture, "nacl.extxyz"); + + const canvas = page.locator("canvas"); + await page.getByRole("button", { name: "Show display controls" }).click(); + const polyhedra = page.getByRole("button", { + name: "Polyhedra", + exact: true, + }); + await expect(polyhedra).toBeEnabled(); + await page.getByRole("button", { name: "Close", exact: true }).click(); + const ballStick = await canvas.screenshot(); + await page.getByRole("button", { name: "Show display controls" }).click(); + await polyhedra.click(); + await expect(polyhedra).toHaveAttribute("aria-pressed", "true"); + await page.getByRole("button", { name: "Close", exact: true }).click(); + await expect.poll( + async () => differentPixelCount(ballStick, await canvas.screenshot()), + ).toBeGreaterThan(2_000); + + const downloadPromise = page.waitForEvent("download"); + await page.getByRole("button", { name: "Figure", exact: true }).click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe("nacl-2400x1800.png"); + const file = await download.path(); + expect(file).not.toBeNull(); + const png = await readFile(file!); + expect(png.subarray(1, 4).toString()).toBe("PNG"); + expect(png.readUInt32BE(16)).toBe(2400); + expect(png.readUInt32BE(20)).toBe(1800); + expect(changedPixelCount(png)).toBeGreaterThan(10_000); + expect(errors).toEqual([]); +}); + test("keeps centered periodic controls direct and reversible", async ({ page }) => { const errors = collectBrowserErrors(page); await openPeriodicFixture(page); diff --git a/frontend/src/App.test.ts b/frontend/src/App.test.ts index 063e807..2ec0f1d 100644 --- a/frontend/src/App.test.ts +++ b/frontend/src/App.test.ts @@ -18,6 +18,7 @@ import { repeatImages, sameCellOrigin, selectedProfilePresentation, + shouldNormalizePolyhedra, uniqueAtomIndices, usesPeriodicFigureContext, } from "./App"; @@ -47,11 +48,20 @@ const presentation: ScenePresentation = { }; describe("scene profiles", () => { + it("keeps a recipe representation while its target frame loads", () => { + expect(shouldNormalizePolyhedra("polyhedra", false, true)).toBe(false); + expect(shouldNormalizePolyhedra("polyhedra", false, false)).toBe(true); + expect(shouldNormalizePolyhedra("polyhedra", true, false)).toBe(false); + expect(shouldNormalizePolyhedra("ball-stick", false, false)).toBe(false); + }); + it("keeps periodic solids on the crystal profile when forces are present", () => { const capabilities: SceneCapabilities = { water: false, ribbon: false, ribbonReason: "Backbone topology unavailable", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "crystal", }; expect(autoProfile(capabilities, true, true)).toBe("crystal"); @@ -68,6 +78,8 @@ describe("scene profiles", () => { water: false, ribbon: false, ribbonReason: "Backbone topology unavailable", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "crystal", }; expect(selectedProfilePresentation( @@ -85,6 +97,8 @@ describe("scene profiles", () => { water: false, ribbon: false, ribbonReason: "Backbone topology unavailable", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "crystal", }; expect(selectedProfilePresentation( @@ -102,6 +116,8 @@ describe("scene profiles", () => { water: true, ribbon: true, ribbonReason: "Backbone available", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "protein", }; expect(autoProfile(capabilities, true, true)).toBe("protein"); @@ -112,6 +128,8 @@ describe("scene profiles", () => { water: false, ribbon: false, ribbonReason: "Backbone topology unavailable", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "molecule", }; expect(autoProfile(capabilities, true, true)).toBe("molecule"); @@ -122,6 +140,8 @@ describe("scene profiles", () => { water: false, ribbon: false, ribbonReason: "Backbone topology unavailable", + polyhedra: false, + polyhedraReason: "Coordination topology unavailable", suggestedProfile: "molecule", }; expect(profilePresentation("trajectory", presentation, true, true, capabilities)).toMatchObject({ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index cb8aa35..f8c319c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -49,6 +49,8 @@ import { framePbc, hasFrameCell, MoleculeScene, + POLYHEDRA_REQUIREMENT, + sceneCapabilities, } from "./MoleculeScene"; import type { TrajectoryOverlays, @@ -73,7 +75,12 @@ import { measureAtomSelection, updateSceneSelection, } from "./selection"; -import { MAX_ATOM_INSTANCES, MAX_PERIODIC_IMAGES } from "./scene/model"; +import { + MAX_ATOM_INSTANCES, + MAX_PERIODIC_IMAGES, + prepareTopology, +} from "./scene/model"; +import type { PreparedTopology } from "./scene/model"; import { cloneSelections, createNamedSelection, @@ -257,6 +264,10 @@ export default function App() { const [opening, setOpening] = useState(false); const [notice, setNotice] = useState(null); const [sceneInfo, setSceneInfo] = useState(null); + const [sceneTopology, setSceneTopology] = useState<{ + manifest: Manifest; + value: PreparedTopology; + } | null>(null); const cache = useRef(new FrameCache()); const moleculeSceneRef = useRef(null); const fileInputRef = useRef(null); @@ -285,6 +296,10 @@ export default function App() { const datasetCheckPending = useRef(false); const manifestGeneration = useRef(""); const activeManifest = useRef(null); + const sceneTopologyRef = useRef<{ + manifest: Manifest; + value: PreparedTopology; + } | null>(null); const datasetChannel = useRef(null); const playbackClock = useRef<{ key: string; requestTimeMs: number | null }>({ key: "", @@ -324,6 +339,8 @@ export default function App() { datasetReloadPending.current = false; manifestGeneration.current = value.dataset_generation ?? ""; activeManifest.current = value; + sceneTopologyRef.current = null; + setSceneTopology(null); setManifest(value); setFrameIndex(0); setLoadedFrame(null); @@ -412,6 +429,33 @@ export default function App() { ) { throw new Error("The saved frame content changed"); } + const activeTopology = sceneTopologyRef.current?.manifest === dataset + ? sceneTopologyRef.current.value + : null; + const requestedTopology = activeTopology + ?? prepareTopology(dataset, candidate); + if (!requestedTopology) { + throw new Error("The molecular topology is unavailable"); + } + const requestedCapabilities = sceneCapabilities( + dataset, + candidate, + recipe.scene.presentation, + requestedTopology, + ); + if ( + recipe.scene.presentation.mode === "polyhedra" + && !requestedCapabilities.polyhedra + ) { + throw new Error( + `Polyhedra unavailable · ${requestedCapabilities.polyhedraReason}`, + ); + } + if (!activeTopology) { + const entry = { manifest: dataset, value: requestedTopology }; + sceneTopologyRef.current = entry; + setSceneTopology(entry); + } setPlaying(false); setPlaybackOptionsOpen(false); setMeasurementPlotOpen(false); @@ -472,6 +516,8 @@ export default function App() { datasetReloadPending.current = true; manifestGeneration.current = ""; activeManifest.current = null; + sceneTopologyRef.current = null; + setSceneTopology(null); cache.current.clear(); frameCoordinateModeRef.current = "source"; setManifest(null); @@ -948,6 +994,20 @@ export default function App() { }, [frameLoading, manifest?.name, rendering]); const frame = loadedFrame?.data ?? null; + useEffect(() => { + if ( + !manifest + || !frame + || sceneTopologyRef.current?.manifest === manifest + ) { + return; + } + const value = prepareTopology(manifest, frame); + if (!value) return; + const entry = { manifest, value }; + sceneTopologyRef.current = entry; + setSceneTopology(entry); + }, [frame, manifest]); const displayedFrameIndex = loadedFrame?.index ?? frameIndex; const displayedFrameMark = useMemo( () => frameMark(displayedFrameIndex, frame), @@ -1062,6 +1122,30 @@ export default function App() { selectionVisibilityKey, ]); const capabilities = sceneInfo?.capabilities ?? null; + useEffect(() => { + if (!shouldNormalizePolyhedra( + presentation.mode, + capabilities?.polyhedra ?? null, + recipeApplying, + )) { + return; + } + setPresentation((current) => ( + current.mode === "polyhedra" + ? { ...current, mode: "ball-stick" } + : current + )); + setProfile("custom"); + setNotice({ + message: `Polyhedra unavailable · ${capabilities?.polyhedraReason ?? POLYHEDRA_REQUIREMENT}`, + tone: "status", + }); + }, [ + capabilities?.polyhedra, + capabilities?.polyhedraReason, + presentation.mode, + recipeApplying, + ]); const canPlay = (manifest?.frame_count ?? 0) > 1; const loadedCoordinateMode = frame?.header.coordinates === "unwrapped" ? "unwrapped" @@ -1984,9 +2068,16 @@ export default function App() { ]); const updatePresentation = useCallback((change: Partial) => { + if (change.mode === "polyhedra" && !capabilities?.polyhedra) { + setNotice({ + message: `Polyhedra unavailable · ${capabilities?.polyhedraReason ?? POLYHEDRA_REQUIREMENT}`, + tone: "status", + }); + return; + } setPresentation((current) => ({ ...current, ...change })); setProfile("custom"); - }, []); + }, [capabilities]); useEffect(() => { if (!manifest || !frame || !capabilities || profile !== "auto") return; @@ -2441,13 +2532,17 @@ export default function App() { detail: presentation.mode === "ribbon" ? "Current" : undefined, run: run(() => updatePresentation({ mode: "ribbon" })), }] : []), - ...(cellAvailable ? [{ + { id: "mode-polyhedra", label: "Representation · Polyhedra", keywords: "style crystal coordination octahedra tetrahedra polygons", - detail: presentation.mode === "polyhedra" ? "Current" : "Bond-derived", + detail: capabilities?.polyhedra + ? presentation.mode === "polyhedra" ? "Current" : "Bond-derived" + : capabilities?.polyhedraReason ?? POLYHEDRA_REQUIREMENT, + disabled: !capabilities?.polyhedra, + discoverableWhenDisabled: true, run: run(() => updatePresentation({ mode: "polyhedra" })), - }] : []), + }, ...(capabilities?.water ? [{ id: "water", label: presentation.water === "hide" ? "Show water" : "Hide water", keywords: "solvent", detail: "W", run: run(() => updatePresentation({ water: presentation.water === "hide" ? "show" : "hide" })) }] : []), ...(cellAvailable ? [{ id: "cell", label: presentation.cell ? "Hide cell" : "Show cell", keywords: "box periodic pbc", detail: "C", run: run(() => updatePresentation({ cell: !presentation.cell })) }] : []), ...(forceAvailable ? [{ id: "forces", label: presentation.forces ? "Hide forces" : "Show forces", keywords: "vectors arrows", detail: "F", run: run(() => updatePresentation({ forces: !presentation.forces })) }] : []), @@ -2836,6 +2931,11 @@ export default function App() { ref={moleculeSceneRef} manifest={manifest} frame={frame} + preparedTopology={ + sceneTopology?.manifest === manifest + ? sceneTopology.value + : null + } presentation={presentation} selectedAtoms={selectedAtoms} resetSignal={resetSignal} @@ -3214,9 +3314,18 @@ interface CommandAction { keywords?: string; detail?: string; disabled?: boolean; + discoverableWhenDisabled?: boolean; run: () => void; } +export function shouldNormalizePolyhedra( + mode: RepresentationMode, + available: boolean | null, + recipeApplying: boolean, +): boolean { + return mode === "polyhedra" && available === false && !recipeApplying; +} + export function filterCommandActions( actions: readonly T[], query: string, @@ -3602,11 +3711,20 @@ function CommandPalette({ type="button" role="option" aria-selected={index === active} - disabled={action.disabled} + aria-disabled={action.disabled || undefined} className={index === active ? "is-active" : ""} onPointerMove={() => setActive(index)} - onClick={action.run} - >{action.label}{action.detail && {action.detail}})} + onClick={() => { + if (!action.disabled) action.run(); + }} + > + {action.label} + {action.detail && ( + action.disabled + ? {action.detail} + : {action.detail} + )} + )} {visible.length === 0 &&

No commands found

} @@ -3723,15 +3841,26 @@ function ScenePanel({ onForceScale: (scale: number) => void; onVelocityScale: (scale: number) => void; }) { - const modes: RepresentationMode[] = [ - "ball-stick", - "spacefill", - "lines", - ...(capabilities.ribbon ? ["ribbon" as const] : []), - ...(cellAvailable - ? ["polyhedra" as const] + const modes: Array<{ + mode: RepresentationMode; + available: boolean; + reason?: string; + }> = [ + { mode: "ball-stick", available: true }, + { mode: "spacefill", available: true }, + { mode: "lines", available: true }, + ...(capabilities.ribbon + ? [{ mode: "ribbon" as const, available: true }] + : []), + ...(cellAvailable || capabilities.polyhedra + ? [{ + mode: "polyhedra" as const, + available: capabilities.polyhedra, + reason: capabilities.polyhedraReason, + }] : []), ]; + const polyhedraNoteId = "polyhedra-requirement"; const repeatCounts = repeatCountsFromImages(presentation.images, pbc); const imageBudget = Math.min( MAX_PERIODIC_IMAGES, @@ -3755,14 +3884,24 @@ function ScenePanel({
Representation
- {modes.map((mode) => )}
+ {cellAvailable && !capabilities.polyhedra && ( + + Polyhedra · {capabilities.polyhedraReason} + + )}
{(capabilities.water || cellAvailable || forceAvailable || velocityAvailable) &&
diff --git a/frontend/src/MoleculeScene.test.ts b/frontend/src/MoleculeScene.test.ts index 5b95811..c4cdd79 100644 --- a/frontend/src/MoleculeScene.test.ts +++ b/frontend/src/MoleculeScene.test.ts @@ -6,6 +6,7 @@ import { atomSelectionForInstance, centeredFramePositions, clearOrbitMotion, + hasRenderablePolyhedra, isAdditivePick, nextKeyboardAtomCursor, nextKeyboardAtomSelection, @@ -40,6 +41,7 @@ import { periodicImageOffsets, prepareFrameGeometry, prepareScene, + prepareTopology, publicationBondGeometry, representationRadius, sameFrameGeometryLayout, @@ -1365,6 +1367,96 @@ describe("scientific representations", () => { expect(unavailable.ribbonReason.length).toBeGreaterThan(0); }); + it("offers polyhedra only for a supported coordination shell", () => { + const unsupportedManifest = manifest( + [8, 1, 1], + [[0, 1], [0, 2]], + ); + const unsupportedFrame = frame( + [0, 0, 0, 0.9, 0, 0, -0.3, 0.85, 0], + [8, 0, 0, 0, 8, 0, 0, 0, 8], + [true, true, true], + ); + const unavailable = sceneCapabilities( + unsupportedManifest, + unsupportedFrame, + basePresentation, + ); + expect(unavailable.polyhedra).toBe(false); + expect(unavailable.polyhedraReason).toContain("3+ bonded ligands"); + + const supportedManifest = manifest( + [14, 8, 8, 8, 8], + [[0, 1], [0, 2], [0, 3], [0, 4]], + ); + const supportedFrame = frame([ + 0, 0, 0, + 1, 1, 1, + -1, -1, 1, + -1, 1, -1, + 1, -1, -1, + ]); + const available = sceneCapabilities( + supportedManifest, + supportedFrame, + basePresentation, + ); + const model = prepareScene( + supportedManifest, + supportedFrame, + basePresentation, + ); + expect(model).not.toBeNull(); + expect(hasRenderablePolyhedra(model!)).toBe(true); + expect(available.polyhedra).toBe(true); + + const hydrogenShell = manifest( + [14, 8, 1, 1, 1], + [[0, 1], [0, 2], [0, 3], [0, 4]], + ); + expect(sceneCapabilities( + hydrogenShell, + supportedFrame, + { ...basePresentation, hydrogens: false }, + ).polyhedra).toBe(false); + }); + + it("checks polyhedra against the trajectory's stable topology", () => { + const topology = manifest([14, 8, 8, 8, 8]); + topology.topology.bond_source = "inferred"; + const bonded = frame([ + 0, 0, 0, + 1, 1, 1, + -1, -1, 1, + -1, 1, -1, + 1, -1, -1, + ]); + const separated = frame([ + 0, 0, 0, + 5, 5, 5, + -5, -5, 5, + -5, 5, -5, + 5, -5, -5, + ]); + const bondedTopology = prepareTopology(topology, bonded); + const separatedTopology = prepareTopology(topology, separated); + expect(bondedTopology?.bonds).toHaveLength(4); + expect(separatedTopology?.bonds).toHaveLength(0); + + expect(sceneCapabilities( + topology, + separated, + basePresentation, + bondedTopology, + ).polyhedra).toBe(true); + expect(sceneCapabilities( + topology, + bonded, + basePresentation, + separatedTopology, + ).polyhedra).toBe(false); + }); + it("keeps PDB chains and unresolved sequence gaps separate", () => { const residueCount = 7; const topology = manifest(Array(residueCount * 4).fill(6)); diff --git a/frontend/src/MoleculeScene.tsx b/frontend/src/MoleculeScene.tsx index 187e01b..c4572b8 100644 --- a/frontend/src/MoleculeScene.tsx +++ b/frontend/src/MoleculeScene.tsx @@ -26,7 +26,13 @@ import { inferProteinSecondaryStructure, type ProteinCartoonResidue, } from "./scene/ribbon"; -import { buildCoordinationPolyhedraGeometry } from "./scene/polyhedra"; +import { + buildCoordinationPolyhedraGeometry, + hasCoordinationPolyhedra, + prepareCoordinationPolyhedraTopology, + type CoordinationPolyhedraInput, + type PreparedCoordinationPolyhedraTopology, +} from "./scene/polyhedra"; import { backboneResidues, cellImageCorners, @@ -44,7 +50,6 @@ import { periodicBondSegments, prepareFrameGeometry, prepareScene, - prepareTopology, publicationBondGeometry, representationRadius, sameFrameGeometryLayout, @@ -83,6 +88,8 @@ const MAX_SELECTION_RING_MARKERS = 512; export const MAX_TRAIL_ATOMS = 16; export const MAX_TRAIL_POINTS = 512; export const MAX_DISPLACEMENT_ATOMS = 32; +export const POLYHEDRA_REQUIREMENT = + "Requires a supported center with 3+ bonded ligands"; const emptyTrajectoryOverlays: TrajectoryOverlays = Object.freeze({ trails: Object.freeze([]), @@ -148,9 +155,27 @@ interface SelectionRectangle { height: number; } +interface PreparedPolyhedraRequest { + input: CoordinationPolyhedraInput; + maxCenters: number; + topology?: PreparedCoordinationPolyhedraTopology; +} + +interface PolyhedraCapabilityCache { + sceneTopology: PreparedTopology; + frame: FrameData | null; + water: ScenePresentation["water"]; + hydrogens: boolean; + bonds: CoordinationPolyhedraInput["bonds"]; + maxCenters: number; + available: boolean; + topology: PreparedCoordinationPolyhedraTopology; +} + interface MoleculeSceneProps { manifest: Manifest; frame: FrameData | null; + preparedTopology: PreparedTopology | null; presentation: ScenePresentation; selectedAtoms: AtomSelection[]; trajectoryOverlays?: TrajectoryOverlays; @@ -367,17 +392,32 @@ const scenePalettes: Record = { const yAxis = new THREE.Vector3(0, 1, 0); -export function sceneCapabilities(manifest: Manifest, frame: FrameData | null): SceneCapabilities { +export function sceneCapabilities( + manifest: Manifest, + frame: FrameData | null, + presentation?: ScenePresentation, + preparedTopology?: PreparedTopology | null, +): SceneCapabilities { const water = detectWaterAtoms(manifest, frame).size > 0; const ribbon = backboneResidues(manifest).length >= 3; const periodic = hasFrameCell(frame) && framePbc(frame).some(Boolean); - return buildSceneCapabilities(manifest, water, ribbon, periodic); + const model = presentation + ? prepareScene(manifest, frame, presentation, preparedTopology) + : null; + return buildSceneCapabilities( + manifest, + water, + ribbon, + Boolean(model && hasRenderablePolyhedra(model)), + periodic, + ); } function buildSceneCapabilities( manifest: Manifest, water: boolean, ribbon: boolean, + polyhedra: boolean, periodic: boolean, ): SceneCapabilities { let ribbonReason = "Backbone available"; @@ -390,6 +430,10 @@ function buildSceneCapabilities( water, ribbon, ribbonReason, + polyhedra, + polyhedraReason: polyhedra + ? "Coordination centers available" + : POLYHEDRA_REQUIREMENT, suggestedProfile: ribbon ? "protein" : periodic && !water ? "crystal" : "molecule", }; } @@ -398,6 +442,7 @@ function renderedSceneInfo( manifest: Manifest, model: PreparedScene, geometry: FrameGeometryPlan, + polyhedra: boolean, ): RenderedSceneInfo { return { imageCount: model.images.length, @@ -409,6 +454,7 @@ function renderedSceneInfo( manifest, model.waterAtoms.size > 0, model.backbone.length >= 3, + polyhedra, Boolean(model.basis && model.pbc.some(Boolean)), ), }; @@ -448,12 +494,15 @@ function sameRenderedSceneInfo(left: RenderedSceneInfo, right: RenderedSceneInfo && left.capabilities.water === right.capabilities.water && left.capabilities.ribbon === right.capabilities.ribbon && left.capabilities.ribbonReason === right.capabilities.ribbonReason + && left.capabilities.polyhedra === right.capabilities.polyhedra + && left.capabilities.polyhedraReason === right.capabilities.polyhedraReason && left.capabilities.suggestedProfile === right.capabilities.suggestedProfile; } export const MoleculeScene = forwardRef(function MoleculeScene({ manifest, frame, + preparedTopology, presentation, selectedAtoms, trajectoryOverlays = emptyTrajectoryOverlays, @@ -478,6 +527,7 @@ export const MoleculeScene = forwardRef const selectionContextRef = useRef(onSelectionContext); const selectionPositionsRef = useRef(onSelectionPositions); const reportedInfoRef = useRef<{ manifest: Manifest; info: RenderedSceneInfo } | null>(null); + const polyhedraCapabilityRef = useRef(null); const exportActiveRef = useRef(false); const [keyboardSelection, setKeyboardSelection] = useState(null); const [boxSelection, setBoxSelection] = useState(null); @@ -910,10 +960,14 @@ export const MoleculeScene = forwardRef useEffect(() => { const state = stateRef.current; if (!state) return; - if (state.topologyManifest !== manifest || !state.preparedTopology) { + if ( + state.topologyManifest !== manifest + || state.preparedTopology !== preparedTopology + ) { state.topologyManifest = manifest; - state.preparedTopology = prepareTopology(manifest, frame); + state.preparedTopology = preparedTopology; } + if (!state.preparedTopology) return; const model = prepareScene(manifest, frame, presentation, state.preparedTopology); if (!model) { if (reportedInfoRef.current) { @@ -923,16 +977,49 @@ export const MoleculeScene = forwardRef selectionContextRef.current?.(null); return; } + const activeTopology = state.preparedTopology; + if (!activeTopology) return; const forces = frameArray(frame, ["forces", "force"]); const velocities = frameArray(frame, ["velocities", "velocity", "vel"]); const frameGeometry = prepareFrameGeometry(model, presentation, forces, velocities); - const info = renderedSceneInfo(manifest, model, frameGeometry); + let cachedPolyhedra = polyhedraCapabilityRef.current; if ( - reportedInfoRef.current?.manifest !== manifest - || !sameRenderedSceneInfo(reportedInfoRef.current.info, info) + !cachedPolyhedra + || cachedPolyhedra.sceneTopology !== activeTopology + || cachedPolyhedra.water !== presentation.water + || cachedPolyhedra.hydrogens !== presentation.hydrogens ) { - reportedInfoRef.current = { manifest, info }; - sceneInfoRef.current?.(info); + const request = polyhedraRequest(model); + cachedPolyhedra = { + sceneTopology: activeTopology, + frame: null, + water: presentation.water, + hydrogens: presentation.hydrogens, + bonds: request.input.bonds, + maxCenters: request.maxCenters, + available: false, + topology: prepareCoordinationPolyhedraTopology( + request.input, + { maxCenters: request.maxCenters }, + ), + }; + polyhedraCapabilityRef.current = cachedPolyhedra; + } + const activePolyhedraRequest: PreparedPolyhedraRequest = { + input: polyhedraInput(model, cachedPolyhedra.bonds), + maxCenters: cachedPolyhedra.maxCenters, + topology: cachedPolyhedra.topology, + }; + if ( + presentation.mode !== "polyhedra" + && cachedPolyhedra.frame !== frame + ) { + cachedPolyhedra.available = hasCoordinationPolyhedra( + activePolyhedraRequest.input, + { maxCenters: activePolyhedraRequest.maxCenters }, + activePolyhedraRequest.topology, + ); + cachedPolyhedra.frame = frame; } const frameLayout = frameGeometryLayout(frameGeometry); const configKey = renderConfigKey(presentation); @@ -967,8 +1054,22 @@ export const MoleculeScene = forwardRef forceScale, velocityScale, frameGeometry, + activePolyhedraRequest, ); } + if (presentation.mode === "polyhedra") { + cachedPolyhedra.available = state.polyhedra !== null; + cachedPolyhedra.frame = frame; + } + const polyhedra = cachedPolyhedra.available; + const info = renderedSceneInfo(manifest, model, frameGeometry, polyhedra); + if ( + reportedInfoRef.current?.manifest !== manifest + || !sameRenderedSceneInfo(reportedInfoRef.current.info, info) + ) { + reportedInfoRef.current = { manifest, info }; + sceneInfoRef.current?.(info); + } state.model = model; const mappedAtoms = state.atomObject?.userData.instanceToAtom; const mappedImages = state.atomObject?.userData.instanceImages; @@ -1009,6 +1110,7 @@ export const MoleculeScene = forwardRef velocityScale, frame, manifest, + preparedTopology, presentation, resetSignal, viewPreset, @@ -1096,6 +1198,9 @@ function capturePublicationSnapshot(state: SceneState): PublicationSnapshot { const manifest = state.topologyManifest; const presentation = state.fitContext?.presentation; if (!model || !manifest || !presentation) throw new Error("The molecular scene is not ready to export"); + if (presentation.mode === "polyhedra" && !state.polyhedra) { + throw new Error(`Polyhedra unavailable · ${POLYHEDRA_REQUIREMENT}`); + } return { model, manifest, @@ -1790,13 +1895,15 @@ function buildPublicationScene( true, ) : null; + if (presentation.mode === "polyhedra" && !polyhedra) { + throw new Error(`Polyhedra unavailable · ${POLYHEDRA_REQUIREMENT}`); + } const atoms = buildAtoms( model, manifest, publicationPresentation, "light", true, - presentation.mode === "polyhedra" && !polyhedra ? "ball-stick" : undefined, ); if (atoms) { stylePublicationMaterials(atoms, resources); @@ -2587,6 +2694,7 @@ function buildFrameRenderables( forceScale: number, velocityScale: number, frameGeometry: FrameGeometryPlan, + polyhedraRequest: PreparedPolyhedraRequest, ): void { const palette = scenePalettes[appearance]; if (presentation.mode === "ribbon") { @@ -2629,21 +2737,32 @@ function buildFrameRenderables( } } else { state.polyhedra = presentation.mode === "polyhedra" - ? buildPolyhedra(model, manifest, presentation, appearance, palette) + ? buildPolyhedra( + model, + manifest, + presentation, + appearance, + palette, + false, + polyhedraRequest, + ) : null; - state.atomObject = buildAtoms( - model, - manifest, - presentation, - appearance, - false, - presentation.mode === "polyhedra" && !state.polyhedra ? "ball-stick" : undefined, - ); + const unsupportedPolyhedra = presentation.mode === "polyhedra" + && !state.polyhedra; + state.atomObject = unsupportedPolyhedra + ? null + : buildAtoms( + model, + manifest, + presentation, + appearance, + false, + ); if (state.atomObject) { state.root.add(state.atomObject); state.pickables.push(state.atomObject); } - state.bonds = state.polyhedra + state.bonds = state.polyhedra || unsupportedPolyhedra ? null : buildBonds(presentation, palette, frameGeometry.bondKind, frameGeometry.bondSegments); if (state.bonds) state.root.add(state.bonds); @@ -3055,21 +3174,14 @@ function buildPolyhedra( appearance: Appearance, palette: ScenePalette, publication = false, + preparedRequest?: PreparedPolyhedraRequest, ): THREE.Group | null { - const visibleAtoms = new Set(model.visibleAtoms); + const request = preparedRequest ?? polyhedraRequest(model); const geometry = buildCoordinationPolyhedraGeometry( - { - positions: model.positions, - atomicNumbers: model.atomicNumbers, - bonds: model.bonds.filter(([left, right]) => ( - visibleAtoms.has(left) && visibleAtoms.has(right) - )), - basis: model.basis, - pbc: model.pbc, - }, + request.input, { images: model.images, - maxCenters: model.visibleAtoms.length > 24 ? 8 : 64, + maxCenters: request.maxCenters, colorForCenter: (atom, atomicNumber) => atomColor( manifest, atom, @@ -3078,6 +3190,7 @@ function buildPolyhedra( appearance, ), }, + request.topology, ); if (!geometry) return null; @@ -3129,6 +3242,51 @@ function buildPolyhedra( return group; } +export function hasRenderablePolyhedra(model: PreparedScene): boolean { + const request = polyhedraRequest(model); + const topology = prepareCoordinationPolyhedraTopology( + request.input, + { maxCenters: request.maxCenters }, + ); + return hasCoordinationPolyhedra( + request.input, + { maxCenters: request.maxCenters }, + topology, + ); +} + +function polyhedraRequest(model: PreparedScene): PreparedPolyhedraRequest { + const bonds = model.visibleAtoms.length === model.count + ? model.bonds + : visibleBonds(model); + return { + input: polyhedraInput(model, bonds), + maxCenters: model.visibleAtoms.length > 24 ? 8 : 64, + }; +} + +function polyhedraInput( + model: PreparedScene, + bonds: CoordinationPolyhedraInput["bonds"], +): CoordinationPolyhedraInput { + return { + positions: model.positions, + atomicNumbers: model.atomicNumbers, + bonds, + basis: model.basis, + pbc: model.pbc, + }; +} + +function visibleBonds( + model: PreparedScene, +): CoordinationPolyhedraInput["bonds"] { + const visibleAtoms = new Set(model.visibleAtoms); + return model.bonds.filter(([left, right]) => ( + visibleAtoms.has(left) && visibleAtoms.has(right) + )); +} + function buildRibbon( model: PreparedScene, manifest: Manifest, diff --git a/frontend/src/commandSearch.test.ts b/frontend/src/commandSearch.test.ts index 873a59c..e67b604 100644 --- a/frontend/src/commandSearch.test.ts +++ b/frontend/src/commandSearch.test.ts @@ -71,13 +71,21 @@ describe("command suggestions", () => { }); describe("command matching", () => { - it("omits disabled actions even when they match", () => { + it("shows disabled actions only when their reason should remain discoverable", () => { const result = searchCommandActions([ { id: "show", label: "Show forces" }, - { id: "hide", label: "Hide forces", disabled: true }, + { + id: "hide", + label: "Hide forces", + detail: "No force data", + disabled: true, + discoverableWhenDisabled: true, + }, + { id: "pause", label: "Pause forces", disabled: true }, ], "forces"); - expect(result.map(({ id }) => id)).toEqual(["show"]); + expect(result.map(({ id }) => id)).toEqual(["show", "hide"]); + expect(result[1].detail).toBe("No force data"); }); it("matches every query term across labels, keywords, and details", () => { diff --git a/frontend/src/commandSearch.ts b/frontend/src/commandSearch.ts index 7b5ad1b..5f377d7 100644 --- a/frontend/src/commandSearch.ts +++ b/frontend/src/commandSearch.ts @@ -4,6 +4,7 @@ export interface SearchableCommand { keywords?: string | readonly string[]; detail?: string; disabled?: boolean; + discoverableWhenDisabled?: boolean; } export interface CommandSearchOptions { @@ -22,27 +23,31 @@ export function searchCommandActions( query: string, options: CommandSearchOptions = {}, ): T[] { - const enabled = actions.filter((action) => !action.disabled); const normalizedQuery = normalize(query); if (!normalizedQuery) { const limit = Math.min(resultLimit(options.limit), EMPTY_RESULT_LIMIT); - return orderedSuggestions(enabled, options).slice(0, limit); + return orderedSuggestions( + actions.filter((action) => !action.disabled), + options, + ).slice(0, limit); } const terms = normalizedQuery.split(" "); const contextRanks = idRanks(options.contextIds); const recentRanks = idRanks(options.recentIds); - const ranked = enabled.flatMap((action, index) => { - const score = matchScore(action, normalizedQuery, terms); - return score === null ? [] : [{ - action, - score, - index, - contextRank: contextRanks.get(action.id), - recentRank: recentRanks.get(action.id), - }]; - }); + const ranked = actions + .filter((action) => !action.disabled || action.discoverableWhenDisabled) + .flatMap((action, index) => { + const score = matchScore(action, normalizedQuery, terms); + return score === null ? [] : [{ + action, + score, + index, + contextRank: contextRanks.get(action.id), + recentRank: recentRanks.get(action.id), + }]; + }); ranked.sort((left, right) => ( right.score - left.score diff --git a/frontend/src/scene/polyhedra.test.ts b/frontend/src/scene/polyhedra.test.ts index 6c7bfb0..eaac8e9 100644 --- a/frontend/src/scene/polyhedra.test.ts +++ b/frontend/src/scene/polyhedra.test.ts @@ -6,7 +6,9 @@ import { createCellBasis, prepareScene } from "./model"; import type { FrameData, Manifest, ScenePresentation } from "../types"; import { buildCoordinationPolyhedraGeometry, + hasCoordinationPolyhedra, inferCoordinationPolyhedra, + prepareCoordinationPolyhedraTopology, type CoordinationPolyhedraInput, } from "./polyhedra"; @@ -108,10 +110,20 @@ describe("coordination polyhedra", () => { const model = prepareScene(manifest, frame, presentation); expect(model).not.toBeNull(); expect(model!.bonds).toHaveLength(384); + const topology = prepareCoordinationPolyhedraTopology(model!); + expect(topology.candidates).toHaveLength(32); + expect(topology.adjacency.size).toBe(32); + expect(hasCoordinationPolyhedra(model!, {}, topology)).toBe(true); const polyhedra = inferCoordinationPolyhedra(model!); expect(polyhedra).toHaveLength(32); expect(polyhedra.every(({ coordinationNumber }) => coordinationNumber === 6)).toBe(true); - expect(buildCoordinationPolyhedraGeometry(model!)).not.toBeNull(); + expect( + inferCoordinationPolyhedra(model!, {}, topology) + .map(({ centerAtom, coordinationNumber }) => [centerAtom, coordinationNumber]), + ).toEqual( + polyhedra.map(({ centerAtom, coordinationNumber }) => [centerAtom, coordinationNumber]), + ); + expect(buildCoordinationPolyhedraGeometry(model!, {}, topology)).not.toBeNull(); }); it("builds a closed tetrahedron with center picking and color attributes", () => { @@ -144,6 +156,40 @@ describe("coordination polyhedra", () => { expectClosedTriangles(geometry!); }); + it("reuses a prepared topology without changing inference or geometry", () => { + const model = input( + [ + [0, 0, 0], + [1, 1, 1], + [-1, -1, 1], + [-1, 1, -1], + [1, -1, -1], + ], + [14, 8, 8, 8, 8], + starBonds(4), + ); + const options = { + centerAtomicNumbers: [14], + colorForCenter: () => "#c46c3b", + }; + const topology = prepareCoordinationPolyhedraTopology(model, options); + const direct = inferCoordinationPolyhedra(model, options); + const prepared = inferCoordinationPolyhedra(model, options, topology); + expect(prepared).toEqual(direct); + + const directGeometry = buildCoordinationPolyhedraGeometry(model, options); + const preparedGeometry = buildCoordinationPolyhedraGeometry(model, options, topology); + expect(preparedGeometry).not.toBeNull(); + expect( + (preparedGeometry!.getAttribute("position") as THREE.BufferAttribute).array, + ).toEqual( + (directGeometry!.getAttribute("position") as THREE.BufferAttribute).array, + ); + expect(preparedGeometry!.userData).toEqual(directGeometry!.userData); + directGeometry!.dispose(); + preparedGeometry!.dispose(); + }); + it("unwraps an octahedron across a periodic boundary", () => { const model = input( [ @@ -192,13 +238,15 @@ describe("coordination polyhedra", () => { ])); model.pbc = [true, true, true]; - const polyhedra = inferCoordinationPolyhedra(model); + const topology = prepareCoordinationPolyhedraTopology(model); + expect(hasCoordinationPolyhedra(model, {}, topology)).toBe(true); + const polyhedra = inferCoordinationPolyhedra(model, {}, topology); expect(polyhedra).toHaveLength(1); expect(polyhedra[0].centerAtom).toBe(0); expect(polyhedra[0].coordinationNumber).toBe(6); expect(new Set(polyhedra[0].ligandAtoms)).toEqual(new Set([1])); expect(polyhedra[0].triangles).toHaveLength(8); - expectClosedTriangles(buildCoordinationPolyhedraGeometry(model)!); + expectClosedTriangles(buildCoordinationPolyhedraGeometry(model, {}, topology)!); }); it("triangulates square faces once for cubic coordination", () => { @@ -250,6 +298,7 @@ describe("coordination polyhedra", () => { starBonds(4), ); expect(inferCoordinationPolyhedra(squarePlanar)[0].triangles).toHaveLength(2); + expect(hasCoordinationPolyhedra(squarePlanar)).toBe(true); const coincident = input( [ @@ -263,6 +312,7 @@ describe("coordination polyhedra", () => { starBonds(4), ); expect(inferCoordinationPolyhedra(coincident)).toEqual([]); + expect(hasCoordinationPolyhedra(coincident)).toBe(false); const methane = input( [ @@ -276,7 +326,48 @@ describe("coordination polyhedra", () => { starBonds(4), ); expect(inferCoordinationPolyhedra(methane)).toEqual([]); - expect(inferCoordinationPolyhedra(methane, { centerAtoms: [0] })).toHaveLength(1); + const automaticTopology = prepareCoordinationPolyhedraTopology(methane); + expect(automaticTopology.candidates).toEqual([]); + expect(automaticTopology.adjacency.size).toBe(0); + expect(hasCoordinationPolyhedra(methane, {}, automaticTopology)).toBe(false); + const explicitOptions = { centerAtoms: [0] }; + const explicitTopology = prepareCoordinationPolyhedraTopology( + methane, + explicitOptions, + ); + expect( + inferCoordinationPolyhedra(methane, explicitOptions, explicitTopology), + ).toHaveLength(1); + expect( + hasCoordinationPolyhedra(methane, explicitOptions, explicitTopology), + ).toBe(true); + }); + + it("does not reuse a topology for filtered bonds or center filters", () => { + const model = input( + [ + [0, 0, 0], + [1, 1, 1], + [-1, -1, 1], + [-1, 1, -1], + [1, -1, -1], + ], + [14, 8, 8, 8, 8], + starBonds(4), + ); + const topology = prepareCoordinationPolyhedraTopology(model); + expect(hasCoordinationPolyhedra(model, {}, topology)).toBe(true); + + const filtered = { + ...model, + bonds: model.bonds.slice(0, 2), + }; + expect(hasCoordinationPolyhedra(filtered)).toBe(false); + expect(hasCoordinationPolyhedra(filtered, {}, topology)).toBe(false); + expect(inferCoordinationPolyhedra(filtered, {}, topology)).toEqual([]); + expect( + hasCoordinationPolyhedra(model, { centerAtomicNumbers: [26] }, topology), + ).toBe(false); }); it("caps complete polyhedra and periodic copies", () => { @@ -309,4 +400,75 @@ describe("coordination polyhedra", () => { expect(geometry!.userData.polyhedronCount).toBe(3); expect(geometry!.getAttribute("position").count).toBe(36); }); + + it("uses the same candidate sampling for exact availability checks", () => { + const positions = Array.from({ length: 9 }, (_, atom) => [atom * 10, 0, 0]); + positions.push( + [81, 1, 1], + [79, -1, 1], + [79, 1, -1], + [81, -1, -1], + ); + const model = input( + positions, + [...Array(9).fill(14), ...Array(4).fill(8)], + Array.from({ length: 4 }, (_, ligand) => [8, ligand + 9]), + ); + const options = { + centerAtoms: Array.from({ length: 9 }, (_, atom) => atom), + maxCenters: 2, + }; + const topology = prepareCoordinationPolyhedraTopology(model, options); + expect(topology.candidates).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]); + expect([...topology.adjacency.keys()]).toEqual([8]); + expect(inferCoordinationPolyhedra(model, options, topology)).toHaveLength(1); + expect(hasCoordinationPolyhedra(model, options, topology)).toBe(true); + expect( + inferCoordinationPolyhedra( + model, + { ...options, maxCenters: 1 }, + topology, + ), + ).toEqual([]); + expect( + hasCoordinationPolyhedra( + model, + { ...options, maxCenters: 1 }, + topology, + ), + ).toBe(false); + }); + + it("keeps prepared adjacency sparse for a large mostly ineligible structure", () => { + const atomCount = 50_000; + const positions = new Float32Array(atomCount * 3); + positions.set([ + 0, 0, 0, + 1, 1, 1, + -1, -1, 1, + -1, 1, -1, + 1, -1, -1, + ]); + const atomicNumbers = Array(atomCount).fill(6); + atomicNumbers[0] = 14; + atomicNumbers.fill(8, 1, 5); + const bonds: Array<[number, number]> = starBonds(4); + for (let atom = 5; atom + 1 < atomCount; atom += 1) { + bonds.push([atom, atom + 1]); + } + const model: CoordinationPolyhedraInput = { + positions, + atomicNumbers, + bonds, + basis: null, + pbc: [false, false, false], + }; + + const topology = prepareCoordinationPolyhedraTopology(model); + expect(topology.candidates).toEqual([0]); + expect(topology.adjacency.size).toBe(1); + expect(topology.adjacency.get(0)).toEqual([1, 2, 3, 4]); + expect(hasCoordinationPolyhedra(model, {}, topology)).toBe(true); + expect(inferCoordinationPolyhedra(model, {}, topology)).toHaveLength(1); + }); }); diff --git a/frontend/src/scene/polyhedra.ts b/frontend/src/scene/polyhedra.ts index 4b440ee..48f2220 100644 --- a/frontend/src/scene/polyhedra.ts +++ b/frontend/src/scene/polyhedra.ts @@ -42,6 +42,12 @@ export interface CoordinationPolyhedron { triangles: Array<[number, number, number]>; } +export interface PreparedCoordinationPolyhedraTopology { + readonly atomCount: number; + readonly candidates: readonly number[]; + readonly adjacency: ReadonlyMap; +} + interface FacePlane { normal: THREE.Vector3; offset: number; @@ -60,74 +66,115 @@ interface CoordinationNeighbor { distance: number; } +interface PreparedTopologyMetadata { + atomicNumbers: readonly number[]; + bonds: ReadonlyArray; + selectionKey: string; +} + const DEFAULT_COLOR = new THREE.Color("#568da3"); const EXCLUDED_AUTO_CENTERS = new Set([ 1, 2, 6, 7, 8, 9, 10, 17, 18, 35, 36, 53, 54, 85, 86, ]); +const EMPTY_NEIGHBORS: readonly number[] = Object.freeze([]); +const preparedTopologyMetadata = new WeakMap< + PreparedCoordinationPolyhedraTopology, + PreparedTopologyMetadata +>(); + +export function prepareCoordinationPolyhedraTopology( + input: CoordinationPolyhedraInput, + options: CoordinationPolyhedraOptions = {}, +): PreparedCoordinationPolyhedraTopology { + const atomCount = inputAtomCount(input); + const explicitCenters = options.centerAtoms !== undefined; + const atomicNumberFilter = coordinationCenterFilter(options); + const explicitCandidates = explicitCenters + ? normalizedAtomIndices(options.centerAtoms ?? [], atomCount) + : null; + const explicitCandidateSet = explicitCandidates + ? new Set(explicitCandidates) + : null; + const adjacencySets = new Map>(); + + const eligible = (atom: number): boolean => { + if (explicitCandidateSet) return explicitCandidateSet.has(atom); + const atomicNumber = input.atomicNumbers[atom] ?? 0; + return isSuitableCoordinationCenter(atomicNumber) + && (!atomicNumberFilter || atomicNumberFilter.has(atomicNumber)); + }; + const addNeighbor = (center: number, neighbor: number): void => { + if (!eligible(center)) return; + let neighbors = adjacencySets.get(center); + if (!neighbors) { + neighbors = new Set(); + adjacencySets.set(center, neighbors); + } + neighbors.add(neighbor); + }; + + for (const bond of input.bonds) { + const left = bond[0]; + const right = bond[1]; + if (!validBond(left, right, atomCount)) continue; + addNeighbor(left, right); + addNeighbor(right, left); + } + + const candidates = explicitCandidates + ?? [...adjacencySets.keys()].sort((left, right) => left - right); + const adjacency = new Map(); + for (const [center, neighbors] of adjacencySets) { + adjacency.set(center, Object.freeze([...neighbors])); + } + const topology: PreparedCoordinationPolyhedraTopology = Object.freeze({ + atomCount, + candidates: Object.freeze(candidates), + adjacency, + }); + preparedTopologyMetadata.set(topology, { + atomicNumbers: input.atomicNumbers, + bonds: input.bonds, + selectionKey: coordinationCenterSelectionKey(options, atomCount), + }); + return topology; +} export function inferCoordinationPolyhedra( input: CoordinationPolyhedraInput, options: CoordinationPolyhedraOptions = {}, + topology?: PreparedCoordinationPolyhedraTopology, ): CoordinationPolyhedron[] { - const atomCount = Math.min( - input.atomicNumbers.length, - Math.floor(input.positions.length / 3), - ); + const atomCount = inputAtomCount(input); if (atomCount < 2) return []; - const maxCoordination = boundedInteger( - options.maxCoordination, - 3, - MAX_COORDINATION_NUMBER, - 12, - ); const maxCenters = boundedInteger( options.maxCenters, 1, MAX_COORDINATION_CENTERS, MAX_COORDINATION_CENTERS, ); - const adjacency = bondAdjacency(input.bonds, atomCount); - const explicitCenters = options.centerAtoms !== undefined; - const atomicNumberFilter = options.centerAtomicNumbers - ? new Set(options.centerAtomicNumbers.filter(Number.isInteger)) - : null; - const candidates = explicitCenters - ? normalizedAtomIndices(options.centerAtoms ?? [], atomCount) - : autoCenterAtoms(input.atomicNumbers, adjacency, atomicNumberFilter); - const sampledCandidates = evenlySample( - candidates, - Math.min(MAX_COORDINATION_CANDIDATES, Math.max(maxCenters * 4, maxCenters)), - ); - const polyhedra: CoordinationPolyhedron[] = []; + const polyhedra = [...coordinationPolyhedronCandidates(input, options, topology)]; + return spatiallySamplePolyhedra(polyhedra, input.positions, maxCenters); +} - for (const centerAtom of sampledCandidates) { - const neighbors = coordinationShell( - input, - centerAtom, - [...adjacency[centerAtom]], - maxCoordination, - ); - if (neighbors.length < 3) continue; - if ( - atomicNumberFilter - && !atomicNumberFilter.has(input.atomicNumbers[centerAtom] ?? 0) - ) continue; - if ( - !explicitCenters - && !isSuitableCoordinationCenter(input.atomicNumbers[centerAtom] ?? 0) - ) continue; - const polyhedron = coordinationPolyhedron(input, centerAtom, neighbors); - if (polyhedron) polyhedra.push(polyhedron); +export function hasCoordinationPolyhedra( + input: CoordinationPolyhedraInput, + options: CoordinationPolyhedraOptions = {}, + topology?: PreparedCoordinationPolyhedraTopology, +): boolean { + for (const polyhedron of coordinationPolyhedronCandidates(input, options, topology)) { + if (polyhedron) return true; } - return spatiallySamplePolyhedra(polyhedra, input.positions, maxCenters); + return false; } export function buildCoordinationPolyhedraGeometry( input: CoordinationPolyhedraInput, options: CoordinationPolyhedraOptions = {}, + topology?: PreparedCoordinationPolyhedraTopology, ): THREE.BufferGeometry | null { - const polyhedra = inferCoordinationPolyhedra(input, options); + const polyhedra = inferCoordinationPolyhedra(input, options, topology); if (polyhedra.length === 0) return null; const images = normalizedImages(options.images); @@ -244,6 +291,112 @@ export function isSuitableCoordinationCenter(atomicNumber: number): boolean { && !EXCLUDED_AUTO_CENTERS.has(atomicNumber); } +function* coordinationPolyhedronCandidates( + input: CoordinationPolyhedraInput, + options: CoordinationPolyhedraOptions, + topology: PreparedCoordinationPolyhedraTopology | undefined, +): Generator { + const atomCount = inputAtomCount(input); + if (atomCount < 2) return; + + const maxCoordination = boundedInteger( + options.maxCoordination, + 3, + MAX_COORDINATION_NUMBER, + 12, + ); + const maxCenters = boundedInteger( + options.maxCenters, + 1, + MAX_COORDINATION_CENTERS, + MAX_COORDINATION_CENTERS, + ); + const prepared = compatiblePreparedTopology(input, options, topology) + ?? prepareCoordinationPolyhedraTopology(input, options); + const explicitCenters = options.centerAtoms !== undefined; + const atomicNumberFilter = coordinationCenterFilter(options); + const sampledCandidates = evenlySample( + prepared.candidates, + Math.min(MAX_COORDINATION_CANDIDATES, Math.max(maxCenters * 4, maxCenters)), + ); + + for (const centerAtom of sampledCandidates) { + const neighbors = coordinationShell( + input, + centerAtom, + prepared.adjacency.get(centerAtom) ?? EMPTY_NEIGHBORS, + maxCoordination, + ); + if (neighbors.length < 3) continue; + if ( + atomicNumberFilter + && !atomicNumberFilter.has(input.atomicNumbers[centerAtom] ?? 0) + ) continue; + if ( + !explicitCenters + && !isSuitableCoordinationCenter(input.atomicNumbers[centerAtom] ?? 0) + ) continue; + const polyhedron = coordinationPolyhedron(input, centerAtom, neighbors); + if (polyhedron) yield polyhedron; + } +} + +function compatiblePreparedTopology( + input: CoordinationPolyhedraInput, + options: CoordinationPolyhedraOptions, + topology: PreparedCoordinationPolyhedraTopology | undefined, +): PreparedCoordinationPolyhedraTopology | null { + if (!topology) return null; + const atomCount = inputAtomCount(input); + const metadata = preparedTopologyMetadata.get(topology); + if ( + topology.atomCount !== atomCount + || metadata?.atomicNumbers !== input.atomicNumbers + || metadata.bonds !== input.bonds + || metadata.selectionKey !== coordinationCenterSelectionKey(options, atomCount) + ) return null; + return topology; +} + +function inputAtomCount(input: CoordinationPolyhedraInput): number { + return Math.min( + input.atomicNumbers.length, + Math.floor(input.positions.length / 3), + ); +} + +function coordinationCenterFilter( + options: CoordinationPolyhedraOptions, +): ReadonlySet | null { + return options.centerAtomicNumbers + ? new Set(options.centerAtomicNumbers.filter(Number.isInteger)) + : null; +} + +function coordinationCenterSelectionKey( + options: CoordinationPolyhedraOptions, + atomCount: number, +): string { + const centers = options.centerAtoms === undefined + ? "auto" + : `explicit:${normalizedAtomIndices(options.centerAtoms, atomCount).join(",")}`; + const filter = coordinationCenterFilter(options); + const atomicNumbers = filter + ? [...filter].sort((left, right) => left - right).join(",") + : "*"; + return `${centers}|${atomicNumbers}`; +} + +function validBond(left: number, right: number, atomCount: number): boolean { + return Number.isInteger(left) + && Number.isInteger(right) + && left >= 0 + && right >= 0 + && left < atomCount + && right < atomCount + && left !== right; +} + function coordinationPolyhedron( input: CoordinationPolyhedraInput, centerAtom: number, @@ -488,24 +641,6 @@ function pointCloudScale(points: readonly THREE.Vector3[]): number { return maximum; } -function autoCenterAtoms( - atomicNumbers: readonly number[], - adjacency: readonly Set[], - filter: ReadonlySet | null, -): number[] { - const result: number[] = []; - const count = Math.min(atomicNumbers.length, adjacency.length); - for (let atom = 0; atom < count; atom += 1) { - const atomicNumber = atomicNumbers[atom] ?? 0; - if ( - adjacency[atom].size >= 1 - && isSuitableCoordinationCenter(atomicNumber) - && (!filter || filter.has(atomicNumber)) - ) result.push(atom); - } - return result; -} - function coordinationShell( input: CoordinationPolyhedraInput, centerAtom: number, @@ -572,29 +707,6 @@ function pointKey(point: THREE.Vector3): string { return `${Math.round(point.x * 1e5)}:${Math.round(point.y * 1e5)}:${Math.round(point.z * 1e5)}`; } -function bondAdjacency( - bonds: ReadonlyArray, - atomCount: number, -): Set[] { - const adjacency = Array.from({ length: atomCount }, () => new Set()); - for (const bond of bonds) { - const left = bond[0]; - const right = bond[1]; - if ( - !Number.isInteger(left) - || !Number.isInteger(right) - || left < 0 - || right < 0 - || left >= atomCount - || right >= atomCount - || left === right - ) continue; - adjacency[left].add(right); - adjacency[right].add(left); - } - return adjacency; -} - function normalizedAtomIndices(values: readonly number[], atomCount: number): number[] { return [...new Set(values.filter((value) => ( Number.isInteger(value) && value >= 0 && value < atomCount diff --git a/frontend/src/styles.css b/frontend/src/styles.css index fd6ec29..aa3e132 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1366,7 +1366,7 @@ input[type="range"]::-moz-range-thumb { background: var(--accent-soft); } -.command-results > button:disabled { +.command-results > button[aria-disabled="true"] { color: var(--disabled); cursor: default; } @@ -1376,6 +1376,14 @@ input[type="range"]::-moz-range-thumb { font-size: 10px; } +.command-results > button small { + max-width: 56%; + color: var(--quiet); + font-size: 10px; + line-height: 1.35; + text-align: right; +} + .command-results > p { margin: 0; padding: 32px 18px; @@ -6188,11 +6196,6 @@ button.measurement-plot__legend-item:hover { padding-top: 4px; } -.command-results button:disabled { - opacity: 0.42; - cursor: default; -} - @media (max-width: 760px) { .selection-bar { width: calc(100% - 16px); diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 855b803..566cdd4 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -156,5 +156,7 @@ export interface SceneCapabilities { water: boolean; ribbon: boolean; ribbonReason: string; + polyhedra: boolean; + polyhedraReason: string; suggestedProfile: "molecule" | "protein" | "crystal"; } diff --git a/pqviewer/static/assets/index-BTT3LpoU.css b/pqviewer/static/assets/index-BTT3LpoU.css deleted file mode 100644 index f4dabef..0000000 --- a/pqviewer/static/assets/index-BTT3LpoU.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2) format("woff2"),url(/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-cyrillic-600-normal-CWCymEST.woff2) format("woff2"),url(/assets/inter-cyrillic-600-normal-4D_pXhcN.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2) format("woff2"),url(/assets/inter-greek-ext-600-normal-B8X0CLgF.woff) format("woff");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-greek-600-normal-plRanbMR.woff2) format("woff2"),url(/assets/inter-greek-600-normal-BZpKdvQh.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2) format("woff2"),url(/assets/inter-vietnamese-600-normal-BuLX-rYi.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2) format("woff2"),url(/assets/inter-latin-ext-600-normal-CIVaiw4L.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-latin-600-normal-LgqL8muc.woff2) format("woff2"),url(/assets/inter-latin-600-normal-CiBQ2DWP.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{color-scheme:light;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;--header-height: 44px;--canvas: #f6f8f8;--surface: #ffffff;--material: rgba(255, 255, 255, .88);--surface-soft: #edf2f3;--line: #d7e0e2;--line-strong: #c2cfd2;--text: #21363c;--muted: #5c7076;--quiet: #62757a;--disabled: #aab7ba;--accent: #257198;--accent-soft: #e3f0f5;--error: #a23d31;--shadow: 0 10px 32px rgba(30, 51, 57, .11), 0 2px 7px rgba(30, 51, 57, .06);--numeric: "SFMono-Regular", "Roboto Mono", Consolas, monospace}:root[data-appearance=dark]{color-scheme:dark;--canvas: #1e2e33;--surface: #26383e;--material: rgba(38, 56, 62, .92);--surface-soft: #30454c;--line: #465b61;--line-strong: #5a7076;--text: #f2f6f5;--muted: #c1ced0;--quiet: #9aadb1;--disabled: #718286;--accent: #63c4d8;--accent-soft: #294c58;--error: #f09a8d;--shadow: 0 12px 36px rgba(0, 0, 0, .3), 0 2px 8px rgba(0, 0, 0, .2)}*{box-sizing:border-box}html,body,#root{width:100%;height:100%;margin:0;overflow:hidden}body{min-width:320px;background:var(--canvas);color:var(--text)}button,select,input{font:inherit}button,select{color:inherit}button{-webkit-tap-highlight-color:transparent}button:focus-visible,select:focus-visible,input:focus-visible,svg:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.molecule-canvas:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}kbd,output{font-family:var(--numeric)}.app-shell,.workspace{width:100%;height:100svh;min-height:0}.workspace{position:relative;isolation:isolate;overflow:hidden;background:var(--canvas)}.molecule-canvas,.canvas-field{position:absolute;top:var(--header-height);left:0;width:100%;height:calc(100% - var(--header-height));display:block}.molecule-canvas{cursor:grab;touch-action:none}.molecule-canvas:active{cursor:grabbing}.molecule-canvas.is-box-selecting{cursor:crosshair}.selection-marquee{position:absolute;z-index:15;pointer-events:none;border:1px solid var(--accent);border-radius:2px;background:color-mix(in srgb,var(--accent) 9%,transparent)}.icon{width:20px;height:20px;display:block}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.topbar{position:absolute;z-index:20;inset:0 0 auto;height:var(--header-height);display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 8px 0 12px;border-bottom:1px solid var(--line);background:var(--surface)}.identity,.topbar-tools,.scene-status,.open-button,.command-button,.customize-button{display:flex;align-items:center}.identity{min-width:0;gap:9px}.identity-mark{width:28px;height:28px;flex:0 0 auto;object-fit:contain}.identity>div{min-width:0;display:flex;align-items:baseline;gap:9px}.identity strong{color:var(--text);font-size:13px;font-weight:650;letter-spacing:-.01em}.identity span:last-child{max-width:min(42vw,520px);overflow:hidden;color:var(--muted);font-size:12px;line-height:1.2;text-overflow:ellipsis;white-space:nowrap}.topbar-tools{flex:0 0 auto;gap:3px}.scene-status{gap:13px;margin-right:8px;color:var(--muted);font-size:11px}.scene-status strong{color:var(--text);font-family:var(--numeric);font-size:10px;font-weight:550}.open-button,.command-button,.customize-button,.more-button,.icon-button{min-width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 9px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:12px;cursor:pointer;transition:background-color .19s ease,color .19s ease}.open-button:hover,.command-button:hover,.customize-button:hover,.more-button:hover,.icon-button:hover{background:var(--surface-soft);color:var(--text)}.open-button .icon,.command-button .icon,.customize-button .icon,.more-button .icon,.icon-button .icon{width:17px;height:17px}.command-button kbd{color:var(--quiet);font-size:10px}.customize-button{width:36px;padding:0}.customize-button[aria-expanded=true]{background:var(--accent-soft);color:var(--text)}.customize-button:disabled{opacity:.48;cursor:default}.more-control{position:relative}.more-button{width:36px;padding:0}.more-menu{position:absolute;z-index:30;top:calc(100% + 7px);right:0;width:226px;padding:5px;border:1px solid var(--line);border-radius:11px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(14px) saturate(1.08);backdrop-filter:blur(14px) saturate(1.08);animation:pop-in .19s ease both}.more-menu button{width:100%;min-height:38px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 10px;border:0;border-radius:7px;background:transparent;color:var(--text);font-size:12px;text-align:left;cursor:pointer}.more-menu button:hover{background:var(--surface-soft)}.more-menu button:disabled{color:var(--disabled);cursor:default}.more-menu button:disabled:hover{background:transparent}.more-menu kbd{color:var(--quiet);font-size:10px}.more-menu hr{height:1px;margin:4px 7px;border:0;background:var(--line)}.scene-control{position:absolute;z-index:12;top:calc(var(--header-height) + 12px);left:14px}.scene-trigger{min-height:44px;display:inline-flex;align-items:center;gap:8px;padding:0 11px 0 12px;border:1px solid var(--line);border-radius:12px;background:var(--material);box-shadow:0 3px 12px #20343a12;-webkit-backdrop-filter:blur(12px) saturate(1.06);backdrop-filter:blur(12px) saturate(1.06);color:var(--text);cursor:pointer;transition:background-color .19s ease,border-color .19s ease,transform .19s ease}.scene-trigger:hover,.scene-trigger[aria-expanded=true]{border-color:var(--line-strong);background:var(--surface)}.scene-trigger:active{transform:scale(.98)}.scene-trigger>span{color:var(--muted);font-size:10px}.scene-trigger>strong{font-size:12px;font-weight:600}.scene-trigger .icon{width:14px;height:14px;color:var(--quiet)}.scene-popover{position:absolute;top:51px;left:0;width:min(356px,calc(100vw - 28px));max-height:min(650px,calc(100svh - 182px));overflow:auto;padding:15px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);scrollbar-color:var(--line-strong) transparent;animation:pop-in .19s ease both}.popover-heading,.sheet-heading,.section-heading-row,.scene-group-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.popover-heading{margin-bottom:13px}.popover-heading>div,.sheet-heading>div{min-width:0}.popover-heading strong,.sheet-heading strong{display:block;color:var(--text);font-size:15px;font-weight:650;letter-spacing:-.01em}.popover-heading span,.sheet-heading span{display:block;margin-top:3px;color:var(--muted);font-size:10px}.profile-strip{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:2px;margin-bottom:13px;padding:2px;border-radius:9px;background:var(--surface-soft)}.profile-strip button{min-width:0;min-height:32px;padding:0 3px;border:0;border-radius:7px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.profile-strip button:hover{color:var(--text)}.profile-strip button.is-active{background:var(--surface);box-shadow:0 1px 3px #1f33391a;color:var(--text);font-weight:600}.scene-group{padding:13px 0;border-top:1px solid var(--line)}.scene-group-label{display:block;margin-bottom:8px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.scene-group-heading{align-items:baseline}.scene-group-heading .scene-group-label{margin-bottom:8px}.scene-group-heading output{color:var(--quiet);font-size:10px}.representation-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}.representation-grid button{min-height:42px;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 10px;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--muted);font-size:11px;text-align:left;cursor:pointer}.representation-grid button:hover:not(:disabled){background:var(--surface-soft);color:var(--text)}.representation-grid button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft);color:var(--text)}.representation-grid button:disabled{color:var(--disabled);cursor:default}.representation-grid button .icon{width:14px;height:14px;color:var(--accent)}.capability-note,.cell-origin-note{display:block;margin-top:7px;color:var(--quiet);font-size:10px;line-height:1.4}.toggle-row,.choice-row{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--text);font-size:12px}.toggle-row.is-disabled,.choice-row.is-disabled{color:var(--disabled)}.toggle-row>button[role=switch],.vim-heading>button[role=switch]{position:relative;width:34px;height:20px;flex:0 0 auto;padding:0;border:0;border-radius:999px;background:var(--line-strong);cursor:pointer;transition:background-color .19s ease}.toggle-row>button[role=switch] i,.vim-heading>button[role=switch] i{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:0 1px 3px #14232833;transition:transform .19s ease}.toggle-row>button[role=switch][aria-checked=true],.vim-heading>button[role=switch][aria-checked=true]{background:var(--accent)}.toggle-row>button[role=switch][aria-checked=true] i,.vim-heading>button[role=switch][aria-checked=true] i{transform:translate(14px)}.toggle-row>button[role=switch]:disabled{cursor:default;opacity:.55}.mini-segmented,.settings-segmented{display:flex;min-height:34px;padding:2px;border-radius:8px;background:var(--surface-soft)}.mini-segmented{width:150px}.mini-segmented button,.settings-segmented button{min-width:0;min-height:30px;flex:1 1 0;padding:0 7px;border:0;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.mini-segmented button.is-active,.settings-segmented button.is-active{background:var(--surface);box-shadow:0 1px 3px #1f33391a;color:var(--text);font-weight:600}.image-presets{display:flex;gap:4px;margin-bottom:8px}.image-presets button{min-height:32px;flex:1 1 0;padding:0 8px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.image-presets button:hover{border-color:var(--line-strong);background:var(--surface-soft);color:var(--text)}.image-presets button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft);color:var(--text)}.image-ranges{display:grid;gap:5px}.image-axis{min-height:36px;display:grid;grid-template-columns:22px 1fr 10px 1fr;align-items:center;padding:0 6px;border:1px solid var(--line);border-radius:8px}.image-axis>span{color:var(--muted);font-family:var(--numeric);font-size:10px}.image-axis>i{color:var(--quiet);font-style:normal;font-size:10px;text-align:center}.image-axis select{min-width:0;width:100%;height:30px;padding:0 4px;border:0;background:transparent;color:var(--text);font-family:var(--numeric);font-size:10px;text-align:center}.image-axis.is-disabled{opacity:.42}.scene-actions{display:flex;align-items:center;justify-content:flex-start;gap:5px;margin:0 -15px -15px;padding:11px 15px 15px;border-top:1px solid var(--line);background:var(--surface)}.scene-actions button{min-height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.scene-actions button:hover{background:var(--surface-soft);color:var(--text)}.scene-actions button.is-active{background:var(--accent-soft);color:var(--text)}.orientation-control{position:absolute;z-index:11;top:calc(var(--header-height) + 12px);right:14px;display:flex;padding:3px;border:1px solid var(--line);border-radius:12px;background:var(--material);box-shadow:0 3px 12px #20343a12;-webkit-backdrop-filter:blur(12px) saturate(1.06);backdrop-filter:blur(12px) saturate(1.06)}.orientation-control button{min-width:38px;height:36px;display:grid;place-items:center;padding:0 7px;border:0;border-radius:8px;background:transparent;color:var(--quiet);font-family:var(--numeric);font-size:10px;cursor:pointer}.orientation-control button:hover,.orientation-control button.is-active{background:var(--surface-soft);color:var(--text)}.orientation-control button.is-active{font-weight:650}.orientation-control button .icon{width:18px;height:18px;color:var(--accent)}.inspector{position:absolute;z-index:14;top:calc(var(--header-height) + 12px);right:14px;bottom:76px;width:316px;overflow:auto;padding:16px 18px 20px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.04);backdrop-filter:blur(16px) saturate(1.04);visibility:hidden;pointer-events:none;opacity:0;transform:translate(14px) scale(.99);transform-origin:top right;transition:opacity .19s ease,transform .19s ease,visibility 0ms linear .19s;scrollbar-color:var(--line-strong) transparent}.inspector.is-open{visibility:visible;pointer-events:auto;opacity:1;transform:translate(0) scale(1);transition-delay:0ms}.panel-heading{min-height:36px;display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:7px}.panel-heading h2{margin:0;font-size:16px;line-height:1.3;font-weight:650;letter-spacing:-.01em}.close-inspector{width:32px;min-width:32px;height:32px;padding:0}.readout-section{padding:13px 0}.readout-section+.readout-section{border-top:1px solid var(--line)}.readout-section h3{margin:0 0 9px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.section-heading-row{min-height:18px;align-items:baseline;margin-bottom:8px}.section-heading-row h3{margin:0}.section-heading-row>span,.section-heading-row>output{color:var(--quiet);font-size:10px}.readout{min-height:27px;display:grid;grid-template-columns:minmax(82px,.82fr) minmax(0,1.18fr);align-items:baseline;gap:10px}.readout span{color:var(--muted);font-size:12px}.readout strong{overflow:hidden;color:var(--text);font-family:var(--numeric);font-size:10px;font-weight:500;text-align:right;text-overflow:ellipsis;white-space:nowrap}.readout.is-accent strong{color:var(--accent);font-weight:650}.cell-metrics-section .readout{grid-template-columns:66px minmax(0,1fr)}.quiet-copy{margin:2px 0 4px;color:var(--muted);font-size:11px;line-height:1.5}.vector-readout{margin:8px 0 9px}.vector-readout>span{display:block;margin-bottom:6px;color:var(--muted);font-size:11px}.vector-readout code{display:grid;grid-template-columns:14px 1fr;row-gap:5px;padding-left:9px;border-left:2px solid var(--line-strong);color:var(--text);font-family:var(--numeric);font-size:10px;line-height:1.25}.vector-readout code i{color:var(--quiet);font-style:normal}.vector-readout code b{position:absolute;right:18px;color:var(--quiet);font-size:10px;font-weight:500}.force-scale{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px}.force-scale>span{color:var(--quiet);font-family:var(--numeric);font-size:10px}.timeline{position:absolute;z-index:18;left:50%;bottom:12px;width:min(960px,calc(100% - 28px));min-height:52px;padding:5px 9px 8px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:0 5px 20px #1f333917;-webkit-backdrop-filter:blur(14px) saturate(1.04);backdrop-filter:blur(14px) saturate(1.04);transform:translate(-50%)}.timeline.is-compact{height:52px;padding-block:4px}.transport-row{min-height:42px;display:flex;align-items:center;gap:10px}.transport-buttons{display:flex;flex:0 0 auto;align-items:center}.transport-button,.play-button{width:40px;height:40px;display:grid;place-items:center;padding:0;border:0;border-radius:9px;background:transparent;color:var(--muted);cursor:pointer}.play-button{color:var(--text)}.transport-button:hover:not(:disabled),.play-button:hover:not(:disabled){background:var(--surface-soft);color:var(--accent)}.transport-button:disabled,.play-button:disabled{opacity:.28;cursor:default}.transport-button .icon,.play-button .icon{width:17px;height:17px}.scrubber{min-width:40px;flex:1 1 auto;display:flex;align-items:center}input[type=range]{width:100%;height:28px;margin:0;appearance:none;background:transparent;cursor:pointer}input[type=range]::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:var(--line-strong)}input[type=range]::-webkit-slider-thumb{width:13px;height:13px;margin-top:-5px;appearance:none;border:2px solid var(--surface);border-radius:50%;background:var(--accent);box-shadow:0 0 0 1px var(--accent)}input[type=range]::-moz-range-track{height:3px;border-radius:3px;background:var(--line-strong)}input[type=range]::-moz-range-thumb{width:11px;height:11px;border:2px solid var(--surface);border-radius:50%;background:var(--accent)}.frame-counter{min-width:74px;color:var(--text);font-size:10px;font-weight:600;text-align:right}.speed-control select,.plot-label select{border:0;background:transparent;cursor:pointer}.speed-control select{width:54px;padding:6px 2px 6px 5px;color:var(--muted);font-family:var(--numeric);font-size:10px;text-align:right}.plot-row{position:relative;height:67px;display:flex;align-items:stretch;gap:12px;padding-top:3px;border-top:1px solid var(--line)}.plot-label{width:106px;display:flex;flex:0 0 auto;flex-direction:column;justify-content:center;gap:3px;overflow:hidden}.plot-label select{width:100%;overflow:hidden;padding:0;color:var(--text);font-size:11px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.plot-label small{color:var(--quiet);font-family:var(--numeric);font-size:10px}.series-plot{position:relative;min-width:0;flex:1 1 auto}.series-plot svg{width:100%;height:100%;display:block;overflow:visible;cursor:crosshair;touch-action:none}.plot-grid{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}.series-line{fill:none;stroke:var(--muted);stroke-width:1.5;vector-effect:non-scaling-stroke}.empty-series-line{stroke:var(--line-strong);stroke-width:1;stroke-dasharray:4 6;vector-effect:non-scaling-stroke}.frame-marker{stroke:var(--accent);stroke-width:1.25;opacity:.82;vector-effect:non-scaling-stroke}.frame-point{fill:var(--surface);stroke:var(--accent);stroke-width:2}.plot-range{position:absolute;inset:4px 3px 4px auto;display:flex;flex-direction:column;justify-content:space-between;color:var(--quiet);font-family:var(--numeric);font-size:10px;pointer-events:none}.frame-error{position:static;max-width:76px;flex:0 1 auto;overflow:hidden;padding:3px 6px;border-radius:5px;background:var(--surface);color:var(--error);font-size:10px;text-overflow:ellipsis;white-space:nowrap}.frame-error-compact{display:none}.command-backdrop,.customize-backdrop{position:absolute;z-index:50;inset:var(--header-height) 0 0;background:#141f232e;animation:fade-in .19s ease both}.command-backdrop{display:grid;place-items:start center;padding:min(14vh,120px) 16px 24px}:root[data-appearance=dark] .command-backdrop,:root[data-appearance=dark] .customize-backdrop{background:#00000057}.command-palette{width:min(560px,100%);max-height:min(620px,calc(100svh - 150px));overflow:hidden;border:1px solid var(--line);border-radius:16px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);animation:palette-in .21s ease both}.command-search{height:56px;display:flex;align-items:center;gap:10px;padding:0 15px;border-bottom:1px solid var(--line)}.command-search .icon{width:19px;height:19px;color:var(--quiet)}.command-search input{min-width:0;flex:1 1 auto;border:0;outline:0;background:transparent;color:var(--text);font-size:15px}.command-search input::placeholder{color:var(--quiet)}.command-search kbd{color:var(--quiet);font-size:10px}.command-results{max-height:min(500px,calc(100svh - 220px));overflow:auto;padding:6px;scrollbar-color:var(--line-strong) transparent}.command-results>button{width:100%;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:0 11px;border:0;border-radius:9px;background:transparent;color:var(--text);font-size:12px;text-align:left;cursor:pointer}.command-results>button:hover,.command-results>button.is-active,.command-results>button[aria-selected=true]{background:var(--accent-soft)}.command-results>button:disabled{color:var(--disabled);cursor:default}.command-results>button kbd{color:var(--quiet);font-size:10px}.command-results>p{margin:0;padding:32px 18px;color:var(--muted);font-size:12px;text-align:center}.shortcut-backdrop{padding-top:min(10vh,76px)}.shortcut-panel{width:min(720px,100%);max-height:min(680px,calc(100svh - 120px));overflow:auto;border:1px solid var(--line);border-radius:16px;background:var(--surface);box-shadow:var(--shadow);animation:palette-in .21s ease both;scrollbar-color:var(--line-strong) transparent}.shortcut-heading,.vim-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px}.shortcut-heading{min-height:66px;padding:15px 16px 12px 18px;border-bottom:1px solid var(--line)}.shortcut-heading strong,.shortcut-heading span,.vim-heading strong,.vim-heading span{display:block}.shortcut-heading strong{color:var(--text);font-size:14px;font-weight:650}.shortcut-heading span,.vim-heading span{margin-top:3px;color:var(--quiet);font-size:10px}.shortcut-groups{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,13.5em),1fr));gap:12px 16px;padding:4px 18px 14px}.shortcut-groups>section{min-width:0;padding:13px 0 4px}.shortcut-groups>section+section{padding-left:0;border-left:0}.shortcut-groups h3{margin:0 0 7px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.shortcut-row{min-height:32px;display:grid;grid-template-columns:minmax(7em,max-content) minmax(0,1fr);align-items:center;gap:9px;color:var(--muted);font-size:10px}.shortcut-row>span{min-width:0;overflow-wrap:anywhere}.shortcut-row kbd{width:fit-content;max-width:100%;padding:3px 5px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--text);font-size:9px;white-space:nowrap}.vim-shortcuts{padding:14px 18px 17px;border-top:1px solid var(--line)}.vim-heading{align-items:center}.vim-heading strong{color:var(--text);font-size:11px;font-weight:650}.vim-shortcut-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,11.5em),1fr));gap:0 12px;margin-top:9px;transition:opacity .18s ease}.vim-shortcuts:not(.is-active) .vim-shortcut-grid{opacity:.62}.customize-sheet{position:absolute;z-index:51;top:12px;right:14px;bottom:14px;width:min(376px,calc(100% - 28px));overflow:auto;padding:17px 18px 18px;border:1px solid var(--line);border-radius:16px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);animation:sheet-in .21s ease both;scrollbar-color:var(--line-strong) transparent}.render-sheet{bottom:auto;max-height:calc(100svh - var(--header-height) - 26px);background:var(--surface);-webkit-backdrop-filter:none;backdrop-filter:none}.sheet-heading{min-height:36px;align-items:flex-start;padding-bottom:11px}.settings-section{padding:14px 0;border-top:1px solid var(--line)}.settings-section h3{margin:0 0 10px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.settings-section>small{display:block;margin-top:8px;color:var(--quiet);font-size:10px;line-height:1.4}.settings-link{width:100%;min-height:38px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0;border:0;border-top:1px solid var(--line);background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.settings-link:hover{color:var(--text)}.settings-link kbd{color:var(--quiet);font-size:10px}.keyboard-settings .toggle-row{min-height:38px}.inline-settings{display:grid;gap:7px}.inline-setting{display:grid;grid-template-columns:76px minmax(0,1fr);align-items:center;gap:9px}.inline-setting>span{color:var(--muted);font-size:10px}.customize-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:14px;border-top:1px solid var(--line)}.customize-pair .settings-section{min-width:0;border-top:0}#customize-sheet .settings-section{padding:12px 0}.geometry-settings{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.slider-settings .geometry-settings label{min-width:0;min-height:44px;grid-template-columns:1fr;align-content:center;gap:2px}.settings-segmented{min-height:36px;border-radius:9px}.settings-segmented button{min-height:32px;border-radius:7px;font-size:10px}.settings-choice{width:100%;min-height:54px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--text);text-align:left;cursor:pointer}.settings-choice:hover,.settings-choice.is-active{background:var(--surface-soft)}.settings-choice.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line))}.settings-choice strong,.settings-choice small{display:block}.settings-choice strong{font-size:11px;font-weight:600}.settings-choice small{margin-top:3px;color:var(--quiet);font-size:10px}.settings-choice .icon{width:15px;height:15px;color:var(--accent)}.render-presets{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.render-presets button{min-height:54px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:0 6px;border:1px solid var(--line);border-radius:9px;background:transparent;color:var(--text);cursor:pointer}.render-presets button:hover{border-color:var(--line-strong);background:var(--surface-soft)}.render-presets button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft)}.render-presets strong{font-size:11px;font-weight:600}.render-presets small{color:var(--muted);font-family:var(--numeric);font-size:10px}.render-size{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:end;gap:8px}.render-size label{display:grid;gap:6px;color:var(--muted);font-size:10px}.render-size input{min-width:0;width:100%;height:38px;padding:0 9px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--text);font-family:var(--numeric);font-size:11px}.render-size>i{padding-bottom:11px;color:var(--quiet);font-style:normal;font-size:11px}.render-options-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:14px;border-top:1px solid var(--line)}.render-options-grid .settings-section{min-width:0;border-top:0}.slider-settings label{min-height:38px;display:grid;grid-template-columns:86px minmax(0,1fr);align-items:center;gap:10px}.slider-settings label>span{display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--muted);font-size:10px}.slider-settings output{color:var(--text);font-size:10px}.sheet-actions{position:sticky;z-index:1;bottom:-18px;display:flex;justify-content:flex-end;gap:6px;margin:0 -18px -18px;padding:12px 18px 18px;border-top:1px solid var(--line);background:var(--surface)}.sheet-actions>button{min-height:38px;padding:0 13px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.sheet-actions>button:hover{background:var(--surface-soft);color:var(--text)}.sheet-actions>button.primary{background:var(--accent);color:#fff;font-weight:600}.sheet-actions>button:disabled,.render-presets button:disabled,.render-sheet .icon-button:disabled{opacity:.5;cursor:default}.drop-overlay{position:absolute;z-index:60;inset:var(--header-height) 0 0;display:grid;place-items:center;padding:24px;background:color-mix(in srgb,var(--canvas) 82%,transparent);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);pointer-events:none;animation:fade-in .15s ease both}.drop-overlay>div{min-width:min(340px,90vw);padding:28px 32px;border:1px solid color-mix(in srgb,var(--accent) 52%,var(--line));border-radius:16px;background:var(--material);box-shadow:var(--shadow);color:var(--text);text-align:center}.drop-overlay .icon{width:27px;height:27px;margin:0 auto 10px;color:var(--accent)}.drop-overlay strong,.drop-overlay span{display:block}.drop-overlay strong{font-size:16px;font-weight:650}.drop-overlay span{margin-top:6px;color:var(--muted);font-size:11px}.notice{position:absolute;z-index:45;left:50%;bottom:78px;max-width:min(440px,calc(100% - 32px));max-height:min(120px,calc(100% - 24px));overflow:hidden;overflow-wrap:anywhere;padding:9px 13px;border:1px solid var(--line);border-radius:9px;background:var(--material);box-shadow:0 4px 15px #1f333917;color:var(--text);font-size:11px;line-height:1.35;white-space:normal;transform:translate(-50%);animation:notice-in .19s ease both}.notice>span{min-width:0;overflow-wrap:anywhere}.notice.is-error{width:min(440px,calc(100% - 16px));max-width:min(440px,calc(100% - 16px));display:grid;grid-template-columns:minmax(0,1fr) 44px;align-items:start;gap:6px;border-color:color-mix(in srgb,var(--error) 30%,var(--line))}.notice.is-error>span{max-height:100px;overflow:auto;overscroll-behavior:contain;scrollbar-color:var(--line-strong) transparent;scrollbar-gutter:stable}.notice-dismiss{width:44px;height:44px;display:grid;place-items:center;margin:-6px -8px -6px 0;padding:0;border:0;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer}.notice-dismiss:hover{background:var(--surface-soft);color:var(--text)}.notice-dismiss .icon{width:16px;height:16px}.notice.is-busy:before{content:"";width:7px;height:7px;display:inline-block;margin-right:8px;border-radius:50%;background:var(--accent);animation:pulse 1.1s ease-in-out infinite}.centered-state{position:absolute;z-index:18;inset:var(--header-height) 0 0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;background:var(--canvas);text-align:center}.centered-state h1{margin:18px 0 0;font-size:17px;font-weight:650;letter-spacing:-.01em}.centered-state p{max-width:360px;margin:7px 0 0;color:var(--muted);font-size:12px;line-height:1.5}.centered-state button{min-height:40px;display:inline-flex;align-items:center;gap:7px;margin-top:17px;padding:0 13px;border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--text);font-size:11px;cursor:pointer}.centered-state button:hover{border-color:var(--accent);color:var(--accent)}.state-orbit{position:relative;width:48px;height:48px}.state-orbit i{position:absolute;inset:13px 2px;border:1px solid var(--muted);border-radius:50%;transform:rotate(30deg)}.state-orbit i:nth-child(2){transform:rotate(-30deg)}.state-orbit b{position:absolute;top:21px;left:21px;width:6px;height:6px;border-radius:50%;background:var(--accent)}.state-orbit.is-busy{animation:orbit-spin 1.8s linear infinite}@keyframes pop-in{0%{opacity:0;transform:translateY(-5px) scale(.985)}}@keyframes palette-in{0%{opacity:0;transform:translateY(-8px) scale(.985)}}@keyframes sheet-in{0%{opacity:0;transform:translate(12px) scale(.99)}}@keyframes mobile-sheet-in{0%{opacity:0;transform:translateY(18px) scale(.99)}}@keyframes notice-in{0%{opacity:0;transform:translate(-50%,6px)}}@keyframes fade-in{0%{opacity:0}}@keyframes orbit-spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.35}}@media(max-width:760px){.panel-button,.render-button{min-width:44px;flex-shrink:0}.scene-status,.command-button{display:none}.topbar{padding-right:4px}.open-button,.customize-button,.more-button,.icon-button{min-width:44px;height:44px}.more-menu button,.profile-strip button,.representation-grid button,.image-presets button,.scene-actions button,.sheet-actions>button,.centered-state button{min-height:44px}.mini-segmented,.settings-segmented{min-height:48px}.mini-segmented button,.settings-segmented button{min-height:44px}.image-axis{min-height:48px}.image-axis select,.speed-control select,.plot-label select,.render-size input,input[type=range]{height:44px}.toggle-row>button[role=switch],.vim-heading>button[role=switch]{width:44px;height:44px;background:transparent}.toggle-row>button[role=switch]:before,.vim-heading>button[role=switch]:before{content:"";position:absolute;top:12px;left:5px;width:34px;height:20px;border-radius:999px;background:var(--line-strong);transition:background-color .19s ease}.toggle-row>button[role=switch] i,.vim-heading>button[role=switch] i{top:15px;left:8px}.toggle-row>button[role=switch][aria-checked=true],.vim-heading>button[role=switch][aria-checked=true]{background:transparent}.toggle-row>button[role=switch][aria-checked=true]:before,.vim-heading>button[role=switch][aria-checked=true]:before{background:var(--accent)}.scene-control{top:calc(var(--header-height) + 8px);left:8px}.scene-popover{position:fixed;z-index:40;inset:auto 8px 70px;width:auto;max-height:min(68svh,610px);border-radius:16px;animation-name:mobile-sheet-in}.orientation-control{top:calc(var(--header-height) + 8px);right:8px}.orientation-control button{min-width:44px;height:44px}.inspector{position:fixed;z-index:36;inset:auto 8px 70px;width:auto;max-height:min(56svh,520px);opacity:0;transform:translateY(18px) scale(.99);transform-origin:bottom center}.inspector.is-open{opacity:1;transform:translateY(0) scale(1)}.timeline{bottom:8px;width:calc(100% - 16px);padding-inline:5px;border-radius:13px}.transport-row{gap:4px}.transport-button,.play-button{width:44px;height:44px}.frame-counter{min-width:58px;font-size:10px}.speed-control select{width:46px}.plot-row{height:72px;gap:7px}.plot-label{width:74px}.plot-range{display:none}.notice{bottom:72px}.command-backdrop{position:fixed;inset:0;align-items:end;padding:8px}.command-palette{max-height:min(74svh,620px);border-radius:17px;animation-name:mobile-sheet-in}.command-results{max-height:calc(74svh - 56px)}.command-results>button{min-height:48px}.shortcut-panel{max-height:calc(100svh - 16px);border-radius:17px;animation-name:mobile-sheet-in}.shortcut-groups{grid-template-columns:1fr}.shortcut-groups>section{padding:13px 0 8px}.shortcut-groups>section+section{padding-left:0;border-top:1px solid var(--line);border-left:0}.vim-shortcut-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.customize-backdrop{position:fixed;inset:0}.customize-sheet{position:absolute;inset:auto 8px 8px;width:auto;max-height:min(78svh,660px);border-radius:17px;animation-name:mobile-sheet-in}.render-sheet{max-height:calc(100svh - 68px)}.toggle-row,.choice-row{min-height:48px}}.scrubber-shell{position:relative;min-width:40px;flex:1 1 auto}.scrubber-shell .scrubber{width:100%}.trajectory-marker-rail{position:absolute;right:6px;bottom:2px;left:6px;height:8px;pointer-events:none}.trajectory-marker{position:absolute;top:-8px;width:24px;height:24px;margin:0;padding:0;border:0;background:transparent;cursor:pointer;pointer-events:auto;transform:translate(-50%)}.trajectory-marker:after{position:absolute;top:9px;left:11px;width:3px;height:6px;border-radius:2px;background:var(--quiet);content:""}.trajectory-marker.is-reference:after{top:8px;left:8px;width:7px;height:7px;border:1px solid var(--surface);border-radius:1px;background:var(--accent);transform:rotate(45deg)}.timeline-options>div{max-height:min(620px,calc(100vh - var(--header-height) - var(--timeline-height) - 24px));overflow-y:auto}.timeline-action-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}.timeline-action-list button{min-width:0;min-height:32px;overflow:hidden;padding:0 8px;border:1px solid var(--line-soft);border-radius:5px;background:var(--surface);color:var(--text);font-size:10px;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.timeline-action-list button:hover{border-color:var(--line-strong);background:var(--surface-soft)}.timeline-action-list button:disabled{opacity:.42;cursor:default}@media(max-width:760px){.timeline-action-list button{min-height:40px}}.measurement-plot__header{overflow:hidden}.measurement-plot__meta{min-width:72px;max-width:220px;flex:0 1 220px;display:flex;align-items:baseline;gap:7px;overflow:hidden}.measurement-plot__meta strong{min-width:0;overflow:hidden;color:var(--text);font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:10px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.measurement-plot__meta span{flex:0 0 auto;white-space:nowrap}.measurement-plot__legend{min-width:0;flex:1 1 auto;display:flex;align-items:center;gap:3px;overflow-x:auto;scrollbar-width:none}.measurement-plot__legend::-webkit-scrollbar{display:none}.measurement-plot__legend-item{min-width:0;height:26px;display:inline-flex;flex:0 1 auto;align-items:center;gap:5px;overflow:hidden;padding:0 6px;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:9px;white-space:nowrap}button.measurement-plot__legend-item{cursor:pointer}button.measurement-plot__legend-item:hover{background:var(--surface-soft);color:var(--text)}.measurement-plot__legend-item>span:not(.measurement-plot__legend-swatch){max-width:150px;overflow:hidden;text-overflow:ellipsis}.measurement-plot__legend-item output{color:var(--text);font-family:var(--numeric)}.measurement-plot__legend-swatch{width:8px;height:2px;flex:0 0 auto;border-radius:2px}.measurement-plot__header-actions,.measurement-plot__context-actions{flex:0 0 auto;display:flex;align-items:center}.measurement-plot__close{width:30px;min-width:30px!important;padding:0!important;font-size:17px!important;font-weight:400!important}.rdf-view-toggle{display:flex;padding:2px;border-radius:5px;background:var(--surface-soft)}.rdf-view-toggle button{min-width:38px;height:26px;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--muted);font:600 10px var(--numeric);cursor:pointer}.rdf-view-toggle button.is-active{background:var(--surface);color:var(--accent);box-shadow:0 1px 3px color-mix(in srgb,var(--text) 12%,transparent)}.rdf-sheet{position:absolute;z-index:42;bottom:calc(var(--timeline-height) + 12px);left:50%;width:min(390px,calc(100% - 24px));max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 24px);display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 14px 44px color-mix(in srgb,var(--text) 16%,transparent);transform:translate(-50%)}.rdf-sheet>header,.rdf-sheet>footer{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 12px}.rdf-sheet>header{border-bottom:1px solid var(--line-soft)}.rdf-sheet>header strong{font-size:12px}.rdf-sheet>header button{width:32px;height:32px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:18px;cursor:pointer}.rdf-sheet__body{display:grid;gap:8px;overflow-y:auto;overscroll-behavior:contain;padding:12px}.rdf-sheet__body>label,.rdf-sheet__body details>div>label{display:grid;grid-template-columns:64px minmax(0,1fr);align-items:center;gap:9px;color:var(--muted);font-size:10px}.rdf-sheet select,.rdf-sheet input{width:100%;height:36px;min-width:0;padding:0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:11px}.rdf-sheet__body details{padding-top:2px}.rdf-sheet__body summary{color:var(--muted);font-size:10px;cursor:pointer}.rdf-sheet__body details>div{display:grid;gap:8px;padding-top:8px}.rdf-sheet>footer{border-top:1px solid var(--line-soft);color:var(--quiet);font-size:9px}.rdf-sheet>footer button{min-width:72px;height:34px;border:0;border-radius:6px;background:var(--accent);color:var(--surface);font-size:10px;font-weight:650;cursor:pointer}.rdf-sheet>footer button:disabled{opacity:.35;cursor:default}.pinned-measurements{display:block;max-width:none;max-height:none;overflow:visible;padding:0}.pinned-measurements>summary{height:34px;display:flex;align-items:center;padding:0 11px;border:1px solid var(--line);border-radius:8px;background:color-mix(in srgb,var(--surface) 94%,transparent);box-shadow:0 3px 12px color-mix(in srgb,var(--text) 7%,transparent);color:var(--muted);font-size:10px;font-weight:650;cursor:pointer;list-style:none}.pinned-measurements>summary::-webkit-details-marker{display:none}.pinned-measurements[open]>summary{border-color:var(--line-strong);color:var(--text)}.pinned-measurements>section{position:absolute;top:calc(100% + 6px);left:0;width:min(360px,calc(100vw - 24px));overflow:hidden;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:var(--shadow)}.pinned-measurements>section>header{min-height:40px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 11px;border-bottom:1px solid var(--line-soft)}.pinned-measurements>section>header strong{font-size:10px}.pinned-measurements>section>header button{height:30px;padding:0 9px;border:0;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:10px;font-weight:650;cursor:pointer}.pinned-measurements__list{max-height:250px;overflow-y:auto;padding:5px}.pinned-measurements__list>div{min-width:0;display:flex;align-items:stretch}.pinned-measurements .selection-chip{flex:1 1 auto;width:auto;max-width:none;box-shadow:none}@media(max-width:520px){.measurement-plot__meta{max-width:92px;flex-basis:92px}.measurement-plot__meta span{display:none}.measurement-plot__legend-item>span:not(.measurement-plot__legend-swatch){max-width:82px}.measurement-plot__actions button{min-width:36px;width:36px;padding-inline:3px}.rdf-sheet{bottom:calc(var(--timeline-height) + 8px);width:calc(100% - 16px)}.rdf-sheet select,.rdf-sheet input,.rdf-sheet>footer button{height:40px}.rdf-sheet>header button{width:40px;height:40px}.pinned-measurements{top:calc(var(--header-height) + 58px)}.selection-bar .selection-track-button{display:none}}@media(max-height:420px){.pinned-measurements[open]{z-index:45}.pinned-measurements>section{position:fixed;top:calc(var(--header-height) + 100px);bottom:calc(var(--timeline-height) + 8px);left:8px;width:min(360px,calc(100vw - 16px));display:grid;grid-template-rows:auto minmax(0,1fr)}.pinned-measurements__list{min-height:0;max-height:none;overscroll-behavior:contain}}@media(max-width:380px){.rdf-view-toggle button{min-width:34px;padding-inline:5px}}@media(max-width:520px){.identity>div{display:block}.identity strong,.identity span:last-child{display:block}.identity span:last-child{max-width:42vw;margin-top:2px;font-size:10px}.identity-mark{width:27px;height:27px}.open-button{width:44px;padding:0;font-size:0}.open-button .icon{width:18px;height:18px}.scene-trigger>span{display:none}.scene-trigger>strong{max-width:178px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orientation-control button{min-width:44px;padding-inline:5px}.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{width:44px}.frame-counter{min-width:54px}.speed-control select{width:42px;padding-inline:1px}}.workspace.timeline-absent{--timeline-height: 0px;--selection-bottom: 14px}.workspace.timeline-present{--timeline-height: 56px;--selection-bottom: 68px}.canvas-controls>button.is-active{background:var(--accent-soft);color:var(--accent);font-weight:650}.section-label{display:block;margin-bottom:8px;color:var(--quiet);font-size:10px;font-weight:700;letter-spacing:.075em;line-height:1.2;text-transform:uppercase}.section-label-spaced{margin-top:14px}.segmented-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px;padding:2px;border:1px solid var(--line);border-radius:8px;background:var(--surface-soft)}.segmented-options button{min-width:0;min-height:34px;padding:0 6px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.segmented-options button:hover:not(:disabled){color:var(--text)}.segmented-options button.is-active{border-color:var(--line);background:var(--surface);color:var(--accent);font-weight:650}.representation-options{grid-template-columns:repeat(2,minmax(0,1fr))}.display-toggles .section-label{margin:4px 0 2px}.display-toggles .vector-scale-row+.toggle-row{border-top:1px solid var(--line)}.vector-scale-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 10px;padding:1px 0 9px 12px;color:var(--muted);font-size:10px}.vector-scale-row input{grid-column:1 / -1;min-width:0}.vector-scale-row output{color:var(--text);font-size:9px}.selection-bar{position:absolute;z-index:16;bottom:var(--selection-bottom);left:50%;width:max-content;max-width:min(640px,calc(100% - 24px));min-height:48px;display:flex;align-items:center;gap:10px;padding:5px 5px 5px 13px;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:0 5px 18px color-mix(in srgb,var(--text) 10%,transparent);transform:translate(-50%)}.selection-readout{min-width:0;flex:1 1 auto;display:flex;align-items:baseline;gap:9px;overflow:hidden}.selection-readout strong,.selection-readout output{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.selection-readout strong{min-width:0;flex:1 1 auto;font-size:11px}.selection-readout output{flex:0 1 auto;max-width:100%;color:var(--accent);font-size:10px}.selection-hint{color:var(--quiet);font-size:10px;white-space:nowrap}.selection-bar>button:not(.icon-button){flex:0 0 auto;min-width:56px;height:36px;padding:0 9px;border:0;border-radius:5px;background:transparent;color:var(--accent);font-size:10px;font-weight:650;cursor:pointer}.selection-bar>button:not(.icon-button):hover{background:var(--accent-soft)}.selection-bar .measurement-mode[aria-pressed=true]{background:var(--accent-soft)}.measurement-mode-compact{display:none}.selection-bar .icon-button{width:36px;padding:0}.timeline,.timeline.is-compact{height:var(--timeline-height);overflow:visible;padding:0 10px}.timeline .transport-row{min-height:var(--timeline-height);gap:9px}.transport-buttons{gap:0}.transport-button,.play-button{width:36px;height:40px;border-radius:5px}.play-button{color:var(--accent)}.frame-counter{min-width:66px;font-variant-numeric:tabular-nums}.frame-counter-compact{display:none}.frame-metadata{max-width:140px;overflow:hidden;color:var(--muted);font-family:var(--numeric);font-size:10px;text-overflow:ellipsis;white-space:nowrap}.timeline-options{position:relative;flex:0 0 auto}.timeline-options>summary{width:40px;height:40px;display:grid;place-items:center;border-radius:5px;color:var(--muted);cursor:pointer;list-style:none}.timeline-options>summary::-webkit-details-marker{display:none}.timeline-options>summary:hover,.timeline-options[open]>summary{background:var(--surface-soft);color:var(--text)}.timeline-options>div{position:absolute;right:0;bottom:calc(100% + 8px);width:244px;display:grid;gap:10px;padding:12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:var(--shadow)}.timeline-options>div>label{min-height:34px;display:grid;grid-template-columns:1fr 112px;align-items:center;gap:12px;color:var(--muted);font-size:10px}.timeline-options select{width:100%;height:34px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--text);font-size:10px}.timeline-options .section-label{margin:3px 0 -3px}.workspace.timeline-present .notice{bottom:calc(var(--timeline-height) + 12px)}.workspace.selection-present .notice{bottom:calc(var(--selection-bottom) + 58px)}@media(min-width:761px){.workbench-open .selection-bar{left:calc((100% - var(--workbench-width) - 24px) / 2);max-width:calc(100% - var(--workbench-width) - 48px)}}@media(max-width:760px){.canvas-controls{right:8px;left:8px;width:auto;height:50px}.canvas-controls>button{min-width:0;height:44px;flex:1 1 0}.frame-metadata{display:none}.selection-bar{max-width:calc(100% - 16px);min-height:52px;gap:5px;padding:4px 4px 4px 11px}.selection-readout{flex:1 1 auto;flex-direction:column;align-items:flex-start;gap:1px}.selection-bar>button:not(.icon-button),.selection-bar .icon-button,.timeline-options>summary{height:44px}}@media(max-width:520px){.workspace.selection-present .workbench{max-height:calc(100% - var(--header-height) - var(--timeline-height) - 80px)}.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{display:none}.timeline,.timeline.is-compact{padding-inline:4px}.timeline .transport-row{gap:4px}.transport-button,.play-button{width:40px;height:44px}.scrubber{min-width:36px}.frame-counter{min-width:54px;font-size:10px}.timeline-options>div{position:fixed;right:8px;bottom:calc(var(--timeline-height) + 8px);width:min(244px,calc(100vw - 16px))}}@media(max-width:360px){.speed-control{display:none}.representation-grid{grid-template-columns:1fr}}@media(max-width:340px){.orientation-control{top:calc(var(--header-height) + 60px)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}:root{--header-height: 48px;--viewport-toolbar-height: 46px;--workbench-width: 344px}.workspace{--timeline-height: 126px;background:var(--canvas)}.workspace.timeline-compact{--timeline-height: 58px}.molecule-canvas,.canvas-field{top:calc(var(--header-height) + var(--viewport-toolbar-height));width:100%;height:calc(100% - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height));transition:width .18s ease}.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:calc(100% - var(--workbench-width))}.topbar{height:var(--header-height);padding:0 8px 0 12px;gap:12px;background:var(--surface);box-shadow:none}.identity-mark{width:30px;height:30px}.identity strong{font-size:13px}.topbar-tools,.panel-button,.inspect-button,.render-button{display:flex;align-items:center}.topbar-tools{gap:4px}.open-button,.panel-button,.inspect-button,.render-button,.command-button,.more-button,.icon-button{min-width:36px;height:34px;border-radius:7px}.open-button,.panel-button,.inspect-button,.render-button{gap:7px;padding:0 10px;border:1px solid transparent;background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.open-button:hover,.panel-button:hover,.panel-button[aria-expanded=true],.inspect-button:hover,.inspect-button[aria-expanded=true]{border-color:var(--line);background:var(--surface-soft);color:var(--text)}.render-button{border-color:var(--accent);background:var(--accent);color:#fff;font-weight:600}.render-button:hover:not(:disabled){background:color-mix(in srgb,var(--accent) 88%,#000)}.render-button:disabled{border-color:var(--line);background:var(--surface-soft);color:var(--disabled);cursor:default}.figure-control{display:flex;align-items:center}.figure-control .render-button{border-radius:7px 0 0 7px}.figure-options-button{width:30px;height:34px;display:grid;place-items:center;padding:0;border:1px solid var(--accent);border-left-color:color-mix(in srgb,var(--accent) 68%,#fff);border-radius:0 7px 7px 0;background:var(--accent);color:#fff;cursor:pointer}.figure-options-button:hover:not(:disabled),.figure-options-button[aria-expanded=true]{background:color-mix(in srgb,var(--accent) 84%,#000)}.figure-options-button:disabled{border-color:var(--line);background:var(--surface-soft);color:var(--disabled);cursor:default}.figure-options-button .icon{width:15px;height:15px}.open-button:disabled,.panel-button:disabled,.inspect-button:disabled,.command-button:disabled,.more-button:disabled{color:var(--disabled);cursor:default;opacity:.52}.panel-button .icon,.render-button .icon{width:16px;height:16px}.viewport-toolbar{position:absolute;z-index:11;top:var(--header-height);left:0;right:0;height:var(--viewport-toolbar-height);display:flex;align-items:center;gap:4px;padding:0 10px;border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--surface) 94%,var(--canvas));transition:right .18s ease}.workbench-open .viewport-toolbar{right:var(--workbench-width)}.viewport-preset{min-width:58px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.05em;text-transform:uppercase}.viewport-toolbar>label{height:32px;display:flex;align-items:center;gap:3px;padding:0 3px 0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface)}.viewport-toolbar>label>span{color:var(--quiet);font-size:9px;font-weight:600;text-transform:uppercase}.viewport-toolbar select{height:30px;max-width:126px;padding:0 24px 0 5px;border:0;background:transparent;color:var(--text);font-size:11px;font-weight:550;cursor:pointer}.viewport-toolbar>button{height:32px;padding:0 9px;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.viewport-toolbar>button:hover,.viewport-toolbar>button.is-active{border-color:var(--line);background:var(--surface);color:var(--text)}.viewport-toolbar>button.is-active{color:var(--accent);font-weight:650}.viewport-divider{width:1px;height:22px;margin:0 3px;background:var(--line)}.viewport-toolbar .orientation-control{position:static;display:flex;margin-left:auto;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.viewport-toolbar .orientation-control button{min-width:34px;height:32px;padding:0 6px;border-radius:6px}.viewport-toolbar .orientation-control button .icon{width:16px;height:16px}.mobile-view-select{display:none!important}.viewport-toolbar button:disabled,.viewport-toolbar select:disabled,.workbench button:disabled,.timeline button:disabled,.timeline select:disabled,.timeline input:disabled{cursor:default;opacity:.48}.workbench{position:absolute;z-index:18;top:var(--header-height);right:0;bottom:0;width:var(--workbench-width);display:flex;flex-direction:column;overflow:hidden;border-left:1px solid var(--line);background:var(--surface);animation:workbench-in .18s ease both}.workbench[hidden],.workbench-pane[hidden]{display:none}@keyframes workbench-in{0%{opacity:0;transform:translate(12px)}to{opacity:1;transform:translate(0)}}.workbench-tabs{height:43px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));flex:0 0 auto;border-bottom:1px solid var(--line)}.workbench-tabs button{position:relative;border:0;background:transparent;color:var(--muted);font-size:10px;font-weight:600;cursor:pointer}.workbench-tabs button:hover,.workbench-tabs button.is-active{color:var(--text)}.workbench-tabs button.is-active:after{content:"";position:absolute;right:18px;bottom:-1px;left:18px;height:2px;background:var(--accent)}.workbench-heading{min-height:62px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex:0 0 auto;padding:10px 12px 10px 16px;border-bottom:1px solid var(--line)}.workbench-heading strong,.workbench-heading span{display:block}.workbench-heading strong{color:var(--text);font-size:14px;font-weight:650}.workbench-heading span{margin-top:2px;color:var(--muted);font-size:10px}.workbench-heading-actions{display:flex;align-items:center;gap:2px}.workbench-expand-button{display:none}.workbench-body{min-height:0;flex:1 1 auto;overflow:auto;scrollbar-color:var(--line-strong) transparent}.workbench-footer{min-height:43px;display:flex;align-items:stretch;flex:0 0 auto;border-top:1px solid var(--line);background:var(--surface)}.workbench-footer button{min-width:0;display:flex;align-items:center;gap:6px;flex:1 1 0;padding:0 10px;border:0;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.workbench-footer button:hover{background:var(--surface-soft);color:var(--text)}.workbench-footer button+button{border-left:1px solid var(--line)}.render-workbench-footer{min-height:54px;padding:8px 10px;gap:8px}.render-workbench-footer button{min-height:36px;justify-content:center;border:1px solid var(--line);border-radius:6px;font-weight:600}.render-workbench-footer button+button{border-left:1px solid var(--accent)}.render-workbench-footer .primary{border-color:var(--accent);background:var(--accent);color:#fff}.render-workbench-footer .primary:hover:not(:disabled){background:color-mix(in srgb,var(--accent) 88%,#000)}.workbench-footer .icon{width:14px;height:14px}.workbench-footer kbd{margin-left:auto;color:var(--quiet);font-size:9px}.workbench-section{padding:14px 16px}.workbench-section+.workbench-section{border-top:1px solid var(--line)}.workbench-section>h3,.workbench-section-heading h3,.render-panel .settings-section h3{margin:0 0 9px;color:var(--muted);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.workbench-section-heading{display:flex;align-items:baseline;justify-content:space-between;gap:10px}.workbench-section-heading span,.workbench-section-heading output{color:var(--quiet);font-family:var(--numeric);font-size:9px}.workbench-section-heading h3{margin-bottom:9px}.workbench .profile-strip{margin:0}.panel-select-row{min-height:40px;display:grid;grid-template-columns:104px minmax(0,1fr);align-items:center;gap:10px;border-top:1px solid var(--line);color:var(--text);font-size:11px}.panel-select-row:first-of-type{border-top:0}.panel-select-row select{min-width:0;height:32px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:10px}.panel-details{margin-top:8px;border-top:1px solid var(--line)}.panel-details summary{min-height:38px;display:flex;align-items:center;color:var(--muted);font-size:10px;cursor:pointer}.panel-details[open] summary{color:var(--text)}.panel-details .geometry-settings,.panel-details .image-ranges{padding:2px 0 7px}.layer-heading{min-height:45px;display:flex;align-items:center;justify-content:space-between;gap:12px}.layer-heading strong,.layer-heading span{display:block}.layer-heading strong{font-size:11px;font-weight:600}.layer-heading span{margin-top:2px;color:var(--quiet);font-size:9px}.layer-heading>.toggle-row{min-height:36px}.layer-heading>.toggle-row>span{display:none}.image-heading{margin-top:8px}.panel-slider{display:block;margin-top:8px}.panel-slider>span{display:flex;justify-content:space-between;color:var(--muted);font-size:10px}.panel-slider output{color:var(--text)}.inspector-content{padding:0 16px 16px}.inspector-content .readout-section:first-child{padding-top:14px}.render-panel .settings-section{padding:14px 16px;border-bottom:1px solid var(--line)}.render-panel .render-presets{grid-template-columns:repeat(2,minmax(0,1fr))}.render-panel .render-options-grid{display:block}.render-panel .sheet-actions{position:sticky;bottom:0;margin:0;padding:10px 16px;background:var(--surface)}.render-print-row{min-height:34px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--muted);font-size:10px}.render-print-row select{height:32px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:10px}.print-scale-section output{display:block;margin:7px 0 3px;color:var(--text);font-family:var(--numeric);font-size:12px}.render-guide-region{position:absolute;z-index:9;top:calc(var(--header-height) + var(--viewport-toolbar-height));right:0;bottom:var(--timeline-height);left:0;display:grid;place-items:center;overflow:hidden;pointer-events:none;transition:right .18s ease}.workbench-open .render-guide-region{right:var(--workbench-width)}.render-guide{position:relative;flex:0 0 auto;border:1px dashed color-mix(in srgb,var(--accent) 68%,transparent);border-radius:2px;box-shadow:0 0 0 200vmax color-mix(in srgb,var(--canvas) 10%,transparent)}.render-guide span{position:absolute;top:7px;left:8px;padding:2px 5px;border-radius:3px;background:color-mix(in srgb,var(--surface) 90%,transparent);color:var(--accent);font-size:8px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.workspace.is-rendering .molecule-canvas,.workspace.is-rendering .canvas-field{pointer-events:none}.timeline.is-busy .series-plot svg{pointer-events:none}.command-backdrop,.customize-backdrop{position:fixed;inset:0}.preferences-sheet{width:min(440px,calc(100vw - 24px))}.timeline,.timeline.is-compact{left:0;bottom:0;width:100%;height:var(--timeline-height);min-height:0;padding:5px 12px 8px;border:0;border-top:1px solid var(--line);border-radius:0;background:var(--surface);box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;transform:none;transition:width .18s ease}.timeline .transport-row{min-height:46px}.timeline .plot-row{height:70px}.playback-mode-control select{width:92px;height:32px;padding:0 5px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--muted);font-size:9px}.notice{bottom:calc(var(--timeline-height) + 12px)}@media(max-width:960px){.viewport-toolbar>label>span,.viewport-preset{display:none}.viewport-toolbar>label{padding-left:3px}.viewport-toolbar>label,.viewport-toolbar>button{min-height:44px}.viewport-toolbar select{height:42px}.viewport-color-control{display:none!important}.viewport-toolbar .orientation-control{display:none}.viewport-toolbar .mobile-view-select{display:flex!important;margin-left:auto}.viewport-toolbar,.workbench-open .viewport-toolbar{overflow-x:auto;scrollbar-width:none}.viewport-toolbar::-webkit-scrollbar{display:none}.playback-mode-control{display:none}}@media(max-width:1100px){.timeline .jump-button{display:none}}@media(max-width:760px){.workspace{--timeline-height: 128px}.workspace.timeline-compact{--timeline-height: 58px}.molecule-canvas,.canvas-field,.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:100%}.topbar{padding-right:4px}.open-button,.panel-button,.render-button,.more-button,.icon-button{width:44px;min-width:44px;height:44px;padding:0;justify-content:center;font-size:0}.panel-button span,.render-button:not(.primary):after{display:none}.viewport-toolbar,.workbench-open .viewport-toolbar{right:0;gap:3px;overflow-x:auto;padding:0 6px;scrollbar-width:none}.viewport-toolbar::-webkit-scrollbar{display:none}.viewport-toolbar>label,.viewport-toolbar>button,.viewport-toolbar .orientation-control{flex:0 0 auto}.viewport-toolbar>label,.viewport-toolbar>button{min-height:44px}.viewport-toolbar select{height:42px;max-width:112px}.viewport-toolbar .orientation-control{display:none}.viewport-toolbar .mobile-view-select{display:flex!important;margin-left:auto}.workbench{position:fixed;top:auto;right:8px;bottom:calc(var(--timeline-height) + 8px);left:8px;width:auto;height:min(44svh,420px);max-height:calc(100svh - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height) - 24px);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);animation-name:mobile-sheet-in}.workbench.is-expanded{height:min(68svh,600px)}.workbench-expand-button{width:44px;height:44px;display:grid;place-items:center;border:0;border-radius:7px;background:transparent;color:var(--muted)}.workbench-expand-button[aria-expanded=true] .icon{transform:rotate(180deg)}.workbench-tabs{height:46px}.workbench-heading{min-height:58px}.panel-select-row,.panel-details summary,.layer-heading{min-height:44px}.panel-select-row select,.render-print-row select{height:44px}.workbench-footer,.workbench-footer button{min-height:48px}.render-workbench-footer{min-height:56px}.render-workbench-footer button{min-height:44px}.workbench-open .timeline{width:100%}.workbench-open .render-guide-region{right:0}.timeline .jump-button{display:none}.timeline,.timeline.is-compact{bottom:0;width:100%;padding-inline:6px;border-radius:0}.notice{bottom:calc(var(--timeline-height) + 8px)}}@media(max-width:420px){.identity span:last-child{max-width:31vw}.viewport-toolbar>button{padding-inline:8px}.viewport-color-control{display:none!important}.playback-mode-control{display:none}}@media(max-width:520px){.identity span:last-child{display:none}}@media(max-width:760px)and (max-height:520px){.workspace,.workspace.timeline-compact{--timeline-height: 58px}.timeline .plot-row{display:none}.workbench,.workbench.is-expanded{height:calc(100svh - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height) - 16px);max-height:none}.workbench-heading,.workbench-expand-button{display:none}}@media(max-width:340px){.viewport-toolbar,.workbench-open .viewport-toolbar{gap:2px;padding-inline:4px}.viewport-style-control select{width:94px;max-width:94px}.mobile-view-select select{width:50px;padding-right:18px}.viewport-toolbar>button{padding-inline:6px}.viewport-divider{margin-inline:1px}}:root{--header-height: 48px;--viewport-toolbar-height: 0px;--workbench-width: 320px;--export-width: 380px}.app-shell,.workspace{min-height:0}.scene-status,.command-button,.viewport-toolbar{display:none}.molecule-canvas,.canvas-field{top:var(--header-height);height:calc(100% - var(--header-height) - var(--timeline-height))}.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - var(--export-width))}.canvas-controls{position:absolute;z-index:12;top:calc(var(--header-height) + 10px);right:12px;height:36px;display:flex;align-items:center;padding:2px;border:1px solid var(--line);border-radius:8px;background:var(--surface);box-shadow:0 4px 14px color-mix(in srgb,var(--text) 7%,transparent);transition:right .18s ease}.workbench-open .canvas-controls{right:calc(var(--workbench-width) + 12px)}.export-open .canvas-controls{right:calc(var(--export-width) + 12px)}.canvas-controls>button,.canvas-controls .orientation-control button{min-width:34px;height:30px;padding:0 8px;border:0;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.canvas-controls>button:hover,.canvas-controls .orientation-control button:hover,.canvas-controls .orientation-control button.is-active{background:var(--surface-soft);color:var(--text)}.canvas-controls .orientation-control{position:static;display:flex;margin:0;padding:0 0 0 2px;border:0;border-left:1px solid var(--line);border-radius:0;background:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.canvas-controls .orientation-control .icon{width:15px;height:15px}.canvas-view-select{display:none}.canvas-controls button:disabled,.canvas-controls select:disabled{opacity:.48;cursor:default}.workbench{top:var(--header-height);left:auto;bottom:0;width:var(--workbench-width);border:0;border-left:1px solid var(--line);border-radius:0;box-shadow:none}.workbench:focus{outline:none}.workbench:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.workbench-heading{min-height:50px;padding:7px 10px 7px 16px}.workbench-heading strong{font-size:13px}.workbench-expand-button .icon{transform:rotate(180deg);transition:transform .16s ease}.workbench.is-expanded .workbench-expand-button .icon{transform:rotate(0)}.workbench-body{overscroll-behavior:contain}.workbench-section:first-child{padding-top:12px}.selection-chip{position:absolute;z-index:13;top:calc(var(--header-height) + 12px);left:12px;height:34px;display:flex;align-items:center;gap:7px;padding:0 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--muted);box-shadow:0 4px 14px color-mix(in srgb,var(--text) 7%,transparent);font-size:10px;cursor:pointer}.selection-chip strong{color:var(--text);font-family:var(--numeric);font-size:11px}.selection-chip:hover{border-color:var(--line-strong);color:var(--accent)}.export-sheet{position:fixed;z-index:32;top:var(--header-height);right:0;bottom:0;width:var(--export-width);display:flex;flex-direction:column;overflow:hidden;border-left:1px solid var(--line);background:var(--surface);box-shadow:-10px 0 28px color-mix(in srgb,var(--text) 8%,transparent);animation:workbench-in .18s ease both}.figure-sheet-backdrop{position:fixed;z-index:31;inset:0;background:color-mix(in srgb,var(--text) 6%,transparent)}.export-sheet[hidden]{display:none}.export-sheet:focus{outline:none}.export-sheet:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.export-heading{min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex:0 0 auto;padding:8px 10px 8px 16px;border-bottom:1px solid var(--line)}.export-heading strong,.export-heading span{display:block}.export-heading strong{color:var(--text);font-size:14px;font-weight:650}.export-heading span{margin-top:2px;color:var(--muted);font-size:10px}.export-body{min-height:0;flex:1 1 auto;overflow:auto;overscroll-behavior:contain}.export-footer{min-height:56px;display:grid;grid-template-columns:1fr 1.45fr;gap:8px;flex:0 0 auto;padding:8px 10px;border-top:1px solid var(--line);background:var(--surface)}.export-footer button{min-height:38px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:11px;font-weight:600;cursor:pointer}.export-footer .primary{border-color:var(--accent);background:var(--accent);color:#fff}.export-footer button:disabled{opacity:.48;cursor:default}.figure-section{padding:16px;border-bottom:1px solid var(--line)}.figure-section-label{display:block;margin-bottom:10px;color:var(--quiet);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.figure-presets,.figure-choice-row{display:grid;gap:6px}.figure-presets{grid-template-columns:repeat(3,minmax(0,1fr));margin-bottom:10px}.figure-choice-row{grid-template-columns:repeat(2,minmax(0,1fr))}.figure-choice-row+.figure-choice-row{margin-top:8px}.figure-presets button,.figure-choice-row button,.figure-recipe-actions button{min-height:34px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:10px;font-weight:600;cursor:pointer}.figure-presets button:hover,.figure-choice-row button:hover,.figure-recipe-actions button:hover,.figure-presets button.is-active,.figure-choice-row button.is-active{border-color:color-mix(in srgb,var(--accent) 62%,var(--line));background:var(--accent-soft);color:var(--accent-strong)}.figure-number-grid{display:grid;grid-template-columns:1fr 1fr .8fr;gap:6px}.figure-number-grid label,.figure-scale-length{display:grid;gap:5px;color:var(--quiet);font-size:9px}.figure-number-grid input,.figure-scale-length input{min-width:0;height:34px;padding:0 8px;border:1px solid var(--line);border-radius:7px;background:var(--surface);color:var(--text);font-family:var(--numeric);font-size:10px}.figure-toggle{min-height:46px;display:flex;align-items:center;justify-content:space-between;gap:14px;border-bottom:1px solid var(--line);cursor:pointer}.figure-toggle:last-of-type{border-bottom:0}.figure-toggle span,.figure-toggle strong,.figure-toggle small{display:block}.figure-toggle strong{color:var(--text);font-size:10px;font-weight:600}.figure-toggle small{margin-top:2px;color:var(--quiet);font-size:9px}.figure-toggle input{width:16px;height:16px;accent-color:var(--accent)}.figure-scale-length{grid-template-columns:1fr 90px auto;align-items:center;margin-top:8px}.figure-recipe-actions p{margin:0 0 10px;color:var(--quiet);font-size:10px;line-height:1.45}.figure-recipe-actions>div{display:grid;grid-template-columns:1fr 1fr;gap:6px}.figure-sheet-open .molecule-canvas,.figure-sheet-open .canvas-field{width:calc(100% - var(--export-width))}.figure-sheet-open .canvas-controls{right:calc(var(--export-width) + 12px)}.figure-sheet-open .timeline{width:calc(100% - var(--export-width))}.export-options{border-bottom:1px solid var(--line)}.export-options>summary{min-height:48px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 16px;color:var(--text);font-size:11px;font-weight:600;cursor:pointer}.export-options>summary small{overflow:hidden;color:var(--quiet);font-size:9px;font-weight:400;text-overflow:ellipsis;white-space:nowrap}.export-options-body{border-top:1px solid var(--line)}.export-open .render-guide-region{right:var(--export-width)}.timeline,.timeline.is-compact{left:0;bottom:0;width:100%;height:var(--timeline-height);border:0;border-top:1px solid var(--line);border-radius:0;background:var(--surface);box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;transform:none}.frame-counter{white-space:nowrap}.workbench-open .timeline{width:calc(100% - var(--workbench-width))}.export-open .timeline{width:calc(100% - var(--export-width))}.playback-mode-control{display:none}@media(max-width:719px){.inspect-button{display:none}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.export-open .molecule-canvas,.export-open .canvas-field,.figure-sheet-open .molecule-canvas,.figure-sheet-open .canvas-field{width:100%}.workbench,.workbench.is-expanded{position:fixed;top:auto;right:0;bottom:var(--timeline-height);left:0;width:auto;height:min(44svh,360px);max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 16px);border:0;border-top:1px solid var(--line);border-radius:0;box-shadow:0 -10px 28px color-mix(in srgb,var(--text) 8%,transparent)}.workbench.is-expanded{height:min(68svh,560px)}.workbench-heading{min-height:50px}.workbench-expand-button{display:grid}.workbench-open .timeline,.export-open .timeline{width:100%}.workbench-open .canvas-controls,.export-open .canvas-controls{right:8px}.canvas-controls{top:calc(var(--header-height) + 8px);right:8px;height:44px}.canvas-controls>button{min-width:44px;height:40px}.canvas-controls .orientation-control{display:none}.canvas-view-select{height:40px;display:flex;align-items:center;border-left:1px solid var(--line)}.canvas-view-select select{width:58px;height:40px;padding:0 18px 0 8px;border:0;background:transparent;color:var(--text);font-size:11px}.selection-chip{top:calc(var(--header-height) + 13px);left:8px;height:40px}.export-sheet{top:auto;right:0;bottom:var(--timeline-height);left:0;width:auto;height:min(72svh,560px);max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 8px);border:0;border-top:1px solid var(--line);box-shadow:0 -10px 28px color-mix(in srgb,var(--text) 8%,transparent);animation-name:mobile-sheet-in}.export-open .render-guide-region{right:0}.export-heading{min-height:50px}.export-footer button{min-height:44px}.figure-options-button{width:44px;height:44px}.figure-presets button,.figure-choice-row button,.figure-recipe-actions button{min-height:44px}.figure-number-grid input,.figure-scale-length input{height:44px}.render-panel .render-presets{grid-template-columns:repeat(4,minmax(0,1fr))}.render-panel .render-presets button{min-width:0;padding-inline:4px}}@media(max-width:760px){.topbar{padding-inline:8px 4px}.open-button{width:44px;min-width:44px;padding:0;font-size:0}.panel-button,.inspect-button,.render-button{width:auto;min-width:50px;height:44px;padding:0 9px;font-size:10px}.panel-button span{display:inline}.more-button{width:44px;min-width:44px;height:44px}}@media(min-width:720px){.more-inspect-action{display:none!important}}@media(max-width:520px){.identity>div{min-width:0;display:block}.identity strong{display:none}.identity span:last-child{display:block;max-width:none;margin:0;font-size:10px}.identity-mark{width:28px;height:28px}}@media(max-width:420px){.identity>div{display:none}}@media(min-width:720px)and (max-width:760px){.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:calc(100% - var(--workbench-width))}.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - var(--export-width))}}@media(min-width:480px)and (max-width:760px)and (max-height:520px){.workspace,.workspace.timeline-compact{--timeline-height: 58px}.timeline .plot-row{display:none}.workbench,.workbench.is-expanded,.export-sheet{top:var(--header-height);right:0;bottom:var(--timeline-height);left:auto;width:min(320px,48vw);height:auto;max-height:none;border:0;border-left:1px solid var(--line);border-radius:0;box-shadow:-10px 0 28px color-mix(in srgb,var(--text) 8%,transparent)}.workbench-heading{display:flex}.workbench-expand-button{display:none}.workbench-open .canvas-controls,.export-open .canvas-controls{right:calc(min(320px,48vw) + 8px)}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - min(320px,48vw))}.export-open .render-guide-region{right:min(320px,48vw)}.workbench-open .timeline,.export-open .timeline{width:calc(100% - min(320px,48vw))}.render-panel .render-presets{grid-template-columns:repeat(2,minmax(0,1fr))}}:root{--timeline-height: 52px;--workbench-width: 280px}.workspace.timeline-compact{--timeline-height: 52px}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.workbench-open .timeline{width:100%}.workbench{position:absolute;z-index:24;top:calc(var(--header-height) + 10px);right:12px;bottom:auto;left:auto;width:var(--workbench-width);height:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 20px);overflow:hidden;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 12px 32px color-mix(in srgb,var(--text) 10%,transparent)}.workbench.atom-card{width:300px}.workbench-heading{min-height:44px;padding:5px 6px 5px 14px}.workbench-body{max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 66px);overflow:auto}.workbench-section,.workbench-section:first-child{padding:8px 14px}.scene-panel .workbench-section+.workbench-section{border-top:1px solid var(--line)}.periodic-control-label{display:block;margin:12px 0 6px;color:var(--muted);font-size:10px;font-weight:600}.workbench-section>.section-label+.periodic-control-label{margin-top:0}.periodic-inline-control,.periodic-repeat-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px}.periodic-inline-control .periodic-control-label,.periodic-repeat-heading .periodic-control-label{margin:0}.periodic-repeat-heading>span:last-child{color:var(--quiet);font-size:9px;font-variant-numeric:tabular-nums}.periodic-axis-options{display:flex;gap:4px}.periodic-axis-options button,.periodic-repeat-row button{width:30px;height:30px;padding:0;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--muted);font-size:11px;cursor:pointer}.periodic-axis-options button:hover:not(:disabled),.periodic-repeat-row button:hover:not(:disabled){border-color:var(--line-strong);color:var(--text)}.periodic-axis-options button.is-active{border-color:color-mix(in srgb,var(--accent) 34%,var(--line));background:var(--accent-soft);color:var(--accent);font-weight:700}.periodic-repeat-grid{display:grid;gap:4px;margin-top:6px}.periodic-repeat-row{display:grid;grid-template-columns:minmax(0,1fr) 30px 38px 30px;align-items:center;gap:4px;min-height:30px}.periodic-repeat-row>span{color:var(--text);font-size:11px;font-style:italic}.periodic-repeat-row output{color:var(--text);font-size:10px;font-variant-numeric:tabular-nums;text-align:center}.periodic-repeat-row.is-disabled>span,.periodic-repeat-row.is-disabled output{color:var(--quiet)}.periodic-axis-options button:disabled,.periodic-repeat-row button:disabled{cursor:default;opacity:.38}.panel-select-row{min-height:34px}.display-toggles{padding-block:4px}.display-toggles .toggle-row{min-height:36px}.display-toggles .toggle-row+.toggle-row{border-top:1px solid var(--line-soft)}.atom-card .inspector-content{padding:0}.atom-card .readout-section{padding:12px 14px 14px;border:0}.workbench-open .canvas-controls{right:12px;opacity:0;pointer-events:none}.timeline,.timeline.is-compact{height:var(--timeline-height)}.timeline .transport-row{min-height:var(--timeline-height)}@media(max-width:520px){.workbench,.workbench.atom-card{position:absolute;top:calc(var(--header-height) + 8px);right:8px;bottom:auto;left:8px;width:auto;height:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 16px);border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 30px color-mix(in srgb,var(--text) 11%,transparent)}.panel-button,.render-button{min-width:0;padding-inline:8px}.panel-button .icon,.render-button .icon{display:none}.periodic-axis-options button,.periodic-repeat-row button{width:42px;height:42px}.periodic-repeat-row{grid-template-columns:minmax(0,1fr) 42px 46px 42px;min-height:42px}}@media(max-width:760px){.open-button{width:auto;min-width:62px;padding-inline:8px;font-size:10px}}.command-button{display:inline-flex;width:auto;min-width:58px;padding-inline:8px}.command-button span{display:none}.command-button kbd{white-space:nowrap}.command-backdrop{z-index:70}@media(max-width:760px){.command-button{display:inline-flex;width:44px;min-width:44px;height:44px;padding:0}.command-button kbd{display:none}.command-button .icon{width:18px;height:18px}}.workspace{--measurement-plot-height: 164px}.measurement-plot-open .selection-bar{z-index:18;bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 7px);width:min(760px,calc(100% - 24px));max-width:none;border-radius:10px 10px 0 0;box-shadow:0 8px 24px color-mix(in srgb,var(--text) 9%,transparent)}.measurement-plot-open .selection-readout{flex:1 1 auto}.workspace.measurement-plot-open .notice{bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 70px)}.measurement-plot{position:absolute;z-index:17;bottom:calc(var(--timeline-height) + 8px);left:50%;width:min(760px,calc(100% - 24px));height:var(--measurement-plot-height);display:grid;grid-template-rows:34px minmax(0,1fr) 18px;overflow:hidden;border:1px solid var(--line);border-top:0;border-radius:0 0 10px 10px;background:var(--surface);box-shadow:0 8px 24px color-mix(in srgb,var(--text) 9%,transparent);transform:translate(-50%)}.measurement-plot.is-complete{grid-template-rows:34px minmax(0,1fr)}.measurement-plot__header{min-width:0;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:3px 5px 3px 13px;border-bottom:1px solid var(--line-soft)}.measurement-plot__meta{color:var(--quiet);font-family:var(--numeric);font-size:10px}.measurement-plot__actions{flex:0 0 auto;display:flex;align-items:center;gap:1px}.measurement-plot__export-menu{position:relative;display:none}.measurement-plot__export-menu>summary{list-style:none}.measurement-plot__export-menu>summary::-webkit-details-marker{display:none}.measurement-plot__actions button{height:30px;min-width:40px;padding:0 7px;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:10px;font-weight:650;cursor:pointer}.measurement-plot__actions button:hover:not(:disabled){background:var(--surface-soft);color:var(--text)}.measurement-plot__actions button:disabled{color:var(--disabled);cursor:default}.measurement-plot__chart{width:100%;height:100%;min-height:0;display:block;cursor:crosshair}.measurement-plot__chart:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.measurement-plot__grid,.measurement-plot__axis{stroke:var(--line-soft);stroke-width:1}.measurement-plot__axis{stroke:var(--line-strong)}.measurement-plot__trace{stroke:var(--accent);stroke-width:1.8}.measurement-plot__trace-point,.measurement-plot__cursor-point{fill:var(--accent)}.measurement-plot__cursor{stroke:var(--accent);stroke-width:1.25;opacity:.7}.measurement-plot__cursor-point{stroke:var(--surface);stroke-width:2}.measurement-plot__tick,.measurement-plot__axis-label,.measurement-plot__unit,.measurement-plot__empty{fill:var(--quiet);font-family:var(--numeric);font-size:12px}.measurement-plot__axis-label,.measurement-plot__unit,.measurement-plot__empty{font-family:inherit;font-size:11px}.measurement-plot__progress{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;padding:0 12px;color:var(--quiet);font-family:var(--numeric);font-size:10px}.measurement-plot__progress progress{width:100%;height:2px;overflow:hidden;border:0;border-radius:2px;background:var(--surface-soft);color:var(--accent);appearance:none}.measurement-plot__progress progress::-webkit-progress-bar{background:var(--surface-soft)}.measurement-plot__progress progress::-webkit-progress-value{background:var(--accent)}.measurement-plot__progress progress::-moz-progress-bar{background:var(--accent)}.selection-bar .selection-plot-button[aria-pressed=true]{background:var(--accent-soft)}@media(max-width:520px){.workspace{--measurement-plot-height: 194px}.measurement-plot,.measurement-plot-open .selection-bar{width:calc(100% - 16px)}.measurement-plot{grid-template-rows:44px minmax(0,1fr) 18px}.measurement-plot.is-complete{grid-template-rows:44px minmax(0,1fr)}.measurement-plot__header{overflow:visible;padding-left:10px}.measurement-plot__actions>button:not(.measurement-plot__close){display:none}.measurement-plot__export-menu{display:block}.measurement-plot__export-menu>summary{width:58px;height:44px;display:grid;place-items:center;border-radius:5px;color:var(--muted);font-size:10px;font-weight:650;cursor:pointer}.measurement-plot__export-menu[open]>summary{background:var(--surface-soft);color:var(--text)}.measurement-plot__export-menu>div{position:absolute;z-index:2;top:calc(100% + 2px);right:0;width:112px;padding:4px;border:1px solid var(--line);border-radius:7px;background:var(--surface);box-shadow:var(--shadow)}.measurement-plot__export-menu>div button{width:100%;display:block;text-align:left}.measurement-plot__actions button{width:44px;height:44px;padding-inline:5px}}@media(max-width:380px){.measurement-plot-open .selection-bar>button:not(.icon-button){min-width:48px;padding-inline:6px}}@media(max-width:760px)and (max-height:520px){.workspace{--measurement-plot-height: 132px}.measurement-plot{grid-template-rows:44px minmax(0,1fr) 14px}.measurement-plot.is-complete{grid-template-rows:44px minmax(0,1fr)}.measurement-plot__progress{padding-inline:9px}}@media(max-width:760px){.open-button,.panel-button,.inspect-button,.render-button,.canvas-controls>button{font-size:11px}.canvas-controls{height:48px}.canvas-controls>button{min-width:44px;height:44px}.transport-button,.play-button,.timeline-options>summary{width:40px;height:44px}.segmented-options button{min-height:40px;font-size:11px}.workspace.selection-present .timeline-options>div{bottom:calc(100% + 82px)}}@media(max-width:600px){.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{display:none}.frame-counter-full{display:none}.frame-counter-compact{display:inline}.frame-error-full{display:none}.frame-error-compact{display:inline}.frame-error{min-width:28px;max-width:28px;flex:0 0 28px;padding-inline:0}}@media(min-width:521px)and (max-width:760px){.workspace.selection-present .workbench{max-height:calc(100% - var(--header-height) - var(--timeline-height) - 80px)}}@media(max-width:479px)and (max-height:520px){.workbench-heading{min-height:44px;display:flex}}@media(max-width:380px){.selection-hint{display:none}.measurement-plot-open .selection-bar{flex-wrap:wrap;justify-content:flex-end;row-gap:4px;padding-block:6px}.measurement-plot-open .selection-readout{flex:1 0 100%;overflow:hidden}.measurement-plot-open .selection-readout strong{min-width:0;max-width:100%;flex:0 1 auto}.measurement-plot-open .selection-readout output{flex:0 0 auto}.workspace.measurement-plot-open .notice{bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 100px)}.measurement-mode-full{display:none}.measurement-mode-compact{display:inline}}@media(max-width:520px){.workspace.selection-present .timeline-options>div{bottom:calc(var(--selection-bottom) + 60px)}}@media(max-width:760px)and (max-height:450px){.workspace.playback-options-open .canvas-controls,.workspace.playback-options-open .selection-bar{visibility:hidden;pointer-events:none}.workspace.playback-options-open .timeline-options>div{bottom:calc(100% + 8px)}}@media(max-width:520px)and (max-height:450px){.workspace.playback-options-open .timeline-options>div{bottom:calc(var(--timeline-height) + 8px)}}@media(max-width:760px)and (max-height:360px){.workspace.measurement-plot-open .canvas-controls{visibility:hidden;pointer-events:none}.workspace.measurement-plot-open .notice{top:calc(var(--header-height) + 8px);bottom:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 16px)}}.selection-tools{position:relative;flex:0 0 auto}.selection-tools>summary{min-width:54px;height:36px;display:grid;place-items:center;padding:0 9px;border-radius:5px;color:var(--accent);font-size:10px;font-weight:650;cursor:pointer;list-style:none}.selection-tools>summary::-webkit-details-marker{display:none}.selection-tools>summary:hover,.selection-tools[open]>summary{background:var(--accent-soft)}.selection-tools-popover{position:absolute;right:0;bottom:calc(100% + 10px);width:min(300px,calc(100vw - 20px));max-height:min(520px,calc(100svh - var(--header-height) - var(--header-height) - var(--timeline-height) - 28px));overflow-x:hidden;overflow-y:auto;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:var(--shadow)}.selection-tools-popover>section{display:grid;gap:8px;padding:11px}.selection-tools-popover>section+section{border-top:1px solid var(--line)}.selection-tools-popover label,.selection-tools-popover section>span{color:var(--quiet);font-size:9px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.selection-scope-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.selection-scope-grid button,.selection-input-row button,.saved-selections button{min-height:32px;border:1px solid var(--line);border-radius:6px;background:transparent;color:var(--text);font-size:10px;cursor:pointer}.selection-scope-grid button:hover:not(:disabled),.selection-input-row button:hover:not(:disabled),.saved-selections button:hover:not(:disabled){border-color:var(--line-strong);background:var(--surface-soft)}.selection-scope-grid button:disabled,.selection-input-row button:disabled{opacity:.4;cursor:default}.selection-input-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:6px}.selection-input-row.is-name{grid-template-columns:minmax(0,1fr) auto}.selection-input-row input{min-width:0;height:34px;padding:0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font:11px var(--numeric)}.selection-input-row input:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)}.selection-input-row>span{color:var(--muted);font:10px var(--numeric)}.selection-input-row button{padding-inline:10px;color:var(--accent);font-weight:650}.saved-selections{max-height:178px;overflow:auto}.saved-selections>div{display:grid;grid-template-columns:minmax(0,1fr) 32px;gap:5px}.saved-selections>div>button:first-child{min-width:0;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 9px;text-align:left}.saved-selections button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saved-selections button small{color:var(--quiet);font:9px var(--numeric)}.saved-selections>div>button:last-child{display:grid;place-items:center;padding:0;color:var(--quiet)}.saved-selections .icon{width:12px;height:12px}.pinned-measurements{position:absolute;z-index:13;top:calc(var(--header-height) + 12px);left:12px;display:block;max-width:none;max-height:none;overflow:visible;padding:0}.pinned-measurements .selection-chip{position:static;min-width:0;border-radius:8px 0 0 8px;box-shadow:none}.pinned-measurements .selection-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pinned-measurements .selection-chip[aria-pressed=true]{border-color:color-mix(in srgb,var(--accent) 48%,var(--line));background:var(--accent-soft)}.pinned-measurement-remove{width:30px;flex:0 0 30px;display:grid;place-items:center;border:1px solid var(--line);border-left:0;border-radius:0 8px 8px 0;background:var(--surface);color:var(--quiet);cursor:pointer}.pinned-measurement-remove:hover{color:var(--text)}.pinned-measurement-remove .icon{width:12px;height:12px}.selection-summary-panel .readout-section{padding-top:4px}.command-results button:disabled{opacity:.42;cursor:default}@media(max-width:760px){.selection-bar{width:calc(100% - 16px)}.selection-tools{position:static}.selection-tools>summary{height:44px}.selection-tools-popover{right:8px;left:8px;width:auto}.selection-scope-grid button,.selection-input-row button,.saved-selections button{min-height:38px}.selection-input-row input{height:40px}.pinned-measurements{top:calc(var(--header-height) + 60px);left:8px}.pinned-measurements .selection-chip{height:40px}}@media(max-width:520px){.selection-bar .measurement-mode{display:none}.selection-readout{min-width:80px}.selection-tools>summary{min-width:48px;padding-inline:7px}} diff --git a/pqviewer/static/assets/index-CFNrMkGz.css b/pqviewer/static/assets/index-CFNrMkGz.css new file mode 100644 index 0000000..3028207 --- /dev/null +++ b/pqviewer/static/assets/index-CFNrMkGz.css @@ -0,0 +1 @@ +@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2) format("woff2"),url(/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-cyrillic-600-normal-CWCymEST.woff2) format("woff2"),url(/assets/inter-cyrillic-600-normal-4D_pXhcN.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2) format("woff2"),url(/assets/inter-greek-ext-600-normal-B8X0CLgF.woff) format("woff");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-greek-600-normal-plRanbMR.woff2) format("woff2"),url(/assets/inter-greek-600-normal-BZpKdvQh.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2) format("woff2"),url(/assets/inter-vietnamese-600-normal-BuLX-rYi.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2) format("woff2"),url(/assets/inter-latin-ext-600-normal-CIVaiw4L.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/inter-latin-600-normal-LgqL8muc.woff2) format("woff2"),url(/assets/inter-latin-600-normal-CiBQ2DWP.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{color-scheme:light;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;--header-height: 44px;--canvas: #f6f8f8;--surface: #ffffff;--material: rgba(255, 255, 255, .88);--surface-soft: #edf2f3;--line: #d7e0e2;--line-strong: #c2cfd2;--text: #21363c;--muted: #5c7076;--quiet: #62757a;--disabled: #aab7ba;--accent: #257198;--accent-soft: #e3f0f5;--error: #a23d31;--shadow: 0 10px 32px rgba(30, 51, 57, .11), 0 2px 7px rgba(30, 51, 57, .06);--numeric: "SFMono-Regular", "Roboto Mono", Consolas, monospace}:root[data-appearance=dark]{color-scheme:dark;--canvas: #1e2e33;--surface: #26383e;--material: rgba(38, 56, 62, .92);--surface-soft: #30454c;--line: #465b61;--line-strong: #5a7076;--text: #f2f6f5;--muted: #c1ced0;--quiet: #9aadb1;--disabled: #718286;--accent: #63c4d8;--accent-soft: #294c58;--error: #f09a8d;--shadow: 0 12px 36px rgba(0, 0, 0, .3), 0 2px 8px rgba(0, 0, 0, .2)}*{box-sizing:border-box}html,body,#root{width:100%;height:100%;margin:0;overflow:hidden}body{min-width:320px;background:var(--canvas);color:var(--text)}button,select,input{font:inherit}button,select{color:inherit}button{-webkit-tap-highlight-color:transparent}button:focus-visible,select:focus-visible,input:focus-visible,svg:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.molecule-canvas:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}kbd,output{font-family:var(--numeric)}.app-shell,.workspace{width:100%;height:100svh;min-height:0}.workspace{position:relative;isolation:isolate;overflow:hidden;background:var(--canvas)}.molecule-canvas,.canvas-field{position:absolute;top:var(--header-height);left:0;width:100%;height:calc(100% - var(--header-height));display:block}.molecule-canvas{cursor:grab;touch-action:none}.molecule-canvas:active{cursor:grabbing}.molecule-canvas.is-box-selecting{cursor:crosshair}.selection-marquee{position:absolute;z-index:15;pointer-events:none;border:1px solid var(--accent);border-radius:2px;background:color-mix(in srgb,var(--accent) 9%,transparent)}.icon{width:20px;height:20px;display:block}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.topbar{position:absolute;z-index:20;inset:0 0 auto;height:var(--header-height);display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 8px 0 12px;border-bottom:1px solid var(--line);background:var(--surface)}.identity,.topbar-tools,.scene-status,.open-button,.command-button,.customize-button{display:flex;align-items:center}.identity{min-width:0;gap:9px}.identity-mark{width:28px;height:28px;flex:0 0 auto;object-fit:contain}.identity>div{min-width:0;display:flex;align-items:baseline;gap:9px}.identity strong{color:var(--text);font-size:13px;font-weight:650;letter-spacing:-.01em}.identity span:last-child{max-width:min(42vw,520px);overflow:hidden;color:var(--muted);font-size:12px;line-height:1.2;text-overflow:ellipsis;white-space:nowrap}.topbar-tools{flex:0 0 auto;gap:3px}.scene-status{gap:13px;margin-right:8px;color:var(--muted);font-size:11px}.scene-status strong{color:var(--text);font-family:var(--numeric);font-size:10px;font-weight:550}.open-button,.command-button,.customize-button,.more-button,.icon-button{min-width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 9px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:12px;cursor:pointer;transition:background-color .19s ease,color .19s ease}.open-button:hover,.command-button:hover,.customize-button:hover,.more-button:hover,.icon-button:hover{background:var(--surface-soft);color:var(--text)}.open-button .icon,.command-button .icon,.customize-button .icon,.more-button .icon,.icon-button .icon{width:17px;height:17px}.command-button kbd{color:var(--quiet);font-size:10px}.customize-button{width:36px;padding:0}.customize-button[aria-expanded=true]{background:var(--accent-soft);color:var(--text)}.customize-button:disabled{opacity:.48;cursor:default}.more-control{position:relative}.more-button{width:36px;padding:0}.more-menu{position:absolute;z-index:30;top:calc(100% + 7px);right:0;width:226px;padding:5px;border:1px solid var(--line);border-radius:11px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(14px) saturate(1.08);backdrop-filter:blur(14px) saturate(1.08);animation:pop-in .19s ease both}.more-menu button{width:100%;min-height:38px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 10px;border:0;border-radius:7px;background:transparent;color:var(--text);font-size:12px;text-align:left;cursor:pointer}.more-menu button:hover{background:var(--surface-soft)}.more-menu button:disabled{color:var(--disabled);cursor:default}.more-menu button:disabled:hover{background:transparent}.more-menu kbd{color:var(--quiet);font-size:10px}.more-menu hr{height:1px;margin:4px 7px;border:0;background:var(--line)}.scene-control{position:absolute;z-index:12;top:calc(var(--header-height) + 12px);left:14px}.scene-trigger{min-height:44px;display:inline-flex;align-items:center;gap:8px;padding:0 11px 0 12px;border:1px solid var(--line);border-radius:12px;background:var(--material);box-shadow:0 3px 12px #20343a12;-webkit-backdrop-filter:blur(12px) saturate(1.06);backdrop-filter:blur(12px) saturate(1.06);color:var(--text);cursor:pointer;transition:background-color .19s ease,border-color .19s ease,transform .19s ease}.scene-trigger:hover,.scene-trigger[aria-expanded=true]{border-color:var(--line-strong);background:var(--surface)}.scene-trigger:active{transform:scale(.98)}.scene-trigger>span{color:var(--muted);font-size:10px}.scene-trigger>strong{font-size:12px;font-weight:600}.scene-trigger .icon{width:14px;height:14px;color:var(--quiet)}.scene-popover{position:absolute;top:51px;left:0;width:min(356px,calc(100vw - 28px));max-height:min(650px,calc(100svh - 182px));overflow:auto;padding:15px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);scrollbar-color:var(--line-strong) transparent;animation:pop-in .19s ease both}.popover-heading,.sheet-heading,.section-heading-row,.scene-group-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.popover-heading{margin-bottom:13px}.popover-heading>div,.sheet-heading>div{min-width:0}.popover-heading strong,.sheet-heading strong{display:block;color:var(--text);font-size:15px;font-weight:650;letter-spacing:-.01em}.popover-heading span,.sheet-heading span{display:block;margin-top:3px;color:var(--muted);font-size:10px}.profile-strip{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:2px;margin-bottom:13px;padding:2px;border-radius:9px;background:var(--surface-soft)}.profile-strip button{min-width:0;min-height:32px;padding:0 3px;border:0;border-radius:7px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.profile-strip button:hover{color:var(--text)}.profile-strip button.is-active{background:var(--surface);box-shadow:0 1px 3px #1f33391a;color:var(--text);font-weight:600}.scene-group{padding:13px 0;border-top:1px solid var(--line)}.scene-group-label{display:block;margin-bottom:8px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.scene-group-heading{align-items:baseline}.scene-group-heading .scene-group-label{margin-bottom:8px}.scene-group-heading output{color:var(--quiet);font-size:10px}.representation-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}.representation-grid button{min-height:42px;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 10px;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--muted);font-size:11px;text-align:left;cursor:pointer}.representation-grid button:hover:not(:disabled){background:var(--surface-soft);color:var(--text)}.representation-grid button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft);color:var(--text)}.representation-grid button:disabled{color:var(--disabled);cursor:default}.representation-grid button .icon{width:14px;height:14px;color:var(--accent)}.capability-note,.cell-origin-note{display:block;margin-top:7px;color:var(--quiet);font-size:10px;line-height:1.4}.toggle-row,.choice-row{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--text);font-size:12px}.toggle-row.is-disabled,.choice-row.is-disabled{color:var(--disabled)}.toggle-row>button[role=switch],.vim-heading>button[role=switch]{position:relative;width:34px;height:20px;flex:0 0 auto;padding:0;border:0;border-radius:999px;background:var(--line-strong);cursor:pointer;transition:background-color .19s ease}.toggle-row>button[role=switch] i,.vim-heading>button[role=switch] i{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:0 1px 3px #14232833;transition:transform .19s ease}.toggle-row>button[role=switch][aria-checked=true],.vim-heading>button[role=switch][aria-checked=true]{background:var(--accent)}.toggle-row>button[role=switch][aria-checked=true] i,.vim-heading>button[role=switch][aria-checked=true] i{transform:translate(14px)}.toggle-row>button[role=switch]:disabled{cursor:default;opacity:.55}.mini-segmented,.settings-segmented{display:flex;min-height:34px;padding:2px;border-radius:8px;background:var(--surface-soft)}.mini-segmented{width:150px}.mini-segmented button,.settings-segmented button{min-width:0;min-height:30px;flex:1 1 0;padding:0 7px;border:0;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.mini-segmented button.is-active,.settings-segmented button.is-active{background:var(--surface);box-shadow:0 1px 3px #1f33391a;color:var(--text);font-weight:600}.image-presets{display:flex;gap:4px;margin-bottom:8px}.image-presets button{min-height:32px;flex:1 1 0;padding:0 8px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.image-presets button:hover{border-color:var(--line-strong);background:var(--surface-soft);color:var(--text)}.image-presets button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft);color:var(--text)}.image-ranges{display:grid;gap:5px}.image-axis{min-height:36px;display:grid;grid-template-columns:22px 1fr 10px 1fr;align-items:center;padding:0 6px;border:1px solid var(--line);border-radius:8px}.image-axis>span{color:var(--muted);font-family:var(--numeric);font-size:10px}.image-axis>i{color:var(--quiet);font-style:normal;font-size:10px;text-align:center}.image-axis select{min-width:0;width:100%;height:30px;padding:0 4px;border:0;background:transparent;color:var(--text);font-family:var(--numeric);font-size:10px;text-align:center}.image-axis.is-disabled{opacity:.42}.scene-actions{display:flex;align-items:center;justify-content:flex-start;gap:5px;margin:0 -15px -15px;padding:11px 15px 15px;border-top:1px solid var(--line);background:var(--surface)}.scene-actions button{min-height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.scene-actions button:hover{background:var(--surface-soft);color:var(--text)}.scene-actions button.is-active{background:var(--accent-soft);color:var(--text)}.orientation-control{position:absolute;z-index:11;top:calc(var(--header-height) + 12px);right:14px;display:flex;padding:3px;border:1px solid var(--line);border-radius:12px;background:var(--material);box-shadow:0 3px 12px #20343a12;-webkit-backdrop-filter:blur(12px) saturate(1.06);backdrop-filter:blur(12px) saturate(1.06)}.orientation-control button{min-width:38px;height:36px;display:grid;place-items:center;padding:0 7px;border:0;border-radius:8px;background:transparent;color:var(--quiet);font-family:var(--numeric);font-size:10px;cursor:pointer}.orientation-control button:hover,.orientation-control button.is-active{background:var(--surface-soft);color:var(--text)}.orientation-control button.is-active{font-weight:650}.orientation-control button .icon{width:18px;height:18px;color:var(--accent)}.inspector{position:absolute;z-index:14;top:calc(var(--header-height) + 12px);right:14px;bottom:76px;width:316px;overflow:auto;padding:16px 18px 20px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.04);backdrop-filter:blur(16px) saturate(1.04);visibility:hidden;pointer-events:none;opacity:0;transform:translate(14px) scale(.99);transform-origin:top right;transition:opacity .19s ease,transform .19s ease,visibility 0ms linear .19s;scrollbar-color:var(--line-strong) transparent}.inspector.is-open{visibility:visible;pointer-events:auto;opacity:1;transform:translate(0) scale(1);transition-delay:0ms}.panel-heading{min-height:36px;display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:7px}.panel-heading h2{margin:0;font-size:16px;line-height:1.3;font-weight:650;letter-spacing:-.01em}.close-inspector{width:32px;min-width:32px;height:32px;padding:0}.readout-section{padding:13px 0}.readout-section+.readout-section{border-top:1px solid var(--line)}.readout-section h3{margin:0 0 9px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.section-heading-row{min-height:18px;align-items:baseline;margin-bottom:8px}.section-heading-row h3{margin:0}.section-heading-row>span,.section-heading-row>output{color:var(--quiet);font-size:10px}.readout{min-height:27px;display:grid;grid-template-columns:minmax(82px,.82fr) minmax(0,1.18fr);align-items:baseline;gap:10px}.readout span{color:var(--muted);font-size:12px}.readout strong{overflow:hidden;color:var(--text);font-family:var(--numeric);font-size:10px;font-weight:500;text-align:right;text-overflow:ellipsis;white-space:nowrap}.readout.is-accent strong{color:var(--accent);font-weight:650}.cell-metrics-section .readout{grid-template-columns:66px minmax(0,1fr)}.quiet-copy{margin:2px 0 4px;color:var(--muted);font-size:11px;line-height:1.5}.vector-readout{margin:8px 0 9px}.vector-readout>span{display:block;margin-bottom:6px;color:var(--muted);font-size:11px}.vector-readout code{display:grid;grid-template-columns:14px 1fr;row-gap:5px;padding-left:9px;border-left:2px solid var(--line-strong);color:var(--text);font-family:var(--numeric);font-size:10px;line-height:1.25}.vector-readout code i{color:var(--quiet);font-style:normal}.vector-readout code b{position:absolute;right:18px;color:var(--quiet);font-size:10px;font-weight:500}.force-scale{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px}.force-scale>span{color:var(--quiet);font-family:var(--numeric);font-size:10px}.timeline{position:absolute;z-index:18;left:50%;bottom:12px;width:min(960px,calc(100% - 28px));min-height:52px;padding:5px 9px 8px;border:1px solid var(--line);border-radius:14px;background:var(--material);box-shadow:0 5px 20px #1f333917;-webkit-backdrop-filter:blur(14px) saturate(1.04);backdrop-filter:blur(14px) saturate(1.04);transform:translate(-50%)}.timeline.is-compact{height:52px;padding-block:4px}.transport-row{min-height:42px;display:flex;align-items:center;gap:10px}.transport-buttons{display:flex;flex:0 0 auto;align-items:center}.transport-button,.play-button{width:40px;height:40px;display:grid;place-items:center;padding:0;border:0;border-radius:9px;background:transparent;color:var(--muted);cursor:pointer}.play-button{color:var(--text)}.transport-button:hover:not(:disabled),.play-button:hover:not(:disabled){background:var(--surface-soft);color:var(--accent)}.transport-button:disabled,.play-button:disabled{opacity:.28;cursor:default}.transport-button .icon,.play-button .icon{width:17px;height:17px}.scrubber{min-width:40px;flex:1 1 auto;display:flex;align-items:center}input[type=range]{width:100%;height:28px;margin:0;appearance:none;background:transparent;cursor:pointer}input[type=range]::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:var(--line-strong)}input[type=range]::-webkit-slider-thumb{width:13px;height:13px;margin-top:-5px;appearance:none;border:2px solid var(--surface);border-radius:50%;background:var(--accent);box-shadow:0 0 0 1px var(--accent)}input[type=range]::-moz-range-track{height:3px;border-radius:3px;background:var(--line-strong)}input[type=range]::-moz-range-thumb{width:11px;height:11px;border:2px solid var(--surface);border-radius:50%;background:var(--accent)}.frame-counter{min-width:74px;color:var(--text);font-size:10px;font-weight:600;text-align:right}.speed-control select,.plot-label select{border:0;background:transparent;cursor:pointer}.speed-control select{width:54px;padding:6px 2px 6px 5px;color:var(--muted);font-family:var(--numeric);font-size:10px;text-align:right}.plot-row{position:relative;height:67px;display:flex;align-items:stretch;gap:12px;padding-top:3px;border-top:1px solid var(--line)}.plot-label{width:106px;display:flex;flex:0 0 auto;flex-direction:column;justify-content:center;gap:3px;overflow:hidden}.plot-label select{width:100%;overflow:hidden;padding:0;color:var(--text);font-size:11px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.plot-label small{color:var(--quiet);font-family:var(--numeric);font-size:10px}.series-plot{position:relative;min-width:0;flex:1 1 auto}.series-plot svg{width:100%;height:100%;display:block;overflow:visible;cursor:crosshair;touch-action:none}.plot-grid{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}.series-line{fill:none;stroke:var(--muted);stroke-width:1.5;vector-effect:non-scaling-stroke}.empty-series-line{stroke:var(--line-strong);stroke-width:1;stroke-dasharray:4 6;vector-effect:non-scaling-stroke}.frame-marker{stroke:var(--accent);stroke-width:1.25;opacity:.82;vector-effect:non-scaling-stroke}.frame-point{fill:var(--surface);stroke:var(--accent);stroke-width:2}.plot-range{position:absolute;inset:4px 3px 4px auto;display:flex;flex-direction:column;justify-content:space-between;color:var(--quiet);font-family:var(--numeric);font-size:10px;pointer-events:none}.frame-error{position:static;max-width:76px;flex:0 1 auto;overflow:hidden;padding:3px 6px;border-radius:5px;background:var(--surface);color:var(--error);font-size:10px;text-overflow:ellipsis;white-space:nowrap}.frame-error-compact{display:none}.command-backdrop,.customize-backdrop{position:absolute;z-index:50;inset:var(--header-height) 0 0;background:#141f232e;animation:fade-in .19s ease both}.command-backdrop{display:grid;place-items:start center;padding:min(14vh,120px) 16px 24px}:root[data-appearance=dark] .command-backdrop,:root[data-appearance=dark] .customize-backdrop{background:#00000057}.command-palette{width:min(560px,100%);max-height:min(620px,calc(100svh - 150px));overflow:hidden;border:1px solid var(--line);border-radius:16px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);animation:palette-in .21s ease both}.command-search{height:56px;display:flex;align-items:center;gap:10px;padding:0 15px;border-bottom:1px solid var(--line)}.command-search .icon{width:19px;height:19px;color:var(--quiet)}.command-search input{min-width:0;flex:1 1 auto;border:0;outline:0;background:transparent;color:var(--text);font-size:15px}.command-search input::placeholder{color:var(--quiet)}.command-search kbd{color:var(--quiet);font-size:10px}.command-results{max-height:min(500px,calc(100svh - 220px));overflow:auto;padding:6px;scrollbar-color:var(--line-strong) transparent}.command-results>button{width:100%;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:0 11px;border:0;border-radius:9px;background:transparent;color:var(--text);font-size:12px;text-align:left;cursor:pointer}.command-results>button:hover,.command-results>button.is-active,.command-results>button[aria-selected=true]{background:var(--accent-soft)}.command-results>button[aria-disabled=true]{color:var(--disabled);cursor:default}.command-results>button kbd{color:var(--quiet);font-size:10px}.command-results>button small{max-width:56%;color:var(--quiet);font-size:10px;line-height:1.35;text-align:right}.command-results>p{margin:0;padding:32px 18px;color:var(--muted);font-size:12px;text-align:center}.shortcut-backdrop{padding-top:min(10vh,76px)}.shortcut-panel{width:min(720px,100%);max-height:min(680px,calc(100svh - 120px));overflow:auto;border:1px solid var(--line);border-radius:16px;background:var(--surface);box-shadow:var(--shadow);animation:palette-in .21s ease both;scrollbar-color:var(--line-strong) transparent}.shortcut-heading,.vim-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px}.shortcut-heading{min-height:66px;padding:15px 16px 12px 18px;border-bottom:1px solid var(--line)}.shortcut-heading strong,.shortcut-heading span,.vim-heading strong,.vim-heading span{display:block}.shortcut-heading strong{color:var(--text);font-size:14px;font-weight:650}.shortcut-heading span,.vim-heading span{margin-top:3px;color:var(--quiet);font-size:10px}.shortcut-groups{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,13.5em),1fr));gap:12px 16px;padding:4px 18px 14px}.shortcut-groups>section{min-width:0;padding:13px 0 4px}.shortcut-groups>section+section{padding-left:0;border-left:0}.shortcut-groups h3{margin:0 0 7px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.shortcut-row{min-height:32px;display:grid;grid-template-columns:minmax(7em,max-content) minmax(0,1fr);align-items:center;gap:9px;color:var(--muted);font-size:10px}.shortcut-row>span{min-width:0;overflow-wrap:anywhere}.shortcut-row kbd{width:fit-content;max-width:100%;padding:3px 5px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--text);font-size:9px;white-space:nowrap}.vim-shortcuts{padding:14px 18px 17px;border-top:1px solid var(--line)}.vim-heading{align-items:center}.vim-heading strong{color:var(--text);font-size:11px;font-weight:650}.vim-shortcut-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,11.5em),1fr));gap:0 12px;margin-top:9px;transition:opacity .18s ease}.vim-shortcuts:not(.is-active) .vim-shortcut-grid{opacity:.62}.customize-sheet{position:absolute;z-index:51;top:12px;right:14px;bottom:14px;width:min(376px,calc(100% - 28px));overflow:auto;padding:17px 18px 18px;border:1px solid var(--line);border-radius:16px;background:var(--material);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px) saturate(1.06);backdrop-filter:blur(16px) saturate(1.06);animation:sheet-in .21s ease both;scrollbar-color:var(--line-strong) transparent}.render-sheet{bottom:auto;max-height:calc(100svh - var(--header-height) - 26px);background:var(--surface);-webkit-backdrop-filter:none;backdrop-filter:none}.sheet-heading{min-height:36px;align-items:flex-start;padding-bottom:11px}.settings-section{padding:14px 0;border-top:1px solid var(--line)}.settings-section h3{margin:0 0 10px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.06em;text-transform:uppercase}.settings-section>small{display:block;margin-top:8px;color:var(--quiet);font-size:10px;line-height:1.4}.settings-link{width:100%;min-height:38px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0;border:0;border-top:1px solid var(--line);background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.settings-link:hover{color:var(--text)}.settings-link kbd{color:var(--quiet);font-size:10px}.keyboard-settings .toggle-row{min-height:38px}.inline-settings{display:grid;gap:7px}.inline-setting{display:grid;grid-template-columns:76px minmax(0,1fr);align-items:center;gap:9px}.inline-setting>span{color:var(--muted);font-size:10px}.customize-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:14px;border-top:1px solid var(--line)}.customize-pair .settings-section{min-width:0;border-top:0}#customize-sheet .settings-section{padding:12px 0}.geometry-settings{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.slider-settings .geometry-settings label{min-width:0;min-height:44px;grid-template-columns:1fr;align-content:center;gap:2px}.settings-segmented{min-height:36px;border-radius:9px}.settings-segmented button{min-height:32px;border-radius:7px;font-size:10px}.settings-choice{width:100%;min-height:54px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--text);text-align:left;cursor:pointer}.settings-choice:hover,.settings-choice.is-active{background:var(--surface-soft)}.settings-choice.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line))}.settings-choice strong,.settings-choice small{display:block}.settings-choice strong{font-size:11px;font-weight:600}.settings-choice small{margin-top:3px;color:var(--quiet);font-size:10px}.settings-choice .icon{width:15px;height:15px;color:var(--accent)}.render-presets{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.render-presets button{min-height:54px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:0 6px;border:1px solid var(--line);border-radius:9px;background:transparent;color:var(--text);cursor:pointer}.render-presets button:hover{border-color:var(--line-strong);background:var(--surface-soft)}.render-presets button.is-active{border-color:color-mix(in srgb,var(--accent) 32%,var(--line));background:var(--accent-soft)}.render-presets strong{font-size:11px;font-weight:600}.render-presets small{color:var(--muted);font-family:var(--numeric);font-size:10px}.render-size{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:end;gap:8px}.render-size label{display:grid;gap:6px;color:var(--muted);font-size:10px}.render-size input{min-width:0;width:100%;height:38px;padding:0 9px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--text);font-family:var(--numeric);font-size:11px}.render-size>i{padding-bottom:11px;color:var(--quiet);font-style:normal;font-size:11px}.render-options-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:14px;border-top:1px solid var(--line)}.render-options-grid .settings-section{min-width:0;border-top:0}.slider-settings label{min-height:38px;display:grid;grid-template-columns:86px minmax(0,1fr);align-items:center;gap:10px}.slider-settings label>span{display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--muted);font-size:10px}.slider-settings output{color:var(--text);font-size:10px}.sheet-actions{position:sticky;z-index:1;bottom:-18px;display:flex;justify-content:flex-end;gap:6px;margin:0 -18px -18px;padding:12px 18px 18px;border-top:1px solid var(--line);background:var(--surface)}.sheet-actions>button{min-height:38px;padding:0 13px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.sheet-actions>button:hover{background:var(--surface-soft);color:var(--text)}.sheet-actions>button.primary{background:var(--accent);color:#fff;font-weight:600}.sheet-actions>button:disabled,.render-presets button:disabled,.render-sheet .icon-button:disabled{opacity:.5;cursor:default}.drop-overlay{position:absolute;z-index:60;inset:var(--header-height) 0 0;display:grid;place-items:center;padding:24px;background:color-mix(in srgb,var(--canvas) 82%,transparent);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);pointer-events:none;animation:fade-in .15s ease both}.drop-overlay>div{min-width:min(340px,90vw);padding:28px 32px;border:1px solid color-mix(in srgb,var(--accent) 52%,var(--line));border-radius:16px;background:var(--material);box-shadow:var(--shadow);color:var(--text);text-align:center}.drop-overlay .icon{width:27px;height:27px;margin:0 auto 10px;color:var(--accent)}.drop-overlay strong,.drop-overlay span{display:block}.drop-overlay strong{font-size:16px;font-weight:650}.drop-overlay span{margin-top:6px;color:var(--muted);font-size:11px}.notice{position:absolute;z-index:45;left:50%;bottom:78px;max-width:min(440px,calc(100% - 32px));max-height:min(120px,calc(100% - 24px));overflow:hidden;overflow-wrap:anywhere;padding:9px 13px;border:1px solid var(--line);border-radius:9px;background:var(--material);box-shadow:0 4px 15px #1f333917;color:var(--text);font-size:11px;line-height:1.35;white-space:normal;transform:translate(-50%);animation:notice-in .19s ease both}.notice>span{min-width:0;overflow-wrap:anywhere}.notice.is-error{width:min(440px,calc(100% - 16px));max-width:min(440px,calc(100% - 16px));display:grid;grid-template-columns:minmax(0,1fr) 44px;align-items:start;gap:6px;border-color:color-mix(in srgb,var(--error) 30%,var(--line))}.notice.is-error>span{max-height:100px;overflow:auto;overscroll-behavior:contain;scrollbar-color:var(--line-strong) transparent;scrollbar-gutter:stable}.notice-dismiss{width:44px;height:44px;display:grid;place-items:center;margin:-6px -8px -6px 0;padding:0;border:0;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer}.notice-dismiss:hover{background:var(--surface-soft);color:var(--text)}.notice-dismiss .icon{width:16px;height:16px}.notice.is-busy:before{content:"";width:7px;height:7px;display:inline-block;margin-right:8px;border-radius:50%;background:var(--accent);animation:pulse 1.1s ease-in-out infinite}.centered-state{position:absolute;z-index:18;inset:var(--header-height) 0 0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;background:var(--canvas);text-align:center}.centered-state h1{margin:18px 0 0;font-size:17px;font-weight:650;letter-spacing:-.01em}.centered-state p{max-width:360px;margin:7px 0 0;color:var(--muted);font-size:12px;line-height:1.5}.centered-state button{min-height:40px;display:inline-flex;align-items:center;gap:7px;margin-top:17px;padding:0 13px;border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--text);font-size:11px;cursor:pointer}.centered-state button:hover{border-color:var(--accent);color:var(--accent)}.state-orbit{position:relative;width:48px;height:48px}.state-orbit i{position:absolute;inset:13px 2px;border:1px solid var(--muted);border-radius:50%;transform:rotate(30deg)}.state-orbit i:nth-child(2){transform:rotate(-30deg)}.state-orbit b{position:absolute;top:21px;left:21px;width:6px;height:6px;border-radius:50%;background:var(--accent)}.state-orbit.is-busy{animation:orbit-spin 1.8s linear infinite}@keyframes pop-in{0%{opacity:0;transform:translateY(-5px) scale(.985)}}@keyframes palette-in{0%{opacity:0;transform:translateY(-8px) scale(.985)}}@keyframes sheet-in{0%{opacity:0;transform:translate(12px) scale(.99)}}@keyframes mobile-sheet-in{0%{opacity:0;transform:translateY(18px) scale(.99)}}@keyframes notice-in{0%{opacity:0;transform:translate(-50%,6px)}}@keyframes fade-in{0%{opacity:0}}@keyframes orbit-spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.35}}@media(max-width:760px){.panel-button,.render-button{min-width:44px;flex-shrink:0}.scene-status,.command-button{display:none}.topbar{padding-right:4px}.open-button,.customize-button,.more-button,.icon-button{min-width:44px;height:44px}.more-menu button,.profile-strip button,.representation-grid button,.image-presets button,.scene-actions button,.sheet-actions>button,.centered-state button{min-height:44px}.mini-segmented,.settings-segmented{min-height:48px}.mini-segmented button,.settings-segmented button{min-height:44px}.image-axis{min-height:48px}.image-axis select,.speed-control select,.plot-label select,.render-size input,input[type=range]{height:44px}.toggle-row>button[role=switch],.vim-heading>button[role=switch]{width:44px;height:44px;background:transparent}.toggle-row>button[role=switch]:before,.vim-heading>button[role=switch]:before{content:"";position:absolute;top:12px;left:5px;width:34px;height:20px;border-radius:999px;background:var(--line-strong);transition:background-color .19s ease}.toggle-row>button[role=switch] i,.vim-heading>button[role=switch] i{top:15px;left:8px}.toggle-row>button[role=switch][aria-checked=true],.vim-heading>button[role=switch][aria-checked=true]{background:transparent}.toggle-row>button[role=switch][aria-checked=true]:before,.vim-heading>button[role=switch][aria-checked=true]:before{background:var(--accent)}.scene-control{top:calc(var(--header-height) + 8px);left:8px}.scene-popover{position:fixed;z-index:40;inset:auto 8px 70px;width:auto;max-height:min(68svh,610px);border-radius:16px;animation-name:mobile-sheet-in}.orientation-control{top:calc(var(--header-height) + 8px);right:8px}.orientation-control button{min-width:44px;height:44px}.inspector{position:fixed;z-index:36;inset:auto 8px 70px;width:auto;max-height:min(56svh,520px);opacity:0;transform:translateY(18px) scale(.99);transform-origin:bottom center}.inspector.is-open{opacity:1;transform:translateY(0) scale(1)}.timeline{bottom:8px;width:calc(100% - 16px);padding-inline:5px;border-radius:13px}.transport-row{gap:4px}.transport-button,.play-button{width:44px;height:44px}.frame-counter{min-width:58px;font-size:10px}.speed-control select{width:46px}.plot-row{height:72px;gap:7px}.plot-label{width:74px}.plot-range{display:none}.notice{bottom:72px}.command-backdrop{position:fixed;inset:0;align-items:end;padding:8px}.command-palette{max-height:min(74svh,620px);border-radius:17px;animation-name:mobile-sheet-in}.command-results{max-height:calc(74svh - 56px)}.command-results>button{min-height:48px}.shortcut-panel{max-height:calc(100svh - 16px);border-radius:17px;animation-name:mobile-sheet-in}.shortcut-groups{grid-template-columns:1fr}.shortcut-groups>section{padding:13px 0 8px}.shortcut-groups>section+section{padding-left:0;border-top:1px solid var(--line);border-left:0}.vim-shortcut-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.customize-backdrop{position:fixed;inset:0}.customize-sheet{position:absolute;inset:auto 8px 8px;width:auto;max-height:min(78svh,660px);border-radius:17px;animation-name:mobile-sheet-in}.render-sheet{max-height:calc(100svh - 68px)}.toggle-row,.choice-row{min-height:48px}}.scrubber-shell{position:relative;min-width:40px;flex:1 1 auto}.scrubber-shell .scrubber{width:100%}.trajectory-marker-rail{position:absolute;right:6px;bottom:2px;left:6px;height:8px;pointer-events:none}.trajectory-marker{position:absolute;top:-8px;width:24px;height:24px;margin:0;padding:0;border:0;background:transparent;cursor:pointer;pointer-events:auto;transform:translate(-50%)}.trajectory-marker:after{position:absolute;top:9px;left:11px;width:3px;height:6px;border-radius:2px;background:var(--quiet);content:""}.trajectory-marker.is-reference:after{top:8px;left:8px;width:7px;height:7px;border:1px solid var(--surface);border-radius:1px;background:var(--accent);transform:rotate(45deg)}.timeline-options>div{max-height:min(620px,calc(100vh - var(--header-height) - var(--timeline-height) - 24px));overflow-y:auto}.timeline-action-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}.timeline-action-list button{min-width:0;min-height:32px;overflow:hidden;padding:0 8px;border:1px solid var(--line-soft);border-radius:5px;background:var(--surface);color:var(--text);font-size:10px;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.timeline-action-list button:hover{border-color:var(--line-strong);background:var(--surface-soft)}.timeline-action-list button:disabled{opacity:.42;cursor:default}@media(max-width:760px){.timeline-action-list button{min-height:40px}}.measurement-plot__header{overflow:hidden}.measurement-plot__meta{min-width:72px;max-width:220px;flex:0 1 220px;display:flex;align-items:baseline;gap:7px;overflow:hidden}.measurement-plot__meta strong{min-width:0;overflow:hidden;color:var(--text);font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:10px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.measurement-plot__meta span{flex:0 0 auto;white-space:nowrap}.measurement-plot__legend{min-width:0;flex:1 1 auto;display:flex;align-items:center;gap:3px;overflow-x:auto;scrollbar-width:none}.measurement-plot__legend::-webkit-scrollbar{display:none}.measurement-plot__legend-item{min-width:0;height:26px;display:inline-flex;flex:0 1 auto;align-items:center;gap:5px;overflow:hidden;padding:0 6px;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:9px;white-space:nowrap}button.measurement-plot__legend-item{cursor:pointer}button.measurement-plot__legend-item:hover{background:var(--surface-soft);color:var(--text)}.measurement-plot__legend-item>span:not(.measurement-plot__legend-swatch){max-width:150px;overflow:hidden;text-overflow:ellipsis}.measurement-plot__legend-item output{color:var(--text);font-family:var(--numeric)}.measurement-plot__legend-swatch{width:8px;height:2px;flex:0 0 auto;border-radius:2px}.measurement-plot__header-actions,.measurement-plot__context-actions{flex:0 0 auto;display:flex;align-items:center}.measurement-plot__close{width:30px;min-width:30px!important;padding:0!important;font-size:17px!important;font-weight:400!important}.rdf-view-toggle{display:flex;padding:2px;border-radius:5px;background:var(--surface-soft)}.rdf-view-toggle button{min-width:38px;height:26px;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--muted);font:600 10px var(--numeric);cursor:pointer}.rdf-view-toggle button.is-active{background:var(--surface);color:var(--accent);box-shadow:0 1px 3px color-mix(in srgb,var(--text) 12%,transparent)}.rdf-sheet{position:absolute;z-index:42;bottom:calc(var(--timeline-height) + 12px);left:50%;width:min(390px,calc(100% - 24px));max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 24px);display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 14px 44px color-mix(in srgb,var(--text) 16%,transparent);transform:translate(-50%)}.rdf-sheet>header,.rdf-sheet>footer{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 12px}.rdf-sheet>header{border-bottom:1px solid var(--line-soft)}.rdf-sheet>header strong{font-size:12px}.rdf-sheet>header button{width:32px;height:32px;padding:0;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:18px;cursor:pointer}.rdf-sheet__body{display:grid;gap:8px;overflow-y:auto;overscroll-behavior:contain;padding:12px}.rdf-sheet__body>label,.rdf-sheet__body details>div>label{display:grid;grid-template-columns:64px minmax(0,1fr);align-items:center;gap:9px;color:var(--muted);font-size:10px}.rdf-sheet select,.rdf-sheet input{width:100%;height:36px;min-width:0;padding:0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:11px}.rdf-sheet__body details{padding-top:2px}.rdf-sheet__body summary{color:var(--muted);font-size:10px;cursor:pointer}.rdf-sheet__body details>div{display:grid;gap:8px;padding-top:8px}.rdf-sheet>footer{border-top:1px solid var(--line-soft);color:var(--quiet);font-size:9px}.rdf-sheet>footer button{min-width:72px;height:34px;border:0;border-radius:6px;background:var(--accent);color:var(--surface);font-size:10px;font-weight:650;cursor:pointer}.rdf-sheet>footer button:disabled{opacity:.35;cursor:default}.pinned-measurements{display:block;max-width:none;max-height:none;overflow:visible;padding:0}.pinned-measurements>summary{height:34px;display:flex;align-items:center;padding:0 11px;border:1px solid var(--line);border-radius:8px;background:color-mix(in srgb,var(--surface) 94%,transparent);box-shadow:0 3px 12px color-mix(in srgb,var(--text) 7%,transparent);color:var(--muted);font-size:10px;font-weight:650;cursor:pointer;list-style:none}.pinned-measurements>summary::-webkit-details-marker{display:none}.pinned-measurements[open]>summary{border-color:var(--line-strong);color:var(--text)}.pinned-measurements>section{position:absolute;top:calc(100% + 6px);left:0;width:min(360px,calc(100vw - 24px));overflow:hidden;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:var(--shadow)}.pinned-measurements>section>header{min-height:40px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 11px;border-bottom:1px solid var(--line-soft)}.pinned-measurements>section>header strong{font-size:10px}.pinned-measurements>section>header button{height:30px;padding:0 9px;border:0;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:10px;font-weight:650;cursor:pointer}.pinned-measurements__list{max-height:250px;overflow-y:auto;padding:5px}.pinned-measurements__list>div{min-width:0;display:flex;align-items:stretch}.pinned-measurements .selection-chip{flex:1 1 auto;width:auto;max-width:none;box-shadow:none}@media(max-width:520px){.measurement-plot__meta{max-width:92px;flex-basis:92px}.measurement-plot__meta span{display:none}.measurement-plot__legend-item>span:not(.measurement-plot__legend-swatch){max-width:82px}.measurement-plot__actions button{min-width:36px;width:36px;padding-inline:3px}.rdf-sheet{bottom:calc(var(--timeline-height) + 8px);width:calc(100% - 16px)}.rdf-sheet select,.rdf-sheet input,.rdf-sheet>footer button{height:40px}.rdf-sheet>header button{width:40px;height:40px}.pinned-measurements{top:calc(var(--header-height) + 58px)}.selection-bar .selection-track-button{display:none}}@media(max-height:420px){.pinned-measurements[open]{z-index:45}.pinned-measurements>section{position:fixed;top:calc(var(--header-height) + 100px);bottom:calc(var(--timeline-height) + 8px);left:8px;width:min(360px,calc(100vw - 16px));display:grid;grid-template-rows:auto minmax(0,1fr)}.pinned-measurements__list{min-height:0;max-height:none;overscroll-behavior:contain}}@media(max-width:380px){.rdf-view-toggle button{min-width:34px;padding-inline:5px}}@media(max-width:520px){.identity>div{display:block}.identity strong,.identity span:last-child{display:block}.identity span:last-child{max-width:42vw;margin-top:2px;font-size:10px}.identity-mark{width:27px;height:27px}.open-button{width:44px;padding:0;font-size:0}.open-button .icon{width:18px;height:18px}.scene-trigger>span{display:none}.scene-trigger>strong{max-width:178px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orientation-control button{min-width:44px;padding-inline:5px}.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{width:44px}.frame-counter{min-width:54px}.speed-control select{width:42px;padding-inline:1px}}.workspace.timeline-absent{--timeline-height: 0px;--selection-bottom: 14px}.workspace.timeline-present{--timeline-height: 56px;--selection-bottom: 68px}.canvas-controls>button.is-active{background:var(--accent-soft);color:var(--accent);font-weight:650}.section-label{display:block;margin-bottom:8px;color:var(--quiet);font-size:10px;font-weight:700;letter-spacing:.075em;line-height:1.2;text-transform:uppercase}.section-label-spaced{margin-top:14px}.segmented-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px;padding:2px;border:1px solid var(--line);border-radius:8px;background:var(--surface-soft)}.segmented-options button{min-width:0;min-height:34px;padding:0 6px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.segmented-options button:hover:not(:disabled){color:var(--text)}.segmented-options button.is-active{border-color:var(--line);background:var(--surface);color:var(--accent);font-weight:650}.representation-options{grid-template-columns:repeat(2,minmax(0,1fr))}.display-toggles .section-label{margin:4px 0 2px}.display-toggles .vector-scale-row+.toggle-row{border-top:1px solid var(--line)}.vector-scale-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 10px;padding:1px 0 9px 12px;color:var(--muted);font-size:10px}.vector-scale-row input{grid-column:1 / -1;min-width:0}.vector-scale-row output{color:var(--text);font-size:9px}.selection-bar{position:absolute;z-index:16;bottom:var(--selection-bottom);left:50%;width:max-content;max-width:min(640px,calc(100% - 24px));min-height:48px;display:flex;align-items:center;gap:10px;padding:5px 5px 5px 13px;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:0 5px 18px color-mix(in srgb,var(--text) 10%,transparent);transform:translate(-50%)}.selection-readout{min-width:0;flex:1 1 auto;display:flex;align-items:baseline;gap:9px;overflow:hidden}.selection-readout strong,.selection-readout output{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.selection-readout strong{min-width:0;flex:1 1 auto;font-size:11px}.selection-readout output{flex:0 1 auto;max-width:100%;color:var(--accent);font-size:10px}.selection-hint{color:var(--quiet);font-size:10px;white-space:nowrap}.selection-bar>button:not(.icon-button){flex:0 0 auto;min-width:56px;height:36px;padding:0 9px;border:0;border-radius:5px;background:transparent;color:var(--accent);font-size:10px;font-weight:650;cursor:pointer}.selection-bar>button:not(.icon-button):hover{background:var(--accent-soft)}.selection-bar .measurement-mode[aria-pressed=true]{background:var(--accent-soft)}.measurement-mode-compact{display:none}.selection-bar .icon-button{width:36px;padding:0}.timeline,.timeline.is-compact{height:var(--timeline-height);overflow:visible;padding:0 10px}.timeline .transport-row{min-height:var(--timeline-height);gap:9px}.transport-buttons{gap:0}.transport-button,.play-button{width:36px;height:40px;border-radius:5px}.play-button{color:var(--accent)}.frame-counter{min-width:66px;font-variant-numeric:tabular-nums}.frame-counter-compact{display:none}.frame-metadata{max-width:140px;overflow:hidden;color:var(--muted);font-family:var(--numeric);font-size:10px;text-overflow:ellipsis;white-space:nowrap}.timeline-options{position:relative;flex:0 0 auto}.timeline-options>summary{width:40px;height:40px;display:grid;place-items:center;border-radius:5px;color:var(--muted);cursor:pointer;list-style:none}.timeline-options>summary::-webkit-details-marker{display:none}.timeline-options>summary:hover,.timeline-options[open]>summary{background:var(--surface-soft);color:var(--text)}.timeline-options>div{position:absolute;right:0;bottom:calc(100% + 8px);width:244px;display:grid;gap:10px;padding:12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:var(--shadow)}.timeline-options>div>label{min-height:34px;display:grid;grid-template-columns:1fr 112px;align-items:center;gap:12px;color:var(--muted);font-size:10px}.timeline-options select{width:100%;height:34px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--text);font-size:10px}.timeline-options .section-label{margin:3px 0 -3px}.workspace.timeline-present .notice{bottom:calc(var(--timeline-height) + 12px)}.workspace.selection-present .notice{bottom:calc(var(--selection-bottom) + 58px)}@media(min-width:761px){.workbench-open .selection-bar{left:calc((100% - var(--workbench-width) - 24px) / 2);max-width:calc(100% - var(--workbench-width) - 48px)}}@media(max-width:760px){.canvas-controls{right:8px;left:8px;width:auto;height:50px}.canvas-controls>button{min-width:0;height:44px;flex:1 1 0}.frame-metadata{display:none}.selection-bar{max-width:calc(100% - 16px);min-height:52px;gap:5px;padding:4px 4px 4px 11px}.selection-readout{flex:1 1 auto;flex-direction:column;align-items:flex-start;gap:1px}.selection-bar>button:not(.icon-button),.selection-bar .icon-button,.timeline-options>summary{height:44px}}@media(max-width:520px){.workspace.selection-present .workbench{max-height:calc(100% - var(--header-height) - var(--timeline-height) - 80px)}.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{display:none}.timeline,.timeline.is-compact{padding-inline:4px}.timeline .transport-row{gap:4px}.transport-button,.play-button{width:40px;height:44px}.scrubber{min-width:36px}.frame-counter{min-width:54px;font-size:10px}.timeline-options>div{position:fixed;right:8px;bottom:calc(var(--timeline-height) + 8px);width:min(244px,calc(100vw - 16px))}}@media(max-width:360px){.speed-control{display:none}.representation-grid{grid-template-columns:1fr}}@media(max-width:340px){.orientation-control{top:calc(var(--header-height) + 60px)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}:root{--header-height: 48px;--viewport-toolbar-height: 46px;--workbench-width: 344px}.workspace{--timeline-height: 126px;background:var(--canvas)}.workspace.timeline-compact{--timeline-height: 58px}.molecule-canvas,.canvas-field{top:calc(var(--header-height) + var(--viewport-toolbar-height));width:100%;height:calc(100% - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height));transition:width .18s ease}.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:calc(100% - var(--workbench-width))}.topbar{height:var(--header-height);padding:0 8px 0 12px;gap:12px;background:var(--surface);box-shadow:none}.identity-mark{width:30px;height:30px}.identity strong{font-size:13px}.topbar-tools,.panel-button,.inspect-button,.render-button{display:flex;align-items:center}.topbar-tools{gap:4px}.open-button,.panel-button,.inspect-button,.render-button,.command-button,.more-button,.icon-button{min-width:36px;height:34px;border-radius:7px}.open-button,.panel-button,.inspect-button,.render-button{gap:7px;padding:0 10px;border:1px solid transparent;background:transparent;color:var(--muted);font-size:11px;cursor:pointer}.open-button:hover,.panel-button:hover,.panel-button[aria-expanded=true],.inspect-button:hover,.inspect-button[aria-expanded=true]{border-color:var(--line);background:var(--surface-soft);color:var(--text)}.render-button{border-color:var(--accent);background:var(--accent);color:#fff;font-weight:600}.render-button:hover:not(:disabled){background:color-mix(in srgb,var(--accent) 88%,#000)}.render-button:disabled{border-color:var(--line);background:var(--surface-soft);color:var(--disabled);cursor:default}.figure-control{display:flex;align-items:center}.figure-control .render-button{border-radius:7px 0 0 7px}.figure-options-button{width:30px;height:34px;display:grid;place-items:center;padding:0;border:1px solid var(--accent);border-left-color:color-mix(in srgb,var(--accent) 68%,#fff);border-radius:0 7px 7px 0;background:var(--accent);color:#fff;cursor:pointer}.figure-options-button:hover:not(:disabled),.figure-options-button[aria-expanded=true]{background:color-mix(in srgb,var(--accent) 84%,#000)}.figure-options-button:disabled{border-color:var(--line);background:var(--surface-soft);color:var(--disabled);cursor:default}.figure-options-button .icon{width:15px;height:15px}.open-button:disabled,.panel-button:disabled,.inspect-button:disabled,.command-button:disabled,.more-button:disabled{color:var(--disabled);cursor:default;opacity:.52}.panel-button .icon,.render-button .icon{width:16px;height:16px}.viewport-toolbar{position:absolute;z-index:11;top:var(--header-height);left:0;right:0;height:var(--viewport-toolbar-height);display:flex;align-items:center;gap:4px;padding:0 10px;border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--surface) 94%,var(--canvas));transition:right .18s ease}.workbench-open .viewport-toolbar{right:var(--workbench-width)}.viewport-preset{min-width:58px;color:var(--muted);font-size:10px;font-weight:650;letter-spacing:.05em;text-transform:uppercase}.viewport-toolbar>label{height:32px;display:flex;align-items:center;gap:3px;padding:0 3px 0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface)}.viewport-toolbar>label>span{color:var(--quiet);font-size:9px;font-weight:600;text-transform:uppercase}.viewport-toolbar select{height:30px;max-width:126px;padding:0 24px 0 5px;border:0;background:transparent;color:var(--text);font-size:11px;font-weight:550;cursor:pointer}.viewport-toolbar>button{height:32px;padding:0 9px;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.viewport-toolbar>button:hover,.viewport-toolbar>button.is-active{border-color:var(--line);background:var(--surface);color:var(--text)}.viewport-toolbar>button.is-active{color:var(--accent);font-weight:650}.viewport-divider{width:1px;height:22px;margin:0 3px;background:var(--line)}.viewport-toolbar .orientation-control{position:static;display:flex;margin-left:auto;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.viewport-toolbar .orientation-control button{min-width:34px;height:32px;padding:0 6px;border-radius:6px}.viewport-toolbar .orientation-control button .icon{width:16px;height:16px}.mobile-view-select{display:none!important}.viewport-toolbar button:disabled,.viewport-toolbar select:disabled,.workbench button:disabled,.timeline button:disabled,.timeline select:disabled,.timeline input:disabled{cursor:default;opacity:.48}.workbench{position:absolute;z-index:18;top:var(--header-height);right:0;bottom:0;width:var(--workbench-width);display:flex;flex-direction:column;overflow:hidden;border-left:1px solid var(--line);background:var(--surface);animation:workbench-in .18s ease both}.workbench[hidden],.workbench-pane[hidden]{display:none}@keyframes workbench-in{0%{opacity:0;transform:translate(12px)}to{opacity:1;transform:translate(0)}}.workbench-tabs{height:43px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));flex:0 0 auto;border-bottom:1px solid var(--line)}.workbench-tabs button{position:relative;border:0;background:transparent;color:var(--muted);font-size:10px;font-weight:600;cursor:pointer}.workbench-tabs button:hover,.workbench-tabs button.is-active{color:var(--text)}.workbench-tabs button.is-active:after{content:"";position:absolute;right:18px;bottom:-1px;left:18px;height:2px;background:var(--accent)}.workbench-heading{min-height:62px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex:0 0 auto;padding:10px 12px 10px 16px;border-bottom:1px solid var(--line)}.workbench-heading strong,.workbench-heading span{display:block}.workbench-heading strong{color:var(--text);font-size:14px;font-weight:650}.workbench-heading span{margin-top:2px;color:var(--muted);font-size:10px}.workbench-heading-actions{display:flex;align-items:center;gap:2px}.workbench-expand-button{display:none}.workbench-body{min-height:0;flex:1 1 auto;overflow:auto;scrollbar-color:var(--line-strong) transparent}.workbench-footer{min-height:43px;display:flex;align-items:stretch;flex:0 0 auto;border-top:1px solid var(--line);background:var(--surface)}.workbench-footer button{min-width:0;display:flex;align-items:center;gap:6px;flex:1 1 0;padding:0 10px;border:0;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.workbench-footer button:hover{background:var(--surface-soft);color:var(--text)}.workbench-footer button+button{border-left:1px solid var(--line)}.render-workbench-footer{min-height:54px;padding:8px 10px;gap:8px}.render-workbench-footer button{min-height:36px;justify-content:center;border:1px solid var(--line);border-radius:6px;font-weight:600}.render-workbench-footer button+button{border-left:1px solid var(--accent)}.render-workbench-footer .primary{border-color:var(--accent);background:var(--accent);color:#fff}.render-workbench-footer .primary:hover:not(:disabled){background:color-mix(in srgb,var(--accent) 88%,#000)}.workbench-footer .icon{width:14px;height:14px}.workbench-footer kbd{margin-left:auto;color:var(--quiet);font-size:9px}.workbench-section{padding:14px 16px}.workbench-section+.workbench-section{border-top:1px solid var(--line)}.workbench-section>h3,.workbench-section-heading h3,.render-panel .settings-section h3{margin:0 0 9px;color:var(--muted);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.workbench-section-heading{display:flex;align-items:baseline;justify-content:space-between;gap:10px}.workbench-section-heading span,.workbench-section-heading output{color:var(--quiet);font-family:var(--numeric);font-size:9px}.workbench-section-heading h3{margin-bottom:9px}.workbench .profile-strip{margin:0}.panel-select-row{min-height:40px;display:grid;grid-template-columns:104px minmax(0,1fr);align-items:center;gap:10px;border-top:1px solid var(--line);color:var(--text);font-size:11px}.panel-select-row:first-of-type{border-top:0}.panel-select-row select{min-width:0;height:32px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:10px}.panel-details{margin-top:8px;border-top:1px solid var(--line)}.panel-details summary{min-height:38px;display:flex;align-items:center;color:var(--muted);font-size:10px;cursor:pointer}.panel-details[open] summary{color:var(--text)}.panel-details .geometry-settings,.panel-details .image-ranges{padding:2px 0 7px}.layer-heading{min-height:45px;display:flex;align-items:center;justify-content:space-between;gap:12px}.layer-heading strong,.layer-heading span{display:block}.layer-heading strong{font-size:11px;font-weight:600}.layer-heading span{margin-top:2px;color:var(--quiet);font-size:9px}.layer-heading>.toggle-row{min-height:36px}.layer-heading>.toggle-row>span{display:none}.image-heading{margin-top:8px}.panel-slider{display:block;margin-top:8px}.panel-slider>span{display:flex;justify-content:space-between;color:var(--muted);font-size:10px}.panel-slider output{color:var(--text)}.inspector-content{padding:0 16px 16px}.inspector-content .readout-section:first-child{padding-top:14px}.render-panel .settings-section{padding:14px 16px;border-bottom:1px solid var(--line)}.render-panel .render-presets{grid-template-columns:repeat(2,minmax(0,1fr))}.render-panel .render-options-grid{display:block}.render-panel .sheet-actions{position:sticky;bottom:0;margin:0;padding:10px 16px;background:var(--surface)}.render-print-row{min-height:34px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--muted);font-size:10px}.render-print-row select{height:32px;padding:0 24px 0 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font-size:10px}.print-scale-section output{display:block;margin:7px 0 3px;color:var(--text);font-family:var(--numeric);font-size:12px}.render-guide-region{position:absolute;z-index:9;top:calc(var(--header-height) + var(--viewport-toolbar-height));right:0;bottom:var(--timeline-height);left:0;display:grid;place-items:center;overflow:hidden;pointer-events:none;transition:right .18s ease}.workbench-open .render-guide-region{right:var(--workbench-width)}.render-guide{position:relative;flex:0 0 auto;border:1px dashed color-mix(in srgb,var(--accent) 68%,transparent);border-radius:2px;box-shadow:0 0 0 200vmax color-mix(in srgb,var(--canvas) 10%,transparent)}.render-guide span{position:absolute;top:7px;left:8px;padding:2px 5px;border-radius:3px;background:color-mix(in srgb,var(--surface) 90%,transparent);color:var(--accent);font-size:8px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.workspace.is-rendering .molecule-canvas,.workspace.is-rendering .canvas-field{pointer-events:none}.timeline.is-busy .series-plot svg{pointer-events:none}.command-backdrop,.customize-backdrop{position:fixed;inset:0}.preferences-sheet{width:min(440px,calc(100vw - 24px))}.timeline,.timeline.is-compact{left:0;bottom:0;width:100%;height:var(--timeline-height);min-height:0;padding:5px 12px 8px;border:0;border-top:1px solid var(--line);border-radius:0;background:var(--surface);box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;transform:none;transition:width .18s ease}.timeline .transport-row{min-height:46px}.timeline .plot-row{height:70px}.playback-mode-control select{width:92px;height:32px;padding:0 5px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--muted);font-size:9px}.notice{bottom:calc(var(--timeline-height) + 12px)}@media(max-width:960px){.viewport-toolbar>label>span,.viewport-preset{display:none}.viewport-toolbar>label{padding-left:3px}.viewport-toolbar>label,.viewport-toolbar>button{min-height:44px}.viewport-toolbar select{height:42px}.viewport-color-control{display:none!important}.viewport-toolbar .orientation-control{display:none}.viewport-toolbar .mobile-view-select{display:flex!important;margin-left:auto}.viewport-toolbar,.workbench-open .viewport-toolbar{overflow-x:auto;scrollbar-width:none}.viewport-toolbar::-webkit-scrollbar{display:none}.playback-mode-control{display:none}}@media(max-width:1100px){.timeline .jump-button{display:none}}@media(max-width:760px){.workspace{--timeline-height: 128px}.workspace.timeline-compact{--timeline-height: 58px}.molecule-canvas,.canvas-field,.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:100%}.topbar{padding-right:4px}.open-button,.panel-button,.render-button,.more-button,.icon-button{width:44px;min-width:44px;height:44px;padding:0;justify-content:center;font-size:0}.panel-button span,.render-button:not(.primary):after{display:none}.viewport-toolbar,.workbench-open .viewport-toolbar{right:0;gap:3px;overflow-x:auto;padding:0 6px;scrollbar-width:none}.viewport-toolbar::-webkit-scrollbar{display:none}.viewport-toolbar>label,.viewport-toolbar>button,.viewport-toolbar .orientation-control{flex:0 0 auto}.viewport-toolbar>label,.viewport-toolbar>button{min-height:44px}.viewport-toolbar select{height:42px;max-width:112px}.viewport-toolbar .orientation-control{display:none}.viewport-toolbar .mobile-view-select{display:flex!important;margin-left:auto}.workbench{position:fixed;top:auto;right:8px;bottom:calc(var(--timeline-height) + 8px);left:8px;width:auto;height:min(44svh,420px);max-height:calc(100svh - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height) - 24px);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);animation-name:mobile-sheet-in}.workbench.is-expanded{height:min(68svh,600px)}.workbench-expand-button{width:44px;height:44px;display:grid;place-items:center;border:0;border-radius:7px;background:transparent;color:var(--muted)}.workbench-expand-button[aria-expanded=true] .icon{transform:rotate(180deg)}.workbench-tabs{height:46px}.workbench-heading{min-height:58px}.panel-select-row,.panel-details summary,.layer-heading{min-height:44px}.panel-select-row select,.render-print-row select{height:44px}.workbench-footer,.workbench-footer button{min-height:48px}.render-workbench-footer{min-height:56px}.render-workbench-footer button{min-height:44px}.workbench-open .timeline{width:100%}.workbench-open .render-guide-region{right:0}.timeline .jump-button{display:none}.timeline,.timeline.is-compact{bottom:0;width:100%;padding-inline:6px;border-radius:0}.notice{bottom:calc(var(--timeline-height) + 8px)}}@media(max-width:420px){.identity span:last-child{max-width:31vw}.viewport-toolbar>button{padding-inline:8px}.viewport-color-control{display:none!important}.playback-mode-control{display:none}}@media(max-width:520px){.identity span:last-child{display:none}}@media(max-width:760px)and (max-height:520px){.workspace,.workspace.timeline-compact{--timeline-height: 58px}.timeline .plot-row{display:none}.workbench,.workbench.is-expanded{height:calc(100svh - var(--header-height) - var(--viewport-toolbar-height) - var(--timeline-height) - 16px);max-height:none}.workbench-heading,.workbench-expand-button{display:none}}@media(max-width:340px){.viewport-toolbar,.workbench-open .viewport-toolbar{gap:2px;padding-inline:4px}.viewport-style-control select{width:94px;max-width:94px}.mobile-view-select select{width:50px;padding-right:18px}.viewport-toolbar>button{padding-inline:6px}.viewport-divider{margin-inline:1px}}:root{--header-height: 48px;--viewport-toolbar-height: 0px;--workbench-width: 320px;--export-width: 380px}.app-shell,.workspace{min-height:0}.scene-status,.command-button,.viewport-toolbar{display:none}.molecule-canvas,.canvas-field{top:var(--header-height);height:calc(100% - var(--header-height) - var(--timeline-height))}.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - var(--export-width))}.canvas-controls{position:absolute;z-index:12;top:calc(var(--header-height) + 10px);right:12px;height:36px;display:flex;align-items:center;padding:2px;border:1px solid var(--line);border-radius:8px;background:var(--surface);box-shadow:0 4px 14px color-mix(in srgb,var(--text) 7%,transparent);transition:right .18s ease}.workbench-open .canvas-controls{right:calc(var(--workbench-width) + 12px)}.export-open .canvas-controls{right:calc(var(--export-width) + 12px)}.canvas-controls>button,.canvas-controls .orientation-control button{min-width:34px;height:30px;padding:0 8px;border:0;border-radius:6px;background:transparent;color:var(--muted);font-size:10px;cursor:pointer}.canvas-controls>button:hover,.canvas-controls .orientation-control button:hover,.canvas-controls .orientation-control button.is-active{background:var(--surface-soft);color:var(--text)}.canvas-controls .orientation-control{position:static;display:flex;margin:0;padding:0 0 0 2px;border:0;border-left:1px solid var(--line);border-radius:0;background:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.canvas-controls .orientation-control .icon{width:15px;height:15px}.canvas-view-select{display:none}.canvas-controls button:disabled,.canvas-controls select:disabled{opacity:.48;cursor:default}.workbench{top:var(--header-height);left:auto;bottom:0;width:var(--workbench-width);border:0;border-left:1px solid var(--line);border-radius:0;box-shadow:none}.workbench:focus{outline:none}.workbench:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.workbench-heading{min-height:50px;padding:7px 10px 7px 16px}.workbench-heading strong{font-size:13px}.workbench-expand-button .icon{transform:rotate(180deg);transition:transform .16s ease}.workbench.is-expanded .workbench-expand-button .icon{transform:rotate(0)}.workbench-body{overscroll-behavior:contain}.workbench-section:first-child{padding-top:12px}.selection-chip{position:absolute;z-index:13;top:calc(var(--header-height) + 12px);left:12px;height:34px;display:flex;align-items:center;gap:7px;padding:0 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--muted);box-shadow:0 4px 14px color-mix(in srgb,var(--text) 7%,transparent);font-size:10px;cursor:pointer}.selection-chip strong{color:var(--text);font-family:var(--numeric);font-size:11px}.selection-chip:hover{border-color:var(--line-strong);color:var(--accent)}.export-sheet{position:fixed;z-index:32;top:var(--header-height);right:0;bottom:0;width:var(--export-width);display:flex;flex-direction:column;overflow:hidden;border-left:1px solid var(--line);background:var(--surface);box-shadow:-10px 0 28px color-mix(in srgb,var(--text) 8%,transparent);animation:workbench-in .18s ease both}.figure-sheet-backdrop{position:fixed;z-index:31;inset:0;background:color-mix(in srgb,var(--text) 6%,transparent)}.export-sheet[hidden]{display:none}.export-sheet:focus{outline:none}.export-sheet:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.export-heading{min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex:0 0 auto;padding:8px 10px 8px 16px;border-bottom:1px solid var(--line)}.export-heading strong,.export-heading span{display:block}.export-heading strong{color:var(--text);font-size:14px;font-weight:650}.export-heading span{margin-top:2px;color:var(--muted);font-size:10px}.export-body{min-height:0;flex:1 1 auto;overflow:auto;overscroll-behavior:contain}.export-footer{min-height:56px;display:grid;grid-template-columns:1fr 1.45fr;gap:8px;flex:0 0 auto;padding:8px 10px;border-top:1px solid var(--line);background:var(--surface)}.export-footer button{min-height:38px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:11px;font-weight:600;cursor:pointer}.export-footer .primary{border-color:var(--accent);background:var(--accent);color:#fff}.export-footer button:disabled{opacity:.48;cursor:default}.figure-section{padding:16px;border-bottom:1px solid var(--line)}.figure-section-label{display:block;margin-bottom:10px;color:var(--quiet);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.figure-presets,.figure-choice-row{display:grid;gap:6px}.figure-presets{grid-template-columns:repeat(3,minmax(0,1fr));margin-bottom:10px}.figure-choice-row{grid-template-columns:repeat(2,minmax(0,1fr))}.figure-choice-row+.figure-choice-row{margin-top:8px}.figure-presets button,.figure-choice-row button,.figure-recipe-actions button{min-height:34px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font-size:10px;font-weight:600;cursor:pointer}.figure-presets button:hover,.figure-choice-row button:hover,.figure-recipe-actions button:hover,.figure-presets button.is-active,.figure-choice-row button.is-active{border-color:color-mix(in srgb,var(--accent) 62%,var(--line));background:var(--accent-soft);color:var(--accent-strong)}.figure-number-grid{display:grid;grid-template-columns:1fr 1fr .8fr;gap:6px}.figure-number-grid label,.figure-scale-length{display:grid;gap:5px;color:var(--quiet);font-size:9px}.figure-number-grid input,.figure-scale-length input{min-width:0;height:34px;padding:0 8px;border:1px solid var(--line);border-radius:7px;background:var(--surface);color:var(--text);font-family:var(--numeric);font-size:10px}.figure-toggle{min-height:46px;display:flex;align-items:center;justify-content:space-between;gap:14px;border-bottom:1px solid var(--line);cursor:pointer}.figure-toggle:last-of-type{border-bottom:0}.figure-toggle span,.figure-toggle strong,.figure-toggle small{display:block}.figure-toggle strong{color:var(--text);font-size:10px;font-weight:600}.figure-toggle small{margin-top:2px;color:var(--quiet);font-size:9px}.figure-toggle input{width:16px;height:16px;accent-color:var(--accent)}.figure-scale-length{grid-template-columns:1fr 90px auto;align-items:center;margin-top:8px}.figure-recipe-actions p{margin:0 0 10px;color:var(--quiet);font-size:10px;line-height:1.45}.figure-recipe-actions>div{display:grid;grid-template-columns:1fr 1fr;gap:6px}.figure-sheet-open .molecule-canvas,.figure-sheet-open .canvas-field{width:calc(100% - var(--export-width))}.figure-sheet-open .canvas-controls{right:calc(var(--export-width) + 12px)}.figure-sheet-open .timeline{width:calc(100% - var(--export-width))}.export-options{border-bottom:1px solid var(--line)}.export-options>summary{min-height:48px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 16px;color:var(--text);font-size:11px;font-weight:600;cursor:pointer}.export-options>summary small{overflow:hidden;color:var(--quiet);font-size:9px;font-weight:400;text-overflow:ellipsis;white-space:nowrap}.export-options-body{border-top:1px solid var(--line)}.export-open .render-guide-region{right:var(--export-width)}.timeline,.timeline.is-compact{left:0;bottom:0;width:100%;height:var(--timeline-height);border:0;border-top:1px solid var(--line);border-radius:0;background:var(--surface);box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;transform:none}.frame-counter{white-space:nowrap}.workbench-open .timeline{width:calc(100% - var(--workbench-width))}.export-open .timeline{width:calc(100% - var(--export-width))}.playback-mode-control{display:none}@media(max-width:719px){.inspect-button{display:none}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.export-open .molecule-canvas,.export-open .canvas-field,.figure-sheet-open .molecule-canvas,.figure-sheet-open .canvas-field{width:100%}.workbench,.workbench.is-expanded{position:fixed;top:auto;right:0;bottom:var(--timeline-height);left:0;width:auto;height:min(44svh,360px);max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 16px);border:0;border-top:1px solid var(--line);border-radius:0;box-shadow:0 -10px 28px color-mix(in srgb,var(--text) 8%,transparent)}.workbench.is-expanded{height:min(68svh,560px)}.workbench-heading{min-height:50px}.workbench-expand-button{display:grid}.workbench-open .timeline,.export-open .timeline{width:100%}.workbench-open .canvas-controls,.export-open .canvas-controls{right:8px}.canvas-controls{top:calc(var(--header-height) + 8px);right:8px;height:44px}.canvas-controls>button{min-width:44px;height:40px}.canvas-controls .orientation-control{display:none}.canvas-view-select{height:40px;display:flex;align-items:center;border-left:1px solid var(--line)}.canvas-view-select select{width:58px;height:40px;padding:0 18px 0 8px;border:0;background:transparent;color:var(--text);font-size:11px}.selection-chip{top:calc(var(--header-height) + 13px);left:8px;height:40px}.export-sheet{top:auto;right:0;bottom:var(--timeline-height);left:0;width:auto;height:min(72svh,560px);max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 8px);border:0;border-top:1px solid var(--line);box-shadow:0 -10px 28px color-mix(in srgb,var(--text) 8%,transparent);animation-name:mobile-sheet-in}.export-open .render-guide-region{right:0}.export-heading{min-height:50px}.export-footer button{min-height:44px}.figure-options-button{width:44px;height:44px}.figure-presets button,.figure-choice-row button,.figure-recipe-actions button{min-height:44px}.figure-number-grid input,.figure-scale-length input{height:44px}.render-panel .render-presets{grid-template-columns:repeat(4,minmax(0,1fr))}.render-panel .render-presets button{min-width:0;padding-inline:4px}}@media(max-width:760px){.topbar{padding-inline:8px 4px}.open-button{width:44px;min-width:44px;padding:0;font-size:0}.panel-button,.inspect-button,.render-button{width:auto;min-width:50px;height:44px;padding:0 9px;font-size:10px}.panel-button span{display:inline}.more-button{width:44px;min-width:44px;height:44px}}@media(min-width:720px){.more-inspect-action{display:none!important}}@media(max-width:520px){.identity>div{min-width:0;display:block}.identity strong{display:none}.identity span:last-child{display:block;max-width:none;margin:0;font-size:10px}.identity-mark{width:28px;height:28px}}@media(max-width:420px){.identity>div{display:none}}@media(min-width:720px)and (max-width:760px){.workbench-open .molecule-canvas,.workbench-open .canvas-field{width:calc(100% - var(--workbench-width))}.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - var(--export-width))}}@media(min-width:480px)and (max-width:760px)and (max-height:520px){.workspace,.workspace.timeline-compact{--timeline-height: 58px}.timeline .plot-row{display:none}.workbench,.workbench.is-expanded,.export-sheet{top:var(--header-height);right:0;bottom:var(--timeline-height);left:auto;width:min(320px,48vw);height:auto;max-height:none;border:0;border-left:1px solid var(--line);border-radius:0;box-shadow:-10px 0 28px color-mix(in srgb,var(--text) 8%,transparent)}.workbench-heading{display:flex}.workbench-expand-button{display:none}.workbench-open .canvas-controls,.export-open .canvas-controls{right:calc(min(320px,48vw) + 8px)}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.export-open .molecule-canvas,.export-open .canvas-field{width:calc(100% - min(320px,48vw))}.export-open .render-guide-region{right:min(320px,48vw)}.workbench-open .timeline,.export-open .timeline{width:calc(100% - min(320px,48vw))}.render-panel .render-presets{grid-template-columns:repeat(2,minmax(0,1fr))}}:root{--timeline-height: 52px;--workbench-width: 280px}.workspace.timeline-compact{--timeline-height: 52px}.workbench-open .molecule-canvas,.workbench-open .canvas-field,.workbench-open .timeline{width:100%}.workbench{position:absolute;z-index:24;top:calc(var(--header-height) + 10px);right:12px;bottom:auto;left:auto;width:var(--workbench-width);height:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 20px);overflow:hidden;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 12px 32px color-mix(in srgb,var(--text) 10%,transparent)}.workbench.atom-card{width:300px}.workbench-heading{min-height:44px;padding:5px 6px 5px 14px}.workbench-body{max-height:calc(100svh - var(--header-height) - var(--timeline-height) - 66px);overflow:auto}.workbench-section,.workbench-section:first-child{padding:8px 14px}.scene-panel .workbench-section+.workbench-section{border-top:1px solid var(--line)}.periodic-control-label{display:block;margin:12px 0 6px;color:var(--muted);font-size:10px;font-weight:600}.workbench-section>.section-label+.periodic-control-label{margin-top:0}.periodic-inline-control,.periodic-repeat-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px}.periodic-inline-control .periodic-control-label,.periodic-repeat-heading .periodic-control-label{margin:0}.periodic-repeat-heading>span:last-child{color:var(--quiet);font-size:9px;font-variant-numeric:tabular-nums}.periodic-axis-options{display:flex;gap:4px}.periodic-axis-options button,.periodic-repeat-row button{width:30px;height:30px;padding:0;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--muted);font-size:11px;cursor:pointer}.periodic-axis-options button:hover:not(:disabled),.periodic-repeat-row button:hover:not(:disabled){border-color:var(--line-strong);color:var(--text)}.periodic-axis-options button.is-active{border-color:color-mix(in srgb,var(--accent) 34%,var(--line));background:var(--accent-soft);color:var(--accent);font-weight:700}.periodic-repeat-grid{display:grid;gap:4px;margin-top:6px}.periodic-repeat-row{display:grid;grid-template-columns:minmax(0,1fr) 30px 38px 30px;align-items:center;gap:4px;min-height:30px}.periodic-repeat-row>span{color:var(--text);font-size:11px;font-style:italic}.periodic-repeat-row output{color:var(--text);font-size:10px;font-variant-numeric:tabular-nums;text-align:center}.periodic-repeat-row.is-disabled>span,.periodic-repeat-row.is-disabled output{color:var(--quiet)}.periodic-axis-options button:disabled,.periodic-repeat-row button:disabled{cursor:default;opacity:.38}.panel-select-row{min-height:34px}.display-toggles{padding-block:4px}.display-toggles .toggle-row{min-height:36px}.display-toggles .toggle-row+.toggle-row{border-top:1px solid var(--line-soft)}.atom-card .inspector-content{padding:0}.atom-card .readout-section{padding:12px 14px 14px;border:0}.workbench-open .canvas-controls{right:12px;opacity:0;pointer-events:none}.timeline,.timeline.is-compact{height:var(--timeline-height)}.timeline .transport-row{min-height:var(--timeline-height)}@media(max-width:520px){.workbench,.workbench.atom-card{position:absolute;top:calc(var(--header-height) + 8px);right:8px;bottom:auto;left:8px;width:auto;height:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 16px);border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 30px color-mix(in srgb,var(--text) 11%,transparent)}.panel-button,.render-button{min-width:0;padding-inline:8px}.panel-button .icon,.render-button .icon{display:none}.periodic-axis-options button,.periodic-repeat-row button{width:42px;height:42px}.periodic-repeat-row{grid-template-columns:minmax(0,1fr) 42px 46px 42px;min-height:42px}}@media(max-width:760px){.open-button{width:auto;min-width:62px;padding-inline:8px;font-size:10px}}.command-button{display:inline-flex;width:auto;min-width:58px;padding-inline:8px}.command-button span{display:none}.command-button kbd{white-space:nowrap}.command-backdrop{z-index:70}@media(max-width:760px){.command-button{display:inline-flex;width:44px;min-width:44px;height:44px;padding:0}.command-button kbd{display:none}.command-button .icon{width:18px;height:18px}}.workspace{--measurement-plot-height: 164px}.measurement-plot-open .selection-bar{z-index:18;bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 7px);width:min(760px,calc(100% - 24px));max-width:none;border-radius:10px 10px 0 0;box-shadow:0 8px 24px color-mix(in srgb,var(--text) 9%,transparent)}.measurement-plot-open .selection-readout{flex:1 1 auto}.workspace.measurement-plot-open .notice{bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 70px)}.measurement-plot{position:absolute;z-index:17;bottom:calc(var(--timeline-height) + 8px);left:50%;width:min(760px,calc(100% - 24px));height:var(--measurement-plot-height);display:grid;grid-template-rows:34px minmax(0,1fr) 18px;overflow:hidden;border:1px solid var(--line);border-top:0;border-radius:0 0 10px 10px;background:var(--surface);box-shadow:0 8px 24px color-mix(in srgb,var(--text) 9%,transparent);transform:translate(-50%)}.measurement-plot.is-complete{grid-template-rows:34px minmax(0,1fr)}.measurement-plot__header{min-width:0;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:3px 5px 3px 13px;border-bottom:1px solid var(--line-soft)}.measurement-plot__meta{color:var(--quiet);font-family:var(--numeric);font-size:10px}.measurement-plot__actions{flex:0 0 auto;display:flex;align-items:center;gap:1px}.measurement-plot__export-menu{position:relative;display:none}.measurement-plot__export-menu>summary{list-style:none}.measurement-plot__export-menu>summary::-webkit-details-marker{display:none}.measurement-plot__actions button{height:30px;min-width:40px;padding:0 7px;border:0;border-radius:5px;background:transparent;color:var(--muted);font-size:10px;font-weight:650;cursor:pointer}.measurement-plot__actions button:hover:not(:disabled){background:var(--surface-soft);color:var(--text)}.measurement-plot__actions button:disabled{color:var(--disabled);cursor:default}.measurement-plot__chart{width:100%;height:100%;min-height:0;display:block;cursor:crosshair}.measurement-plot__chart:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.measurement-plot__grid,.measurement-plot__axis{stroke:var(--line-soft);stroke-width:1}.measurement-plot__axis{stroke:var(--line-strong)}.measurement-plot__trace{stroke:var(--accent);stroke-width:1.8}.measurement-plot__trace-point,.measurement-plot__cursor-point{fill:var(--accent)}.measurement-plot__cursor{stroke:var(--accent);stroke-width:1.25;opacity:.7}.measurement-plot__cursor-point{stroke:var(--surface);stroke-width:2}.measurement-plot__tick,.measurement-plot__axis-label,.measurement-plot__unit,.measurement-plot__empty{fill:var(--quiet);font-family:var(--numeric);font-size:12px}.measurement-plot__axis-label,.measurement-plot__unit,.measurement-plot__empty{font-family:inherit;font-size:11px}.measurement-plot__progress{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;padding:0 12px;color:var(--quiet);font-family:var(--numeric);font-size:10px}.measurement-plot__progress progress{width:100%;height:2px;overflow:hidden;border:0;border-radius:2px;background:var(--surface-soft);color:var(--accent);appearance:none}.measurement-plot__progress progress::-webkit-progress-bar{background:var(--surface-soft)}.measurement-plot__progress progress::-webkit-progress-value{background:var(--accent)}.measurement-plot__progress progress::-moz-progress-bar{background:var(--accent)}.selection-bar .selection-plot-button[aria-pressed=true]{background:var(--accent-soft)}@media(max-width:520px){.workspace{--measurement-plot-height: 194px}.measurement-plot,.measurement-plot-open .selection-bar{width:calc(100% - 16px)}.measurement-plot{grid-template-rows:44px minmax(0,1fr) 18px}.measurement-plot.is-complete{grid-template-rows:44px minmax(0,1fr)}.measurement-plot__header{overflow:visible;padding-left:10px}.measurement-plot__actions>button:not(.measurement-plot__close){display:none}.measurement-plot__export-menu{display:block}.measurement-plot__export-menu>summary{width:58px;height:44px;display:grid;place-items:center;border-radius:5px;color:var(--muted);font-size:10px;font-weight:650;cursor:pointer}.measurement-plot__export-menu[open]>summary{background:var(--surface-soft);color:var(--text)}.measurement-plot__export-menu>div{position:absolute;z-index:2;top:calc(100% + 2px);right:0;width:112px;padding:4px;border:1px solid var(--line);border-radius:7px;background:var(--surface);box-shadow:var(--shadow)}.measurement-plot__export-menu>div button{width:100%;display:block;text-align:left}.measurement-plot__actions button{width:44px;height:44px;padding-inline:5px}}@media(max-width:380px){.measurement-plot-open .selection-bar>button:not(.icon-button){min-width:48px;padding-inline:6px}}@media(max-width:760px)and (max-height:520px){.workspace{--measurement-plot-height: 132px}.measurement-plot{grid-template-rows:44px minmax(0,1fr) 14px}.measurement-plot.is-complete{grid-template-rows:44px minmax(0,1fr)}.measurement-plot__progress{padding-inline:9px}}@media(max-width:760px){.open-button,.panel-button,.inspect-button,.render-button,.canvas-controls>button{font-size:11px}.canvas-controls{height:48px}.canvas-controls>button{min-width:44px;height:44px}.transport-button,.play-button,.timeline-options>summary{width:40px;height:44px}.segmented-options button{min-height:40px;font-size:11px}.workspace.selection-present .timeline-options>div{bottom:calc(100% + 82px)}}@media(max-width:600px){.transport-buttons .transport-button:first-child,.transport-buttons .transport-button:last-child{display:none}.frame-counter-full{display:none}.frame-counter-compact{display:inline}.frame-error-full{display:none}.frame-error-compact{display:inline}.frame-error{min-width:28px;max-width:28px;flex:0 0 28px;padding-inline:0}}@media(min-width:521px)and (max-width:760px){.workspace.selection-present .workbench{max-height:calc(100% - var(--header-height) - var(--timeline-height) - 80px)}}@media(max-width:479px)and (max-height:520px){.workbench-heading{min-height:44px;display:flex}}@media(max-width:380px){.selection-hint{display:none}.measurement-plot-open .selection-bar{flex-wrap:wrap;justify-content:flex-end;row-gap:4px;padding-block:6px}.measurement-plot-open .selection-readout{flex:1 0 100%;overflow:hidden}.measurement-plot-open .selection-readout strong{min-width:0;max-width:100%;flex:0 1 auto}.measurement-plot-open .selection-readout output{flex:0 0 auto}.workspace.measurement-plot-open .notice{bottom:calc(var(--timeline-height) + var(--measurement-plot-height) + 100px)}.measurement-mode-full{display:none}.measurement-mode-compact{display:inline}}@media(max-width:520px){.workspace.selection-present .timeline-options>div{bottom:calc(var(--selection-bottom) + 60px)}}@media(max-width:760px)and (max-height:450px){.workspace.playback-options-open .canvas-controls,.workspace.playback-options-open .selection-bar{visibility:hidden;pointer-events:none}.workspace.playback-options-open .timeline-options>div{bottom:calc(100% + 8px)}}@media(max-width:520px)and (max-height:450px){.workspace.playback-options-open .timeline-options>div{bottom:calc(var(--timeline-height) + 8px)}}@media(max-width:760px)and (max-height:360px){.workspace.measurement-plot-open .canvas-controls{visibility:hidden;pointer-events:none}.workspace.measurement-plot-open .notice{top:calc(var(--header-height) + 8px);bottom:auto;max-height:calc(100% - var(--header-height) - var(--timeline-height) - 16px)}}.selection-tools{position:relative;flex:0 0 auto}.selection-tools>summary{min-width:54px;height:36px;display:grid;place-items:center;padding:0 9px;border-radius:5px;color:var(--accent);font-size:10px;font-weight:650;cursor:pointer;list-style:none}.selection-tools>summary::-webkit-details-marker{display:none}.selection-tools>summary:hover,.selection-tools[open]>summary{background:var(--accent-soft)}.selection-tools-popover{position:absolute;right:0;bottom:calc(100% + 10px);width:min(300px,calc(100vw - 20px));max-height:min(520px,calc(100svh - var(--header-height) - var(--header-height) - var(--timeline-height) - 28px));overflow-x:hidden;overflow-y:auto;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:var(--shadow)}.selection-tools-popover>section{display:grid;gap:8px;padding:11px}.selection-tools-popover>section+section{border-top:1px solid var(--line)}.selection-tools-popover label,.selection-tools-popover section>span{color:var(--quiet);font-size:9px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.selection-scope-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.selection-scope-grid button,.selection-input-row button,.saved-selections button{min-height:32px;border:1px solid var(--line);border-radius:6px;background:transparent;color:var(--text);font-size:10px;cursor:pointer}.selection-scope-grid button:hover:not(:disabled),.selection-input-row button:hover:not(:disabled),.saved-selections button:hover:not(:disabled){border-color:var(--line-strong);background:var(--surface-soft)}.selection-scope-grid button:disabled,.selection-input-row button:disabled{opacity:.4;cursor:default}.selection-input-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:6px}.selection-input-row.is-name{grid-template-columns:minmax(0,1fr) auto}.selection-input-row input{min-width:0;height:34px;padding:0 9px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--text);font:11px var(--numeric)}.selection-input-row input:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)}.selection-input-row>span{color:var(--muted);font:10px var(--numeric)}.selection-input-row button{padding-inline:10px;color:var(--accent);font-weight:650}.saved-selections{max-height:178px;overflow:auto}.saved-selections>div{display:grid;grid-template-columns:minmax(0,1fr) 32px;gap:5px}.saved-selections>div>button:first-child{min-width:0;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 9px;text-align:left}.saved-selections button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saved-selections button small{color:var(--quiet);font:9px var(--numeric)}.saved-selections>div>button:last-child{display:grid;place-items:center;padding:0;color:var(--quiet)}.saved-selections .icon{width:12px;height:12px}.pinned-measurements{position:absolute;z-index:13;top:calc(var(--header-height) + 12px);left:12px;display:block;max-width:none;max-height:none;overflow:visible;padding:0}.pinned-measurements .selection-chip{position:static;min-width:0;border-radius:8px 0 0 8px;box-shadow:none}.pinned-measurements .selection-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pinned-measurements .selection-chip[aria-pressed=true]{border-color:color-mix(in srgb,var(--accent) 48%,var(--line));background:var(--accent-soft)}.pinned-measurement-remove{width:30px;flex:0 0 30px;display:grid;place-items:center;border:1px solid var(--line);border-left:0;border-radius:0 8px 8px 0;background:var(--surface);color:var(--quiet);cursor:pointer}.pinned-measurement-remove:hover{color:var(--text)}.pinned-measurement-remove .icon{width:12px;height:12px}.selection-summary-panel .readout-section{padding-top:4px}@media(max-width:760px){.selection-bar{width:calc(100% - 16px)}.selection-tools{position:static}.selection-tools>summary{height:44px}.selection-tools-popover{right:8px;left:8px;width:auto}.selection-scope-grid button,.selection-input-row button,.saved-selections button{min-height:38px}.selection-input-row input{height:40px}.pinned-measurements{top:calc(var(--header-height) + 60px);left:8px}.pinned-measurements .selection-chip{height:40px}}@media(max-width:520px){.selection-bar .measurement-mode{display:none}.selection-readout{min-width:80px}.selection-tools>summary{min-width:48px;padding-inline:7px}} diff --git a/pqviewer/static/assets/index-DDn9Bo7g.js b/pqviewer/static/assets/index-DDn9Bo7g.js new file mode 100644 index 0000000..7fbda00 --- /dev/null +++ b/pqviewer/static/assets/index-DDn9Bo7g.js @@ -0,0 +1,43 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/publication-Br5bSGOw.js","assets/three-Cf-YKIix.js"])))=>i.map(i=>d[i]); +import{r as w,j as f,c as nd}from"./react-C2StAl4u.js";import{V as F,X as Gs,O as Oc,T as Me,b as Hs,Y as Ie,_ as pn,$ as Vr,B as Yt,a as me,a0 as rd,g as je,F as zt,J as vt,a1 as od,a2 as vo,s as sd,a3 as Lc,a4 as id,a5 as ad,a6 as In,a7 as Bc,a8 as Nr,a9 as cd,aa as nr,ab as jo,M as sr,ac as ld,W as ks,H as ud,m as Ks,l as Fr,P as Sa,u as dd,ad as Dc,ae as ir,af as Ur,D as zc,R as Ma,ag as ar,ah as Uo,ai as Si,aj as Nt,ak as Vc,c as Sr}from"./three-Cf-YKIix.js";import{m as fd}from"./publication-Br5bSGOw.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(o){if(o.ep)return;o.ep=!0;const s=n(o);fetch(o.href,s)}})();const md=new TextDecoder,hd=96,pd=64*1024*1024,ka=4;async function va(){const e=await fetch("/api/manifest",{headers:{Accept:"application/json"}});if(!e.ok)throw new Error(await Ht(e,"Could not load the trajectory"));const t=await e.json();return Uc(t,"The trajectory manifest is incomplete"),t}async function gd(){const e=await fetch("/api/initial-recipe",{headers:{Accept:"application/json"}});if(!e.ok)throw new Error(await Ht(e,"Could not load the figure recipe"));return e.json()}async function bd(e,t){const n=await fetch("/api/positions",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({dataset_generation:e.datasetGeneration,atom_indices:e.atomIndices,frame_indices:e.frameIndices,coordinates:e.coordinates??"unwrapped"}),signal:t});if(n.status===409)throw new ct(await Ht(n,"Trajectory changed. Reloading."));if(!n.ok)throw new Error(await Ht(n,"Could not load selected positions"));return Sd(await n.json(),e)}async function yd(e,t){const n=await fetch("/api/analysis/rdf",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({dataset_generation:e.datasetGeneration,reference_indices:e.referenceIndices,target_indices:e.targetIndices,frame_start:e.frameStart??0,frame_stop:e.frameStop,frame_step:e.frameStep??1,n_bins:e.bins??200,r_max:e.rMax}),signal:t});if(n.status===409)throw new ct(await Ht(n,"Trajectory changed. Reloading."));if(!n.ok)throw new Error(await Ht(n,"Could not run RDF analysis"));return Md(await n.json(),e.datasetGeneration)}async function xd(e,t){if(e.length===0)throw new Error("Choose at least one trajectory file");const n=new FormData;e.forEach(i=>n.append("files",i,i.name));const r=await fetch("/api/open",{method:"POST",headers:{Accept:"application/json"},body:n,signal:t});if(!r.ok)throw new Error(await Ht(r,"Could not open the files"));const o=await r.json(),s="manifest"in o&&o.manifest?o.manifest:o;return Uc(s,"The opened trajectory is incomplete"),s}class ct extends Error{constructor(t){super(t),this.name="DatasetChangedError"}}async function xo(e,t,n,r="source"){const o=[];n&&o.push(`dataset_generation=${encodeURIComponent(n)}`),r==="unwrapped"&&o.push("coordinates=unwrapped");const s=o.length>0?`?${o.join("&")}`:"",i=await fetch(`/api/frames/${e}${s}`,{headers:{Accept:"application/octet-stream"},signal:t});if(i.status===409)throw new ct(await Ht(i,"Trajectory changed. Reloading."));if(!i.ok)throw new Error(await Ht(i,`Could not load frame ${e+1}`));return wd(await i.arrayBuffer())}function wd(e){if(e.byteLength<4)throw new Error("Frame packet is truncated");const n=new DataView(e).getUint32(0,!0),r=4+n;if(r>e.byteLength)throw new Error("Frame header is truncated");let o;try{o=JSON.parse(md.decode(new Uint8Array(e,4,n)))}catch{throw new Error("Frame header is invalid")}if(!Array.isArray(o.arrays))throw new Error("Frame arrays are missing");const s=new Map;for(const i of o.arrays){const a=r+i.byte_offset,c=a+i.byte_length;if(ae.byteLength||c=n||this.values.has(t)||!this.canPrefetch()||this.load(t,!0).catch(()=>{})}cancelPendingExcept(t){for(const[n,r]of this.values)n===t||!r.controller||this.remove(n,r,!0)}clear(){for(const t of this.values.values())t.controller?.abort();this.values.clear(),this.resolvedBytes=0,this.frameByteEstimate=0}load(t,n){const r=new AbortController;let o;const s=xo(t,r.signal,this.datasetGeneration,this.coordinates).then(i=>(o.controller=null,o.byteLength=Ad(i),this.frameByteEstimate=Math.max(this.frameByteEstimate,o.byteLength),this.values.get(t)===o&&(this.resolvedBytes+=o.byteLength,this.trim()),i)).catch(i=>{throw this.values.get(t)===o&&this.remove(t,o,!1),i});return o={promise:s,controller:r,byteLength:0,prefetch:n},this.values.set(t,o),this.trim(),s}canPrefetch(){const t=[...this.values.values()].filter(o=>o.prefetch&&o.controller!==null).length;if(this.frameByteEstimate===0)return tthis.maxFrames||this.resolvedBytes>this.maxBytes;){const t=this.values.keys().next().value;if(t===void 0)break;const n=this.values.get(t);n&&this.remove(t,n,!0)}}remove(t,n,r){this.values.get(t)===n&&(this.values.delete(t),this.resolvedBytes=Math.max(0,this.resolvedBytes-n.byteLength),r&&n.controller?.abort())}}function Ad(e){const t=new Set;let n=0;for(const r of e.arrays.values()){const o=r.buffer;if(!t.has(o)&&(t.add(o),n+=o.byteLength,!Number.isSafeInteger(n)))return Number.MAX_SAFE_INTEGER}return n}function ja(e,t){return!Number.isFinite(e)||e===void 0||e<=0?t:Math.max(1,Math.floor(e))}function Uc(e,t){if(!e.topology||!Number.isFinite(e.frame_count)||qc(e.dataset_generation)===void 0)throw new Error(t)}function Sd(e,t){if(!e||typeof e!="object")throw new Error("Selected positions response is invalid");const n=e,r=qt(n.schema_version,"position schema"),o=Nn(n.dataset_generation,"position generation");if(o!==t.datasetGeneration)throw new ct("Trajectory changed. Reloading.");const s=Xs(n.atom_indices,"position atoms");if(s.length!==t.atomIndices.length||s.some((a,c)=>a!==t.atomIndices[c]))throw new Error("Selected positions do not match the requested atoms");if(!Array.isArray(n.frames)||n.frames.length!==t.frameIndices.length)throw new Error("Selected positions do not match the requested frames");const i=n.frames.map((a,c)=>{if(!a||typeof a!="object")throw new Error("Selected position frame is invalid");const l=a,u=qt(l.index,"position frame");if(u!==t.frameIndices[c])throw new Error("Selected positions are out of order");if(!Array.isArray(l.positions)||l.positions.length!==s.length)throw new Error("Selected position coordinates are incomplete");const d=new Float32Array(s.length*3);return l.positions.forEach((m,g)=>{if(!Array.isArray(m)||m.length!==3||!m.every(h=>typeof h=="number"&&Number.isFinite(h)))throw new Error("Selected position coordinates are invalid");d.set(m,g*3)}),Object.freeze({index:u,key:Ws(l.key),positions:d,step:Rr(l.step),time:Rr(l.time),timeUnit:Mi(l.time_unit)})});return Object.freeze({schemaVersion:r,datasetGeneration:o,atomIndices:Object.freeze([...s]),unit:Nn(n.unit,"position unit"),frames:Object.freeze(i)})}function Md(e,t){if(!e||typeof e!="object")throw new Error("RDF response is invalid");const n=e,r=Nn(n.dataset_generation,"RDF generation");if(r!==t)throw new ct("Trajectory changed. Reloading.");const o=n.frame_range;if(!o||typeof o!="object")throw new Error("RDF frame identity is missing");const s=o,i=Object.freeze({start:qt(s.start,"RDF frame start"),stop:qt(s.stop,"RDF frame stop"),step:js(s.step,"RDF frame step"),count:js(s.count,"RDF frame count"),firstKey:Ws(s.first_key),lastKey:Ws(s.last_key)}),a=ao(n.radius_centers,"RDF radius"),c=ao(n.g_r,"RDF values"),l=ao(n.coordination_radius,"coordination radius"),u=ao(n.coordination,"coordination values");if(a.length!==c.length||l.length!==u.length||a.length!==u.length)throw new Error("RDF result arrays are misaligned");const d=n.units;if(!d||typeof d!="object")throw new Error("RDF units are missing");const m=d,g=n.parameters;if(!g||typeof g!="object")throw new Error("RDF parameters are missing");const h=g,y=n.selections;if(!y||typeof y!="object")throw new Error("RDF selections are missing");const A=y;return Object.freeze({schemaVersion:qt(n.schema_version,"RDF schema"),datasetGeneration:r,referenceIndices:Object.freeze(Xs(A.reference_indices,"RDF reference selection")),targetIndices:Object.freeze(Xs(A.target_indices,"RDF target selection")),frameRange:i,radiusUnit:Nn(m.radius,"RDF radius unit"),rdfUnit:Nn(m.g_r,"RDF unit"),coordinationUnit:Nn(m.coordination,"coordination unit"),bins:js(h.n_bins,"RDF bins"),rMax:Ca(h.r_max,"RDF maximum radius"),deltaR:Ca(h.delta_r,"RDF resolution"),radiusCenters:Object.freeze(a),gR:Object.freeze(c),coordinationRadius:Object.freeze(l),coordination:Object.freeze(u),pqAnalysisVersion:Mi(n.pqanalysis_version)??void 0,elapsedSeconds:Rr(n.elapsed_seconds)??void 0})}function Ws(e){if(!e||typeof e!="object")throw new Error("Frame key is invalid");const t=e;return Object.freeze({source_id:Nn(t.source_id,"frame source"),source_index:qt(t.source_index,"frame source index"),segment_index:qt(t.segment_index,"frame segment"),step:Rr(t.step),time:Rr(t.time),time_unit:Mi(t.time_unit)})}function Xs(e,t){if(!Array.isArray(e))throw new Error(`${t} are invalid`);return e.map(n=>qt(n,t))}function ao(e,t){if(!Array.isArray(e))throw new Error(`${t} are invalid`);return e.map(n=>{if(typeof n!="number"||!Number.isFinite(n))throw new Error(`${t} are invalid`);return n})}function qt(e,t){if(!Number.isSafeInteger(e)||e<0)throw new Error(`${t} is invalid`);return e}function js(e,t){const n=qt(e,t);if(n<1)throw new Error(`${t} is invalid`);return n}function Ca(e,t){if(typeof e!="number"||!Number.isFinite(e)||e<=0)throw new Error(`${t} is invalid`);return e}function Rr(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function Nn(e,t){if(typeof e!="string"||!e.trim())throw new Error(`${t} is invalid`);return e.trim()}function Mi(e){return typeof e=="string"&&e.trim()?e.trim():null}function qc(e){const t=e?.trim();return t||void 0}function ae(e,t){if(!e)return null;for(const n of t){const r=e.arrays.get(n.toLowerCase());if(r instanceof Float32Array)return r}return null}function Co(e,t){if(!e)return null;for(const n of t){const r=e.arrays.get(n.toLowerCase());if(r instanceof Int32Array)return r}return null}function kd(e){if(!e)return[];if(Array.isArray(e))return e.filter(n=>!!(n&&Array.isArray(n.values))).map((n,r)=>({name:n.name??n.key??`series-${r+1}`,label:n.label??Ns(n.name??n.key??`Series ${r+1}`),unit:n.unit,values:Cs(n.values)}));const t=[];for(const[n,r]of Object.entries(e)){if(Array.isArray(r)){t.push({name:n,label:Ns(n),values:Cs(r)});continue}if(r&&typeof r=="object"&&Array.isArray(r.values)){const o=r;t.push({name:n,label:o.label??Ns(o.name??n),unit:o.unit,values:Cs(o.values)})}}return t}async function Ht(e,t){try{const n=await e.json();return n.detail??n.message??t}catch{return t}}function vd(e){return["float32","f4","typeof t=="number"&&Number.isFinite(t)?t:null)}function Ns(e){return e.replace(/[_-]+/g," ").replace(/\b\w/g,t=>t.toUpperCase())}const jd=6;function Cd(e,t,n={}){const r=wo(t);if(!r){const c=Math.min(Ia(n.limit),jd);return Nd(e.filter(l=>!l.disabled),n).slice(0,c)}const o=r.split(" "),s=Fa(n.contextIds),i=Fa(n.recentIds),a=e.filter(c=>!c.disabled||c.discoverableWhenDisabled).flatMap((c,l)=>{const u=Fd(c,r,o);return u===null?[]:[{action:c,score:u,index:l,contextRank:s.get(c.id),recentRank:i.get(c.id)}]});return a.sort((c,l)=>l.score-c.score||co(c.contextRank)-co(l.contextRank)||co(c.recentRank)-co(l.recentRank)||c.index-l.index),a.slice(0,Ia(n.limit)).map(({action:c})=>c)}function Nd(e,t){const n=new Map(e.map(s=>[s.id,s])),r=new Set,o=[];for(const s of[...t.contextIds??[],...t.recentIds??[]]){const i=n.get(s);!i||r.has(s)||(r.add(s),o.push(i))}for(const s of e)r.has(s.id)||(r.add(s.id),o.push(s));return o}function Fd(e,t,n){const r=wo(e.label),o=wo(typeof e.keywords=="string"?e.keywords:e.keywords?.join(" ")??""),s=wo(e.detail??""),i=r.split(" "),a=o.split(" "),c=s.split(" ");let l=0;for(const u of n){const d=Id(u,r,i,o,a,s,c);if(d===0)return null;l+=d}return r===t?l+=1e3:r.startsWith(t)?l+=500:r.includes(t)&&(l+=250),o===t?l+=220:o.startsWith(t)?l+=160:o.includes(t)&&(l+=100),s===t?l+=60:s.includes(t)&&(l+=30),l}function Id(e,t,n,r,o,s,i){return t===e?140:t.startsWith(e)?130:n.some(a=>a.startsWith(e))?120:t.includes(e)?100:o.some(a=>a.startsWith(e))?80:r.includes(e)?60:i.some(a=>a.startsWith(e))?40:s.includes(e)?20:0}function wo(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function Fa(e){const t=new Map;return e?.forEach((n,r)=>{t.has(n)||t.set(n,r)}),t}function co(e){return e??Number.MAX_SAFE_INTEGER}function Ia(e){return e===void 0||!Number.isFinite(e)?Number.MAX_SAFE_INTEGER:Math.max(0,Math.floor(e))}const qo=720,ki=130;function Gc(e,t){const n=Math.max(160,lt(e,qo)),r=Math.max(72,lt(t,ki)),o=n<=520?58:64,s=r<=96;return{width:n,height:r,left:o,right:Math.max(o+1,n-16),top:s?8:12,bottom:Math.max(24,r-(s?24:38))}}const kt=Gc(qo,ki),Hc=1600,Ea=Object.freeze(["#137f78","#b35c2e","#5468a8","#8b5a91","#4f7b45","#b08524","#366e83","#9a4d62"]);function Ed({title:e,unit:t,axisLabel:n,axisUnit:r,xValues:o,values:s,loadedCount:i,complete:a,currentFrame:c,onFrame:l,onExportCsv:u,onExportSvg:d,onExportPdf:m}){const g=w.useMemo(()=>({requestId:0,kind:"measurement",title:e,xLabel:n,xUnit:r,yLabel:Kd(e),yUnit:t,xValues:o,frameIndices:o.map((h,y)=>y),lines:[{id:"measurement",label:e,values:s,discontinuity:Ud(t)}],loadedCount:i,totalCount:o.length,complete:a}),[n,r,a,i,e,t,s,o]);return f.jsx(Kc,{plot:g,currentFrame:c,onFrame:l,onExportCsv:u,onExportSvg:d,onExportPdf:m})}function Kc({plot:e,currentFrame:t,onFrame:n,onRestoreLine:r,onClose:o,headerActions:s,onExportCsv:i,onExportSvg:a,onExportPdf:c}){const{title:l,xLabel:u,xUnit:d,yLabel:m,yUnit:g,xValues:h,lines:y,loadedCount:A,totalCount:x,complete:j}=e,[k,M]=$d(),C=w.useMemo(()=>Gc(M?.width??qo,M?.height??ki),[M?.height,M?.width]),N=h.length,E=e.frameIndices,$=t===void 0?-1:Ld(t,E,N),L=w.useMemo(()=>Bd(E,N),[E,N]),U=w.useMemo(()=>Dd(E,L),[E,L]),z=!!(n&&L.length>0),G=w.useMemo(()=>M===null?{xDomain:[0,Math.max(1,N-1)],yDomain:[0,1],lines:[]}:_d(h,y,{width:C.width,height:C.height,left:C.left,right:C.right,top:C.top,bottom:C.bottom,yDomain:e.yFloor===void 0?void 0:Rd(y,e.yFloor),maxPoints:Math.min(Hc,Math.ceil((C.right-C.left)*2))}),[M,C,y,e.yFloor,N,h]),_=$>=0?Gd($,h,G.xDomain,C.left,C.right):null,R=y.map(P=>vi(P.values[$])),ee=Math.max(0,Math.min(x,Number.isFinite(A)?Math.floor(A):0)),le=d?`${u} (${d})`:u,de=g?`${m} (${g})`:m,I=e.kind==="rdf"?"bins":"frames",J=j?`${x.toLocaleString()} ${I}`:`${ee.toLocaleString()} / ${x.toLocaleString()} ${I}`,Q=e.context?`${J} · ${e.context}`:J,v=$>=0?Hd($,t,h,y,u,d,g):"No linked frame",B=P=>{if(!z||!n)return;const Y=P.currentTarget.getBoundingClientRect(),he=Vd(P.clientX,Y,C.width);if(he===null)return;const q=Od(he,h,C.left,C.right),pe=Ys(E,q);pe!==null&&n(pe)},K=P=>{P.button!==0||!z||(P.currentTarget.setPointerCapture(P.pointerId),B(P))},X=P=>{P.currentTarget.hasPointerCapture(P.pointerId)&&B(P)},ne=P=>{P.currentTarget.hasPointerCapture(P.pointerId)&&P.currentTarget.releasePointerCapture(P.pointerId)},se=P=>{if(!z||!n)return;let Y=null;const he=Math.max(0,L.indexOf($));if(P.key==="ArrowLeft"&&(Y=L[Math.max(0,he-1)]),P.key==="ArrowRight"&&(Y=L[Math.min(L.length-1,he+1)]),P.key==="Home"&&(Y=L[0]),P.key==="End"&&(Y=L.at(-1)??null),Y===null)return;const q=Ys(E,Y);q!==null&&(P.preventDefault(),n(q))};return f.jsxs("section",{className:j?"measurement-plot is-complete":"measurement-plot","aria-label":`${l} trajectory plot`,children:[f.jsxs("header",{className:"measurement-plot__header",children:[f.jsxs("div",{className:"measurement-plot__meta",children:[f.jsx("strong",{title:l,children:l}),f.jsx("span",{title:j?Q:void 0,children:j?Q:"Loading data"})]}),f.jsx("div",{className:"measurement-plot__legend",role:"group","aria-label":"Current values",children:y.map((P,Y)=>{const he=Qs(P.color,Y),q=R[Y],pe=$<0?null:q===null?"unavailable":`${vn(q)}${g?` ${g}`:""}`,re=f.jsxs(f.Fragment,{children:[f.jsx("span",{className:"measurement-plot__legend-swatch","aria-hidden":"true",style:{backgroundColor:he}}),f.jsx("span",{children:P.label}),pe!==null&&f.jsx("output",{"aria-label":`${P.label} current value`,children:pe})]});return r&&P.selection?f.jsx("button",{className:"measurement-plot__legend-item",type:"button",onClick:()=>r(P),"aria-label":pe===null?`Restore ${P.label}`:`Restore ${P.label}; current value ${pe}`,children:re},P.id):f.jsx("span",{className:"measurement-plot__legend-item",children:re},P.id)})}),f.jsxs("div",{className:"measurement-plot__header-actions",role:"group","aria-label":"Plot controls",children:[s&&f.jsx("div",{className:"measurement-plot__context-actions",children:s}),f.jsxs("div",{className:"measurement-plot__actions",children:[f.jsxs("details",{className:"measurement-plot__export-menu",children:[f.jsx("summary",{children:"Export"}),f.jsxs("div",{children:[f.jsx("button",{type:"button",onClick:i,disabled:!j,children:"CSV"}),f.jsx("button",{type:"button",onClick:a,disabled:!j,children:"SVG"}),f.jsx("button",{type:"button",onClick:c,disabled:!j,children:"PDF"})]})]}),f.jsx("button",{type:"button",onClick:i,disabled:!j,children:"CSV"}),f.jsx("button",{type:"button",onClick:a,disabled:!j,children:"SVG"}),f.jsx("button",{type:"button",onClick:c,disabled:!j,children:"PDF"}),o&&f.jsx("button",{className:"measurement-plot__close",type:"button",onClick:o,"aria-label":"Close plot",title:"Close",children:"×"})]})]})]}),f.jsxs("svg",{ref:k,className:z?"measurement-plot__chart is-seekable":"measurement-plot__chart",viewBox:`0 0 ${C.width} ${C.height}`,role:z?"slider":"img",tabIndex:z?0:void 0,"aria-label":z?`${l} frame`:l,"aria-orientation":z?"horizontal":void 0,"aria-valuemin":z?U?.[0]:void 0,"aria-valuemax":z?U?.[1]:void 0,"aria-valuenow":z&&t!==void 0?t:void 0,"aria-valuetext":z?v:void 0,onKeyDown:se,onPointerDown:K,onPointerMove:X,onPointerUp:ne,onPointerCancel:ne,style:{touchAction:z?"none":"auto",cursor:z?"crosshair":"default"},children:[f.jsx("title",{children:l}),f.jsx("desc",{children:z?"Tap or drag to seek. Use arrow keys, Home, or End to move between frames.":`${y.length} plotted series.`}),f.jsx("line",{className:"measurement-plot__grid",x1:C.left,x2:C.right,y1:C.top,y2:C.top}),f.jsx("line",{className:"measurement-plot__grid",x1:C.left,x2:C.right,y1:(C.top+C.bottom)/2,y2:(C.top+C.bottom)/2}),f.jsx("line",{className:"measurement-plot__axis",x1:C.left,x2:C.right,y1:C.bottom,y2:C.bottom}),G.lines.flatMap(P=>P.segments.map((Y,he)=>Y.points.length===1?f.jsx("circle",{className:"measurement-plot__trace-point",cx:Y.points[0].x,cy:Y.points[0].y,r:2,style:{fill:P.color}},`${P.id}-point-${Y.points[0].frame}-${he}`):f.jsx("path",{className:"measurement-plot__trace",d:Y.path,fill:"none",vectorEffect:"non-scaling-stroke",style:{stroke:P.color}},`${P.id}-trace-${Y.points[0].frame}-${he}`))),G.lines.every(({segments:P})=>P.length===0)&&f.jsx("text",{className:"measurement-plot__empty",x:(C.left+C.right)/2,y:(C.top+C.bottom)/2,textAnchor:"middle",children:j?"No valid data":"Loading data…"}),_!==null&&f.jsx("line",{className:"measurement-plot__cursor",x1:_,x2:_,y1:C.top,y2:C.bottom,vectorEffect:"non-scaling-stroke"}),_!==null&&R.map((P,Y)=>P===null?null:f.jsx("circle",{className:"measurement-plot__cursor-point",cx:_,cy:rr(P,G.yDomain,C.bottom,C.top),r:4,style:{fill:Qs(y[Y].color,Y)}},`cursor-${y[Y].id}`)),f.jsx("text",{className:"measurement-plot__tick",x:C.left,y:C.bottom+17,children:vn(G.xDomain[0])}),f.jsx("text",{className:"measurement-plot__tick",x:C.right,y:C.bottom+17,textAnchor:"end",children:vn(G.xDomain[1])}),f.jsx("text",{className:"measurement-plot__tick",x:C.left-8,y:C.top+4,textAnchor:"end",children:vn(G.yDomain[1])}),f.jsx("text",{className:"measurement-plot__tick",x:C.left-8,y:C.bottom,textAnchor:"end",children:vn(G.yDomain[0])}),f.jsx("text",{className:"measurement-plot__axis-label",x:(C.left+C.right)/2,y:C.height-4,textAnchor:"middle",children:le}),f.jsx("text",{className:"measurement-plot__unit",x:12,y:(C.top+C.bottom)/2,textAnchor:"middle",transform:`rotate(-90 12 ${(C.top+C.bottom)/2})`,children:de})]}),!j&&f.jsxs("div",{className:"measurement-plot__progress",role:"status","aria-live":"polite",children:[f.jsx("progress",{"aria-label":e.kind==="rdf"?"Bins loaded":"Frames loaded",max:Math.max(1,x),value:ee}),f.jsx("span",{children:Q})]})]})}function $d(){const e=w.useRef(null),[t,n]=w.useState(null);return w.useEffect(()=>{const r=e.current;if(!r)return;let o=null,s=null;const i=(l,u)=>{if(!Number.isFinite(l)||!Number.isFinite(u)||l<=0||u<=0)return;const d={width:Math.round(l),height:Math.round(u)};n(m=>m&&m.width===d.width&&m.height===d.height?m:d)};let a=window.requestAnimationFrame(()=>{a=window.requestAnimationFrame(()=>{const l=r.getBoundingClientRect();i(l.width,l.height),a=null})});const c=new ResizeObserver(([l])=>{l&&(s={width:l.contentRect.width,height:l.contentRect.height},o!==null&&window.clearTimeout(o),o=window.setTimeout(()=>{s&&i(s.width,s.height),o=null},80))});return c.observe(r),()=>{c.disconnect(),a!==null&&window.cancelAnimationFrame(a),o!==null&&window.clearTimeout(o)}},[]),[e,t]}function $a(e,t,n={}){const r=Ao(n.width,kt.width),o=Ao(n.height,kt.height),s=lt(n.left,kt.left),i=lt(n.right,r-(kt.width-kt.right)),a=lt(n.top,kt.top),c=lt(n.bottom,o-(kt.height-kt.bottom)),l=Td(e,t,n.discontinuityThreshold),u=En(e.map((y,A)=>lt(y,A)),[0,Math.max(1,e.length-1)],!1),d=n.yDomain?En(n.yDomain,[0,1],!1):En(l.flatMap(y=>y.map(({value:A})=>A)),[0,1],!0),m=Math.max(2,Math.floor(Ao(n.maxPoints,Math.max(2,(i-s)*2)))),h=Pd(l,m).map(y=>{const A=y.map(x=>({...x,x:rr(x.xValue,u,s,i),y:rr(x.value,d,c,a)}));return{points:A,path:A.map((x,j)=>`${j===0?"M":"L"}${Ra(x.x)} ${Ra(x.y)}`).join(" ")}});return{xDomain:u,yDomain:d,segments:h}}function _d(e,t,n={}){const r=n.yDomain??En(t.flatMap(({values:c})=>c.flatMap(l=>typeof l=="number"&&Number.isFinite(l)?[l]:[])),[0,1],!0),o=Math.max(2,Math.floor(Ao(n.maxPoints,Hc))),s=Math.max(2,Math.floor(o/Math.max(1,t.length))),i=t.map((c,l)=>{const u=$a(e,c.values,{...n,yDomain:r,discontinuityThreshold:c.discontinuity,maxPoints:s});return{id:c.id,color:Qs(c.color,l),segments:u.segments}});return{xDomain:i.length>0?$a(e,[],{...n,yDomain:r}).xDomain:En(e.map((c,l)=>lt(c,l)),[0,Math.max(1,e.length-1)],!1),yDomain:r,lines:i}}function Rd(e,t){const n=En(e.flatMap(({values:o})=>o.flatMap(s=>typeof s=="number"&&Number.isFinite(s)?[s]:[])),[t,t+1],!0),r=Number.isFinite(t)?t:n[0];return[r,n[1]>r?n[1]:r+1]}function Td(e,t,n){const r=[];let o=[],s=null;const i=Number.isFinite(n)?Math.abs(n):null;for(let a=0;ai)&&(o.length>0&&r.push(o),o=[],s=l,(l===null||!Number.isFinite(c))&&(s=null),l===null||!Number.isFinite(c))||(o.push({frame:a,xValue:c,value:l}),s=l)}return o.length>0&&r.push(o),r}function Pd(e,t){const n=e.filter(a=>a.length>0),r=n.reduce((a,c)=>a+c.length,0),o=Math.max(1,Math.floor(lt(t,1)));if(r<=o)return n.map(a=>[...a]);if(n.length>=o)return _a(n.length,o).map(a=>{const c=n[a];return[c[Math.floor((c.length-1)/2)]]});const s=n.map(a=>Math.min(a.length,a.length>1?2:1));let i=o-s.reduce((a,c)=>a+c,0);if(i<0)return _a(n.length,o).map(a=>[n[a][0]]);for(;i>0;){const a=n.map((c,l)=>({index:l,capacity:c.length-s[l]})).filter(({capacity:c})=>c>0).sort((c,l)=>l.capacity-c.capacity);if(a.length===0)break;for(const{index:c}of a){if(i===0)break;s[c]+=1,i-=1}}return n.map((a,c)=>qd(a,s[c]))}function Od(e,t,n=kt.left,r=kt.right){if(t.length===0)return 0;const o=Math.max(Math.min(n,r),Math.min(Math.max(n,r),e)),s=t[0],i=t[t.length-1];if(!Number.isFinite(s)||!Number.isFinite(i)||i=0&&n.push(o)}return n}function Ys(e,t){const n=e?.[t];return typeof n=="number"&&Number.isSafeInteger(n)&&n>=0?n:null}function Dd(e,t){let n=1/0,r=-1/0;for(const o of t){const s=Ys(e,o);s!==null&&(n=Math.min(n,s),r=Math.max(r,s))}return Number.isFinite(n)&&Number.isFinite(r)?[n,r]:null}function zd(e,t,n,r){const o=t.map((l,u)=>lt(l,u)),s=En(o,[0,Math.max(1,t.length-1)],!1),i=rr(e,[n,r],s[0],s[1]);let a=0,c=1/0;return o.forEach((l,u)=>{const d=Math.abs(l-i);d=e.length||t<=0)return[...e];if(t===1)return[e[0]];if(t===2)return[e[0],e[e.length-1]];const n=[e[0]],r=(e.length-2)/(t-2);let o=0;for(let s=0;sh&&(h=j,y=A)}n.push(e[y]),o=y}return n.push(e[e.length-1]),n}function En(e,t,n){let r=1/0,o=-1/0;if(e.forEach(s=>{Number.isFinite(s)&&(r=Math.min(r,s),o=Math.max(o,s))}),!Number.isFinite(r)||!Number.isFinite(o))return t;if(r===o){const s=Math.max(Math.abs(r)*.05,.5);r-=s,o+=s}else if(n){const s=(o-r)*.08;r-=s,o+=s}return[r,o]}function Gd(e,t,n,r,o){return rr(lt(t[e],e),n,r,o)}function rr(e,t,n,r){return t[0]===t[1]?(n+r)/2:n+(e-t[0])/(t[1]-t[0])*(r-n)}function vi(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function lt(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function Ao(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}function _a(e,t){return t<=1?[0]:Array.from({length:t},(n,r)=>Math.round(r*(e-1)/(t-1)))}function Ra(e){return Number(e.toFixed(2)).toString()}function vn(e){const t=Math.abs(e);return t>0&&t<.001||t>=1e4?e.toExponential(2):Number(e.toPrecision(4)).toString()}function Hd(e,t,n,r,o,s,i){const a=lt(n[e],e),c=`${o} ${vn(a)}${s?` ${s}`:""}`,l=r.map(u=>{const d=vi(u.values[e]);return`${u.label} ${d===null?"unavailable":`${vn(d)}${i?` ${i}`:""}`}`});return[`Frame ${t+1}`,c,...l].join("; ")}function Kd(e){const t=e.indexOf(" · ");return t>0?e.slice(0,t):"Measurement"}function Qs(e,t){const n=e?.trim();return n&&/^#[0-9a-f]{6}$/i.test(n)?n:Ea[t%Ea.length]}const Tr=125,Yn=25e4,ji=8e4,No=ji,Wd=5e4,Xd=2e6,Yd=12e3,Qd=25e4,Zd=4e7,Jd=729,ef=new Set(["H2O","HOH","OH2","WAT","WATER","TIP3","TIP3P","TIP4","TIP4P","SPC","SPCE"]),tf=new Set(["ALA","ARG","ASN","ASP","CYS","GLN","GLU","GLY","HIS","ILE","LEU","LYS","MET","PHE","PRO","SER","THR","TRP","TYR","VAL","ASH","CYX","GLH","HID","HIE","HIP","LYN","MSE"]);function Wc(e,t,n,r){const o=ae(t,["positions","position","pos","coordinates","coords"]);if(!o)return null;const s=Math.min(e.topology.atom_count,Math.floor(o.length/3)),i=$t(ae(t,["cell","cell_vectors","box"])),a=fn(t,i),c=r?.count===s?r:Zs(e,t);if(!c)return null;const{atomicNumbers:l,bonds:u,waterAtoms:d}=c,m=hf(n.cellOrigin),g=mf(t,o,s,i,a,c,n.wrap,m),h=i?Ft(new F(...m),i):new F,y=gf(i,n.mirror),A=yf(g.positions,s,y,h),x=wf(i,y),j=g.baseImages,k=Af(l,d,n),M=rf(n.images.min,n.images.max,a,k.length),{instanceToAtom:C,instanceImages:N}=of(k,M),E=l.map($=>Ci($,n.mode,n.atomScale));return{count:s,atomicNumbers:l,positions:A,baseImages:j,basis:x,cellCenter:h,displayTransform:y,pbc:a,bonds:u,waterAtoms:d,visibleAtoms:k,images:M,instanceToAtom:C,instanceImages:N,radii:E,backbone:ol(e)}}function Zs(e,t){const n=ae(t,["positions","position","pos","coordinates","coords"]);if(!n)return null;const r=Math.min(e.topology.atom_count,Math.floor(n.length/3)),o=Ii(e,r),s=$t(ae(t,["cell","cell_vectors","box"])),i=fn(t,s),a=Fi(e.topology.bonds,r),c=Xc(t,r)??qr(n,r,s,i,[0,0,0]),l=e.topology.bond_source==="topology"||a.length>0?a:al(c,o,r,s,i);return{count:r,atomicNumbers:o,bonds:l,waterAtoms:rl(e,t,l),moleculeGroups:Mf(e,r,l)}}function Xc(e,t){const n=ae(e,["positions","position","pos","coordinates","coords"]);if(!n)return null;const r=ae(e,["centered_positions","centered_position"]),o=Math.min(t,Math.floor(n.length/3));if(r&&r.length>=o*3)return new Float32Array(r.subarray(0,o*3));const s=$t(ae(e,["cell","cell_vectors","box"])),i=Co(e,["centered_image_shifts","centered_images"]);return i&&i.length>=o*3?ei(n,i,o,s,fn(e,s)):qr(n,o,s,fn(e,s),[0,0,0])}function nf(e,t,n=null){const r=ae(e,["positions","position","pos","coordinates","coords"]),o=$t(ae(e,["cell","cell_vectors","box"]));if(!r||!o)return null;const s=Math.min(Math.max(0,Math.floor(t)),Math.floor(r.length/3)),i=new F,a=new F;let c=0;if(n===null){const l=e?.header.coordinates==="unwrapped",u=l?ae(e,["unwrapped_positions","unwrapped_position"]):null,d=u&&u.length>=s*3?u:r,m=l&&d===r?Co(e,["unwrapped_image_shifts","unwrapped_images"]):null;for(let g=0;g=s||u.length!==3||!u.every(Number.isInteger)||(a.fromArray(r,l*3),i.x+=a.dot(o.reciprocal[0])+u[0],i.y+=a.dot(o.reciprocal[1])+u[1],i.z+=a.dot(o.reciprocal[2])+u[2],c+=1);return c===0?null:(i.multiplyScalar(1/c),[i.x,i.y,i.z])}function Yc(e){return!!$t(ae(e,["cell","cell_vectors","box"]))}function Qc(e){const t=$t(ae(e,["cell","cell_vectors","box"]));return fn(e,t)}function $t(e){return!e||e.length<9?null:Zc(new F(e[0],e[1],e[2]),new F(e[3],e[4],e[5]),new F(e[6],e[7],e[8]))}function Zc(e,t,n){const r=new F().crossVectors(t,n),o=e.dot(r);return!Number.isFinite(o)||Math.abs(o)<1e-10?null:{vectors:[e,t,n],reciprocal:[r.multiplyScalar(1/o),new F().crossVectors(n,e).multiplyScalar(1/o),new F().crossVectors(e,t).multiplyScalar(1/o)]}}function fn(e,t){if(!t)return[!1,!1,!1];const n=e?.header.pbc;return n?[!!n[0],!!n[1],!!n[2]]:[!0,!0,!0]}function rf(e,t,n,r){const o=e.map((u,d)=>n[d]?La(u):0),s=t.map((u,d)=>n[d]?La(u):0),i=o.map((u,d)=>Math.min(u,s[d])),a=s.map((u,d)=>Math.max(u,o[d])),c=[];for(let u=i[0];u<=a[0];u+=1)for(let d=i[1];d<=a[1];d+=1)for(let m=i[2];m<=a[2];m+=1)c.push([u,d,m]);c.sort((u,d)=>Ba(u)-Ba(d)||u[0]-d[0]||u[1]-d[1]||u[2]-d[2]);const l=r>0?Math.max(1,Math.floor(Yn/r)):Tr;return c.slice(0,Math.min(Tr,l))}function of(e,t){const n=e.length*t.length,r=new Uint32Array(n),o=new Int8Array(n*3);let s=0;for(const i of t)for(const a of e)r[s]=a,o.set(i,s*3),s+=1;return{instanceToAtom:r,instanceImages:o}}function Ta(e,t){if(!t||t.length1e-24&&(n+=1)}if(n===0)return{instances:[],total:0};const r=Math.min(n,Yd),o=[];let s=0,i=0,a=Math.floor((i+.5)*n/r);for(let c=0;c=a&&(o.push(c),i+=1,a=Math.floor((i+.5)*n/r)),s+=1)}return{instances:o,total:n}}function sf(e,t,n,r=null){const o=e.instanceToAtom.length,s=Jc(t,o),i=t.mode==="ribbon"||o===0?"none":s?"points":"instances",a=tl(e,t),c=a.length===0?"none":t.mode==="lines"||s||a.length>No?"lines":"instances",l=t.forces?Ta(e,n):{instances:[],total:0},u=t.velocities?Ta(e,r):{instances:[],total:0};return{atomKind:i,atomCount:o,bondKind:c,bondSegments:a,cellLineCount:t.cell&&e.basis?e.images.length*12:0,forceInstances:l.instances,forceTotal:l.total,velocityInstances:u.instances,velocityTotal:u.total}}function Jc(e,t){return e.mode==="lines"||t>Yn||t>ji}function el(e,t){return e.quality==="high"&&t<=Wd}function af(e){return{atomKind:e.atomKind,atomCount:e.atomCount,bondKind:e.bondKind,bondCount:e.bondSegments.length,cellLineCount:e.cellLineCount,forceCount:e.forceInstances.length,velocityCount:e.velocityInstances.length}}function cf(e,t){return e.atomKind===t.atomKind&&e.atomCount===t.atomCount&&e.bondKind===t.bondKind&&e.bondCount===t.bondCount&&e.cellLineCount===t.cellLineCount&&e.forceCount===t.forceCount&&e.velocityCount===t.velocityCount}function tl(e,t){if(t.mode==="spacefill"||t.mode==="ribbon")return[];const n=new Set(e.visibleAtoms),r=[];for(const o of e.images){const s=Kt(o,e.basis);for(const[i,a]of e.bonds){if(!n.has(i)||!n.has(a))continue;(t.wrap==="atom"?df(e.positions,i,a,e.basis,e.pbc,e.cellCenter):t.wrap==="unwrapped"?ff(e.positions,i,a,e.basis,e.pbc):[il(e.positions,i,a)]).forEach(({from:l,to:u})=>r.push({from:l.add(s),to:u.add(s)}))}}return r}function Js(e,t,n){if(!n)return{segments:tl(e,t).map(l=>({...l,context:!1})),contextAtoms:[]};if(t.mode==="spacefill"||t.mode==="ribbon")return{segments:[],contextAtoms:[]};const r=new Set(e.visibleAtoms),o=new Set(e.images.map(lo)),s=new Map,i=new Map,a=e.bonds.filter(([l,u])=>r.has(l)&&r.has(u)).map(([l,u])=>({a:l,b:u,shift:t.wrap==="atom"||t.wrap==="unwrapped"?nl(e.positions,l,u,e.basis,e.pbc):[0,0,0]})),c=(l,u,d,m)=>{const g=`${l}:${lo(u)}`,h=`${d}:${lo(m)}`,y=go[l]?Math.round(c.getComponent(l)-a.getComponent(l)):0)}function lf(e,t,n){return n.length>1||n.some(o=>o.some(s=>s!==0))||e===0||t<=e*3.2}function uf(e){if(e.length===0)return{count:0,span:[0,0,0]};const t=[...e[0]],n=[...e[0]];for(const r of e.slice(1))for(let o=0;o<3;o+=1)t[o]=Math.min(t[o],r[o]),n[o]=Math.max(n[o],r[o]);return{count:e.length,span:[n[0]-t[0],n[1]-t[1],n[2]-t[2]]}}function rl(e,t,n){const r=ae(t,["positions","position","pos","coordinates","coords"]),o=Math.min(e.topology.atom_count,Math.floor((r?.length??0)/3)),s=Ii(e,e.topology.atom_count),i=new Set,a=new Set,c=new Map((e.topology.residues??[]).map(h=>[h.index,h]));for(const[h,y]of Ni(e,e.topology.atom_count)){y.forEach(x=>a.add(x));const A=c.get(h);A&&(A.category==="water"||ef.has((A.name??"").trim().toUpperCase()))&&Oa(y,s)&&y.forEach(x=>i.add(x))}if(!r||o===0)return i;const l=$t(ae(t,["cell","cell_vectors","box"])),u=fn(t,l),d=Xc(t,o)??qr(r,o,l,u,[0,0,0]),m=n??Fi(e.topology.bonds,o),g=n!==void 0||m.length>0?m:al(d,s,o,l,u);for(const h of cl(o,g))h.some(y=>a.has(y))||Oa(h,s)&&kf(h,g,s)&&h.forEach(y=>i.add(y));return i}function ol(e){const t=e.topology.atom_names;if(!e.topology.residues?.length)return[];const n=new Map(Ni(e,e.topology.atom_count)),r=Ii(e,e.topology.atom_count),o=Fi(e.topology.bonds,e.topology.atom_count),s=If(e.topology.atom_count,o),i=[];for(const a of[...e.topology.residues].sort((c,l)=>c.index-l.index)){if(a.category!=="amino-acid"&&!tf.has((a.name??"").trim().toUpperCase()))continue;const c=n.get(a.index)??[],l=t?.length?Nf(a.index,c,t,r):null,u=e.topology.bond_source==="topology"?Ff(a.index,c,r,s):null;(l??u)&&i.push({...l??u,chainId:a.chain_id??null,segmentId:a.segment_id??null,sequenceNumber:a.sequence_number??null,insertionCode:a.insertion_code??null})}return Ef(i,o).flatMap((a,c)=>a.map(l=>({...l,runIndex:c})))}function Ci(e,t,n=1){const r=Number.isFinite(n)?Math.max(.1,n):1;return t==="spacefill"?Pf(e)*r:t==="licorice"?.22*r:t==="lines"?.075*r:t==="ribbon"?0:t==="polyhedra"?Math.max(.18,(Qn[e]??.78)*.3)*r:Math.max(.22,(Qn[e]??.78)*.43)*r}function df(e,t,n,r,o,s=new F){const i=new F().fromArray(e,t*3),a=new F().fromArray(e,n*3);if(!r||!o.some(Boolean))return[{from:i,to:a}];const c=Wt(i.clone().sub(s),r),l=Wt(a.clone().sub(s),r).sub(c),u=Pr(l,r,o),d=[0,1],m=[c.x,c.y,c.z],g=[u.x,u.y,u.z];for(let A=0;A<3;A+=1){if(!o[A]||Math.abs(g[A])<1e-12)continue;const x=m[A]+g[A],j=Math.min(m[A],x),k=Math.max(m[A],x),M=Math.ceil(j-.5+1e-9),C=Math.floor(k-.5-1e-9);for(let N=M;N<=C;N+=1){const E=(N+.5-m[A])/g[A];E>1e-9&&E<1-1e-9&&d.push(E)}}d.sort((A,x)=>A-x);const h=d.filter((A,x)=>x===0||Math.abs(A-d[x-1])>1e-8),y=[];for(let A=0;A+11e-10&&y.push({from:C,to:N})}return y}function Kt(e,t){return t?Ft(new F(...e),t):new F}function sl(e,t,n=new F){const r=[];for(let o=0;o<=1;o+=1)for(let s=0;s<=1;s+=1)for(let i=0;i<=1;i+=1)r.push(Kt([t[0]+o,t[1]+s,t[2]+i],e).add(n).addScaledVector(e.vectors[0],-.5).addScaledVector(e.vectors[1],-.5).addScaledVector(e.vectors[2],-.5));return r}function Xn(e,t,n,r){if(!n||!r.some(Boolean))return t.clone();const o=Wt(e,n),s=Pr(Wt(t,n).sub(o),n,r);return e.clone().add(Ft(s,n))}function il(e,t,n){return{from:new F().fromArray(e,t*3),to:new F().fromArray(e,n*3)}}function ff(e,t,n,r,o){const s=il(e,t,n),i=nl(e,t,n,r,o);if(i.every(u=>u===0))return[s];const a=Kt(i,r),c=s.from.clone().add(s.to).add(a).multiplyScalar(.5),l=s.to.clone().add(s.from).sub(a).multiplyScalar(.5);return[{from:s.from,to:c},{from:s.to,to:l}]}function mf(e,t,n,r,o,s,i,a){if(i==="unwrapped"){const l=Co(e,["unwrapped_image_shifts","unwrapped_images"]),u=ae(e,["unwrapped_positions","unwrapped_position"]),d=u&&u.length>=n*3?new Float32Array(u.subarray(0,n*3)):l&&l.length>=n*3?ei(t,l,n,r,o):new Float32Array(t.subarray(0,n*3));return{positions:d,baseImages:Pa(l,t,d,n,r,o)}}if(i==="none")return{positions:new Float32Array(t.subarray(0,n*3)),baseImages:new Int32Array(n*3)};if(i==="molecule"){const l=Sf(t,n,r,o,s.moleculeGroups,s.bonds,a);return{positions:l,baseImages:ti(t,l,n,r,o)}}if(pf(a)){const l=ae(e,["centered_positions","centered_position"]),u=Co(e,["centered_image_shifts","centered_images"]);if(l&&l.length>=n*3||u&&u.length>=n*3){const d=l&&l.length>=n*3?new Float32Array(l.subarray(0,n*3)):ei(t,u,n,r,o);return{positions:d,baseImages:Pa(u,t,d,n,r,o)}}}const c=qr(t,n,r,o,a);return{positions:c,baseImages:ti(t,c,n,r,o)}}function ei(e,t,n,r,o){const s=new Float32Array(e.subarray(0,n*3));if(!r)return s;const i=new F;for(let a=0;a=r*3?new Int32Array(e.subarray(0,r*3)):ti(t,n,r,o,s)}function hf(e){return!e||e.length!==3?[0,0,0]:e.map(t=>Number.isFinite(t)?t:0)}function pf(e){return e[0]===0&&e[1]===0&&e[2]===0}function gf(e,t){const n=t??[!1,!1,!1];if(!e||!n.some(Boolean))return new Gs().identity();const r=bf(e),o=new Gs().set(0,0,0,0,0,0,0,0,0),s=o.elements;return r.forEach((i,a)=>{const c=n[a]?-1:1;s[0]+=c*i.x*i.x,s[1]+=c*i.y*i.x,s[2]+=c*i.z*i.x,s[3]+=c*i.x*i.y,s[4]+=c*i.y*i.y,s[5]+=c*i.z*i.y,s[6]+=c*i.x*i.z,s[7]+=c*i.y*i.z,s[8]+=c*i.z*i.z}),o}function bf(e){const t=e.vectors[0].clone().normalize(),n=e.vectors[1].clone().addScaledVector(t,-e.vectors[1].dot(t));n.lengthSq()<1e-20&&n.copy(Math.abs(t.x)<.8?new F(1,0,0):new F(0,1,0)).addScaledVector(t,-n.dot(t)),n.normalize();const r=new F().crossVectors(t,n).normalize();return r.dot(e.vectors[2])<0&&r.negate(),n.crossVectors(r,t).normalize(),[t,n,r]}function yf(e,t,n,r){const o=new Float32Array(e.subarray(0,t*3));if(n.equals(new Gs().identity()))return o;const s=new F;for(let i=0;iWt(l.fromArray(e,g*3),n)),d=Array.from({length:t},()=>[]);s.forEach(([m,g])=>{d[m]?.push(g),d[g]?.push(m)});for(const[,m]of o){if(m.length===0)continue;m.forEach(M=>c.add(M));const g=new Set(m),h=new Map,y=m[0];h.set(y,u[y].clone());const A=[y];let x=0;for(;xj.add(h.get(M))),j.multiplyScalar(1/m.length);const k=new F(r[0]?qe(j.x-i[0]):0,r[1]?qe(j.y-i[1]):0,r[2]?qe(j.z-i[2]):0);m.forEach(M=>Ft(h.get(M).clone().sub(k),n).toArray(a,M*3))}for(let m=0;m0?cl(t,n).map((r,o)=>[o,r]):Ni(e,t)}function qr(e,t,n,r,o){const s=new Float32Array(e.subarray(0,t*3));if(!n||!r.some(Boolean))return s;const i=new F;for(let a=0;ao.index)),r=new Map;return e.topology.atom_residue_index.slice(0,t).forEach((o,s)=>{if(!Number.isInteger(o)||!n.has(o))return;const i=r.get(o)??[];i.push(s),r.set(o,i)}),[...r.entries()].sort(([o],[s])=>o-s)}function al(e,t,n,r,o){if(n>5e4)return[];const s=t.reduce((d,m)=>Math.max(d,Qn[m]??.78),.78),i=Math.max(1.4,s*2.5),a=new Map,c=vf(r,o,Math.max(s*2*1.22,Da));if(!c||n*c.length*27>Zd)return[];const l=[];let u=0;for(let d=0;dXd)return[];for(const[R,ee,le,de]of _){const I=Math.hypot(M-ee,C-le,N-de),J=A.get(R);(J===void 0||I.2&&M<=$&&(l.push([k,d]),l.length>Qd))return[]}const x=`${Math.floor(g/i)}:${Math.floor(h/i)}:${Math.floor(y/i)}`,j=a.get(x)??[];j.push([d,g,h,y]),a.set(x,j)}return l}function cl(e,t){const n=Array.from({length:e},()=>[]);t.forEach(([s,i])=>{s<0||i<0||s>=e||i>=e||(n[s].push(i),n[i].push(s))});const r=new Uint8Array(e),o=[];for(let s=0;s0;){const c=a.pop();i.push(c);for(const l of n[c])r[l]||(r[l]=1,a.push(l))}o.push(i)}return o}function Oa(e,t){let n=0,r=0,o=0;for(const s of e)if(t[s]===8)n+=1;else if(t[s]===1)r+=1;else if(!t[s])o+=1;else return!1;return n===1&&r===2&&o<=1}function kf(e,t,n){const r=new Set(e),o=e.find(i=>n[i]===8);if(o===void 0)return!1;let s=0;for(const[i,a]of t)(i===o&&r.has(a)&&n[a]===1||a===o&&r.has(i)&&n[i]===1)&&(s+=1);return s===2}function Fi(e,t){if(!e||e.length===0)return[];const n=[];if(typeof e[0]=="number"){const r=e;for(let o=0;o+1=0&&n>=0&&tRf[e.topology.symbols?.[r]??""]??0)}function vf(e,t,n){if(!e||!t.some(Boolean))return[new F];const r=[],o=e.reciprocal.map((s,i)=>t[i]?Math.max(1,Math.ceil(s.length()*n-1e-12)):0);if(o.reduce((s,i)=>s*(i*2+1),1)>Jd)return null;for(let s=-o[0];s<=o[0];s+=1)for(let i=-o[1];i<=o[1];i+=1)for(let a=-o[2];a<=o[2];a+=1)r.push(Ft(new F(s,i,a),e));return r}function Wt(e,t){return new F(e.dot(t.reciprocal[0]),e.dot(t.reciprocal[1]),e.dot(t.reciprocal[2]))}function Ft(e,t){return new F().addScaledVector(t.vectors[0],e.x).addScaledVector(t.vectors[1],e.y).addScaledVector(t.vectors[2],e.z)}function qe(e){const t=Math.floor(e);return t+(e-t>=.5?1:0)}function Pr(e,t,n){const r=e.clone();n[0]&&(r.x-=qe(r.x)),n[1]&&(r.y-=qe(r.y)),n[2]&&(r.z-=qe(r.z));const o=[0,1,2].filter(h=>n[h]);if(o.length===0)return r;const s=[],i=Array.from({length:o.length},()=>Array(o.length).fill(0));o.forEach((h,y)=>{const A=t.vectors[h].clone();for(let x=0;xh.dot(a)),l=Array(o.length).fill(0);for(let h=o.length-1;h>=0;h-=1){let y=c[h];for(let A=h+1;A{if(h<0){yg.setComponent(h,g.getComponent(h)+u[y])),g}function jf(e,t,n){let r=0;for(let o=0;o=t&&i<=n&&r.push(i),s>0&&a>=t&&a<=n&&r.push(a)}return r}function Nf(e,t,n,r){const o=new Map(t.map(l=>[_f(n[l]),l])),s=o.get("N"),i=o.get("CA"),a=o.get("C"),c=o.get("O");return s===void 0||i===void 0||a===void 0||c===void 0||r[s]!==7||r[i]!==6||r[a]!==6||r[c]!==8?null:{residueIndex:e,n:s,ca:i,c:a,o:c}}function Ff(e,t,n,r){const o=new Set(t),s=new Map;for(const l of t)if(n[l]===7){for(const u of r[l]??[])if(!(!o.has(u)||n[u]!==6))for(const d of r[u]??[]){if(!o.has(d)||d===l||n[d]!==6)continue;const m=(r[d]??[]).filter(g=>o.has(g)&&n[g]===8).sort((g,h)=>g-h);m.length!==0&&s.set(`${l}:${u}:${d}`,{residueIndex:e,n:l,ca:u,c:d,o:m[0]})}}let i=[...s.values()];const a=i.filter(({c:l})=>(r[l]??[]).some(u=>!o.has(u)&&n[u]===7));a.length>0&&(i=a);const c=i.filter(({c:l})=>!(r[l]??[]).some(u=>n[u]===1));return c.length>0&&(i=c),i.length===1?i[0]:null}function If(e,t){const n=Array.from({length:e},()=>[]);return t.forEach(([r,o])=>{n[r]?.push(o),n[o]?.push(r)}),n}function Ef(e,t){const n=new Set(t.flatMap(([i,a])=>[`${i}:${a}`,`${a}:${i}`])),r=[];let o=[];const s=()=>{o.length>=3&&r.push(o),o=[]};for(const i of e){const a=o[o.length-1],c=!a||i.residueIndex>a.residueIndex,l=!a||i.chainId===a.chainId,u=!a||i.segmentId===a.segmentId,d=!a||$f(a,i),m=!a||t.length===0||n.has(`${a.c}:${i.n}`),g=!a||a.sequenceNumber!=null&&i.sequenceNumber!=null||i.residueIndex===a.residueIndex+1;(!c||!l||!u||!d||!m||!g)&&s(),o.push(i)}return s(),r}function $f(e,t){if(e.sequenceNumber==null||t.sequenceNumber==null||t.sequenceNumber===e.sequenceNumber+1)return!0;if(t.sequenceNumber!==e.sequenceNumber)return!1;const n=(e.insertionCode??"").trim(),r=(t.insertionCode??"").trim();return r?n?r.length===1&&n.length===1&&r.charCodeAt(0)===n.charCodeAt(0)+1:r==="A":!1}function _f(e){return(e??"").trim().toUpperCase()}function La(e){return Number.isFinite(e)?Math.max(-2,Math.min(2,Math.trunc(e))):0}function Ba(e){return Math.abs(e[0])+Math.abs(e[1])+Math.abs(e[2])}function lo(e){return`${e[0]}:${e[1]}:${e[2]}`}const Rf={H:1,He:2,Li:3,Be:4,B:5,C:6,N:7,O:8,F:9,Ne:10,Na:11,Mg:12,Al:13,Si:14,P:15,S:16,Cl:17,Ar:18,K:19,Ca:20,Sc:21,Ti:22,V:23,Cr:24,Mn:25,Fe:26,Co:27,Ni:28,Cu:29,Zn:30,Ga:31,Ge:32,As:33,Se:34,Br:35,Kr:36,Rb:37,Sr:38,Y:39,Zr:40,Nb:41,Mo:42,Tc:43,Ru:44,Rh:45,Pd:46,Ag:47,Cd:48,In:49,Sn:50,Sb:51,Te:52,I:53,Xe:54,Cs:55,Ba:56,La:57,Ce:58,Pr:59,Nd:60,Pm:61,Sm:62,Eu:63,Gd:64,Tb:65,Dy:66,Ho:67,Er:68,Tm:69,Yb:70,Lu:71,Hf:72,Ta:73,W:74,Re:75,Os:76,Ir:77,Pt:78,Au:79,Hg:80,Tl:81,Pb:82,Bi:83,Po:84,At:85,Rn:86,Fr:87,Ra:88,Ac:89,Th:90,Pa:91,U:92,Np:93,Pu:94,Am:95,Cm:96,Bk:97,Cf:98,Es:99,Fm:100,Md:101,No:102,Lr:103,Rf:104,Db:105,Sg:106,Bh:107,Hs:108,Mt:109,Ds:110,Rg:111,Cn:112,Nh:113,Fl:114,Mc:115,Lv:116,Ts:117,Og:118},Qn={1:.31,2:.28,3:1.28,4:.96,5:.84,6:.76,7:.71,8:.66,9:.57,10:.58,11:1.66,12:1.41,13:1.21,14:1.11,15:1.07,16:1.05,17:1.02,18:1.06,19:2.03,20:1.76,21:1.7,22:1.6,23:1.53,24:1.39,25:1.39,26:1.32,27:1.26,28:1.24,29:1.32,30:1.22,31:1.22,32:1.2,33:1.19,34:1.2,35:1.2,36:1.16,37:2.2,38:1.95,39:1.9,40:1.75,41:1.64,42:1.54,43:1.47,44:1.46,45:1.42,46:1.39,47:1.45,48:1.44,49:1.42,50:1.39,51:1.39,52:1.38,53:1.39,54:1.4,55:2.44,56:2.15,57:2.07,58:2.04,59:2.03,60:2.01,61:1.99,62:1.98,63:1.98,64:1.96,65:1.94,66:1.92,67:1.92,68:1.89,69:1.9,70:1.87,71:1.87,72:1.75,73:1.7,74:1.62,75:1.51,76:1.44,77:1.41,78:1.36,79:1.36,80:1.32,81:1.45,82:1.46,83:1.48,84:1.4,85:1.5,86:1.5,87:2.6,88:2.21,89:2.15,90:2.06,91:2,92:1.96,93:1.9,94:1.87,95:1.8,96:1.69,97:1.68,98:1.68,99:1.65,100:1.67,101:1.73,102:1.76,103:1.61,104:1.57,105:1.49,106:1.43,107:1.41,108:1.34,109:1.29,110:1.28,111:1.21,112:1.22,113:1.36,114:1.43,115:1.62,116:1.75,117:1.65,118:1.57},Da=.85,Tf={1:1.2,2:1.4,3:1.82,4:1.53,5:1.92,6:1.7,7:1.55,8:1.52,9:1.47,10:1.54,11:2.27,12:1.73,13:1.84,14:2.1,15:1.8,16:1.8,17:1.75,18:1.88,19:2.75,20:2.31,21:2.58,22:2.46,23:2.42,24:2.45,25:2.45,26:2.44,27:2.4,28:2.4,29:2.38,30:2.39,31:2.32,32:2.29,33:1.88,34:1.82,35:1.86,36:2.25,37:3.21,38:2.84,39:2.75,40:2.52,41:2.56,42:2.45,43:2.44,44:2.46,45:2.44,46:2.15,47:2.53,48:2.49,49:2.43,50:2.42,51:2.47,52:1.99,53:2.04,54:2.06,55:3.48,56:3.03,57:2.98,58:2.88,59:2.92,60:2.95,61:2.9,62:2.9,63:2.87,64:2.83,65:2.79,66:2.87,67:2.81,68:2.83,69:2.79,70:2.8,71:2.74,72:2.63,73:2.53,74:2.57,75:2.49,76:2.48,77:2.41,78:2.29,79:2.32,80:2.45,81:2.47,82:2.6,83:2.54};function Pf(e){return Tf[e]??(Qn[e]??.9)+.8}const $n=1e-12,Of=1e-12,Lf=1e-8,za=4096;function Bf(e,t,n){Vf(e),ll(t);const r=Es(t);if(n==="replace")return[r];if(n!=="toggle")throw new TypeError(`Unknown selection mode: ${String(n)}`);const o=e.findIndex(s=>Uf(s,t));return o===-1?[...e.map(Es),r]:e.filter((s,i)=>i!==o).map(Es)}function Df(e,t,n){if(!fl(e)||t.some(({image:s})=>s.some(i=>i!==0))&&!qf(n))return null;const o=new Float64Array(t.length*3);for(let s=0;s=e.length/3)return null;const a=ml(e,i.atom);if(a===null)return null;if(o.set(a,s*3),!!n)for(let c=0;c<3;c+=1){const l=i.image[c];o[s*3]+=l*n[c*3],o[s*3+1]+=l*n[c*3+1],o[s*3+2]+=l*n[c*3+2]}}return o}function ni(e,t,n={}){const r=[...t],o=zf(r.length);if(o===null)return ft(r,"selection-size");if(new Set(r).size!==r.length)return ft(r,"duplicate-atoms");if(!fl(e))return ft(r,"invalid-position");const s=[];for(const m of r){if(!dl(m)||m>=e.length/3)return ft(r,"invalid-index");const g=ml(e,m);if(g===null)return ft(r,"invalid-position");s.push(g)}const i=Kf(n);if(i===void 0)return ft(r,"invalid-periodic-context");const a=(m,g)=>Wf(oi(g,m),i);if(o==="distance"){const m=a(s[0],s[1]);return m===null?ft(r,"invalid-periodic-context"):Is(o,r,dn(m),"angstrom")}if(o==="angle"){const m=a(s[1],s[0]),g=a(s[1],s[2]);if(m===null||g===null)return ft(r,"invalid-periodic-context");const h=Gf(m,g);return h===null?ft(r,"degenerate-geometry"):Is(o,r,h,"degree")}const c=a(s[1],s[0]),l=a(s[1],s[2]),u=a(s[2],s[3]);if(c===null||l===null||u===null)return ft(r,"invalid-periodic-context");const d=Hf(c,l,u);return d===null?ft(r,"degenerate-geometry"):Is(o,r,d,"degree")}function zf(e){return e===2?"distance":e===3?"angle":e===4?"dihedral":null}function Is(e,t,n,r){return{ok:!0,kind:e,atomIndices:t,value:Math.abs(n)<=$n?0:n,unit:r}}function ft(e,t){return{ok:!1,atomIndices:e,reason:t}}function Vf(e){const t=new Set;for(const n of e){ll(n);const r=ri(n);if(t.has(r))throw new TypeError("Atom selection contains duplicates");t.add(r)}}function ll(e){if(!ul(e))throw new RangeError("Atom selection must contain an atom and integer image")}function ul(e){return dl(e.atom)&&Array.isArray(e.image)&&e.image.length===3&&e.image.every(Number.isInteger)}function Uf(e,t){return ri(e)===ri(t)}function ri(e){return`${e.atom}:${e.image[0]}:${e.image[1]}:${e.image[2]}`}function Es(e){return{atom:e.atom,image:[...e.image]}}function qf(e){if(!e||!Number.isInteger(e.length)||e.length<9)return!1;for(let t=0;t<9;t+=1)if(!Number.isFinite(e[t]))return!1;return!0}function dl(e){return Number.isInteger(e)&&e>=0}function fl(e){return Number.isInteger(e.length)&&e.length>=0&&e.length%3===0}function ml(e,t){const n=t*3,r=[e[n],e[n+1],e[n+2]];return r.every(Number.isFinite)?r:null}function oi(e,t){return[e[0]-t[0],e[1]-t[1],e[2]-t[2]]}function Fn(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function dn(e){return Math.hypot(e[0],e[1],e[2])}function Gf(e,t){const n=dn(e),r=dn(t);if(n<=$n||r<=$n)return null;const o=Fn(e,t)/(n*r);return gl(Math.acos(Qf(o,-1,1)))}function Hf(e,t,n){const r=dn(t);if(r<=$n)return null;const o=Ir(t,1/r),s=oi(e,Ir(o,Fn(e,o))),i=oi(n,Ir(o,Fn(n,o)));if(dn(s)<=$n||dn(i)<=$n)return null;const a=Fn(s,i),c=Fn(Yf(o,s),i);return gl(Math.atan2(c,a))}function Kf(e){if(e.mode!=="minimum-image")return null;if(e.pbc.length!==3||!e.pbc.every(s=>typeof s=="boolean"))return;if(!e.pbc.some(Boolean))return null;if(!Number.isInteger(e.cell.length)||e.cell.length<9)return;const t=[[e.cell[0],e.cell[1],e.cell[2]],[e.cell[3],e.cell[4],e.cell[5]],[e.cell[6],e.cell[7],e.cell[8]]];if(!t.flat().every(Number.isFinite))return;const n=t.filter((s,i)=>e.pbc[i]),r=[],o=Array.from({length:n.length},()=>Array(n.length).fill(0));for(let s=0;sFn(d,n)),o=Array(t.vectors.length).fill(0);let s=[...o],i=Va(r,t.r,s);for(let d=t.vectors.length-1;d>=0;d-=1){let m=r[d];for(let h=d+1;h{if(d<0)return Ua(m,i)&&(s=[...o],i=m),!0;let g=r[d];for(let M=d+1;Mza-c)return!1;for(const M of Xf(A,j,k)){if(c+=1,c>za)return!1;o[d]=M;const C=g-t.r[d][d]*M,N=m+C*C;if(N<=i+h&&!l(d-1,N))return!1}return!0};if(!l(t.vectors.length-1,0))return null;const u=[...e];return t.vectors.forEach((d,m)=>pl(u,d,s[m])),u}function Va(e,t,n){let r=0;for(let o=0;o=t&&i<=n&&(yield i,o+=1),s>0&&a>=t&&a<=n&&(yield a,o+=1)}}function Ua(e,t){return e({atom:z,image:[...G]})),l=Sl(c.length),u=Ml(l),d=kl(e,c,l),m=Array(t).fill(null),g=Array(t).fill(null),h=Array(t).fill(null),y=Object.freeze(Array.from({length:t},(z,G)=>G)),A=Array(t).fill(null),x=Object.freeze(Ei(t)),j=new Set;let k=0,M=0,C=0;const N=t>=1e4?yl:bl,E=Math.max(1,Math.ceil(t/N));a&&a($s(d,l,u,Or(),x,m,y,A,0,!1));for(let z=0;z=3))throw new Error("Trajectory frames could not be loaded",{cause:_});m[z]=null}k=z+1,a&&k=E)&&(a($s(d,l,u,Or(),x,m,y,A,k,!1)),M=k)}un(s);const{axis:$,xValues:L}=wl(g,h,j),U=$s(d,l,u,$,L,m,y,A,k,!0);return a?.(U),U}async function Jf({manifest:e,frameCount:t,definitions:n,wrap:r,signal:o,loadFrame:s,title:i,onProgress:a}){lm(e,t,n,r),un(o);const c=n.map(U=>{const z=U.selections.map(({atom:_,image:R})=>({atom:_,image:[...R]})),G=Sl(z.length);return{id:U.id.trim(),label:U.label?.trim()||kl(e,z,G),selections:z,minimumImage:U.minimumImage,kind:G,unit:Ml(G),values:Array(t).fill(null)}}),l=c[0].unit;if(c.some(U=>U.unit!==l))throw new TypeError("Compared measurements must use the same unit");const u=i?.trim()||(c.length===1?c[0].label:"Measurement comparison"),d=Array(t).fill(null),m=Array(t).fill(null),g=Object.freeze(Array.from({length:t},(U,z)=>z)),h=Array(t).fill(null),y=Object.freeze(Ei(t)),A=new Set;let x=0,j=0,k=0;const M=t>=1e4?yl:bl,C=Math.max(1,Math.ceil(t/M)),N=(U,z,G)=>am(u,l,U,z,g,h,c,x,G);a?.(N(Or(),y,!1));for(let U=0;U=3))throw new Error("Trajectory frames could not be loaded",{cause:G});for(const _ of c)_.values[U]=null}x=U+1,a&&x=C)&&(a(N(Or(),y,!1)),j=x)}un(o);const{axis:E,xValues:$}=wl(d,m,A),L=N(E,$,!0);return a?.(L),L}function em(e){const t=ut(e.axis.label,e.axis.unit),n=ut(Gr(e.kind),Go(e.unit)),r=[[t,n]],o=Math.min(e.xValues.length,e.values.length);for(let s=0;ss.map(Cl).join(",")).join(` +`)} +`}function Ga(e,t){const n=new Set(e.lines.map(({kind:r})=>r));return Object.freeze({requestId:t,kind:e.lines.length>1?"comparison":"measurement",title:e.title,xLabel:e.axis.label,xUnit:e.axis.unit,yLabel:n.size===1?Gr(e.lines[0].kind):"Measurement",yUnit:Go(e.unit),xValues:e.xValues,frameIndices:e.frameIndices,frameKeys:e.frameKeys,lines:Object.freeze(e.lines.map((r,o)=>Object.freeze({id:r.id,label:r.label,values:r.values,color:_o[o%_o.length],selection:r.selections,minimumImage:r.minimumImage,discontinuity:r.unit==="degree"?180:void 0}))),loadedCount:e.loadedCount,totalCount:e.frameIndices.length,complete:e.complete})}function tm(e){Ri(e);const t=e.frameIndices?.length===e.xValues.length?e.frameIndices:void 0,n=e.frameKeys?.length===e.xValues.length?e.frameKeys:void 0,r=[[...t?["Frame index"]:[],...n?["Source","Segment index","Source frame index"]:[],ut(e.xLabel,e.xUnit),...e.lines.map(o=>ut(o.label,e.yUnit))]];for(let o=0;ojn(i.values[o],10))])}return`${r.map(o=>o.map(Cl).join(",")).join(` +`)} +`}function nm(e,t={}){Ri(e);const n=Eo(t.width??1200,"width"),r=Eo(t.height??720,"height"),s={top:82+Math.ceil(e.lines.length/2)*22,right:48,bottom:86,left:96},i=Math.max(1,n-s.left-s.right),a=Math.max(1,r-s.top-s.bottom),c=_n(e.xValues.filter(Number.isFinite)),l=Fl(e),u=k=>s.left+(k-c[0])/(c[1]-c[0])*i,d=k=>s.top+(1-(k-l[0])/(l[1]-l[0]))*a,m=mn(c[0],c[1],5),g=mn(l[0],l[1],5),h=ut(e.xLabel,e.xUnit),y=ut(e.yLabel,e.yUnit),A=Il(e),x=e.lines.map((k,M)=>{const C=si(k.color,M),N=fm(e.xValues,k.values,k.discontinuity,u,d);return N?``:""}),j=x.some(Boolean);return['',``,'',Re(e.title),"",'',Re(`${e.title}; ${A}.`),"",'',`${Re(e.title)}`,`${Re(A)}`,...e.lines.map((k,M)=>{const C=M%2,N=Math.floor(M/2),E=s.left+C*Math.max(1,i/2),$=83+N*22,L=si(k.color,M);return[``,`${Re(k.label)}`].join("")}),...g.flatMap(k=>{const M=It(d(k));return[``,`${Re(hn(k))}`]}),...m.flatMap(k=>{const M=It(u(k));return[``,`${Re(hn(k))}`]}),``,``,...x,j?"":`No valid data`,`${Re(h)}`,`${Re(y)}`,""].join("")}function rm(e,t={}){Ri(e);const n=$o(t.width??720,"width"),r=$o(t.height??432,"height"),s={top:58+Math.ceil(e.lines.length/2)*14,right:30,bottom:54,left:62},i=Math.max(1,n-s.left-s.right),a=Math.max(1,r-s.top-s.bottom),c=_n(e.xValues.filter(Number.isFinite)),l=Fl(e),u=A=>s.left+(A-c[0])/(c[1]-c[0])*i,d=A=>s.top+(1-(A-l[0])/(l[1]-l[0]))*a,m=mn(c[0],c[1],5),g=mn(l[0],l[1],5),h=["1 1 1 rg",`0 0 ${T(n)} ${T(r)} re f`];for(const A of g){const x=r-d(A);h.push("0.886 0.91 0.902 RG","0.6 w",`${T(s.left)} ${T(x)} m ${T(n-s.right)} ${T(x)} l S`,nt(hn(A),s.left-8,x-3.2,{align:"right",color:[.35,.4,.388],size:8}))}for(const A of m){const x=u(A);h.push("0.929 0.945 0.941 RG","0.6 w",`${T(x)} ${T(s.bottom)} m ${T(x)} ${T(r-s.top)} l S`,nt(hn(A),x,s.bottom-17,{align:"center",color:[.35,.4,.388],size:8}))}h.push("0.518 0.565 0.553 RG","0.8 w",`${T(s.left)} ${T(s.bottom)} m ${T(n-s.right)} ${T(s.bottom)} l S`,`${T(s.left)} ${T(s.bottom)} m ${T(s.left)} ${T(r-s.top)} l S`);let y=!1;return e.lines.forEach((A,x)=>{const j=mm(e.xValues,A.values,A.discontinuity,u,d,r);if(!j)return;y=!0;const[k,M,C]=Ka(A.color,x);h.push(`${T(k)} ${T(M)} ${T(C)} RG`,"1.7 w","1 J 1 j",j,"S")}),y||h.push(nt("No valid data",s.left+i*.5,s.bottom+a*.5,{align:"center",color:[.482,.529,.518],size:9})),h.push(nt(e.title,s.left,r-27,{color:[.09,.137,.129],font:"F2",size:15}),nt(Il(e),s.left,r-42,{color:[.392,.439,.427],size:8.5})),e.lines.forEach((A,x)=>{const j=x%2,k=Math.floor(x/2),M=s.left+j*Math.max(1,i/2),C=r-58-k*14,[N,E,$]=Ka(A.color,x);h.push(`${T(N)} ${T(E)} ${T($)} RG`,"2 w",`${T(M)} ${T(C)} m ${T(M+14)} ${T(C)} l S`,nt(A.label,M+20,C-3,{color:[.224,.275,.259],size:8}))}),h.push(nt(ut(e.xLabel,e.xUnit),s.left+i*.5,17,{align:"center",color:[.161,.212,.2],size:9}),El(ut(e.yLabel,e.yUnit),18,s.bottom+a*.5,{color:[.161,.212,.2],size:9})),_l(n,r,h.filter(Boolean).join(` +`),e.title)}function om(e,t={}){const n=Eo(t.width??1200,"width"),r=Eo(t.height??720,"height"),o={top:78,right:48,bottom:86,left:96},s=Math.max(1,n-o.left-o.right),i=Math.max(1,r-o.top-o.bottom),a=Nl(e),c=_n(a.map(({x:k})=>k)),l=_n(a.map(({y:k})=>k)),u=k=>o.left+(k-c[0])/(c[1]-c[0])*s,d=k=>o.top+(1-(k-l[0])/(l[1]-l[0]))*i,m=mn(c[0],c[1],5),g=mn(l[0],l[1],5),h=um(e,u,d),y=dm(e,u,d),A=ut(e.axis.label,e.axis.unit),x=ut(Gr(e.kind),Go(e.unit)),j=e.complete?`${e.values.filter(k=>k!==null).length} valid frames`:`${e.loadedCount} frames loaded`;return['',``,'',Re(e.title),"",'',Re(`${e.title}; ${j}.`),"",'',`${Re(e.title)}`,`${Re(j)}`,...g.flatMap(k=>{const M=It(d(k));return[``,`${Re(hn(k))}`]}),...m.flatMap(k=>{const M=It(u(k));return[``,`${Re(hn(k))}`]}),``,``,h?``:`No valid measurements`,y,`${Re(A)}`,`${Re(x)}`,""].join("")}function sm(e,t={}){const n=$o(t.width??720,"width"),r=$o(t.height??432,"height"),o={top:52,right:30,bottom:54,left:62},s=Math.max(1,n-o.left-o.right),i=Math.max(1,r-o.top-o.bottom),a=Nl(e),c=_n(a.map(({x:k})=>k)),l=_n(a.map(({y:k})=>k)),u=k=>o.left+(k-c[0])/(c[1]-c[0])*s,d=k=>o.top+(1-(k-l[0])/(l[1]-l[0]))*i,m=mn(c[0],c[1],5),g=mn(l[0],l[1],5),h=ut(e.axis.label,e.axis.unit),y=ut(Gr(e.kind),Go(e.unit)),A=e.complete?`${e.values.filter(k=>k!==null).length} valid frames`:`${e.loadedCount} frames loaded`,x=["1 1 1 rg",`0 0 ${T(n)} ${T(r)} re f`];for(const k of g){const M=r-d(k);x.push("0.886 0.91 0.902 RG","0.6 w",`${T(o.left)} ${T(M)} m ${T(n-o.right)} ${T(M)} l S`,nt(hn(k),o.left-8,M-3.2,{align:"right",color:[.35,.4,.388],size:8}))}for(const k of m){const M=u(k);x.push("0.929 0.945 0.941 RG","0.6 w",`${T(M)} ${T(r-o.top)} m ${T(M)} ${T(o.bottom)} l S`,nt(hn(k),M,o.bottom-17,{align:"center",color:[.35,.4,.388],size:8}))}x.push("0.518 0.565 0.553 RG","0.8 w",`${T(o.left)} ${T(o.bottom)} m ${T(n-o.right)} ${T(o.bottom)} l S`,`${T(o.left)} ${T(r-o.top)} m ${T(o.left)} ${T(o.bottom)} l S`);const j=hm(e,u,d,r);if(j){x.push("0.075 0.498 0.471 RG","1.7 w","1 J 1 j",j,"S");for(const k of pm(e,u,d,r))x.push("0.075 0.498 0.471 rg",gm(k.x,k.y,2),"f")}else x.push(nt("No valid measurements",o.left+s*.5,o.bottom+i*.5,{align:"center",color:[.482,.529,.518],size:10}));return x.push(nt(e.title,o.left,r-27,{color:[.09,.137,.129],font:"F2",size:15}),nt(A,o.left,r-42,{color:[.392,.439,.427],size:8.5}),nt(h,o.left+s*.5,17,{align:"center",color:[.161,.212,.2],size:9}),El(y,18,o.bottom+i*.5,{color:[.161,.212,.2],size:9})),_l(n,r,x.filter(Boolean).join(` +`),e.title)}function xl(e,t,n,r,o){const s=ae(e,["cell","cell_vectors","box"]),i=$t(s),a=im(e,n),c=Df(t,r,i?s:null);if(!c)return null;const l=r.map((d,m)=>m);if(o&&(!s||!a.some(Boolean)))return null;const u=o?ni(c,l,{mode:"minimum-image",cell:s,pbc:a}):ni(c,l);return u.ok&&Number.isFinite(u.value)?u.value:null}function im(e,t){const n=e.header.pbc;return Array.isArray(n)&&n.length===3?[!!n[0],!!n[1],!!n[2]]:[t[0],t[1],t[2]]}function wl(e,t,n){if(Ha(e)&&n.size<=1){const r=[...n][0];return{axis:r?{kind:"time",label:"Time",unit:r}:{kind:"time",label:"Time"},xValues:e}}return Ha(t)?{axis:{kind:"step",label:"Step"},xValues:t}:{axis:Or(),xValues:Ei(e.length)}}function Ha(e){if(e.length<2||e.some(t=>t===null||!Number.isFinite(t)))return!1;for(let t=1;tu?Object.freeze({...u}):null)),loadedCount:c,complete:l})}function am(e,t,n,r,o,s,i,a,c){return Object.freeze({title:e,unit:t,axis:Object.freeze({...n}),xValues:Object.freeze([...r]),frameIndices:o,frameKeys:Object.freeze(s.map(l=>l?Object.freeze({...l}):null)),lines:Object.freeze(i.map(l=>Object.freeze({id:l.id,label:l.label,kind:l.kind,unit:l.unit,selections:Object.freeze(l.selections.map(({atom:u,image:d})=>Object.freeze({atom:u,image:Object.freeze([...d])}))),minimumImage:l.minimumImage,values:Object.freeze([...l.values])}))),loadedCount:a,complete:c})}function Sl(e){return e===2?"distance":e===3?"angle":"dihedral"}function Ml(e){return e==="distance"?"angstrom":"degree"}function kl(e,t,n){return`${Gr(n)} · ${t.map(r=>cm(e,r)).join("–")}`}function cm(e,t){const n=e.topology.symbols?.[t.atom]??xm[e.topology.atomic_numbers?.[t.atom]??0]??"X",r=t.image.map((s,i)=>{if(s===0)return"";const a=s>0?"+":"−",c=Math.abs(s)===1?"":Math.abs(s);return`${a}${c}${"abc"[i]}`}).join(""),o=`${n}${t.atom+1}`;return r?`${o} (${r})`:o}function vl(e,t,n,r){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("Frame count must be a non-negative integer");if(t>e.frame_count)throw new RangeError("Frame count exceeds the trajectory manifest");if(n.length<2||n.length>4)throw new RangeError("A measurement needs two to four selected atoms");if(!["atom","molecule","unwrapped","none"].includes(r))throw new TypeError("Unknown coordinate wrapping mode")}function lm(e,t,n,r){if(n.length<1||n.length>qa)throw new RangeError(`A comparison needs one to ${qa} measurements`);const o=new Set;for(const s of n){const i=s.id.trim();if(!i)throw new TypeError("Each compared measurement needs an id");if(o.has(i))throw new TypeError(`Duplicate measurement id: ${i}`);o.add(i),vl(e,t,s.selections,r)}}function jl(e){return!e||typeof e.source_id!="string"||!e.source_id||!Number.isSafeInteger(e.source_index)||e.source_index<0||!Number.isSafeInteger(e.segment_index)||e.segment_index<0?null:{source_id:e.source_id,source_index:e.source_index,segment_index:e.segment_index,step:typeof e.step=="number"&&Number.isFinite(e.step)?e.step:null,time:typeof e.time=="number"&&Number.isFinite(e.time)?e.time:null,time_unit:typeof e.time_unit=="string"&&e.time_unit.trim()?e.time_unit.trim():null}}function Or(){return{kind:"frame",label:"Frame"}}function Ei(e){return Array.from({length:e},(t,n)=>n+1)}function un(e){if(e.aborted)throw $i(e)}function $i(e,t){return e.reason!==void 0?e.reason:_i(t)?t:new DOMException("The operation was aborted","AbortError")}function _i(e){return e instanceof DOMException?e.name==="AbortError":!!(e&&typeof e=="object"&&"name"in e&&e.name==="AbortError")}function jn(e,t){return typeof e=="number"&&Number.isFinite(e)?String(Number(e.toPrecision(t))):""}function Cl(e){return/[",\r\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function ut(e,t){return t?`${e} [${t}]`:e}function Go(e){return e==="angstrom"?"Å":"°"}function Gr(e){return`${e[0].toUpperCase()}${e.slice(1)}`}function Nl(e){const t=Math.min(e.xValues.length,e.values.length),n=[];for(let r=0;r180&&(s=!1),o.push(`${s?"L":"M"}${It(t(c))} ${It(n(l))}`),s=!0,i=l}return o.join("")}function dm(e,t,n){const r=Math.min(e.xValues.length,e.values.length),o=i=>i<0||i>=r?!1:Number.isFinite(e.xValues[i])&&typeof e.values[i]=="number"&&Number.isFinite(e.values[i]),s=[];for(let i=0;i`);return s.join("")}function Io(e,t,n){if(t<0||n>=Math.min(e.xValues.length,e.values.length))return!1;const r=e.values[t],o=e.values[n];return!Number.isFinite(e.xValues[t])||!Number.isFinite(e.xValues[n])||typeof r!="number"||typeof o!="number"||!Number.isFinite(r)||!Number.isFinite(o)?!1:e.unit!=="degree"||Math.abs(o-r)<=180}function _n(e){if(e.length===0)return[0,1];let t=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;for(const r of e)t=Math.min(t,r),n=Math.max(n,r);if(t===n){const r=Math.max(Math.abs(t)*.05,.5);t-=r,n+=r}else{const r=(n-t)*.04;t-=r,n+=r}return[t,n]}function Fl(e){const t=_n(e.lines.flatMap(({values:n})=>n.filter(r=>typeof r=="number"&&Number.isFinite(r))));return e.yFloor===void 0||!Number.isFinite(e.yFloor)?t:[e.yFloor,t[1]>e.yFloor?t[1]:e.yFloor+1]}function mn(e,t,n){return Array.from({length:n},(r,o)=>e+(t-e)*o/Math.max(n-1,1))}function hn(e){const t=Math.abs(e);return t!==0&&(t>=1e4||t<.001)?e.toExponential(2):new Intl.NumberFormat("en",{maximumFractionDigits:4}).format(e)}function It(e){return Number(e.toFixed(3)).toString()}function Ri(e){if(e.lines.length<1||e.lines.length>32)throw new RangeError("A plot needs one to 32 series");if(!e.title.trim()||!e.xLabel.trim()||!e.yLabel.trim())throw new TypeError("Plot title and axis labels are required")}function fm(e,t,n,r,o){const s=[];let i=!1,a=null;const c=typeof n=="number"&&Number.isFinite(n)?Math.abs(n):null;for(let l=0;lc&&(i=!1),s.push(`${i?"L":"M"}${It(r(u))} ${It(o(d))}`),i=!0,a=d}return s.join("")}function mm(e,t,n,r,o,s){const i=[];let a=!1,c=null;const l=typeof n=="number"&&Number.isFinite(n)?Math.abs(n):null;for(let u=0;ul&&(a=!1),i.push(`${T(r(d))} ${T(s-o(m))} ${a?"l":"m"}`),a=!0,c=m}return i.join(` +`)}function Il(e){const t=e.complete?`${e.lines.length} series · ${e.totalCount.toLocaleString()} points`:`${Math.max(0,e.loadedCount).toLocaleString()} / ${Math.max(0,e.totalCount).toLocaleString()} points`;return e.context?`${t} · ${e.context}`:t}function si(e,t){const n=e?.trim();return n&&/^#[0-9a-f]{6}$/i.test(n)?n:_o[t%_o.length]}function Ka(e,t){const n=si(e,t);return[Number.parseInt(n.slice(1,3),16)/255,Number.parseInt(n.slice(3,5),16)/255,Number.parseInt(n.slice(5,7),16)/255]}function Re(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function Eo(e,t){if(!Number.isSafeInteger(e)||e<=0)throw new RangeError(`SVG ${t} must be a positive integer`);return e}function $o(e,t){if(!Number.isSafeInteger(e)||e<=0||e>14400)throw new RangeError(`PDF ${t} must be a positive integer no larger than 14400 points`);return e}function hm(e,t,n,r){const o=Math.min(e.xValues.length,e.values.length),s=[];let i=!1,a=null;for(let c=0;c180&&(i=!1),s.push(`${T(t(l))} ${T(r-n(u))} ${i?"l":"m"}`),i=!0,a=u}return s.join(` +`)}function pm(e,t,n,r){const o=Math.min(e.xValues.length,e.values.length),s=[],i=a=>a<0||a>=o?!1:Number.isFinite(e.xValues[a])&&typeof e.values[a]=="number"&&Number.isFinite(e.values[a]);for(let a=0;a>","<< /Type /Pages /Kids [3 0 R] /Count 1 >>",`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${T(e)} ${T(t)}] /Resources << /Font << /F1 5 0 R /F2 6 0 R >> >> /Contents 4 0 R >>`,`<< /Length ${o.length} >> +stream +${n} +endstream`,"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>","<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>",`<< /Title ${bm(r)} /Creator (PQViewer) /Producer (PQViewer) >>`];let i=`%PDF-1.4 +%PQV1 +`;const a=[0];for(let l=0;l> +`,i+=`startxref +${c} +%%EOF +`,new TextEncoder().encode(i)}function Rl(e){const t=[];for(const n of e)t.push(ym(n));return`(${t.map(n=>n===40||n===41||n===92?`\\${String.fromCharCode(n)}`:n<32||n>126?`\\${n.toString(8).padStart(3,"0")}`:String.fromCharCode(n)).join("")})`}function bm(e){let t="FEFF";for(let n=0;n`}function ym(e){const t=e.codePointAt(0)??63;return t<=255?t:new Map([[8211,150],[8212,151],[8216,145],[8217,146],[8220,147],[8221,148],[8226,149],[8230,133],[8364,128],[8482,153],[8722,45]]).get(t)??63}function T(e){if(!Number.isFinite(e))throw new Error("PDF contains a non-finite number");return(Math.abs(e)<5e-4?0:Number(e.toFixed(3))).toString()}const _o=Object.freeze(["#137f78","#b35c2e","#5468a8","#8b5a91","#4f7b45","#b08524","#366e83","#9a4d62"]),xm=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca"],_s="pqviewer.figure",Wa=1,wm=new Set(["positions","cell","forces","velocities","charges"]),Am=["unwrapped_positions","unwrapped_image_shifts"];function Ro(e){const t=We(e,"Figure recipe",["schema","schema_version","source","frame","scene","camera","output","annotations"]);if(t.schema!==_s)throw new Error(`Figure recipe schema must be ${_s}`);if(t.schema_version!==Wa)throw new Error(`Unsupported figure recipe version: ${String(t.schema_version)}`);const n=Ti(t.source),r=Im(t.frame);return Em(n,r),{schema:_s,schema_version:Wa,source:n,frame:r,scene:$m(t.scene),camera:Rm(t.camera),output:Tm(t.output),annotations:Pi(t.annotations,"Figure recipe annotations").map((o,s)=>Om(o,s))}}function Sm(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Figure recipe is not valid JSON")}return Ro(t)}function ii(e){return Ro(e)}function Mm(e){return`${JSON.stringify(ii(e),null,2)} +`}function km(e){if(!e.source)throw new Error("The dataset has no source information");return Ti(e.source)}function Xa(e){const t=Ti(e);return JSON.stringify({kind:t.kind,path:t.path,slice:t.slice,segments:t.segments.map(n=>({kind:n.kind,path:n.path,input:n.input,files:n.files}))})}function Rs(e,t){const n=new Gm,r=new Set(wm);t.header.coordinates==="unwrapped"&&Am.forEach(s=>r.add(s));const o=t.header.arrays.filter(s=>r.has(s.name.toLowerCase()));n.value(e.topology),n.value({arrays:o,pbc:t.header.pbc??null});for(const[s,i]of[...t.arrays.entries()].sort(([a],[c])=>a.localeCompare(c))){const a=s.toLowerCase();r.has(a)&&(n.value(a),n.value(i.constructor.name),n.bytes(new Uint8Array(i.buffer,i.byteOffset,i.byteLength)))}return`frame-v1:${n.digest()}`}function vm(e,t){try{return Xa(e)===Xa(t)}catch{return!1}}function jm(e,t){return!!(t.source&&vm(e.source,t.source))}function Mr(e,t){if(!e||!t)return!1;try{const n=ai(e,"First frame key"),r=ai(t,"Second frame key");return n.source_id===r.source_id&&n.source_index===r.source_index&&n.segment_index===r.segment_index&&n.step===r.step&&n.time===r.time&&n.time_unit===r.time_unit}catch{return!1}}function Ti(e){const t=We(e,"Figure source",["kind","path","slice","segments"]),n=gn(t.kind,"Figure source kind"),r=Li(t.path,"Figure source path"),o=t.slice===void 0?{start:null,stop:null,step:null}:Cm(t.slice),s=t.segments===void 0?[]:Pi(t.segments,"Figure source segments").map((i,a)=>Nm(i,a));if(s.length===0)throw new Error("Figure source must include at least one segment");return{kind:n,path:r,slice:o,segments:s}}function Cm(e){const t=We(e,"Figure source slice",["start","stop","step"]),n=So(t.start,"Figure source slice start"),r=So(t.stop,"Figure source slice stop"),o=So(t.step,"Figure source slice step");if(o===0)throw new Error("Figure source slice step cannot be zero");return{start:n,stop:r,step:o}}function Nm(e,t){const n=`Figure source segment ${t+1}`,r=We(e,n,["source_id","kind","path","input","frame_count","files"]),o=Qa(r.path,`${n} path`),s=Qa(r.input,`${n} input`),i=r.files===void 0?{}:Fm(r.files,`${n} files`);if(o===null&&s===null&&Object.keys(i).length===0)throw new Error(`${n} has no durable source`);return{source_id:Oi(r.source_id,`${n} source_id`),kind:gn(r.kind,`${n} kind`),path:o,input:s,frame_count:Lr(r.frame_count,`${n} frame_count`),files:i}}function Fm(e,t){const n=Lm(e,t),r={};for(const o of Object.keys(n).sort()){if(!o.trim()||Hm(o))throw new Error(`${t} contains an invalid role`);r[o]=Li(n[o],`${t}.${o}`)}return r}function Im(e){const t=We(e,"Figure frame",["index","key","fingerprint"]),n=gn(t.fingerprint,"Figure frame fingerprint");if(!/^frame-v1:[0-9a-f]{16}$/.test(n))throw new Error("Figure frame fingerprint is invalid");return{index:Lr(t.index,"Figure frame index"),key:ai(t.key,"Figure frame key"),fingerprint:n}}function Em(e,t){const n=e.segments[t.key.segment_index];if(!n)throw new Error("Figure frame segment is outside the source");if(Oi(n.source_id,"Figure source segment source_id")!==t.key.source_id)throw new Error("Figure frame source_id does not match its segment");if(t.key.source_index>=n.frame_count)throw new Error("Figure frame source_index is outside its segment");const r=e.segments.map(l=>l.frame_count),o=qm(r.reduce((l,u)=>l+u,0),e.slice);if(t.index>=o.count)throw new Error("Figure frame index is outside the source slice");const s=o.start+t.index*o.step;let i=0,a=-1,c=-1;for(let l=0;l=i&&sTl(i,`Figure selection atom ${a+1}`)),s=new Set;for(const i of o){const a=`${i.atom}:${i.image.join(",")}`;if(s.has(a))throw new Error("Figure selection contains duplicate atoms");s.add(a)}return{presentation:_m(t.presentation),selection:{atoms:o,intent:mt(n.intent,["measurement","set"],"Figure selection intent"),minimumImage:jt(n.minimumImage,"Figure selection minimumImage")},vectors:{forceScale:Gt(r.forceScale,"Figure force scale"),velocityScale:Gt(r.velocityScale,"Figure velocity scale")}}}function _m(e){const t=We(e,"Figure presentation",["mode","water","hydrogens","wrap","cellOrigin","mirror","images","cell","forces","velocities","atomScale","bondScale","color","quality"]),n=We(t.images,"Figure presentation images",["min","max"]),r=ci(n.min,"Figure presentation minimum image"),o=ci(n.max,"Figure presentation maximum image");return r.forEach((s,i)=>{if(s>o[i])throw new Error("Figure presentation image minimum cannot exceed its maximum")}),{mode:mt(t.mode,["ball-stick","spacefill","licorice","lines","ribbon","polyhedra"],"Figure presentation mode"),water:mt(t.water,["show","hide","only"],"Figure presentation water"),hydrogens:jt(t.hydrogens,"Figure presentation hydrogens"),wrap:mt(t.wrap,["atom","molecule","unwrapped","none"],"Figure presentation wrap"),cellOrigin:Mo(t.cellOrigin,"Figure presentation cellOrigin"),mirror:Vm(t.mirror,"Figure presentation mirror"),images:{min:r,max:o},cell:jt(t.cell,"Figure presentation cell"),forces:jt(t.forces,"Figure presentation forces"),velocities:jt(t.velocities,"Figure presentation velocities"),atomScale:Gt(t.atomScale,"Figure presentation atomScale"),bondScale:Gt(t.bondScale,"Figure presentation bondScale"),color:mt(t.color,["element","residue","chain"],"Figure presentation color"),quality:mt(t.quality,["auto","high"],"Figure presentation quality")}}function Tl(e,t){const n=We(e,t,["atom","image"]);return{atom:Lr(n.atom,`${t} atom`),image:ci(n.image,`${t} image`)}}function Rm(e){const t=We(e,"Figure camera",["position","target","up","fov","zoom","near","far"]),n=Mo(t.position,"Figure camera position"),r=Mo(t.target,"Figure camera target"),o=Mo(t.up,"Figure camera up"),s=Ct(t.fov,"Figure camera fov"),i=Gt(t.zoom,"Figure camera zoom"),a=Gt(t.near,"Figure camera near"),c=Gt(t.far,"Figure camera far");if(s<=0||s>=180)throw new Error("Figure camera fov must be between 0 and 180");if(c<=a)throw new Error("Figure camera far must be greater than near");if(Um(n,r)<=Number.EPSILON)throw new Error("Figure camera position must differ from target");if(o[0]**2+o[1]**2+o[2]**2<=Number.EPSILON)throw new Error("Figure camera up cannot be zero");const l=[r[0]-n[0],r[1]-n[1],r[2]-n[2]],u=[l[1]*o[2]-l[2]*o[1],l[2]*o[0]-l[0]*o[2],l[0]*o[1]-l[1]*o[0]];if(u[0]**2+u[1]**2+u[2]**2<=Number.EPSILON)throw new Error("Figure camera up cannot be parallel to its view");return{position:n,target:r,up:o,fov:s,zoom:i,near:a,far:c}}function Tm(e){const t=We(e,"Figure output",["format","width","height","dpi","background","projection","fit","padding","periodicContext"]),n=Za(t.width,"Figure output width"),r=Za(t.height,"Figure output height");if(!Number.isSafeInteger(n*r))throw new Error("Figure output dimensions are too large");const o=Ct(t.padding,"Figure output padding");if(o<0||o>.4)throw new Error("Figure output padding must be between 0 and 0.4");return{format:mt(t.format,["png","tiff"],"Figure output format"),width:n,height:r,dpi:Gt(t.dpi,"Figure output dpi"),background:Pm(t.background),projection:mt(t.projection,["orthographic","perspective"],"Figure output projection"),fit:jt(t.fit,"Figure output fit"),padding:o,periodicContext:jt(t.periodicContext,"Figure output periodicContext")}}function Pm(e){const t=Ho(e,"Figure output background");if(t.kind==="transparent")return Zn(t,"Figure output background",["kind"]),{kind:"transparent"};if(t.kind==="solid"){Zn(t,"Figure output background",["kind","color"]);const n=gn(t.color,"Figure output background color").toLowerCase();if(!/^#[0-9a-f]{6}$/.test(n))throw new Error("Figure output background color must use #RRGGBB");return{kind:"solid",color:n}}throw new Error("Figure output background kind must be transparent or solid")}function Om(e,t){const n=`Figure annotation ${t+1}`,r=Ho(e,n);if(r.kind==="atom-label"){Zn(r,n,["kind","atom","text","offset"]);const o={kind:"atom-label",atom:Tl(r.atom,`${n} atom`)};return r.text!==void 0&&(o.text=gn(r.text,`${n} text`)),r.offset!==void 0&&(o.offset=zm(r.offset,`${n} offset`)),o}if(r.kind==="legend")return Zn(r,n,["kind","content","position"]),{kind:"legend",content:mt(r.content,["elements","residues","forces","velocities"],`${n} content`),position:Ya(r.position,`${n} position`)};if(r.kind==="scale-bar")return Zn(r,n,["kind","length","unit","position"]),{kind:"scale-bar",length:Gt(r.length,`${n} length`),unit:mt(r.unit,["angstrom","nanometer"],`${n} unit`),position:Ya(r.position,`${n} position`)};throw new Error(`${n} kind is unsupported`)}function Ya(e,t){return mt(e,["top-left","top-right","bottom-left","bottom-right"],t)}function We(e,t,n){const r=Ho(e,t);return Zn(r,t,n),r}function Lm(e,t){return Ho(e,t)}function Ho(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw new Error(`${t} must be an object`);const n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw new Error(`${t} must be a plain object`);return e}function Zn(e,t,n){const r=Object.keys(e).filter(o=>!n.includes(o));if(r.length>0)throw new Error(`${t} contains unknown field: ${r[0]}`)}function Pi(e,t){if(!Array.isArray(e))throw new Error(`${t} must be an array`);return e}function mt(e,t,n){if(typeof e!="string"||!t.includes(e))throw new Error(`${n} is invalid`);return e}function jt(e,t){if(typeof e!="boolean")throw new Error(`${t} must be boolean`);return e}function gn(e,t){if(typeof e!="string"||!e.trim())throw new Error(`${t} must be a non-empty string`);return e}function Bm(e,t){return e==null?null:gn(e,t)}function Oi(e,t){return gn(e,t)}function Li(e,t){return gn(e,t)}function Qa(e,t){return e==null?null:Li(e,t)}function Ct(e,t){if(typeof e!="number"||!Number.isFinite(e))throw new Error(`${t} must be finite`);return Object.is(e,-0)?0:e}function Gt(e,t){const n=Ct(e,t);if(n<=0)throw new Error(`${t} must be positive`);return n}function Jn(e,t){const n=Ct(e,t);if(!Number.isSafeInteger(n))throw new Error(`${t} must be an integer`);return n}function Za(e,t){const n=Jn(e,t);if(n<=0)throw new Error(`${t} must be positive`);return n}function Lr(e,t){const n=Jn(e,t);if(n<0)throw new Error(`${t} cannot be negative`);return n}function So(e,t){return e==null?null:Jn(e,t)}function Dm(e,t){return e==null?null:Ct(e,t)}function zm(e,t){const n=Ko(e,2,t);return[Ct(n[0],`${t}[0]`),Ct(n[1],`${t}[1]`)]}function Mo(e,t){const n=Ko(e,3,t);return[Ct(n[0],`${t}[0]`),Ct(n[1],`${t}[1]`),Ct(n[2],`${t}[2]`)]}function ci(e,t){const n=Ko(e,3,t);return[Jn(n[0],`${t}[0]`),Jn(n[1],`${t}[1]`),Jn(n[2],`${t}[2]`)]}function Vm(e,t){const n=Ko(e,3,t);return[jt(n[0],`${t}[0]`),jt(n[1],`${t}[1]`),jt(n[2],`${t}[2]`)]}function Ko(e,t,n){if(!Array.isArray(e)||e.length!==t)throw new Error(`${n} must contain ${t} values`);return e}function Um(e,t){return(e[0]-t[0])**2+(e[1]-t[1])**2+(e[2]-t[2])**2}function qm(e,t){const n=t.step??1;if(n>0){const s=Ja(t.start,e,0),i=Ja(t.stop,e,e);return{start:s,step:n,count:s>=i?0:Math.floor((i-s-1)/n)+1}}const r=ec(t.start,e,e-1),o=ec(t.stop,e,-1);return{start:r,step:n,count:r<=o?0:Math.floor((r-o-1)/-n)+1}}function Ja(e,t,n){if(e===null)return n;const r=e<0?e+t:e;return Math.max(0,Math.min(t,r))}function ec(e,t,n){if(e===null)return n;const r=e<0?e+t:e;return Math.max(-1,Math.min(t-1,r))}class Gm{first=2166136261;second=2654435769;numberBuffer=new ArrayBuffer(8);numberView=new DataView(this.numberBuffer);encoder=new TextEncoder;bytes(t){for(const n of t)this.first=Math.imul(this.first^n,16777619),this.second=Math.imul(this.second^n,2246822507),this.second^=this.second>>>13}value(t){if(t==null){this.text("null");return}if(typeof t=="string"){this.text(`s${t.length}:`),this.text(t);return}if(typeof t=="number"){this.text("n"),this.numberView.setFloat64(0,t,!0),this.bytes(new Uint8Array(this.numberBuffer));return}if(typeof t=="boolean"){this.text(t?"true":"false");return}if(Array.isArray(t)){this.text("[");for(const n of t)this.value(n);this.text("]");return}if(typeof t=="object"){this.text("{");for(const n of Object.keys(t).sort())this.value(n),this.value(t[n]);this.text("}");return}this.text(typeof t)}digest(){return[this.first,this.second].map(t=>(t>>>0).toString(16).padStart(8,"0")).join("")}text(t){this.bytes(this.encoder.encode(t))}}function Hm(e){return e==="__proto__"||e==="prototype"||e==="constructor"}const Km="modulepreload",Wm=function(e){return"/"+e},tc={},Hn=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){let c=function(l){return Promise.all(l.map(u=>Promise.resolve(u).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),a=i?.nonce||i?.getAttribute("nonce");o=c(n.map(l=>{if(l=Wm(l),l in tc)return;tc[l]=!0;const u=l.endsWith(".css"),d=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${d}`))return;const m=document.createElement("link");if(m.rel=u?"stylesheet":Km,u||(m.as="script"),m.crossOrigin="",m.href=l,a&&m.setAttribute("nonce",a),document.head.appendChild(m),u)return new Promise((g,h)=>{m.addEventListener("load",g),m.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${l}`)))})}))}function s(i){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=i,window.dispatchEvent(a),!a.defaultPrevented)throw i}return o.then(i=>{for(const a of i||[])a.status==="rejected"&&s(a.reason);return t().catch(s)})},Xm=.08,Pl=12e6;function Ym(e,t){const n=uo(e.width,"width"),r=uo(e.height,"height"),o=uo(t.maxWidth,"maximum width"),s=uo(t.maxHeight,"maximum height"),i=t.maxPixels??Pl;if(!Number.isSafeInteger(i)||i<=0)throw new Error("PNG export pixel limit is invalid");if(n>o||r>s)throw new Error(`PNG export exceeds the WebGL limit of ${o} × ${s} px`);if(n*r>i)throw new Error(`PNG export exceeds the ${i.toLocaleString("en")} pixel safety limit`);const a=e.padding??Xm;if(!Number.isFinite(a)||a<0||a>.4)throw new Error("PNG export padding must be between 0 and 0.4");return{width:n,height:r,transparent:e.transparent??!1,fit:e.fit??!0,projection:e.projection??"orthographic",periodicContext:e.periodicContext??!0,padding:a}}function Qm(e){return!Number.isFinite(e)||e<=0?0:e<=5e6?3:e<=8e6?2:e<=12e6?1:0}function Zm(e){return!Number.isFinite(e)||e<=0?0:e<=8e6?.5:e<=12e6?.35:0}function Jm(e,t){if(e.length<4||e.length%4!==0)return!1;if(t){for(let c=3;c1)return!0;return!1}let n=255,r=255,o=255,s=0,i=0,a=0;for(let c=0;c2||i-r>2||a-o>2}function eh(e,t,n){const r=t*4;if(e.length!==r*n)throw new Error("PNG pixel buffer has an unexpected size");const o=new Uint8Array(r);for(let s=0;sBr)throw new Error("PNG DPI is outside the supported range");const c=new Uint8Array(13),l=new DataView(c.buffer);l.setUint32(0,n),l.setUint32(4,r),c[8]=8,c[9]=6;const u=new Uint8Array(9),d=new DataView(u.buffer);d.setUint32(0,a),d.setUint32(4,a),u[8]=1;const m=new TextEncoder().encode(`DPI\0${o.toString()}`),g=await ah(s);return hh([th,Kn("IHDR",c),Kn("sRGB",new Uint8Array([0])),Kn("pHYs",u),Kn("tEXt",m),Kn("IDAT",g),Kn("IEND",new Uint8Array)],"image/png")}function oh(e,t){const{width:n,height:r,dpi:o}=Ol(e,t),s=uh(e,n*4,r),[i,a]=fh(o),c=new TextEncoder().encode("PQViewer\0");if(i===0)throw new Error("TIFF DPI is outside the supported range");const l=17,u=8,d=2+l*12+4,m=u+d,g=m+8,h=g+8,y=h+8,A=bh(y+c.length),x=gh(A+Ts.length),j=x+s.length;if(j>Br)throw new Error("TIFF output exceeds the 4 GiB baseline limit");const k=new Uint8Array(j),M=new DataView(k.buffer);k[0]=73,k[1]=73,M.setUint16(2,42,!0),M.setUint32(4,u,!0),M.setUint16(u,l,!0),[[256,4,1,n],[257,4,1,r],[258,3,4,m],[259,3,1,32773],[262,3,1,2],[273,4,1,x],[274,3,1,1],[277,3,1,4],[278,4,1,r],[279,4,1,s.length],[282,5,1,g],[283,5,1,h],[284,3,1,1],[296,3,1,2],[305,2,c.length,y],[338,3,1,2],[34675,7,Ts.length,A]].forEach(([N,E,$,L],U)=>{const z=u+2+U*12;M.setUint16(z,N,!0),M.setUint16(z+2,E,!0),M.setUint32(z+4,$,!0),E===3&&$===1?(M.setUint16(z+8,L,!0),M.setUint16(z+10,0,!0)):M.setUint32(z+8,L,!0)}),M.setUint32(u+2+l*12,0,!0);for(let N=0;N<4;N+=1)M.setUint16(m+N*2,8,!0);return M.setUint32(g,i,!0),M.setUint32(g+4,a,!0),M.setUint32(h,i,!0),M.setUint32(h+4,a,!0),k.set(c,y),k.set(Ts,A),k.set(s,x),ph(k,"image/tiff")}function Ol(e,{width:t,height:n,dpi:r}){if(!Number.isSafeInteger(t)||t<=0)throw new Error("Figure width must be a positive integer");if(!Number.isSafeInteger(n)||n<=0)throw new Error("Figure height must be a positive integer");const o=t*n;if(!Number.isSafeInteger(o)||o>Math.floor(Number.MAX_SAFE_INTEGER/4))throw new Error("Figure dimensions are too large");if(e.length!==o*4)throw new Error(`Figure RGBA buffer must contain exactly ${o*4} bytes`);if(!Number.isFinite(r)||r<=0||r>1e6)throw new Error("Figure DPI must be between 0 and 1,000,000");return{width:t,height:n,dpi:r}}function Kn(e,t){if(!/^[A-Za-z]{4}$/.test(e))throw new Error("PNG chunk type is invalid");const n=new TextEncoder().encode(e),r=new Uint8Array(12+t.length),o=new DataView(r.buffer);return o.setUint32(0,t.length),r.set(n,4),r.set(t,8),o.setUint32(8+t.length,sh(r.subarray(4,8+t.length))),r}function sh(e){let t=4294967295;for(const n of e)t=nh[(t^n)&255]^t>>>8;return(t^4294967295)>>>0}function ih(){const e=new Uint32Array(256);for(let t=0;t>>1^(n&1?3988292384:0);e[t]=n>>>0}return e}async function ah(e){if(typeof CompressionStream<"u"){const t=new Blob([Bi(e)]).stream().pipeThrough(new CompressionStream("deflate"));return new Uint8Array(await new Response(t).arrayBuffer())}return ch(e)}function ch(e){const t=Math.max(1,Math.ceil(e.length/65535)),n=new Uint8Array(2+t*5+e.length+4);n[0]=120,n[1]=1;let r=0,o=2;for(let a=0;a>>8;const l=~c&65535;n[o+3]=l&255,n[o+4]=l>>>8,o+=5,n.set(e.subarray(r,r+c),o),r+=c,o+=c}const s=lh(e);return new DataView(n.buffer).setUint32(o,s),n}function lh(e){let t=1,n=0;for(const r of e)t=(t+r)%65521,n=(n+t)%65521;return(n<<16|t)>>>0}function uh(e,t,n){const r=t+Math.ceil(t/128)+1,o=new Uint8Array(r*n);let s=0;for(let i=0;i=3){r[i++]=257-a,r[i++]=e[s],s+=a;continue}const c=s;for(s+=a;s=3)break;s+=Math.min(a,128-(s-c))}const l=s-c;r[i++]=l-1,r.set(e.subarray(c,s),i),i+=l}return i}function fh(e){const t=e.toString().toLowerCase(),[n,r]=t.split("e"),o=r?Number(r):0,s=n.replace("-",""),i=s.indexOf("."),a=i<0?0:s.length-i-1,c=BigInt(s.replace(".","")),l=a-o;let u=l<0?c*10n**BigInt(-l):c,d=l>0?10n**BigInt(l):1n;const m=Ll(u,d);return u/=m,d/=m,u>BigInt(Br)||d>BigInt(Br)?mh(e):[Number(u),Number(d)]}function mh(e){const n=Math.round(e*1e6);if(n===0)throw new Error("TIFF DPI is outside the supported range");const r=Number(Ll(BigInt(n),BigInt(1e6))),o=n/r,s=1e6/r;if(o>Br)throw new Error("TIFF DPI is outside the supported range");return[o,s]}function Ll(e,t){let n=e<0n?-e:e,r=t<0n?-t:t;for(;r!==0n;){const o=n%r;n=r,r=o}return n||1n}function hh(e,t){return new Blob(e.map(Bi),{type:t})}function ph(e,t){return new Blob([Bi(e)],{type:t})}function Bi(e){return e.buffer instanceof ArrayBuffer&&e.byteOffset===0&&e.byteLength===e.buffer.byteLength?e.buffer:e.slice().buffer}function gh(e){return e+(e&1)}function bh(e){return e+(4-e%4)%4}function yh(e){const t=atob(e);return Uint8Array.from(t,n=>n.charCodeAt(0))}function xh(e,t,n){return e||t>n}function wh(e,t,n,r,o,s,i,a){const c=r/o,l=Bl(t.quaternion),u=Ah(e,t.quaternion);if(!u)throw new Error("The molecular scene has no visible geometry");if(s==="perspective"){const L=t.clone();return L.aspect=c,i&&Sh(L,e,u,l,a),L.updateProjectionMatrix(),L.updateMatrixWorld(!0),L}const d=new Oc(-1,1,1,-1,.01,1e4);d.quaternion.copy(t.quaternion);const m=Math.max(t.position.distanceTo(n),.01),g=2*m*Math.tan(Me.degToRad(t.getEffectiveFOV()*.5)),h=i?Dl(u,l):n.clone(),y=Math.max(u.maxRight-u.minRight,.01),A=Math.max(u.maxUp-u.minUp,.01),x=Math.max(.2,1-a*2),j=i?Math.max(A/x,y/(c*x),.1):Math.max(g,.1),k=j*c;d.left=-k*.5,d.right=k*.5,d.top=j*.5,d.bottom=-j*.5;const M=Math.max(u.maxBack-u.minBack,.01),C=Math.max(y,A,M,1),N=h.dot(l.back),E=i?M*.5+C*1.5:Math.max(m,u.maxBack-N+C);d.position.copy(h).addScaledVector(l.back,E);const $=d.position.dot(l.back);return d.near=Math.max($-u.maxBack-C*.25,.01),d.far=Math.max($-u.minBack+C*.25,d.near+10),d.updateProjectionMatrix(),d.updateMatrixWorld(!0),d}function Ah(e,t){e.updateMatrixWorld(!0);const n=Bl(t),r={minRight:1/0,maxRight:-1/0,minUp:1/0,maxUp:-1/0,minBack:1/0,maxBack:-1/0};let o=!1;return Di(e,s=>{const i=s.dot(n.right),a=s.dot(n.up),c=s.dot(n.back);r.minRight=Math.min(r.minRight,i),r.maxRight=Math.max(r.maxRight,i),r.minUp=Math.min(r.minUp,a),r.maxUp=Math.max(r.maxUp,a),r.minBack=Math.min(r.minBack,c),r.maxBack=Math.max(r.maxBack,c),o=!0}),o?r:null}function Sh(e,t,n,r,o){const s=Dl(n,r),i=(n.minRight+n.maxRight)*.5,a=(n.minUp+n.maxUp)*.5,c=(n.minBack+n.maxBack)*.5,l=Me.degToRad(e.getEffectiveFOV()*.5),u=Math.atan(Math.tan(l)*e.aspect),d=Math.max(.2,1-o*2);let m=.01;Di(t,y=>{const A=y.dot(r.back)-c;m=Math.max(m,A+Math.abs(y.dot(r.right)-i)/(Math.tan(u)*d),A+Math.abs(y.dot(r.up)-a)/(Math.tan(l)*d))});const g=Math.max(n.maxBack-n.minBack,.01),h=Math.max(n.maxRight-n.minRight,n.maxUp-n.minUp,g,1);e.position.copy(s).addScaledVector(r.back,m),e.quaternion.setFromRotationMatrix(new Hs().makeBasis(r.right,r.up,r.back));for(let y=0;y<4;y+=1){e.near=Math.max(m-g*.5-h*.25,.01),e.far=Math.max(m+g*.5+h,e.near+10),e.updateProjectionMatrix(),e.updateMatrixWorld(!0);const A=Mh(t,e);if(A<=d*1.001)break;m*=A/d*1.005,e.position.copy(s).addScaledVector(r.back,m)}e.near=Math.max(m-g*.5-h*.25,.01),e.far=Math.max(m+g*.5+h,e.near+10)}function Bl(e){return{right:new F(1,0,0).applyQuaternion(e).normalize(),up:new F(0,1,0).applyQuaternion(e).normalize(),back:new F(0,0,1).applyQuaternion(e).normalize()}}function Dl(e,t){return new F().addScaledVector(t.right,(e.minRight+e.maxRight)*.5).addScaledVector(t.up,(e.minUp+e.maxUp)*.5).addScaledVector(t.back,(e.minBack+e.maxBack)*.5)}function Di(e,t){e.updateMatrixWorld(!0);const n=new Hs,r=new Hs,o=new F,s=new F;e.traverse(i=>{if(i.visible===!1)return;const a=i.userData.publicationFitPositions;if(a){for(let u=0;u+2{r.copy(o).project(t),Number.isFinite(r.x)&&Number.isFinite(r.y)&&(n=Math.max(n,Math.abs(r.x),Math.abs(r.y)))}),n}const kh="Inter",vh=600;function jh(e){return`${vh} ${e}px "${kh}"`}async function Ch(e,t,n=typeof document>"u"?void 0:document.fonts){if(!n)throw new Error("Publication font is unavailable");const r=jh(e),o=t.trim()||"PQ";try{if((await n.load(r,o)).length===0||!n.check(r,o))throw new Error("font did not load")}catch{throw new Error("Publication font is unavailable")}return r}const ht=1e-10;function Nh(e,t,n=1,r=4/3){const o=t.filter(y=>Number.isInteger(y)&&y>=0&&y*3+2new F(Number(e[y*3]),Number(e[y*3+1]),Number(e[y*3+2]))).filter(y=>[y.x,y.y,y.z].every(Number.isFinite));if(o.length<3)return null;const s=o.reduce((y,A)=>y.add(A),new F).multiplyScalar(1/o.length),i=Ph(o,s);if(!i)return null;const a=Number.isFinite(r)&&r>0?r:4/3,{direction:c,right:l,up:u}=Fh(o,s,i,Math.max(a,1/a)),d=a>=1?l:u.clone().negate(),m=a>=1?u:l,g=Lh(o,d,m,c),h=Number.isFinite(n)?Math.max(.2,n):1;return{center:g,direction:c,up:m,points:o,radius:.9*h}}function Fh(e,t,n,r){const o=Th(e,128),s=[],i=Math.PI*(3-Math.sqrt(5));for(let l=0;l<96;l+=1){const u=(l+.5)/96,d=Math.sqrt(Math.max(0,1-u*u)),m=l*i;s.push(n.major.clone().multiplyScalar(d*Math.cos(m)).addScaledVector(n.middle,d*Math.sin(m)).addScaledVector(n.minor,u).normalize())}s.push(n.minor.clone(),n.minor.clone().addScaledVector(n.middle,.35).normalize(),n.minor.clone().addScaledVector(n.major,.35).normalize());let a=null,c=1/0;for(const l of s){const u=Ih(o,t,l),d=Eh(o,t,u,r);d{const $=E.clone().sub(t);return{x:$.dot(n.right),y:$.dot(n.up),depth:$.dot(n.direction)}}),s=o.map(({x:E})=>E),i=o.map(({y:E})=>E),a=Math.max(...s)-Math.min(...s),c=Math.max(...i)-Math.min(...i);if(aE+$.x*$.x+$.y*$.y,0),C=o.reduce((E,$)=>E+$.x*$.x+$.y*$.y+$.depth*$.depth,0),N=Math.sqrt(M/Math.max(C,ht));return u*2.8+x*2.5+j*.6+A*.25+k*.35+N*4}function $h(e){let t=0;for(let n=0;na.x-c.x||a.y-c.y),o=a=>{const c=[];for(const l of a){for(;c.length>=2&&jr(c[c.length-2],c[c.length-1],l)<=0;)c.pop();c.push(l)}return c},s=[...o(r).slice(0,-1),...o([...r].reverse()).slice(0,-1)];let i=0;for(let a=0;ae[Math.round(r/(t-1)*(e.length-1))])}function Ph(e,t){const n=[[0,0,0],[0,0,0],[0,0,0]],r=new F;for(const d of e)r.copy(d).sub(t),n[0][0]+=r.x*r.x,n[0][1]+=r.x*r.y,n[0][2]+=r.x*r.z,n[1][1]+=r.y*r.y,n[1][2]+=r.y*r.z,n[2][2]+=r.z*r.z;n[1][0]=n[0][1],n[2][0]=n[0][2],n[2][1]=n[1][2];const o=Oh(n).sort((d,m)=>m.value-d.value);if(o[0].valueht&&c.dot(a)<0&&a.negate();const l=new F().crossVectors(i,a).normalize();return Bh(e,l)<-ht&&(a.negate(),l.negate()),{major:i,middle:a,minor:l}}function Oh(e){const t=e.map(r=>[...r]),n=[[1,0,0],[0,1,0],[0,0,1]];for(let r=0;r<24;r+=1){let o=0,s=1;for(const[m,g]of[[0,2],[1,2]])Math.abs(t[m][g])>Math.abs(t[o][s])&&(o=m,s=g);if(Math.abs(t[o][s])({value:Math.max(0,t[r][r]),axis:new F(n[0][r],n[1][r],n[2][r])}))}function Lh(e,t,n,r){const o=[1/0,1/0,1/0],s=[-1/0,-1/0,-1/0];for(const i of e)[i.dot(t),i.dot(n),i.dot(r)].forEach((c,l)=>{o[l]=Math.min(o[l],c),s[l]=Math.max(s[l],c)});return t.clone().multiplyScalar((o[0]+s[0])*.5).addScaledVector(n,(o[1]+s[1])*.5).addScaledVector(r,(o[2]+s[2])*.5)}function Bh(e,t){let n=0;for(let r=1;r=-120&&e<=-30&&t>=-100&&t<=45?"helix":e>=-180&&e<=-60&&(t>=60&&t<=180||t>=-180&&t<=-130)?"sheet":"coil"}function Vl(e){const t=Array(e.length).fill("coil");for(let r=1;r=2&&t[0]==="coil"&&t[1]==="sheet"&&(t[0]="sheet");const n=t.length-1;return n>=1&&t[n]==="coil"&&t[n-1]==="sheet"&&(t[n]="sheet"),t}function To(e,t=1){const n=Number.isFinite(t)?Math.max(.2,t):1;return e==="helix"?{width:.38*n,depth:.085*n}:e==="sheet"?{width:.43*n,depth:.065*n}:{width:.13*n,depth:.13*n}}function zh(e,t){if(e.length<3)return null;const n=t.translations?.length?[...t.translations]:[new F],r=n.map((_,R)=>t.translationImages?.[R]??[0,0,0]),o=t.structures?.length===e.length?[...t.structures]:Vl(e),s=t.quality==="high",a=e.length*n.length>2e4?8:s?16:12,c=s?2e5:9e4,l=s?10:6,u=Me.clamp(Math.floor(c/Math.max(1,(e.length-1)*a*n.length)),1,l),d=Vh(e,o,t.scale,u),m=d.length,h=(m*a+2)*n.length,y=(m-1)*a*6,A=a*6,x=(y+A)*n.length,j=new Float32Array(h*3),k=new Float32Array(h*3);k.fill(1);const M=new Float32Array(h),C=new Float32Array(h*3),N=new Float32Array(h),E=new Float32Array(h),$=h>65535?new Uint32Array(x):new Uint16Array(x);let L=0,U=0;const z=new F;for(let _=0;_r.ca.clone());for(const[r,o]of Vi(t,"sheet")){const s=Math.max(0,r-1),i=Math.min(e.length-1,o+1);let a=e.slice(s,i+1).map(c=>c.ca.clone());for(let c=0;c<2;c+=1)a=a.map((l,u)=>u===0||u===a.length-1?l.clone():a[u-1].clone().addScaledVector(l,2).add(a[u+1]).multiplyScalar(.25));for(let c=r;c<=o;c+=1)n[c].copy(a[c-s])}return n}function qh(e,t,n,r){const o=e.clone().addScaledVector(t,-e.dot(t));o.lengthSq()<1e-8&&o.copy(n),o.normalize();const s=n.clone();return o.dot(s)<0&&s.negate(),o.lerp(s,r),o.addScaledVector(t,-o.dot(t)),o.lengthSq()<1e-8?zi(t):o.normalize()}function Gh(e){if(e.length===0)return[];const t=[e[0]];for(let n=1;n0&&t[t.length-1].dot(i)<0&&i.negate(),t.push(i)}return t.map((n,r)=>{const o=n.clone().multiplyScalar(2);return r>0&&o.add(t[r-1]),r=n))for(let s=r;s<=o;s+=1)e[s]="coil"}function Vi(e,t){const n=[];let r=-1;for(let o=0;o<=e.length;o+=1){if(o=0&&n.push([r,o-1]),r=-1}return n}function Kh(e,t,n,r){const o=To("sheet",r);for(const[s,i]of t){const a=i===n.length-1,c=n[Math.min(i+1,n.length-1)],l=Math.max(s,i-.85),u=a?i-.32:i-.24,d=a?i:Math.min(n.length-1,i+.8);if(ed)continue;const m=o.width*1.48;if(e<=u){const y=li((e-l)/Math.max(.01,u-l));return{structure:"sheet",width:Me.lerp(o.width,m,y),depth:o.depth,squareness:1}}const g=li((e-u)/Math.max(.01,d-u)),h=a?{width:.018*Math.max(.2,r),depth:.018*Math.max(.2,r)}:To(c,r);return{structure:"sheet",width:Me.lerp(m,h.width,g),depth:Me.lerp(o.depth,h.depth,g),squareness:Me.lerp(1,c==="sheet"?1:0,g)}}return null}function sc(e,t){const n=Me.lerp(1,.52,Me.clamp(t,0,1));return Math.sign(e)*Math.pow(Math.abs(e),n)}function li(e){const t=Me.clamp(e,0,1);return t*t*(3-2*t)}function ic(e,t,n,r,o,s,i,a,c){e.center.clone().add(t).toArray(r,c*3),o[c]=e.atomIndex,Ul(e.image,n,s,c),i[c]=e.progress,a[c]=zl[e.structure]}function Ul(e,t,n,r){const o=r*3;n[o]=e[0]+t[0],n[o+1]=e[1]+t[1],n[o+2]=e[2]+t[2]}const Po=2048,Wh=8192,Xh=16,ac=6e4,Yh=new je("#568da3"),Qh=new Set([1,2,6,7,8,9,10,17,18,35,36,53,54,85,86]),Zh=Object.freeze([]),ql=new WeakMap;function Ui(e,t={}){const n=Wo(e),r=t.centerAtoms!==void 0,o=qi(t),s=r?Yl(t.centerAtoms??[],n):null,i=s?new Set(s):null,a=new Map,c=g=>{if(i)return i.has(g);const h=e.atomicNumbers[g]??0;return Hl(h)&&(!o||o.has(h))},l=(g,h)=>{if(!c(g))return;let y=a.get(g);y||(y=new Set,a.set(g,y)),y.add(h)};for(const g of e.bonds){const h=g[0],y=g[1];rp(h,y,n)&&(l(h,y),l(y,h))}const u=s??[...a.keys()].sort((g,h)=>g-h),d=new Map;for(const[g,h]of a)d.set(g,Object.freeze([...h]));const m=Object.freeze({atomCount:n,candidates:Object.freeze(u),adjacency:d});return ql.set(m,{atomicNumbers:e.atomicNumbers,bonds:e.bonds,selectionKey:Wl(t,n)}),m}function Jh(e,t={},n){if(Wo(e)<2)return[];const o=Oo(t.maxCenters,1,Po,Po),s=[...Kl(e,t,n)];return pp(s,e.positions,o)}function Gl(e,t={},n){for(const r of Kl(e,t,n))if(r)return!0;return!1}function ep(e,t={},n){const r=Jh(e,t,n);if(r.length===0)return null;const o=hp(t.images),s=Oo(t.maxTriangles,1,ac,ac),i=[],a=[],c=[],l=[],u=[],d=[],m=[],g=[],h=[],y=new je;let A=0,x=0;e:for(const k of o){const M=Kt(k,e.basis);for(let C=0;Cs)break e;y.copy(Yh);const E=t.colorForCenter?.(N.centerAtom,e.atomicNumbers[N.centerAtom]??0);E!==void 0&&y.set(E);for(const $ of N.triangles){for(const L of $)N.vertices[L].clone().add(M).toArray(i,i.length),a.push(y.r,y.g,y.b),c.push(N.centerAtom),l.push(N.centerAtom),u.push(N.vertexAtoms[L]),d.push(N.coordinationNumber),m.push(k[0],k[1],k[2]),g.push(C);A+=1}for(const[$,L]of tp(N))N.vertices[$].clone().add(M).toArray(h,h.length),N.vertices[L].clone().add(M).toArray(h,h.length);x+=1}}if(A===0)return null;const j=new Yt;return j.setAttribute("position",new zt(i,3)),j.setAttribute("color",new zt(a,3)),j.setAttribute("atomIndex",new zt(c,1)),j.setAttribute("centerAtomIndex",new zt(l,1)),j.setAttribute("ligandAtomIndex",new zt(u,1)),j.setAttribute("coordinationNumber",new zt(d,1)),j.setAttribute("imageOffset",new zt(m,3)),j.setAttribute("polyhedronIndex",new zt(g,1)),j.computeVertexNormals(),j.computeBoundingBox(),j.computeBoundingSphere(),j.userData.polyhedronCount=x,j.userData.triangleCount=A,j.userData.edgePositions=new Float32Array(h),j}function tp(e){const t=new Map;for(const[r,o,s]of e.triangles){const i=new F().subVectors(e.vertices[o],e.vertices[r]).cross(new F().subVectors(e.vertices[s],e.vertices[r])).normalize();for(const[a,c]of[[r,o],[o,s],[s,r]]){const l=ar.length===1||r.some((o,s)=>r.slice(s+1).some(i=>Math.abs(o.dot(i))r)}function Hl(e){return Number.isInteger(e)&&e>0&&e<=118&&!Qh.has(e)}function*Kl(e,t,n){if(Wo(e)<2)return;const o=Oo(t.maxCoordination,3,Xh,12),s=Oo(t.maxCenters,1,Po,Po),i=np(e,t,n)??Ui(e,t),a=t.centerAtoms!==void 0,c=qi(t),l=Ql(i.candidates,Math.min(Wh,Math.max(s*4,s)));for(const u of l){const d=dp(e,u,i.adjacency.get(u)??Zh,o);if(d.length<3||c&&!c.has(e.atomicNumbers[u]??0)||!a&&!Hl(e.atomicNumbers[u]??0))continue;const m=op(e,u,d);m&&(yield m)}}function np(e,t,n){if(!n)return null;const r=Wo(e),o=ql.get(n);return n.atomCount!==r||o?.atomicNumbers!==e.atomicNumbers||o.bonds!==e.bonds||o.selectionKey!==Wl(t,r)?null:n}function Wo(e){return Math.min(e.atomicNumbers.length,Math.floor(e.positions.length/3))}function qi(e){return e.centerAtomicNumbers?new Set(e.centerAtomicNumbers.filter(Number.isInteger)):null}function Wl(e,t){const n=e.centerAtoms===void 0?"auto":`explicit:${Yl(e.centerAtoms,t).join(",")}`,r=qi(e),o=r?[...r].sort((s,i)=>s-i).join(","):"*";return`${n}|${o}`}function rp(e,t,n){return Number.isInteger(e)&&Number.isInteger(t)&&e>=0&&t>=0&&ec),s=n.map(({point:c})=>c.clone()),i=[...o];if(s.some(c=>!ui(c))||up(s))return null;const a=sp(s)??ip(s);return a?{centerAtom:t,coordinationNumber:n.length,ligandAtoms:o,vertices:s,vertexAtoms:i,triangles:a}:null}function sp(e){if(e.length<3)return null;const t=Gi(e);if(!Number.isFinite(t)||t<=1e-8)return null;const n=Math.max(1e-8,t*2e-6);let r=null;for(let i=0;in*n&&(r=l.normalize())}if(!r)return null;const o=-r.dot(e[0]);if(e.some(i=>Math.abs(r.dot(i)+o)>n))return null;const s=Xl(e,e.map((i,a)=>a),r,n);return s.length!==e.length?null:Array.from({length:s.length-2},(i,a)=>[s[0],s[a+1],s[a+2]])}function ip(e){if(e.length<4)return null;const t=Gi(e);if(!Number.isFinite(t)||t<=1e-8)return null;const n=Math.max(1e-8,t*1e-6),r=Math.max(1e-9,t*2e-6);if(!lp(e,t))return null;const o=[];for(let a=0;ar?m=!0:A<-r&&(g=!0)}if(m&&g)continue;m&&(u.negate(),d*=-1);let h=o.find(y=>y.normal.dot(u)>1-1e-5&&Math.abs(y.offset-d)<=r);h||(h={normal:u,offset:d,vertices:new Set},o.push(h)),e.forEach((y,A)=>{Math.abs(h.normal.dot(y)+h.offset)<=r&&h.vertices.add(A)})}const s=[];for(const a of o){const c=Xl(e,[...a.vertices],a.normal,n);if(!(c.length<3))for(let l=1;l{const d=e[u].clone().sub(o);return{index:u,x:d.dot(s),y:d.dot(i)}}).sort((u,d)=>u.x-d.x||u.y-d.y||u.index-d.index),c=[],l=[];for(const u of a){for(;c.length>=2&&cc(c.at(-2),c.at(-1),u)<=r*r;)c.pop();c.push(u)}for(let u=a.length-1;u>=0;u-=1){const d=a[u];for(;l.length>=2&&cc(l.at(-2),l.at(-1),d)<=r*r;)l.pop();l.push(d)}return[...c.slice(0,-1),...l.slice(0,-1)].map(u=>u.index)}function ap(e,t){const n=new Map,r=new Set;for(const o of t){r.add(o[0]),r.add(o[1]),r.add(o[2]);for(const[s,i]of[[o[0],o[1]],[o[1],o[2]],[o[2],o[0]]]){const a=so===2)}function cp(e,t){let n=0;const r=new F;for(const[o,s,i]of t)r.crossVectors(e[s],e[i]),n+=e[o].dot(r)/6;return Math.abs(n)}function lp(e,t){const n=Math.max(1e-10,t**3*1e-7);for(let r=0;rn)return!0}}return!1}function up(e){const t=Gi(e),n=Math.max(1e-16,t*t*1e-12);for(let r=0;ru.distance-d.distance||u.atom-d.atom);if(c.length===0)return[];const l=c[0].distance*1.32+1e-6;return c.filter(({distance:u})=>u<=l).slice(0,r)}function fp(e,t,n){const r=Xn(t,Xo(e.positions,n),e.basis,e.pbc);if(!e.basis||!e.pbc.some(Boolean))return[r];const o=[];for(const s of e.pbc[0]?[-1,0,1]:[0])for(const i of e.pbc[1]?[-1,0,1]:[0])for(const a of e.pbc[2]?[-1,0,1]:[0])o.push(r.clone().addScaledVector(e.basis.vectors[0],s).addScaledVector(e.basis.vectors[1],i).addScaledVector(e.basis.vectors[2],a));return o}function mp(e){return`${Math.round(e.x*1e5)}:${Math.round(e.y*1e5)}:${Math.round(e.z*1e5)}`}function Yl(e,t){return[...new Set(e.filter(n=>Number.isInteger(n)&&n>=0&&nn-r)}function hp(e){const t=e?.length?e:[[0,0,0]],n=[],r=new Set;for(const o of t){if(o.length!==3||!o.every(Number.isInteger))continue;const s=[o[0],o[1],o[2]],i=s.join(":");if(r.has(i)||(n.push(s),r.add(i)),n.length>=125)break}return n.length?n:[[0,0,0]]}function Ql(e,t){return e.length<=t?[...e]:Array.from({length:t},(n,r)=>e[Math.floor((r+.5)*e.length/t)])}function pp(e,t,n){if(e.length<=n)return[...e];if(n>128)return Ql(e,n);const r=e.map(({centerAtom:u})=>Xo(t,u)),o=new vt().setFromPoints(r).getCenter(new F);let s=0,i=1/0;r.forEach((u,d)=>{const m=u.distanceToSquared(o);mu.distanceToSquared(r[s]));for(;a.lengthd&&(u=m,d=l[m]);if(u<0)break;a.push(u),c.add(u),r.forEach((m,g)=>{l[g]=Math.min(l[g],m.distanceToSquared(r[u]))})}return a.map(u=>e[u])}function Xo(e,t){return new F().fromArray(e,t*3)}function ui(e){return Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.z)}function cc(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function Oo(e,t,n,r){return Number.isFinite(e)?Me.clamp(Math.floor(e),t,n):r}const gp=512,bp=16,yp=512,xp=32,er="Requires a supported center with 3+ bonded ligands",wp=Object.freeze({trails:Object.freeze([]),displacements:Object.freeze([])});function Ap(e,t,n){const r=e>gp?"points":"rings",o=r==="points"?Math.min(e,t):0;return{mode:r,pointCapacity:o,reusePointBuffer:r==="points"&&n!==null&&n>=o,clearRingMarkers:r==="points"}}function Sp(e){return e.pointerType==="touch"||e.pointerType==="pen"||e.shiftKey||e.metaKey||e.ctrlKey}const Lo={light:{background:"#F6F8F8",bond:"#375159",bondOpacity:.9,cell:"#2D7DA4",cellOpacity:.58,selection:"#3DACCB",selectionOpacity:.34,force:"#B8522D",velocity:"#6B62A8",displacement:"#087F8C",ribbon:"#3D879D",hemisphereSky:"#ffffff",hemisphereGround:"#c6d2d5",hemisphereIntensity:1.55,key:"#ffffff",keyIntensity:2.25,rim:"#8fcbd3",rimIntensity:.22,exposure:.95},dark:{background:"#1e2e33",bond:"#c0c9cb",bondOpacity:.9,cell:"#5db8d2",cellOpacity:.74,selection:"#72d4df",selectionOpacity:.42,force:"#f0a75a",velocity:"#9e98d7",displacement:"#72d4df",ribbon:"#6cb9ca",hemisphereSky:"#f5f6f2",hemisphereGround:"#17272c",hemisphereIntensity:1.42,key:"#eef2ef",keyIntensity:2,rim:"#62b7cd",rimIntensity:.34,exposure:.96}},di=new F(0,1,0);function Mp(e,t,n,r){const o=rl(e,t).size>0,s=ol(e).length>=3,i=Yc(t)&&Qc(t).some(Boolean),a=n?Wc(e,t,n,r):null;return Zl(e,o,s,!!(a&&ug(a)),i)}function Zl(e,t,n,r,o){let s="Backbone available";return n||(s=e.topology.residues?.length&&e.topology.atom_names?.length?"Three complete backbone residues required":"Backbone topology unavailable"),{water:t,ribbon:n,ribbonReason:s,polyhedra:r,polyhedraReason:r?"Coordination centers available":er,suggestedProfile:n?"protein":o&&!t?"crystal":"molecule"}}function kp(e,t,n,r){return{imageCount:t.images.length,forceCount:n.forceInstances.length,forceTotal:n.forceTotal,velocityCount:n.velocityInstances.length,velocityTotal:n.velocityTotal,capabilities:Zl(e,t.waterAtoms.size>0,t.backbone.length>=3,r,!!(t.basis&&t.pbc.some(Boolean)))}}function vp(e,t){const n=t.basis?Float64Array.from(t.basis.vectors.flatMap(r=>[r.x,r.y,r.z])):null;return{count:t.count,atomicNumbers:t.atomicNumbers,positions:t.positions,baseImages:t.baseImages,cell:n,bonds:t.bonds,waterAtoms:t.waterAtoms,instanceToAtom:t.instanceToAtom,instanceImages:t.instanceImages,atomResidueIndex:e.topology.atom_residue_index}}function jp(e,t){return e.imageCount===t.imageCount&&e.forceCount===t.forceCount&&e.forceTotal===t.forceTotal&&e.velocityCount===t.velocityCount&&e.velocityTotal===t.velocityTotal&&e.capabilities.water===t.capabilities.water&&e.capabilities.ribbon===t.capabilities.ribbon&&e.capabilities.ribbonReason===t.capabilities.ribbonReason&&e.capabilities.polyhedra===t.capabilities.polyhedra&&e.capabilities.polyhedraReason===t.capabilities.polyhedraReason&&e.capabilities.suggestedProfile===t.capabilities.suggestedProfile}const Cp=w.forwardRef(function({manifest:t,frame:n,preparedTopology:r,presentation:o,selectedAtoms:s,trajectoryOverlays:i=wp,resetSignal:a,forceScale:c,velocityScale:l,appearance:u,viewPreset:d="perspective",viewSignal:m=0,onSelect:g,onSelectMany:h,onSceneInfo:y,onSelectionContext:A,onSelectionPositions:x},j){const k=w.useRef(null),M=w.useRef(null),C=w.useRef(g),N=w.useRef(h),E=w.useRef(s),$=w.useRef(y),L=w.useRef(A),U=w.useRef(x),z=w.useRef(null),G=w.useRef(null),_=w.useRef(!1),[R,ee]=w.useState(null),[le,de]=w.useState(null),I=w.useRef(null),J=w.useRef(null);C.current=g,N.current=h,E.current=s,I.current=R,$.current=y,L.current=A,U.current=x,w.useImperativeHandle(j,()=>({exportPng:async v=>{if(_.current)throw new Error("A figure export is already in progress");const B=M.current;if(!B?.model)throw new Error("The molecular scene is not ready to export");const K=lc(B);_.current=!0;try{return await uc(B.renderer,K,{...v,format:"png"})}finally{_.current=!1}},exportFigure:async v=>{if(_.current)throw new Error("A figure export is already in progress");const B=M.current;if(!B?.model)throw new Error("The molecular scene is not ready to export");const K=lc(B);_.current=!0;try{return await uc(B.renderer,K,v)}finally{_.current=!1}},captureCamera:()=>{const v=M.current;if(!v)throw new Error("The molecular scene is not ready");return Mg(v.camera,v.controls.target)},restoreCamera:v=>{const B=M.current;if(!B)throw new Error("The molecular scene is not ready");kg(B,v)}}),[]),w.useEffect(()=>{const v=k.current;if(!v)return;const B=new od({canvas:v,antialias:!0,powerPreference:"high-performance"});let K=Math.min(window.devicePixelRatio,2);B.outputColorSpace=vo,B.toneMapping=sd;const X=new Lc,ne=Lo.light;X.background=new je(ne.background);const se=new id(34,1,.02,5e3);se.position.set(7,5,9);const P=new ad(se,v);P.enableDamping=!0,P.dampingFactor=.065,P.screenSpacePanning=!0,P.zoomToCursor=!0;const Y=new In;X.add(Y);const he=new Bc(ne.hemisphereSky,ne.hemisphereGround,ne.hemisphereIntensity);X.add(he);const q=new Nr(ne.key,ne.keyIntensity);q.position.set(7,10,8),X.add(q);const pe=new Nr(ne.rim,ne.rimIntensity);pe.position.set(-8,-2,-5),X.add(pe);const re=new In,xe=new cd(.94,1,64),be=new nr({color:ne.selection,transparent:!0,opacity:ne.selectionOpacity,side:jo,depthTest:!1});Y.add(re);const ge=new Vr({color:ne.selection,size:7,sizeAttenuation:!1,transparent:!0,opacity:.9,depthTest:!1}),H=new pn(new Yt,ge);H.renderOrder=10,H.frustumCulled=!1,H.visible=!1,Y.add(H);const oe=new nr({color:ne.selection,transparent:!0,opacity:.78,side:jo,depthTest:!1}),rt=new sr(xe,oe);rt.renderOrder=11,rt.visible=!1,Y.add(rt);const Ee=new In;Ee.renderOrder=7,Y.add(Ee);const ce={renderer:B,scene:X,hemisphere:he,key:q,rim:pe,camera:se,controls:P,root:Y,atomObject:null,bonds:null,cell:null,forces:null,velocities:null,ribbon:null,polyhedra:null,trajectoryOverlays:Ee,selection:re,selectionGeometry:xe,selectionMaterial:be,selectionPoints:H,selectionPointsMaterial:ge,keyboardFocus:rt,keyboardFocusMaterial:oe,pickables:[],instanceToAtom:new Uint32Array,instanceImages:new Int8Array,baseImages:new Int32Array,ribbonSelections:new Map,model:null,topologyManifest:null,preparedTopology:null,renderTopology:null,renderConfigKey:"",frameLayout:null,fittedKey:"",lastResetSignal:a,lastViewSignal:m,lastFittedAspect:1,fitContext:null,cameraMode:"fit"};M.current=ce;const Ye=()=>{ce.cameraMode="manual"};P.addEventListener("start",Ye);let _t=0,cr=0;const Qt=()=>{const V=Math.max(v.clientWidth,1),ke=Math.max(v.clientHeight,1),Ce=Math.min(window.devicePixelRatio,2);V===_t&&ke===cr&&!(Ce!==K)||(K=Ce,_t=V,cr=ke,B.setDrawingBufferSize(V,ke,K),se.aspect=V/ke,se.updateProjectionMatrix(),ce.cameraMode==="fit"&&ce.fitContext&&Math.abs(Math.log(se.aspect/ce.lastFittedAspect))>.06&&gc(ce,ce.fitContext))},ot=new ResizeObserver(Qt);ot.observe(v),window.addEventListener("resize",Qt),Qt();const Oe=new Sr;let gt=null,Qe=null,Le=!1;const Tn=new Sr,st=new ld;st.params.Points.threshold=.24;const ue=(V,ke=Sp(V))=>{const Ce=v.getBoundingClientRect();Tn.set((V.clientX-Ce.left)/Ce.width*2-1,-((V.clientY-Ce.top)/Ce.height)*2+1),st.setFromCamera(Tn,se);const W=st.intersectObjects(ce.pickables,!1)[0],Rt=W?pg(W,ce):null;document.activeElement===v&&(I.current=Rt,J.current=W&&W.object===ce.atomObject?W.instanceId??W.index??null:null,ee(Rt)),C.current(Rt,ke)},$e=()=>{Qe!==null&&v.hasPointerCapture(Qe)&&v.releasePointerCapture(Qe),Qe=null,P.enabled=!0,de(null)},bt=V=>{if(V.pointerType==="mouse"&&V.button===0&&V.shiftKey&&Qe===null){Qe=V.pointerId,Oe.set(V.clientX,V.clientY),P.enabled=!1,v.setPointerCapture(V.pointerId),V.preventDefault(),V.stopImmediatePropagation();return}if(gt!==null&&V.pointerId!==gt){Le=!0;return}V.isPrimary&&(gt=V.pointerId,Le=!1,Oe.set(V.clientX,V.clientY))},Fe=V=>{if(V.pointerId!==Qe)return;const ke=v.getBoundingClientRect(),Ce=Math.max(ke.left,Math.min(Oe.x,V.clientX)),W=Math.min(ke.right,Math.max(Oe.x,V.clientX)),Rt=Math.max(ke.top,Math.min(Oe.y,V.clientY)),Jt=Math.min(ke.bottom,Math.max(Oe.y,V.clientY));Oe.distanceTo(new Sr(V.clientX,V.clientY))>5&&de({left:Ce,top:Rt,width:Math.max(0,W-Ce),height:Math.max(0,Jt-Rt)}),V.preventDefault(),V.stopImmediatePropagation()},Zt=V=>{if(V.pointerId===Qe){Oe.distanceTo(new Sr(V.clientX,V.clientY))>5?N.current?.(xg(ce,v.getBoundingClientRect(),Oe,{x:V.clientX,y:V.clientY}),!0):ue(V,!0),$e(),V.preventDefault(),V.stopImmediatePropagation();return}if(V.pointerId!==gt)return;gt=null;const ke=Oe.distanceTo(new Sr(V.clientX,V.clientY))>5,Ce=Le;Le=!1,!(ke||Ce)&&ue(V)},Te=V=>{if(V.pointerId===Qe){$e();return}V.pointerId===gt&&(gt=null,Le=!1)},it=V=>{V.key!=="Escape"||Qe===null||($e(),V.preventDefault())},bn=(V,ke)=>{I.current=V,J.current=ke,ee(V)},Kr=()=>{if(I.current)return;const V=fo(ce.instanceToAtom,ce.instanceImages,E.current.at(-1)??null,null,0,ce.baseImages);bn(V?.selection??null,V?.instance??null)},Pn=()=>bn(null,null),yn=V=>{if(V.metaKey||V.ctrlKey||V.altKey)return;const ke=V.key==="ArrowDown"?1:V.key==="ArrowUp"?-1:0;if(ke!==0){V.preventDefault();const W=fo(ce.instanceToAtom,ce.instanceImages,I.current,J.current,ke,ce.baseImages);bn(W?.selection??null,W?.instance??null);return}if(V.key!=="Enter"||(V.preventDefault(),V.repeat))return;const Ce=fo(ce.instanceToAtom,ce.instanceImages,I.current??E.current.at(-1)??null,J.current,0,ce.baseImages);Ce&&(bn(Ce.selection,Ce.instance),C.current(Ce.selection,!0))};return v.addEventListener("pointerdown",bt,!0),v.addEventListener("pointermove",Fe,!0),v.addEventListener("pointerup",Zt,!0),v.addEventListener("pointercancel",Te,!0),v.addEventListener("focus",Kr),v.addEventListener("blur",Pn),v.addEventListener("keydown",yn),window.addEventListener("keydown",it),B.setAnimationLoop(()=>{P.update(),re.children.forEach(V=>{V.visible&&V.quaternion.copy(se.quaternion)}),rt.visible&&rt.quaternion.copy(se.quaternion),B.render(X,se)}),()=>{B.setAnimationLoop(null),ot.disconnect(),window.removeEventListener("resize",Qt),v.removeEventListener("pointerdown",bt,!0),v.removeEventListener("pointermove",Fe,!0),v.removeEventListener("pointerup",Zt,!0),v.removeEventListener("pointercancel",Te,!0),v.removeEventListener("focus",Kr),v.removeEventListener("blur",Pn),v.removeEventListener("keydown",yn),window.removeEventListener("keydown",it),P.removeEventListener("start",Ye),P.dispose(),Y.remove(re),Y.remove(H),Y.remove(rt),Y.remove(Ee),zo(Y),zo(Ee),re.clear(),xe.dispose(),be.dispose(),H.geometry.dispose(),ge.dispose(),oe.dispose(),B.dispose(),M.current=null,L.current?.(null),U.current?.(null)}},[]),w.useEffect(()=>{const v=M.current;if(!v)return;const B=Lo[u];Wp(v,B),v.model&&Xp(v,t,v.model,o,u,B)},[u]),w.useEffect(()=>{const v=M.current;if(!v||((v.topologyManifest!==t||v.preparedTopology!==r)&&(v.topologyManifest=t,v.preparedTopology=r),!v.preparedTopology))return;const B=Wc(t,n,o,v.preparedTopology);if(!B){z.current&&(z.current=null,$.current?.(null)),L.current?.(null);return}const K=v.preparedTopology;if(!K)return;const X=ae(n,["forces","force"]),ne=ae(n,["velocities","velocity","vel"]),se=sf(B,o,X,ne);let P=G.current;if(!P||P.sceneTopology!==K||P.water!==o.water||P.hydrogens!==o.hydrogens){const _t=Ki(B);P={sceneTopology:K,frame:null,water:o.water,hydrogens:o.hydrogens,bonds:_t.input.bonds,maxCenters:_t.maxCenters,available:!1,topology:Ui(_t.input,{maxCenters:_t.maxCenters})},G.current=P}const Y={input:au(B,P.bonds),maxCenters:P.maxCenters,topology:P.topology};o.mode!=="polyhedra"&&P.frame!==n&&(P.available=Gl(Y.input,{maxCenters:Y.maxCenters},Y.topology),P.frame=n);const he=af(se),q=cg(o);o.mode!=="ribbon"&&o.mode!=="polyhedra"&&v.preparedTopology?.count===B.count&&v.renderTopology===v.preparedTopology&&v.renderConfigKey===q&&v.frameLayout!==null&&cf(v.frameLayout,he)&&rg(v,B,o,X,ne,c,l,se)||(Qp(v),ng(v,B,t,o,u,X,ne,c,l,se,Y)),o.mode==="polyhedra"&&(P.available=v.polyhedra!==null,P.frame=n);const re=P.available,xe=kp(t,B,se,re);(z.current?.manifest!==t||!jp(z.current.info,xe))&&(z.current={manifest:t,info:xe},$.current?.(xe)),v.model=B;const be=v.atomObject?.userData.instanceToAtom,ge=v.atomObject?.userData.instanceImages,H=be instanceof Uint32Array&&ge instanceof Int8Array,oe=H?be:o.mode==="ribbon"?new Uint32Array:B.instanceToAtom,rt=H?ge:o.mode==="ribbon"?new Int8Array:B.instanceImages,Ee=o.mode==="ribbon"?mg(B,v.ribbonSelections,oe,rt):{instanceToAtom:oe,instanceImages:rt};v.instanceToAtom=Ee.instanceToAtom,v.instanceImages=Ee.instanceImages,v.baseImages=B.baseImages,v.renderTopology=v.preparedTopology,v.renderConfigKey=q,v.frameLayout=he,L.current?.(vp(t,B));const ce={model:B,presentation:o,preset:d};v.fitContext=ce;const Ye=vg(B,o);(v.fittedKey!==Ye||v.lastResetSignal!==a||v.lastViewSignal!==m)&&(gc(v,ce),v.fittedKey=Ye,v.lastResetSignal=a,v.lastViewSignal=m)},[c,l,n,t,r,o,a,d,m]),w.useEffect(()=>{const v=M.current;v&&Zp(v.trajectoryOverlays,v.model,i,u)},[u,n,o,i]),w.useEffect(()=>{const v=M.current,B=v?hg(v,s,!!U.current):null;let K=R,X=J.current;const ne=document.activeElement===k.current,se=v?Ps(v,K,X):null,P=se!==null;if(P&&(X=se),ne&&!P&&v){const Y=fo(v.instanceToAtom,v.instanceImages,s.at(-1)??null,null,0,v.baseImages);K=Y?.selection??null,X=Y?.instance??null,Y&&Ps(v,Y.selection,Y.instance)}else(!v||!P)&&K&&(K=null,X=null,v&&Ps(v,null,null));J.current=X,bg(K,R)||(I.current=K,ee(K)),U.current?.(B)},[n,R,o,s]);const Q=R?yg(t,R):"";return f.jsxs(f.Fragment,{children:[f.jsx("canvas",{ref:k,className:le?"molecule-canvas is-box-selecting":"molecule-canvas",role:"region","aria-label":"Molecular structure","aria-description":"Use Up and Down to browse visible atoms. Press Enter to toggle an atom selection. Shift-drag to select a box.","aria-keyshortcuts":"ArrowUp ArrowDown Enter",tabIndex:0}),le&&f.jsx("div",{className:"selection-marquee","data-testid":"selection-marquee",style:le,"aria-hidden":"true"}),f.jsx("span",{className:"sr-only","aria-live":"polite",children:Q?`${Q}. Press Enter to toggle selection.`:""})]})});function lc(e){const t=e.model,n=e.topologyManifest,r=e.fitContext?.presentation;if(!t||!n||!r)throw new Error("The molecular scene is not ready to export");if(r.mode==="polyhedra"&&!e.polyhedra)throw new Error(`Polyhedra unavailable · ${er}`);return{model:t,manifest:n,presentation:{...r,cellOrigin:[...r.cellOrigin],mirror:[...r.mirror],images:{min:[...r.images.min],max:[...r.images.max]}},forces:e.forces?.clone(!0)??null,velocities:e.velocities?.clone(!0)??null,camera:e.camera.clone(),target:e.controls.target.clone()}}async function uc(e,t,n){const r=e.getContext();if(r.isContextLost())throw new Error("Figure export is unavailable because the WebGL context was lost");const[{GTAOPass:o},{OutputPass:s},{SSAARenderPass:i},{LineMaterial:a},{LineSegments2:c},{LineSegmentsGeometry:l}]=await Promise.all([Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.G),__vite__mapDeps([0,1])),Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.O),__vite__mapDeps([0,1])),Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.S),__vite__mapDeps([0,1])),Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.L),__vite__mapDeps([0,1])),Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.b),__vite__mapDeps([0,1])),Hn(()=>import("./publication-Br5bSGOw.js").then(K=>K.a),__vite__mapDeps([0,1]))]),u=Np(n,Tp(e,r)),d=Op(t,u,{LineMaterial:a,LineSegments2:c,LineSegmentsGeometry:l}),m=wh(d.root,t.camera,t.target,u.width,u.height,u.projection,u.fit,u.padding);zp(d.scene,d.root,m);const g=e.capabilities.isWebGL2?e.extensions.has("EXT_color_buffer_float"):e.extensions.has("EXT_color_buffer_half_float"),h=g?ud:Ks,y=new ks(u.width,u.height,{depthBuffer:!0,format:Fr,stencilBuffer:!1,type:h});y.texture.name="Publication beauty";const A=new ks(u.width,u.height,{depthBuffer:!1,format:Fr,stencilBuffer:!1,type:Ks});A.texture.name="Publication sRGB";const x=u.width*u.height,j=g?Qm(x):0,k=g&&e.capabilities.isWebGL2&&d.hasAoGeometry?Zm(x):0,M=k>0?new ks(u.width,u.height,{depthBuffer:!1,format:Fr,stencilBuffer:!1,type:h}):null;M&&(M.texture.name="Publication ambient occlusion");const C=j>0?new i(d.scene,m,0,0):null;C&&(C.sampleLevel=j,C.unbiased=!0);const N=M?Vp(o,d.scene,d.root,m,u,k):null,E=new s;E.renderToScreen=!1,qp(E,u.background);const $=e.getRenderTarget(),L=e.getActiveCubeFace(),U=e.getActiveMipmapLevel(),z=e.getViewport(new Sa).clone(),G=e.getScissor(new Sa).clone(),_=e.getScissorTest(),R=e.getClearColor(new je).clone(),ee=e.getClearAlpha(),le=e.xr.enabled,de=e.toneMapping,I=e.toneMappingExposure,J=e.outputColorSpace,Q=e.autoClear,v=new Uint8Array(u.width*u.height*4);let B=null;try{Hp(r),e.initRenderTarget(y),e.initRenderTarget(A),M&&e.initRenderTarget(M),e.xr.enabled=!1,e.toneMapping=dd,e.toneMappingExposure=Ne.exposure,e.outputColorSpace=vo,e.autoClear=!0,e.setScissorTest(!1),C?C.render(e,y,y,0,!1):(e.setRenderTarget(y),e.setViewport(0,0,u.width,u.height),e.setClearColor(0,0),e.clear(!0,!0,!0),e.render(d.scene,m));let K=y;N&&M&&(N.render(e,M,y,0,!1),K=M),E.render(e,A,K,0,!1),e.readRenderTargetPixels(A,0,0,u.width,u.height,v);const X=r.getError();if(X!==r.NO_ERROR)throw new Error(Kp(X,r));if(!Jm(v,u.transparent))throw new Error("the rendered image was blank");eh(v,u.width,u.height),await Fp(v,d,m,t,u)}catch(K){B=K}finally{e.xr.enabled=le,e.toneMapping=de,e.toneMappingExposure=I,e.outputColorSpace=J,e.autoClear=Q,e.setClearColor(R,ee),e.setRenderTarget($,L,U),e.setViewport(z),e.setScissor(G),e.setScissorTest(_),C?.dispose(),N&&(N.gtaoMaterial.dispose(),N.blendMaterial.dispose(),N.dispose()),E.dispose(),y.dispose(),M?.dispose(),A.dispose(),Gp(d)}if(B){const K=B instanceof Error?B.message:"unknown rendering error";throw new Error(`Figure export failed: ${K}`)}return u.format==="tiff"?oh(v,u):rh(v,u)}function Np(e,t){const n=e.background??(e.transparent?{kind:"transparent"}:{kind:"solid",color:"#ffffff"});if(n.kind==="solid"&&!/^#[0-9a-f]{6}$/i.test(n.color))throw new Error("Figure background must use #RRGGBB");const r=e.dpi??300;if(!Number.isFinite(r)||r<=0||r>1e6)throw new Error("Figure DPI must be between 0 and 1,000,000");return{...Ym({...e,transparent:n.kind==="transparent"},t),format:e.format??"png",dpi:r,background:n,annotations:e.annotations??[]}}async function Fp(e,t,n,r,o){if(o.annotations.length===0)return;if(typeof document>"u")throw new Error("Figure annotations require a browser canvas");const s=document.createElement("canvas");s.width=o.width,s.height=o.height;const i=s.getContext("2d");if(!i)throw new Error("Figure annotations are unavailable");const a=i.createImageData(o.width,o.height);a.data.set(e),i.putImageData(a,0,0);const c=Math.max(12,Math.min(34,Math.round(Math.min(o.width,o.height)/70))),l=Math.max(16,Math.round(c*1.25));i.font=await Ch(c,Ip(r,o.annotations)),i.lineCap="round",i.lineJoin="round";for(const u of o.annotations){if(u.kind==="atom-label"){const d=Ep(r.model,u.atom,r.presentation.mode);if(!d)throw new Error("A figure atom label is outside the rendered scene");const m=fi(d,n,o.width,o.height);if(!m||m.depth<-1||m.depth>1)continue;const g=u.text??Jl(r.manifest,u.atom.atom),h=u.offset??[c*.72,-c*.72];Rp(i,g,m.x+h[0],m.y+h[1],c,"left");continue}if(u.kind==="legend"){$p(i,eu(r,u.content),u.position,o.width,o.height,c,l);continue}_p(i,t.root,n,u.length,u.unit,u.position,o.width,o.height,c,l)}e.set(i.getImageData(0,0,o.width,o.height).data)}function Ip(e,t){const n=["PQ","Å","nm"];for(const r of t){if(r.kind==="atom-label"){n.push(r.text??Jl(e.manifest,r.atom.atom));continue}if(r.kind==="legend"){n.push(...eu(e,r.content).map(({label:o})=>o));continue}n.push(nu(r.length))}return n.join(" ")}function Ep(e,t,n){if(n==="ribbon"){const o=uu(e).map(i=>du(e,i)),s=fu(o,e).get(Xt(t.atom,t.image));if(s)return s.position.clone()}const r=new F;for(let o=0;o({color:`#${pt(n,d,r.atomicNumbers[d],"residue","light").getHexString(vo)}`,label:c[u]?.name?.trim()||`Residue ${u+1}`}))}const i=new Map;for(const a of s){const c=r.atomicNumbers[a];i.has(c)||i.set(c,a)}return[...i.entries()].sort(([a],[c])=>a-c).slice(0,16).map(([a,c])=>({color:`#${pt(n,c,a,o.color,"light").getHexString(vo)}`,label:n.topology.symbols?.[c]??`Z ${a}`}))}function $p(e,t,n,r,o,s,i){if(t.length===0)return;const a=Math.max(6,Math.round(s*.45)),c=Math.max(8,Math.round(s*.72)),l=Math.round(s*1.4),u=Math.max(...t.map(({label:h})=>e.measureText(h).width))+c+a+s,d=t.length*l+s,{x:m,y:g}=tu(n,r,o,u,d,i);e.save(),e.fillStyle="rgba(255, 255, 255, 0.88)",e.fillRect(m,g,u,d),t.forEach((h,y)=>{const A=g+s*.5+l*(y+.5);e.fillStyle=h.color,e.beginPath(),e.arc(m+s,A,c*.5,0,Math.PI*2),e.fill(),e.fillStyle="#17302e",e.textAlign="left",e.textBaseline="middle",e.fillText(h.label,m+s+c*.5+a,A)}),e.restore()}function _p(e,t,n,r,o,s,i,a,c,l){if(!(n instanceof Oc))throw new Error("Scale bars require orthographic projection");const u=o==="nanometer"?r*10:r,d=new vt().setFromObject(t).getCenter(new F),m=new F(1,0,0).applyQuaternion(n.quaternion),g=fi(d,n,i,a),h=fi(d.clone().addScaledVector(m,u),n,i,a);if(!g||!h)throw new Error("Scale bar could not be projected");const y=Math.hypot(h.x-g.x,h.y-g.y);if(!Number.isFinite(y)||y<8||y>i*.7)throw new Error("Scale bar length does not fit the figure");const A=y+c*1.5,x=c*2.6,j=tu(s,i,a,A,x,l),k=j.y+c*.78,M=j.x+c*.75;e.save(),e.strokeStyle="#17302e",e.fillStyle="#17302e",e.lineWidth=Math.max(2,Math.round(c*.12)),e.beginPath(),e.moveTo(M,k),e.lineTo(M+y,k),e.moveTo(M,k-c*.22),e.lineTo(M,k+c*.22),e.moveTo(M+y,k-c*.22),e.lineTo(M+y,k+c*.22),e.stroke(),e.textAlign="center",e.textBaseline="top",e.fillText(`${nu(r)} ${o==="nanometer"?"nm":"Å"}`,M+y*.5,k+c*.45),e.restore()}function tu(e,t,n,r,o,s){return{x:e.endsWith("right")?t-s-r:s,y:e.startsWith("bottom")?n-s-o:s}}function Rp(e,t,n,r,o,s){e.save(),e.textAlign=s,e.textBaseline="middle",e.strokeStyle="rgba(255, 255, 255, 0.94)",e.lineWidth=Math.max(3,o*.28),e.strokeText(t,n,r),e.fillStyle="#17302e",e.fillText(t,n,r),e.restore()}function nu(e){return Number(e.toPrecision(5)).toString()}function Tp(e,t){const n=t.getParameter(t.MAX_VIEWPORT_DIMS),r=e.capabilities.maxTextureSize,o=Number(t.getParameter(t.MAX_RENDERBUFFER_SIZE));return{maxWidth:Math.max(1,Math.floor(Math.min(r,o,Number(n[0])))),maxHeight:Math.max(1,Math.floor(Math.min(r,o,Number(n[1])))),maxPixels:Pl}}const Ne={background:"#ffffff",bond:"#48575a",bondOpacity:1,cell:"#4f7882",cellOpacity:.46,selection:"#3DACCB",selectionOpacity:0,force:"#b34c2b",velocity:"#625c9f",displacement:"#087F8C",ribbon:"#347f96",hemisphereSky:"#ffffff",hemisphereGround:"#d8e0df",hemisphereIntensity:1.18,key:"#ffffff",keyIntensity:2.1,rim:"#c9e0e4",rimIntensity:.2,exposure:.98},Pp={...Ne,bond:"#9aa7a9"};function Op(e,t,n){const{model:r,manifest:o,presentation:s}=e,i=new Lc;i.background=null;const a=new In;i.add(a);const c={geometries:new Set,materials:new Set,textures:new Set},l=r.instanceToAtom.length<=12e3?{...s,quality:"high"}:s;if(s.mode==="ribbon"){const d=cu(r,o,l,"light",Ne);d&&(kn(d,c),Dt(d,c),a.add(d));const m=lu(r,o);if(m){const g={...l,mode:"ball-stick"},h=Bo(m,o,g,"light",!0,"ball-stick");h&&(kn(h,c),Dt(h,c),a.add(h));const y=Js(m,g,!1),A=Er(g,Ne,y.segments.length>No?"lines":"instances",y.segments,!0);A&&(kn(A,c),Dt(A,c),a.add(A))}}else{const d=s.mode==="polyhedra"?iu(r,o,l,"light",Ne,!0):null;if(s.mode==="polyhedra"&&!d)throw new Error(`Polyhedra unavailable · ${er}`);const m=Bo(r,o,l,"light",!0);m&&(kn(m,c),Dt(m,c),a.add(m));const g=Js(r,s,t.periodicContext),h=g.segments.filter(C=>!C.context),y=g.segments.filter(C=>C.context),A=m instanceof pn,x=s.mode==="lines"||A||g.segments.length>No?"lines":"instances",j=d?null:Er(l,Ne,x,h,!0);j&&(kn(j,c),Dt(j,c),a.add(j));const k=d?null:Er(l,Pp,x,y,!0);k&&(kn(k,c),Dt(k,c),a.add(k));const M=Lp(r,o,s,g.contextAtoms,xh(A,r.instanceToAtom.length+g.contextAtoms.length,ji));M&&(kn(M,c),Dt(M,c),a.add(M)),d&&(Dt(d,c),a.add(d))}if(s.cell){const d=Bp(r,t.width,t.height,n);d&&(Dt(d,c),a.add(d))}e.forces&&a.add(fc(e.forces,Ne.force,c)),e.velocities&&a.add(fc(e.velocities,Ne.velocity,c));let u=!1;return a.traverse(d=>{d instanceof sr&&d.userData.publicationExcludeFromAo!==!0&&(u=!0)}),{scene:i,root:a,resources:c,hasAoGeometry:u}}function Lp(e,t,n,r,o){if(r.length===0)return null;if(o){const d=new Float32Array(r.length*3),m=new Float32Array(r.length*3);r.forEach(({atomIndex:h,position:y},A)=>{y.toArray(d,A*3),pt(t,h,e.atomicNumbers[h],n.color,"light").toArray(m,A*3)});const g=new Yt;return g.setAttribute("position",new me(d,3)),g.setAttribute("color",new me(m,3)),g.computeBoundingSphere(),new pn(g,new Vr({vertexColors:!0,size:n.mode==="lines"?.14:.22,sizeAttenuation:!0}))}const i=e.instanceToAtom.length+r.length<=5e3?[40,28]:[24,16],a=new Dc(1,i[0],i[1]),c=new ir({roughness:.7,metalness:0}),l=new Ie(a,c,r.length),u=new Ur;return r.forEach(({atomIndex:d,position:m},g)=>{u.position.copy(m),u.scale.setScalar((e.radii[d]??.25)*.9),u.updateMatrix(),l.setMatrixAt(g,u.matrix),l.setColorAt(g,pt(t,d,e.atomicNumbers[d],n.color,"light"))}),l.instanceMatrix.needsUpdate=!0,l.instanceColor&&(l.instanceColor.needsUpdate=!0),l.computeBoundingSphere(),l}function Bp(e,t,n,{LineMaterial:r,LineSegments2:o,LineSegmentsGeometry:s}){if(!e.basis||e.images.length===0)return null;const i=[],a=new Set;for(const d of e.images){const m=[];Hi(m,e.basis,d,e.cellCenter);for(let g=0;g{const s=o;if(!s.material)return;const a=(Array.isArray(s.material)?s.material:[s.material]).map(c=>{const l=c.clone();return"color"in l&&l.color instanceof je&&l.color.set(t),n.materials.add(l),l});s.material=Array.isArray(s.material)?a:a[0]}),r}function kn(e,t){e.traverse(n=>{const r=n,o=Array.isArray(r.material)?r.material:r.material?[r.material]:[];for(const s of o){if(s.opacity=1,s.transparent=!1,s instanceof ir&&(s.roughness=.64,s.metalness=0),s instanceof Vr){const i=Dp();s.map=i,s.alphaTest=.04,s.transparent=!0,s.depthWrite=!0,s.size*=1.08,t.textures.add(i)}s.needsUpdate=!0}})}function Dp(){const t=new Uint8Array(9216);for(let r=0;r<48;r+=1)for(let o=0;o<48;o+=1){const s=(o+.5)/48*2-1,i=(r+.5)/48*2-1,a=Math.sqrt(s*s+i*i),c=Me.clamp((1-a)*12,0,1),l=(r*48+o)*4;t[l]=255,t[l+1]=255,t[l+2]=255,t[l+3]=Math.round(c*255)}const n=new zc(t,48,48,Fr);return n.needsUpdate=!0,n}function Dt(e,t){e.traverse(n=>{const r=n;r.geometry&&t.geometries.add(r.geometry),(Array.isArray(r.material)?r.material:r.material?[r.material]:[]).forEach(s=>t.materials.add(s))})}function zp(e,t,n){const r=new vt().setFromObject(t),o=r.getCenter(new F),s=Math.max(r.getSize(new F).length(),1),i=new F(1,0,0).applyQuaternion(n.quaternion),a=new F(0,1,0).applyQuaternion(n.quaternion),c=new F(0,0,1).applyQuaternion(n.quaternion);e.add(new Bc(Ne.hemisphereSky,Ne.hemisphereGround,Ne.hemisphereIntensity));const l=new Nr(Ne.key,Ne.keyIntensity);l.position.copy(o).addScaledVector(i,-s*.75).addScaledVector(a,s).addScaledVector(c,s*1.1),l.target.position.copy(o),e.add(l,l.target);const u=new Nr("#dce9eb",.48);u.position.copy(o).addScaledVector(i,s).addScaledVector(a,s*.2).addScaledVector(c,s*.45),u.target.position.copy(o),e.add(u,u.target);const d=new Nr(Ne.rim,Ne.rimIntensity);d.position.copy(o).addScaledVector(i,-s*.4).addScaledVector(a,-s*.3).addScaledVector(c,-s),d.target.position.copy(o),e.add(d,d.target)}function Vp(e,t,n,r,o,s){const i=Math.max(1,Math.round(o.width*s)),a=Math.max(1,Math.round(o.height*s)),c=new vt().setFromObject(n),l=Me.clamp(c.getSize(new F).length()*.018,.18,.55),u=new e(t,r,i,a);return u.pdNoiseTexture.dispose(),u.pdNoiseTexture=Up(),u.pdMaterial.uniforms.tNoise.value=u.pdNoiseTexture,u.renderToScreen=!1,u.blendIntensity=.38,u.setSceneClipBox(c),u.updateGtaoMaterial({radius:l,thickness:l*2.5,distanceExponent:1,distanceFallOff:1,scale:1,samples:16,screenSpaceRadius:!1}),u.updatePdMaterial({samples:8,rings:2,radius:4,radiusExponent:2}),u}function Up(e=64){const t=new Uint8Array(e*e*4);let n=1831565813;for(let o=0;o>>17,n^=n<<5,t[o]=n>>>24;const r=new zc(t,e,e,Fr,Ks);return r.wrapS=Ma,r.wrapT=Ma,r.needsUpdate=!0,r}function qp(e,t){const n="gl_FragColor = texture2D( tDiffuse, vUv );",r=e.material.fragmentShader,o=r.replace("uniform sampler2D tDiffuse;",`uniform sampler2D tDiffuse; +uniform float publicationTransparent; +uniform vec3 publicationBackground;`).replace(n,`${n} + float publicationCoverage = gl_FragColor.a; + gl_FragColor.rgb = publicationCoverage > 0.000001 + ? gl_FragColor.rgb / publicationCoverage + : vec3( 0.0 );`).replace("// color space",`if ( publicationTransparent < 0.5 ) { + gl_FragColor.rgb = gl_FragColor.rgb * publicationCoverage + publicationBackground * ( 1.0 - publicationCoverage ); + gl_FragColor.a = 1.0; + } + + // color space`);if(o===r||!o.includes("publicationCoverage"))throw new Error("Publication output shader is incompatible");e.material.fragmentShader=o,e.material.uniforms.publicationTransparent={value:t.kind==="transparent"?1:0},e.material.uniforms.publicationBackground={value:new je(t.kind==="solid"?t.color:"#000000")},e.material.needsUpdate=!0}function Gp(e){e.root.traverse(t=>{t instanceof Ie&&t.dispose()}),e.resources.geometries.forEach(t=>t.dispose()),e.resources.materials.forEach(t=>t.dispose()),e.resources.textures.forEach(t=>t.dispose())}function Hp(e){for(let t=0;t<16&&e.getError()!==e.NO_ERROR;t+=1);}function Kp(e,t){return e===t.OUT_OF_MEMORY?"the GPU could not allocate the requested image":e===t.INVALID_VALUE?"the requested image dimensions are unsupported":e===t.INVALID_FRAMEBUFFER_OPERATION?"the export framebuffer is incomplete":`WebGL error 0x${e.toString(16)}`}function Wp(e,t){e.scene.background instanceof je&&e.scene.background.set(t.background),e.renderer.toneMappingExposure=t.exposure,e.hemisphere.color.set(t.hemisphereSky),e.hemisphere.groundColor.set(t.hemisphereGround),e.hemisphere.intensity=t.hemisphereIntensity,e.key.color.set(t.key),e.key.intensity=t.keyIntensity,e.rim.color.set(t.rim),e.rim.intensity=t.rimIntensity,e.selectionMaterial.color.set(t.selection),e.selectionMaterial.opacity=t.selectionOpacity,e.selectionPointsMaterial.color.set(t.selection),e.keyboardFocusMaterial.color.set(t.selection)}function Xp(e,t,n,r,o,s){const i=e.atomObject?.userData.instanceToAtom instanceof Uint32Array?e.atomObject.userData.instanceToAtom:n.instanceToAtom;if(e.atomObject instanceof pn){const a=e.atomObject.geometry.getAttribute("color");for(let c=0;cCr(a,s.force)),e.velocities?.children.forEach(a=>Cr(a,s.velocity)),e.ribbon&&ru(e.ribbon.geometry,t,n,r,o,s),e.polyhedra&&Yp(e.polyhedra,t,n,r,o,s)}function ru(e,t,n,r,o,s){const i=e.getAttribute("color"),a=e.getAttribute("atomIndex"),c=e.getAttribute("secondaryStructure");if(!(i instanceof me)||!(a instanceof me))return;const l=new je(s.ribbon),u=o==="light"?[new je("#3f817e"),new je("#c94f5b"),new je("#d99a2b")]:[new je("#6bb7b2"),new je("#ed7d86"),new je("#f1c15a")];for(let d=0;d{if(i.userData.polyhedronEdges===!0){Cr(i,s.bond);return}if(!(i instanceof sr))return;const a=i.geometry.getAttribute("color"),c=i.geometry.getAttribute("centerAtomIndex");if(!(!(a instanceof me)||!(c instanceof me))){for(let l=0;l{const o=r.material;(Array.isArray(o)?o:o?[o]:[]).forEach(i=>{"color"in i&&i.color instanceof je&&i.color.set(t),n!==void 0&&"opacity"in i&&(i.opacity=n)})})}function Qp(e){for(const t of[e.atomObject,e.bonds,e.cell,e.forces,e.velocities,e.ribbon,e.polyhedra])t&&(e.root.remove(t),zo(t));e.atomObject=null,e.bonds=null,e.cell=null,e.forces=null,e.velocities=null,e.ribbon=null,e.polyhedra=null,e.ribbonSelections.clear(),e.pickables=[]}function Zp(e,t,n,r){for(;e.children.length>0;){const l=e.children[e.children.length-1];e.remove(l),zo(l)}if(!t)return;const o=Lo[r],s=new je(o.background),i=new je(o.selection);for(const l of n.trails.slice(0,bp)){const u=Jp(t,l);if(u.length===0)continue;const d=u.length/6,m=new Float32Array(d*6);for(let y=0;yeg(t,l)).filter(l=>l!==null),c=tg(a,o.displacement);c&&(c.name="reference-displacements",e.add(c))}function Jp(e,t){if(!Number.isSafeInteger(t.atom)||t.atom<0||t.atom>=e.count||t.image.length!==3||!t.image.every(Number.isInteger)||t.points.length<6||t.points.length%3!==0)return new Float32Array;const n=Math.min(yp,Math.floor(t.points.length/3)),r=Math.floor(t.points.length/3)-n,o=t.points.length-3,s=new F().fromArray(t.points,o);if(![s.x,s.y,s.z].every(Number.isFinite))return new Float32Array;const i=ou(e,t.atom,t.image);if(!i)return new Float32Array;const a=[],c=new F;for(let u=r;u=e.count||n.length!==3||!n.every(Number.isInteger))return null;const r=new F().fromArray(e.positions,t*3);if(!e.basis)return r;const o=t*3,s=[n[0]-(e.baseImages[o]??0),n[1]-(e.baseImages[o+1]??0),n[2]-(e.baseImages[o+2]??0)];return r.add(Kt(s,e.basis))}function eg(e,t){const n=ou(e,t.atom,t.image);if(!n||![...t.from,...t.to].every(Number.isFinite))return null;const r=new F(t.to[0]-t.from[0],t.to[1]-t.from[1],t.to[2]-t.from[2]).applyMatrix3(e.displayTransform),o=r.length();if(!Number.isFinite(o)||o<=1e-10)return null;const s=r.clone().multiplyScalar(1/o),i=Math.min(.22,Math.max(.07,o*.18),o*.45);return{tail:n.clone().sub(r),tip:n,direction:s,head:i}}function tg(e,t){if(e.length===0)return null;const n=new In,r=new Ie(new Si(.014,.014,1,8,1,!1),new nr({color:t,transparent:!0,opacity:.82,depthWrite:!1}),e.length);r.instanceMatrix.setUsage(Nt),n.add(r);const o=new Ie(new Vc(1,1,9),new nr({color:t,transparent:!0,opacity:.88,depthWrite:!1}),e.length);return o.instanceMatrix.setUsage(Nt),n.add(o),Do(n,[...e]),n}function ng(e,t,n,r,o,s,i,a,c,l,u){const d=Lo[o];if(r.mode==="ribbon"){if(e.ribbon=cu(t,n,r,o,d),e.ribbon){e.root.add(e.ribbon),e.pickables.push(e.ribbon);const g=e.ribbon.userData.ribbonSelections;g instanceof Map&&(e.ribbonSelections=g)}const m=lu(t,n);if(m){const g={...r,mode:"ball-stick"};e.atomObject=Bo(m,n,g,o,!1,"ball-stick"),e.atomObject&&(e.atomObject.userData.instanceToAtom=m.instanceToAtom,e.atomObject.userData.instanceImages=m.instanceImages,e.root.add(e.atomObject),e.pickables.push(e.atomObject));const h=Js(m,g,!1);e.bonds=Er(g,d,h.segments.length>No?"lines":"instances",h.segments),e.bonds&&e.root.add(e.bonds)}}else{e.polyhedra=r.mode==="polyhedra"?iu(t,n,r,o,d,!1,u):null;const m=r.mode==="polyhedra"&&!e.polyhedra;e.atomObject=m?null:Bo(t,n,r,o,!1),e.atomObject&&(e.root.add(e.atomObject),e.pickables.push(e.atomObject)),e.bonds=e.polyhedra||m?null:Er(r,d,l.bondKind,l.bondSegments),e.bonds&&e.root.add(e.bonds),e.polyhedra&&e.root.add(e.polyhedra)}e.cell=r.cell?lg(t,d):null,e.cell&&e.root.add(e.cell),e.forces=r.forces?hc(t,s,a,d.force,l.forceInstances):null,e.forces&&e.root.add(e.forces),e.velocities=r.velocities?hc(t,i,c,d.velocity,l.velocityInstances):null,e.velocities&&e.root.add(e.velocities)}function rg(e,t,n,r,o,s,i,a){if(!og(e,a))return!1;const c=a.forceInstances.length>0?mi(t,r,s,a.forceInstances):[],l=a.velocityInstances.length>0?mi(t,o,i,a.velocityInstances):[];return c.length!==a.forceInstances.length||l.length!==a.velocityInstances.length?!1:(e.atomObject&&sg(e.atomObject,t),e.bonds&&ig(e.bonds,a.bondSegments),e.cell&&ag(e.cell,t),e.forces&&Do(e.forces,c),e.velocities&&Do(e.velocities,l),n.mode!=="ribbon"&&n.mode!=="polyhedra")}function og(e,t){if(e.ribbon||e.polyhedra)return!1;if(t.atomKind==="none"){if(e.atomObject)return!1}else if(t.atomKind==="points"){if(!(e.atomObject instanceof pn)||e.atomObject.geometry.getAttribute("position").count!==t.atomCount)return!1}else if(!(e.atomObject instanceof Ie)||e.atomObject.instanceMatrix.count!==t.atomCount)return!1;if(t.bondKind==="none"){if(e.bonds)return!1}else if(t.bondKind==="lines"){if(!(e.bonds instanceof ar)||e.bonds.geometry.getAttribute("position").count!==t.bondSegments.length*2)return!1}else if(!(e.bonds instanceof Ie)||e.bonds.instanceMatrix.count!==t.bondSegments.length)return!1;if(t.cellLineCount===0){if(e.cell)return!1}else if(!e.cell||e.cell.geometry.getAttribute("position").count!==t.cellLineCount*2)return!1;return mc(e.forces,t.forceInstances.length)&&mc(e.velocities,t.velocityInstances.length)}function mc(e,t){if(t===0)return e===null;const[n,r]=e?.children??[];return n instanceof Ie&&r instanceof Ie&&n.instanceMatrix.count===t&&r.instanceMatrix.count===t}function sg(e,t){const n=new F;if(e instanceof pn){const o=e.geometry.getAttribute("position");for(let s=0;s{n.setXYZ(s*2,r.x,r.y,r.z),n.setXYZ(s*2+1,o.x,o.y,o.z)}),n.needsUpdate=!0,e.geometry.computeBoundingSphere();return}e instanceof Ie&&su(e,t)}function ag(e,t){if(!t.basis)return;const n=[];t.images.forEach(o=>Hi(n,t.basis,o,t.cellCenter));const r=e.geometry.getAttribute("position");r.array.set(n),r.needsUpdate=!0,e.geometry.computeBoundingSphere()}function cg(e){return JSON.stringify([e.mode,e.water,e.hydrogens,e.images.min,e.images.max,e.cell,e.forces,e.velocities,e.atomScale,e.bondScale,e.color,e.quality])}function Bo(e,t,n,r,o=!1,s){const i=e.instanceToAtom.length;if(i===0)return null;if(Jc(n,i)){const g=new Float32Array(i*3),h=new Float32Array(i*3),y=new F;for(let x=0;x{m.toArray(u,h*6),g.toArray(u,h*6+3)});const d=new Yt;return d.setAttribute("position",new me(u,3).setUsage(Nt)),new ar(d,new Uo({color:t.bond,transparent:!0,opacity:t.bondOpacity}))}const s=(e.mode==="licorice"?.14:e.mode==="polyhedra"?.025:.045)*Math.max(.1,e.bondScale),i=o&&r.length<=12e3?16:el(e,r.length)?12:8,a=new Si(s,s,1,i,1,!1),c=new ir({color:t.bond,roughness:.56,metalness:.01,transparent:!0,opacity:t.bondOpacity}),l=new Ie(a,c,r.length);return l.instanceMatrix.setUsage(Nt),su(l,r),l}function su(e,t){const n=new Ur,r=new F;t.forEach(({from:o,to:s},i)=>{r.subVectors(s,o);const a=r.length();n.position.copy(o).add(s).multiplyScalar(.5),n.quaternion.setFromUnitVectors(di,r.normalize()),n.scale.set(1,a,1),n.updateMatrix(),e.setMatrixAt(i,n.matrix)}),e.instanceMatrix.needsUpdate=!0,e.computeBoundingSphere()}function lg(e,t){if(!e.basis||e.images.length===0)return null;const n=[];e.images.forEach(o=>Hi(n,e.basis,o,e.cellCenter));const r=new Yt;return r.setAttribute("position",new zt(n,3).setUsage(Nt)),new ar(r,new Uo({color:t.cell,transparent:!0,opacity:t.cellOpacity}))}function Hi(e,t,n,r){const o=sl(t,n,r),s=(a,c,l)=>a*4+c*2+l,i=[];for(let a=0;a<=1;a+=1){for(let c=0;c<=1;c+=1)i.push([s(a,c,0),s(a,c,1)]);for(let c=0;c<=1;c+=1)i.push([s(a,0,c),s(a,1,c)])}for(let a=0;a<=1;a+=1)for(let c=0;c<=1;c+=1)i.push([s(0,a,c),s(1,a,c)]);i.forEach(([a,c])=>e.push(...o[a].toArray(),...o[c].toArray()))}function hc(e,t,n,r,o){const s=mi(e,t,n,o);if(s.length===0)return null;const i=new In,a=new Ie(new Si(.018,.018,1,8,1,!1),new nr({color:r}),s.length);a.instanceMatrix.setUsage(Nt),i.add(a);const c=new Ie(new Vc(1,1,9),new nr({color:r}),s.length);return c.instanceMatrix.setUsage(Nt),i.add(c),Do(i,s),i}function mi(e,t,n,r){if(!t||t.length{const d=e.instanceToAtom[u];return Math.hypot(t[d*3],t[d*3+1],t[d*3+2])}).filter(u=>Number.isFinite(u)&&u>1e-12).sort((u,d)=>u-d);if(o.length===0)return[];const i=1.45/o[Math.floor((o.length-1)*.9)]*n,a=[],c=new F,l=new F;for(const u of r){const d=e.instanceToAtom[u],m=d*3;c.set(t[m],t[m+1],t[m+2]);const g=c.length();xf(c.normalize(),e),Et(l,e,u);const h=g*i,y=Math.min(Math.min(.24,Math.max(.075,h*.24)),h*.5),A=(e.radii[d]??.3)*1.03;a.push({tail:l.clone().addScaledVector(c,A),tip:l.clone().addScaledVector(c,A+h),direction:c.clone(),head:y})}return a}function Do(e,t){const[n,r]=e.children;if(!(n instanceof Ie)||!(r instanceof Ie))return;const o=new Ur,s=new F;t.forEach((i,a)=>{s.copy(i.tip).addScaledVector(i.direction,-i.head*.48),o.position.copy(i.tail).add(s).multiplyScalar(.5),o.quaternion.setFromUnitVectors(di,i.direction),o.scale.set(1,i.tail.distanceTo(s),1),o.updateMatrix(),n.setMatrixAt(a,o.matrix)}),n.instanceMatrix.needsUpdate=!0,t.forEach((i,a)=>{o.position.copy(i.tip).addScaledVector(i.direction,-i.head*.5),o.quaternion.setFromUnitVectors(di,i.direction),o.scale.set(i.head*.34,i.head,i.head*.34),o.updateMatrix(),r.setMatrixAt(a,o.matrix)}),r.instanceMatrix.needsUpdate=!0,n.computeBoundingSphere(),n.boundingBox=null,r.computeBoundingSphere(),r.boundingBox=null}function iu(e,t,n,r,o,s=!1,i){const a=i??Ki(e),c=ep(a.input,{images:e.images,maxCenters:a.maxCenters,colorForCenter:(g,h)=>pt(t,g,h,n.color,r)},a.topology);if(!c)return null;const l=new In,u=new sr(c,new ir({vertexColors:!0,transparent:!0,opacity:s?.34:r==="light"?.28:.34,depthWrite:!0,roughness:.58,metalness:0,flatShading:!0,side:jo,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}));u.userData.publicationExcludeFromAo=!0,u.renderOrder=1,l.add(u);const d=new Yt;d.setAttribute("position",new me(c.userData.edgePositions instanceof Float32Array?c.userData.edgePositions:new Float32Array,3)),d.computeBoundingSphere();const m=new ar(d,new Uo({color:o.bond,transparent:!0,opacity:s?.42:.36,depthWrite:!1}));return m.userData.polyhedronEdges=!0,m.userData.publicationExcludeFromAo=!0,m.renderOrder=2,l.add(m),l}function ug(e){const t=Ki(e),n=Ui(t.input,{maxCenters:t.maxCenters});return Gl(t.input,{maxCenters:t.maxCenters},n)}function Ki(e){const t=e.visibleAtoms.length===e.count?e.bonds:dg(e);return{input:au(e,t),maxCenters:e.visibleAtoms.length>24?8:64}}function au(e,t){return{positions:e.positions,atomicNumbers:e.atomicNumbers,bonds:t,basis:e.basis,pbc:e.pbc}}function dg(e){const t=new Set(e.visibleAtoms);return e.bonds.filter(([n,r])=>t.has(n)&&t.has(r))}function cu(e,t,n,r,o){if(e.backbone.length<3)return null;const s=new Map((t.topology.residues??[]).filter(d=>d.secondary_structure).map(d=>[d.index,d.secondary_structure])),i=e.images.map(d=>Kt(d,e.basis)),a=uu(e).map(d=>du(e,d)),c=[];for(const d of a){const m=Vl(d);for(let h=0;h1&&c.forEach(d=>d.dispose()),ru(l,t,e,n,r,o);const u=new sr(l,new ir({vertexColors:!0,roughness:.5,metalness:0,dithering:!0,side:jo}));return u.userData.ribbonSelections=fu(a,e),u}function lu(e,t){const n=new Map((t.topology.residues??[]).map(c=>[c.index,c])),r=t.topology.atom_residue_index??[],o=e.visibleAtoms.filter(c=>n.get(r[c]??-1)?.category!=="amino-acid");if(o.length===0)return null;const s=new Set(o),i=[],a=[];for(let c=0;cs.has(c)&&s.has(l)),visibleAtoms:o,instanceToAtom:Uint32Array.from(i),instanceImages:Int8Array.from(a),radii:e.atomicNumbers.map(c=>Ci(c,"ball-stick",.82)),backbone:[]}}function uu(e){const t=[];for(const n of e.backbone){const r=n.runIndex??0;for(;t.length<=r;)t.push([]);t[r].push(n)}return t.filter(n=>n.length>=3)}function du(e,t){const n=[];for(const r of t){const o=new F().fromArray(e.positions,r.ca*3),s=n.length===0?o:Xn(n[n.length-1].ca,o,e.basis,e.pbc),i=fg(o,s,e.basis,e.pbc),a=r.ca*3,c=[(e.baseImages[a]??0)+i[0],(e.baseImages[a+1]??0)+i[1],(e.baseImages[a+2]??0)+i[2]],l=Xn(s,new F().fromArray(e.positions,r.n*3),e.basis,e.pbc),u=Xn(s,new F().fromArray(e.positions,r.c*3),e.basis,e.pbc),d=Xn(u,new F().fromArray(e.positions,r.o*3),e.basis,e.pbc);n.push({atomIndex:r.ca,residueIndex:r.residueIndex,image:c,n:l,ca:s,c:u,o:d})}return n}function fg(e,t,n,r){if(!n)return[0,0,0];const o=t.clone().sub(e);return[r[0]?Math.round(o.dot(n.reciprocal[0])):0,r[1]?Math.round(o.dot(n.reciprocal[1])):0,r[2]?Math.round(o.dot(n.reciprocal[2])):0]}function fu(e,t){const n=new Map;for(const r of e)for(const o of r)for(const s of t.images){const i=[(o.image?.[0]??0)+s[0],(o.image?.[1]??0)+s[1],(o.image?.[2]??0)+s[2]],a={atom:o.atomIndex,image:i};n.set(Xt(a.atom,a.image),{selection:a,position:o.ca.clone().add(Kt(s,t.basis))})}return n}function mg(e,t,n,r){const o=[],s=[],i=new Set,a=c=>{const l=Xt(c.atom,c.image);if(i.has(l))return;const u=c.atom*3,d=c.image.map((m,g)=>m-(e.baseImages[u+g]??0));d.some(m=>m<-127||m>127)||(i.add(l),o.push(c.atom),s.push(d[0],d[1],d[2]))};for(const c of t.values())a(c.selection);for(let c=0;c{x>=0&&x{if(!h.has(j)){if(h.add(j),i&&M.toArray(i,k*3),c)M.toArray(c,o*3);else{let C=e.selection.children[o];C||(C=new sr(e.selectionGeometry,e.selectionMaterial),C.renderOrder=10,e.selection.add(C)),C.position.copy(M),C.scale.setScalar(Math.max(.24,r.radii[x]||.3)*1.35),C.visible=!0}m&&(m[k]=1),o+=1}};for(const[x,j]of e.ribbonSelections??[]){const k=s.get(x);k!==void 0&&y(j.selection.atom,x,k,j.position)}for(let x=0;xA;)e.selection.remove(e.selection.children[e.selection.children.length-1]);return c?(a.needsUpdate=!0,e.selectionPoints.geometry.setDrawRange(0,o),e.selectionPoints.visible=o>0):(e.selectionPoints.geometry.setDrawRange(0,0),e.selectionPoints.visible=!1),i&&m&&t.length>0&&m.every(x=>x===1)?i:null}function Ps(e,t,n){const r=e.model;if(e.keyboardFocus.visible=!1,!r||!t)return null;const o=e.ribbonSelections.get(Xt(t.atom,t.image));if(o)return e.keyboardFocus.position.copy(o.position),e.keyboardFocus.scale.setScalar(Math.max(.24,r.radii[t.atom]||.3)*1.62),e.keyboardFocus.visible=!0,-1;if(n!==null&&Dr(e.instanceToAtom,e.instanceImages,n,t,e.baseImages))return pc(e,t,n),n;for(let s=0;s=0?a:0:a>=0?(a+Math.sign(o)+i)%i:o<0?i-1:0,l=Wi(e,t,c,s);return l?{selection:l,instance:c}:null}function Dr(e,t,n,r,o=new Int32Array){if(!r||!Number.isInteger(n)||n<0||n>=e.length)return!1;const s=n*3,i=e[n]*3;return s+2n.distanceToSquared(new F().fromBufferAttribute(r,u))=0&&mu(c)?{atom:a,image:c}:null}function Wi(e,t,n,r=new Int32Array){if(!Number.isInteger(n)||n<0||n>=e.length)return null;const o=n*3;if(o+2>=t.length)return null;const s=e[n],i=s*3;return{atom:s,image:[(r[i]??0)+t[o],(r[i+1]??0)+t[o+1],(r[i+2]??0)+t[o+2]]}}function mu(e){return e.length===3&&e.every(Number.isInteger)}function Xt(e,t){return`${e}:${t[0]}:${t[1]}:${t[2]}`}function bg(e,t){return!e||!t?e===t:Xt(e.atom,e.image)===Xt(t.atom,t.image)}function yg(e,t){const n=`${e.topology.symbols?.[t.atom]??"Atom"} ${t.atom+1}`,r=t.image.map((o,s)=>{if(o===0)return"";const i=o>0?"+":"−",a=Math.abs(o)===1?"":Math.abs(o);return`${i}${a}${"abc"[s]}`}).join("");return r?`${n} (${r})`:n}function xg(e,t,n,r){const o=e.model;if(!o||t.width<=0||t.height<=0)return[];const s=Math.max(t.left,Math.min(n.x,r.x)),i=Math.min(t.right,Math.max(n.x,r.x)),a=Math.max(t.top,Math.min(n.y,r.y)),c=Math.min(t.bottom,Math.max(n.y,r.y));if(i<=s||c<=a)return[];e.camera.updateMatrixWorld();const l=new F,u=[],d=new Set,m=(g,h)=>{const y=Xt(g.atom,g.image);if(d.has(y)||(l.copy(h),l.project(e.camera),!Number.isFinite(l.x)||!Number.isFinite(l.y)||l.z<-1||l.z>1))return;const A=t.left+(l.x+1)*.5*t.width,x=t.top+(1-l.y)*.5*t.height;Ai||xc||(d.add(y),u.push(g))};for(const g of e.ribbonSelections.values())m(g.selection,g.position);for(let g=0;gjg(s,n.basis,c,n.cellCenter));const i=o.getSize(new F).length(),a=s.getSize(new F).length();lf(i,a,n.images)&&o.union(s)}return o.isEmpty()?null:o}function gc(e,t){const n=Ag(e,t);if(!n)return;hu(e.controls);const r=t.presentation.mode==="ribbon"&&t.preset==="perspective"&&t.model.images.length===1?Sg(t.model):null,o=r?Nh(r,t.model.backbone.map((x,j)=>j),t.presentation.atomScale,e.camera.aspect):null,s=o?.center??n.getCenter(new F),i=Me.degToRad(e.camera.fov*.5),a=Math.atan(Math.tan(i)*e.camera.aspect),c=Math.min(i,a),{direction:l,up:u}=o??Ng(t.preset),d=new F().crossVectors(u,l).normalize(),m=new F().crossVectors(l,d).normalize(),g=.78,h=o?.points??Cg(n),y=o?.radius??0;let A=1.6/Math.tan(c)*1.08;for(const x of h){const j=x.clone().sub(s),k=j.dot(l);A=Math.max(A,k+(Math.abs(j.dot(d))+y)/(Math.tan(a)*g),k+(Math.abs(j.dot(m))+y)/(Math.tan(i)*g))}e.camera.up.copy(u),e.camera.position.copy(s).addScaledVector(l,A),e.camera.near=Math.max(A/500,.01),e.camera.far=Math.max(A*30,100),e.camera.updateProjectionMatrix(),e.controls.target.copy(s),e.controls.update(),e.lastFittedAspect=e.camera.aspect,e.cameraMode="fit"}function Sg(e){const t=new Float32Array(e.backbone.length*3);let n=null,r;for(let o=0;o0&&s!==r&&(n=null);const i=new F().fromArray(e.positions,e.backbone[o].ca*3),a=n?Xn(n,i,e.basis,e.pbc):i;a.toArray(t,o*3),n=a,r=s}return t}function Mg(e,t){return{position:e.position.toArray(),target:t.toArray(),up:e.up.toArray(),fov:e.fov,zoom:e.zoom,near:e.near,far:e.far}}function kg(e,t){if([...t.position,...t.target,...t.up,t.fov,t.zoom,t.near,t.far].some(r=>!Number.isFinite(r))||t.fov<=0||t.fov>=180||t.zoom<=0||t.near<=0||t.far<=t.near)throw new Error("The saved camera is invalid");hu(e.controls),e.camera.position.fromArray(t.position),e.camera.up.fromArray(t.up).normalize(),e.camera.fov=t.fov,e.camera.zoom=t.zoom,e.camera.near=t.near,e.camera.far=t.far,e.controls.target.fromArray(t.target),e.camera.updateProjectionMatrix(),e.controls.update(),e.cameraMode="manual"}function hu(e){const t=e.enableDamping;e.enableDamping=!1;try{e.update()}finally{e.enableDamping=t}}function vg(e,t){const n=uf(e.images);return[t.mode,t.wrap,t.cellOrigin.join(","),t.mirror.join(","),t.cell,e.visibleAtoms.length,n.count,n.span.join(",")].join(":")}function jg(e,t,n,r){sl(t,n,r).forEach(o=>e.expandByPoint(o))}function Cg(e){const t=[];for(const n of[e.min.x,e.max.x])for(const r of[e.min.y,e.max.y])for(const o of[e.min.z,e.max.z])t.push(new F(n,r,o));return t}function Ng(e){return e==="xy"?{direction:new F(0,0,1),up:new F(0,1,0)}:e==="xz"?{direction:new F(0,1,0),up:new F(0,0,1)}:e==="yz"?{direction:new F(1,0,0),up:new F(0,0,1)}:{direction:new F(1,.68,1.15).normalize(),up:new F(0,1,0)}}function zo(e){const t=new Set,n=new Set;e.traverse(r=>{const o=r;r instanceof Ie&&r.dispose(),o.geometry&&!t.has(o.geometry)&&(t.add(o.geometry),o.geometry.dispose()),(Array.isArray(o.material)?o.material:o.material?[o.material]:[]).forEach(i=>{n.has(i)||(n.add(i),i.dispose())})})}const pu={1:"#f0eee7",2:"#d8f2f2",3:"#b889df",4:"#bed17f",5:"#d4956d",6:"#94a3a7",7:"#5680dd",8:"#df6259",9:"#6cba79",10:"#7bcdd0",11:"#9874ce",12:"#89a86d",13:"#c7b8ae",14:"#d5aa82",15:"#ed9e54",16:"#ead462",17:"#74ca88",18:"#8bdce2",19:"#aa7bdd",20:"#99ba7b",26:"#cf8964",29:"#d19a71",30:"#adb3b7",35:"#b65a4c",53:"#8d61b5"},Fg={...pu,1:"#aab5b3",6:"#273a3f",7:"#315bb8",8:"#c94138",9:"#318448",15:"#d87924",16:"#c5a51c",17:"#348b4c"},gu=1e4;function Ig({open:e,frameCount:t,options:n,defaultReferenceId:r,initialView:o,onRun:s,onClose:i}){const a=mo(n,r)??n[0],c=n.find(_=>_.id!==a?.id)??a,[l,u]=w.useState(a?.id??""),[d,m]=w.useState(c?.id??""),[g,h]=w.useState("all"),[y,A]=w.useState("200"),[x,j]=w.useState(""),k=w.useRef(null);w.useEffect(()=>{if(!e)return;const _=mo(n,r)??n[0],R=n.find(ee=>ee.id!==_?.id)??_;u(_?.id??""),m(R?.id??""),h("all"),A("200"),j("")},[r,e,n]),w.useEffect(()=>{if(!e)return;const _=requestAnimationFrame(()=>k.current?.focus());return()=>cancelAnimationFrame(_)},[e]);const M=Math.max(1,Math.ceil(t/gu)),C=Math.ceil(t/M),N=w.useMemo(()=>[{value:"all",label:M===1?`All · ${t.toLocaleString()}`:`All · ${C.toLocaleString()} sampled`},...t>100?[{value:"last-100",label:"Last 100"}]:[],...t>1e3?[{value:"last-1000",label:"Last 1,000"}]:[]],[M,t,C]);if(!e)return null;const E=mo(n,l),$=mo(n,d),L=Number(y),U=x.trim()?Number(x):void 0,z=!!(E&&$&&Number.isSafeInteger(L)&&L>=20&&L<=2e3&&(U===void 0||Number.isFinite(U)&&U>0)),G=()=>{!z||!E||!$||s(Eg({reference:E,target:$,frames:g,frameCount:t,bins:L,rMax:U,initialView:o}))};return f.jsxs("section",{className:"rdf-sheet",role:"dialog","aria-labelledby":"rdf-sheet-title",onKeyDown:_=>{_.key==="Escape"&&(_.preventDefault(),i())},children:[f.jsxs("header",{children:[f.jsx("strong",{id:"rdf-sheet-title",children:"Pair analysis"}),f.jsx("button",{type:"button",onClick:i,"aria-label":"Close",children:"×"})]}),f.jsxs("div",{className:"rdf-sheet__body",children:[f.jsxs("label",{children:[f.jsx("span",{children:"From"}),f.jsx("select",{ref:k,value:l,onChange:_=>u(_.target.value),children:n.map(_=>f.jsxs("option",{value:_.id,children:[_.label," · ",_.atomIndices.length.toLocaleString()]},_.id))})]}),f.jsxs("label",{children:[f.jsx("span",{children:"To"}),f.jsx("select",{value:d,onChange:_=>m(_.target.value),children:n.map(_=>f.jsxs("option",{value:_.id,children:[_.label," · ",_.atomIndices.length.toLocaleString()]},_.id))})]}),f.jsxs("label",{children:[f.jsx("span",{children:"Frames"}),f.jsx("select",{value:g,onChange:_=>h(_.target.value),children:N.map(_=>f.jsx("option",{value:_.value,children:_.label},_.value))})]}),f.jsxs("details",{children:[f.jsx("summary",{children:"Advanced"}),f.jsxs("div",{children:[f.jsxs("label",{children:[f.jsx("span",{children:"Bins"}),f.jsx("input",{inputMode:"numeric",value:y,onChange:_=>A(_.target.value)})]}),f.jsxs("label",{children:[f.jsx("span",{children:"r max · Å"}),f.jsx("input",{inputMode:"decimal",value:x,placeholder:"Automatic",onChange:_=>j(_.target.value)})]})]})]})]}),f.jsxs("footer",{children:[f.jsx("span",{children:"PQAnalysis · full periodic cells"}),f.jsx("button",{type:"button",disabled:!z,onClick:G,children:"Run"})]})]})}function Eg({reference:e,target:t,frames:n,frameCount:r,bins:o,rMax:s,initialView:i}){const a=n==="last-100"?100:n==="last-1000"?1e3:r,c=n==="all"?Math.max(1,Math.ceil(r/gu)):1;return{reference:e,target:t,frameStart:Math.max(0,r-a),frameStop:r,frameStep:c,bins:o,rMax:s,initialView:i}}function mo(e,t){return t?e.find(n=>n.id===t):void 0}function $g(e){return _g(e)?{commands:"⌘K",open:"⌘O",export:"⌘⇧S"}:{commands:"Ctrl K",open:"Ctrl O",export:"Ctrl Shift S"}}function _g(e){return/mac|iphone|ipad|ipod/i.test(e)}function Rg(e){return e==="true"}function Tg(e,t,n){return n<=0?0:Math.max(0,Math.min(n-1,e+t))}function Pg(e,t){return e==="g"?t==="g"?{action:"first-frame",prefix:null}:{action:null,prefix:"g"}:e==="G"?{action:"last-frame",prefix:null}:e==="l"?{action:"next-frame",prefix:null}:e==="L"?{action:"next-ten-frames",prefix:null}:e==="h"?{action:"previous-frame",prefix:null}:e==="H"?{action:"previous-ten-frames",prefix:null}:e===":"?{action:"commands",prefix:null}:{action:null,prefix:null}}const Ut=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No","Lr","Rf","Db","Sg","Bh","Hs","Mt","Ds","Rg","Cn","Nh","Fl","Mc","Lv","Ts","Og"],Og="unknown hydrogen helium lithium beryllium boron carbon nitrogen oxygen fluorine neon sodium magnesium aluminium silicon phosphorus sulfur chlorine argon potassium calcium scandium titanium vanadium chromium manganese iron cobalt nickel copper zinc gallium germanium arsenic selenium bromine krypton rubidium strontium yttrium zirconium niobium molybdenum technetium ruthenium rhodium palladium silver cadmium indium tin antimony tellurium iodine xenon caesium barium lanthanum cerium praseodymium neodymium promethium ",Lg="samarium europium gadolinium terbium dysprosium holmium erbium thulium ytterbium lutetium hafnium tantalum tungsten rhenium osmium iridium platinum gold mercury thallium lead bismuth polonium astatine radon francium radium actinium thorium protactinium uranium ",Bg="neptunium plutonium americium curium berkelium californium einsteinium fermium ",Dg="mendelevium nobelium lawrencium rutherfordium dubnium seaborgium bohrium hassium ",zg="meitnerium darmstadtium roentgenium copernicium nihonium flerovium moscovium livermorium tennessine oganesson",hi=(Og+Lg+Bg+Dg+zg).split(" "),Rn=new Map;for(let e=1;e0&&e[Vt(l),u])),s=or(t);if(n==="add"){for(const l of s){const u=Vt(l);o.has(u)||(o.set(u,r.length),r.push(l))}return r}const i=new Set(s.map(Vt)),a=r.filter(l=>!i.has(Vt(l))),c=new Set(r.map(Vt));for(const l of s)c.has(Vt(l))||a.push(l);return a}function qg(e,t){return{name:Qg(e),selections:or(t)}}function bu(e){const t=new Map;for(const o of e){const s=Ut[o]??"X";t.set(s,(t.get(s)??0)+1)}const n=[...t.keys()];return(t.has("C")?["C",...t.has("H")?["H"]:[],...n.filter(o=>o!=="C"&&o!=="H").sort()]:n.sort()).map(o=>{const s=t.get(o);return`${o}${s===1?"":s}`}).join("")}function Gg(e){const t=e.match(/^\s*select\s+within\s+((?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)\s*(?:a|å|angstroms?)\s+of\s+selection\s*$/i);if(!t)return null;const n=Number(t[1]);return Number.isFinite(n)&&n>0?n:null}function yu(e){xu(e);const t=Kg(e),n=new Int32Array(e.count),r=new Uint8Array(e.count);for(let i=0;i0,componentRoots:s,residueIndices:t}}class Hg{context;hasConnectivity;componentRoots;residueIndices;visibleInstances=null;constructor(t,n=yu(t)){if(xu(t),n.count!==t.count)throw new RangeError("Selection topology does not match the scene");this.context=t,this.hasConnectivity=n.hasConnectivity,this.componentRoots=n.componentRoots,this.residueIndices=n.residueIndices}selectionAt(t){if(!Number.isInteger(t)||t<0||t>=this.context.instanceToAtom.length)return null;const n=this.context.instanceToAtom[t];if(n>=this.context.count)return null;const r=t*3,o=n*3;return{atom:n,image:[this.context.baseImages[o]+this.context.instanceImages[r],this.context.baseImages[o+1]+this.context.instanceImages[r+1],this.context.baseImages[o+2]+this.context.instanceImages[r+2]]}}displayedPosition(t){if(!Wn(t,this.context.count))return null;const n=new Float64Array(3);return Os(n,this.context,t)?[n[0],n[1],n[2]]:null}isVisible(t){return this.instanceFor(t)!==null}selectScope(t,n){if(!Wn(t,this.context.count))return[];const r=this.displayImage(t);if(!r||this.instanceFor(t)===null)return[];if(n==="atom")return[Xi(t)];const o=t.atom;let s;if(n==="element"){const i=this.context.atomicNumbers[o];s=a=>this.context.atomicNumbers[a]===i}else if(n==="residue"){const i=this.residueIndices[o];if(i<0)return null;s=a=>this.residueIndices[a]===i}else if(n==="component"){if(!this.hasConnectivity)return null;const i=this.componentRoots[o];s=a=>this.componentRoots[a]===i}else if(n==="molecule"){const i=this.residueIndices[o];if(this.hasConnectivity){const a=this.componentRoots[o];s=c=>this.componentRoots[c]===a}else{if(i<0)return null;s=a=>this.residueIndices[a]===i}}else throw new TypeError(`Unknown scientific selection scope: ${String(n)}`);return this.collectVisible((i,a)=>s(i)&&this.instanceHasDisplayImage(a,r))}selectElement(t){const n=Vg(t);return n===null?[]:this.collectVisible(r=>this.context.atomicNumbers[r]===n)}selectWater(){return this.collectVisible(t=>this.context.waterAtoms.has(t))}withinDistance(t,n){return this.withinDistanceOf([t],n)}withinDistanceOf(t,n){if(!Number.isFinite(n)||n<=0)return[];const r=new Float64Array(3),o=new Map,s=new Set;for(const c of t){if(!Wn(c,this.context.count))continue;const l=Vt(c);if(s.has(l)||(s.add(l),!Os(r,this.context,c)))continue;const u=yc(Math.floor(r[0]/n),Math.floor(r[1]/n),Math.floor(r[2]/n)),d=o.get(u);d?d.push(r[0],r[1],r[2]):o.set(u,[r[0],r[1],r[2]])}if(o.size===0)return[];const i=n*n,a=new Float64Array(3);return this.collectVisible((c,l)=>{const u=l*3;if(!wu(a,0,this.context,c,this.context.instanceImages[u],this.context.instanceImages[u+1],this.context.instanceImages[u+2]))return!1;const d=Math.floor(a[0]/n),m=Math.floor(a[1]/n),g=Math.floor(a[2]/n);for(let h=d-1;h<=d+1;h+=1)for(let y=m-1;y<=m+1;y+=1)for(let A=g-1;A<=g+1;A+=1){const x=o.get(yc(h,y,A));if(x)for(let j=0;j=this.context.count||!t(s,o))continue;const i=this.selectionAt(o);if(!i)continue;const a=Vt(i);r.has(a)||(r.add(a),n.push(i))}return n}displayImage(t){if(!Wn(t,this.context.count))return null;const n=t.atom*3;return[t.image[0]-this.context.baseImages[n],t.image[1]-this.context.baseImages[n+1],t.image[2]-this.context.baseImages[n+2]]}instanceFor(t){const n=this.displayImage(t);return n?this.ensureVisibleInstances().get(bc(t.atom,n[0],n[1],n[2]))??null:null}instanceHasDisplayImage(t,n){const r=t*3;return this.context.instanceImages[r]===n[0]&&this.context.instanceImages[r+1]===n[1]&&this.context.instanceImages[r+2]===n[2]}ensureVisibleInstances(){if(this.visibleInstances)return this.visibleInstances;const t=new Map;for(let n=0;n=this.context.count)continue;const o=n*3,s=bc(r,this.context.instanceImages[o],this.context.instanceImages[o+1],this.context.instanceImages[o+2]);t.has(s)||t.set(s,n)}return this.visibleInstances=t,t}}function xu(e){if(!Number.isInteger(e.count)||e.count<0)throw new RangeError("Selection context count must be a non-negative integer");if(e.atomicNumbers.length=0&&r<=2147483647&&(t[n]=r)}return t}function Os(e,t,n){const r=n.atom*3;return wu(e,0,t,n.atom,n.image[0]-t.baseImages[r],n.image[1]-t.baseImages[r+1],n.image[2]-t.baseImages[r+2])}function wu(e,t,n,r,o,s,i){const a=r*3;let c=n.positions[a],l=n.positions[a+1],u=n.positions[a+2];if(![c,l,u].every(Number.isFinite))return!1;if(o!==0||s!==0||i!==0){const d=n.cell;if(!d)return!1;c+=o*d[0]+s*d[3]+i*d[6],l+=o*d[1]+s*d[4]+i*d[7],u+=o*d[2]+s*d[5]+i*d[8]}return[c,l,u].every(Number.isFinite)?(e[t]=c,e[t+1]=l,e[t+2]=u,!0):!1}function Wg(e,t,n){return Number.isInteger(e)&&Number.isInteger(t)&&e>=0&&t>=0&&e=0&&e.atom80)throw new RangeError("Named selection name is too long");return t}const gi=12,xc=1,wc=60;function Zg(e,t=gi){const n=Number.isFinite(t)&&t>0?Math.min(wc,Math.max(xc,t)):gi;return!Number.isFinite(e)||e<=0?n:Math.min(wc,Math.max(xc,e))}function Jg(e){return 1e3/Zg(e)}function Au(e,t,n){const r=Number.isFinite(e)?Math.max(0,e):0,o=Jg(n),s=t!==null&&Number.isFinite(t)&&t>=0&&t<=r?t:null;if(s===null)return{delayMs:o,requestTimeMs:r+o,stepCount:1};const i=Math.floor((Math.max(0,r-s)+o*1e-9)/o),a=Math.max(1,i),c=s+a*o;return{delayMs:Math.max(0,c-r),requestTimeMs:c,stepCount:a}}function Ac(e){return Math.ceil(Math.max(0,Number.isFinite(e)?e:0))}function bi(e,t=1){const n=Number.isFinite(t)&&t>0?Math.max(1,Math.round(t)):1;return!Number.isFinite(e)||e<=0?n:Math.max(1,Math.round(e))}function Su(e,t,n={}){const r=Mu(t),o=n.direction===-1?-1:1;if(r<2)return{frameIndex:0,direction:o,continuePlaying:!1};const s=r-1,i=yi(e,s),a=bi(n.stride??1),c=n.mode??"loop";if(c==="once"){const l=i+o*a,u=l<=0||l>=s;return{frameIndex:yi(l,s),direction:o,continuePlaying:!u}}return c==="rock"?rb(i,s,o,a):{frameIndex:ku(i+o*a,r),direction:o,continuePlaying:!0}}function eb(e,t,n,r){return Su(e,t,{...n,stride:bi(n.stride??1)*bi(r.stepCount)})}function tb(e,t,n,r,o,s,i){const a=Au(e,t,n);if(a.delayMs>0)return{committed:!1,schedule:a,step:null};const c=eb(r,o,s,a);return i.onStep(c),i.onPulse(),{committed:!0,schedule:a,step:c}}function nb(e,t,n,r=4){const o=Mu(t),s=Number.isFinite(r)?Math.max(0,Math.floor(r)):0;if(o<2||s===0)return[];let i=yi(e,o-1),a=n.direction===-1?-1:1;const c=new Set([i]),l=[];for(let u=0;uwi(o,t.mark.key)),r=n>=0?e.bookmarks.filter((o,s)=>s!==n):[...e.bookmarks,Sc(t.mark)].sort((o,s)=>o.index-s.index).slice(-12);return{...e,bookmarks:Object.freeze(r)}}case"set-reference":return{...e,reference:Sc(t.mark)};case"clear-reference":return{...e,reference:null,tracking:e.tracking==="displacement"?"off":e.tracking};case"set-tracking":return{...e,tracking:t.mode==="displacement"&&e.reference===null?"off":t.mode};case"open-plot":return{...e,plot:Mc(t.plot)};case"update-plot":return e.plot?.requestId===t.plot.requestId?{...e,plot:Mc(t.plot)}:e;case"close-plot":return{...e,plot:null}}}function sb(e,t){if(!Number.isSafeInteger(e)||e<0)return null;const n=t?.header.frame_key;return ib(n)?{index:e,key:Yi(n),step:kc(t,"step"),time:kc(t,"time"),timeUnit:ab(t?.header.scalar_units?.time)}:null}function wi(e,t){return!!(e&&t&&e.source_id===t.source_id&&e.source_index===t.source_index&&e.segment_index===t.segment_index&&po(e.step)===po(t.step)&&po(e.time)===po(t.time)&&(e.time_unit??null)===(t.time_unit??null))}function Cn(e){const t=[e.step===null?"":`step ${vc(e.step)}`,e.time===null?"":`t ${vc(e.time)}${e.timeUnit?` ${e.timeUnit}`:""}`].filter(Boolean);return[`Frame ${e.index+1}`,...t].join(" · ")}function Sc(e){return Object.freeze({...e,key:Object.freeze(Yi(e.key))})}function Yi(e){return{source_id:e.source_id,source_index:e.source_index,segment_index:e.segment_index,step:e.step??null,time:e.time??null,time_unit:e.time_unit??null}}function Mc(e){return Object.freeze({...e,xValues:Object.freeze([...e.xValues]),frameIndices:e.frameIndices?Object.freeze([...e.frameIndices]):void 0,frameKeys:e.frameKeys?Object.freeze(e.frameKeys.map(t=>t?Object.freeze(Yi(t)):null)):void 0,lines:Object.freeze(e.lines.map(t=>Object.freeze({...t,values:Object.freeze([...t.values]),selection:t.selection?Object.freeze(t.selection.map(({atom:n,image:r})=>Object.freeze({atom:n,image:Object.freeze([...r])}))):void 0})))})}function ib(e){return!!(e&&typeof e.source_id=="string"&&e.source_id.length>0&&Number.isSafeInteger(e.source_index)&&e.source_index>=0&&Number.isSafeInteger(e.segment_index)&&e.segment_index>=0)}function kc(e,t){const n=e?.header[t];if(typeof n=="number"&&Number.isFinite(n))return n;const r=e?.header.scalars?.[t];return typeof r=="number"&&Number.isFinite(r)?r:null}function ab(e){return e?.trim()||null}function po(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function vc(e){return new Intl.NumberFormat("en",{maximumFractionDigits:5}).format(e)}const jc=50,$r=16;function cb(e,t,n,r){if(e==="off"||!Number.isSafeInteger(t)||t<0||t>=r)return[];const o=e==="trail"?Array.from({length:Math.min(jc+1,t+1)},(s,i)=>Math.max(0,t-jc)+i):[t];return e==="displacement"&&n!==null&&Number.isSafeInteger(n)&&n>=0&&ns-i)}function lb(e,t,n,r,o){const s=e.frames.find(({index:i})=>i===t);if(n&&(!s||!wi(s.key,n)))return"current";if(r!==null&&o){const i=e.frames.find(({index:a})=>a===r);if(!i||!wi(i.key,o))return"reference"}return null}function ub(e,t,n,r,o){if(t.length>$r)throw new RangeError(`Track up to ${$r} selected atoms`);const s=new Map(e.atomIndices.map((u,d)=>[u,d])),i=new Map(e.frames.map(u=>[u.index,u])),a=i.get(r);if(!a)return{trails:[],displacements:[]};const c=[];if(n==="trail"){const u=e.frames.filter(({index:d})=>d<=r).sort((d,m)=>d.index-m.index).slice(-51);for(const d of t){const m=s.get(d.atom);if(m===void 0||u.length<2)continue;const g=new Float32Array(u.length*3);u.forEach(({positions:h},y)=>{g.set(h.subarray(m*3,m*3+3),y*3)}),c.push({id:Cc(d),atom:d.atom,image:[...d.image],points:g})}}const l=[];if(n==="displacement"&&o!==null){const u=i.get(o);if(u)for(const d of t){const m=s.get(d.atom);if(m===void 0)continue;const g=m*3;l.push({id:Cc(d),atom:d.atom,image:[...d.image],from:[u.positions[g],u.positions[g+1],u.positions[g+2]],to:[a.positions[g],a.positions[g+1],a.positions[g+2]]})}}return{trails:c,displacements:l}}function Cc(e){return`${e.atom}:${e.image.join(":")}`}const db="pqviewer-dataset",fb=1048576,go=4096,Nc={format:"png",width:2400,height:1800,dpi:300,background:{kind:"solid",color:"#ffffff"},projection:"orthographic",fit:!0,padding:.08,periodicContext:!0},tt={mode:"ball-stick",water:"show",hydrogens:!0,wrap:"molecule",images:{min:[0,0,0],max:[0,0,0]},cellOrigin:[0,0,0],mirror:[!1,!1,!1],cell:!0,forces:!0,velocities:!1,atomScale:1,bondScale:1,color:"element",quality:"auto"};function mb(){const[e,t]=w.useState(null),[n,r]=w.useState("loading"),[o,s]=w.useState(""),[i,a]=w.useState(0),[c,l]=w.useState(0),[u,d]=w.useState(null),[m,g]=w.useState(""),[h,y]=w.useState(!1),[A,x]=w.useState(!1),[j,k]=w.useState(gi),[M,C]=w.useState(1),[N,E]=w.useState("loop"),[$,L]=w.useState(1),[U,z]=w.useState(0),[G,_]=w.useState(!1),[R,ee]=w.useState(ny),[le,de]=w.useState("auto"),[I,J]=w.useState([]),[Q,v]=w.useState("measurement"),[B,K]=w.useState(null),[X,ne]=w.useState(null),[se,P]=w.useState([]),[Y,he]=w.useState([]),[q,pe]=w.useState(!0),[re,xe]=w.useState(!1),[be,ge]=w.useState(null),[H,oe]=w.useReducer(ob,xi),[rt,Ee]=w.useState({trails:[],displacements:[]}),[ce,Ye]=w.useState(!1),[_t,cr]=w.useState("rdf"),[Qt,ot]=w.useState(null),[Oe,gt]=w.useState("rdf"),[Qe,Le]=w.useState(!1),[Tn,st]=w.useState(null),[ue,$e]=w.useState(null),[bt,Fe]=w.useState(!1),[Zt,Te]=w.useState(!1),[it,bn]=w.useState(ey),[Kr,Pn]=w.useState(0),[yn,V]=w.useState("perspective"),[ke,Ce]=w.useState(0),[W,Rt]=w.useState(!1),[Jt,Ge]=w.useState(!1),[Tt,Wr]=w.useState(Nc),[Pt,Ot]=w.useState([]),[On,en]=w.useState(!1),[lr,yt]=w.useState(""),[ur,ea]=w.useState(1),[dr,ta]=w.useState(1),[Eu,$u]=w.useState([]),[_u,Yo]=w.useState(!1),[Xr,na]=w.useState(!1),[xt,Z]=w.useState(null),[Ru,Qo]=w.useState(null),[ra,Yr]=w.useState(null),tn=w.useRef(new vs),Qr=w.useRef(null),oa=w.useRef(null),sa=w.useRef(null),ia=w.useRef(null),aa=w.useRef(null),ca=w.useRef(null),Zr=w.useRef({prefix:null,at:0}),fr=w.useRef(0),Jr=w.useRef(""),mr=w.useRef(0),eo=w.useRef(null),hr=w.useRef(0),Be=w.useRef(0),De=w.useRef(null),wt=w.useRef(null),ze=w.useRef(null),Ln=w.useRef(0),At=w.useRef(null),la=w.useRef(1),Zo=w.useRef({selections:null,key:""}),to=w.useRef(!1),Jo=w.useRef(!1),Bn=w.useRef(""),es=w.useRef(null),xn=w.useRef(null),pr=w.useRef(null),Dn=w.useRef({key:"",requestTimeMs:null}),nn=R.wrap==="unwrapped"?"unwrapped":"source",no=w.useRef("source"),ua=w.useRef({playing:A,mode:N,direction:$,stride:M});ua.current={playing:A,mode:N,direction:$,stride:M};const zn=w.useMemo(()=>$g(ty()),[]),Vn=w.useCallback((p,b=!1)=>{Ln.current+=1,Be.current+=1,De.current?.abort(),De.current=null,wt.current?.abort(),wt.current=null,ze.current?.abort(),ze.current=null,tn.current.clear(),tn.current=new vs({datasetGeneration:p.dataset_generation}),no.current="source",to.current=!1,Bn.current=p.dataset_generation??"",es.current=p,xn.current=null,Yr(null),t(p),l(0),d(null),J([]),v("measurement"),K(null),ne(null),P([]),he([]),pe(!0),xe(!1),ge(null),oe({type:"reset",preserveMarks:b}),Ee({trails:[],displacements:[]}),Ye(!1),ot(null),Le(!1),st(null),x(!1),L(1),_(!1),$e(null),Fe(!1),Te(!1),Ge(!1),Wr(vr(Nc)),Ot([]),en(!1),yt(""),At.current=null,Qo(null),ee(S=>({...S,...$c()})),r("ready"),s(""),de("auto"),Jr.current="",document.title=`${p.name||"Trajectory"} · PQViewer`},[]),ro=w.useCallback((p,b)=>{const S=ii(p);if(!jm(S,b))throw new Error("This figure recipe belongs to a different source");if(S.frame.index>=b.frame_count)throw new Error("The saved frame is outside this trajectory");if(S.scene.selection.atoms.some(({atom:D})=>D>=b.topology.atom_count))throw new Error("The saved selection is outside this structure");if(S.annotations.some(D=>D.kind==="atom-label"&&D.atom.atom>=b.topology.atom_count))throw new Error("The saved atom labels are outside this structure");const O=Ln.current+1;Ln.current=O,At.current=null,en(!0),yt(""),xo(S.frame.index,void 0,b.dataset_generation,S.scene.presentation.wrap==="unwrapped"?"unwrapped":"source").then(D=>{if(Ln.current!==O)return;if(!Mr(D.header.frame_key,S.frame.key))throw new Error("The saved frame no longer matches this trajectory");if(Rs(b,D)!==S.frame.fingerprint)throw new Error("The saved frame content changed");const te=xn.current?.manifest===b?xn.current.value:null,ye=te??Zs(b,D);if(!ye)throw new Error("The molecular topology is unavailable");const an=Mp(b,D,S.scene.presentation,ye);if(S.scene.presentation.mode==="polyhedra"&&!an.polyhedra)throw new Error(`Polyhedra unavailable · ${an.polyhedraReason}`);if(!te){const Aa={manifest:b,value:ye};xn.current=Aa,Yr(Aa)}x(!1),_(!1),xe(!1),ge(null),Be.current+=1,De.current?.abort(),De.current=null,wt.current?.abort(),wt.current=null,ze.current?.abort(),ze.current=null,oe({type:"reset"}),Ee({trails:[],displacements:[]}),Ye(!1),ot(null),Le(!1),st(null),$e(null),Fe(!1),Te(!1),Ge(!1),de("custom"),Jr.current=`${b.name}:${b.topology.atom_count}`,ee(S.scene.presentation),J(ho(S.scene.selection.atoms)),v(S.scene.selection.intent),pe(S.scene.selection.minimumImage),ea(S.scene.vectors.forceScale),ta(S.scene.vectors.velocityScale),Wr(vr(S.output)),Ot(ii(S).annotations),g(""),l(S.frame.index),At.current=S}).catch(D=>{if(Ln.current!==O)return;const te=Ke(D);en(!1),yt(te),Z({message:`Figure recipe unavailable · ${te}`,tone:"error"})})},[]),Ze=w.useCallback(()=>{to.current||(Ln.current+=1,Be.current+=1,De.current?.abort(),De.current=null,wt.current?.abort(),wt.current=null,ze.current?.abort(),ze.current=null,to.current=!0,Bn.current="",es.current=null,xn.current=null,Yr(null),tn.current.clear(),no.current="source",t(null),l(0),d(null),g(""),y(!1),J([]),v("measurement"),K(null),ne(null),P([]),he([]),xe(!1),ge(null),oe({type:"reset"}),Ee({trails:[],displacements:[]}),Ye(!1),ot(null),Le(!1),st(null),x(!1),L(1),_(!1),$e(null),Fe(!1),Te(!1),Ge(!1),Ot([]),en(!1),yt(""),At.current=null,Qo(null),r("loading"),s(""),Z({message:"Trajectory changed in another tab · reloading",tone:"status"}),a(p=>p+1))},[]);w.useEffect(()=>{if(typeof window.BroadcastChannel!="function")return;const p=new BroadcastChannel(db);return pr.current=p,p.onmessage=b=>{const S=typeof b.data=="object"&&b.data!==null&&"datasetGeneration"in b.data&&typeof b.data.datasetGeneration=="string"?b.data.datasetGeneration:"";S&&Bn.current&&S!==Bn.current&&Ze()},()=>{pr.current===p&&(pr.current=null),p.close()}},[Ze]);const ts=w.useCallback(async()=>{const p=Bn.current;if(!(!p||Jo.current||to.current)){Jo.current=!0;try{const b=await va();if(Bn.current!==p)return;b.dataset_generation!==p&&(Vn(b,Gb(es.current,b)),Z({message:"Trajectory changed · updated",tone:"status"}),pr.current?.postMessage({datasetGeneration:b.dataset_generation}))}catch{}finally{Jo.current=!1}}},[Vn]);w.useEffect(()=>{const p=()=>{ts()},b=()=>{document.visibilityState==="visible"&&ts()};return window.addEventListener("focus",p),document.addEventListener("visibilitychange",b),()=>{window.removeEventListener("focus",p),document.removeEventListener("visibilitychange",b)}},[ts]),w.useEffect(()=>{document.documentElement.dataset.appearance="light",document.documentElement.style.colorScheme="light",document.querySelector('meta[name="theme-color"]')?.setAttribute("content","#f6f8f8");try{window.localStorage.setItem("pqviewer-presentation",JSON.stringify({mode:R.mode,water:R.water,cell:R.cell,forces:R.forces,velocities:R.velocities}))}catch{}},[R]),w.useEffect(()=>{try{window.localStorage.setItem("pqviewer-vim-navigation",String(it))}catch{}},[it]),w.useEffect(()=>{let p=!0;return r("loading"),s(""),Promise.all([va(),gd()]).then(([b,S])=>{if(p&&(Vn(b),S!==null))try{ro(Ro(S),b)}catch(O){const D=Ke(O);yt(D),Z({message:`Figure recipe unavailable · ${D}`,tone:"error"})}}).catch(b=>{p&&(s(Ke(b)),r("error"))}),()=>{p=!1}},[Vn,i,ro]),w.useEffect(()=>{!e||no.current===nn||(tn.current.clear(),tn.current=new vs({datasetGeneration:e.dataset_generation,coordinates:nn}),no.current=nn,g(""))},[nn,e]),w.useEffect(()=>{if(!e||e.frame_count===0||W)return;let p=!0;return tn.current.cancelPendingExcept(c),y(!0),g(""),tn.current.get(c).then(b=>{if(!p)return;d({index:c,data:b}),y(!1);const S=ua.current;(S.playing?nb(c,e.frame_count,{mode:S.mode,direction:S.direction,stride:S.stride}):Array.from({length:Math.min(4,e.frame_count-1)},(D,te)=>(c+te+1)%e.frame_count)).forEach(D=>tn.current.prefetch(D,e.frame_count))}).catch(b=>{if(p){if(b instanceof ct){Ze();return}if(nn==="unwrapped"){ee(S=>S.wrap==="unwrapped"?{...S,wrap:"atom"}:S),Z({message:"Unwrapped coordinates unavailable · showing atoms",tone:"error"}),y(!1),x(!1);return}g(Ke(b)),y(!1),x(!1)}}),()=>{p=!1}},[nn,c,e,Ze,W]),w.useEffect(()=>{const p=At.current;if(!p||!e||!u||u.index!==p.frame.index||h)return;if(!Mr(u.data.header.frame_key,p.frame.key)){At.current=null,en(!1),yt("The saved frame no longer matches this trajectory"),Z({message:"Figure recipe unavailable · saved frame changed",tone:"error"});return}if(Rs(e,u.data)!==p.frame.fingerprint){At.current=null,en(!1),yt("The saved frame content changed"),Z({message:"Figure recipe unavailable · saved frame changed",tone:"error"});return}let b=!1,S=0,O=0;return S=requestAnimationFrame(()=>{O=requestAnimationFrame(()=>{if(!(b||At.current!==p))try{const D=Qr.current;if(!D)throw new Error("The molecular scene is not ready");D.restoreCamera(p.camera),At.current=null,en(!1),yt(""),Ub()||Z({message:"Figure recipe restored",tone:"status"})}catch(D){At.current=null,en(!1);const te=Ke(D);yt(te),Z({message:`Figure recipe unavailable · ${te}`,tone:"error"})}})}),()=>{b=!0,cancelAnimationFrame(S),cancelAnimationFrame(O)}},[h,u,e,R,ur,dr]);const He=w.useCallback(p=>{e?.frame_count&&(L(1),l(Math.max(0,Math.min(e.frame_count-1,Math.round(p)))))},[e?.frame_count]),rn=w.useCallback(p=>{e?.frame_count&&(L(1),l(b=>Tg(b,p,e.frame_count)))},[e?.frame_count]),da=w.useCallback(()=>{requestAnimationFrame(()=>ca.current?.focus())},[]),on=w.useCallback((p,b=!1)=>{_(!1),Ge(!1),xe(!1),ge(null),$e(p),b&&da()},[da]),St=w.useCallback((p=!1)=>{const b=ue;$e(null),p&&requestAnimationFrame(()=>{b==="inspect"?document.querySelector(".molecule-canvas")?.focus():b==="summary"?document.querySelector(".selection-summary-button")?.focus():ia.current?.focus()})},[ue]),Tu=w.useCallback((p,b=!1)=>{if(_(!1),v("measurement"),p===null){J([]),K(null),xe(!1),ge(null),$e(S=>S==="inspect"||S==="summary"?null:S);return}K({atom:p.atom,image:[...p.image]}),J(S=>Bf(S,p,b?"toggle":"replace"))},[]),Pu=w.useCallback((p,b=!1)=>{if(p.length===0)return;_(!1),v("set"),xe(!1),ge(null),J(O=>b?Ug(O,p,"toggle"):or(p));const S=p.at(-1);S&&K({atom:S.atom,image:[...S.image]})},[]);w.useEffect(()=>{if(I.length===0)K(null),$e(p=>p==="inspect"||p==="summary"?null:p);else if(!B||!I.some(p=>Cu(p,B))){const p=I.at(-1);K({atom:p.atom,image:[...p.image]})}(I.length<2||I.length>4)&&(xe(!1),ge(null)),I.length<=4&&$e(p=>p==="summary"?null:p)},[I,B]);const gr=w.useCallback(p=>{V(p),Ce(b=>b+1)},[]),Un=w.useCallback(()=>{W||(Fe(!1),Te(!1),Ge(!1),_(!1),$e(null),oa.current?.click())},[W]),qn=w.useCallback(()=>{W||(x(!1),Te(!1),Ge(!1),_(!1),Fe(!0))},[W]),oo=w.useCallback(()=>{W||(Fe(!1),Ge(!1),_(!1),Te(!0))},[W]),Gn=w.useCallback(async(p,b=!1)=>{const S=Qr.current;if(!S||W)return null;if(h)return Z({message:"Wait for the current frame to finish loading.",tone:"status"}),null;const O=p.format??"png",D=document.activeElement,te=D&&D!==document.body&&"focus"in D?D:null;x(!1),Rt(!0),Z({message:`Exporting ${O==="tiff"?"TIFF":"PNG"}…`,tone:"status"});try{const ye=await S.exportFigure(p);return cn(ye,zb(e?.name,p.width,p.height,O)),Z({message:`Exported ${p.width.toLocaleString()} × ${p.height.toLocaleString()} px`,tone:"status"}),ye}catch(ye){if(Z({message:`Export failed · ${Ke(ye)}`,tone:"error"}),b)throw ye;return null}finally{Rt(!1),requestAnimationFrame(()=>vu(te?.isConnected?te:aa.current))}},[h,e?.name,W]),ve=u?.data??null;w.useEffect(()=>{if(!e||!ve||xn.current?.manifest===e)return;const p=Zs(e,ve);if(!p)return;const b={manifest:e,value:p};xn.current=b,Yr(b)},[ve,e]);const Lt=u?.index??c,we=w.useMemo(()=>sb(Lt,ve),[Lt,ve]);w.useEffect(()=>{if(!we)return;const p=H.bookmarks.find(({index:b})=>b===we.index);p&&!Mr(p.key,we.key)&&(oe({type:"toggle-bookmark",mark:p}),Z({message:"Removed a stale bookmark",tone:"status"})),H.reference?.index===we.index&&!Mr(H.reference.key,we.key)&&(oe({type:"clear-reference"}),Z({message:"Reference frame changed · cleared",tone:"status"}))},[we,H.bookmarks,H.reference]);const sn=I.at(-1)?.atom??null,Ve=Yc(ve),fa=ae(ve,["forces","force"]),wn=!!(fa&&fa.length>=(e?.topology.atom_count??0)*3),ma=ae(ve,["velocities","velocity","vel"]),ns=!!(ma&&ma.length>=(e?.topology.atom_count??0)*3),_e=Ib(ve),so=Ve&&(ue==="view"||bt),An=w.useMemo(()=>so?Ic(ve):null,[ve,so]),Sn=w.useMemo(()=>so?Ic(ve,I):null,[ve,so,I]),rs=w.useMemo(()=>X?yu(X):null,[e,X?.atomResidueIndex,X?.bonds,X?.count]),ie=w.useMemo(()=>X&&rs?new Hg(X,rs):null,[X,rs]),ha=w.useMemo(()=>ie&&I.length<=4?Nu(ie,I):null,[I,ie]),os=w.useMemo(()=>X?Rb(X,I):"",[I,X?.atomicNumbers]),Ou=w.useMemo(()=>ue==="summary"?ie?.summarize(I)??null:null,[I,ie,ue]),io=w.useMemo(()=>{if(!X)return[];const p=new Set;for(let b=0;b0&&Sb-S)},[X?.atomicNumbers,X?.count]),ss=[R.wrap,R.water,R.hydrogens,R.images.min.join(","),R.images.max.join(","),R.cellOrigin.join(",")].join(":");w.useEffect(()=>{if(!ie||I.length===0||Zo.current.selections===I&&Zo.current.key===ss||(Zo.current={selections:I,key:ss},B&&ie.isVisible(B)))return;const p=[...I].reverse().find(b=>ie.isVisible(b));p&&K({atom:p.atom,image:[...p.image]})},[I,B,ie,ss]);const fe=Ru?.capabilities??null;w.useEffect(()=>{hb(R.mode,fe?.polyhedra??null,On)&&(ee(p=>p.mode==="polyhedra"?{...p,mode:"ball-stick"}:p),de("custom"),Z({message:`Polyhedra unavailable · ${fe?.polyhedraReason??er}`,tone:"status"}))},[fe?.polyhedra,fe?.polyhedraReason,R.mode,On]);const Pe=(e?.frame_count??0)>1,Lu=ve?.header.coordinates==="unwrapped"?"unwrapped":"source",Ue=!!(ve&&fe&&!h&&!On&&Lu===nn),Mt=Pe&&Q==="measurement"&&I.length>=2&&I.length<=4&&I.every(({atom:p})=>p>=0&&p<(e?.topology.atom_count??0)),is=w.useMemo(()=>kd(e?.series).filter(({name:p,values:b})=>b.length===(e?.frame_count??0)&&!["step","time"].includes(ko(p))),[e?.frame_count,e?.series]),as=w.useMemo(()=>{if(!e)return[];const p=[],b=qs(I.map(({atom:O})=>O),e.topology.atom_count);b.length>0&&b.length<=go&&p.push({id:"selected",label:os||"Selection",atomIndices:b}),se.forEach((O,D)=>{const te=qs(O.selections.map(({atom:ye})=>ye),e.topology.atom_count);te.length>0&&te.length<=go&&p.push({id:`saved-${D}`,label:O.name,atomIndices:te})});const S=e.topology.atomic_numbers??(X?.atomicNumbers?Array.from(X.atomicNumbers):[]);return io.forEach(O=>{const D=S.flatMap((te,ye)=>te===O?[ye]:[]);D.length>0&&D.length<=go&&p.push({id:`element-${O}`,label:`All ${Ut[O]} atoms`,atomIndices:D})}),p.length===0&&e.topology.atom_count>0&&e.topology.atom_count<=go&&p.push({id:"all",label:"All atoms",atomIndices:Array.from({length:e.topology.atom_count},(O,D)=>D)}),p},[e,se,io,X?.atomicNumbers,I,os]),dt=!!(Pe&&e?.source?.path&&_e.every(Boolean)&&as.length>0),Je=Pe&&I.length>0&&I.length<=$r,cs=!!(we&&H.bookmarks.some(({key:p})=>Mr(p,we.key))),Bt=w.useMemo(()=>Hb(Y),[Y]),at=!!(ue&&e&&fe),ls=w.useMemo(()=>re&&e?Array.from({length:e.frame_count},(p,b)=>b):[],[e,re]),Bu=w.useMemo(()=>ls.map(()=>null),[ls]),et=w.useCallback((p,b,S="set")=>{const O=or(p);J(O),v(S),S==="set"&&(xe(!1),ge(null));const D=O.at(-1);K(D?{atom:D.atom,image:[...D.image]}:null),b&&Z({message:b,tone:"status"})},[]),us=w.useCallback(p=>{const b=B??I.at(-1)??null;if(!ie||!b)return;const S=ie.selectScope(b,p);if(S===null){Z({message:p==="residue"?"Residue data is unavailable for this atom.":"Bond connectivity is unavailable for this structure.",tone:"status"});return}if(S.length===0){Z({message:"The anchor is not visible in the current view.",tone:"status"});return}et(S,`${$b(p)} · ${kr(S.length)}`)},[et,I,B,ie]),ds=w.useCallback(p=>{if(!ie||I.length===0||!Number.isFinite(p)||p<=0)return;const b=ie.withinDistanceOf(I,p);if(b.length===0){Z({message:"No visible atoms are within that distance.",tone:"status"});return}et(b,`Within ${Xe(p)} Å · ${kr(b.length)}`)},[et,I,ie]),pa=w.useCallback(p=>{if(!ie)return;const b=ie.selectElement(p);if(b.length===0){Z({message:`No visible ${hi[p]} atoms.`,tone:"status"});return}et(b,`${Ut[p]} · ${kr(b.length)}`)},[et,ie]),ga=w.useCallback(()=>{if(!ie)return;const p=ie.selectWater();if(p.length===0){Z({message:"No visible water molecules found.",tone:"status"});return}et(p,`Water · ${kr(p.length)}`)},[et,ie]),Du=w.useCallback(p=>{if(I.length===0)return!1;try{const b=qg(p,I);return P(S=>[...S.filter(O=>O.name.toLowerCase()!==b.name.toLowerCase()),b]),Z({message:`Saved selection · ${b.name}`,tone:"status"}),!0}catch(b){return Z({message:Ke(b),tone:"error"}),!1}},[I]),fs=w.useCallback(p=>{et(p.selections,`Selection · ${p.name}`)},[et]),zu=w.useCallback(p=>{P(b=>b.filter(S=>S.name!==p))},[]),ms=w.useCallback(()=>{if(Q!=="measurement"||I.length<2||I.length>4)return;const p=la.current;la.current+=1,he(b=>[...b,{id:p,selections:ho(I),minimumImage:q}].slice(-8)),Z({message:"Measurement pinned.",tone:"status"})},[q,I,Q]),hs=w.useCallback(p=>{pe(p.minimumImage),et(p.selections,void 0,"measurement")},[et]),br=w.useCallback((p=!1)=>{Be.current+=1,De.current?.abort(),De.current=null,ze.current?.abort(),ze.current=null,Le(!1),ot(null),st(null),oe({type:"close-plot"}),p&&requestAnimationFrame(()=>{document.querySelector(".timeline-options > summary")?.focus()})},[]),Mn=w.useCallback((p=!1)=>{xe(!1),ge(null),p&&requestAnimationFrame(()=>{document.querySelector(".selection-plot-button")?.focus()})},[]),ps=w.useCallback(()=>{Mt&&(x(!1),Fe(!1),Te(!1),Ge(!1),_(!1),$e(null),ge(null),br(!1),(!Ve||!_e.some(Boolean))&&pe(!1),xe(!0))},[Mt,Ve,br,_e[0],_e[1],_e[2]]);w.useEffect(()=>{const p=hr.current+1;if(hr.current=p,!re||!e||!Mt)return;const b=new AbortController;return ge(null),Zf({manifest:e,frameCount:e.frame_count,selections:I,wrap:R.wrap,minimumImage:q,signal:b.signal,loadFrame:(S,O)=>xo(S,O,e.dataset_generation),onProgress:S=>{hr.current===p&&!b.signal.aborted&&ge(S)}}).then(S=>{hr.current===p&&!b.signal.aborted&&ge(S)}).catch(S=>{if(!(hr.current!==p||b.signal.aborted)){if(S instanceof ct){Ze();return}xe(!1),ge(null),Z({message:`Plot unavailable · ${Ke(S)}`,tone:"error"})}}),()=>b.abort()},[Mt,e,re,q,R.wrap,Ze,I]);const yr=w.useCallback(()=>{if(!we){Z({message:"This frame has no stable source identity.",tone:"status"});return}oe({type:"toggle-bookmark",mark:we})},[we]),gs=w.useCallback(()=>{if(!we){Z({message:"This frame has no stable source identity.",tone:"status"});return}oe({type:"set-reference",mark:we}),Z({message:`Reference · ${Cn(we)}`,tone:"status"})},[we]),xr=w.useCallback(p=>{if(p!=="off"&&!Je){Z({message:I.length>$r?`Track up to ${$r} selected atoms.`:"Select atoms in a trajectory first.",tone:"status"});return}if(p==="displacement"&&H.reference===null){Z({message:"Set a reference frame first.",tone:"status"});return}oe({type:"set-tracking",mode:H.tracking===p?"off":p})},[I.length,H.reference,H.tracking,Je]);w.useEffect(()=>{if(wt.current?.abort(),wt.current=null,H.tracking==="off"||!e||!Je||I.length===0){Ee({trails:[],displacements:[]}),H.tracking!=="off"&&!Je&&oe({type:"set-tracking",mode:"off"});return}const p=cb(H.tracking,Lt,H.reference?.index??null,e.frame_count);if(p.length===0){Ee({trails:[],displacements:[]});return}const b=new AbortController;wt.current=b;const S=ho(I);return bd({datasetGeneration:e.dataset_generation??"",atomIndices:qs(S.map(({atom:O})=>O),e.topology.atom_count),frameIndices:p,coordinates:"unwrapped"},b.signal).then(O=>{if(b.signal.aborted)return;const D=lb(O,Lt,we?.key??null,H.reference?.index??null,H.reference?.key??null);if(D==="reference"){oe({type:"clear-reference"}),Ee({trails:[],displacements:[]}),Z({message:"Reference frame changed · cleared",tone:"status"});return}if(D==="current"){Ze();return}Ee(ub(O,S,H.tracking,Lt,H.reference?.index??null))}).catch(O=>{if(!b.signal.aborted){if(O instanceof ct){Ze();return}oe({type:"set-tracking",mode:"off"}),Ee({trails:[],displacements:[]}),Z({message:`Tracking unavailable · ${Ke(O)}`,tone:"error"})}}),()=>b.abort()},[Lt,we,e,Ze,I,H.reference,H.tracking,Je]);const bs=w.useCallback(p=>{if(!e||p.values.length!==e.frame_count)return;const b=Be.current+1;Be.current=b,De.current?.abort(),De.current=null,ze.current?.abort(),ze.current=null,Le(!1),ot(null),st(null),x(!1),_(!1),xe(!1),ge(null),oe({type:"open-plot",plot:Wb(p,e.frame_count,b)})},[e]),ys=w.useCallback(()=>{if(!e||Bt.length<2)return;const p=Be.current+1;Be.current=p,De.current?.abort();const b=new AbortController;De.current=b,ze.current?.abort(),ze.current=null,Le(!1),ot(null),st(null),x(!1),xe(!1),ge(null);const S=Bt.map(O=>({id:`pin-${O.id}`,label:Rc(e,O.selections),selections:O.selections,minimumImage:O.minimumImage}));oe({type:"open-plot",plot:Kb(S,e.frame_count,p)}),Jf({manifest:e,frameCount:e.frame_count,definitions:S,wrap:R.wrap,signal:b.signal,loadFrame:(O,D)=>xo(O,D,e.dataset_generation),onProgress:O=>{Be.current!==p||b.signal.aborted||oe({type:"update-plot",plot:Ga(O,p)})}}).then(O=>{Be.current!==p||b.signal.aborted||oe({type:"update-plot",plot:Ga(O,p)})}).catch(O=>{if(!(Be.current!==p||b.signal.aborted)){if(O instanceof ct){Ze();return}oe({type:"close-plot"}),Z({message:`Comparison unavailable · ${Ke(O)}`,tone:"error"})}})},[Bt,e,R.wrap,Ze]),wr=w.useCallback(p=>{if(!dt){Z({message:"Pair analysis needs a trajectory with a full periodic cell.",tone:"status"});return}x(!1),Fe(!1),Te(!1),Ge(!1),_(!1),$e(null),xe(!1),ge(null),cr(p),Ye(!0)},[dt]),Vu=w.useCallback(p=>{if(!e)return;const b=Be.current+1;Be.current=b,De.current?.abort(),De.current=null,ze.current?.abort();const S=new AbortController;ze.current=S;const O={requestId:b,referenceLabel:p.reference.label,targetLabel:p.target.label};Ye(!1),cr(p.initialView),gt(p.initialView),ot(null),st(O),Le(!0),oe({type:"open-plot",plot:Xb(p.initialView,O)}),yd({datasetGeneration:e.dataset_generation??"",referenceIndices:p.reference.atomIndices,targetIndices:p.target.atomIndices,frameStart:p.frameStart,frameStop:p.frameStop,frameStep:p.frameStep,bins:p.bins,rMax:p.rMax},S.signal).then(D=>{Be.current!==b||S.signal.aborted||(Le(!1),ot(D),oe({type:"update-plot",plot:Tc(D,p.initialView,O)}))}).catch(D=>{if(!(Be.current!==b||S.signal.aborted)){if(Le(!1),ot(null),st(null),oe({type:"close-plot"}),D instanceof ct){Ze();return}Z({message:`Pair analysis unavailable · ${Ke(D)}`,tone:"error"})}})},[e,Ze]),ba=w.useCallback(p=>{gt(p),!(!Qt||!Tn)&&oe({type:"update-plot",plot:Tc(Qt,p,Tn)})},[Tn,Qt]);w.useEffect(()=>()=>{De.current?.abort(),wt.current?.abort(),ze.current?.abort()},[]);const Ar=w.useCallback(()=>{!Ue||W||(Fe(!1),Te(!1),_(!1),$e(null),Gn({width:2400,height:1800,format:"png",dpi:300,background:{kind:"solid",color:"#ffffff"},periodicContext:Ec(R,_e)}))},[Ue,Gn,_e,R.mode,R.wrap,W]),xs=w.useCallback(()=>{!Ue||W||(x(!1),Fe(!1),Te(!1),_(!1),$e(null),Wr(p=>({...p,periodicContext:Ec(R,_e)})),Ge(!0))},[Ue,_e,R,W]),Uu=w.useCallback(()=>{!Ue||W||(Ge(!1),Gn({...vr(Tt),transparent:Tt.background.kind==="transparent",annotations:Ds(Pt)}))},[Ue,Gn,Pt,Tt,W]),ya=w.useCallback(()=>{const p=Qr.current;if(!e||!u||u.index!==c||(u.data.header.coordinates==="unwrapped"?"unwrapped":"source")!==nn||!p)throw new Error("The current frame is not ready");const b=km(e);if(b.path.includes("pqviewer-upload-"))throw new Error("Open the source from disk before saving a reusable recipe");const S=u.data.header.frame_key;if(!S)throw new Error("The current frame has no stable source key");return Ro({schema:"pqviewer.figure",schema_version:1,source:b,frame:{index:u.index,key:S,fingerprint:Rs(e,u.data)},scene:{presentation:R,selection:{atoms:ho(I),intent:Q,minimumImage:q},vectors:{forceScale:ur,velocityScale:dr}},camera:p.captureCamera(),output:vr(Tt),annotations:Ds(Pt)})},[Pt,Tt,ur,c,u,e,q,R,I,Q,dr]),ws=w.useCallback(()=>{try{const p=ya();cn(new Blob([Mm(p)],{type:"application/json;charset=utf-8"}),Vb(e?.name)),Z({message:"Figure recipe saved",tone:"status"})}catch(p){Z({message:`Recipe unavailable · ${Ke(p)}`,tone:"error"})}},[ya,e?.name]),As=w.useCallback(()=>{!e||W||(Fe(!1),Te(!1),sa.current?.click())},[e,W]),qu=w.useCallback(async p=>{if(e)try{if(p.size>fb)throw new Error("Figure recipe is too large");const b=Sm(await p.text());ro(b,e)}catch(b){const S=Ke(b);yt(S),Z({message:`Recipe unavailable · ${S}`,tone:"error"})}},[e,ro]),Ss=Pt.some(p=>p.kind==="atom-label"),Gu=Pt.some(p=>p.kind==="legend"&&p.content==="elements"),Hu=Pt.find(p=>p.kind==="scale-bar")??null,Ku=w.useCallback(p=>{Ot(b=>[...b.filter(S=>S.kind!=="atom-label"),...p?I.map(S=>({kind:"atom-label",atom:{atom:S.atom,image:[...S.image]}})):[]])},[I]),Wu=w.useCallback(p=>{Ot(b=>[...b.filter(S=>S.kind!=="legend"||S.content!=="elements"),...p?[{kind:"legend",content:"elements",position:"top-right"}]:[]])},[]),Xu=w.useCallback(p=>{Ot(b=>[...b.filter(S=>S.kind!=="scale-bar"),...p?[{kind:"scale-bar",length:5,unit:"angstrom",position:"bottom-left"}]:[]])},[]),Yu=w.useCallback(p=>{!Number.isFinite(p)||p<=0||Ot(b=>b.map(S=>S.kind==="scale-bar"?{...S,length:p}:S))},[]),Qu=w.useCallback(p=>{Wr(b=>({...b,...p,background:p.background??b.background})),p.projection==="perspective"&&Ot(b=>b.filter(S=>S.kind!=="scale-bar"))},[]);w.useEffect(()=>{Ss&&Ot(p=>{const b=new Map(I.map(D=>[zs(D),D])),S=new Set,O=p.filter(D=>{if(D.kind!=="atom-label")return!0;const te=zs(D.atom);return!b.has(te)||S.has(te)?!1:(S.add(te),!0)});for(const D of I){const te=zs(D);S.has(te)||O.push({kind:"atom-label",atom:{atom:D.atom,image:[...D.image]}})}return O})},[Ss,I]),w.useEffect(()=>{const p={ready:Ue&&!On&&!W&&!lr,error:lr||null,export:async(b={})=>{if(lr)throw new Error(lr);if(!Ue||On)throw new Error("The saved figure is not ready");const S=b.transparent===void 0?Tt.background:b.transparent?{kind:"transparent"}:{kind:"solid",color:"#ffffff"};await Gn({...vr(Tt),...b,background:S,transparent:S.kind==="transparent",annotations:Ds(Pt)},!0)}};return window.pqviewerFigure=p,()=>{window.pqviewerFigure===p&&delete window.pqviewerFigure}},[Ue,Gn,Pt,lr,Tt,On,W]);const Ae=w.useCallback(p=>{if(p.mode==="polyhedra"&&!fe?.polyhedra){Z({message:`Polyhedra unavailable · ${fe?.polyhedraReason??er}`,tone:"status"});return}ee(b=>({...b,...p})),de("custom")},[fe]);w.useEffect(()=>{if(!e||!ve||!fe||le!=="auto")return;const p=`${e.name}:${e.topology.atom_count}`;Jr.current!==p&&(Jr.current=p,ee(b=>Bb("auto",b,Ve,wn,!1,fe)))},[fe,Ve,wn,ve,e,le]),w.useEffect(()=>{if(!A||W||!e||e.frame_count<2){Dn.current={key:"",requestTimeMs:null};return}if(h||u?.index!==c)return;const p=[e.dataset_generation??e.name,j,N,M].join(":");Dn.current.key!==p&&(Dn.current={key:p,requestTimeMs:null});const b=performance.now(),S=Dn.current.requestTimeMs??b,O=Au(b,Dn.current.requestTimeMs,j);let D=0;const te=()=>{const ye=tb(performance.now(),S,j,c,e.frame_count,{mode:N,direction:$,stride:M},{onStep:an=>{l(an.frameIndex),L(an.direction),an.continuePlaying||x(!1)},onPulse:()=>z(an=>an+1)});if(!ye.committed){D=window.setTimeout(te,Ac(ye.schedule.delayMs));return}Dn.current.requestTimeMs=ye.schedule.requestTimeMs};return D=window.setTimeout(te,Ac(O.delayMs)),()=>window.clearTimeout(D)},[c,h,u?.index,e,$,j,N,U,M,A,W]);const xa=w.useCallback(async p=>{if(p.length===0||W)return;const b=mr.current+1;mr.current=b,eo.current?.abort();const S=new AbortController;eo.current=S,na(!0),Z({message:"Opening files…",tone:"status"});try{const O=await xd(p,S.signal);if(b!==mr.current)return;Vn(O),pr.current?.postMessage({datasetGeneration:O.dataset_generation}),Z({message:`Opened ${O.name} · ${Cb(O.frame_count)}`,tone:"status"})}catch(O){if(b!==mr.current||S.signal.aborted)return;Z({message:Ke(O),tone:"error"})}finally{b===mr.current&&(eo.current=null,na(!1))}},[Vn,W]);w.useEffect(()=>()=>eo.current?.abort(),[]),w.useEffect(()=>{if(!xt||xt.tone==="error"||Xr||W)return;const p=window.setTimeout(()=>Z(null),Nb(xt.message));return()=>window.clearTimeout(p)},[xt,Xr,W]);const Ms=w.useCallback(()=>{if(Zr.current={prefix:null,at:0},bt)Fe(!1);else if(Zt)Te(!1);else if(ce)Ye(!1);else if(Jt)Ge(!1);else if(G)_(!1),requestAnimationFrame(()=>{document.querySelector(".timeline-options > summary")?.focus()});else if(ue)St(!0);else if(re)Mn(!0);else if(H.plot)br(!0);else if(I.length>0)J([]);else return!1;return!0},[Mn,br,St,bt,Jt,re,ce,G,I.length,Zt,H.plot,ue]),wa=w.useCallback(p=>{if(p==="commands"){qn();return}if(p==="first-frame"){x(!1),He(0);return}if(p==="last-frame"){x(!1),He((e?.frame_count??1)-1);return}const b={"next-frame":1,"next-ten-frames":10,"previous-frame":-1,"previous-ten-frames":-10}[p];x(!1),rn(b)},[e?.frame_count,He,qn,rn]);w.useEffect(()=>{const p=b=>{if(b.defaultPrevented||b.isComposing)return;const S=b.target,O=b.metaKey!==b.ctrlKey&&!b.altKey;if(O&&b.shiftKey&&b.key.toLowerCase()==="s"){b.preventDefault(),Ar();return}if(O&&!b.shiftKey&&b.key.toLowerCase()==="o"){b.preventDefault(),Un();return}if(O&&!b.shiftKey&&b.key.toLowerCase()==="k"){b.preventDefault(),qn();return}if(!W){if(it&&b.ctrlKey&&!b.metaKey&&!b.altKey&&!b.shiftKey&&b.key==="["){b.preventDefault(),Ms();return}if(b.key==="Escape"){Ms()&&b.preventDefault();return}if(!(b.metaKey||b.ctrlKey||b.altKey)&&!Zb(S)&&!(Jb(S)&&(b.key==="Enter"||b.code==="Space"))){if(b.key==="/"){b.preventDefault(),b.repeat||qn();return}if(b.key==="?"){b.preventDefault(),b.repeat||oo();return}if(!(bt||Zt)){if(it){const D=performance.now(),te=D-Zr.current.at<=750?Zr.current.prefix:null;if(b.repeat&&b.key==="g"){b.preventDefault();return}const ye=Pg(b.key,te);if(Zr.current={prefix:ye.prefix,at:ye.prefix?D:0},ye.prefix){b.repeat||b.preventDefault();return}if(ye.action){b.preventDefault(),wa(ye.action);return}}b.key.toLowerCase()==="v"&&fe&&!b.repeat?at&&ue==="view"?St(!0):on("view",!0):b.key.toLowerCase()==="w"&&fe?.water&&!b.repeat?Ae({water:R.water==="hide"?"show":"hide"}):b.key.toLowerCase()==="b"&&!b.repeat?Ae({mode:R.mode==="lines"?"ball-stick":"lines"}):b.key.toLowerCase()==="c"&&Ve&&!b.repeat?Ae({cell:!R.cell}):b.key.toLowerCase()==="f"&&wn&&!b.repeat?Ae({forces:!R.forces}):b.code==="Space"&&!b.repeat?(b.preventDefault(),(e?.frame_count??0)>1&&x(D=>!D)):b.key==="ArrowLeft"?(b.preventDefault(),x(!1),rn(-(b.shiftKey?10:1))):b.key==="ArrowRight"?(b.preventDefault(),x(!1),rn(b.shiftKey?10:1)):b.key==="Home"&&!b.repeat?(b.preventDefault(),x(!1),He(0)):b.key==="End"&&!b.repeat?(b.preventDefault(),x(!1),He((e?.frame_count??1)-1)):b.key.toLowerCase()==="m"&&Pe&&!b.repeat?(b.preventDefault(),yr()):b.key.toLowerCase()==="r"&&!b.repeat?(b.preventDefault(),Pn(D=>D+1)):["1","2","3","4"].includes(b.key)&&!b.shiftKey&&!b.repeat&&(b.preventDefault(),gr({1:"perspective",2:"xy",3:"xz",4:"yz"}[b.key]))}}}};return window.addEventListener("keydown",p),()=>window.removeEventListener("keydown",p)},[fe?.water,Pe,Ve,St,bt,Ms,wn,e?.frame_count,on,R,W,wa,gr,He,Zt,rn,yr,qn,Un,Ar,oo,Ae,ue,at,it]);const Zu=w.useCallback(p=>{const b=Gg(p);return b===null?null:{id:`select-within-${b}`,label:`Select within ${Xe(b)} Å of selection`,keywords:"nearby radius distance atoms",detail:I.length>0&&ie?"Run":"Select atoms first",disabled:I.length===0||!ie,run:()=>{ds(b),Fe(!1)}}},[ds,I.length,ie]),Ju=w.useMemo(()=>{const p=S=>()=>{S(),Fe(!1)};return[{id:"open",label:"Open files",keywords:"structure trajectory PQ ASE load",detail:zn.open,run:p(Un)},{id:"play",label:A?"Pause trajectory":"Play trajectory",keywords:"movie animation",detail:"Space",disabled:!Pe,run:p(()=>x(S=>!S))},{id:"previous",label:"Previous frame",keywords:"back step",detail:"←",disabled:!Pe||c===0,run:p(()=>{x(!1),rn(-1)})},{id:"next",label:"Next frame",keywords:"forward step",detail:"→",disabled:!Pe||c>=(e?.frame_count??1)-1,run:p(()=>{x(!1),rn(1)})},{id:"first",label:"First frame",keywords:"start beginning",detail:"Home",disabled:!Pe||c===0,run:p(()=>{x(!1),He(0)})},{id:"last",label:"Last frame",keywords:"end final",detail:"End",disabled:!Pe||c>=(e?.frame_count??1)-1,run:p(()=>{x(!1),He((e?.frame_count??1)-1)})},{id:"frame-bookmark",label:cs?"Remove frame bookmark":"Bookmark current frame",keywords:"trajectory mark remember frame",detail:"M",disabled:!we,run:p(yr)},{id:"frame-reference",label:"Set current frame as reference",keywords:"trajectory reference displacement compare",disabled:!we,run:p(gs)},...H.reference?[{id:"frame-reference-go",label:"Go to reference frame",keywords:"trajectory reference jump",detail:`Frame ${H.reference.index+1}`,run:p(()=>{x(!1),He(H.reference.index)})},{id:"frame-reference-clear",label:"Clear reference frame",keywords:"trajectory reference displacement",run:p(()=>oe({type:"clear-reference"}))}]:[],{id:"track-trail",label:H.tracking==="trail"?"Hide selected-atom trails":"Track selected atoms",keywords:"trajectory trail path motion history",disabled:!Je,run:p(()=>xr("trail"))},{id:"track-displacement",label:H.tracking==="displacement"?"Hide displacement vectors":"Show displacement from reference",keywords:"trajectory movement vector reference atoms",detail:H.reference?Cn(H.reference):"Set a reference first",disabled:!Je||!H.reference,run:p(()=>xr("displacement"))},...Bt.length>=2?[{id:"measurement-compare",label:"Compare pinned measurements",keywords:"trajectory plot distance angle dihedral lines",detail:`${Bt.length} lines`,run:p(ys)}]:[],...is.map(S=>({id:`plot-property-${S.name}`,label:`Plot ${S.label}`,keywords:`trajectory property scalar ${S.name}`,detail:S.unit,run:p(()=>bs(S))})),{id:"analysis-rdf",label:"Pair distribution",keywords:"trajectory rdf radial distribution structure analysis",detail:dt?"PQAnalysis":"Full periodic cell required",disabled:!dt,run:p(()=>wr("rdf"))},{id:"analysis-coordination",label:"Coordination",keywords:"trajectory coordination number rdf structure analysis",detail:dt?"PQAnalysis":"Full periodic cell required",disabled:!dt,run:p(()=>wr("coordination"))},{id:"fit",label:"Fit structure",keywords:"reset camera center",detail:"R",disabled:!e?.frame_count,run:p(()=>Pn(S=>S+1))},...["perspective","xy","xz","yz"].map((S,O)=>({id:`view-${S}`,label:S==="perspective"?"Perspective view":`${S.toUpperCase()} view`,keywords:"camera orientation axis",detail:S===yn?"Current":String(O+1),run:p(()=>gr(S))})),{id:"display",label:at&&ue==="view"?"Hide display controls":"Show display controls",keywords:"view representation settings",detail:"V",disabled:!fe,run:p(()=>at&&ue==="view"?St(!1):on("view"))},{id:"export",label:"Export figure",keywords:"render image png publication",detail:zn.export,disabled:!Ue,run:p(Ar)},{id:"figure-options",label:"Figure options",keywords:"render image tiff dpi transparent labels legend scale",disabled:!Ue,run:p(xs)},{id:"figure-save-recipe",label:"Save figure recipe",keywords:"reproducible view camera scene json",disabled:!Ue,run:p(ws)},{id:"figure-open-recipe",label:"Open figure recipe",keywords:"restore reproducible view camera scene json",disabled:!e,run:p(As)},...["ball-stick","spacefill","lines"].map(S=>({id:`mode-${S}`,label:`Representation · ${Iu(S)}`,keywords:"style atoms bonds",detail:S===R.mode?"Current":void 0,run:p(()=>Ae({mode:S}))})),...fe?.ribbon?[{id:"mode-ribbon",label:"Representation · Ribbon",keywords:"style protein backbone",detail:R.mode==="ribbon"?"Current":void 0,run:p(()=>Ae({mode:"ribbon"}))}]:[],{id:"mode-polyhedra",label:"Representation · Polyhedra",keywords:"style crystal coordination octahedra tetrahedra polygons",detail:fe?.polyhedra?R.mode==="polyhedra"?"Current":"Bond-derived":fe?.polyhedraReason??er,disabled:!fe?.polyhedra,discoverableWhenDisabled:!0,run:p(()=>Ae({mode:"polyhedra"}))},...fe?.water?[{id:"water",label:R.water==="hide"?"Show water":"Hide water",keywords:"solvent",detail:"W",run:p(()=>Ae({water:R.water==="hide"?"show":"hide"}))}]:[],...Ve?[{id:"cell",label:R.cell?"Hide cell":"Show cell",keywords:"box periodic pbc",detail:"C",run:p(()=>Ae({cell:!R.cell}))}]:[],...wn?[{id:"forces",label:R.forces?"Hide forces":"Show forces",keywords:"vectors arrows",detail:"F",run:p(()=>Ae({forces:!R.forces}))}]:[],...ns?[{id:"velocities",label:R.velocities?"Hide velocities":"Show velocities",keywords:"vectors arrows motion speed",run:p(()=>Ae({velocities:!R.velocities}))}]:[],...Ve?[["atom","Atom coordinates"],["molecule","Molecule coordinates"],["unwrapped","Unwrapped coordinates"],["none","Source coordinates"]].map(([S,O])=>({id:`wrap-${S}`,label:O,keywords:"periodic cell boundary coordinates",detail:R.wrap===S?"Current":void 0,run:p(()=>Ae({wrap:S}))})):[],...Ve?[{id:"cell-center-pq",label:"Center cell at PQ origin",keywords:"periodic centered cell origin zero",detail:tr(R.cellOrigin,[0,0,0])?"Current":void 0,run:p(()=>Ae({cellOrigin:[0,0,0]}))},{id:"cell-center-structure",label:"Center cell on structure",keywords:"periodic centered centroid atoms",disabled:An===null,detail:An&&tr(R.cellOrigin,An)?"Current":void 0,run:p(()=>{An&&Ae({cellOrigin:An})})},{id:"cell-center-selection",label:"Center cell on selection",keywords:"periodic centered centroid selected atoms",disabled:Sn===null,detail:Sn&&tr(R.cellOrigin,Sn)?"Current":I.length===0?"Select atoms first":void 0,run:p(()=>{Sn&&Ae({cellOrigin:Sn})})},...["a","b","c"].map((S,O)=>({id:`mirror-${S}`,label:`Mirror ${S}`,keywords:"periodic reflect flip cell axis",detail:R.mirror[O]?"On":"Off",run:p(()=>Ae({mirror:R.mirror.map((D,te)=>te===O?!D:D)}))})),{id:"repeat-3-3-1",label:"Repeat 3 × 3 × 1",keywords:"periodic supercell images replicate",disabled:!Ai([3,3,1],_e,e?.topology.atom_count??0),run:p(()=>Ae({images:ju([3,3,1],_e)}))},{id:"periodic-reset",label:"Reset periodic display",keywords:"periodic cell coordinates mirror repeat default",run:p(()=>Ae($c()))}]:[],...io.map(S=>({id:`select-element-${S}`,label:`Select ${hi[S]}`,keywords:`${Ut[S]} element atoms`,detail:Ut[S],run:p(()=>pa(S))})),...X?.waterAtoms.size?[{id:"select-water",label:"Select water",keywords:"solvent molecule H2O atoms",run:p(ga)}]:[],...B?[["atom","Select anchor atom"],["element","Select anchor element"],["molecule","Select anchor molecule"],["residue","Select anchor residue"],["component","Select connected component"]].map(([S,O])=>({id:`select-scope-${S}`,label:O,keywords:"selection scope expand atoms",disabled:S==="component"&&!ie?.hasConnectivity,run:p(()=>us(S))})):[],...se.map((S,O)=>({id:`selection-saved-${O}`,label:`Recall selection · ${S.name}`,keywords:"saved named atoms",detail:kr(S.selections.length),run:p(()=>fs(S))})),...Q==="measurement"&&I.length>=2&&I.length<=4?[{id:"pin-measurement",label:"Pin measurement",keywords:"selection distance angle dihedral keep",run:p(ms)}]:[],...Q==="measurement"&&Ve&&_e.some(Boolean)&&I.length>=2&&I.length<=4?[{id:"measurement-geometry",label:q?"Use displayed-image geometry":"Use minimum-image geometry",keywords:"selection measurement periodic distance image",detail:q?"Minimum image":"Displayed images",run:p(()=>pe(S=>!S))}]:[],...Y.map(S=>({id:`measurement-pinned-${S.id}`,label:`Recall pinned measurement ${S.id}`,keywords:"selection distance angle dihedral",detail:`${S.selections.length} atoms`,run:p(()=>hs(S))})),...Mt?[{id:"plot-measurement",label:re?"Hide measurement plot":"Plot measurement",keywords:"selection trajectory distance angle dihedral graph",run:p(()=>re?Mn(!1):ps())}]:[],...I.length===1&&sn!==null?[{id:"inspect-selection",label:"Inspect selected atom",keywords:"selection properties coordinates",run:p(()=>on("inspect"))}]:[],...I.length>0?[{id:"clear-selection",label:"Clear atom selection",keywords:"deselect atoms measurement",detail:"Esc",run:p(()=>J([]))}]:[],{id:"shortcuts",label:"Keyboard shortcuts",keywords:"help keys vim",detail:"?",run:p(oo)},{id:"vim",label:it?"Disable Vim navigation":"Enable Vim navigation",keywords:"keyboard linux hjkl",detail:it?"On":"Off",run:p(()=>bn(S=>!S))}].map(S=>({...S,run:()=>{S.run(),$u(O=>[S.id,...O.filter(D=>D!==S.id)].slice(0,8))}}))},[Pe,Mt,Ue,dt,fe,Ve,Mn,St,Bt,ys,cs,we,wn,c,e?.frame_count,e?.topology.atom_count,re,q,se,on,ms,A,Y,_e,R,is,fs,hs,gr,pa,us,ga,io,B,X,ie,Q,Sn,He,zn,Un,bs,wr,ws,xs,ps,As,Ar,oo,rn,H.reference,H.tracking,gs,xr,yr,Je,Ae,ns,yn,it,ue,at,sn,I.length,An]),ed=w.useMemo(()=>[...I.length===1&&sn!==null?["inspect-selection","clear-selection"]:[],...B?["select-scope-element","select-scope-molecule"]:[],...Q==="measurement"&&I.length>=2&&I.length<=4?["pin-measurement"]:[],...Mt?["plot-measurement"]:[],...we?["frame-bookmark","frame-reference"]:[],...Je?["track-trail"]:[],...H.reference&&Je?["track-displacement"]:[],...Bt.length>=2?["measurement-compare"]:[],...dt?["analysis-rdf"]:[],...Pe?["play","previous","next"]:[],"fit","display","export","figure-options"],[Pe,Mt,dt,Bt.length,we,sn,I.length,B,Q,H.reference,Je]),td=["workspace",at?"workbench-open":"workbench-closed",W?"is-rendering":"",Pe?"timeline-present":"timeline-absent",I.length>0?"selection-present":"",Jt?"figure-sheet-open":"",re||H.plot?"measurement-plot-open":"",G?"playback-options-open":""].filter(Boolean).join(" ");return f.jsxs("main",{className:"app-shell",onDragEnter:p=>{p.preventDefault(),!W&&(fr.current+=1,Yo(!0))},onDragOver:p=>p.preventDefault(),onDragLeave:p=>{p.preventDefault(),fr.current=Math.max(0,fr.current-1),fr.current===0&&Yo(!1)},onDrop:p=>{p.preventDefault(),fr.current=0,Yo(!1),!W&&xa([...p.dataTransfer.files])},children:[f.jsx("input",{ref:oa,className:"sr-only file-input",type:"file",tabIndex:-1,disabled:W,multiple:!0,onChange:p=>{W||(xa([...p.currentTarget.files??[]]),p.currentTarget.value="")}}),f.jsx("input",{ref:sa,className:"sr-only file-input",type:"file",accept:".pqfigure.json,.pqv.json,application/json",tabIndex:-1,disabled:W,onChange:p=>{const b=p.currentTarget.files?.[0];b&&!W&&qu(b),p.currentTarget.value=""}}),f.jsxs("div",{className:td,"aria-busy":n==="loading"||h||Xr||W,children:[f.jsxs("header",{className:"topbar",children:[f.jsxs("div",{className:"identity",title:e?.name||"Molecular trajectory",children:[f.jsx("img",{className:"identity-mark",src:"/pq-logo.png",alt:""}),f.jsxs("div",{children:[f.jsx("strong",{children:"PQViewer"}),f.jsx("span",{title:e?.name||"Molecular trajectory",children:e?.name||"Molecular trajectory"})]})]}),f.jsxs("div",{className:"topbar-tools",children:[e&&f.jsxs("div",{className:"scene-status",children:[f.jsxs("span",{children:[f.jsx("strong",{children:e.topology.atom_count.toLocaleString()})," ",e.topology.atom_count===1?"atom":"atoms"]}),f.jsxs("span",{children:[f.jsx("strong",{children:e.frame_count.toLocaleString()})," ",e.frame_count===1?"frame":"frames"]}),Ve&&f.jsxs("span",{children:["PBC ",f.jsx("strong",{children:_e.map((p,b)=>p?"abc"[b]:"").join("")||"off"})]})]}),f.jsxs("button",{className:"open-button",type:"button",disabled:W,"aria-keyshortcuts":"Meta+O Control+O",onClick:Un,children:[f.jsx(Se,{name:"folder"}),"Open"]}),f.jsxs("button",{className:"command-button",type:"button","aria-label":"Search commands","aria-keyshortcuts":"Meta+K Control+K","aria-haspopup":"dialog","aria-expanded":bt,disabled:W,title:`Search commands · ${zn.commands}`,onClick:qn,children:[f.jsx(Se,{name:"search"}),f.jsx("span",{children:"Search"}),f.jsx("kbd",{children:zn.commands})]}),f.jsxs("button",{ref:ia,className:"panel-button",type:"button","aria-label":at&&ue==="view"?"Hide display controls":"Show display controls","aria-controls":"workbench","aria-expanded":at&&ue==="view",disabled:W||!fe,onClick:()=>at&&ue==="view"?St(!1):on("view",!0),children:[f.jsx(Se,{name:"sliders"}),f.jsx("span",{children:"View"})]}),f.jsxs("div",{className:"figure-control",children:[f.jsxs("button",{ref:aa,className:"render-button",type:"button",disabled:!Ue||W,"aria-keyshortcuts":"Meta+Shift+S Control+Shift+S",onClick:Ar,children:[f.jsx(Se,{name:"image"}),W?"Exporting…":"Figure"]}),f.jsx("button",{className:"figure-options-button",type:"button","aria-label":"Figure options","aria-controls":"figure-sheet","aria-expanded":Jt,disabled:!Ue||W,onClick:()=>Jt?Ge(!1):xs(),children:f.jsx(Se,{name:"more"})})]})]})]}),e&&e.frame_count>0&&fe&&f.jsx(xb,{busy:W,viewPreset:yn,onFit:()=>Pn(p=>p+1),onView:gr}),e&&e.frame_count>0?f.jsx(Cp,{ref:Qr,manifest:e,frame:ve,preparedTopology:ra?.manifest===e?ra.value:null,presentation:R,selectedAtoms:I,resetSignal:Kr,viewPreset:yn,viewSignal:ke,forceScale:ur,velocityScale:dr,trajectoryOverlays:rt,appearance:"light",onSelect:Tu,onSelectMany:Pu,onSceneInfo:Qo,onSelectionContext:ne}):f.jsx("div",{className:"canvas-field"}),e&&fe&&f.jsxs("aside",{ref:ca,className:ue==="inspect"?"workbench atom-card":"workbench",id:"workbench","aria-labelledby":"workbench-title",hidden:!at,tabIndex:-1,children:[f.jsxs("div",{className:"workbench-heading",children:[f.jsx("strong",{id:"workbench-title",children:ue==="view"?"View":ue==="summary"?"Selection":sn===null?"Atom":`${Zi(e,sn)} · ${sn+1}`}),f.jsx("button",{className:"icon-button",type:"button",disabled:W,onClick:()=>{St(!0)},"aria-label":"Close",children:f.jsx(Se,{name:"close"})})]}),f.jsxs("div",{className:"workbench-body",children:[ue==="view"&&f.jsx(yb,{presentation:R,capabilities:fe,cellAvailable:Ve,forceAvailable:wn,velocityAvailable:ns,pbc:_e,atomCount:e.topology.atom_count,structureCellOrigin:An,selectionCellOrigin:Sn,forceScale:ur,velocityScale:dr,onPresentation:Ae,onForceScale:ea,onVelocityScale:ta}),ue==="inspect"&&f.jsx(kb,{manifest:e,frame:ve,selectedAtom:sn,selectedPosition:Pb(ha,I.length-1),cellAvailable:Ve}),ue==="summary"&&f.jsx(Mb,{summary:Ou,uniqueAtoms:new Set(I.map(({atom:p})=>p)).size})]})]}),e&&Y.length>0&&ie&&f.jsx(Sb,{manifest:e,pins:Y,index:ie,cell:X?.cell??null,pbc:_e,activeId:Q==="measurement"?Y.find(p=>p.minimumImage===q&&_b(p.selections,I))?.id??null:null,onRestore:hs,onRemove:p=>he(b=>b.filter(S=>S.id!==p)),canCompare:Bt.length>=2,onCompare:ys}),e&&I.length>0&&!ce&&f.jsx(Ab,{manifest:e,selectedAtoms:I,displayedPositions:ha,cell:X?.cell??null,pbc:_e,selectionFormula:os,namedSelections:se,selectionAnchor:B,connectivityAvailable:!!ie?.hasConnectivity,minimumImage:q,measurementEnabled:Q==="measurement",canPlot:Mt,plotOpen:re,trackingAvailable:Je,trackingMode:H.tracking,analysisAvailable:dt,onMinimumImage:()=>pe(p=>!p),onPlot:()=>re?Mn(!1):ps(),onClear:()=>{J([]),v("measurement"),K(null),Mn(!1)},onScope:us,onWithin:ds,onSave:Du,onRecall:fs,onRemoveSaved:zu,onPin:ms,onTracking:xr,onAnalyze:()=>wr("rdf"),onDetails:()=>at&&ue==="inspect"?St(!1):on("inspect",!0),onSummary:()=>at&&ue==="summary"?St(!1):on("summary",!0)}),e&&re&&Mt&&f.jsx(Ed,{title:be?.title??Rc(e,I),unit:qb(be?.unit??(I.length===2?"angstrom":"degree")),axisLabel:be?.axis.label??"Frame",axisUnit:be?.axis.unit,xValues:be?.xValues??ls,values:be?.values??Bu,loadedCount:be?.loadedCount??0,complete:be?.complete??!1,currentFrame:Lt,onFrame:p=>{x(!1),He(p)},onExportCsv:()=>{be?.complete&&cn(new Blob([em(be)],{type:"text/csv;charset=utf-8"}),Vs(e.name,be.kind,"csv"))},onExportSvg:()=>{be?.complete&&cn(new Blob([om(be)],{type:"image/svg+xml;charset=utf-8"}),Vs(e.name,be.kind,"svg"))},onExportPdf:()=>{if(!be?.complete)return;const p=sm(be),b=p.buffer.slice(p.byteOffset,p.byteOffset+p.byteLength);cn(new Blob([b],{type:"application/pdf"}),Vs(e.name,be.kind,"pdf"))}}),e&&H.plot&&f.jsx(Kc,{plot:H.plot,currentFrame:Lt,onFrame:H.plot.frameIndices?p=>{x(!1),He(p)}:void 0,onRestoreLine:p=>{p.selection&&(p.minimumImage!==void 0&&pe(p.minimumImage),et(p.selection,void 0,"measurement"))},headerActions:H.plot.kind==="rdf"?f.jsxs("div",{className:"rdf-view-toggle",role:"group","aria-label":"Pair analysis view",children:[f.jsx("button",{type:"button",className:Oe==="rdf"?"is-active":"","aria-pressed":Oe==="rdf",disabled:Qe,onClick:()=>ba("rdf"),children:"g(r)"}),f.jsx("button",{type:"button",className:Oe==="coordination"?"is-active":"","aria-pressed":Oe==="coordination",disabled:Qe,onClick:()=>ba("coordination"),children:"N(r)"})]}):void 0,onClose:()=>br(!1),onExportCsv:()=>{H.plot?.complete&&cn(new Blob([tm(H.plot)],{type:"text/csv;charset=utf-8"}),Us(e.name,H.plot,"csv"))},onExportSvg:()=>{H.plot?.complete&&cn(new Blob([nm(H.plot)],{type:"image/svg+xml;charset=utf-8"}),Us(e.name,H.plot,"svg"))},onExportPdf:()=>{if(!H.plot?.complete)return;const p=rm(H.plot),b=p.buffer.slice(p.byteOffset,p.byteOffset+p.byteLength);cn(new Blob([b],{type:"application/pdf"}),Us(e.name,H.plot,"pdf"))}}),e&&e.frame_count>1&&f.jsx(vb,{busy:W,frameCount:e.frame_count,frameIndex:c,displayedFrameIndex:Lt,playing:A,canPlay:Pe,frameError:m,frame:ve,fps:j,stride:M,mode:N,optionsOpen:G,bookmarks:H.bookmarks,reference:H.reference,currentBookmarked:cs,propertySeries:is,analysisAvailable:dt,trackingAvailable:Je,trackingMode:H.tracking,onFrame:p=>{x(!1),He(p)},onPlay:()=>Pe&&x(p=>!p),onFps:k,onStride:C,onOptionsOpen:p=>{_(p),p&&re&&Mn(!1)},onToggleBookmark:yr,onSetReference:gs,onClearReference:()=>oe({type:"clear-reference"}),onGoToReference:()=>{H.reference&&(x(!1),He(H.reference.index))},onProperty:bs,onAnalyze:wr,onTracking:xr,onMode:p=>{E(p),L(1)}}),n==="loading"&&f.jsx(Ls,{title:"Opening files",busy:!0}),n==="error"&&f.jsx(Ls,{title:"Trajectory unavailable",detail:o,alert:!0,action:"Try again",onAction:()=>a(p=>p+1)}),n==="ready"&&e?.frame_count===0&&f.jsx(Ls,{title:e.name==="No trajectory"?"Open files":"No frames found",detail:"Drop a structure, trajectory, or PQ run bundle.",action:"Open",onAction:Un}),xt&&f.jsxs("div",{className:`${Xr||W?"notice is-busy":"notice"}${xt.tone==="error"?" is-error":""}`,role:xt.tone==="error"?"alert":"status",title:xt.message,children:[f.jsx("span",{children:xt.message}),xt.tone==="error"&&f.jsx("button",{className:"notice-dismiss",type:"button","aria-label":"Dismiss message",onClick:()=>Z(null),children:f.jsx(Se,{name:"close"})})]}),Jt&&f.jsx(pb,{output:Tt,selectedCount:I.length,atomLabels:Ss,elementLegend:Gu,scaleBar:Hu,busy:W,onOutput:Qu,onAtomLabels:Ku,onElementLegend:Wu,onScaleBar:Xu,onScaleBarLength:Yu,onExport:Uu,onSaveRecipe:ws,onOpenRecipe:As,onClose:()=>Ge(!1)}),e&&f.jsx(Ig,{open:ce,frameCount:e.frame_count,options:as,defaultReferenceId:as[0]?.id,initialView:_t,onRun:Vu,onClose:()=>Ye(!1)}),_u&&f.jsx(wb,{replacing:!!e}),bt&&f.jsx(gb,{actions:Ju,contextIds:ed,recentIds:Eu,resolveAction:Zu,onClose:()=>Fe(!1)}),Zt&&f.jsx(bb,{shortcutLabels:zn,vimMode:it,onVimMode:bn,onClose:()=>Te(!1)})]})]})}function hb(e,t,n){return e==="polyhedra"&&t===!1&&!n}function Qi(e,t){w.useEffect(()=>{const n=e.current;if(!n)return;const r=document.activeElement instanceof HTMLElement?document.activeElement:null,o=n.parentElement,s=o?.parentElement?[...o.parentElement.children].filter(d=>d instanceof HTMLElement&&d!==o).map(d=>({element:d,inert:d.inert})):[];s.forEach(({element:d})=>{d.inert=!0});const i=()=>[...n.querySelectorAll('button:not([disabled]), input:not([disabled]):not([tabindex="-1"]), select:not([disabled]), textarea:not([disabled]), [href], [tabindex]:not([tabindex="-1"])')].filter(d=>d.offsetParent!==null),a=()=>(t?.current??i()[0]??n).focus(),c=requestAnimationFrame(a),l=d=>{n.contains(d.target)||a()},u=d=>{if(d.key!=="Tab")return;const m=i();if(m.length===0){d.preventDefault(),n.focus();return}const g=m[0],h=m[m.length-1];d.shiftKey&&(document.activeElement===g||!n.contains(document.activeElement))?(d.preventDefault(),h.focus()):!d.shiftKey&&document.activeElement===h&&(d.preventDefault(),g.focus())};return document.addEventListener("focusin",l),n.addEventListener("keydown",u),()=>{cancelAnimationFrame(c),document.removeEventListener("focusin",l),n.removeEventListener("keydown",u),s.forEach(({element:d,inert:m})=>{d.inert=m}),vu(r)}},[])}function vu(e){if(!e?.isConnected)return;if(!e.matches(":disabled")){e.focus();return}const t=new MutationObserver(()=>{!e.isConnected||e.matches(":disabled")||(window.clearTimeout(n),t.disconnect(),e.focus())}),n=window.setTimeout(()=>t.disconnect(),3e4);t.observe(e,{attributes:!0,attributeFilter:["disabled"]})}function pb({output:e,selectedCount:t,atomLabels:n,elementLegend:r,scaleBar:o,busy:s,onOutput:i,onAtomLabels:a,onElementLegend:c,onScaleBar:l,onScaleBarLength:u,onExport:d,onSaveRecipe:m,onOpenRecipe:g,onClose:h}){const y=w.useRef(null);Qi(y);const A=[{label:"Landscape",width:2400,height:1800},{label:"Square",width:2400,height:2400},{label:"Wide",width:3200,height:1800}];return f.jsx("div",{className:"figure-sheet-backdrop",onPointerDown:x=>x.target===x.currentTarget&&h(),children:f.jsxs("aside",{ref:y,className:"figure-sheet export-sheet",id:"figure-sheet",role:"dialog","aria-modal":"true","aria-label":"Figure options",tabIndex:-1,children:[f.jsxs("header",{className:"export-heading",children:[f.jsxs("div",{children:[f.jsx("strong",{children:"Figure"}),f.jsxs("span",{children:[e.width.toLocaleString()," × ",e.height.toLocaleString()," px · ",Xe(e.dpi)," DPI"]})]}),f.jsx("button",{className:"icon-button",type:"button",onClick:h,"aria-label":"Close figure options",children:f.jsx(Se,{name:"close"})})]}),f.jsxs("div",{className:"export-body",children:[f.jsxs("section",{className:"figure-section",children:[f.jsx("span",{className:"figure-section-label",children:"Size"}),f.jsx("div",{className:"figure-presets",role:"group","aria-label":"Figure size preset",children:A.map(x=>f.jsx("button",{type:"button",className:e.width===x.width&&e.height===x.height?"is-active":"","aria-pressed":e.width===x.width&&e.height===x.height,onClick:()=>i({width:x.width,height:x.height}),children:x.label},x.label))}),f.jsxs("div",{className:"figure-number-grid",children:[f.jsxs("label",{children:[f.jsx("span",{children:"Width"}),f.jsx("input",{type:"number",min:"1",max:"8192",step:"1",value:e.width,onChange:x=>i({width:Number(x.currentTarget.value)})})]}),f.jsxs("label",{children:[f.jsx("span",{children:"Height"}),f.jsx("input",{type:"number",min:"1",max:"8192",step:"1",value:e.height,onChange:x=>i({height:Number(x.currentTarget.value)})})]}),f.jsxs("label",{children:[f.jsx("span",{children:"DPI"}),f.jsx("input",{type:"number",min:"1",max:"2400",step:"1",value:e.dpi,onChange:x=>i({dpi:Number(x.currentTarget.value)})})]})]})]}),f.jsxs("section",{className:"figure-section",children:[f.jsx("span",{className:"figure-section-label",children:"File"}),f.jsx("div",{className:"figure-choice-row",role:"group","aria-label":"Figure format",children:["png","tiff"].map(x=>f.jsx("button",{type:"button",className:e.format===x?"is-active":"","aria-pressed":e.format===x,onClick:()=>i({format:x}),children:x==="png"?"PNG":"TIFF"},x))}),f.jsxs("div",{className:"figure-choice-row",role:"group","aria-label":"Figure background",children:[f.jsx("button",{type:"button",className:e.background.kind==="solid"?"is-active":"","aria-pressed":e.background.kind==="solid",onClick:()=>i({background:{kind:"solid",color:"#ffffff"}}),children:"White"}),f.jsx("button",{type:"button",className:e.background.kind==="transparent"?"is-active":"","aria-pressed":e.background.kind==="transparent",onClick:()=>i({background:{kind:"transparent"}}),children:"Transparent"})]})]}),f.jsxs("section",{className:"figure-section",children:[f.jsx("span",{className:"figure-section-label",children:"Camera"}),f.jsx("div",{className:"figure-choice-row",role:"group","aria-label":"Figure projection",children:["orthographic","perspective"].map(x=>f.jsx("button",{type:"button",className:e.projection===x?"is-active":"","aria-pressed":e.projection===x,onClick:()=>i({projection:x}),children:x==="orthographic"?"Orthographic":"Perspective"},x))})]}),f.jsxs("section",{className:"figure-section",children:[f.jsx("span",{className:"figure-section-label",children:"Annotations"}),f.jsxs("label",{className:"figure-toggle",children:[f.jsxs("span",{children:[f.jsx("strong",{children:"Selected atom labels"}),f.jsx("small",{children:t>0?`${t} selected`:"Select atoms first"})]}),f.jsx("input",{type:"checkbox",checked:n,disabled:t===0,onChange:x=>a(x.currentTarget.checked)})]}),f.jsxs("label",{className:"figure-toggle",children:[f.jsxs("span",{children:[f.jsx("strong",{children:"Element legend"}),f.jsx("small",{children:"Visible elements"})]}),f.jsx("input",{type:"checkbox",checked:r,onChange:x=>c(x.currentTarget.checked)})]}),f.jsxs("label",{className:"figure-toggle",children:[f.jsxs("span",{children:[f.jsx("strong",{children:"Scale bar"}),f.jsx("small",{children:"Orthographic figures"})]}),f.jsx("input",{type:"checkbox",checked:!!o,disabled:e.projection!=="orthographic",onChange:x=>l(x.currentTarget.checked)})]}),o&&f.jsxs("label",{className:"figure-scale-length",children:[f.jsx("span",{children:"Length"}),f.jsx("input",{type:"number",min:"0.0001",step:"any",value:o.length,onChange:x=>u(Number(x.currentTarget.value))}),f.jsx("span",{children:"Å"})]})]}),f.jsxs("section",{className:"figure-section figure-recipe-actions",children:[f.jsx("span",{className:"figure-section-label",children:"Recipe"}),f.jsx("p",{children:"Save this source, frame, scene, and camera as one reproducible view."}),f.jsxs("div",{children:[f.jsx("button",{type:"button",onClick:g,children:"Open"}),f.jsx("button",{type:"button",onClick:m,children:"Save"})]})]})]}),f.jsxs("footer",{className:"export-footer",children:[f.jsx("button",{type:"button",onClick:h,children:"Cancel"}),f.jsx("button",{className:"primary",type:"button",disabled:s,onClick:d,children:s?"Exporting…":`Export ${e.format==="tiff"?"TIFF":"PNG"}`})]})]})})}function gb({actions:e,contextIds:t,recentIds:n,resolveAction:r,onClose:o}){const[s,i]=w.useState(""),[a,c]=w.useState(0),l=w.useRef(null),u=w.useRef(null),d=w.useRef([]),m=w.useMemo(()=>{const h=Cd(e,s,{contextIds:t,recentIds:n}),y=r?.(s)??null;return y?[y,...h.filter(A=>A.id!==y.id)]:h},[e,t,s,n,r]);Qi(u,l),w.useEffect(()=>c(0),[m]),w.useEffect(()=>{d.current[a]?.scrollIntoView({block:"nearest"})},[a,m]);const g=h=>{m.length!==0&&c(y=>(y+h+m.length)%m.length)};return f.jsx("div",{className:"command-backdrop",onPointerDown:h=>h.target===h.currentTarget&&o(),children:f.jsxs("section",{ref:u,className:"command-palette",role:"dialog","aria-modal":"true","aria-label":"Commands",tabIndex:-1,children:[f.jsxs("label",{className:"command-search",children:[f.jsx(Se,{name:"search"}),f.jsx("input",{ref:l,value:s,placeholder:"Search commands","aria-label":"Search commands",role:"combobox","aria-autocomplete":"list","aria-controls":"command-results","aria-expanded":"true","aria-activedescendant":m[a]?`command-${m[a].id}`:void 0,onChange:h=>i(h.target.value),onKeyDown:h=>{h.key==="ArrowDown"?(h.preventDefault(),g(1)):h.key==="ArrowUp"?(h.preventDefault(),g(-1)):h.key==="Enter"&&(h.preventDefault(),m[a]?.disabled||m[a]?.run())}}),f.jsx("kbd",{children:"esc"})]}),f.jsxs("div",{className:"command-results",id:"command-results",role:"listbox",children:[m.map((h,y)=>f.jsxs("button",{ref:A=>{d.current[y]=A},id:`command-${h.id}`,type:"button",role:"option","aria-selected":y===a,"aria-disabled":h.disabled||void 0,className:y===a?"is-active":"",onPointerMove:()=>c(y),onClick:()=>{h.disabled||h.run()},children:[f.jsx("span",{children:h.label}),h.detail&&(h.disabled?f.jsx("small",{children:h.detail}):f.jsx("kbd",{children:h.detail}))]},h.id)),m.length===0&&f.jsx("p",{children:"No commands found"})]})]})})}function bb({shortcutLabels:e,vimMode:t,onVimMode:n,onClose:r}){const o=w.useRef(null);Qi(o);const s=[{title:"Trajectory",items:[["← / →","Previous / next frame"],["Shift ← / →","Move ten frames"],["Home / End","First / last frame"],["Space","Play / pause"],["M","Bookmark frame"]]},{title:"View",items:[["R","Fit structure"],["1 / 2 / 3 / 4","3D / XY / XZ / YZ"],["↑ / ↓","Browse atoms"],["Enter","Toggle atom"],["V","View controls"],["B","Bonds / lines"],["C / F / W","Cell / forces / water"]]},{title:"Workspace",items:[[e.commands,"Search commands"],[e.open,"Open files"],[e.export,"Export figure"],["? / Esc","Shortcuts / close"]]}],i=[["l / h","Next / previous frame"],["L / H","Forward / back ten"],["gg / G","First / last frame"],[":","Search commands"],["Ctrl [","Close surface"]];return f.jsx("div",{className:"command-backdrop shortcut-backdrop",onPointerDown:a=>a.target===a.currentTarget&&r(),children:f.jsxs("section",{ref:o,className:"shortcut-panel",role:"dialog","aria-modal":"true","aria-label":"Keyboard shortcuts",tabIndex:-1,children:[f.jsxs("div",{className:"shortcut-heading",children:[f.jsxs("div",{children:[f.jsx("strong",{children:"Keyboard shortcuts"}),f.jsx("span",{children:"Everything remains available with the mouse."})]}),f.jsx("button",{className:"icon-button",type:"button",onClick:r,"aria-label":"Close keyboard shortcuts",children:f.jsx(Se,{name:"close"})})]}),f.jsx("div",{className:"shortcut-groups",children:s.map(a=>f.jsxs("section",{children:[f.jsx("h3",{children:a.title}),a.items.map(([c,l])=>f.jsxs("div",{className:"shortcut-row",children:[f.jsx("kbd",{children:c}),f.jsx("span",{children:l})]},`${c}:${l}`))]},a.title))}),f.jsxs("section",{className:t?"vim-shortcuts is-active":"vim-shortcuts",children:[f.jsxs("div",{className:"vim-heading",children:[f.jsxs("div",{children:[f.jsx("strong",{children:"Vim navigation"}),f.jsx("span",{children:"Optional; standard shortcuts stay active."})]}),f.jsx("button",{type:"button",role:"switch","aria-label":"Vim navigation","aria-checked":t,onClick:()=>n(!t),children:f.jsx("i",{})})]}),t&&f.jsx("div",{className:"vim-shortcut-grid",children:i.map(([a,c])=>f.jsxs("div",{className:"shortcut-row",children:[f.jsx("kbd",{children:a}),f.jsx("span",{children:c})]},`${a}:${c}`))})]})]})})}function yb({presentation:e,capabilities:t,cellAvailable:n,forceAvailable:r,velocityAvailable:o,pbc:s,atomCount:i,structureCellOrigin:a,selectionCellOrigin:c,forceScale:l,velocityScale:u,onPresentation:d,onForceScale:m,onVelocityScale:g}){const h=[{mode:"ball-stick",available:!0},{mode:"spacefill",available:!0},{mode:"lines",available:!0},...t.ribbon?[{mode:"ribbon",available:!0}]:[],...n||t.polyhedra?[{mode:"polyhedra",available:t.polyhedra,reason:t.polyhedraReason}]:[]],y="polyhedra-requirement",A=Eb(e.images,s),x=Math.min(Tr,Math.max(1,Math.floor(Yn/Math.max(1,i)))),j=tr(e.cellOrigin,[0,0,0]),k=!j&&!!(c&&tr(e.cellOrigin,c)),M=!j&&!k&&!!(a&&tr(e.cellOrigin,a)),C=(N,E)=>{const $=[...A];$[N]=Math.max(1,Math.min(5,Math.round(E))),Ai($,s,i)&&d({images:ju($,s)})};return f.jsxs("div",{className:"scene-panel",children:[f.jsxs("section",{className:"workbench-section",children:[f.jsx("span",{className:"section-label",children:"Representation"}),f.jsx("div",{className:"segmented-options representation-options",children:h.map(({mode:N,available:E,reason:$})=>f.jsx("button",{type:"button",className:e.mode===N?"is-active":"","aria-pressed":e.mode===N,"aria-describedby":!E&&N==="polyhedra"?y:void 0,disabled:!E,title:E?void 0:$,onClick:()=>d({mode:N}),children:Iu(N)},N))}),n&&!t.polyhedra&&f.jsxs("span",{className:"capability-note",id:y,children:["Polyhedra · ",t.polyhedraReason]})]}),(t.water||n||r||o)&&f.jsxs("section",{className:"workbench-section display-toggles",children:[f.jsx("span",{className:"section-label",children:"Overlays"}),t.water&&f.jsx(bo,{label:"Water",checked:e.water!=="hide",onChange:N=>d({water:N?"show":"hide"})}),n&&f.jsx(bo,{label:"Cell",checked:e.cell,onChange:N=>d({cell:N})}),r&&f.jsx(bo,{label:"Forces",checked:e.forces,onChange:N=>d({forces:N})}),r&&e.forces&&f.jsx(Fc,{label:"Force scale",value:l,onChange:m}),o&&f.jsx(bo,{label:"Velocities",checked:e.velocities,onChange:N=>d({velocities:N})}),o&&e.velocities&&f.jsx(Fc,{label:"Velocity scale",value:u,onChange:g})]}),n&&f.jsxs("section",{className:"workbench-section",children:[f.jsx("span",{className:"section-label",children:"Periodic system"}),f.jsx("span",{className:"periodic-control-label",children:"Coordinates"}),f.jsx("div",{className:"segmented-options periodic-coordinate-options",children:[["atom","Atoms"],["molecule","Molecules"],["unwrapped","Unwrapped"]].map(([N,E])=>f.jsx("button",{type:"button",className:e.wrap===N?"is-active":"","aria-pressed":e.wrap===N,onClick:()=>d({wrap:N}),children:E},N))}),f.jsx("span",{className:"periodic-control-label",children:"Center cell"}),f.jsxs("div",{className:"segmented-options periodic-center-options",children:[f.jsx("button",{type:"button",className:j?"is-active":"","aria-pressed":j,onClick:()=>d({cellOrigin:[0,0,0]}),children:"PQ"}),f.jsx("button",{type:"button",className:M?"is-active":"","aria-pressed":M,disabled:!a,onClick:()=>{a&&d({cellOrigin:a})},children:"Structure"}),f.jsx("button",{type:"button",className:k?"is-active":"","aria-pressed":k,disabled:!c,title:c?"Center on the selected atoms":"Select atoms first",onClick:()=>{c&&d({cellOrigin:c})},children:"Selection"})]}),f.jsxs("div",{className:"periodic-inline-control",children:[f.jsx("span",{className:"periodic-control-label",children:"Mirror"}),f.jsx("div",{className:"periodic-axis-options","aria-label":"Mirror cell axes",children:["a","b","c"].map((N,E)=>f.jsx("button",{type:"button",className:e.mirror[E]?"is-active":"","aria-label":`Mirror ${N}`,"aria-pressed":e.mirror[E],onClick:()=>d({mirror:e.mirror.map(($,L)=>L===E?!$:$)}),children:N},N))})]}),f.jsxs("div",{className:"periodic-repeat-heading",children:[f.jsx("span",{className:"periodic-control-label",children:"Repeat"}),f.jsxs("span",{children:[A.reduce((N,E)=>N*E,1)," / ",x," cells"]})]}),f.jsx("div",{className:"periodic-repeat-grid",children:["a","b","c"].map((N,E)=>{const $=A[E],L=[...A];L[E]=$+1;const U=s[E],z=Ai(L,s,i),G=L.reduce((R,ee)=>R*ee,1),_=U?$>=5?"Maximum 5 repeats":z?`Repeat ${N}`:i*G>Yn?`${Yn.toLocaleString()} atom display limit`:`${Tr} cell display limit`:`${N} is not periodic`;return f.jsxs("div",{className:U?"periodic-repeat-row":"periodic-repeat-row is-disabled",children:[f.jsx("span",{children:N}),f.jsx("button",{type:"button","aria-label":`Decrease ${N} repeats`,disabled:!U||$<=1,onClick:()=>C(E,$-1),children:"−"}),f.jsxs("output",{"aria-label":`${N} repeats`,children:[$,"×"]}),f.jsx("button",{type:"button","aria-label":`Increase ${N} repeats`,disabled:!U||$>=5||!z,title:_,onClick:()=>C(E,$+1),children:"+"})]},N)})})]})]})}function Fc({label:e,value:t,onChange:n}){return f.jsxs("label",{className:"vector-scale-row",children:[f.jsx("span",{children:e}),f.jsx("input",{type:"range",min:.1,max:3,step:.1,value:t,onChange:r=>n(Number(r.target.value))}),f.jsxs("output",{children:[t.toFixed(1),"×"]})]})}function bo({label:e,checked:t,disabled:n=!1,onChange:r}){return f.jsxs("div",{className:n?"toggle-row is-disabled":"toggle-row",children:[f.jsx("span",{children:e}),f.jsx("button",{type:"button",role:"switch","aria-label":e,"aria-checked":t,disabled:n,onClick:()=>r(!t),children:f.jsx("i",{})})]})}function xb({busy:e,viewPreset:t,onFit:n,onView:r}){return f.jsxs("div",{className:"canvas-controls",role:"toolbar","aria-label":"Camera controls",children:[f.jsx("button",{type:"button",disabled:e,onClick:n,children:"Fit"}),[["perspective","3D"],["xy","XY"],["xz","XZ"],["yz","YZ"]].map(([o,s])=>f.jsx("button",{type:"button",disabled:e,className:t===o?"is-active":"","aria-pressed":t===o,onClick:()=>r(o),children:s},o))]})}function wb({replacing:e}){return f.jsx("div",{className:"drop-overlay",role:"status",children:f.jsxs("div",{children:[f.jsx(Se,{name:"folder"}),f.jsx("strong",{children:e?"Replace data":"Open files"}),f.jsx("span",{children:"Structures, trajectories, and PQ run bundles"})]})})}function Ab({manifest:e,selectedAtoms:t,displayedPositions:n,cell:r,pbc:o,selectionFormula:s,namedSelections:i,selectionAnchor:a,connectivityAvailable:c,minimumImage:l,measurementEnabled:u,canPlot:d,plotOpen:m,trackingAvailable:g,trackingMode:h,analysisAvailable:y,onMinimumImage:A,onPlot:x,onClear:j,onScope:k,onWithin:M,onSave:C,onRecall:N,onRemoveSaved:E,onPin:$,onTracking:L,onAnalyze:U,onDetails:z,onSummary:G}){const[_,R]=w.useState(""),[ee,le]=w.useState("3.0"),de=w.useRef(null);w.useEffect(()=>{const q=pe=>{const re=de.current;re?.open&&!re.contains(pe.target)&&(re.open=!1)};return document.addEventListener("pointerdown",q),()=>document.removeEventListener("pointerdown",q)},[]);const I=t.filter(({atom:q})=>q>=0&&qq),Q=!!(u&&r&&o.some(Boolean)&&I.length>=2&&I.length<=4),v=n&&n.length===I.length*3&&I.length===t.length?n:null,B=v&&u&&I.length>=2&&I.length<=4?Fu(v,Q&&l,r,o):null,K=I.map(q=>Ji(e,q));let X=J.length===1?K[0]:s?`${s} · ${J.length.toLocaleString()} atoms`:`${J.length.toLocaleString()} atoms`,ne="";B?.ok?(X=`${B.kind[0].toUpperCase()}${B.kind.slice(1)} · ${K.join("–")}`,ne=`${Xe(B.value)} ${B.unit==="angstrom"?"Å":"°"}`):J.length===1&&v?ne=[v[0],v[1],v[2]].map(q=>Xe(q)).join(" "):u&&J.length>1&&J.length<=4&&(ne=K.slice(0,4).join(" · "));const se=a?e.topology.atom_residue_index?.[a.atom]??-1:-1,P=()=>{de.current&&(de.current.open=!1)},Y=()=>{const q=Number(ee);!Number.isFinite(q)||q<=0||(M(q),P())},he=()=>{C(_)&&(R(""),P())};return f.jsxs("section",{className:"selection-bar","aria-label":"Atom selection",children:[f.jsxs("div",{className:"selection-readout",children:[f.jsx("strong",{title:X,children:X}),ne&&f.jsx("output",{children:ne})]}),Q?f.jsxs("button",{className:"measurement-mode",type:"button","aria-pressed":l,"aria-label":l?"Minimum image":"Displayed images",title:"Choose minimum-image or displayed-image geometry",onClick:A,children:[f.jsx("span",{className:"measurement-mode-full",children:l?"Minimum image":"Displayed images"}),f.jsx("span",{className:"measurement-mode-compact","aria-hidden":"true",children:l?"Min. image":"Images"})]}):I.length===1?f.jsx("span",{className:"selection-hint",children:"Shift-click or Shift-drag"}):null,f.jsxs("details",{ref:de,className:"selection-tools",onKeyDown:q=>{q.key!=="Escape"||!q.currentTarget.open||(q.preventDefault(),q.stopPropagation(),q.currentTarget.open=!1,q.currentTarget.querySelector("summary")?.focus())},children:[f.jsx("summary",{children:"Select"}),f.jsxs("div",{className:"selection-tools-popover",children:[f.jsxs("section",{children:[f.jsx("span",{children:"From anchor"}),f.jsx("div",{className:"selection-scope-grid",children:[["atom","Atom"],["element","Element"],["molecule","Molecule"],["residue","Residue"],["component","Component"]].map(([q,pe])=>{const re=q==="residue"&&se<0||q==="component"&&!c||q==="molecule"&&se<0&&!c;return f.jsx("button",{type:"button",disabled:re,onClick:()=>{k(q),P()},children:pe},q)})})]}),f.jsxs("section",{children:[f.jsx("label",{htmlFor:"selection-distance",children:"Within selection"}),f.jsxs("div",{className:"selection-input-row",children:[f.jsx("input",{id:"selection-distance",inputMode:"decimal",value:ee,"aria-label":"Distance in angstrom",onChange:q=>le(q.target.value),onKeyDown:q=>q.key==="Enter"&&Y()}),f.jsx("span",{children:"Å"}),f.jsx("button",{type:"button",onClick:Y,children:"Apply"})]})]}),f.jsxs("section",{children:[f.jsx("label",{htmlFor:"selection-name",children:"Save selection"}),f.jsxs("div",{className:"selection-input-row is-name",children:[f.jsx("input",{id:"selection-name",value:_,maxLength:80,placeholder:"e.g. active site",onChange:q=>R(q.target.value),onKeyDown:q=>q.key==="Enter"&&he()}),f.jsx("button",{type:"button",disabled:!_.trim(),onClick:he,children:"Save"})]})]}),i.length>0&&f.jsxs("section",{className:"saved-selections",children:[f.jsx("span",{children:"Saved"}),i.map(q=>f.jsxs("div",{children:[f.jsxs("button",{type:"button",onClick:()=>{N(q),P()},children:[f.jsx("span",{children:q.name}),f.jsx("small",{children:q.selections.length.toLocaleString()})]}),f.jsx("button",{type:"button","aria-label":`Delete ${q.name}`,onClick:()=>E(q.name),children:f.jsx(Se,{name:"close"})})]},q.name))]})]})]}),d&&f.jsx("button",{className:"selection-plot-button",type:"button","aria-pressed":m,onClick:x,children:m?"Hide plot":"Plot"}),u&&I.length>=2&&I.length<=4&&f.jsx("button",{className:"selection-pin-button",type:"button",onClick:$,children:"Pin"}),g&&f.jsx("button",{className:"selection-track-button",type:"button","aria-pressed":h!=="off",title:h==="displacement"?"Showing displacement from the reference frame":"Show the previous 50 frames",onClick:()=>L(h==="off"?"trail":"off"),children:h==="off"?"Track":"Stop"}),y&&(!u||I.length>4)&&f.jsx("button",{className:"selection-analyze-button",type:"button",onClick:U,children:"Analyze"}),I.length===1&&f.jsx("button",{type:"button",onClick:z,children:"Details"}),I.length>4&&f.jsx("button",{className:"selection-summary-button",type:"button",onClick:G,children:"Summary"}),f.jsx("button",{className:"icon-button",type:"button",onClick:j,"aria-label":"Clear selection",children:f.jsx(Se,{name:"close"})})]})}function Sb({manifest:e,pins:t,index:n,cell:r,pbc:o,activeId:s,onRestore:i,onRemove:a,canCompare:c,onCompare:l}){const u=w.useRef(null);return w.useEffect(()=>{const d=m=>{u.current?.open&&!u.current.contains(m.target)&&(u.current.open=!1)};return document.addEventListener("pointerdown",d),()=>document.removeEventListener("pointerdown",d)},[]),f.jsxs("details",{ref:u,className:"pinned-measurements",children:[f.jsxs("summary",{children:["Measurements · ",t.length]}),f.jsxs("section",{"aria-label":"Pinned measurements",children:[f.jsxs("header",{children:[f.jsx("strong",{children:"Measurements"}),c&&f.jsx("button",{type:"button",onClick:()=>{u.current&&(u.current.open=!1),l()},children:"Compare"})]}),f.jsx("div",{className:"pinned-measurements__list",children:t.map(d=>{const m=Tb(e,n,d,r,o);return f.jsxs("div",{children:[f.jsxs("button",{className:"selection-chip",type:"button","aria-pressed":s===d.id,onClick:()=>{i(d),u.current&&(u.current.open=!1)},children:[f.jsx("span",{children:m.title}),f.jsx("strong",{children:m.value})]}),f.jsx("button",{className:"pinned-measurement-remove",type:"button","aria-label":`Remove pinned ${m.title.toLowerCase()} · ${d.minimumImage?"minimum image":"displayed images"} · ${m.value}`,onClick:()=>a(d.id),children:f.jsx(Se,{name:"close"})})]},d.id)})})]})]})}function Mb({summary:e,uniqueAtoms:t}){return e?f.jsx("div",{className:"inspector-content selection-summary-panel",children:f.jsxs("section",{className:"readout-section",children:[f.jsx(ln,{label:"Formula",value:e.formula||"—"}),f.jsx(ln,{label:"Occurrences",value:e.count.toLocaleString()}),t!==e.count&&f.jsx(ln,{label:"Unique atoms",value:t.toLocaleString()}),f.jsx(_r,{label:"Cartesian centroid",values:e.centroid,offset:0,unit:"Å"}),f.jsx(_r,{label:"Extent",values:e.extent,offset:0,unit:"Å"})]})}):f.jsx("p",{className:"quiet-copy",children:"Selection geometry is unavailable."})}function kb({manifest:e,frame:t,selectedAtom:n,selectedPosition:r,cellAvailable:o}){const s=ae(t,["forces","force"]),i=ae(t,["velocities","velocity","vel"]),a=ae(t,["charges","charge"]),c=n!==null&&nA.index===g),y=Fb(e.topology.residue_ids,c);return f.jsx("div",{className:"inspector-content",children:c===null?f.jsx("p",{className:"quiet-copy",children:"Click an atom to inspect it."}):f.jsxs("section",{className:"readout-section atom-section",children:[f.jsx(ln,{label:"Element",value:l??"—"}),e.topology.atom_names?.[c]&&f.jsx(ln,{label:"Name",value:e.topology.atom_names[c]}),h&&f.jsx(ln,{label:"Residue",value:`${h.name??`Type ${h.type_id??"—"}`} · ${h.index+1}`}),!h&&y!==null&&f.jsx(ln,{label:"Residue ID",value:y}),r&&f.jsx(_r,{label:o?"Displayed cell position":"Displayed position",values:r,offset:0,unit:"Å"}),s&&f.jsx(_r,{label:"Force",values:s,offset:c*3,unit:u}),i&&f.jsx(_r,{label:"Velocity",values:i,offset:c*3,unit:d}),a&&a[c]!==void 0&&f.jsx(ln,{label:"Charge",value:Ob(Xe(a[c]),m)})]})})}function vb({busy:e,frameCount:t,frameIndex:n,displayedFrameIndex:r,playing:o,canPlay:s,frameError:i,frame:a,fps:c,stride:l,mode:u,optionsOpen:d,bookmarks:m,reference:g,currentBookmarked:h,propertySeries:y,analysisAvailable:A,trackingAvailable:x,trackingMode:j,onFrame:k,onPlay:M,onFps:C,onStride:N,onOptionsOpen:E,onToggleBookmark:$,onSetReference:L,onClearReference:U,onGoToReference:z,onProperty:G,onAnalyze:_,onTracking:R,onMode:ee}){const le=String(r+1).padStart(String(t).length,"0"),de=String(n+1).padStart(String(t).length,"0"),I=r===n?`${le} / ${t}`:`${le} → ${de}`,J=r===n?`${yo(r+1)} / ${yo(t)}`:`${yo(r+1)} → ${yo(n+1)}`,Q=jb(a);return f.jsx("section",{className:`timeline is-compact${e?" is-busy":""}`,"aria-label":"Trajectory controls",children:f.jsxs("div",{className:"transport-row",children:[f.jsxs("div",{className:"transport-buttons",children:[f.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(0),disabled:e||n===0,"aria-label":"First frame",children:f.jsx(Se,{name:"first"})}),f.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(n-1),disabled:e||n===0,"aria-label":"Previous frame",children:f.jsx(Se,{name:"back"})}),f.jsx("button",{type:"button",className:"play-button",onClick:M,disabled:e||!s,"aria-label":o?"Pause":"Play",children:f.jsx(Se,{name:o?"pause":"play"})}),f.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(n+1),disabled:e||n===t-1,"aria-label":"Next frame",children:f.jsx(Se,{name:"next"})}),f.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(t-1),disabled:e||n===t-1,"aria-label":"Last frame",children:f.jsx(Se,{name:"last"})})]}),f.jsxs("div",{className:"scrubber-shell",children:[f.jsxs("label",{className:"scrubber",children:[f.jsx("span",{className:"sr-only",children:"Frame"}),f.jsx("input",{type:"range",min:0,max:Math.max(t-1,0),value:n,disabled:e,onChange:v=>k(Number(v.target.value))})]}),(m.length>0||g)&&f.jsxs("div",{className:"trajectory-marker-rail",children:[m.map(v=>f.jsx("button",{type:"button",className:"trajectory-marker is-bookmark",style:{left:`${Pc(v.index,t)}%`},"aria-label":`Go to ${Cn(v)}`,title:Cn(v),onClick:()=>k(v.index)},`${v.key.source_id}:${v.key.segment_index}:${v.key.source_index}`)),g&&f.jsx("button",{type:"button",className:"trajectory-marker is-reference",style:{left:`${Pc(g.index,t)}%`},"aria-label":`Go to reference · ${Cn(g)}`,title:`Reference · ${Cn(g)}`,onClick:z})]})]}),!i&&f.jsxs("output",{className:"frame-counter","aria-label":r===n?`Frame ${r+1} of ${t}`:`Showing frame ${r+1}; loading frame ${n+1}`,children:[f.jsx("span",{className:"frame-counter-full",children:I}),f.jsx("span",{className:"frame-counter-compact","aria-hidden":"true",children:J})]}),Q&&f.jsx("span",{className:"frame-metadata",children:Q}),i&&f.jsxs("span",{className:"frame-error",title:i,"aria-label":"Frame unavailable",children:[f.jsx("span",{className:"frame-error-full",children:"Frame error"}),f.jsx("span",{className:"frame-error-compact","aria-hidden":"true",children:"Error"})]}),f.jsxs("details",{className:"timeline-options",open:d,onToggle:v=>E(v.currentTarget.open),children:[f.jsx("summary",{"aria-label":"Playback options",children:f.jsx(Se,{name:"more"})}),f.jsxs("div",{children:[f.jsx("span",{className:"section-label",children:"Frame"}),f.jsxs("div",{className:"timeline-action-list",children:[f.jsx("button",{type:"button",onClick:$,children:h?"Remove bookmark":"Bookmark frame"}),f.jsx("button",{type:"button",onClick:L,children:"Set as reference"}),g&&f.jsxs(f.Fragment,{children:[f.jsx("button",{type:"button",onClick:z,children:"Go to reference"}),f.jsx("button",{type:"button",onClick:U,children:"Clear reference"}),f.jsx("button",{type:"button",disabled:!x,onClick:()=>R("displacement"),children:j==="displacement"?"Hide displacement":"Show displacement"})]})]}),m.length>0&&f.jsxs(f.Fragment,{children:[f.jsx("span",{className:"section-label",children:"Bookmarks"}),f.jsx("div",{className:"timeline-action-list",children:m.map(v=>f.jsx("button",{type:"button",onClick:()=>k(v.index),children:Cn(v)},`bookmark-action:${v.key.source_id}:${v.key.segment_index}:${v.key.source_index}`))})]}),y.length>0&&f.jsxs(f.Fragment,{children:[f.jsx("span",{className:"section-label",children:"Plot"}),f.jsx("div",{className:"timeline-action-list",children:y.map(v=>f.jsx("button",{type:"button",onClick:()=>G(v),children:v.label},v.name))})]}),A&&f.jsxs(f.Fragment,{children:[f.jsx("span",{className:"section-label",children:"Pair analysis"}),f.jsxs("div",{className:"timeline-action-list",children:[f.jsx("button",{type:"button",onClick:()=>_("rdf"),children:"Pair distribution"}),f.jsx("button",{type:"button",onClick:()=>_("coordination"),children:"Coordination"})]})]}),f.jsx("span",{className:"section-label",children:"Playback"}),f.jsxs("label",{children:[f.jsx("span",{children:"Speed"}),f.jsx("select",{value:c,onChange:v=>C(Number(v.target.value)),children:[1,5,10,12,15,24,30,60].map(v=>f.jsxs("option",{value:v,children:[v," fps"]},v))})]}),f.jsxs("label",{children:[f.jsx("span",{children:"Stride"}),f.jsx("select",{value:l,onChange:v=>N(Number(v.target.value)),children:[1,2,5,10].map(v=>f.jsxs("option",{value:v,children:[v," frame",v===1?"":"s"]},v))})]}),f.jsx("div",{className:"segmented-options",children:[["once","Once"],["loop","Loop"],["rock","Rock"]].map(([v,B])=>f.jsx("button",{type:"button",className:u===v?"is-active":"","aria-pressed":u===v,onClick:()=>ee(v),children:B},v))})]})]})]})})}function jb(e){const t=_c(e,"step"),n=_c(e,"time"),r=t===null?"":`step ${Xe(t)}`,o=n===null?"":`t ${Xe(n)}`;return[r,o].filter(Boolean).join(" · ")}function yo(e){const t=Math.max(0,Math.round(e));if(t<1e4)return String(t);const[n,r]=t>=1e9?[1e9,"B"]:t>=1e6?[1e6,"M"]:[1e3,"k"],o=t/n;return`${Number(o.toFixed(o>=10?0:1))}${r}`}function Cb(e){const t=Number.isFinite(e)?Math.max(0,Math.floor(e)):0;return`${t.toLocaleString()} ${t===1?"frame":"frames"}`}function kr(e){return`${e.toLocaleString()} ${e===1?"atom":"atoms"}`}function Nb(e){return Math.min(1e4,Math.max(4200,e.length*70))}function Fb(e,t){if(t===null||!Number.isInteger(t)||t<0||!e||t>=e.length)return null;const n=e.some(o=>{const s=String(o).trim();return s!==""&&s!=="0"}),r=String(e[t]).trim();return n&&r!==""?r:null}function Ib(e){const t=e?.header.pbc;return Array.isArray(t)&&t.length===3?[!!t[0],!!t[1],!!t[2]]:Qc(e)}function Ic(e,t){const n=ae(e,["positions","position","coordinates","coords"]);return n?nf(e,Math.floor(n.length/3),t??null):null}function tr(e,t){return e.length>=3&&t.length>=3&&[0,1,2].every(n=>Math.abs(e[n]-t[n])<=1e-6)}function Eb(e,t){return e.min.map((n,r)=>t[r]?Math.max(1,Math.min(5,Math.round(Math.abs(e.max[r]-n)+1))):1)}function ju(e,t){const n=e.map((r,o)=>t[o]?Math.max(1,Math.min(5,Math.round(r))):1);return{min:n.map(r=>{const o=Math.floor((r-1)/2);return o===0?0:-o}),max:n.map(r=>Math.ceil((r-1)/2))}}function Ai(e,t,n){if(e.some(s=>!Number.isInteger(s)||s<1||s>5)||e.some((s,i)=>!t[i]&&s!==1))return!1;const r=e.reduce((s,i)=>s*i,1),o=Math.min(Tr,Math.max(1,Math.floor(Yn/Math.max(1,n))));return r<=o}function Ec(e,t){return t.some(Boolean)&&(e.wrap==="atom"||e.wrap==="unwrapped")&&e.mode!=="spacefill"&&e.mode!=="ribbon"}function $c(){return{wrap:"atom",images:{min:[0,0,0],max:[0,0,0]},cellOrigin:[0,0,0],mirror:[!1,!1,!1]}}function $b(e){return{atom:"Atom",element:"Element",molecule:"Molecule",residue:"Residue",component:"Connected component"}[e]}function Cu(e,t){return e.atom===t.atom&&e.image[0]===t.image[0]&&e.image[1]===t.image[1]&&e.image[2]===t.image[2]}function _b(e,t){return e.length===t.length&&e.every((n,r)=>Cu(n,t[r]))}function Nu(e,t){const n=new Float64Array(t.length*3);for(let r=0;r=e.count||n.has(o.atom)||(n.add(o.atom),r.push(e.atomicNumbers[o.atom]));return bu(r)}function Fu(e,t,n,r){const o=Math.floor(e.length/3);return ni(e,Array.from({length:o},(s,i)=>i),t&&n&&r.some(Boolean)?{mode:"minimum-image",cell:n,pbc:r}:{mode:"direct"})}function Tb(e,t,n,r,o){const s=n.selections.map(c=>Ji(e,c)),i=Nu(t,n.selections),a=i?Fu(i,n.minimumImage,r,o):null;return a?.ok?{title:`${a.kind[0].toUpperCase()}${a.kind.slice(1)} · ${s.join("–")}`,value:`${Xe(a.value)} ${a.unit==="angstrom"?"Å":"°"}`}:{title:s.join("–")||"Measurement",value:"—"}}function Pb(e,t){return!e||t<0||e.length<(t+1)*3?null:e.slice(t*3,t*3+3)}function _c(e,t){const n=e?.header[t];if(typeof n=="number"&&Number.isFinite(n))return n;const r=e?.header.scalars?.[t];return typeof r=="number"&&Number.isFinite(r)?r:null}function ln({label:e,value:t}){return f.jsxs("div",{className:"readout",children:[f.jsx("span",{children:e}),f.jsx("strong",{children:t})]})}function _r({label:e,values:t,offset:n,unit:r}){return f.jsxs("div",{className:"vector-readout",children:[f.jsx("span",{children:e}),f.jsxs("code",{children:[f.jsx("i",{children:"x"}),Xe(t[n]),f.jsx("i",{children:"y"}),Xe(t[n+1]),f.jsx("i",{children:"z"}),Xe(t[n+2]),r&&f.jsx("b",{children:r})]})]})}function Ls({title:e,detail:t,busy:n=!1,alert:r=!1,action:o,onAction:s}){return f.jsxs("div",{className:"centered-state",role:r?"alert":"status",children:[f.jsxs("div",{className:n?"state-orbit is-busy":"state-orbit","aria-hidden":"true",children:[f.jsx("i",{}),f.jsx("i",{}),f.jsx("b",{})]}),f.jsx("h1",{children:e}),t&&f.jsx("p",{children:t}),o&&s&&f.jsxs("button",{type:"button",onClick:s,children:[f.jsx(Se,{name:o==="Open"?"folder":"retry"}),o]})]})}function Se({name:e}){const t={fill:"none",stroke:"currentColor",strokeWidth:1.6,strokeLinecap:"round",strokeLinejoin:"round"};return f.jsxs("svg",{className:"icon",viewBox:"0 0 24 24","aria-hidden":"true",children:[e==="folder"&&f.jsx("path",{d:"M4 7.5h6l1.6 2H20v8.5H4V7.5Z",...t}),e==="image"&&f.jsxs(f.Fragment,{children:[f.jsx("rect",{x:"4",y:"5",width:"16",height:"14",rx:"2",...t}),f.jsx("circle",{cx:"9",cy:"10",r:"1.5",...t}),f.jsx("path",{d:"m6.5 17 4.2-4 2.6 2.4 2.2-2 2 1.8",...t})]}),e==="sliders"&&f.jsxs(f.Fragment,{children:[f.jsx("path",{d:"M5 7h5m4 0h5M5 17h3m4 0h7",...t}),f.jsx("circle",{cx:"12",cy:"7",r:"2",...t}),f.jsx("circle",{cx:"10",cy:"17",r:"2",...t})]}),e==="play"&&f.jsx("path",{d:"m9 7 7 5-7 5V7Z",fill:"currentColor"}),e==="pause"&&f.jsx(f.Fragment,{children:f.jsx("path",{d:"M9 7v10M15 7v10",...t,strokeWidth:"2"})}),e==="first"&&f.jsxs(f.Fragment,{children:[f.jsx("path",{d:"M7.5 7v10",...t}),f.jsx("path",{d:"m16 8-5 4 5 4",...t})]}),e==="back"&&f.jsx("path",{d:"m14.5 8-5 4 5 4",...t}),e==="next"&&f.jsx("path",{d:"m9.5 8 5 4-5 4",...t}),e==="last"&&f.jsxs(f.Fragment,{children:[f.jsx("path",{d:"M16.5 7v10",...t}),f.jsx("path",{d:"m8 8 5 4-5 4",...t})]}),e==="more"&&f.jsxs(f.Fragment,{children:[f.jsx("circle",{cx:"7",cy:"12",r:"1",fill:"currentColor"}),f.jsx("circle",{cx:"12",cy:"12",r:"1",fill:"currentColor"}),f.jsx("circle",{cx:"17",cy:"12",r:"1",fill:"currentColor"})]}),e==="search"&&f.jsxs(f.Fragment,{children:[f.jsx("circle",{cx:"10.5",cy:"10.5",r:"5.5",...t}),f.jsx("path",{d:"m14.6 14.6 4 4",...t})]}),e==="close"&&f.jsx("path",{d:"m8 8 8 8m0-8-8 8",...t}),e==="retry"&&f.jsxs(f.Fragment,{children:[f.jsx("path",{d:"M18 9a7 7 0 1 0 .5 5",...t}),f.jsx("path",{d:"M18 5v4h-4",...t})]})]})}function Bs(e,t,n){const r=ko(n),o=e?.header.arrays.find(i=>ko(i.name)===r),s=Object.entries(t.properties??{}).find(([i])=>ko(i)===r)?.[1];return Hr(o?.unit??s?.unit)}function Hr(e){if(e)return e.replace(/angstrom/gi,"Å").replace(/Angstrom/g,"Å")}function Ob(e,t){return t?`${e} ${t}`:e}function Zi(e,t){return e.topology.symbols?.[t]??ry[e.topology.atomic_numbers?.[t]??0]??"X"}function Ji(e,t){const n=`${Zi(e,t.atom)}${t.atom+1}`,r=t.image.map((o,s)=>{if(o===0)return"";const i=o>0?"+":"−",a=Math.abs(o)===1?"":Math.abs(o);return`${i}${a}${"abc"[s]}`}).join("");return r?`${n} (${r})`:n}function ko(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"")}function Xe(e){if(!Number.isFinite(e))return"—";const t=Math.abs(e);return t!==0&&(t>=1e4||t<.001)?e.toExponential(3):new Intl.NumberFormat("en",{maximumFractionDigits:4}).format(e)}function Ke(e){return e instanceof Error?e.message:"Unexpected error"}function Iu(e){return{"ball-stick":"Ball + stick",spacefill:"Spacefill",licorice:"Licorice",lines:"Lines",ribbon:"Ribbon",polyhedra:"Polyhedra"}[e]}function Lb(e,t,n){return e.ribbon?"protein":e.suggestedProfile==="crystal"?"crystal":"molecule"}function Bb(e,t,n,r,o,s){const i=Lb(s);return Db(i,t,n,r,s)}function Db(e,t,n,r,o){const s={min:[0,0,0],max:[0,0,0]};return e==="protein"?{...t,mode:o.ribbon?"ribbon":"licorice",water:o.water?"hide":"show",hydrogens:!1,wrap:"molecule",images:s,cell:!1,forces:!1,velocities:!1,color:o.ribbon?"residue":"element"}:e==="crystal"?{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:"atom",images:s,cell:n,forces:!1,velocities:!1,color:"element"}:e==="trajectory"?{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:n?"atom":"none",images:s,cell:n,forces:r,velocities:!1,color:"element"}:{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:"molecule",images:s,cell:!1,forces:r,velocities:!1,color:"element"}}function zb(e,t,n,r){return`${zr(e,"molecule")}-${t}x${n}.${r}`}function Vb(e){return`${zr(e,"molecule")}.pqfigure.json`}function zr(e,t){return(e??t).replace(/\.[^.]+$/,"").replace(/[^a-z0-9._-]+/gi,"-").replace(/^-+|-+$/g,"")||t}function vr(e){return{...e,background:e.background.kind==="transparent"?{kind:"transparent"}:{kind:"solid",color:e.background.color}}}function Ds(e){return e.map(t=>t.kind==="atom-label"?{...t,atom:{atom:t.atom.atom,image:[...t.atom.image]},...t.offset?{offset:[...t.offset]}:{}}:{...t})}function zs(e){return`${e.atom}:${e.image.join(",")}`}function Ub(){return new URLSearchParams(window.location.search).get("headless")==="1"}function Rc(e,t){return`${t.length===2?"Distance":t.length===3?"Angle":"Dihedral"} · ${t.map(r=>Ji(e,r)).join("–")}`}function qb(e){return e==="angstrom"?"Å":"°"}function Vs(e,t,n){return`${zr(e,"trajectory")}-${t}.${n}`}function Us(e,t,n){const r=t.kind==="comparison"?"measurements":t.kind==="rdf"?"pair-analysis":zr(t.lines[0]?.label,t.kind);return`${zr(e,"trajectory")}-${r}.${n}`}function qs(e,t){return[...new Set(e.filter(n=>Number.isSafeInteger(n)&&n>=0&&nn-r)}function Gb(e,t){if(!e||t.frame_counts!==r[i]))return!1;const o=s=>s.source?.segments?.map(i=>({source_id:i.source_id,kind:i.kind,path:i.path??null,input:i.input??null,files:i.files??null}))??[];return JSON.stringify(o(e))===JSON.stringify(o(t))}function Hb(e){const t=e.filter(({selections:r})=>r.length===2),n=e.filter(({selections:r})=>r.length===3||r.length===4);return n.length>t.length?n:t}function Kb(e,t,n){const r=Array.from({length:t},(o,s)=>s+1);return{requestId:n,kind:"comparison",title:"Measurement comparison",xLabel:"Frame",yLabel:e[0]?.selections.length===2?"Distance":"Angle",yUnit:e[0]?.selections.length===2?"Å":"°",xValues:r,frameIndices:r.map((o,s)=>s),lines:e.map(o=>({id:o.id,label:o.label??o.id,values:r.map(()=>null),selection:o.selections,minimumImage:o.minimumImage})),loadedCount:0,totalCount:t,complete:!1}}function Wb(e,t,n){return{requestId:n,kind:"property",title:e.label,xLabel:"Frame",yLabel:e.label,yUnit:Hr(e.unit),xValues:Array.from({length:t},(r,o)=>o+1),frameIndices:Array.from({length:t},(r,o)=>o),lines:[{id:e.name,label:e.label,values:e.values}],loadedCount:t,totalCount:t,complete:!0}}function Xb(e,t){const n=e==="coordination";return{requestId:t.requestId,kind:"rdf",title:`${n?"Coordination":"Pair distribution"} · ${Vo(t.referenceLabel)} → ${Vo(t.targetLabel)}`,xLabel:"Radius",xUnit:"Å",yLabel:n?"N(r)":"g(r)",yFloor:0,xValues:[],lines:[{id:n?"coordination":"rdf",label:n?"N(r)":"g(r)",values:[]}],loadedCount:0,totalCount:0,complete:!1}}function Tc(e,t,n){const r=t==="coordination",o=r?e.coordinationRadius:e.radiusCenters,s=r?e.coordination:e.gR;return{requestId:n.requestId,kind:"rdf",title:`${r?"Coordination":"Pair distribution"} · ${Vo(n.referenceLabel)} → ${Vo(n.targetLabel)}`,xLabel:"Radius",xUnit:Hr(e.radiusUnit),yLabel:r?"N(r)":"g(r)",yFloor:0,yUnit:Qb(r?e.coordinationUnit:e.rdfUnit),context:Yb(e),xValues:o,lines:[{id:r?"coordination":"rdf",label:r?"N(r)":"g(r)",values:s}],loadedCount:s.length,totalCount:s.length,complete:!0}}function Yb(e){const t=Hr(e.radiusUnit)??e.radiusUnit;return[`${e.frameRange.count.toLocaleString()} frames`,`${e.referenceIndices.length.toLocaleString()}×${e.targetIndices.length.toLocaleString()} atoms`,`Δr ${Xe(e.deltaR)} ${t}`,`r max ${Xe(e.rMax)} ${t}`].join(" · ")}function Qb(e){const t=e?.trim().toLowerCase();return t&&!["1","dimensionless","unitless"].includes(t)?Hr(e):void 0}function Vo(e){return/^All (.+) atoms$/.exec(e)?.[1]??e}function Pc(e,t){return t<=1?0:Math.max(0,Math.min(100,e/(t-1)*100))}function cn(e,t){const n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t,r.style.display="none",document.body.append(r),r.click(),r.remove(),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)}function Zb(e){return!!(e?.isContentEditable||e?.closest('input, select, textarea, [role="textbox"]'))}function Jb(e){return!!e?.closest('button, a[href], [role="button"], [role="menuitem"]')}function ey(){try{return Rg(window.localStorage.getItem("pqviewer-vim-navigation"))}catch{return!1}}function ty(){return typeof navigator>"u"?"":navigator.userAgentData?.platform||navigator.platform||navigator.userAgent}function ny(){try{const e=JSON.parse(window.localStorage.getItem("pqviewer-presentation")??"null");return!e||typeof e!="object"?tt:{mode:["ball-stick","spacefill","lines","ribbon","polyhedra"].includes(e.mode)?e.mode:tt.mode,water:e.water==="hide"?"hide":"show",hydrogens:tt.hydrogens,wrap:tt.wrap,images:tt.images,cellOrigin:[0,0,0],mirror:[!1,!1,!1],cell:typeof e.cell=="boolean"?e.cell:tt.cell,forces:typeof e.forces=="boolean"?e.forces:tt.forces,velocities:typeof e.velocities=="boolean"?e.velocities:tt.velocities,atomScale:tt.atomScale,bondScale:tt.bondScale,color:tt.color,quality:tt.quality}}catch{return tt}}const ry=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr"];nd.createRoot(document.getElementById("root")).render(f.jsx(w.StrictMode,{children:f.jsx(mb,{})})); diff --git a/pqviewer/static/assets/index-FHrJwky5.js b/pqviewer/static/assets/index-FHrJwky5.js deleted file mode 100644 index 814e5f0..0000000 --- a/pqviewer/static/assets/index-FHrJwky5.js +++ /dev/null @@ -1,43 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/publication-Br5bSGOw.js","assets/three-Cf-YKIix.js"])))=>i.map(i=>d[i]); -import{r as w,j as d,c as Tu}from"./react-C2StAl4u.js";import{V as F,X as Ls,O as Nc,T as Se,b as Bs,Y as Ce,_ as fn,$ as Lr,B as Kt,a as he,a0 as Pu,g as Me,F as Lt,J as kt,a1 as Ou,a2 as Ao,s as Lu,a3 as Fc,a4 as Bu,a5 as Du,a6 as kn,a7 as Cc,a8 as kr,a9 as zu,aa as Xn,ab as So,M as Zn,ac as Vu,W as bs,H as Uu,m as Ds,l as vr,P as fa,u as qu,ad as Ic,ae as Jn,af as Br,D as Ec,R as da,ag as er,ah as Oo,ai as pi,aj as Nt,ak as $c,c as yr}from"./three-Cf-YKIix.js";import{m as Gu}from"./publication-Br5bSGOw.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(o){if(o.ep)return;o.ep=!0;const s=n(o);fetch(o.href,s)}})();const Hu=new TextDecoder,Ku=96,Wu=64*1024*1024,ma=4;async function ha(){const e=await fetch("/api/manifest",{headers:{Accept:"application/json"}});if(!e.ok)throw new Error(await Ut(e,"Could not load the trajectory"));const t=await e.json();return _c(t,"The trajectory manifest is incomplete"),t}async function Xu(){const e=await fetch("/api/initial-recipe",{headers:{Accept:"application/json"}});if(!e.ok)throw new Error(await Ut(e,"Could not load the figure recipe"));return e.json()}async function Yu(e,t){const n=await fetch("/api/positions",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({dataset_generation:e.datasetGeneration,atom_indices:e.atomIndices,frame_indices:e.frameIndices,coordinates:e.coordinates??"unwrapped"}),signal:t});if(n.status===409)throw new it(await Ut(n,"Trajectory changed. Reloading."));if(!n.ok)throw new Error(await Ut(n,"Could not load selected positions"));return tf(await n.json(),e)}async function Qu(e,t){const n=await fetch("/api/analysis/rdf",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({dataset_generation:e.datasetGeneration,reference_indices:e.referenceIndices,target_indices:e.targetIndices,frame_start:e.frameStart??0,frame_stop:e.frameStop,frame_step:e.frameStep??1,n_bins:e.bins??200,r_max:e.rMax}),signal:t});if(n.status===409)throw new it(await Ut(n,"Trajectory changed. Reloading."));if(!n.ok)throw new Error(await Ut(n,"Could not run RDF analysis"));return nf(await n.json(),e.datasetGeneration)}async function Zu(e,t){if(e.length===0)throw new Error("Choose at least one trajectory file");const n=new FormData;e.forEach(i=>n.append("files",i,i.name));const r=await fetch("/api/open",{method:"POST",headers:{Accept:"application/json"},body:n,signal:t});if(!r.ok)throw new Error(await Ut(r,"Could not open the files"));const o=await r.json(),s="manifest"in o&&o.manifest?o.manifest:o;return _c(s,"The opened trajectory is incomplete"),s}class it extends Error{constructor(t){super(t),this.name="DatasetChangedError"}}async function po(e,t,n,r="source"){const o=[];n&&o.push(`dataset_generation=${encodeURIComponent(n)}`),r==="unwrapped"&&o.push("coordinates=unwrapped");const s=o.length>0?`?${o.join("&")}`:"",i=await fetch(`/api/frames/${e}${s}`,{headers:{Accept:"application/octet-stream"},signal:t});if(i.status===409)throw new it(await Ut(i,"Trajectory changed. Reloading."));if(!i.ok)throw new Error(await Ut(i,`Could not load frame ${e+1}`));return Ju(await i.arrayBuffer())}function Ju(e){if(e.byteLength<4)throw new Error("Frame packet is truncated");const n=new DataView(e).getUint32(0,!0),r=4+n;if(r>e.byteLength)throw new Error("Frame header is truncated");let o;try{o=JSON.parse(Hu.decode(new Uint8Array(e,4,n)))}catch{throw new Error("Frame header is invalid")}if(!Array.isArray(o.arrays))throw new Error("Frame arrays are missing");const s=new Map;for(const i of o.arrays){const a=r+i.byte_offset,c=a+i.byte_length;if(ae.byteLength||c=n||this.values.has(t)||!this.canPrefetch()||this.load(t,!0).catch(()=>{})}cancelPendingExcept(t){for(const[n,r]of this.values)n===t||!r.controller||this.remove(n,r,!0)}clear(){for(const t of this.values.values())t.controller?.abort();this.values.clear(),this.resolvedBytes=0,this.frameByteEstimate=0}load(t,n){const r=new AbortController;let o;const s=po(t,r.signal,this.datasetGeneration,this.coordinates).then(i=>(o.controller=null,o.byteLength=ef(i),this.frameByteEstimate=Math.max(this.frameByteEstimate,o.byteLength),this.values.get(t)===o&&(this.resolvedBytes+=o.byteLength,this.trim()),i)).catch(i=>{throw this.values.get(t)===o&&this.remove(t,o,!1),i});return o={promise:s,controller:r,byteLength:0,prefetch:n},this.values.set(t,o),this.trim(),s}canPrefetch(){const t=[...this.values.values()].filter(o=>o.prefetch&&o.controller!==null).length;if(this.frameByteEstimate===0)return tthis.maxFrames||this.resolvedBytes>this.maxBytes;){const t=this.values.keys().next().value;if(t===void 0)break;const n=this.values.get(t);n&&this.remove(t,n,!0)}}remove(t,n,r){this.values.get(t)===n&&(this.values.delete(t),this.resolvedBytes=Math.max(0,this.resolvedBytes-n.byteLength),r&&n.controller?.abort())}}function ef(e){const t=new Set;let n=0;for(const r of e.arrays.values()){const o=r.buffer;if(!t.has(o)&&(t.add(o),n+=o.byteLength,!Number.isSafeInteger(n)))return Number.MAX_SAFE_INTEGER}return n}function pa(e,t){return!Number.isFinite(e)||e===void 0||e<=0?t:Math.max(1,Math.floor(e))}function _c(e,t){if(!e.topology||!Number.isFinite(e.frame_count)||Rc(e.dataset_generation)===void 0)throw new Error(t)}function tf(e,t){if(!e||typeof e!="object")throw new Error("Selected positions response is invalid");const n=e,r=zt(n.schema_version,"position schema"),o=Sn(n.dataset_generation,"position generation");if(o!==t.datasetGeneration)throw new it("Trajectory changed. Reloading.");const s=Vs(n.atom_indices,"position atoms");if(s.length!==t.atomIndices.length||s.some((a,c)=>a!==t.atomIndices[c]))throw new Error("Selected positions do not match the requested atoms");if(!Array.isArray(n.frames)||n.frames.length!==t.frameIndices.length)throw new Error("Selected positions do not match the requested frames");const i=n.frames.map((a,c)=>{if(!a||typeof a!="object")throw new Error("Selected position frame is invalid");const l=a,u=zt(l.index,"position frame");if(u!==t.frameIndices[c])throw new Error("Selected positions are out of order");if(!Array.isArray(l.positions)||l.positions.length!==s.length)throw new Error("Selected position coordinates are incomplete");const f=new Float32Array(s.length*3);return l.positions.forEach((m,g)=>{if(!Array.isArray(m)||m.length!==3||!m.every(p=>typeof p=="number"&&Number.isFinite(p)))throw new Error("Selected position coordinates are invalid");f.set(m,g*3)}),Object.freeze({index:u,key:zs(l.key),positions:f,step:Ir(l.step),time:Ir(l.time),timeUnit:gi(l.time_unit)})});return Object.freeze({schemaVersion:r,datasetGeneration:o,atomIndices:Object.freeze([...s]),unit:Sn(n.unit,"position unit"),frames:Object.freeze(i)})}function nf(e,t){if(!e||typeof e!="object")throw new Error("RDF response is invalid");const n=e,r=Sn(n.dataset_generation,"RDF generation");if(r!==t)throw new it("Trajectory changed. Reloading.");const o=n.frame_range;if(!o||typeof o!="object")throw new Error("RDF frame identity is missing");const s=o,i=Object.freeze({start:zt(s.start,"RDF frame start"),stop:zt(s.stop,"RDF frame stop"),step:xs(s.step,"RDF frame step"),count:xs(s.count,"RDF frame count"),firstKey:zs(s.first_key),lastKey:zs(s.last_key)}),a=ro(n.radius_centers,"RDF radius"),c=ro(n.g_r,"RDF values"),l=ro(n.coordination_radius,"coordination radius"),u=ro(n.coordination,"coordination values");if(a.length!==c.length||l.length!==u.length||a.length!==u.length)throw new Error("RDF result arrays are misaligned");const f=n.units;if(!f||typeof f!="object")throw new Error("RDF units are missing");const m=f,g=n.parameters;if(!g||typeof g!="object")throw new Error("RDF parameters are missing");const p=g,x=n.selections;if(!x||typeof x!="object")throw new Error("RDF selections are missing");const A=x;return Object.freeze({schemaVersion:zt(n.schema_version,"RDF schema"),datasetGeneration:r,referenceIndices:Object.freeze(Vs(A.reference_indices,"RDF reference selection")),targetIndices:Object.freeze(Vs(A.target_indices,"RDF target selection")),frameRange:i,radiusUnit:Sn(m.radius,"RDF radius unit"),rdfUnit:Sn(m.g_r,"RDF unit"),coordinationUnit:Sn(m.coordination,"coordination unit"),bins:xs(p.n_bins,"RDF bins"),rMax:ga(p.r_max,"RDF maximum radius"),deltaR:ga(p.delta_r,"RDF resolution"),radiusCenters:Object.freeze(a),gR:Object.freeze(c),coordinationRadius:Object.freeze(l),coordination:Object.freeze(u),pqAnalysisVersion:gi(n.pqanalysis_version)??void 0,elapsedSeconds:Ir(n.elapsed_seconds)??void 0})}function zs(e){if(!e||typeof e!="object")throw new Error("Frame key is invalid");const t=e;return Object.freeze({source_id:Sn(t.source_id,"frame source"),source_index:zt(t.source_index,"frame source index"),segment_index:zt(t.segment_index,"frame segment"),step:Ir(t.step),time:Ir(t.time),time_unit:gi(t.time_unit)})}function Vs(e,t){if(!Array.isArray(e))throw new Error(`${t} are invalid`);return e.map(n=>zt(n,t))}function ro(e,t){if(!Array.isArray(e))throw new Error(`${t} are invalid`);return e.map(n=>{if(typeof n!="number"||!Number.isFinite(n))throw new Error(`${t} are invalid`);return n})}function zt(e,t){if(!Number.isSafeInteger(e)||e<0)throw new Error(`${t} is invalid`);return e}function xs(e,t){const n=zt(e,t);if(n<1)throw new Error(`${t} is invalid`);return n}function ga(e,t){if(typeof e!="number"||!Number.isFinite(e)||e<=0)throw new Error(`${t} is invalid`);return e}function Ir(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function Sn(e,t){if(typeof e!="string"||!e.trim())throw new Error(`${t} is invalid`);return e.trim()}function gi(e){return typeof e=="string"&&e.trim()?e.trim():null}function Rc(e){const t=e?.trim();return t||void 0}function le(e,t){if(!e)return null;for(const n of t){const r=e.arrays.get(n.toLowerCase());if(r instanceof Float32Array)return r}return null}function Mo(e,t){if(!e)return null;for(const n of t){const r=e.arrays.get(n.toLowerCase());if(r instanceof Int32Array)return r}return null}function rf(e){if(!e)return[];if(Array.isArray(e))return e.filter(n=>!!(n&&Array.isArray(n.values))).map((n,r)=>({name:n.name??n.key??`series-${r+1}`,label:n.label??As(n.name??n.key??`Series ${r+1}`),unit:n.unit,values:ws(n.values)}));const t=[];for(const[n,r]of Object.entries(e)){if(Array.isArray(r)){t.push({name:n,label:As(n),values:ws(r)});continue}if(r&&typeof r=="object"&&Array.isArray(r.values)){const o=r;t.push({name:n,label:o.label??As(o.name??n),unit:o.unit,values:ws(o.values)})}}return t}async function Ut(e,t){try{const n=await e.json();return n.detail??n.message??t}catch{return t}}function of(e){return["float32","f4","typeof t=="number"&&Number.isFinite(t)?t:null)}function As(e){return e.replace(/[_-]+/g," ").replace(/\b\w/g,t=>t.toUpperCase())}const sf=6;function af(e,t,n={}){const r=e.filter(l=>!l.disabled),o=go(t);if(!o){const l=Math.min(xa(n.limit),sf);return cf(r,n).slice(0,l)}const s=o.split(" "),i=ya(n.contextIds),a=ya(n.recentIds),c=r.flatMap((l,u)=>{const f=lf(l,o,s);return f===null?[]:[{action:l,score:f,index:u,contextRank:i.get(l.id),recentRank:a.get(l.id)}]});return c.sort((l,u)=>u.score-l.score||oo(l.contextRank)-oo(u.contextRank)||oo(l.recentRank)-oo(u.recentRank)||l.index-u.index),c.slice(0,xa(n.limit)).map(({action:l})=>l)}function cf(e,t){const n=new Map(e.map(s=>[s.id,s])),r=new Set,o=[];for(const s of[...t.contextIds??[],...t.recentIds??[]]){const i=n.get(s);!i||r.has(s)||(r.add(s),o.push(i))}for(const s of e)r.has(s.id)||(r.add(s.id),o.push(s));return o}function lf(e,t,n){const r=go(e.label),o=go(typeof e.keywords=="string"?e.keywords:e.keywords?.join(" ")??""),s=go(e.detail??""),i=r.split(" "),a=o.split(" "),c=s.split(" ");let l=0;for(const u of n){const f=uf(u,r,i,o,a,s,c);if(f===0)return null;l+=f}return r===t?l+=1e3:r.startsWith(t)?l+=500:r.includes(t)&&(l+=250),o===t?l+=220:o.startsWith(t)?l+=160:o.includes(t)&&(l+=100),s===t?l+=60:s.includes(t)&&(l+=30),l}function uf(e,t,n,r,o,s,i){return t===e?140:t.startsWith(e)?130:n.some(a=>a.startsWith(e))?120:t.includes(e)?100:o.some(a=>a.startsWith(e))?80:r.includes(e)?60:i.some(a=>a.startsWith(e))?40:s.includes(e)?20:0}function go(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function ya(e){const t=new Map;return e?.forEach((n,r)=>{t.has(n)||t.set(n,r)}),t}function oo(e){return e??Number.MAX_SAFE_INTEGER}function xa(e){return e===void 0||!Number.isFinite(e)?Number.MAX_SAFE_INTEGER:Math.max(0,Math.floor(e))}const Lo=720,bi=130;function Tc(e,t){const n=Math.max(160,at(e,Lo)),r=Math.max(72,at(t,bi)),o=n<=520?58:64,s=r<=96;return{width:n,height:r,left:o,right:Math.max(o+1,n-16),top:s?8:12,bottom:Math.max(24,r-(s?24:38))}}const Mt=Tc(Lo,bi),Pc=1600,wa=Object.freeze(["#137f78","#b35c2e","#5468a8","#8b5a91","#4f7b45","#b08524","#366e83","#9a4d62"]);function ff({title:e,unit:t,axisLabel:n,axisUnit:r,xValues:o,values:s,loadedCount:i,complete:a,currentFrame:c,onFrame:l,onExportCsv:u,onExportSvg:f,onExportPdf:m}){const g=w.useMemo(()=>({requestId:0,kind:"measurement",title:e,xLabel:n,xUnit:r,yLabel:Nf(e),yUnit:t,xValues:o,frameIndices:o.map((p,x)=>x),lines:[{id:"measurement",label:e,values:s,discontinuity:Mf(t)}],loadedCount:i,totalCount:o.length,complete:a}),[n,r,a,i,e,t,s,o]);return d.jsx(Oc,{plot:g,currentFrame:c,onFrame:l,onExportCsv:u,onExportSvg:f,onExportPdf:m})}function Oc({plot:e,currentFrame:t,onFrame:n,onRestoreLine:r,onClose:o,headerActions:s,onExportCsv:i,onExportSvg:a,onExportPdf:c}){const{title:l,xLabel:u,xUnit:f,yLabel:m,yUnit:g,xValues:p,lines:x,loadedCount:A,totalCount:b,complete:j}=e,[k,v]=df(),S=w.useMemo(()=>Tc(v?.width??Lo,v?.height??bi),[v?.height,v?.width]),I=p.length,E=e.frameIndices,_=t===void 0?-1:yf(t,E,I),U=w.useMemo(()=>xf(E,I),[E,I]),G=w.useMemo(()=>wf(E,U),[E,U]),B=!!(n&&U.length>0),V=w.useMemo(()=>v===null?{xDomain:[0,Math.max(1,I-1)],yDomain:[0,1],lines:[]}:mf(p,x,{width:S.width,height:S.height,left:S.left,right:S.right,top:S.top,bottom:S.bottom,yDomain:e.yFloor===void 0?void 0:hf(x,e.yFloor),maxPoints:Math.min(Pc,Math.ceil((S.right-S.left)*2))}),[v,S,x,e.yFloor,I,p]),$=_>=0?vf(_,p,V.xDomain,S.left,S.right):null,L=x.map(W=>yi(W.values[_])),ee=Math.max(0,Math.min(b,Number.isFinite(A)?Math.floor(A):0)),te=f?`${u} (${f})`:u,oe=g?`${m} (${g})`:m,C=e.kind==="rdf"?"bins":"frames",N=j?`${b.toLocaleString()} ${C}`:`${ee.toLocaleString()} / ${b.toLocaleString()} ${C}`,P=e.context?`${N} · ${e.context}`:N,R=_>=0?jf(_,t,p,x,u,f,g):"No linked frame",Y=W=>{if(!B||!n)return;const J=W.currentTarget.getBoundingClientRect(),me=Sf(W.clientX,J,S.width);if(me===null)return;const q=bf(me,p,S.left,S.right),de=Us(E,q);de!==null&&n(de)},H=W=>{W.button!==0||!B||(W.currentTarget.setPointerCapture(W.pointerId),Y(W))},X=W=>{W.currentTarget.hasPointerCapture(W.pointerId)&&Y(W)},ne=W=>{W.currentTarget.hasPointerCapture(W.pointerId)&&W.currentTarget.releasePointerCapture(W.pointerId)},re=W=>{if(!B||!n)return;let J=null;const me=Math.max(0,U.indexOf(_));if(W.key==="ArrowLeft"&&(J=U[Math.max(0,me-1)]),W.key==="ArrowRight"&&(J=U[Math.min(U.length-1,me+1)]),W.key==="Home"&&(J=U[0]),W.key==="End"&&(J=U.at(-1)??null),J===null)return;const q=Us(E,J);q!==null&&(W.preventDefault(),n(q))};return d.jsxs("section",{className:j?"measurement-plot is-complete":"measurement-plot","aria-label":`${l} trajectory plot`,children:[d.jsxs("header",{className:"measurement-plot__header",children:[d.jsxs("div",{className:"measurement-plot__meta",children:[d.jsx("strong",{title:l,children:l}),d.jsx("span",{title:j?P:void 0,children:j?P:"Loading data"})]}),d.jsx("div",{className:"measurement-plot__legend",role:"group","aria-label":"Current values",children:x.map((W,J)=>{const me=qs(W.color,J),q=L[J],de=_<0?null:q===null?"unavailable":`${xn(q)}${g?` ${g}`:""}`,se=d.jsxs(d.Fragment,{children:[d.jsx("span",{className:"measurement-plot__legend-swatch","aria-hidden":"true",style:{backgroundColor:me}}),d.jsx("span",{children:W.label}),de!==null&&d.jsx("output",{"aria-label":`${W.label} current value`,children:de})]});return r&&W.selection?d.jsx("button",{className:"measurement-plot__legend-item",type:"button",onClick:()=>r(W),"aria-label":de===null?`Restore ${W.label}`:`Restore ${W.label}; current value ${de}`,children:se},W.id):d.jsx("span",{className:"measurement-plot__legend-item",children:se},W.id)})}),d.jsxs("div",{className:"measurement-plot__header-actions",role:"group","aria-label":"Plot controls",children:[s&&d.jsx("div",{className:"measurement-plot__context-actions",children:s}),d.jsxs("div",{className:"measurement-plot__actions",children:[d.jsxs("details",{className:"measurement-plot__export-menu",children:[d.jsx("summary",{children:"Export"}),d.jsxs("div",{children:[d.jsx("button",{type:"button",onClick:i,disabled:!j,children:"CSV"}),d.jsx("button",{type:"button",onClick:a,disabled:!j,children:"SVG"}),d.jsx("button",{type:"button",onClick:c,disabled:!j,children:"PDF"})]})]}),d.jsx("button",{type:"button",onClick:i,disabled:!j,children:"CSV"}),d.jsx("button",{type:"button",onClick:a,disabled:!j,children:"SVG"}),d.jsx("button",{type:"button",onClick:c,disabled:!j,children:"PDF"}),o&&d.jsx("button",{className:"measurement-plot__close",type:"button",onClick:o,"aria-label":"Close plot",title:"Close",children:"×"})]})]})]}),d.jsxs("svg",{ref:k,className:B?"measurement-plot__chart is-seekable":"measurement-plot__chart",viewBox:`0 0 ${S.width} ${S.height}`,role:B?"slider":"img",tabIndex:B?0:void 0,"aria-label":B?`${l} frame`:l,"aria-orientation":B?"horizontal":void 0,"aria-valuemin":B?G?.[0]:void 0,"aria-valuemax":B?G?.[1]:void 0,"aria-valuenow":B&&t!==void 0?t:void 0,"aria-valuetext":B?R:void 0,onKeyDown:re,onPointerDown:H,onPointerMove:X,onPointerUp:ne,onPointerCancel:ne,style:{touchAction:B?"none":"auto",cursor:B?"crosshair":"default"},children:[d.jsx("title",{children:l}),d.jsx("desc",{children:B?"Tap or drag to seek. Use arrow keys, Home, or End to move between frames.":`${x.length} plotted series.`}),d.jsx("line",{className:"measurement-plot__grid",x1:S.left,x2:S.right,y1:S.top,y2:S.top}),d.jsx("line",{className:"measurement-plot__grid",x1:S.left,x2:S.right,y1:(S.top+S.bottom)/2,y2:(S.top+S.bottom)/2}),d.jsx("line",{className:"measurement-plot__axis",x1:S.left,x2:S.right,y1:S.bottom,y2:S.bottom}),V.lines.flatMap(W=>W.segments.map((J,me)=>J.points.length===1?d.jsx("circle",{className:"measurement-plot__trace-point",cx:J.points[0].x,cy:J.points[0].y,r:2,style:{fill:W.color}},`${W.id}-point-${J.points[0].frame}-${me}`):d.jsx("path",{className:"measurement-plot__trace",d:J.path,fill:"none",vectorEffect:"non-scaling-stroke",style:{stroke:W.color}},`${W.id}-trace-${J.points[0].frame}-${me}`))),V.lines.every(({segments:W})=>W.length===0)&&d.jsx("text",{className:"measurement-plot__empty",x:(S.left+S.right)/2,y:(S.top+S.bottom)/2,textAnchor:"middle",children:j?"No valid data":"Loading data…"}),$!==null&&d.jsx("line",{className:"measurement-plot__cursor",x1:$,x2:$,y1:S.top,y2:S.bottom,vectorEffect:"non-scaling-stroke"}),$!==null&&L.map((W,J)=>W===null?null:d.jsx("circle",{className:"measurement-plot__cursor-point",cx:$,cy:Yn(W,V.yDomain,S.bottom,S.top),r:4,style:{fill:qs(x[J].color,J)}},`cursor-${x[J].id}`)),d.jsx("text",{className:"measurement-plot__tick",x:S.left,y:S.bottom+17,children:xn(V.xDomain[0])}),d.jsx("text",{className:"measurement-plot__tick",x:S.right,y:S.bottom+17,textAnchor:"end",children:xn(V.xDomain[1])}),d.jsx("text",{className:"measurement-plot__tick",x:S.left-8,y:S.top+4,textAnchor:"end",children:xn(V.yDomain[1])}),d.jsx("text",{className:"measurement-plot__tick",x:S.left-8,y:S.bottom,textAnchor:"end",children:xn(V.yDomain[0])}),d.jsx("text",{className:"measurement-plot__axis-label",x:(S.left+S.right)/2,y:S.height-4,textAnchor:"middle",children:te}),d.jsx("text",{className:"measurement-plot__unit",x:12,y:(S.top+S.bottom)/2,textAnchor:"middle",transform:`rotate(-90 12 ${(S.top+S.bottom)/2})`,children:oe})]}),!j&&d.jsxs("div",{className:"measurement-plot__progress",role:"status","aria-live":"polite",children:[d.jsx("progress",{"aria-label":e.kind==="rdf"?"Bins loaded":"Frames loaded",max:Math.max(1,b),value:ee}),d.jsx("span",{children:P})]})]})}function df(){const e=w.useRef(null),[t,n]=w.useState(null);return w.useEffect(()=>{const r=e.current;if(!r)return;let o=null,s=null;const i=(l,u)=>{if(!Number.isFinite(l)||!Number.isFinite(u)||l<=0||u<=0)return;const f={width:Math.round(l),height:Math.round(u)};n(m=>m&&m.width===f.width&&m.height===f.height?m:f)};let a=window.requestAnimationFrame(()=>{a=window.requestAnimationFrame(()=>{const l=r.getBoundingClientRect();i(l.width,l.height),a=null})});const c=new ResizeObserver(([l])=>{l&&(s={width:l.contentRect.width,height:l.contentRect.height},o!==null&&window.clearTimeout(o),o=window.setTimeout(()=>{s&&i(s.width,s.height),o=null},80))});return c.observe(r),()=>{c.disconnect(),a!==null&&window.cancelAnimationFrame(a),o!==null&&window.clearTimeout(o)}},[]),[e,t]}function Aa(e,t,n={}){const r=bo(n.width,Mt.width),o=bo(n.height,Mt.height),s=at(n.left,Mt.left),i=at(n.right,r-(Mt.width-Mt.right)),a=at(n.top,Mt.top),c=at(n.bottom,o-(Mt.height-Mt.bottom)),l=pf(e,t,n.discontinuityThreshold),u=vn(e.map((x,A)=>at(x,A)),[0,Math.max(1,e.length-1)],!1),f=n.yDomain?vn(n.yDomain,[0,1],!1):vn(l.flatMap(x=>x.map(({value:A})=>A)),[0,1],!0),m=Math.max(2,Math.floor(bo(n.maxPoints,Math.max(2,(i-s)*2)))),p=gf(l,m).map(x=>{const A=x.map(b=>({...b,x:Yn(b.xValue,u,s,i),y:Yn(b.value,f,c,a)}));return{points:A,path:A.map((b,j)=>`${j===0?"M":"L"}${Ma(b.x)} ${Ma(b.y)}`).join(" ")}});return{xDomain:u,yDomain:f,segments:p}}function mf(e,t,n={}){const r=n.yDomain??vn(t.flatMap(({values:c})=>c.flatMap(l=>typeof l=="number"&&Number.isFinite(l)?[l]:[])),[0,1],!0),o=Math.max(2,Math.floor(bo(n.maxPoints,Pc))),s=Math.max(2,Math.floor(o/Math.max(1,t.length))),i=t.map((c,l)=>{const u=Aa(e,c.values,{...n,yDomain:r,discontinuityThreshold:c.discontinuity,maxPoints:s});return{id:c.id,color:qs(c.color,l),segments:u.segments}});return{xDomain:i.length>0?Aa(e,[],{...n,yDomain:r}).xDomain:vn(e.map((c,l)=>at(c,l)),[0,Math.max(1,e.length-1)],!1),yDomain:r,lines:i}}function hf(e,t){const n=vn(e.flatMap(({values:o})=>o.flatMap(s=>typeof s=="number"&&Number.isFinite(s)?[s]:[])),[t,t+1],!0),r=Number.isFinite(t)?t:n[0];return[r,n[1]>r?n[1]:r+1]}function pf(e,t,n){const r=[];let o=[],s=null;const i=Number.isFinite(n)?Math.abs(n):null;for(let a=0;ai)&&(o.length>0&&r.push(o),o=[],s=l,(l===null||!Number.isFinite(c))&&(s=null),l===null||!Number.isFinite(c))||(o.push({frame:a,xValue:c,value:l}),s=l)}return o.length>0&&r.push(o),r}function gf(e,t){const n=e.filter(a=>a.length>0),r=n.reduce((a,c)=>a+c.length,0),o=Math.max(1,Math.floor(at(t,1)));if(r<=o)return n.map(a=>[...a]);if(n.length>=o)return Sa(n.length,o).map(a=>{const c=n[a];return[c[Math.floor((c.length-1)/2)]]});const s=n.map(a=>Math.min(a.length,a.length>1?2:1));let i=o-s.reduce((a,c)=>a+c,0);if(i<0)return Sa(n.length,o).map(a=>[n[a][0]]);for(;i>0;){const a=n.map((c,l)=>({index:l,capacity:c.length-s[l]})).filter(({capacity:c})=>c>0).sort((c,l)=>l.capacity-c.capacity);if(a.length===0)break;for(const{index:c}of a){if(i===0)break;s[c]+=1,i-=1}}return n.map((a,c)=>kf(a,s[c]))}function bf(e,t,n=Mt.left,r=Mt.right){if(t.length===0)return 0;const o=Math.max(Math.min(n,r),Math.min(Math.max(n,r),e)),s=t[0],i=t[t.length-1];if(!Number.isFinite(s)||!Number.isFinite(i)||i=0&&n.push(o)}return n}function Us(e,t){const n=e?.[t];return typeof n=="number"&&Number.isSafeInteger(n)&&n>=0?n:null}function wf(e,t){let n=1/0,r=-1/0;for(const o of t){const s=Us(e,o);s!==null&&(n=Math.min(n,s),r=Math.max(r,s))}return Number.isFinite(n)&&Number.isFinite(r)?[n,r]:null}function Af(e,t,n,r){const o=t.map((l,u)=>at(l,u)),s=vn(o,[0,Math.max(1,t.length-1)],!1),i=Yn(e,[n,r],s[0],s[1]);let a=0,c=1/0;return o.forEach((l,u)=>{const f=Math.abs(l-i);f=e.length||t<=0)return[...e];if(t===1)return[e[0]];if(t===2)return[e[0],e[e.length-1]];const n=[e[0]],r=(e.length-2)/(t-2);let o=0;for(let s=0;sp&&(p=j,x=A)}n.push(e[x]),o=x}return n.push(e[e.length-1]),n}function vn(e,t,n){let r=1/0,o=-1/0;if(e.forEach(s=>{Number.isFinite(s)&&(r=Math.min(r,s),o=Math.max(o,s))}),!Number.isFinite(r)||!Number.isFinite(o))return t;if(r===o){const s=Math.max(Math.abs(r)*.05,.5);r-=s,o+=s}else if(n){const s=(o-r)*.08;r-=s,o+=s}return[r,o]}function vf(e,t,n,r,o){return Yn(at(t[e],e),n,r,o)}function Yn(e,t,n,r){return t[0]===t[1]?(n+r)/2:n+(e-t[0])/(t[1]-t[0])*(r-n)}function yi(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function at(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function bo(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}function Sa(e,t){return t<=1?[0]:Array.from({length:t},(n,r)=>Math.round(r*(e-1)/(t-1)))}function Ma(e){return Number(e.toFixed(2)).toString()}function xn(e){const t=Math.abs(e);return t>0&&t<.001||t>=1e4?e.toExponential(2):Number(e.toPrecision(4)).toString()}function jf(e,t,n,r,o,s,i){const a=at(n[e],e),c=`${o} ${xn(a)}${s?` ${s}`:""}`,l=r.map(u=>{const f=yi(u.values[e]);return`${u.label} ${f===null?"unavailable":`${xn(f)}${i?` ${i}`:""}`}`});return[`Frame ${t+1}`,c,...l].join("; ")}function Nf(e){const t=e.indexOf(" · ");return t>0?e.slice(0,t):"Measurement"}function qs(e,t){const n=e?.trim();return n&&/^#[0-9a-f]{6}$/i.test(n)?n:wa[t%wa.length]}const Er=125,qn=25e4,xi=8e4,ko=xi,Ff=5e4,Cf=2e6,If=12e3,Ef=25e4,$f=4e7,_f=729,Rf=new Set(["H2O","HOH","OH2","WAT","WATER","TIP3","TIP3P","TIP4","TIP4P","SPC","SPCE"]),Tf=new Set(["ALA","ARG","ASN","ASP","CYS","GLN","GLU","GLY","HIS","ILE","LEU","LYS","MET","PHE","PRO","SER","THR","TRP","TYR","VAL","ASH","CYX","GLH","HID","HIE","HIP","LYN","MSE"]);function Pf(e,t,n,r){const o=le(t,["positions","position","pos","coordinates","coords"]);if(!o)return null;const s=Math.min(e.topology.atom_count,Math.floor(o.length/3)),i=Et(le(t,["cell","cell_vectors","box"])),a=cn(t,i),c=r?.count===s?r:Lc(e,t);if(!c)return null;const{atomicNumbers:l,bonds:u,waterAtoms:f}=c,m=Zf(n.cellOrigin),g=Qf(t,o,s,i,a,c,n.wrap,m),p=i?Ft(new F(...m),i):new F,x=ed(i,n.mirror),A=nd(g.positions,s,x,p),b=od(i,x),j=g.baseImages,k=sd(l,f,n),v=Df(n.images.min,n.images.max,a,k.length),{instanceToAtom:S,instanceImages:I}=zf(k,v),E=l.map(_=>wi(_,n.mode,n.atomScale));return{count:s,atomicNumbers:l,positions:A,baseImages:j,basis:b,cellCenter:p,displayTransform:x,pbc:a,bonds:u,waterAtoms:f,visibleAtoms:k,images:v,instanceToAtom:S,instanceImages:I,radii:E,backbone:Wf(e)}}function Lc(e,t){const n=le(t,["positions","position","pos","coordinates","coords"]);if(!n)return null;const r=Math.min(e.topology.atom_count,Math.floor(n.length/3)),o=Mi(e,r),s=Et(le(t,["cell","cell_vectors","box"])),i=cn(t,s),a=Si(e.topology.bonds,r),c=Bc(t,r)??Dr(n,r,s,i,[0,0,0]),l=e.topology.bond_source==="topology"||a.length>0?a:Kc(c,o,r,s,i);return{count:r,atomicNumbers:o,bonds:l,waterAtoms:Kf(e,t,l),moleculeGroups:ad(e,r,l)}}function Bc(e,t){const n=le(e,["positions","position","pos","coordinates","coords"]);if(!n)return null;const r=le(e,["centered_positions","centered_position"]),o=Math.min(t,Math.floor(n.length/3));if(r&&r.length>=o*3)return new Float32Array(r.subarray(0,o*3));const s=Et(le(e,["cell","cell_vectors","box"])),i=Mo(e,["centered_image_shifts","centered_images"]);return i&&i.length>=o*3?Hs(n,i,o,s,cn(e,s)):Dr(n,o,s,cn(e,s),[0,0,0])}function Of(e,t,n=null){const r=le(e,["positions","position","pos","coordinates","coords"]),o=Et(le(e,["cell","cell_vectors","box"]));if(!r||!o)return null;const s=Math.min(Math.max(0,Math.floor(t)),Math.floor(r.length/3)),i=new F,a=new F;let c=0;if(n===null){const l=e?.header.coordinates==="unwrapped",u=l?le(e,["unwrapped_positions","unwrapped_position"]):null,f=u&&u.length>=s*3?u:r,m=l&&f===r?Mo(e,["unwrapped_image_shifts","unwrapped_images"]):null;for(let g=0;g=s||u.length!==3||!u.every(Number.isInteger)||(a.fromArray(r,l*3),i.x+=a.dot(o.reciprocal[0])+u[0],i.y+=a.dot(o.reciprocal[1])+u[1],i.z+=a.dot(o.reciprocal[2])+u[2],c+=1);return c===0?null:(i.multiplyScalar(1/c),[i.x,i.y,i.z])}function Lf(e){return!!Et(le(e,["cell","cell_vectors","box"]))}function Bf(e){const t=Et(le(e,["cell","cell_vectors","box"]));return cn(e,t)}function Et(e){return!e||e.length<9?null:Dc(new F(e[0],e[1],e[2]),new F(e[3],e[4],e[5]),new F(e[6],e[7],e[8]))}function Dc(e,t,n){const r=new F().crossVectors(t,n),o=e.dot(r);return!Number.isFinite(o)||Math.abs(o)<1e-10?null:{vectors:[e,t,n],reciprocal:[r.multiplyScalar(1/o),new F().crossVectors(n,e).multiplyScalar(1/o),new F().crossVectors(e,t).multiplyScalar(1/o)]}}function cn(e,t){if(!t)return[!1,!1,!1];const n=e?.header.pbc;return n?[!!n[0],!!n[1],!!n[2]]:[!0,!0,!0]}function Df(e,t,n,r){const o=e.map((u,f)=>n[f]?Na(u):0),s=t.map((u,f)=>n[f]?Na(u):0),i=o.map((u,f)=>Math.min(u,s[f])),a=s.map((u,f)=>Math.max(u,o[f])),c=[];for(let u=i[0];u<=a[0];u+=1)for(let f=i[1];f<=a[1];f+=1)for(let m=i[2];m<=a[2];m+=1)c.push([u,f,m]);c.sort((u,f)=>Fa(u)-Fa(f)||u[0]-f[0]||u[1]-f[1]||u[2]-f[2]);const l=r>0?Math.max(1,Math.floor(qn/r)):Er;return c.slice(0,Math.min(Er,l))}function zf(e,t){const n=e.length*t.length,r=new Uint32Array(n),o=new Int8Array(n*3);let s=0;for(const i of t)for(const a of e)r[s]=a,o.set(i,s*3),s+=1;return{instanceToAtom:r,instanceImages:o}}function ka(e,t){if(!t||t.length1e-24&&(n+=1)}if(n===0)return{instances:[],total:0};const r=Math.min(n,If),o=[];let s=0,i=0,a=Math.floor((i+.5)*n/r);for(let c=0;c=a&&(o.push(c),i+=1,a=Math.floor((i+.5)*n/r)),s+=1)}return{instances:o,total:n}}function Vf(e,t,n,r=null){const o=e.instanceToAtom.length,s=zc(t,o),i=t.mode==="ribbon"||o===0?"none":s?"points":"instances",a=Uc(e,t),c=a.length===0?"none":t.mode==="lines"||s||a.length>ko?"lines":"instances",l=t.forces?ka(e,n):{instances:[],total:0},u=t.velocities?ka(e,r):{instances:[],total:0};return{atomKind:i,atomCount:o,bondKind:c,bondSegments:a,cellLineCount:t.cell&&e.basis?e.images.length*12:0,forceInstances:l.instances,forceTotal:l.total,velocityInstances:u.instances,velocityTotal:u.total}}function zc(e,t){return e.mode==="lines"||t>qn||t>xi}function Vc(e,t){return e.quality==="high"&&t<=Ff}function Uf(e){return{atomKind:e.atomKind,atomCount:e.atomCount,bondKind:e.bondKind,bondCount:e.bondSegments.length,cellLineCount:e.cellLineCount,forceCount:e.forceInstances.length,velocityCount:e.velocityInstances.length}}function qf(e,t){return e.atomKind===t.atomKind&&e.atomCount===t.atomCount&&e.bondKind===t.bondKind&&e.bondCount===t.bondCount&&e.cellLineCount===t.cellLineCount&&e.forceCount===t.forceCount&&e.velocityCount===t.velocityCount}function Uc(e,t){if(t.mode==="spacefill"||t.mode==="ribbon")return[];const n=new Set(e.visibleAtoms),r=[];for(const o of e.images){const s=qt(o,e.basis);for(const[i,a]of e.bonds){if(!n.has(i)||!n.has(a))continue;(t.wrap==="atom"?Xf(e.positions,i,a,e.basis,e.pbc,e.cellCenter):t.wrap==="unwrapped"?Yf(e.positions,i,a,e.basis,e.pbc):[Hc(e.positions,i,a)]).forEach(({from:l,to:u})=>r.push({from:l.add(s),to:u.add(s)}))}}return r}function Gs(e,t,n){if(!n)return{segments:Uc(e,t).map(l=>({...l,context:!1})),contextAtoms:[]};if(t.mode==="spacefill"||t.mode==="ribbon")return{segments:[],contextAtoms:[]};const r=new Set(e.visibleAtoms),o=new Set(e.images.map(so)),s=new Map,i=new Map,a=e.bonds.filter(([l,u])=>r.has(l)&&r.has(u)).map(([l,u])=>({a:l,b:u,shift:t.wrap==="atom"||t.wrap==="unwrapped"?qc(e.positions,l,u,e.basis,e.pbc):[0,0,0]})),c=(l,u,f,m)=>{const g=`${l}:${so(u)}`,p=`${f}:${so(m)}`,x=go[l]?Math.round(c.getComponent(l)-a.getComponent(l)):0)}function Gf(e,t,n){return n.length>1||n.some(o=>o.some(s=>s!==0))||e===0||t<=e*3.2}function Hf(e){if(e.length===0)return{count:0,span:[0,0,0]};const t=[...e[0]],n=[...e[0]];for(const r of e.slice(1))for(let o=0;o<3;o+=1)t[o]=Math.min(t[o],r[o]),n[o]=Math.max(n[o],r[o]);return{count:e.length,span:[n[0]-t[0],n[1]-t[1],n[2]-t[2]]}}function Kf(e,t,n){const r=le(t,["positions","position","pos","coordinates","coords"]),o=Math.min(e.topology.atom_count,Math.floor((r?.length??0)/3)),s=Mi(e,e.topology.atom_count),i=new Set,a=new Set,c=new Map((e.topology.residues??[]).map(p=>[p.index,p]));for(const[p,x]of Ai(e,e.topology.atom_count)){x.forEach(b=>a.add(b));const A=c.get(p);A&&(A.category==="water"||Rf.has((A.name??"").trim().toUpperCase()))&&ja(x,s)&&x.forEach(b=>i.add(b))}if(!r||o===0)return i;const l=Et(le(t,["cell","cell_vectors","box"])),u=cn(t,l),f=Bc(t,o)??Dr(r,o,l,u,[0,0,0]),m=n??Si(e.topology.bonds,o),g=n!==void 0||m.length>0?m:Kc(f,s,o,l,u);for(const p of Wc(o,g))p.some(x=>a.has(x))||ja(p,s)&&cd(p,g,s)&&p.forEach(x=>i.add(x));return i}function Wf(e){const t=e.topology.atom_names;if(!e.topology.residues?.length)return[];const n=new Map(Ai(e,e.topology.atom_count)),r=Mi(e,e.topology.atom_count),o=Si(e.topology.bonds,e.topology.atom_count),s=hd(e.topology.atom_count,o),i=[];for(const a of[...e.topology.residues].sort((c,l)=>c.index-l.index)){if(a.category!=="amino-acid"&&!Tf.has((a.name??"").trim().toUpperCase()))continue;const c=n.get(a.index)??[],l=t?.length?dd(a.index,c,t,r):null,u=e.topology.bond_source==="topology"?md(a.index,c,r,s):null;(l??u)&&i.push({...l??u,chainId:a.chain_id??null,segmentId:a.segment_id??null,sequenceNumber:a.sequence_number??null,insertionCode:a.insertion_code??null})}return pd(i,o).flatMap((a,c)=>a.map(l=>({...l,runIndex:c})))}function wi(e,t,n=1){const r=Number.isFinite(n)?Math.max(.1,n):1;return t==="spacefill"?wd(e)*r:t==="licorice"?.22*r:t==="lines"?.075*r:t==="ribbon"?0:t==="polyhedra"?Math.max(.18,(Gn[e]??.78)*.3)*r:Math.max(.22,(Gn[e]??.78)*.43)*r}function Xf(e,t,n,r,o,s=new F){const i=new F().fromArray(e,t*3),a=new F().fromArray(e,n*3);if(!r||!o.some(Boolean))return[{from:i,to:a}];const c=Gt(i.clone().sub(s),r),l=Gt(a.clone().sub(s),r).sub(c),u=$r(l,r,o),f=[0,1],m=[c.x,c.y,c.z],g=[u.x,u.y,u.z];for(let A=0;A<3;A+=1){if(!o[A]||Math.abs(g[A])<1e-12)continue;const b=m[A]+g[A],j=Math.min(m[A],b),k=Math.max(m[A],b),v=Math.ceil(j-.5+1e-9),S=Math.floor(k-.5-1e-9);for(let I=v;I<=S;I+=1){const E=(I+.5-m[A])/g[A];E>1e-9&&E<1-1e-9&&f.push(E)}}f.sort((A,b)=>A-b);const p=f.filter((A,b)=>b===0||Math.abs(A-f[b-1])>1e-8),x=[];for(let A=0;A+11e-10&&x.push({from:S,to:I})}return x}function qt(e,t){return t?Ft(new F(...e),t):new F}function Gc(e,t,n=new F){const r=[];for(let o=0;o<=1;o+=1)for(let s=0;s<=1;s+=1)for(let i=0;i<=1;i+=1)r.push(qt([t[0]+o,t[1]+s,t[2]+i],e).add(n).addScaledVector(e.vectors[0],-.5).addScaledVector(e.vectors[1],-.5).addScaledVector(e.vectors[2],-.5));return r}function Un(e,t,n,r){if(!n||!r.some(Boolean))return t.clone();const o=Gt(e,n),s=$r(Gt(t,n).sub(o),n,r);return e.clone().add(Ft(s,n))}function Hc(e,t,n){return{from:new F().fromArray(e,t*3),to:new F().fromArray(e,n*3)}}function Yf(e,t,n,r,o){const s=Hc(e,t,n),i=qc(e,t,n,r,o);if(i.every(u=>u===0))return[s];const a=qt(i,r),c=s.from.clone().add(s.to).add(a).multiplyScalar(.5),l=s.to.clone().add(s.from).sub(a).multiplyScalar(.5);return[{from:s.from,to:c},{from:s.to,to:l}]}function Qf(e,t,n,r,o,s,i,a){if(i==="unwrapped"){const l=Mo(e,["unwrapped_image_shifts","unwrapped_images"]),u=le(e,["unwrapped_positions","unwrapped_position"]),f=u&&u.length>=n*3?new Float32Array(u.subarray(0,n*3)):l&&l.length>=n*3?Hs(t,l,n,r,o):new Float32Array(t.subarray(0,n*3));return{positions:f,baseImages:va(l,t,f,n,r,o)}}if(i==="none")return{positions:new Float32Array(t.subarray(0,n*3)),baseImages:new Int32Array(n*3)};if(i==="molecule"){const l=id(t,n,r,o,s.moleculeGroups,s.bonds,a);return{positions:l,baseImages:Ks(t,l,n,r,o)}}if(Jf(a)){const l=le(e,["centered_positions","centered_position"]),u=Mo(e,["centered_image_shifts","centered_images"]);if(l&&l.length>=n*3||u&&u.length>=n*3){const f=l&&l.length>=n*3?new Float32Array(l.subarray(0,n*3)):Hs(t,u,n,r,o);return{positions:f,baseImages:va(u,t,f,n,r,o)}}}const c=Dr(t,n,r,o,a);return{positions:c,baseImages:Ks(t,c,n,r,o)}}function Hs(e,t,n,r,o){const s=new Float32Array(e.subarray(0,n*3));if(!r)return s;const i=new F;for(let a=0;a=r*3?new Int32Array(e.subarray(0,r*3)):Ks(t,n,r,o,s)}function Zf(e){return!e||e.length!==3?[0,0,0]:e.map(t=>Number.isFinite(t)?t:0)}function Jf(e){return e[0]===0&&e[1]===0&&e[2]===0}function ed(e,t){const n=t??[!1,!1,!1];if(!e||!n.some(Boolean))return new Ls().identity();const r=td(e),o=new Ls().set(0,0,0,0,0,0,0,0,0),s=o.elements;return r.forEach((i,a)=>{const c=n[a]?-1:1;s[0]+=c*i.x*i.x,s[1]+=c*i.y*i.x,s[2]+=c*i.z*i.x,s[3]+=c*i.x*i.y,s[4]+=c*i.y*i.y,s[5]+=c*i.z*i.y,s[6]+=c*i.x*i.z,s[7]+=c*i.y*i.z,s[8]+=c*i.z*i.z}),o}function td(e){const t=e.vectors[0].clone().normalize(),n=e.vectors[1].clone().addScaledVector(t,-e.vectors[1].dot(t));n.lengthSq()<1e-20&&n.copy(Math.abs(t.x)<.8?new F(1,0,0):new F(0,1,0)).addScaledVector(t,-n.dot(t)),n.normalize();const r=new F().crossVectors(t,n).normalize();return r.dot(e.vectors[2])<0&&r.negate(),n.crossVectors(r,t).normalize(),[t,n,r]}function nd(e,t,n,r){const o=new Float32Array(e.subarray(0,t*3));if(n.equals(new Ls().identity()))return o;const s=new F;for(let i=0;iGt(l.fromArray(e,g*3),n)),f=Array.from({length:t},()=>[]);s.forEach(([m,g])=>{f[m]?.push(g),f[g]?.push(m)});for(const[,m]of o){if(m.length===0)continue;m.forEach(v=>c.add(v));const g=new Set(m),p=new Map,x=m[0];p.set(x,u[x].clone());const A=[x];let b=0;for(;bj.add(p.get(v))),j.multiplyScalar(1/m.length);const k=new F(r[0]?qe(j.x-i[0]):0,r[1]?qe(j.y-i[1]):0,r[2]?qe(j.z-i[2]):0);m.forEach(v=>Ft(p.get(v).clone().sub(k),n).toArray(a,v*3))}for(let m=0;m0?Wc(t,n).map((r,o)=>[o,r]):Ai(e,t)}function Dr(e,t,n,r,o){const s=new Float32Array(e.subarray(0,t*3));if(!n||!r.some(Boolean))return s;const i=new F;for(let a=0;ao.index)),r=new Map;return e.topology.atom_residue_index.slice(0,t).forEach((o,s)=>{if(!Number.isInteger(o)||!n.has(o))return;const i=r.get(o)??[];i.push(s),r.set(o,i)}),[...r.entries()].sort(([o],[s])=>o-s)}function Kc(e,t,n,r,o){if(n>5e4)return[];const s=t.reduce((f,m)=>Math.max(f,Gn[m]??.78),.78),i=Math.max(1.4,s*2.5),a=new Map,c=ld(r,o,Math.max(s*2*1.22,Ca));if(!c||n*c.length*27>$f)return[];const l=[];let u=0;for(let f=0;fCf)return[];for(const[L,ee,te,oe]of $){const C=Math.hypot(v-ee,S-te,I-oe),N=A.get(L);(N===void 0||C.2&&v<=_&&(l.push([k,f]),l.length>Ef))return[]}const b=`${Math.floor(g/i)}:${Math.floor(p/i)}:${Math.floor(x/i)}`,j=a.get(b)??[];j.push([f,g,p,x]),a.set(b,j)}return l}function Wc(e,t){const n=Array.from({length:e},()=>[]);t.forEach(([s,i])=>{s<0||i<0||s>=e||i>=e||(n[s].push(i),n[i].push(s))});const r=new Uint8Array(e),o=[];for(let s=0;s0;){const c=a.pop();i.push(c);for(const l of n[c])r[l]||(r[l]=1,a.push(l))}o.push(i)}return o}function ja(e,t){let n=0,r=0,o=0;for(const s of e)if(t[s]===8)n+=1;else if(t[s]===1)r+=1;else if(!t[s])o+=1;else return!1;return n===1&&r===2&&o<=1}function cd(e,t,n){const r=new Set(e),o=e.find(i=>n[i]===8);if(o===void 0)return!1;let s=0;for(const[i,a]of t)(i===o&&r.has(a)&&n[a]===1||a===o&&r.has(i)&&n[i]===1)&&(s+=1);return s===2}function Si(e,t){if(!e||e.length===0)return[];const n=[];if(typeof e[0]=="number"){const r=e;for(let o=0;o+1=0&&n>=0&&tyd[e.topology.symbols?.[r]??""]??0)}function ld(e,t,n){if(!e||!t.some(Boolean))return[new F];const r=[],o=e.reciprocal.map((s,i)=>t[i]?Math.max(1,Math.ceil(s.length()*n-1e-12)):0);if(o.reduce((s,i)=>s*(i*2+1),1)>_f)return null;for(let s=-o[0];s<=o[0];s+=1)for(let i=-o[1];i<=o[1];i+=1)for(let a=-o[2];a<=o[2];a+=1)r.push(Ft(new F(s,i,a),e));return r}function Gt(e,t){return new F(e.dot(t.reciprocal[0]),e.dot(t.reciprocal[1]),e.dot(t.reciprocal[2]))}function Ft(e,t){return new F().addScaledVector(t.vectors[0],e.x).addScaledVector(t.vectors[1],e.y).addScaledVector(t.vectors[2],e.z)}function qe(e){const t=Math.floor(e);return t+(e-t>=.5?1:0)}function $r(e,t,n){const r=e.clone();n[0]&&(r.x-=qe(r.x)),n[1]&&(r.y-=qe(r.y)),n[2]&&(r.z-=qe(r.z));const o=[0,1,2].filter(p=>n[p]);if(o.length===0)return r;const s=[],i=Array.from({length:o.length},()=>Array(o.length).fill(0));o.forEach((p,x)=>{const A=t.vectors[p].clone();for(let b=0;bp.dot(a)),l=Array(o.length).fill(0);for(let p=o.length-1;p>=0;p-=1){let x=c[p];for(let A=p+1;A{if(p<0){xg.setComponent(p,g.getComponent(p)+u[x])),g}function ud(e,t,n){let r=0;for(let o=0;o=t&&i<=n&&r.push(i),s>0&&a>=t&&a<=n&&r.push(a)}return r}function dd(e,t,n,r){const o=new Map(t.map(l=>[bd(n[l]),l])),s=o.get("N"),i=o.get("CA"),a=o.get("C"),c=o.get("O");return s===void 0||i===void 0||a===void 0||c===void 0||r[s]!==7||r[i]!==6||r[a]!==6||r[c]!==8?null:{residueIndex:e,n:s,ca:i,c:a,o:c}}function md(e,t,n,r){const o=new Set(t),s=new Map;for(const l of t)if(n[l]===7){for(const u of r[l]??[])if(!(!o.has(u)||n[u]!==6))for(const f of r[u]??[]){if(!o.has(f)||f===l||n[f]!==6)continue;const m=(r[f]??[]).filter(g=>o.has(g)&&n[g]===8).sort((g,p)=>g-p);m.length!==0&&s.set(`${l}:${u}:${f}`,{residueIndex:e,n:l,ca:u,c:f,o:m[0]})}}let i=[...s.values()];const a=i.filter(({c:l})=>(r[l]??[]).some(u=>!o.has(u)&&n[u]===7));a.length>0&&(i=a);const c=i.filter(({c:l})=>!(r[l]??[]).some(u=>n[u]===1));return c.length>0&&(i=c),i.length===1?i[0]:null}function hd(e,t){const n=Array.from({length:e},()=>[]);return t.forEach(([r,o])=>{n[r]?.push(o),n[o]?.push(r)}),n}function pd(e,t){const n=new Set(t.flatMap(([i,a])=>[`${i}:${a}`,`${a}:${i}`])),r=[];let o=[];const s=()=>{o.length>=3&&r.push(o),o=[]};for(const i of e){const a=o[o.length-1],c=!a||i.residueIndex>a.residueIndex,l=!a||i.chainId===a.chainId,u=!a||i.segmentId===a.segmentId,f=!a||gd(a,i),m=!a||t.length===0||n.has(`${a.c}:${i.n}`),g=!a||a.sequenceNumber!=null&&i.sequenceNumber!=null||i.residueIndex===a.residueIndex+1;(!c||!l||!u||!f||!m||!g)&&s(),o.push(i)}return s(),r}function gd(e,t){if(e.sequenceNumber==null||t.sequenceNumber==null||t.sequenceNumber===e.sequenceNumber+1)return!0;if(t.sequenceNumber!==e.sequenceNumber)return!1;const n=(e.insertionCode??"").trim(),r=(t.insertionCode??"").trim();return r?n?r.length===1&&n.length===1&&r.charCodeAt(0)===n.charCodeAt(0)+1:r==="A":!1}function bd(e){return(e??"").trim().toUpperCase()}function Na(e){return Number.isFinite(e)?Math.max(-2,Math.min(2,Math.trunc(e))):0}function Fa(e){return Math.abs(e[0])+Math.abs(e[1])+Math.abs(e[2])}function so(e){return`${e[0]}:${e[1]}:${e[2]}`}const yd={H:1,He:2,Li:3,Be:4,B:5,C:6,N:7,O:8,F:9,Ne:10,Na:11,Mg:12,Al:13,Si:14,P:15,S:16,Cl:17,Ar:18,K:19,Ca:20,Sc:21,Ti:22,V:23,Cr:24,Mn:25,Fe:26,Co:27,Ni:28,Cu:29,Zn:30,Ga:31,Ge:32,As:33,Se:34,Br:35,Kr:36,Rb:37,Sr:38,Y:39,Zr:40,Nb:41,Mo:42,Tc:43,Ru:44,Rh:45,Pd:46,Ag:47,Cd:48,In:49,Sn:50,Sb:51,Te:52,I:53,Xe:54,Cs:55,Ba:56,La:57,Ce:58,Pr:59,Nd:60,Pm:61,Sm:62,Eu:63,Gd:64,Tb:65,Dy:66,Ho:67,Er:68,Tm:69,Yb:70,Lu:71,Hf:72,Ta:73,W:74,Re:75,Os:76,Ir:77,Pt:78,Au:79,Hg:80,Tl:81,Pb:82,Bi:83,Po:84,At:85,Rn:86,Fr:87,Ra:88,Ac:89,Th:90,Pa:91,U:92,Np:93,Pu:94,Am:95,Cm:96,Bk:97,Cf:98,Es:99,Fm:100,Md:101,No:102,Lr:103,Rf:104,Db:105,Sg:106,Bh:107,Hs:108,Mt:109,Ds:110,Rg:111,Cn:112,Nh:113,Fl:114,Mc:115,Lv:116,Ts:117,Og:118},Gn={1:.31,2:.28,3:1.28,4:.96,5:.84,6:.76,7:.71,8:.66,9:.57,10:.58,11:1.66,12:1.41,13:1.21,14:1.11,15:1.07,16:1.05,17:1.02,18:1.06,19:2.03,20:1.76,21:1.7,22:1.6,23:1.53,24:1.39,25:1.39,26:1.32,27:1.26,28:1.24,29:1.32,30:1.22,31:1.22,32:1.2,33:1.19,34:1.2,35:1.2,36:1.16,37:2.2,38:1.95,39:1.9,40:1.75,41:1.64,42:1.54,43:1.47,44:1.46,45:1.42,46:1.39,47:1.45,48:1.44,49:1.42,50:1.39,51:1.39,52:1.38,53:1.39,54:1.4,55:2.44,56:2.15,57:2.07,58:2.04,59:2.03,60:2.01,61:1.99,62:1.98,63:1.98,64:1.96,65:1.94,66:1.92,67:1.92,68:1.89,69:1.9,70:1.87,71:1.87,72:1.75,73:1.7,74:1.62,75:1.51,76:1.44,77:1.41,78:1.36,79:1.36,80:1.32,81:1.45,82:1.46,83:1.48,84:1.4,85:1.5,86:1.5,87:2.6,88:2.21,89:2.15,90:2.06,91:2,92:1.96,93:1.9,94:1.87,95:1.8,96:1.69,97:1.68,98:1.68,99:1.65,100:1.67,101:1.73,102:1.76,103:1.61,104:1.57,105:1.49,106:1.43,107:1.41,108:1.34,109:1.29,110:1.28,111:1.21,112:1.22,113:1.36,114:1.43,115:1.62,116:1.75,117:1.65,118:1.57},Ca=.85,xd={1:1.2,2:1.4,3:1.82,4:1.53,5:1.92,6:1.7,7:1.55,8:1.52,9:1.47,10:1.54,11:2.27,12:1.73,13:1.84,14:2.1,15:1.8,16:1.8,17:1.75,18:1.88,19:2.75,20:2.31,21:2.58,22:2.46,23:2.42,24:2.45,25:2.45,26:2.44,27:2.4,28:2.4,29:2.38,30:2.39,31:2.32,32:2.29,33:1.88,34:1.82,35:1.86,36:2.25,37:3.21,38:2.84,39:2.75,40:2.52,41:2.56,42:2.45,43:2.44,44:2.46,45:2.44,46:2.15,47:2.53,48:2.49,49:2.43,50:2.42,51:2.47,52:1.99,53:2.04,54:2.06,55:3.48,56:3.03,57:2.98,58:2.88,59:2.92,60:2.95,61:2.9,62:2.9,63:2.87,64:2.83,65:2.79,66:2.87,67:2.81,68:2.83,69:2.79,70:2.8,71:2.74,72:2.63,73:2.53,74:2.57,75:2.49,76:2.48,77:2.41,78:2.29,79:2.32,80:2.45,81:2.47,82:2.6,83:2.54};function wd(e){return xd[e]??(Gn[e]??.9)+.8}const jn=1e-12,Ad=1e-12,Sd=1e-8,Ia=4096;function Md(e,t,n){jd(e),Xc(t);const r=ks(t);if(n==="replace")return[r];if(n!=="toggle")throw new TypeError(`Unknown selection mode: ${String(n)}`);const o=e.findIndex(s=>Nd(s,t));return o===-1?[...e.map(ks),r]:e.filter((s,i)=>i!==o).map(ks)}function kd(e,t,n){if(!Zc(e)||t.some(({image:s})=>s.some(i=>i!==0))&&!Fd(n))return null;const o=new Float64Array(t.length*3);for(let s=0;s=e.length/3)return null;const a=Jc(e,i.atom);if(a===null)return null;if(o.set(a,s*3),!!n)for(let c=0;c<3;c+=1){const l=i.image[c];o[s*3]+=l*n[c*3],o[s*3+1]+=l*n[c*3+1],o[s*3+2]+=l*n[c*3+2]}}return o}function Ws(e,t,n={}){const r=[...t],o=vd(r.length);if(o===null)return ft(r,"selection-size");if(new Set(r).size!==r.length)return ft(r,"duplicate-atoms");if(!Zc(e))return ft(r,"invalid-position");const s=[];for(const m of r){if(!Qc(m)||m>=e.length/3)return ft(r,"invalid-index");const g=Jc(e,m);if(g===null)return ft(r,"invalid-position");s.push(g)}const i=Ed(n);if(i===void 0)return ft(r,"invalid-periodic-context");const a=(m,g)=>$d(Ys(g,m),i);if(o==="distance"){const m=a(s[0],s[1]);return m===null?ft(r,"invalid-periodic-context"):Ms(o,r,an(m),"angstrom")}if(o==="angle"){const m=a(s[1],s[0]),g=a(s[1],s[2]);if(m===null||g===null)return ft(r,"invalid-periodic-context");const p=Cd(m,g);return p===null?ft(r,"degenerate-geometry"):Ms(o,r,p,"degree")}const c=a(s[1],s[0]),l=a(s[1],s[2]),u=a(s[2],s[3]);if(c===null||l===null||u===null)return ft(r,"invalid-periodic-context");const f=Id(c,l,u);return f===null?ft(r,"degenerate-geometry"):Ms(o,r,f,"degree")}function vd(e){return e===2?"distance":e===3?"angle":e===4?"dihedral":null}function Ms(e,t,n,r){return{ok:!0,kind:e,atomIndices:t,value:Math.abs(n)<=jn?0:n,unit:r}}function ft(e,t){return{ok:!1,atomIndices:e,reason:t}}function jd(e){const t=new Set;for(const n of e){Xc(n);const r=Xs(n);if(t.has(r))throw new TypeError("Atom selection contains duplicates");t.add(r)}}function Xc(e){if(!Yc(e))throw new RangeError("Atom selection must contain an atom and integer image")}function Yc(e){return Qc(e.atom)&&Array.isArray(e.image)&&e.image.length===3&&e.image.every(Number.isInteger)}function Nd(e,t){return Xs(e)===Xs(t)}function Xs(e){return`${e.atom}:${e.image[0]}:${e.image[1]}:${e.image[2]}`}function ks(e){return{atom:e.atom,image:[...e.image]}}function Fd(e){if(!e||!Number.isInteger(e.length)||e.length<9)return!1;for(let t=0;t<9;t+=1)if(!Number.isFinite(e[t]))return!1;return!0}function Qc(e){return Number.isInteger(e)&&e>=0}function Zc(e){return Number.isInteger(e.length)&&e.length>=0&&e.length%3===0}function Jc(e,t){const n=t*3,r=[e[n],e[n+1],e[n+2]];return r.every(Number.isFinite)?r:null}function Ys(e,t){return[e[0]-t[0],e[1]-t[1],e[2]-t[2]]}function Mn(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function an(e){return Math.hypot(e[0],e[1],e[2])}function Cd(e,t){const n=an(e),r=an(t);if(n<=jn||r<=jn)return null;const o=Mn(e,t)/(n*r);return nl(Math.acos(Td(o,-1,1)))}function Id(e,t,n){const r=an(t);if(r<=jn)return null;const o=jr(t,1/r),s=Ys(e,jr(o,Mn(e,o))),i=Ys(n,jr(o,Mn(n,o)));if(an(s)<=jn||an(i)<=jn)return null;const a=Mn(s,i),c=Mn(Rd(o,s),i);return nl(Math.atan2(c,a))}function Ed(e){if(e.mode!=="minimum-image")return null;if(e.pbc.length!==3||!e.pbc.every(s=>typeof s=="boolean"))return;if(!e.pbc.some(Boolean))return null;if(!Number.isInteger(e.cell.length)||e.cell.length<9)return;const t=[[e.cell[0],e.cell[1],e.cell[2]],[e.cell[3],e.cell[4],e.cell[5]],[e.cell[6],e.cell[7],e.cell[8]]];if(!t.flat().every(Number.isFinite))return;const n=t.filter((s,i)=>e.pbc[i]),r=[],o=Array.from({length:n.length},()=>Array(n.length).fill(0));for(let s=0;sMn(f,n)),o=Array(t.vectors.length).fill(0);let s=[...o],i=Ea(r,t.r,s);for(let f=t.vectors.length-1;f>=0;f-=1){let m=r[f];for(let p=f+1;p{if(f<0)return $a(m,i)&&(s=[...o],i=m),!0;let g=r[f];for(let v=f+1;vIa-c)return!1;for(const v of _d(A,j,k)){if(c+=1,c>Ia)return!1;o[f]=v;const S=g-t.r[f][f]*v,I=m+S*S;if(I<=i+p&&!l(f-1,I))return!1}return!0};if(!l(t.vectors.length-1,0))return null;const u=[...e];return t.vectors.forEach((f,m)=>tl(u,f,s[m])),u}function Ea(e,t,n){let r=0;for(let o=0;o=t&&i<=n&&(yield i,o+=1),s>0&&a>=t&&a<=n&&(yield a,o+=1)}}function $a(e,t){return e({atom:B,image:[...V]})),l=cl(c.length),u=ll(l),f=ul(e,c,l),m=Array(t).fill(null),g=Array(t).fill(null),p=Array(t).fill(null),x=Object.freeze(Array.from({length:t},(B,V)=>V)),A=Array(t).fill(null),b=Object.freeze(ki(t)),j=new Set;let k=0,v=0,S=0;const I=t>=1e4?ol:rl,E=Math.max(1,Math.ceil(t/I));a&&a(vs(f,l,u,_r(),b,m,x,A,0,!1));for(let B=0;B=3))throw new Error("Trajectory frames could not be loaded",{cause:$});m[B]=null}k=B+1,a&&k=E)&&(a(vs(f,l,u,_r(),b,m,x,A,k,!1)),v=k)}sn(s);const{axis:_,xValues:U}=il(g,p,j),G=vs(f,l,u,_,U,m,x,A,k,!0);return a?.(G),G}async function Od({manifest:e,frameCount:t,definitions:n,wrap:r,signal:o,loadFrame:s,title:i,onProgress:a}){Kd(e,t,n,r),sn(o);const c=n.map(G=>{const B=G.selections.map(({atom:$,image:L})=>({atom:$,image:[...L]})),V=cl(B.length);return{id:G.id.trim(),label:G.label?.trim()||ul(e,B,V),selections:B,minimumImage:G.minimumImage,kind:V,unit:ll(V),values:Array(t).fill(null)}}),l=c[0].unit;if(c.some(G=>G.unit!==l))throw new TypeError("Compared measurements must use the same unit");const u=i?.trim()||(c.length===1?c[0].label:"Measurement comparison"),f=Array(t).fill(null),m=Array(t).fill(null),g=Object.freeze(Array.from({length:t},(G,B)=>B)),p=Array(t).fill(null),x=Object.freeze(ki(t)),A=new Set;let b=0,j=0,k=0;const v=t>=1e4?ol:rl,S=Math.max(1,Math.ceil(t/v)),I=(G,B,V)=>Gd(u,l,G,B,g,p,c,b,V);a?.(I(_r(),x,!1));for(let G=0;G=3))throw new Error("Trajectory frames could not be loaded",{cause:V});for(const $ of c)$.values[G]=null}b=G+1,a&&b=S)&&(a(I(_r(),x,!1)),j=b)}sn(o);const{axis:E,xValues:_}=il(f,m,A),U=I(E,_,!0);return a?.(U),U}function Ld(e){const t=ct(e.axis.label,e.axis.unit),n=ct(zr(e.kind),Bo(e.unit)),r=[[t,n]],o=Math.min(e.xValues.length,e.values.length);for(let s=0;ss.map(ml).join(",")).join(` -`)} -`}function Ra(e,t){const n=new Set(e.lines.map(({kind:r})=>r));return Object.freeze({requestId:t,kind:e.lines.length>1?"comparison":"measurement",title:e.title,xLabel:e.axis.label,xUnit:e.axis.unit,yLabel:n.size===1?zr(e.lines[0].kind):"Measurement",yUnit:Bo(e.unit),xValues:e.xValues,frameIndices:e.frameIndices,frameKeys:e.frameKeys,lines:Object.freeze(e.lines.map((r,o)=>Object.freeze({id:r.id,label:r.label,values:r.values,color:Co[o%Co.length],selection:r.selections,minimumImage:r.minimumImage,discontinuity:r.unit==="degree"?180:void 0}))),loadedCount:e.loadedCount,totalCount:e.frameIndices.length,complete:e.complete})}function Bd(e){Ni(e);const t=e.frameIndices?.length===e.xValues.length?e.frameIndices:void 0,n=e.frameKeys?.length===e.xValues.length?e.frameKeys:void 0,r=[[...t?["Frame index"]:[],...n?["Source","Segment index","Source frame index"]:[],ct(e.xLabel,e.xUnit),...e.lines.map(o=>ct(o.label,e.yUnit))]];for(let o=0;own(i.values[o],10))])}return`${r.map(o=>o.map(ml).join(",")).join(` -`)} -`}function Dd(e,t={}){Ni(e);const n=No(t.width??1200,"width"),r=No(t.height??720,"height"),s={top:82+Math.ceil(e.lines.length/2)*22,right:48,bottom:86,left:96},i=Math.max(1,n-s.left-s.right),a=Math.max(1,r-s.top-s.bottom),c=Nn(e.xValues.filter(Number.isFinite)),l=pl(e),u=k=>s.left+(k-c[0])/(c[1]-c[0])*i,f=k=>s.top+(1-(k-l[0])/(l[1]-l[0]))*a,m=ln(c[0],c[1],5),g=ln(l[0],l[1],5),p=ct(e.xLabel,e.xUnit),x=ct(e.yLabel,e.yUnit),A=gl(e),b=e.lines.map((k,v)=>{const S=Qs(k.color,v),I=Yd(e.xValues,k.values,k.discontinuity,u,f);return I?``:""}),j=b.some(Boolean);return['',``,'',_e(e.title),"",'',_e(`${e.title}; ${A}.`),"",'',`${_e(e.title)}`,`${_e(A)}`,...e.lines.map((k,v)=>{const S=v%2,I=Math.floor(v/2),E=s.left+S*Math.max(1,i/2),_=83+I*22,U=Qs(k.color,v);return[``,`${_e(k.label)}`].join("")}),...g.flatMap(k=>{const v=Ct(f(k));return[``,`${_e(un(k))}`]}),...m.flatMap(k=>{const v=Ct(u(k));return[``,`${_e(un(k))}`]}),``,``,...b,j?"":`No valid data`,`${_e(p)}`,`${_e(x)}`,""].join("")}function zd(e,t={}){Ni(e);const n=Fo(t.width??720,"width"),r=Fo(t.height??432,"height"),s={top:58+Math.ceil(e.lines.length/2)*14,right:30,bottom:54,left:62},i=Math.max(1,n-s.left-s.right),a=Math.max(1,r-s.top-s.bottom),c=Nn(e.xValues.filter(Number.isFinite)),l=pl(e),u=A=>s.left+(A-c[0])/(c[1]-c[0])*i,f=A=>s.top+(1-(A-l[0])/(l[1]-l[0]))*a,m=ln(c[0],c[1],5),g=ln(l[0],l[1],5),p=["1 1 1 rg",`0 0 ${T(n)} ${T(r)} re f`];for(const A of g){const b=r-f(A);p.push("0.886 0.91 0.902 RG","0.6 w",`${T(s.left)} ${T(b)} m ${T(n-s.right)} ${T(b)} l S`,nt(un(A),s.left-8,b-3.2,{align:"right",color:[.35,.4,.388],size:8}))}for(const A of m){const b=u(A);p.push("0.929 0.945 0.941 RG","0.6 w",`${T(b)} ${T(s.bottom)} m ${T(b)} ${T(r-s.top)} l S`,nt(un(A),b,s.bottom-17,{align:"center",color:[.35,.4,.388],size:8}))}p.push("0.518 0.565 0.553 RG","0.8 w",`${T(s.left)} ${T(s.bottom)} m ${T(n-s.right)} ${T(s.bottom)} l S`,`${T(s.left)} ${T(s.bottom)} m ${T(s.left)} ${T(r-s.top)} l S`);let x=!1;return e.lines.forEach((A,b)=>{const j=Qd(e.xValues,A.values,A.discontinuity,u,f,r);if(!j)return;x=!0;const[k,v,S]=Pa(A.color,b);p.push(`${T(k)} ${T(v)} ${T(S)} RG`,"1.7 w","1 J 1 j",j,"S")}),x||p.push(nt("No valid data",s.left+i*.5,s.bottom+a*.5,{align:"center",color:[.482,.529,.518],size:9})),p.push(nt(e.title,s.left,r-27,{color:[.09,.137,.129],font:"F2",size:15}),nt(gl(e),s.left,r-42,{color:[.392,.439,.427],size:8.5})),e.lines.forEach((A,b)=>{const j=b%2,k=Math.floor(b/2),v=s.left+j*Math.max(1,i/2),S=r-58-k*14,[I,E,_]=Pa(A.color,b);p.push(`${T(I)} ${T(E)} ${T(_)} RG`,"2 w",`${T(v)} ${T(S)} m ${T(v+14)} ${T(S)} l S`,nt(A.label,v+20,S-3,{color:[.224,.275,.259],size:8}))}),p.push(nt(ct(e.xLabel,e.xUnit),s.left+i*.5,17,{align:"center",color:[.161,.212,.2],size:9}),bl(ct(e.yLabel,e.yUnit),18,s.bottom+a*.5,{color:[.161,.212,.2],size:9})),xl(n,r,p.filter(Boolean).join(` -`),e.title)}function Vd(e,t={}){const n=No(t.width??1200,"width"),r=No(t.height??720,"height"),o={top:78,right:48,bottom:86,left:96},s=Math.max(1,n-o.left-o.right),i=Math.max(1,r-o.top-o.bottom),a=hl(e),c=Nn(a.map(({x:k})=>k)),l=Nn(a.map(({y:k})=>k)),u=k=>o.left+(k-c[0])/(c[1]-c[0])*s,f=k=>o.top+(1-(k-l[0])/(l[1]-l[0]))*i,m=ln(c[0],c[1],5),g=ln(l[0],l[1],5),p=Wd(e,u,f),x=Xd(e,u,f),A=ct(e.axis.label,e.axis.unit),b=ct(zr(e.kind),Bo(e.unit)),j=e.complete?`${e.values.filter(k=>k!==null).length} valid frames`:`${e.loadedCount} frames loaded`;return['',``,'',_e(e.title),"",'',_e(`${e.title}; ${j}.`),"",'',`${_e(e.title)}`,`${_e(j)}`,...g.flatMap(k=>{const v=Ct(f(k));return[``,`${_e(un(k))}`]}),...m.flatMap(k=>{const v=Ct(u(k));return[``,`${_e(un(k))}`]}),``,``,p?``:`No valid measurements`,x,`${_e(A)}`,`${_e(b)}`,""].join("")}function Ud(e,t={}){const n=Fo(t.width??720,"width"),r=Fo(t.height??432,"height"),o={top:52,right:30,bottom:54,left:62},s=Math.max(1,n-o.left-o.right),i=Math.max(1,r-o.top-o.bottom),a=hl(e),c=Nn(a.map(({x:k})=>k)),l=Nn(a.map(({y:k})=>k)),u=k=>o.left+(k-c[0])/(c[1]-c[0])*s,f=k=>o.top+(1-(k-l[0])/(l[1]-l[0]))*i,m=ln(c[0],c[1],5),g=ln(l[0],l[1],5),p=ct(e.axis.label,e.axis.unit),x=ct(zr(e.kind),Bo(e.unit)),A=e.complete?`${e.values.filter(k=>k!==null).length} valid frames`:`${e.loadedCount} frames loaded`,b=["1 1 1 rg",`0 0 ${T(n)} ${T(r)} re f`];for(const k of g){const v=r-f(k);b.push("0.886 0.91 0.902 RG","0.6 w",`${T(o.left)} ${T(v)} m ${T(n-o.right)} ${T(v)} l S`,nt(un(k),o.left-8,v-3.2,{align:"right",color:[.35,.4,.388],size:8}))}for(const k of m){const v=u(k);b.push("0.929 0.945 0.941 RG","0.6 w",`${T(v)} ${T(r-o.top)} m ${T(v)} ${T(o.bottom)} l S`,nt(un(k),v,o.bottom-17,{align:"center",color:[.35,.4,.388],size:8}))}b.push("0.518 0.565 0.553 RG","0.8 w",`${T(o.left)} ${T(o.bottom)} m ${T(n-o.right)} ${T(o.bottom)} l S`,`${T(o.left)} ${T(r-o.top)} m ${T(o.left)} ${T(o.bottom)} l S`);const j=Zd(e,u,f,r);if(j){b.push("0.075 0.498 0.471 RG","1.7 w","1 J 1 j",j,"S");for(const k of Jd(e,u,f,r))b.push("0.075 0.498 0.471 rg",em(k.x,k.y,2),"f")}else b.push(nt("No valid measurements",o.left+s*.5,o.bottom+i*.5,{align:"center",color:[.482,.529,.518],size:10}));return b.push(nt(e.title,o.left,r-27,{color:[.09,.137,.129],font:"F2",size:15}),nt(A,o.left,r-42,{color:[.392,.439,.427],size:8.5}),nt(p,o.left+s*.5,17,{align:"center",color:[.161,.212,.2],size:9}),bl(x,18,o.bottom+i*.5,{color:[.161,.212,.2],size:9})),xl(n,r,b.filter(Boolean).join(` -`),e.title)}function sl(e,t,n,r,o){const s=le(e,["cell","cell_vectors","box"]),i=Et(s),a=qd(e,n),c=kd(t,r,i?s:null);if(!c)return null;const l=r.map((f,m)=>m);if(o&&(!s||!a.some(Boolean)))return null;const u=o?Ws(c,l,{mode:"minimum-image",cell:s,pbc:a}):Ws(c,l);return u.ok&&Number.isFinite(u.value)?u.value:null}function qd(e,t){const n=e.header.pbc;return Array.isArray(n)&&n.length===3?[!!n[0],!!n[1],!!n[2]]:[t[0],t[1],t[2]]}function il(e,t,n){if(Ta(e)&&n.size<=1){const r=[...n][0];return{axis:r?{kind:"time",label:"Time",unit:r}:{kind:"time",label:"Time"},xValues:e}}return Ta(t)?{axis:{kind:"step",label:"Step"},xValues:t}:{axis:_r(),xValues:ki(e.length)}}function Ta(e){if(e.length<2||e.some(t=>t===null||!Number.isFinite(t)))return!1;for(let t=1;tu?Object.freeze({...u}):null)),loadedCount:c,complete:l})}function Gd(e,t,n,r,o,s,i,a,c){return Object.freeze({title:e,unit:t,axis:Object.freeze({...n}),xValues:Object.freeze([...r]),frameIndices:o,frameKeys:Object.freeze(s.map(l=>l?Object.freeze({...l}):null)),lines:Object.freeze(i.map(l=>Object.freeze({id:l.id,label:l.label,kind:l.kind,unit:l.unit,selections:Object.freeze(l.selections.map(({atom:u,image:f})=>Object.freeze({atom:u,image:Object.freeze([...f])}))),minimumImage:l.minimumImage,values:Object.freeze([...l.values])}))),loadedCount:a,complete:c})}function cl(e){return e===2?"distance":e===3?"angle":"dihedral"}function ll(e){return e==="distance"?"angstrom":"degree"}function ul(e,t,n){return`${zr(n)} · ${t.map(r=>Hd(e,r)).join("–")}`}function Hd(e,t){const n=e.topology.symbols?.[t.atom]??rm[e.topology.atomic_numbers?.[t.atom]??0]??"X",r=t.image.map((s,i)=>{if(s===0)return"";const a=s>0?"+":"−",c=Math.abs(s)===1?"":Math.abs(s);return`${a}${c}${"abc"[i]}`}).join(""),o=`${n}${t.atom+1}`;return r?`${o} (${r})`:o}function fl(e,t,n,r){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("Frame count must be a non-negative integer");if(t>e.frame_count)throw new RangeError("Frame count exceeds the trajectory manifest");if(n.length<2||n.length>4)throw new RangeError("A measurement needs two to four selected atoms");if(!["atom","molecule","unwrapped","none"].includes(r))throw new TypeError("Unknown coordinate wrapping mode")}function Kd(e,t,n,r){if(n.length<1||n.length>_a)throw new RangeError(`A comparison needs one to ${_a} measurements`);const o=new Set;for(const s of n){const i=s.id.trim();if(!i)throw new TypeError("Each compared measurement needs an id");if(o.has(i))throw new TypeError(`Duplicate measurement id: ${i}`);o.add(i),fl(e,t,s.selections,r)}}function dl(e){return!e||typeof e.source_id!="string"||!e.source_id||!Number.isSafeInteger(e.source_index)||e.source_index<0||!Number.isSafeInteger(e.segment_index)||e.segment_index<0?null:{source_id:e.source_id,source_index:e.source_index,segment_index:e.segment_index,step:typeof e.step=="number"&&Number.isFinite(e.step)?e.step:null,time:typeof e.time=="number"&&Number.isFinite(e.time)?e.time:null,time_unit:typeof e.time_unit=="string"&&e.time_unit.trim()?e.time_unit.trim():null}}function _r(){return{kind:"frame",label:"Frame"}}function ki(e){return Array.from({length:e},(t,n)=>n+1)}function sn(e){if(e.aborted)throw vi(e)}function vi(e,t){return e.reason!==void 0?e.reason:ji(t)?t:new DOMException("The operation was aborted","AbortError")}function ji(e){return e instanceof DOMException?e.name==="AbortError":!!(e&&typeof e=="object"&&"name"in e&&e.name==="AbortError")}function wn(e,t){return typeof e=="number"&&Number.isFinite(e)?String(Number(e.toPrecision(t))):""}function ml(e){return/[",\r\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function ct(e,t){return t?`${e} [${t}]`:e}function Bo(e){return e==="angstrom"?"Å":"°"}function zr(e){return`${e[0].toUpperCase()}${e.slice(1)}`}function hl(e){const t=Math.min(e.xValues.length,e.values.length),n=[];for(let r=0;r180&&(s=!1),o.push(`${s?"L":"M"}${Ct(t(c))} ${Ct(n(l))}`),s=!0,i=l}return o.join("")}function Xd(e,t,n){const r=Math.min(e.xValues.length,e.values.length),o=i=>i<0||i>=r?!1:Number.isFinite(e.xValues[i])&&typeof e.values[i]=="number"&&Number.isFinite(e.values[i]),s=[];for(let i=0;i`);return s.join("")}function jo(e,t,n){if(t<0||n>=Math.min(e.xValues.length,e.values.length))return!1;const r=e.values[t],o=e.values[n];return!Number.isFinite(e.xValues[t])||!Number.isFinite(e.xValues[n])||typeof r!="number"||typeof o!="number"||!Number.isFinite(r)||!Number.isFinite(o)?!1:e.unit!=="degree"||Math.abs(o-r)<=180}function Nn(e){if(e.length===0)return[0,1];let t=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;for(const r of e)t=Math.min(t,r),n=Math.max(n,r);if(t===n){const r=Math.max(Math.abs(t)*.05,.5);t-=r,n+=r}else{const r=(n-t)*.04;t-=r,n+=r}return[t,n]}function pl(e){const t=Nn(e.lines.flatMap(({values:n})=>n.filter(r=>typeof r=="number"&&Number.isFinite(r))));return e.yFloor===void 0||!Number.isFinite(e.yFloor)?t:[e.yFloor,t[1]>e.yFloor?t[1]:e.yFloor+1]}function ln(e,t,n){return Array.from({length:n},(r,o)=>e+(t-e)*o/Math.max(n-1,1))}function un(e){const t=Math.abs(e);return t!==0&&(t>=1e4||t<.001)?e.toExponential(2):new Intl.NumberFormat("en",{maximumFractionDigits:4}).format(e)}function Ct(e){return Number(e.toFixed(3)).toString()}function Ni(e){if(e.lines.length<1||e.lines.length>32)throw new RangeError("A plot needs one to 32 series");if(!e.title.trim()||!e.xLabel.trim()||!e.yLabel.trim())throw new TypeError("Plot title and axis labels are required")}function Yd(e,t,n,r,o){const s=[];let i=!1,a=null;const c=typeof n=="number"&&Number.isFinite(n)?Math.abs(n):null;for(let l=0;lc&&(i=!1),s.push(`${i?"L":"M"}${Ct(r(u))} ${Ct(o(f))}`),i=!0,a=f}return s.join("")}function Qd(e,t,n,r,o,s){const i=[];let a=!1,c=null;const l=typeof n=="number"&&Number.isFinite(n)?Math.abs(n):null;for(let u=0;ul&&(a=!1),i.push(`${T(r(f))} ${T(s-o(m))} ${a?"l":"m"}`),a=!0,c=m}return i.join(` -`)}function gl(e){const t=e.complete?`${e.lines.length} series · ${e.totalCount.toLocaleString()} points`:`${Math.max(0,e.loadedCount).toLocaleString()} / ${Math.max(0,e.totalCount).toLocaleString()} points`;return e.context?`${t} · ${e.context}`:t}function Qs(e,t){const n=e?.trim();return n&&/^#[0-9a-f]{6}$/i.test(n)?n:Co[t%Co.length]}function Pa(e,t){const n=Qs(e,t);return[Number.parseInt(n.slice(1,3),16)/255,Number.parseInt(n.slice(3,5),16)/255,Number.parseInt(n.slice(5,7),16)/255]}function _e(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function No(e,t){if(!Number.isSafeInteger(e)||e<=0)throw new RangeError(`SVG ${t} must be a positive integer`);return e}function Fo(e,t){if(!Number.isSafeInteger(e)||e<=0||e>14400)throw new RangeError(`PDF ${t} must be a positive integer no larger than 14400 points`);return e}function Zd(e,t,n,r){const o=Math.min(e.xValues.length,e.values.length),s=[];let i=!1,a=null;for(let c=0;c180&&(i=!1),s.push(`${T(t(l))} ${T(r-n(u))} ${i?"l":"m"}`),i=!0,a=u}return s.join(` -`)}function Jd(e,t,n,r){const o=Math.min(e.xValues.length,e.values.length),s=[],i=a=>a<0||a>=o?!1:Number.isFinite(e.xValues[a])&&typeof e.values[a]=="number"&&Number.isFinite(e.values[a]);for(let a=0;a>","<< /Type /Pages /Kids [3 0 R] /Count 1 >>",`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${T(e)} ${T(t)}] /Resources << /Font << /F1 5 0 R /F2 6 0 R >> >> /Contents 4 0 R >>`,`<< /Length ${o.length} >> -stream -${n} -endstream`,"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>","<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>",`<< /Title ${tm(r)} /Creator (PQViewer) /Producer (PQViewer) >>`];let i=`%PDF-1.4 -%PQV1 -`;const a=[0];for(let l=0;l> -`,i+=`startxref -${c} -%%EOF -`,new TextEncoder().encode(i)}function wl(e){const t=[];for(const n of e)t.push(nm(n));return`(${t.map(n=>n===40||n===41||n===92?`\\${String.fromCharCode(n)}`:n<32||n>126?`\\${n.toString(8).padStart(3,"0")}`:String.fromCharCode(n)).join("")})`}function tm(e){let t="FEFF";for(let n=0;n`}function nm(e){const t=e.codePointAt(0)??63;return t<=255?t:new Map([[8211,150],[8212,151],[8216,145],[8217,146],[8220,147],[8221,148],[8226,149],[8230,133],[8364,128],[8482,153],[8722,45]]).get(t)??63}function T(e){if(!Number.isFinite(e))throw new Error("PDF contains a non-finite number");return(Math.abs(e)<5e-4?0:Number(e.toFixed(3))).toString()}const Co=Object.freeze(["#137f78","#b35c2e","#5468a8","#8b5a91","#4f7b45","#b08524","#366e83","#9a4d62"]),rm=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca"],js="pqviewer.figure",Oa=1,om=new Set(["positions","cell","forces","velocities","charges"]),sm=["unwrapped_positions","unwrapped_image_shifts"];function Io(e){const t=Xe(e,"Figure recipe",["schema","schema_version","source","frame","scene","camera","output","annotations"]);if(t.schema!==js)throw new Error(`Figure recipe schema must be ${js}`);if(t.schema_version!==Oa)throw new Error(`Unsupported figure recipe version: ${String(t.schema_version)}`);const n=Fi(t.source),r=hm(t.frame);return pm(n,r),{schema:js,schema_version:Oa,source:n,frame:r,scene:gm(t.scene),camera:ym(t.camera),output:xm(t.output),annotations:Ci(t.annotations,"Figure recipe annotations").map((o,s)=>Am(o,s))}}function im(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Figure recipe is not valid JSON")}return Io(t)}function Zs(e){return Io(e)}function am(e){return`${JSON.stringify(Zs(e),null,2)} -`}function cm(e){if(!e.source)throw new Error("The dataset has no source information");return Fi(e.source)}function La(e){const t=Fi(e);return JSON.stringify({kind:t.kind,path:t.path,slice:t.slice,segments:t.segments.map(n=>({kind:n.kind,path:n.path,input:n.input,files:n.files}))})}function Ns(e,t){const n=new Cm,r=new Set(om);t.header.coordinates==="unwrapped"&&sm.forEach(s=>r.add(s));const o=t.header.arrays.filter(s=>r.has(s.name.toLowerCase()));n.value(e.topology),n.value({arrays:o,pbc:t.header.pbc??null});for(const[s,i]of[...t.arrays.entries()].sort(([a],[c])=>a.localeCompare(c))){const a=s.toLowerCase();r.has(a)&&(n.value(a),n.value(i.constructor.name),n.bytes(new Uint8Array(i.buffer,i.byteOffset,i.byteLength)))}return`frame-v1:${n.digest()}`}function lm(e,t){try{return La(e)===La(t)}catch{return!1}}function um(e,t){return!!(t.source&&lm(e.source,t.source))}function xr(e,t){if(!e||!t)return!1;try{const n=Js(e,"First frame key"),r=Js(t,"Second frame key");return n.source_id===r.source_id&&n.source_index===r.source_index&&n.segment_index===r.segment_index&&n.step===r.step&&n.time===r.time&&n.time_unit===r.time_unit}catch{return!1}}function Fi(e){const t=Xe(e,"Figure source",["kind","path","slice","segments"]),n=dn(t.kind,"Figure source kind"),r=Ei(t.path,"Figure source path"),o=t.slice===void 0?{start:null,stop:null,step:null}:fm(t.slice),s=t.segments===void 0?[]:Ci(t.segments,"Figure source segments").map((i,a)=>dm(i,a));if(s.length===0)throw new Error("Figure source must include at least one segment");return{kind:n,path:r,slice:o,segments:s}}function fm(e){const t=Xe(e,"Figure source slice",["start","stop","step"]),n=yo(t.start,"Figure source slice start"),r=yo(t.stop,"Figure source slice stop"),o=yo(t.step,"Figure source slice step");if(o===0)throw new Error("Figure source slice step cannot be zero");return{start:n,stop:r,step:o}}function dm(e,t){const n=`Figure source segment ${t+1}`,r=Xe(e,n,["source_id","kind","path","input","frame_count","files"]),o=Da(r.path,`${n} path`),s=Da(r.input,`${n} input`),i=r.files===void 0?{}:mm(r.files,`${n} files`);if(o===null&&s===null&&Object.keys(i).length===0)throw new Error(`${n} has no durable source`);return{source_id:Ii(r.source_id,`${n} source_id`),kind:dn(r.kind,`${n} kind`),path:o,input:s,frame_count:Rr(r.frame_count,`${n} frame_count`),files:i}}function mm(e,t){const n=Sm(e,t),r={};for(const o of Object.keys(n).sort()){if(!o.trim()||Im(o))throw new Error(`${t} contains an invalid role`);r[o]=Ei(n[o],`${t}.${o}`)}return r}function hm(e){const t=Xe(e,"Figure frame",["index","key","fingerprint"]),n=dn(t.fingerprint,"Figure frame fingerprint");if(!/^frame-v1:[0-9a-f]{16}$/.test(n))throw new Error("Figure frame fingerprint is invalid");return{index:Rr(t.index,"Figure frame index"),key:Js(t.key,"Figure frame key"),fingerprint:n}}function pm(e,t){const n=e.segments[t.key.segment_index];if(!n)throw new Error("Figure frame segment is outside the source");if(Ii(n.source_id,"Figure source segment source_id")!==t.key.source_id)throw new Error("Figure frame source_id does not match its segment");if(t.key.source_index>=n.frame_count)throw new Error("Figure frame source_index is outside its segment");const r=e.segments.map(l=>l.frame_count),o=Fm(r.reduce((l,u)=>l+u,0),e.slice);if(t.index>=o.count)throw new Error("Figure frame index is outside the source slice");const s=o.start+t.index*o.step;let i=0,a=-1,c=-1;for(let l=0;l=i&&sAl(i,`Figure selection atom ${a+1}`)),s=new Set;for(const i of o){const a=`${i.atom}:${i.image.join(",")}`;if(s.has(a))throw new Error("Figure selection contains duplicate atoms");s.add(a)}return{presentation:bm(t.presentation),selection:{atoms:o,intent:dt(n.intent,["measurement","set"],"Figure selection intent"),minimumImage:vt(n.minimumImage,"Figure selection minimumImage")},vectors:{forceScale:Vt(r.forceScale,"Figure force scale"),velocityScale:Vt(r.velocityScale,"Figure velocity scale")}}}function bm(e){const t=Xe(e,"Figure presentation",["mode","water","hydrogens","wrap","cellOrigin","mirror","images","cell","forces","velocities","atomScale","bondScale","color","quality"]),n=Xe(t.images,"Figure presentation images",["min","max"]),r=ei(n.min,"Figure presentation minimum image"),o=ei(n.max,"Figure presentation maximum image");return r.forEach((s,i)=>{if(s>o[i])throw new Error("Figure presentation image minimum cannot exceed its maximum")}),{mode:dt(t.mode,["ball-stick","spacefill","licorice","lines","ribbon","polyhedra"],"Figure presentation mode"),water:dt(t.water,["show","hide","only"],"Figure presentation water"),hydrogens:vt(t.hydrogens,"Figure presentation hydrogens"),wrap:dt(t.wrap,["atom","molecule","unwrapped","none"],"Figure presentation wrap"),cellOrigin:xo(t.cellOrigin,"Figure presentation cellOrigin"),mirror:jm(t.mirror,"Figure presentation mirror"),images:{min:r,max:o},cell:vt(t.cell,"Figure presentation cell"),forces:vt(t.forces,"Figure presentation forces"),velocities:vt(t.velocities,"Figure presentation velocities"),atomScale:Vt(t.atomScale,"Figure presentation atomScale"),bondScale:Vt(t.bondScale,"Figure presentation bondScale"),color:dt(t.color,["element","residue","chain"],"Figure presentation color"),quality:dt(t.quality,["auto","high"],"Figure presentation quality")}}function Al(e,t){const n=Xe(e,t,["atom","image"]);return{atom:Rr(n.atom,`${t} atom`),image:ei(n.image,`${t} image`)}}function ym(e){const t=Xe(e,"Figure camera",["position","target","up","fov","zoom","near","far"]),n=xo(t.position,"Figure camera position"),r=xo(t.target,"Figure camera target"),o=xo(t.up,"Figure camera up"),s=jt(t.fov,"Figure camera fov"),i=Vt(t.zoom,"Figure camera zoom"),a=Vt(t.near,"Figure camera near"),c=Vt(t.far,"Figure camera far");if(s<=0||s>=180)throw new Error("Figure camera fov must be between 0 and 180");if(c<=a)throw new Error("Figure camera far must be greater than near");if(Nm(n,r)<=Number.EPSILON)throw new Error("Figure camera position must differ from target");if(o[0]**2+o[1]**2+o[2]**2<=Number.EPSILON)throw new Error("Figure camera up cannot be zero");const l=[r[0]-n[0],r[1]-n[1],r[2]-n[2]],u=[l[1]*o[2]-l[2]*o[1],l[2]*o[0]-l[0]*o[2],l[0]*o[1]-l[1]*o[0]];if(u[0]**2+u[1]**2+u[2]**2<=Number.EPSILON)throw new Error("Figure camera up cannot be parallel to its view");return{position:n,target:r,up:o,fov:s,zoom:i,near:a,far:c}}function xm(e){const t=Xe(e,"Figure output",["format","width","height","dpi","background","projection","fit","padding","periodicContext"]),n=za(t.width,"Figure output width"),r=za(t.height,"Figure output height");if(!Number.isSafeInteger(n*r))throw new Error("Figure output dimensions are too large");const o=jt(t.padding,"Figure output padding");if(o<0||o>.4)throw new Error("Figure output padding must be between 0 and 0.4");return{format:dt(t.format,["png","tiff"],"Figure output format"),width:n,height:r,dpi:Vt(t.dpi,"Figure output dpi"),background:wm(t.background),projection:dt(t.projection,["orthographic","perspective"],"Figure output projection"),fit:vt(t.fit,"Figure output fit"),padding:o,periodicContext:vt(t.periodicContext,"Figure output periodicContext")}}function wm(e){const t=Do(e,"Figure output background");if(t.kind==="transparent")return Hn(t,"Figure output background",["kind"]),{kind:"transparent"};if(t.kind==="solid"){Hn(t,"Figure output background",["kind","color"]);const n=dn(t.color,"Figure output background color").toLowerCase();if(!/^#[0-9a-f]{6}$/.test(n))throw new Error("Figure output background color must use #RRGGBB");return{kind:"solid",color:n}}throw new Error("Figure output background kind must be transparent or solid")}function Am(e,t){const n=`Figure annotation ${t+1}`,r=Do(e,n);if(r.kind==="atom-label"){Hn(r,n,["kind","atom","text","offset"]);const o={kind:"atom-label",atom:Al(r.atom,`${n} atom`)};return r.text!==void 0&&(o.text=dn(r.text,`${n} text`)),r.offset!==void 0&&(o.offset=vm(r.offset,`${n} offset`)),o}if(r.kind==="legend")return Hn(r,n,["kind","content","position"]),{kind:"legend",content:dt(r.content,["elements","residues","forces","velocities"],`${n} content`),position:Ba(r.position,`${n} position`)};if(r.kind==="scale-bar")return Hn(r,n,["kind","length","unit","position"]),{kind:"scale-bar",length:Vt(r.length,`${n} length`),unit:dt(r.unit,["angstrom","nanometer"],`${n} unit`),position:Ba(r.position,`${n} position`)};throw new Error(`${n} kind is unsupported`)}function Ba(e,t){return dt(e,["top-left","top-right","bottom-left","bottom-right"],t)}function Xe(e,t,n){const r=Do(e,t);return Hn(r,t,n),r}function Sm(e,t){return Do(e,t)}function Do(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw new Error(`${t} must be an object`);const n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw new Error(`${t} must be a plain object`);return e}function Hn(e,t,n){const r=Object.keys(e).filter(o=>!n.includes(o));if(r.length>0)throw new Error(`${t} contains unknown field: ${r[0]}`)}function Ci(e,t){if(!Array.isArray(e))throw new Error(`${t} must be an array`);return e}function dt(e,t,n){if(typeof e!="string"||!t.includes(e))throw new Error(`${n} is invalid`);return e}function vt(e,t){if(typeof e!="boolean")throw new Error(`${t} must be boolean`);return e}function dn(e,t){if(typeof e!="string"||!e.trim())throw new Error(`${t} must be a non-empty string`);return e}function Mm(e,t){return e==null?null:dn(e,t)}function Ii(e,t){return dn(e,t)}function Ei(e,t){return dn(e,t)}function Da(e,t){return e==null?null:Ei(e,t)}function jt(e,t){if(typeof e!="number"||!Number.isFinite(e))throw new Error(`${t} must be finite`);return Object.is(e,-0)?0:e}function Vt(e,t){const n=jt(e,t);if(n<=0)throw new Error(`${t} must be positive`);return n}function Kn(e,t){const n=jt(e,t);if(!Number.isSafeInteger(n))throw new Error(`${t} must be an integer`);return n}function za(e,t){const n=Kn(e,t);if(n<=0)throw new Error(`${t} must be positive`);return n}function Rr(e,t){const n=Kn(e,t);if(n<0)throw new Error(`${t} cannot be negative`);return n}function yo(e,t){return e==null?null:Kn(e,t)}function km(e,t){return e==null?null:jt(e,t)}function vm(e,t){const n=zo(e,2,t);return[jt(n[0],`${t}[0]`),jt(n[1],`${t}[1]`)]}function xo(e,t){const n=zo(e,3,t);return[jt(n[0],`${t}[0]`),jt(n[1],`${t}[1]`),jt(n[2],`${t}[2]`)]}function ei(e,t){const n=zo(e,3,t);return[Kn(n[0],`${t}[0]`),Kn(n[1],`${t}[1]`),Kn(n[2],`${t}[2]`)]}function jm(e,t){const n=zo(e,3,t);return[vt(n[0],`${t}[0]`),vt(n[1],`${t}[1]`),vt(n[2],`${t}[2]`)]}function zo(e,t,n){if(!Array.isArray(e)||e.length!==t)throw new Error(`${n} must contain ${t} values`);return e}function Nm(e,t){return(e[0]-t[0])**2+(e[1]-t[1])**2+(e[2]-t[2])**2}function Fm(e,t){const n=t.step??1;if(n>0){const s=Va(t.start,e,0),i=Va(t.stop,e,e);return{start:s,step:n,count:s>=i?0:Math.floor((i-s-1)/n)+1}}const r=Ua(t.start,e,e-1),o=Ua(t.stop,e,-1);return{start:r,step:n,count:r<=o?0:Math.floor((r-o-1)/-n)+1}}function Va(e,t,n){if(e===null)return n;const r=e<0?e+t:e;return Math.max(0,Math.min(t,r))}function Ua(e,t,n){if(e===null)return n;const r=e<0?e+t:e;return Math.max(-1,Math.min(t-1,r))}class Cm{first=2166136261;second=2654435769;numberBuffer=new ArrayBuffer(8);numberView=new DataView(this.numberBuffer);encoder=new TextEncoder;bytes(t){for(const n of t)this.first=Math.imul(this.first^n,16777619),this.second=Math.imul(this.second^n,2246822507),this.second^=this.second>>>13}value(t){if(t==null){this.text("null");return}if(typeof t=="string"){this.text(`s${t.length}:`),this.text(t);return}if(typeof t=="number"){this.text("n"),this.numberView.setFloat64(0,t,!0),this.bytes(new Uint8Array(this.numberBuffer));return}if(typeof t=="boolean"){this.text(t?"true":"false");return}if(Array.isArray(t)){this.text("[");for(const n of t)this.value(n);this.text("]");return}if(typeof t=="object"){this.text("{");for(const n of Object.keys(t).sort())this.value(n),this.value(t[n]);this.text("}");return}this.text(typeof t)}digest(){return[this.first,this.second].map(t=>(t>>>0).toString(16).padStart(8,"0")).join("")}text(t){this.bytes(this.encoder.encode(t))}}function Im(e){return e==="__proto__"||e==="prototype"||e==="constructor"}const Em="modulepreload",$m=function(e){return"/"+e},qa={},Dn=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){let c=function(l){return Promise.all(l.map(u=>Promise.resolve(u).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),a=i?.nonce||i?.getAttribute("nonce");o=c(n.map(l=>{if(l=$m(l),l in qa)return;qa[l]=!0;const u=l.endsWith(".css"),f=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${f}`))return;const m=document.createElement("link");if(m.rel=u?"stylesheet":Em,u||(m.as="script"),m.crossOrigin="",m.href=l,a&&m.setAttribute("nonce",a),document.head.appendChild(m),u)return new Promise((g,p)=>{m.addEventListener("load",g),m.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${l}`)))})}))}function s(i){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=i,window.dispatchEvent(a),!a.defaultPrevented)throw i}return o.then(i=>{for(const a of i||[])a.status==="rejected"&&s(a.reason);return t().catch(s)})},_m=.08,Sl=12e6;function Rm(e,t){const n=io(e.width,"width"),r=io(e.height,"height"),o=io(t.maxWidth,"maximum width"),s=io(t.maxHeight,"maximum height"),i=t.maxPixels??Sl;if(!Number.isSafeInteger(i)||i<=0)throw new Error("PNG export pixel limit is invalid");if(n>o||r>s)throw new Error(`PNG export exceeds the WebGL limit of ${o} × ${s} px`);if(n*r>i)throw new Error(`PNG export exceeds the ${i.toLocaleString("en")} pixel safety limit`);const a=e.padding??_m;if(!Number.isFinite(a)||a<0||a>.4)throw new Error("PNG export padding must be between 0 and 0.4");return{width:n,height:r,transparent:e.transparent??!1,fit:e.fit??!0,projection:e.projection??"orthographic",periodicContext:e.periodicContext??!0,padding:a}}function Tm(e){return!Number.isFinite(e)||e<=0?0:e<=5e6?3:e<=8e6?2:e<=12e6?1:0}function Pm(e){return!Number.isFinite(e)||e<=0?0:e<=8e6?.5:e<=12e6?.35:0}function Om(e,t){if(e.length<4||e.length%4!==0)return!1;if(t){for(let c=3;c1)return!0;return!1}let n=255,r=255,o=255,s=0,i=0,a=0;for(let c=0;c2||i-r>2||a-o>2}function Lm(e,t,n){const r=t*4;if(e.length!==r*n)throw new Error("PNG pixel buffer has an unexpected size");const o=new Uint8Array(r);for(let s=0;sTr)throw new Error("PNG DPI is outside the supported range");const c=new Uint8Array(13),l=new DataView(c.buffer);l.setUint32(0,n),l.setUint32(4,r),c[8]=8,c[9]=6;const u=new Uint8Array(9),f=new DataView(u.buffer);f.setUint32(0,a),f.setUint32(4,a),u[8]=1;const m=new TextEncoder().encode(`DPI\0${o.toString()}`),g=await Gm(s);return Zm([Bm,zn("IHDR",c),zn("sRGB",new Uint8Array([0])),zn("pHYs",u),zn("tEXt",m),zn("IDAT",g),zn("IEND",new Uint8Array)],"image/png")}function Vm(e,t){const{width:n,height:r,dpi:o}=Ml(e,t),s=Wm(e,n*4,r),[i,a]=Ym(o),c=new TextEncoder().encode("PQViewer\0");if(i===0)throw new Error("TIFF DPI is outside the supported range");const l=17,u=8,f=2+l*12+4,m=u+f,g=m+8,p=g+8,x=p+8,A=th(x+c.length),b=eh(A+Fs.length),j=b+s.length;if(j>Tr)throw new Error("TIFF output exceeds the 4 GiB baseline limit");const k=new Uint8Array(j),v=new DataView(k.buffer);k[0]=73,k[1]=73,v.setUint16(2,42,!0),v.setUint32(4,u,!0),v.setUint16(u,l,!0),[[256,4,1,n],[257,4,1,r],[258,3,4,m],[259,3,1,32773],[262,3,1,2],[273,4,1,b],[274,3,1,1],[277,3,1,4],[278,4,1,r],[279,4,1,s.length],[282,5,1,g],[283,5,1,p],[284,3,1,1],[296,3,1,2],[305,2,c.length,x],[338,3,1,2],[34675,7,Fs.length,A]].forEach(([I,E,_,U],G)=>{const B=u+2+G*12;v.setUint16(B,I,!0),v.setUint16(B+2,E,!0),v.setUint32(B+4,_,!0),E===3&&_===1?(v.setUint16(B+8,U,!0),v.setUint16(B+10,0,!0)):v.setUint32(B+8,U,!0)}),v.setUint32(u+2+l*12,0,!0);for(let I=0;I<4;I+=1)v.setUint16(m+I*2,8,!0);return v.setUint32(g,i,!0),v.setUint32(g+4,a,!0),v.setUint32(p,i,!0),v.setUint32(p+4,a,!0),k.set(c,x),k.set(Fs,A),k.set(s,b),Jm(k,"image/tiff")}function Ml(e,{width:t,height:n,dpi:r}){if(!Number.isSafeInteger(t)||t<=0)throw new Error("Figure width must be a positive integer");if(!Number.isSafeInteger(n)||n<=0)throw new Error("Figure height must be a positive integer");const o=t*n;if(!Number.isSafeInteger(o)||o>Math.floor(Number.MAX_SAFE_INTEGER/4))throw new Error("Figure dimensions are too large");if(e.length!==o*4)throw new Error(`Figure RGBA buffer must contain exactly ${o*4} bytes`);if(!Number.isFinite(r)||r<=0||r>1e6)throw new Error("Figure DPI must be between 0 and 1,000,000");return{width:t,height:n,dpi:r}}function zn(e,t){if(!/^[A-Za-z]{4}$/.test(e))throw new Error("PNG chunk type is invalid");const n=new TextEncoder().encode(e),r=new Uint8Array(12+t.length),o=new DataView(r.buffer);return o.setUint32(0,t.length),r.set(n,4),r.set(t,8),o.setUint32(8+t.length,Um(r.subarray(4,8+t.length))),r}function Um(e){let t=4294967295;for(const n of e)t=Dm[(t^n)&255]^t>>>8;return(t^4294967295)>>>0}function qm(){const e=new Uint32Array(256);for(let t=0;t>>1^(n&1?3988292384:0);e[t]=n>>>0}return e}async function Gm(e){if(typeof CompressionStream<"u"){const t=new Blob([$i(e)]).stream().pipeThrough(new CompressionStream("deflate"));return new Uint8Array(await new Response(t).arrayBuffer())}return Hm(e)}function Hm(e){const t=Math.max(1,Math.ceil(e.length/65535)),n=new Uint8Array(2+t*5+e.length+4);n[0]=120,n[1]=1;let r=0,o=2;for(let a=0;a>>8;const l=~c&65535;n[o+3]=l&255,n[o+4]=l>>>8,o+=5,n.set(e.subarray(r,r+c),o),r+=c,o+=c}const s=Km(e);return new DataView(n.buffer).setUint32(o,s),n}function Km(e){let t=1,n=0;for(const r of e)t=(t+r)%65521,n=(n+t)%65521;return(n<<16|t)>>>0}function Wm(e,t,n){const r=t+Math.ceil(t/128)+1,o=new Uint8Array(r*n);let s=0;for(let i=0;i=3){r[i++]=257-a,r[i++]=e[s],s+=a;continue}const c=s;for(s+=a;s=3)break;s+=Math.min(a,128-(s-c))}const l=s-c;r[i++]=l-1,r.set(e.subarray(c,s),i),i+=l}return i}function Ym(e){const t=e.toString().toLowerCase(),[n,r]=t.split("e"),o=r?Number(r):0,s=n.replace("-",""),i=s.indexOf("."),a=i<0?0:s.length-i-1,c=BigInt(s.replace(".","")),l=a-o;let u=l<0?c*10n**BigInt(-l):c,f=l>0?10n**BigInt(l):1n;const m=kl(u,f);return u/=m,f/=m,u>BigInt(Tr)||f>BigInt(Tr)?Qm(e):[Number(u),Number(f)]}function Qm(e){const n=Math.round(e*1e6);if(n===0)throw new Error("TIFF DPI is outside the supported range");const r=Number(kl(BigInt(n),BigInt(1e6))),o=n/r,s=1e6/r;if(o>Tr)throw new Error("TIFF DPI is outside the supported range");return[o,s]}function kl(e,t){let n=e<0n?-e:e,r=t<0n?-t:t;for(;r!==0n;){const o=n%r;n=r,r=o}return n||1n}function Zm(e,t){return new Blob(e.map($i),{type:t})}function Jm(e,t){return new Blob([$i(e)],{type:t})}function $i(e){return e.buffer instanceof ArrayBuffer&&e.byteOffset===0&&e.byteLength===e.buffer.byteLength?e.buffer:e.slice().buffer}function eh(e){return e+(e&1)}function th(e){return e+(4-e%4)%4}function nh(e){const t=atob(e);return Uint8Array.from(t,n=>n.charCodeAt(0))}function rh(e,t,n){return e||t>n}function oh(e,t,n,r,o,s,i,a){const c=r/o,l=vl(t.quaternion),u=sh(e,t.quaternion);if(!u)throw new Error("The molecular scene has no visible geometry");if(s==="perspective"){const U=t.clone();return U.aspect=c,i&&ih(U,e,u,l,a),U.updateProjectionMatrix(),U.updateMatrixWorld(!0),U}const f=new Nc(-1,1,1,-1,.01,1e4);f.quaternion.copy(t.quaternion);const m=Math.max(t.position.distanceTo(n),.01),g=2*m*Math.tan(Se.degToRad(t.getEffectiveFOV()*.5)),p=i?jl(u,l):n.clone(),x=Math.max(u.maxRight-u.minRight,.01),A=Math.max(u.maxUp-u.minUp,.01),b=Math.max(.2,1-a*2),j=i?Math.max(A/b,x/(c*b),.1):Math.max(g,.1),k=j*c;f.left=-k*.5,f.right=k*.5,f.top=j*.5,f.bottom=-j*.5;const v=Math.max(u.maxBack-u.minBack,.01),S=Math.max(x,A,v,1),I=p.dot(l.back),E=i?v*.5+S*1.5:Math.max(m,u.maxBack-I+S);f.position.copy(p).addScaledVector(l.back,E);const _=f.position.dot(l.back);return f.near=Math.max(_-u.maxBack-S*.25,.01),f.far=Math.max(_-u.minBack+S*.25,f.near+10),f.updateProjectionMatrix(),f.updateMatrixWorld(!0),f}function sh(e,t){e.updateMatrixWorld(!0);const n=vl(t),r={minRight:1/0,maxRight:-1/0,minUp:1/0,maxUp:-1/0,minBack:1/0,maxBack:-1/0};let o=!1;return _i(e,s=>{const i=s.dot(n.right),a=s.dot(n.up),c=s.dot(n.back);r.minRight=Math.min(r.minRight,i),r.maxRight=Math.max(r.maxRight,i),r.minUp=Math.min(r.minUp,a),r.maxUp=Math.max(r.maxUp,a),r.minBack=Math.min(r.minBack,c),r.maxBack=Math.max(r.maxBack,c),o=!0}),o?r:null}function ih(e,t,n,r,o){const s=jl(n,r),i=(n.minRight+n.maxRight)*.5,a=(n.minUp+n.maxUp)*.5,c=(n.minBack+n.maxBack)*.5,l=Se.degToRad(e.getEffectiveFOV()*.5),u=Math.atan(Math.tan(l)*e.aspect),f=Math.max(.2,1-o*2);let m=.01;_i(t,x=>{const A=x.dot(r.back)-c;m=Math.max(m,A+Math.abs(x.dot(r.right)-i)/(Math.tan(u)*f),A+Math.abs(x.dot(r.up)-a)/(Math.tan(l)*f))});const g=Math.max(n.maxBack-n.minBack,.01),p=Math.max(n.maxRight-n.minRight,n.maxUp-n.minUp,g,1);e.position.copy(s).addScaledVector(r.back,m),e.quaternion.setFromRotationMatrix(new Bs().makeBasis(r.right,r.up,r.back));for(let x=0;x<4;x+=1){e.near=Math.max(m-g*.5-p*.25,.01),e.far=Math.max(m+g*.5+p,e.near+10),e.updateProjectionMatrix(),e.updateMatrixWorld(!0);const A=ah(t,e);if(A<=f*1.001)break;m*=A/f*1.005,e.position.copy(s).addScaledVector(r.back,m)}e.near=Math.max(m-g*.5-p*.25,.01),e.far=Math.max(m+g*.5+p,e.near+10)}function vl(e){return{right:new F(1,0,0).applyQuaternion(e).normalize(),up:new F(0,1,0).applyQuaternion(e).normalize(),back:new F(0,0,1).applyQuaternion(e).normalize()}}function jl(e,t){return new F().addScaledVector(t.right,(e.minRight+e.maxRight)*.5).addScaledVector(t.up,(e.minUp+e.maxUp)*.5).addScaledVector(t.back,(e.minBack+e.maxBack)*.5)}function _i(e,t){e.updateMatrixWorld(!0);const n=new Bs,r=new Bs,o=new F,s=new F;e.traverse(i=>{if(i.visible===!1)return;const a=i.userData.publicationFitPositions;if(a){for(let u=0;u+2{r.copy(o).project(t),Number.isFinite(r.x)&&Number.isFinite(r.y)&&(n=Math.max(n,Math.abs(r.x),Math.abs(r.y)))}),n}const ch="Inter",lh=600;function uh(e){return`${lh} ${e}px "${ch}"`}async function fh(e,t,n=typeof document>"u"?void 0:document.fonts){if(!n)throw new Error("Publication font is unavailable");const r=uh(e),o=t.trim()||"PQ";try{if((await n.load(r,o)).length===0||!n.check(r,o))throw new Error("font did not load")}catch{throw new Error("Publication font is unavailable")}return r}const mt=1e-10;function dh(e,t,n=1,r=4/3){const o=t.filter(x=>Number.isInteger(x)&&x>=0&&x*3+2new F(Number(e[x*3]),Number(e[x*3+1]),Number(e[x*3+2]))).filter(x=>[x.x,x.y,x.z].every(Number.isFinite));if(o.length<3)return null;const s=o.reduce((x,A)=>x.add(A),new F).multiplyScalar(1/o.length),i=wh(o,s);if(!i)return null;const a=Number.isFinite(r)&&r>0?r:4/3,{direction:c,right:l,up:u}=mh(o,s,i,Math.max(a,1/a)),f=a>=1?l:u.clone().negate(),m=a>=1?u:l,g=Sh(o,f,m,c),p=Number.isFinite(n)?Math.max(.2,n):1;return{center:g,direction:c,up:m,points:o,radius:.9*p}}function mh(e,t,n,r){const o=xh(e,128),s=[],i=Math.PI*(3-Math.sqrt(5));for(let l=0;l<96;l+=1){const u=(l+.5)/96,f=Math.sqrt(Math.max(0,1-u*u)),m=l*i;s.push(n.major.clone().multiplyScalar(f*Math.cos(m)).addScaledVector(n.middle,f*Math.sin(m)).addScaledVector(n.minor,u).normalize())}s.push(n.minor.clone(),n.minor.clone().addScaledVector(n.middle,.35).normalize(),n.minor.clone().addScaledVector(n.major,.35).normalize());let a=null,c=1/0;for(const l of s){const u=hh(o,t,l),f=ph(o,t,u,r);f{const _=E.clone().sub(t);return{x:_.dot(n.right),y:_.dot(n.up),depth:_.dot(n.direction)}}),s=o.map(({x:E})=>E),i=o.map(({y:E})=>E),a=Math.max(...s)-Math.min(...s),c=Math.max(...i)-Math.min(...i);if(aE+_.x*_.x+_.y*_.y,0),S=o.reduce((E,_)=>E+_.x*_.x+_.y*_.y+_.depth*_.depth,0),I=Math.sqrt(v/Math.max(S,mt));return u*2.8+b*2.5+j*.6+A*.25+k*.35+I*4}function gh(e){let t=0;for(let n=0;na.x-c.x||a.y-c.y),o=a=>{const c=[];for(const l of a){for(;c.length>=2&&Sr(c[c.length-2],c[c.length-1],l)<=0;)c.pop();c.push(l)}return c},s=[...o(r).slice(0,-1),...o([...r].reverse()).slice(0,-1)];let i=0;for(let a=0;ae[Math.round(r/(t-1)*(e.length-1))])}function wh(e,t){const n=[[0,0,0],[0,0,0],[0,0,0]],r=new F;for(const f of e)r.copy(f).sub(t),n[0][0]+=r.x*r.x,n[0][1]+=r.x*r.y,n[0][2]+=r.x*r.z,n[1][1]+=r.y*r.y,n[1][2]+=r.y*r.z,n[2][2]+=r.z*r.z;n[1][0]=n[0][1],n[2][0]=n[0][2],n[2][1]=n[1][2];const o=Ah(n).sort((f,m)=>m.value-f.value);if(o[0].valuemt&&c.dot(a)<0&&a.negate();const l=new F().crossVectors(i,a).normalize();return Mh(e,l)<-mt&&(a.negate(),l.negate()),{major:i,middle:a,minor:l}}function Ah(e){const t=e.map(r=>[...r]),n=[[1,0,0],[0,1,0],[0,0,1]];for(let r=0;r<24;r+=1){let o=0,s=1;for(const[m,g]of[[0,2],[1,2]])Math.abs(t[m][g])>Math.abs(t[o][s])&&(o=m,s=g);if(Math.abs(t[o][s])({value:Math.max(0,t[r][r]),axis:new F(n[0][r],n[1][r],n[2][r])}))}function Sh(e,t,n,r){const o=[1/0,1/0,1/0],s=[-1/0,-1/0,-1/0];for(const i of e)[i.dot(t),i.dot(n),i.dot(r)].forEach((c,l)=>{o[l]=Math.min(o[l],c),s[l]=Math.max(s[l],c)});return t.clone().multiplyScalar((o[0]+s[0])*.5).addScaledVector(n,(o[1]+s[1])*.5).addScaledVector(r,(o[2]+s[2])*.5)}function Mh(e,t){let n=0;for(let r=1;r=-120&&e<=-30&&t>=-100&&t<=45?"helix":e>=-180&&e<=-60&&(t>=60&&t<=180||t>=-180&&t<=-130)?"sheet":"coil"}function Fl(e){const t=Array(e.length).fill("coil");for(let r=1;r=2&&t[0]==="coil"&&t[1]==="sheet"&&(t[0]="sheet");const n=t.length-1;return n>=1&&t[n]==="coil"&&t[n-1]==="sheet"&&(t[n]="sheet"),t}function Eo(e,t=1){const n=Number.isFinite(t)?Math.max(.2,t):1;return e==="helix"?{width:.38*n,depth:.085*n}:e==="sheet"?{width:.43*n,depth:.065*n}:{width:.13*n,depth:.13*n}}function vh(e,t){if(e.length<3)return null;const n=t.translations?.length?[...t.translations]:[new F],r=n.map(($,L)=>t.translationImages?.[L]??[0,0,0]),o=t.structures?.length===e.length?[...t.structures]:Fl(e),s=t.quality==="high",a=e.length*n.length>2e4?8:s?16:12,c=s?2e5:9e4,l=s?10:6,u=Se.clamp(Math.floor(c/Math.max(1,(e.length-1)*a*n.length)),1,l),f=jh(e,o,t.scale,u),m=f.length,p=(m*a+2)*n.length,x=(m-1)*a*6,A=a*6,b=(x+A)*n.length,j=new Float32Array(p*3),k=new Float32Array(p*3);k.fill(1);const v=new Float32Array(p),S=new Float32Array(p*3),I=new Float32Array(p),E=new Float32Array(p),_=p>65535?new Uint32Array(b):new Uint16Array(b);let U=0,G=0;const B=new F;for(let $=0;$r.ca.clone());for(const[r,o]of Ti(t,"sheet")){const s=Math.max(0,r-1),i=Math.min(e.length-1,o+1);let a=e.slice(s,i+1).map(c=>c.ca.clone());for(let c=0;c<2;c+=1)a=a.map((l,u)=>u===0||u===a.length-1?l.clone():a[u-1].clone().addScaledVector(l,2).add(a[u+1]).multiplyScalar(.25));for(let c=r;c<=o;c+=1)n[c].copy(a[c-s])}return n}function Fh(e,t,n,r){const o=e.clone().addScaledVector(t,-e.dot(t));o.lengthSq()<1e-8&&o.copy(n),o.normalize();const s=n.clone();return o.dot(s)<0&&s.negate(),o.lerp(s,r),o.addScaledVector(t,-o.dot(t)),o.lengthSq()<1e-8?Ri(t):o.normalize()}function Ch(e){if(e.length===0)return[];const t=[e[0]];for(let n=1;n0&&t[t.length-1].dot(i)<0&&i.negate(),t.push(i)}return t.map((n,r)=>{const o=n.clone().multiplyScalar(2);return r>0&&o.add(t[r-1]),r=n))for(let s=r;s<=o;s+=1)e[s]="coil"}function Ti(e,t){const n=[];let r=-1;for(let o=0;o<=e.length;o+=1){if(o=0&&n.push([r,o-1]),r=-1}return n}function Eh(e,t,n,r){const o=Eo("sheet",r);for(const[s,i]of t){const a=i===n.length-1,c=n[Math.min(i+1,n.length-1)],l=Math.max(s,i-.85),u=a?i-.32:i-.24,f=a?i:Math.min(n.length-1,i+.8);if(ef)continue;const m=o.width*1.48;if(e<=u){const x=ti((e-l)/Math.max(.01,u-l));return{structure:"sheet",width:Se.lerp(o.width,m,x),depth:o.depth,squareness:1}}const g=ti((e-u)/Math.max(.01,f-u)),p=a?{width:.018*Math.max(.2,r),depth:.018*Math.max(.2,r)}:Eo(c,r);return{structure:"sheet",width:Se.lerp(m,p.width,g),depth:Se.lerp(o.depth,p.depth,g),squareness:Se.lerp(1,c==="sheet"?1:0,g)}}return null}function Wa(e,t){const n=Se.lerp(1,.52,Se.clamp(t,0,1));return Math.sign(e)*Math.pow(Math.abs(e),n)}function ti(e){const t=Se.clamp(e,0,1);return t*t*(3-2*t)}function Xa(e,t,n,r,o,s,i,a,c){e.center.clone().add(t).toArray(r,c*3),o[c]=e.atomIndex,Cl(e.image,n,s,c),i[c]=e.progress,a[c]=Nl[e.structure]}function Cl(e,t,n,r){const o=r*3;n[o]=e[0]+t[0],n[o+1]=e[1]+t[1],n[o+2]=e[2]+t[2]}const Ya=2048,$h=8192,_h=16,Qa=6e4,Rh=new Me("#568da3"),Th=new Set([1,2,6,7,8,9,10,17,18,35,36,53,54,85,86]);function Ph(e,t={}){const n=Math.min(e.atomicNumbers.length,Math.floor(e.positions.length/3));if(n<2)return[];const r=ri(t.maxCoordination,3,_h,12),o=ri(t.maxCenters,1,Ya,Ya),s=Yh(e.bonds,n),i=t.centerAtoms!==void 0,a=t.centerAtomicNumbers?new Set(t.centerAtomicNumbers.filter(Number.isInteger)):null,c=i?Qh(t.centerAtoms??[],n):Hh(e.atomicNumbers,s,a),l=$l(c,Math.min($h,Math.max(o*4,o))),u=[];for(const f of l){const m=Kh(e,f,[...s[f]],r);if(m.length<3||a&&!a.has(e.atomicNumbers[f]??0)||!i&&!Il(e.atomicNumbers[f]??0))continue;const g=Bh(e,f,m);g&&u.push(g)}return Jh(u,e.positions,o)}function Oh(e,t={}){const n=Ph(e,t);if(n.length===0)return null;const r=Zh(t.images),o=ri(t.maxTriangles,1,Qa,Qa),s=[],i=[],a=[],c=[],l=[],u=[],f=[],m=[],g=[],p=new Me;let x=0,A=0;e:for(const j of r){const k=qt(j,e.basis);for(let v=0;vo)break e;p.copy(Rh);const I=t.colorForCenter?.(S.centerAtom,e.atomicNumbers[S.centerAtom]??0);I!==void 0&&p.set(I);for(const E of S.triangles){for(const _ of E)S.vertices[_].clone().add(k).toArray(s,s.length),i.push(p.r,p.g,p.b),a.push(S.centerAtom),c.push(S.centerAtom),l.push(S.vertexAtoms[_]),u.push(S.coordinationNumber),f.push(j[0],j[1],j[2]),m.push(v);x+=1}for(const[E,_]of Lh(S))S.vertices[E].clone().add(k).toArray(g,g.length),S.vertices[_].clone().add(k).toArray(g,g.length);A+=1}}if(x===0)return null;const b=new Kt;return b.setAttribute("position",new Lt(s,3)),b.setAttribute("color",new Lt(i,3)),b.setAttribute("atomIndex",new Lt(a,1)),b.setAttribute("centerAtomIndex",new Lt(c,1)),b.setAttribute("ligandAtomIndex",new Lt(l,1)),b.setAttribute("coordinationNumber",new Lt(u,1)),b.setAttribute("imageOffset",new Lt(f,3)),b.setAttribute("polyhedronIndex",new Lt(m,1)),b.computeVertexNormals(),b.computeBoundingBox(),b.computeBoundingSphere(),b.userData.polyhedronCount=A,b.userData.triangleCount=x,b.userData.edgePositions=new Float32Array(g),b}function Lh(e){const t=new Map;for(const[r,o,s]of e.triangles){const i=new F().subVectors(e.vertices[o],e.vertices[r]).cross(new F().subVectors(e.vertices[s],e.vertices[r])).normalize();for(const[a,c]of[[r,o],[o,s],[s,r]]){const l=ar.length===1||r.some((o,s)=>r.slice(s+1).some(i=>Math.abs(o.dot(i))r)}function Il(e){return Number.isInteger(e)&&e>0&&e<=118&&!Th.has(e)}function Bh(e,t,n){const r=Vo(e.positions,t);if(!ni(r))return null;const o=n.map(({atom:c})=>c),s=n.map(({point:c})=>c.clone()),i=[...o];if(s.some(c=>!ni(c))||Gh(s))return null;const a=Dh(s)??zh(s);return a?{centerAtom:t,coordinationNumber:n.length,ligandAtoms:o,vertices:s,vertexAtoms:i,triangles:a}:null}function Dh(e){if(e.length<3)return null;const t=Pi(e);if(!Number.isFinite(t)||t<=1e-8)return null;const n=Math.max(1e-8,t*2e-6);let r=null;for(let i=0;in*n&&(r=l.normalize())}if(!r)return null;const o=-r.dot(e[0]);if(e.some(i=>Math.abs(r.dot(i)+o)>n))return null;const s=El(e,e.map((i,a)=>a),r,n);return s.length!==e.length?null:Array.from({length:s.length-2},(i,a)=>[s[0],s[a+1],s[a+2]])}function zh(e){if(e.length<4)return null;const t=Pi(e);if(!Number.isFinite(t)||t<=1e-8)return null;const n=Math.max(1e-8,t*1e-6),r=Math.max(1e-9,t*2e-6);if(!qh(e,t))return null;const o=[];for(let a=0;ar?m=!0:A<-r&&(g=!0)}if(m&&g)continue;m&&(u.negate(),f*=-1);let p=o.find(x=>x.normal.dot(u)>1-1e-5&&Math.abs(x.offset-f)<=r);p||(p={normal:u,offset:f,vertices:new Set},o.push(p)),e.forEach((x,A)=>{Math.abs(p.normal.dot(x)+p.offset)<=r&&p.vertices.add(A)})}const s=[];for(const a of o){const c=El(e,[...a.vertices],a.normal,n);if(!(c.length<3))for(let l=1;l{const f=e[u].clone().sub(o);return{index:u,x:f.dot(s),y:f.dot(i)}}).sort((u,f)=>u.x-f.x||u.y-f.y||u.index-f.index),c=[],l=[];for(const u of a){for(;c.length>=2&&Za(c.at(-2),c.at(-1),u)<=r*r;)c.pop();c.push(u)}for(let u=a.length-1;u>=0;u-=1){const f=a[u];for(;l.length>=2&&Za(l.at(-2),l.at(-1),f)<=r*r;)l.pop();l.push(f)}return[...c.slice(0,-1),...l.slice(0,-1)].map(u=>u.index)}function Vh(e,t){const n=new Map,r=new Set;for(const o of t){r.add(o[0]),r.add(o[1]),r.add(o[2]);for(const[s,i]of[[o[0],o[1]],[o[1],o[2]],[o[2],o[0]]]){const a=so===2)}function Uh(e,t){let n=0;const r=new F;for(const[o,s,i]of t)r.crossVectors(e[s],e[i]),n+=e[o].dot(r)/6;return Math.abs(n)}function qh(e,t){const n=Math.max(1e-10,t**3*1e-7);for(let r=0;rn)return!0}}return!1}function Gh(e){const t=Pi(e),n=Math.max(1e-16,t*t*1e-12);for(let r=0;r=1&&Il(i)&&(!n||n.has(i))&&r.push(s)}return r}function Kh(e,t,n,r){const o=Vo(e.positions,t);if(!ni(o))return[];const s=!!(e.basis&&e.pbc.some(Boolean)),i=new Map,a=[];for(const u of n)for(const f of Wh(e,o,u)){const m=o.distanceTo(f);if(!Number.isFinite(m)||m<=1e-6)continue;if(!s){a.push({atom:u,point:f,distance:m});continue}const g=Xh(f),p=i.get(g);(!p||uu.distance-f.distance||u.atom-f.atom);if(c.length===0)return[];const l=c[0].distance*1.32+1e-6;return c.filter(({distance:u})=>u<=l).slice(0,r)}function Wh(e,t,n){const r=Un(t,Vo(e.positions,n),e.basis,e.pbc);if(!e.basis||!e.pbc.some(Boolean))return[r];const o=[];for(const s of e.pbc[0]?[-1,0,1]:[0])for(const i of e.pbc[1]?[-1,0,1]:[0])for(const a of e.pbc[2]?[-1,0,1]:[0])o.push(r.clone().addScaledVector(e.basis.vectors[0],s).addScaledVector(e.basis.vectors[1],i).addScaledVector(e.basis.vectors[2],a));return o}function Xh(e){return`${Math.round(e.x*1e5)}:${Math.round(e.y*1e5)}:${Math.round(e.z*1e5)}`}function Yh(e,t){const n=Array.from({length:t},()=>new Set);for(const r of e){const o=r[0],s=r[1];!Number.isInteger(o)||!Number.isInteger(s)||o<0||s<0||o>=t||s>=t||o===s||(n[o].add(s),n[s].add(o))}return n}function Qh(e,t){return[...new Set(e.filter(n=>Number.isInteger(n)&&n>=0&&nn-r)}function Zh(e){const t=e?.length?e:[[0,0,0]],n=[],r=new Set;for(const o of t){if(o.length!==3||!o.every(Number.isInteger))continue;const s=[o[0],o[1],o[2]],i=s.join(":");if(r.has(i)||(n.push(s),r.add(i)),n.length>=125)break}return n.length?n:[[0,0,0]]}function $l(e,t){return e.length<=t?[...e]:Array.from({length:t},(n,r)=>e[Math.floor((r+.5)*e.length/t)])}function Jh(e,t,n){if(e.length<=n)return[...e];if(n>128)return $l(e,n);const r=e.map(({centerAtom:u})=>Vo(t,u)),o=new kt().setFromPoints(r).getCenter(new F);let s=0,i=1/0;r.forEach((u,f)=>{const m=u.distanceToSquared(o);mu.distanceToSquared(r[s]));for(;a.lengthf&&(u=m,f=l[m]);if(u<0)break;a.push(u),c.add(u),r.forEach((m,g)=>{l[g]=Math.min(l[g],m.distanceToSquared(r[u]))})}return a.map(u=>e[u])}function Vo(e,t){return new F().fromArray(e,t*3)}function ni(e){return Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.z)}function Za(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function ri(e,t,n,r){return Number.isFinite(e)?Se.clamp(Math.floor(e),t,n):r}const ep=512,tp=16,np=512,rp=32,op=Object.freeze({trails:Object.freeze([]),displacements:Object.freeze([])});function sp(e,t,n){const r=e>ep?"points":"rings",o=r==="points"?Math.min(e,t):0;return{mode:r,pointCapacity:o,reusePointBuffer:r==="points"&&n!==null&&n>=o,clearRingMarkers:r==="points"}}function ip(e){return e.pointerType==="touch"||e.pointerType==="pen"||e.shiftKey||e.metaKey||e.ctrlKey}const $o={light:{background:"#F6F8F8",bond:"#375159",bondOpacity:.9,cell:"#2D7DA4",cellOpacity:.58,selection:"#3DACCB",selectionOpacity:.34,force:"#B8522D",velocity:"#6B62A8",displacement:"#087F8C",ribbon:"#3D879D",hemisphereSky:"#ffffff",hemisphereGround:"#c6d2d5",hemisphereIntensity:1.55,key:"#ffffff",keyIntensity:2.25,rim:"#8fcbd3",rimIntensity:.22,exposure:.95},dark:{background:"#1e2e33",bond:"#c0c9cb",bondOpacity:.9,cell:"#5db8d2",cellOpacity:.74,selection:"#72d4df",selectionOpacity:.42,force:"#f0a75a",velocity:"#9e98d7",displacement:"#72d4df",ribbon:"#6cb9ca",hemisphereSky:"#f5f6f2",hemisphereGround:"#17272c",hemisphereIntensity:1.42,key:"#eef2ef",keyIntensity:2,rim:"#62b7cd",rimIntensity:.34,exposure:.96}},oi=new F(0,1,0);function ap(e,t,n,r){let o="Backbone available";return n||(o=e.topology.residues?.length&&e.topology.atom_names?.length?"Three complete backbone residues required":"Backbone topology unavailable"),{water:t,ribbon:n,ribbonReason:o,suggestedProfile:n?"protein":r&&!t?"crystal":"molecule"}}function cp(e,t,n){return{imageCount:t.images.length,forceCount:n.forceInstances.length,forceTotal:n.forceTotal,velocityCount:n.velocityInstances.length,velocityTotal:n.velocityTotal,capabilities:ap(e,t.waterAtoms.size>0,t.backbone.length>=3,!!(t.basis&&t.pbc.some(Boolean)))}}function lp(e,t){const n=t.basis?Float64Array.from(t.basis.vectors.flatMap(r=>[r.x,r.y,r.z])):null;return{count:t.count,atomicNumbers:t.atomicNumbers,positions:t.positions,baseImages:t.baseImages,cell:n,bonds:t.bonds,waterAtoms:t.waterAtoms,instanceToAtom:t.instanceToAtom,instanceImages:t.instanceImages,atomResidueIndex:e.topology.atom_residue_index}}function up(e,t){return e.imageCount===t.imageCount&&e.forceCount===t.forceCount&&e.forceTotal===t.forceTotal&&e.velocityCount===t.velocityCount&&e.velocityTotal===t.velocityTotal&&e.capabilities.water===t.capabilities.water&&e.capabilities.ribbon===t.capabilities.ribbon&&e.capabilities.ribbonReason===t.capabilities.ribbonReason&&e.capabilities.suggestedProfile===t.capabilities.suggestedProfile}const fp=w.forwardRef(function({manifest:t,frame:n,presentation:r,selectedAtoms:o,trajectoryOverlays:s=op,resetSignal:i,forceScale:a,velocityScale:c,appearance:l,viewPreset:u="perspective",viewSignal:f=0,onSelect:m,onSelectMany:g,onSceneInfo:p,onSelectionContext:x,onSelectionPositions:A},b){const j=w.useRef(null),k=w.useRef(null),v=w.useRef(m),S=w.useRef(g),I=w.useRef(o),E=w.useRef(p),_=w.useRef(x),U=w.useRef(A),G=w.useRef(null),B=w.useRef(!1),[V,$]=w.useState(null),[L,ee]=w.useState(null),te=w.useRef(null),oe=w.useRef(null);v.current=m,S.current=g,I.current=o,te.current=V,E.current=p,_.current=x,U.current=A,w.useImperativeHandle(b,()=>({exportPng:async N=>{if(B.current)throw new Error("A figure export is already in progress");const P=k.current;if(!P?.model)throw new Error("The molecular scene is not ready to export");const R=Ja(P);B.current=!0;try{return await ec(P.renderer,R,{...N,format:"png"})}finally{B.current=!1}},exportFigure:async N=>{if(B.current)throw new Error("A figure export is already in progress");const P=k.current;if(!P?.model)throw new Error("The molecular scene is not ready to export");const R=Ja(P);B.current=!0;try{return await ec(P.renderer,R,N)}finally{B.current=!1}},captureCamera:()=>{const N=k.current;if(!N)throw new Error("The molecular scene is not ready");return sg(N.camera,N.controls.target)},restoreCamera:N=>{const P=k.current;if(!P)throw new Error("The molecular scene is not ready");ig(P,N)}}),[]),w.useEffect(()=>{const N=j.current;if(!N)return;const P=new Ou({canvas:N,antialias:!0,powerPreference:"high-performance"});let R=Math.min(window.devicePixelRatio,2);P.outputColorSpace=Ao,P.toneMapping=Lu;const Y=new Fc,H=$o.light;Y.background=new Me(H.background);const X=new Bu(34,1,.02,5e3);X.position.set(7,5,9);const ne=new Du(X,N);ne.enableDamping=!0,ne.dampingFactor=.065,ne.screenSpacePanning=!0,ne.zoomToCursor=!0;const re=new kn;Y.add(re);const W=new Cc(H.hemisphereSky,H.hemisphereGround,H.hemisphereIntensity);Y.add(W);const J=new kr(H.key,H.keyIntensity);J.position.set(7,10,8),Y.add(J);const me=new kr(H.rim,H.rimIntensity);me.position.set(-8,-2,-5),Y.add(me);const q=new kn,de=new zu(.94,1,64),se=new Xn({color:H.selection,transparent:!0,opacity:H.selectionOpacity,side:So,depthTest:!1});re.add(q);const xe=new Lr({color:H.selection,size:7,sizeAttenuation:!1,transparent:!0,opacity:.9,depthTest:!1}),ue=new fn(new Kt,xe);ue.renderOrder=10,ue.frustumCulled=!1,ue.visible=!1,re.add(ue);const pe=new Xn({color:H.selection,transparent:!0,opacity:.78,side:So,depthTest:!1}),K=new Zn(de,pe);K.renderOrder=11,K.visible=!1,re.add(K);const ie=new kn;ie.renderOrder=7,re.add(ie);const be={renderer:P,scene:Y,hemisphere:W,key:J,rim:me,camera:X,controls:ne,root:re,atomObject:null,bonds:null,cell:null,forces:null,velocities:null,ribbon:null,polyhedra:null,trajectoryOverlays:ie,selection:q,selectionGeometry:de,selectionMaterial:se,selectionPoints:ue,selectionPointsMaterial:xe,keyboardFocus:K,keyboardFocusMaterial:pe,pickables:[],instanceToAtom:new Uint32Array,instanceImages:new Int8Array,baseImages:new Int32Array,ribbonSelections:new Map,model:null,topologyManifest:null,preparedTopology:null,renderTopology:null,renderConfigKey:"",frameLayout:null,fittedKey:"",lastResetSignal:i,lastViewSignal:f,lastFittedAspect:1,fitContext:null,cameraMode:"fit"};k.current=be;const lt=()=>{be.cameraMode="manual"};ne.addEventListener("start",lt);let mn=0,pt=0;const Cn=()=>{const D=Math.max(N.clientWidth,1),ge=Math.max(N.clientHeight,1),ke=Math.min(window.devicePixelRatio,2);D===mn&&ge===pt&&!(ke!==R)||(R=ke,mn=D,pt=ge,P.setDrawingBufferSize(D,ge,R),X.aspect=D/ge,X.updateProjectionMatrix(),be.cameraMode==="fit"&&be.fitContext&&Math.abs(Math.log(X.aspect/be.lastFittedAspect))>.06&&ic(be,be.fitContext))},tr=new ResizeObserver(Cn);tr.observe(N),window.addEventListener("resize",Cn),Cn();const Ge=new yr;let Ie=null,Le=null,Wt=!1;const nr=new yr,Qe=new Vu;Qe.params.Points.threshold=.24;const In=(D,ge=ip(D))=>{const ke=N.getBoundingClientRect();nr.set((D.clientX-ke.left)/ke.width*2-1,-((D.clientY-ke.top)/ke.height)*2+1),Qe.setFromCamera(nr,X);const Be=Qe.intersectObjects(be.pickables,!1)[0],Yt=Be?Qp(Be,be):null;document.activeElement===N&&(te.current=Yt,oe.current=Be&&Be.object===be.atomObject?Be.instanceId??Be.index??null:null,$(Yt)),v.current(Yt,ge)},rt=()=>{Le!==null&&N.hasPointerCapture(Le)&&N.releasePointerCapture(Le),Le=null,ne.enabled=!0,ee(null)},fe=D=>{if(D.pointerType==="mouse"&&D.button===0&&D.shiftKey&&Le===null){Le=D.pointerId,Ge.set(D.clientX,D.clientY),ne.enabled=!1,N.setPointerCapture(D.pointerId),D.preventDefault(),D.stopImmediatePropagation();return}if(Ie!==null&&D.pointerId!==Ie){Wt=!0;return}D.isPrimary&&(Ie=D.pointerId,Wt=!1,Ge.set(D.clientX,D.clientY))},Re=D=>{if(D.pointerId!==Le)return;const ge=N.getBoundingClientRect(),ke=Math.max(ge.left,Math.min(Ge.x,D.clientX)),Be=Math.min(ge.right,Math.max(Ge.x,D.clientX)),Yt=Math.max(ge.top,Math.min(Ge.y,D.clientY)),Q=Math.min(ge.bottom,Math.max(Ge.y,D.clientY));Ge.distanceTo(new yr(D.clientX,D.clientY))>5&&ee({left:ke,top:Yt,width:Math.max(0,Be-ke),height:Math.max(0,Q-Yt)}),D.preventDefault(),D.stopImmediatePropagation()},gt=D=>{if(D.pointerId===Le){Ge.distanceTo(new yr(D.clientX,D.clientY))>5?S.current?.(tg(be,N.getBoundingClientRect(),Ge,{x:D.clientX,y:D.clientY}),!0):In(D,!0),rt(),D.preventDefault(),D.stopImmediatePropagation();return}if(D.pointerId!==Ie)return;Ie=null;const ge=Ge.distanceTo(new yr(D.clientX,D.clientY))>5,ke=Wt;Wt=!1,!(ge||ke)&&In(D)},Ne=D=>{if(D.pointerId===Le){rt();return}D.pointerId===Ie&&(Ie=null,Wt=!1)},Xt=D=>{D.key!=="Escape"||Le===null||(rt(),D.preventDefault())},Fe=(D,ge)=>{te.current=D,oe.current=ge,$(D)},ot=()=>{if(te.current)return;const D=ao(be.instanceToAtom,be.instanceImages,I.current.at(-1)??null,null,0,be.baseImages);Fe(D?.selection??null,D?.instance??null)},rr=()=>Fe(null,null),Ur=D=>{if(D.metaKey||D.ctrlKey||D.altKey)return;const ge=D.key==="ArrowDown"?1:D.key==="ArrowUp"?-1:0;if(ge!==0){D.preventDefault();const Be=ao(be.instanceToAtom,be.instanceImages,te.current,oe.current,ge,be.baseImages);Fe(Be?.selection??null,Be?.instance??null);return}if(D.key!=="Enter"||(D.preventDefault(),D.repeat))return;const ke=ao(be.instanceToAtom,be.instanceImages,te.current??I.current.at(-1)??null,oe.current,0,be.baseImages);ke&&(Fe(ke.selection,ke.instance),v.current(ke.selection,!0))};return N.addEventListener("pointerdown",fe,!0),N.addEventListener("pointermove",Re,!0),N.addEventListener("pointerup",gt,!0),N.addEventListener("pointercancel",Ne,!0),N.addEventListener("focus",ot),N.addEventListener("blur",rr),N.addEventListener("keydown",Ur),window.addEventListener("keydown",Xt),P.setAnimationLoop(()=>{ne.update(),q.children.forEach(D=>{D.visible&&D.quaternion.copy(X.quaternion)}),K.visible&&K.quaternion.copy(X.quaternion),P.render(Y,X)}),()=>{P.setAnimationLoop(null),tr.disconnect(),window.removeEventListener("resize",Cn),N.removeEventListener("pointerdown",fe,!0),N.removeEventListener("pointermove",Re,!0),N.removeEventListener("pointerup",gt,!0),N.removeEventListener("pointercancel",Ne,!0),N.removeEventListener("focus",ot),N.removeEventListener("blur",rr),N.removeEventListener("keydown",Ur),window.removeEventListener("keydown",Xt),ne.removeEventListener("start",lt),ne.dispose(),re.remove(q),re.remove(ue),re.remove(K),re.remove(ie),To(re),To(ie),q.clear(),de.dispose(),se.dispose(),ue.geometry.dispose(),xe.dispose(),pe.dispose(),P.dispose(),k.current=null,_.current?.(null),U.current?.(null)}},[]),w.useEffect(()=>{const N=k.current;if(!N)return;const P=$o[l];$p(N,P),N.model&&_p(N,t,N.model,r,l,P)},[l]),w.useEffect(()=>{const N=k.current;if(!N)return;(N.topologyManifest!==t||!N.preparedTopology)&&(N.topologyManifest=t,N.preparedTopology=Lc(t,n));const P=Pf(t,n,r,N.preparedTopology);if(!P){G.current&&(G.current=null,E.current?.(null)),_.current?.(null);return}const R=le(n,["forces","force"]),Y=le(n,["velocities","velocity","vel"]),H=Vf(P,r,R,Y),X=cp(t,P,H);(G.current?.manifest!==t||!up(G.current.info,X))&&(G.current={manifest:t,info:X},E.current?.(X));const ne=Uf(H),re=Hp(r);r.mode!=="ribbon"&&r.mode!=="polyhedra"&&N.preparedTopology?.count===P.count&&N.renderTopology===N.preparedTopology&&N.renderConfigKey===re&&N.frameLayout!==null&&qf(N.frameLayout,ne)&&zp(N,P,r,R,Y,a,c,H)||(Tp(N),Dp(N,P,t,r,l,R,Y,a,c,H)),N.model=P;const J=N.atomObject?.userData.instanceToAtom,me=N.atomObject?.userData.instanceImages,q=J instanceof Uint32Array&&me instanceof Int8Array,de=q?J:r.mode==="ribbon"?new Uint32Array:P.instanceToAtom,se=q?me:r.mode==="ribbon"?new Int8Array:P.instanceImages,xe=r.mode==="ribbon"?Xp(P,N.ribbonSelections,de,se):{instanceToAtom:de,instanceImages:se};N.instanceToAtom=xe.instanceToAtom,N.instanceImages=xe.instanceImages,N.baseImages=P.baseImages,N.renderTopology=N.preparedTopology,N.renderConfigKey=re,N.frameLayout=ne,_.current?.(lp(t,P));const ue={model:P,presentation:r,preset:u};N.fitContext=ue;const pe=ag(P,r);(N.fittedKey!==pe||N.lastResetSignal!==i||N.lastViewSignal!==f)&&(ic(N,ue),N.fittedKey=pe,N.lastResetSignal=i,N.lastViewSignal=f)},[a,c,n,t,r,i,u,f]),w.useEffect(()=>{const N=k.current;N&&Pp(N.trajectoryOverlays,N.model,s,l)},[l,n,r,s]),w.useEffect(()=>{const N=k.current,P=N?Yp(N,o,!!U.current):null;let R=V,Y=oe.current;const H=document.activeElement===j.current,X=N?Cs(N,R,Y):null,ne=X!==null;if(ne&&(Y=X),H&&!ne&&N){const re=ao(N.instanceToAtom,N.instanceImages,o.at(-1)??null,null,0,N.baseImages);R=re?.selection??null,Y=re?.instance??null,re&&Cs(N,re.selection,re.instance)}else(!N||!ne)&&R&&(R=null,Y=null,N&&Cs(N,null,null));oe.current=Y,Jp(R,V)||(te.current=R,$(R)),U.current?.(P)},[n,V,r,o]);const C=V?eg(t,V):"";return d.jsxs(d.Fragment,{children:[d.jsx("canvas",{ref:j,className:L?"molecule-canvas is-box-selecting":"molecule-canvas",role:"region","aria-label":"Molecular structure","aria-description":"Use Up and Down to browse visible atoms. Press Enter to toggle an atom selection. Shift-drag to select a box.","aria-keyshortcuts":"ArrowUp ArrowDown Enter",tabIndex:0}),L&&d.jsx("div",{className:"selection-marquee","data-testid":"selection-marquee",style:L,"aria-hidden":"true"}),d.jsx("span",{className:"sr-only","aria-live":"polite",children:C?`${C}. Press Enter to toggle selection.`:""})]})});function Ja(e){const t=e.model,n=e.topologyManifest,r=e.fitContext?.presentation;if(!t||!n||!r)throw new Error("The molecular scene is not ready to export");return{model:t,manifest:n,presentation:{...r,cellOrigin:[...r.cellOrigin],mirror:[...r.mirror],images:{min:[...r.images.min],max:[...r.images.max]}},forces:e.forces?.clone(!0)??null,velocities:e.velocities?.clone(!0)??null,camera:e.camera.clone(),target:e.controls.target.clone()}}async function ec(e,t,n){const r=e.getContext();if(r.isContextLost())throw new Error("Figure export is unavailable because the WebGL context was lost");const[{GTAOPass:o},{OutputPass:s},{SSAARenderPass:i},{LineMaterial:a},{LineSegments2:c},{LineSegmentsGeometry:l}]=await Promise.all([Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.G),__vite__mapDeps([0,1])),Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.O),__vite__mapDeps([0,1])),Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.S),__vite__mapDeps([0,1])),Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.L),__vite__mapDeps([0,1])),Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.b),__vite__mapDeps([0,1])),Dn(()=>import("./publication-Br5bSGOw.js").then(H=>H.a),__vite__mapDeps([0,1]))]),u=dp(n,xp(e,r)),f=Ap(t,u,{LineMaterial:a,LineSegments2:c,LineSegmentsGeometry:l}),m=oh(f.root,t.camera,t.target,u.width,u.height,u.projection,u.fit,u.padding);vp(f.scene,f.root,m);const g=e.capabilities.isWebGL2?e.extensions.has("EXT_color_buffer_float"):e.extensions.has("EXT_color_buffer_half_float"),p=g?Uu:Ds,x=new bs(u.width,u.height,{depthBuffer:!0,format:vr,stencilBuffer:!1,type:p});x.texture.name="Publication beauty";const A=new bs(u.width,u.height,{depthBuffer:!1,format:vr,stencilBuffer:!1,type:Ds});A.texture.name="Publication sRGB";const b=u.width*u.height,j=g?Tm(b):0,k=g&&e.capabilities.isWebGL2&&f.hasAoGeometry?Pm(b):0,v=k>0?new bs(u.width,u.height,{depthBuffer:!1,format:vr,stencilBuffer:!1,type:p}):null;v&&(v.texture.name="Publication ambient occlusion");const S=j>0?new i(f.scene,m,0,0):null;S&&(S.sampleLevel=j,S.unbiased=!0);const I=v?jp(o,f.scene,f.root,m,u,k):null,E=new s;E.renderToScreen=!1,Fp(E,u.background);const _=e.getRenderTarget(),U=e.getActiveCubeFace(),G=e.getActiveMipmapLevel(),B=e.getViewport(new fa).clone(),V=e.getScissor(new fa).clone(),$=e.getScissorTest(),L=e.getClearColor(new Me).clone(),ee=e.getClearAlpha(),te=e.xr.enabled,oe=e.toneMapping,C=e.toneMappingExposure,N=e.outputColorSpace,P=e.autoClear,R=new Uint8Array(u.width*u.height*4);let Y=null;try{Ip(r),e.initRenderTarget(x),e.initRenderTarget(A),v&&e.initRenderTarget(v),e.xr.enabled=!1,e.toneMapping=qu,e.toneMappingExposure=je.exposure,e.outputColorSpace=Ao,e.autoClear=!0,e.setScissorTest(!1),S?S.render(e,x,x,0,!1):(e.setRenderTarget(x),e.setViewport(0,0,u.width,u.height),e.setClearColor(0,0),e.clear(!0,!0,!0),e.render(f.scene,m));let H=x;I&&v&&(I.render(e,v,x,0,!1),H=v),E.render(e,A,H,0,!1),e.readRenderTargetPixels(A,0,0,u.width,u.height,R);const X=r.getError();if(X!==r.NO_ERROR)throw new Error(Ep(X,r));if(!Om(R,u.transparent))throw new Error("the rendered image was blank");Lm(R,u.width,u.height),await mp(R,f,m,t,u)}catch(H){Y=H}finally{e.xr.enabled=te,e.toneMapping=oe,e.toneMappingExposure=C,e.outputColorSpace=N,e.autoClear=P,e.setClearColor(L,ee),e.setRenderTarget(_,U,G),e.setViewport(B),e.setScissor(V),e.setScissorTest($),S?.dispose(),I&&(I.gtaoMaterial.dispose(),I.blendMaterial.dispose(),I.dispose()),E.dispose(),x.dispose(),v?.dispose(),A.dispose(),Cp(f)}if(Y){const H=Y instanceof Error?Y.message:"unknown rendering error";throw new Error(`Figure export failed: ${H}`)}return u.format==="tiff"?Vm(R,u):zm(R,u)}function dp(e,t){const n=e.background??(e.transparent?{kind:"transparent"}:{kind:"solid",color:"#ffffff"});if(n.kind==="solid"&&!/^#[0-9a-f]{6}$/i.test(n.color))throw new Error("Figure background must use #RRGGBB");const r=e.dpi??300;if(!Number.isFinite(r)||r<=0||r>1e6)throw new Error("Figure DPI must be between 0 and 1,000,000");return{...Rm({...e,transparent:n.kind==="transparent"},t),format:e.format??"png",dpi:r,background:n,annotations:e.annotations??[]}}async function mp(e,t,n,r,o){if(o.annotations.length===0)return;if(typeof document>"u")throw new Error("Figure annotations require a browser canvas");const s=document.createElement("canvas");s.width=o.width,s.height=o.height;const i=s.getContext("2d");if(!i)throw new Error("Figure annotations are unavailable");const a=i.createImageData(o.width,o.height);a.data.set(e),i.putImageData(a,0,0);const c=Math.max(12,Math.min(34,Math.round(Math.min(o.width,o.height)/70))),l=Math.max(16,Math.round(c*1.25));i.font=await fh(c,hp(r,o.annotations)),i.lineCap="round",i.lineJoin="round";for(const u of o.annotations){if(u.kind==="atom-label"){const f=pp(r.model,u.atom,r.presentation.mode);if(!f)throw new Error("A figure atom label is outside the rendered scene");const m=si(f,n,o.width,o.height);if(!m||m.depth<-1||m.depth>1)continue;const g=u.text??_l(r.manifest,u.atom.atom),p=u.offset??[c*.72,-c*.72];yp(i,g,m.x+p[0],m.y+p[1],c,"left");continue}if(u.kind==="legend"){gp(i,Rl(r,u.content),u.position,o.width,o.height,c,l);continue}bp(i,t.root,n,u.length,u.unit,u.position,o.width,o.height,c,l)}e.set(i.getImageData(0,0,o.width,o.height).data)}function hp(e,t){const n=["PQ","Å","nm"];for(const r of t){if(r.kind==="atom-label"){n.push(r.text??_l(e.manifest,r.atom.atom));continue}if(r.kind==="legend"){n.push(...Rl(e,r.content).map(({label:o})=>o));continue}n.push(Pl(r.length))}return n.join(" ")}function pp(e,t,n){if(n==="ribbon"){const o=Ul(e).map(i=>ql(e,i)),s=Gl(o,e).get(Ht(t.atom,t.image));if(s)return s.position.clone()}const r=new F;for(let o=0;o({color:`#${ht(n,f,r.atomicNumbers[f],"residue","light").getHexString(Ao)}`,label:c[u]?.name?.trim()||`Residue ${u+1}`}))}const i=new Map;for(const a of s){const c=r.atomicNumbers[a];i.has(c)||i.set(c,a)}return[...i.entries()].sort(([a],[c])=>a-c).slice(0,16).map(([a,c])=>({color:`#${ht(n,c,a,o.color,"light").getHexString(Ao)}`,label:n.topology.symbols?.[c]??`Z ${a}`}))}function gp(e,t,n,r,o,s,i){if(t.length===0)return;const a=Math.max(6,Math.round(s*.45)),c=Math.max(8,Math.round(s*.72)),l=Math.round(s*1.4),u=Math.max(...t.map(({label:p})=>e.measureText(p).width))+c+a+s,f=t.length*l+s,{x:m,y:g}=Tl(n,r,o,u,f,i);e.save(),e.fillStyle="rgba(255, 255, 255, 0.88)",e.fillRect(m,g,u,f),t.forEach((p,x)=>{const A=g+s*.5+l*(x+.5);e.fillStyle=p.color,e.beginPath(),e.arc(m+s,A,c*.5,0,Math.PI*2),e.fill(),e.fillStyle="#17302e",e.textAlign="left",e.textBaseline="middle",e.fillText(p.label,m+s+c*.5+a,A)}),e.restore()}function bp(e,t,n,r,o,s,i,a,c,l){if(!(n instanceof Nc))throw new Error("Scale bars require orthographic projection");const u=o==="nanometer"?r*10:r,f=new kt().setFromObject(t).getCenter(new F),m=new F(1,0,0).applyQuaternion(n.quaternion),g=si(f,n,i,a),p=si(f.clone().addScaledVector(m,u),n,i,a);if(!g||!p)throw new Error("Scale bar could not be projected");const x=Math.hypot(p.x-g.x,p.y-g.y);if(!Number.isFinite(x)||x<8||x>i*.7)throw new Error("Scale bar length does not fit the figure");const A=x+c*1.5,b=c*2.6,j=Tl(s,i,a,A,b,l),k=j.y+c*.78,v=j.x+c*.75;e.save(),e.strokeStyle="#17302e",e.fillStyle="#17302e",e.lineWidth=Math.max(2,Math.round(c*.12)),e.beginPath(),e.moveTo(v,k),e.lineTo(v+x,k),e.moveTo(v,k-c*.22),e.lineTo(v,k+c*.22),e.moveTo(v+x,k-c*.22),e.lineTo(v+x,k+c*.22),e.stroke(),e.textAlign="center",e.textBaseline="top",e.fillText(`${Pl(r)} ${o==="nanometer"?"nm":"Å"}`,v+x*.5,k+c*.45),e.restore()}function Tl(e,t,n,r,o,s){return{x:e.endsWith("right")?t-s-r:s,y:e.startsWith("bottom")?n-s-o:s}}function yp(e,t,n,r,o,s){e.save(),e.textAlign=s,e.textBaseline="middle",e.strokeStyle="rgba(255, 255, 255, 0.94)",e.lineWidth=Math.max(3,o*.28),e.strokeText(t,n,r),e.fillStyle="#17302e",e.fillText(t,n,r),e.restore()}function Pl(e){return Number(e.toPrecision(5)).toString()}function xp(e,t){const n=t.getParameter(t.MAX_VIEWPORT_DIMS),r=e.capabilities.maxTextureSize,o=Number(t.getParameter(t.MAX_RENDERBUFFER_SIZE));return{maxWidth:Math.max(1,Math.floor(Math.min(r,o,Number(n[0])))),maxHeight:Math.max(1,Math.floor(Math.min(r,o,Number(n[1])))),maxPixels:Sl}}const je={background:"#ffffff",bond:"#48575a",bondOpacity:1,cell:"#4f7882",cellOpacity:.46,selection:"#3DACCB",selectionOpacity:0,force:"#b34c2b",velocity:"#625c9f",displacement:"#087F8C",ribbon:"#347f96",hemisphereSky:"#ffffff",hemisphereGround:"#d8e0df",hemisphereIntensity:1.18,key:"#ffffff",keyIntensity:2.1,rim:"#c9e0e4",rimIntensity:.2,exposure:.98},wp={...je,bond:"#9aa7a9"};function Ap(e,t,n){const{model:r,manifest:o,presentation:s}=e,i=new Fc;i.background=null;const a=new kn;i.add(a);const c={geometries:new Set,materials:new Set,textures:new Set},l=r.instanceToAtom.length<=12e3?{...s,quality:"high"}:s;if(s.mode==="ribbon"){const f=zl(r,o,l,"light",je);f&&(yn(f,c),Ot(f,c),a.add(f));const m=Vl(r,o);if(m){const g={...l,mode:"ball-stick"},p=_o(m,o,g,"light",!0,"ball-stick");p&&(yn(p,c),Ot(p,c),a.add(p));const x=Gs(m,g,!1),A=Nr(g,je,x.segments.length>ko?"lines":"instances",x.segments,!0);A&&(yn(A,c),Ot(A,c),a.add(A))}}else{const f=s.mode==="polyhedra"?Dl(r,o,l,"light",je,!0):null,m=_o(r,o,l,"light",!0,s.mode==="polyhedra"&&!f?"ball-stick":void 0);m&&(yn(m,c),Ot(m,c),a.add(m));const g=Gs(r,s,t.periodicContext),p=g.segments.filter(S=>!S.context),x=g.segments.filter(S=>S.context),A=m instanceof fn,b=s.mode==="lines"||A||g.segments.length>ko?"lines":"instances",j=f?null:Nr(l,je,b,p,!0);j&&(yn(j,c),Ot(j,c),a.add(j));const k=f?null:Nr(l,wp,b,x,!0);k&&(yn(k,c),Ot(k,c),a.add(k));const v=Sp(r,o,s,g.contextAtoms,rh(A,r.instanceToAtom.length+g.contextAtoms.length,xi));v&&(yn(v,c),Ot(v,c),a.add(v)),f&&(Ot(f,c),a.add(f))}if(s.cell){const f=Mp(r,t.width,t.height,n);f&&(Ot(f,c),a.add(f))}e.forces&&a.add(nc(e.forces,je.force,c)),e.velocities&&a.add(nc(e.velocities,je.velocity,c));let u=!1;return a.traverse(f=>{f instanceof Zn&&f.userData.publicationExcludeFromAo!==!0&&(u=!0)}),{scene:i,root:a,resources:c,hasAoGeometry:u}}function Sp(e,t,n,r,o){if(r.length===0)return null;if(o){const f=new Float32Array(r.length*3),m=new Float32Array(r.length*3);r.forEach(({atomIndex:p,position:x},A)=>{x.toArray(f,A*3),ht(t,p,e.atomicNumbers[p],n.color,"light").toArray(m,A*3)});const g=new Kt;return g.setAttribute("position",new he(f,3)),g.setAttribute("color",new he(m,3)),g.computeBoundingSphere(),new fn(g,new Lr({vertexColors:!0,size:n.mode==="lines"?.14:.22,sizeAttenuation:!0}))}const i=e.instanceToAtom.length+r.length<=5e3?[40,28]:[24,16],a=new Ic(1,i[0],i[1]),c=new Jn({roughness:.7,metalness:0}),l=new Ce(a,c,r.length),u=new Br;return r.forEach(({atomIndex:f,position:m},g)=>{u.position.copy(m),u.scale.setScalar((e.radii[f]??.25)*.9),u.updateMatrix(),l.setMatrixAt(g,u.matrix),l.setColorAt(g,ht(t,f,e.atomicNumbers[f],n.color,"light"))}),l.instanceMatrix.needsUpdate=!0,l.instanceColor&&(l.instanceColor.needsUpdate=!0),l.computeBoundingSphere(),l}function Mp(e,t,n,{LineMaterial:r,LineSegments2:o,LineSegmentsGeometry:s}){if(!e.basis||e.images.length===0)return null;const i=[],a=new Set;for(const f of e.images){const m=[];Oi(m,e.basis,f,e.cellCenter);for(let g=0;g{const s=o;if(!s.material)return;const a=(Array.isArray(s.material)?s.material:[s.material]).map(c=>{const l=c.clone();return"color"in l&&l.color instanceof Me&&l.color.set(t),n.materials.add(l),l});s.material=Array.isArray(s.material)?a:a[0]}),r}function yn(e,t){e.traverse(n=>{const r=n,o=Array.isArray(r.material)?r.material:r.material?[r.material]:[];for(const s of o){if(s.opacity=1,s.transparent=!1,s instanceof Jn&&(s.roughness=.64,s.metalness=0),s instanceof Lr){const i=kp();s.map=i,s.alphaTest=.04,s.transparent=!0,s.depthWrite=!0,s.size*=1.08,t.textures.add(i)}s.needsUpdate=!0}})}function kp(){const t=new Uint8Array(9216);for(let r=0;r<48;r+=1)for(let o=0;o<48;o+=1){const s=(o+.5)/48*2-1,i=(r+.5)/48*2-1,a=Math.sqrt(s*s+i*i),c=Se.clamp((1-a)*12,0,1),l=(r*48+o)*4;t[l]=255,t[l+1]=255,t[l+2]=255,t[l+3]=Math.round(c*255)}const n=new Ec(t,48,48,vr);return n.needsUpdate=!0,n}function Ot(e,t){e.traverse(n=>{const r=n;r.geometry&&t.geometries.add(r.geometry),(Array.isArray(r.material)?r.material:r.material?[r.material]:[]).forEach(s=>t.materials.add(s))})}function vp(e,t,n){const r=new kt().setFromObject(t),o=r.getCenter(new F),s=Math.max(r.getSize(new F).length(),1),i=new F(1,0,0).applyQuaternion(n.quaternion),a=new F(0,1,0).applyQuaternion(n.quaternion),c=new F(0,0,1).applyQuaternion(n.quaternion);e.add(new Cc(je.hemisphereSky,je.hemisphereGround,je.hemisphereIntensity));const l=new kr(je.key,je.keyIntensity);l.position.copy(o).addScaledVector(i,-s*.75).addScaledVector(a,s).addScaledVector(c,s*1.1),l.target.position.copy(o),e.add(l,l.target);const u=new kr("#dce9eb",.48);u.position.copy(o).addScaledVector(i,s).addScaledVector(a,s*.2).addScaledVector(c,s*.45),u.target.position.copy(o),e.add(u,u.target);const f=new kr(je.rim,je.rimIntensity);f.position.copy(o).addScaledVector(i,-s*.4).addScaledVector(a,-s*.3).addScaledVector(c,-s),f.target.position.copy(o),e.add(f,f.target)}function jp(e,t,n,r,o,s){const i=Math.max(1,Math.round(o.width*s)),a=Math.max(1,Math.round(o.height*s)),c=new kt().setFromObject(n),l=Se.clamp(c.getSize(new F).length()*.018,.18,.55),u=new e(t,r,i,a);return u.pdNoiseTexture.dispose(),u.pdNoiseTexture=Np(),u.pdMaterial.uniforms.tNoise.value=u.pdNoiseTexture,u.renderToScreen=!1,u.blendIntensity=.38,u.setSceneClipBox(c),u.updateGtaoMaterial({radius:l,thickness:l*2.5,distanceExponent:1,distanceFallOff:1,scale:1,samples:16,screenSpaceRadius:!1}),u.updatePdMaterial({samples:8,rings:2,radius:4,radiusExponent:2}),u}function Np(e=64){const t=new Uint8Array(e*e*4);let n=1831565813;for(let o=0;o>>17,n^=n<<5,t[o]=n>>>24;const r=new Ec(t,e,e,vr,Ds);return r.wrapS=da,r.wrapT=da,r.needsUpdate=!0,r}function Fp(e,t){const n="gl_FragColor = texture2D( tDiffuse, vUv );",r=e.material.fragmentShader,o=r.replace("uniform sampler2D tDiffuse;",`uniform sampler2D tDiffuse; -uniform float publicationTransparent; -uniform vec3 publicationBackground;`).replace(n,`${n} - float publicationCoverage = gl_FragColor.a; - gl_FragColor.rgb = publicationCoverage > 0.000001 - ? gl_FragColor.rgb / publicationCoverage - : vec3( 0.0 );`).replace("// color space",`if ( publicationTransparent < 0.5 ) { - gl_FragColor.rgb = gl_FragColor.rgb * publicationCoverage + publicationBackground * ( 1.0 - publicationCoverage ); - gl_FragColor.a = 1.0; - } - - // color space`);if(o===r||!o.includes("publicationCoverage"))throw new Error("Publication output shader is incompatible");e.material.fragmentShader=o,e.material.uniforms.publicationTransparent={value:t.kind==="transparent"?1:0},e.material.uniforms.publicationBackground={value:new Me(t.kind==="solid"?t.color:"#000000")},e.material.needsUpdate=!0}function Cp(e){e.root.traverse(t=>{t instanceof Ce&&t.dispose()}),e.resources.geometries.forEach(t=>t.dispose()),e.resources.materials.forEach(t=>t.dispose()),e.resources.textures.forEach(t=>t.dispose())}function Ip(e){for(let t=0;t<16&&e.getError()!==e.NO_ERROR;t+=1);}function Ep(e,t){return e===t.OUT_OF_MEMORY?"the GPU could not allocate the requested image":e===t.INVALID_VALUE?"the requested image dimensions are unsupported":e===t.INVALID_FRAMEBUFFER_OPERATION?"the export framebuffer is incomplete":`WebGL error 0x${e.toString(16)}`}function $p(e,t){e.scene.background instanceof Me&&e.scene.background.set(t.background),e.renderer.toneMappingExposure=t.exposure,e.hemisphere.color.set(t.hemisphereSky),e.hemisphere.groundColor.set(t.hemisphereGround),e.hemisphere.intensity=t.hemisphereIntensity,e.key.color.set(t.key),e.key.intensity=t.keyIntensity,e.rim.color.set(t.rim),e.rim.intensity=t.rimIntensity,e.selectionMaterial.color.set(t.selection),e.selectionMaterial.opacity=t.selectionOpacity,e.selectionPointsMaterial.color.set(t.selection),e.keyboardFocusMaterial.color.set(t.selection)}function _p(e,t,n,r,o,s){const i=e.atomObject?.userData.instanceToAtom instanceof Uint32Array?e.atomObject.userData.instanceToAtom:n.instanceToAtom;if(e.atomObject instanceof fn){const a=e.atomObject.geometry.getAttribute("color");for(let c=0;cMr(a,s.force)),e.velocities?.children.forEach(a=>Mr(a,s.velocity)),e.ribbon&&Ol(e.ribbon.geometry,t,n,r,o,s),e.polyhedra&&Rp(e.polyhedra,t,n,r,o,s)}function Ol(e,t,n,r,o,s){const i=e.getAttribute("color"),a=e.getAttribute("atomIndex"),c=e.getAttribute("secondaryStructure");if(!(i instanceof he)||!(a instanceof he))return;const l=new Me(s.ribbon),u=o==="light"?[new Me("#3f817e"),new Me("#c94f5b"),new Me("#d99a2b")]:[new Me("#6bb7b2"),new Me("#ed7d86"),new Me("#f1c15a")];for(let f=0;f{if(i.userData.polyhedronEdges===!0){Mr(i,s.bond);return}if(!(i instanceof Zn))return;const a=i.geometry.getAttribute("color"),c=i.geometry.getAttribute("centerAtomIndex");if(!(!(a instanceof he)||!(c instanceof he))){for(let l=0;l{const o=r.material;(Array.isArray(o)?o:o?[o]:[]).forEach(i=>{"color"in i&&i.color instanceof Me&&i.color.set(t),n!==void 0&&"opacity"in i&&(i.opacity=n)})})}function Tp(e){for(const t of[e.atomObject,e.bonds,e.cell,e.forces,e.velocities,e.ribbon,e.polyhedra])t&&(e.root.remove(t),To(t));e.atomObject=null,e.bonds=null,e.cell=null,e.forces=null,e.velocities=null,e.ribbon=null,e.polyhedra=null,e.ribbonSelections.clear(),e.pickables=[]}function Pp(e,t,n,r){for(;e.children.length>0;){const l=e.children[e.children.length-1];e.remove(l),To(l)}if(!t)return;const o=$o[r],s=new Me(o.background),i=new Me(o.selection);for(const l of n.trails.slice(0,tp)){const u=Op(t,l);if(u.length===0)continue;const f=u.length/6,m=new Float32Array(f*6);for(let x=0;xLp(t,l)).filter(l=>l!==null),c=Bp(a,o.displacement);c&&(c.name="reference-displacements",e.add(c))}function Op(e,t){if(!Number.isSafeInteger(t.atom)||t.atom<0||t.atom>=e.count||t.image.length!==3||!t.image.every(Number.isInteger)||t.points.length<6||t.points.length%3!==0)return new Float32Array;const n=Math.min(np,Math.floor(t.points.length/3)),r=Math.floor(t.points.length/3)-n,o=t.points.length-3,s=new F().fromArray(t.points,o);if(![s.x,s.y,s.z].every(Number.isFinite))return new Float32Array;const i=Ll(e,t.atom,t.image);if(!i)return new Float32Array;const a=[],c=new F;for(let u=r;u=e.count||n.length!==3||!n.every(Number.isInteger))return null;const r=new F().fromArray(e.positions,t*3);if(!e.basis)return r;const o=t*3,s=[n[0]-(e.baseImages[o]??0),n[1]-(e.baseImages[o+1]??0),n[2]-(e.baseImages[o+2]??0)];return r.add(qt(s,e.basis))}function Lp(e,t){const n=Ll(e,t.atom,t.image);if(!n||![...t.from,...t.to].every(Number.isFinite))return null;const r=new F(t.to[0]-t.from[0],t.to[1]-t.from[1],t.to[2]-t.from[2]).applyMatrix3(e.displayTransform),o=r.length();if(!Number.isFinite(o)||o<=1e-10)return null;const s=r.clone().multiplyScalar(1/o),i=Math.min(.22,Math.max(.07,o*.18),o*.45);return{tail:n.clone().sub(r),tip:n,direction:s,head:i}}function Bp(e,t){if(e.length===0)return null;const n=new kn,r=new Ce(new pi(.014,.014,1,8,1,!1),new Xn({color:t,transparent:!0,opacity:.82,depthWrite:!1}),e.length);r.instanceMatrix.setUsage(Nt),n.add(r);const o=new Ce(new $c(1,1,9),new Xn({color:t,transparent:!0,opacity:.88,depthWrite:!1}),e.length);return o.instanceMatrix.setUsage(Nt),n.add(o),Ro(n,[...e]),n}function Dp(e,t,n,r,o,s,i,a,c,l){const u=$o[o];if(r.mode==="ribbon"){if(e.ribbon=zl(t,n,r,o,u),e.ribbon){e.root.add(e.ribbon),e.pickables.push(e.ribbon);const m=e.ribbon.userData.ribbonSelections;m instanceof Map&&(e.ribbonSelections=m)}const f=Vl(t,n);if(f){const m={...r,mode:"ball-stick"};e.atomObject=_o(f,n,m,o,!1,"ball-stick"),e.atomObject&&(e.atomObject.userData.instanceToAtom=f.instanceToAtom,e.atomObject.userData.instanceImages=f.instanceImages,e.root.add(e.atomObject),e.pickables.push(e.atomObject));const g=Gs(f,m,!1);e.bonds=Nr(m,u,g.segments.length>ko?"lines":"instances",g.segments),e.bonds&&e.root.add(e.bonds)}}else e.polyhedra=r.mode==="polyhedra"?Dl(t,n,r,o,u):null,e.atomObject=_o(t,n,r,o,!1,r.mode==="polyhedra"&&!e.polyhedra?"ball-stick":void 0),e.atomObject&&(e.root.add(e.atomObject),e.pickables.push(e.atomObject)),e.bonds=e.polyhedra?null:Nr(r,u,l.bondKind,l.bondSegments),e.bonds&&e.root.add(e.bonds),e.polyhedra&&e.root.add(e.polyhedra);e.cell=r.cell?Kp(t,u):null,e.cell&&e.root.add(e.cell),e.forces=r.forces?oc(t,s,a,u.force,l.forceInstances):null,e.forces&&e.root.add(e.forces),e.velocities=r.velocities?oc(t,i,c,u.velocity,l.velocityInstances):null,e.velocities&&e.root.add(e.velocities)}function zp(e,t,n,r,o,s,i,a){if(!Vp(e,a))return!1;const c=a.forceInstances.length>0?ii(t,r,s,a.forceInstances):[],l=a.velocityInstances.length>0?ii(t,o,i,a.velocityInstances):[];return c.length!==a.forceInstances.length||l.length!==a.velocityInstances.length?!1:(e.atomObject&&Up(e.atomObject,t),e.bonds&&qp(e.bonds,a.bondSegments),e.cell&&Gp(e.cell,t),e.forces&&Ro(e.forces,c),e.velocities&&Ro(e.velocities,l),n.mode!=="ribbon"&&n.mode!=="polyhedra")}function Vp(e,t){if(e.ribbon||e.polyhedra)return!1;if(t.atomKind==="none"){if(e.atomObject)return!1}else if(t.atomKind==="points"){if(!(e.atomObject instanceof fn)||e.atomObject.geometry.getAttribute("position").count!==t.atomCount)return!1}else if(!(e.atomObject instanceof Ce)||e.atomObject.instanceMatrix.count!==t.atomCount)return!1;if(t.bondKind==="none"){if(e.bonds)return!1}else if(t.bondKind==="lines"){if(!(e.bonds instanceof er)||e.bonds.geometry.getAttribute("position").count!==t.bondSegments.length*2)return!1}else if(!(e.bonds instanceof Ce)||e.bonds.instanceMatrix.count!==t.bondSegments.length)return!1;if(t.cellLineCount===0){if(e.cell)return!1}else if(!e.cell||e.cell.geometry.getAttribute("position").count!==t.cellLineCount*2)return!1;return rc(e.forces,t.forceInstances.length)&&rc(e.velocities,t.velocityInstances.length)}function rc(e,t){if(t===0)return e===null;const[n,r]=e?.children??[];return n instanceof Ce&&r instanceof Ce&&n.instanceMatrix.count===t&&r.instanceMatrix.count===t}function Up(e,t){const n=new F;if(e instanceof fn){const o=e.geometry.getAttribute("position");for(let s=0;s{n.setXYZ(s*2,r.x,r.y,r.z),n.setXYZ(s*2+1,o.x,o.y,o.z)}),n.needsUpdate=!0,e.geometry.computeBoundingSphere();return}e instanceof Ce&&Bl(e,t)}function Gp(e,t){if(!t.basis)return;const n=[];t.images.forEach(o=>Oi(n,t.basis,o,t.cellCenter));const r=e.geometry.getAttribute("position");r.array.set(n),r.needsUpdate=!0,e.geometry.computeBoundingSphere()}function Hp(e){return JSON.stringify([e.mode,e.water,e.hydrogens,e.images.min,e.images.max,e.cell,e.forces,e.velocities,e.atomScale,e.bondScale,e.color,e.quality])}function _o(e,t,n,r,o=!1,s){const i=e.instanceToAtom.length;if(i===0)return null;if(zc(n,i)){const g=new Float32Array(i*3),p=new Float32Array(i*3),x=new F;for(let b=0;b{m.toArray(u,p*6),g.toArray(u,p*6+3)});const f=new Kt;return f.setAttribute("position",new he(u,3).setUsage(Nt)),new er(f,new Oo({color:t.bond,transparent:!0,opacity:t.bondOpacity}))}const s=(e.mode==="licorice"?.14:e.mode==="polyhedra"?.025:.045)*Math.max(.1,e.bondScale),i=o&&r.length<=12e3?16:Vc(e,r.length)?12:8,a=new pi(s,s,1,i,1,!1),c=new Jn({color:t.bond,roughness:.56,metalness:.01,transparent:!0,opacity:t.bondOpacity}),l=new Ce(a,c,r.length);return l.instanceMatrix.setUsage(Nt),Bl(l,r),l}function Bl(e,t){const n=new Br,r=new F;t.forEach(({from:o,to:s},i)=>{r.subVectors(s,o);const a=r.length();n.position.copy(o).add(s).multiplyScalar(.5),n.quaternion.setFromUnitVectors(oi,r.normalize()),n.scale.set(1,a,1),n.updateMatrix(),e.setMatrixAt(i,n.matrix)}),e.instanceMatrix.needsUpdate=!0,e.computeBoundingSphere()}function Kp(e,t){if(!e.basis||e.images.length===0)return null;const n=[];e.images.forEach(o=>Oi(n,e.basis,o,e.cellCenter));const r=new Kt;return r.setAttribute("position",new Lt(n,3).setUsage(Nt)),new er(r,new Oo({color:t.cell,transparent:!0,opacity:t.cellOpacity}))}function Oi(e,t,n,r){const o=Gc(t,n,r),s=(a,c,l)=>a*4+c*2+l,i=[];for(let a=0;a<=1;a+=1){for(let c=0;c<=1;c+=1)i.push([s(a,c,0),s(a,c,1)]);for(let c=0;c<=1;c+=1)i.push([s(a,0,c),s(a,1,c)])}for(let a=0;a<=1;a+=1)for(let c=0;c<=1;c+=1)i.push([s(0,a,c),s(1,a,c)]);i.forEach(([a,c])=>e.push(...o[a].toArray(),...o[c].toArray()))}function oc(e,t,n,r,o){const s=ii(e,t,n,o);if(s.length===0)return null;const i=new kn,a=new Ce(new pi(.018,.018,1,8,1,!1),new Xn({color:r}),s.length);a.instanceMatrix.setUsage(Nt),i.add(a);const c=new Ce(new $c(1,1,9),new Xn({color:r}),s.length);return c.instanceMatrix.setUsage(Nt),i.add(c),Ro(i,s),i}function ii(e,t,n,r){if(!t||t.length{const f=e.instanceToAtom[u];return Math.hypot(t[f*3],t[f*3+1],t[f*3+2])}).filter(u=>Number.isFinite(u)&&u>1e-12).sort((u,f)=>u-f);if(o.length===0)return[];const i=1.45/o[Math.floor((o.length-1)*.9)]*n,a=[],c=new F,l=new F;for(const u of r){const f=e.instanceToAtom[u],m=f*3;c.set(t[m],t[m+1],t[m+2]);const g=c.length();rd(c.normalize(),e),It(l,e,u);const p=g*i,x=Math.min(Math.min(.24,Math.max(.075,p*.24)),p*.5),A=(e.radii[f]??.3)*1.03;a.push({tail:l.clone().addScaledVector(c,A),tip:l.clone().addScaledVector(c,A+p),direction:c.clone(),head:x})}return a}function Ro(e,t){const[n,r]=e.children;if(!(n instanceof Ce)||!(r instanceof Ce))return;const o=new Br,s=new F;t.forEach((i,a)=>{s.copy(i.tip).addScaledVector(i.direction,-i.head*.48),o.position.copy(i.tail).add(s).multiplyScalar(.5),o.quaternion.setFromUnitVectors(oi,i.direction),o.scale.set(1,i.tail.distanceTo(s),1),o.updateMatrix(),n.setMatrixAt(a,o.matrix)}),n.instanceMatrix.needsUpdate=!0,t.forEach((i,a)=>{o.position.copy(i.tip).addScaledVector(i.direction,-i.head*.5),o.quaternion.setFromUnitVectors(oi,i.direction),o.scale.set(i.head*.34,i.head,i.head*.34),o.updateMatrix(),r.setMatrixAt(a,o.matrix)}),r.instanceMatrix.needsUpdate=!0,n.computeBoundingSphere(),n.boundingBox=null,r.computeBoundingSphere(),r.boundingBox=null}function Dl(e,t,n,r,o,s=!1){const i=new Set(e.visibleAtoms),a=Oh({positions:e.positions,atomicNumbers:e.atomicNumbers,bonds:e.bonds.filter(([m,g])=>i.has(m)&&i.has(g)),basis:e.basis,pbc:e.pbc},{images:e.images,maxCenters:e.visibleAtoms.length>24?8:64,colorForCenter:(m,g)=>ht(t,m,g,n.color,r)});if(!a)return null;const c=new kn,l=new Zn(a,new Jn({vertexColors:!0,transparent:!0,opacity:s?.34:r==="light"?.28:.34,depthWrite:!0,roughness:.58,metalness:0,flatShading:!0,side:So,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}));l.userData.publicationExcludeFromAo=!0,l.renderOrder=1,c.add(l);const u=new Kt;u.setAttribute("position",new he(a.userData.edgePositions instanceof Float32Array?a.userData.edgePositions:new Float32Array,3)),u.computeBoundingSphere();const f=new er(u,new Oo({color:o.bond,transparent:!0,opacity:s?.42:.36,depthWrite:!1}));return f.userData.polyhedronEdges=!0,f.userData.publicationExcludeFromAo=!0,f.renderOrder=2,c.add(f),c}function zl(e,t,n,r,o){if(e.backbone.length<3)return null;const s=new Map((t.topology.residues??[]).filter(f=>f.secondary_structure).map(f=>[f.index,f.secondary_structure])),i=e.images.map(f=>qt(f,e.basis)),a=Ul(e).map(f=>ql(e,f)),c=[];for(const f of a){const m=Fl(f);for(let p=0;p1&&c.forEach(f=>f.dispose()),Ol(l,t,e,n,r,o);const u=new Zn(l,new Jn({vertexColors:!0,roughness:.5,metalness:0,dithering:!0,side:So}));return u.userData.ribbonSelections=Gl(a,e),u}function Vl(e,t){const n=new Map((t.topology.residues??[]).map(c=>[c.index,c])),r=t.topology.atom_residue_index??[],o=e.visibleAtoms.filter(c=>n.get(r[c]??-1)?.category!=="amino-acid");if(o.length===0)return null;const s=new Set(o),i=[],a=[];for(let c=0;cs.has(c)&&s.has(l)),visibleAtoms:o,instanceToAtom:Uint32Array.from(i),instanceImages:Int8Array.from(a),radii:e.atomicNumbers.map(c=>wi(c,"ball-stick",.82)),backbone:[]}}function Ul(e){const t=[];for(const n of e.backbone){const r=n.runIndex??0;for(;t.length<=r;)t.push([]);t[r].push(n)}return t.filter(n=>n.length>=3)}function ql(e,t){const n=[];for(const r of t){const o=new F().fromArray(e.positions,r.ca*3),s=n.length===0?o:Un(n[n.length-1].ca,o,e.basis,e.pbc),i=Wp(o,s,e.basis,e.pbc),a=r.ca*3,c=[(e.baseImages[a]??0)+i[0],(e.baseImages[a+1]??0)+i[1],(e.baseImages[a+2]??0)+i[2]],l=Un(s,new F().fromArray(e.positions,r.n*3),e.basis,e.pbc),u=Un(s,new F().fromArray(e.positions,r.c*3),e.basis,e.pbc),f=Un(u,new F().fromArray(e.positions,r.o*3),e.basis,e.pbc);n.push({atomIndex:r.ca,residueIndex:r.residueIndex,image:c,n:l,ca:s,c:u,o:f})}return n}function Wp(e,t,n,r){if(!n)return[0,0,0];const o=t.clone().sub(e);return[r[0]?Math.round(o.dot(n.reciprocal[0])):0,r[1]?Math.round(o.dot(n.reciprocal[1])):0,r[2]?Math.round(o.dot(n.reciprocal[2])):0]}function Gl(e,t){const n=new Map;for(const r of e)for(const o of r)for(const s of t.images){const i=[(o.image?.[0]??0)+s[0],(o.image?.[1]??0)+s[1],(o.image?.[2]??0)+s[2]],a={atom:o.atomIndex,image:i};n.set(Ht(a.atom,a.image),{selection:a,position:o.ca.clone().add(qt(s,t.basis))})}return n}function Xp(e,t,n,r){const o=[],s=[],i=new Set,a=c=>{const l=Ht(c.atom,c.image);if(i.has(l))return;const u=c.atom*3,f=c.image.map((m,g)=>m-(e.baseImages[u+g]??0));f.some(m=>m<-127||m>127)||(i.add(l),o.push(c.atom),s.push(f[0],f[1],f[2]))};for(const c of t.values())a(c.selection);for(let c=0;c{b>=0&&b{if(!p.has(j)){if(p.add(j),i&&v.toArray(i,k*3),c)v.toArray(c,o*3);else{let S=e.selection.children[o];S||(S=new Zn(e.selectionGeometry,e.selectionMaterial),S.renderOrder=10,e.selection.add(S)),S.position.copy(v),S.scale.setScalar(Math.max(.24,r.radii[b]||.3)*1.35),S.visible=!0}m&&(m[k]=1),o+=1}};for(const[b,j]of e.ribbonSelections??[]){const k=s.get(b);k!==void 0&&x(j.selection.atom,b,k,j.position)}for(let b=0;bA;)e.selection.remove(e.selection.children[e.selection.children.length-1]);return c?(a.needsUpdate=!0,e.selectionPoints.geometry.setDrawRange(0,o),e.selectionPoints.visible=o>0):(e.selectionPoints.geometry.setDrawRange(0,0),e.selectionPoints.visible=!1),i&&m&&t.length>0&&m.every(b=>b===1)?i:null}function Cs(e,t,n){const r=e.model;if(e.keyboardFocus.visible=!1,!r||!t)return null;const o=e.ribbonSelections.get(Ht(t.atom,t.image));if(o)return e.keyboardFocus.position.copy(o.position),e.keyboardFocus.scale.setScalar(Math.max(.24,r.radii[t.atom]||.3)*1.62),e.keyboardFocus.visible=!0,-1;if(n!==null&&Pr(e.instanceToAtom,e.instanceImages,n,t,e.baseImages))return sc(e,t,n),n;for(let s=0;s=0?a:0:a>=0?(a+Math.sign(o)+i)%i:o<0?i-1:0,l=Li(e,t,c,s);return l?{selection:l,instance:c}:null}function Pr(e,t,n,r,o=new Int32Array){if(!r||!Number.isInteger(n)||n<0||n>=e.length)return!1;const s=n*3,i=e[n]*3;return s+2n.distanceToSquared(new F().fromBufferAttribute(r,u))=0&&Hl(c)?{atom:a,image:c}:null}function Li(e,t,n,r=new Int32Array){if(!Number.isInteger(n)||n<0||n>=e.length)return null;const o=n*3;if(o+2>=t.length)return null;const s=e[n],i=s*3;return{atom:s,image:[(r[i]??0)+t[o],(r[i+1]??0)+t[o+1],(r[i+2]??0)+t[o+2]]}}function Hl(e){return e.length===3&&e.every(Number.isInteger)}function Ht(e,t){return`${e}:${t[0]}:${t[1]}:${t[2]}`}function Jp(e,t){return!e||!t?e===t:Ht(e.atom,e.image)===Ht(t.atom,t.image)}function eg(e,t){const n=`${e.topology.symbols?.[t.atom]??"Atom"} ${t.atom+1}`,r=t.image.map((o,s)=>{if(o===0)return"";const i=o>0?"+":"−",a=Math.abs(o)===1?"":Math.abs(o);return`${i}${a}${"abc"[s]}`}).join("");return r?`${n} (${r})`:n}function tg(e,t,n,r){const o=e.model;if(!o||t.width<=0||t.height<=0)return[];const s=Math.max(t.left,Math.min(n.x,r.x)),i=Math.min(t.right,Math.max(n.x,r.x)),a=Math.max(t.top,Math.min(n.y,r.y)),c=Math.min(t.bottom,Math.max(n.y,r.y));if(i<=s||c<=a)return[];e.camera.updateMatrixWorld();const l=new F,u=[],f=new Set,m=(g,p)=>{const x=Ht(g.atom,g.image);if(f.has(x)||(l.copy(p),l.project(e.camera),!Number.isFinite(l.x)||!Number.isFinite(l.y)||l.z<-1||l.z>1))return;const A=t.left+(l.x+1)*.5*t.width,b=t.top+(1-l.y)*.5*t.height;Ai||bc||(f.add(x),u.push(g))};for(const g of e.ribbonSelections.values())m(g.selection,g.position);for(let g=0;gcg(s,n.basis,c,n.cellCenter));const i=o.getSize(new F).length(),a=s.getSize(new F).length();Gf(i,a,n.images)&&o.union(s)}return o.isEmpty()?null:o}function ic(e,t){const n=rg(e,t);if(!n)return;Kl(e.controls);const r=t.presentation.mode==="ribbon"&&t.preset==="perspective"&&t.model.images.length===1?og(t.model):null,o=r?dh(r,t.model.backbone.map((b,j)=>j),t.presentation.atomScale,e.camera.aspect):null,s=o?.center??n.getCenter(new F),i=Se.degToRad(e.camera.fov*.5),a=Math.atan(Math.tan(i)*e.camera.aspect),c=Math.min(i,a),{direction:l,up:u}=o??ug(t.preset),f=new F().crossVectors(u,l).normalize(),m=new F().crossVectors(l,f).normalize(),g=.78,p=o?.points??lg(n),x=o?.radius??0;let A=1.6/Math.tan(c)*1.08;for(const b of p){const j=b.clone().sub(s),k=j.dot(l);A=Math.max(A,k+(Math.abs(j.dot(f))+x)/(Math.tan(a)*g),k+(Math.abs(j.dot(m))+x)/(Math.tan(i)*g))}e.camera.up.copy(u),e.camera.position.copy(s).addScaledVector(l,A),e.camera.near=Math.max(A/500,.01),e.camera.far=Math.max(A*30,100),e.camera.updateProjectionMatrix(),e.controls.target.copy(s),e.controls.update(),e.lastFittedAspect=e.camera.aspect,e.cameraMode="fit"}function og(e){const t=new Float32Array(e.backbone.length*3);let n=null,r;for(let o=0;o0&&s!==r&&(n=null);const i=new F().fromArray(e.positions,e.backbone[o].ca*3),a=n?Un(n,i,e.basis,e.pbc):i;a.toArray(t,o*3),n=a,r=s}return t}function sg(e,t){return{position:e.position.toArray(),target:t.toArray(),up:e.up.toArray(),fov:e.fov,zoom:e.zoom,near:e.near,far:e.far}}function ig(e,t){if([...t.position,...t.target,...t.up,t.fov,t.zoom,t.near,t.far].some(r=>!Number.isFinite(r))||t.fov<=0||t.fov>=180||t.zoom<=0||t.near<=0||t.far<=t.near)throw new Error("The saved camera is invalid");Kl(e.controls),e.camera.position.fromArray(t.position),e.camera.up.fromArray(t.up).normalize(),e.camera.fov=t.fov,e.camera.zoom=t.zoom,e.camera.near=t.near,e.camera.far=t.far,e.controls.target.fromArray(t.target),e.camera.updateProjectionMatrix(),e.controls.update(),e.cameraMode="manual"}function Kl(e){const t=e.enableDamping;e.enableDamping=!1;try{e.update()}finally{e.enableDamping=t}}function ag(e,t){const n=Hf(e.images);return[t.mode,t.wrap,t.cellOrigin.join(","),t.mirror.join(","),t.cell,e.visibleAtoms.length,n.count,n.span.join(",")].join(":")}function cg(e,t,n,r){Gc(t,n,r).forEach(o=>e.expandByPoint(o))}function lg(e){const t=[];for(const n of[e.min.x,e.max.x])for(const r of[e.min.y,e.max.y])for(const o of[e.min.z,e.max.z])t.push(new F(n,r,o));return t}function ug(e){return e==="xy"?{direction:new F(0,0,1),up:new F(0,1,0)}:e==="xz"?{direction:new F(0,1,0),up:new F(0,0,1)}:e==="yz"?{direction:new F(1,0,0),up:new F(0,0,1)}:{direction:new F(1,.68,1.15).normalize(),up:new F(0,1,0)}}function To(e){const t=new Set,n=new Set;e.traverse(r=>{const o=r;r instanceof Ce&&r.dispose(),o.geometry&&!t.has(o.geometry)&&(t.add(o.geometry),o.geometry.dispose()),(Array.isArray(o.material)?o.material:o.material?[o.material]:[]).forEach(i=>{n.has(i)||(n.add(i),i.dispose())})})}const Wl={1:"#f0eee7",2:"#d8f2f2",3:"#b889df",4:"#bed17f",5:"#d4956d",6:"#94a3a7",7:"#5680dd",8:"#df6259",9:"#6cba79",10:"#7bcdd0",11:"#9874ce",12:"#89a86d",13:"#c7b8ae",14:"#d5aa82",15:"#ed9e54",16:"#ead462",17:"#74ca88",18:"#8bdce2",19:"#aa7bdd",20:"#99ba7b",26:"#cf8964",29:"#d19a71",30:"#adb3b7",35:"#b65a4c",53:"#8d61b5"},fg={...Wl,1:"#aab5b3",6:"#273a3f",7:"#315bb8",8:"#c94138",9:"#318448",15:"#d87924",16:"#c5a51c",17:"#348b4c"},Xl=1e4;function dg({open:e,frameCount:t,options:n,defaultReferenceId:r,initialView:o,onRun:s,onClose:i}){const a=co(n,r)??n[0],c=n.find($=>$.id!==a?.id)??a,[l,u]=w.useState(a?.id??""),[f,m]=w.useState(c?.id??""),[g,p]=w.useState("all"),[x,A]=w.useState("200"),[b,j]=w.useState(""),k=w.useRef(null);w.useEffect(()=>{if(!e)return;const $=co(n,r)??n[0],L=n.find(ee=>ee.id!==$?.id)??$;u($?.id??""),m(L?.id??""),p("all"),A("200"),j("")},[r,e,n]),w.useEffect(()=>{if(!e)return;const $=requestAnimationFrame(()=>k.current?.focus());return()=>cancelAnimationFrame($)},[e]);const v=Math.max(1,Math.ceil(t/Xl)),S=Math.ceil(t/v),I=w.useMemo(()=>[{value:"all",label:v===1?`All · ${t.toLocaleString()}`:`All · ${S.toLocaleString()} sampled`},...t>100?[{value:"last-100",label:"Last 100"}]:[],...t>1e3?[{value:"last-1000",label:"Last 1,000"}]:[]],[v,t,S]);if(!e)return null;const E=co(n,l),_=co(n,f),U=Number(x),G=b.trim()?Number(b):void 0,B=!!(E&&_&&Number.isSafeInteger(U)&&U>=20&&U<=2e3&&(G===void 0||Number.isFinite(G)&&G>0)),V=()=>{!B||!E||!_||s(mg({reference:E,target:_,frames:g,frameCount:t,bins:U,rMax:G,initialView:o}))};return d.jsxs("section",{className:"rdf-sheet",role:"dialog","aria-labelledby":"rdf-sheet-title",onKeyDown:$=>{$.key==="Escape"&&($.preventDefault(),i())},children:[d.jsxs("header",{children:[d.jsx("strong",{id:"rdf-sheet-title",children:"Pair analysis"}),d.jsx("button",{type:"button",onClick:i,"aria-label":"Close",children:"×"})]}),d.jsxs("div",{className:"rdf-sheet__body",children:[d.jsxs("label",{children:[d.jsx("span",{children:"From"}),d.jsx("select",{ref:k,value:l,onChange:$=>u($.target.value),children:n.map($=>d.jsxs("option",{value:$.id,children:[$.label," · ",$.atomIndices.length.toLocaleString()]},$.id))})]}),d.jsxs("label",{children:[d.jsx("span",{children:"To"}),d.jsx("select",{value:f,onChange:$=>m($.target.value),children:n.map($=>d.jsxs("option",{value:$.id,children:[$.label," · ",$.atomIndices.length.toLocaleString()]},$.id))})]}),d.jsxs("label",{children:[d.jsx("span",{children:"Frames"}),d.jsx("select",{value:g,onChange:$=>p($.target.value),children:I.map($=>d.jsx("option",{value:$.value,children:$.label},$.value))})]}),d.jsxs("details",{children:[d.jsx("summary",{children:"Advanced"}),d.jsxs("div",{children:[d.jsxs("label",{children:[d.jsx("span",{children:"Bins"}),d.jsx("input",{inputMode:"numeric",value:x,onChange:$=>A($.target.value)})]}),d.jsxs("label",{children:[d.jsx("span",{children:"r max · Å"}),d.jsx("input",{inputMode:"decimal",value:b,placeholder:"Automatic",onChange:$=>j($.target.value)})]})]})]})]}),d.jsxs("footer",{children:[d.jsx("span",{children:"PQAnalysis · full periodic cells"}),d.jsx("button",{type:"button",disabled:!B,onClick:V,children:"Run"})]})]})}function mg({reference:e,target:t,frames:n,frameCount:r,bins:o,rMax:s,initialView:i}){const a=n==="last-100"?100:n==="last-1000"?1e3:r,c=n==="all"?Math.max(1,Math.ceil(r/Xl)):1;return{reference:e,target:t,frameStart:Math.max(0,r-a),frameStop:r,frameStep:c,bins:o,rMax:s,initialView:i}}function co(e,t){return t?e.find(n=>n.id===t):void 0}function hg(e){return pg(e)?{commands:"⌘K",open:"⌘O",export:"⌘⇧S"}:{commands:"Ctrl K",open:"Ctrl O",export:"Ctrl Shift S"}}function pg(e){return/mac|iphone|ipad|ipod/i.test(e)}function gg(e){return e==="true"}function bg(e,t,n){return n<=0?0:Math.max(0,Math.min(n-1,e+t))}function yg(e,t){return e==="g"?t==="g"?{action:"first-frame",prefix:null}:{action:null,prefix:"g"}:e==="G"?{action:"last-frame",prefix:null}:e==="l"?{action:"next-frame",prefix:null}:e==="L"?{action:"next-ten-frames",prefix:null}:e==="h"?{action:"previous-frame",prefix:null}:e==="H"?{action:"previous-ten-frames",prefix:null}:e===":"?{action:"commands",prefix:null}:{action:null,prefix:null}}const Dt=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No","Lr","Rf","Db","Sg","Bh","Hs","Mt","Ds","Rg","Cn","Nh","Fl","Mc","Lv","Ts","Og"],xg="unknown hydrogen helium lithium beryllium boron carbon nitrogen oxygen fluorine neon sodium magnesium aluminium silicon phosphorus sulfur chlorine argon potassium calcium scandium titanium vanadium chromium manganese iron cobalt nickel copper zinc gallium germanium arsenic selenium bromine krypton rubidium strontium yttrium zirconium niobium molybdenum technetium ruthenium rhodium palladium silver cadmium indium tin antimony tellurium iodine xenon caesium barium lanthanum cerium praseodymium neodymium promethium ",wg="samarium europium gadolinium terbium dysprosium holmium erbium thulium ytterbium lutetium hafnium tantalum tungsten rhenium osmium iridium platinum gold mercury thallium lead bismuth polonium astatine radon francium radium actinium thorium protactinium uranium ",Ag="neptunium plutonium americium curium berkelium californium einsteinium fermium ",Sg="mendelevium nobelium lawrencium rutherfordium dubnium seaborgium bohrium hassium ",Mg="meitnerium darmstadtium roentgenium copernicium nihonium flerovium moscovium livermorium tennessine oganesson",ai=(xg+wg+Ag+Sg+Mg).split(" "),Fn=new Map;for(let e=1;e0&&e[Bt(l),u])),s=Qn(t);if(n==="add"){for(const l of s){const u=Bt(l);o.has(u)||(o.set(u,r.length),r.push(l))}return r}const i=new Set(s.map(Bt)),a=r.filter(l=>!i.has(Bt(l))),c=new Set(r.map(Bt));for(const l of s)c.has(Bt(l))||a.push(l);return a}function jg(e,t){return{name:_g(e),selections:Qn(t)}}function Yl(e){const t=new Map;for(const o of e){const s=Dt[o]??"X";t.set(s,(t.get(s)??0)+1)}const n=[...t.keys()];return(t.has("C")?["C",...t.has("H")?["H"]:[],...n.filter(o=>o!=="C"&&o!=="H").sort()]:n.sort()).map(o=>{const s=t.get(o);return`${o}${s===1?"":s}`}).join("")}function Ng(e){const t=e.match(/^\s*select\s+within\s+((?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)\s*(?:a|å|angstroms?)\s+of\s+selection\s*$/i);if(!t)return null;const n=Number(t[1]);return Number.isFinite(n)&&n>0?n:null}function Ql(e){Zl(e);const t=Cg(e),n=new Int32Array(e.count),r=new Uint8Array(e.count);for(let i=0;i0,componentRoots:s,residueIndices:t}}class Fg{context;hasConnectivity;componentRoots;residueIndices;visibleInstances=null;constructor(t,n=Ql(t)){if(Zl(t),n.count!==t.count)throw new RangeError("Selection topology does not match the scene");this.context=t,this.hasConnectivity=n.hasConnectivity,this.componentRoots=n.componentRoots,this.residueIndices=n.residueIndices}selectionAt(t){if(!Number.isInteger(t)||t<0||t>=this.context.instanceToAtom.length)return null;const n=this.context.instanceToAtom[t];if(n>=this.context.count)return null;const r=t*3,o=n*3;return{atom:n,image:[this.context.baseImages[o]+this.context.instanceImages[r],this.context.baseImages[o+1]+this.context.instanceImages[r+1],this.context.baseImages[o+2]+this.context.instanceImages[r+2]]}}displayedPosition(t){if(!Vn(t,this.context.count))return null;const n=new Float64Array(3);return Is(n,this.context,t)?[n[0],n[1],n[2]]:null}isVisible(t){return this.instanceFor(t)!==null}selectScope(t,n){if(!Vn(t,this.context.count))return[];const r=this.displayImage(t);if(!r||this.instanceFor(t)===null)return[];if(n==="atom")return[Bi(t)];const o=t.atom;let s;if(n==="element"){const i=this.context.atomicNumbers[o];s=a=>this.context.atomicNumbers[a]===i}else if(n==="residue"){const i=this.residueIndices[o];if(i<0)return null;s=a=>this.residueIndices[a]===i}else if(n==="component"){if(!this.hasConnectivity)return null;const i=this.componentRoots[o];s=a=>this.componentRoots[a]===i}else if(n==="molecule"){const i=this.residueIndices[o];if(this.hasConnectivity){const a=this.componentRoots[o];s=c=>this.componentRoots[c]===a}else{if(i<0)return null;s=a=>this.residueIndices[a]===i}}else throw new TypeError(`Unknown scientific selection scope: ${String(n)}`);return this.collectVisible((i,a)=>s(i)&&this.instanceHasDisplayImage(a,r))}selectElement(t){const n=kg(t);return n===null?[]:this.collectVisible(r=>this.context.atomicNumbers[r]===n)}selectWater(){return this.collectVisible(t=>this.context.waterAtoms.has(t))}withinDistance(t,n){return this.withinDistanceOf([t],n)}withinDistanceOf(t,n){if(!Number.isFinite(n)||n<=0)return[];const r=new Float64Array(3),o=new Map,s=new Set;for(const c of t){if(!Vn(c,this.context.count))continue;const l=Bt(c);if(s.has(l)||(s.add(l),!Is(r,this.context,c)))continue;const u=cc(Math.floor(r[0]/n),Math.floor(r[1]/n),Math.floor(r[2]/n)),f=o.get(u);f?f.push(r[0],r[1],r[2]):o.set(u,[r[0],r[1],r[2]])}if(o.size===0)return[];const i=n*n,a=new Float64Array(3);return this.collectVisible((c,l)=>{const u=l*3;if(!Jl(a,0,this.context,c,this.context.instanceImages[u],this.context.instanceImages[u+1],this.context.instanceImages[u+2]))return!1;const f=Math.floor(a[0]/n),m=Math.floor(a[1]/n),g=Math.floor(a[2]/n);for(let p=f-1;p<=f+1;p+=1)for(let x=m-1;x<=m+1;x+=1)for(let A=g-1;A<=g+1;A+=1){const b=o.get(cc(p,x,A));if(b)for(let j=0;j=this.context.count||!t(s,o))continue;const i=this.selectionAt(o);if(!i)continue;const a=Bt(i);r.has(a)||(r.add(a),n.push(i))}return n}displayImage(t){if(!Vn(t,this.context.count))return null;const n=t.atom*3;return[t.image[0]-this.context.baseImages[n],t.image[1]-this.context.baseImages[n+1],t.image[2]-this.context.baseImages[n+2]]}instanceFor(t){const n=this.displayImage(t);return n?this.ensureVisibleInstances().get(ac(t.atom,n[0],n[1],n[2]))??null:null}instanceHasDisplayImage(t,n){const r=t*3;return this.context.instanceImages[r]===n[0]&&this.context.instanceImages[r+1]===n[1]&&this.context.instanceImages[r+2]===n[2]}ensureVisibleInstances(){if(this.visibleInstances)return this.visibleInstances;const t=new Map;for(let n=0;n=this.context.count)continue;const o=n*3,s=ac(r,this.context.instanceImages[o],this.context.instanceImages[o+1],this.context.instanceImages[o+2]);t.has(s)||t.set(s,n)}return this.visibleInstances=t,t}}function Zl(e){if(!Number.isInteger(e.count)||e.count<0)throw new RangeError("Selection context count must be a non-negative integer");if(e.atomicNumbers.length=0&&r<=2147483647&&(t[n]=r)}return t}function Is(e,t,n){const r=n.atom*3;return Jl(e,0,t,n.atom,n.image[0]-t.baseImages[r],n.image[1]-t.baseImages[r+1],n.image[2]-t.baseImages[r+2])}function Jl(e,t,n,r,o,s,i){const a=r*3;let c=n.positions[a],l=n.positions[a+1],u=n.positions[a+2];if(![c,l,u].every(Number.isFinite))return!1;if(o!==0||s!==0||i!==0){const f=n.cell;if(!f)return!1;c+=o*f[0]+s*f[3]+i*f[6],l+=o*f[1]+s*f[4]+i*f[7],u+=o*f[2]+s*f[5]+i*f[8]}return[c,l,u].every(Number.isFinite)?(e[t]=c,e[t+1]=l,e[t+2]=u,!0):!1}function Ig(e,t,n){return Number.isInteger(e)&&Number.isInteger(t)&&e>=0&&t>=0&&e=0&&e.atom80)throw new RangeError("Named selection name is too long");return t}const li=12,lc=1,uc=60;function Rg(e,t=li){const n=Number.isFinite(t)&&t>0?Math.min(uc,Math.max(lc,t)):li;return!Number.isFinite(e)||e<=0?n:Math.min(uc,Math.max(lc,e))}function Tg(e){return 1e3/Rg(e)}function eu(e,t,n){const r=Number.isFinite(e)?Math.max(0,e):0,o=Tg(n),s=t!==null&&Number.isFinite(t)&&t>=0&&t<=r?t:null;if(s===null)return{delayMs:o,requestTimeMs:r+o,stepCount:1};const i=Math.floor((Math.max(0,r-s)+o*1e-9)/o),a=Math.max(1,i),c=s+a*o;return{delayMs:Math.max(0,c-r),requestTimeMs:c,stepCount:a}}function fc(e){return Math.ceil(Math.max(0,Number.isFinite(e)?e:0))}function ui(e,t=1){const n=Number.isFinite(t)&&t>0?Math.max(1,Math.round(t)):1;return!Number.isFinite(e)||e<=0?n:Math.max(1,Math.round(e))}function tu(e,t,n={}){const r=nu(t),o=n.direction===-1?-1:1;if(r<2)return{frameIndex:0,direction:o,continuePlaying:!1};const s=r-1,i=fi(e,s),a=ui(n.stride??1),c=n.mode??"loop";if(c==="once"){const l=i+o*a,u=l<=0||l>=s;return{frameIndex:fi(l,s),direction:o,continuePlaying:!u}}return c==="rock"?Bg(i,s,o,a):{frameIndex:ru(i+o*a,r),direction:o,continuePlaying:!0}}function Pg(e,t,n,r){return tu(e,t,{...n,stride:ui(n.stride??1)*ui(r.stepCount)})}function Og(e,t,n,r,o,s,i){const a=eu(e,t,n);if(a.delayMs>0)return{committed:!1,schedule:a,step:null};const c=Pg(r,o,s,a);return i.onStep(c),i.onPulse(),{committed:!0,schedule:a,step:c}}function Lg(e,t,n,r=4){const o=nu(t),s=Number.isFinite(r)?Math.max(0,Math.floor(r)):0;if(o<2||s===0)return[];let i=fi(e,o-1),a=n.direction===-1?-1:1;const c=new Set([i]),l=[];for(let u=0;umi(o,t.mark.key)),r=n>=0?e.bookmarks.filter((o,s)=>s!==n):[...e.bookmarks,dc(t.mark)].sort((o,s)=>o.index-s.index).slice(-12);return{...e,bookmarks:Object.freeze(r)}}case"set-reference":return{...e,reference:dc(t.mark)};case"clear-reference":return{...e,reference:null,tracking:e.tracking==="displacement"?"off":e.tracking};case"set-tracking":return{...e,tracking:t.mode==="displacement"&&e.reference===null?"off":t.mode};case"open-plot":return{...e,plot:mc(t.plot)};case"update-plot":return e.plot?.requestId===t.plot.requestId?{...e,plot:mc(t.plot)}:e;case"close-plot":return{...e,plot:null}}}function zg(e,t){if(!Number.isSafeInteger(e)||e<0)return null;const n=t?.header.frame_key;return Vg(n)?{index:e,key:Di(n),step:hc(t,"step"),time:hc(t,"time"),timeUnit:Ug(t?.header.scalar_units?.time)}:null}function mi(e,t){return!!(e&&t&&e.source_id===t.source_id&&e.source_index===t.source_index&&e.segment_index===t.segment_index&&uo(e.step)===uo(t.step)&&uo(e.time)===uo(t.time)&&(e.time_unit??null)===(t.time_unit??null))}function An(e){const t=[e.step===null?"":`step ${pc(e.step)}`,e.time===null?"":`t ${pc(e.time)}${e.timeUnit?` ${e.timeUnit}`:""}`].filter(Boolean);return[`Frame ${e.index+1}`,...t].join(" · ")}function dc(e){return Object.freeze({...e,key:Object.freeze(Di(e.key))})}function Di(e){return{source_id:e.source_id,source_index:e.source_index,segment_index:e.segment_index,step:e.step??null,time:e.time??null,time_unit:e.time_unit??null}}function mc(e){return Object.freeze({...e,xValues:Object.freeze([...e.xValues]),frameIndices:e.frameIndices?Object.freeze([...e.frameIndices]):void 0,frameKeys:e.frameKeys?Object.freeze(e.frameKeys.map(t=>t?Object.freeze(Di(t)):null)):void 0,lines:Object.freeze(e.lines.map(t=>Object.freeze({...t,values:Object.freeze([...t.values]),selection:t.selection?Object.freeze(t.selection.map(({atom:n,image:r})=>Object.freeze({atom:n,image:Object.freeze([...r])}))):void 0})))})}function Vg(e){return!!(e&&typeof e.source_id=="string"&&e.source_id.length>0&&Number.isSafeInteger(e.source_index)&&e.source_index>=0&&Number.isSafeInteger(e.segment_index)&&e.segment_index>=0)}function hc(e,t){const n=e?.header[t];if(typeof n=="number"&&Number.isFinite(n))return n;const r=e?.header.scalars?.[t];return typeof r=="number"&&Number.isFinite(r)?r:null}function Ug(e){return e?.trim()||null}function uo(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function pc(e){return new Intl.NumberFormat("en",{maximumFractionDigits:5}).format(e)}const gc=50,Fr=16;function qg(e,t,n,r){if(e==="off"||!Number.isSafeInteger(t)||t<0||t>=r)return[];const o=e==="trail"?Array.from({length:Math.min(gc+1,t+1)},(s,i)=>Math.max(0,t-gc)+i):[t];return e==="displacement"&&n!==null&&Number.isSafeInteger(n)&&n>=0&&ns-i)}function Gg(e,t,n,r,o){const s=e.frames.find(({index:i})=>i===t);if(n&&(!s||!mi(s.key,n)))return"current";if(r!==null&&o){const i=e.frames.find(({index:a})=>a===r);if(!i||!mi(i.key,o))return"reference"}return null}function Hg(e,t,n,r,o){if(t.length>Fr)throw new RangeError(`Track up to ${Fr} selected atoms`);const s=new Map(e.atomIndices.map((u,f)=>[u,f])),i=new Map(e.frames.map(u=>[u.index,u])),a=i.get(r);if(!a)return{trails:[],displacements:[]};const c=[];if(n==="trail"){const u=e.frames.filter(({index:f})=>f<=r).sort((f,m)=>f.index-m.index).slice(-51);for(const f of t){const m=s.get(f.atom);if(m===void 0||u.length<2)continue;const g=new Float32Array(u.length*3);u.forEach(({positions:p},x)=>{g.set(p.subarray(m*3,m*3+3),x*3)}),c.push({id:bc(f),atom:f.atom,image:[...f.image],points:g})}}const l=[];if(n==="displacement"&&o!==null){const u=i.get(o);if(u)for(const f of t){const m=s.get(f.atom);if(m===void 0)continue;const g=m*3;l.push({id:bc(f),atom:f.atom,image:[...f.image],from:[u.positions[g],u.positions[g+1],u.positions[g+2]],to:[a.positions[g],a.positions[g+1],a.positions[g+2]]})}}return{trails:c,displacements:l}}function bc(e){return`${e.atom}:${e.image.join(":")}`}const Kg="pqviewer-dataset",Wg=1048576,fo=4096,yc={format:"png",width:2400,height:1800,dpi:300,background:{kind:"solid",color:"#ffffff"},projection:"orthographic",fit:!0,padding:.08,periodicContext:!0},tt={mode:"ball-stick",water:"show",hydrogens:!0,wrap:"molecule",images:{min:[0,0,0],max:[0,0,0]},cellOrigin:[0,0,0],mirror:[!1,!1,!1],cell:!0,forces:!0,velocities:!1,atomScale:1,bondScale:1,color:"element",quality:"auto"};function Xg(){const[e,t]=w.useState(null),[n,r]=w.useState("loading"),[o,s]=w.useState(""),[i,a]=w.useState(0),[c,l]=w.useState(0),[u,f]=w.useState(null),[m,g]=w.useState(""),[p,x]=w.useState(!1),[A,b]=w.useState(!1),[j,k]=w.useState(li),[v,S]=w.useState(1),[I,E]=w.useState("loop"),[_,U]=w.useState(1),[G,B]=w.useState(0),[V,$]=w.useState(!1),[L,ee]=w.useState(Ob),[te,oe]=w.useState("auto"),[C,N]=w.useState([]),[P,R]=w.useState("measurement"),[Y,H]=w.useState(null),[X,ne]=w.useState(null),[re,W]=w.useState([]),[J,me]=w.useState([]),[q,de]=w.useState(!0),[se,xe]=w.useState(!1),[ue,pe]=w.useState(null),[K,ie]=w.useReducer(Dg,di),[be,lt]=w.useState({trails:[],displacements:[]}),[mn,pt]=w.useState(!1),[Cn,tr]=w.useState("rdf"),[Ge,Ie]=w.useState(null),[Le,Wt]=w.useState("rdf"),[nr,Qe]=w.useState(!1),[In,rt]=w.useState(null),[fe,Re]=w.useState(null),[gt,Ne]=w.useState(!1),[Xt,Fe]=w.useState(!1),[ot,rr]=w.useState(Tb),[Ur,D]=w.useState(0),[ge,ke]=w.useState("perspective"),[Be,Yt]=w.useState(0),[Q,qi]=w.useState(!1),[En,He]=w.useState(!1),[$t,qr]=w.useState(yc),[_t,Rt]=w.useState([]),[Gr,Qt]=w.useState(!1),[or,bt]=w.useState(""),[sr,Gi]=w.useState(1),[ir,Hi]=w.useState(1),[uu,fu]=w.useState([]),[du,Uo]=w.useState(!1),[Hr,Ki]=w.useState(!1),[yt,Z]=w.useState(null),[mu,qo]=w.useState(null),Zt=w.useRef(new ys),Kr=w.useRef(null),Wi=w.useRef(null),Xi=w.useRef(null),Yi=w.useRef(null),Qi=w.useRef(null),Zi=w.useRef(null),Wr=w.useRef({prefix:null,at:0}),ar=w.useRef(0),Xr=w.useRef(""),cr=w.useRef(0),Yr=w.useRef(null),lr=w.useRef(0),De=w.useRef(0),ze=w.useRef(null),xt=w.useRef(null),Ve=w.useRef(null),$n=w.useRef(0),wt=w.useRef(null),Ji=w.useRef(1),Go=w.useRef({selections:null,key:""}),Qr=w.useRef(!1),Ho=w.useRef(!1),_n=w.useRef(""),Ko=w.useRef(null),ur=w.useRef(null),Rn=w.useRef({key:"",requestTimeMs:null}),Jt=L.wrap==="unwrapped"?"unwrapped":"source",Zr=w.useRef("source"),ea=w.useRef({playing:A,mode:I,direction:_,stride:v});ea.current={playing:A,mode:I,direction:_,stride:v};const Tn=w.useMemo(()=>hg(Pb()),[]),Pn=w.useCallback((h,y=!1)=>{$n.current+=1,De.current+=1,ze.current?.abort(),ze.current=null,xt.current?.abort(),xt.current=null,Ve.current?.abort(),Ve.current=null,Zt.current.clear(),Zt.current=new ys({datasetGeneration:h.dataset_generation}),Zr.current="source",Qr.current=!1,_n.current=h.dataset_generation??"",Ko.current=h,t(h),l(0),f(null),N([]),R("measurement"),H(null),ne(null),W([]),me([]),de(!0),xe(!1),pe(null),ie({type:"reset",preserveMarks:y}),lt({trails:[],displacements:[]}),pt(!1),Ie(null),Qe(!1),rt(null),b(!1),U(1),$(!1),Re(null),Ne(!1),Fe(!1),He(!1),qr(Ar(yc)),Rt([]),Qt(!1),bt(""),wt.current=null,qo(null),ee(M=>({...M,...Sc()})),r("ready"),s(""),oe("auto"),Xr.current="",document.title=`${h.name||"Trajectory"} · PQViewer`},[]),Jr=w.useCallback((h,y)=>{const M=Zs(h);if(!um(M,y))throw new Error("This figure recipe belongs to a different source");if(M.frame.index>=y.frame_count)throw new Error("The saved frame is outside this trajectory");if(M.scene.selection.atoms.some(({atom:z})=>z>=y.topology.atom_count))throw new Error("The saved selection is outside this structure");if(M.annotations.some(z=>z.kind==="atom-label"&&z.atom.atom>=y.topology.atom_count))throw new Error("The saved atom labels are outside this structure");const O=$n.current+1;$n.current=O,wt.current=null,Qt(!0),bt(""),po(M.frame.index,void 0,y.dataset_generation,M.scene.presentation.wrap==="unwrapped"?"unwrapped":"source").then(z=>{if($n.current===O){if(!xr(z.header.frame_key,M.frame.key))throw new Error("The saved frame no longer matches this trajectory");if(Ns(y,z)!==M.frame.fingerprint)throw new Error("The saved frame content changed");b(!1),$(!1),xe(!1),pe(null),De.current+=1,ze.current?.abort(),ze.current=null,xt.current?.abort(),xt.current=null,Ve.current?.abort(),Ve.current=null,ie({type:"reset"}),lt({trails:[],displacements:[]}),pt(!1),Ie(null),Qe(!1),rt(null),Re(null),Ne(!1),Fe(!1),He(!1),oe("custom"),Xr.current=`${y.name}:${y.topology.atom_count}`,ee(M.scene.presentation),N(lo(M.scene.selection.atoms)),R(M.scene.selection.intent),de(M.scene.selection.minimumImage),Gi(M.scene.vectors.forceScale),Hi(M.scene.vectors.velocityScale),qr(Ar(M.output)),Rt(Zs(M).annotations),g(""),l(M.frame.index),wt.current=M}}).catch(z=>{if($n.current!==O)return;const ce=We(z);Qt(!1),bt(ce),Z({message:`Figure recipe unavailable · ${ce}`,tone:"error"})})},[]),Ze=w.useCallback(()=>{Qr.current||($n.current+=1,De.current+=1,ze.current?.abort(),ze.current=null,xt.current?.abort(),xt.current=null,Ve.current?.abort(),Ve.current=null,Qr.current=!0,_n.current="",Ko.current=null,Zt.current.clear(),Zr.current="source",t(null),l(0),f(null),g(""),x(!1),N([]),R("measurement"),H(null),ne(null),W([]),me([]),xe(!1),pe(null),ie({type:"reset"}),lt({trails:[],displacements:[]}),pt(!1),Ie(null),Qe(!1),rt(null),b(!1),U(1),$(!1),Re(null),Ne(!1),Fe(!1),He(!1),Rt([]),Qt(!1),bt(""),wt.current=null,qo(null),r("loading"),s(""),Z({message:"Trajectory changed in another tab · reloading",tone:"status"}),a(h=>h+1))},[]);w.useEffect(()=>{if(typeof window.BroadcastChannel!="function")return;const h=new BroadcastChannel(Kg);return ur.current=h,h.onmessage=y=>{const M=typeof y.data=="object"&&y.data!==null&&"datasetGeneration"in y.data&&typeof y.data.datasetGeneration=="string"?y.data.datasetGeneration:"";M&&_n.current&&M!==_n.current&&Ze()},()=>{ur.current===h&&(ur.current=null),h.close()}},[Ze]);const Wo=w.useCallback(async()=>{const h=_n.current;if(!(!h||Ho.current||Qr.current)){Ho.current=!0;try{const y=await ha();if(_n.current!==h)return;y.dataset_generation!==h&&(Pn(y,jb(Ko.current,y)),Z({message:"Trajectory changed · updated",tone:"status"}),ur.current?.postMessage({datasetGeneration:y.dataset_generation}))}catch{}finally{Ho.current=!1}}},[Pn]);w.useEffect(()=>{const h=()=>{Wo()},y=()=>{document.visibilityState==="visible"&&Wo()};return window.addEventListener("focus",h),document.addEventListener("visibilitychange",y),()=>{window.removeEventListener("focus",h),document.removeEventListener("visibilitychange",y)}},[Wo]),w.useEffect(()=>{document.documentElement.dataset.appearance="light",document.documentElement.style.colorScheme="light",document.querySelector('meta[name="theme-color"]')?.setAttribute("content","#f6f8f8");try{window.localStorage.setItem("pqviewer-presentation",JSON.stringify({mode:L.mode,water:L.water,cell:L.cell,forces:L.forces,velocities:L.velocities}))}catch{}},[L]),w.useEffect(()=>{try{window.localStorage.setItem("pqviewer-vim-navigation",String(ot))}catch{}},[ot]),w.useEffect(()=>{let h=!0;return r("loading"),s(""),Promise.all([ha(),Xu()]).then(([y,M])=>{if(h&&(Pn(y),M!==null))try{Jr(Io(M),y)}catch(O){const z=We(O);bt(z),Z({message:`Figure recipe unavailable · ${z}`,tone:"error"})}}).catch(y=>{h&&(s(We(y)),r("error"))}),()=>{h=!1}},[Pn,i,Jr]),w.useEffect(()=>{!e||Zr.current===Jt||(Zt.current.clear(),Zt.current=new ys({datasetGeneration:e.dataset_generation,coordinates:Jt}),Zr.current=Jt,g(""))},[Jt,e]),w.useEffect(()=>{if(!e||e.frame_count===0||Q)return;let h=!0;return Zt.current.cancelPendingExcept(c),x(!0),g(""),Zt.current.get(c).then(y=>{if(!h)return;f({index:c,data:y}),x(!1);const M=ea.current;(M.playing?Lg(c,e.frame_count,{mode:M.mode,direction:M.direction,stride:M.stride}):Array.from({length:Math.min(4,e.frame_count-1)},(z,ce)=>(c+ce+1)%e.frame_count)).forEach(z=>Zt.current.prefetch(z,e.frame_count))}).catch(y=>{if(h){if(y instanceof it){Ze();return}if(Jt==="unwrapped"){ee(M=>M.wrap==="unwrapped"?{...M,wrap:"atom"}:M),Z({message:"Unwrapped coordinates unavailable · showing atoms",tone:"error"}),x(!1),b(!1);return}g(We(y)),x(!1),b(!1)}}),()=>{h=!1}},[Jt,c,e,Ze,Q]),w.useEffect(()=>{const h=wt.current;if(!h||!e||!u||u.index!==h.frame.index||p)return;if(!xr(u.data.header.frame_key,h.frame.key)){wt.current=null,Qt(!1),bt("The saved frame no longer matches this trajectory"),Z({message:"Figure recipe unavailable · saved frame changed",tone:"error"});return}if(Ns(e,u.data)!==h.frame.fingerprint){wt.current=null,Qt(!1),bt("The saved frame content changed"),Z({message:"Figure recipe unavailable · saved frame changed",tone:"error"});return}let y=!1,M=0,O=0;return M=requestAnimationFrame(()=>{O=requestAnimationFrame(()=>{if(!(y||wt.current!==h))try{const z=Kr.current;if(!z)throw new Error("The molecular scene is not ready");z.restoreCamera(h.camera),wt.current=null,Qt(!1),bt(""),kb()||Z({message:"Figure recipe restored",tone:"status"})}catch(z){wt.current=null,Qt(!1);const ce=We(z);bt(ce),Z({message:`Figure recipe unavailable · ${ce}`,tone:"error"})}})}),()=>{y=!0,cancelAnimationFrame(M),cancelAnimationFrame(O)}},[p,u,e,L,sr,ir]);const Ke=w.useCallback(h=>{e?.frame_count&&(U(1),l(Math.max(0,Math.min(e.frame_count-1,Math.round(h)))))},[e?.frame_count]),en=w.useCallback(h=>{e?.frame_count&&(U(1),l(y=>bg(y,h,e.frame_count)))},[e?.frame_count]),ta=w.useCallback(()=>{requestAnimationFrame(()=>Zi.current?.focus())},[]),tn=w.useCallback((h,y=!1)=>{$(!1),He(!1),xe(!1),pe(null),Re(h),y&&ta()},[ta]),At=w.useCallback((h=!1)=>{const y=fe;Re(null),h&&requestAnimationFrame(()=>{y==="inspect"?document.querySelector(".molecule-canvas")?.focus():y==="summary"?document.querySelector(".selection-summary-button")?.focus():Yi.current?.focus()})},[fe]),hu=w.useCallback((h,y=!1)=>{if($(!1),R("measurement"),h===null){N([]),H(null),xe(!1),pe(null),Re(M=>M==="inspect"||M==="summary"?null:M);return}H({atom:h.atom,image:[...h.image]}),N(M=>Md(M,h,y?"toggle":"replace"))},[]),pu=w.useCallback((h,y=!1)=>{if(h.length===0)return;$(!1),R("set"),xe(!1),pe(null),N(O=>y?vg(O,h,"toggle"):Qn(h));const M=h.at(-1);M&&H({atom:M.atom,image:[...M.image]})},[]);w.useEffect(()=>{if(C.length===0)H(null),Re(h=>h==="inspect"||h==="summary"?null:h);else if(!Y||!C.some(h=>iu(h,Y))){const h=C.at(-1);H({atom:h.atom,image:[...h.image]})}(C.length<2||C.length>4)&&(xe(!1),pe(null)),C.length<=4&&Re(h=>h==="summary"?null:h)},[C,Y]);const fr=w.useCallback(h=>{ke(h),Yt(y=>y+1)},[]),On=w.useCallback(()=>{Q||(Ne(!1),Fe(!1),He(!1),$(!1),Re(null),Wi.current?.click())},[Q]),Ln=w.useCallback(()=>{Q||(b(!1),Fe(!1),He(!1),$(!1),Ne(!0))},[Q]),eo=w.useCallback(()=>{Q||(Ne(!1),He(!1),$(!1),Fe(!0))},[Q]),Bn=w.useCallback(async(h,y=!1)=>{const M=Kr.current;if(!M||Q)return null;if(p)return Z({message:"Wait for the current frame to finish loading.",tone:"status"}),null;const O=h.format??"png",z=document.activeElement,ce=z&&z!==document.body&&"focus"in z?z:null;b(!1),qi(!0),Z({message:`Exporting ${O==="tiff"?"TIFF":"PNG"}…`,tone:"status"});try{const ve=await M.exportFigure(h);return rn(ve,Sb(e?.name,h.width,h.height,O)),Z({message:`Exported ${h.width.toLocaleString()} × ${h.height.toLocaleString()} px`,tone:"status"}),ve}catch(ve){if(Z({message:`Export failed · ${We(ve)}`,tone:"error"}),y)throw ve;return null}finally{qi(!1),requestAnimationFrame(()=>ou(ce?.isConnected?ce:Qi.current))}},[p,e?.name,Q]),Ee=u?.data??null,Tt=u?.index??c,ye=w.useMemo(()=>zg(Tt,Ee),[Tt,Ee]);w.useEffect(()=>{if(!ye)return;const h=K.bookmarks.find(({index:y})=>y===ye.index);h&&!xr(h.key,ye.key)&&(ie({type:"toggle-bookmark",mark:h}),Z({message:"Removed a stale bookmark",tone:"status"})),K.reference?.index===ye.index&&!xr(K.reference.key,ye.key)&&(ie({type:"clear-reference"}),Z({message:"Reference frame changed · cleared",tone:"status"}))},[ye,K.bookmarks,K.reference]);const nn=C.at(-1)?.atom??null,Te=Lf(Ee),na=le(Ee,["forces","force"]),hn=!!(na&&na.length>=(e?.topology.atom_count??0)*3),ra=le(Ee,["velocities","velocity","vel"]),Xo=!!(ra&&ra.length>=(e?.topology.atom_count??0)*3),$e=fb(Ee),to=Te&&(fe==="view"||gt),pn=w.useMemo(()=>to?wc(Ee):null,[Ee,to]),gn=w.useMemo(()=>to?wc(Ee,C):null,[Ee,to,C]),Yo=w.useMemo(()=>X?Ql(X):null,[e,X?.atomResidueIndex,X?.bonds,X?.count]),ae=w.useMemo(()=>X&&Yo?new Fg(X,Yo):null,[X,Yo]),oa=w.useMemo(()=>ae&&C.length<=4?au(ae,C):null,[C,ae]),Qo=w.useMemo(()=>X?pb(X,C):"",[C,X?.atomicNumbers]),gu=w.useMemo(()=>fe==="summary"?ae?.summarize(C)??null:null,[C,ae,fe]),no=w.useMemo(()=>{if(!X)return[];const h=new Set;for(let y=0;y0&&My-M)},[X?.atomicNumbers,X?.count]),Zo=[L.wrap,L.water,L.hydrogens,L.images.min.join(","),L.images.max.join(","),L.cellOrigin.join(",")].join(":");w.useEffect(()=>{if(!ae||C.length===0||Go.current.selections===C&&Go.current.key===Zo||(Go.current={selections:C,key:Zo},Y&&ae.isVisible(Y)))return;const h=[...C].reverse().find(y=>ae.isVisible(y));h&&H({atom:h.atom,image:[...h.image]})},[C,Y,ae,Zo]);const Pe=mu?.capabilities??null,Oe=(e?.frame_count??0)>1,bu=Ee?.header.coordinates==="unwrapped"?"unwrapped":"source",Ue=!!(Ee&&Pe&&!p&&!Gr&&bu===Jt),St=Oe&&P==="measurement"&&C.length>=2&&C.length<=4&&C.every(({atom:h})=>h>=0&&h<(e?.topology.atom_count??0)),Jo=w.useMemo(()=>rf(e?.series).filter(({name:h,values:y})=>y.length===(e?.frame_count??0)&&!["step","time"].includes(wo(h))),[e?.frame_count,e?.series]),es=w.useMemo(()=>{if(!e)return[];const h=[],y=Os(C.map(({atom:O})=>O),e.topology.atom_count);y.length>0&&y.length<=fo&&h.push({id:"selected",label:Qo||"Selection",atomIndices:y}),re.forEach((O,z)=>{const ce=Os(O.selections.map(({atom:ve})=>ve),e.topology.atom_count);ce.length>0&&ce.length<=fo&&h.push({id:`saved-${z}`,label:O.name,atomIndices:ce})});const M=e.topology.atomic_numbers??(X?.atomicNumbers?Array.from(X.atomicNumbers):[]);return no.forEach(O=>{const z=M.flatMap((ce,ve)=>ce===O?[ve]:[]);z.length>0&&z.length<=fo&&h.push({id:`element-${O}`,label:`All ${Dt[O]} atoms`,atomIndices:z})}),h.length===0&&e.topology.atom_count>0&&e.topology.atom_count<=fo&&h.push({id:"all",label:"All atoms",atomIndices:Array.from({length:e.topology.atom_count},(O,z)=>z)}),h},[e,re,no,X?.atomicNumbers,C,Qo]),ut=!!(Oe&&e?.source?.path&&$e.every(Boolean)&&es.length>0),Je=Oe&&C.length>0&&C.length<=Fr,ts=!!(ye&&K.bookmarks.some(({key:h})=>xr(h,ye.key))),Pt=w.useMemo(()=>Nb(J),[J]),st=!!(fe&&e&&Pe),ns=w.useMemo(()=>se&&e?Array.from({length:e.frame_count},(h,y)=>y):[],[e,se]),yu=w.useMemo(()=>ns.map(()=>null),[ns]),et=w.useCallback((h,y,M="set")=>{const O=Qn(h);N(O),R(M),M==="set"&&(xe(!1),pe(null));const z=O.at(-1);H(z?{atom:z.atom,image:[...z.image]}:null),y&&Z({message:y,tone:"status"})},[]),rs=w.useCallback(h=>{const y=Y??C.at(-1)??null;if(!ae||!y)return;const M=ae.selectScope(y,h);if(M===null){Z({message:h==="residue"?"Residue data is unavailable for this atom.":"Bond connectivity is unavailable for this structure.",tone:"status"});return}if(M.length===0){Z({message:"The anchor is not visible in the current view.",tone:"status"});return}et(M,`${mb(h)} · ${wr(M.length)}`)},[et,C,Y,ae]),os=w.useCallback(h=>{if(!ae||C.length===0||!Number.isFinite(h)||h<=0)return;const y=ae.withinDistanceOf(C,h);if(y.length===0){Z({message:"No visible atoms are within that distance.",tone:"status"});return}et(y,`Within ${Ye(h)} Å · ${wr(y.length)}`)},[et,C,ae]),sa=w.useCallback(h=>{if(!ae)return;const y=ae.selectElement(h);if(y.length===0){Z({message:`No visible ${ai[h]} atoms.`,tone:"status"});return}et(y,`${Dt[h]} · ${wr(y.length)}`)},[et,ae]),ia=w.useCallback(()=>{if(!ae)return;const h=ae.selectWater();if(h.length===0){Z({message:"No visible water molecules found.",tone:"status"});return}et(h,`Water · ${wr(h.length)}`)},[et,ae]),xu=w.useCallback(h=>{if(C.length===0)return!1;try{const y=jg(h,C);return W(M=>[...M.filter(O=>O.name.toLowerCase()!==y.name.toLowerCase()),y]),Z({message:`Saved selection · ${y.name}`,tone:"status"}),!0}catch(y){return Z({message:We(y),tone:"error"}),!1}},[C]),ss=w.useCallback(h=>{et(h.selections,`Selection · ${h.name}`)},[et]),wu=w.useCallback(h=>{W(y=>y.filter(M=>M.name!==h))},[]),is=w.useCallback(()=>{if(P!=="measurement"||C.length<2||C.length>4)return;const h=Ji.current;Ji.current+=1,me(y=>[...y,{id:h,selections:lo(C),minimumImage:q}].slice(-8)),Z({message:"Measurement pinned.",tone:"status"})},[q,C,P]),as=w.useCallback(h=>{de(h.minimumImage),et(h.selections,void 0,"measurement")},[et]),dr=w.useCallback((h=!1)=>{De.current+=1,ze.current?.abort(),ze.current=null,Ve.current?.abort(),Ve.current=null,Qe(!1),Ie(null),rt(null),ie({type:"close-plot"}),h&&requestAnimationFrame(()=>{document.querySelector(".timeline-options > summary")?.focus()})},[]),bn=w.useCallback((h=!1)=>{xe(!1),pe(null),h&&requestAnimationFrame(()=>{document.querySelector(".selection-plot-button")?.focus()})},[]),cs=w.useCallback(()=>{St&&(b(!1),Ne(!1),Fe(!1),He(!1),$(!1),Re(null),pe(null),dr(!1),(!Te||!$e.some(Boolean))&&de(!1),xe(!0))},[St,Te,dr,$e[0],$e[1],$e[2]]);w.useEffect(()=>{const h=lr.current+1;if(lr.current=h,!se||!e||!St)return;const y=new AbortController;return pe(null),Pd({manifest:e,frameCount:e.frame_count,selections:C,wrap:L.wrap,minimumImage:q,signal:y.signal,loadFrame:(M,O)=>po(M,O,e.dataset_generation),onProgress:M=>{lr.current===h&&!y.signal.aborted&&pe(M)}}).then(M=>{lr.current===h&&!y.signal.aborted&&pe(M)}).catch(M=>{if(!(lr.current!==h||y.signal.aborted)){if(M instanceof it){Ze();return}xe(!1),pe(null),Z({message:`Plot unavailable · ${We(M)}`,tone:"error"})}}),()=>y.abort()},[St,e,se,q,L.wrap,Ze,C]);const mr=w.useCallback(()=>{if(!ye){Z({message:"This frame has no stable source identity.",tone:"status"});return}ie({type:"toggle-bookmark",mark:ye})},[ye]),ls=w.useCallback(()=>{if(!ye){Z({message:"This frame has no stable source identity.",tone:"status"});return}ie({type:"set-reference",mark:ye}),Z({message:`Reference · ${An(ye)}`,tone:"status"})},[ye]),hr=w.useCallback(h=>{if(h!=="off"&&!Je){Z({message:C.length>Fr?`Track up to ${Fr} selected atoms.`:"Select atoms in a trajectory first.",tone:"status"});return}if(h==="displacement"&&K.reference===null){Z({message:"Set a reference frame first.",tone:"status"});return}ie({type:"set-tracking",mode:K.tracking===h?"off":h})},[C.length,K.reference,K.tracking,Je]);w.useEffect(()=>{if(xt.current?.abort(),xt.current=null,K.tracking==="off"||!e||!Je||C.length===0){lt({trails:[],displacements:[]}),K.tracking!=="off"&&!Je&&ie({type:"set-tracking",mode:"off"});return}const h=qg(K.tracking,Tt,K.reference?.index??null,e.frame_count);if(h.length===0){lt({trails:[],displacements:[]});return}const y=new AbortController;xt.current=y;const M=lo(C);return Yu({datasetGeneration:e.dataset_generation??"",atomIndices:Os(M.map(({atom:O})=>O),e.topology.atom_count),frameIndices:h,coordinates:"unwrapped"},y.signal).then(O=>{if(y.signal.aborted)return;const z=Gg(O,Tt,ye?.key??null,K.reference?.index??null,K.reference?.key??null);if(z==="reference"){ie({type:"clear-reference"}),lt({trails:[],displacements:[]}),Z({message:"Reference frame changed · cleared",tone:"status"});return}if(z==="current"){Ze();return}lt(Hg(O,M,K.tracking,Tt,K.reference?.index??null))}).catch(O=>{if(!y.signal.aborted){if(O instanceof it){Ze();return}ie({type:"set-tracking",mode:"off"}),lt({trails:[],displacements:[]}),Z({message:`Tracking unavailable · ${We(O)}`,tone:"error"})}}),()=>y.abort()},[Tt,ye,e,Ze,C,K.reference,K.tracking,Je]);const us=w.useCallback(h=>{if(!e||h.values.length!==e.frame_count)return;const y=De.current+1;De.current=y,ze.current?.abort(),ze.current=null,Ve.current?.abort(),Ve.current=null,Qe(!1),Ie(null),rt(null),b(!1),$(!1),xe(!1),pe(null),ie({type:"open-plot",plot:Cb(h,e.frame_count,y)})},[e]),fs=w.useCallback(()=>{if(!e||Pt.length<2)return;const h=De.current+1;De.current=h,ze.current?.abort();const y=new AbortController;ze.current=y,Ve.current?.abort(),Ve.current=null,Qe(!1),Ie(null),rt(null),b(!1),xe(!1),pe(null);const M=Pt.map(O=>({id:`pin-${O.id}`,label:kc(e,O.selections),selections:O.selections,minimumImage:O.minimumImage}));ie({type:"open-plot",plot:Fb(M,e.frame_count,h)}),Od({manifest:e,frameCount:e.frame_count,definitions:M,wrap:L.wrap,signal:y.signal,loadFrame:(O,z)=>po(O,z,e.dataset_generation),onProgress:O=>{De.current!==h||y.signal.aborted||ie({type:"update-plot",plot:Ra(O,h)})}}).then(O=>{De.current!==h||y.signal.aborted||ie({type:"update-plot",plot:Ra(O,h)})}).catch(O=>{if(!(De.current!==h||y.signal.aborted)){if(O instanceof it){Ze();return}ie({type:"close-plot"}),Z({message:`Comparison unavailable · ${We(O)}`,tone:"error"})}})},[Pt,e,L.wrap,Ze]),pr=w.useCallback(h=>{if(!ut){Z({message:"Pair analysis needs a trajectory with a full periodic cell.",tone:"status"});return}b(!1),Ne(!1),Fe(!1),He(!1),$(!1),Re(null),xe(!1),pe(null),tr(h),pt(!0)},[ut]),Au=w.useCallback(h=>{if(!e)return;const y=De.current+1;De.current=y,ze.current?.abort(),ze.current=null,Ve.current?.abort();const M=new AbortController;Ve.current=M;const O={requestId:y,referenceLabel:h.reference.label,targetLabel:h.target.label};pt(!1),tr(h.initialView),Wt(h.initialView),Ie(null),rt(O),Qe(!0),ie({type:"open-plot",plot:Ib(h.initialView,O)}),Qu({datasetGeneration:e.dataset_generation??"",referenceIndices:h.reference.atomIndices,targetIndices:h.target.atomIndices,frameStart:h.frameStart,frameStop:h.frameStop,frameStep:h.frameStep,bins:h.bins,rMax:h.rMax},M.signal).then(z=>{De.current!==y||M.signal.aborted||(Qe(!1),Ie(z),ie({type:"update-plot",plot:vc(z,h.initialView,O)}))}).catch(z=>{if(!(De.current!==y||M.signal.aborted)){if(Qe(!1),Ie(null),rt(null),ie({type:"close-plot"}),z instanceof it){Ze();return}Z({message:`Pair analysis unavailable · ${We(z)}`,tone:"error"})}})},[e,Ze]),aa=w.useCallback(h=>{Wt(h),!(!Ge||!In)&&ie({type:"update-plot",plot:vc(Ge,h,In)})},[In,Ge]);w.useEffect(()=>()=>{ze.current?.abort(),xt.current?.abort(),Ve.current?.abort()},[]);const gr=w.useCallback(()=>{!Ue||Q||(Ne(!1),Fe(!1),$(!1),Re(null),Bn({width:2400,height:1800,format:"png",dpi:300,background:{kind:"solid",color:"#ffffff"},periodicContext:Ac(L,$e)}))},[Ue,Bn,$e,L.mode,L.wrap,Q]),ds=w.useCallback(()=>{!Ue||Q||(b(!1),Ne(!1),Fe(!1),$(!1),Re(null),qr(h=>({...h,periodicContext:Ac(L,$e)})),He(!0))},[Ue,$e,L,Q]),Su=w.useCallback(()=>{!Ue||Q||(He(!1),Bn({...Ar($t),transparent:$t.background.kind==="transparent",annotations:_s(_t)}))},[Ue,Bn,_t,$t,Q]),ca=w.useCallback(()=>{const h=Kr.current;if(!e||!u||u.index!==c||(u.data.header.coordinates==="unwrapped"?"unwrapped":"source")!==Jt||!h)throw new Error("The current frame is not ready");const y=cm(e);if(y.path.includes("pqviewer-upload-"))throw new Error("Open the source from disk before saving a reusable recipe");const M=u.data.header.frame_key;if(!M)throw new Error("The current frame has no stable source key");return Io({schema:"pqviewer.figure",schema_version:1,source:y,frame:{index:u.index,key:M,fingerprint:Ns(e,u.data)},scene:{presentation:L,selection:{atoms:lo(C),intent:P,minimumImage:q},vectors:{forceScale:sr,velocityScale:ir}},camera:h.captureCamera(),output:Ar($t),annotations:_s(_t)})},[_t,$t,sr,c,u,e,q,L,C,P,ir]),ms=w.useCallback(()=>{try{const h=ca();rn(new Blob([am(h)],{type:"application/json;charset=utf-8"}),Mb(e?.name)),Z({message:"Figure recipe saved",tone:"status"})}catch(h){Z({message:`Recipe unavailable · ${We(h)}`,tone:"error"})}},[ca,e?.name]),hs=w.useCallback(()=>{!e||Q||(Ne(!1),Fe(!1),Xi.current?.click())},[e,Q]),Mu=w.useCallback(async h=>{if(e)try{if(h.size>Wg)throw new Error("Figure recipe is too large");const y=im(await h.text());Jr(y,e)}catch(y){const M=We(y);bt(M),Z({message:`Recipe unavailable · ${M}`,tone:"error"})}},[e,Jr]),ps=_t.some(h=>h.kind==="atom-label"),ku=_t.some(h=>h.kind==="legend"&&h.content==="elements"),vu=_t.find(h=>h.kind==="scale-bar")??null,ju=w.useCallback(h=>{Rt(y=>[...y.filter(M=>M.kind!=="atom-label"),...h?C.map(M=>({kind:"atom-label",atom:{atom:M.atom,image:[...M.image]}})):[]])},[C]),Nu=w.useCallback(h=>{Rt(y=>[...y.filter(M=>M.kind!=="legend"||M.content!=="elements"),...h?[{kind:"legend",content:"elements",position:"top-right"}]:[]])},[]),Fu=w.useCallback(h=>{Rt(y=>[...y.filter(M=>M.kind!=="scale-bar"),...h?[{kind:"scale-bar",length:5,unit:"angstrom",position:"bottom-left"}]:[]])},[]),Cu=w.useCallback(h=>{!Number.isFinite(h)||h<=0||Rt(y=>y.map(M=>M.kind==="scale-bar"?{...M,length:h}:M))},[]),Iu=w.useCallback(h=>{qr(y=>({...y,...h,background:h.background??y.background})),h.projection==="perspective"&&Rt(y=>y.filter(M=>M.kind!=="scale-bar"))},[]);w.useEffect(()=>{ps&&Rt(h=>{const y=new Map(C.map(z=>[Rs(z),z])),M=new Set,O=h.filter(z=>{if(z.kind!=="atom-label")return!0;const ce=Rs(z.atom);return!y.has(ce)||M.has(ce)?!1:(M.add(ce),!0)});for(const z of C){const ce=Rs(z);M.has(ce)||O.push({kind:"atom-label",atom:{atom:z.atom,image:[...z.image]}})}return O})},[ps,C]),w.useEffect(()=>{const h={ready:Ue&&!Gr&&!Q&&!or,error:or||null,export:async(y={})=>{if(or)throw new Error(or);if(!Ue||Gr)throw new Error("The saved figure is not ready");const M=y.transparent===void 0?$t.background:y.transparent?{kind:"transparent"}:{kind:"solid",color:"#ffffff"};await Bn({...Ar($t),...y,background:M,transparent:M.kind==="transparent",annotations:_s(_t)},!0)}};return window.pqviewerFigure=h,()=>{window.pqviewerFigure===h&&delete window.pqviewerFigure}},[Ue,Bn,_t,or,$t,Gr,Q]);const we=w.useCallback(h=>{ee(y=>({...y,...h})),oe("custom")},[]);w.useEffect(()=>{if(!e||!Ee||!Pe||te!=="auto")return;const h=`${e.name}:${e.topology.atom_count}`;Xr.current!==h&&(Xr.current=h,ee(y=>wb("auto",y,Te,hn,!1,Pe)))},[Pe,Te,hn,Ee,e,te]),w.useEffect(()=>{if(!A||Q||!e||e.frame_count<2){Rn.current={key:"",requestTimeMs:null};return}if(p||u?.index!==c)return;const h=[e.dataset_generation??e.name,j,I,v].join(":");Rn.current.key!==h&&(Rn.current={key:h,requestTimeMs:null});const y=performance.now(),M=Rn.current.requestTimeMs??y,O=eu(y,Rn.current.requestTimeMs,j);let z=0;const ce=()=>{const ve=Og(performance.now(),M,j,c,e.frame_count,{mode:I,direction:_,stride:v},{onStep:br=>{l(br.frameIndex),U(br.direction),br.continuePlaying||b(!1)},onPulse:()=>B(br=>br+1)});if(!ve.committed){z=window.setTimeout(ce,fc(ve.schedule.delayMs));return}Rn.current.requestTimeMs=ve.schedule.requestTimeMs};return z=window.setTimeout(ce,fc(O.delayMs)),()=>window.clearTimeout(z)},[c,p,u?.index,e,_,j,I,G,v,A,Q]);const la=w.useCallback(async h=>{if(h.length===0||Q)return;const y=cr.current+1;cr.current=y,Yr.current?.abort();const M=new AbortController;Yr.current=M,Ki(!0),Z({message:"Opening files…",tone:"status"});try{const O=await Zu(h,M.signal);if(y!==cr.current)return;Pn(O),ur.current?.postMessage({datasetGeneration:O.dataset_generation}),Z({message:`Opened ${O.name} · ${cb(O.frame_count)}`,tone:"status"})}catch(O){if(y!==cr.current||M.signal.aborted)return;Z({message:We(O),tone:"error"})}finally{y===cr.current&&(Yr.current=null,Ki(!1))}},[Pn,Q]);w.useEffect(()=>()=>Yr.current?.abort(),[]),w.useEffect(()=>{if(!yt||yt.tone==="error"||Hr||Q)return;const h=window.setTimeout(()=>Z(null),lb(yt.message));return()=>window.clearTimeout(h)},[yt,Hr,Q]);const gs=w.useCallback(()=>{if(Wr.current={prefix:null,at:0},gt)Ne(!1);else if(Xt)Fe(!1);else if(mn)pt(!1);else if(En)He(!1);else if(V)$(!1),requestAnimationFrame(()=>{document.querySelector(".timeline-options > summary")?.focus()});else if(fe)At(!0);else if(se)bn(!0);else if(K.plot)dr(!0);else if(C.length>0)N([]);else return!1;return!0},[bn,dr,At,gt,En,se,mn,V,C.length,Xt,K.plot,fe]),ua=w.useCallback(h=>{if(h==="commands"){Ln();return}if(h==="first-frame"){b(!1),Ke(0);return}if(h==="last-frame"){b(!1),Ke((e?.frame_count??1)-1);return}const y={"next-frame":1,"next-ten-frames":10,"previous-frame":-1,"previous-ten-frames":-10}[h];b(!1),en(y)},[e?.frame_count,Ke,Ln,en]);w.useEffect(()=>{const h=y=>{if(y.defaultPrevented||y.isComposing)return;const M=y.target,O=y.metaKey!==y.ctrlKey&&!y.altKey;if(O&&y.shiftKey&&y.key.toLowerCase()==="s"){y.preventDefault(),gr();return}if(O&&!y.shiftKey&&y.key.toLowerCase()==="o"){y.preventDefault(),On();return}if(O&&!y.shiftKey&&y.key.toLowerCase()==="k"){y.preventDefault(),Ln();return}if(!Q){if(ot&&y.ctrlKey&&!y.metaKey&&!y.altKey&&!y.shiftKey&&y.key==="["){y.preventDefault(),gs();return}if(y.key==="Escape"){gs()&&y.preventDefault();return}if(!(y.metaKey||y.ctrlKey||y.altKey)&&!_b(M)&&!(Rb(M)&&(y.key==="Enter"||y.code==="Space"))){if(y.key==="/"){y.preventDefault(),y.repeat||Ln();return}if(y.key==="?"){y.preventDefault(),y.repeat||eo();return}if(!(gt||Xt)){if(ot){const z=performance.now(),ce=z-Wr.current.at<=750?Wr.current.prefix:null;if(y.repeat&&y.key==="g"){y.preventDefault();return}const ve=yg(y.key,ce);if(Wr.current={prefix:ve.prefix,at:ve.prefix?z:0},ve.prefix){y.repeat||y.preventDefault();return}if(ve.action){y.preventDefault(),ua(ve.action);return}}y.key.toLowerCase()==="v"&&Pe&&!y.repeat?st&&fe==="view"?At(!0):tn("view",!0):y.key.toLowerCase()==="w"&&Pe?.water&&!y.repeat?we({water:L.water==="hide"?"show":"hide"}):y.key.toLowerCase()==="b"&&!y.repeat?we({mode:L.mode==="lines"?"ball-stick":"lines"}):y.key.toLowerCase()==="c"&&Te&&!y.repeat?we({cell:!L.cell}):y.key.toLowerCase()==="f"&&hn&&!y.repeat?we({forces:!L.forces}):y.code==="Space"&&!y.repeat?(y.preventDefault(),(e?.frame_count??0)>1&&b(z=>!z)):y.key==="ArrowLeft"?(y.preventDefault(),b(!1),en(-(y.shiftKey?10:1))):y.key==="ArrowRight"?(y.preventDefault(),b(!1),en(y.shiftKey?10:1)):y.key==="Home"&&!y.repeat?(y.preventDefault(),b(!1),Ke(0)):y.key==="End"&&!y.repeat?(y.preventDefault(),b(!1),Ke((e?.frame_count??1)-1)):y.key.toLowerCase()==="m"&&Oe&&!y.repeat?(y.preventDefault(),mr()):y.key.toLowerCase()==="r"&&!y.repeat?(y.preventDefault(),D(z=>z+1)):["1","2","3","4"].includes(y.key)&&!y.shiftKey&&!y.repeat&&(y.preventDefault(),fr({1:"perspective",2:"xy",3:"xz",4:"yz"}[y.key]))}}}};return window.addEventListener("keydown",h),()=>window.removeEventListener("keydown",h)},[Pe?.water,Oe,Te,At,gt,gs,hn,e?.frame_count,tn,L,Q,ua,fr,Ke,Xt,en,mr,Ln,On,gr,eo,we,fe,st,ot]);const Eu=w.useCallback(h=>{const y=Ng(h);return y===null?null:{id:`select-within-${y}`,label:`Select within ${Ye(y)} Å of selection`,keywords:"nearby radius distance atoms",detail:C.length>0&&ae?"Run":"Select atoms first",disabled:C.length===0||!ae,run:()=>{os(y),Ne(!1)}}},[os,C.length,ae]),$u=w.useMemo(()=>{const h=M=>()=>{M(),Ne(!1)};return[{id:"open",label:"Open files",keywords:"structure trajectory PQ ASE load",detail:Tn.open,run:h(On)},{id:"play",label:A?"Pause trajectory":"Play trajectory",keywords:"movie animation",detail:"Space",disabled:!Oe,run:h(()=>b(M=>!M))},{id:"previous",label:"Previous frame",keywords:"back step",detail:"←",disabled:!Oe||c===0,run:h(()=>{b(!1),en(-1)})},{id:"next",label:"Next frame",keywords:"forward step",detail:"→",disabled:!Oe||c>=(e?.frame_count??1)-1,run:h(()=>{b(!1),en(1)})},{id:"first",label:"First frame",keywords:"start beginning",detail:"Home",disabled:!Oe||c===0,run:h(()=>{b(!1),Ke(0)})},{id:"last",label:"Last frame",keywords:"end final",detail:"End",disabled:!Oe||c>=(e?.frame_count??1)-1,run:h(()=>{b(!1),Ke((e?.frame_count??1)-1)})},{id:"frame-bookmark",label:ts?"Remove frame bookmark":"Bookmark current frame",keywords:"trajectory mark remember frame",detail:"M",disabled:!ye,run:h(mr)},{id:"frame-reference",label:"Set current frame as reference",keywords:"trajectory reference displacement compare",disabled:!ye,run:h(ls)},...K.reference?[{id:"frame-reference-go",label:"Go to reference frame",keywords:"trajectory reference jump",detail:`Frame ${K.reference.index+1}`,run:h(()=>{b(!1),Ke(K.reference.index)})},{id:"frame-reference-clear",label:"Clear reference frame",keywords:"trajectory reference displacement",run:h(()=>ie({type:"clear-reference"}))}]:[],{id:"track-trail",label:K.tracking==="trail"?"Hide selected-atom trails":"Track selected atoms",keywords:"trajectory trail path motion history",disabled:!Je,run:h(()=>hr("trail"))},{id:"track-displacement",label:K.tracking==="displacement"?"Hide displacement vectors":"Show displacement from reference",keywords:"trajectory movement vector reference atoms",detail:K.reference?An(K.reference):"Set a reference first",disabled:!Je||!K.reference,run:h(()=>hr("displacement"))},...Pt.length>=2?[{id:"measurement-compare",label:"Compare pinned measurements",keywords:"trajectory plot distance angle dihedral lines",detail:`${Pt.length} lines`,run:h(fs)}]:[],...Jo.map(M=>({id:`plot-property-${M.name}`,label:`Plot ${M.label}`,keywords:`trajectory property scalar ${M.name}`,detail:M.unit,run:h(()=>us(M))})),{id:"analysis-rdf",label:"Pair distribution",keywords:"trajectory rdf radial distribution structure analysis",detail:ut?"PQAnalysis":"Full periodic cell required",disabled:!ut,run:h(()=>pr("rdf"))},{id:"analysis-coordination",label:"Coordination",keywords:"trajectory coordination number rdf structure analysis",detail:ut?"PQAnalysis":"Full periodic cell required",disabled:!ut,run:h(()=>pr("coordination"))},{id:"fit",label:"Fit structure",keywords:"reset camera center",detail:"R",disabled:!e?.frame_count,run:h(()=>D(M=>M+1))},...["perspective","xy","xz","yz"].map((M,O)=>({id:`view-${M}`,label:M==="perspective"?"Perspective view":`${M.toUpperCase()} view`,keywords:"camera orientation axis",detail:M===ge?"Current":String(O+1),run:h(()=>fr(M))})),{id:"display",label:st&&fe==="view"?"Hide display controls":"Show display controls",keywords:"view representation settings",detail:"V",disabled:!Pe,run:h(()=>st&&fe==="view"?At(!1):tn("view"))},{id:"export",label:"Export figure",keywords:"render image png publication",detail:Tn.export,disabled:!Ue,run:h(gr)},{id:"figure-options",label:"Figure options",keywords:"render image tiff dpi transparent labels legend scale",disabled:!Ue,run:h(ds)},{id:"figure-save-recipe",label:"Save figure recipe",keywords:"reproducible view camera scene json",disabled:!Ue,run:h(ms)},{id:"figure-open-recipe",label:"Open figure recipe",keywords:"restore reproducible view camera scene json",disabled:!e,run:h(hs)},...["ball-stick","spacefill","lines"].map(M=>({id:`mode-${M}`,label:`Representation · ${lu(M)}`,keywords:"style atoms bonds",detail:M===L.mode?"Current":void 0,run:h(()=>we({mode:M}))})),...Pe?.ribbon?[{id:"mode-ribbon",label:"Representation · Ribbon",keywords:"style protein backbone",detail:L.mode==="ribbon"?"Current":void 0,run:h(()=>we({mode:"ribbon"}))}]:[],...Te?[{id:"mode-polyhedra",label:"Representation · Polyhedra",keywords:"style crystal coordination octahedra tetrahedra polygons",detail:L.mode==="polyhedra"?"Current":"Bond-derived",run:h(()=>we({mode:"polyhedra"}))}]:[],...Pe?.water?[{id:"water",label:L.water==="hide"?"Show water":"Hide water",keywords:"solvent",detail:"W",run:h(()=>we({water:L.water==="hide"?"show":"hide"}))}]:[],...Te?[{id:"cell",label:L.cell?"Hide cell":"Show cell",keywords:"box periodic pbc",detail:"C",run:h(()=>we({cell:!L.cell}))}]:[],...hn?[{id:"forces",label:L.forces?"Hide forces":"Show forces",keywords:"vectors arrows",detail:"F",run:h(()=>we({forces:!L.forces}))}]:[],...Xo?[{id:"velocities",label:L.velocities?"Hide velocities":"Show velocities",keywords:"vectors arrows motion speed",run:h(()=>we({velocities:!L.velocities}))}]:[],...Te?[["atom","Atom coordinates"],["molecule","Molecule coordinates"],["unwrapped","Unwrapped coordinates"],["none","Source coordinates"]].map(([M,O])=>({id:`wrap-${M}`,label:O,keywords:"periodic cell boundary coordinates",detail:L.wrap===M?"Current":void 0,run:h(()=>we({wrap:M}))})):[],...Te?[{id:"cell-center-pq",label:"Center cell at PQ origin",keywords:"periodic centered cell origin zero",detail:Wn(L.cellOrigin,[0,0,0])?"Current":void 0,run:h(()=>we({cellOrigin:[0,0,0]}))},{id:"cell-center-structure",label:"Center cell on structure",keywords:"periodic centered centroid atoms",disabled:pn===null,detail:pn&&Wn(L.cellOrigin,pn)?"Current":void 0,run:h(()=>{pn&&we({cellOrigin:pn})})},{id:"cell-center-selection",label:"Center cell on selection",keywords:"periodic centered centroid selected atoms",disabled:gn===null,detail:gn&&Wn(L.cellOrigin,gn)?"Current":C.length===0?"Select atoms first":void 0,run:h(()=>{gn&&we({cellOrigin:gn})})},...["a","b","c"].map((M,O)=>({id:`mirror-${M}`,label:`Mirror ${M}`,keywords:"periodic reflect flip cell axis",detail:L.mirror[O]?"On":"Off",run:h(()=>we({mirror:L.mirror.map((z,ce)=>ce===O?!z:z)}))})),{id:"repeat-3-3-1",label:"Repeat 3 × 3 × 1",keywords:"periodic supercell images replicate",disabled:!hi([3,3,1],$e,e?.topology.atom_count??0),run:h(()=>we({images:su([3,3,1],$e)}))},{id:"periodic-reset",label:"Reset periodic display",keywords:"periodic cell coordinates mirror repeat default",run:h(()=>we(Sc()))}]:[],...no.map(M=>({id:`select-element-${M}`,label:`Select ${ai[M]}`,keywords:`${Dt[M]} element atoms`,detail:Dt[M],run:h(()=>sa(M))})),...X?.waterAtoms.size?[{id:"select-water",label:"Select water",keywords:"solvent molecule H2O atoms",run:h(ia)}]:[],...Y?[["atom","Select anchor atom"],["element","Select anchor element"],["molecule","Select anchor molecule"],["residue","Select anchor residue"],["component","Select connected component"]].map(([M,O])=>({id:`select-scope-${M}`,label:O,keywords:"selection scope expand atoms",disabled:M==="component"&&!ae?.hasConnectivity,run:h(()=>rs(M))})):[],...re.map((M,O)=>({id:`selection-saved-${O}`,label:`Recall selection · ${M.name}`,keywords:"saved named atoms",detail:wr(M.selections.length),run:h(()=>ss(M))})),...P==="measurement"&&C.length>=2&&C.length<=4?[{id:"pin-measurement",label:"Pin measurement",keywords:"selection distance angle dihedral keep",run:h(is)}]:[],...P==="measurement"&&Te&&$e.some(Boolean)&&C.length>=2&&C.length<=4?[{id:"measurement-geometry",label:q?"Use displayed-image geometry":"Use minimum-image geometry",keywords:"selection measurement periodic distance image",detail:q?"Minimum image":"Displayed images",run:h(()=>de(M=>!M))}]:[],...J.map(M=>({id:`measurement-pinned-${M.id}`,label:`Recall pinned measurement ${M.id}`,keywords:"selection distance angle dihedral",detail:`${M.selections.length} atoms`,run:h(()=>as(M))})),...St?[{id:"plot-measurement",label:se?"Hide measurement plot":"Plot measurement",keywords:"selection trajectory distance angle dihedral graph",run:h(()=>se?bn(!1):cs())}]:[],...C.length===1&&nn!==null?[{id:"inspect-selection",label:"Inspect selected atom",keywords:"selection properties coordinates",run:h(()=>tn("inspect"))}]:[],...C.length>0?[{id:"clear-selection",label:"Clear atom selection",keywords:"deselect atoms measurement",detail:"Esc",run:h(()=>N([]))}]:[],{id:"shortcuts",label:"Keyboard shortcuts",keywords:"help keys vim",detail:"?",run:h(eo)},{id:"vim",label:ot?"Disable Vim navigation":"Enable Vim navigation",keywords:"keyboard linux hjkl",detail:ot?"On":"Off",run:h(()=>rr(M=>!M))}].map(M=>({...M,run:()=>{M.run(),fu(O=>[M.id,...O.filter(z=>z!==M.id)].slice(0,8))}}))},[Oe,St,Ue,ut,Pe,Te,bn,At,Pt,fs,ts,ye,hn,c,e?.frame_count,e?.topology.atom_count,se,q,re,tn,is,A,J,$e,L,Jo,ss,as,fr,sa,rs,ia,no,Y,X,ae,P,gn,Ke,Tn,On,us,pr,ms,ds,cs,hs,gr,eo,en,K.reference,K.tracking,ls,hr,mr,Je,we,Xo,ge,ot,fe,st,nn,C.length,pn]),_u=w.useMemo(()=>[...C.length===1&&nn!==null?["inspect-selection","clear-selection"]:[],...Y?["select-scope-element","select-scope-molecule"]:[],...P==="measurement"&&C.length>=2&&C.length<=4?["pin-measurement"]:[],...St?["plot-measurement"]:[],...ye?["frame-bookmark","frame-reference"]:[],...Je?["track-trail"]:[],...K.reference&&Je?["track-displacement"]:[],...Pt.length>=2?["measurement-compare"]:[],...ut?["analysis-rdf"]:[],...Oe?["play","previous","next"]:[],"fit","display","export","figure-options"],[Oe,St,ut,Pt.length,ye,nn,C.length,Y,P,K.reference,Je]),Ru=["workspace",st?"workbench-open":"workbench-closed",Q?"is-rendering":"",Oe?"timeline-present":"timeline-absent",C.length>0?"selection-present":"",En?"figure-sheet-open":"",se||K.plot?"measurement-plot-open":"",V?"playback-options-open":""].filter(Boolean).join(" ");return d.jsxs("main",{className:"app-shell",onDragEnter:h=>{h.preventDefault(),!Q&&(ar.current+=1,Uo(!0))},onDragOver:h=>h.preventDefault(),onDragLeave:h=>{h.preventDefault(),ar.current=Math.max(0,ar.current-1),ar.current===0&&Uo(!1)},onDrop:h=>{h.preventDefault(),ar.current=0,Uo(!1),!Q&&la([...h.dataTransfer.files])},children:[d.jsx("input",{ref:Wi,className:"sr-only file-input",type:"file",tabIndex:-1,disabled:Q,multiple:!0,onChange:h=>{Q||(la([...h.currentTarget.files??[]]),h.currentTarget.value="")}}),d.jsx("input",{ref:Xi,className:"sr-only file-input",type:"file",accept:".pqfigure.json,.pqv.json,application/json",tabIndex:-1,disabled:Q,onChange:h=>{const y=h.currentTarget.files?.[0];y&&!Q&&Mu(y),h.currentTarget.value=""}}),d.jsxs("div",{className:Ru,"aria-busy":n==="loading"||p||Hr||Q,children:[d.jsxs("header",{className:"topbar",children:[d.jsxs("div",{className:"identity",title:e?.name||"Molecular trajectory",children:[d.jsx("img",{className:"identity-mark",src:"/pq-logo.png",alt:""}),d.jsxs("div",{children:[d.jsx("strong",{children:"PQViewer"}),d.jsx("span",{title:e?.name||"Molecular trajectory",children:e?.name||"Molecular trajectory"})]})]}),d.jsxs("div",{className:"topbar-tools",children:[e&&d.jsxs("div",{className:"scene-status",children:[d.jsxs("span",{children:[d.jsx("strong",{children:e.topology.atom_count.toLocaleString()})," ",e.topology.atom_count===1?"atom":"atoms"]}),d.jsxs("span",{children:[d.jsx("strong",{children:e.frame_count.toLocaleString()})," ",e.frame_count===1?"frame":"frames"]}),Te&&d.jsxs("span",{children:["PBC ",d.jsx("strong",{children:$e.map((h,y)=>h?"abc"[y]:"").join("")||"off"})]})]}),d.jsxs("button",{className:"open-button",type:"button",disabled:Q,"aria-keyshortcuts":"Meta+O Control+O",onClick:On,children:[d.jsx(Ae,{name:"folder"}),"Open"]}),d.jsxs("button",{className:"command-button",type:"button","aria-label":"Search commands","aria-keyshortcuts":"Meta+K Control+K","aria-haspopup":"dialog","aria-expanded":gt,disabled:Q,title:`Search commands · ${Tn.commands}`,onClick:Ln,children:[d.jsx(Ae,{name:"search"}),d.jsx("span",{children:"Search"}),d.jsx("kbd",{children:Tn.commands})]}),d.jsxs("button",{ref:Yi,className:"panel-button",type:"button","aria-label":st&&fe==="view"?"Hide display controls":"Show display controls","aria-controls":"workbench","aria-expanded":st&&fe==="view",disabled:Q||!Pe,onClick:()=>st&&fe==="view"?At(!1):tn("view",!0),children:[d.jsx(Ae,{name:"sliders"}),d.jsx("span",{children:"View"})]}),d.jsxs("div",{className:"figure-control",children:[d.jsxs("button",{ref:Qi,className:"render-button",type:"button",disabled:!Ue||Q,"aria-keyshortcuts":"Meta+Shift+S Control+Shift+S",onClick:gr,children:[d.jsx(Ae,{name:"image"}),Q?"Exporting…":"Figure"]}),d.jsx("button",{className:"figure-options-button",type:"button","aria-label":"Figure options","aria-controls":"figure-sheet","aria-expanded":En,disabled:!Ue||Q,onClick:()=>En?He(!1):ds(),children:d.jsx(Ae,{name:"more"})})]})]})]}),e&&e.frame_count>0&&Pe&&d.jsx(eb,{busy:Q,viewPreset:ge,onFit:()=>D(h=>h+1),onView:fr}),e&&e.frame_count>0?d.jsx(fp,{ref:Kr,manifest:e,frame:Ee,presentation:L,selectedAtoms:C,resetSignal:Ur,viewPreset:ge,viewSignal:Be,forceScale:sr,velocityScale:ir,trajectoryOverlays:be,appearance:"light",onSelect:hu,onSelectMany:pu,onSceneInfo:qo,onSelectionContext:ne}):d.jsx("div",{className:"canvas-field"}),e&&Pe&&d.jsxs("aside",{ref:Zi,className:fe==="inspect"?"workbench atom-card":"workbench",id:"workbench","aria-labelledby":"workbench-title",hidden:!st,tabIndex:-1,children:[d.jsxs("div",{className:"workbench-heading",children:[d.jsx("strong",{id:"workbench-title",children:fe==="view"?"View":fe==="summary"?"Selection":nn===null?"Atom":`${Vi(e,nn)} · ${nn+1}`}),d.jsx("button",{className:"icon-button",type:"button",disabled:Q,onClick:()=>{At(!0)},"aria-label":"Close",children:d.jsx(Ae,{name:"close"})})]}),d.jsxs("div",{className:"workbench-body",children:[fe==="view"&&d.jsx(Jg,{presentation:L,capabilities:Pe,cellAvailable:Te,forceAvailable:hn,velocityAvailable:Xo,pbc:$e,atomCount:e.topology.atom_count,structureCellOrigin:pn,selectionCellOrigin:gn,forceScale:sr,velocityScale:ir,onPresentation:we,onForceScale:Gi,onVelocityScale:Hi}),fe==="inspect"&&d.jsx(sb,{manifest:e,frame:Ee,selectedAtom:nn,selectedPosition:bb(oa,C.length-1),cellAvailable:Te}),fe==="summary"&&d.jsx(ob,{summary:gu,uniqueAtoms:new Set(C.map(({atom:h})=>h)).size})]})]}),e&&J.length>0&&ae&&d.jsx(rb,{manifest:e,pins:J,index:ae,cell:X?.cell??null,pbc:$e,activeId:P==="measurement"?J.find(h=>h.minimumImage===q&&hb(h.selections,C))?.id??null:null,onRestore:as,onRemove:h=>me(y=>y.filter(M=>M.id!==h)),canCompare:Pt.length>=2,onCompare:fs}),e&&C.length>0&&!mn&&d.jsx(nb,{manifest:e,selectedAtoms:C,displayedPositions:oa,cell:X?.cell??null,pbc:$e,selectionFormula:Qo,namedSelections:re,selectionAnchor:Y,connectivityAvailable:!!ae?.hasConnectivity,minimumImage:q,measurementEnabled:P==="measurement",canPlot:St,plotOpen:se,trackingAvailable:Je,trackingMode:K.tracking,analysisAvailable:ut,onMinimumImage:()=>de(h=>!h),onPlot:()=>se?bn(!1):cs(),onClear:()=>{N([]),R("measurement"),H(null),bn(!1)},onScope:rs,onWithin:os,onSave:xu,onRecall:ss,onRemoveSaved:wu,onPin:is,onTracking:hr,onAnalyze:()=>pr("rdf"),onDetails:()=>st&&fe==="inspect"?At(!1):tn("inspect",!0),onSummary:()=>st&&fe==="summary"?At(!1):tn("summary",!0)}),e&&se&&St&&d.jsx(ff,{title:ue?.title??kc(e,C),unit:vb(ue?.unit??(C.length===2?"angstrom":"degree")),axisLabel:ue?.axis.label??"Frame",axisUnit:ue?.axis.unit,xValues:ue?.xValues??ns,values:ue?.values??yu,loadedCount:ue?.loadedCount??0,complete:ue?.complete??!1,currentFrame:Tt,onFrame:h=>{b(!1),Ke(h)},onExportCsv:()=>{ue?.complete&&rn(new Blob([Ld(ue)],{type:"text/csv;charset=utf-8"}),Ts(e.name,ue.kind,"csv"))},onExportSvg:()=>{ue?.complete&&rn(new Blob([Vd(ue)],{type:"image/svg+xml;charset=utf-8"}),Ts(e.name,ue.kind,"svg"))},onExportPdf:()=>{if(!ue?.complete)return;const h=Ud(ue),y=h.buffer.slice(h.byteOffset,h.byteOffset+h.byteLength);rn(new Blob([y],{type:"application/pdf"}),Ts(e.name,ue.kind,"pdf"))}}),e&&K.plot&&d.jsx(Oc,{plot:K.plot,currentFrame:Tt,onFrame:K.plot.frameIndices?h=>{b(!1),Ke(h)}:void 0,onRestoreLine:h=>{h.selection&&(h.minimumImage!==void 0&&de(h.minimumImage),et(h.selection,void 0,"measurement"))},headerActions:K.plot.kind==="rdf"?d.jsxs("div",{className:"rdf-view-toggle",role:"group","aria-label":"Pair analysis view",children:[d.jsx("button",{type:"button",className:Le==="rdf"?"is-active":"","aria-pressed":Le==="rdf",disabled:nr,onClick:()=>aa("rdf"),children:"g(r)"}),d.jsx("button",{type:"button",className:Le==="coordination"?"is-active":"","aria-pressed":Le==="coordination",disabled:nr,onClick:()=>aa("coordination"),children:"N(r)"})]}):void 0,onClose:()=>dr(!1),onExportCsv:()=>{K.plot?.complete&&rn(new Blob([Bd(K.plot)],{type:"text/csv;charset=utf-8"}),Ps(e.name,K.plot,"csv"))},onExportSvg:()=>{K.plot?.complete&&rn(new Blob([Dd(K.plot)],{type:"image/svg+xml;charset=utf-8"}),Ps(e.name,K.plot,"svg"))},onExportPdf:()=>{if(!K.plot?.complete)return;const h=zd(K.plot),y=h.buffer.slice(h.byteOffset,h.byteOffset+h.byteLength);rn(new Blob([y],{type:"application/pdf"}),Ps(e.name,K.plot,"pdf"))}}),e&&e.frame_count>1&&d.jsx(ib,{busy:Q,frameCount:e.frame_count,frameIndex:c,displayedFrameIndex:Tt,playing:A,canPlay:Oe,frameError:m,frame:Ee,fps:j,stride:v,mode:I,optionsOpen:V,bookmarks:K.bookmarks,reference:K.reference,currentBookmarked:ts,propertySeries:Jo,analysisAvailable:ut,trackingAvailable:Je,trackingMode:K.tracking,onFrame:h=>{b(!1),Ke(h)},onPlay:()=>Oe&&b(h=>!h),onFps:k,onStride:S,onOptionsOpen:h=>{$(h),h&&se&&bn(!1)},onToggleBookmark:mr,onSetReference:ls,onClearReference:()=>ie({type:"clear-reference"}),onGoToReference:()=>{K.reference&&(b(!1),Ke(K.reference.index))},onProperty:us,onAnalyze:pr,onTracking:hr,onMode:h=>{E(h),U(1)}}),n==="loading"&&d.jsx(Es,{title:"Opening files",busy:!0}),n==="error"&&d.jsx(Es,{title:"Trajectory unavailable",detail:o,alert:!0,action:"Try again",onAction:()=>a(h=>h+1)}),n==="ready"&&e?.frame_count===0&&d.jsx(Es,{title:e.name==="No trajectory"?"Open files":"No frames found",detail:"Drop a structure, trajectory, or PQ run bundle.",action:"Open",onAction:On}),yt&&d.jsxs("div",{className:`${Hr||Q?"notice is-busy":"notice"}${yt.tone==="error"?" is-error":""}`,role:yt.tone==="error"?"alert":"status",title:yt.message,children:[d.jsx("span",{children:yt.message}),yt.tone==="error"&&d.jsx("button",{className:"notice-dismiss",type:"button","aria-label":"Dismiss message",onClick:()=>Z(null),children:d.jsx(Ae,{name:"close"})})]}),En&&d.jsx(Yg,{output:$t,selectedCount:C.length,atomLabels:ps,elementLegend:ku,scaleBar:vu,busy:Q,onOutput:Iu,onAtomLabels:ju,onElementLegend:Nu,onScaleBar:Fu,onScaleBarLength:Cu,onExport:Su,onSaveRecipe:ms,onOpenRecipe:hs,onClose:()=>He(!1)}),e&&d.jsx(dg,{open:mn,frameCount:e.frame_count,options:es,defaultReferenceId:es[0]?.id,initialView:Cn,onRun:Au,onClose:()=>pt(!1)}),du&&d.jsx(tb,{replacing:!!e}),gt&&d.jsx(Qg,{actions:$u,contextIds:_u,recentIds:uu,resolveAction:Eu,onClose:()=>Ne(!1)}),Xt&&d.jsx(Zg,{shortcutLabels:Tn,vimMode:ot,onVimMode:rr,onClose:()=>Fe(!1)})]})]})}function zi(e,t){w.useEffect(()=>{const n=e.current;if(!n)return;const r=document.activeElement instanceof HTMLElement?document.activeElement:null,o=n.parentElement,s=o?.parentElement?[...o.parentElement.children].filter(f=>f instanceof HTMLElement&&f!==o).map(f=>({element:f,inert:f.inert})):[];s.forEach(({element:f})=>{f.inert=!0});const i=()=>[...n.querySelectorAll('button:not([disabled]), input:not([disabled]):not([tabindex="-1"]), select:not([disabled]), textarea:not([disabled]), [href], [tabindex]:not([tabindex="-1"])')].filter(f=>f.offsetParent!==null),a=()=>(t?.current??i()[0]??n).focus(),c=requestAnimationFrame(a),l=f=>{n.contains(f.target)||a()},u=f=>{if(f.key!=="Tab")return;const m=i();if(m.length===0){f.preventDefault(),n.focus();return}const g=m[0],p=m[m.length-1];f.shiftKey&&(document.activeElement===g||!n.contains(document.activeElement))?(f.preventDefault(),p.focus()):!f.shiftKey&&document.activeElement===p&&(f.preventDefault(),g.focus())};return document.addEventListener("focusin",l),n.addEventListener("keydown",u),()=>{cancelAnimationFrame(c),document.removeEventListener("focusin",l),n.removeEventListener("keydown",u),s.forEach(({element:f,inert:m})=>{f.inert=m}),ou(r)}},[])}function ou(e){if(!e?.isConnected)return;if(!e.matches(":disabled")){e.focus();return}const t=new MutationObserver(()=>{!e.isConnected||e.matches(":disabled")||(window.clearTimeout(n),t.disconnect(),e.focus())}),n=window.setTimeout(()=>t.disconnect(),3e4);t.observe(e,{attributes:!0,attributeFilter:["disabled"]})}function Yg({output:e,selectedCount:t,atomLabels:n,elementLegend:r,scaleBar:o,busy:s,onOutput:i,onAtomLabels:a,onElementLegend:c,onScaleBar:l,onScaleBarLength:u,onExport:f,onSaveRecipe:m,onOpenRecipe:g,onClose:p}){const x=w.useRef(null);zi(x);const A=[{label:"Landscape",width:2400,height:1800},{label:"Square",width:2400,height:2400},{label:"Wide",width:3200,height:1800}];return d.jsx("div",{className:"figure-sheet-backdrop",onPointerDown:b=>b.target===b.currentTarget&&p(),children:d.jsxs("aside",{ref:x,className:"figure-sheet export-sheet",id:"figure-sheet",role:"dialog","aria-modal":"true","aria-label":"Figure options",tabIndex:-1,children:[d.jsxs("header",{className:"export-heading",children:[d.jsxs("div",{children:[d.jsx("strong",{children:"Figure"}),d.jsxs("span",{children:[e.width.toLocaleString()," × ",e.height.toLocaleString()," px · ",Ye(e.dpi)," DPI"]})]}),d.jsx("button",{className:"icon-button",type:"button",onClick:p,"aria-label":"Close figure options",children:d.jsx(Ae,{name:"close"})})]}),d.jsxs("div",{className:"export-body",children:[d.jsxs("section",{className:"figure-section",children:[d.jsx("span",{className:"figure-section-label",children:"Size"}),d.jsx("div",{className:"figure-presets",role:"group","aria-label":"Figure size preset",children:A.map(b=>d.jsx("button",{type:"button",className:e.width===b.width&&e.height===b.height?"is-active":"","aria-pressed":e.width===b.width&&e.height===b.height,onClick:()=>i({width:b.width,height:b.height}),children:b.label},b.label))}),d.jsxs("div",{className:"figure-number-grid",children:[d.jsxs("label",{children:[d.jsx("span",{children:"Width"}),d.jsx("input",{type:"number",min:"1",max:"8192",step:"1",value:e.width,onChange:b=>i({width:Number(b.currentTarget.value)})})]}),d.jsxs("label",{children:[d.jsx("span",{children:"Height"}),d.jsx("input",{type:"number",min:"1",max:"8192",step:"1",value:e.height,onChange:b=>i({height:Number(b.currentTarget.value)})})]}),d.jsxs("label",{children:[d.jsx("span",{children:"DPI"}),d.jsx("input",{type:"number",min:"1",max:"2400",step:"1",value:e.dpi,onChange:b=>i({dpi:Number(b.currentTarget.value)})})]})]})]}),d.jsxs("section",{className:"figure-section",children:[d.jsx("span",{className:"figure-section-label",children:"File"}),d.jsx("div",{className:"figure-choice-row",role:"group","aria-label":"Figure format",children:["png","tiff"].map(b=>d.jsx("button",{type:"button",className:e.format===b?"is-active":"","aria-pressed":e.format===b,onClick:()=>i({format:b}),children:b==="png"?"PNG":"TIFF"},b))}),d.jsxs("div",{className:"figure-choice-row",role:"group","aria-label":"Figure background",children:[d.jsx("button",{type:"button",className:e.background.kind==="solid"?"is-active":"","aria-pressed":e.background.kind==="solid",onClick:()=>i({background:{kind:"solid",color:"#ffffff"}}),children:"White"}),d.jsx("button",{type:"button",className:e.background.kind==="transparent"?"is-active":"","aria-pressed":e.background.kind==="transparent",onClick:()=>i({background:{kind:"transparent"}}),children:"Transparent"})]})]}),d.jsxs("section",{className:"figure-section",children:[d.jsx("span",{className:"figure-section-label",children:"Camera"}),d.jsx("div",{className:"figure-choice-row",role:"group","aria-label":"Figure projection",children:["orthographic","perspective"].map(b=>d.jsx("button",{type:"button",className:e.projection===b?"is-active":"","aria-pressed":e.projection===b,onClick:()=>i({projection:b}),children:b==="orthographic"?"Orthographic":"Perspective"},b))})]}),d.jsxs("section",{className:"figure-section",children:[d.jsx("span",{className:"figure-section-label",children:"Annotations"}),d.jsxs("label",{className:"figure-toggle",children:[d.jsxs("span",{children:[d.jsx("strong",{children:"Selected atom labels"}),d.jsx("small",{children:t>0?`${t} selected`:"Select atoms first"})]}),d.jsx("input",{type:"checkbox",checked:n,disabled:t===0,onChange:b=>a(b.currentTarget.checked)})]}),d.jsxs("label",{className:"figure-toggle",children:[d.jsxs("span",{children:[d.jsx("strong",{children:"Element legend"}),d.jsx("small",{children:"Visible elements"})]}),d.jsx("input",{type:"checkbox",checked:r,onChange:b=>c(b.currentTarget.checked)})]}),d.jsxs("label",{className:"figure-toggle",children:[d.jsxs("span",{children:[d.jsx("strong",{children:"Scale bar"}),d.jsx("small",{children:"Orthographic figures"})]}),d.jsx("input",{type:"checkbox",checked:!!o,disabled:e.projection!=="orthographic",onChange:b=>l(b.currentTarget.checked)})]}),o&&d.jsxs("label",{className:"figure-scale-length",children:[d.jsx("span",{children:"Length"}),d.jsx("input",{type:"number",min:"0.0001",step:"any",value:o.length,onChange:b=>u(Number(b.currentTarget.value))}),d.jsx("span",{children:"Å"})]})]}),d.jsxs("section",{className:"figure-section figure-recipe-actions",children:[d.jsx("span",{className:"figure-section-label",children:"Recipe"}),d.jsx("p",{children:"Save this source, frame, scene, and camera as one reproducible view."}),d.jsxs("div",{children:[d.jsx("button",{type:"button",onClick:g,children:"Open"}),d.jsx("button",{type:"button",onClick:m,children:"Save"})]})]})]}),d.jsxs("footer",{className:"export-footer",children:[d.jsx("button",{type:"button",onClick:p,children:"Cancel"}),d.jsx("button",{className:"primary",type:"button",disabled:s,onClick:f,children:s?"Exporting…":`Export ${e.format==="tiff"?"TIFF":"PNG"}`})]})]})})}function Qg({actions:e,contextIds:t,recentIds:n,resolveAction:r,onClose:o}){const[s,i]=w.useState(""),[a,c]=w.useState(0),l=w.useRef(null),u=w.useRef(null),f=w.useRef([]),m=w.useMemo(()=>{const p=af(e,s,{contextIds:t,recentIds:n}),x=r?.(s)??null;return x?[x,...p.filter(A=>A.id!==x.id)]:p},[e,t,s,n,r]);zi(u,l),w.useEffect(()=>c(0),[m]),w.useEffect(()=>{f.current[a]?.scrollIntoView({block:"nearest"})},[a,m]);const g=p=>{m.length!==0&&c(x=>(x+p+m.length)%m.length)};return d.jsx("div",{className:"command-backdrop",onPointerDown:p=>p.target===p.currentTarget&&o(),children:d.jsxs("section",{ref:u,className:"command-palette",role:"dialog","aria-modal":"true","aria-label":"Commands",tabIndex:-1,children:[d.jsxs("label",{className:"command-search",children:[d.jsx(Ae,{name:"search"}),d.jsx("input",{ref:l,value:s,placeholder:"Search commands","aria-label":"Search commands",role:"combobox","aria-autocomplete":"list","aria-controls":"command-results","aria-expanded":"true","aria-activedescendant":m[a]?`command-${m[a].id}`:void 0,onChange:p=>i(p.target.value),onKeyDown:p=>{p.key==="ArrowDown"?(p.preventDefault(),g(1)):p.key==="ArrowUp"?(p.preventDefault(),g(-1)):p.key==="Enter"&&(p.preventDefault(),m[a]?.disabled||m[a]?.run())}}),d.jsx("kbd",{children:"esc"})]}),d.jsxs("div",{className:"command-results",id:"command-results",role:"listbox",children:[m.map((p,x)=>d.jsxs("button",{ref:A=>{f.current[x]=A},id:`command-${p.id}`,type:"button",role:"option","aria-selected":x===a,disabled:p.disabled,className:x===a?"is-active":"",onPointerMove:()=>c(x),onClick:p.run,children:[d.jsx("span",{children:p.label}),p.detail&&d.jsx("kbd",{children:p.detail})]},p.id)),m.length===0&&d.jsx("p",{children:"No commands found"})]})]})})}function Zg({shortcutLabels:e,vimMode:t,onVimMode:n,onClose:r}){const o=w.useRef(null);zi(o);const s=[{title:"Trajectory",items:[["← / →","Previous / next frame"],["Shift ← / →","Move ten frames"],["Home / End","First / last frame"],["Space","Play / pause"],["M","Bookmark frame"]]},{title:"View",items:[["R","Fit structure"],["1 / 2 / 3 / 4","3D / XY / XZ / YZ"],["↑ / ↓","Browse atoms"],["Enter","Toggle atom"],["V","View controls"],["B","Bonds / lines"],["C / F / W","Cell / forces / water"]]},{title:"Workspace",items:[[e.commands,"Search commands"],[e.open,"Open files"],[e.export,"Export figure"],["? / Esc","Shortcuts / close"]]}],i=[["l / h","Next / previous frame"],["L / H","Forward / back ten"],["gg / G","First / last frame"],[":","Search commands"],["Ctrl [","Close surface"]];return d.jsx("div",{className:"command-backdrop shortcut-backdrop",onPointerDown:a=>a.target===a.currentTarget&&r(),children:d.jsxs("section",{ref:o,className:"shortcut-panel",role:"dialog","aria-modal":"true","aria-label":"Keyboard shortcuts",tabIndex:-1,children:[d.jsxs("div",{className:"shortcut-heading",children:[d.jsxs("div",{children:[d.jsx("strong",{children:"Keyboard shortcuts"}),d.jsx("span",{children:"Everything remains available with the mouse."})]}),d.jsx("button",{className:"icon-button",type:"button",onClick:r,"aria-label":"Close keyboard shortcuts",children:d.jsx(Ae,{name:"close"})})]}),d.jsx("div",{className:"shortcut-groups",children:s.map(a=>d.jsxs("section",{children:[d.jsx("h3",{children:a.title}),a.items.map(([c,l])=>d.jsxs("div",{className:"shortcut-row",children:[d.jsx("kbd",{children:c}),d.jsx("span",{children:l})]},`${c}:${l}`))]},a.title))}),d.jsxs("section",{className:t?"vim-shortcuts is-active":"vim-shortcuts",children:[d.jsxs("div",{className:"vim-heading",children:[d.jsxs("div",{children:[d.jsx("strong",{children:"Vim navigation"}),d.jsx("span",{children:"Optional; standard shortcuts stay active."})]}),d.jsx("button",{type:"button",role:"switch","aria-label":"Vim navigation","aria-checked":t,onClick:()=>n(!t),children:d.jsx("i",{})})]}),t&&d.jsx("div",{className:"vim-shortcut-grid",children:i.map(([a,c])=>d.jsxs("div",{className:"shortcut-row",children:[d.jsx("kbd",{children:a}),d.jsx("span",{children:c})]},`${a}:${c}`))})]})]})})}function Jg({presentation:e,capabilities:t,cellAvailable:n,forceAvailable:r,velocityAvailable:o,pbc:s,atomCount:i,structureCellOrigin:a,selectionCellOrigin:c,forceScale:l,velocityScale:u,onPresentation:f,onForceScale:m,onVelocityScale:g}){const p=["ball-stick","spacefill","lines",...t.ribbon?["ribbon"]:[],...n?["polyhedra"]:[]],x=db(e.images,s),A=Math.min(Er,Math.max(1,Math.floor(qn/Math.max(1,i)))),b=Wn(e.cellOrigin,[0,0,0]),j=!b&&!!(c&&Wn(e.cellOrigin,c)),k=!b&&!j&&!!(a&&Wn(e.cellOrigin,a)),v=(S,I)=>{const E=[...x];E[S]=Math.max(1,Math.min(5,Math.round(I))),hi(E,s,i)&&f({images:su(E,s)})};return d.jsxs("div",{className:"scene-panel",children:[d.jsxs("section",{className:"workbench-section",children:[d.jsx("span",{className:"section-label",children:"Representation"}),d.jsx("div",{className:"segmented-options representation-options",children:p.map(S=>d.jsx("button",{type:"button",className:e.mode===S?"is-active":"","aria-pressed":e.mode===S,onClick:()=>f({mode:S}),children:lu(S)},S))})]}),(t.water||n||r||o)&&d.jsxs("section",{className:"workbench-section display-toggles",children:[d.jsx("span",{className:"section-label",children:"Overlays"}),t.water&&d.jsx(mo,{label:"Water",checked:e.water!=="hide",onChange:S=>f({water:S?"show":"hide"})}),n&&d.jsx(mo,{label:"Cell",checked:e.cell,onChange:S=>f({cell:S})}),r&&d.jsx(mo,{label:"Forces",checked:e.forces,onChange:S=>f({forces:S})}),r&&e.forces&&d.jsx(xc,{label:"Force scale",value:l,onChange:m}),o&&d.jsx(mo,{label:"Velocities",checked:e.velocities,onChange:S=>f({velocities:S})}),o&&e.velocities&&d.jsx(xc,{label:"Velocity scale",value:u,onChange:g})]}),n&&d.jsxs("section",{className:"workbench-section",children:[d.jsx("span",{className:"section-label",children:"Periodic system"}),d.jsx("span",{className:"periodic-control-label",children:"Coordinates"}),d.jsx("div",{className:"segmented-options periodic-coordinate-options",children:[["atom","Atoms"],["molecule","Molecules"],["unwrapped","Unwrapped"]].map(([S,I])=>d.jsx("button",{type:"button",className:e.wrap===S?"is-active":"","aria-pressed":e.wrap===S,onClick:()=>f({wrap:S}),children:I},S))}),d.jsx("span",{className:"periodic-control-label",children:"Center cell"}),d.jsxs("div",{className:"segmented-options periodic-center-options",children:[d.jsx("button",{type:"button",className:b?"is-active":"","aria-pressed":b,onClick:()=>f({cellOrigin:[0,0,0]}),children:"PQ"}),d.jsx("button",{type:"button",className:k?"is-active":"","aria-pressed":k,disabled:!a,onClick:()=>{a&&f({cellOrigin:a})},children:"Structure"}),d.jsx("button",{type:"button",className:j?"is-active":"","aria-pressed":j,disabled:!c,title:c?"Center on the selected atoms":"Select atoms first",onClick:()=>{c&&f({cellOrigin:c})},children:"Selection"})]}),d.jsxs("div",{className:"periodic-inline-control",children:[d.jsx("span",{className:"periodic-control-label",children:"Mirror"}),d.jsx("div",{className:"periodic-axis-options","aria-label":"Mirror cell axes",children:["a","b","c"].map((S,I)=>d.jsx("button",{type:"button",className:e.mirror[I]?"is-active":"","aria-label":`Mirror ${S}`,"aria-pressed":e.mirror[I],onClick:()=>f({mirror:e.mirror.map((E,_)=>_===I?!E:E)}),children:S},S))})]}),d.jsxs("div",{className:"periodic-repeat-heading",children:[d.jsx("span",{className:"periodic-control-label",children:"Repeat"}),d.jsxs("span",{children:[x.reduce((S,I)=>S*I,1)," / ",A," cells"]})]}),d.jsx("div",{className:"periodic-repeat-grid",children:["a","b","c"].map((S,I)=>{const E=x[I],_=[...x];_[I]=E+1;const U=s[I],G=hi(_,s,i),B=_.reduce(($,L)=>$*L,1),V=U?E>=5?"Maximum 5 repeats":G?`Repeat ${S}`:i*B>qn?`${qn.toLocaleString()} atom display limit`:`${Er} cell display limit`:`${S} is not periodic`;return d.jsxs("div",{className:U?"periodic-repeat-row":"periodic-repeat-row is-disabled",children:[d.jsx("span",{children:S}),d.jsx("button",{type:"button","aria-label":`Decrease ${S} repeats`,disabled:!U||E<=1,onClick:()=>v(I,E-1),children:"−"}),d.jsxs("output",{"aria-label":`${S} repeats`,children:[E,"×"]}),d.jsx("button",{type:"button","aria-label":`Increase ${S} repeats`,disabled:!U||E>=5||!G,title:V,onClick:()=>v(I,E+1),children:"+"})]},S)})})]})]})}function xc({label:e,value:t,onChange:n}){return d.jsxs("label",{className:"vector-scale-row",children:[d.jsx("span",{children:e}),d.jsx("input",{type:"range",min:.1,max:3,step:.1,value:t,onChange:r=>n(Number(r.target.value))}),d.jsxs("output",{children:[t.toFixed(1),"×"]})]})}function mo({label:e,checked:t,disabled:n=!1,onChange:r}){return d.jsxs("div",{className:n?"toggle-row is-disabled":"toggle-row",children:[d.jsx("span",{children:e}),d.jsx("button",{type:"button",role:"switch","aria-label":e,"aria-checked":t,disabled:n,onClick:()=>r(!t),children:d.jsx("i",{})})]})}function eb({busy:e,viewPreset:t,onFit:n,onView:r}){return d.jsxs("div",{className:"canvas-controls",role:"toolbar","aria-label":"Camera controls",children:[d.jsx("button",{type:"button",disabled:e,onClick:n,children:"Fit"}),[["perspective","3D"],["xy","XY"],["xz","XZ"],["yz","YZ"]].map(([o,s])=>d.jsx("button",{type:"button",disabled:e,className:t===o?"is-active":"","aria-pressed":t===o,onClick:()=>r(o),children:s},o))]})}function tb({replacing:e}){return d.jsx("div",{className:"drop-overlay",role:"status",children:d.jsxs("div",{children:[d.jsx(Ae,{name:"folder"}),d.jsx("strong",{children:e?"Replace data":"Open files"}),d.jsx("span",{children:"Structures, trajectories, and PQ run bundles"})]})})}function nb({manifest:e,selectedAtoms:t,displayedPositions:n,cell:r,pbc:o,selectionFormula:s,namedSelections:i,selectionAnchor:a,connectivityAvailable:c,minimumImage:l,measurementEnabled:u,canPlot:f,plotOpen:m,trackingAvailable:g,trackingMode:p,analysisAvailable:x,onMinimumImage:A,onPlot:b,onClear:j,onScope:k,onWithin:v,onSave:S,onRecall:I,onRemoveSaved:E,onPin:_,onTracking:U,onAnalyze:G,onDetails:B,onSummary:V}){const[$,L]=w.useState(""),[ee,te]=w.useState("3.0"),oe=w.useRef(null);w.useEffect(()=>{const q=de=>{const se=oe.current;se?.open&&!se.contains(de.target)&&(se.open=!1)};return document.addEventListener("pointerdown",q),()=>document.removeEventListener("pointerdown",q)},[]);const C=t.filter(({atom:q})=>q>=0&&qq),P=!!(u&&r&&o.some(Boolean)&&C.length>=2&&C.length<=4),R=n&&n.length===C.length*3&&C.length===t.length?n:null,Y=R&&u&&C.length>=2&&C.length<=4?cu(R,P&&l,r,o):null,H=C.map(q=>Ui(e,q));let X=N.length===1?H[0]:s?`${s} · ${N.length.toLocaleString()} atoms`:`${N.length.toLocaleString()} atoms`,ne="";Y?.ok?(X=`${Y.kind[0].toUpperCase()}${Y.kind.slice(1)} · ${H.join("–")}`,ne=`${Ye(Y.value)} ${Y.unit==="angstrom"?"Å":"°"}`):N.length===1&&R?ne=[R[0],R[1],R[2]].map(q=>Ye(q)).join(" "):u&&N.length>1&&N.length<=4&&(ne=H.slice(0,4).join(" · "));const re=a?e.topology.atom_residue_index?.[a.atom]??-1:-1,W=()=>{oe.current&&(oe.current.open=!1)},J=()=>{const q=Number(ee);!Number.isFinite(q)||q<=0||(v(q),W())},me=()=>{S($)&&(L(""),W())};return d.jsxs("section",{className:"selection-bar","aria-label":"Atom selection",children:[d.jsxs("div",{className:"selection-readout",children:[d.jsx("strong",{title:X,children:X}),ne&&d.jsx("output",{children:ne})]}),P?d.jsxs("button",{className:"measurement-mode",type:"button","aria-pressed":l,"aria-label":l?"Minimum image":"Displayed images",title:"Choose minimum-image or displayed-image geometry",onClick:A,children:[d.jsx("span",{className:"measurement-mode-full",children:l?"Minimum image":"Displayed images"}),d.jsx("span",{className:"measurement-mode-compact","aria-hidden":"true",children:l?"Min. image":"Images"})]}):C.length===1?d.jsx("span",{className:"selection-hint",children:"Shift-click or Shift-drag"}):null,d.jsxs("details",{ref:oe,className:"selection-tools",onKeyDown:q=>{q.key!=="Escape"||!q.currentTarget.open||(q.preventDefault(),q.stopPropagation(),q.currentTarget.open=!1,q.currentTarget.querySelector("summary")?.focus())},children:[d.jsx("summary",{children:"Select"}),d.jsxs("div",{className:"selection-tools-popover",children:[d.jsxs("section",{children:[d.jsx("span",{children:"From anchor"}),d.jsx("div",{className:"selection-scope-grid",children:[["atom","Atom"],["element","Element"],["molecule","Molecule"],["residue","Residue"],["component","Component"]].map(([q,de])=>{const se=q==="residue"&&re<0||q==="component"&&!c||q==="molecule"&&re<0&&!c;return d.jsx("button",{type:"button",disabled:se,onClick:()=>{k(q),W()},children:de},q)})})]}),d.jsxs("section",{children:[d.jsx("label",{htmlFor:"selection-distance",children:"Within selection"}),d.jsxs("div",{className:"selection-input-row",children:[d.jsx("input",{id:"selection-distance",inputMode:"decimal",value:ee,"aria-label":"Distance in angstrom",onChange:q=>te(q.target.value),onKeyDown:q=>q.key==="Enter"&&J()}),d.jsx("span",{children:"Å"}),d.jsx("button",{type:"button",onClick:J,children:"Apply"})]})]}),d.jsxs("section",{children:[d.jsx("label",{htmlFor:"selection-name",children:"Save selection"}),d.jsxs("div",{className:"selection-input-row is-name",children:[d.jsx("input",{id:"selection-name",value:$,maxLength:80,placeholder:"e.g. active site",onChange:q=>L(q.target.value),onKeyDown:q=>q.key==="Enter"&&me()}),d.jsx("button",{type:"button",disabled:!$.trim(),onClick:me,children:"Save"})]})]}),i.length>0&&d.jsxs("section",{className:"saved-selections",children:[d.jsx("span",{children:"Saved"}),i.map(q=>d.jsxs("div",{children:[d.jsxs("button",{type:"button",onClick:()=>{I(q),W()},children:[d.jsx("span",{children:q.name}),d.jsx("small",{children:q.selections.length.toLocaleString()})]}),d.jsx("button",{type:"button","aria-label":`Delete ${q.name}`,onClick:()=>E(q.name),children:d.jsx(Ae,{name:"close"})})]},q.name))]})]})]}),f&&d.jsx("button",{className:"selection-plot-button",type:"button","aria-pressed":m,onClick:b,children:m?"Hide plot":"Plot"}),u&&C.length>=2&&C.length<=4&&d.jsx("button",{className:"selection-pin-button",type:"button",onClick:_,children:"Pin"}),g&&d.jsx("button",{className:"selection-track-button",type:"button","aria-pressed":p!=="off",title:p==="displacement"?"Showing displacement from the reference frame":"Show the previous 50 frames",onClick:()=>U(p==="off"?"trail":"off"),children:p==="off"?"Track":"Stop"}),x&&(!u||C.length>4)&&d.jsx("button",{className:"selection-analyze-button",type:"button",onClick:G,children:"Analyze"}),C.length===1&&d.jsx("button",{type:"button",onClick:B,children:"Details"}),C.length>4&&d.jsx("button",{className:"selection-summary-button",type:"button",onClick:V,children:"Summary"}),d.jsx("button",{className:"icon-button",type:"button",onClick:j,"aria-label":"Clear selection",children:d.jsx(Ae,{name:"close"})})]})}function rb({manifest:e,pins:t,index:n,cell:r,pbc:o,activeId:s,onRestore:i,onRemove:a,canCompare:c,onCompare:l}){const u=w.useRef(null);return w.useEffect(()=>{const f=m=>{u.current?.open&&!u.current.contains(m.target)&&(u.current.open=!1)};return document.addEventListener("pointerdown",f),()=>document.removeEventListener("pointerdown",f)},[]),d.jsxs("details",{ref:u,className:"pinned-measurements",children:[d.jsxs("summary",{children:["Measurements · ",t.length]}),d.jsxs("section",{"aria-label":"Pinned measurements",children:[d.jsxs("header",{children:[d.jsx("strong",{children:"Measurements"}),c&&d.jsx("button",{type:"button",onClick:()=>{u.current&&(u.current.open=!1),l()},children:"Compare"})]}),d.jsx("div",{className:"pinned-measurements__list",children:t.map(f=>{const m=gb(e,n,f,r,o);return d.jsxs("div",{children:[d.jsxs("button",{className:"selection-chip",type:"button","aria-pressed":s===f.id,onClick:()=>{i(f),u.current&&(u.current.open=!1)},children:[d.jsx("span",{children:m.title}),d.jsx("strong",{children:m.value})]}),d.jsx("button",{className:"pinned-measurement-remove",type:"button","aria-label":`Remove pinned ${m.title.toLowerCase()} · ${f.minimumImage?"minimum image":"displayed images"} · ${m.value}`,onClick:()=>a(f.id),children:d.jsx(Ae,{name:"close"})})]},f.id)})})]})]})}function ob({summary:e,uniqueAtoms:t}){return e?d.jsx("div",{className:"inspector-content selection-summary-panel",children:d.jsxs("section",{className:"readout-section",children:[d.jsx(on,{label:"Formula",value:e.formula||"—"}),d.jsx(on,{label:"Occurrences",value:e.count.toLocaleString()}),t!==e.count&&d.jsx(on,{label:"Unique atoms",value:t.toLocaleString()}),d.jsx(Cr,{label:"Cartesian centroid",values:e.centroid,offset:0,unit:"Å"}),d.jsx(Cr,{label:"Extent",values:e.extent,offset:0,unit:"Å"})]})}):d.jsx("p",{className:"quiet-copy",children:"Selection geometry is unavailable."})}function sb({manifest:e,frame:t,selectedAtom:n,selectedPosition:r,cellAvailable:o}){const s=le(t,["forces","force"]),i=le(t,["velocities","velocity","vel"]),a=le(t,["charges","charge"]),c=n!==null&&nA.index===g),x=ub(e.topology.residue_ids,c);return d.jsx("div",{className:"inspector-content",children:c===null?d.jsx("p",{className:"quiet-copy",children:"Click an atom to inspect it."}):d.jsxs("section",{className:"readout-section atom-section",children:[d.jsx(on,{label:"Element",value:l??"—"}),e.topology.atom_names?.[c]&&d.jsx(on,{label:"Name",value:e.topology.atom_names[c]}),p&&d.jsx(on,{label:"Residue",value:`${p.name??`Type ${p.type_id??"—"}`} · ${p.index+1}`}),!p&&x!==null&&d.jsx(on,{label:"Residue ID",value:x}),r&&d.jsx(Cr,{label:o?"Displayed cell position":"Displayed position",values:r,offset:0,unit:"Å"}),s&&d.jsx(Cr,{label:"Force",values:s,offset:c*3,unit:u}),i&&d.jsx(Cr,{label:"Velocity",values:i,offset:c*3,unit:f}),a&&a[c]!==void 0&&d.jsx(on,{label:"Charge",value:yb(Ye(a[c]),m)})]})})}function ib({busy:e,frameCount:t,frameIndex:n,displayedFrameIndex:r,playing:o,canPlay:s,frameError:i,frame:a,fps:c,stride:l,mode:u,optionsOpen:f,bookmarks:m,reference:g,currentBookmarked:p,propertySeries:x,analysisAvailable:A,trackingAvailable:b,trackingMode:j,onFrame:k,onPlay:v,onFps:S,onStride:I,onOptionsOpen:E,onToggleBookmark:_,onSetReference:U,onClearReference:G,onGoToReference:B,onProperty:V,onAnalyze:$,onTracking:L,onMode:ee}){const te=String(r+1).padStart(String(t).length,"0"),oe=String(n+1).padStart(String(t).length,"0"),C=r===n?`${te} / ${t}`:`${te} → ${oe}`,N=r===n?`${ho(r+1)} / ${ho(t)}`:`${ho(r+1)} → ${ho(n+1)}`,P=ab(a);return d.jsx("section",{className:`timeline is-compact${e?" is-busy":""}`,"aria-label":"Trajectory controls",children:d.jsxs("div",{className:"transport-row",children:[d.jsxs("div",{className:"transport-buttons",children:[d.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(0),disabled:e||n===0,"aria-label":"First frame",children:d.jsx(Ae,{name:"first"})}),d.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(n-1),disabled:e||n===0,"aria-label":"Previous frame",children:d.jsx(Ae,{name:"back"})}),d.jsx("button",{type:"button",className:"play-button",onClick:v,disabled:e||!s,"aria-label":o?"Pause":"Play",children:d.jsx(Ae,{name:o?"pause":"play"})}),d.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(n+1),disabled:e||n===t-1,"aria-label":"Next frame",children:d.jsx(Ae,{name:"next"})}),d.jsx("button",{type:"button",className:"transport-button",onClick:()=>k(t-1),disabled:e||n===t-1,"aria-label":"Last frame",children:d.jsx(Ae,{name:"last"})})]}),d.jsxs("div",{className:"scrubber-shell",children:[d.jsxs("label",{className:"scrubber",children:[d.jsx("span",{className:"sr-only",children:"Frame"}),d.jsx("input",{type:"range",min:0,max:Math.max(t-1,0),value:n,disabled:e,onChange:R=>k(Number(R.target.value))})]}),(m.length>0||g)&&d.jsxs("div",{className:"trajectory-marker-rail",children:[m.map(R=>d.jsx("button",{type:"button",className:"trajectory-marker is-bookmark",style:{left:`${jc(R.index,t)}%`},"aria-label":`Go to ${An(R)}`,title:An(R),onClick:()=>k(R.index)},`${R.key.source_id}:${R.key.segment_index}:${R.key.source_index}`)),g&&d.jsx("button",{type:"button",className:"trajectory-marker is-reference",style:{left:`${jc(g.index,t)}%`},"aria-label":`Go to reference · ${An(g)}`,title:`Reference · ${An(g)}`,onClick:B})]})]}),!i&&d.jsxs("output",{className:"frame-counter","aria-label":r===n?`Frame ${r+1} of ${t}`:`Showing frame ${r+1}; loading frame ${n+1}`,children:[d.jsx("span",{className:"frame-counter-full",children:C}),d.jsx("span",{className:"frame-counter-compact","aria-hidden":"true",children:N})]}),P&&d.jsx("span",{className:"frame-metadata",children:P}),i&&d.jsxs("span",{className:"frame-error",title:i,"aria-label":"Frame unavailable",children:[d.jsx("span",{className:"frame-error-full",children:"Frame error"}),d.jsx("span",{className:"frame-error-compact","aria-hidden":"true",children:"Error"})]}),d.jsxs("details",{className:"timeline-options",open:f,onToggle:R=>E(R.currentTarget.open),children:[d.jsx("summary",{"aria-label":"Playback options",children:d.jsx(Ae,{name:"more"})}),d.jsxs("div",{children:[d.jsx("span",{className:"section-label",children:"Frame"}),d.jsxs("div",{className:"timeline-action-list",children:[d.jsx("button",{type:"button",onClick:_,children:p?"Remove bookmark":"Bookmark frame"}),d.jsx("button",{type:"button",onClick:U,children:"Set as reference"}),g&&d.jsxs(d.Fragment,{children:[d.jsx("button",{type:"button",onClick:B,children:"Go to reference"}),d.jsx("button",{type:"button",onClick:G,children:"Clear reference"}),d.jsx("button",{type:"button",disabled:!b,onClick:()=>L("displacement"),children:j==="displacement"?"Hide displacement":"Show displacement"})]})]}),m.length>0&&d.jsxs(d.Fragment,{children:[d.jsx("span",{className:"section-label",children:"Bookmarks"}),d.jsx("div",{className:"timeline-action-list",children:m.map(R=>d.jsx("button",{type:"button",onClick:()=>k(R.index),children:An(R)},`bookmark-action:${R.key.source_id}:${R.key.segment_index}:${R.key.source_index}`))})]}),x.length>0&&d.jsxs(d.Fragment,{children:[d.jsx("span",{className:"section-label",children:"Plot"}),d.jsx("div",{className:"timeline-action-list",children:x.map(R=>d.jsx("button",{type:"button",onClick:()=>V(R),children:R.label},R.name))})]}),A&&d.jsxs(d.Fragment,{children:[d.jsx("span",{className:"section-label",children:"Pair analysis"}),d.jsxs("div",{className:"timeline-action-list",children:[d.jsx("button",{type:"button",onClick:()=>$("rdf"),children:"Pair distribution"}),d.jsx("button",{type:"button",onClick:()=>$("coordination"),children:"Coordination"})]})]}),d.jsx("span",{className:"section-label",children:"Playback"}),d.jsxs("label",{children:[d.jsx("span",{children:"Speed"}),d.jsx("select",{value:c,onChange:R=>S(Number(R.target.value)),children:[1,5,10,12,15,24,30,60].map(R=>d.jsxs("option",{value:R,children:[R," fps"]},R))})]}),d.jsxs("label",{children:[d.jsx("span",{children:"Stride"}),d.jsx("select",{value:l,onChange:R=>I(Number(R.target.value)),children:[1,2,5,10].map(R=>d.jsxs("option",{value:R,children:[R," frame",R===1?"":"s"]},R))})]}),d.jsx("div",{className:"segmented-options",children:[["once","Once"],["loop","Loop"],["rock","Rock"]].map(([R,Y])=>d.jsx("button",{type:"button",className:u===R?"is-active":"","aria-pressed":u===R,onClick:()=>ee(R),children:Y},R))})]})]})]})})}function ab(e){const t=Mc(e,"step"),n=Mc(e,"time"),r=t===null?"":`step ${Ye(t)}`,o=n===null?"":`t ${Ye(n)}`;return[r,o].filter(Boolean).join(" · ")}function ho(e){const t=Math.max(0,Math.round(e));if(t<1e4)return String(t);const[n,r]=t>=1e9?[1e9,"B"]:t>=1e6?[1e6,"M"]:[1e3,"k"],o=t/n;return`${Number(o.toFixed(o>=10?0:1))}${r}`}function cb(e){const t=Number.isFinite(e)?Math.max(0,Math.floor(e)):0;return`${t.toLocaleString()} ${t===1?"frame":"frames"}`}function wr(e){return`${e.toLocaleString()} ${e===1?"atom":"atoms"}`}function lb(e){return Math.min(1e4,Math.max(4200,e.length*70))}function ub(e,t){if(t===null||!Number.isInteger(t)||t<0||!e||t>=e.length)return null;const n=e.some(o=>{const s=String(o).trim();return s!==""&&s!=="0"}),r=String(e[t]).trim();return n&&r!==""?r:null}function fb(e){const t=e?.header.pbc;return Array.isArray(t)&&t.length===3?[!!t[0],!!t[1],!!t[2]]:Bf(e)}function wc(e,t){const n=le(e,["positions","position","coordinates","coords"]);return n?Of(e,Math.floor(n.length/3),t??null):null}function Wn(e,t){return e.length>=3&&t.length>=3&&[0,1,2].every(n=>Math.abs(e[n]-t[n])<=1e-6)}function db(e,t){return e.min.map((n,r)=>t[r]?Math.max(1,Math.min(5,Math.round(Math.abs(e.max[r]-n)+1))):1)}function su(e,t){const n=e.map((r,o)=>t[o]?Math.max(1,Math.min(5,Math.round(r))):1);return{min:n.map(r=>{const o=Math.floor((r-1)/2);return o===0?0:-o}),max:n.map(r=>Math.ceil((r-1)/2))}}function hi(e,t,n){if(e.some(s=>!Number.isInteger(s)||s<1||s>5)||e.some((s,i)=>!t[i]&&s!==1))return!1;const r=e.reduce((s,i)=>s*i,1),o=Math.min(Er,Math.max(1,Math.floor(qn/Math.max(1,n))));return r<=o}function Ac(e,t){return t.some(Boolean)&&(e.wrap==="atom"||e.wrap==="unwrapped")&&e.mode!=="spacefill"&&e.mode!=="ribbon"}function Sc(){return{wrap:"atom",images:{min:[0,0,0],max:[0,0,0]},cellOrigin:[0,0,0],mirror:[!1,!1,!1]}}function mb(e){return{atom:"Atom",element:"Element",molecule:"Molecule",residue:"Residue",component:"Connected component"}[e]}function iu(e,t){return e.atom===t.atom&&e.image[0]===t.image[0]&&e.image[1]===t.image[1]&&e.image[2]===t.image[2]}function hb(e,t){return e.length===t.length&&e.every((n,r)=>iu(n,t[r]))}function au(e,t){const n=new Float64Array(t.length*3);for(let r=0;r=e.count||n.has(o.atom)||(n.add(o.atom),r.push(e.atomicNumbers[o.atom]));return Yl(r)}function cu(e,t,n,r){const o=Math.floor(e.length/3);return Ws(e,Array.from({length:o},(s,i)=>i),t&&n&&r.some(Boolean)?{mode:"minimum-image",cell:n,pbc:r}:{mode:"direct"})}function gb(e,t,n,r,o){const s=n.selections.map(c=>Ui(e,c)),i=au(t,n.selections),a=i?cu(i,n.minimumImage,r,o):null;return a?.ok?{title:`${a.kind[0].toUpperCase()}${a.kind.slice(1)} · ${s.join("–")}`,value:`${Ye(a.value)} ${a.unit==="angstrom"?"Å":"°"}`}:{title:s.join("–")||"Measurement",value:"—"}}function bb(e,t){return!e||t<0||e.length<(t+1)*3?null:e.slice(t*3,t*3+3)}function Mc(e,t){const n=e?.header[t];if(typeof n=="number"&&Number.isFinite(n))return n;const r=e?.header.scalars?.[t];return typeof r=="number"&&Number.isFinite(r)?r:null}function on({label:e,value:t}){return d.jsxs("div",{className:"readout",children:[d.jsx("span",{children:e}),d.jsx("strong",{children:t})]})}function Cr({label:e,values:t,offset:n,unit:r}){return d.jsxs("div",{className:"vector-readout",children:[d.jsx("span",{children:e}),d.jsxs("code",{children:[d.jsx("i",{children:"x"}),Ye(t[n]),d.jsx("i",{children:"y"}),Ye(t[n+1]),d.jsx("i",{children:"z"}),Ye(t[n+2]),r&&d.jsx("b",{children:r})]})]})}function Es({title:e,detail:t,busy:n=!1,alert:r=!1,action:o,onAction:s}){return d.jsxs("div",{className:"centered-state",role:r?"alert":"status",children:[d.jsxs("div",{className:n?"state-orbit is-busy":"state-orbit","aria-hidden":"true",children:[d.jsx("i",{}),d.jsx("i",{}),d.jsx("b",{})]}),d.jsx("h1",{children:e}),t&&d.jsx("p",{children:t}),o&&s&&d.jsxs("button",{type:"button",onClick:s,children:[d.jsx(Ae,{name:o==="Open"?"folder":"retry"}),o]})]})}function Ae({name:e}){const t={fill:"none",stroke:"currentColor",strokeWidth:1.6,strokeLinecap:"round",strokeLinejoin:"round"};return d.jsxs("svg",{className:"icon",viewBox:"0 0 24 24","aria-hidden":"true",children:[e==="folder"&&d.jsx("path",{d:"M4 7.5h6l1.6 2H20v8.5H4V7.5Z",...t}),e==="image"&&d.jsxs(d.Fragment,{children:[d.jsx("rect",{x:"4",y:"5",width:"16",height:"14",rx:"2",...t}),d.jsx("circle",{cx:"9",cy:"10",r:"1.5",...t}),d.jsx("path",{d:"m6.5 17 4.2-4 2.6 2.4 2.2-2 2 1.8",...t})]}),e==="sliders"&&d.jsxs(d.Fragment,{children:[d.jsx("path",{d:"M5 7h5m4 0h5M5 17h3m4 0h7",...t}),d.jsx("circle",{cx:"12",cy:"7",r:"2",...t}),d.jsx("circle",{cx:"10",cy:"17",r:"2",...t})]}),e==="play"&&d.jsx("path",{d:"m9 7 7 5-7 5V7Z",fill:"currentColor"}),e==="pause"&&d.jsx(d.Fragment,{children:d.jsx("path",{d:"M9 7v10M15 7v10",...t,strokeWidth:"2"})}),e==="first"&&d.jsxs(d.Fragment,{children:[d.jsx("path",{d:"M7.5 7v10",...t}),d.jsx("path",{d:"m16 8-5 4 5 4",...t})]}),e==="back"&&d.jsx("path",{d:"m14.5 8-5 4 5 4",...t}),e==="next"&&d.jsx("path",{d:"m9.5 8 5 4-5 4",...t}),e==="last"&&d.jsxs(d.Fragment,{children:[d.jsx("path",{d:"M16.5 7v10",...t}),d.jsx("path",{d:"m8 8 5 4-5 4",...t})]}),e==="more"&&d.jsxs(d.Fragment,{children:[d.jsx("circle",{cx:"7",cy:"12",r:"1",fill:"currentColor"}),d.jsx("circle",{cx:"12",cy:"12",r:"1",fill:"currentColor"}),d.jsx("circle",{cx:"17",cy:"12",r:"1",fill:"currentColor"})]}),e==="search"&&d.jsxs(d.Fragment,{children:[d.jsx("circle",{cx:"10.5",cy:"10.5",r:"5.5",...t}),d.jsx("path",{d:"m14.6 14.6 4 4",...t})]}),e==="close"&&d.jsx("path",{d:"m8 8 8 8m0-8-8 8",...t}),e==="retry"&&d.jsxs(d.Fragment,{children:[d.jsx("path",{d:"M18 9a7 7 0 1 0 .5 5",...t}),d.jsx("path",{d:"M18 5v4h-4",...t})]})]})}function $s(e,t,n){const r=wo(n),o=e?.header.arrays.find(i=>wo(i.name)===r),s=Object.entries(t.properties??{}).find(([i])=>wo(i)===r)?.[1];return Vr(o?.unit??s?.unit)}function Vr(e){if(e)return e.replace(/angstrom/gi,"Å").replace(/Angstrom/g,"Å")}function yb(e,t){return t?`${e} ${t}`:e}function Vi(e,t){return e.topology.symbols?.[t]??Lb[e.topology.atomic_numbers?.[t]??0]??"X"}function Ui(e,t){const n=`${Vi(e,t.atom)}${t.atom+1}`,r=t.image.map((o,s)=>{if(o===0)return"";const i=o>0?"+":"−",a=Math.abs(o)===1?"":Math.abs(o);return`${i}${a}${"abc"[s]}`}).join("");return r?`${n} (${r})`:n}function wo(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"")}function Ye(e){if(!Number.isFinite(e))return"—";const t=Math.abs(e);return t!==0&&(t>=1e4||t<.001)?e.toExponential(3):new Intl.NumberFormat("en",{maximumFractionDigits:4}).format(e)}function We(e){return e instanceof Error?e.message:"Unexpected error"}function lu(e){return{"ball-stick":"Ball + stick",spacefill:"Spacefill",licorice:"Licorice",lines:"Lines",ribbon:"Ribbon",polyhedra:"Polyhedra"}[e]}function xb(e,t,n){return e.ribbon?"protein":e.suggestedProfile==="crystal"?"crystal":"molecule"}function wb(e,t,n,r,o,s){const i=xb(s);return Ab(i,t,n,r,s)}function Ab(e,t,n,r,o){const s={min:[0,0,0],max:[0,0,0]};return e==="protein"?{...t,mode:o.ribbon?"ribbon":"licorice",water:o.water?"hide":"show",hydrogens:!1,wrap:"molecule",images:s,cell:!1,forces:!1,velocities:!1,color:o.ribbon?"residue":"element"}:e==="crystal"?{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:"atom",images:s,cell:n,forces:!1,velocities:!1,color:"element"}:e==="trajectory"?{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:n?"atom":"none",images:s,cell:n,forces:r,velocities:!1,color:"element"}:{...t,mode:"ball-stick",water:"show",hydrogens:!0,wrap:"molecule",images:s,cell:!1,forces:r,velocities:!1,color:"element"}}function Sb(e,t,n,r){return`${Or(e,"molecule")}-${t}x${n}.${r}`}function Mb(e){return`${Or(e,"molecule")}.pqfigure.json`}function Or(e,t){return(e??t).replace(/\.[^.]+$/,"").replace(/[^a-z0-9._-]+/gi,"-").replace(/^-+|-+$/g,"")||t}function Ar(e){return{...e,background:e.background.kind==="transparent"?{kind:"transparent"}:{kind:"solid",color:e.background.color}}}function _s(e){return e.map(t=>t.kind==="atom-label"?{...t,atom:{atom:t.atom.atom,image:[...t.atom.image]},...t.offset?{offset:[...t.offset]}:{}}:{...t})}function Rs(e){return`${e.atom}:${e.image.join(",")}`}function kb(){return new URLSearchParams(window.location.search).get("headless")==="1"}function kc(e,t){return`${t.length===2?"Distance":t.length===3?"Angle":"Dihedral"} · ${t.map(r=>Ui(e,r)).join("–")}`}function vb(e){return e==="angstrom"?"Å":"°"}function Ts(e,t,n){return`${Or(e,"trajectory")}-${t}.${n}`}function Ps(e,t,n){const r=t.kind==="comparison"?"measurements":t.kind==="rdf"?"pair-analysis":Or(t.lines[0]?.label,t.kind);return`${Or(e,"trajectory")}-${r}.${n}`}function Os(e,t){return[...new Set(e.filter(n=>Number.isSafeInteger(n)&&n>=0&&nn-r)}function jb(e,t){if(!e||t.frame_counts!==r[i]))return!1;const o=s=>s.source?.segments?.map(i=>({source_id:i.source_id,kind:i.kind,path:i.path??null,input:i.input??null,files:i.files??null}))??[];return JSON.stringify(o(e))===JSON.stringify(o(t))}function Nb(e){const t=e.filter(({selections:r})=>r.length===2),n=e.filter(({selections:r})=>r.length===3||r.length===4);return n.length>t.length?n:t}function Fb(e,t,n){const r=Array.from({length:t},(o,s)=>s+1);return{requestId:n,kind:"comparison",title:"Measurement comparison",xLabel:"Frame",yLabel:e[0]?.selections.length===2?"Distance":"Angle",yUnit:e[0]?.selections.length===2?"Å":"°",xValues:r,frameIndices:r.map((o,s)=>s),lines:e.map(o=>({id:o.id,label:o.label??o.id,values:r.map(()=>null),selection:o.selections,minimumImage:o.minimumImage})),loadedCount:0,totalCount:t,complete:!1}}function Cb(e,t,n){return{requestId:n,kind:"property",title:e.label,xLabel:"Frame",yLabel:e.label,yUnit:Vr(e.unit),xValues:Array.from({length:t},(r,o)=>o+1),frameIndices:Array.from({length:t},(r,o)=>o),lines:[{id:e.name,label:e.label,values:e.values}],loadedCount:t,totalCount:t,complete:!0}}function Ib(e,t){const n=e==="coordination";return{requestId:t.requestId,kind:"rdf",title:`${n?"Coordination":"Pair distribution"} · ${Po(t.referenceLabel)} → ${Po(t.targetLabel)}`,xLabel:"Radius",xUnit:"Å",yLabel:n?"N(r)":"g(r)",yFloor:0,xValues:[],lines:[{id:n?"coordination":"rdf",label:n?"N(r)":"g(r)",values:[]}],loadedCount:0,totalCount:0,complete:!1}}function vc(e,t,n){const r=t==="coordination",o=r?e.coordinationRadius:e.radiusCenters,s=r?e.coordination:e.gR;return{requestId:n.requestId,kind:"rdf",title:`${r?"Coordination":"Pair distribution"} · ${Po(n.referenceLabel)} → ${Po(n.targetLabel)}`,xLabel:"Radius",xUnit:Vr(e.radiusUnit),yLabel:r?"N(r)":"g(r)",yFloor:0,yUnit:$b(r?e.coordinationUnit:e.rdfUnit),context:Eb(e),xValues:o,lines:[{id:r?"coordination":"rdf",label:r?"N(r)":"g(r)",values:s}],loadedCount:s.length,totalCount:s.length,complete:!0}}function Eb(e){const t=Vr(e.radiusUnit)??e.radiusUnit;return[`${e.frameRange.count.toLocaleString()} frames`,`${e.referenceIndices.length.toLocaleString()}×${e.targetIndices.length.toLocaleString()} atoms`,`Δr ${Ye(e.deltaR)} ${t}`,`r max ${Ye(e.rMax)} ${t}`].join(" · ")}function $b(e){const t=e?.trim().toLowerCase();return t&&!["1","dimensionless","unitless"].includes(t)?Vr(e):void 0}function Po(e){return/^All (.+) atoms$/.exec(e)?.[1]??e}function jc(e,t){return t<=1?0:Math.max(0,Math.min(100,e/(t-1)*100))}function rn(e,t){const n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t,r.style.display="none",document.body.append(r),r.click(),r.remove(),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)}function _b(e){return!!(e?.isContentEditable||e?.closest('input, select, textarea, [role="textbox"]'))}function Rb(e){return!!e?.closest('button, a[href], [role="button"], [role="menuitem"]')}function Tb(){try{return gg(window.localStorage.getItem("pqviewer-vim-navigation"))}catch{return!1}}function Pb(){return typeof navigator>"u"?"":navigator.userAgentData?.platform||navigator.platform||navigator.userAgent}function Ob(){try{const e=JSON.parse(window.localStorage.getItem("pqviewer-presentation")??"null");return!e||typeof e!="object"?tt:{mode:["ball-stick","spacefill","lines","ribbon","polyhedra"].includes(e.mode)?e.mode:tt.mode,water:e.water==="hide"?"hide":"show",hydrogens:tt.hydrogens,wrap:tt.wrap,images:tt.images,cellOrigin:[0,0,0],mirror:[!1,!1,!1],cell:typeof e.cell=="boolean"?e.cell:tt.cell,forces:typeof e.forces=="boolean"?e.forces:tt.forces,velocities:typeof e.velocities=="boolean"?e.velocities:tt.velocities,atomScale:tt.atomScale,bondScale:tt.bondScale,color:tt.color,quality:tt.quality}}catch{return tt}}const Lb=["X","H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr"];Tu.createRoot(document.getElementById("root")).render(d.jsx(w.StrictMode,{children:d.jsx(Xg,{})})); diff --git a/pqviewer/static/index.html b/pqviewer/static/index.html index 0863f0d..dafd39c 100644 --- a/pqviewer/static/index.html +++ b/pqviewer/static/index.html @@ -7,11 +7,11 @@ PQViewer - + - +
diff --git a/tests/test_recipe.py b/tests/test_recipe.py index e92727e..a523ecf 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -652,6 +652,42 @@ def test_headless_render_rejects_invalid_timeout_concisely( assert "Traceback" not in error +@pytest.mark.skipif( + os.environ.get("PQVIEWER_HEADLESS_TEST") != "1", + reason="requires the render extra and Chromium", +) +def test_headless_render_rejects_unsupported_polyhedra( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + source = tmp_path / "run.xyz" + recipe_path = tmp_path / "polyhedra.pqfigure.json" + output = tmp_path / "figure.png" + write_source(source) + recipe = figure_recipe("run.xyz") + recipe["scene"]["presentation"]["mode"] = "polyhedra" + write_recipe(recipe_path, recipe) + + with pytest.raises(SystemExit) as exit_info: + render_cli.main([ + str(recipe_path), + "--output", + str(output), + "--width", + "320", + "--height", + "240", + ]) + + assert exit_info.value.code == 1 + error = capsys.readouterr().err + assert error.startswith("pqviewer render: ") + assert "Polyhedra unavailable" in error + assert "supported center with 3+ bonded ligands" in error + assert "Traceback" not in error + assert not output.exists() + + @pytest.mark.skipif( os.environ.get("PQVIEWER_HEADLESS_TEST") != "1", reason="requires the render extra and Chromium",