Skip to content

Commit e396778

Browse files
authored
Merge pull request #346 from SSShooter/feature/markdown
3 Features
2 parents fdad000 + 7782e7a commit e396778

File tree

13 files changed

+489
-31
lines changed

13 files changed

+489
-31
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mind-elixir",
3-
"version": "5.0.6",
3+
"version": "5.1.0-beta.1",
44
"type": "module",
55
"description": "Mind elixir is a free open source mind map core.",
66
"keywords": [
@@ -95,6 +95,7 @@
9595
"husky": "^8.0.3",
9696
"less": "^4.2.0",
9797
"lint-staged": "^13.3.0",
98+
"marked": "^16.2.0",
9899
"nyc": "^17.1.0",
99100
"prettier": "2.8.4",
100101
"rimraf": "^6.0.1",

pnpm-lock.yaml

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

readme.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ let options = {
184184
return true
185185
},
186186
},
187+
// Custom markdown parser (optional)
188+
// markdown: (text) => customMarkdownParser(text), // provide your own markdown parser function
187189
}
188190

189191
let mind = new MindElixir(options)
@@ -270,6 +272,36 @@ mind.init(data)
270272
mind.refresh(data)
271273
```
272274

275+
### Markdown Support
276+
277+
Mind Elixir supports custom markdown parsing:
278+
279+
```javascript
280+
// Disable markdown (default)
281+
let mind = new MindElixir({
282+
// markdown option omitted - no markdown processing
283+
})
284+
285+
// Use custom markdown parser
286+
let mind = new MindElixir({
287+
markdown: (text) => {
288+
// Your custom markdown implementation
289+
return text
290+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
291+
.replace(/\*(.*?)\*/g, '<em>$1</em>')
292+
.replace(/`(.*?)`/g, '<code>$1</code>')
293+
},
294+
})
295+
296+
// Use any markdown library (e.g., marked, markdown-it, etc.)
297+
import { marked } from 'marked'
298+
let mind = new MindElixir({
299+
markdown: (text) => marked(text),
300+
})
301+
```
302+
303+
For detailed markdown configuration examples, see [docs/markdown-configuration.md](docs/markdown-configuration.md).
304+
273305
### Operation Guards
274306

275307
```javascript

src/dev.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import MindElixir from './index'
33
import example from './exampleData/1'
44
import example2 from './exampleData/2'
55
import example3 from './exampleData/3'
6-
import type { Options, MindElixirData, MindElixirInstance } from './types/index'
6+
import markdownExample from './exampleData/markdown'
7+
import type { Options, MindElixirInstance, NodeObj } from './types/index'
78
import type { Operation } from './utils/pubsub'
89
import style from '../index.css?raw'
910
import katex from '../katex.css?raw'
1011
import { layoutSSR, renderSSRHTML } from './utils/layout-ssr'
1112
import { snapdom } from '@zumer/snapdom'
13+
import type { Tokens } from 'marked'
14+
import { marked } from 'marked'
1215

1316
interface Window {
1417
m?: MindElixirInstance
@@ -17,6 +20,8 @@ interface Window {
1720
downloadPng: ReturnType<typeof download>
1821
downloadSvg: ReturnType<typeof download>
1922
destroy: () => void
23+
testMarkdown: () => void
24+
addMarkdownNode: () => void
2025
}
2126

2227
declare let window: Window
@@ -29,6 +34,34 @@ const options: Options = {
2934
// mouseSelectionButton: 2,
3035
draggable: true,
3136
editable: true,
37+
// Custom markdown parser (user must provide their own implementation)
38+
// markdown: (text: string) => {
39+
// console.log('md process', text)
40+
// // Simple custom markdown implementation
41+
// // Process in correct order: bold first, then italic to avoid conflicts
42+
// return text
43+
// .replace(/\*\*\*(.*?)\*\*\*/g, '<strong><em>$1</em></strong>') // Bold + Italic
44+
// .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // Bold
45+
// .replace(/\*(.*?)\*/g, '<em>$1</em>') // Italic
46+
// .replace(/`(.*?)`/g, '<code>$1</code>') // Inline code
47+
// .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>') // Links
48+
// },
49+
markdown: (text: string) => {
50+
// Configure marked renderer to add target="_blank" to links
51+
const renderer = {
52+
link(token: Tokens.Link) {
53+
const href = token.href || ''
54+
const title = token.title ? ` title="${token.title}"` : ''
55+
const text = token.text || ''
56+
return `<a href="${href}"${title} target="_blank">${text}</a>`
57+
},
58+
}
59+
60+
marked.use({ renderer })
61+
const html = marked(text) as string
62+
return html
63+
},
64+
// To disable markdown, simply omit the markdown option or set it to undefined
3265
// if you set contextMenu to false, you should handle contextmenu event by yourself, e.g. preventDefault
3366
contextMenu: {
3467
focus: true,
@@ -80,7 +113,8 @@ const options: Options = {
80113
let mind = new MindElixir(options)
81114

82115
const data = MindElixir.new('new topic')
83-
mind.init(example)
116+
// mind.init(example)
117+
mind.init(markdownExample)
84118

85119
const m2 = new MindElixir({
86120
el: '#map2',

src/exampleData/markdown.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import type { MindElixirData } from '../index'
2+
3+
/**
4+
* Markdown test example data
5+
* This example demonstrates various markdown syntax features
6+
* that can be rendered in Mind Elixir nodes
7+
*/
8+
const markdownExample: MindElixirData = {
9+
nodeData: {
10+
id: 'markdown-root',
11+
topic: 'Markdown Examples',
12+
children: [
13+
{
14+
topic: '**Text Formatting**',
15+
id: 'markdown-formatting',
16+
direction: 0,
17+
expanded: true,
18+
children: [
19+
{
20+
topic: 'This is **bold** text',
21+
id: 'markdown-bold',
22+
},
23+
{
24+
topic: 'This is *italic* text',
25+
id: 'markdown-italic',
26+
},
27+
{
28+
topic: 'This is ***bold and italic*** text',
29+
id: 'markdown-bold-italic',
30+
},
31+
{
32+
topic: '**Important**: *Always* use proper formatting',
33+
id: 'markdown-mixed-format',
34+
},
35+
],
36+
},
37+
{
38+
topic: '`Code Examples`',
39+
id: 'markdown-code',
40+
direction: 0,
41+
expanded: true,
42+
children: [
43+
{
44+
topic: 'Inline code: `console.log("Hello World")`',
45+
id: 'markdown-inline-code',
46+
},
47+
{
48+
topic: 'Variable: `const name = "Mind Elixir"`',
49+
id: 'markdown-variable',
50+
},
51+
{
52+
topic: 'Function: `function add(a, b) { return a + b; }`',
53+
id: 'markdown-function',
54+
},
55+
{
56+
topic: 'Install package: `npm install mind-elixir`',
57+
id: 'markdown-command',
58+
},
59+
],
60+
},
61+
{
62+
topic: 'Links and References',
63+
id: 'markdown-links',
64+
direction: 1,
65+
expanded: true,
66+
children: [
67+
{
68+
topic: 'Visit [GitHub](https://github.com) for repositories',
69+
id: 'markdown-github-link',
70+
},
71+
{
72+
topic: 'Check out [Mind Elixir](https://github.com/ssshooter/mind-elixir-core)',
73+
id: 'markdown-project-link',
74+
},
75+
{
76+
topic: 'Documentation: [MDN Web Docs](https://developer.mozilla.org)',
77+
id: 'markdown-docs-link',
78+
},
79+
{
80+
topic: 'Learn more at [TypeScript](https://www.typescriptlang.org/)',
81+
id: 'markdown-typescript-link',
82+
},
83+
],
84+
},
85+
{
86+
topic: 'Mixed Content Examples',
87+
id: 'markdown-mixed',
88+
direction: 1,
89+
expanded: true,
90+
children: [
91+
{
92+
topic: '**Important**: Use `npm install` to install packages',
93+
id: 'markdown-mixed-1',
94+
},
95+
{
96+
topic: '*Note*: Check [documentation](https://docs.npmjs.com) for details',
97+
id: 'markdown-mixed-2',
98+
},
99+
{
100+
topic: 'Run `npm start` to **start** the *development* server',
101+
id: 'markdown-mixed-3',
102+
},
103+
{
104+
topic: '**Pro tip**: Use `git commit -m "message"` for [version control](https://git-scm.com)',
105+
id: 'markdown-mixed-4',
106+
},
107+
],
108+
},
109+
{
110+
topic: 'Development Workflow',
111+
id: 'markdown-workflow',
112+
direction: 0,
113+
expanded: true,
114+
children: [
115+
{
116+
topic: '**Step 1**: Clone the [repository](https://github.com/ssshooter/mind-elixir-core)',
117+
id: 'markdown-step-1',
118+
},
119+
{
120+
topic: '**Step 2**: Run `npm install` to install *dependencies*',
121+
id: 'markdown-step-2',
122+
},
123+
{
124+
topic: '**Step 3**: Use `npm run dev` to start **development** mode',
125+
id: 'markdown-step-3',
126+
},
127+
{
128+
topic: '**Step 4**: Edit files and see *live* changes',
129+
id: 'markdown-step-4',
130+
},
131+
],
132+
},
133+
{
134+
topic: 'API Examples',
135+
id: 'markdown-api',
136+
direction: 1,
137+
expanded: true,
138+
children: [
139+
{
140+
topic: 'Create instance: `new MindElixir(options)`',
141+
id: 'markdown-api-create',
142+
},
143+
{
144+
topic: 'Initialize: `mind.init(data)`',
145+
id: 'markdown-api-init',
146+
},
147+
{
148+
topic: 'Add child: `mind.addChild(node)`',
149+
id: 'markdown-api-add',
150+
},
151+
{
152+
topic: 'Export data: `mind.getData()`',
153+
id: 'markdown-api-export',
154+
},
155+
],
156+
},
157+
],
158+
expanded: true,
159+
},
160+
direction: 2,
161+
theme: {
162+
name: 'Latte',
163+
palette: ['#dd7878', '#ea76cb', '#8839ef', '#e64553', '#fe640b', '#df8e1d', '#40a02b', '#209fb5', '#1e66f5', '#7287fd'],
164+
cssVar: {
165+
'--node-gap-x': '30px',
166+
'--node-gap-y': '10px',
167+
'--main-gap-x': '32px',
168+
'--main-gap-y': '12px',
169+
'--root-radius': '30px',
170+
'--main-radius': '20px',
171+
'--root-color': '#ffffff',
172+
'--root-bgcolor': '#4c4f69',
173+
'--root-border-color': 'rgba(0, 0, 0, 0)',
174+
'--main-color': '#444446',
175+
'--main-bgcolor': '#ffffff',
176+
'--topic-padding': '3px',
177+
'--color': '#777777',
178+
'--bgcolor': '#f6f6f6',
179+
'--selected': '#4dc4ff',
180+
'--panel-color': '#444446',
181+
'--panel-bgcolor': '#ffffff',
182+
'--panel-border-color': '#eaeaea',
183+
'--map-padding': '50px 80px',
184+
},
185+
},
186+
}
187+
188+
export default markdownExample

src/index.less

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,18 @@
249249
& > iframe {
250250
pointer-events: auto;
251251
}
252+
& > .text {
253+
display: inline-block;
254+
// Allow links inside markdown text to be clickable
255+
a {
256+
pointer-events: auto;
257+
}
258+
}
252259
& > img {
253260
display: block;
254261
margin-bottom: 8px;
255262
object-fit: cover;
256263
}
257-
& > .text {
258-
display: inline-block;
259-
}
260264
}
261265
.circle {
262266
position: absolute;

0 commit comments

Comments
 (0)