astryx-dracula 0.2.1 → 0.3.0

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 (83) hide show
  1. package/BRAND.md +30 -18
  2. package/README.md +17 -12
  3. package/USAGE.md +29 -10
  4. package/astryx-dracula.js +19 -13
  5. package/astryx-dracula.variants.d.ts +15 -0
  6. package/package.json +59 -17
  7. package/shared/auth-chrome-config.ts +18 -0
  8. package/shared/auth-chrome.tsx +25 -0
  9. package/shared/auth-copy.ts +22 -0
  10. package/shared/chaptered-doc-config.ts +92 -0
  11. package/shared/chaptered-doc.tsx +332 -0
  12. package/shared/chart-hues.ts +27 -0
  13. package/shared/chart-labels.tsx +44 -0
  14. package/shared/chart-legend.tsx +100 -0
  15. package/shared/chart-panel-style.ts +13 -0
  16. package/shared/data-bar.tsx +160 -0
  17. package/shared/gallery-image.ts +20 -0
  18. package/shared/login-demo.ts +7 -0
  19. package/shared/metric-delta.tsx +36 -0
  20. package/shared/order-desk.tsx +273 -0
  21. package/shared/revenue-chart.tsx +174 -0
  22. package/shared/scene-castle.tsx +322 -0
  23. package/shared/scene-frame.tsx +88 -0
  24. package/shared/scene-hues.ts +49 -0
  25. package/shared/scene-tile.tsx +128 -0
  26. package/shared/settings-data.ts +125 -0
  27. package/shared/settings-rows.tsx +179 -0
  28. package/shared/sparkline.tsx +94 -0
  29. package/shared/sso-icons.tsx +30 -0
  30. package/templates/ai-chat-landing.tsx +66 -33
  31. package/templates/ai-chat.tsx +36 -19
  32. package/templates/blank.tsx +4 -3
  33. package/templates/centered-hero.tsx +14 -125
  34. package/templates/classic-gallery.tsx +20 -66
  35. package/templates/contact-form.tsx +59 -39
  36. package/templates/dashboard-portfolio.tsx +143 -226
  37. package/templates/dashboard.tsx +122 -122
  38. package/templates/detail-page.tsx +95 -91
  39. package/templates/documentation-design.template.mjs +1 -1
  40. package/templates/documentation-design.tsx +83 -96
  41. package/templates/documentation-technical.template.mjs +1 -1
  42. package/templates/documentation-technical.tsx +29 -19
  43. package/templates/documentation.template.mjs +1 -1
  44. package/templates/documentation.tsx +52 -74
  45. package/templates/editor.tsx +112 -84
  46. package/templates/file-explorer.tsx +131 -84
  47. package/templates/form-two-column.tsx +61 -125
  48. package/templates/gallery-hero.tsx +15 -171
  49. package/templates/ide.tsx +22 -20
  50. package/templates/incident-console.tsx +104 -70
  51. package/templates/kanban-board.tsx +99 -42
  52. package/templates/library.template.mjs +2 -2
  53. package/templates/library.tsx +37 -42
  54. package/templates/login-card.tsx +58 -87
  55. package/templates/login-split.tsx +88 -126
  56. package/templates/login-sso.tsx +105 -64
  57. package/templates/login.tsx +107 -53
  58. package/templates/messaging-shell.tsx +171 -144
  59. package/templates/mixed-gallery.tsx +29 -130
  60. package/templates/payment-form.tsx +368 -290
  61. package/templates/product-detail.tsx +39 -58
  62. package/templates/product-gallery.tsx +30 -41
  63. package/templates/product-tour.template.mjs +6 -0
  64. package/templates/product-tour.tsx +916 -0
  65. package/templates/settings-dialog.tsx +135 -282
  66. package/templates/settings-sidebar.tsx +178 -268
  67. package/templates/settings.tsx +41 -14
  68. package/templates/shell-nav.tsx +31 -24
  69. package/templates/shell-side-nav.tsx +29 -20
  70. package/templates/shell-top-nav.tsx +80 -44
  71. package/templates/side-gallery.tsx +38 -94
  72. package/templates/table-grouped.tsx +62 -34
  73. package/templates/table-page-chart.tsx +23 -302
  74. package/templates/table-page-heatmap-status.tsx +156 -92
  75. package/templates/table-page-shoe-store-heatmap.tsx +18 -293
  76. package/templates/table-page.tsx +23 -7
  77. package/templates/table.tsx +23 -8
  78. package/templates/tech-report.template.mjs +6 -0
  79. package/templates/tech-report.tsx +1486 -0
  80. package/templates/theme-showcase.template.mjs +1 -1
  81. package/templates/theme-showcase.tsx +299 -237
  82. package/theme.css +76 -14
  83. package/tokens.css +425 -30
@@ -1,7 +1,25 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > (LH > H[g2 a=center] > Hd"Status"[level=1] + IB*2 + B"Refresh") + (LC[p3] > V[g4] > (V[g3] > C[p3] + Tx"Incidents by day and hour"[t=supporting] + (H[g4] > (H[g2] > Tx[t=supporting])*4)) + (T > (TR > THC*8) + (TR > TC*8)*5))
3
+ // L > (LH[divider] > H[g2 a=center] > Hd"Status"[level=1] + IB"Filter"[variant=ghost] + IB"Export"[variant=ghost] + B.secondary"Refresh") + (LC[p3] > V[g4] > (V[g3] > C[p4] + Tx"Incidents by day and hour"[t=supporting] + (H[g4] > (H[g2] > Tx[t=supporting])*4)) + (T[hover] > (TR > THC"Incident" + THC"Product" + THC"Description" + THC"Started" + THC"Duration" + THC"On-call" + THC"Status" + THC"Date") + (TR > TC"INC-4008" + TC"Business Suite" + TC"Notification delivery delays" + TC"09:15" + TC"ongoing" + TC"Carlos Mendez" + (TC > Tk.red"Ongoing") + TC"2025-01-15")*5))
4
4
 
5
+ /**
6
+ * Status — the incident heatmap for the week plus the log behind it.
7
+ *
8
+ * Frame: page header (title + icon actions) | content column (heatmap, table).
9
+ *
10
+ * Container policy: the day/hour heatmap is a single Card widget; incidents
11
+ * render as dense rows in one edge-to-edge table. Every heat cell prints its
12
+ * count, so the green/orange/red ramp reinforces the numbers rather than
13
+ * carrying the reading on its own, and status is a Token instead of a badge.
14
+ *
15
+ * Responsive contract:
16
+ * no media queries — the heatmap is an SVG that scales to its column, so it
17
+ * reflows at any width. The table declares a ~910px floor across its eight
18
+ * columns and truncates cells (description to two lines), so narrower
19
+ * viewports scroll the table horizontally instead of widening the page.
20
+ */
21
+
22
+ import type {CSSProperties} from 'react';
5
23
  import {
6
24
  VStack,
7
25
  HStack,
@@ -15,12 +33,16 @@ import {Button} from '@astryxdesign/core/Button';
15
33
  import {IconButton} from '@astryxdesign/core/IconButton';
16
34
  import {Icon} from '@astryxdesign/core/Icon';
17
35
  import {Avatar} from '@astryxdesign/core/Avatar';
18
- import {Badge} from '@astryxdesign/core/Badge';
36
+ import {Token} from '@astryxdesign/core/Token';
19
37
  import {Card} from '@astryxdesign/core/Card';
20
38
  import {Link} from '@astryxdesign/core/Link';
21
39
  import {Table, proportional, pixel} from '@astryxdesign/core/Table';
22
40
  import type {TableColumn} from '@astryxdesign/core/Table';
23
41
  import {Filter, Download, RotateCw} from 'lucide-react';
42
+ import {CHART_HUES} from 'astryx-dracula/shared/chart-hues';
43
+ import {ChartLegend, type ChartLegendEntry} from 'astryx-dracula/shared/chart-legend';
44
+ import {CHART_PANEL_STYLE} from 'astryx-dracula/shared/chart-panel-style';
45
+ import {ChartLabel} from 'astryx-dracula/shared/chart-labels';
24
46
 
25
47
  // ============= ICONS (verified lucide-react exports) =============
26
48
  // Filter ← FunnelIcon, Download ← ArrowDownTrayIcon, RotateCw ← ArrowPathIcon.
@@ -58,7 +80,7 @@ const incidents: IncidentRow[] = [
58
80
  id: 'INC-4008',
59
81
  product: 'Business Suite',
60
82
  title:
61
- 'Notification delivery delays affecting mobile push and email channels — queue saturation under investigation',
83
+ 'Notification delivery delays affecting mobile push and email channels, queue saturation under investigation',
62
84
  severity: 'major',
63
85
  oncall: 'Carlos Mendez',
64
86
  status: 'ongoing',
@@ -73,7 +95,7 @@ const incidents: IncidentRow[] = [
73
95
  id: 'INC-4001',
74
96
  product: 'Ads Manager',
75
97
  title:
76
- 'Campaign creation timing out for ~12% of advertisers in EMEA region — root caused to elevated DB write latency',
98
+ 'Campaign creation timing out for ~12% of advertisers in EMEA region, root caused to elevated DB write latency',
77
99
  severity: 'critical',
78
100
  oncall: 'Sarah Chen',
79
101
  status: 'resolved',
@@ -160,7 +182,7 @@ const incidents: IncidentRow[] = [
160
182
  id: 'INC-4007',
161
183
  product: 'Messenger API',
162
184
  title:
163
- 'Webhook delivery failures for subscribed page events — retries succeeded but signaled false alerts',
185
+ 'Webhook delivery failures for subscribed page events, retries succeeded but signaled false alerts',
164
186
  severity: 'minor',
165
187
  oncall: 'Mei Lin',
166
188
  status: 'resolved',
@@ -176,7 +198,7 @@ const incidents: IncidentRow[] = [
176
198
  id: 'INC-4009',
177
199
  product: 'Pages',
178
200
  title:
179
- 'Image upload timeouts for posts larger than 4MB — auto-recovered after CDN region failover',
201
+ 'Image upload timeouts for posts larger than 4MB, auto-recovered after CDN region failover',
180
202
  severity: 'minor',
181
203
  oncall: 'Fatima Al-Rashid',
182
204
  status: 'resolved',
@@ -190,7 +212,7 @@ const incidents: IncidentRow[] = [
190
212
  id: 'INC-4010',
191
213
  product: 'Graph API',
192
214
  title:
193
- 'Intermittent 503 errors on a single load balancer in us-east-1 — auto-healed by health check rotation',
215
+ 'Intermittent 503 errors on a single load balancer in us-east-1, auto-healed by health check rotation',
194
216
  severity: 'minor',
195
217
  oncall: 'Lucas Andersson',
196
218
  status: 'resolved',
@@ -246,7 +268,7 @@ const incidents: IncidentRow[] = [
246
268
  id: 'INC-4014',
247
269
  product: 'Business Suite',
248
270
  title:
249
- 'Slow inbox loading times (>3s p95) for accounts with large message histories — index rebuild in progress',
271
+ 'Slow inbox loading times (>3s p95) for accounts with large message histories, index rebuild in progress',
250
272
  severity: 'minor',
251
273
  oncall: 'Andre Santos',
252
274
  status: 'resolved',
@@ -291,19 +313,28 @@ function buildHeatmapData(data: IncidentRow[]) {
291
313
  );
292
314
  }
293
315
 
294
- const statusVariant: Record<string, 'error' | 'warning' | 'info' | 'success'> =
295
- {
296
- ongoing: 'error',
297
- identified: 'warning',
298
- monitoring: 'info',
299
- resolved: 'success',
300
- };
316
+ const STATUS_TOKEN_COLOR: Record<
317
+ IncidentRow['status'],
318
+ 'red' | 'yellow' | 'cyan' | 'green'
319
+ > = {
320
+ ongoing: 'red',
321
+ identified: 'yellow',
322
+ monitoring: 'cyan',
323
+ resolved: 'green',
324
+ };
325
+
326
+ const STATUS_LABEL: Record<IncidentRow['status'], string> = {
327
+ ongoing: 'Ongoing',
328
+ identified: 'Identified',
329
+ monitoring: 'Monitoring',
330
+ resolved: 'Resolved',
331
+ };
301
332
 
302
333
  const columns: TableColumn<IncidentRow>[] = [
303
334
  {
304
335
  key: 'id',
305
336
  header: 'Incident',
306
- width: pixel(110),
337
+ width: pixel(96),
307
338
  renderCell: (item: IncidentRow) => (
308
339
  <Link href="#/templates/table-page-heatmap-status" isStandalone>
309
340
  {item.id}
@@ -313,21 +344,29 @@ const columns: TableColumn<IncidentRow>[] = [
313
344
  {
314
345
  key: 'product',
315
346
  header: 'Product',
316
- width: proportional(2),
317
- renderCell: (item: IncidentRow) => <Text type="body">{item.product}</Text>,
347
+ width: proportional(2, {minWidth: 120}),
348
+ renderCell: (item: IncidentRow) => (
349
+ <Text type="body" maxLines={1}>
350
+ {item.product}
351
+ </Text>
352
+ ),
318
353
  },
319
354
  {
320
355
  key: 'title',
321
356
  header: 'Description',
322
- width: proportional(5),
323
- renderCell: (item: IncidentRow) => <Text type="body">{item.title}</Text>,
357
+ width: proportional(5, {minWidth: 160}),
358
+ renderCell: (item: IncidentRow) => (
359
+ <Text type="body" maxLines={2}>
360
+ {item.title}
361
+ </Text>
362
+ ),
324
363
  },
325
364
  {
326
365
  key: 'startTime',
327
366
  header: 'Started',
328
- width: pixel(96),
367
+ width: pixel(88),
329
368
  renderCell: (item: IncidentRow) => (
330
- <Text type="body" hasTabularNumbers>
369
+ <Text type="body" hasTabularNumbers maxLines={1}>
331
370
  {item.startTime}
332
371
  </Text>
333
372
  ),
@@ -335,9 +374,9 @@ const columns: TableColumn<IncidentRow>[] = [
335
374
  {
336
375
  key: 'duration',
337
376
  header: 'Duration',
338
- width: pixel(100),
377
+ width: pixel(96),
339
378
  renderCell: (item: IncidentRow) => (
340
- <Text type="body" hasTabularNumbers>
379
+ <Text type="body" hasTabularNumbers maxLines={1}>
341
380
  {item.duration}
342
381
  </Text>
343
382
  ),
@@ -345,11 +384,15 @@ const columns: TableColumn<IncidentRow>[] = [
345
384
  {
346
385
  key: 'oncall',
347
386
  header: 'On-call',
348
- width: proportional(2),
387
+ width: proportional(2, {minWidth: 120}),
349
388
  renderCell: (item: IncidentRow) => (
350
389
  <HStack gap={2} vAlign="center">
351
390
  <Avatar name={item.oncall} size="sm" />
352
- <Text type="body">{item.oncall}</Text>
391
+ <StackItem size="fill">
392
+ <Text type="body" maxLines={1}>
393
+ {item.oncall}
394
+ </Text>
395
+ </StackItem>
353
396
  </HStack>
354
397
  ),
355
398
  },
@@ -358,18 +401,19 @@ const columns: TableColumn<IncidentRow>[] = [
358
401
  header: 'Status',
359
402
  width: pixel(120),
360
403
  renderCell: (item: IncidentRow) => (
361
- <Badge
362
- label={item.status.charAt(0).toUpperCase() + item.status.slice(1)}
363
- variant={statusVariant[item.status]}
404
+ <Token
405
+ size="sm"
406
+ color={STATUS_TOKEN_COLOR[item.status]}
407
+ label={STATUS_LABEL[item.status]}
364
408
  />
365
409
  ),
366
410
  },
367
411
  {
368
412
  key: 'date',
369
413
  header: 'Date',
370
- width: pixel(110),
414
+ width: pixel(112),
371
415
  renderCell: (item: IncidentRow) => (
372
- <Text type="body" hasTabularNumbers>
416
+ <Text type="body" hasTabularNumbers maxLines={1}>
373
417
  {item.date}
374
418
  </Text>
375
419
  ),
@@ -380,24 +424,32 @@ const columns: TableColumn<IncidentRow>[] = [
380
424
 
381
425
  // Incident-count ramp: quiet days rest on the Dracula surface, busier cells
382
426
  // climb green → orange → red.
427
+ // Delegates to CHART_HUES, the sanctioned categorical source. Writing the
428
+ // literal here is what let a --dracula-* primitive reach a chart mark: the
429
+ // dictionary itself held primitives, so "use the module" was not available.
383
430
  function heatFill(count: number): string {
384
431
  if (count >= 3) {
385
- return 'var(--dracula-red)';
432
+ return CHART_HUES.red;
386
433
  }
387
434
  if (count === 2) {
388
- return 'var(--dracula-orange)';
435
+ return CHART_HUES.orange;
389
436
  }
390
437
  if (count === 1) {
391
- return 'var(--dracula-green)';
438
+ return CHART_HUES.green;
392
439
  }
393
- return 'var(--dracula-bg-light)';
440
+ // Zero is a magnitude, not a severity, so it wears the surface tier rather
441
+ // than a data hue -- which is why the legend type needs a background arm.
442
+ return 'var(--color-background-card)';
394
443
  }
395
444
 
396
- const HEAT_LEGEND = [
397
- {label: '0', fill: 'var(--dracula-bg-light)'},
398
- {label: '1', fill: 'var(--dracula-green)'},
399
- {label: '2', fill: 'var(--dracula-orange)'},
400
- {label: '3+', fill: 'var(--dracula-red)'},
445
+ // The zero swatch and its cell MUST move together: the legend is how a reader
446
+ // learns what a colour means, so a legend that disagrees with the grid teaches
447
+ // them the wrong mapping.
448
+ const HEAT_LEGEND: readonly ChartLegendEntry[] = [
449
+ {label: '0', color: 'var(--color-background-card)'},
450
+ {label: '1', color: CHART_HUES.green},
451
+ {label: '2', color: CHART_HUES.orange},
452
+ {label: '3+', color: CHART_HUES.red},
401
453
  ];
402
454
 
403
455
  function OutageHeatmap() {
@@ -413,40 +465,28 @@ function OutageHeatmap() {
413
465
  heatmapData.find(d => d.day === day && d.hour === hour)?.incidents ?? 0;
414
466
  return (
415
467
  <VStack gap={3}>
416
- <Card
417
- padding={3}
418
- style={{
419
- backgroundColor: 'var(--color-background)',
420
- border: 'var(--border-width) solid var(--color-separator)',
421
- }}>
468
+ <Card padding={4} style={CHART_PANEL_STYLE}>
422
469
  <svg
423
470
  viewBox={`0 0 ${W} ${H}`}
424
471
  width="100%"
425
472
  role="img"
426
473
  aria-label="Incidents by day and hour, January 12 to 18">
427
474
  {DAYS.map((day, di) => (
428
- <text
475
+ <ChartLabel
429
476
  key={day}
430
477
  x={labelW + di * (cellW + gap) + cellW / 2}
431
- y={14}
432
- textAnchor="middle"
433
- fontSize={9}
434
- fill="var(--color-text-paragraph)"
435
- fontFamily="var(--font-family-mono)">
478
+ y={14}>
436
479
  {day}
437
- </text>
480
+ </ChartLabel>
438
481
  ))}
439
482
  {HOURS.map((hour, hi) => (
440
483
  <g key={hour}>
441
- <text
484
+ <ChartLabel
442
485
  x={labelW - 6}
443
486
  y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
444
- textAnchor="end"
445
- fontSize={9}
446
- fill="var(--color-text-paragraph)"
447
- fontFamily="var(--font-family-mono)">
487
+ textAnchor="end">
448
488
  {hour}
449
- </text>
489
+ </ChartLabel>
450
490
  {DAYS.map((day, di) => {
451
491
  const count = countFor(day, hour);
452
492
  return (
@@ -459,17 +499,28 @@ function OutageHeatmap() {
459
499
  rx={4}
460
500
  fill={heatFill(count)}
461
501
  />
462
- {count > 0 && (
463
- <text
464
- x={labelW + di * (cellW + gap) + cellW / 2}
465
- y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
466
- textAnchor="middle"
467
- fontSize={9}
468
- fill="var(--dracula-bg-dark)"
469
- fontFamily="var(--font-family-mono)">
470
- {count}
471
- </text>
472
- )}
502
+ {/* Every cell prints its count, empty ones included, so
503
+ the ramp reinforces the number instead of carrying the
504
+ reading on its own. */}
505
+ <ChartLabel
506
+ x={labelW + di * (cellW + gap) + cellW / 2}
507
+ y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
508
+ // On-fill ink follows the cell it sits on, so it takes
509
+ // the --color-on-* arm for that status rather than one
510
+ // fixed token. --color-on-dark is the wrong target here:
511
+ // it is #F8F8F2, near-white, which would fail on the
512
+ // saturated green/orange/red fills.
513
+ fill={
514
+ count >= 3
515
+ ? 'var(--color-on-error)'
516
+ : count === 2
517
+ ? 'var(--color-on-warning)'
518
+ : count === 1
519
+ ? 'var(--color-on-success)'
520
+ : undefined
521
+ }>
522
+ {count}
523
+ </ChartLabel>
473
524
  </g>
474
525
  );
475
526
  })}
@@ -477,40 +528,48 @@ function OutageHeatmap() {
477
528
  ))}
478
529
  </svg>
479
530
  </Card>
480
- <Text type="supporting" color="secondary">
481
- Incidents by day and hour · Jan 12–18
482
- </Text>
483
- <HStack gap={4} vAlign="center">
484
- {HEAT_LEGEND.map(entry => (
485
- <HStack key={entry.label} gap={2} vAlign="center">
486
- <svg
487
- width={12}
488
- height={12}
489
- role="img"
490
- aria-label={`${entry.label} incidents`}>
491
- <rect width={12} height={12} rx={4} fill={entry.fill} />
492
- </svg>
493
- <Text type="supporting" color="secondary" hasTabularNumbers>
494
- {entry.label}
495
- </Text>
496
- </HStack>
497
- ))}
498
- </HStack>
531
+ {/* Legend rows go through shared/chart-legend.tsx. The ramp itself stays
532
+ local: it encodes SEVERITY (0/1/2/3+ against green/orange/red), which
533
+ is a status vocabulary, not a magnitude. A sequential magnitude ramp
534
+ would invert this one — on a dark page a high count reads lighter,
535
+ which is the opposite of "more is worse" here. The --color-data-<family>-
536
+ 1..5 steps remain in tokens.css for a consumer that genuinely needs
537
+ one; nothing in this kit does, which is why the module that wrapped
538
+ them is gone. */}
539
+ <ChartLegend
540
+ entries={HEAT_LEGEND}
541
+ gap={2}
542
+ swatchSize={12}
543
+ caption={
544
+ <Text type="supporting" color="secondary">
545
+ Incidents by day and hour · Jan 12–18
546
+ </Text>
547
+ }
548
+ />
499
549
  </VStack>
500
550
  );
501
551
  }
502
552
 
503
553
  // ============= PAGE =============
504
554
 
555
+ // A definite ancestor for `Layout height="fill"`. 100dvh, not minHeight: '100%':
556
+ // a percentage min-height against an indefinite containing block computes to 0
557
+ // (CSS 2.1 10.5), so the soft failure would survive and the document would keep
558
+ // the scroll. LayoutContent is overflow:auto, so the definite height lands as a
559
+ // content-pane scroller rather than a clip. Same shape as dashboard.tsx and
560
+ // editor.tsx:302.
561
+ const pageStyle: CSSProperties = {height: '100dvh'};
562
+
505
563
  export default function HeatmapTable() {
506
564
  return (
507
565
  <Layout
566
+ style={pageStyle}
508
567
  height="fill"
509
568
  header={
510
- <LayoutHeader hasDivider>
569
+ <LayoutHeader hasDivider padding={6}>
511
570
  <HStack gap={2} vAlign="center">
512
571
  <StackItem size="fill">
513
- <Heading level={1}>Status</Heading>
572
+ <Heading level={1} type="display-2">Status</Heading>
514
573
  </StackItem>
515
574
  <IconButton
516
575
  label="Filter"
@@ -524,7 +583,11 @@ export default function HeatmapTable() {
524
583
  variant="ghost"
525
584
  tooltip="Export"
526
585
  />
527
- <Button label="Refresh" icon={<Icon icon={RotateCw} size="sm" />} />
586
+ <Button
587
+ label="Refresh"
588
+ variant="secondary"
589
+ icon={<Icon icon={RotateCw} size="sm" />}
590
+ />
528
591
  </HStack>
529
592
  </LayoutHeader>
530
593
  }
@@ -539,6 +602,7 @@ export default function HeatmapTable() {
539
602
  idKey="id"
540
603
  density="balanced"
541
604
  dividers="rows"
605
+ textOverflow="truncate"
542
606
  hasHover
543
607
  />
544
608
  </VStack>