@tightknitai/block-kitchen 0.8.9 → 0.8.11

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, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, MoreHorizontal, Send, Search, CheckCircle2, Check, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Pencil, Copy, 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, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, Pencil, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, Send, Search, Info, CheckCircle2, Check, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Copy, Type, Bold, Italic, Strikethrough, Underline, Code, List, ListOrdered, Quote, Undo2, Redo2, Smile, Link as Link$1 } from 'lucide-react';
4
4
  import * as React from 'react';
5
5
  import { createContext, useMemo, useState, useCallback, useRef, useEffect, useId, useContext } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -2694,6 +2694,91 @@ function JsonDrawer({
2694
2694
  ] })
2695
2695
  ] }) });
2696
2696
  }
2697
+ var buttonVariants = cva(
2698
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2699
+ {
2700
+ variants: {
2701
+ variant: {
2702
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2703
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2704
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2705
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2706
+ ghost: "hover:bg-accent hover:text-accent-foreground",
2707
+ link: "text-primary underline-offset-4 hover:underline"
2708
+ },
2709
+ size: {
2710
+ default: "h-9 px-4 py-2",
2711
+ sm: "h-8 rounded-md px-3 text-xs",
2712
+ lg: "h-10 rounded-md px-8",
2713
+ icon: "h-9 w-9"
2714
+ }
2715
+ },
2716
+ defaultVariants: {
2717
+ variant: "default",
2718
+ size: "default"
2719
+ }
2720
+ }
2721
+ );
2722
+ var Button = React.forwardRef(
2723
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
2724
+ const Comp = asChild ? Slot : "button";
2725
+ return /* @__PURE__ */ jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
2726
+ }
2727
+ );
2728
+ Button.displayName = "Button";
2729
+ var Dialog = DialogPrimitive.Root;
2730
+ var DialogPortal = DialogPrimitive.Portal;
2731
+ var DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2732
+ DialogPrimitive.Overlay,
2733
+ {
2734
+ ref,
2735
+ className: cn(
2736
+ "bk-portal-content fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2737
+ className
2738
+ ),
2739
+ ...props
2740
+ }
2741
+ ));
2742
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
2743
+ var DialogContent = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
2744
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
2745
+ /* @__PURE__ */ jsxs(
2746
+ DialogPrimitive.Content,
2747
+ {
2748
+ ref,
2749
+ className: cn(
2750
+ "bk-portal-content fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg",
2751
+ className
2752
+ ),
2753
+ ...props,
2754
+ children: [
2755
+ children,
2756
+ /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring", children: [
2757
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
2758
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2759
+ ] })
2760
+ ]
2761
+ }
2762
+ )
2763
+ ] }));
2764
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
2765
+ function DialogHeader({ className, ...props }) {
2766
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
2767
+ }
2768
+ function DialogFooter({ className, ...props }) {
2769
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props });
2770
+ }
2771
+ var DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2772
+ DialogPrimitive.Title,
2773
+ {
2774
+ ref,
2775
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
2776
+ ...props
2777
+ }
2778
+ ));
2779
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
2780
+ var DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
2781
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
2697
2782
  var Input = React.forwardRef(
2698
2783
  ({ className, type, ...props }, ref) => /* @__PURE__ */ jsx(
2699
2784
  "input",
@@ -2709,6 +2794,213 @@ var Input = React.forwardRef(
2709
2794
  )
2710
2795
  );
2711
2796
  Input.displayName = "Input";
2797
+ var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2798
+ LabelPrimitive.Root,
2799
+ {
2800
+ ref,
2801
+ className: cn("text-sm font-medium leading-none text-foreground", className),
2802
+ ...props
2803
+ }
2804
+ ));
2805
+ Label.displayName = LabelPrimitive.Root.displayName;
2806
+ function recentToResult(msg) {
2807
+ return {
2808
+ ok: true,
2809
+ channelId: msg.channelId,
2810
+ channelName: msg.channelName,
2811
+ ts: msg.ts,
2812
+ blocks: msg.blocks,
2813
+ editableVia: msg.editableVia ?? "bot",
2814
+ workspaceName: msg.workspaceName,
2815
+ username: msg.username,
2816
+ iconUrl: msg.iconUrl
2817
+ };
2818
+ }
2819
+ function LoadMessageDialog({
2820
+ open,
2821
+ onOpenChange,
2822
+ onLoadMessage,
2823
+ loadRecentMessages,
2824
+ onLoaded,
2825
+ onOpenAsNew
2826
+ }) {
2827
+ const [link, setLink] = useState("");
2828
+ const [status, setStatus] = useState({ kind: "idle" });
2829
+ const [recent, setRecent] = useState(null);
2830
+ const [recentError, setRecentError] = useState(null);
2831
+ const inputRef = useRef(null);
2832
+ const onLoadMessageRef = useRef(onLoadMessage);
2833
+ const loadRecentMessagesRef = useRef(loadRecentMessages);
2834
+ useEffect(() => {
2835
+ onLoadMessageRef.current = onLoadMessage;
2836
+ loadRecentMessagesRef.current = loadRecentMessages;
2837
+ });
2838
+ const hasRecent = !!loadRecentMessages;
2839
+ useEffect(() => {
2840
+ if (!open) {
2841
+ return;
2842
+ }
2843
+ setLink("");
2844
+ setStatus({ kind: "idle" });
2845
+ if (!loadRecentMessagesRef.current) {
2846
+ setRecent([]);
2847
+ setRecentError(null);
2848
+ return;
2849
+ }
2850
+ setRecent(null);
2851
+ setRecentError(null);
2852
+ let cancelled = false;
2853
+ loadRecentMessagesRef.current().then((list) => {
2854
+ if (!cancelled) {
2855
+ setRecent(list);
2856
+ }
2857
+ }).catch((e) => {
2858
+ if (!cancelled) {
2859
+ setRecentError(e instanceof Error ? e.message : "Failed to load recent messages.");
2860
+ }
2861
+ });
2862
+ return () => {
2863
+ cancelled = true;
2864
+ };
2865
+ }, [open]);
2866
+ const handleLoad = async () => {
2867
+ const trimmed = link.trim();
2868
+ if (!trimmed) {
2869
+ setStatus({ kind: "error", error: "Paste a Slack message link first." });
2870
+ return;
2871
+ }
2872
+ setStatus({ kind: "loading" });
2873
+ try {
2874
+ const result = await onLoadMessageRef.current({ link: trimmed });
2875
+ if (result.ok) {
2876
+ onLoaded(result);
2877
+ return;
2878
+ }
2879
+ setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks });
2880
+ } catch (e) {
2881
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
2882
+ }
2883
+ };
2884
+ const notEditable = status.kind === "not-editable" ? status : null;
2885
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2886
+ DialogContent,
2887
+ {
2888
+ className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg",
2889
+ onOpenAutoFocus: (e) => {
2890
+ e.preventDefault();
2891
+ inputRef.current?.focus();
2892
+ },
2893
+ children: [
2894
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
2895
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Edit an existing message" }),
2896
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5", children: [
2897
+ /* @__PURE__ */ jsx(DialogDescription, { children: hasRecent ? "Paste a Slack message link, or pick a recent message your app posted." : `Paste a Slack message link (Slack's "Copy link") to load its blocks for editing.` }),
2898
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
2899
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2900
+ "button",
2901
+ {
2902
+ type: "button",
2903
+ "aria-label": "How to copy a message link",
2904
+ className: "mt-0.5 shrink-0 rounded text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
2905
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
2906
+ }
2907
+ ) }),
2908
+ /* @__PURE__ */ jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
2909
+ "In Slack, hover over the message and click the ",
2910
+ /* @__PURE__ */ jsx("strong", { children: "\u22EE More actions" }),
2911
+ " button (or right-click the message), then choose ",
2912
+ /* @__PURE__ */ jsx("strong", { children: "Copy link" }),
2913
+ "."
2914
+ ] })
2915
+ ] })
2916
+ ] })
2917
+ ] }),
2918
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2919
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
2920
+ /* @__PURE__ */ jsx(Label, { htmlFor: "message-link", children: "Message link" }),
2921
+ /* @__PURE__ */ jsx(
2922
+ Input,
2923
+ {
2924
+ ref: inputRef,
2925
+ id: "message-link",
2926
+ value: link,
2927
+ onChange: (e) => {
2928
+ setLink(e.target.value);
2929
+ if (status.kind !== "idle" && status.kind !== "loading") {
2930
+ setStatus({ kind: "idle" });
2931
+ }
2932
+ },
2933
+ onKeyDown: (e) => {
2934
+ if (e.key === "Enter" && status.kind !== "loading") {
2935
+ e.preventDefault();
2936
+ handleLoad();
2937
+ }
2938
+ },
2939
+ placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
2940
+ autoComplete: "off",
2941
+ spellCheck: false
2942
+ }
2943
+ )
2944
+ ] }),
2945
+ status.kind === "error" && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: status.error }),
2946
+ notEditable && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-amber-200! bg-amber-50 p-3 text-xs text-amber-800", children: [
2947
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
2948
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
2949
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: notEditable.reason })
2950
+ ] }),
2951
+ /* @__PURE__ */ jsx(
2952
+ Button,
2953
+ {
2954
+ type: "button",
2955
+ variant: "outline",
2956
+ size: "sm",
2957
+ className: "self-start",
2958
+ onClick: () => onOpenAsNew(notEditable.blocks),
2959
+ children: "Open as a new message instead"
2960
+ }
2961
+ )
2962
+ ] }),
2963
+ hasRecent && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
2964
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
2965
+ /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" }),
2966
+ "or pick a recent message",
2967
+ /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" })
2968
+ ] }),
2969
+ recent === null && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
2970
+ recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: recentError }),
2971
+ recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app." }),
2972
+ recent && recent.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
2973
+ const asUser = (m.editableVia ?? "bot") === "user";
2974
+ return /* @__PURE__ */ jsxs(
2975
+ "button",
2976
+ {
2977
+ type: "button",
2978
+ onClick: () => onLoaded(recentToResult(m)),
2979
+ className: "flex flex-col gap-0.5 rounded-md border border-input bg-background px-3 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
2980
+ children: [
2981
+ /* @__PURE__ */ jsxs("span", { className: "flex items-baseline justify-between gap-2", children: [
2982
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-baseline gap-1.5", children: [
2983
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: m.channelName ? `#${m.channelName}` : m.channelId }),
2984
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: asUser ? "You" : "Bot" })
2985
+ ] }),
2986
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-xs text-muted-foreground", children: m.ts })
2987
+ ] }),
2988
+ m.label && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: m.label })
2989
+ ]
2990
+ },
2991
+ `${m.channelId}:${m.ts}`
2992
+ );
2993
+ }) })
2994
+ ] })
2995
+ ] }),
2996
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
2997
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
2998
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleLoad, disabled: status.kind === "loading" || !link.trim(), children: status.kind === "loading" ? "Loading\u2026" : "Load message" })
2999
+ ] })
3000
+ ]
3001
+ }
3002
+ ) });
3003
+ }
2712
3004
  var PALETTE_DRAG_PREFIX = "palette:";
2713
3005
  function parsePaletteDragId(id) {
2714
3006
  if (typeof id !== "string" || !id.startsWith(PALETTE_DRAG_PREFIX)) {
@@ -2908,100 +3200,6 @@ function PaletteItem({
2908
3200
  }
2909
3201
  );
2910
3202
  }
2911
- var buttonVariants = cva(
2912
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2913
- {
2914
- variants: {
2915
- variant: {
2916
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2917
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2918
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2919
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2920
- ghost: "hover:bg-accent hover:text-accent-foreground",
2921
- link: "text-primary underline-offset-4 hover:underline"
2922
- },
2923
- size: {
2924
- default: "h-9 px-4 py-2",
2925
- sm: "h-8 rounded-md px-3 text-xs",
2926
- lg: "h-10 rounded-md px-8",
2927
- icon: "h-9 w-9"
2928
- }
2929
- },
2930
- defaultVariants: {
2931
- variant: "default",
2932
- size: "default"
2933
- }
2934
- }
2935
- );
2936
- var Button = React.forwardRef(
2937
- ({ className, variant, size, asChild = false, ...props }, ref) => {
2938
- const Comp = asChild ? Slot : "button";
2939
- return /* @__PURE__ */ jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
2940
- }
2941
- );
2942
- Button.displayName = "Button";
2943
- var Dialog = DialogPrimitive.Root;
2944
- var DialogPortal = DialogPrimitive.Portal;
2945
- var DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2946
- DialogPrimitive.Overlay,
2947
- {
2948
- ref,
2949
- className: cn(
2950
- "bk-portal-content fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2951
- className
2952
- ),
2953
- ...props
2954
- }
2955
- ));
2956
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
2957
- var DialogContent = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
2958
- /* @__PURE__ */ jsx(DialogOverlay, {}),
2959
- /* @__PURE__ */ jsxs(
2960
- DialogPrimitive.Content,
2961
- {
2962
- ref,
2963
- className: cn(
2964
- "bk-portal-content fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg",
2965
- className
2966
- ),
2967
- ...props,
2968
- children: [
2969
- children,
2970
- /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring", children: [
2971
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
2972
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2973
- ] })
2974
- ]
2975
- }
2976
- )
2977
- ] }));
2978
- DialogContent.displayName = DialogPrimitive.Content.displayName;
2979
- function DialogHeader({ className, ...props }) {
2980
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
2981
- }
2982
- function DialogFooter({ className, ...props }) {
2983
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props });
2984
- }
2985
- var DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2986
- DialogPrimitive.Title,
2987
- {
2988
- ref,
2989
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
2990
- ...props
2991
- }
2992
- ));
2993
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
2994
- var DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
2995
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
2996
- var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2997
- LabelPrimitive.Root,
2998
- {
2999
- ref,
3000
- className: cn("text-sm font-medium leading-none text-foreground", className),
3001
- ...props
3002
- }
3003
- ));
3004
- Label.displayName = LabelPrimitive.Root.displayName;
3005
3203
  function SendDialog({
3006
3204
  open,
3007
3205
  onOpenChange,
@@ -3395,12 +3593,16 @@ function AlertEditor({ block, onChange }) {
3395
3593
  ) })
3396
3594
  ] });
3397
3595
  }
3398
- function CardEditor({ block, onChange }) {
3596
+ function CardEditor({
3597
+ block,
3598
+ onChange,
3599
+ idPrefix = "card"
3600
+ }) {
3399
3601
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3400
- /* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Up to 150 characters. Supports mrkdwn formatting.", htmlFor: "card-title", children: /* @__PURE__ */ jsx(
3602
+ /* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Up to 150 characters. Supports mrkdwn formatting.", htmlFor: `${idPrefix}-title`, children: /* @__PURE__ */ jsx(
3401
3603
  Input,
3402
3604
  {
3403
- id: "card-title",
3605
+ id: `${idPrefix}-title`,
3404
3606
  value: block.title?.text ?? "",
3405
3607
  maxLength: 150,
3406
3608
  placeholder: "e.g. New launch update",
@@ -3410,10 +3612,10 @@ function CardEditor({ block, onChange }) {
3410
3612
  })
3411
3613
  }
3412
3614
  ) }),
3413
- /* @__PURE__ */ jsx(EditorField, { label: "Subtitle", help: "Up to 150 characters.", htmlFor: "card-subtitle", children: /* @__PURE__ */ jsx(
3615
+ /* @__PURE__ */ jsx(EditorField, { label: "Subtitle", help: "Up to 150 characters.", htmlFor: `${idPrefix}-subtitle`, children: /* @__PURE__ */ jsx(
3414
3616
  Input,
3415
3617
  {
3416
- id: "card-subtitle",
3618
+ id: `${idPrefix}-subtitle`,
3417
3619
  value: block.subtitle?.text ?? "",
3418
3620
  maxLength: 150,
3419
3621
  placeholder: "e.g. Released April 28",
@@ -3423,10 +3625,10 @@ function CardEditor({ block, onChange }) {
3423
3625
  })
3424
3626
  }
3425
3627
  ) }),
3426
- /* @__PURE__ */ jsx(EditorField, { label: "Body", help: "Up to 200 characters. Supports mrkdwn formatting.", htmlFor: "card-body", children: /* @__PURE__ */ jsx(
3628
+ /* @__PURE__ */ jsx(EditorField, { label: "Body", help: "Up to 200 characters. Supports mrkdwn formatting.", htmlFor: `${idPrefix}-body`, children: /* @__PURE__ */ jsx(
3427
3629
  Textarea,
3428
3630
  {
3429
- id: "card-body",
3631
+ id: `${idPrefix}-body`,
3430
3632
  value: block.body?.text ?? "",
3431
3633
  maxLength: 200,
3432
3634
  rows: 3,
@@ -3442,7 +3644,7 @@ function CardEditor({ block, onChange }) {
3442
3644
  {
3443
3645
  label: "Hero image",
3444
3646
  help: "Wide image displayed at the top of the card.",
3445
- idPrefix: "card-hero",
3647
+ idPrefix: `${idPrefix}-hero`,
3446
3648
  image: block.hero_image,
3447
3649
  onChange: (next) => onChange({ ...block, hero_image: next })
3448
3650
  }
@@ -3452,7 +3654,7 @@ function CardEditor({ block, onChange }) {
3452
3654
  {
3453
3655
  label: "Icon",
3454
3656
  help: "Small image shown next to the title.",
3455
- idPrefix: "card-icon",
3657
+ idPrefix: `${idPrefix}-icon`,
3456
3658
  image: block.icon,
3457
3659
  onChange: (next) => onChange({ ...block, icon: next })
3458
3660
  }
@@ -3460,6 +3662,7 @@ function CardEditor({ block, onChange }) {
3460
3662
  /* @__PURE__ */ jsx(
3461
3663
  ButtonsField,
3462
3664
  {
3665
+ idPrefix,
3463
3666
  buttons: block.actions ?? [],
3464
3667
  onChange: (next) => onChange({ ...block, actions: next.length > 0 ? next : void 0 })
3465
3668
  }
@@ -3516,7 +3719,11 @@ function ImageFields({
3516
3719
  ) })
3517
3720
  ] });
3518
3721
  }
3519
- function ButtonsField({ buttons, onChange }) {
3722
+ function ButtonsField({
3723
+ buttons,
3724
+ onChange,
3725
+ idPrefix = "card"
3726
+ }) {
3520
3727
  const update = (idx, change) => {
3521
3728
  onChange(buttons.map((b, i) => i === idx ? { ...b, ...change } : b));
3522
3729
  };
@@ -3559,10 +3766,10 @@ function ButtonsField({ buttons, onChange }) {
3559
3766
  }
3560
3767
  )
3561
3768
  ] }),
3562
- /* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: `card-btn-label-${idx}`, children: /* @__PURE__ */ jsx(
3769
+ /* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: `${idPrefix}-btn-label-${idx}`, children: /* @__PURE__ */ jsx(
3563
3770
  Input,
3564
3771
  {
3565
- id: `card-btn-label-${idx}`,
3772
+ id: `${idPrefix}-btn-label-${idx}`,
3566
3773
  value: label,
3567
3774
  placeholder: "e.g. Open",
3568
3775
  onChange: (e) => update(idx, {
@@ -3579,11 +3786,11 @@ function ButtonsField({ buttons, onChange }) {
3579
3786
  {
3580
3787
  label: "Link URL",
3581
3788
  help: "Optional. If set, the button opens this link.",
3582
- htmlFor: `card-btn-url-${idx}`,
3789
+ htmlFor: `${idPrefix}-btn-url-${idx}`,
3583
3790
  children: /* @__PURE__ */ jsx(
3584
3791
  Input,
3585
3792
  {
3586
- id: `card-btn-url-${idx}`,
3793
+ id: `${idPrefix}-btn-url-${idx}`,
3587
3794
  type: "url",
3588
3795
  value: url,
3589
3796
  placeholder: "e.g. https://example.com",
@@ -3602,8 +3809,8 @@ function ButtonsField({ buttons, onChange }) {
3602
3809
  },
3603
3810
  className: "flex flex-row gap-3",
3604
3811
  children: ["default", "primary", "danger"].map((s) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3605
- /* @__PURE__ */ jsx(RadioGroupItem, { value: s, id: `card-btn-style-${idx}-${s}` }),
3606
- /* @__PURE__ */ jsx(Label, { htmlFor: `card-btn-style-${idx}-${s}`, className: "text-xs capitalize", children: s })
3812
+ /* @__PURE__ */ jsx(RadioGroupItem, { value: s, id: `${idPrefix}-btn-style-${idx}-${s}` }),
3813
+ /* @__PURE__ */ jsx(Label, { htmlFor: `${idPrefix}-btn-style-${idx}-${s}`, className: "text-xs capitalize", children: s })
3607
3814
  ] }, s))
3608
3815
  }
3609
3816
  ) })
@@ -3652,8 +3859,8 @@ function CarouselEditor({ block, onChange }) {
3652
3859
  });
3653
3860
  };
3654
3861
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
3655
- /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "Carousels hold 1-10 cards. For icon, hero image, or action edits, use the View JSON drawer." }),
3656
- elements.map((card, idx) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
3862
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "Carousels hold 1-10 cards." }),
3863
+ elements.map((card, idx) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-md border bg-muted/20 p-3", children: [
3657
3864
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3658
3865
  /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
3659
3866
  "Card ",
@@ -3671,33 +3878,7 @@ function CarouselEditor({ block, onChange }) {
3671
3878
  }
3672
3879
  )
3673
3880
  ] }),
3674
- /* @__PURE__ */ jsx(EditorField, { label: "Title", htmlFor: `carousel-card-title-${idx}`, children: /* @__PURE__ */ jsx(
3675
- Input,
3676
- {
3677
- id: `carousel-card-title-${idx}`,
3678
- value: card.title?.text ?? "",
3679
- maxLength: 150,
3680
- placeholder: "e.g. Card title",
3681
- onChange: (e) => updateCard(idx, {
3682
- ...card,
3683
- title: e.target.value ? { type: "mrkdwn", text: e.target.value } : void 0
3684
- })
3685
- }
3686
- ) }),
3687
- /* @__PURE__ */ jsx(EditorField, { label: "Body", htmlFor: `carousel-card-body-${idx}`, children: /* @__PURE__ */ jsx(
3688
- Textarea,
3689
- {
3690
- id: `carousel-card-body-${idx}`,
3691
- value: card.body?.text ?? "",
3692
- maxLength: 200,
3693
- rows: 2,
3694
- placeholder: "e.g. Short card description.",
3695
- onChange: (e) => updateCard(idx, {
3696
- ...card,
3697
- body: e.target.value ? { type: "mrkdwn", text: e.target.value } : void 0
3698
- })
3699
- }
3700
- ) })
3881
+ /* @__PURE__ */ jsx(CardEditor, { block: card, idPrefix: `carousel-card-${idx}`, onChange: (next) => updateCard(idx, next) })
3701
3882
  ] }, idx)),
3702
3883
  /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: addCard, disabled: elements.length >= MAX_CARDS, className: "self-start", children: [
3703
3884
  /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
@@ -4159,7 +4340,7 @@ function TextItemCard({
4159
4340
  id: `ctx-text-${index}`,
4160
4341
  value: element.text,
4161
4342
  rows: 2,
4162
- placeholder: "e.g. Posted by @stephen",
4343
+ placeholder: "e.g. Posted by @riley",
4163
4344
  onChange: (e) => onChange({ ...element, text: e.target.value })
4164
4345
  }
4165
4346
  ) }),
@@ -4244,7 +4425,7 @@ function ImageItemCard({
4244
4425
  {
4245
4426
  id: `ctx-img-alt-${index}`,
4246
4427
  value: element.alt_text,
4247
- placeholder: "e.g. Stephen's avatar",
4428
+ placeholder: "e.g. Riley's avatar",
4248
4429
  onChange: (e) => onChange({ ...element, alt_text: e.target.value })
4249
4430
  }
4250
4431
  ) })
@@ -8577,6 +8758,8 @@ var SURFACE_DROPPABLE_ID = "builder-surface";
8577
8758
  function Surface({
8578
8759
  blocks,
8579
8760
  workspaceName,
8761
+ authorName,
8762
+ authorIcon,
8580
8763
  previewHooks,
8581
8764
  previewTheme,
8582
8765
  previewSurface = "message",
@@ -8652,16 +8835,20 @@ function Surface({
8652
8835
  {
8653
8836
  "aria-label": "Block preview",
8654
8837
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
8655
- children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-2xl", children: previewSurface === "modal" ? /* @__PURE__ */ jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsx(MessageFrame, { workspaceName, isDark, children: blocksList }) })
8838
+ children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-2xl", children: previewSurface === "modal" ? /* @__PURE__ */ jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsx(MessageFrame, { workspaceName, authorName, authorIcon, isDark, children: blocksList }) })
8656
8839
  }
8657
8840
  );
8658
8841
  }
8659
8842
  function MessageFrame({
8660
8843
  workspaceName,
8844
+ authorName,
8845
+ authorIcon,
8661
8846
  isDark,
8662
8847
  children
8663
8848
  }) {
8664
- const initial = (workspaceName ?? "A").slice(0, 1).toUpperCase();
8849
+ const displayName = authorName ?? workspaceName ?? "Your app";
8850
+ const initial = displayName.slice(0, 1).toUpperCase();
8851
+ const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
8665
8852
  return /* @__PURE__ */ jsxs(
8666
8853
  "div",
8667
8854
  {
@@ -8675,7 +8862,7 @@ function MessageFrame({
8675
8862
  {
8676
8863
  className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
8677
8864
  children: [
8678
- /* @__PURE__ */ jsx(
8865
+ safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsx(
8679
8866
  "span",
8680
8867
  {
8681
8868
  className: cn(
@@ -8685,17 +8872,7 @@ function MessageFrame({
8685
8872
  children: initial
8686
8873
  }
8687
8874
  ),
8688
- /* @__PURE__ */ jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: workspaceName ?? "Your app" }),
8689
- /* @__PURE__ */ jsx(
8690
- "span",
8691
- {
8692
- className: cn(
8693
- "rounded px-1 text-[10px] font-semibold",
8694
- isDark ? "bg-white/10 text-white/70" : "bg-[#f3f3f3] text-[#616061]"
8695
- ),
8696
- children: "APP"
8697
- }
8698
- ),
8875
+ /* @__PURE__ */ jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
8699
8876
  /* @__PURE__ */ jsx("span", { children: "10:37 AM" })
8700
8877
  ]
8701
8878
  }
@@ -8897,6 +9074,14 @@ var SURFACE_OPTIONS = [
8897
9074
  ];
8898
9075
  var DEFAULT_DOCS_HREF = "https://docs.slack.dev/reference/block-kit/blocks";
8899
9076
  var DEFAULT_DOCS_LABEL = "Docs";
9077
+ function formatMessageTs(ts) {
9078
+ const seconds = Number.parseFloat(ts);
9079
+ if (!Number.isFinite(seconds)) return ts;
9080
+ const date = new Date(seconds * 1e3);
9081
+ if (Number.isNaN(date.getTime())) return ts;
9082
+ return date.toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
9083
+ }
9084
+ 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";
8900
9085
  function Toolbar2({
8901
9086
  onClear,
8902
9087
  onOpenJson,
@@ -8913,7 +9098,14 @@ function Toolbar2({
8913
9098
  showThemeControl = true,
8914
9099
  docsLink,
8915
9100
  errorCount,
8916
- sendButtonLabel = "Send"
9101
+ sendButtonLabel = "Review & send",
9102
+ editingEnabled = false,
9103
+ editBadge,
9104
+ onOpenLoad,
9105
+ onOpenUpdate,
9106
+ onExitEdit,
9107
+ loadButtonLabel = "Load message",
9108
+ updateButtonLabel = "Review & update"
8917
9109
  }) {
8918
9110
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
8919
9111
  const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
@@ -8923,184 +9115,210 @@ function Toolbar2({
8923
9115
  const docsLabel = docsLink === false ? null : docsLink?.label ?? DEFAULT_DOCS_LABEL;
8924
9116
  const [surfaceMenuOpen, setSurfaceMenuOpen] = useState(false);
8925
9117
  const [themeMenuOpen, setThemeMenuOpen] = useState(false);
8926
- const [moreMenuOpen, setMoreMenuOpen] = useState(false);
8927
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-1 border-b bg-background px-2 py-1.5 sm:px-3 sm:py-2", children: [
8928
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
8929
- onOpenPalette ? /* @__PURE__ */ jsxs(
8930
- Button,
8931
- {
8932
- type: "button",
8933
- variant: "ghost",
8934
- size: "sm",
8935
- onClick: onOpenPalette,
8936
- className: "md:hidden",
8937
- "aria-label": "Add a block",
8938
- children: [
8939
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
8940
- /* @__PURE__ */ jsx("span", { children: "Blocks" })
8941
- ]
8942
- }
8943
- ) : null,
8944
- showSurfaceControl ? /* @__PURE__ */ jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
8945
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
8946
- /* @__PURE__ */ jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
8947
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: activeSurface.label }),
8948
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8949
- ] }) }),
8950
- /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsx(
8951
- Menu,
9118
+ const [sendMenuOpen, setSendMenuOpen] = useState(false);
9119
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
9120
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-1 border-b bg-background px-2 py-1.5 sm:px-3 sm:py-2", children: [
9121
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
9122
+ editingEnabled && !editBadge ? /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: onOpenLoad, "aria-label": loadButtonLabel, children: [
9123
+ /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" }),
9124
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: loadButtonLabel })
9125
+ ] }) : null,
9126
+ onOpenPalette ? /* @__PURE__ */ jsxs(
9127
+ Button,
8952
9128
  {
8953
- ariaLabel: "Preview surface",
8954
- options: surfaceOptions,
8955
- value: previewSurface,
8956
- onChange: (next) => {
8957
- onPreviewSurfaceChange(next);
8958
- setSurfaceMenuOpen(false);
9129
+ type: "button",
9130
+ variant: "ghost",
9131
+ size: "sm",
9132
+ onClick: onOpenPalette,
9133
+ className: "md:hidden",
9134
+ "aria-label": "Add a block",
9135
+ children: [
9136
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
9137
+ /* @__PURE__ */ jsx("span", { children: "Blocks" })
9138
+ ]
9139
+ }
9140
+ ) : null,
9141
+ showSurfaceControl ? /* @__PURE__ */ jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
9142
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
9143
+ /* @__PURE__ */ jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
9144
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: activeSurface.label }),
9145
+ /* @__PURE__ */ jsx(ChevronDown, { className: "hidden h-3.5 w-3.5 opacity-60 md:inline" })
9146
+ ] }) }),
9147
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsx(
9148
+ Menu,
9149
+ {
9150
+ ariaLabel: "Preview surface",
9151
+ options: surfaceOptions,
9152
+ value: previewSurface,
9153
+ onChange: (next) => {
9154
+ onPreviewSurfaceChange(next);
9155
+ setSurfaceMenuOpen(false);
9156
+ }
9157
+ }
9158
+ ) })
9159
+ ] }) : null,
9160
+ showThemeControl ? /* @__PURE__ */ jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
9161
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
9162
+ /* @__PURE__ */ jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
9163
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: activeTheme.label }),
9164
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
9165
+ ] }) }),
9166
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsx(
9167
+ Menu,
9168
+ {
9169
+ ariaLabel: "Preview theme",
9170
+ options: THEME_OPTIONS,
9171
+ value: previewTheme,
9172
+ onChange: (next) => {
9173
+ onPreviewThemeChange(next);
9174
+ setThemeMenuOpen(false);
9175
+ }
8959
9176
  }
9177
+ ) })
9178
+ ] }) : null,
9179
+ docsHref ? /* @__PURE__ */ jsxs(
9180
+ "a",
9181
+ {
9182
+ href: docsHref,
9183
+ target: "_blank",
9184
+ rel: "noreferrer noopener",
9185
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-xs font-medium text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
9186
+ "aria-label": `${docsLabel} (opens in a new tab)`,
9187
+ children: [
9188
+ /* @__PURE__ */ jsx(BookOpen, { className: "h-3.5 w-3.5" }),
9189
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: docsLabel }),
9190
+ /* @__PURE__ */ jsx(ExternalLink, { className: "hidden h-3 w-3 opacity-50 md:inline" })
9191
+ ]
8960
9192
  }
8961
- ) })
8962
- ] }) : null,
8963
- showThemeControl ? /* @__PURE__ */ jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
8964
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
8965
- /* @__PURE__ */ jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
8966
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: activeTheme.label }),
8967
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8968
- ] }) }),
8969
- /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsx(
8970
- Menu,
9193
+ ) : null
9194
+ ] }),
9195
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9196
+ errorCount > 0 ? /* @__PURE__ */ jsxs(
9197
+ Button,
8971
9198
  {
8972
- ariaLabel: "Preview theme",
8973
- options: THEME_OPTIONS,
8974
- value: previewTheme,
8975
- onChange: (next) => {
8976
- onPreviewThemeChange(next);
8977
- setThemeMenuOpen(false);
8978
- }
9199
+ type: "button",
9200
+ variant: "ghost",
9201
+ size: "sm",
9202
+ onClick: onOpenIssues,
9203
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9204
+ "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9205
+ children: [
9206
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5" }),
9207
+ /* @__PURE__ */ jsxs("span", { children: [
9208
+ errorCount,
9209
+ " ",
9210
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: errorCount === 1 ? "issue" : "issues" })
9211
+ ] })
9212
+ ]
8979
9213
  }
8980
- ) })
8981
- ] }) : null,
8982
- docsHref ? /* @__PURE__ */ jsxs(
8983
- "a",
8984
- {
8985
- href: docsHref,
8986
- target: "_blank",
8987
- rel: "noreferrer noopener",
8988
- className: "hidden h-8 items-center gap-1.5 rounded-md px-2 py-1 text-sm text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:inline-flex",
8989
- "aria-label": `${docsLabel} (opens in a new tab)`,
8990
- children: [
8991
- /* @__PURE__ */ jsx(BookOpen, { className: "h-3.5 w-3.5" }),
8992
- docsLabel,
8993
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 opacity-50" })
8994
- ]
8995
- }
8996
- ) : null
8997
- ] }),
8998
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
8999
- errorCount > 0 ? /* @__PURE__ */ jsxs(
9000
- Button,
9001
- {
9002
- type: "button",
9003
- variant: "ghost",
9004
- size: "sm",
9005
- onClick: onOpenIssues,
9006
- className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9007
- "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9008
- children: [
9009
- /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5" }),
9010
- /* @__PURE__ */ jsxs("span", { children: [
9011
- errorCount,
9012
- " ",
9013
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: errorCount === 1 ? "issue" : "issues" })
9014
- ] })
9015
- ]
9016
- }
9017
- ) : null,
9018
- /* @__PURE__ */ jsxs(
9019
- Button,
9020
- {
9021
- type: "button",
9022
- variant: "ghost",
9023
- size: "sm",
9024
- onClick: onClear,
9025
- disabled: !canClear,
9026
- className: "hidden hover:bg-destructive/10 hover:text-destructive sm:inline-flex",
9027
- "aria-label": "Clear all blocks",
9028
- children: [
9029
- /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }),
9030
- "Clear"
9031
- ]
9032
- }
9033
- ),
9034
- /* @__PURE__ */ jsxs(
9035
- Button,
9036
- {
9037
- type: "button",
9038
- variant: "ghost",
9039
- size: "sm",
9040
- onClick: onOpenJson,
9041
- "aria-label": "View JSON",
9042
- className: "hidden sm:inline-flex",
9043
- children: [
9044
- /* @__PURE__ */ jsx(Code2, { className: "h-3.5 w-3.5" }),
9045
- "View JSON"
9046
- ]
9047
- }
9048
- ),
9049
- /* @__PURE__ */ jsxs(Popover, { open: moreMenuOpen, onOpenChange: setMoreMenuOpen, children: [
9050
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
9214
+ ) : null,
9215
+ /* @__PURE__ */ jsxs(
9051
9216
  Button,
9052
9217
  {
9053
9218
  type: "button",
9054
9219
  variant: "ghost",
9055
9220
  size: "sm",
9056
- "aria-label": "More actions",
9057
- "aria-haspopup": "menu",
9058
- className: "px-2 sm:hidden",
9059
- children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-3.5 w-3.5" })
9221
+ onClick: onClear,
9222
+ disabled: !canClear,
9223
+ className: "hover:bg-destructive/10 hover:text-destructive",
9224
+ "aria-label": "Clear all blocks",
9225
+ children: [
9226
+ /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }),
9227
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: "Clear" })
9228
+ ]
9060
9229
  }
9061
- ) }),
9062
- /* @__PURE__ */ jsx(PopoverContent, { align: "end", className: "w-44 p-1", children: /* @__PURE__ */ jsxs("div", { role: "menu", "aria-label": "More actions", className: "flex flex-col", children: [
9063
- /* @__PURE__ */ jsx(
9064
- ActionMenuItem,
9065
- {
9066
- icon: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }),
9067
- onSelect: () => {
9068
- setMoreMenuOpen(false);
9069
- onClear();
9070
- },
9071
- disabled: !canClear,
9072
- tone: "destructive",
9073
- children: "Clear"
9074
- }
9075
- ),
9076
- /* @__PURE__ */ jsx(
9077
- ActionMenuItem,
9078
- {
9079
- icon: /* @__PURE__ */ jsx(Code2, { className: "h-3.5 w-3.5" }),
9080
- onSelect: () => {
9081
- setMoreMenuOpen(false);
9082
- onOpenJson();
9083
- },
9084
- children: "View JSON"
9085
- }
9086
- ),
9087
- docsHref ? /* @__PURE__ */ jsx(
9088
- ActionMenuLink,
9089
- {
9090
- href: docsHref,
9091
- icon: /* @__PURE__ */ jsx(BookOpen, { className: "h-3.5 w-3.5" }),
9092
- trailingIcon: /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 opacity-50" }),
9093
- onSelect: () => setMoreMenuOpen(false),
9094
- children: docsLabel
9095
- }
9096
- ) : null
9097
- ] }) })
9230
+ ),
9231
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onOpenJson, "aria-label": "View JSON", children: [
9232
+ /* @__PURE__ */ jsx(Code2, { className: "h-3.5 w-3.5" }),
9233
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: "View JSON" })
9234
+ ] }),
9235
+ editBadge ? (
9236
+ // A message is loaded: split button. Main action updates it in
9237
+ // place; the menu also offers posting the blocks as a new message.
9238
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch", children: [
9239
+ /* @__PURE__ */ jsxs(
9240
+ Button,
9241
+ {
9242
+ type: "button",
9243
+ size: "sm",
9244
+ onClick: onOpenUpdate,
9245
+ disabled: !canSend,
9246
+ "aria-label": updateButtonLabel,
9247
+ className: "rounded-r-none",
9248
+ children: [
9249
+ /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" }),
9250
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: updateButtonLabel })
9251
+ ]
9252
+ }
9253
+ ),
9254
+ /* @__PURE__ */ jsxs(Popover, { open: sendMenuOpen, onOpenChange: setSendMenuOpen, children: [
9255
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
9256
+ Button,
9257
+ {
9258
+ type: "button",
9259
+ size: "sm",
9260
+ "aria-label": "More message options",
9261
+ "aria-haspopup": "menu",
9262
+ className: "rounded-l-none border-l border-l-primary-foreground/30! px-1.5",
9263
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" })
9264
+ }
9265
+ ) }),
9266
+ /* @__PURE__ */ jsx(PopoverContent, { align: "end", className: "w-56 p-1", children: /* @__PURE__ */ jsxs("div", { role: "menu", "aria-label": "Message options", className: "flex flex-col", children: [
9267
+ /* @__PURE__ */ jsxs(
9268
+ "button",
9269
+ {
9270
+ type: "button",
9271
+ role: "menuitem",
9272
+ disabled: !canSend,
9273
+ onClick: () => {
9274
+ setSendMenuOpen(false);
9275
+ onOpenUpdate?.();
9276
+ },
9277
+ className: SEND_MENU_ITEM,
9278
+ children: [
9279
+ /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" }),
9280
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: "Update message" })
9281
+ ]
9282
+ }
9283
+ ),
9284
+ /* @__PURE__ */ jsxs(
9285
+ "button",
9286
+ {
9287
+ type: "button",
9288
+ role: "menuitem",
9289
+ disabled: !canSend,
9290
+ onClick: () => {
9291
+ setSendMenuOpen(false);
9292
+ onOpenSend();
9293
+ },
9294
+ className: SEND_MENU_ITEM,
9295
+ children: [
9296
+ /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
9297
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: "Send as a new message" })
9298
+ ]
9299
+ }
9300
+ )
9301
+ ] }) })
9302
+ ] })
9303
+ ] })
9304
+ ) : /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9305
+ /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
9306
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: sendButtonLabel })
9307
+ ] })
9308
+ ] })
9309
+ ] }),
9310
+ editingEnabled && editBadge ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 border-b bg-muted px-2 py-2 text-foreground sm:px-3", children: [
9311
+ /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4 shrink-0 text-primary" }),
9312
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 text-sm", children: [
9313
+ "References an existing message in ",
9314
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: editBadge.channelLabel }),
9315
+ /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs opacity-70", title: editBadge.ts, children: formatMessageTs(editBadge.ts) })
9098
9316
  ] }),
9099
- /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9100
- /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
9101
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
9317
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onExitEdit, className: "shrink-0", children: [
9318
+ /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }),
9319
+ "Switch to a new message"
9102
9320
  ] })
9103
- ] })
9321
+ ] }) : null
9104
9322
  ] });
9105
9323
  }
9106
9324
  function Menu({
@@ -9158,52 +9376,132 @@ function Menu({
9158
9376
  );
9159
9377
  }) });
9160
9378
  }
9161
- var ACTION_ITEM_BASE = "flex w-full cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-left text-sm text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50";
9162
- function ActionMenuItem({
9163
- icon,
9164
- children,
9165
- onSelect,
9166
- disabled,
9167
- tone
9379
+ function UpdateDialog({
9380
+ open,
9381
+ onOpenChange,
9382
+ target,
9383
+ blocks,
9384
+ loadSendAsUserStatus,
9385
+ onUpdate,
9386
+ confirmUpdateLabel = "Update message",
9387
+ errorCount,
9388
+ onShowIssues
9168
9389
  }) {
9169
- return /* @__PURE__ */ jsxs(
9170
- "button",
9171
- {
9172
- type: "button",
9173
- role: "menuitem",
9174
- onClick: onSelect,
9175
- disabled,
9176
- className: cn(ACTION_ITEM_BASE, tone === "destructive" && "hover:bg-destructive/10 hover:text-destructive"),
9177
- children: [
9178
- icon,
9179
- /* @__PURE__ */ jsx("span", { className: "flex-1", children })
9180
- ]
9390
+ const asUser = target.editableVia === "user";
9391
+ const [userStatus, setUserStatus] = useState(null);
9392
+ const [status, setStatus] = useState({ kind: "idle" });
9393
+ const loadSendAsUserStatusRef = useRef(loadSendAsUserStatus);
9394
+ useEffect(() => {
9395
+ loadSendAsUserStatusRef.current = loadSendAsUserStatus;
9396
+ });
9397
+ const refreshSendAsUser = useCallback(() => {
9398
+ loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
9399
+ }, []);
9400
+ useEffect(() => {
9401
+ if (!open) {
9402
+ return;
9181
9403
  }
9182
- );
9183
- }
9184
- function ActionMenuLink({
9185
- href,
9186
- icon,
9187
- trailingIcon,
9188
- children,
9189
- onSelect
9190
- }) {
9191
- return /* @__PURE__ */ jsxs(
9192
- "a",
9193
- {
9194
- role: "menuitem",
9195
- href,
9196
- target: "_blank",
9197
- rel: "noreferrer noopener",
9198
- onClick: onSelect,
9199
- className: ACTION_ITEM_BASE,
9200
- children: [
9201
- icon,
9202
- /* @__PURE__ */ jsx("span", { className: "flex-1", children }),
9203
- trailingIcon
9204
- ]
9404
+ setStatus({ kind: "idle" });
9405
+ if (asUser) {
9406
+ setUserStatus(null);
9407
+ refreshSendAsUser();
9205
9408
  }
9206
- );
9409
+ }, [open, asUser, refreshSendAsUser]);
9410
+ useEffect(() => {
9411
+ if (!open || !asUser) {
9412
+ return;
9413
+ }
9414
+ const handler = () => refreshSendAsUser();
9415
+ window.addEventListener("focus", handler);
9416
+ return () => window.removeEventListener("focus", handler);
9417
+ }, [open, asUser, refreshSendAsUser]);
9418
+ const needsSignIn = asUser && userStatus !== null && !userStatus.canSendAsUser;
9419
+ const handleSubmit = async () => {
9420
+ setStatus({ kind: "updating" });
9421
+ try {
9422
+ const result = await onUpdate({
9423
+ channelId: target.channelId,
9424
+ ts: target.ts,
9425
+ blocks: toSlackBlocks(blocks),
9426
+ asUser
9427
+ });
9428
+ if (result.ok) {
9429
+ setStatus({ kind: "idle" });
9430
+ onOpenChange(false);
9431
+ return;
9432
+ }
9433
+ setStatus({ kind: "error", error: result.error ?? "Update failed." });
9434
+ } catch (e) {
9435
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Update failed." });
9436
+ }
9437
+ };
9438
+ const channelLabel = target.channelName ? `#${target.channelName}` : target.channelId;
9439
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg", children: [
9440
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
9441
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Update message" }),
9442
+ /* @__PURE__ */ jsx(DialogDescription, { children: "This replaces the message that's already posted. It stays in the same channel and can't be moved." })
9443
+ ] }),
9444
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
9445
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
9446
+ /* @__PURE__ */ jsx(Label, { children: "Channel" }),
9447
+ /* @__PURE__ */ jsx("p", { className: "flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground", children: channelLabel })
9448
+ ] }),
9449
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
9450
+ /* @__PURE__ */ jsx(Label, { children: "Posting as" }),
9451
+ /* @__PURE__ */ jsx("p", { className: "flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground", children: asUser ? "Your account" : "App bot" }),
9452
+ needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
9453
+ /* @__PURE__ */ jsxs(
9454
+ "a",
9455
+ {
9456
+ href: userStatus.oauthUrl,
9457
+ target: "_blank",
9458
+ rel: "noopener noreferrer",
9459
+ className: "inline-flex items-center gap-1 text-primary hover:underline",
9460
+ children: [
9461
+ "Sign in with Slack ",
9462
+ /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" })
9463
+ ]
9464
+ }
9465
+ ),
9466
+ " ",
9467
+ "to update your own message."
9468
+ ] }),
9469
+ needsSignIn && !userStatus?.oauthUrl && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Sign in with Slack to update your own message." })
9470
+ ] }),
9471
+ errorCount > 0 ? /* @__PURE__ */ jsxs(
9472
+ "button",
9473
+ {
9474
+ type: "button",
9475
+ onClick: onShowIssues,
9476
+ className: "flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/5 p-2 text-left text-xs text-destructive hover:bg-destructive/10",
9477
+ children: [
9478
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
9479
+ /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
9480
+ "Fix ",
9481
+ errorCount,
9482
+ " ",
9483
+ errorCount === 1 ? "issue" : "issues",
9484
+ " before updating."
9485
+ ] }),
9486
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 underline", children: "Show issues" })
9487
+ ]
9488
+ }
9489
+ ) : null,
9490
+ status.kind === "error" && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: status.error })
9491
+ ] }),
9492
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
9493
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
9494
+ /* @__PURE__ */ jsx(
9495
+ Button,
9496
+ {
9497
+ type: "button",
9498
+ onClick: handleSubmit,
9499
+ disabled: status.kind === "updating" || blocks.length === 0 || errorCount > 0 || needsSignIn,
9500
+ children: status.kind === "updating" ? "Updating\u2026" : confirmUpdateLabel
9501
+ }
9502
+ )
9503
+ ] })
9504
+ ] }) });
9207
9505
  }
9208
9506
  function BlockKitchen(props) {
9209
9507
  const {
@@ -9215,6 +9513,10 @@ function BlockKitchen(props) {
9215
9513
  loadChannels,
9216
9514
  loadSendAsUserStatus,
9217
9515
  onSend,
9516
+ editing,
9517
+ loadButtonLabel,
9518
+ updateButtonLabel,
9519
+ confirmUpdateLabel,
9218
9520
  palette,
9219
9521
  disabledBlockTypes,
9220
9522
  showPaletteSearch,
@@ -9260,6 +9562,10 @@ function BlockKitchen(props) {
9260
9562
  }, [blocks]);
9261
9563
  const [jsonOpen, setJsonOpen] = useState(false);
9262
9564
  const [sendOpen, setSendOpen] = useState(false);
9565
+ const [updateOpen, setUpdateOpen] = useState(false);
9566
+ const [loadOpen, setLoadOpen] = useState(false);
9567
+ const [editTarget, setEditTarget] = useState(null);
9568
+ const activeEditTarget = editing ? editTarget : null;
9263
9569
  const [issuesOpen, setIssuesOpen] = useState(false);
9264
9570
  const [paletteOpen, setPaletteOpen] = useState(false);
9265
9571
  const [openBlockId, setOpenBlockId] = useState(null);
@@ -9380,7 +9686,21 @@ function BlockKitchen(props) {
9380
9686
  allowedSurfaces,
9381
9687
  showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
9382
9688
  docsLink,
9383
- sendButtonLabel
9689
+ sendButtonLabel,
9690
+ editingEnabled: !!editing,
9691
+ editBadge: activeEditTarget ? {
9692
+ channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
9693
+ ts: activeEditTarget.ts
9694
+ } : null,
9695
+ onOpenLoad: () => setLoadOpen(true),
9696
+ onOpenUpdate: () => setUpdateOpen(true),
9697
+ onExitEdit: () => {
9698
+ setEditTarget(null);
9699
+ replaceAll([]);
9700
+ setLoadOpen(true);
9701
+ },
9702
+ loadButtonLabel,
9703
+ updateButtonLabel
9384
9704
  }
9385
9705
  ),
9386
9706
  /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -9399,6 +9719,8 @@ function BlockKitchen(props) {
9399
9719
  {
9400
9720
  blocks,
9401
9721
  workspaceName,
9722
+ authorName: activeEditTarget?.username,
9723
+ authorIcon: activeEditTarget?.iconUrl,
9402
9724
  previewHooks: mergedPreviewHooks,
9403
9725
  previewTheme,
9404
9726
  previewSurface,
@@ -9464,6 +9786,52 @@ function BlockKitchen(props) {
9464
9786
  }
9465
9787
  }
9466
9788
  ),
9789
+ activeEditTarget && editing ? /* @__PURE__ */ jsx(
9790
+ UpdateDialog,
9791
+ {
9792
+ open: updateOpen,
9793
+ onOpenChange: setUpdateOpen,
9794
+ target: activeEditTarget,
9795
+ blocks: blockPayloads,
9796
+ loadSendAsUserStatus,
9797
+ onUpdate: editing.onUpdate,
9798
+ confirmUpdateLabel,
9799
+ errorCount: validation.total,
9800
+ onShowIssues: () => {
9801
+ setUpdateOpen(false);
9802
+ setIssuesOpen(true);
9803
+ }
9804
+ }
9805
+ ) : null,
9806
+ editing ? /* @__PURE__ */ jsx(
9807
+ LoadMessageDialog,
9808
+ {
9809
+ open: loadOpen,
9810
+ onOpenChange: setLoadOpen,
9811
+ onLoadMessage: editing.onLoadMessage,
9812
+ loadRecentMessages: editing.loadRecentMessages,
9813
+ onLoaded: (result) => {
9814
+ replaceAll(result.blocks);
9815
+ setEditTarget({
9816
+ channelId: result.channelId,
9817
+ channelName: result.channelName,
9818
+ ts: result.ts,
9819
+ editableVia: result.editableVia,
9820
+ workspaceName: result.workspaceName,
9821
+ username: result.username,
9822
+ iconUrl: result.iconUrl
9823
+ });
9824
+ setLoadOpen(false);
9825
+ },
9826
+ onOpenAsNew: (loadedBlocks) => {
9827
+ if (loadedBlocks) {
9828
+ replaceAll(loadedBlocks);
9829
+ }
9830
+ setEditTarget(null);
9831
+ setLoadOpen(false);
9832
+ }
9833
+ }
9834
+ ) : null,
9467
9835
  /* @__PURE__ */ jsx(
9468
9836
  IssuesSheet,
9469
9837
  {