@tightknitai/block-kitchen 0.8.10 → 0.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,6 +108,10 @@ export function MyBuilderPage() {
108
108
  | `loadChannels` | `() => Promise<{ id: string; name: string }[]>` | yes | Returns channels available to send to. The package never makes Slack API calls itself. |
109
109
  | `loadSendAsUserStatus` | `() => Promise<{ canSendAsUser: boolean; oauthUrl?: string }>` | yes | Whether the current user has a Slack user-token and can post as themselves. If `canSendAsUser` is false, `oauthUrl` is shown as a "Sign in with Slack" link. |
110
110
  | `onSend` | `(payload) => Promise<{ ok: boolean; error?: string }>` | yes | Called when the user submits the send dialog. Payload is `{ channelId, blocks, sendAsUser }`. |
111
+ | `editing` | `{ onLoadMessage, onUpdate, loadRecentMessages? }` | no | Opt-in edit mode. When present, the toolbar exposes "Edit message": the user pastes a Slack message link, `onLoadMessage({ link })` returns a host-computed [editability verdict](#editing-an-existing-message-opt-in), and a successful load flips the primary action to "Update message" wired to `onUpdate`. Pass `loadRecentMessages` to add a "recent messages from this app" picker beside the paste input. Omit `editing` to keep send-only behavior. |
112
+ | `loadButtonLabel` | `string` | no | Label + accessible name for the toolbar button that opens the load-message dialog (the edit-mode entry point). Defaults to `'Load message'`. Only shown when `editing` is set and no message is loaded. |
113
+ | `updateButtonLabel` | `string` | no | Label for the toolbar's primary button while a message is loaded for editing. It's a split button: clicking it updates the message in place; the menu beside it also offers "Send as a new message" (post the current blocks as new). Defaults to `'Review & update'`. |
114
+ | `confirmUpdateLabel` | `string` | no | Label for the update dialog's final confirm button. Defaults to `'Update message'` (shows `'Updating…'` while in flight). |
111
115
  | `previewHooks` | `PreviewHooks` | no | Hooks forwarded to `slack-blocks-to-jsx`'s `<Message>` for resolving user / channel / emoji directives. |
112
116
  | `customEmojis` | `CustomEmoji[]` | no | Workspace custom emoji (`{ name, url, alias }`) the preview resolves. Entries with a `url` render `:name:` as the workspace image; alias entries (`url: null`) fall back to their target emoji. Render-only — never serialized into the emitted Block Kit JSON. A caller-supplied `previewHooks.emoji` takes precedence. |
113
117
  | `palette` | `PaletteSection[]` | no | The left-hand palette of draggable variants. Defaults to `defaultPalette`. Spread it to filter, reorder, or add your own pre-configured variants — see [Customizing the palette](#customizing-the-palette). |
@@ -119,10 +123,70 @@ export function MyBuilderPage() {
119
123
  | `showThemeControl` | `boolean` | no | Defaults to `true`. When `false`, the toolbar's light/dark toggle is hidden and the theme stays at `defaultPreviewTheme`. Ignored when `previewTheme` is set (a controlled theme always hides the toggle). |
120
124
  | `defaultPreviewTheme` | `'light' \| 'dark'` | no | Initial (uncontrolled) preview theme. Pass the host app's current theme so the preview opens matched to the consuming app's appearance. Ignored when `previewTheme` is provided. |
121
125
  | `previewTheme` | `'light' \| 'dark'` | no | Controlled preview theme. When set, the preview renders in this theme, follows it reactively, and the toolbar's light/dark toggle is hidden so the host app fully owns the theme. Leave unset to keep the preview uncontrolled (seeded from `defaultPreviewTheme`, toggle shown). |
122
- | `sendButtonLabel` | `string` | no | Label and accessible name for the toolbar's Send button (which opens the send dialog). Defaults to `'Send'`. Use it to signal that a configuration step follows, e.g. `'Send to channel…'`. |
126
+ | `sendButtonLabel` | `string` | no | Label and accessible name for the toolbar's Send button (which opens the send dialog). Defaults to `'Review & send'` (the dialog is the review step). Override it for product-specific copy, e.g. `'Send to channel…'`. |
123
127
  | `confirmSendLabel` | `string` | no | Label for the send dialog's final confirm button. Defaults to `'Send'` (shows `'Sending…'` while in flight). |
124
128
  | `theme` | `BrandTheme \| BrandPreset` | no | Branding tokens applied to the builder chrome (toolbar, palette, popovers, dialogs). Accepts a `Partial<BrandTokens>` map and optional `light`/`dark` overrides. See [Styling](#styling) below. |
125
129
 
130
+ ## Editing an existing message (opt-in)
131
+
132
+ By default the builder is send-only. Pass `editing` to let users load an
133
+ already-posted message, edit its blocks, and dispatch a `chat.update`. The
134
+ package stays integration-agnostic: it makes no Slack calls and computes
135
+ nothing about who can edit; the host does both.
136
+
137
+ ```tsx
138
+ <BlockKitchen
139
+ /* …send-only props… */
140
+ editing={{
141
+ // Host parses the pasted permalink, fetches the message, and returns a
142
+ // verdict. `chat.update` only edits a message authored by the calling
143
+ // token, so the host decides: bot message → 'bot', the user's own
144
+ // message → 'user', anything else → not editable.
145
+ onLoadMessage: async ({ link }) => {
146
+ const msg = await fetchMessageFromPermalink(link); // your code
147
+ if (!msg) return { ok: false, reason: "Couldn't find that message." };
148
+ if (!msg.blocks?.length) return { ok: false, reason: 'This message has no editable blocks.' };
149
+ // `username` + `iconUrl` are optional; when present they show in the
150
+ // preview header instead of the generic workspace name/avatar.
151
+ const author = { username: msg.authorName, iconUrl: msg.authorAvatarUrl };
152
+ if (msg.appId === MY_APP_ID)
153
+ return { ok: true, channelId: msg.channel, channelName: msg.channelName, ts: msg.ts, blocks: msg.blocks, editableVia: 'bot', ...author };
154
+ if (msg.userId === currentUserId)
155
+ return { ok: true, channelId: msg.channel, channelName: msg.channelName, ts: msg.ts, blocks: msg.blocks, editableVia: 'user', ...author };
156
+ return { ok: false, reason: 'Only messages your app or you posted can be edited.', blocks: msg.blocks };
157
+ },
158
+ // Sibling to onSend; carries the source channel + ts. `asUser` follows
159
+ // the verdict's `editableVia`.
160
+ onUpdate: async ({ channelId, ts, blocks, asUser }) => {
161
+ await chatUpdate({ channel: channelId, ts, blocks, asUser }); // your code
162
+ return { ok: true };
163
+ },
164
+ // Optional: adds a "recent messages from this app" picker beside the paste
165
+ // input. These are editable-by-construction (the app authored them), so
166
+ // picking one loads it straight into edit mode (no verdict needed).
167
+ loadRecentMessages: async () => {
168
+ const msgs = await fetchRecentAppMessages(); // your code
169
+ return msgs.map((m) => ({
170
+ channelId: m.channel,
171
+ channelName: m.channelName,
172
+ ts: m.ts,
173
+ blocks: m.blocks,
174
+ editableVia: 'bot', // defaults to 'bot' if omitted
175
+ label: m.preview // one-line preview shown in the picker row
176
+ }));
177
+ }
178
+ }}
179
+ />
180
+ ```
181
+
182
+ - On `ok`, the builder hydrates with `blocks`, shows an edit-mode badge, locks
183
+ the destination to the source channel, and fixes the post-as identity to
184
+ `editableVia` (the `'user'` path reuses `loadSendAsUserStatus` for the "Sign
185
+ in with Slack" gate).
186
+ - On `{ ok: false, reason }`, the load dialog renders the reason inline and
187
+ offers **Open as a new message instead**. Pass `blocks` on the failure
188
+ result to hydrate the draft for that fallback.
189
+
126
190
  ## Customizing the palette
127
191
 
128
192
  The default palette ships with curated presets for every supported block type. To narrow what's available, or add your own pre-configured variants (e.g. a "Help footer" section), pass a `palette` array. Define it at module scope (or wrap in `useMemo`) so it stays referentially stable across renders.
@@ -1,5 +1,5 @@
1
- import { L as SupportedBlock } from './palette-DBTHRygt.cjs';
2
- export { N as SupportedBlockType } from './palette-DBTHRygt.cjs';
1
+ import { T as SupportedBlock } from './palette-Bup05opK.cjs';
2
+ export { U as SupportedBlockType } from './palette-Bup05opK.cjs';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5
 
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as SupportedBlock } from './palette-DBTHRygt.js';
2
- export { N as SupportedBlockType } from './palette-DBTHRygt.js';
1
+ import { T as SupportedBlock } from './palette-Bup05opK.js';
2
+ export { U as SupportedBlockType } from './palette-Bup05opK.js';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5