Skip to content

fix: anywidget refresh value#9454

Open
Shamik-07 wants to merge 25 commits into
marimo-team:mainfrom
Shamik-07:fix/anywidget_refresh_value
Open

fix: anywidget refresh value#9454
Shamik-07 wants to merge 25 commits into
marimo-team:mainfrom
Shamik-07:fix/anywidget_refresh_value

Conversation

@Shamik-07
Copy link
Copy Markdown
Contributor

@Shamik-07 Shamik-07 commented May 4, 2026

📝 Summary

The Counter anywidget was always defaulting to a default value of 5 upon reloading the notebook as the state wasn't being reattached to the FE. This fixes the issue by sending an echo_update to BE and upon reload reconnects to get the correct state. FE calls reemitState ensure that all listeners receive the state even if one was rendered before the other.
Closes #9420

Screen.Recording.2026-05-04.at.17.29.29.mov

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

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

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 22, 2026 9:05pm

Request Review

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="marimo/_plugins/ui/_impl/comm.py">

<violation number="1" location="marimo/_plugins/ui/_impl/comm.py:123">
P3: Update `_create_model_message` docstring: it still says `echo_update` is skipped, but this branch now emits a `ModelUpdate`.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant UI as Widget UI (FE)
    participant Plugin as AnyWidgetPlugin
    participant Model as Frontend Model
    participant Comm as Marimo Comm (BE)
    participant Kernel as Python Kernel

    Note over UI, Kernel: Phase 1: State Capture (During Interaction)

    UI->>Model: model.set("count", 8)
    Model->>Comm: sendUpdate({ method: "echo_update", state: {count: 8} })
    
    rect rgb(240, 240, 240)
        Note over Comm: CHANGED: Handle "echo_update"
        Comm->>Comm: Create ModelUpdate message
        Note right of Comm: Prevents dropping state<br/>on acknowledgement path
    end
    
    Comm-->>Kernel: Broadcast state to all clients

    Note over UI, Kernel: Phase 2: State Hydration (Upon Notebook Reload)

    Kernel->>Comm: Replay stored state
    Comm->>Model: updateAndEmitDiffs({ count: 8 })
    
    Plugin->>Plugin: bind(widgetDef, model)
    Plugin->>UI: render(element, model)
    
    alt Widget Initialization
        UI->>Model: model.on("change:count", listener)
        Note left of UI: Widget defaults to "5"<br/>(Internal initial state)
    end

    rect rgb(240, 240, 240)
        Note over Plugin, Model: NEW: Re-hydration Flow
        Plugin->>Model: getMarimoInternal(model).reemitState()
        
        loop For each key in state
            Model->>UI: emit("change:count", 8)
        end
        
        Model->>UI: emit("change")
    end

    UI->>UI: listener updates UI to "8"
Loading

Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.
Fix all with cubic.

Comment thread marimo/_plugins/ui/_impl/comm.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes anywidget model state hydration on browser refresh/reconnect by ensuring frontend-originated state changes are preserved for session replay and by re-emitting the current model state after a widget view attaches listeners.

Changes:

  • Backend: treat echo_update messages as ModelUpdate so they are included in replayable model state.
  • Frontend: add an internal reemitState() API to re-dispatch current model values as change:* events after render().
  • Tests: add backend and frontend test coverage for echo_update replay contribution and late listener hydration.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
marimo/_plugins/ui/_impl/comm.py Converts echo_update into ModelUpdate to preserve state for reconnect replay.
tests/_plugins/ui/_impl/test_comm.py Adds coverage ensuring echo_update is broadcast as a state-bearing update.
frontend/src/plugins/impl/anywidget/model.ts Adds internal reemitState() that emits change:* events for current state.
frontend/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx Calls reemitState() after render() so late listeners observe hydrated state.
frontend/src/plugins/impl/anywidget/__tests__/model.test.ts Tests reemitState() emits field and aggregate change events.
frontend/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx Adds regression test for hydration when listener attaches after initial state is set.

Comment on lines 122 to +126
elif method == "echo_update":
# echo_update is for multi-client sync acknowledgment, skip it
return None
# Preserve frontend-driven trait changes for reconnect replay.
# anywidget/ipywidgets can emit echo_update as the synchronisation
# acknowledgement path; dropping it causes stale replay state.
return ModelUpdate(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Agreed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hello @manzt

If you could take a look at this, before it becomes stale, that would be great.

Thank you

@mscolnick mscolnick changed the title Fix/anywidget refresh value fix: anywidget refresh value May 15, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Bundle Report

Changes will increase total bundle size by 140 bytes (0.0%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
marimo-esm 25.26MB 140 bytes (0.0%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: marimo-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/index-*.js 28 bytes 431.41kB 0.01%
assets/dist-*.js -33 bytes 104 bytes -24.09%
assets/dist-*.js 74 bytes 176 bytes 72.55% ⚠️
assets/dist-*.js 19 bytes 183 bytes 11.59% ⚠️
assets/dist-*.js -5 bytes 164 bytes -2.96%
assets/dist-*.js 299 bytes 403 bytes 287.5% ⚠️
assets/dist-*.js -172 bytes 104 bytes -62.32%
assets/dist-*.js 65 bytes 169 bytes 62.5% ⚠️
assets/dist-*.js -46 bytes 137 bytes -25.14%
assets/dist-*.js 131 bytes 387 bytes 51.17% ⚠️
assets/dist-*.js -46 bytes 137 bytes -25.14%
assets/dist-*.js -218 bytes 169 bytes -56.33%
assets/dist-*.js 166 bytes 335 bytes 98.22% ⚠️
assets/dist-*.js 46 bytes 183 bytes 33.58% ⚠️
assets/dist-*.js -233 bytes 102 bytes -69.55%
assets/dist-*.js -147 bytes 256 bytes -36.48%
assets/dist-*.js 116 bytes 276 bytes 72.5% ⚠️
assets/dist-*.js -16 bytes 160 bytes -9.09%
assets/state-*.js 112 bytes 24.9kB 0.45%

Files in assets/index-*.js:

  • ./src/plugins/impl/anywidget/AnyWidgetPlugin.tsx → Total Size: 6.1kB

Files in assets/state-*.js:

  • ./src/plugins/impl/anywidget/model.ts → Total Size: 7.78kB

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.

anywidget model.get() returns default value in render() on browser refresh

3 participants