@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.cjs CHANGED
@@ -14,14 +14,14 @@ var nanoid = require('nanoid');
14
14
  var reactSlot = require('@radix-ui/react-slot');
15
15
  var classVarianceAuthority = require('class-variance-authority');
16
16
  var LabelPrimitive = require('@radix-ui/react-label');
17
+ require('slack-blocks-to-jsx/dist/style.css');
18
+ var slackBlocksToJsx = require('slack-blocks-to-jsx');
17
19
  var utilities = require('@dnd-kit/utilities');
18
20
  var PopoverPrimitive = require('@radix-ui/react-popover');
19
21
  var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
20
22
  var Link = require('@tiptap/extension-link');
21
23
  var react = require('@tiptap/react');
22
24
  var StarterKit = require('@tiptap/starter-kit');
23
- require('slack-blocks-to-jsx/dist/style.css');
24
- var slackBlocksToJsx = require('slack-blocks-to-jsx');
25
25
 
26
26
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
27
27
 
@@ -3054,6 +3054,128 @@ var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__
3054
3054
  }
3055
3055
  ));
3056
3056
  Label.displayName = LabelPrimitive__namespace.Root.displayName;
3057
+ function SlackBlockPreview({
3058
+ block,
3059
+ hooks,
3060
+ theme = "light"
3061
+ }) {
3062
+ const rootRef = React.useRef(null);
3063
+ const safeBlock = React.useMemo(() => sanitizeBlock(block), [block]);
3064
+ React.useEffect(() => {
3065
+ const root = rootRef.current;
3066
+ if (!root) return;
3067
+ for (const wrapper of root.querySelectorAll(
3068
+ ".slack_blocks_to_jsx__image_media_trigger, .slack_blocks_to_jsx__video_title"
3069
+ )) {
3070
+ for (const btn of wrapper.querySelectorAll("button:not([aria-label])")) {
3071
+ const title = wrapper.querySelector(".slack_blocks_to_jsx__image_title")?.textContent?.trim();
3072
+ btn.setAttribute("aria-label", title ? `Toggle ${title}` : "Toggle media");
3073
+ }
3074
+ }
3075
+ for (const a of root.querySelectorAll("a[href]")) {
3076
+ const href = a.getAttribute("href");
3077
+ if (!isSafeHref(href)) {
3078
+ a.setAttribute("href", "#");
3079
+ a.setAttribute("data-bk-blocked-href", "1");
3080
+ }
3081
+ }
3082
+ for (const img of root.querySelectorAll("img[src]")) {
3083
+ const src = img.getAttribute("src");
3084
+ if (!isSafeImageSrc(src)) {
3085
+ img.removeAttribute("src");
3086
+ img.setAttribute("data-bk-blocked-src", "1");
3087
+ }
3088
+ }
3089
+ });
3090
+ return (
3091
+ // The library scopes all of its CSS under `#slack_blocks_to_jsx` and
3092
+ // depends on the `slack_blocks_to_jsx styles_enabled` classes plus the
3093
+ // `data-theme` attribute for its button / mention / divider styling
3094
+ // (e.g. button reset + `bg-green-primary` for primary). `<Message
3095
+ // withoutWrapper>` skips that wrapper entirely, so we re-create it
3096
+ // here without the avatar / timestamp chrome.
3097
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: rootRef, id: "slack_blocks_to_jsx", "data-theme": theme, className: "slack_blocks_to_jsx styles_enabled", children: /* @__PURE__ */ jsxRuntime.jsx(
3098
+ slackBlocksToJsx.Message,
3099
+ {
3100
+ time: /* @__PURE__ */ new Date(),
3101
+ name: "",
3102
+ logo: "",
3103
+ withoutWrapper: true,
3104
+ theme,
3105
+ blocks: [safeBlock],
3106
+ hooks
3107
+ }
3108
+ ) })
3109
+ );
3110
+ }
3111
+ var PLACEHOLDER_TIME = "10:37 AM";
3112
+ function SlackMessageFrame({
3113
+ workspaceName,
3114
+ authorName,
3115
+ authorIcon,
3116
+ time = PLACEHOLDER_TIME,
3117
+ isDark,
3118
+ children
3119
+ }) {
3120
+ const displayName = authorName ?? workspaceName ?? "Your app";
3121
+ const initial = displayName.slice(0, 1).toUpperCase();
3122
+ const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
3123
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3124
+ "div",
3125
+ {
3126
+ className: cn(
3127
+ "flex flex-col overflow-hidden rounded-md border",
3128
+ isDark ? "border-[#2c2d30] bg-[#1a1d21]" : "border-[#e8e8e8] bg-white"
3129
+ ),
3130
+ children: [
3131
+ /* @__PURE__ */ jsxRuntime.jsxs(
3132
+ "div",
3133
+ {
3134
+ className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
3135
+ children: [
3136
+ safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
3137
+ "span",
3138
+ {
3139
+ className: cn(
3140
+ "inline-flex h-7 w-7 items-center justify-center rounded text-[12px] font-semibold",
3141
+ isDark ? "bg-white/10 text-white" : "bg-[#4a154b]/10 text-[#1d1c1d]"
3142
+ ),
3143
+ children: initial
3144
+ }
3145
+ ),
3146
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
3147
+ time ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: time }) : null
3148
+ ]
3149
+ }
3150
+ ),
3151
+ children
3152
+ ]
3153
+ }
3154
+ );
3155
+ }
3156
+ function SlackMessagePreview({
3157
+ blocks,
3158
+ hooks,
3159
+ theme = "light",
3160
+ workspaceName,
3161
+ authorName,
3162
+ authorIcon,
3163
+ time,
3164
+ emptyLabel = "This message has no blocks to preview."
3165
+ }) {
3166
+ const isDark = theme === "dark";
3167
+ return /* @__PURE__ */ jsxRuntime.jsx(
3168
+ SlackMessageFrame,
3169
+ {
3170
+ workspaceName,
3171
+ authorName,
3172
+ authorIcon,
3173
+ time,
3174
+ isDark,
3175
+ children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("p", { className: cn("py-1 text-sm", isDark ? "text-white/60" : "text-[#616061]"), children: emptyLabel }) : blocks.map((block, index) => /* @__PURE__ */ jsxRuntime.jsx(SlackBlockPreview, { block, hooks, theme }, `${block.type}-${index}`)) })
3176
+ }
3177
+ );
3178
+ }
3057
3179
  var POLL_INTERVAL_MS = 2500;
3058
3180
  var MAX_POLLS = 24;
3059
3181
  function useSlackSignIn(loadStatus, { open, enabled }) {
@@ -3127,6 +3249,13 @@ function SlackSignInButton({ onClick, polling }) {
3127
3249
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, {})
3128
3250
  ] }) });
3129
3251
  }
3252
+ var LINK_DEBOUNCE_MS = 400;
3253
+ var LINK_HELP_TEXT = 'Click the \u2807menu while hovering over the message in Slack (or right-click), and select "Copy Link".';
3254
+ var LINK_HELP_LABEL = "Where to find a message link";
3255
+ var TABS = [
3256
+ { id: "recent", label: "Pick from Recent" },
3257
+ { id: "link", label: "Direct Link" }
3258
+ ];
3130
3259
  function recentToResult(msg) {
3131
3260
  return {
3132
3261
  ok: true,
@@ -3151,6 +3280,16 @@ function previewText(m) {
3151
3280
  }
3152
3281
  return `${m.blocks.length} block${m.blocks.length === 1 ? "" : "s"}`;
3153
3282
  }
3283
+ function formatTs(ts) {
3284
+ const seconds = Number(ts);
3285
+ if (!ts || !Number.isFinite(seconds) || seconds <= 0) {
3286
+ return "";
3287
+ }
3288
+ return new Date(seconds * 1e3).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
3289
+ }
3290
+ function editableLabel(editableVia) {
3291
+ return editableVia === "user" ? "You" : "Bot";
3292
+ }
3154
3293
  function LoadMessageDialog({
3155
3294
  open,
3156
3295
  onOpenChange,
@@ -3158,10 +3297,14 @@ function LoadMessageDialog({
3158
3297
  loadRecentMessages,
3159
3298
  loadChannels,
3160
3299
  onLoaded,
3161
- onOpenAsNew
3300
+ onOpenAsNew,
3301
+ previewHooks,
3302
+ previewTheme = "light"
3162
3303
  }) {
3163
3304
  const [link, setLink] = React.useState("");
3164
- const [status, setStatus] = React.useState({ kind: "idle" });
3305
+ const [linkStatus, setLinkStatus] = React.useState({ kind: "idle" });
3306
+ const [emptyLinkError, setEmptyLinkError] = React.useState(null);
3307
+ const [committing, setCommitting] = React.useState(false);
3165
3308
  const [channels, setChannels] = React.useState(null);
3166
3309
  const [channelsError, setChannelsError] = React.useState(null);
3167
3310
  const [channelId, setChannelId] = React.useState("");
@@ -3169,8 +3312,14 @@ function LoadMessageDialog({
3169
3312
  const [recentError, setRecentError] = React.useState(null);
3170
3313
  const [selectedRecent, setSelectedRecent] = React.useState(null);
3171
3314
  const [signInPolling, setSignInPolling] = React.useState(false);
3315
+ const [tab, setTab] = React.useState("recent");
3172
3316
  const inputRef = React.useRef(null);
3173
3317
  const signInPollRef = React.useRef(null);
3318
+ const resolvedLinkRef = React.useRef(null);
3319
+ const linkRequestRef = React.useRef(0);
3320
+ const domId = React.useId();
3321
+ const tabDomId = (id) => `${domId}-${id}-tab`;
3322
+ const panelDomId = (id) => `${domId}-${id}-panel`;
3174
3323
  const onLoadMessageRef = React.useRef(onLoadMessage);
3175
3324
  const loadRecentMessagesRef = React.useRef(loadRecentMessages);
3176
3325
  const loadChannelsRef = React.useRef(loadChannels);
@@ -3180,16 +3329,22 @@ function LoadMessageDialog({
3180
3329
  loadChannelsRef.current = loadChannels;
3181
3330
  });
3182
3331
  const hasRecent = !!loadRecentMessages && !!loadChannels;
3332
+ const activeTab = hasRecent ? tab : "link";
3183
3333
  React.useEffect(() => {
3184
3334
  if (!open) {
3185
3335
  return;
3186
3336
  }
3187
3337
  setLink("");
3188
- setStatus({ kind: "idle" });
3338
+ setLinkStatus({ kind: "idle" });
3339
+ setEmptyLinkError(null);
3340
+ setCommitting(false);
3189
3341
  setChannelId("");
3190
3342
  setRecent(null);
3191
3343
  setRecentError(null);
3192
3344
  setSelectedRecent(null);
3345
+ setTab("recent");
3346
+ resolvedLinkRef.current = null;
3347
+ linkRequestRef.current += 1;
3193
3348
  }, [open]);
3194
3349
  React.useEffect(() => {
3195
3350
  const load = loadChannelsRef.current;
@@ -3248,27 +3403,71 @@ function LoadMessageDialog({
3248
3403
  }
3249
3404
  return stopSignInPoll;
3250
3405
  }, [open, stopSignInPoll]);
3406
+ const resolveLink = React.useCallback(async (trimmed) => {
3407
+ const requestId = ++linkRequestRef.current;
3408
+ setLinkStatus({ kind: "loading" });
3409
+ try {
3410
+ const result = await onLoadMessageRef.current({ link: trimmed });
3411
+ if (linkRequestRef.current !== requestId) {
3412
+ return null;
3413
+ }
3414
+ resolvedLinkRef.current = trimmed;
3415
+ setLinkStatus(
3416
+ result.ok ? { kind: "ready", result } : { kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl }
3417
+ );
3418
+ return result;
3419
+ } catch (e) {
3420
+ if (linkRequestRef.current !== requestId) {
3421
+ return null;
3422
+ }
3423
+ resolvedLinkRef.current = trimmed;
3424
+ setLinkStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
3425
+ return null;
3426
+ }
3427
+ }, []);
3428
+ React.useEffect(() => {
3429
+ if (!open) {
3430
+ return;
3431
+ }
3432
+ const trimmed = link.trim();
3433
+ if (!trimmed) {
3434
+ linkRequestRef.current += 1;
3435
+ resolvedLinkRef.current = null;
3436
+ setLinkStatus({ kind: "idle" });
3437
+ return;
3438
+ }
3439
+ if (resolvedLinkRef.current === trimmed) {
3440
+ return;
3441
+ }
3442
+ const timer = setTimeout(() => {
3443
+ if (resolvedLinkRef.current !== trimmed) {
3444
+ void resolveLink(trimmed);
3445
+ }
3446
+ }, LINK_DEBOUNCE_MS);
3447
+ return () => clearTimeout(timer);
3448
+ }, [open, link, resolveLink]);
3251
3449
  const handleLoad = async () => {
3252
3450
  stopSignInPoll();
3253
- if (selectedRecent) {
3254
- onLoaded(recentToResult(selectedRecent));
3451
+ if (activeTab === "recent") {
3452
+ if (selectedRecent) {
3453
+ onLoaded(recentToResult(selectedRecent));
3454
+ }
3255
3455
  return;
3256
3456
  }
3257
3457
  const trimmed = link.trim();
3258
3458
  if (!trimmed) {
3259
- setStatus({ kind: "error", error: "Paste a Slack message link first." });
3459
+ setEmptyLinkError("Paste a Slack message link first.");
3260
3460
  return;
3261
3461
  }
3262
- setStatus({ kind: "loading" });
3263
- try {
3264
- const result = await onLoadMessageRef.current({ link: trimmed });
3265
- if (result.ok) {
3266
- onLoaded(result);
3267
- return;
3268
- }
3269
- setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl });
3270
- } catch (e) {
3271
- setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
3462
+ if (linkStatus.kind === "ready" && resolvedLinkRef.current === trimmed) {
3463
+ onLoaded(linkStatus.result);
3464
+ return;
3465
+ }
3466
+ setCommitting(true);
3467
+ const result = await resolveLink(trimmed);
3468
+ setCommitting(false);
3469
+ if (result?.ok) {
3470
+ onLoaded(result);
3272
3471
  }
3273
3472
  };
3274
3473
  const startFindSignIn = (oauthUrl) => {
@@ -3300,191 +3499,301 @@ function LoadMessageDialog({
3300
3499
  });
3301
3500
  }, POLL_INTERVAL_MS2);
3302
3501
  };
3303
- const notEditable = status.kind === "not-editable" ? status : null;
3304
- const signInUrl = notEditable?.oauthUrl && isSafeHref(notEditable.oauthUrl) ? notEditable.oauthUrl : null;
3502
+ 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;
3503
+ const canLoad = activeTab === "recent" ? !!selectedRecent : !!link.trim();
3504
+ const panelProps = (id) => hasRecent ? { role: "tabpanel", id: panelDomId(id), "aria-labelledby": tabDomId(id) } : {};
3305
3505
  return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
3306
3506
  DialogContent,
3307
3507
  {
3308
- className: "w-[calc(100vw-1.5rem)] max-w-lg rounded-lg",
3508
+ className: "flex max-h-[85svh] w-[calc(100vw-1.5rem)] max-w-4xl flex-col rounded-lg max-lg:max-w-lg",
3309
3509
  onOpenAutoFocus: (e) => {
3510
+ if (hasRecent) {
3511
+ return;
3512
+ }
3310
3513
  e.preventDefault();
3311
3514
  inputRef.current?.focus();
3312
3515
  },
3313
3516
  children: [
3314
- /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
3517
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { className: "shrink-0", children: [
3315
3518
  /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Find an existing message" }),
3316
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-1.5 text-left", children: [
3317
- /* @__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.` }),
3318
- /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
3319
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3320
- "button",
3321
- {
3322
- type: "button",
3323
- "aria-label": "How to copy a message link",
3324
- 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",
3325
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-3.5 w-3.5" })
3326
- }
3327
- ) }),
3328
- /* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
3329
- "In Slack, hover over the message and click the ",
3330
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u22EE More actions" }),
3331
- " button (or right-click the message), then choose ",
3332
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Copy link" }),
3333
- "."
3334
- ] })
3335
- ] })
3336
- ] })
3519
+ /* @__PURE__ */ jsxRuntime.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.` })
3337
3520
  ] }),
3338
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [
3339
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3340
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message-link", children: "Message link" }),
3341
- /* @__PURE__ */ jsxRuntime.jsx(
3342
- Input,
3521
+ /* @__PURE__ */ jsxRuntime.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: [
3522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-3 lg:min-h-0 lg:w-[19rem]", children: [
3523
+ hasRecent && /* @__PURE__ */ jsxRuntime.jsx(TabStrip, { activeTab, onSelect: setTab, tabDomId, panelDomId }),
3524
+ activeTab === "recent" ? /* @__PURE__ */ jsxRuntime.jsxs(
3525
+ "div",
3343
3526
  {
3344
- ref: inputRef,
3345
- id: "message-link",
3346
- value: link,
3347
- onChange: (e) => {
3348
- setLink(e.target.value);
3349
- setSelectedRecent(null);
3350
- stopSignInPoll();
3351
- if (status.kind !== "idle" && status.kind !== "loading") {
3352
- setStatus({ kind: "idle" });
3353
- }
3354
- },
3355
- onKeyDown: (e) => {
3356
- if (e.key === "Enter" && status.kind !== "loading") {
3357
- e.preventDefault();
3358
- handleLoad();
3359
- }
3360
- },
3361
- placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
3362
- autoComplete: "off",
3363
- spellCheck: false
3527
+ ...panelProps("recent"),
3528
+ className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-hidden",
3529
+ children: [
3530
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-col gap-1.5", children: [
3531
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
3532
+ channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
3533
+ channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
3534
+ channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
3535
+ channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
3536
+ "select",
3537
+ {
3538
+ id: "recent-channel-picker",
3539
+ value: channelId,
3540
+ onChange: (e) => setChannelId(e.target.value),
3541
+ 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",
3542
+ children: [
3543
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
3544
+ channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
3545
+ "#",
3546
+ c.name
3547
+ ] }, c.id))
3548
+ ]
3549
+ }
3550
+ )
3551
+ ] }),
3552
+ !channelId && channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
3553
+ channelId && recent === null && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3554
+ channelId && recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-xs text-destructive", children: recentError }),
3555
+ channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
3556
+ channelId && recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.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) => {
3557
+ const asUser = (m.editableVia ?? "bot") === "user";
3558
+ const authorName = m.username || (asUser ? "You" : "App bot");
3559
+ const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
3560
+ const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
3561
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3562
+ "button",
3563
+ {
3564
+ type: "button",
3565
+ "aria-pressed": isSelected,
3566
+ onClick: () => {
3567
+ setSelectedRecent(m);
3568
+ },
3569
+ className: cn(
3570
+ // Inset rings so the scroll container's overflow clip
3571
+ // can't shave the corners off the selected outline.
3572
+ "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",
3573
+ isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
3574
+ ),
3575
+ children: [
3576
+ safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
3577
+ "span",
3578
+ {
3579
+ "aria-hidden": true,
3580
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
3581
+ children: authorName.charAt(0).toUpperCase()
3582
+ }
3583
+ ),
3584
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3585
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
3586
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
3587
+ /* @__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" })
3588
+ ] }),
3589
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
3590
+ new Date(Number(m.ts) * 1e3).toLocaleString(),
3591
+ " ",
3592
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
3593
+ "(",
3594
+ m.ts,
3595
+ ")"
3596
+ ] })
3597
+ ] }),
3598
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
3599
+ ] })
3600
+ ]
3601
+ },
3602
+ `${m.channelId}:${m.ts}`
3603
+ );
3604
+ }) })
3605
+ ]
3606
+ }
3607
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
3608
+ "div",
3609
+ {
3610
+ ...panelProps("link"),
3611
+ className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-y-auto",
3612
+ children: [
3613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3614
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3615
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message-link", children: "Message link" }),
3616
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
3617
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3618
+ "button",
3619
+ {
3620
+ type: "button",
3621
+ "aria-label": LINK_HELP_LABEL,
3622
+ 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",
3623
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-3.5 w-3.5" })
3624
+ }
3625
+ ) }),
3626
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "right", children: LINK_HELP_TEXT })
3627
+ ] })
3628
+ ] }),
3629
+ /* @__PURE__ */ jsxRuntime.jsx(
3630
+ Input,
3631
+ {
3632
+ ref: inputRef,
3633
+ id: "message-link",
3634
+ value: link,
3635
+ onChange: (e) => {
3636
+ setLink(e.target.value);
3637
+ setEmptyLinkError(null);
3638
+ stopSignInPoll();
3639
+ },
3640
+ onKeyDown: (e) => {
3641
+ if (e.key === "Enter" && !committing) {
3642
+ e.preventDefault();
3643
+ handleLoad();
3644
+ }
3645
+ },
3646
+ placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
3647
+ autoComplete: "off",
3648
+ spellCheck: false
3649
+ }
3650
+ )
3651
+ ] }),
3652
+ emptyLinkError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: emptyLinkError })
3653
+ ]
3364
3654
  }
3365
3655
  )
3366
3656
  ] }),
3367
- 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 }),
3368
- 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: [
3657
+ /* @__PURE__ */ jsxRuntime.jsx(
3658
+ PreviewPane,
3659
+ {
3660
+ state: preview,
3661
+ hooks: previewHooks,
3662
+ theme: previewTheme,
3663
+ emptyHint: activeTab === "recent" ? "Select a recent message to preview it here." : "Paste a message link to preview it here.",
3664
+ signInPolling,
3665
+ onSignIn: startFindSignIn,
3666
+ onOpenAsNew
3667
+ }
3668
+ )
3669
+ ] }),
3670
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { className: "shrink-0", children: [
3671
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3672
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", onClick: handleLoad, disabled: committing || !canLoad, children: committing ? "Loading\u2026" : "Load message" })
3673
+ ] })
3674
+ ]
3675
+ }
3676
+ ) });
3677
+ }
3678
+ function TabStrip({
3679
+ activeTab,
3680
+ onSelect,
3681
+ tabDomId,
3682
+ panelDomId
3683
+ }) {
3684
+ const tabRefs = React.useRef({});
3685
+ const moveTo = (next) => {
3686
+ onSelect(next);
3687
+ tabRefs.current[next]?.focus();
3688
+ };
3689
+ return /* @__PURE__ */ jsxRuntime.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) => {
3690
+ const selected = entry.id === activeTab;
3691
+ return /* @__PURE__ */ jsxRuntime.jsx(
3692
+ "button",
3693
+ {
3694
+ ref: (node) => {
3695
+ tabRefs.current[entry.id] = node;
3696
+ },
3697
+ type: "button",
3698
+ role: "tab",
3699
+ id: tabDomId(entry.id),
3700
+ "aria-selected": selected,
3701
+ "aria-controls": panelDomId(entry.id),
3702
+ tabIndex: selected ? 0 : -1,
3703
+ onClick: () => onSelect(entry.id),
3704
+ onKeyDown: (e) => {
3705
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
3706
+ e.preventDefault();
3707
+ const delta = e.key === "ArrowRight" ? 1 : -1;
3708
+ moveTo(TABS[(index + delta + TABS.length) % TABS.length].id);
3709
+ } else if (e.key === "Home") {
3710
+ e.preventDefault();
3711
+ moveTo(TABS[0].id);
3712
+ } else if (e.key === "End") {
3713
+ e.preventDefault();
3714
+ moveTo(TABS[TABS.length - 1].id);
3715
+ }
3716
+ },
3717
+ className: cn(
3718
+ "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",
3719
+ selected ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
3720
+ ),
3721
+ children: entry.label
3722
+ },
3723
+ entry.id
3724
+ );
3725
+ }) });
3726
+ }
3727
+ function PreviewPane({
3728
+ state,
3729
+ hooks,
3730
+ theme,
3731
+ emptyHint,
3732
+ signInPolling,
3733
+ onSignIn,
3734
+ onOpenAsNew
3735
+ }) {
3736
+ const target = state.kind === "ready" ? state.target : null;
3737
+ const signInUrl = state.kind === "not-editable" && state.oauthUrl && isSafeHref(state.oauthUrl) ? state.oauthUrl : null;
3738
+ const verdictBlocks = state.kind === "not-editable" ? state.blocks : void 0;
3739
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-2 lg:min-h-0 lg:flex-1", children: [
3740
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: [
3741
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Preview" }),
3742
+ target?.channelName || target?.channelId ? /* @__PURE__ */ jsxRuntime.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,
3743
+ target?.editableVia ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rounded border px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: [
3744
+ "Editable as ",
3745
+ editableLabel(target.editableVia)
3746
+ ] }) : null
3747
+ ] }),
3748
+ /* @__PURE__ */ jsxRuntime.jsxs(
3749
+ "div",
3750
+ {
3751
+ role: "region",
3752
+ "aria-label": "Message preview",
3753
+ tabIndex: 0,
3754
+ 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",
3755
+ children: [
3756
+ state.kind === "not-editable" && /* @__PURE__ */ jsxRuntime.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: [
3369
3757
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
3370
3758
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
3371
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: notEditable.reason })
3759
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: state.reason })
3372
3760
  ] }),
3373
- signInUrl && /* @__PURE__ */ jsxRuntime.jsx(SlackSignInButton, { onClick: () => startFindSignIn(signInUrl), polling: signInPolling }),
3374
- notEditable.blocks && notEditable.blocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3761
+ signInUrl && /* @__PURE__ */ jsxRuntime.jsx(SlackSignInButton, { onClick: () => onSignIn(signInUrl), polling: signInPolling }),
3762
+ verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3375
3763
  Button,
3376
3764
  {
3377
3765
  type: "button",
3378
3766
  variant: "outline",
3379
3767
  size: "sm",
3380
3768
  className: "self-start",
3381
- onClick: () => onOpenAsNew(notEditable.blocks),
3769
+ onClick: () => onOpenAsNew(verdictBlocks),
3382
3770
  children: "Open as a new message instead"
3383
3771
  }
3384
3772
  )
3385
3773
  ] }),
3386
- hasRecent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
3387
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
3388
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" }),
3389
- "or pick a recent message",
3390
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })
3391
- ] }),
3392
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3393
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
3394
- channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
3395
- channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
3396
- channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
3397
- channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
3398
- "select",
3399
- {
3400
- id: "recent-channel-picker",
3401
- value: channelId,
3402
- onChange: (e) => setChannelId(e.target.value),
3403
- 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",
3404
- children: [
3405
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
3406
- channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
3407
- "#",
3408
- c.name
3409
- ] }, c.id))
3410
- ]
3411
- }
3412
- )
3413
- ] }),
3414
- !channelId && channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
3415
- channelId && recent === null && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
3416
- channelId && recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: recentError }),
3417
- 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." }),
3418
- channelId && recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-48 min-w-0 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
3419
- const asUser = (m.editableVia ?? "bot") === "user";
3420
- const authorName = m.username || (asUser ? "You" : "App bot");
3421
- const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
3422
- const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
3423
- return /* @__PURE__ */ jsxRuntime.jsxs(
3424
- "button",
3425
- {
3426
- type: "button",
3427
- "aria-pressed": isSelected,
3428
- onClick: () => {
3429
- setSelectedRecent(m);
3430
- setLink("");
3431
- if (status.kind !== "idle" && status.kind !== "loading") {
3432
- setStatus({ kind: "idle" });
3433
- }
3434
- },
3435
- className: cn(
3436
- // Inset rings so the scroll container's overflow clip
3437
- // can't shave the corners off the selected outline.
3438
- "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",
3439
- isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
3440
- ),
3441
- children: [
3442
- safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
3443
- "span",
3444
- {
3445
- "aria-hidden": true,
3446
- className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
3447
- children: authorName.charAt(0).toUpperCase()
3448
- }
3449
- ),
3450
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
3451
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
3452
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
3453
- /* @__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" })
3454
- ] }),
3455
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
3456
- new Date(Number(m.ts) * 1e3).toLocaleString(),
3457
- " ",
3458
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
3459
- "(",
3460
- m.ts,
3461
- ")"
3462
- ] })
3463
- ] }),
3464
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
3465
- ] })
3466
- ]
3467
- },
3468
- `${m.channelId}:${m.ts}`
3469
- );
3470
- }) })
3471
- ] })
3472
- ] }),
3473
- /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
3474
- /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
3475
- /* @__PURE__ */ jsxRuntime.jsx(
3476
- Button,
3774
+ state.kind === "error" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: state.error }),
3775
+ state.kind === "loading" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "m-auto flex items-center gap-2 text-sm text-muted-foreground", children: [
3776
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
3777
+ "Looking up that message\u2026"
3778
+ ] }),
3779
+ state.kind === "empty" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-auto px-4 text-center text-sm text-muted-foreground", children: emptyHint }),
3780
+ target && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
3781
+ SlackMessagePreview,
3477
3782
  {
3478
- type: "button",
3479
- onClick: handleLoad,
3480
- disabled: status.kind === "loading" || !selectedRecent && !link.trim(),
3481
- children: status.kind === "loading" ? "Loading\u2026" : "Load message"
3783
+ blocks: target.blocks,
3784
+ hooks,
3785
+ theme,
3786
+ workspaceName: target.workspaceName,
3787
+ authorName: target.username,
3788
+ authorIcon: target.iconUrl,
3789
+ time: formatTs(target.ts)
3482
3790
  }
3483
- )
3484
- ] })
3485
- ]
3486
- }
3487
- ) });
3791
+ ) }),
3792
+ verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SlackMessagePreview, { blocks: verdictBlocks, hooks, theme, time: "" }) })
3793
+ ]
3794
+ }
3795
+ )
3796
+ ] });
3488
3797
  }
3489
3798
  var PALETTE_DRAG_PREFIX = "palette:";
3490
3799
  function parsePaletteDragId(id) {
@@ -8707,60 +9016,6 @@ function dispatch(block, onChange) {
8707
9016
  return null;
8708
9017
  }
8709
9018
  }
8710
- function SlackBlockPreview({
8711
- block,
8712
- hooks,
8713
- theme = "light"
8714
- }) {
8715
- const rootRef = React.useRef(null);
8716
- const safeBlock = React.useMemo(() => sanitizeBlock(block), [block]);
8717
- React.useEffect(() => {
8718
- const root = rootRef.current;
8719
- if (!root) return;
8720
- for (const wrapper of root.querySelectorAll(
8721
- ".slack_blocks_to_jsx__image_media_trigger, .slack_blocks_to_jsx__video_title"
8722
- )) {
8723
- for (const btn of wrapper.querySelectorAll("button:not([aria-label])")) {
8724
- const title = wrapper.querySelector(".slack_blocks_to_jsx__image_title")?.textContent?.trim();
8725
- btn.setAttribute("aria-label", title ? `Toggle ${title}` : "Toggle media");
8726
- }
8727
- }
8728
- for (const a of root.querySelectorAll("a[href]")) {
8729
- const href = a.getAttribute("href");
8730
- if (!isSafeHref(href)) {
8731
- a.setAttribute("href", "#");
8732
- a.setAttribute("data-bk-blocked-href", "1");
8733
- }
8734
- }
8735
- for (const img of root.querySelectorAll("img[src]")) {
8736
- const src = img.getAttribute("src");
8737
- if (!isSafeImageSrc(src)) {
8738
- img.removeAttribute("src");
8739
- img.setAttribute("data-bk-blocked-src", "1");
8740
- }
8741
- }
8742
- });
8743
- return (
8744
- // The library scopes all of its CSS under `#slack_blocks_to_jsx` and
8745
- // depends on the `slack_blocks_to_jsx styles_enabled` classes plus the
8746
- // `data-theme` attribute for its button / mention / divider styling
8747
- // (e.g. button reset + `bg-green-primary` for primary). `<Message
8748
- // withoutWrapper>` skips that wrapper entirely, so we re-create it
8749
- // here without the avatar / timestamp chrome.
8750
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: rootRef, id: "slack_blocks_to_jsx", "data-theme": theme, className: "slack_blocks_to_jsx styles_enabled", children: /* @__PURE__ */ jsxRuntime.jsx(
8751
- slackBlocksToJsx.Message,
8752
- {
8753
- time: /* @__PURE__ */ new Date(),
8754
- name: "",
8755
- logo: "",
8756
- withoutWrapper: true,
8757
- theme,
8758
- blocks: [safeBlock],
8759
- hooks
8760
- }
8761
- ) })
8762
- );
8763
- }
8764
9019
  var BLOCK_TYPE_LABELS = {
8765
9020
  section: "Section",
8766
9021
  header: "Header",
@@ -9383,50 +9638,16 @@ function Surface({
9383
9638
  {
9384
9639
  "aria-label": "Block preview",
9385
9640
  className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
9386
- 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 }) })
9387
- }
9388
- );
9389
- }
9390
- function MessageFrame({
9391
- workspaceName,
9392
- authorName,
9393
- authorIcon,
9394
- isDark,
9395
- children
9396
- }) {
9397
- const displayName = authorName ?? workspaceName ?? "Your app";
9398
- const initial = displayName.slice(0, 1).toUpperCase();
9399
- const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
9400
- return /* @__PURE__ */ jsxRuntime.jsxs(
9401
- "div",
9402
- {
9403
- className: cn(
9404
- "flex flex-col overflow-hidden rounded-md border",
9405
- isDark ? "border-[#2c2d30] bg-[#1a1d21]" : "border-[#e8e8e8] bg-white"
9406
- ),
9407
- children: [
9408
- /* @__PURE__ */ jsxRuntime.jsxs(
9409
- "div",
9410
- {
9411
- className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
9412
- children: [
9413
- safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
9414
- "span",
9415
- {
9416
- className: cn(
9417
- "inline-flex h-7 w-7 items-center justify-center rounded text-[12px] font-semibold",
9418
- isDark ? "bg-white/10 text-white" : "bg-[#4a154b]/10 text-[#1d1c1d]"
9419
- ),
9420
- children: initial
9421
- }
9422
- ),
9423
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
9424
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "10:37 AM" })
9425
- ]
9426
- }
9427
- ),
9428
- children
9429
- ]
9641
+ 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(
9642
+ SlackMessageFrame,
9643
+ {
9644
+ workspaceName,
9645
+ authorName,
9646
+ authorIcon,
9647
+ isDark,
9648
+ children: blocksList
9649
+ }
9650
+ ) })
9430
9651
  }
9431
9652
  );
9432
9653
  }
@@ -10515,6 +10736,8 @@ function BlockKitchen(props) {
10515
10736
  onLoadMessage: loading.onLoadMessage,
10516
10737
  loadRecentMessages: loading.loadRecentMessages,
10517
10738
  loadChannels: recentChannelSource,
10739
+ previewHooks: mergedPreviewHooks,
10740
+ previewTheme,
10518
10741
  onLoaded: (result) => {
10519
10742
  resetAll(result.blocks);
10520
10743
  setEditTarget(toLoadedMessage(result));