@tightknitai/block-kitchen 0.10.0 → 0.10.1

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCenter, DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
2
2
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
3
- import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, Trash2, Code2, Pencil, Send, Search, Check, Copy, Info, CheckCircle2, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Type, Bold, Italic, Strikethrough, Underline, Code, List, ListOrdered, Quote, Undo2, Redo2, Smile, Link as Link$1 } from 'lucide-react';
3
+ import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, Undo2, Redo2, Trash2, Code2, Pencil, Send, Search, Check, Copy, Info, CheckCircle2, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Type, Bold, Italic, Strikethrough, Underline, Code, List, ListOrdered, Quote, Smile, Link as Link$1 } from 'lucide-react';
4
4
  import * as React from 'react';
5
5
  import { createContext, useState, useRef, useEffect, useCallback, useMemo, useId, useContext } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -2036,6 +2036,69 @@ function useBlockKitValidation(blocks, surface) {
2036
2036
  }, [blocks, surface]);
2037
2037
  return state;
2038
2038
  }
2039
+ var MAX_HISTORY = 20;
2040
+ function useHistoryState(initial) {
2041
+ const [record, setRecord] = useState(() => ({
2042
+ past: [],
2043
+ present: typeof initial === "function" ? initial() : initial,
2044
+ future: [],
2045
+ lastTag: null
2046
+ }));
2047
+ const commit = useCallback((updater, options) => {
2048
+ const tag = options?.tag ?? null;
2049
+ setRecord((h) => {
2050
+ const next = typeof updater === "function" ? updater(h.present) : updater;
2051
+ if (Object.is(next, h.present)) {
2052
+ return h;
2053
+ }
2054
+ if (tag !== null && h.lastTag === tag) {
2055
+ return { ...h, present: next, future: [] };
2056
+ }
2057
+ const past = [...h.past, h.present];
2058
+ return {
2059
+ past: past.length > MAX_HISTORY ? past.slice(past.length - MAX_HISTORY) : past,
2060
+ present: next,
2061
+ future: [],
2062
+ lastTag: tag
2063
+ };
2064
+ });
2065
+ }, []);
2066
+ const undo = useCallback(() => {
2067
+ setRecord((h) => {
2068
+ if (h.past.length === 0) {
2069
+ return h;
2070
+ }
2071
+ const previous = h.past[h.past.length - 1];
2072
+ return { past: h.past.slice(0, -1), present: previous, future: [h.present, ...h.future], lastTag: null };
2073
+ });
2074
+ }, []);
2075
+ const redo = useCallback(() => {
2076
+ setRecord((h) => {
2077
+ if (h.future.length === 0) {
2078
+ return h;
2079
+ }
2080
+ const [next, ...rest] = h.future;
2081
+ return { past: [...h.past, h.present], present: next, future: rest, lastTag: null };
2082
+ });
2083
+ }, []);
2084
+ const reset = useCallback((next) => {
2085
+ setRecord({ past: [], present: next, future: [], lastTag: null });
2086
+ }, []);
2087
+ return useMemo(
2088
+ () => ({
2089
+ present: record.present,
2090
+ canUndo: record.past.length > 0,
2091
+ canRedo: record.future.length > 0,
2092
+ commit,
2093
+ undo,
2094
+ redo,
2095
+ reset
2096
+ }),
2097
+ [record, commit, undo, redo, reset]
2098
+ );
2099
+ }
2100
+
2101
+ // src/state/use-block-kitchen-state.ts
2039
2102
  function mirror(node) {
2040
2103
  if (!node.children) {
2041
2104
  return node;
@@ -2086,7 +2149,15 @@ function useBlockKitchenState({
2086
2149
  initialBlocks,
2087
2150
  onChange
2088
2151
  } = {}) {
2089
- const [blocks, setBlocks] = useState(() => (initialBlocks ?? []).map(wrap));
2152
+ const {
2153
+ present: blocks,
2154
+ commit,
2155
+ undo,
2156
+ redo,
2157
+ reset,
2158
+ canUndo,
2159
+ canRedo
2160
+ } = useHistoryState(() => (initialBlocks ?? []).map(wrap));
2090
2161
  const onChangeRef = useRef(onChange);
2091
2162
  useEffect(() => {
2092
2163
  onChangeRef.current = onChange;
@@ -2099,121 +2170,155 @@ function useBlockKitchenState({
2099
2170
  }
2100
2171
  onChangeRef.current?.(blocks.map((b) => b.block));
2101
2172
  }, [blocks]);
2102
- const addBlock = useCallback((block, atIndex) => {
2103
- setBlocks((prev) => {
2104
- const next = [...prev];
2105
- const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2106
- next.splice(idx, 0, wrap(block));
2107
- return next;
2108
- });
2109
- }, []);
2110
- const addChild = useCallback((parentId, block, atIndex) => {
2111
- if (!isContainerChildType(block.type)) {
2112
- return;
2113
- }
2114
- setBlocks(
2115
- (prev) => withChildren(prev, parentId, (children) => {
2116
- if (children.length >= MAX_CONTAINER_CHILDREN) {
2117
- return children;
2118
- }
2119
- const next = [...children];
2173
+ const addBlock = useCallback(
2174
+ (block, atIndex) => {
2175
+ commit((prev) => {
2176
+ const next = [...prev];
2120
2177
  const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2121
2178
  next.splice(idx, 0, wrap(block));
2122
2179
  return next;
2123
- })
2124
- );
2125
- }, []);
2126
- const updateBlock = useCallback((id, block) => {
2127
- setBlocks(
2128
- (prev) => prev.map((b) => {
2129
- if (b.id === id) {
2130
- return applyUpdate(b, block);
2131
- }
2132
- if (b.children?.some((c) => c.id === id)) {
2133
- return mirror({ ...b, children: b.children.map((c) => c.id === id ? applyUpdate(c, block) : c) });
2134
- }
2135
- return b;
2136
- })
2137
- );
2138
- }, []);
2139
- const removeBlock = useCallback((id) => {
2140
- setBlocks((prev) => {
2141
- if (prev.some((b) => b.id === id)) {
2142
- return prev.filter((b) => b.id !== id);
2180
+ });
2181
+ },
2182
+ [commit]
2183
+ );
2184
+ const addChild = useCallback(
2185
+ (parentId, block, atIndex) => {
2186
+ if (!isContainerChildType(block.type)) {
2187
+ return;
2143
2188
  }
2144
- return prev.map(
2145
- (b) => b.children?.some((c) => c.id === id) ? mirror({ ...b, children: b.children.filter((c) => c.id !== id) }) : b
2189
+ commit(
2190
+ (prev) => withChildren(prev, parentId, (children) => {
2191
+ if (children.length >= MAX_CONTAINER_CHILDREN) {
2192
+ return children;
2193
+ }
2194
+ const next = [...children];
2195
+ const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2196
+ next.splice(idx, 0, wrap(block));
2197
+ return next;
2198
+ })
2146
2199
  );
2147
- });
2148
- }, []);
2149
- const duplicateBlock = useCallback((id) => {
2150
- setBlocks((prev) => {
2151
- const loc = locate(prev, id);
2152
- if (!loc) {
2153
- return prev;
2154
- }
2155
- if (loc.kind === "top") {
2156
- const next = [...prev];
2157
- next.splice(loc.index + 1, 0, cloneNode(prev[loc.index]));
2158
- return next;
2159
- }
2160
- return withChildren(prev, loc.parentId, (children) => {
2161
- const next = [...children];
2162
- next.splice(loc.index + 1, 0, cloneNode(children[loc.index]));
2163
- return next;
2200
+ },
2201
+ [commit]
2202
+ );
2203
+ const updateBlock = useCallback(
2204
+ (id, block) => {
2205
+ commit(
2206
+ (prev) => prev.map((b) => {
2207
+ if (b.id === id) {
2208
+ return applyUpdate(b, block);
2209
+ }
2210
+ if (b.children?.some((c) => c.id === id)) {
2211
+ return mirror({ ...b, children: b.children.map((c) => c.id === id ? applyUpdate(c, block) : c) });
2212
+ }
2213
+ return b;
2214
+ }),
2215
+ // Coalesce a run of edits to the same block (every keystroke in an
2216
+ // editor field lands here) into a single undo step. Editing a
2217
+ // different block, or any structural change, starts a new step.
2218
+ { tag: `update:${id}` }
2219
+ );
2220
+ },
2221
+ [commit]
2222
+ );
2223
+ const removeBlock = useCallback(
2224
+ (id) => {
2225
+ commit((prev) => {
2226
+ if (prev.some((b) => b.id === id)) {
2227
+ return prev.filter((b) => b.id !== id);
2228
+ }
2229
+ return prev.map(
2230
+ (b) => b.children?.some((c) => c.id === id) ? mirror({ ...b, children: b.children.filter((c) => c.id !== id) }) : b
2231
+ );
2164
2232
  });
2165
- });
2166
- }, []);
2167
- const reorderBlock = useCallback((fromId, toIndex) => {
2168
- setBlocks((prev) => {
2169
- const loc = locate(prev, fromId);
2170
- if (!loc) {
2171
- return prev;
2172
- }
2173
- const reorder = (list) => {
2174
- const next = [...list];
2175
- const [moved] = next.splice(loc.index, 1);
2176
- next.splice(Math.max(0, Math.min(toIndex, next.length)), 0, moved);
2177
- return next;
2178
- };
2179
- if (loc.kind === "top") {
2180
- return reorder(prev);
2181
- }
2182
- return withChildren(prev, loc.parentId, reorder);
2183
- });
2184
- }, []);
2185
- const moveBlock = useCallback((activeId, target) => {
2186
- setBlocks((prev) => {
2187
- const loc = locate(prev, activeId);
2188
- if (!loc) {
2189
- return prev;
2190
- }
2191
- const moved = loc.kind === "top" ? prev[loc.index] : prev.find((b) => b.id === loc.parentId)?.children?.[loc.index];
2192
- if (!moved) {
2193
- return prev;
2194
- }
2195
- if (target.kind === "container") {
2196
- if (moved.children || !isContainerChildType(moved.block.type)) {
2233
+ },
2234
+ [commit]
2235
+ );
2236
+ const duplicateBlock = useCallback(
2237
+ (id) => {
2238
+ commit((prev) => {
2239
+ const loc = locate(prev, id);
2240
+ if (!loc) {
2197
2241
  return prev;
2198
2242
  }
2199
- const sameContainer = loc.kind === "child" && loc.parentId === target.parentId;
2200
- const destCount = prev.find((b) => b.id === target.parentId)?.children?.length ?? 0;
2201
- if (!sameContainer && destCount >= MAX_CONTAINER_CHILDREN) {
2243
+ if (loc.kind === "top") {
2244
+ const next = [...prev];
2245
+ next.splice(loc.index + 1, 0, cloneNode(prev[loc.index]));
2246
+ return next;
2247
+ }
2248
+ return withChildren(prev, loc.parentId, (children) => {
2249
+ const next = [...children];
2250
+ next.splice(loc.index + 1, 0, cloneNode(children[loc.index]));
2251
+ return next;
2252
+ });
2253
+ });
2254
+ },
2255
+ [commit]
2256
+ );
2257
+ const reorderBlock = useCallback(
2258
+ (fromId, toIndex) => {
2259
+ commit((prev) => {
2260
+ const loc = locate(prev, fromId);
2261
+ if (!loc) {
2202
2262
  return prev;
2203
2263
  }
2204
- }
2205
- const detached = loc.kind === "top" ? prev.filter((_, i) => i !== loc.index) : withChildren(prev, loc.parentId, (children) => children.filter((_, i) => i !== loc.index));
2206
- const insert = (list) => {
2207
- const next = [...list];
2208
- next.splice(Math.max(0, Math.min(target.index, next.length)), 0, moved);
2209
- return next;
2210
- };
2211
- return target.kind === "container" ? withChildren(detached, target.parentId, insert) : insert(detached);
2212
- });
2213
- }, []);
2214
- const replaceAll = useCallback((newBlocks) => {
2215
- setBlocks(sanitizeBlocks(newBlocks).map(wrap));
2216
- }, []);
2264
+ const reorder = (list) => {
2265
+ const next = [...list];
2266
+ const [moved] = next.splice(loc.index, 1);
2267
+ next.splice(Math.max(0, Math.min(toIndex, next.length)), 0, moved);
2268
+ return next;
2269
+ };
2270
+ if (loc.kind === "top") {
2271
+ return reorder(prev);
2272
+ }
2273
+ return withChildren(prev, loc.parentId, reorder);
2274
+ });
2275
+ },
2276
+ [commit]
2277
+ );
2278
+ const moveBlock = useCallback(
2279
+ (activeId, target) => {
2280
+ commit((prev) => {
2281
+ const loc = locate(prev, activeId);
2282
+ if (!loc) {
2283
+ return prev;
2284
+ }
2285
+ const moved = loc.kind === "top" ? prev[loc.index] : prev.find((b) => b.id === loc.parentId)?.children?.[loc.index];
2286
+ if (!moved) {
2287
+ return prev;
2288
+ }
2289
+ if (target.kind === "container") {
2290
+ if (moved.children || !isContainerChildType(moved.block.type)) {
2291
+ return prev;
2292
+ }
2293
+ const sameContainer = loc.kind === "child" && loc.parentId === target.parentId;
2294
+ const destCount = prev.find((b) => b.id === target.parentId)?.children?.length ?? 0;
2295
+ if (!sameContainer && destCount >= MAX_CONTAINER_CHILDREN) {
2296
+ return prev;
2297
+ }
2298
+ }
2299
+ const detached = loc.kind === "top" ? prev.filter((_, i) => i !== loc.index) : withChildren(prev, loc.parentId, (children) => children.filter((_, i) => i !== loc.index));
2300
+ const insert = (list) => {
2301
+ const next = [...list];
2302
+ next.splice(Math.max(0, Math.min(target.index, next.length)), 0, moved);
2303
+ return next;
2304
+ };
2305
+ return target.kind === "container" ? withChildren(detached, target.parentId, insert) : insert(detached);
2306
+ });
2307
+ },
2308
+ [commit]
2309
+ );
2310
+ const replaceAll = useCallback(
2311
+ (newBlocks) => {
2312
+ commit(sanitizeBlocks(newBlocks).map(wrap));
2313
+ },
2314
+ [commit]
2315
+ );
2316
+ const resetAll = useCallback(
2317
+ (newBlocks) => {
2318
+ reset(sanitizeBlocks(newBlocks).map(wrap));
2319
+ },
2320
+ [reset]
2321
+ );
2217
2322
  return {
2218
2323
  blocks,
2219
2324
  addBlock,
@@ -2223,7 +2328,12 @@ function useBlockKitchenState({
2223
2328
  duplicateBlock,
2224
2329
  reorderBlock,
2225
2330
  moveBlock,
2226
- replaceAll
2331
+ replaceAll,
2332
+ resetAll,
2333
+ undo,
2334
+ redo,
2335
+ canUndo,
2336
+ canRedo
2227
2337
  };
2228
2338
  }
2229
2339
  function useDevWarning(condition, message) {
@@ -8452,7 +8562,8 @@ function VideoEditor({ block, onChange }) {
8452
8562
  function BlockEditor({
8453
8563
  block,
8454
8564
  errors,
8455
- onChange
8565
+ onChange,
8566
+ onDone
8456
8567
  }) {
8457
8568
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
8458
8569
  /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold text-foreground", children: [
@@ -8460,7 +8571,8 @@ function BlockEditor({
8460
8571
  labelForBlockType(block.type)
8461
8572
  ] }) }),
8462
8573
  errors && errors.length > 0 ? /* @__PURE__ */ 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__ */ jsx("li", { children: err }, i)) }) : null,
8463
- dispatch(block, onChange)
8574
+ dispatch(block, onChange),
8575
+ onDone ? /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t pt-3", children: /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: onDone, children: "Done" }) }) : null
8464
8576
  ] });
8465
8577
  }
8466
8578
  function dispatch(block, onChange) {
@@ -8677,7 +8789,8 @@ function BlockRow({
8677
8789
  {
8678
8790
  block: builderBlock.block,
8679
8791
  errors,
8680
- onChange: (next) => onUpdate(builderBlock.id, next)
8792
+ onChange: (next) => onUpdate(builderBlock.id, next),
8793
+ onDone: () => onOpenChange?.(false)
8681
8794
  }
8682
8795
  ) })
8683
8796
  ] }),
@@ -8941,7 +9054,15 @@ function ContainerRow({
8941
9054
  ]
8942
9055
  }
8943
9056
  ) }),
8944
- /* @__PURE__ */ jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsx(BlockEditor, { block, errors, onChange: (next) => onUpdate(id, next) }) })
9057
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsx(
9058
+ BlockEditor,
9059
+ {
9060
+ block,
9061
+ errors,
9062
+ onChange: (next) => onUpdate(id, next),
9063
+ onDone: () => onOpenBlockChange?.(null)
9064
+ }
9065
+ ) })
8945
9066
  ] }),
8946
9067
  /* @__PURE__ */ jsx(
8947
9068
  "div",
@@ -9422,6 +9543,10 @@ function formatMessageTs(ts) {
9422
9543
  }
9423
9544
  var SEND_MENU_ITEM = "flex w-full cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-left text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50";
9424
9545
  function Toolbar2({
9546
+ canUndo = false,
9547
+ canRedo = false,
9548
+ onUndo,
9549
+ onRedo,
9425
9550
  onClear,
9426
9551
  onOpenJson,
9427
9552
  onOpenIssues,
@@ -9535,6 +9660,36 @@ function Toolbar2({
9535
9660
  ) : null
9536
9661
  ] }),
9537
9662
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9663
+ onUndo && onRedo ? /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
9664
+ /* @__PURE__ */ jsx(
9665
+ Button,
9666
+ {
9667
+ type: "button",
9668
+ variant: "ghost",
9669
+ size: "sm",
9670
+ onClick: onUndo,
9671
+ disabled: !canUndo,
9672
+ "aria-label": "Undo",
9673
+ title: "Undo (Ctrl+Z)",
9674
+ className: "px-2",
9675
+ children: /* @__PURE__ */ jsx(Undo2, { className: "h-3.5 w-3.5" })
9676
+ }
9677
+ ),
9678
+ /* @__PURE__ */ jsx(
9679
+ Button,
9680
+ {
9681
+ type: "button",
9682
+ variant: "ghost",
9683
+ size: "sm",
9684
+ onClick: onRedo,
9685
+ disabled: !canRedo,
9686
+ "aria-label": "Redo",
9687
+ title: "Redo (Ctrl+Shift+Z)",
9688
+ className: "px-2",
9689
+ children: /* @__PURE__ */ jsx(Redo2, { className: "h-3.5 w-3.5" })
9690
+ }
9691
+ )
9692
+ ] }) : null,
9538
9693
  errorCount > 0 ? /* @__PURE__ */ jsxs(
9539
9694
  Button,
9540
9695
  {
@@ -9935,7 +10090,22 @@ function BlockKitchen(props) {
9935
10090
  Boolean(primaryAction && sendEnabled),
9936
10091
  "[BlockKitchen] `primaryAction` is only available in compose-only mode \u2014 with the send integration wired, the built-in Send/Update flow owns the toolbar\u2019s primary action; ignoring `primaryAction`."
9937
10092
  );
9938
- const { blocks, addBlock, addChild, updateBlock, removeBlock, duplicateBlock, reorderBlock, moveBlock, replaceAll } = useBlockKitchenState({ initialBlocks: seededBlocks, onChange });
10093
+ const {
10094
+ blocks,
10095
+ addBlock,
10096
+ addChild,
10097
+ updateBlock,
10098
+ removeBlock,
10099
+ duplicateBlock,
10100
+ reorderBlock,
10101
+ moveBlock,
10102
+ replaceAll,
10103
+ resetAll,
10104
+ undo,
10105
+ redo,
10106
+ canUndo,
10107
+ canRedo
10108
+ } = useBlockKitchenState({ initialBlocks: seededBlocks, onChange });
9939
10109
  const childParentById = useMemo(() => {
9940
10110
  const map = /* @__PURE__ */ new Map();
9941
10111
  for (const top of blocks) {
@@ -10051,6 +10221,30 @@ function BlockKitchen(props) {
10051
10221
  const handleDragCancel = useCallback(() => {
10052
10222
  setActivePaletteVariantId(null);
10053
10223
  }, []);
10224
+ const handleKeyDown = useCallback(
10225
+ (event) => {
10226
+ if (!(event.metaKey || event.ctrlKey) || event.altKey) {
10227
+ return;
10228
+ }
10229
+ const key = event.key.toLowerCase();
10230
+ const isUndo = key === "z" && !event.shiftKey;
10231
+ const isRedo = key === "y" || key === "z" && event.shiftKey;
10232
+ if (!isUndo && !isRedo) {
10233
+ return;
10234
+ }
10235
+ const target = event.target;
10236
+ if (target && (target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName))) {
10237
+ return;
10238
+ }
10239
+ event.preventDefault();
10240
+ if (isRedo) {
10241
+ if (canRedo) redo();
10242
+ } else if (canUndo) {
10243
+ undo();
10244
+ }
10245
+ },
10246
+ [canRedo, canUndo, redo, undo]
10247
+ );
10054
10248
  const activePaletteVariant = activePaletteVariantId ? variantById.get(activePaletteVariantId) : null;
10055
10249
  const blockPayloads = blocks.map((b) => b.block);
10056
10250
  return /* @__PURE__ */ jsx(BrandThemeScope, { theme, children: /* @__PURE__ */ jsx(CustomEmojiProvider, { customEmojis, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(
@@ -10062,91 +10256,102 @@ function BlockKitchen(props) {
10062
10256
  onDragEnd: handleDragEnd,
10063
10257
  onDragCancel: handleDragCancel,
10064
10258
  children: [
10065
- /* @__PURE__ */ jsxs("div", { className: "bk-root flex h-full w-full flex-col overflow-hidden rounded-md border bg-background text-foreground", children: [
10066
- /* @__PURE__ */ jsx(
10067
- Toolbar2,
10068
- {
10069
- onClear: () => replaceAll([]),
10070
- onOpenJson: () => setJsonOpen(true),
10071
- onOpenIssues: () => setIssuesOpen(true),
10072
- onOpenSend: () => setSendOpen(true),
10073
- onOpenPalette: () => setPaletteOpen(true),
10074
- canSend: blocks.length > 0,
10075
- canClear: blocks.length > 0,
10076
- errorCount: validation.total,
10077
- previewTheme,
10078
- onPreviewThemeChange: setUncontrolledPreviewTheme,
10079
- previewSurface,
10080
- onPreviewSurfaceChange: setPreviewSurface,
10081
- allowedSurfaces,
10082
- showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
10083
- docsLink,
10084
- showSend: sendEnabled,
10085
- primaryAction: !sendEnabled && primaryAction ? {
10086
- label: primaryAction.label,
10087
- onClick: () => primaryAction.onClick({
10088
- blocks: blockPayloads,
10089
- validation: validationSummary,
10090
- loadedMessage: activeEditTarget
10091
- }),
10092
- disabled: primaryAction.disableWhenInvalid ? !validationSummary.valid : false
10093
- } : null,
10094
- sendButtonLabel,
10095
- loadEnabled,
10096
- updateEnabled: Boolean(onUpdate),
10097
- editBadge: activeEditTarget ? {
10098
- channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
10099
- ts: activeEditTarget.ts
10100
- } : null,
10101
- onOpenLoad: () => setLoadOpen(true),
10102
- onOpenUpdate: () => setUpdateOpen(true),
10103
- onExitEdit: () => {
10104
- if (sendEnabled) {
10105
- setEditTarget(null);
10106
- replaceAll([]);
10107
- setLoadOpen(true);
10108
- return;
10259
+ /* @__PURE__ */ jsxs(
10260
+ "div",
10261
+ {
10262
+ className: "bk-root flex h-full w-full flex-col overflow-hidden rounded-md border bg-background text-foreground",
10263
+ onKeyDown: handleKeyDown,
10264
+ children: [
10265
+ /* @__PURE__ */ jsx(
10266
+ Toolbar2,
10267
+ {
10268
+ canUndo,
10269
+ canRedo,
10270
+ onUndo: undo,
10271
+ onRedo: redo,
10272
+ onClear: () => replaceAll([]),
10273
+ onOpenJson: () => setJsonOpen(true),
10274
+ onOpenIssues: () => setIssuesOpen(true),
10275
+ onOpenSend: () => setSendOpen(true),
10276
+ onOpenPalette: () => setPaletteOpen(true),
10277
+ canSend: blocks.length > 0,
10278
+ canClear: blocks.length > 0,
10279
+ errorCount: validation.total,
10280
+ previewTheme,
10281
+ onPreviewThemeChange: setUncontrolledPreviewTheme,
10282
+ previewSurface,
10283
+ onPreviewSurfaceChange: setPreviewSurface,
10284
+ allowedSurfaces,
10285
+ showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
10286
+ docsLink,
10287
+ showSend: sendEnabled,
10288
+ primaryAction: !sendEnabled && primaryAction ? {
10289
+ label: primaryAction.label,
10290
+ onClick: () => primaryAction.onClick({
10291
+ blocks: blockPayloads,
10292
+ validation: validationSummary,
10293
+ loadedMessage: activeEditTarget
10294
+ }),
10295
+ disabled: primaryAction.disableWhenInvalid ? !validationSummary.valid : false
10296
+ } : null,
10297
+ sendButtonLabel,
10298
+ loadEnabled,
10299
+ updateEnabled: Boolean(onUpdate),
10300
+ editBadge: activeEditTarget ? {
10301
+ channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
10302
+ ts: activeEditTarget.ts
10303
+ } : null,
10304
+ onOpenLoad: () => setLoadOpen(true),
10305
+ onOpenUpdate: () => setUpdateOpen(true),
10306
+ onExitEdit: () => {
10307
+ if (sendEnabled) {
10308
+ setEditTarget(null);
10309
+ resetAll([]);
10310
+ setLoadOpen(true);
10311
+ return;
10312
+ }
10313
+ setEditTarget(null);
10314
+ },
10315
+ loadButtonLabel,
10316
+ updateButtonLabel
10109
10317
  }
10110
- setEditTarget(null);
10111
- },
10112
- loadButtonLabel,
10113
- updateButtonLabel
10114
- }
10115
- ),
10116
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
10117
- /* @__PURE__ */ jsx("div", { className: "hidden min-h-0 md:flex", children: /* @__PURE__ */ jsx(
10118
- Palette,
10119
- {
10120
- onAddBlock: (block) => addBlock(block),
10121
- sections: paletteSections,
10122
- showSearch: showPaletteSearch,
10123
- searchPlaceholder: paletteSearchPlaceholder,
10124
- defaultOpenSections
10125
- }
10126
- ) }),
10127
- /* @__PURE__ */ jsx(
10128
- Surface,
10129
- {
10130
- blocks,
10131
- workspaceName,
10132
- authorName: activeEditTarget?.username,
10133
- authorIcon: activeEditTarget?.iconUrl,
10134
- previewHooks: mergedPreviewHooks,
10135
- previewTheme,
10136
- previewSurface,
10137
- errorsByBlockId: validation.byBlockId,
10138
- openBlockId,
10139
- onOpenBlockChange: setOpenBlockId,
10140
- onUpdate: updateBlock,
10141
- onDuplicate: duplicateBlock,
10142
- onDelete: removeBlock,
10143
- onReorder: reorderBlock,
10144
- isPaletteDrag: activePaletteVariant !== null,
10145
- onOpenPalette: () => setPaletteOpen(true)
10146
- }
10147
- )
10148
- ] })
10149
- ] }),
10318
+ ),
10319
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
10320
+ /* @__PURE__ */ jsx("div", { className: "hidden min-h-0 md:flex", children: /* @__PURE__ */ jsx(
10321
+ Palette,
10322
+ {
10323
+ onAddBlock: (block) => addBlock(block),
10324
+ sections: paletteSections,
10325
+ showSearch: showPaletteSearch,
10326
+ searchPlaceholder: paletteSearchPlaceholder,
10327
+ defaultOpenSections
10328
+ }
10329
+ ) }),
10330
+ /* @__PURE__ */ jsx(
10331
+ Surface,
10332
+ {
10333
+ blocks,
10334
+ workspaceName,
10335
+ authorName: activeEditTarget?.username,
10336
+ authorIcon: activeEditTarget?.iconUrl,
10337
+ previewHooks: mergedPreviewHooks,
10338
+ previewTheme,
10339
+ previewSurface,
10340
+ errorsByBlockId: validation.byBlockId,
10341
+ openBlockId,
10342
+ onOpenBlockChange: setOpenBlockId,
10343
+ onUpdate: updateBlock,
10344
+ onDuplicate: duplicateBlock,
10345
+ onDelete: removeBlock,
10346
+ onReorder: reorderBlock,
10347
+ isPaletteDrag: activePaletteVariant !== null,
10348
+ onOpenPalette: () => setPaletteOpen(true)
10349
+ }
10350
+ )
10351
+ ] })
10352
+ ]
10353
+ }
10354
+ ),
10150
10355
  /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: activePaletteVariant ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded border bg-background px-1.5 py-1 text-xs text-foreground shadow-md", children: [
10151
10356
  /* @__PURE__ */ jsx(GripVertical, { className: "h-3 w-3 shrink-0" }),
10152
10357
  /* @__PURE__ */ jsx("span", { className: "truncate", children: activePaletteVariant.label })
@@ -10223,13 +10428,13 @@ function BlockKitchen(props) {
10223
10428
  loadRecentMessages: loading.loadRecentMessages,
10224
10429
  loadChannels: recentChannelSource,
10225
10430
  onLoaded: (result) => {
10226
- replaceAll(result.blocks);
10431
+ resetAll(result.blocks);
10227
10432
  setEditTarget(toLoadedMessage(result));
10228
10433
  setLoadOpen(false);
10229
10434
  },
10230
10435
  onOpenAsNew: (loadedBlocks) => {
10231
10436
  if (loadedBlocks) {
10232
- replaceAll(loadedBlocks);
10437
+ resetAll(loadedBlocks);
10233
10438
  }
10234
10439
  setEditTarget(null);
10235
10440
  setLoadOpen(false);