@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.cjs CHANGED
@@ -2066,6 +2066,69 @@ function useBlockKitValidation(blocks, surface) {
2066
2066
  }, [blocks, surface]);
2067
2067
  return state;
2068
2068
  }
2069
+ var MAX_HISTORY = 20;
2070
+ function useHistoryState(initial) {
2071
+ const [record, setRecord] = React.useState(() => ({
2072
+ past: [],
2073
+ present: typeof initial === "function" ? initial() : initial,
2074
+ future: [],
2075
+ lastTag: null
2076
+ }));
2077
+ const commit = React.useCallback((updater, options) => {
2078
+ const tag = options?.tag ?? null;
2079
+ setRecord((h) => {
2080
+ const next = typeof updater === "function" ? updater(h.present) : updater;
2081
+ if (Object.is(next, h.present)) {
2082
+ return h;
2083
+ }
2084
+ if (tag !== null && h.lastTag === tag) {
2085
+ return { ...h, present: next, future: [] };
2086
+ }
2087
+ const past = [...h.past, h.present];
2088
+ return {
2089
+ past: past.length > MAX_HISTORY ? past.slice(past.length - MAX_HISTORY) : past,
2090
+ present: next,
2091
+ future: [],
2092
+ lastTag: tag
2093
+ };
2094
+ });
2095
+ }, []);
2096
+ const undo = React.useCallback(() => {
2097
+ setRecord((h) => {
2098
+ if (h.past.length === 0) {
2099
+ return h;
2100
+ }
2101
+ const previous = h.past[h.past.length - 1];
2102
+ return { past: h.past.slice(0, -1), present: previous, future: [h.present, ...h.future], lastTag: null };
2103
+ });
2104
+ }, []);
2105
+ const redo = React.useCallback(() => {
2106
+ setRecord((h) => {
2107
+ if (h.future.length === 0) {
2108
+ return h;
2109
+ }
2110
+ const [next, ...rest] = h.future;
2111
+ return { past: [...h.past, h.present], present: next, future: rest, lastTag: null };
2112
+ });
2113
+ }, []);
2114
+ const reset = React.useCallback((next) => {
2115
+ setRecord({ past: [], present: next, future: [], lastTag: null });
2116
+ }, []);
2117
+ return React.useMemo(
2118
+ () => ({
2119
+ present: record.present,
2120
+ canUndo: record.past.length > 0,
2121
+ canRedo: record.future.length > 0,
2122
+ commit,
2123
+ undo,
2124
+ redo,
2125
+ reset
2126
+ }),
2127
+ [record, commit, undo, redo, reset]
2128
+ );
2129
+ }
2130
+
2131
+ // src/state/use-block-kitchen-state.ts
2069
2132
  function mirror(node) {
2070
2133
  if (!node.children) {
2071
2134
  return node;
@@ -2116,7 +2179,15 @@ function useBlockKitchenState({
2116
2179
  initialBlocks,
2117
2180
  onChange
2118
2181
  } = {}) {
2119
- const [blocks, setBlocks] = React.useState(() => (initialBlocks ?? []).map(wrap));
2182
+ const {
2183
+ present: blocks,
2184
+ commit,
2185
+ undo,
2186
+ redo,
2187
+ reset,
2188
+ canUndo,
2189
+ canRedo
2190
+ } = useHistoryState(() => (initialBlocks ?? []).map(wrap));
2120
2191
  const onChangeRef = React.useRef(onChange);
2121
2192
  React.useEffect(() => {
2122
2193
  onChangeRef.current = onChange;
@@ -2129,121 +2200,155 @@ function useBlockKitchenState({
2129
2200
  }
2130
2201
  onChangeRef.current?.(blocks.map((b) => b.block));
2131
2202
  }, [blocks]);
2132
- const addBlock = React.useCallback((block, atIndex) => {
2133
- setBlocks((prev) => {
2134
- const next = [...prev];
2135
- const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2136
- next.splice(idx, 0, wrap(block));
2137
- return next;
2138
- });
2139
- }, []);
2140
- const addChild = React.useCallback((parentId, block, atIndex) => {
2141
- if (!isContainerChildType(block.type)) {
2142
- return;
2143
- }
2144
- setBlocks(
2145
- (prev) => withChildren(prev, parentId, (children) => {
2146
- if (children.length >= MAX_CONTAINER_CHILDREN) {
2147
- return children;
2148
- }
2149
- const next = [...children];
2203
+ const addBlock = React.useCallback(
2204
+ (block, atIndex) => {
2205
+ commit((prev) => {
2206
+ const next = [...prev];
2150
2207
  const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2151
2208
  next.splice(idx, 0, wrap(block));
2152
2209
  return next;
2153
- })
2154
- );
2155
- }, []);
2156
- const updateBlock = React.useCallback((id, block) => {
2157
- setBlocks(
2158
- (prev) => prev.map((b) => {
2159
- if (b.id === id) {
2160
- return applyUpdate(b, block);
2161
- }
2162
- if (b.children?.some((c) => c.id === id)) {
2163
- return mirror({ ...b, children: b.children.map((c) => c.id === id ? applyUpdate(c, block) : c) });
2164
- }
2165
- return b;
2166
- })
2167
- );
2168
- }, []);
2169
- const removeBlock = React.useCallback((id) => {
2170
- setBlocks((prev) => {
2171
- if (prev.some((b) => b.id === id)) {
2172
- return prev.filter((b) => b.id !== id);
2210
+ });
2211
+ },
2212
+ [commit]
2213
+ );
2214
+ const addChild = React.useCallback(
2215
+ (parentId, block, atIndex) => {
2216
+ if (!isContainerChildType(block.type)) {
2217
+ return;
2173
2218
  }
2174
- return prev.map(
2175
- (b) => b.children?.some((c) => c.id === id) ? mirror({ ...b, children: b.children.filter((c) => c.id !== id) }) : b
2219
+ commit(
2220
+ (prev) => withChildren(prev, parentId, (children) => {
2221
+ if (children.length >= MAX_CONTAINER_CHILDREN) {
2222
+ return children;
2223
+ }
2224
+ const next = [...children];
2225
+ const idx = atIndex === void 0 ? next.length : Math.max(0, Math.min(atIndex, next.length));
2226
+ next.splice(idx, 0, wrap(block));
2227
+ return next;
2228
+ })
2176
2229
  );
2177
- });
2178
- }, []);
2179
- const duplicateBlock = React.useCallback((id) => {
2180
- setBlocks((prev) => {
2181
- const loc = locate(prev, id);
2182
- if (!loc) {
2183
- return prev;
2184
- }
2185
- if (loc.kind === "top") {
2186
- const next = [...prev];
2187
- next.splice(loc.index + 1, 0, cloneNode(prev[loc.index]));
2188
- return next;
2189
- }
2190
- return withChildren(prev, loc.parentId, (children) => {
2191
- const next = [...children];
2192
- next.splice(loc.index + 1, 0, cloneNode(children[loc.index]));
2193
- return next;
2230
+ },
2231
+ [commit]
2232
+ );
2233
+ const updateBlock = React.useCallback(
2234
+ (id, block) => {
2235
+ commit(
2236
+ (prev) => prev.map((b) => {
2237
+ if (b.id === id) {
2238
+ return applyUpdate(b, block);
2239
+ }
2240
+ if (b.children?.some((c) => c.id === id)) {
2241
+ return mirror({ ...b, children: b.children.map((c) => c.id === id ? applyUpdate(c, block) : c) });
2242
+ }
2243
+ return b;
2244
+ }),
2245
+ // Coalesce a run of edits to the same block (every keystroke in an
2246
+ // editor field lands here) into a single undo step. Editing a
2247
+ // different block, or any structural change, starts a new step.
2248
+ { tag: `update:${id}` }
2249
+ );
2250
+ },
2251
+ [commit]
2252
+ );
2253
+ const removeBlock = React.useCallback(
2254
+ (id) => {
2255
+ commit((prev) => {
2256
+ if (prev.some((b) => b.id === id)) {
2257
+ return prev.filter((b) => b.id !== id);
2258
+ }
2259
+ return prev.map(
2260
+ (b) => b.children?.some((c) => c.id === id) ? mirror({ ...b, children: b.children.filter((c) => c.id !== id) }) : b
2261
+ );
2194
2262
  });
2195
- });
2196
- }, []);
2197
- const reorderBlock = React.useCallback((fromId, toIndex) => {
2198
- setBlocks((prev) => {
2199
- const loc = locate(prev, fromId);
2200
- if (!loc) {
2201
- return prev;
2202
- }
2203
- const reorder = (list) => {
2204
- const next = [...list];
2205
- const [moved] = next.splice(loc.index, 1);
2206
- next.splice(Math.max(0, Math.min(toIndex, next.length)), 0, moved);
2207
- return next;
2208
- };
2209
- if (loc.kind === "top") {
2210
- return reorder(prev);
2211
- }
2212
- return withChildren(prev, loc.parentId, reorder);
2213
- });
2214
- }, []);
2215
- const moveBlock = React.useCallback((activeId, target) => {
2216
- setBlocks((prev) => {
2217
- const loc = locate(prev, activeId);
2218
- if (!loc) {
2219
- return prev;
2220
- }
2221
- const moved = loc.kind === "top" ? prev[loc.index] : prev.find((b) => b.id === loc.parentId)?.children?.[loc.index];
2222
- if (!moved) {
2223
- return prev;
2224
- }
2225
- if (target.kind === "container") {
2226
- if (moved.children || !isContainerChildType(moved.block.type)) {
2263
+ },
2264
+ [commit]
2265
+ );
2266
+ const duplicateBlock = React.useCallback(
2267
+ (id) => {
2268
+ commit((prev) => {
2269
+ const loc = locate(prev, id);
2270
+ if (!loc) {
2227
2271
  return prev;
2228
2272
  }
2229
- const sameContainer = loc.kind === "child" && loc.parentId === target.parentId;
2230
- const destCount = prev.find((b) => b.id === target.parentId)?.children?.length ?? 0;
2231
- if (!sameContainer && destCount >= MAX_CONTAINER_CHILDREN) {
2273
+ if (loc.kind === "top") {
2274
+ const next = [...prev];
2275
+ next.splice(loc.index + 1, 0, cloneNode(prev[loc.index]));
2276
+ return next;
2277
+ }
2278
+ return withChildren(prev, loc.parentId, (children) => {
2279
+ const next = [...children];
2280
+ next.splice(loc.index + 1, 0, cloneNode(children[loc.index]));
2281
+ return next;
2282
+ });
2283
+ });
2284
+ },
2285
+ [commit]
2286
+ );
2287
+ const reorderBlock = React.useCallback(
2288
+ (fromId, toIndex) => {
2289
+ commit((prev) => {
2290
+ const loc = locate(prev, fromId);
2291
+ if (!loc) {
2232
2292
  return prev;
2233
2293
  }
2234
- }
2235
- const detached = loc.kind === "top" ? prev.filter((_, i) => i !== loc.index) : withChildren(prev, loc.parentId, (children) => children.filter((_, i) => i !== loc.index));
2236
- const insert = (list) => {
2237
- const next = [...list];
2238
- next.splice(Math.max(0, Math.min(target.index, next.length)), 0, moved);
2239
- return next;
2240
- };
2241
- return target.kind === "container" ? withChildren(detached, target.parentId, insert) : insert(detached);
2242
- });
2243
- }, []);
2244
- const replaceAll = React.useCallback((newBlocks) => {
2245
- setBlocks(sanitizeBlocks(newBlocks).map(wrap));
2246
- }, []);
2294
+ const reorder = (list) => {
2295
+ const next = [...list];
2296
+ const [moved] = next.splice(loc.index, 1);
2297
+ next.splice(Math.max(0, Math.min(toIndex, next.length)), 0, moved);
2298
+ return next;
2299
+ };
2300
+ if (loc.kind === "top") {
2301
+ return reorder(prev);
2302
+ }
2303
+ return withChildren(prev, loc.parentId, reorder);
2304
+ });
2305
+ },
2306
+ [commit]
2307
+ );
2308
+ const moveBlock = React.useCallback(
2309
+ (activeId, target) => {
2310
+ commit((prev) => {
2311
+ const loc = locate(prev, activeId);
2312
+ if (!loc) {
2313
+ return prev;
2314
+ }
2315
+ const moved = loc.kind === "top" ? prev[loc.index] : prev.find((b) => b.id === loc.parentId)?.children?.[loc.index];
2316
+ if (!moved) {
2317
+ return prev;
2318
+ }
2319
+ if (target.kind === "container") {
2320
+ if (moved.children || !isContainerChildType(moved.block.type)) {
2321
+ return prev;
2322
+ }
2323
+ const sameContainer = loc.kind === "child" && loc.parentId === target.parentId;
2324
+ const destCount = prev.find((b) => b.id === target.parentId)?.children?.length ?? 0;
2325
+ if (!sameContainer && destCount >= MAX_CONTAINER_CHILDREN) {
2326
+ return prev;
2327
+ }
2328
+ }
2329
+ const detached = loc.kind === "top" ? prev.filter((_, i) => i !== loc.index) : withChildren(prev, loc.parentId, (children) => children.filter((_, i) => i !== loc.index));
2330
+ const insert = (list) => {
2331
+ const next = [...list];
2332
+ next.splice(Math.max(0, Math.min(target.index, next.length)), 0, moved);
2333
+ return next;
2334
+ };
2335
+ return target.kind === "container" ? withChildren(detached, target.parentId, insert) : insert(detached);
2336
+ });
2337
+ },
2338
+ [commit]
2339
+ );
2340
+ const replaceAll = React.useCallback(
2341
+ (newBlocks) => {
2342
+ commit(sanitizeBlocks(newBlocks).map(wrap));
2343
+ },
2344
+ [commit]
2345
+ );
2346
+ const resetAll = React.useCallback(
2347
+ (newBlocks) => {
2348
+ reset(sanitizeBlocks(newBlocks).map(wrap));
2349
+ },
2350
+ [reset]
2351
+ );
2247
2352
  return {
2248
2353
  blocks,
2249
2354
  addBlock,
@@ -2253,7 +2358,12 @@ function useBlockKitchenState({
2253
2358
  duplicateBlock,
2254
2359
  reorderBlock,
2255
2360
  moveBlock,
2256
- replaceAll
2361
+ replaceAll,
2362
+ resetAll,
2363
+ undo,
2364
+ redo,
2365
+ canUndo,
2366
+ canRedo
2257
2367
  };
2258
2368
  }
2259
2369
  function useDevWarning(condition, message) {
@@ -8482,7 +8592,8 @@ function VideoEditor({ block, onChange }) {
8482
8592
  function BlockEditor({
8483
8593
  block,
8484
8594
  errors,
8485
- onChange
8595
+ onChange,
8596
+ onDone
8486
8597
  }) {
8487
8598
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
8488
8599
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-sm font-semibold text-foreground", children: [
@@ -8490,7 +8601,8 @@ function BlockEditor({
8490
8601
  labelForBlockType(block.type)
8491
8602
  ] }) }),
8492
8603
  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,
8493
- dispatch(block, onChange)
8604
+ dispatch(block, onChange),
8605
+ onDone ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", onClick: onDone, children: "Done" }) }) : null
8494
8606
  ] });
8495
8607
  }
8496
8608
  function dispatch(block, onChange) {
@@ -8707,7 +8819,8 @@ function BlockRow({
8707
8819
  {
8708
8820
  block: builderBlock.block,
8709
8821
  errors,
8710
- onChange: (next) => onUpdate(builderBlock.id, next)
8822
+ onChange: (next) => onUpdate(builderBlock.id, next),
8823
+ onDone: () => onOpenChange?.(false)
8711
8824
  }
8712
8825
  ) })
8713
8826
  ] }),
@@ -8971,7 +9084,15 @@ function ContainerRow({
8971
9084
  ]
8972
9085
  }
8973
9086
  ) }),
8974
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(BlockEditor, { block, errors, onChange: (next) => onUpdate(id, next) }) })
9087
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
9088
+ BlockEditor,
9089
+ {
9090
+ block,
9091
+ errors,
9092
+ onChange: (next) => onUpdate(id, next),
9093
+ onDone: () => onOpenBlockChange?.(null)
9094
+ }
9095
+ ) })
8975
9096
  ] }),
8976
9097
  /* @__PURE__ */ jsxRuntime.jsx(
8977
9098
  "div",
@@ -9452,6 +9573,10 @@ function formatMessageTs(ts) {
9452
9573
  }
9453
9574
  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";
9454
9575
  function Toolbar2({
9576
+ canUndo = false,
9577
+ canRedo = false,
9578
+ onUndo,
9579
+ onRedo,
9455
9580
  onClear,
9456
9581
  onOpenJson,
9457
9582
  onOpenIssues,
@@ -9565,6 +9690,36 @@ function Toolbar2({
9565
9690
  ) : null
9566
9691
  ] }),
9567
9692
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9693
+ onUndo && onRedo ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
9694
+ /* @__PURE__ */ jsxRuntime.jsx(
9695
+ Button,
9696
+ {
9697
+ type: "button",
9698
+ variant: "ghost",
9699
+ size: "sm",
9700
+ onClick: onUndo,
9701
+ disabled: !canUndo,
9702
+ "aria-label": "Undo",
9703
+ title: "Undo (Ctrl+Z)",
9704
+ className: "px-2",
9705
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { className: "h-3.5 w-3.5" })
9706
+ }
9707
+ ),
9708
+ /* @__PURE__ */ jsxRuntime.jsx(
9709
+ Button,
9710
+ {
9711
+ type: "button",
9712
+ variant: "ghost",
9713
+ size: "sm",
9714
+ onClick: onRedo,
9715
+ disabled: !canRedo,
9716
+ "aria-label": "Redo",
9717
+ title: "Redo (Ctrl+Shift+Z)",
9718
+ className: "px-2",
9719
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { className: "h-3.5 w-3.5" })
9720
+ }
9721
+ )
9722
+ ] }) : null,
9568
9723
  errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9569
9724
  Button,
9570
9725
  {
@@ -9965,7 +10120,22 @@ function BlockKitchen(props) {
9965
10120
  Boolean(primaryAction && sendEnabled),
9966
10121
  "[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`."
9967
10122
  );
9968
- const { blocks, addBlock, addChild, updateBlock, removeBlock, duplicateBlock, reorderBlock, moveBlock, replaceAll } = useBlockKitchenState({ initialBlocks: seededBlocks, onChange });
10123
+ const {
10124
+ blocks,
10125
+ addBlock,
10126
+ addChild,
10127
+ updateBlock,
10128
+ removeBlock,
10129
+ duplicateBlock,
10130
+ reorderBlock,
10131
+ moveBlock,
10132
+ replaceAll,
10133
+ resetAll,
10134
+ undo,
10135
+ redo,
10136
+ canUndo,
10137
+ canRedo
10138
+ } = useBlockKitchenState({ initialBlocks: seededBlocks, onChange });
9969
10139
  const childParentById = React.useMemo(() => {
9970
10140
  const map = /* @__PURE__ */ new Map();
9971
10141
  for (const top of blocks) {
@@ -10081,6 +10251,30 @@ function BlockKitchen(props) {
10081
10251
  const handleDragCancel = React.useCallback(() => {
10082
10252
  setActivePaletteVariantId(null);
10083
10253
  }, []);
10254
+ const handleKeyDown = React.useCallback(
10255
+ (event) => {
10256
+ if (!(event.metaKey || event.ctrlKey) || event.altKey) {
10257
+ return;
10258
+ }
10259
+ const key = event.key.toLowerCase();
10260
+ const isUndo = key === "z" && !event.shiftKey;
10261
+ const isRedo = key === "y" || key === "z" && event.shiftKey;
10262
+ if (!isUndo && !isRedo) {
10263
+ return;
10264
+ }
10265
+ const target = event.target;
10266
+ if (target && (target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName))) {
10267
+ return;
10268
+ }
10269
+ event.preventDefault();
10270
+ if (isRedo) {
10271
+ if (canRedo) redo();
10272
+ } else if (canUndo) {
10273
+ undo();
10274
+ }
10275
+ },
10276
+ [canRedo, canUndo, redo, undo]
10277
+ );
10084
10278
  const activePaletteVariant = activePaletteVariantId ? variantById.get(activePaletteVariantId) : null;
10085
10279
  const blockPayloads = blocks.map((b) => b.block);
10086
10280
  return /* @__PURE__ */ jsxRuntime.jsx(BrandThemeScope, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(CustomEmojiProvider, { customEmojis, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -10092,91 +10286,102 @@ function BlockKitchen(props) {
10092
10286
  onDragEnd: handleDragEnd,
10093
10287
  onDragCancel: handleDragCancel,
10094
10288
  children: [
10095
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bk-root flex h-full w-full flex-col overflow-hidden rounded-md border bg-background text-foreground", children: [
10096
- /* @__PURE__ */ jsxRuntime.jsx(
10097
- Toolbar2,
10098
- {
10099
- onClear: () => replaceAll([]),
10100
- onOpenJson: () => setJsonOpen(true),
10101
- onOpenIssues: () => setIssuesOpen(true),
10102
- onOpenSend: () => setSendOpen(true),
10103
- onOpenPalette: () => setPaletteOpen(true),
10104
- canSend: blocks.length > 0,
10105
- canClear: blocks.length > 0,
10106
- errorCount: validation.total,
10107
- previewTheme,
10108
- onPreviewThemeChange: setUncontrolledPreviewTheme,
10109
- previewSurface,
10110
- onPreviewSurfaceChange: setPreviewSurface,
10111
- allowedSurfaces,
10112
- showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
10113
- docsLink,
10114
- showSend: sendEnabled,
10115
- primaryAction: !sendEnabled && primaryAction ? {
10116
- label: primaryAction.label,
10117
- onClick: () => primaryAction.onClick({
10118
- blocks: blockPayloads,
10119
- validation: validationSummary,
10120
- loadedMessage: activeEditTarget
10121
- }),
10122
- disabled: primaryAction.disableWhenInvalid ? !validationSummary.valid : false
10123
- } : null,
10124
- sendButtonLabel,
10125
- loadEnabled,
10126
- updateEnabled: Boolean(onUpdate),
10127
- editBadge: activeEditTarget ? {
10128
- channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
10129
- ts: activeEditTarget.ts
10130
- } : null,
10131
- onOpenLoad: () => setLoadOpen(true),
10132
- onOpenUpdate: () => setUpdateOpen(true),
10133
- onExitEdit: () => {
10134
- if (sendEnabled) {
10135
- setEditTarget(null);
10136
- replaceAll([]);
10137
- setLoadOpen(true);
10138
- return;
10289
+ /* @__PURE__ */ jsxRuntime.jsxs(
10290
+ "div",
10291
+ {
10292
+ className: "bk-root flex h-full w-full flex-col overflow-hidden rounded-md border bg-background text-foreground",
10293
+ onKeyDown: handleKeyDown,
10294
+ children: [
10295
+ /* @__PURE__ */ jsxRuntime.jsx(
10296
+ Toolbar2,
10297
+ {
10298
+ canUndo,
10299
+ canRedo,
10300
+ onUndo: undo,
10301
+ onRedo: redo,
10302
+ onClear: () => replaceAll([]),
10303
+ onOpenJson: () => setJsonOpen(true),
10304
+ onOpenIssues: () => setIssuesOpen(true),
10305
+ onOpenSend: () => setSendOpen(true),
10306
+ onOpenPalette: () => setPaletteOpen(true),
10307
+ canSend: blocks.length > 0,
10308
+ canClear: blocks.length > 0,
10309
+ errorCount: validation.total,
10310
+ previewTheme,
10311
+ onPreviewThemeChange: setUncontrolledPreviewTheme,
10312
+ previewSurface,
10313
+ onPreviewSurfaceChange: setPreviewSurface,
10314
+ allowedSurfaces,
10315
+ showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
10316
+ docsLink,
10317
+ showSend: sendEnabled,
10318
+ primaryAction: !sendEnabled && primaryAction ? {
10319
+ label: primaryAction.label,
10320
+ onClick: () => primaryAction.onClick({
10321
+ blocks: blockPayloads,
10322
+ validation: validationSummary,
10323
+ loadedMessage: activeEditTarget
10324
+ }),
10325
+ disabled: primaryAction.disableWhenInvalid ? !validationSummary.valid : false
10326
+ } : null,
10327
+ sendButtonLabel,
10328
+ loadEnabled,
10329
+ updateEnabled: Boolean(onUpdate),
10330
+ editBadge: activeEditTarget ? {
10331
+ channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
10332
+ ts: activeEditTarget.ts
10333
+ } : null,
10334
+ onOpenLoad: () => setLoadOpen(true),
10335
+ onOpenUpdate: () => setUpdateOpen(true),
10336
+ onExitEdit: () => {
10337
+ if (sendEnabled) {
10338
+ setEditTarget(null);
10339
+ resetAll([]);
10340
+ setLoadOpen(true);
10341
+ return;
10342
+ }
10343
+ setEditTarget(null);
10344
+ },
10345
+ loadButtonLabel,
10346
+ updateButtonLabel
10139
10347
  }
10140
- setEditTarget(null);
10141
- },
10142
- loadButtonLabel,
10143
- updateButtonLabel
10144
- }
10145
- ),
10146
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
10147
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden min-h-0 md:flex", children: /* @__PURE__ */ jsxRuntime.jsx(
10148
- Palette,
10149
- {
10150
- onAddBlock: (block) => addBlock(block),
10151
- sections: paletteSections,
10152
- showSearch: showPaletteSearch,
10153
- searchPlaceholder: paletteSearchPlaceholder,
10154
- defaultOpenSections
10155
- }
10156
- ) }),
10157
- /* @__PURE__ */ jsxRuntime.jsx(
10158
- Surface,
10159
- {
10160
- blocks,
10161
- workspaceName,
10162
- authorName: activeEditTarget?.username,
10163
- authorIcon: activeEditTarget?.iconUrl,
10164
- previewHooks: mergedPreviewHooks,
10165
- previewTheme,
10166
- previewSurface,
10167
- errorsByBlockId: validation.byBlockId,
10168
- openBlockId,
10169
- onOpenBlockChange: setOpenBlockId,
10170
- onUpdate: updateBlock,
10171
- onDuplicate: duplicateBlock,
10172
- onDelete: removeBlock,
10173
- onReorder: reorderBlock,
10174
- isPaletteDrag: activePaletteVariant !== null,
10175
- onOpenPalette: () => setPaletteOpen(true)
10176
- }
10177
- )
10178
- ] })
10179
- ] }),
10348
+ ),
10349
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
10350
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden min-h-0 md:flex", children: /* @__PURE__ */ jsxRuntime.jsx(
10351
+ Palette,
10352
+ {
10353
+ onAddBlock: (block) => addBlock(block),
10354
+ sections: paletteSections,
10355
+ showSearch: showPaletteSearch,
10356
+ searchPlaceholder: paletteSearchPlaceholder,
10357
+ defaultOpenSections
10358
+ }
10359
+ ) }),
10360
+ /* @__PURE__ */ jsxRuntime.jsx(
10361
+ Surface,
10362
+ {
10363
+ blocks,
10364
+ workspaceName,
10365
+ authorName: activeEditTarget?.username,
10366
+ authorIcon: activeEditTarget?.iconUrl,
10367
+ previewHooks: mergedPreviewHooks,
10368
+ previewTheme,
10369
+ previewSurface,
10370
+ errorsByBlockId: validation.byBlockId,
10371
+ openBlockId,
10372
+ onOpenBlockChange: setOpenBlockId,
10373
+ onUpdate: updateBlock,
10374
+ onDuplicate: duplicateBlock,
10375
+ onDelete: removeBlock,
10376
+ onReorder: reorderBlock,
10377
+ isPaletteDrag: activePaletteVariant !== null,
10378
+ onOpenPalette: () => setPaletteOpen(true)
10379
+ }
10380
+ )
10381
+ ] })
10382
+ ]
10383
+ }
10384
+ ),
10180
10385
  /* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { dropAnimation: null, children: activePaletteVariant ? /* @__PURE__ */ jsxRuntime.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: [
10181
10386
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-3 w-3 shrink-0" }),
10182
10387
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: activePaletteVariant.label })
@@ -10253,13 +10458,13 @@ function BlockKitchen(props) {
10253
10458
  loadRecentMessages: loading.loadRecentMessages,
10254
10459
  loadChannels: recentChannelSource,
10255
10460
  onLoaded: (result) => {
10256
- replaceAll(result.blocks);
10461
+ resetAll(result.blocks);
10257
10462
  setEditTarget(toLoadedMessage(result));
10258
10463
  setLoadOpen(false);
10259
10464
  },
10260
10465
  onOpenAsNew: (loadedBlocks) => {
10261
10466
  if (loadedBlocks) {
10262
- replaceAll(loadedBlocks);
10467
+ resetAll(loadedBlocks);
10263
10468
  }
10264
10469
  setEditTarget(null);
10265
10470
  setLoadOpen(false);