@tightknitai/block-kitchen 0.10.21 → 0.10.23

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
@@ -138,6 +138,7 @@ Given no height to work with — a plain `<div>` in ordinary document flow — i
138
138
  | `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'`. |
139
139
  | `confirmUpdateLabel` | `string` | no | Label for the update dialog's final confirm button. Defaults to `'Update message'` (shows `'Updating…'` while in flight). |
140
140
  | `previewHooks` | `PreviewHooks` | no | Hooks forwarded to `slack-blocks-to-jsx`'s `<Message>` for resolving user / channel / emoji directives. |
141
+ | `blockActions` | `BlockAction[]` | no | Host buttons added to every block's action toolbar, before Edit / Duplicate / Delete. Each is `{ id, label, icon, onSelect }`, and `onSelect` receives the block's `{ index, childIndex }`. See [Adding your own block actions](#adding-your-own-block-actions-blockactions). |
141
142
  | `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. |
142
143
  | `mentions` | `{ searchUsers, getUserName?, searchChannels?, getChannelName? }` | no | Opt-in rich-text mentions. `@` and the Mention button search users; when `searchChannels` is provided, `#` and the Channel button search channels. Picks serialize as Slack `user` / `channel` elements. See [Mentions](#mentions-opt-in). |
143
144
  | `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). |
@@ -441,6 +442,39 @@ update against the target from `loadedMessage` / `onLoadedMessageChange`.
441
442
  > <BlockKitchen loading={{ onLoadMessage, loadRecentMessages }} onUpdate={onUpdate} />
442
443
  > ```
443
444
 
445
+ ## Adding your own block actions (`blockActions`)
446
+
447
+ Actions about a block that belong to your app, like "Comment on this block",
448
+ can sit in the block's own toolbar (the hover toolbar on desktop, the inline
449
+ row on mobile) through `blockActions`:
450
+
451
+ ```tsx
452
+ <BlockKitchen
453
+ blockActions={[
454
+ {
455
+ id: "comment",
456
+ label: "Comment on block",
457
+ icon: <MessageSquare />,
458
+ onSelect: ({ index, childIndex }) => openCommentOn(blocks[index], childIndex),
459
+ },
460
+ ]}
461
+ />
462
+ ```
463
+
464
+ `onSelect` gets the block's position: `index` among the draft's top-level
465
+ blocks, and `childIndex` inside the container holding it (`null` for a
466
+ top-level block). Map it onto the blocks `onChange` last reported; builder
467
+ ids never leave the package. An `svg` icon is sized to match the built-in
468
+ ones.
469
+
470
+ Each block's row also carries `data-bk-block-index` (plus
471
+ `data-bk-child-index` inside a container), so the host can find a block on
472
+ the canvas, for example to scroll to it or highlight it:
473
+
474
+ ```ts
475
+ document.querySelector(`.bk-root [data-bk-block-index="${index}"]:not([data-bk-child-index])`);
476
+ ```
477
+
444
478
  ## Customizing the palette
445
479
 
446
480
  On desktop the palette is a rail down the left-hand side, with a chevron handle on its outer border that hides and restores it — collapsed, the preview gets the full width, and the rail comes back with its open sections and search exactly as they were. Below the `md` breakpoint there's no rail: the toolbar's **Blocks** button opens the palette as a bottom sheet instead, and picking a block closes it.
@@ -1,5 +1,5 @@
1
- import { S as SupportedBlock } from './palette-CXXqlwIH.cjs';
2
- export { a8 as SupportedBlockType } from './palette-CXXqlwIH.cjs';
1
+ import { S as SupportedBlock } from './palette-DFTL0Han.cjs';
2
+ export { aa as SupportedBlockType } from './palette-DFTL0Han.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 { S as SupportedBlock } from './palette-CXXqlwIH.js';
2
- export { a8 as SupportedBlockType } from './palette-CXXqlwIH.js';
1
+ import { S as SupportedBlock } from './palette-DFTL0Han.js';
2
+ export { aa as SupportedBlockType } from './palette-DFTL0Han.js';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5
 
package/dist/index.cjs CHANGED
@@ -4631,6 +4631,40 @@ function SendDialog({
4631
4631
  ] })
4632
4632
  ] }) });
4633
4633
  }
4634
+ function BlockActionButtons({
4635
+ actions,
4636
+ position,
4637
+ variant
4638
+ }) {
4639
+ if (!actions?.length || !position) {
4640
+ return null;
4641
+ }
4642
+ return actions.map(
4643
+ (action) => variant === "toolbar" ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
4644
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4645
+ "button",
4646
+ {
4647
+ type: "button",
4648
+ "aria-label": action.label,
4649
+ onClick: () => action.onSelect(position),
4650
+ className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring [&_svg]:h-3.5 [&_svg]:w-3.5",
4651
+ children: action.icon
4652
+ }
4653
+ ) }),
4654
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: action.label })
4655
+ ] }, action.id) : /* @__PURE__ */ jsxRuntime.jsx(
4656
+ "button",
4657
+ {
4658
+ type: "button",
4659
+ "aria-label": action.label,
4660
+ onClick: () => action.onSelect(position),
4661
+ className: "flex h-9 w-9 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground [&_svg]:h-4 [&_svg]:w-4",
4662
+ children: action.icon
4663
+ },
4664
+ action.id
4665
+ )
4666
+ );
4667
+ }
4634
4668
  var RadioGroup = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(RadioGroupPrimitive__namespace.Root, { className: cn("grid gap-2", className), ...props, ref }));
4635
4669
  RadioGroup.displayName = RadioGroupPrimitive__namespace.Root.displayName;
4636
4670
  var RadioGroupItem = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -10465,7 +10499,9 @@ function BlockRow({
10465
10499
  index,
10466
10500
  total,
10467
10501
  onReorder,
10468
- isPaletteDrag = false
10502
+ isPaletteDrag = false,
10503
+ position,
10504
+ blockActions
10469
10505
  }) {
10470
10506
  const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver } = sortable.useSortable({
10471
10507
  id: builderBlock.id
@@ -10485,6 +10521,8 @@ function BlockRow({
10485
10521
  {
10486
10522
  ref: setNodeRef,
10487
10523
  style,
10524
+ "data-bk-block-index": position?.index,
10525
+ "data-bk-child-index": position?.childIndex ?? void 0,
10488
10526
  className: cn("group relative mb-2 last:mb-0 hover:z-10 md:mb-0", isDragging && "opacity-40"),
10489
10527
  children: [
10490
10528
  showDropIndicator ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -10617,6 +10655,7 @@ function BlockRow({
10617
10655
  /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move down" })
10618
10656
  ] })
10619
10657
  ] }) : null,
10658
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10620
10659
  /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10621
10660
  /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10622
10661
  "button",
@@ -10679,6 +10718,7 @@ function BlockRow({
10679
10718
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-4 w-4" })
10680
10719
  }
10681
10720
  ) : null,
10721
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "inline" }),
10682
10722
  /* @__PURE__ */ jsxRuntime.jsx(
10683
10723
  "button",
10684
10724
  {
@@ -10760,7 +10800,8 @@ function ContainerRow({
10760
10800
  onReorder,
10761
10801
  index,
10762
10802
  total,
10763
- isPaletteDrag = false
10803
+ isPaletteDrag = false,
10804
+ blockActions
10764
10805
  }) {
10765
10806
  const { id } = builderBlock;
10766
10807
  const block = builderBlock.block;
@@ -10773,11 +10814,13 @@ function ContainerRow({
10773
10814
  const { setNodeRef: setBodyRef, isOver: isBodyOver } = core.useDroppable({ id: containerBodyId(id) });
10774
10815
  const style = { transform: utilities.CSS.Transform.toString(transform), transition };
10775
10816
  const icon = block.icon && "image_url" in block.icon ? block.icon.image_url : void 0;
10817
+ const position = typeof index === "number" ? { index, childIndex: null } : void 0;
10776
10818
  return /* @__PURE__ */ jsxRuntime.jsxs(
10777
10819
  "div",
10778
10820
  {
10779
10821
  ref: setNodeRef,
10780
10822
  style,
10823
+ "data-bk-block-index": index,
10781
10824
  className: cn("group/container relative mb-2 last:mb-0 hover:z-10", isDragging && "opacity-40"),
10782
10825
  children: [
10783
10826
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -10858,7 +10901,9 @@ function ContainerRow({
10858
10901
  index: idx,
10859
10902
  total: children.length,
10860
10903
  onReorder,
10861
- isPaletteDrag
10904
+ isPaletteDrag,
10905
+ position: typeof index === "number" ? { index, childIndex: idx } : void 0,
10906
+ blockActions
10862
10907
  },
10863
10908
  child.id
10864
10909
  )),
@@ -10901,6 +10946,7 @@ function ContainerRow({
10901
10946
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Move up", disabled: index === 0, onClick: () => onReorder(id, index - 1), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3.5 w-3.5" }) }),
10902
10947
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Move down", disabled: index >= total - 1, onClick: () => onReorder(id, index + 1), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-3.5 w-3.5" }) })
10903
10948
  ] }) : null,
10949
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10904
10950
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Edit", onClick: () => onOpenBlockChange?.(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }) }),
10905
10951
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Duplicate", onClick: () => onDuplicate(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" }) }),
10906
10952
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Delete", destructive: true, onClick: () => onDelete(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }) })
@@ -10910,6 +10956,7 @@ function ContainerRow({
10910
10956
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center justify-between gap-2 rounded-md border border-dashed bg-muted/40 px-2 py-1 md:hidden", children: [
10911
10957
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] font-medium text-muted-foreground", children: "Container" }),
10912
10958
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
10959
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "inline" }),
10913
10960
  /* @__PURE__ */ jsxRuntime.jsx(
10914
10961
  "button",
10915
10962
  {
@@ -10991,7 +11038,8 @@ function Surface({
10991
11038
  onDelete,
10992
11039
  onReorder,
10993
11040
  isPaletteDrag = false,
10994
- onOpenPalette
11041
+ onOpenPalette,
11042
+ blockActions
10995
11043
  }) {
10996
11044
  const { setNodeRef, isOver } = core.useDroppable({ id: SURFACE_DROPPABLE_ID });
10997
11045
  const isDark = previewTheme === "dark";
@@ -11023,7 +11071,8 @@ function Surface({
11023
11071
  index: idx,
11024
11072
  total: blocks.length,
11025
11073
  onReorder,
11026
- isPaletteDrag
11074
+ isPaletteDrag,
11075
+ blockActions
11027
11076
  },
11028
11077
  block.id
11029
11078
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -11041,7 +11090,9 @@ function Surface({
11041
11090
  index: idx,
11042
11091
  total: blocks.length,
11043
11092
  onReorder,
11044
- isPaletteDrag
11093
+ isPaletteDrag,
11094
+ position: { index: idx, childIndex: null },
11095
+ blockActions
11045
11096
  },
11046
11097
  block.id
11047
11098
  )
@@ -11799,6 +11850,7 @@ function BlockKitchen(props) {
11799
11850
  onChange,
11800
11851
  onValidationChange,
11801
11852
  previewHooks,
11853
+ blockActions,
11802
11854
  customEmojis,
11803
11855
  mentions,
11804
11856
  loadChannels,
@@ -12179,7 +12231,8 @@ function BlockKitchen(props) {
12179
12231
  onDelete: removeBlock,
12180
12232
  onReorder: reorderBlock,
12181
12233
  isPaletteDrag: activePaletteVariant !== null,
12182
- onOpenPalette: () => setPaletteOpen(true)
12234
+ onOpenPalette: () => setPaletteOpen(true),
12235
+ blockActions
12183
12236
  }
12184
12237
  ) })
12185
12238
  ] })