@tightknitai/block-kitchen 0.10.25 → 0.10.27

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
@@ -1958,6 +1958,36 @@ function CustomEmojiProvider({ customEmojis, children }) {
1958
1958
  function useCustomEmojis() {
1959
1959
  return React.useContext(CustomEmojiContext);
1960
1960
  }
1961
+ var DEFAULT_IMAGE_UPLOAD_ACCEPT = ["image/png", "image/jpeg", "image/gif"];
1962
+ var ImageUploadContext = React.createContext(null);
1963
+ function ImageUploadProvider({
1964
+ imageUpload,
1965
+ children
1966
+ }) {
1967
+ const uploadImage = imageUpload?.uploadImage;
1968
+ const accept = imageUpload?.accept;
1969
+ const value = React.useMemo(
1970
+ () => uploadImage ? { uploadImage, accept: accept ?? DEFAULT_IMAGE_UPLOAD_ACCEPT } : null,
1971
+ [uploadImage, accept]
1972
+ );
1973
+ return /* @__PURE__ */ jsxRuntime.jsx(ImageUploadContext.Provider, { value, children });
1974
+ }
1975
+ function useImageUpload() {
1976
+ return React.useContext(ImageUploadContext);
1977
+ }
1978
+ function isAcceptedImage(file, accept) {
1979
+ return accept.includes(file.type);
1980
+ }
1981
+ function uploadErrorMessage(error) {
1982
+ return error instanceof Error && error.message ? error.message : "Upload failed. Try again.";
1983
+ }
1984
+ function isFileDrag(dataTransfer) {
1985
+ return Boolean(dataTransfer && Array.from(dataTransfer.types).includes("Files"));
1986
+ }
1987
+ function formatList(accept) {
1988
+ const names = accept.map((type) => type.replace(/^image\//, "").toUpperCase());
1989
+ return names.length > 1 ? `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}` : names[0] ?? "";
1990
+ }
1961
1991
  var MentionsContext = React.createContext(null);
1962
1992
  function MentionsProvider({ mentions, children }) {
1963
1993
  return /* @__PURE__ */ jsxRuntime.jsx(MentionsContext.Provider, { value: mentions ?? null, children });
@@ -3269,6 +3299,13 @@ var PopoverContent = React__namespace.forwardRef(({ className, align = "start",
3269
3299
  }
3270
3300
  ) }));
3271
3301
  PopoverContent.displayName = PopoverPrimitive__namespace.Content.displayName;
3302
+ function returnFocusUnlessMoved(event) {
3303
+ const active = document.activeElement;
3304
+ const closing = event.target instanceof Node ? event.target : null;
3305
+ if (active && active !== document.body && !closing?.contains(active)) {
3306
+ event.preventDefault();
3307
+ }
3308
+ }
3272
3309
  function matchesQuery(name, query) {
3273
3310
  const needle = query.replace(/^\s*#\s*/, "").trim().toLowerCase();
3274
3311
  return needle === "" || name.toLowerCase().includes(needle);
@@ -4504,7 +4541,7 @@ function PaletteItem({
4504
4541
  {
4505
4542
  ref: setNodeRef,
4506
4543
  className: cn(
4507
- "group flex items-center gap-2 rounded px-2 py-1 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground has-[:focus-visible]:bg-accent has-[:focus-visible]:text-foreground",
4544
+ "group flex items-start gap-2 rounded px-2 py-1 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground has-[:focus-visible]:bg-accent has-[:focus-visible]:text-foreground",
4508
4545
  isDragging && "opacity-50"
4509
4546
  ),
4510
4547
  children: [
@@ -4512,16 +4549,12 @@ function PaletteItem({
4512
4549
  "div",
4513
4550
  {
4514
4551
  ref: setActivatorNodeRef,
4515
- className: "-my-1 flex min-w-0 flex-1 cursor-grab items-center gap-2 rounded py-1 active:cursor-grabbing focus-visible:outline-none",
4552
+ className: "-my-1 flex min-w-0 flex-1 cursor-grab items-start gap-2 rounded py-1 active:cursor-grabbing focus-visible:outline-none",
4516
4553
  ...attributes,
4517
4554
  ...listeners,
4518
4555
  children: [
4519
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-3.5 w-3.5 shrink-0" }),
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
4556
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "mt-[3px] h-3.5 w-3.5 shrink-0" }),
4557
+ /* @__PURE__ */ jsxRuntime.jsx(RailLabel, { variant })
4525
4558
  ]
4526
4559
  }
4527
4560
  ),
@@ -4546,13 +4579,15 @@ function LockedPaletteItem({
4546
4579
  }) {
4547
4580
  const select = variant.onLockedSelect;
4548
4581
  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"
4582
+ "flex w-full text-left text-muted-foreground",
4583
+ isSheet ? "min-h-11 items-center gap-3 rounded-md px-3 py-2 text-sm font-medium" : "items-start gap-2 rounded px-2 py-1 text-sm"
4551
4584
  );
4552
4585
  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
4586
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { "aria-hidden": true, className: cn("shrink-0", isSheet ? "h-4 w-4" : "mt-[3px] h-3.5 w-3.5") }),
4587
+ isSheet ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4588
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: variant.label }),
4589
+ variant.badge ? /* @__PURE__ */ jsxRuntime.jsx(VariantBadge, { badge: variant.badge }) : null
4590
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(RailLabel, { variant })
4556
4591
  ] });
4557
4592
  if (!select) {
4558
4593
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-disabled": "true", className, children: content });
@@ -4574,12 +4609,31 @@ function LockedPaletteItem({
4574
4609
  }
4575
4610
  );
4576
4611
  }
4612
+ function RailLabel({ variant }) {
4613
+ if (!variant.badge) {
4614
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: variant.label });
4615
+ }
4616
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col items-start gap-0.5", children: [
4617
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate", children: variant.label }),
4618
+ " ",
4619
+ /* @__PURE__ */ jsxRuntime.jsx(VariantBadge, { badge: variant.badge })
4620
+ ] });
4621
+ }
4577
4622
  function VariantBadge({ badge }) {
4578
4623
  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
- ] });
4624
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4625
+ "span",
4626
+ {
4627
+ className: cn(
4628
+ "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",
4629
+ badge.className
4630
+ ),
4631
+ children: [
4632
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "h-2.5 w-2.5 shrink-0" }) : null,
4633
+ badge.label
4634
+ ]
4635
+ }
4636
+ );
4583
4637
  }
4584
4638
  function SendDialog({
4585
4639
  open,
@@ -4743,6 +4797,73 @@ function SendDialog({
4743
4797
  ] })
4744
4798
  ] }) });
4745
4799
  }
4800
+ var ALT_TEXT_MAX_LENGTH = 2e3;
4801
+ function altTextFromFileName(file) {
4802
+ const name = file.name.replace(/\.[^.]+$/, "").trim();
4803
+ return (name || "Image").slice(0, ALT_TEXT_MAX_LENGTH);
4804
+ }
4805
+ function useCanvasImageDrop({
4806
+ upload,
4807
+ enabled,
4808
+ onAddBlock
4809
+ }) {
4810
+ const [dragging, setDragging] = React.useState(false);
4811
+ const [pending, setPending] = React.useState(0);
4812
+ const [error, setError] = React.useState(null);
4813
+ const onAddBlockRef = React.useRef(onAddBlock);
4814
+ onAddBlockRef.current = onAddBlock;
4815
+ const addFiles = async (activeUpload, files) => {
4816
+ const images = files.filter((file) => isAcceptedImage(file, activeUpload.accept));
4817
+ const skipped = files.length - images.length;
4818
+ setError(
4819
+ skipped > 0 ? `${skipped === 1 ? "1 file was" : `${skipped} files were`} skipped. Only ${formatList(activeUpload.accept)} images can be added.` : null
4820
+ );
4821
+ if (images.length === 0) {
4822
+ return;
4823
+ }
4824
+ setPending((count) => count + images.length);
4825
+ const results = await Promise.allSettled(images.map((file) => activeUpload.uploadImage(file)));
4826
+ setPending((count) => count - images.length);
4827
+ const failures = [];
4828
+ results.forEach((result, idx) => {
4829
+ const file = images[idx];
4830
+ if (result.status === "fulfilled") {
4831
+ onAddBlockRef.current?.({ type: "image", image_url: result.value.url, alt_text: altTextFromFileName(file) });
4832
+ } else {
4833
+ failures.push(`Couldn't upload "${file.name}": ${uploadErrorMessage(result.reason)}`);
4834
+ }
4835
+ });
4836
+ if (failures.length > 0) {
4837
+ setError(failures.join(" "));
4838
+ }
4839
+ };
4840
+ const isOwnEvent = (e) => isFileDrag(e.dataTransfer) && e.currentTarget.contains(e.target);
4841
+ const handlers = upload && enabled && onAddBlock ? {
4842
+ onDragOver: (e) => {
4843
+ if (!isOwnEvent(e)) {
4844
+ return;
4845
+ }
4846
+ e.preventDefault();
4847
+ e.dataTransfer.dropEffect = "copy";
4848
+ setDragging(true);
4849
+ },
4850
+ onDragLeave: (e) => {
4851
+ const next = e.relatedTarget;
4852
+ if (!next || !e.currentTarget.contains(next)) {
4853
+ setDragging(false);
4854
+ }
4855
+ },
4856
+ onDrop: (e) => {
4857
+ if (!isOwnEvent(e)) {
4858
+ return;
4859
+ }
4860
+ e.preventDefault();
4861
+ setDragging(false);
4862
+ void addFiles(upload, Array.from(e.dataTransfer.files));
4863
+ }
4864
+ } : void 0;
4865
+ return { handlers, dragging, pending, error, dismissError: () => setError(null) };
4866
+ }
4746
4867
  function BlockActionButtons({
4747
4868
  actions,
4748
4869
  position,
@@ -4997,6 +5118,129 @@ function AlertEditor({ block, onChange }) {
4997
5118
  ) })
4998
5119
  ] });
4999
5120
  }
5121
+ function ImageUrlInput({
5122
+ id,
5123
+ value,
5124
+ placeholder,
5125
+ onChange
5126
+ }) {
5127
+ const upload = useImageUpload();
5128
+ const input = /* @__PURE__ */ jsxRuntime.jsx(Input, { id, type: "url", value, placeholder, onChange: (e) => onChange(e.target.value) });
5129
+ if (!upload) {
5130
+ return input;
5131
+ }
5132
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
5133
+ /* @__PURE__ */ jsxRuntime.jsx(UploadDropZone, { upload, inputId: id, onUploaded: onChange, variant: "inline" }),
5134
+ input
5135
+ ] });
5136
+ }
5137
+ function UploadDropZone({
5138
+ upload,
5139
+ inputId,
5140
+ onUploaded,
5141
+ variant
5142
+ }) {
5143
+ const [uploading, setUploading] = React.useState(false);
5144
+ const [dragging, setDragging] = React.useState(false);
5145
+ const [error, setError] = React.useState(null);
5146
+ const fileInputRef = React.useRef(null);
5147
+ const onUploadedRef = React.useRef(onUploaded);
5148
+ onUploadedRef.current = onUploaded;
5149
+ const uploadFile = async (file) => {
5150
+ if (!isAcceptedImage(file, upload.accept)) {
5151
+ setError(`"${file.name}" isn't a supported image. Use ${formatList(upload.accept)}.`);
5152
+ return;
5153
+ }
5154
+ setError(null);
5155
+ setUploading(true);
5156
+ try {
5157
+ const { url } = await upload.uploadImage(file);
5158
+ onUploadedRef.current(url);
5159
+ } catch (err) {
5160
+ setError(uploadErrorMessage(err));
5161
+ } finally {
5162
+ setUploading(false);
5163
+ }
5164
+ };
5165
+ const onDragOver = (e) => {
5166
+ if (!isFileDrag(e.dataTransfer)) {
5167
+ return;
5168
+ }
5169
+ e.preventDefault();
5170
+ e.stopPropagation();
5171
+ setDragging(true);
5172
+ };
5173
+ const onDrop = (e) => {
5174
+ if (!isFileDrag(e.dataTransfer)) {
5175
+ return;
5176
+ }
5177
+ e.preventDefault();
5178
+ e.stopPropagation();
5179
+ setDragging(false);
5180
+ const [file] = Array.from(e.dataTransfer.files);
5181
+ if (file && !uploading) {
5182
+ void uploadFile(file);
5183
+ }
5184
+ };
5185
+ const fileInput = /* @__PURE__ */ jsxRuntime.jsx(
5186
+ "input",
5187
+ {
5188
+ ref: fileInputRef,
5189
+ id: `${inputId}-file`,
5190
+ type: "file",
5191
+ accept: upload.accept.join(","),
5192
+ className: "hidden",
5193
+ tabIndex: -1,
5194
+ "aria-hidden": "true",
5195
+ onChange: (e) => {
5196
+ const file = e.target.files?.[0];
5197
+ e.target.value = "";
5198
+ if (file) {
5199
+ void uploadFile(file);
5200
+ }
5201
+ }
5202
+ }
5203
+ );
5204
+ const pickButton = (label) => /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", className: "h-7", disabled: uploading, onClick: () => fileInputRef.current?.click(), children: label });
5205
+ const dropHandlers = { onDragOver, onDragLeave: () => setDragging(false), onDrop };
5206
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5207
+ variant === "panel" ? /* @__PURE__ */ jsxRuntime.jsxs(
5208
+ "section",
5209
+ {
5210
+ "aria-label": "Upload an image",
5211
+ ...dropHandlers,
5212
+ className: cn(
5213
+ "flex flex-col items-center justify-center gap-2 rounded-md border border-dashed px-4 py-6 text-center text-xs text-muted-foreground transition-colors",
5214
+ dragging && "border-primary bg-primary/5 text-primary"
5215
+ ),
5216
+ children: [
5217
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: cn("h-5 w-5", uploading && "animate-pulse"), "aria-hidden": "true" }),
5218
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-live": "polite", children: uploading ? "Uploading\u2026" : dragging ? "Drop to upload" : "Drag an image here, or" }),
5219
+ pickButton("Choose file"),
5220
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: formatList(upload.accept) }),
5221
+ fileInput
5222
+ ]
5223
+ }
5224
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
5225
+ "section",
5226
+ {
5227
+ "aria-label": "Upload an image",
5228
+ ...dropHandlers,
5229
+ className: cn(
5230
+ "flex items-center gap-2 rounded-md border border-dashed px-3 py-2 text-[11px] text-muted-foreground transition-colors",
5231
+ dragging && "border-primary bg-primary/5 text-primary"
5232
+ ),
5233
+ children: [
5234
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: cn("h-3.5 w-3.5 shrink-0", uploading && "animate-pulse"), "aria-hidden": "true" }),
5235
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", "aria-live": "polite", children: uploading ? "Uploading\u2026" : dragging ? "Drop to upload" : "Drop an image here, or paste a URL below" }),
5236
+ pickButton("Upload image"),
5237
+ fileInput
5238
+ ]
5239
+ }
5240
+ ),
5241
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-[11px] leading-snug text-destructive", children: error }) : null
5242
+ ] });
5243
+ }
5000
5244
  function CardEditor({
5001
5245
  block,
5002
5246
  onChange,
@@ -5103,13 +5347,12 @@ function ImageFields({
5103
5347
  };
5104
5348
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
5105
5349
  /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label, help, htmlFor: `${idPrefix}-url`, children: /* @__PURE__ */ jsxRuntime.jsx(
5106
- Input,
5350
+ ImageUrlInput,
5107
5351
  {
5108
5352
  id: `${idPrefix}-url`,
5109
- type: "url",
5110
5353
  value: url,
5111
5354
  placeholder: "e.g. https://example.com/image.png",
5112
- onChange: (e) => update({ url: e.target.value })
5355
+ onChange: (next) => update({ url: next })
5113
5356
  }
5114
5357
  ) }),
5115
5358
  /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Alt text", help: "Describes the image for screen readers.", htmlFor: `${idPrefix}-alt`, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -7669,15 +7912,14 @@ function ContainerEditor({ block, onChange }) {
7669
7912
  }
7670
7913
  ) : null,
7671
7914
  /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Icon URL", help: "Optional image shown left of the title.", htmlFor: "container-icon", children: /* @__PURE__ */ jsxRuntime.jsx(
7672
- Input,
7915
+ ImageUrlInput,
7673
7916
  {
7674
7917
  id: "container-icon",
7675
- type: "url",
7676
7918
  value: iconUrl,
7677
7919
  placeholder: "https://example.com/icon.png",
7678
- onChange: (e) => onChange({
7920
+ onChange: (url) => onChange({
7679
7921
  ...block,
7680
- icon: e.target.value ? { type: "image", image_url: e.target.value, alt_text: iconAlt || "Container icon" } : void 0
7922
+ icon: url ? { type: "image", image_url: url, alt_text: iconAlt || "Container icon" } : void 0
7681
7923
  })
7682
7924
  }
7683
7925
  ) }),
@@ -8175,13 +8417,12 @@ function ImageItemCard({
8175
8417
  help: "A publicly accessible image URL. Best at small sizes (around 20px).",
8176
8418
  htmlFor: `ctx-img-url-${index}`,
8177
8419
  children: /* @__PURE__ */ jsxRuntime.jsx(
8178
- Input,
8420
+ ImageUrlInput,
8179
8421
  {
8180
8422
  id: `ctx-img-url-${index}`,
8181
- type: "url",
8182
8423
  value: element.image_url,
8183
8424
  placeholder: "e.g. https://example.com/avatar.png",
8184
- onChange: (e) => onChange({ ...element, image_url: e.target.value })
8425
+ onChange: (url) => onChange({ ...element, image_url: url })
8185
8426
  }
8186
8427
  )
8187
8428
  }
@@ -8923,36 +9164,44 @@ function HeaderEditor({ block, onChange }) {
8923
9164
  ] });
8924
9165
  }
8925
9166
  function ImageEditor({ block, onChange }) {
8926
- const source = "slack_file" in block ? "slack_file" : "url";
9167
+ const upload = useImageUpload();
9168
+ const [urlSource, setUrlSource] = React.useState(upload ? "upload" : "url");
9169
+ const isSlackFile = "slack_file" in block;
9170
+ const source = isSlackFile ? "slack_file" : upload ? urlSource : "url";
8927
9171
  const titleText = block.title?.text ?? "";
8928
9172
  const setSource = (next) => {
8929
9173
  if (next === source) {
8930
9174
  return;
8931
9175
  }
8932
- if (next === "url") {
9176
+ if (next !== "slack_file") {
9177
+ setUrlSource(next);
9178
+ }
9179
+ if (next !== "slack_file" && isSlackFile) {
8933
9180
  const { slack_file: _omit, ...rest } = block;
8934
9181
  onChange({ image_url: "", ...rest });
8935
- } else {
9182
+ } else if (next === "slack_file") {
8936
9183
  const { image_url: _omit, ...rest } = block;
8937
9184
  onChange({ slack_file: { id: "" }, ...rest });
8938
9185
  }
8939
9186
  };
9187
+ const sourceOptions = [
9188
+ ...upload ? [["upload", "Upload image"]] : [],
9189
+ ["url", "Public URL"],
9190
+ ["slack_file", "Slack file"]
9191
+ ];
8940
9192
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
8941
9193
  /* @__PURE__ */ jsxRuntime.jsx(
8942
9194
  EditorField,
8943
9195
  {
8944
9196
  label: "Image source",
8945
- help: "Where the image comes from: a public URL, or a file already uploaded to your Slack workspace.",
9197
+ help: upload ? "Where the image comes from: a file from your device, a public URL, or a file already uploaded to your Slack workspace." : "Where the image comes from: a public URL, or a file already uploaded to your Slack workspace.",
8946
9198
  children: /* @__PURE__ */ jsxRuntime.jsx(
8947
9199
  RadioGroup,
8948
9200
  {
8949
9201
  value: source,
8950
9202
  onValueChange: (v) => setSource(v),
8951
9203
  className: "flex flex-row flex-wrap gap-3",
8952
- children: [
8953
- ["url", "Public URL"],
8954
- ["slack_file", "Slack file"]
8955
- ].map(([value, label]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
9204
+ children: sourceOptions.map(([value, label]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
8956
9205
  /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value, id: `img-source-${value}` }),
8957
9206
  /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `img-source-${value}`, className: "text-xs", children: label })
8958
9207
  ] }, value))
@@ -8960,7 +9209,15 @@ function ImageEditor({ block, onChange }) {
8960
9209
  )
8961
9210
  }
8962
9211
  ),
8963
- "slack_file" in block ? /* @__PURE__ */ jsxRuntime.jsx(SlackFileFields, { block, onChange }) : /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Image URL", help: "A publicly accessible image URL (PNG, JPG, GIF).", htmlFor: "img-url", children: /* @__PURE__ */ jsxRuntime.jsx(
9212
+ "slack_file" in block ? /* @__PURE__ */ jsxRuntime.jsx(SlackFileFields, { block, onChange }) : source === "upload" && upload ? /* @__PURE__ */ jsxRuntime.jsx(
9213
+ UploadDropZone,
9214
+ {
9215
+ upload,
9216
+ inputId: "img-upload",
9217
+ variant: "panel",
9218
+ onUploaded: (url) => onChange({ ...block, image_url: url })
9219
+ }
9220
+ ) : /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Image URL", help: "A publicly accessible image URL (PNG, JPG, GIF).", htmlFor: "img-url", children: /* @__PURE__ */ jsxRuntime.jsx(
8964
9221
  Input,
8965
9222
  {
8966
9223
  id: "img-url",
@@ -10143,13 +10400,12 @@ function ImageAccessoryFields({ image, onChange }) {
10143
10400
  help: "A publicly accessible image URL (PNG, JPG, GIF).",
10144
10401
  htmlFor: "section-acc-img-url",
10145
10402
  children: /* @__PURE__ */ jsxRuntime.jsx(
10146
- Input,
10403
+ ImageUrlInput,
10147
10404
  {
10148
10405
  id: "section-acc-img-url",
10149
- type: "url",
10150
10406
  value: image.image_url ?? "",
10151
10407
  placeholder: "e.g. https://example.com/thumb.png",
10152
- onChange: (e) => onChange({ ...image, image_url: e.target.value })
10408
+ onChange: (url) => onChange({ ...image, image_url: url })
10153
10409
  }
10154
10410
  )
10155
10411
  }
@@ -10512,10 +10768,24 @@ function BlockEditor({
10512
10768
  const custom = useCustomBlockEditor(block);
10513
10769
  const CustomForm = custom?.component;
10514
10770
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
10515
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-sm font-semibold text-foreground", children: [
10516
- "Edit ",
10517
- custom ? custom.label : labelForBlockType(block.type)
10518
- ] }) }),
10771
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
10772
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-sm font-semibold text-foreground", children: [
10773
+ "Edit ",
10774
+ custom ? custom.label : labelForBlockType(block.type)
10775
+ ] }),
10776
+ onDone ? /* @__PURE__ */ jsxRuntime.jsx(
10777
+ Button,
10778
+ {
10779
+ type: "button",
10780
+ variant: "ghost",
10781
+ size: "icon",
10782
+ "aria-label": "Close editor",
10783
+ className: "-my-1 -mr-1 h-7 w-7 text-muted-foreground",
10784
+ onClick: onDone,
10785
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {})
10786
+ }
10787
+ ) : null
10788
+ ] }),
10519
10789
  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,
10520
10790
  CustomForm ? /* @__PURE__ */ jsxRuntime.jsx(CustomForm, { block, onChange }) : dispatch(block, onChange),
10521
10791
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -10677,15 +10947,23 @@ function BlockRow({
10677
10947
  children: preview
10678
10948
  }
10679
10949
  ) }),
10680
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10681
- BlockEditor,
10950
+ /* @__PURE__ */ jsxRuntime.jsx(
10951
+ PopoverContent,
10682
10952
  {
10683
- block: builderBlock.block,
10684
- errors,
10685
- onChange: (next) => onUpdate(builderBlock.id, next),
10686
- onDone: () => onOpenChange?.(false)
10953
+ className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]",
10954
+ align: "start",
10955
+ onCloseAutoFocus: returnFocusUnlessMoved,
10956
+ children: /* @__PURE__ */ jsxRuntime.jsx(
10957
+ BlockEditor,
10958
+ {
10959
+ block: builderBlock.block,
10960
+ errors,
10961
+ onChange: (next) => onUpdate(builderBlock.id, next),
10962
+ onDone: () => onOpenChange?.(false)
10963
+ }
10964
+ )
10687
10965
  }
10688
- ) })
10966
+ )
10689
10967
  ] });
10690
10968
  const adornment = position && renderBlockAdornment?.(position);
10691
10969
  const hoverChrome = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -10951,7 +11229,7 @@ function ContainerRow({
10951
11229
  hasErrors ? "border-destructive/60" : "border-border"
10952
11230
  ),
10953
11231
  children: [
10954
- /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: (open) => onOpenBlockChange?.(open ? id : null), children: [
11232
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: (open) => onOpenBlockChange?.(id, open), children: [
10955
11233
  /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10956
11234
  "div",
10957
11235
  {
@@ -10963,7 +11241,7 @@ function ContainerRow({
10963
11241
  onKeyDown: (e) => {
10964
11242
  if (e.key === "Enter" || e.key === " ") {
10965
11243
  e.preventDefault();
10966
- onOpenBlockChange?.(!isOpen ? id : null);
11244
+ onOpenBlockChange?.(id, !isOpen);
10967
11245
  }
10968
11246
  },
10969
11247
  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",
@@ -10977,15 +11255,23 @@ function ContainerRow({
10977
11255
  ]
10978
11256
  }
10979
11257
  ) }),
10980
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]", align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
10981
- BlockEditor,
11258
+ /* @__PURE__ */ jsxRuntime.jsx(
11259
+ PopoverContent,
10982
11260
  {
10983
- block,
10984
- errors,
10985
- onChange: (next) => onUpdate(id, next),
10986
- onDone: () => onOpenBlockChange?.(null)
11261
+ className: "w-[min(32rem,calc(100vw-1.5rem))] sm:w-[32rem]",
11262
+ align: "start",
11263
+ onCloseAutoFocus: returnFocusUnlessMoved,
11264
+ children: /* @__PURE__ */ jsxRuntime.jsx(
11265
+ BlockEditor,
11266
+ {
11267
+ block,
11268
+ errors,
11269
+ onChange: (next) => onUpdate(id, next),
11270
+ onDone: () => onOpenBlockChange?.(id, false)
11271
+ }
11272
+ )
10987
11273
  }
10988
- ) })
11274
+ )
10989
11275
  ] }),
10990
11276
  /* @__PURE__ */ jsxRuntime.jsx(
10991
11277
  "div",
@@ -11014,7 +11300,7 @@ function ContainerRow({
11014
11300
  previewTheme,
11015
11301
  errors: errorsByBlockId?.get(child.id),
11016
11302
  isOpen: openBlockId === child.id,
11017
- onOpenChange: (open) => onOpenBlockChange?.(open ? child.id : null),
11303
+ onOpenChange: (open) => onOpenBlockChange?.(child.id, open),
11018
11304
  onUpdate,
11019
11305
  onDuplicate,
11020
11306
  onDelete,
@@ -11049,7 +11335,7 @@ function ContainerRow({
11049
11335
  {
11050
11336
  type: "button",
11051
11337
  "aria-label": `Show ${errors.length} validation ${errors.length === 1 ? "issue" : "issues"}`,
11052
- onClick: () => onOpenBlockChange?.(id),
11338
+ onClick: () => onOpenBlockChange?.(id, true),
11053
11339
  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
11340
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" })
11055
11341
  }
@@ -11068,7 +11354,7 @@ function ContainerRow({
11068
11354
  /* @__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
11355
  ] }) : null,
11070
11356
  /* @__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" }) }),
11357
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Edit", onClick: () => onOpenBlockChange?.(id, true), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }) }),
11072
11358
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton2, { label: "Duplicate", onClick: () => onDuplicate(id), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" }) }),
11073
11359
  /* @__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
11360
  ]
@@ -11101,7 +11387,7 @@ function ContainerRow({
11101
11387
  {
11102
11388
  type: "button",
11103
11389
  "aria-label": "Edit container",
11104
- onClick: () => onOpenBlockChange?.(id),
11390
+ onClick: () => onOpenBlockChange?.(id, true),
11105
11391
  className: "flex h-9 w-9 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
11106
11392
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4" })
11107
11393
  }
@@ -11179,10 +11465,14 @@ function Surface({
11179
11465
  isPaletteDrag = false,
11180
11466
  onOpenPalette,
11181
11467
  blockActions,
11182
- renderBlockAdornment
11468
+ renderBlockAdornment,
11469
+ onAddBlock,
11470
+ imageDropEnabled = true
11183
11471
  }) {
11184
11472
  const { setNodeRef, isOver } = core.useDroppable({ id: SURFACE_DROPPABLE_ID });
11185
11473
  const isDark = previewTheme === "dark";
11474
+ const imageDrop = useCanvasImageDrop({ upload: useImageUpload(), enabled: imageDropEnabled, onAddBlock });
11475
+ const isDropTarget = isPaletteDrag || imageDrop.dragging;
11186
11476
  const showEndDropZone = isPaletteDrag && isOver && blocks.length > 0;
11187
11477
  const blocksList = /* @__PURE__ */ jsxRuntime.jsx(
11188
11478
  "div",
@@ -11191,10 +11481,19 @@ function Surface({
11191
11481
  className: cn(
11192
11482
  "flex min-h-[180px] flex-col px-3 py-2 transition-colors sm:min-h-[240px] sm:px-5",
11193
11483
  isDark ? "bg-[#1a1d21]" : "bg-white",
11194
- isPaletteDrag && (isDark ? "bg-[#22262c] outline-2 outline-primary/40 -outline-offset-2 outline-dashed" : "bg-[#f5f8ff] outline-2 outline-primary/40 -outline-offset-2 outline-dashed"),
11195
- isPaletteDrag && isOver && (isDark ? "bg-[#2c3036]" : "bg-[#eaf0ff]")
11484
+ isDropTarget && (isDark ? "bg-[#22262c] outline-2 outline-primary/40 -outline-offset-2 outline-dashed" : "bg-[#f5f8ff] outline-2 outline-primary/40 -outline-offset-2 outline-dashed"),
11485
+ (isPaletteDrag && isOver || imageDrop.dragging) && (isDark ? "bg-[#2c3036]" : "bg-[#eaf0ff]")
11196
11486
  ),
11197
- children: blocks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { isDark, isPaletteDrag, isOver, onOpenPalette }) : /* @__PURE__ */ jsxRuntime.jsxs(sortable.SortableContext, { items: blocks.map((b) => b.id), strategy: sortable.verticalListSortingStrategy, children: [
11487
+ children: blocks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
11488
+ EmptyState,
11489
+ {
11490
+ isDark,
11491
+ isPaletteDrag,
11492
+ isOver,
11493
+ isFileDrag: imageDrop.dragging,
11494
+ onOpenPalette
11495
+ }
11496
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(sortable.SortableContext, { items: blocks.map((b) => b.id), strategy: sortable.verticalListSortingStrategy, children: [
11198
11497
  blocks.map(
11199
11498
  (block, idx) => block.block.type === "container" ? /* @__PURE__ */ jsxRuntime.jsx(
11200
11499
  ContainerRow,
@@ -11224,7 +11523,7 @@ function Surface({
11224
11523
  previewTheme,
11225
11524
  errors: errorsByBlockId?.get(block.id),
11226
11525
  isOpen: openBlockId === block.id,
11227
- onOpenChange: (open) => onOpenBlockChange?.(open ? block.id : null),
11526
+ onOpenChange: (open) => onOpenBlockChange?.(block.id, open),
11228
11527
  onUpdate,
11229
11528
  onDuplicate,
11230
11529
  onDelete,
@@ -11239,7 +11538,7 @@ function Surface({
11239
11538
  block.id
11240
11539
  )
11241
11540
  ),
11242
- showEndDropZone ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator2, {}) : null
11541
+ showEndDropZone || imageDrop.dragging ? /* @__PURE__ */ jsxRuntime.jsx(DropIndicator2, {}) : null
11243
11542
  ] })
11244
11543
  }
11245
11544
  );
@@ -11248,16 +11547,28 @@ function Surface({
11248
11547
  {
11249
11548
  "aria-label": "Block preview",
11250
11549
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
11251
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-2xl", children: previewSurface === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsxRuntime.jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsxRuntime.jsx(
11252
- SlackMessageFrame,
11253
- {
11254
- workspaceName,
11255
- authorName,
11256
- authorIcon,
11257
- isDark,
11258
- children: blocksList
11259
- }
11260
- ) })
11550
+ ...imageDrop.handlers,
11551
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-2xl", children: [
11552
+ imageDrop.handlers ? /* @__PURE__ */ jsxRuntime.jsx(
11553
+ ImageDropStatus,
11554
+ {
11555
+ dragging: imageDrop.dragging,
11556
+ pending: imageDrop.pending,
11557
+ error: imageDrop.error,
11558
+ onDismissError: imageDrop.dismissError
11559
+ }
11560
+ ) : null,
11561
+ previewSurface === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsxRuntime.jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsxRuntime.jsx(
11562
+ SlackMessageFrame,
11563
+ {
11564
+ workspaceName,
11565
+ authorName,
11566
+ authorIcon,
11567
+ isDark,
11568
+ children: blocksList
11569
+ }
11570
+ )
11571
+ ] })
11261
11572
  }
11262
11573
  );
11263
11574
  }
@@ -11386,9 +11697,10 @@ function EmptyState({
11386
11697
  isDark,
11387
11698
  isPaletteDrag,
11388
11699
  isOver,
11700
+ isFileDrag: isFileDrag2 = false,
11389
11701
  onOpenPalette
11390
11702
  }) {
11391
- const active = isPaletteDrag && isOver;
11703
+ const active = isPaletteDrag && isOver || isFileDrag2;
11392
11704
  return /* @__PURE__ */ jsxRuntime.jsxs(
11393
11705
  "div",
11394
11706
  {
@@ -11415,7 +11727,7 @@ function EmptyState({
11415
11727
  "text-sm font-semibold transition-colors",
11416
11728
  active ? "text-primary" : isDark ? "text-white" : "text-[#1d1c1d]"
11417
11729
  ),
11418
- children: active ? "Drop to add block" : "Start adding blocks!"
11730
+ children: isFileDrag2 ? "Drop to add image" : active ? "Drop to add block" : "Start adding blocks!"
11419
11731
  }
11420
11732
  ),
11421
11733
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: active ? "Release to insert it here." : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -11439,6 +11751,40 @@ function EmptyState({
11439
11751
  }
11440
11752
  );
11441
11753
  }
11754
+ function ImageDropStatus({
11755
+ dragging,
11756
+ pending,
11757
+ error,
11758
+ onDismissError
11759
+ }) {
11760
+ const progress = dragging ? "Drop images to add them at the end of the message." : pending > 0 ? `Uploading ${pending === 1 ? "1 image" : `${pending} images`}\u2026` : null;
11761
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11762
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: "empty:hidden", children: progress ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mb-2 flex items-center gap-1.5 text-xs text-muted-foreground", children: [
11763
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImagePlus, { className: cn("h-3.5 w-3.5", pending > 0 && !dragging && "animate-pulse"), "aria-hidden": "true" }),
11764
+ progress
11765
+ ] }) : null }),
11766
+ error ? /* @__PURE__ */ jsxRuntime.jsxs(
11767
+ "div",
11768
+ {
11769
+ role: "alert",
11770
+ className: "mb-2 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
11771
+ children: [
11772
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1", children: error }),
11773
+ /* @__PURE__ */ jsxRuntime.jsx(
11774
+ "button",
11775
+ {
11776
+ type: "button",
11777
+ "aria-label": "Dismiss",
11778
+ onClick: onDismissError,
11779
+ className: "rounded p-0.5 hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
11780
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" })
11781
+ }
11782
+ )
11783
+ ]
11784
+ }
11785
+ ) : null
11786
+ ] });
11787
+ }
11442
11788
  function DropIndicator2() {
11443
11789
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "pointer-events-none relative my-1 h-0.5 w-full rounded-full bg-primary", 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)]" }) });
11444
11790
  }
@@ -12001,6 +12347,7 @@ function BlockKitchen(props) {
12001
12347
  blockEditors,
12002
12348
  customEmojis,
12003
12349
  mentions,
12350
+ imageUpload,
12004
12351
  loadChannels,
12005
12352
  loadSendAsUserStatus,
12006
12353
  onSend,
@@ -12131,6 +12478,9 @@ function BlockKitchen(props) {
12131
12478
  setPaletteAdvanced(false);
12132
12479
  }
12133
12480
  const [openBlockId, setOpenBlockId] = React.useState(null);
12481
+ const setBlockOpen = React.useCallback((id, open) => {
12482
+ setOpenBlockId((current) => open ? id : current === id ? null : current);
12483
+ }, []);
12134
12484
  const [uncontrolledPreviewTheme, setUncontrolledPreviewTheme] = React.useState(defaultPreviewTheme);
12135
12485
  const isPreviewThemeControlled = controlledPreviewTheme !== void 0;
12136
12486
  const previewTheme = controlledPreviewTheme ?? uncontrolledPreviewTheme;
@@ -12384,7 +12734,7 @@ function BlockKitchen(props) {
12384
12734
  }
12385
12735
  )
12386
12736
  ] }),
12387
- /* @__PURE__ */ jsxRuntime.jsx(MentionsProvider, { mentions, children: /* @__PURE__ */ jsxRuntime.jsx(BlockEditorsProvider, { blockEditors, children: /* @__PURE__ */ jsxRuntime.jsx(
12737
+ /* @__PURE__ */ jsxRuntime.jsx(MentionsProvider, { mentions, children: /* @__PURE__ */ jsxRuntime.jsx(ImageUploadProvider, { imageUpload, children: /* @__PURE__ */ jsxRuntime.jsx(BlockEditorsProvider, { blockEditors, children: /* @__PURE__ */ jsxRuntime.jsx(
12388
12738
  Surface,
12389
12739
  {
12390
12740
  blocks,
@@ -12396,7 +12746,7 @@ function BlockKitchen(props) {
12396
12746
  previewSurface,
12397
12747
  errorsByBlockId: validation.byBlockId,
12398
12748
  openBlockId,
12399
- onOpenBlockChange: setOpenBlockId,
12749
+ onOpenBlockChange: setBlockOpen,
12400
12750
  onUpdate: updateBlock,
12401
12751
  onDuplicate: duplicateBlock,
12402
12752
  onDelete: removeBlock,
@@ -12404,9 +12754,11 @@ function BlockKitchen(props) {
12404
12754
  isPaletteDrag: activePaletteVariant !== null,
12405
12755
  onOpenPalette: () => setPaletteOpen(true),
12406
12756
  blockActions,
12407
- renderBlockAdornment
12757
+ renderBlockAdornment,
12758
+ onAddBlock: addBlock,
12759
+ imageDropEnabled: !disabledBlockTypes?.includes("image")
12408
12760
  }
12409
- ) }) })
12761
+ ) }) }) })
12410
12762
  ] })
12411
12763
  ]
12412
12764
  }