@teton/smith-ui 0.2.180 → 0.2.182

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.
@@ -0,0 +1,20 @@
1
+ export interface BreadcrumbSegment {
2
+ label: string;
3
+ /** Distinguishes segments that repeat (e.g. `/var/lib/var`). */
4
+ key: string;
5
+ }
6
+ /**
7
+ * Path breadcrumb with a clickable trail. The last segment is the current
8
+ * location and renders as static text; the rest call `onNavigate` with their
9
+ * index.
10
+ *
11
+ * Deep paths collapse in the middle rather than wrapping or truncating the
12
+ * end — the first and last few segments are the ones that orient you, and the
13
+ * hidden middle stays reachable through the overflow menu.
14
+ */
15
+ export declare function Breadcrumbs({ segments, onNavigate, maxVisible, className, }: {
16
+ segments: BreadcrumbSegment[];
17
+ onNavigate: (index: number) => void;
18
+ maxVisible?: number;
19
+ className?: string;
20
+ }): import("react/jsx-runtime").JSX.Element;
package/dist/card.d.ts CHANGED
@@ -23,6 +23,29 @@ export declare function Panel({ icon: Icon, title, theme, count, actions, bodyCl
23
23
  className?: string;
24
24
  children: ReactNode;
25
25
  }): import("react/jsx-runtime").JSX.Element;
26
+ declare const ALERT_TONES: {
27
+ red: {
28
+ accent: string;
29
+ chip: string;
30
+ };
31
+ amber: {
32
+ accent: string;
33
+ chip: string;
34
+ };
35
+ };
36
+ export type AlertTone = keyof typeof ALERT_TONES;
37
+ /**
38
+ * Attention banner for a problem the page wants to lead with: colored left
39
+ * accent, headline plus severity chip, one line of detail, optional action on
40
+ * the right. Callers render it conditionally — it has no "all clear" state.
41
+ */
42
+ export declare function AlertBanner({ tone, title, severity, action, children, }: {
43
+ tone?: AlertTone;
44
+ title: ReactNode;
45
+ severity?: string;
46
+ action?: ReactNode;
47
+ children: ReactNode;
48
+ }): import("react/jsx-runtime").JSX.Element;
26
49
  /**
27
50
  * Card with a colored, titled header bar — for grouped lists.
28
51
  * Right side shows a count pill (when `count` is set) and/or `actions`.
@@ -53,3 +76,4 @@ export declare function ViewAllFooter({ to, count, noun, }: {
53
76
  count: number;
54
77
  noun?: string;
55
78
  }): import("react/jsx-runtime").JSX.Element;
79
+ export {};
package/dist/detail.d.ts CHANGED
@@ -14,9 +14,11 @@ export interface TabItem {
14
14
  state?: unknown;
15
15
  }
16
16
  /** Underlined tab bar used across the device detail subpages. The active tab
17
- * renders as static text; the rest are router links. */
18
- export declare function TabNav({ items }: {
17
+ * renders as static text; the rest are router links. `actions` renders on the
18
+ * right of the bar, for controls that belong to the active tab's content. */
19
+ export declare function TabNav({ items, actions, }: {
19
20
  items: TabItem[];
21
+ actions?: ReactNode;
20
22
  }): import("react/jsx-runtime").JSX.Element;
21
23
  export interface SideNavItem {
22
24
  label: string;
@@ -0,0 +1,33 @@
1
+ import { ReactNode } from 'react';
2
+ import { ButtonSize, ButtonTone, ButtonVariant } from './button';
3
+ export interface DropdownItem {
4
+ label: ReactNode;
5
+ /** Secondary line under the label — good for the detail a tooltip carried. */
6
+ description?: ReactNode;
7
+ icon?: ReactNode;
8
+ /** Tints the icon with the tone the action would have as a standalone button. */
9
+ tone?: ButtonTone;
10
+ onClick?: () => void;
11
+ /** Leaves the menu open after selecting — for items that swap to an
12
+ * in-place confirmation (e.g. "Copied!") instead of navigating away. */
13
+ keepOpen?: boolean;
14
+ /** Renders the item as an external link instead of a button. */
15
+ href?: string;
16
+ target?: string;
17
+ disabled?: boolean;
18
+ }
19
+ /**
20
+ * A button that opens a menu of actions — for collapsing a row of related
21
+ * buttons into one control. Closes on outside click, Escape, or item select.
22
+ */
23
+ export declare function DropdownMenu({ label, items, icon, variant, tone, size, align, className, }: {
24
+ label: ReactNode;
25
+ items: DropdownItem[];
26
+ icon?: ReactNode;
27
+ variant?: ButtonVariant;
28
+ tone?: ButtonTone;
29
+ size?: ButtonSize;
30
+ /** Which edge of the trigger the menu is anchored to. */
31
+ align?: "left" | "right";
32
+ className?: string;
33
+ }): import("react/jsx-runtime").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  export { BADGE_COLORS, Badge, type BadgeVariant, LabelChip } from './badge';
2
+ export { type BreadcrumbSegment, Breadcrumbs } from './breadcrumbs';
2
3
  export { Button, type ButtonSize, type ButtonTone, type ButtonVariant, } from './button';
3
- export { Card, ListRow, Panel, SectionCard, ViewAllFooter } from './card';
4
+ export { AlertBanner, type AlertTone, Card, ListRow, Panel, SectionCard, ViewAllFooter, } from './card';
4
5
  export { CountryFlag } from './country-flag';
5
6
  export { BackLink, InfoRow, SideNav, type SideNavItem, type TabItem, TabNav, } from './detail';
7
+ export { type DropdownItem, DropdownMenu } from './dropdown';
6
8
  export { PageContainer } from './page-container';
7
9
  export { SearchInput } from './search-input';
8
10
  export { StatCard } from './stat-card';
9
- export { StateTimeline, type TimelineLane, type TimelineSpan, type TimelineTone, } from './state-timeline';
11
+ export { StateTimeline, type TimelineLane, type TimelineSpan, type TimelineTone, UptimeBars, type UptimeBucket, } from './state-timeline';
10
12
  export { type IconComponent, SECTION_THEMES, type SectionTheme, type SectionThemeName, } from './theme';
11
13
  export { Toast, type ToastState } from './toast';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { jsx as e, jsxs as l, Fragment as y } from "react/jsx-runtime";
2
- import { X as v, Loader2 as E, ArrowLeft as S, Search as L, Check as C } from "lucide-react";
3
- import { Link as h } from "react-router";
4
- import { useRef as j, useEffect as A } from "react";
5
- const R = {
1
+ import { jsx as e, jsxs as l, Fragment as v } from "react/jsx-runtime";
2
+ import { X as $, ChevronRight as w, MoreHorizontal as R, Loader2 as j, ArrowLeft as D, ChevronDown as B, Search as I, Check as K } from "lucide-react";
3
+ import { useState as k, useRef as A, useEffect as O } from "react";
4
+ import { Link as f } from "react-router";
5
+ const z = {
6
6
  gray: "bg-gray-100 text-gray-700",
7
7
  blue: "bg-blue-100 text-blue-700",
8
8
  green: "bg-green-100 text-green-700",
@@ -11,55 +11,55 @@ const R = {
11
11
  purple: "bg-purple-100 text-purple-700",
12
12
  orange: "bg-orange-100 text-orange-700"
13
13
  };
14
- function U({
15
- variant: t = "gray",
16
- pill: r = !1,
14
+ function W({
15
+ variant: r = "gray",
16
+ pill: t = !1,
17
17
  className: n = "",
18
- children: o
18
+ children: a
19
19
  }) {
20
20
  return /* @__PURE__ */ e(
21
21
  "span",
22
22
  {
23
- className: `px-1.5 py-0.5 text-xs font-medium ${r ? "rounded-full" : "rounded"} ${R[t]} ${n}`,
24
- children: o
23
+ className: `px-1.5 py-0.5 text-xs font-medium ${t ? "rounded-full" : "rounded"} ${z[r]} ${n}`,
24
+ children: a
25
25
  }
26
26
  );
27
27
  }
28
- function V({
29
- name: t,
30
- value: r,
28
+ function ee({
29
+ name: r,
30
+ value: t,
31
31
  onClick: n,
32
- onRemove: o,
33
- active: a = !1,
34
- title: g
32
+ onRemove: a,
33
+ active: o = !1,
34
+ title: m
35
35
  }) {
36
- const s = /* @__PURE__ */ l(y, { children: [
36
+ const s = /* @__PURE__ */ l(v, { children: [
37
37
  /* @__PURE__ */ e(
38
38
  "span",
39
39
  {
40
- className: `px-2 py-1 font-medium ${a ? "text-blue-700 bg-blue-100" : "text-gray-500 bg-gray-50"}`,
41
- children: t
40
+ className: `px-2 py-1 font-medium ${o ? "text-blue-700 bg-blue-100" : "text-gray-500 bg-gray-50"}`,
41
+ children: r
42
42
  }
43
43
  ),
44
- r !== void 0 && r !== "" && /* @__PURE__ */ e(
44
+ t !== void 0 && t !== "" && /* @__PURE__ */ e(
45
45
  "span",
46
46
  {
47
- className: `px-2 py-1 font-mono border-l ${a ? "text-blue-700 bg-blue-100 border-blue-200" : "text-gray-900 bg-white border-gray-200"}`,
48
- children: r
47
+ className: `px-2 py-1 font-mono border-l ${o ? "text-blue-700 bg-blue-100 border-blue-200" : "text-gray-900 bg-white border-gray-200"}`,
48
+ children: t
49
49
  }
50
50
  )
51
- ] }), i = `inline-flex items-center overflow-hidden rounded-md border text-xs ${a ? "border-blue-300" : "border-gray-200"}`;
52
- return o ? /* @__PURE__ */ l("span", { className: i, children: [
51
+ ] }), b = `inline-flex items-center overflow-hidden rounded-md border text-xs ${o ? "border-blue-300" : "border-gray-200"}`;
52
+ return a ? /* @__PURE__ */ l("span", { className: b, children: [
53
53
  s,
54
54
  /* @__PURE__ */ e(
55
55
  "button",
56
56
  {
57
57
  type: "button",
58
- onClick: o,
59
- title: g,
60
- "aria-label": `Remove ${r !== void 0 ? `${t}=${r}` : t}`,
61
- className: `flex items-center px-1.5 py-1 border-l cursor-pointer transition-colors ${a ? "bg-blue-100 border-blue-200 text-blue-500 hover:bg-blue-200 hover:text-blue-700" : "border-gray-200 text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
62
- children: /* @__PURE__ */ e(v, { className: "w-3 h-3" })
58
+ onClick: a,
59
+ title: m,
60
+ "aria-label": `Remove ${t !== void 0 ? `${r}=${t}` : r}`,
61
+ className: `flex items-center px-1.5 py-1 border-l cursor-pointer transition-colors ${o ? "bg-blue-100 border-blue-200 text-blue-500 hover:bg-blue-200 hover:text-blue-700" : "border-gray-200 text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
62
+ children: /* @__PURE__ */ e($, { className: "w-3 h-3" })
63
63
  }
64
64
  )
65
65
  ] }) : n ? /* @__PURE__ */ e(
@@ -67,13 +67,71 @@ function V({
67
67
  {
68
68
  type: "button",
69
69
  onClick: n,
70
- title: g,
71
- className: `${i} cursor-pointer transition-colors ${a ? "" : "hover:border-gray-300"}`,
70
+ title: m,
71
+ className: `${b} cursor-pointer transition-colors ${o ? "" : "hover:border-gray-300"}`,
72
72
  children: s
73
73
  }
74
- ) : /* @__PURE__ */ e("span", { className: i, children: s });
74
+ ) : /* @__PURE__ */ e("span", { className: b, children: s });
75
+ }
76
+ function re({
77
+ segments: r,
78
+ onNavigate: t,
79
+ maxVisible: n = 5,
80
+ className: a = ""
81
+ }) {
82
+ const [o, m] = k(!1), s = !o && r.length > n, b = s ? r.slice(0, 1) : [], p = s ? r.length - n : 0, u = s ? r.slice(1 + p) : r, h = s ? 1 + p : 0, d = (i, c) => c === r.length - 1 ? /* @__PURE__ */ e(
83
+ "span",
84
+ {
85
+ "aria-current": "page",
86
+ className: "px-1.5 py-0.5 font-medium text-gray-900 truncate max-w-[16rem]",
87
+ children: i.label
88
+ },
89
+ i.key
90
+ ) : /* @__PURE__ */ e(
91
+ "button",
92
+ {
93
+ type: "button",
94
+ onClick: () => t(c),
95
+ className: "px-1.5 py-0.5 rounded text-gray-500 hover:text-gray-900 hover:bg-gray-100 transition-colors cursor-pointer truncate max-w-[12rem]",
96
+ children: i.label
97
+ },
98
+ i.key
99
+ );
100
+ return /* @__PURE__ */ l(
101
+ "nav",
102
+ {
103
+ "aria-label": "Breadcrumb",
104
+ className: `flex items-center gap-0.5 text-sm min-w-0 overflow-hidden ${a}`,
105
+ children: [
106
+ b.map((i, c) => /* @__PURE__ */ l("span", { className: "flex items-center gap-0.5 min-w-0", children: [
107
+ d(i, c),
108
+ /* @__PURE__ */ e(w, { className: "w-3.5 h-3.5 text-gray-300 flex-shrink-0" })
109
+ ] }, i.key)),
110
+ s && /* @__PURE__ */ l("span", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
111
+ /* @__PURE__ */ e(
112
+ "button",
113
+ {
114
+ type: "button",
115
+ onClick: () => m(!0),
116
+ title: `Show ${p} hidden ${p === 1 ? "folder" : "folders"}`,
117
+ className: "px-1 py-0.5 rounded text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors cursor-pointer",
118
+ children: /* @__PURE__ */ e(R, { className: "w-4 h-4" })
119
+ }
120
+ ),
121
+ /* @__PURE__ */ e(w, { className: "w-3.5 h-3.5 text-gray-300" })
122
+ ] }),
123
+ u.map((i, c) => {
124
+ const g = h + c;
125
+ return /* @__PURE__ */ l("span", { className: "flex items-center gap-0.5 min-w-0", children: [
126
+ d(i, g),
127
+ g < r.length - 1 && /* @__PURE__ */ e(w, { className: "w-3.5 h-3.5 text-gray-300 flex-shrink-0" })
128
+ ] }, i.key);
129
+ })
130
+ ]
131
+ }
132
+ );
75
133
  }
76
- const _ = {
134
+ const P = {
77
135
  solid: {
78
136
  blue: "bg-blue-600 text-white hover:bg-blue-700",
79
137
  purple: "bg-purple-600 text-white hover:bg-purple-700",
@@ -98,129 +156,158 @@ const _ = {
98
156
  orange: "text-orange-600 hover:bg-orange-50",
99
157
  gray: "text-gray-600 hover:bg-gray-100"
100
158
  }
101
- }, B = {
159
+ }, U = {
102
160
  sm: "px-2.5 py-1.5 text-xs gap-1.5",
103
161
  md: "px-3 py-2 text-sm gap-2"
104
- }, M = {
162
+ }, X = {
105
163
  solid: "shadow-sm hover:shadow-md",
106
164
  soft: "shadow-sm hover:shadow",
107
165
  ghost: ""
108
166
  };
109
- function X({
110
- variant: t = "soft",
111
- tone: r = "blue",
167
+ function F({
168
+ variant: r = "soft",
169
+ tone: t = "blue",
112
170
  size: n = "md",
113
- icon: o,
114
- loading: a = !1,
115
- disabled: g = !1,
171
+ icon: a,
172
+ loading: o = !1,
173
+ disabled: m = !1,
116
174
  type: s = "button",
117
- title: i,
118
- onClick: u,
119
- to: c,
120
- href: m,
121
- target: f,
122
- className: d = "",
123
- children: b
175
+ title: b,
176
+ onClick: p,
177
+ to: u,
178
+ href: h,
179
+ target: d,
180
+ className: i = "",
181
+ children: c
124
182
  }) {
125
- const x = `inline-flex items-center justify-center font-medium rounded-lg transition-colors duration-100 cursor-pointer active:scale-[.98] ${B[n]} ${M[t]} ${_[t][r]} ${d}`, p = /* @__PURE__ */ l(y, { children: [
126
- a ? /* @__PURE__ */ e(E, { className: "w-4 h-4 animate-spin" }) : o,
127
- b
183
+ const g = `inline-flex items-center justify-center font-medium rounded-lg transition-colors duration-100 cursor-pointer active:scale-[.98] ${U[n]} ${X[r]} ${P[r][t]} ${i}`, x = /* @__PURE__ */ l(v, { children: [
184
+ o ? /* @__PURE__ */ e(j, { className: "w-4 h-4 animate-spin" }) : a,
185
+ c
128
186
  ] });
129
- return c ? /* @__PURE__ */ e(h, { to: c, className: x, title: i, children: p }) : m ? /* @__PURE__ */ e(
187
+ return u ? /* @__PURE__ */ e(f, { to: u, className: g, title: b, children: x }) : h ? /* @__PURE__ */ e(
130
188
  "a",
131
189
  {
132
- href: m,
133
- target: f,
134
- rel: f === "_blank" ? "noopener noreferrer" : void 0,
135
- className: x,
136
- title: i,
137
- children: p
190
+ href: h,
191
+ target: d,
192
+ rel: d === "_blank" ? "noopener noreferrer" : void 0,
193
+ className: g,
194
+ title: b,
195
+ children: x
138
196
  }
139
197
  ) : /* @__PURE__ */ e(
140
198
  "button",
141
199
  {
142
200
  type: s,
143
- onClick: u,
144
- disabled: g || a,
145
- title: i,
146
- className: `${x} disabled:opacity-50 disabled:cursor-not-allowed`,
147
- children: p
201
+ onClick: p,
202
+ disabled: m || o,
203
+ title: b,
204
+ className: `${g} disabled:opacity-50 disabled:cursor-not-allowed`,
205
+ children: x
148
206
  }
149
207
  );
150
208
  }
151
- function $({
152
- className: t = "",
153
- children: r
209
+ function T({
210
+ className: r = "",
211
+ children: t
154
212
  }) {
155
213
  return /* @__PURE__ */ e(
156
214
  "div",
157
215
  {
158
- className: `bg-white rounded-xl border border-gray-200/80 shadow-sm ${t}`,
159
- children: r
216
+ className: `bg-white rounded-xl border border-gray-200/80 shadow-sm ${r}`,
217
+ children: t
160
218
  }
161
219
  );
162
220
  }
163
- function Y({
164
- icon: t,
165
- title: r,
221
+ function te({
222
+ icon: r,
223
+ title: t,
166
224
  theme: n,
167
- count: o,
168
- actions: a,
169
- bodyClassName: g = "p-5",
225
+ count: a,
226
+ actions: o,
227
+ bodyClassName: m = "p-5",
170
228
  className: s = "",
171
- children: i
229
+ children: b
172
230
  }) {
173
- return /* @__PURE__ */ l($, { className: `overflow-hidden ${s}`, children: [
231
+ return /* @__PURE__ */ l(T, { className: `overflow-hidden ${s}`, children: [
174
232
  /* @__PURE__ */ l(
175
233
  "div",
176
234
  {
177
235
  className: `px-4 py-3 flex items-center justify-between border-b border-black/5 ${n.header}`,
178
236
  children: [
179
237
  /* @__PURE__ */ l("h4", { className: "text-sm font-semibold flex items-center", children: [
180
- t && /* @__PURE__ */ e(t, { className: "w-4 h-4 mr-2" }),
181
- r
238
+ r && /* @__PURE__ */ e(r, { className: "w-4 h-4 mr-2" }),
239
+ t
182
240
  ] }),
183
- (a || o !== void 0) && /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
184
- a,
185
- o !== void 0 && /* @__PURE__ */ e(
241
+ (o || a !== void 0) && /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
242
+ o,
243
+ a !== void 0 && /* @__PURE__ */ e(
186
244
  "span",
187
245
  {
188
246
  className: `text-xs font-semibold px-2 py-0.5 rounded-full ${n.badge}`,
189
- children: o
247
+ children: a
190
248
  }
191
249
  )
192
250
  ] })
193
251
  ]
194
252
  }
195
253
  ),
196
- /* @__PURE__ */ e("div", { className: g, children: i })
254
+ /* @__PURE__ */ e("div", { className: m, children: b })
197
255
  ] });
198
256
  }
199
- function z({
200
- icon: t,
201
- title: r,
257
+ const H = {
258
+ red: { accent: "border-l-red-500", chip: "bg-red-50 text-red-600" },
259
+ amber: { accent: "border-l-amber-500", chip: "bg-amber-50 text-amber-700" }
260
+ };
261
+ function ne({
262
+ tone: r = "red",
263
+ title: t,
264
+ severity: n,
265
+ action: a,
266
+ children: o
267
+ }) {
268
+ const { accent: m, chip: s } = H[r];
269
+ return /* @__PURE__ */ e(T, { className: `border-l-4 ${m} px-5 py-4`, children: /* @__PURE__ */ l("div", { className: "flex items-start justify-between gap-4", children: [
270
+ /* @__PURE__ */ l("div", { className: "min-w-0 flex-1", children: [
271
+ /* @__PURE__ */ l("div", { className: "flex items-center gap-2.5 flex-wrap", children: [
272
+ /* @__PURE__ */ e("h4", { className: "text-base font-semibold text-gray-900", children: t }),
273
+ n && /* @__PURE__ */ e(
274
+ "span",
275
+ {
276
+ className: `text-[11px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded ${s}`,
277
+ children: n
278
+ }
279
+ )
280
+ ] }),
281
+ /* @__PURE__ */ e("div", { className: "mt-1 text-sm text-gray-600", children: o })
282
+ ] }),
283
+ a && /* @__PURE__ */ e("div", { className: "shrink-0 text-sm", children: a })
284
+ ] }) });
285
+ }
286
+ function ae({
287
+ icon: r,
288
+ title: t,
202
289
  count: n,
203
- theme: o,
204
- actions: a,
205
- footer: g,
290
+ theme: a,
291
+ actions: o,
292
+ footer: m,
206
293
  children: s
207
294
  }) {
208
- return /* @__PURE__ */ l($, { className: "overflow-hidden flex flex-col", children: [
295
+ return /* @__PURE__ */ l(T, { className: "overflow-hidden flex flex-col", children: [
209
296
  /* @__PURE__ */ l(
210
297
  "div",
211
298
  {
212
- className: `px-4 py-3 flex items-center justify-between border-b border-black/5 ${o.header}`,
299
+ className: `px-4 py-3 flex items-center justify-between border-b border-black/5 ${a.header}`,
213
300
  children: [
214
301
  /* @__PURE__ */ l("h4", { className: "text-sm font-semibold flex items-center", children: [
215
- t && /* @__PURE__ */ e(t, { className: "w-4 h-4 mr-2" }),
216
- r
302
+ r && /* @__PURE__ */ e(r, { className: "w-4 h-4 mr-2" }),
303
+ t
217
304
  ] }),
218
305
  /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
219
- a,
306
+ o,
220
307
  n !== void 0 && /* @__PURE__ */ e(
221
308
  "span",
222
309
  {
223
- className: `text-xs font-semibold px-2 py-0.5 rounded-full ${o.badge}`,
310
+ className: `text-xs font-semibold px-2 py-0.5 rounded-full ${a.badge}`,
224
311
  children: n
225
312
  }
226
313
  )
@@ -229,40 +316,40 @@ function z({
229
316
  }
230
317
  ),
231
318
  /* @__PURE__ */ e("div", { className: "divide-y divide-gray-100", children: s }),
232
- g
319
+ m
233
320
  ] });
234
321
  }
235
- function G({
236
- to: t,
237
- onClick: r,
322
+ function oe({
323
+ to: r,
324
+ onClick: t,
238
325
  hover: n = "hover:bg-gray-50",
239
- className: o = "",
240
- children: a
326
+ className: a = "",
327
+ children: o
241
328
  }) {
242
- const s = `flex items-center justify-between px-4 py-3 transition-colors ${n} ${!!(t || r) ? "cursor-pointer" : ""} ${o}`;
243
- return t ? /* @__PURE__ */ e(h, { to: t, className: s, children: a }) : r ? /* @__PURE__ */ e(
329
+ const s = `flex items-center justify-between px-4 py-3 transition-colors ${n} ${!!(r || t) ? "cursor-pointer" : ""} ${a}`;
330
+ return r ? /* @__PURE__ */ e(f, { to: r, className: s, children: o }) : t ? /* @__PURE__ */ e(
244
331
  "button",
245
332
  {
246
333
  type: "button",
247
- onClick: r,
334
+ onClick: t,
248
335
  className: `w-full text-left ${s}`,
249
- children: a
336
+ children: o
250
337
  }
251
- ) : /* @__PURE__ */ e("div", { className: s, children: a });
338
+ ) : /* @__PURE__ */ e("div", { className: s, children: o });
252
339
  }
253
- function H({
254
- to: t,
255
- count: r,
340
+ function se({
341
+ to: r,
342
+ count: t,
256
343
  noun: n = "items"
257
344
  }) {
258
345
  return /* @__PURE__ */ l(
259
- h,
346
+ f,
260
347
  {
261
- to: t,
348
+ to: r,
262
349
  className: "block px-4 py-2.5 text-sm font-medium text-blue-600 hover:text-blue-700 hover:bg-gray-50 transition-colors",
263
350
  children: [
264
351
  "View all ",
265
- r,
352
+ t,
266
353
  " ",
267
354
  n,
268
355
  " →"
@@ -270,16 +357,16 @@ function H({
270
357
  }
271
358
  );
272
359
  }
273
- const O = (t) => `https://flagicons.lipis.dev/flags/4x3/${t.toLowerCase()}.svg`;
274
- function Z({
275
- countryCode: t,
276
- country: r
360
+ const V = (r) => `https://flagicons.lipis.dev/flags/4x3/${r.toLowerCase()}.svg`;
361
+ function le({
362
+ countryCode: r,
363
+ country: t
277
364
  }) {
278
- return t ? /* @__PURE__ */ e(
365
+ return r ? /* @__PURE__ */ e(
279
366
  "img",
280
367
  {
281
- src: O(t),
282
- alt: r || "Country flag",
368
+ src: V(r),
369
+ alt: t || "Country flag",
283
370
  className: "w-4 h-3 flex-shrink-0 rounded-sm ring-1 ring-black/5",
284
371
  onError: (n) => {
285
372
  n.target.style.display = "none";
@@ -287,107 +374,211 @@ function Z({
287
374
  }
288
375
  ) : null;
289
376
  }
290
- function q({
291
- to: t,
292
- onClick: r,
377
+ function ie({
378
+ to: r,
379
+ onClick: t,
293
380
  children: n
294
381
  }) {
295
- const o = "inline-flex items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer", a = /* @__PURE__ */ l(y, { children: [
296
- /* @__PURE__ */ e(S, { className: "w-4 h-4" }),
382
+ const a = "inline-flex items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer", o = /* @__PURE__ */ l(v, { children: [
383
+ /* @__PURE__ */ e(D, { className: "w-4 h-4" }),
297
384
  /* @__PURE__ */ e("span", { children: n })
298
385
  ] });
299
- return t ? /* @__PURE__ */ e(h, { to: t, className: o, children: a }) : /* @__PURE__ */ e("button", { type: "button", onClick: r, className: o, children: a });
386
+ return r ? /* @__PURE__ */ e(f, { to: r, className: a, children: o }) : /* @__PURE__ */ e("button", { type: "button", onClick: t, className: a, children: o });
300
387
  }
301
- function J({ items: t }) {
302
- return /* @__PURE__ */ e("div", { className: "border-b border-gray-200", children: /* @__PURE__ */ e("nav", { className: "-mb-px flex space-x-8", children: t.map((r) => {
303
- const n = r.active ? "py-2 px-1 border-b-2 border-blue-500 text-blue-600 font-medium text-sm" : "py-2 px-1 border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm transition-colors cursor-pointer";
304
- return r.to && !r.active ? /* @__PURE__ */ e(
305
- h,
306
- {
307
- to: r.to,
308
- state: r.state,
309
- className: n,
310
- children: r.label
311
- },
312
- r.label
313
- ) : /* @__PURE__ */ e("span", { className: n, children: r.label }, r.label);
314
- }) }) });
388
+ function de({
389
+ items: r,
390
+ actions: t
391
+ }) {
392
+ return /* @__PURE__ */ l("div", { className: "border-b border-gray-200 flex items-end justify-between gap-4", children: [
393
+ /* @__PURE__ */ e("nav", { className: "-mb-px flex space-x-8", children: r.map((n) => {
394
+ const a = n.active ? "py-2 px-1 border-b-2 border-blue-500 text-blue-600 font-medium text-sm" : "py-2 px-1 border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm transition-colors cursor-pointer";
395
+ return n.to && !n.active ? /* @__PURE__ */ e(
396
+ f,
397
+ {
398
+ to: n.to,
399
+ state: n.state,
400
+ className: a,
401
+ children: n.label
402
+ },
403
+ n.label
404
+ ) : /* @__PURE__ */ e("span", { className: a, children: n.label }, n.label);
405
+ }) }),
406
+ t && /* @__PURE__ */ e("div", { className: "flex items-center gap-2 pb-1.5", children: t })
407
+ ] });
315
408
  }
316
- function Q({ items: t }) {
317
- return /* @__PURE__ */ e("nav", { className: "space-y-1", children: t.map((r) => {
318
- const n = r.icon, o = `group/side relative flex items-center gap-3 h-10 rounded-md px-3 text-sm transition-all duration-200 cursor-pointer ${r.active ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}`, a = /* @__PURE__ */ l(y, { children: [
409
+ function ce({ items: r }) {
410
+ return /* @__PURE__ */ e("nav", { className: "space-y-1", children: r.map((t) => {
411
+ const n = t.icon, a = `group/side relative flex items-center gap-3 h-10 rounded-md px-3 text-sm transition-all duration-200 cursor-pointer ${t.active ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}`, o = /* @__PURE__ */ l(v, { children: [
319
412
  /* @__PURE__ */ e(
320
413
  "span",
321
414
  {
322
- className: `absolute left-0 top-1.5 bottom-1.5 w-[3px] rounded-r-full bg-blue-600 transition-all duration-300 ease-out ${r.active ? "opacity-100 scale-y-100" : "opacity-0 scale-y-0"}`
415
+ className: `absolute left-0 top-1.5 bottom-1.5 w-[3px] rounded-r-full bg-blue-600 transition-all duration-300 ease-out ${t.active ? "opacity-100 scale-y-100" : "opacity-0 scale-y-0"}`
323
416
  }
324
417
  ),
325
418
  n && /* @__PURE__ */ e(n, { className: "w-[18px] h-[18px] shrink-0 transition-transform duration-200 group-hover/side:scale-110" }),
326
- /* @__PURE__ */ e("span", { className: "truncate", children: r.label })
419
+ /* @__PURE__ */ e("span", { className: "truncate", children: t.label })
327
420
  ] });
328
- return r.active ? /* @__PURE__ */ e("span", { className: o, "aria-current": "page", children: a }, r.to) : /* @__PURE__ */ e(h, { to: r.to, className: o, children: a }, r.to);
421
+ return t.active ? /* @__PURE__ */ e("span", { className: a, "aria-current": "page", children: o }, t.to) : /* @__PURE__ */ e(f, { to: t.to, className: a, children: o }, t.to);
329
422
  }) });
330
423
  }
331
- function W({
332
- label: t,
333
- icon: r,
424
+ function ge({
425
+ label: r,
426
+ icon: t,
334
427
  iconClassName: n = "text-gray-400",
335
- children: o
428
+ children: a
336
429
  }) {
337
430
  return /* @__PURE__ */ l("div", { className: "flex items-center justify-between gap-4", children: [
338
431
  /* @__PURE__ */ l("span", { className: "text-gray-700 flex items-center flex-shrink-0", children: [
339
- r && /* @__PURE__ */ e(r, { className: `w-4 h-4 mr-2 ${n}` }),
340
- t
432
+ t && /* @__PURE__ */ e(t, { className: `w-4 h-4 mr-2 ${n}` }),
433
+ r
341
434
  ] }),
342
- /* @__PURE__ */ e("span", { className: "text-sm text-gray-900 text-right min-w-0", children: o })
435
+ /* @__PURE__ */ e("span", { className: "text-sm text-gray-900 text-right min-w-0", children: a })
343
436
  ] });
344
437
  }
345
- function ee({
346
- className: t = "space-y-6",
347
- children: r
438
+ const Y = {
439
+ blue: "text-blue-600",
440
+ purple: "text-purple-600",
441
+ red: "text-red-600",
442
+ green: "text-green-600",
443
+ orange: "text-orange-600",
444
+ gray: "text-gray-500"
445
+ }, S = "w-full flex items-start gap-3 px-3 py-2 text-left transition-colors hover:bg-gray-50 cursor-pointer";
446
+ function be({
447
+ label: r,
448
+ items: t,
449
+ icon: n,
450
+ variant: a = "soft",
451
+ tone: o = "gray",
452
+ size: m = "md",
453
+ align: s = "right",
454
+ className: b = ""
455
+ }) {
456
+ const [p, u] = k(!1), h = A(null);
457
+ return O(() => {
458
+ if (!p) return;
459
+ const d = (c) => {
460
+ var g;
461
+ (g = h.current) != null && g.contains(c.target) || u(!1);
462
+ }, i = (c) => {
463
+ c.key === "Escape" && u(!1);
464
+ };
465
+ return document.addEventListener("mousedown", d), document.addEventListener("keydown", i), () => {
466
+ document.removeEventListener("mousedown", d), document.removeEventListener("keydown", i);
467
+ };
468
+ }, [p]), /* @__PURE__ */ l("div", { className: `relative ${b}`, ref: h, children: [
469
+ /* @__PURE__ */ l(
470
+ F,
471
+ {
472
+ variant: a,
473
+ tone: o,
474
+ size: m,
475
+ icon: n,
476
+ onClick: () => u((d) => !d),
477
+ children: [
478
+ r,
479
+ /* @__PURE__ */ e(
480
+ B,
481
+ {
482
+ className: `w-4 h-4 transition-transform duration-200 ${p ? "rotate-180" : ""}`
483
+ }
484
+ )
485
+ ]
486
+ }
487
+ ),
488
+ p && /* @__PURE__ */ e(
489
+ "div",
490
+ {
491
+ className: `absolute ${s === "right" ? "right-0" : "left-0"} mt-2 w-64 py-1 bg-white rounded-lg shadow-lg border border-gray-200 z-50 animate-dropdown-in`,
492
+ children: t.map((d, i) => {
493
+ const c = Y[d.tone ?? "gray"], g = /* @__PURE__ */ l(v, { children: [
494
+ d.icon && /* @__PURE__ */ e(
495
+ "span",
496
+ {
497
+ className: `flex items-center gap-1 mt-0.5 flex-shrink-0 ${c}`,
498
+ children: d.icon
499
+ }
500
+ ),
501
+ /* @__PURE__ */ l("span", { className: "min-w-0", children: [
502
+ /* @__PURE__ */ e("span", { className: "block text-sm font-medium text-gray-900", children: d.label }),
503
+ d.description && /* @__PURE__ */ e("span", { className: "block text-xs text-gray-500 mt-0.5 break-all", children: d.description })
504
+ ] })
505
+ ] });
506
+ return d.href ? /* @__PURE__ */ e(
507
+ "a",
508
+ {
509
+ href: d.href,
510
+ target: d.target,
511
+ rel: d.target === "_blank" ? "noopener noreferrer" : void 0,
512
+ className: S,
513
+ onClick: () => u(!1),
514
+ children: g
515
+ },
516
+ i
517
+ ) : /* @__PURE__ */ e(
518
+ "button",
519
+ {
520
+ type: "button",
521
+ disabled: d.disabled,
522
+ onClick: () => {
523
+ var x;
524
+ d.keepOpen || u(!1), (x = d.onClick) == null || x.call(d);
525
+ },
526
+ className: `${S} disabled:opacity-50 disabled:cursor-not-allowed`,
527
+ children: g
528
+ },
529
+ i
530
+ );
531
+ })
532
+ }
533
+ )
534
+ ] });
535
+ }
536
+ function ue({
537
+ className: r = "space-y-6",
538
+ children: t
348
539
  }) {
349
540
  return /* @__PURE__ */ e(
350
541
  "div",
351
542
  {
352
- className: `flex-1 overflow-y-auto px-4 sm:px-6 lg:px-8 py-6 ${t}`,
353
- children: r
543
+ className: `flex-1 overflow-y-auto px-4 sm:px-6 lg:px-8 py-6 ${r}`,
544
+ children: t
354
545
  }
355
546
  );
356
547
  }
357
- function re({
358
- value: t,
359
- onChange: r,
548
+ function me({
549
+ value: r,
550
+ onChange: t,
360
551
  placeholder: n = "Search...",
361
- className: o = "w-full",
362
- slashToFocus: a = !1
552
+ className: a = "w-full",
553
+ slashToFocus: o = !1
363
554
  }) {
364
- const g = j(null);
365
- return A(() => {
366
- if (!a) return;
367
- function s(i) {
368
- var c;
369
- if (i.key !== "/" || i.ctrlKey || i.metaKey || i.altKey) return;
370
- const u = i.target;
371
- u.tagName === "INPUT" || u.tagName === "TEXTAREA" || u.tagName === "SELECT" || u.isContentEditable || (i.preventDefault(), (c = g.current) == null || c.focus());
555
+ const m = A(null);
556
+ return O(() => {
557
+ if (!o) return;
558
+ function s(b) {
559
+ var u;
560
+ if (b.key !== "/" || b.ctrlKey || b.metaKey || b.altKey) return;
561
+ const p = b.target;
562
+ p.tagName === "INPUT" || p.tagName === "TEXTAREA" || p.tagName === "SELECT" || p.isContentEditable || (b.preventDefault(), (u = m.current) == null || u.focus());
372
563
  }
373
564
  return document.addEventListener("keydown", s), () => document.removeEventListener("keydown", s);
374
- }, [a]), /* @__PURE__ */ l("div", { className: `relative ${o}`, children: [
375
- /* @__PURE__ */ e(L, { className: "absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-4 h-4 pointer-events-none" }),
565
+ }, [o]), /* @__PURE__ */ l("div", { className: `relative ${a}`, children: [
566
+ /* @__PURE__ */ e(I, { className: "absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-4 h-4 pointer-events-none" }),
376
567
  /* @__PURE__ */ e(
377
568
  "input",
378
569
  {
379
- ref: g,
570
+ ref: m,
380
571
  type: "text",
381
- value: t,
382
- onChange: (s) => r(s.target.value),
572
+ value: r,
573
+ onChange: (s) => t(s.target.value),
383
574
  placeholder: n,
384
575
  className: "w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 placeholder-gray-400"
385
576
  }
386
577
  ),
387
- a && !t && /* @__PURE__ */ e("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 text-xs border border-gray-300 rounded px-1 py-0.5 font-mono leading-none pointer-events-none", children: "/" })
578
+ o && !r && /* @__PURE__ */ e("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 text-xs border border-gray-300 rounded px-1 py-0.5 font-mono leading-none pointer-events-none", children: "/" })
388
579
  ] });
389
580
  }
390
- const D = {
581
+ const G = {
391
582
  neutral: { badge: "bg-gray-100", icon: "text-gray-400" },
392
583
  green: { badge: "bg-green-100", icon: "text-green-600" },
393
584
  blue: { badge: "bg-blue-100", icon: "text-blue-600" },
@@ -395,87 +586,148 @@ const D = {
395
586
  red: { badge: "bg-red-100", icon: "text-red-600" },
396
587
  purple: { badge: "bg-purple-100", icon: "text-purple-600" }
397
588
  };
398
- function te({
399
- icon: t,
400
- label: r,
589
+ function pe({
590
+ icon: r,
591
+ label: t,
401
592
  value: n,
402
- tone: o = "neutral"
593
+ tone: a = "neutral"
403
594
  }) {
404
- const a = D[o];
595
+ const o = G[a];
405
596
  return /* @__PURE__ */ l("div", { className: "flex items-center gap-4", children: [
406
597
  /* @__PURE__ */ e(
407
598
  "div",
408
599
  {
409
- className: `flex h-12 w-12 items-center justify-center rounded-xl ${a.badge}`,
410
- children: /* @__PURE__ */ e(t, { className: `w-6 h-6 ${a.icon}` })
600
+ className: `flex h-12 w-12 items-center justify-center rounded-xl ${o.badge}`,
601
+ children: /* @__PURE__ */ e(r, { className: `w-6 h-6 ${o.icon}` })
411
602
  }
412
603
  ),
413
604
  /* @__PURE__ */ l("div", { children: [
414
- /* @__PURE__ */ e("p", { className: "text-sm font-medium text-gray-500", children: r }),
605
+ /* @__PURE__ */ e("p", { className: "text-sm font-medium text-gray-500", children: t }),
415
606
  /* @__PURE__ */ e("p", { className: "text-3xl font-bold text-gray-900 tabular-nums", children: n })
416
607
  ] })
417
608
  ] });
418
609
  }
419
- const N = {
610
+ const M = {
420
611
  ok: "bg-green-500",
421
612
  down: "bg-red-500",
422
613
  unknown: "bg-gray-300"
423
- }, w = (t) => `${(t * 100).toFixed(4)}%`;
424
- function ne({
425
- from: t,
426
- to: r,
614
+ }, _ = (r) => `${(r * 100).toFixed(4)}%`;
615
+ function he({
616
+ from: r,
617
+ to: t,
618
+ spans: n,
619
+ buckets: a = 48,
620
+ height: o = "1.75rem",
621
+ renderTooltip: m
622
+ }) {
623
+ const [s, b] = k(null), p = r.getTime(), u = t.getTime() - p;
624
+ if (!(u > 0)) return null;
625
+ const h = u / a, d = Array.from({ length: a }, (c, g) => {
626
+ const x = p + g * h, N = x + h;
627
+ let y = 0;
628
+ for (const E of n) {
629
+ const L = Math.max(E.start.getTime(), x), C = Math.min(E.end.getTime(), N);
630
+ C > L && (y += C - L);
631
+ }
632
+ return {
633
+ start: new Date(x),
634
+ end: new Date(N),
635
+ downMs: y,
636
+ ratio: 1 - y / h
637
+ };
638
+ });
639
+ return /* @__PURE__ */ l("div", { className: "relative", children: [
640
+ s !== null && m && /* @__PURE__ */ e(
641
+ "div",
642
+ {
643
+ className: "pointer-events-none absolute bottom-full z-10 mb-2 whitespace-nowrap rounded-md bg-gray-900 px-2 py-1.5 text-[11px] leading-tight text-white shadow-lg",
644
+ style: ((c) => {
645
+ const g = (c + 0.5) / a;
646
+ return g < 0.15 ? { left: 0 } : g > 0.85 ? { right: 0 } : { left: `${g * 100}%`, transform: "translateX(-50%)" };
647
+ })(s),
648
+ children: m(d[s])
649
+ }
650
+ ),
651
+ /* @__PURE__ */ e(
652
+ "div",
653
+ {
654
+ className: "flex items-stretch gap-[2px]",
655
+ style: { height: o },
656
+ onMouseLeave: () => b(null),
657
+ children: d.map((c, g) => /* @__PURE__ */ e(
658
+ "div",
659
+ {
660
+ onMouseEnter: () => b(g),
661
+ className: `relative min-w-[2px] flex-1 cursor-pointer overflow-hidden rounded-[2px] bg-emerald-500 transition-opacity ${s === g ? "opacity-60" : ""}`,
662
+ children: c.downMs > 0 && /* @__PURE__ */ e(
663
+ "div",
664
+ {
665
+ className: "absolute inset-x-0 bottom-0 bg-red-500",
666
+ style: { height: `max(3px, ${(1 - c.ratio) * 100}%)` }
667
+ }
668
+ )
669
+ },
670
+ c.start.getTime()
671
+ ))
672
+ }
673
+ )
674
+ ] });
675
+ }
676
+ function xe({
677
+ from: r,
678
+ to: t,
427
679
  lanes: n,
428
- baseline: o = "ok",
429
- ticks: a = 5,
430
- formatTick: g = (c) => c.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
680
+ baseline: a = "ok",
681
+ ticks: o = 5,
682
+ formatTick: m = (u) => u.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
431
683
  labelWidth: s = "8rem",
432
- rowHeight: i = "1.25rem",
433
- emptyLabel: u = "No data"
684
+ rowHeight: b = "1.25rem",
685
+ emptyLabel: p = "No data"
434
686
  }) {
435
- const c = t.getTime(), m = r.getTime() - c;
436
- if (!(m > 0) || n.length === 0)
437
- return /* @__PURE__ */ e("div", { className: "py-8 text-center text-sm text-gray-500", children: u });
438
- const f = Array.from(
439
- { length: Math.max(2, a) },
440
- (d, b) => new Date(c + m * b / (Math.max(2, a) - 1))
687
+ const u = r.getTime(), h = t.getTime() - u;
688
+ if (!(h > 0) || n.length === 0)
689
+ return /* @__PURE__ */ e("div", { className: "py-8 text-center text-sm text-gray-500", children: p });
690
+ const d = Array.from(
691
+ { length: Math.max(2, o) },
692
+ (i, c) => new Date(u + h * c / (Math.max(2, o) - 1))
441
693
  );
442
694
  return /* @__PURE__ */ l("div", { className: "space-y-1.5", children: [
443
- n.map((d) => /* @__PURE__ */ l("div", { className: "flex items-center gap-3", children: [
695
+ n.map((i) => /* @__PURE__ */ l("div", { className: "flex items-center gap-3", children: [
444
696
  /* @__PURE__ */ e(
445
697
  "div",
446
698
  {
447
699
  className: "shrink-0 truncate font-mono text-xs text-gray-600",
448
700
  style: { width: s },
449
- title: d.name,
450
- children: d.label ?? d.name
701
+ title: i.name,
702
+ children: i.label ?? i.name
451
703
  }
452
704
  ),
453
705
  /* @__PURE__ */ e(
454
706
  "div",
455
707
  {
456
- className: `relative flex-1 overflow-hidden rounded-sm ${N[o]}`,
457
- style: { height: i },
458
- children: d.spans.map((b) => {
459
- const x = Math.max(b.start.getTime(), c), p = Math.min(b.end.getTime(), c + m);
460
- if (p <= x) return null;
461
- const T = (x - c) / m, k = (p - x) / m;
708
+ className: `relative flex-1 overflow-hidden rounded-sm ${M[a]}`,
709
+ style: { height: b },
710
+ children: i.spans.map((c) => {
711
+ const g = Math.max(c.start.getTime(), u), x = Math.min(c.end.getTime(), u + h);
712
+ if (x <= g) return null;
713
+ const N = (g - u) / h, y = (x - g) / h;
462
714
  return /* @__PURE__ */ e(
463
715
  "div",
464
716
  {
465
- className: `absolute inset-y-0 ${N[b.tone]}`,
717
+ className: `absolute inset-y-0 ${M[c.tone]}`,
466
718
  style: {
467
- left: w(T),
468
- width: `max(2px, ${w(k)})`
719
+ left: _(N),
720
+ width: `max(2px, ${_(y)})`
469
721
  },
470
- title: b.label
722
+ title: c.label
471
723
  },
472
- `${b.start.getTime()}-${b.end.getTime()}`
724
+ `${c.start.getTime()}-${c.end.getTime()}`
473
725
  );
474
726
  })
475
727
  }
476
728
  ),
477
- d.trailing !== void 0 && /* @__PURE__ */ e("div", { className: "w-16 shrink-0 text-right font-mono text-xs tabular-nums text-gray-600", children: d.trailing })
478
- ] }, d.name)),
729
+ i.trailing !== void 0 && /* @__PURE__ */ e("div", { className: "w-16 shrink-0 text-right font-mono text-xs tabular-nums text-gray-600", children: i.trailing })
730
+ ] }, i.name)),
479
731
  /* @__PURE__ */ e(
480
732
  "div",
481
733
  {
@@ -483,14 +735,14 @@ function ne({
483
735
  style: {
484
736
  marginLeft: s,
485
737
  paddingLeft: "0.75rem",
486
- marginRight: n.some((d) => d.trailing !== void 0) ? "4rem" : void 0
738
+ marginRight: n.some((i) => i.trailing !== void 0) ? "4rem" : void 0
487
739
  },
488
- children: f.map((d) => /* @__PURE__ */ e("span", { children: g(d) }, d.getTime()))
740
+ children: d.map((i) => /* @__PURE__ */ e("span", { children: m(i) }, i.getTime()))
489
741
  }
490
742
  )
491
743
  ] });
492
744
  }
493
- const ae = {
745
+ const fe = {
494
746
  blue: {
495
747
  header: "bg-blue-50 text-blue-800",
496
748
  hover: "hover:bg-blue-50",
@@ -532,24 +784,24 @@ const ae = {
532
784
  badge: "bg-gray-200 text-gray-700"
533
785
  }
534
786
  };
535
- function oe({
536
- toast: t,
537
- onClose: r
787
+ function ye({
788
+ toast: r,
789
+ onClose: t
538
790
  }) {
539
- return t ? /* @__PURE__ */ e(
791
+ return r ? /* @__PURE__ */ e(
540
792
  "div",
541
793
  {
542
- className: `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg border transition-all duration-300 ease-in-out ${t.type === "success" ? "bg-green-50 text-green-800 border-green-200" : "bg-red-50 text-red-800 border-red-200"}`,
794
+ className: `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg border transition-all duration-300 ease-in-out ${r.type === "success" ? "bg-green-50 text-green-800 border-green-200" : "bg-red-50 text-red-800 border-red-200"}`,
543
795
  children: /* @__PURE__ */ l("div", { className: "flex items-center space-x-2", children: [
544
- t.type === "success" ? /* @__PURE__ */ e(C, { className: "w-5 h-5 text-green-600" }) : /* @__PURE__ */ e(v, { className: "w-5 h-5 text-red-600" }),
545
- /* @__PURE__ */ e("span", { className: "text-sm font-medium", children: t.message }),
796
+ r.type === "success" ? /* @__PURE__ */ e(K, { className: "w-5 h-5 text-green-600" }) : /* @__PURE__ */ e($, { className: "w-5 h-5 text-red-600" }),
797
+ /* @__PURE__ */ e("span", { className: "text-sm font-medium", children: r.message }),
546
798
  /* @__PURE__ */ e(
547
799
  "button",
548
800
  {
549
801
  type: "button",
550
- onClick: r,
802
+ onClick: t,
551
803
  className: "ml-2 text-gray-400 hover:text-gray-600 cursor-pointer",
552
- children: /* @__PURE__ */ e(v, { className: "w-4 h-4" })
804
+ children: /* @__PURE__ */ e($, { className: "w-4 h-4" })
553
805
  }
554
806
  )
555
807
  ] })
@@ -557,24 +809,28 @@ function oe({
557
809
  ) : null;
558
810
  }
559
811
  export {
560
- R as BADGE_COLORS,
561
- q as BackLink,
562
- U as Badge,
563
- X as Button,
564
- $ as Card,
565
- Z as CountryFlag,
566
- W as InfoRow,
567
- V as LabelChip,
568
- G as ListRow,
569
- ee as PageContainer,
570
- Y as Panel,
571
- ae as SECTION_THEMES,
572
- re as SearchInput,
573
- z as SectionCard,
574
- Q as SideNav,
575
- te as StatCard,
576
- ne as StateTimeline,
577
- J as TabNav,
578
- oe as Toast,
579
- H as ViewAllFooter
812
+ ne as AlertBanner,
813
+ z as BADGE_COLORS,
814
+ ie as BackLink,
815
+ W as Badge,
816
+ re as Breadcrumbs,
817
+ F as Button,
818
+ T as Card,
819
+ le as CountryFlag,
820
+ be as DropdownMenu,
821
+ ge as InfoRow,
822
+ ee as LabelChip,
823
+ oe as ListRow,
824
+ ue as PageContainer,
825
+ te as Panel,
826
+ fe as SECTION_THEMES,
827
+ me as SearchInput,
828
+ ae as SectionCard,
829
+ ce as SideNav,
830
+ pe as StatCard,
831
+ xe as StateTimeline,
832
+ de as TabNav,
833
+ ye as Toast,
834
+ he as UptimeBars,
835
+ se as ViewAllFooter
580
836
  };
@@ -18,6 +18,33 @@ export interface TimelineLane {
18
18
  trailing?: ReactNode;
19
19
  }
20
20
  export type TimelineTone = "ok" | "down" | "unknown";
21
+ export interface UptimeBucket {
22
+ start: Date;
23
+ end: Date;
24
+ /** Downtime inside this bucket, in ms. */
25
+ downMs: number;
26
+ /** Fraction of the bucket spent up, 0..1. */
27
+ ratio: number;
28
+ }
29
+ /**
30
+ * Status-page style availability bars: the window is split into equal buckets,
31
+ * each drawn green with a red portion sized to the downtime inside it.
32
+ *
33
+ * The proportion is the point — colouring a whole bucket red for a 30-second
34
+ * blip makes a flapping device look permanently offline. A red sliver reads as
35
+ * "briefly down", a full red bar as "down the whole time", with no intermediate
36
+ * colour whose meaning has to be guessed.
37
+ */
38
+ export declare function UptimeBars({ from, to, spans, buckets, height, renderTooltip, }: {
39
+ from: Date;
40
+ to: Date;
41
+ /** Down intervals in absolute time; clipped to the window. */
42
+ spans: TimelineSpan[];
43
+ buckets?: number;
44
+ height?: string;
45
+ /** Hover card contents for a bucket. No tooltip is shown when omitted. */
46
+ renderTooltip?: (bucket: UptimeBucket) => ReactNode;
47
+ }): import("react/jsx-runtime").JSX.Element | null;
21
48
  /**
22
49
  * Horizontal state timeline: one lane per series, spans drawn as coloured bands
23
50
  * against a baseline.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teton/smith-ui",
3
- "version": "0.2.180",
3
+ "version": "0.2.182",
4
4
  "description": "Smith dashboard design-system primitives (React + Tailwind CSS v4).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {