@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.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, Container, 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,186 @@ 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
+ ]
1124
+ },
1125
+ {
1126
+ name: "Container",
1127
+ icon: Container,
1128
+ variants: [
1129
+ {
1130
+ id: "container_collapsible",
1131
+ label: "Collapsible container",
1132
+ factory: () => ({
1133
+ type: "container",
1134
+ title: { type: "plain_text", text: "Bulk update: 2 records selected" },
1135
+ subtitle: { type: "plain_text", text: "Review changes before confirming" },
1136
+ is_collapsible: true,
1137
+ child_blocks: [
1138
+ {
1139
+ type: "section",
1140
+ text: { type: "mrkdwn", text: "*DCW-1024*\nStatus: Open \u2192 Closed" }
1141
+ },
1142
+ { type: "divider" },
1143
+ {
1144
+ type: "section",
1145
+ text: { type: "mrkdwn", text: "*DCW-1025*\nStatus: In Progress \u2192 Closed" }
1146
+ },
1147
+ {
1148
+ type: "actions",
1149
+ elements: [
1150
+ {
1151
+ type: "button",
1152
+ text: { type: "plain_text", text: "Confirm All", emoji: true },
1153
+ style: "primary",
1154
+ action_id: "bulk_confirm"
1155
+ },
1156
+ {
1157
+ type: "button",
1158
+ text: { type: "plain_text", text: "Cancel", emoji: true },
1159
+ action_id: "bulk_cancel"
1160
+ }
1161
+ ]
1162
+ }
1163
+ ]
1164
+ })
1165
+ }
1166
+ ]
987
1167
  }
988
1168
  ];
989
1169
  var legacyInputVariants = [
@@ -1462,6 +1642,10 @@ function labelForBlockType(type) {
1462
1642
  return "Plan";
1463
1643
  case "task_card":
1464
1644
  return "Task Card";
1645
+ case "data_visualization":
1646
+ return "Data Visualization";
1647
+ case "container":
1648
+ return "Container";
1465
1649
  }
1466
1650
  }
1467
1651
  function cn(...inputs) {
@@ -3376,6 +3560,160 @@ function CarouselEditor({ block, onChange }) {
3376
3560
  ] })
3377
3561
  ] });
3378
3562
  }
3563
+ var MAX_CHILDREN = 10;
3564
+ var WIDTHS = ["narrow", "standard", "wide", "full"];
3565
+ function ContainerEditor({ block, onChange }) {
3566
+ const children = block.child_blocks ?? [];
3567
+ const iconUrl = block.icon && "image_url" in block.icon ? block.icon.image_url ?? "" : "";
3568
+ const iconAlt = block.icon?.alt_text ?? "";
3569
+ const removeChildAt = (idx) => {
3570
+ onChange({ ...block, child_blocks: children.filter((_, i) => i !== idx) });
3571
+ };
3572
+ const addChild = () => {
3573
+ const child = {
3574
+ type: "section",
3575
+ text: { type: "mrkdwn", text: "New block." }
3576
+ };
3577
+ onChange({ ...block, child_blocks: [...children, child] });
3578
+ };
3579
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3580
+ /* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Heading shown at the top of the container.", htmlFor: "container-title", children: /* @__PURE__ */ jsx(
3581
+ Input,
3582
+ {
3583
+ id: "container-title",
3584
+ value: block.title?.text ?? "",
3585
+ maxLength: 150,
3586
+ placeholder: "e.g. Bulk update: 2 records selected",
3587
+ onChange: (e) => onChange({ ...block, title: { type: "plain_text", text: e.target.value } })
3588
+ }
3589
+ ) }),
3590
+ /* @__PURE__ */ jsx(EditorField, { label: "Subtitle", help: "Optional secondary line under the title.", htmlFor: "container-subtitle", children: /* @__PURE__ */ jsx(
3591
+ Input,
3592
+ {
3593
+ id: "container-subtitle",
3594
+ value: block.subtitle?.text ?? "",
3595
+ maxLength: 150,
3596
+ placeholder: "e.g. Review changes before confirming",
3597
+ onChange: (e) => onChange({
3598
+ ...block,
3599
+ subtitle: e.target.value ? { type: "plain_text", text: e.target.value } : void 0
3600
+ })
3601
+ }
3602
+ ) }),
3603
+ /* @__PURE__ */ jsx(EditorField, { label: "Icon URL", help: "Optional image shown left of the title.", htmlFor: "container-icon", children: /* @__PURE__ */ jsx(
3604
+ Input,
3605
+ {
3606
+ id: "container-icon",
3607
+ type: "url",
3608
+ value: iconUrl,
3609
+ placeholder: "https://example.com/icon.png",
3610
+ onChange: (e) => onChange({
3611
+ ...block,
3612
+ icon: e.target.value ? { type: "image", image_url: e.target.value, alt_text: iconAlt || "Container icon" } : void 0
3613
+ })
3614
+ }
3615
+ ) }),
3616
+ /* @__PURE__ */ jsx(EditorField, { label: "Width", help: "How wide the container renders in the message.", children: /* @__PURE__ */ jsx(
3617
+ RadioGroup,
3618
+ {
3619
+ value: block.width ?? "standard",
3620
+ onValueChange: (v) => onChange({ ...block, width: v }),
3621
+ className: "flex flex-row flex-wrap gap-3",
3622
+ children: WIDTHS.map((w) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3623
+ /* @__PURE__ */ jsx(RadioGroupItem, { value: w, id: `container-width-${w}` }),
3624
+ /* @__PURE__ */ jsx(Label, { htmlFor: `container-width-${w}`, className: "text-xs capitalize", children: w })
3625
+ ] }, w))
3626
+ }
3627
+ ) }),
3628
+ /* @__PURE__ */ jsx(
3629
+ Checkbox,
3630
+ {
3631
+ id: "container-collapsible",
3632
+ label: "Collapsible",
3633
+ help: "Let readers expand and collapse the container.",
3634
+ checked: !!block.is_collapsible,
3635
+ onChange: (checked) => onChange({
3636
+ ...block,
3637
+ is_collapsible: checked || void 0,
3638
+ default_collapsed: checked ? block.default_collapsed : void 0
3639
+ })
3640
+ }
3641
+ ),
3642
+ /* @__PURE__ */ jsx(
3643
+ Checkbox,
3644
+ {
3645
+ id: "container-default-collapsed",
3646
+ label: "Collapsed by default",
3647
+ help: "Start collapsed on first render. Requires Collapsible.",
3648
+ checked: !!block.default_collapsed,
3649
+ disabled: !block.is_collapsible,
3650
+ onChange: (checked) => onChange({ ...block, default_collapsed: checked || void 0 })
3651
+ }
3652
+ ),
3653
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3654
+ /* @__PURE__ */ jsx(Label, { children: "Child blocks" }),
3655
+ /* @__PURE__ */ 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." }),
3656
+ children.map((child, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border bg-muted/20 px-3 py-2", children: [
3657
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
3658
+ idx + 1,
3659
+ ". ",
3660
+ labelForBlockType(child.type)
3661
+ ] }),
3662
+ /* @__PURE__ */ jsx(
3663
+ "button",
3664
+ {
3665
+ type: "button",
3666
+ "aria-label": "Remove child block",
3667
+ onClick: () => removeChildAt(idx),
3668
+ disabled: children.length <= 1,
3669
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive disabled:cursor-not-allowed disabled:opacity-40",
3670
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
3671
+ }
3672
+ )
3673
+ ] }, idx)),
3674
+ /* @__PURE__ */ jsxs(
3675
+ Button,
3676
+ {
3677
+ type: "button",
3678
+ size: "sm",
3679
+ onClick: addChild,
3680
+ disabled: children.length >= MAX_CHILDREN,
3681
+ className: "self-start",
3682
+ children: [
3683
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
3684
+ " Add block"
3685
+ ]
3686
+ }
3687
+ )
3688
+ ] })
3689
+ ] });
3690
+ }
3691
+ function Checkbox({
3692
+ id,
3693
+ label,
3694
+ help,
3695
+ checked,
3696
+ disabled,
3697
+ onChange
3698
+ }) {
3699
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3700
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3701
+ /* @__PURE__ */ jsx(
3702
+ "input",
3703
+ {
3704
+ id,
3705
+ type: "checkbox",
3706
+ checked,
3707
+ disabled,
3708
+ onChange: (e) => onChange(e.target.checked),
3709
+ className: "h-3.5 w-3.5 cursor-pointer rounded border-input disabled:cursor-not-allowed disabled:opacity-40"
3710
+ }
3711
+ ),
3712
+ /* @__PURE__ */ jsx(Label, { htmlFor: id, className: "cursor-pointer text-xs", children: label })
3713
+ ] }),
3714
+ help ? /* @__PURE__ */ jsx("p", { className: "ml-5 text-[11px] leading-snug text-muted-foreground", children: help }) : null
3715
+ ] });
3716
+ }
3379
3717
  var MAX_ELEMENTS = 5;
3380
3718
  function ContextActionsEditor({ block, onChange }) {
3381
3719
  const elements = block.elements ?? [];
@@ -3814,6 +4152,294 @@ function ImageItemCard({
3814
4152
  ) })
3815
4153
  ] });
3816
4154
  }
4155
+ var CHART_TYPES = ["line", "bar", "area", "pie"];
4156
+ function isCartesian(chart) {
4157
+ return chart.type !== "pie";
4158
+ }
4159
+ function cartesianToSegments(chart) {
4160
+ return (chart.series[0]?.data ?? []).map((d) => ({ label: d.label, value: d.value }));
4161
+ }
4162
+ function segmentsToCartesian(type, segments) {
4163
+ return {
4164
+ type,
4165
+ series: [{ name: "Series 1", data: segments.map((s) => ({ label: s.label, value: s.value })) }],
4166
+ axis_config: { categories: segments.map((s) => s.label) }
4167
+ };
4168
+ }
4169
+ function convertChart(chart, nextType) {
4170
+ if (nextType === chart.type) {
4171
+ return chart;
4172
+ }
4173
+ if (nextType === "pie") {
4174
+ return { type: "pie", segments: isCartesian(chart) ? cartesianToSegments(chart) : chart.segments };
4175
+ }
4176
+ if (isCartesian(chart)) {
4177
+ return { ...chart, type: nextType };
4178
+ }
4179
+ return segmentsToCartesian(nextType, chart.segments);
4180
+ }
4181
+ function DataVisualizationEditor({ block, onChange }) {
4182
+ const { chart } = block;
4183
+ const setChart = (next) => onChange({ ...block, chart: next });
4184
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
4185
+ /* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Heading shown above the chart.", htmlFor: "dataviz-title", children: /* @__PURE__ */ jsx(
4186
+ Input,
4187
+ {
4188
+ id: "dataviz-title",
4189
+ value: block.title,
4190
+ placeholder: "e.g. Weekly active users",
4191
+ onChange: (e) => onChange({ ...block, title: e.target.value })
4192
+ }
4193
+ ) }),
4194
+ /* @__PURE__ */ jsx(EditorField, { label: "Chart type", children: /* @__PURE__ */ jsx(
4195
+ RadioGroup,
4196
+ {
4197
+ value: chart.type,
4198
+ onValueChange: (v) => setChart(convertChart(chart, v)),
4199
+ className: "flex flex-row flex-wrap gap-3",
4200
+ children: CHART_TYPES.map((t) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
4201
+ /* @__PURE__ */ jsx(RadioGroupItem, { value: t, id: `dataviz-type-${t}` }),
4202
+ /* @__PURE__ */ jsx(Label, { htmlFor: `dataviz-type-${t}`, className: "text-xs capitalize", children: t })
4203
+ ] }, t))
4204
+ }
4205
+ ) }),
4206
+ isCartesian(chart) ? /* @__PURE__ */ jsx(CartesianFields, { chart, onChange: setChart }) : /* @__PURE__ */ jsx(PieFields, { chart, onChange: setChart })
4207
+ ] });
4208
+ }
4209
+ function NumberInput({
4210
+ id,
4211
+ value,
4212
+ onCommit,
4213
+ ariaLabel,
4214
+ className
4215
+ }) {
4216
+ const [draft, setDraft] = useState(String(value));
4217
+ useEffect(() => {
4218
+ setDraft(String(value));
4219
+ }, [value]);
4220
+ return /* @__PURE__ */ jsx(
4221
+ Input,
4222
+ {
4223
+ id,
4224
+ type: "number",
4225
+ inputMode: "numeric",
4226
+ "aria-label": ariaLabel,
4227
+ value: draft,
4228
+ className,
4229
+ onChange: (e) => {
4230
+ const raw = e.target.value;
4231
+ setDraft(raw);
4232
+ if (raw === "" || raw === "-" || raw === "." || raw === "-.") {
4233
+ return;
4234
+ }
4235
+ const next = Number(raw);
4236
+ if (!Number.isNaN(next)) {
4237
+ onCommit(next);
4238
+ }
4239
+ },
4240
+ onBlur: () => {
4241
+ const next = Number(draft);
4242
+ const safe = draft.trim() === "" || Number.isNaN(next) ? 0 : next;
4243
+ onCommit(safe);
4244
+ setDraft(String(safe));
4245
+ }
4246
+ }
4247
+ );
4248
+ }
4249
+ function CartesianFields({ chart, onChange }) {
4250
+ const series = chart.series;
4251
+ const categories = chart.axis_config?.categories && chart.axis_config.categories.length > 0 ? chart.axis_config.categories : series[0]?.data.map((d) => d.label) ?? [];
4252
+ const xLabel = chart.axis_config?.x_label ?? "";
4253
+ const yLabel = chart.axis_config?.y_label ?? "";
4254
+ const commit = (nextCategories, nextSeries, axis) => {
4255
+ const aligned = nextSeries.map((s) => ({
4256
+ name: s.name,
4257
+ data: nextCategories.map((label, i) => ({ label, value: s.data[i]?.value ?? 0 }))
4258
+ }));
4259
+ const x = axis?.x_label ?? xLabel;
4260
+ const y = axis?.y_label ?? yLabel;
4261
+ onChange({
4262
+ type: chart.type,
4263
+ series: aligned,
4264
+ axis_config: {
4265
+ categories: nextCategories,
4266
+ ...x ? { x_label: x } : {},
4267
+ ...y ? { y_label: y } : {}
4268
+ }
4269
+ });
4270
+ };
4271
+ const setCategory = (col, label) => commit(
4272
+ categories.map((c, i) => i === col ? label : c),
4273
+ series
4274
+ );
4275
+ const addCategory = () => commit([...categories, `Cat ${categories.length + 1}`], series);
4276
+ const removeCategory = (col) => commit(
4277
+ categories.filter((_, i) => i !== col),
4278
+ series.map((s) => ({ name: s.name, data: s.data.filter((_, i) => i !== col) }))
4279
+ );
4280
+ const setCell = (seriesIdx, col, value) => commit(
4281
+ categories,
4282
+ series.map(
4283
+ (s, i) => i === seriesIdx ? { name: s.name, data: s.data.map((d, j) => j === col ? { ...d, value } : d) } : s
4284
+ )
4285
+ );
4286
+ const setSeriesName = (seriesIdx, name) => commit(
4287
+ categories,
4288
+ series.map((s, i) => i === seriesIdx ? { ...s, name } : s)
4289
+ );
4290
+ const addSeries = () => commit(categories, [
4291
+ ...series,
4292
+ { name: `Series ${series.length + 1}`, data: categories.map((label) => ({ label, value: 0 })) }
4293
+ ]);
4294
+ const removeSeries = (seriesIdx) => commit(
4295
+ categories,
4296
+ series.filter((_, i) => i !== seriesIdx)
4297
+ );
4298
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
4299
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4300
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "X-axis label", htmlFor: "dataviz-x-label", children: /* @__PURE__ */ jsx(
4301
+ Input,
4302
+ {
4303
+ id: "dataviz-x-label",
4304
+ value: xLabel,
4305
+ placeholder: "e.g. Day",
4306
+ onChange: (e) => commit(categories, series, { x_label: e.target.value })
4307
+ }
4308
+ ) }) }),
4309
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "Y-axis label", htmlFor: "dataviz-y-label", children: /* @__PURE__ */ jsx(
4310
+ Input,
4311
+ {
4312
+ id: "dataviz-y-label",
4313
+ value: yLabel,
4314
+ placeholder: "e.g. Users",
4315
+ onChange: (e) => commit(categories, series, { y_label: e.target.value })
4316
+ }
4317
+ ) }) })
4318
+ ] }),
4319
+ /* @__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: [
4320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
4321
+ /* @__PURE__ */ jsx("span", { className: "w-36 shrink-0", "aria-hidden": "true" }),
4322
+ categories.map((cat, c) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-[88px] flex-1 items-center gap-1", children: [
4323
+ /* @__PURE__ */ jsx(
4324
+ Input,
4325
+ {
4326
+ "aria-label": `Category ${c + 1}`,
4327
+ value: cat,
4328
+ placeholder: `Cat ${c + 1}`,
4329
+ onChange: (e) => setCategory(c, e.target.value),
4330
+ className: "h-8 text-xs"
4331
+ }
4332
+ ),
4333
+ /* @__PURE__ */ jsx(
4334
+ "button",
4335
+ {
4336
+ type: "button",
4337
+ "aria-label": `Remove category ${c + 1}`,
4338
+ onClick: () => removeCategory(c),
4339
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4340
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
4341
+ }
4342
+ )
4343
+ ] }, c))
4344
+ ] }),
4345
+ series.map((s, si) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
4346
+ /* @__PURE__ */ jsxs("div", { className: "flex w-36 shrink-0 items-center gap-1", children: [
4347
+ /* @__PURE__ */ jsx(
4348
+ Input,
4349
+ {
4350
+ "aria-label": `Series ${si + 1} name`,
4351
+ value: s.name,
4352
+ placeholder: `Series ${si + 1}`,
4353
+ onChange: (e) => setSeriesName(si, e.target.value),
4354
+ className: "h-8 text-xs"
4355
+ }
4356
+ ),
4357
+ /* @__PURE__ */ jsx(
4358
+ "button",
4359
+ {
4360
+ type: "button",
4361
+ "aria-label": `Remove series ${si + 1}`,
4362
+ onClick: () => removeSeries(si),
4363
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4364
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
4365
+ }
4366
+ )
4367
+ ] }),
4368
+ categories.map((cat, c) => /* @__PURE__ */ jsx("div", { className: "min-w-[88px] flex-1", children: /* @__PURE__ */ jsx(
4369
+ NumberInput,
4370
+ {
4371
+ value: s.data[c]?.value ?? 0,
4372
+ onCommit: (n) => setCell(si, c, n),
4373
+ ariaLabel: `${s.name || `Series ${si + 1}`} value for ${cat || `category ${c + 1}`}`,
4374
+ className: "h-8 text-xs"
4375
+ }
4376
+ ) }, c))
4377
+ ] }, si))
4378
+ ] }) }),
4379
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4380
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: addSeries, className: "self-start", children: [
4381
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4382
+ " Add series"
4383
+ ] }),
4384
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addCategory, className: "self-start", children: [
4385
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4386
+ " Add category"
4387
+ ] })
4388
+ ] })
4389
+ ] });
4390
+ }
4391
+ function PieFields({ chart, onChange }) {
4392
+ const segments = chart.segments;
4393
+ const setSegment = (idx, change) => onChange({ ...chart, segments: segments.map((s, i) => i === idx ? { ...s, ...change } : s) });
4394
+ const removeSegment = (idx) => onChange({ ...chart, segments: segments.filter((_, i) => i !== idx) });
4395
+ const addSegment = () => onChange({ ...chart, segments: [...segments, { label: `Segment ${segments.length + 1}`, value: 0 }] });
4396
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-md border bg-muted/20 p-3", children: [
4397
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: "Segments" }),
4398
+ segments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "No segments. Add one to render a slice." }) : null,
4399
+ segments.map((seg, idx) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded border bg-background p-2", children: [
4400
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
4401
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
4402
+ "Segment ",
4403
+ idx + 1
4404
+ ] }),
4405
+ /* @__PURE__ */ jsx(
4406
+ "button",
4407
+ {
4408
+ type: "button",
4409
+ "aria-label": "Remove segment",
4410
+ onClick: () => removeSegment(idx),
4411
+ className: "rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
4412
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
4413
+ }
4414
+ )
4415
+ ] }),
4416
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4417
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(EditorField, { label: "Label", htmlFor: `pie-label-${idx}`, children: /* @__PURE__ */ jsx(
4418
+ Input,
4419
+ {
4420
+ id: `pie-label-${idx}`,
4421
+ value: seg.label,
4422
+ placeholder: "e.g. Free",
4423
+ onChange: (e) => setSegment(idx, { label: e.target.value })
4424
+ }
4425
+ ) }) }),
4426
+ /* @__PURE__ */ jsx("div", { className: "w-28", children: /* @__PURE__ */ jsx(EditorField, { label: "Value", htmlFor: `pie-value-${idx}`, children: /* @__PURE__ */ jsx(
4427
+ NumberInput,
4428
+ {
4429
+ id: `pie-value-${idx}`,
4430
+ value: seg.value,
4431
+ onCommit: (n) => setSegment(idx, { value: n }),
4432
+ ariaLabel: `Segment ${idx + 1} value`
4433
+ }
4434
+ ) }) })
4435
+ ] })
4436
+ ] }, idx)),
4437
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", onClick: addSegment, className: "self-start", children: [
4438
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
4439
+ " Add segment"
4440
+ ] })
4441
+ ] });
4442
+ }
3817
4443
  function DividerEditor() {
3818
4444
  return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "A horizontal line that separates content. This block has no options to configure." });
3819
4445
  }
@@ -5262,7 +5888,7 @@ var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
5262
5888
  "rich_text_preformatted"
5263
5889
  ]);
5264
5890
  var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji"]);
5265
- var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "code"]);
5891
+ var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline", "code"]);
5266
5892
  var MAX_LIST_INDENT = 8;
5267
5893
  function detectLossy(block) {
5268
5894
  const out = [];
@@ -5446,6 +6072,9 @@ function styleToMarks(style) {
5446
6072
  if (style.strike) {
5447
6073
  marks.push({ type: "strike" });
5448
6074
  }
6075
+ if (style.underline) {
6076
+ marks.push({ type: "underline" });
6077
+ }
5449
6078
  if (style.code) {
5450
6079
  marks.push({ type: "code" });
5451
6080
  }
@@ -5592,6 +6221,9 @@ function marksToStyle(marks) {
5592
6221
  if (m.type === "strike") {
5593
6222
  style.strike = true;
5594
6223
  }
6224
+ if (m.type === "underline") {
6225
+ style.underline = true;
6226
+ }
5595
6227
  if (m.type === "code") {
5596
6228
  style.code = true;
5597
6229
  }
@@ -5679,7 +6311,7 @@ var INLINE_LABEL = {
5679
6311
  broadcast: "Broadcast",
5680
6312
  usergroup: "User group"
5681
6313
  };
5682
- var STYLE_KEYS = ["bold", "italic", "strike", "code"];
6314
+ var STYLE_KEYS = ["bold", "italic", "strike", "underline", "code"];
5683
6315
  function RichTextStructuredEditor({ block, onChange }) {
5684
6316
  const elements = block.elements ?? [];
5685
6317
  const replaceElements = (next) => {
@@ -6358,6 +6990,15 @@ function Toolbar({ editor }) {
6358
6990
  Icon: Strikethrough
6359
6991
  }
6360
6992
  ),
6993
+ /* @__PURE__ */ jsx(
6994
+ ToolbarButton,
6995
+ {
6996
+ label: "Underline",
6997
+ active: editor.isActive("underline"),
6998
+ onClick: () => editor.chain().focus().toggleUnderline().run(),
6999
+ Icon: Underline
7000
+ }
7001
+ ),
6361
7002
  /* @__PURE__ */ jsx(
6362
7003
  ToolbarButton,
6363
7004
  {
@@ -7240,6 +7881,10 @@ function dispatch(block, onChange) {
7240
7881
  return /* @__PURE__ */ jsx(PlanEditor, { block, onChange: (next) => onChange(next) });
7241
7882
  case "task_card":
7242
7883
  return /* @__PURE__ */ jsx(TaskCardEditor, { block, onChange: (next) => onChange(next) });
7884
+ case "data_visualization":
7885
+ return /* @__PURE__ */ jsx(DataVisualizationEditor, { block, onChange: (next) => onChange(next) });
7886
+ case "container":
7887
+ return /* @__PURE__ */ jsx(ContainerEditor, { block, onChange: (next) => onChange(next) });
7243
7888
  default:
7244
7889
  return null;
7245
7890
  }
@@ -7315,7 +7960,9 @@ var BLOCK_TYPE_LABELS = {
7315
7960
  input: "Input",
7316
7961
  video: "Video",
7317
7962
  plan: "Plan",
7318
- task_card: "Task Card"
7963
+ task_card: "Task Card",
7964
+ data_visualization: "Data Visualization",
7965
+ container: "Container"
7319
7966
  };
7320
7967
  function BlockRow({
7321
7968
  builderBlock,