@tightknitai/block-kitchen 0.10.2 → 0.10.4
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/README.md +21 -8
- package/dist/helpers.cjs +1 -8
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +17 -11
- package/dist/helpers.d.ts +17 -11
- package/dist/helpers.js +1 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +483 -281
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +484 -282
- package/dist/index.js.map +1 -1
- package/dist/{palette-BtSwlGUC.d.cts → palette-Sz27861i.d.cts} +15 -10
- package/dist/{palette-BtSwlGUC.d.ts → palette-Sz27861i.d.ts} +15 -10
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/styles.css +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCenter, DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
|
|
2
2
|
import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
3
|
-
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, Undo2, Redo2, Trash2, Code2, Pencil, Send, Search, Check, Copy,
|
|
3
|
+
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, Undo2, Redo2, Trash2, Code2, Pencil, Send, Search, Check, Copy, CheckCircle2, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Type, Bold, Italic, Strikethrough, Underline, Code, List, ListOrdered, Quote, Smile, Link as Link$1 } from 'lucide-react';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { createContext, useState, useRef, useEffect, useCallback, useMemo, useId, useContext } from 'react';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -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
|
|
|
@@ -1957,14 +1957,7 @@ function sanitizeBlocks(blocks) {
|
|
|
1957
1957
|
|
|
1958
1958
|
// src/lib/to-slack-blocks.ts
|
|
1959
1959
|
function toSlackBlocks(blocks) {
|
|
1960
|
-
return blocks.map((block) =>
|
|
1961
|
-
const safe = sanitizeBlock(block);
|
|
1962
|
-
if (safe.type === "header" && "level" in safe) {
|
|
1963
|
-
const { level: _omit, ...rest } = safe;
|
|
1964
|
-
return rest;
|
|
1965
|
-
}
|
|
1966
|
-
return safe;
|
|
1967
|
-
});
|
|
1960
|
+
return blocks.map((block) => sanitizeBlock(block));
|
|
1968
1961
|
}
|
|
1969
1962
|
|
|
1970
1963
|
// src/lib/validation-sanitize.ts
|
|
@@ -3031,6 +3024,128 @@ var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
|
|
|
3031
3024
|
}
|
|
3032
3025
|
));
|
|
3033
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
|
+
}
|
|
3034
3149
|
var POLL_INTERVAL_MS = 2500;
|
|
3035
3150
|
var MAX_POLLS = 24;
|
|
3036
3151
|
function useSlackSignIn(loadStatus, { open, enabled }) {
|
|
@@ -3104,6 +3219,12 @@ function SlackSignInButton({ onClick, polling }) {
|
|
|
3104
3219
|
/* @__PURE__ */ jsx(ExternalLink, {})
|
|
3105
3220
|
] }) });
|
|
3106
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 TABS = [
|
|
3225
|
+
{ id: "recent", label: "Pick from Recent" },
|
|
3226
|
+
{ id: "link", label: "Direct Link" }
|
|
3227
|
+
];
|
|
3107
3228
|
function recentToResult(msg) {
|
|
3108
3229
|
return {
|
|
3109
3230
|
ok: true,
|
|
@@ -3128,6 +3249,16 @@ function previewText(m) {
|
|
|
3128
3249
|
}
|
|
3129
3250
|
return `${m.blocks.length} block${m.blocks.length === 1 ? "" : "s"}`;
|
|
3130
3251
|
}
|
|
3252
|
+
function formatTs(ts) {
|
|
3253
|
+
const seconds = Number(ts);
|
|
3254
|
+
if (!ts || !Number.isFinite(seconds) || seconds <= 0) {
|
|
3255
|
+
return "";
|
|
3256
|
+
}
|
|
3257
|
+
return new Date(seconds * 1e3).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
|
|
3258
|
+
}
|
|
3259
|
+
function editableLabel(editableVia) {
|
|
3260
|
+
return editableVia === "user" ? "You" : "Bot";
|
|
3261
|
+
}
|
|
3131
3262
|
function LoadMessageDialog({
|
|
3132
3263
|
open,
|
|
3133
3264
|
onOpenChange,
|
|
@@ -3135,10 +3266,14 @@ function LoadMessageDialog({
|
|
|
3135
3266
|
loadRecentMessages,
|
|
3136
3267
|
loadChannels,
|
|
3137
3268
|
onLoaded,
|
|
3138
|
-
onOpenAsNew
|
|
3269
|
+
onOpenAsNew,
|
|
3270
|
+
previewHooks,
|
|
3271
|
+
previewTheme = "light"
|
|
3139
3272
|
}) {
|
|
3140
3273
|
const [link, setLink] = useState("");
|
|
3141
|
-
const [
|
|
3274
|
+
const [linkStatus, setLinkStatus] = useState({ kind: "idle" });
|
|
3275
|
+
const [emptyLinkError, setEmptyLinkError] = useState(null);
|
|
3276
|
+
const [committing, setCommitting] = useState(false);
|
|
3142
3277
|
const [channels, setChannels] = useState(null);
|
|
3143
3278
|
const [channelsError, setChannelsError] = useState(null);
|
|
3144
3279
|
const [channelId, setChannelId] = useState("");
|
|
@@ -3146,8 +3281,14 @@ function LoadMessageDialog({
|
|
|
3146
3281
|
const [recentError, setRecentError] = useState(null);
|
|
3147
3282
|
const [selectedRecent, setSelectedRecent] = useState(null);
|
|
3148
3283
|
const [signInPolling, setSignInPolling] = useState(false);
|
|
3284
|
+
const [tab, setTab] = useState("recent");
|
|
3149
3285
|
const inputRef = useRef(null);
|
|
3150
3286
|
const signInPollRef = useRef(null);
|
|
3287
|
+
const resolvedLinkRef = useRef(null);
|
|
3288
|
+
const linkRequestRef = useRef(0);
|
|
3289
|
+
const domId = useId();
|
|
3290
|
+
const tabDomId = (id) => `${domId}-${id}-tab`;
|
|
3291
|
+
const panelDomId = (id) => `${domId}-${id}-panel`;
|
|
3151
3292
|
const onLoadMessageRef = useRef(onLoadMessage);
|
|
3152
3293
|
const loadRecentMessagesRef = useRef(loadRecentMessages);
|
|
3153
3294
|
const loadChannelsRef = useRef(loadChannels);
|
|
@@ -3157,16 +3298,22 @@ function LoadMessageDialog({
|
|
|
3157
3298
|
loadChannelsRef.current = loadChannels;
|
|
3158
3299
|
});
|
|
3159
3300
|
const hasRecent = !!loadRecentMessages && !!loadChannels;
|
|
3301
|
+
const activeTab = hasRecent ? tab : "link";
|
|
3160
3302
|
useEffect(() => {
|
|
3161
3303
|
if (!open) {
|
|
3162
3304
|
return;
|
|
3163
3305
|
}
|
|
3164
3306
|
setLink("");
|
|
3165
|
-
|
|
3307
|
+
setLinkStatus({ kind: "idle" });
|
|
3308
|
+
setEmptyLinkError(null);
|
|
3309
|
+
setCommitting(false);
|
|
3166
3310
|
setChannelId("");
|
|
3167
3311
|
setRecent(null);
|
|
3168
3312
|
setRecentError(null);
|
|
3169
3313
|
setSelectedRecent(null);
|
|
3314
|
+
setTab("recent");
|
|
3315
|
+
resolvedLinkRef.current = null;
|
|
3316
|
+
linkRequestRef.current += 1;
|
|
3170
3317
|
}, [open]);
|
|
3171
3318
|
useEffect(() => {
|
|
3172
3319
|
const load = loadChannelsRef.current;
|
|
@@ -3225,27 +3372,71 @@ function LoadMessageDialog({
|
|
|
3225
3372
|
}
|
|
3226
3373
|
return stopSignInPoll;
|
|
3227
3374
|
}, [open, stopSignInPoll]);
|
|
3375
|
+
const resolveLink = useCallback(async (trimmed) => {
|
|
3376
|
+
const requestId = ++linkRequestRef.current;
|
|
3377
|
+
setLinkStatus({ kind: "loading" });
|
|
3378
|
+
try {
|
|
3379
|
+
const result = await onLoadMessageRef.current({ link: trimmed });
|
|
3380
|
+
if (linkRequestRef.current !== requestId) {
|
|
3381
|
+
return null;
|
|
3382
|
+
}
|
|
3383
|
+
resolvedLinkRef.current = trimmed;
|
|
3384
|
+
setLinkStatus(
|
|
3385
|
+
result.ok ? { kind: "ready", result } : { kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl }
|
|
3386
|
+
);
|
|
3387
|
+
return result;
|
|
3388
|
+
} catch (e) {
|
|
3389
|
+
if (linkRequestRef.current !== requestId) {
|
|
3390
|
+
return null;
|
|
3391
|
+
}
|
|
3392
|
+
resolvedLinkRef.current = trimmed;
|
|
3393
|
+
setLinkStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
|
|
3394
|
+
return null;
|
|
3395
|
+
}
|
|
3396
|
+
}, []);
|
|
3397
|
+
useEffect(() => {
|
|
3398
|
+
if (!open) {
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
const trimmed = link.trim();
|
|
3402
|
+
if (!trimmed) {
|
|
3403
|
+
linkRequestRef.current += 1;
|
|
3404
|
+
resolvedLinkRef.current = null;
|
|
3405
|
+
setLinkStatus({ kind: "idle" });
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
if (resolvedLinkRef.current === trimmed) {
|
|
3409
|
+
return;
|
|
3410
|
+
}
|
|
3411
|
+
const timer = setTimeout(() => {
|
|
3412
|
+
if (resolvedLinkRef.current !== trimmed) {
|
|
3413
|
+
void resolveLink(trimmed);
|
|
3414
|
+
}
|
|
3415
|
+
}, LINK_DEBOUNCE_MS);
|
|
3416
|
+
return () => clearTimeout(timer);
|
|
3417
|
+
}, [open, link, resolveLink]);
|
|
3228
3418
|
const handleLoad = async () => {
|
|
3229
3419
|
stopSignInPoll();
|
|
3230
|
-
if (
|
|
3231
|
-
|
|
3420
|
+
if (activeTab === "recent") {
|
|
3421
|
+
if (selectedRecent) {
|
|
3422
|
+
onLoaded(recentToResult(selectedRecent));
|
|
3423
|
+
}
|
|
3232
3424
|
return;
|
|
3233
3425
|
}
|
|
3234
3426
|
const trimmed = link.trim();
|
|
3235
3427
|
if (!trimmed) {
|
|
3236
|
-
|
|
3428
|
+
setEmptyLinkError("Paste a Slack message link first.");
|
|
3237
3429
|
return;
|
|
3238
3430
|
}
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
|
|
3431
|
+
if (linkStatus.kind === "ready" && resolvedLinkRef.current === trimmed) {
|
|
3432
|
+
onLoaded(linkStatus.result);
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
setCommitting(true);
|
|
3436
|
+
const result = await resolveLink(trimmed);
|
|
3437
|
+
setCommitting(false);
|
|
3438
|
+
if (result?.ok) {
|
|
3439
|
+
onLoaded(result);
|
|
3249
3440
|
}
|
|
3250
3441
|
};
|
|
3251
3442
|
const startFindSignIn = (oauthUrl) => {
|
|
@@ -3277,191 +3468,288 @@ function LoadMessageDialog({
|
|
|
3277
3468
|
});
|
|
3278
3469
|
}, POLL_INTERVAL_MS2);
|
|
3279
3470
|
};
|
|
3280
|
-
const
|
|
3281
|
-
const
|
|
3471
|
+
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;
|
|
3472
|
+
const canLoad = activeTab === "recent" ? !!selectedRecent : !!link.trim();
|
|
3473
|
+
const panelProps = (id) => hasRecent ? { role: "tabpanel", id: panelDomId(id), "aria-labelledby": tabDomId(id) } : {};
|
|
3282
3474
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
3283
3475
|
DialogContent,
|
|
3284
3476
|
{
|
|
3285
|
-
className: "w-[calc(100vw-1.5rem)]
|
|
3477
|
+
className: "flex max-h-[85svh] w-[calc(100vw-1.5rem)] flex-col rounded-lg lg:max-w-4xl",
|
|
3286
3478
|
onOpenAutoFocus: (e) => {
|
|
3479
|
+
if (hasRecent) {
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3287
3482
|
e.preventDefault();
|
|
3288
3483
|
inputRef.current?.focus();
|
|
3289
3484
|
},
|
|
3290
3485
|
children: [
|
|
3291
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3486
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "shrink-0", children: [
|
|
3292
3487
|
/* @__PURE__ */ jsx(DialogTitle, { children: "Find an existing message" }),
|
|
3293
|
-
/* @__PURE__ */
|
|
3294
|
-
/* @__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.` }),
|
|
3295
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
3296
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
3297
|
-
"button",
|
|
3298
|
-
{
|
|
3299
|
-
type: "button",
|
|
3300
|
-
"aria-label": "How to copy a message link",
|
|
3301
|
-
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",
|
|
3302
|
-
children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
|
|
3303
|
-
}
|
|
3304
|
-
) }),
|
|
3305
|
-
/* @__PURE__ */ jsxs(TooltipContent, { className: "max-w-xs leading-relaxed", children: [
|
|
3306
|
-
"In Slack, hover over the message and click the ",
|
|
3307
|
-
/* @__PURE__ */ jsx("strong", { children: "\u22EE More actions" }),
|
|
3308
|
-
" button (or right-click the message), then choose ",
|
|
3309
|
-
/* @__PURE__ */ jsx("strong", { children: "Copy link" }),
|
|
3310
|
-
"."
|
|
3311
|
-
] })
|
|
3312
|
-
] })
|
|
3313
|
-
] })
|
|
3488
|
+
/* @__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.` })
|
|
3314
3489
|
] }),
|
|
3315
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [
|
|
3316
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-
|
|
3317
|
-
/* @__PURE__ */ jsx(
|
|
3318
|
-
/* @__PURE__ */
|
|
3319
|
-
|
|
3490
|
+
/* @__PURE__ */ jsxs("div", { className: "-mx-1 flex min-h-0 min-w-0 flex-1 flex-col gap-4 overflow-y-auto px-1 lg:flex-row lg:gap-5 lg:overflow-hidden", children: [
|
|
3491
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-3 lg:min-h-0 lg:w-[19rem]", children: [
|
|
3492
|
+
hasRecent && /* @__PURE__ */ jsx(TabStrip, { activeTab, onSelect: setTab, tabDomId, panelDomId }),
|
|
3493
|
+
activeTab === "recent" ? /* @__PURE__ */ jsxs(
|
|
3494
|
+
"div",
|
|
3320
3495
|
{
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3496
|
+
...panelProps("recent"),
|
|
3497
|
+
className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-hidden",
|
|
3498
|
+
children: [
|
|
3499
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col gap-1.5", children: [
|
|
3500
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
|
|
3501
|
+
channels === null && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
|
|
3502
|
+
channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: channelsError }),
|
|
3503
|
+
channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
|
|
3504
|
+
channels && channels.length > 0 && /* @__PURE__ */ jsxs(
|
|
3505
|
+
"select",
|
|
3506
|
+
{
|
|
3507
|
+
id: "recent-channel-picker",
|
|
3508
|
+
value: channelId,
|
|
3509
|
+
onChange: (e) => setChannelId(e.target.value),
|
|
3510
|
+
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",
|
|
3511
|
+
children: [
|
|
3512
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
|
|
3513
|
+
channels.map((c) => /* @__PURE__ */ jsxs("option", { value: c.id, children: [
|
|
3514
|
+
"#",
|
|
3515
|
+
c.name
|
|
3516
|
+
] }, c.id))
|
|
3517
|
+
]
|
|
3518
|
+
}
|
|
3519
|
+
)
|
|
3520
|
+
] }),
|
|
3521
|
+
!channelId && channels && channels.length > 0 && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
|
|
3522
|
+
channelId && recent === null && !recentError && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
|
|
3523
|
+
channelId && recentError && /* @__PURE__ */ jsx("p", { className: "shrink-0 text-xs text-destructive", children: recentError }),
|
|
3524
|
+
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." }),
|
|
3525
|
+
channelId && recent && recent.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex max-h-60 min-h-0 min-w-0 flex-col gap-1 overflow-y-auto lg:max-h-none lg:flex-1", children: recent.map((m) => {
|
|
3526
|
+
const asUser = (m.editableVia ?? "bot") === "user";
|
|
3527
|
+
const authorName = m.username || (asUser ? "You" : "App bot");
|
|
3528
|
+
const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
|
|
3529
|
+
const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
|
|
3530
|
+
return /* @__PURE__ */ jsxs(
|
|
3531
|
+
"button",
|
|
3532
|
+
{
|
|
3533
|
+
type: "button",
|
|
3534
|
+
"aria-pressed": isSelected,
|
|
3535
|
+
onClick: () => {
|
|
3536
|
+
setSelectedRecent(m);
|
|
3537
|
+
},
|
|
3538
|
+
className: cn(
|
|
3539
|
+
// Inset rings so the scroll container's overflow clip
|
|
3540
|
+
// can't shave the corners off the selected outline.
|
|
3541
|
+
"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",
|
|
3542
|
+
isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
|
|
3543
|
+
),
|
|
3544
|
+
children: [
|
|
3545
|
+
safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsx(
|
|
3546
|
+
"span",
|
|
3547
|
+
{
|
|
3548
|
+
"aria-hidden": true,
|
|
3549
|
+
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
|
|
3550
|
+
children: authorName.charAt(0).toUpperCase()
|
|
3551
|
+
}
|
|
3552
|
+
),
|
|
3553
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3554
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
3555
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
|
|
3556
|
+
/* @__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" })
|
|
3557
|
+
] }),
|
|
3558
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3559
|
+
new Date(Number(m.ts) * 1e3).toLocaleString(),
|
|
3560
|
+
" ",
|
|
3561
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
3562
|
+
"(",
|
|
3563
|
+
m.ts,
|
|
3564
|
+
")"
|
|
3565
|
+
] })
|
|
3566
|
+
] }),
|
|
3567
|
+
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
|
|
3568
|
+
] })
|
|
3569
|
+
]
|
|
3570
|
+
},
|
|
3571
|
+
`${m.channelId}:${m.ts}`
|
|
3572
|
+
);
|
|
3573
|
+
}) })
|
|
3574
|
+
]
|
|
3575
|
+
}
|
|
3576
|
+
) : /* @__PURE__ */ jsxs(
|
|
3577
|
+
"div",
|
|
3578
|
+
{
|
|
3579
|
+
...panelProps("link"),
|
|
3580
|
+
className: "-mx-1 flex min-w-0 flex-col gap-2 px-1 lg:min-h-0 lg:flex-1 lg:overflow-y-auto",
|
|
3581
|
+
children: [
|
|
3582
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3583
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "message-link", children: "Message link" }),
|
|
3584
|
+
/* @__PURE__ */ jsx(
|
|
3585
|
+
Input,
|
|
3586
|
+
{
|
|
3587
|
+
ref: inputRef,
|
|
3588
|
+
id: "message-link",
|
|
3589
|
+
value: link,
|
|
3590
|
+
onChange: (e) => {
|
|
3591
|
+
setLink(e.target.value);
|
|
3592
|
+
setEmptyLinkError(null);
|
|
3593
|
+
stopSignInPoll();
|
|
3594
|
+
},
|
|
3595
|
+
onKeyDown: (e) => {
|
|
3596
|
+
if (e.key === "Enter" && !committing) {
|
|
3597
|
+
e.preventDefault();
|
|
3598
|
+
handleLoad();
|
|
3599
|
+
}
|
|
3600
|
+
},
|
|
3601
|
+
placeholder: "https://your-workspace.slack.com/archives/C\u2026/p\u2026",
|
|
3602
|
+
autoComplete: "off",
|
|
3603
|
+
spellCheck: false
|
|
3604
|
+
}
|
|
3605
|
+
),
|
|
3606
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: LINK_HELP_TEXT })
|
|
3607
|
+
] }),
|
|
3608
|
+
emptyLinkError && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-2 text-xs text-destructive", children: emptyLinkError })
|
|
3609
|
+
]
|
|
3341
3610
|
}
|
|
3342
3611
|
)
|
|
3343
3612
|
] }),
|
|
3344
|
-
|
|
3345
|
-
|
|
3613
|
+
/* @__PURE__ */ jsx(
|
|
3614
|
+
PreviewPane,
|
|
3615
|
+
{
|
|
3616
|
+
state: preview,
|
|
3617
|
+
hooks: previewHooks,
|
|
3618
|
+
theme: previewTheme,
|
|
3619
|
+
emptyHint: activeTab === "recent" ? "Select a recent message to preview it here." : "Paste a message link to preview it here.",
|
|
3620
|
+
signInPolling,
|
|
3621
|
+
onSignIn: startFindSignIn,
|
|
3622
|
+
onOpenAsNew
|
|
3623
|
+
}
|
|
3624
|
+
)
|
|
3625
|
+
] }),
|
|
3626
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: "shrink-0", children: [
|
|
3627
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3628
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: handleLoad, disabled: committing || !canLoad, children: committing ? "Loading\u2026" : "Load message" })
|
|
3629
|
+
] })
|
|
3630
|
+
]
|
|
3631
|
+
}
|
|
3632
|
+
) });
|
|
3633
|
+
}
|
|
3634
|
+
function TabStrip({
|
|
3635
|
+
activeTab,
|
|
3636
|
+
onSelect,
|
|
3637
|
+
tabDomId,
|
|
3638
|
+
panelDomId
|
|
3639
|
+
}) {
|
|
3640
|
+
const tabRefs = useRef({});
|
|
3641
|
+
const moveTo = (next) => {
|
|
3642
|
+
onSelect(next);
|
|
3643
|
+
tabRefs.current[next]?.focus();
|
|
3644
|
+
};
|
|
3645
|
+
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) => {
|
|
3646
|
+
const selected = entry.id === activeTab;
|
|
3647
|
+
return /* @__PURE__ */ jsx(
|
|
3648
|
+
"button",
|
|
3649
|
+
{
|
|
3650
|
+
ref: (node) => {
|
|
3651
|
+
tabRefs.current[entry.id] = node;
|
|
3652
|
+
},
|
|
3653
|
+
type: "button",
|
|
3654
|
+
role: "tab",
|
|
3655
|
+
id: tabDomId(entry.id),
|
|
3656
|
+
"aria-selected": selected,
|
|
3657
|
+
"aria-controls": panelDomId(entry.id),
|
|
3658
|
+
tabIndex: selected ? 0 : -1,
|
|
3659
|
+
onClick: () => onSelect(entry.id),
|
|
3660
|
+
onKeyDown: (e) => {
|
|
3661
|
+
if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
|
|
3662
|
+
e.preventDefault();
|
|
3663
|
+
const delta = e.key === "ArrowRight" ? 1 : -1;
|
|
3664
|
+
moveTo(TABS[(index + delta + TABS.length) % TABS.length].id);
|
|
3665
|
+
} else if (e.key === "Home") {
|
|
3666
|
+
e.preventDefault();
|
|
3667
|
+
moveTo(TABS[0].id);
|
|
3668
|
+
} else if (e.key === "End") {
|
|
3669
|
+
e.preventDefault();
|
|
3670
|
+
moveTo(TABS[TABS.length - 1].id);
|
|
3671
|
+
}
|
|
3672
|
+
},
|
|
3673
|
+
className: cn(
|
|
3674
|
+
"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",
|
|
3675
|
+
selected ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
|
3676
|
+
),
|
|
3677
|
+
children: entry.label
|
|
3678
|
+
},
|
|
3679
|
+
entry.id
|
|
3680
|
+
);
|
|
3681
|
+
}) });
|
|
3682
|
+
}
|
|
3683
|
+
function PreviewPane({
|
|
3684
|
+
state,
|
|
3685
|
+
hooks,
|
|
3686
|
+
theme,
|
|
3687
|
+
emptyHint,
|
|
3688
|
+
signInPolling,
|
|
3689
|
+
onSignIn,
|
|
3690
|
+
onOpenAsNew
|
|
3691
|
+
}) {
|
|
3692
|
+
const target = state.kind === "ready" ? state.target : null;
|
|
3693
|
+
const signInUrl = state.kind === "not-editable" && state.oauthUrl && isSafeHref(state.oauthUrl) ? state.oauthUrl : null;
|
|
3694
|
+
const verdictBlocks = state.kind === "not-editable" ? state.blocks : void 0;
|
|
3695
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-2 lg:min-h-0 lg:flex-1", children: [
|
|
3696
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: [
|
|
3697
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Preview" }),
|
|
3698
|
+
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,
|
|
3699
|
+
target?.editableVia ? /* @__PURE__ */ jsxs("span", { className: "rounded border px-1.5 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: [
|
|
3700
|
+
"Editable as ",
|
|
3701
|
+
editableLabel(target.editableVia)
|
|
3702
|
+
] }) : null
|
|
3703
|
+
] }),
|
|
3704
|
+
/* @__PURE__ */ jsxs(
|
|
3705
|
+
"div",
|
|
3706
|
+
{
|
|
3707
|
+
role: "region",
|
|
3708
|
+
"aria-label": "Message preview",
|
|
3709
|
+
tabIndex: 0,
|
|
3710
|
+
className: "flex max-h-72 min-h-32 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 lg:max-h-none lg:min-h-0",
|
|
3711
|
+
children: [
|
|
3712
|
+
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: [
|
|
3346
3713
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
3347
3714
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
3348
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1", children:
|
|
3715
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: state.reason })
|
|
3349
3716
|
] }),
|
|
3350
|
-
signInUrl && /* @__PURE__ */ jsx(SlackSignInButton, { onClick: () =>
|
|
3351
|
-
|
|
3717
|
+
signInUrl && /* @__PURE__ */ jsx(SlackSignInButton, { onClick: () => onSignIn(signInUrl), polling: signInPolling }),
|
|
3718
|
+
verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsx(
|
|
3352
3719
|
Button,
|
|
3353
3720
|
{
|
|
3354
3721
|
type: "button",
|
|
3355
3722
|
variant: "outline",
|
|
3356
3723
|
size: "sm",
|
|
3357
3724
|
className: "self-start",
|
|
3358
|
-
onClick: () => onOpenAsNew(
|
|
3725
|
+
onClick: () => onOpenAsNew(verdictBlocks),
|
|
3359
3726
|
children: "Open as a new message instead"
|
|
3360
3727
|
}
|
|
3361
3728
|
)
|
|
3362
3729
|
] }),
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
channels === null && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
|
|
3372
|
-
channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: channelsError }),
|
|
3373
|
-
channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
|
|
3374
|
-
channels && channels.length > 0 && /* @__PURE__ */ jsxs(
|
|
3375
|
-
"select",
|
|
3376
|
-
{
|
|
3377
|
-
id: "recent-channel-picker",
|
|
3378
|
-
value: channelId,
|
|
3379
|
-
onChange: (e) => setChannelId(e.target.value),
|
|
3380
|
-
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",
|
|
3381
|
-
children: [
|
|
3382
|
-
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
|
|
3383
|
-
channels.map((c) => /* @__PURE__ */ jsxs("option", { value: c.id, children: [
|
|
3384
|
-
"#",
|
|
3385
|
-
c.name
|
|
3386
|
-
] }, c.id))
|
|
3387
|
-
]
|
|
3388
|
-
}
|
|
3389
|
-
)
|
|
3390
|
-
] }),
|
|
3391
|
-
!channelId && channels && channels.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
|
|
3392
|
-
channelId && recent === null && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
|
|
3393
|
-
channelId && recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: recentError }),
|
|
3394
|
-
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." }),
|
|
3395
|
-
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) => {
|
|
3396
|
-
const asUser = (m.editableVia ?? "bot") === "user";
|
|
3397
|
-
const authorName = m.username || (asUser ? "You" : "App bot");
|
|
3398
|
-
const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
|
|
3399
|
-
const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
|
|
3400
|
-
return /* @__PURE__ */ jsxs(
|
|
3401
|
-
"button",
|
|
3402
|
-
{
|
|
3403
|
-
type: "button",
|
|
3404
|
-
"aria-pressed": isSelected,
|
|
3405
|
-
onClick: () => {
|
|
3406
|
-
setSelectedRecent(m);
|
|
3407
|
-
setLink("");
|
|
3408
|
-
if (status.kind !== "idle" && status.kind !== "loading") {
|
|
3409
|
-
setStatus({ kind: "idle" });
|
|
3410
|
-
}
|
|
3411
|
-
},
|
|
3412
|
-
className: cn(
|
|
3413
|
-
// Inset rings so the scroll container's overflow clip
|
|
3414
|
-
// can't shave the corners off the selected outline.
|
|
3415
|
-
"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",
|
|
3416
|
-
isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
|
|
3417
|
-
),
|
|
3418
|
-
children: [
|
|
3419
|
-
safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsx(
|
|
3420
|
-
"span",
|
|
3421
|
-
{
|
|
3422
|
-
"aria-hidden": true,
|
|
3423
|
-
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
|
|
3424
|
-
children: authorName.charAt(0).toUpperCase()
|
|
3425
|
-
}
|
|
3426
|
-
),
|
|
3427
|
-
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3428
|
-
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
3429
|
-
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
|
|
3430
|
-
/* @__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" })
|
|
3431
|
-
] }),
|
|
3432
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3433
|
-
new Date(Number(m.ts) * 1e3).toLocaleString(),
|
|
3434
|
-
" ",
|
|
3435
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
3436
|
-
"(",
|
|
3437
|
-
m.ts,
|
|
3438
|
-
")"
|
|
3439
|
-
] })
|
|
3440
|
-
] }),
|
|
3441
|
-
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
|
|
3442
|
-
] })
|
|
3443
|
-
]
|
|
3444
|
-
},
|
|
3445
|
-
`${m.channelId}:${m.ts}`
|
|
3446
|
-
);
|
|
3447
|
-
}) })
|
|
3448
|
-
] })
|
|
3449
|
-
] }),
|
|
3450
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3451
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3452
|
-
/* @__PURE__ */ jsx(
|
|
3453
|
-
Button,
|
|
3730
|
+
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 }),
|
|
3731
|
+
state.kind === "loading" && /* @__PURE__ */ jsxs("p", { className: "m-auto flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
3732
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
3733
|
+
"Looking up that message\u2026"
|
|
3734
|
+
] }),
|
|
3735
|
+
state.kind === "empty" && /* @__PURE__ */ jsx("p", { className: "m-auto px-4 text-center text-sm text-muted-foreground", children: emptyHint }),
|
|
3736
|
+
target && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsx(
|
|
3737
|
+
SlackMessagePreview,
|
|
3454
3738
|
{
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3739
|
+
blocks: target.blocks,
|
|
3740
|
+
hooks,
|
|
3741
|
+
theme,
|
|
3742
|
+
workspaceName: target.workspaceName,
|
|
3743
|
+
authorName: target.username,
|
|
3744
|
+
authorIcon: target.iconUrl,
|
|
3745
|
+
time: formatTs(target.ts)
|
|
3459
3746
|
}
|
|
3460
|
-
)
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3747
|
+
) }),
|
|
3748
|
+
verdictBlocks && verdictBlocks.length > 0 && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsx(SlackMessagePreview, { blocks: verdictBlocks, hooks, theme, time: "" }) })
|
|
3749
|
+
]
|
|
3750
|
+
}
|
|
3751
|
+
)
|
|
3752
|
+
] });
|
|
3465
3753
|
}
|
|
3466
3754
|
var PALETTE_DRAG_PREFIX = "palette:";
|
|
3467
3755
|
function parsePaletteDragId(id) {
|
|
@@ -5654,7 +5942,7 @@ function HeaderEditor({ block, onChange }) {
|
|
|
5654
5942
|
] })
|
|
5655
5943
|
}
|
|
5656
5944
|
),
|
|
5657
|
-
/* @__PURE__ */ jsx(EditorField, { label: "Level", help: "Optional.
|
|
5945
|
+
/* @__PURE__ */ jsx(EditorField, { label: "Level", help: "Optional. Sets the heading size Slack renders, from H1 (largest) to H4.", children: /* @__PURE__ */ jsxs(
|
|
5658
5946
|
RadioGroup,
|
|
5659
5947
|
{
|
|
5660
5948
|
value: level === void 0 ? "default" : String(level),
|
|
@@ -8684,60 +8972,6 @@ function dispatch(block, onChange) {
|
|
|
8684
8972
|
return null;
|
|
8685
8973
|
}
|
|
8686
8974
|
}
|
|
8687
|
-
function SlackBlockPreview({
|
|
8688
|
-
block,
|
|
8689
|
-
hooks,
|
|
8690
|
-
theme = "light"
|
|
8691
|
-
}) {
|
|
8692
|
-
const rootRef = useRef(null);
|
|
8693
|
-
const safeBlock = useMemo(() => sanitizeBlock(block), [block]);
|
|
8694
|
-
useEffect(() => {
|
|
8695
|
-
const root = rootRef.current;
|
|
8696
|
-
if (!root) return;
|
|
8697
|
-
for (const wrapper of root.querySelectorAll(
|
|
8698
|
-
".slack_blocks_to_jsx__image_media_trigger, .slack_blocks_to_jsx__video_title"
|
|
8699
|
-
)) {
|
|
8700
|
-
for (const btn of wrapper.querySelectorAll("button:not([aria-label])")) {
|
|
8701
|
-
const title = wrapper.querySelector(".slack_blocks_to_jsx__image_title")?.textContent?.trim();
|
|
8702
|
-
btn.setAttribute("aria-label", title ? `Toggle ${title}` : "Toggle media");
|
|
8703
|
-
}
|
|
8704
|
-
}
|
|
8705
|
-
for (const a of root.querySelectorAll("a[href]")) {
|
|
8706
|
-
const href = a.getAttribute("href");
|
|
8707
|
-
if (!isSafeHref(href)) {
|
|
8708
|
-
a.setAttribute("href", "#");
|
|
8709
|
-
a.setAttribute("data-bk-blocked-href", "1");
|
|
8710
|
-
}
|
|
8711
|
-
}
|
|
8712
|
-
for (const img of root.querySelectorAll("img[src]")) {
|
|
8713
|
-
const src = img.getAttribute("src");
|
|
8714
|
-
if (!isSafeImageSrc(src)) {
|
|
8715
|
-
img.removeAttribute("src");
|
|
8716
|
-
img.setAttribute("data-bk-blocked-src", "1");
|
|
8717
|
-
}
|
|
8718
|
-
}
|
|
8719
|
-
});
|
|
8720
|
-
return (
|
|
8721
|
-
// The library scopes all of its CSS under `#slack_blocks_to_jsx` and
|
|
8722
|
-
// depends on the `slack_blocks_to_jsx styles_enabled` classes plus the
|
|
8723
|
-
// `data-theme` attribute for its button / mention / divider styling
|
|
8724
|
-
// (e.g. button reset + `bg-green-primary` for primary). `<Message
|
|
8725
|
-
// withoutWrapper>` skips that wrapper entirely, so we re-create it
|
|
8726
|
-
// here without the avatar / timestamp chrome.
|
|
8727
|
-
/* @__PURE__ */ jsx("div", { ref: rootRef, id: "slack_blocks_to_jsx", "data-theme": theme, className: "slack_blocks_to_jsx styles_enabled", children: /* @__PURE__ */ jsx(
|
|
8728
|
-
Message,
|
|
8729
|
-
{
|
|
8730
|
-
time: /* @__PURE__ */ new Date(),
|
|
8731
|
-
name: "",
|
|
8732
|
-
logo: "",
|
|
8733
|
-
withoutWrapper: true,
|
|
8734
|
-
theme,
|
|
8735
|
-
blocks: [safeBlock],
|
|
8736
|
-
hooks
|
|
8737
|
-
}
|
|
8738
|
-
) })
|
|
8739
|
-
);
|
|
8740
|
-
}
|
|
8741
8975
|
var BLOCK_TYPE_LABELS = {
|
|
8742
8976
|
section: "Section",
|
|
8743
8977
|
header: "Header",
|
|
@@ -9360,50 +9594,16 @@ function Surface({
|
|
|
9360
9594
|
{
|
|
9361
9595
|
"aria-label": "Block preview",
|
|
9362
9596
|
className: cn("flex min-w-0 flex-1 flex-col overflow-y-auto bg-muted p-3 sm:p-6"),
|
|
9363
|
-
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(
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
}) {
|
|
9374
|
-
const displayName = authorName ?? workspaceName ?? "Your app";
|
|
9375
|
-
const initial = displayName.slice(0, 1).toUpperCase();
|
|
9376
|
-
const safeIcon = authorIcon && isSafeImageSrc(authorIcon) ? authorIcon : null;
|
|
9377
|
-
return /* @__PURE__ */ jsxs(
|
|
9378
|
-
"div",
|
|
9379
|
-
{
|
|
9380
|
-
className: cn(
|
|
9381
|
-
"flex flex-col overflow-hidden rounded-md border",
|
|
9382
|
-
isDark ? "border-[#2c2d30] bg-[#1a1d21]" : "border-[#e8e8e8] bg-white"
|
|
9383
|
-
),
|
|
9384
|
-
children: [
|
|
9385
|
-
/* @__PURE__ */ jsxs(
|
|
9386
|
-
"div",
|
|
9387
|
-
{
|
|
9388
|
-
className: cn("flex items-center gap-2 px-5 pt-3 pb-1 text-xs", isDark ? "text-white/60" : "text-[#616061]"),
|
|
9389
|
-
children: [
|
|
9390
|
-
safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-7 w-7 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsx(
|
|
9391
|
-
"span",
|
|
9392
|
-
{
|
|
9393
|
-
className: cn(
|
|
9394
|
-
"inline-flex h-7 w-7 items-center justify-center rounded text-[12px] font-semibold",
|
|
9395
|
-
isDark ? "bg-white/10 text-white" : "bg-[#4a154b]/10 text-[#1d1c1d]"
|
|
9396
|
-
),
|
|
9397
|
-
children: initial
|
|
9398
|
-
}
|
|
9399
|
-
),
|
|
9400
|
-
/* @__PURE__ */ jsx("span", { className: cn("font-bold text-sm", isDark ? "text-white" : "text-[#1d1c1d]"), children: displayName }),
|
|
9401
|
-
/* @__PURE__ */ jsx("span", { children: "10:37 AM" })
|
|
9402
|
-
]
|
|
9403
|
-
}
|
|
9404
|
-
),
|
|
9405
|
-
children
|
|
9406
|
-
]
|
|
9597
|
+
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(
|
|
9598
|
+
SlackMessageFrame,
|
|
9599
|
+
{
|
|
9600
|
+
workspaceName,
|
|
9601
|
+
authorName,
|
|
9602
|
+
authorIcon,
|
|
9603
|
+
isDark,
|
|
9604
|
+
children: blocksList
|
|
9605
|
+
}
|
|
9606
|
+
) })
|
|
9407
9607
|
}
|
|
9408
9608
|
);
|
|
9409
9609
|
}
|
|
@@ -10492,6 +10692,8 @@ function BlockKitchen(props) {
|
|
|
10492
10692
|
onLoadMessage: loading.onLoadMessage,
|
|
10493
10693
|
loadRecentMessages: loading.loadRecentMessages,
|
|
10494
10694
|
loadChannels: recentChannelSource,
|
|
10695
|
+
previewHooks: mergedPreviewHooks,
|
|
10696
|
+
previewTheme,
|
|
10495
10697
|
onLoaded: (result) => {
|
|
10496
10698
|
resetAll(result.blocks);
|
|
10497
10699
|
setEditTarget(toLoadedMessage(result));
|