Refresh app theme and update dependencies

This commit is contained in:
2026-03-08 11:09:36 -04:00
parent 2f7933f4a3
commit c3f3c3d5a9
19 changed files with 502 additions and 477 deletions

View File

@@ -89,11 +89,11 @@ const FINANCIAL_VALUE_SCALE_OPTIONS: Array<{ value: NumberScaleUnit; label: stri
{ value: 'billions', label: 'Billions (B)' }
];
const CHART_TEXT = '#e8fff8';
const CHART_MUTED = '#b4ced9';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)';
const CHART_TEXT = '#f3f5f7';
const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
function formatShortDate(value: string) {
return format(new Date(value), 'MMM yyyy');
@@ -633,9 +633,9 @@ function AnalysisPageContent() {
}}
labelStyle={{ color: CHART_TEXT }}
itemStyle={{ color: CHART_TEXT }}
cursor={{ stroke: 'rgba(104, 255, 213, 0.35)', strokeWidth: 1 }}
cursor={{ stroke: 'rgba(220, 226, 234, 0.28)', strokeWidth: 1 }}
/>
<Line type="monotone" dataKey="close" stroke="#68ffd5" strokeWidth={2} dot={false} />
<Line type="monotone" dataKey="close" stroke="#d9dee5" strokeWidth={2} dot={false} />
</LineChart>
</ResponsiveContainer>
</div>

View File

@@ -128,7 +128,7 @@ function SignInPageContent() {
</div>
{error ? <p className="text-sm text-[#ff9f9f]">{error}</p> : null}
{message ? <p className="text-sm text-[#9fffcf]">{message}</p> : null}
{message ? <p className="text-sm text-[color:var(--accent)]">{message}</p> : null}
<Button type="submit" className="w-full" disabled={busyAction !== null}>
{busyAction === 'password' ? 'Signing in...' : 'Sign in with password'}

View File

@@ -91,10 +91,10 @@ const DISPLAY_MODE_OPTIONS: FinancialControlOption[] = [
{ value: 'faithful', label: 'Filing-faithful' }
];
const CHART_MUTED = '#b4ced9';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)';
const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
function formatLongDate(value: string) {
const parsed = new Date(value);
@@ -714,7 +714,7 @@ function FinancialsPageContent() {
type="monotone"
dataKey={series.key}
name={series.label}
stroke={['#68ffd5', '#5fd3ff', '#ffd08a', '#ff8a8a'][index % 4]}
stroke={['#d9dee5', '#c1c8d1', '#aab2bc', '#8f98a3'][index % 4]}
strokeWidth={2}
dot={false}
/>

View File

@@ -3,20 +3,21 @@
:root {
--font-display: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-mono: "Menlo", "SFMono-Regular", "Consolas", "Liberation Mono", monospace;
--bg-0: #05080d;
--bg-1: #08121a;
--bg-2: #0b1f28;
--panel: rgba(6, 17, 24, 0.8);
--panel-soft: rgba(7, 22, 31, 0.62);
--panel-bright: rgba(10, 33, 45, 0.9);
--line-weak: rgba(126, 217, 255, 0.22);
--line-strong: rgba(123, 255, 217, 0.75);
--accent: #68ffd5;
--accent-strong: #8cffeb;
--danger: #ff7070;
--danger-soft: rgba(122, 33, 33, 0.44);
--terminal-bright: #e8fff8;
--terminal-muted: #94b9c5;
--bg-0: #121417;
--bg-1: #181b20;
--bg-2: #21252b;
--panel: rgba(28, 31, 36, 0.84);
--panel-soft: rgba(36, 39, 45, 0.72);
--panel-bright: rgba(49, 53, 60, 0.94);
--line-weak: rgba(196, 202, 211, 0.18);
--line-strong: rgba(220, 226, 234, 0.34);
--accent: #d9dee5;
--accent-strong: #f4f7fb;
--danger: #ff8e8e;
--danger-soft: rgba(111, 46, 46, 0.42);
--terminal-bright: #f3f5f7;
--terminal-muted: #a1a9b3;
--focus-ring: rgba(229, 231, 235, 0.14);
}
* {
@@ -34,13 +35,13 @@ html {
}
[data-sonner-toaster] {
--normal-bg: rgba(7, 22, 31, 0.96);
--normal-text: #e8fff8;
--normal-border: rgba(123, 255, 217, 0.45);
--success-bg: rgba(8, 58, 42, 0.96);
--success-text: #d0ffe9;
--success-border: rgba(104, 255, 213, 0.7);
--error-bg: rgba(67, 22, 22, 0.96);
--normal-bg: rgba(31, 34, 39, 0.96);
--normal-text: #f3f5f7;
--normal-border: rgba(220, 226, 234, 0.24);
--success-bg: rgba(34, 44, 39, 0.96);
--success-text: #e4efe7;
--success-border: rgba(163, 191, 171, 0.55);
--error-bg: rgba(67, 27, 27, 0.96);
--error-text: #ffd6d6;
--error-border: rgba(255, 112, 112, 0.8);
}
@@ -56,8 +57,8 @@ body {
font-family: var(--font-display), sans-serif;
color: var(--terminal-bright);
background:
radial-gradient(circle at 18% -10%, rgba(126, 217, 255, 0.25), transparent 35%),
radial-gradient(circle at 84% 0%, rgba(104, 255, 213, 0.2), transparent 30%),
radial-gradient(circle at 18% -10%, rgba(170, 178, 188, 0.16), transparent 35%),
radial-gradient(circle at 84% 0%, rgba(121, 128, 138, 0.14), transparent 30%),
linear-gradient(140deg, var(--bg-0), var(--bg-1) 50%, var(--bg-2));
}
@@ -72,8 +73,8 @@ body {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(126, 217, 255, 0.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(126, 217, 255, 0.07) 1px, transparent 1px);
linear-gradient(rgba(204, 210, 218, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(204, 210, 218, 0.05) 1px, transparent 1px);
background-size: 34px 34px;
mask-image: radial-gradient(ellipse at center, black 20%, transparent 75%);
pointer-events: none;
@@ -83,8 +84,8 @@ body {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.3;
background-image: radial-gradient(rgba(160, 255, 227, 0.15) 0.7px, transparent 0.7px);
opacity: 0.24;
background-image: radial-gradient(rgba(220, 226, 234, 0.1) 0.7px, transparent 0.7px);
background-size: 4px 4px;
}
@@ -127,7 +128,7 @@ textarea {
}
.data-table tbody tr:hover {
background-color: rgba(17, 47, 61, 0.45);
background-color: rgba(63, 68, 76, 0.32);
}
@media (prefers-reduced-motion: no-preference) {
@@ -157,8 +158,8 @@ textarea {
@media (max-width: 640px) {
body {
background:
radial-gradient(circle at 24% -4%, rgba(126, 217, 255, 0.2), transparent 36%),
radial-gradient(circle at 82% 2%, rgba(104, 255, 213, 0.16), transparent 30%),
radial-gradient(circle at 24% -4%, rgba(170, 178, 188, 0.14), transparent 36%),
radial-gradient(circle at 82% 2%, rgba(121, 128, 138, 0.12), transparent 30%),
linear-gradient(155deg, var(--bg-0), var(--bg-1) 54%, var(--bg-2));
}

View File

@@ -49,11 +49,11 @@ import type {
import { companyFinancialStatementsQueryOptions } from '@/lib/query/options';
import { cn } from '@/lib/utils';
const CHART_COLORS = ['#68ffd5', '#5fd3ff', '#ffd08a', '#ff8a8a', '#c39bff'] as const;
const CHART_MUTED = '#b4ced9';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)';
const CHART_COLORS = ['#d9dee5', '#c7cdd5', '#b5bcc5', '#a4acb6', '#939ca7'] as const;
const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
type TooltipEntry = {
dataKey?: string | number;
@@ -215,7 +215,7 @@ function ComparisonTooltip(props: {
</p>
<div className="mt-3 space-y-2">
{entries.map((entry) => (
<div key={entry.ticker} className="rounded-lg border border-[color:var(--line-weak)] bg-[color:rgba(4,16,24,0.72)] px-2 py-2">
<div key={entry.ticker} className="rounded-lg border border-[color:var(--line-weak)] bg-[color:rgba(45,49,55,0.72)] px-2 py-2">
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<span className="size-2 rounded-full" style={{ backgroundColor: entry.color }} aria-hidden="true" />
@@ -495,10 +495,10 @@ function GraphingPageContent() {
aria-label="Metric selector"
value={graphState.metric}
onChange={(event) => replaceGraphState({ metric: event.target.value })}
className="w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2.5 text-sm text-[color:var(--terminal-bright)] outline-none transition focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_rgba(0,255,180,0.14)]"
className="w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2.5 text-sm text-[color:var(--terminal-bright)] outline-none transition focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]"
>
{metricOptions.map((option) => (
<option key={option.key} value={option.key} className="bg-[#07161f]">
<option key={option.key} value={option.key} className="bg-[#1f2227]">
{option.label}
</option>
))}

View File

@@ -11,7 +11,7 @@ export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
viewportFit: 'cover',
themeColor: '#05080d'
themeColor: '#121417'
};
export default function RootLayout({ children }: { children: React.ReactNode }) {

View File

@@ -35,12 +35,12 @@ type FormState = {
currentPrice: string;
};
const CHART_COLORS = ['#6effd8', '#5fd3ff', '#66ffa1', '#8dbbff', '#f4f88f', '#ff9c9c'];
const CHART_TEXT = '#e8fff8';
const CHART_MUTED = '#b4ced9';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)';
const CHART_COLORS = ['#d9dee5', '#c7cdd5', '#b5bcc5', '#a4acb6', '#939ca7', '#828b97'];
const CHART_TEXT = '#f3f5f7';
const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
const EMPTY_SUMMARY: PortfolioSummary = {
positions: 0,
@@ -283,9 +283,9 @@ export default function PortfolioPage() {
}}
labelStyle={{ color: CHART_TEXT }}
itemStyle={{ color: CHART_TEXT }}
cursor={{ fill: 'rgba(104, 255, 213, 0.08)' }}
cursor={{ fill: 'rgba(220, 226, 234, 0.08)' }}
/>
<Bar dataKey="value" fill="#68ffd5" radius={[4, 4, 0, 0]} />
<Bar dataKey="value" fill="#d9dee5" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>

View File

@@ -444,7 +444,7 @@ function ResearchPageContent() {
{ticker && workspace ? (
<>
<Panel className="overflow-hidden border-[color:var(--line-strong)] bg-[linear-gradient(135deg,rgba(6,16,20,0.96),rgba(6,16,20,0.82)_45%,rgba(8,28,30,0.9))]">
<Panel className="overflow-hidden border-[color:var(--line-strong)] bg-[linear-gradient(135deg,rgba(29,31,36,0.97),rgba(34,37,42,0.9)_45%,rgba(42,46,52,0.94))]">
<div className="grid gap-5 lg:grid-cols-[1.6fr_1fr]">
<div>
<p className="text-xs uppercase tracking-[0.22em] text-[color:var(--accent)]">Buy-Side Research Workspace</p>
@@ -456,14 +456,14 @@ function ResearchPageContent() {
</p>
<div className="mt-4 flex flex-wrap gap-2">
{(workspace.coverage?.tags ?? []).map((tag) => (
<span key={tag} className="rounded-full border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.02)] px-3 py-1 text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">
<span key={tag} className="rounded-full border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] px-3 py-1 text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">
{tag}
</span>
))}
</div>
</div>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] p-4">
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.04)] p-4">
<p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">Memo posture</p>
<p className="mt-2 text-lg font-semibold text-[color:var(--terminal-bright)]">
{workspace.memo?.rating ? workspace.memo.rating.replace('_', ' ') : 'Unrated'}
@@ -472,7 +472,7 @@ function ResearchPageContent() {
Conviction: {workspace.memo?.conviction ?? 'unset'}
</p>
</div>
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] p-4">
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.04)] p-4">
<p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">Research depth</p>
<p className="mt-2 text-lg font-semibold text-[color:var(--terminal-bright)]">{workspace.library.length} artifacts</p>
<p className="mt-1 text-sm text-[color:var(--terminal-muted)]">{memoEvidenceCount} evidence links in the packet</p>
@@ -542,7 +542,7 @@ function ResearchPageContent() {
<input type="checkbox" checked={linkedOnly} onChange={(event) => setLinkedOnly(event.target.checked)} />
Show memo-linked evidence only
</label>
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.02)] p-4">
<div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] p-4">
<div className="flex items-center gap-2 text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">
<ShieldCheck className="size-4 text-[color:var(--accent)]" />
Access Model
@@ -566,7 +566,7 @@ function ResearchPageContent() {
value={noteForm.bodyMarkdown}
onChange={(event) => setNoteForm((current) => ({ ...current, bodyMarkdown: event.target.value }))}
placeholder="Write the actual research note, variant view, or diligence conclusion..."
className="min-h-[160px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_rgba(0,255,180,0.14)]"
className="min-h-[160px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]"
/>
<Input aria-label="Research note tags" value={noteForm.tags} onChange={(event) => setNoteForm((current) => ({ ...current, tags: event.target.value }))} placeholder="Tags, comma-separated" />
<div className="flex flex-wrap gap-2">
@@ -746,7 +746,7 @@ function ResearchPageContent() {
aria-label={`Memo ${section.label}`}
value={memoForm[field]}
onChange={(event) => setMemoForm((current) => ({ ...current, [field]: event.target.value }))}
className="min-h-[108px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_rgba(0,255,180,0.14)]"
className="min-h-[108px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]"
placeholder={`Write ${section.label.toLowerCase()}...`}
/>
</div>

View File

@@ -58,7 +58,7 @@ const EMPTY_FORM: FormState = {
tags: ''
};
const SELECT_CLASS_NAME = 'min-h-11 w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_rgba(0,255,180,0.14)]';
const SELECT_CLASS_NAME = 'min-h-11 w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]';
function parseTagsInput(input: string) {
const unique = new Set<string>();