fix(scope): prevent browser default drop from creating empty rounds#254
Open
beruro wants to merge 7 commits into
Open
fix(scope): prevent browser default drop from creating empty rounds#254beruro wants to merge 7 commits into
beruro wants to merge 7 commits into
Conversation
When an OS file is dragged onto the chat input, the browser's default contenteditable drop behaviour (inserting the file name as raw text) was not being suppressed. This corrupted the editor state and, in certain timing windows, triggered an empty conversation round. Two-part fix: 1. `ComposerInput/index.tsx` – `handleDropEvent` now calls `event.preventDefault()` unconditionally before invoking `createDropHandler`. OS file drops are already handled by GlobalDragDrop via the Jotai `droppedFilesAtom` → `useDroppedFilesConsumer` pill path; letting the browser also insert raw text was the defect. 2. `useContainerDrag.ts` – `handleContainerDrop` now calls `event.preventDefault()` + `event.stopPropagation()` for ALL drop events on the composer container, not only for internal/reference drags. Only internal-type drops (file-tree, application/x-file-reference, reference drag) are forwarded to the `handleDrop` handler. Extracted `isInternalDropType` into `containerDropHelpers.ts` and added unit tests covering both helpers. Added `TEST_CASES.md` for manual QA. Closes #250 Pre-commit hook ran. Total eslint: 1, total circular: 0
- SessionFilterButton: dropdown rows → DropdownItem, trigger → IconButton - FileHeaderMoreMenu: 8 menu rows → DropdownItem, remove MenuItemContent helper - McpTable: bulk action bar → Button size=small variant=tertiary - ProjectForm / ReviewerConfigSection: <select>/<textarea>/<input> → DS Select/Textarea/Input - KiroSessionSetup: 5 icon-only buttons → IconButton / Button ghost - BottomPanelHeader: delete 127-line commented dead code block - DB connection fields: <input> → DS Input, extract AddConnectionFormField wrapper - Extract shared DateRangePills component from duplicate filter sidebars Pre-commit hook ran. Total eslint: 1, total circular: 0
- status.rs: add STATUS_CACHE (2s TTL, keyed on HEAD SHA) to skip redundant git-status subprocess spawns on rapid frontend polls - numstat.rs: add NUMSTAT_CACHE (500ms TTL) to skip repeated libgit2 diff walks when the sidebar re-renders without any working-tree change - watcher.rs: convert polling thread from std::thread::spawn to tokio::spawn + tokio::time::sleep so the poller lives on the async executor instead of blocking a dedicated OS thread - useRepoStatusListener / useWorkspaceGitStatus / useFileTree: add document.hidden guard before processing repo:status_updated events so background tabs do not trigger redundant React state updates Pre-commit hook ran. Total eslint: 1, total circular: 0
- Memoize provider values in Files/Terminal/Browser workstation contexts so consumers don't re-render on every Provider render. - Extract static inline style to module const (VIRTUALIZED_BODY_STYLE) and memoize dynamic container style in ChatHistory. - Stabilize WorkstationTabBar leadingSlot as a module-level element. - Wrap ChatBubble primitives (Avatar/Header/Body/Layout) and AgentChatItemDefault with React.memo. - Optimize VirtualizedSessionList: memoized SessionRow, stable itemContent via Virtuoso (index, data) signature, memoized style and formatted timestamp. - Stabilize VirtualizedStickyTree itemContent / computeItemKey with useCallback so Virtuoso's internal memo can hit. - Memoize FileRow in ChangedFilesList. - Adopt React 19 useDeferredValue on filteredFiles in SourceControlContent so the filter input stays responsive while the virtualized tree recomputes at lower priority. Pre-commit hook ran. Total eslint: 1, total circular: 0
- Fix off-by-one in terminal pill label: call `.trimEnd()` before
`.split("\n")` so trailing newlines don't inflate lineCount
(useAtMention, useAddToAgentInsertion, useComposerInput)
- Rename `checkout` → `checkoutRaw` in branchOps / GitOperationsService
to make the unguarded path explicit and prevent accidental UI use
- Two-step create-then-checkout in gitCreateBranchFromCommit: pass
`checkout: false` to backend create, then route through
`checkoutWithDialog` so dirty-tree conflicts surface the dialog
- Narrow TextSelectionDropdown primary width 280→180px, submenu 280→240px
Pre-commit hook ran. Total eslint: 1, total circular: 0
- Add lineStart/lineEnd to TerminalSelectionInfo, SelectionState, AddToAgentRequest, and __orgiiLastTerminalCopy for end-to-end flow - terminalHandlers: call getSelectionPosition() and convert 0-based xterm rows to 1-based for display (lineStart/lineEnd) - TerminalCore: thread lineStart/lineEnd through selection state, copy handler, and handleAddToChat; add to useEffect dep array; clear on selection hide to prevent stale position leakage into copy handler - useAddToAgentInsertion: use real buffer positions for pill label when available, fall back to counting text lines for legacy callers - pasteHandlers: prefer real lineStart/lineEnd from __orgiiLastTerminalCopy when available, fall back to lineCount range for legacy paste paths Pre-commit hook ran. Total eslint: 1, total circular: 0
Resolve conflicts for PR #254. Pre-commit hook ran. Total eslint: 1, total circular: 0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
contenteditabledrop behaviour (inserting the filename as raw text) was not suppressed — allowing browser-injected text to corrupt the editor and, in certain timing windows, produce an empty conversation round.ComposerInput/index.tsx:handleDropEventnow unconditionally callsevent.preventDefault()before invokingcreateDropHandler. OS file drops are already handled byGlobalDragDrop→droppedFilesAtom→useDroppedFilesConsumer; the browser must not also insert raw text.useContainerDrag.ts:handleContainerDropnow callsevent.preventDefault()+event.stopPropagation()for all drops on the composer container, stopping the event before it can reach the innerInputEditordiv or the contenteditable host. Internal/reference drops are still forwarded tohandleDropas before.Closes #250
Root Cause
createDropHandler(inpasteHandlers.ts) only handles reference pill drags and returnsfalsefor everything else — without callingevent.preventDefault(). The event propagated to thecontenteditablehost where the browser inserted the file name as text. This corrupted the editor state and, when combined with timing arounduseDroppedFilesConsumerupdatinghasContentRef, could produce an empty conversation round.Changes
src/components/ComposerInput/index.tsxevent.preventDefault()inhandleDropEventsrc/engines/ChatPanel/InputArea/hooks/useContainerDrag.tspreventDefault+stopPropagationinhandleContainerDrop; useisInternalDropTypehelpersrc/engines/ChatPanel/InputArea/hooks/containerDropHelpers.tsisInternalDropTypepure helpersrc/components/ComposerInput/__tests__/dropHandler.test.tscreateDropHandlerOS-file-drop behavioursrc/engines/ChatPanel/InputArea/hooks/__tests__/containerDropHelpers.test.tsisInternalDropTypesrc/engines/ChatPanel/InputArea/hooks/__tests__/TEST_CASES.mdTest Plan
pnpm testpasses (18 new tests, 0 failures).tsfile from Finder onto the chat input → only a pill is inserted, no new empty conversation round appearshandleDrop)