@spunto/design-system 0.9.1 → 0.9.2
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/README.md +9 -35
- package/package.json +2 -6
- package/src/components/terminal-panel.tsx +695 -0
- package/src/components/terminal.tsx +152 -1
- package/src/index.ts +13 -6
- package/src/utils.ts +0 -23
- package/src/components/workers/creator-avatar.tsx +0 -38
- package/src/components/workers/git-branch-chips.tsx +0 -120
- package/src/components/workers/index.ts +0 -35
- package/src/components/workers/resource-bars.tsx +0 -100
- package/src/components/workers/setup-progress.tsx +0 -130
- package/src/components/workers/tag-chips.tsx +0 -139
- package/src/components/workers/tag-color.ts +0 -28
- package/src/components/workers/types.ts +0 -44
- package/src/components/workers/worker-card.tsx +0 -309
- package/src/components/workers/worker-status.tsx +0 -181
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// The single state → colours/label table both worker views read from.
|
|
2
|
-
//
|
|
3
|
-
// It used to live twice — `STATUS_CONFIG` in the dashboard and its hand-copied
|
|
4
|
-
// twin in Spunto Lite — and the two had already drifted (different labels for
|
|
5
|
-
// the same docker state, transient states handled in one and not the other).
|
|
6
|
-
// This module is the union of both, plus the transient states (`pulling`,
|
|
7
|
-
// `stopping`, `deleting`) that only ever existed as ad-hoc `if`s above the table.
|
|
8
|
-
//
|
|
9
|
-
// No "use client": pure functions and hookless components, so a React Server
|
|
10
|
-
// Component can call them.
|
|
11
|
-
|
|
12
|
-
import { cn } from "../../utils"
|
|
13
|
-
import type { WorkerCardWorker } from "./types"
|
|
14
|
-
|
|
15
|
-
export type WorkerStatusKey =
|
|
16
|
-
| "running"
|
|
17
|
-
| "stopped"
|
|
18
|
-
| "exited"
|
|
19
|
-
| "created"
|
|
20
|
-
| "not_found"
|
|
21
|
-
| "error"
|
|
22
|
-
| "pending"
|
|
23
|
-
| "setup"
|
|
24
|
-
| "pulling"
|
|
25
|
-
| "stopping"
|
|
26
|
-
| "deleting"
|
|
27
|
-
|
|
28
|
-
export interface WorkerStatusConfig {
|
|
29
|
-
label: string
|
|
30
|
-
/** Class for the 8 px dot. */
|
|
31
|
-
dotClass: string
|
|
32
|
-
/** Classes for the rounded state pill (background + text + border). */
|
|
33
|
-
pillClass: string
|
|
34
|
-
/** Left border accent, for surfaces that stripe the card by state. */
|
|
35
|
-
accent: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Status semantics follow docs/design-system.md: green = running/healthy,
|
|
39
|
-
// amber/yellow = in progress or warning, red = failure, neutral = idle. Errors
|
|
40
|
-
// go through the `destructive` token rather than a raw red so they track the
|
|
41
|
-
// theme; green/yellow have no token, so the Tailwind palette stays.
|
|
42
|
-
const RUNNING: WorkerStatusConfig = {
|
|
43
|
-
label: "Running",
|
|
44
|
-
dotClass: "bg-green-500",
|
|
45
|
-
pillClass: "bg-green-500/10 text-green-600 dark:text-green-400 border-green-500/20",
|
|
46
|
-
accent: "border-l-green-500",
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const IDLE: WorkerStatusConfig = {
|
|
50
|
-
label: "Stopped",
|
|
51
|
-
dotClass: "bg-muted-foreground/50",
|
|
52
|
-
pillClass: "bg-muted text-muted-foreground border-border",
|
|
53
|
-
accent: "border-l-border",
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const BUSY: WorkerStatusConfig = {
|
|
57
|
-
label: "Starting…",
|
|
58
|
-
dotClass: "bg-yellow-400",
|
|
59
|
-
pillClass: "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400 border-yellow-500/20",
|
|
60
|
-
accent: "border-l-yellow-400",
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const workerStatusConfig: Record<WorkerStatusKey, WorkerStatusConfig> = {
|
|
64
|
-
running: RUNNING,
|
|
65
|
-
stopped: IDLE,
|
|
66
|
-
exited: { ...IDLE, label: "Exited" },
|
|
67
|
-
created: { ...BUSY, label: "Created" },
|
|
68
|
-
not_found: {
|
|
69
|
-
label: "Not found",
|
|
70
|
-
dotClass: "bg-orange-400",
|
|
71
|
-
pillClass: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/20",
|
|
72
|
-
accent: "border-l-orange-400",
|
|
73
|
-
},
|
|
74
|
-
error: {
|
|
75
|
-
label: "Error",
|
|
76
|
-
dotClass: "bg-destructive",
|
|
77
|
-
pillClass: "bg-destructive/10 text-destructive border-destructive/20",
|
|
78
|
-
accent: "border-l-destructive",
|
|
79
|
-
},
|
|
80
|
-
pending: BUSY,
|
|
81
|
-
setup: { ...BUSY, label: "Setting up…" },
|
|
82
|
-
pulling: { ...BUSY, label: "Pulling image…" },
|
|
83
|
-
stopping: { ...BUSY, label: "Stopping…" },
|
|
84
|
-
deleting: { ...BUSY, label: "Deleting…" },
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface WorkerStatus extends WorkerStatusConfig {
|
|
88
|
-
key: WorkerStatusKey
|
|
89
|
-
/** The worker is still being provisioned — the setup bar is the interesting part. */
|
|
90
|
-
settingUp: boolean
|
|
91
|
-
/** Container up and past its setup — stats, git chips and the pulsing dot apply. */
|
|
92
|
-
running: boolean
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Lifecycle states that mean "not usable yet, a setup is in flight". */
|
|
96
|
-
const SETUP_STATES = new Set(["provisioning", "starting", "setup"])
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Worker snapshot → one status, tolerant of shapes it has never seen.
|
|
100
|
-
*
|
|
101
|
-
* Order matters: the *lifecycle* state wins whenever it describes an action in
|
|
102
|
-
* flight (deleting, stopping, pulling, setting up), because docker still reports
|
|
103
|
-
* the container as `running` throughout — showing "Running" while a worker is
|
|
104
|
-
* being deleted is the bug this ordering exists to prevent. Past that, the
|
|
105
|
-
* docker state is the truth. Apps with a single `state` field (Spunto Lite) fall
|
|
106
|
-
* through to the last block, and anything unknown lands on `pending` rather than
|
|
107
|
-
* throwing.
|
|
108
|
-
*/
|
|
109
|
-
export function resolveWorkerStatus(worker: WorkerCardWorker): WorkerStatus {
|
|
110
|
-
const state = worker.state ?? null
|
|
111
|
-
const docker = worker.dockerState ?? null
|
|
112
|
-
|
|
113
|
-
const of = (key: WorkerStatusKey, flags?: Partial<Pick<WorkerStatus, "settingUp" | "running">>): WorkerStatus => ({
|
|
114
|
-
key,
|
|
115
|
-
...workerStatusConfig[key],
|
|
116
|
-
settingUp: false,
|
|
117
|
-
running: key === "running",
|
|
118
|
-
...flags,
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
if (state === "deleting") return of("deleting")
|
|
122
|
-
if (state === "stopping") return of("stopping")
|
|
123
|
-
if (state === "pulling") return of("pulling", { settingUp: true })
|
|
124
|
-
if (state && SETUP_STATES.has(state)) return of("setup", { settingUp: true })
|
|
125
|
-
|
|
126
|
-
if (docker && docker in workerStatusConfig) return of(docker as WorkerStatusKey)
|
|
127
|
-
if (docker) return of("pending")
|
|
128
|
-
|
|
129
|
-
// Single-`state` apps (Lite): no docker level to consult.
|
|
130
|
-
if (state === "ready") return of("running")
|
|
131
|
-
if (state === "error") return of("error")
|
|
132
|
-
if (state === "stopped") return of("stopped")
|
|
133
|
-
return of("pending")
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* The 8 px state dot. It pings while the worker runs — the one animation on the
|
|
138
|
-
* card, reserved for the one state where "it is alive right now" is the message.
|
|
139
|
-
*/
|
|
140
|
-
export function WorkerStatusDot({
|
|
141
|
-
status,
|
|
142
|
-
className,
|
|
143
|
-
}: {
|
|
144
|
-
status: WorkerStatus | WorkerStatusKey
|
|
145
|
-
className?: string
|
|
146
|
-
}) {
|
|
147
|
-
const cfg = typeof status === "string" ? { ...workerStatusConfig[status], running: status === "running" } : status
|
|
148
|
-
return (
|
|
149
|
-
<span className={cn("relative flex h-2 w-2 shrink-0", className)}>
|
|
150
|
-
<span className={cn("h-2 w-2 rounded-full", cfg.dotClass)} />
|
|
151
|
-
{cfg.running && (
|
|
152
|
-
<span className="absolute inset-0 h-2 w-2 animate-ping rounded-full bg-green-500 opacity-50 motion-reduce:animate-none" />
|
|
153
|
-
)}
|
|
154
|
-
</span>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** The state chip that sits next to the worker name. */
|
|
159
|
-
export function WorkerStatusPill({
|
|
160
|
-
status,
|
|
161
|
-
label,
|
|
162
|
-
className,
|
|
163
|
-
}: {
|
|
164
|
-
status: WorkerStatus | WorkerStatusKey
|
|
165
|
-
/** Overrides the table's label — e.g. a live setup phase instead of "Setting up…". */
|
|
166
|
-
label?: string
|
|
167
|
-
className?: string
|
|
168
|
-
}) {
|
|
169
|
-
const cfg = typeof status === "string" ? workerStatusConfig[status] : status
|
|
170
|
-
return (
|
|
171
|
-
<span
|
|
172
|
-
className={cn(
|
|
173
|
-
"inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-medium leading-none",
|
|
174
|
-
cfg.pillClass,
|
|
175
|
-
className
|
|
176
|
-
)}
|
|
177
|
-
>
|
|
178
|
-
{label ?? cfg.label}
|
|
179
|
-
</span>
|
|
180
|
-
)
|
|
181
|
-
}
|