Spaces:
Sleeping
Sleeping
Commit ·
6c4baf7
1
Parent(s): 4cbe583
Enhance: Auto-save, Export Image, Footnotes, New Themes
Browse files- static/js/main.js +138 -4
- 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
themeSelect.addEventListener('change', (e) => {
|
| 201 |
currentTheme = e.target.value;
|
| 202 |
render();
|
|
|
|
| 203 |
});
|
| 204 |
|
| 205 |
// Init
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
© 2024 Article Publisher Pro.
|
|
|
|
| 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>© 2024 Article Publisher Pro.</span>
|
| 121 |
+
<span id="auto-save-status" class="text-gray-300">已自动保存</span>
|
| 122 |
</div>
|
| 123 |
</footer>
|
| 124 |
|