Record where an upload was opened from in a new useUploadNavContext
state. When a tile is clicked on a user profile, the thread page passes
that context to the upload detail API, which scopes the newer/older
neighbors to that user's uploads. Arrow clicks reseed the context for
the next thread, so the chain stays within the source list without a
URL parameter. Threads opened directly (notification, reload) fall back
to the global order. The breadcrumb reads the same context and shows
the profile path for profile-originated threads, now including the
thread id in the final crumb.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
All internal links (tiles, prev/next nav, comments, notifications,
breadcrumb, dropzone route check) point to /t/<id>. A global route
middleware permanently redirects old /thread/<id> URLs including
query string and comment anchor.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Select mode in the filter row (upload.delete permission): tiles
toggle selection in the capture phase, a floating action bar
offers select-all and delete with confirmation popover
- New POST /api/uploads/bulk-delete endpoint (validated, deduped,
max 200 ids) removing rows, files and attachment data
- upload-deleted broadcast on bulk and single delete; other clients
refresh the grid and drop matching notifications
- Also: skeleton tile grid as loading state, dropzone logic moved
to the layout-level GlobalDropZone, obsolete ytimg preconnect
removed
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>