Skip to content

Support file tree path drops into terminal tabs#296

Merged
AkaraChen merged 4 commits into
devfrom
terminal/file-tree-drop-input
Jun 3, 2026
Merged

Support file tree path drops into terminal tabs#296
AkaraChen merged 4 commits into
devfrom
terminal/file-tree-drop-input

Conversation

@AkaraChen
Copy link
Copy Markdown
Owner

@AkaraChen AkaraChen commented May 30, 2026

Summary

  • add file-tree drag payload helpers for terminal drops
  • let terminal tab and active pane refs accept file-tree drop events and write raw paths into the PTY
  • prevent drag gestures from immediately opening files while preserving normal click behavior
  • add focused coverage for file-tree payloads and terminal path drop handling

Validation

  • Not run per request

Summary by CodeRabbit

Release Notes

  • New Features

    • Drag-and-drop file paths from the file tree directly into terminal tabs to insert absolute file paths
    • Terminal tabs now properly handle dropped files and activate the target tab
  • Tests

    • Added comprehensive test coverage for file tree drag-and-drop operations
    • Added tests for terminal tab drop interactions, payload validation, and path formatting

@netlify
Copy link
Copy Markdown

netlify Bot commented May 30, 2026

Deploy Preview for grand-selkie-225f87 canceled.

Name Link
🔨 Latest commit aa545d8
🔍 Latest deploy log https://app.netlify.com/projects/grand-selkie-225f87/deploys/6a1f91aaf674c9000814d1c1

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Review Change Stack

Warning

Review limit reached

@AkaraChen, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 41 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ed3b419-fcdb-4b63-a316-2d81e4068a02

📥 Commits

Reviewing files that changed from the base of the PR and between 6940f98 and aa545d8.

📒 Files selected for processing (1)
  • src/features/terminal/TerminalTabs.tsx
📝 Walkthrough

Walkthrough

This pull request implements file tree drag-and-drop into terminal tabs. It establishes a shared drag-drop payload contract with serialization utilities, adds drag initiation in FileTreePanel, exposes terminal tab elements via ref callbacks in TabStrip, and implements drop handling in TerminalTabs to activate tabs and write paths to the PTY.

Changes

File tree drag-and-drop to terminal tabs

Layer / File(s) Summary
Terminal-drop payload contract and utilities
src/shared/lib/fileTreeTerminalDrop.ts, src/shared/lib/fileTreeTerminalDrop.test.ts
Exports constants for custom MIME type, event name, and DOM target attribute. Defines FileTreeTerminalDropPayload (profileId, rootPath, relative/absolute path arrays) and FileTreeTerminalDropEventDetail (client coordinates and payload). Implements utilities: createFileTreeTerminalDropPayload constructs payloads, writeFileTreeTerminalDropPayload serializes to DataTransfer (JSON + text/plain), readFileTreeTerminalDropPayload deserializes with validation, getFileTreeTerminalDropTargetAtPoint detects drop targets, formatTerminalPathInput formats paths for terminal. Tests verify round-trip serialization and formatting.
FileTreePanel drag initiation and payload creation
src/features/projects/FileTreePanel.tsx, src/features/projects/FileTreePanel.test.tsx
Adds getTreeItemPathFromComposedPath helper to resolve item paths from native event. Introduces refs (skipNextClickOpenRef, skipNextClickOpenUntilRef, dragPayloadRef) to coordinate click and drag timing. Updates handleTreeClick and handleTreeMouseDown to suppress opening when dragging. Implements handleTreeDragStart to create and write payload, and handleTreeDragEnd to dispatch custom terminal-drop event with client coordinates and payload. Wires onDragStart/onDragEnd into FileTree. Tests verify click-timing suppression and drag-drop data serialization.
Terminal tab ref infrastructure
src/features/terminal/TabStrip.tsx
Extends TabStripItem interface with optional elementRef: RefCallback<HTMLDivElement>. Updates TabButton to accept and attach elementRef to Chakra Box. Forwards elementRef from TabMotionItem to TabButton.
TerminalTabs drop handler and ref binding
src/features/terminal/TerminalTabs.tsx, src/features/terminal/TerminalTabs.test.tsx
Adds handleTerminalPathDrop to validate payload profileId, format paths, activate target terminal tab, and call writeToPty. Introduces createTerminalDropRef to attach/remove drop event listener and manage drop-target DOM attribute. Wires elementRef: createTerminalDropRef(tab) to each terminal tab. Conditionally attaches drop ref to terminal container based on active tab. Test verifies drop payload activates non-active tab, writes concatenated paths to PTY with correct sessionId, sets fileTabActive to false, and renders terminal with data-active="true".
PTY write mock
src/test/setup.ts
Adds writeToPty mocked IPC command to test setup that resolves successfully.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Files now drag through the breeze,
Landing softly in terminal trees,
Refs thread paths like whispered song,
Drop targets glow when tabs belong,
Tab by tab, the PTY sings!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and specifically summarizes the main feature: enabling file tree path drops into terminal tabs. It accurately reflects the core functionality across all changed files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch terminal/file-tree-drop-input

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@AkaraChen
Copy link
Copy Markdown
Owner Author

@codex review

Repository owner deleted a comment from chatgpt-codex-connector Bot May 30, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for dragging file-tree paths into terminal tabs/panes and writing those paths into the target PTY session.

Changes:

  • Added shared file-tree terminal drop payload helpers.
  • Wired file-tree drag start/end handling to dispatch terminal drop events.
  • Added terminal tab/pane drop targets and tests for payload/drop handling.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/shared/lib/fileTreeTerminalDrop.ts Defines the drag payload format, serialization helpers, target lookup, and terminal input formatting.
src/shared/lib/fileTreeTerminalDrop.test.ts Adds coverage for payload round-tripping and path formatting.
src/features/projects/FileTreePanel.tsx Creates file-tree drag payloads and dispatches custom terminal drop events on drag end.
src/features/projects/FileTreePanel.test.tsx Adds coverage for click suppression and drag payload generation.
src/features/terminal/TerminalTabs.tsx Registers terminal tabs/pane as drop targets and writes dropped paths to PTY sessions.
src/features/terminal/TerminalTabs.test.tsx Adds terminal drop behavior coverage.
src/features/terminal/TabStrip.tsx Exposes tab element refs so terminal tabs can be marked as drop targets.
src/test/setup.ts Adds a writeToPty mock for tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +148 to +149
fireEvent.dragOver(terminalTab, { dataTransfer });
fireEvent.drop(terminalTab, { dataTransfer });
Comment on lines +103 to +105
return paths
.filter((path) => path.length > 0)
.join(" ");
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1abc51a1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +102 to +105
export function formatTerminalPathInput(paths: readonly string[]) {
return paths
.filter((path) => path.length > 0)
.join(" ");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Escape dropped paths before writing them

When any dropped file or directory contains spaces, this formatter writes the raw absolute path into the PTY and separates paths with a single space, so a normal shell command like cat /root/path with spaces/file.ts is parsed as multiple arguments instead of one path. This affects common project/file names with spaces; quote or shell-escape each path before joining so the terminal receives usable path arguments.

Useful? React with 👍 / 👎.

Comment on lines +1011 to +1016
if (payload) {
const target = getFileTreeTerminalDropTargetAtPoint(
event.clientX,
event.clientY,
);
target?.dispatchEvent(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore canceled drags before dispatching terminal drops

This dispatches the terminal-drop event for any dragend that finishes over a terminal target, even if the user canceled the drag (for example by pressing Escape while hovering over the terminal); MDN documents dropEffect === "none" on dragend for canceled drags (https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#finishing_the_drag). In that scenario the path is still written into the PTY despite no drop being performed, so the handler needs to distinguish a real accepted drop from cancellation.

Useful? React with 👍 / 👎.

Comment on lines +148 to +149
fireEvent.dragOver(terminalTab, { dataTransfer });
fireEvent.drop(terminalTab, { dataTransfer });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Dispatch the custom terminal-drop event in this test

This test fires native dragOver/drop events, but the component added in this change only listens for FILE_TREE_TERMINAL_DROP_EVENT via addEventListener, not for the browser drop event. As written, handleTerminalPathDrop never runs and writeToPtyMock remains uncalled, so the new test fails unless it dispatches the same custom event that FileTreePanel sends on drag end.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/features/projects/FileTreePanel.test.tsx (1)

278-305: 💤 Low value

Consider extracting createTestDataTransfer to a shared test utility.

This helper is duplicated in src/shared/lib/fileTreeTerminalDrop.test.ts (lines 10-28). While test helpers are often locally defined, extracting it to src/test/testUtils.ts or similar would reduce duplication and ensure consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/projects/FileTreePanel.test.tsx` around lines 278 - 305, The
createTestDataTransfer helper is duplicated; extract the function
createTestDataTransfer into a shared test utilities module (e.g., export it from
a new test utils file) and replace the local copies with imports, ensuring the
exported function preserves the same behavior (returns a DataTransfer-like
object with dropEffect, effectAllowed, files, items, types, clearData, getData,
setData, setDragImage and uses vi.fn for spies). Update the tests that used the
duplicated implementation to import createTestDataTransfer from the new shared
module and remove the local definitions so all tests reuse the single exported
helper.
src/features/terminal/TerminalTabs.tsx (1)

220-241: ⚖️ Poor tradeoff

createTerminalDropRef returns a new callback every render, forcing listener teardown/re-add on each commit.

Because a fresh function instance is passed as ref on every render, React invokes the previous callback with null (removing the listener + attribute) and the new one with the node after each commit. This works but is wasteful. Consider memoizing per-tab refs (e.g. a Map<sessionId, RefCallback> kept in a ref) so identities stay stable across renders.

Also applies to: 255-255, 417-421

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/terminal/TerminalTabs.tsx` around lines 220 - 241,
createTerminalDropRef currently yields a new ref callback every render which
forces React to call the old callback with null (tearing down
listeners/attributes) and then reattach on the new callback; to fix, memoize
stable ref callbacks per tab id by keeping a Map<string, RefCallback> in a React
ref (e.g., useRef(new Map())), return the cached callback for tab.id from
createTerminalDropRef, and ensure the cached callback manages its own cleanup
(removing FILE_TREE_TERMINAL_DROP_EVENT listener and
FILE_TREE_TERMINAL_DROP_TARGET_ATTR) and calls handleTerminalPathDrop with the
CustomEvent<FileTreeTerminalDropEventDetail> when triggered; update all usages
(including the other occurrences noted) to use the memoized callbacks so
listener identity remains stable across renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/terminal/TerminalTabs.test.tsx`:
- Around line 142-167: The test fails because it dispatches native drag/drop
events but TerminalTabs listens for the custom FILE_TREE_TERMINAL_DROP_EVENT
(via createTerminalDropRef) so handleTerminalPathDrop and writeToPty never run;
fix the test by dispatching a CustomEvent with the same shape used in production
(event type FILE_TREE_TERMINAL_DROP_EVENT and detail.payload matching
writeFileTreeTerminalDropPayload), target the tab element (terminal-session
element or terminal tab) with dispatchEvent(element.dispatchEvent(new
CustomEvent(...))), import FILE_TREE_TERMINAL_DROP_EVENT from
"`@/shared/lib/fileTreeTerminalDrop`", and remove now-unused helpers/imports
(createTestDataTransfer, createFileTreeDrop, fireEvent usage for native drop,
and writeFileTreeTerminalDropPayload) to satisfy noUnusedLocals.

In `@src/features/terminal/TerminalTabs.tsx`:
- Around line 205-218: In handleTerminalPathDrop, the call to writeToPty({
sessionId: tab.id, data }) returns a Promise that isn’t handled; wrap this call
with proper error handling—either make handleTerminalPathDrop async and await
writeToPty inside a try/catch, or append .catch(...) to writeToPty—to log or
surface IPC errors. Keep existing calls to setActiveTab and setTerminalActive
unchanged and ensure errors reference the sessionId/tab.id and data context for
easier debugging.

In `@src/shared/lib/fileTreeTerminalDrop.test.ts`:
- Around line 48-55: The test in fileTreeTerminalDrop.test.ts currently asserts
unescaped space-containing paths and must be updated to expect properly
shell-escaped/quoted output from formatTerminalPathInput; change the expected
string to match the escaping convention implemented in formatTerminalPathInput
(e.g., wrap paths with spaces in single-quotes or otherwise escape spaces) so
the test asserts the escaped form (refer to formatTerminalPathInput and the
related escape logic in fileTreeTerminalDrop.ts around the earlier comment).
Ensure the test still passes multiple-path input and checks the exact
escaped/quoted concatenation returned by formatTerminalPathInput.

In `@src/shared/lib/fileTreeTerminalDrop.ts`:
- Around line 102-106: The current formatTerminalPathInput(paths) blindly joins
with spaces and must instead perform shell-aware escaping; update
formatTerminalPathInput to accept an optional shell parameter (or detect via
process.env.SHELL / process.platform) and escape each path per-shell before
joining. For Unix-like shells, wrap each path in single quotes and escape
embedded single quotes by replacing ' with '\'' (i.e. "'\"'\"'"); for
PowerShell/Windows, wrap in double quotes and escape embedded " appropriately
(e.g. by doubling or using PowerShell backtick); ensure the function returns the
joined, escaped paths string used when writing to the PTY so paths with
spaces/special chars are preserved.

---

Nitpick comments:
In `@src/features/projects/FileTreePanel.test.tsx`:
- Around line 278-305: The createTestDataTransfer helper is duplicated; extract
the function createTestDataTransfer into a shared test utilities module (e.g.,
export it from a new test utils file) and replace the local copies with imports,
ensuring the exported function preserves the same behavior (returns a
DataTransfer-like object with dropEffect, effectAllowed, files, items, types,
clearData, getData, setData, setDragImage and uses vi.fn for spies). Update the
tests that used the duplicated implementation to import createTestDataTransfer
from the new shared module and remove the local definitions so all tests reuse
the single exported helper.

In `@src/features/terminal/TerminalTabs.tsx`:
- Around line 220-241: createTerminalDropRef currently yields a new ref callback
every render which forces React to call the old callback with null (tearing down
listeners/attributes) and then reattach on the new callback; to fix, memoize
stable ref callbacks per tab id by keeping a Map<string, RefCallback> in a React
ref (e.g., useRef(new Map())), return the cached callback for tab.id from
createTerminalDropRef, and ensure the cached callback manages its own cleanup
(removing FILE_TREE_TERMINAL_DROP_EVENT listener and
FILE_TREE_TERMINAL_DROP_TARGET_ATTR) and calls handleTerminalPathDrop with the
CustomEvent<FileTreeTerminalDropEventDetail> when triggered; update all usages
(including the other occurrences noted) to use the memoized callbacks so
listener identity remains stable across renders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9465e04a-d0a0-4e45-93dc-32c4d1dfd51f

📥 Commits

Reviewing files that changed from the base of the PR and between 6a40145 and 6940f98.

📒 Files selected for processing (8)
  • src/features/projects/FileTreePanel.test.tsx
  • src/features/projects/FileTreePanel.tsx
  • src/features/terminal/TabStrip.tsx
  • src/features/terminal/TerminalTabs.test.tsx
  • src/features/terminal/TerminalTabs.tsx
  • src/shared/lib/fileTreeTerminalDrop.test.ts
  • src/shared/lib/fileTreeTerminalDrop.ts
  • src/test/setup.ts

Comment on lines +142 to +167
const dataTransfer = createFileTreeDrop([
"/root/src/index.ts",
"/root/src/components",
]);
const terminalTab = screen.getByRole("tab", { name: /Terminal 2/ });

fireEvent.dragOver(terminalTab, { dataTransfer });
fireEvent.drop(terminalTab, { dataTransfer });

await waitFor(() => {
expect(writeToPtyMock).toHaveBeenCalledWith({
sessionId: "session-2",
data: "/root/src/index.ts /root/src/components",
});
});
expect(useTerminalStore.getState().profiles[profileId].activeTabId).toBe(
"session-2",
);
expect(
useFileViewerTabsStore.getState().profiles[profileId]?.fileTabActive,
).toBe(false);
expect(screen.getByTestId("terminal-session-2")).toHaveAttribute(
"data-active",
"true",
);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Test never triggers the drop handler — root cause of the failing pipeline.

TerminalTabs attaches its listener for the custom FILE_TREE_TERMINAL_DROP_EVENT (createTerminalDropRef), not the native drop event. Firing fireEvent.dragOver/fireEvent.drop dispatches native events that no listener handles, so handleTerminalPathDrop (and thus writeToPty) never runs — matching the Number of calls: 0 failure at Line 152. Dispatch the custom event with a detail.payload instead.

💚 Proposed fix
-		const dataTransfer = createFileTreeDrop([
-			"/root/src/index.ts",
-			"/root/src/components",
-		]);
-		const terminalTab = screen.getByRole("tab", { name: /Terminal 2/ });
-
-		fireEvent.dragOver(terminalTab, { dataTransfer });
-		fireEvent.drop(terminalTab, { dataTransfer });
+		const absolutePaths = ["/root/src/index.ts", "/root/src/components"];
+		const payload = createFileTreeTerminalDropPayload({
+			profileId,
+			rootPath: "/root",
+			relativePaths: absolutePaths.map((p) => p.replace("/root/", "")),
+			absolutePaths,
+		});
+		const terminalTab = screen.getByRole("tab", { name: /Terminal 2/ });
+
+		terminalTab.dispatchEvent(
+			new CustomEvent(FILE_TREE_TERMINAL_DROP_EVENT, {
+				bubbles: true,
+				detail: { clientX: 0, clientY: 0, payload },
+			}),
+		);

Note: with this change createTestDataTransfer, createFileTreeDrop, fireEvent, and the writeFileTreeTerminalDropPayload import become unused and must be removed (project enforces noUnusedLocals). You will also need to import FILE_TREE_TERMINAL_DROP_EVENT from @/shared/lib/fileTreeTerminalDrop.

🧰 Tools
🪛 GitHub Check: Frontend

[failure] 152-152: src/features/terminal/TerminalTabs.test.tsx > terminalTabs file tree drops > activates the dropped terminal tab and writes dropped paths to its PTY
AssertionError: expected "vi.fn()" to be called with arguments: [ { sessionId: 'session-2', …(1) } ]

Number of calls: 0

Ignored nodes: comments, script, style

Terminal 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/terminal/TerminalTabs.test.tsx` around lines 142 - 167, The test
fails because it dispatches native drag/drop events but TerminalTabs listens for
the custom FILE_TREE_TERMINAL_DROP_EVENT (via createTerminalDropRef) so
handleTerminalPathDrop and writeToPty never run; fix the test by dispatching a
CustomEvent with the same shape used in production (event type
FILE_TREE_TERMINAL_DROP_EVENT and detail.payload matching
writeFileTreeTerminalDropPayload), target the tab element (terminal-session
element or terminal tab) with dispatchEvent(element.dispatchEvent(new
CustomEvent(...))), import FILE_TREE_TERMINAL_DROP_EVENT from
"`@/shared/lib/fileTreeTerminalDrop`", and remove now-unused helpers/imports
(createTestDataTransfer, createFileTreeDrop, fireEvent usage for native drop,
and writeFileTreeTerminalDropPayload) to satisfy noUnusedLocals.

Comment on lines +205 to +218
function handleTerminalPathDrop(
detail: FileTreeTerminalDropEventDetail,
tab: { id: string },
) {
const { payload } = detail;
if (payload.profileId !== profileId) return;

const data = formatTerminalPathInput(payload.absolutePaths);
if (!data) return;

setActiveTab(profileId, tab.id);
setTerminalActive(profileId);
writeToPty({ sessionId: tab.id, data });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

writeToPty rejection is unhandled.

writeToPty returns a promise that is neither awaited nor caught here; an IPC failure becomes an unhandled rejection. Add minimal error handling.

🛡️ Proposed fix
 		setActiveTab(profileId, tab.id);
 		setTerminalActive(profileId);
-		writeToPty({ sessionId: tab.id, data });
+		writeToPty({ sessionId: tab.id, data }).catch(() => {
+			// PTY write failed; nothing actionable on the UI side
+		});
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function handleTerminalPathDrop(
detail: FileTreeTerminalDropEventDetail,
tab: { id: string },
) {
const { payload } = detail;
if (payload.profileId !== profileId) return;
const data = formatTerminalPathInput(payload.absolutePaths);
if (!data) return;
setActiveTab(profileId, tab.id);
setTerminalActive(profileId);
writeToPty({ sessionId: tab.id, data });
}
function handleTerminalPathDrop(
detail: FileTreeTerminalDropEventDetail,
tab: { id: string },
) {
const { payload } = detail;
if (payload.profileId !== profileId) return;
const data = formatTerminalPathInput(payload.absolutePaths);
if (!data) return;
setActiveTab(profileId, tab.id);
setTerminalActive(profileId);
writeToPty({ sessionId: tab.id, data }).catch(() => {
// PTY write failed; nothing actionable on the UI side
});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/terminal/TerminalTabs.tsx` around lines 205 - 218, In
handleTerminalPathDrop, the call to writeToPty({ sessionId: tab.id, data })
returns a Promise that isn’t handled; wrap this call with proper error
handling—either make handleTerminalPathDrop async and await writeToPty inside a
try/catch, or append .catch(...) to writeToPty—to log or surface IPC errors.
Keep existing calls to setActiveTab and setTerminalActive unchanged and ensure
errors reference the sessionId/tab.id and data context for easier debugging.

Comment on lines +48 to +55
it("formats terminal input as raw absolute paths", () => {
expect(
formatTerminalPathInput([
"/root/src/index.ts",
"/root/path with spaces/file.ts",
]),
).toBe("/root/src/index.ts /root/path with spaces/file.ts");
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Test validates broken shell parsing behavior.

This test expects unquoted paths with spaces to be joined with a single space, which produces invalid shell input. The expected result "/root/src/index.ts /root/path with spaces/file.ts" would be mis-parsed by shells as multiple arguments. Once formatTerminalPathInput is fixed to add proper shell escaping/quoting (see comment on lines 102-106 in fileTreeTerminalDrop.ts), update this test to verify the escaped output.

📝 Expected test update after fixing formatTerminalPathInput

If using single-quote escaping for Unix shells:

 	it("formats terminal input as raw absolute paths", () => {
 		expect(
 			formatTerminalPathInput([
 				"/root/src/index.ts",
 				"/root/path with spaces/file.ts",
 			]),
-		).toBe("/root/src/index.ts /root/path with spaces/file.ts");
+		).toBe("'/root/src/index.ts' '/root/path with spaces/file.ts'");
 	});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/lib/fileTreeTerminalDrop.test.ts` around lines 48 - 55, The test
in fileTreeTerminalDrop.test.ts currently asserts unescaped space-containing
paths and must be updated to expect properly shell-escaped/quoted output from
formatTerminalPathInput; change the expected string to match the escaping
convention implemented in formatTerminalPathInput (e.g., wrap paths with spaces
in single-quotes or otherwise escape spaces) so the test asserts the escaped
form (refer to formatTerminalPathInput and the related escape logic in
fileTreeTerminalDrop.ts around the earlier comment). Ensure the test still
passes multiple-path input and checks the exact escaped/quoted concatenation
returned by formatTerminalPathInput.

Comment on lines +102 to +106
export function formatTerminalPathInput(paths: readonly string[]) {
return paths
.filter((path) => path.length > 0)
.join(" ");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

❓ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Verify shell quoting behavior for paths with spaces

echo "Testing shell argument parsing with spaces:"
echo
echo "Unquoted path with spaces (will split incorrectly):"
echo /root/path with spaces/file.ts | xargs -n1 echo "Arg:"
echo
echo "Single-quoted path (preserves literal, correct):"
echo '/root/path with spaces/file.ts' | xargs -n1 echo "Arg:"
echo
echo "Escaped spaces (also correct):"
echo /root/path\ with\ spaces/file.ts | xargs -n1 echo "Arg:"

Repository: AkaraChen/2code

Length of output: 376


Fix shell-escaping for terminal path arguments
formatTerminalPathInput joins paths with a single space but does not quote/escape them; when this string is written into a shell command line (via PTY), paths containing spaces/special characters will be split into multiple arguments, breaking common inputs.

The provided shell-parsing snippet doesn’t properly validate quoting behavior for the PTY case because the local shell strips quote characters before xargs performs its splitting.

🛡️ Proposed fix (apply for Unix-like shells)
 export function formatTerminalPathInput(paths: readonly string[]) {
-	return paths
-		.filter((path) =&gt; path.length &gt; 0)
-		.join(" ");
+	return paths
+		.filter((path) =&gt; path.length &gt; 0)
+		.map((path) =&gt; `'${path.replace(/'/g, "'\\''")}'`)
+		.join(" ");
 }

Need shell/OS-specific escaping (PowerShell vs bash/zsh differ); Unix single-quote wrapping shouldn’t be applied blindly if the active terminal shell is PowerShell/Windows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/lib/fileTreeTerminalDrop.ts` around lines 102 - 106, The current
formatTerminalPathInput(paths) blindly joins with spaces and must instead
perform shell-aware escaping; update formatTerminalPathInput to accept an
optional shell parameter (or detect via process.env.SHELL / process.platform)
and escape each path per-shell before joining. For Unix-like shells, wrap each
path in single quotes and escape embedded single quotes by replacing ' with '\''
(i.e. "'\"'\"'"); for PowerShell/Windows, wrap in double quotes and escape
embedded " appropriately (e.g. by doubling or using PowerShell backtick); ensure
the function returns the joined, escaped paths string used when writing to the
PTY so paths with spaces/special chars are preserved.

@AkaraChen AkaraChen merged commit 0174e95 into dev Jun 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants