@tightknitai/block-kitchen 0.10.23 → 0.10.25

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/dist/index.cjs CHANGED
@@ -1817,6 +1817,35 @@ function labelForBlockType(type) {
1817
1817
  return "Data Table";
1818
1818
  }
1819
1819
  }
1820
+ function historyShortcutOf(event) {
1821
+ if (!(event.metaKey || event.ctrlKey) || event.altKey) {
1822
+ return null;
1823
+ }
1824
+ const key = event.key.toLowerCase();
1825
+ if (key === "z") {
1826
+ return event.shiftKey ? "redo" : "undo";
1827
+ }
1828
+ return key === "y" ? "redo" : null;
1829
+ }
1830
+ function isTextEditingTarget(target) {
1831
+ if (!(target instanceof HTMLElement)) {
1832
+ return false;
1833
+ }
1834
+ return target.isContentEditable || // jsdom has no `isContentEditable`, so check the attribute too.
1835
+ target.closest('[contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"]') !== null || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
1836
+ }
1837
+ function isApplePlatform() {
1838
+ if (typeof navigator === "undefined") {
1839
+ return false;
1840
+ }
1841
+ const { userAgentData } = navigator;
1842
+ return /mac|iphone|ipad|ipod/i.test(userAgentData?.platform ?? navigator.platform ?? "");
1843
+ }
1844
+ var subscribeToNothing = () => () => {
1845
+ };
1846
+ function useIsApplePlatform() {
1847
+ return React.useSyncExternalStore(subscribeToNothing, isApplePlatform, () => false);
1848
+ }
1820
1849
  var Sheet = DialogPrimitive__namespace.Root;
1821
1850
  var SheetOverlay = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1822
1851
  DialogPrimitive__namespace.Overlay,
@@ -1898,6 +1927,16 @@ function useIsMobile() {
1898
1927
  }, []);
1899
1928
  return isMobile;
1900
1929
  }
1930
+ var BlockEditorsContext = React.createContext([]);
1931
+ function BlockEditorsProvider({
1932
+ blockEditors,
1933
+ children
1934
+ }) {
1935
+ return /* @__PURE__ */ jsxRuntime.jsx(BlockEditorsContext.Provider, { value: blockEditors ?? [], children });
1936
+ }
1937
+ function useCustomBlockEditor(block) {
1938
+ return React.useContext(BlockEditorsContext).find((editor) => editor.match(block));
1939
+ }
1901
1940
  var EMPTY = {
1902
1941
  customEmojis: [],
1903
1942
  byName: /* @__PURE__ */ new Map()
@@ -4239,6 +4278,7 @@ function Palette({
4239
4278
  mode = "advanced",
4240
4279
  defaultAdvanced = false,
4241
4280
  onAdvancedChange,
4281
+ onLockedPicked,
4242
4282
  variant = "aside"
4243
4283
  }) {
4244
4284
  const [query, setQuery] = React.useState("");
@@ -4332,7 +4372,16 @@ function Palette({
4332
4372
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", isSheet ? "px-2 pb-6" : "p-3"), children: !advanced ? (
4333
4373
  // Simple mode: a flat list, no section headings — with this few
4334
4374
  // rows the headings would outnumber the blocks under them.
4335
- basics.map((v) => /* @__PURE__ */ jsxRuntime.jsx(PaletteItem, { variant: v, onAdd: () => onAddBlock(v.factory()), mode: variant }, v.id))
4375
+ basics.map((v) => /* @__PURE__ */ jsxRuntime.jsx(
4376
+ PaletteItem,
4377
+ {
4378
+ variant: v,
4379
+ onAdd: () => onAddBlock(v.factory()),
4380
+ onLockedPicked,
4381
+ mode: variant
4382
+ },
4383
+ v.id
4384
+ ))
4336
4385
  ) : visibleSections.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-1 py-2 text-xs text-muted-foreground", children: "No blocks match." }) : visibleSections.map((section) => /* @__PURE__ */ jsxRuntime.jsx(
4337
4386
  PaletteSection,
4338
4387
  {
@@ -4340,6 +4389,7 @@ function Palette({
4340
4389
  defaultOpen: isDefaultOpen(section.name, defaultOpenSections),
4341
4390
  forceOpen: queryActive,
4342
4391
  onAddBlock,
4392
+ onLockedPicked,
4343
4393
  variant
4344
4394
  },
4345
4395
  section.name
@@ -4371,6 +4421,7 @@ function PaletteSection({
4371
4421
  defaultOpen,
4372
4422
  forceOpen,
4373
4423
  onAddBlock,
4424
+ onLockedPicked,
4374
4425
  variant = "aside"
4375
4426
  }) {
4376
4427
  const [open, setOpen] = React.useState(defaultOpen);
@@ -4402,18 +4453,32 @@ function PaletteSection({
4402
4453
  ]
4403
4454
  }
4404
4455
  ),
4405
- isOpen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex flex-col", children: section.variants.map((v) => /* @__PURE__ */ jsxRuntime.jsx(PaletteItem, { variant: v, onAdd: () => onAddBlock(v.factory()), mode: variant }, v.id)) }) : null
4456
+ isOpen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex flex-col", children: section.variants.map((v) => /* @__PURE__ */ jsxRuntime.jsx(
4457
+ PaletteItem,
4458
+ {
4459
+ variant: v,
4460
+ onAdd: () => onAddBlock(v.factory()),
4461
+ onLockedPicked,
4462
+ mode: variant
4463
+ },
4464
+ v.id
4465
+ )) }) : null
4406
4466
  ] });
4407
4467
  }
4408
4468
  function PaletteItem({
4409
4469
  variant,
4410
4470
  onAdd,
4471
+ onLockedPicked,
4411
4472
  mode = "aside"
4412
4473
  }) {
4413
4474
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, isDragging } = core.useDraggable({
4414
- id: `${PALETTE_DRAG_PREFIX}${variant.id}`
4475
+ id: `${PALETTE_DRAG_PREFIX}${variant.id}`,
4476
+ disabled: variant.locked
4415
4477
  });
4416
4478
  const isSheet = mode === "sheet";
4479
+ if (variant.locked) {
4480
+ return /* @__PURE__ */ jsxRuntime.jsx(LockedPaletteItem, { variant, isSheet, onPicked: onLockedPicked });
4481
+ }
4417
4482
  if (isSheet) {
4418
4483
  return /* @__PURE__ */ jsxRuntime.jsxs(
4419
4484
  "button",
@@ -4428,6 +4493,7 @@ function PaletteItem({
4428
4493
  ),
4429
4494
  children: [
4430
4495
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: variant.label }),
4496
+ variant.badge ? /* @__PURE__ */ jsxRuntime.jsx(VariantBadge, { badge: variant.badge }) : null,
4431
4497
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground transition-colors group-hover:bg-primary/10 group-hover:text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" }) })
4432
4498
  ]
4433
4499
  }
@@ -4451,7 +4517,11 @@ function PaletteItem({
4451
4517
  ...listeners,
4452
4518
  children: [
4453
4519
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-3.5 w-3.5 shrink-0" }),
4454
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: variant.label })
4520
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: variant.label }),
4521
+ variant.badge ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4522
+ " ",
4523
+ /* @__PURE__ */ jsxRuntime.jsx(VariantBadge, { badge: variant.badge })
4524
+ ] }) : null
4455
4525
  ]
4456
4526
  }
4457
4527
  ),
@@ -4469,6 +4539,48 @@ function PaletteItem({
4469
4539
  }
4470
4540
  );
4471
4541
  }
4542
+ function LockedPaletteItem({
4543
+ variant,
4544
+ isSheet,
4545
+ onPicked
4546
+ }) {
4547
+ const select = variant.onLockedSelect;
4548
+ const className = cn(
4549
+ "flex w-full items-center text-left text-muted-foreground",
4550
+ isSheet ? "min-h-11 gap-3 rounded-md px-3 py-2 text-sm font-medium" : "gap-2 rounded px-2 py-1 text-sm"
4551
+ );
4552
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4553
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { "aria-hidden": true, className: cn("shrink-0", isSheet ? "h-4 w-4" : "h-3.5 w-3.5") }),
4554
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: variant.label }),
4555
+ variant.badge ? /* @__PURE__ */ jsxRuntime.jsx(VariantBadge, { badge: variant.badge }) : null
4556
+ ] });
4557
+ if (!select) {
4558
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-disabled": "true", className, children: content });
4559
+ }
4560
+ return /* @__PURE__ */ jsxRuntime.jsx(
4561
+ "button",
4562
+ {
4563
+ type: "button",
4564
+ "aria-label": `${variant.label} (locked${variant.badge ? `, ${variant.badge.label}` : ""})`,
4565
+ onClick: () => {
4566
+ onPicked?.();
4567
+ select();
4568
+ },
4569
+ className: cn(
4570
+ className,
4571
+ "cursor-pointer appearance-none border-0 bg-transparent transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
4572
+ ),
4573
+ children: content
4574
+ }
4575
+ );
4576
+ }
4577
+ function VariantBadge({ badge }) {
4578
+ const Icon = badge.icon;
4579
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 rounded-full border bg-background px-1.5 text-[10px] font-medium leading-4 text-muted-foreground", children: [
4580
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "h-2.5 w-2.5 shrink-0" }) : null,
4581
+ badge.label
4582
+ ] });
4583
+ }
4472
4584
  function SendDialog({
4473
4585
  open,
4474
4586
  onOpenChange,
@@ -6288,7 +6400,7 @@ function MentionPicker({
6288
6400
  const listRef = React.useRef(null);
6289
6401
  const listboxId = React.useId();
6290
6402
  const activeOptionId = items[activeIndex] ? `${listboxId}-${activeIndex}` : void 0;
6291
- React.useEffect(() => {
6403
+ React.useLayoutEffect(() => {
6292
6404
  const root = editor.view.dom;
6293
6405
  root.setAttribute("role", "combobox");
6294
6406
  root.setAttribute("aria-autocomplete", "list");
@@ -6307,7 +6419,7 @@ function MentionPicker({
6307
6419
  root.removeAttribute("aria-activedescendant");
6308
6420
  };
6309
6421
  }, [activeOptionId, editor, listboxId]);
6310
- React.useEffect(() => {
6422
+ React.useLayoutEffect(() => {
6311
6423
  keyHandlerRef.current = (event) => {
6312
6424
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
6313
6425
  if (items.length > 0) {
@@ -10397,13 +10509,15 @@ function BlockEditor({
10397
10509
  onChange,
10398
10510
  onDone
10399
10511
  }) {
10512
+ const custom = useCustomBlockEditor(block);
10513
+ const CustomForm = custom?.component;
10400
10514
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
10401
10515
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-sm font-semibold text-foreground", children: [
10402
10516
  "Edit ",
10403
- labelForBlockType(block.type)
10517
+ custom ? custom.label : labelForBlockType(block.type)
10404
10518
  ] }) }),
10405
10519
  errors && errors.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-0.5 rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: errors.map((err, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: err }, i)) }) : null,
10406
- dispatch(block, onChange),
10520
+ CustomForm ? /* @__PURE__ */ jsxRuntime.jsx(CustomForm, { block, onChange }) : dispatch(block, onChange),
10407
10521
  /* @__PURE__ */ jsxRuntime.jsx(
10408
10522
  AdvancedIdField,
10409
10523
  {
@@ -10501,7 +10615,8 @@ function BlockRow({
10501
10615
  onReorder,
10502
10616
  isPaletteDrag = false,
10503
10617
  position,
10504
- blockActions
10618
+ blockActions,
10619
+ renderBlockAdornment
10505
10620
  }) {
10506
10621
  const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver } = sortable.useSortable({
10507
10622
  id: builderBlock.id
@@ -10516,6 +10631,179 @@ function BlockRow({
10516
10631
  const [inlineEditing, setInlineEditing] = React.useState(false);
10517
10632
  const showDropIndicator = isPaletteDrag && isOver;
10518
10633
  const preview = /* @__PURE__ */ jsxRuntime.jsx(SlackBlockPreview, { block: builderBlock.block, hooks: previewHooks, theme: previewTheme });
10634
+ const body = isRichText && inlineEditing ? /* @__PURE__ */ jsxRuntime.jsx(
10635
+ RichTextInlineEditor,
10636
+ {
10637
+ block: builderBlock.block,
10638
+ onSave: (next) => {
10639
+ onUpdate(builderBlock.id, next);
10640
+ setInlineEditing(false);
10641
+ },
10642
+ onCancel: () => setInlineEditing(false)
10643
+ }
10644
+ ) : isRichText ? /* @__PURE__ */ jsxRuntime.jsx(
10645
+ "button",
10646
+ {
10647
+ type: "button",
10648
+ "aria-label": "Edit block",
10649
+ onClick: () => setInlineEditing(true),
10650
+ ...attributes,
10651
+ ...listeners,
10652
+ className: cn(
10653
+ "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",
10654
+ hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10655
+ ),
10656
+ children: preview
10657
+ }
10658
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange, children: [
10659
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10660
+ "div",
10661
+ {
10662
+ role: "button",
10663
+ tabIndex: 0,
10664
+ "aria-label": "Edit block",
10665
+ ...sortableA11yAttrs,
10666
+ ...listeners,
10667
+ onKeyDown: (e) => {
10668
+ if (e.key === "Enter" || e.key === " ") {
10669
+ e.preventDefault();
10670
+ onOpenChange?.(!isOpen);
10671
+ }
10672
+ },
10673
+ className: cn(
10674
+ "block w-full cursor-grab rounded-sm transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10675
+ hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10676
+ ),
10677
+ children: preview
10678
+ }
10679
+ ) }),
10680
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10681
+ BlockEditor,
10682
+ {
10683
+ block: builderBlock.block,
10684
+ errors,
10685
+ onChange: (next) => onUpdate(builderBlock.id, next),
10686
+ onDone: () => onOpenChange?.(false)
10687
+ }
10688
+ ) })
10689
+ ] });
10690
+ const adornment = position && renderBlockAdornment?.(position);
10691
+ const hoverChrome = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10692
+ /* @__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] }),
10693
+ /* @__PURE__ */ jsxRuntime.jsxs(
10694
+ "div",
10695
+ {
10696
+ className: cn(
10697
+ // Hidden on mobile in favor of the inline row below; on desktop
10698
+ // reveal on hover, on keyboard focus inside the row (so Tab-stops on
10699
+ // the floating buttons themselves keep them visible), and when there
10700
+ // are validation errors worth surfacing immediately.
10701
+ // :focus-visible, not :focus-within — a drag leaves pointer focus on
10702
+ // the block, which would otherwise pin the toolbar open until a click.
10703
+ "absolute -top-3 right-2 z-10 hidden items-center gap-0.5 rounded-md border bg-background p-0.5 shadow-sm transition-opacity md:flex",
10704
+ hasErrors ? "opacity-100" : "opacity-0 group-hover:opacity-100 group-has-[:focus-visible]:opacity-100"
10705
+ ),
10706
+ children: [
10707
+ hasErrors ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10708
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10709
+ "button",
10710
+ {
10711
+ type: "button",
10712
+ "aria-label": `Show ${errors.length} validation ${errors.length === 1 ? "issue" : "issues"}`,
10713
+ onClick: () => onOpenChange?.(true),
10714
+ className: "flex h-6 w-6 items-center justify-center rounded text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10715
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" })
10716
+ }
10717
+ ) }),
10718
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", align: "end", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex flex-col gap-0.5", children: [
10719
+ errors.slice(0, 4).map((err, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: err }, i)),
10720
+ errors.length > 4 ? /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-muted-foreground", children: [
10721
+ "and ",
10722
+ errors.length - 4,
10723
+ " more"
10724
+ ] }) : null
10725
+ ] }) })
10726
+ ] }) : null,
10727
+ onReorder && typeof index === "number" && typeof total === "number" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10728
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10729
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10730
+ "button",
10731
+ {
10732
+ type: "button",
10733
+ "aria-label": "Move block up",
10734
+ disabled: index === 0,
10735
+ onClick: () => onReorder(builderBlock.id, index - 1),
10736
+ 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 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground",
10737
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3.5 w-3.5" })
10738
+ }
10739
+ ) }),
10740
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move up" })
10741
+ ] }),
10742
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10743
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10744
+ "button",
10745
+ {
10746
+ type: "button",
10747
+ "aria-label": "Move block down",
10748
+ disabled: index >= total - 1,
10749
+ onClick: () => onReorder(builderBlock.id, index + 1),
10750
+ 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 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground",
10751
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-3.5 w-3.5" })
10752
+ }
10753
+ ) }),
10754
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move down" })
10755
+ ] })
10756
+ ] }) : null,
10757
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10758
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10759
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10760
+ "button",
10761
+ {
10762
+ type: "button",
10763
+ "aria-label": "Edit block",
10764
+ onClick: () => {
10765
+ if (isRichText) {
10766
+ setInlineEditing(true);
10767
+ } else {
10768
+ onOpenChange?.(true);
10769
+ }
10770
+ },
10771
+ 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",
10772
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" })
10773
+ }
10774
+ ) }),
10775
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Edit" })
10776
+ ] }),
10777
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10778
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10779
+ "button",
10780
+ {
10781
+ type: "button",
10782
+ "aria-label": "Duplicate block",
10783
+ onClick: () => onDuplicate(builderBlock.id),
10784
+ 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",
10785
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" })
10786
+ }
10787
+ ) }),
10788
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Duplicate" })
10789
+ ] }),
10790
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10791
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10792
+ "button",
10793
+ {
10794
+ type: "button",
10795
+ "aria-label": "Delete block",
10796
+ onClick: () => onDelete(builderBlock.id),
10797
+ className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10798
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
10799
+ }
10800
+ ) }),
10801
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Delete" })
10802
+ ] })
10803
+ ]
10804
+ }
10805
+ )
10806
+ ] });
10519
10807
  return /* @__PURE__ */ jsxRuntime.jsxs(
10520
10808
  "div",
10521
10809
  {
@@ -10533,178 +10821,16 @@ function BlockRow({
10533
10821
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "-left-1 -top-[3px] absolute h-2 w-2 rounded-full bg-primary shadow-[0_0_0_2px_var(--color-background)]" })
10534
10822
  }
10535
10823
  ) : null,
10536
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
10537
- isRichText && inlineEditing ? /* @__PURE__ */ jsxRuntime.jsx(
10538
- RichTextInlineEditor,
10539
- {
10540
- block: builderBlock.block,
10541
- onSave: (next) => {
10542
- onUpdate(builderBlock.id, next);
10543
- setInlineEditing(false);
10544
- },
10545
- onCancel: () => setInlineEditing(false)
10546
- }
10547
- ) : isRichText ? /* @__PURE__ */ jsxRuntime.jsx(
10548
- "button",
10549
- {
10550
- type: "button",
10551
- "aria-label": "Edit block",
10552
- onClick: () => setInlineEditing(true),
10553
- ...attributes,
10554
- ...listeners,
10555
- className: cn(
10556
- "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",
10557
- hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10558
- ),
10559
- children: preview
10560
- }
10561
- ) : /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange, children: [
10562
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10563
- "div",
10564
- {
10565
- role: "button",
10566
- tabIndex: 0,
10567
- "aria-label": "Edit block",
10568
- ...sortableA11yAttrs,
10569
- ...listeners,
10570
- onKeyDown: (e) => {
10571
- if (e.key === "Enter" || e.key === " ") {
10572
- e.preventDefault();
10573
- onOpenChange?.(!isOpen);
10574
- }
10575
- },
10576
- className: cn(
10577
- "block w-full cursor-grab rounded-sm transition-shadow hover:shadow-md focus-visible:ring-1 focus-visible:ring-ring active:cursor-grabbing",
10578
- hasErrors ? "ring-1 ring-destructive/60 hover:ring-destructive" : "hover:ring-1 hover:ring-border"
10579
- ),
10580
- children: preview
10581
- }
10582
- ) }),
10583
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10584
- BlockEditor,
10585
- {
10586
- block: builderBlock.block,
10587
- errors,
10588
- onChange: (next) => onUpdate(builderBlock.id, next),
10589
- onDone: () => onOpenChange?.(false)
10590
- }
10591
- ) })
10824
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", children: renderBlockAdornment ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
10825
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-w-0 flex-1", children: [
10826
+ body,
10827
+ hoverChrome
10592
10828
  ] }),
10593
- /* @__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] }),
10594
- /* @__PURE__ */ jsxRuntime.jsxs(
10595
- "div",
10596
- {
10597
- className: cn(
10598
- // Hidden on mobile in favor of the inline row below; on desktop
10599
- // reveal on hover, on keyboard focus inside the row (so Tab-stops on
10600
- // the floating buttons themselves keep them visible), and when there
10601
- // are validation errors worth surfacing immediately.
10602
- // :focus-visible, not :focus-within — a drag leaves pointer focus on
10603
- // the block, which would otherwise pin the toolbar open until a click.
10604
- "absolute -top-3 right-2 z-10 hidden items-center gap-0.5 rounded-md border bg-background p-0.5 shadow-sm transition-opacity md:flex",
10605
- hasErrors ? "opacity-100" : "opacity-0 group-hover:opacity-100 group-has-[:focus-visible]:opacity-100"
10606
- ),
10607
- children: [
10608
- hasErrors ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10609
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10610
- "button",
10611
- {
10612
- type: "button",
10613
- "aria-label": `Show ${errors.length} validation ${errors.length === 1 ? "issue" : "issues"}`,
10614
- onClick: () => onOpenChange?.(true),
10615
- className: "flex h-6 w-6 items-center justify-center rounded text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10616
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" })
10617
- }
10618
- ) }),
10619
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", align: "end", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex flex-col gap-0.5", children: [
10620
- errors.slice(0, 4).map((err, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: err }, i)),
10621
- errors.length > 4 ? /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-muted-foreground", children: [
10622
- "and ",
10623
- errors.length - 4,
10624
- " more"
10625
- ] }) : null
10626
- ] }) })
10627
- ] }) : null,
10628
- onReorder && typeof index === "number" && typeof total === "number" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10629
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10630
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10631
- "button",
10632
- {
10633
- type: "button",
10634
- "aria-label": "Move block up",
10635
- disabled: index === 0,
10636
- onClick: () => onReorder(builderBlock.id, index - 1),
10637
- 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 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground",
10638
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3.5 w-3.5" })
10639
- }
10640
- ) }),
10641
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move up" })
10642
- ] }),
10643
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10644
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10645
- "button",
10646
- {
10647
- type: "button",
10648
- "aria-label": "Move block down",
10649
- disabled: index >= total - 1,
10650
- onClick: () => onReorder(builderBlock.id, index + 1),
10651
- 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 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground",
10652
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-3.5 w-3.5" })
10653
- }
10654
- ) }),
10655
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Move down" })
10656
- ] })
10657
- ] }) : null,
10658
- /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10659
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10660
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10661
- "button",
10662
- {
10663
- type: "button",
10664
- "aria-label": "Edit block",
10665
- onClick: () => {
10666
- if (isRichText) {
10667
- setInlineEditing(true);
10668
- } else {
10669
- onOpenChange?.(true);
10670
- }
10671
- },
10672
- 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",
10673
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" })
10674
- }
10675
- ) }),
10676
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Edit" })
10677
- ] }),
10678
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10679
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10680
- "button",
10681
- {
10682
- type: "button",
10683
- "aria-label": "Duplicate block",
10684
- onClick: () => onDuplicate(builderBlock.id),
10685
- 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",
10686
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" })
10687
- }
10688
- ) }),
10689
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Duplicate" })
10690
- ] }),
10691
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10692
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10693
- "button",
10694
- {
10695
- type: "button",
10696
- "aria-label": "Delete block",
10697
- onClick: () => onDelete(builderBlock.id),
10698
- className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10699
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
10700
- }
10701
- ) }),
10702
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: "Delete" })
10703
- ] })
10704
- ]
10705
- }
10706
- )
10707
- ] }),
10829
+ adornment != null && adornment !== false ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: adornment }) : null
10830
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10831
+ body,
10832
+ hoverChrome
10833
+ ] }) }),
10708
10834
  /* @__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: [
10709
10835
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] font-medium text-muted-foreground", children: BLOCK_TYPE_LABELS[builderBlock.block.type] }),
10710
10836
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
@@ -10801,7 +10927,8 @@ function ContainerRow({
10801
10927
  index,
10802
10928
  total,
10803
10929
  isPaletteDrag = false,
10804
- blockActions
10930
+ blockActions,
10931
+ renderBlockAdornment
10805
10932
  }) {
10806
10933
  const { id } = builderBlock;
10807
10934
  const block = builderBlock.block;
@@ -10815,144 +10942,156 @@ function ContainerRow({
10815
10942
  const style = { transform: utilities.CSS.Transform.toString(transform), transition };
10816
10943
  const icon = block.icon && "image_url" in block.icon ? block.icon.image_url : void 0;
10817
10944
  const position = typeof index === "number" ? { index, childIndex: null } : void 0;
10818
- return /* @__PURE__ */ jsxRuntime.jsxs(
10945
+ const adornment = position && renderBlockAdornment?.(position);
10946
+ const card = /* @__PURE__ */ jsxRuntime.jsxs(
10819
10947
  "div",
10820
10948
  {
10821
- ref: setNodeRef,
10822
- style,
10823
- "data-bk-block-index": index,
10824
- className: cn("group/container relative mb-2 last:mb-0 hover:z-10", isDragging && "opacity-40"),
10949
+ className: cn(
10950
+ "overflow-hidden rounded-md border bg-card transition-shadow",
10951
+ hasErrors ? "border-destructive/60" : "border-border"
10952
+ ),
10825
10953
  children: [
10826
- /* @__PURE__ */ jsxRuntime.jsxs(
10827
- "div",
10828
- {
10829
- className: cn(
10830
- "overflow-hidden rounded-md border bg-card transition-shadow",
10831
- hasErrors ? "border-destructive/60" : "border-border"
10832
- ),
10833
- children: [
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(
10871
- "div",
10872
- {
10873
- ref: setBodyRef,
10874
- className: cn(
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"
10878
- ),
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,
10891
- {
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
- },
10908
- child.id
10909
- )),
10910
- isPaletteDrag && isBodyOver ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator, {}) : null
10911
- ] })
10954
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: (open) => onOpenBlockChange?.(open ? id : null), children: [
10955
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10956
+ "div",
10957
+ {
10958
+ role: "button",
10959
+ tabIndex: 0,
10960
+ "aria-label": "Edit container",
10961
+ ...sortableA11yAttrs,
10962
+ ...listeners,
10963
+ onKeyDown: (e) => {
10964
+ if (e.key === "Enter" || e.key === " ") {
10965
+ e.preventDefault();
10966
+ onOpenBlockChange?.(!isOpen ? id : null);
10912
10967
  }
10913
- )
10914
- ]
10915
- }
10916
- ),
10917
- /* @__PURE__ */ jsxRuntime.jsxs(
10968
+ },
10969
+ 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",
10970
+ children: [
10971
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: icon, alt: "", "aria-hidden": "true", className: "h-5 w-5 shrink-0 rounded object-cover" }) : null,
10972
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
10973
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs font-semibold text-foreground", children: containerTitleText(block) || "Container" }),
10974
+ block.subtitle?.text ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: block.subtitle.text }) : null
10975
+ ] }),
10976
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Container" })
10977
+ ]
10978
+ }
10979
+ ) }),
10980
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10981
+ BlockEditor,
10982
+ {
10983
+ block,
10984
+ errors,
10985
+ onChange: (next) => onUpdate(id, next),
10986
+ onDone: () => onOpenBlockChange?.(null)
10987
+ }
10988
+ ) })
10989
+ ] }),
10990
+ /* @__PURE__ */ jsxRuntime.jsx(
10918
10991
  "div",
10919
10992
  {
10993
+ ref: setBodyRef,
10920
10994
  className: cn(
10921
- "absolute -top-3 right-2 z-10 hidden items-center gap-0.5 rounded-md border bg-background p-0.5 shadow-sm transition-opacity md:flex",
10922
- hasErrors ? "opacity-100" : "opacity-0 group-hover/container:opacity-100 group-has-[:focus-visible]/container:opacity-100"
10995
+ "flex flex-col gap-2 p-3 transition-colors",
10996
+ isPaletteDrag && "outline-2 -outline-offset-2 outline-dashed outline-primary/40",
10997
+ isPaletteDrag && isBodyOver && "bg-primary/5"
10923
10998
  ),
10924
- children: [
10925
- hasErrors ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
10926
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
10927
- "button",
10928
- {
10929
- type: "button",
10930
- "aria-label": `Show ${errors.length} validation ${errors.length === 1 ? "issue" : "issues"}`,
10931
- onClick: () => onOpenBlockChange?.(id),
10932
- className: "flex h-6 w-6 items-center justify-center rounded text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10933
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" })
10934
- }
10935
- ) }),
10936
- /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", align: "end", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex flex-col gap-0.5", children: [
10937
- errors.slice(0, 4).map((err, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: err }, i)),
10938
- errors.length > 4 ? /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-muted-foreground", children: [
10939
- "and ",
10940
- errors.length - 4,
10941
- " more"
10942
- ] }) : null
10943
- ] }) })
10944
- ] }) : null,
10945
- onReorder && typeof index === "number" && typeof total === "number" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
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" }) }),
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" }) })
10948
- ] }) : null,
10949
- /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
10950
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Edit", onClick: () => onOpenBlockChange?.(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }) }),
10951
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Duplicate", onClick: () => onDuplicate(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" }) }),
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" }) })
10953
- ]
10999
+ children: children.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
11000
+ "div",
11001
+ {
11002
+ className: cn(
11003
+ "flex items-center justify-center rounded-md border border-dashed px-3 py-6 text-center text-[11px] transition-colors",
11004
+ isBodyOver ? "border-primary/60 text-primary" : "border-border text-muted-foreground"
11005
+ ),
11006
+ children: "Drag blocks here"
11007
+ }
11008
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(sortable.SortableContext, { items: children.map((c) => c.id), strategy: sortable.verticalListSortingStrategy, children: [
11009
+ children.map((child, idx) => /* @__PURE__ */ jsxRuntime.jsx(
11010
+ BlockRow,
11011
+ {
11012
+ builderBlock: child,
11013
+ previewHooks,
11014
+ previewTheme,
11015
+ errors: errorsByBlockId?.get(child.id),
11016
+ isOpen: openBlockId === child.id,
11017
+ onOpenChange: (open) => onOpenBlockChange?.(open ? child.id : null),
11018
+ onUpdate,
11019
+ onDuplicate,
11020
+ onDelete,
11021
+ index: idx,
11022
+ total: children.length,
11023
+ onReorder,
11024
+ isPaletteDrag,
11025
+ position: typeof index === "number" ? { index, childIndex: idx } : void 0,
11026
+ blockActions,
11027
+ renderBlockAdornment
11028
+ },
11029
+ child.id
11030
+ )),
11031
+ isPaletteDrag && isBodyOver ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator, {}) : null
11032
+ ] })
10954
11033
  }
10955
- ),
11034
+ )
11035
+ ]
11036
+ }
11037
+ );
11038
+ const toolbar = /* @__PURE__ */ jsxRuntime.jsxs(
11039
+ "div",
11040
+ {
11041
+ className: cn(
11042
+ "absolute -top-3 right-2 z-10 hidden items-center gap-0.5 rounded-md border bg-background p-0.5 shadow-sm transition-opacity md:flex",
11043
+ hasErrors ? "opacity-100" : "opacity-0 group-hover/container:opacity-100 group-has-[:focus-visible]/container:opacity-100"
11044
+ ),
11045
+ children: [
11046
+ hasErrors ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
11047
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
11048
+ "button",
11049
+ {
11050
+ type: "button",
11051
+ "aria-label": `Show ${errors.length} validation ${errors.length === 1 ? "issue" : "issues"}`,
11052
+ onClick: () => onOpenBlockChange?.(id),
11053
+ className: "flex h-6 w-6 items-center justify-center rounded text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
11054
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" })
11055
+ }
11056
+ ) }),
11057
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", align: "end", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex flex-col gap-0.5", children: [
11058
+ errors.slice(0, 4).map((err, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: err }, i)),
11059
+ errors.length > 4 ? /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-muted-foreground", children: [
11060
+ "and ",
11061
+ errors.length - 4,
11062
+ " more"
11063
+ ] }) : null
11064
+ ] }) })
11065
+ ] }) : null,
11066
+ onReorder && typeof index === "number" && typeof total === "number" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11067
+ /* @__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" }) }),
11068
+ /* @__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" }) })
11069
+ ] }) : null,
11070
+ /* @__PURE__ */ jsxRuntime.jsx(BlockActionButtons, { actions: blockActions, position, variant: "toolbar" }),
11071
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Edit", onClick: () => onOpenBlockChange?.(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }) }),
11072
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Duplicate", onClick: () => onDuplicate(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" }) }),
11073
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Delete", destructive: true, onClick: () => onDelete(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }) })
11074
+ ]
11075
+ }
11076
+ );
11077
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11078
+ "div",
11079
+ {
11080
+ ref: setNodeRef,
11081
+ style,
11082
+ "data-bk-block-index": index,
11083
+ className: cn("group/container relative mb-2 last:mb-0 hover:z-10", isDragging && "opacity-40"),
11084
+ children: [
11085
+ renderBlockAdornment ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
11086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-w-0 flex-1", children: [
11087
+ card,
11088
+ toolbar
11089
+ ] }),
11090
+ adornment != null && adornment !== false ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: adornment }) : null
11091
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11092
+ card,
11093
+ toolbar
11094
+ ] }),
10956
11095
  /* @__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: [
10957
11096
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] font-medium text-muted-foreground", children: "Container" }),
10958
11097
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
@@ -11039,7 +11178,8 @@ function Surface({
11039
11178
  onReorder,
11040
11179
  isPaletteDrag = false,
11041
11180
  onOpenPalette,
11042
- blockActions
11181
+ blockActions,
11182
+ renderBlockAdornment
11043
11183
  }) {
11044
11184
  const { setNodeRef, isOver } = core.useDroppable({ id: SURFACE_DROPPABLE_ID });
11045
11185
  const isDark = previewTheme === "dark";
@@ -11072,7 +11212,8 @@ function Surface({
11072
11212
  total: blocks.length,
11073
11213
  onReorder,
11074
11214
  isPaletteDrag,
11075
- blockActions
11215
+ blockActions,
11216
+ renderBlockAdornment
11076
11217
  },
11077
11218
  block.id
11078
11219
  ) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -11092,7 +11233,8 @@ function Surface({
11092
11233
  onReorder,
11093
11234
  isPaletteDrag,
11094
11235
  position: { index: idx, childIndex: null },
11095
- blockActions
11236
+ blockActions,
11237
+ renderBlockAdornment
11096
11238
  },
11097
11239
  block.id
11098
11240
  )
@@ -11366,6 +11508,7 @@ function Toolbar2({
11366
11508
  canRedo = false,
11367
11509
  onUndo,
11368
11510
  onRedo,
11511
+ undoRedoShortcuts = true,
11369
11512
  onClear,
11370
11513
  onOpenJson,
11371
11514
  onOpenIssues,
@@ -11399,6 +11542,7 @@ function Toolbar2({
11399
11542
  const showSurfaceControl = surfaceOptions.length > 1;
11400
11543
  const docsHref = docsLink === false ? null : docsLink?.href ?? DEFAULT_DOCS_HREF;
11401
11544
  const docsLabel = docsLink === false ? null : docsLink?.label ?? DEFAULT_DOCS_LABEL;
11545
+ const isApple = useIsApplePlatform();
11402
11546
  const [surfaceMenuOpen, setSurfaceMenuOpen] = React.useState(false);
11403
11547
  const [themeMenuOpen, setThemeMenuOpen] = React.useState(false);
11404
11548
  const [sendMenuOpen, setSendMenuOpen] = React.useState(false);
@@ -11489,7 +11633,8 @@ function Toolbar2({
11489
11633
  onClick: onUndo,
11490
11634
  disabled: !canUndo,
11491
11635
  "aria-label": "Undo",
11492
- title: "Undo (Ctrl+Z)",
11636
+ "aria-keyshortcuts": undoRedoShortcuts ? "Control+Z Meta+Z" : void 0,
11637
+ title: undoRedoShortcuts ? `Undo (${isApple ? "\u2318Z" : "Ctrl+Z"})` : "Undo",
11493
11638
  className: "px-2",
11494
11639
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { className: "h-3.5 w-3.5" })
11495
11640
  }
@@ -11503,7 +11648,8 @@ function Toolbar2({
11503
11648
  onClick: onRedo,
11504
11649
  disabled: !canRedo,
11505
11650
  "aria-label": "Redo",
11506
- title: "Redo (Ctrl+Shift+Z)",
11651
+ "aria-keyshortcuts": undoRedoShortcuts ? "Control+Shift+Z Meta+Shift+Z Control+Y" : void 0,
11652
+ title: undoRedoShortcuts ? `Redo (${isApple ? "\u21E7\u2318Z" : "Ctrl+Shift+Z"})` : "Redo",
11507
11653
  className: "px-2",
11508
11654
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { className: "h-3.5 w-3.5" })
11509
11655
  }
@@ -11851,6 +11997,8 @@ function BlockKitchen(props) {
11851
11997
  onValidationChange,
11852
11998
  previewHooks,
11853
11999
  blockActions,
12000
+ renderBlockAdornment,
12001
+ blockEditors,
11854
12002
  customEmojis,
11855
12003
  mentions,
11856
12004
  loadChannels,
@@ -11876,7 +12024,8 @@ function BlockKitchen(props) {
11876
12024
  previewTheme: controlledPreviewTheme,
11877
12025
  sendButtonLabel,
11878
12026
  confirmSendLabel,
11879
- theme
12027
+ theme,
12028
+ undoRedoShortcuts = true
11880
12029
  // Widen the all-or-nothing union so the branch-specific props
11881
12030
  // destructure as independent optionals: untyped JS consumers can still
11882
12031
  // pass partial wiring (handled by the runtime guards below), and the
@@ -12056,7 +12205,7 @@ function BlockKitchen(props) {
12056
12205
  const variantId = parsePaletteDragId(active.id);
12057
12206
  if (variantId) {
12058
12207
  const variant = variantById.get(variantId);
12059
- if (!variant) {
12208
+ if (!variant || variant.locked) {
12060
12209
  return;
12061
12210
  }
12062
12211
  if (target.kind === "container") {
@@ -12075,30 +12224,47 @@ function BlockKitchen(props) {
12075
12224
  const handleDragCancel = React.useCallback(() => {
12076
12225
  setActivePaletteVariantId(null);
12077
12226
  }, []);
12078
- const handleKeyDown = React.useCallback(
12227
+ const runHistoryShortcut = React.useCallback(
12079
12228
  (event) => {
12080
- if (!(event.metaKey || event.ctrlKey) || event.altKey) {
12229
+ const action = historyShortcutOf(event);
12230
+ if (!undoRedoShortcuts || !action || event.defaultPrevented || isTextEditingTarget(event.target)) {
12081
12231
  return;
12082
12232
  }
12083
- const key = event.key.toLowerCase();
12084
- const isUndo = key === "z" && !event.shiftKey;
12085
- const isRedo = key === "y" || key === "z" && event.shiftKey;
12086
- if (!isUndo && !isRedo) {
12087
- return;
12088
- }
12089
- const target = event.target;
12090
- if (target && (target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName))) {
12233
+ if (action === "undo" ? !canUndo : !canRedo) {
12091
12234
  return;
12092
12235
  }
12093
12236
  event.preventDefault();
12094
- if (isRedo) {
12095
- if (canRedo) redo();
12096
- } else if (canUndo) {
12237
+ if (action === "undo") {
12097
12238
  undo();
12239
+ } else {
12240
+ redo();
12098
12241
  }
12099
12242
  },
12100
- [canRedo, canUndo, redo, undo]
12243
+ [canRedo, canUndo, redo, undo, undoRedoShortcuts]
12101
12244
  );
12245
+ const rootRef = React.useRef(null);
12246
+ const lastClickInBuilderRef = React.useRef(false);
12247
+ React.useEffect(() => {
12248
+ if (!undoRedoShortcuts) {
12249
+ return;
12250
+ }
12251
+ const onPointerDown = () => {
12252
+ lastClickInBuilderRef.current = false;
12253
+ };
12254
+ const onKeyDown = (event) => {
12255
+ const root = rootRef.current;
12256
+ const focusAroundBuilder = event.target instanceof Node && root !== null && event.target.contains(root);
12257
+ if (focusAroundBuilder && lastClickInBuilderRef.current) {
12258
+ runHistoryShortcut(event);
12259
+ }
12260
+ };
12261
+ window.addEventListener("pointerdown", onPointerDown, true);
12262
+ window.addEventListener("keydown", onKeyDown);
12263
+ return () => {
12264
+ window.removeEventListener("pointerdown", onPointerDown, true);
12265
+ window.removeEventListener("keydown", onKeyDown);
12266
+ };
12267
+ }, [runHistoryShortcut, undoRedoShortcuts]);
12102
12268
  const activePaletteVariant = activePaletteVariantId ? variantById.get(activePaletteVariantId) : null;
12103
12269
  const blockPayloads = blocks.map((b) => b.block);
12104
12270
  return /* @__PURE__ */ jsxRuntime.jsx(BrandThemeScope, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(CustomEmojiProvider, { customEmojis, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -12113,8 +12279,12 @@ function BlockKitchen(props) {
12113
12279
  /* @__PURE__ */ jsxRuntime.jsxs(
12114
12280
  "div",
12115
12281
  {
12282
+ ref: rootRef,
12116
12283
  className: "bk-root flex h-full max-h-[var(--bk-max-height,100svh)] w-full flex-col overflow-hidden rounded-md border bg-background text-foreground",
12117
- onKeyDown: handleKeyDown,
12284
+ onKeyDown: runHistoryShortcut,
12285
+ onPointerDownCapture: () => {
12286
+ lastClickInBuilderRef.current = true;
12287
+ },
12118
12288
  children: [
12119
12289
  /* @__PURE__ */ jsxRuntime.jsx(
12120
12290
  Toolbar2,
@@ -12123,6 +12293,7 @@ function BlockKitchen(props) {
12123
12293
  canRedo,
12124
12294
  onUndo: undo,
12125
12295
  onRedo: redo,
12296
+ undoRedoShortcuts,
12126
12297
  onClear: () => replaceAll([]),
12127
12298
  onOpenJson: () => setJsonOpen(true),
12128
12299
  onOpenIssues: () => setIssuesOpen(true),
@@ -12213,7 +12384,7 @@ function BlockKitchen(props) {
12213
12384
  }
12214
12385
  )
12215
12386
  ] }),
12216
- /* @__PURE__ */ jsxRuntime.jsx(MentionsProvider, { mentions, children: /* @__PURE__ */ jsxRuntime.jsx(
12387
+ /* @__PURE__ */ jsxRuntime.jsx(MentionsProvider, { mentions, children: /* @__PURE__ */ jsxRuntime.jsx(BlockEditorsProvider, { blockEditors, children: /* @__PURE__ */ jsxRuntime.jsx(
12217
12388
  Surface,
12218
12389
  {
12219
12390
  blocks,
@@ -12232,9 +12403,10 @@ function BlockKitchen(props) {
12232
12403
  onReorder: reorderBlock,
12233
12404
  isPaletteDrag: activePaletteVariant !== null,
12234
12405
  onOpenPalette: () => setPaletteOpen(true),
12235
- blockActions
12406
+ blockActions,
12407
+ renderBlockAdornment
12236
12408
  }
12237
- ) })
12409
+ ) }) })
12238
12410
  ] })
12239
12411
  ]
12240
12412
  }
@@ -12265,6 +12437,7 @@ function BlockKitchen(props) {
12265
12437
  addBlock(block);
12266
12438
  setPaletteOpen(false);
12267
12439
  },
12440
+ onLockedPicked: () => setPaletteOpen(false),
12268
12441
  sections: paletteSections,
12269
12442
  mode: paletteMode,
12270
12443
  showSearch: showPaletteSearch,