Attach images to a task, staged under .loopboard/cache/ - #52
Conversation
…che/ Drag-drop, clipboard paste, and a file-picker icon on every editable-phase card all stage an image under .loopboard/cache/<id>/ via Store.stageAttachment (images only, configurable size cap via loopBoard.maxAttachmentSizeMB), then append a plain markdown link to the task's Description — no task-file grammar change. Extended the description-link renderer to treat a .loopboard/cache/ relative path as clickable, and openLink to open it via vscode.open. Staged files are ephemeral (.loopboard/ is gitignored) and are cleaned up when the task is accepted to DONE.md (also opportunistically on task deletion).
|
📦 Build succeeded — installable Download from the run's Artifacts, unzip, then install: |
Attach (file-picker + drag-drop + paste) was only wired on renderCard, so a draft created via the New Story composer had no way to attach an image until it was groomed into a full story. Wire the same affordance onto renderDraft cards, keyed on the id the draft already carries.
|
📦 Build succeeded — installable Download from the run's Artifacts, unzip, then install: |
…s too
Removes the file-picker attach button per user feedback ("I can not attach
images to new stories" / no visible buttons wanted). Drag-drop and paste now
work on the New Story composer (auto-saves the draft first, since there's no
task id yet), and on an open Description or answer field specifically (the
link folds into that field's own value and saves through the normal
field-patch path instead of always landing in Description).
|
📦 Build succeeded — installable Download from the run's Artifacts, unzip, then install: |
Pasting right after clicking "New Story" landed on whatever was previously focused (or nothing), so the composer's paste listener never saw the event. Focus the textarea on open, same pattern as the search-box focus-restore.
|
📦 Build succeeded — installable Download from the run's Artifacts, unzip, then install: |
|
📦 Build succeeded — installable Download from the run's Artifacts, unzip, then install: |
Summary
Store.stageAttachment(taskId, filename, bytes, maxBytes?)(src/store.ts) validates an image-extension allowlist and a configurable size cap, sanitizes the filename, dedupes collisions, writes bytes under.loopboard/cache/<id>/, and appends a plain[name](.loopboard/cache/<id>/name)markdown link to the task's## Description— no task-file grammar change, so the parser/writer fixpoint suites are untouched.clearAttachments(taskId)deletes that task's cache dir, wired intoacceptToDone(the decided single cleanup trigger) anddeleteTask(opportunistic).src/controller.ts:pickAttachmentopens a host-sideshowOpenDialog(no bytes cross the webview boundary);attachdecodes a base64 payload from drag-drop/paste (the only path for webview-originated bytes).openLinknow also opens a.loopboard/-relative attachment link viavscode.open, alongside the existing http/httpsopenExternalpath.media/board.js: extended the description markdown-link renderer to treat a.loopboard/cache/relative path as clickable (previously http/https only), so the appended link actually renders.loopBoard.maxAttachmentSizeMB(default 10, min 1) makes the size cap configurable, added mid-review per request.readme.md: documentedcache/<id>/in the storage layout and that staged attachments are ephemeral/gitignored, cleaned up on acceptance.id(assigned on serialize), so the same file-picker/drag-drop/paste wiring used on regular cards is now also wired onrenderDraft.Implements t-att1.
Test plan
make check(build + 102 unit tests + package) greenloopBoard.maxAttachmentSizeMBactually caps upload sizeNote
One acceptance-criterion detail — "verified by a unit test on the store cleanup path" — is not met literally:
store.tsis a VSCode-touching module with no unit-test coverage anywhere in this codebase (per CLAUDE.md, manual F5 verification only). Adding vscode-mocking test infra for just this path would be new test architecture, not a surgical change, soclearAttachmentsis verified manually like the rest ofstore.ts. Flagging for reviewer visibility rather than silently deviating.