@tightknitai/block-kitchen 0.10.22 → 0.10.24

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,8 @@ 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). |
142
+ | `renderBlockAdornment` | `(position: BlockPosition) => ReactNode` | no | Host content shown beside every block, at its right edge, all the time (unlike `blockActions`, which show on hover), e.g. a count of comments on the block. Return `null` for a block with nothing to show. See [Adding your own block actions](#adding-your-own-block-actions-blockactions). |
141
143
  | `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
144
  | `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
145
  | `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 +443,52 @@ update against the target from `loadedMessage` / `onLoadedMessageChange`.
441
443
  > <BlockKitchen loading={{ onLoadMessage, loadRecentMessages }} onUpdate={onUpdate} />
442
444
  > ```
443
445
 
446
+ ## Adding your own block actions (`blockActions`)
447
+
448
+ Actions about a block that belong to your app, like "Comment on this block",
449
+ can sit in the block's own toolbar (the hover toolbar on desktop, the inline
450
+ row on mobile) through `blockActions`:
451
+
452
+ ```tsx
453
+ <BlockKitchen
454
+ blockActions={[
455
+ {
456
+ id: "comment",
457
+ label: "Comment on block",
458
+ icon: <MessageSquare />,
459
+ onSelect: ({ index, childIndex }) => openCommentOn(blocks[index], childIndex),
460
+ },
461
+ ]}
462
+ />
463
+ ```
464
+
465
+ `onSelect` gets the block's position: `index` among the draft's top-level
466
+ blocks, and `childIndex` inside the container holding it (`null` for a
467
+ top-level block). Map it onto the blocks `onChange` last reported; builder
468
+ ids never leave the package. An `svg` icon is sized to match the built-in
469
+ ones.
470
+
471
+ For something that should show all the time, like a count of comments on
472
+ each block, pass `renderBlockAdornment`. It's called with the same position
473
+ and renders beside the block, at its right edge; return `null` for a block
474
+ with nothing to show:
475
+
476
+ ```tsx
477
+ <BlockKitchen
478
+ renderBlockAdornment={({ index, childIndex }) =>
479
+ childIndex === null && counts[index] ? <CommentCount count={counts[index]} /> : null
480
+ }
481
+ />
482
+ ```
483
+
484
+ Each block's row also carries `data-bk-block-index` (plus
485
+ `data-bk-child-index` inside a container), so the host can find a block on
486
+ the canvas, for example to scroll to it or highlight it:
487
+
488
+ ```ts
489
+ document.querySelector(`.bk-root [data-bk-block-index="${index}"]:not([data-bk-child-index])`);
490
+ ```
491
+
444
492
  ## Customizing the palette
445
493
 
446
494
  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-Bup6i-9c.cjs';
2
+ export { aa as SupportedBlockType } from './palette-Bup6i-9c.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-Bup6i-9c.js';
2
+ export { aa as SupportedBlockType } from './palette-Bup6i-9c.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,10 @@ function BlockRow({
10465
10499
  index,
10466
10500
  total,
10467
10501
  onReorder,
10468
- isPaletteDrag = false
10502
+ isPaletteDrag = false,
10503
+ position,
10504
+ blockActions,
10505
+ renderBlockAdornment
10469
10506
  }) {
10470
10507
  const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver } = sortable.useSortable({
10471
10508
  id: builderBlock.id
@@ -10480,11 +10517,70 @@ function BlockRow({
10480
10517
  const [inlineEditing, setInlineEditing] = React.useState(false);
10481
10518
  const showDropIndicator = isPaletteDrag && isOver;
10482
10519
  const preview = /* @__PURE__ */ jsxRuntime.jsx(SlackBlockPreview, { block: builderBlock.block, hooks: previewHooks, theme: previewTheme });
10520
+ const body = isRichText && inlineEditing ? /* @__PURE__ */ jsxRuntime.jsx(
10521
+ RichTextInlineEditor,
10522
+ {
10523
+ block: builderBlock.block,
10524
+ onSave: (next) => {
10525
+ onUpdate(builderBlock.id, next);
10526
+ setInlineEditing(false);
10527
+ },
10528
+ onCancel: () => setInlineEditing(false)
10529
+ }
10530
+ ) : isRichText ? /* @__PURE__ */ jsxRuntime.jsx(
10531
+ "button",
10532
+ {
10533
+ type: "button",
10534
+ "aria-label": "Edit block",
10535
+ onClick: () => setInlineEditing(true),
10536
+ ...attributes,
10537
+ ...listeners,
10538
+ className: cn(
10539
+ "block w-full cursor-grab rounded-sm border-0 bg-transparent p-0 text-left transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10540
+ hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10541
+ ),
10542
+ children: preview
10543
+ }
10544
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange, children: [
10545
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10546
+ "div",
10547
+ {
10548
+ role: "button",
10549
+ tabIndex: 0,
10550
+ "aria-label": "Edit block",
10551
+ ...sortableA11yAttrs,
10552
+ ...listeners,
10553
+ onKeyDown: (e) => {
10554
+ if (e.key === "Enter" || e.key === " ") {
10555
+ e.preventDefault();
10556
+ onOpenChange?.(!isOpen);
10557
+ }
10558
+ },
10559
+ className: cn(
10560
+ "block w-full cursor-grab rounded-sm transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10561
+ hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10562
+ ),
10563
+ children: preview
10564
+ }
10565
+ ) }),
10566
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10567
+ BlockEditor,
10568
+ {
10569
+ block: builderBlock.block,
10570
+ errors,
10571
+ onChange: (next) => onUpdate(builderBlock.id, next),
10572
+ onDone: () => onOpenChange?.(false)
10573
+ }
10574
+ ) })
10575
+ ] });
10576
+ const adornment = position && renderBlockAdornment?.(position);
10483
10577
  return /* @__PURE__ */ jsxRuntime.jsxs(
10484
10578
  "div",
10485
10579
  {
10486
10580
  ref: setNodeRef,
10487
10581
  style,
10582
+ "data-bk-block-index": position?.index,
10583
+ "data-bk-child-index": position?.childIndex ?? void 0,
10488
10584
  className: cn("group relative mb-2 last:mb-0 hover:z-10 md:mb-0", isDragging && "opacity-40"),
10489
10585
  children: [
10490
10586
  showDropIndicator ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -10496,62 +10592,10 @@ function BlockRow({
10496
10592
  }
10497
10593
  ) : null,
10498
10594
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
10499
- isRichText && inlineEditing ? /* @__PURE__ */ jsxRuntime.jsx(
10500
- RichTextInlineEditor,
10501
- {
10502
- block: builderBlock.block,
10503
- onSave: (next) => {
10504
- onUpdate(builderBlock.id, next);
10505
- setInlineEditing(false);
10506
- },
10507
- onCancel: () => setInlineEditing(false)
10508
- }
10509
- ) : isRichText ? /* @__PURE__ */ jsxRuntime.jsx(
10510
- "button",
10511
- {
10512
- type: "button",
10513
- "aria-label": "Edit block",
10514
- onClick: () => setInlineEditing(true),
10515
- ...attributes,
10516
- ...listeners,
10517
- className: cn(
10518
- "block w-full cursor-grab rounded-sm border-0 bg-transparent p-0 text-left transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10519
- hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10520
- ),
10521
- children: preview
10522
- }
10523
- ) : /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange, children: [
10524
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10525
- "div",
10526
- {
10527
- role: "button",
10528
- tabIndex: 0,
10529
- "aria-label": "Edit block",
10530
- ...sortableA11yAttrs,
10531
- ...listeners,
10532
- onKeyDown: (e) => {
10533
- if (e.key === "Enter" || e.key === " ") {
10534
- e.preventDefault();
10535
- onOpenChange?.(!isOpen);
10536
- }
10537
- },
10538
- className: cn(
10539
- "block w-full cursor-grab rounded-sm transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10540
- hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10541
- ),
10542
- children: preview
10543
- }
10544
- ) }),
10545
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10546
- BlockEditor,
10547
- {
10548
- block: builderBlock.block,
10549
- errors,
10550
- onChange: (next) => onUpdate(builderBlock.id, next),
10551
- onDone: () => onOpenChange?.(false)
10552
- }
10553
- ) })
10554
- ] }),
10595
+ renderBlockAdornment ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
10596
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: body }),
10597
+ adornment != null && adornment !== false ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: adornment }) : null
10598
+ ] }) : body,
10555
10599
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "-translate-x-1/2 pointer-events-none absolute bottom-full left-1/2 z-10 hidden bg-background px-1.5 text-[11px] text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100 md:block", children: BLOCK_TYPE_LABELS[builderBlock.block.type] }),
10556
10600
  /* @__PURE__ */ jsxRuntime.jsxs(
10557
10601
  "div",
@@ -10617,6 +10661,7 @@ function BlockRow({
10617
10661
  /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move down" })
10618
10662
  ] })
10619
10663
  ] }) : null,
10664
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10620
10665
  /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10621
10666
  /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10622
10667
  "button",
@@ -10679,6 +10724,7 @@ function BlockRow({
10679
10724
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-4 w-4" })
10680
10725
  }
10681
10726
  ) : null,
10727
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "inline" }),
10682
10728
  /* @__PURE__ */ jsxRuntime.jsx(
10683
10729
  "button",
10684
10730
  {
@@ -10760,7 +10806,9 @@ function ContainerRow({
10760
10806
  onReorder,
10761
10807
  index,
10762
10808
  total,
10763
- isPaletteDrag = false
10809
+ isPaletteDrag = false,
10810
+ blockActions,
10811
+ renderBlockAdornment
10764
10812
  }) {
10765
10813
  const { id } = builderBlock;
10766
10814
  const block = builderBlock.block;
@@ -10773,102 +10821,112 @@ function ContainerRow({
10773
10821
  const { setNodeRef: setBodyRef, isOver: isBodyOver } = core.useDroppable({ id: containerBodyId(id) });
10774
10822
  const style = { transform: utilities.CSS.Transform.toString(transform), transition };
10775
10823
  const icon = block.icon && "image_url" in block.icon ? block.icon.image_url : void 0;
10776
- return /* @__PURE__ */ jsxRuntime.jsxs(
10824
+ const position = typeof index === "number" ? { index, childIndex: null } : void 0;
10825
+ const adornment = position && renderBlockAdornment?.(position);
10826
+ const card = /* @__PURE__ */ jsxRuntime.jsxs(
10777
10827
  "div",
10778
10828
  {
10779
- ref: setNodeRef,
10780
- style,
10781
- className: cn("group/container relative mb-2 last:mb-0 hover:z-10", isDragging && "opacity-40"),
10829
+ className: cn(
10830
+ "overflow-hidden rounded-md border bg-card transition-shadow",
10831
+ hasErrors ? "border-destructive/60" : "border-border"
10832
+ ),
10782
10833
  children: [
10783
- /* @__PURE__ */ jsxRuntime.jsxs(
10834
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: (open) => onOpenBlockChange?.(open ? id : null), children: [
10835
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10836
+ "div",
10837
+ {
10838
+ role: "button",
10839
+ tabIndex: 0,
10840
+ "aria-label": "Edit container",
10841
+ ...sortableA11yAttrs,
10842
+ ...listeners,
10843
+ onKeyDown: (e) => {
10844
+ if (e.key === "Enter" || e.key === " ") {
10845
+ e.preventDefault();
10846
+ onOpenBlockChange?.(!isOpen ? id : null);
10847
+ }
10848
+ },
10849
+ className: "flex w-full cursor-grab items-center gap-2 border-b bg-muted/40 px-3 py-2 text-left transition-colors hover:bg-muted active:cursor-grabbing",
10850
+ children: [
10851
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: icon, alt: "", "aria-hidden": "true", className: "h-5 w-5 shrink-0 rounded object-cover" }) : null,
10852
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
10853
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs font-semibold text-foreground", children: containerTitleText(block) || "Container" }),
10854
+ block.subtitle?.text ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: block.subtitle.text }) : null
10855
+ ] }),
10856
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Container" })
10857
+ ]
10858
+ }
10859
+ ) }),
10860
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10861
+ BlockEditor,
10862
+ {
10863
+ block,
10864
+ errors,
10865
+ onChange: (next) => onUpdate(id, next),
10866
+ onDone: () => onOpenBlockChange?.(null)
10867
+ }
10868
+ ) })
10869
+ ] }),
10870
+ /* @__PURE__ */ jsxRuntime.jsx(
10784
10871
  "div",
10785
10872
  {
10873
+ ref: setBodyRef,
10786
10874
  className: cn(
10787
- "overflow-hidden rounded-md border bg-card transition-shadow",
10788
- hasErrors ? "border-destructive/60" : "border-border"
10875
+ "flex flex-col gap-2 p-3 transition-colors",
10876
+ isPaletteDrag && "outline-2 -outline-offset-2 outline-dashed outline-primary/40",
10877
+ isPaletteDrag && isBodyOver && "bg-primary/5"
10789
10878
  ),
10790
- children: [
10791
- /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: (open) => onOpenBlockChange?.(open ? id : null), children: [
10792
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10793
- "div",
10794
- {
10795
- role: "button",
10796
- tabIndex: 0,
10797
- "aria-label": "Edit container",
10798
- ...sortableA11yAttrs,
10799
- ...listeners,
10800
- onKeyDown: (e) => {
10801
- if (e.key === "Enter" || e.key === " ") {
10802
- e.preventDefault();
10803
- onOpenBlockChange?.(!isOpen ? id : null);
10804
- }
10805
- },
10806
- className: "flex w-full cursor-grab items-center gap-2 border-b bg-muted/40 px-3 py-2 text-left transition-colors hover:bg-muted active:cursor-grabbing",
10807
- children: [
10808
- icon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: icon, alt: "", "aria-hidden": "true", className: "h-5 w-5 shrink-0 rounded object-cover" }) : null,
10809
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
10810
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs font-semibold text-foreground", children: containerTitleText(block) || "Container" }),
10811
- block.subtitle?.text ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: block.subtitle.text }) : null
10812
- ] }),
10813
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Container" })
10814
- ]
10815
- }
10816
- ) }),
10817
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10818
- BlockEditor,
10819
- {
10820
- block,
10821
- errors,
10822
- onChange: (next) => onUpdate(id, next),
10823
- onDone: () => onOpenBlockChange?.(null)
10824
- }
10825
- ) })
10826
- ] }),
10827
- /* @__PURE__ */ jsxRuntime.jsx(
10828
- "div",
10879
+ children: children.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
10880
+ "div",
10881
+ {
10882
+ className: cn(
10883
+ "flex items-center justify-center rounded-md border border-dashed px-3 py-6 text-center text-[11px] transition-colors",
10884
+ isBodyOver ? "border-primary/60 text-primary" : "border-border text-muted-foreground"
10885
+ ),
10886
+ children: "Drag blocks here"
10887
+ }
10888
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(sortable.SortableContext, { items: children.map((c) => c.id), strategy: sortable.verticalListSortingStrategy, children: [
10889
+ children.map((child, idx) => /* @__PURE__ */ jsxRuntime.jsx(
10890
+ BlockRow,
10829
10891
  {
10830
- ref: setBodyRef,
10831
- className: cn(
10832
- "flex flex-col gap-2 p-3 transition-colors",
10833
- isPaletteDrag && "outline-2 -outline-offset-2 outline-dashed outline-primary/40",
10834
- isPaletteDrag && isBodyOver && "bg-primary/5"
10835
- ),
10836
- children: children.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
10837
- "div",
10838
- {
10839
- className: cn(
10840
- "flex items-center justify-center rounded-md border border-dashed px-3 py-6 text-center text-[11px] transition-colors",
10841
- isBodyOver ? "border-primary/60 text-primary" : "border-border text-muted-foreground"
10842
- ),
10843
- children: "Drag blocks here"
10844
- }
10845
- ) : /* @__PURE__ */ jsxRuntime.jsxs(sortable.SortableContext, { items: children.map((c) => c.id), strategy: sortable.verticalListSortingStrategy, children: [
10846
- children.map((child, idx) => /* @__PURE__ */ jsxRuntime.jsx(
10847
- BlockRow,
10848
- {
10849
- builderBlock: child,
10850
- previewHooks,
10851
- previewTheme,
10852
- errors: errorsByBlockId?.get(child.id),
10853
- isOpen: openBlockId === child.id,
10854
- onOpenChange: (open) => onOpenBlockChange?.(open ? child.id : null),
10855
- onUpdate,
10856
- onDuplicate,
10857
- onDelete,
10858
- index: idx,
10859
- total: children.length,
10860
- onReorder,
10861
- isPaletteDrag
10862
- },
10863
- child.id
10864
- )),
10865
- isPaletteDrag && isBodyOver ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator, {}) : null
10866
- ] })
10867
- }
10868
- )
10869
- ]
10892
+ builderBlock: child,
10893
+ previewHooks,
10894
+ previewTheme,
10895
+ errors: errorsByBlockId?.get(child.id),
10896
+ isOpen: openBlockId === child.id,
10897
+ onOpenChange: (open) => onOpenBlockChange?.(open ? child.id : null),
10898
+ onUpdate,
10899
+ onDuplicate,
10900
+ onDelete,
10901
+ index: idx,
10902
+ total: children.length,
10903
+ onReorder,
10904
+ isPaletteDrag,
10905
+ position: typeof index === "number" ? { index, childIndex: idx } : void 0,
10906
+ blockActions,
10907
+ renderBlockAdornment
10908
+ },
10909
+ child.id
10910
+ )),
10911
+ isPaletteDrag && isBodyOver ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator, {}) : null
10912
+ ] })
10870
10913
  }
10871
- ),
10914
+ )
10915
+ ]
10916
+ }
10917
+ );
10918
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10919
+ "div",
10920
+ {
10921
+ ref: setNodeRef,
10922
+ style,
10923
+ "data-bk-block-index": index,
10924
+ className: cn("group/container relative mb-2 last:mb-0 hover:z-10", isDragging && "opacity-40"),
10925
+ children: [
10926
+ renderBlockAdornment ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
10927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: card }),
10928
+ adornment != null && adornment !== false ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: adornment }) : null
10929
+ ] }) : card,
10872
10930
  /* @__PURE__ */ jsxRuntime.jsxs(
10873
10931
  "div",
10874
10932
  {
@@ -10901,6 +10959,7 @@ function ContainerRow({
10901
10959
  /* @__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
10960
  /* @__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
10961
  ] }) : null,
10962
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10904
10963
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Edit", onClick: () => onOpenBlockChange?.(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }) }),
10905
10964
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Duplicate", onClick: () => onDuplicate(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" }) }),
10906
10965
  /* @__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 +10969,7 @@ function ContainerRow({
10910
10969
  /* @__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
10970
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] font-medium text-muted-foreground", children: "Container" }),
10912
10971
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
10972
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "inline" }),
10913
10973
  /* @__PURE__ */ jsxRuntime.jsx(
10914
10974
  "button",
10915
10975
  {
@@ -10991,7 +11051,9 @@ function Surface({
10991
11051
  onDelete,
10992
11052
  onReorder,
10993
11053
  isPaletteDrag = false,
10994
- onOpenPalette
11054
+ onOpenPalette,
11055
+ blockActions,
11056
+ renderBlockAdornment
10995
11057
  }) {
10996
11058
  const { setNodeRef, isOver } = core.useDroppable({ id: SURFACE_DROPPABLE_ID });
10997
11059
  const isDark = previewTheme === "dark";
@@ -11023,7 +11085,9 @@ function Surface({
11023
11085
  index: idx,
11024
11086
  total: blocks.length,
11025
11087
  onReorder,
11026
- isPaletteDrag
11088
+ isPaletteDrag,
11089
+ blockActions,
11090
+ renderBlockAdornment
11027
11091
  },
11028
11092
  block.id
11029
11093
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -11041,7 +11105,10 @@ function Surface({
11041
11105
  index: idx,
11042
11106
  total: blocks.length,
11043
11107
  onReorder,
11044
- isPaletteDrag
11108
+ isPaletteDrag,
11109
+ position: { index: idx, childIndex: null },
11110
+ blockActions,
11111
+ renderBlockAdornment
11045
11112
  },
11046
11113
  block.id
11047
11114
  )
@@ -11799,6 +11866,8 @@ function BlockKitchen(props) {
11799
11866
  onChange,
11800
11867
  onValidationChange,
11801
11868
  previewHooks,
11869
+ blockActions,
11870
+ renderBlockAdornment,
11802
11871
  customEmojis,
11803
11872
  mentions,
11804
11873
  loadChannels,
@@ -12179,7 +12248,9 @@ function BlockKitchen(props) {
12179
12248
  onDelete: removeBlock,
12180
12249
  onReorder: reorderBlock,
12181
12250
  isPaletteDrag: activePaletteVariant !== null,
12182
- onOpenPalette: () => setPaletteOpen(true)
12251
+ onOpenPalette: () => setPaletteOpen(true),
12252
+ blockActions,
12253
+ renderBlockAdornment
12183
12254
  }
12184
12255
  ) })
12185
12256
  ] })