iCode-TUI 0.29.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chrys/__init__.py +16 -0
- chrys/app/__init__.py +3 -0
- chrys/app/acp/__init__.py +3 -0
- chrys/app/acp/bridge.py +664 -0
- chrys/app/acp/content.py +132 -0
- chrys/app/acp/doc/frontend-api.md +425 -0
- chrys/app/acp/history.py +585 -0
- chrys/app/acp/server.py +1796 -0
- chrys/app/acp/session_manager.py +1815 -0
- chrys/app/acp/tool_status.py +45 -0
- chrys/app/cli/__init__.py +3 -0
- chrys/app/cli/acp.py +178 -0
- chrys/app/cli/app.py +165 -0
- chrys/app/cli/approvals.py +101 -0
- chrys/app/cli/headless.py +223 -0
- chrys/app/cli/launch_cwd.py +42 -0
- chrys/app/cli/profiles.py +298 -0
- chrys/app/cli/progress.py +857 -0
- chrys/app/cli/run.py +425 -0
- chrys/app/cli/serve.py +1593 -0
- chrys/app/cli/tool_summary.py +225 -0
- chrys/app/cli/trajectory.py +201 -0
- chrys/app/cli/workflow.py +601 -0
- chrys/app/features/__init__.py +3 -0
- chrys/app/features/buddy/__init__.py +3 -0
- chrys/app/features/buddy/actions.py +68 -0
- chrys/app/features/buddy/animation.py +76 -0
- chrys/app/features/buddy/commands.py +196 -0
- chrys/app/features/buddy/docs/design.md +187 -0
- chrys/app/features/buddy/hatchery.py +80 -0
- chrys/app/features/buddy/lifecycle.py +41 -0
- chrys/app/features/buddy/model.py +265 -0
- chrys/app/features/buddy/pixel_renderer.py +113 -0
- chrys/app/features/buddy/pixel_sprites.py +279 -0
- chrys/app/features/buddy/portrait.py +76 -0
- chrys/app/features/buddy/progression.py +58 -0
- chrys/app/features/buddy/replies.py +191 -0
- chrys/app/features/buddy/sprites/alien.toml +70 -0
- chrys/app/features/buddy/sprites/axolotl.toml +71 -0
- chrys/app/features/buddy/sprites/bat.toml +69 -0
- chrys/app/features/buddy/sprites/bee.toml +69 -0
- chrys/app/features/buddy/sprites/cactus.toml +73 -0
- chrys/app/features/buddy/sprites/capybara.toml +70 -0
- chrys/app/features/buddy/sprites/cat.toml +69 -0
- chrys/app/features/buddy/sprites/crab.toml +70 -0
- chrys/app/features/buddy/sprites/dragon.toml +71 -0
- chrys/app/features/buddy/sprites/duck.toml +69 -0
- chrys/app/features/buddy/sprites/fox.toml +69 -0
- chrys/app/features/buddy/sprites/frog.toml +70 -0
- chrys/app/features/buddy/sprites/ghost.toml +70 -0
- chrys/app/features/buddy/sprites/goose.toml +69 -0
- chrys/app/features/buddy/sprites/jelly.toml +70 -0
- chrys/app/features/buddy/sprites/llama.toml +70 -0
- chrys/app/features/buddy/sprites/mushroom.toml +69 -0
- chrys/app/features/buddy/sprites/octopus.toml +70 -0
- chrys/app/features/buddy/sprites/owl.toml +70 -0
- chrys/app/features/buddy/sprites/panda.toml +71 -0
- chrys/app/features/buddy/sprites/penguin.toml +69 -0
- chrys/app/features/buddy/sprites/rabbit.toml +70 -0
- chrys/app/features/buddy/sprites/robot.toml +72 -0
- chrys/app/features/buddy/sprites/shark.toml +70 -0
- chrys/app/features/buddy/sprites/snail.toml +69 -0
- chrys/app/features/buddy/sprites/snake.toml +69 -0
- chrys/app/features/buddy/sprites/tabby.toml +69 -0
- chrys/app/features/buddy/sprites/turtle.toml +69 -0
- chrys/app/features/buddy/store.py +143 -0
- chrys/app/features/session_title/__init__.py +7 -0
- chrys/app/features/session_title/updater.py +582 -0
- chrys/app/installer.py +651 -0
- chrys/app/parsing.py +17 -0
- chrys/app/tui/__init__.py +3 -0
- chrys/app/tui/app.py +1287 -0
- chrys/app/tui/behaviors/__init__.py +3 -0
- chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
- chrys/app/tui/behaviors/insert_clipboard.py +73 -0
- chrys/app/tui/behaviors/right_click_copy.py +146 -0
- chrys/app/tui/binding_display.py +139 -0
- chrys/app/tui/buddy_messages.py +17 -0
- chrys/app/tui/buddy_reply.py +86 -0
- chrys/app/tui/chrys.tcss +549 -0
- chrys/app/tui/clipboard.py +109 -0
- chrys/app/tui/copy_messages.py +10 -0
- chrys/app/tui/i18n.py +230 -0
- chrys/app/tui/language.py +58 -0
- chrys/app/tui/notifications/__init__.py +12 -0
- chrys/app/tui/notifications/drivers.py +180 -0
- chrys/app/tui/notifications/service.py +126 -0
- chrys/app/tui/notifications/settings.py +141 -0
- chrys/app/tui/screens/__init__.py +3 -0
- chrys/app/tui/screens/agents/__init__.py +8 -0
- chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
- chrys/app/tui/screens/agents/config.py +2142 -0
- chrys/app/tui/screens/agents/config.tcss +169 -0
- chrys/app/tui/screens/agents/panels/__init__.py +3 -0
- chrys/app/tui/screens/agents/panels/acp.py +1176 -0
- chrys/app/tui/screens/agents/panels/basic.py +497 -0
- chrys/app/tui/screens/agents/panels/compaction.py +231 -0
- chrys/app/tui/screens/agents/panels/config_card.py +218 -0
- chrys/app/tui/screens/agents/panels/instructions.py +91 -0
- chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
- chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
- chrys/app/tui/screens/agents/panels/memory.py +684 -0
- chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
- chrys/app/tui/screens/agents/panels/skills.py +806 -0
- chrys/app/tui/screens/agents/panels/subagents.py +652 -0
- chrys/app/tui/screens/agents/panels/tools.py +205 -0
- chrys/app/tui/screens/agents/picker.py +103 -0
- chrys/app/tui/screens/agents/skill_paths.py +38 -0
- chrys/app/tui/screens/agents/validation_messages.py +452 -0
- chrys/app/tui/screens/dialogs/__init__.py +35 -0
- chrys/app/tui/screens/dialogs/agent_load.py +834 -0
- chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
- chrys/app/tui/screens/dialogs/app_mode.py +55 -0
- chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
- chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
- chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
- chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
- chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
- chrys/app/tui/screens/dialogs/approval/body.py +110 -0
- chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
- chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
- chrys/app/tui/screens/dialogs/ask_user.py +148 -0
- chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
- chrys/app/tui/screens/dialogs/base.py +84 -0
- chrys/app/tui/screens/dialogs/confirm.py +184 -0
- chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
- chrys/app/tui/screens/dialogs/connection_test.py +183 -0
- chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
- chrys/app/tui/screens/dialogs/diagram.py +102 -0
- chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
- chrys/app/tui/screens/dialogs/editor.py +378 -0
- chrys/app/tui/screens/dialogs/editor.tcss +132 -0
- chrys/app/tui/screens/dialogs/file_picker.py +582 -0
- chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
- chrys/app/tui/screens/dialogs/fork_session.py +219 -0
- chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
- chrys/app/tui/screens/dialogs/image_compression.py +102 -0
- chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
- chrys/app/tui/screens/dialogs/man_page.py +172 -0
- chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
- chrys/app/tui/screens/dialogs/new_folder.py +277 -0
- chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
- chrys/app/tui/screens/dialogs/notice.tcss +14 -0
- chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
- chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
- chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
- chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
- chrys/app/tui/screens/dialogs/session_title.py +110 -0
- chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
- chrys/app/tui/screens/dialogs/tool_view.py +207 -0
- chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
- chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
- chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
- chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
- chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
- chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
- chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
- chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
- chrys/app/tui/screens/diff/__init__.py +22 -0
- chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
- chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
- chrys/app/tui/screens/diff/screen.py +1532 -0
- chrys/app/tui/screens/diff/screen.tcss +104 -0
- chrys/app/tui/screens/guides/__init__.py +7 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
- chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/index.py +215 -0
- chrys/app/tui/screens/guides/reader.py +173 -0
- chrys/app/tui/screens/guides/screen.py +442 -0
- chrys/app/tui/screens/guides/screen.tcss +52 -0
- chrys/app/tui/screens/logs/__init__.py +7 -0
- chrys/app/tui/screens/logs/screen.py +504 -0
- chrys/app/tui/screens/logs/screen.tcss +45 -0
- chrys/app/tui/screens/main/__init__.py +7 -0
- chrys/app/tui/screens/main/action_availability.py +30 -0
- chrys/app/tui/screens/main/buddy_command.py +143 -0
- chrys/app/tui/screens/main/commands.py +1073 -0
- chrys/app/tui/screens/main/config_actions.py +464 -0
- chrys/app/tui/screens/main/copy_actions.py +117 -0
- chrys/app/tui/screens/main/dialog_controllers.py +826 -0
- chrys/app/tui/screens/main/dialog_gateway.py +166 -0
- chrys/app/tui/screens/main/diff_controller.py +478 -0
- chrys/app/tui/screens/main/engine_read_model.py +184 -0
- chrys/app/tui/screens/main/event_handlers.py +2014 -0
- chrys/app/tui/screens/main/input_flow.py +241 -0
- chrys/app/tui/screens/main/live_diff.py +244 -0
- chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
- chrys/app/tui/screens/main/model_indicator.py +217 -0
- chrys/app/tui/screens/main/navigation.py +343 -0
- chrys/app/tui/screens/main/ports.py +571 -0
- chrys/app/tui/screens/main/recent_dirs.py +123 -0
- chrys/app/tui/screens/main/rollback_controller.py +586 -0
- chrys/app/tui/screens/main/runtime_info.py +71 -0
- chrys/app/tui/screens/main/screen.py +2265 -0
- chrys/app/tui/screens/main/screen.tcss +21 -0
- chrys/app/tui/screens/main/session_handlers.py +1127 -0
- chrys/app/tui/screens/main/session_title.py +239 -0
- chrys/app/tui/screens/main/settings_coordinator.py +365 -0
- chrys/app/tui/screens/main/settings_persistence.py +184 -0
- chrys/app/tui/screens/main/shell_mode.py +112 -0
- chrys/app/tui/screens/main/state.py +289 -0
- chrys/app/tui/screens/main/submit_state.py +11 -0
- chrys/app/tui/screens/main/subscriptions.py +177 -0
- chrys/app/tui/screens/main/suggestions.py +982 -0
- chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
- chrys/app/tui/screens/main/view_adapter.py +1567 -0
- chrys/app/tui/screens/main/workflow_browser.py +747 -0
- chrys/app/tui/screens/main/workflow_content.py +248 -0
- chrys/app/tui/screens/main/workflow_controller.py +240 -0
- chrys/app/tui/screens/main/workflow_feedback.py +137 -0
- chrys/app/tui/screens/main/workflow_flow.py +49 -0
- chrys/app/tui/screens/main/workflow_run_control.py +456 -0
- chrys/app/tui/screens/main/workflow_session_view.py +316 -0
- chrys/app/tui/screens/main/workspace_actions.py +272 -0
- chrys/app/tui/screens/main/workspace_branch.py +194 -0
- chrys/app/tui/screens/menus/__init__.py +8 -0
- chrys/app/tui/screens/menus/languages.py +114 -0
- chrys/app/tui/screens/models/__init__.py +7 -0
- chrys/app/tui/screens/models/picker.py +147 -0
- chrys/app/tui/screens/models/screen.py +2043 -0
- chrys/app/tui/screens/models/screen.tcss +255 -0
- chrys/app/tui/screens/sessions/__init__.py +8 -0
- chrys/app/tui/screens/sessions/presenter.py +586 -0
- chrys/app/tui/screens/sessions/screen.py +779 -0
- chrys/app/tui/screens/sessions/screen.tcss +127 -0
- chrys/app/tui/screens/settings/__init__.py +29 -0
- chrys/app/tui/screens/settings/danger.py +26 -0
- chrys/app/tui/screens/settings/dialog.py +281 -0
- chrys/app/tui/screens/settings/layout.py +519 -0
- chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
- chrys/app/tui/screens/settings/panes/__init__.py +3 -0
- chrys/app/tui/screens/settings/panes/notifications.py +248 -0
- chrys/app/tui/screens/settings/panes/sessions.py +148 -0
- chrys/app/tui/screens/settings/ports.py +55 -0
- chrys/app/tui/screens/settings/provenance.py +102 -0
- chrys/app/tui/screens/settings/rows.py +546 -0
- chrys/app/tui/screens/settings/settings.tcss +406 -0
- chrys/app/tui/screens/themes/__init__.py +8 -0
- chrys/app/tui/screens/themes/dialogs.py +330 -0
- chrys/app/tui/screens/themes/editor.py +632 -0
- chrys/app/tui/screens/themes/editor.tcss +268 -0
- chrys/app/tui/screens/themes/messages.py +95 -0
- chrys/app/tui/screens/themes/palette.py +694 -0
- chrys/app/tui/screens/themes/panel.py +113 -0
- chrys/app/tui/screens/themes/picker.py +203 -0
- chrys/app/tui/screens/themes/save.py +191 -0
- chrys/app/tui/support/__init__.py +3 -0
- chrys/app/tui/support/file_manager.py +64 -0
- chrys/app/tui/support/gc_freeze.py +1114 -0
- chrys/app/tui/support/path_shortcuts.py +50 -0
- chrys/app/tui/support/prompt_history.py +298 -0
- chrys/app/tui/support/workspace_mru.py +476 -0
- chrys/app/tui/terminal/__init__.py +6 -0
- chrys/app/tui/terminal/_pty_backend.py +114 -0
- chrys/app/tui/terminal/emulator/__init__.py +24 -0
- chrys/app/tui/terminal/emulator/buffer.py +456 -0
- chrys/app/tui/terminal/emulator/charsets.py +113 -0
- chrys/app/tui/terminal/emulator/core.py +963 -0
- chrys/app/tui/terminal/emulator/input.py +342 -0
- chrys/app/tui/terminal/emulator/parser.py +227 -0
- chrys/app/tui/terminal/emulator/pen.py +158 -0
- chrys/app/tui/terminal/launcher.py +455 -0
- chrys/app/tui/terminal/leaked_reports.py +44 -0
- chrys/app/tui/terminal/panel.py +323 -0
- chrys/app/tui/terminal/pty_process.py +405 -0
- chrys/app/tui/terminal/rendering.py +151 -0
- chrys/app/tui/terminal/shell.py +273 -0
- chrys/app/tui/terminal/shell_integration.py +199 -0
- chrys/app/tui/terminal/title.py +224 -0
- chrys/app/tui/terminal/widget.py +654 -0
- chrys/app/tui/theme.py +422 -0
- chrys/app/tui/theme_loader.py +201 -0
- chrys/app/tui/theme_stylesheet.py +49 -0
- chrys/app/tui/themes/__init__.py +3 -0
- chrys/app/tui/themes/document.py +120 -0
- chrys/app/tui/themes/preview.py +170 -0
- chrys/app/tui/themes/store.py +176 -0
- chrys/app/tui/toasts.py +69 -0
- chrys/app/tui/util/__init__.py +3 -0
- chrys/app/tui/util/context_pressure.py +146 -0
- chrys/app/tui/util/debug_log.py +292 -0
- chrys/app/tui/util/diff_entries.py +64 -0
- chrys/app/tui/util/formatting.py +53 -0
- chrys/app/tui/util/git_branch.py +326 -0
- chrys/app/tui/util/invocation_progress.py +57 -0
- chrys/app/tui/util/logo.py +32 -0
- chrys/app/tui/util/message_gate.py +35 -0
- chrys/app/tui/util/mutation_sources.py +14 -0
- chrys/app/tui/util/picker_text.py +27 -0
- chrys/app/tui/util/removal.py +47 -0
- chrys/app/tui/util/rich_style.py +36 -0
- chrys/app/tui/util/source_text.py +12 -0
- chrys/app/tui/util/static_update.py +88 -0
- chrys/app/tui/util/todo_format.py +51 -0
- chrys/app/tui/util/visibility.py +214 -0
- chrys/app/tui/widgets/__init__.py +60 -0
- chrys/app/tui/widgets/ask_user_controls.py +652 -0
- chrys/app/tui/widgets/ask_user_markdown.py +17 -0
- chrys/app/tui/widgets/ask_user_prompt.py +606 -0
- chrys/app/tui/widgets/buttons.py +52 -0
- chrys/app/tui/widgets/chat/__init__.py +3 -0
- chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
- chrys/app/tui/widgets/chat/chrome.py +296 -0
- chrys/app/tui/widgets/chat/compaction_card.py +353 -0
- chrys/app/tui/widgets/chat/context_fold.py +77 -0
- chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
- chrys/app/tui/widgets/chat/image_preview.py +214 -0
- chrys/app/tui/widgets/chat/live_renderer.py +383 -0
- chrys/app/tui/widgets/chat/messages.py +1039 -0
- chrys/app/tui/widgets/chat/panel.py +1769 -0
- chrys/app/tui/widgets/chat/ports.py +245 -0
- chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
- chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
- chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
- chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
- chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
- chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
- chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
- chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
- chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
- chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
- chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
- chrys/app/tui/widgets/chat/renderers/search.py +348 -0
- chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
- chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
- chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
- chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
- chrys/app/tui/widgets/chat/renderers/web.py +69 -0
- chrys/app/tui/widgets/chat/replay.py +1605 -0
- chrys/app/tui/widgets/chat/replay_mount.py +322 -0
- chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
- chrys/app/tui/widgets/chat/selection_controller.py +435 -0
- chrys/app/tui/widgets/chat/session_json.py +508 -0
- chrys/app/tui/widgets/chat/toc_model.py +156 -0
- chrys/app/tui/widgets/chat/tool_call.py +2478 -0
- chrys/app/tui/widgets/chat/tool_registry.py +753 -0
- chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
- chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
- chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
- chrys/app/tui/widgets/checkbox.py +24 -0
- chrys/app/tui/widgets/chrome/__init__.py +3 -0
- chrys/app/tui/widgets/chrome/app_header.py +283 -0
- chrys/app/tui/widgets/chrome/commands.py +95 -0
- chrys/app/tui/widgets/chrome/file_index.py +68 -0
- chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
- chrys/app/tui/widgets/chrome/footer.py +284 -0
- chrys/app/tui/widgets/chrome/image_paste.py +351 -0
- chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
- chrys/app/tui/widgets/chrome/status_bar.py +911 -0
- chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
- chrys/app/tui/widgets/click_affordance.py +24 -0
- chrys/app/tui/widgets/color_picker/__init__.py +254 -0
- chrys/app/tui/widgets/color_picker/controls.py +171 -0
- chrys/app/tui/widgets/color_picker/model.py +89 -0
- chrys/app/tui/widgets/dialog_buttons.py +58 -0
- chrys/app/tui/widgets/dialog_scroll.py +24 -0
- chrys/app/tui/widgets/diff_view/__init__.py +20 -0
- chrys/app/tui/widgets/diff_view/cells.py +59 -0
- chrys/app/tui/widgets/diff_view/code.py +280 -0
- chrys/app/tui/widgets/diff_view/compute.py +286 -0
- chrys/app/tui/widgets/diff_view/gutter.py +161 -0
- chrys/app/tui/widgets/diff_view/inline.py +77 -0
- chrys/app/tui/widgets/diff_view/palette.py +87 -0
- chrys/app/tui/widgets/diff_view/protocols.py +24 -0
- chrys/app/tui/widgets/diff_view/rows.py +138 -0
- chrys/app/tui/widgets/diff_view/unified.py +267 -0
- chrys/app/tui/widgets/diff_view/widget.py +209 -0
- chrys/app/tui/widgets/editor/__init__.py +79 -0
- chrys/app/tui/widgets/editor/coordinates.py +30 -0
- chrys/app/tui/widgets/editor/highlighter.py +139 -0
- chrys/app/tui/widgets/editor/keymaps.py +357 -0
- chrys/app/tui/widgets/editor/types.py +216 -0
- chrys/app/tui/widgets/editor/vim.py +1195 -0
- chrys/app/tui/widgets/editor/widget.py +401 -0
- chrys/app/tui/widgets/file_preview.py +58 -0
- chrys/app/tui/widgets/hatch.py +138 -0
- chrys/app/tui/widgets/input.py +66 -0
- chrys/app/tui/widgets/loading.py +125 -0
- chrys/app/tui/widgets/markdown/__init__.py +25 -0
- chrys/app/tui/widgets/markdown/_utils.py +21 -0
- chrys/app/tui/widgets/markdown/anchors.py +71 -0
- chrys/app/tui/widgets/markdown/blocks.py +115 -0
- chrys/app/tui/widgets/markdown/copyable.py +163 -0
- chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
- chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
- chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
- chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
- chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
- chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
- chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
- chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
- chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
- chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
- chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
- chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
- chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
- chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
- chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
- chrys/app/tui/widgets/markdown/links.py +47 -0
- chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
- chrys/app/tui/widgets/markdown/math/layout.py +739 -0
- chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
- chrys/app/tui/widgets/markdown/math/parser.py +325 -0
- chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
- chrys/app/tui/widgets/markdown/navigator.py +80 -0
- chrys/app/tui/widgets/markdown/parser.py +1057 -0
- chrys/app/tui/widgets/markdown/stream.py +73 -0
- chrys/app/tui/widgets/markdown/toc.py +98 -0
- chrys/app/tui/widgets/markdown/viewer.py +146 -0
- chrys/app/tui/widgets/markdown/widget.py +2046 -0
- chrys/app/tui/widgets/marquee.py +101 -0
- chrys/app/tui/widgets/option_menu.py +82 -0
- chrys/app/tui/widgets/page_navigator.py +118 -0
- chrys/app/tui/widgets/select.py +196 -0
- chrys/app/tui/widgets/selection.py +59 -0
- chrys/app/tui/widgets/sidebar/__init__.py +3 -0
- chrys/app/tui/widgets/sidebar/buddy.py +367 -0
- chrys/app/tui/widgets/sidebar/context.py +482 -0
- chrys/app/tui/widgets/sidebar/debug.py +195 -0
- chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
- chrys/app/tui/widgets/sidebar/panel.py +198 -0
- chrys/app/tui/widgets/sidebar/tasks.py +198 -0
- chrys/app/tui/widgets/sidebar/toc.py +166 -0
- chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
- chrys/app/tui/widgets/syntax_theme.py +85 -0
- chrys/app/tui/widgets/text_area.py +258 -0
- chrys/app/tui/widgets/trajectory/__init__.py +8 -0
- chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
- chrys/app/tui/widgets/trajectory/insights.py +1266 -0
- chrys/app/tui/widgets/trajectory/overview.py +1101 -0
- chrys/app/tui/widgets/trajectory/panel.py +983 -0
- chrys/app/tui/widgets/trajectory/presentation.py +311 -0
- chrys/app/tui/widgets/trajectory/session_info.py +124 -0
- chrys/app/tui/widgets/trajectory/text_view.py +124 -0
- chrys/app/tui/widgets/trajectory/timeline.py +380 -0
- chrys/app/tui/widgets/welcome.py +126 -0
- chrys/app/tui/widgets/workflow/__init__.py +5 -0
- chrys/app/tui/widgets/workflow/edges.py +89 -0
- chrys/app/tui/widgets/workflow/graph.py +869 -0
- chrys/app/tui/widgets/workflow/info.py +310 -0
- chrys/app/tui/widgets/workflow/node_view.py +27 -0
- chrys/app/tui/widgets/workflow/output.py +225 -0
- chrys/app/tui/widgets/workflow/panel.py +658 -0
- chrys/app/tui/widgets/workflow/projector.py +324 -0
- chrys/app/tui/widgets/workflow/records.py +69 -0
- chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
- chrys/app/tui/widgets/workflow/selection.py +151 -0
- chrys/app/tui/widgets/workflow/source.py +65 -0
- chrys/app/tui/widgets/workflow/text.py +257 -0
- chrys/app/tui/widgets/workflow/values.py +317 -0
- chrys/foundation/__init__.py +0 -0
- chrys/foundation/branding.py +15 -0
- chrys/foundation/config/__init__.py +7 -0
- chrys/foundation/config/coercion.py +374 -0
- chrys/foundation/config/context.py +34 -0
- chrys/foundation/config/env_file.py +141 -0
- chrys/foundation/config/env_layers.py +370 -0
- chrys/foundation/config/migrations.py +521 -0
- chrys/foundation/config/notification_events.py +22 -0
- chrys/foundation/config/process_settings.py +332 -0
- chrys/foundation/config/runtime_pointer.py +133 -0
- chrys/foundation/config/settings.py +1952 -0
- chrys/foundation/config/settings_store.py +996 -0
- chrys/foundation/config/spec.py +396 -0
- chrys/foundation/config/user_settings.py +140 -0
- chrys/foundation/config/warnings.py +262 -0
- chrys/foundation/config/web_values.py +135 -0
- chrys/foundation/config/yaml_store.py +487 -0
- chrys/foundation/errors/__init__.py +68 -0
- chrys/foundation/errors/_legacy.py +117 -0
- chrys/foundation/errors/_text.py +13 -0
- chrys/foundation/errors/_walk.py +85 -0
- chrys/foundation/errors/classify.py +278 -0
- chrys/foundation/errors/display.py +274 -0
- chrys/foundation/errors/formatting.py +283 -0
- chrys/foundation/errors/kinds.py +67 -0
- chrys/foundation/errors/network.py +497 -0
- chrys/foundation/errors/provider.py +445 -0
- chrys/foundation/errors/route.py +85 -0
- chrys/foundation/events/__init__.py +8 -0
- chrys/foundation/events/bus.py +143 -0
- chrys/foundation/events/types.py +1629 -0
- chrys/foundation/events/workflow.py +38 -0
- chrys/foundation/hosted_tools.py +142 -0
- chrys/foundation/i18n/__init__.py +32 -0
- chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
- chrys/foundation/i18n/catalogs.py +42 -0
- chrys/foundation/i18n/formatting.py +202 -0
- chrys/foundation/i18n/locale.py +92 -0
- chrys/foundation/i18n/localizer.py +187 -0
- chrys/foundation/i18n/messages.py +268 -0
- chrys/foundation/io/__init__.py +3 -0
- chrys/foundation/io/result_content.py +237 -0
- chrys/foundation/models/__init__.py +3 -0
- chrys/foundation/models/approval_reuse.py +23 -0
- chrys/foundation/models/ask_user.py +468 -0
- chrys/foundation/models/execution.py +18 -0
- chrys/foundation/models/history_markers.py +115 -0
- chrys/foundation/models/invocations.py +63 -0
- chrys/foundation/models/mutation_scope.py +28 -0
- chrys/foundation/models/session_env.py +87 -0
- chrys/foundation/models/session_surface.py +29 -0
- chrys/foundation/models/todos.py +74 -0
- chrys/foundation/models/turns.py +125 -0
- chrys/foundation/models/workflow_session.py +89 -0
- chrys/foundation/models/workspace.py +59 -0
- chrys/foundation/net/__init__.py +3 -0
- chrys/foundation/net/address_class.py +57 -0
- chrys/foundation/net/route_probe.py +94 -0
- chrys/foundation/net/url.py +107 -0
- chrys/foundation/observability/__init__.py +3 -0
- chrys/foundation/observability/exporters.py +174 -0
- chrys/foundation/observability/gate.py +31 -0
- chrys/foundation/observability/setup.py +497 -0
- chrys/foundation/observability/sink.py +228 -0
- chrys/foundation/patches/__init__.py +19 -0
- chrys/foundation/patches/mdurl_cache.py +59 -0
- chrys/foundation/patches/patcher.py +287 -0
- chrys/foundation/patches/staged_members.py +205 -0
- chrys/foundation/patches/textual_block_border.py +105 -0
- chrys/foundation/patches/textual_button_ansi.py +65 -0
- chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
- chrys/foundation/patches/textual_callback_cache.py +248 -0
- chrys/foundation/patches/textual_callback_dispatch.py +109 -0
- chrys/foundation/patches/textual_compositor_cjk.py +566 -0
- chrys/foundation/patches/textual_dispatch_cache.py +150 -0
- chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
- chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
- chrys/foundation/patches/textual_lru_acyclic.py +241 -0
- chrys/foundation/patches/textual_message_pump.py +136 -0
- chrys/foundation/patches/textual_node_diet.py +875 -0
- chrys/foundation/patches/textual_one_shot_timer.py +48 -0
- chrys/foundation/patches/textual_option_list.py +90 -0
- chrys/foundation/patches/textual_precompose.py +89 -0
- chrys/foundation/patches/textual_pruned_tabs.py +69 -0
- chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
- chrys/foundation/patches/textual_removed_node_caches.py +86 -0
- chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
- chrys/foundation/patches/textual_selection_extract.py +48 -0
- chrys/foundation/patches/textual_strip_cycles.py +119 -0
- chrys/foundation/patches/textual_tab_selection.py +1611 -0
- chrys/foundation/patches/textual_timer_skip.py +70 -0
- chrys/foundation/patches/textual_utf8_decoder.py +106 -0
- chrys/foundation/patches/textual_win_sleep.py +167 -0
- chrys/foundation/patches/watchdog_fsevents.py +70 -0
- chrys/foundation/patches/watchdog_windows.py +52 -0
- chrys/foundation/platform/__init__.py +556 -0
- chrys/foundation/platform/c_api.py +32 -0
- chrys/foundation/platform/child_reap.py +107 -0
- chrys/foundation/platform/command_line.py +95 -0
- chrys/foundation/platform/files.py +1470 -0
- chrys/foundation/platform/output_capture.py +113 -0
- chrys/foundation/platform/paths.py +193 -0
- chrys/foundation/platform/process.py +1468 -0
- chrys/foundation/platform/pty_output.py +19 -0
- chrys/foundation/platform/runtime_paths.py +281 -0
- chrys/foundation/platform/win_console.py +129 -0
- chrys/foundation/reasoning_origin.py +67 -0
- chrys/foundation/recovery.py +16 -0
- chrys/foundation/retry.py +278 -0
- chrys/foundation/text/__init__.py +3 -0
- chrys/foundation/text/encoding.py +603 -0
- chrys/foundation/text/images.py +304 -0
- chrys/foundation/text/lines.py +24 -0
- chrys/foundation/text/mentions.py +90 -0
- chrys/foundation/text/model_json.py +25 -0
- chrys/foundation/text/reading_order.py +20 -0
- chrys/foundation/text/tokenizer.py +25 -0
- chrys/foundation/text/tool_output.py +287 -0
- chrys/foundation/text/yaml_io.py +62 -0
- chrys/foundation/tool_call_context.py +202 -0
- chrys/foundation/tool_execution_stamp.py +184 -0
- chrys/foundation/tool_invocation_order.py +48 -0
- chrys/foundation/tool_kinds.py +102 -0
- chrys/foundation/tool_result_metadata.py +287 -0
- chrys/foundation/trajectory/__init__.py +10 -0
- chrys/foundation/trajectory/context.py +481 -0
- chrys/foundation/trajectory/envelope.py +571 -0
- chrys/foundation/trajectory/event_types.py +294 -0
- chrys/foundation/trajectory/finalizers.py +46 -0
- chrys/foundation/trajectory/fingerprint.py +62 -0
- chrys/foundation/trajectory/ids.py +155 -0
- chrys/foundation/trajectory/keys.py +99 -0
- chrys/foundation/trajectory/lease.py +173 -0
- chrys/foundation/trajectory/metadata.py +72 -0
- chrys/foundation/trajectory/reader.py +271 -0
- chrys/foundation/trajectory/recovery.py +225 -0
- chrys/foundation/trajectory/revisions.py +152 -0
- chrys/foundation/trajectory/segments.py +225 -0
- chrys/foundation/trajectory/tools.py +117 -0
- chrys/foundation/trajectory/usage.py +151 -0
- chrys/foundation/trajectory/writer.py +972 -0
- chrys/foundation/trajectory_timing.py +97 -0
- chrys/foundation/util/__init__.py +3 -0
- chrys/foundation/util/async_tasks.py +29 -0
- chrys/foundation/util/chrys_headers.py +23 -0
- chrys/foundation/util/env_templates.py +104 -0
- chrys/foundation/util/filenames.py +96 -0
- chrys/foundation/util/header_charset.py +138 -0
- chrys/foundation/util/httpx_helpers.py +12 -0
- chrys/foundation/util/lock.py +236 -0
- chrys/foundation/util/once_close.py +46 -0
- chrys/foundation/util/session_ids.py +29 -0
- chrys/foundation/util/shell_tokens.py +88 -0
- chrys/foundation/util/sub_agent_context.py +38 -0
- chrys/foundation/util/time.py +35 -0
- chrys/foundation/util/unicode_scalars.py +36 -0
- chrys/foundation/vendor/__init__.py +43 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
- chrys/kernel/__init__.py +256 -0
- chrys/kernel/_content.py +1866 -0
- chrys/kernel/_loop_recorder.py +631 -0
- chrys/kernel/_null_overlay.py +1026 -0
- chrys/kernel/_result_ceiling.py +216 -0
- chrys/kernel/_serialization.py +431 -0
- chrys/kernel/_tool_arg_errors.py +1629 -0
- chrys/kernel/_tool_execution.py +1172 -0
- chrys/kernel/_tool_expansion.py +26 -0
- chrys/kernel/_types.py +2598 -0
- chrys/kernel/agent.py +994 -0
- chrys/kernel/client.py +1084 -0
- chrys/kernel/compaction.py +1315 -0
- chrys/kernel/exceptions.py +211 -0
- chrys/kernel/exchanges.py +683 -0
- chrys/kernel/identity.py +95 -0
- chrys/kernel/images.py +36 -0
- chrys/kernel/instrumentation.py +1618 -0
- chrys/kernel/loop.py +2308 -0
- chrys/kernel/middleware.py +616 -0
- chrys/kernel/sessions.py +604 -0
- chrys/kernel/tools.py +1061 -0
- chrys/kernel/types.py +126 -0
- chrys/orchestration/__init__.py +3 -0
- chrys/orchestration/engine/__init__.py +3 -0
- chrys/orchestration/engine/assembly.py +236 -0
- chrys/orchestration/engine/build/__init__.py +3 -0
- chrys/orchestration/engine/build/builder.py +1061 -0
- chrys/orchestration/engine/build/construction.py +408 -0
- chrys/orchestration/engine/build/loaded.py +139 -0
- chrys/orchestration/engine/engine.py +789 -0
- chrys/orchestration/engine/execution.py +771 -0
- chrys/orchestration/engine/loader.py +865 -0
- chrys/orchestration/engine/rollback.py +918 -0
- chrys/orchestration/engine/run/__init__.py +3 -0
- chrys/orchestration/engine/run/active_injection.py +419 -0
- chrys/orchestration/engine/run/attachments.py +519 -0
- chrys/orchestration/engine/run/bindings.py +1095 -0
- chrys/orchestration/engine/run/coordinator.py +1062 -0
- chrys/orchestration/engine/run/finalizer.py +417 -0
- chrys/orchestration/engine/run/input_refs.py +60 -0
- chrys/orchestration/engine/run/prompt_content.py +457 -0
- chrys/orchestration/engine/run/resume.py +299 -0
- chrys/orchestration/engine/run/retry.py +948 -0
- chrys/orchestration/engine/run/runner.py +625 -0
- chrys/orchestration/engine/run/runtime_skills.py +146 -0
- chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
- chrys/orchestration/engine/run/turn_hooks.py +258 -0
- chrys/orchestration/engine/run/turn_state.py +149 -0
- chrys/orchestration/engine/run/working_dir.py +50 -0
- chrys/orchestration/engine/session_lifecycle.py +2013 -0
- chrys/orchestration/engine/state/__init__.py +3 -0
- chrys/orchestration/engine/state/active_session.py +183 -0
- chrys/orchestration/engine/state/controls.py +639 -0
- chrys/orchestration/engine/state/current_agent.py +26 -0
- chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
- chrys/orchestration/engine/state/machine.py +193 -0
- chrys/orchestration/engine/state/session_writer.py +434 -0
- chrys/orchestration/engine/trajectory.py +714 -0
- chrys/orchestration/engine/usage.py +118 -0
- chrys/orchestration/invoker/__init__.py +3 -0
- chrys/orchestration/invoker/acp.py +727 -0
- chrys/orchestration/invoker/acp_protocol.py +1115 -0
- chrys/orchestration/invoker/acp_spec.py +79 -0
- chrys/orchestration/invoker/attempts.py +994 -0
- chrys/orchestration/invoker/child_compaction.py +131 -0
- chrys/orchestration/invoker/child_history.py +99 -0
- chrys/orchestration/invoker/contracts.py +253 -0
- chrys/orchestration/invoker/evidence.py +94 -0
- chrys/orchestration/invoker/kernel.py +343 -0
- chrys/orchestration/invoker/origin.py +79 -0
- chrys/orchestration/invoker/resources.py +343 -0
- chrys/orchestration/invoker/runtime.py +299 -0
- chrys/orchestration/invoker/usage.py +80 -0
- chrys/orchestration/session_hooks.py +117 -0
- chrys/orchestration/session_host.py +1023 -0
- chrys/orchestration/session_resources.py +95 -0
- chrys/orchestration/session_usage.py +219 -0
- chrys/orchestration/startup.py +255 -0
- chrys/orchestration/sub_agents/__init__.py +3 -0
- chrys/orchestration/sub_agents/acp_policy.py +269 -0
- chrys/orchestration/sub_agents/kernel_policy.py +848 -0
- chrys/orchestration/sub_agents/registration.py +44 -0
- chrys/orchestration/sub_agents/shell.py +337 -0
- chrys/orchestration/sub_agents/tools.py +2228 -0
- chrys/orchestration/workflows/__init__.py +3 -0
- chrys/orchestration/workflows/agent_archive.py +156 -0
- chrys/orchestration/workflows/agent_node.py +894 -0
- chrys/orchestration/workflows/agent_node_build.py +687 -0
- chrys/orchestration/workflows/catalog.py +332 -0
- chrys/orchestration/workflows/coordinator.py +771 -0
- chrys/orchestration/workflows/hooks.py +176 -0
- chrys/orchestration/workflows/preview.py +322 -0
- chrys/orchestration/workflows/rollback.py +151 -0
- chrys/orchestration/workflows/runner.py +986 -0
- chrys/orchestration/workflows/session.py +390 -0
- chrys/orchestration/workflows/settings.py +97 -0
- chrys/orchestration/workflows/validation.py +674 -0
- chrys/orchestration/workflows/worker_client.py +1002 -0
- chrys/service/__init__.py +3 -0
- chrys/service/acp_client/__init__.py +65 -0
- chrys/service/acp_client/_transport.py +922 -0
- chrys/service/acp_client/client.py +1311 -0
- chrys/service/acp_client/errors.py +224 -0
- chrys/service/acp_client/protocol.py +220 -0
- chrys/service/acp_client/spawn.py +239 -0
- chrys/service/acp_client/spec.py +100 -0
- chrys/service/agent_middleware/__init__.py +60 -0
- chrys/service/agent_middleware/_metadata_keys.py +52 -0
- chrys/service/agent_middleware/control/__init__.py +3 -0
- chrys/service/agent_middleware/control/approval.py +769 -0
- chrys/service/agent_middleware/control/approval_hooks.py +62 -0
- chrys/service/agent_middleware/control/ask_user.py +207 -0
- chrys/service/agent_middleware/control/interrupt.py +50 -0
- chrys/service/agent_middleware/control/sleep.py +118 -0
- chrys/service/agent_middleware/control/todo.py +117 -0
- chrys/service/agent_middleware/events/__init__.py +3 -0
- chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
- chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
- chrys/service/agent_middleware/events/intermediate_text.py +128 -0
- chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
- chrys/service/agent_middleware/events/result_persistence.py +98 -0
- chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
- chrys/service/agent_middleware/events/tool_error_text.py +22 -0
- chrys/service/agent_middleware/events/tool_events.py +779 -0
- chrys/service/agent_middleware/injection.py +510 -0
- chrys/service/agent_middleware/reminders/__init__.py +91 -0
- chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
- chrys/service/agent_middleware/reminders/base.py +25 -0
- chrys/service/agent_middleware/reminders/context_usage.py +74 -0
- chrys/service/agent_middleware/reminders/file_change.py +63 -0
- chrys/service/agent_middleware/reminders/mcp.py +39 -0
- chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
- chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
- chrys/service/agent_middleware/reminders/skills.py +44 -0
- chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
- chrys/service/agent_middleware/reminders/todo.py +35 -0
- chrys/service/agent_middleware/reminders/turn_line.py +48 -0
- chrys/service/agent_middleware/response_validation.py +1380 -0
- chrys/service/agent_middleware/system_reminder.py +1107 -0
- chrys/service/agent_middleware/validators.py +396 -0
- chrys/service/analytics/__init__.py +124 -0
- chrys/service/analytics/_actions.py +896 -0
- chrys/service/analytics/_context_evidence.py +357 -0
- chrys/service/analytics/_critical_path.py +179 -0
- chrys/service/analytics/_facts.py +919 -0
- chrys/service/analytics/_insights.py +769 -0
- chrys/service/analytics/_metric_ops.py +83 -0
- chrys/service/analytics/_session_projection.py +460 -0
- chrys/service/analytics/_timeline.py +434 -0
- chrys/service/analytics/_turn_graph.py +1237 -0
- chrys/service/analytics/_turns.py +1377 -0
- chrys/service/analytics/_workflows.py +171 -0
- chrys/service/analytics/aggregation.py +1474 -0
- chrys/service/analytics/classification.py +128 -0
- chrys/service/analytics/export.py +803 -0
- chrys/service/analytics/findings.py +282 -0
- chrys/service/analytics/math.py +79 -0
- chrys/service/analytics/model.py +886 -0
- chrys/service/analytics/reader.py +321 -0
- chrys/service/approval/__init__.py +3 -0
- chrys/service/approval/arbitration.py +102 -0
- chrys/service/approval/command_identity.py +109 -0
- chrys/service/approval/correlation.py +57 -0
- chrys/service/approval/grant_store.py +108 -0
- chrys/service/approval/judge.py +589 -0
- chrys/service/approval/level.py +15 -0
- chrys/service/approval/policy.py +209 -0
- chrys/service/approval/reuse.py +223 -0
- chrys/service/approval/reuse_binding.py +133 -0
- chrys/service/approval/safety_classifier.py +536 -0
- chrys/service/approval/turn_context.py +49 -0
- chrys/service/context/__init__.py +3 -0
- chrys/service/context/compaction/__init__.py +38 -0
- chrys/service/context/compaction/breaker.py +156 -0
- chrys/service/context/compaction/budgets.py +93 -0
- chrys/service/context/compaction/calibration.py +77 -0
- chrys/service/context/compaction/compression.py +690 -0
- chrys/service/context/compaction/current_turn_drop.py +458 -0
- chrys/service/context/compaction/events.py +201 -0
- chrys/service/context/compaction/exclusions.py +261 -0
- chrys/service/context/compaction/groups.py +361 -0
- chrys/service/context/compaction/last_words.py +1996 -0
- chrys/service/context/compaction/last_words_state.py +752 -0
- chrys/service/context/compaction/scoped.py +472 -0
- chrys/service/context/compaction/spill.py +1947 -0
- chrys/service/context/compaction/strategy.py +1116 -0
- chrys/service/context/compaction/summaries.py +319 -0
- chrys/service/context/compaction/turns.py +375 -0
- chrys/service/context/manager.py +174 -0
- chrys/service/context/memory_loader.py +792 -0
- chrys/service/context/middleware/__init__.py +3 -0
- chrys/service/context/middleware/usage.py +332 -0
- chrys/service/context/providers/__init__.py +3 -0
- chrys/service/context/providers/context_management.py +432 -0
- chrys/service/context/providers/history.py +647 -0
- chrys/service/context/providers/memory.py +50 -0
- chrys/service/context/tool_content.py +22 -0
- chrys/service/hooks/__init__.py +37 -0
- chrys/service/hooks/detached_worker.py +118 -0
- chrys/service/hooks/docs/README.md +14 -0
- chrys/service/hooks/docs/authoring.md +684 -0
- chrys/service/hooks/docs/configuration.md +499 -0
- chrys/service/hooks/docs/design.md +359 -0
- chrys/service/hooks/docs/project-hooks.md +348 -0
- chrys/service/hooks/events.py +157 -0
- chrys/service/hooks/loader.py +665 -0
- chrys/service/hooks/manager.py +807 -0
- chrys/service/hooks/matcher.py +111 -0
- chrys/service/hooks/outbox.py +294 -0
- chrys/service/hooks/runner.py +572 -0
- chrys/service/hooks/schema.py +407 -0
- chrys/service/llm/__init__.py +7 -0
- chrys/service/llm/_structured_outputs.py +192 -0
- chrys/service/llm/anthropic_messages/__init__.py +9 -0
- chrys/service/llm/anthropic_messages/client.py +254 -0
- chrys/service/llm/anthropic_messages/decode.py +331 -0
- chrys/service/llm/anthropic_messages/history.py +279 -0
- chrys/service/llm/anthropic_messages/request.py +323 -0
- chrys/service/llm/anthropic_messages/server_tools.py +319 -0
- chrys/service/llm/anthropic_messages/stream.py +297 -0
- chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
- chrys/service/llm/chat_completions/__init__.py +9 -0
- chrys/service/llm/chat_completions/client.py +369 -0
- chrys/service/llm/chat_completions/decode.py +338 -0
- chrys/service/llm/chat_completions/history.py +657 -0
- chrys/service/llm/chat_completions/reasoning.py +244 -0
- chrys/service/llm/chat_completions/request.py +192 -0
- chrys/service/llm/chat_completions/stream.py +546 -0
- chrys/service/llm/chat_completions/validation.py +308 -0
- chrys/service/llm/clients.py +613 -0
- chrys/service/llm/images.py +65 -0
- chrys/service/llm/mock.py +413 -0
- chrys/service/llm/observer.py +446 -0
- chrys/service/llm/one_shot.py +106 -0
- chrys/service/llm/openai_exceptions.py +93 -0
- chrys/service/llm/openai_responses/__init__.py +10 -0
- chrys/service/llm/openai_responses/client.py +323 -0
- chrys/service/llm/openai_responses/decode.py +432 -0
- chrys/service/llm/openai_responses/history.py +519 -0
- chrys/service/llm/openai_responses/hosted.py +625 -0
- chrys/service/llm/openai_responses/replay.py +629 -0
- chrys/service/llm/openai_responses/request.py +342 -0
- chrys/service/llm/openai_responses/stream.py +847 -0
- chrys/service/llm/openai_timestamps.py +57 -0
- chrys/service/llm/providers.py +107 -0
- chrys/service/llm/proxy_route.py +54 -0
- chrys/service/llm/raw_http_log.py +309 -0
- chrys/service/llm/route_facts.py +63 -0
- chrys/service/llm/route_sessions.py +49 -0
- chrys/service/llm/sdk_anthropic.py +18 -0
- chrys/service/llm/sdk_headers.py +47 -0
- chrys/service/llm/sdk_openai.py +18 -0
- chrys/service/llm/sdk_retry.py +59 -0
- chrys/service/llm/wire_client.py +246 -0
- chrys/service/mcp/__init__.py +3 -0
- chrys/service/mcp/_connection.py +235 -0
- chrys/service/mcp/_http_transport.py +350 -0
- chrys/service/mcp/_progressive.py +459 -0
- chrys/service/mcp/_stdio_transport.py +614 -0
- chrys/service/mcp/_tool_mixins.py +96 -0
- chrys/service/mcp/adapter.py +1031 -0
- chrys/service/mcp/cache.py +476 -0
- chrys/service/mcp/content.py +32 -0
- chrys/service/mcp/errors.py +169 -0
- chrys/service/mcp/owned.py +1330 -0
- chrys/service/mcp/result_limits.py +168 -0
- chrys/service/mcp/thinking_warning.py +95 -0
- chrys/service/mcp/validation.py +74 -0
- chrys/service/mutations/__init__.py +84 -0
- chrys/service/mutations/coordination.py +894 -0
- chrys/service/mutations/detector.py +458 -0
- chrys/service/mutations/git_calibrator.py +397 -0
- chrys/service/mutations/git_state.py +647 -0
- chrys/service/mutations/pipeline.py +779 -0
- chrys/service/mutations/scanner.py +466 -0
- chrys/service/mutations/snapshot_files.py +233 -0
- chrys/service/mutations/store.py +625 -0
- chrys/service/mutations/tool_names.py +15 -0
- chrys/service/mutations/trace.py +246 -0
- chrys/service/mutations/tracker.py +1289 -0
- chrys/service/mutations/types.py +559 -0
- chrys/service/mutations/workspace_changes.py +1583 -0
- chrys/service/profiles/__init__.py +2 -0
- chrys/service/profiles/agents/__init__.py +3 -0
- chrys/service/profiles/agents/builtins/Code.yaml +199 -0
- chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
- chrys/service/profiles/agents/builtins/General.yaml +114 -0
- chrys/service/profiles/agents/builtins/QA.yaml +128 -0
- chrys/service/profiles/agents/loader.py +808 -0
- chrys/service/profiles/agents/registry.py +727 -0
- chrys/service/profiles/agents/schema.py +371 -0
- chrys/service/profiles/agents/serializer.py +196 -0
- chrys/service/profiles/coercion.py +22 -0
- chrys/service/profiles/models/__init__.py +3 -0
- chrys/service/profiles/models/env_bridge.py +157 -0
- chrys/service/profiles/models/loader.py +282 -0
- chrys/service/profiles/models/options.py +438 -0
- chrys/service/profiles/models/registry.py +104 -0
- chrys/service/profiles/models/resolver.py +286 -0
- chrys/service/profiles/models/schema.py +91 -0
- chrys/service/profiles/models/serializer.py +76 -0
- chrys/service/session/__init__.py +3 -0
- chrys/service/session/checkpoint.py +156 -0
- chrys/service/session/history.py +1616 -0
- chrys/service/session/message_metadata.py +151 -0
- chrys/service/session/persistence.py +435 -0
- chrys/service/session/runtime_metadata.py +210 -0
- chrys/service/session/sub_agent_logs.py +1069 -0
- chrys/service/session/sub_agent_transcript.py +654 -0
- chrys/service/skills/__init__.py +3 -0
- chrys/service/skills/adapter.py +408 -0
- chrys/service/skills/constants.py +8 -0
- chrys/service/skills/loader.py +575 -0
- chrys/service/skills/model.py +130 -0
- chrys/service/skills/provider.py +841 -0
- chrys/service/skills/runner.py +390 -0
- chrys/service/state/__init__.py +3 -0
- chrys/service/state/_fork.py +710 -0
- chrys/service/state/_session_files.py +175 -0
- chrys/service/state/_session_meta.py +552 -0
- chrys/service/state/locks.py +154 -0
- chrys/service/state/serializers.py +156 -0
- chrys/service/state/session_catalog.py +361 -0
- chrys/service/state/session_listing.py +84 -0
- chrys/service/state/session_migration.py +446 -0
- chrys/service/state/session_mru.py +315 -0
- chrys/service/state/store.py +2132 -0
- chrys/service/state/workflow.py +158 -0
- chrys/service/text_blocks.py +47 -0
- chrys/service/todos/__init__.py +3 -0
- chrys/service/todos/reminder.py +31 -0
- chrys/service/todos/tracker.py +58 -0
- chrys/service/tools/__init__.py +3 -0
- chrys/service/tools/approval_targets.py +92 -0
- chrys/service/tools/builtins/__init__.py +3 -0
- chrys/service/tools/builtins/ask_user.py +326 -0
- chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
- chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
- chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
- chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
- chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
- chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
- chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
- chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
- chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
- chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
- chrys/service/tools/builtins/doc_converter/registry.py +55 -0
- chrys/service/tools/builtins/filesystem.py +1071 -0
- chrys/service/tools/builtins/search.py +1265 -0
- chrys/service/tools/builtins/shell.py +967 -0
- chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
- chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
- chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
- chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
- chrys/service/tools/builtins/shell_filter.py +24 -0
- chrys/service/tools/builtins/sleep.py +91 -0
- chrys/service/tools/builtins/todo.py +64 -0
- chrys/service/tools/builtins/web/__init__.py +6 -0
- chrys/service/tools/builtins/web/build.py +268 -0
- chrys/service/tools/builtins/web/config.py +338 -0
- chrys/service/tools/builtins/web/credentials.py +37 -0
- chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
- chrys/service/tools/builtins/web/fetch/cache.py +68 -0
- chrys/service/tools/builtins/web/fetch/convert.py +56 -0
- chrys/service/tools/builtins/web/fetch/tool.py +223 -0
- chrys/service/tools/builtins/web/hosted.py +61 -0
- chrys/service/tools/builtins/web/http.py +330 -0
- chrys/service/tools/builtins/web/progress.py +18 -0
- chrys/service/tools/builtins/web/search/__init__.py +3 -0
- chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
- chrys/service/tools/builtins/web/search/html.py +152 -0
- chrys/service/tools/builtins/web/search/json_api.py +227 -0
- chrys/service/tools/builtins/web/search/providers.py +77 -0
- chrys/service/tools/builtins/web/search/tool.py +218 -0
- chrys/service/tools/builtins/web/search/types.py +28 -0
- chrys/service/tools/builtins/web/tls_identity.py +66 -0
- chrys/service/tools/kinds.py +115 -0
- chrys/service/tools/names.py +47 -0
- chrys/service/tools/registry.py +242 -0
- chrys/service/tools/result_metadata.py +103 -0
- chrys/service/tools/session_artifacts.py +196 -0
- chrys/service/tools/spill.py +218 -0
- chrys/service/tools/workspace_paths.py +81 -0
- chrys/service/trajectory/__init__.py +11 -0
- chrys/service/trajectory/approvals.py +201 -0
- chrys/service/trajectory/compaction.py +420 -0
- chrys/service/trajectory/fork.py +75 -0
- chrys/service/trajectory/hooks.py +217 -0
- chrys/service/trajectory/items.py +41 -0
- chrys/service/trajectory/preparation.py +374 -0
- chrys/service/trajectory/retries.py +95 -0
- chrys/service/trajectory/revisions.py +129 -0
- chrys/service/trajectory/segmented.py +163 -0
- chrys/service/trajectory/session.py +762 -0
- chrys/service/trajectory/state.py +83 -0
- chrys/service/trajectory/sub_agents.py +169 -0
- chrys/service/trajectory/tombstone.py +262 -0
- chrys/service/trajectory/tools.py +323 -0
- chrys/service/trajectory/validation.py +130 -0
- chrys/service/trajectory/waits.py +161 -0
- chrys/service/trajectory/workflow.py +148 -0
- chrys/service/vision.py +128 -0
- chrys/service/workflows/__init__.py +3 -0
- chrys/service/workflows/admission.py +151 -0
- chrys/service/workflows/artifacts.py +109 -0
- chrys/service/workflows/asks.py +130 -0
- chrys/service/workflows/builtins/__init__.py +12 -0
- chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
- chrys/service/workflows/builtins/demo-workflow.py +756 -0
- chrys/service/workflows/discovery.py +554 -0
- chrys/service/workflows/environment.py +284 -0
- chrys/service/workflows/graph.py +430 -0
- chrys/service/workflows/history.py +82 -0
- chrys/service/workflows/interpreter.py +107 -0
- chrys/service/workflows/journal.py +334 -0
- chrys/service/workflows/layout.py +56 -0
- chrys/service/workflows/ledger.py +149 -0
- chrys/service/workflows/model_selection.py +18 -0
- chrys/service/workflows/orphans.py +209 -0
- chrys/service/workflows/outcomes.py +46 -0
- chrys/service/workflows/protocol.py +210 -0
- chrys/service/workflows/records.py +142 -0
- chrys/service/workflows/scheduler.py +770 -0
- chrys/service/workflows/sdk/__init__.py +51 -0
- chrys/service/workflows/sdk/_ask.py +167 -0
- chrys/service/workflows/sdk/_builder.py +781 -0
- chrys/service/workflows/sdk/_values.py +92 -0
- chrys/service/workflows/sdk_artifact.py +74 -0
- chrys/service/workflows/store.py +494 -0
- chrys/service/workflows/transcript.py +102 -0
- chrys/service/workflows/values.py +73 -0
- chrys/service/workflows/worker_host.py +1820 -0
- chrys/workflows.py +38 -0
- icode_tui-0.29.1.dist-info/METADATA +104 -0
- icode_tui-0.29.1.dist-info/RECORD +1138 -0
- icode_tui-0.29.1.dist-info/WHEEL +4 -0
- icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
- icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
- icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
# Copyright (c) 2021 Will McGugan
|
|
2
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
3
|
+
# Contains code adapted from Textual (MIT License; see NOTICE).
|
|
4
|
+
|
|
5
|
+
"""Patch: a Textual reflow replays the arrangement of every subtree that has not changed.
|
|
6
|
+
|
|
7
|
+
Problem
|
|
8
|
+
-------
|
|
9
|
+
Each layout refresh (``Screen._refresh_layout`` → ``Compositor.reflow``) arranges the whole
|
|
10
|
+
widget tree from scratch: box models, placements and map entries for every mounted transcript
|
|
11
|
+
widget, and then compares every widget's old and new geometry. A streaming reply, a sidebar tab
|
|
12
|
+
switch or a panel toggle changes a handful of widgets, yet each frame's layout costs time and
|
|
13
|
+
garbage in proportion to the mounted transcript.
|
|
14
|
+
|
|
15
|
+
Solution
|
|
16
|
+
--------
|
|
17
|
+
Nodes carry the layout epoch in which they, or a node beneath them, last changed in a way the
|
|
18
|
+
compositor reads (``DOMNode._mark_layout_dirty``): a mount, a child-list update, any
|
|
19
|
+
``refresh()``, a layout or scroll change, an absolute offset, anchoring, a raw style-rule
|
|
20
|
+
write (``Styles.set_rule``/``clear_rule``, which chrome widgets use to skip layout escalation)
|
|
21
|
+
or an anchored widget's container-size change. The compositor pins an anchored widget from its
|
|
22
|
+
container size, which ``_size_updated`` stores only after the arrangement that pinned it, and
|
|
23
|
+
nothing else stamps the widget when its scrollbar is hidden. A reactive that refreshes stamps
|
|
24
|
+
before its watchers run rather than at that refresh: a watcher may look up geometry, which can
|
|
25
|
+
rebuild the full map (``TextArea`` sizes its wrapping when its scrollbar shows).
|
|
26
|
+
A full arrangement records what arranging each widget produced, and the next reflow replays the
|
|
27
|
+
record of every subtree unchanged since then, translating it when only its origin moved.
|
|
28
|
+
``Widget.layers`` takes the outermost ``layers`` declaration among a widget and its ancestors, so
|
|
29
|
+
a declaration above a subtree restacks it without stamping it: a record also keeps the
|
|
30
|
+
declaration its widget inherits, ``None`` when none does, which an explicit ``default`` must not
|
|
31
|
+
match.
|
|
32
|
+
Replayed placements are the recorded objects, so the reflow diff settles most widgets by
|
|
33
|
+
identity.
|
|
34
|
+
|
|
35
|
+
Only a reflow from ``Screen._refresh_layout``, ``full_map`` or ``reflow_reusing_records`` reuses
|
|
36
|
+
records. The last serves code that writes raw style rules outside a layout pass (chrome visibility
|
|
37
|
+
and width flips) and then resynchronizes the compositor: the rule writes stamp their widgets, so
|
|
38
|
+
only those widgets' ancestor paths are arranged again. ``_arrange_root`` keeps its signature and
|
|
39
|
+
takes the choice from ``_reuse_next_arrangement``. As upstream's, a reflow leaves a pending
|
|
40
|
+
full-map rebuild pending: a repaint outside the visible map requests one, and a widget shown after
|
|
41
|
+
the reflow then reports the region its next layout gives it. That rebuild must not decide which
|
|
42
|
+
widgets are new, though: a reflow reports Show against the map the previous reflow produced, so a
|
|
43
|
+
widget shown in between still gets Show and its first Resize after a lookup mapped it. Tests set
|
|
44
|
+
``textual._compositor._VERIFY_REUSE`` to check every reusing arrangement against a from-scratch
|
|
45
|
+
one that starts from the same scroll and anchor state: anchoring and ``arrange`` overrides such
|
|
46
|
+
as the chat panel's write that state, so arranging twice in a row can differ. The check compares
|
|
47
|
+
the state each leaves as well as the maps, and restores the layout epochs the from-scratch
|
|
48
|
+
arrangement's watchers stamp, which would otherwise hide a change that was not stamped.
|
|
49
|
+
``Widget._set_dirty`` also stops marking the lines of a styles cache it has just cleared:
|
|
50
|
+
reading ``self.size`` for that could rebuild the full map.
|
|
51
|
+
|
|
52
|
+
The patch is runtime only. Its fragments span nine modules, and file patches apply all or
|
|
53
|
+
nothing per file: after a Textual change a compositor could be rewritten to reuse records while
|
|
54
|
+
another module kept the upstream code that does not stamp, painting stale placements from then
|
|
55
|
+
on. The runtime install stages every module before installing any, and must precede the first
|
|
56
|
+
widget: ``absolute_offset``, ``_anchored`` and ``_anchor_released`` become properties, which
|
|
57
|
+
hide the values an existing instance keeps in its ``__dict__``.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
from __future__ import annotations
|
|
61
|
+
|
|
62
|
+
import logging
|
|
63
|
+
from typing import TYPE_CHECKING, Protocol, TypeGuard
|
|
64
|
+
|
|
65
|
+
from chrys.foundation.patches.patcher import FilePatch
|
|
66
|
+
from chrys.foundation.patches.staged_members import StagedSourceDriftError, members_installed, stage_members
|
|
67
|
+
|
|
68
|
+
if TYPE_CHECKING:
|
|
69
|
+
from textual._compositor import Compositor, ReflowResult
|
|
70
|
+
from textual.geometry import Size
|
|
71
|
+
from textual.widget import Widget
|
|
72
|
+
|
|
73
|
+
_RUNTIME_PATCH_MARKER = "_chrys_reflow_reuse"
|
|
74
|
+
_RUNTIME_PATCH_TEXTUAL_VERSION = "8.2.7"
|
|
75
|
+
_RUNTIME_MEMBERS: dict[str, dict[str, tuple[str, ...]]] = {
|
|
76
|
+
"textual._compositor": {
|
|
77
|
+
"Compositor": (
|
|
78
|
+
"__init__",
|
|
79
|
+
"clear",
|
|
80
|
+
"reflow",
|
|
81
|
+
"full_map",
|
|
82
|
+
"_arrange_root",
|
|
83
|
+
"_arrange",
|
|
84
|
+
"_arrangement_state",
|
|
85
|
+
"_restore_arrangement_state",
|
|
86
|
+
"_verify_reuse",
|
|
87
|
+
),
|
|
88
|
+
},
|
|
89
|
+
"textual.css.styles": {"Styles": ("clear_rule", "set_rule")},
|
|
90
|
+
"textual._node_list": {"NodeList": ("updated",)},
|
|
91
|
+
"textual.dom": {"DOMNode": ("_layout_epoch", "_layout_dirty_epoch", "_mark_layout_dirty", "_on_mounted_state")},
|
|
92
|
+
"textual.message_pump": {"MessagePump": ("_pre_process", "_on_mounted_state", "_process_messages_loop")},
|
|
93
|
+
"textual.reactive": {"Reactive": ("_set",)},
|
|
94
|
+
"textual.screen": {"Screen": ("_refresh_layout",)},
|
|
95
|
+
"textual.scroll_view": {"ScrollView": ("_size_updated",)},
|
|
96
|
+
"textual.widget": {
|
|
97
|
+
"Widget": (
|
|
98
|
+
"_absolute_offset",
|
|
99
|
+
"_anchored_flag",
|
|
100
|
+
"_anchor_released_flag",
|
|
101
|
+
"absolute_offset",
|
|
102
|
+
"_anchored",
|
|
103
|
+
"_anchor_released",
|
|
104
|
+
"_set_dirty",
|
|
105
|
+
"set_scroll",
|
|
106
|
+
"_refresh_scroll",
|
|
107
|
+
"refresh",
|
|
108
|
+
"_check_refresh",
|
|
109
|
+
"_size_updated",
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
logger = logging.getLogger(__name__)
|
|
114
|
+
|
|
115
|
+
# _compositor.py: <import>
|
|
116
|
+
_REFLOW_1_OLD = """\
|
|
117
|
+
from textual._context import visible_screen_stack
|
|
118
|
+
from textual._loop import loop_last
|
|
119
|
+
from textual.geometry import NULL_SPACING, Offset, Region, Size, Spacing
|
|
120
|
+
from textual.map_geometry import MapGeometry"""
|
|
121
|
+
|
|
122
|
+
_REFLOW_1_NEW = """\
|
|
123
|
+
from textual._context import visible_screen_stack
|
|
124
|
+
from textual._loop import loop_last
|
|
125
|
+
from textual.dom import DOMNode
|
|
126
|
+
from textual.geometry import NULL_SPACING, Offset, Region, Size, Spacing
|
|
127
|
+
from textual.map_geometry import MapGeometry"""
|
|
128
|
+
|
|
129
|
+
# _compositor.py: <if>, _ArrangeRecord=, _VERIFY_REUSE=
|
|
130
|
+
_REFLOW_2_OLD = """\
|
|
131
|
+
from typing_extensions import TypeAlias
|
|
132
|
+
|
|
133
|
+
from textual.screen import Screen
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
_REFLOW_2_NEW = """\
|
|
138
|
+
from typing_extensions import TypeAlias
|
|
139
|
+
|
|
140
|
+
from textual.layout import WidgetPlacement
|
|
141
|
+
from textual.screen import Screen
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
_VERIFY_REUSE = False
|
|
145
|
+
\"\"\"Check every reusing arrangement against a from-scratch one (a debug aid for tests).\"\"\"
|
|
146
|
+
|
|
147
|
+
_ArrangeRecord: TypeAlias = "tuple[int, tuple[Region, Region, tuple[tuple[int, int, int], ...], int, Region, bool, Spacing, tuple[str, ...] | None], bool, set[Widget] | None, tuple[Widget, MapGeometry] | None, list[Widget], list[tuple[Widget, MapGeometry]], Region | None, frozenset[Widget], bool]"
|
|
148
|
+
\"\"\"What arranging one widget produced: (epoch, arguments, visible, arranged widgets, cover entry, placed children,
|
|
149
|
+
map entries, children's clipping region, overlay children, whether the subtree may be translated).\"\"\"
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
# _compositor.py: Compositor.__init__, Compositor.clear
|
|
154
|
+
_REFLOW_3_OLD = """\
|
|
155
|
+
self._layers_visible: list[list[tuple[Widget, Region, Region]]] | None = None
|
|
156
|
+
|
|
157
|
+
def clear(self) -> None:
|
|
158
|
+
\"\"\"Remove all references to widgets (used when the screen closes).\"\"\""""
|
|
159
|
+
|
|
160
|
+
_REFLOW_3_NEW = """\
|
|
161
|
+
self._layers_visible: list[list[tuple[Widget, Region, Region]]] | None = None
|
|
162
|
+
|
|
163
|
+
# What the last full arrangement produced per widget, for replaying unchanged subtrees
|
|
164
|
+
self._arrange_records: dict[Widget, _ArrangeRecord] = {}
|
|
165
|
+
self._arrange_records_size: Size | None = None
|
|
166
|
+
self._arrange_records_root: Widget | None = None
|
|
167
|
+
# Set by a caller for the next `_arrange_root` call, which consumes it
|
|
168
|
+
self._reuse_next_arrangement = False
|
|
169
|
+
# The map the last reflow produced, which Show is reported against
|
|
170
|
+
self._reflowed_map: CompositorMap = {}
|
|
171
|
+
|
|
172
|
+
def clear(self) -> None:
|
|
173
|
+
\"\"\"Remove all references to widgets (used when the screen closes).\"\"\""""
|
|
174
|
+
|
|
175
|
+
# _compositor.py: Compositor._regions_to_spans, Compositor.clear
|
|
176
|
+
_REFLOW_4_OLD = """\
|
|
177
|
+
self._visible_widgets = None
|
|
178
|
+
self._layers_visible = None
|
|
179
|
+
|
|
180
|
+
@classmethod"""
|
|
181
|
+
|
|
182
|
+
_REFLOW_4_NEW = """\
|
|
183
|
+
self._visible_widgets = None
|
|
184
|
+
self._layers_visible = None
|
|
185
|
+
self._arrange_records = {}
|
|
186
|
+
self._arrange_records_root = None
|
|
187
|
+
self._reflowed_map = {}
|
|
188
|
+
|
|
189
|
+
@classmethod"""
|
|
190
|
+
|
|
191
|
+
# _compositor.py: Compositor.__rich_repr__, Compositor.reflow
|
|
192
|
+
_REFLOW_5_OLD = """\
|
|
193
|
+
yield "widgets", self.widgets
|
|
194
|
+
|
|
195
|
+
def reflow(self, parent: Widget, size: Size) -> ReflowResult:
|
|
196
|
+
\"\"\"Reflow (layout) widget and its children.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
_REFLOW_5_NEW = """\
|
|
200
|
+
yield "widgets", self.widgets
|
|
201
|
+
|
|
202
|
+
def reflow(self, parent: Widget, size: Size, reuse: bool = False) -> ReflowResult:
|
|
203
|
+
\"\"\"Reflow (layout) widget and its children.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
# _compositor.py: Compositor.reflow
|
|
207
|
+
_REFLOW_6_OLD = """\
|
|
208
|
+
Args:
|
|
209
|
+
parent: The root widget.
|
|
210
|
+
size: Size of the area to be filled.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Hidden, shown, and resized widgets."""
|
|
214
|
+
|
|
215
|
+
_REFLOW_6_NEW = """\
|
|
216
|
+
Args:
|
|
217
|
+
parent: The root widget.
|
|
218
|
+
size: Size of the area to be filled.
|
|
219
|
+
reuse: Replay the placements of subtrees unchanged since the last full arrangement.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Hidden, shown, and resized widgets."""
|
|
223
|
+
|
|
224
|
+
# _compositor.py: Compositor.reflow
|
|
225
|
+
_REFLOW_7_OLD = """\
|
|
226
|
+
old_widgets = old_map.keys()
|
|
227
|
+
|
|
228
|
+
map, widgets = self._arrange_root(parent, size, visible_only=False)
|
|
229
|
+
|
|
230
|
+
new_widgets = map.keys()
|
|
231
|
+
|
|
232
|
+
# Newly visible widgets
|
|
233
|
+
shown_widgets = new_widgets - old_widgets
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
_REFLOW_7_NEW = """\
|
|
237
|
+
old_widgets = old_map.keys()
|
|
238
|
+
|
|
239
|
+
self._reuse_next_arrangement = reuse
|
|
240
|
+
state = self._arrangement_state(parent) if reuse and _VERIFY_REUSE else None
|
|
241
|
+
map, widgets = self._arrange_root(parent, size, visible_only=False)
|
|
242
|
+
if state is not None:
|
|
243
|
+
self._verify_reuse(parent, size, map, widgets, state)
|
|
244
|
+
|
|
245
|
+
new_widgets = map.keys()
|
|
246
|
+
|
|
247
|
+
# Newly visible widgets: those the last reflow did not place. A geometry lookup since
|
|
248
|
+
# then may have rebuilt the full map with them already in it, and compared with that
|
|
249
|
+
# map they would never get Show, or the Resize a shown widget gets.
|
|
250
|
+
shown_widgets = new_widgets - self._reflowed_map.keys()
|
|
251
|
+
self._reflowed_map = map
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
# _compositor.py: Compositor.reflow
|
|
255
|
+
_REFLOW_8_OLD = """\
|
|
256
|
+
# Replace map and widgets
|
|
257
|
+
self._full_map = map
|
|
258
|
+
self.widgets = widgets
|
|
259
|
+
|
|
260
|
+
# Contains widgets + geometry for every widget that changed (added, removed, or updated)
|
|
261
|
+
changes = map.items() ^ old_map.items()
|
|
262
|
+
|
|
263
|
+
# Widgets in both new and old
|
|
264
|
+
common_widgets = old_widgets & new_widgets
|
|
265
|
+
|
|
266
|
+
# Mark dirty regions.
|
|
267
|
+
screen_region = size.region
|
|
268
|
+
if screen_region not in self._dirty_regions:
|
|
269
|
+
regions = {
|
|
270
|
+
region
|
|
271
|
+
for region in (
|
|
272
|
+
map_geometry.clip.intersection(map_geometry.region)
|
|
273
|
+
for _, map_geometry in changes
|
|
274
|
+
)
|
|
275
|
+
if region
|
|
276
|
+
}
|
|
277
|
+
self._dirty_regions.update(regions)
|
|
278
|
+
|
|
279
|
+
resized_widgets = {
|
|
280
|
+
widget
|
|
281
|
+
for widget, (region, *_) in changes
|
|
282
|
+
if (widget in common_widgets and old_map[widget].region.size != region.size)
|
|
283
|
+
}
|
|
284
|
+
return ReflowResult(
|
|
285
|
+
hidden=hidden_widgets,"""
|
|
286
|
+
|
|
287
|
+
_REFLOW_8_NEW = """\
|
|
288
|
+
# Replace map and widgets
|
|
289
|
+
self._full_map = map
|
|
290
|
+
self.widgets = widgets
|
|
291
|
+
|
|
292
|
+
# Compare each widget's geometry with its previous one (the symmetric difference of
|
|
293
|
+
# the two maps' items). Replayed placements are shared objects, so most unchanged
|
|
294
|
+
# widgets are settled by identity.
|
|
295
|
+
screen_region = size.region
|
|
296
|
+
mark_dirty = screen_region not in self._dirty_regions
|
|
297
|
+
dirty_regions: set[Region] = set()
|
|
298
|
+
add_dirty_region = dirty_regions.add
|
|
299
|
+
resized_widgets: set[Widget] = set()
|
|
300
|
+
add_resized_widget = resized_widgets.add
|
|
301
|
+
get_old_geometry = old_map.get
|
|
302
|
+
intersection = Region.intersection
|
|
303
|
+
for widget, geometry in map.items():
|
|
304
|
+
old_geometry = get_old_geometry(widget)
|
|
305
|
+
if old_geometry is geometry:
|
|
306
|
+
continue
|
|
307
|
+
if old_geometry is not None:
|
|
308
|
+
if old_geometry == geometry:
|
|
309
|
+
continue
|
|
310
|
+
old_region = old_geometry[0]
|
|
311
|
+
region = geometry[0]
|
|
312
|
+
if old_region[2] != region[2] or old_region[3] != region[3]:
|
|
313
|
+
add_resized_widget(widget)
|
|
314
|
+
if mark_dirty:
|
|
315
|
+
add_dirty_region(intersection(old_geometry[2], old_region))
|
|
316
|
+
if mark_dirty:
|
|
317
|
+
add_dirty_region(intersection(geometry[2], geometry[0]))
|
|
318
|
+
if mark_dirty:
|
|
319
|
+
for widget in old_widgets - new_widgets:
|
|
320
|
+
old_geometry = old_map[widget]
|
|
321
|
+
add_dirty_region(intersection(old_geometry[2], old_geometry[0]))
|
|
322
|
+
self._dirty_regions.update(region for region in dirty_regions if region)
|
|
323
|
+
|
|
324
|
+
return ReflowResult(
|
|
325
|
+
hidden=hidden_widgets,"""
|
|
326
|
+
|
|
327
|
+
# _compositor.py: Compositor.full_map
|
|
328
|
+
_REFLOW_9_OLD = """\
|
|
329
|
+
if self._full_map_invalidated:
|
|
330
|
+
self._full_map_invalidated = False
|
|
331
|
+
map, _widgets = self._arrange_root(self.root, self.size, visible_only=False)
|
|
332
|
+
# Update any widgets which became visible in the interim
|
|
333
|
+
self._full_map = map"""
|
|
334
|
+
|
|
335
|
+
_REFLOW_9_NEW = """\
|
|
336
|
+
if self._full_map_invalidated:
|
|
337
|
+
self._full_map_invalidated = False
|
|
338
|
+
self._reuse_next_arrangement = True
|
|
339
|
+
state = self._arrangement_state(self.root) if _VERIFY_REUSE else None
|
|
340
|
+
map, _widgets = self._arrange_root(self.root, self.size, visible_only=False)
|
|
341
|
+
if state is not None:
|
|
342
|
+
self._verify_reuse(self.root, self.size, map, _widgets, state)
|
|
343
|
+
# Update any widgets which became visible in the interim
|
|
344
|
+
self._full_map = map"""
|
|
345
|
+
|
|
346
|
+
# _compositor.py: Compositor._arrange, Compositor._arrange_root
|
|
347
|
+
_REFLOW_10_OLD = """\
|
|
348
|
+
\"\"\"Arrange a widget's children based on its layout attribute.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
root: Top level widget.
|
|
352
|
+
size: Size of visible area (screen).
|
|
353
|
+
visible_only: Only update visible widgets (used in scrolling).
|
|
354
|
+
|
|
355
|
+
Returns:"""
|
|
356
|
+
|
|
357
|
+
_REFLOW_10_NEW = """\
|
|
358
|
+
\"\"\"Arrange a widget's children based on its layout attribute.
|
|
359
|
+
|
|
360
|
+
A full arrangement records its placements. It replays those of subtrees
|
|
361
|
+
unchanged since the last one only if its caller set `_reuse_next_arrangement`,
|
|
362
|
+
which this call consumes.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
root: Top level widget.
|
|
366
|
+
size: Size of visible area (screen).
|
|
367
|
+
visible_only: Only update visible widgets (used in scrolling).
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Compositor map and set of widgets.
|
|
371
|
+
\"\"\"
|
|
372
|
+
reuse = self._reuse_next_arrangement
|
|
373
|
+
self._reuse_next_arrangement = False
|
|
374
|
+
return self._arrange(
|
|
375
|
+
root, size, visible_only, reuse=reuse, record=not visible_only
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def _arrange(
|
|
379
|
+
self,
|
|
380
|
+
root: Widget,
|
|
381
|
+
size: Size,
|
|
382
|
+
visible_only: bool,
|
|
383
|
+
*,
|
|
384
|
+
reuse: bool,
|
|
385
|
+
record: bool,
|
|
386
|
+
) -> tuple[CompositorMap, set[Widget]]:
|
|
387
|
+
\"\"\"Arrange a widget's children (see `_arrange_root`).
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
root: Top level widget.
|
|
391
|
+
size: Size of visible area (screen).
|
|
392
|
+
visible_only: Only update visible widgets (used in scrolling).
|
|
393
|
+
reuse: Replay the recorded placements of subtrees that have not changed
|
|
394
|
+
since they were recorded (full arrangements only).
|
|
395
|
+
record: Record placements so a later arrangement may reuse them
|
|
396
|
+
(full arrangements only).
|
|
397
|
+
|
|
398
|
+
Returns:"""
|
|
399
|
+
|
|
400
|
+
# _compositor.py: Compositor._arrange
|
|
401
|
+
_REFLOW_11_OLD = """\
|
|
402
|
+
no_clip = size.region
|
|
403
|
+
|
|
404
|
+
def add_widget(
|
|
405
|
+
widget: Widget,"""
|
|
406
|
+
|
|
407
|
+
_REFLOW_11_NEW = """\
|
|
408
|
+
no_clip = size.region
|
|
409
|
+
|
|
410
|
+
records: dict[Widget, _ArrangeRecord] | None = None
|
|
411
|
+
old_records: dict[Widget, _ArrangeRecord] | None = None
|
|
412
|
+
epoch = 0
|
|
413
|
+
if record:
|
|
414
|
+
DOMNode._layout_epoch += 1
|
|
415
|
+
epoch = DOMNode._layout_epoch
|
|
416
|
+
records = {}
|
|
417
|
+
if (
|
|
418
|
+
reuse
|
|
419
|
+
and self._arrange_records_size == size
|
|
420
|
+
and self._arrange_records_root is root
|
|
421
|
+
):
|
|
422
|
+
old_records = self._arrange_records
|
|
423
|
+
|
|
424
|
+
def replay(widget: Widget, arrange_record: _ArrangeRecord) -> None:
|
|
425
|
+
\"\"\"Re-emit the recorded placements of an unchanged subtree.\"\"\"
|
|
426
|
+
records[widget] = arrange_record
|
|
427
|
+
_, _, visible, arranged_widgets, cover_entry, children, entries, *_ = (
|
|
428
|
+
arrange_record
|
|
429
|
+
)
|
|
430
|
+
if visible:
|
|
431
|
+
add_new_widget(widget)
|
|
432
|
+
else:
|
|
433
|
+
add_new_invisible_widget(widget)
|
|
434
|
+
if arranged_widgets is not None:
|
|
435
|
+
widgets.update(arranged_widgets)
|
|
436
|
+
if cover_entry is not None:
|
|
437
|
+
map[cover_entry[0]] = cover_entry[1]
|
|
438
|
+
for child in children:
|
|
439
|
+
replay(child, old_records[child])
|
|
440
|
+
for chrome_widget, geometry in entries:
|
|
441
|
+
map[chrome_widget] = geometry
|
|
442
|
+
|
|
443
|
+
def replay_moved(
|
|
444
|
+
widget: Widget,
|
|
445
|
+
arrange_record: _ArrangeRecord,
|
|
446
|
+
dx: int,
|
|
447
|
+
dy: int,
|
|
448
|
+
arrange_args: tuple,
|
|
449
|
+
_MapGeometry: type[MapGeometry] = MapGeometry,
|
|
450
|
+
_Region: type[Region] = Region,
|
|
451
|
+
_new: Callable[..., tuple] = tuple.__new__,
|
|
452
|
+
) -> None:
|
|
453
|
+
\"\"\"Re-emit an unchanged subtree whose position or clip changed.
|
|
454
|
+
|
|
455
|
+
Every region within the subtree moves by `(dx, dy)`; clips are re-derived
|
|
456
|
+
from the new clip and the (moved) regions that clip children.
|
|
457
|
+
\"\"\"
|
|
458
|
+
# Regions and geometries are built with `tuple.__new__`, which skips the
|
|
459
|
+
# named tuples' Python-level constructors: this runs for every moved widget.
|
|
460
|
+
(
|
|
461
|
+
record_epoch,
|
|
462
|
+
_,
|
|
463
|
+
visible,
|
|
464
|
+
arranged_widgets,
|
|
465
|
+
cover_entry,
|
|
466
|
+
children,
|
|
467
|
+
entries,
|
|
468
|
+
child_region,
|
|
469
|
+
overlay_children,
|
|
470
|
+
translatable,
|
|
471
|
+
) = arrange_record
|
|
472
|
+
clip = arrange_args[4]
|
|
473
|
+
if visible:
|
|
474
|
+
add_new_widget(widget)
|
|
475
|
+
else:
|
|
476
|
+
add_new_invisible_widget(widget)
|
|
477
|
+
if arranged_widgets is not None:
|
|
478
|
+
widgets.update(arranged_widgets)
|
|
479
|
+
if cover_entry is not None:
|
|
480
|
+
cover_widget, (
|
|
481
|
+
(x, y, width, height),
|
|
482
|
+
order,
|
|
483
|
+
_,
|
|
484
|
+
virtual_size,
|
|
485
|
+
container_size,
|
|
486
|
+
virtual_region,
|
|
487
|
+
dock_gutter,
|
|
488
|
+
) = cover_entry
|
|
489
|
+
geometry = _new(
|
|
490
|
+
_MapGeometry,
|
|
491
|
+
(
|
|
492
|
+
_new(_Region, (x + dx, y + dy, width, height)),
|
|
493
|
+
order,
|
|
494
|
+
clip,
|
|
495
|
+
virtual_size,
|
|
496
|
+
container_size,
|
|
497
|
+
virtual_region,
|
|
498
|
+
dock_gutter,
|
|
499
|
+
),
|
|
500
|
+
)
|
|
501
|
+
cover_entry = (cover_widget, geometry)
|
|
502
|
+
map[cover_widget] = geometry
|
|
503
|
+
if child_region is not None:
|
|
504
|
+
x, y, width, height = child_region
|
|
505
|
+
child_region = _new(_Region, (x + dx, y + dy, width, height))
|
|
506
|
+
sub_clip = clip.intersection(child_region)
|
|
507
|
+
for child in children:
|
|
508
|
+
child_record = old_records[child]
|
|
509
|
+
(
|
|
510
|
+
child_virtual_region,
|
|
511
|
+
(x, y, width, height),
|
|
512
|
+
child_order,
|
|
513
|
+
child_layer_order,
|
|
514
|
+
_,
|
|
515
|
+
child_visible,
|
|
516
|
+
child_dock_gutter,
|
|
517
|
+
child_inherited_layers,
|
|
518
|
+
) = child_record[1]
|
|
519
|
+
replay_moved(
|
|
520
|
+
child,
|
|
521
|
+
child_record,
|
|
522
|
+
dx,
|
|
523
|
+
dy,
|
|
524
|
+
(
|
|
525
|
+
child_virtual_region,
|
|
526
|
+
_new(_Region, (x + dx, y + dy, width, height)),
|
|
527
|
+
child_order,
|
|
528
|
+
child_layer_order,
|
|
529
|
+
no_clip if child in overlay_children else sub_clip,
|
|
530
|
+
child_visible,
|
|
531
|
+
child_dock_gutter,
|
|
532
|
+
child_inherited_layers,
|
|
533
|
+
),
|
|
534
|
+
)
|
|
535
|
+
if entries:
|
|
536
|
+
# Chrome (scrollbars) first, the widget itself last; chrome regions are
|
|
537
|
+
# absolute, the widget's virtual region is relative to its container.
|
|
538
|
+
last = len(entries) - 1
|
|
539
|
+
moved_entries: list[tuple[Widget, MapGeometry]] = []
|
|
540
|
+
for index, (
|
|
541
|
+
chrome_widget,
|
|
542
|
+
(
|
|
543
|
+
(x, y, width, height),
|
|
544
|
+
order,
|
|
545
|
+
_,
|
|
546
|
+
virtual_size,
|
|
547
|
+
container_size,
|
|
548
|
+
virtual_region,
|
|
549
|
+
dock_gutter,
|
|
550
|
+
),
|
|
551
|
+
) in enumerate(entries):
|
|
552
|
+
if index != last:
|
|
553
|
+
vx, vy, vwidth, vheight = virtual_region
|
|
554
|
+
virtual_region = _new(
|
|
555
|
+
_Region, (vx + dx, vy + dy, vwidth, vheight)
|
|
556
|
+
)
|
|
557
|
+
moved = _new(
|
|
558
|
+
_MapGeometry,
|
|
559
|
+
(
|
|
560
|
+
_new(_Region, (x + dx, y + dy, width, height)),
|
|
561
|
+
order,
|
|
562
|
+
clip,
|
|
563
|
+
virtual_size,
|
|
564
|
+
container_size,
|
|
565
|
+
virtual_region,
|
|
566
|
+
dock_gutter,
|
|
567
|
+
),
|
|
568
|
+
)
|
|
569
|
+
map[chrome_widget] = moved
|
|
570
|
+
moved_entries.append((chrome_widget, moved))
|
|
571
|
+
entries = moved_entries
|
|
572
|
+
records[widget] = (
|
|
573
|
+
record_epoch,
|
|
574
|
+
arrange_args,
|
|
575
|
+
visible,
|
|
576
|
+
arranged_widgets,
|
|
577
|
+
cover_entry,
|
|
578
|
+
children,
|
|
579
|
+
entries,
|
|
580
|
+
child_region,
|
|
581
|
+
overlay_children,
|
|
582
|
+
translatable,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
def add_widget(
|
|
586
|
+
widget: Widget,"""
|
|
587
|
+
|
|
588
|
+
# _compositor.py: Compositor._arrange
|
|
589
|
+
_REFLOW_12_OLD = """\
|
|
590
|
+
dock_gutter: Spacing,
|
|
591
|
+
_MapGeometry: type[MapGeometry] = MapGeometry,
|
|
592
|
+
) -> None:
|
|
593
|
+
\"\"\"Called recursively to place a widget and its children in the map.
|
|
594
|
+
"""
|
|
595
|
+
|
|
596
|
+
_REFLOW_12_NEW = """\
|
|
597
|
+
dock_gutter: Spacing,
|
|
598
|
+
inherited_layers: tuple[str, ...] | None,
|
|
599
|
+
_MapGeometry: type[MapGeometry] = MapGeometry,
|
|
600
|
+
) -> bool:
|
|
601
|
+
\"\"\"Called recursively to place a widget and its children in the map.
|
|
602
|
+
"""
|
|
603
|
+
|
|
604
|
+
# _compositor.py: Compositor._arrange
|
|
605
|
+
_REFLOW_13_OLD = """\
|
|
606
|
+
visible: Whether the widget should be visible by default.
|
|
607
|
+
This may be overridden by the CSS rule `visibility`.
|
|
608
|
+
\"\"\"
|
|
609
|
+
if not widget._is_mounted:
|
|
610
|
+
return
|
|
611
|
+
styles = widget.styles
|
|
612
|
+
"""
|
|
613
|
+
|
|
614
|
+
_REFLOW_13_NEW = """\
|
|
615
|
+
visible: Whether the widget should be visible by default.
|
|
616
|
+
This may be overridden by the CSS rule `visibility`.
|
|
617
|
+
inherited_layers: The outermost `layers` declaration among the widget's
|
|
618
|
+
ancestors, or `None` if none declares any.
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
`True` if the widget was placed, `False` if it isn't mounted.
|
|
622
|
+
\"\"\"
|
|
623
|
+
if not widget._is_mounted:
|
|
624
|
+
return False
|
|
625
|
+
|
|
626
|
+
if records is not None:
|
|
627
|
+
arrange_args = (
|
|
628
|
+
virtual_region,
|
|
629
|
+
region,
|
|
630
|
+
order,
|
|
631
|
+
layer_order,
|
|
632
|
+
clip,
|
|
633
|
+
visible,
|
|
634
|
+
dock_gutter,
|
|
635
|
+
inherited_layers,
|
|
636
|
+
)
|
|
637
|
+
if old_records is not None:
|
|
638
|
+
arrange_record = old_records.get(widget)
|
|
639
|
+
if (
|
|
640
|
+
arrange_record is not None
|
|
641
|
+
and widget._layout_dirty_epoch < arrange_record[0]
|
|
642
|
+
):
|
|
643
|
+
old_args = arrange_record[1]
|
|
644
|
+
if old_args == arrange_args:
|
|
645
|
+
replay(widget, arrange_record)
|
|
646
|
+
return True
|
|
647
|
+
old_region = old_args[1]
|
|
648
|
+
if (
|
|
649
|
+
arrange_record[9]
|
|
650
|
+
and old_region.size == region.size
|
|
651
|
+
and old_args[0] == virtual_region
|
|
652
|
+
and old_args[2] == order
|
|
653
|
+
and old_args[3] == layer_order
|
|
654
|
+
and old_args[5] == visible
|
|
655
|
+
and old_args[6] == dock_gutter
|
|
656
|
+
and old_args[7] == inherited_layers
|
|
657
|
+
):
|
|
658
|
+
replay_moved(
|
|
659
|
+
widget,
|
|
660
|
+
arrange_record,
|
|
661
|
+
region[0] - old_region[0],
|
|
662
|
+
region[1] - old_region[1],
|
|
663
|
+
arrange_args,
|
|
664
|
+
)
|
|
665
|
+
return True
|
|
666
|
+
|
|
667
|
+
styles = widget.styles
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
# _compositor.py: Compositor._arrange
|
|
671
|
+
_REFLOW_14_OLD = """\
|
|
672
|
+
else:
|
|
673
|
+
add_new_invisible_widget(widget)
|
|
674
|
+
|
|
675
|
+
# Container region is minus border"""
|
|
676
|
+
|
|
677
|
+
_REFLOW_14_NEW = """\
|
|
678
|
+
else:
|
|
679
|
+
add_new_invisible_widget(widget)
|
|
680
|
+
|
|
681
|
+
arranged_widgets: set[Widget] | None = None
|
|
682
|
+
cover_entry: tuple[Widget, MapGeometry] | None = None
|
|
683
|
+
children: list[Widget] = []
|
|
684
|
+
entries: list[tuple[Widget, MapGeometry]] = []
|
|
685
|
+
clip_region: Region | None = None
|
|
686
|
+
overlay_children: frozenset[Widget] = frozenset()
|
|
687
|
+
translatable = True
|
|
688
|
+
|
|
689
|
+
# Container region is minus border"""
|
|
690
|
+
|
|
691
|
+
# _compositor.py: Compositor._arrange
|
|
692
|
+
_REFLOW_15_OLD = """\
|
|
693
|
+
)
|
|
694
|
+
)
|
|
695
|
+
widget.set_reactive(Widget.scroll_y, new_scroll_y)
|
|
696
|
+
widget.set_reactive(Widget.scroll_target_y, new_scroll_y)"""
|
|
697
|
+
|
|
698
|
+
_REFLOW_15_NEW = """\
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
if new_scroll_y != widget.scroll_y:
|
|
702
|
+
# Placements beneath this widget move with its scroll offset.
|
|
703
|
+
widget._mark_layout_dirty()
|
|
704
|
+
widget.set_reactive(Widget.scroll_y, new_scroll_y)
|
|
705
|
+
widget.set_reactive(Widget.scroll_target_y, new_scroll_y)"""
|
|
706
|
+
|
|
707
|
+
# _compositor.py: Compositor._arrange
|
|
708
|
+
_REFLOW_16_OLD = """\
|
|
709
|
+
placement_scroll_offset = placement_offset - widget.scroll_offset
|
|
710
|
+
|
|
711
|
+
placements = [
|
|
712
|
+
placement.process_offset(size.region, placement_scroll_offset)
|
|
713
|
+
for placement in placements
|
|
714
|
+
]
|
|
715
|
+
|
|
716
|
+
layers_to_index = {"""
|
|
717
|
+
|
|
718
|
+
_REFLOW_16_NEW = """\
|
|
719
|
+
placement_scroll_offset = placement_offset - widget.scroll_offset
|
|
720
|
+
|
|
721
|
+
if records is None:
|
|
722
|
+
placements = [
|
|
723
|
+
placement.process_offset(
|
|
724
|
+
size.region, placement_scroll_offset
|
|
725
|
+
)
|
|
726
|
+
for placement in placements
|
|
727
|
+
]
|
|
728
|
+
else:
|
|
729
|
+
clip_region = child_region
|
|
730
|
+
processed_placements: list[WidgetPlacement] = []
|
|
731
|
+
for placement in placements:
|
|
732
|
+
placement_widget = placement.widget
|
|
733
|
+
if (
|
|
734
|
+
placement_widget.absolute_offset is not None
|
|
735
|
+
or placement_widget.styles.has_any_rules(
|
|
736
|
+
"constrain_x", "constrain_y"
|
|
737
|
+
)
|
|
738
|
+
):
|
|
739
|
+
# Placed relative to the screen: moving this subtree
|
|
740
|
+
# would not simply move the placement.
|
|
741
|
+
translatable = False
|
|
742
|
+
placement = placement.process_offset(
|
|
743
|
+
size.region, placement_scroll_offset
|
|
744
|
+
)
|
|
745
|
+
processed_placements.append(placement)
|
|
746
|
+
placements = processed_placements
|
|
747
|
+
|
|
748
|
+
# `Widget.layers` takes the outermost declaration, which a replayed
|
|
749
|
+
# subtree inherits without being stamped when it changes.
|
|
750
|
+
children_inherited_layers = inherited_layers
|
|
751
|
+
if children_inherited_layers is None and styles.has_rule("layers"):
|
|
752
|
+
children_inherited_layers = styles.layers
|
|
753
|
+
|
|
754
|
+
layers_to_index = {"""
|
|
755
|
+
|
|
756
|
+
# _compositor.py: Compositor._arrange
|
|
757
|
+
_REFLOW_17_OLD = """\
|
|
758
|
+
|
|
759
|
+
if widget._cover_widget is not None:
|
|
760
|
+
map[widget._cover_widget] = _MapGeometry(
|
|
761
|
+
region.shrink(widget.styles.gutter),
|
|
762
|
+
order,
|
|
763
|
+
clip,
|
|
764
|
+
region.size,
|
|
765
|
+
container_size,
|
|
766
|
+
virtual_region,
|
|
767
|
+
dock_gutter,
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
# Add all the widgets"""
|
|
771
|
+
|
|
772
|
+
_REFLOW_17_NEW = """\
|
|
773
|
+
|
|
774
|
+
if widget._cover_widget is not None:
|
|
775
|
+
cover_entry = (
|
|
776
|
+
widget._cover_widget,
|
|
777
|
+
_MapGeometry(
|
|
778
|
+
region.shrink(widget.styles.gutter),
|
|
779
|
+
order,
|
|
780
|
+
clip,
|
|
781
|
+
region.size,
|
|
782
|
+
container_size,
|
|
783
|
+
virtual_region,
|
|
784
|
+
dock_gutter,
|
|
785
|
+
),
|
|
786
|
+
)
|
|
787
|
+
map[cover_entry[0]] = cover_entry[1]
|
|
788
|
+
|
|
789
|
+
# Add all the widgets"""
|
|
790
|
+
|
|
791
|
+
# _compositor.py: Compositor._arrange
|
|
792
|
+
_REFLOW_18_OLD = """\
|
|
793
|
+
|
|
794
|
+
if widget._cover_widget is None:
|
|
795
|
+
add_widget(
|
|
796
|
+
sub_widget,
|
|
797
|
+
sub_region,"""
|
|
798
|
+
|
|
799
|
+
_REFLOW_18_NEW = """\
|
|
800
|
+
|
|
801
|
+
if widget._cover_widget is None:
|
|
802
|
+
if add_widget(
|
|
803
|
+
sub_widget,
|
|
804
|
+
sub_region,"""
|
|
805
|
+
|
|
806
|
+
# _compositor.py: Compositor._arrange
|
|
807
|
+
_REFLOW_19_OLD = """\
|
|
808
|
+
visible,
|
|
809
|
+
arrange_result.scroll_spacing,
|
|
810
|
+
)
|
|
811
|
+
layer_order -= 1
|
|
812
|
+
else:"""
|
|
813
|
+
|
|
814
|
+
_REFLOW_19_NEW = """\
|
|
815
|
+
visible,
|
|
816
|
+
arrange_result.scroll_spacing,
|
|
817
|
+
children_inherited_layers,
|
|
818
|
+
):
|
|
819
|
+
children.append(sub_widget)
|
|
820
|
+
if overlay:
|
|
821
|
+
overlay_children = overlay_children | {sub_widget}
|
|
822
|
+
layer_order -= 1
|
|
823
|
+
else:"""
|
|
824
|
+
|
|
825
|
+
# _compositor.py: Compositor._arrange
|
|
826
|
+
_REFLOW_20_OLD = """\
|
|
827
|
+
container_region
|
|
828
|
+
):
|
|
829
|
+
map[chrome_widget] = _MapGeometry(
|
|
830
|
+
chrome_region,
|
|
831
|
+
order,
|
|
832
|
+
clip,
|
|
833
|
+
container_size,
|
|
834
|
+
container_size,
|
|
835
|
+
chrome_region,
|
|
836
|
+
dock_gutter,
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
map[widget._render_widget] = _MapGeometry(
|
|
840
|
+
region,
|
|
841
|
+
order,
|
|
842
|
+
clip,
|
|
843
|
+
total_region.size,
|
|
844
|
+
container_size,
|
|
845
|
+
virtual_region,
|
|
846
|
+
dock_gutter,
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
elif visible:
|
|
850
|
+
# Add the widget to the map
|
|
851
|
+
map[widget._render_widget] = _MapGeometry(
|
|
852
|
+
region,
|
|
853
|
+
order,
|
|
854
|
+
clip,
|
|
855
|
+
region.size,
|
|
856
|
+
container_size,
|
|
857
|
+
virtual_region,
|
|
858
|
+
dock_gutter,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# Add top level (root) widget"""
|
|
862
|
+
|
|
863
|
+
_REFLOW_20_NEW = """\
|
|
864
|
+
container_region
|
|
865
|
+
):
|
|
866
|
+
entries.append(
|
|
867
|
+
(
|
|
868
|
+
chrome_widget,
|
|
869
|
+
_MapGeometry(
|
|
870
|
+
chrome_region,
|
|
871
|
+
order,
|
|
872
|
+
clip,
|
|
873
|
+
container_size,
|
|
874
|
+
container_size,
|
|
875
|
+
chrome_region,
|
|
876
|
+
dock_gutter,
|
|
877
|
+
),
|
|
878
|
+
)
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
entries.append(
|
|
882
|
+
(
|
|
883
|
+
widget._render_widget,
|
|
884
|
+
_MapGeometry(
|
|
885
|
+
region,
|
|
886
|
+
order,
|
|
887
|
+
clip,
|
|
888
|
+
total_region.size,
|
|
889
|
+
container_size,
|
|
890
|
+
virtual_region,
|
|
891
|
+
dock_gutter,
|
|
892
|
+
),
|
|
893
|
+
)
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
elif visible:
|
|
897
|
+
# Add the widget to the map
|
|
898
|
+
entries.append(
|
|
899
|
+
(
|
|
900
|
+
widget._render_widget,
|
|
901
|
+
_MapGeometry(
|
|
902
|
+
region,
|
|
903
|
+
order,
|
|
904
|
+
clip,
|
|
905
|
+
region.size,
|
|
906
|
+
container_size,
|
|
907
|
+
virtual_region,
|
|
908
|
+
dock_gutter,
|
|
909
|
+
),
|
|
910
|
+
)
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
for chrome_widget, geometry in entries:
|
|
914
|
+
map[chrome_widget] = geometry
|
|
915
|
+
|
|
916
|
+
if records is not None:
|
|
917
|
+
if translatable:
|
|
918
|
+
for child in children:
|
|
919
|
+
if not records[child][9]:
|
|
920
|
+
translatable = False
|
|
921
|
+
break
|
|
922
|
+
records[widget] = (
|
|
923
|
+
epoch,
|
|
924
|
+
arrange_args,
|
|
925
|
+
visible,
|
|
926
|
+
arranged_widgets,
|
|
927
|
+
cover_entry,
|
|
928
|
+
children,
|
|
929
|
+
entries,
|
|
930
|
+
clip_region,
|
|
931
|
+
overlay_children,
|
|
932
|
+
translatable,
|
|
933
|
+
)
|
|
934
|
+
return True
|
|
935
|
+
|
|
936
|
+
root_inherited_layers: tuple[str, ...] | None = None
|
|
937
|
+
ancestor = root._parent
|
|
938
|
+
while isinstance(ancestor, Widget):
|
|
939
|
+
if ancestor.styles.has_rule("layers"):
|
|
940
|
+
root_inherited_layers = ancestor.styles.layers
|
|
941
|
+
ancestor = ancestor._parent
|
|
942
|
+
|
|
943
|
+
# Add top level (root) widget"""
|
|
944
|
+
|
|
945
|
+
# _compositor.py: Compositor._arrange, Compositor._arrangement_state, Compositor._restore_arrangement_state, Compositor._verify_reuse, Compositor.layers
|
|
946
|
+
_REFLOW_21_OLD = """\
|
|
947
|
+
NULL_SPACING,
|
|
948
|
+
)
|
|
949
|
+
widgets -= invisible_widgets
|
|
950
|
+
return map, widgets
|
|
951
|
+
|
|
952
|
+
@property"""
|
|
953
|
+
|
|
954
|
+
_REFLOW_21_NEW = """\
|
|
955
|
+
NULL_SPACING,
|
|
956
|
+
root_inherited_layers,
|
|
957
|
+
)
|
|
958
|
+
# The nested functions refer to themselves through closure cells; clearing the
|
|
959
|
+
# cells breaks that cycle, so this arrangement's maps and records are freed by
|
|
960
|
+
# reference counting instead of waiting for the cyclic garbage collector.
|
|
961
|
+
del add_widget, replay, replay_moved
|
|
962
|
+
widgets -= invisible_widgets
|
|
963
|
+
if records is not None:
|
|
964
|
+
self._arrange_records = records
|
|
965
|
+
self._arrange_records_size = size
|
|
966
|
+
self._arrange_records_root = root
|
|
967
|
+
return map, widgets
|
|
968
|
+
|
|
969
|
+
@staticmethod
|
|
970
|
+
def _arrangement_state(
|
|
971
|
+
root: Widget,
|
|
972
|
+
) -> list[tuple[DOMNode, tuple[str, ...], dict[str, object]]]:
|
|
973
|
+
\"\"\"Snapshot the state an arrangement writes (debug aid).
|
|
974
|
+
|
|
975
|
+
Scroll offsets, anchoring and container sizes: anchoring and `arrange` overrides
|
|
976
|
+
change them, so a second arrangement only matches the first when it starts from the
|
|
977
|
+
same state. And every node's layout epoch, which the watchers of the offsets an
|
|
978
|
+
arrangement sets stamp, from the node they refresh up to the App.
|
|
979
|
+
\"\"\"
|
|
980
|
+
widget_names = (
|
|
981
|
+
"_reactive_scroll_x",
|
|
982
|
+
"_reactive_scroll_y",
|
|
983
|
+
"_reactive_scroll_target_x",
|
|
984
|
+
"_reactive_scroll_target_y",
|
|
985
|
+
"_anchored_flag",
|
|
986
|
+
"_anchor_released_flag",
|
|
987
|
+
"_container_size",
|
|
988
|
+
"_layout_dirty_epoch",
|
|
989
|
+
)
|
|
990
|
+
scrollbar_names = ("_reactive_position", "_layout_dirty_epoch")
|
|
991
|
+
epoch_names = ("_layout_dirty_epoch",)
|
|
992
|
+
nodes: list[tuple[DOMNode, tuple[str, ...]]] = [
|
|
993
|
+
(ancestor, epoch_names) for ancestor in root.ancestors
|
|
994
|
+
]
|
|
995
|
+
for node in root.walk_children(with_self=True):
|
|
996
|
+
if not isinstance(node, Widget):
|
|
997
|
+
nodes.append((node, epoch_names))
|
|
998
|
+
continue
|
|
999
|
+
nodes.append((node, widget_names if node.is_scrollable else epoch_names))
|
|
1000
|
+
nodes.extend(
|
|
1001
|
+
(scrollbar, scrollbar_names)
|
|
1002
|
+
for scrollbar in (node._vertical_scrollbar, node._horizontal_scrollbar)
|
|
1003
|
+
if scrollbar is not None
|
|
1004
|
+
)
|
|
1005
|
+
if node._scrollbar_corner is not None:
|
|
1006
|
+
nodes.append((node._scrollbar_corner, epoch_names))
|
|
1007
|
+
return [
|
|
1008
|
+
(
|
|
1009
|
+
node,
|
|
1010
|
+
names,
|
|
1011
|
+
{name: vars(node)[name] for name in names if name in vars(node)},
|
|
1012
|
+
)
|
|
1013
|
+
for node, names in nodes
|
|
1014
|
+
]
|
|
1015
|
+
|
|
1016
|
+
@staticmethod
|
|
1017
|
+
def _restore_arrangement_state(
|
|
1018
|
+
state: list[tuple[DOMNode, tuple[str, ...], dict[str, object]]],
|
|
1019
|
+
) -> None:
|
|
1020
|
+
\"\"\"Restore a snapshot taken by `_arrangement_state` (debug aid).\"\"\"
|
|
1021
|
+
for node, names, values in state:
|
|
1022
|
+
namespace = vars(node)
|
|
1023
|
+
for name in names:
|
|
1024
|
+
if name in values:
|
|
1025
|
+
namespace[name] = values[name]
|
|
1026
|
+
else:
|
|
1027
|
+
namespace.pop(name, None)
|
|
1028
|
+
|
|
1029
|
+
def _verify_reuse(
|
|
1030
|
+
self,
|
|
1031
|
+
root: Widget,
|
|
1032
|
+
size: Size,
|
|
1033
|
+
map: CompositorMap,
|
|
1034
|
+
widgets: set[Widget],
|
|
1035
|
+
state: list[tuple[DOMNode, tuple[str, ...], dict[str, object]]],
|
|
1036
|
+
) -> None:
|
|
1037
|
+
\"\"\"Check a reusing arrangement against a from-scratch one (debug aid).
|
|
1038
|
+
|
|
1039
|
+
The from-scratch arrangement starts from the state the reusing one started from. Both
|
|
1040
|
+
must place the same widgets and leave the same scroll, anchor and container state,
|
|
1041
|
+
which the map does not show for a widget that scrolls its own content. The state the
|
|
1042
|
+
reusing one left is restored afterwards, layout epochs included: the from-scratch
|
|
1043
|
+
arrangement's watchers stamp the widgets they refresh, which would invalidate the
|
|
1044
|
+
records under check and hide a change that was not stamped.
|
|
1045
|
+
\"\"\"
|
|
1046
|
+
after = self._arrangement_state(root)
|
|
1047
|
+
self._restore_arrangement_state(state)
|
|
1048
|
+
try:
|
|
1049
|
+
full_map, full_widgets = self._arrange(
|
|
1050
|
+
root, size, False, reuse=False, record=False
|
|
1051
|
+
)
|
|
1052
|
+
full_after = self._arrangement_state(root)
|
|
1053
|
+
finally:
|
|
1054
|
+
self._restore_arrangement_state(after)
|
|
1055
|
+
unset = object()
|
|
1056
|
+
full_values = {
|
|
1057
|
+
(node, name): value
|
|
1058
|
+
for node, _, values in full_after
|
|
1059
|
+
for name, value in values.items()
|
|
1060
|
+
}
|
|
1061
|
+
state_changed = [
|
|
1062
|
+
(node, name, values.get(name, unset), full_values.get((node, name), unset))
|
|
1063
|
+
for node, names, values in after
|
|
1064
|
+
for name in names
|
|
1065
|
+
if name != "_layout_dirty_epoch"
|
|
1066
|
+
and values.get(name, unset) != full_values.get((node, name), unset)
|
|
1067
|
+
]
|
|
1068
|
+
if (
|
|
1069
|
+
list(full_map.items()) != list(map.items())
|
|
1070
|
+
or full_widgets != widgets
|
|
1071
|
+
or state_changed
|
|
1072
|
+
):
|
|
1073
|
+
missing = full_map.keys() - map.keys()
|
|
1074
|
+
extra = map.keys() - full_map.keys()
|
|
1075
|
+
changed = [
|
|
1076
|
+
(widget, map[widget], geometry)
|
|
1077
|
+
for widget, geometry in full_map.items()
|
|
1078
|
+
if widget in map and map[widget] != geometry
|
|
1079
|
+
]
|
|
1080
|
+
raise AssertionError(
|
|
1081
|
+
"compositor reuse diverged: "
|
|
1082
|
+
f"missing={list(missing)[:5]} extra={list(extra)[:5]} "
|
|
1083
|
+
f"changed={changed[:3]} "
|
|
1084
|
+
f"order_equal={list(full_map) == list(map)} "
|
|
1085
|
+
f"widgets_missing={list(full_widgets - widgets)[:5]} "
|
|
1086
|
+
f"widgets_extra={list(widgets - full_widgets)[:5]} "
|
|
1087
|
+
f"state_changed={state_changed[:3]}"
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
@property"""
|
|
1091
|
+
|
|
1092
|
+
# _node_list.py: NodeList.updated
|
|
1093
|
+
_REFLOW_22_OLD = """\
|
|
1094
|
+
self._updates += 1
|
|
1095
|
+
node = None if self._parent is None else self._parent()
|
|
1096
|
+
while node is not None and (node := node._parent) is not None:
|
|
1097
|
+
node._nodes._updates += 1"""
|
|
1098
|
+
|
|
1099
|
+
_REFLOW_22_NEW = """\
|
|
1100
|
+
self._updates += 1
|
|
1101
|
+
node = None if self._parent is None else self._parent()
|
|
1102
|
+
if node is not None:
|
|
1103
|
+
node._mark_layout_dirty()
|
|
1104
|
+
while node is not None and (node := node._parent) is not None:
|
|
1105
|
+
node._nodes._updates += 1"""
|
|
1106
|
+
|
|
1107
|
+
# dom.py: DOMNode._PSEUDO_CLASSES, DOMNode.__init__, DOMNode._layout_dirty_epoch, DOMNode._layout_epoch, DOMNode._mark_layout_dirty, DOMNode._on_mounted_state
|
|
1108
|
+
_REFLOW_23_OLD = """\
|
|
1109
|
+
_PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App[Any]], bool]]] = {}
|
|
1110
|
+
\"\"\"Pseudo class checks.\"\"\"
|
|
1111
|
+
|
|
1112
|
+
def __init__("""
|
|
1113
|
+
|
|
1114
|
+
_REFLOW_23_NEW = """\
|
|
1115
|
+
_PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App[Any]], bool]]] = {}
|
|
1116
|
+
\"\"\"Pseudo class checks.\"\"\"
|
|
1117
|
+
|
|
1118
|
+
_layout_epoch: ClassVar[int] = 1
|
|
1119
|
+
\"\"\"Global arrangement epoch, advanced by every full compositor arrangement.\"\"\"
|
|
1120
|
+
|
|
1121
|
+
_layout_dirty_epoch: int = 0
|
|
1122
|
+
\"\"\"The epoch in which this node, or a node beneath it, last changed in a way the compositor reads.\"\"\"
|
|
1123
|
+
|
|
1124
|
+
def _mark_layout_dirty(self) -> None:
|
|
1125
|
+
\"\"\"Record that this node's compositor placement may have changed.
|
|
1126
|
+
|
|
1127
|
+
Stamps the node and its ancestors with the current epoch, stopping at the first
|
|
1128
|
+
ancestor already stamped (whose own ancestors are then stamped too).
|
|
1129
|
+
\"\"\"
|
|
1130
|
+
epoch = DOMNode._layout_epoch
|
|
1131
|
+
node: DOMNode | None = self
|
|
1132
|
+
while node is not None and node._layout_dirty_epoch != epoch:
|
|
1133
|
+
node._layout_dirty_epoch = epoch
|
|
1134
|
+
node = node._parent
|
|
1135
|
+
|
|
1136
|
+
def _on_mounted_state(self) -> None:
|
|
1137
|
+
# The compositor skips unmounted widgets, so mounting changes placement.
|
|
1138
|
+
self._mark_layout_dirty()
|
|
1139
|
+
|
|
1140
|
+
def __init__("""
|
|
1141
|
+
|
|
1142
|
+
# message_pump.py: MessagePump._on_mounted_state, MessagePump._post_mount, MessagePump._pre_process
|
|
1143
|
+
_REFLOW_24_OLD = """\
|
|
1144
|
+
self._mounted_event.set()
|
|
1145
|
+
self._is_mounted = True
|
|
1146
|
+
return True
|
|
1147
|
+
|
|
1148
|
+
def _post_mount(self):"""
|
|
1149
|
+
|
|
1150
|
+
_REFLOW_24_NEW = """\
|
|
1151
|
+
self._mounted_event.set()
|
|
1152
|
+
self._is_mounted = True
|
|
1153
|
+
self._on_mounted_state()
|
|
1154
|
+
return True
|
|
1155
|
+
|
|
1156
|
+
def _on_mounted_state(self) -> None:
|
|
1157
|
+
\"\"\"Called once `_is_mounted` becomes `True`.\"\"\"
|
|
1158
|
+
|
|
1159
|
+
def _post_mount(self):"""
|
|
1160
|
+
|
|
1161
|
+
# message_pump.py: MessagePump._process_messages_loop
|
|
1162
|
+
_REFLOW_25_OLD = """\
|
|
1163
|
+
self._mounted_event.set()
|
|
1164
|
+
self._is_mounted = True
|
|
1165
|
+
self.app._handle_exception(error)
|
|
1166
|
+
break"""
|
|
1167
|
+
|
|
1168
|
+
_REFLOW_25_NEW = """\
|
|
1169
|
+
self._mounted_event.set()
|
|
1170
|
+
self._is_mounted = True
|
|
1171
|
+
self._on_mounted_state()
|
|
1172
|
+
self.app._handle_exception(error)
|
|
1173
|
+
break"""
|
|
1174
|
+
|
|
1175
|
+
# screen.py: Screen._refresh_layout
|
|
1176
|
+
_REFLOW_26_OLD = """\
|
|
1177
|
+
|
|
1178
|
+
else:
|
|
1179
|
+
hidden, shown, resized = self._compositor.reflow(self, size)
|
|
1180
|
+
self._layout_widgets.clear()
|
|
1181
|
+
Hide = events.Hide"""
|
|
1182
|
+
|
|
1183
|
+
_REFLOW_26_NEW = """\
|
|
1184
|
+
|
|
1185
|
+
else:
|
|
1186
|
+
hidden, shown, resized = self._compositor.reflow(
|
|
1187
|
+
self, size, reuse=True
|
|
1188
|
+
)
|
|
1189
|
+
self._layout_widgets.clear()
|
|
1190
|
+
Hide = events.Hide"""
|
|
1191
|
+
|
|
1192
|
+
# widget.py: Widget._absolute_offset, Widget._anchor_released, Widget._anchor_released_flag, Widget._anchored, Widget._anchored_flag, Widget._cover, Widget.absolute_offset, Widget.pre_render
|
|
1193
|
+
_REFLOW_27_OLD = """\
|
|
1194
|
+
\"\"\"
|
|
1195
|
+
self._visual_style = None
|
|
1196
|
+
|
|
1197
|
+
def _cover(self, widget: Widget) -> None:"""
|
|
1198
|
+
|
|
1199
|
+
_REFLOW_27_NEW = """\
|
|
1200
|
+
\"\"\"
|
|
1201
|
+
self._visual_style = None
|
|
1202
|
+
|
|
1203
|
+
# Backing fields of the properties below, which stamp the layout epoch on change.
|
|
1204
|
+
_absolute_offset: Offset | None = None
|
|
1205
|
+
_anchored_flag: bool = False
|
|
1206
|
+
_anchor_released_flag: bool = False
|
|
1207
|
+
|
|
1208
|
+
@property
|
|
1209
|
+
def absolute_offset(self) -> Offset | None:
|
|
1210
|
+
\"\"\"Force an absolute offset for the widget (used by tooltips).\"\"\"
|
|
1211
|
+
return self._absolute_offset
|
|
1212
|
+
|
|
1213
|
+
@absolute_offset.setter
|
|
1214
|
+
def absolute_offset(self, offset: Offset | None) -> None:
|
|
1215
|
+
if offset != self._absolute_offset:
|
|
1216
|
+
self._absolute_offset = offset
|
|
1217
|
+
self._mark_layout_dirty()
|
|
1218
|
+
|
|
1219
|
+
@property
|
|
1220
|
+
def _anchored(self) -> bool:
|
|
1221
|
+
\"\"\"Has this widget been anchored?\"\"\"
|
|
1222
|
+
return self._anchored_flag
|
|
1223
|
+
|
|
1224
|
+
@_anchored.setter
|
|
1225
|
+
def _anchored(self, anchored: bool) -> None:
|
|
1226
|
+
if anchored != self._anchored_flag:
|
|
1227
|
+
self._anchored_flag = anchored
|
|
1228
|
+
self._mark_layout_dirty()
|
|
1229
|
+
|
|
1230
|
+
@property
|
|
1231
|
+
def _anchor_released(self) -> bool:
|
|
1232
|
+
\"\"\"Has the anchor been released?\"\"\"
|
|
1233
|
+
return self._anchor_released_flag
|
|
1234
|
+
|
|
1235
|
+
@_anchor_released.setter
|
|
1236
|
+
def _anchor_released(self, released: bool) -> None:
|
|
1237
|
+
if released != self._anchor_released_flag:
|
|
1238
|
+
self._anchor_released_flag = released
|
|
1239
|
+
self._mark_layout_dirty()
|
|
1240
|
+
|
|
1241
|
+
def _cover(self, widget: Widget) -> None:"""
|
|
1242
|
+
|
|
1243
|
+
# widget.py: Widget._set_dirty
|
|
1244
|
+
_REFLOW_28_OLD = """\
|
|
1245
|
+
self._dirty_regions.clear()
|
|
1246
|
+
self._repaint_regions.clear()
|
|
1247
|
+
self._styles_cache.clear()
|
|
1248
|
+
self._styles_cache.set_dirty(self.size.region)
|
|
1249
|
+
outer_size = self.outer_size
|
|
1250
|
+
self._dirty_regions.add(outer_size.region)"""
|
|
1251
|
+
|
|
1252
|
+
_REFLOW_28_NEW = """\
|
|
1253
|
+
self._dirty_regions.clear()
|
|
1254
|
+
self._repaint_regions.clear()
|
|
1255
|
+
# A cleared cache re-renders every line; marking lines dirty as well would
|
|
1256
|
+
# only cost a compositor lookup (`self.size`), which may rebuild the full map.
|
|
1257
|
+
self._styles_cache.clear()
|
|
1258
|
+
outer_size = self.outer_size
|
|
1259
|
+
self._dirty_regions.add(outer_size.region)"""
|
|
1260
|
+
|
|
1261
|
+
# widget.py: Widget.scroll_to, Widget.set_scroll
|
|
1262
|
+
_REFLOW_29_OLD = """\
|
|
1263
|
+
if y is not None:
|
|
1264
|
+
self.set_reactive(Widget.scroll_y, round(y))
|
|
1265
|
+
|
|
1266
|
+
def scroll_to("""
|
|
1267
|
+
|
|
1268
|
+
_REFLOW_29_NEW = """\
|
|
1269
|
+
if y is not None:
|
|
1270
|
+
self.set_reactive(Widget.scroll_y, round(y))
|
|
1271
|
+
self._mark_layout_dirty()
|
|
1272
|
+
|
|
1273
|
+
def scroll_to("""
|
|
1274
|
+
|
|
1275
|
+
# widget.py: Widget._refresh_scroll
|
|
1276
|
+
_REFLOW_30_OLD = """\
|
|
1277
|
+
\"\"\"Refreshes the scroll position.\"\"\"
|
|
1278
|
+
self._scroll_required = True
|
|
1279
|
+
self.check_idle()
|
|
1280
|
+
"""
|
|
1281
|
+
|
|
1282
|
+
_REFLOW_30_NEW = """\
|
|
1283
|
+
\"\"\"Refreshes the scroll position.\"\"\"
|
|
1284
|
+
self._scroll_required = True
|
|
1285
|
+
self._mark_layout_dirty()
|
|
1286
|
+
self.check_idle()
|
|
1287
|
+
"""
|
|
1288
|
+
|
|
1289
|
+
# widget.py: Widget.refresh
|
|
1290
|
+
_REFLOW_31_OLD = """\
|
|
1291
|
+
\"\"\"
|
|
1292
|
+
|
|
1293
|
+
if layout and not self._layout_required:
|
|
1294
|
+
self._layout_required = True"""
|
|
1295
|
+
|
|
1296
|
+
_REFLOW_31_NEW = """\
|
|
1297
|
+
\"\"\"
|
|
1298
|
+
|
|
1299
|
+
self._mark_layout_dirty()
|
|
1300
|
+
if layout and not self._layout_required:
|
|
1301
|
+
self._layout_required = True"""
|
|
1302
|
+
|
|
1303
|
+
# widget.py: Widget._check_refresh
|
|
1304
|
+
_REFLOW_32_OLD = """\
|
|
1305
|
+
if self._layout_required:
|
|
1306
|
+
self._layout_required = False
|
|
1307
|
+
for ancestor in self.ancestors:
|
|
1308
|
+
if not isinstance(ancestor, Widget):"""
|
|
1309
|
+
|
|
1310
|
+
_REFLOW_32_NEW = """\
|
|
1311
|
+
if self._layout_required:
|
|
1312
|
+
self._layout_required = False
|
|
1313
|
+
self._mark_layout_dirty()
|
|
1314
|
+
for ancestor in self.ancestors:
|
|
1315
|
+
if not isinstance(ancestor, Widget):"""
|
|
1316
|
+
|
|
1317
|
+
# css/styles.py: Styles.clear_rule, Styles.get_rules
|
|
1318
|
+
_REFLOW_33_OLD = """\
|
|
1319
|
+
changed = self._rules.pop(rule_name, None) is not None # type: ignore
|
|
1320
|
+
if changed:
|
|
1321
|
+
self._updates += 1
|
|
1322
|
+
return changed
|
|
1323
|
+
|
|
1324
|
+
def get_rules(self) -> RulesMap:"""
|
|
1325
|
+
|
|
1326
|
+
_REFLOW_33_NEW = """\
|
|
1327
|
+
changed = self._rules.pop(rule_name, None) is not None # type: ignore
|
|
1328
|
+
if changed:
|
|
1329
|
+
self._updates += 1
|
|
1330
|
+
if self.node is not None:
|
|
1331
|
+
# A raw rule write skips the refresh a style property makes.
|
|
1332
|
+
self.node._mark_layout_dirty()
|
|
1333
|
+
return changed
|
|
1334
|
+
|
|
1335
|
+
def get_rules(self) -> RulesMap:"""
|
|
1336
|
+
|
|
1337
|
+
# css/styles.py: Styles.set_rule
|
|
1338
|
+
_REFLOW_34_OLD = """\
|
|
1339
|
+
if value is None:
|
|
1340
|
+
changed = self._rules.pop(rule, None) is not None # type: ignore
|
|
1341
|
+
if changed:
|
|
1342
|
+
self._updates += 1
|
|
1343
|
+
return changed
|
|
1344
|
+
current = self._rules.get(rule)
|
|
1345
|
+
self._rules[rule] = value # type: ignore
|
|
1346
|
+
changed = current != value
|
|
1347
|
+
if changed:
|
|
1348
|
+
self._updates += 1
|
|
1349
|
+
return changed
|
|
1350
|
+
"""
|
|
1351
|
+
|
|
1352
|
+
_REFLOW_34_NEW = """\
|
|
1353
|
+
if value is None:
|
|
1354
|
+
changed = self._rules.pop(rule, None) is not None # type: ignore
|
|
1355
|
+
else:
|
|
1356
|
+
current = self._rules.get(rule)
|
|
1357
|
+
self._rules[rule] = value # type: ignore
|
|
1358
|
+
changed = current != value
|
|
1359
|
+
if changed:
|
|
1360
|
+
self._updates += 1
|
|
1361
|
+
if self.node is not None:
|
|
1362
|
+
# A raw rule write skips the refresh a style property makes.
|
|
1363
|
+
self.node._mark_layout_dirty()
|
|
1364
|
+
return changed
|
|
1365
|
+
"""
|
|
1366
|
+
|
|
1367
|
+
# widget.py: Widget._size_updated
|
|
1368
|
+
_REFLOW_35_OLD = """\
|
|
1369
|
+
else:
|
|
1370
|
+
self.set_reactive(Widget.virtual_size, virtual_size)
|
|
1371
|
+
self._container_size = container_size"""
|
|
1372
|
+
|
|
1373
|
+
_REFLOW_35_NEW = """\
|
|
1374
|
+
else:
|
|
1375
|
+
self.set_reactive(Widget.virtual_size, virtual_size)
|
|
1376
|
+
if (
|
|
1377
|
+
self._container_size != container_size
|
|
1378
|
+
and self._anchored
|
|
1379
|
+
and not self._anchor_released
|
|
1380
|
+
):
|
|
1381
|
+
# An anchored widget is pinned from its container size, which is only
|
|
1382
|
+
# known after the arrangement that pinned it.
|
|
1383
|
+
self._mark_layout_dirty()
|
|
1384
|
+
self._container_size = container_size"""
|
|
1385
|
+
|
|
1386
|
+
# scroll_view.py: ScrollView._size_updated
|
|
1387
|
+
_REFLOW_36_OLD = """\
|
|
1388
|
+
virtual_size = self.virtual_size
|
|
1389
|
+
self._container_size = size - self.styles.gutter.totals"""
|
|
1390
|
+
|
|
1391
|
+
_REFLOW_36_NEW = """\
|
|
1392
|
+
virtual_size = self.virtual_size
|
|
1393
|
+
new_container_size = size - self.styles.gutter.totals
|
|
1394
|
+
if (
|
|
1395
|
+
self._container_size != new_container_size
|
|
1396
|
+
and self._anchored
|
|
1397
|
+
and not self._anchor_released
|
|
1398
|
+
):
|
|
1399
|
+
# An anchored widget is pinned from its container size, which is only
|
|
1400
|
+
# known after the arrangement that pinned it.
|
|
1401
|
+
self._mark_layout_dirty()
|
|
1402
|
+
self._container_size = new_container_size"""
|
|
1403
|
+
|
|
1404
|
+
# reactive.py: Reactive._set
|
|
1405
|
+
_REFLOW_37_OLD = """\
|
|
1406
|
+
# Store the internal value
|
|
1407
|
+
setattr(obj, self.internal_name, value)
|
|
1408
|
+
|
|
1409
|
+
# Check all watchers
|
|
1410
|
+
self._check_watchers(obj, name, current_value)"""
|
|
1411
|
+
|
|
1412
|
+
_REFLOW_37_NEW = """\
|
|
1413
|
+
# Store the internal value
|
|
1414
|
+
setattr(obj, self.internal_name, value)
|
|
1415
|
+
|
|
1416
|
+
if self._layout or self._repaint or self._recompose:
|
|
1417
|
+
# The refresh below would stamp the change only after the watchers, which
|
|
1418
|
+
# may look up geometry and so rebuild the full map from records.
|
|
1419
|
+
obj._mark_layout_dirty()
|
|
1420
|
+
|
|
1421
|
+
# Check all watchers
|
|
1422
|
+
self._check_watchers(obj, name, current_value)"""
|
|
1423
|
+
|
|
1424
|
+
_PATCHES = (
|
|
1425
|
+
FilePatch(
|
|
1426
|
+
package="textual",
|
|
1427
|
+
module_file="_compositor.py",
|
|
1428
|
+
old_fragment=_REFLOW_1_OLD,
|
|
1429
|
+
new_fragment=_REFLOW_1_NEW,
|
|
1430
|
+
description="Reuse unchanged arrangements: <import>",
|
|
1431
|
+
),
|
|
1432
|
+
FilePatch(
|
|
1433
|
+
package="textual",
|
|
1434
|
+
module_file="_compositor.py",
|
|
1435
|
+
old_fragment=_REFLOW_2_OLD,
|
|
1436
|
+
new_fragment=_REFLOW_2_NEW,
|
|
1437
|
+
description="Reuse unchanged arrangements: <if>, _ArrangeRecord=, _VERIFY_REUSE=",
|
|
1438
|
+
),
|
|
1439
|
+
FilePatch(
|
|
1440
|
+
package="textual",
|
|
1441
|
+
module_file="_compositor.py",
|
|
1442
|
+
old_fragment=_REFLOW_3_OLD,
|
|
1443
|
+
new_fragment=_REFLOW_3_NEW,
|
|
1444
|
+
description="Reuse unchanged arrangements: Compositor.__init__, Compositor.clear",
|
|
1445
|
+
),
|
|
1446
|
+
FilePatch(
|
|
1447
|
+
package="textual",
|
|
1448
|
+
module_file="_compositor.py",
|
|
1449
|
+
old_fragment=_REFLOW_4_OLD,
|
|
1450
|
+
new_fragment=_REFLOW_4_NEW,
|
|
1451
|
+
description="Reuse unchanged arrangements: Compositor._regions_to_spans, Compositor.clear",
|
|
1452
|
+
),
|
|
1453
|
+
FilePatch(
|
|
1454
|
+
package="textual",
|
|
1455
|
+
module_file="_compositor.py",
|
|
1456
|
+
old_fragment=_REFLOW_5_OLD,
|
|
1457
|
+
new_fragment=_REFLOW_5_NEW,
|
|
1458
|
+
description="Reuse unchanged arrangements: Compositor.__rich_repr__, Compositor.reflow",
|
|
1459
|
+
),
|
|
1460
|
+
FilePatch(
|
|
1461
|
+
package="textual",
|
|
1462
|
+
module_file="_compositor.py",
|
|
1463
|
+
old_fragment=_REFLOW_6_OLD,
|
|
1464
|
+
new_fragment=_REFLOW_6_NEW,
|
|
1465
|
+
description="Reuse unchanged arrangements: Compositor.reflow",
|
|
1466
|
+
),
|
|
1467
|
+
FilePatch(
|
|
1468
|
+
package="textual",
|
|
1469
|
+
module_file="_compositor.py",
|
|
1470
|
+
old_fragment=_REFLOW_7_OLD,
|
|
1471
|
+
new_fragment=_REFLOW_7_NEW,
|
|
1472
|
+
description="Reuse unchanged arrangements: Compositor.reflow",
|
|
1473
|
+
),
|
|
1474
|
+
FilePatch(
|
|
1475
|
+
package="textual",
|
|
1476
|
+
module_file="_compositor.py",
|
|
1477
|
+
old_fragment=_REFLOW_8_OLD,
|
|
1478
|
+
new_fragment=_REFLOW_8_NEW,
|
|
1479
|
+
description="Reuse unchanged arrangements: Compositor.reflow",
|
|
1480
|
+
),
|
|
1481
|
+
FilePatch(
|
|
1482
|
+
package="textual",
|
|
1483
|
+
module_file="_compositor.py",
|
|
1484
|
+
old_fragment=_REFLOW_9_OLD,
|
|
1485
|
+
new_fragment=_REFLOW_9_NEW,
|
|
1486
|
+
description="Reuse unchanged arrangements: Compositor.full_map",
|
|
1487
|
+
),
|
|
1488
|
+
FilePatch(
|
|
1489
|
+
package="textual",
|
|
1490
|
+
module_file="_compositor.py",
|
|
1491
|
+
old_fragment=_REFLOW_10_OLD,
|
|
1492
|
+
new_fragment=_REFLOW_10_NEW,
|
|
1493
|
+
description="Reuse unchanged arrangements: Compositor._arrange, Compositor._arrange_root",
|
|
1494
|
+
),
|
|
1495
|
+
FilePatch(
|
|
1496
|
+
package="textual",
|
|
1497
|
+
module_file="_compositor.py",
|
|
1498
|
+
old_fragment=_REFLOW_11_OLD,
|
|
1499
|
+
new_fragment=_REFLOW_11_NEW,
|
|
1500
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1501
|
+
),
|
|
1502
|
+
FilePatch(
|
|
1503
|
+
package="textual",
|
|
1504
|
+
module_file="_compositor.py",
|
|
1505
|
+
old_fragment=_REFLOW_12_OLD,
|
|
1506
|
+
new_fragment=_REFLOW_12_NEW,
|
|
1507
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1508
|
+
),
|
|
1509
|
+
FilePatch(
|
|
1510
|
+
package="textual",
|
|
1511
|
+
module_file="_compositor.py",
|
|
1512
|
+
old_fragment=_REFLOW_13_OLD,
|
|
1513
|
+
new_fragment=_REFLOW_13_NEW,
|
|
1514
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1515
|
+
),
|
|
1516
|
+
FilePatch(
|
|
1517
|
+
package="textual",
|
|
1518
|
+
module_file="_compositor.py",
|
|
1519
|
+
old_fragment=_REFLOW_14_OLD,
|
|
1520
|
+
new_fragment=_REFLOW_14_NEW,
|
|
1521
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1522
|
+
),
|
|
1523
|
+
FilePatch(
|
|
1524
|
+
package="textual",
|
|
1525
|
+
module_file="_compositor.py",
|
|
1526
|
+
old_fragment=_REFLOW_15_OLD,
|
|
1527
|
+
new_fragment=_REFLOW_15_NEW,
|
|
1528
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1529
|
+
),
|
|
1530
|
+
FilePatch(
|
|
1531
|
+
package="textual",
|
|
1532
|
+
module_file="_compositor.py",
|
|
1533
|
+
old_fragment=_REFLOW_16_OLD,
|
|
1534
|
+
new_fragment=_REFLOW_16_NEW,
|
|
1535
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1536
|
+
),
|
|
1537
|
+
FilePatch(
|
|
1538
|
+
package="textual",
|
|
1539
|
+
module_file="_compositor.py",
|
|
1540
|
+
old_fragment=_REFLOW_17_OLD,
|
|
1541
|
+
new_fragment=_REFLOW_17_NEW,
|
|
1542
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1543
|
+
),
|
|
1544
|
+
FilePatch(
|
|
1545
|
+
package="textual",
|
|
1546
|
+
module_file="_compositor.py",
|
|
1547
|
+
old_fragment=_REFLOW_18_OLD,
|
|
1548
|
+
new_fragment=_REFLOW_18_NEW,
|
|
1549
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1550
|
+
),
|
|
1551
|
+
FilePatch(
|
|
1552
|
+
package="textual",
|
|
1553
|
+
module_file="_compositor.py",
|
|
1554
|
+
old_fragment=_REFLOW_19_OLD,
|
|
1555
|
+
new_fragment=_REFLOW_19_NEW,
|
|
1556
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1557
|
+
),
|
|
1558
|
+
FilePatch(
|
|
1559
|
+
package="textual",
|
|
1560
|
+
module_file="_compositor.py",
|
|
1561
|
+
old_fragment=_REFLOW_20_OLD,
|
|
1562
|
+
new_fragment=_REFLOW_20_NEW,
|
|
1563
|
+
description="Reuse unchanged arrangements: Compositor._arrange",
|
|
1564
|
+
),
|
|
1565
|
+
FilePatch(
|
|
1566
|
+
package="textual",
|
|
1567
|
+
module_file="_compositor.py",
|
|
1568
|
+
old_fragment=_REFLOW_21_OLD,
|
|
1569
|
+
new_fragment=_REFLOW_21_NEW,
|
|
1570
|
+
description="Reuse unchanged arrangements: Compositor._arrange, Compositor._arrangement_state, Compositor._restore_arrangement_state, Compositor._verify_reuse, Compositor.layers",
|
|
1571
|
+
),
|
|
1572
|
+
FilePatch(
|
|
1573
|
+
package="textual",
|
|
1574
|
+
module_file="_node_list.py",
|
|
1575
|
+
old_fragment=_REFLOW_22_OLD,
|
|
1576
|
+
new_fragment=_REFLOW_22_NEW,
|
|
1577
|
+
description="Reuse unchanged arrangements: NodeList.updated",
|
|
1578
|
+
),
|
|
1579
|
+
FilePatch(
|
|
1580
|
+
package="textual",
|
|
1581
|
+
module_file="dom.py",
|
|
1582
|
+
old_fragment=_REFLOW_23_OLD,
|
|
1583
|
+
new_fragment=_REFLOW_23_NEW,
|
|
1584
|
+
description="Reuse unchanged arrangements: DOMNode._PSEUDO_CLASSES, DOMNode.__init__, DOMNode._layout_dirty_epoch, DOMNode._layout_epoch, DOMNode._mark_layout_dirty, DOMNode._on_mounted_state",
|
|
1585
|
+
),
|
|
1586
|
+
FilePatch(
|
|
1587
|
+
package="textual",
|
|
1588
|
+
module_file="message_pump.py",
|
|
1589
|
+
old_fragment=_REFLOW_24_OLD,
|
|
1590
|
+
new_fragment=_REFLOW_24_NEW,
|
|
1591
|
+
description="Reuse unchanged arrangements: MessagePump._on_mounted_state, MessagePump._post_mount, MessagePump._pre_process",
|
|
1592
|
+
),
|
|
1593
|
+
FilePatch(
|
|
1594
|
+
package="textual",
|
|
1595
|
+
module_file="message_pump.py",
|
|
1596
|
+
old_fragment=_REFLOW_25_OLD,
|
|
1597
|
+
new_fragment=_REFLOW_25_NEW,
|
|
1598
|
+
description="Reuse unchanged arrangements: MessagePump._process_messages_loop",
|
|
1599
|
+
),
|
|
1600
|
+
FilePatch(
|
|
1601
|
+
package="textual",
|
|
1602
|
+
module_file="screen.py",
|
|
1603
|
+
old_fragment=_REFLOW_26_OLD,
|
|
1604
|
+
new_fragment=_REFLOW_26_NEW,
|
|
1605
|
+
description="Reuse unchanged arrangements: Screen._refresh_layout",
|
|
1606
|
+
),
|
|
1607
|
+
FilePatch(
|
|
1608
|
+
package="textual",
|
|
1609
|
+
module_file="widget.py",
|
|
1610
|
+
old_fragment=_REFLOW_27_OLD,
|
|
1611
|
+
new_fragment=_REFLOW_27_NEW,
|
|
1612
|
+
description="Reuse unchanged arrangements: Widget._absolute_offset, Widget._anchor_released, Widget._anchor_released_flag, Widget._anchored, Widget._anchored_flag, Widget._cover, Widget.absolute_offset, Widget.pre_render",
|
|
1613
|
+
),
|
|
1614
|
+
FilePatch(
|
|
1615
|
+
package="textual",
|
|
1616
|
+
module_file="widget.py",
|
|
1617
|
+
old_fragment=_REFLOW_28_OLD,
|
|
1618
|
+
new_fragment=_REFLOW_28_NEW,
|
|
1619
|
+
description="Reuse unchanged arrangements: Widget._set_dirty",
|
|
1620
|
+
),
|
|
1621
|
+
FilePatch(
|
|
1622
|
+
package="textual",
|
|
1623
|
+
module_file="widget.py",
|
|
1624
|
+
old_fragment=_REFLOW_29_OLD,
|
|
1625
|
+
new_fragment=_REFLOW_29_NEW,
|
|
1626
|
+
description="Reuse unchanged arrangements: Widget.scroll_to, Widget.set_scroll",
|
|
1627
|
+
),
|
|
1628
|
+
FilePatch(
|
|
1629
|
+
package="textual",
|
|
1630
|
+
module_file="widget.py",
|
|
1631
|
+
old_fragment=_REFLOW_30_OLD,
|
|
1632
|
+
new_fragment=_REFLOW_30_NEW,
|
|
1633
|
+
description="Reuse unchanged arrangements: Widget._refresh_scroll",
|
|
1634
|
+
),
|
|
1635
|
+
FilePatch(
|
|
1636
|
+
package="textual",
|
|
1637
|
+
module_file="widget.py",
|
|
1638
|
+
old_fragment=_REFLOW_31_OLD,
|
|
1639
|
+
new_fragment=_REFLOW_31_NEW,
|
|
1640
|
+
description="Reuse unchanged arrangements: Widget.refresh",
|
|
1641
|
+
),
|
|
1642
|
+
FilePatch(
|
|
1643
|
+
package="textual",
|
|
1644
|
+
module_file="widget.py",
|
|
1645
|
+
old_fragment=_REFLOW_32_OLD,
|
|
1646
|
+
new_fragment=_REFLOW_32_NEW,
|
|
1647
|
+
description="Reuse unchanged arrangements: Widget._check_refresh",
|
|
1648
|
+
),
|
|
1649
|
+
FilePatch(
|
|
1650
|
+
package="textual",
|
|
1651
|
+
module_file="css/styles.py",
|
|
1652
|
+
old_fragment=_REFLOW_33_OLD,
|
|
1653
|
+
new_fragment=_REFLOW_33_NEW,
|
|
1654
|
+
description="Reuse unchanged arrangements: Styles.clear_rule, Styles.get_rules",
|
|
1655
|
+
),
|
|
1656
|
+
FilePatch(
|
|
1657
|
+
package="textual",
|
|
1658
|
+
module_file="css/styles.py",
|
|
1659
|
+
old_fragment=_REFLOW_34_OLD,
|
|
1660
|
+
new_fragment=_REFLOW_34_NEW,
|
|
1661
|
+
description="Reuse unchanged arrangements: Styles.set_rule",
|
|
1662
|
+
),
|
|
1663
|
+
FilePatch(
|
|
1664
|
+
package="textual",
|
|
1665
|
+
module_file="widget.py",
|
|
1666
|
+
old_fragment=_REFLOW_35_OLD,
|
|
1667
|
+
new_fragment=_REFLOW_35_NEW,
|
|
1668
|
+
description="Reuse unchanged arrangements: Widget._size_updated",
|
|
1669
|
+
),
|
|
1670
|
+
FilePatch(
|
|
1671
|
+
package="textual",
|
|
1672
|
+
module_file="scroll_view.py",
|
|
1673
|
+
old_fragment=_REFLOW_36_OLD,
|
|
1674
|
+
new_fragment=_REFLOW_36_NEW,
|
|
1675
|
+
description="Reuse unchanged arrangements: ScrollView._size_updated",
|
|
1676
|
+
),
|
|
1677
|
+
FilePatch(
|
|
1678
|
+
package="textual",
|
|
1679
|
+
module_file="reactive.py",
|
|
1680
|
+
old_fragment=_REFLOW_37_OLD,
|
|
1681
|
+
new_fragment=_REFLOW_37_NEW,
|
|
1682
|
+
description="Reuse unchanged arrangements: Reactive._set",
|
|
1683
|
+
),
|
|
1684
|
+
)
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
def apply_runtime_patch() -> None:
|
|
1688
|
+
"""Install the reusing reflow on classes already imported; on drift in any module, in none."""
|
|
1689
|
+
try:
|
|
1690
|
+
import importlib
|
|
1691
|
+
|
|
1692
|
+
import textual
|
|
1693
|
+
except ImportError:
|
|
1694
|
+
return
|
|
1695
|
+
if textual.__version__ != _RUNTIME_PATCH_TEXTUAL_VERSION:
|
|
1696
|
+
logger.warning(
|
|
1697
|
+
"Skipping Textual reflow reuse runtime patch: loaded Textual is not the pinned %s that the patch targets.",
|
|
1698
|
+
_RUNTIME_PATCH_TEXTUAL_VERSION,
|
|
1699
|
+
)
|
|
1700
|
+
return
|
|
1701
|
+
modules = {name: importlib.import_module(name) for name in _RUNTIME_MEMBERS}
|
|
1702
|
+
if all(
|
|
1703
|
+
members_installed(modules[name], members, _RUNTIME_PATCH_MARKER) for name, members in _RUNTIME_MEMBERS.items()
|
|
1704
|
+
):
|
|
1705
|
+
return
|
|
1706
|
+
try:
|
|
1707
|
+
# Stage every module before installing any: a compositor that reuses records while
|
|
1708
|
+
# widgets do not stamp their changes would paint stale placements.
|
|
1709
|
+
staged = [
|
|
1710
|
+
stage_members(
|
|
1711
|
+
modules[name],
|
|
1712
|
+
[patch for patch in _PATCHES if _module_name(patch) == name],
|
|
1713
|
+
members,
|
|
1714
|
+
label="reflow reuse",
|
|
1715
|
+
)
|
|
1716
|
+
for name, members in _RUNTIME_MEMBERS.items()
|
|
1717
|
+
]
|
|
1718
|
+
except StagedSourceDriftError as exc:
|
|
1719
|
+
logger.warning("Skipping Textual reflow reuse runtime patch: %s", exc)
|
|
1720
|
+
return
|
|
1721
|
+
compositor_globals = vars(modules["textual._compositor"])
|
|
1722
|
+
compositor_globals.setdefault("DOMNode", vars(modules["textual.dom"])["DOMNode"])
|
|
1723
|
+
compositor_globals.setdefault("_VERIFY_REUSE", False)
|
|
1724
|
+
for members in staged:
|
|
1725
|
+
members.install(_RUNTIME_PATCH_MARKER)
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
def reflow_reusing_records(compositor: Compositor, root: Widget, size: Size) -> ReflowResult:
|
|
1729
|
+
"""``compositor.reflow(root, size)`` that replays every subtree unchanged since the last full arrangement.
|
|
1730
|
+
|
|
1731
|
+
For a caller that resynchronizes the compositor outside a layout pass after changes that stamp
|
|
1732
|
+
their widgets (a raw style-rule write does): only the stamped widgets' ancestor paths are
|
|
1733
|
+
arranged again, instead of every displayed widget. Without the installed patch (another
|
|
1734
|
+
Textual version) this is the stock reflow, which arranges the whole tree.
|
|
1735
|
+
"""
|
|
1736
|
+
if _reflow_reuses_records(compositor):
|
|
1737
|
+
return compositor.reflow(root, size, reuse=True)
|
|
1738
|
+
return compositor.reflow(root, size)
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
class _ReusingCompositor(Protocol):
|
|
1742
|
+
"""A compositor whose ``reflow`` is the installed one, which Textual's own signature does not describe."""
|
|
1743
|
+
|
|
1744
|
+
def reflow(self, parent: Widget, size: Size, reuse: bool = False) -> ReflowResult: ...
|
|
1745
|
+
|
|
1746
|
+
|
|
1747
|
+
def _reflow_reuses_records(compositor: Compositor) -> TypeGuard[_ReusingCompositor]:
|
|
1748
|
+
"""Whether ``compositor.reflow`` is the installed reusing reflow, which takes ``reuse``."""
|
|
1749
|
+
return bool(vars(type(compositor).reflow).get(_RUNTIME_PATCH_MARKER, False))
|
|
1750
|
+
|
|
1751
|
+
|
|
1752
|
+
def _module_name(patch: FilePatch) -> str:
|
|
1753
|
+
return "textual." + patch.module_file.removesuffix(".py").replace("/", ".")
|