@hanzo/ui 8.0.7 → 8.0.11

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 (227) hide show
  1. package/dist/ThemeToggleNext-7727UR5W.js +4 -0
  2. package/dist/ThemeToggleNext-7727UR5W.js.map +1 -0
  3. package/dist/ThemeToggleNext-ICB5KL5J.cjs +19 -0
  4. package/dist/ThemeToggleNext-ICB5KL5J.cjs.map +1 -0
  5. package/dist/billing.cjs +15 -0
  6. package/dist/billing.cjs.map +1 -0
  7. package/{types → dist}/billing.d.ts +0 -1
  8. package/dist/billing.js +4 -0
  9. package/dist/billing.js.map +1 -0
  10. package/dist/canvas.cjs +15 -0
  11. package/dist/canvas.cjs.map +1 -0
  12. package/dist/canvas.d.ts +1 -0
  13. package/dist/canvas.js +4 -0
  14. package/dist/canvas.js.map +1 -0
  15. package/{types → dist}/cd/layout.d.ts +0 -1
  16. package/{types → dist}/cd/status.d.ts +0 -1
  17. package/{types → dist}/cd/time.d.ts +0 -1
  18. package/{types → dist}/cd/types.d.ts +0 -1
  19. package/dist/chunk-42CU5GSF.js +98 -0
  20. package/dist/chunk-42CU5GSF.js.map +1 -0
  21. package/dist/chunk-M2ILMK44.cjs +2104 -0
  22. package/dist/chunk-M2ILMK44.cjs.map +1 -0
  23. package/dist/chunk-YBXVUV7P.cjs +103 -0
  24. package/dist/chunk-YBXVUV7P.cjs.map +1 -0
  25. package/dist/chunk-YSURJUOA.js +2035 -0
  26. package/dist/chunk-YSURJUOA.js.map +1 -0
  27. package/dist/dashboard.cjs +15 -0
  28. package/dist/dashboard.cjs.map +1 -0
  29. package/dist/dashboard.d.ts +1 -0
  30. package/dist/dashboard.js +4 -0
  31. package/dist/dashboard.js.map +1 -0
  32. package/dist/data.cjs +15 -0
  33. package/dist/data.cjs.map +1 -0
  34. package/dist/data.d.ts +1 -0
  35. package/dist/data.js +4 -0
  36. package/dist/data.js.map +1 -0
  37. package/dist/gitops.cjs +15 -0
  38. package/dist/gitops.cjs.map +1 -0
  39. package/dist/gitops.d.ts +1 -0
  40. package/dist/gitops.js +4 -0
  41. package/dist/gitops.js.map +1 -0
  42. package/dist/index.cjs +299 -0
  43. package/dist/index.cjs.map +1 -0
  44. package/{types → dist}/index.d.ts +0 -1
  45. package/dist/index.js +6 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/network.cjs +15 -0
  48. package/dist/network.cjs.map +1 -0
  49. package/{types → dist}/network.d.ts +0 -1
  50. package/dist/network.js +4 -0
  51. package/dist/network.js.map +1 -0
  52. package/dist/oss/blueprint.d.ts +37 -0
  53. package/dist/oss/catalog.d.ts +67 -0
  54. package/dist/oss.cjs +190 -0
  55. package/dist/oss.cjs.map +1 -0
  56. package/dist/oss.d.ts +2 -0
  57. package/dist/oss.js +180 -0
  58. package/dist/oss.js.map +1 -0
  59. package/dist/primitives/bases/data/index.cjs +15 -0
  60. package/dist/primitives/bases/data/index.cjs.map +1 -0
  61. package/dist/primitives/bases/data/index.d.ts +1 -0
  62. package/dist/primitives/bases/data/index.js +4 -0
  63. package/dist/primitives/bases/data/index.js.map +1 -0
  64. package/{types → dist}/product/AppHeader.d.ts +0 -1
  65. package/{types → dist}/product/BrandMark.d.ts +0 -1
  66. package/{types → dist}/product/Charts.d.ts +0 -1
  67. package/{types → dist}/product/ComboBox.d.ts +0 -1
  68. package/{types → dist}/product/DataTable.d.ts +0 -1
  69. package/{types → dist}/product/Donut.d.ts +0 -1
  70. package/{types → dist}/product/EmptyState.d.ts +0 -1
  71. package/{types → dist}/product/FadeIn.d.ts +0 -1
  72. package/{types → dist}/product/Field.d.ts +0 -1
  73. package/{types → dist}/product/HanzoMark.d.ts +0 -1
  74. package/{types → dist}/product/Metric.d.ts +0 -1
  75. package/dist/product/OrgMark.d.ts +21 -0
  76. package/{types → dist}/product/OrgSwitcher.d.ts +8 -2
  77. package/{types → dist}/product/PageHeader.d.ts +0 -1
  78. package/{types → dist}/product/PrimaryButton.d.ts +0 -1
  79. package/{types → dist}/product/ProductIcon.d.ts +0 -1
  80. package/{types → dist}/product/ProviderLogo.d.ts +0 -1
  81. package/{types → dist}/product/Reorder.d.ts +0 -1
  82. package/{types → dist}/product/SelectMenu.d.ts +6 -8
  83. package/{types → dist}/product/SlideOver.d.ts +0 -1
  84. package/{types → dist}/product/StatusTag.d.ts +0 -1
  85. package/{types → dist}/product/ThemeToggle.d.ts +0 -1
  86. package/{types → dist}/product/ThemeToggleNext.d.ts +0 -1
  87. package/{types → dist}/product/Toast.d.ts +0 -1
  88. package/{types → dist}/product/color.d.ts +0 -1
  89. package/{types → dist}/product/combobox/filter.d.ts +0 -1
  90. package/dist/product/index.cjs +286 -0
  91. package/dist/product/index.cjs.map +1 -0
  92. package/{types → dist}/product/index.d.ts +1 -1
  93. package/dist/product/index.js +5 -0
  94. package/dist/product/index.js.map +1 -0
  95. package/{types → dist}/product/menu/ContextMenu.d.ts +3 -6
  96. package/dist/product/menu/DropdownMenu.d.ts +29 -0
  97. package/dist/product/menu/FloatingMenu.d.ts +36 -0
  98. package/{types → dist}/product/menu/index.d.ts +1 -1
  99. package/{types → dist}/product/menu/items.d.ts +0 -1
  100. package/{types → dist}/product/menu/portal-theme.d.ts +0 -1
  101. package/{types → dist}/product/menu/roving.d.ts +0 -1
  102. package/{types → dist}/product/scope.d.ts +0 -1
  103. package/{types → dist}/product/surfaces.data.d.ts +0 -1
  104. package/dist/telemetry.cjs +15 -0
  105. package/dist/telemetry.cjs.map +1 -0
  106. package/dist/telemetry.d.ts +1 -0
  107. package/dist/telemetry.js +4 -0
  108. package/dist/telemetry.js.map +1 -0
  109. package/dist/usage.cjs +15 -0
  110. package/dist/usage.cjs.map +1 -0
  111. package/dist/usage.d.ts +1 -0
  112. package/dist/usage.js +4 -0
  113. package/dist/usage.js.map +1 -0
  114. package/dist/wallet.cjs +15 -0
  115. package/dist/wallet.cjs.map +1 -0
  116. package/{types → dist}/wallet.d.ts +0 -1
  117. package/dist/wallet.js +4 -0
  118. package/dist/wallet.js.map +1 -0
  119. package/package.json +69 -34
  120. package/src/billing.ts +0 -9
  121. package/src/canvas.ts +0 -11
  122. package/src/cd/layout.ts +0 -184
  123. package/src/cd/status.ts +0 -158
  124. package/src/cd/time.ts +0 -52
  125. package/src/cd/types.ts +0 -318
  126. package/src/dashboard.ts +0 -8
  127. package/src/data.ts +0 -12
  128. package/src/gitops.ts +0 -5
  129. package/src/gui-env.d.ts +0 -27
  130. package/src/index.ts +0 -22
  131. package/src/network.ts +0 -11
  132. package/src/primitives/bases/data/index.ts +0 -13
  133. package/src/product/AppHeader.tsx +0 -183
  134. package/src/product/BrandMark.tsx +0 -27
  135. package/src/product/Charts.tsx +0 -471
  136. package/src/product/ComboBox.tsx +0 -127
  137. package/src/product/DataTable.tsx +0 -99
  138. package/src/product/Donut.tsx +0 -83
  139. package/src/product/EmptyState.tsx +0 -129
  140. package/src/product/FadeIn.tsx +0 -38
  141. package/src/product/Field.tsx +0 -160
  142. package/src/product/HanzoMark.tsx +0 -31
  143. package/src/product/Metric.tsx +0 -253
  144. package/src/product/OrgSwitcher.tsx +0 -332
  145. package/src/product/PageHeader.tsx +0 -31
  146. package/src/product/PrimaryButton.tsx +0 -17
  147. package/src/product/ProductIcon.tsx +0 -61
  148. package/src/product/ProviderLogo.tsx +0 -137
  149. package/src/product/Reorder.test.ts +0 -41
  150. package/src/product/Reorder.tsx +0 -110
  151. package/src/product/SelectMenu.tsx +0 -80
  152. package/src/product/SlideOver.tsx +0 -197
  153. package/src/product/StatusTag.tsx +0 -49
  154. package/src/product/ThemeToggle.tsx +0 -135
  155. package/src/product/ThemeToggleNext.tsx +0 -27
  156. package/src/product/Toast.tsx +0 -166
  157. package/src/product/color.ts +0 -17
  158. package/src/product/combobox/filter.test.ts +0 -51
  159. package/src/product/combobox/filter.ts +0 -39
  160. package/src/product/index.ts +0 -76
  161. package/src/product/menu/ContextMenu.tsx +0 -114
  162. package/src/product/menu/DropdownMenu.tsx +0 -76
  163. package/src/product/menu/index.ts +0 -15
  164. package/src/product/menu/items.tsx +0 -230
  165. package/src/product/menu/portal-theme.tsx +0 -33
  166. package/src/product/menu/roving.ts +0 -34
  167. package/src/product/scope.test.ts +0 -92
  168. package/src/product/scope.ts +0 -123
  169. package/src/product/surfaces.data.ts +0 -42
  170. package/src/usage.ts +0 -8
  171. package/src/wallet.ts +0 -11
  172. package/types/billing.d.ts.map +0 -1
  173. package/types/canvas.d.ts +0 -2
  174. package/types/canvas.d.ts.map +0 -1
  175. package/types/cd/layout.d.ts.map +0 -1
  176. package/types/cd/status.d.ts.map +0 -1
  177. package/types/cd/time.d.ts.map +0 -1
  178. package/types/cd/types.d.ts.map +0 -1
  179. package/types/dashboard.d.ts +0 -2
  180. package/types/dashboard.d.ts.map +0 -1
  181. package/types/data.d.ts +0 -2
  182. package/types/data.d.ts.map +0 -1
  183. package/types/gitops.d.ts +0 -2
  184. package/types/gitops.d.ts.map +0 -1
  185. package/types/index.d.ts.map +0 -1
  186. package/types/network.d.ts.map +0 -1
  187. package/types/primitives/bases/data/index.d.ts +0 -2
  188. package/types/primitives/bases/data/index.d.ts.map +0 -1
  189. package/types/product/AppHeader.d.ts.map +0 -1
  190. package/types/product/BrandMark.d.ts.map +0 -1
  191. package/types/product/Charts.d.ts.map +0 -1
  192. package/types/product/ComboBox.d.ts.map +0 -1
  193. package/types/product/DataTable.d.ts.map +0 -1
  194. package/types/product/Donut.d.ts.map +0 -1
  195. package/types/product/EmptyState.d.ts.map +0 -1
  196. package/types/product/FadeIn.d.ts.map +0 -1
  197. package/types/product/Field.d.ts.map +0 -1
  198. package/types/product/HanzoMark.d.ts.map +0 -1
  199. package/types/product/Metric.d.ts.map +0 -1
  200. package/types/product/OrgSwitcher.d.ts.map +0 -1
  201. package/types/product/PageHeader.d.ts.map +0 -1
  202. package/types/product/PrimaryButton.d.ts.map +0 -1
  203. package/types/product/ProductIcon.d.ts.map +0 -1
  204. package/types/product/ProviderLogo.d.ts.map +0 -1
  205. package/types/product/Reorder.d.ts.map +0 -1
  206. package/types/product/SelectMenu.d.ts.map +0 -1
  207. package/types/product/SlideOver.d.ts.map +0 -1
  208. package/types/product/StatusTag.d.ts.map +0 -1
  209. package/types/product/ThemeToggle.d.ts.map +0 -1
  210. package/types/product/ThemeToggleNext.d.ts.map +0 -1
  211. package/types/product/Toast.d.ts.map +0 -1
  212. package/types/product/color.d.ts.map +0 -1
  213. package/types/product/combobox/filter.d.ts.map +0 -1
  214. package/types/product/index.d.ts.map +0 -1
  215. package/types/product/menu/ContextMenu.d.ts.map +0 -1
  216. package/types/product/menu/DropdownMenu.d.ts +0 -34
  217. package/types/product/menu/DropdownMenu.d.ts.map +0 -1
  218. package/types/product/menu/index.d.ts.map +0 -1
  219. package/types/product/menu/items.d.ts.map +0 -1
  220. package/types/product/menu/portal-theme.d.ts.map +0 -1
  221. package/types/product/menu/roving.d.ts.map +0 -1
  222. package/types/product/scope.d.ts.map +0 -1
  223. package/types/product/surfaces.data.d.ts.map +0 -1
  224. package/types/usage.d.ts +0 -2
  225. package/types/usage.d.ts.map +0 -1
  226. package/types/wallet.d.ts.map +0 -1
  227. /package/{src → dist}/styles/hanzo-motion.css +0 -0
@@ -1,83 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * Donut — a tiny dependency-free SVG ring for a categorical breakdown
5
- * (e.g. machines by status). Standard library first: no chart dependency for a
6
- * single ring. It renders ONLY the real segments it is given and returns `null`
7
- * when the total is zero, so the caller shows an honest empty state instead of an
8
- * empty circle. Purely presentational — all values come from the caller.
9
- */
10
- export type DonutSegment = { label: string; value: number; color: string }
11
-
12
- export function Donut({
13
- segments,
14
- size = 160,
15
- thickness = 18,
16
- centerValue,
17
- centerLabel,
18
- trackColor = '#2a2d2e',
19
- valueColor = '#ecedee',
20
- labelColor = '#9ba1a6',
21
- }: {
22
- segments: DonutSegment[]
23
- size?: number
24
- thickness?: number
25
- /** Big number in the middle (e.g. total). */
26
- centerValue?: string
27
- /** Caption under the center value. */
28
- centerLabel?: string
29
- /** Unfilled-ring color (explicit hex so it never depends on theme var names). */
30
- trackColor?: string
31
- valueColor?: string
32
- labelColor?: string
33
- }) {
34
- const total = segments.reduce((n, s) => n + Math.max(0, s.value), 0)
35
- if (total <= 0) return null
36
-
37
- const r = (size - thickness) / 2
38
- const cx = size / 2
39
- const cy = size / 2
40
- const circ = 2 * Math.PI * r
41
-
42
- let offset = 0
43
- const arcs = segments
44
- .filter((s) => s.value > 0)
45
- .map((s) => {
46
- const frac = s.value / total
47
- const dash = frac * circ
48
- const node = (
49
- <circle
50
- key={s.label}
51
- cx={cx}
52
- cy={cy}
53
- r={r}
54
- fill="none"
55
- stroke={s.color}
56
- strokeWidth={thickness}
57
- strokeDasharray={`${dash} ${circ - dash}`}
58
- strokeDashoffset={-offset}
59
- />
60
- )
61
- offset += dash
62
- return node
63
- })
64
-
65
- return (
66
- <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} role="img" aria-label="Donut chart">
67
- {/* track */}
68
- <circle cx={cx} cy={cy} r={r} fill="none" stroke={trackColor} strokeWidth={thickness} />
69
- {/* segments, drawn from 12 o'clock */}
70
- <g transform={`rotate(-90 ${cx} ${cy})`}>{arcs}</g>
71
- {centerValue ? (
72
- <text x={cx} y={cy - 2} textAnchor="middle" fontSize={size * 0.2} fontWeight="800" fill={valueColor}>
73
- {centerValue}
74
- </text>
75
- ) : null}
76
- {centerLabel ? (
77
- <text x={cx} y={cy + size * 0.13} textAnchor="middle" fontSize={size * 0.08} fill={labelColor}>
78
- {centerLabel}
79
- </text>
80
- ) : null}
81
- </svg>
82
- )
83
- }
@@ -1,129 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * Rich empty state — the ONE way the console turns a real, honest "nothing here
5
- * yet" into a helpful onboarding moment (DigitalOcean / Vercel class) instead of
6
- * a dead screen.
7
- *
8
- * It is NOT a fabricated-data fallback: callers render it only when a load
9
- * genuinely succeeded with zero rows (or a resource has no surface yet). It shows
10
- * the product icon, a one-line "what this is", a couple of guidance bullets, a
11
- * single primary call-to-action (Deploy / Create / Connect …) and an optional
12
- * secondary link (Docs / CLI). Orthogonal to `BackendStateCard` (failures) and
13
- * `DataTable`'s thin inline empty (in-table) — this is the full-bleed first-run
14
- * surface a module shows in place of its table.
15
- */
16
- import type { ReactElement, ReactNode } from 'react'
17
- import { Button, Card, Text, XStack, YStack } from '@hanzo/gui'
18
- import { ArrowRight, Check, ExternalLink } from '@hanzogui/lucide-icons-2'
19
-
20
- import type { IconLike } from './color'
21
- import { PrimaryButton } from './PrimaryButton'
22
-
23
- /** A single call-to-action — primary (filled) or a secondary/external link. */
24
- export type EmptyAction = {
25
- label: string
26
- /** In-app navigation / handler. */
27
- onPress?: () => void
28
- /** External URL — opens in a new tab with an external-link affordance. */
29
- href?: string
30
- icon?: ReactElement
31
- }
32
-
33
- /** Open an external URL in a new tab (no-opener), guarded for SSR. */
34
- function openHref(href: string): void {
35
- if (typeof window !== 'undefined') window.open(href, '_blank', 'noopener')
36
- }
37
-
38
- export function EmptyState({
39
- icon: Icon,
40
- title,
41
- description,
42
- bullets,
43
- primary,
44
- secondary,
45
- }: {
46
- /** Product/section icon — any lucide/Gui icon component (size + optional color). */
47
- icon: IconLike
48
- title: string
49
- description: string
50
- /** Optional "here's how to get started" guidance bullets. */
51
- bullets?: string[]
52
- /** The single high-emphasis action (Deploy / Create / Connect). */
53
- primary?: EmptyAction
54
- /** An optional lower-emphasis action (Docs / CLI / Learn more). */
55
- secondary?: EmptyAction
56
- }) {
57
- return (
58
- <Card
59
- borderWidth={1}
60
- borderColor="$borderColor"
61
- borderStyle="dashed"
62
- p="$6"
63
- gap="$4"
64
- items="center"
65
- maxWidth={640}
66
- self="center"
67
- width="100%"
68
- >
69
- <YStack
70
- width={48}
71
- height={48}
72
- items="center"
73
- justify="center"
74
- rounded="$4"
75
- bg="$color3"
76
- >
77
- <Icon size={24} />
78
- </YStack>
79
-
80
- <YStack gap="$2" items="center" maxW={480}>
81
- <Text fontSize="$6" fontWeight="800" text="center">
82
- {title}
83
- </Text>
84
- <Text fontSize="$3" color="$color11" text="center">
85
- {description}
86
- </Text>
87
- </YStack>
88
-
89
- {bullets && bullets.length ? (
90
- <YStack gap="$2" self="stretch" maxW={420} mx="auto">
91
- {bullets.map((b) => (
92
- <XStack key={b} gap="$2" items="flex-start">
93
- <YStack pt="$1">
94
- <Check size={14} color="$color10" />
95
- </YStack>
96
- <Text fontSize="$3" color="$color11" flex={1}>
97
- {b}
98
- </Text>
99
- </XStack>
100
- ))}
101
- </YStack>
102
- ) : null}
103
-
104
- {primary || secondary ? (
105
- <XStack gap="$2" items="center" flexWrap="wrap" justify="center">
106
- {primary ? (
107
- <PrimaryButton
108
- icon={primary.icon}
109
- iconAfter={primary.href ? <ExternalLink size={15} /> : <ArrowRight size={15} />}
110
- onPress={() => (primary.href ? openHref(primary.href) : primary.onPress?.())}
111
- >
112
- {primary.label}
113
- </PrimaryButton>
114
- ) : null}
115
- {secondary ? (
116
- <Button
117
- chromeless
118
- icon={secondary.icon}
119
- iconAfter={secondary.href ? <ExternalLink size={14} /> : undefined}
120
- onPress={() => (secondary.href ? openHref(secondary.href) : secondary.onPress?.())}
121
- >
122
- {secondary.label}
123
- </Button>
124
- ) : null}
125
- </XStack>
126
- ) : null}
127
- </Card>
128
- )
129
- }
@@ -1,38 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * FadeIn — the ONE entrance animation for the console.
5
- *
6
- * A thin wrapper that applies the shared `.hz-fade-up` keyframe (defined once in
7
- * globals.css) plus an optional stagger delay, so sections and cards rise in the
8
- * way the hanzo.ai marketing surface does. Framework-free (no animation driver,
9
- * no JS observers) so it works under static export and honors
10
- * `prefers-reduced-motion` (handled in the CSS). Pass `index` to stagger a list
11
- * (delay = index * step), or `delayMs` for an explicit delay.
12
- */
13
- import type { CSSProperties, ReactNode } from 'react'
14
-
15
- export function FadeIn({
16
- children,
17
- index = 0,
18
- step = 50,
19
- delayMs,
20
- style,
21
- }: {
22
- children: ReactNode
23
- /** Position in a list — staggers the entrance by `index * step` ms. */
24
- index?: number
25
- /** Per-index stagger in ms (default 50). */
26
- step?: number
27
- /** Explicit delay in ms; overrides the index-based stagger. */
28
- delayMs?: number
29
- /** Extra styles for the wrapper (e.g. layout/centering). */
30
- style?: CSSProperties
31
- }) {
32
- const delay = delayMs ?? index * step
33
- return (
34
- <div className="hz-fade-up" style={{ animationDelay: `${delay}ms`, ...style }}>
35
- {children}
36
- </div>
37
- )
38
- }
@@ -1,160 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * Form field primitives — labeled rows over Hanzo GUI inputs.
5
- *
6
- * One way to render a labeled control. Every editor field goes through these, so
7
- * spacing, disabled styling, and the label column stay consistent. Style props
8
- * use the v5 shorthand set (p/px/items/justify/...).
9
- */
10
- import type { ReactNode } from 'react'
11
- import {
12
- Input,
13
- Label,
14
- Select,
15
- Switch,
16
- TextArea,
17
- Slider,
18
- Text,
19
- XStack,
20
- YStack,
21
- } from '@hanzo/gui'
22
- import { ChevronDown } from '@hanzogui/lucide-icons-2'
23
-
24
- /** Labeled row: fixed label column + flexing control. */
25
- export function FieldRow({ label, children }: { label: string; children: ReactNode }) {
26
- return (
27
- <XStack gap="$3" items="flex-start" flexWrap="wrap">
28
- <Label width={180} pt="$2" color="$color11" fontSize="$3">
29
- {label}
30
- </Label>
31
- <YStack flex={1} minW={240}>
32
- {children}
33
- </YStack>
34
- </XStack>
35
- )
36
- }
37
-
38
- export function FieldText({
39
- value,
40
- onChange,
41
- disabled,
42
- secure,
43
- placeholder,
44
- }: {
45
- value: string
46
- onChange: (v: string) => void
47
- disabled?: boolean
48
- secure?: boolean
49
- placeholder?: string
50
- }) {
51
- return (
52
- <Input
53
- value={value}
54
- onChangeText={onChange}
55
- disabled={disabled}
56
- secureTextEntry={secure}
57
- placeholder={placeholder}
58
- autoCapitalize="none"
59
- />
60
- )
61
- }
62
-
63
- export function FieldTextArea({
64
- value,
65
- onChange,
66
- disabled,
67
- rows = 6,
68
- }: {
69
- value: string
70
- onChange: (v: string) => void
71
- disabled?: boolean
72
- rows?: number
73
- }) {
74
- return (
75
- <TextArea value={value} onChangeText={onChange} disabled={disabled} numberOfLines={rows} />
76
- )
77
- }
78
-
79
- export function FieldSwitch({
80
- checked,
81
- onChange,
82
- disabled,
83
- }: {
84
- checked: boolean
85
- onChange: (v: boolean) => void
86
- disabled?: boolean
87
- }) {
88
- return (
89
- <Switch checked={checked} onCheckedChange={onChange} disabled={disabled} size="$3">
90
- <Switch.Thumb />
91
- </Switch>
92
- )
93
- }
94
-
95
- export function FieldSelect({
96
- value,
97
- options,
98
- onChange,
99
- disabled,
100
- }: {
101
- value: string
102
- options: string[]
103
- onChange: (v: string) => void
104
- disabled?: boolean
105
- }) {
106
- return (
107
- <Select value={value} onValueChange={onChange} disablePreventBodyScroll native>
108
- <Select.Trigger disabled={disabled} iconAfter={<ChevronDown size={16} />}>
109
- <Select.Value placeholder="Select…" />
110
- </Select.Trigger>
111
- <Select.Content>
112
- <Select.Viewport>
113
- {options.map((opt, i) => (
114
- <Select.Item key={opt} index={i} value={opt}>
115
- <Select.ItemText>{opt}</Select.ItemText>
116
- </Select.Item>
117
- ))}
118
- </Select.Viewport>
119
- </Select.Content>
120
- </Select>
121
- )
122
- }
123
-
124
- export function FieldSlider({
125
- value,
126
- min,
127
- max,
128
- step,
129
- onChange,
130
- disabled,
131
- }: {
132
- value: number
133
- min: number
134
- max: number
135
- step: number
136
- onChange: (v: number) => void
137
- disabled?: boolean
138
- }) {
139
- return (
140
- <XStack gap="$3" items="center">
141
- <Text width={56} fontSize="$3" color="$color11">
142
- {value}
143
- </Text>
144
- <Slider
145
- flex={1}
146
- min={min}
147
- max={max}
148
- step={step}
149
- value={[value]}
150
- onValueChange={(v) => onChange(v[0] ?? min)}
151
- disabled={disabled}
152
- >
153
- <Slider.Track>
154
- <Slider.TrackActive />
155
- </Slider.Track>
156
- <Slider.Thumb index={0} circular />
157
- </Slider>
158
- </XStack>
159
- )
160
- }
@@ -1,31 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * The Hanzo "H" mark — the canonical 7-path shaded glyph: five body blocks +
5
- * two shade slivers (geometry canon: @hanzo/logo `MARK_PATHS`).
6
- *
7
- * Rendered as inline SVG with `fill: currentColor` (shade at reduced opacity),
8
- * so it inherits the surrounding text color and adapts to the dark/light theme
9
- * with no per-theme asset. ONE source for the mark across the chrome.
10
- */
11
- export function HanzoMark({ size = 22, color = 'currentColor' }: { size?: number; color?: string }) {
12
- return (
13
- <svg
14
- width={size}
15
- height={size}
16
- viewBox="0 0 67 67"
17
- role="img"
18
- aria-label="Hanzo"
19
- fill={color}
20
- style={{ display: 'block', flexShrink: 0 }}
21
- >
22
- <path d="M22.21 67V44.6369H0V67H22.21Z" />
23
- <path opacity={0.55} d="M0 44.6369L22.21 46.8285V44.6369H0Z" />
24
- <path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" />
25
- <path d="M22.21 0H0V22.3184H22.21V0Z" />
26
- <path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" />
27
- <path opacity={0.55} d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" />
28
- <path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" />
29
- </svg>
30
- )
31
- }
@@ -1,253 +0,0 @@
1
- 'use client'
2
-
3
- /**
4
- * Console metric primitives — the ONE shared set of stat tiles, panels, legend
5
- * rows, util bars, and "real action or honest-disabled" buttons that every
6
- * resource console (GPUs, Kubernetes, Containers, Training, Tasks) renders.
7
- *
8
- * These were first written for the GPU surface; promoted here so a sibling
9
- * product never reaches into `gpus/` for shell chrome (separation of concerns).
10
- * `gpus/charts.tsx` now re-exports them, so there is exactly one definition.
11
- *
12
- * Honest by construction: a `MetricCard` shows the pre-formatted `value` the
13
- * caller passes (an absent metric is an em-dash, never a fabricated number); a
14
- * `Sparkline`/`MiniBars` with no real series renders nothing; a `HintButton`
15
- * with no `onPress` is visibly disabled with a tooltip saying WHY. Inline SVG is
16
- * the house pattern (see ui/Loader, ui/HanzoMark); style props use the v5
17
- * shorthand set.
18
- */
19
- import type { ReactElement, ReactNode } from 'react'
20
- import { Button, Card, Text, XStack, YStack } from '@hanzo/gui'
21
-
22
- /** Distinct, theme-neutral series colors (mid-saturation reads on dark + light). */
23
- export const SERIES = ['#6ea8fe', '#7ee787', '#f0a868', '#c792ea', '#56d4c4', '#e879a6', '#d6c15a', '#8b9bb4'] as const
24
- export const colorForIndex = (i: number): string => SERIES[i % SERIES.length]
25
- const TRACK = 'rgba(128,128,128,0.18)'
26
-
27
- /** Tone for a utilization/temperature value (green calm → red hot). */
28
- export function utilColor(pct: number): string {
29
- if (pct >= 90) return '#e5534b'
30
- if (pct >= 70) return '#f0a868'
31
- return '#7ee787'
32
- }
33
-
34
- /**
35
- * A single-series sparkline over real points. Renders nothing meaningful for <2
36
- * points (the caller shows `—` instead). The y-range is the data's own min/max.
37
- */
38
- export function Sparkline({
39
- points,
40
- width = 104,
41
- height = 30,
42
- color = SERIES[0],
43
- }: {
44
- points: number[]
45
- width?: number
46
- height?: number
47
- color?: string
48
- }): ReactElement | null {
49
- if (!points || points.length < 2) return null
50
- const min = Math.min(...points)
51
- const max = Math.max(...points)
52
- const span = max - min || 1
53
- const stepX = width / (points.length - 1)
54
- const y = (v: number) => height - 2 - ((v - min) / span) * (height - 4)
55
- const d = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${(i * stepX).toFixed(1)},${y(p).toFixed(1)}`).join(' ')
56
- return (
57
- <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} role="img" aria-label="trend">
58
- <path d={d} fill="none" stroke={color} strokeWidth={1.5} strokeLinejoin="round" strokeLinecap="round" />
59
- </svg>
60
- )
61
- }
62
-
63
- /** A small vertical-bar chart (e.g. cost per day). One bar per real datum. */
64
- export function MiniBars({
65
- bars,
66
- width = 320,
67
- height = 96,
68
- color = SERIES[0],
69
- }: {
70
- bars: { label: string; value: number }[]
71
- width?: number
72
- height?: number
73
- color?: string
74
- }): ReactElement | null {
75
- if (!bars.length) return null
76
- const max = Math.max(...bars.map((b) => b.value), 1)
77
- const gap = 4
78
- const bw = (width - gap * (bars.length - 1)) / bars.length
79
- return (
80
- <svg width="100%" height={height} viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" role="img" aria-label="bars">
81
- {bars.map((b, i) => {
82
- const h = Math.max(1, (b.value / max) * (height - 2))
83
- return (
84
- <rect key={b.label + i} x={i * (bw + gap)} y={height - h} width={bw} height={h} rx={2} fill={color} opacity={0.85}>
85
- <title>{`${b.label}: ${b.value}`}</title>
86
- </rect>
87
- )
88
- })}
89
- </svg>
90
- )
91
- }
92
-
93
- /**
94
- * A thin horizontal bar (0–100). By default the fill tones by value (green→red,
95
- * for utilization/temperature). Pass a fixed `color` for a neutral meter like
96
- * training PROGRESS, where a high value is good, not "hot".
97
- */
98
- export function UtilBar({ value, width = 120, color }: { value: number; width?: number; color?: string }): ReactElement {
99
- const v = Math.max(0, Math.min(100, value))
100
- return (
101
- <svg width={width} height={8} viewBox={`0 0 ${width} 8`} role="img" aria-label={`${Math.round(v)}%`}>
102
- <rect x={0} y={1} width={width} height={6} rx={3} fill={TRACK} />
103
- <rect x={0} y={1} width={(v / 100) * width} height={6} rx={3} fill={color ?? utilColor(v)} />
104
- </svg>
105
- )
106
- }
107
-
108
- /** A legend row: color swatch + label + value. */
109
- export function LegendDot({ color, label, value }: { color: string; label: string; value?: ReactNode }) {
110
- return (
111
- <XStack items="center" gap="$2" justify="space-between">
112
- <XStack items="center" gap="$2">
113
- <YStack width={10} height={10} rounded="$1" bg={color as never} />
114
- <Text fontSize="$2" color="$color11">
115
- {label}
116
- </Text>
117
- </XStack>
118
- {value != null ? (
119
- <Text fontSize="$2" color="$color12" fontWeight="600">
120
- {value}
121
- </Text>
122
- ) : null}
123
- </XStack>
124
- )
125
- }
126
-
127
- /**
128
- * One overview metric tile. `value` is pre-formatted by the caller (so an absent
129
- * metric is an honest `—`, never a fabricated number). The sparkline only appears
130
- * when the caller has a REAL series; the delta only when a real comparison exists.
131
- */
132
- export function MetricCard({
133
- icon,
134
- label,
135
- value,
136
- caption,
137
- spark,
138
- sparkColor,
139
- delta,
140
- }: {
141
- icon: ReactElement
142
- label: string
143
- value: string
144
- caption?: string
145
- spark?: number[]
146
- sparkColor?: string
147
- delta?: { pct: number }
148
- }) {
149
- const up = delta ? delta.pct >= 0 : false
150
- return (
151
- <Card p="$3.5" gap="$2" borderWidth={1} borderColor="$borderColor" flex={1} minW={172}>
152
- <XStack items="center" gap="$2" justify="space-between">
153
- <XStack items="center" gap="$2">
154
- {icon}
155
- <Text fontSize="$2" color="$color11" fontWeight="600">
156
- {label}
157
- </Text>
158
- </XStack>
159
- {delta ? (
160
- <Text fontSize="$1" color={up ? '#7ee787' : '#e5534b'}>
161
- {up ? '▲' : '▼'} {Math.abs(delta.pct)}%
162
- </Text>
163
- ) : null}
164
- </XStack>
165
- <XStack items="flex-end" justify="space-between" gap="$2">
166
- <Text fontSize="$8" fontWeight="900" color="$color12" numberOfLines={1}>
167
- {value}
168
- </Text>
169
- {spark && spark.length >= 2 ? <Sparkline points={spark} color={sparkColor ?? SERIES[0]} /> : null}
170
- </XStack>
171
- {caption ? (
172
- <Text fontSize="$1" color="$color10" numberOfLines={1}>
173
- {caption}
174
- </Text>
175
- ) : null}
176
- </Card>
177
- )
178
- }
179
-
180
- /**
181
- * A button that is either a real action (onPress) or HONESTLY disabled with a
182
- * native tooltip explaining why (the action has no backend yet). One way to
183
- * render the "real route or honest-disabled" affordance the consoles use.
184
- */
185
- export function HintButton({
186
- icon,
187
- iconAfter,
188
- children,
189
- onPress,
190
- disabled,
191
- hint,
192
- theme,
193
- }: {
194
- icon?: ReactElement
195
- iconAfter?: ReactElement
196
- children: ReactNode
197
- onPress?: () => void
198
- disabled?: boolean
199
- /** Native tooltip text (shown on hover) — say WHY it is disabled. */
200
- hint?: string
201
- theme?: 'light'
202
- }) {
203
- const btn = (
204
- <Button
205
- size="$2"
206
- theme={theme}
207
- icon={icon}
208
- iconAfter={iconAfter}
209
- disabled={disabled}
210
- onPress={disabled ? undefined : onPress}
211
- >
212
- {children}
213
- </Button>
214
- )
215
- if (!hint) return btn
216
- return (
217
- <span title={hint} style={{ display: 'inline-flex' }}>
218
- {btn}
219
- </span>
220
- )
221
- }
222
-
223
- /**
224
- * A titled panel wrapper used across the consoles (chart + list cards). `grow`
225
- * (default true) sets `flex={1}` so panels SHARE width in a ROW; pass `grow={false}`
226
- * inside a vertical rail (a column YStack) so each panel sizes to its content and
227
- * stacks — flex-grow in a column with no fixed height collapses them onto each other.
228
- */
229
- export function Panel({
230
- title,
231
- right,
232
- children,
233
- minW = 280,
234
- grow = true,
235
- }: {
236
- title: string
237
- right?: ReactNode
238
- children: ReactNode
239
- minW?: number
240
- grow?: boolean
241
- }) {
242
- return (
243
- <Card p="$4" gap="$3" borderWidth={1} borderColor="$borderColor" flex={grow ? 1 : undefined} minW={minW} width={grow ? undefined : '100%'}>
244
- <XStack items="center" justify="space-between" gap="$2">
245
- <Text fontSize="$4" fontWeight="800" color="$color12">
246
- {title}
247
- </Text>
248
- {right}
249
- </XStack>
250
- {children}
251
- </Card>
252
- )
253
- }