chore: remove dead code — 8 components, 5 libs, 4 API routes, 4 npm packages, 30+ scripts, dead CSS, dead exports
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
Removed unused components: - brainstorm-canvas, brainstorm-create-dialog, invite-dialog, manual-idea-dialog - note-inline-editor, profile-page-header, quota-paywall, label-management-dialog Removed dead lib files: - api-auth.ts, color-harmony-recommendation.ts, label-storage.ts, modern-color-options.ts - hooks/use-card-size-mode.ts Removed dead API routes: - ai/test-chat, ai/test-embeddings, ai/test-tags, admin/randomize-labels Removed unused npm packages: - cmdk, novel, tippy.js, react-force-graph-2d Cleaned dead CSS from globals.css: - acrylic-*, win11-shadow-*, muuri-grid/item, ai-glass, ai-tab-indicator, ai-send-btn, sidebar-view-toggle, memento-sidebar-depth Removed 29 orphan scripts and 3 root orphan files Cleaned dead exports from 8 lib files: - NOTE_TYPE_CONFIG, getPublishableKey, PROVIDER_DEFAULTS, useNotes/useNote/invalidateNote, etc.
This commit is contained in:
@@ -32,7 +32,7 @@ async function readLocalImage(relativePath: string): Promise<Buffer | null> {
|
||||
* Replaces local image references with cid: placeholders for inline attachments.
|
||||
* Returns the HTML and a list of attachments to include.
|
||||
*/
|
||||
export async function markdownToEmailHtml(md: string, appUrl: string): Promise<{ html: string; attachments: EmailAttachment[] }> {
|
||||
async function markdownToEmailHtml(md: string, appUrl: string): Promise<{ html: string; attachments: EmailAttachment[] }> {
|
||||
let html = md
|
||||
const attachments: EmailAttachment[] = []
|
||||
const baseUrl = appUrl.replace(/\/$/, '')
|
||||
|
||||
@@ -274,4 +274,4 @@ export function getChatProvider(config?: Record<string, string>): AIProvider {
|
||||
}
|
||||
|
||||
// Export for use by admin settings form and deploy scripts
|
||||
export { PROVIDER_DEFAULTS, getProviderConfigKeys };
|
||||
export { getProviderConfigKeys };
|
||||
|
||||
@@ -174,20 +174,6 @@ function getSecondaryProvider(lane: AiFeatureLane, config: Record<string, string
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldLogAiFallback(): boolean {
|
||||
return process.env.NODE_ENV !== 'production' || process.env.MEMENTO_AI_ROUTE_DEBUG === '1'
|
||||
}
|
||||
|
||||
export function formatAiFallbackDebug(meta: {
|
||||
lane: AiFeatureLane
|
||||
primaryProvider: string
|
||||
secondaryProvider: string
|
||||
primaryStatus?: number
|
||||
fallbackMs: number
|
||||
}): string {
|
||||
return JSON.stringify(meta)
|
||||
}
|
||||
|
||||
function logFallbackSuccess(meta: {
|
||||
lane: AiFeatureLane
|
||||
primaryProvider: string
|
||||
@@ -195,12 +181,13 @@ function logFallbackSuccess(meta: {
|
||||
primaryStatus?: number
|
||||
fallbackMs: number
|
||||
}): void {
|
||||
if (!shouldLogAiFallback()) return
|
||||
console.debug('[ai-fallback]', formatAiFallbackDebug(meta))
|
||||
if (meta.fallbackMs > FALLBACK_BUDGET_MS) {
|
||||
console.warn(
|
||||
`[ai-fallback] NFR-R1 budget exceeded: ${meta.fallbackMs.toFixed(1)}ms > ${FALLBACK_BUDGET_MS}ms`
|
||||
)
|
||||
if (process.env.NODE_ENV !== 'production' || process.env.MEMENTO_AI_ROUTE_DEBUG === '1') {
|
||||
console.debug('[ai-fallback]', JSON.stringify(meta))
|
||||
if (meta.fallbackMs > FALLBACK_BUDGET_MS) {
|
||||
console.warn(
|
||||
`[ai-fallback] NFR-R1 budget exceeded: ${meta.fallbackMs.toFixed(1)}ms > ${FALLBACK_BUDGET_MS}ms`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,21 +47,21 @@ async function resolveProviderForLane(
|
||||
return { provider, usedByok, route };
|
||||
}
|
||||
|
||||
export async function getChatProviderForBillingUser(
|
||||
async function getChatProviderForBillingUser(
|
||||
config: Record<string, string>,
|
||||
billingUserId?: string,
|
||||
): Promise<ProviderForUserResult> {
|
||||
return resolveProviderForLane('chat', config, billingUserId);
|
||||
}
|
||||
|
||||
export async function getTagsProviderForBillingUser(
|
||||
async function getTagsProviderForBillingUser(
|
||||
config: Record<string, string>,
|
||||
billingUserId?: string,
|
||||
): Promise<ProviderForUserResult> {
|
||||
return resolveProviderForLane('tags', config, billingUserId);
|
||||
}
|
||||
|
||||
export async function getEmbeddingsProviderForBillingUser(
|
||||
async function getEmbeddingsProviderForBillingUser(
|
||||
config: Record<string, string>,
|
||||
billingUserId?: string,
|
||||
): Promise<ProviderForUserResult> {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { auth } from '@/auth'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function requireAuth() {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return { session: null, error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) }
|
||||
}
|
||||
return { session, error: null }
|
||||
}
|
||||
|
||||
export async function requireAdmin() {
|
||||
const result = await requireAuth()
|
||||
if (result.error) return result
|
||||
if ((result.session!.user as any).role !== 'ADMIN') {
|
||||
return { session: null, error: NextResponse.json({ error: 'Forbidden' }, { status: 403 }) }
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* Valeurs persistées pour User.theme / localStorage `theme-preference`.
|
||||
* Une seule source de vérité pour le DOM : évite les écarts entre header, settings et hydratation.
|
||||
*/
|
||||
export const THEME_IDS = [
|
||||
const THEME_IDS = [
|
||||
'light',
|
||||
'dark',
|
||||
'auto',
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
/**
|
||||
* PROPOSITION D'HARMONIE DE COULEURS
|
||||
* ===================================
|
||||
*
|
||||
* Recommandations pour un système de couleurs unifié et moderne
|
||||
* Approche contemporaine des couleurs de notes
|
||||
*
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 1. PALETTE PRINCIPALE DU THÈME (OKLCH)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Proposition de palette principale avec meilleure cohérence
|
||||
* Utilise OKLCH pour une meilleure accessibilité et cohérence perceptive
|
||||
*/
|
||||
export const RECOMMENDED_THEME_COLORS = {
|
||||
/* ============ THEME LIGHT ============ */
|
||||
light: {
|
||||
// Backgrounds
|
||||
background: 'oklch(0.99 0.002 250)', // Blanc très légèrement bleuté
|
||||
card: 'oklch(1 0 0)', // Blanc pur
|
||||
sidebar: 'oklch(0.96 0.005 250)', // Gris-bleu très pâle
|
||||
input: 'oklch(0.97 0.003 250)', // Gris-bleu pâle
|
||||
|
||||
// Textes
|
||||
foreground: 'oklch(0.18 0.01 250)', // Gris-bleu foncé (meilleur contraste)
|
||||
'foreground-secondary': 'oklch(0.45 0.01 250)', // Gris moyen
|
||||
'foreground-muted': 'oklch(0.6 0.005 250)', // Gris clair
|
||||
|
||||
// Primary Actions
|
||||
primary: 'oklch(0.55 0.2 250)', // Bleu Keep (plus vibrant)
|
||||
'primary-hover': 'oklch(0.5 0.22 250)', // Bleu Keep foncé
|
||||
'primary-foreground': 'oklch(0.99 0 0)', // Blanc pur
|
||||
|
||||
// Accents
|
||||
accent: 'oklch(0.92 0.015 250)', // Bleu très pâle
|
||||
'accent-foreground': 'oklch(0.18 0.01 250)',
|
||||
|
||||
// Borders
|
||||
border: 'oklch(0.88 0.01 250)', // Gris-bleu très clair
|
||||
'border-hover': 'oklch(0.8 0.015 250)',
|
||||
|
||||
// Functional
|
||||
success: 'oklch(0.65 0.15 145)', // Vert
|
||||
warning: 'oklch(0.75 0.12 70)', // Jaune/orange
|
||||
destructive: 'oklch(0.6 0.2 25)', // Rouge
|
||||
},
|
||||
|
||||
/* ============ THEME DARK ============ */
|
||||
dark: {
|
||||
// Backgrounds
|
||||
background: 'oklch(0.12 0.01 250)', // Noir légèrement bleuté
|
||||
card: 'oklch(0.16 0.01 250)', // Gris-bleu foncé
|
||||
sidebar: 'oklch(0.1 0.01 250)', // Noir bleuté
|
||||
input: 'oklch(0.18 0.01 250)',
|
||||
|
||||
// Textes
|
||||
foreground: 'oklch(0.96 0.002 250)', // Blanc légèrement bleuté
|
||||
'foreground-secondary': 'oklch(0.75 0.005 250)',
|
||||
'foreground-muted': 'oklch(0.55 0.01 250)',
|
||||
|
||||
// Primary Actions
|
||||
primary: 'oklch(0.65 0.2 250)', // Bleu plus clair
|
||||
'primary-hover': 'oklch(0.7 0.2 250)',
|
||||
'primary-foreground': 'oklch(0.1 0 0)',
|
||||
|
||||
// Accents
|
||||
accent: 'oklch(0.22 0.01 250)',
|
||||
'accent-foreground': 'oklch(0.96 0.002 250)',
|
||||
|
||||
// Borders
|
||||
border: 'oklch(0.25 0.015 250)',
|
||||
'border-hover': 'oklch(0.35 0.015 250)',
|
||||
|
||||
// Functional
|
||||
success: 'oklch(0.7 0.15 145)',
|
||||
warning: 'oklch(0.8 0.12 70)',
|
||||
destructive: 'oklch(0.65 0.2 25)',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// 2. PALETTE DES COULEURS DE NOTES (UNIFIÉE)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Nouvelle palette de couleurs pour les notes
|
||||
* Harmonisée avec le thème principal
|
||||
* Meilleure accessibilité WCAG AA (contraste minimum 4.5:1)
|
||||
*/
|
||||
export const RECOMMENDED_NOTE_COLORS = {
|
||||
default: {
|
||||
// Light theme
|
||||
bg: 'bg-white',
|
||||
'bg-dark': 'dark:bg-neutral-900',
|
||||
border: 'border-neutral-200',
|
||||
'border-dark': 'dark:border-neutral-800',
|
||||
hover: 'hover:bg-neutral-50',
|
||||
'hover-dark': 'dark:hover:bg-neutral-800',
|
||||
// Pour texte sombre
|
||||
text: 'text-neutral-900',
|
||||
'text-dark': 'dark:text-neutral-100',
|
||||
},
|
||||
red: {
|
||||
bg: 'bg-red-50',
|
||||
'bg-dark': 'dark:bg-red-950/40',
|
||||
border: 'border-red-100',
|
||||
'border-dark': 'dark:border-red-900/50',
|
||||
hover: 'hover:bg-red-100',
|
||||
'hover-dark': 'dark:hover:bg-red-950/60',
|
||||
text: 'text-red-950',
|
||||
'text-dark': 'dark:text-red-100',
|
||||
},
|
||||
orange: {
|
||||
bg: 'bg-orange-50',
|
||||
'bg-dark': 'dark:bg-orange-950/40',
|
||||
border: 'border-orange-100',
|
||||
'border-dark': 'dark:border-orange-900/50',
|
||||
hover: 'hover:bg-orange-100',
|
||||
'hover-dark': 'dark:hover:bg-orange-950/60',
|
||||
text: 'text-orange-950',
|
||||
'text-dark': 'dark:text-orange-100',
|
||||
},
|
||||
yellow: {
|
||||
bg: 'bg-yellow-50',
|
||||
'bg-dark': 'dark:bg-yellow-950/40',
|
||||
border: 'border-yellow-100',
|
||||
'border-dark': 'dark:border-yellow-900/50',
|
||||
hover: 'hover:bg-yellow-100',
|
||||
'hover-dark': 'dark:hover:bg-yellow-950/60',
|
||||
text: 'text-yellow-950',
|
||||
'text-dark': 'dark:text-yellow-100',
|
||||
},
|
||||
green: {
|
||||
bg: 'bg-emerald-50',
|
||||
'bg-dark': 'dark:bg-emerald-950/40',
|
||||
border: 'border-emerald-100',
|
||||
'border-dark': 'dark:border-emerald-900/50',
|
||||
hover: 'hover:bg-emerald-100',
|
||||
'hover-dark': 'dark:hover:bg-emerald-950/60',
|
||||
text: 'text-emerald-950',
|
||||
'text-dark': 'dark:text-emerald-100',
|
||||
},
|
||||
teal: {
|
||||
bg: 'bg-teal-50',
|
||||
'bg-dark': 'dark:bg-teal-950/40',
|
||||
border: 'border-teal-100',
|
||||
'border-dark': 'dark:border-teal-900/50',
|
||||
hover: 'hover:bg-teal-100',
|
||||
'hover-dark': 'dark:hover:bg-teal-950/60',
|
||||
text: 'text-teal-950',
|
||||
'text-dark': 'dark:text-teal-100',
|
||||
},
|
||||
blue: {
|
||||
bg: 'bg-blue-50',
|
||||
'bg-dark': 'dark:bg-blue-950/40',
|
||||
border: 'border-blue-100',
|
||||
'border-dark': 'dark:border-blue-900/50',
|
||||
hover: 'hover:bg-blue-100',
|
||||
'hover-dark': 'dark:hover:bg-blue-950/60',
|
||||
text: 'text-blue-950',
|
||||
'text-dark': 'dark:text-blue-100',
|
||||
},
|
||||
indigo: {
|
||||
bg: 'bg-indigo-50',
|
||||
'bg-dark': 'dark:bg-indigo-950/40',
|
||||
border: 'border-indigo-100',
|
||||
'border-dark': 'dark:border-indigo-900/50',
|
||||
hover: 'hover:bg-indigo-100',
|
||||
'hover-dark': 'dark:hover:bg-indigo-950/60',
|
||||
text: 'text-indigo-950',
|
||||
'text-dark': 'dark:text-indigo-100',
|
||||
},
|
||||
violet: {
|
||||
bg: 'bg-violet-50',
|
||||
'bg-dark': 'dark:bg-violet-950/40',
|
||||
border: 'border-violet-100',
|
||||
'border-dark': 'dark:border-violet-900/50',
|
||||
hover: 'hover:bg-violet-100',
|
||||
'hover-dark': 'dark:hover:bg-violet-950/60',
|
||||
text: 'text-violet-950',
|
||||
'text-dark': 'dark:text-violet-100',
|
||||
},
|
||||
purple: {
|
||||
bg: 'bg-purple-50',
|
||||
'bg-dark': 'dark:bg-purple-950/40',
|
||||
border: 'border-purple-100',
|
||||
'border-dark': 'dark:border-purple-900/50',
|
||||
hover: 'hover:bg-purple-100',
|
||||
'hover-dark': 'dark:hover:bg-purple-950/60',
|
||||
text: 'text-purple-950',
|
||||
'text-dark': 'dark:text-purple-100',
|
||||
},
|
||||
pink: {
|
||||
bg: 'bg-pink-50',
|
||||
'bg-dark': 'dark:bg-pink-950/40',
|
||||
border: 'border-pink-100',
|
||||
'border-dark': 'dark:border-pink-900/50',
|
||||
hover: 'hover:bg-pink-100',
|
||||
'hover-dark': 'dark:hover:bg-pink-950/60',
|
||||
text: 'text-pink-950',
|
||||
'text-dark': 'dark:text-pink-100',
|
||||
},
|
||||
rose: {
|
||||
bg: 'bg-rose-50',
|
||||
'bg-dark': 'dark:bg-rose-950/40',
|
||||
border: 'border-rose-100',
|
||||
'border-dark': 'dark:border-rose-900/50',
|
||||
hover: 'hover:bg-rose-100',
|
||||
'hover-dark': 'dark:hover:bg-rose-950/60',
|
||||
text: 'text-rose-950',
|
||||
'text-dark': 'dark:text-rose-100',
|
||||
},
|
||||
gray: {
|
||||
bg: 'bg-neutral-100',
|
||||
'bg-dark': 'dark:bg-neutral-800',
|
||||
border: 'border-neutral-200',
|
||||
'border-dark': 'dark:border-neutral-700',
|
||||
hover: 'hover:bg-neutral-200',
|
||||
'hover-dark': 'dark:hover:bg-neutral-700',
|
||||
text: 'text-neutral-900',
|
||||
'text-dark': 'dark:text-neutral-100',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// 3. RÈGLES DE DESIGN
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Principes de design couleur :
|
||||
*
|
||||
* 1. HARMONIE : Toutes les couleurs partagent la même teinte de base (bleu 250°)
|
||||
* - Crée une cohérence visuelle
|
||||
* - Réduit la fatigue visuelle
|
||||
* - Améliore la perception de marque
|
||||
*
|
||||
* 2. CONTRASTE : Respecte WCAG AA (4.5:1 minimum)
|
||||
* - Texte sur fond : toujours ≥ 4.5:1
|
||||
* - Éléments interactifs : ≥ 3:1
|
||||
* - Utilise OKLCH pour une mesure plus précise
|
||||
*
|
||||
* 3. ACCESSIBILITÉ :
|
||||
* - Ne pas utiliser la couleur seule pour véhiculer l'information
|
||||
* - Inclure des icônes ou des symboles
|
||||
* - Supporter le mode de contraste élevé
|
||||
*
|
||||
* 4. ADAPTABILITÉ :
|
||||
* - Mode light/dark automatique
|
||||
* - Variations de couleur par note
|
||||
* - Thèmes alternatifs (midnight, blue, sepia)
|
||||
*
|
||||
* 5. PERFORMANCE PERCEPTIVE :
|
||||
* - OKLCH pour une échelle perceptuelle uniforme
|
||||
* - Même légèreté perçue entre light et dark
|
||||
* - Transition fluide entre thèmes
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 4. EXEMPLE D'IMPLEMENTATION
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Comment utiliser ces couleurs dans les composants :
|
||||
*
|
||||
* ```tsx
|
||||
* import { RECOMMENDED_NOTE_COLORS } from '@/lib/color-harmony-recommendation'
|
||||
*
|
||||
* // Pour une carte de note
|
||||
* <div className={`
|
||||
* ${noteColors.bg}
|
||||
* dark:${noteColors['bg-dark']}
|
||||
* ${noteColors.border}
|
||||
* dark:${noteColors['border-dark']}
|
||||
* ${noteColors.text}
|
||||
* dark:${noteColors['text-dark']}
|
||||
* hover:${noteColors.hover}
|
||||
* dark:hover:${noteColors['hover-dark']}
|
||||
* `}>
|
||||
* {note.content}
|
||||
* </div>
|
||||
*
|
||||
* // Pour le thème global (globals.css)
|
||||
* :root {
|
||||
* --background: oklch(0.99 0.002 250);
|
||||
* --foreground: oklch(0.18 0.01 250);
|
||||
* --primary: oklch(0.55 0.2 250);
|
||||
* // ... autres couleurs
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 5. AVANTAGES DE CETTE APPROCHE
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* ✅ Cohérence visuelle accrue
|
||||
* ✅ Meilleure accessibilité (WCAG AA+)
|
||||
* ✅ Adaptation native aux modes light/dark
|
||||
* ✅ Palette extensible (facile à ajouter de nouvelles couleurs)
|
||||
* ✅ Performance OKLCH (perception humaine)
|
||||
* ✅ Compatible avec Tailwind CSS
|
||||
* ✅ Maintenance simplifiée
|
||||
* ✅ Professionalisme et modernité
|
||||
*/
|
||||
|
||||
export type RecommendedNoteColor = keyof typeof RECOMMENDED_NOTE_COLORS;
|
||||
@@ -1,57 +0,0 @@
|
||||
import { LabelColorName } from './types'
|
||||
|
||||
const STORAGE_KEY = 'memento-label-colors'
|
||||
|
||||
// Store label colors in localStorage
|
||||
export function getLabelColor(label: string): LabelColorName {
|
||||
if (typeof window === 'undefined') return 'gray'
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
if (!stored) return 'gray'
|
||||
|
||||
const colors = JSON.parse(stored) as Record<string, LabelColorName>
|
||||
return colors[label] || 'gray'
|
||||
} catch {
|
||||
return 'gray'
|
||||
}
|
||||
}
|
||||
|
||||
export function setLabelColor(label: string, color: LabelColorName) {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
const colors = stored ? JSON.parse(stored) as Record<string, LabelColorName> : {}
|
||||
colors[label] = color
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(colors))
|
||||
} catch (error) {
|
||||
console.error('Failed to save label color:', error)
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllLabelColors(): Record<string, LabelColorName> {
|
||||
if (typeof window === 'undefined') return {}
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
return stored ? JSON.parse(stored) : {}
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteLabelColor(label: string) {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
if (!stored) return
|
||||
|
||||
const colors = JSON.parse(stored) as Record<string, LabelColorName>
|
||||
delete colors[label]
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(colors))
|
||||
} catch (error) {
|
||||
console.error('Failed to delete label color:', error)
|
||||
}
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/**
|
||||
* OPTIONS DE COULEURS MODERNES - PAS DE DÉGRADÉS
|
||||
* =================================================
|
||||
*
|
||||
* Alternatives au bleu traditionnel pour un design contemporain
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// OPTION 1: GRIS-BLEU (SLATE) - RECOMMANDÉE ✅
|
||||
// =============================================================================
|
||||
/**
|
||||
* Gris-bleu moderne et professionnel
|
||||
* Inspiré par Linear, Vercel, GitHub
|
||||
* Très élégant, discret et apaisant pour les yeux
|
||||
*/
|
||||
export const SLATE_THEME = {
|
||||
name: 'Gris-Bleu (Slate)',
|
||||
description: 'Moderne, professionnel et apaisant - comme Linear/Vercel',
|
||||
|
||||
light: {
|
||||
// Backgrounds
|
||||
background: 'oklch(0.985 0.003 230)', // Blanc grisâtre très léger
|
||||
card: 'oklch(1 0 0)', // Blanc pur
|
||||
sidebar: 'oklch(0.97 0.004 230)', // Gris-bleu très pâle
|
||||
input: 'oklch(0.98 0.003 230)', // Gris-bleu pâle
|
||||
|
||||
// Textes
|
||||
foreground: 'oklch(0.2 0.02 230)', // Gris-bleu foncé
|
||||
'foreground-secondary': 'oklch(0.45 0.015 230)', // Gris-bleu moyen
|
||||
'foreground-muted': 'oklch(0.6 0.01 230)', // Gris-bleu clair
|
||||
|
||||
// Primary (le point coloré de l'interface)
|
||||
primary: 'oklch(0.45 0.08 230)', // Gris-bleu doux
|
||||
'primary-hover': 'oklch(0.4 0.09 230)', // Gris-bleu plus foncé
|
||||
'primary-foreground': 'oklch(0.99 0 0)', // Blanc
|
||||
|
||||
// Accents
|
||||
accent: 'oklch(0.94 0.005 230)', // Gris-bleu très pâle
|
||||
'accent-foreground': 'oklch(0.2 0.02 230)',
|
||||
|
||||
// Borders
|
||||
border: 'oklch(0.9 0.008 230)', // Gris-bleu très clair
|
||||
'border-hover': 'oklch(0.82 0.01 230)',
|
||||
|
||||
// Functional
|
||||
success: 'oklch(0.65 0.15 145)', // Vert emerald
|
||||
warning: 'oklch(0.75 0.12 70)', // Jaune/orange
|
||||
destructive: 'oklch(0.6 0.18 25)', // Rouge
|
||||
},
|
||||
|
||||
dark: {
|
||||
// Backgrounds
|
||||
background: 'oklch(0.14 0.005 230)', // Noir grisâtre léger
|
||||
card: 'oklch(0.18 0.006 230)', // Gris-bleu foncé
|
||||
sidebar: 'oklch(0.12 0.005 230)', // Noir grisâtre
|
||||
input: 'oklch(0.2 0.006 230)',
|
||||
|
||||
// Textes
|
||||
foreground: 'oklch(0.97 0.003 230)', // Blanc grisâtre
|
||||
'foreground-secondary': 'oklch(0.75 0.008 230)',
|
||||
'foreground-muted': 'oklch(0.55 0.01 230)',
|
||||
|
||||
// Primary
|
||||
primary: 'oklch(0.55 0.08 230)', // Gris-bleu plus clair
|
||||
'primary-hover': 'oklch(0.6 0.09 230)',
|
||||
'primary-foreground': 'oklch(0.1 0 0)',
|
||||
|
||||
// Accents
|
||||
accent: 'oklch(0.24 0.006 230)',
|
||||
'accent-foreground': 'oklch(0.97 0.003 230)',
|
||||
|
||||
// Borders
|
||||
border: 'oklch(0.28 0.01 230)',
|
||||
'border-hover': 'oklch(0.38 0.012 230)',
|
||||
|
||||
// Functional
|
||||
success: 'oklch(0.7 0.15 145)',
|
||||
warning: 'oklch(0.8 0.12 70)',
|
||||
destructive: 'oklch(0.65 0.18 25)',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// OPTION 2: MONOCHROME GRIS (MINIMALISTE)
|
||||
// =============================================================================
|
||||
/**
|
||||
* Noir et blanc avec subtilités de gris
|
||||
* Style minimaliste ultra-moderne (Linear, Stripe, Apple)
|
||||
* Absolument pas de couleur sauf pour les fonctionnalités
|
||||
*/
|
||||
export const MONOCHROME_THEME = {
|
||||
name: 'Monochrome Gris',
|
||||
description: 'Minimaliste et élégant - style Linear/Apple',
|
||||
|
||||
light: {
|
||||
background: 'oklch(0.99 0 0)', // Blanc pur
|
||||
card: 'oklch(1 0 0)', // Blanc pur
|
||||
sidebar: 'oklch(0.96 0 0)', // Gris très pâle
|
||||
input: 'oklch(0.98 0 0)',
|
||||
|
||||
foreground: 'oklch(0.15 0 0)', // Noir pur
|
||||
'foreground-secondary': 'oklch(0.45 0 0)', // Gris moyen
|
||||
'foreground-muted': 'oklch(0.6 0 0)', // Gris clair
|
||||
|
||||
primary: 'oklch(0.2 0 0)', // Gris foncé
|
||||
'primary-hover': 'oklch(0.15 0 0)', // Noir
|
||||
'primary-foreground': 'oklch(1 0 0)', // Blanc
|
||||
|
||||
accent: 'oklch(0.95 0 0)', // Gris très pâle
|
||||
'accent-foreground': 'oklch(0.2 0 0)',
|
||||
|
||||
border: 'oklch(0.89 0 0)', // Gris très clair
|
||||
'border-hover': 'oklch(0.75 0 0)',
|
||||
|
||||
success: 'oklch(0.6 0.15 145)', // Vert subtil
|
||||
warning: 'oklch(0.7 0.12 70)', // Jaune subtil
|
||||
destructive: 'oklch(0.55 0.18 25)', // Rouge subtil
|
||||
},
|
||||
|
||||
dark: {
|
||||
background: 'oklch(0.1 0 0)', // Noir pur
|
||||
card: 'oklch(0.14 0 0)', // Gris très foncé
|
||||
sidebar: 'oklch(0.08 0 0)', // Noir pur
|
||||
input: 'oklch(0.16 0 0)',
|
||||
|
||||
foreground: 'oklch(0.98 0 0)', // Blanc pur
|
||||
'foreground-secondary': 'oklch(0.7 0 0)', // Gris moyen
|
||||
'foreground-muted': 'oklch(0.5 0 0)', // Gris foncé
|
||||
|
||||
primary: 'oklch(0.85 0 0)', // Gris clair
|
||||
'primary-hover': 'oklch(0.9 0 0)', // Blanc
|
||||
'primary-foreground': 'oklch(0.1 0 0)', // Noir
|
||||
|
||||
accent: 'oklch(0.2 0 0)', // Gris foncé
|
||||
'accent-foreground': 'oklch(0.98 0 0)',
|
||||
|
||||
border: 'oklch(0.25 0 0)', // Gris foncé
|
||||
'border-hover': 'oklch(0.35 0 0)',
|
||||
|
||||
success: 'oklch(0.65 0.15 145)',
|
||||
warning: 'oklch(0.75 0.12 70)',
|
||||
destructive: 'oklch(0.6 0.18 25)',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// OPTION 3: VIOLET PROFOND (INDIGO)
|
||||
// =============================================================================
|
||||
/**
|
||||
* Violet profond élégant et moderne
|
||||
* Entre le bleu et le violet
|
||||
* Très professionnel (Discord, Notion, Figma)
|
||||
*/
|
||||
export const INDIGO_THEME = {
|
||||
name: 'Violet Profond',
|
||||
description: 'Élégant et moderne - style Discord/Notion',
|
||||
|
||||
light: {
|
||||
background: 'oklch(0.99 0.005 260)', // Blanc légèrement violacé
|
||||
card: 'oklch(1 0 0)',
|
||||
sidebar: 'oklch(0.96 0.006 260)',
|
||||
input: 'oklch(0.97 0.005 260)',
|
||||
|
||||
foreground: 'oklch(0.2 0.015 260)', // Gris-violet foncé
|
||||
'foreground-secondary': 'oklch(0.45 0.012 260)',
|
||||
'foreground-muted': 'oklch(0.6 0.008 260)',
|
||||
|
||||
primary: 'oklch(0.55 0.18 260)', // Violet profond
|
||||
'primary-hover': 'oklch(0.5 0.2 260)', // Violet plus foncé
|
||||
'primary-foreground': 'oklch(0.99 0 0)',
|
||||
|
||||
accent: 'oklch(0.94 0.008 260)',
|
||||
'accent-foreground': 'oklch(0.2 0.015 260)',
|
||||
|
||||
border: 'oklch(0.9 0.01 260)',
|
||||
'border-hover': 'oklch(0.82 0.012 260)',
|
||||
|
||||
success: 'oklch(0.65 0.15 145)',
|
||||
warning: 'oklch(0.75 0.12 70)',
|
||||
destructive: 'oklch(0.6 0.18 25)',
|
||||
},
|
||||
|
||||
dark: {
|
||||
background: 'oklch(0.14 0.008 260)', // Noir légèrement violacé
|
||||
card: 'oklch(0.18 0.01 260)', // Gris-violet foncé
|
||||
sidebar: 'oklch(0.12 0.008 260)',
|
||||
input: 'oklch(0.2 0.01 260)',
|
||||
|
||||
foreground: 'oklch(0.97 0.005 260)', // Blanc légèrement violacé
|
||||
'foreground-secondary': 'oklch(0.75 0.008 260)',
|
||||
'foreground-muted': 'oklch(0.55 0.01 260)',
|
||||
|
||||
primary: 'oklch(0.65 0.18 260)', // Violet plus clair
|
||||
'primary-hover': 'oklch(0.7 0.2 260)',
|
||||
'primary-foreground': 'oklch(0.1 0 0)',
|
||||
|
||||
accent: 'oklch(0.24 0.01 260)',
|
||||
'accent-foreground': 'oklch(0.97 0.005 260)',
|
||||
|
||||
border: 'oklch(0.28 0.012 260)',
|
||||
'border-hover': 'oklch(0.38 0.015 260)',
|
||||
|
||||
success: 'oklch(0.7 0.15 145)',
|
||||
warning: 'oklch(0.8 0.12 70)',
|
||||
destructive: 'oklch(0.65 0.18 25)',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// OPTION 4: TEAL (TURQUOISE)
|
||||
// =============================================================================
|
||||
/**
|
||||
* Turquoise/teal moderne et rafraîchissante
|
||||
* Entre le bleu et le vert
|
||||
* Très appréciée dans le design moderne (Linear, Atlassian)
|
||||
*/
|
||||
export const TEAL_THEME = {
|
||||
name: 'Teal (Turquoise)',
|
||||
description: 'Moderne et rafraîchissant - style Atlassian/Linear',
|
||||
|
||||
light: {
|
||||
background: 'oklch(0.99 0.003 195)', // Blanc légèrement teinté
|
||||
card: 'oklch(1 0 0)',
|
||||
sidebar: 'oklch(0.96 0.004 195)',
|
||||
input: 'oklch(0.97 0.003 195)',
|
||||
|
||||
foreground: 'oklch(0.2 0.015 195)', // Gris-teal foncé
|
||||
'foreground-secondary': 'oklch(0.45 0.012 195)',
|
||||
'foreground-muted': 'oklch(0.6 0.008 195)',
|
||||
|
||||
primary: 'oklch(0.55 0.14 195)', // Teal moderne
|
||||
'primary-hover': 'oklch(0.5 0.16 195)', // Teal plus foncé
|
||||
'primary-foreground': 'oklch(0.99 0 0)',
|
||||
|
||||
accent: 'oklch(0.94 0.005 195)',
|
||||
'accent-foreground': 'oklch(0.2 0.015 195)',
|
||||
|
||||
border: 'oklch(0.9 0.008 195)',
|
||||
'border-hover': 'oklch(0.82 0.01 195)',
|
||||
|
||||
success: 'oklch(0.65 0.15 145)',
|
||||
warning: 'oklch(0.75 0.12 70)',
|
||||
destructive: 'oklch(0.6 0.18 25)',
|
||||
},
|
||||
|
||||
dark: {
|
||||
background: 'oklch(0.14 0.005 195)', // Noir légèrement teinté
|
||||
card: 'oklch(0.18 0.006 195)', // Gris-teal foncé
|
||||
sidebar: 'oklch(0.12 0.005 195)',
|
||||
input: 'oklch(0.2 0.006 195)',
|
||||
|
||||
foreground: 'oklch(0.97 0.003 195)', // Blanc légèrement teinté
|
||||
'foreground-secondary': 'oklch(0.75 0.006 195)',
|
||||
'foreground-muted': 'oklch(0.55 0.008 195)',
|
||||
|
||||
primary: 'oklch(0.65 0.14 195)', // Teal plus clair
|
||||
'primary-hover': 'oklch(0.7 0.16 195)',
|
||||
'primary-foreground': 'oklch(0.1 0 0)',
|
||||
|
||||
accent: 'oklch(0.24 0.006 195)',
|
||||
'accent-foreground': 'oklch(0.97 0.003 195)',
|
||||
|
||||
border: 'oklch(0.28 0.01 195)',
|
||||
'border-hover': 'oklch(0.38 0.012 195)',
|
||||
|
||||
success: 'oklch(0.7 0.15 145)',
|
||||
warning: 'oklch(0.8 0.12 70)',
|
||||
destructive: 'oklch(0.65 0.18 25)',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// RÉSUMÉ DES OPTIONS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Comparaison des options :
|
||||
*
|
||||
* | Option | Modernité | Professionnalisme | Fatigue oculaire | Unicité |
|
||||
* |--------|-----------|-------------------|------------------|----------|
|
||||
* | Slate (Gris-Bleu) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
* | Monochrome | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
* | Indigo | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
* | Teal | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
*
|
||||
* Recommandation : Slate (Gris-Bleu)
|
||||
* - Le plus professionnel
|
||||
* - Fatigue oculaire minimale
|
||||
* - Très moderne et tendance
|
||||
* - Différent du bleu traditionnel
|
||||
* - Cohérent avec votre suggestion
|
||||
*/
|
||||
|
||||
export type ThemeOption = 'slate' | 'monochrome' | 'indigo' | 'teal';
|
||||
|
||||
/**
|
||||
* Pour choisir le thème :
|
||||
*
|
||||
* function getTheme(option: ThemeOption) {
|
||||
* switch(option) {
|
||||
* case 'slate': return SLATE_THEME;
|
||||
* case 'monochrome': return MONOCHROME_THEME;
|
||||
* case 'indigo': return INDIGO_THEME;
|
||||
* case 'teal': return TEAL_THEME;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@@ -1,38 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { useQueryClient, useQuery } from '@tanstack/react-query'
|
||||
import { queryKeys } from './query-keys'
|
||||
import type { Note, Notebook, Label } from '@/lib/types'
|
||||
import type { Notebook, Label } from '@/lib/types'
|
||||
|
||||
// Re-export query keys
|
||||
export { queryKeys }
|
||||
|
||||
// ===== useNotes =====
|
||||
export function useNotes(notebookId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.notes(notebookId),
|
||||
queryFn: async (): Promise<Note[]> => {
|
||||
const url = notebookId ? `/api/notes?notebookId=${notebookId}` : '/api/notes'
|
||||
const res = await fetch(url, { cache: 'no-store', credentials: 'include' })
|
||||
const data = await res.json()
|
||||
return data.notes || []
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ===== useNote =====
|
||||
export function useNote(noteId: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.note(noteId),
|
||||
queryFn: async (): Promise<Note> => {
|
||||
const res = await fetch(`/api/notes/${noteId}`, { cache: 'no-store', credentials: 'include' })
|
||||
const data = await res.json()
|
||||
return data.note || data
|
||||
},
|
||||
enabled: !!noteId,
|
||||
})
|
||||
}
|
||||
|
||||
// ===== useNotebooks =====
|
||||
export function useNotebooksQuery() {
|
||||
return useQuery({
|
||||
@@ -64,10 +38,6 @@ export function invalidateNotes(queryClient: ReturnType<typeof useQueryClient>,
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.notes(notebookId) })
|
||||
}
|
||||
|
||||
export function invalidateNote(queryClient: ReturnType<typeof useQueryClient>, noteId: string) {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.note(noteId) })
|
||||
}
|
||||
|
||||
export function invalidateNotebooks(queryClient: ReturnType<typeof useQueryClient>) {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
|
||||
}
|
||||
|
||||
@@ -11,8 +11,4 @@ export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? 'sk_test_place
|
||||
typescript: true,
|
||||
});
|
||||
|
||||
export function getPublishableKey(): string {
|
||||
const key = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
|
||||
if (!key) throw new Error('NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY is not set');
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +50,6 @@ export interface LinkMetadata {
|
||||
|
||||
export type NoteType = 'text' | 'markdown' | 'richtext' | 'checklist'
|
||||
|
||||
export const NOTE_TYPE_CONFIG: Record<NoteType, { icon: string; label: string }> = {
|
||||
text: { icon: 'AlignLeft', label: 'Text' },
|
||||
markdown: { icon: 'FileCode2', label: 'Markdown' },
|
||||
richtext: { icon: 'PenLine', label: 'Rich Text' },
|
||||
checklist: { icon: 'ListChecks', label: 'Checklist' },
|
||||
}
|
||||
|
||||
export interface Note {
|
||||
id: string;
|
||||
title: string | null;
|
||||
|
||||
Reference in New Issue
Block a user