@wakastellar/ui 2.1.2 → 2.3.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.
Files changed (123) hide show
  1. package/dist/blocks/apm-overview/index.d.ts +58 -0
  2. package/dist/blocks/cicd-builder/index.d.ts +47 -0
  3. package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
  4. package/dist/blocks/container-orchestrator/index.d.ts +63 -0
  5. package/dist/blocks/database-admin/index.d.ts +84 -0
  6. package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
  7. package/dist/blocks/incident-manager/index.d.ts +44 -0
  8. package/dist/blocks/index.d.ts +10 -0
  9. package/dist/blocks/infrastructure-map/index.d.ts +32 -0
  10. package/dist/blocks/on-call-schedule/index.d.ts +43 -0
  11. package/dist/blocks/release-notes/index.d.ts +49 -0
  12. package/dist/components/index.d.ts +34 -0
  13. package/dist/components/waka-ad-banner/index.d.ts +36 -0
  14. package/dist/components/waka-ad-fallback/index.d.ts +33 -0
  15. package/dist/components/waka-ad-inline/index.d.ts +15 -0
  16. package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
  17. package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
  18. package/dist/components/waka-ad-provider/index.d.ts +103 -0
  19. package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
  20. package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
  21. package/dist/components/waka-alert-panel/index.d.ts +45 -0
  22. package/dist/components/waka-artifact-list/index.d.ts +32 -0
  23. package/dist/components/waka-build-matrix/index.d.ts +36 -0
  24. package/dist/components/waka-config-comparator/index.d.ts +37 -0
  25. package/dist/components/waka-container-list/index.d.ts +51 -0
  26. package/dist/components/waka-content-recommendation/index.d.ts +23 -0
  27. package/dist/components/waka-database-card/index.d.ts +46 -0
  28. package/dist/components/waka-dependency-tree/index.d.ts +38 -0
  29. package/dist/components/waka-env-var-editor/index.d.ts +30 -0
  30. package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
  31. package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
  32. package/dist/components/waka-log-viewer/index.d.ts +38 -0
  33. package/dist/components/waka-migration-list/index.d.ts +36 -0
  34. package/dist/components/waka-outstream-video/index.d.ts +24 -0
  35. package/dist/components/waka-pod-card/index.d.ts +73 -0
  36. package/dist/components/waka-query-explain/index.d.ts +48 -0
  37. package/dist/components/waka-secret-card/index.d.ts +43 -0
  38. package/dist/components/waka-security-scan-result/index.d.ts +45 -0
  39. package/dist/components/waka-service-graph/index.d.ts +44 -0
  40. package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
  41. package/dist/components/waka-sponsored-card/index.d.ts +25 -0
  42. package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
  43. package/dist/components/waka-test-report/index.d.ts +60 -0
  44. package/dist/components/waka-trace-viewer/index.d.ts +36 -0
  45. package/dist/components/waka-video-ad/index.d.ts +32 -0
  46. package/dist/components/waka-video-overlay/index.d.ts +26 -0
  47. package/dist/index.cjs.js +251 -200
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.es.js +47315 -35823
  50. package/dist/utils/security.d.ts +96 -0
  51. package/package.json +4 -4
  52. package/src/blocks/apm-overview/index.tsx +672 -0
  53. package/src/blocks/cicd-builder/index.tsx +738 -0
  54. package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
  55. package/src/blocks/container-orchestrator/index.tsx +729 -0
  56. package/src/blocks/database-admin/index.tsx +679 -0
  57. package/src/blocks/gitops-sync-status/index.tsx +557 -0
  58. package/src/blocks/incident-manager/index.tsx +586 -0
  59. package/src/blocks/index.ts +119 -0
  60. package/src/blocks/infrastructure-map/index.tsx +638 -0
  61. package/src/blocks/on-call-schedule/index.tsx +615 -0
  62. package/src/blocks/release-notes/index.tsx +643 -0
  63. package/src/blocks/sidebar/index.tsx +6 -6
  64. package/src/components/DataTable/templates/index.tsx +3 -2
  65. package/src/components/index.ts +283 -0
  66. package/src/components/waka-3d-pie-chart/index.tsx +11 -11
  67. package/src/components/waka-achievement-unlock/index.tsx +16 -16
  68. package/src/components/waka-ad-banner/index.tsx +275 -0
  69. package/src/components/waka-ad-fallback/index.tsx +181 -0
  70. package/src/components/waka-ad-inline/index.tsx +103 -0
  71. package/src/components/waka-ad-interstitial/index.tsx +278 -0
  72. package/src/components/waka-ad-placeholder/index.tsx +84 -0
  73. package/src/components/waka-ad-provider/index.tsx +329 -0
  74. package/src/components/waka-ad-sidebar/index.tsx +113 -0
  75. package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
  76. package/src/components/waka-alert-panel/index.tsx +493 -0
  77. package/src/components/waka-artifact-list/index.tsx +416 -0
  78. package/src/components/waka-badge-showcase/index.tsx +12 -11
  79. package/src/components/waka-build-matrix/index.tsx +396 -0
  80. package/src/components/waka-command-bar/index.tsx +2 -1
  81. package/src/components/waka-config-comparator/index.tsx +416 -0
  82. package/src/components/waka-container-list/index.tsx +475 -0
  83. package/src/components/waka-content-recommendation/index.tsx +294 -0
  84. package/src/components/waka-cost-breakdown/index.tsx +10 -10
  85. package/src/components/waka-database-card/index.tsx +473 -0
  86. package/src/components/waka-dependency-tree/index.tsx +542 -0
  87. package/src/components/waka-env-var-editor/index.tsx +417 -0
  88. package/src/components/waka-feature-flag-row/index.tsx +386 -0
  89. package/src/components/waka-funnel-chart/index.tsx +8 -8
  90. package/src/components/waka-health-pulse/index.tsx +6 -6
  91. package/src/components/waka-kubernetes-overview/index.tsx +536 -0
  92. package/src/components/waka-leaderboard/index.tsx +9 -9
  93. package/src/components/waka-log-viewer/index.tsx +386 -0
  94. package/src/components/waka-loot-box/index.tsx +20 -20
  95. package/src/components/waka-migration-list/index.tsx +487 -0
  96. package/src/components/waka-outstream-video/index.tsx +240 -0
  97. package/src/components/waka-player-card/index.tsx +5 -5
  98. package/src/components/waka-pod-card/index.tsx +528 -0
  99. package/src/components/waka-query-explain/index.tsx +657 -0
  100. package/src/components/waka-quota-bar/index.tsx +4 -4
  101. package/src/components/waka-radar-score/index.tsx +10 -10
  102. package/src/components/waka-scratch-card/index.tsx +5 -4
  103. package/src/components/waka-secret-card/index.tsx +371 -0
  104. package/src/components/waka-security-scan-result/index.tsx +473 -0
  105. package/src/components/waka-server-rack/index.tsx +28 -27
  106. package/src/components/waka-service-graph/index.tsx +445 -0
  107. package/src/components/waka-sponsored-badge/index.tsx +97 -0
  108. package/src/components/waka-sponsored-card/index.tsx +275 -0
  109. package/src/components/waka-sponsored-feed/index.tsx +127 -0
  110. package/src/components/waka-spotlight/index.tsx +2 -1
  111. package/src/components/waka-success-explosion/index.tsx +4 -4
  112. package/src/components/waka-test-report/index.tsx +469 -0
  113. package/src/components/waka-trace-viewer/index.tsx +490 -0
  114. package/src/components/waka-video-ad/index.tsx +406 -0
  115. package/src/components/waka-video-overlay/index.tsx +257 -0
  116. package/src/components/waka-xp-bar/index.tsx +13 -13
  117. package/src/styles/base.css +16 -0
  118. package/src/styles/tailwind.preset.js +12 -0
  119. package/src/styles/themes/forest.css +16 -0
  120. package/src/styles/themes/monochrome.css +16 -0
  121. package/src/styles/themes/perpetuity.css +16 -0
  122. package/src/styles/themes/sunset.css +16 -0
  123. package/src/styles/themes/twilight.css +16 -0
@@ -0,0 +1,417 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils/cn"
5
+ import { Button } from "../button"
6
+ import { Input } from "../input"
7
+ import { Badge } from "../badge"
8
+ import { ScrollArea } from "../scroll-area"
9
+ import {
10
+ Tooltip,
11
+ TooltipContent,
12
+ TooltipProvider,
13
+ TooltipTrigger,
14
+ } from "../tooltip"
15
+ import {
16
+ Variable,
17
+ Plus,
18
+ Trash2,
19
+ Eye,
20
+ EyeOff,
21
+ Copy,
22
+ CheckCircle2,
23
+ Search,
24
+ Lock,
25
+ Unlock,
26
+ AlertTriangle,
27
+ Download,
28
+ Upload,
29
+ Save,
30
+ } from "lucide-react"
31
+
32
+ export interface EnvVariable {
33
+ key: string
34
+ value: string
35
+ isSecret?: boolean
36
+ isRequired?: boolean
37
+ description?: string
38
+ source?: string // e.g., "production", "staging", "local"
39
+ }
40
+
41
+ export interface WakaEnvVarEditorProps {
42
+ /** List of environment variables */
43
+ variables: EnvVariable[]
44
+ /** Callback when variables change */
45
+ onChange?: (variables: EnvVariable[]) => void
46
+ /** Callback when saving */
47
+ onSave?: (variables: EnvVariable[]) => void
48
+ /** Callback when importing */
49
+ onImport?: (content: string) => void
50
+ /** Callback when exporting */
51
+ onExport?: () => void
52
+ /** Read-only mode */
53
+ readOnly?: boolean
54
+ /** Show source column */
55
+ showSource?: boolean
56
+ /** Title */
57
+ title?: string
58
+ /** Custom class name */
59
+ className?: string
60
+ }
61
+
62
+ function EnvVarRow({
63
+ variable,
64
+ index,
65
+ onChange,
66
+ onDelete,
67
+ readOnly,
68
+ showSource,
69
+ }: {
70
+ variable: EnvVariable
71
+ index: number
72
+ onChange: (index: number, field: keyof EnvVariable, value: string | boolean) => void
73
+ onDelete: (index: number) => void
74
+ readOnly: boolean
75
+ showSource: boolean
76
+ }) {
77
+ const [showValue, setShowValue] = React.useState(!variable.isSecret)
78
+ const [copied, setCopied] = React.useState(false)
79
+
80
+ const copyValue = () => {
81
+ navigator.clipboard.writeText(variable.value)
82
+ setCopied(true)
83
+ setTimeout(() => setCopied(false), 2000)
84
+ }
85
+
86
+ const maskedValue = variable.isSecret && !showValue
87
+ ? "•".repeat(Math.min(variable.value.length, 20))
88
+ : variable.value
89
+
90
+ return (
91
+ <div className={cn(
92
+ "flex items-center gap-2 p-2 border-b hover:bg-muted/30 transition-colors",
93
+ variable.isRequired && !variable.value && "bg-yellow-500/5"
94
+ )}>
95
+ {/* Secret indicator */}
96
+ <TooltipProvider>
97
+ <Tooltip>
98
+ <TooltipTrigger asChild>
99
+ <button
100
+ className={cn(
101
+ "p-1.5 rounded hover:bg-muted transition-colors",
102
+ readOnly && "pointer-events-none"
103
+ )}
104
+ onClick={() => !readOnly && onChange(index, "isSecret", !variable.isSecret)}
105
+ >
106
+ {variable.isSecret ? (
107
+ <Lock className="h-4 w-4 text-yellow-500" />
108
+ ) : (
109
+ <Unlock className="h-4 w-4 text-muted-foreground" />
110
+ )}
111
+ </button>
112
+ </TooltipTrigger>
113
+ <TooltipContent>{variable.isSecret ? "Secret variable" : "Not a secret"}</TooltipContent>
114
+ </Tooltip>
115
+ </TooltipProvider>
116
+
117
+ {/* Key */}
118
+ <Input
119
+ value={variable.key}
120
+ onChange={(e) => onChange(index, "key", e.target.value)}
121
+ placeholder="KEY_NAME"
122
+ className="font-mono text-sm w-48 h-8"
123
+ readOnly={readOnly}
124
+ />
125
+
126
+ <span className="text-muted-foreground">=</span>
127
+
128
+ {/* Value */}
129
+ <div className="flex-1 relative">
130
+ <Input
131
+ type={variable.isSecret && !showValue ? "password" : "text"}
132
+ value={variable.value}
133
+ onChange={(e) => onChange(index, "value", e.target.value)}
134
+ placeholder="value"
135
+ className="font-mono text-sm h-8 pr-16"
136
+ readOnly={readOnly}
137
+ />
138
+ <div className="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-1">
139
+ {variable.isSecret && (
140
+ <Button
141
+ variant="ghost"
142
+ size="sm"
143
+ className="h-6 w-6 p-0"
144
+ onClick={() => setShowValue(!showValue)}
145
+ >
146
+ {showValue ? (
147
+ <EyeOff className="h-3 w-3" />
148
+ ) : (
149
+ <Eye className="h-3 w-3" />
150
+ )}
151
+ </Button>
152
+ )}
153
+ <Button
154
+ variant="ghost"
155
+ size="sm"
156
+ className="h-6 w-6 p-0"
157
+ onClick={copyValue}
158
+ >
159
+ {copied ? (
160
+ <CheckCircle2 className="h-3 w-3 text-green-500" />
161
+ ) : (
162
+ <Copy className="h-3 w-3" />
163
+ )}
164
+ </Button>
165
+ </div>
166
+ </div>
167
+
168
+ {/* Source */}
169
+ {showSource && variable.source && (
170
+ <Badge variant="outline" className="text-xs shrink-0">
171
+ {variable.source}
172
+ </Badge>
173
+ )}
174
+
175
+ {/* Required indicator */}
176
+ {variable.isRequired && !variable.value && (
177
+ <TooltipProvider>
178
+ <Tooltip>
179
+ <TooltipTrigger>
180
+ <AlertTriangle className="h-4 w-4 text-yellow-500" />
181
+ </TooltipTrigger>
182
+ <TooltipContent>Required variable is empty</TooltipContent>
183
+ </Tooltip>
184
+ </TooltipProvider>
185
+ )}
186
+
187
+ {/* Delete */}
188
+ {!readOnly && (
189
+ <Button
190
+ variant="ghost"
191
+ size="sm"
192
+ className="h-8 w-8 p-0 text-destructive hover:text-destructive"
193
+ onClick={() => onDelete(index)}
194
+ >
195
+ <Trash2 className="h-4 w-4" />
196
+ </Button>
197
+ )}
198
+ </div>
199
+ )
200
+ }
201
+
202
+ export function WakaEnvVarEditor({
203
+ variables: initialVariables,
204
+ onChange,
205
+ onSave,
206
+ onImport,
207
+ onExport,
208
+ readOnly = false,
209
+ showSource = false,
210
+ title = "Environment Variables",
211
+ className,
212
+ }: WakaEnvVarEditorProps) {
213
+ const [variables, setVariables] = React.useState<EnvVariable[]>(initialVariables)
214
+ const [searchQuery, setSearchQuery] = React.useState("")
215
+ const [hasChanges, setHasChanges] = React.useState(false)
216
+ const fileInputRef = React.useRef<HTMLInputElement>(null)
217
+
218
+ // Update internal state when props change
219
+ React.useEffect(() => {
220
+ setVariables(initialVariables)
221
+ setHasChanges(false)
222
+ }, [initialVariables])
223
+
224
+ const handleChange = (index: number, field: keyof EnvVariable, value: string | boolean) => {
225
+ const newVariables = [...variables]
226
+ newVariables[index] = { ...newVariables[index], [field]: value }
227
+ setVariables(newVariables)
228
+ setHasChanges(true)
229
+ onChange?.(newVariables)
230
+ }
231
+
232
+ const handleDelete = (index: number) => {
233
+ const newVariables = variables.filter((_, i) => i !== index)
234
+ setVariables(newVariables)
235
+ setHasChanges(true)
236
+ onChange?.(newVariables)
237
+ }
238
+
239
+ const handleAdd = () => {
240
+ const newVariables = [...variables, { key: "", value: "", isSecret: false }]
241
+ setVariables(newVariables)
242
+ setHasChanges(true)
243
+ onChange?.(newVariables)
244
+ }
245
+
246
+ const handleSave = () => {
247
+ onSave?.(variables)
248
+ setHasChanges(false)
249
+ }
250
+
251
+ const handleFileImport = (e: React.ChangeEvent<HTMLInputElement>) => {
252
+ const file = e.target.files?.[0]
253
+ if (file) {
254
+ const reader = new FileReader()
255
+ reader.onload = (event) => {
256
+ const content = event.target?.result as string
257
+ if (onImport) {
258
+ onImport(content)
259
+ } else {
260
+ // Default .env parsing
261
+ const parsed = content
262
+ .split("\n")
263
+ .filter((line) => line.trim() && !line.startsWith("#"))
264
+ .map((line) => {
265
+ const [key, ...valueParts] = line.split("=")
266
+ const value = valueParts.join("=").replace(/^["']|["']$/g, "")
267
+ return { key: key.trim(), value, isSecret: key.includes("SECRET") || key.includes("PASSWORD") || key.includes("KEY") }
268
+ })
269
+ setVariables(parsed)
270
+ setHasChanges(true)
271
+ onChange?.(parsed)
272
+ }
273
+ }
274
+ reader.readAsText(file)
275
+ }
276
+ // Reset input
277
+ if (fileInputRef.current) {
278
+ fileInputRef.current.value = ""
279
+ }
280
+ }
281
+
282
+ // Filter variables
283
+ const filteredVariables = variables.filter((v) =>
284
+ v.key.toLowerCase().includes(searchQuery.toLowerCase()) ||
285
+ (!v.isSecret && v.value.toLowerCase().includes(searchQuery.toLowerCase()))
286
+ )
287
+
288
+ // Count secrets
289
+ const secretCount = variables.filter((v) => v.isSecret).length
290
+ const requiredEmpty = variables.filter((v) => v.isRequired && !v.value).length
291
+
292
+ return (
293
+ <div className={cn("flex flex-col border rounded-lg bg-background", className)}>
294
+ {/* Header */}
295
+ <div className="flex items-center justify-between gap-4 p-3 border-b">
296
+ <div className="flex items-center gap-3">
297
+ <Variable className="h-5 w-5" />
298
+ <h3 className="font-semibold">{title}</h3>
299
+ <Badge variant="secondary">{variables.length}</Badge>
300
+ {secretCount > 0 && (
301
+ <Badge variant="outline" className="text-yellow-500">
302
+ <Lock className="h-3 w-3 mr-1" />
303
+ {secretCount} secrets
304
+ </Badge>
305
+ )}
306
+ {requiredEmpty > 0 && (
307
+ <Badge variant="outline" className="text-yellow-500">
308
+ <AlertTriangle className="h-3 w-3 mr-1" />
309
+ {requiredEmpty} missing
310
+ </Badge>
311
+ )}
312
+ </div>
313
+
314
+ <div className="flex items-center gap-2">
315
+ {onSave && hasChanges && (
316
+ <Button size="sm" onClick={handleSave}>
317
+ <Save className="h-4 w-4 mr-1" />
318
+ Save
319
+ </Button>
320
+ )}
321
+ </div>
322
+ </div>
323
+
324
+ {/* Toolbar */}
325
+ <div className="flex items-center gap-2 p-2 border-b bg-muted/30">
326
+ <div className="relative flex-1 max-w-sm">
327
+ <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
328
+ <Input
329
+ placeholder="Search variables..."
330
+ value={searchQuery}
331
+ onChange={(e) => setSearchQuery(e.target.value)}
332
+ className="pl-8 h-8"
333
+ />
334
+ </div>
335
+
336
+ {!readOnly && (
337
+ <>
338
+ <Button variant="outline" size="sm" className="h-8" onClick={handleAdd}>
339
+ <Plus className="h-4 w-4 mr-1" />
340
+ Add
341
+ </Button>
342
+
343
+ <input
344
+ type="file"
345
+ ref={fileInputRef}
346
+ accept=".env,.txt"
347
+ onChange={handleFileImport}
348
+ className="hidden"
349
+ />
350
+ <Button
351
+ variant="outline"
352
+ size="sm"
353
+ className="h-8"
354
+ onClick={() => fileInputRef.current?.click()}
355
+ >
356
+ <Upload className="h-4 w-4 mr-1" />
357
+ Import
358
+ </Button>
359
+ </>
360
+ )}
361
+
362
+ {onExport && (
363
+ <Button variant="outline" size="sm" className="h-8" onClick={onExport}>
364
+ <Download className="h-4 w-4 mr-1" />
365
+ Export
366
+ </Button>
367
+ )}
368
+ </div>
369
+
370
+ {/* Variables list */}
371
+ <ScrollArea className="flex-1 max-h-[400px]">
372
+ {filteredVariables.length === 0 ? (
373
+ <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
374
+ <Variable className="h-8 w-8 mb-2" />
375
+ <span>No variables found</span>
376
+ {!readOnly && (
377
+ <Button variant="link" size="sm" onClick={handleAdd}>
378
+ Add your first variable
379
+ </Button>
380
+ )}
381
+ </div>
382
+ ) : (
383
+ filteredVariables.map((variable, index) => (
384
+ <EnvVarRow
385
+ key={index}
386
+ variable={variable}
387
+ index={variables.indexOf(variable)}
388
+ onChange={handleChange}
389
+ onDelete={handleDelete}
390
+ readOnly={readOnly}
391
+ showSource={showSource}
392
+ />
393
+ ))
394
+ )}
395
+ </ScrollArea>
396
+
397
+ {/* Footer hint */}
398
+ {!readOnly && (
399
+ <div className="px-3 py-2 border-t bg-muted/30 text-xs text-muted-foreground">
400
+ 💡 Click the lock icon to mark a variable as secret. Secrets are masked by default.
401
+ </div>
402
+ )}
403
+ </div>
404
+ )
405
+ }
406
+
407
+ // Default sample variables for demo
408
+ export const defaultEnvVariables: EnvVariable[] = [
409
+ { key: "NODE_ENV", value: "production", isRequired: true },
410
+ { key: "DATABASE_URL", value: "postgresql://user:pass@localhost:5432/db", isSecret: true, isRequired: true },
411
+ { key: "API_KEY", value: "sk-1234567890abcdef", isSecret: true },
412
+ { key: "PORT", value: "3000" },
413
+ { key: "LOG_LEVEL", value: "info" },
414
+ { key: "REDIS_URL", value: "redis://localhost:6379", isSecret: true },
415
+ { key: "AWS_ACCESS_KEY_ID", value: "", isSecret: true, isRequired: true },
416
+ { key: "AWS_SECRET_ACCESS_KEY", value: "", isSecret: true, isRequired: true },
417
+ ]