@tightknitai/block-kitchen 0.8.5 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { B as BlockKitchenProps, E as Template, q as PreviewSurface, r as PreviewTheme } from './palette-Dco-CVMi.cjs';
3
- export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as ChannelOption, h as ContextActionsBlock, i as ContextActionsElement, j as CustomEmoji, F as FeedbackButtonSubobject, k as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, l as IconButtonIcon, m as InputBlock, M as MarkdownBlock, P as PaletteSection, n as PaletteVariant, o as PlanBlock, p as PreviewHooks, S as SendAsUserStatus, s as SendPayload, t as SendResult, u as SupportedBlock, v as SupportedBlockType, w as SupportedHeaderBlock, T as TableBlock, x as TableCell, y as TableColumnSetting, z as TaskCardBlock, D as TaskCardStatus, U as UrlSourceElement, V as VideoBlock, J as defaultPalette, K as extraAlertVariant, L as legacyInputVariants } from './palette-Dco-CVMi.cjs';
2
+ import { B as BlockKitchenProps, U as Template, y as PreviewSurface, z as PreviewTheme } from './palette-WZCUVJTn.cjs';
3
+ export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContextActionsBlock, o as ContextActionsElement, p as CustomEmoji, D as DataVisualizationBlock, F as FeedbackButtonSubobject, q as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, r as IconButtonIcon, s as InputBlock, M as MarkdownBlock, P as PaletteSection, t as PaletteVariant, u as PieChart, v as PieChartSegment, w as PlanBlock, x as PreviewHooks, S as SendAsUserStatus, E as SendPayload, G as SendResult, J as SupportedBlock, K as SupportedBlockType, L as SupportedHeaderBlock, T as TableBlock, N as TableCell, O as TableColumnSetting, Q as TaskCardBlock, R as TaskCardStatus, V as UrlSourceElement, W as VideoBlock, Y as defaultPalette, Z as extraAlertVariant, _ as legacyInputVariants } from './palette-WZCUVJTn.cjs';
4
4
  export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.cjs';
5
5
  import 'slack-web-api-client';
6
6
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { B as BlockKitchenProps, E as Template, q as PreviewSurface, r as PreviewTheme } from './palette-Dco-CVMi.js';
3
- export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as ChannelOption, h as ContextActionsBlock, i as ContextActionsElement, j as CustomEmoji, F as FeedbackButtonSubobject, k as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, l as IconButtonIcon, m as InputBlock, M as MarkdownBlock, P as PaletteSection, n as PaletteVariant, o as PlanBlock, p as PreviewHooks, S as SendAsUserStatus, s as SendPayload, t as SendResult, u as SupportedBlock, v as SupportedBlockType, w as SupportedHeaderBlock, T as TableBlock, x as TableCell, y as TableColumnSetting, z as TaskCardBlock, D as TaskCardStatus, U as UrlSourceElement, V as VideoBlock, J as defaultPalette, K as extraAlertVariant, L as legacyInputVariants } from './palette-Dco-CVMi.js';
2
+ import { B as BlockKitchenProps, U as Template, y as PreviewSurface, z as PreviewTheme } from './palette-WZCUVJTn.js';
3
+ export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContextActionsBlock, o as ContextActionsElement, p as CustomEmoji, D as DataVisualizationBlock, F as FeedbackButtonSubobject, q as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, r as IconButtonIcon, s as InputBlock, M as MarkdownBlock, P as PaletteSection, t as PaletteVariant, u as PieChart, v as PieChartSegment, w as PlanBlock, x as PreviewHooks, S as SendAsUserStatus, E as SendPayload, G as SendResult, J as SupportedBlock, K as SupportedBlockType, L as SupportedHeaderBlock, T as TableBlock, N as TableCell, O as TableColumnSetting, Q as TaskCardBlock, R as TaskCardStatus, V as UrlSourceElement, W as VideoBlock, Y as defaultPalette, Z as extraAlertVariant, _ as legacyInputVariants } from './palette-WZCUVJTn.js';
4
4
  export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.js';
5
5
  import 'slack-web-api-client';
6
6
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCenter, DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
2
2
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
3
- import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, 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';
3
+ import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, 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, Underline, Code, List, ListOrdered, Quote, Undo2, Redo2, Link as Link$1, Smile } from 'lucide-react';
4
4
  import * as React from 'react';
5
5
  import { createContext, useMemo, useState, useCallback, useRef, useEffect, useId, useContext } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -984,6 +984,143 @@ var defaultPalette = [
984
984
  })
985
985
  }
986
986
  ]
987
+ },
988
+ {
989
+ name: "Data Visualization",
990
+ icon: ChartColumnBig,
991
+ variants: [
992
+ {
993
+ id: "dataviz_line",
994
+ label: "Line chart",
995
+ factory: () => ({
996
+ type: "data_visualization",
997
+ title: "Weekly active users by platform",
998
+ chart: {
999
+ type: "line",
1000
+ series: [
1001
+ {
1002
+ name: "Desktop",
1003
+ data: [
1004
+ { label: "Mon", value: 800 },
1005
+ { label: "Tue", value: 920 },
1006
+ { label: "Wed", value: 880 },
1007
+ { label: "Thu", value: 1010 },
1008
+ { label: "Fri", value: 1120 }
1009
+ ]
1010
+ },
1011
+ {
1012
+ name: "Mobile",
1013
+ data: [
1014
+ { label: "Mon", value: 400 },
1015
+ { label: "Tue", value: 530 },
1016
+ { label: "Wed", value: 500 },
1017
+ { label: "Thu", value: 590 },
1018
+ { label: "Fri", value: 600 }
1019
+ ]
1020
+ }
1021
+ ],
1022
+ axis_config: {
1023
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1024
+ x_label: "Day",
1025
+ y_label: "Users"
1026
+ }
1027
+ }
1028
+ })
1029
+ },
1030
+ {
1031
+ id: "dataviz_bar",
1032
+ label: "Bar chart",
1033
+ factory: () => ({
1034
+ type: "data_visualization",
1035
+ title: "Messages sent by platform",
1036
+ chart: {
1037
+ type: "bar",
1038
+ series: [
1039
+ {
1040
+ name: "Desktop",
1041
+ data: [
1042
+ { label: "Mon", value: 1500 },
1043
+ { label: "Tue", value: 1400 },
1044
+ { label: "Wed", value: 1700 },
1045
+ { label: "Thu", value: 1600 },
1046
+ { label: "Fri", value: 1280 }
1047
+ ]
1048
+ },
1049
+ {
1050
+ name: "Mobile",
1051
+ data: [
1052
+ { label: "Mon", value: 800 },
1053
+ { label: "Tue", value: 750 },
1054
+ { label: "Wed", value: 920 },
1055
+ { label: "Thu", value: 880 },
1056
+ { label: "Fri", value: 700 }
1057
+ ]
1058
+ }
1059
+ ],
1060
+ axis_config: {
1061
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1062
+ x_label: "Day",
1063
+ y_label: "Messages"
1064
+ }
1065
+ }
1066
+ })
1067
+ },
1068
+ {
1069
+ id: "dataviz_area",
1070
+ label: "Area chart",
1071
+ factory: () => ({
1072
+ type: "data_visualization",
1073
+ title: "Concurrent users by platform",
1074
+ chart: {
1075
+ type: "area",
1076
+ series: [
1077
+ {
1078
+ name: "Desktop",
1079
+ data: [
1080
+ { label: "Mon", value: 2800 },
1081
+ { label: "Tue", value: 3e3 },
1082
+ { label: "Wed", value: 3400 },
1083
+ { label: "Thu", value: 3200 },
1084
+ { label: "Fri", value: 3500 }
1085
+ ]
1086
+ },
1087
+ {
1088
+ name: "Mobile",
1089
+ data: [
1090
+ { label: "Mon", value: 1400 },
1091
+ { label: "Tue", value: 1500 },
1092
+ { label: "Wed", value: 1700 },
1093
+ { label: "Thu", value: 1600 },
1094
+ { label: "Fri", value: 1800 }
1095
+ ]
1096
+ }
1097
+ ],
1098
+ axis_config: {
1099
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1100
+ x_label: "Day",
1101
+ y_label: "Users"
1102
+ }
1103
+ }
1104
+ })
1105
+ },
1106
+ {
1107
+ id: "dataviz_pie",
1108
+ label: "Pie chart",
1109
+ factory: () => ({
1110
+ type: "data_visualization",
1111
+ title: "Plan distribution by tier",
1112
+ chart: {
1113
+ type: "pie",
1114
+ segments: [
1115
+ { label: "Free", value: 4200 },
1116
+ { label: "Pro", value: 2300 },
1117
+ { label: "Business+", value: 1100 },
1118
+ { label: "Enterprise", value: 480 }
1119
+ ]
1120
+ }
1121
+ })
1122
+ }
1123
+ ]
987
1124
  }
988
1125
  ];
989
1126
  var legacyInputVariants = [
@@ -1462,6 +1599,8 @@ function labelForBlockType(type) {
1462
1599
  return "Plan";
1463
1600
  case "task_card":
1464
1601
  return "Task Card";
1602
+ case "data_visualization":
1603
+ return "Data Visualization";
1465
1604
  }
1466
1605
  }
1467
1606
  function cn(...inputs) {
@@ -2244,6 +2383,18 @@ function IssuesSheet({
2244
2383
  ] })
2245
2384
  ] }) });
2246
2385
  }
2386
+
2387
+ // src/lib/parse-blocks-input.ts
2388
+ var BLOCKS_INPUT_SHAPE_ERROR = "Expected an array of blocks, or an object with a `blocks` array.";
2389
+ function unwrapBlocksInput(parsed) {
2390
+ if (Array.isArray(parsed)) {
2391
+ return parsed;
2392
+ }
2393
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.blocks)) {
2394
+ return parsed.blocks;
2395
+ }
2396
+ return null;
2397
+ }
2247
2398
  var MAX_JSON_BYTES = 1024 * 1024;
2248
2399
  function JsonDrawer({
2249
2400
  open,
@@ -2282,14 +2433,15 @@ function JsonDrawer({
2282
2433
  setValidationErrors([]);
2283
2434
  return;
2284
2435
  }
2285
- if (!Array.isArray(parsed)) {
2286
- setParseError("Top-level value must be an array of blocks.");
2436
+ const blocks2 = unwrapBlocksInput(parsed);
2437
+ if (!blocks2) {
2438
+ setParseError(BLOCKS_INPUT_SHAPE_ERROR);
2287
2439
  setValidationErrors([]);
2288
2440
  return;
2289
2441
  }
2290
2442
  setParseError(null);
2291
- onApply(parsed);
2292
- const result = validateBlockKit(parsed, {
2443
+ onApply(blocks2);
2444
+ const result = validateBlockKit(blocks2, {
2293
2445
  target: "blocks",
2294
2446
  surface: "message"
2295
2447
  });
@@ -2667,6 +2819,7 @@ function SendDialog({
2667
2819
  loadChannels,
2668
2820
  loadSendAsUserStatus,
2669
2821
  onSend,
2822
+ confirmSendLabel = "Send",
2670
2823
  errorCount,
2671
2824
  onShowIssues
2672
2825
  }) {
@@ -2840,7 +2993,7 @@ function SendDialog({
2840
2993
  type: "button",
2841
2994
  onClick: handleSubmit,
2842
2995
  disabled: status.kind === "sending" || !channelId || blocks.length === 0 || errorCount > 0,
2843
- children: status.kind === "sending" ? "Sending\u2026" : "Send"
2996
+ children: status.kind === "sending" ? "Sending\u2026" : confirmSendLabel
2844
2997
  }
2845
2998
  )
2846
2999
  ] })
@@ -3800,6 +3953,294 @@ function ImageItemCard({
3800
3953
  ) })
3801
3954
  ] });
3802
3955
  }
3956
+ var CHART_TYPES = ["line", "bar", "area", "pie"];
3957
+ function isCartesian(chart) {
3958
+ return chart.type !== "pie";
3959
+ }
3960
+ function cartesianToSegments(chart) {
3961
+ return (chart.series[0]?.data ?? []).map((d) => ({ label: d.label, value: d.value }));
3962
+ }
3963
+ function segmentsToCartesian(type, segments) {
3964
+ return {
3965
+ type,
3966
+ series: [{ name: "Series 1", data: segments.map((s) => ({ label: s.label, value: s.value })) }],
3967
+ axis_config: { categories: segments.map((s) => s.label) }
3968
+ };
3969
+ }
3970
+ function convertChart(chart, nextType) {
3971
+ if (nextType === chart.type) {
3972
+ return chart;
3973
+ }
3974
+ if (nextType === "pie") {
3975
+ return { type: "pie", segments: isCartesian(chart) ? cartesianToSegments(chart) : chart.segments };
3976
+ }
3977
+ if (isCartesian(chart)) {
3978
+ return { ...chart, type: nextType };
3979
+ }
3980
+ return segmentsToCartesian(nextType, chart.segments);
3981
+ }
3982
+ function DataVisualizationEditor({ block, onChange }) {
3983
+ const { chart } = block;
3984
+ const setChart = (next) => onChange({ ...block, chart: next });
3985
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3986
+ /* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Heading shown above the chart.", htmlFor: "dataviz-title", children: /* @__PURE__ */ jsx(
3987
+ Input,
3988
+ {
3989
+ id: "dataviz-title",
3990
+ value: block.title,
3991
+ placeholder: "e.g. Weekly active users",
3992
+ onChange: (e) => onChange({ ...block, title: e.target.value })
3993
+ }
3994
+ ) }),
3995
+ /* @__PURE__ */ jsx(EditorField, { label: "Chart type", children: /* @__PURE__ */ jsx(
3996
+ RadioGroup,
3997
+ {
3998
+ value: chart.type,
3999
+ onValueChange: (v) => setChart(convertChart(chart, v)),
4000
+ className: "flex flex-row flex-wrap gap-3",
4001
+ children: CHART_TYPES.map((t) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
4002
+ /* @__PURE__ */ jsx(RadioGroupItem, { value: t, id: `dataviz-type-${t}` }),
4003
+ /* @__PURE__ */ jsx(Label, { htmlFor: `dataviz-type-${t}`, className: "text-xs capitalize", children: t })
4004
+ ] }, t))
4005
+ }
4006
+ ) }),
4007
+ isCartesian(chart) ? /* @__PURE__ */ jsx(CartesianFields, { chart, onChange: setChart }) : /* @__PURE__ */ jsx(PieFields, { chart, onChange: setChart })
4008
+ ] });
4009
+ }
4010
+ function NumberInput({
4011
+ id,
4012
+ value,
4013
+ onCommit,
4014
+ ariaLabel,
4015
+ className
4016
+ }) {
4017
+ const [draft, setDraft] = useState(String(value));
4018
+ useEffect(() => {
4019
+ setDraft(String(value));
4020
+ }, [value]);
4021
+ return /* @__PURE__ */ jsx(
4022
+ Input,
4023
+ {
4024
+ id,
4025
+ type: "number",
4026
+ inputMode: "numeric",
4027
+ "aria-label": ariaLabel,
4028
+ value: draft,
4029
+ className,
4030
+ onChange: (e) => {
4031
+ const raw = e.target.value;
4032
+ setDraft(raw);
4033
+ if (raw === "" || raw === "-" || raw === "." || raw === "-.") {
4034
+ return;
4035
+ }
4036
+ const next = Number(raw);
4037
+ if (!Number.isNaN(next)) {
4038
+ onCommit(next);
4039
+ }
4040
+ },
4041
+ onBlur: () => {
4042
+ const next = Number(draft);
4043
+ const safe = draft.trim() === "" || Number.isNaN(next) ? 0 : next;
4044
+ onCommit(safe);
4045
+ setDraft(String(safe));
4046
+ }
4047
+ }
4048
+ );
4049
+ }
4050
+ function CartesianFields({ chart, onChange }) {
4051
+ const series = chart.series;
4052
+ const categories = chart.axis_config?.categories && chart.axis_config.categories.length > 0 ? chart.axis_config.categories : series[0]?.data.map((d) => d.label) ?? [];
4053
+ const xLabel = chart.axis_config?.x_label ?? "";
4054
+ const yLabel = chart.axis_config?.y_label ?? "";
4055
+ const commit = (nextCategories, nextSeries, axis) => {
4056
+ const aligned = nextSeries.map((s) => ({
4057
+ name: s.name,
4058
+ data: nextCategories.map((label, i) => ({ label, value: s.data[i]?.value ?? 0 }))
4059
+ }));
4060
+ const x = axis?.x_label ?? xLabel;
4061
+ const y = axis?.y_label ?? yLabel;
4062
+ onChange({
4063
+ type: chart.type,
4064
+ series: aligned,
4065
+ axis_config: {
4066
+ categories: nextCategories,
4067
+ ...x ? { x_label: x } : {},
4068
+ ...y ? { y_label: y } : {}
4069
+ }
4070
+ });
4071
+ };
4072
+ const setCategory = (col, label) => commit(
4073
+ categories.map((c, i) => i === col ? label : c),
4074
+ series
4075
+ );
4076
+ const addCategory = () => commit([...categories, `Cat ${categories.length + 1}`], series);
4077
+ const removeCategory = (col) => commit(
4078
+ categories.filter((_, i) => i !== col),
4079
+ series.map((s) => ({ name: s.name, data: s.data.filter((_, i) => i !== col) }))
4080
+ );
4081
+ const setCell = (seriesIdx, col, value) => commit(
4082
+ categories,
4083
+ series.map(
4084
+ (s, i) => i === seriesIdx ? { name: s.name, data: s.data.map((d, j) => j === col ? { ...d, value } : d) } : s
4085
+ )
4086
+ );
4087
+ const setSeriesName = (seriesIdx, name) => commit(
4088
+ categories,
4089
+ series.map((s, i) => i === seriesIdx ? { ...s, name } : s)
4090
+ );
4091
+ const addSeries = () => commit(categories, [
4092
+ ...series,
4093
+ { name: `Series ${series.length + 1}`, data: categories.map((label) => ({ label, value: 0 })) }
4094
+ ]);
4095
+ const removeSeries = (seriesIdx) => commit(
4096
+ categories,
4097
+ series.filter((_, i) => i !== seriesIdx)
4098
+ );
4099
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
4100
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4101
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "X-axis label", htmlFor: "dataviz-x-label", children: /* @__PURE__ */ jsx(
4102
+ Input,
4103
+ {
4104
+ id: "dataviz-x-label",
4105
+ value: xLabel,
4106
+ placeholder: "e.g. Day",
4107
+ onChange: (e) => commit(categories, series, { x_label: e.target.value })
4108
+ }
4109
+ ) }) }),
4110
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "Y-axis label", htmlFor: "dataviz-y-label", children: /* @__PURE__ */ jsx(
4111
+ Input,
4112
+ {
4113
+ id: "dataviz-y-label",
4114
+ value: yLabel,
4115
+ placeholder: "e.g. Users",
4116
+ onChange: (e) => commit(categories, series, { y_label: e.target.value })
4117
+ }
4118
+ ) }) })
4119
+ ] }),
4120
+ /* @__PURE__ */ jsx(EditorField, { label: "Data", help: "Categories are the x-axis; each series is plotted across them.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 overflow-x-auto", children: [
4121
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
4122
+ /* @__PURE__ */ jsx("span", { className: "w-36 shrink-0", "aria-hidden": "true" }),
4123
+ categories.map((cat, c) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-[88px] flex-1 items-center gap-1", children: [
4124
+ /* @__PURE__ */ jsx(
4125
+ Input,
4126
+ {
4127
+ "aria-label": `Category ${c + 1}`,
4128
+ value: cat,
4129
+ placeholder: `Cat ${c + 1}`,
4130
+ onChange: (e) => setCategory(c, e.target.value),
4131
+ className: "h-8 text-xs"
4132
+ }
4133
+ ),
4134
+ /* @__PURE__ */ jsx(
4135
+ "button",
4136
+ {
4137
+ type: "button",
4138
+ "aria-label": `Remove category ${c + 1}`,
4139
+ onClick: () => removeCategory(c),
4140
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4141
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
4142
+ }
4143
+ )
4144
+ ] }, c))
4145
+ ] }),
4146
+ series.map((s, si) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
4147
+ /* @__PURE__ */ jsxs("div", { className: "flex w-36 shrink-0 items-center gap-1", children: [
4148
+ /* @__PURE__ */ jsx(
4149
+ Input,
4150
+ {
4151
+ "aria-label": `Series ${si + 1} name`,
4152
+ value: s.name,
4153
+ placeholder: `Series ${si + 1}`,
4154
+ onChange: (e) => setSeriesName(si, e.target.value),
4155
+ className: "h-8 text-xs"
4156
+ }
4157
+ ),
4158
+ /* @__PURE__ */ jsx(
4159
+ "button",
4160
+ {
4161
+ type: "button",
4162
+ "aria-label": `Remove series ${si + 1}`,
4163
+ onClick: () => removeSeries(si),
4164
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4165
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
4166
+ }
4167
+ )
4168
+ ] }),
4169
+ categories.map((cat, c) => /* @__PURE__ */ jsx("div", { className: "min-w-[88px] flex-1", children: /* @__PURE__ */ jsx(
4170
+ NumberInput,
4171
+ {
4172
+ value: s.data[c]?.value ?? 0,
4173
+ onCommit: (n) => setCell(si, c, n),
4174
+ ariaLabel: `${s.name || `Series ${si + 1}`} value for ${cat || `category ${c + 1}`}`,
4175
+ className: "h-8 text-xs"
4176
+ }
4177
+ ) }, c))
4178
+ ] }, si))
4179
+ ] }) }),
4180
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4181
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: addSeries, className: "self-start", children: [
4182
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4183
+ " Add series"
4184
+ ] }),
4185
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addCategory, className: "self-start", children: [
4186
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4187
+ " Add category"
4188
+ ] })
4189
+ ] })
4190
+ ] });
4191
+ }
4192
+ function PieFields({ chart, onChange }) {
4193
+ const segments = chart.segments;
4194
+ const setSegment = (idx, change) => onChange({ ...chart, segments: segments.map((s, i) => i === idx ? { ...s, ...change } : s) });
4195
+ const removeSegment = (idx) => onChange({ ...chart, segments: segments.filter((_, i) => i !== idx) });
4196
+ const addSegment = () => onChange({ ...chart, segments: [...segments, { label: `Segment ${segments.length + 1}`, value: 0 }] });
4197
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
4198
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: "Segments" }),
4199
+ segments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "No segments. Add one to render a slice." }) : null,
4200
+ segments.map((seg, idx) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded border bg-background p-2", children: [
4201
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
4202
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
4203
+ "Segment ",
4204
+ idx + 1
4205
+ ] }),
4206
+ /* @__PURE__ */ jsx(
4207
+ "button",
4208
+ {
4209
+ type: "button",
4210
+ "aria-label": "Remove segment",
4211
+ onClick: () => removeSegment(idx),
4212
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4213
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
4214
+ }
4215
+ )
4216
+ ] }),
4217
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4218
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: `pie-label-${idx}`, children: /* @__PURE__ */ jsx(
4219
+ Input,
4220
+ {
4221
+ id: `pie-label-${idx}`,
4222
+ value: seg.label,
4223
+ placeholder: "e.g. Free",
4224
+ onChange: (e) => setSegment(idx, { label: e.target.value })
4225
+ }
4226
+ ) }) }),
4227
+ /* @__PURE__ */ jsx("div", { className: "w-28", children: /* @__PURE__ */ jsx(EditorField, { label: "Value", htmlFor: `pie-value-${idx}`, children: /* @__PURE__ */ jsx(
4228
+ NumberInput,
4229
+ {
4230
+ id: `pie-value-${idx}`,
4231
+ value: seg.value,
4232
+ onCommit: (n) => setSegment(idx, { value: n }),
4233
+ ariaLabel: `Segment ${idx + 1} value`
4234
+ }
4235
+ ) }) })
4236
+ ] })
4237
+ ] }, idx)),
4238
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: addSegment, className: "self-start", children: [
4239
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4240
+ " Add segment"
4241
+ ] })
4242
+ ] });
4243
+ }
3803
4244
  function DividerEditor() {
3804
4245
  return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "A horizontal line that separates content. This block has no options to configure." });
3805
4246
  }
@@ -5248,7 +5689,7 @@ var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
5248
5689
  "rich_text_preformatted"
5249
5690
  ]);
5250
5691
  var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji"]);
5251
- var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "code"]);
5692
+ var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline", "code"]);
5252
5693
  var MAX_LIST_INDENT = 8;
5253
5694
  function detectLossy(block) {
5254
5695
  const out = [];
@@ -5432,6 +5873,9 @@ function styleToMarks(style) {
5432
5873
  if (style.strike) {
5433
5874
  marks.push({ type: "strike" });
5434
5875
  }
5876
+ if (style.underline) {
5877
+ marks.push({ type: "underline" });
5878
+ }
5435
5879
  if (style.code) {
5436
5880
  marks.push({ type: "code" });
5437
5881
  }
@@ -5578,6 +6022,9 @@ function marksToStyle(marks) {
5578
6022
  if (m.type === "strike") {
5579
6023
  style.strike = true;
5580
6024
  }
6025
+ if (m.type === "underline") {
6026
+ style.underline = true;
6027
+ }
5581
6028
  if (m.type === "code") {
5582
6029
  style.code = true;
5583
6030
  }
@@ -5665,7 +6112,7 @@ var INLINE_LABEL = {
5665
6112
  broadcast: "Broadcast",
5666
6113
  usergroup: "User group"
5667
6114
  };
5668
- var STYLE_KEYS = ["bold", "italic", "strike", "code"];
6115
+ var STYLE_KEYS = ["bold", "italic", "strike", "underline", "code"];
5669
6116
  function RichTextStructuredEditor({ block, onChange }) {
5670
6117
  const elements = block.elements ?? [];
5671
6118
  const replaceElements = (next) => {
@@ -6344,6 +6791,15 @@ function Toolbar({ editor }) {
6344
6791
  Icon: Strikethrough
6345
6792
  }
6346
6793
  ),
6794
+ /* @__PURE__ */ jsx(
6795
+ ToolbarButton,
6796
+ {
6797
+ label: "Underline",
6798
+ active: editor.isActive("underline"),
6799
+ onClick: () => editor.chain().focus().toggleUnderline().run(),
6800
+ Icon: Underline
6801
+ }
6802
+ ),
6347
6803
  /* @__PURE__ */ jsx(
6348
6804
  ToolbarButton,
6349
6805
  {
@@ -7226,6 +7682,8 @@ function dispatch(block, onChange) {
7226
7682
  return /* @__PURE__ */ jsx(PlanEditor, { block, onChange: (next) => onChange(next) });
7227
7683
  case "task_card":
7228
7684
  return /* @__PURE__ */ jsx(TaskCardEditor, { block, onChange: (next) => onChange(next) });
7685
+ case "data_visualization":
7686
+ return /* @__PURE__ */ jsx(DataVisualizationEditor, { block, onChange: (next) => onChange(next) });
7229
7687
  default:
7230
7688
  return null;
7231
7689
  }
@@ -7301,7 +7759,8 @@ var BLOCK_TYPE_LABELS = {
7301
7759
  input: "Input",
7302
7760
  video: "Video",
7303
7761
  plan: "Plan",
7304
- task_card: "Task Card"
7762
+ task_card: "Task Card",
7763
+ data_visualization: "Data Visualization"
7305
7764
  };
7306
7765
  function BlockRow({
7307
7766
  builderBlock,
@@ -7914,7 +8373,8 @@ function Toolbar2({
7914
8373
  allowedSurfaces,
7915
8374
  showThemeControl = true,
7916
8375
  docsLink,
7917
- errorCount
8376
+ errorCount,
8377
+ sendButtonLabel = "Send"
7918
8378
  }) {
7919
8379
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
7920
8380
  const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
@@ -8004,7 +8464,7 @@ function Toolbar2({
8004
8464
  variant: "ghost",
8005
8465
  size: "sm",
8006
8466
  onClick: onOpenIssues,
8007
- className: "text-destructive hover:text-destructive",
8467
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
8008
8468
  "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
8009
8469
  children: [
8010
8470
  /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5" }),
@@ -8097,9 +8557,9 @@ function Toolbar2({
8097
8557
  ) : null
8098
8558
  ] }) })
8099
8559
  ] }),
8100
- /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": "Send", children: [
8560
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
8101
8561
  /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
8102
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Send" })
8562
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
8103
8563
  ] })
8104
8564
  ] })
8105
8565
  ] });
@@ -8225,6 +8685,9 @@ function BlockKitchen(props) {
8225
8685
  showThemeControl = true,
8226
8686
  docsLink,
8227
8687
  defaultPreviewTheme = "light",
8688
+ previewTheme: controlledPreviewTheme,
8689
+ sendButtonLabel,
8690
+ confirmSendLabel,
8228
8691
  theme
8229
8692
  } = props;
8230
8693
  const paletteSections = useMemo(() => {
@@ -8252,7 +8715,9 @@ function BlockKitchen(props) {
8252
8715
  const [issuesOpen, setIssuesOpen] = useState(false);
8253
8716
  const [paletteOpen, setPaletteOpen] = useState(false);
8254
8717
  const [openBlockId, setOpenBlockId] = useState(null);
8255
- const [previewTheme, setPreviewTheme] = useState(defaultPreviewTheme);
8718
+ const [uncontrolledPreviewTheme, setUncontrolledPreviewTheme] = useState(defaultPreviewTheme);
8719
+ const isPreviewThemeControlled = controlledPreviewTheme !== void 0;
8720
+ const previewTheme = controlledPreviewTheme ?? uncontrolledPreviewTheme;
8256
8721
  const [previewSurface, setPreviewSurface] = useState(allowedSurfaces[0]);
8257
8722
  const [activePaletteVariantId, setActivePaletteVariantId] = useState(null);
8258
8723
  const isMobile = useIsMobile();
@@ -8334,12 +8799,13 @@ function BlockKitchen(props) {
8334
8799
  canClear: blocks.length > 0,
8335
8800
  errorCount: validation.total,
8336
8801
  previewTheme,
8337
- onPreviewThemeChange: setPreviewTheme,
8802
+ onPreviewThemeChange: setUncontrolledPreviewTheme,
8338
8803
  previewSurface,
8339
8804
  onPreviewSurfaceChange: setPreviewSurface,
8340
8805
  allowedSurfaces,
8341
- showThemeControl,
8342
- docsLink
8806
+ showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
8807
+ docsLink,
8808
+ sendButtonLabel
8343
8809
  }
8344
8810
  ),
8345
8811
  /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -8415,6 +8881,7 @@ function BlockKitchen(props) {
8415
8881
  loadChannels,
8416
8882
  loadSendAsUserStatus,
8417
8883
  onSend,
8884
+ confirmSendLabel,
8418
8885
  errorCount: validation.total,
8419
8886
  onShowIssues: () => {
8420
8887
  setSendOpen(false);