Attempt to fix note resizing with React keys and Muuri sync
This commit is contained in:
@@ -177,7 +177,7 @@ export function AI_TESTER({ type }: { type: 'tags' | 'embeddings' }) {
|
||||
{type === 'tags' && result.success && result.tags && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Info className="h-4 w-4 text-blue-600" />
|
||||
<Info className="h-4 w-4 text-primary" />
|
||||
<span className="text-sm font-medium">Generated Tags:</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -247,7 +247,7 @@ export function AI_TESTER({ type }: { type: 'tags' | 'embeddings' }) {
|
||||
{/* Loading State */}
|
||||
{isLoading && (
|
||||
<div className="text-center py-4">
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-blue-600" />
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-primary" />
|
||||
<p className="text-sm text-muted-foreground mt-2">
|
||||
Testing {type === 'tags' ? 'tags generation' : 'embeddings'}...
|
||||
</p>
|
||||
|
||||
@@ -36,8 +36,8 @@ export default async function AITestPage() {
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
{/* Tags Provider Test */}
|
||||
<Card className="border-blue-200 dark:border-blue-900">
|
||||
<CardHeader className="bg-blue-50/50 dark:bg-blue-950/20">
|
||||
<Card className="border-primary/20 dark:border-primary/30">
|
||||
<CardHeader className="bg-primary/5 dark:bg-primary/10">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<span className="text-2xl">🏷️</span>
|
||||
Tags Generation Test
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function AdminAIPage() {
|
||||
title: 'Avg Response Time',
|
||||
value: '1.2s',
|
||||
trend: { value: 5, isPositive: true },
|
||||
icon: <Activity className="h-5 w-5 text-blue-600 dark:text-blue-400" />,
|
||||
icon: <Activity className="h-5 w-5 text-primary dark:text-primary-foreground" />,
|
||||
},
|
||||
{
|
||||
title: 'Active Features',
|
||||
@@ -103,7 +103,7 @@ export default async function AdminAIPage() {
|
||||
className={`px-2 py-1 text-xs font-medium rounded-full ${
|
||||
provider.status === 'Connected'
|
||||
? 'text-green-700 dark:text-green-400 bg-green-100 dark:bg-green-900'
|
||||
: 'text-blue-700 dark:text-blue-400 bg-blue-100 dark:bg-blue-900'
|
||||
: 'text-primary dark:text-primary-foreground bg-primary/10 dark:bg-primary/20'
|
||||
}`}
|
||||
>
|
||||
{provider.status}
|
||||
|
||||
@@ -11,7 +11,7 @@ export default async function AdminPage() {
|
||||
title: 'Total Users',
|
||||
value: users.length,
|
||||
trend: { value: 12, isPositive: true },
|
||||
icon: <Users className="h-5 w-5 text-blue-600 dark:text-blue-400" />,
|
||||
icon: <Users className="h-5 w-5 text-primary dark:text-primary-foreground" />,
|
||||
},
|
||||
{
|
||||
title: 'Active Sessions',
|
||||
|
||||
@@ -213,9 +213,9 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
||||
<form action={handleSaveAI}>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Tags Generation Section */}
|
||||
<div className="space-y-4 p-4 border rounded-lg bg-blue-50/50 dark:bg-blue-950/20">
|
||||
<div className="space-y-4 p-4 border rounded-lg bg-primary/5 dark:bg-primary/10">
|
||||
<h3 className="text-base font-semibold flex items-center gap-2">
|
||||
<span className="text-blue-600">🏷️</span> Tags Generation Provider
|
||||
<span className="text-primary">🏷️</span> Tags Generation Provider
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">AI provider for automatic tag suggestions. Recommended: Ollama (free, local).</p>
|
||||
|
||||
@@ -264,7 +264,7 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="OPENAI_API_KEY">API Key</Label>
|
||||
<Input id="OPENAI_API_KEY" name="OPENAI_API_KEY" type="password" defaultValue={config.OPENAI_API_KEY || ''} placeholder="sk-..." />
|
||||
<p className="text-xs text-muted-foreground">Your OpenAI API key from <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">platform.openai.com</a></p>
|
||||
<p className="text-xs text-muted-foreground">Your OpenAI API key from <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">platform.openai.com</a></p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="AI_MODEL_TAGS_OPENAI">Model</Label>
|
||||
@@ -278,7 +278,7 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
||||
<option key={model} value={model}>{model}</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="text-xs text-muted-foreground"><strong className="text-green-600">gpt-4o-mini</strong> = Best value • <strong className="text-blue-600">gpt-4o</strong> = Best quality</p>
|
||||
<p className="text-xs text-muted-foreground"><strong className="text-green-600">gpt-4o-mini</strong> = Best value • <strong className="text-primary">gpt-4o</strong> = Best quality</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -372,7 +372,7 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="OPENAI_API_KEY">API Key</Label>
|
||||
<Input id="OPENAI_API_KEY" name="OPENAI_API_KEY" type="password" defaultValue={config.OPENAI_API_KEY || ''} placeholder="sk-..." />
|
||||
<p className="text-xs text-muted-foreground">Your OpenAI API key from <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">platform.openai.com</a></p>
|
||||
<p className="text-xs text-muted-foreground">Your OpenAI API key from <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">platform.openai.com</a></p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="AI_MODEL_EMBEDDING_OPENAI">Model</Label>
|
||||
@@ -386,7 +386,7 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
||||
<option key={model} value={model}>{model}</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="text-xs text-muted-foreground"><strong className="text-green-600">text-embedding-3-small</strong> = Best value • <strong className="text-blue-600">text-embedding-3-large</strong> = Best quality</p>
|
||||
<p className="text-xs text-muted-foreground"><strong className="text-green-600">text-embedding-3-small</strong> = Best value • <strong className="text-primary">text-embedding-3-large</strong> = Best quality</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -26,7 +26,7 @@ import { cn } from '@/lib/utils'
|
||||
import { LabelFilter } from '@/components/label-filter'
|
||||
|
||||
export default function HomePage() {
|
||||
console.log('[HomePage] Component rendering')
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
// Force re-render when search params change (for filtering)
|
||||
@@ -58,7 +58,7 @@ export default function HomePage() {
|
||||
|
||||
// Callback for NoteInput to trigger notebook suggestion and update UI
|
||||
const handleNoteCreated = useCallback((note: Note) => {
|
||||
console.log('[NotebookSuggestion] Note created:', { id: note.id, notebookId: note.notebookId, contentLength: note.content?.length })
|
||||
|
||||
|
||||
// Update UI immediately by adding the note to the list if it matches current filters
|
||||
setNotes((prevNotes) => {
|
||||
@@ -120,19 +120,19 @@ export default function HomePage() {
|
||||
// Only suggest if note has no notebook and has 20+ words
|
||||
if (!note.notebookId) {
|
||||
const wordCount = (note.content || '').trim().split(/\s+/).filter(w => w.length > 0).length
|
||||
console.log('[NotebookSuggestion] Word count:', wordCount)
|
||||
|
||||
|
||||
if (wordCount >= 20) {
|
||||
console.log('[NotebookSuggestion] Triggering suggestion for note:', note.id)
|
||||
|
||||
setNotebookSuggestion({
|
||||
noteId: note.id,
|
||||
content: note.content || ''
|
||||
})
|
||||
} else {
|
||||
console.log('[NotebookSuggestion] Not enough words, need 20+')
|
||||
|
||||
}
|
||||
} else {
|
||||
console.log('[NotebookSuggestion] Note has notebook, skipping')
|
||||
|
||||
}
|
||||
|
||||
// Refresh in background to ensure data consistency (non-blocking)
|
||||
@@ -265,10 +265,10 @@ export default function HomePage() {
|
||||
|
||||
// Helper for Breadcrumbs
|
||||
const Breadcrumbs = ({ notebookName }: { notebookName: string }) => (
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-1">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground mb-1">
|
||||
<span>Notebooks</span>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
<span className="font-medium text-blue-600">{notebookName}</span>
|
||||
<span className="font-medium text-primary">{notebookName}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -283,12 +283,12 @@ export default function HomePage() {
|
||||
<div className="flex items-start justify-between">
|
||||
{/* Title Section */}
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-xl">
|
||||
<div className="p-3 bg-primary/10 dark:bg-primary/20 rounded-xl">
|
||||
{(() => {
|
||||
const Icon = getNotebookIcon(currentNotebook.icon || 'folder')
|
||||
return (
|
||||
<Icon
|
||||
className={cn("w-8 h-8", !currentNotebook.color && "text-blue-600 dark:text-blue-400")}
|
||||
className={cn("w-8 h-8", !currentNotebook.color && "text-primary dark:text-primary-foreground")}
|
||||
style={currentNotebook.color ? { color: currentNotebook.color } : undefined}
|
||||
/>
|
||||
)
|
||||
@@ -311,7 +311,7 @@ export default function HomePage() {
|
||||
/>
|
||||
<Button
|
||||
onClick={() => setShowNoteInput(!showNoteInput)}
|
||||
className="h-10 px-6 rounded-full bg-blue-600 hover:bg-blue-700 text-white font-medium shadow-sm gap-2 transition-all"
|
||||
className="h-10 px-6 rounded-full bg-primary hover:bg-primary/90 text-primary-foreground font-medium shadow-sm gap-2 transition-all"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add Note
|
||||
@@ -329,7 +329,7 @@ export default function HomePage() {
|
||||
{/* Title Section */}
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="p-3 bg-white border border-gray-100 dark:bg-gray-800 dark:border-gray-700 rounded-xl shadow-sm">
|
||||
<FileText className="w-8 h-8 text-blue-600" />
|
||||
<FileText className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-gray-900 dark:text-white tracking-tight">Notes</h1>
|
||||
</div>
|
||||
@@ -362,7 +362,7 @@ export default function HomePage() {
|
||||
|
||||
<Button
|
||||
onClick={() => setShowNoteInput(!showNoteInput)}
|
||||
className="h-10 px-6 rounded-full bg-blue-600 hover:bg-blue-700 text-white font-medium shadow-sm gap-2 transition-all"
|
||||
className="h-10 px-6 rounded-full bg-primary hover:bg-primary/90 text-primary-foreground font-medium shadow-sm gap-2 transition-all"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add Note
|
||||
|
||||
@@ -30,13 +30,15 @@ export function AppearanceSettingsForm({ initialTheme, initialFontSize }: Appear
|
||||
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'].includes(value)) root.classList.add('dark')
|
||||
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' })
|
||||
await updateUser({ theme: value as 'light' | 'dark' | 'auto' | 'sepia' | 'midnight' | 'blue' })
|
||||
}
|
||||
|
||||
const handleFontSizeChange = async (value: string) => {
|
||||
@@ -71,10 +73,11 @@ export function AppearanceSettingsForm({ initialTheme, initialFontSize }: Appear
|
||||
description="Select app's visual theme"
|
||||
value={theme}
|
||||
options={[
|
||||
{ value: 'light', label: 'Light' },
|
||||
{ value: 'slate', label: 'Light' },
|
||||
{ value: 'dark', label: 'Dark' },
|
||||
{ value: 'sepia', label: 'Sepia' },
|
||||
{ value: 'midnight', label: 'Midnight' },
|
||||
{ value: 'blue', label: 'Blue' },
|
||||
{ value: 'auto', label: 'Auto (system)' },
|
||||
]}
|
||||
onChange={handleThemeChange}
|
||||
|
||||
@@ -100,7 +100,7 @@ export default function SettingsPage() {
|
||||
</Link>
|
||||
<Link href="/settings/profile">
|
||||
<div className="p-4 border rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer">
|
||||
<RefreshCw className="h-6 w-6 text-blue-500 mb-2" />
|
||||
<RefreshCw className="h-6 w-6 text-primary mb-2" />
|
||||
<h3 className="font-semibold">Profile Settings</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Manage your account and preferences
|
||||
|
||||
@@ -152,7 +152,7 @@ export async function getNotes(includeArchived = false) {
|
||||
|
||||
try {
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
...(includeArchived ? {} : { isArchived: false }),
|
||||
},
|
||||
@@ -177,9 +177,9 @@ export async function getArchivedNotes() {
|
||||
|
||||
try {
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
isArchived: true
|
||||
isArchived: true
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' }
|
||||
})
|
||||
@@ -226,8 +226,8 @@ export async function searchNotes(query: string, useSemantic: boolean = false, n
|
||||
|
||||
// Check if query exists in title, content, or any label
|
||||
return title.includes(queryLower) ||
|
||||
content.includes(queryLower) ||
|
||||
labels.some((label: string) => label.toLowerCase().includes(queryLower));
|
||||
content.includes(queryLower) ||
|
||||
labels.some((label: string) => label.toLowerCase().includes(queryLower));
|
||||
});
|
||||
|
||||
return filteredNotes.map(parseNote);
|
||||
@@ -269,8 +269,8 @@ async function semanticSearch(query: string, userId: string, notebookId?: string
|
||||
|
||||
// Keyword match
|
||||
const keywordMatch = title.includes(queryLower) ||
|
||||
content.includes(queryLower) ||
|
||||
labels.some((l: string) => l.toLowerCase().includes(queryLower));
|
||||
content.includes(queryLower) ||
|
||||
labels.some((l: string) => l.toLowerCase().includes(queryLower));
|
||||
|
||||
// Semantic match (if embedding available)
|
||||
let semanticMatch = false;
|
||||
@@ -455,6 +455,9 @@ export async function updateNote(id: string, data: {
|
||||
if ('images' in data) updateData.images = data.images ? JSON.stringify(data.images) : null
|
||||
if ('links' in data) updateData.links = data.links ? JSON.stringify(data.links) : null
|
||||
if ('notebookId' in data) updateData.notebookId = data.notebookId
|
||||
// Explicitly handle size to ensure it propagates
|
||||
if ('size' in data && data.size) updateData.size = data.size
|
||||
|
||||
updateData.updatedAt = new Date()
|
||||
|
||||
const note = await prisma.note.update({
|
||||
@@ -469,17 +472,22 @@ export async function updateNote(id: string, data: {
|
||||
}
|
||||
|
||||
// IMPORTANT: Call revalidatePath to ensure UI updates
|
||||
// Revalidate main page, the note itself, and both old and new notebook paths
|
||||
revalidatePath('/')
|
||||
revalidatePath(`/note/${id}`)
|
||||
// BUT skip if only updating size (let optimistic UI handle it)
|
||||
const isSizeOnlyUpdate = Object.keys(data).length === 1 && 'size' in data
|
||||
|
||||
// If notebook changed, revalidate both notebook paths
|
||||
if (data.notebookId !== undefined && data.notebookId !== oldNotebookId) {
|
||||
if (oldNotebookId) {
|
||||
revalidatePath(`/notebook/${oldNotebookId}`)
|
||||
}
|
||||
if (data.notebookId) {
|
||||
revalidatePath(`/notebook/${data.notebookId}`)
|
||||
if (!isSizeOnlyUpdate) {
|
||||
// Revalidate main page, the note itself, and both old and new notebook paths
|
||||
revalidatePath('/')
|
||||
revalidatePath(`/note/${id}`)
|
||||
|
||||
// If notebook changed, revalidate both notebook paths
|
||||
if (data.notebookId !== undefined && data.notebookId !== oldNotebookId) {
|
||||
if (oldNotebookId) {
|
||||
revalidatePath(`/notebook/${oldNotebookId}`)
|
||||
}
|
||||
if (data.notebookId) {
|
||||
revalidatePath(`/notebook/${data.notebookId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,10 +525,12 @@ export async function updateColor(id: string, color: string) { return updateNote
|
||||
export async function updateLabels(id: string, labels: string[]) { return updateNote(id, { labels }) }
|
||||
export async function removeFusedBadge(id: string) { return updateNote(id, { autoGenerated: null }) }
|
||||
|
||||
// Update note size with revalidation
|
||||
// Update note size WITHOUT revalidation - client uses optimistic updates
|
||||
export async function updateSize(id: string, size: 'small' | 'medium' | 'large') {
|
||||
await updateNote(id, { size })
|
||||
revalidatePath('/')
|
||||
|
||||
const result = await updateNote(id, { size })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Get all unique labels
|
||||
@@ -711,7 +721,7 @@ export async function syncAllEmbeddings() {
|
||||
await prisma.note.update({ where: { id: note.id }, data: { embedding: JSON.stringify(embedding) } })
|
||||
updatedCount++;
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
}
|
||||
return { success: true, count: updatedCount }
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { prisma } from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export type UserSettingsData = {
|
||||
theme?: 'light' | 'dark' | 'auto' | 'sepia' | 'midnight'
|
||||
theme?: 'light' | 'dark' | 'auto' | 'sepia' | 'midnight' | 'blue'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ export async function getUserSettings(userId?: string) {
|
||||
})
|
||||
|
||||
return {
|
||||
theme: (user?.theme || 'light') as 'light' | 'dark' | 'auto'
|
||||
theme: (user?.theme || 'light') as 'light' | 'dark' | 'auto' | 'sepia' | 'midnight' | 'blue'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting user settings:', error)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
--breakpoint-ultra-wide: 1920px;
|
||||
|
||||
/* Custom colors matching Keep design */
|
||||
--color-primary: #356ac0;
|
||||
--color-primary: #64748b;
|
||||
--color-background-light: #f7f7f8;
|
||||
--color-background-dark: #1a1d23;
|
||||
}
|
||||
@@ -99,142 +99,307 @@
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--background: oklch(0.985 0.003 230); /* Blanc grisâtre */
|
||||
--foreground: oklch(0.2 0.02 230); /* Gris-bleu foncé */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--card-foreground: oklch(0.2 0.02 230);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
--primary: oklch(0.45 0.08 230); /* Gris-bleu doux */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
--secondary: oklch(0.94 0.005 230); /* Gris-bleu très pâle */
|
||||
--secondary-foreground: oklch(0.2 0.02 230);
|
||||
--muted: oklch(0.92 0.005 230);
|
||||
--muted-foreground: oklch(0.6 0.01 230);
|
||||
--accent: oklch(0.94 0.005 230);
|
||||
--accent-foreground: oklch(0.2 0.02 230);
|
||||
--destructive: oklch(0.6 0.18 25); /* Rouge */
|
||||
--border: oklch(0.9 0.008 230); /* Gris-bleu très clair */
|
||||
--input: oklch(0.98 0.003 230);
|
||||
--ring: oklch(0.7 0.005 230);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--sidebar: oklch(0.97 0.004 230);
|
||||
--sidebar-foreground: oklch(0.2 0.02 230);
|
||||
--sidebar-primary: oklch(0.45 0.08 230);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.94 0.005 230);
|
||||
--sidebar-accent-foreground: oklch(0.2 0.02 230);
|
||||
--sidebar-border: oklch(0.9 0.008 230);
|
||||
--sidebar-ring: oklch(0.7 0.005 230);
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
--background: oklch(0.985 0.003 230); /* Blanc grisâtre */
|
||||
--foreground: oklch(0.2 0.02 230); /* Gris-bleu foncé */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--card-foreground: oklch(0.2 0.02 230);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
--primary: oklch(0.45 0.08 230); /* Gris-bleu doux */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
--secondary: oklch(0.94 0.005 230); /* Gris-bleu très pâle */
|
||||
--secondary-foreground: oklch(0.2 0.02 230);
|
||||
--muted: oklch(0.92 0.005 230);
|
||||
--muted-foreground: oklch(0.6 0.01 230);
|
||||
--accent: oklch(0.94 0.005 230);
|
||||
--accent-foreground: oklch(0.2 0.02 230);
|
||||
--destructive: oklch(0.6 0.18 25); /* Rouge */
|
||||
--border: oklch(0.9 0.008 230); /* Gris-bleu très clair */
|
||||
--input: oklch(0.98 0.003 230);
|
||||
--ring: oklch(0.7 0.005 230);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
--sidebar: oklch(0.97 0.004 230);
|
||||
--sidebar-foreground: oklch(0.2 0.02 230);
|
||||
--sidebar-primary: oklch(0.45 0.08 230);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.94 0.005 230);
|
||||
--sidebar-accent-foreground: oklch(0.2 0.02 230);
|
||||
--sidebar-border: oklch(0.9 0.008 230);
|
||||
--sidebar-ring: oklch(0.7 0.005 230);
|
||||
}
|
||||
|
||||
[data-theme='light'].dark {
|
||||
--background: oklch(0.14 0.005 230); /* Noir grisâtre */
|
||||
--foreground: oklch(0.97 0.003 230); /* Blanc grisâtre */
|
||||
--card: oklch(0.18 0.006 230); /* Gris-bleu foncé */
|
||||
--card-foreground: oklch(0.97 0.003 230);
|
||||
--popover: oklch(0.18 0.006 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
--primary: oklch(0.55 0.08 230); /* Gris-bleu plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
--secondary: oklch(0.24 0.006 230);
|
||||
--secondary-foreground: oklch(0.97 0.003 230);
|
||||
--muted: oklch(0.22 0.006 230);
|
||||
--muted-foreground: oklch(0.55 0.01 230);
|
||||
--accent: oklch(0.24 0.006 230);
|
||||
--accent-foreground: oklch(0.97 0.003 230);
|
||||
--destructive: oklch(0.65 0.18 25);
|
||||
--border: oklch(0.28 0.01 230);
|
||||
--input: oklch(0.2 0.006 230);
|
||||
--ring: oklch(0.6 0.01 230);
|
||||
--popover: oklch(0.18 0.006 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
--sidebar: oklch(0.12 0.005 230);
|
||||
--sidebar-foreground: oklch(0.97 0.003 230);
|
||||
--sidebar-primary: oklch(0.55 0.08 230);
|
||||
--sidebar-primary-foreground: oklch(0.1 0 0);
|
||||
--sidebar-accent: oklch(0.24 0.006 230);
|
||||
--sidebar-accent-foreground: oklch(0.97 0.003 230);
|
||||
--sidebar-border: oklch(0.28 0.01 230);
|
||||
--sidebar-ring: oklch(0.6 0.01 230);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--background: oklch(0.14 0.005 230); /* Noir grisâtre */
|
||||
--foreground: oklch(0.97 0.003 230); /* Blanc grisâtre */
|
||||
--card: oklch(0.18 0.006 230); /* Gris-bleu foncé */
|
||||
--card-foreground: oklch(0.97 0.003 230);
|
||||
--popover: oklch(0.18 0.006 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
--primary: oklch(0.55 0.08 230); /* Gris-bleu plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
--secondary: oklch(0.24 0.006 230);
|
||||
--secondary-foreground: oklch(0.97 0.003 230);
|
||||
--muted: oklch(0.22 0.006 230);
|
||||
--muted-foreground: oklch(0.55 0.01 230);
|
||||
--accent: oklch(0.24 0.006 230);
|
||||
--accent-foreground: oklch(0.97 0.003 230);
|
||||
--destructive: oklch(0.65 0.18 25);
|
||||
--border: oklch(0.28 0.01 230);
|
||||
--input: oklch(0.2 0.006 230);
|
||||
--ring: oklch(0.6 0.01 230);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
--sidebar: oklch(0.12 0.005 230);
|
||||
--sidebar-foreground: oklch(0.97 0.003 230);
|
||||
--sidebar-primary: oklch(0.55 0.08 230);
|
||||
--sidebar-primary-foreground: oklch(0.1 0 0);
|
||||
--sidebar-accent: oklch(0.24 0.006 230);
|
||||
--sidebar-accent-foreground: oklch(0.97 0.003 230);
|
||||
--sidebar-border: oklch(0.28 0.01 230);
|
||||
--sidebar-ring: oklch(0.6 0.01 230);
|
||||
}
|
||||
|
||||
[data-theme='midnight'] {
|
||||
--background: oklch(0.18 0.04 260);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.22 0.05 260);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.22 0.05 260);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.7 0.15 260);
|
||||
--primary-foreground: oklch(0.18 0.04 260);
|
||||
--secondary: oklch(0.28 0.05 260);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.28 0.05 260);
|
||||
--muted-foreground: oklch(0.8 0.05 260);
|
||||
--accent: oklch(0.28 0.05 260);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.6 0.25 25);
|
||||
--border: oklch(0.3 0.05 260);
|
||||
--input: oklch(0.3 0.05 260);
|
||||
--ring: oklch(0.7 0.15 260);
|
||||
--background: oklch(0.94 0.005 250); /* Gris-bleu très pâle */
|
||||
--foreground: oklch(0.18 0.03 250); /* Gris-bleu très foncé */
|
||||
--card: oklch(0.97 0.006 250); /* Gris-bleu pâle */
|
||||
--card-foreground: oklch(0.18 0.03 250);
|
||||
--primary: oklch(0.5 0.12 250); /* Gris-bleu saturé */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
--secondary: oklch(0.2 0.01 250);
|
||||
--secondary-foreground: oklch(0.18 0.03 250);
|
||||
--muted: oklch(0.22 0.01 250);
|
||||
--muted-foreground: oklch(0.55 0.02 250);
|
||||
--accent: oklch(0.25 0.015 250);
|
||||
--accent-foreground: oklch(0.18 0.03 250);
|
||||
--destructive: oklch(0.6 0.22 25);
|
||||
--border: oklch(0.85 0.015 250);
|
||||
--input: oklch(0.25 0.01 250);
|
||||
--ring: oklch(0.65 0.015 250);
|
||||
--popover: oklch(0.97 0.006 250);
|
||||
--popover-foreground: oklch(0.18 0.03 250);
|
||||
--sidebar: oklch(0.9 0.01 250);
|
||||
--sidebar-foreground: oklch(0.18 0.03 250);
|
||||
--sidebar-primary: oklch(0.5 0.12 250);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.25 0.015 250);
|
||||
--sidebar-accent-foreground: oklch(0.18 0.03 250);
|
||||
--sidebar-border: oklch(0.85 0.015 250);
|
||||
--sidebar-ring: oklch(0.65 0.015 250);
|
||||
}
|
||||
|
||||
[data-theme='midnight'].dark {
|
||||
--background: oklch(0.1 0.01 250); /* Noir profond */
|
||||
--foreground: oklch(0.96 0.005 250); /* Blanc grisâtre */
|
||||
--card: oklch(0.15 0.015 250); /* Gris-bleu très foncé */
|
||||
--card-foreground: oklch(0.96 0.005 250);
|
||||
--primary: oklch(0.6 0.12 250); /* Gris-bleu vibrant */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
--secondary: oklch(0.18 0.015 250);
|
||||
--secondary-foreground: oklch(0.96 0.005 250);
|
||||
--muted: oklch(0.2 0.015 250);
|
||||
--muted-foreground: oklch(0.5 0.02 250);
|
||||
--accent: oklch(0.22 0.02 250);
|
||||
--accent-foreground: oklch(0.96 0.005 250);
|
||||
--destructive: oklch(0.65 0.2 25);
|
||||
--border: oklch(0.3 0.02 250);
|
||||
--input: oklch(0.22 0.02 250);
|
||||
--ring: oklch(0.55 0.02 250);
|
||||
--popover: oklch(0.15 0.015 250);
|
||||
--popover-foreground: oklch(0.96 0.005 250);
|
||||
--sidebar: oklch(0.08 0.01 250);
|
||||
--sidebar-foreground: oklch(0.96 0.005 250);
|
||||
--sidebar-primary: oklch(0.6 0.12 250);
|
||||
--sidebar-primary-foreground: oklch(0.1 0 0);
|
||||
--sidebar-accent: oklch(0.22 0.02 250);
|
||||
--sidebar-accent-foreground: oklch(0.96 0.005 250);
|
||||
--sidebar-border: oklch(0.3 0.02 250);
|
||||
--sidebar-ring: oklch(0.55 0.02 250);
|
||||
}
|
||||
|
||||
[data-theme='blue'] {
|
||||
--background: oklch(0.96 0.02 240);
|
||||
--foreground: oklch(0.15 0.05 240);
|
||||
--card: oklch(0.98 0.01 240);
|
||||
--card-foreground: oklch(0.15 0.05 240);
|
||||
--popover: oklch(0.98 0.01 240);
|
||||
--popover-foreground: oklch(0.15 0.05 240);
|
||||
--primary: oklch(0.45 0.15 240);
|
||||
--primary-foreground: oklch(0.98 0.01 240);
|
||||
--secondary: oklch(0.92 0.03 240);
|
||||
--secondary-foreground: oklch(0.15 0.05 240);
|
||||
--muted: oklch(0.92 0.03 240);
|
||||
--muted-foreground: oklch(0.5 0.05 240);
|
||||
--accent: oklch(0.92 0.03 240);
|
||||
--accent-foreground: oklch(0.15 0.05 240);
|
||||
--background: oklch(0.985 0.005 225); /* Blanc légèrement bleuté */
|
||||
--foreground: oklch(0.18 0.035 225); /* Gris-bleu foncé saturé */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--card-foreground: oklch(0.18 0.035 225);
|
||||
--primary: oklch(0.5 0.15 225); /* Bleu vibrant */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
--secondary: oklch(0.93 0.008 225);
|
||||
--secondary-foreground: oklch(0.18 0.035 225);
|
||||
--muted: oklch(0.9 0.01 225);
|
||||
--muted-foreground: oklch(0.58 0.015 225);
|
||||
--accent: oklch(0.93 0.01 225);
|
||||
--accent-foreground: oklch(0.18 0.035 225);
|
||||
--destructive: oklch(0.6 0.2 25);
|
||||
--border: oklch(0.85 0.05 240);
|
||||
--input: oklch(0.85 0.05 240);
|
||||
--ring: oklch(0.45 0.15 240);
|
||||
--sidebar: oklch(0.95 0.02 240);
|
||||
--sidebar-foreground: oklch(0.15 0.05 240);
|
||||
--sidebar-primary: oklch(0.45 0.15 240);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.01 240);
|
||||
--sidebar-accent: oklch(0.92 0.03 240);
|
||||
--sidebar-accent-foreground: oklch(0.15 0.05 240);
|
||||
--sidebar-border: oklch(0.85 0.05 240);
|
||||
--sidebar-ring: oklch(0.45 0.15 240);
|
||||
--border: oklch(0.87 0.012 225);
|
||||
--input: oklch(0.95 0.01 225);
|
||||
--ring: oklch(0.65 0.015 225);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.18 0.035 225);
|
||||
--sidebar: oklch(0.965 0.008 225);
|
||||
--sidebar-foreground: oklch(0.18 0.035 225);
|
||||
--sidebar-primary: oklch(0.5 0.15 225);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.93 0.01 225);
|
||||
--sidebar-accent-foreground: oklch(0.18 0.035 225);
|
||||
--sidebar-border: oklch(0.87 0.012 225);
|
||||
--sidebar-ring: oklch(0.65 0.015 225);
|
||||
}
|
||||
|
||||
[data-theme='blue'].dark {
|
||||
--background: oklch(0.13 0.008 225); /* Noir légèrement bleuté */
|
||||
--foreground: oklch(0.97 0.006 225); /* Blanc légèrement bleuté */
|
||||
--card: oklch(0.17 0.01 225); /* Gris-bleu foncé */
|
||||
--card-foreground: oklch(0.97 0.006 225);
|
||||
--primary: oklch(0.6 0.15 225); /* Bleu vibrant plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
--secondary: oklch(0.22 0.015 225);
|
||||
--secondary-foreground: oklch(0.97 0.006 225);
|
||||
--muted: oklch(0.25 0.02 225);
|
||||
--muted-foreground: oklch(0.52 0.018 225);
|
||||
--accent: oklch(0.25 0.025 225);
|
||||
--accent-foreground: oklch(0.97 0.006 225);
|
||||
--destructive: oklch(0.65 0.22 25);
|
||||
--border: oklch(0.32 0.018 225);
|
||||
--input: oklch(0.25 0.02 225);
|
||||
--ring: oklch(0.55 0.02 225);
|
||||
--popover: oklch(0.17 0.01 225);
|
||||
--popover-foreground: oklch(0.97 0.006 225);
|
||||
--sidebar: oklch(0.1 0.01 225);
|
||||
--sidebar-foreground: oklch(0.97 0.006 225);
|
||||
--sidebar-primary: oklch(0.6 0.15 225);
|
||||
--sidebar-primary-foreground: oklch(0.1 0 0);
|
||||
--sidebar-accent: oklch(0.25 0.025 225);
|
||||
--sidebar-accent-foreground: oklch(0.97 0.006 225);
|
||||
--sidebar-border: oklch(0.32 0.018 225);
|
||||
--sidebar-ring: oklch(0.55 0.02 225);
|
||||
}
|
||||
|
||||
[data-theme='sepia'] {
|
||||
--background: oklch(0.96 0.02 85);
|
||||
--foreground: oklch(0.25 0.02 85);
|
||||
--card: oklch(0.98 0.01 85);
|
||||
--card-foreground: oklch(0.25 0.02 85);
|
||||
--popover: oklch(0.98 0.01 85);
|
||||
--popover-foreground: oklch(0.25 0.02 85);
|
||||
--primary: oklch(0.45 0.1 35);
|
||||
--primary-foreground: oklch(0.98 0.01 85);
|
||||
--secondary: oklch(0.92 0.03 85);
|
||||
--secondary-foreground: oklch(0.25 0.02 85);
|
||||
--muted: oklch(0.92 0.03 85);
|
||||
--muted-foreground: oklch(0.5 0.05 85);
|
||||
--accent: oklch(0.92 0.03 85);
|
||||
--accent-foreground: oklch(0.25 0.02 85);
|
||||
--background: oklch(0.985 0.004 45); /* Blanc légèrement doré */
|
||||
--foreground: oklch(0.2 0.015 45); /* Gris-brun foncé */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--card-foreground: oklch(0.2 0.015 45);
|
||||
--primary: oklch(0.45 0.08 45); /* Gris-brun chaud */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
--secondary: oklch(0.94 0.008 45);
|
||||
--secondary-foreground: oklch(0.2 0.015 45);
|
||||
--muted: oklch(0.91 0.01 45);
|
||||
--muted-foreground: oklch(0.6 0.012 45);
|
||||
--accent: oklch(0.93 0.01 45);
|
||||
--accent-foreground: oklch(0.2 0.015 45);
|
||||
--destructive: oklch(0.6 0.2 25);
|
||||
--border: oklch(0.85 0.05 85);
|
||||
--input: oklch(0.85 0.05 85);
|
||||
--ring: oklch(0.45 0.1 35);
|
||||
--border: oklch(0.88 0.012 45);
|
||||
--input: oklch(0.97 0.008 45);
|
||||
--ring: oklch(0.68 0.01 45);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.2 0.015 45);
|
||||
--sidebar: oklch(0.96 0.01 45);
|
||||
--sidebar-foreground: oklch(0.2 0.015 45);
|
||||
--sidebar-primary: oklch(0.45 0.08 45);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.93 0.01 45);
|
||||
--sidebar-accent-foreground: oklch(0.2 0.015 45);
|
||||
--sidebar-border: oklch(0.88 0.012 45);
|
||||
--sidebar-ring: oklch(0.68 0.01 45);
|
||||
}
|
||||
|
||||
[data-theme='sepia'].dark {
|
||||
--background: oklch(0.15 0.008 45); /* Noir légèrement bruni */
|
||||
--foreground: oklch(0.97 0.005 45); /* Blanc légèrement bruni */
|
||||
--card: oklch(0.19 0.01 45); /* Gris-brun foncé */
|
||||
--card-foreground: oklch(0.97 0.005 45);
|
||||
--primary: oklch(0.55 0.08 45); /* Gris-brun plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
--secondary: oklch(0.25 0.015 45);
|
||||
--secondary-foreground: oklch(0.97 0.005 45);
|
||||
--muted: oklch(0.23 0.02 45);
|
||||
--muted-foreground: oklch(0.55 0.012 45);
|
||||
--accent: oklch(0.27 0.018 45);
|
||||
--accent-foreground: oklch(0.97 0.005 45);
|
||||
--destructive: oklch(0.65 0.2 25);
|
||||
--border: oklch(0.3 0.018 45);
|
||||
--input: oklch(0.27 0.02 45);
|
||||
--ring: oklch(0.58 0.02 45);
|
||||
--popover: oklch(0.19 0.01 45);
|
||||
--popover-foreground: oklch(0.97 0.005 45);
|
||||
--sidebar: oklch(0.12 0.01 45);
|
||||
--sidebar-foreground: oklch(0.97 0.005 45);
|
||||
--sidebar-primary: oklch(0.55 0.08 45);
|
||||
--sidebar-primary-foreground: oklch(0.1 0 0);
|
||||
--sidebar-accent: oklch(0.27 0.018 45);
|
||||
--sidebar-accent-foreground: oklch(0.97 0.005 45);
|
||||
--sidebar-border: oklch(0.3 0.018 45);
|
||||
--sidebar-ring: oklch(0.58 0.02 45);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
||||
Reference in New Issue
Block a user