@tightknitai/block-kitchen 0.8.2 → 0.8.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 +7 -0
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +862 -160
- 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 +865 -163
- package/dist/index.js.map +1 -1
- package/dist/{palette-CTUROcUC.d.cts → palette-Dco-CVMi.d.cts} +32 -1
- package/dist/{palette-CTUROcUC.d.ts → palette-Dco-CVMi.d.ts} +32 -1
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, MoreHorizontal, Send, Search, CheckCircle2, Check, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Pencil, Copy, Type, Bold, Italic, Strikethrough, Code, List, ListOrdered, Quote, Undo2, Redo2, Link as Link$1 } from 'lucide-react';
|
|
3
|
+
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, MoreHorizontal, Send, Search, CheckCircle2, Check, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Pencil, Copy, Type, Bold, Italic, Strikethrough, Code, List, ListOrdered, Quote, Undo2, Redo2, Link as Link$1, Smile } from 'lucide-react';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import { useMemo, useState, useCallback, useRef, useEffect, useId } from 'react';
|
|
5
|
+
import { createContext, useMemo, useState, useCallback, useRef, useEffect, useId, useContext } from 'react';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
7
8
|
import { clsx } from 'clsx';
|
|
8
9
|
import { twMerge } from 'tailwind-merge';
|
|
9
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
11
11
|
import { validateBlockKit, CARD_ACTIONS_MAX } from '@tightknitai/slack-block-kit-validator';
|
|
12
12
|
import { nanoid } from 'nanoid';
|
|
@@ -17,12 +17,41 @@ import { CSS } from '@dnd-kit/utilities';
|
|
|
17
17
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
18
18
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
19
19
|
import Link from '@tiptap/extension-link';
|
|
20
|
-
import { useEditor, EditorContent } from '@tiptap/react';
|
|
20
|
+
import { Node, ReactNodeViewRenderer, mergeAttributes, NodeViewWrapper, useEditor, EditorContent } from '@tiptap/react';
|
|
21
21
|
import StarterKit from '@tiptap/starter-kit';
|
|
22
22
|
import 'slack-blocks-to-jsx/dist/style.css';
|
|
23
23
|
import { Message } from 'slack-blocks-to-jsx';
|
|
24
24
|
|
|
25
25
|
// src/components/block-kitchen.tsx
|
|
26
|
+
function makeEmojiHook(byName) {
|
|
27
|
+
return (data, parse) => {
|
|
28
|
+
const entry = byName.get(data.name);
|
|
29
|
+
if (entry) {
|
|
30
|
+
if (entry.url) {
|
|
31
|
+
return /* @__PURE__ */ jsx(
|
|
32
|
+
"img",
|
|
33
|
+
{
|
|
34
|
+
src: entry.url,
|
|
35
|
+
alt: `:${data.name}:`,
|
|
36
|
+
title: `:${data.name}:`,
|
|
37
|
+
className: "bk-custom-emoji",
|
|
38
|
+
style: {
|
|
39
|
+
display: "inline-block",
|
|
40
|
+
width: "1.25em",
|
|
41
|
+
height: "1.25em",
|
|
42
|
+
verticalAlign: "-0.25em",
|
|
43
|
+
objectFit: "contain"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (entry.alias) {
|
|
49
|
+
return parse({ name: entry.alias, unicode: data.unicode, skin_tone: data.skin_tone });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return parse(data);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
26
55
|
var defaultPalette = [
|
|
27
56
|
{
|
|
28
57
|
name: "Agents",
|
|
@@ -1519,50 +1548,63 @@ function useIsMobile() {
|
|
|
1519
1548
|
}, []);
|
|
1520
1549
|
return isMobile;
|
|
1521
1550
|
}
|
|
1551
|
+
var EMPTY = {
|
|
1552
|
+
customEmojis: [],
|
|
1553
|
+
byName: /* @__PURE__ */ new Map()
|
|
1554
|
+
};
|
|
1555
|
+
var CustomEmojiContext = createContext(EMPTY);
|
|
1556
|
+
function CustomEmojiProvider({ customEmojis, children }) {
|
|
1557
|
+
const value = useMemo(() => {
|
|
1558
|
+
if (!customEmojis || customEmojis.length === 0) {
|
|
1559
|
+
return EMPTY;
|
|
1560
|
+
}
|
|
1561
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1562
|
+
for (const emoji of customEmojis) {
|
|
1563
|
+
byName.set(emoji.name, emoji);
|
|
1564
|
+
}
|
|
1565
|
+
return { customEmojis, byName };
|
|
1566
|
+
}, [customEmojis]);
|
|
1567
|
+
return /* @__PURE__ */ jsx(CustomEmojiContext.Provider, { value, children });
|
|
1568
|
+
}
|
|
1569
|
+
function useCustomEmojis() {
|
|
1570
|
+
return useContext(CustomEmojiContext);
|
|
1571
|
+
}
|
|
1522
1572
|
|
|
1523
1573
|
// src/lib/error-grouping.ts
|
|
1524
1574
|
function toValidatorSurface(surface) {
|
|
1525
1575
|
return surface === "app_home" ? "home" : surface;
|
|
1526
1576
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1577
|
+
var BLOCK_PREFIX = /^blocks\[(\d+)\]/;
|
|
1578
|
+
function extractBlockIndex(raw) {
|
|
1579
|
+
const match = BLOCK_PREFIX.exec(raw);
|
|
1580
|
+
return match ? Number.parseInt(match[1], 10) : null;
|
|
1581
|
+
}
|
|
1582
|
+
function toBlockMessage(raw) {
|
|
1583
|
+
const rest = raw.replace(BLOCK_PREFIX, "");
|
|
1584
|
+
if (rest.startsWith(":")) {
|
|
1585
|
+
return rest.replace(/^:\s*/, "");
|
|
1534
1586
|
}
|
|
1535
|
-
return
|
|
1587
|
+
return rest.replace(/^\./, "");
|
|
1536
1588
|
}
|
|
1537
|
-
function
|
|
1538
|
-
return
|
|
1589
|
+
function toGeneralMessage(raw) {
|
|
1590
|
+
return raw.startsWith("(root)") ? raw.replace(/^\(root\):?\s*/, "") : raw;
|
|
1539
1591
|
}
|
|
1540
1592
|
function groupValidatorErrors(errors, blocks) {
|
|
1541
1593
|
const byBlockId = /* @__PURE__ */ new Map();
|
|
1542
1594
|
const general = [];
|
|
1543
1595
|
for (const raw of errors) {
|
|
1544
|
-
const
|
|
1545
|
-
const path = space === -1 ? "(root)" : raw.slice(0, space);
|
|
1546
|
-
const message = space === -1 ? raw : raw.slice(space + 1);
|
|
1547
|
-
const idx = extractBlockIndex(path);
|
|
1596
|
+
const idx = extractBlockIndex(raw);
|
|
1548
1597
|
if (idx === null || idx >= blocks.length) {
|
|
1549
|
-
general.push(raw);
|
|
1598
|
+
general.push(toGeneralMessage(raw));
|
|
1550
1599
|
continue;
|
|
1551
1600
|
}
|
|
1552
1601
|
const id = blocks[idx].id;
|
|
1553
|
-
const friendly = formatMessage(stripBlockPrefix(path), message);
|
|
1554
1602
|
const bucket = byBlockId.get(id) ?? [];
|
|
1555
|
-
bucket.push(
|
|
1603
|
+
bucket.push(toBlockMessage(raw));
|
|
1556
1604
|
byBlockId.set(id, bucket);
|
|
1557
1605
|
}
|
|
1558
1606
|
return { byBlockId, general, total: errors.length };
|
|
1559
1607
|
}
|
|
1560
|
-
function formatMessage(relativePath, message) {
|
|
1561
|
-
if (relativePath === "" || relativePath === "(root)") {
|
|
1562
|
-
return message;
|
|
1563
|
-
}
|
|
1564
|
-
return `${relativePath}: ${message}`;
|
|
1565
|
-
}
|
|
1566
1608
|
|
|
1567
1609
|
// src/lib/url-safety.ts
|
|
1568
1610
|
var SAFE_LINK_PROTOCOLS = /^(https?|ircs?|mailto|tel|sms|xmpp)$/i;
|
|
@@ -1682,10 +1724,47 @@ function toSlackBlocks(blocks) {
|
|
|
1682
1724
|
});
|
|
1683
1725
|
}
|
|
1684
1726
|
|
|
1727
|
+
// src/lib/validation-sanitize.ts
|
|
1728
|
+
var VALIDATION_EMOJI_PLACEHOLDER = "white_check_mark";
|
|
1729
|
+
function rewriteEmojiNames(value) {
|
|
1730
|
+
if (Array.isArray(value)) {
|
|
1731
|
+
let changed = false;
|
|
1732
|
+
const next = value.map((item) => {
|
|
1733
|
+
const rewritten = rewriteEmojiNames(item);
|
|
1734
|
+
if (rewritten !== item) {
|
|
1735
|
+
changed = true;
|
|
1736
|
+
}
|
|
1737
|
+
return rewritten;
|
|
1738
|
+
});
|
|
1739
|
+
return changed ? next : value;
|
|
1740
|
+
}
|
|
1741
|
+
if (value !== null && typeof value === "object") {
|
|
1742
|
+
const source = value;
|
|
1743
|
+
let changed = false;
|
|
1744
|
+
const next = {};
|
|
1745
|
+
for (const key of Object.keys(source)) {
|
|
1746
|
+
const rewritten = rewriteEmojiNames(source[key]);
|
|
1747
|
+
next[key] = rewritten;
|
|
1748
|
+
if (rewritten !== source[key]) {
|
|
1749
|
+
changed = true;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
if (next.type === "emoji" && typeof next.name === "string" && next.name !== VALIDATION_EMOJI_PLACEHOLDER) {
|
|
1753
|
+
next.name = VALIDATION_EMOJI_PLACEHOLDER;
|
|
1754
|
+
changed = true;
|
|
1755
|
+
}
|
|
1756
|
+
return changed ? next : value;
|
|
1757
|
+
}
|
|
1758
|
+
return value;
|
|
1759
|
+
}
|
|
1760
|
+
function stripCustomEmojiForValidation(blocks) {
|
|
1761
|
+
return blocks.map((block) => rewriteEmojiNames(block));
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1685
1764
|
// src/state/use-block-kit-validation.ts
|
|
1686
1765
|
var DEBOUNCE_MS = 150;
|
|
1687
1766
|
function computeValidation(blocks, surface) {
|
|
1688
|
-
const payload = toSlackBlocks(blocks.map((b) => b.block));
|
|
1767
|
+
const payload = stripCustomEmojiForValidation(toSlackBlocks(blocks.map((b) => b.block)));
|
|
1689
1768
|
const result = validateBlockKit(payload, {
|
|
1690
1769
|
target: "blocks",
|
|
1691
1770
|
surface: toValidatorSurface(surface)
|
|
@@ -3531,52 +3610,55 @@ function ContextEditor({ block, onChange }) {
|
|
|
3531
3610
|
{
|
|
3532
3611
|
label: "Items",
|
|
3533
3612
|
help: "Shown in a horizontal row, usually as footnote-style content. Slack supports up to 10 items.",
|
|
3534
|
-
children: /* @__PURE__ */
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3613
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
3614
|
+
elements.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed bg-muted/20 px-3 py-2 text-xs text-muted-foreground", children: "No items yet \u2014 a context block needs at least one. Add text or an image below, or delete the block if you don't need it." }) : null,
|
|
3615
|
+
elements.map((el, idx) => {
|
|
3616
|
+
if (el.type === "mrkdwn" || el.type === "plain_text") {
|
|
3617
|
+
return /* @__PURE__ */ jsx(
|
|
3618
|
+
TextItemCard,
|
|
3619
|
+
{
|
|
3620
|
+
index: idx,
|
|
3621
|
+
element: el,
|
|
3622
|
+
onChange: (next) => updateAt(idx, () => next),
|
|
3623
|
+
onRemove: () => removeAt(idx)
|
|
3624
|
+
},
|
|
3625
|
+
idx
|
|
3626
|
+
);
|
|
3627
|
+
}
|
|
3628
|
+
if (el.type === "image" && "image_url" in el) {
|
|
3629
|
+
return /* @__PURE__ */ jsx(
|
|
3630
|
+
ImageItemCard,
|
|
3631
|
+
{
|
|
3632
|
+
index: idx,
|
|
3633
|
+
element: el,
|
|
3634
|
+
onChange: (next) => updateAt(idx, () => next),
|
|
3635
|
+
onRemove: () => removeAt(idx)
|
|
3636
|
+
},
|
|
3637
|
+
idx
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
3640
|
+
return /* @__PURE__ */ jsxs(
|
|
3641
|
+
"div",
|
|
3550
3642
|
{
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3643
|
+
className: "flex items-center justify-between rounded border bg-muted/30 px-2 py-1.5 text-xs text-muted-foreground",
|
|
3644
|
+
children: [
|
|
3645
|
+
/* @__PURE__ */ jsx("span", { children: "Item not editable in the visual builder" }),
|
|
3646
|
+
/* @__PURE__ */ jsx(
|
|
3647
|
+
"button",
|
|
3648
|
+
{
|
|
3649
|
+
type: "button",
|
|
3650
|
+
"aria-label": "Remove item",
|
|
3651
|
+
onClick: () => removeAt(idx),
|
|
3652
|
+
className: "text-muted-foreground hover:text-destructive",
|
|
3653
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
3654
|
+
}
|
|
3655
|
+
)
|
|
3656
|
+
]
|
|
3555
3657
|
},
|
|
3556
3658
|
idx
|
|
3557
3659
|
);
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
"div",
|
|
3561
|
-
{
|
|
3562
|
-
className: "flex items-center justify-between rounded border bg-muted/30 px-2 py-1.5 text-xs text-muted-foreground",
|
|
3563
|
-
children: [
|
|
3564
|
-
/* @__PURE__ */ jsx("span", { children: "Item not editable in the visual builder" }),
|
|
3565
|
-
/* @__PURE__ */ jsx(
|
|
3566
|
-
"button",
|
|
3567
|
-
{
|
|
3568
|
-
type: "button",
|
|
3569
|
-
"aria-label": "Remove item",
|
|
3570
|
-
onClick: () => removeAt(idx),
|
|
3571
|
-
className: "text-muted-foreground hover:text-destructive",
|
|
3572
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
3573
|
-
}
|
|
3574
|
-
)
|
|
3575
|
-
]
|
|
3576
|
-
},
|
|
3577
|
-
idx
|
|
3578
|
-
);
|
|
3579
|
-
}) })
|
|
3660
|
+
})
|
|
3661
|
+
] })
|
|
3580
3662
|
}
|
|
3581
3663
|
),
|
|
3582
3664
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -3715,12 +3797,423 @@ function ImageItemCard({
|
|
|
3715
3797
|
function DividerEditor() {
|
|
3716
3798
|
return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "A horizontal line that separates content. This block has no options to configure." });
|
|
3717
3799
|
}
|
|
3800
|
+
|
|
3801
|
+
// src/lib/emoji-data.ts
|
|
3802
|
+
var TONE_CODEPOINT_TO_SKIN_TONE = {
|
|
3803
|
+
"1F3FB": 2,
|
|
3804
|
+
"1F3FC": 3,
|
|
3805
|
+
"1F3FD": 4,
|
|
3806
|
+
"1F3FE": 5,
|
|
3807
|
+
"1F3FF": 6
|
|
3808
|
+
};
|
|
3809
|
+
var CATEGORY_ORDER = [
|
|
3810
|
+
"Smileys & Emotion",
|
|
3811
|
+
"People & Body",
|
|
3812
|
+
"Animals & Nature",
|
|
3813
|
+
"Food & Drink",
|
|
3814
|
+
"Travel & Places",
|
|
3815
|
+
"Activities",
|
|
3816
|
+
"Objects",
|
|
3817
|
+
"Symbols",
|
|
3818
|
+
"Flags"
|
|
3819
|
+
];
|
|
3820
|
+
function codepointsToGlyph(unified) {
|
|
3821
|
+
return unified.split("-").map((cp) => {
|
|
3822
|
+
const n = Number.parseInt(cp, 16);
|
|
3823
|
+
return Number.isFinite(n) ? String.fromCodePoint(n) : "";
|
|
3824
|
+
}).join("");
|
|
3825
|
+
}
|
|
3826
|
+
function unifiedToUnicode(unified) {
|
|
3827
|
+
return unified.toLowerCase();
|
|
3828
|
+
}
|
|
3829
|
+
function composeTextEmoji(name, skinTone) {
|
|
3830
|
+
if (skinTone && skinTone >= 2 && skinTone <= 6) {
|
|
3831
|
+
return `:${name}::skin-tone-${skinTone}:`;
|
|
3832
|
+
}
|
|
3833
|
+
return `:${name}:`;
|
|
3834
|
+
}
|
|
3835
|
+
function buildEmojiIndex(raw) {
|
|
3836
|
+
const all = [];
|
|
3837
|
+
const byName = /* @__PURE__ */ new Map();
|
|
3838
|
+
const byUnified = /* @__PURE__ */ new Map();
|
|
3839
|
+
for (const r of raw) {
|
|
3840
|
+
if (!CATEGORY_ORDER.includes(r.category)) {
|
|
3841
|
+
continue;
|
|
3842
|
+
}
|
|
3843
|
+
const shortNames = r.short_names?.length ? r.short_names : [r.short_name];
|
|
3844
|
+
const skinUnified = {};
|
|
3845
|
+
if (r.skin_variations) {
|
|
3846
|
+
for (const [toneKey, variation] of Object.entries(r.skin_variations)) {
|
|
3847
|
+
const skinTone = TONE_CODEPOINT_TO_SKIN_TONE[toneKey];
|
|
3848
|
+
if (skinTone && variation?.unified) {
|
|
3849
|
+
skinUnified[skinTone] = variation.unified;
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
const datum = {
|
|
3854
|
+
name: shortNames[0],
|
|
3855
|
+
shortNames,
|
|
3856
|
+
unified: r.unified,
|
|
3857
|
+
category: r.category,
|
|
3858
|
+
sortOrder: r.sort_order,
|
|
3859
|
+
skinUnified
|
|
3860
|
+
};
|
|
3861
|
+
all.push(datum);
|
|
3862
|
+
byUnified.set(r.unified.toUpperCase(), datum);
|
|
3863
|
+
for (const sn of shortNames) {
|
|
3864
|
+
if (!byName.has(sn)) {
|
|
3865
|
+
byName.set(sn, datum);
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
all.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
3870
|
+
const byCategory = CATEGORY_ORDER.map((category) => ({
|
|
3871
|
+
category,
|
|
3872
|
+
emoji: all.filter((e) => e.category === category)
|
|
3873
|
+
})).filter((group) => group.emoji.length > 0);
|
|
3874
|
+
return { all, byName, byUnified, byCategory };
|
|
3875
|
+
}
|
|
3876
|
+
var indexPromise = null;
|
|
3877
|
+
var cachedIndex = null;
|
|
3878
|
+
async function loadEmojiIndex() {
|
|
3879
|
+
if (cachedIndex) {
|
|
3880
|
+
return cachedIndex;
|
|
3881
|
+
}
|
|
3882
|
+
if (!indexPromise) {
|
|
3883
|
+
indexPromise = import('emoji-datasource/emoji.json').then((mod) => {
|
|
3884
|
+
const raw = mod.default ?? mod;
|
|
3885
|
+
cachedIndex = buildEmojiIndex(raw);
|
|
3886
|
+
return cachedIndex;
|
|
3887
|
+
}).catch((err) => {
|
|
3888
|
+
indexPromise = null;
|
|
3889
|
+
throw err;
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
return indexPromise;
|
|
3893
|
+
}
|
|
3894
|
+
function getEmojiIndex() {
|
|
3895
|
+
return cachedIndex;
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
// src/lib/emoji-recents.ts
|
|
3899
|
+
var STORAGE_KEY = "bk:emoji-recents";
|
|
3900
|
+
var SKIN_TONE_KEY = "bk:emoji-skin-tone";
|
|
3901
|
+
var MAX_RECENTS = 24;
|
|
3902
|
+
function readEmojiSkinTone() {
|
|
3903
|
+
try {
|
|
3904
|
+
const raw = globalThis.localStorage?.getItem(SKIN_TONE_KEY);
|
|
3905
|
+
if (!raw) {
|
|
3906
|
+
return null;
|
|
3907
|
+
}
|
|
3908
|
+
const n = Number.parseInt(raw, 10);
|
|
3909
|
+
return n >= 2 && n <= 6 ? n : null;
|
|
3910
|
+
} catch {
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
function writeEmojiSkinTone(skinTone) {
|
|
3915
|
+
try {
|
|
3916
|
+
if (skinTone && skinTone >= 2 && skinTone <= 6) {
|
|
3917
|
+
globalThis.localStorage?.setItem(SKIN_TONE_KEY, String(skinTone));
|
|
3918
|
+
} else {
|
|
3919
|
+
globalThis.localStorage?.removeItem(SKIN_TONE_KEY);
|
|
3920
|
+
}
|
|
3921
|
+
} catch {
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
function readEmojiRecents() {
|
|
3925
|
+
try {
|
|
3926
|
+
const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
3927
|
+
if (!raw) {
|
|
3928
|
+
return [];
|
|
3929
|
+
}
|
|
3930
|
+
const parsed = JSON.parse(raw);
|
|
3931
|
+
return Array.isArray(parsed) ? parsed.filter((n) => typeof n === "string") : [];
|
|
3932
|
+
} catch {
|
|
3933
|
+
return [];
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
function pushEmojiRecent(name) {
|
|
3937
|
+
const next = [name, ...readEmojiRecents().filter((n) => n !== name)].slice(0, MAX_RECENTS);
|
|
3938
|
+
try {
|
|
3939
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
3940
|
+
} catch {
|
|
3941
|
+
}
|
|
3942
|
+
return next;
|
|
3943
|
+
}
|
|
3944
|
+
var SKIN_TONES = [
|
|
3945
|
+
{ tone: null, label: "Default", swatch: "\u270B" },
|
|
3946
|
+
{ tone: 2, label: "Light", swatch: "\u270B\u{1F3FB}" },
|
|
3947
|
+
{ tone: 3, label: "Medium-light", swatch: "\u270B\u{1F3FC}" },
|
|
3948
|
+
{ tone: 4, label: "Medium", swatch: "\u270B\u{1F3FD}" },
|
|
3949
|
+
{ tone: 5, label: "Medium-dark", swatch: "\u270B\u{1F3FE}" },
|
|
3950
|
+
{ tone: 6, label: "Dark", swatch: "\u270B\u{1F3FF}" }
|
|
3951
|
+
];
|
|
3952
|
+
function EmojiPicker({
|
|
3953
|
+
onSelect,
|
|
3954
|
+
autoFocus = true
|
|
3955
|
+
}) {
|
|
3956
|
+
const { customEmojis } = useCustomEmojis();
|
|
3957
|
+
const [index, setIndex] = useState(null);
|
|
3958
|
+
const [loadError, setLoadError] = useState(false);
|
|
3959
|
+
const [query, setQuery] = useState("");
|
|
3960
|
+
const [skinTone, setSkinTone] = useState(() => readEmojiSkinTone());
|
|
3961
|
+
const [recents, setRecents] = useState(() => readEmojiRecents());
|
|
3962
|
+
const searchRef = useRef(null);
|
|
3963
|
+
useEffect(() => {
|
|
3964
|
+
let active = true;
|
|
3965
|
+
loadEmojiIndex().then((idx) => {
|
|
3966
|
+
if (active) {
|
|
3967
|
+
setIndex(idx);
|
|
3968
|
+
}
|
|
3969
|
+
}).catch(() => {
|
|
3970
|
+
if (active) {
|
|
3971
|
+
setLoadError(true);
|
|
3972
|
+
}
|
|
3973
|
+
});
|
|
3974
|
+
return () => {
|
|
3975
|
+
active = false;
|
|
3976
|
+
};
|
|
3977
|
+
}, []);
|
|
3978
|
+
useEffect(() => {
|
|
3979
|
+
if (autoFocus) {
|
|
3980
|
+
searchRef.current?.focus();
|
|
3981
|
+
}
|
|
3982
|
+
}, [autoFocus]);
|
|
3983
|
+
const customByName = useMemo(() => {
|
|
3984
|
+
const map = /* @__PURE__ */ new Map();
|
|
3985
|
+
for (const e of customEmojis) {
|
|
3986
|
+
map.set(e.name, e);
|
|
3987
|
+
}
|
|
3988
|
+
return map;
|
|
3989
|
+
}, [customEmojis]);
|
|
3990
|
+
const customImageEmoji = useMemo(() => customEmojis.filter((e) => e.url), [customEmojis]);
|
|
3991
|
+
const handlePick = (selection) => {
|
|
3992
|
+
setRecents(pushEmojiRecent(selection.name));
|
|
3993
|
+
onSelect(selection);
|
|
3994
|
+
};
|
|
3995
|
+
const handleSkinTone = (tone) => {
|
|
3996
|
+
setSkinTone(tone);
|
|
3997
|
+
writeEmojiSkinTone(tone);
|
|
3998
|
+
};
|
|
3999
|
+
const sections = useMemo(() => {
|
|
4000
|
+
if (!index) {
|
|
4001
|
+
return [];
|
|
4002
|
+
}
|
|
4003
|
+
const trimmed = query.trim().toLowerCase();
|
|
4004
|
+
if (trimmed) {
|
|
4005
|
+
const standard = index.all.filter((d) => d.shortNames.some((sn) => sn.includes(trimmed))).slice(0, 120).map((datum) => ({ datum }));
|
|
4006
|
+
const custom = customImageEmoji.filter((e) => e.name.toLowerCase().includes(trimmed)).map((custom2) => ({ custom: custom2 }));
|
|
4007
|
+
return [{ key: "search", title: "Search results", emoji: [...custom, ...standard] }];
|
|
4008
|
+
}
|
|
4009
|
+
const out = [];
|
|
4010
|
+
if (recents.length > 0) {
|
|
4011
|
+
const recentEmoji = recents.map((name) => {
|
|
4012
|
+
const custom = customByName.get(name);
|
|
4013
|
+
if (custom?.url) {
|
|
4014
|
+
return { custom };
|
|
4015
|
+
}
|
|
4016
|
+
const datum = index.byName.get(name);
|
|
4017
|
+
return datum ? { datum } : null;
|
|
4018
|
+
}).filter((e) => e !== null);
|
|
4019
|
+
if (recentEmoji.length > 0) {
|
|
4020
|
+
out.push({ key: "recents", title: "Frequently used", emoji: recentEmoji });
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
if (customImageEmoji.length > 0) {
|
|
4024
|
+
out.push({ key: "custom", title: "Custom", emoji: customImageEmoji.map((custom) => ({ custom })) });
|
|
4025
|
+
}
|
|
4026
|
+
for (const group of index.byCategory) {
|
|
4027
|
+
out.push({ key: group.category, title: group.category, emoji: group.emoji.map((datum) => ({ datum })) });
|
|
4028
|
+
}
|
|
4029
|
+
return out;
|
|
4030
|
+
}, [index, query, recents, customByName, customImageEmoji]);
|
|
4031
|
+
return /* @__PURE__ */ jsxs("div", { className: "bk-emoji-picker flex w-[320px] max-w-[90vw] flex-col gap-2", children: [
|
|
4032
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4033
|
+
/* @__PURE__ */ jsx(
|
|
4034
|
+
Input,
|
|
4035
|
+
{
|
|
4036
|
+
ref: searchRef,
|
|
4037
|
+
value: query,
|
|
4038
|
+
placeholder: "Search emoji\u2026",
|
|
4039
|
+
"aria-label": "Search emoji",
|
|
4040
|
+
onChange: (e) => setQuery(e.target.value),
|
|
4041
|
+
className: "h-8"
|
|
4042
|
+
}
|
|
4043
|
+
),
|
|
4044
|
+
/* @__PURE__ */ jsx(SkinToneSelector, { value: skinTone, onChange: handleSkinTone })
|
|
4045
|
+
] }),
|
|
4046
|
+
/* @__PURE__ */ jsx("div", { className: "h-64 overflow-y-auto pr-1", children: loadError ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-xs text-muted-foreground", children: "Couldn't load emoji." }) : !index ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-xs text-muted-foreground", children: "Loading emoji\u2026" }) : sections.every((s) => s.emoji.length === 0) ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-xs text-muted-foreground", children: "No emoji found." }) : sections.map((section) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
|
|
4047
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-background px-1 py-1 text-[11px] font-medium text-muted-foreground", children: section.title }),
|
|
4048
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-8 gap-0.5", children: section.emoji.map(
|
|
4049
|
+
(entry, i) => entry.custom ? /* @__PURE__ */ jsx(
|
|
4050
|
+
CustomEmojiButton,
|
|
4051
|
+
{
|
|
4052
|
+
emoji: entry.custom,
|
|
4053
|
+
onPick: handlePick
|
|
4054
|
+
},
|
|
4055
|
+
`${section.key}-${entry.custom.name}-${i}`
|
|
4056
|
+
) : entry.datum ? /* @__PURE__ */ jsx(
|
|
4057
|
+
StandardEmojiButton,
|
|
4058
|
+
{
|
|
4059
|
+
datum: entry.datum,
|
|
4060
|
+
skinTone,
|
|
4061
|
+
onPick: handlePick
|
|
4062
|
+
},
|
|
4063
|
+
`${section.key}-${entry.datum.name}-${i}`
|
|
4064
|
+
) : null
|
|
4065
|
+
) })
|
|
4066
|
+
] }, section.key)) })
|
|
4067
|
+
] });
|
|
4068
|
+
}
|
|
4069
|
+
function resolveStandard(datum, skinTone) {
|
|
4070
|
+
const tonedUnified = skinTone ? datum.skinUnified[skinTone] : void 0;
|
|
4071
|
+
return {
|
|
4072
|
+
name: datum.name,
|
|
4073
|
+
unicode: unifiedToUnicode(tonedUnified ?? datum.unified),
|
|
4074
|
+
src: null,
|
|
4075
|
+
skinTone: tonedUnified ? skinTone : null
|
|
4076
|
+
};
|
|
4077
|
+
}
|
|
4078
|
+
var emojiButtonClass = "flex h-8 w-8 cursor-pointer items-center justify-center rounded text-lg leading-none hover:bg-accent";
|
|
4079
|
+
function StandardEmojiButton({
|
|
4080
|
+
datum,
|
|
4081
|
+
skinTone,
|
|
4082
|
+
onPick
|
|
4083
|
+
}) {
|
|
4084
|
+
const tonedUnified = skinTone && datum.skinUnified[skinTone] || datum.unified;
|
|
4085
|
+
return /* @__PURE__ */ jsx(
|
|
4086
|
+
"button",
|
|
4087
|
+
{
|
|
4088
|
+
type: "button",
|
|
4089
|
+
className: emojiButtonClass,
|
|
4090
|
+
title: `:${datum.name}:`,
|
|
4091
|
+
"aria-label": datum.name,
|
|
4092
|
+
onClick: () => onPick(resolveStandard(datum, skinTone)),
|
|
4093
|
+
children: codepointsToGlyph(tonedUnified)
|
|
4094
|
+
}
|
|
4095
|
+
);
|
|
4096
|
+
}
|
|
4097
|
+
function CustomEmojiButton({ emoji, onPick }) {
|
|
4098
|
+
return /* @__PURE__ */ jsx(
|
|
4099
|
+
"button",
|
|
4100
|
+
{
|
|
4101
|
+
type: "button",
|
|
4102
|
+
className: emojiButtonClass,
|
|
4103
|
+
title: `:${emoji.name}:`,
|
|
4104
|
+
"aria-label": emoji.name,
|
|
4105
|
+
onClick: () => onPick({ name: emoji.name, unicode: null, src: emoji.url, skinTone: null }),
|
|
4106
|
+
children: emoji.url ? /* @__PURE__ */ jsx("img", { src: emoji.url, alt: `:${emoji.name}:`, className: "h-5 w-5 object-contain" }) : /* @__PURE__ */ jsxs("span", { className: "text-[10px]", children: [
|
|
4107
|
+
":",
|
|
4108
|
+
emoji.name,
|
|
4109
|
+
":"
|
|
4110
|
+
] })
|
|
4111
|
+
}
|
|
4112
|
+
);
|
|
4113
|
+
}
|
|
4114
|
+
function SkinToneSelector({ value, onChange }) {
|
|
4115
|
+
const [open, setOpen] = useState(false);
|
|
4116
|
+
const active = SKIN_TONES.find((t) => t.tone === value) ?? SKIN_TONES[0];
|
|
4117
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", children: [
|
|
4118
|
+
/* @__PURE__ */ jsx(
|
|
4119
|
+
"button",
|
|
4120
|
+
{
|
|
4121
|
+
type: "button",
|
|
4122
|
+
"aria-label": `Skin tone: ${active.label}`,
|
|
4123
|
+
title: `Skin tone: ${active.label}`,
|
|
4124
|
+
className: "flex h-8 w-8 items-center justify-center rounded border text-lg leading-none hover:bg-accent",
|
|
4125
|
+
onClick: () => setOpen((o) => !o),
|
|
4126
|
+
children: active.swatch
|
|
4127
|
+
}
|
|
4128
|
+
),
|
|
4129
|
+
open && /* @__PURE__ */ jsx("div", { className: "absolute right-0 z-20 mt-1 flex gap-0.5 rounded-md border bg-background p-1 shadow-md", children: SKIN_TONES.map((t) => /* @__PURE__ */ jsx(
|
|
4130
|
+
"button",
|
|
4131
|
+
{
|
|
4132
|
+
type: "button",
|
|
4133
|
+
"aria-label": t.label,
|
|
4134
|
+
title: t.label,
|
|
4135
|
+
className: cn(emojiButtonClass, value === t.tone && "bg-accent"),
|
|
4136
|
+
onClick: () => {
|
|
4137
|
+
onChange(t.tone);
|
|
4138
|
+
setOpen(false);
|
|
4139
|
+
},
|
|
4140
|
+
children: t.swatch
|
|
4141
|
+
},
|
|
4142
|
+
t.label
|
|
4143
|
+
)) })
|
|
4144
|
+
] });
|
|
4145
|
+
}
|
|
4146
|
+
function EmojiPickerButton({
|
|
4147
|
+
onSelect,
|
|
4148
|
+
className,
|
|
4149
|
+
label = "Insert emoji",
|
|
4150
|
+
align = "start"
|
|
4151
|
+
}) {
|
|
4152
|
+
const [open, setOpen] = useState(false);
|
|
4153
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
4154
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
4155
|
+
"button",
|
|
4156
|
+
{
|
|
4157
|
+
type: "button",
|
|
4158
|
+
"aria-label": label,
|
|
4159
|
+
title: label,
|
|
4160
|
+
className: cn(
|
|
4161
|
+
"flex h-7 w-7 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
4162
|
+
className
|
|
4163
|
+
),
|
|
4164
|
+
children: /* @__PURE__ */ jsx(Smile, { className: "h-3.5 w-3.5" })
|
|
4165
|
+
}
|
|
4166
|
+
) }),
|
|
4167
|
+
/* @__PURE__ */ jsx(PopoverContent, { align, className: "w-auto p-2", children: /* @__PURE__ */ jsx(
|
|
4168
|
+
EmojiPicker,
|
|
4169
|
+
{
|
|
4170
|
+
onSelect: (selection) => {
|
|
4171
|
+
onSelect(selection);
|
|
4172
|
+
setOpen(false);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
) })
|
|
4176
|
+
] });
|
|
4177
|
+
}
|
|
4178
|
+
function EmojiTextInsertButton({
|
|
4179
|
+
targetRef,
|
|
4180
|
+
value,
|
|
4181
|
+
onChange,
|
|
4182
|
+
className,
|
|
4183
|
+
align = "end"
|
|
4184
|
+
}) {
|
|
4185
|
+
return /* @__PURE__ */ jsx(
|
|
4186
|
+
EmojiPickerButton,
|
|
4187
|
+
{
|
|
4188
|
+
align,
|
|
4189
|
+
label: "Insert emoji",
|
|
4190
|
+
className,
|
|
4191
|
+
onSelect: (sel) => {
|
|
4192
|
+
const el = targetRef.current;
|
|
4193
|
+
const insert = composeTextEmoji(sel.name, sel.skinTone ?? void 0);
|
|
4194
|
+
const start = el?.selectionStart ?? value.length;
|
|
4195
|
+
const end = el?.selectionEnd ?? value.length;
|
|
4196
|
+
const next = value.slice(0, start) + insert + value.slice(end);
|
|
4197
|
+
onChange(next);
|
|
4198
|
+
requestAnimationFrame(() => {
|
|
4199
|
+
if (el) {
|
|
4200
|
+
const caret = start + insert.length;
|
|
4201
|
+
el.focus();
|
|
4202
|
+
el.setSelectionRange(caret, caret);
|
|
4203
|
+
}
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
3718
4209
|
var HEADER_MAX = 150;
|
|
3719
4210
|
var HEADER_LEVELS = [1, 2, 3, 4];
|
|
3720
4211
|
function HeaderEditor({ block, onChange }) {
|
|
3721
4212
|
const text = block.text?.text ?? "";
|
|
3722
4213
|
const remaining = HEADER_MAX - text.length;
|
|
3723
4214
|
const level = block.level;
|
|
4215
|
+
const inputRef = useRef(null);
|
|
4216
|
+
const setText = (next) => onChange({ ...block, text: { type: "plain_text", text: next, emoji: true } });
|
|
3724
4217
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
3725
4218
|
/* @__PURE__ */ jsx(
|
|
3726
4219
|
EditorField,
|
|
@@ -3728,23 +4221,20 @@ function HeaderEditor({ block, onChange }) {
|
|
|
3728
4221
|
label: "Heading text",
|
|
3729
4222
|
help: `Plain text only. ${remaining} characters remaining.`,
|
|
3730
4223
|
htmlFor: "header-text",
|
|
3731
|
-
children: /* @__PURE__ */
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
})
|
|
3746
|
-
}
|
|
3747
|
-
)
|
|
4224
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
4225
|
+
/* @__PURE__ */ jsx(
|
|
4226
|
+
Input,
|
|
4227
|
+
{
|
|
4228
|
+
ref: inputRef,
|
|
4229
|
+
id: "header-text",
|
|
4230
|
+
value: text,
|
|
4231
|
+
maxLength: HEADER_MAX,
|
|
4232
|
+
placeholder: "e.g. Weekly roundup",
|
|
4233
|
+
onChange: (e) => setText(e.target.value)
|
|
4234
|
+
}
|
|
4235
|
+
),
|
|
4236
|
+
/* @__PURE__ */ jsx(EmojiTextInsertButton, { targetRef: inputRef, value: text, onChange: setText, className: "shrink-0 border" })
|
|
4237
|
+
] })
|
|
3748
4238
|
}
|
|
3749
4239
|
),
|
|
3750
4240
|
/* @__PURE__ */ jsx(EditorField, { label: "Level", help: "Optional. Builder-only extension; Slack's API ignores this value.", children: /* @__PURE__ */ jsxs(
|
|
@@ -4607,22 +5097,29 @@ function CheckboxField({
|
|
|
4607
5097
|
] });
|
|
4608
5098
|
}
|
|
4609
5099
|
function MarkdownEditor({ block, onChange }) {
|
|
5100
|
+
const textRef = useRef(null);
|
|
5101
|
+
const text = block.text ?? "";
|
|
5102
|
+
const setText = (next) => onChange({ ...block, text: next });
|
|
4610
5103
|
return /* @__PURE__ */ jsx(
|
|
4611
5104
|
EditorField,
|
|
4612
5105
|
{
|
|
4613
5106
|
label: "Markdown",
|
|
4614
5107
|
help: "Standard markdown with GFM: **bold**, _italic_, lists, tables, ```code```, [links](url).",
|
|
4615
5108
|
htmlFor: "markdown-text",
|
|
4616
|
-
children: /* @__PURE__ */
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
5109
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5", children: [
|
|
5110
|
+
/* @__PURE__ */ jsx(
|
|
5111
|
+
Textarea,
|
|
5112
|
+
{
|
|
5113
|
+
ref: textRef,
|
|
5114
|
+
id: "markdown-text",
|
|
5115
|
+
value: text,
|
|
5116
|
+
rows: 8,
|
|
5117
|
+
placeholder: "e.g. **Roadmap**\\n\\n- Item one\\n- Item two",
|
|
5118
|
+
onChange: (e) => setText(e.target.value)
|
|
5119
|
+
}
|
|
5120
|
+
),
|
|
5121
|
+
/* @__PURE__ */ jsx(EmojiTextInsertButton, { targetRef: textRef, value: text, onChange: setText, className: "mt-1 shrink-0 border" })
|
|
5122
|
+
] })
|
|
4626
5123
|
}
|
|
4627
5124
|
);
|
|
4628
5125
|
}
|
|
@@ -4717,6 +5214,26 @@ function PlanEditor({ block, onChange }) {
|
|
|
4717
5214
|
] });
|
|
4718
5215
|
}
|
|
4719
5216
|
|
|
5217
|
+
// src/lib/emoji-resolve.ts
|
|
5218
|
+
function makeEmojiImportResolver(customByName, index = getEmojiIndex()) {
|
|
5219
|
+
return (el) => {
|
|
5220
|
+
const name = el.name ?? "";
|
|
5221
|
+
const skinTone = el.skin_tone ?? null;
|
|
5222
|
+
const custom = customByName.get(name);
|
|
5223
|
+
if (custom?.url) {
|
|
5224
|
+
return { name, src: custom.url, unicode: null, skinTone };
|
|
5225
|
+
}
|
|
5226
|
+
const lookupName = custom?.alias ?? name;
|
|
5227
|
+
const datum = index?.byName.get(lookupName);
|
|
5228
|
+
let unicode = el.unicode ?? null;
|
|
5229
|
+
if (!unicode && datum) {
|
|
5230
|
+
const toned = skinTone ? datum.skinUnified[skinTone] : void 0;
|
|
5231
|
+
unicode = unifiedToUnicode(toned ?? datum.unified);
|
|
5232
|
+
}
|
|
5233
|
+
return { name, src: null, unicode, skinTone };
|
|
5234
|
+
};
|
|
5235
|
+
}
|
|
5236
|
+
|
|
4720
5237
|
// src/lib/rich-text-tiptap.ts
|
|
4721
5238
|
var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
|
|
4722
5239
|
"rich_text_section",
|
|
@@ -4724,7 +5241,7 @@ var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
|
|
|
4724
5241
|
"rich_text_quote",
|
|
4725
5242
|
"rich_text_preformatted"
|
|
4726
5243
|
]);
|
|
4727
|
-
var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link"]);
|
|
5244
|
+
var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji"]);
|
|
4728
5245
|
var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "code"]);
|
|
4729
5246
|
var MAX_LIST_INDENT = 8;
|
|
4730
5247
|
function detectLossy(block) {
|
|
@@ -4771,16 +5288,22 @@ function scanInlines(inlines, where, out) {
|
|
|
4771
5288
|
}
|
|
4772
5289
|
});
|
|
4773
5290
|
}
|
|
4774
|
-
|
|
5291
|
+
var defaultEmojiResolver = (el) => ({
|
|
5292
|
+
name: el.name ?? "",
|
|
5293
|
+
src: null,
|
|
5294
|
+
unicode: el.unicode ?? null,
|
|
5295
|
+
skinTone: el.skin_tone ?? null
|
|
5296
|
+
});
|
|
5297
|
+
function richTextToProseMirror(block, resolveEmoji = defaultEmojiResolver) {
|
|
4775
5298
|
const content = [];
|
|
4776
5299
|
const listsByIndent = /* @__PURE__ */ new Map();
|
|
4777
5300
|
for (const el of block.elements ?? []) {
|
|
4778
5301
|
if (el.type === "rich_text_list") {
|
|
4779
|
-
appendRichTextList(el, content, listsByIndent);
|
|
5302
|
+
appendRichTextList(el, content, listsByIndent, resolveEmoji);
|
|
4780
5303
|
continue;
|
|
4781
5304
|
}
|
|
4782
5305
|
listsByIndent.clear();
|
|
4783
|
-
const node = blockElementToPM(el);
|
|
5306
|
+
const node = blockElementToPM(el, resolveEmoji);
|
|
4784
5307
|
if (node) {
|
|
4785
5308
|
content.push(node);
|
|
4786
5309
|
}
|
|
@@ -4790,12 +5313,12 @@ function richTextToProseMirror(block) {
|
|
|
4790
5313
|
content: content.length > 0 ? content : [{ type: "paragraph" }]
|
|
4791
5314
|
};
|
|
4792
5315
|
}
|
|
4793
|
-
function appendRichTextList(el, topLevel, listsByIndent) {
|
|
5316
|
+
function appendRichTextList(el, topLevel, listsByIndent, resolveEmoji) {
|
|
4794
5317
|
const indent = el.indent ?? 0;
|
|
4795
5318
|
const listType = el.style === "ordered" ? "orderedList" : "bulletList";
|
|
4796
5319
|
const items = (el.elements ?? []).map((section) => ({
|
|
4797
5320
|
type: "listItem",
|
|
4798
|
-
content: [{ type: "paragraph", content: inlinesToPM(section.elements ?? []) }]
|
|
5321
|
+
content: [{ type: "paragraph", content: inlinesToPM(section.elements ?? [], resolveEmoji) }]
|
|
4799
5322
|
}));
|
|
4800
5323
|
for (const k of Array.from(listsByIndent.keys())) {
|
|
4801
5324
|
if (k > indent) {
|
|
@@ -4821,11 +5344,11 @@ function appendRichTextList(el, topLevel, listsByIndent) {
|
|
|
4821
5344
|
const parentLastItem = parent.content[parent.content.length - 1];
|
|
4822
5345
|
parentLastItem.content = [...parentLastItem.content ?? [], list];
|
|
4823
5346
|
}
|
|
4824
|
-
function blockElementToPM(el) {
|
|
5347
|
+
function blockElementToPM(el, resolveEmoji) {
|
|
4825
5348
|
if (el.type === "rich_text_section") {
|
|
4826
5349
|
return {
|
|
4827
5350
|
type: "paragraph",
|
|
4828
|
-
content: inlinesToPM(el.elements ?? [])
|
|
5351
|
+
content: inlinesToPM(el.elements ?? [], resolveEmoji)
|
|
4829
5352
|
};
|
|
4830
5353
|
}
|
|
4831
5354
|
if (el.type === "rich_text_quote") {
|
|
@@ -4834,7 +5357,7 @@ function blockElementToPM(el) {
|
|
|
4834
5357
|
content: [
|
|
4835
5358
|
{
|
|
4836
5359
|
type: "paragraph",
|
|
4837
|
-
content: inlinesToPM(el.elements ?? [])
|
|
5360
|
+
content: inlinesToPM(el.elements ?? [], resolveEmoji)
|
|
4838
5361
|
}
|
|
4839
5362
|
]
|
|
4840
5363
|
};
|
|
@@ -4849,7 +5372,7 @@ function blockElementToPM(el) {
|
|
|
4849
5372
|
}
|
|
4850
5373
|
return null;
|
|
4851
5374
|
}
|
|
4852
|
-
function inlinesToPM(inlines) {
|
|
5375
|
+
function inlinesToPM(inlines, resolveEmoji) {
|
|
4853
5376
|
const out = [];
|
|
4854
5377
|
for (const inline of inlines) {
|
|
4855
5378
|
if (inline.type === "text") {
|
|
@@ -4858,6 +5381,12 @@ function inlinesToPM(inlines) {
|
|
|
4858
5381
|
}
|
|
4859
5382
|
const marks = styleToMarks(inline.style);
|
|
4860
5383
|
out.push({ type: "text", text: inline.text, marks });
|
|
5384
|
+
} else if (inline.type === "emoji") {
|
|
5385
|
+
const attrs = resolveEmoji(inline);
|
|
5386
|
+
if (!attrs.name) {
|
|
5387
|
+
continue;
|
|
5388
|
+
}
|
|
5389
|
+
out.push({ type: "emoji", attrs });
|
|
4861
5390
|
} else if (inline.type === "link") {
|
|
4862
5391
|
const link = inline;
|
|
4863
5392
|
const text = link.text || link.url || "";
|
|
@@ -4975,6 +5504,19 @@ function flattenList(listNode, indent, out) {
|
|
|
4975
5504
|
function proseMirrorInlinesToRichTextElements(nodes) {
|
|
4976
5505
|
const out = [];
|
|
4977
5506
|
for (const node of nodes) {
|
|
5507
|
+
if (node.type === "emoji") {
|
|
5508
|
+
const name = String(node.attrs?.name ?? "");
|
|
5509
|
+
if (!name) {
|
|
5510
|
+
continue;
|
|
5511
|
+
}
|
|
5512
|
+
const emoji = { type: "emoji", name };
|
|
5513
|
+
const skinTone = node.attrs?.skinTone;
|
|
5514
|
+
if (typeof skinTone === "number" && skinTone >= 2 && skinTone <= 6) {
|
|
5515
|
+
emoji.skin_tone = skinTone;
|
|
5516
|
+
}
|
|
5517
|
+
out.push(emoji);
|
|
5518
|
+
continue;
|
|
5519
|
+
}
|
|
4978
5520
|
if (node.type !== "text" || !node.text) {
|
|
4979
5521
|
continue;
|
|
4980
5522
|
}
|
|
@@ -5045,6 +5587,49 @@ function sameStyle(a, b) {
|
|
|
5045
5587
|
}
|
|
5046
5588
|
return ak.every((k, i) => k === bk[i]);
|
|
5047
5589
|
}
|
|
5590
|
+
function EmojiNodeView({ node }) {
|
|
5591
|
+
const { name, src, unicode } = node.attrs;
|
|
5592
|
+
const label = `:${name}:`;
|
|
5593
|
+
return /* @__PURE__ */ jsx(NodeViewWrapper, { as: "span", className: "bk-emoji-node", "data-emoji": name, title: label, children: src ? /* @__PURE__ */ jsx(
|
|
5594
|
+
"img",
|
|
5595
|
+
{
|
|
5596
|
+
src,
|
|
5597
|
+
alt: label,
|
|
5598
|
+
style: {
|
|
5599
|
+
display: "inline-block",
|
|
5600
|
+
width: "1.25em",
|
|
5601
|
+
height: "1.25em",
|
|
5602
|
+
verticalAlign: "-0.25em",
|
|
5603
|
+
objectFit: "contain"
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
) : /* @__PURE__ */ jsx("span", { "aria-label": label, role: "img", children: unicode ? codepointsToGlyph(unicode) : label }) });
|
|
5607
|
+
}
|
|
5608
|
+
var EmojiNode = Node.create({
|
|
5609
|
+
name: "emoji",
|
|
5610
|
+
group: "inline",
|
|
5611
|
+
inline: true,
|
|
5612
|
+
atom: true,
|
|
5613
|
+
selectable: true,
|
|
5614
|
+
addAttributes() {
|
|
5615
|
+
return {
|
|
5616
|
+
name: { default: "" },
|
|
5617
|
+
src: { default: null },
|
|
5618
|
+
unicode: { default: null },
|
|
5619
|
+
skinTone: { default: null }
|
|
5620
|
+
};
|
|
5621
|
+
},
|
|
5622
|
+
parseHTML() {
|
|
5623
|
+
return [{ tag: "span[data-emoji]" }];
|
|
5624
|
+
},
|
|
5625
|
+
renderHTML({ HTMLAttributes }) {
|
|
5626
|
+
const name = HTMLAttributes.name ?? "";
|
|
5627
|
+
return ["span", mergeAttributes({ "data-emoji": name }, HTMLAttributes), `:${name}:`];
|
|
5628
|
+
},
|
|
5629
|
+
addNodeView() {
|
|
5630
|
+
return ReactNodeViewRenderer(EmojiNodeView);
|
|
5631
|
+
}
|
|
5632
|
+
});
|
|
5048
5633
|
var BLOCK_ELEMENT_LABEL = {
|
|
5049
5634
|
rich_text_section: "Section",
|
|
5050
5635
|
rich_text_list: "List",
|
|
@@ -5383,15 +5968,34 @@ function InlineFields({
|
|
|
5383
5968
|
}
|
|
5384
5969
|
if (kind === "emoji") {
|
|
5385
5970
|
const emoji = element;
|
|
5386
|
-
return /* @__PURE__ */ jsx(
|
|
5387
|
-
|
|
5971
|
+
return /* @__PURE__ */ jsx(
|
|
5972
|
+
EditorField,
|
|
5388
5973
|
{
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5974
|
+
label: "Name",
|
|
5975
|
+
help: "Pick from the emoji list, or type a shortcode (without colons).",
|
|
5976
|
+
htmlFor: `${idPrefix}-emoji`,
|
|
5977
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
5978
|
+
/* @__PURE__ */ jsx(
|
|
5979
|
+
Input,
|
|
5980
|
+
{
|
|
5981
|
+
id: `${idPrefix}-emoji`,
|
|
5982
|
+
value: emoji.name ?? "",
|
|
5983
|
+
placeholder: "e.g. wave",
|
|
5984
|
+
onChange: (e) => onChange({ ...emoji, name: e.target.value })
|
|
5985
|
+
}
|
|
5986
|
+
),
|
|
5987
|
+
/* @__PURE__ */ jsx(
|
|
5988
|
+
EmojiPickerButton,
|
|
5989
|
+
{
|
|
5990
|
+
align: "end",
|
|
5991
|
+
label: "Pick emoji",
|
|
5992
|
+
className: "shrink-0 border",
|
|
5993
|
+
onSelect: (sel) => onChange({ ...emoji, name: sel.name, skin_tone: sel.skinTone ?? void 0, unicode: void 0 })
|
|
5994
|
+
}
|
|
5995
|
+
)
|
|
5996
|
+
] })
|
|
5393
5997
|
}
|
|
5394
|
-
)
|
|
5998
|
+
);
|
|
5395
5999
|
}
|
|
5396
6000
|
if (kind === "user") {
|
|
5397
6001
|
const user = element;
|
|
@@ -5602,6 +6206,20 @@ function LossyBanner({ reasons }) {
|
|
|
5602
6206
|
}
|
|
5603
6207
|
function RichTextWysiwygEditor({ block, onChange }) {
|
|
5604
6208
|
const lastEmittedRef = useRef(null);
|
|
6209
|
+
const { byName: customByName } = useCustomEmojis();
|
|
6210
|
+
const [emojiIndex, setEmojiIndex] = useState(null);
|
|
6211
|
+
useEffect(() => {
|
|
6212
|
+
let active = true;
|
|
6213
|
+
loadEmojiIndex().then((idx) => active && setEmojiIndex(idx)).catch(() => {
|
|
6214
|
+
});
|
|
6215
|
+
return () => {
|
|
6216
|
+
active = false;
|
|
6217
|
+
};
|
|
6218
|
+
}, []);
|
|
6219
|
+
const resolveEmoji = useMemo(
|
|
6220
|
+
() => makeEmojiImportResolver(customByName, emojiIndex),
|
|
6221
|
+
[customByName, emojiIndex]
|
|
6222
|
+
);
|
|
5605
6223
|
const handleUpdate = useCallback(
|
|
5606
6224
|
(editor2) => {
|
|
5607
6225
|
const next = proseMirrorToRichText(editor2.getJSON());
|
|
@@ -5616,6 +6234,7 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
5616
6234
|
heading: false,
|
|
5617
6235
|
horizontalRule: false
|
|
5618
6236
|
}),
|
|
6237
|
+
EmojiNode,
|
|
5619
6238
|
Link.configure({
|
|
5620
6239
|
openOnClick: false,
|
|
5621
6240
|
autolink: true,
|
|
@@ -5632,7 +6251,7 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
5632
6251
|
HTMLAttributes: { rel: "noreferrer noopener", target: "_blank" }
|
|
5633
6252
|
})
|
|
5634
6253
|
],
|
|
5635
|
-
content: richTextToProseMirror(block),
|
|
6254
|
+
content: richTextToProseMirror(block, resolveEmoji),
|
|
5636
6255
|
editorProps: {
|
|
5637
6256
|
attributes: {
|
|
5638
6257
|
class: "bk-rich-editor min-h-[120px] rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-ring [&_p]:my-1 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1 [&_ol]:pl-5 [&_li]:my-0 [&_pre]:bg-muted [&_pre]:rounded [&_pre]:p-2 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground"
|
|
@@ -5647,10 +6266,10 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
5647
6266
|
if (lastEmittedRef.current && JSON.stringify(lastEmittedRef.current) === JSON.stringify(block)) {
|
|
5648
6267
|
return;
|
|
5649
6268
|
}
|
|
5650
|
-
editor.commands.setContent(richTextToProseMirror(block), {
|
|
6269
|
+
editor.commands.setContent(richTextToProseMirror(block, resolveEmoji), {
|
|
5651
6270
|
emitUpdate: false
|
|
5652
6271
|
});
|
|
5653
|
-
}, [block, editor]);
|
|
6272
|
+
}, [block, editor, resolveEmoji]);
|
|
5654
6273
|
if (!editor) {
|
|
5655
6274
|
return /* @__PURE__ */ jsx("div", { className: "min-h-[120px] rounded-md border border-input bg-background" });
|
|
5656
6275
|
}
|
|
@@ -5699,6 +6318,16 @@ function Toolbar({ editor }) {
|
|
|
5699
6318
|
),
|
|
5700
6319
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
5701
6320
|
/* @__PURE__ */ jsx(LinkPopover, { editor }),
|
|
6321
|
+
/* @__PURE__ */ jsx(
|
|
6322
|
+
EmojiPickerButton,
|
|
6323
|
+
{
|
|
6324
|
+
align: "start",
|
|
6325
|
+
onSelect: (sel) => editor.chain().focus().insertContent({
|
|
6326
|
+
type: "emoji",
|
|
6327
|
+
attrs: { name: sel.name, src: sel.src, unicode: sel.unicode, skinTone: sel.skinTone }
|
|
6328
|
+
}).run()
|
|
6329
|
+
}
|
|
6330
|
+
),
|
|
5702
6331
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
5703
6332
|
/* @__PURE__ */ jsx(
|
|
5704
6333
|
ToolbarButton,
|
|
@@ -5897,6 +6526,8 @@ function defaultImage() {
|
|
|
5897
6526
|
function SectionEditor({ block, onChange }) {
|
|
5898
6527
|
const text = block.text?.text ?? "";
|
|
5899
6528
|
const accessoryKind = detectAccessory(block.accessory);
|
|
6529
|
+
const textRef = useRef(null);
|
|
6530
|
+
const setText = (next) => onChange({ ...block, text: { type: "mrkdwn", text: next } });
|
|
5900
6531
|
const setAccessoryKind = (next) => {
|
|
5901
6532
|
if (next === accessoryKind) {
|
|
5902
6533
|
return;
|
|
@@ -5920,19 +6551,20 @@ function SectionEditor({ block, onChange }) {
|
|
|
5920
6551
|
label: "Text",
|
|
5921
6552
|
help: "Supports *bold*, _italic_, ~strike~, `code`, and <url|link> formatting.",
|
|
5922
6553
|
htmlFor: "section-text",
|
|
5923
|
-
children: /* @__PURE__ */
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
}
|
|
5934
|
-
|
|
5935
|
-
|
|
6554
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5", children: [
|
|
6555
|
+
/* @__PURE__ */ jsx(
|
|
6556
|
+
Textarea,
|
|
6557
|
+
{
|
|
6558
|
+
ref: textRef,
|
|
6559
|
+
id: "section-text",
|
|
6560
|
+
value: text,
|
|
6561
|
+
rows: 4,
|
|
6562
|
+
placeholder: "e.g. Welcome to the channel! Ping <@U123> with any questions.",
|
|
6563
|
+
onChange: (e) => setText(e.target.value)
|
|
6564
|
+
}
|
|
6565
|
+
),
|
|
6566
|
+
/* @__PURE__ */ jsx(EmojiTextInsertButton, { targetRef: textRef, value: text, onChange: setText, className: "mt-1 shrink-0 border" })
|
|
6567
|
+
] })
|
|
5936
6568
|
}
|
|
5937
6569
|
),
|
|
5938
6570
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-md border bg-muted/20 p-3", children: [
|
|
@@ -5973,19 +6605,22 @@ function ButtonAccessoryFields({ button, onChange }) {
|
|
|
5973
6605
|
const url = button.url ?? "";
|
|
5974
6606
|
const value = button.value ?? "";
|
|
5975
6607
|
const style = button.style === "primary" || button.style === "danger" ? button.style : "default";
|
|
6608
|
+
const labelRef = useRef(null);
|
|
6609
|
+
const setLabel = (next) => onChange({ ...button, text: { type: "plain_text", text: next, emoji: true } });
|
|
5976
6610
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
5977
|
-
/* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: "section-acc-btn-label", children: /* @__PURE__ */
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
}
|
|
5987
|
-
|
|
5988
|
-
|
|
6611
|
+
/* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: "section-acc-btn-label", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
6612
|
+
/* @__PURE__ */ jsx(
|
|
6613
|
+
Input,
|
|
6614
|
+
{
|
|
6615
|
+
ref: labelRef,
|
|
6616
|
+
id: "section-acc-btn-label",
|
|
6617
|
+
value: label,
|
|
6618
|
+
placeholder: "e.g. Learn more",
|
|
6619
|
+
onChange: (e) => setLabel(e.target.value)
|
|
6620
|
+
}
|
|
6621
|
+
),
|
|
6622
|
+
/* @__PURE__ */ jsx(EmojiTextInsertButton, { targetRef: labelRef, value: label, onChange: setLabel, className: "shrink-0 border" })
|
|
6623
|
+
] }) }),
|
|
5989
6624
|
/* @__PURE__ */ jsx(
|
|
5990
6625
|
EditorField,
|
|
5991
6626
|
{
|
|
@@ -6207,10 +6842,14 @@ function TableEditor({ block, onChange }) {
|
|
|
6207
6842
|
] });
|
|
6208
6843
|
}
|
|
6209
6844
|
var STATUSES2 = ["pending", "in_progress", "complete", "error"];
|
|
6845
|
+
var EMPTY_RICH_TEXT = {
|
|
6846
|
+
type: "rich_text",
|
|
6847
|
+
elements: [{ type: "rich_text_section", elements: [{ type: "text", text: "" }] }]
|
|
6848
|
+
};
|
|
6210
6849
|
function TaskCardEditor({ block, onChange }) {
|
|
6211
6850
|
const status = block.status ?? "pending";
|
|
6212
6851
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
6213
|
-
/* @__PURE__ */ jsx(EditorField, { label: "Title", help: "
|
|
6852
|
+
/* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Title of the task in plain text.", htmlFor: "task-card-title", children: /* @__PURE__ */ jsx(
|
|
6214
6853
|
Input,
|
|
6215
6854
|
{
|
|
6216
6855
|
id: "task-card-title",
|
|
@@ -6219,24 +6858,16 @@ function TaskCardEditor({ block, onChange }) {
|
|
|
6219
6858
|
onChange: (e) => onChange({ ...block, title: e.target.value })
|
|
6220
6859
|
}
|
|
6221
6860
|
) }),
|
|
6222
|
-
/* @__PURE__ */ jsx(
|
|
6223
|
-
|
|
6861
|
+
/* @__PURE__ */ jsx(EditorField, { label: "Task ID", help: "ID for the task.", htmlFor: "task-card-id", children: /* @__PURE__ */ jsx(
|
|
6862
|
+
Input,
|
|
6224
6863
|
{
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
Input,
|
|
6230
|
-
{
|
|
6231
|
-
id: "task-card-id",
|
|
6232
|
-
value: block.task_id,
|
|
6233
|
-
placeholder: "e.g. task_1",
|
|
6234
|
-
onChange: (e) => onChange({ ...block, task_id: e.target.value })
|
|
6235
|
-
}
|
|
6236
|
-
)
|
|
6864
|
+
id: "task-card-id",
|
|
6865
|
+
value: block.task_id,
|
|
6866
|
+
placeholder: "e.g. task_1",
|
|
6867
|
+
onChange: (e) => onChange({ ...block, task_id: e.target.value })
|
|
6237
6868
|
}
|
|
6238
|
-
),
|
|
6239
|
-
/* @__PURE__ */ jsx(EditorField, { label: "Status", help:
|
|
6869
|
+
) }),
|
|
6870
|
+
/* @__PURE__ */ jsx(EditorField, { label: "Status", help: 'The state of a task. Can be "pending", "in_progress", "complete", or "error".', children: /* @__PURE__ */ jsx(
|
|
6240
6871
|
RadioGroup,
|
|
6241
6872
|
{
|
|
6242
6873
|
value: status,
|
|
@@ -6248,6 +6879,24 @@ function TaskCardEditor({ block, onChange }) {
|
|
|
6248
6879
|
] }, s))
|
|
6249
6880
|
}
|
|
6250
6881
|
) }),
|
|
6882
|
+
/* @__PURE__ */ jsx(
|
|
6883
|
+
RichTextField,
|
|
6884
|
+
{
|
|
6885
|
+
label: "Details",
|
|
6886
|
+
help: "Details of the task in the form of a single rich_text entity.",
|
|
6887
|
+
value: block.details,
|
|
6888
|
+
onChange: (next) => onChange({ ...block, details: next })
|
|
6889
|
+
}
|
|
6890
|
+
),
|
|
6891
|
+
/* @__PURE__ */ jsx(
|
|
6892
|
+
RichTextField,
|
|
6893
|
+
{
|
|
6894
|
+
label: "Output",
|
|
6895
|
+
help: "Output of the task in the form of a single rich_text entity.",
|
|
6896
|
+
value: block.output,
|
|
6897
|
+
onChange: (next) => onChange({ ...block, output: next })
|
|
6898
|
+
}
|
|
6899
|
+
),
|
|
6251
6900
|
/* @__PURE__ */ jsx(
|
|
6252
6901
|
SourcesField,
|
|
6253
6902
|
{
|
|
@@ -6257,6 +6906,51 @@ function TaskCardEditor({ block, onChange }) {
|
|
|
6257
6906
|
)
|
|
6258
6907
|
] });
|
|
6259
6908
|
}
|
|
6909
|
+
function RichTextField({
|
|
6910
|
+
label,
|
|
6911
|
+
help,
|
|
6912
|
+
value,
|
|
6913
|
+
onChange
|
|
6914
|
+
}) {
|
|
6915
|
+
if (!value) {
|
|
6916
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
6917
|
+
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
6918
|
+
/* @__PURE__ */ jsxs(
|
|
6919
|
+
Button,
|
|
6920
|
+
{
|
|
6921
|
+
type: "button",
|
|
6922
|
+
size: "sm",
|
|
6923
|
+
variant: "outline",
|
|
6924
|
+
className: "self-start",
|
|
6925
|
+
onClick: () => onChange(EMPTY_RICH_TEXT),
|
|
6926
|
+
children: [
|
|
6927
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
6928
|
+
" Add ",
|
|
6929
|
+
label.toLowerCase()
|
|
6930
|
+
]
|
|
6931
|
+
}
|
|
6932
|
+
),
|
|
6933
|
+
help && /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: help })
|
|
6934
|
+
] });
|
|
6935
|
+
}
|
|
6936
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
6937
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
6938
|
+
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
6939
|
+
/* @__PURE__ */ jsx(
|
|
6940
|
+
"button",
|
|
6941
|
+
{
|
|
6942
|
+
type: "button",
|
|
6943
|
+
"aria-label": `Remove ${label.toLowerCase()}`,
|
|
6944
|
+
onClick: () => onChange(void 0),
|
|
6945
|
+
className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
|
|
6946
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
6947
|
+
}
|
|
6948
|
+
)
|
|
6949
|
+
] }),
|
|
6950
|
+
/* @__PURE__ */ jsx(RichTextEditor, { block: value, onChange }),
|
|
6951
|
+
help && /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: help })
|
|
6952
|
+
] });
|
|
6953
|
+
}
|
|
6260
6954
|
function SourcesField({
|
|
6261
6955
|
sources,
|
|
6262
6956
|
onChange
|
|
@@ -6270,7 +6964,7 @@ function SourcesField({
|
|
|
6270
6964
|
};
|
|
6271
6965
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
|
|
6272
6966
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: "Sources" }),
|
|
6273
|
-
|
|
6967
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "Array of URL source elements used to generate a response." }),
|
|
6274
6968
|
sources.map((src, idx) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded border bg-background p-2", children: [
|
|
6275
6969
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
6276
6970
|
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
|
|
@@ -7481,6 +8175,7 @@ function BlockKitchen(props) {
|
|
|
7481
8175
|
initialBlocks,
|
|
7482
8176
|
onChange,
|
|
7483
8177
|
previewHooks,
|
|
8178
|
+
customEmojis,
|
|
7484
8179
|
loadChannels,
|
|
7485
8180
|
loadSendAsUserStatus,
|
|
7486
8181
|
onSend,
|
|
@@ -7524,6 +8219,13 @@ function BlockKitchen(props) {
|
|
|
7524
8219
|
const [previewSurface, setPreviewSurface] = useState(allowedSurfaces[0]);
|
|
7525
8220
|
const [activePaletteVariantId, setActivePaletteVariantId] = useState(null);
|
|
7526
8221
|
const isMobile = useIsMobile();
|
|
8222
|
+
const mergedPreviewHooks = useMemo(() => {
|
|
8223
|
+
if (!customEmojis || customEmojis.length === 0) {
|
|
8224
|
+
return previewHooks;
|
|
8225
|
+
}
|
|
8226
|
+
const byName = new Map(customEmojis.map((emoji) => [emoji.name, emoji]));
|
|
8227
|
+
return { emoji: makeEmojiHook(byName), ...previewHooks };
|
|
8228
|
+
}, [customEmojis, previewHooks]);
|
|
7527
8229
|
const validation = useBlockKitValidation(blocks, "message");
|
|
7528
8230
|
const sensors = useSensors(
|
|
7529
8231
|
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
@@ -7573,7 +8275,7 @@ function BlockKitchen(props) {
|
|
|
7573
8275
|
}, []);
|
|
7574
8276
|
const activePaletteVariant = activePaletteVariantId ? variantById.get(activePaletteVariantId) : null;
|
|
7575
8277
|
const blockPayloads = blocks.map((b) => b.block);
|
|
7576
|
-
return /* @__PURE__ */ jsx(BrandThemeScope, { theme, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(
|
|
8278
|
+
return /* @__PURE__ */ jsx(BrandThemeScope, { theme, children: /* @__PURE__ */ jsx(CustomEmojiProvider, { customEmojis, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(
|
|
7577
8279
|
DndContext,
|
|
7578
8280
|
{
|
|
7579
8281
|
sensors,
|
|
@@ -7619,7 +8321,7 @@ function BlockKitchen(props) {
|
|
|
7619
8321
|
{
|
|
7620
8322
|
blocks,
|
|
7621
8323
|
workspaceName,
|
|
7622
|
-
previewHooks,
|
|
8324
|
+
previewHooks: mergedPreviewHooks,
|
|
7623
8325
|
previewTheme,
|
|
7624
8326
|
previewSurface,
|
|
7625
8327
|
errorsByBlockId: validation.byBlockId,
|
|
@@ -7695,7 +8397,7 @@ function BlockKitchen(props) {
|
|
|
7695
8397
|
)
|
|
7696
8398
|
]
|
|
7697
8399
|
}
|
|
7698
|
-
) }) });
|
|
8400
|
+
) }) }) });
|
|
7699
8401
|
}
|
|
7700
8402
|
var UNCATEGORIZED = "Other";
|
|
7701
8403
|
function TemplateThumbnail({ blocks, theme }) {
|