kon-coding-agent 0.3.9__tar.gz → 0.3.10__tar.gz
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.
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/CHANGELOG.md +17 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/PKG-INFO +1 -2
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/pyproject.toml +1 -2
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/app.py +22 -7
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/blocks.py +9 -27
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/input.py +17 -5
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/styles.py +27 -28
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/widgets.py +1 -1
- kon_coding_agent-0.3.10/tests/tools/test_read_image_resize.py +39 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_tool_output_expansion.py +22 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/uv.lock +1 -21
- kon_coding_agent-0.3.9/src/kon/ui/terminal_image.py +0 -34
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.github/workflows/test.yml +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.gitignore +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-release-publish/SKILL.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-tmux-test/SKILL.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-tmux-test/run-e2e-tests.sh +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-tmux-test/setup-test-project.sh +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.python-version +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/AGENTS.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/LICENSE +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/README.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/docs/e2e-test-coverage-review.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/docs/images/kon-screenshot.png +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/docs/local-models.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/scripts/show_themes.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/async_utils.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/builtin_skills/init/SKILL.md +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/config.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/_xml.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/agent_mds.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/git.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/loader.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/context/skills.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/core/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/core/compaction.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/core/handoff.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/core/types.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/defaults/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/defaults/config.toml +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/events.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/git_branch.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/base.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/models.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/oauth/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/oauth/copilot.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/oauth/openai.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/anthropic.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/azure_ai_foundry.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/copilot.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/copilot_anthropic.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/github_copilot_headers.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/mock.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_codex_responses.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_compat.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_completions.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_responses.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/sanitize.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/loop.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/notify.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/permissions.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/py.typed +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/runtime.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/session.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/sounds/completion.wav +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/sounds/error.wav +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/sounds/permission.wav +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/themes.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/_read_image.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/_tool_utils.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/base.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/bash.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/edit.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/find.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/grep.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/read.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/web_fetch.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/web_search.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools/write.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/tools_manager.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/turn.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/app_protocol.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/autocomplete.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/chat.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/clipboard.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/commands.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/export.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/floating_list.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/formatting.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/latex.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/path_complete.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/prompt_history.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/selection_mode.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/session_ui.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/tool_output.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/tree.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/ui/welcome.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/update_check.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/conftest.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/context/test_agents.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/context/test_skills.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/__init__.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_anthropic_provider.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_azure_ai_foundry_provider.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_mock_provider.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_openai_codex_provider_errors.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_openai_oauth.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_agentic_loop.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_cli_auth_flags.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_cli_provider_resolution.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_compaction.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_config_binaries.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_config_error_fallback.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_config_injection.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_config_migration.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_git_branch.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_handoff.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_handoff_link_interrupt.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_launch_warnings.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_llm_lazy_imports.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_local_auth_config.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_model_provider_resolution.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_notifications_config.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_notify.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_openai_compat.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_permissions.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_runtime_switch_model.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_session_persistence.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_session_queries.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_session_resume.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_session_tree.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_system_prompt.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_system_prompt_git_context.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_themes.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_tools_manager.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_ui_notifications.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_update_check.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/test_update_notice_behavior.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_bash_truncation.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_diff.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_edit.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_edit_display.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_read.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_read_image.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_read_image_integration.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_subprocess_cancellation.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_web_fetch.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_write.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_app_approval_keys.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_autocomplete.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_floating_list.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_info_bar_clicks.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_info_bar_permissions.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_input_approval_submit.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_input_cursor_theme.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_input_handoff.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_input_paste.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_input_shell_style.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_keybindings.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_latex.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_permission_selection_status.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_permissions_command.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_prompt_history.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_queue_editing.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_shell_command_detection.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_status_line.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_streaming_blocks.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_styles.py +0 -0
- {kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_thinking_notifications_commands.py +0 -0
|
@@ -6,6 +6,23 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
|
|
7
7
|
- No changes yet.
|
|
8
8
|
|
|
9
|
+
## 0.3.10 - 2026-05-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Increased completion popup rows from 5 to 8.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Redesigned the editor to match user message styling with panel user background and prefix.
|
|
18
|
+
- Reordered the info bar to show provider, model, and thinking state.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Stabilized completion popup scrolling.
|
|
23
|
+
- Reverted textual image display to avoid freezing issues.
|
|
24
|
+
- Corrected the image resize test expectation.
|
|
25
|
+
|
|
9
26
|
## 0.3.9 - 2026-05-20
|
|
10
27
|
|
|
11
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kon-coding-agent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: Minimal coding agent
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -15,7 +15,6 @@ Requires-Dist: pillow>=12.1.1
|
|
|
15
15
|
Requires-Dist: pydantic>=2.12.5
|
|
16
16
|
Requires-Dist: readability-lxml>=0.8.4
|
|
17
17
|
Requires-Dist: rich>=14.3.2
|
|
18
|
-
Requires-Dist: textual-image[textual]>=0.12.0
|
|
19
18
|
Requires-Dist: textual>=8.0.0
|
|
20
19
|
Description-Content-Type: text/markdown
|
|
21
20
|
|
|
@@ -14,7 +14,7 @@ default = true
|
|
|
14
14
|
|
|
15
15
|
[project]
|
|
16
16
|
name = "kon-coding-agent"
|
|
17
|
-
version = "0.3.
|
|
17
|
+
version = "0.3.10"
|
|
18
18
|
description = "Minimal coding agent"
|
|
19
19
|
readme = "README.md"
|
|
20
20
|
requires-python = ">=3.12"
|
|
@@ -31,7 +31,6 @@ dependencies = [
|
|
|
31
31
|
"readability-lxml>=0.8.4",
|
|
32
32
|
"rich>=14.3.2",
|
|
33
33
|
"textual>=8.0.0",
|
|
34
|
-
"textual-image[textual]>=0.12.0",
|
|
35
34
|
]
|
|
36
35
|
|
|
37
36
|
[dependency-groups]
|
|
@@ -23,7 +23,7 @@ from ..context.skills import (
|
|
|
23
23
|
merge_registered_skills,
|
|
24
24
|
render_skill_prompt,
|
|
25
25
|
)
|
|
26
|
-
from ..core.types import
|
|
26
|
+
from ..core.types import StopReason
|
|
27
27
|
from ..events import (
|
|
28
28
|
AgentEndEvent,
|
|
29
29
|
AgentStartEvent,
|
|
@@ -86,7 +86,7 @@ _CHANGELOG_URL = "https://github.com/0xku/kon/blob/main/CHANGELOG.md"
|
|
|
86
86
|
try:
|
|
87
87
|
VERSION = version(_PYPI_PACKAGE_NAME)
|
|
88
88
|
except PackageNotFoundError:
|
|
89
|
-
VERSION = "0.3.
|
|
89
|
+
VERSION = "0.3.10"
|
|
90
90
|
|
|
91
91
|
_NOTIFY_EVENTS = (AgentEndEvent, ToolApprovalEvent)
|
|
92
92
|
_GIT_BRANCH_REFRESH_INTERVAL_SECONDS = 1.0
|
|
@@ -214,7 +214,7 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
214
214
|
yield QueueDisplay(id="queue-display")
|
|
215
215
|
yield StatusLine(id="status-line")
|
|
216
216
|
yield InputBox(cwd=self._cwd, id="input-box")
|
|
217
|
-
yield FloatingList(window_size=
|
|
217
|
+
yield FloatingList(window_size=8, label_width=12, id="completion-list")
|
|
218
218
|
yield TreeSelector(id="tree-selector")
|
|
219
219
|
yield InfoBar(
|
|
220
220
|
cwd=self._cwd,
|
|
@@ -490,21 +490,38 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
490
490
|
# Completion message handlers
|
|
491
491
|
# -------------------------------------------------------------------------
|
|
492
492
|
|
|
493
|
+
def _is_chat_at_bottom(self) -> bool:
|
|
494
|
+
chat = self.query_one("#chat-log", ChatLog)
|
|
495
|
+
return abs(chat.max_scroll_y - chat.scroll_y) < 1
|
|
496
|
+
|
|
497
|
+
def _restore_chat_scroll_if_needed(self, was_at_bottom: bool) -> None:
|
|
498
|
+
# The completion list is a normal grid row, not a true overlay. Showing or
|
|
499
|
+
# hiding it changes the available height for ChatLog. When the chat is
|
|
500
|
+
# already bottom-aligned, that resize can leave the viewport briefly at an
|
|
501
|
+
# intermediate scroll offset and cause a visible flicker. Restore the
|
|
502
|
+
# bottom scroll position after Textual has applied the layout change.
|
|
503
|
+
if was_at_bottom:
|
|
504
|
+
chat = self.query_one("#chat-log", ChatLog)
|
|
505
|
+
chat.scroll_end(animate=False)
|
|
506
|
+
|
|
493
507
|
@on(InputBox.CompletionUpdate)
|
|
494
508
|
def on_completion_update(self, event: InputBox.CompletionUpdate) -> None:
|
|
495
509
|
if self._selection_mode is not None:
|
|
496
510
|
return
|
|
497
511
|
|
|
498
512
|
completion_list = self.query_one("#completion-list", FloatingList)
|
|
513
|
+
was_at_bottom = self._is_chat_at_bottom()
|
|
499
514
|
if completion_list.is_visible:
|
|
500
515
|
completion_list.update_items(event.items)
|
|
501
516
|
else:
|
|
502
517
|
completion_list.show(event.items)
|
|
518
|
+
self.call_after_refresh(lambda: self._restore_chat_scroll_if_needed(was_at_bottom))
|
|
503
519
|
|
|
504
520
|
@on(InputBox.CompletionHide)
|
|
505
521
|
def on_completion_hide(self, event: InputBox.CompletionHide) -> None:
|
|
506
522
|
completion_list = self.query_one("#completion-list", FloatingList)
|
|
507
523
|
input_box = self.query_one("#input-box", InputBox)
|
|
524
|
+
was_at_bottom = self._is_chat_at_bottom()
|
|
508
525
|
|
|
509
526
|
with self.batch_update():
|
|
510
527
|
completion_list.hide()
|
|
@@ -523,6 +540,8 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
523
540
|
|
|
524
541
|
input_box.set_completing(False)
|
|
525
542
|
|
|
543
|
+
self.call_after_refresh(lambda: self._restore_chat_scroll_if_needed(was_at_bottom))
|
|
544
|
+
|
|
526
545
|
@on(InputBox.CompletionSelect)
|
|
527
546
|
def on_completion_select(self, event: InputBox.CompletionSelect) -> None:
|
|
528
547
|
input_box = self.query_one("#input-box", InputBox)
|
|
@@ -1176,9 +1195,6 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
1176
1195
|
if ui_summary is None and ui_details is None and r.content:
|
|
1177
1196
|
ui_details, ui_details_full = self._format_tool_result_text(r)
|
|
1178
1197
|
success = not r.is_error
|
|
1179
|
-
images = [
|
|
1180
|
-
part for part in r.content if isinstance(part, ImageContent)
|
|
1181
|
-
]
|
|
1182
1198
|
chat.set_tool_result(
|
|
1183
1199
|
id,
|
|
1184
1200
|
ui_summary,
|
|
@@ -1186,7 +1202,6 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
1186
1202
|
success,
|
|
1187
1203
|
markup=markup,
|
|
1188
1204
|
ui_details_full=ui_details_full,
|
|
1189
|
-
images=images or None,
|
|
1190
1205
|
)
|
|
1191
1206
|
if fc:
|
|
1192
1207
|
info_bar.update_file_changes(fc.path, fc.added, fc.removed)
|
|
@@ -14,7 +14,6 @@ from kon.core.types import ImageContent
|
|
|
14
14
|
from kon.permissions import ApprovalResponse
|
|
15
15
|
|
|
16
16
|
from .formatting import format_markdown, strip_markdown_for_collapsed_text
|
|
17
|
-
from .terminal_image import image_content_to_pil, image_fallback
|
|
18
17
|
|
|
19
18
|
_UPDATE_COMMAND = "uv tool upgrade kon-coding-agent"
|
|
20
19
|
|
|
@@ -277,7 +276,6 @@ class ToolBlock(Static):
|
|
|
277
276
|
def compose(self) -> ComposeResult:
|
|
278
277
|
yield Label(self._format_header(), id="tool-header")
|
|
279
278
|
yield Label("", id="tool-output", classes="tool-output -hidden")
|
|
280
|
-
yield Static(id="tool-images", classes="tool-images -hidden")
|
|
281
279
|
|
|
282
280
|
def _format_header(self, truncate: bool = True) -> Text:
|
|
283
281
|
colors = config.ui.colors
|
|
@@ -453,7 +451,6 @@ class ToolBlock(Static):
|
|
|
453
451
|
self._awaiting_approval = False
|
|
454
452
|
self._set_state(success)
|
|
455
453
|
self._render_result_output()
|
|
456
|
-
self._render_images()
|
|
457
454
|
self.query_one("#tool-header", Label).update(self._format_header())
|
|
458
455
|
|
|
459
456
|
def set_expanded(self, expanded: bool) -> None:
|
|
@@ -478,35 +475,21 @@ class ToolBlock(Static):
|
|
|
478
475
|
output.remove_class("-hidden")
|
|
479
476
|
output.remove_class("-details")
|
|
480
477
|
output.update(rendered)
|
|
478
|
+
elif self._images:
|
|
479
|
+
image_count = len(self._images)
|
|
480
|
+
image_label = "image" if image_count == 1 else "images"
|
|
481
|
+
rendered = Text(f"Attached {image_count} {image_label}", style=config.ui.colors.dim)
|
|
482
|
+
self.remove_class("-compact")
|
|
483
|
+
self.add_class("-with-details")
|
|
484
|
+
output.remove_class("-hidden")
|
|
485
|
+
output.remove_class("-details")
|
|
486
|
+
output.update(rendered)
|
|
481
487
|
else:
|
|
482
488
|
output.update(Text(""))
|
|
483
489
|
self.remove_class("-with-details")
|
|
484
490
|
output.remove_class("-details")
|
|
485
491
|
output.add_class("-hidden")
|
|
486
492
|
|
|
487
|
-
def _render_images(self) -> None:
|
|
488
|
-
container = self.query_one("#tool-images", Static)
|
|
489
|
-
container.remove_children()
|
|
490
|
-
|
|
491
|
-
if not self._images:
|
|
492
|
-
container.add_class("-hidden")
|
|
493
|
-
return
|
|
494
|
-
|
|
495
|
-
from textual_image.widget import Image as TextualImage
|
|
496
|
-
|
|
497
|
-
self.remove_class("-compact")
|
|
498
|
-
self.add_class("-with-details")
|
|
499
|
-
container.remove_class("-hidden")
|
|
500
|
-
|
|
501
|
-
for image in self._images:
|
|
502
|
-
try:
|
|
503
|
-
widget = TextualImage(image_content_to_pil(image), classes="tool-image")
|
|
504
|
-
widget.styles.width = 60
|
|
505
|
-
widget.styles.height = "auto"
|
|
506
|
-
container.mount(widget)
|
|
507
|
-
except Exception:
|
|
508
|
-
container.mount(Label(image_fallback(image), classes="tool-image-fallback"))
|
|
509
|
-
|
|
510
493
|
|
|
511
494
|
class UserBlock(Static):
|
|
512
495
|
ALLOW_SELECT = True
|
|
@@ -526,7 +509,6 @@ class UserBlock(Static):
|
|
|
526
509
|
text.append(self._content)
|
|
527
510
|
stylize_badge_markers(text, [f"[{self._highlighted_skill}]", "[query]"])
|
|
528
511
|
else:
|
|
529
|
-
text.append("> ", style="bold")
|
|
530
512
|
text.append(self._content)
|
|
531
513
|
|
|
532
514
|
yield Label(text)
|
|
@@ -11,9 +11,9 @@ from textual import events
|
|
|
11
11
|
from textual._ansi_sequences import ANSI_SEQUENCES_KEYS
|
|
12
12
|
from textual.app import ComposeResult
|
|
13
13
|
from textual.binding import Binding
|
|
14
|
-
from textual.containers import Vertical
|
|
14
|
+
from textual.containers import Horizontal, Vertical
|
|
15
15
|
from textual.message import Message
|
|
16
|
-
from textual.widgets import TextArea
|
|
16
|
+
from textual.widgets import Label, TextArea
|
|
17
17
|
from textual.widgets.text_area import TextAreaTheme
|
|
18
18
|
|
|
19
19
|
from kon import config
|
|
@@ -131,14 +131,24 @@ class InputBox(Vertical):
|
|
|
131
131
|
height: auto;
|
|
132
132
|
min-height: 3;
|
|
133
133
|
max-height: 30vh;
|
|
134
|
-
border-top: solid
|
|
135
|
-
border-bottom: solid
|
|
134
|
+
border-top: solid transparent;
|
|
135
|
+
border-bottom: solid transparent;
|
|
136
136
|
border-title-align: left;
|
|
137
137
|
border-subtitle-align: left;
|
|
138
138
|
border-title-color: grey;
|
|
139
139
|
border-subtitle-color: grey;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
#input-row {
|
|
143
|
+
height: auto;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#input-prefix {
|
|
147
|
+
width: auto;
|
|
148
|
+
padding: 0 0 0 1;
|
|
149
|
+
text-style: bold;
|
|
150
|
+
}
|
|
151
|
+
|
|
142
152
|
InputBox .input-textarea {
|
|
143
153
|
width: 1fr;
|
|
144
154
|
height: auto;
|
|
@@ -186,7 +196,9 @@ class InputBox(Vertical):
|
|
|
186
196
|
self._selected_skill_commands: list[str] = []
|
|
187
197
|
|
|
188
198
|
def compose(self) -> ComposeResult:
|
|
189
|
-
|
|
199
|
+
with Horizontal(id="input-row"):
|
|
200
|
+
yield Label("\u203a", id="input-prefix")
|
|
201
|
+
yield Kon(self._transform_paste, id="input-textarea", classes="input-textarea")
|
|
190
202
|
|
|
191
203
|
def on_mount(self) -> None:
|
|
192
204
|
textarea = self.query_one("#input-textarea", TextArea)
|
|
@@ -17,6 +17,10 @@ def _blend_hex(base: str, overlay: str, overlay_weight: float) -> str:
|
|
|
17
17
|
def get_styles() -> str:
|
|
18
18
|
colors = config.ui.colors
|
|
19
19
|
approval_bg = _blend_hex(colors.bg, colors.accent, overlay_weight=0.05)
|
|
20
|
+
shell_bg = _blend_hex(colors.panel_user, colors.success, overlay_weight=0.15)
|
|
21
|
+
thinking_medium_bg = _blend_hex(colors.panel_user, colors.accent, overlay_weight=0.08)
|
|
22
|
+
thinking_high_bg = _blend_hex(colors.panel_user, colors.accent, overlay_weight=0.18)
|
|
23
|
+
thinking_xhigh_bg = _blend_hex(colors.panel_user, colors.badge.label, overlay_weight=0.18)
|
|
20
24
|
|
|
21
25
|
return f"""
|
|
22
26
|
Screen {{
|
|
@@ -147,22 +151,6 @@ Screen {{
|
|
|
147
151
|
padding: 0 0 0 2;
|
|
148
152
|
}}
|
|
149
153
|
|
|
150
|
-
#tool-images,
|
|
151
|
-
.tool-images {{
|
|
152
|
-
color: {colors.dim};
|
|
153
|
-
padding: 1 0 0 0;
|
|
154
|
-
}}
|
|
155
|
-
|
|
156
|
-
#tool-images.-hidden {{
|
|
157
|
-
display: none;
|
|
158
|
-
height: 0;
|
|
159
|
-
}}
|
|
160
|
-
|
|
161
|
-
.tool-image {{
|
|
162
|
-
width: 60;
|
|
163
|
-
height: auto;
|
|
164
|
-
}}
|
|
165
|
-
|
|
166
154
|
.tool-block.-with-details {{
|
|
167
155
|
padding: 0 1;
|
|
168
156
|
}}
|
|
@@ -240,37 +228,48 @@ Screen {{
|
|
|
240
228
|
|
|
241
229
|
/* Input area */
|
|
242
230
|
#input-box {{
|
|
243
|
-
|
|
244
|
-
border-
|
|
231
|
+
background: {colors.panel_user};
|
|
232
|
+
border-top: solid {colors.panel_user};
|
|
233
|
+
border-bottom: solid {colors.panel_user};
|
|
245
234
|
border-title-color: {colors.dim};
|
|
246
235
|
border-subtitle-color: {colors.dim};
|
|
247
236
|
}}
|
|
248
237
|
|
|
238
|
+
#input-prefix {{
|
|
239
|
+
color: {colors.fg};
|
|
240
|
+
text-style: bold;
|
|
241
|
+
}}
|
|
242
|
+
|
|
249
243
|
#input-box.-thinking-none,
|
|
250
244
|
#input-box.-thinking-minimal,
|
|
251
245
|
#input-box.-thinking-low {{
|
|
252
|
-
|
|
253
|
-
border-
|
|
246
|
+
background: {colors.panel_user};
|
|
247
|
+
border-top: solid {colors.panel_user};
|
|
248
|
+
border-bottom: solid {colors.panel_user};
|
|
254
249
|
}}
|
|
255
250
|
|
|
256
251
|
#input-box.-thinking-medium {{
|
|
257
|
-
|
|
258
|
-
border-
|
|
252
|
+
background: {thinking_medium_bg};
|
|
253
|
+
border-top: solid {thinking_medium_bg};
|
|
254
|
+
border-bottom: solid {thinking_medium_bg};
|
|
259
255
|
}}
|
|
260
256
|
|
|
261
257
|
#input-box.-thinking-high {{
|
|
262
|
-
|
|
263
|
-
border-
|
|
258
|
+
background: {thinking_high_bg};
|
|
259
|
+
border-top: solid {thinking_high_bg};
|
|
260
|
+
border-bottom: solid {thinking_high_bg};
|
|
264
261
|
}}
|
|
265
262
|
|
|
266
263
|
#input-box.-thinking-xhigh {{
|
|
267
|
-
|
|
268
|
-
border-
|
|
264
|
+
background: {thinking_xhigh_bg};
|
|
265
|
+
border-top: solid {thinking_xhigh_bg};
|
|
266
|
+
border-bottom: solid {thinking_xhigh_bg};
|
|
269
267
|
}}
|
|
270
268
|
|
|
271
269
|
#input-box.-shell-command {{
|
|
272
|
-
|
|
273
|
-
border-
|
|
270
|
+
background: {shell_bg};
|
|
271
|
+
border-top: solid {shell_bg};
|
|
272
|
+
border-bottom: solid {shell_bg};
|
|
274
273
|
}}
|
|
275
274
|
|
|
276
275
|
#input-box.-shell-command .input-textarea {{
|
|
@@ -241,7 +241,7 @@ class InfoBar(Vertical):
|
|
|
241
241
|
def _format_row2_right(self) -> Text:
|
|
242
242
|
model_text = self._model
|
|
243
243
|
if self._model_provider:
|
|
244
|
-
model_text = f"{self.
|
|
244
|
+
model_text = f"({self._model_provider}) {self._model}"
|
|
245
245
|
result = Text(model_text)
|
|
246
246
|
result.append(f" • {self._thinking_level}")
|
|
247
247
|
return result
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import io
|
|
3
|
+
|
|
4
|
+
from PIL import Image
|
|
5
|
+
|
|
6
|
+
from kon.tools._read_image import MAX_DIMENSION, read_and_process_image
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _write_image(path, size: tuple[int, int], color: str = "red") -> None:
|
|
10
|
+
img = Image.new("RGB", size, color=color)
|
|
11
|
+
img.save(path)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _decoded_size(base64_data: str) -> tuple[int, int]:
|
|
15
|
+
data = base64.b64decode(base64_data)
|
|
16
|
+
with Image.open(io.BytesIO(data)) as img:
|
|
17
|
+
return img.size
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_read_image_downsizes_large_images(tmp_path):
|
|
21
|
+
image_path = tmp_path / "large.png"
|
|
22
|
+
_write_image(image_path, (2520, 842))
|
|
23
|
+
|
|
24
|
+
base64_data, mime_type, resize_note = read_and_process_image(str(image_path))
|
|
25
|
+
|
|
26
|
+
assert mime_type == "image/png"
|
|
27
|
+
assert _decoded_size(base64_data) == (MAX_DIMENSION, 668)
|
|
28
|
+
assert resize_note == f"[{MAX_DIMENSION}x668, resized from 2520x842]"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_read_image_keeps_small_images(tmp_path):
|
|
32
|
+
image_path = tmp_path / "small.png"
|
|
33
|
+
_write_image(image_path, (100, 100))
|
|
34
|
+
|
|
35
|
+
base64_data, mime_type, resize_note = read_and_process_image(str(image_path))
|
|
36
|
+
|
|
37
|
+
assert mime_type == "image/png"
|
|
38
|
+
assert _decoded_size(base64_data) == (100, 100)
|
|
39
|
+
assert resize_note == "[100x100]"
|
|
@@ -3,6 +3,7 @@ from rich.text import Text
|
|
|
3
3
|
from textual.app import App, ComposeResult
|
|
4
4
|
from textual.widgets import Label
|
|
5
5
|
|
|
6
|
+
from kon.core.types import ImageContent
|
|
6
7
|
from kon.ui.blocks import ToolBlock
|
|
7
8
|
from kon.ui.chat import ChatLog
|
|
8
9
|
from kon.ui.tool_output import format_expand_hint, truncate_tool_output_text
|
|
@@ -65,3 +66,24 @@ async def test_start_tool_uses_expanded_state_before_mount():
|
|
|
65
66
|
|
|
66
67
|
assert block._expanded is True
|
|
67
68
|
assert block.query_one("#tool-output", Label)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.asyncio
|
|
72
|
+
async def test_tool_result_with_images_renders_textual_attachment_notice():
|
|
73
|
+
async with ToolExpansionTestApp().run_test() as pilot:
|
|
74
|
+
chat = pilot.app.query_one("#chat-log", ChatLog)
|
|
75
|
+
block = chat.start_tool("read", "tool-1", "~/image.png")
|
|
76
|
+
await pilot.pause()
|
|
77
|
+
|
|
78
|
+
chat.set_tool_result(
|
|
79
|
+
"tool-1",
|
|
80
|
+
"[dim]Read image[/dim]",
|
|
81
|
+
None,
|
|
82
|
+
True,
|
|
83
|
+
images=[ImageContent(data="base64data", mime_type="image/png")],
|
|
84
|
+
)
|
|
85
|
+
await pilot.pause()
|
|
86
|
+
|
|
87
|
+
output = block.query_one("#tool-output", Label)
|
|
88
|
+
assert str(output.render()) == "Attached 1 image"
|
|
89
|
+
assert not output.has_class("-hidden")
|
|
@@ -759,7 +759,7 @@ wheels = [
|
|
|
759
759
|
|
|
760
760
|
[[package]]
|
|
761
761
|
name = "kon-coding-agent"
|
|
762
|
-
version = "0.3.
|
|
762
|
+
version = "0.3.10"
|
|
763
763
|
source = { editable = "." }
|
|
764
764
|
dependencies = [
|
|
765
765
|
{ name = "aiofiles" },
|
|
@@ -774,7 +774,6 @@ dependencies = [
|
|
|
774
774
|
{ name = "readability-lxml" },
|
|
775
775
|
{ name = "rich" },
|
|
776
776
|
{ name = "textual" },
|
|
777
|
-
{ name = "textual-image", extra = ["textual"] },
|
|
778
777
|
]
|
|
779
778
|
|
|
780
779
|
[package.dev-dependencies]
|
|
@@ -800,7 +799,6 @@ requires-dist = [
|
|
|
800
799
|
{ name = "readability-lxml", specifier = ">=0.8.4" },
|
|
801
800
|
{ name = "rich", specifier = ">=14.3.2" },
|
|
802
801
|
{ name = "textual", specifier = ">=8.0.0" },
|
|
803
|
-
{ name = "textual-image", extras = ["textual"], specifier = ">=0.12.0" },
|
|
804
802
|
]
|
|
805
803
|
|
|
806
804
|
[package.metadata.requires-dev]
|
|
@@ -1643,24 +1641,6 @@ wheels = [
|
|
|
1643
1641
|
{ url = "https://files.pythonhosted.org/packages/d3/be/e191c2a15da20530fde03564564e3e4b4220eb9d687d4014957e5c6a5e85/textual-8.0.0-py3-none-any.whl", hash = "sha256:8908f4ebe93a6b4f77ca7262197784a52162bc88b05f4ecf50ac93a92d49bb8f", size = 718904, upload-time = "2026-02-16T17:12:11.962Z" },
|
|
1644
1642
|
]
|
|
1645
1643
|
|
|
1646
|
-
[[package]]
|
|
1647
|
-
name = "textual-image"
|
|
1648
|
-
version = "0.12.0"
|
|
1649
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1650
|
-
dependencies = [
|
|
1651
|
-
{ name = "pillow" },
|
|
1652
|
-
{ name = "rich" },
|
|
1653
|
-
]
|
|
1654
|
-
sdist = { url = "https://files.pythonhosted.org/packages/c2/e7/c82ea0604874b6d51d5717a0911061ae5810e36dad2e4d2b11fa7d54cdaa/textual_image-0.12.0.tar.gz", hash = "sha256:fdd0b5ff9c8a99740bc360a99ce014d563fa97d07a5b49b472470809f57c0a74", size = 116403, upload-time = "2026-04-12T17:37:44.696Z" }
|
|
1655
|
-
wheels = [
|
|
1656
|
-
{ url = "https://files.pythonhosted.org/packages/67/2c/38ac586a3834a3bd8cbcf0c8ea1ae23bf68b9cc13b96a67f47a825479dd3/textual_image-0.12.0-py3-none-any.whl", hash = "sha256:0b13f62fe6a29f4ed6dfd641f2779ffe92dde41f16163fa1de69158477aa50aa", size = 115626, upload-time = "2026-04-12T17:37:41.238Z" },
|
|
1657
|
-
]
|
|
1658
|
-
|
|
1659
|
-
[package.optional-dependencies]
|
|
1660
|
-
textual = [
|
|
1661
|
-
{ name = "textual" },
|
|
1662
|
-
]
|
|
1663
|
-
|
|
1664
1644
|
[[package]]
|
|
1665
1645
|
name = "tqdm"
|
|
1666
1646
|
version = "4.67.3"
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import io
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
|
|
5
|
-
from PIL import Image as PILImage
|
|
6
|
-
|
|
7
|
-
from ..core.types import ImageContent
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass(frozen=True)
|
|
11
|
-
class ImageDimensions:
|
|
12
|
-
width: int
|
|
13
|
-
height: int
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def image_content_to_pil(image: ImageContent) -> PILImage.Image:
|
|
17
|
-
data = base64.b64decode(image.data)
|
|
18
|
-
return PILImage.open(io.BytesIO(data))
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def get_image_dimensions(image: ImageContent) -> ImageDimensions | None:
|
|
22
|
-
try:
|
|
23
|
-
with image_content_to_pil(image) as img:
|
|
24
|
-
width, height = img.size
|
|
25
|
-
return ImageDimensions(width=width, height=height)
|
|
26
|
-
except Exception:
|
|
27
|
-
return None
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def image_fallback(image: ImageContent) -> str:
|
|
31
|
-
dimensions = get_image_dimensions(image)
|
|
32
|
-
if dimensions is None:
|
|
33
|
-
return f"[Image: {image.mime_type}]"
|
|
34
|
-
return f"[Image: {image.mime_type} {dimensions.width}x{dimensions.height}]"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-tmux-test/run-e2e-tests.sh
RENAMED
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/.kon/skills/kon-tmux-test/setup-test-project.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/azure_ai_foundry.py
RENAMED
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/copilot_anthropic.py
RENAMED
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/github_copilot_headers.py
RENAMED
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_codex_responses.py
RENAMED
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_completions.py
RENAMED
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/src/kon/llm/providers/openai_responses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_azure_ai_foundry_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/llm/test_openai_codex_provider_errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_read_image_integration.py
RENAMED
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/tools/test_subprocess_cancellation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_permission_selection_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kon_coding_agent-0.3.9 → kon_coding_agent-0.3.10}/tests/ui/test_thinking_notifications_commands.py
RENAMED
|
File without changes
|