@@ -50,6 +50,67 @@ function Logo(props: any) {
50
50
)
51
51
}
52
52
53
+ function formatTime ( ms : number ) : string {
54
+ if ( ms === 0 ) return '0s'
55
+
56
+ const units = [ 's' , 'min' , 'h' , 'd' ]
57
+ const values = [ ms / 1000 , ms / 60000 , ms / 3600000 , ms / 86400000 ]
58
+
59
+ let chosenUnitIndex = 0
60
+ for ( let i = 1 ; i < values . length ; i ++ ) {
61
+ if ( values [ i ] ! < 1 ) break
62
+ chosenUnitIndex = i
63
+ }
64
+
65
+ const formatter = new Intl . NumberFormat ( navigator . language , {
66
+ compactDisplay : 'short' ,
67
+ notation : 'compact' ,
68
+ maximumFractionDigits : 0 ,
69
+ } )
70
+
71
+ return formatter . format ( values [ chosenUnitIndex ] ! ) + units [ chosenUnitIndex ]
72
+ }
73
+
74
+ function CollectionStats ( { collection } : { collection : CollectionMetadata } ) {
75
+ const styles = useStyles ( )
76
+
77
+ if ( collection . type === 'collection' ) {
78
+ // Standard collection stats
79
+ return (
80
+ < div class = { styles ( ) . collectionStats } >
81
+ < div > { collection . size } </ div >
82
+ < div > /</ div >
83
+ < div > { collection . transactionCount } </ div >
84
+ < div > /</ div >
85
+ < div > { formatTime ( collection . gcTime || 0 ) } </ div >
86
+ < div > /</ div >
87
+ < div class = { cx (
88
+ styles ( ) . collectionStatus ,
89
+ collection . status === 'error' ? styles ( ) . collectionStatusError : ''
90
+ ) } >
91
+ { collection . status }
92
+ </ div >
93
+ </ div >
94
+ )
95
+ } else {
96
+ // Live query collection stats
97
+ return (
98
+ < div class = { styles ( ) . collectionStats } >
99
+ < div > { collection . size } </ div >
100
+ < div > /</ div >
101
+ < div > { formatTime ( collection . gcTime || 0 ) } </ div >
102
+ < div > /</ div >
103
+ < div class = { cx (
104
+ styles ( ) . collectionStatus ,
105
+ collection . status === 'error' ? styles ( ) . collectionStatusError : ''
106
+ ) } >
107
+ { collection . status }
108
+ </ div >
109
+ </ div >
110
+ )
111
+ }
112
+ }
113
+
53
114
function CollectionItem ( {
54
115
collection,
55
116
isActive,
@@ -70,13 +131,7 @@ function CollectionItem({
70
131
onClick = { ( ) => onSelect ( collection ) }
71
132
>
72
133
< div class = { styles ( ) . collectionName } > { collection . id } </ div >
73
- < div class = { styles ( ) . collectionCount } > ({ collection . size } )</ div >
74
- < div class = { cx (
75
- styles ( ) . collectionStatus ,
76
- collection . status === 'error' ? styles ( ) . collectionStatusError : ''
77
- ) } >
78
- { collection . status }
79
- </ div >
134
+ < CollectionStats collection = { collection } />
80
135
</ div >
81
136
)
82
137
}
@@ -112,8 +167,13 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
112
167
const updateCollections = ( ) => {
113
168
if ( typeof window === 'undefined' ) return
114
169
try {
115
- const collections = registry ( ) . getAllCollectionMetadata ( )
116
- setCollections ( collections )
170
+ const newCollections = registry ( ) . getAllCollectionMetadata ( )
171
+ const currentCollections = collections ( )
172
+
173
+ // Only update if collections data actually changed
174
+ if ( hasCollectionsChanged ( currentCollections , newCollections ) ) {
175
+ setCollections ( newCollections )
176
+ }
117
177
} catch ( error ) {
118
178
// Silently handle errors when fetching collections metadata
119
179
}
@@ -179,6 +239,14 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
179
239
)
180
240
} )
181
241
242
+ // Group collections by type
243
+ const standardCollections = createMemo ( ( ) =>
244
+ sortedCollections ( ) . filter ( c => c . type === 'collection' )
245
+ )
246
+
247
+ const liveCollections = createMemo ( ( ) =>
248
+ sortedCollections ( ) . filter ( c => c . type === 'live-query' )
249
+ )
182
250
183
251
184
252
return (
@@ -224,39 +292,36 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
224
292
225
293
< div class = { styles ( ) . firstContainer } >
226
294
< div class = { styles ( ) . row } >
227
- < Logo />
295
+ < div class = { styles ( ) . headerContainer } >
296
+ < Logo />
297
+ { /* Tab Navigation */ }
298
+ < div class = { styles ( ) . tabNav } >
299
+ < button
300
+ onClick = { ( ) => setSelectedView ( 'collections' ) }
301
+ class = { cx (
302
+ styles ( ) . tabBtn ,
303
+ selectedView ( ) === 'collections' && styles ( ) . tabBtnActive
304
+ ) }
305
+ >
306
+ Collections ({ collections ( ) . length } )
307
+ </ button >
308
+ < button
309
+ onClick = { ( ) => setSelectedView ( 'transactions' ) }
310
+ class = { cx (
311
+ styles ( ) . tabBtn ,
312
+ selectedView ( ) === 'transactions' && styles ( ) . tabBtnActive
313
+ ) }
314
+ >
315
+ Transactions ({ registry ( ) . getTransactions ( ) . length } )
316
+ </ button >
317
+ </ div >
318
+ </ div >
228
319
</ div >
229
320
< div class = { styles ( ) . collectionsExplorerContainer } >
230
- { /* Tab Navigation */ }
231
- < div class = { styles ( ) . tabNav } >
232
- < button
233
- onClick = { ( ) => setSelectedView ( 'collections' ) }
234
- class = { cx (
235
- styles ( ) . tabBtn ,
236
- selectedView ( ) === 'collections' && styles ( ) . tabBtnActive
237
- ) }
238
- >
239
- Collections
240
- </ button >
241
- < button
242
- onClick = { ( ) => setSelectedView ( 'transactions' ) }
243
- class = { cx (
244
- styles ( ) . tabBtn ,
245
- selectedView ( ) === 'transactions' && styles ( ) . tabBtnActive
246
- ) }
247
- >
248
- Transactions ({ registry ( ) . getTransactions ( ) . length } )
249
- </ button >
250
- </ div >
251
-
252
321
{ /* Content based on selected view */ }
253
322
< div class = { styles ( ) . sidebarContent } >
254
323
< Show when = { selectedView ( ) === 'collections' } >
255
324
< div class = { styles ( ) . collectionsExplorer } >
256
- < div class = { styles ( ) . collectionsHeader } >
257
- < div > Collections ({ collections ( ) . length } )</ div >
258
- </ div >
259
-
260
325
< div class = { styles ( ) . collectionsList } >
261
326
< Show
262
327
when = { sortedCollections ( ) . length > 0 }
@@ -266,13 +331,53 @@ export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel(
266
331
</ div >
267
332
}
268
333
>
269
- < For each = { sortedCollections ( ) } > { ( collection ) =>
270
- < CollectionItem
271
- collection = { collection }
272
- isActive = { ( ) => collection . id === activeCollectionId ( ) }
273
- onSelect = { ( c ) => setActiveCollectionId ( c . id ) }
274
- />
275
- } </ For >
334
+ { /* Standard Collections */ }
335
+ < Show when = { standardCollections ( ) . length > 0 } >
336
+ < div class = { styles ( ) . collectionGroup } >
337
+ < div class = { styles ( ) . collectionGroupHeader } >
338
+ < div > Standard Collections ({ standardCollections ( ) . length } )</ div >
339
+ < div class = { styles ( ) . collectionGroupStats } >
340
+ < span > Items</ span >
341
+ < span > /</ span >
342
+ < span > Txn</ span >
343
+ < span > /</ span >
344
+ < span > GC</ span >
345
+ < span > /</ span >
346
+ < span > Status</ span >
347
+ </ div >
348
+ </ div >
349
+ < For each = { standardCollections ( ) } > { ( collection ) =>
350
+ < CollectionItem
351
+ collection = { collection }
352
+ isActive = { ( ) => collection . id === activeCollectionId ( ) }
353
+ onSelect = { ( c ) => setActiveCollectionId ( c . id ) }
354
+ />
355
+ } </ For >
356
+ </ div >
357
+ </ Show >
358
+
359
+ { /* Live Collections */ }
360
+ < Show when = { liveCollections ( ) . length > 0 } >
361
+ < div class = { styles ( ) . collectionGroup } >
362
+ < div class = { styles ( ) . collectionGroupHeader } >
363
+ < div > Live Collections ({ liveCollections ( ) . length } )</ div >
364
+ < div class = { styles ( ) . collectionGroupStats } >
365
+ < span > Items</ span >
366
+ < span > /</ span >
367
+ < span > GC</ span >
368
+ < span > /</ span >
369
+ < span > Status</ span >
370
+ </ div >
371
+ </ div >
372
+ < For each = { liveCollections ( ) } > { ( collection ) =>
373
+ < CollectionItem
374
+ collection = { collection }
375
+ isActive = { ( ) => collection . id === activeCollectionId ( ) }
376
+ onSelect = { ( c ) => setActiveCollectionId ( c . id ) }
377
+ />
378
+ } </ For >
379
+ </ div >
380
+ </ Show >
276
381
</ Show >
277
382
</ div >
278
383
</ div >
0 commit comments