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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ user-visible changes.
- Hide duplicate View/Edit/Analyze tabs in the inspector on wide layouts
- Treat Export as a quiet header action until the figure sheet is open
- Show the Bonds switch as off when the current representation does not use bonds
- Ignore `?renderer=three` so interactive viewing stays on 3Dmol

### Changed

- Capitalize the Python distribution name as `MolarVerse-PQViewer`
- Put representation, atoms, and layers first in View, with the periodic cell and appearance last
- Move interactive quality next to light and dark appearance
- Enlarge inspector labels and controls from 9–10 px to 11–12 px and load Inter at regular weights
- Ship only Latin, Latin-extended, and Greek Inter files

## [0.1.0] - 2026-08-06

Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Contributing

PQViewer welcomes focused bug fixes, scientific regression cases, documentation,
and viewer improvements.
and viewer improvements. What to work on next, and what not to add, is in
[PRODUCT_DIRECTION.md](PRODUCT_DIRECTION.md).

## Before changing code

Expand Down
60 changes: 49 additions & 11 deletions PRODUCT_DIRECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,65 @@ they are stable enough for the wider ecosystem.

## Current release scope

The first public release covers:
The public beta already covers:

- PQ trajectories, inputs, run directories, and declared restart chains
- optional ASE files, `Atoms`, and indexed trajectories
- Jupyter `view()` embedding and a static web demo
- lazy PQ frame access and bounded frontend prefetching
- PQ-centered orthorhombic and triclinic cells
- atom, molecule, unwrapped, mirrored, centered, and repeated periodic views
- local atom and cell edits with EXTXYZ download
- direct and scoped selection, measurements, saved selections, and comparisons
- trajectory playback, bookmarks, reference displacement, and atom trails
- scalar, measurement, pair-distribution, and coordination plots
- forces, velocities, charges, water display, and topology-aware ribbons
- command search, broad keyboard access, and optional Vim navigation
- publication raster figures, vector plot output, and source-validated recipes

## Next priorities
Packaging (`MolarVerse-PQViewer` on PyPI) and notebook embedding exist. Do not
rebuild them. Finish honesty, the PQ run loop, and citable exports.

1. Publish a documented, installable release with stable packaging and examples.
2. Make unsupported actions explain their data requirements in the interface.
3. Improve exported scientific metadata for measurement and pair-analysis CSV.
4. Expand redistributable examples for liquids, crystals, MOFs, and proteins.
5. Add notebook embedding around the same dataset and renderer contracts.
6. Define extension contracts for representations and PQAnalysis results after
the core API has release experience.
## Next steps

Work in this order. Do not start later items to look busy.

### 1. Honest disabled actions

Every control that cannot run should say which data it needs: ribbon, polyhedra,
unwrapped coordinates, pair distribution, coordination, tracking, recipes, and
missing sidecars. Missing capabilities stay hidden or disabled. They are never
faked.

### 2. The PQ run as the default path

`pqviewer path/to/run` should be the usual step after a job. Explain restart
chains, incomplete companions, and growing files in the interface. Keep
`refresh()` bounded. Do not add a file manager.

### 3. Exports a paper can reuse

Measurement and pair-analysis CSV should record units, frame identity, periodic
mode, and analysis populations. Figure recipes already validate the source;
keep them the reproducibility contract.

### 4. Examples people can open

Ship small redistributable liquid, crystal, MOF, and protein fixtures in
`examples/`, including the sources already used in the docs. Provenance stays
in `examples/README.md`.

### 5. After 1.0

Harden Jupyter for remote kernels (loopback and port forwarding). Move stable
`FrameKey`, centered-cell, and pair-result contracts upstream into PQAnalysis
only after they stop changing.

### Not now

Do not start extension APIs, a second interactive engine, PQEnalyzer embedding,
or VMD/OVITO plugin parity. Those wait until the core viewer has release
experience.

## Quality gates

Expand All @@ -118,9 +154,11 @@ Every release needs:

- simulation setup or execution
- cluster and job management
- coordinate editing or calculator setup
- calculator setup (ASE calculator results may be read; never trigger `calculate`)
- growing Edit into molecule building
- a permanent energy dashboard
- embedding the PQEnalyzer interface
- duplicating PQAnalysis calculations in the frontend
- multiple interactive rendering engines without a clear scientific benefit
- a second interactive rendering engine (`?renderer=three` is not a product)
- broad plugin parity with VMD, OVITO, or ChimeraX
- extension APIs before 1.0
4 changes: 2 additions & 2 deletions docs/viewer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ Use command search for **Source coordinates** when the stored coordinates need
to be shown without display wrapping.

The interactive view uses the locally bundled 3Dmol renderer. If it cannot
initialize, PQViewer keeps the established Three renderer available as a
fallback.
initialize, PQViewer falls back to the publication renderer so the structure
stays visible. Do not add a second interactive engine.

## Export

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/RendererScene.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { describe, expect, it } from "vitest";
import { resolveRendererEngine } from "./RendererScene";

describe("renderer selection", () => {
it("uses the bundled 3Dmol engine by default", () => {
it("uses the bundled 3Dmol engine for interactive viewing", () => {
expect(resolveRendererEngine("")).toBe("3dmol");
expect(resolveRendererEngine("?renderer=3dmol")).toBe("3dmol");
});

it("keeps the previous renderer as an explicit fallback", () => {
expect(resolveRendererEngine("?renderer=three")).toBe("three");
it("does not offer a second interactive engine through the URL", () => {
expect(resolveRendererEngine("?renderer=three")).toBe("3dmol");
expect(resolveRendererEngine("?renderer=other")).toBe("3dmol");
});
});
7 changes: 2 additions & 5 deletions frontend/src/RendererScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ interface PublicationRequest {
timeout: number;
}

export function resolveRendererEngine(search?: string): RendererEngineId {
const value = new URLSearchParams(
search ?? (typeof window === "undefined" ? "" : window.location.search),
).get("renderer");
return value === "three" ? "three" : "3dmol";
export function resolveRendererEngine(_search?: string): RendererEngineId {
return "3dmol";
}

export const MoleculeScene = forwardRef<MoleculeSceneHandle, MoleculeSceneProps>(
Expand Down
16 changes: 12 additions & 4 deletions frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "@fontsource/inter/400.css";
import "@fontsource/inter/500.css";
import "@fontsource/inter/600.css";
import "@fontsource/inter/700.css";
import "@fontsource/inter/latin-400.css";
import "@fontsource/inter/latin-500.css";
import "@fontsource/inter/latin-600.css";
import "@fontsource/inter/latin-700.css";
import "@fontsource/inter/latin-ext-400.css";
import "@fontsource/inter/latin-ext-500.css";
import "@fontsource/inter/latin-ext-600.css";
import "@fontsource/inter/latin-ext-700.css";
import "@fontsource/inter/greek-400.css";
import "@fontsource/inter/greek-500.css";
import "@fontsource/inter/greek-600.css";
import "@fontsource/inter/greek-700.css";
import App from "./App";
import "./styles.css";

Expand Down
133 changes: 0 additions & 133 deletions frontend/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -468,39 +468,6 @@ output {
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 rgba(31, 51, 57, 0.1);
color: var(--text);
font-weight: 600;
}

.scene-group {
padding: 13px 0;
border-top: 1px solid var(--line);
Expand Down Expand Up @@ -2130,7 +2097,6 @@ input[type="range"]::-moz-range-thumb {
}

.more-menu button,
.profile-strip button,
.representation-grid button,
.image-presets button,
.scene-actions button,
Expand Down Expand Up @@ -3684,41 +3650,6 @@ button.measurement-plot__legend-item:hover {
pointer-events: none;
}

.preset-options {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 5px;
}

.preset-options button {
min-width: 0;
min-height: 34px;
padding: 0 9px;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--surface);
color: var(--muted);
font-size: 10px;
cursor: pointer;
}

.preset-options button:hover:not(:disabled) {
border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
color: var(--text);
}

.preset-options button.is-active {
border-color: color-mix(in srgb, var(--accent) 48%, var(--line));
background: var(--accent-soft);
color: var(--accent);
font-weight: 650;
}

.preset-options button:disabled {
cursor: default;
opacity: 0.45;
}

.workbench[hidden],
.workbench-pane[hidden] {
display: none;
Expand Down Expand Up @@ -3918,10 +3849,6 @@ button.measurement-plot__legend-item:hover {
margin-bottom: 9px;
}

.workbench .profile-strip {
margin: 0;
}

.panel-select-row {
min-height: 40px;
display: grid;
Expand Down Expand Up @@ -5485,23 +5412,6 @@ button.measurement-plot__legend-item:hover {
border-top: 1px solid var(--line);
}

.preset-options {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 6px;
}

.preset-options button {
min-height: 36px;
padding-inline: 5px;
border-color: var(--line-soft);
background: var(--surface-soft);
}

.preset-options button.is-active {
border-color: color-mix(in srgb, var(--accent) 38%, var(--line));
background: var(--accent-soft);
}

.periodic-settings {
padding: 0;
}
Expand Down Expand Up @@ -7027,49 +6937,6 @@ button.measurement-plot__legend-item:hover {
cursor: default;
}

.profile-settings {
padding: 0;
}

.profile-settings > summary {
min-height: 48px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 0 16px;
list-style: none;
color: var(--text);
font-size: 12px;
font-weight: 600;
cursor: pointer;
}

.profile-settings > summary::-webkit-details-marker {
display: none;
}

.profile-settings > summary::after {
content: "+";
margin-left: auto;
color: var(--quiet);
font-family: var(--numeric);
}

.profile-settings[open] > summary::after {
content: "−";
}

.profile-settings > summary small {
color: var(--quiet);
font-size: 11px;
font-weight: 400;
}

.profile-settings .preset-options {
padding: 0 16px 14px;
}

.atom-display-settings .vector-scale-row {
border-top: 1px solid var(--line);
}
Expand Down
1 change: 0 additions & 1 deletion pqviewer/static/assets/index-B9KxOlif.css

This file was deleted.

1 change: 1 addition & 0 deletions pqviewer/static/assets/index-CQmxkr4B.css

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions pqviewer/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<meta name="description" content="PQ molecular trajectory viewer" />
<link rel="icon" href="/pq-logo.png" type="image/png" />
<title>PQViewer</title>
<script type="module" crossorigin src="/assets/index-B5Kg_efE.js"></script>
<script type="module" crossorigin src="/assets/index-Dtn8HXMR.js"></script>
<link rel="modulepreload" crossorigin href="/assets/react-BuGNL1hR.js">
<link rel="modulepreload" crossorigin href="/assets/three-B9jy988d.js">
<link rel="modulepreload" crossorigin href="/assets/publication-D6BtQUrp.js">
<link rel="stylesheet" crossorigin href="/assets/index-B9KxOlif.css">
<link rel="stylesheet" crossorigin href="/assets/index-CQmxkr4B.css">
</head>
<body>
<div id="root"></div>
Expand Down
Loading