@tightknitai/block-kitchen 0.8.10 → 0.9.0

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,265 @@ 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
+ loadChannels,
2855
+ onLoaded,
2856
+ onOpenAsNew
2857
+ }) {
2858
+ const [link, setLink] = React.useState("");
2859
+ const [status, setStatus] = React.useState({ kind: "idle" });
2860
+ const [channels, setChannels] = React.useState(null);
2861
+ const [channelsError, setChannelsError] = React.useState(null);
2862
+ const [channelId, setChannelId] = React.useState("");
2863
+ const [recent, setRecent] = React.useState(null);
2864
+ const [recentError, setRecentError] = React.useState(null);
2865
+ const inputRef = React.useRef(null);
2866
+ const onLoadMessageRef = React.useRef(onLoadMessage);
2867
+ const loadRecentMessagesRef = React.useRef(loadRecentMessages);
2868
+ const loadChannelsRef = React.useRef(loadChannels);
2869
+ React.useEffect(() => {
2870
+ onLoadMessageRef.current = onLoadMessage;
2871
+ loadRecentMessagesRef.current = loadRecentMessages;
2872
+ loadChannelsRef.current = loadChannels;
2873
+ });
2874
+ const hasRecent = !!loadRecentMessages;
2875
+ React.useEffect(() => {
2876
+ if (!open) {
2877
+ return;
2878
+ }
2879
+ setLink("");
2880
+ setStatus({ kind: "idle" });
2881
+ setChannelId("");
2882
+ setRecent(null);
2883
+ setRecentError(null);
2884
+ if (!loadRecentMessagesRef.current) {
2885
+ setChannels([]);
2886
+ setChannelsError(null);
2887
+ return;
2888
+ }
2889
+ setChannels(null);
2890
+ setChannelsError(null);
2891
+ let cancelled = false;
2892
+ loadChannelsRef.current().then((list) => {
2893
+ if (!cancelled) {
2894
+ setChannels(list);
2895
+ }
2896
+ }).catch((e) => {
2897
+ if (!cancelled) {
2898
+ setChannelsError(e instanceof Error ? e.message : "Failed to load channels");
2899
+ }
2900
+ });
2901
+ return () => {
2902
+ cancelled = true;
2903
+ };
2904
+ }, [open]);
2905
+ React.useEffect(() => {
2906
+ if (!open || !loadRecentMessagesRef.current || !channelId) {
2907
+ return;
2908
+ }
2909
+ setRecent(null);
2910
+ setRecentError(null);
2911
+ let cancelled = false;
2912
+ loadRecentMessagesRef.current(channelId).then((list) => {
2913
+ if (!cancelled) {
2914
+ setRecent(list);
2915
+ }
2916
+ }).catch((e) => {
2917
+ if (!cancelled) {
2918
+ setRecentError(e instanceof Error ? e.message : "Failed to load recent messages.");
2919
+ }
2920
+ });
2921
+ return () => {
2922
+ cancelled = true;
2923
+ };
2924
+ }, [open, channelId]);
2925
+ const handleLoad = async () => {
2926
+ const trimmed = link.trim();
2927
+ if (!trimmed) {
2928
+ setStatus({ kind: "error", error: "Paste a Slack message link first." });
2929
+ return;
2930
+ }
2931
+ setStatus({ kind: "loading" });
2932
+ try {
2933
+ const result = await onLoadMessageRef.current({ link: trimmed });
2934
+ if (result.ok) {
2935
+ onLoaded(result);
2936
+ return;
2937
+ }
2938
+ setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks });
2939
+ } catch (e) {
2940
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
2941
+ }
2942
+ };
2943
+ const notEditable = status.kind === "not-editable" ? status : null;
2944
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
2945
+ DialogContent,
2946
+ {
2947
+ className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg",
2948
+ onOpenAutoFocus: (e) => {
2949
+ e.preventDefault();
2950
+ inputRef.current?.focus();
2951
+ },
2952
+ children: [
2953
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
2954
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Edit an existing message" }),
2955
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-1.5", children: [
2956
+ /* @__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.` }),
2957
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
2958
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
2959
+ "button",
2960
+ {
2961
+ type: "button",
2962
+ "aria-label": "How to copy a message link",
2963
+ 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",
2964
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-3.5 w-3.5" })
2965
+ }
2966
+ ) }),
2967
+ /* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
2968
+ "In Slack, hover over the message and click the ",
2969
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u22EE More actions" }),
2970
+ " button (or right-click the message), then choose ",
2971
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Copy link" }),
2972
+ "."
2973
+ ] })
2974
+ ] })
2975
+ ] })
2976
+ ] }),
2977
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2978
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
2979
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message-link", children: "Message link" }),
2980
+ /* @__PURE__ */ jsxRuntime.jsx(
2981
+ Input,
2982
+ {
2983
+ ref: inputRef,
2984
+ id: "message-link",
2985
+ value: link,
2986
+ onChange: (e) => {
2987
+ setLink(e.target.value);
2988
+ if (status.kind !== "idle" && status.kind !== "loading") {
2989
+ setStatus({ kind: "idle" });
2990
+ }
2991
+ },
2992
+ onKeyDown: (e) => {
2993
+ if (e.key === "Enter" && status.kind !== "loading") {
2994
+ e.preventDefault();
2995
+ handleLoad();
2996
+ }
2997
+ },
2998
+ placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
2999
+ autoComplete: "off",
3000
+ spellCheck: false
3001
+ }
3002
+ )
3003
+ ] }),
3004
+ 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 }),
3005
+ 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: [
3006
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
3007
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
3008
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: notEditable.reason })
3009
+ ] }),
3010
+ /* @__PURE__ */ jsxRuntime.jsx(
3011
+ Button,
3012
+ {
3013
+ type: "button",
3014
+ variant: "outline",
3015
+ size: "sm",
3016
+ className: "self-start",
3017
+ onClick: () => onOpenAsNew(notEditable.blocks),
3018
+ children: "Open as a new message instead"
3019
+ }
3020
+ )
3021
+ ] }),
3022
+ hasRecent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3023
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
3024
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" }),
3025
+ "or pick a recent message",
3026
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })
3027
+ ] }),
3028
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3029
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
3030
+ channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
3031
+ channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
3032
+ channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
3033
+ channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
3034
+ "select",
3035
+ {
3036
+ id: "recent-channel-picker",
3037
+ value: channelId,
3038
+ onChange: (e) => setChannelId(e.target.value),
3039
+ className: "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
3040
+ children: [
3041
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
3042
+ channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
3043
+ "#",
3044
+ c.name
3045
+ ] }, c.id))
3046
+ ]
3047
+ }
3048
+ )
3049
+ ] }),
3050
+ !channelId && channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
3051
+ channelId && recent === null && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3052
+ channelId && recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: recentError }),
3053
+ channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
3054
+ channelId && recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
3055
+ const asUser = (m.editableVia ?? "bot") === "user";
3056
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3057
+ "button",
3058
+ {
3059
+ type: "button",
3060
+ onClick: () => onLoaded(recentToResult(m)),
3061
+ 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",
3062
+ children: [
3063
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-baseline justify-between gap-2", children: [
3064
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-baseline gap-1.5", children: [
3065
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: m.channelName ? `#${m.channelName}` : m.channelId }),
3066
+ /* @__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" })
3067
+ ] }),
3068
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-xs text-muted-foreground", children: m.ts })
3069
+ ] }),
3070
+ m.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-muted-foreground", children: m.label })
3071
+ ]
3072
+ },
3073
+ `${m.channelId}:${m.ts}`
3074
+ );
3075
+ }) })
3076
+ ] })
3077
+ ] }),
3078
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
3079
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3080
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", onClick: handleLoad, disabled: status.kind === "loading" || !link.trim(), children: status.kind === "loading" ? "Loading\u2026" : "Load message" })
3081
+ ] })
3082
+ ]
3083
+ }
3084
+ ) });
3085
+ }
2742
3086
  var PALETTE_DRAG_PREFIX = "palette:";
2743
3087
  function parsePaletteDragId(id) {
2744
3088
  if (typeof id !== "string" || !id.startsWith(PALETTE_DRAG_PREFIX)) {
@@ -2938,100 +3282,6 @@ function PaletteItem({
2938
3282
  }
2939
3283
  );
2940
3284
  }
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
3285
  function SendDialog({
3036
3286
  open,
3037
3287
  onOpenChange,
@@ -4172,7 +4422,7 @@ function TextItemCard({
4172
4422
  id: `ctx-text-${index}`,
4173
4423
  value: element.text,
4174
4424
  rows: 2,
4175
- placeholder: "e.g. Posted by @stephen",
4425
+ placeholder: "e.g. Posted by @riley",
4176
4426
  onChange: (e) => onChange({ ...element, text: e.target.value })
4177
4427
  }
4178
4428
  ) }),
@@ -4257,7 +4507,7 @@ function ImageItemCard({
4257
4507
  {
4258
4508
  id: `ctx-img-alt-${index}`,
4259
4509
  value: element.alt_text,
4260
- placeholder: "e.g. Stephen's avatar",
4510
+ placeholder: "e.g. Riley's avatar",
4261
4511
  onChange: (e) => onChange({ ...element, alt_text: e.target.value })
4262
4512
  }
4263
4513
  ) })
@@ -8590,6 +8840,8 @@ var SURFACE_DROPPABLE_ID = "builder-surface";
8590
8840
  function Surface({
8591
8841
  blocks,
8592
8842
  workspaceName,
8843
+ authorName,
8844
+ authorIcon,
8593
8845
  previewHooks,
8594
8846
  previewTheme,
8595
8847
  previewSurface = "message",
@@ -8665,16 +8917,20 @@ function Surface({
8665
8917
  {
8666
8918
  "aria-label": "Block preview",
8667
8919
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
8668
- 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 }) })
8920
+ 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 }) })
8669
8921
  }
8670
8922
  );
8671
8923
  }
8672
8924
  function MessageFrame({
8673
8925
  workspaceName,
8926
+ authorName,
8927
+ authorIcon,
8674
8928
  isDark,
8675
8929
  children
8676
8930
  }) {
8677
- const initial = (workspaceName ?? "A").slice(0, 1).toUpperCase();
8931
+ const displayName = authorName ?? workspaceName ?? "Your app";
8932
+ const initial = displayName.slice(0, 1).toUpperCase();
8933
+ const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
8678
8934
  return /* @__PURE__ */ jsxRuntime.jsxs(
8679
8935
  "div",
8680
8936
  {
@@ -8688,7 +8944,7 @@ function MessageFrame({
8688
8944
  {
8689
8945
  className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
8690
8946
  children: [
8691
- /* @__PURE__ */ jsxRuntime.jsx(
8947
+ safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
8692
8948
  "span",
8693
8949
  {
8694
8950
  className: cn(
@@ -8698,17 +8954,7 @@ function MessageFrame({
8698
8954
  children: initial
8699
8955
  }
8700
8956
  ),
8701
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: workspaceName ?? "Your app" }),
8702
- /* @__PURE__ */ jsxRuntime.jsx(
8703
- "span",
8704
- {
8705
- className: cn(
8706
- "rounded px-1 text-[10px] font-semibold",
8707
- isDark ? "bg-white/10 text-white/70" : "bg-[#f3f3f3] text-[#616061]"
8708
- ),
8709
- children: "APP"
8710
- }
8711
- ),
8957
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
8712
8958
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "10:37 AM" })
8713
8959
  ]
8714
8960
  }
@@ -8910,6 +9156,14 @@ var SURFACE_OPTIONS = [
8910
9156
  ];
8911
9157
  var DEFAULT_DOCS_HREF = "https://docs.slack.dev/reference/block-kit/blocks";
8912
9158
  var DEFAULT_DOCS_LABEL = "Docs";
9159
+ function formatMessageTs(ts) {
9160
+ const seconds = Number.parseFloat(ts);
9161
+ if (!Number.isFinite(seconds)) return ts;
9162
+ const date = new Date(seconds * 1e3);
9163
+ if (Number.isNaN(date.getTime())) return ts;
9164
+ return date.toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
9165
+ }
9166
+ 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";
8913
9167
  function Toolbar2({
8914
9168
  onClear,
8915
9169
  onOpenJson,
@@ -8926,194 +9180,227 @@ function Toolbar2({
8926
9180
  showThemeControl = true,
8927
9181
  docsLink,
8928
9182
  errorCount,
8929
- sendButtonLabel = "Send"
9183
+ sendButtonLabel = "Review & send",
9184
+ editingEnabled = false,
9185
+ editBadge,
9186
+ onOpenLoad,
9187
+ onOpenUpdate,
9188
+ onExitEdit,
9189
+ loadButtonLabel = "Load message",
9190
+ updateButtonLabel = "Review & update"
8930
9191
  }) {
8931
9192
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
8932
- const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
8933
- const surfaceOptions = SURFACE_OPTIONS.filter((s) => allowedSurfaces.includes(s.value));
8934
- const showSurfaceControl = surfaceOptions.length > 1;
8935
- const docsHref = docsLink === false ? null : docsLink?.href ?? DEFAULT_DOCS_HREF;
8936
- const docsLabel = docsLink === false ? null : docsLink?.label ?? DEFAULT_DOCS_LABEL;
8937
- const [surfaceMenuOpen, setSurfaceMenuOpen] = React.useState(false);
8938
- const [themeMenuOpen, setThemeMenuOpen] = React.useState(false);
8939
- const [moreMenuOpen, setMoreMenuOpen] = React.useState(false);
8940
- 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: [
8941
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
8942
- onOpenPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
8943
- Button,
8944
- {
8945
- type: "button",
8946
- variant: "ghost",
8947
- size: "sm",
8948
- onClick: onOpenPalette,
8949
- className: "md:hidden",
8950
- "aria-label": "Add a block",
8951
- children: [
8952
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
8953
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Blocks" })
8954
- ]
8955
- }
8956
- ) : null,
8957
- showSurfaceControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
8958
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
8959
- /* @__PURE__ */ jsxRuntime.jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
8960
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: activeSurface.label }),
8961
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8962
- ] }) }),
8963
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
8964
- Menu,
9193
+ const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
9194
+ const surfaceOptions = SURFACE_OPTIONS.filter((s) => allowedSurfaces.includes(s.value));
9195
+ const showSurfaceControl = surfaceOptions.length > 1;
9196
+ const docsHref = docsLink === false ? null : docsLink?.href ?? DEFAULT_DOCS_HREF;
9197
+ const docsLabel = docsLink === false ? null : docsLink?.label ?? DEFAULT_DOCS_LABEL;
9198
+ const [surfaceMenuOpen, setSurfaceMenuOpen] = React.useState(false);
9199
+ const [themeMenuOpen, setThemeMenuOpen] = React.useState(false);
9200
+ const [sendMenuOpen, setSendMenuOpen] = React.useState(false);
9201
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9202
+ /* @__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: [
9203
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
9204
+ editingEnabled && !editBadge ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenLoad, "aria-label": loadButtonLabel, children: [
9205
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9206
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: loadButtonLabel })
9207
+ ] }) : null,
9208
+ onOpenPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
9209
+ Button,
8965
9210
  {
8966
- ariaLabel: "Preview surface",
8967
- options: surfaceOptions,
8968
- value: previewSurface,
8969
- onChange: (next) => {
8970
- onPreviewSurfaceChange(next);
8971
- setSurfaceMenuOpen(false);
9211
+ type: "button",
9212
+ variant: "ghost",
9213
+ size: "sm",
9214
+ onClick: onOpenPalette,
9215
+ className: "md:hidden",
9216
+ "aria-label": "Add a block",
9217
+ children: [
9218
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
9219
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Blocks" })
9220
+ ]
9221
+ }
9222
+ ) : null,
9223
+ showSurfaceControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: surfaceMenuOpen, onOpenChange: setSurfaceMenuOpen, children: [
9224
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview surface: ${activeSurface.label}`, children: [
9225
+ /* @__PURE__ */ jsxRuntime.jsx(activeSurface.Icon, { className: "h-3.5 w-3.5" }),
9226
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: activeSurface.label }),
9227
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "hidden h-3.5 w-3.5 opacity-60 md:inline" })
9228
+ ] }) }),
9229
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-36 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
9230
+ Menu,
9231
+ {
9232
+ ariaLabel: "Preview surface",
9233
+ options: surfaceOptions,
9234
+ value: previewSurface,
9235
+ onChange: (next) => {
9236
+ onPreviewSurfaceChange(next);
9237
+ setSurfaceMenuOpen(false);
9238
+ }
9239
+ }
9240
+ ) })
9241
+ ] }) : null,
9242
+ showThemeControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
9243
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
9244
+ /* @__PURE__ */ jsxRuntime.jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
9245
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: activeTheme.label }),
9246
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
9247
+ ] }) }),
9248
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
9249
+ Menu,
9250
+ {
9251
+ ariaLabel: "Preview theme",
9252
+ options: THEME_OPTIONS,
9253
+ value: previewTheme,
9254
+ onChange: (next) => {
9255
+ onPreviewThemeChange(next);
9256
+ setThemeMenuOpen(false);
9257
+ }
8972
9258
  }
9259
+ ) })
9260
+ ] }) : null,
9261
+ docsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
9262
+ "a",
9263
+ {
9264
+ href: docsHref,
9265
+ target: "_blank",
9266
+ rel: "noreferrer noopener",
9267
+ 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",
9268
+ "aria-label": `${docsLabel} (opens in a new tab)`,
9269
+ children: [
9270
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9271
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: docsLabel }),
9272
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "hidden h-3 w-3 opacity-50 md:inline" })
9273
+ ]
8973
9274
  }
8974
- ) })
8975
- ] }) : null,
8976
- showThemeControl ? /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: themeMenuOpen, onOpenChange: setThemeMenuOpen, children: [
8977
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Preview theme: ${activeTheme.label}`, children: [
8978
- /* @__PURE__ */ jsxRuntime.jsx(activeTheme.Icon, { className: "h-3.5 w-3.5" }),
8979
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: activeTheme.label }),
8980
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 opacity-60" })
8981
- ] }) }),
8982
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-32 p-1", children: /* @__PURE__ */ jsxRuntime.jsx(
8983
- Menu,
9275
+ ) : null
9276
+ ] }),
9277
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9278
+ errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9279
+ Button,
8984
9280
  {
8985
- ariaLabel: "Preview theme",
8986
- options: THEME_OPTIONS,
8987
- value: previewTheme,
8988
- onChange: (next) => {
8989
- onPreviewThemeChange(next);
8990
- setThemeMenuOpen(false);
8991
- }
9281
+ type: "button",
9282
+ variant: "ghost",
9283
+ size: "sm",
9284
+ onClick: onOpenIssues,
9285
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9286
+ "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9287
+ children: [
9288
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
9289
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
9290
+ errorCount,
9291
+ " ",
9292
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: errorCount === 1 ? "issue" : "issues" })
9293
+ ] })
9294
+ ]
8992
9295
  }
8993
- ) })
8994
- ] }) : null,
8995
- docsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
8996
- "a",
8997
- {
8998
- href: docsHref,
8999
- target: "_blank",
9000
- rel: "noreferrer noopener",
9001
- 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",
9002
- "aria-label": `${docsLabel} (opens in a new tab)`,
9003
- children: [
9004
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9005
- docsLabel,
9006
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3 opacity-50" })
9007
- ]
9008
- }
9009
- ) : null
9010
- ] }),
9011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 sm:gap-2", children: [
9012
- errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9013
- Button,
9014
- {
9015
- type: "button",
9016
- variant: "ghost",
9017
- size: "sm",
9018
- onClick: onOpenIssues,
9019
- className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
9020
- "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
9021
- children: [
9022
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
9023
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
9024
- errorCount,
9025
- " ",
9026
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: errorCount === 1 ? "issue" : "issues" })
9027
- ] })
9028
- ]
9029
- }
9030
- ) : null,
9031
- /* @__PURE__ */ jsxRuntime.jsxs(
9032
- Button,
9033
- {
9034
- type: "button",
9035
- variant: "ghost",
9036
- size: "sm",
9037
- onClick: onClear,
9038
- disabled: !canClear,
9039
- className: "hidden hover:bg-destructive/10 hover:text-destructive sm:inline-flex",
9040
- "aria-label": "Clear all blocks",
9041
- children: [
9042
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9043
- "Clear"
9044
- ]
9045
- }
9046
- ),
9047
- /* @__PURE__ */ jsxRuntime.jsxs(
9048
- Button,
9049
- {
9050
- type: "button",
9051
- variant: "ghost",
9052
- size: "sm",
9053
- onClick: onOpenJson,
9054
- "aria-label": "View JSON",
9055
- className: "hidden sm:inline-flex",
9056
- children: [
9057
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9058
- "View JSON"
9059
- ]
9060
- }
9061
- ),
9062
- /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: moreMenuOpen, onOpenChange: setMoreMenuOpen, children: [
9063
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
9296
+ ) : null,
9297
+ /* @__PURE__ */ jsxRuntime.jsxs(
9064
9298
  Button,
9065
9299
  {
9066
9300
  type: "button",
9067
9301
  variant: "ghost",
9068
9302
  size: "sm",
9069
- "aria-label": "More actions",
9070
- "aria-haspopup": "menu",
9071
- className: "px-2 sm:hidden",
9072
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "h-3.5 w-3.5" })
9303
+ onClick: onClear,
9304
+ disabled: !canClear,
9305
+ className: "hover:bg-destructive/10 hover:text-destructive",
9306
+ "aria-label": "Clear all blocks",
9307
+ children: [
9308
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9309
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: "Clear" })
9310
+ ]
9073
9311
  }
9074
- ) }),
9075
- /* @__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: [
9076
- /* @__PURE__ */ jsxRuntime.jsx(
9077
- ActionMenuItem,
9078
- {
9079
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
9080
- onSelect: () => {
9081
- setMoreMenuOpen(false);
9082
- onClear();
9083
- },
9084
- disabled: !canClear,
9085
- tone: "destructive",
9086
- children: "Clear"
9087
- }
9088
- ),
9089
- /* @__PURE__ */ jsxRuntime.jsx(
9090
- ActionMenuItem,
9091
- {
9092
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9093
- onSelect: () => {
9094
- setMoreMenuOpen(false);
9095
- onOpenJson();
9096
- },
9097
- children: "View JSON"
9098
- }
9099
- ),
9100
- docsHref ? /* @__PURE__ */ jsxRuntime.jsx(
9101
- ActionMenuLink,
9102
- {
9103
- href: docsHref,
9104
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpen, { className: "h-3.5 w-3.5" }),
9105
- trailingIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3 opacity-50" }),
9106
- onSelect: () => setMoreMenuOpen(false),
9107
- children: docsLabel
9108
- }
9109
- ) : null
9110
- ] }) })
9312
+ ),
9313
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onOpenJson, "aria-label": "View JSON", children: [
9314
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
9315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: "View JSON" })
9316
+ ] }),
9317
+ editBadge ? (
9318
+ // A message is loaded: split button. Main action updates it in
9319
+ // place; the menu also offers posting the blocks as a new message.
9320
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-stretch", children: [
9321
+ /* @__PURE__ */ jsxRuntime.jsxs(
9322
+ Button,
9323
+ {
9324
+ type: "button",
9325
+ size: "sm",
9326
+ onClick: onOpenUpdate,
9327
+ disabled: !canSend,
9328
+ "aria-label": updateButtonLabel,
9329
+ className: "rounded-r-none",
9330
+ children: [
9331
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9332
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: updateButtonLabel })
9333
+ ]
9334
+ }
9335
+ ),
9336
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: sendMenuOpen, onOpenChange: setSendMenuOpen, children: [
9337
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
9338
+ Button,
9339
+ {
9340
+ type: "button",
9341
+ size: "sm",
9342
+ "aria-label": "More message options",
9343
+ "aria-haspopup": "menu",
9344
+ className: "rounded-l-none border-l border-l-primary-foreground/30! px-1.5",
9345
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5" })
9346
+ }
9347
+ ) }),
9348
+ /* @__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: [
9349
+ /* @__PURE__ */ jsxRuntime.jsxs(
9350
+ "button",
9351
+ {
9352
+ type: "button",
9353
+ role: "menuitem",
9354
+ disabled: !canSend,
9355
+ onClick: () => {
9356
+ setSendMenuOpen(false);
9357
+ onOpenUpdate?.();
9358
+ },
9359
+ className: SEND_MENU_ITEM,
9360
+ children: [
9361
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" }),
9362
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: "Update message" })
9363
+ ]
9364
+ }
9365
+ ),
9366
+ /* @__PURE__ */ jsxRuntime.jsxs(
9367
+ "button",
9368
+ {
9369
+ type: "button",
9370
+ role: "menuitem",
9371
+ disabled: !canSend,
9372
+ onClick: () => {
9373
+ setSendMenuOpen(false);
9374
+ onOpenSend();
9375
+ },
9376
+ className: SEND_MENU_ITEM,
9377
+ children: [
9378
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9379
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: "Send as a new message" })
9380
+ ]
9381
+ }
9382
+ )
9383
+ ] }) })
9384
+ ] })
9385
+ ] })
9386
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9387
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9388
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: sendButtonLabel })
9389
+ ] })
9390
+ ] })
9391
+ ] }),
9392
+ 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: [
9393
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4 shrink-0 text-primary" }),
9394
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 text-sm", children: [
9395
+ "References an existing message in ",
9396
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: editBadge.channelLabel }),
9397
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs opacity-70", title: editBadge.ts, children: formatMessageTs(editBadge.ts) })
9111
9398
  ] }),
9112
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
9113
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
9114
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
9399
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onExitEdit, className: "shrink-0", children: [
9400
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" }),
9401
+ "Switch to a new message"
9115
9402
  ] })
9116
- ] })
9403
+ ] }) : null
9117
9404
  ] });
9118
9405
  }
9119
9406
  function Menu({
@@ -9171,52 +9458,132 @@ function Menu({
9171
9458
  );
9172
9459
  }) });
9173
9460
  }
9174
- 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";
9175
- function ActionMenuItem({
9176
- icon,
9177
- children,
9178
- onSelect,
9179
- disabled,
9180
- tone
9461
+ function UpdateDialog({
9462
+ open,
9463
+ onOpenChange,
9464
+ target,
9465
+ blocks,
9466
+ loadSendAsUserStatus,
9467
+ onUpdate,
9468
+ confirmUpdateLabel = "Update message",
9469
+ errorCount,
9470
+ onShowIssues
9181
9471
  }) {
9182
- return /* @__PURE__ */ jsxRuntime.jsxs(
9183
- "button",
9184
- {
9185
- type: "button",
9186
- role: "menuitem",
9187
- onClick: onSelect,
9188
- disabled,
9189
- className: cn(ACTION_ITEM_BASE, tone === "destructive" && "hover:bg-destructive/10 hover:text-destructive"),
9190
- children: [
9191
- icon,
9192
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children })
9193
- ]
9472
+ const asUser = target.editableVia === "user";
9473
+ const [userStatus, setUserStatus] = React.useState(null);
9474
+ const [status, setStatus] = React.useState({ kind: "idle" });
9475
+ const loadSendAsUserStatusRef = React.useRef(loadSendAsUserStatus);
9476
+ React.useEffect(() => {
9477
+ loadSendAsUserStatusRef.current = loadSendAsUserStatus;
9478
+ });
9479
+ const refreshSendAsUser = React.useCallback(() => {
9480
+ loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
9481
+ }, []);
9482
+ React.useEffect(() => {
9483
+ if (!open) {
9484
+ return;
9194
9485
  }
9195
- );
9196
- }
9197
- function ActionMenuLink({
9198
- href,
9199
- icon,
9200
- trailingIcon,
9201
- children,
9202
- onSelect
9203
- }) {
9204
- return /* @__PURE__ */ jsxRuntime.jsxs(
9205
- "a",
9206
- {
9207
- role: "menuitem",
9208
- href,
9209
- target: "_blank",
9210
- rel: "noreferrer noopener",
9211
- onClick: onSelect,
9212
- className: ACTION_ITEM_BASE,
9213
- children: [
9214
- icon,
9215
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children }),
9216
- trailingIcon
9217
- ]
9486
+ setStatus({ kind: "idle" });
9487
+ if (asUser) {
9488
+ setUserStatus(null);
9489
+ refreshSendAsUser();
9218
9490
  }
9219
- );
9491
+ }, [open, asUser, refreshSendAsUser]);
9492
+ React.useEffect(() => {
9493
+ if (!open || !asUser) {
9494
+ return;
9495
+ }
9496
+ const handler = () => refreshSendAsUser();
9497
+ window.addEventListener("focus", handler);
9498
+ return () => window.removeEventListener("focus", handler);
9499
+ }, [open, asUser, refreshSendAsUser]);
9500
+ const needsSignIn = asUser && userStatus !== null && !userStatus.canSendAsUser;
9501
+ const handleSubmit = async () => {
9502
+ setStatus({ kind: "updating" });
9503
+ try {
9504
+ const result = await onUpdate({
9505
+ channelId: target.channelId,
9506
+ ts: target.ts,
9507
+ blocks: toSlackBlocks(blocks),
9508
+ asUser
9509
+ });
9510
+ if (result.ok) {
9511
+ setStatus({ kind: "idle" });
9512
+ onOpenChange(false);
9513
+ return;
9514
+ }
9515
+ setStatus({ kind: "error", error: result.error ?? "Update failed." });
9516
+ } catch (e) {
9517
+ setStatus({ kind: "error", error: e instanceof Error ? e.message : "Update failed." });
9518
+ }
9519
+ };
9520
+ const channelLabel = target.channelName ? `#${target.channelName}` : target.channelId;
9521
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg", children: [
9522
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
9523
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Update message" }),
9524
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "This replaces the message that's already posted. It stays in the same channel and can't be moved." })
9525
+ ] }),
9526
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
9527
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
9528
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Channel" }),
9529
+ /* @__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 })
9530
+ ] }),
9531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
9532
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Posting as" }),
9533
+ /* @__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" }),
9534
+ needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
9535
+ /* @__PURE__ */ jsxRuntime.jsxs(
9536
+ "a",
9537
+ {
9538
+ href: userStatus.oauthUrl,
9539
+ target: "_blank",
9540
+ rel: "noopener noreferrer",
9541
+ className: "inline-flex items-center gap-1 text-primary hover:underline",
9542
+ children: [
9543
+ "Sign in with Slack ",
9544
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
9545
+ ]
9546
+ }
9547
+ ),
9548
+ " ",
9549
+ "to update your own message."
9550
+ ] }),
9551
+ needsSignIn && !userStatus?.oauthUrl && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Sign in with Slack to update your own message." })
9552
+ ] }),
9553
+ errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
9554
+ "button",
9555
+ {
9556
+ type: "button",
9557
+ onClick: onShowIssues,
9558
+ 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",
9559
+ children: [
9560
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
9561
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
9562
+ "Fix ",
9563
+ errorCount,
9564
+ " ",
9565
+ errorCount === 1 ? "issue" : "issues",
9566
+ " before updating."
9567
+ ] }),
9568
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 underline", children: "Show issues" })
9569
+ ]
9570
+ }
9571
+ ) : null,
9572
+ 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 })
9573
+ ] }),
9574
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
9575
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
9576
+ /* @__PURE__ */ jsxRuntime.jsx(
9577
+ Button,
9578
+ {
9579
+ type: "button",
9580
+ onClick: handleSubmit,
9581
+ disabled: status.kind === "updating" || blocks.length === 0 || errorCount > 0 || needsSignIn,
9582
+ children: status.kind === "updating" ? "Updating\u2026" : confirmUpdateLabel
9583
+ }
9584
+ )
9585
+ ] })
9586
+ ] }) });
9220
9587
  }
9221
9588
  function BlockKitchen(props) {
9222
9589
  const {
@@ -9228,6 +9595,10 @@ function BlockKitchen(props) {
9228
9595
  loadChannels,
9229
9596
  loadSendAsUserStatus,
9230
9597
  onSend,
9598
+ editing,
9599
+ loadButtonLabel,
9600
+ updateButtonLabel,
9601
+ confirmUpdateLabel,
9231
9602
  palette,
9232
9603
  disabledBlockTypes,
9233
9604
  showPaletteSearch,
@@ -9273,6 +9644,10 @@ function BlockKitchen(props) {
9273
9644
  }, [blocks]);
9274
9645
  const [jsonOpen, setJsonOpen] = React.useState(false);
9275
9646
  const [sendOpen, setSendOpen] = React.useState(false);
9647
+ const [updateOpen, setUpdateOpen] = React.useState(false);
9648
+ const [loadOpen, setLoadOpen] = React.useState(false);
9649
+ const [editTarget, setEditTarget] = React.useState(null);
9650
+ const activeEditTarget = editing ? editTarget : null;
9276
9651
  const [issuesOpen, setIssuesOpen] = React.useState(false);
9277
9652
  const [paletteOpen, setPaletteOpen] = React.useState(false);
9278
9653
  const [openBlockId, setOpenBlockId] = React.useState(null);
@@ -9393,7 +9768,21 @@ function BlockKitchen(props) {
9393
9768
  allowedSurfaces,
9394
9769
  showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
9395
9770
  docsLink,
9396
- sendButtonLabel
9771
+ sendButtonLabel,
9772
+ editingEnabled: !!editing,
9773
+ editBadge: activeEditTarget ? {
9774
+ channelLabel: activeEditTarget.channelName ? `#${activeEditTarget.channelName}` : activeEditTarget.channelId,
9775
+ ts: activeEditTarget.ts
9776
+ } : null,
9777
+ onOpenLoad: () => setLoadOpen(true),
9778
+ onOpenUpdate: () => setUpdateOpen(true),
9779
+ onExitEdit: () => {
9780
+ setEditTarget(null);
9781
+ replaceAll([]);
9782
+ setLoadOpen(true);
9783
+ },
9784
+ loadButtonLabel,
9785
+ updateButtonLabel
9397
9786
  }
9398
9787
  ),
9399
9788
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -9412,6 +9801,8 @@ function BlockKitchen(props) {
9412
9801
  {
9413
9802
  blocks,
9414
9803
  workspaceName,
9804
+ authorName: activeEditTarget?.username,
9805
+ authorIcon: activeEditTarget?.iconUrl,
9415
9806
  previewHooks: mergedPreviewHooks,
9416
9807
  previewTheme,
9417
9808
  previewSurface,
@@ -9477,6 +9868,53 @@ function BlockKitchen(props) {
9477
9868
  }
9478
9869
  }
9479
9870
  ),
9871
+ activeEditTarget && editing ? /* @__PURE__ */ jsxRuntime.jsx(
9872
+ UpdateDialog,
9873
+ {
9874
+ open: updateOpen,
9875
+ onOpenChange: setUpdateOpen,
9876
+ target: activeEditTarget,
9877
+ blocks: blockPayloads,
9878
+ loadSendAsUserStatus,
9879
+ onUpdate: editing.onUpdate,
9880
+ confirmUpdateLabel,
9881
+ errorCount: validation.total,
9882
+ onShowIssues: () => {
9883
+ setUpdateOpen(false);
9884
+ setIssuesOpen(true);
9885
+ }
9886
+ }
9887
+ ) : null,
9888
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(
9889
+ LoadMessageDialog,
9890
+ {
9891
+ open: loadOpen,
9892
+ onOpenChange: setLoadOpen,
9893
+ onLoadMessage: editing.onLoadMessage,
9894
+ loadRecentMessages: editing.loadRecentMessages,
9895
+ loadChannels,
9896
+ onLoaded: (result) => {
9897
+ replaceAll(result.blocks);
9898
+ setEditTarget({
9899
+ channelId: result.channelId,
9900
+ channelName: result.channelName,
9901
+ ts: result.ts,
9902
+ editableVia: result.editableVia,
9903
+ workspaceName: result.workspaceName,
9904
+ username: result.username,
9905
+ iconUrl: result.iconUrl
9906
+ });
9907
+ setLoadOpen(false);
9908
+ },
9909
+ onOpenAsNew: (loadedBlocks) => {
9910
+ if (loadedBlocks) {
9911
+ replaceAll(loadedBlocks);
9912
+ }
9913
+ setEditTarget(null);
9914
+ setLoadOpen(false);
9915
+ }
9916
+ }
9917
+ ) : null,
9480
9918
  /* @__PURE__ */ jsxRuntime.jsx(
9481
9919
  IssuesSheet,
9482
9920
  {