feat(editing): opt-in message edit mode (load existing + update) - #119
Merged
Conversation
Add an opt-in `editing` prop to BlockKitchen so a host can load an
existing Slack message into the editor and dispatch an update, instead
of only sending new messages. Default off — existing send-only consumers
are unaffected. The package stays integration-agnostic: no network calls,
no token knowledge; the host brokers all I/O and computes the editability
verdict.
- `editing: { onLoadMessage, onUpdate, loadRecentMessages? }` — presence
enables edit mode.
- Two entry points in the load dialog: paste a Slack permalink, or pick a
"recent message from this app". Each recent row shows the identity it
was posted as (BOT/YOU) via `editableVia`, which also routes the update
through the correct token.
- Toolbar gains an "Edit message" entry, an edit-mode badge with
switch-back, and flips the primary action Send → Update message.
- UpdateDialog locks the channel to the source and fixes the post-as
identity to the verdict's `editableVia` (the user path reuses
`loadSendAsUserStatus` for the sign-in gate).
- Not-editable verdicts render the host's reason inline with an
"Open as a new message instead" fallback.
Extends the mocked Playground to exercise every branch in-memory: a
message store, mock load/update/recent hooks computing the real verdict,
and knobs for `editing` on/off, `canSendAsUser`, and `oauthUrl`. The
demo models conservative host behavior by hiding user-authored recent
messages when there's no user token.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
block-kitchen | 99b5bad | Commit Preview URL Branch Preview URL |
Jun 30 2026, 01:26 AM |
- Collapse the edit-mode demo panel by default so it doesn't crowd the builder on load. - Move the "copy a link…" instruction out of the title into a subtitle under "Edit-mode demo (mocked host)". - Rewrite the knob checkbox labels in plain English (prop name in parens). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rewrite the confusing update-dialog description ("Re-send the edited
blocks… The channel is locked") to plain language.
- Split the edit-mode button labels to mirror send: the toolbar primary
defaults to "Review & update" (opens the dialog), the dialog confirm
defaults to "Update message" (commits). Add `updateButtonLabel` /
`confirmUpdateLabel`.
- Make the load-message entry button configurable via `loadButtonLabel`
(default "Load message") and give it the primary color.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…left edge The edit-mode entry button (and the editing badge that replaces it) now render as the leftmost item in the toolbar, ahead of the surface/theme/docs controls, rather than after them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…creens Surface picker, Docs, Clear, and View JSON now render icon-only on small screens instead of collapsing Clear/View JSON/Docs into a "⋯" overflow menu (and the surface picker drops its caret). Labels return at `sm+`. Removes the now-unused overflow menu and its helper components. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add an info tooltip next to the load dialog's description explaining how to get a message link in Slack (hover → More actions / right-click → Copy link). - Demo: replace the "enable edit mode" checkbox with an "Editing" dropdown (Write-only / Read & Write) in the header, left of the Theme picker. "Read & Write" exposes the existing settings + message-store panel; "Write-only" hides it and falls back to send-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the on-page edit-mode panel with a header button ("Editing:
…", left of Theme) that opens a modal. The modal has Write-only /
Read & Write tabs; the Read & Write tab holds the user-token settings
and the message store, so none of it lives on the page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace em dashes with periods/commas in the update dialog description, the demo edit-mode modal text, and the README editing section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Move the edit-mode indicator out of the toolbar control row into its own
full-width banner beneath it ("Editing an existing message in #channel
…" with a "Switch to a new message" action), so the editing state is
front and center. Uses high-contrast foreground text on a muted surface
(passes the a11y color-contrast check).
- Demo: make the light/dark toggle an icon-only button (☀️/🌙).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restyle the Write-only / Read & Write mode tabs as a segmented control (muted track, raised active segment) so they read as a switcher choice rather than underline tabs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…age" Leaving edit mode now discards the loaded message's blocks and reopens the load-message dialog, so the user starts from a clean slate and can immediately pick another message to edit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- The primary action is now a split button in edit mode: the main button is "Review & send" (new message) or "Review & update" (loaded message), and a separated caret opens a menu to "Edit a message" or "Send as a new message". Removes the standalone "Load message" toolbar button (and the now-unused loadButtonLabel prop). - Fix the load dialog auto-opening its help tooltip: focus the link input on open instead of letting the info button take autofocus. - Move the toolbar's icon-only breakpoint from sm to md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Demo: onUpdate now shows a confirmation alert like onSend, so the playground responds the same way for both actions. - Reword the edit-mode banner from "Editing an existing message in …" to "References an existing message in …". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The header mode picker now reads "Mode: <value>" and the theme picker "Theme: <value>" with the label inside the control, instead of a separate label sitting outside it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d control Replace the native theme <select> with a button-style dropdown: the closed control reads "Theme: <value>" while the open list shows bare option labels, instead of repeating "Theme:" on every row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switches the remaining sm-gated header text (the "— live demo" suffix and the subtitle) to md, so the whole app collapses to icon-only / hides labels at the same md breakpoint as the toolbar and pickers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correct the edit-mode action model: - Restore the standalone "Load message" toolbar button (its original left-side entry point), unrelated to the send button's menu. - When a message is loaded, the top-right is a split button: the main action updates the message in place, and its menu offers "Update message" and "Send as a new message" (post the current blocks as a brand-new message via the normal Send dialog). - Render the Send and Update dialogs independently so "Send as a new message" gets the full channel picker while "Update message" stays channel-locked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Docs link used text-sm (20px line-height) while every other toolbar button is text-xs; the taller line box made its icon/label read as vertically off. Match text-xs + font-medium and drop the redundant py-1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The toolbar send button (which opens the review/send dialog) now defaults to "Review & send" instead of "Send", mirroring the "Review & update" edit action. The dialog's confirm button stays "Send". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render the loaded message's Slack timestamp as a readable date (e.g. "Jun 9, 2024, 11:14 PM") in the "References an existing message" banner instead of the raw ts; the raw value stays in a title tooltip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Let the host optionally pass `username` and `iconUrl` on a loaded message (LoadResult / RecentMessage). When present, the preview's message header shows that author name and avatar instead of the generic workspace name; the avatar URL is validated with the same safe-image check as block images. Both fields are optional and default to the existing workspace chrome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface each fixture's mocked author identity (avatar + username) in the edit-mode modal's message store, so the sample usernames and icons are visible in the playground, not just after loading a message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the real name used in the demo's sample author identity and the context editor's example placeholders with a fictional "Riley Park" / "@riley". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
Adds an opt-in
editingprop toBlockKitchenso a host can load an existing Slack message into the editor and dispatch achat.update, instead of only sending new messages. Default off — existing send-only consumers are unaffected. Implements ENG-5099; host wiring is tracked on parent ENG-5096.Why
Today the package is send-only (
onSendwith{ channelId, blocks, sendAsUser }) — no message reference, no update path. Hosts need to let users edit an already-posted message. The capability is gated behind props and stays integration-agnostic: no network calls, no Slack-token knowledge; the host brokers all I/O and computes the editability verdict.What changed
editing: { onLoadMessage, onUpdate, loadRecentMessages? }— presence enables edit mode.BOT/YOU) viaeditableVia, which also routes the update through the correct token.editableVia(the user path reusesloadSendAsUserStatusfor the "Sign in with Slack" gate).reasoninline with an "Open as a new message instead" fallback (hydrates blocks when the host supplies them).editingon/off,canSendAsUser, andoauthUrl. Models conservative host behavior by hiding user-authored recent messages when there's no user token.EditingConfig,LoadResult,LoadMessageInput,UpdatePayload,UpdateResult,EditableVia,RecentMessage. README documents the prop + flow.Test plan
pnpm typecheckpnpm lintpnpm test(356 passing; +2 new toolbar edit-mode stories)pnpm demo:build)Manually verified in the demo (browser):
canSendAsUseris offBOT/YOUidentity chips; user rows hidden when no user token, restored when re-enablededitingoff restores send-only behavior (no edit entry, primary back to Send)Notes for reviewer
LoadResult.okcarries optionalchannelName(for a readable badge) and the!okvariant carries optionalblocks(so "Open as new" can hydrate). Flag if you'd rather match the issue verbatim.editableViafield (no parallel infrastructure); the caller controls which messages appear and how each is tagged.