Skip to content

fix: restore view state after present-mode callbacks - #10452

Merged
mscolnick merged 1 commit into
marimo-team:mainfrom
peter-gy:ptr/better-present-mode-lifecycle
Aug 4, 2026
Merged

mscolnick merged 1 commit into
marimo-team:mainfrom
peter-gy:ptr/better-present-mode-lifecycle

Conversation

@peter-gy

@peter-gy peter-gy commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

runDuringPresentMode could leave the app in present when its callback rejected. Calls from read, home, or gallery could also restore the app to edit, so the export dialog (#10429) maintained its own restoration logic.

This change makes runDuringPresentMode own the full lifecycle. It transitions from edit to present, restores the captured view state in finally, and runs callbacks directly in every other mode. Focused tests cover success, rejection, present mode, and non-edit modes.

Copilot AI review requested due to automatic review settings August 4, 2026 06:09
@peter-gy peter-gy added the bug Something isn't working label Aug 4, 2026
@vercel

vercel Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview Aug 4, 2026 6:12am

Request Review

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes view-mode restoration issues when running callbacks in present mode by making runDuringPresentMode fully responsible for transitioning into/out of present mode and reliably restoring the prior view state even on rejection. This removes duplicated/restoration logic from callers (notably the export dialog) and adds focused unit tests for the lifecycle behavior across modes.

Changes:

  • Update runDuringPresentMode to only transition when starting from edit, and to always restore the captured viewState in a finally block.
  • Add unit tests covering success, rejection, already-present mode, and non-edit modes (read, home, gallery).
  • Simplify export dialog screenshot capture by removing manual view-state restoration logic and relying on runDuringPresentMode.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
frontend/src/core/mode.ts Makes runDuringPresentMode own the present-mode lifecycle with reliable state restoration.
frontend/src/core/tests/mode.test.ts Adds vitest coverage for present-mode lifecycle behavior across success/error and different modes.
frontend/src/components/editor/actions/export-dialog/use-export-dialog.ts Removes redundant view-state capture/restore logic and delegates lifecycle to runDuringPresentMode.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 43.84kB (0.17%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
marimo-esm 25.56MB 43.84kB (0.17%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: marimo-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/dist-*.js 5 bytes 169 bytes 3.05%
assets/dist-*.js 62 bytes 164 bytes 60.78% ⚠️
assets/dist-*.js 198 bytes 335 bytes 144.53% ⚠️
assets/dist-*.js -40 bytes 137 bytes -22.6%
assets/dist-*.js -283 bytes 104 bytes -73.13%
assets/dist-*.js -39 bytes 137 bytes -22.16%
assets/dist-*.js -234 bytes 169 bytes -58.06%
assets/dist-*.js 152 bytes 256 bytes 146.15% ⚠️
assets/dist-*.js 7 bytes 176 bytes 4.14%
assets/dist-*.js 128 bytes 387 bytes 49.42% ⚠️
assets/dist-*.js -35 bytes 102 bytes -25.55%
assets/dist-*.js -231 bytes 104 bytes -68.96%
assets/dist-*.js 23 bytes 183 bytes 14.37% ⚠️
assets/dist-*.js -96 bytes 160 bytes -37.5%
assets/dist-*.js 220 bytes 403 bytes 120.22% ⚠️
assets/dist-*.js 73 bytes 177 bytes 70.19% ⚠️
assets/dist-*.js -65 bytes 104 bytes -38.46%
assets/dist-*.js 155 bytes 259 bytes 149.04% ⚠️
assets/add-*.js 674 bytes 55.23kB 1.24%
assets/file-*.js 869 bytes 54.37kB 1.62%
assets/useNotebookActions-*.js -48 bytes 60.91kB -0.08%
assets/components-*.js 42.35kB 54.12kB 359.7% ⚠️
assets/config-*.js -7 bytes 7.55kB -0.09%
assets/ttcn-*.js 7 bytes 64 bytes 12.28% ⚠️
assets/ttcn-*.js -7 bytes 57 bytes -10.94%

Files in assets/useNotebookActions-*.js:

  • ./src/components/editor/actions/export-dialog/use-export-dialog.ts → Total Size: 10.2kB

Files in assets/config-*.js:

  • ./src/core/mode.ts → Total Size: 2.09kB

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Architecture diagram
sequenceDiagram
    participant Export as Export Dialog
    participant Mode as runDuringPresentMode()
    participant State as Jotai Store (viewStateAtom)
    participant Render as Page Render

    Note over Export,Render: NEW: Unified lifecycle ownership in runDuringPresentMode

    Export->>Mode: capturePNG() → runDuringPresentMode(capture)

    Mode->>State: get viewStateAtom
    State-->>Mode: { mode, cellAnchor }

    alt mode === "edit"
        Mode->>State: set({ ...state, mode: "present" })
        Mode->>Render: setTimeout(100ms)
        Render-->>Mode: render complete
        Mode->>Render: requestAnimationFrame (frame 1)
        Mode->>Render: requestAnimationFrame (frame 2)
        Render-->>Mode: two frames rendered
        Mode->>Export: run callback fn()
        Export->>Export: capture current app view
        Export-->>Mode: callback complete (resolve or reject)
        alt callback rejects
            Mode->>Mode: catch error
            Mode->>State: finally: set(original state)
            Mode-->>Export: re-throw error
        else callback resolves
            Mode->>State: finally: set(original state)
            Mode-->>Export: void
        end
    else mode === "present"
        Mode->>Export: run callback fn() directly
        Export->>Export: capture current app view
        Export-->>Mode: callback complete
        Mode-->>Export: void
    else mode in ["read", "home", "gallery"]
        Mode->>Export: run callback fn() directly
        Export->>Export: capture current app view
        Export-->>Mode: callback complete
        Mode-->>Export: void
    end
Loading

Re-trigger cubic

@peter-gy
peter-gy requested a review from mscolnick August 4, 2026 06:18
@mscolnick
mscolnick merged commit 8d2bc6e into marimo-team:main Aug 4, 2026
31 checks passed
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.17-dev13

williambdean pushed a commit to williambdean/marimo that referenced this pull request Sep 6, 2026
`runDuringPresentMode` could leave the app in `present` when its
callback rejected. Calls from `read`, `home`, or `gallery` could also
restore the app to `edit`, so the export dialog (marimo-team#10429) maintained its
own restoration logic.

This change makes `runDuringPresentMode` own the full lifecycle. It
transitions from `edit` to `present`, restores the captured view state
in `finally`, and runs callbacks directly in every other mode. Focused
tests cover success, rejection, present mode, and non-edit modes.

This branch was successfully deployed

1 active deployment
Preview — a13abd4f Deployed Aug 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants