Repository navigation
Add the talk-note screen with pictures and file attachments - #702
Merged
Merged
Conversation
A note taken during a conference talk (what was said, the slide photo, the handout) now lands in the desktop Inbox as one ordinary capture. It is called a talk note throughout, never a session note: Sessions is its own bounded context. - /note in Backlog.Mobile.UI has a title, a Markdown body that dictation appends to, the speaker as one @name, tags, and an attachment strip. The draft lives in a scoped TalkNoteDraft, so it survives tab switches and backgrounding. - IAttachmentPicker mirrors ISpeechTranscriber. Android uses MediaPicker and FilePicker (CAMERA and the IMAGE_CAPTURE query are in the manifest). The harness uses hidden <InputFile multiple> elements. - A file over the 25 MB cap or off the ADR 0014 allowlist is refused on its tile with the reason, and the note is not sent until it is removed. The cap and the allowlist move to SyncAttachmentLimits in Sync.Abstractions, so the phone and the service share one list. - Pictures are downscaled with SkiaSharp before they queue: 1600 px longest edge, JPEG, EXIF reduced to the orientation. A per-note "Send originals" switch keeps them as taken. - A sent note is one outbox entry of kind talk-note. It uploads each file (PUT /api/sync/attachments/{id}) and checkpoints it through the new IStagedOutboxKind, so a retry never re-sends an uploaded id and each file gets its own attempt budget. It then posts the capture naming them. DeviceOutbox.Sending drives "waiting / uploading 2 of 3 / synced". - Pending talk notes show in the phone's Inbox list, marked waiting. - An AttachmentStrip library component with its storybook page. - The button reads "Send to Inbox" rather than "Save". interaction-guidelines.md#auto-save-no-save-buttons forbids a Save button as the means of persistence. - Devbook: a Talk note sub-feature in capture/features.md, a Talk Note Upload sequence in arc42/06, and a SkiaSharp entry in tech/mobile.md. Plan item talk-note-screen of plan mobile-inbox-slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The QA run wrote two scratch files at the repository root (a path that lost its separators), and they rode along in the previous commit. The fixture PDF and JPEG are byte-exact inputs, so they are marked binary: a Windows checkout's line-ending conversion would otherwise move the PDF's xref offsets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds the phone's talk note: a note taken during a conference talk, with the slide photo and any handout, that lands in the desktop Inbox as one ordinary capture. It is plan item
talk-note-screenof planmobile-inbox-slice, aftercapture-outbox(#652) andattachment-store-service(#668). It builds the phone half of local ADR 0014.It is called a talk note in the UI, the code and the devbook. It is never a "session note", because Sessions is a bounded context of its own.
Changes
/notescreen (Backlog.Mobile.UI): title, a Markdown body that dictation appends to, the speaker as one@name, tags, three attach buttons (Take photo / Choose pictures / Choose files), the attachment strip, a "Send originals" switch, and a "Last note: waiting / uploading 2 of 3 / synced" line.TalkNoteDraft, so it survives tab switches and backgrounding.talk-notes/drafts, thentalk-notes/outbox).IAttachmentPicker, mirroringISpeechTranscriber:AndroidAttachmentPickerusesMediaPicker.CapturePhotoAsync/PickPhotosAsyncandFilePicker.PickMultipleAsync. The manifest gainsCAMERAand theIMAGE_CAPTUREquery.WebAttachmentPickeruses hidden<InputFile multiple accept=…>elements plusattachments.js, for the harness.SyncAttachmentOptions(Api) toSyncAttachmentLimitsinSync.Abstractions. The options still default from them, so the service's behaviour is unchanged.PictureDownscaleruses SkiaSharp 4.152.1 (new dependency, MIT). Pictures go out at a 1600 px longest edge as JPEG q85.ExifOrientationwrites a minimal APP1 segment that keeps only the orientation tag, so location, camera and time are dropped.talk-note.TalkNoteOutboxKinduploads each file (PUT /api/sync/attachments/{id}withX-Attachment-Sha256), then posts the capture naming them.IStagedOutboxKindlets the kind checkpoint after each upload. An uploaded id is never re-sent, and each file gets the full attempt budget, because a checkpoint hands the attempts back.DeviceOutboxnow builds on the checkpointed entry after an attempt, and exposesSending.AttachmentStripis a new library component (Backlog.UI.Components/Inputs), with a storybook page (/attachment-strip), an index entry and aStorybookOrderTestsfolder route..devbook/domain/capture/features.md..devbook/arc42/06-runtime-view.md, and the Capture Attachments and outbox-kind lines brought level with it..devbook/tech/mobile.md..gitattributesmarkstests/**/Fixtures/**binary, so the fixture PDF and JPEG stay byte-exact.Changed on the way
interaction-guidelines.md#auto-save-no-save-buttonsforbids a Save button as the means of persistence, and the draft already keeps itself. It does exactly what the plan's "Save" describes.Reviewer notes
desktop-attachment-intake, a separate plan item that has not landed.Testing
Backlog.slnbuilds with 0 errors. The Android head (net10.0-android) builds clean.Backlog.Mobile.UI.UnitTests: generated title, draft surviving a route change, downscaler output for a fixture photo (1600×1200, EXIF reduced to orientation 6), a refused file naming its reason and never read, upload-before-capture order, per-attachment retry, a separate budget per upload, refusal by file name, "uploading 2 of 3", offline waiting and file release, originals vs downscaled, body/tags/speaker on the capture.Backlog.UI.Components.UnitTests:AttachmentStrip..exerefused on its tile, with Send disabledPUT /api/sync/attachments×2 (201) beforePOST /api/sync/inbox(201)@adasyncstopped the note is "waiting", then flushes whensyncis backsynclogged zero errors (the benign warnings were the ephemeral dev signing key andinvalid_tokenafter the deliberate restart). No browser console errors during the run.node tools/devbook/check-metadata.mjs: 0 blocking.Verification
devbook:verify-changeand the plan item's criteria were checked against the change set; 114/114 mobile UI tests pass on a clean build./notefields, scoped draftcapture/features.md#talk-note06-runtime-view.md#talk-note-upload,#capture-attachments,#mobile-capture-and-synctech/mobile.md#skiasharpThe Android picker and a draft surviving real backgrounding can only be checked on a device. That is the plan's
device-validationentry.Related
3ad44e90-39ef-491c-be0c-23d9ca224d07(plan itemtalk-note-screen, planmobile-inbox-slice). There is no GitHub issue..devbook/arc42/adr/0014-attachments-travel-through-a-blob-store-beside-the-replica.md), Keep phone captures in an offline outbox and make the Inbox list readable #652, Add the attachment store to the sync service and the AppHost (ADR 0014) #668.Checklist
build\stop-aspire-before-pr.ps1)🤖 Generated with Claude Code