@xiangfa/mindmap 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +534 -0
  2. package/README.zh-CN.md +534 -0
  3. package/dist/MindMap.d.ts +3 -0
  4. package/dist/components/MindMapContextMenu.d.ts +20 -0
  5. package/dist/components/MindMapControls.d.ts +16 -0
  6. package/dist/components/MindMapNode.d.ts +32 -0
  7. package/dist/components/icons.d.ts +8 -0
  8. package/dist/esm/MindMap2.js +713 -0
  9. package/dist/esm/components/MindMapContextMenu.js +123 -0
  10. package/dist/esm/components/MindMapControls.js +114 -0
  11. package/dist/esm/components/MindMapNode.js +588 -0
  12. package/dist/esm/components/icons.js +45 -0
  13. package/dist/esm/hooks/useDrag.js +346 -0
  14. package/dist/esm/hooks/useNewNodeAnimation.js +20 -0
  15. package/dist/esm/hooks/useNodeEdit.js +57 -0
  16. package/dist/esm/hooks/usePanZoom.js +85 -0
  17. package/dist/esm/hooks/useTheme.js +16 -0
  18. package/dist/esm/index.js +14 -0
  19. package/dist/esm/logo.svg +9 -0
  20. package/dist/esm/plugins/cross-link.js +65 -0
  21. package/dist/esm/plugins/dotted-line.js +23 -0
  22. package/dist/esm/plugins/folding.js +20 -0
  23. package/dist/esm/plugins/front-matter.js +19 -0
  24. package/dist/esm/plugins/index.js +19 -0
  25. package/dist/esm/plugins/latex.js +132 -0
  26. package/dist/esm/plugins/multi-line.js +39 -0
  27. package/dist/esm/plugins/runner.js +128 -0
  28. package/dist/esm/plugins/tags.js +55 -0
  29. package/dist/esm/style.css +2 -0
  30. package/dist/esm/utils/export.js +50 -0
  31. package/dist/esm/utils/i18n.js +61 -0
  32. package/dist/esm/utils/inline-markdown.js +189 -0
  33. package/dist/esm/utils/layout.js +208 -0
  34. package/dist/esm/utils/markdown.js +288 -0
  35. package/dist/esm/utils/theme.js +119 -0
  36. package/dist/esm/utils/tree-ops.js +136 -0
  37. package/dist/hooks/useDrag.d.ts +40 -0
  38. package/dist/hooks/useNewNodeAnimation.d.ts +2 -0
  39. package/dist/hooks/useNodeEdit.d.ts +17 -0
  40. package/dist/hooks/usePanZoom.d.ts +26 -0
  41. package/dist/hooks/useTheme.d.ts +3 -0
  42. package/dist/index.d.ts +16 -0
  43. package/dist/logo.svg +9 -0
  44. package/dist/mindmap.umd.cjs +24 -0
  45. package/dist/plugins/cross-link.d.ts +2 -0
  46. package/dist/plugins/dotted-line.d.ts +2 -0
  47. package/dist/plugins/folding.d.ts +2 -0
  48. package/dist/plugins/front-matter.d.ts +2 -0
  49. package/dist/plugins/index.d.ts +11 -0
  50. package/dist/plugins/latex.d.ts +20 -0
  51. package/dist/plugins/multi-line.d.ts +2 -0
  52. package/dist/plugins/runner.d.ts +30 -0
  53. package/dist/plugins/tags.d.ts +2 -0
  54. package/dist/plugins/types.d.ts +78 -0
  55. package/dist/style.css +2 -0
  56. package/dist/types.d.ts +105 -0
  57. package/dist/utils/export.d.ts +18 -0
  58. package/dist/utils/i18n.d.ts +22 -0
  59. package/dist/utils/inline-markdown.d.ts +66 -0
  60. package/dist/utils/layout.d.ts +14 -0
  61. package/dist/utils/markdown.d.ts +20 -0
  62. package/dist/utils/theme.d.ts +62 -0
  63. package/dist/utils/tree-ops.d.ts +36 -0
  64. package/package.json +65 -0
@@ -0,0 +1,2 @@
1
+ import type { MindMapPlugin } from './types';
2
+ export declare const multiLinePlugin: MindMapPlugin;
@@ -0,0 +1,30 @@
1
+ import type { MindMapPlugin, ParseContext, ParsedLineResult, LayoutContext } from './types';
2
+ import type { MindMapData, LayoutNode, Edge } from '../types';
3
+ import type { TokenLayout } from '../utils/inline-markdown';
4
+ import type { ThemeColors } from '../utils/theme';
5
+ export declare function runPreParseMarkdown(plugins: MindMapPlugin[], md: string, ctx: ParseContext): string;
6
+ export declare function runParseLine(plugins: MindMapPlugin[], line: string, index: number, ctx: ParseContext): ParsedLineResult | null;
7
+ export declare function runCollectFollowLines(plugins: MindMapPlugin[], lines: string[], startIdx: number, node: MindMapData, ctx: ParseContext): number;
8
+ export declare function runTransformNodeData(plugins: MindMapPlugin[], node: MindMapData, rawText: string, ctx: ParseContext): MindMapData;
9
+ export declare function runPostParseTree(plugins: MindMapPlugin[], roots: MindMapData[], ctx: ParseContext): MindMapData[];
10
+ export declare function runSerializePreamble(plugins: MindMapPlugin[], roots: MindMapData[]): string;
11
+ export declare function runSerializeListMarker(plugins: MindMapPlugin[], node: MindMapData, defaultMarker: string): string;
12
+ export declare function runSerializeNodeText(plugins: MindMapPlugin[], node: MindMapData, baseText: string): string;
13
+ export declare function runSerializeFollowLines(plugins: MindMapPlugin[], node: MindMapData, indent: number): string[];
14
+ export declare function runAdjustNodeSize(plugins: MindMapPlugin[], node: MindMapData, width: number, height: number, fontSize: number): {
15
+ width: number;
16
+ height: number;
17
+ };
18
+ export declare function runFilterChildren(plugins: MindMapPlugin[], node: MindMapData, children: MindMapData[], ctx: LayoutContext): MindMapData[];
19
+ export declare function runTransformEdge(plugins: MindMapPlugin[], edge: Edge, fromNode: LayoutNode, toNode: LayoutNode): Edge;
20
+ export declare function runGenerateExtraEdges(plugins: MindMapPlugin[], nodes: LayoutNode[], roots: MindMapData[], ctx: LayoutContext): Edge[];
21
+ export declare function runTransformNodeColor(plugins: MindMapPlugin[], node: LayoutNode, data: MindMapData, parentColor: string): {
22
+ color?: string;
23
+ bgColor?: string;
24
+ };
25
+ export declare function runRenderNodeDecoration(plugins: MindMapPlugin[], node: LayoutNode, theme: ThemeColors): React.ReactNode[];
26
+ export declare function runRenderInlineToken(plugins: MindMapPlugin[], layout: TokenLayout, key: number): React.ReactNode | null;
27
+ export declare function runRenderOverlay(plugins: MindMapPlugin[], nodes: LayoutNode[], edges: Edge[], theme: ThemeColors): React.ReactNode[];
28
+ export declare function runExportNodeDecoration(plugins: MindMapPlugin[], node: LayoutNode, theme: ThemeColors, allPlugins?: MindMapPlugin[], pngSafe?: boolean): string;
29
+ export declare function runExportInlineToken(plugins: MindMapPlugin[], layout: TokenLayout, pngSafe?: boolean): string | null;
30
+ export declare function runExportOverlay(plugins: MindMapPlugin[], nodes: LayoutNode[], edges: Edge[], theme: ThemeColors): string;
@@ -0,0 +1,2 @@
1
+ import type { MindMapPlugin } from './types';
2
+ export declare const tagsPlugin: MindMapPlugin;
@@ -0,0 +1,78 @@
1
+ import type { MindMapData, LayoutNode, Edge, LayoutDirection } from "../types";
2
+ import type { InlineToken, TokenLayout } from "../utils/inline-markdown";
3
+ import type { ThemeColors } from "../utils/theme";
4
+ export interface ParseContext {
5
+ lines: string[];
6
+ frontMatter: Record<string, string>;
7
+ }
8
+ export interface LayoutContext {
9
+ direction: LayoutDirection;
10
+ theme: ThemeColors;
11
+ readonly: boolean;
12
+ foldOverrides: Record<string, boolean>;
13
+ }
14
+ export interface ParsedLineResult {
15
+ indent: number;
16
+ text: string;
17
+ taskStatus?: import("../types").TaskStatus;
18
+ dottedLine?: boolean;
19
+ collapsed?: boolean;
20
+ }
21
+ export interface MindMapPlugin {
22
+ name: string;
23
+ /** Pre-process entire markdown string (e.g. extract frontmatter) */
24
+ preParseMarkdown?(md: string, ctx: ParseContext): string;
25
+ /** Match a non-standard line marker (-. or +). Return null to let core handle it. */
26
+ parseLine?(line: string, index: number, ctx: ParseContext): ParsedLineResult | null;
27
+ /** Collect follow-up lines after a node (e.g. | text). Return count of consumed lines. */
28
+ collectFollowLines?(lines: string[], startIdx: number, node: MindMapData, ctx: ParseContext): number;
29
+ /** Transform node data: extract #tags, @color(), {#id}, etc. from text into fields */
30
+ transformNodeData?(node: MindMapData, rawText: string, ctx: ParseContext): MindMapData;
31
+ /** Post-process the fully-built tree (e.g. resolve cross-link anchor references) */
32
+ postParseTree?(roots: MindMapData[], ctx: ParseContext): MindMapData[];
33
+ /** Emit text before the markdown body (e.g. frontmatter block) */
34
+ serializePreamble?(roots: MindMapData[]): string;
35
+ /** Override list marker for a node. Default is '- '. */
36
+ serializeListMarker?(node: MindMapData, defaultMarker: string): string;
37
+ /** Transform node text when serializing (e.g. re-attach #tags, @color(), {#id}) */
38
+ serializeNodeText?(node: MindMapData, baseText: string): string;
39
+ /** Emit extra lines after a node line (e.g. | text). Returns array of line contents (caller adds indent). */
40
+ serializeFollowLines?(node: MindMapData, indent: number): string[];
41
+ /** Additional regex pattern to splice into the inline markdown parser */
42
+ inlineTokenPattern?(): {
43
+ pattern: string;
44
+ priority: number;
45
+ };
46
+ /** Create an InlineToken from a regex match produced by this plugin's pattern */
47
+ createInlineToken?(match: RegExpExecArray, groupOffset: number): InlineToken | null;
48
+ /** Adjust a node's measured dimensions */
49
+ adjustNodeSize?(node: MindMapData, width: number, height: number, fontSize: number): {
50
+ width: number;
51
+ height: number;
52
+ };
53
+ /** Filter children before layout (folding plugin hides collapsed children) */
54
+ filterChildren?(node: MindMapData, children: MindMapData[], ctx: LayoutContext): MindMapData[];
55
+ /** Modify an edge's properties (e.g. set strokeDasharray for dotted lines) */
56
+ transformEdge?(edge: Edge, fromNode: LayoutNode, toNode: LayoutNode): Edge;
57
+ /** Produce additional edges after tree layout (e.g. cross-links) */
58
+ generateExtraEdges?(nodes: LayoutNode[], roots: MindMapData[], ctx: LayoutContext): Edge[];
59
+ /** Override node color (e.g. decorator plugin sets custom branch color) */
60
+ transformNodeColor?(node: LayoutNode, data: MindMapData, parentColor: string): {
61
+ color?: string;
62
+ bgColor?: string;
63
+ };
64
+ /** Render additional SVG elements on a node */
65
+ renderNodeDecoration?(node: LayoutNode, theme: ThemeColors): React.ReactNode;
66
+ /** Render an inline token introduced by this plugin */
67
+ renderInlineToken?(layout: TokenLayout, key: number): React.ReactNode;
68
+ /** Render an SVG overlay layer (e.g. cross-link arrows) */
69
+ renderOverlay?(nodes: LayoutNode[], edges: Edge[], theme: ThemeColors): React.ReactNode;
70
+ /** Build additional SVG string for a node during export. pngSafe=true means no foreignObject. */
71
+ exportNodeDecoration?(node: LayoutNode, theme: ThemeColors, plugins?: MindMapPlugin[], pngSafe?: boolean): string;
72
+ /** Build SVG tspan string for a custom inline token. pngSafe=true means no foreignObject. */
73
+ exportInlineToken?(layout: TokenLayout, pngSafe?: boolean): string;
74
+ /** Build SVG string for overlay elements during export */
75
+ exportOverlay?(nodes: LayoutNode[], edges: Edge[], theme: ThemeColors): string;
76
+ /** Load KaTex styles */
77
+ loadKatexStyle?(): void;
78
+ }
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .mindmap-container{width:100%;height:100%;position:relative;overflow:hidden}.mindmap-svg{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none;width:100%;height:100%;display:block}.mindmap-svg:focus{outline:none}.mindmap-svg.dragging-canvas,.mindmap-svg.dragging-node{cursor:grabbing}.mindmap-node-animated{transition:transform .3s ease-out}.mindmap-edge-animated{transition:d .3s ease-out}@keyframes mindmap-node-appear{0%{opacity:0;transform:scale(.85)}to{opacity:1;transform:scale(1)}}.mindmap-node-new{animation:.3s ease-out mindmap-node-appear}@keyframes mindmap-expand-appear{0%{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.mindmap-node-expanding{animation:.3s ease-out both mindmap-expand-appear}@keyframes mindmap-edge-draw{0%{stroke-dashoffset:300px}to{stroke-dashoffset:0}}.mindmap-edge-expanding{stroke-dasharray:300;animation:.3s ease-out both mindmap-edge-draw}.mindmap-edit-input{text-align:center;box-sizing:border-box;background:0 0;border:none;outline:none;width:100%;height:100%;margin:0;padding:0}.mindmap-edit-root{color:#fff;background:0 0}.mindmap-edit-child{color:inherit;background:0 0;border-radius:4px}.mindmap-zoom-controls{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:8px;align-items:center;gap:2px;padding:4px;display:flex;position:absolute;bottom:16px;left:16px;box-shadow:0 2px 8px #0000001a}.mindmap-ctrl-btn{cursor:pointer;width:32px;height:32px;color:inherit;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;transition:background .15s,color .15s;display:flex}.mindmap-ctrl-btn:hover{background:#80808033}.mindmap-ctrl-pct{cursor:pointer;min-width:48px;height:32px;color:inherit;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;font-family:system-ui,sans-serif;font-size:13px;font-weight:500;transition:background .15s;display:flex}.mindmap-ctrl-pct:hover{background:#80808033}.mindmap-extra-controls{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:20;border-radius:8px;align-items:center;gap:2px;padding:4px;display:flex;position:absolute;bottom:16px;right:16px;box-shadow:0 2px 8px #0000001a}.mindmap-text-editor{resize:none;box-sizing:border-box;z-index:10;border:none;outline:none;width:100%;height:100%;padding:24px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:15px;line-height:1.7;position:absolute;inset:0}.mindmap-add-btn{opacity:0;cursor:pointer;transition:opacity .2s}.mindmap-node-g:hover .mindmap-add-btn{opacity:1}.mindmap-context-menu{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);z-index:1000;border:1px solid;border-radius:8px;min-width:150px;padding:4px 0;font-family:system-ui,-apple-system,sans-serif;position:absolute}.mindmap-ctx-item{cursor:pointer;white-space:nowrap;justify-content:space-between;align-items:center;padding:8px 16px;font-size:14px;display:flex;position:relative}.mindmap-ctx-item:hover{filter:brightness(.92);background:#8080801a}.mindmap-ctx-arrow{opacity:.5;margin-left:12px;font-size:8px}.mindmap-ctx-divider{opacity:.3;border-top:1px solid;height:0;margin:4px 8px}.mindmap-ctx-submenu{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid;border-radius:8px;min-width:140px;padding:4px 0;position:absolute;top:-4px;left:100%}.mindmap-ctx-has-sub{position:relative}.mindmap-dialog-backdrop{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:2000;background:#0003;justify-content:center;align-items:center;font-family:system-ui,-apple-system,sans-serif;animation:.15s ease-out mindmap-fade-in;display:flex;position:absolute;inset:0}@keyframes mindmap-fade-in{0%{opacity:0}to{opacity:1}}.mindmap-dialog-modal{border:1px solid;border-radius:12px;width:calc(100% - 32px);max-width:480px;max-height:80%;padding:24px;animation:.2s ease-out mindmap-dialog-enter;position:relative;overflow-y:auto;box-shadow:0 8px 32px #0003}@keyframes mindmap-dialog-enter{0%{opacity:0;transform:scale(.95)translateY(8px)}to{opacity:1;transform:scale(1)translateY(0)}}.mindmap-dialog-header{justify-content:space-between;align-items:center;margin-bottom:16px;display:flex}.mindmap-dialog-title{margin:0;font-size:16px;font-weight:600}.mindmap-dialog-close{cursor:pointer;width:28px;height:28px;color:inherit;opacity:.5;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;transition:opacity .15s,background .15s;display:flex}.mindmap-dialog-close:hover{opacity:1;background:#80808033}@media (width<=500px){.mindmap-dialog-modal{max-height:90%;padding:16px}}@media (width<=768px){.mindmap-ctrl-btn{width:28px;height:28px}.mindmap-ctrl-btn svg{width:14px;height:14px}.mindmap-ctrl-pct{min-width:40px;height:28px;font-size:12px}.mindmap-zoom-controls,.mindmap-extra-controls{gap:1px;padding:3px;bottom:12px}.mindmap-zoom-controls{left:12px}.mindmap-extra-controls{right:12px}}.mindmap-node-content{white-space:nowrap;box-sizing:border-box;justify-content:center;align-items:center;gap:4px;line-height:1;display:flex;overflow:hidden}.mindmap-node-root-content{line-height:1}.mindmap-node-content strong{font-weight:700}.mindmap-node-content em{font-style:italic}.mindmap-node-content del{opacity:.6;text-decoration:line-through}.mindmap-node-content .mindmap-inline-code{background:#8080801f;border-radius:3px;padding:1px 4px;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:.88em}.mindmap-node-content .mindmap-highlight{color:#fac800;background:#fcd34d33;border-radius:2px;padding:1px 2px}.mindmap-node-content .mindmap-link{color:#2563eb;cursor:pointer;text-decoration:none}.mindmap-node-content .mindmap-link:hover{text-decoration:underline}.mindmap-node-content .mindmap-inline-image{vertical-align:middle;border-radius:2px;max-width:80px;max-height:1.2em}.mindmap-node-root-content .mindmap-inline-code{background:#ffffff26}.mindmap-node-root-content .mindmap-highlight{color:#fcd34d;background:#fbbf2433}.mindmap-node-root-content .mindmap-link{color:#93c5fd}.mindmap-task-icon{vertical-align:middle;flex-shrink:0}.mindmap-remark-indicator{opacity:.5;cursor:help;flex-shrink:0;font-size:.7em;line-height:1;transition:opacity .15s}.mindmap-remark-indicator:hover{opacity:1}.mindmap-remark-tooltip{white-space:pre-wrap;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);z-index:1000;pointer-events:none;border-radius:8px;max-width:280px;padding:8px 12px;font-family:system-ui,-apple-system,sans-serif;font-size:13px;line-height:1.5;animation:.15s ease-out mindmap-fade-in;position:absolute;box-shadow:0 4px 16px #00000026}
2
+ /*$vite$:1*/
@@ -0,0 +1,105 @@
1
+ export type TaskStatus = 'todo' | 'doing' | 'done';
2
+ export interface CrossLink {
3
+ targetAnchorId: string;
4
+ label?: string;
5
+ dotted?: boolean;
6
+ }
7
+ export interface MindMapData {
8
+ id: string;
9
+ text: string;
10
+ children?: MindMapData[];
11
+ remark?: string;
12
+ taskStatus?: TaskStatus;
13
+ dottedLine?: boolean;
14
+ multiLineContent?: string[];
15
+ tags?: string[];
16
+ anchorId?: string;
17
+ crossLinks?: CrossLink[];
18
+ collapsed?: boolean;
19
+ }
20
+ export type LayoutDirection = 'left' | 'right' | 'both';
21
+ export type ThemeMode = 'light' | 'dark' | 'auto';
22
+ export interface ToolbarConfig {
23
+ zoom?: boolean;
24
+ }
25
+ export interface LayoutNode {
26
+ id: string;
27
+ text: string;
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ color: string;
33
+ depth: number;
34
+ side: 'left' | 'right' | 'root';
35
+ parentId?: string;
36
+ remark?: string;
37
+ taskStatus?: TaskStatus;
38
+ dottedLine?: boolean;
39
+ multiLineContent?: string[];
40
+ tags?: string[];
41
+ anchorId?: string;
42
+ crossLinks?: CrossLink[];
43
+ collapsed?: boolean;
44
+ }
45
+ export interface Edge {
46
+ key: string;
47
+ path: string;
48
+ color: string;
49
+ fromId: string;
50
+ toId: string;
51
+ strokeDasharray?: string;
52
+ label?: string;
53
+ isCrossLink?: boolean;
54
+ }
55
+ export type MindMapEvent = {
56
+ type: 'nodeAdd';
57
+ node: MindMapData;
58
+ parentId: string | null;
59
+ } | {
60
+ type: 'nodeDelete';
61
+ nodeId: string;
62
+ } | {
63
+ type: 'nodeTextChange';
64
+ nodeId: string;
65
+ oldText: string;
66
+ newText: string;
67
+ } | {
68
+ type: 'nodeSelect';
69
+ nodeId: string | null;
70
+ } | {
71
+ type: 'modeChange';
72
+ mode: 'view' | 'text';
73
+ } | {
74
+ type: 'directionChange';
75
+ direction: LayoutDirection;
76
+ } | {
77
+ type: 'zoomChange';
78
+ zoom: number;
79
+ } | {
80
+ type: 'fullscreenChange';
81
+ fullscreen: boolean;
82
+ };
83
+ export interface MindMapProps {
84
+ data?: MindMapData | MindMapData[];
85
+ markdown?: string;
86
+ defaultDirection?: LayoutDirection;
87
+ theme?: ThemeMode;
88
+ locale?: string;
89
+ messages?: Partial<import('./utils/i18n').MindMapMessages>;
90
+ readonly?: boolean;
91
+ toolbar?: boolean | ToolbarConfig;
92
+ onDataChange?: (data: MindMapData[]) => void;
93
+ onEvent?: (event: MindMapEvent) => void;
94
+ plugins?: import('./plugins/types').MindMapPlugin[];
95
+ }
96
+ export interface MindMapRef {
97
+ exportToSVG(): string;
98
+ exportToPNG(): Promise<Blob>;
99
+ exportToOutline(): string;
100
+ getData(): MindMapData[];
101
+ setData(data: MindMapData | MindMapData[]): void;
102
+ setMarkdown(md: string): void;
103
+ fitView(): void;
104
+ setDirection(dir: LayoutDirection): void;
105
+ }
@@ -0,0 +1,18 @@
1
+ import type { LayoutNode, Edge } from '../types';
2
+ import type { ThemeColors } from './theme';
3
+ import type { MindMapPlugin } from '../plugins/types';
4
+ interface ExportOptions {
5
+ padding?: number;
6
+ scale?: number;
7
+ background?: string;
8
+ /** When true, avoid foreignObject elements (for PNG canvas export) */
9
+ pngSafe?: boolean;
10
+ }
11
+ /**
12
+ * Build SVG string for export. Uses pure SVG elements with inline styles.
13
+ * Works for both SVG file export and PNG conversion.
14
+ */
15
+ export declare function buildExportSVG(nodes: LayoutNode[], edges: Edge[], options?: ExportOptions, theme?: ThemeColors, plugins?: MindMapPlugin[]): string;
16
+ export declare const buildExportSVGForPNG: typeof buildExportSVG;
17
+ export declare function exportToPNG(svgString: string, options?: ExportOptions): Promise<Blob>;
18
+ export {};
@@ -0,0 +1,22 @@
1
+ export interface MindMapMessages {
2
+ newNode: string;
3
+ zoomIn: string;
4
+ zoomOut: string;
5
+ resetView: string;
6
+ layoutLeft: string;
7
+ layoutBoth: string;
8
+ layoutRight: string;
9
+ textMode: string;
10
+ viewMode: string;
11
+ fullscreen: string;
12
+ exitFullscreen: string;
13
+ newRootNode: string;
14
+ export: string;
15
+ exportSVG: string;
16
+ exportPNG: string;
17
+ exportMarkdown: string;
18
+ layout: string;
19
+ close: string;
20
+ }
21
+ export declare function detectLocale(): string;
22
+ export declare function resolveMessages(locale?: string, overrides?: Partial<MindMapMessages>): MindMapMessages;
@@ -0,0 +1,66 @@
1
+ import type { MindMapPlugin } from "../plugins/types";
2
+ export type InlineToken = {
3
+ type: "text";
4
+ content: string;
5
+ } | {
6
+ type: "bold";
7
+ content: string;
8
+ } | {
9
+ type: "italic";
10
+ content: string;
11
+ } | {
12
+ type: "strikethrough";
13
+ content: string;
14
+ } | {
15
+ type: "code";
16
+ content: string;
17
+ } | {
18
+ type: "highlight";
19
+ content: string;
20
+ } | {
21
+ type: "link";
22
+ text: string;
23
+ url: string;
24
+ } | {
25
+ type: "image";
26
+ alt: string;
27
+ url: string;
28
+ } | {
29
+ type: "latex-inline";
30
+ content: string;
31
+ } | {
32
+ type: "latex-block";
33
+ content: string;
34
+ };
35
+ /**
36
+ * Parse inline markdown text into tokens.
37
+ * Priority: image > link > code > bold > italic > strikethrough > highlight
38
+ * Plugins can inject additional patterns (e.g. LaTeX $...$).
39
+ */
40
+ export declare function parseInlineMarkdown(text: string, plugins?: MindMapPlugin[]): InlineToken[];
41
+ /**
42
+ * Strip all inline markdown markers, returning plain text for measurement.
43
+ */
44
+ export declare function stripInlineMarkdown(text: string): string;
45
+ export interface TokenLayout {
46
+ token: InlineToken;
47
+ x: number;
48
+ width: number;
49
+ }
50
+ /**
51
+ * Compute per-token layout (x offset and width) for SVG rendering.
52
+ */
53
+ export declare function computeTokenLayouts(tokens: InlineToken[], fontSize: number, fontWeight: number, fontFamily: string): TokenLayout[];
54
+ declare function escapeXml(str: string): string;
55
+ export { escapeXml };
56
+ /**
57
+ * Build SVG elements string for a node's formatted text content.
58
+ * Returns background rects + text element + remark indicator as SVG string.
59
+ * All styles are inline SVG attributes — no CSS dependencies.
60
+ */
61
+ export declare function buildSvgNodeTextString(text: string, fontSize: number, fontWeight: number, fontFamily: string, textColor: string, taskStatus: string | undefined, remarkText: string | undefined, plugins?: MindMapPlugin[], highlightTextColor?: string, highlightBgColor?: string, pngSafe?: boolean): string;
62
+ /**
63
+ * Build SVG string for a single line of inline-markdown-formatted text at a given y coordinate.
64
+ * Used by plugins (e.g. multi-line) to render additional text lines during export.
65
+ */
66
+ export declare function buildSvgTextLineString(text: string, fontSize: number, fontWeight: number, fontFamily: string, textColor: string, y: number, plugins?: MindMapPlugin[], highlightTextColor?: string, highlightBgColor?: string, opacity?: number): string;
@@ -0,0 +1,14 @@
1
+ import type { MindMapData, LayoutNode, Edge, LayoutDirection } from '../types';
2
+ import type { MindMapPlugin } from '../plugins/types';
3
+ export declare function computeEdgePath(fromX: number, fromY: number, fromW: number, toX: number, toY: number, toW: number, side: 'left' | 'right' | 'root'): string;
4
+ export declare function layoutMindMap(data: MindMapData, direction?: LayoutDirection, colorMap?: Record<string, string>, splitIndex?: number, plugins?: MindMapPlugin[], readonly?: boolean, foldOverrides?: Record<string, boolean>): {
5
+ nodes: LayoutNode[];
6
+ edges: Edge[];
7
+ };
8
+ /**
9
+ * Layout multiple independent root trees, stacked vertically.
10
+ */
11
+ export declare function layoutMultiRoot(roots: MindMapData[], direction?: LayoutDirection, colorMap?: Record<string, string>, splitIndices?: Record<string, number>, plugins?: MindMapPlugin[], readonly?: boolean, foldOverrides?: Record<string, boolean>): {
12
+ nodes: LayoutNode[];
13
+ edges: Edge[];
14
+ };
@@ -0,0 +1,20 @@
1
+ import type { MindMapData } from '../types';
2
+ import type { MindMapPlugin } from '../plugins/types';
3
+ export declare function parseMarkdownList(md: string, plugins?: MindMapPlugin[]): MindMapData;
4
+ export declare function toMarkdownList(data: MindMapData, indent?: number, plugins?: MindMapPlugin[]): string;
5
+ /**
6
+ * Parse markdown with multiple root trees separated by blank lines.
7
+ */
8
+ export declare function parseMarkdownMultiRoot(md: string, plugins?: MindMapPlugin[]): MindMapData[];
9
+ /**
10
+ * Convert multiple root trees to markdown, separated by blank lines.
11
+ */
12
+ export declare function toMarkdownMultiRoot(roots: MindMapData[], plugins?: MindMapPlugin[]): string;
13
+ /**
14
+ * Get frontMatter from a parsed markdown context.
15
+ * Call this after parseMarkdownMultiRoot to retrieve frontmatter config.
16
+ */
17
+ export declare function parseMarkdownWithFrontMatter(md: string, plugins: MindMapPlugin[]): {
18
+ roots: MindMapData[];
19
+ frontMatter: Record<string, string>;
20
+ };
@@ -0,0 +1,62 @@
1
+ export declare const BRANCH_COLORS: string[];
2
+ export interface ThemeColors {
3
+ root: {
4
+ fontSize: number;
5
+ fontWeight: number;
6
+ fontFamily: string;
7
+ paddingH: number;
8
+ paddingV: number;
9
+ bgColor: string;
10
+ textColor: string;
11
+ };
12
+ node: {
13
+ fontSize: number;
14
+ fontWeight: number;
15
+ fontFamily: string;
16
+ paddingH: number;
17
+ paddingV: number;
18
+ textColor: string;
19
+ };
20
+ level1: {
21
+ fontSize: number;
22
+ fontWeight: number;
23
+ };
24
+ connection: {
25
+ strokeWidth: number;
26
+ };
27
+ layout: {
28
+ horizontalGap: number;
29
+ verticalGap: number;
30
+ };
31
+ canvas: {
32
+ bgColor: string;
33
+ };
34
+ controls: {
35
+ bgColor: string;
36
+ textColor: string;
37
+ hoverBg: string;
38
+ activeBg: string;
39
+ };
40
+ contextMenu: {
41
+ bgColor: string;
42
+ textColor: string;
43
+ hoverBg: string;
44
+ borderColor: string;
45
+ shadowColor: string;
46
+ };
47
+ addBtn: {
48
+ fill: string;
49
+ hoverFill: string;
50
+ iconColor: string;
51
+ };
52
+ selection: {
53
+ strokeColor: string;
54
+ fillColor: string;
55
+ };
56
+ highlight: {
57
+ textColor: string;
58
+ bgColor: string;
59
+ };
60
+ }
61
+ export declare function getTheme(mode: "light" | "dark"): ThemeColors;
62
+ export declare const THEME: ThemeColors;
@@ -0,0 +1,36 @@
1
+ import type { MindMapData } from '../types';
2
+ export declare function generateId(): string;
3
+ export declare function normalizeData(data: MindMapData | MindMapData[]): MindMapData[];
4
+ export declare function updateNodeText(node: MindMapData, id: string, text: string): MindMapData;
5
+ export declare function updateNodeFields(node: MindMapData, id: string, fields: Partial<Pick<MindMapData, 'text' | 'taskStatus' | 'remark'>>): MindMapData;
6
+ export declare function addChild(node: MindMapData, parentId: string, child: MindMapData): MindMapData;
7
+ export declare function removeNode(node: MindMapData, targetId: string): MindMapData;
8
+ export declare function swapSiblings(node: MindMapData, id1: string, id2: string): MindMapData;
9
+ export declare function findSubtree(node: MindMapData, targetId: string): MindMapData | null;
10
+ export declare function regenerateIds(node: MindMapData): MindMapData;
11
+ export declare function getDescendantIds(nodeId: string, nodes: {
12
+ id: string;
13
+ parentId?: string;
14
+ }[]): string[];
15
+ export declare function updateNodeTextMulti(roots: MindMapData[], id: string, text: string): MindMapData[];
16
+ export declare function updateNodeFieldsMulti(roots: MindMapData[], id: string, fields: Partial<Pick<MindMapData, 'text' | 'taskStatus' | 'remark'>>): MindMapData[];
17
+ export declare function addChildMulti(roots: MindMapData[], parentId: string, child: MindMapData): MindMapData[];
18
+ export declare function removeNodeMulti(roots: MindMapData[], targetId: string): MindMapData[];
19
+ export declare function swapSiblingsMulti(roots: MindMapData[], id1: string, id2: string): MindMapData[];
20
+ export declare function findSubtreeMulti(roots: MindMapData[], targetId: string): MindMapData | null;
21
+ /**
22
+ * Add a child to a specific side of a root node's children.
23
+ * Returns the updated root and the new splitIndex.
24
+ */
25
+ export declare function addChildToSide(root: MindMapData, child: MindMapData, side: 'left' | 'right', splitIndex: number): {
26
+ data: MindMapData;
27
+ newSplitIndex: number;
28
+ };
29
+ /**
30
+ * Move a direct child of root from one side to the other.
31
+ * Returns the updated root and new splitIndex, or null if already on target side.
32
+ */
33
+ export declare function moveChildToSide(root: MindMapData, childId: string, toSide: 'left' | 'right', splitIndex: number): {
34
+ data: MindMapData;
35
+ newSplitIndex: number;
36
+ } | null;
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@xiangfa/mindmap",
3
+ "description": "A beautiful, interactive mind map component for React.",
4
+ "private": false,
5
+ "version": "0.3.0",
6
+ "type": "module",
7
+ "main": "dist/mindmap.umd.cjs",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "sideEffects": [
11
+ "*.css",
12
+ "./dist/esm/plugins/latex.js"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/esm/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/mindmap.umd.cjs"
23
+ }
24
+ },
25
+ "./style.css": "./dist/style.css"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "dev": "vite",
32
+ "build": "tsc -b && vite build",
33
+ "build:lib": "rm -rf dist && vite build --mode lib && tsc -p tsconfig.lib.json",
34
+ "lint": "eslint .",
35
+ "preview": "vite preview"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=18",
39
+ "react-dom": ">=18",
40
+ "katex": ">=0.16"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "katex": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "dependencies": {
48
+ "react": "^19.2.4",
49
+ "react-dom": "^19.2.4"
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.39.4",
53
+ "@types/node": "^24.12.0",
54
+ "@types/react": "^19.2.14",
55
+ "@types/react-dom": "^19.2.3",
56
+ "@vitejs/plugin-react": "^6.0.0",
57
+ "eslint": "^9.39.4",
58
+ "eslint-plugin-react-hooks": "^7.0.1",
59
+ "eslint-plugin-react-refresh": "^0.5.2",
60
+ "globals": "^17.4.0",
61
+ "typescript": "~5.9.3",
62
+ "typescript-eslint": "^8.56.1",
63
+ "vite": "^8.0.0"
64
+ }
65
+ }