astryx-dracula 0.5.0 → 0.6.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 (129) hide show
  1. package/BRAND.md +3 -3
  2. package/README.md +7 -4
  3. package/USAGE.md +49 -49
  4. package/astryx-dracula.d.ts +2 -2
  5. package/astryx-dracula.js +25 -9
  6. package/astryx-dracula.variants.d.ts +2 -2
  7. package/astryx.integration.mjs +1 -1
  8. package/icons.mjs +21 -17
  9. package/icons.tsx +69 -62
  10. package/package.json +125 -120
  11. package/shared/auth-chrome-config.ts +6 -6
  12. package/shared/auth-chrome.tsx +143 -144
  13. package/shared/auth-copy.ts +14 -14
  14. package/shared/chaptered-doc-config.ts +48 -51
  15. package/shared/chaptered-doc.tsx +267 -289
  16. package/shared/chart-hues.ts +25 -10
  17. package/shared/chart-labels.tsx +31 -30
  18. package/shared/chart-legend.tsx +55 -56
  19. package/shared/chart-panel-style.ts +3 -3
  20. package/shared/data-bar.tsx +97 -102
  21. package/shared/format-money.ts +1 -1
  22. package/shared/gallery-image.ts +6 -6
  23. package/shared/login-demo.ts +1 -1
  24. package/shared/metric-card.tsx +101 -105
  25. package/shared/metric-delta.tsx +28 -27
  26. package/shared/order-desk.tsx +205 -216
  27. package/shared/revenue-chart.tsx +145 -165
  28. package/shared/scene-castle.tsx +206 -291
  29. package/shared/scene-frame.tsx +33 -32
  30. package/shared/scene-hues.ts +17 -17
  31. package/shared/scene-tile.tsx +88 -92
  32. package/shared/settings-data.ts +119 -125
  33. package/shared/settings-rows.tsx +122 -159
  34. package/shared/sparkline.tsx +75 -88
  35. package/shared/sso-icons.tsx +10 -22
  36. package/shared/stars.tsx +16 -25
  37. package/templates/ai-chat-landing.template.mjs +5 -4
  38. package/templates/ai-chat-landing.tsx +473 -460
  39. package/templates/ai-chat.template.mjs +5 -4
  40. package/templates/ai-chat.tsx +543 -612
  41. package/templates/blank.template.mjs +5 -4
  42. package/templates/blank.tsx +18 -14
  43. package/templates/centered-hero.template.mjs +5 -4
  44. package/templates/centered-hero.tsx +55 -68
  45. package/templates/classic-gallery.template.mjs +5 -4
  46. package/templates/classic-gallery.tsx +97 -104
  47. package/templates/contact-form.template.mjs +5 -4
  48. package/templates/contact-form.tsx +247 -282
  49. package/templates/dashboard-portfolio.template.mjs +5 -4
  50. package/templates/dashboard-portfolio.tsx +717 -548
  51. package/templates/dashboard.template.mjs +5 -4
  52. package/templates/dashboard.tsx +596 -567
  53. package/templates/detail-page.template.mjs +5 -4
  54. package/templates/detail-page.tsx +546 -603
  55. package/templates/documentation-design.template.mjs +5 -4
  56. package/templates/documentation-design.tsx +231 -243
  57. package/templates/documentation-technical.template.mjs +5 -4
  58. package/templates/documentation-technical.tsx +236 -239
  59. package/templates/documentation.template.mjs +5 -4
  60. package/templates/documentation.tsx +267 -279
  61. package/templates/editor.template.mjs +5 -4
  62. package/templates/editor.tsx +844 -877
  63. package/templates/file-explorer.template.mjs +5 -4
  64. package/templates/file-explorer.tsx +566 -590
  65. package/templates/form-two-column.template.mjs +5 -4
  66. package/templates/form-two-column.tsx +195 -210
  67. package/templates/gallery-hero.template.mjs +5 -4
  68. package/templates/gallery-hero.tsx +93 -114
  69. package/templates/ide.template.mjs +5 -4
  70. package/templates/ide.tsx +407 -453
  71. package/templates/incident-console.template.mjs +5 -4
  72. package/templates/incident-console.tsx +593 -575
  73. package/templates/kanban-board.template.mjs +5 -4
  74. package/templates/kanban-board.tsx +665 -683
  75. package/templates/library.template.mjs +5 -4
  76. package/templates/library.tsx +409 -430
  77. package/templates/login-card.template.mjs +5 -4
  78. package/templates/login-card.tsx +63 -72
  79. package/templates/login-split.template.mjs +5 -4
  80. package/templates/login-split.tsx +192 -193
  81. package/templates/login-sso.template.mjs +5 -4
  82. package/templates/login-sso.tsx +257 -258
  83. package/templates/login.template.mjs +5 -4
  84. package/templates/login.tsx +53 -49
  85. package/templates/messaging-shell.template.mjs +5 -4
  86. package/templates/messaging-shell.tsx +556 -574
  87. package/templates/mixed-gallery.template.mjs +5 -4
  88. package/templates/mixed-gallery.tsx +60 -58
  89. package/templates/payment-form.template.mjs +5 -4
  90. package/templates/payment-form.tsx +882 -991
  91. package/templates/product-detail.template.mjs +5 -4
  92. package/templates/product-detail.tsx +261 -272
  93. package/templates/product-gallery.template.mjs +5 -4
  94. package/templates/product-gallery.tsx +126 -137
  95. package/templates/product-tour.template.mjs +5 -4
  96. package/templates/product-tour.tsx +741 -773
  97. package/templates/settings-dialog.template.mjs +5 -4
  98. package/templates/settings-dialog.tsx +688 -717
  99. package/templates/settings-sidebar.template.mjs +5 -4
  100. package/templates/settings-sidebar.tsx +762 -807
  101. package/templates/settings.template.mjs +5 -4
  102. package/templates/settings.tsx +289 -304
  103. package/templates/shell-nav.template.mjs +5 -4
  104. package/templates/shell-nav.tsx +212 -219
  105. package/templates/shell-side-nav.template.mjs +5 -4
  106. package/templates/shell-side-nav.tsx +135 -157
  107. package/templates/shell-top-nav.template.mjs +5 -4
  108. package/templates/shell-top-nav.tsx +182 -203
  109. package/templates/side-gallery.template.mjs +5 -4
  110. package/templates/side-gallery.tsx +85 -90
  111. package/templates/table-grouped.template.mjs +5 -4
  112. package/templates/table-grouped.tsx +1000 -1053
  113. package/templates/table-page-chart.template.mjs +5 -4
  114. package/templates/table-page-chart.tsx +400 -398
  115. package/templates/table-page-heatmap-status.template.mjs +5 -4
  116. package/templates/table-page-heatmap-status.tsx +483 -512
  117. package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
  118. package/templates/table-page-shoe-store-heatmap.tsx +748 -746
  119. package/templates/table-page.template.mjs +5 -4
  120. package/templates/table-page.tsx +396 -405
  121. package/templates/table.template.mjs +5 -4
  122. package/templates/table.tsx +92 -93
  123. package/templates/tech-report.template.mjs +5 -4
  124. package/templates/tech-report.tsx +1316 -1304
  125. package/templates/theme-showcase.template.mjs +5 -4
  126. package/templates/theme-showcase.tsx +871 -951
  127. package/theme.css +35 -77
  128. package/theme.css.d.ts +9 -0
  129. package/tokens.css +373 -380
@@ -1,141 +1,143 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] + Tx"1 year"[t=body]) + (G[c={min:280} g=4] > (C > V[g=2] > Tx"Total value"[t=supporting] + (H[g=2] > Tx"$294,200"[t=display-3] + Tx"+14.8%"[t=body]))*4) + (G[c={min:280} g=4] > (C > V[g=4] > (H[j=between] > Hd"Vault value"[level=2] + Lk"View details") + (C > V[g=3] > Tx"Weekly closes"[t=supporting])) + (V[g=4] > (H[j=between] > Hd"Top holdings"[level=2] + Lk"View all") + UL)) + D + (H[j=between a=start] > (V[g=1] > Hd"Market at midnight"[level=2] + Tx"Past 24 hours under moonlight"[t=body]) + B"View more") + (G[c={min:280} g=4] > (C > V[g=3] > Hd"Index"[level=3] + Tx"$5,200"[t=body])*8) + (C > V[g=4] > Hd"Trending stocks"[level=3] + T)
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] + Tx"1 year"[t=body]) + (G[c={min:280} g=4] > (C > V[g=2] > Tx"Total value"[t=supporting] + (H[g=2 a=center] > Tx"$294,200"[t=display-3] + (H[g=1 a=center] > Ic + Tx"+14.8%"[t=body])) + Tx"Weekly closes · trailing 12 months"[t=supporting])*4) + (G[c={min:280} g=4] > (C > V[g=4] > (H[j=between a=center] > Hd"Vault value"[level=2] + Lk"View details") + (V[g=3] > C[p=4] + Tx"Weekly closes · trailing 12 months"[t=supporting])) + (V[g=4] > (H[j=between a=center] > Hd"Top holdings"[level=2] + Lk"View all") + (UL[density=spacious] > LI*5))) + D + (H[j=between a=start] > (V[g=1] > Hd"Market at midnight"[level=2] + Tx"Past 24 hours under moonlight"[t=body]) + B.secondary"View more") + (G[c={min:280} g=4] > (C > V[g=4] > (V[g=0] > Hd"Dow Jones"[level=3] + Tx"DJI"[t=supporting]) + Spl + (H[g=3 a=center] > Tx"43,821.67"[t=display-3] + (H[g=1 a=center] > Ic + Tx"+0.42%"[t=body])))*8) + (C > V[g=6] > (H[j=between a=center] > Hd"Trending stocks"[level=3]) + T[hover density=compact dv=rows])
4
6
 
7
+ import { Avatar } from "@astryxdesign/core/Avatar";
8
+ import { Button } from "@astryxdesign/core/Button";
9
+ import { Card } from "@astryxdesign/core/Card";
10
+ import { Divider } from "@astryxdesign/core/Divider";
11
+ import { Grid } from "@astryxdesign/core/Grid";
5
12
  /**
6
13
  * Portfolio Dashboard — the night vault: KPI tiles, a weekly value chart, the holdings list, and the market board. (Frame/responsive/container: see XLE header above.)
7
14
  */
8
- import {VStack, HStack, Layout, LayoutContent} from '@astryxdesign/core/Layout';
9
- import {Text, Heading} from '@astryxdesign/core/Text';
10
- import {Card} from '@astryxdesign/core/Card';
11
- import {Grid} from '@astryxdesign/core/Grid';
12
- import {Link} from '@astryxdesign/core/Link';
13
- import {Avatar} from '@astryxdesign/core/Avatar';
14
- import {List, ListItem} from '@astryxdesign/core/List';
15
- import {Button} from '@astryxdesign/core/Button';
16
- import {proportional, pixel} from '@astryxdesign/core/Table';
17
- import type {TableColumn, PixelWidth} from '@astryxdesign/core/Table';
18
- import {MetricCard, TableCard} from 'astryx-dracula/shared/metric-card';
19
- import {Divider} from '@astryxdesign/core/Divider';
20
- import {MetricDelta} from 'astryx-dracula/shared/metric-delta';
21
- import {Sparkline, type SparkPoint} from 'astryx-dracula/shared/sparkline';
22
- import {CHART_PANEL_STYLE} from 'astryx-dracula/shared/chart-panel-style';
23
- import {ChartLabel} from 'astryx-dracula/shared/chart-labels';
15
+ import { HStack, Layout, LayoutContent, VStack } from "@astryxdesign/core/Layout";
16
+ import { Link } from "@astryxdesign/core/Link";
17
+ import { List, ListItem } from "@astryxdesign/core/List";
18
+ import type { PixelWidth, TableColumn } from "@astryxdesign/core/Table";
19
+ import { pixel, proportional } from "@astryxdesign/core/Table";
20
+ import { Heading, Text } from "@astryxdesign/core/Text";
21
+ import { ChartLabel } from "astryx-dracula/shared/chart-labels";
22
+ import { CHART_PANEL_STYLE } from "astryx-dracula/shared/chart-panel-style";
23
+ import { MetricCard, TableCard } from "astryx-dracula/shared/metric-card";
24
+ import { MetricDelta } from "astryx-dracula/shared/metric-delta";
25
+ import { Sparkline, type SparkPoint } from "astryx-dracula/shared/sparkline";
24
26
 
25
27
  // ============= DATA =============
26
28
 
27
29
  // Portfolio value over ~12 months (Oct 2024 → Oct 2025), one data point per day.
28
30
  // Realistic fluctuations: dips in Feb–Mar, recovery in summer, climb into fall.
29
31
  const portfolioData = (() => {
30
- const anchors: Array<[number, number]> = [
31
- [0, 230000],
32
- [14, 238000],
33
- [28, 245000],
34
- [42, 250000],
35
- [56, 245000],
36
- [70, 258000],
37
- [84, 252000],
38
- [98, 260000],
39
- [112, 255000],
40
- [126, 245000],
41
- [140, 222000],
42
- [154, 218000],
43
- [168, 225000],
44
- [182, 232000],
45
- [196, 225000],
46
- [210, 235000],
47
- [224, 240000],
48
- [238, 245000],
49
- [252, 235000],
50
- [266, 248000],
51
- [280, 255000],
52
- [294, 260000],
53
- [308, 268000],
54
- [322, 275000],
55
- [336, 278000],
56
- [350, 285000],
57
- [364, 288000],
58
- [378, 290000],
59
- [392, 292000],
60
- [406, 294200],
61
- ];
62
- const totalDays = anchors[anchors.length - 1][0];
63
- const monthPerDay = 12 / totalDays;
64
- const out: Array<{month: number; label: string; value: number}> = [];
65
- let ai = 0;
66
- for (let day = 0; day <= totalDays; day++) {
67
- while (ai < anchors.length - 2 && day >= anchors[ai + 1][0]) {
68
- ai++;
69
- }
70
- const [d0, v0] = anchors[ai];
71
- const [d1, v1] = anchors[ai + 1];
72
- const t = (day - d0) / (d1 - d0);
73
- const base = v0 + (v1 - v0) * t;
74
- const seed = Math.sin(day * 12.9898 + 78.233) * 43758.5453;
75
- const noise = (seed - Math.floor(seed) - 0.5) * 3600;
76
- out.push({
77
- month: day * monthPerDay,
78
- label: `Day ${day + 1}`,
79
- value: Math.round(base + noise),
80
- });
81
- }
82
- return out;
32
+ const anchors: Array<[number, number]> = [
33
+ [0, 230000],
34
+ [14, 238000],
35
+ [28, 245000],
36
+ [42, 250000],
37
+ [56, 245000],
38
+ [70, 258000],
39
+ [84, 252000],
40
+ [98, 260000],
41
+ [112, 255000],
42
+ [126, 245000],
43
+ [140, 222000],
44
+ [154, 218000],
45
+ [168, 225000],
46
+ [182, 232000],
47
+ [196, 225000],
48
+ [210, 235000],
49
+ [224, 240000],
50
+ [238, 245000],
51
+ [252, 235000],
52
+ [266, 248000],
53
+ [280, 255000],
54
+ [294, 260000],
55
+ [308, 268000],
56
+ [322, 275000],
57
+ [336, 278000],
58
+ [350, 285000],
59
+ [364, 288000],
60
+ [378, 290000],
61
+ [392, 292000],
62
+ [406, 294200],
63
+ ];
64
+ const totalDays = anchors[anchors.length - 1][0];
65
+ const monthPerDay = 12 / totalDays;
66
+ const out: Array<{ month: number; label: string; value: number }> = [];
67
+ let ai = 0;
68
+ for (let day = 0; day <= totalDays; day++) {
69
+ while (ai < anchors.length - 2 && day >= anchors[ai + 1][0]) {
70
+ ai++;
71
+ }
72
+ const [d0, v0] = anchors[ai];
73
+ const [d1, v1] = anchors[ai + 1];
74
+ const t = (day - d0) / (d1 - d0);
75
+ const base = v0 + (v1 - v0) * t;
76
+ const seed = Math.sin(day * 12.9898 + 78.233) * 43758.5453;
77
+ const noise = (seed - Math.floor(seed) - 0.5) * 3600;
78
+ out.push({
79
+ month: day * monthPerDay,
80
+ label: `Day ${day + 1}`,
81
+ value: Math.round(base + noise),
82
+ });
83
+ }
84
+ return out;
83
85
  })();
84
86
 
85
87
  const xAxisTicks = [0, 3, 6, 9, 12];
86
88
  const xAxisLabels: Record<number, string> = {
87
- 0: 'Oct',
88
- 3: 'Jan',
89
- 6: 'Apr',
90
- 9: 'Jul',
91
- 12: 'Oct',
89
+ 0: "Oct",
90
+ 3: "Jan",
91
+ 6: "Apr",
92
+ 9: "Jul",
93
+ 12: "Oct",
92
94
  };
93
95
 
94
96
  // KPI summary metrics
95
97
  const metrics = [
96
- {
97
- value: '$294,200',
98
- change: '+14.8%',
99
- label: 'Total value',
100
- caption: 'Weekly closes · trailing 12 months',
101
- },
102
- {
103
- value: '14.8%',
104
- change: '+2.1%',
105
- label: 'Annual return',
106
- caption: 'Trailing 12 months under moonlight',
107
- },
108
- {
109
- value: '2.8%',
110
- change: '$2,060/qtr',
111
- label: 'Dividend yield',
112
- caption: 'Paid quarterly under moonlight',
113
- },
114
- {
115
- value: '23',
116
- change: '+4 YTD',
117
- label: 'Total asset holdings',
118
- caption: 'Across the night vault',
119
- },
98
+ {
99
+ value: "$294,200",
100
+ change: "+14.8%",
101
+ label: "Total value",
102
+ caption: "Weekly closes · trailing 12 months",
103
+ },
104
+ {
105
+ value: "14.8%",
106
+ change: "+2.1%",
107
+ label: "Annual return",
108
+ caption: "Trailing 12 months under moonlight",
109
+ },
110
+ {
111
+ value: "2.8%",
112
+ change: "$2,060/qtr",
113
+ label: "Dividend yield",
114
+ caption: "Paid quarterly under moonlight",
115
+ },
116
+ {
117
+ value: "23",
118
+ change: "+4 YTD",
119
+ label: "Total asset holdings",
120
+ caption: "Across the night vault",
121
+ },
120
122
  ];
121
123
 
122
124
  // Top holdings
123
125
  const topAssets = [
124
- {ticker: 'AAPL', name: 'Apple Inc.', value: '$87,200', change: '+18.4%'},
125
- {ticker: 'MSFT', name: 'Microsoft Corp.', value: '$72,500', change: '+14.7%'},
126
- {ticker: 'NVDA', name: 'NVIDIA Corp.', value: '$63,800', change: '+31.2%'},
127
- {
128
- ticker: 'VTI',
129
- name: 'Vanguard Total Stock',
130
- value: '$58,400',
131
- change: '+11.3%',
132
- },
133
- {
134
- ticker: 'BND',
135
- name: 'Vanguard Total Bond',
136
- value: '$45,600',
137
- change: '+4.2%',
138
- },
126
+ { ticker: "AAPL", name: "Apple Inc.", value: "$87,200", change: "+18.4%" },
127
+ { ticker: "MSFT", name: "Microsoft Corp.", value: "$72,500", change: "+14.7%" },
128
+ { ticker: "NVDA", name: "NVIDIA Corp.", value: "$63,800", change: "+31.2%" },
129
+ {
130
+ ticker: "VTI",
131
+ name: "Vanguard Total Stock",
132
+ value: "$58,400",
133
+ change: "+11.3%",
134
+ },
135
+ {
136
+ ticker: "BND",
137
+ name: "Vanguard Total Bond",
138
+ value: "$45,600",
139
+ change: "+4.2%",
140
+ },
139
141
  ];
140
142
 
141
143
  // 96 points per series = one tick every 15 minutes across a 24h window.
@@ -145,197 +147,373 @@ const topAssets = [
145
147
  // sample carries the slot it was generated for, so the bars key off data
146
148
  // rather than off the array position.
147
149
  function toSparkSeries(seed: number, values: number[]): SparkPoint[] {
148
- return values.map((value, i) => ({id: `${seed}-${i}`, value}));
150
+ return values.map((value, i) => ({ id: `${seed}-${i}`, value }));
149
151
  }
150
152
 
151
153
  // Market index cards — 24h sparkline data (every 15min, 96 points)
152
154
  const marketIndices = [
153
- {
154
- name: 'Dow Jones',
155
- ticker: 'DJI',
156
- price: '43,821.67',
157
- change: '+0.42%',
158
- positive: true,
159
- // prettier-ignore
160
- spark: toSparkSeries(1337, [77.87, 75.96, 76.35, 77.99, 78.95, 77.57, 78.14, 75.12, 74.24, 74.03, 72.88, 71.93, 74.65, 71.43, 69.56, 74.19, 72.66, 72.02, 76.09, 74.07, 75.82, 78.14, 77.91, 80.15, 79.18, 76.96, 76.57, 81.07, 83.59, 83.21, 81.58, 83.82, 84.82, 88.56, 87.03, 89.64, 87.35, 89.63, 90.67, 87.45, 87.4, 86.09, 87.85, 82.63, 81.81, 84.78, 82.26, 83.25, 80.04, 80.63, 83.56, 84.28, 83.96, 84.49, 80.85, 79.3, 80.45, 84.58, 81.83, 83.45, 87.92, 90.46, 89.1, 88.1, 90.6, 87.26, 84.82, 84.27, 86.7, 82.89, 84.38, 89.24, 82.39, 82.18, 82.24, 81.38, 82.38, 78.55, 76.91, 75.31, 78.55, 76.46, 73.89, 76.36, 77.01, 79.15, 78.98, 79.79, 78.68, 77.35, 76.01, 84.41, 82.31, 81.97, 80.42, 80.29]),
161
- },
162
- {
163
- name: 'NASDAQ',
164
- ticker: 'IXIC',
165
- price: '18,942.18',
166
- change: '-0.50%',
167
- positive: false,
168
- // prettier-ignore
169
- spark: toSparkSeries(2042, [88.89, 94.82, 92.53, 93.22, 91.88, 91.07, 90.86, 89.3, 89.42, 85.69, 88.02, 82.52, 84.79, 86.08, 81.15, 78.83, 79.56, 80.01, 83.14, 84.1, 88.09, 84.29, 82.48, 84.9, 84.15, 86.04, 87.12, 86.85, 82.29, 84.8, 81.87, 79.88, 79.57, 78.77, 82.32, 83.19, 85.35, 86.71, 87.68, 80, 74.7, 73.28, 77.07, 71.96, 68.62, 70.7, 73.72, 74.67, 68.28, 71.29, 69.49, 71.73, 72.22, 73.75, 77.29, 79.86, 76.32, 78.94, 78.77, 77.52, 78.62, 80.01, 85.09, 86.36, 83.71, 88.49, 89.99, 86.02, 89.11, 83.46, 84.72, 85.43, 85.97, 84.73, 86.18, 88.06, 82.82, 82.32, 87.12, 82.77, 79.32, 84.78, 84.46, 80.3, 84.21, 82.15, 84.84, 75.1, 82.07, 85.92, 78.74, 82.77, 82.99, 87.84, 88.57, 90.07]),
170
- },
171
- {
172
- name: 'S&P 500',
173
- ticker: 'SPX',
174
- price: '5,918.33',
175
- change: '+0.21%',
176
- positive: true,
177
- // prettier-ignore
178
- spark: toSparkSeries(3155, [73.95, 72.03, 71.08, 71.6, 73.2, 72.67, 74.14, 73.45, 76.38, 75.1, 77.61, 79.72, 77.52, 79.27, 78.25, 75.63, 76.23, 76.58, 72.42, 72.4, 69.84, 66.76, 67.12, 67.73, 64.69, 68.27, 65.01, 64.02, 66.05, 69.6, 68.7, 70.05, 69.54, 65.52, 66.42, 66.58, 69.36, 71.28, 71.66, 73.08, 70.32, 69.85, 72.45, 72.94, 72.02, 74.88, 71.61, 71.7, 72.69, 74.3, 74.46, 71.99, 70.45, 70.11, 72.53, 69.96, 71.6, 72, 74.4, 70.78, 72.74, 71.44, 71.37, 73.54, 71.05, 72.86, 70.79, 69.5, 73.43, 72.36, 73.76, 75.05, 77.58, 77.17, 78.5, 82.25, 78.78, 78.42, 78.07, 82.01, 78.82, 78.48, 78.36, 79.8, 79, 76.6, 75.18, 75.82, 78.39, 80.59, 80.93, 79.71, 78.44, 74.41, 74.09, 75.98]),
179
- },
180
- {
181
- name: 'NYSE Composite',
182
- ticker: 'NYA',
183
- price: '19,752.41',
184
- change: '-0.20%',
185
- positive: false,
186
- // prettier-ignore
187
- spark: toSparkSeries(4287, [64.45, 61.11, 62.04, 60.88, 61.84, 66.42, 65.95, 68.68, 66.53, 66.2, 64.61, 66.78, 65.37, 64.69, 64.48, 60.48, 70.11, 69.62, 67.37, 70.31, 73.61, 74.74, 71.95, 68.83, 68.85, 66.69, 65.72, 64.01, 65.91, 64.38, 68.51, 63.62, 68.71, 66.45, 66.61, 70.22, 67.86, 68.96, 72.44, 74.53, 74.4, 75.52, 72.63, 69.2, 67.72, 72.46, 75.34, 74.64, 76.13, 73.74, 73.53, 74.47, 76.73, 76.4, 74.91, 74.34, 73.92, 70.94, 71.5, 68.68, 68.44, 65.97, 68.07, 69.63, 70.45, 68.45, 65.12, 63.93, 64.71, 66.68, 61.08, 58, 61.79, 63.61, 65.05, 62.4, 68.06, 65.87, 66.63, 68.18, 68.06, 67.32, 64.23, 65.72, 63.78, 62.09, 63.94, 61.55, 59.14, 59.95, 58.74, 59.44, 61.25, 63.96, 60.62, 59.61]),
188
- },
189
- {
190
- name: 'NVIDIA Corp.',
191
- ticker: 'NVDA',
192
- price: '$177.39',
193
- change: '+0.93%',
194
- positive: true,
195
- // prettier-ignore
196
- spark: toSparkSeries(5519, [66.26, 68.28, 68.39, 65.49, 62.97, 68.15, 71.28, 67.14, 71.53, 68.43, 69.39, 73.97, 67.92, 57.1, 61.58, 64.45, 59.15, 55.38, 56.78, 60.73, 63.46, 59.3, 56.58, 56.55, 59.43, 58.93, 57.88, 57.25, 56.11, 61.79, 62.87, 65.09, 66.15, 59.93, 60.64, 63.97, 65.3, 65.5, 66.02, 67.5, 67.43, 61.9, 59.69, 59.48, 59.88, 61.71, 59.95, 54.99, 52.18, 51.09, 52.02, 53.2, 55.88, 60.58, 56.65, 58.89, 55.9, 58.03, 60.31, 62.14, 57.94, 61.24, 60.94, 59.09, 64.39, 63.59, 64.45, 65.84, 66.83, 69.01, 68.41, 71.9, 74.79, 69.82, 67.76, 68.77, 67.7, 65.13, 62.97, 66.86, 69.31, 69.64, 67.68, 67.14, 67.12, 68.13, 61.27, 66.8, 63.53, 68.48, 64.28, 68.53, 69.09, 66.14, 66.67, 72.57]),
197
- },
198
- {
199
- name: 'Intel Corp.',
200
- ticker: 'INTC',
201
- price: '$50.38',
202
- change: '+4.89%',
203
- positive: true,
204
- // prettier-ignore
205
- spark: toSparkSeries(6701, [40.29, 36.52, 38.65, 38.66, 38.73, 39.59, 39.74, 40.27, 37.68, 37.74, 38.19, 38.45, 34.69, 33.53, 36.19, 35.7, 37.18, 36.76, 36.8, 38.82, 38.54, 36.97, 38.21, 39.09, 43.08, 45.5, 42.94, 40.59, 40.6, 43.51, 42.51, 44.7, 47.77, 46.53, 44.78, 45.66, 45.29, 47.54, 45.72, 47.71, 47.55, 49.13, 47.53, 43.52, 41.69, 40.58, 41.74, 46.68, 47.53, 49.99, 46.64, 49.91, 49.01, 51.21, 47.53, 45.66, 47.51, 48.7, 50.76, 53.95, 48.88, 50.92, 51.45, 51.69, 49.34, 52.86, 55.07, 51.08, 50.1, 49.78, 49.18, 49.55, 49.15, 45.34, 47.77, 45.99, 43.61, 44.84, 48.01, 46.33, 43.42, 46.93, 46.35, 47.6, 45.09, 45.85, 43.72, 41.2, 42.89, 42.83, 43.48, 46.41, 49.01, 48.49, 48.68, 51.78]),
206
- },
207
- {
208
- name: 'Nokia Corp.',
209
- ticker: 'NOK',
210
- price: '$8.82',
211
- change: '+6.65%',
212
- positive: true,
213
- // prettier-ignore
214
- spark: toSparkSeries(7823, [27.46, 26.98, 25.8, 25.95, 22.92, 23.25, 27.1, 28.27, 26.12, 28.96, 28.95, 27.78, 29.41, 30.28, 30.85, 32.49, 31.78, 32.11, 34.47, 36.23, 36.59, 37.96, 35.38, 36.79, 37.14, 37.87, 36.61, 35.2, 39.04, 40.18, 41.09, 41.92, 38.86, 36.21, 33.95, 35.27, 35.61, 35.38, 37.29, 37.02, 36.76, 34.27, 34.9, 34.79, 33.48, 34.53, 35.99, 38.24, 37.76, 39.23, 38.1, 36.08, 35.04, 36.38, 37.29, 39.2, 41.21, 42.11, 41.35, 38.23, 36.64, 37.97, 39.03, 39.17, 36.98, 35.69, 33.04, 31.86, 38.61, 35.08, 35.33, 36.35, 34.24, 36.73, 32.08, 32.11, 32.87, 31.23, 33.2, 33.36, 32.8, 31.44, 32.34, 29.65, 36.02, 38.33, 39.14, 42.58, 44.72, 43.06, 40.97, 40.47, 41.21, 40.21, 40.21, 40.11]),
215
- },
216
- {
217
- name: 'Tesla, Inc.',
218
- ticker: 'TSLA',
219
- price: '$360.59',
220
- change: '-5.42%',
221
- positive: false,
222
- // prettier-ignore
223
- spark: toSparkSeries(8945, [89.21, 88.42, 91.95, 94.9, 97.2, 93.76, 95.02, 93.99, 96.41, 93.71, 92.92, 95.4, 91.94, 97.48, 96.94, 95.45, 92.4, 93.76, 96.33, 97.28, 96.74, 92.99, 92.26, 90.48, 93.76, 92.11, 86.82, 83.2, 87.28, 88.08, 86.09, 81.46, 77.09, 79.91, 85.21, 80.13, 85.23, 80.03, 81.33, 84.31, 84.52, 84.57, 81.19, 81.81, 75.98, 77.99, 77.95, 75.93, 76.87, 81.09, 83.04, 82.12, 78.12, 78.69, 77.63, 79.19, 74.18, 75.44, 77.03, 77.73, 71.89, 76.98, 74.25, 77.95, 77.4, 81.04, 78.54, 77.9, 78.6, 76.53, 73.58, 71.02, 71.9, 70.44, 76.85, 77.69, 76.22, 81.78, 82.78, 85.32, 87.59, 91.37, 88.55, 86.27, 84.09, 86.52, 85.12, 84.64, 86.29, 82.58, 81.71, 86.28, 88.26, 83.47, 80.1, 79.92]),
224
- },
155
+ {
156
+ name: "Dow Jones",
157
+ ticker: "DJI",
158
+ price: "43,821.67",
159
+ change: "+0.42%",
160
+ positive: true,
161
+ // prettier-ignore
162
+ spark: toSparkSeries(
163
+ 1337,
164
+ [
165
+ 77.87, 75.96, 76.35, 77.99, 78.95, 77.57, 78.14, 75.12, 74.24, 74.03, 72.88, 71.93, 74.65,
166
+ 71.43, 69.56, 74.19, 72.66, 72.02, 76.09, 74.07, 75.82, 78.14, 77.91, 80.15, 79.18, 76.96,
167
+ 76.57, 81.07, 83.59, 83.21, 81.58, 83.82, 84.82, 88.56, 87.03, 89.64, 87.35, 89.63, 90.67,
168
+ 87.45, 87.4, 86.09, 87.85, 82.63, 81.81, 84.78, 82.26, 83.25, 80.04, 80.63, 83.56, 84.28,
169
+ 83.96, 84.49, 80.85, 79.3, 80.45, 84.58, 81.83, 83.45, 87.92, 90.46, 89.1, 88.1, 90.6,
170
+ 87.26, 84.82, 84.27, 86.7, 82.89, 84.38, 89.24, 82.39, 82.18, 82.24, 81.38, 82.38, 78.55,
171
+ 76.91, 75.31, 78.55, 76.46, 73.89, 76.36, 77.01, 79.15, 78.98, 79.79, 78.68, 77.35, 76.01,
172
+ 84.41, 82.31, 81.97, 80.42, 80.29,
173
+ ],
174
+ ),
175
+ },
176
+ {
177
+ name: "NASDAQ",
178
+ ticker: "IXIC",
179
+ price: "18,942.18",
180
+ change: "-0.50%",
181
+ positive: false,
182
+ // prettier-ignore
183
+ spark: toSparkSeries(
184
+ 2042,
185
+ [
186
+ 88.89, 94.82, 92.53, 93.22, 91.88, 91.07, 90.86, 89.3, 89.42, 85.69, 88.02, 82.52, 84.79,
187
+ 86.08, 81.15, 78.83, 79.56, 80.01, 83.14, 84.1, 88.09, 84.29, 82.48, 84.9, 84.15, 86.04,
188
+ 87.12, 86.85, 82.29, 84.8, 81.87, 79.88, 79.57, 78.77, 82.32, 83.19, 85.35, 86.71, 87.68,
189
+ 80, 74.7, 73.28, 77.07, 71.96, 68.62, 70.7, 73.72, 74.67, 68.28, 71.29, 69.49, 71.73, 72.22,
190
+ 73.75, 77.29, 79.86, 76.32, 78.94, 78.77, 77.52, 78.62, 80.01, 85.09, 86.36, 83.71, 88.49,
191
+ 89.99, 86.02, 89.11, 83.46, 84.72, 85.43, 85.97, 84.73, 86.18, 88.06, 82.82, 82.32, 87.12,
192
+ 82.77, 79.32, 84.78, 84.46, 80.3, 84.21, 82.15, 84.84, 75.1, 82.07, 85.92, 78.74, 82.77,
193
+ 82.99, 87.84, 88.57, 90.07,
194
+ ],
195
+ ),
196
+ },
197
+ {
198
+ name: "S&P 500",
199
+ ticker: "SPX",
200
+ price: "5,918.33",
201
+ change: "+0.21%",
202
+ positive: true,
203
+ // prettier-ignore
204
+ spark: toSparkSeries(
205
+ 3155,
206
+ [
207
+ 73.95, 72.03, 71.08, 71.6, 73.2, 72.67, 74.14, 73.45, 76.38, 75.1, 77.61, 79.72, 77.52,
208
+ 79.27, 78.25, 75.63, 76.23, 76.58, 72.42, 72.4, 69.84, 66.76, 67.12, 67.73, 64.69, 68.27,
209
+ 65.01, 64.02, 66.05, 69.6, 68.7, 70.05, 69.54, 65.52, 66.42, 66.58, 69.36, 71.28, 71.66,
210
+ 73.08, 70.32, 69.85, 72.45, 72.94, 72.02, 74.88, 71.61, 71.7, 72.69, 74.3, 74.46, 71.99,
211
+ 70.45, 70.11, 72.53, 69.96, 71.6, 72, 74.4, 70.78, 72.74, 71.44, 71.37, 73.54, 71.05, 72.86,
212
+ 70.79, 69.5, 73.43, 72.36, 73.76, 75.05, 77.58, 77.17, 78.5, 82.25, 78.78, 78.42, 78.07,
213
+ 82.01, 78.82, 78.48, 78.36, 79.8, 79, 76.6, 75.18, 75.82, 78.39, 80.59, 80.93, 79.71, 78.44,
214
+ 74.41, 74.09, 75.98,
215
+ ],
216
+ ),
217
+ },
218
+ {
219
+ name: "NYSE Composite",
220
+ ticker: "NYA",
221
+ price: "19,752.41",
222
+ change: "-0.20%",
223
+ positive: false,
224
+ // prettier-ignore
225
+ spark: toSparkSeries(
226
+ 4287,
227
+ [
228
+ 64.45, 61.11, 62.04, 60.88, 61.84, 66.42, 65.95, 68.68, 66.53, 66.2, 64.61, 66.78, 65.37,
229
+ 64.69, 64.48, 60.48, 70.11, 69.62, 67.37, 70.31, 73.61, 74.74, 71.95, 68.83, 68.85, 66.69,
230
+ 65.72, 64.01, 65.91, 64.38, 68.51, 63.62, 68.71, 66.45, 66.61, 70.22, 67.86, 68.96, 72.44,
231
+ 74.53, 74.4, 75.52, 72.63, 69.2, 67.72, 72.46, 75.34, 74.64, 76.13, 73.74, 73.53, 74.47,
232
+ 76.73, 76.4, 74.91, 74.34, 73.92, 70.94, 71.5, 68.68, 68.44, 65.97, 68.07, 69.63, 70.45,
233
+ 68.45, 65.12, 63.93, 64.71, 66.68, 61.08, 58, 61.79, 63.61, 65.05, 62.4, 68.06, 65.87,
234
+ 66.63, 68.18, 68.06, 67.32, 64.23, 65.72, 63.78, 62.09, 63.94, 61.55, 59.14, 59.95, 58.74,
235
+ 59.44, 61.25, 63.96, 60.62, 59.61,
236
+ ],
237
+ ),
238
+ },
239
+ {
240
+ name: "NVIDIA Corp.",
241
+ ticker: "NVDA",
242
+ price: "$177.39",
243
+ change: "+0.93%",
244
+ positive: true,
245
+ // prettier-ignore
246
+ spark: toSparkSeries(
247
+ 5519,
248
+ [
249
+ 66.26, 68.28, 68.39, 65.49, 62.97, 68.15, 71.28, 67.14, 71.53, 68.43, 69.39, 73.97, 67.92,
250
+ 57.1, 61.58, 64.45, 59.15, 55.38, 56.78, 60.73, 63.46, 59.3, 56.58, 56.55, 59.43, 58.93,
251
+ 57.88, 57.25, 56.11, 61.79, 62.87, 65.09, 66.15, 59.93, 60.64, 63.97, 65.3, 65.5, 66.02,
252
+ 67.5, 67.43, 61.9, 59.69, 59.48, 59.88, 61.71, 59.95, 54.99, 52.18, 51.09, 52.02, 53.2,
253
+ 55.88, 60.58, 56.65, 58.89, 55.9, 58.03, 60.31, 62.14, 57.94, 61.24, 60.94, 59.09, 64.39,
254
+ 63.59, 64.45, 65.84, 66.83, 69.01, 68.41, 71.9, 74.79, 69.82, 67.76, 68.77, 67.7, 65.13,
255
+ 62.97, 66.86, 69.31, 69.64, 67.68, 67.14, 67.12, 68.13, 61.27, 66.8, 63.53, 68.48, 64.28,
256
+ 68.53, 69.09, 66.14, 66.67, 72.57,
257
+ ],
258
+ ),
259
+ },
260
+ {
261
+ name: "Intel Corp.",
262
+ ticker: "INTC",
263
+ price: "$50.38",
264
+ change: "+4.89%",
265
+ positive: true,
266
+ // prettier-ignore
267
+ spark: toSparkSeries(
268
+ 6701,
269
+ [
270
+ 40.29, 36.52, 38.65, 38.66, 38.73, 39.59, 39.74, 40.27, 37.68, 37.74, 38.19, 38.45, 34.69,
271
+ 33.53, 36.19, 35.7, 37.18, 36.76, 36.8, 38.82, 38.54, 36.97, 38.21, 39.09, 43.08, 45.5,
272
+ 42.94, 40.59, 40.6, 43.51, 42.51, 44.7, 47.77, 46.53, 44.78, 45.66, 45.29, 47.54, 45.72,
273
+ 47.71, 47.55, 49.13, 47.53, 43.52, 41.69, 40.58, 41.74, 46.68, 47.53, 49.99, 46.64, 49.91,
274
+ 49.01, 51.21, 47.53, 45.66, 47.51, 48.7, 50.76, 53.95, 48.88, 50.92, 51.45, 51.69, 49.34,
275
+ 52.86, 55.07, 51.08, 50.1, 49.78, 49.18, 49.55, 49.15, 45.34, 47.77, 45.99, 43.61, 44.84,
276
+ 48.01, 46.33, 43.42, 46.93, 46.35, 47.6, 45.09, 45.85, 43.72, 41.2, 42.89, 42.83, 43.48,
277
+ 46.41, 49.01, 48.49, 48.68, 51.78,
278
+ ],
279
+ ),
280
+ },
281
+ {
282
+ name: "Nokia Corp.",
283
+ ticker: "NOK",
284
+ price: "$8.82",
285
+ change: "+6.65%",
286
+ positive: true,
287
+ // prettier-ignore
288
+ spark: toSparkSeries(
289
+ 7823,
290
+ [
291
+ 27.46, 26.98, 25.8, 25.95, 22.92, 23.25, 27.1, 28.27, 26.12, 28.96, 28.95, 27.78, 29.41,
292
+ 30.28, 30.85, 32.49, 31.78, 32.11, 34.47, 36.23, 36.59, 37.96, 35.38, 36.79, 37.14, 37.87,
293
+ 36.61, 35.2, 39.04, 40.18, 41.09, 41.92, 38.86, 36.21, 33.95, 35.27, 35.61, 35.38, 37.29,
294
+ 37.02, 36.76, 34.27, 34.9, 34.79, 33.48, 34.53, 35.99, 38.24, 37.76, 39.23, 38.1, 36.08,
295
+ 35.04, 36.38, 37.29, 39.2, 41.21, 42.11, 41.35, 38.23, 36.64, 37.97, 39.03, 39.17, 36.98,
296
+ 35.69, 33.04, 31.86, 38.61, 35.08, 35.33, 36.35, 34.24, 36.73, 32.08, 32.11, 32.87, 31.23,
297
+ 33.2, 33.36, 32.8, 31.44, 32.34, 29.65, 36.02, 38.33, 39.14, 42.58, 44.72, 43.06, 40.97,
298
+ 40.47, 41.21, 40.21, 40.21, 40.11,
299
+ ],
300
+ ),
301
+ },
302
+ {
303
+ name: "Tesla, Inc.",
304
+ ticker: "TSLA",
305
+ price: "$360.59",
306
+ change: "-5.42%",
307
+ positive: false,
308
+ // prettier-ignore
309
+ spark: toSparkSeries(
310
+ 8945,
311
+ [
312
+ 89.21, 88.42, 91.95, 94.9, 97.2, 93.76, 95.02, 93.99, 96.41, 93.71, 92.92, 95.4, 91.94,
313
+ 97.48, 96.94, 95.45, 92.4, 93.76, 96.33, 97.28, 96.74, 92.99, 92.26, 90.48, 93.76, 92.11,
314
+ 86.82, 83.2, 87.28, 88.08, 86.09, 81.46, 77.09, 79.91, 85.21, 80.13, 85.23, 80.03, 81.33,
315
+ 84.31, 84.52, 84.57, 81.19, 81.81, 75.98, 77.99, 77.95, 75.93, 76.87, 81.09, 83.04, 82.12,
316
+ 78.12, 78.69, 77.63, 79.19, 74.18, 75.44, 77.03, 77.73, 71.89, 76.98, 74.25, 77.95, 77.4,
317
+ 81.04, 78.54, 77.9, 78.6, 76.53, 73.58, 71.02, 71.9, 70.44, 76.85, 77.69, 76.22, 81.78,
318
+ 82.78, 85.32, 87.59, 91.37, 88.55, 86.27, 84.09, 86.52, 85.12, 84.64, 86.29, 82.58, 81.71,
319
+ 86.28, 88.26, 83.47, 80.1, 79.92,
320
+ ],
321
+ ),
322
+ },
225
323
  ];
226
324
 
227
325
  // Trending stocks table data
228
326
  interface StockRow extends Record<string, unknown> {
229
- id: string;
230
- ticker: string;
231
- price: string;
232
- dailyPts: number;
233
- dailyPct: number;
234
- weekChg: number;
235
- spark: SparkPoint[];
327
+ id: string;
328
+ ticker: string;
329
+ price: string;
330
+ dailyPts: number;
331
+ dailyPct: number;
332
+ weekChg: number;
333
+ spark: SparkPoint[];
236
334
  }
237
335
 
238
336
  const trendingStocks: StockRow[] = [
239
- {
240
- id: '1',
241
- ticker: 'AAPL',
242
- price: '$188.72',
243
- dailyPts: 1.35,
244
- dailyPct: 0.72,
245
- weekChg: 22.4,
246
- // prettier-ignore
247
- spark: toSparkSeries(11023, [62.59, 62.87, 66.28, 66.94, 63.05, 60.71, 60.87, 63.01, 65.63, 66.72, 66.14, 65.67, 63.28, 63.24, 64.03, 65.31, 69.18, 67.03, 67.02, 65.16, 63.09, 63.23, 59.79, 57.91, 60.58, 62.7, 59.12, 60.59, 60.07, 64.53, 64.61, 68.12, 66.44, 67.6, 65.99, 65.37, 67.56, 64.35, 65.35, 66.14, 65.38, 65.53, 63.89, 68.35, 70.22, 72.78, 73.31, 77.18]),
248
- },
249
- {
250
- id: '2',
251
- ticker: 'MSFT',
252
- price: '$415.6',
253
- dailyPts: 3.2,
254
- dailyPct: 0.78,
255
- weekChg: 18.6,
256
- // prettier-ignore
257
- spark: toSparkSeries(12591, [52.61, 54.67, 54.37, 52.39, 53.13, 54.24, 53.77, 56.96, 54.21, 56.36, 52.87, 52.91, 52.91, 53.06, 54.05, 56.77, 55.22, 53.5, 55.24, 55.21, 55.57, 57.52, 58.11, 60.48, 62.67, 64.05, 67.62, 67.64, 70.11, 69.7, 67.71, 66.07, 64.31, 61.5, 60.25, 60.01, 62.04, 63.71, 64.69, 65.02, 63.57, 64.63, 65.9, 61.94, 64.71, 63.25, 60.84, 62.99]),
258
- },
259
- {
260
- id: '3',
261
- ticker: 'NVDA',
262
- price: '$177.39',
263
- dailyPts: 1.65,
264
- dailyPct: 0.93,
265
- weekChg: 45.2,
266
- // prettier-ignore
267
- spark: toSparkSeries(13744, [42.97, 43.84, 42.55, 43.07, 41.45, 45.69, 47.46, 45.15, 47.75, 43.08, 44.46, 37.8, 37.94, 35.72, 35.14, 32.33, 41.42, 45.22, 46.61, 45, 44.99, 48.42, 56.57, 59.5, 54.67, 55.56, 53.34, 54.15, 56.29, 57.95, 59.47, 59.02, 58.39, 61.6, 59.14, 66.24, 68.92, 69.13, 73.86, 76.15, 73.14, 74.89, 71.4, 71.95, 74.65, 69.61, 63.98, 63.68]),
268
- },
269
- {
270
- id: '4',
271
- ticker: 'AMZN',
272
- price: '$186.5',
273
- dailyPts: -0.8,
274
- dailyPct: -0.43,
275
- weekChg: 15.3,
276
- // prettier-ignore
277
- spark: toSparkSeries(14882, [65.57, 69.23, 72.54, 70.58, 69.7, 67.79, 66.66, 69.84, 72.05, 73.69, 71.33, 72.56, 68.97, 67.02, 69.25, 68.04, 70.12, 73.38, 71.49, 70.24, 70.59, 69.81, 70.5, 66.45, 63.72, 61.63, 63.06, 60.22, 61.87, 61.91, 61.4, 61.43, 62.03, 63.27, 65.56, 61.76, 62.77, 62.52, 63.4, 65.86, 61.54, 60.71, 61.09, 66, 62.46, 64, 67.5, 65.32]),
278
- },
279
- {
280
- id: '5',
281
- ticker: 'GOOGL',
282
- price: '$155.72',
283
- dailyPts: 2.1,
284
- dailyPct: 1.37,
285
- weekChg: 12.8,
286
- // prettier-ignore
287
- spark: toSparkSeries(15967, [50.19, 52.48, 56.14, 57.13, 53.58, 52.4, 49.76, 51.71, 53.73, 52.83, 47.01, 48.64, 46.49, 44.71, 45.84, 47.51, 51.21, 51.75, 47.07, 53.25, 53.02, 56.04, 53.9, 54.27, 53.09, 56.7, 54.86, 53.61, 54.39, 55.27, 58.44, 59.21, 58.89, 60.97, 63.5, 64.41, 64.28, 65.48, 63.86, 62.88, 65.82, 64.06, 60.89, 57.93, 60.15, 57.94, 57.86, 54.45]),
288
- },
289
- {
290
- id: '6',
291
- ticker: 'META',
292
- price: '$505.3',
293
- dailyPts: 4.5,
294
- dailyPct: 0.9,
295
- weekChg: 35.1,
296
- // prettier-ignore
297
- spark: toSparkSeries(17105, [46.75, 47.76, 46.13, 45.91, 45.34, 49.3, 50.87, 48.9, 46.85, 49.07, 50.88, 54.37, 54.34, 54.17, 52.62, 52.71, 55.65, 55.18, 57.28, 59.71, 56.07, 57.36, 56.8, 52.95, 49.59, 50.35, 50.11, 48.92, 51.6, 52.4, 50.29, 52.17, 54.87, 55.44, 54.27, 52.27, 53.76, 55.31, 55.75, 56.16, 57.8, 58.73, 54.6, 53.58, 55.27, 55.62, 62.45, 62.94]),
298
- },
299
- {
300
- id: '7',
301
- ticker: 'TSLA',
302
- price: '$360.59',
303
- dailyPts: -20.67,
304
- dailyPct: -5.42,
305
- weekChg: -8.3,
306
- // prettier-ignore
307
- spark: toSparkSeries(18249, [83.75, 78.44, 81.1, 79.12, 76.61, 84.38, 84.64, 87.7, 86.31, 80.02, 74.82, 75.1, 75.36, 73.06, 74.86, 75.38, 78.07, 78.02, 75.42, 81.25, 85.54, 86.68, 84.46, 86.89, 80.57, 84.1, 82.48, 85.83, 87.06, 79.95, 74.26, 68.41, 64.98, 70.44, 71.17, 74.21, 69.56, 73.59, 70.47, 70.24, 65.12, 64.41, 69.18, 62.39, 63.5, 59.78, 59.96, 60.64]),
308
- },
309
- {
310
- id: '8',
311
- ticker: 'INTC',
312
- price: '$50.38',
313
- dailyPts: 2.35,
314
- dailyPct: 4.89,
315
- weekChg: -12.5,
316
- // prettier-ignore
317
- spark: toSparkSeries(19388, [71.45, 75.23, 76.21, 71.96, 69.34, 69.92, 69.21, 65.68, 62.87, 64.07, 64.44, 62.98, 59.15, 61.12, 62.74, 64.13, 62.29, 65.14, 60.86, 58.23, 56.22, 57.6, 60.39, 57.87, 57.04, 54.84, 55.29, 56.38, 56.92, 58.48, 61.09, 59.01, 58.65, 61.44, 61.55, 58.75, 61.48, 60.6, 58.99, 61.4, 56.7, 57.42, 59.98, 57.74, 64.44, 59.11, 59.16, 59.45]),
318
- },
319
- {
320
- id: '9',
321
- ticker: 'AMD',
322
- price: '$162.45',
323
- dailyPts: -1.2,
324
- dailyPct: -0.73,
325
- weekChg: 28.7,
326
- // prettier-ignore
327
- spark: toSparkSeries(20471, [44.8, 46.66, 43.7, 46.12, 47.65, 50.65, 48.05, 52.96, 54.41, 54.91, 53.77, 49.18, 50.01, 52.14, 49.46, 49.81, 52.08, 52.56, 50.02, 50.61, 56.27, 56.79, 62.63, 61.99, 65.99, 62.88, 59.6, 57.44, 58.61, 62.78, 62, 59.58, 57.36, 54.92, 59.1, 55.37, 64.03, 63.13, 63.88, 65.3, 65.22, 60.4, 62.84, 60.12, 59.42, 65.11, 58, 57.8]),
328
- },
329
- {
330
- id: '10',
331
- ticker: 'NFLX',
332
- price: '$628.9',
333
- dailyPts: 5.4,
334
- dailyPct: 0.87,
335
- weekChg: 42.1,
336
- // prettier-ignore
337
- spark: toSparkSeries(21556, [42.07, 41.72, 43.08, 43.95, 42.71, 43, 47.8, 51.53, 49.82, 51.72, 51.75, 54.86, 54.64, 54.28, 55.52, 55.2, 53.51, 49.34, 45.58, 51.95, 44.7, 46.57, 48.92, 47.16, 49.62, 47.2, 47.47, 49.84, 51.12, 53.9, 53.39, 51.34, 51.56, 47.93, 50.31, 50.69, 50.32, 50.04, 53.93, 52.8, 52.46, 55.01, 56.66, 58.11, 59.39, 60.81, 62.81, 64]),
338
- },
337
+ {
338
+ id: "1",
339
+ ticker: "AAPL",
340
+ price: "$188.72",
341
+ dailyPts: 1.35,
342
+ dailyPct: 0.72,
343
+ weekChg: 22.4,
344
+ // prettier-ignore
345
+ spark: toSparkSeries(
346
+ 11023,
347
+ [
348
+ 62.59, 62.87, 66.28, 66.94, 63.05, 60.71, 60.87, 63.01, 65.63, 66.72, 66.14, 65.67, 63.28,
349
+ 63.24, 64.03, 65.31, 69.18, 67.03, 67.02, 65.16, 63.09, 63.23, 59.79, 57.91, 60.58, 62.7,
350
+ 59.12, 60.59, 60.07, 64.53, 64.61, 68.12, 66.44, 67.6, 65.99, 65.37, 67.56, 64.35, 65.35,
351
+ 66.14, 65.38, 65.53, 63.89, 68.35, 70.22, 72.78, 73.31, 77.18,
352
+ ],
353
+ ),
354
+ },
355
+ {
356
+ id: "2",
357
+ ticker: "MSFT",
358
+ price: "$415.6",
359
+ dailyPts: 3.2,
360
+ dailyPct: 0.78,
361
+ weekChg: 18.6,
362
+ // prettier-ignore
363
+ spark: toSparkSeries(
364
+ 12591,
365
+ [
366
+ 52.61, 54.67, 54.37, 52.39, 53.13, 54.24, 53.77, 56.96, 54.21, 56.36, 52.87, 52.91, 52.91,
367
+ 53.06, 54.05, 56.77, 55.22, 53.5, 55.24, 55.21, 55.57, 57.52, 58.11, 60.48, 62.67, 64.05,
368
+ 67.62, 67.64, 70.11, 69.7, 67.71, 66.07, 64.31, 61.5, 60.25, 60.01, 62.04, 63.71, 64.69,
369
+ 65.02, 63.57, 64.63, 65.9, 61.94, 64.71, 63.25, 60.84, 62.99,
370
+ ],
371
+ ),
372
+ },
373
+ {
374
+ id: "3",
375
+ ticker: "NVDA",
376
+ price: "$177.39",
377
+ dailyPts: 1.65,
378
+ dailyPct: 0.93,
379
+ weekChg: 45.2,
380
+ // prettier-ignore
381
+ spark: toSparkSeries(
382
+ 13744,
383
+ [
384
+ 42.97, 43.84, 42.55, 43.07, 41.45, 45.69, 47.46, 45.15, 47.75, 43.08, 44.46, 37.8, 37.94,
385
+ 35.72, 35.14, 32.33, 41.42, 45.22, 46.61, 45, 44.99, 48.42, 56.57, 59.5, 54.67, 55.56,
386
+ 53.34, 54.15, 56.29, 57.95, 59.47, 59.02, 58.39, 61.6, 59.14, 66.24, 68.92, 69.13, 73.86,
387
+ 76.15, 73.14, 74.89, 71.4, 71.95, 74.65, 69.61, 63.98, 63.68,
388
+ ],
389
+ ),
390
+ },
391
+ {
392
+ id: "4",
393
+ ticker: "AMZN",
394
+ price: "$186.5",
395
+ dailyPts: -0.8,
396
+ dailyPct: -0.43,
397
+ weekChg: 15.3,
398
+ // prettier-ignore
399
+ spark: toSparkSeries(
400
+ 14882,
401
+ [
402
+ 65.57, 69.23, 72.54, 70.58, 69.7, 67.79, 66.66, 69.84, 72.05, 73.69, 71.33, 72.56, 68.97,
403
+ 67.02, 69.25, 68.04, 70.12, 73.38, 71.49, 70.24, 70.59, 69.81, 70.5, 66.45, 63.72, 61.63,
404
+ 63.06, 60.22, 61.87, 61.91, 61.4, 61.43, 62.03, 63.27, 65.56, 61.76, 62.77, 62.52, 63.4,
405
+ 65.86, 61.54, 60.71, 61.09, 66, 62.46, 64, 67.5, 65.32,
406
+ ],
407
+ ),
408
+ },
409
+ {
410
+ id: "5",
411
+ ticker: "GOOGL",
412
+ price: "$155.72",
413
+ dailyPts: 2.1,
414
+ dailyPct: 1.37,
415
+ weekChg: 12.8,
416
+ // prettier-ignore
417
+ spark: toSparkSeries(
418
+ 15967,
419
+ [
420
+ 50.19, 52.48, 56.14, 57.13, 53.58, 52.4, 49.76, 51.71, 53.73, 52.83, 47.01, 48.64, 46.49,
421
+ 44.71, 45.84, 47.51, 51.21, 51.75, 47.07, 53.25, 53.02, 56.04, 53.9, 54.27, 53.09, 56.7,
422
+ 54.86, 53.61, 54.39, 55.27, 58.44, 59.21, 58.89, 60.97, 63.5, 64.41, 64.28, 65.48, 63.86,
423
+ 62.88, 65.82, 64.06, 60.89, 57.93, 60.15, 57.94, 57.86, 54.45,
424
+ ],
425
+ ),
426
+ },
427
+ {
428
+ id: "6",
429
+ ticker: "META",
430
+ price: "$505.3",
431
+ dailyPts: 4.5,
432
+ dailyPct: 0.9,
433
+ weekChg: 35.1,
434
+ // prettier-ignore
435
+ spark: toSparkSeries(
436
+ 17105,
437
+ [
438
+ 46.75, 47.76, 46.13, 45.91, 45.34, 49.3, 50.87, 48.9, 46.85, 49.07, 50.88, 54.37, 54.34,
439
+ 54.17, 52.62, 52.71, 55.65, 55.18, 57.28, 59.71, 56.07, 57.36, 56.8, 52.95, 49.59, 50.35,
440
+ 50.11, 48.92, 51.6, 52.4, 50.29, 52.17, 54.87, 55.44, 54.27, 52.27, 53.76, 55.31, 55.75,
441
+ 56.16, 57.8, 58.73, 54.6, 53.58, 55.27, 55.62, 62.45, 62.94,
442
+ ],
443
+ ),
444
+ },
445
+ {
446
+ id: "7",
447
+ ticker: "TSLA",
448
+ price: "$360.59",
449
+ dailyPts: -20.67,
450
+ dailyPct: -5.42,
451
+ weekChg: -8.3,
452
+ // prettier-ignore
453
+ spark: toSparkSeries(
454
+ 18249,
455
+ [
456
+ 83.75, 78.44, 81.1, 79.12, 76.61, 84.38, 84.64, 87.7, 86.31, 80.02, 74.82, 75.1, 75.36,
457
+ 73.06, 74.86, 75.38, 78.07, 78.02, 75.42, 81.25, 85.54, 86.68, 84.46, 86.89, 80.57, 84.1,
458
+ 82.48, 85.83, 87.06, 79.95, 74.26, 68.41, 64.98, 70.44, 71.17, 74.21, 69.56, 73.59, 70.47,
459
+ 70.24, 65.12, 64.41, 69.18, 62.39, 63.5, 59.78, 59.96, 60.64,
460
+ ],
461
+ ),
462
+ },
463
+ {
464
+ id: "8",
465
+ ticker: "INTC",
466
+ price: "$50.38",
467
+ dailyPts: 2.35,
468
+ dailyPct: 4.89,
469
+ weekChg: -12.5,
470
+ // prettier-ignore
471
+ spark: toSparkSeries(
472
+ 19388,
473
+ [
474
+ 71.45, 75.23, 76.21, 71.96, 69.34, 69.92, 69.21, 65.68, 62.87, 64.07, 64.44, 62.98, 59.15,
475
+ 61.12, 62.74, 64.13, 62.29, 65.14, 60.86, 58.23, 56.22, 57.6, 60.39, 57.87, 57.04, 54.84,
476
+ 55.29, 56.38, 56.92, 58.48, 61.09, 59.01, 58.65, 61.44, 61.55, 58.75, 61.48, 60.6, 58.99,
477
+ 61.4, 56.7, 57.42, 59.98, 57.74, 64.44, 59.11, 59.16, 59.45,
478
+ ],
479
+ ),
480
+ },
481
+ {
482
+ id: "9",
483
+ ticker: "AMD",
484
+ price: "$162.45",
485
+ dailyPts: -1.2,
486
+ dailyPct: -0.73,
487
+ weekChg: 28.7,
488
+ // prettier-ignore
489
+ spark: toSparkSeries(
490
+ 20471,
491
+ [
492
+ 44.8, 46.66, 43.7, 46.12, 47.65, 50.65, 48.05, 52.96, 54.41, 54.91, 53.77, 49.18, 50.01,
493
+ 52.14, 49.46, 49.81, 52.08, 52.56, 50.02, 50.61, 56.27, 56.79, 62.63, 61.99, 65.99, 62.88,
494
+ 59.6, 57.44, 58.61, 62.78, 62, 59.58, 57.36, 54.92, 59.1, 55.37, 64.03, 63.13, 63.88, 65.3,
495
+ 65.22, 60.4, 62.84, 60.12, 59.42, 65.11, 58, 57.8,
496
+ ],
497
+ ),
498
+ },
499
+ {
500
+ id: "10",
501
+ ticker: "NFLX",
502
+ price: "$628.9",
503
+ dailyPts: 5.4,
504
+ dailyPct: 0.87,
505
+ weekChg: 42.1,
506
+ // prettier-ignore
507
+ spark: toSparkSeries(
508
+ 21556,
509
+ [
510
+ 42.07, 41.72, 43.08, 43.95, 42.71, 43, 47.8, 51.53, 49.82, 51.72, 51.75, 54.86, 54.64,
511
+ 54.28, 55.52, 55.2, 53.51, 49.34, 45.58, 51.95, 44.7, 46.57, 48.92, 47.16, 49.62, 47.2,
512
+ 47.47, 49.84, 51.12, 53.9, 53.39, 51.34, 51.56, 47.93, 50.31, 50.69, 50.32, 50.04, 53.93,
513
+ 52.8, 52.46, 55.01, 56.66, 58.11, 59.39, 60.81, 62.81, 64,
514
+ ],
515
+ ),
516
+ },
339
517
  ];
340
518
 
341
519
  // ============= CHART COMPONENTS =============
@@ -350,292 +528,283 @@ const PORTFOLIO_LEFT = 52;
350
528
  const PORTFOLIO_WIDTH = 500;
351
529
  const PORTFOLIO_STEP = PORTFOLIO_WIDTH / portfolioBars.length;
352
530
  const portfolioYFor = (v: number) =>
353
- PORTFOLIO_BASE - ((v - PORTFOLIO_MIN) / (PORTFOLIO_MAX - PORTFOLIO_MIN)) * PORTFOLIO_PLOT;
531
+ PORTFOLIO_BASE - ((v - PORTFOLIO_MIN) / (PORTFOLIO_MAX - PORTFOLIO_MIN)) * PORTFOLIO_PLOT;
354
532
  const PORTFOLIO_Y_TICKS = [200000, 240000, 280000, 320000];
355
533
 
356
534
  function PortfolioChart() {
357
- return (
358
- <VStack gap={3}>
359
- <Card padding={4} style={CHART_PANEL_STYLE}>
360
- <svg
361
- viewBox="0 0 560 210"
362
- width="100%"
363
- role="img"
364
- aria-label="Vault value by week, October to October">
365
- {PORTFOLIO_Y_TICKS.map(t => (
366
- <g key={t}>
367
- <line
368
- x1={PORTFOLIO_LEFT}
369
- y1={portfolioYFor(t)}
370
- x2={PORTFOLIO_LEFT + PORTFOLIO_WIDTH}
371
- y2={portfolioYFor(t)}
372
- stroke="var(--color-separator)"
373
- strokeDasharray="3 3"
374
- opacity={0.5}
375
- />
376
- <ChartLabel
377
- x={PORTFOLIO_LEFT - 8}
378
- y={portfolioYFor(t) + 4}
379
- textAnchor="end">
380
- ${(t / 1000).toFixed(0)}k
381
- </ChartLabel>
382
- </g>
383
- ))}
384
- {portfolioBars.map((d, i) => {
385
- const h = Math.max(3, PORTFOLIO_BASE - portfolioYFor(d.value));
386
- return (
387
- <rect
388
- key={d.label}
389
- x={PORTFOLIO_LEFT + i * PORTFOLIO_STEP + 1}
390
- y={PORTFOLIO_BASE - h}
391
- width={Math.max(2, PORTFOLIO_STEP - 2)}
392
- height={h}
393
- rx={4}
394
- fill="var(--dracula-green)"
395
- />
396
- );
397
- })}
398
- {xAxisTicks.map(m => (
399
- <ChartLabel
400
- key={m}
401
- x={PORTFOLIO_LEFT + (m / 12) * PORTFOLIO_WIDTH}
402
- y={192}
403
- textAnchor={m === 12 ? 'end' : 'middle'}>
404
- {xAxisLabels[m] ?? ''}
405
- </ChartLabel>
406
- ))}
407
- <ChartLabel
408
- x={PORTFOLIO_LEFT + PORTFOLIO_WIDTH - 4}
409
- y={portfolioYFor(294200) - 8}
410
- textAnchor="end"
411
- fill="var(--color-text-highlight)">
412
- $294k
413
- </ChartLabel>
414
- </svg>
415
- </Card>
416
- <Text type="supporting" color="secondary">
417
- Weekly closes · trailing 12 months
418
- </Text>
419
- </VStack>
420
- );
535
+ return (
536
+ <VStack gap={3}>
537
+ <Card padding={4} style={CHART_PANEL_STYLE}>
538
+ <svg
539
+ viewBox="0 0 560 210"
540
+ width="100%"
541
+ role="img"
542
+ aria-label="Vault value by week, October to October"
543
+ >
544
+ {PORTFOLIO_Y_TICKS.map((t) => (
545
+ <g key={t}>
546
+ <line
547
+ x1={PORTFOLIO_LEFT}
548
+ y1={portfolioYFor(t)}
549
+ x2={PORTFOLIO_LEFT + PORTFOLIO_WIDTH}
550
+ y2={portfolioYFor(t)}
551
+ stroke="var(--color-separator)"
552
+ strokeDasharray="3 3"
553
+ opacity={0.5}
554
+ />
555
+ <ChartLabel x={PORTFOLIO_LEFT - 8} y={portfolioYFor(t) + 4} textAnchor="end">
556
+ ${(t / 1000).toFixed(0)}k
557
+ </ChartLabel>
558
+ </g>
559
+ ))}
560
+ {portfolioBars.map((d, i) => {
561
+ const h = Math.max(3, PORTFOLIO_BASE - portfolioYFor(d.value));
562
+ return (
563
+ <rect
564
+ key={d.label}
565
+ x={PORTFOLIO_LEFT + i * PORTFOLIO_STEP + 1}
566
+ y={PORTFOLIO_BASE - h}
567
+ width={Math.max(2, PORTFOLIO_STEP - 2)}
568
+ height={h}
569
+ rx={4}
570
+ fill="var(--dracula-green)"
571
+ />
572
+ );
573
+ })}
574
+ {xAxisTicks.map((m) => (
575
+ <ChartLabel
576
+ key={m}
577
+ x={PORTFOLIO_LEFT + (m / 12) * PORTFOLIO_WIDTH}
578
+ y={192}
579
+ textAnchor={m === 12 ? "end" : "middle"}
580
+ >
581
+ {xAxisLabels[m] ?? ""}
582
+ </ChartLabel>
583
+ ))}
584
+ <ChartLabel
585
+ x={PORTFOLIO_LEFT + PORTFOLIO_WIDTH - 4}
586
+ y={portfolioYFor(294200) - 8}
587
+ textAnchor="end"
588
+ fill="var(--color-text-highlight)"
589
+ >
590
+ $294k
591
+ </ChartLabel>
592
+ </svg>
593
+ </Card>
594
+ <Text type="supporting" color="secondary">
595
+ Weekly closes · trailing 12 months
596
+ </Text>
597
+ </VStack>
598
+ );
421
599
  }
422
600
 
423
601
  // ============= CARD COMPONENTS =============
424
602
 
425
603
  function MarketCard({
426
- name,
427
- ticker,
428
- price,
429
- change,
430
- positive,
431
- spark,
604
+ name,
605
+ ticker,
606
+ price,
607
+ change,
608
+ positive,
609
+ spark,
432
610
  }: {
433
- name: string;
434
- ticker: string;
435
- price: string;
436
- change: string;
437
- positive: boolean;
438
- spark: SparkPoint[];
611
+ name: string;
612
+ ticker: string;
613
+ price: string;
614
+ change: string;
615
+ positive: boolean;
616
+ spark: SparkPoint[];
439
617
  }) {
440
- return (
441
- <Card>
442
- <VStack gap={4}>
443
- <VStack gap={0}>
444
- <Heading level={3}>{name}</Heading>
445
- <Text type="supporting" color="secondary">
446
- {ticker}
447
- </Text>
448
- </VStack>
449
- <Sparkline
450
- data={spark}
451
- label={`${name} 24-hour trend`}
452
- positive={positive}
453
- mode="range"
454
- />
455
- <HStack gap={3} vAlign="center">
456
- <Text type="display-3" weight="semibold" hasTabularNumbers>
457
- {price}
458
- </Text>
459
- <MetricDelta value={change} positive={positive} />
460
- </HStack>
461
- </VStack>
462
- </Card>
463
- );
618
+ return (
619
+ <Card>
620
+ <VStack gap={4}>
621
+ <VStack gap={0}>
622
+ <Heading level={3}>{name}</Heading>
623
+ <Text type="supporting" color="secondary">
624
+ {ticker}
625
+ </Text>
626
+ </VStack>
627
+ <Sparkline data={spark} label={`${name} 24-hour trend`} positive={positive} mode="range" />
628
+ <HStack gap={3} vAlign="center">
629
+ <Text type="display-3" weight="semibold" hasTabularNumbers>
630
+ {price}
631
+ </Text>
632
+ <MetricDelta value={change} positive={positive} />
633
+ </HStack>
634
+ </VStack>
635
+ </Card>
636
+ );
464
637
  }
465
638
 
466
639
  // Signed figures render with an explicit sign, so the value reads as a move
467
640
  // even before the tone does.
468
- const formatSigned = (value: number, digits: number, suffix = '') =>
469
- `${value >= 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`;
641
+ const formatSigned = (value: number, digits: number, suffix = "") =>
642
+ `${value >= 0 ? "+" : ""}${value.toFixed(digits)}${suffix}`;
470
643
 
471
644
  const chgColumns: Array<{
472
- key: 'dailyPts' | 'dailyPct' | 'weekChg';
473
- header: string;
474
- width: PixelWidth;
475
- digits: number;
476
- suffix: string;
645
+ key: "dailyPts" | "dailyPct" | "weekChg";
646
+ header: string;
647
+ width: PixelWidth;
648
+ digits: number;
649
+ suffix: string;
477
650
  }> = [
478
- {key: 'dailyPts', header: 'Chg (pts)', width: pixel(104), digits: 2, suffix: ''},
479
- {key: 'dailyPct', header: 'Chg (%)', width: pixel(96), digits: 2, suffix: '%'},
480
- {key: 'weekChg', header: '52W (%)', width: pixel(96), digits: 1, suffix: '%'},
651
+ { key: "dailyPts", header: "Chg (pts)", width: pixel(104), digits: 2, suffix: "" },
652
+ { key: "dailyPct", header: "Chg (%)", width: pixel(96), digits: 2, suffix: "%" },
653
+ { key: "weekChg", header: "52W (%)", width: pixel(96), digits: 1, suffix: "%" },
481
654
  ];
482
655
  const trendingColumns: TableColumn<StockRow>[] = [
483
- {
484
- key: 'ticker',
485
- header: 'Ticker',
486
- width: pixel(88),
487
- renderCell: (row: StockRow) => (
488
- <Text type="body" weight="semibold" maxLines={1}>
489
- {row.ticker}
490
- </Text>
491
- ),
492
- },
493
- {
494
- key: 'price',
495
- header: 'Price',
496
- width: pixel(88),
497
- renderCell: (row: StockRow) => (
498
- <Text type="body" hasTabularNumbers maxLines={1}>
499
- {row.price}
500
- </Text>
501
- ),
502
- },
503
- ...chgColumns.map(column => ({
504
- key: column.key,
505
- header: column.header,
506
- width: column.width,
507
- renderCell: (row: StockRow) => {
508
- const value = row[column.key] as number;
509
- return (
510
- <MetricDelta value={formatSigned(value, column.digits, column.suffix)} positive={value >= 0} />
511
- );
512
- },
513
- })),
514
- {
515
- key: 'spark',
516
- header: '24h Trend',
517
- width: proportional(1, {minWidth: 96}),
518
- renderCell: (row: StockRow) => (
519
- <Sparkline
520
- data={row.spark}
521
- label={`${row.ticker} 24-hour trend`}
522
- positive={row.dailyPct >= 0}
523
- mode="range"
524
- isCompact
525
- />
526
- ),
527
- },
656
+ {
657
+ key: "ticker",
658
+ header: "Ticker",
659
+ width: pixel(88),
660
+ renderCell: (row: StockRow) => (
661
+ <Text type="body" weight="semibold" maxLines={1}>
662
+ {row.ticker}
663
+ </Text>
664
+ ),
665
+ },
666
+ {
667
+ key: "price",
668
+ header: "Price",
669
+ width: pixel(88),
670
+ renderCell: (row: StockRow) => (
671
+ <Text type="body" hasTabularNumbers maxLines={1}>
672
+ {row.price}
673
+ </Text>
674
+ ),
675
+ },
676
+ ...chgColumns.map((column) => ({
677
+ key: column.key,
678
+ header: column.header,
679
+ width: column.width,
680
+ renderCell: (row: StockRow) => {
681
+ const value = row[column.key] as number;
682
+ return (
683
+ <MetricDelta
684
+ value={formatSigned(value, column.digits, column.suffix)}
685
+ positive={value >= 0}
686
+ />
687
+ );
688
+ },
689
+ })),
690
+ {
691
+ key: "spark",
692
+ header: "24h Trend",
693
+ width: proportional(1, { minWidth: 96 }),
694
+ renderCell: (row: StockRow) => (
695
+ <Sparkline
696
+ data={row.spark}
697
+ label={`${row.ticker} 24-hour trend`}
698
+ positive={row.dailyPct >= 0}
699
+ mode="range"
700
+ isCompact
701
+ />
702
+ ),
703
+ },
528
704
  ];
529
705
 
530
-
531
706
  function AssetRow({
532
- ticker,
533
- name,
534
- value,
535
- change,
707
+ ticker,
708
+ name,
709
+ value,
710
+ change,
536
711
  }: {
537
- ticker: string;
538
- name: string;
539
- value: string;
540
- change: string;
712
+ ticker: string;
713
+ name: string;
714
+ value: string;
715
+ change: string;
541
716
  }) {
542
- return (
543
- <ListItem
544
- label={<Text weight="semibold">{ticker}</Text>}
545
- description={name}
546
- href="#/templates/dashboard-portfolio"
547
- startContent={<Avatar name={ticker} size="md" />}
548
- endContent={
549
- <VStack gap={0} hAlign="end">
550
- <Text type="body" hasTabularNumbers>
551
- {value}
552
- </Text>
553
- <MetricDelta value={change} positive={!change.startsWith('-')} />
554
- </VStack>
555
- }
556
- />
557
- );
717
+ return (
718
+ <ListItem
719
+ label={<Text weight="semibold">{ticker}</Text>}
720
+ description={name}
721
+ href="#/templates/dashboard-portfolio"
722
+ startContent={<Avatar name={ticker} size="md" />}
723
+ endContent={
724
+ <VStack gap={0} hAlign="end">
725
+ <Text type="body" hasTabularNumbers>
726
+ {value}
727
+ </Text>
728
+ <MetricDelta value={change} positive={!change.startsWith("-")} />
729
+ </VStack>
730
+ }
731
+ />
732
+ );
558
733
  }
559
734
 
560
735
  // ============= MAIN COMPONENT =============
561
736
 
562
737
  export default function DashboardPortfolio() {
563
- return (
564
- <Layout
565
- height="fill"
566
- content={
567
- <LayoutContent padding={6}>
568
- <VStack gap={6}>
569
- {/* Page header */}
570
- <HStack hAlign="between" vAlign="center">
571
- <Heading level={1}>The night vault</Heading>
572
- <Text type="body" color="secondary">
573
- 1 year
574
- </Text>
575
- </HStack>
738
+ return (
739
+ <Layout
740
+ height="fill"
741
+ content={
742
+ <LayoutContent padding={6}>
743
+ <VStack gap={6}>
744
+ {/* Page header */}
745
+ <HStack hAlign="between" vAlign="center">
746
+ <Heading level={1}>The night vault</Heading>
747
+ <Text type="body" color="secondary">
748
+ 1 year
749
+ </Text>
750
+ </HStack>
576
751
 
577
- {/* KPI metric cards */}
578
- <Grid columns={{minWidth: 280, repeat: 'fit'}} gap={4}>
579
- {metrics.map(m => (
580
- <MetricCard key={m.label} {...m} />
581
- ))}
582
- </Grid>
752
+ {/* KPI metric cards */}
753
+ <Grid columns={{ minWidth: 280, repeat: "fit" }} gap={4}>
754
+ {metrics.map((m) => (
755
+ <MetricCard key={m.label} {...m} />
756
+ ))}
757
+ </Grid>
583
758
 
584
- {/* Chart + Top holdings */}
585
- <Grid columns={{minWidth: 280, repeat: 'fit'}} gap={4}>
586
- <Card>
587
- <VStack gap={4}>
588
- <HStack hAlign="between" vAlign="center">
589
- <Heading level={2}>Vault value</Heading>
590
- <Link href="#/templates/dashboard-portfolio">
591
- View details
592
- </Link>
593
- </HStack>
594
- <PortfolioChart />
595
- </VStack>
596
- </Card>
597
- <VStack gap={4}>
598
- <HStack hAlign="between" vAlign="center">
599
- <Heading level={2}>Top holdings</Heading>
600
- <Link href="#/templates/dashboard-portfolio">View all</Link>
601
- </HStack>
602
- <List density="spacious">
603
- {topAssets.map(asset => (
604
- <AssetRow key={asset.ticker} {...asset} />
605
- ))}
606
- </List>
607
- </VStack>
608
- </Grid>
759
+ {/* Chart + Top holdings */}
760
+ <Grid columns={{ minWidth: 280, repeat: "fit" }} gap={4}>
761
+ <Card>
762
+ <VStack gap={4}>
763
+ <HStack hAlign="between" vAlign="center">
764
+ <Heading level={2}>Vault value</Heading>
765
+ <Link href="#/templates/dashboard-portfolio">View details</Link>
766
+ </HStack>
767
+ <PortfolioChart />
768
+ </VStack>
769
+ </Card>
770
+ <VStack gap={4}>
771
+ <HStack hAlign="between" vAlign="center">
772
+ <Heading level={2}>Top holdings</Heading>
773
+ <Link href="#/templates/dashboard-portfolio">View all</Link>
774
+ </HStack>
775
+ <List density="spacious">
776
+ {topAssets.map((asset) => (
777
+ <AssetRow key={asset.ticker} {...asset} />
778
+ ))}
779
+ </List>
780
+ </VStack>
781
+ </Grid>
609
782
 
610
- <Divider />
783
+ <Divider />
611
784
 
612
- {/* Market section */}
613
- <HStack hAlign="between" vAlign="start">
614
- <VStack gap={1}>
615
- <Heading level={2}>Market at midnight</Heading>
616
- <Text type="body" color="secondary">
617
- Past 24 hours under moonlight
618
- </Text>
619
- </VStack>
620
- <Button label="View more" variant="secondary" size="md" />
621
- </HStack>
785
+ {/* Market section */}
786
+ <HStack hAlign="between" vAlign="start">
787
+ <VStack gap={1}>
788
+ <Heading level={2}>Market at midnight</Heading>
789
+ <Text type="body" color="secondary">
790
+ Past 24 hours under moonlight
791
+ </Text>
792
+ </VStack>
793
+ <Button label="View more" variant="secondary" size="md" />
794
+ </HStack>
622
795
 
623
- {/* Market index cards */}
624
- <Grid columns={{minWidth: 280, repeat: 'fit'}} gap={4}>
625
- {marketIndices.map(m => (
626
- <MarketCard key={m.ticker} {...m} />
627
- ))}
628
- </Grid>
796
+ {/* Market index cards */}
797
+ <Grid columns={{ minWidth: 280, repeat: "fit" }} gap={4}>
798
+ {marketIndices.map((m) => (
799
+ <MarketCard key={m.ticker} {...m} />
800
+ ))}
801
+ </Grid>
629
802
 
630
- {/* Trending stocks table */}
631
- <TableCard
632
- title="Trending stocks"
633
- data={trendingStocks}
634
- columns={trendingColumns}
635
- />
636
- </VStack>
637
- </LayoutContent>
638
- }
639
- />
640
- );
803
+ {/* Trending stocks table */}
804
+ <TableCard title="Trending stocks" data={trendingStocks} columns={trendingColumns} />
805
+ </VStack>
806
+ </LayoutContent>
807
+ }
808
+ />
809
+ );
641
810
  }