From 0d5fa13d65fa982f3ba17d71c686dae5d4904032 Mon Sep 17 00:00:00 2001 From: timmy Date: Sun, 16 Aug 2026 07:28:10 +0000 Subject: [PATCH] feat: edit photos in conversation replies (Closes #947) --- README.md | 2 +- frontend/dashboard.css | 2 +- frontend/dashboard.js | 39 +++++++++++++++++++++++++++++ frontend/index.html | 9 ++++--- frontend/issue-attachment.js | 3 ++- frontend/issue-evidence-editor.js | 1 + tests/test_issue_evidence_editor.py | 32 +++++++++++++++++++++++ 7 files changed, 82 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6a2bbf4..14ea875 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ threads, create and self-assign issues, discover, claim, and release issue assig list repository labels and open milestones, set or clear due dates on assigned issues, create issue comments, close assigned issues, inspect/comment on assigned pull requests, merge assigned pull requests, and submit pull-request reviews. -Assigned-issue, assigned-pull-request, and unread-update conversation composers accept an ordered bundle of up to five PNG, JPEG, or WebP photos. Repeated camera captures append to the bundle, the gallery picker accepts multiple images, and each composer automatically optimizes oversized screenshots on-device to fit the 2 MB upload boundary. +Assigned-issue, assigned-pull-request, and unread-update conversation composers accept an ordered bundle of up to five PNG, JPEG, or WebP photos. Repeated camera captures append to the bundle, the gallery picker accepts multiple images, and each composer automatically optimizes oversized screenshots on-device to fit the 2 MB upload boundary. Before sending, the selected conversation photo can use the same touch editor as New issue evidence to crop, privacy-redact, highlight, or add an arrow; Apply replaces only that flattened derivative while preserving its caption and bundle position, and Cancel leaves the original unchanged. For online delivery, every photo uploads before the comment is posted to the exact conversation target, producing one ordered Markdown comment or reply; validation or upload failures keep the typed text and removable preview available for retry. Offline photo conversations admit every image Blob to IndexedDB before confirmation, keep only bounded metadata in localStorage, and checkpoint each upload separately so reconnect resumes at the first unconfirmed photo without duplicating an upload, comment, reply, or reply-and-read transition. The mobile **New issue** capture-first stage accepts an ordered evidence bundle of up to five PNG, JPEG, or WebP screenshots before a repository is chosen, optimizing each image independently to the 2 MB boundary. **Save to Drafts** durably writes every optimized Blob to IndexedDB before diff --git a/frontend/dashboard.css b/frontend/dashboard.css index ae641f2..7cf33a7 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -704,7 +704,7 @@ textarea { resize: vertical; min-height: 120px; } .conversation-evidence-review { grid-template-columns:1fr; width:100%; min-width:0; box-sizing:border-box; overflow-x:hidden; } .conversation-evidence-review > img { grid-row:auto; width:100%; height:auto; max-height:280px; } .conversation-evidence-review .issue-evidence-tray { overflow-x:auto; } -.conversation-evidence-review .issue-evidence-review-actions { grid-template-columns:repeat(3,minmax(0,1fr)); } +.conversation-evidence-review .issue-evidence-review-actions { grid-template-columns:repeat(4,minmax(0,1fr)); } .conversation-evidence-review .issue-evidence-review-actions button { min-width:44px; min-height:44px; width:100%; } @media (max-width:390px) { .conversation-evidence-review .issue-evidence-review-actions { grid-template-columns:1fr; } diff --git a/frontend/dashboard.js b/frontend/dashboard.js index 6cc6442..0b256d3 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -508,6 +508,19 @@ note: qs('#issue-attachment-note'), noteLabel: qs('#issue-attachment-note-label'), status: qs('#issue-comment-status'), + editor: { + document, + edit: qs('#edit-issue-attachment'), + dialog: qs('#issue-evidence-editor'), + canvas: qs('#issue-evidence-editor-canvas'), + exportCanvas: qs('#issue-evidence-editor-export'), + crop: qs('#crop-issue-evidence'), redact: qs('#redact-issue-evidence'), + highlight: qs('#highlight-issue-evidence'), arrow: qs('#arrow-issue-evidence'), + undo: qs('#undo-issue-evidence-edit'), reset: qs('#reset-issue-evidence-edit'), + cancel: qs('#cancel-issue-evidence-edit'), apply: qs('#apply-issue-evidence-edit'), + status: qs('#issue-evidence-editor-status'), + appliedMessage: 'Edited photo flattened and ready to send.', + }, createObjectURL: file => URL.createObjectURL(file), revokeObjectURL: url => URL.revokeObjectURL(url), readDataUrl: file => new Promise((resolve, reject) => { @@ -545,6 +558,19 @@ note: qs('#pull-attachment-note'), noteLabel: qs('#pull-attachment-note-label'), status: qs('#pull-comment-status'), + editor: { + document, + edit: qs('#edit-pull-attachment'), + dialog: qs('#issue-evidence-editor'), + canvas: qs('#issue-evidence-editor-canvas'), + exportCanvas: qs('#issue-evidence-editor-export'), + crop: qs('#crop-issue-evidence'), redact: qs('#redact-issue-evidence'), + highlight: qs('#highlight-issue-evidence'), arrow: qs('#arrow-issue-evidence'), + undo: qs('#undo-issue-evidence-edit'), reset: qs('#reset-issue-evidence-edit'), + cancel: qs('#cancel-issue-evidence-edit'), apply: qs('#apply-issue-evidence-edit'), + status: qs('#issue-evidence-editor-status'), + appliedMessage: 'Edited photo flattened and ready to send.', + }, createObjectURL: file => URL.createObjectURL(file), revokeObjectURL: url => URL.revokeObjectURL(url), upload: payload => { @@ -575,6 +601,19 @@ status: qs('#update-reply-status'), readyMessage: 'Screenshot ready to send with this reply.', removedMessage: 'Screenshot removed. Your reply is unchanged.', + editor: { + document, + edit: qs('#edit-update-reply-attachment'), + dialog: qs('#issue-evidence-editor'), + canvas: qs('#issue-evidence-editor-canvas'), + exportCanvas: qs('#issue-evidence-editor-export'), + crop: qs('#crop-issue-evidence'), redact: qs('#redact-issue-evidence'), + highlight: qs('#highlight-issue-evidence'), arrow: qs('#arrow-issue-evidence'), + undo: qs('#undo-issue-evidence-edit'), reset: qs('#reset-issue-evidence-edit'), + cancel: qs('#cancel-issue-evidence-edit'), apply: qs('#apply-issue-evidence-edit'), + status: qs('#issue-evidence-editor-status'), + appliedMessage: 'Edited photo flattened and ready to send.', + }, createObjectURL: file => URL.createObjectURL(file), revokeObjectURL: url => URL.revokeObjectURL(url), upload: payload => fetchReviewJson( diff --git a/frontend/index.html b/frontend/index.html index 00d7d44..d59c808 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -677,6 +677,7 @@
+
@@ -954,10 +955,11 @@
-