The audit split the menu's omissions in two. Three were deliberate — OCR,
Transcribe and Extract Audio were removed in July because the task system does
them better, and those stay gone. The rest were nobody's decision. This is the
rest.
── One menu body instead of two ──
`DropdownMenuItems` and `ContextMenuItems` were character-identical apart from the
component prefix: 159 lines duplicated, including the same comment twice. Radix
gives both families the same props, so the components are now a parameter and
there is one list. That is why this diff removes more than it adds while adding
seven items — and why the next item only has to be written once.
── Added to the row menu ──
- Open — double-click was the only way. Every file manager puts it first.
- Open in editor — `/code-editor` is on the SAME `files` permission as `/files`,
so anyone who can browse could already edit; there was just no way to get there
from a file. Narrower than Read Aloud on purpose: that one uses the `text`
FALLBACK type, which matches .exe and .bin.
- Paste into folder — a move was only expressible as cut → navigate → paste.
Right-clicking the destination is the obvious gesture and did not exist.
- Duplicate — one call away the whole time; `/copy` already resolves collisions
to " (copy 2)".
- Compress — the missing half of Extract. `/download` shelled out to zip but
streamed the result away, so nothing could make an archive and keep it. New
`POST /file-browser/compress` writes it beside the original.
- Pin / Unpin — pinning existed only in the widget, which is not where you meet
a file worth keeping to hand.
── Added to the background menu ──
- Clone repository and Show/Hide hidden files. Both were toolbar-only, and the
toolbar hides them under `md:` — so on a phone neither existed. All four
desktop-only toolbar actions are now in this menu, which long-press reaches.
- Paste is gated on the clipboard, as both toolbars already were. Ungated it fell
through to the OS clipboard path and either toasted a secure-context error or
silently did nothing — an item that looked available and was not.
── Removed ──
`pcm-worklet-processor.js`, orphaned when cliamp left for the music plugin this
morning. Verified unreferenced; the plugin ships its own copy inline.
The `Play` hole itself is NOT closed and cannot be from here: there is no way for
a plugin to register a context-menu item. Tasks and agents are the only extension
mechanism and a task can only contribute a task entry. That is a platform gap and
wants its own decision.
Verified the zip invocation by hand for all three shapes — single file, directory
(recursive, contents included), and a multi-item selection. Archive naming strips
the extension only for a file, so a directory called `my.photos` stays
`my.photos.zip` rather than becoming `my.zip`.
tsgo clean, frontend builds, 808 pass / 7 fail unchanged.
SelectionActions.tsx and usePipelineRunner.ts are prettier reflowing long lines —
no semantic change, caught by formatting the folder.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>