@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,643 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils/cn"
5
+ import { Badge } from "../../components/badge"
6
+ import { Button } from "../../components/button"
7
+ import { Card, CardContent, CardHeader, CardTitle } from "../../components/card"
8
+ import { Input } from "../../components/input"
9
+ import { Textarea } from "../../components/textarea"
10
+ import { Avatar, AvatarFallback, AvatarImage } from "../../components/avatar"
11
+ import { ScrollArea } from "../../components/scroll-area"
12
+ import {
13
+ Select,
14
+ SelectContent,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue,
18
+ } from "../../components/select"
19
+ import {
20
+ Tag,
21
+ GitCommit,
22
+ GitBranch,
23
+ GitPullRequest,
24
+ Plus,
25
+ Trash2,
26
+ Rocket,
27
+ Bug,
28
+ Sparkles,
29
+ Wrench,
30
+ AlertTriangle,
31
+ ShieldCheck,
32
+ Zap,
33
+ BookOpen,
34
+ Users,
35
+ Calendar,
36
+ Link,
37
+ ExternalLink,
38
+ Eye,
39
+ Edit,
40
+ Copy,
41
+ Download,
42
+ Clock,
43
+ CheckCircle2,
44
+ } from "lucide-react"
45
+
46
+ export type ChangeType = "feature" | "fix" | "improvement" | "breaking" | "security" | "performance" | "docs" | "deprecation"
47
+ export type ReleaseType = "major" | "minor" | "patch" | "prerelease"
48
+ export type ReleaseStatus = "draft" | "published" | "scheduled"
49
+
50
+ export interface Contributor {
51
+ id: string
52
+ name: string
53
+ username: string
54
+ avatar?: string
55
+ }
56
+
57
+ export interface ChangeItem {
58
+ id: string
59
+ type: ChangeType
60
+ title: string
61
+ description?: string
62
+ pullRequest?: {
63
+ number: number
64
+ url: string
65
+ }
66
+ commits?: string[]
67
+ contributors?: Contributor[]
68
+ breaking?: boolean
69
+ }
70
+
71
+ export interface Release {
72
+ id: string
73
+ version: string
74
+ name?: string
75
+ releaseType: ReleaseType
76
+ status: ReleaseStatus
77
+ publishedAt?: Date
78
+ scheduledAt?: Date
79
+ createdAt: Date
80
+ changes: ChangeItem[]
81
+ summary?: string
82
+ upgradeNotes?: string
83
+ contributors: Contributor[]
84
+ compareUrl?: string
85
+ downloadUrl?: string
86
+ }
87
+
88
+ export interface ReleaseNotesProps {
89
+ releases: Release[]
90
+ onCreateRelease?: () => void
91
+ onEditRelease?: (release: Release) => void
92
+ onPublishRelease?: (release: Release) => void
93
+ onAddChange?: (releaseId: string, change: ChangeItem) => void
94
+ showEditor?: boolean
95
+ className?: string
96
+ }
97
+
98
+ const changeTypeConfig: Record<ChangeType, { icon: React.ElementType; color: string; bgColor: string; label: string }> = {
99
+ feature: { icon: Sparkles, color: "text-green-500", bgColor: "bg-green-500/10", label: "New Feature" },
100
+ fix: { icon: Bug, color: "text-red-500", bgColor: "bg-red-500/10", label: "Bug Fix" },
101
+ improvement: { icon: Wrench, color: "text-blue-500", bgColor: "bg-blue-500/10", label: "Improvement" },
102
+ breaking: { icon: AlertTriangle, color: "text-orange-500", bgColor: "bg-orange-500/10", label: "Breaking Change" },
103
+ security: { icon: ShieldCheck, color: "text-purple-500", bgColor: "bg-purple-500/10", label: "Security" },
104
+ performance: { icon: Zap, color: "text-yellow-500", bgColor: "bg-yellow-500/10", label: "Performance" },
105
+ docs: { icon: BookOpen, color: "text-cyan-500", bgColor: "bg-cyan-500/10", label: "Documentation" },
106
+ deprecation: { icon: AlertTriangle, color: "text-gray-500", bgColor: "bg-gray-500/10", label: "Deprecation" },
107
+ }
108
+
109
+ const releaseTypeConfig: Record<ReleaseType, { label: string; color: string }> = {
110
+ major: { label: "Major", color: "text-red-500" },
111
+ minor: { label: "Minor", color: "text-blue-500" },
112
+ patch: { label: "Patch", color: "text-green-500" },
113
+ prerelease: { label: "Pre-release", color: "text-purple-500" },
114
+ }
115
+
116
+ function formatDate(date: Date): string {
117
+ return date.toLocaleDateString("en-US", {
118
+ year: "numeric",
119
+ month: "long",
120
+ day: "numeric",
121
+ })
122
+ }
123
+
124
+ function ChangeEntry({ change }: { change: ChangeItem }) {
125
+ const config = changeTypeConfig[change.type]
126
+ const Icon = config.icon
127
+
128
+ return (
129
+ <div className={cn(
130
+ "flex items-start gap-3 p-3 rounded-lg",
131
+ config.bgColor
132
+ )}>
133
+ <div className="p-1.5 rounded bg-background">
134
+ <Icon className={cn("h-4 w-4", config.color)} />
135
+ </div>
136
+
137
+ <div className="flex-1 min-w-0">
138
+ <div className="flex items-center gap-2 flex-wrap">
139
+ <span className="font-medium">{change.title}</span>
140
+ {change.breaking && (
141
+ <Badge className="bg-orange-500 text-xs">Breaking</Badge>
142
+ )}
143
+ {change.pullRequest && (
144
+ <a
145
+ href={change.pullRequest.url}
146
+ target="_blank"
147
+ rel="noopener noreferrer"
148
+ className="text-xs text-muted-foreground hover:text-primary flex items-center gap-1"
149
+ >
150
+ <GitPullRequest className="h-3 w-3" />
151
+ #{change.pullRequest.number}
152
+ </a>
153
+ )}
154
+ </div>
155
+
156
+ {change.description && (
157
+ <p className="text-sm text-muted-foreground mt-1">
158
+ {change.description}
159
+ </p>
160
+ )}
161
+
162
+ {change.contributors && change.contributors.length > 0 && (
163
+ <div className="flex items-center gap-1 mt-2">
164
+ {change.contributors.map((contributor) => (
165
+ <Avatar key={contributor.id} className="h-5 w-5">
166
+ <AvatarImage src={contributor.avatar} />
167
+ <AvatarFallback className="text-xs">
168
+ {contributor.name.slice(0, 2).toUpperCase()}
169
+ </AvatarFallback>
170
+ </Avatar>
171
+ ))}
172
+ </div>
173
+ )}
174
+ </div>
175
+ </div>
176
+ )
177
+ }
178
+
179
+ function ReleaseCard({
180
+ release,
181
+ onEdit,
182
+ onPublish,
183
+ expanded = false,
184
+ }: {
185
+ release: Release
186
+ onEdit?: () => void
187
+ onPublish?: () => void
188
+ expanded?: boolean
189
+ }) {
190
+ const [isExpanded, setIsExpanded] = React.useState(expanded)
191
+ const releaseConf = releaseTypeConfig[release.releaseType]
192
+
193
+ // Group changes by type
194
+ const groupedChanges = React.useMemo(() => {
195
+ const groups: Record<ChangeType, ChangeItem[]> = {
196
+ breaking: [],
197
+ security: [],
198
+ feature: [],
199
+ improvement: [],
200
+ performance: [],
201
+ fix: [],
202
+ docs: [],
203
+ deprecation: [],
204
+ }
205
+ release.changes.forEach((change) => {
206
+ groups[change.type].push(change)
207
+ })
208
+ return groups
209
+ }, [release.changes])
210
+
211
+ return (
212
+ <Card className={cn(
213
+ release.status === "draft" && "border-dashed",
214
+ release.status === "scheduled" && "border-blue-500/30"
215
+ )}>
216
+ <CardHeader className="pb-3">
217
+ <div className="flex items-start justify-between">
218
+ <div className="space-y-2">
219
+ <div className="flex items-center gap-2">
220
+ <Tag className="h-5 w-5" />
221
+ <CardTitle className="text-xl">{release.version}</CardTitle>
222
+ {release.name && (
223
+ <span className="text-muted-foreground">- {release.name}</span>
224
+ )}
225
+ </div>
226
+
227
+ <div className="flex items-center gap-2">
228
+ <Badge variant="outline" className={releaseConf.color}>
229
+ {releaseConf.label}
230
+ </Badge>
231
+ <Badge
232
+ variant={release.status === "published" ? "default" : "secondary"}
233
+ className={release.status === "published" ? "bg-green-500" : ""}
234
+ >
235
+ {release.status === "published" ? (
236
+ <CheckCircle2 className="h-3 w-3 mr-1" />
237
+ ) : release.status === "scheduled" ? (
238
+ <Clock className="h-3 w-3 mr-1" />
239
+ ) : (
240
+ <Edit className="h-3 w-3 mr-1" />
241
+ )}
242
+ {release.status}
243
+ </Badge>
244
+ <span className="text-sm text-muted-foreground flex items-center gap-1">
245
+ <Calendar className="h-3 w-3" />
246
+ {release.publishedAt ? formatDate(release.publishedAt) : formatDate(release.createdAt)}
247
+ </span>
248
+ </div>
249
+ </div>
250
+
251
+ <div className="flex items-center gap-2">
252
+ {release.compareUrl && (
253
+ <Button variant="ghost" size="sm" asChild>
254
+ <a href={release.compareUrl} target="_blank" rel="noopener noreferrer">
255
+ <GitCommit className="h-4 w-4 mr-1" />
256
+ Compare
257
+ </a>
258
+ </Button>
259
+ )}
260
+ {onEdit && release.status === "draft" && (
261
+ <Button variant="outline" size="sm" onClick={onEdit}>
262
+ <Edit className="h-4 w-4 mr-1" />
263
+ Edit
264
+ </Button>
265
+ )}
266
+ {onPublish && release.status === "draft" && (
267
+ <Button size="sm" onClick={onPublish}>
268
+ <Rocket className="h-4 w-4 mr-1" />
269
+ Publish
270
+ </Button>
271
+ )}
272
+ </div>
273
+ </div>
274
+ </CardHeader>
275
+
276
+ <CardContent className="space-y-4">
277
+ {/* Summary */}
278
+ {release.summary && (
279
+ <p className="text-muted-foreground">{release.summary}</p>
280
+ )}
281
+
282
+ {/* Upgrade Notes */}
283
+ {release.upgradeNotes && (
284
+ <div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
285
+ <div className="flex items-center gap-2 font-medium text-yellow-600 mb-1">
286
+ <AlertTriangle className="h-4 w-4" />
287
+ Upgrade Notes
288
+ </div>
289
+ <p className="text-sm text-muted-foreground">{release.upgradeNotes}</p>
290
+ </div>
291
+ )}
292
+
293
+ {/* Changes */}
294
+ <div className="space-y-4">
295
+ {Object.entries(groupedChanges).map(([type, changes]) => {
296
+ if (changes.length === 0) return null
297
+ const config = changeTypeConfig[type as ChangeType]
298
+
299
+ return (
300
+ <div key={type}>
301
+ <h4 className="font-medium flex items-center gap-2 mb-2">
302
+ <config.icon className={cn("h-4 w-4", config.color)} />
303
+ {config.label}s ({changes.length})
304
+ </h4>
305
+ <div className="space-y-2">
306
+ {(isExpanded ? changes : changes.slice(0, 3)).map((change) => (
307
+ <ChangeEntry key={change.id} change={change} />
308
+ ))}
309
+ {!isExpanded && changes.length > 3 && (
310
+ <Button
311
+ variant="ghost"
312
+ size="sm"
313
+ className="w-full"
314
+ onClick={() => setIsExpanded(true)}
315
+ >
316
+ Show {changes.length - 3} more...
317
+ </Button>
318
+ )}
319
+ </div>
320
+ </div>
321
+ )
322
+ })}
323
+ </div>
324
+
325
+ {/* Contributors */}
326
+ {release.contributors.length > 0 && (
327
+ <div className="pt-4 border-t">
328
+ <h4 className="font-medium flex items-center gap-2 mb-2">
329
+ <Users className="h-4 w-4" />
330
+ Contributors ({release.contributors.length})
331
+ </h4>
332
+ <div className="flex flex-wrap gap-2">
333
+ {release.contributors.map((contributor) => (
334
+ <div
335
+ key={contributor.id}
336
+ className="flex items-center gap-2 px-2 py-1 bg-muted rounded-full"
337
+ >
338
+ <Avatar className="h-5 w-5">
339
+ <AvatarImage src={contributor.avatar} />
340
+ <AvatarFallback className="text-xs">
341
+ {contributor.name.slice(0, 2).toUpperCase()}
342
+ </AvatarFallback>
343
+ </Avatar>
344
+ <span className="text-sm">@{contributor.username}</span>
345
+ </div>
346
+ ))}
347
+ </div>
348
+ </div>
349
+ )}
350
+
351
+ {/* Download */}
352
+ {release.downloadUrl && (
353
+ <div className="pt-4 border-t">
354
+ <Button variant="outline" asChild>
355
+ <a href={release.downloadUrl} download>
356
+ <Download className="h-4 w-4 mr-2" />
357
+ Download Release
358
+ </a>
359
+ </Button>
360
+ </div>
361
+ )}
362
+ </CardContent>
363
+ </Card>
364
+ )
365
+ }
366
+
367
+ export function ReleaseNotes({
368
+ releases,
369
+ onCreateRelease,
370
+ onEditRelease,
371
+ onPublishRelease,
372
+ onAddChange,
373
+ showEditor = false,
374
+ className,
375
+ }: ReleaseNotesProps) {
376
+ const [filterType, setFilterType] = React.useState<ReleaseType | "all">("all")
377
+ const [filterStatus, setFilterStatus] = React.useState<ReleaseStatus | "all">("all")
378
+
379
+ // Filter releases
380
+ const filteredReleases = React.useMemo(() => {
381
+ return releases
382
+ .filter((r) => {
383
+ if (filterType !== "all" && r.releaseType !== filterType) return false
384
+ if (filterStatus !== "all" && r.status !== filterStatus) return false
385
+ return true
386
+ })
387
+ .sort((a, b) => {
388
+ // Sort by version (semantic versioning)
389
+ const parseVersion = (v: string) => {
390
+ const parts = v.replace(/^v/, "").split(/[.-]/)
391
+ return parts.map((p) => parseInt(p) || 0)
392
+ }
393
+ const aVer = parseVersion(a.version)
394
+ const bVer = parseVersion(b.version)
395
+ for (let i = 0; i < Math.max(aVer.length, bVer.length); i++) {
396
+ const diff = (bVer[i] || 0) - (aVer[i] || 0)
397
+ if (diff !== 0) return diff
398
+ }
399
+ return 0
400
+ })
401
+ }, [releases, filterType, filterStatus])
402
+
403
+ // Stats
404
+ const stats = React.useMemo(() => {
405
+ return {
406
+ total: releases.length,
407
+ published: releases.filter((r) => r.status === "published").length,
408
+ draft: releases.filter((r) => r.status === "draft").length,
409
+ features: releases.reduce((acc, r) => acc + r.changes.filter((c) => c.type === "feature").length, 0),
410
+ fixes: releases.reduce((acc, r) => acc + r.changes.filter((c) => c.type === "fix").length, 0),
411
+ }
412
+ }, [releases])
413
+
414
+ return (
415
+ <div className={cn("space-y-6", className)}>
416
+ {/* Header */}
417
+ <Card>
418
+ <CardHeader>
419
+ <div className="flex items-center justify-between">
420
+ <div className="flex items-center gap-3">
421
+ <Rocket className="h-6 w-6" />
422
+ <div>
423
+ <CardTitle>Release Notes</CardTitle>
424
+ <p className="text-sm text-muted-foreground">
425
+ Track changes and publish releases
426
+ </p>
427
+ </div>
428
+ </div>
429
+
430
+ <div className="flex items-center gap-4">
431
+ <div className="flex items-center gap-6 text-sm">
432
+ <span className="flex items-center gap-1">
433
+ <Tag className="h-4 w-4 text-muted-foreground" />
434
+ <strong>{stats.total}</strong> releases
435
+ </span>
436
+ <span className="flex items-center gap-1">
437
+ <Sparkles className="h-4 w-4 text-green-500" />
438
+ <strong>{stats.features}</strong> features
439
+ </span>
440
+ <span className="flex items-center gap-1">
441
+ <Bug className="h-4 w-4 text-red-500" />
442
+ <strong>{stats.fixes}</strong> fixes
443
+ </span>
444
+ </div>
445
+
446
+ {onCreateRelease && (
447
+ <Button onClick={onCreateRelease}>
448
+ <Plus className="h-4 w-4 mr-1" />
449
+ New Release
450
+ </Button>
451
+ )}
452
+ </div>
453
+ </div>
454
+ </CardHeader>
455
+
456
+ <CardContent className="pt-0">
457
+ <div className="flex items-center gap-3">
458
+ <Select value={filterType} onValueChange={(v) => setFilterType(v as ReleaseType | "all")}>
459
+ <SelectTrigger className="w-32">
460
+ <SelectValue placeholder="Type" />
461
+ </SelectTrigger>
462
+ <SelectContent>
463
+ <SelectItem value="all">All Types</SelectItem>
464
+ <SelectItem value="major">Major</SelectItem>
465
+ <SelectItem value="minor">Minor</SelectItem>
466
+ <SelectItem value="patch">Patch</SelectItem>
467
+ <SelectItem value="prerelease">Pre-release</SelectItem>
468
+ </SelectContent>
469
+ </Select>
470
+
471
+ <Select value={filterStatus} onValueChange={(v) => setFilterStatus(v as ReleaseStatus | "all")}>
472
+ <SelectTrigger className="w-32">
473
+ <SelectValue placeholder="Status" />
474
+ </SelectTrigger>
475
+ <SelectContent>
476
+ <SelectItem value="all">All Status</SelectItem>
477
+ <SelectItem value="published">Published</SelectItem>
478
+ <SelectItem value="draft">Draft</SelectItem>
479
+ <SelectItem value="scheduled">Scheduled</SelectItem>
480
+ </SelectContent>
481
+ </Select>
482
+ </div>
483
+ </CardContent>
484
+ </Card>
485
+
486
+ {/* Releases */}
487
+ <ScrollArea className="h-[600px]">
488
+ <div className="space-y-4 pr-4">
489
+ {filteredReleases.length === 0 ? (
490
+ <div className="flex flex-col items-center justify-center h-48 text-muted-foreground">
491
+ <Tag className="h-12 w-12 mb-4" />
492
+ <p className="text-lg font-medium">No releases found</p>
493
+ <p className="text-sm">Create your first release to get started</p>
494
+ </div>
495
+ ) : (
496
+ filteredReleases.map((release, index) => (
497
+ <ReleaseCard
498
+ key={release.id}
499
+ release={release}
500
+ expanded={index === 0}
501
+ onEdit={onEditRelease ? () => onEditRelease(release) : undefined}
502
+ onPublish={onPublishRelease ? () => onPublishRelease(release) : undefined}
503
+ />
504
+ ))
505
+ )}
506
+ </div>
507
+ </ScrollArea>
508
+ </div>
509
+ )
510
+ }
511
+
512
+ // Default sample data
513
+ const sampleContributors: Contributor[] = [
514
+ { id: "1", name: "John Doe", username: "johndoe", avatar: "" },
515
+ { id: "2", name: "Jane Smith", username: "janesmith", avatar: "" },
516
+ { id: "3", name: "Bob Wilson", username: "bobwilson", avatar: "" },
517
+ ]
518
+
519
+ export const defaultReleases: Release[] = [
520
+ {
521
+ id: "1",
522
+ version: "v2.5.0",
523
+ name: "Aurora",
524
+ releaseType: "minor",
525
+ status: "published",
526
+ publishedAt: new Date(Date.now() - 2 * 24 * 3600000),
527
+ createdAt: new Date(Date.now() - 5 * 24 * 3600000),
528
+ summary: "This release introduces new authentication features, performance improvements, and several bug fixes.",
529
+ contributors: sampleContributors,
530
+ compareUrl: "https://github.com/example/repo/compare/v2.4.0...v2.5.0",
531
+ changes: [
532
+ {
533
+ id: "c1",
534
+ type: "feature",
535
+ title: "Added OAuth 2.0 support for GitHub and Google",
536
+ description: "Users can now sign in using their GitHub or Google accounts",
537
+ pullRequest: { number: 234, url: "#" },
538
+ contributors: [sampleContributors[0]],
539
+ },
540
+ {
541
+ id: "c2",
542
+ type: "feature",
543
+ title: "New dashboard widgets for analytics",
544
+ pullRequest: { number: 238, url: "#" },
545
+ contributors: [sampleContributors[1]],
546
+ },
547
+ {
548
+ id: "c3",
549
+ type: "improvement",
550
+ title: "Redesigned settings page with better UX",
551
+ pullRequest: { number: 241, url: "#" },
552
+ },
553
+ {
554
+ id: "c4",
555
+ type: "performance",
556
+ title: "Reduced initial bundle size by 30%",
557
+ description: "Implemented code splitting and lazy loading for major routes",
558
+ pullRequest: { number: 245, url: "#" },
559
+ contributors: [sampleContributors[2]],
560
+ },
561
+ {
562
+ id: "c5",
563
+ type: "fix",
564
+ title: "Fixed memory leak in WebSocket connection",
565
+ pullRequest: { number: 250, url: "#" },
566
+ },
567
+ {
568
+ id: "c6",
569
+ type: "fix",
570
+ title: "Fixed date picker not respecting timezone",
571
+ pullRequest: { number: 252, url: "#" },
572
+ },
573
+ {
574
+ id: "c7",
575
+ type: "security",
576
+ title: "Updated dependencies with security vulnerabilities",
577
+ description: "Updated axios, lodash, and other packages with known CVEs",
578
+ pullRequest: { number: 248, url: "#" },
579
+ },
580
+ ],
581
+ },
582
+ {
583
+ id: "2",
584
+ version: "v2.4.1",
585
+ releaseType: "patch",
586
+ status: "published",
587
+ publishedAt: new Date(Date.now() - 14 * 24 * 3600000),
588
+ createdAt: new Date(Date.now() - 15 * 24 * 3600000),
589
+ contributors: [sampleContributors[0]],
590
+ changes: [
591
+ {
592
+ id: "c8",
593
+ type: "fix",
594
+ title: "Fixed critical authentication bypass vulnerability",
595
+ breaking: false,
596
+ pullRequest: { number: 230, url: "#" },
597
+ },
598
+ {
599
+ id: "c9",
600
+ type: "fix",
601
+ title: "Fixed incorrect pagination in search results",
602
+ pullRequest: { number: 231, url: "#" },
603
+ },
604
+ ],
605
+ },
606
+ {
607
+ id: "3",
608
+ version: "v3.0.0-beta.1",
609
+ name: "Next Generation",
610
+ releaseType: "prerelease",
611
+ status: "draft",
612
+ createdAt: new Date(Date.now() - 1 * 24 * 3600000),
613
+ contributors: sampleContributors,
614
+ upgradeNotes: "This is a major release with breaking changes. Please review the migration guide before upgrading.",
615
+ changes: [
616
+ {
617
+ id: "c10",
618
+ type: "breaking",
619
+ title: "Removed deprecated API endpoints",
620
+ description: "The v1 API endpoints have been removed. Please migrate to v2 endpoints.",
621
+ breaking: true,
622
+ },
623
+ {
624
+ id: "c11",
625
+ type: "feature",
626
+ title: "New real-time collaboration features",
627
+ description: "Multiple users can now edit documents simultaneously with live cursors",
628
+ },
629
+ {
630
+ id: "c12",
631
+ type: "feature",
632
+ title: "Dark mode support throughout the application",
633
+ },
634
+ {
635
+ id: "c13",
636
+ type: "breaking",
637
+ title: "Changed authentication token format",
638
+ description: "Tokens now use JWT format. Old tokens will be invalidated.",
639
+ breaking: true,
640
+ },
641
+ ],
642
+ },
643
+ ]
@@ -554,13 +554,13 @@ export function WakaSidebar({
554
554
  onClose: () => setIsOpen(false),
555
555
  }
556
556
 
557
- // Styles personnalisés
557
+ // Styles personnalisés - utilise les variables CSS du thème par défaut
558
558
  const customStyles = {
559
- "--sidebar-bg": backgroundColor || "hsl(222 47% 11%)",
560
- "--sidebar-text": textColor || "hsl(210 40% 96%)",
561
- "--sidebar-active": activeColor || "hsl(187 85% 43%)",
562
- "--sidebar-active-foreground": "hsl(222 47% 11%)",
563
- "--sidebar-hover": hoverColor || "rgba(255, 255, 255, 0.1)",
559
+ "--sidebar-bg": backgroundColor || "hsl(var(--sidebar-background, var(--card)))",
560
+ "--sidebar-text": textColor || "hsl(var(--sidebar-foreground, var(--card-foreground)))",
561
+ "--sidebar-active": activeColor || "hsl(var(--sidebar-primary, var(--primary)))",
562
+ "--sidebar-active-foreground": "hsl(var(--sidebar-primary-foreground, var(--primary-foreground)))",
563
+ "--sidebar-hover": hoverColor || "hsl(var(--sidebar-accent, var(--accent)))",
564
564
  } as React.CSSProperties
565
565
 
566
566
  const sidebarClasses = cn(
@@ -28,6 +28,7 @@ import {
28
28
  AlertDialogTitle,
29
29
  } from "../../alert-dialog"
30
30
  import { cn } from "../../../utils/cn"
31
+ import { createHighlightRegex } from "../../../utils/security"
31
32
  import { formatters } from "../formatters"
32
33
  import type { ColumnTemplate, ColumnTemplateOptions, ColumnTemplateAction } from "../types"
33
34
 
@@ -715,9 +716,9 @@ export const textTemplate: ColumnTemplate<unknown, unknown> = {
715
716
  ? formatters.truncate(strValue, options.maxLength)
716
717
  : strValue
717
718
 
718
- // Highlight si spécifié
719
+ // Highlight si spécifié (with escaped regex for security)
719
720
  if (options?.highlight) {
720
- const regex = new RegExp(`(${options.highlight})`, "gi")
721
+ const regex = createHighlightRegex(options.highlight)
721
722
  const parts = displayValue.split(regex)
722
723
 
723
724
  return (