fix(web): paint dark mode from CSS tokens instead of utility remaps

Drive light and dark from one token table so Tailwind utilities follow
:root.dark assignments. Drop the leftover dark: color variants and the
[class~="…"] inversion layer, and keep PDF paper as a white surface.
This commit is contained in:
tianma-if committed 2026-09-10 18:23:42 +08:00
1 parent 8584a4a850
commit ab7b3ecf47
87 files changed
+590 -615

No files matched your search

+8 -1
View File
@@ -1,6 +1,13 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
const configDirectory = path.dirname(fileURLToPath(import.meta.url));
export default {
plugins: {
tailwindcss: {},
tailwindcss: {
config: path.join(configDirectory, "tailwind.config.ts"),
},
autoprefixer: {},
},
};
+1 -1
View File
@@ -172,7 +172,7 @@ const AuthenticatedWorkspace = () => {
if (desktopScopeError) {
return (
<main className="flex h-[100dvh] items-center justify-center bg-slate-50 px-4 text-slate-900">
<section className="w-full max-w-md rounded-xl border border-rose-200 bg-white p-6 shadow-sm">
<section className="w-full max-w-md rounded-xl border border-rose-200 bg-card p-6 shadow-sm">
<p className="text-sm leading-6 text-rose-800">{t("login.desktopScopeUnavailable")}</p>
<Button className="mt-4" variant="outline" onClick={() => setDesktopScopeAttempt((value) => value + 1)}>
{t("login.desktopScopeRetry")}
+9 -9
View File
@@ -169,7 +169,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
return (
<div className="flex h-full min-h-0 min-w-0 flex-col bg-slate-50/60">
<header className="flex h-[calc(3.75rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200/80 bg-white px-6 pb-3 pt-[env(safe-area-inset-top)] shadow-2xs lg:h-16 lg:items-center lg:pb-0 lg:pt-0">
<header className="flex h-[calc(3.75rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200/80 bg-card px-6 pb-3 pt-[env(safe-area-inset-top)] shadow-2xs lg:h-16 lg:items-center lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<TooltipProvider delayDuration={0}>
<Tooltip>
@@ -210,7 +210,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
type="button"
size="sm"
variant="outline"
className="gap-1.5 border-slate-200 bg-white text-xs font-semibold text-slate-700 shadow-2xs hover:bg-slate-50 hover:text-slate-900"
className="gap-1.5 border-slate-200 bg-card text-xs font-semibold text-slate-700 shadow-2xs hover:bg-slate-50 hover:text-slate-900"
onClick={() => {
setRestoreFeedback(null);
setRestoreConfirmOpen(true);
@@ -224,7 +224,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
type="button"
size="sm"
variant="outline"
className="gap-1.5 border-slate-200 bg-white text-xs font-semibold text-slate-700 shadow-2xs hover:bg-slate-50 hover:text-slate-900"
className="gap-1.5 border-slate-200 bg-card text-xs font-semibold text-slate-700 shadow-2xs hover:bg-slate-50 hover:text-slate-900"
onClick={startCreating}
>
<Plus className="h-3.5 w-3.5 text-emerald-600" />
@@ -241,7 +241,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
{editorOpen ? (
<form
className="rounded-xl border border-emerald-200 bg-white p-5 shadow-xs"
className="rounded-xl border border-emerald-200 bg-card p-5 shadow-xs"
onSubmit={(event: FormEvent) => {
event.preventDefault();
if (canSubmit && hasChanges) saveMutation.mutate();
@@ -315,7 +315,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
<label className="grid gap-1.5 text-xs font-medium text-slate-700">
{t("aiPrompts.instruction")}
<textarea
className="min-h-36 w-full resize-y rounded-lg border border-slate-200 bg-white p-3 text-sm text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="min-h-36 w-full resize-y rounded-lg border border-slate-200 bg-card p-3 text-sm text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
value={form.instruction}
onChange={(event) => setForm((current) => ({ ...current, instruction: event.target.value }))}
placeholder={t("aiPrompts.instructionPlaceholder")}
@@ -347,7 +347,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
{prompts.map((prompt) => (
<article
key={prompt.id}
className="group flex flex-col justify-between rounded-xl border border-slate-200 bg-white p-4 transition-all duration-200 hover:border-emerald-300 hover:shadow-md"
className="group flex flex-col justify-between rounded-xl border border-slate-200 bg-card p-4 transition-all duration-200 hover:border-emerald-300 hover:shadow-md"
>
<div className="min-w-0">
<div className="flex min-w-0 items-center gap-2">
@@ -368,7 +368,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
type="button"
size="sm"
variant="outline"
className="h-8 gap-1 bg-white text-xs"
className="h-8 gap-1 bg-card text-xs"
onClick={() => setPreview(prompt)}
>
<Eye className="h-3.5 w-3.5" />
@@ -378,7 +378,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
type="button"
size="sm"
variant="outline"
className="h-8 gap-1 bg-white text-xs"
className="h-8 gap-1 bg-card text-xs"
onClick={() => startEditing(prompt)}
>
<Pencil className="h-3.5 w-3.5" />
@@ -402,7 +402,7 @@ export const AiPromptsPane = ({ onClose, onOpenExecutionCenter }: { onClose: ()
))}
</div>
) : (
<div className="rounded-xl border border-dashed border-slate-200 bg-white px-6 py-10 text-center">
<div className="rounded-xl border border-dashed border-slate-200 bg-card px-6 py-10 text-center">
<p className="text-sm font-medium text-slate-700">{t("aiPrompts.empty")}</p>
<p className="mt-1 text-xs text-slate-400">{t("aiPrompts.emptyHint")}</p>
<div className="mt-4 flex flex-wrap items-center justify-center gap-2">
+10 -10
View File
@@ -189,7 +189,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
resource.memoDeleted ? t("assets.deletedMemo") : resource.memoTitle || resource.memoExcerpt || resource.memoId;
return (
<div className="relative flex h-full min-h-0 flex-col bg-white select-none outline-none">
<div className="relative flex h-full min-h-0 flex-col bg-card select-none outline-none">
{/* Header */}
<header className="flex h-[calc(4rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 px-6 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:pb-0 lg:pt-0">
@@ -226,7 +226,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
</header>
{/* Toolbar (Filters, Search, Layout mode) */}
<div className="shrink-0 border-b border-slate-100 bg-white p-4">
<div className="shrink-0 border-b border-slate-100 bg-card p-4">
<div className="mx-auto flex w-full max-w-4xl flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
{/* Category Filters */}
<div className="flex items-center gap-1.5 overflow-x-auto pb-1 sm:pb-0 scrollbar-none">
@@ -256,7 +256,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
aria-label={t("assets.searchPlaceholder")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-9 w-full rounded-lg border border-slate-200 bg-slate-50/50 pl-9 pr-8 text-xs text-slate-800 placeholder-slate-400 transition-colors focus:border-emerald-500/50 focus:bg-white focus:outline-none focus:ring-1 focus:ring-emerald-500/20"
className="h-9 w-full rounded-lg border border-slate-200 bg-slate-50/50 pl-9 pr-8 text-xs text-slate-800 placeholder-slate-400 transition-colors focus:border-emerald-500/50 focus:bg-card focus:outline-none focus:ring-1 focus:ring-emerald-500/20"
/>
{searchQuery && (
<button
@@ -276,7 +276,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
aria-label={t("assets.gridView")}
aria-pressed={layoutMode === "grid"}
className={`flex h-8 w-8 items-center justify-center rounded-md transition-colors ${
layoutMode === "grid" ? "bg-white text-emerald-700 shadow-sm" : "text-slate-400 hover:text-slate-600"
layoutMode === "grid" ? "bg-card text-emerald-700 shadow-sm" : "text-slate-400 hover:text-slate-600"
}`}
>
<Grid className="h-4 w-4" />
@@ -288,7 +288,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
aria-label={t("assets.listView")}
aria-pressed={layoutMode === "list"}
className={`flex h-8 w-8 items-center justify-center rounded-md transition-colors ${
layoutMode === "list" ? "bg-white text-emerald-700 shadow-sm" : "text-slate-400 hover:text-slate-600"
layoutMode === "list" ? "bg-card text-emerald-700 shadow-sm" : "text-slate-400 hover:text-slate-600"
}`}
>
<List className="h-4 w-4" />
@@ -308,7 +308,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
<span className="text-xs font-medium">{t("assets.loading")}</span>
</div>
) : filteredResources.length === 0 ? (
<div className="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-slate-200 bg-white px-6 py-24 text-center">
<div className="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-slate-200 bg-card px-6 py-24 text-center">
<Archive className="h-10 w-10 text-slate-350 mb-3 stroke-[1.5]" />
<p className="text-sm font-semibold text-slate-500">
{searchQuery || filterType !== "all" ? t("assets.noMatches") : t("assets.empty")}
@@ -325,7 +325,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
{filteredResources.map((resource) => (
<div
key={resource.id}
className="group relative flex flex-col overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm transition-all duration-200 hover:border-emerald-500/40 hover:shadow-md"
className="group relative flex flex-col overflow-hidden rounded-xl border border-slate-200 bg-card shadow-sm transition-all duration-200 hover:border-emerald-500/40 hover:shadow-md"
>
<ButtonTooltip title={t("assets.deleteAria", { filename: resource.filename || resource.id })}>
<Button
@@ -333,7 +333,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
size="icon"
variant="ghost"
aria-label={t("assets.deleteAria", { filename: resource.filename || resource.id })}
className="absolute right-2 top-2 z-10 h-8 w-8 bg-white/90 text-slate-500 opacity-0 shadow-sm transition-opacity hover:bg-rose-50 hover:text-rose-600 focus:opacity-100 group-hover:opacity-100"
className="absolute right-2 top-2 z-10 h-8 w-8 bg-card/90 text-slate-500 opacity-0 shadow-sm transition-opacity hover:bg-rose-50 hover:text-rose-600 focus:opacity-100 group-hover:opacity-100"
onClick={() => requestResourceDelete(resource)}
>
<Trash2 className="h-4 w-4" />
@@ -377,7 +377,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
)}
{/* Hover detail overlay */}
<div className="absolute inset-0 bg-slate-900/40 opacity-0 transition-opacity duration-200 group-hover:opacity-100 flex items-center justify-center">
<span className="rounded bg-white/90 px-2.5 py-1.5 text-[11px] font-semibold text-slate-800 shadow flex items-center gap-1">
<span className="rounded bg-card/90 px-2.5 py-1.5 text-[11px] font-semibold text-slate-800 shadow flex items-center gap-1">
{resource.kind === "image"
? t("assets.previewImage")
: isPdfAttachment(resource.mimeType, resource.filename)
@@ -418,7 +418,7 @@ export const AssetsPane = ({ onClose, repository, onOpenExecutionCenter }: Asset
{filteredResources.map((resource) => (
<div
key={resource.id}
className="group relative flex items-center gap-3.5 rounded-xl border border-slate-200/80 bg-white p-3.5 text-left transition-all duration-200 hover:border-emerald-500/35 hover:shadow-sm"
className="group relative flex items-center gap-3.5 rounded-xl border border-slate-200/80 bg-card p-3.5 text-left transition-all duration-200 hover:border-emerald-500/35 hover:shadow-sm"
>
{/* Left Icon/Thumbnail */}
<div
+18 -18
View File
@@ -17,23 +17,23 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
return (
<div className="space-y-3 pt-0.5" aria-label={t(merge ? "companion.actions.merge" : "companion.actions.tag")}>
<p className="line-clamp-3 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm dark:text-slate-300">
<p className="line-clamp-3 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm ">
{action.plan.reason}
</p>
{action.status === "pending" ? (
<div className="space-y-3">
{action.plan.kind === "merge" ? (
<div className="space-y-2 rounded-lg border border-slate-200/70 bg-slate-50/70 p-3 dark:border-slate-800 dark:bg-slate-900/50">
<p className="break-words text-xs font-semibold text-slate-800 dark:text-slate-200">
<div className="space-y-2 rounded-lg border border-slate-200/70 bg-slate-50/70 p-3 ">
<p className="break-words text-xs font-semibold text-slate-800 ">
{t("companion.actions.resultTitle", { title: action.plan.title })}
</p>
<ol className="list-inside list-decimal space-y-2 pt-1.5 text-xs">
{action.notes.map(note => (
<li key={note.id} className="text-slate-700 dark:text-slate-300">
<li key={note.id} className="text-slate-700 ">
<button
type="button"
disabled={busy}
className="inline-flex max-w-full items-center gap-1 font-medium hover:text-emerald-600 hover:underline dark:hover:text-emerald-400"
className="inline-flex max-w-full items-center gap-1 font-medium hover:text-emerald-600 hover:underline "
onClick={() => onOpenNote(note.id, note.notebookId)}
>
<FileText className="h-3 w-3 shrink-0 text-slate-400" />
@@ -41,11 +41,11 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
<ArrowUpRight className="h-2.5 w-2.5 shrink-0 opacity-50" />
</button>
{note.excerpt ? (
<p className="mt-0.5 line-clamp-1 break-words text-[11px] text-slate-500 dark:text-slate-400">
<p className="mt-0.5 line-clamp-1 break-words text-[11px] text-slate-500 ">
{note.excerpt}
</p>
) : null}
<p className="mt-0.5 break-words text-[10px] text-slate-400 dark:text-slate-500">
<p className="mt-0.5 break-words text-[10px] text-slate-400 ">
{t("companion.actions.existingTags", { tags: note.tags.join(" · ") || "—" })}
</p>
</li>
@@ -53,32 +53,32 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
</ol>
</div>
) : (
<div className="space-y-2 rounded-lg border border-slate-200/70 bg-slate-50/70 p-3 dark:border-slate-800 dark:bg-slate-900/50">
<div className="space-y-2 rounded-lg border border-slate-200/70 bg-slate-50/70 p-3 ">
{action.notes[0] ? (
<div>
<button
type="button"
disabled={busy}
onClick={() => onOpenNote(action.notes[0].id, action.notes[0].notebookId)}
className="group/note inline-flex max-w-full items-center gap-1.5 text-xs font-semibold text-slate-800 hover:text-emerald-600 dark:text-slate-200 dark:hover:text-emerald-400"
className="group/note inline-flex max-w-full items-center gap-1.5 text-xs font-semibold text-slate-800 hover:text-emerald-600 "
>
<FileText className="h-3.5 w-3.5 shrink-0 text-slate-400 group-hover/note:text-emerald-600 dark:group-hover/note:text-emerald-400" />
<FileText className="h-3.5 w-3.5 shrink-0 text-slate-400 group-hover/note:text-emerald-600 " />
<span className="truncate">{action.notes[0].title || t("common.untitledMemo")}</span>
<ArrowUpRight className="h-3 w-3 shrink-0 opacity-40 group-hover/note:opacity-100" />
</button>
{action.notes[0].excerpt ? (
<p className="mt-1 line-clamp-2 break-words text-xs text-slate-500 dark:text-slate-400">
<p className="mt-1 line-clamp-2 break-words text-xs text-slate-500 ">
{action.notes[0].excerpt}
</p>
) : null}
</div>
) : null}
<div className="space-y-1 border-t border-slate-200/50 pt-2 text-xs dark:border-slate-800">
<p className="break-words font-medium text-emerald-700 dark:text-emerald-400">
<div className="space-y-1 border-t border-slate-200/50 pt-2 text-xs ">
<p className="break-words font-medium text-emerald-700 ">
{t("companion.actions.addTags", { tags: action.plan.tags.join(" · ") })}
</p>
{action.notes[0] ? (
<p className="break-words text-[11px] text-slate-400 dark:text-slate-500">
<p className="break-words text-[11px] text-slate-400 ">
{t("companion.actions.existingTags", { tags: action.notes[0].tags.join(" · ") || "—" })}
</p>
) : null}
@@ -86,13 +86,13 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
</div>
)}
<div className="flex items-center justify-end gap-2 border-t border-slate-100 pt-2.5 dark:border-slate-800/80">
<div className="flex items-center justify-end gap-2 border-t border-slate-100 pt-2.5 ">
<Button
size="sm"
variant="ghost"
disabled={busy}
onClick={() => onDismiss(action)}
className="h-7 px-2.5 text-xs text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
className="h-7 px-2.5 text-xs text-slate-500 hover:text-slate-700 "
>
{t("companion.actions.dismiss")}
</Button>
@@ -109,7 +109,7 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
</div>
</div>
) : action.status === "applied" ? (
<div className="flex items-center justify-between gap-2 rounded-lg border border-emerald-200/50 bg-emerald-50/60 p-2.5 text-xs text-emerald-700 dark:border-emerald-800/50 dark:bg-emerald-950/30 dark:text-emerald-300">
<div className="flex items-center justify-between gap-2 rounded-lg border border-emerald-200/50 bg-emerald-50/60 p-2.5 text-xs text-emerald-700 ">
<span className="flex items-center gap-1.5 font-medium">
<Check className="h-3.5 w-3.5" />
{t("companion.actions.status.applied")}
@@ -127,7 +127,7 @@ export function CompanionActionCard({ action, busy, onApply, onDismiss, onOpenNo
) : null}
</div>
) : (
<p role="status" className="text-xs text-slate-400 dark:text-slate-500">
<p role="status" className="text-xs text-slate-400 ">
{t(`companion.actions.status.${action.status}`)}
</p>
)}
@@ -109,7 +109,7 @@ export default function CompanionDiscoveryHub({ scope, onOpenNote, onNotesChange
<Button variant="ghost" size="icon" className="relative h-8 w-8 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-950 focus-visible:ring-2 focus-visible:ring-emerald-500/70"
aria-label={t("companion.discovery.title")} onClick={() => { setOpen(true); void feed.refetch(); }}>
<PawPrint className="h-5 w-5" strokeWidth={2.25} aria-hidden="true" />
{items.some(item => !item.seen && item.action?.status !== "applied") ? <span aria-label={t("companion.discovery.unread")} className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-emerald-500 ring-2 ring-white dark:ring-slate-900" /> : null}
{items.some(item => !item.seen && item.action?.status !== "applied") ? <span aria-label={t("companion.discovery.unread")} className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-emerald-500 ring-2 ring-white " /> : null}
</Button>
</TooltipTrigger>
<TooltipContent>{t("companion.discovery.title")}</TooltipContent>
@@ -117,18 +117,18 @@ export default function CompanionDiscoveryHub({ scope, onOpenNote, onNotesChange
</TooltipProvider>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="grid max-h-[min(760px,calc(100dvh-2rem))] w-[calc(100%-2rem)] max-w-xl grid-rows-[auto_minmax(0,1fr)_auto] gap-0 overflow-hidden p-0">
<DialogHeader className="border-b border-slate-100 px-5 py-4 text-left dark:border-slate-800">
<DialogHeader className="border-b border-slate-100 px-5 py-4 text-left ">
<div className="flex items-center justify-between pr-8">
<div className="flex items-center gap-2.5">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-emerald-50 text-emerald-600 ring-1 ring-emerald-500/20 dark:bg-emerald-950/50 dark:text-emerald-400 dark:ring-emerald-500/30">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-emerald-50 text-emerald-600 ring-1 ring-emerald-500/20 ">
<PawPrint className="h-4 w-4" strokeWidth={2.5} />
</div>
<div className="flex items-center gap-2">
<DialogTitle className="text-base font-semibold text-slate-900 dark:text-slate-100">
<DialogTitle className="text-base font-semibold text-slate-900 ">
{t("companion.discovery.title")}
</DialogTitle>
{items.length > 0 ? (
<Badge variant="outline" className="border-emerald-600/20 bg-emerald-50 px-2 font-medium text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-950/40 dark:text-emerald-300">
<Badge variant="outline" className="border-emerald-600/20 bg-emerald-50 px-2 font-medium text-emerald-700 ">
{items.length}
</Badge>
) : null}
@@ -149,10 +149,10 @@ export default function CompanionDiscoveryHub({ scope, onOpenNote, onNotesChange
) : null}
{!feed.isPending && !feed.isError && !items.length ? (
<div className="flex flex-col items-center justify-center py-10 text-center">
<div className="mb-3 flex h-12 w-12 items-center justify-center rounded-2xl bg-slate-50 text-slate-400 ring-1 ring-slate-200 dark:bg-slate-900 dark:text-slate-500 dark:ring-slate-800">
<div className="mb-3 flex h-12 w-12 items-center justify-center rounded-2xl bg-slate-50 text-slate-400 ring-1 ring-slate-200 ">
<PawPrint className="h-6 w-6 stroke-[1.5]" />
</div>
<p className="max-w-xs text-sm font-medium text-slate-600 dark:text-slate-300">
<p className="max-w-xs text-sm font-medium text-slate-600 ">
{t("companion.discovery.empty")}
</p>
</div>
@@ -163,11 +163,11 @@ export default function CompanionDiscoveryHub({ scope, onOpenNote, onNotesChange
onSeen={() => { void api.acknowledgeCompanionDiscovery(item.id).then(() => client.setQueryData<CompanionDiscoveryItem[]>(discoveryFeedKey(scope),
current => current?.map(entry => entry.id === item.id ? { ...entry, seen: true } : entry))).catch(() => {}); }} />)}
</div>
<div className="flex justify-end border-t border-slate-100 bg-slate-50/50 px-5 py-2.5 dark:border-slate-800 dark:bg-slate-900/50">
<div className="flex justify-end border-t border-slate-100 bg-slate-50/50 px-5 py-2.5 ">
<Button
size="sm"
variant="ghost"
className="h-7 shrink-0 gap-1.5 px-2 text-xs text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-100"
className="h-7 shrink-0 gap-1.5 px-2 text-xs text-slate-600 hover:text-slate-900 "
onClick={() => { setOpen(false); onOpenSettings(); }}
>
<Settings className="h-3.5 w-3.5" />
@@ -204,15 +204,15 @@ function DiscoveryCard({ item, busy, open, onApply, onDismiss, onOpenNote, onSee
return (
<article ref={ref}>
<Card className="group relative flex flex-col gap-3 rounded-xl border border-slate-200/90 bg-white p-4 shadow-xs transition-all hover:border-slate-300 hover:shadow-sm dark:border-slate-800 dark:bg-slate-900/60 dark:hover:border-slate-700">
<Card className="group relative flex flex-col gap-3 rounded-xl border border-slate-200/90 bg-card p-4 shadow-xs transition-all hover:border-slate-300 hover:shadow-sm ">
<CardHeader className="flex-row items-start justify-between gap-3 p-0">
<div className="min-w-0">
<CardTitle className="line-clamp-1 break-words text-sm leading-snug text-slate-900 dark:text-slate-100">
<CardTitle className="line-clamp-1 break-words text-sm leading-snug text-slate-900 ">
{item.title}
</CardTitle>
</div>
{formattedDate ? (
<time className="shrink-0 pt-0.5 font-mono text-[11px] text-slate-400 dark:text-slate-500">
<time className="shrink-0 pt-0.5 font-mono text-[11px] text-slate-400 ">
{formattedDate}
</time>
) : null}
@@ -228,12 +228,12 @@ function DiscoveryCard({ item, busy, open, onApply, onDismiss, onOpenNote, onSee
<ul className="mt-2 space-y-1">{item.memories.map(memory => <li key={memory.id}>{companionMemoryText(memory, t)}</li>)}</ul>
</details> : null}
{!item.action ? (
<p className="line-clamp-4 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm dark:text-slate-300">
<p className="line-clamp-4 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm ">
{item.body}
</p>
) : null}
{item.kind === "append" ? (
<p className="rounded-lg border border-emerald-100 bg-emerald-50/50 p-2.5 text-xs leading-relaxed text-emerald-700 dark:border-emerald-900/40 dark:bg-emerald-950/30 dark:text-emerald-400">
<p className="rounded-lg border border-emerald-100 bg-emerald-50/50 p-2.5 text-xs leading-relaxed text-emerald-700 ">
{t("companion.discovery.appendHelp")}
</p>
) : null}
@@ -253,9 +253,9 @@ function DiscoveryCard({ item, busy, open, onApply, onDismiss, onOpenNote, onSee
size="sm"
variant="outline"
onClick={() => onOpenNote(source.id, source.notebookId)}
className="group/btn h-7 max-w-full gap-1.5 bg-slate-50/70 px-2.5 text-xs text-slate-700 hover:border-emerald-400 hover:bg-emerald-50/50 hover:text-emerald-700 dark:border-slate-700 dark:bg-slate-800/80 dark:text-slate-300 dark:hover:border-emerald-600 dark:hover:bg-emerald-950/30 dark:hover:text-emerald-300"
className="group/btn h-7 max-w-full gap-1.5 bg-slate-50/70 px-2.5 text-xs text-slate-700 hover:border-emerald-400 hover:bg-emerald-50/50 hover:text-emerald-700 "
>
<FileText className="h-3 w-3 shrink-0 text-slate-400 group-hover/btn:text-emerald-600 dark:group-hover/btn:text-emerald-400" />
<FileText className="h-3 w-3 shrink-0 text-slate-400 group-hover/btn:text-emerald-600 " />
<span className="max-w-[260px] truncate">{source.title || t("common.untitledMemo")}</span>
<ArrowUpRight className="h-3 w-3 shrink-0 opacity-40 group-hover/btn:opacity-100" />
</Button>
@@ -264,13 +264,13 @@ function DiscoveryCard({ item, busy, open, onApply, onDismiss, onOpenNote, onSee
) : null}
</CardContent>
{!item.action ? (
<CardFooter className="justify-end border-t border-slate-100 p-0 pt-2.5 dark:border-slate-800/80">
<CardFooter className="justify-end border-t border-slate-100 p-0 pt-2.5 ">
<Button
size="sm"
variant="ghost"
disabled={busy}
onClick={onDismiss}
className="h-7 px-2 text-xs text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
className="h-7 px-2 text-xs text-slate-400 hover:text-slate-700 "
>
{t("companion.discovery.dismiss")}
</Button>
+1 -1
View File
@@ -23,7 +23,7 @@ export default function CompanionPane({ available, onBack, onOpenSettings, ...ac
onOpenSettings: () => void;
} & CompanionActionHandlers) {
const { t } = useTranslation();
return <section className="flex h-full min-h-0 flex-col bg-white" aria-labelledby="companion-title">
return <section className="flex h-full min-h-0 flex-col bg-card" aria-labelledby="companion-title">
<header className="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-slate-200 px-4 pb-3 pt-[max(0.75rem,env(safe-area-inset-top))] lg:px-8">
<div className="flex min-w-0 items-center gap-3">
<Button variant="ghost" size="sm" onClick={onBack}>
@@ -24,32 +24,32 @@ export function CompanionToolActionCard({ action, busy, onApply, onDismiss, onOp
return (
<div className="space-y-3 pt-0.5" aria-label={title}>
<div className="space-y-1">
<h4 className="text-xs font-semibold text-slate-900 dark:text-slate-100">{title}</h4>
<p className="line-clamp-3 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 dark:text-slate-300">{action.plan.reason}</p>
<h4 className="text-xs font-semibold text-slate-900 ">{title}</h4>
<p className="line-clamp-3 whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 ">{action.plan.reason}</p>
</div>
{action.status === "pending" ? (
<div className="space-y-2.5">
{action.preview?.affectedCount ? (
<p className="text-xs font-medium text-slate-500 dark:text-slate-400">
<p className="text-xs font-medium text-slate-500 ">
{t("companion.actions.affected", { count: action.preview.affectedCount })}
</p>
) : null}
{action.notes.length > 0 ? (
<div className="space-y-1.5 rounded-lg border border-slate-200/70 bg-slate-50/70 p-2.5 dark:border-slate-800 dark:bg-slate-900/50">
<div className="space-y-1.5 rounded-lg border border-slate-200/70 bg-slate-50/70 p-2.5 ">
{action.notes.map(note => (
<div key={note.id}>
<button
type="button"
disabled={busy}
onClick={() => onOpenNote(note.id, note.notebookId)}
className="inline-flex max-w-full items-center gap-1 text-xs font-medium text-slate-800 hover:text-emerald-600 hover:underline dark:text-slate-200 dark:hover:text-emerald-400"
className="inline-flex max-w-full items-center gap-1 text-xs font-medium text-slate-800 hover:text-emerald-600 hover:underline "
>
<FileText className="h-3 w-3 shrink-0 text-slate-400" />
<span className="truncate">{note.title || t("common.untitledMemo")}</span>
<ArrowUpRight className="h-2.5 w-2.5 shrink-0 opacity-50" />
</button>
{note.excerpt ? (
<p className="line-clamp-1 break-words text-[11px] text-slate-500 dark:text-slate-400">
<p className="line-clamp-1 break-words text-[11px] text-slate-500 ">
{note.excerpt}
</p>
) : null}
@@ -60,30 +60,30 @@ export function CompanionToolActionCard({ action, busy, onApply, onDismiss, onOp
<dl className="space-y-2 text-xs">
{Object.entries(action.plan.arguments).map(([key, value]) => (
<div key={key}>
<dt className="font-medium text-slate-700 dark:text-slate-300">
<dt className="font-medium text-slate-700 ">
{t(`companion.actions.fields.${key}`, { defaultValue: key })}
</dt>
<dd className="mt-1 max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded bg-slate-100/80 p-2 text-[11px] text-slate-800 dark:bg-slate-800 dark:text-slate-200">
<dd className="mt-1 max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded bg-slate-100/80 p-2 text-[11px] text-slate-800 ">
{display(value)}
</dd>
</div>
))}
</dl>
{special ? (
<p className="rounded-lg border border-rose-200/60 bg-rose-50/60 p-2.5 text-xs text-rose-600 dark:border-rose-900/40 dark:bg-rose-950/30 dark:text-rose-400">
<p className="rounded-lg border border-rose-200/60 bg-rose-50/60 p-2.5 text-xs text-rose-600 ">
{t(`companion.actions.effects.${name}`)}
</p>
) : null}
<p className="text-[11px] leading-relaxed text-slate-400 dark:text-slate-500">
<p className="text-[11px] leading-relaxed text-slate-400 ">
{t("companion.actions.toolHelp")}
</p>
<div className="flex items-center justify-end gap-2 border-t border-slate-100 pt-2.5 dark:border-slate-800/80">
<div className="flex items-center justify-end gap-2 border-t border-slate-100 pt-2.5 ">
<Button
size="sm"
variant="ghost"
disabled={busy}
onClick={() => onDismiss(action)}
className="h-7 px-2.5 text-xs text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
className="h-7 px-2.5 text-xs text-slate-500 hover:text-slate-700 "
>
{t("companion.actions.dismiss")}
</Button>
@@ -101,7 +101,7 @@ export function CompanionToolActionCard({ action, busy, onApply, onDismiss, onOp
</div>
) : action.status === "applied" ? (
<div className="space-y-2">
<div className="flex items-center justify-between gap-2 rounded-lg border border-emerald-200/50 bg-emerald-50/60 p-2.5 text-xs text-emerald-700 dark:border-emerald-800/50 dark:bg-emerald-950/30 dark:text-emerald-300">
<div className="flex items-center justify-between gap-2 rounded-lg border border-emerald-200/50 bg-emerald-50/60 p-2.5 text-xs text-emerald-700 ">
<span className="flex items-center gap-1.5 font-medium">
<Check className="h-3.5 w-3.5" />
{t("companion.actions.status.applied")}
@@ -120,17 +120,17 @@ export function CompanionToolActionCard({ action, busy, onApply, onDismiss, onOp
</div>
{action.result ? (
<details className="text-xs text-slate-500">
<summary className="cursor-pointer hover:text-slate-800 dark:hover:text-slate-200">
<summary className="cursor-pointer hover:text-slate-800 ">
{t("companion.actions.receipt")}
</summary>
<pre className="mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded bg-slate-100 p-2 text-[11px] dark:bg-slate-800">
<pre className="mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded bg-slate-100 p-2 text-[11px] ">
{JSON.stringify(action.result, null, 2)}
</pre>
</details>
) : null}
</div>
) : (
<p role="status" className="text-xs text-slate-400 dark:text-slate-500">
<p role="status" className="text-xs text-slate-400 ">
{t(`companion.actions.status.${action.status}`)}
</p>
)}
@@ -63,7 +63,7 @@ export class DesktopRendererErrorBoundary extends React.Component<{ children: Re
return (
<main className="flex min-h-screen items-center justify-center bg-slate-50 p-6 text-slate-900">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-white p-6 shadow-sm" role="alert">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-card p-6 shadow-sm" role="alert">
<div className="mb-4 flex h-11 w-11 items-center justify-center rounded-full bg-rose-50 text-xl text-rose-700">!</div>
<h1 className="text-lg font-semibold">{zh ? "EdgeEver 页面出现异常" : "EdgeEver encountered a page error"}</h1>
<p className="mt-2 text-sm leading-6 text-slate-600">
@@ -85,7 +85,7 @@ export class DesktopRendererErrorBoundary extends React.Component<{ children: Re
</button>
{desktop ? (
<button
className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
className="rounded-lg border border-slate-300 bg-card px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
type="button"
disabled={this.state.reporting}
onClick={() => void this.report()}
+1 -1
View File
@@ -208,7 +208,7 @@ export const DiagramToolbar = ({
const resolvedTheme = themeCatalog === "flowchart" ? resolveFlowchartTheme(theme) : resolveDiagramTheme(theme);
const themeLabel = t(`diagram.theme${resolvedTheme.charAt(0).toUpperCase()}${resolvedTheme.slice(1)}` as "diagram.themeBrand");
return (
<MemoEditorToolbarRow className="shrink-0 border-b border-slate-200 bg-white" role="toolbar" aria-label={t("diagram.toolbar")}>
<MemoEditorToolbarRow className="shrink-0 border-b border-slate-200 bg-card" role="toolbar" aria-label={t("diagram.toolbar")}>
{leading ? <>{leading}<MemoEditorToolbarDivider /></> : null}
<Tooltip><TooltipTrigger asChild><Button size="icon" variant="ghost" aria-label={t("diagram.undo")} disabled={!canUndo || readOnly} onClick={onUndo}><Undo2 className="h-4 w-4" /></Button></TooltipTrigger><TooltipContent>{t("diagram.undo")}</TooltipContent></Tooltip>
<Tooltip><TooltipTrigger asChild><Button size="icon" variant="ghost" aria-label={t("diagram.redo")} disabled={!canRedo || readOnly} onClick={onRedo}><Redo2 className="h-4 w-4" /></Button></TooltipTrigger><TooltipContent>{t("diagram.redo")}</TooltipContent></Tooltip>
+9 -9
View File
@@ -485,7 +485,7 @@ export const EditorPane = (props: EditorPaneProps) => {
if (editingActive) {
return (
<div className="flex h-full min-h-0 items-center justify-center bg-white text-sm font-medium text-slate-400">
<div className="flex h-full min-h-0 items-center justify-center bg-card text-sm font-medium text-slate-400">
{t("editor.openEditor")}
</div>
);
@@ -3550,7 +3550,7 @@ const RichEditorPane = ({
if (isSelectionMode) {
return (
<div className="flex h-full min-w-0 flex-col bg-white">
<div className="flex h-full min-w-0 flex-col bg-card">
{selectionActionBar}
</div>
);
@@ -3558,7 +3558,7 @@ const RichEditorPane = ({
if (isLoading && !memo) {
return (
<div className="flex h-full min-w-0 flex-col bg-white">
<div className="flex h-full min-w-0 flex-col bg-card">
<EmptyEditorHeader />
{selectionActionBar}
<div className="flex min-h-0 flex-1 items-center justify-center text-sm text-slate-500">{t("editor.loading")}</div>
@@ -3568,7 +3568,7 @@ const RichEditorPane = ({
if (!memo) {
return (
<div className="flex h-full min-w-0 flex-col bg-white">
<div className="flex h-full min-w-0 flex-col bg-card">
<EmptyEditorHeader />
{selectionActionBar}
<div className="flex min-h-0 flex-1 items-center justify-center px-8 text-center">
@@ -3773,7 +3773,7 @@ const RichEditorPane = ({
};
return (
<div className="relative flex h-full min-w-0 flex-col bg-white">
<div className="relative flex h-full min-w-0 flex-col bg-card">
{selectionActionBar}
<ExternalLinkDialog
open={externalLinkDialogOpen}
@@ -3796,7 +3796,7 @@ const RichEditorPane = ({
onInsert={insertMemoLink}
/>
)}
<header className="shrink-0 border-b border-slate-200 bg-white">
<header className="shrink-0 border-b border-slate-200 bg-card">
<div className={MEMO_EDITOR_TOP_ROW_CLASS_NAME}>
<MemoEditorTopRowLeading
desktopFocusMode={desktopFocusMode}
@@ -4243,7 +4243,7 @@ const RichEditorPane = ({
: {}),
} as CSSProperties}
className={cn(
"edgeever-editor relative min-h-0 flex-1 bg-white",
"edgeever-editor relative min-h-0 flex-1 bg-card",
useMobilePlainTextEditor
? "overflow-visible"
: useMarkdownSourceEditor
@@ -4317,7 +4317,7 @@ const RichEditorPane = ({
spellCheck
data-edgeever-mobile-editor="plain-textarea"
aria-label={t("editor.noteBodyAria")}
className="block min-h-[60dvh] w-full resize-none border border-slate-200 bg-white px-4 py-3 pr-32 text-base leading-7 text-slate-950 outline-none placeholder:text-slate-400 sm:px-7"
className="block min-h-[60dvh] w-full resize-none border border-slate-200 bg-card px-4 py-3 pr-32 text-base leading-7 text-slate-950 outline-none placeholder:text-slate-400 sm:px-7"
placeholder={t("editor.placeholder")}
style={{ WebkitUserSelect: "text", userSelect: "text", caretColor: "auto" }}
/>
@@ -4330,7 +4330,7 @@ const RichEditorPane = ({
{t("editor.paste")}
</button>
<button
className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-sm font-semibold text-slate-700 shadow-sm"
className="rounded-full border border-slate-200 bg-card px-3 py-1.5 text-sm font-semibold text-slate-700 shadow-sm"
type="button"
onClick={handleMobilePromptInput}
>
@@ -50,7 +50,7 @@ export class EditorPaneErrorBoundary extends React.Component<Props, State> {
return (
<main className="flex h-full min-h-0 items-center justify-center bg-slate-50 p-6 text-slate-900">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-white p-6 shadow-sm" role="alert">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-card p-6 shadow-sm" role="alert">
<div className="mb-4 flex h-11 w-11 items-center justify-center rounded-full bg-amber-50 text-xl text-amber-700">!</div>
<h1 className="text-lg font-semibold">{zh ? "这条笔记暂时无法显示" : "This note cannot be displayed"}</h1>
<p className="mt-2 text-sm leading-6 text-slate-600">
@@ -67,7 +67,7 @@ export class EditorPaneErrorBoundary extends React.Component<Props, State> {
{zh ? "返回笔记列表" : "Back to note list"}
</button>
<button
className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
className="rounded-lg border border-slate-300 bg-card px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
type="button"
onClick={this.retry}
>
@@ -84,7 +84,7 @@ export const EditorRecoveryPane = () => {
const zh = isChineseInterface();
return (
<main className="flex h-full min-h-0 items-center justify-center bg-slate-50 p-6 text-slate-900">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-white p-6 shadow-sm" role="status">
<section className="w-full max-w-lg rounded-2xl border border-slate-200 bg-card p-6 shadow-sm" role="status">
<div className="mb-4 flex h-11 w-11 items-center justify-center rounded-full bg-emerald-50 text-xl text-emerald-700">✓</div>
<h1 className="text-lg font-semibold">{zh ? "已进入安全启动模式" : "Safe startup mode is active"}</h1>
<p className="mt-2 text-sm leading-6 text-slate-600">
+1 -1
View File
@@ -160,7 +160,7 @@ export const EditorTagPicker = ({ contentMarkdown, disabled, loadTags, title, va
{selectedTags.slice(0, 3).map((tag) => (
<span
key={tag}
className="rounded-sm border border-emerald-200/70 bg-emerald-50/70 px-1.5 py-0.5 text-[11px] font-medium text-emerald-800 transition-colors dark:border-emerald-800/50 dark:bg-emerald-950/40 dark:text-emerald-300 sm:text-xs"
className="rounded-sm border border-emerald-200/70 bg-emerald-50/70 px-1.5 py-0.5 text-[11px] font-medium text-emerald-800 transition-colors sm:text-xs"
>
#{tag}
</span>
+10 -10
View File
@@ -279,11 +279,11 @@ export const EditorToolbar = ({
return (
<TooltipProvider delayDuration={0} skipDelayDuration={0}>
<div
className="relative min-w-0 max-w-full border-t border-slate-100 bg-white"
className="relative min-w-0 max-w-full border-t border-slate-100 bg-card"
role="toolbar"
aria-label={t("editorToolbar.toolbar")}
>
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 w-4 bg-gradient-to-r from-white to-transparent sm:hidden" />
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 w-4 bg-gradient-to-r from-card to-transparent sm:hidden" />
<MemoEditorToolbarRow
ref={controlsRef}
className={cn(
@@ -300,7 +300,7 @@ export const EditorToolbar = ({
"flex h-8 shrink-0 items-center rounded-md border px-2.5 text-xs font-medium transition-colors disabled:pointer-events-none disabled:opacity-40",
markdownMode
? "border-emerald-200 bg-emerald-50 text-emerald-800"
: "border-slate-200/80 bg-white text-slate-700 hover:border-slate-300 hover:bg-slate-50"
: "border-slate-200/80 bg-card text-slate-700 hover:border-slate-300 hover:bg-slate-50"
)}
type="button"
aria-label={markdownMode ? t("editorToolbar.richText") : t("editorToolbar.markdown")}
@@ -315,7 +315,7 @@ export const EditorToolbar = ({
<TooltipContent side="bottom" className="flex items-center gap-2">
<span>{markdownMode ? t("editorToolbar.richText") : t("editorToolbar.markdown")}</span>
{markdownModeShortcutLabel && (
<kbd className="rounded border border-white/20 bg-white/10 px-1.5 py-0.5 font-mono text-[10px] leading-none">
<kbd className="rounded border border-border/20 bg-card/10 px-1.5 py-0.5 font-mono text-[10px] leading-none">
{markdownModeShortcutLabel}
</kbd>
)}
@@ -374,10 +374,10 @@ export const EditorToolbar = ({
disabled={disabled}
onValueChange={(value) => setBlock(value)}
>
<SelectTrigger className="h-8 w-20 shrink-0 whitespace-nowrap border-slate-200 bg-white text-xs text-slate-800 [&>span]:truncate [&>span]:whitespace-nowrap">
<SelectTrigger className="h-8 w-20 shrink-0 whitespace-nowrap border-slate-200 bg-card text-xs text-slate-800 [&>span]:truncate [&>span]:whitespace-nowrap">
<SelectValue placeholder={t("editorToolbar.paragraph")} />
</SelectTrigger>
<SelectContent className="bg-white border border-slate-200 rounded-md py-1 shadow-md">
<SelectContent className="bg-card border border-slate-200 rounded-md py-1 shadow-md">
<SelectItem value="paragraph">{t("editorToolbar.paragraph")}</SelectItem>
<SelectItem value="heading-1">{t("editorToolbar.heading1")}</SelectItem>
<SelectItem value="heading-2">{t("editorToolbar.heading2")}</SelectItem>
@@ -485,12 +485,12 @@ export const EditorToolbar = ({
}
>
<SelectTrigger
className="h-8 w-32 shrink-0 whitespace-nowrap border-slate-200 bg-white text-xs text-slate-800 [&>span]:truncate [&>span]:whitespace-nowrap"
className="h-8 w-32 shrink-0 whitespace-nowrap border-slate-200 bg-card text-xs text-slate-800 [&>span]:truncate [&>span]:whitespace-nowrap"
aria-label={t("editorToolbar.codeLanguage")}
>
<SelectValue placeholder={t("editorToolbar.plainText")} />
</SelectTrigger>
<SelectContent className="bg-white border border-slate-200 rounded-md py-1 shadow-md">
<SelectContent className="bg-card border border-slate-200 rounded-md py-1 shadow-md">
{CODE_BLOCK_LANGUAGES.map((language) => (
<SelectItem key={language.value} value={language.value}>
{language.value === "plaintext" ? t("editorToolbar.plainText") : language.label}
@@ -519,11 +519,11 @@ export const EditorToolbar = ({
)}
</MemoEditorToolbarRow>
{hasOverflow && (
<div className="absolute right-3 top-2 z-20 flex h-8 items-center bg-gradient-to-l from-white via-white to-transparent pl-5 sm:right-4 sm:top-0.5">
<div className="absolute right-3 top-2 z-20 flex h-8 items-center bg-gradient-to-l from-card via-card to-transparent pl-5 sm:right-4 sm:top-0.5">
<Tooltip>
<TooltipTrigger asChild>
<button
className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/30"
className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-200 bg-card text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/30"
type="button"
aria-expanded={expanded}
aria-label={t(expanded ? "editorToolbar.showLess" : "editorToolbar.showMore")}
@@ -15,7 +15,7 @@ export const EvernoteImportGuidePane = ({ onClose, onOpenExecutionCenter }: { on
return (
<div className="flex h-full min-h-0 min-w-0 flex-col overflow-x-hidden bg-slate-50">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-white px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-card px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<Button
size="icon"
@@ -41,7 +41,7 @@ export const EvernoteImportGuidePane = ({ onClose, onOpenExecutionCenter }: { on
</header>
<main className="min-w-0 flex-1 overflow-x-hidden overflow-y-auto px-4 py-4 lg:px-6 lg:py-6">
<article className="mx-auto w-full min-w-0 max-w-4xl rounded-lg border border-slate-200 bg-white p-6 shadow-sm sm:p-8">
<article className="mx-auto w-full min-w-0 max-w-4xl rounded-lg border border-slate-200 bg-card p-6 shadow-sm sm:p-8">
<div
className="markdown-content max-w-none"
dangerouslySetInnerHTML={{ __html: htmlContent }}
+5 -5
View File
@@ -48,9 +48,9 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
return (
<main className="flex h-[100dvh] items-center justify-center bg-[var(--workspace-canvas)] px-4 py-8 text-slate-950">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_15%,rgb(var(--brand-green-rgb)/0.045),transparent_42%)]" />
<GitHubRepositoryLink className="absolute right-4 top-[max(1rem,env(safe-area-inset-top))] z-10 h-10 w-10 justify-center rounded-full border border-slate-200 bg-white/85 text-slate-600 shadow-[0_4px_16px_rgb(var(--slate-900-rgb)/0.05)] backdrop-blur transition hover:border-slate-300 hover:bg-white hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/60" iconClassName="h-5 w-5" />
<GitHubRepositoryLink className="absolute right-4 top-[max(1rem,env(safe-area-inset-top))] z-10 h-10 w-10 justify-center rounded-full border border-slate-200 bg-card/85 text-slate-600 shadow-[0_4px_16px_rgb(var(--slate-900-rgb)/0.05)] backdrop-blur transition hover:border-slate-300 hover:bg-card hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/60" iconClassName="h-5 w-5" />
<section className="relative w-full max-w-[400px] rounded-2xl border border-slate-200 bg-white/95 p-8 shadow-[0_20px_50px_rgb(var(--slate-900-rgb)/0.08)] backdrop-blur-md">
<section className="relative w-full max-w-[400px] rounded-2xl border border-slate-200 bg-card/95 p-8 shadow-[0_20px_50px_rgb(var(--slate-900-rgb)/0.08)] backdrop-blur-md">
<div className="mb-8 flex items-center gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-emerald-500 text-white shadow-[0_8px_18px_-8px_rgb(var(--brand-green-rgb)/0.45)]">
<LockKeyhole className="h-5.5 w-5.5" />
@@ -84,7 +84,7 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
<span className="mb-2 block text-sm font-semibold text-slate-700">{t("login.desktopInstanceUrl")}</span>
<Input
autoComplete="url"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-white focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-card focus-visible:ring-2 focus-visible:ring-emerald-500/20"
placeholder={t("login.instanceUrlPlaceholder")}
required
type="url"
@@ -98,7 +98,7 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
<span className="mb-2 block text-sm font-semibold text-slate-700">{t("login.username")}</span>
<Input
autoComplete="username"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-white focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-card focus-visible:ring-2 focus-visible:ring-emerald-500/20"
required
value={username}
onChange={(event) => setUsername(event.target.value)}
@@ -109,7 +109,7 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
<span className="mb-2 block text-sm font-semibold text-slate-700">{t("login.password")}</span>
<Input
autoComplete="current-password"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-white focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-card focus-visible:ring-2 focus-visible:ring-emerald-500/20"
required
type="password"
value={password}
+7 -7
View File
@@ -325,7 +325,7 @@ export const MemoCard = ({
draggable={!isTrashView}
onDragStart={handleDragStart}
className={cn(
"edgeever-memo-divider group relative overflow-hidden border border-slate-100 bg-white transition lg:rounded-none lg:border-x-0 lg:border-t-0 lg:border-slate-200 lg:shadow-none lg:last:border-b-0 transition-all duration-200 select-none",
"edgeever-memo-divider group relative overflow-hidden border border-slate-100 bg-card transition lg:rounded-none lg:border-x-0 lg:border-t-0 lg:border-slate-200 lg:shadow-none lg:last:border-b-0 transition-all duration-200 select-none",
listDensity === "compact" ? "rounded-md shadow-none" : "rounded-lg shadow-[0_4px_16px_rgba(15,23,42,0.045)]",
!selectionMode && selected
? "edgeever-workspace-selection-desktop"
@@ -358,7 +358,7 @@ export const MemoCard = ({
"flex h-6 w-6 items-center justify-center rounded-full border transition-all duration-150 lg:h-4 lg:w-4",
checked
? "border-slate-700 bg-slate-700 text-white shadow-[0_4px_10px_rgba(15,23,42,0.16)]"
: "border-slate-300 bg-white text-transparent"
: "border-slate-300 bg-card text-transparent"
)}
aria-hidden="true"
>
@@ -393,20 +393,20 @@ export const MemoCard = ({
</div>
{diagramLabel ? (
<div className="space-y-1.5">
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-slate-500 dark:text-slate-400">
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-slate-500 ">
<span className="inline-flex items-center gap-1 rounded border border-slate-200 px-1.5 py-0.5">
<DiagramIcon className="h-3 w-3" aria-hidden="true" />{diagramLabel}
</span>
{memo.diagramPreview ? <span>{t("diagram.listCounts", { nodes: memo.diagramPreview.nodeCount, edges: memo.diagramPreview.edgeCount })}</span> : null}
</div>
{listDensity !== "compact" && memo.diagramPreview?.labels.length ? (
<div className="line-clamp-2 text-[13px] leading-relaxed text-slate-600 dark:text-slate-400">{memo.diagramPreview.labels.join(" · ")}</div>
<div className="line-clamp-2 text-[13px] leading-relaxed text-slate-600 ">{memo.diagramPreview.labels.join(" · ")}</div>
) : null}
</div>
) : (
<div
className={cn(
"line-clamp-2 min-h-10 text-[13px] leading-relaxed text-slate-600 dark:text-slate-400",
"line-clamp-2 min-h-10 text-[13px] leading-relaxed text-slate-600 ",
listDensity === "compact" && "line-clamp-1 min-h-0 text-[12.5px]"
)}
>
@@ -414,7 +414,7 @@ export const MemoCard = ({
</div>
)}
<div className={cn("mt-3.5 flex flex-wrap items-center gap-2", listDensity === "compact" && "mt-1.5")}>
<time className="text-xs font-normal text-slate-500 dark:text-slate-400" dateTime={listTimestamp.value}>
<time className="text-xs font-normal text-slate-500 " dateTime={listTimestamp.value}>
{listTimestamp.field === "createdAt"
? t("memoCard.createdAt", { time: listTimestampLabel })
: listTimestampLabel}
@@ -422,7 +422,7 @@ export const MemoCard = ({
{memo.tags.slice(0, 3).map((tag) => (
<span
key={tag}
className="rounded-[3px] border border-emerald-200/60 bg-emerald-50/60 px-1.5 py-0.5 text-[11px] font-medium tracking-tight text-emerald-800 transition-colors dark:border-emerald-800/40 dark:bg-emerald-950/30 dark:text-emerald-300"
className="rounded-[3px] border border-emerald-200/60 bg-emerald-50/60 px-1.5 py-0.5 text-[11px] font-medium tracking-tight text-emerald-800 transition-colors "
>
#{tag}
</span>
@@ -89,7 +89,7 @@ export const MemoEditorHeaderActions = ({
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className={cn("border border-slate-200 bg-white py-1 shadow-md", moreMenuClassName)}
className={cn("border border-slate-200 bg-card py-1 shadow-md", moreMenuClassName)}
>
{textNoteMenuItems}
<DropdownMenuItem
@@ -64,7 +64,7 @@ export const MemoEditorMetadataRow = ({
<SelectTrigger className="h-8 min-w-0 whitespace-nowrap border-transparent bg-transparent px-2 text-sm font-medium text-slate-600 hover:border-slate-200 hover:bg-slate-50 hover:text-slate-900">
<SelectValue placeholder={t("editor.notebookPlaceholder")}>{currentNotebookLabel}</SelectValue>
</SelectTrigger>
<SelectContent className="max-h-60 rounded-md border border-slate-200 bg-white py-1 shadow-md">
<SelectContent className="max-h-60 rounded-md border border-slate-200 bg-card py-1 shadow-md">
{notebookOptions.map((notebook) => (
<SelectItem key={notebook.id} value={notebook.id}>
{notebook.selectLabel}
+18 -18
View File
@@ -157,10 +157,10 @@ export const MemoSelectionActionBar = ({
return (
<div
className="hidden h-full min-h-0 flex-1 items-start justify-start bg-white px-6 py-6 lg:flex lg:px-8 lg:py-8 xl:px-10"
className="hidden h-full min-h-0 flex-1 items-start justify-start bg-card px-6 py-6 lg:flex lg:px-8 lg:py-8 xl:px-10"
data-memo-selection-action-bar
>
<m.div className="w-72 overflow-hidden rounded-md border border-slate-200 bg-white py-1 shadow-lg" {...paneEnterMotion}>
<m.div className="w-72 overflow-hidden rounded-md border border-slate-200 bg-card py-1 shadow-lg" {...paneEnterMotion}>
<div className="flex h-9 items-center gap-2 px-3 text-xs font-semibold text-slate-400">
<CheckSquare className="h-4 w-4" />
{getSelectionCountLabel(selectedCount, t)}
@@ -172,7 +172,7 @@ export const MemoSelectionActionBar = ({
<SelectTrigger className="h-8 min-w-0 flex-1 text-xs text-slate-700 border-slate-200">
<SelectValue placeholder={t("memoList.chooseNotebook")}>{selectedMoveNotebookName}</SelectValue>
</SelectTrigger>
<SelectContent className="max-h-60 bg-white border border-slate-200 rounded-md py-1 shadow-md">
<SelectContent className="max-h-60 bg-card border border-slate-200 rounded-md py-1 shadow-md">
{moveNotebookOptions.map((item) => (
<SelectItem key={item.id} value={item.id}>
{item.selectLabel}
@@ -311,7 +311,7 @@ const MobileSelectionActionBar = ({
return (
<nav
className="fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 px-8 pb-[max(0.125rem,env(safe-area-inset-bottom))] pt-1 shadow-[0_-10px_30px_rgba(15,23,42,0.08)] backdrop-blur lg:hidden"
className="fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-card/95 px-8 pb-[max(0.125rem,env(safe-area-inset-bottom))] pt-1 shadow-[0_-10px_30px_rgba(15,23,42,0.08)] backdrop-blur lg:hidden"
aria-label={t("mobileSheets.bulkActions")}
>
<div className="grid h-14 grid-cols-3 items-center">
@@ -1049,7 +1049,7 @@ export const MemoListPane = ({
</button>
<div
className={cn(
"flex h-9 min-w-0 flex-1 items-center gap-2 rounded-full border bg-white px-3 text-sm shadow-[0_8px_18px_rgba(15,23,42,0.05)] transition",
"flex h-9 min-w-0 flex-1 items-center gap-2 rounded-full border bg-card px-3 text-sm shadow-[0_8px_18px_rgba(15,23,42,0.05)] transition",
searchActive
? "border-emerald-400 bg-emerald-50/80 text-emerald-700 ring-2 ring-emerald-200/70"
: "border-slate-200 text-slate-500"
@@ -1188,7 +1188,7 @@ export const MemoListPane = ({
className={cn(
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border text-xs font-medium transition-all duration-200 outline-none",
filterMode === "all"
? "border-slate-200 bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-900"
? "border-slate-200 bg-card text-slate-600 hover:bg-slate-50 hover:text-slate-900"
: "border-slate-300 bg-slate-100 text-slate-900 hover:bg-slate-200"
)}
title={t("memoList.filterTitle", { label: activeFilterLabel })}
@@ -1196,7 +1196,7 @@ export const MemoListPane = ({
{getMobileFilterIcon(filterMode)}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-44 bg-white border border-slate-200 rounded-md py-1 shadow-md">
<DropdownMenuContent align="start" className="w-44 bg-card border border-slate-200 rounded-md py-1 shadow-md">
{filterOptions.map((option: any) => (
<DropdownMenuItem
key={option.value}
@@ -1216,13 +1216,13 @@ export const MemoListPane = ({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-slate-200 bg-white text-xs font-medium text-slate-600 transition hover:bg-slate-50 hover:text-slate-900 outline-none"
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-slate-200 bg-card text-xs font-medium text-slate-600 transition hover:bg-slate-50 hover:text-slate-900 outline-none"
title={t("memoList.sortTitle", { label: activeSortLabel })}
>
<ArrowDownWideNarrow className="h-4 w-4" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-44 bg-white border border-slate-200 rounded-md py-1 shadow-md">
<DropdownMenuContent align="start" className="w-44 bg-card border border-slate-200 rounded-md py-1 shadow-md">
{memoSortOptions.map((option: any) => (
<DropdownMenuItem
key={option.value}
@@ -1301,7 +1301,7 @@ export const MemoListPane = ({
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40 bg-white border border-slate-200 rounded-md py-1 shadow-md">
<DropdownMenuContent align="end" className="w-40 bg-card border border-slate-200 rounded-md py-1 shadow-md">
<DropdownMenuItem
className="flex h-9 w-full items-center gap-2 px-3 text-left text-sm text-slate-700 hover:bg-slate-50 cursor-pointer outline-none"
onClick={onOpenTags}
@@ -1340,8 +1340,8 @@ export const MemoListPane = ({
className={cn(
"flex h-mobile-control min-w-0 flex-1 items-center gap-2 rounded-full border px-3 text-sm transition-all duration-200 focus-within:ring-2 lg:rounded-md",
searchActive
? "border-emerald-400 bg-emerald-50/80 text-emerald-700 shadow-[0_0_0_1px_rgba(52,211,153,0.18)] ring-1 ring-emerald-200 focus-within:border-emerald-500 focus-within:bg-white focus-within:ring-emerald-300/50"
: "border-slate-200 bg-white text-slate-500 hover:border-slate-300 focus-within:border-emerald-400/90 focus-within:bg-white focus-within:ring-emerald-200/60"
? "border-emerald-400 bg-emerald-50/80 text-emerald-700 shadow-[0_0_0_1px_rgba(52,211,153,0.18)] ring-1 ring-emerald-200 focus-within:border-emerald-500 focus-within:bg-card focus-within:ring-emerald-300/50"
: "border-slate-200 bg-card text-slate-500 hover:border-slate-300 focus-within:border-emerald-400/90 focus-within:bg-card focus-within:ring-emerald-200/60"
)}
>
<Search className={cn("h-4 w-4 shrink-0", searchActive && "text-emerald-600")} />
@@ -1367,7 +1367,7 @@ export const MemoListPane = ({
/>
{search && (
<button
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-slate-400 transition hover:bg-white hover:text-slate-700"
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-slate-400 transition hover:bg-card hover:text-slate-700"
type="button"
title={t("memoList.clearSearch")}
aria-label={t("memoList.clearSearch")}
@@ -1386,7 +1386,7 @@ export const MemoListPane = ({
"flex h-mobile-control w-mobile-control items-center justify-center rounded-full border transition",
filterMode === option.value
? "border-slate-700 bg-slate-700 text-white shadow-[0_8px_18px_rgba(15,23,42,0.16)]"
: "border-slate-200 bg-white text-slate-500 hover:bg-slate-50 hover:text-slate-800"
: "border-slate-200 bg-card text-slate-500 hover:bg-slate-50 hover:text-slate-800"
)}
type="button"
title={filterMode === option.value ? t("memoList.toggleOffFilter", { label: option.label }) : option.label}
@@ -1406,7 +1406,7 @@ export const MemoListPane = ({
"mt-3 flex min-h-8 items-center gap-2 rounded-md border px-3 py-1.5 text-xs",
searchActive
? "border-emerald-200 bg-emerald-50 text-emerald-800 shadow-[inset_3px_0_0_#10b981]"
: "border-slate-200 bg-white text-slate-500"
: "border-slate-200 bg-card text-slate-500"
)}
role="status"
{...contentEnterMotion}
@@ -1457,7 +1457,7 @@ export const MemoListPane = ({
</Button>
</div>
) : memos.length === 0 ? (
<div className="rounded-md border border-dashed border-slate-300 bg-white px-4 py-9 text-center">
<div className="rounded-md border border-dashed border-slate-300 bg-card px-4 py-9 text-center">
<div className="text-sm font-semibold text-slate-800">
{memos.length === 0 ? (view === "trash" ? t("memoList.trashEmptyTitle") : t("memoList.emptyTitle")) : t("memoList.noFilteredTitle")}
</div>
@@ -1476,7 +1476,7 @@ export const MemoListPane = ({
)}
</div>
) : (
<div className="space-y-4 lg:space-y-0 lg:overflow-hidden lg:rounded-sm lg:border-y lg:border-slate-200 lg:bg-white">
<div className="space-y-4 lg:space-y-0 lg:overflow-hidden lg:rounded-sm lg:border-y lg:border-slate-200 lg:bg-card">
<div className="space-y-3 lg:space-y-0">
{memos.map((memo) => (
<MemoCard
@@ -1520,7 +1520,7 @@ export const MemoListPane = ({
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="max-h-[calc(100dvh-1.5rem)] w-56 max-w-[calc(100vw-1.5rem)] overflow-y-auto bg-white border border-slate-200 rounded-md py-1 shadow-md"
className="max-h-[calc(100dvh-1.5rem)] w-56 max-w-[calc(100vw-1.5rem)] overflow-y-auto bg-card border border-slate-200 rounded-md py-1 shadow-md"
data-memo-actions-menu
>
<DropdownMenuItem
+35 -35
View File
@@ -76,7 +76,7 @@ const FIREFOX_CLIPPER_URL = "https://addons.mozilla.org/firefox/addon/edgeever-w
const BrandIconContainer = ({ children, className }: { children: ReactNode; className?: string }) => (
<span
className={cn(
"flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300",
"flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-slate-100 text-slate-700 ",
className
)}
>
@@ -98,7 +98,7 @@ const BrandIcon = ({ path, color, className }: { path: string; color?: string; c
const DiagramBetaBadge = () => (
<Badge
variant="outline"
className="ml-auto border-emerald-200/80 bg-emerald-50 px-1.5 py-0 text-[10px] leading-4 tracking-wide text-emerald-700 dark:border-emerald-800/60 dark:bg-emerald-950/50 dark:text-emerald-300"
className="ml-auto border-emerald-200/80 bg-emerald-50 px-1.5 py-0 text-[10px] leading-4 tracking-wide text-emerald-700 "
>
Beta
</Badge>
@@ -152,7 +152,7 @@ const SidebarNavButton = ({
aria-current={active ? "page" : undefined}
onClick={onClick}
>
<span className={cn("flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-200", active && "text-emerald-600 dark:text-emerald-400")}>{icon}</span>
<span className={cn("flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-200", active && "text-emerald-600 ")}>{icon}</span>
<span className="min-w-0 flex-1 truncate">{label}</span>
</button>
);
@@ -174,7 +174,7 @@ const SidebarShortcutButton = ({
<button
className={cn(
"flex h-9 min-w-0 w-full items-center justify-center rounded-md px-0 text-xs font-medium transition-colors duration-200",
active ? "edgeever-workspace-selection text-emerald-600 dark:text-emerald-400" : "text-slate-600 hover:bg-slate-50 hover:text-slate-950"
active ? "edgeever-workspace-selection text-emerald-600 " : "text-slate-600 hover:bg-slate-50 hover:text-slate-950"
)}
type="button"
aria-current={active ? "page" : undefined}
@@ -213,7 +213,7 @@ const SidebarTrashShortcut = ({
{!active && (
<div className="pointer-events-none absolute right-0 top-full z-20 w-max pt-1 opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100">
<button
className="relative flex h-8 items-center gap-1.5 rounded-md border border-rose-200 bg-white px-2 text-xs font-medium text-rose-700 shadow-lg shadow-slate-900/10 transition-colors before:absolute before:-top-1 before:right-16 before:h-2 before:w-2 before:rotate-45 before:border-l before:border-t before:border-rose-200 before:bg-white hover:bg-rose-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-400/70"
className="relative flex h-8 items-center gap-1.5 rounded-md border border-rose-200 bg-card px-2 text-xs font-medium text-rose-700 shadow-lg shadow-slate-900/10 transition-colors before:absolute before:-top-1 before:right-16 before:h-2 before:w-2 before:rotate-45 before:border-l before:border-t before:border-rose-200 before:bg-card hover:bg-rose-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-400/70"
type="button"
onClick={onEmptyTrash}
>
@@ -282,7 +282,7 @@ const SyncStatusBar = ({
? "border-amber-200 bg-amber-50 text-amber-800"
: hasQueuedWork
? "border-slate-200 bg-slate-50 text-slate-700"
: "border-slate-200 bg-white text-slate-500";
: "border-slate-200 bg-card text-slate-500";
return (
<div
@@ -315,7 +315,7 @@ const SyncStatusBar = ({
</button>
{summary.conflict > 0 && (
<button
className="shrink-0 rounded-md px-2 py-1 text-[11px] font-semibold text-amber-800 transition-colors hover:bg-white/70 disabled:opacity-50"
className="shrink-0 rounded-md px-2 py-1 text-[11px] font-semibold text-amber-800 transition-colors hover:bg-card/70 disabled:opacity-50"
type="button"
disabled={!isOnline || isSyncing}
onClick={onDiscardConflicts}
@@ -328,7 +328,7 @@ const SyncStatusBar = ({
<Tooltip>
<TooltipTrigger asChild>
<button
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md hover:bg-white/70 disabled:opacity-50 transition-colors"
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md hover:bg-card/70 disabled:opacity-50 transition-colors"
type="button"
aria-label={t("notebookPane.syncNow")}
disabled={!isOnline || isSyncing}
@@ -573,7 +573,7 @@ export const NotebookPane = ({
)}
<div className="hidden shrink-0 px-3 pb-4 pt-4 lg:block">
<div className="flex overflow-hidden rounded-2xl border border-slate-200/90 bg-white shadow-[0_5px_16px_rgba(15,23,42,0.06)] transition-shadow duration-200 hover:shadow-[0_7px_20px_rgba(15,23,42,0.09)]">
<div className="flex overflow-hidden rounded-2xl border border-slate-200/90 bg-card shadow-[0_5px_16px_rgba(15,23,42,0.06)] transition-shadow duration-200 hover:shadow-[0_7px_20px_rgba(15,23,42,0.09)]">
<button
className="group flex h-12 min-w-0 flex-1 items-center gap-3 px-3 text-left transition-colors duration-150 hover:bg-slate-50 focus-visible:bg-slate-50 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
type="button"
@@ -742,11 +742,11 @@ export const NotebookPane = ({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="flex h-8 w-full items-center gap-3 rounded-md px-3 text-left text-sm font-medium leading-none text-slate-700 transition-colors duration-200 hover:bg-slate-50 hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400/70 data-[state=open]:bg-slate-100 data-[state=open]:text-slate-950 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white dark:data-[state=open]:bg-slate-800"
className="flex h-8 w-full items-center gap-3 rounded-md px-3 text-left text-sm font-medium leading-none text-slate-700 transition-colors duration-200 hover:bg-slate-50 hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400/70 data-[state=open]:bg-slate-100 data-[state=open]:text-slate-950 "
type="button"
aria-label={t("pwa.sidebarDownloadsTitle") || "下载 EdgeEver 客户端与浏览器插件"}
>
<span className="flex h-4 w-4 shrink-0 items-center justify-center text-emerald-600 dark:text-emerald-400">
<span className="flex h-4 w-4 shrink-0 items-center justify-center text-emerald-600 ">
<Download className="h-4 w-4" />
</span>
<span className="min-w-0 flex-1 truncate">{t("pwa.sidebarDownloads") || "下载客户端"}</span>
@@ -756,10 +756,10 @@ export const NotebookPane = ({
side="top"
align="start"
sideOffset={6}
className="w-64 rounded-lg border border-slate-200 bg-white p-1.5 shadow-xl dark:border-slate-800 dark:bg-slate-900"
className="w-64 rounded-lg border border-slate-200 bg-card p-1.5 shadow-xl "
>
<DropdownMenuGroup>
<DropdownMenuLabel className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-slate-400 dark:text-slate-500">
<DropdownMenuLabel className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-slate-400 ">
{t("pwa.sidebarGroupApps") || "客户端应用"}
</DropdownMenuLabel>
<DropdownMenuItem asChild>
@@ -767,7 +767,7 @@ export const NotebookPane = ({
href={DESKTOP_DOWNLOAD_URL}
target="_blank"
rel="noreferrer"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -775,7 +775,7 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarMac") || "macOS"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">DMG</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
@@ -787,7 +787,7 @@ export const NotebookPane = ({
target="_blank"
rel="noreferrer"
aria-label={`${t("pwa.sidebarLinux")} ${t("pwa.sidebarLinuxBadge")}`}
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -795,8 +795,8 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarLinux") || "Linux"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-amber-700 dark:text-amber-400">
<span className="rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-semibold dark:bg-amber-950/40">
<div className="flex shrink-0 items-center gap-1 text-amber-700 ">
<span className="rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-semibold ">
{t("pwa.sidebarLinuxBadge") || "Preview"}
</span>
<ExternalLink className="h-3.5 w-3.5" />
@@ -809,7 +809,7 @@ export const NotebookPane = ({
target="_blank"
rel="noreferrer"
aria-label={`${t("pwa.sidebarWindows")} ${t("pwa.sidebarWindowsBadge")}`}
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -817,8 +817,8 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarWindows") || "Windows"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-amber-700 dark:text-amber-400">
<span className="rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-semibold dark:bg-amber-950/40">
<div className="flex shrink-0 items-center gap-1 text-amber-700 ">
<span className="rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-semibold ">
{t("pwa.sidebarWindowsBadge") || "Preview"}
</span>
<ExternalLink className="h-3.5 w-3.5" />
@@ -831,7 +831,7 @@ export const NotebookPane = ({
target="_blank"
rel="noreferrer"
aria-label={t("pwa.sidebarAndroidTitle") || "在 Google Play 下载 EdgeEver 安卓端"}
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -839,7 +839,7 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarAndroid") || "Android"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">{t("pwa.sidebarAndroidGooglePlay") || "Google Play"}</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
@@ -850,15 +850,15 @@ export const NotebookPane = ({
href={ANDROID_APK_URL}
target="_blank"
rel="noreferrer"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
<Download className="h-3.5 w-3.5 text-emerald-600 dark:text-emerald-400" />
<Download className="h-3.5 w-3.5 text-emerald-600 " />
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarAndroidApk") || "APK 下载"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">Releases</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
@@ -870,27 +870,27 @@ export const NotebookPane = ({
target="_blank"
rel="noreferrer"
aria-label={t("pwa.sidebarIosTitle") || "在 App Store 下载 EdgeEver iOS 端(仅支持非大陆区 Apple ID)"}
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2">
<BrandIconContainer>
<AppStoreIcon />
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarIos") || "iOS"}</span>
<span className="shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-500 dark:bg-slate-800 dark:text-slate-400">
<span className="shrink-0 rounded bg-slate-100 px-1.5 py-0.5 text-[10px] font-medium text-slate-500 ">
{t("pwa.sidebarIosRegionBadge") || "非大陆区"}
</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">{t("pwa.sidebarIosBadge") || "App Store"}</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
</a>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator className="my-1 bg-slate-100 dark:bg-slate-800" />
<DropdownMenuSeparator className="my-1 bg-slate-100 " />
<DropdownMenuGroup>
<DropdownMenuLabel className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-slate-400 dark:text-slate-500">
<DropdownMenuLabel className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-slate-400 ">
{t("pwa.sidebarGroupClippers") || "浏览器剪藏插件"}
</DropdownMenuLabel>
<DropdownMenuItem asChild>
@@ -898,7 +898,7 @@ export const NotebookPane = ({
href={CHROMIUM_CLIPPER_URL}
target="_blank"
rel="noreferrer"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -909,7 +909,7 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarChromeEdge") || "Chrome / Edge"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">{t("pwa.sidebarWebStoreBadge") || "扩展商店"}</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
@@ -920,7 +920,7 @@ export const NotebookPane = ({
href={FIREFOX_CLIPPER_URL}
target="_blank"
rel="noreferrer"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
className="group flex cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-sm text-slate-700 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus:bg-slate-100 focus:text-slate-900 "
>
<div className="flex min-w-0 items-center gap-2.5">
<BrandIconContainer>
@@ -928,7 +928,7 @@ export const NotebookPane = ({
</BrandIconContainer>
<span className="truncate font-medium">{t("pwa.sidebarFirefox") || "Firefox"}</span>
</div>
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
<div className="flex shrink-0 items-center gap-1 text-slate-400 group-hover:text-slate-600 ">
<span className="text-[11px]">{t("pwa.sidebarAddonsBadge") || "附加组件"}</span>
<ExternalLink className="h-3.5 w-3.5" />
</div>
+3 -3
View File
@@ -262,7 +262,7 @@ export const NotebookTreeItem = ({
}
}}
>
<NotebookIcon className={cn("h-4 w-4 shrink-0 transition-colors duration-200", selected ? "text-emerald-600 dark:text-emerald-400" : hasSelectedDescendant ? "text-slate-700" : "text-slate-500")} />
<NotebookIcon className={cn("h-4 w-4 shrink-0 transition-colors duration-200", selected ? "text-emerald-600 " : hasSelectedDescendant ? "text-slate-700" : "text-slate-500")} />
<span
className={cn(
"truncate font-medium transition-colors duration-200",
@@ -299,7 +299,7 @@ export const NotebookTreeItem = ({
<MoreHorizontal className="h-3.5 w-3.5" />
</button>
{actionsOpen && (
<m.div className="absolute right-0 top-8 z-50 w-44 overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-lg" {...contentEnterMotion}>
<m.div className="absolute right-0 top-8 z-50 w-44 overflow-hidden rounded-md border border-slate-200 bg-card p-1 text-slate-950 shadow-lg" {...contentEnterMotion}>
<button
className="flex h-9 w-full items-center gap-2 rounded-sm px-2 text-left text-sm outline-none hover:bg-slate-100"
type="button"
@@ -352,7 +352,7 @@ export const NotebookTreeItem = ({
)}
</div>
</ContextMenuTrigger>
<ContextMenuContent className="w-48 bg-white border border-slate-200 rounded-md py-1 shadow-md">
<ContextMenuContent className="w-48 bg-card border border-slate-200 rounded-md py-1 shadow-md">
<ContextMenuItem
className="flex h-9 items-center gap-2 px-3 text-sm text-slate-700 hover:bg-slate-50 cursor-pointer outline-none"
onClick={() => onCreateNotebook(node.id)}
@@ -34,7 +34,7 @@ export const PluginMarketplacePane = ({
return (
<div className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-slate-50">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-white px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-card px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<Button
size="icon"
+2 -2
View File
@@ -174,7 +174,7 @@ export const PublicSharePage = () => {
if (!share) {
return (
<main className="flex min-h-[100dvh] items-center justify-center bg-slate-50 px-5">
<section className="max-w-md rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm">
<section className="max-w-md rounded-2xl border border-slate-200 bg-card p-8 text-center shadow-sm">
<FileText className="mx-auto h-9 w-9 text-slate-400" />
<h1 className="mt-4 text-xl font-semibold text-slate-900">{t("sharing.publicUnavailable")}</h1>
<p className="mt-2 text-sm leading-6 text-slate-500">{t("sharing.publicUnavailableHint")}</p>
@@ -185,7 +185,7 @@ export const PublicSharePage = () => {
return (
<main className="edgeever-public-share min-h-[100dvh] bg-slate-50 px-4 py-6 sm:px-8 sm:py-10">
<article className="mx-auto max-w-4xl overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
<article className="mx-auto max-w-4xl overflow-hidden rounded-2xl border border-slate-200 bg-card shadow-sm">
<header className="border-b border-slate-200 px-5 py-6 sm:px-10 sm:py-8">
<div className="mb-5 flex items-center justify-between gap-4 text-xs text-slate-500">
<span className="flex items-center gap-1.5 font-semibold text-emerald-700">
+3 -3
View File
@@ -13,7 +13,7 @@ export const PwaIosPrompt = () => {
return (
<div className="fixed bottom-6 left-4 right-4 z-50 mx-auto max-w-md animate-in fade-in slide-in-from-bottom-5 duration-300">
<div className="relative overflow-hidden rounded-2xl border border-slate-200/80 bg-white/95 p-4 shadow-[0_12px_30px_rgba(15,23,42,0.08)] backdrop-blur-md">
<div className="relative overflow-hidden rounded-2xl border border-slate-200/80 bg-card/95 p-4 shadow-[0_12px_30px_rgba(15,23,42,0.08)] backdrop-blur-md">
{/* Decorative top accent line */}
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-emerald-500 to-teal-500" />
@@ -43,7 +43,7 @@ export const PwaIosPrompt = () => {
<div className="mt-3.5 flex flex-col gap-2 rounded-lg bg-slate-50 p-2.5 text-xs text-slate-600">
<div className="flex items-center gap-2">
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-white border border-slate-200/60 shadow-sm text-slate-500 shrink-0">
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-card border border-slate-200/60 shadow-sm text-slate-500 shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
<polyline points="16 6 12 2 8 6" />
@@ -55,7 +55,7 @@ export const PwaIosPrompt = () => {
</span>
</div>
<div className="flex items-center gap-2">
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-white border border-slate-200/60 shadow-sm text-slate-500 shrink-0">
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-card border border-slate-200/60 shadow-sm text-slate-500 shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
<line x1="12" y1="8" x2="12" y2="16" />
+1 -1
View File
@@ -106,7 +106,7 @@ export const PwaUpdateNotice = () => {
return (
<div
className={cn(
"fixed z-[80] max-w-[calc(100vw-2rem)] rounded-lg border border-slate-200 bg-white/95 p-3 text-slate-900 shadow-[0_18px_48px_rgba(15,23,42,0.16)] backdrop-blur",
"fixed z-[80] max-w-[calc(100vw-2rem)] rounded-lg border border-slate-200 bg-card/95 p-3 text-slate-900 shadow-[0_18px_48px_rgba(15,23,42,0.16)] backdrop-blur",
displayMode.isDesktop
? "right-5 top-5 w-[22rem]"
: "inset-x-4 bottom-[calc(4rem+env(safe-area-inset-bottom))]"
+5 -6
View File
@@ -71,9 +71,8 @@ interface SettingsPaneProps {
}
// Slate and brand color variables already switch values with the root theme.
// Keep this pane on the base utilities so dark variants do not invert them twice.
const SettingsGroup = ({ children }: { children: ReactNode }) => (
<div className="min-w-0 divide-y divide-slate-200 overflow-hidden rounded-xl border border-slate-200 bg-white [&>*]:rounded-none [&>*]:border-0 [&>*]:bg-transparent">
<div className="min-w-0 divide-y divide-slate-200 overflow-hidden rounded-xl border border-slate-200 bg-card [&>*]:rounded-none [&>*]:border-0 [&>*]:bg-transparent">
{children}
</div>
);
@@ -309,7 +308,7 @@ export const SettingsPane = ({
return (
<div className="flex h-full min-h-0 min-w-0 flex-col overflow-x-hidden bg-slate-50">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-white px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<header className="flex h-[calc(3.5rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 bg-card px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<Button
size="icon"
@@ -386,7 +385,7 @@ export const SettingsPane = ({
{activeMobileTab === null ? (
/* 分类主菜单 */
<div className="grid gap-2">
<div className="overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div className="overflow-hidden rounded-2xl border border-slate-200 bg-card">
<button
type="button"
onClick={onOpenTemplates}
@@ -414,7 +413,7 @@ export const SettingsPane = ({
<ChevronRight className="h-4 w-4 text-slate-400" />
</button>
</div>
<div className="divide-y divide-slate-100 overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div className="divide-y divide-slate-100 overflow-hidden rounded-2xl border border-slate-200 bg-card">
{tabItems.map((item) => {
const Icon = item.icon;
return (
@@ -440,7 +439,7 @@ export const SettingsPane = ({
);
})}
</div>
<div className="divide-y divide-slate-100 overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div className="divide-y divide-slate-100 overflow-hidden rounded-2xl border border-slate-200 bg-card">
<button
type="button"
onClick={() => setSystemInfoOpen(true)}
+2 -2
View File
@@ -51,7 +51,7 @@ export const TagsPane = ({
};
return (
<div className="flex h-full min-h-0 min-w-0 flex-col bg-white">
<div className="flex h-full min-h-0 min-w-0 flex-col bg-card">
<header className="flex h-[calc(4rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200 px-6 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<Button size="icon" variant="ghost" title={t("common.back")} aria-label={t("common.back")} onClick={onClose} className="h-9 w-9 rounded-lg hover:bg-slate-100">
@@ -77,7 +77,7 @@ export const TagsPane = ({
const isEditing = editingTagName === tag.name;
const nextName = editingTagValue.trim();
return (
<div key={tag.name} className={cn("flex min-h-12 items-center gap-3 rounded-md border border-slate-200 bg-white px-3 py-2", isEditing && "border-emerald-200 bg-emerald-50/30")}>
<div key={tag.name} className={cn("flex min-h-12 items-center gap-3 rounded-md border border-slate-200 bg-card px-3 py-2", isEditing && "border-emerald-200 bg-emerald-50/30")}>
{isEditing ? (
<div className="min-w-0 flex-1">
<form className="flex min-w-0 flex-col gap-2 sm:flex-row sm:items-center" onSubmit={(event) => { event.preventDefault(); if (nextName && nextName !== tag.name && !renameMutation.isPending) renameMutation.mutate({ tag: tag.name, name: nextName }); }}>
+11 -11
View File
@@ -161,7 +161,7 @@ export const TemplatesPane = ({
<TooltipProvider>
<div className="flex h-full min-h-0 min-w-0 flex-col bg-slate-50/60">
{/* Header */}
<header className="flex h-[calc(3.75rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200/80 bg-white px-6 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:pb-0 lg:pt-0 shadow-2xs">
<header className="flex h-[calc(3.75rem+env(safe-area-inset-top))] shrink-0 items-end justify-between border-b border-slate-200/80 bg-card px-6 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:pb-0 lg:pt-0 shadow-2xs">
<div className="flex min-w-0 items-center gap-3">
<Tooltip>
<TooltipTrigger asChild>
@@ -203,7 +203,7 @@ export const TemplatesPane = ({
type="button"
size="sm"
variant="outline"
className="gap-1.5 border-slate-200 bg-white text-slate-700 hover:bg-slate-50 hover:text-slate-900 text-xs font-semibold shadow-2xs"
className="gap-1.5 border-slate-200 bg-card text-slate-700 hover:bg-slate-50 hover:text-slate-900 text-xs font-semibold shadow-2xs"
onClick={startCreating}
disabled={isCreating}
>
@@ -215,7 +215,7 @@ export const TemplatesPane = ({
{/* Form for Creating / Editing Template */}
{(editingTemplate || creatingTemplate) && (
<div className="mb-6 rounded-xl border border-emerald-200 bg-white p-5 shadow-xs transition-all">
<div className="mb-6 rounded-xl border border-emerald-200 bg-card p-5 shadow-xs transition-all">
<div className="mb-4 flex items-center justify-between border-b border-slate-100 pb-3">
<h3 className="flex items-center gap-2 text-sm font-bold text-slate-900">
<Pencil className="h-4 w-4 text-emerald-600" />
@@ -272,14 +272,14 @@ export const TemplatesPane = ({
<div className="inline-flex rounded-lg bg-slate-100 p-0.5 text-xs font-medium text-slate-600">
<button
type="button"
className={`rounded-md px-2.5 py-1 transition ${editorTab === "raw" ? "bg-white text-slate-900 shadow-2xs" : "hover:text-slate-900"}`}
className={`rounded-md px-2.5 py-1 transition ${editorTab === "raw" ? "bg-card text-slate-900 shadow-2xs" : "hover:text-slate-900"}`}
onClick={() => setEditorTab("raw")}
>
{t("templates.rawEditor")}
</button>
<button
type="button"
className={`rounded-md px-2.5 py-1 transition ${editorTab === "preview" ? "bg-white text-slate-900 shadow-2xs" : "hover:text-slate-900"}`}
className={`rounded-md px-2.5 py-1 transition ${editorTab === "preview" ? "bg-card text-slate-900 shadow-2xs" : "hover:text-slate-900"}`}
onClick={() => setEditorTab("preview")}
>
{t("templates.previewEditor")}
@@ -289,7 +289,7 @@ export const TemplatesPane = ({
{editorTab === "raw" ? (
<textarea
className="min-h-48 w-full resize-y rounded-lg border border-slate-200 bg-white p-3.5 font-mono text-xs text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="min-h-48 w-full resize-y rounded-lg border border-slate-200 bg-card p-3.5 font-mono text-xs text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
value={draft.contentMarkdown}
onChange={(e) => setDraft((c) => ({ ...c, contentMarkdown: e.target.value }))}
placeholder={t("templates.contentPlaceholder")}
@@ -331,7 +331,7 @@ export const TemplatesPane = ({
{savedTemplates.map((template) => (
<div
key={template.id}
className="group relative flex flex-col justify-between rounded-xl border border-slate-200 bg-white p-4 transition-all duration-200 hover:border-emerald-300 hover:shadow-md cursor-pointer"
className="group relative flex flex-col justify-between rounded-xl border border-slate-200 bg-card p-4 transition-all duration-200 hover:border-emerald-300 hover:shadow-md cursor-pointer"
onClick={() =>
setPreviewTemplate({
title: template.name,
@@ -439,7 +439,7 @@ export const TemplatesPane = ({
))}
</div>
) : !creatingTemplate && !editingTemplate ? (
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-slate-200 bg-white p-7 text-center shadow-2xs">
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-slate-200 bg-card p-7 text-center shadow-2xs">
<div className="mb-2.5 flex h-11 w-11 items-center justify-center rounded-full bg-emerald-50 text-emerald-600">
<Sparkles className="h-5 w-5" />
</div>
@@ -464,7 +464,7 @@ export const TemplatesPane = ({
{/* Preview Dialog */}
{previewTemplate && (
<Dialog open={true} onOpenChange={(open) => { if (!open) setPreviewTemplate(null); }}>
<DialogContent className="max-w-xl bg-white p-0 overflow-hidden border border-slate-200 rounded-xl shadow-xl">
<DialogContent className="max-w-xl bg-card p-0 overflow-hidden border border-slate-200 rounded-xl shadow-xl">
<DialogHeader className="border-b border-slate-100 px-6 py-4 text-left bg-slate-50/50">
<DialogTitle className="text-base font-bold text-slate-900">{previewTemplate.title}</DialogTitle>
{previewTemplate.description && (
@@ -484,7 +484,7 @@ export const TemplatesPane = ({
<div>
<h4 className="mb-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">{t("templates.previewTitle")}</h4>
<div className="rounded-xl border border-slate-200 bg-white p-4 text-xs text-slate-800 shadow-2xs min-h-[120px]">
<div className="rounded-xl border border-slate-200 bg-card p-4 text-xs text-slate-800 shadow-2xs min-h-[120px]">
<div className="prose prose-xs max-w-none" dangerouslySetInnerHTML={{ __html: previewHtmlContent }} />
</div>
</div>
@@ -516,7 +516,7 @@ export const TemplatesPane = ({
{/* Delete Confirmation Dialog */}
{deleteConfirmTemplate && (
<Dialog open={true} onOpenChange={(open) => { if (!open) setDeleteConfirmTemplate(null); }}>
<DialogContent className="max-w-md bg-white p-6 border border-slate-200 rounded-xl shadow-xl">
<DialogContent className="max-w-md bg-card p-6 border border-slate-200 rounded-xl shadow-xl">
<DialogHeader className="text-left">
<DialogTitle className="text-base font-bold text-slate-900">
{t("templates.deleteConfirmTitle", { name: deleteConfirmTemplate.name })}
+1 -1
View File
@@ -183,7 +183,7 @@ export const getStoredMarkdownTheme = (): MarkdownThemePreference => {
const stored = readLocalStorageItem(MARKDOWN_THEME_STORAGE_KEY);
return MARKDOWN_THEME_PREFERENCES.includes(stored as MarkdownThemePreference)
? (stored as MarkdownThemePreference)
: "tokyo-night";
: "auto";
};
export const resolveMarkdownTheme = (
+5 -5
View File
@@ -173,7 +173,7 @@ const ExecutionCenterPane = lazy(() =>
);
const PaneLoadingFallback = ({ label = "Loading" }: { label?: string }) => (
<div className="flex h-full min-h-0 items-center justify-center bg-white text-sm font-medium text-slate-400" role="status">
<div className="flex h-full min-h-0 items-center justify-center bg-card text-sm font-medium text-slate-400" role="status">
{label}
</div>
);
@@ -381,7 +381,7 @@ const MobileBottomNav = ({
return (
<nav
className="fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 px-5 pb-[max(0.125rem,env(safe-area-inset-bottom))] pt-0 shadow-[0_-10px_30px_rgba(15,23,42,0.08)] backdrop-blur lg:hidden"
className="fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-card/95 px-5 pb-[max(0.125rem,env(safe-area-inset-bottom))] pt-0 shadow-[0_-10px_30px_rgba(15,23,42,0.08)] backdrop-blur lg:hidden"
aria-label={t("nav.mobileMain")}
>
<div className="relative grid h-mobile-bottom-nav grid-cols-3 items-center">
@@ -516,7 +516,7 @@ const MobileNotebookPicker = ({
/>
{notebookSearch && (
<button
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-slate-400 transition hover:bg-white hover:text-slate-700"
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-slate-400 transition hover:bg-card hover:text-slate-700"
type="button"
title={t("mobileNotebookPicker.clearSearch")}
aria-label={t("mobileNotebookPicker.clearSearch")}
@@ -3261,7 +3261,7 @@ export const WorkspaceApp = ({
style={{ transform: `translateY(${Math.max(0, pullToRefreshDistance - 24)}px)` }}
aria-hidden="true"
>
<div className="inline-flex h-9 items-center gap-2 rounded-full border border-slate-200 bg-white/95 px-3 text-xs font-semibold text-slate-600 shadow-[0_10px_28px_rgba(15,23,42,0.12)] backdrop-blur">
<div className="inline-flex h-9 items-center gap-2 rounded-full border border-slate-200 bg-card/95 px-3 text-xs font-semibold text-slate-600 shadow-[0_10px_28px_rgba(15,23,42,0.12)] backdrop-blur">
<RefreshCw className={cn("h-4 w-4 text-slate-500", (isPullRefreshing || pullToRefreshReady) && "animate-spin")} />
<span>{pullToRefreshLabel}</span>
</div>
@@ -3570,7 +3570,7 @@ export const WorkspaceApp = ({
) : rendererRecoveryMode ? (
<EditorRecoveryPane />
) : memoSelectionModeActive ? (
<div className="flex h-full min-w-0 flex-col bg-white">
<div className="flex h-full min-w-0 flex-col bg-card">
{memoSelectionActionBar}
</div>
) : (
@@ -580,7 +580,7 @@ export const AiAssistantDialog = ({
<section
ref={assignPanelRef}
aria-label={t("aiAssistant.title")}
className="fixed z-[70] flex max-h-[70dvh] w-[min(36rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-xl border border-slate-200 bg-white p-4 shadow-2xl ring-1 ring-slate-950/5"
className="fixed z-[70] flex max-h-[70dvh] w-[min(36rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-xl border border-slate-200 bg-card p-4 shadow-2xl ring-1 ring-slate-950/5"
role="dialog"
style={panelStyle}
onKeyDown={(event) => {
@@ -682,7 +682,7 @@ export const AiAssistantDialog = ({
<Button type="button" variant="solid" className="h-10 min-w-0 w-full gap-1.5 whitespace-nowrap px-3 text-sm font-semibold" disabled={generateDisabled} onClick={() => void generate()}>
<Sparkles className="h-4 w-4 shrink-0" />
{t("aiAssistant.generate")}
<kbd aria-hidden="true" className="ml-0.5 rounded bg-white/10 px-1 py-0.5 text-[10px] font-medium leading-none text-white/65">
<kbd aria-hidden="true" className="ml-0.5 rounded bg-card/10 px-1 py-0.5 text-[10px] font-medium leading-none text-white/65">
↵
</kbd>
</Button>
@@ -739,7 +739,7 @@ export const AiAssistantDialog = ({
{t(isFreeformCustom ? "aiAssistant.customInstruction" : "aiAssistant.inputContent")}
<textarea
ref={instructionRef}
className="min-h-24 resize-y rounded-md border border-slate-200 bg-white px-3 py-2 text-sm font-normal text-slate-900 outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-500/15"
className="min-h-24 resize-y rounded-md border border-slate-200 bg-card px-3 py-2 text-sm font-normal text-slate-900 outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-500/15"
value={customInstruction}
onChange={(event) => {
handleComposerChange(event.target.value);
@@ -861,11 +861,11 @@ export const AiAssistantDialog = ({
</div>
</div>
{output && !isGenerating ? (
<div className="order-4 grid gap-1.5 rounded-lg border border-slate-200 bg-white p-3">
<div className="order-4 grid gap-1.5 rounded-lg border border-slate-200 bg-card p-3">
<span className="text-sm font-medium text-slate-700">{t("aiAssistant.refine")}</span>
<div className="flex flex-col gap-2 sm:flex-row">
<input
className="h-10 min-w-0 flex-1 rounded-md border border-slate-200 bg-white px-3 text-sm outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-500/15"
className="h-10 min-w-0 flex-1 rounded-md border border-slate-200 bg-card px-3 text-sm outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-500/15"
value={refinement}
onChange={(event) => setRefinement(event.target.value)}
aria-label={t("aiAssistant.refine")}
@@ -50,7 +50,7 @@ export const AppConfirmDialog = ({
return (
<Dialog open={true} onOpenChange={(open) => { if (!open && !isWorking) onCancel(); }}>
<DialogContent className="max-w-md p-0 overflow-hidden border border-slate-200 bg-white shadow-lg rounded-lg">
<DialogContent className="max-w-md p-0 overflow-hidden border border-slate-200 bg-card shadow-lg rounded-lg">
<DialogHeader className="flex flex-row items-start gap-4 border-b border-slate-200 px-5 py-5 text-left">
<span className={cn("flex h-10 w-10 shrink-0 items-center justify-center rounded-full", toneClassName)}>
<Icon className="h-5 w-5" />
@@ -150,7 +150,7 @@ export const NotebookNameDialog = ({
return (
<Dialog open={true} onOpenChange={(open) => { if (!open && !isSaving) onCancel(); }}>
<DialogContent className="max-w-md p-0 overflow-hidden border border-slate-200 bg-white shadow-lg rounded-lg">
<DialogContent className="max-w-md p-0 overflow-hidden border border-slate-200 bg-card shadow-lg rounded-lg">
<form
onSubmit={(event) => {
event.preventDefault();
@@ -82,7 +82,7 @@ export const RevisionHistoryDialog = ({
return (
<Dialog open={true} onOpenChange={(open) => { if (!open && !restoreRevisionConfirmationId) onClose(); }}>
<DialogContent className="grid max-h-[88dvh] max-w-[1120px] grid-rows-[auto_minmax(0,1fr)] gap-0 overflow-hidden rounded-lg border border-slate-200 bg-white p-0 shadow-xl">
<DialogContent className="grid max-h-[88dvh] max-w-[1120px] grid-rows-[auto_minmax(0,1fr)] gap-0 overflow-hidden rounded-lg border border-slate-200 bg-card p-0 shadow-xl">
<DialogHeader className="border-b border-slate-200 px-5 py-4 pr-12 text-left">
<div className="min-w-0">
<DialogTitle className="flex items-center gap-2 text-lg font-semibold text-slate-950">
@@ -95,8 +95,8 @@ export const RevisionHistoryDialog = ({
</div>
</DialogHeader>
<div className="flex min-h-0 flex-col bg-white">
<div className="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-slate-200 bg-white px-5 py-3">
<div className="flex min-h-0 flex-col bg-card">
<div className="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-slate-200 bg-card px-5 py-3">
<div className="min-w-0 flex flex-wrap items-center gap-2">
<div className="text-sm font-semibold text-slate-900">
{selectedRevision ? t("revisions.compareTitle", { revision: selectedRevision.revision }) : t("revisions.noRevisionSelected")}
@@ -147,7 +147,7 @@ export const RevisionHistoryDialog = ({
"group flex flex-col w-full rounded-lg border p-3 text-left transition-all duration-200",
selectedRevision?.id === revision.id
? "border-emerald-200 bg-emerald-50/30 shadow-sm ring-1 ring-emerald-100/50"
: "border-slate-100/80 bg-white/60 hover:border-slate-200 hover:bg-slate-50/80"
: "border-slate-100/80 bg-card/60 hover:border-slate-200 hover:bg-slate-50/80"
)}
onClick={() => setSelectedRevisionId(revision.id)}
>
@@ -245,7 +245,7 @@ export const ShareNoteImageDialog = ({
"group relative flex items-center gap-2.5 rounded-lg border p-2 text-left text-xs font-medium transition-all outline-none focus-visible:ring-2 focus-visible:ring-emerald-500",
isSelected
? "border-emerald-500 bg-emerald-50/40 text-emerald-950 ring-1 ring-emerald-500"
: "border-slate-200 bg-white text-slate-700 hover:border-slate-300 hover:bg-slate-50/70",
: "border-slate-200 bg-card text-slate-700 hover:border-slate-300 hover:bg-slate-50/70",
)}
type="button"
onClick={() => setTheme(item.id)}
@@ -276,7 +276,7 @@ export const ShareNoteImageDialog = ({
"h-8 rounded-md border text-xs font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-emerald-500",
fontStyle === item.id
? "border-emerald-500 bg-emerald-50 text-emerald-900 font-semibold"
: "border-slate-200 bg-white text-slate-600 hover:bg-slate-50",
: "border-slate-200 bg-card text-slate-600 hover:bg-slate-50",
)}
type="button"
onClick={() => setFontStyle(item.id)}
@@ -298,7 +298,7 @@ export const ShareNoteImageDialog = ({
"h-7 rounded border text-xs font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-emerald-500",
fontSize === item.id
? "border-emerald-500 bg-emerald-50/80 text-emerald-900 font-semibold"
: "border-slate-200 bg-white text-slate-600 hover:bg-slate-50",
: "border-slate-200 bg-card text-slate-600 hover:bg-slate-50",
)}
type="button"
onClick={() => setFontSize(item.id)}
@@ -321,7 +321,7 @@ export const ShareNoteImageDialog = ({
"h-7 rounded border text-xs font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-emerald-500",
cardWidth === item.id
? "border-emerald-500 bg-emerald-50/80 text-emerald-900 font-semibold"
: "border-slate-200 bg-white text-slate-600 hover:bg-slate-50",
: "border-slate-200 bg-card text-slate-600 hover:bg-slate-50",
)}
type="button"
onClick={() => setCardWidth(item.id)}
@@ -54,7 +54,7 @@ export const TemplatesDialog = ({
return (
<Dialog open={true} onOpenChange={(open) => { if (!open && !isCreating) onClose(); }}>
<DialogContent className="max-w-[620px] p-0 overflow-hidden border border-slate-200 bg-white shadow-lg rounded-lg">
<DialogContent className="max-w-[620px] p-0 overflow-hidden border border-slate-200 bg-card shadow-lg rounded-lg">
<DialogHeader className="flex flex-row items-start justify-between gap-3 border-b border-slate-200 px-5 py-4 text-left">
<div className="min-w-0">
<DialogTitle className="flex items-center gap-2 text-base font-semibold text-slate-950">
@@ -72,7 +72,7 @@ export const TemplatesDialog = ({
<section>
<h3 className="mb-2 text-xs font-bold text-slate-900">{t("templates.myTemplates")}</h3>
{editingTemplate && (
<div className="mb-4 space-y-3 rounded-xl border border-emerald-200/80 bg-white p-4 shadow-xs">
<div className="mb-4 space-y-3 rounded-xl border border-emerald-200/80 bg-card p-4 shadow-xs">
<h4 className="text-xs font-semibold text-slate-800">{t("templates.editTemplateTitle")}</h4>
<div className="grid gap-2.5 sm:grid-cols-2">
<Input value={draft.name} onChange={(event) => setDraft((current) => ({ ...current, name: event.target.value }))} placeholder={t("templates.namePlaceholder")} />
@@ -81,7 +81,7 @@ export const TemplatesDialog = ({
<Input value={draft.tags} onChange={(event) => setDraft((current) => ({ ...current, tags: event.target.value }))} placeholder={t("templates.tagsPlaceholder")} />
</div>
<textarea
className="min-h-36 w-full resize-y rounded-lg border border-slate-200 bg-white p-3 font-mono text-xs text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
className="min-h-36 w-full resize-y rounded-lg border border-slate-200 bg-card p-3 font-mono text-xs text-slate-900 outline-none focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20"
value={draft.contentMarkdown}
onChange={(event) => setDraft((current) => ({ ...current, contentMarkdown: event.target.value }))}
placeholder={t("templates.contentPlaceholder")}
@@ -94,7 +94,7 @@ export const TemplatesDialog = ({
)}
<div className="grid gap-3 sm:grid-cols-2">
{savedTemplates.map((template) => (
<div key={template.id} className="rounded-xl border border-slate-200 bg-white p-3.5 shadow-2xs hover:border-emerald-200 transition">
<div key={template.id} className="rounded-xl border border-slate-200 bg-card p-3.5 shadow-2xs hover:border-emerald-200 transition">
<button
className="block w-full text-left disabled:opacity-50"
type="button"
@@ -32,7 +32,7 @@ export const EditorNoteLinkPicker = ({
return (
<div
className="absolute left-3 right-3 top-14 z-30 h-[min(22rem,calc(100%-4rem))] max-w-xl rounded-lg border border-slate-200 bg-white shadow-xl sm:left-5 sm:right-auto sm:w-[28rem]"
className="absolute left-3 right-3 top-14 z-30 h-[min(22rem,calc(100%-4rem))] max-w-xl rounded-lg border border-slate-200 bg-card shadow-xl sm:left-5 sm:right-auto sm:w-[28rem]"
role="dialog"
aria-label={t("noteLinkPicker.title")}
>
@@ -50,7 +50,7 @@ export const NoteLinkInteractionHint = ({
}) => createPortal(
<div
role="tooltip"
className="pointer-events-none fixed z-[100] whitespace-nowrap rounded-md bg-slate-950 px-2.5 py-1.5 text-xs font-medium text-white shadow-md"
className="pointer-events-none fixed z-[100] whitespace-nowrap rounded-md bg-[var(--tooltip-bg)] px-2.5 py-1.5 text-xs font-medium text-[var(--tooltip-fg)] shadow-md"
style={{
left: position.left,
top: position.top,
@@ -107,7 +107,7 @@ export const ResourceActionMenu = ({
data-edgeever-resource-menu
role="toolbar"
aria-label={labels.download}
className="fixed z-[110] flex items-center gap-1 rounded-lg border border-slate-200 bg-white p-1 shadow-lg"
className="fixed z-[110] flex items-center gap-1 rounded-lg border border-slate-200 bg-card p-1 shadow-lg"
style={imageElement ? {
...floatingStyles,
visibility: isPositioned ? "visible" : "hidden",
@@ -30,7 +30,7 @@ const FileAttachmentNodeView = ({ node }: NodeViewProps) => {
return (
<NodeViewWrapper as="span" className="edgeever-file-attachment-node" contentEditable={false}>
<span className={cn("edgeever-file-viewer flex min-h-12 flex-col overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm", COMPACT_ATTACHMENT_WIDTH_CLASS)}>
<span className={cn("edgeever-file-viewer flex min-h-12 flex-col overflow-hidden rounded-xl border border-slate-200 bg-card shadow-sm", COMPACT_ATTACHMENT_WIDTH_CLASS)}>
<span className="flex min-h-12 items-center gap-2 px-3">
<a
href={resolvedUrl}
@@ -55,7 +55,7 @@ const PluginEmbedNodeView = ({ node, host }: NodeViewProps & { host: EdgeEverPlu
const fallbackTitle = attributes?.title || attributes?.type || "Plugin embed";
return (
<NodeViewWrapper className="edgeever-plugin-embed-node" contentEditable={false}>
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white">
<div className="overflow-hidden rounded-xl border border-slate-200 bg-card">
{registered ? <div ref={containerRef} className="min-h-32" /> : (
<div className="flex min-h-24 items-center justify-center bg-slate-50 px-4 text-sm font-medium text-slate-500">
{fallbackTitle}
@@ -45,7 +45,7 @@ export const ExecutionCenterPane = ({
return (
<div className="flex h-full min-h-0 min-w-0 flex-col bg-slate-50">
<header className="flex h-[calc(4rem+env(safe-area-inset-top))] shrink-0 items-end justify-between gap-3 border-b border-slate-200 bg-white px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<header className="flex h-[calc(4rem+env(safe-area-inset-top))] shrink-0 items-end justify-between gap-3 border-b border-slate-200 bg-card px-4 pb-3 pt-[env(safe-area-inset-top)] lg:h-16 lg:items-center lg:px-6 lg:pb-0 lg:pt-0">
<div className="flex min-w-0 items-center gap-3">
<Button
type="button"
@@ -87,7 +87,7 @@ export const ExecutionCenterPane = ({
</Button>
</div>
) : items.length === 0 ? (
<div className="flex min-h-56 flex-col items-center justify-center gap-2 rounded-xl border border-dashed border-slate-200 bg-white text-center text-slate-400">
<div className="flex min-h-56 flex-col items-center justify-center gap-2 rounded-xl border border-dashed border-slate-200 bg-card text-center text-slate-400">
<Clock3 className="h-7 w-7" />
<p className="text-sm">{t("executionHistory.empty")}</p>
</div>
@@ -52,7 +52,7 @@ export const ExecutionHistoryItemCard = ({
: t("executionHistory.device", { id: item.executorId.slice(-8) });
return (
<article className="rounded-lg border border-slate-200 bg-white p-3">
<article className="rounded-lg border border-slate-200 bg-card p-3">
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="min-w-0">
{item.title ? <p className="truncate text-sm font-semibold text-slate-900">{item.title}</p> : null}
+1 -1
View File
@@ -135,7 +135,7 @@ export const PdfThumbnail = memo(({ url, label, byteSize, className }: PdfThumbn
aria-label={rendered ? t("assets.pdfThumbnail", { filename: label }) : undefined}
aria-hidden={rendered ? undefined : true}
className={cn(
"absolute max-h-full max-w-full bg-white shadow-sm transition-opacity duration-200",
"edgeever-paper absolute max-h-full max-w-full bg-white shadow-sm transition-opacity duration-200",
rendered ? "opacity-100" : "opacity-0",
)}
/>
+4 -4
View File
@@ -96,7 +96,7 @@ const PdfPage = ({ document, pageNumber, fitWidth, zoom }: PdfPageProps) => {
return (
<span ref={hostRef} className="flex w-full justify-center" style={{ minHeight: visible && page ? undefined : placeholderHeight }}>
<canvas ref={canvasRef} className="max-w-none bg-white shadow-sm" aria-label={`PDF page ${pageNumber}`} />
<canvas ref={canvasRef} className="edgeever-paper max-w-none bg-white shadow-sm" aria-label={`PDF page ${pageNumber}`} />
</span>
);
};
@@ -250,7 +250,7 @@ export const PdfViewer = ({
<span
ref={rootRef}
className={cn(
"edgeever-pdf-viewer block overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm",
"edgeever-pdf-viewer block overflow-hidden rounded-xl border border-slate-200 bg-card shadow-sm",
expanded ? "w-full" : COMPACT_ATTACHMENT_WIDTH_CLASS,
isFullscreen && "fixed inset-0 z-[120] flex rounded-none border-0 bg-slate-950/95 p-3 sm:p-5",
className,
@@ -259,8 +259,8 @@ export const PdfViewer = ({
aria-modal={isFullscreen ? true : undefined}
aria-label={isFullscreen ? t("pdfViewer.fullscreenLabel", { filename: label }) : undefined}
>
<span className={cn("flex min-w-0 flex-col", isFullscreen && "mx-auto h-full w-full max-w-6xl overflow-hidden rounded-xl bg-white shadow-2xl")}>
<span className="flex min-h-12 shrink-0 items-center gap-2 overflow-x-auto border-b border-slate-200 bg-white px-3">
<span className={cn("flex min-w-0 flex-col", isFullscreen && "mx-auto h-full w-full max-w-6xl overflow-hidden rounded-xl bg-card shadow-2xl")}>
<span className="flex min-h-12 shrink-0 items-center gap-2 overflow-x-auto border-b border-slate-200 bg-card px-3">
{isFullscreen && onPrevious ? (
<ButtonTooltip title={t("pdfViewer.previous")}>
<button type="button" className="pdf-viewer-action" aria-label={t("pdfViewer.previous")} onClick={onPrevious}>
@@ -66,7 +66,7 @@ export const PluginCatalogCard = ({
role={extension ? "link" : undefined}
tabIndex={extension ? 0 : undefined}
aria-label={extension ? t("plugins.details.open", { name }) : undefined}
className={`flex min-w-0 flex-col rounded-lg border border-slate-200 bg-white p-3 ${
className={`flex min-w-0 flex-col rounded-lg border border-slate-200 bg-card p-3 ${
extension
? "cursor-pointer transition-colors hover:border-emerald-300 hover:bg-emerald-50/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/70"
: ""
@@ -93,7 +93,7 @@ export const PluginPanelDialog = ({ host, panel, options, onClose }: {
<div ref={setContainer} hidden={Boolean(chrome.empty)} className={cn(
chromeEnabled
? "min-h-40 text-sm text-slate-700"
: "min-h-40 rounded-lg border border-slate-200 bg-white p-4 text-sm text-slate-700",
: "min-h-40 rounded-lg border border-slate-200 bg-card p-4 text-sm text-slate-700",
panel?.presentation === "fullscreen" && "min-h-0 flex-1 overflow-auto",
)} />
</DialogContent>
@@ -22,7 +22,7 @@ export const PluginUpdateDialog = ({
return (
<Dialog open={true} onOpenChange={(open) => { if (!open && !isUpdating) onCancel(); }}>
<DialogContent className="max-w-md overflow-hidden rounded-lg border border-slate-200 bg-white p-0 shadow-lg">
<DialogContent className="max-w-md overflow-hidden rounded-lg border border-slate-200 bg-card p-0 shadow-lg">
<DialogHeader className="flex flex-row items-start gap-4 border-b border-slate-200 px-5 py-5 text-left">
<span className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-full ${hasAddedAccess ? "bg-amber-50 text-amber-700" : "bg-emerald-50 text-emerald-700"}`}>
{hasAddedAccess ? <AlertTriangle className="h-5 w-5" /> : <ShieldCheck className="h-5 w-5" />}
@@ -54,13 +54,13 @@ export const AdvancedPlayCard = () => {
const prompt = t(`advancedPlay.prompts.${key}.prompt`);
return (
<div key={key} className="rounded-lg border border-slate-200 bg-white p-3.5">
<div key={key} className="rounded-lg border border-slate-200 bg-card p-3.5">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div className="text-sm font-semibold text-slate-900">{title}</div>
<Button
size="sm"
variant="outline"
className="h-8 w-full gap-1.5 justify-center bg-white px-2.5 text-xs sm:w-auto"
className="h-8 w-full gap-1.5 justify-center bg-card px-2.5 text-xs sm:w-auto"
type="button"
onClick={() => void handleCopyPrompt(title, prompt)}
>
@@ -172,7 +172,7 @@ export const AiModelCard = () => {
onValueChange={(value) => defaultMutation.mutate(value === "none" ? null : value)}
disabled={readOnly || defaultMutation.isPending}
>
<SelectTrigger className="h-8 bg-white text-xs"><SelectValue /></SelectTrigger>
<SelectTrigger className="h-8 bg-card text-xs"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="none">{t("aiModel.noDefaultModel")}</SelectItem>
{allModels.map((model) => (
@@ -219,14 +219,14 @@ export const AiModelCard = () => {
</span>
</div>
<DisabledActionTooltip label={!canAddProvider ? addDisabledReason : undefined}>
<Button type="button" variant="outline" size="sm" className="h-8 gap-1.5 bg-white text-xs" disabled={!canAddProvider} onClick={openAddDialog}>
<Button type="button" variant="outline" size="sm" className="h-8 gap-1.5 bg-card text-xs" disabled={!canAddProvider} onClick={openAddDialog}>
<Plus className="h-3.5 w-3.5" />{t("aiModel.addProvider")}
</Button>
</DisabledActionTooltip>
</div>
{settings?.providers.length ? (
<div className="overflow-hidden rounded-lg border border-slate-200 divide-y divide-slate-100 bg-white">
<div className="overflow-hidden rounded-lg border border-slate-200 divide-y divide-slate-100 bg-card">
{settings.providers.map((item, index) => (
<AiProviderCard
key={item.id}
@@ -243,9 +243,9 @@ export const AiModelCard = () => {
)}
</section>
<div className="flex items-start gap-2 border-t border-slate-200/60 pt-3 dark:border-slate-800/60">
<ShieldCheck className="mt-0.5 h-3.5 w-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" />
<p className="text-xs leading-relaxed text-slate-500 dark:text-slate-400">
<div className="flex items-start gap-2 border-t border-slate-200/60 pt-3 ">
<ShieldCheck className="mt-0.5 h-3.5 w-3.5 shrink-0 text-emerald-600 " />
<p className="text-xs leading-relaxed text-slate-500 ">
{t("aiModel.privacyNotice")}
</p>
</div>
@@ -42,7 +42,7 @@ export const AiPromptsCard = ({ onOpenLibrary }: { onOpenLibrary: () => void })
<p className="text-sm font-medium text-slate-800">{t("aiPrompts.listTitle")}</p>
<p className="mt-0.5 text-xs text-slate-500">{t("aiPrompts.count", { count })}</p>
</div>
<Button type="button" variant="outline" size="sm" className="h-8 gap-1.5 bg-white text-xs" onClick={onOpenLibrary}>
<Button type="button" variant="outline" size="sm" className="h-8 gap-1.5 bg-card text-xs" onClick={onOpenLibrary}>
{t("aiPrompts.openLibrary")}
<ChevronRight className="h-3.5 w-3.5" />
</Button>
@@ -260,7 +260,7 @@ export const AiProviderCard = ({ provider: saved, defaultDisplayName, defaultMod
type="button"
variant="ghost"
size="icon"
className="h-4 w-4 shrink-0 text-slate-400 hover:bg-white hover:text-rose-600"
className="h-4 w-4 shrink-0 text-slate-400 hover:bg-card hover:text-rose-600"
disabled={readOnly || deleteModelMutation.isPending}
onClick={() => deleteModel(model)}
aria-label={`${t("aiModel.removeModel")}: ${model.displayName}`}
@@ -73,7 +73,7 @@ export const AiTagSuggestionPromptCard = () => {
<>
<textarea
aria-label={t("settings.aiTagPromptTitle")}
className="min-h-44 w-full resize-y rounded-md border border-slate-200 bg-white px-3 py-2 font-mono text-xs leading-5 text-slate-800 outline-none transition focus:border-emerald-500 focus:ring-2 focus:ring-emerald-100 disabled:cursor-not-allowed disabled:bg-slate-50 disabled:text-slate-500"
className="min-h-44 w-full resize-y rounded-md border border-slate-200 bg-card px-3 py-2 font-mono text-xs leading-5 text-slate-800 outline-none transition focus:border-emerald-500 focus:ring-2 focus:ring-emerald-100 disabled:cursor-not-allowed disabled:bg-slate-50 disabled:text-slate-500"
disabled={disabled}
maxLength={4000}
onChange={(event) => setPrompt(event.target.value)}
@@ -73,7 +73,7 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
<Sparkles className={SETTINGS_CARD_ICON_CLASSNAME} />
{t("companion.discovery.settingsTitle")}
</CardTitle>
<span className="inline-flex items-center rounded-full border border-emerald-200/80 bg-emerald-50 px-2 py-0.5 text-[10px] font-semibold tracking-wide text-emerald-700 dark:border-emerald-800/60 dark:bg-emerald-950/50 dark:text-emerald-300">
<span className="inline-flex items-center rounded-full border border-emerald-200/80 bg-emerald-50 px-2 py-0.5 text-[10px] font-semibold tracking-wide text-emerald-700 ">
Beta
</span>
</div>
@@ -84,7 +84,7 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
<div className="flex shrink-0 items-center justify-between gap-3 sm:pt-0.5">
<label
htmlFor="companion-discovery-enabled"
className="cursor-pointer text-sm font-medium text-slate-700 dark:text-slate-300"
className="cursor-pointer text-sm font-medium text-slate-700 "
>
{t("companion.discovery.enable")}
</label>
@@ -97,9 +97,9 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
</div>
</div>
<div className="mt-2 flex items-center gap-2 rounded-md bg-emerald-50/80 px-3 py-2 text-xs text-emerald-800 dark:bg-emerald-950/40 dark:text-emerald-300">
<div className="mt-2 flex items-center gap-2 rounded-md bg-emerald-50/80 px-3 py-2 text-xs text-emerald-800 ">
<span className="text-sm leading-none" aria-hidden="true">🐾</span>
<p className="font-medium text-emerald-700 dark:text-emerald-300">{t("companion.discovery.tagline")}</p>
<p className="font-medium text-emerald-700 ">{t("companion.discovery.tagline")}</p>
</div>
</CardHeader>
@@ -110,10 +110,10 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
</p>
) : null}
{error === "model" || (aiSettings && !defaultModelReady) ? (
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900/50 dark:bg-amber-950/30">
<p role="alert" className="text-sm text-amber-900 dark:text-amber-200">{t("companion.discovery.modelRequired")}</p>
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3 ">
<p role="alert" className="text-sm text-amber-900 ">{t("companion.discovery.modelRequired")}</p>
<button type="button" onClick={onOpenAiSettings}
className="rounded-md border border-amber-300 bg-white px-3 py-1.5 text-xs font-medium text-amber-900 hover:bg-amber-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-600 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">
className="rounded-md border border-amber-300 bg-card px-3 py-1.5 text-xs font-medium text-amber-900 hover:bg-amber-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-600 ">
{t("companion.discovery.configureModel")}
</button>
</div>
@@ -128,26 +128,26 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
</div>)}
</div>
<section
className="space-y-3 rounded-lg border border-slate-200/80 bg-white p-4 dark:border-slate-800 dark:bg-slate-900"
className="space-y-3 rounded-lg border border-slate-200/80 bg-card p-4 "
aria-labelledby="companion-discovery-status-title"
>
<div className="flex items-center justify-between gap-3 border-b border-slate-200/60 pb-3 dark:border-slate-800/60">
<div className="flex items-center justify-between gap-3 border-b border-slate-200/60 pb-3 ">
<h3
id="companion-discovery-status-title"
className="flex items-center gap-2 text-sm font-medium text-slate-900 dark:text-slate-100"
className="flex items-center gap-2 text-sm font-medium text-slate-900 "
>
<Activity className="h-4 w-4 text-slate-500 dark:text-slate-400" />
<Activity className="h-4 w-4 text-slate-500 " />
{t("companion.discovery.transparency.title")}
</h3>
<span
className={`inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ${
enabled
? settings?.lastStatus === "failed"
? "border border-rose-200/80 bg-rose-50 text-rose-700 dark:border-rose-900/40 dark:bg-rose-950/40 dark:text-rose-300"
? "border border-rose-200/80 bg-rose-50 text-rose-700 "
: settings?.lastStatus === "running"
? "border border-amber-200/80 bg-amber-50 text-amber-700 dark:border-amber-900/40 dark:bg-amber-950/40 dark:text-amber-300"
: "border border-emerald-200/80 bg-emerald-50 text-emerald-700 dark:border-emerald-900/40 dark:bg-emerald-950/40 dark:text-emerald-300"
: "border border-slate-200 bg-slate-100 text-slate-600 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400"
? "border border-amber-200/80 bg-amber-50 text-amber-700 "
: "border border-emerald-200/80 bg-emerald-50 text-emerald-700 "
: "border border-slate-200 bg-slate-100 text-slate-600 "
}`}
role="status"
>
@@ -168,28 +168,28 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
<dl className="space-y-2.5 text-xs sm:text-sm">
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
<dt className="flex items-center gap-1.5 font-medium text-slate-500 dark:text-slate-400">
<Clock className="h-3.5 w-3.5 text-slate-400 dark:text-slate-500" />
<dt className="flex items-center gap-1.5 font-medium text-slate-500 ">
<Clock className="h-3.5 w-3.5 text-slate-400 " />
{t("companion.discovery.transparency.lastCheck")}
</dt>
<dd className="font-medium text-slate-800 dark:text-slate-200">
<dd className="font-medium text-slate-800 ">
{settings?.lastCheckAt ? formatTime(settings.lastCheckAt) : t("companion.discovery.transparency.neverChecked")}
</dd>
</div>
<div className="flex flex-col gap-1 sm:flex-row sm:items-baseline sm:justify-between">
<dt className="flex shrink-0 items-center gap-1.5 font-medium text-slate-500 dark:text-slate-400">
<Sparkles className="h-3.5 w-3.5 text-slate-400 dark:text-slate-500" />
<dt className="flex shrink-0 items-center gap-1.5 font-medium text-slate-500 ">
<Sparkles className="h-3.5 w-3.5 text-slate-400 " />
{t("companion.discovery.transparency.nextCheck")}
</dt>
<dd className="text-xs leading-relaxed text-slate-600 sm:max-w-md sm:text-right dark:text-slate-300">
<dd className="text-xs leading-relaxed text-slate-600 sm:max-w-md sm:text-right ">
{t(enabled ? "companion.discovery.transparency.nextCheckEnabled" : "companion.discovery.transparency.nextCheckDisabled")}
</dd>
</div>
</dl>
<div className="flex items-start gap-2 border-t border-slate-200/60 pt-2.5 dark:border-slate-800/60">
<ShieldCheck className="mt-0.5 h-3.5 w-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" />
<p className="text-xs leading-relaxed text-slate-500 dark:text-slate-400">
<div className="flex items-start gap-2 border-t border-slate-200/60 pt-2.5 ">
<ShieldCheck className="mt-0.5 h-3.5 w-3.5 shrink-0 text-emerald-600 " />
<p className="text-xs leading-relaxed text-slate-500 ">
{t("companion.discovery.transparency.scope")}
</p>
</div>
@@ -199,34 +199,34 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
<section className="space-y-3" aria-labelledby="companion-latest-discovery-title">
<h3
id="companion-latest-discovery-title"
className="text-sm font-medium text-slate-900 dark:text-slate-100"
className="text-sm font-medium text-slate-900 "
>
{t("companion.discovery.transparency.latest")}
</h3>
{feed.isPending ? <p role="status" className="text-sm text-slate-500">{t("common.loading")}</p> : null}
{feed.isError ? <p role="alert" className="text-sm text-destructive">{t("companion.discovery.loadFailed")}</p> : null}
{!feed.isPending && !feed.isError && !latest ? (
<div className="flex flex-col items-center justify-center rounded-lg border border-slate-200/80 bg-white px-4 py-6 text-center dark:border-slate-800 dark:bg-slate-900">
<div className="mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-emerald-50 text-emerald-600 dark:bg-emerald-950/50 dark:text-emerald-400">
<div className="flex flex-col items-center justify-center rounded-lg border border-slate-200/80 bg-card px-4 py-6 text-center ">
<div className="mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-emerald-50 text-emerald-600 ">
<Sparkles className="h-4 w-4" />
</div>
<p className="max-w-md text-xs text-slate-500 sm:text-sm dark:text-slate-400">
<p className="max-w-md text-xs text-slate-500 sm:text-sm ">
{t("companion.discovery.transparency.noDiscovery")}
</p>
</div>
) : null}
{latest ? (
<article className="space-y-2.5 rounded-lg border border-slate-200/80 bg-white p-4 shadow-sm transition-colors dark:border-slate-800 dark:bg-slate-900">
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-100 pb-2 dark:border-slate-800">
<span className="inline-flex items-center rounded-full bg-emerald-50 px-2 py-0.5 text-[11px] font-medium text-emerald-700 dark:bg-emerald-950/50 dark:text-emerald-300">
<article className="space-y-2.5 rounded-lg border border-slate-200/80 bg-card p-4 shadow-sm transition-colors ">
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-100 pb-2 ">
<span className="inline-flex items-center rounded-full bg-emerald-50 px-2 py-0.5 text-[11px] font-medium text-emerald-700 ">
{t(`companion.discovery.kind.${latest.kind}`)}
</span>
<time className="text-xs text-slate-400 dark:text-slate-500" dateTime={latest.createdAt}>
<time className="text-xs text-slate-400 " dateTime={latest.createdAt}>
{formatTime(latest.createdAt)}
</time>
</div>
<h4 className="break-words text-sm font-semibold text-slate-900 dark:text-slate-100">{latest.title}</h4>
<p className="whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm dark:text-slate-300">
<h4 className="break-words text-sm font-semibold text-slate-900 ">{latest.title}</h4>
<p className="whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-600 sm:text-sm ">
{latest.body}
</p>
</article>
@@ -238,22 +238,22 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion, onOpenA
<button
type="button"
onClick={onOpenCompanion}
className="group flex w-full items-center justify-between rounded-lg border border-slate-200/80 bg-white p-3.5 text-left transition-all hover:border-emerald-300 hover:bg-emerald-50/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-600 dark:border-slate-800 dark:bg-slate-900 dark:hover:border-emerald-700/60 dark:hover:bg-emerald-950/20"
className="group flex w-full items-center justify-between rounded-lg border border-slate-200/80 bg-card p-3.5 text-left transition-all hover:border-emerald-300 hover:bg-emerald-50/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-600 "
>
<div className="flex min-w-0 items-center gap-3">
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-slate-200/60 bg-slate-50 text-emerald-600 shadow-sm transition-colors group-hover:border-emerald-200 group-hover:bg-emerald-50 group-hover:text-emerald-700 dark:border-slate-800 dark:bg-slate-800 dark:text-emerald-400">
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-slate-200/60 bg-slate-50 text-emerald-600 shadow-sm transition-colors group-hover:border-emerald-200 group-hover:bg-emerald-50 group-hover:text-emerald-700 ">
<MessageSquareText className="h-4.5 w-4.5" />
</div>
<div className="min-w-0">
<div className="text-sm font-medium text-slate-900 transition-colors group-hover:text-emerald-800 dark:text-slate-100 dark:group-hover:text-emerald-300">
<div className="text-sm font-medium text-slate-900 transition-colors group-hover:text-emerald-800 ">
{t("companion.discovery.openCompanion")}
</div>
<div className="text-xs text-slate-500 dark:text-slate-400">
<div className="text-xs text-slate-500 ">
{t("companion.discovery.openCompanionDesc")}
</div>
</div>
</div>
<ChevronRight className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-hover:translate-x-0.5 group-hover:text-emerald-600 dark:text-slate-500 dark:group-hover:text-emerald-400" />
<ChevronRight className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-hover:translate-x-0.5 group-hover:text-emerald-600 " />
</button>
</div>
</CardContent>
@@ -161,7 +161,7 @@ export const CustomEditorThemeDialog = ({
onClick={() => setActiveMode("light")}
className={`flex flex-1 items-center justify-center gap-1.5 rounded-md py-1 text-xs font-medium transition-all ${
activeMode === "light"
? "bg-white text-emerald-800 shadow-sm"
? "bg-card text-emerald-800 shadow-sm"
: "text-slate-500 hover:text-slate-800"
}`}
>
@@ -173,7 +173,7 @@ export const CustomEditorThemeDialog = ({
onClick={() => setActiveMode("dark")}
className={`flex flex-1 items-center justify-center gap-1.5 rounded-md py-1 text-xs font-medium transition-all ${
activeMode === "dark"
? "bg-white text-emerald-800 shadow-sm"
? "bg-card text-emerald-800 shadow-sm"
: "text-slate-500 hover:text-slate-800"
}`}
>
@@ -192,7 +192,7 @@ export const CustomEditorThemeDialog = ({
type="color"
value={/^#[0-9a-f]{6}$/i.test(activeColors[key]) ? activeColors[key] : defaultColors[key]}
onChange={(event) => updateColor(key as keyof ThemeColors, event.target.value)}
className="h-7 w-7 cursor-pointer rounded border border-slate-200 bg-white p-0.5"
className="h-7 w-7 cursor-pointer rounded border border-slate-200 bg-card p-0.5"
aria-label={t(labelKey)}
/>
<Input
@@ -224,7 +224,7 @@ export const CustomEditorThemeDialog = ({
value={draft.customCss || ""}
onChange={(event) => setDraft((current) => ({ ...current, customCss: event.target.value }))}
placeholder="e.g. h1 { font-style: italic; } blockquote { border-radius: 6px; }"
className="min-h-[72px] w-full rounded-md border border-slate-200 bg-white p-2 font-mono text-[11px] focus:border-emerald-500 focus:outline-none"
className="min-h-[72px] w-full rounded-md border border-slate-200 bg-card p-2 font-mono text-[11px] focus:border-emerald-500 focus:outline-none"
maxLength={2000}
/>
</label>
@@ -60,7 +60,7 @@ export const DesktopLocalBackupCard = () => {
<CardContent className="grid gap-3 p-4 pt-0">
<CardDescription className="text-xs leading-5">{t("dataExport.desktopLocalDescription")}</CardDescription>
{backups.length > 0 ? (
<select className="h-10 rounded-md border border-slate-200 bg-white px-3 text-sm text-slate-950 outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20" value={selectedPath} disabled={state === "working"} onChange={(event) => setSelectedPath(event.target.value)}>
<select className="h-10 rounded-md border border-slate-200 bg-card px-3 text-sm text-slate-950 outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20" value={selectedPath} disabled={state === "working"} onChange={(event) => setSelectedPath(event.target.value)}>
{backups.map((backup) => <option key={backup.path} value={backup.path}>{backup.name} · {formatSize(backup.size)}</option>)}
</select>
) : <p className="text-xs text-slate-500">{state === "working" ? t("dataExport.desktopLocalRefreshing") : t("dataExport.desktopLocalEmpty")}</p>}
@@ -27,7 +27,7 @@ export const EvernoteImportGuideCard = () => {
</CardDescription>
</div>
<div className="flex shrink-0 items-center gap-2">
<Button size="sm" variant="outline" className="h-8 gap-1.5 bg-white px-3 text-xs" type="button" asChild>
<Button size="sm" variant="outline" className="h-8 gap-1.5 bg-card px-3 text-xs" type="button" asChild>
<a
href={EVERNOTE_MIGRATION_BLOG_URL}
target="_blank"
@@ -61,7 +61,7 @@ const McpExampleDialog = () => {
return (
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="outline" className="h-7 bg-white px-2.5 text-xs" type="button">
<Button size="sm" variant="outline" className="h-7 bg-card px-2.5 text-xs" type="button">
{t("mcp.example")}
</Button>
</DialogTrigger>
@@ -117,7 +117,7 @@ const AccessLevelPicker = ({ value, onChange }: AccessLevelPickerProps) => {
className={cn(
"flex h-8 cursor-pointer select-none items-center gap-1.5 rounded-[5px] px-2.5 text-xs font-medium transition-all focus-within:ring-2 focus-within:ring-emerald-500/40",
checked
? "bg-white font-semibold text-emerald-800 shadow-sm"
? "bg-card font-semibold text-emerald-800 shadow-sm"
: "text-slate-600 hover:text-slate-900"
)}
>
@@ -192,7 +192,7 @@ const TokenList = ({ tokens, availableScopes, newlyCreatedTokenId, isLoading, is
}
return (
<div className="overflow-hidden rounded-lg border border-slate-200 divide-y divide-slate-100 bg-white">
<div className="overflow-hidden rounded-lg border border-slate-200 divide-y divide-slate-100 bg-card">
{tokens.map((token) => {
const accessLevel = getStoredTokenAccessLevel(token.scopes, availableScopes);
const accessLabel = accessLevel === "legacy-custom"
@@ -257,7 +257,7 @@ const TokenList = ({ tokens, availableScopes, newlyCreatedTokenId, isLoading, is
<Button
size="sm"
variant="outline"
className="h-8 gap-1.5 border-slate-200 bg-white px-2.5 text-xs font-normal text-slate-700 hover:bg-slate-50 hover:text-slate-900"
className="h-8 gap-1.5 border-slate-200 bg-card px-2.5 text-xs font-normal text-slate-700 hover:bg-slate-50 hover:text-slate-900"
aria-label={token.token ? t("mcp.copyToken") : t("mcp.legacyTokenCannotCopy")}
disabled={token.isRevoked || !token.token}
onClick={() => void handleCopy(token, "token")}
@@ -283,7 +283,7 @@ const TokenList = ({ tokens, availableScopes, newlyCreatedTokenId, isLoading, is
<Button
size="sm"
variant="outline"
className="h-8 gap-1.5 border-slate-200 bg-white px-2.5 text-xs font-normal text-slate-700 hover:bg-slate-50 hover:text-slate-900"
className="h-8 gap-1.5 border-slate-200 bg-card px-2.5 text-xs font-normal text-slate-700 hover:bg-slate-50 hover:text-slate-900"
aria-label={token.token ? t("mcp.copyConfig") : t("mcp.legacyConfigCannotCopy")}
disabled={token.isRevoked || !token.token}
onClick={() => void handleCopy(token, "config")}
@@ -391,7 +391,7 @@ export const McpConfigCard = () => {
<div className="rounded-lg border border-slate-200/70 bg-slate-50/50 p-3 sm:p-3.5">
<form className="flex flex-col gap-2.5 sm:flex-row sm:items-center" onSubmit={handleSubmit}>
<Input
className="h-9 min-w-0 flex-1 bg-white text-xs"
className="h-9 min-w-0 flex-1 bg-card text-xs"
value={name}
onChange={(event) => setName(event.target.value)}
placeholder={t("mcp.namePlaceholder")}
@@ -133,7 +133,7 @@ export const ObjectStorageCard = ({ demoMode }: { demoMode: boolean }) => {
<Field label={t("objectStorage.accessKeyId")}><Input value={accessKeyId} onChange={(event) => setAccessKeyId(event.target.value)} required autoComplete="off" /></Field>
<Field label={t("objectStorage.secretAccessKey")} hint={hasSavedSecret ? t("objectStorage.secretSavedHint") : undefined}><Input type="password" value={secretAccessKey} onChange={(event) => setSecretAccessKey(event.target.value)} required={!hasSavedSecret} autoComplete="new-password" placeholder={hasSavedSecret ? "••••••••••••" : ""} /></Field>
<Field label={t("objectStorage.objectPrefix")} hint={t("objectStorage.objectPrefixHint")}><Input value={objectPrefix} onChange={(event) => setObjectPrefix(event.target.value)} placeholder="edgeever" /></Field>
<label className="flex items-center justify-between gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2.5 text-sm font-medium text-slate-700">
<label className="flex items-center justify-between gap-3 rounded-lg border border-slate-200 bg-card px-3 py-2.5 text-sm font-medium text-slate-700">
<span><span className="block">{t("objectStorage.pathStyle")}</span><span className="mt-0.5 block text-xs font-normal text-slate-500">{t("objectStorage.pathStyleHint")}</span></span>
<Switch checked={forcePathStyle} onCheckedChange={setForcePathStyle} />
</label>
@@ -85,7 +85,7 @@ const LegacyManualScheduledTasksSection = () => {
<div className="mt-4 grid gap-2">
{tasks.map((task) => (
<div key={task.id} className="flex flex-wrap items-center gap-3 rounded-md border border-slate-200 bg-white px-3 py-2">
<div key={task.id} className="flex flex-wrap items-center gap-3 rounded-md border border-slate-200 bg-card px-3 py-2">
<div className="min-w-0 flex-1">
<div className="truncate text-xs font-semibold text-slate-800">{task.name}</div>
<div className="mt-0.5 truncate font-mono text-[10px] text-slate-400">
@@ -426,7 +426,7 @@ export const PluginManagerCard = ({
<CardTitle className="flex items-center gap-2 text-sm">
<Puzzle className="h-4 w-4 text-emerald-700" />
{selectedPluginId ? t("plugins.details.title") : t("plugins.title")}
<span className="inline-flex items-center rounded-full border border-emerald-200/80 bg-emerald-50 px-2 py-0.5 text-[10px] font-semibold tracking-wide text-emerald-700 dark:border-emerald-800/60 dark:bg-emerald-950/50 dark:text-emerald-300">
<span className="inline-flex items-center rounded-full border border-emerald-200/80 bg-emerald-50 px-2 py-0.5 text-[10px] font-semibold tracking-wide text-emerald-700 ">
Beta
</span>
</CardTitle>
@@ -473,7 +473,7 @@ export const PluginManagerCard = ({
<span>{t("plugins.marketplace.loadFailed", {
message: marketplaceQuery.error instanceof Error ? marketplaceQuery.error.message : String(marketplaceQuery.error),
})}</span>
<Button size="sm" variant="outline" className="h-7 bg-white px-2 text-xs" onClick={() => void marketplaceQuery.refetch()}>
<Button size="sm" variant="outline" className="h-7 bg-card px-2 text-xs" onClick={() => void marketplaceQuery.refetch()}>
{t("plugins.marketplace.retry")}
</Button>
</div>
@@ -12,6 +12,18 @@ describe("editor content alignment preference", () => {
});
});
describe("appearance preference", () => {
test("exposes light, dark, and system as a first-class setting", () => {
const preferenceCard = readFileSync(new URL("./PreferenceCard.tsx", import.meta.url), "utf8");
expect(preferenceCard).toContain('t("settings.themeTitle")');
expect(preferenceCard).toContain('setAppearancePreference(value as ThemePreference)');
expect(preferenceCard).toContain('value="system"');
expect(preferenceCard).toContain('value="light"');
expect(preferenceCard).toContain('value="dark"');
});
});
describe("custom editor theme portability", () => {
test("offers import and export while keeping contrast as a warning", () => {
const dialog = readFileSync(new URL("./CustomEditorThemeDialog.tsx", import.meta.url), "utf8");
@@ -1,4 +1,4 @@
import { AlignHorizontalJustifyCenter, ChartNoAxesCombined, FileCode2, Image, Keyboard, Languages, MousePointerClick, Palette, Sparkles } from "lucide-react";
import { AlignHorizontalJustifyCenter, ChartNoAxesCombined, FileCode2, Image, Keyboard, Languages, MousePointerClick, Palette, Sparkles, SunMoon } from "lucide-react";
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import type { EditorContentAlignment, ShortcutSettings } from "@/lib/app-helpers";
@@ -42,12 +42,14 @@ import { CustomEditorThemeDialog } from "./CustomEditorThemeDialog";
import {
MARKDOWN_THEME_PREFERENCES,
MERMAID_THEME_PREFERENCES,
useAppearanceTheme,
useEditorTheme,
useMarkdownTheme,
useMermaidTheme,
DEFAULT_CUSTOM_LIGHT_COLORS,
DEFAULT_CUSTOM_DARK_COLORS,
type CustomEditorTheme,
type ThemePreference,
} from "../ThemeProvider";
interface PreferenceCardProps {
@@ -74,6 +76,7 @@ export const PreferenceCard = ({
setCustomEditorThemes,
setEditorTheme,
} = useEditorTheme();
const { preference: appearancePreference, setPreference: setAppearancePreference } = useAppearanceTheme();
const { mermaidThemePreference, setMermaidTheme } = useMermaidTheme();
const { markdownThemePreference, setMarkdownTheme } = useMarkdownTheme();
const [customThemeDialogOpen, setCustomThemeDialogOpen] = useState(false);
@@ -211,7 +214,7 @@ export const PreferenceCard = ({
value={activeLocalePreference}
onValueChange={(preference) => handleLocalePreferenceChange(preference as AppLocalePreference)}
>
<SelectTrigger aria-label={t("common.language")} className="h-9 bg-white">
<SelectTrigger aria-label={t("common.language")} className="h-9 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -226,6 +229,31 @@ export const PreferenceCard = ({
</div>
</div>
<div className="flex min-h-16 flex-col items-start gap-3 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4">
<div className="flex min-w-0 items-start gap-3">
<SunMoon className={SETTINGS_ITEM_ICON_CLASSNAME} />
<div className="min-w-0">
<div className={SETTINGS_ITEM_TITLE_CLASSNAME}>{t("settings.themeTitle")}</div>
<div className={SETTINGS_ITEM_DESCRIPTION_CLASSNAME}>{t("settings.themeDescription")}</div>
</div>
</div>
<div className="w-full shrink-0 sm:w-80">
<Select
value={appearancePreference}
onValueChange={(value) => setAppearancePreference(value as ThemePreference)}
>
<SelectTrigger aria-label={t("settings.themeTitle")} className="h-9 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">{t("settings.themeSystem")}</SelectItem>
<SelectItem value="light">{t("settings.themeLight")}</SelectItem>
<SelectItem value="dark">{t("settings.themeDark")}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="hidden min-h-16 flex-col items-start gap-3 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4 lg:flex">
<div className="flex min-w-0 items-start gap-3">
<AlignHorizontalJustifyCenter className={SETTINGS_ITEM_ICON_CLASSNAME} />
@@ -239,7 +267,7 @@ export const PreferenceCard = ({
value={editorContentAlignment}
onValueChange={(value) => onEditorContentAlignmentChange(value as EditorContentAlignment)}
>
<SelectTrigger aria-label={t("settings.editorContentAlignmentTitle")} className="h-9 bg-white">
<SelectTrigger aria-label={t("settings.editorContentAlignmentTitle")} className="h-9 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -276,7 +304,7 @@ export const PreferenceCard = ({
}
}}
>
<SelectTrigger aria-label={t("settings.editorThemeTitle")} className="h-9 w-full min-w-0 flex-1 bg-white">
<SelectTrigger aria-label={t("settings.editorThemeTitle")} className="h-9 w-full min-w-0 flex-1 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -320,7 +348,7 @@ export const PreferenceCard = ({
</div>
<div className="w-full shrink-0 sm:w-80">
<Select value={markdownThemePreference} onValueChange={(value) => setMarkdownTheme(value as typeof markdownThemePreference)}>
<SelectTrigger aria-label={t("settings.markdownThemeTitle")} className="h-9 bg-white">
<SelectTrigger aria-label={t("settings.markdownThemeTitle")} className="h-9 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -344,7 +372,7 @@ export const PreferenceCard = ({
</div>
<div className="w-full shrink-0 sm:w-80">
<Select value={mermaidThemePreference} onValueChange={(value) => setMermaidTheme(value as typeof mermaidThemePreference)}>
<SelectTrigger aria-label={t("settings.mermaidThemeTitle")} className="h-9 bg-white">
<SelectTrigger aria-label={t("settings.mermaidThemeTitle")} className="h-9 bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -119,7 +119,7 @@ export const ShortcutSettingsItem = ({ shortcutSettings, onShortcutSettingsChang
<Button
size="sm"
variant="outline"
className="h-8 w-full bg-white px-3 text-xs sm:w-auto"
className="h-8 w-full bg-card px-3 text-xs sm:w-auto"
type="button"
onClick={() => setShortcutsOpen(true)}
>
@@ -154,7 +154,7 @@ export const ShortcutSettingsItem = ({ shortcutSettings, onShortcutSettingsChang
ref={recording ? captureButtonRef : null}
type="button"
variant={recording ? "solid" : "outline"}
className={cn("h-9 min-w-32 px-3 font-mono text-xs", !recording && "bg-white")}
className={cn("h-9 min-w-32 px-3 font-mono text-xs", !recording && "bg-card")}
onClick={() => {
setRecordingAction(item.value);
setCaptureMessage("");
@@ -365,7 +365,7 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => {
<Button
size="sm"
variant="outline"
className="h-7 gap-1.5 bg-white px-2.5 text-xs text-slate-700 shadow-xs hover:bg-slate-50"
className="h-7 gap-1.5 bg-card px-2.5 text-xs text-slate-700 shadow-xs hover:bg-slate-50"
type="button"
onClick={() => void handleCopy()}
>
@@ -398,7 +398,7 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => {
<Button
size="sm"
variant="outline"
className="h-7 bg-white px-2.5 text-xs shadow-xs hover:bg-slate-50"
className="h-7 bg-card px-2.5 text-xs shadow-xs hover:bg-slate-50"
type="button"
disabled={desktopUpdateBusy || desktopUpdateState === "available"}
onClick={handleDesktopUpdate}
@@ -421,7 +421,7 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => {
asChild
size="sm"
variant="outline"
className="h-7 bg-white px-2.5 text-xs shadow-xs hover:bg-slate-50"
className="h-7 bg-card px-2.5 text-xs shadow-xs hover:bg-slate-50"
>
<a href="https://github.com/tianma-if/edgeever/releases/latest" target="_blank" rel="noreferrer">
<ExternalLink className="h-3.5 w-3.5" />
@@ -455,7 +455,7 @@ export const SystemInfoPanel = ({ active = true }: { active?: boolean }) => {
{desktopUpdateStatus}
</p>
) : null}
<div className="rounded-lg border border-slate-200/80 bg-white p-3 sm:p-3.5">
<div className="rounded-lg border border-slate-200/80 bg-card p-3 sm:p-3.5">
<dl className="grid grid-cols-2 gap-x-4 gap-y-2.5 sm:grid-cols-3 sm:gap-x-5">
{group.items.map((item) => (
<div
@@ -122,7 +122,7 @@ export const WebDavBackupCard = () => {
{WEBDAV_AUTO_BACKUP_ENABLED ? (
<>
<label className="grid gap-1.5 text-xs font-medium text-slate-700">{t("dataExport.webdavInterval")}
<select className="h-10 rounded-md border border-slate-200 bg-white px-3 text-sm text-slate-950 outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20" value={schedule.intervalDays} disabled={busy} onChange={(event) => updateSchedule({ intervalDays: Number(event.target.value) as WebDavBackupSchedule["intervalDays"] })}>
<select className="h-10 rounded-md border border-slate-200 bg-card px-3 text-sm text-slate-950 outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20" value={schedule.intervalDays} disabled={busy} onChange={(event) => updateSchedule({ intervalDays: Number(event.target.value) as WebDavBackupSchedule["intervalDays"] })}>
<option value={1}>{t("dataExport.webdavIntervals.daily")}</option><option value={7}>{t("dataExport.webdavIntervals.weekly")}</option><option value={14}>{t("dataExport.webdavIntervals.biweekly")}</option><option value={30}>{t("dataExport.webdavIntervals.monthly")}</option>
</select>
</label>
+2 -2
View File
@@ -7,9 +7,9 @@ const alertVariants = cva(
{
variants: {
variant: {
default: "border-slate-200 bg-white text-slate-950",
default: "border-slate-200 bg-card text-slate-950",
destructive:
"border-red-200 bg-red-50/50 text-red-700 [&>svg]:text-red-600 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-400 dark:[&>svg]:text-red-400",
"border-rose-200 bg-rose-50/50 text-rose-700 [&>svg]:text-rose-600",
},
},
defaultVariants: { variant: "default" },
+2 -2
View File
@@ -8,9 +8,9 @@ const badgeVariants = cva(
variants: {
variant: {
default: "border-transparent bg-emerald-600 text-white",
secondary: "border-transparent bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-100",
secondary: "border-transparent bg-slate-100 text-slate-900",
destructive: "border-transparent bg-red-600 text-white",
outline: "border-slate-200 text-slate-950 dark:border-slate-700 dark:text-slate-100",
outline: "border-slate-200 text-slate-950",
},
},
defaultVariants: { variant: "default" },
+1 -1
View File
@@ -15,7 +15,7 @@ const buttonVariants = cva(
solid: "bg-emerald-500 text-white hover:bg-emerald-600 border-emerald-500",
soft: "bg-slate-50 text-slate-700 hover:bg-slate-100 border-slate-200",
ghost: "bg-transparent text-slate-600 hover:bg-slate-50 hover:text-slate-900 border-transparent",
outline: "bg-white text-slate-700 hover:bg-slate-50 border border-slate-200",
outline: "bg-card text-slate-700 hover:bg-slate-50 border border-slate-200",
danger: "bg-rose-50 text-rose-700 hover:bg-rose-100 border border-rose-100",
},
size: {
+1 -1
View File
@@ -2,7 +2,7 @@ import * as React from "react";
import { cn } from "@/lib/utils";
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("rounded-lg border border-slate-200 bg-white text-slate-950 shadow-sm", className)} {...props} />
<div ref={ref} className={cn("rounded-lg border border-slate-200 bg-card text-slate-950 shadow-sm", className)} {...props} />
));
Card.displayName = "Card";
+1 -1
View File
@@ -10,7 +10,7 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded border border-slate-300 bg-white text-white shadow-sm outline-none transition focus-visible:ring-2 focus-visible:ring-emerald-500/70 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-emerald-500 data-[state=checked]:bg-emerald-500",
"peer h-4 w-4 shrink-0 rounded border border-slate-300 bg-card text-white shadow-sm outline-none transition focus-visible:ring-2 focus-visible:ring-emerald-500/70 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-emerald-500 data-[state=checked]:bg-emerald-500",
className
)}
{...props}
+1 -1
View File
@@ -12,7 +12,7 @@ const Command = React.forwardRef<
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-slate-950", className)}
className={cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-card text-slate-950", className)}
{...props}
/>
));
+2 -2
View File
@@ -38,7 +38,7 @@ const ContextMenuSubContent = React.forwardRef<
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-card p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
@@ -54,7 +54,7 @@ const ContextMenuContent = React.forwardRef<
<ContextMenuPrimitive.Content
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-card p-1 text-slate-950 shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
+2 -2
View File
@@ -32,13 +32,13 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-1/2 data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-1/2 rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-1/2 data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-1/2 rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 data-[state=open]:text-slate-500">
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 data-[state=open]:text-slate-500">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
+1 -1
View File
@@ -35,7 +35,7 @@ const DrawerContent = React.forwardRef<
<DrawerPrimitive.Content
ref={ref}
className={cn(
"fixed inset-x-3 bottom-0 z-50 mt-24 flex max-h-[calc(100dvh_-_1rem_-_env(safe-area-inset-bottom))] flex-col rounded-t-md border border-slate-200 bg-white shadow-panel outline-none",
"fixed inset-x-3 bottom-0 z-50 mt-24 flex max-h-[calc(100dvh_-_1rem_-_env(safe-area-inset-bottom))] flex-col rounded-t-md border border-slate-200 bg-card shadow-panel outline-none",
className
)}
{...props}
+2 -2
View File
@@ -40,7 +40,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-card p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
@@ -59,7 +59,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-card p-1 text-slate-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
+1 -1
View File
@@ -8,7 +8,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
ref={ref}
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 text-sm text-slate-950 outline-none transition placeholder:text-slate-400 focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20 disabled:cursor-not-allowed disabled:opacity-50",
"flex h-10 w-full rounded-md border border-slate-200 bg-card px-3 text-sm text-slate-950 outline-none transition placeholder:text-slate-400 focus-visible:border-emerald-500 focus-visible:ring-2 focus-visible:ring-emerald-500/20 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
+2 -2
View File
@@ -14,7 +14,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"flex h-9 w-full items-center justify-between rounded-md border border-slate-200 bg-card px-3 py-2 text-sm ring-offset-background placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
@@ -70,7 +70,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white text-slate-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-card text-slate-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
+1 -1
View File
@@ -16,7 +16,7 @@ const Switch = React.forwardRef<
>
<SwitchPrimitive.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
"pointer-events-none block h-4 w-4 rounded-full bg-[var(--switch-thumb)] shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>
+1 -1
View File
@@ -17,7 +17,7 @@ const TooltipContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-slate-950 px-2.5 py-1.5 text-xs text-white shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
"z-50 overflow-hidden rounded-md bg-[var(--tooltip-bg)] px-2.5 py-1.5 text-xs text-[var(--tooltip-fg)] shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
className
)}
{...props}
+32 -3
View File
@@ -42,9 +42,7 @@ describe("dark theme contracts", () => {
const css = readFileSync(new URL("../styles/globals.css", import.meta.url), "utf8");
const memoCard = readFileSync(new URL("../components/MemoCard.tsx", import.meta.url), "utf8");
expect(css).toContain(":root.dark .edgeever-public-share .ProseMirror");
expect(css).toContain("color: #f8fafc;");
expect(css).toContain('[class~="divide-slate-100"]');
expect(css).toContain('[class~="text-emerald-700"]');
expect(css).toContain("color: hsl(var(--foreground));");
expect(css).toContain("--workspace-memo-divider: #3b4540;");
expect(css).toContain(":root.dark .edgeever-workspace-memo-list .edgeever-memo-divider");
expect(memoCard).toContain("edgeever-memo-divider");
@@ -52,6 +50,21 @@ describe("dark theme contracts", () => {
expect(memoCard).not.toContain("dark:lg:border-slate-300/70");
});
test("components do not add a second dark: color path on top of theme tokens", () => {
const css = readFileSync(new URL("../styles/globals.css", import.meta.url), "utf8");
const notebookPane = readFileSync(new URL("../components/NotebookPane.tsx", import.meta.url), "utf8");
const companion = readFileSync(new URL("../components/CompanionActionCard.tsx", import.meta.url), "utf8");
const badge = readFileSync(new URL("../components/ui/badge.tsx", import.meta.url), "utf8");
expect(css).not.toContain('[class~="bg-white"]');
expect(css).not.toContain('[class~="bg-slate-50"]');
expect(css).not.toContain('[class~="text-slate-700"]');
expect(css).not.toContain("--dark-utility-alpha");
expect(notebookPane).not.toContain("dark:bg-slate-");
expect(companion).not.toContain("dark:text-slate-");
expect(badge).not.toContain("dark:bg-slate-");
});
test("workspace dark surfaces stay neutral and bundled editor themes blend into the canvas", () => {
const css = readFileSync(new URL("../styles/globals.css", import.meta.url), "utf8");
@@ -65,6 +78,22 @@ describe("dark theme contracts", () => {
expect(contrastRatio("#9aa9a0", "#191e1b")).toBeGreaterThanOrEqual(4.5);
});
test("dark chrome uses workspace tokens instead of leftover blue-slate", () => {
const css = readFileSync(new URL("../styles/globals.css", import.meta.url), "utf8");
expect(css).toContain("html.dark[data-edgeever-environment=\"local\"] body::after");
expect(css).toContain("--tooltip-bg: #252c28;");
expect(css).toContain("--scrollbar-thumb: rgb(137 150 142 / 0.38);");
expect(css).toContain("--search-match: rgb(22 160 110 / 0.32);");
expect(css).toContain(":root.dark .ProseMirror .edgeever-mermaid-preview");
expect(css).toContain("background: var(--workspace-editor);");
expect(css).toContain("border-color: var(--workspace-divider);");
expect(css).not.toContain("background: #0f172a;");
expect(css).not.toContain("border-color: #334155;");
expect(css).toContain(":root.dark .edgeever-paper");
expect(css).toContain("--tw-ring-offset-color: var(--workspace-canvas);");
});
test("every bundled editor theme has a complete accessible dark palette", () => {
for (const theme of BUILT_IN_EDITOR_THEMES) {
const { tokens } = readDarkThemeTokens(theme);
+6
View File
@@ -1,5 +1,6 @@
import { describe, expect, test } from "bun:test";
import {
getStoredMarkdownTheme,
MARKDOWN_THEME_NAMES,
MARKDOWN_THEME_PREFERENCES,
resolveMarkdownTheme,
@@ -15,6 +16,11 @@ describe("markdown theme contracts", () => {
expect(resolveMarkdownTheme("tokyo-night", "light")).toBe("tokyo-night");
});
test("unset Markdown theme preference follows appearance instead of a dark-only default", () => {
expect(getStoredMarkdownTheme()).toBe("auto");
expect(MARKDOWN_THEME_PREFERENCES[0]).toBe("auto");
});
test("all markdown theme preferences are correctly defined", () => {
expect(MARKDOWN_THEME_PREFERENCES[0]).toBe("auto");
for (const theme of MARKDOWN_THEME_NAMES) {
+16
View File
@@ -0,0 +1,16 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
describe("tailwind theme tokens", () => {
test("web PostCSS loads the web Tailwind config that paints with CSS variables", () => {
const postcss = readFileSync(new URL("../../postcss.config.js", import.meta.url), "utf8");
const tailwind = readFileSync(new URL("../../tailwind.config.ts", import.meta.url), "utf8");
expect(postcss).toContain('path.join(configDirectory, "tailwind.config.ts")');
expect(tailwind).toContain("rgb(var(--slate-${shade}-rgb) / <alpha-value>)");
expect(tailwind).toContain("rgb(var(--brand-green-${shade}-rgb) / <alpha-value>)");
expect(tailwind).toContain("rgb(var(--amber-50-rgb) / <alpha-value>)");
expect(tailwind).toContain("rgb(var(--rose-50-rgb) / <alpha-value>)");
expect(tailwind).toContain("950: slate(950)");
});
});
+5
View File
@@ -21,6 +21,9 @@ describe("application color system", () => {
expect(globals).toContain("--workspace-editor: #ffffff;");
expect(globals).toContain("--workspace-selection: #edf0f2;");
expect(globals).toContain("--slate-500-rgb: 115 115 115;");
expect(globals).toContain("--slate-950-rgb: 10 10 10;");
expect(globals).toContain("--amber-50-rgb: 255 251 235;");
expect(globals).toContain("--rose-50-rgb: 255 241 242;");
expect(globals).not.toContain("--slate-500-rgb: 100 116 139;");
expect(contrastRatio("#222222", "#ffffff")).toBeGreaterThanOrEqual(4.5);
expect(contrastRatio("#737373", "#ffffff")).toBeGreaterThanOrEqual(4.5);
@@ -31,7 +34,9 @@ describe("application color system", () => {
expect(globals).toContain("--workspace-sidebar: #121612;");
expect(globals).toContain("--workspace-memo-list: #151a17;");
expect(globals).toContain("--workspace-editor: #191e1b;");
expect(globals).not.toContain("scrollbar-color: rgba(100, 116, 139, 0.18)");
expect(contrastRatio("#cad4ce", "#191e1b")).toBeGreaterThanOrEqual(4.5);
expect(contrastRatio("#9aa9a0", "#191e1b")).toBeGreaterThanOrEqual(4.5);
expect(contrastRatio("#84948a", "#191e1b")).toBeGreaterThanOrEqual(4.5);
});
});
+114 -252
View File
@@ -63,6 +63,20 @@ html[data-edgeever-environment="demo"] body::after {
color: rgb(180 83 9);
}
html.dark[data-edgeever-environment="local"] body::after {
border-color: rgb(22 160 110 / 42%);
background: rgb(18 42 36 / 92%);
color: #9ee0c8;
box-shadow: 0 2px 8px rgb(0 0 0 / 28%);
}
html.dark[data-edgeever-environment="demo"] body::after {
border-color: rgb(217 119 6 / 48%);
background: rgb(69 26 3 / 88%);
color: #fde68a;
box-shadow: 0 2px 8px rgb(0 0 0 / 28%);
}
.ProseMirror .tiptap-mathematics-render[data-type="block-math"] {
max-width: 100%;
margin: 1rem 0;
@@ -117,6 +131,13 @@ html[data-edgeever-environment="demo"] body::after {
--workspace-divider: #e9ebed;
--workspace-memo-divider: #edf0f2;
--workspace-selection: #edf0f2;
--tooltip-bg: #1a1f1c;
--tooltip-fg: #f4f7f5;
--scrollbar-thumb: rgb(115 115 115 / 0.28);
--scrollbar-thumb-hover: rgb(115 115 115 / 0.42);
--search-match: rgb(254 240 138 / 0.8);
--search-match-active: rgb(251 191 36 / 0.9);
--switch-thumb: #ffffff;
--slate-50-rgb: 250 250 250;
--slate-100-rgb: 245 245 245;
--slate-200-rgb: 229 229 229;
@@ -127,6 +148,24 @@ html[data-edgeever-environment="demo"] body::after {
--slate-700-rgb: 64 64 64;
--slate-800-rgb: 38 38 38;
--slate-900-rgb: 24 24 24;
--slate-950-rgb: 10 10 10;
--amber-50-rgb: 255 251 235;
--amber-100-rgb: 254 243 199;
--amber-200-rgb: 253 230 138;
--amber-600-rgb: 217 119 6;
--amber-700-rgb: 180 83 9;
--amber-800-rgb: 146 64 14;
--amber-900-rgb: 120 53 15;
--amber-950-rgb: 69 26 3;
--rose-50-rgb: 255 241 242;
--rose-100-rgb: 255 228 230;
--rose-200-rgb: 254 205 211;
--rose-500-rgb: 244 63 94;
--rose-600-rgb: 225 29 72;
--rose-700-rgb: 190 18 60;
--rose-800-rgb: 159 18 57;
--rose-950-rgb: 76 5 25;
--blue-600-rgb: 37 99 235;
--dark-surface-rgb: 255 255 255;
--editor-body-font-size: 0.9375rem;
--editor-body-line-height: 1.6;
@@ -201,16 +240,41 @@ html[data-edgeever-environment="demo"] body::after {
--workspace-divider: #2a312d;
--workspace-memo-divider: #3b4540;
--workspace-selection: #1d3028;
--tooltip-bg: #252c28;
--tooltip-fg: #e9efeb;
--scrollbar-thumb: rgb(137 150 142 / 0.38);
--scrollbar-thumb-hover: rgb(137 150 142 / 0.55);
--search-match: rgb(22 160 110 / 0.32);
--search-match-active: rgb(36 185 134 / 0.48);
--switch-thumb: #eef2ef;
--slate-50-rgb: 15 20 17;
--slate-100-rgb: 21 28 24;
--slate-200-rgb: 38 49 43;
--slate-300-rgb: 55 68 61;
--slate-400-rgb: 78 92 84;
--slate-500-rgb: 137 150 142;
--slate-300-rgb: 110 124 116;
--slate-400-rgb: 132 148 138;
--slate-500-rgb: 154 169 160;
--slate-600-rgb: 182 195 187;
--slate-700-rgb: 202 212 206;
--slate-800-rgb: 221 229 224;
--slate-900-rgb: 233 239 235;
--slate-950-rgb: 238 243 240;
--amber-50-rgb: 69 26 3;
--amber-100-rgb: 80 30 5;
--amber-200-rgb: 146 64 14;
--amber-600-rgb: 253 230 138;
--amber-700-rgb: 253 230 138;
--amber-800-rgb: 252 211 77;
--amber-900-rgb: 254 243 199;
--amber-950-rgb: 255 251 235;
--rose-50-rgb: 76 5 25;
--rose-100-rgb: 88 8 30;
--rose-200-rgb: 136 19 55;
--rose-500-rgb: 253 164 175;
--rose-600-rgb: 253 164 175;
--rose-700-rgb: 254 205 211;
--rose-800-rgb: 254 205 211;
--rose-950-rgb: 255 241 242;
--blue-600-rgb: 147 197 253;
--dark-surface-rgb: 25 30 27;
--brand-green: #16a06e;
--brand-green-hover: #24b986;
@@ -290,6 +354,7 @@ html[data-edgeever-extension-theme] body {
}
html[data-edgeever-extension-theme] .bg-white,
html[data-edgeever-extension-theme] .bg-card,
html[data-edgeever-extension-theme] .bg-slate-50,
html[data-edgeever-extension-theme] .bg-slate-50\/50 {
background-color: var(--edgeever-theme-surface, #fff);
@@ -340,12 +405,6 @@ html[data-edgeever-extension-theme] kbd {
font-family: var(--edgeever-theme-font-mono, ui-monospace, monospace);
}
/* Keep the strongest text neutral instead of falling back to Tailwind's
* blue-black slate-950 literal. Background uses of slate-950 remain intact. */
:root [class~="text-slate-950"] {
color: hsl(var(--foreground));
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 180ms;
@@ -628,12 +687,12 @@ textarea {
}
:root.dark .edgeever-public-share .ProseMirror {
color: #f8fafc;
color: hsl(var(--foreground));
}
:root.dark .edgeever-public-share .ProseMirror [data-edgeever-theme-block] {
border-color: #334155;
background: #1e293b;
border-color: var(--workspace-divider);
background: var(--workspace-sidebar);
}
:root.dark .edgeever-public-share .ProseMirror [data-theme-block-kind="chapter"] {
@@ -649,13 +708,13 @@ textarea {
.ProseMirror .edgeever-search-match {
border-radius: 0.2rem;
background-color: rgb(254 240 138 / 0.8);
box-shadow: 0 0 0 1px rgb(234 179 8 / 0.25);
background-color: var(--search-match);
box-shadow: 0 0 0 1px rgb(var(--brand-green-rgb) / 0.22);
}
.ProseMirror .edgeever-search-match-active {
background-color: rgb(251 191 36 / 0.9);
box-shadow: 0 0 0 2px rgb(217 119 6 / 0.45);
background-color: var(--search-match-active);
box-shadow: 0 0 0 2px rgb(var(--brand-green-rgb) / 0.4);
}
@media (max-width: 1023px) {
@@ -1104,7 +1163,7 @@ textarea {
}
.ProseMirror .edgeever-mermaid-tool-button:focus-visible {
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #0f766e;
box-shadow: 0 0 0 2px var(--workspace-editor), 0 0 0 4px var(--brand-green);
}
.ProseMirror .edgeever-mermaid-tool-button[data-state="copied"] {
@@ -1128,7 +1187,7 @@ textarea {
border: 1px solid #d9e2dc;
border-radius: 8px;
background: #fbfdfb;
scrollbar-color: rgb(100 116 139 / 0.35) transparent;
scrollbar-color: var(--scrollbar-thumb) transparent;
}
.ProseMirror .edgeever-mermaid-message,
@@ -1172,6 +1231,10 @@ textarea {
color: #24292e;
}
:root.dark .ProseMirror .edgeever-code-block {
color: hsl(var(--foreground));
}
.ProseMirror hr {
margin: var(--memo-content-divider-spacing) 0;
border: 0;
@@ -1556,16 +1619,16 @@ textarea {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(100, 116, 139, 0.18);
background: var(--scrollbar-thumb);
border-radius: 9999px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(100, 116, 139, 0.34);
background: var(--scrollbar-thumb-hover);
}
* {
scrollbar-width: thin;
scrollbar-color: rgba(100, 116, 139, 0.18) transparent;
scrollbar-color: var(--scrollbar-thumb) transparent;
}
/* Standard Markdown Content Rendering Styles */
@@ -1686,7 +1749,7 @@ textarea {
border-radius: 6px;
background: #fff;
overscroll-behavior-inline: contain;
scrollbar-color: rgb(100 116 139 / 0.28) transparent;
scrollbar-color: var(--scrollbar-thumb) transparent;
box-shadow: 0 1px 3px rgb(var(--slate-900-rgb) / 0.03);
}
@@ -1862,239 +1925,38 @@ textarea {
background: rgb(var(--slate-100-rgb) / 0.8);
}
/* Theme-aware surfaces for legacy white utilities and fixed semantic colors. */
:root.dark [class~="bg-white"] {
background-color: rgb(var(--dark-surface-rgb)) !important;
:root.dark .edgeever-paper,
:root.dark .edgeever-pdf-viewer canvas {
background-color: #fff !important;
}
:root.dark [class~="bg-white/70"] {
background-color: rgb(var(--dark-surface-rgb) / 0.7) !important;
}
:root.dark [class~="bg-white/75"] {
background-color: rgb(var(--dark-surface-rgb) / 0.75) !important;
}
:root.dark [class~="bg-white/80"] {
background-color: rgb(var(--dark-surface-rgb) / 0.8) !important;
}
:root.dark [class~="bg-white/85"] {
background-color: rgb(var(--dark-surface-rgb) / 0.85) !important;
}
:root.dark [class~="bg-white/90"] {
background-color: rgb(var(--dark-surface-rgb) / 0.9) !important;
}
:root.dark [class~="bg-white/95"] {
background-color: rgb(var(--dark-surface-rgb) / 0.95) !important;
}
:root.dark [class~="border-white"] {
border-color: rgb(var(--slate-200-rgb)) !important;
}
:root.dark [class~="text-slate-950"] {
color: hsl(var(--foreground)) !important;
}
:root.dark [class~="bg-slate-50"],
:root.dark [class*="bg-slate-50/"] { background-color: rgb(var(--slate-50-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-slate-100"],
:root.dark [class*="bg-slate-100/"] { background-color: rgb(var(--slate-100-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-slate-200"],
:root.dark [class*="bg-slate-200/"] { background-color: rgb(var(--slate-200-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-slate-300"],
:root.dark [class*="bg-slate-300/"] { background-color: rgb(var(--slate-300-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-slate-400"],
:root.dark [class*="bg-slate-400/"] { background-color: rgb(var(--slate-400-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-slate-500"],
:root.dark [class*="bg-slate-500/"] { background-color: rgb(var(--slate-500-rgb)) !important; }
:root.dark [class~="bg-slate-600"],
:root.dark [class*="bg-slate-600/"] { background-color: rgb(var(--slate-600-rgb)) !important; }
:root.dark [class~="bg-slate-700"],
:root.dark [class*="bg-slate-700/"] { background-color: rgb(var(--slate-700-rgb)) !important; }
:root.dark [class~="bg-slate-800"],
:root.dark [class*="bg-slate-800/"] { background-color: rgb(var(--slate-800-rgb)) !important; }
:root.dark [class~="bg-slate-900"],
:root.dark [class*="bg-slate-900/"] { background-color: rgb(var(--slate-900-rgb)) !important; }
:root.dark [class~="text-slate-50"] { color: #eef2ef !important; }
:root.dark [class~="text-slate-100"] { color: #e5ebe7 !important; }
:root.dark [class~="text-slate-200"] { color: #d7dfda !important; }
:root.dark [class~="text-slate-300"] { color: #c6d0ca !important; }
:root.dark [class~="text-slate-350"] { color: #aebbb3 !important; }
:root.dark [class~="text-slate-400"],
:root.dark [class*="text-slate-400/"] { color: rgb(132 148 138 / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="text-slate-500"] { color: #9aa9a0 !important; }
:root.dark [class~="text-slate-600"],
:root.dark [class~="text-slate-650"] { color: #b6c3bb !important; }
:root.dark [class~="text-slate-700"] { color: #cad4ce !important; }
:root.dark [class~="text-slate-800"] { color: #dde5e0 !important; }
:root.dark [class~="text-slate-900"] { color: #e9efeb !important; }
:root.dark [class~="border-slate-50"],
:root.dark [class*="border-slate-50/"] { border-color: rgb(var(--slate-50-rgb)) !important; }
:root.dark [class~="border-slate-100"],
:root.dark [class*="border-slate-100/"] { border-color: rgb(var(--slate-100-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-slate-200"],
:root.dark [class*="border-slate-200/"] { border-color: rgb(var(--slate-200-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-slate-300"],
:root.dark [class*="border-slate-300/"] { border-color: rgb(var(--slate-300-rgb) / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-slate-400"],
:root.dark [class*="border-slate-400/"] { border-color: rgb(var(--slate-400-rgb)) !important; }
:root.dark [class*="hover:bg-slate-50"]:hover,
:root.dark [class*="hover:bg-slate-100"]:hover { background-color: rgb(var(--slate-100-rgb)) !important; }
:root.dark [class*="hover:text-slate-900"]:hover,
:root.dark [class*="focus:text-slate-900"]:focus { color: rgb(var(--slate-900-rgb)) !important; }
/* Preserve slash opacity while remapping legacy light palette utilities. */
:root.dark [class*="/20"] { --dark-utility-alpha: 0.2; }
:root.dark [class*="/25"] { --dark-utility-alpha: 0.25; }
:root.dark [class*="/30"] { --dark-utility-alpha: 0.3; }
:root.dark [class*="/35"] { --dark-utility-alpha: 0.35; }
:root.dark [class*="/40"] { --dark-utility-alpha: 0.4; }
:root.dark [class*="/45"] { --dark-utility-alpha: 0.45; }
:root.dark [class*="/50"] { --dark-utility-alpha: 0.5; }
:root.dark [class*="/60"] { --dark-utility-alpha: 0.6; }
:root.dark [class*="/70"] { --dark-utility-alpha: 0.7; }
:root.dark [class*="/80"] { --dark-utility-alpha: 0.8; }
:root.dark [class*="/85"] { --dark-utility-alpha: 0.85; }
:root.dark [class*="/90"] { --dark-utility-alpha: 0.9; }
:root.dark [class*="/95"] { --dark-utility-alpha: 0.95; }
:root.dark [class~="bg-slate-50/20"] { --dark-utility-alpha: 0.2; }
:root.dark [class~="bg-slate-50/30"] { --dark-utility-alpha: 0.3; }
:root.dark [class~="bg-slate-50/50"],
:root.dark [class~="bg-slate-100/50"],
:root.dark [class~="bg-slate-200/50"],
:root.dark [class~="border-slate-200/50"] { --dark-utility-alpha: 0.5; }
:root.dark [class~="bg-slate-50/60"],
:root.dark [class~="border-slate-200/60"] { --dark-utility-alpha: 0.6; }
:root.dark [class~="bg-slate-100/70"],
:root.dark [class~="bg-slate-300/70"],
:root.dark [class~="border-slate-300/70"] { --dark-utility-alpha: 0.7; }
:root.dark [class~="bg-slate-50/80"],
:root.dark [class~="bg-slate-200/80"],
:root.dark [class~="bg-slate-400/80"],
:root.dark [class~="border-slate-100/80"],
:root.dark [class~="border-slate-200/80"] { --dark-utility-alpha: 0.8; }
:root.dark [class~="bg-slate-50/90"],
:root.dark [class~="text-slate-400/90"] { --dark-utility-alpha: 0.9; }
:root.dark [class~="divide-slate-100"] > :not([hidden]) ~ :not([hidden]) {
border-color: rgb(var(--slate-100-rgb)) !important;
}
:root.dark [class~="divide-slate-100/50"] > :not([hidden]) ~ :not([hidden]) {
border-color: rgb(var(--slate-100-rgb) / 0.5) !important;
}
:root.dark [class~="divide-slate-200"] > :not([hidden]) ~ :not([hidden]) {
border-color: rgb(var(--slate-200-rgb)) !important;
}
:root.dark [class~="divide-slate-200/80"] > :not([hidden]) ~ :not([hidden]) {
border-color: rgb(var(--slate-200-rgb) / 0.8) !important;
}
/* Semantic status colors need their own dark palette; numeric inversion is not meaningful for them. */
:root.dark [class~="text-emerald-500"],
:root.dark [class~="text-emerald-600"],
:root.dark [class~="text-emerald-650"],
:root.dark [class~="text-emerald-700"] { color: #6ee7b7 !important; }
:root.dark [class~="text-emerald-800"],
:root.dark [class~="text-emerald-900"],
:root.dark [class~="text-emerald-950"] { color: #a7f3d0 !important; }
:root.dark [class~="bg-emerald-50"],
:root.dark [class*="bg-emerald-50/"] { background-color: rgb(6 78 59 / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="bg-emerald-100"],
:root.dark [class*="bg-emerald-100/"] { background-color: rgb(6 95 70 / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-emerald-100"],
:root.dark [class~="border-emerald-200"],
:root.dark [class*="border-emerald-200/"] { border-color: #047857 !important; }
:root.dark [class~="text-amber-600"],
:root.dark [class~="text-amber-700"],
:root.dark [class~="text-amber-800"],
:root.dark [class~="text-amber-900"],
:root.dark [class~="text-amber-950"] { color: #fde68a !important; }
:root.dark [class~="bg-amber-50"],
:root.dark [class*="bg-amber-50/"] { background-color: rgb(69 26 3 / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-amber-100"],
:root.dark [class~="border-amber-200"],
:root.dark [class*="border-amber-200/"] { border-color: #92400e !important; }
:root.dark [class~="text-rose-500"],
:root.dark [class~="text-rose-600"],
:root.dark [class~="text-rose-700"],
:root.dark [class~="text-rose-800"],
:root.dark [class~="text-rose-950"] { color: #fda4af !important; }
:root.dark [class~="bg-rose-50"],
:root.dark [class*="bg-rose-50/"] { background-color: rgb(76 5 25 / var(--dark-utility-alpha, 1)) !important; }
:root.dark [class~="border-rose-100"],
:root.dark [class~="border-rose-200"] { border-color: #9f1239 !important; }
:root.dark [class~="text-blue-600"] { color: #93c5fd !important; }
:root.dark * {
--tw-ring-offset-color: #0f172a;
}
:root.dark [class~="bg-amber-50/60"] {
background-color: rgb(69 26 3 / 72%) !important;
}
:root.dark [class~="bg-amber-50"],
:root.dark [class~="bg-amber-50/95"] {
background-color: #451a03 !important;
}
:root.dark [class~="text-amber-600/80"] {
color: rgb(252 211 77 / 80%) !important;
}
:root.dark [class~="text-amber-700"],
:root.dark [class~="text-amber-800"] {
color: #fcd34d !important;
}
:root.dark [class~="bg-rose-50"],
:root.dark [class~="bg-rose-50/30"] {
background-color: #4c0519 !important;
}
:root.dark [class~="text-rose-600"],
:root.dark [class~="text-rose-700"] {
color: #fda4af !important;
}
:root.dark [class~="border-rose-100"],
:root.dark [class~="border-rose-200"] {
border-color: #881337 !important;
--tw-ring-offset-color: var(--workspace-canvas);
}
:root.dark .ProseMirror blockquote {
color: #cbd5e1;
color: hsl(var(--foreground));
background: color-mix(in srgb, var(--brand-green) 7%, transparent);
}
:root.dark .ProseMirror code,
:root.dark .markdown-content code {
border-color: rgba(22, 160, 110, 0.28);
background: rgba(22, 160, 110, 0.12);
color: #6ee7b7;
border-color: rgb(var(--brand-green-rgb) / 0.28);
background: rgb(var(--brand-green-rgb) / 0.1);
color: var(--brand-green-text);
}
:root.dark .ProseMirror pre,
:root.dark .markdown-content pre {
border-color: rgb(var(--slate-200-rgb) / 0.8);
background: rgb(var(--slate-100-rgb) / 0.8);
color: #e2e8f0;
color: hsl(var(--foreground));
}
:root.dark .ProseMirror .edgeever-code-copy-button {
border-color: rgb(var(--slate-200-rgb) / 0.9);
background: rgb(var(--slate-100-rgb) / 0.9);
color: #94a3b8;
color: rgb(var(--slate-500-rgb));
}
:root.dark .ProseMirror .edgeever-code-copy-button:hover,
@@ -2109,56 +1971,56 @@ textarea {
}
:root.dark .ProseMirror .edgeever-mermaid-toolbar {
border-color: #334155;
background: rgb(15 23 42 / 0.94);
border-color: var(--workspace-divider);
background: rgb(var(--dark-surface-rgb) / 0.94);
box-shadow: 0 1px 3px rgb(0 0 0 / 0.28);
}
:root.dark .ProseMirror .edgeever-mermaid-tool-button {
color: #cbd5e1;
color: hsl(var(--foreground));
}
:root.dark .ProseMirror .edgeever-mermaid-tool-button:hover,
:root.dark .ProseMirror .edgeever-mermaid-tool-button:focus-visible,
:root.dark .ProseMirror .edgeever-mermaid-tool-button[aria-pressed="true"] {
background: #1e3a2a;
color: #5eead4;
background: var(--brand-green-soft);
color: var(--brand-green-text);
}
:root.dark .ProseMirror .edgeever-mermaid-tool-button:focus-visible {
box-shadow: 0 0 0 2px #0f172a, 0 0 0 4px #5eead4;
box-shadow: 0 0 0 2px var(--workspace-editor), 0 0 0 4px var(--brand-green);
}
:root.dark .ProseMirror .edgeever-code-source {
border-color: rgb(var(--slate-200-rgb) / 0.8);
background: rgb(var(--slate-100-rgb) / 0.8);
color: #e2e8f0;
color: hsl(var(--foreground));
}
:root.dark .ProseMirror .edgeever-mermaid-preview {
border-color: #334155;
background: #0f172a;
border-color: var(--workspace-divider);
background: var(--workspace-editor);
}
:root.dark .markdown-content {
color: #cbd5e1;
color: hsl(var(--foreground));
}
:root.dark .markdown-content h1,
:root.dark .markdown-content h2,
:root.dark .markdown-content h3 {
color: #f8fafc;
color: hsl(var(--foreground));
}
:root.dark .markdown-content h1,
:root.dark .markdown-content hr {
border-color: #334155;
border-color: var(--workspace-divider);
}
:root.dark .markdown-content pre {
background-color: #13261a;
border-color: #166534;
color: #cbd5e1;
background-color: rgb(var(--slate-100-rgb) / 0.8);
border-color: rgb(var(--slate-200-rgb) / 0.8);
color: hsl(var(--foreground));
}
.edgeever-mermaid-viewer .yarl__toolbar {
@@ -2168,7 +2030,7 @@ textarea {
padding: 0.25rem;
border: 1px solid rgb(255 255 255 / 0.18);
border-radius: 8px;
background: rgb(15 23 42 / 0.88);
background: rgb(26 31 28 / 0.92);
box-shadow: 0 4px 16px rgb(0 0 0 / 0.24);
backdrop-filter: blur(8px);
}
@@ -2253,7 +2115,7 @@ textarea {
}
:root.dark .markdown-content code {
background-color: #13261a;
border-color: #166534;
color: #cbd5e1;
background-color: rgb(var(--brand-green-rgb) / 0.1);
border-color: rgb(var(--brand-green-rgb) / 0.28);
color: var(--brand-green-text);
}
+29 -1
View File
@@ -67,7 +67,35 @@ export default {
700: slate(700),
800: slate(800),
900: slate(900),
950: "#020617",
950: slate(950),
},
amber: {
50: "rgb(var(--amber-50-rgb) / <alpha-value>)",
100: "rgb(var(--amber-100-rgb) / <alpha-value>)",
200: "rgb(var(--amber-200-rgb) / <alpha-value>)",
600: "rgb(var(--amber-600-rgb) / <alpha-value>)",
700: "rgb(var(--amber-700-rgb) / <alpha-value>)",
800: "rgb(var(--amber-800-rgb) / <alpha-value>)",
900: "rgb(var(--amber-900-rgb) / <alpha-value>)",
950: "rgb(var(--amber-950-rgb) / <alpha-value>)",
},
rose: {
50: "rgb(var(--rose-50-rgb) / <alpha-value>)",
100: "rgb(var(--rose-100-rgb) / <alpha-value>)",
200: "rgb(var(--rose-200-rgb) / <alpha-value>)",
500: "rgb(var(--rose-500-rgb) / <alpha-value>)",
600: "rgb(var(--rose-600-rgb) / <alpha-value>)",
700: "rgb(var(--rose-700-rgb) / <alpha-value>)",
800: "rgb(var(--rose-800-rgb) / <alpha-value>)",
950: "rgb(var(--rose-950-rgb) / <alpha-value>)",
},
red: {
50: "rgb(var(--rose-50-rgb) / <alpha-value>)",
600: "rgb(var(--rose-600-rgb) / <alpha-value>)",
700: "rgb(var(--rose-700-rgb) / <alpha-value>)",
},
blue: {
600: "rgb(var(--blue-600-rgb) / <alpha-value>)",
},
border: "hsl(var(--border))",
input: "hsl(var(--input))",
+1 -1
View File
@@ -585,7 +585,7 @@ export const enUS = {
mermaidThemeTitle: "Diagram theme",
mermaidThemeDescription: "Follow the app appearance or choose a fixed visual theme for diagrams.",
markdownThemeTitle: "Markdown source theme",
markdownThemeDescription: "Choose a syntax highlighting and color theme for Markdown source editing mode.",
markdownThemeDescription: "Follow the app appearance or choose a fixed syntax highlighting theme for Markdown source editing.",
editorThemeTitle: "Editor theme",
editorThemeDescription: "Choose a writing style for the note editor. Saved in this browser.",
editorContentAlignmentTitle: "Note content layout",
+1 -1
View File
@@ -585,7 +585,7 @@ export const zhCN = {
mermaidThemeTitle: "图表主题",
mermaidThemeDescription: "跟随应用外观,或为笔记图表选择固定主题。",
markdownThemeTitle: "Markdown 源码主题",
markdownThemeDescription: "为 Markdown 源码编辑模式选择代码高亮与配色方案。",
markdownThemeDescription: "跟随应用外观,或为 Markdown 源码编辑模式选择固定的代码高亮配色。",
editorThemeTitle: "编辑器主题",
editorThemeDescription: "选择正文编辑区的排版风格,偏好会保存在当前浏览器中。",
editorContentAlignmentTitle: "笔记正文布局",
+2 -19
View File
@@ -1,24 +1,7 @@
import type { Config } from "tailwindcss";
import webConfig from "./apps/web/tailwind.config.ts";
export default {
...webConfig,
content: ["./apps/web/index.html", "./apps/web/src/**/*.{ts,tsx}"],
theme: {
extend: {
fontFamily: {
sans: [
"Inter",
"ui-sans-serif",
"system-ui",
"-apple-system",
"BlinkMacSystemFont",
"Segoe UI",
"sans-serif",
],
},
boxShadow: {
panel: "0 1px 2px rgb(15 23 42 / 0.08), 0 12px 30px rgb(15 23 42 / 0.06)",
},
},
},
plugins: [],
} satisfies Config;