Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b6f9e17
feat: websocket-only frontend for VS Code-style notebook hosts (#281)
sspickle Aug 16, 2026
b4b1757
feat: comm-only frontend for Google Colab
sspickle Aug 16, 2026
32dae7b
fix: compound/text/extrusion/pause deadlock forever in Colab — raise …
sspickle Aug 16, 2026
a1dff68
fix: self-healing bootstrap — Colab sometimes drops mid-import display
sspickle Aug 16, 2026
51f6667
fix: retry comm-open from the kernel idle loop
sspickle Aug 16, 2026
f3029f1
fix: comm-open retries must not close previous attempts
sspickle Aug 16, 2026
3a2b741
feat: browser-initiated comm handshake for Colab (kernel target + com…
sspickle Aug 16, 2026
f3d621b
fix: session nonce freezes out zombie bootstrap frames
sspickle Aug 16, 2026
c9bc63a
fix: fallback opens carry the session nonce too
sspickle Aug 16, 2026
32e1647
fix: load the Colab bootstrap as an external CDN script
sspickle Aug 16, 2026
1fd6455
fix: drop the idle-loop comm retry — it made Colab drop display outputs
sspickle Aug 16, 2026
57298f4
fix: never display from import — Colab drops import-time display output
sspickle Aug 16, 2026
3dc277a
fix: self-heal displays the bootstrap once, not per unconnected cell
sspickle Aug 16, 2026
1a0501b
fix: show() injects the CDN bootstrap dynamically
sspickle Aug 16, 2026
c7d7b4b
feat: scene replay on every frontend attach
sspickle Aug 16, 2026
f299def
fix: cache-bust the evolving frontend files with the session nonce
sspickle Aug 16, 2026
dd5ca1a
fix: backfill the journal with cmds emitted before the frontend imported
sspickle Aug 17, 2026
ab0123d
fix: follow-up cmds (title/caption) must not clobber constructors in …
sspickle Aug 17, 2026
48b25c4
fix: journal copies constructors at replay time, not record time
sspickle Aug 17, 2026
8475feb
fix: replay cmds in original emission order (dim-scene bug)
sspickle Aug 17, 2026
d534f6b
feat: VPYTHON_WS_PORT pins the websocket port
sspickle Aug 17, 2026
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
Prev Previous commit
Next Next commit
fix: journal copies constructors at replay time, not record time
canvas enriches its cmd dict AFTER appendcmd; a record-time copy ships a
bare canvas and glow renders its dim default stage. Store the live
reference; copy at read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G7y9rTA1r8r8EhEnPSQenR
  • Loading branch information
sspickle and claude committed Aug 17, 2026
commit 48b25c45685491cf7ca57647c9c9f6717bd32a2c
9 changes: 6 additions & 3 deletions vpython/_scene_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,20 @@ def record_cmd(self, cmd):
if cmd.get('cmd') is None:
# Follow-up on an existing object (title/caption/...): same idx
# as its constructor — must NOT clobber it.
self._extras.append(dict(cmd))
self._extras.append(cmd)
return
if idx not in self._cmds:
self._order.append(idx)
self._cmds[idx] = dict(cmd)
# Store the LIVE reference, copy at replay time: constructors are
# enriched after appendcmd (canvas adds its attrs afterwards), and a
# record-time copy ships a bare canvas (observed: dim default scene).
self._cmds[idx] = cmd

def record_attr(self, idx, attr):
self._dirty.add((idx, attr))

def constructors(self):
return [self._cmds[i] for i in self._order]
return [dict(self._cmds[i]) for i in self._order]

def dirty_attrs(self):
return set(self._dirty)
Expand Down
13 changes: 9 additions & 4 deletions vpython/test/test_scene_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ def test_records_constructors_in_creation_order():
assert [c['cmd'] for c in cmds] == ['canvas', 'sphere', 'box']


def test_constructor_copies_are_isolated_from_caller_mutation():
def test_late_enriched_constructor_keys_are_included_at_replay():
# canvas builds its cmd incrementally AFTER appendcmd; the journal must
# reflect the enriched dict, not a bare record-time snapshot.
j = SceneJournal()
cmd = {'cmd': 'sphere', 'idx': 2}
cmd = {'cmd': 'canvas', 'idx': 1}
j.record_cmd(cmd)
cmd['radius'] = 99
assert 'radius' not in j.constructors()[0]
cmd['ambient'] = [0.2, 0.2, 0.2]
assert j.constructors()[0]['ambient'] == [0.2, 0.2, 0.2]
# but the returned list is still a copy: mutating it is inert
j.constructors()[0]['hacked'] = True
assert 'hacked' not in j.constructors()[0]


def test_delete_removes_object_and_its_dirty_attrs():
Expand Down