@tollerud/ui 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2650 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import * as React2 from 'react';
4
+ import { forwardRef, useState, useCallback, lazy, useRef, useEffect, useMemo, useId, Suspense } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ import { motion } from 'framer-motion';
7
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
8
+ import { X } from 'lucide-react';
9
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
10
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
11
+ import * as ProgressPrimitive from '@radix-ui/react-progress';
12
+ import { Toaster as Toaster$1 } from 'sonner';
13
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
14
+
15
+ // lib/utils.ts
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ var variants = {
20
+ primary: "bg-tollerud-yellow text-tollerud-black border-tollerud-yellow hover:bg-tollerud-yellow-bright hover:shadow-tollerud-glow",
21
+ secondary: "bg-transparent text-tollerud-text-primary border-tollerud-border hover:border-tollerud-text-secondary hover:bg-tollerud-surface-hover",
22
+ ghost: "bg-transparent text-tollerud-text-secondary border-transparent hover:text-tollerud-text-primary hover:bg-tollerud-surface-hover",
23
+ destructive: "bg-tollerud-error text-white border-tollerud-error hover:shadow-[0_0_12px_rgba(239,68,68,0.3)]",
24
+ terminal: 'font-mono text-tollerud-yellow border-[rgba(232,213,0,0.25)] bg-transparent before:content-["\u276F_"] before:opacity-70 hover:border-tollerud-yellow hover:shadow-tollerud-glow hover:bg-[rgba(232,213,0,0.05)]'
25
+ };
26
+ var sizes = {
27
+ sm: "px-3 py-1 text-sm",
28
+ md: "px-4 py-2 text-base",
29
+ lg: "px-6 py-3 text-lg"
30
+ };
31
+ var Button = forwardRef(
32
+ ({ className, variant = "secondary", size = "md", ...props }, ref) => {
33
+ return /* @__PURE__ */ jsx(
34
+ "button",
35
+ {
36
+ ref,
37
+ className: cn(
38
+ "inline-flex items-center justify-center gap-2 font-semibold rounded transition-all duration-[150ms] focus-visible:outline-2 focus-visible:outline-tollerud-yellow focus-visible:outline-offset-2",
39
+ "border cursor-pointer",
40
+ "disabled:opacity-50 disabled:pointer-events-none",
41
+ variants[variant],
42
+ sizes[size],
43
+ className
44
+ ),
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ );
50
+ Button.displayName = "Button";
51
+ var toneStyles = {
52
+ default: { wrapper: "border-tollerud-border bg-tollerud-surface-raised", icon: "text-tollerud-text-muted" },
53
+ accent: { wrapper: "border-tollerud-yellow/30 bg-tollerud-yellow/5", icon: "text-tollerud-yellow" },
54
+ info: { wrapper: "border-blue-500/30 bg-blue-500/5", icon: "text-blue-400" },
55
+ success: { wrapper: "border-green-500/30 bg-green-500/5", icon: "text-green-400" },
56
+ error: { wrapper: "border-red-500/30 bg-red-500/5", icon: "text-red-400" }
57
+ };
58
+ var Alert = forwardRef(
59
+ ({ className, tone = "default", title, icon, children, ...props }, ref) => {
60
+ const styles = toneStyles[tone];
61
+ return /* @__PURE__ */ jsxs(
62
+ "div",
63
+ {
64
+ ref,
65
+ role: "alert",
66
+ className: cn(
67
+ "flex gap-3 rounded-md border p-4",
68
+ styles.wrapper,
69
+ className
70
+ ),
71
+ ...props,
72
+ children: [
73
+ icon && /* @__PURE__ */ jsx("span", { className: cn("mt-0.5 flex-shrink-0 w-[18px] h-[18px]", styles.icon), children: icon }),
74
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
75
+ title && /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-tollerud-text-primary leading-snug", children: title }),
76
+ children && /* @__PURE__ */ jsx("p", { className: cn("text-sm text-tollerud-text-secondary leading-relaxed", title && "mt-1"), children })
77
+ ] })
78
+ ]
79
+ }
80
+ );
81
+ }
82
+ );
83
+ Alert.displayName = "Alert";
84
+ var Card = forwardRef(
85
+ ({ className, accent, density, ...props }, ref) => {
86
+ return /* @__PURE__ */ jsx(
87
+ "div",
88
+ {
89
+ ref,
90
+ "data-density": density ?? void 0,
91
+ className: cn(
92
+ "rounded-lg border bg-tollerud-surface-raised p-6 transition-[border-color] duration-[150ms]",
93
+ accent ? "border-tollerud-yellow/25" : "border-tollerud-border",
94
+ "hover:border-tollerud-noir-500",
95
+ className
96
+ ),
97
+ ...props
98
+ }
99
+ );
100
+ }
101
+ );
102
+ Card.displayName = "Card";
103
+ var badgeVariants = {
104
+ default: "bg-tollerud-noir-700 text-tollerud-text-secondary",
105
+ accent: "bg-tollerud-yellow/15 text-tollerud-yellow",
106
+ success: "bg-tollerud-success/15 text-tollerud-success",
107
+ error: "bg-tollerud-error/15 text-tollerud-error",
108
+ info: "bg-tollerud-info/15 text-tollerud-info",
109
+ warning: "bg-tollerud-yellow/15 text-tollerud-warning"
110
+ };
111
+ var Badge = forwardRef(
112
+ ({ className, variant = "default", ...props }, ref) => {
113
+ return /* @__PURE__ */ jsx(
114
+ "span",
115
+ {
116
+ ref,
117
+ className: cn(
118
+ "inline-flex items-center px-2 py-0.5 text-xs font-medium rounded",
119
+ "tracking-wide",
120
+ badgeVariants[variant],
121
+ className
122
+ ),
123
+ ...props
124
+ }
125
+ );
126
+ }
127
+ );
128
+ Badge.displayName = "Badge";
129
+ var statusColors = {
130
+ online: "bg-tollerud-success",
131
+ offline: "bg-tollerud-error",
132
+ warning: "bg-tollerud-yellow",
133
+ idle: "bg-tollerud-noir-400"
134
+ };
135
+ var pulseColors = {
136
+ online: "rgba(34,197,94,0.5)",
137
+ warning: "rgba(232,213,0,0.45)",
138
+ offline: "rgba(239,68,68,0.5)"
139
+ };
140
+ var animationKeyframes = `
141
+ @keyframes tollerud-dot-pulse {
142
+ 0%, 100% { box-shadow: 0 0 0 0 var(--pulse-color, rgba(34,197,94,0.5)); }
143
+ 50% { box-shadow: 0 0 0 5px var(--pulse-color, rgba(34,197,94,0)); }
144
+ }
145
+ `;
146
+ var StatusDot = forwardRef(
147
+ ({ className, status = "idle", label, noPulse, ...props }, ref) => {
148
+ const shouldPulse = !noPulse && (status === "online" || status === "warning" || status === "offline");
149
+ const pulseColor = pulseColors[status];
150
+ return /* @__PURE__ */ jsxs(
151
+ "span",
152
+ {
153
+ ref,
154
+ className: cn("inline-flex items-center gap-1.5 text-xs font-medium", className),
155
+ ...props,
156
+ children: [
157
+ /* @__PURE__ */ jsx("style", { children: animationKeyframes }),
158
+ /* @__PURE__ */ jsx(
159
+ motion.span,
160
+ {
161
+ layout: true,
162
+ animate: {
163
+ scaleX: [1, 1.6, 1],
164
+ scaleY: [1, 0.6, 1]
165
+ },
166
+ transition: {
167
+ duration: 0.4,
168
+ ease: "easeOut",
169
+ times: [0, 0.3, 1]
170
+ },
171
+ className: cn(
172
+ "inline-block w-2 h-2 rounded-full",
173
+ statusColors[status],
174
+ shouldPulse && "animate-tollerud-dot-pulse"
175
+ ),
176
+ style: {
177
+ ...shouldPulse && pulseColor ? { "--pulse-color": pulseColor } : {},
178
+ animation: shouldPulse ? "tollerud-dot-pulse 2s ease-in-out infinite" : void 0
179
+ }
180
+ },
181
+ status
182
+ ),
183
+ label && /* @__PURE__ */ jsx("span", { children: label })
184
+ ]
185
+ }
186
+ );
187
+ }
188
+ );
189
+ StatusDot.displayName = "StatusDot";
190
+ var Input = forwardRef(
191
+ ({ className, label, error, id, ...props }, ref) => {
192
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
193
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
194
+ /* @__PURE__ */ jsx(
195
+ "input",
196
+ {
197
+ ref,
198
+ id,
199
+ className: cn(
200
+ "font-sans text-base px-3 py-2 rounded",
201
+ "bg-tollerud-surface-raised border",
202
+ "text-tollerud-text-primary",
203
+ "placeholder:text-tollerud-text-muted",
204
+ "transition-[border-color] duration-[150ms]",
205
+ "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
206
+ error ? "border-tollerud-error" : "border-tollerud-border",
207
+ className
208
+ ),
209
+ ...props
210
+ }
211
+ ),
212
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
213
+ ] });
214
+ }
215
+ );
216
+ Input.displayName = "Input";
217
+ var CodeBlock = forwardRef(
218
+ ({ className, children, code, promptPrefix, showCopy = true, ...props }, ref) => {
219
+ const [copied, setCopied] = useState(false);
220
+ const content = code ? /* @__PURE__ */ jsx("code", { className: "text-tollerud-noir-200", children: code }) : children;
221
+ const handleCopy = useCallback(async () => {
222
+ const text = code ?? (typeof children === "string" ? children : "");
223
+ if (!text) return;
224
+ try {
225
+ await navigator.clipboard.writeText(text);
226
+ } catch {
227
+ const textarea = document.createElement("textarea");
228
+ textarea.value = text;
229
+ textarea.style.position = "fixed";
230
+ textarea.style.opacity = "0";
231
+ document.body.appendChild(textarea);
232
+ textarea.select();
233
+ document.execCommand("copy");
234
+ document.body.removeChild(textarea);
235
+ }
236
+ setCopied(true);
237
+ setTimeout(() => setCopied(false), 1500);
238
+ }, [code, children]);
239
+ return /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
240
+ /* @__PURE__ */ jsx(
241
+ "pre",
242
+ {
243
+ ref,
244
+ className: cn(
245
+ "font-mono text-sm leading-relaxed overflow-x-auto rounded border p-4",
246
+ "bg-tollerud-noir-900 border-tollerud-border text-tollerud-noir-200",
247
+ className
248
+ ),
249
+ ...props,
250
+ children: content
251
+ }
252
+ ),
253
+ showCopy && /* @__PURE__ */ jsx(
254
+ "button",
255
+ {
256
+ onClick: handleCopy,
257
+ className: cn(
258
+ "absolute top-2 right-2 px-2 py-1 rounded text-xs font-medium",
259
+ "opacity-0 group-hover:opacity-100 transition-all",
260
+ "bg-tollerud-noir-800 border border-tollerud-border/30 text-tollerud-text-muted",
261
+ "hover:bg-tollerud-surface-raised hover:text-tollerud-text-primary",
262
+ copied && "opacity-100 bg-tollerud-success/15 text-tollerud-success border-tollerud-success/40"
263
+ ),
264
+ "aria-label": copied ? "Copied" : "Copy code",
265
+ children: copied ? "\u2713 Copied" : "Copy"
266
+ }
267
+ )
268
+ ] });
269
+ }
270
+ );
271
+ CodeBlock.displayName = "CodeBlock";
272
+ var StatCard = forwardRef(
273
+ ({ className, label, value, change, accent, ...props }, ref) => {
274
+ return /* @__PURE__ */ jsxs(
275
+ "div",
276
+ {
277
+ ref,
278
+ className: cn(
279
+ "group relative rounded-lg border p-4",
280
+ "bg-tollerud-surface-raised",
281
+ "transition-all duration-200 ease-out",
282
+ "hover:translate-y-[-1px]",
283
+ accent ? "border-tollerud-yellow/20 hover:border-tollerud-yellow/40 hover:shadow-[0_0_20px_rgba(232,213,0,0.08)]" : "border-tollerud-border hover:border-tollerud-noir-500 hover:shadow-[0_4px_12px_rgba(0,0,0,0.3)]",
284
+ className
285
+ ),
286
+ ...props,
287
+ children: [
288
+ accent && /* @__PURE__ */ jsx("span", { className: "absolute top-0 left-4 right-4 h-px bg-gradient-to-r from-transparent via-tollerud-yellow/60 to-transparent" }),
289
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
290
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wider text-tollerud-text-muted", children: label }),
291
+ change && /* @__PURE__ */ jsxs(
292
+ "span",
293
+ {
294
+ className: cn(
295
+ "inline-flex items-center gap-0.5 text-[11px] font-semibold whitespace-nowrap",
296
+ change.direction === "up" ? "text-tollerud-success" : "text-tollerud-error"
297
+ ),
298
+ children: [
299
+ /* @__PURE__ */ jsx(
300
+ "svg",
301
+ {
302
+ className: cn(
303
+ "w-3 h-3",
304
+ change.direction === "down" && "rotate-180"
305
+ ),
306
+ viewBox: "0 0 24 24",
307
+ fill: "none",
308
+ stroke: "currentColor",
309
+ strokeWidth: 2.5,
310
+ strokeLinecap: "round",
311
+ strokeLinejoin: "round",
312
+ children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12l7 7 7-7" })
313
+ }
314
+ ),
315
+ change.value
316
+ ]
317
+ }
318
+ )
319
+ ] }),
320
+ /* @__PURE__ */ jsx(
321
+ "p",
322
+ {
323
+ className: cn(
324
+ "text-2xl font-bold tracking-tight mt-1",
325
+ accent ? "text-tollerud-yellow" : "text-tollerud-text-primary"
326
+ ),
327
+ children: value
328
+ }
329
+ )
330
+ ]
331
+ }
332
+ );
333
+ }
334
+ );
335
+ StatCard.displayName = "StatCard";
336
+ var Container = forwardRef(
337
+ ({ className, as: Tag = "div", ...props }, ref) => {
338
+ return /* @__PURE__ */ jsx(
339
+ Tag,
340
+ {
341
+ ref,
342
+ className: cn("mx-auto w-full max-w-[1100px] px-6", className),
343
+ ...props
344
+ }
345
+ );
346
+ }
347
+ );
348
+ Container.displayName = "Container";
349
+ var GrainGradient = lazy(
350
+ () => import('@paper-design/shaders-react').then((module) => ({
351
+ default: module.GrainGradient
352
+ }))
353
+ );
354
+ var intensityMap = {
355
+ subtle: 0.24,
356
+ medium: 0.45,
357
+ loud: 0.68
358
+ };
359
+ var speedMap = {
360
+ still: 0,
361
+ slow: 0.45,
362
+ medium: 1,
363
+ fast: 1.8
364
+ };
365
+ var grainMap = {
366
+ none: 0,
367
+ soft: 0.12,
368
+ high: 0.28
369
+ };
370
+ function cx(...classes) {
371
+ return classes.filter(Boolean).join(" ");
372
+ }
373
+ function CssFallback({ preserveCenter = true, noiseOverlay = true }) {
374
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
375
+ /* @__PURE__ */ jsx("div", { className: "tollerud-noir-glow-bg", "aria-hidden": "true" }),
376
+ preserveCenter && /* @__PURE__ */ jsx("div", { className: "tollerud-noir-glow-vignette", "aria-hidden": "true" }),
377
+ noiseOverlay && /* @__PURE__ */ jsx("div", { className: "tollerud-noir-noise", "aria-hidden": "true" })
378
+ ] });
379
+ }
380
+ function NoirGlowBackground({
381
+ className,
382
+ style,
383
+ shape = "corners",
384
+ intensity = "medium",
385
+ speed = "medium",
386
+ grain = "none",
387
+ softness = 0.76,
388
+ colorBack = "hsl(0, 0%, 0%)",
389
+ colors = ["hsl(54, 85%, 66%)", "hsl(56, 100%, 80%)", "hsl(56, 100%, 50%)"],
390
+ preserveCenter = false,
391
+ noiseOverlay = false,
392
+ forceCssFallback = false,
393
+ inert = true
394
+ }) {
395
+ const wrapperClassName = cx(
396
+ "tollerud-noir-glow-root absolute inset-0 z-0 overflow-hidden",
397
+ inert && "pointer-events-none",
398
+ className
399
+ );
400
+ if (forceCssFallback) {
401
+ return /* @__PURE__ */ jsx("div", { className: wrapperClassName, style, "aria-hidden": "true", children: /* @__PURE__ */ jsx(CssFallback, { preserveCenter, noiseOverlay }) });
402
+ }
403
+ return /* @__PURE__ */ jsxs("div", { className: wrapperClassName, style, "aria-hidden": "true", children: [
404
+ /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(CssFallback, { preserveCenter, noiseOverlay }), children: /* @__PURE__ */ jsx(
405
+ GrainGradient,
406
+ {
407
+ style: { height: "100%", width: "100%" },
408
+ colorBack,
409
+ softness,
410
+ intensity: intensityMap[intensity],
411
+ noise: grainMap[grain],
412
+ shape,
413
+ offsetX: 0,
414
+ offsetY: 0,
415
+ scale: 1,
416
+ rotation: 0,
417
+ speed: speedMap[speed],
418
+ colors
419
+ }
420
+ ) }),
421
+ preserveCenter && /* @__PURE__ */ jsx("div", { className: "tollerud-noir-glow-vignette", "aria-hidden": "true" }),
422
+ noiseOverlay && /* @__PURE__ */ jsx("div", { className: "tollerud-noir-noise", "aria-hidden": "true" })
423
+ ] });
424
+ }
425
+ var Kbd = forwardRef(
426
+ ({ className, keys, size = "md", ...props }, ref) => {
427
+ const keyArray = typeof keys === "string" ? [keys] : keys;
428
+ return /* @__PURE__ */ jsx(
429
+ "span",
430
+ {
431
+ ref,
432
+ className: cn(
433
+ "tollerud-kbd",
434
+ size === "sm" && "tollerud-kbd--sm",
435
+ className
436
+ ),
437
+ ...props,
438
+ children: keyArray.map((key, i) => /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: key }, i))
439
+ }
440
+ );
441
+ }
442
+ );
443
+ Kbd.displayName = "Kbd";
444
+ var ActionRow = forwardRef(
445
+ ({
446
+ action,
447
+ highlighted = false,
448
+ showShortcut = true,
449
+ className,
450
+ disabled,
451
+ onClick,
452
+ ...props
453
+ }, ref) => {
454
+ return /* @__PURE__ */ jsxs(
455
+ "button",
456
+ {
457
+ ref,
458
+ type: "button",
459
+ disabled: disabled ?? action.disabled,
460
+ onClick: (e) => {
461
+ onClick?.(e);
462
+ action.onSelect?.();
463
+ },
464
+ className: cn(
465
+ "tollerud-action-row",
466
+ highlighted && "tollerud-action-row--highlighted",
467
+ (disabled ?? action.disabled) && "tollerud-action-row--disabled",
468
+ className
469
+ ),
470
+ role: "option",
471
+ "aria-selected": highlighted,
472
+ "aria-disabled": disabled ?? action.disabled,
473
+ ...props,
474
+ children: [
475
+ action.icon && /* @__PURE__ */ jsx("span", { className: "tollerud-action-row__icon", children: action.icon }),
476
+ /* @__PURE__ */ jsxs("span", { className: "tollerud-action-row__content", children: [
477
+ /* @__PURE__ */ jsx("span", { className: "tollerud-action-row__label", children: action.label }),
478
+ action.description && /* @__PURE__ */ jsx("span", { className: "tollerud-action-row__description", children: action.description })
479
+ ] }),
480
+ showShortcut && action.shortcut && /* @__PURE__ */ jsx("span", { className: "tollerud-action-row__shortcut", children: /* @__PURE__ */ jsx(Kbd, { keys: action.shortcut, size: "sm" }) })
481
+ ]
482
+ }
483
+ );
484
+ }
485
+ );
486
+ ActionRow.displayName = "ActionRow";
487
+ var CommandMenu = forwardRef(
488
+ ({
489
+ open,
490
+ onOpenChange,
491
+ groups,
492
+ placeholder = "Type a command\u2026",
493
+ emptyMessage = "No matching commands",
494
+ className,
495
+ filter: customFilter,
496
+ onAction
497
+ }, ref) => {
498
+ const [query, setQuery] = useState("");
499
+ const [selectedIndex, setSelectedIndex] = useState(0);
500
+ const inputRef = useRef(null);
501
+ const listRef = useRef(null);
502
+ const flatItems = groups.flatMap((g) => g.items);
503
+ const handleKeyDown = useCallback(
504
+ (e) => {
505
+ if (e.key === "Escape") {
506
+ e.preventDefault();
507
+ onOpenChange(false);
508
+ return;
509
+ }
510
+ if (e.key === "ArrowDown") {
511
+ e.preventDefault();
512
+ setSelectedIndex(
513
+ (prev) => prev < flatItems.length - 1 ? prev + 1 : 0
514
+ );
515
+ return;
516
+ }
517
+ if (e.key === "ArrowUp") {
518
+ e.preventDefault();
519
+ setSelectedIndex(
520
+ (prev) => prev > 0 ? prev - 1 : flatItems.length - 1
521
+ );
522
+ return;
523
+ }
524
+ if (e.key === "Enter") {
525
+ e.preventDefault();
526
+ const item = flatItems[selectedIndex];
527
+ if (item && !item.disabled) {
528
+ item.onSelect?.();
529
+ onAction?.(item);
530
+ onOpenChange(false);
531
+ }
532
+ return;
533
+ }
534
+ },
535
+ [flatItems, selectedIndex, onOpenChange, onAction]
536
+ );
537
+ useEffect(() => {
538
+ function handleGlobalKey(e) {
539
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
540
+ e.preventDefault();
541
+ onOpenChange(!open);
542
+ }
543
+ }
544
+ document.addEventListener("keydown", handleGlobalKey);
545
+ return () => document.removeEventListener("keydown", handleGlobalKey);
546
+ }, [open, onOpenChange]);
547
+ useEffect(() => {
548
+ if (open) {
549
+ setQuery("");
550
+ setSelectedIndex(0);
551
+ const timer = setTimeout(() => inputRef.current?.focus(), 50);
552
+ return () => clearTimeout(timer);
553
+ }
554
+ }, [open]);
555
+ useEffect(() => {
556
+ if (open) {
557
+ document.body.style.overflow = "hidden";
558
+ } else {
559
+ document.body.style.overflow = "";
560
+ }
561
+ return () => {
562
+ document.body.style.overflow = "";
563
+ };
564
+ }, [open]);
565
+ const filteredGroups = customFilter ? customFilter(query, groups) : query.trim() ? groups.map((g) => ({
566
+ ...g,
567
+ items: g.items.filter(
568
+ (item) => item.label.toLowerCase().includes(query.toLowerCase()) || item.description?.toLowerCase().includes(query.toLowerCase()) || item.group?.toLowerCase().includes(query.toLowerCase())
569
+ )
570
+ })).filter((g) => g.items.length > 0) : groups;
571
+ const currentFlat = filteredGroups.flatMap((g) => g.items);
572
+ useEffect(() => {
573
+ if (selectedIndex >= currentFlat.length) {
574
+ setSelectedIndex(0);
575
+ }
576
+ }, [currentFlat.length, selectedIndex]);
577
+ if (!open) return null;
578
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
579
+ /* @__PURE__ */ jsx(
580
+ "div",
581
+ {
582
+ className: "tollerud-cmd-overlay",
583
+ onClick: () => onOpenChange(false),
584
+ "aria-hidden": "true"
585
+ }
586
+ ),
587
+ /* @__PURE__ */ jsxs(
588
+ "div",
589
+ {
590
+ className: cn("tollerud-cmd", className),
591
+ role: "listbox",
592
+ "aria-label": "Command palette",
593
+ onKeyDown: handleKeyDown,
594
+ children: [
595
+ /* @__PURE__ */ jsxs("div", { className: "tollerud-cmd__header", children: [
596
+ /* @__PURE__ */ jsx("span", { className: "tollerud-cmd__search-icon", children: /* @__PURE__ */ jsxs(
597
+ "svg",
598
+ {
599
+ width: "18",
600
+ height: "18",
601
+ viewBox: "0 0 24 24",
602
+ fill: "none",
603
+ stroke: "currentColor",
604
+ strokeWidth: "2",
605
+ strokeLinecap: "round",
606
+ strokeLinejoin: "round",
607
+ children: [
608
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
609
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.35-4.35" })
610
+ ]
611
+ }
612
+ ) }),
613
+ /* @__PURE__ */ jsx(
614
+ "input",
615
+ {
616
+ ref: inputRef,
617
+ type: "text",
618
+ className: "tollerud-cmd__input",
619
+ placeholder,
620
+ value: query,
621
+ onChange: (e) => {
622
+ setQuery(e.target.value);
623
+ setSelectedIndex(0);
624
+ },
625
+ autoComplete: "off",
626
+ spellCheck: false
627
+ }
628
+ )
629
+ ] }),
630
+ /* @__PURE__ */ jsxs("div", { ref: listRef, className: "tollerud-cmd__list", children: [
631
+ filteredGroups.length === 0 && /* @__PURE__ */ jsx("div", { className: "tollerud-cmd__empty", children: emptyMessage }),
632
+ filteredGroups.map((group, gi) => {
633
+ const flatOffset = filteredGroups.slice(0, gi).reduce((acc, g) => acc + g.items.length, 0);
634
+ return /* @__PURE__ */ jsxs("div", { className: "tollerud-cmd__group", children: [
635
+ /* @__PURE__ */ jsx("div", { className: "tollerud-cmd__group-label", children: group.label }),
636
+ group.items.map((item, ii) => {
637
+ const flatIndex = flatOffset + ii;
638
+ return /* @__PURE__ */ jsx(
639
+ ActionRow,
640
+ {
641
+ action: item,
642
+ highlighted: selectedIndex === flatIndex,
643
+ onClick: () => {
644
+ item.onSelect?.();
645
+ onAction?.(item);
646
+ onOpenChange(false);
647
+ },
648
+ onMouseEnter: () => setSelectedIndex(flatIndex)
649
+ },
650
+ item.id
651
+ );
652
+ })
653
+ ] }, group.label);
654
+ })
655
+ ] }),
656
+ /* @__PURE__ */ jsxs("div", { className: "tollerud-cmd__footer", children: [
657
+ /* @__PURE__ */ jsxs("span", { className: "tollerud-cmd__hint", children: [
658
+ /* @__PURE__ */ jsx("span", { className: "tollerud-kbd tollerud-kbd--sm", children: /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: "\u2191" }) }),
659
+ /* @__PURE__ */ jsx("span", { className: "tollerud-kbd tollerud-kbd--sm", children: /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: "\u2193" }) }),
660
+ /* @__PURE__ */ jsx("span", { className: "tollerud-cmd__hint-text", children: "navigate" })
661
+ ] }),
662
+ /* @__PURE__ */ jsxs("span", { className: "tollerud-cmd__hint", children: [
663
+ /* @__PURE__ */ jsx("span", { className: "tollerud-kbd tollerud-kbd--sm", children: /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: "\u21B5" }) }),
664
+ /* @__PURE__ */ jsx("span", { className: "tollerud-cmd__hint-text", children: "select" })
665
+ ] }),
666
+ /* @__PURE__ */ jsxs("span", { className: "tollerud-cmd__hint", children: [
667
+ /* @__PURE__ */ jsx("span", { className: "tollerud-kbd tollerud-kbd--sm", children: /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: "Esc" }) }),
668
+ /* @__PURE__ */ jsx("span", { className: "tollerud-cmd__hint-text", children: "close" })
669
+ ] })
670
+ ] })
671
+ ]
672
+ }
673
+ )
674
+ ] });
675
+ }
676
+ );
677
+ CommandMenu.displayName = "CommandMenu";
678
+ var ServiceHealthCard = forwardRef(
679
+ ({ className, service, status = "online", uptime, responseTime, version, loading, ...props }, ref) => {
680
+ return /* @__PURE__ */ jsxs(
681
+ "div",
682
+ {
683
+ ref,
684
+ className: cn(
685
+ "rounded-lg border bg-tollerud-surface-raised p-4",
686
+ "transition-[border-color] duration-[150ms]",
687
+ status === "offline" && "border-tollerud-error/40",
688
+ status === "warning" && "border-tollerud-yellow/30",
689
+ status === "online" && "border-tollerud-border hover:border-tollerud-noir-500",
690
+ status === "idle" && "border-tollerud-border opacity-60",
691
+ loading && "animate-pulse",
692
+ className
693
+ ),
694
+ ...props,
695
+ children: [
696
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
697
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground truncate", children: service }),
698
+ /* @__PURE__ */ jsx(StatusDot, { status })
699
+ ] }),
700
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-xs text-tollerud-text-muted", children: [
701
+ uptime && /* @__PURE__ */ jsxs("span", { children: [
702
+ "Uptime: ",
703
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: uptime })
704
+ ] }),
705
+ responseTime && /* @__PURE__ */ jsxs("span", { children: [
706
+ "Response: ",
707
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: responseTime })
708
+ ] }),
709
+ version && /* @__PURE__ */ jsxs("span", { children: [
710
+ "Version: ",
711
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: version })
712
+ ] })
713
+ ] })
714
+ ]
715
+ }
716
+ );
717
+ }
718
+ );
719
+ ServiceHealthCard.displayName = "ServiceHealthCard";
720
+ var HostCard = forwardRef(
721
+ ({ className, hostname, ip, status = "online", cpu, memory, disk, uptime, containers, loading, ...props }, ref) => {
722
+ return /* @__PURE__ */ jsxs(
723
+ "div",
724
+ {
725
+ ref,
726
+ className: cn(
727
+ "rounded-lg border bg-tollerud-surface-raised p-4",
728
+ "transition-[border-color] duration-[150ms]",
729
+ status === "offline" && "border-tollerud-error/40",
730
+ status === "warning" && "border-tollerud-yellow/30",
731
+ status === "online" && "border-tollerud-border hover:border-tollerud-noir-500",
732
+ loading && "animate-pulse",
733
+ className
734
+ ),
735
+ ...props,
736
+ children: [
737
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
738
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
739
+ /* @__PURE__ */ jsx(StatusDot, { status }),
740
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-sm text-tollerud-foreground truncate", children: hostname })
741
+ ] }),
742
+ containers !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-tollerud-text-muted whitespace-nowrap ml-2", children: [
743
+ containers,
744
+ " container",
745
+ containers !== 1 ? "s" : ""
746
+ ] })
747
+ ] }),
748
+ ip && /* @__PURE__ */ jsx("div", { className: "text-xs text-tollerud-text-muted mb-2 font-mono", children: ip }),
749
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-xs text-tollerud-text-muted", children: [
750
+ cpu && /* @__PURE__ */ jsxs("span", { children: [
751
+ "CPU: ",
752
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: cpu })
753
+ ] }),
754
+ memory && /* @__PURE__ */ jsxs("span", { children: [
755
+ "RAM: ",
756
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: memory })
757
+ ] }),
758
+ disk && /* @__PURE__ */ jsxs("span", { children: [
759
+ "Disk: ",
760
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: disk })
761
+ ] }),
762
+ uptime && /* @__PURE__ */ jsxs("span", { children: [
763
+ "Up: ",
764
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary", children: uptime })
765
+ ] })
766
+ ] })
767
+ ]
768
+ }
769
+ );
770
+ }
771
+ );
772
+ HostCard.displayName = "HostCard";
773
+ var DockerStackCard = forwardRef(
774
+ ({ className, name, services, composePath, loading, ...props }, ref) => {
775
+ const onlineCount = services.filter((s) => s.status === "online").length;
776
+ const degraded = services.some((s) => s.status === "offline" || s.status === "warning");
777
+ const status = services.every((s) => s.status === "online") ? "online" : degraded ? "warning" : "offline";
778
+ return /* @__PURE__ */ jsxs(
779
+ "div",
780
+ {
781
+ ref,
782
+ className: cn(
783
+ "rounded-lg border bg-tollerud-surface-raised p-4",
784
+ "transition-[border-color] duration-[150ms]",
785
+ status === "offline" && "border-tollerud-error/40",
786
+ status === "warning" && "border-tollerud-yellow/30",
787
+ status === "online" && "border-tollerud-border hover:border-tollerud-noir-500",
788
+ loading && "animate-pulse",
789
+ className
790
+ ),
791
+ ...props,
792
+ children: [
793
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
794
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
795
+ /* @__PURE__ */ jsx(StatusDot, { status }),
796
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-sm text-tollerud-foreground truncate", children: name })
797
+ ] }),
798
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-tollerud-text-muted whitespace-nowrap ml-2", children: [
799
+ onlineCount,
800
+ "/",
801
+ services.length,
802
+ " healthy"
803
+ ] })
804
+ ] }),
805
+ composePath && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-tollerud-text-muted font-mono mb-2 truncate", children: composePath }),
806
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: services.map((svc) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
807
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary truncate", children: svc.name }),
808
+ /* @__PURE__ */ jsx(StatusDot, { status: svc.status })
809
+ ] }, svc.name)) })
810
+ ]
811
+ }
812
+ );
813
+ }
814
+ );
815
+ DockerStackCard.displayName = "DockerStackCard";
816
+ var severityStyles = {
817
+ critical: { border: "border-tollerud-error/50", dot: "bg-tollerud-error shadow-[0_0_8px_rgba(239,68,68,0.6)]", label: "text-tollerud-error" },
818
+ high: { border: "border-tollerud-yellow/50", dot: "bg-tollerud-yellow shadow-[0_0_8px_rgba(232,213,0,0.5)]", label: "text-tollerud-yellow" },
819
+ medium: { border: "border-tollerud-amber/40", dot: "bg-tollerud-amber", label: "text-tollerud-amber" },
820
+ low: { border: "border-tollerud-noir-500", dot: "bg-tollerud-noir-400", label: "text-tollerud-text-muted" },
821
+ info: { border: "border-tollerud-info/30", dot: "bg-tollerud-info", label: "text-tollerud-info" }
822
+ };
823
+ var IncidentCard = forwardRef(
824
+ ({ className, title, severity, timestamp, description, service, acknowledged, loading, ...props }, ref) => {
825
+ const style = severityStyles[severity];
826
+ return /* @__PURE__ */ jsx(
827
+ "div",
828
+ {
829
+ ref,
830
+ className: cn(
831
+ "rounded-lg border bg-tollerud-surface-raised p-4",
832
+ "transition-[border-color] duration-[150ms]",
833
+ style.border,
834
+ acknowledged && "opacity-50",
835
+ loading && "animate-pulse",
836
+ className
837
+ ),
838
+ ...props,
839
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
840
+ /* @__PURE__ */ jsx("span", { className: cn("w-2 h-2 rounded-full mt-1.5 flex-shrink-0", style.dot) }),
841
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
842
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
843
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground truncate", children: title }),
844
+ /* @__PURE__ */ jsx("span", { className: cn("text-[11px] font-medium uppercase whitespace-nowrap", style.label), children: severity })
845
+ ] }),
846
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5 text-xs text-tollerud-text-muted", children: [
847
+ /* @__PURE__ */ jsx("span", { children: timestamp }),
848
+ service && /* @__PURE__ */ jsxs(Fragment, { children: [
849
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-noir-500", children: "\xB7" }),
850
+ /* @__PURE__ */ jsx("span", { children: service })
851
+ ] })
852
+ ] }),
853
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-tollerud-text-secondary leading-relaxed", children: description })
854
+ ] })
855
+ ] })
856
+ }
857
+ );
858
+ }
859
+ );
860
+ IncidentCard.displayName = "IncidentCard";
861
+ var stateStyles = {
862
+ pending: "border-tollerud-yellow/30 hover:border-tollerud-yellow/50",
863
+ approved: "border-tollerud-success/40 opacity-70",
864
+ rejected: "border-tollerud-error/40 opacity-70"
865
+ };
866
+ var stateLabels = {
867
+ pending: { text: "Awaiting approval", cls: "text-tollerud-yellow" },
868
+ approved: { text: "Approved", cls: "text-tollerud-success" },
869
+ rejected: { text: "Rejected", cls: "text-tollerud-error" }
870
+ };
871
+ var ApprovalCard = forwardRef(
872
+ ({ className, action, description, source, state = "pending", timestamp, onApprove, onReject, disabled, loading, ...props }, ref) => {
873
+ const label = stateLabels[state];
874
+ return /* @__PURE__ */ jsxs(
875
+ "div",
876
+ {
877
+ ref,
878
+ className: cn(
879
+ "rounded-lg border bg-tollerud-surface-raised p-4",
880
+ "transition-all duration-[150ms]",
881
+ stateStyles[state],
882
+ loading && "animate-pulse",
883
+ className
884
+ ),
885
+ ...props,
886
+ children: [
887
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [
888
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground truncate", children: action }),
889
+ /* @__PURE__ */ jsx("span", { className: cn("text-[11px] font-medium whitespace-nowrap ml-2", label.cls), children: label.text })
890
+ ] }),
891
+ description && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-text-secondary mb-1.5 leading-relaxed", children: description }),
892
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-0.5 text-[11px] text-tollerud-text-muted mb-3", children: [
893
+ source && /* @__PURE__ */ jsx("span", { className: "font-mono", children: source }),
894
+ timestamp && /* @__PURE__ */ jsx("span", { children: timestamp })
895
+ ] }),
896
+ state === "pending" && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
897
+ /* @__PURE__ */ jsx(
898
+ "button",
899
+ {
900
+ type: "button",
901
+ disabled,
902
+ onClick: onApprove,
903
+ className: cn(
904
+ "flex-1 rounded-md px-3 py-1.5 text-xs font-semibold transition-all",
905
+ "bg-tollerud-success text-tollerud-text-inverse",
906
+ "hover:brightness-110 disabled:opacity-40 disabled:cursor-not-allowed"
907
+ ),
908
+ children: "Approve"
909
+ }
910
+ ),
911
+ /* @__PURE__ */ jsx(
912
+ "button",
913
+ {
914
+ type: "button",
915
+ disabled,
916
+ onClick: onReject,
917
+ className: cn(
918
+ "flex-1 rounded-md px-3 py-1.5 text-xs font-semibold transition-all",
919
+ "bg-tollerud-error text-white",
920
+ "hover:brightness-110 disabled:opacity-40 disabled:cursor-not-allowed"
921
+ ),
922
+ children: "Reject"
923
+ }
924
+ )
925
+ ] })
926
+ ]
927
+ }
928
+ );
929
+ }
930
+ );
931
+ ApprovalCard.displayName = "ApprovalCard";
932
+ var BackupStatusPanel = forwardRef(
933
+ ({ className, jobs, totalSize, lastFullBackup, loading, ...props }, ref) => {
934
+ const failed = jobs.filter((j) => j.status === "offline").length;
935
+ return /* @__PURE__ */ jsxs(
936
+ "div",
937
+ {
938
+ ref,
939
+ className: cn(
940
+ "rounded-lg border border-tollerud-border bg-tollerud-surface-raised",
941
+ loading && "animate-pulse",
942
+ className
943
+ ),
944
+ ...props,
945
+ children: [
946
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-tollerud-border", children: [
947
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground", children: "Backups" }),
948
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-[11px] text-tollerud-text-muted", children: [
949
+ totalSize && /* @__PURE__ */ jsx("span", { children: totalSize }),
950
+ lastFullBackup && /* @__PURE__ */ jsxs("span", { children: [
951
+ "Last full: ",
952
+ lastFullBackup
953
+ ] })
954
+ ] })
955
+ ] }),
956
+ /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
957
+ jobs.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-6 text-xs text-center text-tollerud-text-muted", children: "No backup jobs configured" }),
958
+ jobs.map((job) => /* @__PURE__ */ jsxs(
959
+ "div",
960
+ {
961
+ className: "flex items-center justify-between px-3 py-2 rounded-md hover:bg-tollerud-noir-800/50 transition-colors",
962
+ children: [
963
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
964
+ /* @__PURE__ */ jsx(StatusDot, { status: job.status }),
965
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-tollerud-foreground truncate", children: job.name })
966
+ ] }),
967
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-[11px] text-tollerud-text-muted flex-shrink-0 ml-2", children: [
968
+ job.size && /* @__PURE__ */ jsx("span", { children: job.size }),
969
+ job.target && /* @__PURE__ */ jsx("span", { className: "hidden sm:inline font-mono", children: job.target })
970
+ ] })
971
+ ]
972
+ },
973
+ job.name
974
+ ))
975
+ ] }),
976
+ failed > 0 && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 border-t border-tollerud-border", children: /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-tollerud-error", children: [
977
+ failed,
978
+ " job",
979
+ failed !== 1 ? "s" : "",
980
+ " failed \u2014 check logs"
981
+ ] }) })
982
+ ]
983
+ }
984
+ );
985
+ }
986
+ );
987
+ BackupStatusPanel.displayName = "BackupStatusPanel";
988
+ var Timeline = forwardRef(
989
+ ({ className, items, active, loading, ...props }, ref) => {
990
+ return /* @__PURE__ */ jsx(
991
+ "div",
992
+ {
993
+ ref,
994
+ className: cn("tollerud-timeline", loading && "animate-pulse", className),
995
+ role: "list",
996
+ "aria-label": "Activity timeline",
997
+ ...props,
998
+ children: items.map((item, i) => {
999
+ const isLast = i === items.length - 1;
1000
+ return /* @__PURE__ */ jsxs("div", { className: "tollerud-timeline__item", role: "listitem", children: [
1001
+ /* @__PURE__ */ jsx("div", { className: "tollerud-timeline__marker", children: /* @__PURE__ */ jsxs("div", { className: "tollerud-timeline__dot-group", children: [
1002
+ item.icon ? /* @__PURE__ */ jsx("span", { className: "tollerud-timeline__icon", children: item.icon }) : /* @__PURE__ */ jsx(
1003
+ "span",
1004
+ {
1005
+ className: cn(
1006
+ "tollerud-timeline__dot",
1007
+ active && "tollerud-timeline__dot--active",
1008
+ item.status === "online" && "bg-tollerud-success shadow-[0_0_6px_rgba(34,197,94,0.5)]",
1009
+ item.status === "offline" && "bg-tollerud-error",
1010
+ item.status === "warning" && "bg-tollerud-yellow shadow-[0_0_6px_rgba(232,213,0,0.5)]",
1011
+ !item.status && "bg-tollerud-noir-500"
1012
+ )
1013
+ }
1014
+ ),
1015
+ !isLast && /* @__PURE__ */ jsx("div", { className: "tollerud-timeline__line" })
1016
+ ] }) }),
1017
+ /* @__PURE__ */ jsxs("div", { className: "tollerud-timeline__content", children: [
1018
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1019
+ /* @__PURE__ */ jsx("span", { className: "tollerud-timeline__title", children: item.title }),
1020
+ /* @__PURE__ */ jsx("span", { className: "tollerud-timeline__time", children: item.time })
1021
+ ] }),
1022
+ item.description && /* @__PURE__ */ jsx("p", { className: "tollerud-timeline__description", children: item.description }),
1023
+ item.meta && item.meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 mt-1", children: item.meta.map((m) => /* @__PURE__ */ jsx("span", { className: "tollerud-timeline__meta", children: m }, m)) })
1024
+ ] })
1025
+ ] }, item.id);
1026
+ })
1027
+ }
1028
+ );
1029
+ }
1030
+ );
1031
+ Timeline.displayName = "Timeline";
1032
+ var AlertInbox = forwardRef(
1033
+ ({ className, alerts, filterSeverity = "", onAcknowledge, loading, emptyMessage = "All clear \u2014 no incidents", ...props }, ref) => {
1034
+ const filtered = filterSeverity ? alerts.filter((a) => a.severity === filterSeverity) : alerts;
1035
+ const counts = {
1036
+ total: alerts.length,
1037
+ unacknowledged: alerts.filter((a) => !a.acknowledged).length,
1038
+ critical: alerts.filter((a) => a.severity === "critical" && !a.acknowledged).length
1039
+ };
1040
+ return /* @__PURE__ */ jsxs(
1041
+ "div",
1042
+ {
1043
+ ref,
1044
+ className: cn(
1045
+ "rounded-lg border border-tollerud-border bg-tollerud-surface-raised overflow-hidden",
1046
+ loading && "animate-pulse",
1047
+ className
1048
+ ),
1049
+ ...props,
1050
+ children: [
1051
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-tollerud-border", children: [
1052
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1053
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground", children: "Alerts" }),
1054
+ counts.unacknowledged > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-[11px] text-tollerud-error font-medium", children: [
1055
+ /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-tollerud-error animate-pulse" }),
1056
+ counts.unacknowledged,
1057
+ " unread"
1058
+ ] }),
1059
+ counts.critical > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-tollerud-error font-bold", children: [
1060
+ counts.critical,
1061
+ " critical"
1062
+ ] })
1063
+ ] }),
1064
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-tollerud-text-muted", children: [
1065
+ filtered.length,
1066
+ " of ",
1067
+ alerts.length
1068
+ ] })
1069
+ ] }),
1070
+ /* @__PURE__ */ jsxs("div", { className: "divide-y divide-tollerud-border/50 max-h-[480px] overflow-y-auto", children: [
1071
+ filtered.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-xs text-center text-tollerud-text-muted", children: emptyMessage }),
1072
+ filtered.map((alert) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
1073
+ /* @__PURE__ */ jsx(
1074
+ IncidentCard,
1075
+ {
1076
+ title: alert.title,
1077
+ severity: alert.severity,
1078
+ timestamp: alert.timestamp,
1079
+ description: alert.description,
1080
+ service: alert.service,
1081
+ acknowledged: alert.acknowledged,
1082
+ className: "border-0 rounded-none bg-transparent hover:bg-tollerud-noir-800/30"
1083
+ }
1084
+ ),
1085
+ alert.acknowledged && /* @__PURE__ */ jsx("span", { className: "absolute bottom-2 right-3 text-[10px] font-medium text-tollerud-text-muted/50", children: "Acknowledged" }),
1086
+ !alert.acknowledged && onAcknowledge && /* @__PURE__ */ jsx(
1087
+ "button",
1088
+ {
1089
+ type: "button",
1090
+ onClick: () => onAcknowledge(alert.id),
1091
+ className: cn(
1092
+ "absolute bottom-2 right-3 text-[10px] font-medium px-2 py-0.5 rounded",
1093
+ "text-tollerud-yellow/70 hover:text-tollerud-yellow hover:bg-tollerud-yellow/10",
1094
+ "opacity-0 group-hover:opacity-100 transition-opacity duration-150"
1095
+ ),
1096
+ children: "Acknowledge"
1097
+ }
1098
+ )
1099
+ ] }, alert.id))
1100
+ ] })
1101
+ ]
1102
+ }
1103
+ );
1104
+ }
1105
+ );
1106
+ AlertInbox.displayName = "AlertInbox";
1107
+ var stepIcon = {
1108
+ pending: "\u25CB",
1109
+ running: "\u25C9",
1110
+ success: "\u25CF",
1111
+ failed: "\u2715",
1112
+ skipped: "\u2014"
1113
+ };
1114
+ var stepStyles = {
1115
+ pending: "text-tollerud-noir-400",
1116
+ running: "text-tollerud-yellow animate-pulse",
1117
+ success: "text-tollerud-success",
1118
+ failed: "text-tollerud-error",
1119
+ skipped: "text-tollerud-noir-500"
1120
+ };
1121
+ var RollbackPlan = forwardRef(
1122
+ ({ className, name, steps, executing, loading, ...props }, ref) => {
1123
+ const statusSummary = {
1124
+ pending: steps.filter((s) => s.status === "pending").length,
1125
+ running: steps.filter((s) => s.status === "running").length,
1126
+ success: steps.filter((s) => s.status === "success").length,
1127
+ failed: steps.filter((s) => s.status === "failed").length,
1128
+ skipped: steps.filter((s) => s.status === "skipped").length
1129
+ };
1130
+ return /* @__PURE__ */ jsxs(
1131
+ "div",
1132
+ {
1133
+ ref,
1134
+ className: cn(
1135
+ "rounded-lg border border-tollerud-border bg-tollerud-surface-raised",
1136
+ loading && "animate-pulse",
1137
+ className
1138
+ ),
1139
+ ...props,
1140
+ children: [
1141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-tollerud-border", children: [
1142
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1143
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground", children: name }),
1144
+ executing && /* @__PURE__ */ jsx("span", { className: "text-[11px] text-tollerud-yellow font-medium animate-pulse", children: "Executing\u2026" })
1145
+ ] }),
1146
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[11px] text-tollerud-text-muted", children: [
1147
+ statusSummary.success > 0 && /* @__PURE__ */ jsxs("span", { className: "text-tollerud-success", children: [
1148
+ statusSummary.success,
1149
+ " done"
1150
+ ] }),
1151
+ statusSummary.failed > 0 && /* @__PURE__ */ jsxs("span", { className: "text-tollerud-error", children: [
1152
+ statusSummary.failed,
1153
+ " failed"
1154
+ ] }),
1155
+ statusSummary.running > 0 && /* @__PURE__ */ jsxs("span", { className: "text-tollerud-yellow", children: [
1156
+ statusSummary.running,
1157
+ " running"
1158
+ ] })
1159
+ ] })
1160
+ ] }),
1161
+ /* @__PURE__ */ jsx("div", { className: "p-1", children: steps.map((step) => /* @__PURE__ */ jsxs(
1162
+ "div",
1163
+ {
1164
+ className: cn(
1165
+ "flex items-start gap-3 px-3 py-2 rounded-md",
1166
+ "transition-colors duration-[150ms]",
1167
+ step.status === "failed" && "bg-tollerud-error/5",
1168
+ step.status === "running" && "bg-tollerud-yellow/5"
1169
+ ),
1170
+ children: [
1171
+ /* @__PURE__ */ jsx("span", { className: cn("font-mono text-sm mt-0.5 flex-shrink-0", stepStyles[step.status]), children: stepIcon[step.status] }),
1172
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1173
+ /* @__PURE__ */ jsx("span", { className: cn(
1174
+ "text-xs font-medium",
1175
+ step.status === "success" ? "text-tollerud-success" : step.status === "failed" ? "text-tollerud-error" : step.status === "skipped" ? "text-tollerud-noir-500" : "text-tollerud-foreground"
1176
+ ), children: step.label }),
1177
+ step.description && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-tollerud-text-muted mt-0.5", children: step.description })
1178
+ ] }),
1179
+ /* @__PURE__ */ jsx("span", { className: cn(
1180
+ "text-[10px] uppercase font-semibold flex-shrink-0",
1181
+ stepStyles[step.status]
1182
+ ), children: step.status })
1183
+ ]
1184
+ },
1185
+ step.id
1186
+ )) })
1187
+ ]
1188
+ }
1189
+ );
1190
+ }
1191
+ );
1192
+ RollbackPlan.displayName = "RollbackPlan";
1193
+ var diffStyles = {
1194
+ add: "bg-tollerud-success/[0.06] text-tollerud-success hover:bg-tollerud-success/[0.10]",
1195
+ remove: "bg-tollerud-error/[0.06] text-tollerud-error hover:bg-tollerud-error/[0.10]",
1196
+ context: "text-tollerud-noir-300"
1197
+ };
1198
+ var diffPrefix = {
1199
+ add: "+",
1200
+ remove: "-",
1201
+ context: " "
1202
+ };
1203
+ var ActionDiff = forwardRef(
1204
+ ({ className, lines, label, view = "unified", loading, ...props }, ref) => {
1205
+ const [showContext, setShowContext] = useState(true);
1206
+ const stats = useMemo(() => {
1207
+ const adds = lines.filter((l) => l.type === "add").length;
1208
+ const rems = lines.filter((l) => l.type === "remove").length;
1209
+ return { adds, rems };
1210
+ }, [lines]);
1211
+ const displayLines = showContext ? lines : lines.filter((l) => l.type !== "context");
1212
+ const hasContext = lines.some((l) => l.type === "context");
1213
+ return /* @__PURE__ */ jsxs(
1214
+ "div",
1215
+ {
1216
+ ref,
1217
+ className: cn(
1218
+ "rounded-lg border border-tollerud-border bg-[var(--color-tollerud-surface-raised)] overflow-hidden",
1219
+ loading && "animate-pulse",
1220
+ className
1221
+ ),
1222
+ ...props,
1223
+ children: [
1224
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 border-b border-tollerud-border bg-tollerud-noir-900", children: [
1225
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 min-w-0", children: label && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-tollerud-foreground font-mono truncate", children: label }) }),
1226
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-[11px]", children: [
1227
+ /* @__PURE__ */ jsxs("span", { className: "text-tollerud-success", children: [
1228
+ "+",
1229
+ stats.adds
1230
+ ] }),
1231
+ /* @__PURE__ */ jsxs("span", { className: "text-tollerud-error", children: [
1232
+ "-",
1233
+ stats.rems
1234
+ ] }),
1235
+ hasContext && /* @__PURE__ */ jsx(
1236
+ "button",
1237
+ {
1238
+ type: "button",
1239
+ onClick: () => setShowContext(!showContext),
1240
+ className: "text-tollerud-text-muted hover:text-tollerud-foreground transition-colors",
1241
+ children: showContext ? "Hide context" : "Show context"
1242
+ }
1243
+ )
1244
+ ] })
1245
+ ] }),
1246
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto font-mono text-xs leading-relaxed", children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse", children: /* @__PURE__ */ jsx("tbody", { children: displayLines.map((line, i) => /* @__PURE__ */ jsxs(
1247
+ "tr",
1248
+ {
1249
+ className: cn(
1250
+ "transition-colors duration-[100ms]",
1251
+ diffStyles[line.type]
1252
+ ),
1253
+ children: [
1254
+ /* @__PURE__ */ jsx("td", { className: cn(
1255
+ "w-10 text-right px-2 py-px select-none",
1256
+ "text-tollerud-noir-500 border-r border-tollerud-border/30",
1257
+ "text-[10px] align-top"
1258
+ ), children: line.oldLine ?? "" }),
1259
+ /* @__PURE__ */ jsx("td", { className: cn(
1260
+ "w-10 text-right px-2 py-px select-none",
1261
+ "text-tollerud-noir-500 border-r border-tollerud-border/30",
1262
+ "text-[10px] align-top"
1263
+ ), children: line.newLine ?? "" }),
1264
+ /* @__PURE__ */ jsxs("td", { className: "px-2 py-px whitespace-pre-wrap", children: [
1265
+ /* @__PURE__ */ jsx("span", { className: "select-none opacity-50 mr-2", children: diffPrefix[line.type] }),
1266
+ line.text
1267
+ ] })
1268
+ ]
1269
+ },
1270
+ i
1271
+ )) }) }) })
1272
+ ]
1273
+ }
1274
+ );
1275
+ }
1276
+ );
1277
+ ActionDiff.displayName = "ActionDiff";
1278
+ var levelStyles = {
1279
+ debug: "text-tollerud-noir-400",
1280
+ trace: "text-tollerud-noir-300",
1281
+ info: "text-tollerud-foreground",
1282
+ warn: "text-tollerud-amber",
1283
+ error: "text-tollerud-error"
1284
+ };
1285
+ var levelLabels = {
1286
+ debug: "DBG",
1287
+ trace: "TRC",
1288
+ info: "INF",
1289
+ warn: "WRN",
1290
+ error: "ERR"
1291
+ };
1292
+ var LogViewer = forwardRef(
1293
+ ({ className, lines, maxLines = 5e3, showLineNumbers = true, showTimestamps = true, follow = true, searchable, height = "400px", loading, ...props }, ref) => {
1294
+ const [search, setSearch] = useState("");
1295
+ const [isFollowing, setIsFollowing] = useState(follow);
1296
+ const scrollRef = useRef(null);
1297
+ const displayedLines = useMemo(() => {
1298
+ return lines.length > maxLines ? lines.slice(-maxLines) : lines;
1299
+ }, [lines, maxLines]);
1300
+ const filteredLines = useMemo(() => {
1301
+ if (!search.trim()) return displayedLines;
1302
+ return displayedLines.filter(
1303
+ (l) => l.text.toLowerCase().includes(search.toLowerCase()) || l.source?.toLowerCase().includes(search.toLowerCase())
1304
+ );
1305
+ }, [displayedLines, search]);
1306
+ useEffect(() => {
1307
+ if (isFollowing && scrollRef.current) {
1308
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1309
+ }
1310
+ }, [filteredLines.length, isFollowing]);
1311
+ const handleScroll = (e) => {
1312
+ const el = e.currentTarget;
1313
+ const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;
1314
+ setIsFollowing(atBottom);
1315
+ };
1316
+ const filteredFrom = displayedLines.indexOf(filteredLines[0]);
1317
+ return /* @__PURE__ */ jsxs(
1318
+ "div",
1319
+ {
1320
+ ref,
1321
+ className: cn(
1322
+ "rounded-lg border border-tollerud-border bg-[var(--color-tollerud-surface-raised)] overflow-hidden tollerud-log-viewer",
1323
+ loading && "animate-pulse",
1324
+ className
1325
+ ),
1326
+ ...props,
1327
+ children: [
1328
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-tollerud-border bg-tollerud-noir-900", children: [
1329
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-tollerud-text-muted", children: [
1330
+ isFollowing ? /* @__PURE__ */ jsx("span", { className: "text-tollerud-yellow font-medium", children: "\u25CF Live" }) : /* @__PURE__ */ jsx("span", { className: "text-tollerud-noir-400", children: "\u25CF Paused" }),
1331
+ /* @__PURE__ */ jsxs("span", { children: [
1332
+ filteredLines.length,
1333
+ " lines"
1334
+ ] }),
1335
+ lines.length > maxLines && /* @__PURE__ */ jsxs("span", { className: "text-tollerud-amber", children: [
1336
+ "(showing last ",
1337
+ maxLines,
1338
+ ")"
1339
+ ] })
1340
+ ] }),
1341
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: searchable && /* @__PURE__ */ jsx(
1342
+ "input",
1343
+ {
1344
+ type: "text",
1345
+ value: search,
1346
+ onChange: (e) => setSearch(e.target.value),
1347
+ placeholder: "Search logs\u2026",
1348
+ className: cn(
1349
+ "w-40 text-[11px] px-2 py-1 rounded bg-tollerud-noir-800",
1350
+ "border border-tollerud-noir-600 text-tollerud-foreground",
1351
+ "placeholder:text-tollerud-noir-400 outline-none",
1352
+ "focus:border-tollerud-yellow/50 transition-colors"
1353
+ )
1354
+ }
1355
+ ) })
1356
+ ] }),
1357
+ /* @__PURE__ */ jsxs(
1358
+ "div",
1359
+ {
1360
+ ref: scrollRef,
1361
+ onScroll: handleScroll,
1362
+ className: "overflow-y-auto font-mono text-xs leading-relaxed p-3",
1363
+ style: { height },
1364
+ children: [
1365
+ filteredLines.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-tollerud-noir-400 text-xs", children: search ? "No matching log lines" : "No log output" }),
1366
+ filteredLines.map((line, i) => {
1367
+ const absLineNum = filteredFrom >= 0 ? filteredFrom + i + 1 : i + 1;
1368
+ const level = line.level ?? "info";
1369
+ return /* @__PURE__ */ jsxs(
1370
+ "div",
1371
+ {
1372
+ className: cn(
1373
+ "flex gap-3 hover:bg-[var(--color-tollerud-surface-hover)] px-1 py-px rounded-sm",
1374
+ levelStyles[level]
1375
+ ),
1376
+ children: [
1377
+ showLineNumbers && /* @__PURE__ */ jsx("span", { className: "text-tollerud-noir-500 text-right select-none w-8 flex-shrink-0", children: absLineNum }),
1378
+ showTimestamps && line.timestamp && /* @__PURE__ */ jsx("span", { className: "text-tollerud-noir-400 flex-shrink-0 select-none", children: line.timestamp }),
1379
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 w-7 text-center font-bold text-[10px] uppercase opacity-60", children: levelLabels[level] }),
1380
+ /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-all", children: line.text })
1381
+ ]
1382
+ },
1383
+ `${absLineNum}-${i}`
1384
+ );
1385
+ })
1386
+ ]
1387
+ }
1388
+ )
1389
+ ]
1390
+ }
1391
+ );
1392
+ }
1393
+ );
1394
+ LogViewer.displayName = "LogViewer";
1395
+ var Textarea = forwardRef(
1396
+ ({ className, label, error, id, ...props }, ref) => {
1397
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1398
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
1399
+ /* @__PURE__ */ jsx(
1400
+ "textarea",
1401
+ {
1402
+ ref,
1403
+ id,
1404
+ className: cn(
1405
+ "font-sans text-base px-3 py-2 rounded min-h-[80px] resize-y",
1406
+ "bg-tollerud-surface-raised border",
1407
+ "text-tollerud-text-primary",
1408
+ "placeholder:text-tollerud-text-muted",
1409
+ "transition-[border-color] duration-[150ms]",
1410
+ "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
1411
+ error ? "border-tollerud-error" : "border-tollerud-border",
1412
+ className
1413
+ ),
1414
+ ...props
1415
+ }
1416
+ ),
1417
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
1418
+ ] });
1419
+ }
1420
+ );
1421
+ Textarea.displayName = "Textarea";
1422
+ var Select = forwardRef(
1423
+ ({ className, label, error, placeholder, options = [], value, onChange, ...props }, ref) => {
1424
+ const [open, setOpen] = useState(false);
1425
+ const [highlightedIdx, setHighlightedIdx] = useState(0);
1426
+ const containerRef = useRef(null);
1427
+ const listRef = useRef(null);
1428
+ const selectedOption = options.find((o) => o.value === value);
1429
+ useEffect(() => {
1430
+ if (!open) return;
1431
+ const handleClick = (e) => {
1432
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
1433
+ setOpen(false);
1434
+ }
1435
+ };
1436
+ document.addEventListener("mousedown", handleClick);
1437
+ return () => document.removeEventListener("mousedown", handleClick);
1438
+ }, [open]);
1439
+ useEffect(() => {
1440
+ if (open) {
1441
+ const idx = value ? options.findIndex((o) => o.value === value) : -1;
1442
+ setHighlightedIdx(idx >= 0 ? idx : 0);
1443
+ }
1444
+ }, [open, options, value]);
1445
+ useEffect(() => {
1446
+ if (open && listRef.current) {
1447
+ const item = listRef.current.children[highlightedIdx];
1448
+ item?.scrollIntoView({ block: "nearest" });
1449
+ }
1450
+ }, [open, highlightedIdx]);
1451
+ const selectOption = useCallback(
1452
+ (opt) => {
1453
+ onChange?.(opt.value);
1454
+ setOpen(false);
1455
+ },
1456
+ [onChange]
1457
+ );
1458
+ const handleKeyDown = (e) => {
1459
+ if (!open) {
1460
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
1461
+ e.preventDefault();
1462
+ setOpen(true);
1463
+ }
1464
+ return;
1465
+ }
1466
+ switch (e.key) {
1467
+ case "Escape":
1468
+ e.preventDefault();
1469
+ setOpen(false);
1470
+ break;
1471
+ case "ArrowDown":
1472
+ e.preventDefault();
1473
+ setHighlightedIdx((prev) => Math.min(prev + 1, options.length - 1));
1474
+ break;
1475
+ case "ArrowUp":
1476
+ e.preventDefault();
1477
+ setHighlightedIdx((prev) => Math.max(prev - 1, 0));
1478
+ break;
1479
+ case "Enter":
1480
+ e.preventDefault();
1481
+ if (options[highlightedIdx]) {
1482
+ selectOption(options[highlightedIdx]);
1483
+ }
1484
+ break;
1485
+ }
1486
+ };
1487
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", ref, children: [
1488
+ label && /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-tollerud-text-muted", children: label }),
1489
+ /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
1490
+ /* @__PURE__ */ jsxs(
1491
+ "button",
1492
+ {
1493
+ type: "button",
1494
+ onClick: () => setOpen(!open),
1495
+ onKeyDown: handleKeyDown,
1496
+ "aria-haspopup": "listbox",
1497
+ "aria-expanded": open,
1498
+ className: cn(
1499
+ "font-sans text-sm w-full flex items-center justify-between px-3 py-2.5 rounded-lg",
1500
+ "bg-tollerud-surface-raised",
1501
+ "text-tollerud-text-primary text-left",
1502
+ "transition-all duration-150 ease-out cursor-pointer",
1503
+ error ? "border-tollerud-error/70 focus:border-tollerud-error focus:shadow-[0_0_0_1px_#EF4444]" : "border-tollerud-border focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
1504
+ "border hover:border-tollerud-noir-400",
1505
+ "focus:outline-none",
1506
+ className
1507
+ ),
1508
+ children: [
1509
+ /* @__PURE__ */ jsx("span", { className: cn(!selectedOption && "text-tollerud-text-muted"), children: selectedOption ? selectedOption.label : placeholder || "Select\u2026" }),
1510
+ /* @__PURE__ */ jsx(
1511
+ "svg",
1512
+ {
1513
+ className: cn(
1514
+ "h-4 w-4 text-tollerud-text-muted transition-transform duration-150 flex-shrink-0",
1515
+ open && "rotate-180"
1516
+ ),
1517
+ fill: "none",
1518
+ viewBox: "0 0 24 24",
1519
+ stroke: "currentColor",
1520
+ strokeWidth: 2,
1521
+ "aria-hidden": "true",
1522
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" })
1523
+ }
1524
+ )
1525
+ ]
1526
+ }
1527
+ ),
1528
+ open && /* @__PURE__ */ jsxs(
1529
+ "div",
1530
+ {
1531
+ ref: listRef,
1532
+ role: "listbox",
1533
+ className: cn(
1534
+ "absolute z-10 left-0 right-0 mt-1 py-1",
1535
+ "rounded-lg border border-tollerud-border bg-tollerud-surface-overlay",
1536
+ "shadow-[0_8px_24px_rgba(0,0,0,0.4)]",
1537
+ "max-h-60 overflow-y-auto"
1538
+ ),
1539
+ children: [
1540
+ options.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-tollerud-text-muted text-center", children: "No options" }),
1541
+ options.map((opt, idx) => /* @__PURE__ */ jsx(
1542
+ "button",
1543
+ {
1544
+ type: "button",
1545
+ role: "option",
1546
+ "aria-selected": opt.value === value,
1547
+ onClick: () => selectOption(opt),
1548
+ onMouseEnter: () => setHighlightedIdx(idx),
1549
+ className: cn(
1550
+ "w-full text-sm text-left px-3 py-2 transition-colors duration-75",
1551
+ "cursor-pointer",
1552
+ opt.value === value ? "text-tollerud-yellow" : "text-tollerud-text-primary",
1553
+ idx === highlightedIdx && !(opt.value === value) ? "bg-tollerud-noir-700" : "hover:bg-tollerud-noir-700/60",
1554
+ opt.value === value && highlightedIdx === idx && "bg-tollerud-noir-700"
1555
+ ),
1556
+ children: opt.label
1557
+ },
1558
+ opt.value
1559
+ ))
1560
+ ]
1561
+ }
1562
+ )
1563
+ ] }),
1564
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
1565
+ ] });
1566
+ }
1567
+ );
1568
+ Select.displayName = "Select";
1569
+ var Checkbox = forwardRef(
1570
+ ({ className, label, id: idProp, checked, ...props }, ref) => {
1571
+ const autoId = useId();
1572
+ const id = idProp ?? autoId;
1573
+ return /* @__PURE__ */ jsxs(
1574
+ "label",
1575
+ {
1576
+ htmlFor: id,
1577
+ className: cn(
1578
+ "inline-flex items-center gap-2 cursor-pointer select-none group",
1579
+ "text-sm text-tollerud-text-primary",
1580
+ props.disabled && "opacity-50 pointer-events-none",
1581
+ className
1582
+ ),
1583
+ children: [
1584
+ /* @__PURE__ */ jsxs("span", { className: "relative flex items-center justify-center", children: [
1585
+ /* @__PURE__ */ jsx(
1586
+ "input",
1587
+ {
1588
+ ref,
1589
+ id,
1590
+ type: "checkbox",
1591
+ checked,
1592
+ className: "peer sr-only",
1593
+ ...props
1594
+ }
1595
+ ),
1596
+ /* @__PURE__ */ jsx(
1597
+ "span",
1598
+ {
1599
+ className: cn(
1600
+ "h-4 w-4 rounded border transition-all duration-[150ms]",
1601
+ "flex items-center justify-center",
1602
+ "bg-tollerud-surface-raised border-tollerud-border",
1603
+ "peer-focus-visible:outline-2 peer-focus-visible:outline-tollerud-yellow",
1604
+ "peer-checked:bg-tollerud-yellow peer-checked:border-tollerud-yellow",
1605
+ "group-hover:border-tollerud-text-secondary"
1606
+ ),
1607
+ children: /* @__PURE__ */ jsx(
1608
+ "svg",
1609
+ {
1610
+ className: cn(
1611
+ "h-3 w-3 text-tollerud-noir-black transition-opacity duration-[150ms]",
1612
+ checked ? "opacity-100" : "opacity-0"
1613
+ ),
1614
+ viewBox: "0 0 12 12",
1615
+ fill: "none",
1616
+ "aria-hidden": "true",
1617
+ children: /* @__PURE__ */ jsx(
1618
+ "path",
1619
+ {
1620
+ d: "M2.5 6l2.5 2.5 4.5-5",
1621
+ stroke: "currentColor",
1622
+ strokeWidth: 2,
1623
+ strokeLinecap: "round",
1624
+ strokeLinejoin: "round"
1625
+ }
1626
+ )
1627
+ }
1628
+ )
1629
+ }
1630
+ )
1631
+ ] }),
1632
+ label && /* @__PURE__ */ jsx("span", { children: label })
1633
+ ]
1634
+ }
1635
+ );
1636
+ }
1637
+ );
1638
+ Checkbox.displayName = "Checkbox";
1639
+ var Switch = forwardRef(
1640
+ ({ className, label, id: idProp, checked, ...props }, ref) => {
1641
+ const autoId = useId();
1642
+ const id = idProp ?? autoId;
1643
+ return /* @__PURE__ */ jsxs(
1644
+ "label",
1645
+ {
1646
+ htmlFor: id,
1647
+ className: cn(
1648
+ "inline-flex items-center gap-2.5 cursor-pointer select-none group",
1649
+ "text-sm text-tollerud-text-primary",
1650
+ props.disabled && "opacity-40 pointer-events-none cursor-not-allowed",
1651
+ className
1652
+ ),
1653
+ children: [
1654
+ /* @__PURE__ */ jsxs(
1655
+ "span",
1656
+ {
1657
+ className: cn(
1658
+ "relative inline-flex items-center h-5 w-9 flex-shrink-0 rounded-full",
1659
+ "transition-colors duration-200 ease-out",
1660
+ checked ? "bg-tollerud-yellow" : "bg-tollerud-noir-600",
1661
+ "group-hover:bg-tollerud-noir-500",
1662
+ checked && "group-hover:bg-tollerud-yellow-bright",
1663
+ "peer-focus-visible:outline-2 peer-focus-visible:outline-tollerud-yellow peer-focus-visible:outline-offset-2"
1664
+ ),
1665
+ children: [
1666
+ /* @__PURE__ */ jsx(
1667
+ "input",
1668
+ {
1669
+ ref,
1670
+ id,
1671
+ type: "checkbox",
1672
+ role: "switch",
1673
+ checked,
1674
+ className: "peer absolute inset-0 opacity-0 w-full h-full cursor-pointer z-10",
1675
+ ...props
1676
+ }
1677
+ ),
1678
+ /* @__PURE__ */ jsx(
1679
+ "span",
1680
+ {
1681
+ className: cn(
1682
+ "block h-3.5 w-3.5 rounded-full shadow-sm",
1683
+ "transition-all duration-200 ease-out",
1684
+ checked ? "translate-x-[18px] bg-tollerud-black" : "translate-x-[3px] bg-tollerud-white"
1685
+ )
1686
+ }
1687
+ )
1688
+ ]
1689
+ }
1690
+ ),
1691
+ label && /* @__PURE__ */ jsx("span", { children: label })
1692
+ ]
1693
+ }
1694
+ );
1695
+ }
1696
+ );
1697
+ Switch.displayName = "Switch";
1698
+ var RadioGroup = forwardRef(
1699
+ ({ label, error, children, className }, ref) => {
1700
+ useId();
1701
+ return /* @__PURE__ */ jsxs("fieldset", { ref, className: cn("flex flex-col gap-1", className), children: [
1702
+ label && /* @__PURE__ */ jsx("legend", { className: "text-xs font-medium text-tollerud-text-muted mb-1", children: label }),
1703
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children }),
1704
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
1705
+ ] });
1706
+ }
1707
+ );
1708
+ RadioGroup.displayName = "RadioGroup";
1709
+ var Radio = forwardRef(
1710
+ ({ className, label, id: idProp, ...props }, ref) => {
1711
+ const autoId = useId();
1712
+ const id = idProp ?? autoId;
1713
+ return /* @__PURE__ */ jsxs(
1714
+ "label",
1715
+ {
1716
+ htmlFor: id,
1717
+ className: cn(
1718
+ "inline-flex items-center gap-2 cursor-pointer select-none group",
1719
+ "text-sm text-tollerud-text-primary",
1720
+ props.disabled && "opacity-50 pointer-events-none",
1721
+ className
1722
+ ),
1723
+ children: [
1724
+ /* @__PURE__ */ jsxs("span", { className: "relative flex items-center justify-center", children: [
1725
+ /* @__PURE__ */ jsx(
1726
+ "input",
1727
+ {
1728
+ ref,
1729
+ id,
1730
+ type: "radio",
1731
+ className: "peer sr-only",
1732
+ ...props
1733
+ }
1734
+ ),
1735
+ /* @__PURE__ */ jsx(
1736
+ "span",
1737
+ {
1738
+ className: cn(
1739
+ "h-4 w-4 rounded-full border transition-all duration-[150ms]",
1740
+ "bg-tollerud-surface-raised border-tollerud-border",
1741
+ "peer-focus-visible:outline-2 peer-focus-visible:outline-tollerud-yellow",
1742
+ "peer-checked:border-tollerud-yellow",
1743
+ "group-hover:border-tollerud-text-secondary",
1744
+ "flex items-center justify-center"
1745
+ ),
1746
+ children: /* @__PURE__ */ jsx(
1747
+ "span",
1748
+ {
1749
+ className: cn(
1750
+ "h-2 w-2 rounded-full bg-tollerud-yellow transition-opacity duration-[150ms]",
1751
+ props.checked ? "opacity-100" : "opacity-0"
1752
+ )
1753
+ }
1754
+ )
1755
+ }
1756
+ )
1757
+ ] }),
1758
+ label && /* @__PURE__ */ jsx("span", { children: label })
1759
+ ]
1760
+ }
1761
+ );
1762
+ }
1763
+ );
1764
+ Radio.displayName = "Radio";
1765
+ var defaultLabels = {
1766
+ tollerudProject: "A Tollerud Project",
1767
+ allRightsReserved: "All rights reserved."
1768
+ };
1769
+ var MONOGRAM_PATH = "M82.4839273,140.272626 L95.1738252,140.272626 L95.1738252,143 L34.8114657,143 L34.8114657,140.272626 L47.5013636,140.272626 L47.5013636,28.2924381 C40.1277806,26.4177752 32.9252955,25.2241422 26.4088393,25.2241422 C12.1757856,25.2241422 4.11617359,34.5982703 4.11617359,39.8821508 C4.11617359,40.9049161 4.63028596,41.5867596 5.65932936,41.5867596 C7.20248513,41.5867596 7.37440169,40.3931266 8.06043062,38.8593855 C10.4615319,33.575505 15.6059302,31.5307881 20.4073141,31.5307881 C29.152955,31.5307881 35.1552988,38.5184637 35.1552988,47.2107482 C35.1552988,56.2447681 28.8107592,62.8907084 18.0070315,62.8907084 C7.5454996,62.891522 0,53.6882617 0,43.8023442 C0,30.8497582 11.3178401,21.986606 26.5799372,21.986606 C51.1026062,21.986606 84.1989996,39.2011209 104.948509,39.2011209 C118.495534,39.2011209 126.384048,31.7016558 126.384048,19.4300996 C126.384048,10.3968933 118.667451,4.60203698 115.580321,4.60203698 C114.552096,4.60203698 113.69415,5.1130128 113.69415,6.13577809 C113.69415,7.49946515 114.552096,7.6695192 115.409223,8.01044097 C115.752237,8.18049502 122.268693,10.5669474 122.268693,19.2592319 C122.268693,28.2924381 115.238125,34.0872945 107.177694,34.0872945 C97.7460244,34.0872945 91.0584702,26.4177752 91.0584702,17.8955448 C91.0584702,6.64675391 99.9760277,0 109.749893,0 C122.268693,0 129.642276,9.88510384 129.642276,19.6001536 C129.642276,34.2581622 119.181563,42.4386572 104.947691,42.4386572 C98.0890388,42.4386572 90.5443579,40.9049161 82.4839273,38.6901451 L82.4839273,140.272626 Z";
1770
+ function Footer({
1771
+ labels,
1772
+ layout = "responsive",
1773
+ className,
1774
+ style,
1775
+ unstyled = false,
1776
+ accent = false,
1777
+ classNameInner,
1778
+ classNameLogo,
1779
+ classNameText,
1780
+ classNameLink
1781
+ }) {
1782
+ const t = { ...defaultLabels, ...labels };
1783
+ const attribution = t.attribution?.trim();
1784
+ const footerSurface = unstyled ? "" : accent ? "border-t border-tollerud-yellow/20 bg-tollerud-yellow/5" : "border-t border-tollerud-border bg-tollerud-noir-900/80";
1785
+ const innerLayoutClasses = layout === "row" ? "max-w-7xl mx-auto px-8 flex flex-row items-center justify-between gap-4" : "max-w-7xl mx-auto px-8 flex flex-col md:flex-row items-center justify-center md:justify-between gap-4 md:gap-0";
1786
+ const logoColor = unstyled ? "" : accent ? "text-tollerud-yellow" : "text-tollerud-text-muted";
1787
+ const textWrapperClasses = layout === "row" ? "flex-1 text-right ml-4" : "flex-1 text-center md:text-right md:ml-4";
1788
+ const textLayoutClasses = layout === "row" ? "text-sm text-tollerud-text-secondary inline-flex flex-row items-center justify-end text-right gap-0" : "text-sm text-tollerud-text-secondary flex flex-col md:flex-row md:inline gap-0";
1789
+ return /* @__PURE__ */ jsx("footer", { className: cn("w-full pt-4 pb-4", footerSurface, className), style, children: /* @__PURE__ */ jsxs("div", { className: cn(innerLayoutClasses, classNameInner), children: [
1790
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 md:flex-shrink-0", children: /* @__PURE__ */ jsxs(
1791
+ "svg",
1792
+ {
1793
+ width: "24",
1794
+ height: "24",
1795
+ viewBox: "0 0 130 143",
1796
+ version: "1.1",
1797
+ xmlns: "http://www.w3.org/2000/svg",
1798
+ xmlnsXlink: "http://www.w3.org/1999/xlink",
1799
+ className: cn("h-5 w-5", logoColor, classNameLogo),
1800
+ role: "img",
1801
+ children: [
1802
+ /* @__PURE__ */ jsx("title", { children: "Tollerud Logo" }),
1803
+ /* @__PURE__ */ jsx("g", { id: "Page-1", stroke: "none", strokeWidth: "1", fill: "none", fillRule: "evenodd", children: /* @__PURE__ */ jsx("g", { id: "Tollerud-Monogram", transform: "translate(-86.000000, -109.000000)", fill: "currentColor", children: /* @__PURE__ */ jsx("g", { id: "Group-2", transform: "translate(32.000000, 55.000000)", children: /* @__PURE__ */ jsx("g", { id: "Group", transform: "translate(54.000000, 54.000000)", children: /* @__PURE__ */ jsx("path", { d: MONOGRAM_PATH, id: "Monogram" }) }) }) }) })
1804
+ ]
1805
+ }
1806
+ ) }),
1807
+ /* @__PURE__ */ jsx("div", { className: textWrapperClasses, children: /* @__PURE__ */ jsxs("p", { className: cn(textLayoutClasses, classNameText), children: [
1808
+ /* @__PURE__ */ jsxs("span", { children: [
1809
+ /* @__PURE__ */ jsx(
1810
+ "a",
1811
+ {
1812
+ href: "https://tollerud.no",
1813
+ target: "_blank",
1814
+ rel: "noopener noreferrer",
1815
+ className: cn(
1816
+ "underline decoration-tollerud-yellow decoration-[3px] underline-offset-[4px] hover:opacity-80 transition-opacity",
1817
+ classNameLink
1818
+ ),
1819
+ style: {
1820
+ textDecorationThickness: "3px",
1821
+ textUnderlineOffset: "4px"
1822
+ },
1823
+ children: t.tollerudProject
1824
+ }
1825
+ ),
1826
+ attribution ? /* @__PURE__ */ jsxs(Fragment, { children: [
1827
+ " ",
1828
+ /* @__PURE__ */ jsx("span", { children: attribution })
1829
+ ] }) : null,
1830
+ " "
1831
+ ] }),
1832
+ /* @__PURE__ */ jsx("span", { className: layout === "row" ? "ml-1" : "md:ml-1", children: t.allRightsReserved })
1833
+ ] }) })
1834
+ ] }) });
1835
+ }
1836
+ var Dialog = DialogPrimitive.Root;
1837
+ var DialogTrigger = DialogPrimitive.Trigger;
1838
+ var DialogPortal = DialogPrimitive.Portal;
1839
+ var DialogClose = DialogPrimitive.Close;
1840
+ var DialogOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1841
+ DialogPrimitive.Overlay,
1842
+ {
1843
+ ref,
1844
+ className: cn(
1845
+ "fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
1846
+ className
1847
+ ),
1848
+ ...props
1849
+ }
1850
+ ));
1851
+ DialogOverlay.displayName = "DialogOverlay";
1852
+ var DialogContent = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
1853
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1854
+ /* @__PURE__ */ jsxs(
1855
+ DialogPrimitive.Content,
1856
+ {
1857
+ ref,
1858
+ className: cn(
1859
+ "fixed top-1/2 left-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2",
1860
+ "rounded-lg border border-tollerud-border/30 bg-tollerud-noir-900 p-6 shadow-xl",
1861
+ "data-[state=open]:animate-none data-[state=closed]:animate-none",
1862
+ className
1863
+ ),
1864
+ ...props,
1865
+ children: [
1866
+ children,
1867
+ /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute top-4 right-4 rounded-sm p-1 text-tollerud-text-muted hover:text-tollerud-foreground transition-colors cursor-pointer", children: [
1868
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
1869
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1870
+ ] })
1871
+ ]
1872
+ }
1873
+ )
1874
+ ] }));
1875
+ DialogContent.displayName = "DialogContent";
1876
+ var DialogHeader = ({
1877
+ className,
1878
+ ...props
1879
+ }) => /* @__PURE__ */ jsx(
1880
+ "div",
1881
+ {
1882
+ className: cn("flex flex-col gap-1.5 mb-4", className),
1883
+ ...props
1884
+ }
1885
+ );
1886
+ DialogHeader.displayName = "DialogHeader";
1887
+ var DialogFooter = ({
1888
+ className,
1889
+ ...props
1890
+ }) => /* @__PURE__ */ jsx(
1891
+ "div",
1892
+ {
1893
+ className: cn("flex flex-col-reverse sm:flex-row sm:justify-end gap-2 mt-6", className),
1894
+ ...props
1895
+ }
1896
+ );
1897
+ DialogFooter.displayName = "DialogFooter";
1898
+ var DialogTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1899
+ DialogPrimitive.Title,
1900
+ {
1901
+ ref,
1902
+ className: cn("text-base font-semibold text-tollerud-foreground", className),
1903
+ ...props
1904
+ }
1905
+ ));
1906
+ DialogTitle.displayName = "DialogTitle";
1907
+ var DialogDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1908
+ DialogPrimitive.Description,
1909
+ {
1910
+ ref,
1911
+ className: cn("text-sm text-tollerud-text-secondary", className),
1912
+ ...props
1913
+ }
1914
+ ));
1915
+ DialogDescription.displayName = "DialogDescription";
1916
+ var TooltipProvider = TooltipPrimitive.Provider;
1917
+ var TooltipContext = React2.createContext(null);
1918
+ function Tooltip({
1919
+ children,
1920
+ defaultOpen,
1921
+ open: controlledOpen,
1922
+ onOpenChange: controlledOnOpenChange,
1923
+ ...props
1924
+ }) {
1925
+ const [internalOpen, setInternalOpen] = React2.useState(defaultOpen ?? false);
1926
+ const isControlled = controlledOpen !== void 0;
1927
+ const open = isControlled ? controlledOpen : internalOpen;
1928
+ const setOpen = isControlled ? controlledOnOpenChange ?? setInternalOpen : setInternalOpen;
1929
+ return /* @__PURE__ */ jsx(TooltipContext.Provider, { value: { open, setOpen }, children: /* @__PURE__ */ jsx(TooltipPrimitive.Root, { open, onOpenChange: (v) => setOpen(v), ...props, children }) });
1930
+ }
1931
+ Tooltip.displayName = "Tooltip";
1932
+ var TooltipTrigger = React2.forwardRef(({ onClick, onTouchStart, children, ...props }, ref) => {
1933
+ const ctx = React2.useContext(TooltipContext);
1934
+ const touchFired = React2.useRef(false);
1935
+ return /* @__PURE__ */ jsx(
1936
+ TooltipPrimitive.Trigger,
1937
+ {
1938
+ ref,
1939
+ onClick: (e) => {
1940
+ if (touchFired.current) {
1941
+ touchFired.current = false;
1942
+ return;
1943
+ }
1944
+ if (ctx) {
1945
+ ctx.setOpen(!ctx.open);
1946
+ }
1947
+ onClick?.(e);
1948
+ },
1949
+ onTouchStart: (e) => {
1950
+ touchFired.current = true;
1951
+ if (ctx && !ctx.open) {
1952
+ ctx.setOpen(true);
1953
+ }
1954
+ onTouchStart?.(e);
1955
+ },
1956
+ ...props,
1957
+ children
1958
+ }
1959
+ );
1960
+ });
1961
+ TooltipTrigger.displayName = "TooltipTrigger";
1962
+ var TooltipContent = React2.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
1963
+ TooltipPrimitive.Content,
1964
+ {
1965
+ ref,
1966
+ sideOffset,
1967
+ className: cn(
1968
+ "z-50 overflow-hidden rounded-md border border-tollerud-border/30",
1969
+ "bg-tollerud-noir-800 px-3 py-1.5 text-xs text-tollerud-foreground",
1970
+ "shadow-md",
1971
+ "animate-in fade-in-0 zoom-in-95",
1972
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
1973
+ "data-[side=bottom]:slide-in-from-top-1",
1974
+ "data-[side=left]:slide-in-from-right-1",
1975
+ "data-[side=right]:slide-in-from-left-1",
1976
+ "data-[side=top]:slide-in-from-bottom-1",
1977
+ className
1978
+ ),
1979
+ ...props
1980
+ }
1981
+ ));
1982
+ TooltipContent.displayName = "TooltipContent";
1983
+ var Tabs = TabsPrimitive.Root;
1984
+ var TabsList = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1985
+ TabsPrimitive.List,
1986
+ {
1987
+ ref,
1988
+ className: cn(
1989
+ "inline-flex h-9 items-center justify-center rounded-lg",
1990
+ "bg-tollerud-noir-800 p-1 text-tollerud-text-muted",
1991
+ className
1992
+ ),
1993
+ ...props
1994
+ }
1995
+ ));
1996
+ TabsList.displayName = "TabsList";
1997
+ var TabsTrigger = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1998
+ TabsPrimitive.Trigger,
1999
+ {
2000
+ ref,
2001
+ className: cn(
2002
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1",
2003
+ "text-xs font-medium transition-all",
2004
+ "text-tollerud-text-muted hover:text-tollerud-foreground",
2005
+ "data-[state=active]:bg-tollerud-surface-raised data-[state=active]:text-tollerud-foreground data-[state=active]:shadow-sm",
2006
+ "disabled:pointer-events-none disabled:opacity-50",
2007
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-tollerud-yellow/50",
2008
+ className
2009
+ ),
2010
+ ...props
2011
+ }
2012
+ ));
2013
+ TabsTrigger.displayName = "TabsTrigger";
2014
+ var TabsContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2015
+ TabsPrimitive.Content,
2016
+ {
2017
+ ref,
2018
+ className: cn(
2019
+ "mt-2 ring-offset-tollerud-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-tollerud-yellow/50 focus-visible:ring-offset-2",
2020
+ className
2021
+ ),
2022
+ ...props
2023
+ }
2024
+ ));
2025
+ TabsContent.displayName = "TabsContent";
2026
+ function Skeleton({
2027
+ className,
2028
+ ...props
2029
+ }) {
2030
+ return /* @__PURE__ */ jsx(
2031
+ "div",
2032
+ {
2033
+ className: cn(
2034
+ "animate-pulse rounded-md bg-tollerud-noir-800",
2035
+ className
2036
+ ),
2037
+ ...props
2038
+ }
2039
+ );
2040
+ }
2041
+ var Progress = React2.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(
2042
+ ProgressPrimitive.Root,
2043
+ {
2044
+ ref,
2045
+ className: cn(
2046
+ "relative h-2 w-full overflow-hidden rounded-full bg-tollerud-noir-800",
2047
+ className
2048
+ ),
2049
+ ...props,
2050
+ children: /* @__PURE__ */ jsx(
2051
+ ProgressPrimitive.Indicator,
2052
+ {
2053
+ className: "h-full w-full flex-1 rounded-full bg-tollerud-yellow transition-all duration-300 ease-in-out",
2054
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
2055
+ }
2056
+ )
2057
+ }
2058
+ ));
2059
+ Progress.displayName = "Progress";
2060
+ var Toaster = ({ theme = "dark", ...props }) => {
2061
+ const [mobile, setMobile] = useState(false);
2062
+ useEffect(() => {
2063
+ const check = () => setMobile(window.innerWidth < 640);
2064
+ check();
2065
+ window.addEventListener("resize", check);
2066
+ return () => window.removeEventListener("resize", check);
2067
+ }, []);
2068
+ return /* @__PURE__ */ jsx(
2069
+ Toaster$1,
2070
+ {
2071
+ position: mobile ? "top-center" : "top-right",
2072
+ theme,
2073
+ className: "toaster group",
2074
+ toastOptions: {
2075
+ classNames: {
2076
+ toast: "group toast group-[.toaster]:bg-tollerud-noir-900 group-[.toaster]:text-tollerud-foreground group-[.toaster]:border group-[.toaster]:border-tollerud-border/30 group-[.toaster]:shadow-lg",
2077
+ description: "group-[.toast]:text-tollerud-text-muted text-xs",
2078
+ actionButton: "group-[.toast]:bg-tollerud-yellow group-[.toast]:text-tollerud-black group-[.toast]:text-xs group-[.toast]:font-medium group-[.toast]:px-3 group-[.toast]:py-1 group-[.toast]:rounded",
2079
+ cancelButton: "group-[.toast]:bg-tollerud-noir-800 group-[.toast]:text-tollerud-text-muted group-[.toast]:text-xs group-[.toast]:px-3 group-[.toast]:py-1 group-[.toast]:rounded",
2080
+ success: "group-[.toast]:border-l-tollerud-success group-[.toast]:border-l-2",
2081
+ error: "group-[.toast]:border-l-tollerud-error group-[.toast]:border-l-2",
2082
+ warning: "group-[.toast]:border-l-tollerud-yellow group-[.toast]:border-l-2",
2083
+ info: "group-[.toast]:border-l-tollerud-info group-[.toast]:border-l-2"
2084
+ }
2085
+ },
2086
+ ...props
2087
+ }
2088
+ );
2089
+ };
2090
+ var Empty = forwardRef(
2091
+ ({ className, ...props }, ref) => {
2092
+ return /* @__PURE__ */ jsx(
2093
+ "div",
2094
+ {
2095
+ ref,
2096
+ className: cn(
2097
+ "flex w-full flex-1 flex-col items-center justify-center text-center py-12",
2098
+ className
2099
+ ),
2100
+ ...props
2101
+ }
2102
+ );
2103
+ }
2104
+ );
2105
+ Empty.displayName = "Empty";
2106
+ var EmptyHeader = forwardRef(
2107
+ ({ className, ...props }, ref) => {
2108
+ return /* @__PURE__ */ jsx(
2109
+ "div",
2110
+ {
2111
+ ref,
2112
+ className: cn(
2113
+ "flex max-w-sm flex-col items-center gap-2",
2114
+ className
2115
+ ),
2116
+ ...props
2117
+ }
2118
+ );
2119
+ }
2120
+ );
2121
+ EmptyHeader.displayName = "EmptyHeader";
2122
+ var EmptyIcon = forwardRef(
2123
+ ({ className, ...props }, ref) => {
2124
+ return /* @__PURE__ */ jsx(
2125
+ "div",
2126
+ {
2127
+ ref,
2128
+ className: cn(
2129
+ "flex items-center justify-center w-12 h-12 rounded-full bg-tollerud-noir-800 text-tollerud-text-muted mb-2",
2130
+ className
2131
+ ),
2132
+ ...props
2133
+ }
2134
+ );
2135
+ }
2136
+ );
2137
+ EmptyIcon.displayName = "EmptyIcon";
2138
+ var EmptyTitle = forwardRef(
2139
+ ({ className, ...props }, ref) => {
2140
+ return /* @__PURE__ */ jsx(
2141
+ "h3",
2142
+ {
2143
+ ref,
2144
+ className: cn(
2145
+ "text-base font-semibold text-tollerud-foreground",
2146
+ className
2147
+ ),
2148
+ ...props
2149
+ }
2150
+ );
2151
+ }
2152
+ );
2153
+ EmptyTitle.displayName = "EmptyTitle";
2154
+ var EmptyDescription = forwardRef(
2155
+ ({ className, ...props }, ref) => {
2156
+ return /* @__PURE__ */ jsx(
2157
+ "p",
2158
+ {
2159
+ ref,
2160
+ className: cn(
2161
+ "text-sm text-tollerud-text-secondary max-w-sm",
2162
+ className
2163
+ ),
2164
+ ...props
2165
+ }
2166
+ );
2167
+ }
2168
+ );
2169
+ EmptyDescription.displayName = "EmptyDescription";
2170
+ var EmptyContent = forwardRef(
2171
+ ({ className, ...props }, ref) => {
2172
+ return /* @__PURE__ */ jsx(
2173
+ "div",
2174
+ {
2175
+ ref,
2176
+ className: cn(
2177
+ "flex w-full max-w-sm flex-col items-center gap-4 mt-4",
2178
+ className
2179
+ ),
2180
+ ...props
2181
+ }
2182
+ );
2183
+ }
2184
+ );
2185
+ EmptyContent.displayName = "EmptyContent";
2186
+ var DropdownMenu = DropdownMenuPrimitive.Root;
2187
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
2188
+ var DropdownMenuContent = forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
2189
+ DropdownMenuPrimitive.Content,
2190
+ {
2191
+ ref,
2192
+ sideOffset,
2193
+ className: cn(
2194
+ "z-50 min-w-[9rem] overflow-hidden rounded-lg border p-1 shadow-md",
2195
+ "bg-tollerud-noir-850 border-tollerud-border/30 text-tollerud-text-primary",
2196
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
2197
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2198
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
2199
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
2200
+ "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
2201
+ className
2202
+ ),
2203
+ ...props
2204
+ }
2205
+ ) }));
2206
+ DropdownMenuContent.displayName = "DropdownMenuContent";
2207
+ var DropdownMenuItem = forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
2208
+ DropdownMenuPrimitive.Item,
2209
+ {
2210
+ ref,
2211
+ className: cn(
2212
+ "relative flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none",
2213
+ "text-tollerud-text-secondary data-[highlighted]:text-tollerud-text-primary",
2214
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
2215
+ "data-[highlighted]:bg-tollerud-surface-raised",
2216
+ inset && "pl-8",
2217
+ className
2218
+ ),
2219
+ ...props
2220
+ }
2221
+ ));
2222
+ DropdownMenuItem.displayName = "DropdownMenuItem";
2223
+ var DropdownMenuSeparator = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2224
+ DropdownMenuPrimitive.Separator,
2225
+ {
2226
+ ref,
2227
+ className: cn("-mx-1 my-1 h-px bg-tollerud-border/30", className),
2228
+ ...props
2229
+ }
2230
+ ));
2231
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
2232
+ var DropdownMenuLabel = forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
2233
+ DropdownMenuPrimitive.Label,
2234
+ {
2235
+ ref,
2236
+ className: cn("px-2 py-1.5 text-xs font-semibold text-tollerud-text-muted", inset && "pl-8", className),
2237
+ ...props
2238
+ }
2239
+ ));
2240
+ DropdownMenuLabel.displayName = "DropdownMenuLabel";
2241
+ var Sheet = ({ open, onOpenChange, children }) => /* @__PURE__ */ jsx(DialogPrimitive.Root, { open, onOpenChange, children });
2242
+ var SheetTrigger = DialogPrimitive.Trigger;
2243
+ var SheetClose = DialogPrimitive.Close;
2244
+ var SheetOverlay = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2245
+ DialogPrimitive.Overlay,
2246
+ {
2247
+ ref,
2248
+ className: cn(
2249
+ "fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
2250
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
2251
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2252
+ className
2253
+ ),
2254
+ ...props
2255
+ }
2256
+ ));
2257
+ SheetOverlay.displayName = "SheetOverlay";
2258
+ var SheetContent = forwardRef(({ className, children, side = "right", ...props }, ref) => /* @__PURE__ */ jsx(SheetOverlay, { children: /* @__PURE__ */ jsxs(
2259
+ DialogPrimitive.Content,
2260
+ {
2261
+ ref,
2262
+ className: cn(
2263
+ "fixed z-50 gap-4 bg-tollerud-noir-900 border-tollerud-border/30 p-6 shadow-lg",
2264
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
2265
+ side === "right" && [
2266
+ "inset-y-0 right-0 h-full w-full max-w-md border-l",
2267
+ "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right"
2268
+ ],
2269
+ side === "left" && [
2270
+ "inset-y-0 left-0 h-full w-full max-w-md border-r",
2271
+ "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left"
2272
+ ],
2273
+ className
2274
+ ),
2275
+ ...props,
2276
+ children: [
2277
+ children,
2278
+ /* @__PURE__ */ jsxs(SheetClose, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100 transition-opacity text-tollerud-text-muted hover:text-tollerud-text-primary", children: [
2279
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2280
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
2281
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
2282
+ ] }),
2283
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2284
+ ] })
2285
+ ]
2286
+ }
2287
+ ) }));
2288
+ SheetContent.displayName = "SheetContent";
2289
+ var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-left", className), ...props });
2290
+ var SheetTitle = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2291
+ DialogPrimitive.Title,
2292
+ {
2293
+ ref,
2294
+ className: cn("text-base font-semibold text-tollerud-text-primary", className),
2295
+ ...props
2296
+ }
2297
+ ));
2298
+ SheetTitle.displayName = "SheetTitle";
2299
+ var SheetDescription = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2300
+ DialogPrimitive.Description,
2301
+ {
2302
+ ref,
2303
+ className: cn("text-sm text-tollerud-text-secondary", className),
2304
+ ...props
2305
+ }
2306
+ ));
2307
+ SheetDescription.displayName = "SheetDescription";
2308
+ function DataTableInner({
2309
+ columns,
2310
+ data,
2311
+ rowKey,
2312
+ onRowClick,
2313
+ className,
2314
+ emptyMessage = "No data",
2315
+ forwardedRef
2316
+ }) {
2317
+ const [sortKey, setSortKey] = useState(null);
2318
+ const [sortDir, setSortDir] = useState("asc");
2319
+ const [filters, setFilters] = useState({});
2320
+ const filtered = useMemo(() => {
2321
+ const activeFilters = Object.entries(filters).filter(([, v]) => v.trim() !== "");
2322
+ if (activeFilters.length === 0) return data;
2323
+ return data.filter(
2324
+ (row) => activeFilters.every(([key, filterValue]) => {
2325
+ const cellValue = String(row[key] ?? "").toLowerCase();
2326
+ return cellValue.includes(filterValue.toLowerCase());
2327
+ })
2328
+ );
2329
+ }, [data, filters]);
2330
+ const sorted = useMemo(() => {
2331
+ if (!sortKey) return filtered;
2332
+ const col = columns.find((c) => c.key === sortKey);
2333
+ if (!col?.sortable) return filtered;
2334
+ return [...filtered].sort((a, b) => {
2335
+ const aVal = a[sortKey];
2336
+ const bVal = b[sortKey];
2337
+ if (typeof aVal === "number" && typeof bVal === "number") {
2338
+ return sortDir === "asc" ? aVal - bVal : bVal - aVal;
2339
+ }
2340
+ const aStr = String(aVal ?? "");
2341
+ const bStr = String(bVal ?? "");
2342
+ const cmp = aStr.localeCompare(bStr, "nb", { numeric: true });
2343
+ return sortDir === "asc" ? cmp : -cmp;
2344
+ });
2345
+ }, [filtered, sortKey, sortDir, columns]);
2346
+ const getRowKey = (row, i) => {
2347
+ if (typeof rowKey === "function") return rowKey(row);
2348
+ if (rowKey) return row[rowKey];
2349
+ return row.id ?? row.key ?? i;
2350
+ };
2351
+ const toggleSort = (key) => {
2352
+ if (sortKey === key) {
2353
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
2354
+ } else {
2355
+ setSortKey(key);
2356
+ setSortDir("asc");
2357
+ }
2358
+ };
2359
+ const updateFilter = (key, value) => {
2360
+ setFilters((prev) => {
2361
+ const next = { ...prev };
2362
+ if (value.trim() === "") {
2363
+ delete next[key];
2364
+ } else {
2365
+ next[key] = value;
2366
+ }
2367
+ return next;
2368
+ });
2369
+ };
2370
+ const hasActiveFilters = Object.values(filters).some((v) => v.trim() !== "");
2371
+ const filterableColumns = columns.filter((c) => c.filterable);
2372
+ return /* @__PURE__ */ jsx("div", { ref: forwardedRef, className: cn("overflow-x-auto rounded-lg border border-tollerud-border/30", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
2373
+ /* @__PURE__ */ jsxs("thead", { children: [
2374
+ /* @__PURE__ */ jsx("tr", { className: "border-b border-tollerud-border/30 bg-tollerud-noir-900", children: columns.map((col) => /* @__PURE__ */ jsx(
2375
+ "th",
2376
+ {
2377
+ className: cn(
2378
+ "px-3 py-2.5 text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider",
2379
+ col.sortable && "cursor-pointer select-none hover:text-tollerud-text-primary transition-colors",
2380
+ col.align === "right" && "text-right",
2381
+ col.align === "center" && "text-center"
2382
+ ),
2383
+ style: col.width ? { width: col.width } : void 0,
2384
+ onClick: () => col.sortable && toggleSort(col.key),
2385
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
2386
+ col.label,
2387
+ col.sortable && sortKey === col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-accent", children: sortDir === "asc" ? "\u2191" : "\u2193" }),
2388
+ col.sortable && sortKey !== col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-muted/30", children: "\u2195" }),
2389
+ col.filterable && /* @__PURE__ */ jsx(
2390
+ "svg",
2391
+ {
2392
+ width: "12",
2393
+ height: "12",
2394
+ viewBox: "0 0 24 24",
2395
+ fill: "none",
2396
+ stroke: "currentColor",
2397
+ strokeWidth: "2",
2398
+ strokeLinecap: "round",
2399
+ strokeLinejoin: "round",
2400
+ className: cn(
2401
+ "opacity-40",
2402
+ filters[col.key] && "opacity-100 text-tollerud-yellow"
2403
+ ),
2404
+ children: /* @__PURE__ */ jsx("path", { d: "M4 4h16v2.172a2 2 0 0 1-.586 1.414L15 12v7l-6 2v-8.5L4.52 7.53A2 2 0 0 1 4 6.16V4z" })
2405
+ }
2406
+ )
2407
+ ] })
2408
+ },
2409
+ col.key
2410
+ )) }),
2411
+ filterableColumns.length > 0 && /* @__PURE__ */ jsx("tr", { className: "border-b border-tollerud-border/20", children: columns.map((col) => /* @__PURE__ */ jsx(
2412
+ "th",
2413
+ {
2414
+ className: cn(
2415
+ "px-1.5 py-1",
2416
+ col.align === "right" && "text-right",
2417
+ col.align === "center" && "text-center"
2418
+ ),
2419
+ children: col.filterable ? /* @__PURE__ */ jsx(
2420
+ "input",
2421
+ {
2422
+ type: "text",
2423
+ placeholder: `Filter ${col.label.toLowerCase()}\u2026`,
2424
+ value: filters[col.key] ?? "",
2425
+ onChange: (e) => updateFilter(col.key, e.target.value),
2426
+ className: cn(
2427
+ "w-full px-2 py-1 text-xs rounded border transition-colors outline-none",
2428
+ "bg-tollerud-noir-900/50 border-tollerud-border/20 text-tollerud-text-primary placeholder:text-tollerud-text-muted/40",
2429
+ "focus:border-tollerud-yellow/40 focus:ring-1 focus:ring-tollerud-yellow/20"
2430
+ )
2431
+ }
2432
+ ) : null
2433
+ },
2434
+ `filter-${col.key}`
2435
+ )) })
2436
+ ] }),
2437
+ /* @__PURE__ */ jsx("tbody", { children: sorted.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
2438
+ "td",
2439
+ {
2440
+ colSpan: columns.length,
2441
+ className: "px-3 py-8 text-center text-sm text-tollerud-text-muted",
2442
+ children: hasActiveFilters ? "No matching rows" : emptyMessage
2443
+ }
2444
+ ) }) : sorted.map((row, i) => /* @__PURE__ */ jsx(
2445
+ "tr",
2446
+ {
2447
+ className: cn(
2448
+ "border-b border-tollerud-border/20 transition-colors",
2449
+ onRowClick && "cursor-pointer hover:bg-tollerud-surface-raised/50"
2450
+ ),
2451
+ onClick: () => onRowClick?.(row),
2452
+ children: columns.map((col) => {
2453
+ const value = row[col.key];
2454
+ return /* @__PURE__ */ jsx(
2455
+ "td",
2456
+ {
2457
+ className: cn(
2458
+ "px-3 py-2.5 text-tollerud-text-secondary",
2459
+ col.align === "right" && "text-right",
2460
+ col.align === "center" && "text-center",
2461
+ "font-mono text-xs"
2462
+ ),
2463
+ children: col.render ? col.render(value, row) : String(value ?? "\u2014")
2464
+ },
2465
+ col.key
2466
+ );
2467
+ })
2468
+ },
2469
+ getRowKey(row, i)
2470
+ )) })
2471
+ ] }) });
2472
+ }
2473
+ var DataTable = forwardRef((props, ref) => /* @__PURE__ */ jsx(DataTableInner, { ...props, forwardedRef: ref }));
2474
+ DataTable.displayName = "DataTable";
2475
+ function GlowCard({
2476
+ children,
2477
+ className,
2478
+ glowColor = "var(--tollerud-accent, #FFFF00)",
2479
+ intensity = 0.15
2480
+ }) {
2481
+ const ref = useRef(null);
2482
+ const [pos, setPos] = useState({ x: 0, y: 0 });
2483
+ const [isHovered, setIsHovered] = useState(false);
2484
+ const handleMouseMove = (e) => {
2485
+ if (!ref.current) return;
2486
+ const rect = ref.current.getBoundingClientRect();
2487
+ setPos({
2488
+ x: (e.clientX - rect.left) / rect.width * 100,
2489
+ y: (e.clientY - rect.top) / rect.height * 100
2490
+ });
2491
+ };
2492
+ return /* @__PURE__ */ jsxs(
2493
+ "div",
2494
+ {
2495
+ ref,
2496
+ className: cn("relative overflow-hidden", className),
2497
+ onMouseMove: handleMouseMove,
2498
+ onMouseEnter: () => setIsHovered(true),
2499
+ onMouseLeave: () => setIsHovered(false),
2500
+ children: [
2501
+ /* @__PURE__ */ jsx(
2502
+ "div",
2503
+ {
2504
+ className: "pointer-events-none absolute inset-0 transition-opacity duration-300",
2505
+ style: {
2506
+ opacity: isHovered ? 1 : 0,
2507
+ background: `radial-gradient(600px circle at ${pos.x}% ${pos.y}%, ${glowColor} ${intensity * 100}%, transparent 70%)`
2508
+ }
2509
+ }
2510
+ ),
2511
+ /* @__PURE__ */ jsx("div", { className: "relative z-10", children })
2512
+ ]
2513
+ }
2514
+ );
2515
+ }
2516
+ function BentoDashboard({
2517
+ title,
2518
+ hosts = [],
2519
+ metrics = [],
2520
+ services = [],
2521
+ incidents = [],
2522
+ className
2523
+ }) {
2524
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
2525
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
2526
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-tollerud-text-primary", children: title }),
2527
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-tollerud-text-muted", children: [
2528
+ hosts.length,
2529
+ " hosts \xB7 ",
2530
+ services.length,
2531
+ " services",
2532
+ incidents.length > 0 && ` \xB7 ${incidents.length} active incident${incidents.length > 1 ? "s" : ""}`
2533
+ ] })
2534
+ ] }) }),
2535
+ hosts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [
2536
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 lg:col-span-1", children: /* @__PURE__ */ jsx(HostCardComponent, { host: hosts[0] }) }),
2537
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 md:col-span-2 lg:col-span-1", children: hosts.slice(1).map((h, i) => /* @__PURE__ */ jsx(HostCardComponent, { host: h }, h.hostname ?? i)) })
2538
+ ] }),
2539
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-3", children: [
2540
+ metrics.length > 0 && /* @__PURE__ */ jsxs("div", { className: "lg:col-span-2", children: [
2541
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Metrics" }),
2542
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-2", children: metrics.map((m, i) => /* @__PURE__ */ jsx(StatCardComponent, { stat: m }, m.label ?? i)) })
2543
+ ] }),
2544
+ services.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
2545
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Services" }),
2546
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: services.slice(0, 4).map((s, i) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-tollerud-border/20 bg-tollerud-surface p-3", children: [
2547
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2548
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-tollerud-text-primary", children: s.service }),
2549
+ /* @__PURE__ */ jsx("span", { className: cn(
2550
+ "text-xs px-1.5 py-0.5 rounded-full font-medium",
2551
+ s.status === "online" && "text-tollerud-accent bg-tollerud-accent/10",
2552
+ s.status === "warning" && "text-tollerud-warning bg-tollerud-warning/10",
2553
+ s.status === "offline" && "text-tollerud-error bg-tollerud-error/10",
2554
+ s.status === "idle" && "text-tollerud-text-muted bg-tollerud-noir-800"
2555
+ ), children: s.status })
2556
+ ] }),
2557
+ s.responseTime && /* @__PURE__ */ jsxs("p", { className: "text-xs text-tollerud-text-muted mt-1", children: [
2558
+ s.responseTime,
2559
+ " response"
2560
+ ] })
2561
+ ] }, s.service ?? i)) })
2562
+ ] })
2563
+ ] }),
2564
+ incidents.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
2565
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Recent Incidents" }),
2566
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: incidents.slice(0, 4).map((inc, i) => /* @__PURE__ */ jsx(IncidentCardItem, { incident: inc }, inc.title + i)) })
2567
+ ] })
2568
+ ] });
2569
+ }
2570
+ function SectionLabel({ children }) {
2571
+ return /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider mb-2", children });
2572
+ }
2573
+ function HostCardComponent({ host }) {
2574
+ return /* @__PURE__ */ jsxs("div", { className: cn(
2575
+ "rounded-xl border p-4",
2576
+ host.status === "online" ? "border-tollerud-border/30 bg-tollerud-surface" : host.status === "warning" ? "border-tollerud-warning/30 bg-tollerud-warning/5" : "border-tollerud-border/20 bg-tollerud-surface opacity-70"
2577
+ ), children: [
2578
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
2579
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2580
+ /* @__PURE__ */ jsx(StatusDotComp, { status: host.status }),
2581
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-text-primary", children: host.hostname })
2582
+ ] }),
2583
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-tollerud-text-muted font-mono", children: host.ip })
2584
+ ] }),
2585
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-xs", children: [
2586
+ host.cpu && /* @__PURE__ */ jsx(MetricBadge, { label: "CPU", value: host.cpu }),
2587
+ host.memory && /* @__PURE__ */ jsx(MetricBadge, { label: "Mem", value: host.memory }),
2588
+ host.disk && /* @__PURE__ */ jsx(MetricBadge, { label: "Disk", value: host.disk }),
2589
+ host.uptime && /* @__PURE__ */ jsx(MetricBadge, { label: "Up", value: host.uptime })
2590
+ ] })
2591
+ ] });
2592
+ }
2593
+ function StatCardComponent({ stat }) {
2594
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-tollerud-border/20 bg-tollerud-surface p-3", children: [
2595
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-text-muted", children: stat.label }),
2596
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-tollerud-text-primary mt-0.5", children: stat.value }),
2597
+ stat.change && /* @__PURE__ */ jsx("span", { className: cn(
2598
+ "text-xs font-medium",
2599
+ stat.change.direction === "up" ? "text-tollerud-accent" : "text-tollerud-error"
2600
+ ), children: stat.change.value })
2601
+ ] });
2602
+ }
2603
+ function IncidentCardItem({ incident }) {
2604
+ return /* @__PURE__ */ jsxs("div", { className: cn(
2605
+ "flex items-start gap-3 rounded-lg border p-3",
2606
+ incident.acknowledged ? "border-tollerud-border/10 bg-tollerud-noir-900/50 opacity-60" : "border-tollerud-border/20 bg-tollerud-surface"
2607
+ ), children: [
2608
+ /* @__PURE__ */ jsx(SeverityDot, { severity: incident.severity }),
2609
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2610
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2611
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-tollerud-text-primary truncate", children: incident.title }),
2612
+ incident.acknowledged && /* @__PURE__ */ jsx("span", { className: "text-xs text-tollerud-text-muted", children: "(acknowledged)" })
2613
+ ] }),
2614
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-tollerud-text-muted mt-0.5", children: [
2615
+ /* @__PURE__ */ jsx("span", { children: incident.service }),
2616
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2617
+ /* @__PURE__ */ jsx("span", { children: incident.timestamp })
2618
+ ] }),
2619
+ incident.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-text-secondary mt-1", children: incident.description })
2620
+ ] })
2621
+ ] });
2622
+ }
2623
+ function StatusDotComp({ status }) {
2624
+ return /* @__PURE__ */ jsx("span", { className: cn(
2625
+ "inline-block w-2 h-2 rounded-full",
2626
+ status === "online" && "bg-tollerud-accent shadow-[0_0_4px_var(--tollerud-accent)]",
2627
+ status === "warning" && "bg-tollerud-warning",
2628
+ status === "offline" && "bg-tollerud-error"
2629
+ ) });
2630
+ }
2631
+ function SeverityDot({ severity }) {
2632
+ return /* @__PURE__ */ jsx("span", { className: cn(
2633
+ "inline-block w-2 h-2 rounded-full mt-1 shrink-0",
2634
+ severity === "critical" && "bg-tollerud-error shadow-[0_0_4px_var(--tollerud-error)]",
2635
+ severity === "high" && "bg-tollerud-warning",
2636
+ severity === "medium" && "bg-tollerud-accent",
2637
+ severity === "low" && "bg-tollerud-text-muted",
2638
+ severity === "info" && "bg-tollerud-info"
2639
+ ) });
2640
+ }
2641
+ function MetricBadge({ label, value }) {
2642
+ return /* @__PURE__ */ jsxs("div", { className: "rounded bg-tollerud-noir-800 px-2 py-1.5", children: [
2643
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-muted", children: label }),
2644
+ /* @__PURE__ */ jsx("span", { className: "float-right text-tollerud-text-primary font-mono", children: value })
2645
+ ] });
2646
+ }
2647
+
2648
+ export { ActionDiff, ActionRow, Alert, AlertInbox, ApprovalCard, BackupStatusPanel, Badge, BentoDashboard, Button, Card, Checkbox, CodeBlock, CommandMenu, Container, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DockerStackCard, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyIcon, EmptyTitle, Footer, GlowCard, HostCard, IncidentCard, Input, Kbd, LogViewer, NoirGlowBackground, Progress, Radio, RadioGroup, RollbackPlan, Select, ServiceHealthCard, Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Skeleton, StatCard, StatusDot, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Timeline, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn };
2649
+ //# sourceMappingURL=index.js.map
2650
+ //# sourceMappingURL=index.js.map