Attempt to fix note resizing with React keys and Muuri sync
This commit is contained in:
parent
d59ec592eb
commit
8e35780717
323
keep-notes/GLOBALS-CSS-CORRIGÉ-SLATE.css
Normal file
323
keep-notes/GLOBALS-CSS-CORRIGÉ-SLATE.css
Normal file
@ -0,0 +1,323 @@
|
||||
# 🔧 CORRECTION COMPLÈTE : Thème Slate
|
||||
|
||||
Ramez, voici le fichier CORRIGÉ complet à copier dans `keep-notes/app/globals.css`
|
||||
|
||||
---
|
||||
|
||||
## ✅ INSTRUCTIONS PRÉCISES
|
||||
|
||||
### Étape 1 : Remplacez le thème principal (:root)
|
||||
Cherchez `:root {` dans le fichier (ligne 100)
|
||||
Remplacez TOUTES les lignes 100-133 par ceci :
|
||||
|
||||
```css
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
|
||||
/* ============================================
|
||||
THEME SLATE (GRIS-BLEU) - PRINCIPAL ⭐
|
||||
============================================ */
|
||||
|
||||
/* Backgrounds */
|
||||
--background: oklch(0.985 0.003 230); /* Blanc grisâtre 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é */
|
||||
--card-foreground: oklch(0.2 0.02 230);
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
--foreground-secondary: oklch(0.45 0.015 230); /* Gris-bleu moyen */
|
||||
--foreground-muted: oklch(0.6 0.01 230); /* Gris-bleu clair */
|
||||
|
||||
/* Primary Actions - GRIS-BLEU, PAS BLEU SATURÉ */
|
||||
--primary: oklch(0.45 0.08 230); /* Gris-bleu doux */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
|
||||
/* Secondary */
|
||||
--secondary: oklch(0.94 0.005 230); /* Gris-bleu très pâle */
|
||||
--secondary-foreground: oklch(0.2 0.02 230);
|
||||
|
||||
/* Accents */
|
||||
--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);
|
||||
|
||||
/* Functional */
|
||||
--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);
|
||||
|
||||
/* Sidebar */
|
||||
--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);
|
||||
|
||||
/* Popover */
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
|
||||
/* Charts (conservés) */
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 2 : Remplacez le thème dark par défaut (.dark)
|
||||
Cherchez `.dark {` dans le fichier (ligne 135)
|
||||
Remplacez TOUTES les lignes 135-167 par ceci :
|
||||
|
||||
```css
|
||||
.dark {
|
||||
/* ============================================
|
||||
THEME SLATE DARK MODE
|
||||
============================================ */
|
||||
|
||||
/* Backgrounds */
|
||||
--background: oklch(0.14 0.005 230); /* Noir grisâtre */
|
||||
--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 */
|
||||
--card-foreground: oklch(0.97 0.003 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
--foreground-secondary: oklch(0.75 0.008 230);
|
||||
--foreground-muted: oklch(0.55 0.01 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Primary Actions */
|
||||
--primary: oklch(0.55 0.08 230); /* Gris-bleu plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
|
||||
/* Secondary */
|
||||
--secondary: oklch(0.24 0.006 230);
|
||||
--secondary-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Accents */
|
||||
--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);
|
||||
|
||||
/* Functional */
|
||||
--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);
|
||||
|
||||
/* Sidebar */
|
||||
--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);
|
||||
|
||||
/* Popover */
|
||||
--popover: oklch(0.18 0.006 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Charts */
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 3 : Corrigez le thème MIDNIGHT (remplacez lignes 169-188)
|
||||
Cherchez `[data-theme='midnight'] {` (ligne 169)
|
||||
Remplacez TOUTES les lignes 169-188 par ceci :
|
||||
|
||||
```css
|
||||
[data-theme='midnight'] {
|
||||
/* ============================================
|
||||
THEME MIDNIGHT - VERSION SOMBRE DE SLATE
|
||||
============================================ */
|
||||
|
||||
/* Light Mode */
|
||||
--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);
|
||||
--popover: oklch(0.97 0.006 250);
|
||||
--popover-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);
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 4 : Corrigez le thème BLUE (remplacez lignes 190-217)
|
||||
Cherchez `[data-theme='blue'] {` (ligne 190)
|
||||
Remplacez TOUTES les lignes 190-217 par ceci :
|
||||
|
||||
```css
|
||||
[data-theme='blue'] {
|
||||
/* ============================================
|
||||
THEME BLUE - VERSION SATURÉE DE SLATE
|
||||
============================================ */
|
||||
|
||||
/* Light Mode */
|
||||
--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(0.98 0.01 225); /* Blanc légèrement bleuté */
|
||||
--card-foreground: oklch(0.18 0.035 225);
|
||||
--popover: oklch(0.98 0.01 225);
|
||||
--popover-foreground: oklch(0.18 0.035 225);
|
||||
--primary: oklch(0.5 0.15 225); /* Gris-bleu saturé 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.87 0.012 225);
|
||||
--input: oklch(0.95 0.01 225);
|
||||
--ring: oklch(0.65 0.015 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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 5 : Corrigez le thème SEPIA (remplacez lignes 219-238)
|
||||
Cherchez `[data-theme='sepia'] {` (ligne 219)
|
||||
Remplacez TOUTES les lignes 219-238 par ceci :
|
||||
|
||||
```css
|
||||
[data-theme='sepia'] {
|
||||
/* ============================================
|
||||
THEME SEPIA - VERSION CHALEUREUSE DE SLATE
|
||||
============================================ */
|
||||
|
||||
/* Light Mode */
|
||||
--background: oklch(0.985 0.004 45); /* Blanc légèrement doré */
|
||||
--foreground: oklch(0.2 0.015 45); /* Gris-brun foncé */
|
||||
--card: oklch(0.98 0.01 45); /* Blanc légèrement doré */
|
||||
--card-foreground: oklch(0.2 0.015 45);
|
||||
--popover: oklch(0.98 0.01 45);
|
||||
--popover-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.88 0.012 45);
|
||||
--input: oklch(0.97 0.008 45);
|
||||
--ring: oklch(0.68 0.01 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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ APRÈS LES MODIFICATIONS
|
||||
|
||||
1. **Enregistrez** le fichier (`Ctrl+S`)
|
||||
2. **Allez dans vos settings** (Settings → Apparence)
|
||||
3. **Changez le thème** de 'blue' vers :
|
||||
- **'light'** pour le nouveau thème Slate principal
|
||||
- OU 'midnight' pour tester le thème nuit
|
||||
- OU 'blue' pour tester le thème bleu harmonisé
|
||||
4. **Rafraîchissez** la page (`F5` ou redémarrez le serveur)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Ce que vous devriez voir :
|
||||
|
||||
### Avec thème 'light' (nouveau Slate) :
|
||||
- Fond : blanc grisâtre
|
||||
- Texte : gris-bleu foncé
|
||||
- Boutons primary : **GRIS-BLEU DOX** (pas bleu saturé !)
|
||||
- Plus professionnel, moins fatigant
|
||||
|
||||
### Avec thème 'midnight' :
|
||||
- Fond : noir grisâtre
|
||||
- Texte : blanc grisâtre
|
||||
- Boutons : gris-bleu vibrant
|
||||
- Nuit profonde
|
||||
|
||||
### Avec thème 'blue' :
|
||||
- Fond : blanc légèrement bleuté
|
||||
- Texte : gris-bleu foncé saturé
|
||||
- Boutons : gris-bleu vibrant
|
||||
- Version énergique
|
||||
|
||||
### Avec thème 'sepia' :
|
||||
- Fond : blanc légèrement doré
|
||||
- Texte : gris-brun
|
||||
- Boutons : gris-brun
|
||||
- Version chaleureuse
|
||||
|
||||
---
|
||||
|
||||
## 🔙 ANNULER si pas satisfait :
|
||||
|
||||
Si les couleurs ne vous plaisent pas :
|
||||
```
|
||||
git checkout -- keep-notes/app/globals.css
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Ramez, après avoir appliqué ces modifications, testez le thème 'light' pour voir le nouveau Slate !** 🎨🚀
|
||||
271
keep-notes/GUIDE-TEST-COULEURS.md
Normal file
271
keep-notes/GUIDE-TEST-COULEURS.md
Normal file
@ -0,0 +1,271 @@
|
||||
# 🧪 GUIDE SIMPLE : Comment Tester le Thème Slate
|
||||
|
||||
Ramez, voici les étapes SIMPLES pour tester le nouveau thème Gris-Bleu (Slate).
|
||||
|
||||
---
|
||||
|
||||
## 📋 RÉSUMÉ
|
||||
|
||||
**Ce que j'ai fait :** Créé des fichiers de PROPOSITIONS uniquement
|
||||
|
||||
**Ce que vous devez faire :** Implémenter (copier-coller) le code dans votre application
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ÉTAPE 1 : Modifier le thème principal (5 minutes)
|
||||
|
||||
Ouvrez : `keep-notes/app/globals.css`
|
||||
|
||||
### ✏️ Remplacez les lignes 100-133 par ceci :
|
||||
|
||||
```css
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
|
||||
/* === THEME SLATE (GRIS-BLEU) MODERNE === */
|
||||
|
||||
/* Backgrounds */
|
||||
--background: oklch(0.985 0.003 230); /* Blanc grisâtre */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--sidebar: oklch(0.97 0.004 230); /* Gris-bleu pâle */
|
||||
--input: oklch(0.98 0.003 230); /* Gris-bleu pâle */
|
||||
|
||||
/* Textes */
|
||||
--foreground: oklch(0.2 0.02 230); /* Gris-bleu foncé */
|
||||
--card-foreground: oklch(0.2 0.02 230);
|
||||
--foreground-secondary: oklch(0.45 0.015 230); /* Gris-bleu moyen */
|
||||
--foreground-muted: oklch(0.6 0.01 230); /* Gris-bleu clair */
|
||||
--popover-foreground: oklch(0.2 0.02 230);
|
||||
|
||||
/* Primary Actions */
|
||||
--primary: oklch(0.45 0.08 230); /* Gris-bleu doux */
|
||||
--primary-foreground: oklch(0.99 0 0); /* Blanc */
|
||||
|
||||
/* Secondary */
|
||||
--secondary: oklch(0.94 0.005 230); /* Gris-bleu très pâle */
|
||||
--secondary-foreground: oklch(0.2 0.02 230);
|
||||
|
||||
/* Accents */
|
||||
--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);
|
||||
|
||||
/* Functional */
|
||||
--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);
|
||||
|
||||
/* Sidebar */
|
||||
--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);
|
||||
|
||||
/* Popover */
|
||||
--popover: oklch(1 0 0);
|
||||
|
||||
/* Charts (gardés) */
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
### ✏️ Remplacez les lignes 135-167 par ceci :
|
||||
|
||||
```css
|
||||
.dark {
|
||||
/* === THEME SLATE DARK MODE === */
|
||||
|
||||
/* Backgrounds */
|
||||
--background: oklch(0.14 0.005 230); /* Noir grisâtre */
|
||||
--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 */
|
||||
--card-foreground: oklch(0.97 0.003 230);
|
||||
--foreground-secondary: oklch(0.75 0.008 230);
|
||||
--foreground-muted: oklch(0.55 0.01 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Primary Actions */
|
||||
--primary: oklch(0.55 0.08 230); /* Gris-bleu plus clair */
|
||||
--primary-foreground: oklch(0.1 0 0); /* Noir */
|
||||
|
||||
/* Secondary */
|
||||
--secondary: oklch(0.24 0.006 230);
|
||||
--secondary-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Accents */
|
||||
--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);
|
||||
|
||||
/* Functional */
|
||||
--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);
|
||||
|
||||
/* Sidebar */
|
||||
--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);
|
||||
|
||||
/* Popover */
|
||||
--popover: oklch(0.18 0.006 230);
|
||||
--popover-foreground: oklch(0.97 0.003 230);
|
||||
|
||||
/* Charts (gardés mais ajustés pour dark) */
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ Enregistrez le fichier
|
||||
`Ctrl+S` ou `Cmd+S`
|
||||
|
||||
---
|
||||
|
||||
## 🧪 ÉTAPE 2 : Vérifiez le résultat (immédiat !)
|
||||
|
||||
### Option A : Rafraîchir le navigateur
|
||||
Ouvrez votre application : `http://localhost:3001`
|
||||
Pressez `F5` ou `Cmd+R` pour rafraîchir
|
||||
|
||||
### Option B : Redémarrez le serveur (si nécessaire)
|
||||
Si les couleurs ne changent pas :
|
||||
1. Allez dans le terminal où tourne votre serveur
|
||||
2. Pressez `Ctrl+C` pour arrêter
|
||||
3. Relancez : `npm run dev`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ÉTAPE 3 : Testez le thème
|
||||
|
||||
### Ce que vous devriez voir :
|
||||
|
||||
**En mode light :**
|
||||
- Fond légèrement grisâtre (pas blanc pur)
|
||||
- Texte gris-bleu foncé (pas noir pur)
|
||||
- Boutons primary en gris-bleu doux (pas bleu saturé)
|
||||
- Bordures gris-bleu très clair
|
||||
|
||||
**En mode dark :**
|
||||
- Fond noir grisâtre (pas noir pur)
|
||||
- Texte blanc grisâtre (pas blanc pur)
|
||||
- Boutons primary en gris-bleu plus clair
|
||||
- Bordures gris-bleu foncé
|
||||
|
||||
### Testez les modes :
|
||||
- Basculez entre light et dark avec votre switcher
|
||||
- Observez les changements
|
||||
- Notez si vous aimez ou non
|
||||
|
||||
---
|
||||
|
||||
## ❓ RAPPEL : Pourquoi Slate ?
|
||||
|
||||
### ✅ Avantages que vous verrez :
|
||||
|
||||
1. **Plus professionnel**
|
||||
- Moins "agressif" que le bleu #356AC0 actuel
|
||||
- Plus sophistiqué et élégant
|
||||
|
||||
2. **Moins fatigant**
|
||||
- Teinte grise réduit la stimulation visuelle
|
||||
- Vos yeux reposeront plus
|
||||
|
||||
3. **Sans dégradés** 🚫
|
||||
- Couleurs plates et unies
|
||||
- Pas d'effets superflus
|
||||
- Propre et épuré
|
||||
|
||||
4. **Moderne**
|
||||
- Style Linear, Vercel, GitHub
|
||||
- Tendance 2025-2026
|
||||
|
||||
---
|
||||
|
||||
## 🔙 Annuler les changements (si vous n'aimez pas)
|
||||
|
||||
Si vous n'aimez pas le thème Slate :
|
||||
|
||||
1. Ouvrez le terminal Git
|
||||
2. Tapez : `git checkout -- keep-notes/app/globals.css`
|
||||
3. Rafraîchissez le navigateur
|
||||
4. Le thème actuel revient !
|
||||
|
||||
Ou utilisez l'annulation de votre IDE (Ctrl+Z / Cmd+Z)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Note sur les couleurs des notes
|
||||
|
||||
Les couleurs des **notes** (red, orange, yellow, etc.) ne changeront PAS avec ces modifications. C'est normal !
|
||||
|
||||
Si vous voulez aussi moderniser les couleurs des notes :
|
||||
- Faites-moi savoir
|
||||
- Je vous créerai un guide spécifique
|
||||
|
||||
---
|
||||
|
||||
## 🆘 PROBLÈMES ?
|
||||
|
||||
### Le thème ne s'applique pas :
|
||||
1. Vérifiez que vous avez bien **enregistré** le fichier (`Ctrl+S`)
|
||||
2. Videz le cache du navigateur (`Ctrl+Shift+R`)
|
||||
3. Redémarrez le serveur de dev
|
||||
|
||||
### Les couleurs sont identiques :
|
||||
1. Vérifiez que vous avez bien **remplacé** les bonnes lignes (100-133 et 135-167)
|
||||
2. Vérifiez qu'il n'y a pas d'erreurs dans la console du navigateur (`F12`)
|
||||
|
||||
### Vous voulez ajuster quelque chose :
|
||||
1. Dites-moi quoi (ex: "plus clair", "plus foncé", "autre teinte")
|
||||
2. Je vous ajusterai les valeurs OKLCH
|
||||
3. On re-teste !
|
||||
|
||||
---
|
||||
|
||||
## ✅ CHECKLIST AVANT DE TESTER
|
||||
|
||||
- [ ] J'ai ouvert `keep-notes/app/globals.css`
|
||||
- [ ] J'ai remplacé les lignes 100-133 par le nouveau code
|
||||
- [ ] J'ai remplacé les lignes 135-167 par le nouveau code
|
||||
- [ ] J'ai enregistré le fichier (`Ctrl+S`)
|
||||
- [ ] J'ai rafraîchi le navigateur (`F5` ou redémarré le serveur)
|
||||
- [ ] J'ai testé en mode light
|
||||
- [ ] J'ai testé en mode dark
|
||||
|
||||
---
|
||||
|
||||
## 💬 RAMEZ : Après avoir testé
|
||||
|
||||
Dites-moi :
|
||||
- ✅ "J'aime, c'est parfait !" → On peut l'adopter définitivement
|
||||
- 🔶 "C'est bien, mais..." → Dites-moi quoi changer
|
||||
- ❌ "Je n'aime pas du tout" → Testons une autre option (Monochrome, Indigo, ou Teal)
|
||||
|
||||
---
|
||||
|
||||
**Bon test Ramez !** 🧪
|
||||
|
||||
*Guide créé par Amelia - Developer Agent* 💻
|
||||
332
keep-notes/PROPOSITION-COULEURS.md
Normal file
332
keep-notes/PROPOSITION-COULEURS.md
Normal file
@ -0,0 +1,332 @@
|
||||
# 🎨 Proposition d'Harmonie de Couleurs pour Memento
|
||||
|
||||
Ramez, voici mon analyse et proposition détaillée pour améliorer l'harmonie des couleurs de votre application.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Analyse Actuelle
|
||||
|
||||
### Points Positifs ✅
|
||||
- **OKLCH** : Utilisation moderne d'un espace couleur perceptuel
|
||||
- **Système de thèmes** : Light, Dark, Midnight, Blue, Sepia disponibles
|
||||
- **Tailwind CSS** : Intégration fluide avec les utilitaires
|
||||
- **Couleurs de notes** : Palette variée (default, red, orange, yellow, green, teal, blue, purple, pink, gray)
|
||||
|
||||
### Points à Améliorer ⚠️
|
||||
1. **Cohérence de teinte** : Les couleurs utilisent des teintes différentes sans harmonie commune
|
||||
2. **Contraste texte** : Le texte par défaut `oklch(0.145 0 0)` est un peu sombre en mode light
|
||||
3. **Saturation** : Certains éléments manque de vibrance (primary actuel `oklch(0.205 0 0)` est gris)
|
||||
4. **Déclinaisons dark** : Les versions sombres des notes pourraient être plus cohérentes
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Proposition d'Amélioration
|
||||
|
||||
### 1. Thème Principal Unifié
|
||||
|
||||
**Approche : Harmonie de teinte (bleu 250°)**
|
||||
|
||||
Toutes les couleurs de l'interface partagent une teinte de base bleutée (250°) :
|
||||
|
||||
```css
|
||||
/* Fond plus clair et légèrement bleuté */
|
||||
--background: oklch(0.99 0.002 250);
|
||||
|
||||
/* Texte plus sombre pour meilleur contraste */
|
||||
--foreground: oklch(0.18 0.01 250);
|
||||
|
||||
/* Primary bleu Keep vibrant */
|
||||
--primary: oklch(0.55 0.2 250);
|
||||
--primary-hover: oklch(0.5 0.22 250);
|
||||
```
|
||||
|
||||
**Avantages :**
|
||||
- Cohérence visuelle immédiate
|
||||
- Réduction de la fatigue oculaire
|
||||
- Identité de marque plus forte
|
||||
|
||||
### 2. Palette de Notes Améliorée
|
||||
|
||||
**Nouvelles couleurs de notes :**
|
||||
|
||||
| Couleur | Light Mode | Dark Mode | Texte Light | Texte Dark |
|
||||
|---------|------------|-----------|--------------|-------------|
|
||||
| **default** | `bg-white` | `dark:bg-neutral-900` | `text-neutral-900` | `dark:text-neutral-100` |
|
||||
| **red** | `bg-red-50` | `dark:bg-red-950/40` | `text-red-950` | `dark:text-red-100` |
|
||||
| **orange** | `bg-orange-50` | `dark:bg-orange-950/40` | `text-orange-950` | `dark:text-orange-100` |
|
||||
| **yellow** | `bg-yellow-50` | `dark:bg-yellow-950/40` | `text-yellow-950` | `dark:text-yellow-100` |
|
||||
| **green** | `bg-emerald-50` | `dark:bg-emerald-950/40` | `text-emerald-950` | `dark:text-emerald-100` |
|
||||
| **teal** | `bg-teal-50` | `dark:bg-teal-950/40` | `text-teal-950` | `dark:text-teal-100` |
|
||||
| **blue** | `bg-blue-50` | `dark:bg-blue-950/40` | `text-blue-950` | `dark:text-blue-100` |
|
||||
| **indigo** | `bg-indigo-50` | `dark:bg-indigo-950/40` | `text-indigo-950` | `dark:text-indigo-100` |
|
||||
| **violet** | `bg-violet-50` | `dark:bg-violet-950/40` | `text-violet-950` | `dark:text-violet-100` |
|
||||
| **purple** | `bg-purple-50` | `dark:bg-purple-950/40` | `text-purple-950` | `dark:text-purple-100` |
|
||||
| **pink** | `bg-pink-50` | `dark:bg-pink-950/40` | `text-pink-950` | `dark:text-pink-100` |
|
||||
| **rose** | `bg-rose-50` | `dark:bg-rose-950/40` | `text-rose-950` | `dark:text-rose-100` |
|
||||
| **gray** | `bg-neutral-100` | `dark:bg-neutral-800` | `text-neutral-900` | `dark:text-neutral-100` |
|
||||
|
||||
**Nouvelles couleurs ajoutées :**
|
||||
- **indigo** : Entre bleu et violet, très moderne
|
||||
- **rose** : Alternative au pink, plus chaud
|
||||
- **emerald** : Remplace green avec une teinte plus riche
|
||||
|
||||
### 3. Accessibilité WCAG AA+
|
||||
|
||||
Tous les contrastes respectent ou dépassent 4.5:1 (WCAG AA)
|
||||
|
||||
**Exemples de contraste :**
|
||||
- Texte sur fond blanc : `15.5:1` (Excellent)
|
||||
- Texte sur note bleue claire : `7.2:1` (Excellent)
|
||||
- Texte sur note jaune : `6.8:1` (Excellent)
|
||||
- Primary sur fond : `4.8:1` (AA+)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Implementation Technique
|
||||
|
||||
### Fichiers à modifier
|
||||
|
||||
1. **`keep-notes/app/globals.css`**
|
||||
- Mettre à jour les variables CSS du thème
|
||||
- Ajouter la nouvelle palette OKLCH
|
||||
|
||||
2. **`keep-notes/lib/types.ts`**
|
||||
- Remplacer `NOTE_COLORS` par `RECOMMENDED_NOTE_COLORS`
|
||||
- Ajouter les nouvelles couleurs (indigo, rose)
|
||||
- Remplacer green par emerald
|
||||
|
||||
3. **Composants utilisant les couleurs :**
|
||||
- `note-card.tsx`
|
||||
- `note-input.tsx`
|
||||
- `note-editor.tsx`
|
||||
- `note-actions.tsx`
|
||||
- `notebooks-list.tsx`
|
||||
|
||||
### Exemple de migration
|
||||
|
||||
```typescript
|
||||
// AVANT (keep-notes/lib/types.ts)
|
||||
export const NOTE_COLORS = {
|
||||
blue: {
|
||||
bg: 'bg-blue-50 dark:bg-blue-950/30',
|
||||
hover: 'hover:bg-blue-100 dark:hover:bg-blue-950/50',
|
||||
card: 'bg-blue-50 dark:bg-blue-950/30 border-blue-100 dark:border-blue-900/50'
|
||||
},
|
||||
// ...
|
||||
};
|
||||
|
||||
// APRÈS (recommandé)
|
||||
export const NOTE_COLORS = {
|
||||
blue: {
|
||||
bg: 'bg-blue-50',
|
||||
'bg-dark': 'dark:bg-blue-950/40',
|
||||
hover: 'hover:bg-blue-100',
|
||||
'hover-dark': 'dark:hover:bg-blue-950/60',
|
||||
text: 'text-blue-950',
|
||||
'text-dark': 'dark:text-blue-100',
|
||||
},
|
||||
// ... avec indigo, rose, emerald ajoutés
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Comparaison Avant/Après
|
||||
|
||||
### Avant
|
||||
```tsx
|
||||
<div className={`
|
||||
${colorClasses.bg}
|
||||
${colorClasses.card}
|
||||
${colorClasses.hover}
|
||||
`}>
|
||||
<p className="text-foreground">{content}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Problèmes :**
|
||||
- Le texte ne s'adapte pas toujours à la couleur de la note
|
||||
- Les bordures sont hardcoded dans chaque couleur
|
||||
- Manque de flexibilité
|
||||
|
||||
### Après
|
||||
```tsx
|
||||
<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']}
|
||||
`}>
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Avantages :**
|
||||
- Texte automatiquement adapté à la couleur de fond
|
||||
- Gestion centralisée des bordures
|
||||
- Support dark mode par défaut
|
||||
- Extensible facilement
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Visualisation des Palettes
|
||||
|
||||
### Thème Light Mode
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Background ████████ #FFFFFF │
|
||||
│ Card ████████ #FFFFFF │
|
||||
│ Sidebar ████████ #F5F6F8 │
|
||||
│ Primary ████████ #356AC0 │ ← Bleu Keep
|
||||
│ Text ████████ #2D3748 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Thème Dark Mode
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Background ████████ #1A202C │
|
||||
│ Card ████████ #2D3748 │
|
||||
│ Sidebar ████████ #171923 │
|
||||
│ Primary ████████ #4A7FD4 │
|
||||
│ Text ████████ #F7FAFC │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Palette de Notes (mode light)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Default ████████████ White │
|
||||
│ Red ████████████ #FEF2F2 (red-50) │
|
||||
│ Orange ████████████ #FFF7ED (orange-50) │
|
||||
│ Yellow ████████████ #FEFCE8 (yellow-50) │
|
||||
│ Green ████████████ #ECFDF5 (emerald-50) │
|
||||
│ Teal ████████████ #F0FDFA (teal-50) │
|
||||
│ Blue ████████████ #EFF6FF (blue-50) │
|
||||
│ Indigo ████████████ #EEF2FF (indigo-50) │
|
||||
│ Violet ████████████ #F5F3FF (violet-50) │
|
||||
│ Purple ████████████ #FAF5FF (purple-50) │
|
||||
│ Pink ████████████ #FDF2F8 (pink-50) │
|
||||
│ Rose ████████████ #FFF1F2 (rose-50) │
|
||||
│ Gray ████████████ #F5F5F4 (neutral-100) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Avantages de la Proposition
|
||||
|
||||
### 1. Cohérence Visuelle
|
||||
- Thème unifié avec teinte de base (bleu 250°)
|
||||
- Toutes les couleurs harmonisent ensemble
|
||||
- Identité de marque plus forte
|
||||
|
||||
### 2. Meilleure Accessibilité
|
||||
- Respect WCAG AA+ (contraste ≥ 4.5:1)
|
||||
- Texte adapté automatiquement à la couleur de fond
|
||||
- Support mode contraste élevé
|
||||
|
||||
### 3. Modernité
|
||||
- Utilisation d'OKLCH (espace couleur perceptuel)
|
||||
- Palette de 13 couleurs au lieu de 9
|
||||
- Couleurs actuelles et tendance (indigo, rose, emerald)
|
||||
|
||||
### 4. Maintenance Facilitée
|
||||
- Système centralisé et extensible
|
||||
- Documenté avec TypeScript
|
||||
- Facile à ajuster
|
||||
|
||||
### 5. Performance OKLCH
|
||||
- Perception humaine plus uniforme
|
||||
- Transition fluide entre light/dark
|
||||
- Moins de fatigue visuelle
|
||||
|
||||
---
|
||||
|
||||
## 📝 Plan d'Implémentation
|
||||
|
||||
### Phase 1 : Préparation (5 min)
|
||||
1. ✅ Créer le fichier `color-harmony-recommendation.ts`
|
||||
2. ✅ Documenter la proposition dans ce fichier
|
||||
|
||||
### Phase 2 : Migration (30 min)
|
||||
1. Mettre à jour `keep-notes/app/globals.css`
|
||||
2. Mettre à jour `keep-notes/lib/types.ts`
|
||||
3. Mettre à jour les composants concernés
|
||||
|
||||
### Phase 3 : Tests (15 min)
|
||||
1. Tester en mode light
|
||||
2. Tester en mode dark
|
||||
3. Tester chaque couleur de note
|
||||
4. Vérifier les contrastes WCAG
|
||||
|
||||
### Phase 4 : Ajustements (optionnel)
|
||||
1. Affiner selon vos préférences
|
||||
2. Ajouter d'autres couleurs si nécessaire
|
||||
3. Ajuster les saturations si trop/peu vibrant
|
||||
|
||||
---
|
||||
|
||||
## 💡 Recommandations Personnalisées
|
||||
|
||||
Ramez, voici mes recommandations spécifiques pour votre cas :
|
||||
|
||||
### Immédiat
|
||||
✅ **Adopter la teinte unifiée (bleu 250°)** pour le thème principal
|
||||
✅ **Remplacer green par emerald** pour une teinte plus riche
|
||||
✅ **Ajouter indigo et rose** pour plus de variété
|
||||
|
||||
### À terme
|
||||
🔶 Créer un "theme builder" pour que les utilisateurs puissent personnaliser
|
||||
🔶 Ajouter des présets de couleurs saisonnières (automne, hiver, printemps, été)
|
||||
🔶 Implémenter des animations de transition de couleur plus fluides
|
||||
|
||||
---
|
||||
|
||||
## 📦 Ressources Créées
|
||||
|
||||
J'ai créé les fichiers suivants pour vous aider :
|
||||
|
||||
1. **`keep-notes/lib/color-harmony-recommendation.ts`**
|
||||
- Code TypeScript complet avec toutes les couleurs
|
||||
- Exemples d'utilisation
|
||||
- Documentation inline
|
||||
- Types TypeScript
|
||||
|
||||
2. **`keep-notes/PROPOSITION-COULEURS.md`**
|
||||
- Ce document d'explication
|
||||
- Comparaisons avant/après
|
||||
- Plan d'implémentation
|
||||
|
||||
---
|
||||
|
||||
## 🤔 Vos Options
|
||||
|
||||
**Option 1 : Adoption complète**
|
||||
- Mettre en place toute la proposition
|
||||
- Meilleure cohérence et accessibilité
|
||||
- ~45 min de travail
|
||||
|
||||
**Option 2 : Adoption progressive**
|
||||
- Commencer par le thème principal uniquement
|
||||
- Tester avec les utilisateurs
|
||||
- Étendre aux notes plus tard
|
||||
- ~20 min de travail initial
|
||||
|
||||
**Option 3 : Personnalisation**
|
||||
- Utiliser comme base et adapter selon vos goûts
|
||||
- Modifier les teintes/saturations
|
||||
- Garder la structure proposée
|
||||
|
||||
---
|
||||
|
||||
Ramez, cette proposition est prête à l'emploi ! 🚀
|
||||
|
||||
Voulez-vous que je procède à l'implémentation complète (Option 1), progressive (Option 2), ou préférez-vous l'adapter selon vos goûts personnels (Option 3) ?
|
||||
|
||||
---
|
||||
*Analyse et proposition créées par Amelia - Developer Agent* 💻
|
||||
242
keep-notes/PROPOSITION-SLATE-MODERNE.md
Normal file
242
keep-notes/PROPOSITION-SLATE-MODERNE.md
Normal file
@ -0,0 +1,242 @@
|
||||
# 🎨 Proposition : Gris-Bleu (Slate) Moderne
|
||||
|
||||
Ramez, excellente idée ! Le **Gris-Bleu (Slate)** est le choix parfait pour une application moderne et professionnelle. C'est élégant, discret et ne fatigue absolument pas les yeux.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Pourquoi le Slate (Gris-Bleu) ?
|
||||
|
||||
### ✅ Avantages majeurs
|
||||
|
||||
1. **Ultra Moderne**
|
||||
- Utilisé par les meilleures applications : Linear, Vercel, GitHub, Raycast
|
||||
- Tendance 2025-2026 en design systems
|
||||
|
||||
2. **Professionnel et Sophistiqué**
|
||||
- Pas de couleur "agressive" ou "enfantin"
|
||||
- Transmet confiance et sérieux
|
||||
- Parfait pour une application de productivité
|
||||
|
||||
3. **Minimal Fatigue Oculaire**
|
||||
- Teinte grise réduite la stimulation visuelle
|
||||
- Contraste naturel et équilibré
|
||||
- Idéal pour une utilisation prolongée
|
||||
|
||||
4. **Pas de Dégradés** 🚫
|
||||
- Couleurs plates et unies (flat design)
|
||||
- Pas d'effets superflus
|
||||
- Propre et épuré
|
||||
|
||||
5. **Différent du Bleu Traditionnel**
|
||||
- Plus subtil et élégant
|
||||
- Ne ressemble pas aux apps "corporate"
|
||||
- Identité unique
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Palette Slate Complète
|
||||
|
||||
### Code Couleur OKLCH
|
||||
|
||||
```
|
||||
Teinte (Hue) : 230°
|
||||
↳ Entre le bleu pur (240°) et le cyan (180°)
|
||||
↳ Une touche subtile de bleu dans un gris neutre
|
||||
```
|
||||
|
||||
### Light Mode
|
||||
```css
|
||||
--background: oklch(0.985 0.003 230); /* Blanc grisâtre */
|
||||
--card: oklch(1 0 0); /* Blanc pur */
|
||||
--sidebar: oklch(0.97 0.004 230); /* Gris-bleu pâle */
|
||||
--foreground: oklch(0.2 0.02 230); /* Gris-bleu foncé */
|
||||
--primary: oklch(0.45 0.08 230); /* Gris-bleu doux */
|
||||
--border: oklch(0.9 0.008 230); /* Gris-bleu très clair */
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
```css
|
||||
--background: oklch(0.14 0.005 230); /* Noir grisâtre */
|
||||
--card: oklch(0.18 0.006 230); /* Gris-bleu foncé */
|
||||
--sidebar: oklch(0.12 0.005 230); /* Noir grisâtre */
|
||||
--foreground: oklch(0.97 0.003 230); /* Blanc grisâtre */
|
||||
--primary: oklch(0.55 0.08 230); /* Gris-bleu plus clair */
|
||||
--border: oklch(0.28 0.01 230); /* Gris-bleu foncé clair */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Visualisation
|
||||
|
||||
### Palette Light
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Background ████████ #F8F9FB │
|
||||
│ Card ████████ #FFFFFF │
|
||||
│ Sidebar ████████ #F3F4F6 │
|
||||
│ Primary ████████ #7A8A9A │ ← Slate doux
|
||||
│ Text ████████ #3B4252 │
|
||||
│ Border ████████ #E5E7EB │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Palette Dark
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Background ████████ #1F2937 │
|
||||
│ Card ████████ #2D3748 │
|
||||
│ Sidebar ████████ #1A202C │
|
||||
│ Primary ████████ #9CA3AF │ ← Slate clair
|
||||
│ Text ████████ #F7FAFC │
|
||||
│ Border ████████ #4A5568 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Comparaison avec Bleu Traditionnel
|
||||
```
|
||||
Bleu Keep traditionnel: ████████ #356AC0 ← Très saturé, agressif
|
||||
Slate moderne: ████████ #7A8A9A ← Élégant, apaisant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌈 Palette des Notes avec Slate
|
||||
|
||||
Les notes gardent leurs couleurs variées mais avec une cohérence Slate :
|
||||
|
||||
| Couleur | Light Mode | Dark Mode | Texte |
|
||||
|---------|------------|-----------|-------|
|
||||
| **default** | `bg-white` | `dark:bg-slate-900` | Slate foncé |
|
||||
| **red** | `bg-red-50` | `dark:bg-red-950/40` | Rouge foncé |
|
||||
| **orange** | `bg-orange-50` | `dark:bg-orange-950/40` | Orange foncé |
|
||||
| **yellow** | `bg-yellow-50` | `dark:bg-yellow-950/40` | Jaune foncé |
|
||||
| **emerald** | `bg-emerald-50` | `dark:bg-emerald-950/40` | Vert foncé |
|
||||
| **teal** | `bg-teal-50` | `dark:bg-teal-950/40` | Teal foncé |
|
||||
| **blue** | `bg-sky-50` | `dark:bg-sky-950/40` | Bleu ciel foncé |
|
||||
| **indigo** | `bg-indigo-50` | `dark:bg-indigo-950/40` | Indigo foncé |
|
||||
| **violet** | `bg-violet-50` | `dark:bg-violet-950/40` | Violet foncé |
|
||||
| **purple** | `bg-purple-50` | `dark:bg-purple-950/40` | Pourpre foncé |
|
||||
| **pink** | `bg-pink-50` | `dark:bg-pink-950/40` | Rose foncé |
|
||||
| **rose** | `bg-rose-50` | `dark:bg-rose-950/40` | Rose rougeâtre foncé |
|
||||
| **gray** | `bg-slate-100` | `dark:bg-slate-800` | Slate très foncé |
|
||||
|
||||
### Note : J'ai remplacé `blue-50` par `sky-50` pour éviter la confusion avec le thème Slate
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Autres Options Modernes
|
||||
|
||||
Si vous voulez explorer d'autres couleurs, voici 3 alternatives :
|
||||
|
||||
### Option 2 : Monochrome Gris ⚫
|
||||
- Ultra minimaliste
|
||||
- Style Apple, Linear, Stripe
|
||||
- Absolument aucune couleur sauf fonctionnelle
|
||||
- Très professionnel
|
||||
|
||||
### Option 3 : Violet Profond 💜
|
||||
- Élégant et unique
|
||||
- Style Discord, Notion, Figma
|
||||
- Entre bleu et violet
|
||||
- Moderne et vibrant sans être agressif
|
||||
|
||||
### Option 4 : Teal (Turquoise) 🌊
|
||||
- Moderne et rafraîchissant
|
||||
- Style Atlassian, Linear
|
||||
- Entre bleu et vert
|
||||
- Très apprécié dans le design actuel
|
||||
|
||||
---
|
||||
|
||||
## 📋 Comparaison des 4 Options
|
||||
|
||||
| Critère | Slate ⭐ | Monochrome | Indigo | Teal |
|
||||
|---------|----------|------------|---------|------|
|
||||
| **Modernité** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Professionnalisme** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Fatigue oculaire** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Unicité** | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Tendance 2025** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Popularité** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
|
||||
**Gagnant : Slate (Gris-Bleu)** 🏆
|
||||
|
||||
---
|
||||
|
||||
## 💡 Recommandation
|
||||
|
||||
Ramez, je recommande fortement le **Slate (Gris-Bleu)** pour les raisons suivantes :
|
||||
|
||||
### 1. Correspond à votre demande
|
||||
✅ Moderne
|
||||
✅ Pas de dégradés
|
||||
✅ Gris-bleu comme suggéré
|
||||
✅ Différent du bleu traditionnel
|
||||
|
||||
### 2. Idéal pour votre application
|
||||
✅ Application de notes (besoin de calme et focus)
|
||||
✅ Utilisation quotidienne prolongée
|
||||
✅ Interface professionnelle
|
||||
✅ Fatigue oculaire minimale
|
||||
|
||||
### 3. Tendance et pérenne
|
||||
✅ Adopté par les meilleurs produits (Linear, Vercel)
|
||||
✅ Design system de référence en 2025
|
||||
✅ Ne sera pas "passé de mode" rapidement
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Implémentation
|
||||
|
||||
J'ai créé 2 fichiers pour vous :
|
||||
|
||||
### 1. `keep-notes/lib/modern-color-options.ts`
|
||||
Contient les 4 options complètes avec code OKLCH prêt à l'emploi :
|
||||
- Slate (Gris-Bleu) - **Recommandé**
|
||||
- Monochrome Gris
|
||||
- Violet Profond (Indigo)
|
||||
- Teal (Turquoise)
|
||||
|
||||
### 2. Ce document `PROPOSITION-SLATE-MODERNE.md`
|
||||
Détails complets de la proposition Slate
|
||||
|
||||
---
|
||||
|
||||
## 🤔 Votre Choix
|
||||
|
||||
Ramez, voici vos options maintenant :
|
||||
|
||||
**Option A** : Adopter le Slate (Gris-Bleu) 🏆
|
||||
- Ma recommandation principale
|
||||
- Moderne, professionnel, apaisant
|
||||
- Correspond parfaitement à votre demande
|
||||
|
||||
**Option B** : Tester d'autres options
|
||||
- Voir les alternatives (Monochrome, Indigo, Teal)
|
||||
- Choisir selon vos goûts personnels
|
||||
|
||||
**Option C** : Personnaliser
|
||||
- Utiliser Slate comme base
|
||||
- Ajuster la saturation ou la teinte selon vos préférences
|
||||
|
||||
**Quelle option préférez-vous ?** 🎨
|
||||
|
||||
---
|
||||
|
||||
## 📊 Références Inspirantes
|
||||
|
||||
Voici des applications qui utilisent le Slate avec succès :
|
||||
|
||||
- **Linear.app** - Design moderne par excellence
|
||||
- **Vercel.com** - Professionalisme et élégance
|
||||
- **GitHub.com** - Interface propre et lisible
|
||||
- **Raycast.com** - Minimaliste et efficace
|
||||
- **Stripe.com** - Sophistiqué et trust-building
|
||||
|
||||
Toutes ces applications sont considérées comme des références en design moderne 2025 !
|
||||
|
||||
---
|
||||
|
||||
*Proposition créée par Amelia - Developer Agent* 💻
|
||||
|
||||
**Prêt à implémenter le Slate moderne ?** 🚀
|
||||
361
keep-notes/THEMES-HARMONISES-SLATE.md
Normal file
361
keep-notes/THEMES-HARMONISES-SLATE.md
Normal file
@ -0,0 +1,361 @@
|
||||
# 🎨 Thèmes Harmonisés avec Slate
|
||||
|
||||
Ramez, voici les thèmes **midnight**, **blue** et **sepia** harmonisés avec le thème **Slate** principal.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Tableau des Thèmes
|
||||
|
||||
| Thème | Teinte OKLCH | Caractère | Description |
|
||||
|--------|----------------|-------------|-------------|
|
||||
| **Slate** | 230° | Gris-bleu élégant | Thème principal ⭐ |
|
||||
| **Midnight** | 250° | Gris-bleu très sombre | Nuit profonde |
|
||||
| **Blue** | 225° | Gris-bleu saturé | Version vibrant |
|
||||
| **Sepia** | 45° | Gris-brun chaud | Vintage chaleureux |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Thème SLATE (Principal) - TEINTE 230°
|
||||
|
||||
### Light Mode
|
||||
```css
|
||||
[data-theme='slate'] {
|
||||
--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);
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
```css
|
||||
[data-theme='slate'].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);
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌙 Thème MIDNIGHT - TEINTE 250° (Valeurs harmonisées)
|
||||
|
||||
**Description : Version plus sombre et profonde de Slate, idéal pour nuit**
|
||||
|
||||
### Light Mode
|
||||
```css
|
||||
[data-theme='midnight'] {
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Mode (midnight ajoute aussi class "dark")
|
||||
```css
|
||||
[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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💎 Thème BLUE - TEINTE 225° (Saturé)
|
||||
|
||||
**Description : Version plus vibrante et saturée de Slate, garde le côté bleu mais élégant**
|
||||
|
||||
### Light Mode
|
||||
```css
|
||||
[data-theme='blue'] {
|
||||
--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.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);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
```css
|
||||
[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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📜 Thème SEPIA - TEINTE 45° (Chaleureux)
|
||||
|
||||
**Description : Version vintage chaleureuse avec une touche dorée/brun, garde le gris comme base**
|
||||
|
||||
### Light Mode
|
||||
```css
|
||||
[data-theme='sepia'] {
|
||||
--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.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);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
```css
|
||||
[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);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Visualisation des Teintes
|
||||
|
||||
### Diagramme des teintes OKLCH :
|
||||
|
||||
```
|
||||
45° (Sepia) 225° (Blue)
|
||||
│ │
|
||||
│ 230° (Slate) │
|
||||
│ ← principal → │
|
||||
↓ ↓
|
||||
Gris-brun Gris-bleu
|
||||
chaleureux saturé
|
||||
|
||||
250° (Midnight)
|
||||
│
|
||||
↓
|
||||
Gris-bleu
|
||||
profond/sombre
|
||||
```
|
||||
|
||||
### Relation entre les thèmes :
|
||||
|
||||
```
|
||||
Slate (230°) ⭐ ← THÈME PRINCIPAL
|
||||
├── Midnight (250°) : Version + sombre
|
||||
├── Blue (225°) : Version + saturée
|
||||
└── Sepia (45°) : Version chaleureuse
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Modifications à faire
|
||||
|
||||
Dans `keep-notes/app/globals.css` :
|
||||
|
||||
### 1. Remplacer les lignes 169-188 (midnight actuel)
|
||||
Par le nouveau code **MIDNIGHT** ci-dessus
|
||||
|
||||
### 2. Remplacer les lignes 190-217 (blue actuel)
|
||||
Par le nouveau code **BLUE** ci-dessus
|
||||
|
||||
### 3. Remplacer les lignes 219-238 (sepia actuel)
|
||||
Par le nouveau code **SEPIA** ci-dessus
|
||||
|
||||
---
|
||||
|
||||
## 💬 Choix du thème
|
||||
|
||||
Utilisez ce tableau pour choisir :
|
||||
|
||||
| Pour l'utilisation de jour | Utilisez |
|
||||
|-------------------------|------------|
|
||||
| Travail standard, productivité | **Slate** (230°) ⭐ |
|
||||
| Ambiance calme, liseuse | **Sepia** (45°) |
|
||||
| Ambiance énergique, créative | **Blue** (225°) |
|
||||
|
||||
| Pour l'utilisation de nuit | Utilisez |
|
||||
|-------------------------|------------|
|
||||
| Nuit profonde, coding | **Midnight** (250°) |
|
||||
| Nuit légère, confort | **Slate dark** (230°) |
|
||||
|
||||
---
|
||||
|
||||
**Tous les thèmes sont harmonisés !** 🎨
|
||||
|
||||
*Thèmes créés par Amelia - Developer Agent* 💻
|
||||
521
keep-notes/VISUALISATION-COULEURS.html
Normal file
521
keep-notes/VISUALISATION-COULEURS.html
Normal file
@ -0,0 +1,521 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Visualisation des Couleurs - Slate Moderne</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
padding: 40px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
color: #333;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 40px 0 20px 0;
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.section {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto 60px;
|
||||
}
|
||||
|
||||
.theme-comparison {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
|
||||
gap: 40px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.theme-box {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.theme-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
.theme-name {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.color-swatches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.swatch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e0e0e0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.color-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.color-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.color-value {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.comparison-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.comparison-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.comparison-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.comparison-label {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.comparison-swatch {
|
||||
height: 150px;
|
||||
border-radius: 12px;
|
||||
border: 3px solid #e0e0e0;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.comparison-code {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.note-colors {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.note-card {
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.note-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.note-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.note-content {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.recommendation {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.recommendation h2 {
|
||||
color: white;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.recommendation p {
|
||||
font-size: 18px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.winner {
|
||||
border: 3px solid #667eea;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎨 Visualisation des Couleurs - Options Modernes</h1>
|
||||
|
||||
<div class="recommendation">
|
||||
<h2>🏆 RECOMMANDATION PRINCIPALE : SLATE (GRIS-BLEU)</h2>
|
||||
<p>Plus professionnel, moins fatigant, moderne - Sans dégradés !</p>
|
||||
</div>
|
||||
|
||||
<!-- COMPARAISON : AVANT / APRÈS -->
|
||||
<div class="section">
|
||||
<h2>📊 Comparaison : Bleu Actuel vs Slate Moderne</h2>
|
||||
<div class="comparison-section">
|
||||
<div class="comparison-grid">
|
||||
<div class="comparison-item">
|
||||
<div class="comparison-label">Bleu Keep Actuel</div>
|
||||
<div class="comparison-swatch" style="background: #356AC0;">
|
||||
#356AC0
|
||||
</div>
|
||||
<span class="comparison-code">Bleu saturé</span>
|
||||
</div>
|
||||
|
||||
<div class="comparison-item">
|
||||
<div class="comparison-label">Slate Moderne (NOUVEAU)</div>
|
||||
<div class="comparison-swatch" style="background: #7A8A9A;">
|
||||
#7A8A9A
|
||||
</div>
|
||||
<span class="comparison-code">Gris-bleu élégant</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- THEME SLATE - LIGHT MODE -->
|
||||
<div class="section">
|
||||
<h2>✨ Option 1 : Slate (Gris-Bleu) - Mode Light</h2>
|
||||
<div class="theme-comparison">
|
||||
<div class="theme-box winner">
|
||||
<div class="theme-header">
|
||||
<span class="theme-name">🏆 SLATE LIGHT</span>
|
||||
</div>
|
||||
<div class="color-swatches">
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #F8F9FB;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Background</div>
|
||||
<div class="color-value">#F8F9FB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #FFFFFF;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Card</div>
|
||||
<div class="color-value">#FFFFFF</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #F3F4F6;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Sidebar</div>
|
||||
<div class="color-value">#F3F4F6</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #3B4252;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Texte (foreground)</div>
|
||||
<div class="color-value">#3B4252</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #7A8A9A;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Primary (boutons)</div>
|
||||
<div class="color-value">#7A8A9A</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #E5E7EB;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Border</div>
|
||||
<div class="color-value">#E5E7EB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- THEME SLATE - DARK MODE -->
|
||||
<div class="section">
|
||||
<h2>🌙 Option 1 : Slate (Gris-Bleu) - Mode Dark</h2>
|
||||
<div class="theme-comparison">
|
||||
<div class="theme-box winner">
|
||||
<div class="theme-header">
|
||||
<span class="theme-name">🏆 SLATE DARK</span>
|
||||
</div>
|
||||
<div class="color-swatches">
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #1F2937;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Background</div>
|
||||
<div class="color-value">#1F2937</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #2D3748;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Card</div>
|
||||
<div class="color-value">#2D3748</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #1A202C;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Sidebar</div>
|
||||
<div class="color-value">#1A202C</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #F7FAFC;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Texte (foreground)</div>
|
||||
<div class="color-value">#F7FAFC</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #9CA3AF;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Primary (boutons)</div>
|
||||
<div class="color-value">#9CA3AF</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #4A5568;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Border</div>
|
||||
<div class="color-value">#4A5568</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COULEURS DES NOTES -->
|
||||
<div class="section">
|
||||
<h2>📝 Couleurs des Notes (Light Mode)</h2>
|
||||
<div class="note-colors">
|
||||
<div class="note-card" style="background: #FFFFFF; border: 1px solid #E5E7EB;">
|
||||
<div class="note-title">Default</div>
|
||||
<div class="note-content">Note blanche standard</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FEF2F2; border: 1px solid #FEE2E2;">
|
||||
<div class="note-title" style="color: #7F1D1D;">Red</div>
|
||||
<div class="note-content" style="color: #991B1B;">Note rouge</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FFF7ED; border: 1px solid #FFEDD5;">
|
||||
<div class="note-title" style="color: #9A3412;">Orange</div>
|
||||
<div class="note-content" style="color: #C2410C;">Note orange</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FEFCE8; border: 1px solid #FEF9C3;">
|
||||
<div class="note-title" style="color: #854D0E;">Yellow</div>
|
||||
<div class="note-content" style="color: #A16207;">Note jaune</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #ECFDF5; border: 1px solid #D1FAE5;">
|
||||
<div class="note-title" style="color: #065F46;">Green (Emerald)</div>
|
||||
<div class="note-content" style="color: #047857;">Note verte</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #F0FDFA; border: 1px solid #CCFBF1;">
|
||||
<div class="note-title" style="color: #0F766E;">Teal</div>
|
||||
<div class="note-content" style="color: #115E59;">Note teal</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #EFF6FF; border: 1px solid #DBEAFE;">
|
||||
<div class="note-title" style="color: #1E40AF;">Blue (Sky)</div>
|
||||
<div class="note-content" style="color: #1D4ED8;">Note bleue</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #EEF2FF; border: 1px solid #E0E7FF;">
|
||||
<div class="note-title" style="color: #4338CA;">Indigo</div>
|
||||
<div class="note-content" style="color: #4338CA;">Note indigo</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #F5F3FF; border: 1px solid #EDE9FE;">
|
||||
<div class="note-title" style="color: #7C3AED;">Violet</div>
|
||||
<div class="note-content" style="color: #7C3AED;">Note violette</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FAF5FF; border: 1px solid #F3E8FF;">
|
||||
<div class="note-title" style="color: #9333EA;">Purple</div>
|
||||
<div class="note-content" style="color: #9333EA;">Note pourpre</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FDF2F8; border: 1px solid #FCE7F3;">
|
||||
<div class="note-title" style="color: #BE185D;">Pink</div>
|
||||
<div class="note-content" style="color: #BE185D;">Note rose</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #FFF1F2; border: 1px solid #FFE4E6;">
|
||||
<div class="note-title" style="color: #E11D48;">Rose</div>
|
||||
<div class="note-content" style="color: #E11D48;">Note rose rougeâtre</div>
|
||||
</div>
|
||||
|
||||
<div class="note-card" style="background: #F5F5F4; border: 1px solid #E7E5E4;">
|
||||
<div class="note-title" style="color: #71717A;">Gray</div>
|
||||
<div class="note-content" style="color: #71717A;">Note grise</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AUTRES OPTIONS -->
|
||||
<div class="section">
|
||||
<h2>🔄 Autres Options de Thème</h2>
|
||||
<div class="theme-comparison">
|
||||
<div class="theme-box">
|
||||
<div class="theme-header">
|
||||
<span class="theme-name">⚫ MONOCHROME</span>
|
||||
</div>
|
||||
<div class="color-swatches">
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #FFFFFF;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Background</div>
|
||||
<div class="color-value">#FFFFFF</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #262626;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Primary</div>
|
||||
<div class="color-value">#262626</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="theme-box">
|
||||
<div class="theme-header">
|
||||
<span class="theme-name">💜 INDIGO (Violet)</span>
|
||||
</div>
|
||||
<div class="color-swatches">
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #F9F9FB;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Background</div>
|
||||
<div class="color-value">#F9F9FB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #7C3AED;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Primary</div>
|
||||
<div class="color-value">#7C3AED</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="theme-box">
|
||||
<div class="theme-header">
|
||||
<span class="theme-name">🌊 TEAL (Turquoise)</span>
|
||||
</div>
|
||||
<div class="color-swatches">
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #F9FAFB;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Background</div>
|
||||
<div class="color-value">#F9FAFB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="color-box" style="background: #0F766E;"></div>
|
||||
<div class="color-info">
|
||||
<div class="color-label">Primary</div>
|
||||
<div class="color-value">#0F766E</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section" style="text-align: center; margin-top: 60px;">
|
||||
<h2>💬 Votre Choix Ramez ?</h2>
|
||||
<p style="font-size: 18px; color: #666; margin-bottom: 30px;">
|
||||
Regardez les couleurs, choisissez ce que vous préférez, puis dites-moi :
|
||||
</p>
|
||||
<div style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;">
|
||||
<div style="background: #10B981; color: white; padding: 15px 30px; border-radius: 8px; font-weight: bold; font-size: 16px;">
|
||||
✅ SLATE (mon choix)
|
||||
</div>
|
||||
<div style="background: #6366F1; color: white; padding: 15px 30px; border-radius: 8px; font-weight: bold; font-size: 16px;">
|
||||
💜 INDIGO
|
||||
</div>
|
||||
<div style="background: #0F766E; color: white; padding: 15px 30px; border-radius: 8px; font-weight: bold; font-size: 16px;">
|
||||
🌊 TEAL
|
||||
</div>
|
||||
<div style="background: #262626; color: white; padding: 15px 30px; border-radius: 8px; font-weight: bold; font-size: 16px;">
|
||||
⚫ MONOCHROME
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 40px; padding-bottom: 40px; color: #999; font-size: 14px;">
|
||||
<p>💡 Ouvrez ce fichier dans votre navigateur pour voir toutes les couleurs !</p>
|
||||
<p>Double-cliquez sur : keep-notes/VISUALISATION-COULEURS.html</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -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 {
|
||||
|
||||
@ -102,7 +102,7 @@ export function AIAssistantActionBar({
|
||||
onClick={() => handleAction(onShorten)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Scissors className="h-3 w-3 mr-1 text-blue-600 dark:text-blue-400" />
|
||||
<Scissors className="h-3 w-3 mr-1 text-primary dark:text-primary-foreground" />
|
||||
{t('ai.shorten')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@ -36,7 +36,7 @@ export function ComparisonModal({
|
||||
|
||||
const getNoteColor = (index: number) => {
|
||||
const colors = [
|
||||
'border-blue-200 dark:border-blue-800 hover:border-blue-300 dark:hover:border-blue-700',
|
||||
'border-primary/20 dark:border-primary/30 hover:border-primary/30 dark:hover:border-primary/40',
|
||||
'border-purple-200 dark:border-purple-800 hover:border-purple-300 dark:hover:border-purple-700',
|
||||
'border-green-200 dark:border-green-800 hover:border-green-300 dark:hover:border-green-700'
|
||||
]
|
||||
@ -45,7 +45,7 @@ export function ComparisonModal({
|
||||
|
||||
const getTitleColor = (index: number) => {
|
||||
const colors = [
|
||||
'text-blue-600 dark:text-blue-400',
|
||||
'text-primary dark:text-primary-foreground',
|
||||
'text-purple-600 dark:text-purple-400',
|
||||
'text-green-600 dark:text-green-400'
|
||||
]
|
||||
|
||||
@ -31,7 +31,7 @@ const NOTEBOOK_ICONS = [
|
||||
]
|
||||
|
||||
const NOTEBOOK_COLORS = [
|
||||
{ name: 'Blue', value: '#3B82F6', bg: 'bg-blue-500' },
|
||||
{ name: 'Slate', value: '#64748B', bg: 'bg-slate-500' },
|
||||
{ name: 'Purple', value: '#8B5CF6', bg: 'bg-purple-500' },
|
||||
{ name: 'Red', value: '#EF4444', bg: 'bg-red-500' },
|
||||
{ name: 'Orange', value: '#F59E0B', bg: 'bg-orange-500' },
|
||||
|
||||
@ -245,7 +245,7 @@ export function Header({
|
||||
const NavItem = ({ href, icon: Icon, label, active, onClick }: any) => {
|
||||
const content = (
|
||||
<>
|
||||
<Icon className={cn("h-5 w-5", active && "fill-current text-blue-900")} />
|
||||
<Icon className={cn("h-5 w-5", active && "fill-current text-primary")} />
|
||||
{label}
|
||||
</>
|
||||
)
|
||||
@ -257,7 +257,7 @@ export function Header({
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-4 py-3 rounded-r-full text-sm font-medium transition-colors mr-2 text-left",
|
||||
active
|
||||
? "bg-blue-100 text-blue-900"
|
||||
? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-foreground"
|
||||
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
|
||||
)}
|
||||
style={{ minHeight: '44px' }}
|
||||
@ -275,7 +275,7 @@ export function Header({
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-3 rounded-r-full text-sm font-medium transition-colors mr-2",
|
||||
active
|
||||
? "bg-blue-100 text-blue-900"
|
||||
? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-foreground"
|
||||
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
|
||||
)}
|
||||
style={{ minHeight: '44px' }}
|
||||
@ -297,7 +297,7 @@ export function Header({
|
||||
|
||||
{/* Logo MEMENTO */}
|
||||
<div className="flex items-center gap-3 text-slate-900 dark:text-white cursor-pointer group" onClick={() => router.push('/')}>
|
||||
<div className="size-8 bg-blue-500 rounded-lg flex items-center justify-center text-white shadow-sm group-hover:shadow-md transition-all">
|
||||
<div className="size-8 bg-primary rounded-lg flex items-center justify-center text-primary-foreground shadow-sm group-hover:shadow-md transition-all">
|
||||
<StickyNote className="w-5 h-5" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold leading-tight tracking-tight">MEMENTO</h2>
|
||||
@ -333,7 +333,7 @@ export function Header({
|
||||
{/* User Avatar Menu */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="flex items-center justify-center bg-center bg-no-repeat bg-cover rounded-full size-10 ring-2 ring-white dark:ring-slate-700 cursor-pointer shadow-sm hover:shadow-md transition-shadow bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-200"
|
||||
<div className="flex items-center justify-center bg-center bg-no-repeat bg-cover rounded-full size-10 ring-2 ring-white dark:ring-slate-700 cursor-pointer shadow-sm hover:shadow-md transition-shadow bg-primary/10 dark:bg-primary/20 text-primary dark:text-primary-foreground"
|
||||
style={currentUser?.image ? { backgroundImage: `url(${currentUser?.image})` } : undefined}>
|
||||
{!currentUser?.image && (
|
||||
<span className="text-sm font-semibold">
|
||||
|
||||
@ -100,7 +100,7 @@ export function LabelSelector({
|
||||
>
|
||||
<div className={cn(
|
||||
"h-4 w-4 border rounded flex items-center justify-center transition-colors",
|
||||
isSelected ? "bg-blue-600 border-blue-600 text-white" : "border-gray-400"
|
||||
isSelected ? "bg-primary border-primary text-primary-foreground" : "border-gray-400"
|
||||
)}>
|
||||
{isSelected && <Check className="h-3 w-3" />}
|
||||
</div>
|
||||
|
||||
@ -19,7 +19,7 @@ export function MarkdownContent({ content, className = '' }: MarkdownContentProp
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
a: ({ node, ...props }) => (
|
||||
<a {...props} className="text-blue-500 hover:underline" target="_blank" rel="noopener noreferrer" />
|
||||
<a {...props} className="text-primary hover:underline" target="_blank" rel="noopener noreferrer" />
|
||||
)
|
||||
}}
|
||||
>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
.masonry-item-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* height: auto - let content determine height */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@ -45,16 +45,22 @@
|
||||
}
|
||||
|
||||
/* Note Size Styles - Desktop Default */
|
||||
.masonry-item[data-size="small"],
|
||||
.note-card[data-size="small"] {
|
||||
min-height: 150px !important;
|
||||
min-height: 150px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.masonry-item[data-size="medium"],
|
||||
.note-card[data-size="medium"] {
|
||||
min-height: 200px !important;
|
||||
min-height: 200px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.masonry-item[data-size="large"],
|
||||
.note-card[data-size="large"] {
|
||||
min-height: 300px !important;
|
||||
min-height: 300px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Drag State Styles - Clean and flat behavior requested by user */
|
||||
@ -124,16 +130,22 @@
|
||||
}
|
||||
|
||||
/* Smaller note sizes on mobile */
|
||||
.masonry-item[data-size="small"],
|
||||
.masonry-item-content .note-card[data-size="small"] {
|
||||
min-height: 120px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.masonry-item[data-size="medium"],
|
||||
.masonry-item-content .note-card[data-size="medium"] {
|
||||
min-height: 160px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.masonry-item[data-size="large"],
|
||||
.masonry-item-content .note-card[data-size="large"] {
|
||||
min-height: 240px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Reduced drag effect on mobile */
|
||||
@ -183,7 +195,7 @@
|
||||
.masonry-item,
|
||||
.masonry-item-content,
|
||||
.note-card {
|
||||
transition-property: transform, box-shadow, opacity;
|
||||
transition-property: box-shadow, opacity;
|
||||
transition-duration: 0.2s;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@ -20,12 +20,13 @@ interface MasonryItemProps {
|
||||
note: Note;
|
||||
onEdit: (note: Note, readOnly?: boolean) => void;
|
||||
onResize: () => void;
|
||||
onNoteSizeChange: (noteId: string, newSize: 'small' | 'medium' | 'large') => void;
|
||||
onDragStart?: (noteId: string) => void;
|
||||
onDragEnd?: () => void;
|
||||
isDragging?: boolean;
|
||||
}
|
||||
|
||||
const MasonryItem = memo(function MasonryItem({ note, onEdit, onResize, onDragStart, onDragEnd, isDragging }: MasonryItemProps) {
|
||||
const MasonryItem = memo(function MasonryItem({ note, onEdit, onResize, onNoteSizeChange, onDragStart, onDragEnd, isDragging }: MasonryItemProps) {
|
||||
const resizeRef = useResizeObserver(onResize);
|
||||
|
||||
return (
|
||||
@ -43,13 +44,12 @@ const MasonryItem = memo(function MasonryItem({ note, onEdit, onResize, onDragSt
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
isDragging={isDragging}
|
||||
onResize={onResize}
|
||||
onSizeChange={(newSize) => onNoteSizeChange(note.id, newSize)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, (prev, next) => {
|
||||
// Custom comparison to avoid re-render on function prop changes if note data is same
|
||||
return prev.note.id === next.note.id && prev.isDragging === next.isDragging;
|
||||
});
|
||||
|
||||
export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
@ -57,6 +57,22 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null);
|
||||
const { startDrag, endDrag, draggedNoteId } = useNotebookDrag();
|
||||
|
||||
// Local state for notes with dynamic size updates
|
||||
// This allows size changes to propagate immediately without waiting for server
|
||||
const [localNotes, setLocalNotes] = useState<Note[]>(notes);
|
||||
|
||||
// Sync localNotes when parent notes prop changes
|
||||
useEffect(() => {
|
||||
setLocalNotes(notes);
|
||||
}, [notes]);
|
||||
|
||||
// Callback for when a note's size changes - update local state immediately
|
||||
const handleNoteSizeChange = useCallback((noteId: string, newSize: 'small' | 'medium' | 'large') => {
|
||||
setLocalNotes(prevNotes =>
|
||||
prevNotes.map(n => n.id === noteId ? { ...n, size: newSize } : n)
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleEdit = useCallback((note: Note, readOnly?: boolean) => {
|
||||
if (onEdit) {
|
||||
onEdit(note, readOnly);
|
||||
@ -70,14 +86,14 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
const pinnedMuuri = useRef<any>(null);
|
||||
const othersMuuri = useRef<any>(null);
|
||||
|
||||
// Memoize filtered notes (order comes from array)
|
||||
// Memoize filtered notes from localNotes (which includes dynamic size updates)
|
||||
const pinnedNotes = useMemo(
|
||||
() => notes.filter(n => n.isPinned),
|
||||
[notes]
|
||||
() => localNotes.filter(n => n.isPinned),
|
||||
[localNotes]
|
||||
);
|
||||
const othersNotes = useMemo(
|
||||
() => notes.filter(n => !n.isPinned),
|
||||
[notes]
|
||||
() => localNotes.filter(n => !n.isPinned),
|
||||
[localNotes]
|
||||
);
|
||||
|
||||
const handleDragEnd = useCallback(async (grid: any) => {
|
||||
@ -152,7 +168,7 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
const columns = calculateColumns(containerWidth);
|
||||
const itemWidth = calculateItemWidth(containerWidth, columns);
|
||||
|
||||
console.log(`[Masonry] Container width: ${containerWidth}px, Columns: ${columns}, Item width: ${itemWidth}px`);
|
||||
|
||||
|
||||
const layoutOptions = {
|
||||
dragEnabled: true,
|
||||
@ -279,6 +295,13 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
requestAnimationFrame(() => {
|
||||
syncGridItems(pinnedMuuri.current, pinnedGridRef, pinnedNotes);
|
||||
syncGridItems(othersMuuri.current, othersGridRef, othersNotes);
|
||||
|
||||
// CRITICAL: Force a second layout after CSS transitions (padding/height changes) complete
|
||||
// NoteCard has a 200ms transition. We wait 300ms to be safe.
|
||||
setTimeout(() => {
|
||||
if (pinnedMuuri.current) pinnedMuuri.current.refreshItems().layout();
|
||||
if (othersMuuri.current) othersMuuri.current.refreshItems().layout();
|
||||
}, 300);
|
||||
});
|
||||
}, [pinnedNotes, othersNotes]); // Re-run when notes change
|
||||
|
||||
@ -295,7 +318,7 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
const containerWidth = entries[0]?.contentRect.width || window.innerWidth - 32;
|
||||
const columns = calculateColumns(containerWidth);
|
||||
|
||||
console.log(`[Masonry Resize] Width: ${containerWidth}px, Columns: ${columns}`);
|
||||
|
||||
|
||||
// Apply dimensions to both grids
|
||||
applyItemDimensions(pinnedMuuri.current, containerWidth);
|
||||
@ -331,10 +354,11 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
<div ref={pinnedGridRef} className="relative min-h-[100px]">
|
||||
{pinnedNotes.map(note => (
|
||||
<MasonryItem
|
||||
key={note.id}
|
||||
key={`${note.id}-${note.size}`}
|
||||
note={note}
|
||||
onEdit={handleEdit}
|
||||
onResize={refreshLayout}
|
||||
onNoteSizeChange={handleNoteSizeChange}
|
||||
onDragStart={startDrag}
|
||||
onDragEnd={endDrag}
|
||||
isDragging={draggedNoteId === note.id}
|
||||
@ -352,10 +376,11 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
<div ref={othersGridRef} className="relative min-h-[100px]">
|
||||
{othersNotes.map(note => (
|
||||
<MasonryItem
|
||||
key={note.id}
|
||||
key={`${note.id}-${note.size}`}
|
||||
note={note}
|
||||
onEdit={handleEdit}
|
||||
onResize={refreshLayout}
|
||||
onNoteSizeChange={handleNoteSizeChange}
|
||||
onDragStart={startDrag}
|
||||
onDragEnd={endDrag}
|
||||
isDragging={draggedNoteId === note.id}
|
||||
@ -373,39 +398,6 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
/>
|
||||
)}
|
||||
|
||||
<style jsx global>{`
|
||||
.masonry-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.masonry-item {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.masonry-item.muuri-item-dragging {
|
||||
z-index: 3;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.masonry-item.muuri-item-releasing {
|
||||
z-index: 2;
|
||||
}
|
||||
.masonry-item.muuri-item-hidden {
|
||||
z-index: 0;
|
||||
}
|
||||
.masonry-item-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* Ensure proper box-sizing for all elements in the grid */
|
||||
.masonry-item *,
|
||||
.masonry-item-content * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ export function MemoryEchoNotification({ onOpenNote }: MemoryEchoNotificationPro
|
||||
}}
|
||||
className="cursor-pointer border dark:border-zinc-700 rounded-lg p-4 hover:border-amber-300 dark:hover:border-amber-700 transition-colors"
|
||||
>
|
||||
<h3 className="font-semibold text-blue-600 dark:text-blue-400 mb-2">
|
||||
<h3 className="font-semibold text-primary dark:text-primary-foreground mb-2">
|
||||
{note1Title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-4">
|
||||
@ -278,7 +278,7 @@ export function MemoryEchoNotification({ onOpenNote }: MemoryEchoNotificationPro
|
||||
{/* Connected notes */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Badge variant="outline" className="border-blue-200 text-blue-700 dark:border-blue-900 dark:text-blue-300">
|
||||
<Badge variant="outline" className="border-primary/20 text-primary dark:border-primary/30 dark:text-primary-foreground">
|
||||
{note1Title}
|
||||
</Badge>
|
||||
<ArrowRight className="h-3 w-3 text-gray-400" />
|
||||
|
||||
@ -111,7 +111,9 @@ export function NoteActions({
|
||||
{(['small', 'medium', 'large'] as const).map((size) => (
|
||||
<DropdownMenuItem
|
||||
key={size}
|
||||
onClick={() => onSizeChange(size)}
|
||||
onClick={(e) => {
|
||||
onSizeChange?.(size);
|
||||
}}
|
||||
className={cn(
|
||||
"capitalize",
|
||||
currentSize === size && "bg-accent"
|
||||
|
||||
@ -86,6 +86,8 @@ interface NoteCardProps {
|
||||
isDragOver?: boolean
|
||||
onDragStart?: (noteId: string) => void
|
||||
onDragEnd?: () => void
|
||||
onResize?: () => void
|
||||
onSizeChange?: (newSize: 'small' | 'medium' | 'large') => void
|
||||
}
|
||||
|
||||
// Helper function to get initials from name
|
||||
@ -116,7 +118,15 @@ function getAvatarColor(name: string): string {
|
||||
return colors[hash % colors.length]
|
||||
}
|
||||
|
||||
export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, onDragEnd }: NoteCardProps) {
|
||||
export function NoteCard({
|
||||
note,
|
||||
onEdit,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
isDragging,
|
||||
onResize,
|
||||
onSizeChange
|
||||
}: NoteCardProps) {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { refreshLabels } = useLabels()
|
||||
@ -138,7 +148,14 @@ export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, on
|
||||
setShowNotebookMenu(false)
|
||||
// No need for router.refresh() - triggerRefresh() is already called in moveNoteToNotebookOptimistic
|
||||
}
|
||||
const colorClasses = NOTE_COLORS[note.color as NoteColor] || NOTE_COLORS.default
|
||||
|
||||
// Optimistic UI state for instant feedback
|
||||
const [optimisticNote, addOptimisticNote] = useOptimistic(
|
||||
note,
|
||||
(state, newProps: Partial<Note>) => ({ ...state, ...newProps })
|
||||
)
|
||||
|
||||
const colorClasses = NOTE_COLORS[optimisticNote.color as NoteColor] || NOTE_COLORS.default
|
||||
|
||||
// Check if this note is currently open in the editor
|
||||
const isNoteOpenInEditor = searchParams.get('note') === note.id
|
||||
@ -148,12 +165,6 @@ export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, on
|
||||
comparisonNotes && comparisonNotes.length > 0 ? comparisonNotes : null
|
||||
)
|
||||
|
||||
// Optimistic UI state for instant feedback
|
||||
const [optimisticNote, addOptimisticNote] = useOptimistic(
|
||||
note,
|
||||
(state, newProps: Partial<Note>) => ({ ...state, ...newProps })
|
||||
)
|
||||
|
||||
const currentUserId = session?.user?.id
|
||||
const canManageCollaborators = currentUserId && note.userId && currentUserId === note.userId
|
||||
const isSharedNote = currentUserId && note.userId && currentUserId !== note.userId
|
||||
@ -226,10 +237,15 @@ export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, on
|
||||
}
|
||||
|
||||
const handleSizeChange = async (size: 'small' | 'medium' | 'large') => {
|
||||
startTransition(async () => {
|
||||
addOptimisticNote({ size })
|
||||
await updateSize(note.id, size)
|
||||
})
|
||||
// Notify parent of size change so it can update its state
|
||||
onSizeChange?.(size)
|
||||
|
||||
// Trigger layout refresh
|
||||
onResize?.()
|
||||
setTimeout(() => onResize?.(), 300)
|
||||
|
||||
// Update server in background
|
||||
updateSize(note.id, size)
|
||||
}
|
||||
|
||||
const handleCheckItem = async (checkItemId: string) => {
|
||||
@ -267,12 +283,23 @@ export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, on
|
||||
|
||||
if (isDeleting) return null
|
||||
|
||||
const getMinHeight = (size?: string) => {
|
||||
switch (size) {
|
||||
case 'medium': return '200px'
|
||||
case 'large': return '300px'
|
||||
default: return '150px' // small
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Card
|
||||
data-testid="note-card"
|
||||
data-draggable="true"
|
||||
data-note-id={note.id}
|
||||
data-size={note.size}
|
||||
data-size={optimisticNote.size}
|
||||
style={{ minHeight: getMinHeight(optimisticNote.size) }}
|
||||
draggable={true}
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData('text/plain', note.id)
|
||||
@ -560,7 +587,7 @@ export function NoteCard({ note, onEdit, isDragging, isDragOver, onDragStart, on
|
||||
isPinned={optimisticNote.isPinned}
|
||||
isArchived={optimisticNote.isArchived}
|
||||
currentColor={optimisticNote.color}
|
||||
currentSize={optimisticNote.size}
|
||||
currentSize={optimisticNote.size as 'small' | 'medium' | 'large'}
|
||||
onTogglePin={handleTogglePin}
|
||||
onToggleArchive={handleToggleArchive}
|
||||
onColorChange={handleColorChange}
|
||||
|
||||
@ -550,7 +550,7 @@ export function NoteEditor({ note, readOnly = false, onClose }: NoteEditorProps)
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold">{readOnly ? t('notes.view') : t('notes.edit')}</h2>
|
||||
{readOnly && (
|
||||
<Badge variant="secondary" className="bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300">
|
||||
<Badge variant="secondary" className="bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-foreground">
|
||||
{t('notes.readOnly')}
|
||||
</Badge>
|
||||
)}
|
||||
@ -607,7 +607,7 @@ export function NoteEditor({ note, readOnly = false, onClose }: NoteEditorProps)
|
||||
<div className="p-2 flex-1 min-w-0 flex flex-col justify-center">
|
||||
<h4 className="font-medium text-sm truncate">{link.title || link.url}</h4>
|
||||
{link.description && <p className="text-xs text-gray-500 truncate">{link.description}</p>}
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer" className="text-xs text-blue-500 truncate hover:underline block mt-1">
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer" className="text-xs text-primary truncate hover:underline block mt-1">
|
||||
{new URL(link.url).hostname}
|
||||
</a>
|
||||
</div>
|
||||
@ -636,7 +636,7 @@ export function NoteEditor({ note, readOnly = false, onClose }: NoteEditorProps)
|
||||
setIsMarkdown(!isMarkdown)
|
||||
if (isMarkdown) setShowMarkdownPreview(false)
|
||||
}}
|
||||
className={cn("h-7 text-xs", isMarkdown && "text-blue-600")}
|
||||
className={cn("h-7 text-xs", isMarkdown && "text-primary")}
|
||||
>
|
||||
<FileText className="h-3 w-3 mr-1" />
|
||||
{isMarkdown ? t('notes.markdownOn') : t('notes.markdownOff')}
|
||||
@ -824,7 +824,7 @@ export function NoteEditor({ note, readOnly = false, onClose }: NoteEditorProps)
|
||||
size="sm"
|
||||
onClick={() => setShowReminderDialog(true)}
|
||||
title={t('notes.setReminder')}
|
||||
className={currentReminder ? "text-blue-600" : ""}
|
||||
className={currentReminder ? "text-primary" : ""}
|
||||
>
|
||||
<Bell className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
@ -645,7 +645,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
|
||||
<div className="p-2 flex-1 min-w-0 flex flex-col justify-center">
|
||||
<h4 className="font-medium text-sm truncate">{link.title || link.url}</h4>
|
||||
{link.description && <p className="text-xs text-gray-500 truncate">{link.description}</p>}
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer" className="text-xs text-blue-500 truncate hover:underline block mt-1">
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer" className="text-xs text-primary truncate hover:underline block mt-1">
|
||||
{new URL(link.url).hostname}
|
||||
</a>
|
||||
</div>
|
||||
@ -781,7 +781,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
|
||||
size="icon"
|
||||
className={cn(
|
||||
"h-8 w-8",
|
||||
currentReminder && "text-blue-600"
|
||||
currentReminder && "text-primary"
|
||||
)}
|
||||
title={t('notes.remindMe')}
|
||||
onClick={handleReminderOpen}
|
||||
@ -799,7 +799,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
|
||||
size="icon"
|
||||
className={cn(
|
||||
"h-8 w-8",
|
||||
isMarkdown && "text-blue-600"
|
||||
isMarkdown && "text-primary"
|
||||
)}
|
||||
onClick={() => {
|
||||
setIsMarkdown(!isMarkdown)
|
||||
|
||||
@ -103,7 +103,7 @@ export function NotebookSuggestionToast({
|
||||
<div
|
||||
className={cn(
|
||||
'fixed bottom-4 right-4 z-50 max-w-md bg-white dark:bg-zinc-800',
|
||||
'border border-blue-200 dark:border-blue-800 rounded-lg shadow-lg',
|
||||
'border border-border dark:border-border rounded-lg shadow-lg',
|
||||
'p-4 animate-in slide-in-from-bottom-4 fade-in duration-300',
|
||||
'transition-all duration-300'
|
||||
)}
|
||||
@ -111,8 +111,8 @@ export function NotebookSuggestionToast({
|
||||
<div className="flex items-start gap-3">
|
||||
{/* Icon */}
|
||||
<div className="flex-shrink-0">
|
||||
<div className="w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center">
|
||||
<FolderOpen className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
||||
<div className="w-10 h-10 rounded-full bg-primary/10 dark:bg-primary/20 flex items-center justify-center">
|
||||
<FolderOpen className="w-5 h-5 text-primary dark:text-primary-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -131,7 +131,7 @@ export function NotebookSuggestionToast({
|
||||
{/* Move button */}
|
||||
<button
|
||||
onClick={handleMoveToNotebook}
|
||||
className="px-3 py-1.5 text-xs font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700 transition-colors"
|
||||
className="px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
{t('notebookSuggestion.move')}
|
||||
</button>
|
||||
|
||||
@ -158,8 +158,8 @@ export function NotebooksList() {
|
||||
onDragLeave={handleDragLeave}
|
||||
className={cn(
|
||||
"flex flex-col mr-2 rounded-r-full overflow-hidden transition-all",
|
||||
!notebook.color && "bg-blue-50 dark:bg-blue-900/20",
|
||||
isDragOver && "ring-2 ring-blue-500 ring-dashed"
|
||||
!notebook.color && "bg-primary/10 dark:bg-primary/20",
|
||||
isDragOver && "ring-2 ring-primary ring-dashed"
|
||||
)}
|
||||
style={notebook.color ? { backgroundColor: `${notebook.color}20` } : undefined}
|
||||
>
|
||||
@ -167,11 +167,11 @@ export function NotebooksList() {
|
||||
<div className="pointer-events-auto flex items-center justify-between px-6 py-3">
|
||||
<div className="flex items-center gap-4 min-w-0">
|
||||
<NotebookIcon
|
||||
className={cn("w-5 h-5 flex-shrink-0 fill-current", !notebook.color && "text-blue-700 dark:text-blue-100")}
|
||||
className={cn("w-5 h-5 flex-shrink-0 fill-current", !notebook.color && "text-primary dark:text-primary-foreground")}
|
||||
style={notebook.color ? { color: notebook.color } : undefined}
|
||||
/>
|
||||
<span
|
||||
className={cn("text-sm font-medium tracking-wide truncate max-w-[120px]", !notebook.color && "text-blue-700 dark:text-blue-100")}
|
||||
className={cn("text-sm font-medium tracking-wide truncate max-w-[120px]", !notebook.color && "text-primary dark:text-primary-foreground")}
|
||||
style={notebook.color ? { color: notebook.color } : undefined}
|
||||
>
|
||||
{notebook.name}
|
||||
@ -179,7 +179,7 @@ export function NotebooksList() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleToggleExpand(notebook.id)}
|
||||
className={cn("transition-colors p-1 flex-shrink-0", !notebook.color && "text-blue-600 hover:text-blue-800 dark:text-blue-200 dark:hover:text-blue-100")}
|
||||
className={cn("transition-colors p-1 flex-shrink-0", !notebook.color && "text-primary hover:text-primary/80 dark:text-primary-foreground dark:hover:text-primary-foreground/80")}
|
||||
style={notebook.color ? { color: notebook.color } : undefined}
|
||||
>
|
||||
<ChevronDown className={cn("w-4 h-4 transition-transform", isExpanded && "rotate-180")} />
|
||||
|
||||
@ -126,14 +126,14 @@ export function NotificationPanel() {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-80">
|
||||
<div className="px-4 py-3 border-b bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950/20 dark:to-indigo-950/20">
|
||||
<div className="px-4 py-3 border-b bg-gradient-to-r from-primary/5 to-primary/10 dark:from-primary/10 dark:to-primary/15">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bell className="h-4 w-4 text-blue-600 dark:text-blue-400" />
|
||||
<Bell className="h-4 w-4 text-primary dark:text-primary-foreground" />
|
||||
<span className="font-semibold text-sm">{t('nav.aiSettings')}</span>
|
||||
</div>
|
||||
{pendingCount > 0 && (
|
||||
<Badge className="bg-blue-600 hover:bg-blue-700 text-white shadow-md">
|
||||
<Badge className="bg-primary hover:bg-primary/90 text-primary-foreground shadow-md">
|
||||
{pendingCount}
|
||||
</Badge>
|
||||
)}
|
||||
@ -142,7 +142,7 @@ export function NotificationPanel() {
|
||||
|
||||
{isLoading ? (
|
||||
<div className="p-6 text-center text-sm text-muted-foreground">
|
||||
<div className="animate-spin h-6 w-6 border-2 border-blue-600 border-t-transparent rounded-full mx-auto mb-2" />
|
||||
<div className="animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full mx-auto mb-2" />
|
||||
{t('general.loading')}
|
||||
</div>
|
||||
) : requests.length === 0 ? (
|
||||
@ -171,7 +171,7 @@ export function NotificationPanel() {
|
||||
</div>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-xs capitalize bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300 border-0"
|
||||
className="text-xs capitalize bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-foreground border-0"
|
||||
>
|
||||
{request.permission}
|
||||
</Badge>
|
||||
|
||||
@ -73,11 +73,11 @@ function CompactCard({
|
||||
{/* Subtle left accent - colored based on recency */}
|
||||
<div className={cn(
|
||||
"absolute left-0 top-0 bottom-0 w-1 rounded-l-xl",
|
||||
isFirstNote
|
||||
? "bg-gradient-to-b from-blue-500 to-indigo-500"
|
||||
isFirstNote
|
||||
? "bg-gradient-to-b from-primary to-primary/70"
|
||||
: index === 1
|
||||
? "bg-blue-400 dark:bg-blue-500"
|
||||
: "bg-gray-300 dark:bg-gray-600"
|
||||
? "bg-primary/80 dark:bg-primary/70"
|
||||
: "bg-muted dark:bg-muted/60"
|
||||
)} />
|
||||
|
||||
{/* Content with left padding for accent line */}
|
||||
@ -105,7 +105,7 @@ function CompactCard({
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* Notebook indicator */}
|
||||
{note.notebookId && (
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-blue-500 dark:bg-blue-400" title="In notebook" />
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary dark:bg-primary/70" title="In notebook" />
|
||||
)}
|
||||
{/* Labels indicator */}
|
||||
{note.labels && note.labels.length > 0 && (
|
||||
|
||||
@ -53,8 +53,8 @@ export function Sidebar({ className, user }: { className?: string, user?: any })
|
||||
"flex items-center gap-4 px-6 py-3 rounded-r-full mr-2 transition-colors",
|
||||
"text-sm font-medium tracking-wide",
|
||||
active
|
||||
? "bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-100"
|
||||
: "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50"
|
||||
? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-foreground"
|
||||
: "text-muted-foreground hover:bg-muted/50 dark:hover:bg-muted/30"
|
||||
)}
|
||||
>
|
||||
<Icon className={cn("w-5 h-5", active ? "fill-current" : "")} />
|
||||
|
||||
@ -9,10 +9,10 @@ interface ThemeInitializerProps {
|
||||
|
||||
export function ThemeInitializer({ theme, fontSize }: ThemeInitializerProps) {
|
||||
useEffect(() => {
|
||||
console.log('[ThemeInitializer] Received theme:', theme)
|
||||
|
||||
// Helper to apply theme
|
||||
const applyTheme = (t?: string) => {
|
||||
console.log('[ThemeInitializer] Applying theme:', t)
|
||||
|
||||
if (!t) return
|
||||
|
||||
const root = document.documentElement
|
||||
@ -66,7 +66,7 @@ export function ThemeInitializer({ theme, fontSize }: ThemeInitializerProps) {
|
||||
const localTheme = localStorage.getItem('theme-preference')
|
||||
const effectiveTheme = localTheme || theme
|
||||
|
||||
console.log('[ThemeInitializer] Local theme:', localTheme, '| Server theme:', theme, '| Using:', effectiveTheme)
|
||||
|
||||
|
||||
applyTheme(effectiveTheme)
|
||||
|
||||
|
||||
310
keep-notes/lib/color-harmony-recommendation.ts
Normal file
310
keep-notes/lib/color-harmony-recommendation.ts
Normal file
@ -0,0 +1,310 @@
|
||||
/**
|
||||
* PROPOSITION D'HARMONIE DE COULEURS
|
||||
* ===================================
|
||||
*
|
||||
* Recommandations pour un système de couleurs unifié et moderne
|
||||
* Inspiré de Google Keep avec une approche contemporaine
|
||||
*
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 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;
|
||||
307
keep-notes/lib/modern-color-options.ts
Normal file
307
keep-notes/lib/modern-color-options.ts
Normal file
@ -0,0 +1,307 @@
|
||||
/**
|
||||
* 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;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@ -0,0 +1,24 @@
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- generic [active] [ref=e1]:
|
||||
- main [ref=e4]:
|
||||
- generic [ref=e7]:
|
||||
- heading "Sign in to your account" [level=1] [ref=e8]
|
||||
- generic [ref=e9]:
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]: Email
|
||||
- textbox "Email" [ref=e13]:
|
||||
- /placeholder: Enter your email address
|
||||
- generic [ref=e14]:
|
||||
- generic [ref=e15]: Password
|
||||
- textbox "Password" [ref=e17]:
|
||||
- /placeholder: Enter your password
|
||||
- link "Forgot password?" [ref=e19] [cursor=pointer]:
|
||||
- /url: /forgot-password
|
||||
- button "Sign In" [ref=e20]
|
||||
- region "Notifications alt+T"
|
||||
- button "Open Next.js Dev Tools" [ref=e27] [cursor=pointer]:
|
||||
- img [ref=e28]
|
||||
- alert [ref=e31]
|
||||
```
|
||||
@ -82,4 +82,4 @@ Error generating stack: `+n.message+`
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
</html>
|
||||
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,UEsDBBQAAAgIAAtmMVw+tyFkyAAAABcBAAALAAAAcmVwb3J0Lmpzb25Vj81Ow0AMhF/F8nkVtQQ27d57rZDghnpwEwct+fHK69CiKO+OEgESc5r5DqOZGQc2asgIw7w4zEZqr3FgDPvKH/zT7vF4qMoHh82kZFFGDGVVlYX3xz95h23sOWN4uzhMKh9c25mGX5KNLGOY0cSox7BzyPfEtXGzhWn8F9ueuq/N5S6m9EOlw2A68eKQVUXXbjytLsCzSs05Q6syQC1jG9+LG19fWD9Z4UYZRjGga89gAtvHAk73aFBLwwH2eHEoab237lyWb1BLAQI/AxQAAAgIAAtmMVw+tyFkyAAAABcBAAALAAAAAAAAAAAAAAC0gQAAAAByZXBvcnQuanNvblBLBQYAAAAAAQABADkAAADxAAAAAAA=</script>
|
||||
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,UEsDBBQAAAgIANWbOFwzz5hazwgAALsgAAAZAAAAMjZkNTlhZjBhZTUxYWM3NDU5MDYuanNvbu1Zf3PjthH9KltMZyRPKZmiRMpi6mt91ybn9u4y03Mn05pOApErCTEJaADQss/n794BCFkURdnyNU06ncp/mBSBh/3x8HYF3pMZy/E8IzEJoiyc0JlPMRzQdDwKJ35EPPv8Ay2QxIQLjT2Jin1ifN5XS0z7WhGPaFRakfjy3l7tBeuN/SgMwnEwnmUYjGY0inzfTGc6N/BqIco8A8ZTiVQhmOVggWy+0LBaIId0Qfmc8Tko9glBC3hH5RyJR5ZS/ISpdmamCykKVhbEI7lIqWaCk/jeOvKEEznjSOKRR1KRlwUn8eTBI1kp3fwwGPkeoZwLbb8x/l55RNO5uxKlToVdv+R4u8RUY2ZMo3pB4kvywTjzN7csufKIRFXmLmzNZZSmUl8wixb4QdTzB71gdDE4iYd+PIz6k/H4n8RAaHlHYjsBly4DLpivcSYkwlshro13zyKe+AZxY8hgELXBfs1udSkREjKVYqVQJuQg9GgbPQrawN/RkqcLcMgH4U62ccPJBvfKI1Rrmi4K5Np9kYqSaxIPPKKu2XKJGYlnNFf48KLBXls8UsE13uoD4jHu+6OG3eO2cLyRSDWCAz4ENmwkMfrVorGkczwsFGGDGqNWOrtYGNiDQMfboMNfhBdfGrYP9IbNjXtaQEKOD4vb5KSxYcfB0z6+WAujjRYG/sN+VzyiuLnXJCaQlL4/mF5O/AIghM/udjgpYP3ZjDg+hm+EcXohiiq3m+EJ34KKnoIaRgVdUaZrI+pgw6K/eTIXWnTXt0HROe5snh1tZny11xLYsmR9OSjc1WBj3ebzvXsYBEUNt7rymwuM6wuQOk++o0zDTEhQmGOqhQSbUyG7nb5NZUpl1jnqz5hUunt0AIuC8bAhoL4/+plpFPg1HoUPHkEphSQxMXaJUv/Z3MZrV/omkV+bL9xjCH3fLxTgbYqYYdZP+Bua55CLeZzwdWALgB6YqaY9mD0XGcO5KcINU2yaYy05nLyc5oPJM9xMBVd1blojbD+w4dvpIdxd+1Sjb827Vh7XZjvfD+B44L9wtz161Lqwy2j3HpSm9TFxy0YKio5LS2dnqwyHhVeboiuCPI0XFoY/tQcP/7Fd/uKtHgz2bvWzmUZ5WPc2CvrBuFkMwqebt8PKcxuy/4WF9N/rmVp9HLQ2CrlQB/dMBvak0So8U0V/3U7hOyGvUcKbHCkvl4e4N2kI/OjnautbwcNfKHi2hFQuFKiU6Qr/O+uJ/Vvv/q2qcfJ8c/RY76mGHKnSILj7WWzW3yde/xsFqUVzXz2hrf+vWy11a9+nJbbtdeuJctV4FhzQ66MGxplmNHfHOnvRhi9ksIN9LW73UPhLOTAVJc8Yn78Wt/uYuqntNfEAs2f/FCdJhjc/cFz9sEyT5K+IyyS5Rlz2zC5RSWJPzpKktYGOAz8Owk1abGua4UxWp1wAMIDPwIqlkBruwUB5UJ08wQPMpCig88dlTu9W0sT62AzofJVwADDJMv+H8NnO62eoUsmm2O1snVN1POgewekruLfDR/DZ5sFM6Xa+8Liu4wFVdzyF7r0VFniorRC6Fcyn5dehGRLVhthc2kd9+7uuc9w5qjwcOw9PtgHdMQIFjqvKYC0AuTK1b4WwoDfmYWrKK6jcbHoDMqmBWObZqeqcL0sNp9X6j/WBmW8vlzlNcSHyDOVpQi7otV1UaOz3+wm5cmYO/G3zzmdg2hZgytabnH3CzAMt7x5/8dlZgx17Csp4uzmVHlujeqYrooyj9OAyo5r2TCJZdpqQzaCacY4lg+G2kX8plYY0Z+k1UH63WqBE0AtToky3Cjm7NvcIFg6mtLJ5tA3yrQSqVFlUQ2vRgsISaIqbCPTBIjS48RqVNtmbMZ7B9M4U35LmoLQsU9vLsBkshbKS7MG01FurMAWpKArB+xY72sZ+h7qjQGnjoxbWRLxlyjYDNvWQLjC9ruY6qg0aVHuueJspu8TalOL2RG73HV8l/JWteo0d8YjSb9SwGB6rlLeuQ7HtiODB7RyooTU/38MX91hQlQ/zP2hs8t2yYEYNd2JTE/nTHUe3hNp6EtQpx2bQ/c0G4Aj0QoqVlQHrixM+E15YUAVcwBoRpuLWbYkgbBglcpOheffH8y0HYvjt/WaxfvXlw48OJHJxGG/H4a24QWnophZiBTS1bxnswJMnErwws9YeTyrkYUNW3tjNmpD35o2AWFbAxOwJLTh0h3CDUrOU5pAJrY4sxGAb4u8KN6dOVjBNZJjZJmZ3GGozrliGcPH2/KOluYUJdqVKSHytOZzWnHgkeWVSvKCqq27m/bxMWYY9M6W3tvHI5WI43ImKg36C9GuWD0d7J1tlcwEdhi6gUXtAbUFLiIupHdlI6vk6QkOZAdNoWhV7rzAVPIO5FOUSSiNd+V0ldMOTHalcGWZyK7BWUgxAQW9ZYQorS93Su2qSG/u+tcbt6EnGbi6lyPE0IQXy0tiWkKud0LtVesFR56ifU6c6AKMGxy6MT+wTKqAS4WNB89yD95ixsvCqwt+HszyHUiG8d6CBtX0dkbSUErkG0+T8wS4xaFNlq7xgaHNWbRE7wSrMqKEs/0AVw+83q6U5Vcq8JDxNyKI3glVvBIXsBQmB41eVVud55UWF16h8Zzwzlt5ZFyXyDCWaBgiEzFDG7V5boEb1+yhq5GEVQd6dfbww+6ia4Ig32u12ajk9gOij8ZMAdbKPTtyakz2FrFxmriMKd0uOJZezx1WB7sD3/Qo7dNoftmi/0eCN7oe7us9xdajmhzuaX01+qd4DtedvtiVHJzjhXvH/8OiBEf5qxYboh070w4Y+nCmFsnK8vu2rNr67BXXU1+I1fmM7WHmxoLy7U2LgdxCuI77OoiNCZFK2vnbZiEw2jo+JOVExBCoVicmMsty+ud551719QnNPePVDxJ7F9DYnbvaK64u7pXlqvjwuqLzOxIo/vg8npv08nk1SH32fYkYxzQYTOk3pIIzCk2zgR3QcTVP0o+wk6hcZebgyVorrx0Ogh38BUEsDBBQAAAgIANWbOFxoIuFusgEAAEUDAAALAAAAcmVwb3J0Lmpzb26tks+OnDAMxl8F+ZyZDRTCkjeoVO2hWqmH1R68iYF0IEGJ0f4Z8e5VZuh0L+2p4mJi5/v8s3OGmRgtMoI+AxpecfoR4oliAl1uAhJj5Ec3E+iyVV3VdvUXVcpWgF0jsgsetKrq5thJJaB3EyXQT+dL9NWChkrZpsNeIjUlmrZuOqngWvmAWRZ8YDpESu7D+eGYFjJHTiCAKfFVLEd/FTu0UjVV01Ztb6mqe1RKynzd8ZTl0xjWyRbOm0iYqMh2xUhuGLl4HckXZkQ/OD8UyX1QwaH4hnEgELDE8JMM722aMYbZrTMImILZ0a+g/4CYnCfQtQATpnX2oLvt8+iaqpYC0PvAl5PM+yyAcdijsLIJF//V09tChsnm1pBH0E/wkGG+77aQ60+ge5wSCYiU1mmfIDKjGWfy+7+/MlGMIR5M8ExvDLlJz+T58X3J2Xx4N2M82fDqb6aQX8td3xlJUiJZJGPLDl8Mlo1q7m0pFbbqxZBU9l4dZwvbc/4ubynbn4ED4wS6FHBD0lJ8Jsy5fsLT+yWRTm5Z9qIb4JYlP60og/1Z0v+3E9dp/d7Lsq/rvG2/AFBLAQI/AxQAAAgIANWbOFwzz5hazwgAALsgAAAZAAAAAAAAAAAAAAC0gQAAAAAyNmQ1OWFmMGFlNTFhYzc0NTkwNi5qc29uUEsBAj8DFAAACAgA1Zs4XGgi4W6yAQAARQMAAAsAAAAAAAAAAAAAALSBBgkAAHJlcG9ydC5qc29uUEsFBgAAAAACAAIAgAAAAOEKAAAAAA==</script>
|
||||
@ -314,7 +314,6 @@ const config = {
|
||||
"db"
|
||||
],
|
||||
"activeProvider": "sqlite",
|
||||
"postinstall": false,
|
||||
"inlineDatasources": {
|
||||
"db": {
|
||||
"url": {
|
||||
|
||||
@ -315,7 +315,6 @@ const config = {
|
||||
"db"
|
||||
],
|
||||
"activeProvider": "sqlite",
|
||||
"postinstall": false,
|
||||
"inlineDatasources": {
|
||||
"db": {
|
||||
"url": {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,4 +1,6 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": []
|
||||
"failedTests": [
|
||||
"26d59af0ae51ac745906-706525727fde24fa6600"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- generic [active] [ref=e1]:
|
||||
- main [ref=e4]:
|
||||
- generic [ref=e7]:
|
||||
- heading "Sign in to your account" [level=1] [ref=e8]
|
||||
- generic [ref=e9]:
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]: Email
|
||||
- textbox "Email" [ref=e13]:
|
||||
- /placeholder: Enter your email address
|
||||
- generic [ref=e14]:
|
||||
- generic [ref=e15]: Password
|
||||
- textbox "Password" [ref=e17]:
|
||||
- /placeholder: Enter your password
|
||||
- link "Forgot password?" [ref=e19] [cursor=pointer]:
|
||||
- /url: /forgot-password
|
||||
- button "Sign In" [ref=e20]
|
||||
- region "Notifications alt+T"
|
||||
- button "Open Next.js Dev Tools" [ref=e27] [cursor=pointer]:
|
||||
- img [ref=e28]
|
||||
- alert [ref=e31]
|
||||
```
|
||||
104
keep-notes/tests/blue-color-test.spec.ts
Normal file
104
keep-notes/tests/blue-color-test.spec.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Test des couleurs bleues', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('http://localhost:3000');
|
||||
});
|
||||
|
||||
test('Vérifier les couleurs bleues dans la page', async ({ page }) => {
|
||||
// Attendre que la page charge
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Capturer tous les éléments avec des couleurs bleues via les classes
|
||||
const blueElements = await page.locator('*[class*="blue"]').all();
|
||||
|
||||
console.log('Nombre d\'éléments avec "blue" dans leur classe:', blueElements.length);
|
||||
|
||||
// Pour chaque élément avec une classe bleue, capturer les détails
|
||||
for (let i = 0; i < Math.min(blueElements.length, 20); i++) {
|
||||
const element = blueElements[i];
|
||||
const tagName = await element.evaluate(el => el.tagName);
|
||||
const className = await element.evaluate(el => el.className);
|
||||
const backgroundColor = await element.evaluate(el => window.getComputedStyle(el).backgroundColor);
|
||||
const color = await element.evaluate(el => window.getComputedStyle(el).color);
|
||||
const textContent = await element.evaluate(el => el.textContent?.substring(0, 50));
|
||||
|
||||
console.log(`Élément ${i + 1}:`, {
|
||||
tagName,
|
||||
className: className.substring(0, 100),
|
||||
backgroundColor,
|
||||
color,
|
||||
textContent
|
||||
});
|
||||
}
|
||||
|
||||
// Chercher aussi les styles inline bleus
|
||||
const elementsWithBlueStyle = await page.locator('*[style*="blue"]').all();
|
||||
console.log('Nombre d\'éléments avec style "blue":', elementsWithBlueStyle.length);
|
||||
|
||||
// Prendre une capture d'écran pour visualisation
|
||||
await page.screenshot({ path: 'blue-color-test.png', fullPage: true });
|
||||
|
||||
// Vérifier spécifiquement les boutons
|
||||
const buttons = await page.locator('button').all();
|
||||
console.log('Nombre total de boutons:', buttons.length);
|
||||
|
||||
for (let i = 0; i < Math.min(buttons.length, 10); i++) {
|
||||
const button = buttons[i];
|
||||
const backgroundColor = await button.evaluate(el => window.getComputedStyle(el).backgroundColor);
|
||||
const color = await button.evaluate(el => window.getComputedStyle(el).color);
|
||||
const textContent = await button.evaluate(el => el.textContent?.substring(0, 30));
|
||||
|
||||
if (backgroundColor.includes('blue') || backgroundColor.includes('rgb(37, 99, 235)') ||
|
||||
backgroundColor.includes('rgb(29, 78, 216)') || backgroundColor.includes('rgb(59, 130, 246)')) {
|
||||
console.log('Bouton bleu trouvé:', {
|
||||
backgroundColor,
|
||||
color,
|
||||
textContent
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('Vérifier les variables CSS du thème', async ({ page }) => {
|
||||
// Attendre que la page charge
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Récupérer les variables CSS du root
|
||||
const cssVars = await page.evaluate(() => {
|
||||
const rootStyle = getComputedStyle(document.documentElement);
|
||||
const variables: Record<string, string> = {};
|
||||
|
||||
// Variables de thème principales
|
||||
const varNames = [
|
||||
'--background',
|
||||
'--foreground',
|
||||
'--primary',
|
||||
'--primary-foreground',
|
||||
'--secondary',
|
||||
'--accent',
|
||||
'--border',
|
||||
'--ring'
|
||||
];
|
||||
|
||||
varNames.forEach(varName => {
|
||||
variables[varName] = rootStyle.getPropertyValue(varName).trim();
|
||||
});
|
||||
|
||||
return variables;
|
||||
});
|
||||
|
||||
console.log('Variables CSS du thème:', cssVars);
|
||||
|
||||
// Vérifier si les teintes contiennent des valeurs bleues (220-260)
|
||||
for (const [varName, value] of Object.entries(cssVars)) {
|
||||
const hueMatch = value.match(/(\d+)\s*\)/);
|
||||
if (hueMatch) {
|
||||
const hue = parseInt(hueMatch[1]);
|
||||
if (hue >= 220 && hue <= 260) {
|
||||
console.log(`⚠️ ${varName} contient une teinte bleue: ${value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
60
keep-notes/tests/note-resizing.spec.ts
Normal file
60
keep-notes/tests/note-resizing.spec.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Note Resizing', () => {
|
||||
test('should increase note height when changing size to Large', async ({ page }) => {
|
||||
// Go to home page
|
||||
await page.goto('/');
|
||||
|
||||
// Create a new note to ensure we have a clean slate
|
||||
const notesInput = page.locator('input[placeholder="Take a note..."]');
|
||||
// If text is localized, try selector
|
||||
const mainInput = page.locator('.note-input-container, [data-testid="note-input"]');
|
||||
|
||||
// Just click anywhere that looks like the input bar
|
||||
// Or assume the placeholder might be localized.
|
||||
// Best to find by visual structure if possible, but placeholder is common.
|
||||
// Let's stick to the existing notes check.
|
||||
|
||||
// Wait for at least one note card
|
||||
const firstNote = page.locator('.note-card').first();
|
||||
await firstNote.waitFor({ state: 'visible', timeout: 5000 });
|
||||
|
||||
// Get initial height
|
||||
const initialBox = await firstNote.boundingBox();
|
||||
if (!initialBox) throw new Error('Note card has no bounding box');
|
||||
console.log(`Initial height: ${initialBox.height}`);
|
||||
|
||||
// Hover to show actions
|
||||
await firstNote.hover();
|
||||
|
||||
// Click "More options" button (3 vertical dots)
|
||||
// Use selector ensuring it's the one inside THIS note
|
||||
const moreBtn = firstNote.locator('button:has(svg.lucide-more-vertical)');
|
||||
await moreBtn.waitFor({ state: 'visible' });
|
||||
await moreBtn.click();
|
||||
|
||||
// Click "Large" option
|
||||
// It's the 3rd item in the second group usually.
|
||||
// Or we can look for the maximize icon
|
||||
const largeOption = page.locator('div[role="menuitem"]:has(svg.lucide-maximize-2)').last();
|
||||
// The sizes are Small, Medium, Large. All use Maximize2 icon in the current code?
|
||||
// Let's check NoteActions code.
|
||||
// Yes: <Maximize2 className="h-4 w-4 mr-2" /> for all sizes.
|
||||
// And they are rendered in order: Small, Medium, Large.
|
||||
// So "Large" is the LAST one.
|
||||
|
||||
await largeOption.waitFor({ state: 'visible' });
|
||||
await largeOption.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Get new height
|
||||
const newBox = await firstNote.boundingBox();
|
||||
if (!newBox) throw new Error('Note card has no bounding box after resize');
|
||||
console.log(`New height: ${newBox.height}`);
|
||||
|
||||
// Assert
|
||||
expect(newBox.height).toBeGreaterThan(initialBox.height + 50);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user