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,963 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""The terminal itself: what each control does to the screen, and what the terminal says back.
|
|
4
|
+
|
|
5
|
+
`TerminalEmulator` is a display-less VT220-class terminal with the xterm extensions programs rely
|
|
6
|
+
on today. Feed it a program's output and it keeps two screens of styled cells current; read the
|
|
7
|
+
active `ScreenBuffer` to draw them. It owns no I/O: replies the protocol owes the program come back
|
|
8
|
+
from `feed` for the caller to deliver.
|
|
9
|
+
|
|
10
|
+
Sequences are named here as the standards name them (ECMA-48, the DEC VT510 manual, xterm's
|
|
11
|
+
ctlseqs); each handler states the mnemonic it implements.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import binascii
|
|
18
|
+
import re
|
|
19
|
+
import socket
|
|
20
|
+
from collections.abc import Callable, Sequence
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from functools import lru_cache
|
|
24
|
+
from typing import Final, NamedTuple
|
|
25
|
+
from urllib.parse import unquote, urlsplit
|
|
26
|
+
|
|
27
|
+
from rich.cells import cell_len, get_character_cell_size
|
|
28
|
+
|
|
29
|
+
from chrys.app.tui.terminal.emulator.buffer import ScreenBuffer
|
|
30
|
+
from chrys.app.tui.terminal.emulator.charsets import Charsets, CharsetSnapshot
|
|
31
|
+
from chrys.app.tui.terminal.emulator.parser import SequenceParser
|
|
32
|
+
from chrys.app.tui.terminal.emulator.pen import DEFAULT_PEN, Pen, apply_sgr
|
|
33
|
+
|
|
34
|
+
DEFAULT_HISTORY_LIMIT: Final = 10_000
|
|
35
|
+
|
|
36
|
+
_ZERO_WIDTH_JOINER: Final = "\u200d"
|
|
37
|
+
_TAB_INTERVAL: Final = 8
|
|
38
|
+
_MAX_PARAMETER: Final = 0xFFFF
|
|
39
|
+
|
|
40
|
+
# DEC private modes that are plain switches. The alternate screen and the mouse modes are not:
|
|
41
|
+
# several numbers drive one piece of state, so they are handled by name below.
|
|
42
|
+
_APPLICATION_CURSOR: Final = 1
|
|
43
|
+
_ORIGIN: Final = 6
|
|
44
|
+
_AUTOWRAP: Final = 7
|
|
45
|
+
_CURSOR_BLINK: Final = 12
|
|
46
|
+
_CURSOR_VISIBLE: Final = 25
|
|
47
|
+
_APPLICATION_KEYPAD: Final = 66
|
|
48
|
+
_FOCUS_REPORTING: Final = 1004
|
|
49
|
+
_ALTERNATE_SCROLL: Final = 1007
|
|
50
|
+
_BRACKETED_PASTE: Final = 2004
|
|
51
|
+
_SWITCHES: Final = frozenset(
|
|
52
|
+
{
|
|
53
|
+
_APPLICATION_CURSOR,
|
|
54
|
+
_ORIGIN,
|
|
55
|
+
_AUTOWRAP,
|
|
56
|
+
_CURSOR_BLINK,
|
|
57
|
+
_CURSOR_VISIBLE,
|
|
58
|
+
_APPLICATION_KEYPAD,
|
|
59
|
+
_FOCUS_REPORTING,
|
|
60
|
+
_ALTERNATE_SCROLL,
|
|
61
|
+
_BRACKETED_PASTE,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
# xterm leaves alternate scroll off, but every terminal people use today turns the wheel into
|
|
65
|
+
# arrow keys on the alternate screen unless told not to, and pagers are read with that in hand.
|
|
66
|
+
_SWITCHES_ON_AT_RESET: Final = frozenset({_AUTOWRAP, _CURSOR_VISIBLE, _ALTERNATE_SCROLL})
|
|
67
|
+
|
|
68
|
+
_INSERT: Final = 4
|
|
69
|
+
_NEWLINE: Final = 20
|
|
70
|
+
|
|
71
|
+
# 47 and 1047 only switch screens; 1049 also saves the cursor going in and restores it coming out.
|
|
72
|
+
_ALTERNATE_SCREENS: Final = frozenset({47, 1047, 1049})
|
|
73
|
+
_ALTERNATE_SCREEN_SAVING_CURSOR: Final = 1049
|
|
74
|
+
|
|
75
|
+
# Of the kitty keyboard protocol's progressive enhancements only "disambiguate escape codes" is
|
|
76
|
+
# offered. A program reads back which flags took, so asking for more degrades cleanly.
|
|
77
|
+
_KITTY_DISAMBIGUATE: Final = 1
|
|
78
|
+
_KITTY_STACK_LIMIT: Final = 16
|
|
79
|
+
_XTERM_MODIFY_OTHER_KEYS: Final = 4
|
|
80
|
+
|
|
81
|
+
_DRIVE_PATH = re.compile(r"/[A-Za-z]:(/|$)")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class MouseTracking(Enum):
|
|
85
|
+
"""Which pointer events the program asked to hear about. Values are the DEC private modes."""
|
|
86
|
+
|
|
87
|
+
OFF = 0
|
|
88
|
+
PRESS = 9
|
|
89
|
+
PRESS_RELEASE = 1000
|
|
90
|
+
DRAG = 1002
|
|
91
|
+
MOTION = 1003
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class MouseEncoding(Enum):
|
|
95
|
+
"""How pointer reports are spelled. Values are the DEC private modes."""
|
|
96
|
+
|
|
97
|
+
DEFAULT = 0
|
|
98
|
+
UTF8 = 1005
|
|
99
|
+
SGR = 1006
|
|
100
|
+
URXVT = 1015
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class KeyProtocol(Enum):
|
|
104
|
+
"""How keys the classic encoding cannot tell apart are spelled, once a program asks."""
|
|
105
|
+
|
|
106
|
+
LEGACY = "legacy"
|
|
107
|
+
MODIFY_OTHER_KEYS_1 = "modify_other_keys_1"
|
|
108
|
+
"""xterm's ``CSI 27 ; modifiers ; code ~``, switched on with XTMODKEYS, for the keys that have
|
|
109
|
+
no classic spelling of their own. A key with a well-known one keeps it, less a modifier or not."""
|
|
110
|
+
MODIFY_OTHER_KEYS_2 = "modify_other_keys_2"
|
|
111
|
+
"""The same spelling for every modified key, those with a well-known one included."""
|
|
112
|
+
KITTY = "kitty"
|
|
113
|
+
"""The kitty keyboard protocol's ``CSI code ; modifiers u``."""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CursorShape(Enum):
|
|
117
|
+
BLOCK = "block"
|
|
118
|
+
UNDERLINE = "underline"
|
|
119
|
+
BAR = "bar"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# By XTMODKEYS level. Level 3 also spells out keys held with nothing, which no program here has been
|
|
123
|
+
# seen to want; it is answered with the most there is.
|
|
124
|
+
_MODIFY_OTHER_KEYS_LEVELS: Final = (
|
|
125
|
+
KeyProtocol.LEGACY,
|
|
126
|
+
KeyProtocol.MODIFY_OTHER_KEYS_1,
|
|
127
|
+
KeyProtocol.MODIFY_OTHER_KEYS_2,
|
|
128
|
+
)
|
|
129
|
+
_TRACKING_MODES: Final = {tracking.value: tracking for tracking in MouseTracking if tracking.value}
|
|
130
|
+
_ENCODING_MODES: Final = {encoding.value: encoding for encoding in MouseEncoding if encoding.value}
|
|
131
|
+
_CURSOR_SHAPES: Final = (
|
|
132
|
+
CursorShape.BLOCK,
|
|
133
|
+
CursorShape.BLOCK,
|
|
134
|
+
CursorShape.BLOCK,
|
|
135
|
+
CursorShape.UNDERLINE,
|
|
136
|
+
CursorShape.UNDERLINE,
|
|
137
|
+
CursorShape.BAR,
|
|
138
|
+
CursorShape.BAR,
|
|
139
|
+
)
|
|
140
|
+
"""By DECSCUSR parameter. Each shape comes blinking and steady, and zero is the default."""
|
|
141
|
+
_STEADY_CURSOR_PARAMETERS: Final = {CursorShape.BLOCK: 2, CursorShape.UNDERLINE: 4, CursorShape.BAR: 6}
|
|
142
|
+
"""What DECRQSS reports: the cursor is drawn without blinking, whichever variant was asked for."""
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True, slots=True)
|
|
146
|
+
class DirectoryChanged:
|
|
147
|
+
"""The shell reported its working directory."""
|
|
148
|
+
|
|
149
|
+
path: str
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True, slots=True)
|
|
153
|
+
class CommandSubmitted:
|
|
154
|
+
"""The shell reported the command line it is about to run."""
|
|
155
|
+
|
|
156
|
+
command: str
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
type TerminalEvent = DirectoryChanged | CommandSubmitted
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass(frozen=True, slots=True)
|
|
163
|
+
class Update:
|
|
164
|
+
"""What one `TerminalEmulator.feed` changed."""
|
|
165
|
+
|
|
166
|
+
damaged: set[int] | None
|
|
167
|
+
"""Rows of the active buffer to redraw, as indices into its ``rows``; ``None`` means all."""
|
|
168
|
+
trimmed: int
|
|
169
|
+
"""History rows dropped from the front of the active buffer: every later row moved up by this."""
|
|
170
|
+
replies: str
|
|
171
|
+
"""Bytes the protocol owes the program (status reports and the like), to write to its input."""
|
|
172
|
+
events: tuple[TerminalEvent, ...]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class _SavedCursor(NamedTuple):
|
|
176
|
+
x: int
|
|
177
|
+
y: int
|
|
178
|
+
pending_wrap: bool
|
|
179
|
+
pen: Pen
|
|
180
|
+
charsets: CharsetSnapshot
|
|
181
|
+
origin: bool
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@lru_cache(maxsize=1024)
|
|
185
|
+
def _parse_parameters(parameters: str) -> tuple[int, ...]:
|
|
186
|
+
"""Numeric parameters, omitted ones as zero. Sub-parameters mean nothing outside SGR."""
|
|
187
|
+
return tuple(min(int(field.partition(":")[0] or 0), _MAX_PARAMETER) for field in parameters.split(";"))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _awaits_joined_character(cluster: str) -> bool:
|
|
191
|
+
"""Whether the cluster ends in a zero-width joiner that has not had its character yet.
|
|
192
|
+
|
|
193
|
+
A joiner takes the one character after it, whatever that is, and a joiner taken that way is
|
|
194
|
+
spent: it joins nothing itself. That is how `rich.cells` measures, nonsense included.
|
|
195
|
+
"""
|
|
196
|
+
return (len(cluster) - len(cluster.rstrip(_ZERO_WIDTH_JOINER))) % 2 == 1
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _decode_payload(payload: str) -> str | None:
|
|
200
|
+
"""The text of a shell-integration report, which may travel as ``b64:`` to survive any content."""
|
|
201
|
+
if not payload.startswith("b64:"):
|
|
202
|
+
return payload
|
|
203
|
+
try:
|
|
204
|
+
return base64.b64decode(payload[4:], validate=True).decode("utf-8", "replace")
|
|
205
|
+
except binascii.Error, ValueError:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class TerminalEmulator:
|
|
210
|
+
"""A terminal without a display."""
|
|
211
|
+
|
|
212
|
+
def __init__(self, columns: int = 80, lines: int = 24, *, history_limit: int = DEFAULT_HISTORY_LIMIT) -> None:
|
|
213
|
+
self.columns = columns
|
|
214
|
+
self.lines = lines
|
|
215
|
+
self._history_limit = history_limit
|
|
216
|
+
self._parser = SequenceParser(self)
|
|
217
|
+
self._replies: list[str] = []
|
|
218
|
+
self._events: list[TerminalEvent] = []
|
|
219
|
+
self._history_dropped = 0
|
|
220
|
+
"""History rows the program threw away (ED 3, RIS) that no `Update` has reported yet."""
|
|
221
|
+
self._control_sequences = self._control_sequence_table()
|
|
222
|
+
self._escapes = self._escape_table()
|
|
223
|
+
self._commands = self._operating_system_command_table()
|
|
224
|
+
self._reset()
|
|
225
|
+
|
|
226
|
+
def _reset(self) -> None:
|
|
227
|
+
self._primary = ScreenBuffer(self.columns, self.lines, history_limit=self._history_limit)
|
|
228
|
+
self._alternate = ScreenBuffer(self.columns, self.lines, history_limit=0)
|
|
229
|
+
self.buffer = self._primary
|
|
230
|
+
self.pen = DEFAULT_PEN
|
|
231
|
+
self.cursor_shape = CursorShape.BLOCK
|
|
232
|
+
self.mouse_tracking = MouseTracking.OFF
|
|
233
|
+
self.mouse_encoding = MouseEncoding.DEFAULT
|
|
234
|
+
self._charsets = Charsets()
|
|
235
|
+
self._switches = set(_SWITCHES_ON_AT_RESET)
|
|
236
|
+
self._insert = False
|
|
237
|
+
self._newline = False
|
|
238
|
+
self._tab_stops = set(range(_TAB_INTERVAL, self.columns, _TAB_INTERVAL))
|
|
239
|
+
self._saved_cursors: dict[bool, _SavedCursor] = {}
|
|
240
|
+
self._modify_other_keys = 0
|
|
241
|
+
# One stack per screen, so a full-screen program that dies without popping its flags
|
|
242
|
+
# does not leave the shell behind it reading keys it cannot parse.
|
|
243
|
+
self._kitty_flags: dict[bool, list[int]] = {False: [0], True: [0]}
|
|
244
|
+
self._last_printed = ""
|
|
245
|
+
self._anchor_pending = False
|
|
246
|
+
|
|
247
|
+
# -- state a front end reads -------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def alternate_screen(self) -> bool:
|
|
251
|
+
return self.buffer is self._alternate
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def cursor_visible(self) -> bool:
|
|
255
|
+
return _CURSOR_VISIBLE in self._switches
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def application_cursor_keys(self) -> bool:
|
|
259
|
+
return _APPLICATION_CURSOR in self._switches
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def bracketed_paste(self) -> bool:
|
|
263
|
+
return _BRACKETED_PASTE in self._switches
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def focus_reporting(self) -> bool:
|
|
267
|
+
return _FOCUS_REPORTING in self._switches
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def alternate_scroll(self) -> bool:
|
|
271
|
+
return _ALTERNATE_SCROLL in self._switches
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def key_protocol(self) -> KeyProtocol:
|
|
275
|
+
if self._kitty_flags[self.alternate_screen][-1] & _KITTY_DISAMBIGUATE:
|
|
276
|
+
return KeyProtocol.KITTY
|
|
277
|
+
return _MODIFY_OTHER_KEYS_LEVELS[min(self._modify_other_keys, len(_MODIFY_OTHER_KEYS_LEVELS) - 1)]
|
|
278
|
+
|
|
279
|
+
# -- driving -----------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
def feed(self, text: str) -> Update:
|
|
282
|
+
"""Interpret the next piece of the program's output."""
|
|
283
|
+
buffer = self.buffer
|
|
284
|
+
cursor_before = (buffer.cursor_index, buffer.cursor.x, self.cursor_visible, self.cursor_shape)
|
|
285
|
+
self._parser.feed(text)
|
|
286
|
+
trimmed = 0
|
|
287
|
+
if self.buffer is self._primary:
|
|
288
|
+
# What the limit takes now and what the program threw away itself. Both wait for the
|
|
289
|
+
# primary screen to be the one on show: it is its rows that a front end numbers.
|
|
290
|
+
trimmed = self._history_dropped + self._primary.trim_history()
|
|
291
|
+
self._history_dropped = 0
|
|
292
|
+
if self.buffer is buffer:
|
|
293
|
+
cursor_after = (buffer.cursor_index, buffer.cursor.x, self.cursor_visible, self.cursor_shape)
|
|
294
|
+
if cursor_after != cursor_before:
|
|
295
|
+
# The cursor is drawn with its row, so moving it dirties the row it left too.
|
|
296
|
+
buffer.damage(cursor_before[0])
|
|
297
|
+
buffer.damage(cursor_after[0])
|
|
298
|
+
update = Update(self.buffer.take_damage(), trimmed, "".join(self._replies), tuple(self._events))
|
|
299
|
+
self._replies.clear()
|
|
300
|
+
self._events.clear()
|
|
301
|
+
return update
|
|
302
|
+
|
|
303
|
+
def resize(self, columns: int, lines: int, *, reflow: bool = True, host_repaints: bool = False) -> None:
|
|
304
|
+
"""Change the screen size.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
columns: New width.
|
|
308
|
+
lines: New height.
|
|
309
|
+
reflow: Rewrap the primary screen and its history to the new width.
|
|
310
|
+
host_repaints: Whoever feeds us keeps a screen of its own, reflows that, and repaints
|
|
311
|
+
ours with absolute positions afterwards (Windows ConPTY does). The screen origin
|
|
312
|
+
then stays where that host believes it is, and the first position it sends is
|
|
313
|
+
taken as the truth about which row the cursor is on.
|
|
314
|
+
"""
|
|
315
|
+
self._tab_stops.update(range(-(-self.columns // _TAB_INTERVAL) * _TAB_INTERVAL, columns, _TAB_INTERVAL))
|
|
316
|
+
self.columns, self.lines = columns, lines
|
|
317
|
+
primary = self._primary
|
|
318
|
+
cursor = primary.cursor
|
|
319
|
+
saved = self._saved_cursors.get(False)
|
|
320
|
+
if saved is not None and (saved.x, saved.y) != (cursor.x, cursor.y):
|
|
321
|
+
saved = None
|
|
322
|
+
primary.resize(columns, lines, reflow=reflow, pull_history=not host_repaints)
|
|
323
|
+
if saved is not None:
|
|
324
|
+
# Saved where the cursor is, as entering the alternate screen saves it: it goes where
|
|
325
|
+
# the cursor went, or leaving that screen would put the shell back on the wrong row.
|
|
326
|
+
self._saved_cursors[False] = saved._replace(x=cursor.x, y=cursor.y, pending_wrap=cursor.pending_wrap)
|
|
327
|
+
self._alternate.resize(columns, lines, reflow=False, pull_history=False)
|
|
328
|
+
if host_repaints:
|
|
329
|
+
if reflow and primary.used_height <= lines:
|
|
330
|
+
# Everything fits on one screen, which is how the host lays it out too.
|
|
331
|
+
primary.anchor_cursor_row(primary.cursor_index)
|
|
332
|
+
self._anchor_pending = not self.alternate_screen
|
|
333
|
+
|
|
334
|
+
# -- SequenceHandler ---------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
def print(self, text: str) -> None:
|
|
337
|
+
text = self._charsets.translate(text)
|
|
338
|
+
self._anchor_pending = False
|
|
339
|
+
if text.isascii() and not _awaits_joined_character(self._cell_behind_cursor()[1]):
|
|
340
|
+
self._last_printed = text[-1]
|
|
341
|
+
self._write(text)
|
|
342
|
+
elif cells := self._cells(text):
|
|
343
|
+
self._last_printed = next(cell for cell in reversed(cells) if cell)
|
|
344
|
+
self._write(cells)
|
|
345
|
+
|
|
346
|
+
def execute(self, control: str) -> None:
|
|
347
|
+
cursor = self.buffer.cursor
|
|
348
|
+
if control == "\n" or control in "\v\f":
|
|
349
|
+
self._line_feed()
|
|
350
|
+
elif control == "\r":
|
|
351
|
+
cursor.x = 0
|
|
352
|
+
cursor.pending_wrap = False
|
|
353
|
+
elif control == "\b":
|
|
354
|
+
cursor.x = max(cursor.x - 1, 0)
|
|
355
|
+
cursor.pending_wrap = False
|
|
356
|
+
elif control == "\t":
|
|
357
|
+
self._tab_forward(1)
|
|
358
|
+
elif control == "\x0e":
|
|
359
|
+
self._charsets.lock(1)
|
|
360
|
+
elif control == "\x0f":
|
|
361
|
+
self._charsets.lock(0)
|
|
362
|
+
# BEL and the rest have no effect here: the bell belongs to the outer terminal, and
|
|
363
|
+
# ringing it for an embedded program would badge the whole application.
|
|
364
|
+
|
|
365
|
+
def escape(self, intermediates: str, final: str) -> None:
|
|
366
|
+
if intermediates and intermediates[0] in "()*+":
|
|
367
|
+
self._charsets.designate("()*+".index(intermediates[0]), intermediates[1:] + final)
|
|
368
|
+
elif (handler := self._escapes.get(intermediates + final)) is not None:
|
|
369
|
+
handler()
|
|
370
|
+
|
|
371
|
+
def control_sequence(self, marker: str, parameters: str, intermediates: str, final: str) -> None:
|
|
372
|
+
if final == "m" and not marker and not intermediates:
|
|
373
|
+
self.pen = apply_sgr(self.pen, parameters)
|
|
374
|
+
elif (handler := self._control_sequences.get(marker + intermediates + final)) is not None:
|
|
375
|
+
handler(_parse_parameters(parameters))
|
|
376
|
+
|
|
377
|
+
def operating_system_command(self, payload: str) -> None:
|
|
378
|
+
command, _, argument = payload.partition(";")
|
|
379
|
+
if (handler := self._commands.get(command)) is not None:
|
|
380
|
+
handler(argument)
|
|
381
|
+
|
|
382
|
+
def device_control(self, payload: str) -> None:
|
|
383
|
+
"""DECRQSS, the one device control string programs send to probe a terminal."""
|
|
384
|
+
if not payload.startswith("$q"):
|
|
385
|
+
return
|
|
386
|
+
setting = payload[2:]
|
|
387
|
+
if setting == "r":
|
|
388
|
+
report = f"{self.buffer.margin_top + 1};{self.buffer.margin_bottom + 1}r"
|
|
389
|
+
elif setting == " q":
|
|
390
|
+
report = f"{_STEADY_CURSOR_PARAMETERS[self.cursor_shape]} q"
|
|
391
|
+
else:
|
|
392
|
+
self._replies.append("\x1bP0$r\x1b\\")
|
|
393
|
+
return
|
|
394
|
+
self._replies.append(f"\x1bP1$r{report}\x1b\\")
|
|
395
|
+
|
|
396
|
+
# -- printing ----------------------------------------------------------------------------
|
|
397
|
+
|
|
398
|
+
def _cells(self, text: str) -> list[str]:
|
|
399
|
+
"""Split text into cells the way the renderer will measure it.
|
|
400
|
+
|
|
401
|
+
A zero-width character joins the cell before it, a character after a zero-width joiner
|
|
402
|
+
joins too, and a variation selector may widen its base: exactly the rules `rich.cells`
|
|
403
|
+
measures by, so a row's cell count always equals the rendered width of its text.
|
|
404
|
+
"""
|
|
405
|
+
cells: list[str] = []
|
|
406
|
+
for character in text:
|
|
407
|
+
base = len(cells) - 1 if cells and cells[-1] else len(cells) - 2
|
|
408
|
+
if base < 0:
|
|
409
|
+
if self._joins_cell_behind_cursor(character):
|
|
410
|
+
continue
|
|
411
|
+
width = get_character_cell_size(character)
|
|
412
|
+
elif _awaits_joined_character(cells[base]):
|
|
413
|
+
cells[base] += character
|
|
414
|
+
continue
|
|
415
|
+
elif not (width := get_character_cell_size(character)):
|
|
416
|
+
cells[base] += character
|
|
417
|
+
if base == len(cells) - 1 and cell_len(cells[base]) == 2:
|
|
418
|
+
cells.append("")
|
|
419
|
+
continue
|
|
420
|
+
cells.append(character)
|
|
421
|
+
if width == 2:
|
|
422
|
+
cells.append("")
|
|
423
|
+
return cells
|
|
424
|
+
|
|
425
|
+
def _joins_cell_behind_cursor(self, character: str) -> bool:
|
|
426
|
+
"""Whether a character opening a run belongs to the cell already written before the cursor.
|
|
427
|
+
|
|
428
|
+
That is the case for a zero-width character, and for anything following a zero-width
|
|
429
|
+
joiner, when the two halves of a sequence arrive in separate runs.
|
|
430
|
+
"""
|
|
431
|
+
column, behind = self._cell_behind_cursor()
|
|
432
|
+
if get_character_cell_size(character) and not _awaits_joined_character(behind):
|
|
433
|
+
return False
|
|
434
|
+
if behind:
|
|
435
|
+
# The cluster is what was printed last now, and what REP repeats.
|
|
436
|
+
self._last_printed = behind + character
|
|
437
|
+
self._rewrite_cell(column, self._last_printed)
|
|
438
|
+
return True
|
|
439
|
+
|
|
440
|
+
def _cell_behind_cursor(self) -> tuple[int, str]:
|
|
441
|
+
"""The column and the text of the cell before the cursor, ``""`` where there is none.
|
|
442
|
+
|
|
443
|
+
With the last column just written the cursor is past it in all but position, so that
|
|
444
|
+
column is the one behind it, whether or not the next character is going to wrap.
|
|
445
|
+
"""
|
|
446
|
+
cursor = self.buffer.cursor
|
|
447
|
+
cells = self.buffer.row(cursor.y).cells
|
|
448
|
+
column = cursor.x if cursor.pending_wrap else cursor.x - 1
|
|
449
|
+
if 0 < column < len(cells) and not cells[column]:
|
|
450
|
+
column -= 1
|
|
451
|
+
return column, cells[column] if 0 <= column < len(cells) else ""
|
|
452
|
+
|
|
453
|
+
def _rewrite_cell(self, column: int, cluster: str) -> None:
|
|
454
|
+
"""Put ``cluster`` in the cursor's row, where the cell at ``column`` holds its beginning.
|
|
455
|
+
|
|
456
|
+
A variation selector makes a narrow character wide. What is left then is what printing
|
|
457
|
+
the cluster in one piece leaves: a program's output is cut wherever a read happens to
|
|
458
|
+
end, and that must not show on the screen.
|
|
459
|
+
"""
|
|
460
|
+
columns = self.columns
|
|
461
|
+
cursor = self.buffer.cursor
|
|
462
|
+
row = self.buffer.edit(cursor.y)
|
|
463
|
+
pen = row.pens[column]
|
|
464
|
+
width = cell_len(cluster)
|
|
465
|
+
if width == cell_len(row.cells[column]):
|
|
466
|
+
# Rewritten whole, second cell and all: half a double-width cell would be blanked.
|
|
467
|
+
row.put(column, [cluster, *[""] * (width - 1)], pen)
|
|
468
|
+
elif cursor.pending_wrap:
|
|
469
|
+
# The last column has no room for it: it is printed again whole, to land where it
|
|
470
|
+
# would have, on the next row or, without autowrap, over the last two columns.
|
|
471
|
+
row.erase(column, columns, DEFAULT_PEN, columns)
|
|
472
|
+
self._write([cluster, ""], pen)
|
|
473
|
+
else:
|
|
474
|
+
if self._insert:
|
|
475
|
+
row.insert(cursor.x, [""], pen, columns)
|
|
476
|
+
row.put(column, [cluster, ""], pen)
|
|
477
|
+
cursor.x += 1
|
|
478
|
+
if cursor.x >= columns:
|
|
479
|
+
cursor.x = columns - 1
|
|
480
|
+
cursor.pending_wrap = True
|
|
481
|
+
|
|
482
|
+
def _write(self, cells: Sequence[str], pen: Pen | None = None) -> None:
|
|
483
|
+
"""Print cells at the cursor, in the current pen unless another is given."""
|
|
484
|
+
buffer, columns = self.buffer, self.columns
|
|
485
|
+
if pen is None:
|
|
486
|
+
pen = self.pen
|
|
487
|
+
cursor = buffer.cursor
|
|
488
|
+
autowrap = _AUTOWRAP in self._switches
|
|
489
|
+
position, total = 0, len(cells)
|
|
490
|
+
while position < total:
|
|
491
|
+
if cursor.pending_wrap:
|
|
492
|
+
if autowrap:
|
|
493
|
+
self._wrap()
|
|
494
|
+
else:
|
|
495
|
+
# Nowhere to go: the last column is written over.
|
|
496
|
+
cursor.pending_wrap = False
|
|
497
|
+
end = min(position + columns - cursor.x, total)
|
|
498
|
+
if end < total and not cells[end]:
|
|
499
|
+
# A double-width character does not fit in what is left of the row.
|
|
500
|
+
end -= 1
|
|
501
|
+
if end == position:
|
|
502
|
+
if not cursor.x:
|
|
503
|
+
# A screen one column wide has no room for it anywhere.
|
|
504
|
+
position += 2
|
|
505
|
+
elif autowrap:
|
|
506
|
+
self._wrap()
|
|
507
|
+
else:
|
|
508
|
+
# Written over the last two columns, as a narrow one is over the last.
|
|
509
|
+
cursor.x -= 1
|
|
510
|
+
continue
|
|
511
|
+
row = buffer.edit(cursor.y)
|
|
512
|
+
if self._insert:
|
|
513
|
+
row.insert(cursor.x, cells[position:end], pen, columns)
|
|
514
|
+
else:
|
|
515
|
+
row.put(cursor.x, cells[position:end], pen)
|
|
516
|
+
cursor.x += end - position
|
|
517
|
+
position = end
|
|
518
|
+
if cursor.x >= columns:
|
|
519
|
+
# Whether that is a wrap is for the next character to find out: until then it only
|
|
520
|
+
# says which cell was printed last, which a character completing it has to know.
|
|
521
|
+
cursor.x = columns - 1
|
|
522
|
+
cursor.pending_wrap = True
|
|
523
|
+
|
|
524
|
+
def _wrap(self) -> None:
|
|
525
|
+
buffer = self.buffer
|
|
526
|
+
cursor = buffer.cursor
|
|
527
|
+
row = buffer.row(cursor.y)
|
|
528
|
+
cursor.x = 0
|
|
529
|
+
buffer.index(self.pen.eraser)
|
|
530
|
+
# Flagged only once the cursor has moved on. Scrolling, which may be how it moved, unflags
|
|
531
|
+
# the row it leaves behind; and below the margins on the last row it does not move at all.
|
|
532
|
+
row.wrapped = buffer.row(cursor.y) is not row
|
|
533
|
+
|
|
534
|
+
def _line_feed(self) -> None:
|
|
535
|
+
self.buffer.index(self.pen.eraser)
|
|
536
|
+
if self._newline:
|
|
537
|
+
self.buffer.cursor.x = 0
|
|
538
|
+
|
|
539
|
+
# -- cursor ------------------------------------------------------------------------------
|
|
540
|
+
|
|
541
|
+
def _move_to(self, x: int, y: int) -> None:
|
|
542
|
+
"""Absolute positioning. Under origin mode rows count from, and stay inside, the margins."""
|
|
543
|
+
buffer = self.buffer
|
|
544
|
+
if _ORIGIN in self._switches:
|
|
545
|
+
y = min(buffer.margin_top + max(y, 0), buffer.margin_bottom)
|
|
546
|
+
buffer.cursor.y = min(max(y, 0), self.lines - 1)
|
|
547
|
+
buffer.cursor.x = min(max(x, 0), self.columns - 1)
|
|
548
|
+
buffer.cursor.pending_wrap = False
|
|
549
|
+
|
|
550
|
+
def _move_by(self, columns: int, rows: int) -> None:
|
|
551
|
+
"""Relative movement, which stops at a margin the cursor starts inside of."""
|
|
552
|
+
buffer = self.buffer
|
|
553
|
+
cursor = buffer.cursor
|
|
554
|
+
if rows < 0:
|
|
555
|
+
limit = buffer.margin_top if cursor.y >= buffer.margin_top else 0
|
|
556
|
+
cursor.y = max(cursor.y + rows, limit)
|
|
557
|
+
elif rows:
|
|
558
|
+
limit = buffer.margin_bottom if cursor.y <= buffer.margin_bottom else self.lines - 1
|
|
559
|
+
cursor.y = min(cursor.y + rows, limit)
|
|
560
|
+
cursor.x = min(max(cursor.x + columns, 0), self.columns - 1)
|
|
561
|
+
cursor.pending_wrap = False
|
|
562
|
+
|
|
563
|
+
def _row_in_origin(self) -> int:
|
|
564
|
+
"""The cursor row as reports and absolute positioning count it."""
|
|
565
|
+
buffer = self.buffer
|
|
566
|
+
return buffer.cursor.y - (buffer.margin_top if _ORIGIN in self._switches else 0)
|
|
567
|
+
|
|
568
|
+
def _tab_forward(self, count: int) -> None:
|
|
569
|
+
cursor = self.buffer.cursor
|
|
570
|
+
for _ in range(count):
|
|
571
|
+
cursor.x = min((stop for stop in self._tab_stops if stop > cursor.x), default=self.columns - 1)
|
|
572
|
+
cursor.x = min(cursor.x, self.columns - 1)
|
|
573
|
+
|
|
574
|
+
def _tab_backward(self, count: int) -> None:
|
|
575
|
+
cursor = self.buffer.cursor
|
|
576
|
+
for _ in range(count):
|
|
577
|
+
cursor.x = max((stop for stop in self._tab_stops if stop < cursor.x), default=0)
|
|
578
|
+
cursor.pending_wrap = False
|
|
579
|
+
|
|
580
|
+
def _save_cursor(self) -> None:
|
|
581
|
+
cursor = self.buffer.cursor
|
|
582
|
+
self._saved_cursors[self.alternate_screen] = _SavedCursor(
|
|
583
|
+
cursor.x, cursor.y, cursor.pending_wrap, self.pen, self._charsets.snapshot(), _ORIGIN in self._switches
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
def _restore_cursor(self) -> None:
|
|
587
|
+
saved = self._saved_cursors.get(self.alternate_screen)
|
|
588
|
+
if saved is None:
|
|
589
|
+
# Nothing saved: DEC specifies the power-up state, home with default rendition.
|
|
590
|
+
self._switches.discard(_ORIGIN)
|
|
591
|
+
self._move_to(0, 0)
|
|
592
|
+
self.pen = self.pen._replace(foreground=None, background=None, attributes=DEFAULT_PEN.attributes)
|
|
593
|
+
return
|
|
594
|
+
cursor = self.buffer.cursor
|
|
595
|
+
cursor.x = min(saved.x, self.columns - 1)
|
|
596
|
+
cursor.y = min(saved.y, self.lines - 1)
|
|
597
|
+
cursor.pending_wrap = saved.pending_wrap and cursor.x == self.columns - 1
|
|
598
|
+
self.pen = saved.pen._replace(link=self.pen.link)
|
|
599
|
+
self._charsets.restore(saved.charsets)
|
|
600
|
+
self._set_switch(_ORIGIN, saved.origin)
|
|
601
|
+
|
|
602
|
+
# -- screens and modes -------------------------------------------------------------------
|
|
603
|
+
|
|
604
|
+
def _set_switch(self, mode: int, enabled: bool) -> None:
|
|
605
|
+
if enabled:
|
|
606
|
+
self._switches.add(mode)
|
|
607
|
+
else:
|
|
608
|
+
self._switches.discard(mode)
|
|
609
|
+
|
|
610
|
+
def _set_private_mode(self, mode: int, enabled: bool) -> None:
|
|
611
|
+
if mode in _SWITCHES:
|
|
612
|
+
self._set_switch(mode, enabled)
|
|
613
|
+
if mode == _ORIGIN:
|
|
614
|
+
self._move_to(0, 0)
|
|
615
|
+
elif mode in _ALTERNATE_SCREENS:
|
|
616
|
+
self._show_alternate_screen(enabled, save_cursor=mode == _ALTERNATE_SCREEN_SAVING_CURSOR)
|
|
617
|
+
elif (tracking := _TRACKING_MODES.get(mode)) is not None:
|
|
618
|
+
if enabled or self.mouse_tracking is tracking:
|
|
619
|
+
self.mouse_tracking = tracking if enabled else MouseTracking.OFF
|
|
620
|
+
elif (encoding := _ENCODING_MODES.get(mode)) is not None:
|
|
621
|
+
if enabled or self.mouse_encoding is encoding:
|
|
622
|
+
self.mouse_encoding = encoding if enabled else MouseEncoding.DEFAULT
|
|
623
|
+
|
|
624
|
+
def _private_mode_status(self, mode: int) -> int:
|
|
625
|
+
"""DECRPM status: 1 set, 2 reset, 0 for a mode this terminal does not have."""
|
|
626
|
+
if mode in _SWITCHES:
|
|
627
|
+
enabled = mode in self._switches
|
|
628
|
+
elif mode in _ALTERNATE_SCREENS:
|
|
629
|
+
enabled = self.alternate_screen
|
|
630
|
+
elif mode in _TRACKING_MODES:
|
|
631
|
+
enabled = self.mouse_tracking.value == mode
|
|
632
|
+
elif mode in _ENCODING_MODES:
|
|
633
|
+
enabled = self.mouse_encoding.value == mode
|
|
634
|
+
else:
|
|
635
|
+
return 0
|
|
636
|
+
return 1 if enabled else 2
|
|
637
|
+
|
|
638
|
+
def _show_alternate_screen(self, show: bool, *, save_cursor: bool) -> None:
|
|
639
|
+
if show == self.alternate_screen:
|
|
640
|
+
return
|
|
641
|
+
if show:
|
|
642
|
+
if save_cursor:
|
|
643
|
+
self._save_cursor()
|
|
644
|
+
primary_cursor = self._primary.cursor
|
|
645
|
+
self._alternate = ScreenBuffer(self.columns, self.lines, history_limit=0)
|
|
646
|
+
self._kitty_flags[True] = [0]
|
|
647
|
+
self._alternate.cursor.x, self._alternate.cursor.y = primary_cursor.x, primary_cursor.y
|
|
648
|
+
self.buffer = self._alternate
|
|
649
|
+
else:
|
|
650
|
+
self.buffer = self._primary
|
|
651
|
+
if save_cursor:
|
|
652
|
+
self._restore_cursor()
|
|
653
|
+
self.buffer.damage_everything()
|
|
654
|
+
|
|
655
|
+
def _soft_reset(self) -> None:
|
|
656
|
+
"""DECSTR: back to sane modes without touching what is on the screen."""
|
|
657
|
+
self._switches.discard(_ORIGIN)
|
|
658
|
+
self._switches.discard(_APPLICATION_CURSOR)
|
|
659
|
+
self._switches.discard(_APPLICATION_KEYPAD)
|
|
660
|
+
self._switches.update(_SWITCHES_ON_AT_RESET)
|
|
661
|
+
self._insert = False
|
|
662
|
+
self.pen = self.pen._replace(foreground=None, background=None, attributes=DEFAULT_PEN.attributes)
|
|
663
|
+
self.cursor_shape = CursorShape.BLOCK
|
|
664
|
+
self._charsets = Charsets()
|
|
665
|
+
self._saved_cursors.clear()
|
|
666
|
+
self.buffer.set_margins(0, self.lines - 1)
|
|
667
|
+
|
|
668
|
+
def _hard_reset(self) -> None:
|
|
669
|
+
"""RIS: the state the terminal was constructed in, history gone."""
|
|
670
|
+
self._history_dropped += self._primary.top
|
|
671
|
+
self._reset()
|
|
672
|
+
self._parser.reset()
|
|
673
|
+
|
|
674
|
+
# -- dispatch tables ---------------------------------------------------------------------
|
|
675
|
+
|
|
676
|
+
def _escape_table(self) -> dict[str, Callable[[], None]]:
|
|
677
|
+
return {
|
|
678
|
+
"7": self._save_cursor, # DECSC
|
|
679
|
+
"8": self._restore_cursor, # DECRC
|
|
680
|
+
"D": lambda: self.buffer.index(self.pen.eraser), # IND
|
|
681
|
+
"E": self._next_line, # NEL
|
|
682
|
+
"H": lambda: self._tab_stops.add(self.buffer.cursor.x), # HTS
|
|
683
|
+
"M": lambda: self.buffer.reverse_index(self.pen.eraser), # RI
|
|
684
|
+
"N": lambda: self._charsets.shift_once(2), # SS2
|
|
685
|
+
"O": lambda: self._charsets.shift_once(3), # SS3
|
|
686
|
+
"n": lambda: self._charsets.lock(2), # LS2
|
|
687
|
+
"o": lambda: self._charsets.lock(3), # LS3
|
|
688
|
+
"c": self._hard_reset, # RIS
|
|
689
|
+
"=": lambda: self._switches.add(_APPLICATION_KEYPAD), # DECKPAM
|
|
690
|
+
">": lambda: self._switches.discard(_APPLICATION_KEYPAD), # DECKPNM
|
|
691
|
+
"#8": lambda: self.buffer.fill("E"), # DECALN
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
def _control_sequence_table(self) -> dict[str, Callable[[tuple[int, ...]], None]]:
|
|
695
|
+
return {
|
|
696
|
+
"@": self._insert_characters,
|
|
697
|
+
"A": lambda p: self._move_by(0, -(p[0] or 1)), # CUU
|
|
698
|
+
"B": lambda p: self._move_by(0, p[0] or 1), # CUD
|
|
699
|
+
"C": lambda p: self._move_by(p[0] or 1, 0), # CUF
|
|
700
|
+
"D": lambda p: self._move_by(-(p[0] or 1), 0), # CUB
|
|
701
|
+
"E": lambda p: self._move_by(-self.columns, p[0] or 1), # CNL
|
|
702
|
+
"F": lambda p: self._move_by(-self.columns, -(p[0] or 1)), # CPL
|
|
703
|
+
"G": lambda p: self._move_to(p[0] - 1, self._row_in_origin()), # CHA
|
|
704
|
+
"H": self._cursor_position,
|
|
705
|
+
"I": lambda p: self._tab_forward(p[0] or 1), # CHT
|
|
706
|
+
"J": self._erase_in_display,
|
|
707
|
+
"?J": self._erase_in_display, # DECSED; nothing here is protected, so it erases like ED
|
|
708
|
+
"K": self._erase_in_line,
|
|
709
|
+
"?K": self._erase_in_line, # DECSEL
|
|
710
|
+
"L": lambda p: self._edit_lines(self.buffer.insert_lines, p), # IL
|
|
711
|
+
"M": lambda p: self._edit_lines(self.buffer.delete_lines, p), # DL
|
|
712
|
+
"P": self._delete_characters,
|
|
713
|
+
"S": lambda p: self.buffer.scroll_up(p[0] or 1, self.pen.eraser), # SU
|
|
714
|
+
"T": lambda p: self.buffer.scroll_down(p[0] or 1, self.pen.eraser), # SD
|
|
715
|
+
"X": self._erase_characters,
|
|
716
|
+
"Z": lambda p: self._tab_backward(p[0] or 1), # CBT
|
|
717
|
+
"`": lambda p: self._move_to(p[0] - 1, self._row_in_origin()), # HPA
|
|
718
|
+
"a": lambda p: self._move_by(p[0] or 1, 0), # HPR
|
|
719
|
+
"b": self._repeat,
|
|
720
|
+
"c": self._report_primary_attributes,
|
|
721
|
+
">c": self._report_secondary_attributes,
|
|
722
|
+
"d": self._line_position_absolute,
|
|
723
|
+
"e": lambda p: self._move_by(0, p[0] or 1), # VPR
|
|
724
|
+
"f": self._cursor_position, # HVP
|
|
725
|
+
"g": self._clear_tab_stops,
|
|
726
|
+
"h": lambda p: self._set_modes(p, enabled=True), # SM
|
|
727
|
+
"l": lambda p: self._set_modes(p, enabled=False), # RM
|
|
728
|
+
"?h": lambda p: self._set_private_modes(p, enabled=True), # DECSET
|
|
729
|
+
"?l": lambda p: self._set_private_modes(p, enabled=False), # DECRST
|
|
730
|
+
"n": self._report_status,
|
|
731
|
+
"?n": self._report_status_extended,
|
|
732
|
+
"!p": lambda _: self._soft_reset(), # DECSTR
|
|
733
|
+
"$p": self._report_mode,
|
|
734
|
+
"?$p": self._report_private_mode,
|
|
735
|
+
" q": self._set_cursor_shape,
|
|
736
|
+
"r": self._set_scrolling_region,
|
|
737
|
+
"s": lambda _: self._save_cursor(), # SCOSC
|
|
738
|
+
"t": self._window_operation,
|
|
739
|
+
"u": lambda _: self._restore_cursor(), # SCORC
|
|
740
|
+
">m": lambda p: self._set_modify_other_keys(p, p[1] if len(p) > 1 else 0), # XTMODKEYS
|
|
741
|
+
">n": lambda p: self._set_modify_other_keys(p, 0),
|
|
742
|
+
">u": self._push_kitty_flags,
|
|
743
|
+
"<u": self._pop_kitty_flags,
|
|
744
|
+
"=u": self._set_kitty_flags,
|
|
745
|
+
"?u": lambda _: self._replies.append(f"\x1b[?{self._kitty_flags[self.alternate_screen][-1]}u"),
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
def _operating_system_command_table(self) -> dict[str, Callable[[str], None]]:
|
|
749
|
+
return {
|
|
750
|
+
# Titles (OSC 0 and 2) fall through with everything else unlisted: accepted, ignored.
|
|
751
|
+
"7": self._report_directory_url,
|
|
752
|
+
"8": self._set_hyperlink,
|
|
753
|
+
"2025": self._report_directory,
|
|
754
|
+
"2026": self._report_command,
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
# -- control sequences -------------------------------------------------------------------
|
|
758
|
+
|
|
759
|
+
def _cursor_position(self, parameters: tuple[int, ...]) -> None:
|
|
760
|
+
"""CUP / HVP."""
|
|
761
|
+
row = (parameters[0] or 1) - 1
|
|
762
|
+
self._anchor_to_host(row)
|
|
763
|
+
self._move_to((parameters[1] or 1) - 1 if len(parameters) > 1 else 0, row)
|
|
764
|
+
|
|
765
|
+
def _line_position_absolute(self, parameters: tuple[int, ...]) -> None:
|
|
766
|
+
"""VPA."""
|
|
767
|
+
row = (parameters[0] or 1) - 1
|
|
768
|
+
self._anchor_to_host(row)
|
|
769
|
+
self._move_to(self.buffer.cursor.x, row)
|
|
770
|
+
|
|
771
|
+
def _anchor_to_host(self, row: int) -> None:
|
|
772
|
+
"""Believe the first row a repainting host names after a resize; see `resize`."""
|
|
773
|
+
if self._anchor_pending:
|
|
774
|
+
self._anchor_pending = False
|
|
775
|
+
self.buffer.anchor_cursor_row(row)
|
|
776
|
+
|
|
777
|
+
def _next_line(self) -> None:
|
|
778
|
+
"""NEL."""
|
|
779
|
+
self.buffer.index(self.pen.eraser)
|
|
780
|
+
self.buffer.cursor.x = 0
|
|
781
|
+
|
|
782
|
+
def _erase_in_display(self, parameters: tuple[int, ...]) -> None:
|
|
783
|
+
"""ED."""
|
|
784
|
+
buffer = self.buffer
|
|
785
|
+
cursor = buffer.cursor
|
|
786
|
+
eraser = self.pen.eraser
|
|
787
|
+
cursor.pending_wrap = False
|
|
788
|
+
if parameters[0] == 0:
|
|
789
|
+
buffer.edit(cursor.y).erase(cursor.x, self.columns, eraser, self.columns)
|
|
790
|
+
buffer.erase_rows(cursor.y + 1, self.lines - 1, eraser)
|
|
791
|
+
elif parameters[0] == 1:
|
|
792
|
+
buffer.erase_rows(0, cursor.y - 1, eraser)
|
|
793
|
+
buffer.edit(cursor.y).erase(0, cursor.x + 1, eraser, self.columns)
|
|
794
|
+
elif parameters[0] == 2:
|
|
795
|
+
buffer.erase_rows(0, self.lines - 1, eraser)
|
|
796
|
+
elif parameters[0] == 3:
|
|
797
|
+
self._history_dropped += buffer.clear_history()
|
|
798
|
+
|
|
799
|
+
def _erase_in_line(self, parameters: tuple[int, ...]) -> None:
|
|
800
|
+
"""EL."""
|
|
801
|
+
cursor = self.buffer.cursor
|
|
802
|
+
cursor.pending_wrap = False
|
|
803
|
+
start, end = ((cursor.x, self.columns), (0, cursor.x + 1), (0, self.columns))[min(parameters[0], 2)]
|
|
804
|
+
self.buffer.edit(cursor.y).erase(start, end, self.pen.eraser, self.columns)
|
|
805
|
+
|
|
806
|
+
def _erase_characters(self, parameters: tuple[int, ...]) -> None:
|
|
807
|
+
"""ECH."""
|
|
808
|
+
cursor = self.buffer.cursor
|
|
809
|
+
cursor.pending_wrap = False
|
|
810
|
+
end = min(cursor.x + (parameters[0] or 1), self.columns)
|
|
811
|
+
self.buffer.edit(cursor.y).erase(cursor.x, end, self.pen.eraser, self.columns)
|
|
812
|
+
|
|
813
|
+
def _insert_characters(self, parameters: tuple[int, ...]) -> None:
|
|
814
|
+
"""ICH."""
|
|
815
|
+
cursor = self.buffer.cursor
|
|
816
|
+
cursor.pending_wrap = False
|
|
817
|
+
blanks = " " * min(parameters[0] or 1, self.columns - cursor.x)
|
|
818
|
+
self.buffer.edit(cursor.y).insert(cursor.x, blanks, self.pen.eraser, self.columns)
|
|
819
|
+
|
|
820
|
+
def _delete_characters(self, parameters: tuple[int, ...]) -> None:
|
|
821
|
+
"""DCH."""
|
|
822
|
+
cursor = self.buffer.cursor
|
|
823
|
+
cursor.pending_wrap = False
|
|
824
|
+
self.buffer.edit(cursor.y).delete(cursor.x, parameters[0] or 1, self.pen.eraser, self.columns)
|
|
825
|
+
|
|
826
|
+
def _edit_lines(self, operation: Callable[[int, Pen], None], parameters: tuple[int, ...]) -> None:
|
|
827
|
+
"""IL / DL, which leave the cursor at the start of its row."""
|
|
828
|
+
operation(parameters[0] or 1, self.pen.eraser)
|
|
829
|
+
self.buffer.cursor.x = 0
|
|
830
|
+
self.buffer.cursor.pending_wrap = False
|
|
831
|
+
|
|
832
|
+
def _repeat(self, parameters: tuple[int, ...]) -> None:
|
|
833
|
+
"""REP."""
|
|
834
|
+
if self._last_printed:
|
|
835
|
+
repeated = [self._last_printed] * min(parameters[0] or 1, self.columns * self.lines)
|
|
836
|
+
self._write([cell for cluster in repeated for cell in (cluster, *[""] * (cell_len(cluster) - 1))])
|
|
837
|
+
|
|
838
|
+
def _clear_tab_stops(self, parameters: tuple[int, ...]) -> None:
|
|
839
|
+
"""TBC."""
|
|
840
|
+
if parameters[0] == 0:
|
|
841
|
+
self._tab_stops.discard(self.buffer.cursor.x)
|
|
842
|
+
elif parameters[0] == 3:
|
|
843
|
+
self._tab_stops.clear()
|
|
844
|
+
|
|
845
|
+
def _set_private_modes(self, parameters: tuple[int, ...], *, enabled: bool) -> None:
|
|
846
|
+
"""DECSET / DECRST."""
|
|
847
|
+
for mode in parameters:
|
|
848
|
+
self._set_private_mode(mode, enabled)
|
|
849
|
+
|
|
850
|
+
def _set_modes(self, parameters: tuple[int, ...], *, enabled: bool) -> None:
|
|
851
|
+
"""SM / RM."""
|
|
852
|
+
for mode in parameters:
|
|
853
|
+
if mode == _INSERT:
|
|
854
|
+
self._insert = enabled
|
|
855
|
+
elif mode == _NEWLINE:
|
|
856
|
+
self._newline = enabled
|
|
857
|
+
|
|
858
|
+
def _set_scrolling_region(self, parameters: tuple[int, ...]) -> None:
|
|
859
|
+
"""DECSTBM."""
|
|
860
|
+
top = (parameters[0] or 1) - 1
|
|
861
|
+
bottom = (parameters[1] if len(parameters) > 1 and parameters[1] else self.lines) - 1
|
|
862
|
+
bottom = min(bottom, self.lines - 1)
|
|
863
|
+
if top < bottom:
|
|
864
|
+
self.buffer.set_margins(top, bottom)
|
|
865
|
+
self._move_to(0, 0)
|
|
866
|
+
|
|
867
|
+
def _set_cursor_shape(self, parameters: tuple[int, ...]) -> None:
|
|
868
|
+
"""DECSCUSR."""
|
|
869
|
+
if parameters[0] < len(_CURSOR_SHAPES):
|
|
870
|
+
self.cursor_shape = _CURSOR_SHAPES[parameters[0]]
|
|
871
|
+
|
|
872
|
+
def _set_modify_other_keys(self, parameters: tuple[int, ...], level: int) -> None:
|
|
873
|
+
if parameters[0] == _XTERM_MODIFY_OTHER_KEYS:
|
|
874
|
+
self._modify_other_keys = level
|
|
875
|
+
|
|
876
|
+
def _push_kitty_flags(self, parameters: tuple[int, ...]) -> None:
|
|
877
|
+
stack = self._kitty_flags[self.alternate_screen]
|
|
878
|
+
stack.append(parameters[0] & _KITTY_DISAMBIGUATE)
|
|
879
|
+
del stack[1:-_KITTY_STACK_LIMIT]
|
|
880
|
+
|
|
881
|
+
def _pop_kitty_flags(self, parameters: tuple[int, ...]) -> None:
|
|
882
|
+
stack = self._kitty_flags[self.alternate_screen]
|
|
883
|
+
del stack[max(len(stack) - (parameters[0] or 1), 0) :]
|
|
884
|
+
if not stack:
|
|
885
|
+
# Emptied, which the protocol says resets every flag: those set in place on the
|
|
886
|
+
# bottom entry, never pushed, go too.
|
|
887
|
+
stack.append(0)
|
|
888
|
+
|
|
889
|
+
def _set_kitty_flags(self, parameters: tuple[int, ...]) -> None:
|
|
890
|
+
stack = self._kitty_flags[self.alternate_screen]
|
|
891
|
+
flags = parameters[0] & _KITTY_DISAMBIGUATE
|
|
892
|
+
how = parameters[1] if len(parameters) > 1 else 1
|
|
893
|
+
stack[-1] = flags if how <= 1 else stack[-1] | flags if how == 2 else stack[-1] & ~flags
|
|
894
|
+
|
|
895
|
+
def _window_operation(self, parameters: tuple[int, ...]) -> None:
|
|
896
|
+
"""XTWINOPS. Only the size report: nothing else about a window applies to a widget."""
|
|
897
|
+
if parameters[0] == 18:
|
|
898
|
+
self._replies.append(f"\x1b[8;{self.lines};{self.columns}t")
|
|
899
|
+
|
|
900
|
+
# -- reports -----------------------------------------------------------------------------
|
|
901
|
+
|
|
902
|
+
def _report_primary_attributes(self, parameters: tuple[int, ...]) -> None:
|
|
903
|
+
"""DA1: a VT220 with ANSI color."""
|
|
904
|
+
if parameters[0] == 0:
|
|
905
|
+
self._replies.append("\x1b[?62;22c")
|
|
906
|
+
|
|
907
|
+
def _report_secondary_attributes(self, parameters: tuple[int, ...]) -> None:
|
|
908
|
+
"""DA2: terminal type VT220, no firmware version worth testing against."""
|
|
909
|
+
if parameters[0] == 0:
|
|
910
|
+
self._replies.append("\x1b[>1;0;0c")
|
|
911
|
+
|
|
912
|
+
def _report_status(self, parameters: tuple[int, ...]) -> None:
|
|
913
|
+
"""DSR: operating status, or the cursor position (CPR)."""
|
|
914
|
+
if parameters[0] == 5:
|
|
915
|
+
self._replies.append("\x1b[0n")
|
|
916
|
+
elif parameters[0] == 6:
|
|
917
|
+
self._replies.append(f"\x1b[{self._row_in_origin() + 1};{self.buffer.cursor.x + 1}R")
|
|
918
|
+
|
|
919
|
+
def _report_status_extended(self, parameters: tuple[int, ...]) -> None:
|
|
920
|
+
"""DECXCPR."""
|
|
921
|
+
if parameters[0] == 6:
|
|
922
|
+
self._replies.append(f"\x1b[?{self._row_in_origin() + 1};{self.buffer.cursor.x + 1}R")
|
|
923
|
+
|
|
924
|
+
def _report_mode(self, parameters: tuple[int, ...]) -> None:
|
|
925
|
+
"""DECRQM for an ANSI mode. The answer (DECRPM) says 1 for set, 2 for reset, 0 for unknown."""
|
|
926
|
+
mode = parameters[0]
|
|
927
|
+
states = {_INSERT: self._insert, _NEWLINE: self._newline}
|
|
928
|
+
status = 0 if mode not in states else 1 if states[mode] else 2
|
|
929
|
+
self._replies.append(f"\x1b[{mode};{status}$y")
|
|
930
|
+
|
|
931
|
+
def _report_private_mode(self, parameters: tuple[int, ...]) -> None:
|
|
932
|
+
"""DECRQM for a DEC private mode."""
|
|
933
|
+
self._replies.append(f"\x1b[?{parameters[0]};{self._private_mode_status(parameters[0])}$y")
|
|
934
|
+
|
|
935
|
+
# -- operating system commands -----------------------------------------------------------
|
|
936
|
+
|
|
937
|
+
def _set_hyperlink(self, argument: str) -> None:
|
|
938
|
+
"""OSC 8 ; params ; URI. An empty URI closes the link."""
|
|
939
|
+
_, _, uri = argument.partition(";")
|
|
940
|
+
self.pen = self.pen._replace(link=uri or None)
|
|
941
|
+
|
|
942
|
+
def _report_directory_url(self, url: str) -> None:
|
|
943
|
+
"""OSC 7, the ``file://host/path`` convention shells use to announce their directory."""
|
|
944
|
+
try:
|
|
945
|
+
parts = urlsplit(url)
|
|
946
|
+
except ValueError:
|
|
947
|
+
# Brackets around something that is no IP address, say. Nowhere a shell can be.
|
|
948
|
+
return
|
|
949
|
+
host = (parts.hostname or "").partition(".")[0]
|
|
950
|
+
# A shell on the far side of ssh reports paths that mean nothing on this machine.
|
|
951
|
+
if parts.scheme == "file" and host in ("", "localhost", socket.gethostname().lower().partition(".")[0]):
|
|
952
|
+
path = unquote(parts.path)
|
|
953
|
+
# A drive path is written file:///C:/..., where the leading slash belongs to the URL.
|
|
954
|
+
self._events.append(DirectoryChanged(path[1:] if _DRIVE_PATH.match(path) else path))
|
|
955
|
+
|
|
956
|
+
def _report_directory(self, payload: str) -> None:
|
|
957
|
+
if path := _decode_payload(payload):
|
|
958
|
+
self._events.append(DirectoryChanged(path))
|
|
959
|
+
|
|
960
|
+
def _report_command(self, payload: str) -> None:
|
|
961
|
+
# An empty command is Enter at an empty prompt: nothing ran.
|
|
962
|
+
if command := _decode_payload(payload):
|
|
963
|
+
self._events.append(CommandSubmitted(command))
|