@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.cjs CHANGED
@@ -1014,6 +1014,143 @@ var defaultPalette = [
1014
1014
  })
1015
1015
  }
1016
1016
  ]
1017
+ },
1018
+ {
1019
+ name: "Data Visualization",
1020
+ icon: lucideReact.ChartColumnBig,
1021
+ variants: [
1022
+ {
1023
+ id: "dataviz_line",
1024
+ label: "Line chart",
1025
+ factory: () => ({
1026
+ type: "data_visualization",
1027
+ title: "Weekly active users by platform",
1028
+ chart: {
1029
+ type: "line",
1030
+ series: [
1031
+ {
1032
+ name: "Desktop",
1033
+ data: [
1034
+ { label: "Mon", value: 800 },
1035
+ { label: "Tue", value: 920 },
1036
+ { label: "Wed", value: 880 },
1037
+ { label: "Thu", value: 1010 },
1038
+ { label: "Fri", value: 1120 }
1039
+ ]
1040
+ },
1041
+ {
1042
+ name: "Mobile",
1043
+ data: [
1044
+ { label: "Mon", value: 400 },
1045
+ { label: "Tue", value: 530 },
1046
+ { label: "Wed", value: 500 },
1047
+ { label: "Thu", value: 590 },
1048
+ { label: "Fri", value: 600 }
1049
+ ]
1050
+ }
1051
+ ],
1052
+ axis_config: {
1053
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1054
+ x_label: "Day",
1055
+ y_label: "Users"
1056
+ }
1057
+ }
1058
+ })
1059
+ },
1060
+ {
1061
+ id: "dataviz_bar",
1062
+ label: "Bar chart",
1063
+ factory: () => ({
1064
+ type: "data_visualization",
1065
+ title: "Messages sent by platform",
1066
+ chart: {
1067
+ type: "bar",
1068
+ series: [
1069
+ {
1070
+ name: "Desktop",
1071
+ data: [
1072
+ { label: "Mon", value: 1500 },
1073
+ { label: "Tue", value: 1400 },
1074
+ { label: "Wed", value: 1700 },
1075
+ { label: "Thu", value: 1600 },
1076
+ { label: "Fri", value: 1280 }
1077
+ ]
1078
+ },
1079
+ {
1080
+ name: "Mobile",
1081
+ data: [
1082
+ { label: "Mon", value: 800 },
1083
+ { label: "Tue", value: 750 },
1084
+ { label: "Wed", value: 920 },
1085
+ { label: "Thu", value: 880 },
1086
+ { label: "Fri", value: 700 }
1087
+ ]
1088
+ }
1089
+ ],
1090
+ axis_config: {
1091
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1092
+ x_label: "Day",
1093
+ y_label: "Messages"
1094
+ }
1095
+ }
1096
+ })
1097
+ },
1098
+ {
1099
+ id: "dataviz_area",
1100
+ label: "Area chart",
1101
+ factory: () => ({
1102
+ type: "data_visualization",
1103
+ title: "Concurrent users by platform",
1104
+ chart: {
1105
+ type: "area",
1106
+ series: [
1107
+ {
1108
+ name: "Desktop",
1109
+ data: [
1110
+ { label: "Mon", value: 2800 },
1111
+ { label: "Tue", value: 3e3 },
1112
+ { label: "Wed", value: 3400 },
1113
+ { label: "Thu", value: 3200 },
1114
+ { label: "Fri", value: 3500 }
1115
+ ]
1116
+ },
1117
+ {
1118
+ name: "Mobile",
1119
+ data: [
1120
+ { label: "Mon", value: 1400 },
1121
+ { label: "Tue", value: 1500 },
1122
+ { label: "Wed", value: 1700 },
1123
+ { label: "Thu", value: 1600 },
1124
+ { label: "Fri", value: 1800 }
1125
+ ]
1126
+ }
1127
+ ],
1128
+ axis_config: {
1129
+ categories: ["Mon", "Tue", "Wed", "Thu", "Fri"],
1130
+ x_label: "Day",
1131
+ y_label: "Users"
1132
+ }
1133
+ }
1134
+ })
1135
+ },
1136
+ {
1137
+ id: "dataviz_pie",
1138
+ label: "Pie chart",
1139
+ factory: () => ({
1140
+ type: "data_visualization",
1141
+ title: "Plan distribution by tier",
1142
+ chart: {
1143
+ type: "pie",
1144
+ segments: [
1145
+ { label: "Free", value: 4200 },
1146
+ { label: "Pro", value: 2300 },
1147
+ { label: "Business+", value: 1100 },
1148
+ { label: "Enterprise", value: 480 }
1149
+ ]
1150
+ }
1151
+ })
1152
+ }
1153
+ ]
1017
1154
  }
1018
1155
  ];
1019
1156
  var legacyInputVariants = [
@@ -1492,6 +1629,8 @@ function labelForBlockType(type) {
1492
1629
  return "Plan";
1493
1630
  case "task_card":
1494
1631
  return "Task Card";
1632
+ case "data_visualization":
1633
+ return "Data Visualization";
1495
1634
  }
1496
1635
  }
1497
1636
  function cn(...inputs) {
@@ -2274,6 +2413,18 @@ function IssuesSheet({
2274
2413
  ] })
2275
2414
  ] }) });
2276
2415
  }
2416
+
2417
+ // src/lib/parse-blocks-input.ts
2418
+ var BLOCKS_INPUT_SHAPE_ERROR = "Expected an array of blocks, or an object with a `blocks` array.";
2419
+ function unwrapBlocksInput(parsed) {
2420
+ if (Array.isArray(parsed)) {
2421
+ return parsed;
2422
+ }
2423
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.blocks)) {
2424
+ return parsed.blocks;
2425
+ }
2426
+ return null;
2427
+ }
2277
2428
  var MAX_JSON_BYTES = 1024 * 1024;
2278
2429
  function JsonDrawer({
2279
2430
  open,
@@ -2312,14 +2463,15 @@ function JsonDrawer({
2312
2463
  setValidationErrors([]);
2313
2464
  return;
2314
2465
  }
2315
- if (!Array.isArray(parsed)) {
2316
- setParseError("Top-level value must be an array of blocks.");
2466
+ const blocks2 = unwrapBlocksInput(parsed);
2467
+ if (!blocks2) {
2468
+ setParseError(BLOCKS_INPUT_SHAPE_ERROR);
2317
2469
  setValidationErrors([]);
2318
2470
  return;
2319
2471
  }
2320
2472
  setParseError(null);
2321
- onApply(parsed);
2322
- const result = slackBlockKitValidator.validateBlockKit(parsed, {
2473
+ onApply(blocks2);
2474
+ const result = slackBlockKitValidator.validateBlockKit(blocks2, {
2323
2475
  target: "blocks",
2324
2476
  surface: "message"
2325
2477
  });
@@ -2697,6 +2849,7 @@ function SendDialog({
2697
2849
  loadChannels,
2698
2850
  loadSendAsUserStatus,
2699
2851
  onSend,
2852
+ confirmSendLabel = "Send",
2700
2853
  errorCount,
2701
2854
  onShowIssues
2702
2855
  }) {
@@ -2870,7 +3023,7 @@ function SendDialog({
2870
3023
  type: "button",
2871
3024
  onClick: handleSubmit,
2872
3025
  disabled: status.kind === "sending" || !channelId || blocks.length === 0 || errorCount > 0,
2873
- children: status.kind === "sending" ? "Sending\u2026" : "Send"
3026
+ children: status.kind === "sending" ? "Sending\u2026" : confirmSendLabel
2874
3027
  }
2875
3028
  )
2876
3029
  ] })
@@ -3830,6 +3983,294 @@ function ImageItemCard({
3830
3983
  ) })
3831
3984
  ] });
3832
3985
  }
3986
+ var CHART_TYPES = ["line", "bar", "area", "pie"];
3987
+ function isCartesian(chart) {
3988
+ return chart.type !== "pie";
3989
+ }
3990
+ function cartesianToSegments(chart) {
3991
+ return (chart.series[0]?.data ?? []).map((d) => ({ label: d.label, value: d.value }));
3992
+ }
3993
+ function segmentsToCartesian(type, segments) {
3994
+ return {
3995
+ type,
3996
+ series: [{ name: "Series 1", data: segments.map((s) => ({ label: s.label, value: s.value })) }],
3997
+ axis_config: { categories: segments.map((s) => s.label) }
3998
+ };
3999
+ }
4000
+ function convertChart(chart, nextType) {
4001
+ if (nextType === chart.type) {
4002
+ return chart;
4003
+ }
4004
+ if (nextType === "pie") {
4005
+ return { type: "pie", segments: isCartesian(chart) ? cartesianToSegments(chart) : chart.segments };
4006
+ }
4007
+ if (isCartesian(chart)) {
4008
+ return { ...chart, type: nextType };
4009
+ }
4010
+ return segmentsToCartesian(nextType, chart.segments);
4011
+ }
4012
+ function DataVisualizationEditor({ block, onChange }) {
4013
+ const { chart } = block;
4014
+ const setChart = (next) => onChange({ ...block, chart: next });
4015
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
4016
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", help: "Heading shown above the chart.", htmlFor: "dataviz-title", children: /* @__PURE__ */ jsxRuntime.jsx(
4017
+ Input,
4018
+ {
4019
+ id: "dataviz-title",
4020
+ value: block.title,
4021
+ placeholder: "e.g. Weekly active users",
4022
+ onChange: (e) => onChange({ ...block, title: e.target.value })
4023
+ }
4024
+ ) }),
4025
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Chart type", children: /* @__PURE__ */ jsxRuntime.jsx(
4026
+ RadioGroup,
4027
+ {
4028
+ value: chart.type,
4029
+ onValueChange: (v) => setChart(convertChart(chart, v)),
4030
+ className: "flex flex-row flex-wrap gap-3",
4031
+ children: CHART_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
4032
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: t, id: `dataviz-type-${t}` }),
4033
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `dataviz-type-${t}`, className: "text-xs capitalize", children: t })
4034
+ ] }, t))
4035
+ }
4036
+ ) }),
4037
+ isCartesian(chart) ? /* @__PURE__ */ jsxRuntime.jsx(CartesianFields, { chart, onChange: setChart }) : /* @__PURE__ */ jsxRuntime.jsx(PieFields, { chart, onChange: setChart })
4038
+ ] });
4039
+ }
4040
+ function NumberInput({
4041
+ id,
4042
+ value,
4043
+ onCommit,
4044
+ ariaLabel,
4045
+ className
4046
+ }) {
4047
+ const [draft, setDraft] = React.useState(String(value));
4048
+ React.useEffect(() => {
4049
+ setDraft(String(value));
4050
+ }, [value]);
4051
+ return /* @__PURE__ */ jsxRuntime.jsx(
4052
+ Input,
4053
+ {
4054
+ id,
4055
+ type: "number",
4056
+ inputMode: "numeric",
4057
+ "aria-label": ariaLabel,
4058
+ value: draft,
4059
+ className,
4060
+ onChange: (e) => {
4061
+ const raw = e.target.value;
4062
+ setDraft(raw);
4063
+ if (raw === "" || raw === "-" || raw === "." || raw === "-.") {
4064
+ return;
4065
+ }
4066
+ const next = Number(raw);
4067
+ if (!Number.isNaN(next)) {
4068
+ onCommit(next);
4069
+ }
4070
+ },
4071
+ onBlur: () => {
4072
+ const next = Number(draft);
4073
+ const safe = draft.trim() === "" || Number.isNaN(next) ? 0 : next;
4074
+ onCommit(safe);
4075
+ setDraft(String(safe));
4076
+ }
4077
+ }
4078
+ );
4079
+ }
4080
+ function CartesianFields({ chart, onChange }) {
4081
+ const series = chart.series;
4082
+ const categories = chart.axis_config?.categories && chart.axis_config.categories.length > 0 ? chart.axis_config.categories : series[0]?.data.map((d) => d.label) ?? [];
4083
+ const xLabel = chart.axis_config?.x_label ?? "";
4084
+ const yLabel = chart.axis_config?.y_label ?? "";
4085
+ const commit = (nextCategories, nextSeries, axis) => {
4086
+ const aligned = nextSeries.map((s) => ({
4087
+ name: s.name,
4088
+ data: nextCategories.map((label, i) => ({ label, value: s.data[i]?.value ?? 0 }))
4089
+ }));
4090
+ const x = axis?.x_label ?? xLabel;
4091
+ const y = axis?.y_label ?? yLabel;
4092
+ onChange({
4093
+ type: chart.type,
4094
+ series: aligned,
4095
+ axis_config: {
4096
+ categories: nextCategories,
4097
+ ...x ? { x_label: x } : {},
4098
+ ...y ? { y_label: y } : {}
4099
+ }
4100
+ });
4101
+ };
4102
+ const setCategory = (col, label) => commit(
4103
+ categories.map((c, i) => i === col ? label : c),
4104
+ series
4105
+ );
4106
+ const addCategory = () => commit([...categories, `Cat ${categories.length + 1}`], series);
4107
+ const removeCategory = (col) => commit(
4108
+ categories.filter((_, i) => i !== col),
4109
+ series.map((s) => ({ name: s.name, data: s.data.filter((_, i) => i !== col) }))
4110
+ );
4111
+ const setCell = (seriesIdx, col, value) => commit(
4112
+ categories,
4113
+ series.map(
4114
+ (s, i) => i === seriesIdx ? { name: s.name, data: s.data.map((d, j) => j === col ? { ...d, value } : d) } : s
4115
+ )
4116
+ );
4117
+ const setSeriesName = (seriesIdx, name) => commit(
4118
+ categories,
4119
+ series.map((s, i) => i === seriesIdx ? { ...s, name } : s)
4120
+ );
4121
+ const addSeries = () => commit(categories, [
4122
+ ...series,
4123
+ { name: `Series ${series.length + 1}`, data: categories.map((label) => ({ label, value: 0 })) }
4124
+ ]);
4125
+ const removeSeries = (seriesIdx) => commit(
4126
+ categories,
4127
+ series.filter((_, i) => i !== seriesIdx)
4128
+ );
4129
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
4130
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4131
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "X-axis label", htmlFor: "dataviz-x-label", children: /* @__PURE__ */ jsxRuntime.jsx(
4132
+ Input,
4133
+ {
4134
+ id: "dataviz-x-label",
4135
+ value: xLabel,
4136
+ placeholder: "e.g. Day",
4137
+ onChange: (e) => commit(categories, series, { x_label: e.target.value })
4138
+ }
4139
+ ) }) }),
4140
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Y-axis label", htmlFor: "dataviz-y-label", children: /* @__PURE__ */ jsxRuntime.jsx(
4141
+ Input,
4142
+ {
4143
+ id: "dataviz-y-label",
4144
+ value: yLabel,
4145
+ placeholder: "e.g. Users",
4146
+ onChange: (e) => commit(categories, series, { y_label: e.target.value })
4147
+ }
4148
+ ) }) })
4149
+ ] }),
4150
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Data", help: "Categories are the x-axis; each series is plotted across them.", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 overflow-x-auto", children: [
4151
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
4152
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-36 shrink-0", "aria-hidden": "true" }),
4153
+ categories.map((cat, c) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-[88px] flex-1 items-center gap-1", children: [
4154
+ /* @__PURE__ */ jsxRuntime.jsx(
4155
+ Input,
4156
+ {
4157
+ "aria-label": `Category ${c + 1}`,
4158
+ value: cat,
4159
+ placeholder: `Cat ${c + 1}`,
4160
+ onChange: (e) => setCategory(c, e.target.value),
4161
+ className: "h-8 text-xs"
4162
+ }
4163
+ ),
4164
+ /* @__PURE__ */ jsxRuntime.jsx(
4165
+ "button",
4166
+ {
4167
+ type: "button",
4168
+ "aria-label": `Remove category ${c + 1}`,
4169
+ onClick: () => removeCategory(c),
4170
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4171
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
4172
+ }
4173
+ )
4174
+ ] }, c))
4175
+ ] }),
4176
+ series.map((s, si) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
4177
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-36 shrink-0 items-center gap-1", children: [
4178
+ /* @__PURE__ */ jsxRuntime.jsx(
4179
+ Input,
4180
+ {
4181
+ "aria-label": `Series ${si + 1} name`,
4182
+ value: s.name,
4183
+ placeholder: `Series ${si + 1}`,
4184
+ onChange: (e) => setSeriesName(si, e.target.value),
4185
+ className: "h-8 text-xs"
4186
+ }
4187
+ ),
4188
+ /* @__PURE__ */ jsxRuntime.jsx(
4189
+ "button",
4190
+ {
4191
+ type: "button",
4192
+ "aria-label": `Remove series ${si + 1}`,
4193
+ onClick: () => removeSeries(si),
4194
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4195
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
4196
+ }
4197
+ )
4198
+ ] }),
4199
+ categories.map((cat, c) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[88px] flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
4200
+ NumberInput,
4201
+ {
4202
+ value: s.data[c]?.value ?? 0,
4203
+ onCommit: (n) => setCell(si, c, n),
4204
+ ariaLabel: `${s.name || `Series ${si + 1}`} value for ${cat || `category ${c + 1}`}`,
4205
+ className: "h-8 text-xs"
4206
+ }
4207
+ ) }, c))
4208
+ ] }, si))
4209
+ ] }) }),
4210
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4211
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: addSeries, className: "self-start", children: [
4212
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4213
+ " Add series"
4214
+ ] }),
4215
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addCategory, className: "self-start", children: [
4216
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4217
+ " Add category"
4218
+ ] })
4219
+ ] })
4220
+ ] });
4221
+ }
4222
+ function PieFields({ chart, onChange }) {
4223
+ const segments = chart.segments;
4224
+ const setSegment = (idx, change) => onChange({ ...chart, segments: segments.map((s, i) => i === idx ? { ...s, ...change } : s) });
4225
+ const removeSegment = (idx) => onChange({ ...chart, segments: segments.filter((_, i) => i !== idx) });
4226
+ const addSegment = () => onChange({ ...chart, segments: [...segments, { label: `Segment ${segments.length + 1}`, value: 0 }] });
4227
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
4228
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: "Segments" }),
4229
+ segments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "No segments. Add one to render a slice." }) : null,
4230
+ segments.map((seg, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded border bg-background p-2", children: [
4231
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4232
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground", children: [
4233
+ "Segment ",
4234
+ idx + 1
4235
+ ] }),
4236
+ /* @__PURE__ */ jsxRuntime.jsx(
4237
+ "button",
4238
+ {
4239
+ type: "button",
4240
+ "aria-label": "Remove segment",
4241
+ onClick: () => removeSegment(idx),
4242
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4243
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
4244
+ }
4245
+ )
4246
+ ] }),
4247
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4248
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Label", htmlFor: `pie-label-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
4249
+ Input,
4250
+ {
4251
+ id: `pie-label-${idx}`,
4252
+ value: seg.label,
4253
+ placeholder: "e.g. Free",
4254
+ onChange: (e) => setSegment(idx, { label: e.target.value })
4255
+ }
4256
+ ) }) }),
4257
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Value", htmlFor: `pie-value-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
4258
+ NumberInput,
4259
+ {
4260
+ id: `pie-value-${idx}`,
4261
+ value: seg.value,
4262
+ onCommit: (n) => setSegment(idx, { value: n }),
4263
+ ariaLabel: `Segment ${idx + 1} value`
4264
+ }
4265
+ ) }) })
4266
+ ] })
4267
+ ] }, idx)),
4268
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: addSegment, className: "self-start", children: [
4269
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4270
+ " Add segment"
4271
+ ] })
4272
+ ] });
4273
+ }
3833
4274
  function DividerEditor() {
3834
4275
  return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "A horizontal line that separates content. This block has no options to configure." });
3835
4276
  }
@@ -5278,7 +5719,7 @@ var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
5278
5719
  "rich_text_preformatted"
5279
5720
  ]);
5280
5721
  var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji"]);
5281
- var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "code"]);
5722
+ var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline", "code"]);
5282
5723
  var MAX_LIST_INDENT = 8;
5283
5724
  function detectLossy(block) {
5284
5725
  const out = [];
@@ -5462,6 +5903,9 @@ function styleToMarks(style) {
5462
5903
  if (style.strike) {
5463
5904
  marks.push({ type: "strike" });
5464
5905
  }
5906
+ if (style.underline) {
5907
+ marks.push({ type: "underline" });
5908
+ }
5465
5909
  if (style.code) {
5466
5910
  marks.push({ type: "code" });
5467
5911
  }
@@ -5608,6 +6052,9 @@ function marksToStyle(marks) {
5608
6052
  if (m.type === "strike") {
5609
6053
  style.strike = true;
5610
6054
  }
6055
+ if (m.type === "underline") {
6056
+ style.underline = true;
6057
+ }
5611
6058
  if (m.type === "code") {
5612
6059
  style.code = true;
5613
6060
  }
@@ -5695,7 +6142,7 @@ var INLINE_LABEL = {
5695
6142
  broadcast: "Broadcast",
5696
6143
  usergroup: "User group"
5697
6144
  };
5698
- var STYLE_KEYS = ["bold", "italic", "strike", "code"];
6145
+ var STYLE_KEYS = ["bold", "italic", "strike", "underline", "code"];
5699
6146
  function RichTextStructuredEditor({ block, onChange }) {
5700
6147
  const elements = block.elements ?? [];
5701
6148
  const replaceElements = (next) => {
@@ -6374,6 +6821,15 @@ function Toolbar({ editor }) {
6374
6821
  Icon: lucideReact.Strikethrough
6375
6822
  }
6376
6823
  ),
6824
+ /* @__PURE__ */ jsxRuntime.jsx(
6825
+ ToolbarButton,
6826
+ {
6827
+ label: "Underline",
6828
+ active: editor.isActive("underline"),
6829
+ onClick: () => editor.chain().focus().toggleUnderline().run(),
6830
+ Icon: lucideReact.Underline
6831
+ }
6832
+ ),
6377
6833
  /* @__PURE__ */ jsxRuntime.jsx(
6378
6834
  ToolbarButton,
6379
6835
  {
@@ -7256,6 +7712,8 @@ function dispatch(block, onChange) {
7256
7712
  return /* @__PURE__ */ jsxRuntime.jsx(PlanEditor, { block, onChange: (next) => onChange(next) });
7257
7713
  case "task_card":
7258
7714
  return /* @__PURE__ */ jsxRuntime.jsx(TaskCardEditor, { block, onChange: (next) => onChange(next) });
7715
+ case "data_visualization":
7716
+ return /* @__PURE__ */ jsxRuntime.jsx(DataVisualizationEditor, { block, onChange: (next) => onChange(next) });
7259
7717
  default:
7260
7718
  return null;
7261
7719
  }
@@ -7331,7 +7789,8 @@ var BLOCK_TYPE_LABELS = {
7331
7789
  input: "Input",
7332
7790
  video: "Video",
7333
7791
  plan: "Plan",
7334
- task_card: "Task Card"
7792
+ task_card: "Task Card",
7793
+ data_visualization: "Data Visualization"
7335
7794
  };
7336
7795
  function BlockRow({
7337
7796
  builderBlock,
@@ -7944,7 +8403,8 @@ function Toolbar2({
7944
8403
  allowedSurfaces,
7945
8404
  showThemeControl = true,
7946
8405
  docsLink,
7947
- errorCount
8406
+ errorCount,
8407
+ sendButtonLabel = "Send"
7948
8408
  }) {
7949
8409
  const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
7950
8410
  const activeSurface = SURFACE_OPTIONS.find((s) => s.value === previewSurface) ?? SURFACE_OPTIONS[0];
@@ -8034,7 +8494,7 @@ function Toolbar2({
8034
8494
  variant: "ghost",
8035
8495
  size: "sm",
8036
8496
  onClick: onOpenIssues,
8037
- className: "text-destructive hover:text-destructive",
8497
+ className: "border border-red-200! bg-red-50 text-red-700 hover:bg-red-100 hover:text-red-700",
8038
8498
  "aria-label": `${errorCount} ${errorCount === 1 ? "issue" : "issues"}`,
8039
8499
  children: [
8040
8500
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5" }),
@@ -8127,9 +8587,9 @@ function Toolbar2({
8127
8587
  ) : null
8128
8588
  ] }) })
8129
8589
  ] }),
8130
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": "Send", children: [
8590
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenSend, disabled: !canSend, "aria-label": sendButtonLabel, children: [
8131
8591
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-3.5 w-3.5" }),
8132
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Send" })
8592
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: sendButtonLabel })
8133
8593
  ] })
8134
8594
  ] })
8135
8595
  ] });
@@ -8255,6 +8715,9 @@ function BlockKitchen(props) {
8255
8715
  showThemeControl = true,
8256
8716
  docsLink,
8257
8717
  defaultPreviewTheme = "light",
8718
+ previewTheme: controlledPreviewTheme,
8719
+ sendButtonLabel,
8720
+ confirmSendLabel,
8258
8721
  theme
8259
8722
  } = props;
8260
8723
  const paletteSections = React.useMemo(() => {
@@ -8282,7 +8745,9 @@ function BlockKitchen(props) {
8282
8745
  const [issuesOpen, setIssuesOpen] = React.useState(false);
8283
8746
  const [paletteOpen, setPaletteOpen] = React.useState(false);
8284
8747
  const [openBlockId, setOpenBlockId] = React.useState(null);
8285
- const [previewTheme, setPreviewTheme] = React.useState(defaultPreviewTheme);
8748
+ const [uncontrolledPreviewTheme, setUncontrolledPreviewTheme] = React.useState(defaultPreviewTheme);
8749
+ const isPreviewThemeControlled = controlledPreviewTheme !== void 0;
8750
+ const previewTheme = controlledPreviewTheme ?? uncontrolledPreviewTheme;
8286
8751
  const [previewSurface, setPreviewSurface] = React.useState(allowedSurfaces[0]);
8287
8752
  const [activePaletteVariantId, setActivePaletteVariantId] = React.useState(null);
8288
8753
  const isMobile = useIsMobile();
@@ -8364,12 +8829,13 @@ function BlockKitchen(props) {
8364
8829
  canClear: blocks.length > 0,
8365
8830
  errorCount: validation.total,
8366
8831
  previewTheme,
8367
- onPreviewThemeChange: setPreviewTheme,
8832
+ onPreviewThemeChange: setUncontrolledPreviewTheme,
8368
8833
  previewSurface,
8369
8834
  onPreviewSurfaceChange: setPreviewSurface,
8370
8835
  allowedSurfaces,
8371
- showThemeControl,
8372
- docsLink
8836
+ showThemeControl: isPreviewThemeControlled ? false : showThemeControl,
8837
+ docsLink,
8838
+ sendButtonLabel
8373
8839
  }
8374
8840
  ),
8375
8841
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 items-stretch", children: [
@@ -8445,6 +8911,7 @@ function BlockKitchen(props) {
8445
8911
  loadChannels,
8446
8912
  loadSendAsUserStatus,
8447
8913
  onSend,
8914
+ confirmSendLabel,
8448
8915
  errorCount: validation.total,
8449
8916
  onShowIssues: () => {
8450
8917
  setSendOpen(false);