fix(code): make /timestamps toggle instant via per-footer class (#4095)

Speed up the `/timestamps` toggle so it applies instantly, even on long
conversations.

---

The `/timestamps` command shows or hides a timestamp footer under each
chat message. The original toggle was slow on long conversations because
it mounted footers lazily: turning timestamps on walked every message,
recomputed `messages.children` and ran an `index()` lookup per message
(O(n²) overall), then toggled a `message-timestamps-visible` class on
the `#messages` container.

That container class was itself a cost. The visibility selector was
keyed off `#messages`, so flipping it forced Textual to re-cascade
styles across the entire message subtree — O(mounted widgets) work on
every toggle.

This moves visibility onto the footers themselves:

- **Footers are always mounted**, built alongside their message on every
path (live messages, restored history, hydrated scrollback). Each is
stamped with a `message-timestamp-footer-visible` class at build time
when the preference is on, so restored and streamed footers render
immediately without waiting for a toggle.
- **Toggling no longer mounts or removes anything.** `/timestamps` flips
the visible class on the already-mounted footer leaves inside a single
`batch_update()`. Because the selector is now compound on the leaf
rather than a descendant of `#messages`, only the footers restyle — not
the whole subtree.

The result is a single, bounded layout pass per toggle regardless of
conversation length, with no per-message `index()` scan.

Made by [Open
SWE](https://openswe.vercel.app/agents/ab4b0f22-d55a-9ff6-0d11-30183d52ac5a)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Mason Daugherty
2026-06-20 19:20:15 -04:00
committed by GitHub
parent 5e337ae50a
commit 7ae32b0a60
3 changed files with 303 additions and 82 deletions
+54 -73
View File
@@ -115,6 +115,16 @@ deadlock detector when two threads cold-import overlapping modules.
"""
_MESSAGE_TIMESTAMP_FOOTER_CLASS = "message-timestamp-footer"
"""CSS class applied to individual message timestamp footer widgets."""
_MESSAGE_TIMESTAMP_FOOTER_VISIBLE_CLASS = "message-timestamp-footer-visible"
"""CSS class applied to a footer widget when it should be shown.
Visibility is toggled on the footer leaves rather than on `#messages`: a class
change on the container would force Textual to re-cascade styles across every
message subtree (O(mounted widgets)), whereas flipping the leaf footers
restyles only the footers.
"""
_TIMESTAMP_FOOTER_EXCLUDED_TYPES: frozenset[MessageType] = frozenset(
{MessageType.APP, MessageType.SUMMARIZATION}
@@ -133,11 +143,6 @@ def _message_timestamp_footer_id(message_id: str) -> str:
return f"{message_id}-timestamp-footer"
def _is_message_timestamp_footer(widget: Widget) -> bool:
"""Return whether `widget` is a timestamp footer."""
return widget.has_class(_MESSAGE_TIMESTAMP_FOOTER_CLASS)
def _create_model_with_deepagents_import_lock(
model_spec: str | None = None,
*,
@@ -4697,7 +4702,9 @@ class DeepAgentsApp(App):
for widget, msg_data in reversed(hydrated_widgets):
try:
footer = self._build_message_timestamp_footer(msg_data)
footer = self._build_message_timestamp_footer(
msg_data, visible=self._message_timestamps_visible
)
if first_child:
if footer is not None:
await messages_container.mount(footer, before=first_child)
@@ -7907,7 +7914,9 @@ class DeepAgentsApp(App):
nodes: list[Widget] = []
for widget, msg_data in zip(widgets, visible, strict=False):
nodes.append(widget)
footer = self._build_message_timestamp_footer(msg_data)
footer = self._build_message_timestamp_footer(
msg_data, visible=self._message_timestamps_visible
)
if footer is not None:
nodes.append(footer)
await messages_container.mount(*nodes)
@@ -7955,38 +7964,59 @@ class DeepAgentsApp(App):
)
await self._mount_message(AppMessage(f"Could not load history: {e}"))
def _build_message_timestamp_footer(self, data: MessageData) -> Static | None:
"""Build a visible timestamp footer for a message.
@staticmethod
def _build_message_timestamp_footer(
data: MessageData, *, visible: bool
) -> Static | None:
"""Build a timestamp footer for a message.
Args:
data: Message data carrying the timestamp.
visible: Whether the footer should be shown immediately. New
footers built while timestamps are on must carry the visible
class so they render without waiting for a toggle.
Returns:
A footer widget, or `None` when disabled, when the timestamp is
invalid, or when the message type is in
`_TIMESTAMP_FOOTER_EXCLUDED_TYPES`.
A footer widget, or `None` when the message type is in
`_TIMESTAMP_FOOTER_EXCLUDED_TYPES` or when the timestamp is
invalid.
"""
if not self._message_timestamps_visible:
return None
if data.type in _TIMESTAMP_FOOTER_EXCLUDED_TYPES:
return None
label = format_message_timestamp(data.timestamp)
if label is None:
logger.warning("Invalid timestamp for message %s", data.id)
return None
classes = _MESSAGE_TIMESTAMP_FOOTER_CLASS
if visible:
classes = f"{classes} {_MESSAGE_TIMESTAMP_FOOTER_VISIBLE_CLASS}"
return Static(
Content.styled(label, "dim"),
id=_message_timestamp_footer_id(data.id),
classes=_MESSAGE_TIMESTAMP_FOOTER_CLASS,
classes=classes,
)
def _sync_message_timestamps_display(self) -> None:
"""Apply the current visibility to every mounted timestamp footer.
Flips the visible class on the footer leaves directly (not on
`#messages`) so a toggle restyles only the footers rather than
re-cascading the entire message subtree. `batch_update` coalesces the
relayout into a single pass.
"""
footers = self.query(f".{_MESSAGE_TIMESTAMP_FOOTER_CLASS}")
if not footers:
return
with self.batch_update():
footers.set_class(
self._message_timestamps_visible,
_MESSAGE_TIMESTAMP_FOOTER_VISIBLE_CLASS,
)
async def _toggle_message_timestamp_footers(self) -> None:
"""Toggle visible timestamp footers and persist the preference."""
self._message_timestamps_visible = not self._message_timestamps_visible
if self._message_timestamps_visible:
await self._show_message_timestamp_footers()
else:
await self._hide_message_timestamp_footers()
self._sync_message_timestamps_display()
await self._persist_message_timestamps_visible()
async def _persist_message_timestamps_visible(self) -> None:
@@ -8009,57 +8039,6 @@ class DeepAgentsApp(App):
exc_info=True,
)
async def _show_message_timestamp_footers(self) -> None:
"""Insert timestamp footers under mounted message widgets."""
try:
messages = self.query_one("#messages", Container)
except NoMatches:
return
for widget in list(messages.children):
if (
_is_message_timestamp_footer(widget)
or isinstance(widget, QueuedUserMessage)
or not widget.id
):
continue
data = self._message_store.get_message(widget.id)
if data is None:
# Mounted widget without a backing store entry => DOM/store
# desync; skip it but leave a breadcrumb (mirrors pruning).
logger.debug(
"No store entry for mounted widget %s; skipping footer",
widget.id,
)
continue
footer_id = _message_timestamp_footer_id(data.id)
with suppress(NoMatches):
messages.query_one(f"#{footer_id}")
continue
footer = self._build_message_timestamp_footer(data)
if footer is None:
continue
children = list(messages.children)
try:
index = children.index(widget)
except ValueError:
await messages.mount(footer)
continue
next_child = children[index + 1] if index + 1 < len(children) else None
if next_child is not None:
await messages.mount(footer, before=next_child)
else:
await messages.mount(footer)
async def _hide_message_timestamp_footers(self) -> None:
"""Remove all mounted timestamp footers."""
try:
messages = self.query_one("#messages", Container)
except NoMatches:
return
for widget in list(messages.children):
if _is_message_timestamp_footer(widget):
await widget.remove()
async def _mount_message(
self,
widget: Static | AssistantMessage | ToolCallMessage | SkillMessage,
@@ -8101,11 +8080,13 @@ class DeepAgentsApp(App):
# Store message data for virtualization
message_data = MessageData.from_widget(widget)
if not widget.id:
# Keep the widget DOM id == store id so timestamp-footer toggling
# can map a mounted widget back to its MessageData.
# Keep the widget DOM id == store id so pruning can locate a
# mounted widget (and its timestamp footer) from its MessageData.
widget.id = message_data.id
self._message_store.append(message_data)
footer = self._build_message_timestamp_footer(message_data)
footer = self._build_message_timestamp_footer(
message_data, visible=self._message_timestamps_visible
)
await self._mount_before_queued(messages, widget)
if footer is not None:
+10
View File
@@ -22,6 +22,7 @@ Screen {
/* Main content goes on base layer by default */
.message-timestamp-footer {
display: none;
height: auto;
color: $text-muted;
text-style: dim;
@@ -29,6 +30,15 @@ Screen {
padding: 0;
}
/*
* Visibility is toggled per-footer (not via a class on #messages) so a
* `/timestamps` toggle restyles only the footer leaves instead of forcing a
* re-cascade across every message subtree.
*/
.message-timestamp-footer.message-timestamp-footer-visible {
display: block;
}
/* Chat area - main scrollable messages area */
#chat {
height: 1fr;
+239 -9
View File
@@ -4167,7 +4167,7 @@ class TestMessageTimestampFooters:
async def test_toggle_adds_and_removes_footers(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""The `/timestamps` toggle adds and removes visible footer widgets."""
"""The `/timestamps` toggle shows and hides existing footer widgets."""
previous_tz = os.environ.get("TZ")
monkeypatch.setenv("TZ", "UTC")
# Sandbox config so the toggle's persistence starts from "hidden" and
@@ -4187,23 +4187,21 @@ class TestMessageTimestampFooters:
async with app.run_test() as pilot:
await pilot.pause()
await app._mount_message(UserMessage("hello", id="msg-fixed"))
data = app._message_store.get_message("msg-fixed")
assert data is not None
data.timestamp = 1_704_110_405.0
footer = app.query_one("#msg-fixed-timestamp-footer", Static)
assert footer.display is False
await app._toggle_message_timestamp_footers()
await pilot.pause()
footer = app.query_one("#msg-fixed-timestamp-footer", Static)
rendered = footer.render()
assert isinstance(rendered, Content)
assert rendered.plain == "Jan 1, 12:00:05 PM"
assert footer.display is True
await app._toggle_message_timestamp_footers()
await pilot.pause()
with pytest.raises(NoMatches):
app.query_one("#msg-fixed-timestamp-footer", Static)
footer = app.query_one("#msg-fixed-timestamp-footer", Static)
assert footer.display is False
finally:
if previous_tz is None:
monkeypatch.delenv("TZ", raising=False)
@@ -4211,6 +4209,238 @@ class TestMessageTimestampFooters:
monkeypatch.setenv("TZ", previous_tz)
self._sync_tz()
def test_build_footer_formats_timestamp(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Timestamp footer labels are formatted; `visible` stamps the class.
`visible` is the only build-time lever over footer visibility, so it is
asserted in both directions: a footer built with `visible=False` must
not carry the visible class, and one built with `visible=True` must.
"""
from deepagents_code.widgets.message_store import MessageData, MessageType
previous_tz = os.environ.get("TZ")
monkeypatch.setenv("TZ", "UTC")
monkeypatch.setattr(
"deepagents_code.formatting.uses_24_hour_clock", lambda: False
)
self._sync_tz()
app = DeepAgentsApp()
data = MessageData(
type=MessageType.USER,
content="hello",
id="msg-fixed",
timestamp=1_704_110_405.0,
)
try:
footer = app._build_message_timestamp_footer(data, visible=False)
assert footer is not None
rendered = footer.render()
assert isinstance(rendered, Content)
assert rendered.plain == "Jan 1, 12:00:05 PM"
assert not footer.has_class("message-timestamp-footer-visible")
visible_footer = app._build_message_timestamp_footer(data, visible=True)
assert visible_footer is not None
assert visible_footer.has_class("message-timestamp-footer-visible")
finally:
if previous_tz is None:
monkeypatch.delenv("TZ", raising=False)
else:
monkeypatch.setenv("TZ", previous_tz)
self._sync_tz()
async def test_toggle_positions_footer_after_each_message(self) -> None:
"""Message mounting keeps one footer directly after every message."""
from deepagents_code.app import _message_timestamp_footer_id
app = DeepAgentsApp()
async with app.run_test() as pilot:
await pilot.pause()
for index in range(5):
await app._mount_message(UserMessage("hi", id=f"msg-{index}"))
await pilot.pause()
messages = app.query_one("#messages", Container)
children = list(messages.children)
for index in range(5):
message = app.query_one(f"#msg-{index}", UserMessage)
position = children.index(message)
footer = children[position + 1]
assert footer.id == _message_timestamp_footer_id(f"msg-{index}")
async def test_repeated_toggles_do_not_mount_or_remove_footers(self) -> None:
"""Toggling flips visibility only; it never mounts or removes footers."""
app = DeepAgentsApp()
async with app.run_test() as pilot:
await pilot.pause()
await app._mount_message(UserMessage("hi", id="msg-dup"))
await pilot.pause()
assert len(app.query("#msg-dup-timestamp-footer")) == 1
await app._toggle_message_timestamp_footers()
await app._toggle_message_timestamp_footers()
await pilot.pause()
assert len(app.query("#msg-dup-timestamp-footer")) == 1
async def test_toggle_with_no_footers_is_a_noop(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Toggling on an empty thread takes the no-footer early-return safely.
With zero footers mounted, `_sync_message_timestamps_display` queries an
empty `DOMQuery` and returns early. This guards that realistic path
(open app, run `/timestamps` before sending anything) against a crash,
and confirms the visibility flag still flips as the source of truth.
"""
# Sandbox the config so the toggle's persistence never touches the real
# user config on disk.
config = tmp_path / "config.toml"
monkeypatch.setattr("deepagents_code.model_config.DEFAULT_CONFIG_PATH", config)
app = DeepAgentsApp()
async with app.run_test() as pilot:
await pilot.pause()
assert not app.query(".message-timestamp-footer")
before = app._message_timestamps_visible
await app._toggle_message_timestamp_footers()
await pilot.pause()
assert not app.query(".message-timestamp-footer")
assert app._message_timestamps_visible is not before
async def test_footers_visible_on_startup_when_preference_saved(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A saved `visible` preference shows footers on startup without toggling.
Footers are always mounted; new ones built while the preference is on
carry the visible class so they render without waiting for a toggle.
This guards the restart-visibility path.
"""
# Sandbox config with the preference persisted as visible so __init__
# loads `True` and new footers build with the visible class.
config = tmp_path / "config.toml"
config.write_text("[ui]\nshow_message_timestamps = true\n")
monkeypatch.setattr("deepagents_code.model_config.DEFAULT_CONFIG_PATH", config)
app = DeepAgentsApp()
assert app._message_timestamps_visible is True
async with app.run_test() as pilot:
await pilot.pause()
await app._mount_message(UserMessage("hello", id="msg-start"))
await pilot.pause()
footer = app.query_one("#msg-start-timestamp-footer", Static)
assert footer.has_class("message-timestamp-footer-visible")
assert footer.display is True
async def test_footers_render_for_restored_thread_history(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Restoring an old thread builds visible footers for its messages."""
from deepagents_code.app import _ThreadHistoryPayload
from deepagents_code.widgets.message_store import MessageData, MessageType
config = tmp_path / "config.toml"
config.write_text("[ui]\nshow_message_timestamps = true\n")
monkeypatch.setattr("deepagents_code.model_config.DEFAULT_CONFIG_PATH", config)
app = DeepAgentsApp()
async with app.run_test() as pilot:
await pilot.pause()
payload = _ThreadHistoryPayload(
[
MessageData(
type=MessageType.USER,
content="restored",
id="hist-msg",
timestamp=1_704_110_405.0,
),
# Excluded types never receive a footer, even on restore.
MessageData(
type=MessageType.APP,
content="Resumed thread",
id="hist-app",
timestamp=1_704_110_406.0,
),
],
0,
"",
)
await app._load_thread_history(
thread_id="t-restored", preloaded_payload=payload
)
await pilot.pause()
footer = app.query_one("#hist-msg-timestamp-footer", Static)
assert footer.display is True
# Footer sits directly after its message in the DOM.
messages = app.query_one("#messages", Container)
children = list(messages.children)
anchor = app.query_one("#hist-msg", UserMessage)
assert children[children.index(anchor) + 1] is footer
# Excluded-type messages get no footer, even on restore.
with pytest.raises(NoMatches):
app.query_one("#hist-app-timestamp-footer", Static)
async def test_footers_render_for_hydrated_messages_above(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Scroll-up hydration of older messages builds visible footers."""
from deepagents_code.app import _ThreadHistoryPayload
from deepagents_code.widgets.message_store import MessageData, MessageType
config = tmp_path / "config.toml"
config.write_text("[ui]\nshow_message_timestamps = true\n")
monkeypatch.setattr("deepagents_code.model_config.DEFAULT_CONFIG_PATH", config)
app = DeepAgentsApp()
async with app.run_test() as pilot:
await pilot.pause()
# Shrink the window so a small load archives messages above the
# visible range, mirroring a long thread scrolled to the bottom.
monkeypatch.setattr(app._message_store, "WINDOW_SIZE", 2)
payload = _ThreadHistoryPayload(
[
MessageData(
type=MessageType.USER,
content=f"m{index}",
id=f"hist-{index}",
timestamp=1_704_110_400.0 + index,
)
for index in range(4)
],
0,
"",
)
await app._load_thread_history(
thread_id="t-long", preloaded_payload=payload
)
await pilot.pause()
# Older messages start archived (no widget/footer mounted yet).
with pytest.raises(NoMatches):
app.query_one("#hist-0-timestamp-footer", Static)
await app._hydrate_messages_above()
await pilot.pause()
footer = app.query_one("#hist-0-timestamp-footer", Static)
assert footer.display is True
# Footer sits directly after its hydrated message in the DOM.
messages = app.query_one("#messages", Container)
children = list(messages.children)
anchor = app.query_one("#hist-0", UserMessage)
assert children[children.index(anchor) + 1] is footer
async def test_mount_message_adds_footer_when_enabled(self) -> None:
"""New messages receive a footer while timestamps are enabled."""
app = DeepAgentsApp()