@hanzo/ui 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +63 -4
- package/src/billing.ts +9 -0
- package/src/canvas.ts +11 -0
- package/src/cd/layout.ts +184 -0
- package/src/cd/status.ts +158 -0
- package/src/cd/time.ts +52 -0
- package/src/cd/types.ts +318 -0
- package/src/dashboard.ts +8 -0
- package/src/gitops.ts +5 -0
- package/src/network.ts +11 -0
- package/src/product/AppHeader.tsx +175 -0
- package/src/product/BrandMark.tsx +27 -0
- package/src/product/HanzoMark.tsx +7 -4
- package/src/product/OrgSwitcher.tsx +332 -0
- package/src/product/index.ts +10 -5
- package/src/product/scope.test.ts +92 -0
- package/src/product/scope.ts +123 -0
- package/src/usage.ts +8 -0
- package/src/wallet.ts +11 -0
- package/types/billing.d.ts +2 -0
- package/types/billing.d.ts.map +1 -0
- package/types/canvas.d.ts +2 -0
- package/types/canvas.d.ts.map +1 -0
- package/types/cd/layout.d.ts +57 -0
- package/types/cd/layout.d.ts.map +1 -0
- package/types/cd/status.d.ts +50 -0
- package/types/cd/status.d.ts.map +1 -0
- package/types/cd/time.d.ts +15 -0
- package/types/cd/time.d.ts.map +1 -0
- package/types/cd/types.d.ts +269 -0
- package/types/cd/types.d.ts.map +1 -0
- package/types/dashboard.d.ts +2 -0
- package/types/dashboard.d.ts.map +1 -0
- package/types/gitops.d.ts +2 -0
- package/types/gitops.d.ts.map +1 -0
- package/types/network.d.ts +2 -0
- package/types/network.d.ts.map +1 -0
- package/types/product/AppHeader.d.ts +49 -0
- package/types/product/AppHeader.d.ts.map +1 -0
- package/types/product/BrandMark.d.ts +8 -0
- package/types/product/BrandMark.d.ts.map +1 -0
- package/types/product/Charts.d.ts +5 -5
- package/types/product/Charts.d.ts.map +1 -1
- package/types/product/ComboBox.d.ts +1 -1
- package/types/product/ComboBox.d.ts.map +1 -1
- package/types/product/DataTable.d.ts +1 -1
- package/types/product/DataTable.d.ts.map +1 -1
- package/types/product/Donut.d.ts +1 -1
- package/types/product/Donut.d.ts.map +1 -1
- package/types/product/EmptyState.d.ts +1 -1
- package/types/product/EmptyState.d.ts.map +1 -1
- package/types/product/FadeIn.d.ts +1 -1
- package/types/product/FadeIn.d.ts.map +1 -1
- package/types/product/Field.d.ts +6 -6
- package/types/product/Field.d.ts.map +1 -1
- package/types/product/HanzoMark.d.ts +6 -5
- package/types/product/HanzoMark.d.ts.map +1 -1
- package/types/product/Metric.d.ts +4 -4
- package/types/product/Metric.d.ts.map +1 -1
- package/types/product/OrgSwitcher.d.ts +19 -0
- package/types/product/OrgSwitcher.d.ts.map +1 -0
- package/types/product/PageHeader.d.ts +1 -1
- package/types/product/PageHeader.d.ts.map +1 -1
- package/types/product/PrimaryButton.d.ts +1 -1
- package/types/product/PrimaryButton.d.ts.map +1 -1
- package/types/product/ProductIcon.d.ts +1 -1
- package/types/product/ProductIcon.d.ts.map +1 -1
- package/types/product/ProviderLogo.d.ts +2 -2
- package/types/product/ProviderLogo.d.ts.map +1 -1
- package/types/product/Reorder.d.ts +1 -1
- package/types/product/Reorder.d.ts.map +1 -1
- package/types/product/SelectMenu.d.ts +1 -1
- package/types/product/SelectMenu.d.ts.map +1 -1
- package/types/product/SlideOver.d.ts +1 -1
- package/types/product/SlideOver.d.ts.map +1 -1
- package/types/product/StatusTag.d.ts +1 -1
- package/types/product/StatusTag.d.ts.map +1 -1
- package/types/product/ThemeToggle.d.ts +1 -1
- package/types/product/ThemeToggle.d.ts.map +1 -1
- package/types/product/Toast.d.ts +1 -1
- package/types/product/Toast.d.ts.map +1 -1
- package/types/product/index.d.ts +4 -2
- package/types/product/index.d.ts.map +1 -1
- package/types/product/scope.d.ts +56 -0
- package/types/product/scope.d.ts.map +1 -0
- package/types/usage.d.ts +2 -0
- package/types/usage.d.ts.map +1 -0
- package/types/wallet.d.ts +2 -0
- package/types/wallet.d.ts.map +1 -0
- package/src/product/social/CampaignCard.tsx +0 -62
- package/src/product/social/ChannelBadge.tsx +0 -60
- package/src/product/social/PostCard.tsx +0 -80
- package/src/product/social/index.ts +0 -6
- package/src/product/usage/UsageDashboard.tsx +0 -54
- package/src/product/usage/UsageMeter.tsx +0 -75
- package/src/product/usage/UsageProviderCard.tsx +0 -153
- package/src/product/usage/index.ts +0 -10
- package/types/product/social/CampaignCard.d.ts +0 -14
- package/types/product/social/CampaignCard.d.ts.map +0 -1
- package/types/product/social/ChannelBadge.d.ts +0 -7
- package/types/product/social/ChannelBadge.d.ts.map +0 -1
- package/types/product/social/PostCard.d.ts +0 -16
- package/types/product/social/PostCard.d.ts.map +0 -1
- package/types/product/social/index.d.ts +0 -4
- package/types/product/social/index.d.ts.map +0 -1
- package/types/product/usage/UsageDashboard.d.ts +0 -10
- package/types/product/usage/UsageDashboard.d.ts.map +0 -1
- package/types/product/usage/UsageMeter.d.ts +0 -15
- package/types/product/usage/UsageMeter.d.ts.map +0 -1
- package/types/product/usage/UsageProviderCard.d.ts +0 -35
- package/types/product/usage/UsageProviderCard.d.ts.map +0 -1
- package/types/product/usage/index.d.ts +0 -4
- package/types/product/usage/index.d.ts.map +0 -1
package/src/cd/types.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hanzo/ui/cd — the Hanzo CD (continuous-delivery) data contract.
|
|
3
|
+
*
|
|
4
|
+
* This is the FE↔BE handshake for the `/v1/gitops` API. Every component in this
|
|
5
|
+
* module is presentational and data-prop-driven: a host console fetches these
|
|
6
|
+
* rows (via the injected `GitopsClient`) and hands them to the views. The shapes
|
|
7
|
+
* live in ONE place so the pure folds (status → tone, tree → graph, diff → hunks)
|
|
8
|
+
* are unit-tested in isolation and the visual components never reach into a data
|
|
9
|
+
* layer.
|
|
10
|
+
*
|
|
11
|
+
* The vocabulary mirrors a GitOps engine's model (health/sync verdicts, the
|
|
12
|
+
* owner-reference resource graph, operation phases) so a backend that projects a
|
|
13
|
+
* cluster's live state maps onto it directly, but the names are engine-neutral.
|
|
14
|
+
*
|
|
15
|
+
* Attribution: the information design of these views is a derivative port of the
|
|
16
|
+
* Argo CD web UI (Apache-2.0). See ./NOTICE.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Status vocabularies — small, glanceable, closed unions.
|
|
21
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A resource/application health verdict. `Unknown` is honest — a missing signal
|
|
25
|
+
* is never guessed up to `Healthy`.
|
|
26
|
+
*/
|
|
27
|
+
export type HealthStatus =
|
|
28
|
+
| 'Healthy'
|
|
29
|
+
| 'Progressing'
|
|
30
|
+
| 'Degraded'
|
|
31
|
+
| 'Suspended'
|
|
32
|
+
| 'Missing'
|
|
33
|
+
| 'Unknown'
|
|
34
|
+
|
|
35
|
+
/** Whether live state matches the desired (Git) state. */
|
|
36
|
+
export type SyncStatus = 'Synced' | 'OutOfSync' | 'Unknown'
|
|
37
|
+
|
|
38
|
+
/** The phase of an in-flight sync/rollback operation. */
|
|
39
|
+
export type OperationPhase =
|
|
40
|
+
| 'Running'
|
|
41
|
+
| 'Terminating'
|
|
42
|
+
| 'Failed'
|
|
43
|
+
| 'Error'
|
|
44
|
+
| 'Succeeded'
|
|
45
|
+
| 'Pending'
|
|
46
|
+
|
|
47
|
+
/** A pod's lifecycle phase (drives the pod-status dot). */
|
|
48
|
+
export type PodPhase = 'Pending' | 'Running' | 'Succeeded' | 'Failed' | 'Unknown'
|
|
49
|
+
|
|
50
|
+
/** Per-resource result code within a sync operation. */
|
|
51
|
+
export type SyncResultCode = 'Synced' | 'Pruned' | 'SyncFailed' | 'Unknown'
|
|
52
|
+
|
|
53
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
// Resource graph — the owner-reference tree of live cluster objects.
|
|
55
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/** A group/version/kind/namespace/name coordinate for a cluster object. */
|
|
58
|
+
export interface ResourceRef {
|
|
59
|
+
/** Stable unique id (k8s uid, or a synthesized `group/kind/ns/name`). */
|
|
60
|
+
uid: string
|
|
61
|
+
group: string
|
|
62
|
+
version: string
|
|
63
|
+
kind: string
|
|
64
|
+
namespace: string
|
|
65
|
+
name: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** A labelled datum shown as a chip on a node / in the summary (e.g. `Revision: 3`). */
|
|
69
|
+
export interface InfoItem {
|
|
70
|
+
name: string
|
|
71
|
+
value: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Ingress/service surface of a networked resource (Service, Ingress). */
|
|
75
|
+
export interface ResourceNetworking {
|
|
76
|
+
/** Objects this resource routes to (Service → Pods, Ingress → Service). */
|
|
77
|
+
targetRefs?: ResourceRef[]
|
|
78
|
+
labels?: Record<string, string>
|
|
79
|
+
/** Externally reachable URLs (a domain's live site). */
|
|
80
|
+
externalURLs?: string[]
|
|
81
|
+
ingress?: { hostname?: string; ip?: string }[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* One node of the live resource graph. Ownership is expressed by `parentRefs`
|
|
86
|
+
* (an object may have several owners); the graph is the transitive closure of
|
|
87
|
+
* those edges rooted at the application. Pure data — no React, no back-refs.
|
|
88
|
+
*/
|
|
89
|
+
export interface ResourceNode extends ResourceRef {
|
|
90
|
+
/** The object(s) that own this one — the edges of the graph. */
|
|
91
|
+
parentRefs?: ResourceRef[]
|
|
92
|
+
/** Health verdict for this object, when the engine computes one. */
|
|
93
|
+
health?: { status: HealthStatus; message?: string }
|
|
94
|
+
/** Sync verdict for this object (managed objects only). */
|
|
95
|
+
status?: SyncStatus
|
|
96
|
+
/** Glanceable facts shown as chips (revision, replicas, image tag, …). */
|
|
97
|
+
info?: InfoItem[]
|
|
98
|
+
networkingInfo?: ResourceNetworking
|
|
99
|
+
images?: string[]
|
|
100
|
+
createdAt?: string
|
|
101
|
+
/** True for hook/sync-operation objects; `resourceVersion` for freshness. */
|
|
102
|
+
hook?: boolean
|
|
103
|
+
resourceVersion?: string
|
|
104
|
+
/** Present on Pod nodes — drives pod-specific rendering. */
|
|
105
|
+
podInfo?: PodInfo
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Extra facts a Pod node carries (containers, phase, restarts, node/host). */
|
|
109
|
+
export interface PodInfo {
|
|
110
|
+
phase: PodPhase
|
|
111
|
+
/** Reason string when the phase alone is ambiguous (CrashLoopBackOff, …). */
|
|
112
|
+
reason?: string
|
|
113
|
+
ready?: string // e.g. "2/3"
|
|
114
|
+
restarts?: number
|
|
115
|
+
node?: string
|
|
116
|
+
age?: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The full resource graph for an application: the managed nodes plus any
|
|
121
|
+
* orphaned objects (live in the app's namespace but not owned by it).
|
|
122
|
+
*/
|
|
123
|
+
export interface ResourceTree {
|
|
124
|
+
nodes: ResourceNode[]
|
|
125
|
+
orphanedNodes?: ResourceNode[]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
129
|
+
// Application — the deployable unit the list shows and the detail drills into.
|
|
130
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/** Where an application's desired state comes from (a Git repo path @ revision). */
|
|
133
|
+
export interface AppSource {
|
|
134
|
+
repoURL: string
|
|
135
|
+
path?: string
|
|
136
|
+
targetRevision?: string
|
|
137
|
+
chart?: string
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The destination cluster + namespace an application deploys to. */
|
|
141
|
+
export interface AppDestination {
|
|
142
|
+
server?: string
|
|
143
|
+
name?: string
|
|
144
|
+
namespace: string
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Revision metadata resolved from the source repo (for the status panel). */
|
|
148
|
+
export interface RevisionMetadata {
|
|
149
|
+
revision: string
|
|
150
|
+
author?: string
|
|
151
|
+
date?: string
|
|
152
|
+
message?: string
|
|
153
|
+
tags?: string[]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* A summary of one application — the shape the list/tiles render. Enough to draw
|
|
158
|
+
* a row without fetching the tree.
|
|
159
|
+
*/
|
|
160
|
+
export interface AppSummary {
|
|
161
|
+
name: string
|
|
162
|
+
namespace?: string
|
|
163
|
+
project?: string
|
|
164
|
+
health: HealthStatus
|
|
165
|
+
sync: SyncStatus
|
|
166
|
+
/** Short revision (sha/tag) currently reconciled. */
|
|
167
|
+
revision?: string
|
|
168
|
+
source?: AppSource
|
|
169
|
+
destination?: AppDestination
|
|
170
|
+
/** Epoch ms of the last successful sync (rendered relative). */
|
|
171
|
+
lastSyncedAt?: number
|
|
172
|
+
/** Phase of an in-flight operation, when one is running. */
|
|
173
|
+
operationPhase?: OperationPhase
|
|
174
|
+
/** Rollup counts of managed resources by health, for the tile mini-bar. */
|
|
175
|
+
resourceCounts?: Partial<Record<HealthStatus, number>>
|
|
176
|
+
labels?: Record<string, string>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** A single sync/deploy in the history (for the rollback list). */
|
|
180
|
+
export interface DeployRevision {
|
|
181
|
+
/** Monotonic history id — the handle passed to `rollback`. */
|
|
182
|
+
id: number
|
|
183
|
+
revision: string
|
|
184
|
+
deployedAt?: number
|
|
185
|
+
source?: AppSource
|
|
186
|
+
metadata?: RevisionMetadata
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The result of (or progress of) the most recent sync/rollback operation. */
|
|
190
|
+
export interface OperationState {
|
|
191
|
+
phase: OperationPhase
|
|
192
|
+
message?: string
|
|
193
|
+
startedAt?: number
|
|
194
|
+
finishedAt?: number
|
|
195
|
+
/** The revision the operation targeted. */
|
|
196
|
+
revision?: string
|
|
197
|
+
/** Per-resource results (kind/name → code + message). */
|
|
198
|
+
syncResult?: {
|
|
199
|
+
ref: ResourceRef
|
|
200
|
+
result: SyncResultCode
|
|
201
|
+
message?: string
|
|
202
|
+
hookPhase?: string
|
|
203
|
+
}[]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The full application detail — everything the status panel + controls need
|
|
208
|
+
* beyond the tree. Returned by `getApp`.
|
|
209
|
+
*/
|
|
210
|
+
export interface AppDetail extends AppSummary {
|
|
211
|
+
/** Resolved metadata for the reconciled revision. */
|
|
212
|
+
revisionMetadata?: RevisionMetadata
|
|
213
|
+
/** Deploy/rollout history, newest last (matches engine ordering). */
|
|
214
|
+
history?: DeployRevision[]
|
|
215
|
+
/** In-flight or last-completed operation. */
|
|
216
|
+
operationState?: OperationState
|
|
217
|
+
/** Free-form conditions/warnings surfaced by the engine. */
|
|
218
|
+
conditions?: { type: string; message: string; lastTransitionTime?: string }[]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
222
|
+
// Node detail — manifest + events + logs for the drawer.
|
|
223
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
/** A k8s-style event row shown in the node drawer's Events tab. */
|
|
226
|
+
export interface ResourceEvent {
|
|
227
|
+
reason: string
|
|
228
|
+
message: string
|
|
229
|
+
type?: 'Normal' | 'Warning'
|
|
230
|
+
count?: number
|
|
231
|
+
firstSeen?: string
|
|
232
|
+
lastSeen?: string
|
|
233
|
+
source?: string
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** One log line (optionally tagged with its container + a parsed timestamp). */
|
|
237
|
+
export interface LogEntry {
|
|
238
|
+
content: string
|
|
239
|
+
timestamp?: string
|
|
240
|
+
container?: string
|
|
241
|
+
/** True for stderr — rendered in the warn tone. */
|
|
242
|
+
err?: boolean
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Everything the node drawer needs for a single resource: the live manifest
|
|
247
|
+
* (YAML), recent events, a recent-logs snapshot (pods), and the desired/live
|
|
248
|
+
* pair for the inline diff. Returned by `getResource`.
|
|
249
|
+
*/
|
|
250
|
+
export interface ResourceDetail {
|
|
251
|
+
ref: ResourceRef
|
|
252
|
+
health?: { status: HealthStatus; message?: string }
|
|
253
|
+
syncStatus?: SyncStatus
|
|
254
|
+
/** Live manifest as YAML (or JSON) text. */
|
|
255
|
+
liveManifest?: string
|
|
256
|
+
/** Desired manifest as YAML text — enables the drawer's inline diff. */
|
|
257
|
+
desiredManifest?: string
|
|
258
|
+
events?: ResourceEvent[]
|
|
259
|
+
/** Recent log lines for a pod/container (a snapshot; streaming is optional). */
|
|
260
|
+
logs?: LogEntry[]
|
|
261
|
+
/** Container names, when the resource is a Pod (drives the log selector). */
|
|
262
|
+
containers?: string[]
|
|
263
|
+
info?: InfoItem[]
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
267
|
+
// Diff — desired vs live for the whole application.
|
|
268
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
/** One resource's desired/live manifest pair for the application diff view. */
|
|
271
|
+
export interface ResourceDiff {
|
|
272
|
+
ref: ResourceRef
|
|
273
|
+
/** Sync verdict for this resource (drives the section pill). */
|
|
274
|
+
status?: SyncStatus
|
|
275
|
+
desired?: string
|
|
276
|
+
live?: string
|
|
277
|
+
/** True when the resource is unmanaged/hook (rendered muted). */
|
|
278
|
+
hook?: boolean
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The full application diff payload. Returned by `getDiff`. */
|
|
282
|
+
export interface AppDiff {
|
|
283
|
+
items: ResourceDiff[]
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
287
|
+
// Mutations — sync + rollback request bodies.
|
|
288
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
/** Body for `POST /v1/gitops/{name}/sync`. */
|
|
291
|
+
export interface SyncRequest {
|
|
292
|
+
/** Target revision; omit to sync to the source's configured revision. */
|
|
293
|
+
revision?: string
|
|
294
|
+
/** Prune resources that no longer exist in the desired state. */
|
|
295
|
+
prune?: boolean
|
|
296
|
+
/** Server-side dry-run (compute the plan without applying). */
|
|
297
|
+
dryRun?: boolean
|
|
298
|
+
/** Restrict the sync to specific resources (default: all). */
|
|
299
|
+
resources?: ResourceRef[]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Body for `POST /v1/gitops/{name}/rollback`. */
|
|
303
|
+
export interface RollbackRequest {
|
|
304
|
+
/** The history id (from `DeployRevision.id`) to roll back to. */
|
|
305
|
+
id: number
|
|
306
|
+
prune?: boolean
|
|
307
|
+
dryRun?: boolean
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** The response to a sync/rollback — the operation the engine kicked off. */
|
|
311
|
+
export interface OperationResponse {
|
|
312
|
+
operationState: OperationState
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** The apps-list response: `GET /v1/gitops`. */
|
|
316
|
+
export interface AppListResponse {
|
|
317
|
+
items: AppSummary[]
|
|
318
|
+
}
|
package/src/dashboard.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @hanzo/ui/dashboard — the console dashboard kit (landing + deploy pipeline +
|
|
2
|
+
// composable overview primitives + charts/motion layer), re-exported from its
|
|
3
|
+
// home package @hanzo/dashboard. Thin subpath:
|
|
4
|
+
//
|
|
5
|
+
// import { ... } from '@hanzo/ui/dashboard'
|
|
6
|
+
//
|
|
7
|
+
// Optional peer — only pulled when the subpath is used.
|
|
8
|
+
export * from '@hanzo/dashboard'
|
package/src/gitops.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @hanzo/ui/gitops — the CD/GitOps surface, re-exported from its home package
|
|
2
|
+
// @hanzo/gitops (an Apache-2.0 clean-room port of the Argo CD UI). Kept as a thin
|
|
3
|
+
// subpath so a console can `import { GitopsAppList, GitopsAppTree, GitopsNodeInfo,
|
|
4
|
+
// HealthBadge, ... } from '@hanzo/ui/gitops'` while the code lives in one place.
|
|
5
|
+
export * from '@hanzo/gitops'
|
package/src/network.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @hanzo/ui/network — the ONE network selector, re-exported from its home module
|
|
2
|
+
// @hanzo/ui-shadcn/network. `NetworkSwitcher` renders the selected environment
|
|
3
|
+
// and a menu of configured networks + a custom-endpoint form, backed by the
|
|
4
|
+
// shared selected-network store; `configureNetworks` swaps the network set so a
|
|
5
|
+
// downstream brand can white-label it. Thin subpath:
|
|
6
|
+
//
|
|
7
|
+
// import { NetworkSwitcher, useNetwork, configureNetworks, HANZO_NETWORKS,
|
|
8
|
+
// type Network } from '@hanzo/ui/network'
|
|
9
|
+
//
|
|
10
|
+
// Optional peer — only pulled when the subpath is used.
|
|
11
|
+
export * from '@hanzo/ui-shadcn/network'
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AppHeader — the shared shell header every Hanzo surface renders: the brand
|
|
5
|
+
* mark top-left (from @hanzo/logo, white-label via `wordmark`/`brand`), an
|
|
6
|
+
* org/project slot (usually `<OrgSwitcher/>`), an app switcher listing the
|
|
7
|
+
* Hanzo surfaces (modeled on the console's AppLauncher), and an identity menu
|
|
8
|
+
* (Profile · Theme · Team settings · Billing · Sign out).
|
|
9
|
+
*
|
|
10
|
+
* Host-agnostic: every action is an injected handler; a row renders only when
|
|
11
|
+
* its handler is provided (honest — no dead menu items). Mobile-first: fluid
|
|
12
|
+
* flex + truncation, the wordmark collapse is the brand motion itself.
|
|
13
|
+
*/
|
|
14
|
+
import { useState, type ReactNode } from 'react'
|
|
15
|
+
import { Button, Popover, Separator, Text, XStack, YStack } from '@hanzo/gui'
|
|
16
|
+
import { CreditCard, Grip, LogOut, Settings2, UserRound } from '@hanzogui/lucide-icons-2'
|
|
17
|
+
|
|
18
|
+
import { BrandMark } from './BrandMark'
|
|
19
|
+
import { ThemeToggle } from './ThemeToggle'
|
|
20
|
+
|
|
21
|
+
/** One Hanzo surface the app switcher offers. */
|
|
22
|
+
export type Surface = { id: string; label: string; href: string; hint?: string }
|
|
23
|
+
|
|
24
|
+
/** The five Hanzo surfaces (cloud opens the console). */
|
|
25
|
+
export const SURFACES: Surface[] = [
|
|
26
|
+
{ id: 'ai', label: 'Hanzo AI', href: 'https://hanzo.ai', hint: 'hanzo.ai' },
|
|
27
|
+
{ id: 'cloud', label: 'Cloud', href: 'https://console.hanzo.ai', hint: 'console' },
|
|
28
|
+
{ id: 'console', label: 'Console', href: 'https://console.hanzo.ai', hint: 'console.hanzo.ai' },
|
|
29
|
+
{ id: 'app', label: 'App', href: 'https://hanzo.app', hint: 'hanzo.app' },
|
|
30
|
+
{ id: 'team', label: 'Team', href: 'https://hanzo.team', hint: 'hanzo.team' },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
const openHref = (href: string) => {
|
|
34
|
+
if (typeof window !== 'undefined') window.open(href, '_blank', 'noopener')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function MenuRow({ icon, label, onPress }: { icon: ReactNode; label: string; onPress: () => void }) {
|
|
38
|
+
return (
|
|
39
|
+
<XStack onPress={onPress} cursor="pointer" items="center" gap="$2.5" px="$2" py="$2" rounded="$3" hoverStyle={{ bg: '$color5' }}>
|
|
40
|
+
{icon}
|
|
41
|
+
<Text fontSize="$2" color="$color12">
|
|
42
|
+
{label}
|
|
43
|
+
</Text>
|
|
44
|
+
</XStack>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type AppHeaderProps = {
|
|
49
|
+
/** Brand slot — defaults to the animated `<BrandMark/>`. */
|
|
50
|
+
brand?: ReactNode
|
|
51
|
+
/** White-label wordmark for the default brand mark. */
|
|
52
|
+
wordmark?: string
|
|
53
|
+
/** Brand press (usually "go home"). */
|
|
54
|
+
onBrand?: () => void
|
|
55
|
+
/** Org/project slot — usually `<OrgSwitcher/>` (+ a project switcher). */
|
|
56
|
+
org?: ReactNode
|
|
57
|
+
/** Free slot between the org slot and the right cluster. */
|
|
58
|
+
children?: ReactNode
|
|
59
|
+
/** App-switcher surfaces; [] hides the switcher. */
|
|
60
|
+
surfaces?: Surface[]
|
|
61
|
+
/** Open a surface — default `window.open` (new tab). */
|
|
62
|
+
open?: (surface: Surface) => void
|
|
63
|
+
/** Identity-menu trigger label (the signed-in user's name/email). */
|
|
64
|
+
user?: string
|
|
65
|
+
/** Replace the whole identity menu content. */
|
|
66
|
+
menu?: ReactNode
|
|
67
|
+
/** Theme row content — default `<ThemeToggle/>`; null hides the row. */
|
|
68
|
+
theme?: ReactNode
|
|
69
|
+
onProfile?: () => void
|
|
70
|
+
onTeam?: () => void
|
|
71
|
+
onBilling?: () => void
|
|
72
|
+
onSignOut?: () => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function AppHeader({
|
|
76
|
+
brand,
|
|
77
|
+
wordmark = 'Hanzo',
|
|
78
|
+
onBrand,
|
|
79
|
+
org,
|
|
80
|
+
children,
|
|
81
|
+
surfaces = SURFACES,
|
|
82
|
+
open,
|
|
83
|
+
user,
|
|
84
|
+
menu,
|
|
85
|
+
theme,
|
|
86
|
+
onProfile,
|
|
87
|
+
onTeam,
|
|
88
|
+
onBilling,
|
|
89
|
+
onSignOut,
|
|
90
|
+
}: AppHeaderProps) {
|
|
91
|
+
const [appsOpen, setAppsOpen] = useState(false)
|
|
92
|
+
const [menuOpen, setMenuOpen] = useState(false)
|
|
93
|
+
const launch = (s: Surface) => {
|
|
94
|
+
setAppsOpen(false)
|
|
95
|
+
if (open) open(s)
|
|
96
|
+
else openHref(s.href)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<XStack items="center" gap="$2" px="$3" height={52} borderBottomWidth={1} borderColor="$borderColor" bg="$background">
|
|
101
|
+
<XStack items="center" cursor={onBrand ? 'pointer' : undefined} onPress={onBrand}>
|
|
102
|
+
{brand ?? <BrandMark wordmark={wordmark} />}
|
|
103
|
+
</XStack>
|
|
104
|
+
|
|
105
|
+
{org ? (
|
|
106
|
+
<XStack items="center" minW={0}>
|
|
107
|
+
{org}
|
|
108
|
+
</XStack>
|
|
109
|
+
) : null}
|
|
110
|
+
|
|
111
|
+
<XStack flex={1} items="center" minW={0}>
|
|
112
|
+
{children}
|
|
113
|
+
</XStack>
|
|
114
|
+
|
|
115
|
+
{surfaces.length > 0 ? (
|
|
116
|
+
<Popover open={appsOpen} onOpenChange={setAppsOpen} placement="bottom-end">
|
|
117
|
+
<Popover.Trigger asChild>
|
|
118
|
+
<Button size="$2" chromeless icon={<Grip size={16} />} aria-label="Apps" />
|
|
119
|
+
</Popover.Trigger>
|
|
120
|
+
<Popover.Content bordered elevate p="$2" width={230} bg="$color2" borderColor="$borderColor">
|
|
121
|
+
<YStack gap="$1">
|
|
122
|
+
{surfaces.map((s) => (
|
|
123
|
+
<XStack key={s.id} onPress={() => launch(s)} cursor="pointer" items="center" gap="$2.5" px="$2" py="$2" rounded="$3" hoverStyle={{ bg: '$color5' }}>
|
|
124
|
+
<Text flex={1} fontSize="$2" fontWeight="600" color="$color12">
|
|
125
|
+
{s.label}
|
|
126
|
+
</Text>
|
|
127
|
+
{s.hint ? (
|
|
128
|
+
<Text fontSize="$1" color="$color10">
|
|
129
|
+
{s.hint}
|
|
130
|
+
</Text>
|
|
131
|
+
) : null}
|
|
132
|
+
</XStack>
|
|
133
|
+
))}
|
|
134
|
+
</YStack>
|
|
135
|
+
</Popover.Content>
|
|
136
|
+
</Popover>
|
|
137
|
+
) : null}
|
|
138
|
+
|
|
139
|
+
<Popover open={menuOpen} onOpenChange={setMenuOpen} placement="bottom-end">
|
|
140
|
+
<Popover.Trigger asChild>
|
|
141
|
+
<Button size="$2" chromeless icon={<UserRound size={16} />} aria-label="Account">
|
|
142
|
+
{user ? (
|
|
143
|
+
<Text fontSize="$2" color="$color12" numberOfLines={1} maxW={140}>
|
|
144
|
+
{user}
|
|
145
|
+
</Text>
|
|
146
|
+
) : null}
|
|
147
|
+
</Button>
|
|
148
|
+
</Popover.Trigger>
|
|
149
|
+
<Popover.Content bordered elevate p="$2" width={220} bg="$color2" borderColor="$borderColor">
|
|
150
|
+
{menu ?? (
|
|
151
|
+
<YStack gap="$1">
|
|
152
|
+
{onProfile ? <MenuRow icon={<UserRound size={15} />} label="Profile" onPress={() => (setMenuOpen(false), onProfile())} /> : null}
|
|
153
|
+
{theme !== null ? (
|
|
154
|
+
<XStack items="center" gap="$2.5" px="$2" py="$1" rounded="$3">
|
|
155
|
+
<Text flex={1} fontSize="$2" color="$color12">
|
|
156
|
+
Theme
|
|
157
|
+
</Text>
|
|
158
|
+
{theme ?? <ThemeToggle />}
|
|
159
|
+
</XStack>
|
|
160
|
+
) : null}
|
|
161
|
+
{onTeam ? <MenuRow icon={<Settings2 size={15} />} label="Team settings" onPress={() => (setMenuOpen(false), onTeam())} /> : null}
|
|
162
|
+
{onBilling ? <MenuRow icon={<CreditCard size={15} />} label="Billing" onPress={() => (setMenuOpen(false), onBilling())} /> : null}
|
|
163
|
+
{onSignOut ? (
|
|
164
|
+
<>
|
|
165
|
+
<Separator borderColor="$borderColor" my="$1" />
|
|
166
|
+
<MenuRow icon={<LogOut size={15} />} label="Sign out" onPress={() => (setMenuOpen(false), onSignOut())} />
|
|
167
|
+
</>
|
|
168
|
+
) : null}
|
|
169
|
+
</YStack>
|
|
170
|
+
)}
|
|
171
|
+
</Popover.Content>
|
|
172
|
+
</Popover>
|
|
173
|
+
</XStack>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BrandMark — the canonical brand lockup for an app header, from @hanzo/logo
|
|
5
|
+
* (the ONE home of the mark geometry + motion). Animated by default: intro
|
|
6
|
+
* flip + idle breathing + a wordmark that slides in, holds, then collapses
|
|
7
|
+
* (returns on hover). Pure CSS, reduced-motion-safe; the mark inherits
|
|
8
|
+
* `currentColor` so it themes for free. White-label via `wordmark`.
|
|
9
|
+
*/
|
|
10
|
+
import { HanzoLogo } from '@hanzo/logo/react'
|
|
11
|
+
|
|
12
|
+
import { HanzoMark } from './HanzoMark'
|
|
13
|
+
|
|
14
|
+
export function BrandMark({
|
|
15
|
+
size = 20,
|
|
16
|
+
wordmark = 'Hanzo',
|
|
17
|
+
animated = true,
|
|
18
|
+
}: {
|
|
19
|
+
size?: number
|
|
20
|
+
/** Wordmark text (white-label): "Hanzo", "Lux", "Zoo", … */
|
|
21
|
+
wordmark?: string
|
|
22
|
+
/** false = the static mark alone (no motion shell, no wordmark). */
|
|
23
|
+
animated?: boolean
|
|
24
|
+
}) {
|
|
25
|
+
if (!animated) return <HanzoMark size={size} />
|
|
26
|
+
return <HanzoLogo animated size={size} wordmark={wordmark} />
|
|
27
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The Hanzo "H" mark — the canonical
|
|
4
|
+
* The Hanzo "H" mark — the canonical 7-path shaded glyph: five body blocks +
|
|
5
|
+
* two shade slivers (geometry canon: @hanzo/logo `MARK_PATHS`).
|
|
5
6
|
*
|
|
6
|
-
* Rendered as inline SVG with `fill: currentColor
|
|
7
|
-
* surrounding text color and adapts to the dark/light theme
|
|
8
|
-
* asset. ONE source for the mark across the chrome
|
|
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.
|
|
9
10
|
*/
|
|
10
11
|
export function HanzoMark({ size = 22, color = 'currentColor' }: { size?: number; color?: string }) {
|
|
11
12
|
return (
|
|
@@ -19,9 +20,11 @@ export function HanzoMark({ size = 22, color = 'currentColor' }: { size?: number
|
|
|
19
20
|
style={{ display: 'block', flexShrink: 0 }}
|
|
20
21
|
>
|
|
21
22
|
<path d="M22.21 67V44.6369H0V67H22.21Z" />
|
|
23
|
+
<path opacity={0.55} d="M0 44.6369L22.21 46.8285V44.6369H0Z" />
|
|
22
24
|
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" />
|
|
23
25
|
<path d="M22.21 0H0V22.3184H22.21V0Z" />
|
|
24
26
|
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" />
|
|
27
|
+
<path opacity={0.55} d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" />
|
|
25
28
|
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" />
|
|
26
29
|
</svg>
|
|
27
30
|
)
|