feat: implémenter système thèmes complet avec 14 thèmes prédéfinis (DEFAULT/GitHub/Linear/Spotify/Notion/Discord/Dracula/OneDark/TokyoNight/Nord/NightOwl/Anthracite/Cyberpunk/NavyElegance/Earthy), panel gestion thèmes dans tools.html avec grid previews et sélection interactive, synchronisation bidirectionnelle via bookmark shaarit_config (fetch/save JSON config), enforcement dark-only pour thèmes sans support light mode (disable toggle sidebar, auto-force dark), ajout data-theme-id attribute
This commit is contained in:
parent
861b3d347c
commit
404c036108
469
shaarli-pro/css/themes.css
Normal file
469
shaarli-pro/css/themes.css
Normal file
@ -0,0 +1,469 @@
|
||||
/* === Système de design ShaarIt - 16 Thèmes === */
|
||||
|
||||
/* Typographie globale (s'applique à tous) */
|
||||
:root {
|
||||
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--text-xs: 0.75rem;
|
||||
--text-sm: 0.875rem;
|
||||
--text-base: 1rem;
|
||||
--text-lg: 1.125rem;
|
||||
--text-xl: 1.25rem;
|
||||
--text-2xl: 1.5rem;
|
||||
|
||||
--radius-xs: 4px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 28px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 1. DEFAULT (ShaarIt) - DARK (Par défaut si pas de light) */
|
||||
:root, :root[data-theme-id="DEFAULT"] {
|
||||
--primary: #00D4AA;
|
||||
--on-primary: #0A1628;
|
||||
--primary-container: #243447;
|
||||
--on-primary-container: #4EECC4;
|
||||
|
||||
--secondary: #0EA5E9;
|
||||
--on-secondary: #0A1628;
|
||||
--secondary-container: #2A3F54;
|
||||
--on-secondary-container: #38BDF8;
|
||||
|
||||
--tertiary: #4EECC4;
|
||||
--on-tertiary: #0A1628;
|
||||
|
||||
--background: #0A1628;
|
||||
--on-background: #E2E8F0;
|
||||
|
||||
--surface: #0D1B2A;
|
||||
--on-surface: #E2E8F0;
|
||||
|
||||
--surface-variant: #1B2838;
|
||||
--on-surface-variant: #94A3B8;
|
||||
|
||||
--outline: #64748B;
|
||||
--outline-variant: #2A3F54;
|
||||
|
||||
--error: #EF4444;
|
||||
--on-error: #FFFFFF;
|
||||
--error-container: #450A0A;
|
||||
--on-error-container: #FCA5A5;
|
||||
}
|
||||
|
||||
/* 1. DEFAULT (ShaarIt) - LIGHT */
|
||||
:root[data-theme="light"][data-theme-id="DEFAULT"] {
|
||||
--primary: #006B5A;
|
||||
--on-primary: #FFFFFF;
|
||||
--primary-container: #B2F5E6;
|
||||
--on-primary-container: #00201A;
|
||||
|
||||
--secondary: #0077B6;
|
||||
--on-secondary: #FFFFFF;
|
||||
--secondary-container: #D0E8FF;
|
||||
--on-secondary-container: #001E36;
|
||||
|
||||
--tertiary: #00897B;
|
||||
--on-tertiary: #FFFFFF;
|
||||
|
||||
--background: #F8FAFA;
|
||||
--on-background: #1A1C1E;
|
||||
|
||||
--surface: #FFFFFF;
|
||||
--on-surface: #1A1C1E;
|
||||
|
||||
--surface-variant: #E7F0EE;
|
||||
--on-surface-variant: #404944;
|
||||
|
||||
--outline: #707974;
|
||||
--outline-variant: #C0C9C4;
|
||||
|
||||
--error: #BA1A1A;
|
||||
--on-error: #FFFFFF;
|
||||
--error-container: #FFDAD6;
|
||||
--on-error-container: #410002;
|
||||
}
|
||||
|
||||
/* 2. GITHUB - DARK */
|
||||
:root[data-theme-id="GITHUB"] {
|
||||
--primary: #58A6FF;
|
||||
--on-primary: #0D1117;
|
||||
--primary-container: #1F2937;
|
||||
--on-primary-container: #79C0FF;
|
||||
|
||||
--secondary: #3FB950;
|
||||
--on-secondary: #0D1117;
|
||||
--secondary-container: #1C2D22;
|
||||
--on-secondary-container: #56D364;
|
||||
|
||||
--tertiary: #D2A8FF;
|
||||
--on-tertiary: #0D1117;
|
||||
|
||||
--background: #0D1117;
|
||||
--on-background: #C9D1D9;
|
||||
|
||||
--surface: #161B22;
|
||||
--on-surface: #C9D1D9;
|
||||
|
||||
--surface-variant: #21262D;
|
||||
--on-surface-variant: #8B949E;
|
||||
|
||||
--outline: #30363D;
|
||||
--outline-variant: #21262D;
|
||||
|
||||
--error: #F85149;
|
||||
--on-error: #FFFFFF;
|
||||
--error-container: #490202;
|
||||
--on-error-container: #FFA198;
|
||||
}
|
||||
|
||||
/* 2. GITHUB - LIGHT */
|
||||
:root[data-theme="light"][data-theme-id="GITHUB"] {
|
||||
--primary: #0969DA;
|
||||
--on-primary: #FFFFFF;
|
||||
--primary-container: #DDF4FF;
|
||||
--on-primary-container: #0A3069;
|
||||
|
||||
--secondary: #1A7F37;
|
||||
--on-secondary: #FFFFFF;
|
||||
--secondary-container: #DAFBE1;
|
||||
--on-secondary-container: #0E4F1F;
|
||||
|
||||
--tertiary: #8250DF;
|
||||
--on-tertiary: #FFFFFF;
|
||||
|
||||
--background: #FFFFFF;
|
||||
--on-background: #1F2328;
|
||||
|
||||
--surface: #F6F8FA;
|
||||
--on-surface: #1F2328;
|
||||
|
||||
--surface-variant: #EAEEF2;
|
||||
--on-surface-variant: #656D76;
|
||||
|
||||
--outline: #D0D7DE;
|
||||
--outline-variant: #E1E4E8;
|
||||
|
||||
--error: #CF222E;
|
||||
--on-error: #FFFFFF;
|
||||
--error-container: #FFEBE9;
|
||||
--on-error-container: #82071E;
|
||||
}
|
||||
|
||||
/* 3. LINEAR - DARK */
|
||||
:root[data-theme-id="LINEAR"] {
|
||||
--primary: #5E6AD2;
|
||||
--on-primary: #FFFFFF;
|
||||
--primary-container: #2A2B3D;
|
||||
--on-primary-container: #8B8FE8;
|
||||
|
||||
--secondary: #4EA7FC;
|
||||
--on-secondary: #12131A;
|
||||
--secondary-container: #1E2A3A;
|
||||
--on-secondary-container: #7DC4FF;
|
||||
|
||||
--tertiary: #E8A861;
|
||||
--on-tertiary: #12131A;
|
||||
|
||||
--background: #12131A;
|
||||
--on-background: #EEEFF2;
|
||||
|
||||
--surface: #1B1C24;
|
||||
--on-surface: #EEEFF2;
|
||||
|
||||
--surface-variant: #22232E;
|
||||
--on-surface-variant: #8A8F98;
|
||||
|
||||
--outline: #3B3D4A;
|
||||
--outline-variant: #2A2B3D;
|
||||
|
||||
--error: #EB5757;
|
||||
--on-error: #FFFFFF;
|
||||
--error-container: #3D1515;
|
||||
--on-error-container: #FF9B9B;
|
||||
}
|
||||
|
||||
/* 4. SPOTIFY - DARK */
|
||||
:root[data-theme-id="SPOTIFY"] {
|
||||
--primary: #1DB954;
|
||||
--on-primary: #000000;
|
||||
--primary-container: #1A3D27;
|
||||
--on-primary-container: #1ED760;
|
||||
|
||||
--secondary: #1DB954;
|
||||
--on-secondary: #000000;
|
||||
|
||||
--tertiary: #B3B3B3;
|
||||
--on-tertiary: #000000;
|
||||
|
||||
--background: #000000;
|
||||
--on-background: #FFFFFF;
|
||||
|
||||
--surface: #121212;
|
||||
--on-surface: #FFFFFF;
|
||||
|
||||
--surface-variant: #1A1A1A;
|
||||
--on-surface-variant: #B3B3B3;
|
||||
|
||||
--outline: #333333;
|
||||
--outline-variant: #282828;
|
||||
|
||||
--error: #E22134;
|
||||
}
|
||||
|
||||
/* 5. NOTION - DARK */
|
||||
:root[data-theme-id="NOTION"] {
|
||||
--primary: #529CCA;
|
||||
--on-primary: #191919;
|
||||
|
||||
--secondary: #E07A5F;
|
||||
--on-secondary: #191919;
|
||||
|
||||
--tertiary: #81B29A;
|
||||
--on-tertiary: #191919;
|
||||
|
||||
--background: #191919;
|
||||
--on-background: #E0E0E0;
|
||||
|
||||
--surface: #202020;
|
||||
--on-surface: #E0E0E0;
|
||||
|
||||
--surface-variant: #2B2B2B;
|
||||
--on-surface-variant: #9B9B9B;
|
||||
|
||||
--outline: #3E3E3E;
|
||||
}
|
||||
|
||||
/* 6. DISCORD - DARK */
|
||||
:root[data-theme-id="DISCORD"] {
|
||||
--primary: #5865F2;
|
||||
--on-primary: #FFFFFF;
|
||||
|
||||
--secondary: #57F287;
|
||||
--on-secondary: #1E2124;
|
||||
|
||||
--tertiary: #FEE75C;
|
||||
--on-tertiary: #1E2124;
|
||||
|
||||
--background: #313338;
|
||||
--on-background: #DBDEE1;
|
||||
|
||||
--surface: #2B2D31;
|
||||
--on-surface: #DBDEE1;
|
||||
|
||||
--surface-variant: #383A40;
|
||||
--on-surface-variant: #B5BAC1;
|
||||
|
||||
--outline: #4E5058;
|
||||
}
|
||||
|
||||
/* 7. DRACULA - DARK */
|
||||
:root[data-theme-id="DRACULA"] {
|
||||
--primary: #BD93F9;
|
||||
--on-primary: #21222C;
|
||||
|
||||
--secondary: #50FA7B;
|
||||
--on-secondary: #21222C;
|
||||
|
||||
--tertiary: #FF79C6;
|
||||
--on-tertiary: #21222C;
|
||||
|
||||
--background: #282A36;
|
||||
--on-background: #F8F8F2;
|
||||
|
||||
--surface: #21222C;
|
||||
--on-surface: #F8F8F2;
|
||||
|
||||
--surface-variant: #343746;
|
||||
--on-surface-variant: #BFBFBF;
|
||||
|
||||
--outline: #6272A4;
|
||||
}
|
||||
|
||||
/* 8. ONE DARK PRO - DARK */
|
||||
:root[data-theme-id="ONE_DARK_PRO"] {
|
||||
--primary: #61AFEF;
|
||||
--on-primary: #1E2127;
|
||||
|
||||
--secondary: #98C379;
|
||||
--on-secondary: #1E2127;
|
||||
|
||||
--tertiary: #E5C07B;
|
||||
--on-tertiary: #1E2127;
|
||||
|
||||
--background: #282C34;
|
||||
--on-background: #ABB2BF;
|
||||
|
||||
--surface: #21252B;
|
||||
--on-surface: #ABB2BF;
|
||||
|
||||
--surface-variant: #2C313A;
|
||||
--on-surface-variant: #8B929E;
|
||||
|
||||
--outline: #3E4452;
|
||||
}
|
||||
|
||||
/* 9. TOKYO NIGHT - DARK */
|
||||
:root[data-theme-id="TOKYO_NIGHT"] {
|
||||
--primary: #7AA2F7;
|
||||
--on-primary: #1A1B26;
|
||||
|
||||
--secondary: #9ECE6A;
|
||||
--on-secondary: #1A1B26;
|
||||
|
||||
--tertiary: #BB9AF7;
|
||||
--on-tertiary: #1A1B26;
|
||||
|
||||
--background: #1A1B26;
|
||||
--on-background: #C0CAF5;
|
||||
|
||||
--surface: #16171F;
|
||||
--on-surface: #C0CAF5;
|
||||
|
||||
--surface-variant: #24283B;
|
||||
--on-surface-variant: #9AA5CE;
|
||||
|
||||
--outline: #3B4261;
|
||||
}
|
||||
|
||||
/* 10. NORD - DARK */
|
||||
:root[data-theme-id="NORD"] {
|
||||
--primary: #88C0D0;
|
||||
--on-primary: #2E3440;
|
||||
|
||||
--secondary: #A3BE8C;
|
||||
--on-secondary: #2E3440;
|
||||
|
||||
--tertiary: #EBCB8B;
|
||||
--on-tertiary: #2E3440;
|
||||
|
||||
--background: #2E3440;
|
||||
--on-background: #ECEFF4;
|
||||
|
||||
--surface: #3B4252;
|
||||
--on-surface: #D8DEE9;
|
||||
|
||||
--surface-variant: #434C5E;
|
||||
--on-surface-variant: #D8DEE9;
|
||||
|
||||
--outline: #4C566A;
|
||||
}
|
||||
|
||||
/* 11. NIGHT OWL - DARK */
|
||||
:root[data-theme-id="NIGHT_OWL"] {
|
||||
--primary: #7FDBCA;
|
||||
--on-primary: #011627;
|
||||
|
||||
--secondary: #ADDB67;
|
||||
--on-secondary: #011627;
|
||||
|
||||
--tertiary: #C792EA;
|
||||
--on-tertiary: #011627;
|
||||
|
||||
--background: #011627;
|
||||
--on-background: #D6DEEB;
|
||||
|
||||
--surface: #0B2942;
|
||||
--on-surface: #D6DEEB;
|
||||
|
||||
--surface-variant: #112B45;
|
||||
--on-surface-variant: #9FAFC2;
|
||||
|
||||
--outline: #1D3B58;
|
||||
}
|
||||
|
||||
/* 12. ANTHRACITE - DARK */
|
||||
:root[data-theme-id="ANTHRACITE"] {
|
||||
--primary: #BB86FC;
|
||||
--on-primary: #121212;
|
||||
|
||||
--secondary: #03DAC6;
|
||||
--on-secondary: #121212;
|
||||
|
||||
--tertiary: #CF6679;
|
||||
--on-tertiary: #121212;
|
||||
|
||||
--background: #121212;
|
||||
--on-background: #E1E1E1;
|
||||
|
||||
--surface: #1E1E1E;
|
||||
--on-surface: #E1E1E1;
|
||||
|
||||
--surface-variant: #2C2C2C;
|
||||
--on-surface-variant: #AAAAAA;
|
||||
|
||||
--outline: #3D3D3D;
|
||||
}
|
||||
|
||||
/* 13. CYBERPUNK - DARK */
|
||||
:root[data-theme-id="CYBERPUNK"] {
|
||||
--primary: #00FFFF;
|
||||
--on-primary: #0A0A14;
|
||||
|
||||
--secondary: #FF00FF;
|
||||
--on-secondary: #0A0A14;
|
||||
|
||||
--tertiary: #FFFF00;
|
||||
--on-tertiary: #0A0A14;
|
||||
|
||||
--background: #0A0A14;
|
||||
--on-background: #E0E0F0;
|
||||
|
||||
--surface: #12121E;
|
||||
--on-surface: #E0E0F0;
|
||||
|
||||
--surface-variant: #1A1A2E;
|
||||
--on-surface-variant: #A0A0C0;
|
||||
|
||||
--outline: #2A2A44;
|
||||
}
|
||||
|
||||
/* 14. NAVY ELEGANCE - DARK */
|
||||
:root[data-theme-id="NAVY_ELEGANCE"] {
|
||||
--primary: #D4AF37;
|
||||
--on-primary: #0B1929;
|
||||
|
||||
--secondary: #C0C0C0;
|
||||
--on-secondary: #0B1929;
|
||||
|
||||
--tertiary: #87CEEB;
|
||||
--on-tertiary: #0B1929;
|
||||
|
||||
--background: #0B1929;
|
||||
--on-background: #E0E4EA;
|
||||
|
||||
--surface: #0F2035;
|
||||
--on-surface: #E0E4EA;
|
||||
|
||||
--surface-variant: #162A42;
|
||||
--on-surface-variant: #A0AABB;
|
||||
|
||||
--outline: #1E3550;
|
||||
}
|
||||
|
||||
/* 15. EARTHY - DARK */
|
||||
:root[data-theme-id="EARTHY"] {
|
||||
--primary: #81C784;
|
||||
--on-primary: #1A1510;
|
||||
|
||||
--secondary: #D4A574;
|
||||
--on-secondary: #1A1510;
|
||||
|
||||
--tertiary: #A5D6A7;
|
||||
--on-tertiary: #1A1510;
|
||||
|
||||
--background: #1A1510;
|
||||
--on-background: #E0D8CF;
|
||||
|
||||
--surface: #231E18;
|
||||
--on-surface: #E0D8CF;
|
||||
|
||||
--surface-variant: #2E2720;
|
||||
--on-surface-variant: #ADA49A;
|
||||
|
||||
--outline: #3D342B;
|
||||
}
|
||||
@ -10,10 +10,22 @@
|
||||
<!-- Prevent dark mode flash (FOUC) - must run before CSS loads -->
|
||||
<script>
|
||||
(function() {
|
||||
var t = localStorage.getItem('theme') ||
|
||||
var themeMode = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
if (t === 'dark') {
|
||||
var themeId = localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
|
||||
|
||||
// Disable light mode for dark-only themes
|
||||
var darkOnlyThemes = ['LINEAR', 'SPOTIFY', 'NOTION', 'DISCORD', 'DRACULA', 'ONE_DARK_PRO', 'TOKYO_NIGHT', 'NORD', 'NIGHT_OWL', 'ANTHRACITE', 'CYBERPUNK', 'NAVY_ELEGANCE', 'EARTHY'];
|
||||
if (darkOnlyThemes.indexOf(themeId) !== -1) {
|
||||
themeMode = 'dark';
|
||||
}
|
||||
|
||||
if (themeMode === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme-id', themeId);
|
||||
|
||||
if (themeMode === 'dark') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var mc = document.querySelector('meta[name="theme-color"]');
|
||||
if (mc) mc.setAttribute('content', '#1e293b');
|
||||
@ -23,6 +35,7 @@
|
||||
</script>
|
||||
|
||||
<!-- Professional Theme CSS -->
|
||||
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/themes.css?v=1.0.0" />
|
||||
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/style.css?v=1.0.4" />
|
||||
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/custom_views.css?v=1.0.5" />
|
||||
{if="$pageName=='editlink' || $pageName=='addlink' || $pageName=='editlinkbatch'"}
|
||||
|
||||
@ -36,12 +36,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeIconLight = document.getElementById('theme-icon-light');
|
||||
const themeLabelSpan = document.querySelector('.theme-toggle-label span');
|
||||
|
||||
function checkThemeRestrictions() {
|
||||
const themeId = localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
|
||||
const darkOnlyThemes = ['LINEAR', 'SPOTIFY', 'NOTION', 'DISCORD', 'DRACULA', 'ONE_DARK_PRO', 'TOKYO_NIGHT', 'NORD', 'NIGHT_OWL', 'ANTHRACITE', 'CYBERPUNK', 'NAVY_ELEGANCE', 'EARTHY'];
|
||||
return darkOnlyThemes.indexOf(themeId) !== -1;
|
||||
}
|
||||
|
||||
function updateTheme(theme) {
|
||||
// Enforce dark mode if the theme only supports dark
|
||||
if (checkThemeRestrictions()) {
|
||||
theme = 'dark';
|
||||
}
|
||||
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme', theme);
|
||||
|
||||
if (themeCheckbox) {
|
||||
themeCheckbox.checked = theme === 'dark';
|
||||
// Disable the checkbox if restricted to dark mode
|
||||
themeCheckbox.disabled = checkThemeRestrictions();
|
||||
themeCheckbox.closest('.theme-switch').style.opacity = themeCheckbox.disabled ? '0.5' : '1';
|
||||
}
|
||||
if (themeIconLight) {
|
||||
themeIconLight.className = theme === 'dark' ? 'mdi mdi-weather-night' : 'mdi mdi-weather-sunny';
|
||||
@ -51,13 +65,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle themeChanged event fired by tools.html
|
||||
window.addEventListener('themeChanged', () => {
|
||||
updateTheme(localStorage.getItem('theme') || 'dark');
|
||||
});
|
||||
|
||||
// Init Theme
|
||||
const savedTheme = localStorage.getItem('theme') ||
|
||||
let savedTheme = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
updateTheme(savedTheme);
|
||||
|
||||
if (themeCheckbox) {
|
||||
themeCheckbox.addEventListener('change', () => {
|
||||
if (checkThemeRestrictions()) return; // Extra safety
|
||||
const next = themeCheckbox.checked ? 'dark' : 'light';
|
||||
updateTheme(next);
|
||||
});
|
||||
@ -3287,4 +3307,45 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateSummary();
|
||||
renderDays();
|
||||
})();
|
||||
|
||||
// ===== Sync Theme from Bookmark in Background =====
|
||||
setTimeout(() => {
|
||||
// Run sync after a slight delay to avoid blocking initial load
|
||||
if (!window.shaarli || !window.shaarli.basePath) return;
|
||||
|
||||
fetch(window.shaarli.basePath + '/?searchtags=shaarit_config')
|
||||
.then(res => res.text())
|
||||
.then(text => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(text, 'text/html');
|
||||
const linkCard = doc.querySelector('.linklist-item');
|
||||
if (linkCard) {
|
||||
const descEl = linkCard.querySelector('.link-description p');
|
||||
if (descEl) {
|
||||
try {
|
||||
const config = JSON.parse(descEl.textContent.trim());
|
||||
const remoteThemeId = config.theme;
|
||||
const localThemeId = localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
|
||||
|
||||
if (remoteThemeId && remoteThemeId !== localThemeId) {
|
||||
localStorage.setItem('shaarit_theme_id', remoteThemeId);
|
||||
document.documentElement.setAttribute('data-theme-id', remoteThemeId);
|
||||
|
||||
// Enforce light/dark constraints based on new theme
|
||||
const darkOnlyThemes = ['LINEAR', 'SPOTIFY', 'NOTION', 'DISCORD', 'DRACULA', 'ONE_DARK_PRO', 'TOKYO_NIGHT', 'NORD', 'NIGHT_OWL', 'ANTHRACITE', 'CYBERPUNK', 'NAVY_ELEGANCE', 'EARTHY'];
|
||||
if (darkOnlyThemes.indexOf(remoteThemeId) !== -1) {
|
||||
localStorage.setItem('theme', 'dark');
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event('themeChanged'));
|
||||
console.log('[shaarit] Theme synced from bookmark:', remoteThemeId);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.log('[shaarit] Background theme sync failed:', err));
|
||||
}, 1500);
|
||||
|
||||
});
|
||||
|
||||
@ -14,6 +14,16 @@
|
||||
<div class="card">
|
||||
<div class="card-header">{'Settings'|t}</div>
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action ripple" href="#" id="themes-panel-toggle">
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-palette"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Themes'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Choose application visual theme'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/configure">
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-cog"></i>
|
||||
@ -101,6 +111,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Themes Management Panel -->
|
||||
<div class="row" id="themes-panel" style="display: none;">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<div class="card themes-card">
|
||||
<div class="card-header">
|
||||
<span><i class="mdi mdi-palette"></i> {'Application Themes'|t}</span>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="close-themes-panel">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="themes-info">
|
||||
<i class="mdi mdi-information-outline"></i>
|
||||
<span>{'Choose your preferred visual theme. The theme configuration will be synchronized across your devices via the shaarit_config bookmark.'|t}</span>
|
||||
</div>
|
||||
|
||||
<div class="themes-grid" id="themes-grid">
|
||||
<!-- Filled by JavaScript -->
|
||||
</div>
|
||||
|
||||
<div class="themes-actions">
|
||||
<span id="theme-save-status" class="save-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden Tags Management Panel -->
|
||||
<div class="row" id="hidden-tags-panel" style="display: none;">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
@ -249,6 +287,111 @@
|
||||
{include="page.footer"}
|
||||
|
||||
<style>
|
||||
/* Themes Panel Styles */
|
||||
.themes-card .card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.themes-info {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, rgba(0, 212, 170, 0.1) 0%, rgba(14, 165, 233, 0.1) 100%);
|
||||
border: 1px solid rgba(0, 212, 170, 0.2);
|
||||
border-radius: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.themes-info i {
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.themes-info span {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.themes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.theme-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
background: var(--bg-card);
|
||||
border: 2px solid var(--border-light);
|
||||
border-radius: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-item:hover {
|
||||
border-color: rgba(0, 212, 170, 0.4);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.theme-item.active {
|
||||
border-color: var(--primary);
|
||||
background: rgba(var(--primary-rgb), 0.05);
|
||||
}
|
||||
|
||||
.theme-preview {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.theme-preview-top {
|
||||
height: 30%;
|
||||
background: var(--preview-primary);
|
||||
}
|
||||
|
||||
.theme-preview-bottom {
|
||||
height: 70%;
|
||||
background: var(--preview-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.theme-preview-card {
|
||||
width: 60%;
|
||||
height: 50%;
|
||||
background: var(--preview-surface);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.theme-name {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
[data-theme="light"] .themes-info {
|
||||
background: linear-gradient(135deg, rgba(0, 107, 90, 0.05) 0%, rgba(0, 119, 182, 0.05) 100%);
|
||||
}
|
||||
|
||||
/* Hidden Tags Panel Styles */
|
||||
.hidden-tags-card .card-header {
|
||||
display: flex;
|
||||
@ -426,6 +569,177 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Themes Management
|
||||
(function() {
|
||||
const THEMES = [
|
||||
{ id: 'DEFAULT', name: 'ShaarIt', desc: 'Original theme', color: '#00D4AA', bg: '#0A1628', surface: '#0D1B2A', hasLight: true },
|
||||
{ id: 'GITHUB', name: 'GitHub', desc: 'Soft contrast', color: '#58A6FF', bg: '#0D1117', surface: '#161B22', hasLight: true },
|
||||
{ id: 'LINEAR', name: 'Linear', desc: 'Subtle gradients', color: '#5E6AD2', bg: '#12131A', surface: '#1B1C24', hasLight: false },
|
||||
{ id: 'SPOTIFY', name: 'Spotify', desc: 'Pure black & green', color: '#1DB954', bg: '#000000', surface: '#121212', hasLight: false },
|
||||
{ id: 'NOTION', name: 'Notion', desc: 'Clean reading', color: '#529CCA', bg: '#191919', surface: '#202020', hasLight: false },
|
||||
{ id: 'DISCORD', name: 'Discord', desc: 'Community standard', color: '#5865F2', bg: '#313338', surface: '#2B2D31', hasLight: false },
|
||||
{ id: 'DRACULA', name: 'Dracula', desc: 'Pink & purple accents', color: '#BD93F9', bg: '#282A36', surface: '#21222C', hasLight: false },
|
||||
{ id: 'ONE_DARK_PRO', name: 'One Dark', desc: 'Atom inspired', color: '#61AFEF', bg: '#282C34', surface: '#21252B', hasLight: false },
|
||||
{ id: 'TOKYO_NIGHT', name: 'Tokyo Night', desc: 'Neon blue', color: '#7AA2F7', bg: '#1A1B26', surface: '#16171F', hasLight: false },
|
||||
{ id: 'NORD', name: 'Nord', desc: 'Arctic style', color: '#88C0D0', bg: '#2E3440', surface: '#3B4252', hasLight: false },
|
||||
{ id: 'NIGHT_OWL', name: 'Night Owl', desc: 'Optimized for night', color: '#7FDBCA', bg: '#011627', surface: '#0B2942', hasLight: false },
|
||||
{ id: 'ANTHRACITE', name: 'Anthracite', desc: 'Material dark', color: '#BB86FC', bg: '#121212', surface: '#1E1E1E', hasLight: false },
|
||||
{ id: 'CYBERPUNK', name: 'Cyberpunk', desc: 'Vibrant neon', color: '#00FFFF', bg: '#0A0A14', surface: '#12121E', hasLight: false },
|
||||
{ id: 'NAVY_ELEGANCE', name: 'Navy Élégance', desc: 'Deep blue & gold', color: '#D4AF37', bg: '#0B1929', surface: '#0F2035', hasLight: false },
|
||||
{ id: 'EARTHY', name: 'Tons Terreux', desc: 'Organic feel', color: '#81C784', bg: '#1A1510', surface: '#231E18', hasLight: false }
|
||||
];
|
||||
|
||||
function getActiveTheme() {
|
||||
return localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
|
||||
}
|
||||
|
||||
function showSaveStatus() {
|
||||
const status = document.getElementById('theme-save-status');
|
||||
if (status) {
|
||||
status.textContent = 'Saved! Refreshing...';
|
||||
status.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
function renderThemes() {
|
||||
const container = document.getElementById('themes-grid');
|
||||
if (!container) return;
|
||||
|
||||
const activeTheme = getActiveTheme();
|
||||
|
||||
container.innerHTML = THEMES.map(theme => `
|
||||
<div class="theme-item ${activeTheme === theme.id ? 'active' : ''}" data-theme-id="${theme.id}" data-has-light="${theme.hasLight}">
|
||||
<div class="theme-preview" style="--preview-primary: ${theme.color}; --preview-bg: ${theme.bg}; --preview-surface: ${theme.surface};">
|
||||
<div class="theme-preview-top"></div>
|
||||
<div class="theme-preview-bottom">
|
||||
<div class="theme-preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="theme-name">${theme.name}</div>
|
||||
<div class="theme-desc">${theme.desc}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
container.querySelectorAll('.theme-item').forEach(item => {
|
||||
item.addEventListener('click', function() {
|
||||
const themeId = this.dataset.themeId;
|
||||
const hasLight = this.dataset.hasLight === 'true';
|
||||
|
||||
// Update UI immediately
|
||||
container.querySelectorAll('.theme-item').forEach(el => el.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
// Apply theme locally
|
||||
localStorage.setItem('shaarit_theme_id', themeId);
|
||||
document.documentElement.setAttribute('data-theme-id', themeId);
|
||||
|
||||
// Handle light mode restriction
|
||||
if (!hasLight) {
|
||||
localStorage.setItem('theme', 'dark');
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
// Trigger event for sidebar to update its switch
|
||||
window.dispatchEvent(new Event('themeChanged'));
|
||||
}
|
||||
|
||||
showSaveStatus();
|
||||
|
||||
// Sync to bookmark in background
|
||||
syncThemeToBookmark(themeId).then(() => {
|
||||
// Refresh to ensure everything applies cleanly (plugins etc)
|
||||
setTimeout(() => window.location.reload(), 500);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function syncThemeToBookmark(themeId) {
|
||||
try {
|
||||
// We fetch the current config bookmark if any
|
||||
const res = await fetch(shaarli.basePath + '/?searchtags=shaarit_config');
|
||||
const text = await res.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(text, 'text/html');
|
||||
|
||||
let config = { version: 1, theme: themeId, collections: [] };
|
||||
let editUrl = null;
|
||||
|
||||
// Find existing config
|
||||
const linkCard = doc.querySelector('.linklist-item');
|
||||
if (linkCard) {
|
||||
const descEl = linkCard.querySelector('.link-description p');
|
||||
if (descEl) {
|
||||
try {
|
||||
const parsed = JSON.parse(descEl.textContent.trim());
|
||||
config = Object.assign({}, parsed, { theme: themeId });
|
||||
} catch(e) {}
|
||||
}
|
||||
const editBtn = linkCard.querySelector('.link-action-edit');
|
||||
if (editBtn) editUrl = editBtn.getAttribute('href');
|
||||
}
|
||||
|
||||
// Prepare form data
|
||||
const formData = new FormData();
|
||||
formData.append('lf_title', 'collections');
|
||||
formData.append('lf_url', 'https://shaarit.app/config/collections');
|
||||
formData.append('lf_tags', 'shaarit_config');
|
||||
formData.append('lf_description', JSON.stringify(config));
|
||||
formData.append('save_edit', 'Save');
|
||||
|
||||
let token = '';
|
||||
|
||||
if (editUrl) {
|
||||
// Update existing
|
||||
const editRes = await fetch(editUrl);
|
||||
const editDoc = parser.parseFromString(await editRes.text(), 'text/html');
|
||||
const tokenEl = editDoc.querySelector('input[name="token"]');
|
||||
const idEl = editDoc.querySelector('input[name="lf_id"]');
|
||||
if (tokenEl) token = tokenEl.value;
|
||||
if (idEl) formData.append('lf_id', idEl.value);
|
||||
} else {
|
||||
// Create new
|
||||
const addRes = await fetch(shaarli.basePath + '/admin/add-shaare');
|
||||
const addDoc = parser.parseFromString(await addRes.text(), 'text/html');
|
||||
const tokenEl = addDoc.querySelector('input[name="token"]');
|
||||
if (tokenEl) token = tokenEl.value;
|
||||
}
|
||||
|
||||
if (token) {
|
||||
formData.append('token', token);
|
||||
await fetch(shaarli.basePath + '/admin/shaare', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to sync theme to bookmark', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const toggleBtn = document.getElementById('themes-panel-toggle');
|
||||
const panel = document.getElementById('themes-panel');
|
||||
const closeBtn = document.getElementById('close-themes-panel');
|
||||
|
||||
if (toggleBtn && panel) {
|
||||
toggleBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
panel.style.display = panel.style.display === 'none' ? '' : 'none';
|
||||
if (panel.style.display !== 'none') {
|
||||
renderThemes();
|
||||
panel.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (closeBtn && panel) {
|
||||
closeBtn.addEventListener('click', function() {
|
||||
panel.style.display = 'none';
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// Hidden Tags Management
|
||||
(function() {
|
||||
const STORAGE_KEY = 'shaarli_hidden_tags';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user