feat: nova dashboard improvements with theme (#905)

![image.png](https://app.graphite.com/user-attachments/assets/234115bd-9b60-4d3d-b17c-c27a79dcb2f2.png)
This commit is contained in:
MaheshtheDev 2026-05-06 22:29:21 +00:00
parent 7d762806a9
commit 8c8a6e130f
9 changed files with 107 additions and 93 deletions

View file

@ -56,6 +56,7 @@ import {
qParam,
docParam,
fullscreenParam,
threadParam,
type IntegrationParamValue,
} from "@/lib/search-params"
import { getChatSpaceDisplayLabel } from "@/lib/chat-space-label"
@ -152,6 +153,7 @@ export default function NewPage() {
"fullscreen",
fullscreenParam,
)
const [, setThreadIdUrl] = useQueryState("thread", threadParam)
// Ephemeral local state (not worth URL-encoding)
const [fullscreenInitialContent, setFullscreenInitialContent] = useState("")
@ -168,6 +170,10 @@ export default function NewPage() {
if (!docId) setSelectedDocument(null)
}, [docId])
useEffect(() => {
if (viewMode === "dashboard") void setThreadIdUrl(null)
}, [viewMode, setThreadIdUrl])
// Resolve document from cache when loading with ?doc=<id> (deep link / refresh)
useEffect(() => {
if (!docId || selectedDocument) return
@ -512,7 +518,7 @@ export default function NewPage() {
const isGraphMode = viewMode === "graph" && !isMobile
const isMemoriesDesktop = viewMode === "list" && !isMobile
const isHomeDesktop = viewMode === "dashboard" && !isMobile
const showNovaBackdrop = isGraphMode || isMemoriesDesktop
const showNovaBackdrop = isGraphMode || isMemoriesDesktop || isHomeDesktop
const isDashboardShell =
viewMode === "dashboard" || (viewMode === "graph" && isMobile)
@ -520,31 +526,23 @@ export default function NewPage() {
<HotkeysProvider>
<div
className={cn(
"relative flex min-h-dvh flex-col bg-black",
"relative flex min-h-dvh flex-col bg-[#05080D]",
isGraphMode && "h-dvh overflow-hidden",
)}
>
{showNovaBackdrop && (
<>
<AnimatedGradientBackground
animateFromBottom={isHomeDesktop}
animateFromBottom={false}
topPosition={gradientTopPosition}
/>
<div
className={cn(
"pointer-events-none absolute inset-0 z-0",
isHomeDesktop
? "bg-[linear-gradient(to_top,rgb(0_0_0/0.88)_0%,rgb(0_0_0/0.52)_38%,rgb(0_0_0/0.42)_100%)]"
: "bg-black/50",
)}
className="pointer-events-none absolute inset-0 z-0 bg-[#05080D]/50"
aria-hidden
/>
<div
id="graph-dotted-grid"
className={cn(
"pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]",
isHomeDesktop && "opacity-70",
)}
className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]"
/>
</>
)}
@ -716,7 +714,7 @@ export default function NewPage() {
{isDashboardShell && (
<div
className={cn(
"pointer-events-none fixed inset-x-0 z-30 bg-gradient-to-t from-black via-black/80 to-transparent pt-12",
"pointer-events-none fixed inset-x-0 z-30 bg-gradient-to-t from-black via-black/40 to-transparent pt-12",
isMobile ? "bottom-[4.5rem]" : "bottom-0",
)}
>

View file

@ -3,6 +3,7 @@
import { useMemo } from "react"
import type { UIMessage } from "@ai-sdk/react"
import { MemoryGraph } from "@/components/memory-graph"
import { AnimatedGradientBackground } from "@/components/animated-gradient-background"
import { useProject } from "@/stores"
import { extractHighlightDocumentIdsFromMessages } from "@/lib/chat-highlight-documents"
import { cn } from "@lib/utils"
@ -25,11 +26,14 @@ export function ChatGraphContextRail({
<div
id="chat-graph-context-rail"
className={cn(
"relative flex min-h-0 min-w-0 flex-1 flex-col bg-black",
"relative flex min-h-0 min-w-0 flex-1 flex-col bg-[#05080D] overflow-hidden",
dmSansClassName(),
className,
)}
>
<AnimatedGradientBackground animateFromBottom={false} topPosition="55%" />
<div className="pointer-events-none absolute inset-0 z-0 bg-[#05080D]/50" />
<div className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.25)_1px,transparent_1px)] bg-size-[32px_32px] mask-[radial-gradient(ellipse_at_center,black_60%,transparent_100%)]" />
<div className="pointer-events-none absolute top-3 left-4 z-20">
<p className="text-xs font-medium text-white/70">Memory map</p>
<p className="mt-0.5 max-w-[14rem] text-[10px] leading-snug text-white/35">
@ -38,8 +42,8 @@ export function ChatGraphContextRail({
: "Memories used by Nova will be highlighted here"}
</p>
</div>
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-r from-transparent to-black" />
<div className="min-h-0 flex-1 pt-10">
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-r from-transparent to-[#05080D]" />
<div className="relative z-[2] min-h-0 flex-1 pt-10">
<MemoryGraph
containerTags={effectiveContainerTags}
variant="consumer"

View file

@ -64,12 +64,12 @@ export function HomeChatComposer({
/>
<div
className={cn(
"inline-flex max-w-[min(160px,35vw)] min-w-0 shrink items-center rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5",
"inline-flex max-w-[min(160px,35vw)] min-w-0 shrink items-center rounded-full bg-fg-primary/5 px-3 py-1.5",
dmSansClassName(),
)}
title={chatSpaceLabel}
>
<span className="truncate text-sm text-[#FAFAFA]">
<span className="truncate text-sm text-fg-primary">
{chatSpaceLabel}
</span>
</div>

View file

@ -14,10 +14,10 @@ export function SendButton({
onClick={onClick}
disabled={disabled}
className={cn(
"bg-[#000000] border-[#161F2C] border p-2 rounded-lg shrink-0 transition-opacity",
"bg-surface-card border-surface-border border p-2 rounded-lg shrink-0 transition-opacity",
disabled
? "opacity-50 cursor-not-allowed"
: "cursor-pointer hover:bg-[#161F2C]",
: "cursor-pointer hover:bg-surface-hover",
)}
>
<svg
@ -42,7 +42,7 @@ export function StopButton({ onClick }: { onClick: () => void }) {
<button
type="button"
onClick={onClick}
className="bg-[#000000] border-[#161F2C] border p-2 rounded-lg shrink-0 cursor-pointer hover:bg-[#161F2C] transition-opacity"
className="bg-surface-card border-surface-border border p-2 rounded-lg shrink-0 cursor-pointer hover:bg-surface-hover transition-opacity"
>
<SquareIcon className="size-4 text-white fill-white" />
</button>

View file

@ -122,14 +122,14 @@ export default function ChatInput({
</>
) : null}
{stackedToolbar ? (
<div className="flex flex-col gap-2 rounded-xl border border-[#52596633] bg-[#070E1B] p-2 transition-all duration-200 focus-within:outline-1 focus-within:outline-[#525D6EB2]">
<div className="flex flex-col gap-2 rounded-xl bg-surface-card/60 backdrop-blur-md p-2 shadow-[0_16px_48px_rgba(0,0,0,0.34)] transition-all duration-200 focus-within:ring-1 focus-within:ring-fg-primary/10">
<textarea
ref={textareaRef}
value={value}
onChange={handleChange}
onKeyDown={onKeyDown}
placeholder="Ask your supermemory..."
className="w-full resize-none overflow-y-auto bg-transparent p-2 transition-all duration-200 placeholder:text-[#525D6E] focus:outline-none"
className="w-full resize-none overflow-y-auto bg-transparent p-2 text-fg-primary transition-all duration-200 placeholder:text-fg-faint focus:outline-none"
style={{ minHeight: "36px" }}
rows={1}
disabled={isResponding}
@ -150,7 +150,7 @@ export default function ChatInput({
) : (
<div
className={cn(
"flex items-end gap-2 rounded-xl border border-[#52596633] bg-[#070E1B] p-2 transition-all duration-200 focus-within:outline-1 focus-within:outline-[#525D6EB2]",
"flex items-end gap-2 rounded-xl bg-surface-card/60 backdrop-blur-md p-2 shadow-[0_16px_48px_rgba(0,0,0,0.34)] transition-all duration-200 focus-within:ring-1 focus-within:ring-fg-primary/10",
isMultiline && "flex-col",
)}
>
@ -160,7 +160,7 @@ export default function ChatInput({
onChange={handleChange}
onKeyDown={onKeyDown}
placeholder="Ask your supermemory..."
className="w-full resize-none overflow-y-auto bg-transparent p-2 transition-all duration-200 placeholder:text-[#525D6E] focus:outline-none"
className="w-full resize-none overflow-y-auto bg-transparent p-2 text-fg-primary transition-all duration-200 placeholder:text-fg-faint focus:outline-none"
style={{ minHeight: "36px" }}
rows={1}
disabled={isResponding}

View file

@ -11,7 +11,7 @@ import { analytics } from "@/lib/analytics"
interface ChatModelSelectorProps {
selectedModel?: ModelId
onModelChange?: (model: ModelId) => void
/** Compact pill matching inline send control (black + #161F2C border, rounded-full) */
/** Compact pill matching inline send control. */
minimal?: boolean
}
@ -41,22 +41,22 @@ export default function ChatModelSelector({
<button
type="button"
className={cn(
"flex max-w-[min(100%,220px)] min-w-0 shrink cursor-pointer items-center gap-1.5 rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5 text-sm transition-colors hover:bg-[#161F2C]",
"flex max-w-[min(100%,220px)] min-w-0 shrink cursor-pointer items-center gap-1.5 rounded-full bg-fg-primary/5 px-3 py-1.5 text-sm transition-colors hover:bg-fg-primary/10",
dmSansClassName(),
)}
onClick={() => setIsOpen(!isOpen)}
>
<p className="min-w-0 truncate text-left text-[#FAFAFA]">
<p className="min-w-0 truncate text-left text-fg-primary">
{currentModelData.name}{" "}
<span className="text-[#525D6E]">{currentModelData.version}</span>
<span className="text-fg-subtle">{currentModelData.version}</span>
</p>
<ChevronDownIcon className="size-3.5 shrink-0 text-[#525D6E]" />
<ChevronDownIcon className="size-3.5 shrink-0 text-fg-subtle" />
</button>
) : (
<Button
variant="headers"
className={cn(
"h-10! max-w-[min(100%,220px)] shrink gap-1 rounded-full border-[#73737333] bg-[#0D121A] text-base",
"h-10! max-w-[min(100%,220px)] shrink gap-1 rounded-full border-[#73737333] bg-surface-base text-base",
dmSansClassName(),
)}
style={{
@ -86,7 +86,7 @@ export default function ChatModelSelector({
aria-label="Close model selector"
/>
<div className="absolute bottom-full left-0 mb-2 w-64 bg-[#0D121A] backdrop-blur-xl border border-[#73737333] rounded-lg shadow-xl z-50 overflow-hidden">
<div className="absolute bottom-full left-0 mb-2 w-64 bg-surface-card backdrop-blur-xl border border-surface-border rounded-lg shadow-xl z-50 overflow-hidden">
<div className="p-2 space-y-1">
{models.map((model) => {
const modelData = modelNames[model.id]
@ -107,11 +107,11 @@ export default function ChatModelSelector({
>
<div className="text-sm font-medium text-white">
{modelData.name}{" "}
<span className="text-[#737373]">
<span className="text-fg-subtle">
{modelData.version}
</span>
</div>
<div className="text-xs text-[#737373] truncate w-full">
<div className="text-xs text-fg-muted truncate w-full">
{model.description}
</div>
</button>

View file

@ -294,13 +294,13 @@ function RecommendedPluginsCard({
return (
<div
className={cn(
"bg-[#050709] border border-[#0F1621] rounded-xl px-3 py-2 flex flex-col gap-1",
"bg-surface-card/60 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col gap-1 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
{showPicker ? (
<div className="px-1 py-2 flex flex-col gap-2.5">
<p className="text-[11px] text-[#737373]">
<p className="text-[11px] text-fg-muted">
{isEditing ? "Change your field:" : "What's your field?"}
</p>
<div className="flex flex-wrap gap-1.5">
@ -315,8 +315,8 @@ function RecommendedPluginsCard({
className={cn(
"rounded-full border px-2.5 py-1 text-[11px] font-medium transition-all cursor-pointer",
profession === value
? "border-[#3374FF]/40 bg-[#3374FF]/10 text-[#6BB0FF]"
: "border-[#161F2C] text-[#525D6E] hover:border-[#3374FF]/25 hover:text-[#4BA0FA]",
? "border-[#4BA0FA]/55 bg-[#3374FF]/15 text-[#8BC6FF]"
: "border-surface-border text-fg-subtle hover:border-[#4BA0FA]/40 hover:text-[#6BB0FF]",
)}
>
{label}
@ -327,7 +327,7 @@ function RecommendedPluginsCard({
<button
type="button"
onClick={() => setIsEditing(false)}
className="text-[10px] text-[#3A4455] hover:text-[#525D6E] transition-colors text-left cursor-pointer"
className="text-[10px] text-fg-faint hover:text-fg-muted transition-colors text-left cursor-pointer"
>
Cancel
</button>
@ -335,7 +335,7 @@ function RecommendedPluginsCard({
</div>
) : suggestions.length === 0 ? (
<div className="flex items-center justify-center py-4">
<p className="text-[11px] text-[#525D6E] text-center">
<p className="text-[11px] text-fg-subtle text-center">
You're all set
</p>
</div>
@ -347,18 +347,18 @@ function RecommendedPluginsCard({
<button
type="button"
onClick={plugin.onClick}
className="group w-full flex items-center gap-2.5 rounded-lg px-2 py-2 hover:bg-[#0D121A] transition-colors cursor-pointer"
className="group w-full flex items-center gap-2.5 rounded-lg px-2 py-2 hover:bg-surface-hover transition-colors cursor-pointer"
>
<plugin.Icon className="size-4 shrink-0 text-[#525D6E]" />
<plugin.Icon className="size-4 shrink-0 text-fg-subtle" />
<div className="flex-1 min-w-0 text-left">
<p className="text-[12px] text-[#737373] group-hover:text-white transition-colors leading-tight">
<p className="text-[12px] text-fg-secondary group-hover:text-white transition-colors leading-tight">
{plugin.name}
</p>
<p className="text-[11px] text-[#525D6E] leading-tight mt-0.5">
<p className="text-[11px] text-fg-subtle leading-tight mt-0.5">
{PLUGIN_TAGLINES[profession][plugin.id] ?? plugin.tagline}
</p>
</div>
<span className="shrink-0 text-[10px] font-medium text-[#3374FF] group-hover:text-[#6BB0FF] transition-colors">
<span className="shrink-0 text-[10px] font-medium text-[#5EA8FF] group-hover:text-[#8BC6FF] transition-colors">
{plugin.cta}
</span>
</button>
@ -368,7 +368,7 @@ function RecommendedPluginsCard({
<button
type="button"
onClick={() => setIsEditing(true)}
className="text-left px-2 pb-1 text-[10px] text-[#3A4455] hover:text-[#525D6E] transition-colors cursor-pointer"
className="text-left px-2 pb-1 text-[10px] text-fg-faint hover:text-fg-muted transition-colors cursor-pointer"
>
Not a{" "}
{PROFESSION_LABELS.find(
@ -398,20 +398,20 @@ function MemoryOfDayCard({ data }: { data: MemoryOfDay }) {
type="button"
onClick={() => router.push(href)}
className={cn(
"group w-full h-full text-left bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col justify-between hover:border-[rgba(255,255,255,0.10)] transition-colors cursor-pointer",
"group w-full h-full text-left bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col justify-between transition-colors cursor-pointer shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
<div className="flex flex-col gap-2.5">
<span className="self-start text-[9px] font-semibold tracking-[0.12em] uppercase text-[#4BA0FA] bg-[#4BA0FA]/10 rounded-full px-2 py-0.5">
<span className="self-start text-[9px] font-semibold tracking-[0.12em] uppercase text-[#8BC6FF] bg-[#4BA0FA]/16 rounded-full px-2 py-0.5">
{data.timeLabel}
</span>
<p className="text-[12px] text-[#8B9DB5] leading-relaxed line-clamp-4">
<p className="text-[12px] text-fg-secondary leading-relaxed line-clamp-4">
{memory}
</p>
</div>
<span className="text-[10px] text-[#2A3A50] group-hover:text-[#4A6A80] transition-colors">
<span className="text-[10px] text-fg-faint group-hover:text-fg-muted transition-colors">
View memories
</span>
</button>
@ -475,7 +475,7 @@ function PluginPromoCard({
return (
<div
className={cn(
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col justify-between gap-3 h-full",
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col justify-between gap-3 h-full shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
@ -511,10 +511,10 @@ function PluginPromoCard({
)}
</div>
<div className="flex flex-col gap-1">
<p className="text-[11px] font-semibold text-[#FAFAFA] leading-tight">
<p className="text-[11px] font-semibold text-fg-primary leading-tight">
{plugin.name}
</p>
<p className="text-[10px] text-[#525D6E] leading-normal">
<p className="text-[10px] text-fg-muted leading-normal">
{plugin.tagline}
</p>
</div>
@ -524,7 +524,7 @@ function PluginPromoCard({
<button
type="button"
onClick={plugin.onClick}
className="w-full bg-[#0D121A] rounded-lg px-3 py-1.5 text-[11px] font-medium text-[#4BA0FA] hover:text-white hover:bg-[#141C28] transition-colors cursor-pointer text-left flex items-center justify-between group"
className="w-full bg-surface-card border border-surface-border rounded-lg px-3 py-1.5 text-[11px] font-medium text-[#6BB0FF] hover:text-white hover:bg-surface-hover transition-colors cursor-pointer text-left flex items-center justify-between group"
style={{ boxShadow: "inset 1px 1px 2px rgba(0,0,0,0.5)" }}
>
<span>{plugin.cta}</span>
@ -533,8 +533,8 @@ function PluginPromoCard({
</>
) : (
<div className="flex flex-col items-center justify-center h-full gap-1.5 text-center">
<Terminal className="size-4 text-[#3A4455]" />
<p className="text-[10px] text-[#3A4455]">
<Terminal className="size-4 text-fg-faint" />
<p className="text-[10px] text-fg-subtle">
All integrations connected
</p>
</div>
@ -654,10 +654,10 @@ export function DashboardView({
<motion.header
{...fadeUp}
transition={{ ...fadeUp.transition, delay: 0 }}
className="flex items-end justify-between gap-4 border-b border-[#0F1621] pb-4"
className="flex items-end justify-between gap-4 border-b border-surface-border pb-4"
>
<div className="space-y-0.5">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
Home
</p>
<h1 className="text-xl font-medium tracking-tight text-white md:text-2xl">
@ -670,7 +670,7 @@ export function DashboardView({
<button
type="button"
onClick={onNavigateToGraph}
className="group relative shrink-0 w-[140px] h-[56px] rounded-xl overflow-hidden border border-[rgba(255,255,255,0.05)] hover:border-[rgba(255,255,255,0.14)] transition-all bg-[#0B1017] hover:scale-[1.02]"
className="group relative shrink-0 w-[140px] h-[56px] rounded-xl overflow-hidden border border-surface-border hover:border-[#3A4A63] transition-all bg-surface-card hover:scale-[1.02]"
aria-label="Open graph view"
>
<StaticGraphPreview
@ -696,7 +696,7 @@ export function DashboardView({
className="space-y-2"
>
<div className="flex items-center gap-1.5">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
Daily brief
</p>
<Tooltip>
@ -704,7 +704,7 @@ export function DashboardView({
<button
type="button"
onClick={onResetHighlights}
className="text-[#2A3040] hover:text-[#5A6478] transition-colors cursor-pointer"
className="text-fg-faint hover:text-fg-muted transition-colors cursor-pointer"
aria-label="Refresh daily brief"
>
<RotateCcw className="size-3" />
@ -750,28 +750,28 @@ export function DashboardView({
<button
type="button"
onClick={() => onAddMemory("link")}
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
>
<Link2 className="size-3.5 shrink-0" />
{personalizedCopy.saveLink}
</button>
<span className="text-[#1A2030] select-none">·</span>
<span className="text-[#3A4455] select-none">·</span>
<button
type="button"
onClick={() => onAddMemory("note")}
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
>
<FileText className="size-3.5 shrink-0" />
{personalizedCopy.writeNote}
</button>
<span className="text-[#1A2030] select-none">·</span>
<span className="text-[#3A4455] select-none">·</span>
<button
type="button"
onClick={() => {
analytics.searchOpened({ source: "header" })
onOpenSearch()
}}
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-[#5A6478] hover:bg-[#0D121A] hover:text-white transition-colors cursor-pointer"
className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-fg-subtle hover:bg-surface-hover hover:text-white transition-colors cursor-pointer"
>
<SearchIcon className="size-3.5 shrink-0" />
Search
@ -779,7 +779,7 @@ export function DashboardView({
</div>
{/* Tip of the day */}
<p className="hidden sm:flex items-center gap-1.5 text-[11px] text-[#525D6E] min-w-0 overflow-hidden">
<p className="hidden sm:flex items-center gap-1.5 text-[11px] text-fg-subtle min-w-0 overflow-hidden">
<Lightbulb className="size-3 shrink-0 text-[#3374FF]" />
<span className="truncate">{tip}</span>
</p>
@ -796,12 +796,12 @@ export function DashboardView({
{/* Shared header row — both labels aligned */}
<div className="flex gap-4">
<div className="flex-[3] min-w-0">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
Recently saved
</p>
</div>
<div className="flex-[2] min-w-0 hidden sm:block">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
Suggested for you
</p>
</div>
@ -817,19 +817,19 @@ export function DashboardView({
<button
type="button"
onClick={() => onOpenDocument(doc)}
className="group flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors hover:bg-[#0D121A]"
className="group flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors hover:bg-surface-hover"
>
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-[#0D121A] group-hover:bg-[#131B28] transition-colors">
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-surface-card ring-1 ring-surface-border group-hover:bg-[#182333] transition-colors">
{isLink ? (
<ExternalLink className="size-3 text-[#3A4455]" />
<ExternalLink className="size-3 text-fg-subtle" />
) : (
<FileText className="size-3 text-[#3A4455]" />
<FileText className="size-3 text-fg-subtle" />
)}
</div>
<span className="min-w-0 flex-1 truncate text-sm text-[#737373] group-hover:text-white transition-colors">
<span className="min-w-0 flex-1 truncate text-sm text-fg-muted group-hover:text-white transition-colors">
{doc.title?.trim() || "Untitled"}
</span>
<ArrowRight className="size-3.5 shrink-0 text-[#1E2736] group-hover:text-[#3A4455] transition-colors" />
<ArrowRight className="size-3.5 shrink-0 text-fg-faint group-hover:text-fg-muted transition-colors" />
</button>
</li>
)
@ -851,7 +851,7 @@ export function DashboardView({
) : (
/* No recents yet — show suggestions full-width */
<>
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-[#3A4455]">
<p className="text-[10px] font-medium uppercase tracking-[0.12em] text-fg-faint">
Suggested for you
</p>
<div className="max-w-sm">

View file

@ -102,7 +102,7 @@ export function HighlightsCard({
return (
<div
className={cn(
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3",
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
@ -128,7 +128,7 @@ export function HighlightsCard({
return (
<div
className={cn(
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3 min-h-[180px]",
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 min-h-[180px] shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
@ -146,7 +146,7 @@ export function HighlightsCard({
</div>
</div>
<div className="flex-1 flex items-center justify-center">
<p className="text-[11px] text-[#737373] text-center">
<p className="text-[11px] text-fg-muted text-center">
Add some documents to see highlights here
</p>
</div>
@ -157,7 +157,7 @@ export function HighlightsCard({
return (
<div
className={cn(
"bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col gap-3",
"bg-surface-card/60 backdrop-blur-md rounded-[18px] p-3 flex flex-col gap-3 shadow-[0_12px_40px_rgba(0,0,0,0.22)]",
dmSansClassName(),
)}
>
@ -173,14 +173,14 @@ export function HighlightsCard({
</span>
</div>
</div>
<Info className="size-[14px] text-[#737373]" />
<Info className="size-[14px] text-fg-subtle" />
</div>
<div id="highlights-body" className="flex flex-col gap-1.5">
<p className="text-[12px] font-semibold text-[#FAFAFA] leading-tight truncate">
<p className="text-[12px] font-semibold text-fg-primary leading-tight truncate">
{currentItem.title}
</p>
<div className="text-[12px] text-[#FAFAFA] leading-normal line-clamp-5">
<div className="text-[12px] text-fg-primary leading-normal line-clamp-5">
{renderContent(currentItem.content, currentItem.format)}
</div>
</div>
@ -190,27 +190,27 @@ export function HighlightsCard({
<button
type="button"
onClick={handleChat}
className="bg-[#1B1F24] rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
className="bg-[#182333] border border-surface-border rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
style={{
boxShadow: "0 4px 20px 0 rgba(0, 0, 0, 0.25)",
}}
aria-label="Chat with Nova"
>
<MessageSquare className="size-3.5 text-[#FAFAFA]" />
<span className="text-[11px] text-[#FAFAFA]">Chat</span>
<MessageSquare className="size-3.5 text-fg-primary" />
<span className="text-[11px] text-fg-primary">Chat</span>
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_1px_1px_1px_0_rgba(255,255,255,0.1)]" />
</button>
<button
type="button"
onClick={handleShowRelated}
className="bg-[#1B1F24] rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
className="bg-[#182333] border border-surface-border rounded-[8px] px-2 py-1.5 flex items-center gap-1.5 cursor-pointer relative"
style={{
boxShadow: "0 4px 20px 0 rgba(0, 0, 0, 0.25)",
}}
aria-label="Show related"
>
<Link2 className="size-3.5 text-[#FAFAFA]" />
<span className="text-[11px] text-[#FAFAFA]">Related</span>
<Link2 className="size-3.5 text-fg-primary" />
<span className="text-[11px] text-fg-primary">Related</span>
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_1px_1px_1px_0_rgba(255,255,255,0.1)]" />
</button>
</div>
@ -220,7 +220,7 @@ export function HighlightsCard({
<button
type="button"
onClick={handlePrev}
className="text-[#737373] hover:text-white transition-colors cursor-pointer"
className="text-fg-subtle hover:text-white transition-colors cursor-pointer"
aria-label="Previous item"
>
<ChevronLeft className="size-4" />
@ -235,7 +235,7 @@ export function HighlightsCard({
"rounded-full transition-all cursor-pointer",
idx === activeIndex
? "w-4 h-1.5 bg-[#4BA0FA]"
: "size-1.5 bg-[#737373] hover:bg-[#999999]",
: "size-1.5 bg-fg-subtle hover:bg-fg-secondary",
)}
aria-label={`Go to item ${idx + 1}`}
/>
@ -244,7 +244,7 @@ export function HighlightsCard({
<button
type="button"
onClick={handleNext}
className="text-[#737373] hover:text-white transition-colors cursor-pointer"
className="text-fg-subtle hover:text-white transition-colors cursor-pointer"
aria-label="Next item"
>
<ChevronRight className="size-4" />

View file

@ -4,6 +4,18 @@
@theme {
--color-onboarding: #525966;
--color-fg-primary: #fafafa;
--color-fg-secondary: #e2e8f0;
--color-fg-muted: #d0dae7;
--color-fg-subtle: #b5c2d3;
--color-fg-faint: #a0aec4;
--color-surface-base: #0b1119;
--color-surface-card: #101822;
--color-surface-hover: #131b28;
--color-surface-border: #263348;
--animate-file-upload-grow: file-upload-grow 6s cubic-bezier(0.22, 1, 0.36, 1)
forwards;
}