Repository navigation
feat(messages): let attachment renderers open files from the host file card - #2239
Conversation
|
🤖 We'd like to build on this. Block's Markdown reader plugin (squareup/agents#8965) would claim Two notes:
I'm testing this branch with md-reader in the desktop app and will post results here. |
|
🤖 Follow-up for note 1: #2284 gives |
|
🤖 I tested this end to end with md-reader. I used the buzz-plugin-demos web shell (seeded fictional community), with this PR merged with #2284.
Not yet tried in the native desktop app with a real |
|
🤖 @joahg thanks for testing this with md-reader! This is ready for review. I can't request reviewers on this repo from a fork, so could you add yourself, or whoever should review it?
The PDF viewer that uses this is https://github.com/squareup/agents/pull/8960. |
joahg
left a comment
There was a problem hiding this comment.
🤖 Reviewed at fc7eb11 and tested it end to end with md-reader (squareup/agents#9003) plus #2284 in the demo shell. Clicking the card opens the reader with its message context, and Download still works. Approving. None of the following blocks merging:
- Files now reach
registerAttachmentmatchers. A plugin whose matcher ignoresattachment.kind(for example, a test on the name alone) will now claim file cards too. If its component renders without throwing, the host card disappears. The bundled matchers checkkind, so nothing breaks today. A sentence indocs/plugin-architecture.mdtelling matchers to checkattachment.kindwould cover it. - The failed-download text sits inside the Preview button (
FileAttachment.tsx). The card's meta text reads "Couldn't download · Retry", but clicking that text opens the preview. Retry lives on the trailing icon. Dropping "· Retry" on this path would avoid the confusion. onClick={onPreview}passes the click event to the callback.onClick={() => onPreview()}keeps a plugin that writesonPreview={open}from receiving aMouseEventas its target.
… links (#2284) ## Summary When several plugins claim the same attachment, a precedence rule now picks the one that draws it, instead of activation order. `registerAttachment` accepts the optional `order` that panels and link renderers already take: a number, or a function of the `Attachment`. Lower wins, and equal orders fall to the contribution key. This makes #2239 safe to build on with more than one file plugin installed. #2239 routes file cards through attachment renderers. Once it lands, a Markdown reader (squareup/agents#8965) and a future plugin that also handles `.md` (a summarizer, say) would both match the same card. Without `order`, whichever activated first wins, and disabling and re-enabling a plugin can flip the result. This PR applies the same bands as panels and links: `-10` specialises, `0` is the default, `100` is a catch-all. Tracks AGNTOPS-704. ## Changes - `src/plugins/contributions.ts`: `resolveMatch` and `MatchOrder` take the target type as a parameter (default `string`), so attachments can use the same resolver. Existing callers are unchanged. - `src/features/conversation/contracts.ts` (FOUNDATION; Joah approved this change): `AttachmentRenderer` gains `order?: MatchOrder<Attachment>`. - `src/features/conversation/service.tsx`: `registerAttachment` rejects an `order` that is neither a number nor a function, as `registerLink` does. - `src/features/conversation/AttachmentView.tsx`: picks the renderer with `resolveMatch` instead of the first match. - `docs/plugin-architecture.md`: the Resolution order section now covers attachment renderers. No bundled renderer sets `order`, so with one renderer per attachment nothing changes. ## Test plan - New `AttachmentView.test.tsx` covers four cases: - the lowest order wins, whatever the registry order; - equal orders fall to the contribution key; - an `order` function receives the attachment, and a throwing matcher is skipped; - with no match, the host card stays. - `service.test.tsx`: an invalid `order` is rejected, and a function `order` registers. - `vitest run src/features/conversation src/plugins src/features/messages src/bundled`: 7154 passed, 16 skipped. - `pnpm check` and the pre-push types-and-related-tests hook pass. ### Manual test steps 1. Run `just desktop` from this branch, with #2239 merged in so file cards use renderers. 2. Install two folder plugins that both match `.md` file attachments. One sets `order: -10` and draws a "Reader" label; the other uses the default and draws "Other". 3. Post a `.md` file. The card shows "Reader". 4. In Settings, disable both plugins, then enable the `order: -10` plugin last. The card still shows "Reader". 5. Remove `order` from the first plugin and reload it. The card shows whichever plugin's key sorts first, and toggling the plugins doesn't change it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… links (block#2284) ## Summary When several plugins claim the same attachment, a precedence rule now picks the one that draws it, instead of activation order. `registerAttachment` accepts the optional `order` that panels and link renderers already take: a number, or a function of the `Attachment`. Lower wins, and equal orders fall to the contribution key. This makes block#2239 safe to build on with more than one file plugin installed. block#2239 routes file cards through attachment renderers. Once it lands, a Markdown reader (squareup/agents#8965) and a future plugin that also handles `.md` (a summarizer, say) would both match the same card. Without `order`, whichever activated first wins, and disabling and re-enabling a plugin can flip the result. This PR applies the same bands as panels and links: `-10` specialises, `0` is the default, `100` is a catch-all. Tracks AGNTOPS-704. ## Changes - `src/plugins/contributions.ts`: `resolveMatch` and `MatchOrder` take the target type as a parameter (default `string`), so attachments can use the same resolver. Existing callers are unchanged. - `src/features/conversation/contracts.ts` (FOUNDATION; Joah approved this change): `AttachmentRenderer` gains `order?: MatchOrder<Attachment>`. - `src/features/conversation/service.tsx`: `registerAttachment` rejects an `order` that is neither a number nor a function, as `registerLink` does. - `src/features/conversation/AttachmentView.tsx`: picks the renderer with `resolveMatch` instead of the first match. - `docs/plugin-architecture.md`: the Resolution order section now covers attachment renderers. No bundled renderer sets `order`, so with one renderer per attachment nothing changes. ## Test plan - New `AttachmentView.test.tsx` covers four cases: - the lowest order wins, whatever the registry order; - equal orders fall to the contribution key; - an `order` function receives the attachment, and a throwing matcher is skipped; - with no match, the host card stays. - `service.test.tsx`: an invalid `order` is rejected, and a function `order` registers. - `vitest run src/features/conversation src/plugins src/features/messages src/bundled`: 7154 passed, 16 skipped. - `pnpm check` and the pre-push types-and-related-tests hook pass. ### Manual test steps 1. Run `just desktop` from this branch, with block#2239 merged in so file cards use renderers. 2. Install two folder plugins that both match `.md` file attachments. One sets `order: -10` and draws a "Reader" label; the other uses the default and draws "Other". 3. Post a `.md` file. The card shows "Reader". 4. In Settings, disable both plugins, then enable the `order: -10` plugin last. The card still shows "Reader". 5. Remove `order` from the first plugin and reload it. The card shows whichever plugin's key sorts first, and toggling the plugins doesn't change it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
🤖 @ericm-square #2284 has landed, and this branch now conflicts with main in |
…e card File attachments in message rows now go through attachment renderers. Renderers receive placement and the host FileCard, so a plugin can make the card open a preview while the host keeps native download. Co-Authored-By: Helen Tian <htian@squareup.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fc7eb11 to
ac20f7d
Compare
|
@joahg Can you run the CI checks? |
Summary
Lets plugins open file attachments the way #1469 opens Markdown, without bundling a previewer into Buzz.
File attachments in message rows now go through attachment renderers, like images and audio already do. For files, a renderer also receives
FileCard: the host's own file card. Rendering it withonPreviewmakes clicking the card open the preview, while a separate trailing button keeps the host's native or browser download. Plugins can't reach the native download themselves, so this keeps Download working for them on desktop.Renderers also receive
placement("message"or"composer"), so a plugin can offer the preview only on sent messages and leave the composer's attachment review alone. In message rows they also receivemessage, the message that holds the attachment, so a preview can name who shared it and where.The first consumer is an external PDF viewer plugin: clicking a PDF card opens it in the conversation side pane.
Changes
contracts.ts(FOUNDATION): adds optionalplacement,messageandFileCardtoAttachmentRendererProps, plusFileCardProps.MessageRow.tsx: file attachments go throughAttachmentViewwithplacement="message",messageandFileCard. Audio and image renderers also getplacement.ComposerAttachmentViewer.tsx,ComposerAttachments.tsx: passplacement="composer". The draft viewer also passesFileCard.FileAttachment.tsx,Messages.module.css: the click-to-preview card with a separate Download action, taken from [buzz-app] Preview Markdown and common text attachments #1469 without its summary variant.docs/plugin-architecture.md: documentsregisterAttachment,placementandFileCard.With no plugin claiming a file, message rows render exactly as before.
Relationship to #1469
This takes only the host plumbing from #1469 (the renderer routing,
placement, and the preview card) and leaves out the bundled File Preview plugin. If #1469 lands later, its File Preview can renderFileCardthe same way, and the overlapping pieces should rebase cleanly onto this. Co-authored with htian-dev, whose code this is.Test plan
pnpm checkpasses.vitest run src/features/messages src/features/conversation src/bundled: 6941 passed.FileAttachment.test.tsx: preview and native download are separate keyboard actions.MessageRow.test.tsx: a plugin rendersFileCardin message placement and receives the message; clicking it opens the plugin's target; Download keeps the media address; an unclaimed file keeps the plain download card.Manual test steps
just desktopfrom this branch and sign in..zip. Both show the usual file card, and clicking either downloads it.FileCardwithonPreviewopening a side-pane target (Settings → Plugins → Manage plugins). I tested with a PDF viewer plugin built this way..zipcard still downloads on click.🤖 Generated with Claude Code