Restaure et termine le lot tableur resté non committé. Il n'existait que dans un `git stash` (24 fichiers suivis) et en fichiers non suivis (menus.js, formula.js, color-picker.js, tests, fiches) : la 2.53.3 livrée ne le contenait donc pas. Le stash, créé avant le commit BUG-108, n'a jamais été restauré. #180 Redimensionnement au curseur (en-tête de bord miroité + classe de repaint), grille thémée sur les 15 thèmes et les modes contraste élevé / sépia, menu Fichier au niveau des onglets, barre épinglée pleine largeur. #181 Modèle de saisie Google Sheets : sélection ≠ édition (caret masqué sans quitter contenteditable, double-clic / F2 / première frappe qui remplace, Entrée contextuelle, Échap qui restaure) + inventaire priorisé des écarts. #182 Désélection fiable (clic simple sans dépendre du focus), contours de plage non empilés (sélecteur de classes sans point), couleurs texte/fond sur une plage, clic droit qui préserve la sélection multiple. #183 Panneau de couleurs façon Google Sheets : palette 8 × 10, STANDARD, PERSONNALISÉ, coche selon la luminance, sortie `#rrggbb` (une valeur HSL était rejetée par normHex). #184 Peinture de format complète (toutes propriétés, source sans format = réinitialisation), sélection multi-lignes/colonnes depuis les marges, grille étendue : colonnes A → Z d'emblée, lignes ajoutées PAR BLOCS DE 100 au défilement jusqu'à 1000 — matérialiser 1000 lignes d'un coup = ~26 000 cellules câblées par feuille, ce qui épuisait le tas de la suite JSDOM ; index de cellules `ref → td`, court-circuits formule/styles, marqueur data-wired. #185 Barre de menus : les 10 menus Google Sheets (161 entrées, 125 câblées, 36 annoncées indisponibles), ruban façon Sheets, grille unie 1 px dérivée du thème. #186 « Créer un fichier » propose .xlsx et construit un vrai classeur OPC (openpyxl) au lieu d'une charge utile texte illisible. Corrections trouvées en restaurant et en exerçant le lot : - fuite mémoire : écouteur `click` anonyme posé sur #content-area à chaque rendu, jamais retiré — sa fermeture retenait la grille précédente en entier ; - sorties Markdown / HTML / Imprimer de la barre de menus inertes (`data-xlsx-export` jamais réparti, seul `data-xlsx-action` l'était) ; - collision de classe `.xlsx-structure-menu` entre la barre de menus et le menu Structure de la barre d'outils (toute requête tombait sur un nœud masqué) ; - curseur col/row-resize absent quand le pointeur est sur la table elle-même ; - l'export emportait les lignes et colonnes vides du quadrillage (CSV, Markdown, HTML, impression) ; - clic extérieur avalé par la grâce de 250 ms du menu contextuel (destinée au seul appui long tactile) ; - une entrée indisponible laissait la barre de menus ouverte. Tests : pytest 1595 passés / 2 ignorés ; ruff et mypy 0 erreur ; bandit 0 ; 33 suites frontend vertes (xlsx-viewer 163/163, xlsx-menus 19/19, xlsx-formula 14/14) ; E2E complet 132 passés / 12 ignorés ; E2E xlsx-viewer 19/19.
774 lines
44 KiB
JavaScript
774 lines
44 KiB
JavaScript
/* ObsiGate — Theme System
|
||
* 15 themes x 2 modes (dark/light)
|
||
* Persisted in localStorage as 'obsigate-theme'
|
||
* Usage: import { initThemes, applyTheme, getCurrentTheme, THEMES } from './themes.js'
|
||
*/
|
||
|
||
// ── Theme definitions ────────────────────────────────────────────────
|
||
// Each theme has 'dark' and 'light' variants.
|
||
// CSS variables are applied to :root via setProperty.
|
||
|
||
var THEMES = {
|
||
|
||
'defaut-obsigate': {
|
||
name: 'Defaut ObsiGate',
|
||
desc: 'Theme original',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#0f1117', '--bg-secondary': '#161b22', '--bg-sidebar': '#13161d',
|
||
'--bg-hover': '#1f2430', '--border': '#21262d', '--text-primary': '#e6edf3',
|
||
'--text-secondary': '#8b949e', '--text-muted': '#484f58', '--accent': '#58a6ff',
|
||
'--accent-green': '#3fb950', '--tag-bg': '#1f2a3a', '--tag-text': '#79c0ff',
|
||
'--code-bg': '#161b22', '--search-bg': '#21262d', '--scrollbar': '#30363d',
|
||
'--resize-handle': '#30363d', '--overlay-bg': 'rgba(0,0,0,0.5)',
|
||
'--danger': '#ff7b72', '--danger-bg': '#3d1a18', '--success': '#3fb950', '--success-bg': '#1a3d1f',
|
||
'--accent-card': '#58a6ff', '--accent-bg': '#1f2a3a', '--accent-text': '#79c0ff',
|
||
'--green': '#3fb950', '--green-bg': '#1a3d1f', '--purple': '#a371f7', '--purple-bg': '#2d1b4e',
|
||
'--surface': '#161b22', '--surface2': '#0f1117', '--surface3': '#0d0f14',
|
||
'--border-md': '#30363d', '--text': '#e6edf3', '--text-2': '#8b949e', '--text-3': '#484f58',
|
||
'--ok': '#34d399', '--warn': '#fbbf24', '--ai': '#8b5cf6', '--err': '#f87171'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#ffffff', '--bg-secondary': '#f6f8fa', '--bg-sidebar': '#f0f2f5',
|
||
'--bg-hover': '#e8ecf0', '--border': '#d0d7de', '--text-primary': '#1f2328',
|
||
'--text-secondary': '#57606a', '--text-muted': '#9198a1', '--accent': '#0969da',
|
||
'--accent-green': '#1a7f37', '--tag-bg': '#ddf4ff', '--tag-text': '#0969da',
|
||
'--code-bg': '#f6f8fa', '--search-bg': '#ffffff', '--scrollbar': '#d0d7de',
|
||
'--resize-handle': '#d0d7de', '--overlay-bg': 'rgba(0,0,0,0.3)',
|
||
'--danger': '#cf222e', '--danger-bg': '#ffebe9', '--success': '#1a7f37', '--success-bg': '#dafbe1',
|
||
'--accent-card': '#0969da', '--accent-bg': '#ddf4ff', '--accent-text': '#0969da',
|
||
'--green': '#1a7f37', '--green-bg': '#dafbe1', '--purple': '#8250df', '--purple-bg': '#fbefff',
|
||
'--surface': '#f6f8fa', '--surface2': '#ffffff', '--surface3': '#f0f2f5',
|
||
'--border-md': '#d0d7de', '--text': '#1f2328', '--text-2': '#57606a', '--text-3': '#9198a1',
|
||
'--ok': '#1a7f37', '--warn': '#9a6700', '--ai': '#8250df', '--err': '#cf222e'
|
||
}
|
||
}
|
||
},
|
||
|
||
'github': {
|
||
name: 'GitHub', desc: 'Contraste doux',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#0d1117', '--bg-secondary': '#161b22', '--bg-sidebar': '#0d1117',
|
||
'--bg-hover': '#1c2128', '--border': '#30363d', '--text-primary': '#f0f6fc',
|
||
'--text-secondary': '#8b949e', '--text-muted': '#6e7681', '--accent': '#58a6ff',
|
||
'--accent-green': '#3fb950', '--tag-bg': '#13233a', '--tag-text': '#58a6ff',
|
||
'--code-bg': '#161b22', '--search-bg': '#0d1117', '--scrollbar': '#30363d',
|
||
'--resize-handle': '#30363d', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#f85149', '--danger-bg': '#490202', '--success': '#3fb950', '--success-bg': '#04260f',
|
||
'--accent-card': '#58a6ff', '--accent-bg': '#13233a', '--accent-text': '#58a6ff',
|
||
'--green': '#3fb950', '--green-bg': '#04260f', '--purple': '#a371f7', '--purple-bg': '#1c1130',
|
||
'--surface': '#161b22', '--surface2': '#0d1117', '--surface3': '#010409',
|
||
'--border-md': '#30363d', '--text': '#f0f6fc', '--text-2': '#8b949e', '--text-3': '#6e7681',
|
||
'--ok': '#3fb950', '--warn': '#d29922', '--ai': '#a371f7', '--err': '#f85149'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#ffffff', '--bg-secondary': '#f6f8fa', '--bg-sidebar': '#f6f8fa',
|
||
'--bg-hover': '#eaeef2', '--border': '#d0d7de', '--text-primary': '#1f2328',
|
||
'--text-secondary': '#656d76', '--text-muted': '#8c959f', '--accent': '#0969da',
|
||
'--accent-green': '#1a7f37', '--tag-bg': '#ddf4ff', '--tag-text': '#0969da',
|
||
'--code-bg': '#f6f8fa', '--search-bg': '#ffffff', '--scrollbar': '#d0d7de',
|
||
'--resize-handle': '#d0d7de', '--overlay-bg': 'rgba(0,0,0,0.25)',
|
||
'--danger': '#cf222e', '--danger-bg': '#ffebe9', '--success': '#1a7f37', '--success-bg': '#dafbe1',
|
||
'--accent-card': '#0969da', '--accent-bg': '#ddf4ff', '--accent-text': '#0969da',
|
||
'--green': '#1a7f37', '--green-bg': '#dafbe1', '--purple': '#8250df', '--purple-bg': '#fbefff',
|
||
'--surface': '#f6f8fa', '--surface2': '#ffffff', '--surface3': '#ebedf0',
|
||
'--border-md': '#d0d7de', '--text': '#1f2328', '--text-2': '#656d76', '--text-3': '#8c959f',
|
||
'--ok': '#1a7f37', '--warn': '#9a6700', '--ai': '#8250df', '--err': '#cf222e'
|
||
}
|
||
}
|
||
},
|
||
|
||
'linear': {
|
||
name: 'Linear', desc: 'Dégradés subtils',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#1a1a2e', '--bg-secondary': '#16162a', '--bg-sidebar': '#12121f',
|
||
'--bg-hover': '#222240', '--border': '#2a2a45', '--text-primary': '#e1e1f5',
|
||
'--text-secondary': '#8888aa', '--text-muted': '#555577', '--accent': '#5e6ad2',
|
||
'--accent-green': '#2ecc71', '--tag-bg': '#222050', '--tag-text': '#8b95e0',
|
||
'--code-bg': '#16162a', '--search-bg': '#1e1e38', '--scrollbar': '#2a2a45',
|
||
'--resize-handle': '#2a2a45', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#e0556a', '--danger-bg': '#361520', '--success': '#2ecc71', '--success-bg': '#0d2818',
|
||
'--accent-card': '#5e6ad2', '--accent-bg': '#222050', '--accent-text': '#8b95e0',
|
||
'--green': '#2ecc71', '--green-bg': '#0d2818', '--purple': '#9b6bff', '--purple-bg': '#25183d',
|
||
'--surface': '#16162a', '--surface2': '#1a1a2e', '--surface3': '#12121f',
|
||
'--border-md': '#2a2a45', '--text': '#e1e1f5', '--text-2': '#8888aa', '--text-3': '#555577',
|
||
'--ok': '#2ecc71', '--warn': '#f0b429', '--ai': '#9b6bff', '--err': '#e0556a'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#fafafa', '--bg-secondary': '#f0f0f5', '--bg-sidebar': '#e8e8f0',
|
||
'--bg-hover': '#e0e0ea', '--border': '#d5d5e0', '--text-primary': '#1a1a2e',
|
||
'--text-secondary': '#5c5c7a', '--text-muted': '#9090a8', '--accent': '#5e6ad2',
|
||
'--accent-green': '#1a8b4a', '--tag-bg': '#e8e8ff', '--tag-text': '#5e6ad2',
|
||
'--code-bg': '#f0f0f5', '--search-bg': '#fafafa', '--scrollbar': '#d5d5e0',
|
||
'--resize-handle': '#d5d5e0', '--overlay-bg': 'rgba(0,0,0,0.2)',
|
||
'--danger': '#c92a3e', '--danger-bg': '#ffeaea', '--success': '#1a8b4a', '--success-bg': '#e0ffe0',
|
||
'--accent-card': '#5e6ad2', '--accent-bg': '#e8e8ff', '--accent-text': '#5e6ad2',
|
||
'--green': '#1a8b4a', '--green-bg': '#e0ffe0', '--purple': '#7c5ce0', '--purple-bg': '#efe8ff',
|
||
'--surface': '#f0f0f5', '--surface2': '#fafafa', '--surface3': '#e8e8f0',
|
||
'--border-md': '#d5d5e0', '--text': '#1a1a2e', '--text-2': '#5c5c7a', '--text-3': '#9090a8',
|
||
'--ok': '#1a8b4a', '--warn': '#9a6700', '--ai': '#7c5ce0', '--err': '#c92a3e'
|
||
}
|
||
}
|
||
},
|
||
|
||
'spotify': {
|
||
name: 'Spotify', desc: 'Noir pur & vert',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#000000', '--bg-secondary': '#121212', '--bg-sidebar': '#000000',
|
||
'--bg-hover': '#1a1a1a', '--border': '#282828', '--text-primary': '#ffffff',
|
||
'--text-secondary': '#b3b3b3', '--text-muted': '#727272', '--accent': '#1db954',
|
||
'--accent-green': '#1db954', '--tag-bg': '#0d3317', '--tag-text': '#1db954',
|
||
'--code-bg': '#121212', '--search-bg': '#242424', '--scrollbar': '#333333',
|
||
'--resize-handle': '#282828', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#e22134', '--danger-bg': '#2a0a0e', '--success': '#1db954', '--success-bg': '#0d3317',
|
||
'--accent-card': '#1db954', '--accent-bg': '#0d3317', '--accent-text': '#1db954',
|
||
'--green': '#1db954', '--green-bg': '#0d3317', '--purple': '#af2896', '--purple-bg': '#2a0d33',
|
||
'--surface': '#121212', '--surface2': '#000000', '--surface3': '#000000',
|
||
'--border-md': '#282828', '--text': '#ffffff', '--text-2': '#b3b3b3', '--text-3': '#727272',
|
||
'--ok': '#1db954', '--warn': '#f59b23', '--ai': '#af2896', '--err': '#e22134'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#ffffff', '--bg-secondary': '#f5f5f5', '--bg-sidebar': '#f0f0f0',
|
||
'--bg-hover': '#e5e5e5', '--border': '#d5d5d5', '--text-primary': '#121212',
|
||
'--text-secondary': '#575757', '--text-muted': '#8c8c8c', '--accent': '#1db954',
|
||
'--accent-green': '#1db954', '--tag-bg': '#e0ffe0', '--tag-text': '#128a3c',
|
||
'--code-bg': '#f5f5f5', '--search-bg': '#ffffff', '--scrollbar': '#d5d5d5',
|
||
'--resize-handle': '#d5d5d5', '--overlay-bg': 'rgba(0,0,0,0.2)',
|
||
'--danger': '#c92a2a', '--danger-bg': '#ffe3e3', '--success': '#1db954', '--success-bg': '#e0ffe0',
|
||
'--accent-card': '#1db954', '--accent-bg': '#e0ffe0', '--accent-text': '#128a3c',
|
||
'--green': '#1db954', '--green-bg': '#e0ffe0', '--purple': '#8a2be2', '--purple-bg': '#f3e0ff',
|
||
'--surface': '#f5f5f5', '--surface2': '#ffffff', '--surface3': '#f0f0f0',
|
||
'--border-md': '#d5d5d5', '--text': '#121212', '--text-2': '#575757', '--text-3': '#8c8c8c',
|
||
'--ok': '#1db954', '--warn': '#9a6700', '--ai': '#8a2be2', '--err': '#c92a2a'
|
||
}
|
||
}
|
||
},
|
||
|
||
'notion': {
|
||
name: 'Notion', desc: 'Lecture propre',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#191919', '--bg-secondary': '#202020', '--bg-sidebar': '#191919',
|
||
'--bg-hover': '#262626', '--border': '#333333', '--text-primary': '#e6e6e6',
|
||
'--text-secondary': '#999999', '--text-muted': '#666666', '--accent': '#4b85e8',
|
||
'--accent-green': '#0ca16a', '--tag-bg': '#1c2744', '--tag-text': '#6ba1f0',
|
||
'--code-bg': '#202020', '--search-bg': '#262626', '--scrollbar': '#333333',
|
||
'--resize-handle': '#333333', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#e03e3e', '--danger-bg': '#2a1010', '--success': '#0ca16a', '--success-bg': '#0d2818',
|
||
'--accent-card': '#4b85e8', '--accent-bg': '#1c2744', '--accent-text': '#6ba1f0',
|
||
'--green': '#0ca16a', '--green-bg': '#0d2818', '--purple': '#9065e0', '--purple-bg': '#24183d',
|
||
'--surface': '#202020', '--surface2': '#191919', '--surface3': '#141414',
|
||
'--border-md': '#333333', '--text': '#e6e6e6', '--text-2': '#999999', '--text-3': '#666666',
|
||
'--ok': '#0ca16a', '--warn': '#dfab3e', '--ai': '#9065e0', '--err': '#e03e3e'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#ffffff', '--bg-secondary': '#f7f6f3', '--bg-sidebar': '#f7f6f3',
|
||
'--bg-hover': '#edece9', '--border': '#e3e2e0', '--text-primary': '#37352f',
|
||
'--text-secondary': '#787774', '--text-muted': '#abaaa7', '--accent': '#4b85e8',
|
||
'--accent-green': '#0ca16a', '--tag-bg': '#e8f0fe', '--tag-text': '#4b85e8',
|
||
'--code-bg': '#f7f6f3', '--search-bg': '#ffffff', '--scrollbar': '#e3e2e0',
|
||
'--resize-handle': '#e3e2e0', '--overlay-bg': 'rgba(0,0,0,0.15)',
|
||
'--danger': '#e03e3e', '--danger-bg': '#fdebec', '--success': '#0ca16a', '--success-bg': '#e6f7ec',
|
||
'--accent-card': '#4b85e8', '--accent-bg': '#e8f0fe', '--accent-text': '#4b85e8',
|
||
'--green': '#0ca16a', '--green-bg': '#e6f7ec', '--purple': '#9065e0', '--purple-bg': '#f3effc',
|
||
'--surface': '#f7f6f3', '--surface2': '#ffffff', '--surface3': '#edece9',
|
||
'--border-md': '#e3e2e0', '--text': '#37352f', '--text-2': '#787774', '--text-3': '#abaaa7',
|
||
'--ok': '#0ca16a', '--warn': '#dfab3e', '--ai': '#9065e0', '--err': '#e03e3e'
|
||
}
|
||
}
|
||
},
|
||
|
||
'discord': {
|
||
name: 'Discord', desc: 'Standard communautaire',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#313338', '--bg-secondary': '#2b2d31', '--bg-sidebar': '#1e1f22',
|
||
'--bg-hover': '#3c3e45', '--border': '#3f4147', '--text-primary': '#f2f3f5',
|
||
'--text-secondary': '#b5bac1', '--text-muted': '#80848e', '--accent': '#5865f2',
|
||
'--accent-green': '#23a559', '--tag-bg': '#28315c', '--tag-text': '#8891f2',
|
||
'--code-bg': '#2b2d31', '--search-bg': '#383a40', '--scrollbar': '#1a1b1e',
|
||
'--resize-handle': '#3f4147', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#da373c', '--danger-bg': '#3a1518', '--success': '#23a559', '--success-bg': '#122b1a',
|
||
'--accent-card': '#5865f2', '--accent-bg': '#28315c', '--accent-text': '#8891f2',
|
||
'--green': '#23a559', '--green-bg': '#122b1a', '--purple': '#9c5bf0', '--purple-bg': '#2c1a46',
|
||
'--surface': '#2b2d31', '--surface2': '#313338', '--surface3': '#1e1f22',
|
||
'--border-md': '#3f4147', '--text': '#f2f3f5', '--text-2': '#b5bac1', '--text-3': '#80848e',
|
||
'--ok': '#23a559', '--warn': '#f0b232', '--ai': '#9c5bf0', '--err': '#da373c'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#ffffff', '--bg-secondary': '#f2f3f5', '--bg-sidebar': '#e3e5e8',
|
||
'--bg-hover': '#e3e5e8', '--border': '#d4d7dc', '--text-primary': '#313338',
|
||
'--text-secondary': '#5c5e66', '--text-muted': '#91949e', '--accent': '#5865f2',
|
||
'--accent-green': '#248046', '--tag-bg': '#e8eafe', '--tag-text': '#4752c4',
|
||
'--code-bg': '#f2f3f5', '--search-bg': '#ffffff', '--scrollbar': '#d4d7dc',
|
||
'--resize-handle': '#d4d7dc', '--overlay-bg': 'rgba(0,0,0,0.2)',
|
||
'--danger': '#d92028', '--danger-bg': '#fde8e8', '--success': '#248046', '--success-bg': '#e6f7e8',
|
||
'--accent-card': '#5865f2', '--accent-bg': '#e8eafe', '--accent-text': '#4752c4',
|
||
'--green': '#248046', '--green-bg': '#e6f7e8', '--purple': '#8b5cf6', '--purple-bg': '#f0ebff',
|
||
'--surface': '#f2f3f5', '--surface2': '#ffffff', '--surface3': '#e3e5e8',
|
||
'--border-md': '#d4d7dc', '--text': '#313338', '--text-2': '#5c5e66', '--text-3': '#91949e',
|
||
'--ok': '#248046', '--warn': '#9a6700', '--ai': '#8b5cf6', '--err': '#d92028'
|
||
}
|
||
}
|
||
},
|
||
|
||
'dracula': {
|
||
name: 'Dracula', desc: 'Accents rose & violet',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#282a36', '--bg-secondary': '#21222c', '--bg-sidebar': '#1d1e26',
|
||
'--bg-hover': '#343746', '--border': '#3b3d54', '--text-primary': '#f8f8f2',
|
||
'--text-secondary': '#b2b2b2', '--text-muted': '#6272a4', '--accent': '#bd93f9',
|
||
'--accent-green': '#50fa7b', '--tag-bg': '#2a2240', '--tag-text': '#bd93f9',
|
||
'--code-bg': '#21222c', '--search-bg': '#343746', '--scrollbar': '#44475a',
|
||
'--resize-handle': '#3b3d54', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#ff5555', '--danger-bg': '#3a1a1a', '--success': '#50fa7b', '--success-bg': '#0d2818',
|
||
'--accent-card': '#bd93f9', '--accent-bg': '#2a2240', '--accent-text': '#bd93f9',
|
||
'--green': '#50fa7b', '--green-bg': '#0d2818', '--purple': '#bd93f9', '--purple-bg': '#2a2240',
|
||
'--surface': '#21222c', '--surface2': '#282a36', '--surface3': '#1d1e26',
|
||
'--border-md': '#3b3d54', '--text': '#f8f8f2', '--text-2': '#b2b2b2', '--text-3': '#6272a4',
|
||
'--ok': '#50fa7b', '--warn': '#f1fa8c', '--ai': '#bd93f9', '--err': '#ff5555'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#f8f8f2', '--bg-secondary': '#f0f0ea', '--bg-sidebar': '#e8e8e0',
|
||
'--bg-hover': '#e0e0d8', '--border': '#d5d5cc', '--text-primary': '#282a36',
|
||
'--text-secondary': '#4d5066', '--text-muted': '#8b8fa8', '--accent': '#8a5cf6',
|
||
'--accent-green': '#3d8f54', '--tag-bg': '#ede0ff', '--tag-text': '#8a5cf6',
|
||
'--code-bg': '#f0f0ea', '--search-bg': '#f8f8f2', '--scrollbar': '#d5d5cc',
|
||
'--resize-handle': '#d5d5cc', '--overlay-bg': 'rgba(0,0,0,0.15)',
|
||
'--danger': '#d7373f', '--danger-bg': '#fde8e8', '--success': '#3d8f54', '--success-bg': '#e6f7e8',
|
||
'--accent-card': '#8a5cf6', '--accent-bg': '#ede0ff', '--accent-text': '#8a5cf6',
|
||
'--green': '#3d8f54', '--green-bg': '#e6f7e8', '--purple': '#8a5cf6', '--purple-bg': '#ede0ff',
|
||
'--surface': '#f0f0ea', '--surface2': '#f8f8f2', '--surface3': '#e8e8e0',
|
||
'--border-md': '#d5d5cc', '--text': '#282a36', '--text-2': '#4d5066', '--text-3': '#8b8fa8',
|
||
'--ok': '#3d8f54', '--warn': '#8b7500', '--ai': '#8a5cf6', '--err': '#d7373f'
|
||
}
|
||
}
|
||
},
|
||
|
||
'one-dark': {
|
||
name: 'One Dark', desc: 'Inspire d\'Atom',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#282c34', '--bg-secondary': '#21252b', '--bg-sidebar': '#1c1f26',
|
||
'--bg-hover': '#333842', '--border': '#3e4452', '--text-primary': '#abb2bf',
|
||
'--text-secondary': '#828997', '--text-muted': '#5c6370', '--accent': '#61afef',
|
||
'--accent-green': '#98c379', '--tag-bg': '#1e3350', '--tag-text': '#61afef',
|
||
'--code-bg': '#21252b', '--search-bg': '#333842', '--scrollbar': '#3e4452',
|
||
'--resize-handle': '#3e4452', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#e06c75', '--danger-bg': '#351b1e', '--success': '#98c379', '--success-bg': '#1a2e1a',
|
||
'--accent-card': '#61afef', '--accent-bg': '#1e3350', '--accent-text': '#61afef',
|
||
'--green': '#98c379', '--green-bg': '#1a2e1a', '--purple': '#c678dd', '--purple-bg': '#2c1a3a',
|
||
'--surface': '#21252b', '--surface2': '#282c34', '--surface3': '#1c1f26',
|
||
'--border-md': '#3e4452', '--text': '#abb2bf', '--text-2': '#828997', '--text-3': '#5c6370',
|
||
'--ok': '#98c379', '--warn': '#e5c07b', '--ai': '#c678dd', '--err': '#e06c75'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#fafafa', '--bg-secondary': '#f0f0f0', '--bg-sidebar': '#e8e8e8',
|
||
'--bg-hover': '#e0e0e0', '--border': '#d4d4d4', '--text-primary': '#383a42',
|
||
'--text-secondary': '#696c77', '--text-muted': '#9ca0a4', '--accent': '#4078f2',
|
||
'--accent-green': '#50a14f', '--tag-bg': '#e0e8ff', '--tag-text': '#4078f2',
|
||
'--code-bg': '#f0f0f0', '--search-bg': '#fafafa', '--scrollbar': '#d4d4d4',
|
||
'--resize-handle': '#d4d4d4', '--overlay-bg': 'rgba(0,0,0,0.15)',
|
||
'--danger': '#e45649', '--danger-bg': '#fde8e8', '--success': '#50a14f', '--success-bg': '#e6f7e6',
|
||
'--accent-card': '#4078f2', '--accent-bg': '#e0e8ff', '--accent-text': '#4078f2',
|
||
'--green': '#50a14f', '--green-bg': '#e6f7e6', '--purple': '#a626a4', '--purple-bg': '#f8e0f8',
|
||
'--surface': '#f0f0f0', '--surface2': '#fafafa', '--surface3': '#e8e8e8',
|
||
'--border-md': '#d4d4d4', '--text': '#383a42', '--text-2': '#696c77', '--text-3': '#9ca0a4',
|
||
'--ok': '#50a14f', '--warn': '#986801', '--ai': '#a626a4', '--err': '#e45649'
|
||
}
|
||
}
|
||
},
|
||
|
||
'tokyo-night': {
|
||
name: 'Tokyo Night', desc: 'Bleu neon',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#1a1b26', '--bg-secondary': '#16161e', '--bg-sidebar': '#13131a',
|
||
'--bg-hover': '#24283b', '--border': '#2f3545', '--text-primary': '#c0caf5',
|
||
'--text-secondary': '#a9b1d6', '--text-muted': '#565f89', '--accent': '#7aa2f7',
|
||
'--accent-green': '#9ece6a', '--tag-bg': '#1f2845', '--tag-text': '#7aa2f7',
|
||
'--code-bg': '#16161e', '--search-bg': '#24283b', '--scrollbar': '#2f3545',
|
||
'--resize-handle': '#2f3545', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#f7768e', '--danger-bg': '#361a22', '--success': '#9ece6a', '--success-bg': '#1a2e1a',
|
||
'--accent-card': '#7aa2f7', '--accent-bg': '#1f2845', '--accent-text': '#7aa2f7',
|
||
'--green': '#9ece6a', '--green-bg': '#1a2e1a', '--purple': '#bb9af7', '--purple-bg': '#241a3a',
|
||
'--surface': '#16161e', '--surface2': '#1a1b26', '--surface3': '#13131a',
|
||
'--border-md': '#2f3545', '--text': '#c0caf5', '--text-2': '#a9b1d6', '--text-3': '#565f89',
|
||
'--ok': '#9ece6a', '--warn': '#e0af68', '--ai': '#bb9af7', '--err': '#f7768e'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#d5d6db', '--bg-secondary': '#c8c9d0', '--bg-sidebar': '#bfc0c8',
|
||
'--bg-hover': '#bbbbc4', '--border': '#a9aab3', '--text-primary': '#343b58',
|
||
'--text-secondary': '#565a6e', '--text-muted': '#848cb0', '--accent': '#34548a',
|
||
'--accent-green': '#485e30', '--tag-bg': '#dfe5f5', '--tag-text': '#34548a',
|
||
'--code-bg': '#c8c9d0', '--search-bg': '#d5d6db', '--scrollbar': '#a9aab3',
|
||
'--resize-handle': '#a9aab3', '--overlay-bg': 'rgba(0,0,0,0.15)',
|
||
'--danger': '#8c4351', '--danger-bg': '#f8e0e0', '--success': '#485e30', '--success-bg': '#e0f0d8',
|
||
'--accent-card': '#34548a', '--accent-bg': '#dfe5f5', '--accent-text': '#34548a',
|
||
'--green': '#485e30', '--green-bg': '#e0f0d8', '--purple': '#5a4a9a', '--purple-bg': '#e8e0f8',
|
||
'--surface': '#c8c9d0', '--surface2': '#d5d6db', '--surface3': '#bfc0c8',
|
||
'--border-md': '#a9aab3', '--text': '#343b58', '--text-2': '#565a6e', '--text-3': '#848cb0',
|
||
'--ok': '#485e30', '--warn': '#8f5e15', '--ai': '#5a4a9a', '--err': '#8c4351'
|
||
}
|
||
}
|
||
},
|
||
|
||
'nord': {
|
||
name: 'Nord', desc: 'Style arctique',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#2e3440', '--bg-secondary': '#3b4252', '--bg-sidebar': '#242933',
|
||
'--bg-hover': '#434c5e', '--border': '#4c566a', '--text-primary': '#eceff4',
|
||
'--text-secondary': '#d8dee9', '--text-muted': '#7b88a1', '--accent': '#88c0d0',
|
||
'--accent-green': '#a3be8c', '--tag-bg': '#2a3a48', '--tag-text': '#88c0d0',
|
||
'--code-bg': '#3b4252', '--search-bg': '#434c5e', '--scrollbar': '#4c566a',
|
||
'--resize-handle': '#4c566a', '--overlay-bg': 'rgba(0,0,0,0.6)',
|
||
'--danger': '#bf616a', '--danger-bg': '#3a2025', '--success': '#a3be8c', '--success-bg': '#1a2e1a',
|
||
'--accent-card': '#88c0d0', '--accent-bg': '#2a3a48', '--accent-text': '#88c0d0',
|
||
'--green': '#a3be8c', '--green-bg': '#1a2e1a', '--purple': '#b48ead', '--purple-bg': '#2a2030',
|
||
'--surface': '#3b4252', '--surface2': '#2e3440', '--surface3': '#242933',
|
||
'--border-md': '#4c566a', '--text': '#eceff4', '--text-2': '#d8dee9', '--text-3': '#7b88a1',
|
||
'--ok': '#a3be8c', '--warn': '#ebcb8b', '--ai': '#b48ead', '--err': '#bf616a'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#eceff4', '--bg-secondary': '#e5e9f0', '--bg-sidebar': '#d8dee9',
|
||
'--bg-hover': '#d8dee9', '--border': '#c8cfdb', '--text-primary': '#2e3440',
|
||
'--text-secondary': '#4c566a', '--text-muted': '#7b88a1', '--accent': '#5e81ac',
|
||
'--accent-green': '#719350', '--tag-bg': '#dfe8f0', '--tag-text': '#5e81ac',
|
||
'--code-bg': '#e5e9f0', '--search-bg': '#eceff4', '--scrollbar': '#c8cfdb',
|
||
'--resize-handle': '#c8cfdb', '--overlay-bg': 'rgba(0,0,0,0.12)',
|
||
'--danger': '#bf616a', '--danger-bg': '#f8e0e0', '--success': '#719350', '--success-bg': '#e0f0d8',
|
||
'--accent-card': '#5e81ac', '--accent-bg': '#dfe8f0', '--accent-text': '#5e81ac',
|
||
'--green': '#719350', '--green-bg': '#e0f0d8', '--purple': '#7b5e95', '--purple-bg': '#e8dff0',
|
||
'--surface': '#e5e9f0', '--surface2': '#eceff4', '--surface3': '#d8dee9',
|
||
'--border-md': '#c8cfdb', '--text': '#2e3440', '--text-2': '#4c566a', '--text-3': '#7b88a1',
|
||
'--ok': '#719350', '--warn': '#8f6b20', '--ai': '#7b5e95', '--err': '#bf616a'
|
||
}
|
||
}
|
||
},
|
||
|
||
'night-owl': {
|
||
name: 'Night Owl', desc: 'Optimise pour la nuit',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#011627', '--bg-secondary': '#01111d', '--bg-sidebar': '#010d18',
|
||
'--bg-hover': '#0b2942', '--border': '#1d3b53', '--text-primary': '#d6deeb',
|
||
'--text-secondary': '#7e97b0', '--text-muted': '#4b6479', '--accent': '#82aaff',
|
||
'--accent-green': '#c3e88d', '--tag-bg': '#152d45', '--tag-text': '#82aaff',
|
||
'--code-bg': '#01111d', '--search-bg': '#0b2942', '--scrollbar': '#1d3b53',
|
||
'--resize-handle': '#1d3b53', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#ef5350', '--danger-bg': '#2a1018', '--success': '#c3e88d', '--success-bg': '#0d2818',
|
||
'--accent-card': '#82aaff', '--accent-bg': '#152d45', '--accent-text': '#82aaff',
|
||
'--green': '#c3e88d', '--green-bg': '#0d2818', '--purple': '#c792ea', '--purple-bg': '#201838',
|
||
'--surface': '#01111d', '--surface2': '#011627', '--surface3': '#010d18',
|
||
'--border-md': '#1d3b53', '--text': '#d6deeb', '--text-2': '#7e97b0', '--text-3': '#4b6479',
|
||
'--ok': '#c3e88d', '--warn': '#ffcb8b', '--ai': '#c792ea', '--err': '#ef5350'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#fbfbfb', '--bg-secondary': '#f0f0f0', '--bg-sidebar': '#e8e8e8',
|
||
'--bg-hover': '#e0e0e0', '--border': '#d4d4d4', '--text-primary': '#403f53',
|
||
'--text-secondary': '#5f6c7a', '--text-muted': '#8e99a8', '--accent': '#4876d6',
|
||
'--accent-green': '#2d8a4e', '--tag-bg': '#e0e8ff', '--tag-text': '#4876d6',
|
||
'--code-bg': '#f0f0f0', '--search-bg': '#fbfbfb', '--scrollbar': '#d4d4d4',
|
||
'--resize-handle': '#d4d4d4', '--overlay-bg': 'rgba(0,0,0,0.12)',
|
||
'--danger': '#c92a2a', '--danger-bg': '#ffe3e3', '--success': '#2d8a4e', '--success-bg': '#e0ffe0',
|
||
'--accent-card': '#4876d6', '--accent-bg': '#e0e8ff', '--accent-text': '#4876d6',
|
||
'--green': '#2d8a4e', '--green-bg': '#e0ffe0', '--purple': '#7c3aed', '--purple-bg': '#ede0ff',
|
||
'--surface': '#f0f0f0', '--surface2': '#fbfbfb', '--surface3': '#e8e8e8',
|
||
'--border-md': '#d4d4d4', '--text': '#403f53', '--text-2': '#5f6c7a', '--text-3': '#8e99a8',
|
||
'--ok': '#2d8a4e', '--warn': '#8f6b20', '--ai': '#7c3aed', '--err': '#c92a2a'
|
||
}
|
||
}
|
||
},
|
||
|
||
'anthracite': {
|
||
name: 'Anthracite', desc: 'Material dark',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#1e1e1e', '--bg-secondary': '#252526', '--bg-sidebar': '#1a1a1a',
|
||
'--bg-hover': '#2d2d2d', '--border': '#3e3e3e', '--text-primary': '#d4d4d4',
|
||
'--text-secondary': '#9d9d9d', '--text-muted': '#6e6e6e', '--accent': '#9c7aef',
|
||
'--accent-green': '#6a9955', '--tag-bg': '#2a2250', '--tag-text': '#9c7aef',
|
||
'--code-bg': '#252526', '--search-bg': '#2d2d2d', '--scrollbar': '#3e3e3e',
|
||
'--resize-handle': '#3e3e3e', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#f44747', '--danger-bg': '#3a1a1a', '--success': '#6a9955', '--success-bg': '#1a2e1a',
|
||
'--accent-card': '#9c7aef', '--accent-bg': '#2a2250', '--accent-text': '#9c7aef',
|
||
'--green': '#6a9955', '--green-bg': '#1a2e1a', '--purple': '#9c7aef', '--purple-bg': '#2a2250',
|
||
'--surface': '#252526', '--surface2': '#1e1e1e', '--surface3': '#1a1a1a',
|
||
'--border-md': '#3e3e3e', '--text': '#d4d4d4', '--text-2': '#9d9d9d', '--text-3': '#6e6e6e',
|
||
'--ok': '#6a9955', '--warn': '#d7ba7d', '--ai': '#9c7aef', '--err': '#f44747'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#fafafa', '--bg-secondary': '#f5f5f5', '--bg-sidebar': '#eeeeee',
|
||
'--bg-hover': '#e5e5e5', '--border': '#d5d5d5', '--text-primary': '#212121',
|
||
'--text-secondary': '#616161', '--text-muted': '#9e9e9e', '--accent': '#7c3aed',
|
||
'--accent-green': '#388e3c', '--tag-bg': '#ede0ff', '--tag-text': '#7c3aed',
|
||
'--code-bg': '#f5f5f5', '--search-bg': '#fafafa', '--scrollbar': '#d5d5d5',
|
||
'--resize-handle': '#d5d5d5', '--overlay-bg': 'rgba(0,0,0,0.12)',
|
||
'--danger': '#d32f2f', '--danger-bg': '#fde8e8', '--success': '#388e3c', '--success-bg': '#e8f5e9',
|
||
'--accent-card': '#7c3aed', '--accent-bg': '#ede0ff', '--accent-text': '#7c3aed',
|
||
'--green': '#388e3c', '--green-bg': '#e8f5e9', '--purple': '#7c3aed', '--purple-bg': '#ede0ff',
|
||
'--surface': '#f5f5f5', '--surface2': '#fafafa', '--surface3': '#eeeeee',
|
||
'--border-md': '#d5d5d5', '--text': '#212121', '--text-2': '#616161', '--text-3': '#9e9e9e',
|
||
'--ok': '#388e3c', '--warn': '#8f6b20', '--ai': '#7c3aed', '--err': '#d32f2f'
|
||
}
|
||
}
|
||
},
|
||
|
||
'cyberpunk': {
|
||
name: 'Cyberpunk', desc: 'Neon vibrant',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#0a0a0f', '--bg-secondary': '#111122', '--bg-sidebar': '#08080d',
|
||
'--bg-hover': '#1a1a33', '--border': '#2a2a55', '--text-primary': '#e0e0ff',
|
||
'--text-secondary': '#8888cc', '--text-muted': '#555599', '--accent': '#00ffff',
|
||
'--accent-green': '#00ff88', '--tag-bg': '#0a2a3a', '--tag-text': '#00ffff',
|
||
'--code-bg': '#111122', '--search-bg': '#1a1a33', '--scrollbar': '#2a2a55',
|
||
'--resize-handle': '#2a2a55', '--overlay-bg': 'rgba(0,0,0,0.8)',
|
||
'--danger': '#ff3366', '--danger-bg': '#331122', '--success': '#00ff88', '--success-bg': '#003322',
|
||
'--accent-card': '#00ffff', '--accent-bg': '#0a2a3a', '--accent-text': '#00ffff',
|
||
'--green': '#00ff88', '--green-bg': '#003322', '--purple': '#ff00ff', '--purple-bg': '#220033',
|
||
'--surface': '#111122', '--surface2': '#0a0a0f', '--surface3': '#08080d',
|
||
'--border-md': '#2a2a55', '--text': '#e0e0ff', '--text-2': '#8888cc', '--text-3': '#555599',
|
||
'--ok': '#00ff88', '--warn': '#ffaa00', '--ai': '#ff00ff', '--err': '#ff3366'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#f0f0ff', '--bg-secondary': '#e8e8ff', '--bg-sidebar': '#e0e0f8',
|
||
'--bg-hover': '#d8d8f0', '--border': '#c0c0e0', '--text-primary': '#1a1a33',
|
||
'--text-secondary': '#4a4a6a', '--text-muted': '#8080a0', '--accent': '#0077cc',
|
||
'--accent-green': '#008844', '--tag-bg': '#ddeeff', '--tag-text': '#0077cc',
|
||
'--code-bg': '#e8e8ff', '--search-bg': '#f0f0ff', '--scrollbar': '#c0c0e0',
|
||
'--resize-handle': '#c0c0e0', '--overlay-bg': 'rgba(0,0,0,0.1)',
|
||
'--danger': '#cc2244', '--danger-bg': '#ffe0e8', '--success': '#008844', '--success-bg': '#d8ffe0',
|
||
'--accent-card': '#0077cc', '--accent-bg': '#ddeeff', '--accent-text': '#0077cc',
|
||
'--green': '#008844', '--green-bg': '#d8ffe0', '--purple': '#8800cc', '--purple-bg': '#f0d8ff',
|
||
'--surface': '#e8e8ff', '--surface2': '#f0f0ff', '--surface3': '#e0e0f8',
|
||
'--border-md': '#c0c0e0', '--text': '#1a1a33', '--text-2': '#4a4a6a', '--text-3': '#8080a0',
|
||
'--ok': '#008844', '--warn': '#996600', '--ai': '#8800cc', '--err': '#cc2244'
|
||
}
|
||
}
|
||
},
|
||
|
||
'navy-elegance': {
|
||
name: 'Navy Elegance', desc: 'Bleu profond & or',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#0a1628', '--bg-secondary': '#0d1f3c', '--bg-sidebar': '#081220',
|
||
'--bg-hover': '#162d50', '--border': '#1e3a5f', '--text-primary': '#d4dde8',
|
||
'--text-secondary': '#8899b4', '--text-muted': '#556688', '--accent': '#c9a84c',
|
||
'--accent-green': '#4caf7c', '--tag-bg': '#1e2840', '--tag-text': '#c9a84c',
|
||
'--code-bg': '#0d1f3c', '--search-bg': '#162d50', '--scrollbar': '#1e3a5f',
|
||
'--resize-handle': '#1e3a5f', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#d94a5a', '--danger-bg': '#351a22', '--success': '#4caf7c', '--success-bg': '#12281e',
|
||
'--accent-card': '#c9a84c', '--accent-bg': '#282218', '--accent-text': '#c9a84c',
|
||
'--green': '#4caf7c', '--green-bg': '#12281e', '--purple': '#8b6fc0', '--purple-bg': '#201a3a',
|
||
'--surface': '#0d1f3c', '--surface2': '#0a1628', '--surface3': '#081220',
|
||
'--border-md': '#1e3a5f', '--text': '#d4dde8', '--text-2': '#8899b4', '--text-3': '#556688',
|
||
'--ok': '#4caf7c', '--warn': '#c9a84c', '--ai': '#8b6fc0', '--err': '#d94a5a'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#f5f0e8', '--bg-secondary': '#ede6d8', '--bg-sidebar': '#e8dfd0',
|
||
'--bg-hover': '#e0d8c8', '--border': '#c8bda8', '--text-primary': '#2a3a50',
|
||
'--text-secondary': '#5a6a80', '--text-muted': '#8a9aa8', '--accent': '#8b6914',
|
||
'--accent-green': '#3d7a4c', '--tag-bg': '#f0e8d0', '--tag-text': '#8b6914',
|
||
'--code-bg': '#ede6d8', '--search-bg': '#f5f0e8', '--scrollbar': '#c8bda8',
|
||
'--resize-handle': '#c8bda8', '--overlay-bg': 'rgba(0,0,0,0.1)',
|
||
'--danger': '#b8383a', '--danger-bg': '#f8e0e0', '--success': '#3d7a4c', '--success-bg': '#e0f0e0',
|
||
'--accent-card': '#8b6914', '--accent-bg': '#f0e8d0', '--accent-text': '#8b6914',
|
||
'--green': '#3d7a4c', '--green-bg': '#e0f0e0', '--purple': '#6a4c8a', '--purple-bg': '#e8dff0',
|
||
'--surface': '#ede6d8', '--surface2': '#f5f0e8', '--surface3': '#e8dfd0',
|
||
'--border-md': '#c8bda8', '--text': '#2a3a50', '--text-2': '#5a6a80', '--text-3': '#8a9aa8',
|
||
'--ok': '#3d7a4c', '--warn': '#8b6914', '--ai': '#6a4c8a', '--err': '#b8383a'
|
||
}
|
||
}
|
||
},
|
||
|
||
'tons-terreux': {
|
||
name: 'Tons Terreux', desc: 'Sensation organique',
|
||
modes: {
|
||
dark: {
|
||
'--bg-primary': '#1c1a16', '--bg-secondary': '#252218', '--bg-sidebar': '#161410',
|
||
'--bg-hover': '#302b20', '--border': '#3d3628', '--text-primary': '#e8e0d0',
|
||
'--text-secondary': '#a89880', '--text-muted': '#6b5d48', '--accent': '#c8a050',
|
||
'--accent-green': '#6b9b50', '--tag-bg': '#2a2418', '--tag-text': '#c8a050',
|
||
'--code-bg': '#252218', '--search-bg': '#302b20', '--scrollbar': '#3d3628',
|
||
'--resize-handle': '#3d3628', '--overlay-bg': 'rgba(0,0,0,0.7)',
|
||
'--danger': '#c86050', '--danger-bg': '#352018', '--success': '#6b9b50', '--success-bg': '#1a2a14',
|
||
'--accent-card': '#c8a050', '--accent-bg': '#2a2418', '--accent-text': '#c8a050',
|
||
'--green': '#6b9b50', '--green-bg': '#1a2a14', '--purple': '#9070b0', '--purple-bg': '#221a30',
|
||
'--surface': '#252218', '--surface2': '#1c1a16', '--surface3': '#161410',
|
||
'--border-md': '#3d3628', '--text': '#e8e0d0', '--text-2': '#a89880', '--text-3': '#6b5d48',
|
||
'--ok': '#6b9b50', '--warn': '#c8a050', '--ai': '#9070b0', '--err': '#c86050'
|
||
},
|
||
light: {
|
||
'--bg-primary': '#f8f4ec', '--bg-secondary': '#f0e8d8', '--bg-sidebar': '#e8e0d0',
|
||
'--bg-hover': '#e0d8c8', '--border': '#c8bda0', '--text-primary': '#3d3028',
|
||
'--text-secondary': '#6b5d48', '--text-muted': '#9a8a70', '--accent': '#8b6914',
|
||
'--accent-green': '#5a8440', '--tag-bg': '#f0e0c8', '--tag-text': '#8b6914',
|
||
'--code-bg': '#f0e8d8', '--search-bg': '#f8f4ec', '--scrollbar': '#c8bda0',
|
||
'--resize-handle': '#c8bda0', '--overlay-bg': 'rgba(0,0,0,0.1)',
|
||
'--danger': '#a84040', '--danger-bg': '#f8e0e0', '--success': '#5a8440', '--success-bg': '#e0f0d8',
|
||
'--accent-card': '#8b6914', '--accent-bg': '#f0e0c8', '--accent-text': '#8b6914',
|
||
'--green': '#5a8440', '--green-bg': '#e0f0d8', '--purple': '#7058a0', '--purple-bg': '#e8dff8',
|
||
'--surface': '#f0e8d8', '--surface2': '#f8f4ec', '--surface3': '#e8e0d0',
|
||
'--border-md': '#c8bda0', '--text': '#3d3028', '--text-2': '#6b5d48', '--text-3': '#9a8a70',
|
||
'--ok': '#5a8440', '--warn': '#8b6914', '--ai': '#7058a0', '--err': '#a84040'
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// ── Theme Engine ─────────────────────────────────────────────────────
|
||
var _currentTheme = 'defaut-obsigate';
|
||
var _currentMode = 'dark';
|
||
|
||
var AVAILABLE_MODES = ['dark', 'light', 'high-contrast', 'sepia'];
|
||
|
||
// ── Mode generators ─────────────────────────────────────────────────
|
||
// Generate a high-contrast variant from dark-mode CSS vars.
|
||
function _genHighContrast(darkVars) {
|
||
return {
|
||
'--bg-primary': '#000000', '--bg-secondary': '#0a0a0a', '--bg-sidebar': '#000000',
|
||
'--bg-hover': '#141414', '--border': '#444444', '--text-primary': '#ffffff',
|
||
'--text-secondary': '#cccccc', '--text-muted': '#888888', '--accent': darkVars['--accent'] || '#58a6ff',
|
||
'--accent-green': '#4cff6e', '--tag-bg': '#1a2a44', '--tag-text': '#7dc4ff',
|
||
'--code-bg': '#0a0a0a', '--search-bg': '#111111', '--scrollbar': '#444444',
|
||
'--resize-handle': '#444444', '--overlay-bg': 'rgba(0,0,0,0.85)',
|
||
'--danger': '#ff6b6b', '--danger-bg': '#4a0f0f', '--success': '#4cff6e', '--success-bg': '#0a3a14',
|
||
'--accent-card': darkVars['--accent'] || '#58a6ff', '--accent-bg': '#1a2a44', '--accent-text': '#7dc4ff',
|
||
'--green': '#4cff6e', '--green-bg': '#0a3a14', '--purple': '#c084fc', '--purple-bg': '#2a1a4a',
|
||
'--surface': '#0a0a0a', '--surface2': '#000000', '--surface3': '#000000',
|
||
'--border-md': '#444444', '--text': '#ffffff', '--text-2': '#cccccc', '--text-3': '#888888',
|
||
'--ok': '#4cff6e', '--warn': '#ffcc00', '--ai': '#c084fc', '--err': '#ff6b6b'
|
||
};
|
||
}
|
||
|
||
// Generate a sepia variant from light-mode CSS vars.
|
||
function _genSepia(lightVars) {
|
||
return {
|
||
'--bg-primary': '#f5ecd5', '--bg-secondary': '#eee3c8', '--bg-sidebar': '#e8dcc0',
|
||
'--bg-hover': '#e0d4b4', '--border': '#c8b890', '--text-primary': '#3d2e1a',
|
||
'--text-secondary': '#6b5a3e', '--text-muted': '#9a8a6a', '--accent': '#b8860b',
|
||
'--accent-green': '#5a8a32', '--tag-bg': '#e8dcc0', '--tag-text': '#8b6914',
|
||
'--code-bg': '#eee3c8', '--search-bg': '#f5ecd5', '--scrollbar': '#c8b890',
|
||
'--resize-handle': '#c8b890', '--overlay-bg': 'rgba(60,40,10,0.25)',
|
||
'--danger': '#b33a2a', '--danger-bg': '#f5e0dc', '--success': '#5a8a32', '--success-bg': '#e0edd0',
|
||
'--accent-card': '#b8860b', '--accent-bg': '#f0e4c4', '--accent-text': '#8b6914',
|
||
'--green': '#5a8a32', '--green-bg': '#e0edd0', '--purple': '#7a5a9a', '--purple-bg': '#e8ddf0',
|
||
'--surface': '#eee3c8', '--surface2': '#f5ecd5', '--surface3': '#e8dcc0',
|
||
'--border-md': '#c8b890', '--text': '#3d2e1a', '--text-2': '#6b5a3e', '--text-3': '#9a8a6a',
|
||
'--ok': '#5a8a32', '--warn': '#b8860b', '--ai': '#7a5a9a', '--err': '#b33a2a'
|
||
};
|
||
}
|
||
|
||
// Get CSS vars for a theme + mode (generating HC/sepia on demand).
|
||
function _getVars(themeKey, mode) {
|
||
var theme = THEMES[themeKey];
|
||
if (!theme) return null;
|
||
if (theme.modes[mode]) return theme.modes[mode];
|
||
// Generate high-contrast / sepia from dark / light
|
||
if (mode === 'high-contrast') return _genHighContrast(theme.modes.dark || {});
|
||
if (mode === 'sepia') return _genSepia(theme.modes.light || {});
|
||
return null;
|
||
}
|
||
|
||
function applyTheme(themeKey, mode) {
|
||
var theme = THEMES[themeKey];
|
||
if (!theme) return;
|
||
mode = mode || _currentMode || 'dark';
|
||
var vars = _getVars(themeKey, mode);
|
||
if (!vars) return;
|
||
|
||
var root = document.documentElement;
|
||
Object.keys(vars).forEach(function(key) {
|
||
root.style.setProperty(key, vars[key]);
|
||
});
|
||
// #180 — the spreadsheet grid follows the active theme: derive its tokens
|
||
// from the generic surfaces so every theme (15 built-in × 4 modes, custom
|
||
// imports) gets a matching grid instead of the hardcoded dark/light pair.
|
||
// color-mix keeps the zebra stripe subtle on both dark and light grounds.
|
||
// #185 — `--grid-border` no longer reuses `--border` (which sits 1-2% off the
|
||
// cell background on most themes, so the grid was invisible). The line is now
|
||
// a blend of the text colour into the sheet surface: the SAME single value
|
||
// for every line, and legible on both dark and light grounds.
|
||
var _gridSurface = vars['--surface'] || vars['--bg-secondary'] || '#000000';
|
||
var _gridInk = vars['--text-primary'] || vars['--text'] || '#888888';
|
||
var _grid = {
|
||
'--grid-bg': _gridSurface,
|
||
'--grid-header-bg': vars['--bg-hover'] || _gridSurface,
|
||
'--grid-header-text': _gridInk,
|
||
'--grid-border': 'color-mix(in srgb, ' + _gridInk + ' 22%, ' + _gridSurface + ')',
|
||
'--grid-zebra': 'color-mix(in srgb, ' + _gridInk + ' 4%, transparent)'
|
||
};
|
||
Object.keys(_grid).forEach(function(key) {
|
||
if (_grid[key]) root.style.setProperty(key, _grid[key]);
|
||
});
|
||
// Store resolved vars on the theme for export/preview
|
||
if (!theme.modes[mode]) theme.modes[mode] = vars;
|
||
|
||
_currentTheme = themeKey;
|
||
_currentMode = mode;
|
||
try { localStorage.setItem('obsigate-theme', themeKey); } catch(e) {}
|
||
try { localStorage.setItem('obsigate-theme-mode', mode); } catch(e) {}
|
||
|
||
// Notify other components of theme change
|
||
try {
|
||
root.setAttribute('data-theme', mode);
|
||
// Syntax highlighting follows the light/dark mode (BUG-078): the theme
|
||
// *key* is not a mode, so toggling the sheets by key used to disable both
|
||
// and strip every code block of its colours. Sepia/high-contrast reuse the
|
||
// light palette.
|
||
var darkSheet = document.getElementById('hljs-theme-dark');
|
||
var lightSheet = document.getElementById('hljs-theme-light');
|
||
if (darkSheet && lightSheet) {
|
||
var isDark = mode === 'dark';
|
||
darkSheet.disabled = !isDark;
|
||
lightSheet.disabled = isDark;
|
||
}
|
||
document.dispatchEvent(new CustomEvent('themechange', { detail: { theme: themeKey, mode: mode } }));
|
||
} catch(e) {}
|
||
}
|
||
|
||
function toggleThemeMode() {
|
||
var idx = AVAILABLE_MODES.indexOf(_currentMode);
|
||
var newMode = AVAILABLE_MODES[(idx + 1) % AVAILABLE_MODES.length];
|
||
applyTheme(_currentTheme, newMode);
|
||
return newMode;
|
||
}
|
||
|
||
function getCurrentTheme() { return _currentTheme; }
|
||
function getCurrentMode() { return _currentMode; }
|
||
|
||
// ── Public API ──────────────────────────────────────────────────────
|
||
function setTheme(name) {
|
||
if (THEMES[name]) {
|
||
applyTheme(name, _currentMode);
|
||
}
|
||
}
|
||
|
||
function getTheme() {
|
||
return _currentTheme;
|
||
}
|
||
|
||
function listThemes() {
|
||
return Object.keys(THEMES).map(function(key) {
|
||
return { id: key, name: THEMES[key].name, desc: THEMES[key].desc };
|
||
});
|
||
}
|
||
|
||
function listModes() {
|
||
return AVAILABLE_MODES.slice();
|
||
}
|
||
|
||
// ── Import / Export ─────────────────────────────────────────────────
|
||
function exportTheme(themeKey) {
|
||
themeKey = themeKey || _currentTheme;
|
||
var theme = THEMES[themeKey];
|
||
if (!theme) return null;
|
||
// Ensure HC/sepia are resolved for export
|
||
var modes = {};
|
||
AVAILABLE_MODES.forEach(function(m) {
|
||
modes[m] = _getVars(themeKey, m);
|
||
});
|
||
return JSON.stringify({
|
||
version: 1,
|
||
id: themeKey,
|
||
name: theme.name,
|
||
desc: theme.desc,
|
||
modes: modes
|
||
}, null, 2);
|
||
}
|
||
|
||
function exportAllThemes() {
|
||
var out = {};
|
||
Object.keys(THEMES).forEach(function(key) {
|
||
out[key] = {
|
||
name: THEMES[key].name,
|
||
desc: THEMES[key].desc,
|
||
modes: {}
|
||
};
|
||
AVAILABLE_MODES.forEach(function(m) {
|
||
out[key].modes[m] = _getVars(key, m);
|
||
});
|
||
});
|
||
return JSON.stringify({ version: 1, themes: out }, null, 2);
|
||
}
|
||
|
||
function importTheme(jsonStr) {
|
||
try {
|
||
var data = JSON.parse(jsonStr);
|
||
if (!data || !data.modes) return { ok: false, error: 'Invalid theme JSON: missing modes' };
|
||
|
||
var name = data.name || data.id || ('custom-' + Date.now());
|
||
var key = (data.id || name).toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
||
|
||
var modes = {};
|
||
if (data.modes.dark && typeof data.modes.dark === 'object') modes.dark = data.modes.dark;
|
||
if (data.modes.light && typeof data.modes.light === 'object') modes.light = data.modes.light;
|
||
if (Object.keys(modes).length === 0) return { ok: false, error: 'Theme must define at least dark or light mode' };
|
||
|
||
THEMES[key] = { name: name, desc: data.desc || 'Custom theme', modes: modes };
|
||
|
||
try { localStorage.setItem('obsigate-custom-' + key, JSON.stringify(THEMES[key])); } catch(e) {}
|
||
return { ok: true, key: key };
|
||
} catch(e) {
|
||
return { ok: false, error: 'JSON parse error: ' + e.message };
|
||
}
|
||
}
|
||
|
||
function loadCustomThemes() {
|
||
var keys = [];
|
||
try {
|
||
for (var i = 0; i < localStorage.length; i++) {
|
||
var k = localStorage.key(i);
|
||
if (k && k.indexOf('obsigate-custom-') === 0) {
|
||
keys.push(k);
|
||
}
|
||
}
|
||
} catch(e) {}
|
||
keys.forEach(function(lsKey) {
|
||
try {
|
||
var data = JSON.parse(localStorage.getItem(lsKey));
|
||
if (data && data.modes) {
|
||
var key = lsKey.replace('obsigate-custom-', '');
|
||
THEMES[key] = data;
|
||
}
|
||
} catch(e) {}
|
||
});
|
||
}
|
||
|
||
function deleteCustomTheme(key) {
|
||
if (THEMES[key] && THEMES[key].desc === 'Custom theme') {
|
||
delete THEMES[key];
|
||
try { localStorage.removeItem('obsigate-custom-' + key); } catch(e) {}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function initThemes() {
|
||
// Load any custom themes from localStorage first
|
||
loadCustomThemes();
|
||
var saved = null;
|
||
var savedMode = null;
|
||
try { saved = localStorage.getItem('obsigate-theme'); } catch(e) {}
|
||
try { savedMode = localStorage.getItem('obsigate-theme-mode'); } catch(e) {}
|
||
if (saved && THEMES[saved]) _currentTheme = saved;
|
||
if (savedMode && AVAILABLE_MODES.indexOf(savedMode) !== -1) _currentMode = savedMode;
|
||
applyTheme(_currentTheme, _currentMode);
|
||
}
|
||
|
||
export {
|
||
THEMES, AVAILABLE_MODES,
|
||
applyTheme, toggleThemeMode,
|
||
getCurrentTheme, getCurrentMode,
|
||
getTheme,
|
||
setTheme, listThemes, listModes,
|
||
exportTheme, exportAllThemes, importTheme, deleteCustomTheme, loadCustomThemes,
|
||
initThemes
|
||
};
|