--- phase: 10-ux-interaction plan: 10 type: execute wave: 3 depends_on: [10-04, 10-09, 10-05, 10-06] files_modified: - frontend/src/components/layout/OsDragOverlay.vue - frontend/src/App.vue - frontend/src/views/FileManagerView.vue - frontend/src/components/layout/__tests__/OsDragOverlay.test.js autonomous: true requirements: [UX-09] must_haves: truths: - "Dragging files from the OS over the browser window shows the full-screen drop overlay" - "An in-app element drag (no Files type in dataTransfer.types) does NOT show the overlay" - "Releasing files over the window calls FileManagerView.handleOsDrop(files) which uploads them via the existing flow" - "The overlay disappears on drop and on the depth counter reaching 0 via dragleave" - "Overlay z-index (z-[9998]) is below ToastContainer (z-[9999]) so toasts remain visible during drop" artifacts: - path: "frontend/src/components/layout/OsDragOverlay.vue" provides: "Full-screen OS file drag overlay with depth-counter pattern" - path: "frontend/src/App.vue" provides: "Updated to mount OsDragOverlay and wire @files-dropped to active route view" key_links: - from: "OsDragOverlay.vue" to: "window dragenter/dragleave/dragover/drop events" via: "mounted() addEventListener + beforeUnmount() removeEventListener" pattern: "window\\.addEventListener\\('drag" - from: "App.vue" to: "FileManagerView.handleOsDrop" via: "@files-dropped handler -> routeViewRef.handleOsDrop" pattern: "routeViewRef\\.value\\?\\.handleOsDrop" --- Implement UX-09 (OS file drag onto browser window -> full-screen overlay -> upload). The overlay uses the depth-counter pattern from D-16 (Pitfall 3) to prevent flicker as the cursor moves between child elements, and only activates when the dragged item has `dataTransfer.types.includes('Files')` (which is true only for OS-origin drags). Output: - New component `frontend/src/components/layout/OsDragOverlay.vue` (Options API; Teleport to body; window-level event listeners; emits `files-dropped`) - App.vue mounts `` and routes to the active view via `routeViewRef.value?.handleOsDrop?.(files)` - FileManagerView exposes `handleOsDrop(files)` via `defineExpose` that calls the existing `onFilesSelected({files, autoClassify: true})` - OsDragOverlay test stubs promoted to real tests @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @CLAUDE.md @.planning/phases/10-ux-interaction/10-CONTEXT.md @.planning/phases/10-ux-interaction/10-RESEARCH.md @.planning/phases/10-ux-interaction/10-PATTERNS.md @frontend/src/App.vue @frontend/src/views/FileManagerView.vue @frontend/src/components/documents/DocumentPreviewModal.vue @frontend/src/components/ui/AppIcon.vue @frontend/src/components/upload/DropZone.vue OsDragOverlay.vue (Options API) - from PATTERNS.md §"OsDragOverlay.vue": Data: - `dragDepth: 0` (counter) - `showOverlay: false` Methods: - `onDragEnter(e)` -> ignore unless `e.dataTransfer?.types.includes('Files')`; increment dragDepth; set showOverlay=true - `onDragLeave()` -> dragDepth = max(0, dragDepth - 1); if dragDepth === 0 set showOverlay=false - `onDragOver(e)` -> e.preventDefault() (required to allow drop event) - `onDrop(e)` -> e.preventDefault(); reset dragDepth=0, showOverlay=false; emit `files-dropped` with `Array.from(e.dataTransfer.files)` (when files.length > 0) mounted(): addEventListener for dragenter/dragleave/dragover/drop on `window`. beforeUnmount(): remove all four listeners. Template (Teleport to body): ```

Drop files to upload

``` Plus a `