@gravito/zenith 1.1.2 → 1.1.3

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 (36) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +77 -22
  3. package/README.zh-TW.md +88 -0
  4. package/dist/bin.js +64681 -15842
  5. package/dist/client/assets/index-C80c1frR.css +1 -0
  6. package/dist/client/assets/index-CrWem9u3.js +434 -0
  7. package/dist/server/index.js +64681 -15842
  8. package/package.json +9 -7
  9. package/postcss.config.js +4 -4
  10. package/src/client/Layout.tsx +36 -39
  11. package/src/client/Sidebar.tsx +7 -7
  12. package/src/client/ThroughputChart.tsx +31 -17
  13. package/src/client/WorkerStatus.tsx +56 -80
  14. package/src/client/components/ConfirmDialog.tsx +22 -14
  15. package/src/client/components/JobInspector.tsx +95 -162
  16. package/src/client/index.css +29 -31
  17. package/src/client/pages/LoginPage.tsx +33 -31
  18. package/src/client/pages/MetricsPage.tsx +65 -37
  19. package/src/client/pages/OverviewPage.tsx +30 -28
  20. package/src/client/pages/PulsePage.tsx +111 -190
  21. package/src/client/pages/QueuesPage.tsx +82 -83
  22. package/src/client/pages/SchedulesPage.tsx +56 -61
  23. package/src/client/pages/SettingsPage.tsx +118 -137
  24. package/src/client/pages/WorkersPage.tsx +101 -115
  25. package/src/server/services/CommandService.ts +8 -9
  26. package/src/server/services/PulseService.ts +61 -4
  27. package/src/server/services/QueueService.ts +293 -0
  28. package/src/shared/types.ts +38 -13
  29. package/tailwind.config.js +75 -68
  30. package/tsconfig.json +28 -37
  31. package/tsconfig.node.json +9 -11
  32. package/dist/client/assets/index-BSMp8oq_.js +0 -436
  33. package/dist/client/assets/index-BwxlHx-_.css +0 -1
  34. package/dist/client/index.html +0 -13
  35. package/src/client/index.html +0 -12
  36. /package/{ECOSYSTEM_EXPANSION_RFC.md → doc/ECOSYSTEM_EXPANSION_RFC.md} +0 -0
@@ -53,31 +53,35 @@ export function LoginPage() {
53
53
  className="relative z-10 w-full max-w-md"
54
54
  >
55
55
  {/* Logo & Title */}
56
- <div className="text-center mb-8">
56
+ <div className="text-center mb-8 font-heading">
57
57
  <motion.div
58
58
  initial={{ scale: 0 }}
59
59
  animate={{ scale: 1 }}
60
60
  transition={{ type: 'spring', delay: 0.2 }}
61
- className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-primary to-indigo-600 rounded-3xl shadow-2xl shadow-primary/30 mb-6"
61
+ className="inline-flex items-center justify-center w-24 h-24 bg-primary rounded-3xl shadow-[0_0_40px_rgba(0,240,255,0.2)] mb-8"
62
62
  >
63
- <Activity size={40} className="text-primary-foreground" />
63
+ <Activity size={48} className="text-black" />
64
64
  </motion.div>
65
- <h1 className="text-4xl font-black tracking-tighter mb-2">GRAVITO</h1>
66
- <p className="text-[10px] font-bold text-primary uppercase tracking-[0.4em]">
67
- Flux Console
65
+ <h1 className="text-5xl font-black tracking-tighter mb-2 text-white italic uppercase">
66
+ ZENITH
67
+ </h1>
68
+ <p className="text-[10px] font-black text-primary uppercase tracking-[0.5em] ml-1">
69
+ Quantum Control Plane
68
70
  </p>
69
71
  </div>
70
72
 
71
73
  {/* Login Card */}
72
- <div className="card-premium p-8 backdrop-blur-xl">
73
- <div className="text-center mb-8">
74
- <h2 className="text-xl font-bold mb-2">Welcome Back</h2>
75
- <p className="text-sm text-muted-foreground">
76
- Enter your password to access the console
74
+ <div className="card-premium p-10 backdrop-blur-2xl bg-zinc-900/40 border-white/5">
75
+ <div className="text-center mb-10">
76
+ <h2 className="text-xl font-black text-white uppercase tracking-tight font-heading">
77
+ Secure Access
78
+ </h2>
79
+ <p className="text-xs text-muted-foreground mt-2 font-bold uppercase tracking-widest opacity-60">
80
+ Biometric or Password verification required
77
81
  </p>
78
82
  </div>
79
83
 
80
- <form onSubmit={handleSubmit} className="space-y-6">
84
+ <form onSubmit={handleSubmit} className="space-y-8 font-heading">
81
85
  {/* Error Message */}
82
86
  {error && (
83
87
  <motion.div
@@ -86,21 +90,21 @@ export function LoginPage() {
86
90
  className="flex items-center gap-3 p-4 bg-red-500/10 border border-red-500/20 rounded-xl text-red-500"
87
91
  >
88
92
  <AlertCircle size={18} />
89
- <span className="text-sm font-medium">{error}</span>
93
+ <span className="text-[11px] font-black uppercase tracking-widest">{error}</span>
90
94
  </motion.div>
91
95
  )}
92
96
 
93
97
  {/* Password Field */}
94
- <div className="space-y-2">
98
+ <div className="space-y-3">
95
99
  <label
96
100
  htmlFor="password"
97
- className="text-sm font-bold text-muted-foreground uppercase tracking-widest"
101
+ className="text-[10px] font-black text-muted-foreground uppercase tracking-[0.2em] ml-1"
98
102
  >
99
- Password
103
+ System Authentication
100
104
  </label>
101
105
  <div className="relative">
102
106
  <Lock
103
- className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground"
107
+ className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground/40"
104
108
  size={18}
105
109
  />
106
110
  <input
@@ -108,11 +112,11 @@ export function LoginPage() {
108
112
  type={showPassword ? 'text' : 'password'}
109
113
  value={password}
110
114
  onChange={(e) => setPassword(e.target.value)}
111
- placeholder="Enter console password"
115
+ placeholder="Enter access code..."
112
116
  className={cn(
113
- 'w-full bg-muted/40 border border-border/50 rounded-xl py-4 pl-12 pr-12',
114
- 'text-sm font-medium placeholder:text-muted-foreground/40',
115
- 'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary/50',
117
+ 'w-full bg-black/40 border border-white/5 rounded-xl py-4 pl-12 pr-12 font-mono',
118
+ 'text-sm font-bold placeholder:text-white/10 tracking-widest',
119
+ 'focus:outline-none focus:ring-1 focus:ring-primary/40 focus:border-primary/40',
116
120
  'transition-all'
117
121
  )}
118
122
  disabled={isLoading}
@@ -120,7 +124,7 @@ export function LoginPage() {
120
124
  <button
121
125
  type="button"
122
126
  onClick={() => setShowPassword(!showPassword)}
123
- className="absolute right-4 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
127
+ className="absolute right-4 top-1/2 -translate-y-1/2 text-muted-foreground/40 hover:text-primary transition-colors"
124
128
  >
125
129
  {showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
126
130
  </button>
@@ -132,20 +136,18 @@ export function LoginPage() {
132
136
  type="submit"
133
137
  disabled={isLoading || !password}
134
138
  className={cn(
135
- 'w-full py-4 rounded-xl font-bold text-sm uppercase tracking-widest',
136
- 'bg-gradient-to-r from-primary to-indigo-600 text-primary-foreground',
137
- 'shadow-lg shadow-primary/30 hover:shadow-xl hover:shadow-primary/40',
138
- 'transition-all duration-300 hover:scale-[1.02] active:scale-[0.98]',
139
- 'disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100',
140
- 'flex items-center justify-center gap-2'
139
+ 'w-full py-4 rounded-xl font-black text-xs uppercase tracking-[0.3em]',
140
+ 'bg-primary text-black font-heading shadow-[0_0_30px_rgba(0,240,255,0.2)] hover:shadow-[0_0_40px_rgba(0,240,255,0.4)]',
141
+ 'transition-all duration-500 hover:scale-[1.02] active:scale-95',
142
+ 'disabled:opacity-20 disabled:cursor-not-allowed disabled:hover:scale-100',
143
+ 'flex items-center justify-center gap-3'
141
144
  )}
142
145
  >
143
146
  {isLoading ? (
144
- <div className="w-5 h-5 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
147
+ <div className="w-5 h-5 border-2 border-black/30 border-t-black rounded-full animate-spin" />
145
148
  ) : (
146
149
  <>
147
- Access Console
148
- <ArrowRight size={18} />
150
+ Connect <ArrowRight size={18} />
149
151
  </>
150
152
  )}
151
153
  </button>
@@ -213,7 +213,7 @@ export function MetricsPage() {
213
213
  <AreaChart data={chartData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
214
214
  <defs>
215
215
  <linearGradient id="colorThroughput" x1="0" y1="0" x2="0" y2="1">
216
- <stop offset="5%" stopColor="hsl(var(--primary))" stopOpacity={0.4} />
216
+ <stop offset="5%" stopColor="hsl(var(--primary))" stopOpacity={0.5} />
217
217
  <stop offset="95%" stopColor="hsl(var(--primary))" stopOpacity={0} />
218
218
  </linearGradient>
219
219
  </defs>
@@ -221,25 +221,37 @@ export function MetricsPage() {
221
221
  strokeDasharray="3 3"
222
222
  vertical={false}
223
223
  stroke="hsl(var(--border))"
224
- opacity={0.5}
224
+ opacity={0.3}
225
225
  />
226
226
  <XAxis
227
227
  dataKey="time"
228
228
  axisLine={false}
229
229
  tickLine={false}
230
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
230
+ tick={{
231
+ fontSize: 10,
232
+ fill: 'hsl(var(--muted-foreground))',
233
+ fontWeight: 700,
234
+ fontFamily: 'Fira Code',
235
+ }}
231
236
  />
232
237
  <YAxis
233
238
  axisLine={false}
234
239
  tickLine={false}
235
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
240
+ tick={{
241
+ fontSize: 10,
242
+ fill: 'hsl(var(--muted-foreground))',
243
+ fontWeight: 700,
244
+ fontFamily: 'Fira Code',
245
+ }}
236
246
  />
237
247
  <Tooltip
238
248
  contentStyle={{
239
- backgroundColor: 'hsl(var(--card))',
240
- border: '1px solid hsl(var(--border))',
249
+ backgroundColor: 'rgba(9, 9, 11, 0.95)',
250
+ border: '1px solid rgba(255, 255, 255, 0.1)',
241
251
  borderRadius: '12px',
242
- fontSize: '12px',
252
+ fontSize: '11px',
253
+ fontFamily: 'Fira Code',
254
+ backdropFilter: 'blur(8px)',
243
255
  }}
244
256
  />
245
257
  <Area
@@ -248,7 +260,7 @@ export function MetricsPage() {
248
260
  stroke="hsl(var(--primary))"
249
261
  fillOpacity={1}
250
262
  fill="url(#colorThroughput)"
251
- strokeWidth={2}
263
+ strokeWidth={3}
252
264
  />
253
265
  </AreaChart>
254
266
  </ResponsiveContainer>
@@ -279,37 +291,39 @@ export function MetricsPage() {
279
291
  dataKey="name"
280
292
  axisLine={false}
281
293
  tickLine={false}
282
- tick={{ fontSize: 9, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
294
+ tick={{
295
+ fontSize: 9,
296
+ fill: 'hsl(var(--muted-foreground))',
297
+ fontWeight: 700,
298
+ fontFamily: 'Fira Code',
299
+ }}
283
300
  angle={-45}
284
301
  textAnchor="end"
285
302
  />
286
303
  <YAxis
287
304
  axisLine={false}
288
305
  tickLine={false}
289
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
306
+ tick={{
307
+ fontSize: 10,
308
+ fill: 'hsl(var(--muted-foreground))',
309
+ fontWeight: 700,
310
+ fontFamily: 'Fira Code',
311
+ }}
290
312
  />
291
313
  <Tooltip
292
314
  contentStyle={{
293
- backgroundColor: 'hsl(var(--card))',
294
- border: '1px solid hsl(var(--border))',
315
+ backgroundColor: 'rgba(9, 9, 11, 0.95)',
316
+ border: '1px solid rgba(255, 255, 255, 0.1)',
295
317
  borderRadius: '12px',
296
- fontSize: '12px',
318
+ fontSize: '11px',
319
+ fontFamily: 'Fira Code',
320
+ backdropFilter: 'blur(8px)',
297
321
  }}
298
322
  />
299
- <Legend />
300
- <Bar
301
- dataKey="waiting"
302
- fill="hsl(45, 93%, 47%)"
303
- name="Waiting"
304
- radius={[4, 4, 0, 0]}
305
- />
306
- <Bar
307
- dataKey="delayed"
308
- fill="hsl(217, 91%, 60%)"
309
- name="Delayed"
310
- radius={[4, 4, 0, 0]}
311
- />
312
- <Bar dataKey="failed" fill="hsl(0, 84%, 60%)" name="Failed" radius={[4, 4, 0, 0]} />
323
+ <Legend iconType="circle" />
324
+ <Bar dataKey="waiting" fill="#F59E0B" name="Waiting" radius={[4, 4, 0, 0]} />
325
+ <Bar dataKey="delayed" fill="#3B82F6" name="Delayed" radius={[4, 4, 0, 0]} />
326
+ <Bar dataKey="failed" fill="#EF4444" name="Failed" radius={[4, 4, 0, 0]} />
313
327
  </BarChart>
314
328
  </ResponsiveContainer>
315
329
  </div>
@@ -391,26 +405,40 @@ interface StatCardProps {
391
405
 
392
406
  function StatCard({ icon: Icon, label, value, color }: StatCardProps) {
393
407
  const colorClasses = {
394
- amber: 'text-amber-500 bg-amber-500/10',
395
- blue: 'text-blue-500 bg-blue-500/10',
396
- green: 'text-green-500 bg-green-500/10',
397
- red: 'text-red-500 bg-red-500/10',
398
- indigo: 'text-indigo-500 bg-indigo-500/10',
399
- primary: 'text-primary bg-primary/10',
408
+ amber:
409
+ 'text-amber-500 bg-amber-500/10 border-amber-500/20 shadow-[0_0_15px_rgba(245,158,11,0.05)]',
410
+ blue: 'text-blue-500 bg-blue-500/10 border-blue-500/20 shadow-[0_0_15px_rgba(59,130,246,0.05)]',
411
+ green:
412
+ 'text-emerald-500 bg-emerald-500/10 border-emerald-500/20 shadow-[0_0_15px_rgba(16,185,129,0.05)]',
413
+ red: 'text-red-500 bg-red-500/10 border-red-500/20 shadow-[0_0_15px_rgba(239,68,68,0.05)]',
414
+ indigo:
415
+ 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20 shadow-[0_0_15px_rgba(99,102,241,0.05)]',
416
+ primary: 'text-primary bg-primary/10 border-primary/20 shadow-[0_0_15px_rgba(0,240,255,0.05)]',
400
417
  }
401
418
 
402
419
  return (
403
- <div className="card-premium p-4 flex items-center gap-3">
420
+ <div
421
+ className={cn(
422
+ 'card-premium p-4 flex items-center gap-4 border-l-4',
423
+ colorClasses[color]
424
+ .split(' ')
425
+ .find((c) => c.startsWith('border-'))
426
+ ?.replace('border-', 'border-l-')
427
+ )}
428
+ >
404
429
  <div
405
- className={cn('w-10 h-10 rounded-xl flex items-center justify-center', colorClasses[color])}
430
+ className={cn(
431
+ 'w-10 h-10 rounded-xl flex items-center justify-center border transition-transform group-hover:scale-110',
432
+ colorClasses[color]
433
+ )}
406
434
  >
407
435
  <Icon size={20} />
408
436
  </div>
409
437
  <div>
410
- <p className="text-[9px] font-black text-muted-foreground/50 uppercase tracking-widest">
438
+ <p className="text-[9px] font-black text-muted-foreground/60 uppercase tracking-[0.2em] font-heading mb-0.5">
411
439
  {label}
412
440
  </p>
413
- <p className="text-xl font-black">{value.toLocaleString()}</p>
441
+ <p className="text-xl font-black font-mono tracking-tighter">{value.toLocaleString()}</p>
414
442
  </div>
415
443
  </div>
416
444
  )
@@ -76,6 +76,7 @@ function LiveLogs({
76
76
  <button
77
77
  type="button"
78
78
  onClick={onSearchArchive}
79
+ aria-label="Search Logs Archive"
79
80
  className="flex items-center gap-1.5 px-2 py-1 hover:bg-muted rounded-md text-[10px] font-black uppercase tracking-tighter text-muted-foreground transition-all"
80
81
  >
81
82
  <Search size={12} />
@@ -89,12 +90,12 @@ function LiveLogs({
89
90
  </div>
90
91
  <ul
91
92
  ref={scrollRef}
92
- className="flex-1 min-h-0 overflow-y-auto p-4 font-mono text-[11px] space-y-2.5 scrollbar-thin scroll-smooth"
93
+ className="flex-1 min-h-0 overflow-y-auto p-4 font-mono text-[10px] space-y-2 scrollbar-thin scroll-smooth bg-black/20"
93
94
  >
94
95
  {logs.length === 0 ? (
95
- <div className="h-full flex flex-col items-center justify-center text-muted-foreground/30 gap-2 opacity-50">
96
- <Activity size={24} className="animate-pulse" />
97
- <p className="font-bold uppercase tracking-widest text-[9px]">Awaiting signals...</p>
96
+ <div className="h-full flex flex-col items-center justify-center text-muted-foreground/20 gap-2">
97
+ <Activity size={24} className="animate-pulse opacity-50" />
98
+ <p className="font-bold uppercase tracking-[0.3em] text-[8px]">Scanning spectrum...</p>
98
99
  </div>
99
100
  ) : (
100
101
  logs.map((log, i) => (
@@ -102,9 +103,9 @@ function LiveLogs({
102
103
  key={i}
103
104
  onMouseEnter={() => onWorkerHover?.(log.workerId)}
104
105
  onMouseLeave={() => onWorkerHover?.(null)}
105
- className="group flex gap-3 hover:bg-primary/[0.02] -mx-2 px-2 py-0.5 rounded transition-all animate-in fade-in slide-in-from-left-2 duration-300 cursor-default"
106
+ className="group flex gap-3 hover:bg-white/[0.03] -mx-2 px-3 py-1 rounded transition-all animate-in fade-in slide-in-from-left-1 duration-200 cursor-default border-l-2 border-transparent hover:border-primary/40"
106
107
  >
107
- <span className="text-muted-foreground/40 shrink-0 tabular-nums select-none opacity-0 group-hover:opacity-100 transition-opacity">
108
+ <span className="text-muted-foreground/60 shrink-0 tabular-nums select-none opacity-60 group-hover:opacity-100 transition-opacity">
108
109
  {new Date(log.timestamp).toLocaleTimeString([], {
109
110
  hour12: false,
110
111
  hour: '2-digit',
@@ -116,23 +117,23 @@ function LiveLogs({
116
117
  <div className="flex items-center gap-2 mb-0.5">
117
118
  <span
118
119
  className={cn(
119
- 'text-[9px] font-black uppercase tracking-tighter',
120
+ 'text-[8px] font-black uppercase tracking-widest px-1 rounded',
120
121
  log.level === 'error'
121
- ? 'text-red-500'
122
+ ? 'bg-red-500/10 text-red-500'
122
123
  : log.level === 'warn'
123
- ? 'text-amber-500'
124
+ ? 'bg-amber-500/10 text-amber-500'
124
125
  : log.level === 'success'
125
- ? 'text-green-500'
126
- : 'text-blue-500'
126
+ ? 'bg-green-500/10 text-green-500'
127
+ : 'bg-primary/10 text-primary'
127
128
  )}
128
129
  >
129
- [{log.level}]
130
+ {log.level}
130
131
  </span>
131
- <span className="text-[9px] font-black text-muted-foreground/40 uppercase opacity-0 group-hover:opacity-100 transition-all">
132
- {log.workerId}
132
+ <span className="text-[8px] font-bold text-muted-foreground/40 uppercase tracking-tighter opacity-0 group-hover:opacity-100 transition-all">
133
+ ID:{log.workerId}
133
134
  </span>
134
135
  </div>
135
- <p className="text-foreground/80 leading-relaxed whitespace-pre-wrap break-all">
136
+ <p className="text-foreground/70 group-hover:text-foreground leading-relaxed whitespace-pre-wrap break-all">
136
137
  {log.message}
137
138
  </p>
138
139
  </div>
@@ -219,13 +220,13 @@ function MetricCard({ title, value, icon, color, trend, data }: MetricCardProps)
219
220
  const max = Math.max(...displayData, 10)
220
221
 
221
222
  return (
222
- <div className="card-premium p-8 hover:shadow-2xl transform hover:-translate-y-2 group relative overflow-hidden">
223
+ <div className="card-premium p-6 hover:shadow-2xl transform hover:-translate-y-1 group relative overflow-hidden flex flex-col justify-between">
223
224
  <div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none scanline z-0"></div>
224
225
 
225
- <div className="flex justify-between items-start mb-6 z-10 relative">
226
+ <div className="flex justify-between items-start mb-4 z-10 relative">
226
227
  <div
227
228
  className={cn(
228
- 'p-4 rounded-2xl bg-muted/30 transition-all group-hover:bg-primary/20 group-hover:text-primary group-hover:rotate-12 duration-500 border border-transparent group-hover:border-primary/20 shadow-inner',
229
+ 'p-3 rounded-xl bg-muted/20 transition-all group-hover:scale-110 duration-500 border border-transparent group-hover:border-primary/20 shadow-inner',
229
230
  color
230
231
  )}
231
232
  >
@@ -233,14 +234,14 @@ function MetricCard({ title, value, icon, color, trend, data }: MetricCardProps)
233
234
  </div>
234
235
  {trend && (
235
236
  <div className="flex flex-col items-end">
236
- <span className="text-[10px] font-black text-muted-foreground/40 uppercase tracking-widest">
237
+ <span className={cn('text-[9px] font-black uppercase tracking-widest', color)}>
237
238
  {trend}
238
239
  </span>
239
- <div className="w-8 h-1 bg-muted/50 rounded-full mt-1 overflow-hidden">
240
+ <div className="w-10 h-1 bg-muted/30 rounded-full mt-1 overflow-hidden">
240
241
  <motion.div
241
242
  initial={{ width: 0 }}
242
243
  animate={{ width: '100%' }}
243
- transition={{ duration: 1, repeat: Infinity, repeatType: 'reverse' }}
244
+ transition={{ duration: 1.5, repeat: Infinity, repeatType: 'reverse' }}
244
245
  className={cn('h-full', color.replace('text-', 'bg-'))}
245
246
  />
246
247
  </div>
@@ -249,29 +250,29 @@ function MetricCard({ title, value, icon, color, trend, data }: MetricCardProps)
249
250
  </div>
250
251
 
251
252
  <div className="z-10 relative">
252
- <p className="text-[11px] font-black text-muted-foreground/50 uppercase tracking-[0.2em] mb-2">
253
+ <p className="text-[10px] font-black text-muted-foreground/60 uppercase tracking-[0.2em] mb-1 font-heading">
253
254
  {title}
254
255
  </p>
255
- <div className="text-4xl font-black tracking-tighter flex items-center gap-1">
256
+ <div className="text-3xl font-black tracking-tight flex items-baseline gap-1 font-mono">
256
257
  <AnimatedNumber value={value} />
257
258
  {title === 'Waiting Jobs' && value > 100 && (
258
- <span className="text-red-500 animate-pulse text-xs">!</span>
259
+ <span className="text-red-500 animate-pulse text-sm ml-1">!</span>
259
260
  )}
260
261
  </div>
261
262
  </div>
262
263
 
263
- <div className="mt-8 flex items-end gap-1.5 h-16 opacity-5 group-hover:opacity-20 transition-all duration-700 absolute bottom-0 left-0 right-0 p-1.5 pointer-events-none">
264
+ <div className="mt-6 flex items-end gap-1 h-12 opacity-10 group-hover:opacity-30 transition-all duration-700 absolute bottom-0 left-0 right-0 pointer-events-none">
264
265
  {displayData.map((v, i) => (
265
266
  <div
266
267
  key={i}
267
268
  className={cn(
268
- 'flex-1 rounded-t-lg transition-all duration-1000',
269
+ 'flex-1 rounded-t-sm transition-all duration-1000',
269
270
  color.replace('text-', 'bg-')
270
271
  )}
271
272
  style={{
272
273
  height: `${(v / max) * 100}%`,
273
- opacity: 0.1 + (i / displayData.length) * 0.9,
274
- transitionDelay: `${i * 30}ms`,
274
+ opacity: 0.2 + (i / displayData.length) * 0.8,
275
+ transitionDelay: `${i * 20}ms`,
275
276
  }}
276
277
  ></div>
277
278
  ))}
@@ -333,6 +334,7 @@ function QueueList({
333
334
  onClick={() => setSelectedQueue(queue.name)}
334
335
  className="p-1.5 bg-muted hover:bg-primary/20 hover:text-primary rounded text-muted-foreground transition-all active:scale-90"
335
336
  title="Inspect"
337
+ aria-label={`Inspect ${queue.name} queue`}
336
338
  >
337
339
  <ArrowRight size={14} />
338
340
  </button>