duqing2026 commited on
Commit
c8092b3
·
1 Parent(s): 233fcae

Initial commit of Mind Map Master

Browse files
Files changed (6) hide show
  1. .gitignore +5 -0
  2. Dockerfile +18 -0
  3. README.md +31 -4
  4. app.py +11 -0
  5. requirements.txt +2 -0
  6. templates/index.html +232 -0
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .DS_Store
4
+ venv/
5
+ .env
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ # Create a user to avoid running as root
11
+ RUN useradd -m -u 1000 user
12
+ USER user
13
+ ENV HOME=/home/user \
14
+ PATH=/home/user/.local/bin:$PATH
15
+
16
+ EXPOSE 7860
17
+
18
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
README.md CHANGED
@@ -1,12 +1,39 @@
1
  ---
2
  title: Mind Map Master
3
- emoji: 🐠
4
- colorFrom: purple
5
  colorTo: indigo
6
  sdk: docker
7
- pinned: false
8
  license: mit
9
  short_description: 思维导图大师
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Mind Map Master
3
+ emoji: 🧠
4
+ colorFrom: blue
5
  colorTo: indigo
6
  sdk: docker
7
+ app_port: 7860
8
  license: mit
9
  short_description: 思维导图大师
10
+ pinned: false
11
  ---
12
 
13
+ # 思维导图大师 (Mind Map Master)
14
+
15
+ 一个简单而强大的在线思维导图生成工具。
16
+ A simple yet powerful online mind map generator.
17
+
18
+ ## 功能特点
19
+
20
+ - **Markdown 驱动**: 使用简单的 Markdown 语法即可生成精美的思维导图。
21
+ - **实时预览**: 左侧编辑,右侧即时渲染。
22
+ - **导出支持**: 支持导出为 SVG 矢量图和 PNG 图片。
23
+ - **完全免费**: 无需注册,即开即用。
24
+ - **本地优先**: 数据完全在浏览器端处理,保护隐私。
25
+
26
+ ## 使用方法
27
+
28
+ 1. 在左侧编辑器中输入 Markdown 文本。
29
+ 2. 使用 `#` 表示一级节点,`##` 表示二级节点,`-` 表示列表项。
30
+ 3. 点击右上角的导出按钮保存你的作品。
31
+
32
+ ## 部署
33
+
34
+ 本项目基于 Flask + Tailwind CSS + Markmap 构建,支持 Docker 部署。
35
+
36
+ ```bash
37
+ docker build -t mind-map-master .
38
+ docker run -p 7860:7860 mind-map-master
39
+ ```
app.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import os
3
+
4
+ app = Flask(__name__)
5
+
6
+ @app.route('/')
7
+ def index():
8
+ return render_template('index.html')
9
+
10
+ if __name__ == '__main__':
11
+ app.run(debug=True, host='0.0.0.0', port=7860)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ flask
2
+ gunicorn
templates/index.html ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>思维导图大师 (Mind Map Master)</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/markmap-view@0.16.0/dist/browser/index.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/markmap-lib@0.16.0/dist/browser/index.js"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
12
+ <style>
13
+ body { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
14
+ .editor-container { height: calc(100vh - 64px); }
15
+ #mindmap { width: 100%; height: 100%; }
16
+ /* Custom scrollbar */
17
+ textarea::-webkit-scrollbar {
18
+ width: 8px;
19
+ }
20
+ textarea::-webkit-scrollbar-track {
21
+ background: #f1f1f1;
22
+ }
23
+ textarea::-webkit-scrollbar-thumb {
24
+ background: #888;
25
+ border-radius: 4px;
26
+ }
27
+ textarea::-webkit-scrollbar-thumb:hover {
28
+ background: #555;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body class="bg-gray-50 h-screen flex flex-col overflow-hidden">
33
+ <!-- Header -->
34
+ <header class="bg-white border-b border-gray-200 h-16 flex items-center justify-between px-6 shadow-sm z-10">
35
+ <div class="flex items-center gap-3">
36
+ <div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white font-bold text-lg">M</div>
37
+ <h1 class="text-xl font-bold text-gray-800">思维导图大师</h1>
38
+ <span class="px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded-full font-medium">Pro</span>
39
+ </div>
40
+ <div class="flex items-center gap-4">
41
+ <button onclick="loadExample()" class="text-gray-600 hover:text-blue-600 font-medium text-sm flex items-center gap-1">
42
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
43
+ 加载示例
44
+ </button>
45
+ <div class="h-6 w-px bg-gray-300"></div>
46
+ <button onclick="downloadSVG()" class="text-gray-600 hover:text-blue-600 font-medium text-sm flex items-center gap-1">
47
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
48
+ 导出 SVG
49
+ </button>
50
+ <button onclick="downloadPNG()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition shadow-sm flex items-center gap-2">
51
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>
52
+ 导出图片
53
+ </button>
54
+ </div>
55
+ </header>
56
+
57
+ <!-- Main Content -->
58
+ <main class="flex-1 flex editor-container">
59
+ <!-- Editor -->
60
+ <div class="w-1/3 border-r border-gray-200 bg-white flex flex-col">
61
+ <div class="p-3 bg-gray-50 border-b border-gray-200 text-xs font-semibold text-gray-500 uppercase tracking-wider flex justify-between items-center">
62
+ <span>Markdown 编辑器</span>
63
+ <span class="text-gray-400">支持快捷键</span>
64
+ </div>
65
+ <textarea id="editor" class="flex-1 w-full p-4 resize-none focus:outline-none font-mono text-sm leading-relaxed text-gray-700" placeholder="# 输入你的思维导图内容..." spellcheck="false"></textarea>
66
+ </div>
67
+
68
+ <!-- Preview -->
69
+ <div class="flex-1 bg-gray-50 relative overflow-hidden">
70
+ <svg id="mindmap" class="w-full h-full"></svg>
71
+ <div class="absolute bottom-4 right-4 bg-white/90 backdrop-blur p-2 rounded-lg shadow border border-gray-200 flex gap-2">
72
+ <button onclick="zoomIn()" class="p-1.5 hover:bg-gray-100 rounded text-gray-600" title="放大">
73
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /></svg>
74
+ </button>
75
+ <button onclick="zoomOut()" class="p-1.5 hover:bg-gray-100 rounded text-gray-600" title="缩小">
76
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" /></svg>
77
+ </button>
78
+ <button onclick="fitView()" class="p-1.5 hover:bg-gray-100 rounded text-gray-600" title="适应屏幕">
79
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 4l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" /></svg>
80
+ </button>
81
+ </div>
82
+ </div>
83
+ </main>
84
+
85
+ <script>
86
+ // Initial Markdown
87
+ const initialMarkdown = `# 思维导图大师
88
+ ## 核心功能
89
+ - ⚡️ 实时渲染
90
+ - 🎨 自动布局
91
+ - 📥 导出 SVG/PNG
92
+ - 📱 响应式设计
93
+ ## 使用指南
94
+ - 使用 Markdown 语法
95
+ - # 一级标题
96
+ - ## 二级标题
97
+ - - 列表项作为节点
98
+ ## 技术栈
99
+ - Flask
100
+ - Tailwind CSS
101
+ - Markmap
102
+ - D3.js`;
103
+
104
+ let mm;
105
+ const transformer = new markmap.Transformer();
106
+ const editor = document.getElementById('editor');
107
+ const svgEl = document.getElementById('mindmap');
108
+
109
+ // Initialize
110
+ function init() {
111
+ const { Markmap } = markmap;
112
+ mm = Markmap.create(svgEl, null, null);
113
+ editor.value = initialMarkdown;
114
+ update();
115
+ }
116
+
117
+ // Update Mindmap
118
+ function update() {
119
+ const markdown = editor.value;
120
+ const { root } = transformer.transform(markdown);
121
+ mm.setData(root);
122
+ mm.fit();
123
+ }
124
+
125
+ // Event Listeners
126
+ editor.addEventListener('input', () => {
127
+ const markdown = editor.value;
128
+ const { root } = transformer.transform(markdown);
129
+ mm.setData(root);
130
+ // Don't refit on every keystroke to keep user context
131
+ });
132
+
133
+ // Load Example
134
+ function loadExample() {
135
+ const examples = [
136
+ `# 商业计划书
137
+ ## 市场分析
138
+ - 目标用户
139
+ - 竞品分析
140
+ - 市场规模
141
+ ## 产品介绍
142
+ - 核心功能
143
+ - 差异化优势
144
+ ## 运营策略
145
+ - 获客渠道
146
+ - 用户留存
147
+ ## 财务预测
148
+ - 成本结构
149
+ - 收入来源`,
150
+ `# 学习路线图
151
+ ## 前端基础
152
+ - HTML5
153
+ - CSS3
154
+ - JavaScript (ES6+)
155
+ ## 框架工具
156
+ - React / Vue
157
+ - Tailwind / Bootstrap
158
+ - Webpack / Vite
159
+ ## 后端知识
160
+ - Node.js
161
+ - Database (SQL/NoSQL)
162
+ - API Design`,
163
+ ];
164
+ const randomExample = examples[Math.floor(Math.random() * examples.length)];
165
+ editor.value = randomExample;
166
+ update();
167
+ mm.fit();
168
+ }
169
+
170
+ // Zoom Controls
171
+ function zoomIn() {
172
+ mm.rescale(1.25);
173
+ }
174
+ function zoomOut() {
175
+ mm.rescale(0.8);
176
+ }
177
+ function fitView() {
178
+ mm.fit();
179
+ }
180
+
181
+ // Export SVG
182
+ function downloadSVG() {
183
+ const svgData = new XMLSerializer().serializeToString(svgEl);
184
+ const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
185
+ const url = URL.createObjectURL(blob);
186
+ const link = document.createElement("a");
187
+ link.href = url;
188
+ link.download = "mindmap.svg";
189
+ document.body.appendChild(link);
190
+ link.click();
191
+ document.body.removeChild(link);
192
+ }
193
+
194
+ // Export PNG
195
+ async function downloadPNG() {
196
+ // Need to convert SVG to Canvas/Image
197
+ // Simple approach: Serialize SVG, draw to canvas
198
+ const svgData = new XMLSerializer().serializeToString(svgEl);
199
+ const canvas = document.createElement("canvas");
200
+ const ctx = canvas.getContext("2d");
201
+ const img = new Image();
202
+
203
+ // Get dimensions
204
+ const bbox = svgEl.getBoundingClientRect();
205
+ canvas.width = bbox.width * 2; // High DPI
206
+ canvas.height = bbox.height * 2;
207
+ ctx.scale(2, 2);
208
+
209
+ // Background
210
+ ctx.fillStyle = "#ffffff"; // Default background
211
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
212
+
213
+ const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
214
+ const url = URL.createObjectURL(svgBlob);
215
+
216
+ img.onload = function() {
217
+ ctx.drawImage(img, 0, 0, bbox.width, bbox.height);
218
+ URL.revokeObjectURL(url);
219
+
220
+ const link = document.createElement("a");
221
+ link.download = "mindmap.png";
222
+ link.href = canvas.toDataURL("image/png");
223
+ link.click();
224
+ };
225
+ img.src = url;
226
+ }
227
+
228
+ // Run init
229
+ init();
230
+ </script>
231
+ </body>
232
+ </html>