Skip to content

phase 3b T9/T10/T12: drag overlay + composer paste + attach button#639

Merged
intendednull merged 3 commits into
mainfrom
ui/phase-3b-drag-paste
May 9, 2026
Merged

phase 3b T9/T10/T12: drag overlay + composer paste + attach button#639
intendednull merged 3 commits into
mainfrom
ui/phase-3b-drag-paste

Conversation

@intendednull
Copy link
Copy Markdown
Owner

Summary

Closes the deferred T9 / T10 / T12 from docs/plans/2026-05-08-ui-phase-3b-files-inline.md — files-inline phase 3b is now feature-complete except for T6 (voice notes), which depends on the unshipped audio waveform pipeline.

  • T9Composer's + IconBtn flips UploadQueue::open directly. Standalone <FileShareButton> paperclip retired (mounts removed from app.rs + mobile_shell.rs; component marked #[deprecated]).
  • T10 — new <DragOverlay> mounts at the app shell, installs page-level drag/drop listeners, and renders the spec's tinted overlay with drop to attach. Drop enqueues files and opens <UploadDialog>.
  • T12 — composer textarea grows an on:paste handler that routes ClipboardEvent files into the upload queue (and away from the textarea). Pasted images get pasted-{YYYY-MM-DD-HH-mm-ss}.png.

Refactor

enqueue_file_list in upload_dialog.rs is the single FileList → upload-queue pump used by all three call paths (picker, drop, paste). MAX_ATTACHMENT_SIZE moves to upload_state.rs. spawn_upload_for_file is pub(crate).

Tests

  • Browser tier: phase_3b_drag_overlay (overlay mount/unmount + spec copy + decorative aria-hidden) and phase_3b_paste_filename (locks in the pasted-YYYY-MM-DD-HH-mm-ss.png format).
  • Playwright: e2e/files-inline.spec.ts constructs a real DataTransfer, fires dragenter + drop on document, asserts the overlay surfaces then the dialog opens with the dropped file in the list.

Spec

docs/specs/2026-04-19-ui-design/files-inline.md ticks: drag-and-drop, paste-to-upload, ARIA labels. Voice-note row stays open pending T6.

Test plan

  • just check — fmt + clippy + native + WASM, zero warnings
  • just test-browser — 383 tests passing (incl. 2 new)
  • CI Playwright on the new e2e spec (real DragEvent + DataTransfer)
  • Browser smoke: drag a file from Files into the window → overlay appears → drop → dialog opens with file row → click attach to message → file lands in chat
  • Browser smoke: focus composer → screenshot to clipboard → paste → dialog opens with pasted-{ts}.png row

🤖 Generated with Claude Code

intendednull and others added 3 commits May 8, 2026 23:54
Closes T9 / T10 / T12 from `docs/plans/2026-05-08-ui-phase-3b-files-inline.md`:

- **T9.** Composer's `+` IconBtn (`composer__attach`) flips
  `UploadQueue::open` directly; standalone `<FileShareButton>`
  paperclip retired (mounts removed from `app.rs` + `mobile_shell.rs`,
  component marked `#[deprecated]` so out-of-tree callers still build
  while migrating).
- **T10.** New `<DragOverlay>` mounts at the app shell, installs
  `dragenter` / `dragover` / `dragleave` / `drop` listeners on the
  document, and renders a tinted full-viewport overlay with the spec
  copy `drop to attach` while `queue.drag_active` is true. Drop
  enqueues files into the shared `UploadQueue` and flips
  `queue.open`. Depth-counter pattern collapses the
  enter/leave-during-drag noise so the overlay only hides when the
  cursor truly leaves the window.
- **T12.** Composer textarea grows an `on:paste` handler. When the
  ClipboardEvent carries files (screenshots, copied attachments),
  the textarea call is preventDefault'd and files route to the same
  upload queue. Pasted images that arrive without a meaningful
  filename get `pasted-{YYYY-MM-DD-HH-mm-ss}.png` per spec.

Refactor: `enqueue_file_list` helper in `upload_dialog.rs` is the
single FileList → upload-queue pump used by all three call paths
(picker, drop, paste). `MAX_ATTACHMENT_SIZE` moves to
`upload_state.rs` so the pump enforces the 25 MB cap consistently.
`spawn_upload_for_file` becomes `pub(crate)` (was already named for
this — just promoted visibility for the new callers).

Spec: docs/specs/2026-04-19-ui-design/files-inline.md
§Drag-and-drop, §Paste-to-upload, §Upload dialog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… spec

- Browser tier (`crates/web/tests/browser.rs`):
  - `phase_3b_drag_overlay::overlay_visibility_tracks_drag_active_signal`
    pins overlay mount/unmount, the spec `drop to attach` copy, and
    the decorative `aria-hidden="true"` per spec accessibility table.
  - `phase_3b_paste_filename::filename_matches_spec_pattern` locks
    in the `pasted-YYYY-MM-DD-HH-mm-ss.png` format from
    `pasted_image_filename()`.
- Playwright (`e2e/files-inline.spec.ts`): real DataTransfer drop
  through the page-level handler — overlay surfaces on dragenter,
  drop closes the overlay and opens the upload dialog with the
  dropped file in the list. Lives at the e2e tier per CLAUDE.md
  "real-browser-only" rule (synthetic DragEvent in headless wasm-pack
  can't carry actual files).
- Spec (`docs/specs/2026-04-19-ui-design/files-inline.md`): tick the
  drag-and-drop, paste-to-upload, and ARIA acceptance criteria — all
  three close out with this PR. Voice-note row stays open pending T6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…leave

Review feedback on PR #639:

- Move `<UploadDialog>` from inside the desktop chat view to the app
  shell, alongside `<DragOverlay>` and `<AddFriendDialog>`. The
  previous mount sat inside `.shell-desktop` (which CSS sets to
  `display:none` on mobile), so the composer's `+` attach button
  flipped `queue.open` to true on mobile but the dialog never
  rendered. Single mount now serves both shells; CSS positions the
  dialog as a fixed overlay regardless of parent.
- `dragleave` whose `relatedTarget` is null indicates the cursor
  truly left the window. Firefox occasionally drops the final
  dragleave during nested drags, leaving the depth counter > 0 and
  the overlay stuck open. Treating null-related dragleaves as a hard
  reset (`depth = 0`) recovers without asking the user to refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@intendednull intendednull marked this pull request as ready for review May 9, 2026 07:23
@intendednull intendednull merged commit 1551730 into main May 9, 2026
8 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.

1 participant