tembro 3.1.13 → 3.1.15
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/CHANGELOG.md +11 -0
- package/dist/showcase/package-meta.d.ts +2 -2
- package/dist/src/components/display/kanban.cjs +1 -1
- package/dist/src/components/display/kanban.js +135 -59
- package/dist/src/components/notifications/notification-center.cjs +1 -1
- package/dist/src/components/notifications/notification-center.js +10 -10
- package/dist/src/showcase/package-meta.cjs +1 -1
- package/dist/src/showcase/package-meta.js +1 -1
- package/dist/src/showcase/site-data.cjs +1 -1
- package/dist/src/showcase/site-data.js +1 -1
- package/dist/src/showcase/tembro-registry.json.cjs +1 -1
- package/dist/src/showcase/tembro-registry.json.js +1 -1
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +1 -1
- package/packages/cli/vendor/src/components/display/kanban.tsx +175 -57
- package/packages/cli/vendor/src/components/notifications/notification-center.tsx +11 -11
- package/packages/cli/vendor/src/showcase/package-meta.ts +1 -1
- package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/HeroSection.tsx +2 -2
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/WorkbenchSidebar.tsx +1 -1
- package/registry.json +1 -1
|
@@ -89,16 +89,30 @@ export type KanbanBoardProps = Omit<React.ComponentProps<"div">, "defaultValue">
|
|
|
89
89
|
overlayClassName?: string
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
type KanbanCardViewProps = {
|
|
93
|
-
card: KanbanCard
|
|
94
|
-
column: KanbanColumn
|
|
95
|
-
index: number
|
|
96
|
-
boardDisabled: boolean
|
|
92
|
+
type KanbanCardViewProps = {
|
|
93
|
+
card: KanbanCard
|
|
94
|
+
column: KanbanColumn
|
|
95
|
+
index: number
|
|
96
|
+
boardDisabled: boolean
|
|
97
97
|
renderCard?: KanbanBoardProps["renderCard"]
|
|
98
|
-
onCardClick?: KanbanBoardProps["onCardClick"]
|
|
99
|
-
dragHandleLabel?: KanbanBoardProps["dragHandleLabel"]
|
|
100
|
-
cardClassName?: string
|
|
101
|
-
|
|
98
|
+
onCardClick?: KanbanBoardProps["onCardClick"]
|
|
99
|
+
dragHandleLabel?: KanbanBoardProps["dragHandleLabel"]
|
|
100
|
+
cardClassName?: string
|
|
101
|
+
onNativeDragStart?: (source: NativeDragSource) => void
|
|
102
|
+
onNativeDragEnd?: () => void
|
|
103
|
+
onNativeDrop?: (target: NativeDropTarget) => void
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type NativeDragSource = {
|
|
107
|
+
cardKey: string
|
|
108
|
+
columnKey: string
|
|
109
|
+
index: number
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type NativeDropTarget = {
|
|
113
|
+
columnKey: string
|
|
114
|
+
index?: number
|
|
115
|
+
}
|
|
102
116
|
|
|
103
117
|
function createCardMap(columns: KanbanColumn[]) {
|
|
104
118
|
return new Map(
|
|
@@ -108,10 +122,10 @@ function createCardMap(columns: KanbanColumn[]) {
|
|
|
108
122
|
)
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
function moveKanbanCards(
|
|
112
|
-
columns: KanbanColumn[],
|
|
113
|
-
event: DragOverEvent | DragEndEvent
|
|
114
|
-
) {
|
|
125
|
+
function moveKanbanCards(
|
|
126
|
+
columns: KanbanColumn[],
|
|
127
|
+
event: DragOverEvent | DragEndEvent
|
|
128
|
+
) {
|
|
115
129
|
const cardMap = createCardMap(columns)
|
|
116
130
|
const cardIdsByColumn = Object.fromEntries(
|
|
117
131
|
columns.map((column) => [
|
|
@@ -126,8 +140,57 @@ function moveKanbanCards(
|
|
|
126
140
|
cards: (movedIds[getColumnId(column.key)] ?? [])
|
|
127
141
|
.map((id) => cardMap.get(String(id)))
|
|
128
142
|
.filter((card): card is KanbanCard => Boolean(card)),
|
|
129
|
-
}))
|
|
130
|
-
}
|
|
143
|
+
}))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function moveKanbanCardToTarget(
|
|
147
|
+
columns: KanbanColumn[],
|
|
148
|
+
source: NativeDragSource,
|
|
149
|
+
target: NativeDropTarget
|
|
150
|
+
): { columns: KanbanColumn[]; change: KanbanCardMove } | null {
|
|
151
|
+
const sourceColumn = columns.find((column) => column.key === source.columnKey)
|
|
152
|
+
const targetColumn = columns.find((column) => column.key === target.columnKey)
|
|
153
|
+
if (!sourceColumn || !targetColumn) return null
|
|
154
|
+
|
|
155
|
+
const card = sourceColumn.cards.find((item) => item.key === source.cardKey)
|
|
156
|
+
if (!card) return null
|
|
157
|
+
|
|
158
|
+
const sourceIndex = sourceColumn.cards.findIndex((item) => item.key === source.cardKey)
|
|
159
|
+
if (sourceIndex < 0) return null
|
|
160
|
+
|
|
161
|
+
const targetCards = targetColumn.cards
|
|
162
|
+
let targetIndex = target.index ?? targetCards.length
|
|
163
|
+
if (sourceColumn.key === targetColumn.key && targetIndex > sourceIndex) {
|
|
164
|
+
targetIndex -= 1
|
|
165
|
+
}
|
|
166
|
+
targetIndex = Math.max(0, Math.min(targetIndex, targetCards.length))
|
|
167
|
+
if (sourceColumn.key === targetColumn.key && sourceIndex === targetIndex) return null
|
|
168
|
+
|
|
169
|
+
const nextColumns = columns.map((column) => {
|
|
170
|
+
if (column.key === sourceColumn.key) {
|
|
171
|
+
return { ...column, cards: column.cards.filter((item) => item.key !== source.cardKey) }
|
|
172
|
+
}
|
|
173
|
+
return column
|
|
174
|
+
}).map((column) => {
|
|
175
|
+
if (column.key !== targetColumn.key) return column
|
|
176
|
+
const nextCards = [...column.cards]
|
|
177
|
+
nextCards.splice(targetIndex, 0, card)
|
|
178
|
+
return { ...column, cards: nextCards }
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const resolvedTargetColumn = nextColumns.find((column) => column.key === targetColumn.key) ?? targetColumn
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
columns: nextColumns,
|
|
185
|
+
change: {
|
|
186
|
+
card,
|
|
187
|
+
fromColumn: sourceColumn,
|
|
188
|
+
toColumn: resolvedTargetColumn,
|
|
189
|
+
fromIndex: sourceIndex,
|
|
190
|
+
toIndex: targetIndex,
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
}
|
|
131
194
|
|
|
132
195
|
const KanbanDragHandle = React.forwardRef<HTMLButtonElement, {
|
|
133
196
|
label: string
|
|
@@ -169,16 +232,19 @@ function DefaultKanbanCard({ card, handle }: { card: KanbanCard; handle: React.R
|
|
|
169
232
|
)
|
|
170
233
|
}
|
|
171
234
|
|
|
172
|
-
function KanbanCardView({
|
|
173
|
-
card,
|
|
174
|
-
column,
|
|
175
|
-
index,
|
|
235
|
+
function KanbanCardView({
|
|
236
|
+
card,
|
|
237
|
+
column,
|
|
238
|
+
index,
|
|
176
239
|
boardDisabled,
|
|
177
240
|
renderCard,
|
|
178
|
-
onCardClick,
|
|
179
|
-
dragHandleLabel,
|
|
180
|
-
cardClassName,
|
|
181
|
-
|
|
241
|
+
onCardClick,
|
|
242
|
+
dragHandleLabel,
|
|
243
|
+
cardClassName,
|
|
244
|
+
onNativeDragStart,
|
|
245
|
+
onNativeDragEnd,
|
|
246
|
+
onNativeDrop,
|
|
247
|
+
}: KanbanCardViewProps) {
|
|
182
248
|
const disabled = boardDisabled || column.disabled === true || card.disabled === true
|
|
183
249
|
const { ref, handleRef, isDragging, isDropTarget } = useSortable({
|
|
184
250
|
id: getCardId(card.key),
|
|
@@ -206,16 +272,17 @@ function KanbanCardView({
|
|
|
206
272
|
}
|
|
207
273
|
|
|
208
274
|
return (
|
|
209
|
-
<article
|
|
210
|
-
ref={ref as React.Ref<HTMLElement>}
|
|
211
|
-
data-slot="kanban-card"
|
|
275
|
+
<article
|
|
276
|
+
ref={ref as React.Ref<HTMLElement>}
|
|
277
|
+
data-slot="kanban-card"
|
|
212
278
|
data-dragging={isDragging || undefined}
|
|
213
279
|
data-drop-target={isDropTarget || undefined}
|
|
214
280
|
data-disabled={disabled || undefined}
|
|
215
281
|
role={onCardClick && !disabled ? "button" : undefined}
|
|
216
282
|
tabIndex={onCardClick && !disabled ? 0 : undefined}
|
|
217
|
-
aria-label={typeof card.title === "string" ? card.title : undefined}
|
|
218
|
-
|
|
283
|
+
aria-label={typeof card.title === "string" ? card.title : undefined}
|
|
284
|
+
draggable={!disabled}
|
|
285
|
+
className={cn(
|
|
219
286
|
"rounded-lg outline-none transition-[opacity,box-shadow]",
|
|
220
287
|
onCardClick && !disabled && "cursor-pointer focus-visible:ring-2 focus-visible:ring-ring",
|
|
221
288
|
isDropTarget && "ring-2 ring-primary/30",
|
|
@@ -223,10 +290,27 @@ function KanbanCardView({
|
|
|
223
290
|
disabled && "opacity-55",
|
|
224
291
|
cardClassName
|
|
225
292
|
)}
|
|
226
|
-
onClick={() => {
|
|
227
|
-
if (!disabled) onCardClick?.(card, column)
|
|
228
|
-
}}
|
|
229
|
-
|
|
293
|
+
onClick={() => {
|
|
294
|
+
if (!disabled) onCardClick?.(card, column)
|
|
295
|
+
}}
|
|
296
|
+
onDragStart={(event) => {
|
|
297
|
+
if (disabled) return
|
|
298
|
+
const source = { cardKey: card.key, columnKey: column.key, index }
|
|
299
|
+
event.dataTransfer.effectAllowed = "move"
|
|
300
|
+
event.dataTransfer.setData("text/plain", JSON.stringify(source))
|
|
301
|
+
onNativeDragStart?.(source)
|
|
302
|
+
}}
|
|
303
|
+
onDragEnd={onNativeDragEnd}
|
|
304
|
+
onDragOver={(event) => {
|
|
305
|
+
if (!disabled) event.preventDefault()
|
|
306
|
+
}}
|
|
307
|
+
onDrop={(event) => {
|
|
308
|
+
if (disabled) return
|
|
309
|
+
event.preventDefault()
|
|
310
|
+
event.stopPropagation()
|
|
311
|
+
onNativeDrop?.({ columnKey: column.key, index })
|
|
312
|
+
}}
|
|
313
|
+
onKeyDown={(event) => {
|
|
230
314
|
if (event.target !== event.currentTarget || disabled || !onCardClick) return
|
|
231
315
|
if (event.key === "Enter" || event.key === " ") {
|
|
232
316
|
event.preventDefault()
|
|
@@ -246,18 +330,24 @@ function KanbanColumnView({
|
|
|
246
330
|
onCardClick,
|
|
247
331
|
dragHandleLabel,
|
|
248
332
|
emptyColumn,
|
|
249
|
-
columnClassName,
|
|
250
|
-
cardClassName,
|
|
251
|
-
|
|
333
|
+
columnClassName,
|
|
334
|
+
cardClassName,
|
|
335
|
+
onNativeDragStart,
|
|
336
|
+
onNativeDragEnd,
|
|
337
|
+
onNativeDrop,
|
|
338
|
+
}: {
|
|
252
339
|
column: KanbanColumn
|
|
253
340
|
boardDisabled: boolean
|
|
254
341
|
renderCard?: KanbanBoardProps["renderCard"]
|
|
255
342
|
onCardClick?: KanbanBoardProps["onCardClick"]
|
|
256
343
|
dragHandleLabel?: KanbanBoardProps["dragHandleLabel"]
|
|
257
|
-
emptyColumn: React.ReactNode
|
|
258
|
-
columnClassName?: string
|
|
259
|
-
cardClassName?: string
|
|
260
|
-
|
|
344
|
+
emptyColumn: React.ReactNode
|
|
345
|
+
columnClassName?: string
|
|
346
|
+
cardClassName?: string
|
|
347
|
+
onNativeDragStart?: (source: NativeDragSource) => void
|
|
348
|
+
onNativeDragEnd?: () => void
|
|
349
|
+
onNativeDrop?: (target: NativeDropTarget) => void
|
|
350
|
+
}) {
|
|
261
351
|
const { ref, isDropTarget } = useDroppable({
|
|
262
352
|
id: getColumnId(column.key),
|
|
263
353
|
type: "tembro-kanban-column",
|
|
@@ -274,13 +364,21 @@ function KanbanColumnView({
|
|
|
274
364
|
data-drop-target={isDropTarget || undefined}
|
|
275
365
|
data-disabled={column.disabled || undefined}
|
|
276
366
|
aria-label={typeof column.title === "string" ? column.title : undefined}
|
|
277
|
-
className={cn(
|
|
367
|
+
className={cn(
|
|
278
368
|
"grid min-h-40 content-start gap-3 rounded-lg border bg-muted/35 p-3 transition-[border-color,background-color,box-shadow]",
|
|
279
369
|
isDropTarget && "border-primary/60 bg-primary/5 ring-2 ring-primary/15",
|
|
280
370
|
column.disabled && "opacity-60",
|
|
281
|
-
columnClassName
|
|
282
|
-
)}
|
|
283
|
-
|
|
371
|
+
columnClassName
|
|
372
|
+
)}
|
|
373
|
+
onDragOver={(event) => {
|
|
374
|
+
if (!(boardDisabled || column.disabled === true)) event.preventDefault()
|
|
375
|
+
}}
|
|
376
|
+
onDrop={(event) => {
|
|
377
|
+
if (boardDisabled || column.disabled === true) return
|
|
378
|
+
event.preventDefault()
|
|
379
|
+
onNativeDrop?.({ columnKey: column.key, index: column.cards.length })
|
|
380
|
+
}}
|
|
381
|
+
>
|
|
284
382
|
<div className="flex items-start justify-between gap-3">
|
|
285
383
|
<div className="grid gap-0.5">
|
|
286
384
|
<h3 className="text-sm font-semibold text-foreground">{column.title}</h3>
|
|
@@ -306,9 +404,12 @@ function KanbanColumnView({
|
|
|
306
404
|
boardDisabled={boardDisabled}
|
|
307
405
|
renderCard={renderCard}
|
|
308
406
|
onCardClick={onCardClick}
|
|
309
|
-
dragHandleLabel={dragHandleLabel}
|
|
310
|
-
cardClassName={cardClassName}
|
|
311
|
-
|
|
407
|
+
dragHandleLabel={dragHandleLabel}
|
|
408
|
+
cardClassName={cardClassName}
|
|
409
|
+
onNativeDragStart={onNativeDragStart}
|
|
410
|
+
onNativeDragEnd={onNativeDragEnd}
|
|
411
|
+
onNativeDrop={onNativeDrop}
|
|
412
|
+
/>
|
|
312
413
|
))
|
|
313
414
|
)}
|
|
314
415
|
</div>
|
|
@@ -334,9 +435,10 @@ function KanbanBoard({
|
|
|
334
435
|
...props
|
|
335
436
|
}: KanbanBoardProps) {
|
|
336
437
|
const [internalColumns, setInternalColumns] = React.useState<KanbanColumn[]>(() => defaultColumns)
|
|
337
|
-
const [draftColumns, setDraftColumns] = React.useState<KanbanColumn[] | null>(null)
|
|
338
|
-
const [activeCardId, setActiveCardId] = React.useState<string | null>(null)
|
|
339
|
-
const draftColumnsRef = React.useRef<KanbanColumn[] | null>(null)
|
|
438
|
+
const [draftColumns, setDraftColumns] = React.useState<KanbanColumn[] | null>(null)
|
|
439
|
+
const [activeCardId, setActiveCardId] = React.useState<string | null>(null)
|
|
440
|
+
const draftColumnsRef = React.useRef<KanbanColumn[] | null>(null)
|
|
441
|
+
const nativeDragRef = React.useRef<NativeDragSource | null>(null)
|
|
340
442
|
const columns = columnsProp ?? internalColumns
|
|
341
443
|
const renderedColumns = draftColumns ?? columns
|
|
342
444
|
const isControlled = columnsProp !== undefined
|
|
@@ -361,7 +463,7 @@ function KanbanBoard({
|
|
|
361
463
|
setDraftColumns(nextColumns)
|
|
362
464
|
}
|
|
363
465
|
|
|
364
|
-
function handleDragEnd(event: DragEndEvent) {
|
|
466
|
+
function handleDragEnd(event: DragEndEvent) {
|
|
365
467
|
const source = event.operation.source
|
|
366
468
|
setActiveCardId(null)
|
|
367
469
|
|
|
@@ -393,8 +495,21 @@ function KanbanBoard({
|
|
|
393
495
|
}
|
|
394
496
|
if (!isControlled) setInternalColumns(finalColumns)
|
|
395
497
|
onColumnsChange?.(finalColumns, change)
|
|
396
|
-
onCardMove?.(change)
|
|
397
|
-
}
|
|
498
|
+
onCardMove?.(change)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function handleNativeDrop(target: NativeDropTarget) {
|
|
502
|
+
const source = nativeDragRef.current
|
|
503
|
+
nativeDragRef.current = null
|
|
504
|
+
if (!source) return
|
|
505
|
+
|
|
506
|
+
const result = moveKanbanCardToTarget(columns, source, target)
|
|
507
|
+
if (!result) return
|
|
508
|
+
|
|
509
|
+
if (!isControlled) setInternalColumns(result.columns)
|
|
510
|
+
onColumnsChange?.(result.columns, result.change)
|
|
511
|
+
onCardMove?.(result.change)
|
|
512
|
+
}
|
|
398
513
|
|
|
399
514
|
const activeEntry = activeCardId
|
|
400
515
|
? renderedColumns
|
|
@@ -425,11 +540,14 @@ function KanbanBoard({
|
|
|
425
540
|
boardDisabled={disabled}
|
|
426
541
|
renderCard={renderCard}
|
|
427
542
|
onCardClick={onCardClick}
|
|
428
|
-
dragHandleLabel={dragHandleLabel}
|
|
429
|
-
emptyColumn={emptyColumn}
|
|
430
|
-
columnClassName={columnClassName}
|
|
431
|
-
cardClassName={cardClassName}
|
|
432
|
-
|
|
543
|
+
dragHandleLabel={dragHandleLabel}
|
|
544
|
+
emptyColumn={emptyColumn}
|
|
545
|
+
columnClassName={columnClassName}
|
|
546
|
+
cardClassName={cardClassName}
|
|
547
|
+
onNativeDragStart={(source) => { nativeDragRef.current = source }}
|
|
548
|
+
onNativeDragEnd={() => { nativeDragRef.current = null }}
|
|
549
|
+
onNativeDrop={handleNativeDrop}
|
|
550
|
+
/>
|
|
433
551
|
))}
|
|
434
552
|
</div>
|
|
435
553
|
|
|
@@ -47,21 +47,21 @@ function NotificationCenter({
|
|
|
47
47
|
render={<PopoverTrigger />}
|
|
48
48
|
variant="ghost"
|
|
49
49
|
size="icon"
|
|
50
|
-
className="relative rounded-full border
|
|
50
|
+
className="relative rounded-full border bg-white text-foreground shadow-sm hover:bg-muted dark:bg-neutral-950"
|
|
51
51
|
>
|
|
52
52
|
<BellIcon className="size-5" />
|
|
53
53
|
{unreadCount > 0 && (
|
|
54
|
-
<span className="absolute -right-1 -top-1 inline-flex min-w-5 items-center justify-center rounded-full bg-
|
|
54
|
+
<span className="absolute -right-1 -top-1 inline-flex min-w-5 items-center justify-center rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-bold leading-none text-primary-foreground shadow-[0_8px_20px_rgba(16,185,129,0.24)]">
|
|
55
55
|
{unreadCount}
|
|
56
56
|
<span className="sr-only">{unreadCount} unread notifications</span>
|
|
57
57
|
</span>
|
|
58
58
|
)}
|
|
59
59
|
</Button>
|
|
60
60
|
<PopoverContent
|
|
61
|
-
className="w-[24rem] overflow-hidden rounded-3xl border
|
|
61
|
+
className="isolate w-[24rem] overflow-hidden rounded-3xl border bg-white p-0 text-popover-foreground opacity-100 shadow-[0_24px_60px_rgba(15,23,42,0.18)] dark:bg-neutral-950"
|
|
62
62
|
align="end"
|
|
63
63
|
>
|
|
64
|
-
<div className="flex items-center justify-between border-b
|
|
64
|
+
<div className="flex items-center justify-between border-b bg-white px-5 py-4 dark:bg-neutral-950">
|
|
65
65
|
<div className="grid gap-0.5">
|
|
66
66
|
<h4 className="font-semibold tracking-tight text-foreground">{title}</h4>
|
|
67
67
|
<span className="text-xs text-muted-foreground">
|
|
@@ -73,7 +73,7 @@ function NotificationCenter({
|
|
|
73
73
|
<Button
|
|
74
74
|
variant="ghost"
|
|
75
75
|
size="sm"
|
|
76
|
-
className="h-8 rounded-full border
|
|
76
|
+
className="h-8 rounded-full border px-3 text-xs font-semibold text-muted-foreground hover:bg-background hover:text-foreground"
|
|
77
77
|
onClick={onMarkAllRead}
|
|
78
78
|
>
|
|
79
79
|
Mark all read
|
|
@@ -83,7 +83,7 @@ function NotificationCenter({
|
|
|
83
83
|
<Button
|
|
84
84
|
variant="ghost"
|
|
85
85
|
size="sm"
|
|
86
|
-
className="h-8 rounded-full border
|
|
86
|
+
className="h-8 rounded-full border px-3 text-xs font-semibold text-muted-foreground hover:bg-background hover:text-foreground"
|
|
87
87
|
onClick={onClearAll}
|
|
88
88
|
>
|
|
89
89
|
Clear all
|
|
@@ -91,9 +91,9 @@ function NotificationCenter({
|
|
|
91
91
|
) : null}
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
94
|
-
<div className="max-h-[400px] overflow-y-auto bg-
|
|
94
|
+
<div className="max-h-[400px] overflow-y-auto bg-white p-2 dark:bg-neutral-950">
|
|
95
95
|
{notifications.length === 0 ? (
|
|
96
|
-
<div className="rounded-2xl border border-dashed
|
|
96
|
+
<div className="rounded-2xl border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
|
|
97
97
|
{emptyLabel}
|
|
98
98
|
</div>
|
|
99
99
|
) : (
|
|
@@ -105,8 +105,8 @@ function NotificationCenter({
|
|
|
105
105
|
<button
|
|
106
106
|
key={notification.id}
|
|
107
107
|
className={cn(
|
|
108
|
-
"flex flex-col gap-1 rounded-2xl border border-transparent px-4 py-3 text-left transition hover:border-
|
|
109
|
-
!notification.read && "border-
|
|
108
|
+
"flex flex-col gap-1 rounded-2xl border border-transparent bg-white px-4 py-3 text-left transition hover:border-border hover:bg-muted dark:bg-neutral-950 dark:hover:bg-neutral-900",
|
|
109
|
+
!notification.read && "border-primary/20 bg-slate-50 dark:bg-neutral-900"
|
|
110
110
|
)}
|
|
111
111
|
onClick={() => onNotificationClick?.(notification)}
|
|
112
112
|
>
|
|
@@ -115,7 +115,7 @@ function NotificationCenter({
|
|
|
115
115
|
{notification.title}
|
|
116
116
|
</span>
|
|
117
117
|
{!notification.read && (
|
|
118
|
-
<span className="mt-1 flex size-2 shrink-0 rounded-full bg-
|
|
118
|
+
<span className="mt-1 flex size-2 shrink-0 rounded-full bg-primary shadow-[0_0_0_4px_color-mix(in_srgb,var(--primary)_14%,transparent)]" />
|
|
119
119
|
)}
|
|
120
120
|
</div>
|
|
121
121
|
{notification.description && (
|
|
@@ -25,7 +25,7 @@ export function HeroSection() {
|
|
|
25
25
|
<div className="flex flex-wrap gap-2">
|
|
26
26
|
<Button leftIcon={<PlusIcon className="size-4" />}>Create</Button>
|
|
27
27
|
<Button variant="outline" leftIcon={<SettingsIcon className="size-4" />}>Settings</Button>
|
|
28
|
-
<CopyButton value="npx tembro@3.1.
|
|
28
|
+
<CopyButton value="npx tembro@3.1.15 list --json">Copy list command</CopyButton>
|
|
29
29
|
</div>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ export function HeroSection() {
|
|
|
33
33
|
<StatisticCard label="Registry components" value={moduleCount} change="visible below" trend="up" description="tembro add <name>" />
|
|
34
34
|
<StatisticCard label="Local source files" value="152" change="with hooks/lib" trend="up" description="installed by CLI" />
|
|
35
35
|
<StatisticCard label="Categories" value={registryGroups.length} change="all shown" trend="up" description="actions to wizard" />
|
|
36
|
-
<StatisticCard label="Build" value="Pass" change="doctor pass" trend="up" description="tembro@3.1.
|
|
36
|
+
<StatisticCard label="Build" value="Pass" change="doctor pass" trend="up" description="tembro@3.1.15" />
|
|
37
37
|
</StatisticGrid>
|
|
38
38
|
</div>
|
|
39
39
|
</section>
|