@tightknitai/block-kitchen 0.10.3 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,14 +13,14 @@ import { nanoid } from 'nanoid';
13
13
  import { Slot } from '@radix-ui/react-slot';
14
14
  import { cva } from 'class-variance-authority';
15
15
  import * as LabelPrimitive from '@radix-ui/react-label';
16
+ import 'slack-blocks-to-jsx/dist/style.css';
17
+ import { Message } from 'slack-blocks-to-jsx';
16
18
  import { CSS } from '@dnd-kit/utilities';
17
19
  import * as PopoverPrimitive from '@radix-ui/react-popover';
18
20
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
19
21
  import Link from '@tiptap/extension-link';
20
22
  import { Node, ReactNodeViewRenderer, mergeAttributes, NodeViewWrapper, useEditor, EditorContent } from '@tiptap/react';
21
23
  import StarterKit from '@tiptap/starter-kit';
22
- import 'slack-blocks-to-jsx/dist/style.css';
23
- import { Message } from 'slack-blocks-to-jsx';
24
24
 
25
25
  // src/components/block-kitchen.tsx
26
26
 
@@ -3024,6 +3024,128 @@ var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
3024
3024
  }
3025
3025
  ));
3026
3026
  Label.displayName = LabelPrimitive.Root.displayName;
3027
+ function SlackBlockPreview({
3028
+ block,
3029
+ hooks,
3030
+ theme = "light"
3031
+ }) {
3032
+ const rootRef = useRef(null);
3033
+ const safeBlock = useMemo(() => sanitizeBlock(block), [block]);
3034
+ useEffect(() => {
3035
+ const root = rootRef.current;
3036
+ if (!root) return;
3037
+ for (const wrapper of root.querySelectorAll(
3038
+ ".slack_blocks_to_jsx__image_media_trigger, .slack_blocks_to_jsx__video_title"
3039
+ )) {
3040
+ for (const btn of wrapper.querySelectorAll("button:not([aria-label])")) {
3041
+ const title = wrapper.querySelector(".slack_blocks_to_jsx__image_title")?.textContent?.trim();
3042
+ btn.setAttribute("aria-label", title ? `Toggle ${title}` : "Toggle media");
3043
+ }
3044
+ }
3045
+ for (const a of root.querySelectorAll("a[href]")) {
3046
+ const href = a.getAttribute("href");
3047
+ if (!isSafeHref(href)) {
3048
+ a.setAttribute("href", "#");
3049
+ a.setAttribute("data-bk-blocked-href", "1");
3050
+ }
3051
+ }
3052
+ for (const img of root.querySelectorAll("img[src]")) {
3053
+ const src = img.getAttribute("src");
3054
+ if (!isSafeImageSrc(src)) {
3055
+ img.removeAttribute("src");
3056
+ img.setAttribute("data-bk-blocked-src", "1");
3057
+ }
3058
+ }
3059
+ });
3060
+ return (
3061
+ // The library scopes all of its CSS under `#slack_blocks_to_jsx` and
3062
+ // depends on the `slack_blocks_to_jsx styles_enabled` classes plus the
3063
+ // `data-theme` attribute for its button / mention / divider styling
3064
+ // (e.g. button reset + `bg-green-primary` for primary). `<Message
3065
+ // withoutWrapper>` skips that wrapper entirely, so we re-create it
3066
+ // here without the avatar / timestamp chrome.
3067
+ /* @__PURE__ */ jsx("div", { ref: rootRef, id: "slack_blocks_to_jsx", "data-theme": theme, className: "slack_blocks_to_jsx styles_enabled", children: /* @__PURE__ */ jsx(
3068
+ Message,
3069
+ {
3070
+ time: /* @__PURE__ */ new Date(),
3071
+ name: "",
3072
+ logo: "",
3073
+ withoutWrapper: true,
3074
+ theme,
3075
+ blocks: [safeBlock],
3076
+ hooks
3077
+ }
3078
+ ) })
3079
+ );
3080
+ }
3081
+ var PLACEHOLDER_TIME = "10:37 AM";
3082
+ function SlackMessageFrame({
3083
+ workspaceName,
3084
+ authorName,
3085
+ authorIcon,
3086
+ time = PLACEHOLDER_TIME,
3087
+ isDark,
3088
+ children
3089
+ }) {
3090
+ const displayName = authorName ?? workspaceName ?? "Your app";
3091
+ const initial = displayName.slice(0, 1).toUpperCase();
3092
+ const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
3093
+ return /* @__PURE__ */ jsxs(
3094
+ "div",
3095
+ {
3096
+ className: cn(
3097
+ "flex flex-col overflow-hidden rounded-md border",
3098
+ isDark ? "border-[#2c2d30] bg-[#1a1d21]" : "border-[#e8e8e8] bg-white"
3099
+ ),
3100
+ children: [
3101
+ /* @__PURE__ */ jsxs(
3102
+ "div",
3103
+ {
3104
+ className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
3105
+ children: [
3106
+ safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsx(
3107
+ "span",
3108
+ {
3109
+ className: cn(
3110
+ "inline-flex h-7 w-7 items-center justify-center rounded text-[12px] font-semibold",
3111
+ isDark ? "bg-white/10 text-white" : "bg-[#4a154b]/10 text-[#1d1c1d]"
3112
+ ),
3113
+ children: initial
3114
+ }
3115
+ ),
3116
+ /* @__PURE__ */ jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
3117
+ time ? /* @__PURE__ */ jsx("span", { children: time }) : null
3118
+ ]
3119
+ }
3120
+ ),
3121
+ children
3122
+ ]
3123
+ }
3124
+ );
3125
+ }
3126
+ function SlackMessagePreview({
3127
+ blocks,
3128
+ hooks,
3129
+ theme = "light",
3130
+ workspaceName,
3131
+ authorName,
3132
+ authorIcon,
3133
+ time,
3134
+ emptyLabel = "This message has no blocks to preview."
3135
+ }) {
3136
+ const isDark = theme === "dark";
3137
+ return /* @__PURE__ */ jsx(
3138
+ SlackMessageFrame,
3139
+ {
3140
+ workspaceName,
3141
+ authorName,
3142
+ authorIcon,
3143
+ time,
3144
+ isDark,
3145
+ children: /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 flex-col gap-1 px-5 pb-3", isDark ? "bg-[#1a1d21]" : "bg-white"), children: blocks.length === 0 ? /* @__PURE__ */ jsx("p", { className: cn("py-1 text-sm", isDark ? "text-white/60" : "text-[#616061]"), children: emptyLabel }) : blocks.map((block, index) => /* @__PURE__ */ jsx(SlackBlockPreview, { block, hooks, theme }, `${block.type}-${index}`)) })
3146
+ }
3147
+ );
3148
+ }
3027
3149
  var POLL_INTERVAL_MS = 2500;
3028
3150
  var MAX_POLLS = 24;
3029
3151
  function useSlackSignIn(loadStatus, { open, enabled }) {
@@ -3097,6 +3219,13 @@ function SlackSignInButton({ onClick, polling }) {
3097
3219
  /* @__PURE__ */ jsx(ExternalLink, {})
3098
3220
  ] }) });
3099
3221
  }
3222
+ var LINK_DEBOUNCE_MS = 400;
3223
+ var LINK_HELP_TEXT = 'Click the \u2807menu while hovering over the message in Slack (or right-click), and select "Copy Link".';
3224
+ var LINK_HELP_LABEL = "Where to find a message link";
3225
+ var TABS = [
3226
+ { id: "recent", label: "Pick from Recent" },
3227
+ { id: "link", label: "Direct Link" }
3228
+ ];
3100
3229
  function recentToResult(msg) {
3101
3230
  return {
3102
3231
  ok: true,
@@ -3121,6 +3250,16 @@ function previewText(m) {
3121
3250
  }
3122
3251
  return `${m.blocks.length} block${m.blocks.length === 1 ? "" : "s"}`;
3123
3252
  }
3253
+ function formatTs(ts) {
3254
+ const seconds = Number(ts);
3255
+ if (!ts || !Number.isFinite(seconds) || seconds <= 0) {
3256
+ return "";
3257
+ }
3258
+ return new Date(seconds * 1e3).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
3259
+ }
3260
+ function editableLabel(editableVia) {
3261
+ return editableVia === "user" ? "You" : "Bot";
3262
+ }
3124
3263
  function LoadMessageDialog({
3125
3264
  open,
3126
3265
  onOpenChange,
@@ -3128,10 +3267,14 @@ function LoadMessageDialog({
3128
3267
  loadRecentMessages,
3129
3268
  loadChannels,
3130
3269
  onLoaded,
3131
- onOpenAsNew
3270
+ onOpenAsNew,
3271
+ previewHooks,
3272
+ previewTheme = "light"
3132
3273
  }) {
3133
3274
  const [link, setLink] = useState("");
3134
- const [status, setStatus] = useState({ kind: "idle" });
3275
+ const [linkStatus, setLinkStatus] = useState({ kind: "idle" });
3276
+ const [emptyLinkError, setEmptyLinkError] = useState(null);
3277
+ const [committing, setCommitting] = useState(false);
3135
3278
  const [channels, setChannels] = useState(null);
3136
3279
  const [channelsError, setChannelsError] = useState(null);
3137
3280
  const [channelId, setChannelId] = useState("");
@@ -3139,8 +3282,14 @@ function LoadMessageDialog({
3139
3282
  const [recentError, setRecentError] = useState(null);
3140
3283
  const [selectedRecent, setSelectedRecent] = useState(null);
3141
3284
  const [signInPolling, setSignInPolling] = useState(false);
3285
+ const [tab, setTab] = useState("recent");
3142
3286
  const inputRef = useRef(null);
3143
3287
  const signInPollRef = useRef(null);
3288
+ const resolvedLinkRef = useRef(null);
3289
+ const linkRequestRef = useRef(0);
3290
+ const domId = useId();
3291
+ const tabDomId = (id) => `${domId}-${id}-tab`;
3292
+ const panelDomId = (id) => `${domId}-${id}-panel`;
3144
3293
  const onLoadMessageRef = useRef(onLoadMessage);
3145
3294
  const loadRecentMessagesRef = useRef(loadRecentMessages);
3146
3295
  const loadChannelsRef = useRef(loadChannels);
@@ -3150,16 +3299,22 @@ function LoadMessageDialog({
3150
3299
  loadChannelsRef.current = loadChannels;
3151
3300
  });
3152
3301
  const hasRecent = !!loadRecentMessages && !!loadChannels;
3302
+ const activeTab = hasRecent ? tab : "link";
3153
3303
  useEffect(() => {
3154
3304
  if (!open) {
3155
3305
  return;
3156
3306
  }
3157
3307
  setLink("");
3158
- setStatus({ kind: "idle" });
3308
+ setLinkStatus({ kind: "idle" });
3309
+ setEmptyLinkError(null);
3310
+ setCommitting(false);
3159
3311
  setChannelId("");
3160
3312
  setRecent(null);
3161
3313
  setRecentError(null);
3162
3314
  setSelectedRecent(null);
3315
+ setTab("recent");
3316
+ resolvedLinkRef.current = null;
3317
+ linkRequestRef.current += 1;
3163
3318
  }, [open]);
3164
3319
  useEffect(() => {
3165
3320
  const load = loadChannelsRef.current;
@@ -3218,27 +3373,71 @@ function LoadMessageDialog({
3218
3373
  }
3219
3374
  return stopSignInPoll;
3220
3375
  }, [open, stopSignInPoll]);
3376
+ const resolveLink = useCallback(async (trimmed) => {
3377
+ const requestId = ++linkRequestRef.current;
3378
+ setLinkStatus({ kind: "loading" });
3379
+ try {
3380
+ const result = await onLoadMessageRef.current({ link: trimmed });
3381
+ if (linkRequestRef.current !== requestId) {
3382
+ return null;
3383
+ }
3384
+ resolvedLinkRef.current = trimmed;
3385
+ setLinkStatus(
3386
+ result.ok ? { kind: "ready", result } : { kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl }
3387
+ );
3388
+ return result;
3389
+ } catch (e) {
3390
+ if (linkRequestRef.current !== requestId) {
3391
+ return null;
3392
+ }
3393
+ resolvedLinkRef.current = trimmed;
3394
+ setLinkStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
3395
+ return null;
3396
+ }
3397
+ }, []);
3398
+ useEffect(() => {
3399
+ if (!open) {
3400
+ return;
3401
+ }
3402
+ const trimmed = link.trim();
3403
+ if (!trimmed) {
3404
+ linkRequestRef.current += 1;
3405
+ resolvedLinkRef.current = null;
3406
+ setLinkStatus({ kind: "idle" });
3407
+ return;
3408
+ }
3409
+ if (resolvedLinkRef.current === trimmed) {
3410
+ return;
3411
+ }
3412
+ const timer = setTimeout(() => {
3413
+ if (resolvedLinkRef.current !== trimmed) {
3414
+ void resolveLink(trimmed);
3415
+ }
3416
+ }, LINK_DEBOUNCE_MS);
3417
+ return () => clearTimeout(timer);
3418
+ }, [open, link, resolveLink]);
3221
3419
  const handleLoad = async () => {
3222
3420
  stopSignInPoll();
3223
- if (selectedRecent) {
3224
- onLoaded(recentToResult(selectedRecent));
3421
+ if (activeTab === "recent") {
3422
+ if (selectedRecent) {
3423
+ onLoaded(recentToResult(selectedRecent));
3424
+ }
3225
3425
  return;
3226
3426
  }
3227
3427
  const trimmed = link.trim();
3228
3428
  if (!trimmed) {
3229
- setStatus({ kind: "error", error: "Paste a Slack message link first." });
3429
+ setEmptyLinkError("Paste a Slack message link first.");
3230
3430
  return;
3231
3431
  }
3232
- setStatus({ kind: "loading" });
3233
- try {
3234
- const result = await onLoadMessageRef.current({ link: trimmed });
3235
- if (result.ok) {
3236
- onLoaded(result);
3237
- return;
3238
- }
3239
- setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl });
3240
- } catch (e) {
3241
- setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
3432
+ if (linkStatus.kind === "ready" && resolvedLinkRef.current === trimmed) {
3433
+ onLoaded(linkStatus.result);
3434
+ return;
3435
+ }
3436
+ setCommitting(true);
3437
+ const result = await resolveLink(trimmed);
3438
+ setCommitting(false);
3439
+ if (result?.ok) {
3440
+ onLoaded(result);
3242
3441
  }
3243
3442
  };
3244
3443
  const startFindSignIn = (oauthUrl) => {
@@ -3270,191 +3469,301 @@ function LoadMessageDialog({
3270
3469
  });
3271
3470
  }, POLL_INTERVAL_MS2);
3272
3471
  };
3273
- const notEditable = status.kind === "not-editable" ? status : null;
3274
- const signInUrl = notEditable?.oauthUrl && isSafeHref(notEditable.oauthUrl) ? notEditable.oauthUrl : null;
3472
+ const preview = activeTab === "recent" ? selectedRecent ? { kind: "ready", target: { ...selectedRecent, editableVia: selectedRecent.editableVia ?? "bot" } } : { kind: "empty" } : linkStatus.kind === "idle" ? { kind: "empty" } : linkStatus.kind === "ready" ? { kind: "ready", target: linkStatus.result } : linkStatus;
3473
+ const canLoad = activeTab === "recent" ? !!selectedRecent : !!link.trim();
3474
+ const panelProps = (id) => hasRecent ? { role: "tabpanel", id: panelDomId(id), "aria-labelledby": tabDomId(id) } : {};
3275
3475
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
3276
3476
  DialogContent,
3277
3477
  {
3278
- className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg",
3478
+ className: "flex max-h-[85svh] w-[calc(100vw-1.5rem)] max-w-4xl flex-col rounded-lg max-lg:max-w-lg",
3279
3479
  onOpenAutoFocus: (e) => {
3480
+ if (hasRecent) {
3481
+ return;
3482
+ }
3280
3483
  e.preventDefault();
3281
3484
  inputRef.current?.focus();
3282
3485
  },
3283
3486
  children: [
3284
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
3487
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "shrink-0", children: [
3285
3488
  /* @__PURE__ */ jsx(DialogTitle, { children: "Find an existing message" }),
3286
- /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5 text-left", children: [
3287
- /* @__PURE__ */ jsx(DialogDescription, { children: hasRecent ? "Paste a Slack message link, or pick a recent message your app posted." : `Paste a Slack message link (Slack's "Copy link") to load its blocks for editing.` }),
3288
- /* @__PURE__ */ jsxs(Tooltip, { children: [
3289
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3290
- "button",
3291
- {
3292
- type: "button",
3293
- "aria-label": "How to copy a message link",
3294
- 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",
3295
- children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
3296
- }
3297
- ) }),
3298
- /* @__PURE__ */ jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
3299
- "In Slack, hover over the message and click the ",
3300
- /* @__PURE__ */ jsx("strong", { children: "\u22EE More actions" }),
3301
- " button (or right-click the message), then choose ",
3302
- /* @__PURE__ */ jsx("strong", { children: "Copy link" }),
3303
- "."
3304
- ] })
3305
- ] })
3306
- ] })
3489
+ /* @__PURE__ */ jsx(DialogDescription, { children: hasRecent ? "Pick a recent message your app posted, or paste a Slack message link. Preview it before loading." : `Paste a Slack message link (Slack's "Copy link") to preview it and load its blocks for editing.` })
3307
3490
  ] }),
3308
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [
3309
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3310
- /* @__PURE__ */ jsx(Label, { htmlFor: "message-link", children: "Message link" }),
3311
- /* @__PURE__ */ jsx(
3312
- Input,
3491
+ /* @__PURE__ */ jsxs("div", { className: "-mx-1 flex min-h-0 min-w-0 flex-1 px-1 max-lg:flex-col max-lg:gap-4 max-lg:overflow-y-auto lg:flex-row lg:gap-5 lg:overflow-hidden", children: [
3492
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-3 lg:min-h-0 lg:w-[19rem]", children: [
3493
+ hasRecent && /* @__PURE__ */ jsx(TabStrip, { activeTab, onSelect: setTab, tabDomId, panelDomId }),
3494
+ activeTab === "recent" ? /* @__PURE__ */ jsxs(
3495
+ "div",
3313
3496
  {
3314
- ref: inputRef,
3315
- id: "message-link",
3316
- value: link,
3317
- onChange: (e) => {
3318
- setLink(e.target.value);
3319
- setSelectedRecent(null);
3320
- stopSignInPoll();
3321
- if (status.kind !== "idle" && status.kind !== "loading") {
3322
- setStatus({ kind: "idle" });
3323
- }
3324
- },
3325
- onKeyDown: (e) => {
3326
- if (e.key === "Enter" && status.kind !== "loading") {
3327
- e.preventDefault();
3328
- handleLoad();
3329
- }
3330
- },
3331
- placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
3332
- autoComplete: "off",
3333
- spellCheck: false
3497
+ ...panelProps("recent"),
3498
+ className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-hidden",
3499
+ children: [
3500
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col gap-1.5", children: [
3501
+ /* @__PURE__ */ jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
3502
+ channels === null && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
3503
+ channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: channelsError }),
3504
+ channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
3505
+ channels && channels.length > 0 && /* @__PURE__ */ jsxs(
3506
+ "select",
3507
+ {
3508
+ id: "recent-channel-picker",
3509
+ value: channelId,
3510
+ onChange: (e) => setChannelId(e.target.value),
3511
+ 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",
3512
+ children: [
3513
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
3514
+ channels.map((c) => /* @__PURE__ */ jsxs("option", { value: c.id, children: [
3515
+ "#",
3516
+ c.name
3517
+ ] }, c.id))
3518
+ ]
3519
+ }
3520
+ )
3521
+ ] }),
3522
+ !channelId && channels && channels.length > 0 && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
3523
+ channelId && recent === null && !recentError && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3524
+ channelId && recentError && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-destructive", children: recentError }),
3525
+ channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
3526
+ channelId && recent && recent.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex min-h-0 min-w-0 flex-col gap-1 overflow-y-auto max-lg:max-h-60 lg:flex-1", children: recent.map((m) => {
3527
+ const asUser = (m.editableVia ?? "bot") === "user";
3528
+ const authorName = m.username || (asUser ? "You" : "App bot");
3529
+ const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
3530
+ const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
3531
+ return /* @__PURE__ */ jsxs(
3532
+ "button",
3533
+ {
3534
+ type: "button",
3535
+ "aria-pressed": isSelected,
3536
+ onClick: () => {
3537
+ setSelectedRecent(m);
3538
+ },
3539
+ className: cn(
3540
+ // Inset rings so the scroll container's overflow clip
3541
+ // can't shave the corners off the selected outline.
3542
+ "flex min-w-0 items-start gap-2.5 rounded-md border border-input bg-background px-3 py-2 text-left hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
3543
+ isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
3544
+ ),
3545
+ children: [
3546
+ safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsx(
3547
+ "span",
3548
+ {
3549
+ "aria-hidden": true,
3550
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
3551
+ children: authorName.charAt(0).toUpperCase()
3552
+ }
3553
+ ),
3554
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3555
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
3556
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
3557
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: asUser ? "You" : "Bot" })
3558
+ ] }),
3559
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3560
+ new Date(Number(m.ts) * 1e3).toLocaleString(),
3561
+ " ",
3562
+ /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3563
+ "(",
3564
+ m.ts,
3565
+ ")"
3566
+ ] })
3567
+ ] }),
3568
+ /* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
3569
+ ] })
3570
+ ]
3571
+ },
3572
+ `${m.channelId}:${m.ts}`
3573
+ );
3574
+ }) })
3575
+ ]
3576
+ }
3577
+ ) : /* @__PURE__ */ jsxs(
3578
+ "div",
3579
+ {
3580
+ ...panelProps("link"),
3581
+ className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-y-auto",
3582
+ children: [
3583
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3584
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3585
+ /* @__PURE__ */ jsx(Label, { htmlFor: "message-link", children: "Message link" }),
3586
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3587
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3588
+ "button",
3589
+ {
3590
+ type: "button",
3591
+ "aria-label": LINK_HELP_LABEL,
3592
+ className: "flex h-4 w-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
3593
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
3594
+ }
3595
+ ) }),
3596
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: LINK_HELP_TEXT })
3597
+ ] })
3598
+ ] }),
3599
+ /* @__PURE__ */ jsx(
3600
+ Input,
3601
+ {
3602
+ ref: inputRef,
3603
+ id: "message-link",
3604
+ value: link,
3605
+ onChange: (e) => {
3606
+ setLink(e.target.value);
3607
+ setEmptyLinkError(null);
3608
+ stopSignInPoll();
3609
+ },
3610
+ onKeyDown: (e) => {
3611
+ if (e.key === "Enter" && !committing) {
3612
+ e.preventDefault();
3613
+ handleLoad();
3614
+ }
3615
+ },
3616
+ placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
3617
+ autoComplete: "off",
3618
+ spellCheck: false
3619
+ }
3620
+ )
3621
+ ] }),
3622
+ emptyLinkError && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: emptyLinkError })
3623
+ ]
3334
3624
  }
3335
3625
  )
3336
3626
  ] }),
3337
- status.kind === "error" && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: status.error }),
3338
- notEditable && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border border-amber-200! bg-amber-50 p-3 text-xs text-amber-800", children: [
3627
+ /* @__PURE__ */ jsx(
3628
+ PreviewPane,
3629
+ {
3630
+ state: preview,
3631
+ hooks: previewHooks,
3632
+ theme: previewTheme,
3633
+ emptyHint: activeTab === "recent" ? "Select a recent message to preview it here." : "Paste a message link to preview it here.",
3634
+ signInPolling,
3635
+ onSignIn: startFindSignIn,
3636
+ onOpenAsNew
3637
+ }
3638
+ )
3639
+ ] }),
3640
+ /* @__PURE__ */ jsxs(DialogFooter, { className: "shrink-0", children: [
3641
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3642
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleLoad, disabled: committing || !canLoad, children: committing ? "Loading\u2026" : "Load message" })
3643
+ ] })
3644
+ ]
3645
+ }
3646
+ ) });
3647
+ }
3648
+ function TabStrip({
3649
+ activeTab,
3650
+ onSelect,
3651
+ tabDomId,
3652
+ panelDomId
3653
+ }) {
3654
+ const tabRefs = useRef({});
3655
+ const moveTo = (next) => {
3656
+ onSelect(next);
3657
+ tabRefs.current[next]?.focus();
3658
+ };
3659
+ return /* @__PURE__ */ jsx("div", { role: "tablist", "aria-label": "Message source", className: "flex shrink-0 gap-1 rounded-md bg-muted p-1", children: TABS.map((entry, index) => {
3660
+ const selected = entry.id === activeTab;
3661
+ return /* @__PURE__ */ jsx(
3662
+ "button",
3663
+ {
3664
+ ref: (node) => {
3665
+ tabRefs.current[entry.id] = node;
3666
+ },
3667
+ type: "button",
3668
+ role: "tab",
3669
+ id: tabDomId(entry.id),
3670
+ "aria-selected": selected,
3671
+ "aria-controls": panelDomId(entry.id),
3672
+ tabIndex: selected ? 0 : -1,
3673
+ onClick: () => onSelect(entry.id),
3674
+ onKeyDown: (e) => {
3675
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
3676
+ e.preventDefault();
3677
+ const delta = e.key === "ArrowRight" ? 1 : -1;
3678
+ moveTo(TABS[(index + delta + TABS.length) % TABS.length].id);
3679
+ } else if (e.key === "Home") {
3680
+ e.preventDefault();
3681
+ moveTo(TABS[0].id);
3682
+ } else if (e.key === "End") {
3683
+ e.preventDefault();
3684
+ moveTo(TABS[TABS.length - 1].id);
3685
+ }
3686
+ },
3687
+ className: cn(
3688
+ "flex-1 rounded-sm px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
3689
+ selected ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
3690
+ ),
3691
+ children: entry.label
3692
+ },
3693
+ entry.id
3694
+ );
3695
+ }) });
3696
+ }
3697
+ function PreviewPane({
3698
+ state,
3699
+ hooks,
3700
+ theme,
3701
+ emptyHint,
3702
+ signInPolling,
3703
+ onSignIn,
3704
+ onOpenAsNew
3705
+ }) {
3706
+ const target = state.kind === "ready" ? state.target : null;
3707
+ const signInUrl = state.kind === "not-editable" && state.oauthUrl && isSafeHref(state.oauthUrl) ? state.oauthUrl : null;
3708
+ const verdictBlocks = state.kind === "not-editable" ? state.blocks : void 0;
3709
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-2 lg:min-h-0 lg:flex-1", children: [
3710
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: [
3711
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Preview" }),
3712
+ target?.channelName || target?.channelId ? /* @__PURE__ */ jsx("span", { className: "rounded border px-1.5 py-px text-[10px] font-medium text-muted-foreground", children: target.channelName ? `#${target.channelName}` : target.channelId }) : null,
3713
+ target?.editableVia ? /* @__PURE__ */ jsxs("span", { className: "rounded border px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: [
3714
+ "Editable as ",
3715
+ editableLabel(target.editableVia)
3716
+ ] }) : null
3717
+ ] }),
3718
+ /* @__PURE__ */ jsxs(
3719
+ "div",
3720
+ {
3721
+ role: "region",
3722
+ "aria-label": "Message preview",
3723
+ tabIndex: 0,
3724
+ className: "flex flex-1 flex-col gap-3 overflow-y-auto rounded-md border bg-muted/40 p-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring max-lg:max-h-72 max-lg:min-h-32 lg:min-h-0",
3725
+ children: [
3726
+ state.kind === "not-editable" && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col gap-2 rounded-md border border-amber-200! bg-amber-50 p-3 text-xs text-amber-800", children: [
3339
3727
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
3340
3728
  /* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
3341
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: notEditable.reason })
3729
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: state.reason })
3342
3730
  ] }),
3343
- signInUrl && /* @__PURE__ */ jsx(SlackSignInButton, { onClick: () => startFindSignIn(signInUrl), polling: signInPolling }),
3344
- notEditable.blocks && notEditable.blocks.length > 0 && /* @__PURE__ */ jsx(
3731
+ signInUrl && /* @__PURE__ */ jsx(SlackSignInButton, { onClick: () => onSignIn(signInUrl), polling: signInPolling }),
3732
+ verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsx(
3345
3733
  Button,
3346
3734
  {
3347
3735
  type: "button",
3348
3736
  variant: "outline",
3349
3737
  size: "sm",
3350
3738
  className: "self-start",
3351
- onClick: () => onOpenAsNew(notEditable.blocks),
3739
+ onClick: () => onOpenAsNew(verdictBlocks),
3352
3740
  children: "Open as a new message instead"
3353
3741
  }
3354
3742
  )
3355
3743
  ] }),
3356
- hasRecent && /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
3357
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
3358
- /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" }),
3359
- "or pick a recent message",
3360
- /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" })
3361
- ] }),
3362
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3363
- /* @__PURE__ */ jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
3364
- channels === null && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
3365
- channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: channelsError }),
3366
- channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
3367
- channels && channels.length > 0 && /* @__PURE__ */ jsxs(
3368
- "select",
3369
- {
3370
- id: "recent-channel-picker",
3371
- value: channelId,
3372
- onChange: (e) => setChannelId(e.target.value),
3373
- 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",
3374
- children: [
3375
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
3376
- channels.map((c) => /* @__PURE__ */ jsxs("option", { value: c.id, children: [
3377
- "#",
3378
- c.name
3379
- ] }, c.id))
3380
- ]
3381
- }
3382
- )
3383
- ] }),
3384
- !channelId && channels && channels.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
3385
- channelId && recent === null && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3386
- channelId && recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: recentError }),
3387
- channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
3388
- channelId && recent && recent.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex max-h-48 min-w-0 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
3389
- const asUser = (m.editableVia ?? "bot") === "user";
3390
- const authorName = m.username || (asUser ? "You" : "App bot");
3391
- const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
3392
- const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
3393
- return /* @__PURE__ */ jsxs(
3394
- "button",
3395
- {
3396
- type: "button",
3397
- "aria-pressed": isSelected,
3398
- onClick: () => {
3399
- setSelectedRecent(m);
3400
- setLink("");
3401
- if (status.kind !== "idle" && status.kind !== "loading") {
3402
- setStatus({ kind: "idle" });
3403
- }
3404
- },
3405
- className: cn(
3406
- // Inset rings so the scroll container's overflow clip
3407
- // can't shave the corners off the selected outline.
3408
- "flex min-w-0 items-start gap-2.5 rounded-md border border-input bg-background px-3 py-2 text-left hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
3409
- isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
3410
- ),
3411
- children: [
3412
- safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsx(
3413
- "span",
3414
- {
3415
- "aria-hidden": true,
3416
- className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
3417
- children: authorName.charAt(0).toUpperCase()
3418
- }
3419
- ),
3420
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3421
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
3422
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
3423
- /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: asUser ? "You" : "Bot" })
3424
- ] }),
3425
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3426
- new Date(Number(m.ts) * 1e3).toLocaleString(),
3427
- " ",
3428
- /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3429
- "(",
3430
- m.ts,
3431
- ")"
3432
- ] })
3433
- ] }),
3434
- /* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
3435
- ] })
3436
- ]
3437
- },
3438
- `${m.channelId}:${m.ts}`
3439
- );
3440
- }) })
3441
- ] })
3442
- ] }),
3443
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
3444
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3445
- /* @__PURE__ */ jsx(
3446
- Button,
3744
+ state.kind === "error" && /* @__PURE__ */ jsx("p", { className: "shrink-0 rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: state.error }),
3745
+ state.kind === "loading" && /* @__PURE__ */ jsxs("p", { className: "m-auto flex items-center gap-2 text-sm text-muted-foreground", children: [
3746
+ /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
3747
+ "Looking up that message\u2026"
3748
+ ] }),
3749
+ state.kind === "empty" && /* @__PURE__ */ jsx("p", { className: "m-auto px-4 text-center text-sm text-muted-foreground", children: emptyHint }),
3750
+ target && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsx(
3751
+ SlackMessagePreview,
3447
3752
  {
3448
- type: "button",
3449
- onClick: handleLoad,
3450
- disabled: status.kind === "loading" || !selectedRecent && !link.trim(),
3451
- children: status.kind === "loading" ? "Loading\u2026" : "Load message"
3753
+ blocks: target.blocks,
3754
+ hooks,
3755
+ theme,
3756
+ workspaceName: target.workspaceName,
3757
+ authorName: target.username,
3758
+ authorIcon: target.iconUrl,
3759
+ time: formatTs(target.ts)
3452
3760
  }
3453
- )
3454
- ] })
3455
- ]
3456
- }
3457
- ) });
3761
+ ) }),
3762
+ verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsx(SlackMessagePreview, { blocks: verdictBlocks, hooks, theme, time: "" }) })
3763
+ ]
3764
+ }
3765
+ )
3766
+ ] });
3458
3767
  }
3459
3768
  var PALETTE_DRAG_PREFIX = "palette:";
3460
3769
  function parsePaletteDragId(id) {
@@ -8677,60 +8986,6 @@ function dispatch(block, onChange) {
8677
8986
  return null;
8678
8987
  }
8679
8988
  }
8680
- function SlackBlockPreview({
8681
- block,
8682
- hooks,
8683
- theme = "light"
8684
- }) {
8685
- const rootRef = useRef(null);
8686
- const safeBlock = useMemo(() => sanitizeBlock(block), [block]);
8687
- useEffect(() => {
8688
- const root = rootRef.current;
8689
- if (!root) return;
8690
- for (const wrapper of root.querySelectorAll(
8691
- ".slack_blocks_to_jsx__image_media_trigger, .slack_blocks_to_jsx__video_title"
8692
- )) {
8693
- for (const btn of wrapper.querySelectorAll("button:not([aria-label])")) {
8694
- const title = wrapper.querySelector(".slack_blocks_to_jsx__image_title")?.textContent?.trim();
8695
- btn.setAttribute("aria-label", title ? `Toggle ${title}` : "Toggle media");
8696
- }
8697
- }
8698
- for (const a of root.querySelectorAll("a[href]")) {
8699
- const href = a.getAttribute("href");
8700
- if (!isSafeHref(href)) {
8701
- a.setAttribute("href", "#");
8702
- a.setAttribute("data-bk-blocked-href", "1");
8703
- }
8704
- }
8705
- for (const img of root.querySelectorAll("img[src]")) {
8706
- const src = img.getAttribute("src");
8707
- if (!isSafeImageSrc(src)) {
8708
- img.removeAttribute("src");
8709
- img.setAttribute("data-bk-blocked-src", "1");
8710
- }
8711
- }
8712
- });
8713
- return (
8714
- // The library scopes all of its CSS under `#slack_blocks_to_jsx` and
8715
- // depends on the `slack_blocks_to_jsx styles_enabled` classes plus the
8716
- // `data-theme` attribute for its button / mention / divider styling
8717
- // (e.g. button reset + `bg-green-primary` for primary). `<Message
8718
- // withoutWrapper>` skips that wrapper entirely, so we re-create it
8719
- // here without the avatar / timestamp chrome.
8720
- /* @__PURE__ */ jsx("div", { ref: rootRef, id: "slack_blocks_to_jsx", "data-theme": theme, className: "slack_blocks_to_jsx styles_enabled", children: /* @__PURE__ */ jsx(
8721
- Message,
8722
- {
8723
- time: /* @__PURE__ */ new Date(),
8724
- name: "",
8725
- logo: "",
8726
- withoutWrapper: true,
8727
- theme,
8728
- blocks: [safeBlock],
8729
- hooks
8730
- }
8731
- ) })
8732
- );
8733
- }
8734
8989
  var BLOCK_TYPE_LABELS = {
8735
8990
  section: "Section",
8736
8991
  header: "Header",
@@ -9353,50 +9608,16 @@ function Surface({
9353
9608
  {
9354
9609
  "aria-label": "Block preview",
9355
9610
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
9356
- children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-2xl", children: previewSurface === "modal" ? /* @__PURE__ */ jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsx(MessageFrame, { workspaceName, authorName, authorIcon, isDark, children: blocksList }) })
9357
- }
9358
- );
9359
- }
9360
- function MessageFrame({
9361
- workspaceName,
9362
- authorName,
9363
- authorIcon,
9364
- isDark,
9365
- children
9366
- }) {
9367
- const displayName = authorName ?? workspaceName ?? "Your app";
9368
- const initial = displayName.slice(0, 1).toUpperCase();
9369
- const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
9370
- return /* @__PURE__ */ jsxs(
9371
- "div",
9372
- {
9373
- className: cn(
9374
- "flex flex-col overflow-hidden rounded-md border",
9375
- isDark ? "border-[#2c2d30] bg-[#1a1d21]" : "border-[#e8e8e8] bg-white"
9376
- ),
9377
- children: [
9378
- /* @__PURE__ */ jsxs(
9379
- "div",
9380
- {
9381
- className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
9382
- children: [
9383
- safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsx(
9384
- "span",
9385
- {
9386
- className: cn(
9387
- "inline-flex h-7 w-7 items-center justify-center rounded text-[12px] font-semibold",
9388
- isDark ? "bg-white/10 text-white" : "bg-[#4a154b]/10 text-[#1d1c1d]"
9389
- ),
9390
- children: initial
9391
- }
9392
- ),
9393
- /* @__PURE__ */ jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
9394
- /* @__PURE__ */ jsx("span", { children: "10:37 AM" })
9395
- ]
9396
- }
9397
- ),
9398
- children
9399
- ]
9611
+ children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-2xl", children: previewSurface === "modal" ? /* @__PURE__ */ jsx(ModalFrame, { isDark, children: blocksList }) : previewSurface === "app_home" ? /* @__PURE__ */ jsx(AppHomeFrame, { workspaceName, isDark, children: blocksList }) : /* @__PURE__ */ jsx(
9612
+ SlackMessageFrame,
9613
+ {
9614
+ workspaceName,
9615
+ authorName,
9616
+ authorIcon,
9617
+ isDark,
9618
+ children: blocksList
9619
+ }
9620
+ ) })
9400
9621
  }
9401
9622
  );
9402
9623
  }
@@ -10485,6 +10706,8 @@ function BlockKitchen(props) {
10485
10706
  onLoadMessage: loading.onLoadMessage,
10486
10707
  loadRecentMessages: loading.loadRecentMessages,
10487
10708
  loadChannels: recentChannelSource,
10709
+ previewHooks: mergedPreviewHooks,
10710
+ previewTheme,
10488
10711
  onLoaded: (result) => {
10489
10712
  resetAll(result.blocks);
10490
10713
  setEditTarget(toLoadedMessage(result));