@hex-core/components 1.6.0 → 1.8.0
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/_tsup-dts-rollup.d.ts +1597 -0
- package/dist/arc.d.ts +4 -0
- package/dist/arc.js +147 -0
- package/dist/arc.js.map +1 -0
- package/dist/audio-player.d.ts +2 -0
- package/dist/audio-player.js +119 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/audio-waveform.d.ts +2 -0
- package/dist/audio-waveform.js +72 -0
- package/dist/audio-waveform.js.map +1 -0
- package/dist/canvas.d.ts +2 -0
- package/dist/canvas.js +73 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chord.d.ts +4 -0
- package/dist/chord.js +230 -0
- package/dist/chord.js.map +1 -0
- package/dist/cloze.d.ts +3 -0
- package/dist/cloze.js +98 -0
- package/dist/cloze.js.map +1 -0
- package/dist/compare-table.d.ts +4 -0
- package/dist/compare-table.js +109 -0
- package/dist/compare-table.js.map +1 -0
- package/dist/deck.d.ts +3 -0
- package/dist/deck.js +231 -0
- package/dist/deck.js.map +1 -0
- package/dist/dendrogram.d.ts +3 -0
- package/dist/dendrogram.js +162 -0
- package/dist/dendrogram.js.map +1 -0
- package/dist/diagram.d.ts +2 -0
- package/dist/diagram.js +70 -0
- package/dist/diagram.js.map +1 -0
- package/dist/flashcard.d.ts +2 -0
- package/dist/flashcard.js +107 -0
- package/dist/flashcard.js.map +1 -0
- package/dist/flowchart.d.ts +4 -0
- package/dist/flowchart.js +275 -0
- package/dist/flowchart.js.map +1 -0
- package/dist/funnel.d.ts +3 -0
- package/dist/funnel.js +157 -0
- package/dist/funnel.js.map +1 -0
- package/dist/gantt.d.ts +3 -0
- package/dist/gantt.js +279 -0
- package/dist/gantt.js.map +1 -0
- package/dist/image-occlusion.d.ts +3 -0
- package/dist/image-occlusion.js +106 -0
- package/dist/image-occlusion.js.map +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +4085 -2
- package/dist/index.js.map +1 -1
- package/dist/matrix.d.ts +3 -0
- package/dist/matrix.js +155 -0
- package/dist/matrix.js.map +1 -0
- package/dist/mind-map.d.ts +3 -0
- package/dist/mind-map.js +167 -0
- package/dist/mind-map.js.map +1 -0
- package/dist/org-chart.d.ts +3 -0
- package/dist/org-chart.js +215 -0
- package/dist/org-chart.js.map +1 -0
- package/dist/pyramid.d.ts +3 -0
- package/dist/pyramid.js +150 -0
- package/dist/pyramid.js.map +1 -0
- package/dist/quiz.d.ts +3 -0
- package/dist/quiz.js +128 -0
- package/dist/quiz.js.map +1 -0
- package/dist/sankey.d.ts +4 -0
- package/dist/sankey.js +190 -0
- package/dist/sankey.js.map +1 -0
- package/dist/schemas.d.ts +23 -0
- package/dist/schemas.js +2211 -4
- package/dist/schemas.js.map +1 -1
- package/dist/sequence.d.ts +4 -0
- package/dist/sequence.js +229 -0
- package/dist/sequence.js.map +1 -0
- package/dist/spaced-repetition.d.ts +3 -0
- package/dist/spaced-repetition.js +73 -0
- package/dist/spaced-repetition.js.map +1 -0
- package/dist/speech-recognition.d.ts +2 -0
- package/dist/speech-recognition.js +152 -0
- package/dist/speech-recognition.js.map +1 -0
- package/dist/sunburst.d.ts +3 -0
- package/dist/sunburst.js +205 -0
- package/dist/sunburst.js.map +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +153 -0
- package/dist/terminal.js.map +1 -0
- package/dist/time-axis.d.ts +3 -0
- package/dist/time-axis.js +233 -0
- package/dist/time-axis.js.map +1 -0
- package/dist/tool-call.js +6 -1
- package/dist/tool-call.js.map +1 -1
- package/dist/tree-map.d.ts +3 -0
- package/dist/tree-map.js +171 -0
- package/dist/tree-map.js.map +1 -0
- package/dist/venn.d.ts +3 -0
- package/dist/venn.js +196 -0
- package/dist/venn.js.map +1 -0
- package/package.json +47 -3
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function TimeAxis({
|
|
11
|
+
events,
|
|
12
|
+
start,
|
|
13
|
+
end,
|
|
14
|
+
width = 720,
|
|
15
|
+
height = 200,
|
|
16
|
+
tickCount = 6,
|
|
17
|
+
onEventClick,
|
|
18
|
+
className,
|
|
19
|
+
...rest
|
|
20
|
+
}) {
|
|
21
|
+
const laidOut = React.useMemo(
|
|
22
|
+
() => layout(events, start, end, width, height, tickCount),
|
|
23
|
+
[events, start, end, width, height, tickCount]
|
|
24
|
+
);
|
|
25
|
+
const desc = events.length === 0 ? "Empty time axis" : `Time axis with ${events.length} event${events.length === 1 ? "" : "s"}, range ${laidOut.startLabel} to ${laidOut.endLabel}`;
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"svg",
|
|
28
|
+
{
|
|
29
|
+
...rest,
|
|
30
|
+
"data-hex-time-axis": true,
|
|
31
|
+
role: "img",
|
|
32
|
+
width,
|
|
33
|
+
height,
|
|
34
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
35
|
+
className: cn("block", className),
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ jsx("title", { children: "Time axis" }),
|
|
38
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
39
|
+
/* @__PURE__ */ jsxs("g", { "data-hex-time-axis-axis": true, children: [
|
|
40
|
+
/* @__PURE__ */ jsx(
|
|
41
|
+
"line",
|
|
42
|
+
{
|
|
43
|
+
x1: laidOut.axisLeft,
|
|
44
|
+
x2: laidOut.axisRight,
|
|
45
|
+
y1: laidOut.axisY,
|
|
46
|
+
y2: laidOut.axisY,
|
|
47
|
+
stroke: "hsl(var(--muted-foreground))",
|
|
48
|
+
strokeWidth: 1
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
laidOut.ticks.map((tick, i) => /* @__PURE__ */ jsxs("g", { "data-hex-time-axis-tick": true, children: [
|
|
52
|
+
/* @__PURE__ */ jsx(
|
|
53
|
+
"line",
|
|
54
|
+
{
|
|
55
|
+
x1: tick.x,
|
|
56
|
+
x2: tick.x,
|
|
57
|
+
y1: laidOut.axisY - 4,
|
|
58
|
+
y2: laidOut.axisY + 4,
|
|
59
|
+
stroke: "hsl(var(--muted-foreground))",
|
|
60
|
+
strokeWidth: 1
|
|
61
|
+
}
|
|
62
|
+
),
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
"text",
|
|
65
|
+
{
|
|
66
|
+
x: tick.x,
|
|
67
|
+
y: laidOut.axisY + 18,
|
|
68
|
+
textAnchor: "middle",
|
|
69
|
+
fontSize: 10,
|
|
70
|
+
fill: "hsl(var(--muted-foreground))",
|
|
71
|
+
children: tick.label
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
] }, `tick-${i}`))
|
|
75
|
+
] }),
|
|
76
|
+
/* @__PURE__ */ jsx("g", { "data-hex-time-axis-events": true, children: laidOut.events.map((e) => {
|
|
77
|
+
const interactive = Boolean(onEventClick);
|
|
78
|
+
const handleActivate = () => onEventClick?.(e.event);
|
|
79
|
+
return /* @__PURE__ */ jsxs(
|
|
80
|
+
"g",
|
|
81
|
+
{
|
|
82
|
+
"data-hex-time-axis-event": true,
|
|
83
|
+
"data-row": e.rowIndex,
|
|
84
|
+
role: interactive ? "button" : void 0,
|
|
85
|
+
tabIndex: interactive ? 0 : void 0,
|
|
86
|
+
"aria-label": interactive ? `${e.event.label} on ${formatDate(toDate(e.event.date))}` : void 0,
|
|
87
|
+
style: interactive ? { cursor: "pointer" } : void 0,
|
|
88
|
+
onClick: interactive ? handleActivate : void 0,
|
|
89
|
+
onKeyDown: interactive ? (k) => activateOnKey(k, handleActivate) : void 0,
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
"line",
|
|
93
|
+
{
|
|
94
|
+
x1: e.x,
|
|
95
|
+
x2: e.x,
|
|
96
|
+
y1: e.y,
|
|
97
|
+
y2: laidOut.axisY,
|
|
98
|
+
stroke: "hsl(var(--muted-foreground))",
|
|
99
|
+
strokeOpacity: 0.65,
|
|
100
|
+
strokeWidth: 1
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ jsx(
|
|
104
|
+
"circle",
|
|
105
|
+
{
|
|
106
|
+
cx: e.x,
|
|
107
|
+
cy: e.y,
|
|
108
|
+
r: 5,
|
|
109
|
+
fill: "hsl(var(--primary))",
|
|
110
|
+
stroke: "hsl(var(--background))",
|
|
111
|
+
strokeWidth: 2
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
"text",
|
|
116
|
+
{
|
|
117
|
+
x: e.x + 8,
|
|
118
|
+
y: e.y + 4,
|
|
119
|
+
fontSize: 11,
|
|
120
|
+
fill: "hsl(var(--foreground))",
|
|
121
|
+
style: { paintOrder: "stroke", pointerEvents: "none" },
|
|
122
|
+
stroke: "hsl(var(--background))",
|
|
123
|
+
strokeWidth: 3,
|
|
124
|
+
strokeLinejoin: "round",
|
|
125
|
+
children: e.event.label
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
e.event.id
|
|
131
|
+
);
|
|
132
|
+
}) })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
function layout(events, start, end, width, height, tickCount) {
|
|
138
|
+
const margin = 32;
|
|
139
|
+
const axisLeft = margin;
|
|
140
|
+
const axisRight = width - margin;
|
|
141
|
+
const axisY = height - 40;
|
|
142
|
+
const usableWidth = axisRight - axisLeft;
|
|
143
|
+
const validEvents = events.filter((e) => Number.isFinite(toDate(e.date).getTime()));
|
|
144
|
+
if (validEvents.length === 0) {
|
|
145
|
+
return {
|
|
146
|
+
events: [],
|
|
147
|
+
ticks: [],
|
|
148
|
+
axisY,
|
|
149
|
+
axisLeft,
|
|
150
|
+
axisRight,
|
|
151
|
+
startLabel: "\u2014",
|
|
152
|
+
endLabel: "\u2014"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const dates = validEvents.map((e) => toDate(e.date).getTime());
|
|
156
|
+
const explicitMin = start != null ? toDate(start).getTime() : NaN;
|
|
157
|
+
const explicitMax = end != null ? toDate(end).getTime() : NaN;
|
|
158
|
+
const minTs = Number.isFinite(explicitMin) ? explicitMin : Math.min(...dates);
|
|
159
|
+
const maxTs = Number.isFinite(explicitMax) ? explicitMax : Math.max(...dates);
|
|
160
|
+
const span = Math.max(1, maxTs - minTs);
|
|
161
|
+
const tToX = (t) => axisLeft + (t - minTs) / span * usableWidth;
|
|
162
|
+
const MIN_GAP_PX = 48;
|
|
163
|
+
const ROW_HEIGHT_PX = 22;
|
|
164
|
+
const maxRows = Math.max(1, Math.floor((axisY - 10 - 24) / ROW_HEIGHT_PX) + 1);
|
|
165
|
+
const rowsLastX = [];
|
|
166
|
+
const positioned = validEvents.map((event) => ({ event, t: toDate(event.date).getTime() })).sort((a, b) => a.t - b.t || a.event.id.localeCompare(b.event.id)).map(({ event, t }) => {
|
|
167
|
+
const x = tToX(t);
|
|
168
|
+
let rowIndex = rowsLastX.findIndex((lastX) => x - lastX >= MIN_GAP_PX);
|
|
169
|
+
if (rowIndex === -1) {
|
|
170
|
+
if (rowsLastX.length < maxRows) {
|
|
171
|
+
rowIndex = rowsLastX.length;
|
|
172
|
+
rowsLastX.push(x);
|
|
173
|
+
} else {
|
|
174
|
+
rowIndex = maxRows - 1;
|
|
175
|
+
rowsLastX[rowIndex] = x;
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
rowsLastX[rowIndex] = x;
|
|
179
|
+
}
|
|
180
|
+
return { event, x, y: 24 + rowIndex * ROW_HEIGHT_PX, rowIndex };
|
|
181
|
+
});
|
|
182
|
+
const rawTicks = [];
|
|
183
|
+
for (let i = 0; i < tickCount; i++) {
|
|
184
|
+
const t = minTs + i / Math.max(1, tickCount - 1) * span;
|
|
185
|
+
rawTicks.push({ x: tToX(t), label: formatTick(new Date(t), span) });
|
|
186
|
+
}
|
|
187
|
+
const ticks = rawTicks.map((t, i) => ({
|
|
188
|
+
x: t.x,
|
|
189
|
+
label: i > 0 && rawTicks[i - 1]?.label === t.label ? "" : t.label
|
|
190
|
+
}));
|
|
191
|
+
return {
|
|
192
|
+
events: positioned,
|
|
193
|
+
ticks,
|
|
194
|
+
axisY,
|
|
195
|
+
axisLeft,
|
|
196
|
+
axisRight,
|
|
197
|
+
startLabel: formatDate(new Date(minTs)),
|
|
198
|
+
endLabel: formatDate(new Date(maxTs))
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function toDate(v) {
|
|
202
|
+
if (v instanceof Date) return v;
|
|
203
|
+
if (typeof v === "number") return new Date(v);
|
|
204
|
+
return new Date(v);
|
|
205
|
+
}
|
|
206
|
+
function formatDate(d) {
|
|
207
|
+
if (Number.isNaN(d.getTime())) return "\u2014";
|
|
208
|
+
return d.toISOString().slice(0, 10);
|
|
209
|
+
}
|
|
210
|
+
function formatTick(d, spanMs) {
|
|
211
|
+
if (Number.isNaN(d.getTime())) return "\u2014";
|
|
212
|
+
const ONE_DAY = 24 * 60 * 60 * 1e3;
|
|
213
|
+
if (spanMs <= 7 * ONE_DAY) {
|
|
214
|
+
return d.toISOString().slice(0, 10);
|
|
215
|
+
}
|
|
216
|
+
if (spanMs <= 90 * ONE_DAY) {
|
|
217
|
+
return d.toISOString().slice(5, 10);
|
|
218
|
+
}
|
|
219
|
+
if (spanMs <= 730 * ONE_DAY) {
|
|
220
|
+
return d.toISOString().slice(0, 7);
|
|
221
|
+
}
|
|
222
|
+
return String(d.getUTCFullYear());
|
|
223
|
+
}
|
|
224
|
+
function activateOnKey(e, fn) {
|
|
225
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
fn();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { TimeAxis };
|
|
232
|
+
//# sourceMappingURL=time-axis.js.map
|
|
233
|
+
//# sourceMappingURL=time-axis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/time-axis/time-axis.tsx"],"names":[],"mappings":"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","file":"time-axis.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Events plotted along a horizontal time axis. Pure SVG; no heavy peer\n * dependency. Distinct from the existing `<Timeline>` component in\n * `components/timeline` — that one is an event-list with vertical\n * status markers; this one is a CHART with a real time axis where\n * spacing reflects elapsed time.\n *\n * Use TimeAxis when the *gap between events* is the message (release\n * cadence, incident frequency, sparse-then-dense patterns). Use\n * Timeline when the order of events is the message but the absolute\n * dates are secondary.\n *\n * @example\n * <TimeAxis\n * events={[\n * { id: \"v1\", label: \"v1.0\", date: \"2025-01-15\" },\n * { id: \"v2\", label: \"v2.0\", date: \"2025-04-20\" },\n * { id: \"v3\", label: \"v3.0\", date: \"2025-09-10\" },\n * ]}\n * />\n */\nexport type TimeAxisEvent = {\n\tid: string;\n\tlabel: string;\n\t/** Accepts Date, ISO string, or epoch ms. */\n\tdate: Date | string | number;\n\t/** Optional category — events with the same `category` share a row band. */\n\tcategory?: string;\n};\n\n// `start` and `end` collide with SVG's `<animate start=...>` / `<set end=...>`\n// animation attributes (their value type is string|number, not Date). Omit\n// both from the inherited SVG attributes so consumers can pass Date values\n// freely without a type collision.\nexport interface TimeAxisProps\n\textends Omit<React.SVGAttributes<SVGSVGElement>, \"children\" | \"start\" | \"end\"> {\n\t/** Events to plot. Order doesn't matter — positions come from `date`. */\n\tevents: TimeAxisEvent[];\n\t/** Optional explicit axis start. Auto-derived from `events` if omitted. */\n\tstart?: Date | string | number;\n\t/** Optional explicit axis end. Auto-derived from `events` if omitted. */\n\tend?: Date | string | number;\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 200. */\n\theight?: number;\n\t/** Number of axis ticks to show. Default 6. */\n\ttickCount?: number;\n\t/** Fired when an event is clicked. */\n\tonEventClick?: (event: TimeAxisEvent) => void;\n}\n\ninterface LaidOutEvent {\n\tevent: TimeAxisEvent;\n\tx: number;\n\ty: number;\n\trowIndex: number;\n}\n\ninterface AxisTick {\n\tx: number;\n\tlabel: string;\n}\n\nfunction TimeAxis({\n\tevents,\n\tstart,\n\tend,\n\twidth = 720,\n\theight = 200,\n\ttickCount = 6,\n\tonEventClick,\n\tclassName,\n\t...rest\n}: TimeAxisProps) {\n\tconst laidOut = React.useMemo(\n\t\t() => layout(events, start, end, width, height, tickCount),\n\t\t[events, start, end, width, height, tickCount],\n\t);\n\n\tconst desc =\n\t\tevents.length === 0\n\t\t\t? \"Empty time axis\"\n\t\t\t: `Time axis with ${events.length} event${events.length === 1 ? \"\" : \"s\"}, range ${laidOut.startLabel} to ${laidOut.endLabel}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-time-axis\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Time axis</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{/* Baseline + ticks */}\n\t\t\t<g data-hex-time-axis-axis>\n\t\t\t\t<line\n\t\t\t\t\tx1={laidOut.axisLeft}\n\t\t\t\t\tx2={laidOut.axisRight}\n\t\t\t\t\ty1={laidOut.axisY}\n\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t/>\n\t\t\t\t{laidOut.ticks.map((tick, i) => (\n\t\t\t\t\t<g key={`tick-${i}`} data-hex-time-axis-tick>\n\t\t\t\t\t\t<line\n\t\t\t\t\t\t\tx1={tick.x}\n\t\t\t\t\t\t\tx2={tick.x}\n\t\t\t\t\t\t\ty1={laidOut.axisY - 4}\n\t\t\t\t\t\t\ty2={laidOut.axisY + 4}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={tick.x}\n\t\t\t\t\t\t\ty={laidOut.axisY + 18}\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{tick.label}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-time-axis-events>\n\t\t\t\t{laidOut.events.map((e) => {\n\t\t\t\t\tconst interactive = Boolean(onEventClick);\n\t\t\t\t\tconst handleActivate = () => onEventClick?.(e.event);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={e.event.id}\n\t\t\t\t\t\t\tdata-hex-time-axis-event\n\t\t\t\t\t\t\tdata-row={e.rowIndex}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={interactive ? `${e.event.label} on ${formatDate(toDate(e.event.date))}` : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (k) => activateOnKey(k, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Connector from event marker to baseline */}\n\t\t\t\t\t\t\t<line\n\t\t\t\t\t\t\t\tx1={e.x}\n\t\t\t\t\t\t\t\tx2={e.x}\n\t\t\t\t\t\t\t\ty1={e.y}\n\t\t\t\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t\tstrokeOpacity={0.65}\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={e.x}\n\t\t\t\t\t\t\t\tcy={e.y}\n\t\t\t\t\t\t\t\tr={5}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={e.x + 8}\n\t\t\t\t\t\t\t\ty={e.y + 4}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ paintOrder: \"stroke\", pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={3}\n\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{e.event.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\tevents: TimeAxisEvent[],\n\tstart: Date | string | number | undefined,\n\tend: Date | string | number | undefined,\n\twidth: number,\n\theight: number,\n\ttickCount: number,\n): {\n\tevents: LaidOutEvent[];\n\tticks: AxisTick[];\n\taxisY: number;\n\taxisLeft: number;\n\taxisRight: number;\n\tstartLabel: string;\n\tendLabel: string;\n} {\n\tconst margin = 32;\n\tconst axisLeft = margin;\n\tconst axisRight = width - margin;\n\tconst axisY = height - 40;\n\tconst usableWidth = axisRight - axisLeft;\n\n\t// Drop events whose date doesn't parse — `new Date(\"garbage\")` returns\n\t// NaN, which would propagate through `Math.min/max` and produce NaN\n\t// x-coordinates that crash some SVG renderers and noise up dev\n\t// consoles. Skip silently; downstream components can still inspect\n\t// the unrendered ids if they want to surface validation errors.\n\tconst validEvents = events.filter((e) => Number.isFinite(toDate(e.date).getTime()));\n\n\tif (validEvents.length === 0) {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tticks: [],\n\t\t\taxisY,\n\t\t\taxisLeft,\n\t\t\taxisRight,\n\t\t\tstartLabel: \"—\",\n\t\t\tendLabel: \"—\",\n\t\t};\n\t}\n\n\tconst dates = validEvents.map((e) => toDate(e.date).getTime());\n\tconst explicitMin = start != null ? toDate(start).getTime() : NaN;\n\tconst explicitMax = end != null ? toDate(end).getTime() : NaN;\n\tconst minTs = Number.isFinite(explicitMin) ? explicitMin : Math.min(...dates);\n\tconst maxTs = Number.isFinite(explicitMax) ? explicitMax : Math.max(...dates);\n\tconst span = Math.max(1, maxTs - minTs);\n\n\tconst tToX = (t: number) => axisLeft + ((t - minTs) / span) * usableWidth;\n\n\t// Distribute events across stacked rows so labels don't overlap when\n\t// events cluster. A new event takes the topmost row whose previously-\n\t// placed event is at least MIN_GAP px to the left.\n\t//\n\t// Tiebreaker on id: Array.prototype.sort isn't stable across all\n\t// engines for events with identical timestamps. Sorting by id second\n\t// produces deterministic rows on every engine, so visual diffing\n\t// across builds stays meaningful.\n\tconst MIN_GAP_PX = 48;\n\tconst ROW_HEIGHT_PX = 22;\n\t// Cap rows so events never spill below the baseline. The first row\n\t// sits at y=24; remaining rows go in 22-px steps; clamp at axisY-10.\n\tconst maxRows = Math.max(1, Math.floor((axisY - 10 - 24) / ROW_HEIGHT_PX) + 1);\n\tconst rowsLastX: number[] = [];\n\tconst positioned = validEvents\n\t\t.map((event) => ({ event, t: toDate(event.date).getTime() }))\n\t\t.sort((a, b) => (a.t - b.t) || a.event.id.localeCompare(b.event.id))\n\t\t.map(({ event, t }) => {\n\t\t\tconst x = tToX(t);\n\t\t\tlet rowIndex = rowsLastX.findIndex((lastX) => x - lastX >= MIN_GAP_PX);\n\t\t\tif (rowIndex === -1) {\n\t\t\t\tif (rowsLastX.length < maxRows) {\n\t\t\t\t\trowIndex = rowsLastX.length;\n\t\t\t\t\trowsLastX.push(x);\n\t\t\t\t} else {\n\t\t\t\t\t// Degraded fallback — recycle the last row instead of\n\t\t\t\t\t// pushing the event below the baseline.\n\t\t\t\t\trowIndex = maxRows - 1;\n\t\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t}\n\t\t\treturn { event, x, y: 24 + rowIndex * ROW_HEIGHT_PX, rowIndex };\n\t\t});\n\n\t// Generate ticks then dedupe consecutive identical labels — at year-scale\n\t// or month-scale spans the formatter often emits the same string twice\n\t// (e.g. five ticks all in 2025 render as \"2025\" / \"2025\" / ...). Blanking\n\t// the duplicate keeps the gridline tick but drops the noisy repeat.\n\tconst rawTicks: AxisTick[] = [];\n\tfor (let i = 0; i < tickCount; i++) {\n\t\tconst t = minTs + (i / Math.max(1, tickCount - 1)) * span;\n\t\trawTicks.push({ x: tToX(t), label: formatTick(new Date(t), span) });\n\t}\n\tconst ticks: AxisTick[] = rawTicks.map((t, i) => ({\n\t\tx: t.x,\n\t\tlabel: i > 0 && rawTicks[i - 1]?.label === t.label ? \"\" : t.label,\n\t}));\n\n\treturn {\n\t\tevents: positioned,\n\t\tticks,\n\t\taxisY,\n\t\taxisLeft,\n\t\taxisRight,\n\t\tstartLabel: formatDate(new Date(minTs)),\n\t\tendLabel: formatDate(new Date(maxTs)),\n\t};\n}\n\nfunction toDate(v: Date | string | number): Date {\n\tif (v instanceof Date) return v;\n\tif (typeof v === \"number\") return new Date(v);\n\treturn new Date(v);\n}\n\nfunction formatDate(d: Date): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\treturn d.toISOString().slice(0, 10);\n}\n\nfunction formatTick(d: Date, spanMs: number): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\tconst ONE_DAY = 24 * 60 * 60 * 1000;\n\tif (spanMs <= 7 * ONE_DAY) {\n\t\t// short range — show day\n\t\treturn d.toISOString().slice(0, 10);\n\t}\n\tif (spanMs <= 90 * ONE_DAY) {\n\t\t// up to ~3 months — show month-day so adjacent ticks stay distinct\n\t\treturn d.toISOString().slice(5, 10);\n\t}\n\tif (spanMs <= 730 * ONE_DAY) {\n\t\t// up to ~2 years — show year-month\n\t\treturn d.toISOString().slice(0, 7);\n\t}\n\t// long range — year only\n\treturn String(d.getUTCFullYear());\n}\n\nfunction activateOnKey(e: React.KeyboardEvent, fn: () => void): void {\n\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\te.preventDefault();\n\t\tfn();\n\t}\n}\n\nexport { TimeAxis };\n"]}
|
package/dist/tool-call.js
CHANGED
|
@@ -15,7 +15,12 @@ var STATE_LABEL = {
|
|
|
15
15
|
};
|
|
16
16
|
var STATE_CLASSES = {
|
|
17
17
|
pending: "bg-muted text-muted-foreground",
|
|
18
|
-
|
|
18
|
+
// Solid primary bg + primary-foreground text in dark mode — the
|
|
19
|
+
// previously tinted `bg-primary/15` made the badge fg/bg too close in
|
|
20
|
+
// hue at 10px to clear WCAG AA (was 4.41:1, fails the 4.5 floor).
|
|
21
|
+
// Light mode keeps the soft tint where contrast holds; dark mode uses
|
|
22
|
+
// the solid surface for guaranteed ≥7:1 contrast.
|
|
23
|
+
running: "bg-primary/15 text-primary dark:bg-primary dark:text-primary-foreground animate-pulse",
|
|
19
24
|
result: "bg-accent/30 text-accent-foreground",
|
|
20
25
|
error: "bg-destructive/15 text-destructive"
|
|
21
26
|
};
|
package/dist/tool-call.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/tool-call/tool-call.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,WAAA,GAA6C;AAAA,EAClD,OAAA,EAAS,SAAA;AAAA,EACT,OAAA,EAAS,SAAA;AAAA,EACT,MAAA,EAAQ,MAAA;AAAA,EACR,KAAA,EAAO;AACR,CAAA;AAEA,IAAM,aAAA,GAA+C;AAAA,EACpD,OAAA,EAAS,gCAAA;AAAA,
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/tool-call/tool-call.tsx"],"names":[],"mappings":"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","file":"tool-call.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { ToolCallState } from \"../types.js\";\n\nconst STATE_LABEL: Record<ToolCallState, string> = {\n\tpending: \"Pending\",\n\trunning: \"Running\",\n\tresult: \"Done\",\n\terror: \"Error\",\n};\n\nconst STATE_CLASSES: Record<ToolCallState, string> = {\n\tpending: \"bg-muted text-muted-foreground\",\n\t// Solid primary bg + primary-foreground text in dark mode — the\n\t// previously tinted `bg-primary/15` made the badge fg/bg too close in\n\t// hue at 10px to clear WCAG AA (was 4.41:1, fails the 4.5 floor).\n\t// Light mode keeps the soft tint where contrast holds; dark mode uses\n\t// the solid surface for guaranteed ≥7:1 contrast.\n\trunning: \"bg-primary/15 text-primary dark:bg-primary dark:text-primary-foreground animate-pulse\",\n\tresult: \"bg-accent/30 text-accent-foreground\",\n\terror: \"bg-destructive/15 text-destructive\",\n};\n\n/**\n * Collapsible card displaying a tool / function invocation. Header shows the\n * tool name and lifecycle state badge; body reveals the JSON-stringified\n * arguments and result on expand.\n *\n * Display-only — the component does not run the tool. Wire it up in the\n * consumer (AI SDK `tool-*` parts → ToolCall props, LangChain\n * `AIMessage.tool_calls` → ToolCall props).\n *\n * @example\n * <ToolCall\n * name=\"searchDocs\"\n * state=\"result\"\n * args={{ query: \"auth\" }}\n * result={{ hits: 12 }}\n * />\n */\nexport interface ToolCallProps {\n\tname: string;\n\tstate: ToolCallState;\n\targs?: unknown;\n\tresult?: unknown;\n\tdefaultOpen?: boolean;\n\tclassName?: string;\n}\n\n/**\n * Renders a tool-invocation card with collapsible details.\n * @param props - tool name, state, optional args/result\n * @returns A Collapsible wrapping a header + JSON body\n */\nfunction ToolCall({\n\tname,\n\tstate,\n\targs,\n\tresult,\n\tdefaultOpen = false,\n\tclassName,\n}: ToolCallProps) {\n\treturn (\n\t\t<CollapsiblePrimitive.Root\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-hidden rounded-md border bg-card text-card-foreground\",\n\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\"data-[state=open]:shadow-sm\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<CollapsiblePrimitive.Trigger\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm\",\n\t\t\t\t\t\"hover:bg-muted/40\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<span className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t\t<ToolGlyph />\n\t\t\t\t\t<span className=\"truncate font-mono text-xs text-foreground\">{name}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium\",\n\t\t\t\t\t\t\tSTATE_CLASSES[state],\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{STATE_LABEL[state]}\n\t\t\t\t\t</span>\n\t\t\t\t</span>\n\t\t\t\t<Chevron />\n\t\t\t</CollapsiblePrimitive.Trigger>\n\t\t\t<CollapsiblePrimitive.Content className=\"overflow-hidden border-t bg-muted/20 px-3 py-2 text-xs\">\n\t\t\t\t{args !== undefined ? <CodeSection label=\"Arguments\" value={args} /> : null}\n\t\t\t\t{result !== undefined ? <CodeSection label=\"Result\" value={result} /> : null}\n\t\t\t\t{args === undefined && result === undefined ? (\n\t\t\t\t\t<p className=\"text-muted-foreground\">No arguments or result yet.</p>\n\t\t\t\t) : null}\n\t\t\t</CollapsiblePrimitive.Content>\n\t\t</CollapsiblePrimitive.Root>\n\t);\n}\n\nfunction CodeSection({ label, value }: { label: string; value: unknown }) {\n\tconst text = typeof value === \"string\" ? value : safeStringify(value);\n\treturn (\n\t\t<div className=\"space-y-1 py-1\">\n\t\t\t<div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">{label}</div>\n\t\t\t<pre className=\"overflow-x-auto rounded bg-background/60 p-2 font-mono text-[11px] leading-snug\">\n\t\t\t\t{text}\n\t\t\t</pre>\n\t\t</div>\n\t);\n}\n\nfunction safeStringify(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction ToolGlyph() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground\"\n\t\t>\n\t\t\t<path d=\"M11.5 1.5l3 3-2.5 2.5-3-3 2.5-2.5z\" />\n\t\t\t<path d=\"M9 4l-7 7v3h3l7-7\" />\n\t\t</svg>\n\t);\n}\n\nfunction Chevron() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180\"\n\t\t>\n\t\t\t<path d=\"M4 6l4 4 4-4\" />\n\t\t</svg>\n\t);\n}\n\nexport { ToolCall };\n"]}
|
package/dist/tree-map.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/lib/chart-palette.ts
|
|
8
|
+
var CHART_PALETTE = [
|
|
9
|
+
"hsl(var(--chart-1, var(--primary)))",
|
|
10
|
+
"hsl(var(--chart-2, var(--primary)))",
|
|
11
|
+
"hsl(var(--chart-3, var(--primary)))",
|
|
12
|
+
"hsl(var(--chart-4, var(--primary)))",
|
|
13
|
+
"hsl(var(--chart-5, var(--primary)))",
|
|
14
|
+
"hsl(var(--chart-6, var(--primary)))"
|
|
15
|
+
];
|
|
16
|
+
function pickChartHue(index) {
|
|
17
|
+
const safe = (index % CHART_PALETTE.length + CHART_PALETTE.length) % CHART_PALETTE.length;
|
|
18
|
+
return CHART_PALETTE[safe];
|
|
19
|
+
}
|
|
20
|
+
function cn(...inputs) {
|
|
21
|
+
return twMerge(clsx(inputs));
|
|
22
|
+
}
|
|
23
|
+
function TreeMap({
|
|
24
|
+
root,
|
|
25
|
+
width = 600,
|
|
26
|
+
height = 400,
|
|
27
|
+
padding = 2,
|
|
28
|
+
tile = "squarify",
|
|
29
|
+
colorBy = "depth",
|
|
30
|
+
onLeafClick,
|
|
31
|
+
className,
|
|
32
|
+
...rest
|
|
33
|
+
}) {
|
|
34
|
+
const [d3h, setD3h] = React.useState(null);
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
let cancelled = false;
|
|
37
|
+
void import('d3-hierarchy').then((mod) => {
|
|
38
|
+
if (!cancelled) setD3h(mod);
|
|
39
|
+
});
|
|
40
|
+
return () => {
|
|
41
|
+
cancelled = true;
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
if (!d3h) {
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
"data-hex-tree-map-loading": true,
|
|
49
|
+
"aria-busy": "true",
|
|
50
|
+
className: cn("inline-block bg-muted/20", className),
|
|
51
|
+
style: { width, height }
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const leaves = layout(d3h, root, width, height, padding, tile);
|
|
56
|
+
const maxValue = leaves.reduce((m, l) => l.value > m ? l.value : m, 0) || 1;
|
|
57
|
+
const desc = `Tree map of ${leaves.length} leaf${leaves.length === 1 ? "" : "s"}, rooted at "${root.label}"`;
|
|
58
|
+
return /* @__PURE__ */ jsxs(
|
|
59
|
+
"svg",
|
|
60
|
+
{
|
|
61
|
+
...rest,
|
|
62
|
+
"data-hex-tree-map": true,
|
|
63
|
+
"data-tile": tile,
|
|
64
|
+
role: "img",
|
|
65
|
+
width,
|
|
66
|
+
height,
|
|
67
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
68
|
+
className: cn("block", className),
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ jsx("title", { children: "Tree map" }),
|
|
71
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
72
|
+
leaves.map((l) => {
|
|
73
|
+
const w = l.x1 - l.x0;
|
|
74
|
+
const h = l.y1 - l.y0;
|
|
75
|
+
const fill = resolveFill(l, maxValue, colorBy);
|
|
76
|
+
return /* @__PURE__ */ jsxs(
|
|
77
|
+
"g",
|
|
78
|
+
{
|
|
79
|
+
"data-hex-tree-map-leaf": true,
|
|
80
|
+
"data-depth": l.depth,
|
|
81
|
+
transform: `translate(${l.x0},${l.y0})`,
|
|
82
|
+
style: onLeafClick ? { cursor: "pointer" } : void 0,
|
|
83
|
+
onClick: onLeafClick ? () => onLeafClick(l.node) : void 0,
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
"rect",
|
|
87
|
+
{
|
|
88
|
+
width: w,
|
|
89
|
+
height: h,
|
|
90
|
+
fill,
|
|
91
|
+
fillOpacity: 0.85,
|
|
92
|
+
stroke: "hsl(var(--background))",
|
|
93
|
+
strokeWidth: 1
|
|
94
|
+
}
|
|
95
|
+
),
|
|
96
|
+
w > 40 && h > 16 ? /* @__PURE__ */ jsx(
|
|
97
|
+
"text",
|
|
98
|
+
{
|
|
99
|
+
x: 6,
|
|
100
|
+
y: 16,
|
|
101
|
+
fontSize: 12,
|
|
102
|
+
fontWeight: 500,
|
|
103
|
+
fill: "hsl(var(--background))",
|
|
104
|
+
style: {
|
|
105
|
+
pointerEvents: "none",
|
|
106
|
+
paintOrder: "stroke",
|
|
107
|
+
stroke: "hsl(var(--foreground) / 0.35)",
|
|
108
|
+
strokeWidth: 2
|
|
109
|
+
},
|
|
110
|
+
children: l.node.label
|
|
111
|
+
}
|
|
112
|
+
) : null,
|
|
113
|
+
w > 60 && h > 32 ? /* @__PURE__ */ jsx(
|
|
114
|
+
"text",
|
|
115
|
+
{
|
|
116
|
+
x: 6,
|
|
117
|
+
y: 32,
|
|
118
|
+
fontSize: 11,
|
|
119
|
+
fill: "hsl(var(--background) / 0.85)",
|
|
120
|
+
style: {
|
|
121
|
+
pointerEvents: "none",
|
|
122
|
+
paintOrder: "stroke",
|
|
123
|
+
stroke: "hsl(var(--foreground) / 0.3)",
|
|
124
|
+
strokeWidth: 1.5
|
|
125
|
+
},
|
|
126
|
+
children: l.value.toLocaleString()
|
|
127
|
+
}
|
|
128
|
+
) : null
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
l.node.id
|
|
132
|
+
);
|
|
133
|
+
})
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function layout(d3h, root, width, height, padding, tile) {
|
|
139
|
+
const tileFn = tile === "binary" ? d3h.treemapBinary : tile === "slice-dice" ? d3h.treemapSliceDice : d3h.treemapSquarify;
|
|
140
|
+
const hierarchy = d3h.hierarchy(root).sum((d) => d.children && d.children.length > 0 ? 0 : d.value ?? 0).sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
|
|
141
|
+
const layoutRoot = d3h.treemap().tile(tileFn).size([width, height]).padding(padding)(hierarchy);
|
|
142
|
+
return layoutRoot.leaves().map((leaf, leafIdx) => {
|
|
143
|
+
let cursor = leaf;
|
|
144
|
+
while (cursor && cursor.depth > 1) cursor = cursor.parent;
|
|
145
|
+
const ancestorIdx = cursor?.parent?.children?.indexOf(cursor) ?? leafIdx;
|
|
146
|
+
return {
|
|
147
|
+
node: leaf.data,
|
|
148
|
+
depth: leaf.depth,
|
|
149
|
+
x0: leaf.x0,
|
|
150
|
+
y0: leaf.y0,
|
|
151
|
+
x1: leaf.x1,
|
|
152
|
+
y1: leaf.y1,
|
|
153
|
+
value: leaf.value ?? 0,
|
|
154
|
+
leafIdx,
|
|
155
|
+
rootSiblingIdx: Math.max(0, ancestorIdx)
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function resolveFill(leaf, maxValue, colorBy) {
|
|
160
|
+
if (typeof colorBy === "function") return colorBy(leaf.node, leaf.depth);
|
|
161
|
+
if (colorBy === "value") {
|
|
162
|
+
const t = Math.max(0.2, Math.min(1, leaf.value / maxValue));
|
|
163
|
+
return `hsl(var(--chart-1) / ${t.toFixed(2)})`;
|
|
164
|
+
}
|
|
165
|
+
const idx = leaf.depth > 1 ? leaf.rootSiblingIdx : leaf.leafIdx;
|
|
166
|
+
return pickChartHue(idx);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { TreeMap };
|
|
170
|
+
//# sourceMappingURL=tree-map.js.map
|
|
171
|
+
//# sourceMappingURL=tree-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/tree-map/tree-map.tsx"],"names":[],"mappings":"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","file":"tree-map.js","sourcesContent":["/**\n * Categorical chart palette for diagram primitives that encode categorical\n * data (sunburst, treemap, sankey, chord, funnel, pyramid, venn, matrix).\n * Cycled by an integer key — node index, leaf index, depth-1 ancestor, etc.\n *\n * The values reach into `--chart-1` through `--chart-6`, the dedicated\n * diagram-encoding tokens added in `@hex-core/tokens` 1.4. Each token has a\n * `var(--primary)` fallback so consumers on theme presets that haven't been\n * updated to ship `--chart-N` (or who run a custom theme without the chart\n * family) still see a coherent monochrome rendering instead of black SVG\n * fills.\n *\n * Why a chart palette and not the semantic tokens: `--primary`, `--accent`,\n * `--secondary`, and `--muted` collapse to a single hue family in the\n * default monochrome theme — adjacent segments of a chart end up\n * indistinguishable. Chart tokens are tuned for perceptual differentiation.\n */\nexport const CHART_PALETTE = [\n\t\"hsl(var(--chart-1, var(--primary)))\",\n\t\"hsl(var(--chart-2, var(--primary)))\",\n\t\"hsl(var(--chart-3, var(--primary)))\",\n\t\"hsl(var(--chart-4, var(--primary)))\",\n\t\"hsl(var(--chart-5, var(--primary)))\",\n\t\"hsl(var(--chart-6, var(--primary)))\",\n] as const;\n\n/**\n * Return the chart hue at a stable index. Cycles modulo `CHART_PALETTE.length`\n * so the caller doesn't have to range-check.\n *\n * @param index - Integer key (node index, leaf index, depth-1 ancestor index, …).\n * @returns A `hsl(var(...))` string suitable for SVG `fill` / `stroke`.\n */\nexport function pickChartHue(index: number): string {\n\tconst safe = ((index % CHART_PALETTE.length) + CHART_PALETTE.length) % CHART_PALETTE.length;\n\t// `safe` is provably 0..CHART_PALETTE.length-1 — the assertion documents that.\n\treturn CHART_PALETTE[safe] as string;\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { pickChartHue } from \"../../lib/chart-palette.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Typed React TreeMap. Each leaf is rendered as a rectangle whose area is\n * proportional to its `value`. Internal node values are summed automatically;\n * d3-hierarchy's squarified treemap layout keeps rectangles close to square\n * for legibility.\n *\n * Heavy peer: requires `d3-hierarchy` (~3 KB gzip).\n *\n * @example\n * <TreeMap\n * root={{\n * id: \"root\",\n * label: \"Files\",\n * children: [\n * { id: \"src\", label: \"src\", value: 240 },\n * { id: \"node_modules\", label: \"node_modules\", value: 980 },\n * ],\n * }}\n * />\n */\nexport type TreeMapNode = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n\tchildren?: TreeMapNode[];\n};\n\nexport interface TreeMapProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Root of the hierarchy. Leaves require a positive `value`. */\n\troot: TreeMapNode;\n\t/** Pixel width of the rendered SVG. Default 600. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 400. */\n\theight?: number;\n\t/** Inner padding between sibling rectangles, in pixels. Default 2. */\n\tpadding?: number;\n\t/** Tiling algorithm. \"squarify\" (default) keeps rectangles close to square. */\n\ttile?: \"squarify\" | \"binary\" | \"slice-dice\";\n\t/** Choose a fill color per leaf. \"depth\" cycles through theme tokens; \"value\" interpolates by value; or pass a function. */\n\tcolorBy?: \"depth\" | \"value\" | ((node: TreeMapNode, depth: number) => string);\n\t/** Fired when a leaf is clicked. */\n\tonLeafClick?: (node: TreeMapNode) => void;\n}\n\ninterface LaidOutLeaf {\n\tnode: TreeMapNode;\n\tdepth: number;\n\tx0: number;\n\ty0: number;\n\tx1: number;\n\ty1: number;\n\tvalue: number;\n\t/** Leaf index in pre-order traversal — used to cycle through CHART_PALETTE\n\t * so adjacent rectangles read as distinct categories. */\n\tleafIdx: number;\n\t/** Index of this leaf's depth-1 ancestor; multi-level trees use this so\n\t * descendants of the same branch share a hue family. */\n\trootSiblingIdx: number;\n}\n\ntype D3HierarchyMod = typeof import(\"d3-hierarchy\");\n\nfunction TreeMap({\n\troot,\n\twidth = 600,\n\theight = 400,\n\tpadding = 2,\n\ttile = \"squarify\",\n\tcolorBy = \"depth\",\n\tonLeafClick,\n\tclassName,\n\t...rest\n}: TreeMapProps) {\n\tconst [d3h, setD3h] = React.useState<D3HierarchyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-hierarchy\").then((mod) => {\n\t\t\tif (!cancelled) setD3h(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\tif (!d3h) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-tree-map-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst leaves = layout(d3h, root, width, height, padding, tile);\n\tconst maxValue = leaves.reduce((m, l) => (l.value > m ? l.value : m), 0) || 1;\n\tconst desc = `Tree map of ${leaves.length} leaf${leaves.length === 1 ? \"\" : \"s\"}, rooted at \"${root.label}\"`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-tree-map\n\t\t\tdata-tile={tile}\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Tree map</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{leaves.map((l) => {\n\t\t\t\tconst w = l.x1 - l.x0;\n\t\t\t\tconst h = l.y1 - l.y0;\n\t\t\t\tconst fill = resolveFill(l, maxValue, colorBy);\n\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={l.node.id}\n\t\t\t\t\t\tdata-hex-tree-map-leaf\n\t\t\t\t\t\tdata-depth={l.depth}\n\t\t\t\t\t\ttransform={`translate(${l.x0},${l.y0})`}\n\t\t\t\t\t\tstyle={onLeafClick ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={onLeafClick ? () => onLeafClick(l.node) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\twidth={w}\n\t\t\t\t\t\t\theight={h}\n\t\t\t\t\t\t\tfill={fill}\n\t\t\t\t\t\t\tfillOpacity={0.85}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{w > 40 && h > 16 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={16}\n\t\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\t\tfontWeight={500}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.35)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.node.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{w > 60 && h > 32 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={32}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background) / 0.85)\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.3)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.value.toLocaleString()}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</g>\n\t\t\t\t);\n\t\t\t})}\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3h: D3HierarchyMod,\n\troot: TreeMapNode,\n\twidth: number,\n\theight: number,\n\tpadding: number,\n\ttile: \"squarify\" | \"binary\" | \"slice-dice\",\n): LaidOutLeaf[] {\n\tconst tileFn = tile === \"binary\" ? d3h.treemapBinary : tile === \"slice-dice\" ? d3h.treemapSliceDice : d3h.treemapSquarify;\n\tconst hierarchy = d3h\n\t\t.hierarchy<TreeMapNode>(root)\n\t\t.sum((d) => (d.children && d.children.length > 0 ? 0 : d.value ?? 0))\n\t\t.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));\n\t// treemap(hierarchy) returns HierarchyRectangularNode<T> with typed\n\t// x0/x1/y0/y1 (pixel coords).\n\tconst layoutRoot = d3h\n\t\t.treemap<TreeMapNode>()\n\t\t.tile(tileFn)\n\t\t.size([width, height])\n\t\t.padding(padding)(hierarchy);\n\treturn layoutRoot.leaves().map((leaf, leafIdx) => {\n\t\t// Walk up to depth-1 ancestor; descendants of the same top-level\n\t\t// branch share a hue family for tree visual cohesion.\n\t\tlet cursor: typeof leaf | null = leaf;\n\t\twhile (cursor && cursor.depth > 1) cursor = cursor.parent;\n\t\tconst ancestorIdx = cursor?.parent?.children?.indexOf(cursor) ?? leafIdx;\n\t\treturn {\n\t\t\tnode: leaf.data,\n\t\t\tdepth: leaf.depth,\n\t\t\tx0: leaf.x0,\n\t\t\ty0: leaf.y0,\n\t\t\tx1: leaf.x1,\n\t\t\ty1: leaf.y1,\n\t\t\tvalue: leaf.value ?? 0,\n\t\t\tleafIdx,\n\t\t\trootSiblingIdx: Math.max(0, ancestorIdx),\n\t\t};\n\t});\n}\n\nfunction resolveFill(\n\tleaf: LaidOutLeaf,\n\tmaxValue: number,\n\tcolorBy: TreeMapProps[\"colorBy\"],\n): string {\n\tif (typeof colorBy === \"function\") return colorBy(leaf.node, leaf.depth);\n\tif (colorBy === \"value\") {\n\t\tconst t = Math.max(0.2, Math.min(1, leaf.value / maxValue));\n\t\treturn `hsl(var(--chart-1) / ${t.toFixed(2)})`;\n\t}\n\t// \"depth\" cycles through CHART_PALETTE by ancestor for nested trees,\n\t// or by leaf index for single-level trees (where every leaf is at the\n\t// same depth and would otherwise collapse to one color).\n\tconst idx = leaf.depth > 1 ? leaf.rootSiblingIdx : leaf.leafIdx;\n\treturn pickChartHue(idx);\n}\n\nexport { TreeMap };\n"]}
|
package/dist/venn.d.ts
ADDED