Skip to content

Increased GUI font sizes#74

Closed
amichaellu wants to merge 24 commits into
gui-fontfrom
develop
Closed

Increased GUI font sizes#74
amichaellu wants to merge 24 commits into
gui-fontfrom
develop

Conversation

@amichaellu

Copy link
Copy Markdown
Collaborator

Increased default GUI font sizes - tried adding settings dropdown with clickable font increase/decrease feature, but it was worse than zooming in/out on Google Chrome.

amichaellu and others added 24 commits July 1, 2026 13:07
"Sensor visualization unavailable.
The HDF5 file for this shot has not been fetched yet.
Use the pull panel in the left sidebar to fetch the data."
- "coils" header → "Coils"
- Removed the "coils" overlay checkbox
- Added a "DIII-D first wall" legend label in 3D viewer
Every Plotly figure gets a hover toolbar to save the plot as PNG (2× raster)
or SVG (vector — prints to PDF from any viewer), and node-backed plots get a
"Data" button that downloads that node's arrays as a self-describing HDF5 file.

- service/export.py: node_to_hdf5() — one generic serializer mapping each `kind`
  (contour/heatmap/scatter2d/line/equilibrium/metrics) to datasets + root attrs
  (kind/title/axes/shot/node_id/params), with a fallback dump for unknown kinds.
- service/app.py: GET /api/node/{shot}/{node_id}/download streams the file
  (application/x-hdf5 attachment); forwards + records the view query params so a
  download matches what's on screen. 404/422 mapping mirrors the node route.
- Plot.tsx: hover toolbar (PNG/SVG via Plotly.downloadImage + optional Data link),
  new exportName/download props. Image export works on every plot for free.
- api.ts nodeDownloadUrl(); NodeView forwards the props (metrics panel gets a data
  link). Tabs thread the download descriptor into node-backed plots; client-derived
  plots point their Data button at the source node they're computed from.

Tests: serializer per-kind + every builder downloads valid HDF5 (backend);
nodeDownloadUrl + metrics data-link (frontend).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
kappa (elongation) and other EFIT-tree signals came back "no data" on the
toksearch/remote backend while PTDATA channels succeeded. Root cause: the remote
backend runs the cluster interpreter directly (no conda activate / module load),
so the MDSplus tree-path env EFIT needs is never set and a local `MdsSignal` tree
open fails for every candidate. PTDATA is local and needs no env, so it worked.

Fix: don't use `MdsSignal` for tree signals. The cluster is on-network, so open a
direct mdsip connection to the device's server (network.mdsip → atlas.gat.com:8000)
and reuse the mdsthin path's proven openTree->get fetch — the same server the
mdsthin backend already resolves kappa through. A connection failure degrades to
not-ok Channels so the PTDATA pull still completes.

- _fetch_toksearch: drop the MdsSignal pipeline registration/resolution; take a
  `server` arg and delegate tree signals to the new _toksearch_tree_channels.
- fetch_shot passes the resolved mdsip server through.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Optional pre-gate 2-D Gaussian blur of the rotating spectrogram so contiguous
coherent structure survives aggressive gating: smooth_spectrogram (power +
coherence) and smooth_mode_spectrogram (n-map quality + amplitude) in core, run
before denoise_spectrogram and applied consistently across the spectrogram /
coherence / n-spectrum / n-map nodes.

Two fixes make it actually visible:

- σ in grid cells, not ms/kHz. Physical widths mapped to sub-bin σ on the coarse
  STFT grid (2 ms slice → 0.5 kHz bins; time-decimated columns), so the defaults
  rounded to a near-no-op and moving the sliders did nothing. Cells are one STFT
  bin, resolution-relative, and can't collapse below a bin. The GUI shows the
  physical equivalent per live grid (e.g. "3.0 cells ≈ 6.0 ms") via the new pure
  medianStep helper. Params: smooth_t_cells / smooth_f_cells.

- Smooth LINEAR power (arithmetic mean), not log power. A log/geometric mean is
  dominated by the darkest cell, so a punched-out ridge gap stayed dark and never
  survived the gate. Linear averaging is pulled toward the bright cells, so a
  ridge bleeds into and fills its dim neighbours — the whole point of the pass.

Contract (CONTRACT.md) + plan docs updated. Tests: core smooth incl. ridge-gap
reinforcement, node smoke/no-op, medianStep. Full suite 222 passed / 1 skipped;
frontend tsc + 26 vitest green; ruff clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add an `api` entry to .claude/launch.json (uvicorn on :8000, --reload) so the
FastAPI backend can be launched alongside the web dev server. Re-resolve uv.lock
(drop the stale exclude-newer pin; loosen a pexpect marker).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Plotly's image exporter paints the plot margins with paper_bgcolor,
which is transparent on screen so it blends with the panel. On export
that transparency flattened to grey in most viewers. Temporarily set
paper_bgcolor to the live --panel color (white in light mode, near-black
in dark) around the download and restore it after; the swap is invisible
on screen since the plot already sits on that color.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The HDF5 writer call sat outside the download route's try/except, so a node
that built cleanly (200 on /api/node) could still 500 the download with a raw
stack trace if serialization failed. Wrap it and map any writer error to a
clean, logged 500.

Also switch scatter2d label/group datasets from fixed-width "S" to utf-8 vlen
strings: a φ/θ/µ-decorated sensor label would raise UnicodeEncodeError under
"S". Adds regression tests for both and corrects the _num_array docstring,
which claimed the object-dtype fallback "still succeeds" (h5py can't store it).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
feat(gui): per-node plot image + HDF5 data export
The offline suite reached _toksearch_tree_channels only through its two
early returns and exercised the shared _mdsthin_tree_channels via a
single-candidate stub, so the code this PR actually adds -- the wrapper's
host normalization, the MDSplus->mdsthin import fallback, and the
connection-failure degradation branch -- plus the multi-candidate
fallthrough that production always uses for kappa were untested.

Add tests that inject a fake Connection to drive the wrapper's success
path (asserting host normalization), the mdsthin fallback when MDSplus is
absent, and graceful degradation to not-ok Channels when connect() raises;
plus _mdsthin_tree_channels coverage for second-candidate-wins (on both
degenerate data and open failure), degenerate/non-finite-time rejection,
and client-side window trim.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…fetch

fix(fetch): fetch EFIT tree signals via mdsip on the toksearch backend
…name

The coils cleanup replaced the generic `meta.coils` overlay (which renders
whatever coil sets any device config supplies) with a default-on check against
the literal sensor-set name "All 3D Coils" — a DIII-D-only string. On any other
device (e.g. NSTX-U, whose coils exist only as `meta.coils`, with no coil-kind
sensor set) coils no longer rendered by default and had no toggle at all.

Restore the generic overlay: bring back the `showCoils` state, the 2D footprint
and 3D-loop render blocks, the overlays checkbox, and the `showCoils` memo deps;
remove the `"All 3D Coils"` magic default. This also un-orphans the `CoilSet`
interface and `GeoMeta.coils` field (dead after the readers were removed).

The PR's other changes are kept: the pre-fetch 404 message, the per-loop tilt
saddle-loop orientation, the wall legend proxy, and the label capitalizations.

Verified with Playwright against DIII-D 184927 (fetched live): coils render in
2D + 3D, checkbox on by default, and unchecking removes the coil traces.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
SensorsTab additions: adding pre-fetch message, sensor array checkbox cleanup
Follow-up patch to the 2-D Gaussian pre-smoothing in this feature: it
blurred *linear* power, which haloed bright ridges and, viewed on the log
heatmap, *raised* the visible log-variance instead of lowering it — so the
blur was effectively invisible. Smooth log10 power (then map back) so the
blur is uniform across the dynamic range and matches both the display and
the log-power gate; coherence stays linear. Real-shot log-variance now
drops monotonically with sigma.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ooth test

- app.py: drop the raw exception text from the /download 500 body (keep it in
  logger.exception) so an h5py/OSError message can't leak to the client
- contract.ts: type HeatmapNode.z as (number | null)[][] to match the
  denoise-gated cells the server now emits as null
- test_nodes.py: assert the smoothed spectrogram z actually differs from the
  baseline, so a no-op regression in the smooth-param wiring fails
The contract change typed HeatmapNode.z as (number | null)[][] to match the
denoise-gated cells the server emits as null. tsc -b (strict project build)
then flagged three RotatingTab consumers that assumed number[][]:

- cursor-column power trace: null cell → NaN so the log trace gaps there
- live coherence column: coalesce a gated/absent cell to 0
- stripePlot: widen its param to (number | null)[][]; Plotly renders null as gaps
@amichaellu amichaellu closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants