@tightknitai/block-kitchen 0.8.6 → 0.8.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/dist/index.cjs CHANGED
@@ -1014,6 +1014,186 @@ 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
+ ]
1154
+ },
1155
+ {
1156
+ name: "Container",
1157
+ icon: lucideReact.Container,
1158
+ variants: [
1159
+ {
1160
+ id: "container_collapsible",
1161
+ label: "Collapsible container",
1162
+ factory: () => ({
1163
+ type: "container",
1164
+ title: { type: "plain_text", text: "Bulk update: 2 records selected" },
1165
+ subtitle: { type: "plain_text", text: "Review changes before confirming" },
1166
+ is_collapsible: true,
1167
+ child_blocks: [
1168
+ {
1169
+ type: "section",
1170
+ text: { type: "mrkdwn", text: "*DCW-1024*\nStatus: Open \u2192 Closed" }
1171
+ },
1172
+ { type: "divider" },
1173
+ {
1174
+ type: "section",
1175
+ text: { type: "mrkdwn", text: "*DCW-1025*\nStatus: In Progress \u2192 Closed" }
1176
+ },
1177
+ {
1178
+ type: "actions",
1179
+ elements: [
1180
+ {
1181
+ type: "button",
1182
+ text: { type: "plain_text", text: "Confirm All", emoji: true },
1183
+ style: "primary",
1184
+ action_id: "bulk_confirm"
1185
+ },
1186
+ {
1187
+ type: "button",
1188
+ text: { type: "plain_text", text: "Cancel", emoji: true },
1189
+ action_id: "bulk_cancel"
1190
+ }
1191
+ ]
1192
+ }
1193
+ ]
1194
+ })
1195
+ }
1196
+ ]
1017
1197
  }
1018
1198
  ];
1019
1199
  var legacyInputVariants = [
@@ -1492,6 +1672,10 @@ function labelForBlockType(type) {
1492
1672
  return "Plan";
1493
1673
  case "task_card":
1494
1674
  return "Task Card";
1675
+ case "data_visualization":
1676
+ return "Data Visualization";
1677
+ case "container":
1678
+ return "Container";
1495
1679
  }
1496
1680
  }
1497
1681
  function cn(...inputs) {
@@ -3406,6 +3590,160 @@ function CarouselEditor({ block, onChange }) {
3406
3590
  ] })
3407
3591
  ] });
3408
3592
  }
3593
+ var MAX_CHILDREN = 10;
3594
+ var WIDTHS = ["narrow", "standard", "wide", "full"];
3595
+ function ContainerEditor({ block, onChange }) {
3596
+ const children = block.child_blocks ?? [];
3597
+ const iconUrl = block.icon && "image_url" in block.icon ? block.icon.image_url ?? "" : "";
3598
+ const iconAlt = block.icon?.alt_text ?? "";
3599
+ const removeChildAt = (idx) => {
3600
+ onChange({ ...block, child_blocks: children.filter((_, i) => i !== idx) });
3601
+ };
3602
+ const addChild = () => {
3603
+ const child = {
3604
+ type: "section",
3605
+ text: { type: "mrkdwn", text: "New block." }
3606
+ };
3607
+ onChange({ ...block, child_blocks: [...children, child] });
3608
+ };
3609
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
3610
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", help: "Heading shown at the top of the container.", htmlFor: "container-title", children: /* @__PURE__ */ jsxRuntime.jsx(
3611
+ Input,
3612
+ {
3613
+ id: "container-title",
3614
+ value: block.title?.text ?? "",
3615
+ maxLength: 150,
3616
+ placeholder: "e.g. Bulk update: 2 records selected",
3617
+ onChange: (e) => onChange({ ...block, title: { type: "plain_text", text: e.target.value } })
3618
+ }
3619
+ ) }),
3620
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Subtitle", help: "Optional secondary line under the title.", htmlFor: "container-subtitle", children: /* @__PURE__ */ jsxRuntime.jsx(
3621
+ Input,
3622
+ {
3623
+ id: "container-subtitle",
3624
+ value: block.subtitle?.text ?? "",
3625
+ maxLength: 150,
3626
+ placeholder: "e.g. Review changes before confirming",
3627
+ onChange: (e) => onChange({
3628
+ ...block,
3629
+ subtitle: e.target.value ? { type: "plain_text", text: e.target.value } : void 0
3630
+ })
3631
+ }
3632
+ ) }),
3633
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Icon URL", help: "Optional image shown left of the title.", htmlFor: "container-icon", children: /* @__PURE__ */ jsxRuntime.jsx(
3634
+ Input,
3635
+ {
3636
+ id: "container-icon",
3637
+ type: "url",
3638
+ value: iconUrl,
3639
+ placeholder: "https://example.com/icon.png",
3640
+ onChange: (e) => onChange({
3641
+ ...block,
3642
+ icon: e.target.value ? { type: "image", image_url: e.target.value, alt_text: iconAlt || "Container icon" } : void 0
3643
+ })
3644
+ }
3645
+ ) }),
3646
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Width", help: "How wide the container renders in the message.", children: /* @__PURE__ */ jsxRuntime.jsx(
3647
+ RadioGroup,
3648
+ {
3649
+ value: block.width ?? "standard",
3650
+ onValueChange: (v) => onChange({ ...block, width: v }),
3651
+ className: "flex flex-row flex-wrap gap-3",
3652
+ children: WIDTHS.map((w) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3653
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: w, id: `container-width-${w}` }),
3654
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `container-width-${w}`, className: "text-xs capitalize", children: w })
3655
+ ] }, w))
3656
+ }
3657
+ ) }),
3658
+ /* @__PURE__ */ jsxRuntime.jsx(
3659
+ Checkbox,
3660
+ {
3661
+ id: "container-collapsible",
3662
+ label: "Collapsible",
3663
+ help: "Let readers expand and collapse the container.",
3664
+ checked: !!block.is_collapsible,
3665
+ onChange: (checked) => onChange({
3666
+ ...block,
3667
+ is_collapsible: checked || void 0,
3668
+ default_collapsed: checked ? block.default_collapsed : void 0
3669
+ })
3670
+ }
3671
+ ),
3672
+ /* @__PURE__ */ jsxRuntime.jsx(
3673
+ Checkbox,
3674
+ {
3675
+ id: "container-default-collapsed",
3676
+ label: "Collapsed by default",
3677
+ help: "Start collapsed on first render. Requires Collapsible.",
3678
+ checked: !!block.default_collapsed,
3679
+ disabled: !block.is_collapsible,
3680
+ onChange: (checked) => onChange({ ...block, default_collapsed: checked || void 0 })
3681
+ }
3682
+ ),
3683
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3684
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Child blocks" }),
3685
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "A container holds 1-10 blocks. To edit a child's content, use the View JSON drawer." }),
3686
+ children.map((child, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border bg-muted/20 px-3 py-2", children: [
3687
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground", children: [
3688
+ idx + 1,
3689
+ ". ",
3690
+ labelForBlockType(child.type)
3691
+ ] }),
3692
+ /* @__PURE__ */ jsxRuntime.jsx(
3693
+ "button",
3694
+ {
3695
+ type: "button",
3696
+ "aria-label": "Remove child block",
3697
+ onClick: () => removeChildAt(idx),
3698
+ disabled: children.length <= 1,
3699
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive disabled:cursor-not-allowed disabled:opacity-40",
3700
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
3701
+ }
3702
+ )
3703
+ ] }, idx)),
3704
+ /* @__PURE__ */ jsxRuntime.jsxs(
3705
+ Button,
3706
+ {
3707
+ type: "button",
3708
+ size: "sm",
3709
+ onClick: addChild,
3710
+ disabled: children.length >= MAX_CHILDREN,
3711
+ className: "self-start",
3712
+ children: [
3713
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
3714
+ " Add block"
3715
+ ]
3716
+ }
3717
+ )
3718
+ ] })
3719
+ ] });
3720
+ }
3721
+ function Checkbox({
3722
+ id,
3723
+ label,
3724
+ help,
3725
+ checked,
3726
+ disabled,
3727
+ onChange
3728
+ }) {
3729
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
3730
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3731
+ /* @__PURE__ */ jsxRuntime.jsx(
3732
+ "input",
3733
+ {
3734
+ id,
3735
+ type: "checkbox",
3736
+ checked,
3737
+ disabled,
3738
+ onChange: (e) => onChange(e.target.checked),
3739
+ className: "h-3.5 w-3.5 cursor-pointer rounded border-input disabled:cursor-not-allowed disabled:opacity-40"
3740
+ }
3741
+ ),
3742
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: id, className: "cursor-pointer text-xs", children: label })
3743
+ ] }),
3744
+ help ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ml-5 text-[11px] leading-snug text-muted-foreground", children: help }) : null
3745
+ ] });
3746
+ }
3409
3747
  var MAX_ELEMENTS = 5;
3410
3748
  function ContextActionsEditor({ block, onChange }) {
3411
3749
  const elements = block.elements ?? [];
@@ -3844,6 +4182,294 @@ function ImageItemCard({
3844
4182
  ) })
3845
4183
  ] });
3846
4184
  }
4185
+ var CHART_TYPES = ["line", "bar", "area", "pie"];
4186
+ function isCartesian(chart) {
4187
+ return chart.type !== "pie";
4188
+ }
4189
+ function cartesianToSegments(chart) {
4190
+ return (chart.series[0]?.data ?? []).map((d) => ({ label: d.label, value: d.value }));
4191
+ }
4192
+ function segmentsToCartesian(type, segments) {
4193
+ return {
4194
+ type,
4195
+ series: [{ name: "Series 1", data: segments.map((s) => ({ label: s.label, value: s.value })) }],
4196
+ axis_config: { categories: segments.map((s) => s.label) }
4197
+ };
4198
+ }
4199
+ function convertChart(chart, nextType) {
4200
+ if (nextType === chart.type) {
4201
+ return chart;
4202
+ }
4203
+ if (nextType === "pie") {
4204
+ return { type: "pie", segments: isCartesian(chart) ? cartesianToSegments(chart) : chart.segments };
4205
+ }
4206
+ if (isCartesian(chart)) {
4207
+ return { ...chart, type: nextType };
4208
+ }
4209
+ return segmentsToCartesian(nextType, chart.segments);
4210
+ }
4211
+ function DataVisualizationEditor({ block, onChange }) {
4212
+ const { chart } = block;
4213
+ const setChart = (next) => onChange({ ...block, chart: next });
4214
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
4215
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Title", help: "Heading shown above the chart.", htmlFor: "dataviz-title", children: /* @__PURE__ */ jsxRuntime.jsx(
4216
+ Input,
4217
+ {
4218
+ id: "dataviz-title",
4219
+ value: block.title,
4220
+ placeholder: "e.g. Weekly active users",
4221
+ onChange: (e) => onChange({ ...block, title: e.target.value })
4222
+ }
4223
+ ) }),
4224
+ /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Chart type", children: /* @__PURE__ */ jsxRuntime.jsx(
4225
+ RadioGroup,
4226
+ {
4227
+ value: chart.type,
4228
+ onValueChange: (v) => setChart(convertChart(chart, v)),
4229
+ className: "flex flex-row flex-wrap gap-3",
4230
+ children: CHART_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
4231
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: t, id: `dataviz-type-${t}` }),
4232
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `dataviz-type-${t}`, className: "text-xs capitalize", children: t })
4233
+ ] }, t))
4234
+ }
4235
+ ) }),
4236
+ isCartesian(chart) ? /* @__PURE__ */ jsxRuntime.jsx(CartesianFields, { chart, onChange: setChart }) : /* @__PURE__ */ jsxRuntime.jsx(PieFields, { chart, onChange: setChart })
4237
+ ] });
4238
+ }
4239
+ function NumberInput({
4240
+ id,
4241
+ value,
4242
+ onCommit,
4243
+ ariaLabel,
4244
+ className
4245
+ }) {
4246
+ const [draft, setDraft] = React.useState(String(value));
4247
+ React.useEffect(() => {
4248
+ setDraft(String(value));
4249
+ }, [value]);
4250
+ return /* @__PURE__ */ jsxRuntime.jsx(
4251
+ Input,
4252
+ {
4253
+ id,
4254
+ type: "number",
4255
+ inputMode: "numeric",
4256
+ "aria-label": ariaLabel,
4257
+ value: draft,
4258
+ className,
4259
+ onChange: (e) => {
4260
+ const raw = e.target.value;
4261
+ setDraft(raw);
4262
+ if (raw === "" || raw === "-" || raw === "." || raw === "-.") {
4263
+ return;
4264
+ }
4265
+ const next = Number(raw);
4266
+ if (!Number.isNaN(next)) {
4267
+ onCommit(next);
4268
+ }
4269
+ },
4270
+ onBlur: () => {
4271
+ const next = Number(draft);
4272
+ const safe = draft.trim() === "" || Number.isNaN(next) ? 0 : next;
4273
+ onCommit(safe);
4274
+ setDraft(String(safe));
4275
+ }
4276
+ }
4277
+ );
4278
+ }
4279
+ function CartesianFields({ chart, onChange }) {
4280
+ const series = chart.series;
4281
+ const categories = chart.axis_config?.categories && chart.axis_config.categories.length > 0 ? chart.axis_config.categories : series[0]?.data.map((d) => d.label) ?? [];
4282
+ const xLabel = chart.axis_config?.x_label ?? "";
4283
+ const yLabel = chart.axis_config?.y_label ?? "";
4284
+ const commit = (nextCategories, nextSeries, axis) => {
4285
+ const aligned = nextSeries.map((s) => ({
4286
+ name: s.name,
4287
+ data: nextCategories.map((label, i) => ({ label, value: s.data[i]?.value ?? 0 }))
4288
+ }));
4289
+ const x = axis?.x_label ?? xLabel;
4290
+ const y = axis?.y_label ?? yLabel;
4291
+ onChange({
4292
+ type: chart.type,
4293
+ series: aligned,
4294
+ axis_config: {
4295
+ categories: nextCategories,
4296
+ ...x ? { x_label: x } : {},
4297
+ ...y ? { y_label: y } : {}
4298
+ }
4299
+ });
4300
+ };
4301
+ const setCategory = (col, label) => commit(
4302
+ categories.map((c, i) => i === col ? label : c),
4303
+ series
4304
+ );
4305
+ const addCategory = () => commit([...categories, `Cat ${categories.length + 1}`], series);
4306
+ const removeCategory = (col) => commit(
4307
+ categories.filter((_, i) => i !== col),
4308
+ series.map((s) => ({ name: s.name, data: s.data.filter((_, i) => i !== col) }))
4309
+ );
4310
+ const setCell = (seriesIdx, col, value) => commit(
4311
+ categories,
4312
+ series.map(
4313
+ (s, i) => i === seriesIdx ? { name: s.name, data: s.data.map((d, j) => j === col ? { ...d, value } : d) } : s
4314
+ )
4315
+ );
4316
+ const setSeriesName = (seriesIdx, name) => commit(
4317
+ categories,
4318
+ series.map((s, i) => i === seriesIdx ? { ...s, name } : s)
4319
+ );
4320
+ const addSeries = () => commit(categories, [
4321
+ ...series,
4322
+ { name: `Series ${series.length + 1}`, data: categories.map((label) => ({ label, value: 0 })) }
4323
+ ]);
4324
+ const removeSeries = (seriesIdx) => commit(
4325
+ categories,
4326
+ series.filter((_, i) => i !== seriesIdx)
4327
+ );
4328
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
4329
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4330
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "X-axis label", htmlFor: "dataviz-x-label", children: /* @__PURE__ */ jsxRuntime.jsx(
4331
+ Input,
4332
+ {
4333
+ id: "dataviz-x-label",
4334
+ value: xLabel,
4335
+ placeholder: "e.g. Day",
4336
+ onChange: (e) => commit(categories, series, { x_label: e.target.value })
4337
+ }
4338
+ ) }) }),
4339
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Y-axis label", htmlFor: "dataviz-y-label", children: /* @__PURE__ */ jsxRuntime.jsx(
4340
+ Input,
4341
+ {
4342
+ id: "dataviz-y-label",
4343
+ value: yLabel,
4344
+ placeholder: "e.g. Users",
4345
+ onChange: (e) => commit(categories, series, { y_label: e.target.value })
4346
+ }
4347
+ ) }) })
4348
+ ] }),
4349
+ /* @__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: [
4350
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
4351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-36 shrink-0", "aria-hidden": "true" }),
4352
+ categories.map((cat, c) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-[88px] flex-1 items-center gap-1", children: [
4353
+ /* @__PURE__ */ jsxRuntime.jsx(
4354
+ Input,
4355
+ {
4356
+ "aria-label": `Category ${c + 1}`,
4357
+ value: cat,
4358
+ placeholder: `Cat ${c + 1}`,
4359
+ onChange: (e) => setCategory(c, e.target.value),
4360
+ className: "h-8 text-xs"
4361
+ }
4362
+ ),
4363
+ /* @__PURE__ */ jsxRuntime.jsx(
4364
+ "button",
4365
+ {
4366
+ type: "button",
4367
+ "aria-label": `Remove category ${c + 1}`,
4368
+ onClick: () => removeCategory(c),
4369
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4370
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
4371
+ }
4372
+ )
4373
+ ] }, c))
4374
+ ] }),
4375
+ series.map((s, si) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
4376
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-36 shrink-0 items-center gap-1", children: [
4377
+ /* @__PURE__ */ jsxRuntime.jsx(
4378
+ Input,
4379
+ {
4380
+ "aria-label": `Series ${si + 1} name`,
4381
+ value: s.name,
4382
+ placeholder: `Series ${si + 1}`,
4383
+ onChange: (e) => setSeriesName(si, e.target.value),
4384
+ className: "h-8 text-xs"
4385
+ }
4386
+ ),
4387
+ /* @__PURE__ */ jsxRuntime.jsx(
4388
+ "button",
4389
+ {
4390
+ type: "button",
4391
+ "aria-label": `Remove series ${si + 1}`,
4392
+ onClick: () => removeSeries(si),
4393
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4394
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
4395
+ }
4396
+ )
4397
+ ] }),
4398
+ categories.map((cat, c) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[88px] flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
4399
+ NumberInput,
4400
+ {
4401
+ value: s.data[c]?.value ?? 0,
4402
+ onCommit: (n) => setCell(si, c, n),
4403
+ ariaLabel: `${s.name || `Series ${si + 1}`} value for ${cat || `category ${c + 1}`}`,
4404
+ className: "h-8 text-xs"
4405
+ }
4406
+ ) }, c))
4407
+ ] }, si))
4408
+ ] }) }),
4409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4410
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: addSeries, className: "self-start", children: [
4411
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4412
+ " Add series"
4413
+ ] }),
4414
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addCategory, className: "self-start", children: [
4415
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4416
+ " Add category"
4417
+ ] })
4418
+ ] })
4419
+ ] });
4420
+ }
4421
+ function PieFields({ chart, onChange }) {
4422
+ const segments = chart.segments;
4423
+ const setSegment = (idx, change) => onChange({ ...chart, segments: segments.map((s, i) => i === idx ? { ...s, ...change } : s) });
4424
+ const removeSegment = (idx) => onChange({ ...chart, segments: segments.filter((_, i) => i !== idx) });
4425
+ const addSegment = () => onChange({ ...chart, segments: [...segments, { label: `Segment ${segments.length + 1}`, value: 0 }] });
4426
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
4427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: "Segments" }),
4428
+ 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,
4429
+ segments.map((seg, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded border bg-background p-2", children: [
4430
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4431
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground", children: [
4432
+ "Segment ",
4433
+ idx + 1
4434
+ ] }),
4435
+ /* @__PURE__ */ jsxRuntime.jsx(
4436
+ "button",
4437
+ {
4438
+ type: "button",
4439
+ "aria-label": "Remove segment",
4440
+ onClick: () => removeSegment(idx),
4441
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4442
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
4443
+ }
4444
+ )
4445
+ ] }),
4446
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
4447
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Label", htmlFor: `pie-label-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
4448
+ Input,
4449
+ {
4450
+ id: `pie-label-${idx}`,
4451
+ value: seg.label,
4452
+ placeholder: "e.g. Free",
4453
+ onChange: (e) => setSegment(idx, { label: e.target.value })
4454
+ }
4455
+ ) }) }),
4456
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28", children: /* @__PURE__ */ jsxRuntime.jsx(EditorField, { label: "Value", htmlFor: `pie-value-${idx}`, children: /* @__PURE__ */ jsxRuntime.jsx(
4457
+ NumberInput,
4458
+ {
4459
+ id: `pie-value-${idx}`,
4460
+ value: seg.value,
4461
+ onCommit: (n) => setSegment(idx, { value: n }),
4462
+ ariaLabel: `Segment ${idx + 1} value`
4463
+ }
4464
+ ) }) })
4465
+ ] })
4466
+ ] }, idx)),
4467
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: addSegment, className: "self-start", children: [
4468
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
4469
+ " Add segment"
4470
+ ] })
4471
+ ] });
4472
+ }
3847
4473
  function DividerEditor() {
3848
4474
  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." });
3849
4475
  }
@@ -5292,7 +5918,7 @@ var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
5292
5918
  "rich_text_preformatted"
5293
5919
  ]);
5294
5920
  var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji"]);
5295
- var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "code"]);
5921
+ var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline", "code"]);
5296
5922
  var MAX_LIST_INDENT = 8;
5297
5923
  function detectLossy(block) {
5298
5924
  const out = [];
@@ -5476,6 +6102,9 @@ function styleToMarks(style) {
5476
6102
  if (style.strike) {
5477
6103
  marks.push({ type: "strike" });
5478
6104
  }
6105
+ if (style.underline) {
6106
+ marks.push({ type: "underline" });
6107
+ }
5479
6108
  if (style.code) {
5480
6109
  marks.push({ type: "code" });
5481
6110
  }
@@ -5622,6 +6251,9 @@ function marksToStyle(marks) {
5622
6251
  if (m.type === "strike") {
5623
6252
  style.strike = true;
5624
6253
  }
6254
+ if (m.type === "underline") {
6255
+ style.underline = true;
6256
+ }
5625
6257
  if (m.type === "code") {
5626
6258
  style.code = true;
5627
6259
  }
@@ -5709,7 +6341,7 @@ var INLINE_LABEL = {
5709
6341
  broadcast: "Broadcast",
5710
6342
  usergroup: "User group"
5711
6343
  };
5712
- var STYLE_KEYS = ["bold", "italic", "strike", "code"];
6344
+ var STYLE_KEYS = ["bold", "italic", "strike", "underline", "code"];
5713
6345
  function RichTextStructuredEditor({ block, onChange }) {
5714
6346
  const elements = block.elements ?? [];
5715
6347
  const replaceElements = (next) => {
@@ -6388,6 +7020,15 @@ function Toolbar({ editor }) {
6388
7020
  Icon: lucideReact.Strikethrough
6389
7021
  }
6390
7022
  ),
7023
+ /* @__PURE__ */ jsxRuntime.jsx(
7024
+ ToolbarButton,
7025
+ {
7026
+ label: "Underline",
7027
+ active: editor.isActive("underline"),
7028
+ onClick: () => editor.chain().focus().toggleUnderline().run(),
7029
+ Icon: lucideReact.Underline
7030
+ }
7031
+ ),
6391
7032
  /* @__PURE__ */ jsxRuntime.jsx(
6392
7033
  ToolbarButton,
6393
7034
  {
@@ -7270,6 +7911,10 @@ function dispatch(block, onChange) {
7270
7911
  return /* @__PURE__ */ jsxRuntime.jsx(PlanEditor, { block, onChange: (next) => onChange(next) });
7271
7912
  case "task_card":
7272
7913
  return /* @__PURE__ */ jsxRuntime.jsx(TaskCardEditor, { block, onChange: (next) => onChange(next) });
7914
+ case "data_visualization":
7915
+ return /* @__PURE__ */ jsxRuntime.jsx(DataVisualizationEditor, { block, onChange: (next) => onChange(next) });
7916
+ case "container":
7917
+ return /* @__PURE__ */ jsxRuntime.jsx(ContainerEditor, { block, onChange: (next) => onChange(next) });
7273
7918
  default:
7274
7919
  return null;
7275
7920
  }
@@ -7345,7 +7990,9 @@ var BLOCK_TYPE_LABELS = {
7345
7990
  input: "Input",
7346
7991
  video: "Video",
7347
7992
  plan: "Plan",
7348
- task_card: "Task Card"
7993
+ task_card: "Task Card",
7994
+ data_visualization: "Data Visualization",
7995
+ container: "Container"
7349
7996
  };
7350
7997
  function BlockRow({
7351
7998
  builderBlock,