diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-16 16:36:00 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-16 16:36:00 +0200 |
| commit | 2e4fa11808ccfd94a5694535b37388e884ed17d0 (patch) | |
| tree | 888cceda9b07362fdd18059b534b7517c9f17300 /docs/superpowers/specs/2026-10-16-file-ops-design.md | |
| parent | 12d6830424d24f5a2ed8056b53a12cc4b978ece2 (diff) | |
| download | quickshell-2e4fa11808ccfd94a5694535b37388e884ed17d0.tar.gz quickshell-2e4fa11808ccfd94a5694535b37388e884ed17d0.zip | |
docs: spec the Do row of file operations
Local conversions, extraction, merge and compression through a small
fileops CLI, results in Documents/Assistant, and To .md through the
daemon's new write command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/specs/2026-10-16-file-ops-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-10-16-file-ops-design.md | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-16-file-ops-design.md b/docs/superpowers/specs/2026-10-16-file-ops-design.md new file mode 100644 index 0000000..9f0d7c0 --- /dev/null +++ b/docs/superpowers/specs/2026-10-16-file-ops-design.md @@ -0,0 +1,86 @@ +# File operations (the Do row): design + +Date: 2026-10-16. Status: approved in brainstorming, not implemented. + +## Goal + +Piece 3 of the file work (piece 1 `2026-10-16-file-drop-design.md`, piece 2 +`2026-10-16-ask-presets-design.md`). The file state gains a **Do** row of +local operations on the staged files: convert, extract, merge, compress. +They run without the model, except To .md, which asks it to rewrite a PDF +as Markdown and writes the reply to a file (desktop-assistant, +`docs/superpowers/specs/2026-10-16-write-design.md`). + +Results go to one folder, `$(xdg-user-dir DOCUMENTS)/Assistant`, created +on first use. A taken name becomes `name (1).ext`, `name (2).ext`; nothing +is overwritten. + +## Operations + +| kind | label | op | does | +| --- | --- | --- | --- | +| pdf | To .md | (daemon `write`) | the model rewrites it as Markdown into `name.md` | +| pdf | To images | `pdf-images` | `pdftoppm -r 150 -png` into a folder `name (pages)` | +| pdf, 2+ | Merge | `pdf-merge` | `qpdf --empty --pages ... -- merged.pdf`, in staging order | +| video | Extract audio | `audio` | AAC copied into `.m4a` (`ffprobe` tells), anything else to `.mp3` at `-q:a 2` | +| video | To MP4 | `mp4` | libx264 + AAC | +| video | To WebM | `webm` | libvpx-vp9 + Opus | +| video | To GIF | `gif` | 12 fps, 480 px wide, palettegen + paletteuse | +| video | Trackcrop | `trackcrop` | `trackcrop in out` from PATH; it opens its own object picker window and the op waits for it | +| image | To PNG, To JPEG, To WebP | `png`, `jpeg`, `webp` | `magick in out`; the image's own format is not offered | +| image | Resize 50% | `half` | `magick in -resize 50% out` | +| archive | Extract | `extract` | `bsdtar -xf` into a folder named after the archive | +| any | Compress… | `zip`, `tgz`, `txz`, `tbz2` | one archive of everything staged; the button opens a row of the four formats | + +Matching follows the presets: an operation is offered when every staged +file is of one of its kinds. Merge needs two or more files, all PDF. +Compress fits any mix. Several staged files of one kind run the operation +on each (four images, To PNG, four PNGs), except Merge and Compress, which +make one result. + +## fileops + +`desktop/assistant/fileops`, stdlib Python on `/usr/bin/python3`: +`fileops <op> <outdir> <file...>`, printing one JSON line, +`{"ok": true, "out": [paths]}` or `{"ok": false, "error": "..."}`, exit +status 0 or 1. + +- Each result is written to a temporary name in the output folder and + renamed into place on success, so a failure or a stop leaves nothing + half written. +- Tools run in their own process group; SIGTERM (the canvas's Stop) kills + the group, then removes the temporary file, so no `ffmpeg` keeps running. +- A missing tool is an error naming it, as is trackcrop not on PATH: + "trackcrop not found: link it into ~/bin". +- A tool's failure reports its last stderr line. + +## Card + +- The Do row sits under the Ask row in the file state. To .md sends the + daemon's `write`; every other operation starts `fileops` with `Process`. +- One operation at a time. While it runs, a status line says "Working on + To GIF…" (for To .md, "To .md… 3.4k characters", from the daemon's + `progress`), and a Stop button replaces the rows. Stop stops the + `fileops` process, or sends `stop` to the daemon. +- The result line: "Saved report.md · Open · Folder" (`xdg-open` on the + result, or the output folder); several results say "Saved 4 files". + An error shows in red. The card stays in the file state with the chips, + so another operation can follow. +- To .md while a chat reply streams stops that reply first: llama-server + has one slot. + +`desktop/assistant/Ops.js` holds the list (`{label, op, kinds}`), the +matching, and the format row of Compress…; `test_ops.js` checks it. + +## Testing + +- `test_ops.js`: matching for one PDF, two PDFs (Merge), a PDF and an + image (Compress only), one PNG (no To PNG), a video, an archive. +- `test_fileops.py`: builds a PDF, a PNG, a one-second ffmpeg `testsrc` + clip with a sine tone, and a tarball in a temp folder; runs every + operation except trackcrop into a temp output folder and checks each + result's mime type with `file`; checks a taken name gets ` (1)`; checks + a failing input leaves no file behind; checks SIGTERM during a slow + ffmpeg leaves neither the process nor a partial file. +- By hand: each row on a real file, Stop on a long video conversion, + trackcrop's window opening, To .md on a scanned PDF. |
