@tightknitai/block-kitchen 0.10.6 → 0.10.8
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 +30 -3
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +167 -86
- 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 +168 -87
- package/dist/index.js.map +1 -1
- package/dist/{palette-QbOx7kAa.d.cts → palette-CvSGn2z6.d.cts} +13 -6
- package/dist/{palette-QbOx7kAa.d.ts → palette-CvSGn2z6.d.ts} +13 -6
- package/dist/palette.cjs +10 -2
- package/dist/palette.cjs.map +1 -1
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/palette.js +10 -2
- package/dist/palette.js.map +1 -1
- package/dist/styles.css +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -98,6 +98,27 @@ export function MyBuilderPage() {
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
### Sizing
|
|
102
|
+
|
|
103
|
+
The builder is an app shell: a fixed toolbar over a palette rail and a preview pane that scroll independently. It fills its container, so the height you give that container is the height it takes.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// Give it a definite height and it fills exactly that.
|
|
107
|
+
<div style={{ height: "100%" }}>
|
|
108
|
+
<BlockKitchen {...props} />
|
|
109
|
+
</div>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Given no height to work with — a plain `<div>` in ordinary document flow — it bounds itself to `100svh` rather than growing to fit the palette's full block list, which would run a couple of thousand pixels down the page. Override that bound with `--bk-max-height` on any ancestor:
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
/* Room for a 4rem page header above the builder. */
|
|
116
|
+
.builder-host { --bk-max-height: calc(100svh - 4rem); }
|
|
117
|
+
|
|
118
|
+
/* Or opt out entirely and let it grow with its content. */
|
|
119
|
+
.builder-host { --bk-max-height: none; }
|
|
120
|
+
```
|
|
121
|
+
|
|
101
122
|
## Props
|
|
102
123
|
|
|
103
124
|
| Prop | Type | Required | Description |
|
|
@@ -120,7 +141,7 @@ export function MyBuilderPage() {
|
|
|
120
141
|
| `customEmojis` | `CustomEmoji[]` | no | Workspace custom emoji (`{ name, url, alias }`) the preview resolves. Entries with a `url` render `:name:` as the workspace image; alias entries (`url: null`) fall back to their target emoji. Render-only — never serialized into the emitted Block Kit JSON. A caller-supplied `previewHooks.emoji` takes precedence. |
|
|
121
142
|
| `palette` | `PaletteSection[]` | no | The left-hand palette of draggable variants. Defaults to `defaultPalette`. Spread it to filter, reorder, or add your own pre-configured variants — see [Customizing the palette](#customizing-the-palette). |
|
|
122
143
|
| `disabledBlockTypes` | `SupportedBlockType[]` | no | Block types to hide from the palette without rebuilding it. Filters at the variant level — a section keeps any variants whose block types aren't disabled; sections that end up empty are dropped. Convenient when you want the default palette minus a few types (e.g. `['image', 'table']` for a text-only builder). |
|
|
123
|
-
| `paletteMode` | `'advanced' \| 'simple'` | no | How much of the palette the user meets first. `'advanced'` (default) renders the full sectioned palette with its search input. `'simple'` opens on a flat list of the variants flagged `basic` — in the built-in palette
|
|
144
|
+
| `paletteMode` | `'advanced' \| 'simple'` | no | How much of the palette the user meets first. `'advanced'` (default) renders the full sectioned palette with its search input. `'simple'` opens on a flat list of the variants flagged `basic` — in the built-in palette: **Header**, **Divider**, **Rich Text Section** and **Image** — with no search and an "Advanced" link at the top that swaps in the full palette. Nothing is removed; it's a smaller first screen. See [Simple vs advanced palette](#simple-vs-advanced-palette). |
|
|
124
145
|
| `defaultOpenSections` | `boolean \| string[]` | no | Which palette section headers are expanded on first paint. `true` (default) opens all sections; `false` collapses all (Slack-style); an array opens only sections whose `name` is in the list (e.g. `['Section', 'Actions']`). The palette also has a built-in search input that expands matching sections on demand. |
|
|
125
146
|
| `showPaletteSearch` | `boolean` | no | Whether the palette renders the quick-search input above the section list. Defaults to `true`. Set `false` for compact palettes (e.g. when you've passed a small custom `palette`) where scanning by eye is faster than typing. Simple mode has no search either way — it appears with the rest of the palette behind "Advanced". |
|
|
126
147
|
| `paletteSearchPlaceholder` | `string` | no | Placeholder text for the palette search input. Defaults to `'Search blocks…'`. Useful for localization. |
|
|
@@ -421,6 +442,8 @@ update against the target from `loadedMessage` / `onLoadedMessageChange`.
|
|
|
421
442
|
|
|
422
443
|
## Customizing the palette
|
|
423
444
|
|
|
445
|
+
On desktop the palette is a rail down the left-hand side, with a chevron handle on its outer border that hides and restores it — collapsed, the preview gets the full width, and the rail comes back with its open sections and search exactly as they were. Below the `md` breakpoint there's no rail: the toolbar's **Blocks** button opens the palette as a bottom sheet instead, and picking a block closes it.
|
|
446
|
+
|
|
424
447
|
The default palette ships with curated presets for every supported block type. To narrow what's available, or add your own pre-configured variants (e.g. a "Help footer" section), pass a `palette` array. Define it at module scope (or wrap in `useMemo`) so it stays referentially stable across renders.
|
|
425
448
|
|
|
426
449
|
```tsx
|
|
@@ -455,13 +478,13 @@ Variant `id`s must be unique across the array — the drag-drop lookup keys by i
|
|
|
455
478
|
|
|
456
479
|
### Simple vs advanced palette
|
|
457
480
|
|
|
458
|
-
By default the palette opens on everything it has: every section, plus the search input. For consumers whose users are writing ordinary messages rather than building interactive apps, `paletteMode="simple"` opens on a
|
|
481
|
+
By default the palette opens on everything it has: every section, plus the search input. For consumers whose users are writing ordinary messages rather than building interactive apps, `paletteMode="simple"` opens on a short starter list instead — no sections, no search — behind an **Advanced** link that swaps in the full palette (and back).
|
|
459
482
|
|
|
460
483
|
```tsx
|
|
461
484
|
<BlockKitchen paletteMode="simple" {...rest} />
|
|
462
485
|
```
|
|
463
486
|
|
|
464
|
-
Which variants the simple list holds is a property of the palette, not the flag: a variant opts in with `basic: true`. The built-in palette flags
|
|
487
|
+
Which variants the simple list holds is a property of the palette, not the flag: a variant opts in with `basic: true`. The built-in palette flags four — **Header**, **Divider**, **Rich Text Section** and **Image** — listed in palette order. A custom palette that wants its own starter blocks says so:
|
|
465
488
|
|
|
466
489
|
```tsx
|
|
467
490
|
const PALETTE: readonly PaletteSection[] = [
|
|
@@ -476,8 +499,12 @@ const PALETTE: readonly PaletteSection[] = [
|
|
|
476
499
|
];
|
|
477
500
|
```
|
|
478
501
|
|
|
502
|
+
The flat list has no section headings to lean on, so a variant whose label only reads in context can give simple mode a self-describing one with `basicLabel` — that's how the built-in image variant (**No title**, under **Image**) shows up as **Image** there.
|
|
503
|
+
|
|
479
504
|
A palette that flags none of its variants `basic` has no simple list to show, so it renders as `'advanced'` regardless — the link is withheld rather than leading to an empty rail.
|
|
480
505
|
|
|
506
|
+
The builder remembers which side of that switch the user is on, so the mobile sheet — which is torn down every time it closes — reopens on the full palette rather than sending them back through the **Advanced** link on every block.
|
|
507
|
+
|
|
481
508
|
## Templates (`TemplatePicker`)
|
|
482
509
|
|
|
483
510
|
`TemplatePicker` is a standalone, page-sized picker that renders `Template`s as a grid of cards with live block previews, grouped into category sections — modeled on Slack's own Block Kit Builder templates page. It's pure UI: it owns no dialog and no layout, so you decide whether it's a route, a modal, a slide-over, or a sidebar next to the builder.
|
package/dist/helpers.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SupportedBlock } from './palette-
|
|
2
|
-
export { a1 as SupportedBlockType } from './palette-
|
|
1
|
+
import { S as SupportedBlock } from './palette-CvSGn2z6.cjs';
|
|
2
|
+
export { a1 as SupportedBlockType } from './palette-CvSGn2z6.cjs';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'slack-web-api-client';
|
|
5
5
|
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SupportedBlock } from './palette-
|
|
2
|
-
export { a1 as SupportedBlockType } from './palette-
|
|
1
|
+
import { S as SupportedBlock } from './palette-CvSGn2z6.js';
|
|
2
|
+
export { a1 as SupportedBlockType } from './palette-CvSGn2z6.js';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'slack-web-api-client';
|
|
5
5
|
|
package/dist/index.cjs
CHANGED
|
@@ -4,10 +4,10 @@ var core = require('@dnd-kit/core');
|
|
|
4
4
|
var sortable = require('@dnd-kit/sortable');
|
|
5
5
|
var lucideReact = require('lucide-react');
|
|
6
6
|
var React = require('react');
|
|
7
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
-
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
9
7
|
var clsx = require('clsx');
|
|
10
8
|
var tailwindMerge = require('tailwind-merge');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
11
11
|
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
12
12
|
var slackBlockKitValidator = require('@tightknitai/slack-block-kit-validator');
|
|
13
13
|
var nanoid = require('nanoid');
|
|
@@ -53,6 +53,9 @@ var Link__default = /*#__PURE__*/_interopDefault(Link);
|
|
|
53
53
|
var StarterKit__default = /*#__PURE__*/_interopDefault(StarterKit);
|
|
54
54
|
|
|
55
55
|
// src/components/block-kitchen.tsx
|
|
56
|
+
function cn(...inputs) {
|
|
57
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
58
|
+
}
|
|
56
59
|
|
|
57
60
|
// src/lib/container-blocks.ts
|
|
58
61
|
var CONTAINER_CHILD_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -734,6 +737,7 @@ var defaultPalette = [
|
|
|
734
737
|
{
|
|
735
738
|
id: "structure_header",
|
|
736
739
|
label: "Header",
|
|
740
|
+
basic: true,
|
|
737
741
|
factory: () => ({
|
|
738
742
|
type: "header",
|
|
739
743
|
text: { type: "plain_text", text: "Heading text", emoji: true }
|
|
@@ -742,6 +746,7 @@ var defaultPalette = [
|
|
|
742
746
|
{
|
|
743
747
|
id: "structure_divider",
|
|
744
748
|
label: "Divider",
|
|
749
|
+
basic: true,
|
|
745
750
|
factory: () => ({ type: "divider" })
|
|
746
751
|
},
|
|
747
752
|
{
|
|
@@ -849,8 +854,9 @@ var defaultPalette = [
|
|
|
849
854
|
{
|
|
850
855
|
id: "rich_text_section",
|
|
851
856
|
label: "Rich Text Section",
|
|
852
|
-
// The
|
|
853
|
-
//
|
|
857
|
+
// The block a message is mostly made of, and so one of the handful
|
|
858
|
+
// simple mode offers — listed there without its section heading,
|
|
859
|
+
// hence the self-describing label.
|
|
854
860
|
basic: true,
|
|
855
861
|
factory: () => ({
|
|
856
862
|
type: "rich_text",
|
|
@@ -887,6 +893,11 @@ var defaultPalette = [
|
|
|
887
893
|
{
|
|
888
894
|
id: "image_no_title",
|
|
889
895
|
label: "No title",
|
|
896
|
+
// The plain image is the one an ordinary message reaches for, so it
|
|
897
|
+
// carries simple mode's image slot — under its own name there, with
|
|
898
|
+
// no "Image" heading overhead to lend "No title" its meaning.
|
|
899
|
+
basic: true,
|
|
900
|
+
basicLabel: "Image",
|
|
890
901
|
factory: () => ({
|
|
891
902
|
type: "image",
|
|
892
903
|
image_url: "https://placehold.co/600x300?text=Image",
|
|
@@ -1709,9 +1720,6 @@ function labelForBlockType(type) {
|
|
|
1709
1720
|
return "Container";
|
|
1710
1721
|
}
|
|
1711
1722
|
}
|
|
1712
|
-
function cn(...inputs) {
|
|
1713
|
-
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
1714
|
-
}
|
|
1715
1723
|
var Sheet = DialogPrimitive__namespace.Root;
|
|
1716
1724
|
var SheetOverlay = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1717
1725
|
DialogPrimitive__namespace.Overlay,
|
|
@@ -3812,14 +3820,14 @@ function isDefaultOpen(sectionName, config) {
|
|
|
3812
3820
|
return config.includes(sectionName);
|
|
3813
3821
|
}
|
|
3814
3822
|
var PALETTE_TITLE = "Message Elements";
|
|
3815
|
-
var ADVANCED_HELP = "Browse every Slack block type \u2014 sections, images, buttons, inputs, tables and more
|
|
3816
|
-
var BASIC_HELP = "Go back to the short list:
|
|
3823
|
+
var ADVANCED_HELP = "Browse every Slack block type \u2014 sections, images, buttons, inputs, tables and more.";
|
|
3824
|
+
var BASIC_HELP = "Go back to the short list: the few blocks an ordinary message is made of.";
|
|
3817
3825
|
function basicVariants(sections) {
|
|
3818
3826
|
const out = [];
|
|
3819
3827
|
for (const section of sections) {
|
|
3820
3828
|
for (const variant of section.variants) {
|
|
3821
3829
|
if (variant.basic) {
|
|
3822
|
-
out.push(variant);
|
|
3830
|
+
out.push(variant.basicLabel ? { ...variant, label: variant.basicLabel } : variant);
|
|
3823
3831
|
}
|
|
3824
3832
|
}
|
|
3825
3833
|
}
|
|
@@ -3848,10 +3856,12 @@ function Palette({
|
|
|
3848
3856
|
showSearch = true,
|
|
3849
3857
|
searchPlaceholder = "Search blocks\u2026",
|
|
3850
3858
|
mode = "advanced",
|
|
3859
|
+
defaultAdvanced = false,
|
|
3860
|
+
onAdvancedChange,
|
|
3851
3861
|
variant = "aside"
|
|
3852
3862
|
}) {
|
|
3853
3863
|
const [query, setQuery] = React.useState("");
|
|
3854
|
-
const [advancedOpen, setAdvancedOpen] = React.useState(
|
|
3864
|
+
const [advancedOpen, setAdvancedOpen] = React.useState(defaultAdvanced);
|
|
3855
3865
|
const [prevMode, setPrevMode] = React.useState(mode);
|
|
3856
3866
|
if (prevMode !== mode) {
|
|
3857
3867
|
setPrevMode(mode);
|
|
@@ -3873,69 +3883,70 @@ function Palette({
|
|
|
3873
3883
|
{
|
|
3874
3884
|
className: cn(
|
|
3875
3885
|
"flex min-h-0 flex-col overflow-x-hidden overflow-y-auto",
|
|
3876
|
-
isSheet ? "w-full flex-1 bg-background" : "w-
|
|
3886
|
+
isSheet ? "w-full flex-1 bg-background" : "w-56 shrink-0 border-r bg-muted/20"
|
|
3877
3887
|
),
|
|
3878
3888
|
children: [
|
|
3879
|
-
offersSimple || searchVisible ?
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3893
|
-
"span",
|
|
3894
|
-
{
|
|
3895
|
-
className: cn(
|
|
3896
|
-
"min-w-0 truncate font-medium uppercase tracking-wide text-muted-foreground",
|
|
3897
|
-
isSheet ? "text-xs" : "text-[11px]"
|
|
3898
|
-
),
|
|
3899
|
-
children: PALETTE_TITLE
|
|
3900
|
-
}
|
|
3901
|
-
),
|
|
3902
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3903
|
-
ModeLink,
|
|
3904
|
-
{
|
|
3905
|
-
advanced,
|
|
3906
|
-
isSheet,
|
|
3907
|
-
onToggle: () => {
|
|
3908
|
-
setQuery("");
|
|
3909
|
-
setAdvancedOpen((v) => !v);
|
|
3910
|
-
}
|
|
3911
|
-
}
|
|
3912
|
-
)
|
|
3913
|
-
] })
|
|
3914
|
-
) : null,
|
|
3915
|
-
searchVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
3889
|
+
offersSimple || searchVisible ? (
|
|
3890
|
+
// Two elements so the header is opaque *and* the exact color of the
|
|
3891
|
+
// rail. Painted with the rail's own `bg-muted/20`, it composited over
|
|
3892
|
+
// the scrolled list instead and headings read through the search box
|
|
3893
|
+
// (a `backdrop-blur` softened them, it didn't hide them). Restoring
|
|
3894
|
+
// the `bg-background` base under the tint rebuilds the rail's own two
|
|
3895
|
+
// layers, so the match holds under any theme.
|
|
3896
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 shrink-0 bg-background", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-2 border-b px-3 pt-3 pb-2", isSheet ? "bg-background" : "bg-muted/20"), children: [
|
|
3897
|
+
offersSimple ? (
|
|
3898
|
+
// Titled row: with the sections gone, simple mode's header would
|
|
3899
|
+
// otherwise be a lone right-aligned link over empty space, and
|
|
3900
|
+
// nothing would name what the rail below it holds.
|
|
3901
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3916
3902
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3917
|
-
|
|
3903
|
+
"span",
|
|
3918
3904
|
{
|
|
3919
3905
|
className: cn(
|
|
3920
|
-
"
|
|
3921
|
-
isSheet ? "
|
|
3922
|
-
)
|
|
3906
|
+
"min-w-0 truncate font-medium uppercase tracking-wide text-muted-foreground",
|
|
3907
|
+
isSheet ? "text-xs" : "text-[11px]"
|
|
3908
|
+
),
|
|
3909
|
+
children: PALETTE_TITLE
|
|
3923
3910
|
}
|
|
3924
3911
|
),
|
|
3925
3912
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3926
|
-
|
|
3913
|
+
ModeLink,
|
|
3927
3914
|
{
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3915
|
+
advanced,
|
|
3916
|
+
isSheet,
|
|
3917
|
+
onToggle: () => {
|
|
3918
|
+
setQuery("");
|
|
3919
|
+
const next = !advancedOpen;
|
|
3920
|
+
setAdvancedOpen(next);
|
|
3921
|
+
onAdvancedChange?.(next);
|
|
3922
|
+
}
|
|
3934
3923
|
}
|
|
3935
3924
|
)
|
|
3936
|
-
] })
|
|
3937
|
-
|
|
3938
|
-
|
|
3925
|
+
] })
|
|
3926
|
+
) : null,
|
|
3927
|
+
searchVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
3928
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3929
|
+
lucideReact.Search,
|
|
3930
|
+
{
|
|
3931
|
+
className: cn(
|
|
3932
|
+
"pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground",
|
|
3933
|
+
isSheet ? "h-4 w-4" : "h-3.5 w-3.5"
|
|
3934
|
+
)
|
|
3935
|
+
}
|
|
3936
|
+
),
|
|
3937
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3938
|
+
Input,
|
|
3939
|
+
{
|
|
3940
|
+
type: "search",
|
|
3941
|
+
value: query,
|
|
3942
|
+
onChange: (e) => setQuery(e.target.value),
|
|
3943
|
+
placeholder: searchPlaceholder,
|
|
3944
|
+
"aria-label": searchPlaceholder,
|
|
3945
|
+
className: cn(isSheet ? "h-10 pl-8 text-base" : "h-8 pl-7 text-sm")
|
|
3946
|
+
}
|
|
3947
|
+
)
|
|
3948
|
+
] }) : null
|
|
3949
|
+
] }) })
|
|
3939
3950
|
) : null,
|
|
3940
3951
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", isSheet ? "px-2 pb-6" : "p-3"), children: !advanced ? (
|
|
3941
3952
|
// Simple mode: a flat list, no section headings — with this few
|
|
@@ -4035,8 +4046,8 @@ function PaletteItem({
|
|
|
4035
4046
|
isDragging && "opacity-50"
|
|
4036
4047
|
),
|
|
4037
4048
|
children: [
|
|
4038
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "
|
|
4039
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "
|
|
4049
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: variant.label }),
|
|
4050
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground transition-colors group-hover:bg-primary/10 group-hover:text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" }) })
|
|
4040
4051
|
]
|
|
4041
4052
|
}
|
|
4042
4053
|
);
|
|
@@ -4046,7 +4057,7 @@ function PaletteItem({
|
|
|
4046
4057
|
{
|
|
4047
4058
|
ref: setNodeRef,
|
|
4048
4059
|
className: cn(
|
|
4049
|
-
"group flex items-center gap-2 rounded px-2 py-1
|
|
4060
|
+
"group flex items-center gap-2 rounded px-2 py-1 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground has-[:focus-visible]:bg-accent has-[:focus-visible]:text-foreground",
|
|
4050
4061
|
isDragging && "opacity-50"
|
|
4051
4062
|
),
|
|
4052
4063
|
children: [
|
|
@@ -4054,7 +4065,7 @@ function PaletteItem({
|
|
|
4054
4065
|
"div",
|
|
4055
4066
|
{
|
|
4056
4067
|
ref: setActivatorNodeRef,
|
|
4057
|
-
className: "-my-1
|
|
4068
|
+
className: "-my-1 flex min-w-0 flex-1 cursor-grab items-center gap-2 rounded py-1 active:cursor-grabbing focus-visible:outline-none",
|
|
4058
4069
|
...attributes,
|
|
4059
4070
|
...listeners,
|
|
4060
4071
|
children: [
|
|
@@ -10031,7 +10042,7 @@ function Toolbar2({
|
|
|
10031
10042
|
variant: "ghost",
|
|
10032
10043
|
size: "sm",
|
|
10033
10044
|
onClick: onOpenPalette,
|
|
10034
|
-
className: "md:hidden",
|
|
10045
|
+
className: "gap-1 md:hidden",
|
|
10035
10046
|
"aria-label": "Add a block",
|
|
10036
10047
|
children: [
|
|
10037
10048
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
|
|
@@ -10151,18 +10162,29 @@ function Toolbar2({
|
|
|
10151
10162
|
size: "sm",
|
|
10152
10163
|
onClick: onClear,
|
|
10153
10164
|
disabled: !canClear,
|
|
10154
|
-
className: "hover:bg-destructive/10 hover:text-destructive",
|
|
10165
|
+
className: "group gap-0 hover:bg-destructive/10 hover:text-destructive",
|
|
10155
10166
|
"aria-label": "Clear all blocks",
|
|
10156
10167
|
children: [
|
|
10157
10168
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" }),
|
|
10158
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10169
|
+
/* @__PURE__ */ jsxRuntime.jsx(ExpandingLabel, { children: "Clear" })
|
|
10170
|
+
]
|
|
10171
|
+
}
|
|
10172
|
+
),
|
|
10173
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10174
|
+
Button,
|
|
10175
|
+
{
|
|
10176
|
+
type: "button",
|
|
10177
|
+
variant: "ghost",
|
|
10178
|
+
size: "sm",
|
|
10179
|
+
onClick: onOpenJson,
|
|
10180
|
+
className: "group gap-0",
|
|
10181
|
+
"aria-label": "View JSON",
|
|
10182
|
+
children: [
|
|
10183
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
|
|
10184
|
+
/* @__PURE__ */ jsxRuntime.jsx(ExpandingLabel, { children: "View JSON" })
|
|
10159
10185
|
]
|
|
10160
10186
|
}
|
|
10161
10187
|
),
|
|
10162
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: onOpenJson, "aria-label": "View JSON", children: [
|
|
10163
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { className: "h-3.5 w-3.5" }),
|
|
10164
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: "View JSON" })
|
|
10165
|
-
] }),
|
|
10166
10188
|
!showSend ? primaryAction ? (
|
|
10167
10189
|
// Compose-only mode with a host-owned primary action: same
|
|
10168
10190
|
// placement and styling as the built-in send button. No icon —
|
|
@@ -10272,6 +10294,19 @@ function Toolbar2({
|
|
|
10272
10294
|
] }) : null
|
|
10273
10295
|
] });
|
|
10274
10296
|
}
|
|
10297
|
+
function ExpandingLabel({ children }) {
|
|
10298
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10299
|
+
"span",
|
|
10300
|
+
{
|
|
10301
|
+
className: cn(
|
|
10302
|
+
"grid grid-cols-[0fr] transition-[grid-template-columns] duration-200 ease-out",
|
|
10303
|
+
"group-hover:grid-cols-[1fr] group-focus-visible:grid-cols-[1fr]",
|
|
10304
|
+
"motion-reduce:transition-none"
|
|
10305
|
+
),
|
|
10306
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block whitespace-nowrap pl-1.5", children }) })
|
|
10307
|
+
}
|
|
10308
|
+
);
|
|
10309
|
+
}
|
|
10275
10310
|
function Menu({
|
|
10276
10311
|
ariaLabel,
|
|
10277
10312
|
options,
|
|
@@ -10563,12 +10598,20 @@ function BlockKitchen(props) {
|
|
|
10563
10598
|
useNotifyOnChange(activeEditTarget, loadedMessageKey, loading?.onLoadedMessageChange);
|
|
10564
10599
|
const [issuesOpen, setIssuesOpen] = React.useState(false);
|
|
10565
10600
|
const [paletteOpen, setPaletteOpen] = React.useState(false);
|
|
10601
|
+
const [paletteCollapsed, setPaletteCollapsed] = React.useState(false);
|
|
10602
|
+
const [paletteAdvanced, setPaletteAdvanced] = React.useState(false);
|
|
10603
|
+
const [prevPaletteMode, setPrevPaletteMode] = React.useState(paletteMode);
|
|
10604
|
+
if (prevPaletteMode !== paletteMode) {
|
|
10605
|
+
setPrevPaletteMode(paletteMode);
|
|
10606
|
+
setPaletteAdvanced(false);
|
|
10607
|
+
}
|
|
10566
10608
|
const [openBlockId, setOpenBlockId] = React.useState(null);
|
|
10567
10609
|
const [uncontrolledPreviewTheme, setUncontrolledPreviewTheme] = React.useState(defaultPreviewTheme);
|
|
10568
10610
|
const isPreviewThemeControlled = controlledPreviewTheme !== void 0;
|
|
10569
10611
|
const previewTheme = controlledPreviewTheme ?? uncontrolledPreviewTheme;
|
|
10570
10612
|
const [previewSurface, setPreviewSurface] = React.useState(allowedSurfaces[0]);
|
|
10571
10613
|
const [activePaletteVariantId, setActivePaletteVariantId] = React.useState(null);
|
|
10614
|
+
const paletteSheetRef = React.useRef(null);
|
|
10572
10615
|
const isMobile = useIsMobile();
|
|
10573
10616
|
const mergedPreviewHooks = React.useMemo(() => {
|
|
10574
10617
|
if (!customEmojis || customEmojis.length === 0) {
|
|
@@ -10694,7 +10737,7 @@ function BlockKitchen(props) {
|
|
|
10694
10737
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10695
10738
|
"div",
|
|
10696
10739
|
{
|
|
10697
|
-
className: "bk-root flex h-full w-full flex-col overflow-hidden rounded-md border bg-background text-foreground",
|
|
10740
|
+
className: "bk-root flex h-full max-h-[var(--bk-max-height,100svh)] w-full flex-col overflow-hidden rounded-md border bg-background text-foreground",
|
|
10698
10741
|
onKeyDown: handleKeyDown,
|
|
10699
10742
|
children: [
|
|
10700
10743
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -10752,17 +10795,48 @@ function BlockKitchen(props) {
|
|
|
10752
10795
|
}
|
|
10753
10796
|
),
|
|
10754
10797
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
|
|
10755
|
-
/* @__PURE__ */ jsxRuntime.
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10798
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative hidden min-h-0 shrink-0 md:flex", children: [
|
|
10799
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { hidden: paletteCollapsed, className: cn("min-h-0", !paletteCollapsed && "flex"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10800
|
+
Palette,
|
|
10801
|
+
{
|
|
10802
|
+
onAddBlock: (block) => addBlock(block),
|
|
10803
|
+
sections: paletteSections,
|
|
10804
|
+
mode: paletteMode,
|
|
10805
|
+
showSearch: showPaletteSearch,
|
|
10806
|
+
searchPlaceholder: paletteSearchPlaceholder,
|
|
10807
|
+
defaultOpenSections,
|
|
10808
|
+
defaultAdvanced: paletteAdvanced,
|
|
10809
|
+
onAdvancedChange: setPaletteAdvanced
|
|
10810
|
+
}
|
|
10811
|
+
) }),
|
|
10812
|
+
paletteCollapsed ? (
|
|
10813
|
+
// In a host that hands the builder no height of its own,
|
|
10814
|
+
// the rail is what the shell takes its height from (see
|
|
10815
|
+
// the shell's `max-h` note above) — so hiding it would
|
|
10816
|
+
// shrink the whole frame to the height of the message and
|
|
10817
|
+
// grow it back on the way out. This stands in for the rail
|
|
10818
|
+
// at no width. Inert to everything but layout.
|
|
10819
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "w-0 shrink-0 md:h-[var(--bk-max-height,100svh)]" })
|
|
10820
|
+
) : null,
|
|
10821
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10822
|
+
"button",
|
|
10823
|
+
{
|
|
10824
|
+
type: "button",
|
|
10825
|
+
onClick: () => setPaletteCollapsed((v) => !v),
|
|
10826
|
+
"aria-expanded": !paletteCollapsed,
|
|
10827
|
+
"aria-label": paletteCollapsed ? "Show block palette" : "Hide block palette",
|
|
10828
|
+
title: paletteCollapsed ? "Show block palette" : "Hide block palette",
|
|
10829
|
+
className: cn(
|
|
10830
|
+
// A hairline outline in the rail's own border color, so
|
|
10831
|
+
// the handle reads as part of the edge it rides rather
|
|
10832
|
+
// than a chevron floating loose over the border line.
|
|
10833
|
+
"absolute top-1/2 z-10 flex h-10 w-4 -translate-y-1/2 cursor-pointer appearance-none items-center justify-center rounded-sm border bg-background p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
10834
|
+
paletteCollapsed ? "left-0" : "right-0 translate-x-1/2"
|
|
10835
|
+
),
|
|
10836
|
+
children: paletteCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4", strokeWidth: 1.5 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4", strokeWidth: 1.5 })
|
|
10837
|
+
}
|
|
10838
|
+
)
|
|
10839
|
+
] }),
|
|
10766
10840
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10767
10841
|
Surface,
|
|
10768
10842
|
{
|
|
@@ -10795,8 +10869,13 @@ function BlockKitchen(props) {
|
|
|
10795
10869
|
/* @__PURE__ */ jsxRuntime.jsx(Sheet, { open: paletteOpen && isMobile, onOpenChange: setPaletteOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10796
10870
|
SheetContent,
|
|
10797
10871
|
{
|
|
10872
|
+
ref: paletteSheetRef,
|
|
10798
10873
|
side: "bottom",
|
|
10799
10874
|
className: "bk-portal-content flex h-[85svh] max-h-[85svh] flex-col gap-3 p-0 sm:max-w-none",
|
|
10875
|
+
onOpenAutoFocus: (e) => {
|
|
10876
|
+
e.preventDefault();
|
|
10877
|
+
paletteSheetRef.current?.focus();
|
|
10878
|
+
},
|
|
10800
10879
|
children: [
|
|
10801
10880
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 px-4 pt-5", children: [
|
|
10802
10881
|
/* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { children: "Add a block" }),
|
|
@@ -10814,6 +10893,8 @@ function BlockKitchen(props) {
|
|
|
10814
10893
|
showSearch: showPaletteSearch,
|
|
10815
10894
|
searchPlaceholder: paletteSearchPlaceholder,
|
|
10816
10895
|
defaultOpenSections,
|
|
10896
|
+
defaultAdvanced: paletteAdvanced,
|
|
10897
|
+
onAdvancedChange: setPaletteAdvanced,
|
|
10817
10898
|
variant: "sheet"
|
|
10818
10899
|
}
|
|
10819
10900
|
) })
|