From cc340c4a4d8a5c624b764443957cfc84fcd83664 Mon Sep 17 00:00:00 2001 From: Harry Riddle Date: Wed, 29 Apr 2026 20:39:52 +0700 Subject: [PATCH] fix(tui): always call input.detect_drop for reliable image attachment Remove frontend regex pre-check that truncated paths containing spaces, quotes, or Windows drive letters. Backend _detect_file_drop correctly handles these patterns. This fixes image attachment for common filenames like "Screenshot 2026-04-29.png". Add tests: - test_input_detect_drop_path_with_spaces: attaches image with spaces in name - test_input_detect_drop_path_with_spaces_and_remainder: remainder handling Also restored missing in test_rollback_restore_resolves_number_and_file_path. Scope: tui, vision, tests --- tests/test_tui_gateway_server.py | 49 ++++++++++++++++++++++++++++++++ ui-tui/src/app/useSubmission.ts | 10 ++----- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/tests/test_tui_gateway_server.py b/tests/test_tui_gateway_server.py index 0c6263663ef..a18a1b39bf0 100644 --- a/tests/test_tui_gateway_server.py +++ b/tests/test_tui_gateway_server.py @@ -1923,6 +1923,55 @@ def test_input_detect_drop_attaches_image(monkeypatch): assert resp["result"]["text"] == "[User attached image: cat.png]" +def test_input_detect_drop_path_with_spaces(tmp_path): + """input.detect_drop correctly handles image paths containing spaces.""" + # Create a minimal PNG file with a space in its name + img = tmp_path / "screenshot with spaces.png" + img.write_bytes(b"\x89PNG\r\n\x1a\n") # valid PNG header + + server._sessions["sid"] = _session() + + resp = server.handle_request( + { + "id": "2", + "method": "input.detect_drop", + "params": {"session_id": "sid", "text": str(img)}, + } + ) + + assert resp["result"]["matched"] is True + assert resp["result"]["is_image"] is True + assert resp["result"]["path"] == str(img) + assert resp["result"]["text"] == f"[User attached image: {img.name}]" + # Verify attachment was recorded in the session + assert len(server._sessions["sid"]["attached_images"]) == 1 + assert server._sessions["sid"]["attached_images"][0] == str(img) + + +def test_input_detect_drop_path_with_spaces_and_remainder(tmp_path): + """input.detect_drop splits remainder when path contains spaces.""" + img = tmp_path / "photo with space.jpg" + img.write_bytes(b"\xff\xd8\xff" + b"fakejpeg") # minimal-ish JPEG header + + server._sessions["sid"] = _session() + + user_input = f"{img} describe this image" + resp = server.handle_request( + { + "id": "3", + "method": "input.detect_drop", + "params": {"session_id": "sid", "text": user_input}, + } + ) + + assert resp["result"]["matched"] is True + assert resp["result"]["is_image"] is True + assert resp["result"]["path"] == str(img) + # Remainder becomes the text sent to the model + assert resp["result"]["text"] == "describe this image" + assert server._sessions["sid"]["attached_images"][0] == str(img) + + def test_rollback_restore_resolves_number_and_file_path(): calls = {} diff --git a/ui-tui/src/app/useSubmission.ts b/ui-tui/src/app/useSubmission.ts index bbb288e0012..9f87a6b5dbc 100644 --- a/ui-tui/src/app/useSubmission.ts +++ b/ui-tui/src/app/useSubmission.ts @@ -126,13 +126,9 @@ export function useSubmission(opts: UseSubmissionOptions) { return sys('session not ready yet') } - // Plain prompts are the common path and should not pay an extra RPC - // before prompt.submit. File-drop detection still runs for absolute, - // tilde, file://, and explicit relative paths. - if (!looksLikeSlashCommand(text) && !/(?:^|\s)(?:file:\/\/|~\/|\.?\.\/|\/)[^\s]+/.test(text)) { - return startSubmit(text, expand(text), showUserMessage) - } - + // Always ask the backend whether this looks like a file drop. + // The backend's _detect_file_drop handles paths with spaces, quotes, + // Windows drive letters, and escaped characters correctly. gw.request('input.detect_drop', { session_id: sid, text }) .then(r => { if (!r?.matched) {