feat: architectural grid editor fullPage + slash commands + doc info panel + AI title
This commit is contained in:
@@ -159,6 +159,7 @@ export function AgentsPageClient({
|
||||
role: formData.get('role') as string,
|
||||
sourceUrls: formData.get('sourceUrls') ? JSON.parse(formData.get('sourceUrls') as string) : undefined,
|
||||
sourceNotebookId: (formData.get('sourceNotebookId') as string) || undefined,
|
||||
sourceNoteIds: formData.get('sourceNoteIds') ? JSON.parse(formData.get('sourceNoteIds') as string) : undefined,
|
||||
targetNotebookId: (formData.get('targetNotebookId') as string) || undefined,
|
||||
frequency: formData.get('frequency') as string,
|
||||
tools: formData.get('tools') ? JSON.parse(formData.get('tools') as string) : undefined,
|
||||
@@ -168,6 +169,8 @@ export function AgentsPageClient({
|
||||
scheduledTime: (formData.get('scheduledTime') as string) || undefined,
|
||||
scheduledDay: formData.get('scheduledDay') ? Number(formData.get('scheduledDay')) : undefined,
|
||||
timezone: (formData.get('timezone') as string) || undefined,
|
||||
slideTheme: (formData.get('slideTheme') as string) || undefined,
|
||||
slideStyle: (formData.get('slideStyle') as string) || undefined,
|
||||
}
|
||||
if (editingAgent) {
|
||||
await updateAgent(editingAgent.id, data)
|
||||
@@ -196,70 +199,30 @@ export function AgentsPageClient({
|
||||
const existingAgentNames = useMemo(() => agents.map(a => a.name), [agents])
|
||||
|
||||
return (
|
||||
/* Full-bleed layout: -m-4 cancels the p-4 of the parent <main> */
|
||||
<div className="flex -m-4 h-[calc(100vh-4rem)] overflow-hidden">
|
||||
/* Full-bleed layout */
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
|
||||
{/* ── LEFT SIDEBAR ── */}
|
||||
<aside className="w-60 flex-shrink-0 flex flex-col bg-muted/30 border-r border-border/40 h-full font-display">
|
||||
{/* Brand */}
|
||||
<div className="flex items-center gap-3 px-5 py-5 border-b border-border/40">
|
||||
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center flex-shrink-0">
|
||||
<Bot className="w-4 h-4 text-primary-foreground" />
|
||||
</div>
|
||||
<span className="font-bold text-base tracking-tight">{t('agents.title')}</span>
|
||||
</div>
|
||||
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 p-3 space-y-0.5">
|
||||
<button
|
||||
onClick={() => setActiveTab('dashboard')}
|
||||
className={`w-full flex items-center gap-2.5 px-3 py-2 text-sm font-medium rounded-lg transition-all ${
|
||||
activeTab === 'dashboard'
|
||||
? 'bg-primary/10 text-primary border-r-2 border-primary'
|
||||
: 'text-muted-foreground hover:bg-background/70 hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Bot className="w-4 h-4" />
|
||||
{/* ── Top header bar — architectural grid style ── */}
|
||||
<header className="flex items-center justify-between px-12 py-10 border-b border-border/40 flex-shrink-0">
|
||||
<div>
|
||||
<h1 className="font-memento-serif text-4xl font-medium tracking-tight text-foreground leading-tight">
|
||||
{t('agents.myAgents')}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* Footer: Help */}
|
||||
<div className="p-3 border-t border-border/40">
|
||||
<button
|
||||
onClick={() => setShowHelp(true)}
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-background/70 hover:text-foreground rounded-lg transition-all"
|
||||
>
|
||||
<HelpCircle className="w-4 h-4" />
|
||||
{t('agents.help.btnLabel')}
|
||||
</button>
|
||||
</h1>
|
||||
<p className="text-[11px] text-muted-foreground uppercase tracking-[0.2em] font-bold mt-2">
|
||||
{t('agents.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
<button
|
||||
onClick={handleCreate}
|
||||
className="flex items-center gap-2 px-6 py-3 text-[13px] font-medium uppercase tracking-[0.12em] border border-foreground text-foreground hover:bg-foreground hover:text-background transition-all"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
{t('agents.newAgent')}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{/* ── MAIN CONTENT ── */}
|
||||
<div className="flex-1 flex flex-col min-w-0 bg-background overflow-hidden">
|
||||
|
||||
{/* Top header bar */}
|
||||
<header className="flex items-center justify-between px-8 py-4 border-b border-border/40 bg-background flex-shrink-0 font-display">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold tracking-tight">
|
||||
{t('agents.myAgents')}
|
||||
</h1>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
{t('agents.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCreate}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm font-semibold text-primary-foreground bg-primary hover:bg-primary/90 rounded-lg shadow-sm hover:shadow-md hover:shadow-primary/20 transition-all"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
{t('agents.newAgent')}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{/* Scrollable content area */}
|
||||
<main className="flex-1 overflow-y-auto p-8">
|
||||
{/* ── Scrollable content area ── */}
|
||||
<main className="flex-1 overflow-y-auto px-12 py-10">
|
||||
|
||||
{/* Dashboard tab - agents + templates */}
|
||||
{activeTab === 'dashboard' && (
|
||||
@@ -329,7 +292,6 @@ export function AgentsPageClient({
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{/* Sliding panels */}
|
||||
{showForm && (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Suspense } from "react";
|
||||
import { HeaderWrapper } from "@/components/header-wrapper";
|
||||
import { Sidebar } from "@/components/sidebar";
|
||||
import { ProvidersWrapper } from "@/components/providers-wrapper";
|
||||
import { auth } from "@/auth";
|
||||
@@ -7,28 +6,23 @@ import { headers } from "next/headers";
|
||||
import { detectUserLanguage, parseAcceptLanguage } from "@/lib/i18n/detect-user-language";
|
||||
import { loadTranslations } from "@/lib/i18n/load-translations";
|
||||
import { getAISettings } from "@/app/actions/ai-settings";
|
||||
|
||||
import { AIChat } from "@/components/ai-chat";
|
||||
import { AIChatLayoutBridge } from "@/components/ai-chat-layout-bridge";
|
||||
|
||||
export default async function MainLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
// Read browser language hint from Accept-Language header
|
||||
const headersList = await headers();
|
||||
const browserLang = parseAcceptLanguage(headersList.get("accept-language"));
|
||||
|
||||
// Run auth + language detection + translation loading in parallel
|
||||
const [session, initialLanguage] = await Promise.all([
|
||||
auth(),
|
||||
detectUserLanguage(browserLang),
|
||||
]);
|
||||
|
||||
// Load initial translations server-side to prevent hydration mismatch
|
||||
const initialTranslations = await loadTranslations(initialLanguage);
|
||||
|
||||
// Load AI settings to conditionally render AI features
|
||||
const aiSettings = session?.user?.id
|
||||
? await getAISettings(session.user.id)
|
||||
: null;
|
||||
@@ -36,25 +30,17 @@ export default async function MainLayout({
|
||||
|
||||
return (
|
||||
<ProvidersWrapper initialLanguage={initialLanguage} initialTranslations={initialTranslations}>
|
||||
<div className="bg-background-light dark:bg-background-dark font-display text-slate-900 dark:text-white overflow-hidden h-screen flex flex-col">
|
||||
{/* Top Navigation - Style Keep */}
|
||||
<HeaderWrapper user={session?.user} />
|
||||
{/* No top-bar header — sidebar-only navigation (architectural-grid design) */}
|
||||
<div className="flex h-screen overflow-hidden bg-[#E5E2D9]">
|
||||
<Suspense fallback={<div className="hidden w-80 shrink-0 md:block" />}>
|
||||
<Sidebar user={session?.user} />
|
||||
</Suspense>
|
||||
|
||||
{/* Main Layout */}
|
||||
<div className="flex flex-1 overflow-hidden relative">
|
||||
{/* Sidebar Navigation - Style Keep */}
|
||||
<Suspense fallback={<div className="w-64 flex-none hidden md:flex" />}>
|
||||
<Sidebar className="w-64 flex-none flex-col bg-white dark:bg-[#1e2128] border-e border-slate-200 dark:border-slate-800 overflow-y-auto hidden md:flex" user={session?.user} />
|
||||
</Suspense>
|
||||
<main className="memento-paper-texture flex min-h-0 flex-1 flex-col overflow-y-auto scroll-smooth">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<main className="flex min-h-0 flex-1 flex-col overflow-y-auto bg-background-light dark:bg-background-dark p-4 scroll-smooth">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* AI Chat Drawer — only shown if user has Assistant IA enabled */}
|
||||
{showAIAssistant && <AIChat />}
|
||||
</div>
|
||||
{showAIAssistant && <AIChatLayoutBridge />}
|
||||
</div>
|
||||
</ProvidersWrapper>
|
||||
);
|
||||
|
||||
@@ -10,10 +10,12 @@ export default async function HomePage() {
|
||||
|
||||
const notesViewMode =
|
||||
settings?.notesViewMode === 'masonry'
|
||||
? 'masonry' as const
|
||||
: settings?.notesViewMode === 'tabs' || settings?.notesViewMode === 'list'
|
||||
? 'tabs' as const
|
||||
: 'masonry' as const
|
||||
? ('masonry' as const)
|
||||
: settings?.notesViewMode === 'tabs'
|
||||
? ('tabs' as const)
|
||||
: settings?.notesViewMode === 'list'
|
||||
? ('list' as const)
|
||||
: ('masonry' as const)
|
||||
|
||||
return (
|
||||
<HomeClient
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
import { SettingsSection, SettingSelect } from '@/components/settings'
|
||||
import { updateAISettings as updateAI } from '@/app/actions/ai-settings'
|
||||
import { updateUserSettings as updateUser } from '@/app/actions/user-settings'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
|
||||
interface AppearanceSettingsFormProps {
|
||||
initialTheme: string
|
||||
initialFontSize: string
|
||||
initialCardSizeMode?: string
|
||||
}
|
||||
|
||||
export function AppearanceSettingsForm({ initialTheme, initialFontSize, initialCardSizeMode = 'variable' }: AppearanceSettingsFormProps) {
|
||||
const router = useRouter()
|
||||
const [theme, setTheme] = useState(initialTheme)
|
||||
const [fontSize, setFontSize] = useState(initialFontSize)
|
||||
const [cardSizeMode, setCardSizeMode] = useState(initialCardSizeMode)
|
||||
const { t } = useLanguage()
|
||||
|
||||
const handleThemeChange = async (value: string) => {
|
||||
setTheme(value)
|
||||
localStorage.setItem('theme-preference', value)
|
||||
|
||||
// Instant visual update
|
||||
const root = document.documentElement
|
||||
root.removeAttribute('data-theme')
|
||||
root.classList.remove('dark')
|
||||
|
||||
if (value === 'auto') {
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) root.classList.add('dark')
|
||||
} else if (value === 'dark') {
|
||||
root.classList.add('dark')
|
||||
} else if (value === 'light') {
|
||||
root.setAttribute('data-theme', 'light')
|
||||
} else {
|
||||
root.setAttribute('data-theme', value)
|
||||
if (['midnight', 'blue', 'sepia'].includes(value)) root.classList.add('dark')
|
||||
}
|
||||
|
||||
// Save to DB (no need for router.refresh - localStorage handles immediate visuals)
|
||||
await updateUser({ theme: value as 'light' | 'dark' | 'auto' | 'sepia' | 'midnight' | 'blue' })
|
||||
}
|
||||
|
||||
const handleFontSizeChange = async (value: string) => {
|
||||
setFontSize(value)
|
||||
|
||||
// Instant visual update
|
||||
const fontSizeMap: Record<string, string> = {
|
||||
'small': '14px', 'medium': '16px', 'large': '18px', 'extra-large': '20px'
|
||||
}
|
||||
const root = document.documentElement
|
||||
root.style.setProperty('--user-font-size', fontSizeMap[value] || '16px')
|
||||
|
||||
await updateAI({ fontSize: value as any })
|
||||
}
|
||||
|
||||
const handleCardSizeModeChange = async (value: string) => {
|
||||
setCardSizeMode(value)
|
||||
localStorage.setItem('card-size-mode', value)
|
||||
await updateUser({ cardSizeMode: value as 'variable' | 'uniform' })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2">{t('appearance.title')}</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{t('appearance.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SettingsSection
|
||||
title={t('settings.theme')}
|
||||
icon={<span className="text-2xl">🎨</span>}
|
||||
description={t('settings.themeLight') + ' / ' + t('settings.themeDark')}
|
||||
>
|
||||
<SettingSelect
|
||||
label={t('settings.theme')}
|
||||
description={t('settings.selectLanguage')}
|
||||
value={theme}
|
||||
options={[
|
||||
{ value: 'slate', label: t('settings.themeLight') },
|
||||
{ value: 'dark', label: t('settings.themeDark') },
|
||||
{ value: 'sepia', label: 'Sepia' },
|
||||
{ value: 'midnight', label: 'Midnight' },
|
||||
{ value: 'blue', label: 'Blue' },
|
||||
{ value: 'auto', label: t('settings.themeSystem') },
|
||||
]}
|
||||
onChange={handleThemeChange}
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
title={t('profile.fontSize')}
|
||||
icon={<span className="text-2xl">📝</span>}
|
||||
description={t('profile.fontSizeDescription')}
|
||||
>
|
||||
<SettingSelect
|
||||
label={t('profile.fontSize')}
|
||||
description={t('profile.selectFontSize')}
|
||||
value={fontSize}
|
||||
options={[
|
||||
{ value: 'small', label: t('profile.fontSizeSmall') },
|
||||
{ value: 'medium', label: t('profile.fontSizeMedium') },
|
||||
{ value: 'large', label: t('profile.fontSizeLarge') },
|
||||
]}
|
||||
onChange={handleFontSizeChange}
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
title={t('settings.cardSizeMode')}
|
||||
icon={<span className="text-2xl">📐</span>}
|
||||
description={t('settings.cardSizeModeDescription')}
|
||||
>
|
||||
<SettingSelect
|
||||
label={t('settings.cardSizeMode')}
|
||||
description={t('settings.selectCardSizeMode')}
|
||||
value={cardSizeMode}
|
||||
options={[
|
||||
{ value: 'variable', label: t('settings.cardSizeVariable') },
|
||||
{ value: 'uniform', label: t('settings.cardSizeUniform') },
|
||||
]}
|
||||
onChange={handleCardSizeModeChange}
|
||||
/>
|
||||
</SettingsSection>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import { updateUserSettings } from '@/app/actions/user-settings'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { toast } from 'sonner'
|
||||
import { Palette, Type, LayoutGrid, Maximize2 } from 'lucide-react'
|
||||
import { applyDocumentTheme, normalizeThemeId, type ThemeId } from '@/lib/apply-document-theme'
|
||||
|
||||
interface AppearanceSettingsClientProps {
|
||||
initialFontSize: string
|
||||
initialTheme: string
|
||||
initialNotesViewMode: 'masonry' | 'tabs'
|
||||
initialNotesViewMode: 'masonry' | 'tabs' | 'list'
|
||||
initialCardSizeMode?: 'variable' | 'uniform'
|
||||
initialFontFamily?: string
|
||||
}
|
||||
@@ -23,27 +24,18 @@ export function AppearanceSettingsClient({
|
||||
initialFontFamily = 'inter',
|
||||
}: AppearanceSettingsClientProps) {
|
||||
const { t } = useLanguage()
|
||||
const [theme, setTheme] = useState(initialTheme || 'light')
|
||||
const [theme, setTheme] = useState<ThemeId>(normalizeThemeId(initialTheme || 'light'))
|
||||
const [fontSize, setFontSize] = useState(initialFontSize || 'medium')
|
||||
const [notesViewMode, setNotesViewMode] = useState<'masonry' | 'tabs'>(initialNotesViewMode)
|
||||
const [notesViewMode, setNotesViewMode] = useState<'masonry' | 'tabs' | 'list'>(initialNotesViewMode)
|
||||
const [cardSizeMode, setCardSizeMode] = useState<'variable' | 'uniform'>(initialCardSizeMode)
|
||||
const [fontFamily, setFontFamily] = useState(initialFontFamily)
|
||||
|
||||
const handleThemeChange = async (value: string) => {
|
||||
setTheme(value)
|
||||
localStorage.setItem('theme-preference', value)
|
||||
const root = document.documentElement
|
||||
root.removeAttribute('data-theme')
|
||||
root.classList.remove('dark')
|
||||
if (value === 'auto') {
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) root.classList.add('dark')
|
||||
} else if (value === 'dark') {
|
||||
root.classList.add('dark')
|
||||
} else {
|
||||
root.setAttribute('data-theme', value)
|
||||
if (['midnight'].includes(value)) root.classList.add('dark')
|
||||
}
|
||||
await updateUserSettings({ theme: value as any })
|
||||
const next = normalizeThemeId(value)
|
||||
setTheme(next)
|
||||
localStorage.setItem('theme-preference', next)
|
||||
applyDocumentTheme(next)
|
||||
await updateUserSettings({ theme: next })
|
||||
toast.success(t('settings.settingsSaved') || 'Saved')
|
||||
}
|
||||
|
||||
@@ -56,7 +48,7 @@ export function AppearanceSettingsClient({
|
||||
}
|
||||
|
||||
const handleNotesViewChange = async (value: string) => {
|
||||
const mode = value === 'tabs' ? 'tabs' : 'masonry'
|
||||
const mode = value === 'tabs' ? 'tabs' : value === 'list' ? 'list' : 'masonry'
|
||||
setNotesViewMode(mode)
|
||||
await updateAISettings({ notesViewMode: mode })
|
||||
toast.success(t('settings.settingsSaved') || 'Saved')
|
||||
@@ -87,12 +79,14 @@ export function AppearanceSettingsClient({
|
||||
value,
|
||||
options,
|
||||
onChange,
|
||||
optionGroups,
|
||||
}: {
|
||||
icon: React.ElementType
|
||||
title: string
|
||||
description: string
|
||||
value: string
|
||||
options: { value: string; label: string }[]
|
||||
options?: { value: string; label: string }[]
|
||||
optionGroups?: { label: string; options: { value: string; label: string }[] }[]
|
||||
onChange: (v: string) => void
|
||||
}) => (
|
||||
<div className="bg-card rounded-lg border border-border p-6 shadow-sm flex flex-col gap-4">
|
||||
@@ -111,17 +105,56 @@ export function AppearanceSettingsClient({
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="w-full h-11 px-4 bg-muted border border-border rounded-lg text-foreground text-sm focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none appearance-none cursor-pointer transition-colors"
|
||||
>
|
||||
{options.map((o) => (
|
||||
<option key={o.value} value={o.value}>{o.label}</option>
|
||||
))}
|
||||
{optionGroups
|
||||
? optionGroups.map((g) => (
|
||||
<optgroup key={g.label} label={g.label}>
|
||||
{g.options.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{o.label}
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
))
|
||||
: options?.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{o.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground">
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const themeOptionGroups = [
|
||||
{
|
||||
label: t('settings.themeBaseGroup'),
|
||||
options: [
|
||||
{ value: 'light', label: t('settings.themeLight') },
|
||||
{ value: 'dark', label: t('settings.themeDark') },
|
||||
{ value: 'auto', label: t('settings.themeSystem') },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('settings.themePalettesGroup'),
|
||||
options: [
|
||||
{ value: 'sepia', label: t('settings.themeSepia') },
|
||||
{ value: 'midnight', label: t('settings.themeMidnight') },
|
||||
{ value: 'rose', label: t('settings.themeRose') },
|
||||
{ value: 'green', label: t('settings.themeGreen') },
|
||||
{ value: 'lavender', label: t('settings.themeLavender') },
|
||||
{ value: 'sand', label: t('settings.themeSand') },
|
||||
{ value: 'ocean', label: t('settings.themeOcean') },
|
||||
{ value: 'sunset', label: t('settings.themeSunset') },
|
||||
{ value: 'blue', label: t('settings.themeBlue') },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
@@ -135,19 +168,7 @@ export function AppearanceSettingsClient({
|
||||
title={t('settings.theme')}
|
||||
description={t('appearance.selectTheme')}
|
||||
value={theme}
|
||||
options={[
|
||||
{ value: 'light', label: t('settings.themeLight') },
|
||||
{ value: 'dark', label: t('settings.themeDark') },
|
||||
{ value: 'sepia', label: 'Sepia' },
|
||||
{ value: 'midnight', label: 'Midnight' },
|
||||
{ value: 'rose', label: 'Rose' },
|
||||
{ value: 'green', label: 'Green' },
|
||||
{ value: 'lavender', label: 'Lavender' },
|
||||
{ value: 'sand', label: 'Sand' },
|
||||
{ value: 'ocean', label: 'Ocean' },
|
||||
{ value: 'sunset', label: 'Sunset' },
|
||||
{ value: 'auto', label: t('settings.themeSystem') },
|
||||
]}
|
||||
optionGroups={themeOptionGroups}
|
||||
onChange={handleThemeChange}
|
||||
/>
|
||||
|
||||
@@ -168,7 +189,7 @@ export function AppearanceSettingsClient({
|
||||
<SelectCard
|
||||
icon={Type}
|
||||
title={t('appearance.fontFamilyLabel') || 'Police'}
|
||||
description={t('appearance.fontFamilyDescription') || 'Choisissez la police de l\'application'}
|
||||
description={t('appearance.fontFamilyDescription') || "Choisissez la police de l'application"}
|
||||
value={fontFamily}
|
||||
options={[
|
||||
{ value: 'inter', label: 'Inter' },
|
||||
@@ -184,6 +205,7 @@ export function AppearanceSettingsClient({
|
||||
value={notesViewMode}
|
||||
options={[
|
||||
{ value: 'masonry', label: t('appearance.notesViewMasonry') },
|
||||
{ value: 'list', label: t('appearance.notesViewList') },
|
||||
{ value: 'tabs', label: t('appearance.notesViewTabs') },
|
||||
]}
|
||||
onChange={handleNotesViewChange}
|
||||
|
||||
@@ -19,7 +19,13 @@ export default async function AppearanceSettingsPage() {
|
||||
<AppearanceSettingsClient
|
||||
initialFontSize={aiSettings.fontSize}
|
||||
initialTheme={userSettings.theme}
|
||||
initialNotesViewMode={aiSettings.notesViewMode === 'masonry' ? 'masonry' : 'tabs'}
|
||||
initialNotesViewMode={
|
||||
aiSettings.notesViewMode === 'masonry'
|
||||
? 'masonry'
|
||||
: aiSettings.notesViewMode === 'list'
|
||||
? 'list'
|
||||
: 'tabs'
|
||||
}
|
||||
initialCardSizeMode={userSettings.cardSizeMode}
|
||||
initialFontFamily={aiSettings.fontFamily || 'inter'}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user