@tightknitai/block-kitchen 0.8.5 → 0.8.6

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 CHANGED
@@ -116,8 +116,11 @@ export function MyBuilderPage() {
116
116
  | `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. |
117
117
  | `paletteSearchPlaceholder` | `string` | no | Placeholder text for the palette search input. Defaults to `'Search blocks…'`. Useful for localization. |
118
118
  | `allowedSurfaces` | `PreviewSurface[]` | no | Allowlist of preview surfaces (`'message'`, `'modal'`, `'app_home'`). Defaults to `['message']` — surface dropdown is hidden when only one surface is allowed. The first entry is the initial selection. |
119
- | `showThemeControl` | `boolean` | no | Defaults to `true`. When `false`, the theme is locked to `'light'`. |
120
- | `defaultPreviewTheme` | `'light' \| 'dark'` | no | Pass the host app's current theme so the preview opens matched to the consuming app's appearance. |
119
+ | `showThemeControl` | `boolean` | no | Defaults to `true`. When `false`, the toolbar's light/dark toggle is hidden and the theme stays at `defaultPreviewTheme`. Ignored when `previewTheme` is set (a controlled theme always hides the toggle). |
120
+ | `defaultPreviewTheme` | `'light' \| 'dark'` | no | Initial (uncontrolled) preview theme. Pass the host app's current theme so the preview opens matched to the consuming app's appearance. Ignored when `previewTheme` is provided. |
121
+ | `previewTheme` | `'light' \| 'dark'` | no | Controlled preview theme. When set, the preview renders in this theme, follows it reactively, and the toolbar's light/dark toggle is hidden so the host app fully owns the theme. Leave unset to keep the preview uncontrolled (seeded from `defaultPreviewTheme`, toggle shown). |
122
+ | `sendButtonLabel` | `string` | no | Label and accessible name for the toolbar's Send button (which opens the send dialog). Defaults to `'Send'`. Use it to signal that a configuration step follows, e.g. `'Send to channel…'`. |
123
+ | `confirmSendLabel` | `string` | no | Label for the send dialog's final confirm button. Defaults to `'Send'` (shows `'Sending…'` while in flight). |
121
124
  | `theme` | `BrandTheme \| BrandPreset` | no | Branding tokens applied to the builder chrome (toolbar, palette, popovers, dialogs). Accepts a `Partial<BrandTokens>` map and optional `light`/`dark` overrides. See [Styling](#styling) below. |
122
125
 
123
126
  ## Customizing the palette
@@ -222,7 +225,7 @@ const brand: BrandTheme = {
222
225
  - `tokens` applies in both light and dark contexts.
223
226
  - `light` and `dark` override per mode; the dark variant kicks in under a standard `.dark` ancestor class (next-themes default).
224
227
  - Color tokens take HSL component strings (`"262 83% 58%"`), matching the underlying CSS variable contract; `radius` takes a CSS length.
225
- - Scope is the builder chrome only. The embedded Slack preview keeps its native Slack styling regardless of `theme`; use `defaultPreviewTheme` for the preview's light/dark toggle.
228
+ - Scope is the builder chrome only. The embedded Slack preview keeps its native Slack styling regardless of `theme`; use `previewTheme` (controlled) or `defaultPreviewTheme` (uncontrolled) for the preview's light/dark appearance.
226
229
 
227
230
  The lower-level CSS-variable contract above keeps working; the `theme` prop simply layers on top of it.
228
231
 
@@ -1,5 +1,5 @@
1
- import { u as SupportedBlock } from './palette-Dco-CVMi.cjs';
2
- export { v as SupportedBlockType } from './palette-Dco-CVMi.cjs';
1
+ import { u as SupportedBlock } from './palette-DToUE_3J.cjs';
2
+ export { v as SupportedBlockType } from './palette-DToUE_3J.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 { u as SupportedBlock } from './palette-Dco-CVMi.js';
2
- export { v as SupportedBlockType } from './palette-Dco-CVMi.js';
1
+ import { u as SupportedBlock } from './palette-DToUE_3J.js';
2
+ export { v as SupportedBlockType } from './palette-DToUE_3J.js';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5
 
package/dist/index.cjs CHANGED
@@ -2274,6 +2274,18 @@ function IssuesSheet({
2274
2274
  ] })
2275
2275
  ] }) });
2276
2276
  }
2277
+
2278
+ // src/lib/parse-blocks-input.ts
2279
+ var BLOCKS_INPUT_SHAPE_ERROR = "Expected an array of blocks, or an object with a `blocks` array.";
2280
+ function unwrapBlocksInput(parsed) {
2281
+ if (Array.isArray(parsed)) {
2282
+ return parsed;
2283
+ }
2284
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.blocks)) {
2285
+ return parsed.blocks;
2286
+ }
2287
+ return null;
2288
+ }
2277
2289
  var MAX_JSON_BYTES = 1024 * 1024;
2278
2290
  function JsonDrawer({
2279
2291
  open,
@@ -2312,14 +2324,15 @@ function JsonDrawer({
2312
2324
  setValidationErrors([]);
2313
2325
  return;
2314
2326
  }
2315
- if (!Array.isArray(parsed)) {
2316
- setParseError("Top-level value must be an array of blocks.");
2327
+ const blocks2 = unwrapBlocksInput(parsed);
2328
+ if (!blocks2) {
2329
+ setParseError(BLOCKS_INPUT_SHAPE_ERROR);
2317
2330
  setValidationErrors([]);
2318
2331
  return;
2319
2332
  }
2320
2333
  setParseError(null);
2321
- onApply(parsed);
2322
- const result = slackBlockKitValidator.validateBlockKit(parsed, {
2334
+ onApply(blocks2);
2335
+ const result = slackBlockKitValidator.validateBlockKit(blocks2, {
2323
2336
  target: "blocks",
2324
2337
  surface: "message"
2325
2338
  });
@@ -2697,6 +2710,7 @@ function SendDialog({
2697
2710
  loadChannels,
2698
2711
  loadSendAsUserStatus,
2699
2712
  onSend,
2713
+ confirmSendLabel = "Send",
2700
2714
  errorCount,
2701
2715
  onShowIssues
2702
2716
  }) {
@@ -2870,7 +2884,7 @@ function SendDialog({
2870
2884
  type: "button",
2871
2885
  onClick: handleSubmit,
2872
2886
  disabled: status.kind === "sending" || !channelId || blocks.length === 0 || errorCount > 0,
2873
- children: status.kind === "sending" ? "Sending\u2026" : "Send"
2887
+ children: status.kind === "sending" ? "Sending\u2026" : confirmSendLabel
2874
2888
  }
2875
2889
  )
2876
2890
  ] })
@@ -7944,7 +7958,8 @@ function Toolbar2({
7944
7958
  allowedSurfaces,
7945
7959
  showThemeControl = true,
7946
7960
  docsLink,
7947
- errorCount
7961
+ errorCount,
7962
+ sendButtonLabel = "Send"
7948
7963
  }) {
7949
7964
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
7950
7965
  const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
@@ -8034,7 +8049,7 @@ function Toolbar2({
8034
8049
  variant: "ghost",
8035
8050
  size: "sm",
8036
8051
  onClick: onOpenIssues,
8037
- className: "text-destructive hover:text-destructive",
8052
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
8038
8053
  "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
8039
8054
  children: [
8040
8055
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
@@ -8127,9 +8142,9 @@ function Toolbar2({
8127
8142
  ) : null
8128
8143
  ] }) })
8129
8144
  ] }),
8130
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": "Send", children: [
8145
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
8131
8146
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
8132
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Send" })
8147
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
8133
8148
  ] })
8134
8149
  ] })
8135
8150
  ] });
@@ -8255,6 +8270,9 @@ function BlockKitchen(props) {
8255
8270
  showThemeControl = true,
8256
8271
  docsLink,
8257
8272
  defaultPreviewTheme = "light",
8273
+ previewTheme: controlledPreviewTheme,
8274
+ sendButtonLabel,
8275
+ confirmSendLabel,
8258
8276
  theme
8259
8277
  } = props;
8260
8278
  const paletteSections = React.useMemo(() => {
@@ -8282,7 +8300,9 @@ function BlockKitchen(props) {
8282
8300
  const [issuesOpen, setIssuesOpen] = React.useState(false);
8283
8301
  const [paletteOpen, setPaletteOpen] = React.useState(false);
8284
8302
  const [openBlockId, setOpenBlockId] = React.useState(null);
8285
- const [previewTheme, setPreviewTheme] = React.useState(defaultPreviewTheme);
8303
+ const [uncontrolledPreviewTheme, setUncontrolledPreviewTheme] = React.useState(defaultPreviewTheme);
8304
+ const isPreviewThemeControlled = controlledPreviewTheme !== void 0;
8305
+ const previewTheme = controlledPreviewTheme ?? uncontrolledPreviewTheme;
8286
8306
  const [previewSurface, setPreviewSurface] = React.useState(allowedSurfaces[0]);
8287
8307
  const [activePaletteVariantId, setActivePaletteVariantId] = React.useState(null);
8288
8308
  const isMobile = useIsMobile();
@@ -8364,12 +8384,13 @@ function BlockKitchen(props) {
8364
8384
  canClear: blocks.length > 0,
8365
8385
  errorCount: validation.total,
8366
8386
  previewTheme,
8367
- onPreviewThemeChange: setPreviewTheme,
8387
+ onPreviewThemeChange: setUncontrolledPreviewTheme,
8368
8388
  previewSurface,
8369
8389
  onPreviewSurfaceChange: setPreviewSurface,
8370
8390
  allowedSurfaces,
8371
- showThemeControl,
8372
- docsLink
8391
+ showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
8392
+ docsLink,
8393
+ sendButtonLabel
8373
8394
  }
8374
8395
  ),
8375
8396
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -8445,6 +8466,7 @@ function BlockKitchen(props) {
8445
8466
  loadChannels,
8446
8467
  loadSendAsUserStatus,
8447
8468
  onSend,
8469
+ confirmSendLabel,
8448
8470
  errorCount: validation.total,
8449
8471
  onShowIssues: () => {
8450
8472
  setSendOpen(false);