From 6eb1ad51c8406ef6d334aade94b4c998f33afcf8 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:18:19 -0400 Subject: [PATCH 01/10] docs: scope original bounded native Content consumption --- .../SCOPE.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 evidence/native-content-height-consumption-20261005/SCOPE.md diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md new file mode 100644 index 0000000000..cbd2eaf0ff --- /dev/null +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -0,0 +1,26 @@ +# Bound native Content formatting at its original render height + +Base: Text68/69 merged c1bcc8d33164553432f2a718eeddec1a1ffa4a5f. + +Existing Content.render_strips calls its original _wrap_and_format owner for +ALL paragraphs / _FormattedLine resources before slicing that result to the +requested height. Visual's render height already declares which prefix is +needed; RichVisual bounds its own segment stream with islice, while Content +constructs the unused suffix first. + +Make the existing Content formatting algorithm an iterator. Migrate its entire +consumer family: complete wrap and height measurement consume all; bounded +strip rendering consumes its required prefix; original negative slicing stays +complete before slicing. Public Visual/render_strips/list and wrap/list +contracts, source selection/link/wide-cell metadata and original Content.split +resource remain unchanged. Delete the unused complete formatted-result list. +No new class, cache, map, timer, flag, alternate renderer or Toad implementation. + +The original recorded59.605 RichVisual stack does NOT identify Content or its +widget/invalidation cause. This independent unused-resource source finding is +not an attribution of that sample, a CPU share or a measured speedup. + +AST complete production/dependency/test caller and override evidence precedes +editing; coherent source change then one affected control batch and joined +installed meaningful frame check. No App/build/installed/SDK/provider/media +purpose exists now. Text68/69 wheel a0c2 and qualification stay frozen. From 71e8c7fd4486b772252dafe7556b91e23a08fab9 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:22:05 -0400 Subject: [PATCH 02/10] perf: consume native formatted lines at their declared render height --- .../before.json | 2075 +++++++++++++++++ src/textual/content.py | 17 +- tests/test_content_measurement_projection.py | 4 +- 3 files changed, 2084 insertions(+), 12 deletions(-) create mode 100644 evidence/native-content-height-consumption-20261005/before.json diff --git a/evidence/native-content-height-consumption-20261005/before.json b/evidence/native-content-height-consumption-20261005/before.json new file mode 100644 index 0000000000..c3d888aecb --- /dev/null +++ b/evidence/native-content-height-consumption-20261005/before.json @@ -0,0 +1,2075 @@ +{ + "roots": [ + { + "repository": "/home/ts/wt/textual-native-subtree-strips-20261002", + "head": "6eb1ad51c8406ef6d334aade94b4c998f33afcf8", + "root": "src/textual", + "parsed": 249, + "omitted": [] + }, + { + "repository": "/home/ts/wt/textual-native-subtree-strips-20261002", + "head": "6eb1ad51c8406ef6d334aade94b4c998f33afcf8", + "root": "tests", + "parsed": 460, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "992162f9b27462017c23d627a424ef732d1fe203", + "root": "src/toad", + "parsed": 288, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "992162f9b27462017c23d627a424ef732d1fe203", + "root": "tests", + "parsed": 397, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "992162f9b27462017c23d627a424ef732d1fe203", + "root": "tools", + "parsed": 41, + "omitted": [] + } + ], + "sites": [ + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1595, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(self, size: Size | None = None) -> list[Strip]:" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1562, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1610, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1611, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1544, + "node": "Attribute", + "name": "render_strips", + "source": "self.render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1566, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1570, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1572, + "node": "Name", + "name": "render_strips", + "source": "render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/_compositor.py", + "line": 1734, + "node": "Attribute", + "name": "render_strips", + "source": "self.render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 634, + "node": "FunctionDef", + "name": "_wrap_and_format", + "source": " def _wrap_and_format(" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 728, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1765, + "node": "FunctionDef", + "name": "to_strip", + "source": " def to_strip(self, style: Style) -> tuple[list[Segment], int]:" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 646, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 661, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 747, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1022, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 696, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 615, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self.without_spans._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 691, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 712, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 763, + "node": "Attribute", + "name": "to_strip", + "source": "line.to_strip" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 686, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/renderables/blank.py", + "line": 27, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/textual", + "path": "src/textual/visual.py", + "line": 131, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/textual", + "path": "src/textual/visual.py", + "line": 324, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/textual", + "path": "src/textual/visual.py", + "line": 391, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/textual", + "path": "src/textual/visual.py", + "line": 227, + "node": "Attribute", + "name": "render_strips", + "source": "visual.render_strips" + }, + { + "root": "src/textual", + "path": "src/textual/visual.py", + "line": 411, + "node": "Attribute", + "name": "render_strips", + "source": "self._visual.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor.py", + "line": 388, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor.py", + "line": 308, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor.py", + "line": 392, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor.py", + "line": 313, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor_frame_damage.py", + "line": 139, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor_horizontal_damage.py", + "line": 46, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_compositor_horizontal_damage.py", + "line": 52, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_content.py", + "line": 303, + "node": "Attribute", + "name": "render_strips", + "source": "content.render_strips" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 16, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "content.without_spans._wrap_and_format" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 17, + "node": "Constant", + "name": "_wrap_and_format", + "source": "\"_wrap_and_format\"" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 25, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "content.without_spans._wrap_and_format" + }, + { + "root": "tests", + "path": "tests/test_frame_transaction.py", + "line": 19, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 146, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 168, + "node": "Attribute", + "name": "render_strips", + "source": "owner._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 197, + "node": "Attribute", + "name": "render_strips", + "source": "owner._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 228, + "node": "Attribute", + "name": "render_strips", + "source": "owner._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 178, + "node": "Attribute", + "name": "render_strips", + "source": "owner._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_removed_scene_ownership.py", + "line": 209, + "node": "Attribute", + "name": "render_strips", + "source": "owner._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_viewport_geometry_targets.py", + "line": 166, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_viewport_geometry_targets.py", + "line": 171, + "node": "Attribute", + "name": "render_strips", + "source": "compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/test_viewport_layout.py", + "line": 22, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/toad_frame_damage_installed_pilot.py", + "line": 33, + "node": "Attribute", + "name": "render_strips", + "source": "self.screen._compositor.render_strips" + }, + { + "root": "src/toad", + "path": "src/toad/visuals/columns.py", + "line": 44, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/patch_diff.py", + "line": 245, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(self, width, height, style, options):" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/patch_diff.py", + "line": 339, + "node": "Attribute", + "name": "render_strips", + "source": "row.render_strips" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/patch_diff.py", + "line": 367, + "node": "Attribute", + "name": "render_strips", + "source": "_DiffRow(self.source, y).render_strips" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/path_search.py", + "line": 46, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/path_search.py", + "line": 69, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "line._wrap_and_format" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/path_search.py", + "line": 83, + "node": "Attribute", + "name": "to_strip", + "source": "line.to_strip" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/prepared_markdown.py", + "line": 144, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/prepared_markdown.py", + "line": 155, + "node": "Attribute", + "name": "render_strips", + "source": "self.content.render_strips" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/throbber.py", + "line": 63, + "node": "FunctionDef", + "name": "render_strips", + "source": " def render_strips(" + }, + { + "root": "tests", + "path": "tests/acp_specification_installed_pilot.py", + "line": 24, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/agent_configuration_native_installed_pilot.py", + "line": 45, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/agent_selection_configured_installed_pilot.py", + "line": 194, + "node": "Attribute", + "name": "render_strips", + "source": "app.workspace_screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/agent_startup_failure_installed_pilot.py", + "line": 49, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/agent_startup_failure_installed_pilot.py", + "line": 54, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/async_thread_open_pilot.py", + "line": 108, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/async_thread_open_pilot.py", + "line": 168, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/blank_presentation_pilot.py", + "line": 381, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/blank_presentation_pilot.py", + "line": 572, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/blank_presentation_pilot.py", + "line": 293, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/block_interaction_pilot.py", + "line": 33, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/canonical_wire_conversation_installed_pilot.py", + "line": 264, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/catalog_actions_native_installed_pilot.py", + "line": 88, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/channel_disclosure_publication_pilot.py", + "line": 235, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/channel_partial_paint_pilot.py", + "line": 75, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/checkpoint_source_pivot_pilot.py", + "line": 92, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/client_session_native_installed_pilot.py", + "line": 50, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/client_session_native_installed_pilot.py", + "line": 49, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/client_session_native_installed_pilot.py", + "line": 61, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/client_session_native_installed_pilot.py", + "line": 74, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/client_session_native_installed_pilot.py", + "line": 102, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/command_family_pilot.py", + "line": 62, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/command_family_pilot.py", + "line": 130, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_pilot.py", + "line": 77, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 30, + "node": "Attribute", + "name": "render_strips", + "source": "self.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 137, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 164, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 172, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 184, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/compaction_summary_stream_native_installed_pilot.py", + "line": 369, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/context_compaction_native_installed_pilot.py", + "line": 20, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/context_native_installed_pilot.py", + "line": 27, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/context_native_installed_pilot.py", + "line": 37, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/context_usage_pilot.py", + "line": 44, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/context_usage_pilot.py", + "line": 65, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/diff_row_damage_pilot.py", + "line": 37, + "node": "Attribute", + "name": "render_strips", + "source": "visual.render_strips" + }, + { + "root": "tests", + "path": "tests/diff_row_damage_pilot.py", + "line": 39, + "node": "Attribute", + "name": "render_strips", + "source": "_DiffRow(visual, y).render_strips" + }, + { + "root": "tests", + "path": "tests/existing_thread_tab_title_pilot.py", + "line": 130, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/file_path_preview_pilot.py", + "line": 128, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/file_path_preview_pilot.py", + "line": 162, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/file_path_preview_pilot.py", + "line": 172, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/file_path_preview_pilot.py", + "line": 183, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/file_preview_latency_pilot.py", + "line": 77, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/first_fork_native_installed_pilot.py", + "line": 300, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/fresh_fork_pilot.py", + "line": 104, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/fresh_fork_pilot.py", + "line": 132, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_details_pilot.py", + "line": 41, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_details_pilot.py", + "line": 45, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_objective_edit_pilot.py", + "line": 127, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_objective_edit_pilot.py", + "line": 112, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_server_poll_pilot.py", + "line": 123, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_server_poll_pilot.py", + "line": 136, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_server_poll_pilot.py", + "line": 144, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_standby_history_pilot.py", + "line": 196, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_standby_history_pilot.py", + "line": 170, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/goal_standby_history_pilot.py", + "line": 183, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/guards/test_goal_interaction.py", + "line": 49, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/guards/test_live_output.py", + "line": 56, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/guards/test_tool_output.py", + "line": 87, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/guards/test_transcript_publication.py", + "line": 150, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/history_availability_installed_pilot.py", + "line": 67, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/history_availability_installed_pilot.py", + "line": 79, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/history_availability_installed_pilot.py", + "line": 72, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/history_frame_publication_pilot.py", + "line": 48, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/in_out_underfill_pilot.py", + "line": 67, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/in_out_underfill_pilot.py", + "line": 95, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/input_history_native_installed_pilot.py", + "line": 23, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/input_history_native_installed_pilot.py", + "line": 27, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/l0a_native_installed_pilot.py", + "line": 45, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/l0a_native_installed_pilot.py", + "line": 542, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/large_stream_pilot.py", + "line": 128, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/large_stream_pilot.py", + "line": 70, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/long_message_pilot.py", + "line": 49, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/long_message_pilot.py", + "line": 52, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/long_message_pilot.py", + "line": 56, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/mcp_decision_pty_pilot.py", + "line": 34, + "node": "Attribute", + "name": "render_strips", + "source": "widget.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/message_dividers_pilot.py", + "line": 336, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_loaded_return_cache_pilot.py", + "line": 64, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_loaded_return_cache_pilot.py", + "line": 459, + "node": "Attribute", + "name": "render_strips", + "source": "frame._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_loaded_return_cache_pilot.py", + "line": 244, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_permission_ui.py", + "line": 70, + "node": "Attribute", + "name": "render_strips", + "source": "self.app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_permission_ui.py", + "line": 80, + "node": "Attribute", + "name": "render_strips", + "source": "self.app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_session_retention_pilot.py", + "line": 91, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_session_retention_pilot.py", + "line": 53, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_tool_output_pilot.py", + "line": 46, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/native_widget_actions_installed_pilot.py", + "line": 30, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/original_turn_resource_real_installed_pilot.py", + "line": 249, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/pending_thread_open_pilot.py", + "line": 52, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/permission_pending_mount_pilot.py", + "line": 112, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/permission_pending_mount_pilot.py", + "line": 75, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/persistent_workspace_source_navigation_pilot.py", + "line": 58, + "node": "Attribute", + "name": "render_strips", + "source": "frame._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/prepared_diff_rows_pilot.py", + "line": 55, + "node": "Attribute", + "name": "render_strips", + "source": "_DiffRow(visual, 0).render_strips" + }, + { + "root": "tests", + "path": "tests/quit_save_installed_pilot.py", + "line": 20, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/reader_geometry_pilot.py", + "line": 30, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/readonly_saved_reader_pilot.py", + "line": 53, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/receiver_inbound_installed_pilot.py", + "line": 16, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/reconnect_source_native_installed_pilot.py", + "line": 50, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/resize_render_pilot.py", + "line": 39, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/resize_render_pilot.py", + "line": 51, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/retained_fragment_geometry_pilot.py", + "line": 138, + "node": "Attribute", + "name": "render_strips", + "source": "scene.render_strips" + }, + { + "root": "tests", + "path": "tests/retained_input_origin_observer.py", + "line": 131, + "node": "Attribute", + "name": "render_strips", + "source": "self.app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/saved_state_user_journey_pilot.py", + "line": 161, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/session_details_pilot.py", + "line": 51, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/session_details_pilot.py", + "line": 102, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/session_route_installed_pilot.py", + "line": 51, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/sidebar_navigation_pilot.py", + "line": 38, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/sidebar_retirement_pilot.py", + "line": 61, + "node": "Attribute", + "name": "render_strips", + "source": "widget.screen.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/tab_history_controls_pilot.py", + "line": 26, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/tab_history_controls_pilot.py", + "line": 87, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/tab_scrollbar_top_pilot.py", + "line": 63, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/tab_scrollbar_top_pilot.py", + "line": 121, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/terminal_attention_installed_journey.py", + "line": 142, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/terminal_attention_installed_journey.py", + "line": 163, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/terminal_attention_installed_journey.py", + "line": 212, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/thread_activation_pilot.py", + "line": 38, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/thread_navigation_installed_journey.py", + "line": 122, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/thread_navigation_installed_journey.py", + "line": 150, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/thread_navigation_installed_journey.py", + "line": 163, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/throbber_render_cache_pilot.py", + "line": 40, + "node": "Attribute", + "name": "render_strips", + "source": "visual.render_strips" + }, + { + "root": "tests", + "path": "tests/throbber_render_cache_pilot.py", + "line": 44, + "node": "Attribute", + "name": "render_strips", + "source": "ThrobberVisual(get_time=lambda value=clock: value).render_strips" + }, + { + "root": "tests", + "path": "tests/tool_diff_lifecycle_pilot.py", + "line": 32, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/tool_diff_pilot.py", + "line": 42, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/transcript_filter_installed_journey.py", + "line": 83, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/transcript_history_pilot.py", + "line": 136, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/transcript_history_pilot.py", + "line": 73, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/viewport_body_order_pilot.py", + "line": 447, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/viewport_layout_reentry_pilot.py", + "line": 69, + "node": "Attribute", + "name": "render_strips", + "source": "screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/viewport_rapid_scroll_pilot.py", + "line": 82, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/worker_native_size_pilot.py", + "line": 160, + "node": "Attribute", + "name": "render_strips", + "source": "scene.render_strips" + }, + { + "root": "tests", + "path": "tests/worker_preview_pilot.py", + "line": 99, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/worker_preview_pilot.py", + "line": 111, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + }, + { + "root": "tests", + "path": "tests/worker_preview_pilot.py", + "line": 133, + "node": "Attribute", + "name": "render_strips", + "source": "app.screen._compositor.render_strips" + } + ], + "subclasses": [ + { + "root": "src/toad", + "path": "src/toad/widgets/patch_diff.py", + "line": 125, + "name": "PreparedDiffLine", + "bases": [ + "Content" + ] + }, + { + "root": "src/toad", + "path": "src/toad/widgets/path_search.py", + "line": 44, + "name": "PathContent", + "bases": [ + "Content" + ] + } + ], + "aliases": [ + { + "path": "src/textual/_border.py", + "line": 15, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/_markup_playground.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/_styles_cache.py", + "line": 19, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/app.py", + "line": 98, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/command.py", + "line": 44, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/fuzzy.py", + "line": 18, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/highlight.py", + "line": 11, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/textual/markup.py", + "line": 33, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/markup.py", + "line": 342, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/textual/visual.py", + "line": 100, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/widget.py", + "line": 72, + "source": "from textual.content import Content, ContentType" + }, + { + "path": "src/textual/widgets/_button.py", + "line": 19, + "source": "from textual.content import Content, ContentText" + }, + { + "path": "src/textual/widgets/_collapsible.py", + "line": 7, + "source": "from textual.content import Content, ContentText" + }, + { + "path": "src/textual/widgets/_header.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/widgets/_markdown.py", + "line": 23, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/textual/widgets/_selection_list.py", + "line": 15, + "source": "from textual.content import Content, ContentText" + }, + { + "path": "src/textual/widgets/_static.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/widgets/_tabs.py", + "line": 15, + "source": "from textual.content import Content, ContentText" + }, + { + "path": "src/textual/widgets/_text_area.py", + "line": 22, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/widgets/_toast.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/textual/widgets/_toggle_button.py", + "line": 13, + "source": "from textual.content import Content, ContentText" + }, + { + "path": "tests/snapshot_tests/snapshot_apps/text_selection.py", + "line": 6, + "source": "from textual.content import Content" + }, + { + "path": "tests/snapshot_tests/test_snapshots.py", + "line": 29, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_border.py", + "line": 7, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_closed_presentation_lifetime.py", + "line": 7, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_content.py", + "line": 7, + "source": "from textual.content import Content, Span" + }, + { + "path": "tests/test_content_measurement_projection.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_markdownviewer.py", + "line": 8, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_markup.py", + "line": 5, + "source": "from textual.content import Content, Span" + }, + { + "path": "tests/test_static.py", + "line": 1, + "source": "from textual.content import Content" + }, + { + "path": "tests/test_widget.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "tests/toggles/test_labels.py", + "line": 4, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/app.py", + "line": 24, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/markdown_preparation.py", + "line": 6, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/messages.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/pill.py", + "line": 1, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/prompt_cursor.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/action_modal.py", + "line": 7, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/agent_modal.py", + "line": 8, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/main.py", + "line": 16, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/permissions.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/settings.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/screens/store.py", + "line": 20, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/setting_choices.py", + "line": 205, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/sidebar_preparation.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/sidebar_projection.py", + "line": 4, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/slash_command.py", + "line": 22, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/slash_command_results.py", + "line": 6, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/toad/terminal_attention.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/tool_output.py", + "line": 20, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/visuals/columns.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/channel_participants.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/command_pane.py", + "line": 62, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/comms_chat.py", + "line": 24, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/comms_sidebar.py", + "line": 27, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/condensed_path.py", + "line": 8, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/conversation.py", + "line": 51, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/flash.py", + "line": 3, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/future_text.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/highlighted_textarea.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/inline_message.py", + "line": 6, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/irc_message.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/mandelbrot.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/message_divider.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/message_notifications.py", + "line": 6, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/model_switcher.py", + "line": 12, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/patch_diff.py", + "line": 10, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/toad/widgets/path_search.py", + "line": 22, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/toad/widgets/plan.py", + "line": 4, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/prepared_markdown.py", + "line": 15, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/prompt.py", + "line": 18, + "source": "from textual.content import Content, Span" + }, + { + "path": "src/toad/widgets/question.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/route_header.py", + "line": 8, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/session_sidebar.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/session_tabs.py", + "line": 13, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/shell_terminal.py", + "line": 4, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/strike_text.py", + "line": 5, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/terminal.py", + "line": 9, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/terminal_tool.py", + "line": 6, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/thread_comms.py", + "line": 17, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/tool_call.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "src/toad/widgets/tool_content.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "tests/activity_spinners_pilot.py", + "line": 17, + "source": "from textual.content import Content" + }, + { + "path": "tests/committed_history_pilot.py", + "line": 12, + "source": "from textual.content import Content" + }, + { + "path": "tests/comms_pilot.py", + "line": 31, + "source": "from textual.content import Content" + }, + { + "path": "tests/diff_row_damage_pilot.py", + "line": 7, + "source": "from textual.content import Content" + }, + { + "path": "tests/goal_standby_history_pilot.py", + "line": 23, + "source": "from textual.content import Content" + }, + { + "path": "tests/guards/test_tool_output.py", + "line": 7, + "source": "from textual.content import Content" + }, + { + "path": "tests/markdown_row_render_pilot.py", + "line": 10, + "source": "from textual.content import Content" + }, + { + "path": "tests/model_picker_pilot.py", + "line": 11, + "source": "from textual.content import Content" + }, + { + "path": "tests/native_loaded_return_cache_pilot.py", + "line": 26, + "source": "from textual.content import Content" + }, + { + "path": "tests/prepared_diff_rows_pilot.py", + "line": 7, + "source": "from textual.content import Content, Span" + }, + { + "path": "tests/retained_tool_text_pilot.py", + "line": 16, + "source": "from textual.content import Content" + }, + { + "path": "tests/sidebar_layout_controls_pilot.py", + "line": 14, + "source": "from textual.content import Content" + }, + { + "path": "tests/tool_diff_pilot.py", + "line": 8, + "source": "from textual.content import Content" + } + ], + "limits": "Syntactic declarations/imports/calls/overrides in original roots; runtime-created classes, rebinding and unregistered external private API consumers are not resolved. Public Visual and Content render/measurement contracts stay unchanged." +} diff --git a/src/textual/content.py b/src/textual/content.py index abc254316c..5eb428e728 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -11,8 +11,9 @@ import re from functools import cached_property, total_ordering +from itertools import islice from operator import itemgetter -from typing import Callable, Iterable, NamedTuple, Sequence, Union +from typing import Callable, Iterable, Iterator, NamedTuple, Sequence, Union import rich.repr from rich._wrap import divide_line @@ -612,7 +613,7 @@ def get_height(self, rules: RulesMap, width: int) -> int: else: available = width - line_pad if available <= 0: - height = len(self.without_spans._wrap_and_format( + height = sum(1 for _ in self.without_spans._wrap_and_format( available, overflow=overflow, no_wrap=no_wrap, )) else: @@ -643,7 +644,7 @@ def _wrap_and_format( selection_style: Style | None = None, post_style: Style | None = None, get_style: Callable[[str | Style], Style] = Style.parse, - ) -> list[_FormattedLine]: + ) -> Iterator[_FormattedLine]: """Wraps the text and applies formatting. Args: @@ -656,10 +657,8 @@ def _wrap_and_format( selection_style: Selection style, or `None` if no selection. Returns: - List of formatted lines. + Formatted lines in original source and wrapping order. """ - output_lines: list[_FormattedLine] = [] - if selection is not None: get_span = selection.get_span else: @@ -721,9 +720,7 @@ def get_span(y: int) -> tuple[int, int] | None: ] new_lines[-1].line_end = True - output_lines.extend(new_lines) - - return output_lines + yield from new_lines def render_strips( self, width: int, height: int | None, style: Style, options: RenderOptions @@ -758,7 +755,7 @@ def render_strips( ) if height is not None: - lines = lines[:height] + lines = list(lines)[:height] if height < 0 else islice(lines, height) strip_lines = [Strip(*line.to_strip(style)) for line in lines] return strip_lines diff --git a/tests/test_content_measurement_projection.py b/tests/test_content_measurement_projection.py index 5e5a29b19c..161fa86da2 100644 --- a/tests/test_content_measurement_projection.py +++ b/tests/test_content_measurement_projection.py @@ -13,7 +13,7 @@ def test_height_projection_matches_native_formatter(text, width, wrap, overflow): content = Content(text) rules = {"text_wrap": wrap, "text_overflow": overflow} - expected = len(content.without_spans._wrap_and_format(width, overflow=overflow, no_wrap=wrap == "nowrap")) + expected = sum(1 for _ in content.without_spans._wrap_and_format(width, overflow=overflow, no_wrap=wrap == "nowrap")) with patch.object(Content, "_wrap_and_format", side_effect=AssertionError("Measurement constructed paint objects")): assert content.get_height(rules, width) == expected @@ -22,5 +22,5 @@ def test_line_padding_does_not_alias_another_widths_measurement(): content = Content("one two three four five six") for width, padding in [(10, 1), (12, 0), (8, 2), (12, 0)]: rules = {"line_pad": padding} - expected = len(content.without_spans._wrap_and_format(width - padding * 2)) + expected = sum(1 for _ in content.without_spans._wrap_and_format(width - padding * 2)) assert content.get_height(rules, width) == expected From d41e38b1da59ce34487333237de40efe6d41f347 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:30:46 -0400 Subject: [PATCH 03/10] test: preserve native render prefix metadata across height bounds --- .../SCOPE.md | 24 +++++++++++++ src/textual/content.py | 9 +++-- tests/test_content.py | 34 +++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md index cbd2eaf0ff..7dcf246f3c 100644 --- a/evidence/native-content-height-consumption-20261005/SCOPE.md +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -16,6 +16,30 @@ contracts, source selection/link/wide-cell metadata and original Content.split resource remain unchanged. Delete the unused complete formatted-result list. No new class, cache, map, timer, flag, alternate renderer or Toad implementation. +Published native source: 71e8c7fd4486b772252dafe7556b91e23a08fab9. +The complete formatting list is deleted (7 additions / 10 deletions in the +production source at that checkpoint). Later unrequested paragraphs no longer +construct formatting resources. Original Content.split still acquires all +logical source lines, and a requested paragraph still formats its wrapped +lines together; neither is claimed eliminated. + +The before AST covers 249 native production files / 460 native test files and +288 Toad production files / 397 test files / 41 tools with zero parse omissions. +It identifies one formatter declaration, three native production readers, +two native test count readers plus one monkeypatch, and ONE extra Toad private +reader: PathContent.render_strips in path_search.py. That reader slices the +formatter result and must migrate before qualification. Heisenberg owns this +Toad change, preserving original path shortening, nowrap/clip/tab8/left/zero +padding and RenderOptions through Content.render_strips. PreparedDiffLine +inherits the original rendering implementation without an override. + +Native controls are authored, not executed: original height-measurement +expected counts consume the iterator; public bounded/full/negative render +comparisons retain strip cells, links, wide characters, selection and post +styles. The family remains unqualified until the PathContent closure and final +batched controls. Unregistered external private readers and runtime rebinding +are not proved by the AST. + The original recorded59.605 RichVisual stack does NOT identify Content or its widget/invalidation cause. This independent unused-resource source finding is not an attribution of that sample, a CPU share or a measured speedup. diff --git a/src/textual/content.py b/src/textual/content.py index 5eb428e728..c90b3c21f4 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -613,9 +613,12 @@ def get_height(self, rules: RulesMap, width: int) -> int: else: available = width - line_pad if available <= 0: - height = sum(1 for _ in self.without_spans._wrap_and_format( - available, overflow=overflow, no_wrap=no_wrap, - )) + height = sum( + 1 + for _ in self.without_spans._wrap_and_format( + available, overflow=overflow, no_wrap=no_wrap, + ) + ) else: # Measurement needs only native wrapping boundaries. Building # Content slices, span caches and _FormattedLine objects here diff --git a/tests/test_content.py b/tests/test_content.py index 6a9d54f838..688797d997 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -5,11 +5,45 @@ from textual.color import Color from textual.content import Content, Span +from textual.geometry import Offset +from textual.selection import Selection from textual.style import Style from textual.visual import RenderOptions from textual.widget import Widget +@pytest.mark.parametrize("height", [0, 1, 3, -1, None]) +@pytest.mark.parametrize("width", [9, 20]) +@pytest.mark.parametrize( + "rules", + [ + {}, + {"text_wrap": "nowrap", "text_overflow": "clip", "text_align": "right"}, + {"text_align": "justify", "line_pad": 1}, + ], +) +def test_render_height_preserves_source_segments(width, height, rules): + """A bounded render keeps the full render's cell, link and selection metadata.""" + content = Content.from_markup( + "[link='https://example.com']wide 界 text[/link]\tmore words\n" + "second line wraps too\nthird source paragraph\nfinal line" + ) + options = RenderOptions( + Style.parse, + rules, + selection=Selection.from_offsets(Offset(2, 0), Offset(6, 2)), + selection_style=Style.parse("reverse"), + post_style=Style.parse("italic"), + ) + base_style = Style.parse("white on black") + complete = content.render_strips(width, None, base_style, options) + bounded = content.render_strips(width, height, base_style, options) + expected = complete if height is None else complete[:height] + assert [(strip.cell_length, list(strip)) for strip in bounded] == [ + (strip.cell_length, list(strip)) for strip in expected + ] + + def test_blank(): """Check blank content.""" blank = Content("") From f87735770f6ab62485832a9e19c006940b215df5 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:31:44 -0400 Subject: [PATCH 04/10] fix: type bounded formatting as an iterable consumption --- src/textual/content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/content.py b/src/textual/content.py index c90b3c21f4..530f55222e 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -744,7 +744,7 @@ def render_strips( return [] get_rule = options.rules.get - lines = self._wrap_and_format( + lines: Iterable[_FormattedLine] = self._wrap_and_format( width, align=get_rule("text_align", "left"), overflow=get_rule("text_overflow", "fold"), From 5025502eee092312717ba7839f9d91c25e15f437 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:32:24 -0400 Subject: [PATCH 05/10] test: retain complete height measurement without available width --- tests/test_content_measurement_projection.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_content_measurement_projection.py b/tests/test_content_measurement_projection.py index 161fa86da2..84fa6304c1 100644 --- a/tests/test_content_measurement_projection.py +++ b/tests/test_content_measurement_projection.py @@ -24,3 +24,10 @@ def test_line_padding_does_not_alias_another_widths_measurement(): rules = {"line_pad": padding} expected = sum(1 for _ in content.without_spans._wrap_and_format(width - padding * 2)) assert content.get_height(rules, width) == expected + + +@pytest.mark.parametrize("width", [0, 2]) +def test_no_available_width_measurement_consumes_all_source_lines(width): + content = Content("first\n\nlast") + rules = {"text_wrap": "nowrap", "text_overflow": "clip", "line_pad": 1} + assert content.get_height(rules, width) == 3 From 325a912635f55f6ec15b8365530a66c04edaf1ad Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:34:28 -0400 Subject: [PATCH 06/10] perf: yield native paragraph formatting at the render frontier --- .../SCOPE.md | 17 ++++++---- src/textual/content.py | 33 +++++++------------ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md index 7dcf246f3c..f067f3a672 100644 --- a/evidence/native-content-height-consumption-20261005/SCOPE.md +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -16,12 +16,17 @@ contracts, source selection/link/wide-cell metadata and original Content.split resource remain unchanged. Delete the unused complete formatted-result list. No new class, cache, map, timer, flag, alternate renderer or Toad implementation. -Published native source: 71e8c7fd4486b772252dafe7556b91e23a08fab9. -The complete formatting list is deleted (7 additions / 10 deletions in the -production source at that checkpoint). Later unrequested paragraphs no longer -construct formatting resources. Original Content.split still acquires all -logical source lines, and a requested paragraph still formats its wrapped -lines together; neither is claimed eliminated. +The initial published iterator source was +71e8c7fd4486b772252dafe7556b91e23a08fab9 (7 additions / 10 deletions). +The same owner now also yields each wrapped line directly: no complete +per-paragraph formatting list, intermediate truncated-content list, or +throwaway _FormattedLine solely for its content field. Original loop_last +supplies the last-line justification fact at construction instead of mutating +the last member of a completed list. Current production delta is 23 additions / +34 deletions in one file. Unrequested wrapped suffixes no longer construct +formatted lines or apply their truncation/padding. Original Content.split and +divide still acquire source slices and wrapping boundaries; neither is claimed +eliminated or height bounded. The before AST covers 249 native production files / 460 native test files and 288 Toad production files / 397 test files / 41 tools with zero parse omissions. diff --git a/src/textual/content.py b/src/textual/content.py index 530f55222e..0993cef7e1 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -684,46 +684,35 @@ def get_span(y: int) -> tuple[int, int] | None: if no_wrap: if overflow == "fold": cuts = list(range(0, line.cell_length, width))[1:] - new_lines = [ - _FormattedLine(get_style, line, width, y=y, align=align) - for line in line.divide(cuts) - ] + for content in line.divide(cuts): + yield _FormattedLine(get_style, content, width, y=y, align=align) else: line = line.truncate(width, ellipsis=overflow == "ellipsis") - content_line = _FormattedLine( + yield _FormattedLine( get_style, line, width, y=y, align=align ) - new_lines = [content_line] else: - content_line = _FormattedLine(get_style, line, width, y=y, align=align) offsets = divide_line( line.plain, width - line_pad * 2, fold=overflow == "fold" ) - divided_lines = content_line.content.divide(offsets) ellipsis = overflow == "ellipsis" - divided_lines = [ - ( - line.truncate(width, ellipsis=ellipsis) + for (last, content), offset in zip( + loop_last(line.divide(offsets)), [0, *offsets] + ): + content = ( + content.truncate(width, ellipsis=ellipsis) if last - else line.rstrip().truncate(width, ellipsis=ellipsis) + else content.rstrip().truncate(width, ellipsis=ellipsis) ) - for last, line in loop_last(divided_lines) - ] - - new_lines = [ - _FormattedLine( + yield _FormattedLine( get_style, content.rstrip_end(width).pad(line_pad, line_pad), width, offset, y, align=align, + line_end=last, ) - for content, offset in zip(divided_lines, [0, *offsets]) - ] - new_lines[-1].line_end = True - - yield from new_lines def render_strips( self, width: int, height: int | None, style: Style, options: RenderOptions From 81ca92b137e3070b31abb5146b7bae6f8043e228 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:36:23 -0400 Subject: [PATCH 07/10] docs: bind native formatting consumer closure and remaining Path reader --- .../after.json | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 evidence/native-content-height-consumption-20261005/after.json diff --git a/evidence/native-content-height-consumption-20261005/after.json b/evidence/native-content-height-consumption-20261005/after.json new file mode 100644 index 0000000000..1731cb0054 --- /dev/null +++ b/evidence/native-content-height-consumption-20261005/after.json @@ -0,0 +1,250 @@ +{ + "roots": [ + { + "repository": "/home/ts/wt/textual-native-subtree-strips-20261002", + "head": "325a912635f55f6ec15b8365530a66c04edaf1ad", + "root": "src/textual", + "parsed": 249, + "omitted": [] + }, + { + "repository": "/home/ts/wt/textual-native-subtree-strips-20261002", + "head": "325a912635f55f6ec15b8365530a66c04edaf1ad", + "root": "tests", + "parsed": 460, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "root": "src/toad", + "parsed": 288, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "root": "tests", + "parsed": 397, + "omitted": [] + }, + { + "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", + "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "root": "tools", + "parsed": 41, + "omitted": [] + } + ], + "formatter_sites": [ + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1724, + "node": "ClassDef", + "name": "_FormattedLine", + "source": "class _FormattedLine:" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 638, + "node": "FunctionDef", + "name": "_wrap_and_format", + "source": " def _wrap_and_format(" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 650, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1532, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1552, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 736, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 736, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1011, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1535, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 1537, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 691, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 707, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 618, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "self.without_spans._wrap_and_format" + }, + { + "root": "src/textual", + "path": "src/textual/content.py", + "line": 688, + "node": "Name", + "name": "_FormattedLine", + "source": "_FormattedLine" + }, + { + "root": "src/textual", + "path": "src/textual/document/_wrapped_document.py", + "line": 197, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/document/_wrapped_document.py", + "line": 212, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/document/_wrapped_document.py", + "line": 267, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/widgets/_log.py", + "line": 231, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/widgets/_log.py", + "line": 235, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/widgets/_log.py", + "line": 239, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/widgets/_log.py", + "line": 240, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "src/textual", + "path": "src/textual/widgets/_log.py", + "line": 233, + "node": "Name", + "name": "new_lines", + "source": "new_lines" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 17, + "node": "Constant", + "name": "_wrap_and_format", + "source": "\"_wrap_and_format\"" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 16, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "content.without_spans._wrap_and_format" + }, + { + "root": "tests", + "path": "tests/test_content_measurement_projection.py", + "line": 25, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "content.without_spans._wrap_and_format" + }, + { + "root": "src/toad", + "path": "src/toad/widgets/path_search.py", + "line": 69, + "node": "Attribute", + "name": "_wrap_and_format", + "source": "line._wrap_and_format" + } + ], + "limits": "Syntactic source evidence only; runtime rebinding and unregistered external private readers unresolved. Toad PathContent migration remains required if its private reader is present." +} From 066a41654a99e5e8545eebcbc9108b3b5fbaf7ef Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:51:09 -0400 Subject: [PATCH 08/10] docs: qualify native Content source behavior at its render frontier --- .../SCOPE.md | 17 ++++++++++++----- .../source-content01.json | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 evidence/native-content-height-consumption-20261005/source-content01.json diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md index f067f3a672..923a3699bd 100644 --- a/evidence/native-content-height-consumption-20261005/SCOPE.md +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -38,11 +38,18 @@ Toad change, preserving original path shortening, nowrap/clip/tab8/left/zero padding and RenderOptions through Content.render_strips. PreparedDiffLine inherits the original rendering implementation without an override. -Native controls are authored, not executed: original height-measurement -expected counts consume the iterator; public bounded/full/negative render -comparisons retain strip cells, links, wide characters, selection and post -styles. The family remains unqualified until the PathContent closure and final -batched controls. Unregistered external private readers and runtime rebinding +ONE final native source batch executed at +81ca92b137e3070b31abb5146b7bae6f8043e228, production tree +90f78e605a8dc1470d1de8c19220ab4b26474706: 348 PASS / 0 FAIL in 0.76s +(pytest), controller 2.499691362027079s, terminal 0. Original Content wrapping, +complete height measurement (including no available width), and public +bounded/full/negative rendering preserve strip cells, link/wide-cell/selection +metadata and post styles. Original log SHA256: +9c92eb8034b308320e4b038c3781be785dd0f3a557acc06d39b237548b1125a4. +Heisenberg has closed PathContent in source by delegating to Content.render_strips +with original leaf policy/options; its published successor and final source +closure evidence remain to bind. No installed App/frame/motion qualification +exists for Text70. Unregistered external private readers and runtime rebinding are not proved by the AST. The original recorded59.605 RichVisual stack does NOT identify Content or its diff --git a/evidence/native-content-height-consumption-20261005/source-content01.json b/evidence/native-content-height-consumption-20261005/source-content01.json new file mode 100644 index 0000000000..21fcd78ef2 --- /dev/null +++ b/evidence/native-content-height-consumption-20261005/source-content01.json @@ -0,0 +1,18 @@ +{ + "source_head": "81ca92b137e3070b31abb5146b7bae6f8043e228", + "production_tree": "90f78e605a8dc1470d1de8c19220ab4b26474706", + "argv": [ + "/usr/bin/python", + "-m", + "pytest", + "-q", + "tests/test_content.py", + "tests/test_content_measurement_projection.py" + ], + "started_at_unix": 1791211804.5934083, + "duration_seconds": 2.499691362027079, + "exit_code": 0, + "log_path": "/home/ts/wt/textual-native-subtree-strips-20261002/.artifacts/native-content-height-consumption-20261005/source-content01.log", + "log_sha256": "9c92eb8034b308320e4b038c3781be785dd0f3a557acc06d39b237548b1125a4", + "scope": "Native public Content wrapping, bounded/full/negative render segments and complete measurement. No installed App, provider, SDK or physical recording." +} From 3c8dace57f335dc34d4c8f1d3f876c134c51d9ec Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:54:54 -0400 Subject: [PATCH 09/10] docs: bind native formatting controls and actual Path render producer --- .../SCOPE.md | 12 ++++++--- .../after.json | 26 ++++++++++++------- .../source-content01.log | 6 +++++ 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 evidence/native-content-height-consumption-20261005/source-content01.log diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md index 923a3699bd..b1bf5835e3 100644 --- a/evidence/native-content-height-consumption-20261005/SCOPE.md +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -46,9 +46,15 @@ complete height measurement (including no available width), and public bounded/full/negative rendering preserve strip cells, link/wide-cell/selection metadata and post styles. Original log SHA256: 9c92eb8034b308320e4b038c3781be785dd0f3a557acc06d39b237548b1125a4. -Heisenberg has closed PathContent in source by delegating to Content.render_strips -with original leaf policy/options; its published successor and final source -closure evidence remain to bind. No installed App/frame/motion qualification +Heisenberg published PathContent delegation at +1fbe1f09b80dec9a809609edaeafc6ddb5a661a2 (Toad463); final AST finds zero +Toad private formatter readers. The complete native producer read then found a +caller contract defect: Visual.to_strips supplies widget.styles, not a dict, +and Styles/RenderStyles has no mapping-union operation. PathContent currently +unions that resource with its four fixed rules. Heisenberg owns correction to +the original four-rule leaf policy directly while preserving all other +RenderOptions fields. This is source evidence, not an executed UI failure; the +original snapshot remains recorded and the joined caller is not qualified. No installed App/frame/motion qualification exists for Text70. Unregistered external private readers and runtime rebinding are not proved by the AST. diff --git a/evidence/native-content-height-consumption-20261005/after.json b/evidence/native-content-height-consumption-20261005/after.json index 1731cb0054..eb86b9f72e 100644 --- a/evidence/native-content-height-consumption-20261005/after.json +++ b/evidence/native-content-height-consumption-20261005/after.json @@ -16,21 +16,21 @@ }, { "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", - "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "head": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", "root": "src/toad", "parsed": 288, "omitted": [] }, { "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", - "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "head": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", "root": "tests", "parsed": 397, "omitted": [] }, { "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", - "head": "22177726ea4a5eb6bf7ccec660f58d10ec8cf287", + "head": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", "root": "tools", "parsed": 41, "omitted": [] @@ -236,15 +236,23 @@ "node": "Attribute", "name": "_wrap_and_format", "source": "content.without_spans._wrap_and_format" - }, + } + ], + "limits": "Syntactic source evidence only; runtime rebinding and unregistered external private readers unresolved. Native source/tests AST is reused from identical 325a source; Toad source successor is pinned independently. No installed frame, motion or CPU attribution.", + "toad_private_formatter_readers": [], + "toad_public_content_delegations": [ { "root": "src/toad", "path": "src/toad/widgets/path_search.py", - "line": 69, - "node": "Attribute", - "name": "_wrap_and_format", - "source": "line._wrap_and_format" + "line": 70, + "source": "Content.render_strips(\n line,\n width,\n height,\n style,\n replace(\n options,\n rules=options.rules\n | {\n \"text_wrap\": \"nowrap\",\n \"text_overflow\": \"clip\",\n \"text_align\": \"left\",\n \"line_pad\": 0,\n },\n ),\n )" } ], - "limits": "Syntactic source evidence only; runtime rebinding and unregistered external private readers unresolved. Toad PathContent migration remains required if its private reader is present." + "path_contract_counterexample": { + "source": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", + "producer": "src/textual/visual.py Visual.to_strips constructs RenderOptions with widget.styles; src/textual/widgets/_option_list.py uses that path", + "failure": "Styles/RenderStyles supports get but not mapping union; PathContent options.rules | dict is not supported by the real producer", + "correction_owner": "Heisenberg, Toad463", + "required_correction": "Preserve the four original fixed render rules directly; leave the other RenderOptions fields unchanged. No native change or reproduction required." + } } diff --git a/evidence/native-content-height-consumption-20261005/source-content01.log b/evidence/native-content-height-consumption-20261005/source-content01.log new file mode 100644 index 0000000000..00c90413ff --- /dev/null +++ b/evidence/native-content-height-consumption-20261005/source-content01.log @@ -0,0 +1,6 @@ +........................................................................ [ 20%] +........................................................................ [ 41%] +........................................................................ [ 62%] +........................................................................ [ 82%] +............................................................ [100%] +348 passed in 0.76s From f9fc6b73ca0d9c57ce63f12e239f458947b82854 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:57:52 -0400 Subject: [PATCH 10/10] docs: close Path caller against its actual native style producer --- .../SCOPE.md | 12 ++++++--- .../after.json | 26 ++++++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/evidence/native-content-height-consumption-20261005/SCOPE.md b/evidence/native-content-height-consumption-20261005/SCOPE.md index b1bf5835e3..88860cbafb 100644 --- a/evidence/native-content-height-consumption-20261005/SCOPE.md +++ b/evidence/native-content-height-consumption-20261005/SCOPE.md @@ -51,10 +51,14 @@ Heisenberg published PathContent delegation at Toad private formatter readers. The complete native producer read then found a caller contract defect: Visual.to_strips supplies widget.styles, not a dict, and Styles/RenderStyles has no mapping-union operation. PathContent currently -unions that resource with its four fixed rules. Heisenberg owns correction to -the original four-rule leaf policy directly while preserving all other -RenderOptions fields. This is source evidence, not an executed UI failure; the -original snapshot remains recorded and the joined caller is not qualified. No installed App/frame/motion qualification +unions that resource with its four fixed rules. Heisenberg corrected that original four-rule leaf policy directly at +a4d7cb17435470edd1276360d9c9a26593c4a031 while preserving all other RenderOptions fields. +Final source AST retains zero Toad private formatter readers, one public +Content delegation, and no parse omissions. Unchanged test/tool AST is carried +by exact original Git tree identity; no repeated parse or checks are claimed. +The original 1fbe counterexample remains recorded as source evidence, not an +executed UI failure. Complete declared source family and native source controls +are closed; the changed installed caller/frame journey remains unqualified. No installed App/frame/motion qualification exists for Text70. Unregistered external private readers and runtime rebinding are not proved by the AST. diff --git a/evidence/native-content-height-consumption-20261005/after.json b/evidence/native-content-height-consumption-20261005/after.json index eb86b9f72e..92292f2924 100644 --- a/evidence/native-content-height-consumption-20261005/after.json +++ b/evidence/native-content-height-consumption-20261005/after.json @@ -16,7 +16,7 @@ }, { "repository": "/home/ts/wt/toad-viewport-raster-cpu-continuation-20261001", - "head": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", + "head": "a4d7cb17435470edd1276360d9c9a26593c4a031", "root": "src/toad", "parsed": 288, "omitted": [] @@ -245,7 +245,7 @@ "root": "src/toad", "path": "src/toad/widgets/path_search.py", "line": 70, - "source": "Content.render_strips(\n line,\n width,\n height,\n style,\n replace(\n options,\n rules=options.rules\n | {\n \"text_wrap\": \"nowrap\",\n \"text_overflow\": \"clip\",\n \"text_align\": \"left\",\n \"line_pad\": 0,\n },\n ),\n )" + "source": "Content.render_strips(\n line,\n width,\n height,\n style,\n replace(\n options,\n rules={\n \"text_wrap\": \"nowrap\",\n \"text_overflow\": \"clip\",\n \"text_align\": \"left\",\n \"line_pad\": 0,\n },\n ),\n )" } ], "path_contract_counterexample": { @@ -253,6 +253,24 @@ "producer": "src/textual/visual.py Visual.to_strips constructs RenderOptions with widget.styles; src/textual/widgets/_option_list.py uses that path", "failure": "Styles/RenderStyles supports get but not mapping union; PathContent options.rules | dict is not supported by the real producer", "correction_owner": "Heisenberg, Toad463", - "required_correction": "Preserve the four original fixed render rules directly; leave the other RenderOptions fields unchanged. No native change or reproduction required." - } + "required_correction": "Preserve the four original fixed render rules directly; leave the other RenderOptions fields unchanged. No native change or reproduction required.", + "corrected_source_head": "a4d7cb17435470edd1276360d9c9a26593c4a031", + "correction": "Original four fixed Path render rules are supplied directly in replace(options); native Styles is no longer used as a mapping-union operand." + }, + "source_preserving_transitions": [ + { + "root": "tests", + "parsed_at": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", + "source_head": "a4d7cb17435470edd1276360d9c9a26593c4a031", + "tree": "72d50efa1f3c1fe3379fbff2981313fefddb9a1a", + "source_equal": true + }, + { + "root": "tools", + "parsed_at": "1fbe1f09b80dec9a809609edaeafc6ddb5a661a2", + "source_head": "a4d7cb17435470edd1276360d9c9a26593c4a031", + "tree": "5f291b2f613803e0b1596581215557f35c65c8d6", + "source_equal": true + } + ] }