@oh-my-pi/omp-stats 18.3.4 → 18.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +13 -11
  3. package/build.ts +5 -49
  4. package/dist/client/index.css +1 -1
  5. package/dist/client/index.html +2 -2
  6. package/dist/client/index.js +105 -126
  7. package/dist/types/aggregator.d.ts +25 -22
  8. package/dist/types/client/api.d.ts +11 -7
  9. package/dist/types/client/app/LiveChip.d.ts +2 -0
  10. package/dist/types/client/app/Shell.d.ts +16 -0
  11. package/dist/types/client/app/ThemeToggle.d.ts +1 -0
  12. package/dist/types/client/app/nav.d.ts +18 -0
  13. package/dist/types/client/charts/BarList.d.ts +17 -0
  14. package/dist/types/client/charts/Chart.d.ts +49 -0
  15. package/dist/types/client/charts/Legend.d.ts +16 -0
  16. package/dist/types/client/charts/ShareBar.d.ts +11 -0
  17. package/dist/types/client/charts/Sparkline.d.ts +11 -0
  18. package/dist/types/client/charts/TimeChart.d.ts +9 -0
  19. package/dist/types/client/charts/index.d.ts +7 -0
  20. package/dist/types/client/charts/types.d.ts +25 -0
  21. package/dist/types/client/charts/useWidth.d.ts +3 -0
  22. package/dist/types/client/data/colors.d.ts +25 -0
  23. package/dist/types/client/data/formatters.d.ts +9 -1
  24. package/dist/types/client/data/live.d.ts +29 -0
  25. package/dist/types/client/data/query.d.ts +35 -0
  26. package/dist/types/client/data/range.d.ts +31 -0
  27. package/dist/types/client/data/series.d.ts +28 -0
  28. package/dist/types/client/data/useHashRoute.d.ts +18 -4
  29. package/dist/types/client/data/view-models.d.ts +111 -28
  30. package/dist/types/client/index.d.ts +0 -1
  31. package/dist/types/client/routes/CostsRoute.d.ts +2 -2
  32. package/dist/types/client/routes/ErrorsRoute.d.ts +2 -2
  33. package/dist/types/client/routes/FrustrationRoute.d.ts +7 -0
  34. package/dist/types/client/routes/GainRoute.d.ts +1 -2
  35. package/dist/types/client/routes/ModelsRoute.d.ts +2 -2
  36. package/dist/types/client/routes/OverviewRoute.d.ts +1 -2
  37. package/dist/types/client/routes/ProjectsRoute.d.ts +2 -2
  38. package/dist/types/client/routes/ProvidersRoute.d.ts +2 -2
  39. package/dist/types/client/routes/RequestsRoute.d.ts +1 -2
  40. package/dist/types/client/routes/ToolsRoute.d.ts +2 -2
  41. package/dist/types/client/routes/TracesRoute.d.ts +2 -2
  42. package/dist/types/client/routes/index.d.ts +1 -1
  43. package/dist/types/client/traces/AggregatesPanel.d.ts +4 -3
  44. package/dist/types/client/traces/SummaryStrip.d.ts +2 -2
  45. package/dist/types/client/traces/trace-colors.d.ts +18 -12
  46. package/dist/types/client/ui/Badge.d.ts +19 -0
  47. package/dist/types/client/ui/Card.d.ts +25 -0
  48. package/dist/types/client/ui/Drawer.d.ts +25 -0
  49. package/dist/types/client/ui/JsonBlock.d.ts +2 -2
  50. package/dist/types/client/ui/Modal.d.ts +23 -0
  51. package/dist/types/client/ui/RequestDrawer.d.ts +10 -1
  52. package/dist/types/client/ui/SearchInput.d.ts +8 -0
  53. package/dist/types/client/ui/Segmented.d.ts +15 -0
  54. package/dist/types/client/ui/Stat.d.ts +36 -0
  55. package/dist/types/client/ui/States.d.ts +41 -0
  56. package/dist/types/client/ui/Table.d.ts +48 -0
  57. package/dist/types/client/ui/index.d.ts +9 -10
  58. package/dist/types/db.d.ts +90 -106
  59. package/dist/types/frustration.d.ts +70 -0
  60. package/dist/types/index.d.ts +3 -1
  61. package/dist/types/live.d.ts +30 -0
  62. package/dist/types/rollup.d.ts +115 -0
  63. package/dist/types/server.d.ts +6 -1
  64. package/dist/types/shared-types.d.ts +116 -56
  65. package/dist/types/types.d.ts +4 -0
  66. package/dist/types/user-metrics.d.ts +8 -0
  67. package/package.json +7 -13
  68. package/src/aggregator.ts +276 -211
  69. package/src/client/App.tsx +38 -80
  70. package/src/client/api.ts +65 -7
  71. package/src/client/app/LiveChip.tsx +97 -0
  72. package/src/client/app/Shell.tsx +181 -0
  73. package/src/client/app/ThemeToggle.tsx +5 -2
  74. package/src/client/app/nav.ts +78 -0
  75. package/src/client/charts/BarList.tsx +48 -0
  76. package/src/client/charts/Chart.tsx +388 -0
  77. package/src/client/charts/Legend.tsx +61 -0
  78. package/src/client/charts/ShareBar.tsx +26 -0
  79. package/src/client/charts/Sparkline.tsx +38 -0
  80. package/src/client/charts/TimeChart.tsx +22 -0
  81. package/src/client/charts/index.ts +7 -0
  82. package/src/client/charts/types.ts +27 -0
  83. package/src/client/charts/useWidth.ts +19 -0
  84. package/src/client/data/colors.ts +45 -0
  85. package/src/client/data/formatters.ts +30 -9
  86. package/src/client/data/live.tsx +85 -0
  87. package/src/client/data/query.ts +166 -0
  88. package/src/client/data/range.ts +82 -0
  89. package/src/client/data/series.ts +85 -0
  90. package/src/client/data/useHashRoute.ts +41 -83
  91. package/src/client/data/view-models.ts +347 -140
  92. package/src/client/index.tsx +3 -6
  93. package/src/client/routes/CostsRoute.tsx +394 -222
  94. package/src/client/routes/ErrorsRoute.tsx +401 -98
  95. package/src/client/routes/FrustrationRoute.tsx +924 -0
  96. package/src/client/routes/GainRoute.tsx +209 -206
  97. package/src/client/routes/ModelsRoute.tsx +540 -431
  98. package/src/client/routes/OverviewRoute.tsx +283 -311
  99. package/src/client/routes/ProjectsRoute.tsx +298 -156
  100. package/src/client/routes/ProvidersRoute.tsx +948 -416
  101. package/src/client/routes/RequestsRoute.tsx +295 -107
  102. package/src/client/routes/ToolsRoute.tsx +495 -398
  103. package/src/client/routes/TracesRoute.tsx +136 -117
  104. package/src/client/routes/costs.css +24 -0
  105. package/src/client/routes/errors.css +60 -0
  106. package/src/client/routes/frustration.css +123 -0
  107. package/src/client/routes/index.ts +1 -1
  108. package/src/client/routes/models.css +43 -0
  109. package/src/client/routes/projects.css +6 -0
  110. package/src/client/routes/providers.css +28 -0
  111. package/src/client/routes/tools.css +14 -0
  112. package/src/client/styles.css +1396 -1299
  113. package/src/client/traces/AggregatesPanel.tsx +55 -74
  114. package/src/client/traces/Minimap.tsx +6 -15
  115. package/src/client/traces/SpanDrawer.tsx +128 -194
  116. package/src/client/traces/SummaryStrip.tsx +21 -24
  117. package/src/client/traces/TimelineCanvas.tsx +27 -44
  118. package/src/client/traces/TraceView.tsx +175 -130
  119. package/src/client/traces/TranscriptList.tsx +13 -41
  120. package/src/client/traces/trace-colors.ts +60 -53
  121. package/src/client/traces/traces.css +279 -0
  122. package/src/client/ui/Badge.tsx +29 -0
  123. package/src/client/ui/Card.tsx +61 -0
  124. package/src/client/ui/Drawer.tsx +93 -0
  125. package/src/client/ui/JsonBlock.tsx +26 -44
  126. package/src/client/ui/Modal.tsx +106 -0
  127. package/src/client/ui/RequestDrawer.tsx +163 -188
  128. package/src/client/ui/SearchInput.tsx +25 -0
  129. package/src/client/ui/Segmented.tsx +61 -0
  130. package/src/client/ui/Stat.tsx +104 -0
  131. package/src/client/ui/States.tsx +81 -0
  132. package/src/client/ui/Table.tsx +229 -0
  133. package/src/client/ui/index.ts +9 -10
  134. package/src/client/ui/modal.css +54 -0
  135. package/src/client/ui/request-drawer.css +40 -0
  136. package/src/db.ts +379 -983
  137. package/src/frustration.ts +473 -0
  138. package/src/index.ts +22 -22
  139. package/src/live.ts +259 -0
  140. package/src/parser.ts +55 -66
  141. package/src/rollup.ts +951 -0
  142. package/src/server.ts +123 -40
  143. package/src/shared-types.ts +120 -56
  144. package/src/trace.ts +14 -7
  145. package/src/types.ts +4 -0
  146. package/src/user-metrics.ts +13 -0
  147. package/dist/client/styles.css +0 -1976
  148. package/dist/types/client/app/AppLayout.d.ts +0 -16
  149. package/dist/types/client/app/NavRail.d.ts +0 -7
  150. package/dist/types/client/app/RangeControl.d.ts +0 -7
  151. package/dist/types/client/app/SyncButton.d.ts +0 -14
  152. package/dist/types/client/app/TopBar.d.ts +0 -15
  153. package/dist/types/client/app/routes.d.ts +0 -12
  154. package/dist/types/client/components/AgentTokenShare.d.ts +0 -5
  155. package/dist/types/client/components/chart-shared.d.ts +0 -178
  156. package/dist/types/client/components/models-table-shared.d.ts +0 -175
  157. package/dist/types/client/components/range-meta.d.ts +0 -21
  158. package/dist/types/client/data/charts.d.ts +0 -1
  159. package/dist/types/client/data/useResource.d.ts +0 -13
  160. package/dist/types/client/routes/BehaviorRoute.d.ts +0 -7
  161. package/dist/types/client/ui/AsyncBoundary.d.ts +0 -12
  162. package/dist/types/client/ui/DataTable.d.ts +0 -17
  163. package/dist/types/client/ui/EmptyState.d.ts +0 -7
  164. package/dist/types/client/ui/ErrorState.d.ts +0 -6
  165. package/dist/types/client/ui/MetricCluster.d.ts +0 -5
  166. package/dist/types/client/ui/Panel.d.ts +0 -7
  167. package/dist/types/client/ui/SegmentedControl.d.ts +0 -12
  168. package/dist/types/client/ui/Skeleton.d.ts +0 -8
  169. package/dist/types/client/ui/StatusPill.d.ts +0 -7
  170. package/src/client/app/AppLayout.tsx +0 -93
  171. package/src/client/app/NavRail.tsx +0 -44
  172. package/src/client/app/RangeControl.tsx +0 -39
  173. package/src/client/app/SyncButton.tsx +0 -75
  174. package/src/client/app/TopBar.tsx +0 -73
  175. package/src/client/app/routes.ts +0 -93
  176. package/src/client/components/AgentTokenShare.tsx +0 -68
  177. package/src/client/components/chart-shared.tsx +0 -273
  178. package/src/client/components/models-table-shared.tsx +0 -255
  179. package/src/client/components/range-meta.ts +0 -73
  180. package/src/client/data/charts.ts +0 -14
  181. package/src/client/data/useResource.ts +0 -154
  182. package/src/client/routes/BehaviorRoute.tsx +0 -623
  183. package/src/client/ui/AsyncBoundary.tsx +0 -54
  184. package/src/client/ui/DataTable.tsx +0 -122
  185. package/src/client/ui/EmptyState.tsx +0 -16
  186. package/src/client/ui/ErrorState.tsx +0 -25
  187. package/src/client/ui/MetricCluster.tsx +0 -92
  188. package/src/client/ui/Panel.tsx +0 -24
  189. package/src/client/ui/SegmentedControl.tsx +0 -36
  190. package/src/client/ui/Skeleton.tsx +0 -17
  191. package/src/client/ui/StatusPill.tsx +0 -15
  192. package/tailwind.config.js +0 -40
@@ -1,122 +0,0 @@
1
- import type React from "react";
2
-
3
- export interface DataTableColumn<T> {
4
- key: string;
5
- header: React.ReactNode;
6
- render?: (item: T) => React.ReactNode;
7
- className?: string;
8
- numeric?: boolean;
9
- }
10
-
11
- export interface DataTableProps<T> {
12
- columns: DataTableColumn<T>[];
13
- data: T[];
14
- keyExtractor: (item: T) => string | number;
15
- onRowClick?: (item: T) => void;
16
- renderMobileCard?: (item: T, onClick?: () => void) => React.ReactNode;
17
- emptyText?: string;
18
- }
19
-
20
- export function DataTable<T>({
21
- columns,
22
- data,
23
- keyExtractor,
24
- onRowClick,
25
- renderMobileCard,
26
- emptyText = "No data available",
27
- }: DataTableProps<T>) {
28
- const handleKeyDown = (e: React.KeyboardEvent<HTMLTableRowElement>, item: T) => {
29
- if (onRowClick && (e.key === "Enter" || e.key === " ")) {
30
- e.preventDefault();
31
- onRowClick(item);
32
- }
33
- };
34
-
35
- if (data.length === 0) {
36
- return <div className="stats-table-empty">{emptyText}</div>;
37
- }
38
-
39
- return (
40
- <div className="stats-table-wrapper">
41
- {/* Mobile layout */}
42
- {renderMobileCard && (
43
- <div className="stats-table-mobile-only">
44
- <div className="stats-table-mobile-list">
45
- {data.map(item => {
46
- const key = String(keyExtractor(item));
47
- const onClick = onRowClick ? () => onRowClick(item) : undefined;
48
- return (
49
- <div key={key} className="stats-table-mobile-card-wrapper">
50
- {renderMobileCard(item, onClick)}
51
- </div>
52
- );
53
- })}
54
- </div>
55
- </div>
56
- )}
57
-
58
- {/* Desktop layout */}
59
- <div className={renderMobileCard ? "stats-table-desktop-only" : "stats-table-container"}>
60
- <table className="stats-table">
61
- <thead>
62
- <tr>
63
- {columns.map(col => {
64
- const headerClasses = [
65
- "stats-table-th",
66
- col.numeric ? "stats-text-right" : "stats-text-left",
67
- col.className || "",
68
- ]
69
- .filter(Boolean)
70
- .join(" ");
71
-
72
- return (
73
- <th key={col.key} className={headerClasses}>
74
- {col.header}
75
- </th>
76
- );
77
- })}
78
- </tr>
79
- </thead>
80
- <tbody>
81
- {data.map(item => {
82
- const key = String(keyExtractor(item));
83
- const isClickable = typeof onRowClick === "function";
84
- const rowClasses = ["stats-table-tr", isClickable ? "stats-table-tr-clickable" : ""]
85
- .filter(Boolean)
86
- .join(" ");
87
-
88
- return (
89
- <tr
90
- key={key}
91
- className={rowClasses}
92
- onClick={isClickable ? () => onRowClick!(item) : undefined}
93
- onKeyDown={isClickable ? e => handleKeyDown(e, item) : undefined}
94
- tabIndex={isClickable ? 0 : undefined}
95
- role={isClickable ? "button" : undefined}
96
- >
97
- {columns.map(col => {
98
- const cellClasses = [
99
- "stats-table-td",
100
- col.numeric ? "stats-text-right" : "stats-text-left",
101
- col.className || "",
102
- ]
103
- .filter(Boolean)
104
- .join(" ");
105
-
106
- return (
107
- <td key={col.key} className={cellClasses}>
108
- {col.render
109
- ? col.render(item)
110
- : ((item as Record<string, unknown>)[col.key] as React.ReactNode)}
111
- </td>
112
- );
113
- })}
114
- </tr>
115
- );
116
- })}
117
- </tbody>
118
- </table>
119
- </div>
120
- </div>
121
- );
122
- }
@@ -1,16 +0,0 @@
1
- import { Inbox, type LucideIcon } from "lucide-react";
2
-
3
- export interface EmptyStateProps {
4
- message?: string;
5
- icon?: LucideIcon;
6
- className?: string;
7
- }
8
-
9
- export function EmptyState({ message = "No data available", icon: Icon = Inbox, className = "" }: EmptyStateProps) {
10
- return (
11
- <div className={`stats-empty-state ${className}`}>
12
- <Icon size={24} className="stats-empty-state-icon" aria-hidden="true" />
13
- <p className="stats-empty-state-message">{message}</p>
14
- </div>
15
- );
16
- }
@@ -1,25 +0,0 @@
1
- export interface ErrorStateProps {
2
- error?: Error | null;
3
- onRetry?: () => void;
4
- className?: string;
5
- }
6
-
7
- export function ErrorState({ error, onRetry, className = "" }: ErrorStateProps) {
8
- return (
9
- <div className={`stats-error-state ${className}`}>
10
- <div className="stats-error-state-content">
11
- <h4 className="stats-error-state-title">Failed to load data</h4>
12
- {error && <p className="stats-error-state-message">{error.message}</p>}
13
- {onRetry && (
14
- <button
15
- type="button"
16
- onClick={onRetry}
17
- className="stats-button stats-button-secondary stats-error-state-btn"
18
- >
19
- Retry
20
- </button>
21
- )}
22
- </div>
23
- </div>
24
- );
25
- }
@@ -1,92 +0,0 @@
1
- import {
2
- formatCompact,
3
- formatDurationMs,
4
- formatEstimatedCost,
5
- formatInteger,
6
- formatPercent,
7
- formatTokensPerSecond,
8
- } from "../data/formatters";
9
- import { sumConversationTokens } from "../data/view-models";
10
- import type { AggregatedStats } from "../types";
11
-
12
- export interface MetricClusterProps {
13
- stats: AggregatedStats;
14
- }
15
-
16
- export function MetricCluster({ stats }: MetricClusterProps) {
17
- const conversationTokens = sumConversationTokens(stats);
18
-
19
- return (
20
- <div className="stats-metric-cluster">
21
- <div className="stats-metric-primary-grid">
22
- <div className="stats-metric-card primary">
23
- <div className="stats-metric-label">API-equivalent estimate</div>
24
- <div className="stats-metric-value">
25
- {formatEstimatedCost(
26
- stats.totalCost,
27
- stats.unpricedRequests,
28
- stats.totalCost > 0 && stats.totalCost < 0.01 ? 4 : 2,
29
- )}
30
- </div>
31
- </div>
32
- <div className="stats-metric-card primary">
33
- <div className="stats-metric-label">Requests</div>
34
- <div className="stats-metric-value">{formatInteger(stats.totalRequests)}</div>
35
- </div>
36
- <div
37
- className="stats-metric-card primary"
38
- title="Prompt-input cost saved versus billing the same tokens uncached; cache writes can make this negative"
39
- >
40
- <div className="stats-metric-label">Cache Savings</div>
41
- <div className="stats-metric-value">{formatPercent(stats.cacheSavings)}</div>
42
- </div>
43
- <div
44
- className="stats-metric-card primary"
45
- title="Prompt input served from cache: cache reads / (uncached input + cache reads)"
46
- >
47
- <div className="stats-metric-label">Cache Rate</div>
48
- <div className="stats-metric-value">{formatPercent(stats.cacheRate)}</div>
49
- </div>
50
- <div className="stats-metric-card primary">
51
- <div className="stats-metric-label">Error Rate</div>
52
- <div className="stats-metric-value">{formatPercent(stats.errorRate)}</div>
53
- </div>
54
- </div>
55
-
56
- <div className="stats-metric-secondary-grid">
57
- <div className="stats-metric-card secondary" title="Conversation input not served from cache">
58
- <div className="stats-metric-label">Uncached Input</div>
59
- <div className="stats-metric-value">{formatCompact(stats.totalInputTokens)}</div>
60
- </div>
61
- <div className="stats-metric-card secondary" title="Conversation input read from the prompt cache">
62
- <div className="stats-metric-label">Cache Read</div>
63
- <div className="stats-metric-value">{formatCompact(stats.totalCacheReadTokens)}</div>
64
- </div>
65
- <div className="stats-metric-card secondary">
66
- <div className="stats-metric-label">Output Tokens</div>
67
- <div className="stats-metric-value">{formatCompact(stats.totalOutputTokens)}</div>
68
- </div>
69
- <div className="stats-metric-card secondary" title="Uncached input + cache reads + cache writes + output">
70
- <div className="stats-metric-label">Conversation Total</div>
71
- <div className="stats-metric-value">{formatCompact(conversationTokens)}</div>
72
- </div>
73
- <div className="stats-metric-card secondary">
74
- <div className="stats-metric-label">Premium Requests</div>
75
- <div className="stats-metric-value">{formatInteger(stats.totalPremiumRequests)}</div>
76
- </div>
77
- <div className="stats-metric-card secondary">
78
- <div className="stats-metric-label">Tokens/s</div>
79
- <div className="stats-metric-value">{formatTokensPerSecond(stats.avgTokensPerSecond)}</div>
80
- </div>
81
- <div className="stats-metric-card secondary">
82
- <div className="stats-metric-label">Avg Latency</div>
83
- <div className="stats-metric-value">{formatDurationMs(stats.avgDuration)}</div>
84
- </div>
85
- <div className="stats-metric-card secondary">
86
- <div className="stats-metric-label">Avg TTFT</div>
87
- <div className="stats-metric-value">{formatDurationMs(stats.avgTtft)}</div>
88
- </div>
89
- </div>
90
- </div>
91
- );
92
- }
@@ -1,24 +0,0 @@
1
- import type React from "react";
2
-
3
- export interface PanelProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
4
- title?: React.ReactNode;
5
- subtitle?: React.ReactNode;
6
- actions?: React.ReactNode;
7
- }
8
-
9
- export function Panel({ title, subtitle, actions, children, className = "", ...props }: PanelProps) {
10
- return (
11
- <div className={`stats-panel ${className}`} {...props}>
12
- {(title || subtitle || actions) && (
13
- <div className="stats-panel-header">
14
- <div className="stats-panel-header-titles">
15
- {title && <h3 className="stats-panel-title">{title}</h3>}
16
- {subtitle && <p className="stats-panel-subtitle">{subtitle}</p>}
17
- </div>
18
- {actions && <div className="stats-panel-actions">{actions}</div>}
19
- </div>
20
- )}
21
- <div className="stats-panel-body">{children}</div>
22
- </div>
23
- );
24
- }
@@ -1,36 +0,0 @@
1
- export interface SegmentedControlOption<T> {
2
- value: T;
3
- label: string;
4
- title?: string;
5
- }
6
-
7
- export interface SegmentedControlProps<T> {
8
- options: SegmentedControlOption<T>[];
9
- value: T;
10
- onChange: (value: T) => void;
11
- className?: string;
12
- }
13
-
14
- export function SegmentedControl<T>({ options, value, onChange, className = "" }: SegmentedControlProps<T>) {
15
- return (
16
- <div className={`stats-segmented-control ${className}`} role="radiogroup">
17
- {options.map(opt => {
18
- const isActive = opt.value === value;
19
- return (
20
- <button
21
- key={String(opt.value)}
22
- type="button"
23
- role="radio"
24
- aria-checked={isActive}
25
- data-active={isActive ? "true" : "false"}
26
- className="stats-segmented-control-btn"
27
- title={opt.title}
28
- onClick={() => onChange(opt.value)}
29
- >
30
- {opt.label}
31
- </button>
32
- );
33
- })}
34
- </div>
35
- );
36
- }
@@ -1,17 +0,0 @@
1
- import type React from "react";
2
-
3
- export interface SkeletonProps {
4
- variant?: "text" | "rect" | "circle";
5
- width?: string | number;
6
- height?: string | number;
7
- className?: string;
8
- }
9
-
10
- export function Skeleton({ variant = "text", width, height, className = "" }: SkeletonProps) {
11
- const style: React.CSSProperties = {
12
- width: width !== undefined ? (typeof width === "number" ? `${width}px` : width) : undefined,
13
- height: height !== undefined ? (typeof height === "number" ? `${height}px` : height) : undefined,
14
- };
15
-
16
- return <div className={`stats-skeleton ${className}`} data-variant={variant} style={style} />;
17
- }
@@ -1,15 +0,0 @@
1
- import type React from "react";
2
-
3
- export interface StatusPillProps {
4
- variant: "success" | "danger" | "warning" | "info" | "default";
5
- children: React.ReactNode;
6
- className?: string;
7
- }
8
-
9
- export function StatusPill({ variant, children, className = "" }: StatusPillProps) {
10
- return (
11
- <span className={`stats-status-pill ${className}`} data-variant={variant}>
12
- {children}
13
- </span>
14
- );
15
- }
@@ -1,40 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- import * as path from "node:path";
3
- export default {
4
- content: [path.join(import.meta.dir, "src", "client", "**/*.{js,jsx,ts,tsx}")],
5
- darkMode: "class",
6
- theme: {
7
- extend: {
8
- colors: {
9
- page: "var(--bg-page)",
10
- surface: "var(--bg-surface)",
11
- elevated: "var(--bg-elevated)",
12
- "border-subtle": "var(--border-subtle)",
13
- "border-default": "var(--border-default)",
14
- "text-primary": "var(--text-primary)",
15
- "text-secondary": "var(--text-secondary)",
16
- "text-muted": "var(--text-muted)",
17
- pink: "var(--accent-pink)",
18
- cyan: "var(--accent-cyan)",
19
- violet: "var(--accent-violet)",
20
- },
21
- fontFamily: {
22
- sans: [
23
- "-apple-system",
24
- "BlinkMacSystemFont",
25
- '"Segoe UI"',
26
- "Roboto",
27
- "Helvetica",
28
- "Arial",
29
- "sans-serif",
30
- ],
31
- },
32
- borderRadius: {
33
- sm: "var(--radius-sm)",
34
- md: "var(--radius-md)",
35
- lg: "var(--radius-lg)",
36
- },
37
- },
38
- },
39
- plugins: [],
40
- };