Skip to content

Commit 8445233

Browse files
committed
feat: theme selector with Bootstrap dark mode; use CSS vars; persist theme; show outputs only after successful parse
1 parent 9ce0c7c commit 8445233

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

index.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@
1414
.output-section {
1515
margin-top: 20px;
1616
padding: 15px;
17-
border: 1px solid #ddd;
17+
border: 1px solid var(--bs-border-color);
1818
border-radius: 5px;
19+
background-color: var(--bs-body-bg);
1920
}
2021
pre {
21-
background-color: #f8f9fa;
22+
background-color: var(--bs-tertiary-bg);
2223
padding: 10px;
2324
border-radius: 5px;
2425
max-height: 300px;
2526
overflow-y: auto;
27+
color: var(--bs-body-color);
2628
}
2729
h2 {
2830
margin-top: 30px;
@@ -43,6 +45,12 @@ <h1 id="title" class="m-0">SQL to JSON/Table Converter</h1>
4345
<option value="en" selected>English</option>
4446
<option value="zh">中文</option>
4547
</select>
48+
<label id="themeLabel" for="themeSelect" class="form-label m-0">Theme</label>
49+
<select id="themeSelect" class="form-select form-select-sm" style="width: 140px;">
50+
<option value="system" selected>System</option>
51+
<option value="light">Light</option>
52+
<option value="dark">Dark</option>
53+
</select>
4654
</div>
4755
</div>
4856

script.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ document.addEventListener('DOMContentLoaded', function() {
88
const outputModeSel = document.getElementById('outputMode');
99
const langSelect = document.getElementById('langSelect');
1010
const saveInputToggle = document.getElementById('saveInputToggle');
11+
const themeSelect = document.getElementById('themeSelect');
1112
const downloadJsonBtn = document.getElementById('downloadJsonBtn');
1213
const downloadCsvBtn = document.getElementById('downloadCsvBtn');
1314
const expandJsonToggle = document.getElementById('expandJsonToggle');
@@ -27,6 +28,7 @@ document.addEventListener('DOMContentLoaded', function() {
2728
const I18N = {
2829
en: {
2930
langLabel: 'Language',
31+
themeLabel: 'Theme',
3032
title: 'SQL to JSON/Table Converter',
3133
inputSectionTitle: 'Input & Actions',
3234
sqlLabel: 'Paste your SQL here (you can include CREATE TABLE and multiple INSERT statements):',
@@ -59,6 +61,7 @@ document.addEventListener('DOMContentLoaded', function() {
5961
},
6062
zh: {
6163
langLabel: '语言',
64+
themeLabel: '主题',
6265
title: 'SQL 插入语句 ➜ JSON 与 HTML 表格',
6366
inputSectionTitle: '输入与操作',
6467
sqlLabel: '在此粘贴 SQL(可包含 CREATE TABLE 与多条 INSERT 语句):',
@@ -97,6 +100,7 @@ document.addEventListener('DOMContentLoaded', function() {
97100
function applyI18n() {
98101
const setText = (id, text) => { const el = document.getElementById(id); if (el) el.textContent = text; };
99102
setText('langLabel', t('langLabel'));
103+
setText('themeLabel', t('themeLabel'));
100104
setText('title', t('title'));
101105
setText('inputSectionTitle', t('inputSectionTitle'));
102106
setText('sqlLabel', t('sqlLabel'));
@@ -716,6 +720,7 @@ document.addEventListener('DOMContentLoaded', function() {
716720
const LS_LANG = 'sql2table.lang';
717721
const LS_SAVE_INPUT = 'sql2table.saveInput';
718722
const LS_INPUT = 'sql2table.input';
723+
const LS_THEME = 'sql2table.theme';
719724

720725
// Load persisted settings
721726
try {
@@ -728,6 +733,9 @@ document.addEventListener('DOMContentLoaded', function() {
728733
const savedInput = localStorage.getItem(LS_INPUT);
729734
if (savedInput && sqlInput) sqlInput.value = savedInput;
730735
}
736+
const savedTheme = localStorage.getItem(LS_THEME) || 'system';
737+
if (themeSelect) themeSelect.value = savedTheme;
738+
applyTheme(savedTheme);
731739
} catch (_) {}
732740

733741
// language switching
@@ -759,6 +767,30 @@ document.addEventListener('DOMContentLoaded', function() {
759767
// initial i18n on load
760768
applyI18n();
761769

770+
// Theme handling
771+
function applyTheme(mode) {
772+
const root = document.documentElement;
773+
const mq = window.matchMedia('(prefers-color-scheme: dark)');
774+
function setFromSystem() {
775+
root.setAttribute('data-bs-theme', mq.matches ? 'dark' : 'light');
776+
}
777+
if (mode === 'dark' || mode === 'light') {
778+
root.setAttribute('data-bs-theme', mode);
779+
// remove listener if any
780+
try { mq.removeEventListener('change', onMqlChange); } catch (_) {}
781+
} else {
782+
setFromSystem();
783+
function onMqlChange() { setFromSystem(); }
784+
try { mq.addEventListener('change', onMqlChange); } catch (_) {}
785+
}
786+
}
787+
788+
themeSelect?.addEventListener('change', () => {
789+
const mode = themeSelect.value || 'system';
790+
try { localStorage.setItem(LS_THEME, mode); } catch (_) {}
791+
applyTheme(mode);
792+
});
793+
762794
function getSelected(groupedItems) {
763795
const sels = [];
764796
document.querySelectorAll('.row-select:checked').forEach(cb => {

0 commit comments

Comments
 (0)