duqing2026 commited on
Commit
6c4baf7
·
1 Parent(s): 4cbe583

Enhance: Auto-save, Export Image, Footnotes, New Themes

Browse files
Files changed (2) hide show
  1. static/js/main.js +138 -4
  2. templates/index.html +14 -2
static/js/main.js CHANGED
@@ -13,6 +13,7 @@ const editor = document.getElementById('editor');
13
  const preview = document.getElementById('preview-container');
14
  const themeSelect = document.getElementById('theme-select');
15
  const wordCount = document.getElementById('word-count');
 
16
 
17
  // Default Content
18
  const defaultContent = `# 欢迎使用 Article Publisher Pro
@@ -25,6 +26,8 @@ const defaultContent = `# 欢迎使用 Article Publisher Pro
25
  - 🎨 **多主题支持**:内置多种排版风格,一键切换
26
  - 🔧 **排版优化**:自动在中英文之间添加空格
27
  - 📋 **一键复制**:完美粘贴到公众号后台,保留所有格式
 
 
28
 
29
  ## 代码演示
30
 
@@ -80,6 +83,28 @@ const themes = {
80
  list: 'margin-bottom: 16px; padding-left: 20px;',
81
  li: 'margin-bottom: 8px; line-height: 2;',
82
  link: 'color: #888; text-decoration: none; border-bottom: 1px solid #ccc;'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
  };
85
 
@@ -132,7 +157,15 @@ function applyTheme() {
132
 
133
  // Pre blocks usually need specific styling too
134
  root.querySelectorAll('pre').forEach(el => {
135
- el.style.cssText = 'background: #282c34; padding: 15px; border-radius: 5px; overflow-x: auto; margin: 20px 0; color: #abb2bf;';
 
 
 
 
 
 
 
 
136
  });
137
  }
138
 
@@ -149,6 +182,77 @@ function formatChinese() {
149
  render();
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  function updateWordCount() {
153
  const text = editor.value;
154
  // Simple word count
@@ -156,10 +260,32 @@ function updateWordCount() {
156
  wordCount.textContent = `${count} 字`;
157
  }
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  function clearEditor() {
160
- if(confirm('确定要清空所有内容吗?')) {
161
  editor.value = '';
162
  render();
 
163
  }
164
  }
165
 
@@ -196,12 +322,20 @@ async function copyToClipboard() {
196
  }
197
 
198
  // Event Listeners
199
- editor.addEventListener('input', render);
 
 
 
 
200
  themeSelect.addEventListener('change', (e) => {
201
  currentTheme = e.target.value;
202
  render();
 
203
  });
204
 
205
  // Init
206
- editor.value = defaultContent;
 
 
 
207
  render();
 
13
  const preview = document.getElementById('preview-container');
14
  const themeSelect = document.getElementById('theme-select');
15
  const wordCount = document.getElementById('word-count');
16
+ const autoSaveStatus = document.getElementById('auto-save-status');
17
 
18
  // Default Content
19
  const defaultContent = `# 欢迎使用 Article Publisher Pro
 
26
  - 🎨 **多主题支持**:内置多种排版风格,一键切换
27
  - 🔧 **排版优化**:自动在中英文之间添加空格
28
  - 📋 **一键复制**:完美粘贴到公众号后台,保留所有格式
29
+ - 🖼️ **长图导出**:支持一键生成文章长图分享
30
+ - 💾 **自动保存**:防止内容丢失
31
 
32
  ## 代码演示
33
 
 
83
  list: 'margin-bottom: 16px; padding-left: 20px;',
84
  li: 'margin-bottom: 8px; line-height: 2;',
85
  link: 'color: #888; text-decoration: none; border-bottom: 1px solid #ccc;'
86
+ },
87
+ dark: {
88
+ h1: 'color: #e2e8f0; border-bottom: 1px solid #475569; padding-bottom: 10px; margin-bottom: 20px; font-size: 1.6em; font-weight: bold;',
89
+ h2: 'color: #93c5fd; margin: 30px 0 15px; font-size: 1.4em; font-weight: bold; border-left: 4px solid #3b82f6; padding-left: 10px;',
90
+ h3: 'color: #e2e8f0; margin: 20px 0 10px; font-size: 1.2em; font-weight: bold;',
91
+ p: 'color: #cbd5e1; margin-bottom: 16px; font-size: 15px; line-height: 1.8;',
92
+ blockquote: 'border-left: 4px solid #475569; padding: 10px 15px; color: #94a3b8; background-color: #1e293b; margin: 20px 0;',
93
+ code: 'background-color: #334155; color: #fca5a5; padding: 2px 4px; border-radius: 4px; font-family: monospace; font-size: 0.9em;',
94
+ list: 'color: #cbd5e1; margin-bottom: 16px; padding-left: 20px;',
95
+ li: 'margin-bottom: 8px; line-height: 1.8;',
96
+ link: 'color: #60a5fa; text-decoration: none;'
97
+ },
98
+ green: {
99
+ h1: 'color: #166534; border-bottom: 2px solid #22c55e; padding-bottom: 10px; margin-bottom: 20px; font-size: 1.6em; font-weight: bold;',
100
+ h2: 'color: #15803d; background: #dcfce7; padding: 5px 15px; border-radius: 4px; margin: 30px 0 15px; font-size: 1.4em; font-weight: bold;',
101
+ h3: 'color: #166534; margin: 20px 0 10px; font-size: 1.2em; font-weight: bold;',
102
+ p: 'color: #374151; margin-bottom: 16px; font-size: 15px; line-height: 1.8;',
103
+ blockquote: 'border-left: 4px solid #86efac; padding: 10px 15px; color: #4b5563; background-color: #f0fdf4; margin: 20px 0;',
104
+ code: 'background-color: #dcfce7; color: #166534; padding: 2px 4px; border-radius: 4px; font-family: monospace; font-size: 0.9em;',
105
+ list: 'color: #374151; margin-bottom: 16px; padding-left: 20px;',
106
+ li: 'margin-bottom: 8px; line-height: 1.8;',
107
+ link: 'color: #15803d; text-decoration: underline;'
108
  }
109
  };
110
 
 
157
 
158
  // Pre blocks usually need specific styling too
159
  root.querySelectorAll('pre').forEach(el => {
160
+ if (currentTheme === 'default' || currentTheme === 'tech') {
161
+ el.style.cssText = 'background: #282c34; padding: 15px; border-radius: 5px; overflow-x: auto; margin: 20px 0; color: #abb2bf;';
162
+ } else if (currentTheme === 'literature') {
163
+ el.style.cssText = 'background: #fdf6e3; padding: 15px; border-radius: 2px; overflow-x: auto; margin: 20px 0; color: #586e75; border: 1px solid #eee;';
164
+ } else if (currentTheme === 'dark') {
165
+ el.style.cssText = 'background: #0f172a; padding: 15px; border-radius: 8px; overflow-x: auto; margin: 20px 0; color: #e2e8f0; border: 1px solid #1e293b;';
166
+ } else if (currentTheme === 'green') {
167
+ el.style.cssText = 'background: #f0fdf4; padding: 15px; border-radius: 8px; overflow-x: auto; margin: 20px 0; color: #166534; border: 1px solid #bbf7d0;';
168
+ }
169
  });
170
  }
171
 
 
182
  render();
183
  }
184
 
185
+ function formatFootnotes() {
186
+ let text = editor.value;
187
+ const footnotes = [];
188
+ let counter = 1;
189
+
190
+ // Replace [^something] with [1] and store content
191
+ // This is a simple regex and might need refinement for complex nested footnotes
192
+ // Regex for matching definition: [^id]: content
193
+ // Regex for matching reference: [^id]
194
+
195
+ // First, find all definitions
196
+ const definitionRegex = /^\[\^([^\]]+)\]:\s*(.*)$/gm;
197
+ let match;
198
+ const definitions = {};
199
+
200
+ while ((match = definitionRegex.exec(text)) !== null) {
201
+ definitions[match[1]] = match[2];
202
+ }
203
+
204
+ // Remove definitions from text
205
+ text = text.replace(definitionRegex, '');
206
+
207
+ // Replace references and build new endnotes
208
+ text = text.replace(/\[\^([^\]]+)\]/g, (match, id) => {
209
+ if (definitions[id]) {
210
+ footnotes.push(definitions[id]);
211
+ return `[${counter++}]`;
212
+ }
213
+ return match;
214
+ });
215
+
216
+ // Append footnotes at the end
217
+ if (footnotes.length > 0) {
218
+ text += '\n\n---\n\n### 参考资料\n\n';
219
+ footnotes.forEach((note, index) => {
220
+ text += `[${index + 1}] ${note}\n`;
221
+ });
222
+ }
223
+
224
+ editor.value = text;
225
+ render();
226
+ }
227
+
228
+ async function exportImage() {
229
+ const btn = document.querySelector('button[onclick="exportImage()"]');
230
+ const originalText = btn.innerHTML;
231
+ btn.innerHTML = '<i class="ri-loader-4-line ri-spin"></i> 生成中...';
232
+
233
+ try {
234
+ const canvas = await html2canvas(preview, {
235
+ useCORS: true,
236
+ scale: 2, // Retina display support
237
+ backgroundColor: '#ffffff'
238
+ });
239
+
240
+ const link = document.createElement('a');
241
+ link.download = `article-publisher-pro-${Date.now()}.png`;
242
+ link.href = canvas.toDataURL('image/png');
243
+ link.click();
244
+
245
+ btn.innerHTML = '<i class="ri-check-line"></i> 下载成功';
246
+ setTimeout(() => {
247
+ btn.innerHTML = originalText;
248
+ }, 2000);
249
+ } catch (err) {
250
+ console.error('Export failed', err);
251
+ alert('生成图片失败,请重试');
252
+ btn.innerHTML = originalText;
253
+ }
254
+ }
255
+
256
  function updateWordCount() {
257
  const text = editor.value;
258
  // Simple word count
 
260
  wordCount.textContent = `${count} 字`;
261
  }
262
 
263
+ // Local Storage Logic
264
+ function saveToLocal() {
265
+ localStorage.setItem('app_content', editor.value);
266
+ localStorage.setItem('app_theme', currentTheme);
267
+ autoSaveStatus.textContent = '已保存 ' + new Date().toLocaleTimeString();
268
+ }
269
+
270
+ function loadFromLocal() {
271
+ const savedContent = localStorage.getItem('app_content');
272
+ const savedTheme = localStorage.getItem('app_theme');
273
+
274
+ if (savedContent) {
275
+ editor.value = savedContent;
276
+ }
277
+
278
+ if (savedTheme && themes[savedTheme]) {
279
+ currentTheme = savedTheme;
280
+ themeSelect.value = savedTheme;
281
+ }
282
+ }
283
+
284
  function clearEditor() {
285
+ if(confirm('确定要清空所有内容吗?此操作不可撤销。')) {
286
  editor.value = '';
287
  render();
288
+ saveToLocal();
289
  }
290
  }
291
 
 
322
  }
323
 
324
  // Event Listeners
325
+ editor.addEventListener('input', () => {
326
+ render();
327
+ saveToLocal();
328
+ });
329
+
330
  themeSelect.addEventListener('change', (e) => {
331
  currentTheme = e.target.value;
332
  render();
333
+ saveToLocal();
334
  });
335
 
336
  // Init
337
+ loadFromLocal();
338
+ if (!editor.value) {
339
+ editor.value = defaultContent;
340
+ }
341
  render();
templates/index.html CHANGED
@@ -6,6 +6,7 @@
6
  <title>全平台文章发布助手 - Article Publisher Pro</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
 
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
  <!-- Icons -->
@@ -62,12 +63,22 @@
62
  <option value="default">默认简洁</option>
63
  <option value="tech">科技极客</option>
64
  <option value="literature">文艺书信</option>
 
 
65
  </select>
66
  </div>
67
 
 
 
 
 
68
  <button onclick="formatChinese()" class="text-gray-600 hover:text-indigo-600 transition" title="中英文自动空格">
69
  <i class="ri-translate-2"></i> 排版优化
70
  </button>
 
 
 
 
71
 
72
  <div class="h-6 w-px bg-gray-300 mx-2"></div>
73
 
@@ -105,8 +116,9 @@
105
 
106
  <!-- Footer -->
107
  <footer class="bg-white border-t border-gray-200 py-2">
108
- <div class="max-w-7xl mx-auto px-4 text-center text-xs text-gray-400">
109
- &copy; 2024 Article Publisher Pro. Designed for Creators.
 
110
  </div>
111
  </footer>
112
 
 
6
  <title>全平台文章发布助手 - Article Publisher Pro</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
11
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
12
  <!-- Icons -->
 
63
  <option value="default">默认简洁</option>
64
  <option value="tech">科技极客</option>
65
  <option value="literature">文艺书信</option>
66
+ <option value="dark">暗夜模式</option>
67
+ <option value="green">护眼森系</option>
68
  </select>
69
  </div>
70
 
71
+ <button onclick="formatFootnotes()" class="text-gray-600 hover:text-indigo-600 transition hidden sm:inline-block" title="注脚转尾注">
72
+ <i class="ri-links-line"></i> 注脚优化
73
+ </button>
74
+
75
  <button onclick="formatChinese()" class="text-gray-600 hover:text-indigo-600 transition" title="中英文自动空格">
76
  <i class="ri-translate-2"></i> 排版优化
77
  </button>
78
+
79
+ <button onclick="exportImage()" class="text-gray-600 hover:text-indigo-600 transition" title="生成长图">
80
+ <i class="ri-image-line"></i> 导出长图
81
+ </button>
82
 
83
  <div class="h-6 w-px bg-gray-300 mx-2"></div>
84
 
 
116
 
117
  <!-- Footer -->
118
  <footer class="bg-white border-t border-gray-200 py-2">
119
+ <div class="max-w-7xl mx-auto px-4 text-center text-xs text-gray-400 flex justify-between items-center">
120
+ <span>&copy; 2024 Article Publisher Pro.</span>
121
+ <span id="auto-save-status" class="text-gray-300">已自动保存</span>
122
  </div>
123
  </footer>
124