mirror of
https://github.com/QwIT-Development/firka-extension.git
synced 2026-06-12 03:41:39 +02:00
Custom themes
This commit is contained in:
@@ -245,3 +245,496 @@ h2 {
|
||||
font-size:18px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.custom-themes-section {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--border-border, var(--text-teritary));
|
||||
}
|
||||
|
||||
.custom-themes-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.add-theme-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: var(--accent-15);
|
||||
color: var(--accent-accent);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.add-theme-btn:hover {
|
||||
background: var(--accent-accent);
|
||||
color: var(--button-secondaryFill);
|
||||
}
|
||||
|
||||
.custom-themes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.custom-theme-option {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--button-secondaryFill);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.custom-theme-option:hover {
|
||||
border-color: var(--accent-accent);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.custom-theme-option.active {
|
||||
border-color: var(--accent-accent);
|
||||
box-shadow: 0 0 0 2px var(--accent-15);
|
||||
}
|
||||
|
||||
.custom-theme-option .theme-preview {
|
||||
height: 70px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.custom-theme-option .theme-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 10px;
|
||||
background: var(--button-secondaryFill);
|
||||
border-top: 1px solid var(--border-border, var(--text-teritary));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.custom-theme-option .theme-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.custom-theme-option .theme-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.theme-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: var(--accent-15);
|
||||
color: var(--accent-accent);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-action-btn:hover {
|
||||
background: var(--accent-accent);
|
||||
color: var(--button-secondaryFill);
|
||||
}
|
||||
|
||||
.theme-action-btn.delete-theme {
|
||||
background: rgba(255, 80, 80, 0.15);
|
||||
color: #ff5050;
|
||||
}
|
||||
|
||||
.theme-action-btn.delete-theme:hover {
|
||||
background: #ff5050;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.theme-action-btn .material-icons-round {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.no-custom-themes {
|
||||
grid-column: 1 / -1;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(4px);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
padding: 16px;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.modal-overlay.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--card-card);
|
||||
border-radius: 20px;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
||||
animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-border, var(--text-teritary));
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
background: var(--error-15);
|
||||
color: var(--error-accent);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.modal-close .material-icons-round {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
border-top: 1px solid var(--border-border, var(--text-teritary));
|
||||
}
|
||||
|
||||
.modal-footer button {
|
||||
flex: 1;
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-family: "Montserrat", serif;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-accent);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--accent-15);
|
||||
}
|
||||
|
||||
.theme-form .form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.theme-form label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.theme-form input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
border-radius: 8px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-family: "Montserrat", serif;
|
||||
outline: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-form input[type="text"]:focus {
|
||||
border-color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mode-option {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
border-radius: 8px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-family: "Montserrat", serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.mode-option:hover {
|
||||
border-color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.mode-option.active {
|
||||
background: var(--accent-15);
|
||||
border-color: var(--accent-accent);
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.color-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.color-section h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.color-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.color-group label {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.color-input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.color-input-wrapper input[type="color"] {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.color-input-wrapper input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.color-input-wrapper input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.color-hex {
|
||||
width: 80px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
border-radius: 6px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.theme-preview-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.theme-preview-section h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.live-preview {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.live-preview .preview-header {
|
||||
height: 30%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.live-preview .preview-content {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.live-preview .preview-card {
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.live-preview .preview-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.share-code-wrapper {
|
||||
position: relative;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.share-code-wrapper textarea,
|
||||
#importCode {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-border, var(--text-teritary));
|
||||
border-radius: 8px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.share-code-wrapper textarea:focus,
|
||||
#importCode:focus {
|
||||
border-color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: var(--accent-15);
|
||||
color: var(--accent-accent);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: var(--accent-accent);
|
||||
color: var(--button-secondaryFill);
|
||||
}
|
||||
|
||||
.copy-btn.copied {
|
||||
background: var(--grades-4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--error-accent);
|
||||
font-size: 12px;
|
||||
margin-top: 8px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.import-modal .modal-body p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#importCode {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,17 @@
|
||||
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="custom-themes-section">
|
||||
<div class="custom-themes-header">
|
||||
<span data-i18n="settings.custom_themes.title">Egyéni témák</span>
|
||||
<button class="add-theme-btn" id="addCustomTheme">
|
||||
<span class="material-icons-round">add</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="theme-grid custom-themes-grid" id="customThemesGrid">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-section">
|
||||
<div class="setting-header">
|
||||
@@ -107,6 +118,123 @@
|
||||
<div class="version-info" id="version">v1.3.0</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="themeEditorModal">
|
||||
<div class="modal-content theme-editor-modal">
|
||||
<div class="modal-header">
|
||||
<h3 id="themeEditorTitle" data-i18n="settings.custom_themes.create">Új téma létrehozása</h3>
|
||||
<button class="modal-close" id="closeThemeEditor">
|
||||
<span class="material-icons-round">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="theme-form">
|
||||
<div class="form-group">
|
||||
<label for="themeName" data-i18n="settings.custom_themes.name">Téma neve</label>
|
||||
<input type="text" id="themeName" placeholder="Pl. Kék éjszaka">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label data-i18n="settings.custom_themes.mode">Mód</label>
|
||||
<div class="mode-selector">
|
||||
<button class="mode-option active" data-mode="dark">
|
||||
<span class="material-icons-round">dark_mode</span>
|
||||
<span data-i18n="settings.custom_themes.dark_mode">Sötét</span>
|
||||
</button>
|
||||
<button class="mode-option" data-mode="light">
|
||||
<span class="material-icons-round">light_mode</span>
|
||||
<span data-i18n="settings.custom_themes.light_mode">Világos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-section">
|
||||
<h4 data-i18n="settings.custom_themes.colors">Színek</h4>
|
||||
|
||||
<div class="color-group">
|
||||
<label data-i18n="settings.custom_themes.accent_color">Kiemelő szín</label>
|
||||
<div class="color-input-wrapper">
|
||||
<input type="color" id="accentColor" value="#A7DC22">
|
||||
<input type="text" class="color-hex" id="accentColorHex" value="#A7DC22">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-group">
|
||||
<label data-i18n="settings.custom_themes.background_color">Háttér szín</label>
|
||||
<div class="color-input-wrapper">
|
||||
<input type="color" id="backgroundColor" value="#0D1202">
|
||||
<input type="text" class="color-hex" id="backgroundColorHex" value="#0D1202">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-group">
|
||||
<label data-i18n="settings.custom_themes.card_color">Kártya szín</label>
|
||||
<div class="color-input-wrapper">
|
||||
<input type="color" id="cardColor" value="#141905">
|
||||
<input type="text" class="color-hex" id="cardColorHex" value="#141905">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-group">
|
||||
<label data-i18n="settings.custom_themes.text_color">Szöveg szín</label>
|
||||
<div class="color-input-wrapper">
|
||||
<input type="color" id="textColor" value="#EAF7CC">
|
||||
<input type="text" class="color-hex" id="textColorHex" value="#EAF7CC">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" id="cancelThemeEditor" data-i18n="common.cancel">Mégse</button>
|
||||
<button class="btn-primary" id="saveTheme" data-i18n="common.save">Mentés</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="shareThemeModal">
|
||||
<div class="modal-content share-modal">
|
||||
<div class="modal-header">
|
||||
<h3 data-i18n="settings.custom_themes.share">Téma megosztása</h3>
|
||||
<button class="modal-close" id="closeShareModal">
|
||||
<span class="material-icons-round">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p data-i18n="settings.custom_themes.share_description">Másold ki a kódot és oszd meg másokkal:</p>
|
||||
<div class="share-code-wrapper">
|
||||
<textarea id="shareCode" readonly></textarea>
|
||||
<button class="copy-btn" id="copyShareCode">
|
||||
<span class="material-icons-round">content_copy</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-primary" id="closeShareModalBtn" data-i18n="common.close">Bezárás</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="importThemeModal">
|
||||
<div class="modal-content import-modal">
|
||||
<div class="modal-header">
|
||||
<h3 data-i18n="settings.custom_themes.import">Téma importálása</h3>
|
||||
<button class="modal-close" id="closeImportModal">
|
||||
<span class="material-icons-round">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p data-i18n="settings.custom_themes.import_description">Illeszd be a téma kódot:</p>
|
||||
<textarea id="importCode" placeholder="Illessze be a téma kódot ide..."></textarea>
|
||||
<p class="error-message" id="importError"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" id="cancelImport" data-i18n="common.cancel">Mégse</button>
|
||||
<button class="btn-primary" id="confirmImport" data-i18n="settings.custom_themes.import">Importálás</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../tools/storageManager.js"></script>
|
||||
<script src="../global/language.js"></script>
|
||||
<script src="index.js"></script>
|
||||
|
||||
@@ -3,6 +3,311 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
|
||||
let customThemes = [];
|
||||
let editingThemeId = null;
|
||||
|
||||
async function loadCustomThemes() {
|
||||
try {
|
||||
const saved = await storageManager.get("customThemes", []);
|
||||
customThemes = Array.isArray(saved) ? saved : [];
|
||||
renderCustomThemes();
|
||||
} catch (error) {
|
||||
console.error("Error loading custom themes:", error);
|
||||
customThemes = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCustomThemes() {
|
||||
try {
|
||||
await storageManager.set("customThemes", customThemes);
|
||||
} catch (error) {
|
||||
console.error("Error saving custom themes:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCustomThemes() {
|
||||
const grid = document.getElementById("customThemesGrid");
|
||||
if (!grid) return;
|
||||
|
||||
if (customThemes.length === 0) {
|
||||
grid.innerHTML = `<div class="no-custom-themes" data-i18n="settings.custom_themes.no_themes">Még nincsenek egyéni témák</div>`;
|
||||
if (window.LanguageManager) {
|
||||
window.LanguageManager.updatePageTranslations();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = customThemes.map(theme => `
|
||||
<button class="theme-option custom-theme-option" data-theme="custom-${theme.id}">
|
||||
<div class="theme-preview" style="background: ${theme.colors.background};">
|
||||
<div class="preview-header" style="background: ${theme.colors.card};"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card" style="background: ${theme.colors.accent}20; border: 1px solid ${theme.colors.accent};"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="theme-info">
|
||||
<span class="theme-name">${theme.name}</span>
|
||||
<div class="theme-actions">
|
||||
<span class="theme-action-btn edit-theme" data-id="${theme.id}" title="Szerkesztés">
|
||||
<span class="material-icons-round">edit</span>
|
||||
</span>
|
||||
<span class="theme-action-btn share-theme" data-id="${theme.id}" title="Megosztás">
|
||||
<span class="material-icons-round">share</span>
|
||||
</span>
|
||||
<span class="theme-action-btn delete-theme" data-id="${theme.id}" title="Törlés">
|
||||
<span class="material-icons-round">delete</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
`).join("");
|
||||
|
||||
grid.querySelectorAll(".custom-theme-option").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".theme-action-btn")) return;
|
||||
const themeId = btn.dataset.theme;
|
||||
applyTheme(themeId);
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".edit-theme").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const id = btn.dataset.id;
|
||||
editTheme(id);
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".share-theme").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const id = btn.dataset.id;
|
||||
shareTheme(id);
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".delete-theme").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const id = btn.dataset.id;
|
||||
deleteTheme(id);
|
||||
});
|
||||
});
|
||||
|
||||
updateThemeButtons(getCurrentTheme());
|
||||
}
|
||||
|
||||
function openThemeEditor(theme = null) {
|
||||
const modal = document.getElementById("themeEditorModal");
|
||||
const titleEl = document.getElementById("themeEditorTitle");
|
||||
|
||||
if (theme) {
|
||||
editingThemeId = theme.id;
|
||||
titleEl.setAttribute("data-i18n", "settings.custom_themes.edit");
|
||||
titleEl.textContent = window.LanguageManager ? window.LanguageManager.t("settings.custom_themes.edit") : "Téma szerkesztése";
|
||||
|
||||
document.getElementById("themeName").value = theme.name;
|
||||
document.getElementById("accentColor").value = theme.colors.accent;
|
||||
document.getElementById("accentColorHex").value = theme.colors.accent;
|
||||
document.getElementById("backgroundColor").value = theme.colors.background;
|
||||
document.getElementById("backgroundColorHex").value = theme.colors.background;
|
||||
document.getElementById("cardColor").value = theme.colors.card;
|
||||
document.getElementById("cardColorHex").value = theme.colors.card;
|
||||
document.getElementById("textColor").value = theme.colors.text;
|
||||
document.getElementById("textColorHex").value = theme.colors.text;
|
||||
|
||||
document.querySelectorAll(".mode-option").forEach(btn => {
|
||||
btn.classList.toggle("active", btn.dataset.mode === theme.mode);
|
||||
});
|
||||
} else {
|
||||
editingThemeId = null;
|
||||
titleEl.setAttribute("data-i18n", "settings.custom_themes.create");
|
||||
titleEl.textContent = window.LanguageManager ? window.LanguageManager.t("settings.custom_themes.create") : "Új téma létrehozása";
|
||||
|
||||
document.getElementById("themeName").value = "";
|
||||
document.getElementById("accentColor").value = "#A7DC22";
|
||||
document.getElementById("accentColorHex").value = "#A7DC22";
|
||||
document.getElementById("backgroundColor").value = "#0D1202";
|
||||
document.getElementById("backgroundColorHex").value = "#0D1202";
|
||||
document.getElementById("cardColor").value = "#141905";
|
||||
document.getElementById("cardColorHex").value = "#141905";
|
||||
document.getElementById("textColor").value = "#EAF7CC";
|
||||
document.getElementById("textColorHex").value = "#EAF7CC";
|
||||
|
||||
document.querySelectorAll(".mode-option").forEach(btn => {
|
||||
btn.classList.toggle("active", btn.dataset.mode === "dark");
|
||||
});
|
||||
}
|
||||
|
||||
updateLivePreview();
|
||||
modal.classList.add("active");
|
||||
}
|
||||
|
||||
function closeThemeEditor() {
|
||||
const modal = document.getElementById("themeEditorModal");
|
||||
modal.classList.remove("active");
|
||||
editingThemeId = null;
|
||||
}
|
||||
|
||||
function updateLivePreview() {
|
||||
const preview = document.getElementById("livePreview");
|
||||
if (!preview) return;
|
||||
|
||||
const backgroundColor = document.getElementById("backgroundColor").value;
|
||||
const cardColor = document.getElementById("cardColor").value;
|
||||
const accentColor = document.getElementById("accentColor").value;
|
||||
const textColor = document.getElementById("textColor").value;
|
||||
|
||||
preview.style.background = backgroundColor;
|
||||
preview.querySelector(".preview-header").style.background = cardColor;
|
||||
preview.querySelector(".preview-card").style.background = `${accentColor}20`;
|
||||
preview.querySelector(".preview-card").style.border = `1px solid ${accentColor}`;
|
||||
preview.querySelector(".preview-text").style.color = textColor;
|
||||
}
|
||||
|
||||
function saveThemeFromEditor() {
|
||||
const name = document.getElementById("themeName").value.trim();
|
||||
if (!name) {
|
||||
document.getElementById("themeName").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = document.querySelector(".mode-option.active")?.dataset.mode || "dark";
|
||||
const colors = {
|
||||
accent: document.getElementById("accentColor").value,
|
||||
background: document.getElementById("backgroundColor").value,
|
||||
card: document.getElementById("cardColor").value,
|
||||
text: document.getElementById("textColor").value
|
||||
};
|
||||
|
||||
if (editingThemeId) {
|
||||
const index = customThemes.findIndex(t => t.id === editingThemeId);
|
||||
if (index !== -1) {
|
||||
customThemes[index] = {
|
||||
...customThemes[index],
|
||||
name,
|
||||
mode,
|
||||
colors
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const newTheme = {
|
||||
id: Date.now().toString(36) + Math.random().toString(36).substr(2, 5),
|
||||
name,
|
||||
mode,
|
||||
colors
|
||||
};
|
||||
customThemes.push(newTheme);
|
||||
}
|
||||
|
||||
saveCustomThemes();
|
||||
renderCustomThemes();
|
||||
closeThemeEditor();
|
||||
}
|
||||
|
||||
function editTheme(id) {
|
||||
const theme = customThemes.find(t => t.id === id);
|
||||
if (theme) {
|
||||
openThemeEditor(theme);
|
||||
}
|
||||
}
|
||||
|
||||
function shareTheme(id) {
|
||||
const theme = customThemes.find(t => t.id === id);
|
||||
if (!theme) return;
|
||||
|
||||
const shareData = {
|
||||
v: 1,
|
||||
n: theme.name,
|
||||
m: theme.mode,
|
||||
c: theme.colors
|
||||
};
|
||||
|
||||
const code = btoa(JSON.stringify(shareData));
|
||||
document.getElementById("shareCode").value = code;
|
||||
document.getElementById("shareThemeModal").classList.add("active");
|
||||
}
|
||||
|
||||
function deleteTheme(id) {
|
||||
const confirmMsg = window.LanguageManager ?
|
||||
window.LanguageManager.t("settings.custom_themes.delete_confirm") :
|
||||
"Biztosan törölni szeretnéd ezt a témát?";
|
||||
|
||||
if (confirm(confirmMsg)) {
|
||||
customThemes = customThemes.filter(t => t.id !== id);
|
||||
saveCustomThemes();
|
||||
renderCustomThemes();
|
||||
|
||||
const currentTheme = getCurrentTheme();
|
||||
if (currentTheme === `custom-${id}`) {
|
||||
applyTheme("light-green");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importTheme() {
|
||||
const code = document.getElementById("importCode").value.trim();
|
||||
const errorEl = document.getElementById("importError");
|
||||
errorEl.textContent = "";
|
||||
|
||||
if (!code) {
|
||||
errorEl.textContent = window.LanguageManager ?
|
||||
window.LanguageManager.t("settings.custom_themes.import_error_empty") :
|
||||
"Kérlek illeszd be a téma kódot!";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(atob(code));
|
||||
|
||||
if (!data.n || !data.m || !data.c) {
|
||||
throw new Error("Invalid theme data");
|
||||
}
|
||||
|
||||
const newTheme = {
|
||||
id: Date.now().toString(36) + Math.random().toString(36).substr(2, 5),
|
||||
name: data.n,
|
||||
mode: data.m,
|
||||
colors: data.c
|
||||
};
|
||||
|
||||
customThemes.push(newTheme);
|
||||
saveCustomThemes();
|
||||
renderCustomThemes();
|
||||
closeImportModal();
|
||||
|
||||
} catch (error) {
|
||||
errorEl.textContent = window.LanguageManager ?
|
||||
window.LanguageManager.t("settings.custom_themes.import_error_invalid") :
|
||||
"Érvénytelen téma kód!";
|
||||
}
|
||||
}
|
||||
|
||||
function closeImportModal() {
|
||||
document.getElementById("importThemeModal").classList.remove("active");
|
||||
document.getElementById("importCode").value = "";
|
||||
document.getElementById("importError").textContent = "";
|
||||
}
|
||||
|
||||
function closeShareModal() {
|
||||
document.getElementById("shareThemeModal").classList.remove("active");
|
||||
}
|
||||
|
||||
function copyShareCode() {
|
||||
const textarea = document.getElementById("shareCode");
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
|
||||
const btn = document.getElementById("copyShareCode");
|
||||
btn.classList.add("copied");
|
||||
btn.querySelector(".material-icons-round").textContent = "check";
|
||||
|
||||
setTimeout(() => {
|
||||
btn.classList.remove("copied");
|
||||
btn.querySelector(".material-icons-round").textContent = "content_copy";
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function getCurrentTheme() {
|
||||
return (
|
||||
localStorage.getItem("themePreference") ||
|
||||
@@ -15,7 +320,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const theme = button.dataset.theme;
|
||||
button.classList.toggle("active", theme === currentTheme);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getCurrentLanguage() {
|
||||
@@ -53,6 +357,16 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
|
||||
if (theme.startsWith("custom-")) {
|
||||
const themeId = theme.replace("custom-", "");
|
||||
const customTheme = customThemes.find(t => t.id === themeId);
|
||||
if (customTheme) {
|
||||
applyCustomThemeColors(customTheme);
|
||||
}
|
||||
} else {
|
||||
clearCustomThemeColors();
|
||||
}
|
||||
|
||||
updateThemeButtons(theme);
|
||||
|
||||
const tabs = await chrome.tabs.query({});
|
||||
@@ -61,12 +375,104 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
.sendMessage(tab.id, {
|
||||
action: "changeTheme",
|
||||
theme: theme,
|
||||
customThemes: customThemes,
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
const themeButtons = document.querySelectorAll(".theme-option");
|
||||
function applyCustomThemeColors(theme) {
|
||||
const root = document.documentElement;
|
||||
const isDark = theme.mode === "dark";
|
||||
|
||||
root.style.setProperty("--background", theme.colors.background);
|
||||
root.style.setProperty("--card-card", theme.colors.card);
|
||||
root.style.setProperty("--accent-accent", theme.colors.accent);
|
||||
root.style.setProperty("--text-primary", theme.colors.text);
|
||||
root.style.setProperty("--text-secondary", theme.colors.text + "cc");
|
||||
root.style.setProperty("--text-teritary", theme.colors.text + "80");
|
||||
root.style.setProperty("--accent-15", theme.colors.accent + "26");
|
||||
root.style.setProperty("--button-secondaryFill", isDark ? lightenColor(theme.colors.card, 10) : darkenColor(theme.colors.card, 5));
|
||||
root.style.setProperty("--accent-secondary", isDark ? lightenColor(theme.colors.accent, 20) : darkenColor(theme.colors.accent, 20));
|
||||
root.style.setProperty("--shadow-blur", isDark ? "0" : "2px");
|
||||
root.style.setProperty("--accent-shadow", isDark ? "#0000" : theme.colors.accent + "26");
|
||||
root.style.setProperty("--icon-filter", hexToFilter(theme.colors.accent));
|
||||
}
|
||||
|
||||
function clearCustomThemeColors() {
|
||||
const root = document.documentElement;
|
||||
const properties = [
|
||||
"--background",
|
||||
"--card-card",
|
||||
"--accent-accent",
|
||||
"--text-primary",
|
||||
"--text-secondary",
|
||||
"--text-teritary",
|
||||
"--accent-15",
|
||||
"--button-secondaryFill",
|
||||
"--accent-secondary",
|
||||
"--shadow-blur",
|
||||
"--accent-shadow",
|
||||
"--icon-filter"
|
||||
];
|
||||
|
||||
properties.forEach(prop => root.style.removeProperty(prop));
|
||||
}
|
||||
|
||||
function hexToFilter(hex) {
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
|
||||
const rNorm = r / 255;
|
||||
const gNorm = g / 255;
|
||||
const bNorm = b / 255;
|
||||
|
||||
const max = Math.max(rNorm, gNorm, bNorm);
|
||||
const min = Math.min(rNorm, gNorm, bNorm);
|
||||
let h, s, l = (max + min) / 2;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
const d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch (max) {
|
||||
case rNorm: h = ((gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0)) / 6; break;
|
||||
case gNorm: h = ((bNorm - rNorm) / d + 2) / 6; break;
|
||||
case bNorm: h = ((rNorm - gNorm) / d + 4) / 6; break;
|
||||
}
|
||||
}
|
||||
|
||||
const hue = Math.round(h * 360);
|
||||
const saturation = Math.round(s * 100);
|
||||
const lightness = Math.round(l * 100);
|
||||
|
||||
const brightnessVal = lightness > 50 ? 1 + (lightness - 50) / 100 : 0.5 + lightness / 100;
|
||||
const saturateVal = saturation > 0 ? 1 + saturation / 100 : 0;
|
||||
|
||||
return `brightness(0) saturate(100%) invert(${lightness}%) sepia(${saturation}%) saturate(${Math.min(500, saturation * 5)}%) hue-rotate(${hue}deg) brightness(${brightnessVal}) contrast(${90 + saturation / 10}%)`;
|
||||
}
|
||||
|
||||
function lightenColor(color, percent) {
|
||||
const num = parseInt(color.replace("#", ""), 16);
|
||||
const amt = Math.round(2.55 * percent);
|
||||
const R = Math.min(255, (num >> 16) + amt);
|
||||
const G = Math.min(255, ((num >> 8) & 0x00ff) + amt);
|
||||
const B = Math.min(255, (num & 0x0000ff) + amt);
|
||||
return "#" + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1);
|
||||
}
|
||||
|
||||
function darkenColor(color, percent) {
|
||||
const num = parseInt(color.replace("#", ""), 16);
|
||||
const amt = Math.round(2.55 * percent);
|
||||
const R = Math.max(0, (num >> 16) - amt);
|
||||
const G = Math.max(0, ((num >> 8) & 0x00ff) - amt);
|
||||
const B = Math.max(0, (num & 0x0000ff) - amt);
|
||||
return "#" + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1);
|
||||
}
|
||||
|
||||
const themeButtons = document.querySelectorAll(".theme-option:not(.custom-theme-option)");
|
||||
themeButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const theme = button.dataset.theme;
|
||||
@@ -92,6 +498,74 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("addCustomTheme")?.addEventListener("click", () => openThemeEditor());
|
||||
document.getElementById("closeThemeEditor")?.addEventListener("click", closeThemeEditor);
|
||||
document.getElementById("cancelThemeEditor")?.addEventListener("click", closeThemeEditor);
|
||||
document.getElementById("saveTheme")?.addEventListener("click", saveThemeFromEditor);
|
||||
|
||||
document.querySelectorAll(".mode-option").forEach(btn => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.querySelectorAll(".mode-option").forEach(b => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
|
||||
const isDark = btn.dataset.mode === "dark";
|
||||
if (isDark) {
|
||||
document.getElementById("backgroundColor").value = "#0D1202";
|
||||
document.getElementById("backgroundColorHex").value = "#0D1202";
|
||||
document.getElementById("cardColor").value = "#141905";
|
||||
document.getElementById("cardColorHex").value = "#141905";
|
||||
document.getElementById("textColor").value = "#EAF7CC";
|
||||
document.getElementById("textColorHex").value = "#EAF7CC";
|
||||
} else {
|
||||
document.getElementById("backgroundColor").value = "#FAFFF0";
|
||||
document.getElementById("backgroundColorHex").value = "#FAFFF0";
|
||||
document.getElementById("cardColor").value = "#F3FBDE";
|
||||
document.getElementById("cardColorHex").value = "#F3FBDE";
|
||||
document.getElementById("textColor").value = "#394C0A";
|
||||
document.getElementById("textColorHex").value = "#394C0A";
|
||||
}
|
||||
updateLivePreview();
|
||||
});
|
||||
});
|
||||
|
||||
["accent", "background", "card", "text"].forEach(colorType => {
|
||||
const colorInput = document.getElementById(`${colorType}Color`);
|
||||
const hexInput = document.getElementById(`${colorType}ColorHex`);
|
||||
|
||||
colorInput?.addEventListener("input", () => {
|
||||
hexInput.value = colorInput.value.toUpperCase();
|
||||
updateLivePreview();
|
||||
});
|
||||
|
||||
hexInput?.addEventListener("input", () => {
|
||||
const hex = hexInput.value;
|
||||
if (/^#[0-9A-Fa-f]{6}$/.test(hex)) {
|
||||
colorInput.value = hex;
|
||||
updateLivePreview();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("closeShareModal")?.addEventListener("click", closeShareModal);
|
||||
document.getElementById("closeShareModalBtn")?.addEventListener("click", closeShareModal);
|
||||
document.getElementById("copyShareCode")?.addEventListener("click", copyShareCode);
|
||||
|
||||
let pressTimer;
|
||||
const addBtn = document.getElementById("addCustomTheme");
|
||||
addBtn?.addEventListener("mousedown", () => {
|
||||
pressTimer = setTimeout(() => {
|
||||
document.getElementById("importThemeModal").classList.add("active");
|
||||
}, 500);
|
||||
});
|
||||
addBtn?.addEventListener("mouseup", () => clearTimeout(pressTimer));
|
||||
addBtn?.addEventListener("mouseleave", () => clearTimeout(pressTimer));
|
||||
|
||||
document.getElementById("closeImportModal")?.addEventListener("click", closeImportModal);
|
||||
document.getElementById("cancelImport")?.addEventListener("click", closeImportModal);
|
||||
document.getElementById("confirmImport")?.addEventListener("click", importTheme);
|
||||
|
||||
await loadCustomThemes();
|
||||
|
||||
let initialTheme = getCurrentTheme();
|
||||
|
||||
await applyTheme(initialTheme);
|
||||
|
||||
Reference in New Issue
Block a user