Skip to content

feat(editing): opt-in message edit mode (load existing + update) - #119

Merged
StephenTangCook merged 24 commits into
mainfrom
claude/hardcore-davinci-f1cf91
Jun 30, 2026
Merged

feat(editing): opt-in message edit mode (load existing + update)#119
StephenTangCook merged 24 commits into
mainfrom
claude/hardcore-davinci-f1cf91

Conversation

@StephenTangCook

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in editing prop to BlockKitchen so a host can load an existing Slack message into the editor and dispatch a chat.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 (onSend with { 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.
  • 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 with Slack" gate).
  • Not-editable verdicts render the host's reason inline with an "Open as a new message instead" fallback (hydrates blocks when the host supplies them).
  • Playground demo exercises 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. Models conservative host behavior by hiding user-authored recent messages when there's no user token.
  • New exported types: EditingConfig, LoadResult, LoadMessageInput, UpdatePayload, UpdateResult, EditableVia, RecentMessage. README documents the prop + flow.

Test plan

  • pnpm typecheck
  • pnpm lint
  • pnpm test (356 passing; +2 new toolbar edit-mode stories)
  • Demo built (pnpm demo:build)

Manually verified in the demo (browser):

  • bot-message edit round-trip — update visibly mutated the stored message (2 → 3 blocks)
  • user-message path — update dialog posts as "Your account"; sign-in gate fires + Update disabled when canSendAsUser is off
  • not-editable verdicts (someone else / non-block / edit-window-closed) render reason + "Open as a new message instead"
  • recent-messages picker shows BOT/YOU identity chips; user rows hidden when no user token, restored when re-enabled
  • toggling editing off restores send-only behavior (no edit entry, primary back to Send)
  • no console errors

Notes for reviewer

  • Two small, back-compatible extensions to the TBD prop shape from the issue: LoadResult.ok carries optional channelName (for a readable badge) and the !ok variant carries optional blocks (so "Open as new" can hydrate). Flag if you'd rather match the issue verbatim.
  • The bot-vs-user distinction for recent messages is serviced in the UI from the single editableVia field (no parallel infrastructure); the caller controls which messages appear and how each is tagged.
  • Not published / dep-bumped yet — release-please ships on merge.

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>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

StephenTangCook and others added 23 commits June 29, 2026 14:20
- 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>
@StephenTangCook
StephenTangCook merged commit 2eac3cf into main Jun 30, 2026
13 checks passed
@StephenTangCook
StephenTangCook deleted the claude/hardcore-davinci-f1cf91 branch June 30, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant