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)' } { value: 'billions', label: 'Billions (B)' }
]; ];
const CHART_TEXT = '#e8fff8'; const CHART_TEXT = '#f3f5f7';
const CHART_MUTED = '#b4ced9'; const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)'; const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)'; const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)'; const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
function formatShortDate(value: string) { function formatShortDate(value: string) {
return format(new Date(value), 'MMM yyyy'); return format(new Date(value), 'MMM yyyy');
@@ -633,9 +633,9 @@ function AnalysisPageContent() {
}} }}
labelStyle={{ color: CHART_TEXT }} labelStyle={{ color: CHART_TEXT }}
itemStyle={{ 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> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>
</div> </div>

View File

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

View File

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

View File

@@ -3,20 +3,21 @@
:root { :root {
--font-display: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif; --font-display: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-mono: "Menlo", "SFMono-Regular", "Consolas", "Liberation Mono", monospace; --font-mono: "Menlo", "SFMono-Regular", "Consolas", "Liberation Mono", monospace;
--bg-0: #05080d; --bg-0: #121417;
--bg-1: #08121a; --bg-1: #181b20;
--bg-2: #0b1f28; --bg-2: #21252b;
--panel: rgba(6, 17, 24, 0.8); --panel: rgba(28, 31, 36, 0.84);
--panel-soft: rgba(7, 22, 31, 0.62); --panel-soft: rgba(36, 39, 45, 0.72);
--panel-bright: rgba(10, 33, 45, 0.9); --panel-bright: rgba(49, 53, 60, 0.94);
--line-weak: rgba(126, 217, 255, 0.22); --line-weak: rgba(196, 202, 211, 0.18);
--line-strong: rgba(123, 255, 217, 0.75); --line-strong: rgba(220, 226, 234, 0.34);
--accent: #68ffd5; --accent: #d9dee5;
--accent-strong: #8cffeb; --accent-strong: #f4f7fb;
--danger: #ff7070; --danger: #ff8e8e;
--danger-soft: rgba(122, 33, 33, 0.44); --danger-soft: rgba(111, 46, 46, 0.42);
--terminal-bright: #e8fff8; --terminal-bright: #f3f5f7;
--terminal-muted: #94b9c5; --terminal-muted: #a1a9b3;
--focus-ring: rgba(229, 231, 235, 0.14);
} }
* { * {
@@ -34,13 +35,13 @@ html {
} }
[data-sonner-toaster] { [data-sonner-toaster] {
--normal-bg: rgba(7, 22, 31, 0.96); --normal-bg: rgba(31, 34, 39, 0.96);
--normal-text: #e8fff8; --normal-text: #f3f5f7;
--normal-border: rgba(123, 255, 217, 0.45); --normal-border: rgba(220, 226, 234, 0.24);
--success-bg: rgba(8, 58, 42, 0.96); --success-bg: rgba(34, 44, 39, 0.96);
--success-text: #d0ffe9; --success-text: #e4efe7;
--success-border: rgba(104, 255, 213, 0.7); --success-border: rgba(163, 191, 171, 0.55);
--error-bg: rgba(67, 22, 22, 0.96); --error-bg: rgba(67, 27, 27, 0.96);
--error-text: #ffd6d6; --error-text: #ffd6d6;
--error-border: rgba(255, 112, 112, 0.8); --error-border: rgba(255, 112, 112, 0.8);
} }
@@ -56,8 +57,8 @@ body {
font-family: var(--font-display), sans-serif; font-family: var(--font-display), sans-serif;
color: var(--terminal-bright); color: var(--terminal-bright);
background: background:
radial-gradient(circle at 18% -10%, rgba(126, 217, 255, 0.25), transparent 35%), radial-gradient(circle at 18% -10%, rgba(170, 178, 188, 0.16), transparent 35%),
radial-gradient(circle at 84% 0%, rgba(104, 255, 213, 0.2), transparent 30%), 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)); linear-gradient(140deg, var(--bg-0), var(--bg-1) 50%, var(--bg-2));
} }
@@ -72,8 +73,8 @@ body {
position: absolute; position: absolute;
inset: 0; inset: 0;
background-image: background-image:
linear-gradient(rgba(126, 217, 255, 0.08) 1px, transparent 1px), linear-gradient(rgba(204, 210, 218, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(126, 217, 255, 0.07) 1px, transparent 1px); linear-gradient(90deg, rgba(204, 210, 218, 0.05) 1px, transparent 1px);
background-size: 34px 34px; background-size: 34px 34px;
mask-image: radial-gradient(ellipse at center, black 20%, transparent 75%); mask-image: radial-gradient(ellipse at center, black 20%, transparent 75%);
pointer-events: none; pointer-events: none;
@@ -83,8 +84,8 @@ body {
position: absolute; position: absolute;
inset: 0; inset: 0;
pointer-events: none; pointer-events: none;
opacity: 0.3; opacity: 0.24;
background-image: radial-gradient(rgba(160, 255, 227, 0.15) 0.7px, transparent 0.7px); background-image: radial-gradient(rgba(220, 226, 234, 0.1) 0.7px, transparent 0.7px);
background-size: 4px 4px; background-size: 4px 4px;
} }
@@ -127,7 +128,7 @@ textarea {
} }
.data-table tbody tr:hover { .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) { @media (prefers-reduced-motion: no-preference) {
@@ -157,8 +158,8 @@ textarea {
@media (max-width: 640px) { @media (max-width: 640px) {
body { body {
background: background:
radial-gradient(circle at 24% -4%, rgba(126, 217, 255, 0.2), transparent 36%), radial-gradient(circle at 24% -4%, rgba(170, 178, 188, 0.14), transparent 36%),
radial-gradient(circle at 82% 2%, rgba(104, 255, 213, 0.16), transparent 30%), 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)); 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 { companyFinancialStatementsQueryOptions } from '@/lib/query/options';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
const CHART_COLORS = ['#68ffd5', '#5fd3ff', '#ffd08a', '#ff8a8a', '#c39bff'] as const; const CHART_COLORS = ['#d9dee5', '#c7cdd5', '#b5bcc5', '#a4acb6', '#939ca7'] as const;
const CHART_MUTED = '#b4ced9'; const CHART_MUTED = '#a1a9b3';
const CHART_GRID = 'rgba(126, 217, 255, 0.24)'; const CHART_GRID = 'rgba(196, 202, 211, 0.18)';
const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)'; const CHART_TOOLTIP_BG = 'rgba(31, 34, 39, 0.96)';
const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)'; const CHART_TOOLTIP_BORDER = 'rgba(220, 226, 234, 0.24)';
type TooltipEntry = { type TooltipEntry = {
dataKey?: string | number; dataKey?: string | number;
@@ -215,7 +215,7 @@ function ComparisonTooltip(props: {
</p> </p>
<div className="mt-3 space-y-2"> <div className="mt-3 space-y-2">
{entries.map((entry) => ( {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 justify-between gap-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="size-2 rounded-full" style={{ backgroundColor: entry.color }} aria-hidden="true" /> <span className="size-2 rounded-full" style={{ backgroundColor: entry.color }} aria-hidden="true" />
@@ -495,10 +495,10 @@ function GraphingPageContent() {
aria-label="Metric selector" aria-label="Metric selector"
value={graphState.metric} value={graphState.metric}
onChange={(event) => replaceGraphState({ metric: event.target.value })} 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) => ( {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.label}
</option> </option>
))} ))}

View File

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

View File

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

View File

@@ -444,7 +444,7 @@ function ResearchPageContent() {
{ticker && workspace ? ( {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 className="grid gap-5 lg:grid-cols-[1.6fr_1fr]">
<div> <div>
<p className="text-xs uppercase tracking-[0.22em] text-[color:var(--accent)]">Buy-Side Research Workspace</p> <p className="text-xs uppercase tracking-[0.22em] text-[color:var(--accent)]">Buy-Side Research Workspace</p>
@@ -456,14 +456,14 @@ function ResearchPageContent() {
</p> </p>
<div className="mt-4 flex flex-wrap gap-2"> <div className="mt-4 flex flex-wrap gap-2">
{(workspace.coverage?.tags ?? []).map((tag) => ( {(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} {tag}
</span> </span>
))} ))}
</div> </div>
</div> </div>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-1"> <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="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)]"> <p className="mt-2 text-lg font-semibold text-[color:var(--terminal-bright)]">
{workspace.memo?.rating ? workspace.memo.rating.replace('_', ' ') : 'Unrated'} {workspace.memo?.rating ? workspace.memo.rating.replace('_', ' ') : 'Unrated'}
@@ -472,7 +472,7 @@ function ResearchPageContent() {
Conviction: {workspace.memo?.conviction ?? 'unset'} Conviction: {workspace.memo?.conviction ?? 'unset'}
</p> </p>
</div> </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="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-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> <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)} /> <input type="checkbox" checked={linkedOnly} onChange={(event) => setLinkedOnly(event.target.checked)} />
Show memo-linked evidence only Show memo-linked evidence only
</label> </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)]"> <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)]" /> <ShieldCheck className="size-4 text-[color:var(--accent)]" />
Access Model Access Model
@@ -566,7 +566,7 @@ function ResearchPageContent() {
value={noteForm.bodyMarkdown} value={noteForm.bodyMarkdown}
onChange={(event) => setNoteForm((current) => ({ ...current, bodyMarkdown: event.target.value }))} onChange={(event) => setNoteForm((current) => ({ ...current, bodyMarkdown: event.target.value }))}
placeholder="Write the actual research note, variant view, or diligence conclusion..." 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" /> <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"> <div className="flex flex-wrap gap-2">
@@ -746,7 +746,7 @@ function ResearchPageContent() {
aria-label={`Memo ${section.label}`} aria-label={`Memo ${section.label}`}
value={memoForm[field]} value={memoForm[field]}
onChange={(event) => setMemoForm((current) => ({ ...current, [field]: event.target.value }))} 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()}...`} placeholder={`Write ${section.label.toLowerCase()}...`}
/> />
</div> </div>

View File

@@ -58,7 +58,7 @@ const EMPTY_FORM: FormState = {
tags: '' 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) { function parseTagsInput(input: string) {
const unique = new Set<string>(); const unique = new Set<string>();

792
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -154,10 +154,10 @@ export function TaskDetailModal({ isOpen, taskId, onClose }: TaskDetailModalProp
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className={item.state === 'active' <span className={item.state === 'active'
? 'text-[11px] uppercase tracking-[0.12em] text-[#9fffcf]' ? 'text-[11px] uppercase tracking-[0.12em] text-[color:var(--accent)]'
: item.state === 'completed' : item.state === 'completed'
? 'text-[11px] uppercase tracking-[0.12em] text-[color:var(--terminal-muted)]' ? 'text-[11px] uppercase tracking-[0.12em] text-[color:var(--terminal-muted)]'
: 'text-[11px] uppercase tracking-[0.12em] text-[#6f8791]'} : 'text-[11px] uppercase tracking-[0.12em] text-[#7f8994]'}
> >
{item.state} {item.state}
</span> </span>

View File

@@ -50,7 +50,7 @@ export function TaskNotificationsTrigger({
> >
{unreadCount > 0 ? <BellRing className="size-4" /> : <Bell className="size-4" />} {unreadCount > 0 ? <BellRing className="size-4" /> : <Bell className="size-4" />}
{unreadCount > 0 ? ( {unreadCount > 0 ? (
<span className="absolute -right-1.5 -top-1.5 inline-flex min-w-[1.15rem] items-center justify-center rounded-full bg-[color:var(--accent)] px-1 text-[10px] font-semibold text-[#00241d]"> <span className="absolute -right-1.5 -top-1.5 inline-flex min-w-[1.15rem] items-center justify-center rounded-full bg-[color:var(--accent)] px-1 text-[10px] font-semibold text-[#16181c]">
{unreadCount > 99 ? '99+' : unreadCount} {unreadCount > 99 ? '99+' : unreadCount}
</span> </span>
) : null} ) : null}

View File

@@ -448,7 +448,7 @@ export function AppShell({ title, subtitle, actions, activeTicker, breadcrumbs,
<div className="noise-layer" aria-hidden="true" /> <div className="noise-layer" aria-hidden="true" />
<div className="relative z-10 mx-auto flex min-h-screen w-full max-w-[1300px] gap-4 px-3 pb-10 pt-4 sm:px-4 sm:pb-12 sm:pt-6 md:px-8 lg:gap-6"> <div className="relative z-10 mx-auto flex min-h-screen w-full max-w-[1300px] gap-4 px-3 pb-10 pt-4 sm:px-4 sm:pb-12 sm:pt-6 md:px-8 lg:gap-6">
<aside className="hidden w-72 shrink-0 flex-col gap-6 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] p-5 shadow-[0_0_0_1px_rgba(0,255,180,0.06),0_20px_60px_rgba(1,4,10,0.55)] lg:flex"> <aside className="hidden w-72 shrink-0 flex-col gap-6 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] p-5 shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_20px_60px_rgba(0,0,0,0.42)] lg:flex">
<div> <div>
<p className="terminal-caption text-xs uppercase tracking-[0.25em] text-[color:var(--terminal-muted)]">Fiscal Clone</p> <p className="terminal-caption text-xs uppercase tracking-[0.25em] text-[color:var(--terminal-muted)]">Fiscal Clone</p>
<h1 className="mt-2 text-2xl font-semibold text-[color:var(--terminal-bright)]">Neon Desk</h1> <h1 className="mt-2 text-2xl font-semibold text-[color:var(--terminal-bright)]">Neon Desk</h1>
@@ -474,7 +474,7 @@ export function AppShell({ title, subtitle, actions, activeTicker, breadcrumbs,
className={cn( className={cn(
'flex items-center gap-3 rounded-xl border px-3 py-2 text-sm transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--line-strong)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent', 'flex items-center gap-3 rounded-xl border px-3 py-2 text-sm transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--line-strong)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent',
item.active item.active
? 'border-[color:var(--line-strong)] bg-[color:var(--panel-bright)] text-[color:var(--terminal-bright)] shadow-[0_0_18px_rgba(0,255,180,0.16)]' ? 'border-[color:var(--line-strong)] bg-[color:var(--panel-bright)] text-[color:var(--terminal-bright)] shadow-[0_0_18px_rgba(255,255,255,0.06)]'
: 'border-transparent text-[color:var(--terminal-muted)] hover:border-[color:var(--line-weak)] hover:bg-[color:var(--panel-soft)] hover:text-[color:var(--terminal-bright)]' : 'border-transparent text-[color:var(--terminal-muted)] hover:border-[color:var(--line-weak)] hover:bg-[color:var(--panel-soft)] hover:text-[color:var(--terminal-bright)]'
)} )}
> >
@@ -502,7 +502,7 @@ export function AppShell({ title, subtitle, actions, activeTicker, breadcrumbs,
</aside> </aside>
<div className="min-w-0 flex-1 pb-24 lg:pb-0"> <div className="min-w-0 flex-1 pb-24 lg:pb-0">
<header className="relative mb-4 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] px-4 py-4 pr-16 shadow-[0_0_0_1px_rgba(0,255,180,0.05),0_14px_40px_rgba(1,4,10,0.5)] sm:px-6 sm:py-5 sm:pr-20"> <header className="relative mb-4 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] px-4 py-4 pr-16 shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_14px_40px_rgba(0,0,0,0.4)] sm:px-6 sm:py-5 sm:pr-20">
<div className="absolute right-4 top-4 z-10 sm:right-5 sm:top-5"> <div className="absolute right-4 top-4 z-10 sm:right-5 sm:top-5">
<TaskNotificationsTrigger <TaskNotificationsTrigger
unreadCount={notifications.unreadCount} unreadCount={notifications.unreadCount}
@@ -573,7 +573,7 @@ export function AppShell({ title, subtitle, actions, activeTicker, breadcrumbs,
</div> </div>
<nav <nav
className="fixed inset-x-0 bottom-0 z-40 border-t border-[color:var(--line-weak)] bg-[color:rgba(5,14,22,0.96)] px-2 py-2 backdrop-blur lg:hidden" className="fixed inset-x-0 bottom-0 z-40 border-t border-[color:var(--line-weak)] bg-[color:rgba(24,27,32,0.96)] px-2 py-2 backdrop-blur lg:hidden"
aria-label="Mobile primary" aria-label="Mobile primary"
> >
<div className="mx-auto flex w-full max-w-[1300px] items-center justify-between gap-1 px-1" style={{ paddingBottom: 'calc(env(safe-area-inset-bottom) * 0.5)' }}> <div className="mx-auto flex w-full max-w-[1300px] items-center justify-between gap-1 px-1" style={{ paddingBottom: 'calc(env(safe-area-inset-bottom) * 0.5)' }}>

View File

@@ -7,7 +7,7 @@ type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
}; };
const variantMap: Record<ButtonVariant, string> = { const variantMap: Record<ButtonVariant, string> = {
primary: 'border-[color:var(--line-strong)] bg-[color:var(--accent)] text-[#001515] hover:bg-[color:var(--accent-strong)]', primary: 'border-[color:var(--line-strong)] bg-[color:var(--accent)] text-[#16181c] hover:bg-[color:var(--accent-strong)]',
secondary: 'border-[color:var(--line-weak)] bg-[color:var(--panel-bright)] text-[color:var(--terminal-bright)] hover:border-[color:var(--line-strong)] hover:bg-[color:var(--panel)]', secondary: 'border-[color:var(--line-weak)] bg-[color:var(--panel-bright)] text-[color:var(--terminal-bright)] hover:border-[color:var(--line-strong)] hover:bg-[color:var(--panel)]',
ghost: 'border-[color:var(--line-weak)] bg-transparent text-[color:var(--terminal-bright)] hover:border-[color:var(--line-strong)] hover:bg-[color:var(--panel-soft)]', ghost: 'border-[color:var(--line-weak)] bg-transparent text-[color:var(--terminal-bright)] hover:border-[color:var(--line-strong)] hover:bg-[color:var(--panel-soft)]',
danger: 'border-[color:var(--danger)] bg-[color:var(--danger-soft)] text-[#ffc9c9] hover:bg-[color:var(--danger)] hover:text-[#1e0d0d]' danger: 'border-[color:var(--danger)] bg-[color:var(--danger-soft)] text-[#ffc9c9] hover:bg-[color:var(--danger)] hover:text-[#1e0d0d]'

View File

@@ -6,7 +6,7 @@ export function Input({ className, ...props }: InputProps) {
return ( return (
<input <input
className={cn( className={cn(
'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 placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_rgba(0,255,180,0.14)]', '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 placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]',
className className
)} )}
{...props} {...props}

View File

@@ -12,7 +12,7 @@ export function Panel({ title, subtitle, actions, children, className }: PanelPr
return ( return (
<section <section
className={cn( className={cn(
'min-w-0 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] p-4 shadow-[0_0_0_1px_rgba(0,255,180,0.03),0_12px_30px_rgba(1,4,10,0.45)] sm:p-5', 'min-w-0 rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] p-4 shadow-[0_0_0_1px_rgba(255,255,255,0.03),0_12px_30px_rgba(0,0,0,0.38)] sm:p-5',
className className
)} )}
> >

View File

@@ -6,9 +6,9 @@ type StatusPillProps = {
}; };
const classes: Record<TaskStatus, string> = { const classes: Record<TaskStatus, string> = {
queued: 'border-[#33587a] bg-[#0a2c3f] text-[#7ecaf5]', queued: 'border-[#525861] bg-[#262a30] text-[#c0c7d0]',
running: 'border-[#4f7a33] bg-[#0f311d] text-[#99f085]', running: 'border-[#686e77] bg-[#2d3137] text-[#d8dde4]',
completed: 'border-[#1a7a53] bg-[#083a2a] text-[#8bf7cb]', completed: 'border-[#7b828c] bg-[#353a42] text-[#eef2f6]',
failed: 'border-[#8f3d3d] bg-[#431616] text-[#ff9c9c]' failed: 'border-[#8f3d3d] bg-[#431616] text-[#ff9c9c]'
}; };

2
next-env.d.ts vendored
View File

@@ -1,6 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts"; import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@@ -30,10 +30,10 @@
"@libsql/client": "^0.17.0", "@libsql/client": "^0.17.0",
"@tailwindcss/postcss": "^4.2.1", "@tailwindcss/postcss": "^4.2.1",
"@tanstack/react-query": "^5.90.21", "@tanstack/react-query": "^5.90.21",
"@workflow/world-postgres": "^4.1.0-beta.34", "@workflow/world-postgres": "^4.1.0-beta.42",
"ai": "^6.0.104", "ai": "^6.0.116",
"better-auth": "^1.4.19", "better-auth": "^1.5.4",
"cheerio": "^1.1.2", "cheerio": "^1.2.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"drizzle-orm": "^0.41.0", "drizzle-orm": "^0.41.0",
@@ -42,21 +42,21 @@
"next": "^16.1.6", "next": "^16.1.6",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"recharts": "^3.7.0", "recharts": "^3.8.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"sqlite-vec": "^0.1.7-alpha.2", "sqlite-vec": "^0.1.7-alpha.2",
"workflow": "^4.1.0-beta.60", "workflow": "^4.1.0-beta.63",
"zhipu-ai-provider": "^0.2.2" "zhipu-ai-provider": "^0.2.2"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.56.1", "@playwright/test": "^1.58.2",
"@types/node": "^25.3.0", "@types/node": "^25.3.5",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.24", "autoprefixer": "^10.4.27",
"bun-types": "^1.3.10", "bun-types": "^1.3.10",
"drizzle-kit": "^0.31.4", "drizzle-kit": "^0.31.9",
"postcss": "^8.5.6", "postcss": "^8.5.8",
"tailwindcss": "^4.2.1", "tailwindcss": "^4.2.1",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }