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:
1 parent
8584a4a850
commit
ab7b3ecf47
87 files changed
+590
-615
No files matched your search
@@ -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: {},
|
||||
},
|
||||
};
|
||||
@@ -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")}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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))]"
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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 }); }}>
|
||||
|
||||
@@ -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 })}
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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",
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)");
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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))",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -585,7 +585,7 @@ export const zhCN = {
|
||||
mermaidThemeTitle: "图表主题",
|
||||
mermaidThemeDescription: "跟随应用外观,或为笔记图表选择固定主题。",
|
||||
markdownThemeTitle: "Markdown 源码主题",
|
||||
markdownThemeDescription: "为 Markdown 源码编辑模式选择代码高亮与配色方案。",
|
||||
markdownThemeDescription: "跟随应用外观,或为 Markdown 源码编辑模式选择固定的代码高亮配色。",
|
||||
editorThemeTitle: "编辑器主题",
|
||||
editorThemeDescription: "选择正文编辑区的排版风格,偏好会保存在当前浏览器中。",
|
||||
editorContentAlignmentTitle: "笔记正文布局",
|
||||
|
||||
+2
-19
@@ -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;
|
||||
Reference in new issue
Block a user