Skip to content

Commit 33a579a

Browse files
committed
feat: implement theme customization feature with theme manager and styles integration
1 parent c575657 commit 33a579a

File tree

7 files changed

+335
-11
lines changed

7 files changed

+335
-11
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,10 @@
7474
"image-size": "^2.0.2",
7575
"katex": "^0.16.23",
7676
"marked": "^15.0.8"
77+
},
78+
"release-it": {
79+
"npm": {
80+
"publish": false
81+
}
7782
}
7883
}

web/index.html

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,40 @@
3333
</div>
3434

3535
<div class="flex items-center space-x-4">
36+
<!-- Theme Selector -->
37+
<div class="relative">
38+
<button id="theme-toggle" class="px-3 py-1 text-sm rounded bg-gray-200 hover:bg-gray-100 cursor-pointer flex items-center">
39+
<svg class="h-3.5 w-3.5 mr-1" width="24" height="24" viewBox="0 0 48 48" fill="none"
40+
xmlns="http://www.w3.org/2000/svg">
41+
<path
42+
d="M24 40.9444C26.123 42.8446 28.9266 44 32 44C38.6274 44 44 38.6274 44 32C44 26.4085 40.1757 21.7102 35 20.3781"
43+
stroke="#000000" stroke-width="4" stroke-linejoin="round" />
44+
<path
45+
d="M13 20.3781C7.82432 21.7102 4 26.4085 4 32C4 38.6274 9.37258 44 16 44C22.6274 44 28 38.6274 28 32C28 30.4506 27.7063 28.9697 27.1716 27.6101"
46+
stroke="#000000" stroke-width="4" stroke-linejoin="round" />
47+
<path
48+
d="M24 28C30.6274 28 36 22.6274 36 16C36 9.37258 30.6274 4 24 4C17.3726 4 12 9.37258 12 16C12 22.6274 17.3726 28 24 28Z"
49+
fill="none" stroke="#000000" stroke-width="4" stroke-linejoin="round" />
50+
</svg>
51+
<span class="lang" data-lang="theme">Theme</span>
52+
</button>
53+
<div id="theme-dropdown" class="hidden absolute right-0 mt-1 w-40 bg-white border border-gray-300 rounded-md shadow-lg z-20">
54+
<div class="py-1">
55+
<button class="theme-option block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left" data-theme="default">
56+
<span class="lang" data-lang="theme_default">Default</span>
57+
</button>
58+
<button class="theme-option block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left" data-theme="elegant">
59+
<span class="lang" data-lang="theme_elegant">Elegant</span>
60+
</button>
61+
<button class="theme-option block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left" data-theme="academic">
62+
<span class="lang" data-lang="theme_academic">Academic</span>
63+
</button>
64+
<button class="theme-option block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 text-left" data-theme="modern">
65+
<span class="lang" data-lang="theme_modern">Modern</span>
66+
</button>
67+
</div>
68+
</div>
69+
</div>
3670
<button id="lang-toggle" class="px-3 py-1 text-sm rounded bg-gray-200 hover:bg-gray-100 cursor-pointer">EN</button>
3771
<label for="upload" class="px-3 py-1 rounded bg-gray-200 hover:bg-gray-100 cursor-pointer flex items-center" title="Upload Markdown file">
3872
<svg class="h-5 w-5 mr-2" viewBox="0 0 48 48" fill="none"
@@ -122,6 +156,16 @@ <h3 class="text-lg leading-6 font-medium text-gray-900 flex items-center" id="mo
122156
</h3>
123157
<div class="mt-3 text-center sm:mt-5">
124158
<form class="mt-2">
159+
<div class="mb-4">
160+
<label for="doc-theme" class="block text-sm font-medium text-gray-700 text-left lang" data-lang="theme">Theme</label>
161+
<select id="doc-theme" name="doc-theme" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
162+
<option value="default" class="lang" data-lang="theme_default">Default</option>
163+
<option value="elegant" class="lang" data-lang="theme_elegant">Elegant</option>
164+
<option value="academic" class="lang" data-lang="theme_academic">Academic</option>
165+
<option value="modern" class="lang" data-lang="theme_modern">Modern</option>
166+
</select>
167+
</div>
168+
125169
<div class="mb-4">
126170
<label for="doc-name" class="block text-sm font-medium text-gray-700 text-left lang" data-lang="filename">Filename</label>
127171
<input type="text" id="doc-name" name="doc-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="Document Name" data-placeholder-lang="document_name">
@@ -179,7 +223,6 @@ <h3 class="text-lg leading-6 font-medium text-gray-900 flex items-center" id="mo
179223
</div>
180224
</div>
181225
</div>
182-
183226
<script type="module" src="/src/main.js"></script>
184227
</body>
185228
</html>

web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
},
1818
"dependencies": {
1919
"clsx": "^2.1.1",
20-
"markdown-docx": "^1.4.3"
20+
"markdown-docx": "^1.5.0"
2121
}
2222
}

web/pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/src/style.css

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,97 @@
11
@import "tailwindcss";
22
@plugin "@tailwindcss/typography";
3+
4+
/* 主题相关的CSS变量和样式 */
5+
#markdown-preview {
6+
--theme-primary: #4472C4;
7+
--theme-heading1: #2F5597;
8+
--theme-heading2: #5B9BD5;
9+
--theme-link: #0563C1;
10+
--theme-code: #C7254E;
11+
}
12+
13+
/* 为预览区域应用主题样式 */
14+
#markdown-preview h1 {
15+
color: var(--theme-heading1) !important;
16+
}
17+
18+
#markdown-preview h2 {
19+
color: var(--theme-heading2) !important;
20+
}
21+
22+
#markdown-preview h3,
23+
#markdown-preview h4,
24+
#markdown-preview h5,
25+
#markdown-preview h6 {
26+
color: var(--theme-primary) !important;
27+
}
28+
29+
#markdown-preview a {
30+
color: var(--theme-link) !important;
31+
}
32+
33+
#markdown-preview code {
34+
color: var(--theme-code) !important;
35+
}
36+
37+
#markdown-preview pre {
38+
border-left: 4px solid var(--theme-primary) !important;
39+
}
40+
41+
#markdown-preview blockquote {
42+
border-left: 4px solid var(--theme-primary) !important;
43+
background-color: rgba(68, 114, 196, 0.05) !important;
44+
}
45+
46+
/* 主题下拉菜单增强样式 */
47+
.theme-option:hover {
48+
background: linear-gradient(90deg, #f3f4f6 0%, #e5e7eb 100%);
49+
}
50+
51+
.theme-option[data-theme="default"]:hover {
52+
background: linear-gradient(90deg, rgba(68, 114, 196, 0.1) 0%, rgba(68, 114, 196, 0.05) 100%);
53+
}
54+
55+
.theme-option[data-theme="elegant"]:hover {
56+
background: linear-gradient(90deg, rgba(26, 54, 93, 0.1) 0%, rgba(26, 54, 93, 0.05) 100%);
57+
}
58+
59+
.theme-option[data-theme="academic"]:hover {
60+
background: linear-gradient(90deg, rgba(30, 64, 175, 0.1) 0%, rgba(30, 64, 175, 0.05) 100%);
61+
}
62+
63+
.theme-option[data-theme="modern"]:hover {
64+
background: linear-gradient(90deg, rgba(139, 92, 246, 0.1) 0%, rgba(139, 92, 246, 0.05) 100%);
65+
}
66+
67+
/* 在主题按钮中添加小色块指示器 */
68+
#theme-toggle::before {
69+
content: '';
70+
display: inline-block;
71+
width: 8px;
72+
height: 8px;
73+
border-radius: 50%;
74+
background-color: var(--theme-primary);
75+
margin-right: 4px;
76+
}
77+
78+
/* 主题切换动画效果 */
79+
#markdown-preview {
80+
transition: all 0.3s ease;
81+
}
82+
83+
#markdown-preview h1,
84+
#markdown-preview h2,
85+
#markdown-preview h3,
86+
#markdown-preview h4,
87+
#markdown-preview h5,
88+
#markdown-preview h6,
89+
#markdown-preview a,
90+
#markdown-preview code {
91+
transition: color 0.3s ease;
92+
}
93+
94+
#markdown-preview blockquote,
95+
#markdown-preview pre {
96+
transition: border-color 0.3s ease, background-color 0.3s ease;
97+
}

web/src/theme-manager.js

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// 主题配置定义
2+
const themeConfigs = {
3+
default: {
4+
name: 'Default',
5+
theme: {
6+
heading1: "2F5597",
7+
heading2: "5B9BD5",
8+
heading3: "44546A",
9+
heading4: "44546A",
10+
heading5: "44546A",
11+
heading6: "44546A",
12+
link: "0563C1",
13+
code: "C7254E",
14+
blockquote: "666666",
15+
del: "FF0000"
16+
}
17+
},
18+
elegant: {
19+
name: 'Elegant',
20+
theme: {
21+
heading1: "1A202C",
22+
heading2: "2D3748",
23+
heading3: "4A5568",
24+
heading4: "4A5568",
25+
heading5: "4A5568",
26+
heading6: "4A5568",
27+
link: "2B6CB0",
28+
code: "B83280",
29+
blockquote: "4A5568",
30+
del: "E53E3E"
31+
}
32+
},
33+
academic: {
34+
name: 'Academic',
35+
theme: {
36+
heading1: "111827",
37+
heading2: "1F2937",
38+
heading3: "374151",
39+
heading4: "374151",
40+
heading5: "374151",
41+
heading6: "374151",
42+
link: "1D4ED8",
43+
code: "7C2D12",
44+
blockquote: "6B7280",
45+
del: "DC2626"
46+
}
47+
},
48+
modern: {
49+
name: 'Modern',
50+
theme: {
51+
heading1: "5B21B6",
52+
heading2: "7C3AED",
53+
heading3: "8B5CF6",
54+
heading4: "8B5CF6",
55+
heading5: "8B5CF6",
56+
heading6: "8B5CF6",
57+
link: "3B82F6",
58+
code: "EC4899",
59+
blockquote: "6B7280",
60+
del: "EF4444"
61+
}
62+
}
63+
};
64+
65+
// 全局主题状态
66+
let currentTheme = 'default';
67+
68+
// 主题切换功能
69+
function switchTheme(themeName) {
70+
if (themeConfigs[themeName]) {
71+
currentTheme = themeName;
72+
localStorage.setItem('markdown-docx-theme', themeName);
73+
74+
// 更新UI显示
75+
updateThemeDisplay(themeName);
76+
77+
// 触发自定义事件通知主题已变更
78+
window.dispatchEvent(new CustomEvent('themeChanged', {
79+
detail: {
80+
theme: themeName,
81+
config: themeConfigs[themeName]
82+
}
83+
}));
84+
}
85+
}
86+
87+
function updateThemeDisplay(themeName) {
88+
const config = themeConfigs[themeName];
89+
const themeButton = document.getElementById('theme-toggle');
90+
const themeSelect = document.getElementById('doc-theme');
91+
92+
// 更新按钮文本
93+
if (themeButton) {
94+
const span = themeButton.querySelector('span');
95+
if (span) span.textContent = config.name;
96+
}
97+
98+
// 更新选择框
99+
if (themeSelect) {
100+
themeSelect.value = themeName;
101+
}
102+
103+
// 在预览区域显示主题色彩示例
104+
updatePreviewTheme(config);
105+
}
106+
107+
function updatePreviewTheme(config) {
108+
// 在markdown预览区域应用主题色彩示例
109+
const preview = document.getElementById('markdown-preview');
110+
if (preview) {
111+
preview.style.setProperty('--theme-primary', `#${config.theme.primary}`);
112+
preview.style.setProperty('--theme-heading1', `#${config.theme.heading1}`);
113+
preview.style.setProperty('--theme-heading2', `#${config.theme.heading2}`);
114+
preview.style.setProperty('--theme-link', `#${config.theme.link}`);
115+
preview.style.setProperty('--theme-code', `#${config.theme.code}`);
116+
}
117+
}
118+
119+
// 获取当前主题配置
120+
function getCurrentTheme() {
121+
return {
122+
name: currentTheme,
123+
config: themeConfigs[currentTheme]
124+
};
125+
}
126+
127+
// 初始化主题功能
128+
document.addEventListener('DOMContentLoaded', function () {
129+
// 从localStorage恢复主题设置
130+
const savedTheme = localStorage.getItem('markdown-docx-theme') || 'default';
131+
currentTheme = savedTheme;
132+
133+
// 初始化UI
134+
updateThemeDisplay(currentTheme);
135+
136+
// 绑定主题切换按钮事件
137+
const themeToggle = document.getElementById('theme-toggle');
138+
const themeDropdown = document.getElementById('theme-dropdown');
139+
140+
if (themeToggle && themeDropdown) {
141+
themeToggle.addEventListener('click', function (e) {
142+
e.stopPropagation();
143+
themeDropdown.classList.toggle('hidden');
144+
});
145+
146+
// 绑定主题选项点击事件
147+
themeDropdown.addEventListener('click', function (e) {
148+
const themeOption = e.target.closest('.theme-option');
149+
if (themeOption) {
150+
const themeName = themeOption.dataset.theme;
151+
switchTheme(themeName);
152+
themeDropdown.classList.add('hidden');
153+
}
154+
});
155+
}
156+
157+
// 绑定模态框中的主题选择器
158+
const themeSelect = document.getElementById('doc-theme');
159+
if (themeSelect) {
160+
themeSelect.addEventListener('change', function () {
161+
switchTheme(this.value);
162+
});
163+
}
164+
165+
// 点击其他地方关闭下拉菜单
166+
document.addEventListener('click', function () {
167+
if (themeDropdown) {
168+
themeDropdown.classList.add('hidden');
169+
}
170+
});
171+
});
172+
173+
export const ThemeManager ={
174+
getCurrentTheme,
175+
switchTheme,
176+
themeConfigs
177+
};

0 commit comments

Comments
 (0)