@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.cjs CHANGED
@@ -2724,6 +2724,91 @@ function JsonDrawer({
2724
2724
  ] })
2725
2725
  ] }) });
2726
2726
  }
2727
+ var buttonVariants = classVarianceAuthority.cva(
2728
+ "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",
2729
+ {
2730
+ variants: {
2731
+ variant: {
2732
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2733
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2734
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2735
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2736
+ ghost: "hover:bg-accent hover:text-accent-foreground",
2737
+ link: "text-primary underline-offset-4 hover:underline"
2738
+ },
2739
+ size: {
2740
+ default: "h-9 px-4 py-2",
2741
+ sm: "h-8 rounded-md px-3 text-xs",
2742
+ lg: "h-10 rounded-md px-8",
2743
+ icon: "h-9 w-9"
2744
+ }
2745
+ },
2746
+ defaultVariants: {
2747
+ variant: "default",
2748
+ size: "default"
2749
+ }
2750
+ }
2751
+ );
2752
+ var Button = React__namespace.forwardRef(
2753
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
2754
+ const Comp = asChild ? reactSlot.Slot : "button";
2755
+ return /* @__PURE__ */ jsxRuntime.jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
2756
+ }
2757
+ );
2758
+ Button.displayName = "Button";
2759
+ var Dialog = DialogPrimitive__namespace.Root;
2760
+ var DialogPortal = DialogPrimitive__namespace.Portal;
2761
+ var DialogOverlay = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2762
+ DialogPrimitive__namespace.Overlay,
2763
+ {
2764
+ ref,
2765
+ className: cn(
2766
+ "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",
2767
+ className
2768
+ ),
2769
+ ...props
2770
+ }
2771
+ ));
2772
+ DialogOverlay.displayName = DialogPrimitive__namespace.Overlay.displayName;
2773
+ var DialogContent = React__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
2774
+ /* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
2775
+ /* @__PURE__ */ jsxRuntime.jsxs(
2776
+ DialogPrimitive__namespace.Content,
2777
+ {
2778
+ ref,
2779
+ className: cn(
2780
+ "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",
2781
+ className
2782
+ ),
2783
+ ...props,
2784
+ children: [
2785
+ children,
2786
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.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: [
2787
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }),
2788
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
2789
+ ] })
2790
+ ]
2791
+ }
2792
+ )
2793
+ ] }));
2794
+ DialogContent.displayName = DialogPrimitive__namespace.Content.displayName;
2795
+ function DialogHeader({ className, ...props }) {
2796
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
2797
+ }
2798
+ function DialogFooter({ className, ...props }) {
2799
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props });
2800
+ }
2801
+ var DialogTitle = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2802
+ DialogPrimitive__namespace.Title,
2803
+ {
2804
+ ref,
2805
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
2806
+ ...props
2807
+ }
2808
+ ));
2809
+ DialogTitle.displayName = DialogPrimitive__namespace.Title.displayName;
2810
+ var DialogDescription = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
2811
+ DialogDescription.displayName = DialogPrimitive__namespace.Description.displayName;
2727
2812
  var Input = React__namespace.forwardRef(
2728
2813
  ({ className, type, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2729
2814
  "input",
@@ -2739,6 +2824,213 @@ var Input = React__namespace.forwardRef(
2739
2824
  )
2740
2825
  );
2741
2826
  Input.displayName = "Input";
2827
+ var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2828
+ LabelPrimitive__namespace.Root,
2829
+ {
2830
+ ref,
2831
+ className: cn("text-sm font-medium leading-none text-foreground", className),
2832
+ ...props
2833
+ }
2834
+ ));
2835
+ Label.displayName = LabelPrimitive__namespace.Root.displayName;
2836
+ function recentToResult(msg) {
2837
+ return {
2838
+ ok: true,
2839
+ channelId: msg.channelId,
2840
+ channelName: msg.channelName,
2841
+ ts: msg.ts,
2842
+ blocks: msg.blocks,
2843
+ editableVia: msg.editableVia ?? "bot",
2844
+ workspaceName: msg.workspaceName,
2845
+ username: msg.username,
2846
+ iconUrl: msg.iconUrl
2847
+ };
2848
+ }
2849
+ function LoadMessageDialog({
2850
+ open,
2851
+ onOpenChange,
2852
+ onLoadMessage,
2853
+ loadRecentMessages,
2854
+ onLoaded,
2855
+ onOpenAsNew
2856
+ }) {
2857
+ const [link, setLink] = React.useState("");
2858
+ const [status, setStatus] = React.useState({ kind: "idle" });
2859
+ const [recent, setRecent] = React.useState(null);
2860
+ const [recentError, setRecentError] = React.useState(null);
2861
+ const inputRef = React.useRef(null);
2862
+ const onLoadMessageRef = React.useRef(onLoadMessage);
2863
+ const loadRecentMessagesRef = React.useRef(loadRecentMessages);
2864
+ React.useEffect(() => {
2865
+ onLoadMessageRef.current = onLoadMessage;
2866
+ loadRecentMessagesRef.current = loadRecentMessages;
2867
+ });
2868
+ const hasRecent = !!loadRecentMessages;
2869
+ React.useEffect(() => {
2870
+ if (!open) {
2871
+ return;
2872
+ }
2873
+ setLink("");
2874
+ setStatus({ kind: "idle" });
2875
+ if (!loadRecentMessagesRef.current) {
2876
+ setRecent([]);
2877
+ setRecentError(null);
2878
+ return;
2879
+ }
2880
+ setRecent(null);
2881
+ setRecentError(null);
2882
+ let cancelled = false;
2883
+ loadRecentMessagesRef.current().then((list) => {
2884
+ if (!cancelled) {
2885
+ setRecent(list);
2886
+ }
2887
+ }).catch((e) => {
2888
+ if (!cancelled) {
2889
+ setRecentError(e instanceof Error ? e.message : "Failed to load recent messages.");
2890
+ }
2891
+ });
2892
+ return () => {
2893
+ cancelled = true;
2894
+ };
2895
+ }, [open]);
2896
+ const handleLoad = async () => {
2897
+ const trimmed = link.trim();
2898
+ if (!trimmed) {
2899
+ setStatus({ kind: "error", error: "Paste a Slack message link first." });
2900
+ return;
2901
+ }
2902
+ setStatus({ kind: "loading" });
2903
+ try {
2904
+ const result = await onLoadMessageRef.current({ link: trimmed });
2905
+ if (result.ok) {
2906
+ onLoaded(result);
2907
+ return;
2908
+ }
2909
+ setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks });
2910
+ } catch (e) {
2911
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
2912
+ }
2913
+ };
2914
+ const notEditable = status.kind === "not-editable" ? status : null;
2915
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
2916
+ DialogContent,
2917
+ {
2918
+ className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg",
2919
+ onOpenAutoFocus: (e) => {
2920
+ e.preventDefault();
2921
+ inputRef.current?.focus();
2922
+ },
2923
+ children: [
2924
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
2925
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Edit an existing message" }),
2926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-1.5", children: [
2927
+ /* @__PURE__ */ jsxRuntime.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.` }),
2928
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
2929
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
2930
+ "button",
2931
+ {
2932
+ type: "button",
2933
+ "aria-label": "How to copy a message link",
2934
+ 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",
2935
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-3.5 w-3.5" })
2936
+ }
2937
+ ) }),
2938
+ /* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
2939
+ "In Slack, hover over the message and click the ",
2940
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u22EE More actions" }),
2941
+ " button (or right-click the message), then choose ",
2942
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Copy link" }),
2943
+ "."
2944
+ ] })
2945
+ ] })
2946
+ ] })
2947
+ ] }),
2948
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2949
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
2950
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message-link", children: "Message link" }),
2951
+ /* @__PURE__ */ jsxRuntime.jsx(
2952
+ Input,
2953
+ {
2954
+ ref: inputRef,
2955
+ id: "message-link",
2956
+ value: link,
2957
+ onChange: (e) => {
2958
+ setLink(e.target.value);
2959
+ if (status.kind !== "idle" && status.kind !== "loading") {
2960
+ setStatus({ kind: "idle" });
2961
+ }
2962
+ },
2963
+ onKeyDown: (e) => {
2964
+ if (e.key === "Enter" && status.kind !== "loading") {
2965
+ e.preventDefault();
2966
+ handleLoad();
2967
+ }
2968
+ },
2969
+ placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
2970
+ autoComplete: "off",
2971
+ spellCheck: false
2972
+ }
2973
+ )
2974
+ ] }),
2975
+ status.kind === "error" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: status.error }),
2976
+ notEditable && /* @__PURE__ */ jsxRuntime.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: [
2977
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
2978
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
2979
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: notEditable.reason })
2980
+ ] }),
2981
+ /* @__PURE__ */ jsxRuntime.jsx(
2982
+ Button,
2983
+ {
2984
+ type: "button",
2985
+ variant: "outline",
2986
+ size: "sm",
2987
+ className: "self-start",
2988
+ onClick: () => onOpenAsNew(notEditable.blocks),
2989
+ children: "Open as a new message instead"
2990
+ }
2991
+ )
2992
+ ] }),
2993
+ hasRecent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
2994
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
2995
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" }),
2996
+ "or pick a recent message",
2997
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })
2998
+ ] }),
2999
+ recent === null && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3000
+ recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: recentError }),
3001
+ recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app." }),
3002
+ recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
3003
+ const asUser = (m.editableVia ?? "bot") === "user";
3004
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3005
+ "button",
3006
+ {
3007
+ type: "button",
3008
+ onClick: () => onLoaded(recentToResult(m)),
3009
+ 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",
3010
+ children: [
3011
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-baseline justify-between gap-2", children: [
3012
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-baseline gap-1.5", children: [
3013
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: m.channelName ? `#${m.channelName}` : m.channelId }),
3014
+ /* @__PURE__ */ jsxRuntime.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" })
3015
+ ] }),
3016
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-xs text-muted-foreground", children: m.ts })
3017
+ ] }),
3018
+ m.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-muted-foreground", children: m.label })
3019
+ ]
3020
+ },
3021
+ `${m.channelId}:${m.ts}`
3022
+ );
3023
+ }) })
3024
+ ] })
3025
+ ] }),
3026
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
3027
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3028
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", onClick: handleLoad, disabled: status.kind === "loading" || !link.trim(), children: status.kind === "loading" ? "Loading\u2026" : "Load message" })
3029
+ ] })
3030
+ ]
3031
+ }
3032
+ ) });
3033
+ }
2742
3034
  var PALETTE_DRAG_PREFIX = "palette:";
2743
3035
  function parsePaletteDragId(id) {
2744
3036
  if (typeof id !== "string" || !id.startsWith(PALETTE_DRAG_PREFIX)) {
@@ -2938,100 +3230,6 @@ function PaletteItem({
2938
3230
  }
2939
3231
  );
2940
3232
  }
2941
- var buttonVariants = classVarianceAuthority.cva(
2942
- "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",
2943
- {
2944
- variants: {
2945
- variant: {
2946
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2947
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2948
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2949
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2950
- ghost: "hover:bg-accent hover:text-accent-foreground",
2951
- link: "text-primary underline-offset-4 hover:underline"
2952
- },
2953
- size: {
2954
- default: "h-9 px-4 py-2",
2955
- sm: "h-8 rounded-md px-3 text-xs",
2956
- lg: "h-10 rounded-md px-8",
2957
- icon: "h-9 w-9"
2958
- }
2959
- },
2960
- defaultVariants: {
2961
- variant: "default",
2962
- size: "default"
2963
- }
2964
- }
2965
- );
2966
- var Button = React__namespace.forwardRef(
2967
- ({ className, variant, size, asChild = false, ...props }, ref) => {
2968
- const Comp = asChild ? reactSlot.Slot : "button";
2969
- return /* @__PURE__ */ jsxRuntime.jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
2970
- }
2971
- );
2972
- Button.displayName = "Button";
2973
- var Dialog = DialogPrimitive__namespace.Root;
2974
- var DialogPortal = DialogPrimitive__namespace.Portal;
2975
- var DialogOverlay = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2976
- DialogPrimitive__namespace.Overlay,
2977
- {
2978
- ref,
2979
- className: cn(
2980
- "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",
2981
- className
2982
- ),
2983
- ...props
2984
- }
2985
- ));
2986
- DialogOverlay.displayName = DialogPrimitive__namespace.Overlay.displayName;
2987
- var DialogContent = React__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
2988
- /* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
2989
- /* @__PURE__ */ jsxRuntime.jsxs(
2990
- DialogPrimitive__namespace.Content,
2991
- {
2992
- ref,
2993
- className: cn(
2994
- "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",
2995
- className
2996
- ),
2997
- ...props,
2998
- children: [
2999
- children,
3000
- /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.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: [
3001
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }),
3002
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
3003
- ] })
3004
- ]
3005
- }
3006
- )
3007
- ] }));
3008
- DialogContent.displayName = DialogPrimitive__namespace.Content.displayName;
3009
- function DialogHeader({ className, ...props }) {
3010
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
3011
- }
3012
- function DialogFooter({ className, ...props }) {
3013
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props });
3014
- }
3015
- var DialogTitle = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3016
- DialogPrimitive__namespace.Title,
3017
- {
3018
- ref,
3019
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
3020
- ...props
3021
- }
3022
- ));
3023
- DialogTitle.displayName = DialogPrimitive__namespace.Title.displayName;
3024
- var DialogDescription = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
3025
- DialogDescription.displayName = DialogPrimitive__namespace.Description.displayName;
3026
- var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3027
- LabelPrimitive__namespace.Root,
3028
- {
3029
- ref,
3030
- className: cn("text-sm font-medium leading-none text-foreground", className),
3031
- ...props
3032
- }
3033
- ));
3034
- Label.displayName = LabelPrimitive__namespace.Root.displayName;
3035
3233
  function SendDialog({
3036
3234
  open,
3037
3235
  onOpenChange,
@@ -3425,12 +3623,16 @@ function AlertEditor({ block, onChange }) {
3425
3623
  ) })
3426
3624
  ] });
3427
3625
  }
3428
- function CardEditor({ block, onChange }) {
3626
+ function CardEditor({
3627
+ block,
3628
+ onChange,
3629
+ idPrefix = "card"
3630
+ }) {
3429
3631
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
3430
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", help: "Up to 150 characters. Supports mrkdwn formatting.", htmlFor: "card-title", children: /* @__PURE__ */ jsxRuntime.jsx(
3632
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", help: "Up to 150 characters. Supports mrkdwn formatting.", htmlFor: `${idPrefix}-title`, children: /* @__PURE__ */ jsxRuntime.jsx(
3431
3633
  Input,
3432
3634
  {
3433
- id: "card-title",
3635
+ id: `${idPrefix}-title`,
3434
3636
  value: block.title?.text ?? "",
3435
3637
  maxLength: 150,
3436
3638
  placeholder: "e.g. New launch update",
@@ -3440,10 +3642,10 @@ function CardEditor({ block, onChange }) {
3440
3642
  })
3441
3643
  }
3442
3644
  ) }),
3443
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Subtitle", help: "Up to 150 characters.", htmlFor: "card-subtitle", children: /* @__PURE__ */ jsxRuntime.jsx(
3645
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Subtitle", help: "Up to 150 characters.", htmlFor: `${idPrefix}-subtitle`, children: /* @__PURE__ */ jsxRuntime.jsx(
3444
3646
  Input,
3445
3647
  {
3446
- id: "card-subtitle",
3648
+ id: `${idPrefix}-subtitle`,
3447
3649
  value: block.subtitle?.text ?? "",
3448
3650
  maxLength: 150,
3449
3651
  placeholder: "e.g. Released April 28",
@@ -3453,10 +3655,10 @@ function CardEditor({ block, onChange }) {
3453
3655
  })
3454
3656
  }
3455
3657
  ) }),
3456
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Body", help: "Up to 200 characters. Supports mrkdwn formatting.", htmlFor: "card-body", children: /* @__PURE__ */ jsxRuntime.jsx(
3658
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Body", help: "Up to 200 characters. Supports mrkdwn formatting.", htmlFor: `${idPrefix}-body`, children: /* @__PURE__ */ jsxRuntime.jsx(
3457
3659
  Textarea,
3458
3660
  {
3459
- id: "card-body",
3661
+ id: `${idPrefix}-body`,
3460
3662
  value: block.body?.text ?? "",
3461
3663
  maxLength: 200,
3462
3664
  rows: 3,
@@ -3472,7 +3674,7 @@ function CardEditor({ block, onChange }) {
3472
3674
  {
3473
3675
  label: "Hero image",
3474
3676
  help: "Wide image displayed at the top of the card.",
3475
- idPrefix: "card-hero",
3677
+ idPrefix: `${idPrefix}-hero`,
3476
3678
  image: block.hero_image,
3477
3679
  onChange: (next) => onChange({ ...block, hero_image: next })
3478
3680
  }
@@ -3482,7 +3684,7 @@ function CardEditor({ block, onChange }) {
3482
3684
  {
3483
3685
  label: "Icon",
3484
3686
  help: "Small image shown next to the title.",
3485
- idPrefix: "card-icon",
3687
+ idPrefix: `${idPrefix}-icon`,
3486
3688
  image: block.icon,
3487
3689
  onChange: (next) => onChange({ ...block, icon: next })
3488
3690
  }
@@ -3490,6 +3692,7 @@ function CardEditor({ block, onChange }) {
3490
3692
  /* @__PURE__ */ jsxRuntime.jsx(
3491
3693
  ButtonsField,
3492
3694
  {
3695
+ idPrefix,
3493
3696
  buttons: block.actions ?? [],
3494
3697
  onChange: (next) => onChange({ ...block, actions: next.length > 0 ? next : void 0 })
3495
3698
  }
@@ -3546,7 +3749,11 @@ function ImageFields({
3546
3749
  ) })
3547
3750
  ] });
3548
3751
  }
3549
- function ButtonsField({ buttons, onChange }) {
3752
+ function ButtonsField({
3753
+ buttons,
3754
+ onChange,
3755
+ idPrefix = "card"
3756
+ }) {
3550
3757
  const update = (idx, change) => {
3551
3758
  onChange(buttons.map((b, i) => i === idx ? { ...b, ...change } : b));
3552
3759
  };
@@ -3589,10 +3796,10 @@ function ButtonsField({ buttons, onChange }) {
3589
3796
  }
3590
3797
  )
3591
3798
  ] }),
3592
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Label", htmlFor: `card-btn-label-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
3799
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Label", htmlFor: `${idPrefix}-btn-label-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
3593
3800
  Input,
3594
3801
  {
3595
- id: `card-btn-label-${idx}`,
3802
+ id: `${idPrefix}-btn-label-${idx}`,
3596
3803
  value: label,
3597
3804
  placeholder: "e.g. Open",
3598
3805
  onChange: (e) => update(idx, {
@@ -3609,11 +3816,11 @@ function ButtonsField({ buttons, onChange }) {
3609
3816
  {
3610
3817
  label: "Link URL",
3611
3818
  help: "Optional. If set, the button opens this link.",
3612
- htmlFor: `card-btn-url-${idx}`,
3819
+ htmlFor: `${idPrefix}-btn-url-${idx}`,
3613
3820
  children: /* @__PURE__ */ jsxRuntime.jsx(
3614
3821
  Input,
3615
3822
  {
3616
- id: `card-btn-url-${idx}`,
3823
+ id: `${idPrefix}-btn-url-${idx}`,
3617
3824
  type: "url",
3618
3825
  value: url,
3619
3826
  placeholder: "e.g. https://example.com",
@@ -3632,8 +3839,8 @@ function ButtonsField({ buttons, onChange }) {
3632
3839
  },
3633
3840
  className: "flex flex-row gap-3",
3634
3841
  children: ["default", "primary", "danger"].map((s) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3635
- /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: s, id: `card-btn-style-${idx}-${s}` }),
3636
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `card-btn-style-${idx}-${s}`, className: "text-xs capitalize", children: s })
3842
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: s, id: `${idPrefix}-btn-style-${idx}-${s}` }),
3843
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `${idPrefix}-btn-style-${idx}-${s}`, className: "text-xs capitalize", children: s })
3637
3844
  ] }, s))
3638
3845
  }
3639
3846
  ) })
@@ -3682,8 +3889,8 @@ function CarouselEditor({ block, onChange }) {
3682
3889
  });
3683
3890
  };
3684
3891
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
3685
- /* @__PURE__ */ jsxRuntime.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." }),
3686
- elements.map((card, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
3892
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "Carousels hold 1-10 cards." }),
3893
+ elements.map((card, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 rounded-md border bg-muted/20 p-3", children: [
3687
3894
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3688
3895
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground", children: [
3689
3896
  "Card ",
@@ -3701,33 +3908,7 @@ function CarouselEditor({ block, onChange }) {
3701
3908
  }
3702
3909
  )
3703
3910
  ] }),
3704
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", htmlFor: `carousel-card-title-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
3705
- Input,
3706
- {
3707
- id: `carousel-card-title-${idx}`,
3708
- value: card.title?.text ?? "",
3709
- maxLength: 150,
3710
- placeholder: "e.g. Card title",
3711
- onChange: (e) => updateCard(idx, {
3712
- ...card,
3713
- title: e.target.value ? { type: "mrkdwn", text: e.target.value } : void 0
3714
- })
3715
- }
3716
- ) }),
3717
- /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Body", htmlFor: `carousel-card-body-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
3718
- Textarea,
3719
- {
3720
- id: `carousel-card-body-${idx}`,
3721
- value: card.body?.text ?? "",
3722
- maxLength: 200,
3723
- rows: 2,
3724
- placeholder: "e.g. Short card description.",
3725
- onChange: (e) => updateCard(idx, {
3726
- ...card,
3727
- body: e.target.value ? { type: "mrkdwn", text: e.target.value } : void 0
3728
- })
3729
- }
3730
- ) })
3911
+ /* @__PURE__ */ jsxRuntime.jsx(CardEditor, { block: card, idPrefix: `carousel-card-${idx}`, onChange: (next) => updateCard(idx, next) })
3731
3912
  ] }, idx)),
3732
3913
  /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: addCard, disabled: elements.length >= MAX_CARDS, className: "self-start", children: [
3733
3914
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
@@ -4189,7 +4370,7 @@ function TextItemCard({
4189
4370
  id: `ctx-text-${index}`,
4190
4371
  value: element.text,
4191
4372
  rows: 2,
4192
- placeholder: "e.g. Posted by @stephen",
4373
+ placeholder: "e.g. Posted by @riley",
4193
4374
  onChange: (e) => onChange({ ...element, text: e.target.value })
4194
4375
  }
4195
4376
  ) }),
@@ -4274,7 +4455,7 @@ function ImageItemCard({
4274
4455
  {
4275
4456
  id: `ctx-img-alt-${index}`,
4276
4457
  value: element.alt_text,
4277
- placeholder: "e.g. Stephen's avatar",
4458
+ placeholder: "e.g. Riley's avatar",
4278
4459
  onChange: (e) => onChange({ ...element, alt_text: e.target.value })
4279
4460
  }
4280
4461
  ) })
@@ -8607,6 +8788,8 @@ var SURFACE_DROPPABLE_ID = "builder-surface";
8607
8788
  function Surface({
8608
8789
  blocks,
8609
8790
  workspaceName,
8791
+ authorName,
8792
+ authorIcon,
8610
8793
  previewHooks,
8611
8794
  previewTheme,
8612
8795
  previewSurface = "message",
@@ -8682,16 +8865,20 @@ function Surface({
8682
8865
  {
8683
8866
  "aria-label": "Block preview",
8684
8867
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
8685
- 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(MessageFrame, { workspaceName, isDark, children: blocksList }) })
8868
+ 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(MessageFrame, { workspaceName, authorName, authorIcon, isDark, children: blocksList }) })
8686
8869
  }
8687
8870
  );
8688
8871
  }
8689
8872
  function MessageFrame({
8690
8873
  workspaceName,
8874
+ authorName,
8875
+ authorIcon,
8691
8876
  isDark,
8692
8877
  children
8693
8878
  }) {
8694
- const initial = (workspaceName ?? "A").slice(0, 1).toUpperCase();
8879
+ const displayName = authorName ?? workspaceName ?? "Your app";
8880
+ const initial = displayName.slice(0, 1).toUpperCase();
8881
+ const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
8695
8882
  return /* @__PURE__ */ jsxRuntime.jsxs(
8696
8883
  "div",
8697
8884
  {
@@ -8705,7 +8892,7 @@ function MessageFrame({
8705
8892
  {
8706
8893
  className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
8707
8894
  children: [
8708
- /* @__PURE__ */ jsxRuntime.jsx(
8895
+ safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
8709
8896
  "span",
8710
8897
  {
8711
8898
  className: cn(
@@ -8715,17 +8902,7 @@ function MessageFrame({
8715
8902
  children: initial
8716
8903
  }
8717
8904
  ),
8718
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: workspaceName ?? "Your app" }),
8719
- /* @__PURE__ */ jsxRuntime.jsx(
8720
- "span",
8721
- {
8722
- className: cn(
8723
- "rounded px-1 text-[10px] font-semibold",
8724
- isDark ? "bg-white/10 text-white/70" : "bg-[#f3f3f3] text-[#616061]"
8725
- ),
8726
- children: "APP"
8727
- }
8728
- ),
8905
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
8729
8906
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "10:37 AM" })
8730
8907
  ]
8731
8908
  }
@@ -8927,6 +9104,14 @@ var SURFACE_OPTIONS = [
8927
9104
  ];
8928
9105
  var DEFAULT_DOCS_HREF = "https://docs.slack.dev/reference/block-kit/blocks";
8929
9106
  var DEFAULT_DOCS_LABEL = "Docs";
9107
+ function formatMessageTs(ts) {
9108
+ const seconds = Number.parseFloat(ts);
9109
+ if (!Number.isFinite(seconds)) return ts;
9110
+ const date = new Date(seconds * 1e3);
9111
+ if (Number.isNaN(date.getTime())) return ts;
9112
+ return date.toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
9113
+ }
9114
+ 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";
8930
9115
  function Toolbar2({
8931
9116
  onClear,
8932
9117
  onOpenJson,
@@ -8943,7 +9128,14 @@ function Toolbar2({
8943
9128
  showThemeControl = true,
8944
9129
  docsLink,
8945
9130
  errorCount,
8946
- sendButtonLabel = "Send"
9131
+ sendButtonLabel = "Review & send",
9132
+ editingEnabled = false,
9133
+ editBadge,
9134
+ onOpenLoad,
9135
+ onOpenUpdate,
9136
+ onExitEdit,
9137
+ loadButtonLabel = "Load message",
9138
+ updateButtonLabel = "Review & update"
8947
9139
  }) {
8948
9140
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
8949
9141
  const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
@@ -8953,184 +9145,210 @@ function Toolbar2({
8953
9145
  const docsLabel = docsLink === false ? null : docsLink?.label ?? DEFAULT_DOCS_LABEL;
8954
9146
  const [surfaceMenuOpen, setSurfaceMenuOpen] = React.useState(false);
8955
9147
  const [themeMenuOpen, setThemeMenuOpen] = React.useState(false);
8956
- const [moreMenuOpen, setMoreMenuOpen] = React.useState(false);
8957
- return /* @__PURE__ */ jsxRuntime.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: [
8958
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
8959
- onOpenPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
8960
- Button,
8961
- {
8962
- type: "button",
8963
- variant: "ghost",
8964
- size: "sm",
8965
- onClick: onOpenPalette,
8966
- className: "md:hidden",
8967
- "aria-label": "Add a block",
8968
- children: [
8969
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
8970
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Blocks" })
8971
- ]
8972
- }
8973
- ) : null,
8974
- showSurfaceControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
8975
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
8976
- /* @__PURE__ */ jsxRuntime.jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
8977
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: activeSurface.label }),
8978
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8979
- ] }) }),
8980
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
8981
- Menu,
9148
+ const [sendMenuOpen, setSendMenuOpen] = React.useState(false);
9149
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9150
+ /* @__PURE__ */ jsxRuntime.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: [
9151
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
9152
+ editingEnabled && !editBadge ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenLoad, "aria-label": loadButtonLabel, children: [
9153
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9154
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: loadButtonLabel })
9155
+ ] }) : null,
9156
+ onOpenPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
9157
+ Button,
8982
9158
  {
8983
- ariaLabel: "Preview surface",
8984
- options: surfaceOptions,
8985
- value: previewSurface,
8986
- onChange: (next) => {
8987
- onPreviewSurfaceChange(next);
8988
- setSurfaceMenuOpen(false);
9159
+ type: "button",
9160
+ variant: "ghost",
9161
+ size: "sm",
9162
+ onClick: onOpenPalette,
9163
+ className: "md:hidden",
9164
+ "aria-label": "Add a block",
9165
+ children: [
9166
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
9167
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Blocks" })
9168
+ ]
9169
+ }
9170
+ ) : null,
9171
+ showSurfaceControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
9172
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
9173
+ /* @__PURE__ */ jsxRuntime.jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
9174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: activeSurface.label }),
9175
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "hidden h-3.5 w-3.5 opacity-60 md:inline" })
9176
+ ] }) }),
9177
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
9178
+ Menu,
9179
+ {
9180
+ ariaLabel: "Preview surface",
9181
+ options: surfaceOptions,
9182
+ value: previewSurface,
9183
+ onChange: (next) => {
9184
+ onPreviewSurfaceChange(next);
9185
+ setSurfaceMenuOpen(false);
9186
+ }
9187
+ }
9188
+ ) })
9189
+ ] }) : null,
9190
+ showThemeControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
9191
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
9192
+ /* @__PURE__ */ jsxRuntime.jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
9193
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: activeTheme.label }),
9194
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
9195
+ ] }) }),
9196
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
9197
+ Menu,
9198
+ {
9199
+ ariaLabel: "Preview theme",
9200
+ options: THEME_OPTIONS,
9201
+ value: previewTheme,
9202
+ onChange: (next) => {
9203
+ onPreviewThemeChange(next);
9204
+ setThemeMenuOpen(false);
9205
+ }
8989
9206
  }
9207
+ ) })
9208
+ ] }) : null,
9209
+ docsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
9210
+ "a",
9211
+ {
9212
+ href: docsHref,
9213
+ target: "_blank",
9214
+ rel: "noreferrer noopener",
9215
+ 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",
9216
+ "aria-label": `${docsLabel} (opens in a new tab)`,
9217
+ children: [
9218
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9219
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: docsLabel }),
9220
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "hidden h-3 w-3 opacity-50 md:inline" })
9221
+ ]
8990
9222
  }
8991
- ) })
8992
- ] }) : null,
8993
- showThemeControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
8994
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
8995
- /* @__PURE__ */ jsxRuntime.jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
8996
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: activeTheme.label }),
8997
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8998
- ] }) }),
8999
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
9000
- Menu,
9223
+ ) : null
9224
+ ] }),
9225
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9226
+ errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9227
+ Button,
9001
9228
  {
9002
- ariaLabel: "Preview theme",
9003
- options: THEME_OPTIONS,
9004
- value: previewTheme,
9005
- onChange: (next) => {
9006
- onPreviewThemeChange(next);
9007
- setThemeMenuOpen(false);
9008
- }
9229
+ type: "button",
9230
+ variant: "ghost",
9231
+ size: "sm",
9232
+ onClick: onOpenIssues,
9233
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9234
+ "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9235
+ children: [
9236
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
9237
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
9238
+ errorCount,
9239
+ " ",
9240
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: errorCount === 1 ? "issue" : "issues" })
9241
+ ] })
9242
+ ]
9009
9243
  }
9010
- ) })
9011
- ] }) : null,
9012
- docsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
9013
- "a",
9014
- {
9015
- href: docsHref,
9016
- target: "_blank",
9017
- rel: "noreferrer noopener",
9018
- 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",
9019
- "aria-label": `${docsLabel} (opens in a new tab)`,
9020
- children: [
9021
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9022
- docsLabel,
9023
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3 opacity-50" })
9024
- ]
9025
- }
9026
- ) : null
9027
- ] }),
9028
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9029
- errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9030
- Button,
9031
- {
9032
- type: "button",
9033
- variant: "ghost",
9034
- size: "sm",
9035
- onClick: onOpenIssues,
9036
- className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9037
- "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9038
- children: [
9039
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
9040
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
9041
- errorCount,
9042
- " ",
9043
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: errorCount === 1 ? "issue" : "issues" })
9044
- ] })
9045
- ]
9046
- }
9047
- ) : null,
9048
- /* @__PURE__ */ jsxRuntime.jsxs(
9049
- Button,
9050
- {
9051
- type: "button",
9052
- variant: "ghost",
9053
- size: "sm",
9054
- onClick: onClear,
9055
- disabled: !canClear,
9056
- className: "hidden hover:bg-destructive/10 hover:text-destructive sm:inline-flex",
9057
- "aria-label": "Clear all blocks",
9058
- children: [
9059
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9060
- "Clear"
9061
- ]
9062
- }
9063
- ),
9064
- /* @__PURE__ */ jsxRuntime.jsxs(
9065
- Button,
9066
- {
9067
- type: "button",
9068
- variant: "ghost",
9069
- size: "sm",
9070
- onClick: onOpenJson,
9071
- "aria-label": "View JSON",
9072
- className: "hidden sm:inline-flex",
9073
- children: [
9074
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9075
- "View JSON"
9076
- ]
9077
- }
9078
- ),
9079
- /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: moreMenuOpen, onOpenChange: setMoreMenuOpen, children: [
9080
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
9244
+ ) : null,
9245
+ /* @__PURE__ */ jsxRuntime.jsxs(
9081
9246
  Button,
9082
9247
  {
9083
9248
  type: "button",
9084
9249
  variant: "ghost",
9085
9250
  size: "sm",
9086
- "aria-label": "More actions",
9087
- "aria-haspopup": "menu",
9088
- className: "px-2 sm:hidden",
9089
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "h-3.5 w-3.5" })
9251
+ onClick: onClear,
9252
+ disabled: !canClear,
9253
+ className: "hover:bg-destructive/10 hover:text-destructive",
9254
+ "aria-label": "Clear all blocks",
9255
+ children: [
9256
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9257
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: "Clear" })
9258
+ ]
9090
9259
  }
9091
- ) }),
9092
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "end", className: "w-44 p-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "menu", "aria-label": "More actions", className: "flex flex-col", children: [
9093
- /* @__PURE__ */ jsxRuntime.jsx(
9094
- ActionMenuItem,
9095
- {
9096
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9097
- onSelect: () => {
9098
- setMoreMenuOpen(false);
9099
- onClear();
9100
- },
9101
- disabled: !canClear,
9102
- tone: "destructive",
9103
- children: "Clear"
9104
- }
9105
- ),
9106
- /* @__PURE__ */ jsxRuntime.jsx(
9107
- ActionMenuItem,
9108
- {
9109
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9110
- onSelect: () => {
9111
- setMoreMenuOpen(false);
9112
- onOpenJson();
9113
- },
9114
- children: "View JSON"
9115
- }
9116
- ),
9117
- docsHref ? /* @__PURE__ */ jsxRuntime.jsx(
9118
- ActionMenuLink,
9119
- {
9120
- href: docsHref,
9121
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9122
- trailingIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3 opacity-50" }),
9123
- onSelect: () => setMoreMenuOpen(false),
9124
- children: docsLabel
9125
- }
9126
- ) : null
9127
- ] }) })
9260
+ ),
9261
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onOpenJson, "aria-label": "View JSON", children: [
9262
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: "View JSON" })
9264
+ ] }),
9265
+ editBadge ? (
9266
+ // A message is loaded: split button. Main action updates it in
9267
+ // place; the menu also offers posting the blocks as a new message.
9268
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-stretch", children: [
9269
+ /* @__PURE__ */ jsxRuntime.jsxs(
9270
+ Button,
9271
+ {
9272
+ type: "button",
9273
+ size: "sm",
9274
+ onClick: onOpenUpdate,
9275
+ disabled: !canSend,
9276
+ "aria-label": updateButtonLabel,
9277
+ className: "rounded-r-none",
9278
+ children: [
9279
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9280
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: updateButtonLabel })
9281
+ ]
9282
+ }
9283
+ ),
9284
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: sendMenuOpen, onOpenChange: setSendMenuOpen, children: [
9285
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
9286
+ Button,
9287
+ {
9288
+ type: "button",
9289
+ size: "sm",
9290
+ "aria-label": "More message options",
9291
+ "aria-haspopup": "menu",
9292
+ className: "rounded-l-none border-l border-l-primary-foreground/30! px-1.5",
9293
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5" })
9294
+ }
9295
+ ) }),
9296
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "end", className: "w-56 p-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "menu", "aria-label": "Message options", className: "flex flex-col", children: [
9297
+ /* @__PURE__ */ jsxRuntime.jsxs(
9298
+ "button",
9299
+ {
9300
+ type: "button",
9301
+ role: "menuitem",
9302
+ disabled: !canSend,
9303
+ onClick: () => {
9304
+ setSendMenuOpen(false);
9305
+ onOpenUpdate?.();
9306
+ },
9307
+ className: SEND_MENU_ITEM,
9308
+ children: [
9309
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9310
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: "Update message" })
9311
+ ]
9312
+ }
9313
+ ),
9314
+ /* @__PURE__ */ jsxRuntime.jsxs(
9315
+ "button",
9316
+ {
9317
+ type: "button",
9318
+ role: "menuitem",
9319
+ disabled: !canSend,
9320
+ onClick: () => {
9321
+ setSendMenuOpen(false);
9322
+ onOpenSend();
9323
+ },
9324
+ className: SEND_MENU_ITEM,
9325
+ children: [
9326
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9327
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: "Send as a new message" })
9328
+ ]
9329
+ }
9330
+ )
9331
+ ] }) })
9332
+ ] })
9333
+ ] })
9334
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9335
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9336
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: sendButtonLabel })
9337
+ ] })
9338
+ ] })
9339
+ ] }),
9340
+ editingEnabled && editBadge ? /* @__PURE__ */ jsxRuntime.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: [
9341
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4 shrink-0 text-primary" }),
9342
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 text-sm", children: [
9343
+ "References an existing message in ",
9344
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: editBadge.channelLabel }),
9345
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs opacity-70", title: editBadge.ts, children: formatMessageTs(editBadge.ts) })
9128
9346
  ] }),
9129
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9130
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9131
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
9347
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onExitEdit, className: "shrink-0", children: [
9348
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" }),
9349
+ "Switch to a new message"
9132
9350
  ] })
9133
- ] })
9351
+ ] }) : null
9134
9352
  ] });
9135
9353
  }
9136
9354
  function Menu({
@@ -9188,52 +9406,132 @@ function Menu({
9188
9406
  );
9189
9407
  }) });
9190
9408
  }
9191
- 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";
9192
- function ActionMenuItem({
9193
- icon,
9194
- children,
9195
- onSelect,
9196
- disabled,
9197
- tone
9409
+ function UpdateDialog({
9410
+ open,
9411
+ onOpenChange,
9412
+ target,
9413
+ blocks,
9414
+ loadSendAsUserStatus,
9415
+ onUpdate,
9416
+ confirmUpdateLabel = "Update message",
9417
+ errorCount,
9418
+ onShowIssues
9198
9419
  }) {
9199
- return /* @__PURE__ */ jsxRuntime.jsxs(
9200
- "button",
9201
- {
9202
- type: "button",
9203
- role: "menuitem",
9204
- onClick: onSelect,
9205
- disabled,
9206
- className: cn(ACTION_ITEM_BASE, tone === "destructive" && "hover:bg-destructive/10 hover:text-destructive"),
9207
- children: [
9208
- icon,
9209
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children })
9210
- ]
9420
+ const asUser = target.editableVia === "user";
9421
+ const [userStatus, setUserStatus] = React.useState(null);
9422
+ const [status, setStatus] = React.useState({ kind: "idle" });
9423
+ const loadSendAsUserStatusRef = React.useRef(loadSendAsUserStatus);
9424
+ React.useEffect(() => {
9425
+ loadSendAsUserStatusRef.current = loadSendAsUserStatus;
9426
+ });
9427
+ const refreshSendAsUser = React.useCallback(() => {
9428
+ loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
9429
+ }, []);
9430
+ React.useEffect(() => {
9431
+ if (!open) {
9432
+ return;
9211
9433
  }
9212
- );
9213
- }
9214
- function ActionMenuLink({
9215
- href,
9216
- icon,
9217
- trailingIcon,
9218
- children,
9219
- onSelect
9220
- }) {
9221
- return /* @__PURE__ */ jsxRuntime.jsxs(
9222
- "a",
9223
- {
9224
- role: "menuitem",
9225
- href,
9226
- target: "_blank",
9227
- rel: "noreferrer noopener",
9228
- onClick: onSelect,
9229
- className: ACTION_ITEM_BASE,
9230
- children: [
9231
- icon,
9232
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children }),
9233
- trailingIcon
9234
- ]
9434
+ setStatus({ kind: "idle" });
9435
+ if (asUser) {
9436
+ setUserStatus(null);
9437
+ refreshSendAsUser();
9235
9438
  }
9236
- );
9439
+ }, [open, asUser, refreshSendAsUser]);
9440
+ React.useEffect(() => {
9441
+ if (!open || !asUser) {
9442
+ return;
9443
+ }
9444
+ const handler = () => refreshSendAsUser();
9445
+ window.addEventListener("focus", handler);
9446
+ return () => window.removeEventListener("focus", handler);
9447
+ }, [open, asUser, refreshSendAsUser]);
9448
+ const needsSignIn = asUser && userStatus !== null && !userStatus.canSendAsUser;
9449
+ const handleSubmit = async () => {
9450
+ setStatus({ kind: "updating" });
9451
+ try {
9452
+ const result = await onUpdate({
9453
+ channelId: target.channelId,
9454
+ ts: target.ts,
9455
+ blocks: toSlackBlocks(blocks),
9456
+ asUser
9457
+ });
9458
+ if (result.ok) {
9459
+ setStatus({ kind: "idle" });
9460
+ onOpenChange(false);
9461
+ return;
9462
+ }
9463
+ setStatus({ kind: "error", error: result.error ?? "Update failed." });
9464
+ } catch (e) {
9465
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Update failed." });
9466
+ }
9467
+ };
9468
+ const channelLabel = target.channelName ? `#${target.channelName}` : target.channelId;
9469
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg", children: [
9470
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
9471
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Update message" }),
9472
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "This replaces the message that's already posted. It stays in the same channel and can't be moved." })
9473
+ ] }),
9474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
9475
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
9476
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Channel" }),
9477
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground", children: channelLabel })
9478
+ ] }),
9479
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
9480
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Posting as" }),
9481
+ /* @__PURE__ */ jsxRuntime.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" }),
9482
+ needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
9483
+ /* @__PURE__ */ jsxRuntime.jsxs(
9484
+ "a",
9485
+ {
9486
+ href: userStatus.oauthUrl,
9487
+ target: "_blank",
9488
+ rel: "noopener noreferrer",
9489
+ className: "inline-flex items-center gap-1 text-primary hover:underline",
9490
+ children: [
9491
+ "Sign in with Slack ",
9492
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
9493
+ ]
9494
+ }
9495
+ ),
9496
+ " ",
9497
+ "to update your own message."
9498
+ ] }),
9499
+ needsSignIn && !userStatus?.oauthUrl && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Sign in with Slack to update your own message." })
9500
+ ] }),
9501
+ errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9502
+ "button",
9503
+ {
9504
+ type: "button",
9505
+ onClick: onShowIssues,
9506
+ 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",
9507
+ children: [
9508
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
9509
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
9510
+ "Fix ",
9511
+ errorCount,
9512
+ " ",
9513
+ errorCount === 1 ? "issue" : "issues",
9514
+ " before updating."
9515
+ ] }),
9516
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 underline", children: "Show issues" })
9517
+ ]
9518
+ }
9519
+ ) : null,
9520
+ status.kind === "error" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: status.error })
9521
+ ] }),
9522
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
9523
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
9524
+ /* @__PURE__ */ jsxRuntime.jsx(
9525
+ Button,
9526
+ {
9527
+ type: "button",
9528
+ onClick: handleSubmit,
9529
+ disabled: status.kind === "updating" || blocks.length === 0 || errorCount > 0 || needsSignIn,
9530
+ children: status.kind === "updating" ? "Updating\u2026" : confirmUpdateLabel
9531
+ }
9532
+ )
9533
+ ] })
9534
+ ] }) });
9237
9535
  }
9238
9536
  function BlockKitchen(props) {
9239
9537
  const {
@@ -9245,6 +9543,10 @@ function BlockKitchen(props) {
9245
9543
  loadChannels,
9246
9544
  loadSendAsUserStatus,
9247
9545
  onSend,
9546
+ editing,
9547
+ loadButtonLabel,
9548
+ updateButtonLabel,
9549
+ confirmUpdateLabel,
9248
9550
  palette,
9249
9551
  disabledBlockTypes,
9250
9552
  showPaletteSearch,
@@ -9290,6 +9592,10 @@ function BlockKitchen(props) {
9290
9592
  }, [blocks]);
9291
9593
  const [jsonOpen, setJsonOpen] = React.useState(false);
9292
9594
  const [sendOpen, setSendOpen] = React.useState(false);
9595
+ const [updateOpen, setUpdateOpen] = React.useState(false);
9596
+ const [loadOpen, setLoadOpen] = React.useState(false);
9597
+ const [editTarget, setEditTarget] = React.useState(null);
9598
+ const activeEditTarget = editing ? editTarget : null;
9293
9599
  const [issuesOpen, setIssuesOpen] = React.useState(false);
9294
9600
  const [paletteOpen, setPaletteOpen] = React.useState(false);
9295
9601
  const [openBlockId, setOpenBlockId] = React.useState(null);
@@ -9410,7 +9716,21 @@ function BlockKitchen(props) {
9410
9716
  allowedSurfaces,
9411
9717
  showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
9412
9718
  docsLink,
9413
- sendButtonLabel
9719
+ sendButtonLabel,
9720
+ editingEnabled: !!editing,
9721
+ editBadge: activeEditTarget ? {
9722
+ channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
9723
+ ts: activeEditTarget.ts
9724
+ } : null,
9725
+ onOpenLoad: () => setLoadOpen(true),
9726
+ onOpenUpdate: () => setUpdateOpen(true),
9727
+ onExitEdit: () => {
9728
+ setEditTarget(null);
9729
+ replaceAll([]);
9730
+ setLoadOpen(true);
9731
+ },
9732
+ loadButtonLabel,
9733
+ updateButtonLabel
9414
9734
  }
9415
9735
  ),
9416
9736
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -9429,6 +9749,8 @@ function BlockKitchen(props) {
9429
9749
  {
9430
9750
  blocks,
9431
9751
  workspaceName,
9752
+ authorName: activeEditTarget?.username,
9753
+ authorIcon: activeEditTarget?.iconUrl,
9432
9754
  previewHooks: mergedPreviewHooks,
9433
9755
  previewTheme,
9434
9756
  previewSurface,
@@ -9494,6 +9816,52 @@ function BlockKitchen(props) {
9494
9816
  }
9495
9817
  }
9496
9818
  ),
9819
+ activeEditTarget && editing ? /* @__PURE__ */ jsxRuntime.jsx(
9820
+ UpdateDialog,
9821
+ {
9822
+ open: updateOpen,
9823
+ onOpenChange: setUpdateOpen,
9824
+ target: activeEditTarget,
9825
+ blocks: blockPayloads,
9826
+ loadSendAsUserStatus,
9827
+ onUpdate: editing.onUpdate,
9828
+ confirmUpdateLabel,
9829
+ errorCount: validation.total,
9830
+ onShowIssues: () => {
9831
+ setUpdateOpen(false);
9832
+ setIssuesOpen(true);
9833
+ }
9834
+ }
9835
+ ) : null,
9836
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(
9837
+ LoadMessageDialog,
9838
+ {
9839
+ open: loadOpen,
9840
+ onOpenChange: setLoadOpen,
9841
+ onLoadMessage: editing.onLoadMessage,
9842
+ loadRecentMessages: editing.loadRecentMessages,
9843
+ onLoaded: (result) => {
9844
+ replaceAll(result.blocks);
9845
+ setEditTarget({
9846
+ channelId: result.channelId,
9847
+ channelName: result.channelName,
9848
+ ts: result.ts,
9849
+ editableVia: result.editableVia,
9850
+ workspaceName: result.workspaceName,
9851
+ username: result.username,
9852
+ iconUrl: result.iconUrl
9853
+ });
9854
+ setLoadOpen(false);
9855
+ },
9856
+ onOpenAsNew: (loadedBlocks) => {
9857
+ if (loadedBlocks) {
9858
+ replaceAll(loadedBlocks);
9859
+ }
9860
+ setEditTarget(null);
9861
+ setLoadOpen(false);
9862
+ }
9863
+ }
9864
+ ) : null,
9497
9865
  /* @__PURE__ */ jsxRuntime.jsx(
9498
9866
  IssuesSheet,
9499
9867
  {