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
9 changes: 9 additions & 0 deletions .issueflows/03-solved-issues/issue184_original.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# #184 — update plots when cells modal closes

URL: https://github.com/cellpy/cellpy-simple-gui/issues/184

It is not needed to update the plots immediately when we change a value on the
cells modal. Also, the updates seem to be in a queue and then done one-by-one,
so that the figures keeps updating long after modal is closed. Either only
update when modal closes, or figure out a better way to prevent updates to
happen long after modal is closed.
51 changes: 51 additions & 0 deletions .issueflows/03-solved-issues/issue184_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Plan — #184 update plots when the Manage cells modal closes

## Goal

Editing in the **Manage cells** modal must not trigger a plot round-trip per
edit; the current tab's figure refreshes **once, when the modal closes**. And
no figure may keep re-rendering after the user is done: a plot response that
has been superseded by a newer request is dropped, never drawn.

## What happens today

- Every edit in the modal (`updateCell` / `selectAll` / `removeCell`) calls
`replotCurrent()` straight away. Each is a server round-trip; the modal is
still covering the chart, so the work is invisible and only adds latency.
- Nothing serialises or de-duplicates those requests. Each response arrives
later and calls `Plotly.react` on arrival, so after N quick edits the chart
visibly redraws N times, long after the last edit (and after the modal has
closed). `_withPlotBusy` also clears the spinner when the *first* response
lands, while later ones are still in flight.

## Approach (front-end only, `app.js` + modal markup)

1. **Defer while the modal is open.** Route every "the library changed, redraw"
call through one helper, `_replotAfterEdit()`. While `cellsManagerOpen` it
only sets `_replotOnClose = true`; otherwise it replots as before (sidebar
edits stay immediate). `closeCellsManager()` replots once if the flag is set.
`clearAll()` closes the modal itself and replots directly (unchanged).
2. **Drop stale responses.** Per chart (`summary` / `cycles` / `cell`) keep a
request sequence number. Each plot call takes the next number before the
fetch and, when the response arrives, only draws if it is still the latest.
Superseded responses are discarded without touching Plotly. Busy state
becomes a counter so the spinner stays up until the *last* in-flight request
for that chart settles.
3. A one-line note in the modal footer: "Plots refresh when this dialog
closes." so the deferred behaviour is not mistaken for a broken edit.

## Files to touch

- `src/cellpy_simple_gui/web/static/js/app.js`
- `src/cellpy_simple_gui/web/templates/index.html` (modal footer note)
- `tests/test_gui_playwright.py` — e2e: edits in the modal issue no
`/api/plots/*` requests; one is issued on close (skips without Playwright,
like the existing e2e tests).
- `.issueflows/04-designs-and-guides/manage-cells-modal.md` — behaviour note.

## Test strategy

- Playwright e2e (local): count `/api/plots/summary` requests while editing in
the modal (expect 0) and after Close (expect 1).
- Manual browser walkthrough in `--server` mode (recording).
- Full `uv run pytest` green; essential CI check green.
32 changes: 32 additions & 0 deletions .issueflows/03-solved-issues/issue184_status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Status — #184 update plots when the Manage cells modal closes

- [x] Done

Branch: `cursor/184-defer-replot-cells-modal-713a` (stacked on
`cursor/186-y-ranges-group-avg-spread-713a`, PR #189, because merging from
this environment is not possible — read-only GitHub token).

## What's done

- `app.js`: `_replotAfterEdit()` defers the redraw while `cellsManagerOpen`
(flag `_replotOnClose`); `closeCellsManager()` redraws once. `updateCell`,
`selectAll`, `selectGroup`, `removeCell` use it; sidebar edits unchanged.
- `app.js`: per-chart request sequence (`_plotSeq`) via `_fetchFigure` — a
response overtaken by a newer request is dropped, never drawn
(`summary` / `cycles` / `cell` incl. compare). `plotBusy` backed by an
in-flight counter so the spinner stays up until the last request settles.
- Modal footer hint ("Plots refresh when this dialog closes." → "Edits saved —
…" once something changed); `.mgr-foot-hint` style.
- e2e (`tests/test_gui_playwright.py::test_cells_modal_defers_plot_refresh_until_close`):
label + group + none/all edits in the modal issue **0** `/api/plots/*`
requests; Close issues exactly **1**; reopen/close without edits issues none.
Fails on the previous JS, passes now. Whole e2e module green locally
(Playwright + Chromium installed for this session; CI still runs
`-m essential` only).
- Manual walkthrough in `--server` mode recorded:
`/opt/cursor/artifacts/cells_modal_deferred_replot_184.mp4`.
- Design note added to `04-designs-and-guides/manage-cells-modal.md`.

## Remaining work

- None.
17 changes: 17 additions & 0 deletions .issueflows/04-designs-and-guides/manage-cells-modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@
(`POST /api/export/cells?fmt=cellpy|csv|xlsx`). One cellpy/xlsx file is returned bare;
csv (multi-file) and multi-cell exports are zipped. Reuses `download()` (desktop Save As).

## Plot refresh timing (issue #184)

- Edits made **inside the modal** do not redraw the chart. `updateCell` /
`selectAll` / `removeCell` / `selectGroup` go through `_replotAfterEdit()`,
which only flags `_replotOnClose` while `cellsManagerOpen`; `closeCellsManager()`
then calls `replotCurrent()` once. The footer says so ("Plots refresh when
this dialog closes." / "Edits saved — …"). Sidebar edits still redraw at once.
- **Stale plot responses are dropped.** Each chart (`summary` / `cycles` /
`cell`) carries a request sequence (`_plotSeq`); `_fetchFigure` returns `null`
for a response overtaken by a newer request, so a burst of edits no longer
replays every intermediate figure. `plotBusy` is backed by an in-flight
counter (`_plotInflight`), so the spinner stays until the last request settles.
- Alternatives considered: debouncing per-edit requests (still redraws while
the modal covers the chart; timing-dependent) and cancelling in-flight
fetches with `AbortController` (the server would keep computing the figure
anyway; sequence numbers give the same visible result with less machinery).

## UI location

- Markup: `web/templates/index.html` (modal after `.layout`)
Expand Down
Loading
Loading