@wow-two-beta/ui 0.0.19 → 0.0.21

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.
Files changed (40) hide show
  1. package/dist/{chunk-ZP4CBWO2.js → chunk-CBOC2DT2.js} +1473 -5
  2. package/dist/chunk-CBOC2DT2.js.map +1 -0
  3. package/dist/display/audioPlayer/AudioPlayer.d.ts +18 -0
  4. package/dist/display/audioPlayer/AudioPlayer.d.ts.map +1 -0
  5. package/dist/display/audioPlayer/index.d.ts +2 -0
  6. package/dist/display/audioPlayer/index.d.ts.map +1 -0
  7. package/dist/display/audioWaveform/AudioWaveform.d.ts +19 -0
  8. package/dist/display/audioWaveform/AudioWaveform.d.ts.map +1 -0
  9. package/dist/display/audioWaveform/index.d.ts +2 -0
  10. package/dist/display/audioWaveform/index.d.ts.map +1 -0
  11. package/dist/display/dataGrid/DataGrid.d.ts +34 -0
  12. package/dist/display/dataGrid/DataGrid.d.ts.map +1 -0
  13. package/dist/display/dataGrid/index.d.ts +2 -0
  14. package/dist/display/dataGrid/index.d.ts.map +1 -0
  15. package/dist/display/heatmapCalendar/HeatmapCalendar.d.ts +21 -0
  16. package/dist/display/heatmapCalendar/HeatmapCalendar.d.ts.map +1 -0
  17. package/dist/display/heatmapCalendar/index.d.ts +2 -0
  18. package/dist/display/heatmapCalendar/index.d.ts.map +1 -0
  19. package/dist/display/index.d.ts +8 -0
  20. package/dist/display/index.d.ts.map +1 -1
  21. package/dist/display/index.js +1 -1
  22. package/dist/display/nodeEditor/NodeEditor.d.ts +32 -0
  23. package/dist/display/nodeEditor/NodeEditor.d.ts.map +1 -0
  24. package/dist/display/nodeEditor/index.d.ts +2 -0
  25. package/dist/display/nodeEditor/index.d.ts.map +1 -0
  26. package/dist/display/pdfViewer/PDFViewer.d.ts +22 -0
  27. package/dist/display/pdfViewer/PDFViewer.d.ts.map +1 -0
  28. package/dist/display/pdfViewer/index.d.ts +2 -0
  29. package/dist/display/pdfViewer/index.d.ts.map +1 -0
  30. package/dist/display/sparkline/Sparkline.d.ts +22 -0
  31. package/dist/display/sparkline/Sparkline.d.ts.map +1 -0
  32. package/dist/display/sparkline/index.d.ts +2 -0
  33. package/dist/display/sparkline/index.d.ts.map +1 -0
  34. package/dist/display/videoPlayer/VideoPlayer.d.ts +22 -0
  35. package/dist/display/videoPlayer/VideoPlayer.d.ts.map +1 -0
  36. package/dist/display/videoPlayer/index.d.ts +2 -0
  37. package/dist/display/videoPlayer/index.d.ts.map +1 -0
  38. package/dist/index.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-ZP4CBWO2.js.map +0 -1
@@ -4,9 +4,9 @@ import { Icon } from './chunk-TDX22OWF.js';
4
4
  import { Slot, RovingFocusGroup, useRovingFocusItem, Portal, AnchoredPositioner } from './chunk-NC2CBGX2.js';
5
5
  import { composeRefs } from './chunk-DN7WBRIV.js';
6
6
  import { cn } from './chunk-KZ4VFY2T.js';
7
- import { forwardRef, useState, Children, isValidElement, Fragment as Fragment$1, createContext, useId, useMemo, useCallback, useContext, useEffect, useRef, cloneElement } from 'react';
7
+ import { forwardRef, useState, Children, isValidElement, Fragment as Fragment$1, createContext, useId, useMemo, useCallback, useContext, useEffect, useRef, useImperativeHandle, cloneElement } from 'react';
8
8
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
9
- import { TrendingUp, TrendingDown, Check, Copy, ChevronDown, ChevronLeft, ChevronRight, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
9
+ import { TrendingUp, TrendingDown, Check, Copy, ChevronDown, ChevronLeft, ChevronRight, Plus, Minus, Maximize, Pause, Play, VolumeX, Volume2, Captions, CaptionsOff, PictureInPicture2, Minimize, ZoomOut, ZoomIn, Download, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
10
10
 
11
11
  // src/display/heading/Heading.variants.ts
12
12
  var headingVariants = tv({
@@ -2120,7 +2120,1475 @@ function computeDiff(left, right) {
2120
2120
  rows.reverse();
2121
2121
  return rows;
2122
2122
  }
2123
+ var TONE_CLASS = {
2124
+ brand: "text-primary",
2125
+ success: "text-success",
2126
+ warning: "text-warning",
2127
+ danger: "text-destructive",
2128
+ muted: "text-muted-foreground",
2129
+ current: ""
2130
+ };
2131
+ var Sparkline = forwardRef(function Sparkline2({
2132
+ data,
2133
+ variant = "line",
2134
+ width = 120,
2135
+ height = 32,
2136
+ tone = "brand",
2137
+ min: minProp,
2138
+ max: maxProp,
2139
+ showLast,
2140
+ ariaLabel = "Trend",
2141
+ className,
2142
+ ...rest
2143
+ }, ref) {
2144
+ const titleId = useId();
2145
+ const { points, barWidth, lastX, lastY, areaPath, linePath } = useMemo(() => {
2146
+ if (data.length === 0) {
2147
+ return { points: [], barWidth: 0, lastX: 0, lastY: 0, areaPath: "", linePath: "" };
2148
+ }
2149
+ const min = minProp ?? Math.min(...data);
2150
+ const max = maxProp ?? Math.max(...data);
2151
+ const range = max - min || 1;
2152
+ const stepX = data.length === 1 ? 0 : width / (data.length - 1);
2153
+ const pad = 1;
2154
+ const pointsArr = data.map((v, i) => {
2155
+ const x = i * stepX;
2156
+ const y = height - (v - min) / range * (height - pad * 2) - pad;
2157
+ return [x, y];
2158
+ });
2159
+ const lp = pointsArr.map(([x, y], i) => i === 0 ? `M${x},${y}` : `L${x},${y}`).join(" ");
2160
+ const ap = `${lp} L${pointsArr[pointsArr.length - 1][0]},${height} L${pointsArr[0][0]},${height} Z`;
2161
+ const bw = data.length > 0 ? width / data.length - 1 : 0;
2162
+ const last = pointsArr[pointsArr.length - 1];
2163
+ return { points: pointsArr, barWidth: bw, lastX: last[0], lastY: last[1], areaPath: ap, linePath: lp };
2164
+ }, [data, height, width, minProp, maxProp]);
2165
+ return /* @__PURE__ */ jsxs(
2166
+ "svg",
2167
+ {
2168
+ ref,
2169
+ role: "img",
2170
+ "aria-labelledby": titleId,
2171
+ width,
2172
+ height,
2173
+ viewBox: `0 0 ${width} ${height}`,
2174
+ preserveAspectRatio: "none",
2175
+ className: cn("inline-block overflow-visible", TONE_CLASS[tone], className),
2176
+ ...rest,
2177
+ children: [
2178
+ /* @__PURE__ */ jsx("title", { id: titleId, children: ariaLabel }),
2179
+ variant === "area" && /* @__PURE__ */ jsxs(Fragment, { children: [
2180
+ /* @__PURE__ */ jsx("path", { d: areaPath, fill: "currentColor", fillOpacity: 0.15 }),
2181
+ /* @__PURE__ */ jsx("path", { d: linePath, fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" })
2182
+ ] }),
2183
+ variant === "line" && /* @__PURE__ */ jsx("path", { d: linePath, fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" }),
2184
+ variant === "bar" && /* @__PURE__ */ jsx("g", { children: points.map(([x, y], i) => /* @__PURE__ */ jsx(
2185
+ "rect",
2186
+ {
2187
+ x: x - barWidth / 2,
2188
+ y,
2189
+ width: Math.max(1, barWidth),
2190
+ height: height - y,
2191
+ fill: "currentColor",
2192
+ rx: 1
2193
+ },
2194
+ i
2195
+ )) }),
2196
+ variant === "dot" && /* @__PURE__ */ jsx("g", { children: points.map(([x, y], i) => /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: 1.5, fill: "currentColor" }, i)) }),
2197
+ showLast && data.length > 0 && variant !== "dot" && /* @__PURE__ */ jsx("circle", { cx: lastX, cy: lastY, r: 2.5, fill: "currentColor" })
2198
+ ]
2199
+ }
2200
+ );
2201
+ });
2202
+ var TONE_CLASSES = {
2203
+ brand: ["bg-muted/50", "bg-primary/20", "bg-primary/40", "bg-primary/70", "bg-primary"],
2204
+ success: ["bg-muted/50", "bg-success/20", "bg-success/40", "bg-success/70", "bg-success"],
2205
+ warning: ["bg-muted/50", "bg-warning/20", "bg-warning/40", "bg-warning/70", "bg-warning"],
2206
+ danger: ["bg-muted/50", "bg-destructive/20", "bg-destructive/40", "bg-destructive/70", "bg-destructive"],
2207
+ muted: ["bg-muted/30", "bg-muted", "bg-muted-foreground/30", "bg-muted-foreground/60", "bg-muted-foreground"]
2208
+ };
2209
+ var DEFAULT_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2210
+ var DEFAULT_WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2211
+ function formatDate(d) {
2212
+ const y = d.getFullYear();
2213
+ const m = String(d.getMonth() + 1).padStart(2, "0");
2214
+ const day = String(d.getDate()).padStart(2, "0");
2215
+ return `${y}-${m}-${day}`;
2216
+ }
2217
+ var HeatmapCalendar = forwardRef(
2218
+ function HeatmapCalendar2({
2219
+ values,
2220
+ year = (/* @__PURE__ */ new Date()).getFullYear(),
2221
+ weekStart = 0,
2222
+ cellSize = 12,
2223
+ gap = 2,
2224
+ levels = 5,
2225
+ tone = "brand",
2226
+ onCellClick,
2227
+ monthLabels = DEFAULT_MONTHS,
2228
+ weekdayLabels = DEFAULT_WEEKDAYS,
2229
+ showLegend = true,
2230
+ className,
2231
+ ...rest
2232
+ }, ref) {
2233
+ const valueMap = useMemo(() => {
2234
+ if (values instanceof Map) return values;
2235
+ return new Map(Object.entries(values));
2236
+ }, [values]);
2237
+ const { columns, monthMarkers, maxValue } = useMemo(() => {
2238
+ const start = new Date(year, 0, 1);
2239
+ const end = new Date(year, 11, 31);
2240
+ const cur = new Date(start);
2241
+ const wd = cur.getDay();
2242
+ const offset = (wd - weekStart + 7) % 7;
2243
+ cur.setDate(cur.getDate() - offset);
2244
+ const cols = [];
2245
+ let column = [];
2246
+ const months = [];
2247
+ let lastSeenMonth = -1;
2248
+ let max = 0;
2249
+ while (cur <= end || column.length > 0) {
2250
+ const inYear = cur.getFullYear() === year;
2251
+ const key = formatDate(cur);
2252
+ const v = valueMap.get(key) ?? 0;
2253
+ if (v > max) max = v;
2254
+ column.push({ date: new Date(cur), inYear, value: v });
2255
+ if (inYear && cur.getMonth() !== lastSeenMonth) {
2256
+ lastSeenMonth = cur.getMonth();
2257
+ months.push({ month: cur.getMonth(), col: cols.length });
2258
+ }
2259
+ if (column.length === 7) {
2260
+ cols.push(column);
2261
+ column = [];
2262
+ }
2263
+ cur.setDate(cur.getDate() + 1);
2264
+ if (cur > end && column.length === 0) break;
2265
+ }
2266
+ if (column.length > 0) cols.push(column);
2267
+ return { columns: cols, monthMarkers: months, maxValue: max };
2268
+ }, [year, weekStart, valueMap]);
2269
+ const toneSteps = TONE_CLASSES[tone];
2270
+ const bucket = (v) => {
2271
+ if (v <= 0 || maxValue === 0) return 0;
2272
+ const idx = Math.ceil(v / maxValue * (levels - 1));
2273
+ return Math.min(levels - 1, idx);
2274
+ };
2275
+ const totalWidth = columns.length * (cellSize + gap);
2276
+ const colHeight = 7 * (cellSize + gap);
2277
+ const weekdayOrder = Array.from({ length: 7 }, (_, i) => weekdayLabels[(i + weekStart) % 7]);
2278
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("inline-block", className), ...rest, children: [
2279
+ /* @__PURE__ */ jsx("div", { className: "relative ml-8", style: { height: cellSize, width: totalWidth }, children: monthMarkers.map(({ month, col }) => /* @__PURE__ */ jsx(
2280
+ "span",
2281
+ {
2282
+ className: "absolute text-[10px] uppercase text-muted-foreground",
2283
+ style: { left: col * (cellSize + gap) },
2284
+ children: monthLabels[month]
2285
+ },
2286
+ month
2287
+ )) }),
2288
+ /* @__PURE__ */ jsxs("div", { className: "flex", style: { gap }, children: [
2289
+ /* @__PURE__ */ jsx(
2290
+ "div",
2291
+ {
2292
+ className: "flex flex-col text-[10px] uppercase text-muted-foreground",
2293
+ style: { width: 28, gap, height: colHeight },
2294
+ children: weekdayOrder.map((wd, i) => /* @__PURE__ */ jsx(
2295
+ "span",
2296
+ {
2297
+ className: i % 2 === 0 ? "opacity-0" : "",
2298
+ style: { height: cellSize, lineHeight: `${cellSize}px` },
2299
+ children: wd
2300
+ },
2301
+ i
2302
+ ))
2303
+ }
2304
+ ),
2305
+ /* @__PURE__ */ jsx("div", { className: "flex", style: { gap }, children: columns.map((col, colIdx) => /* @__PURE__ */ jsx("div", { className: "flex flex-col", style: { gap }, children: col.map((cell, rowIdx) => {
2306
+ const dateStr = formatDate(cell.date);
2307
+ const level = bucket(cell.value);
2308
+ const interactive = cell.inYear && onCellClick != null;
2309
+ const Tag = interactive ? "button" : "div";
2310
+ return /* @__PURE__ */ jsx(
2311
+ Tag,
2312
+ {
2313
+ type: interactive ? "button" : void 0,
2314
+ "aria-label": `${dateStr}: ${cell.value}`,
2315
+ "aria-valuenow": level,
2316
+ "aria-valuemin": 0,
2317
+ "aria-valuemax": levels - 1,
2318
+ onClick: interactive ? () => onCellClick?.(dateStr, cell.value) : void 0,
2319
+ style: { width: cellSize, height: cellSize },
2320
+ className: cn(
2321
+ "rounded-[2px] transition-colors",
2322
+ cell.inYear ? toneSteps[level] : "bg-transparent",
2323
+ interactive && "cursor-pointer hover:ring-1 hover:ring-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
2324
+ )
2325
+ },
2326
+ rowIdx
2327
+ );
2328
+ }) }, colIdx)) })
2329
+ ] }),
2330
+ showLegend && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-end gap-1.5 text-[10px] text-muted-foreground", children: [
2331
+ /* @__PURE__ */ jsx("span", { children: "Less" }),
2332
+ toneSteps.map((cls, i) => /* @__PURE__ */ jsx(
2333
+ "span",
2334
+ {
2335
+ "aria-hidden": "true",
2336
+ style: { width: cellSize, height: cellSize },
2337
+ className: cn("rounded-[2px]", cls)
2338
+ },
2339
+ i
2340
+ )),
2341
+ /* @__PURE__ */ jsx("span", { children: "More" })
2342
+ ] })
2343
+ ] });
2344
+ }
2345
+ );
2346
+ function castValue(raw, type) {
2347
+ if (type === "number") {
2348
+ const n = Number(raw);
2349
+ return Number.isFinite(n) ? n : raw;
2350
+ }
2351
+ if (type === "boolean") return raw === "true";
2352
+ return raw;
2353
+ }
2354
+ var DataGrid = forwardRef(
2355
+ function DataGrid2({ columns, rows, rowKey, onRowChange, dense, className, ...rest }, ref) {
2356
+ const [active, setActive] = useState({ row: 0, col: 0 });
2357
+ const [editing, setEditing] = useState(false);
2358
+ const [draft, setDraft] = useState("");
2359
+ const containerRef = useRef(null);
2360
+ const editRef = useRef(null);
2361
+ useEffect(() => {
2362
+ if (editing && editRef.current) {
2363
+ editRef.current.focus();
2364
+ if ("select" in editRef.current && typeof editRef.current.select === "function") {
2365
+ editRef.current.select();
2366
+ }
2367
+ }
2368
+ }, [editing]);
2369
+ const colCount = columns.length;
2370
+ const rowCount = rows.length;
2371
+ const beginEdit = useCallback(() => {
2372
+ const col = columns[active.col];
2373
+ const row = rows[active.row];
2374
+ if (!col || row === void 0) return;
2375
+ if (col.editable === false) return;
2376
+ const raw = col.accessor(row);
2377
+ setDraft(raw == null ? "" : String(raw));
2378
+ setEditing(true);
2379
+ }, [columns, rows, active]);
2380
+ const commitEdit = useCallback(
2381
+ (move) => {
2382
+ const col = columns[active.col];
2383
+ const row = rows[active.row];
2384
+ if (!col || row === void 0) {
2385
+ setEditing(false);
2386
+ return;
2387
+ }
2388
+ const value = castValue(draft, col.type ?? "text");
2389
+ onRowChange?.(row, col.key, value);
2390
+ setEditing(false);
2391
+ if (move === "right") setActive((a) => ({ row: a.row, col: Math.min(colCount - 1, a.col + 1) }));
2392
+ if (move === "down") setActive((a) => ({ row: Math.min(rowCount - 1, a.row + 1), col: a.col }));
2393
+ },
2394
+ [columns, rows, active, draft, onRowChange, colCount, rowCount]
2395
+ );
2396
+ const cancelEdit = useCallback(() => {
2397
+ setEditing(false);
2398
+ }, []);
2399
+ const handleKeyDown = (e) => {
2400
+ if (editing) {
2401
+ if (e.key === "Enter") {
2402
+ e.preventDefault();
2403
+ commitEdit("down");
2404
+ } else if (e.key === "Tab") {
2405
+ e.preventDefault();
2406
+ commitEdit("right");
2407
+ } else if (e.key === "Escape") {
2408
+ e.preventDefault();
2409
+ cancelEdit();
2410
+ }
2411
+ return;
2412
+ }
2413
+ switch (e.key) {
2414
+ case "ArrowRight":
2415
+ e.preventDefault();
2416
+ setActive((a) => ({ row: a.row, col: Math.min(colCount - 1, a.col + 1) }));
2417
+ break;
2418
+ case "ArrowLeft":
2419
+ e.preventDefault();
2420
+ setActive((a) => ({ row: a.row, col: Math.max(0, a.col - 1) }));
2421
+ break;
2422
+ case "ArrowDown":
2423
+ e.preventDefault();
2424
+ setActive((a) => ({ row: Math.min(rowCount - 1, a.row + 1), col: a.col }));
2425
+ break;
2426
+ case "ArrowUp":
2427
+ e.preventDefault();
2428
+ setActive((a) => ({ row: Math.max(0, a.row - 1), col: a.col }));
2429
+ break;
2430
+ case "Home":
2431
+ e.preventDefault();
2432
+ setActive((a) => ({ row: a.row, col: 0 }));
2433
+ break;
2434
+ case "End":
2435
+ e.preventDefault();
2436
+ setActive((a) => ({ row: a.row, col: colCount - 1 }));
2437
+ break;
2438
+ case "Enter":
2439
+ case "F2":
2440
+ e.preventDefault();
2441
+ beginEdit();
2442
+ break;
2443
+ }
2444
+ };
2445
+ const cellPad = dense ? "px-2 py-1" : "px-3 py-2";
2446
+ return /* @__PURE__ */ jsx(
2447
+ "div",
2448
+ {
2449
+ ref: (el) => {
2450
+ containerRef.current = el;
2451
+ if (typeof ref === "function") ref(el);
2452
+ else if (ref) ref.current = el;
2453
+ },
2454
+ role: "grid",
2455
+ "aria-rowcount": rowCount + 1,
2456
+ "aria-colcount": colCount,
2457
+ tabIndex: 0,
2458
+ onKeyDown: handleKeyDown,
2459
+ className: cn(
2460
+ "overflow-auto rounded-md border border-border bg-card text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2461
+ className
2462
+ ),
2463
+ ...rest,
2464
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse", children: [
2465
+ /* @__PURE__ */ jsx("thead", { role: "rowgroup", children: /* @__PURE__ */ jsx("tr", { role: "row", "aria-rowindex": 1, className: "bg-muted/40", children: columns.map((col, ci) => /* @__PURE__ */ jsx(
2466
+ "th",
2467
+ {
2468
+ role: "columnheader",
2469
+ "aria-colindex": ci + 1,
2470
+ scope: "col",
2471
+ style: { width: col.width, textAlign: col.align ?? "left" },
2472
+ className: cn(
2473
+ "border-b border-border font-medium text-muted-foreground",
2474
+ cellPad
2475
+ ),
2476
+ children: col.header
2477
+ },
2478
+ col.key
2479
+ )) }) }),
2480
+ /* @__PURE__ */ jsx("tbody", { role: "rowgroup", children: rows.map((row, ri) => /* @__PURE__ */ jsx("tr", { role: "row", "aria-rowindex": ri + 2, className: "border-b border-border last:border-b-0", children: columns.map((col, ci) => {
2481
+ const isActive = active.row === ri && active.col === ci;
2482
+ const value = col.accessor(row);
2483
+ const isEditable = col.editable !== false;
2484
+ const isEditing = editing && isActive;
2485
+ return /* @__PURE__ */ jsx(
2486
+ "td",
2487
+ {
2488
+ role: "gridcell",
2489
+ "aria-colindex": ci + 1,
2490
+ "aria-readonly": !isEditable || void 0,
2491
+ "aria-selected": isActive || void 0,
2492
+ tabIndex: isActive ? 0 : -1,
2493
+ onClick: () => {
2494
+ setActive({ row: ri, col: ci });
2495
+ if (isEditable && !isEditing) {
2496
+ requestAnimationFrame(() => {
2497
+ const c = columns[ci];
2498
+ if (!c) return;
2499
+ const r = rows[ri];
2500
+ if (r === void 0) return;
2501
+ const raw = c.accessor(r);
2502
+ setDraft(raw == null ? "" : String(raw));
2503
+ setEditing(true);
2504
+ });
2505
+ }
2506
+ },
2507
+ style: { textAlign: col.align ?? "left" },
2508
+ className: cn(
2509
+ "relative cursor-cell whitespace-nowrap",
2510
+ cellPad,
2511
+ isActive && "bg-primary-soft/40 ring-2 ring-inset ring-primary",
2512
+ !isEditable && "cursor-default"
2513
+ ),
2514
+ children: isEditing ? /* @__PURE__ */ jsx(
2515
+ CellEditor,
2516
+ {
2517
+ ref: editRef,
2518
+ col,
2519
+ value: draft,
2520
+ onChange: setDraft,
2521
+ onCommit: commitEdit,
2522
+ onCancel: cancelEdit
2523
+ }
2524
+ ) : col.cell ? col.cell(row) : col.type === "boolean" ? /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: value ? "\u2713" : "\xB7" }) : /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: String(value ?? "") })
2525
+ },
2526
+ col.key
2527
+ );
2528
+ }) }, rowKey(row))) })
2529
+ ] })
2530
+ }
2531
+ );
2532
+ }
2533
+ );
2534
+ var CellEditor = forwardRef(
2535
+ function CellEditor2({ col, value, onChange, onCommit, onCancel }, ref) {
2536
+ const baseClass = "h-7 w-full rounded-sm border border-input bg-background px-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring";
2537
+ if (col.type === "select" && col.options) {
2538
+ return /* @__PURE__ */ jsx(
2539
+ "select",
2540
+ {
2541
+ ref,
2542
+ value,
2543
+ onChange: (e) => {
2544
+ onChange(e.target.value);
2545
+ onCommit("down");
2546
+ },
2547
+ onBlur: () => onCommit(),
2548
+ onKeyDown: (e) => {
2549
+ if (e.key === "Escape") {
2550
+ e.preventDefault();
2551
+ onCancel();
2552
+ }
2553
+ },
2554
+ className: baseClass,
2555
+ children: col.options.map((opt) => /* @__PURE__ */ jsx("option", { value: String(opt.value), children: String(opt.label) }, String(opt.value)))
2556
+ }
2557
+ );
2558
+ }
2559
+ if (col.type === "boolean") {
2560
+ return /* @__PURE__ */ jsxs(
2561
+ "select",
2562
+ {
2563
+ ref,
2564
+ value,
2565
+ onChange: (e) => {
2566
+ onChange(e.target.value);
2567
+ onCommit("down");
2568
+ },
2569
+ onBlur: () => onCommit(),
2570
+ onKeyDown: (e) => {
2571
+ if (e.key === "Escape") {
2572
+ e.preventDefault();
2573
+ onCancel();
2574
+ }
2575
+ },
2576
+ className: baseClass,
2577
+ children: [
2578
+ /* @__PURE__ */ jsx("option", { value: "true", children: "true" }),
2579
+ /* @__PURE__ */ jsx("option", { value: "false", children: "false" })
2580
+ ]
2581
+ }
2582
+ );
2583
+ }
2584
+ return /* @__PURE__ */ jsx(
2585
+ "input",
2586
+ {
2587
+ ref,
2588
+ type: col.type === "number" ? "number" : "text",
2589
+ value,
2590
+ onChange: (e) => onChange(e.target.value),
2591
+ onBlur: () => onCommit(),
2592
+ onKeyDown: (e) => {
2593
+ if (e.key === "Escape") {
2594
+ e.preventDefault();
2595
+ onCancel();
2596
+ }
2597
+ },
2598
+ className: baseClass
2599
+ }
2600
+ );
2601
+ }
2602
+ );
2603
+ var NodeEditor = forwardRef(function NodeEditor2({
2604
+ nodes,
2605
+ edges = [],
2606
+ onNodesChange,
2607
+ onEdgeClick,
2608
+ renderNode,
2609
+ nodeWidth = 160,
2610
+ nodeHeight = 60,
2611
+ minZoom = 0.25,
2612
+ maxZoom = 2,
2613
+ className,
2614
+ ...rest
2615
+ }, ref) {
2616
+ const containerRef = useRef(null);
2617
+ const [viewport, setViewport] = useState({ x: 0, y: 0, zoom: 1 });
2618
+ const dragStateRef = useRef(null);
2619
+ const nodeIndex = useMemo(() => new Map(nodes.map((n) => [n.id, n])), [nodes]);
2620
+ const onContainerPointerDown = (e) => {
2621
+ if (e.button !== 0) return;
2622
+ if (e.target.closest("[data-node]")) return;
2623
+ dragStateRef.current = {
2624
+ kind: "pan",
2625
+ startX: e.clientX,
2626
+ startY: e.clientY,
2627
+ viewportX: viewport.x,
2628
+ viewportY: viewport.y
2629
+ };
2630
+ e.currentTarget.setPointerCapture(e.pointerId);
2631
+ };
2632
+ const onContainerPointerMove = (e) => {
2633
+ const drag = dragStateRef.current;
2634
+ if (!drag) return;
2635
+ if (drag.kind === "pan") {
2636
+ const dx = e.clientX - drag.startX;
2637
+ const dy = e.clientY - drag.startY;
2638
+ setViewport((v) => ({ ...v, x: drag.viewportX + dx, y: drag.viewportY + dy }));
2639
+ } else if (drag.kind === "node") {
2640
+ const dx = (e.clientX - drag.startX) / viewport.zoom;
2641
+ const dy = (e.clientY - drag.startY) / viewport.zoom;
2642
+ const next = nodes.map(
2643
+ (n) => n.id === drag.nodeId ? { ...n, x: drag.nodeStartX + dx, y: drag.nodeStartY + dy } : n
2644
+ );
2645
+ onNodesChange?.(next);
2646
+ }
2647
+ };
2648
+ const onContainerPointerUp = (e) => {
2649
+ dragStateRef.current = null;
2650
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
2651
+ };
2652
+ const onWheel = useCallback(
2653
+ (e) => {
2654
+ e.preventDefault();
2655
+ const rect = containerRef.current?.getBoundingClientRect();
2656
+ if (!rect) return;
2657
+ const cx = e.clientX - rect.left;
2658
+ const cy = e.clientY - rect.top;
2659
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
2660
+ setViewport((v) => {
2661
+ const nextZoom = Math.max(minZoom, Math.min(maxZoom, v.zoom * factor));
2662
+ const ratio = nextZoom / v.zoom;
2663
+ return {
2664
+ zoom: nextZoom,
2665
+ x: cx - (cx - v.x) * ratio,
2666
+ y: cy - (cy - v.y) * ratio
2667
+ };
2668
+ });
2669
+ },
2670
+ [minZoom, maxZoom]
2671
+ );
2672
+ const beginNodeDrag = (e, node) => {
2673
+ e.stopPropagation();
2674
+ dragStateRef.current = {
2675
+ kind: "node",
2676
+ nodeId: node.id,
2677
+ startX: e.clientX,
2678
+ startY: e.clientY,
2679
+ nodeStartX: node.x,
2680
+ nodeStartY: node.y
2681
+ };
2682
+ e.currentTarget.setPointerCapture(e.pointerId);
2683
+ };
2684
+ const fitView = () => {
2685
+ if (nodes.length === 0) {
2686
+ setViewport({ x: 0, y: 0, zoom: 1 });
2687
+ return;
2688
+ }
2689
+ const minX = Math.min(...nodes.map((n) => n.x));
2690
+ const minY = Math.min(...nodes.map((n) => n.y));
2691
+ const maxX = Math.max(...nodes.map((n) => n.x + nodeWidth));
2692
+ const maxY = Math.max(...nodes.map((n) => n.y + nodeHeight));
2693
+ const w = maxX - minX;
2694
+ const h = maxY - minY;
2695
+ const rect = containerRef.current?.getBoundingClientRect();
2696
+ if (!rect) return;
2697
+ const padding = 40;
2698
+ const zoomX = (rect.width - padding * 2) / w;
2699
+ const zoomY = (rect.height - padding * 2) / h;
2700
+ const zoom = Math.max(minZoom, Math.min(maxZoom, Math.min(zoomX, zoomY, 1)));
2701
+ setViewport({
2702
+ zoom,
2703
+ x: padding - minX * zoom + (rect.width - padding * 2 - w * zoom) / 2,
2704
+ y: padding - minY * zoom + (rect.height - padding * 2 - h * zoom) / 2
2705
+ });
2706
+ };
2707
+ return /* @__PURE__ */ jsxs(
2708
+ "div",
2709
+ {
2710
+ ref: (el) => {
2711
+ containerRef.current = el;
2712
+ if (typeof ref === "function") ref(el);
2713
+ else if (ref) ref.current = el;
2714
+ },
2715
+ onPointerDown: onContainerPointerDown,
2716
+ onPointerMove: onContainerPointerMove,
2717
+ onPointerUp: onContainerPointerUp,
2718
+ onPointerCancel: onContainerPointerUp,
2719
+ onWheel,
2720
+ className: cn(
2721
+ "relative h-96 w-full overflow-hidden rounded-md border border-border bg-muted/30 select-none",
2722
+ className
2723
+ ),
2724
+ ...rest,
2725
+ children: [
2726
+ /* @__PURE__ */ jsxs(
2727
+ "div",
2728
+ {
2729
+ className: "absolute inset-0 origin-top-left",
2730
+ style: { transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom})` },
2731
+ children: [
2732
+ /* @__PURE__ */ jsx(
2733
+ "svg",
2734
+ {
2735
+ className: "absolute pointer-events-none",
2736
+ style: { overflow: "visible", width: "100%", height: "100%" },
2737
+ children: edges.map((edge) => {
2738
+ const a = nodeIndex.get(edge.source);
2739
+ const b = nodeIndex.get(edge.target);
2740
+ if (!a || !b) return null;
2741
+ const x1 = a.x + nodeWidth;
2742
+ const y1 = a.y + nodeHeight / 2;
2743
+ const x2 = b.x;
2744
+ const y2 = b.y + nodeHeight / 2;
2745
+ const mid = (x1 + x2) / 2;
2746
+ const path = `M${x1},${y1} C${mid},${y1} ${mid},${y2} ${x2},${y2}`;
2747
+ return /* @__PURE__ */ jsxs("g", { className: "pointer-events-auto cursor-pointer", onClick: () => onEdgeClick?.(edge), children: [
2748
+ /* @__PURE__ */ jsx(
2749
+ "path",
2750
+ {
2751
+ d: path,
2752
+ fill: "none",
2753
+ stroke: "currentColor",
2754
+ strokeWidth: 2 / viewport.zoom,
2755
+ className: "text-border-strong hover:text-primary"
2756
+ }
2757
+ ),
2758
+ edge.label && /* @__PURE__ */ jsx(
2759
+ "text",
2760
+ {
2761
+ x: mid,
2762
+ y: (y1 + y2) / 2 - 6,
2763
+ textAnchor: "middle",
2764
+ className: "fill-muted-foreground text-[10px]",
2765
+ style: { fontSize: 10 / viewport.zoom },
2766
+ children: edge.label
2767
+ }
2768
+ )
2769
+ ] }, edge.id);
2770
+ })
2771
+ }
2772
+ ),
2773
+ nodes.map((node) => /* @__PURE__ */ jsx(
2774
+ "div",
2775
+ {
2776
+ "data-node": true,
2777
+ role: "group",
2778
+ "aria-label": typeof node.label === "string" ? node.label : node.id,
2779
+ onPointerDown: (e) => beginNodeDrag(e, node),
2780
+ style: {
2781
+ position: "absolute",
2782
+ left: node.x,
2783
+ top: node.y,
2784
+ width: nodeWidth,
2785
+ height: nodeHeight
2786
+ },
2787
+ className: "cursor-grab active:cursor-grabbing",
2788
+ children: renderNode ? renderNode(node) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center rounded-md border border-border bg-card px-3 text-sm font-medium shadow-sm", children: node.label ?? node.id })
2789
+ },
2790
+ node.id
2791
+ ))
2792
+ ]
2793
+ }
2794
+ ),
2795
+ /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 right-3 flex flex-col gap-1 rounded-md border border-border bg-card p-1 shadow-sm", children: [
2796
+ /* @__PURE__ */ jsx(
2797
+ "button",
2798
+ {
2799
+ type: "button",
2800
+ "aria-label": "Zoom in",
2801
+ onClick: () => setViewport((v) => ({ ...v, zoom: Math.min(maxZoom, v.zoom * 1.2) })),
2802
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
2803
+ children: /* @__PURE__ */ jsx(Icon, { icon: Plus, size: 14 })
2804
+ }
2805
+ ),
2806
+ /* @__PURE__ */ jsx(
2807
+ "button",
2808
+ {
2809
+ type: "button",
2810
+ "aria-label": "Zoom out",
2811
+ onClick: () => setViewport((v) => ({ ...v, zoom: Math.max(minZoom, v.zoom / 1.2) })),
2812
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
2813
+ children: /* @__PURE__ */ jsx(Icon, { icon: Minus, size: 14 })
2814
+ }
2815
+ ),
2816
+ /* @__PURE__ */ jsx(
2817
+ "button",
2818
+ {
2819
+ type: "button",
2820
+ "aria-label": "Fit view",
2821
+ onClick: fitView,
2822
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
2823
+ children: /* @__PURE__ */ jsx(Icon, { icon: Maximize, size: 12 })
2824
+ }
2825
+ )
2826
+ ] })
2827
+ ]
2828
+ }
2829
+ );
2830
+ });
2831
+ var TONE_CLASS2 = {
2832
+ brand: "text-primary",
2833
+ success: "text-success",
2834
+ warning: "text-warning",
2835
+ danger: "text-destructive",
2836
+ muted: "text-muted-foreground",
2837
+ current: ""
2838
+ };
2839
+ var AudioWaveform = forwardRef(
2840
+ function AudioWaveform2({
2841
+ peaks,
2842
+ progress = 0,
2843
+ width = 320,
2844
+ height = 48,
2845
+ barWidth = 2,
2846
+ gap = 1,
2847
+ tone = "brand",
2848
+ onSeek,
2849
+ interactive,
2850
+ className,
2851
+ ...rest
2852
+ }, ref) {
2853
+ const stepX = barWidth + gap;
2854
+ const barCount = Math.max(1, Math.floor(width / stepX));
2855
+ const sampled = useMemo(() => sampleTo(peaks, barCount), [peaks, barCount]);
2856
+ const playedBars = Math.round(progress * barCount);
2857
+ const isInteractive = interactive ?? onSeek != null;
2858
+ const seekFromX = (clientX, rect) => {
2859
+ const x = Math.max(0, Math.min(rect.width, clientX - rect.left));
2860
+ onSeek?.(x / rect.width);
2861
+ };
2862
+ const svgRef = useRef(null);
2863
+ const handleKey = (e) => {
2864
+ if (!onSeek) return;
2865
+ if (e.key === "ArrowRight") {
2866
+ e.preventDefault();
2867
+ onSeek(Math.min(1, progress + 0.05));
2868
+ } else if (e.key === "ArrowLeft") {
2869
+ e.preventDefault();
2870
+ onSeek(Math.max(0, progress - 0.05));
2871
+ } else if (e.key === "Home") {
2872
+ e.preventDefault();
2873
+ onSeek(0);
2874
+ } else if (e.key === "End") {
2875
+ e.preventDefault();
2876
+ onSeek(1);
2877
+ }
2878
+ };
2879
+ return /* @__PURE__ */ jsx(
2880
+ "svg",
2881
+ {
2882
+ ref: (el) => {
2883
+ svgRef.current = el;
2884
+ if (typeof ref === "function") ref(el);
2885
+ else if (ref) ref.current = el;
2886
+ },
2887
+ role: isInteractive ? "slider" : "img",
2888
+ "aria-label": "Audio waveform",
2889
+ "aria-valuenow": Math.round(progress * 100),
2890
+ "aria-valuemin": 0,
2891
+ "aria-valuemax": 100,
2892
+ tabIndex: isInteractive ? 0 : -1,
2893
+ width,
2894
+ height,
2895
+ viewBox: `0 0 ${width} ${height}`,
2896
+ preserveAspectRatio: "none",
2897
+ onClick: isInteractive ? (e) => {
2898
+ const rect = svgRef.current?.getBoundingClientRect();
2899
+ if (rect) seekFromX(e.clientX, rect);
2900
+ } : void 0,
2901
+ onKeyDown: handleKey,
2902
+ className: cn(
2903
+ "inline-block",
2904
+ isInteractive && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm",
2905
+ TONE_CLASS2[tone],
2906
+ className
2907
+ ),
2908
+ ...rest,
2909
+ children: sampled.map((amp, i) => {
2910
+ const h = Math.max(1, amp * height);
2911
+ const x = i * stepX;
2912
+ const y = (height - h) / 2;
2913
+ const played = i < playedBars;
2914
+ return /* @__PURE__ */ jsx(
2915
+ "rect",
2916
+ {
2917
+ x,
2918
+ y,
2919
+ width: barWidth,
2920
+ height: h,
2921
+ rx: 1,
2922
+ className: played ? "fill-current" : "fill-current opacity-30"
2923
+ },
2924
+ i
2925
+ );
2926
+ })
2927
+ }
2928
+ );
2929
+ }
2930
+ );
2931
+ function sampleTo(peaks, n) {
2932
+ if (peaks.length === 0) return new Array(n).fill(0);
2933
+ if (peaks.length === n) return peaks;
2934
+ const out = new Array(n);
2935
+ const ratio = peaks.length / n;
2936
+ for (let i = 0; i < n; i++) {
2937
+ const start = Math.floor(i * ratio);
2938
+ const end = Math.max(start + 1, Math.floor((i + 1) * ratio));
2939
+ let max = 0;
2940
+ for (let j = start; j < end && j < peaks.length; j++) {
2941
+ const v = Math.abs(peaks[j]);
2942
+ if (v > max) max = v;
2943
+ }
2944
+ out[i] = max;
2945
+ }
2946
+ return out;
2947
+ }
2948
+ var SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 2];
2949
+ function formatTime(s) {
2950
+ if (!Number.isFinite(s) || s < 0) return "0:00";
2951
+ const total = Math.floor(s);
2952
+ const h = Math.floor(total / 3600);
2953
+ const m = Math.floor(total % 3600 / 60);
2954
+ const sec = total % 60;
2955
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
2956
+ return `${m}:${String(sec).padStart(2, "0")}`;
2957
+ }
2958
+ var AudioPlayer = forwardRef(
2959
+ function AudioPlayer2({
2960
+ src,
2961
+ peaks,
2962
+ autoPlay,
2963
+ loop,
2964
+ defaultVolume = 1,
2965
+ defaultPlaybackRate = 1,
2966
+ compact,
2967
+ onPlay,
2968
+ onPause,
2969
+ onTimeUpdate,
2970
+ onEnded,
2971
+ className,
2972
+ ...rest
2973
+ }, forwardedRef) {
2974
+ const audioRef = useRef(null);
2975
+ useImperativeHandle(forwardedRef, () => audioRef.current);
2976
+ const [playing, setPlaying] = useState(!!autoPlay);
2977
+ const [currentTime, setCurrentTime] = useState(0);
2978
+ const [duration, setDuration] = useState(0);
2979
+ const [volume, setVolume] = useState(defaultVolume);
2980
+ const [muted, setMuted] = useState(false);
2981
+ const [speed, setSpeed] = useState(defaultPlaybackRate);
2982
+ useEffect(() => {
2983
+ const a = audioRef.current;
2984
+ if (!a) return;
2985
+ a.volume = volume;
2986
+ a.muted = muted;
2987
+ a.playbackRate = speed;
2988
+ }, [volume, muted, speed]);
2989
+ const togglePlay = useCallback(() => {
2990
+ const a = audioRef.current;
2991
+ if (!a) return;
2992
+ if (a.paused) {
2993
+ a.play().catch(() => {
2994
+ });
2995
+ } else {
2996
+ a.pause();
2997
+ }
2998
+ }, []);
2999
+ const seekTo = useCallback((seconds) => {
3000
+ const a = audioRef.current;
3001
+ if (!a || !Number.isFinite(seconds)) return;
3002
+ a.currentTime = Math.max(0, Math.min(a.duration || 0, seconds));
3003
+ }, []);
3004
+ const seekProgress = useCallback(
3005
+ (p) => {
3006
+ if (duration > 0) seekTo(p * duration);
3007
+ },
3008
+ [duration, seekTo]
3009
+ );
3010
+ const handleKey = (e) => {
3011
+ switch (e.key) {
3012
+ case " ":
3013
+ case "Spacebar":
3014
+ e.preventDefault();
3015
+ togglePlay();
3016
+ break;
3017
+ case "ArrowRight":
3018
+ e.preventDefault();
3019
+ seekTo(currentTime + 5);
3020
+ break;
3021
+ case "ArrowLeft":
3022
+ e.preventDefault();
3023
+ seekTo(currentTime - 5);
3024
+ break;
3025
+ case "ArrowUp":
3026
+ e.preventDefault();
3027
+ setVolume((v) => Math.min(1, v + 0.1));
3028
+ break;
3029
+ case "ArrowDown":
3030
+ e.preventDefault();
3031
+ setVolume((v) => Math.max(0, v - 0.1));
3032
+ break;
3033
+ case "m":
3034
+ case "M":
3035
+ e.preventDefault();
3036
+ setMuted((m) => !m);
3037
+ break;
3038
+ }
3039
+ };
3040
+ const progress = duration > 0 ? currentTime / duration : 0;
3041
+ return /* @__PURE__ */ jsxs(
3042
+ "div",
3043
+ {
3044
+ role: "region",
3045
+ "aria-label": "Audio player",
3046
+ tabIndex: 0,
3047
+ onKeyDown: handleKey,
3048
+ "data-playing": playing || void 0,
3049
+ className: cn(
3050
+ "flex items-center gap-3 rounded-md border border-border bg-card p-2 text-card-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3051
+ compact && "gap-2 p-1.5",
3052
+ className
3053
+ ),
3054
+ children: [
3055
+ /* @__PURE__ */ jsx(
3056
+ "audio",
3057
+ {
3058
+ ref: audioRef,
3059
+ src,
3060
+ autoPlay,
3061
+ loop,
3062
+ onPlay: () => {
3063
+ setPlaying(true);
3064
+ onPlay?.();
3065
+ },
3066
+ onPause: () => {
3067
+ setPlaying(false);
3068
+ onPause?.();
3069
+ },
3070
+ onTimeUpdate: () => {
3071
+ const a = audioRef.current;
3072
+ if (!a) return;
3073
+ setCurrentTime(a.currentTime);
3074
+ onTimeUpdate?.(a.currentTime, a.duration);
3075
+ },
3076
+ onLoadedMetadata: () => {
3077
+ const a = audioRef.current;
3078
+ if (a) setDuration(a.duration || 0);
3079
+ },
3080
+ onEnded: () => {
3081
+ setPlaying(false);
3082
+ onEnded?.();
3083
+ },
3084
+ ...rest
3085
+ }
3086
+ ),
3087
+ /* @__PURE__ */ jsx(
3088
+ "button",
3089
+ {
3090
+ type: "button",
3091
+ "aria-label": playing ? "Pause" : "Play",
3092
+ onClick: togglePlay,
3093
+ className: cn(
3094
+ "inline-flex shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3095
+ compact ? "h-7 w-7" : "h-9 w-9"
3096
+ ),
3097
+ children: /* @__PURE__ */ jsx(Icon, { icon: playing ? Pause : Play, size: compact ? 12 : 14 })
3098
+ }
3099
+ ),
3100
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", style: { minWidth: "3.5rem" }, children: formatTime(currentTime) }),
3101
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: peaks ? /* @__PURE__ */ jsx(
3102
+ AudioWaveform,
3103
+ {
3104
+ peaks,
3105
+ progress,
3106
+ onSeek: seekProgress,
3107
+ width: compact ? 200 : 320,
3108
+ height: compact ? 32 : 40
3109
+ }
3110
+ ) : /* @__PURE__ */ jsx(
3111
+ "input",
3112
+ {
3113
+ type: "range",
3114
+ role: "slider",
3115
+ "aria-label": "Seek",
3116
+ "aria-valuetext": formatTime(currentTime),
3117
+ min: 0,
3118
+ max: duration || 0,
3119
+ step: "any",
3120
+ value: currentTime,
3121
+ onChange: (e) => seekTo(Number(e.target.value)),
3122
+ className: "w-full accent-primary"
3123
+ }
3124
+ ) }),
3125
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", style: { minWidth: "3.5rem", textAlign: "right" }, children: formatTime(duration) }),
3126
+ /* @__PURE__ */ jsx(
3127
+ "button",
3128
+ {
3129
+ type: "button",
3130
+ "aria-label": muted ? "Unmute" : "Mute",
3131
+ onClick: () => setMuted((m) => !m),
3132
+ className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
3133
+ children: /* @__PURE__ */ jsx(Icon, { icon: muted || volume === 0 ? VolumeX : Volume2, size: 14 })
3134
+ }
3135
+ ),
3136
+ /* @__PURE__ */ jsx(
3137
+ "select",
3138
+ {
3139
+ "aria-label": "Playback speed",
3140
+ value: speed,
3141
+ onChange: (e) => setSpeed(Number(e.target.value)),
3142
+ className: "h-7 rounded-sm border border-input bg-background px-1 text-xs",
3143
+ children: SPEEDS.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
3144
+ s,
3145
+ "\xD7"
3146
+ ] }, s))
3147
+ }
3148
+ )
3149
+ ]
3150
+ }
3151
+ );
3152
+ }
3153
+ );
3154
+ var SPEEDS2 = [0.5, 0.75, 1, 1.25, 1.5, 2];
3155
+ function formatTime2(s) {
3156
+ if (!Number.isFinite(s) || s < 0) return "0:00";
3157
+ const total = Math.floor(s);
3158
+ const h = Math.floor(total / 3600);
3159
+ const m = Math.floor(total % 3600 / 60);
3160
+ const sec = total % 60;
3161
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
3162
+ return `${m}:${String(sec).padStart(2, "0")}`;
3163
+ }
3164
+ var VideoPlayer = forwardRef(
3165
+ function VideoPlayer2({
3166
+ src,
3167
+ poster,
3168
+ tracks,
3169
+ aspectRatio = "16 / 9",
3170
+ autoPlay,
3171
+ loop,
3172
+ muted: mutedProp,
3173
+ defaultVolume = 1,
3174
+ defaultPlaybackRate = 1,
3175
+ className,
3176
+ ...rest
3177
+ }, forwardedRef) {
3178
+ const containerRef = useRef(null);
3179
+ const videoRef = useRef(null);
3180
+ useImperativeHandle(forwardedRef, () => videoRef.current);
3181
+ const [playing, setPlaying] = useState(!!autoPlay);
3182
+ const [currentTime, setCurrentTime] = useState(0);
3183
+ const [duration, setDuration] = useState(0);
3184
+ const [volume, setVolume] = useState(defaultVolume);
3185
+ const [muted, setMuted] = useState(!!mutedProp);
3186
+ const [speed, setSpeed] = useState(defaultPlaybackRate);
3187
+ const [fullscreen, setFullscreen] = useState(false);
3188
+ const [captionsOn, setCaptionsOn] = useState(false);
3189
+ const [showControls, setShowControls] = useState(true);
3190
+ const idleTimerRef = useRef(null);
3191
+ useEffect(() => {
3192
+ const v = videoRef.current;
3193
+ if (!v) return;
3194
+ v.volume = volume;
3195
+ v.muted = muted;
3196
+ v.playbackRate = speed;
3197
+ }, [volume, muted, speed]);
3198
+ useEffect(() => {
3199
+ const v = videoRef.current;
3200
+ if (!v || !v.textTracks) return;
3201
+ for (let i = 0; i < v.textTracks.length; i++) {
3202
+ v.textTracks[i].mode = captionsOn ? "showing" : "hidden";
3203
+ }
3204
+ }, [captionsOn, tracks]);
3205
+ useEffect(() => {
3206
+ const onFsChange = () => setFullscreen(document.fullscreenElement === containerRef.current);
3207
+ document.addEventListener("fullscreenchange", onFsChange);
3208
+ return () => document.removeEventListener("fullscreenchange", onFsChange);
3209
+ }, []);
3210
+ const togglePlay = useCallback(() => {
3211
+ const v = videoRef.current;
3212
+ if (!v) return;
3213
+ if (v.paused) v.play().catch(() => {
3214
+ });
3215
+ else v.pause();
3216
+ }, []);
3217
+ const seekTo = useCallback((seconds) => {
3218
+ const v = videoRef.current;
3219
+ if (!v || !Number.isFinite(seconds)) return;
3220
+ v.currentTime = Math.max(0, Math.min(v.duration || 0, seconds));
3221
+ }, []);
3222
+ const toggleFullscreen = useCallback(async () => {
3223
+ const c = containerRef.current;
3224
+ if (!c) return;
3225
+ if (!document.fullscreenElement) {
3226
+ await c.requestFullscreen?.().catch(() => {
3227
+ });
3228
+ } else {
3229
+ await document.exitFullscreen?.().catch(() => {
3230
+ });
3231
+ }
3232
+ }, []);
3233
+ const togglePiP = useCallback(async () => {
3234
+ const v = videoRef.current;
3235
+ if (!v) return;
3236
+ if ("pictureInPictureElement" in document && document.pictureInPictureElement) {
3237
+ await document.exitPictureInPicture?.()?.catch(() => {
3238
+ });
3239
+ } else if ("requestPictureInPicture" in v) {
3240
+ await v.requestPictureInPicture?.()?.catch(() => {
3241
+ });
3242
+ }
3243
+ }, []);
3244
+ const bumpControls = useCallback(() => {
3245
+ setShowControls(true);
3246
+ if (idleTimerRef.current != null) window.clearTimeout(idleTimerRef.current);
3247
+ if (playing) {
3248
+ idleTimerRef.current = window.setTimeout(() => setShowControls(false), 3e3);
3249
+ }
3250
+ }, [playing]);
3251
+ useEffect(() => {
3252
+ bumpControls();
3253
+ return () => {
3254
+ if (idleTimerRef.current != null) window.clearTimeout(idleTimerRef.current);
3255
+ };
3256
+ }, [playing, bumpControls]);
3257
+ const handleKey = (e) => {
3258
+ switch (e.key) {
3259
+ case " ":
3260
+ case "Spacebar":
3261
+ e.preventDefault();
3262
+ togglePlay();
3263
+ break;
3264
+ case "ArrowRight":
3265
+ e.preventDefault();
3266
+ seekTo(currentTime + 5);
3267
+ break;
3268
+ case "ArrowLeft":
3269
+ e.preventDefault();
3270
+ seekTo(currentTime - 5);
3271
+ break;
3272
+ case "ArrowUp":
3273
+ e.preventDefault();
3274
+ setVolume((v) => Math.min(1, v + 0.1));
3275
+ break;
3276
+ case "ArrowDown":
3277
+ e.preventDefault();
3278
+ setVolume((v) => Math.max(0, v - 0.1));
3279
+ break;
3280
+ case "m":
3281
+ case "M":
3282
+ e.preventDefault();
3283
+ setMuted((m) => !m);
3284
+ break;
3285
+ case "f":
3286
+ case "F":
3287
+ e.preventDefault();
3288
+ toggleFullscreen();
3289
+ break;
3290
+ case "c":
3291
+ case "C":
3292
+ e.preventDefault();
3293
+ if (tracks && tracks.length > 0) setCaptionsOn((c) => !c);
3294
+ break;
3295
+ }
3296
+ };
3297
+ return /* @__PURE__ */ jsxs(
3298
+ "div",
3299
+ {
3300
+ ref: containerRef,
3301
+ role: "region",
3302
+ "aria-label": "Video player",
3303
+ tabIndex: 0,
3304
+ onKeyDown: handleKey,
3305
+ onMouseMove: bumpControls,
3306
+ onMouseLeave: () => {
3307
+ if (playing) setShowControls(false);
3308
+ },
3309
+ className: cn(
3310
+ "group relative overflow-hidden rounded-md bg-black text-white shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3311
+ className
3312
+ ),
3313
+ style: { aspectRatio: typeof aspectRatio === "number" ? String(aspectRatio) : aspectRatio },
3314
+ children: [
3315
+ /* @__PURE__ */ jsx(
3316
+ "video",
3317
+ {
3318
+ ref: videoRef,
3319
+ src,
3320
+ poster,
3321
+ autoPlay,
3322
+ loop,
3323
+ muted,
3324
+ onPlay: () => setPlaying(true),
3325
+ onPause: () => setPlaying(false),
3326
+ onTimeUpdate: () => {
3327
+ const v = videoRef.current;
3328
+ if (!v) return;
3329
+ setCurrentTime(v.currentTime);
3330
+ },
3331
+ onLoadedMetadata: () => {
3332
+ const v = videoRef.current;
3333
+ if (v) setDuration(v.duration || 0);
3334
+ },
3335
+ onClick: togglePlay,
3336
+ className: "h-full w-full bg-black",
3337
+ ...rest,
3338
+ children: tracks?.map((t, i) => /* @__PURE__ */ jsx(
3339
+ "track",
3340
+ {
3341
+ src: t.src,
3342
+ srcLang: t.srcLang,
3343
+ label: t.label,
3344
+ kind: t.kind ?? "captions",
3345
+ default: t.default
3346
+ },
3347
+ i
3348
+ ))
3349
+ }
3350
+ ),
3351
+ !playing && /* @__PURE__ */ jsx(
3352
+ "button",
3353
+ {
3354
+ type: "button",
3355
+ "aria-label": "Play",
3356
+ onClick: togglePlay,
3357
+ className: "absolute inset-0 grid place-items-center bg-black/30 transition-opacity hover:bg-black/40",
3358
+ children: /* @__PURE__ */ jsx("span", { className: "grid h-16 w-16 place-items-center rounded-full bg-white/90 text-foreground shadow-lg", children: /* @__PURE__ */ jsx(Icon, { icon: Play, size: 28 }) })
3359
+ }
3360
+ ),
3361
+ /* @__PURE__ */ jsxs(
3362
+ "div",
3363
+ {
3364
+ className: cn(
3365
+ "absolute inset-x-0 bottom-0 flex items-center gap-2 bg-gradient-to-t from-black/80 to-transparent px-3 pb-2 pt-6 transition-opacity",
3366
+ showControls ? "opacity-100" : "opacity-0"
3367
+ ),
3368
+ children: [
3369
+ /* @__PURE__ */ jsx(
3370
+ "button",
3371
+ {
3372
+ type: "button",
3373
+ "aria-label": playing ? "Pause" : "Play",
3374
+ onClick: togglePlay,
3375
+ className: "inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/20 text-white hover:bg-white/30",
3376
+ children: /* @__PURE__ */ jsx(Icon, { icon: playing ? Pause : Play, size: 14 })
3377
+ }
3378
+ ),
3379
+ /* @__PURE__ */ jsxs("span", { className: "text-xs tabular-nums", children: [
3380
+ formatTime2(currentTime),
3381
+ " / ",
3382
+ formatTime2(duration)
3383
+ ] }),
3384
+ /* @__PURE__ */ jsx(
3385
+ "input",
3386
+ {
3387
+ type: "range",
3388
+ role: "slider",
3389
+ "aria-label": "Seek",
3390
+ "aria-valuetext": formatTime2(currentTime),
3391
+ min: 0,
3392
+ max: duration || 0,
3393
+ step: "any",
3394
+ value: currentTime,
3395
+ onChange: (e) => seekTo(Number(e.target.value)),
3396
+ className: "flex-1 accent-primary"
3397
+ }
3398
+ ),
3399
+ /* @__PURE__ */ jsx(
3400
+ "button",
3401
+ {
3402
+ type: "button",
3403
+ "aria-label": muted || volume === 0 ? "Unmute" : "Mute",
3404
+ onClick: () => setMuted((m) => !m),
3405
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3406
+ children: /* @__PURE__ */ jsx(Icon, { icon: muted || volume === 0 ? VolumeX : Volume2, size: 14 })
3407
+ }
3408
+ ),
3409
+ /* @__PURE__ */ jsx(
3410
+ "select",
3411
+ {
3412
+ "aria-label": "Playback speed",
3413
+ value: speed,
3414
+ onChange: (e) => setSpeed(Number(e.target.value)),
3415
+ className: "h-7 rounded-sm border border-white/20 bg-black/40 px-1 text-xs",
3416
+ children: SPEEDS2.map((s) => /* @__PURE__ */ jsxs("option", { value: s, className: "text-foreground", children: [
3417
+ s,
3418
+ "\xD7"
3419
+ ] }, s))
3420
+ }
3421
+ ),
3422
+ tracks && tracks.length > 0 && /* @__PURE__ */ jsx(
3423
+ "button",
3424
+ {
3425
+ type: "button",
3426
+ "aria-label": captionsOn ? "Hide captions" : "Show captions",
3427
+ "aria-pressed": captionsOn,
3428
+ onClick: () => setCaptionsOn((c) => !c),
3429
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3430
+ children: /* @__PURE__ */ jsx(Icon, { icon: captionsOn ? Captions : CaptionsOff, size: 14 })
3431
+ }
3432
+ ),
3433
+ /* @__PURE__ */ jsx(
3434
+ "button",
3435
+ {
3436
+ type: "button",
3437
+ "aria-label": "Picture in picture",
3438
+ onClick: togglePiP,
3439
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3440
+ children: /* @__PURE__ */ jsx(Icon, { icon: PictureInPicture2, size: 14 })
3441
+ }
3442
+ ),
3443
+ /* @__PURE__ */ jsx(
3444
+ "button",
3445
+ {
3446
+ type: "button",
3447
+ "aria-label": fullscreen ? "Exit fullscreen" : "Enter fullscreen",
3448
+ onClick: toggleFullscreen,
3449
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3450
+ children: /* @__PURE__ */ jsx(Icon, { icon: fullscreen ? Minimize : Maximize, size: 14 })
3451
+ }
3452
+ )
3453
+ ]
3454
+ }
3455
+ )
3456
+ ]
3457
+ }
3458
+ );
3459
+ }
3460
+ );
3461
+ var ZOOM_LEVELS = [50, 75, 100, 125, 150, 175, 200, 250, 300, 400];
3462
+ var PDFViewer = forwardRef(function PDFViewer2({
3463
+ src,
3464
+ page: pageProp,
3465
+ defaultPage = 1,
3466
+ onPageChange,
3467
+ zoom: zoomProp,
3468
+ defaultZoom = 100,
3469
+ onZoomChange,
3470
+ pageCount,
3471
+ title = "PDF document",
3472
+ download = true,
3473
+ height = "70vh",
3474
+ className,
3475
+ ...rest
3476
+ }, ref) {
3477
+ const [page, setPage] = useControlled({
3478
+ controlled: pageProp,
3479
+ default: defaultPage,
3480
+ onChange: onPageChange
3481
+ });
3482
+ const [zoom, setZoom] = useControlled({
3483
+ controlled: zoomProp,
3484
+ default: defaultZoom,
3485
+ onChange: onZoomChange
3486
+ });
3487
+ const hashedSrc = useMemo(() => {
3488
+ try {
3489
+ const url = new URL(src, typeof window !== "undefined" ? window.location.href : "http://localhost");
3490
+ url.hash = `page=${page}&zoom=${zoom}`;
3491
+ return url.toString();
3492
+ } catch {
3493
+ return `${src}#page=${page}&zoom=${zoom}`;
3494
+ }
3495
+ }, [src, page, zoom]);
3496
+ const goPrev = () => setPage(Math.max(1, page - 1));
3497
+ const goNext = () => setPage(pageCount ? Math.min(pageCount, page + 1) : page + 1);
3498
+ const zoomIn = () => {
3499
+ const next = ZOOM_LEVELS.find((z) => z > zoom);
3500
+ if (next) setZoom(next);
3501
+ };
3502
+ const zoomOut = () => {
3503
+ const next = [...ZOOM_LEVELS].reverse().find((z) => z < zoom);
3504
+ if (next) setZoom(next);
3505
+ };
3506
+ return /* @__PURE__ */ jsxs(
3507
+ "div",
3508
+ {
3509
+ ref,
3510
+ className: cn(
3511
+ "flex flex-col overflow-hidden rounded-md border border-border bg-card text-card-foreground shadow-sm",
3512
+ className
3513
+ ),
3514
+ ...rest,
3515
+ children: [
3516
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 border-b border-border bg-muted/40 px-2 py-1.5", children: [
3517
+ /* @__PURE__ */ jsx(
3518
+ "button",
3519
+ {
3520
+ type: "button",
3521
+ "aria-label": "Previous page",
3522
+ onClick: goPrev,
3523
+ disabled: page <= 1,
3524
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3525
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronLeft, size: 14 })
3526
+ }
3527
+ ),
3528
+ /* @__PURE__ */ jsx("span", { className: "px-1 text-xs tabular-nums text-foreground", children: pageCount ? `${page} / ${pageCount}` : `Page ${page}` }),
3529
+ /* @__PURE__ */ jsx(
3530
+ "button",
3531
+ {
3532
+ type: "button",
3533
+ "aria-label": "Next page",
3534
+ onClick: goNext,
3535
+ disabled: pageCount != null && page >= pageCount,
3536
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3537
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronRight, size: 14 })
3538
+ }
3539
+ ),
3540
+ /* @__PURE__ */ jsx("span", { className: "mx-2 h-4 w-px bg-border", "aria-hidden": "true" }),
3541
+ /* @__PURE__ */ jsx(
3542
+ "button",
3543
+ {
3544
+ type: "button",
3545
+ "aria-label": "Zoom out",
3546
+ onClick: zoomOut,
3547
+ disabled: zoom <= ZOOM_LEVELS[0],
3548
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3549
+ children: /* @__PURE__ */ jsx(Icon, { icon: ZoomOut, size: 14 })
3550
+ }
3551
+ ),
3552
+ /* @__PURE__ */ jsxs("span", { className: "px-1 text-xs tabular-nums text-foreground", children: [
3553
+ zoom,
3554
+ "%"
3555
+ ] }),
3556
+ /* @__PURE__ */ jsx(
3557
+ "button",
3558
+ {
3559
+ type: "button",
3560
+ "aria-label": "Zoom in",
3561
+ onClick: zoomIn,
3562
+ disabled: zoom >= ZOOM_LEVELS[ZOOM_LEVELS.length - 1],
3563
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3564
+ children: /* @__PURE__ */ jsx(Icon, { icon: ZoomIn, size: 14 })
3565
+ }
3566
+ ),
3567
+ /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1", children: download && /* @__PURE__ */ jsx(
3568
+ "a",
3569
+ {
3570
+ href: src,
3571
+ download: true,
3572
+ "aria-label": "Download PDF",
3573
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
3574
+ children: /* @__PURE__ */ jsx(Icon, { icon: Download, size: 14 })
3575
+ }
3576
+ ) })
3577
+ ] }),
3578
+ /* @__PURE__ */ jsx(
3579
+ "iframe",
3580
+ {
3581
+ src: hashedSrc,
3582
+ title,
3583
+ style: { height, border: 0 },
3584
+ className: "w-full bg-muted"
3585
+ }
3586
+ )
3587
+ ]
3588
+ }
3589
+ );
3590
+ });
2123
3591
 
2124
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Avatar, AvatarGroup, Badge, BadgeOverlay, Card, Carousel, CarouselDot, CarouselDots, CarouselNext, CarouselPrev, CarouselSlide, CarouselSlides, CarouselViewport, Code, Collapsible, CollapsibleContent, CollapsibleTrigger, CountBadge, DataTable, DescriptionList, DiffViewer, EmptyState, Heading, Highlight, Image, InfoRow, Kbd, KeyboardShortcut, List, ListItem, Mark, NotificationDot, Quote, SectionHeader, Separator, Snippet, Stat, Status, SwipeActions, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeaderCell, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Text, Timeline, TimelineDescription, TimelineItem, TimelineTitle, Tooltip, Tree, TreeGroup, TreeItem, avatarVariants, badgeVariants, codeVariants, headingVariants, textVariants };
2125
- //# sourceMappingURL=chunk-ZP4CBWO2.js.map
2126
- //# sourceMappingURL=chunk-ZP4CBWO2.js.map
3592
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AudioPlayer, AudioWaveform, Avatar, AvatarGroup, Badge, BadgeOverlay, Card, Carousel, CarouselDot, CarouselDots, CarouselNext, CarouselPrev, CarouselSlide, CarouselSlides, CarouselViewport, Code, Collapsible, CollapsibleContent, CollapsibleTrigger, CountBadge, DataGrid, DataTable, DescriptionList, DiffViewer, EmptyState, Heading, HeatmapCalendar, Highlight, Image, InfoRow, Kbd, KeyboardShortcut, List, ListItem, Mark, NodeEditor, NotificationDot, PDFViewer, Quote, SectionHeader, Separator, Snippet, Sparkline, Stat, Status, SwipeActions, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeaderCell, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Text, Timeline, TimelineDescription, TimelineItem, TimelineTitle, Tooltip, Tree, TreeGroup, TreeItem, VideoPlayer, avatarVariants, badgeVariants, codeVariants, headingVariants, textVariants };
3593
+ //# sourceMappingURL=chunk-CBOC2DT2.js.map
3594
+ //# sourceMappingURL=chunk-CBOC2DT2.js.map