@umbraci/jsmind 0.9.8 → 0.9.10

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 (50) hide show
  1. package/dist/jsmind.draggable-node.js +9 -0
  2. package/dist/jsmind.draggable-node.js.map +1 -0
  3. package/dist/jsmind.history.js +9 -0
  4. package/dist/jsmind.history.js.map +1 -0
  5. package/dist/jsmind.js +9 -0
  6. package/dist/jsmind.js.map +1 -0
  7. package/es/jsmind.draggable-node.js +9 -0
  8. package/es/jsmind.draggable-node.js.map +1 -0
  9. package/es/jsmind.history.js +9 -0
  10. package/es/jsmind.history.js.map +1 -0
  11. package/es/jsmind.js +9 -0
  12. package/es/jsmind.js.map +1 -0
  13. package/{es6/jsmind.multiline-text.esm.js → es/jsmind.multiline-text.js} +1 -1
  14. package/es/jsmind.multiline-text.js.map +1 -0
  15. package/{es6/jsmind.screenshot.esm.js → es/jsmind.screenshot.js} +1 -1
  16. package/es/jsmind.screenshot.js.map +1 -0
  17. package/lib/jsmind.draggable-node.js +9 -0
  18. package/lib/jsmind.draggable-node.js.map +1 -0
  19. package/lib/jsmind.history.js +9 -0
  20. package/lib/jsmind.history.js.map +1 -0
  21. package/{es6 → lib}/jsmind.js +1 -1
  22. package/lib/jsmind.js.map +1 -0
  23. package/lib/jsmind.multiline-text.js +9 -0
  24. package/lib/jsmind.multiline-text.js.map +1 -0
  25. package/lib/jsmind.screenshot.js +9 -0
  26. package/lib/jsmind.screenshot.js.map +1 -0
  27. package/package.json +27 -12
  28. package/types/generated/jsmind.common.d.ts +4 -2
  29. package/types/generated/jsmind.d.ts +42 -1
  30. package/types/generated/jsmind.dom.d.ts +2 -2
  31. package/types/generated/jsmind.enhanced-plugin.d.ts +103 -0
  32. package/types/generated/plugins/history/history-diff.d.ts +55 -0
  33. package/types/generated/plugins/history/jsmind.history.d.ts +84 -0
  34. package/types/generated/plugins/jsmind.draggable-node.d.ts +38 -6
  35. package/types/generated/plugins/jsmind.multiline-text-v2.d.ts +58 -0
  36. package/es6/README-en.md +0 -37
  37. package/es6/README.md +0 -34
  38. package/es6/jsmind.draggable-node.esm.js +0 -9
  39. package/es6/jsmind.draggable-node.esm.js.map +0 -1
  40. package/es6/jsmind.draggable-node.js +0 -9
  41. package/es6/jsmind.draggable-node.js.map +0 -1
  42. package/es6/jsmind.esm.js +0 -9
  43. package/es6/jsmind.esm.js.map +0 -1
  44. package/es6/jsmind.js.map +0 -1
  45. package/es6/jsmind.multiline-text.esm.js.map +0 -1
  46. package/es6/jsmind.screenshot.esm.js.map +0 -1
  47. /package/{es6 → dist}/jsmind.multiline-text.js +0 -0
  48. /package/{es6 → dist}/jsmind.multiline-text.js.map +0 -0
  49. /package/{es6 → dist}/jsmind.screenshot.js +0 -0
  50. /package/{es6 → dist}/jsmind.screenshot.js.map +0 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license BSD-3-Clause
3
+ * @copyright 2014-2025 hizzgdev@163.com
4
+ *
5
+ * Project Home:
6
+ * https://github.com/hizzgdev/jsmind/
7
+ */
8
+ "use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}Object.defineProperty(exports,"__esModule",{value:!0});var t=e(require("@umbraci/jsmind"));if(!t.default)throw new Error("jsMind is not defined");const n=t.default.$,i={text_width:200,line_height:"1.5"};function o(e={}){const t=Object.assign({},i,e);return function(e,n,i){return!(!i.topic||!i.topic.includes("\n"))&&(n.style.whiteSpace="pre-wrap",n.style.wordBreak="break-word",n.style.maxWidth=t.text_width+"px",n.textContent=i.topic,!0)}}function d(e,o){console.log("[Multiline Plugin] Initializing...",o);const d=Object.assign({},i,o),l=e.view;let r=null,a=null;function s(){if(!r||!a)return;const e=r._data.view.element;a.parentNode&&a.parentNode.removeChild(a),e.style.zIndex="auto",r=null,l.editing_node=null,a=null}l.opts.custom_node_render&&(l.render_node=l._custom_node_render.bind(l),console.log("[Multiline Plugin] Re-bound view.render_node")),e.mind&&e.mind.root&&(!function(e){const t=e.view,n=e.mind;if(!n||!n.root)return;const i=[];for(const e in n.nodes){const t=n.nodes[e];t._data&&t._data.view&&t._data.view.element&&i.push(t)}for(const e of i){const n=e._data.view.element;t.render_node(n,e)}for(const t of i)if(e.layout.is_visible(t)){const e=t._data.view.element;t._data.view.width=e.clientWidth,t._data.view.height=e.clientHeight}e.layout.layout(),e.view.show(!1)}(e),console.log("[Multiline Plugin] Re-rendered all nodes")),l.edit_node_begin=function(e){if(console.log("[Multiline Plugin] edit_node_begin called",e),!e.topic)return;r&&l.edit_node_end(),r=e,l.editing_node=e;const t=n.c("div");t.contentEditable="plaintext-only",t.className="jsmind-multiline-editor",t.textContent=e.topic,a=t;const i=e._data.view.element;Object.assign(t.style,{width:"auto",minHeight:i.clientHeight+"px",lineHeight:d.line_height,border:"none",outline:"none",whiteSpace:"pre-wrap",wordBreak:"break-word",boxSizing:"border-box",overflow:"hidden"});const o=()=>{t.style.height="auto",t.style.height=t.scrollHeight+"px"};n.on(t,"input",o),setTimeout(o,0),n.on(t,"keydown",e=>{"Enter"!==e.key||e.shiftKey?"Escape"===e.key?(e.preventDefault(),e.stopPropagation(),function(){if(!r||!a)return;const e=r;s(),l.render_node(e._data.view.element,e),l.e_panel.focus()}()):"Tab"===e.key&&(e.preventDefault(),e.stopPropagation(),l.edit_node_end()):(e.preventDefault(),e.stopPropagation(),l.edit_node_end())}),n.on(t,"blur",()=>{setTimeout(()=>{r&&l.edit_node_end()},100)}),i.innerHTML="",i.appendChild(t),i.style.zIndex=5,t.focus();const u=n.d.createRange();u.selectNodeContents(t);const c=n.w.getSelection();c.removeAllRanges(),c.addRange(u)},l.edit_node_end=function(){if(!r||!a)return;const n=r,i=(a.textContent||"").trim().replace(/\r\n/g,"\n").replace(/\r/g,"\n").replace(/\n{3,}/g,"\n\n");s(),t.default.util.text.is_empty(i)||n.topic===i?l.render_node(n._data.view.element,n):e.update_node(n.id,i),l.e_panel.focus()}}t.default.register_plugin(new t.default.plugin("multiline_text",d));var l={name:"multiline_text",init:d,createMultilineRender:o};exports.createMultilineRender=o,exports.default=l;
9
+ //# sourceMappingURL=jsmind.multiline-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsmind.multiline-text.js","sources":["../src/plugins/jsmind.multiline-text.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/hizzgdev/jsmind/\n */\n\nimport jsMind from '@umbraci/jsmind';\n\nif (!jsMind) {\n throw new Error('jsMind is not defined');\n}\n\nconst $ = jsMind.$;\n\n/**\n * Multiline Text Plugin\n * Provides multiline text support for jsMind nodes\n */\n\n/**\n * Default plugin options\n * @typedef {Object} MultilineTextOptions\n * @property {number} text_width - Maximum width for multiline text nodes (default: 200)\n * @property {string} line_height - Line height for text (default: '1.5')\n */\nconst DEFAULT_OPTIONS = {\n text_width: 200,\n line_height: '1.5',\n};\n\n/**\n * Create a custom node render function for multiline text\n * @param {MultilineTextOptions} [options={}] - Plugin options\n * @param {number} [options.text_width=200] - Maximum width for multiline text nodes\n * @param {string} [options.line_height='1.5'] - Line height for text\n * @returns {function(jsMind, HTMLElement, Node): boolean} Custom render function\n * @example\n * const options = {\n * view: {\n * custom_node_render: createMultilineRender({\n * text_width: 250,\n * line_height: '1.6',\n * })\n * }\n * };\n */\nexport function createMultilineRender(options = {}) {\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\n\n return function (jm, element, node) {\n if (node.topic && node.topic.includes('\\n')) {\n // Multiline text - apply styles BEFORE setting content\n element.style.whiteSpace = 'pre-wrap';\n element.style.wordBreak = 'break-word';\n element.style.maxWidth = opts.text_width + 'px';\n element.textContent = node.topic;\n return true;\n }\n // Single line text - use default render\n return false;\n };\n}\n\n/**\n * Re-render all nodes to apply multiline styles and recalculate sizes\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @private\n */\nfunction _rerender_all_nodes(jm) {\n const view = jm.view;\n const mind = jm.mind;\n\n if (!mind || !mind.root) {\n return;\n }\n\n // Collect all nodes to update\n const nodesToUpdate = [];\n for (const nodeId in mind.nodes) {\n const node = mind.nodes[nodeId];\n if (node._data && node._data.view && node._data.view.element) {\n nodesToUpdate.push(node);\n }\n }\n\n // Batch render nodes (only update DOM, no layout trigger)\n for (const node of nodesToUpdate) {\n const element = node._data.view.element;\n view.render_node(element, node);\n }\n\n // Batch update node sizes (read all sizes at once to avoid layout thrashing)\n for (const node of nodesToUpdate) {\n if (jm.layout.is_visible(node)) {\n const element = node._data.view.element;\n node._data.view.width = element.clientWidth;\n node._data.view.height = element.clientHeight;\n }\n }\n\n // Finally recalculate layout and show (only trigger reflow/repaint once)\n jm.layout.layout();\n jm.view.show(false);\n}\n\n/**\n * Plugin initialization function\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @param {MultilineTextOptions} options - Plugin options\n * @private\n */\nfunction init(jm, options) {\n console.log('[Multiline Plugin] Initializing...', options);\n\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\n const view = jm.view;\n\n // Plugin state\n let editing_node = null;\n let multiline_editor = null;\n\n // IMPORTANT: Re-set view.render_node to use custom render\n // Because ViewProvider constructor already set it based on options.custom_node_render\n // We need to ensure it uses the custom render function\n if (view.opts.custom_node_render) {\n view.render_node = view._custom_node_render.bind(view);\n console.log('[Multiline Plugin] Re-bound view.render_node');\n }\n\n // Re-render all nodes to apply multiline styles\n if (jm.mind && jm.mind.root) {\n _rerender_all_nodes(jm);\n console.log('[Multiline Plugin] Re-rendered all nodes');\n }\n\n /**\n * Begin editing a node with multiline support\n * @param {import('../jsmind.node.js').Node} node\n */\n view.edit_node_begin = function (node) {\n console.log('[Multiline Plugin] edit_node_begin called', node);\n if (!node.topic) {\n return;\n }\n\n // End editing if another node is being edited\n if (editing_node) {\n view.edit_node_end();\n }\n\n editing_node = node;\n view.editing_node = node;\n\n // Create editor (div with contentEditable)\n const editor = $.c('div');\n editor.contentEditable = 'plaintext-only';\n editor.className = 'jsmind-multiline-editor';\n editor.textContent = node.topic;\n multiline_editor = editor;\n\n // Get element and set editor styles to match\n const element = node._data.view.element;\n\n // Set editor styles to match element width, auto-expand height\n Object.assign(editor.style, {\n width: 'auto',\n minHeight: element.clientHeight + 'px',\n lineHeight: opts.line_height,\n border: 'none',\n outline: 'none',\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n boxSizing: 'border-box',\n overflow: 'hidden',\n });\n\n // Auto-expand height on input\n const autoExpand = () => {\n editor.style.height = 'auto';\n editor.style.height = editor.scrollHeight + 'px';\n };\n $.on(editor, 'input', autoExpand);\n // Initial expand\n setTimeout(autoExpand, 0);\n\n // Keyboard events\n $.on(editor, 'keydown', e => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n e.stopPropagation(); // Prevent jsMind shortcut from triggering\n view.edit_node_end();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n cancel_editing();\n } else if (e.key === 'Tab') {\n e.preventDefault();\n e.stopPropagation();\n view.edit_node_end();\n }\n });\n\n // Blur event - save on blur\n $.on(editor, 'blur', () => {\n setTimeout(() => {\n if (editing_node) {\n view.edit_node_end();\n }\n }, 100);\n });\n\n // Replace node content and focus\n element.innerHTML = '';\n element.appendChild(editor);\n element.style.zIndex = 5;\n editor.focus();\n\n // Select all text\n const range = $.d.createRange();\n range.selectNodeContents(editor);\n const selection = $.w.getSelection();\n selection.removeAllRanges();\n selection.addRange(range);\n };\n\n /**\n * End editing and save changes\n */\n view.edit_node_end = function () {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const node = editing_node;\n const topic = (multiline_editor.textContent || '')\n .trim()\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n');\n\n // Clean up editor\n cleanup_editor();\n\n // Update node if content changed\n if (!jsMind.util.text.is_empty(topic) && node.topic !== topic) {\n jm.update_node(node.id, topic);\n } else {\n view.render_node(node._data.view.element, node);\n }\n\n // Focus panel\n view.e_panel.focus();\n };\n\n /**\n * Cancel editing without saving changes\n */\n function cancel_editing() {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const node = editing_node;\n\n // Clean up editor\n cleanup_editor();\n\n // Re-render node\n view.render_node(node._data.view.element, node);\n\n // Focus panel\n view.e_panel.focus();\n }\n\n /**\n * Clean up editor and reset state\n */\n function cleanup_editor() {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const element = editing_node._data.view.element;\n\n // Remove editor\n if (multiline_editor.parentNode) {\n multiline_editor.parentNode.removeChild(multiline_editor);\n }\n\n // Reset styles and state\n element.style.zIndex = 'auto';\n editing_node = null;\n view.editing_node = null;\n multiline_editor = null;\n }\n}\n\n// Register plugin\njsMind.register_plugin(new jsMind.plugin('multiline_text', init));\n\n// Export for ES6 modules\nexport default {\n name: 'multiline_text',\n init,\n createMultilineRender,\n};\n"],"names":["jsMind","Error","$","DEFAULT_OPTIONS","text_width","line_height","createMultilineRender","options","opts","Object","assign","jm","element","node","topic","includes","style","whiteSpace","wordBreak","maxWidth","textContent","init","console","log","view","editing_node","multiline_editor","cleanup_editor","_data","parentNode","removeChild","zIndex","custom_node_render","render_node","_custom_node_render","bind","mind","root","nodesToUpdate","nodeId","nodes","push","layout","is_visible","width","clientWidth","height","clientHeight","show","_rerender_all_nodes","edit_node_begin","edit_node_end","editor","c","contentEditable","className","minHeight","lineHeight","border","outline","boxSizing","overflow","autoExpand","scrollHeight","on","setTimeout","e","key","shiftKey","preventDefault","stopPropagation","e_panel","focus","cancel_editing","innerHTML","appendChild","range","d","createRange","selectNodeContents","selection","w","getSelection","removeAllRanges","addRange","trim","replace","util","text","is_empty","update_node","id","register_plugin","plugin","jsmind_multilineText","name"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license BSD-3-Clause
3
+ * @copyright 2014-2025 hizzgdev@163.com
4
+ *
5
+ * Project Home:
6
+ * https://github.com/hizzgdev/jsmind/
7
+ */
8
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@umbraci/jsmind"),t=require("dom-to-image");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=i(e),r=i(t);if(!n.default)throw new Error("jsMind is not defined");if(!r.default)throw new Error("dom-to-image is required");const o=n.default.$,s={filename:null,watermark:{left:o.w.location,right:"https://github.com/UmbraCi/jsmind"},background:"transparent"};class a{constructor(e,t){var i={};n.default.util.json.merge(i,s),n.default.util.json.merge(i,t),this.version="0.2.0",this.jm=e,this.options=i,this.dpr=e.view.device_pixel_ratio}shoot(){let e=this.create_canvas(),t=e.getContext("2d");t.scale(this.dpr,this.dpr),Promise.resolve(t).then(()=>this.draw_background(t)).then(()=>this.draw_lines(t)).then(()=>this.draw_nodes(t)).then(()=>this.draw_watermark(e,t)).then(()=>this.download(e)).then(()=>this.clear(e))}create_canvas(){let e=o.c("canvas");const t=this.jm.view.size.w,i=this.jm.view.size.h;return e.width=t*this.dpr,e.height=i*this.dpr,e.style.width=t+"px",e.style.height=i+"px",e.style.visibility="hidden",this.jm.view.e_panel.appendChild(e),e}clear(e){e.parentNode.removeChild(e)}draw_background(e){return new Promise(function(t,i){const n=this.options.background;n&&"transparent"!==n&&(e.fillStyle=this.options.background,e.fillRect(0,0,this.jm.view.size.w,this.jm.view.size.h)),t(e)}.bind(this))}draw_lines(e){return new Promise(function(t,i){this.jm.view.graph.copy_to(e,function(){t(e)})}.bind(this))}draw_nodes(e){return r.default.toSvg(this.jm.view.e_nodes,{style:{zoom:1}}).then(this.load_image).then(function(t){return e.drawImage(t,0,0),e})}draw_watermark(e,t){return t.textBaseline="bottom",t.fillStyle="#000",t.font="11px Verdana,Arial,Helvetica,sans-serif",this.options.watermark.left&&(t.textAlign="left",t.fillText(this.options.watermark.left,5.5,e.height-2.5)),this.options.watermark.right&&(t.textAlign="right",t.fillText(this.options.watermark.right,e.width-5.5,e.height-2.5)),t}load_image(e){return new Promise(function(t,i){let n=new Image;n.onload=function(){t(n)},n.onerror=i,n.src=e})}download(e){var t=(this.options.filename||this.jm.mind.name)+".png";if(navigator.msSaveBlob&&e.msToBlob){var i=e.msToBlob();navigator.msSaveBlob(i,t)}else{var n=e.toDataURL(),r=o.c("a");if("download"in r){r.style.visibility="hidden",r.href=n,r.download=t,o.d.body.appendChild(r);var s=o.d.createEvent("MouseEvents");s.initEvent("click",!0,!0),r.dispatchEvent(s),o.d.body.removeChild(r)}else location.href=n}}}const l=new n.default.plugin("screenshot",function(e,t){var i=new a(e,t);e.screenshot=i,e.shoot=function(){i.shoot()}});n.default.register_plugin(l),exports.JmScreenshot=a,exports.default=a,exports.screenshot_plugin=l;
9
+ //# sourceMappingURL=jsmind.screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsmind.screenshot.js","sources":["../src/plugins/jsmind.screenshot.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport jsMind from '@umbraci/jsmind';\nimport domtoimage from 'dom-to-image';\n\nif (!jsMind) {\n throw new Error('jsMind is not defined');\n}\n\nif (!domtoimage) {\n throw new Error('dom-to-image is required');\n}\n\nconst $ = jsMind.$;\n\n/**\n * Default options for screenshot plugin.\n * @typedef {Object} ScreenshotOptions\n * @property {string|null} [filename]\n * @property {{left?:string|Location,right?:string}} [watermark]\n * @property {string} [background]\n */\nconst DEFAULT_OPTIONS = {\n filename: null,\n watermark: {\n left: $.w.location,\n right: 'https://github.com/UmbraCi/jsmind',\n },\n background: 'transparent',\n};\n\n/**\n * Screenshot plugin for jsMind.\n */\nexport class JmScreenshot {\n /**\n * Create screenshot plugin instance.\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @param {Partial<ScreenshotOptions>} options - Plugin options\n */\n constructor(jm, options) {\n var opts = {};\n jsMind.util.json.merge(opts, DEFAULT_OPTIONS);\n jsMind.util.json.merge(opts, options);\n\n this.version = '0.2.0';\n /** @type {import('../jsmind.js').default} */\n this.jm = jm;\n /** @type {ScreenshotOptions} */\n this.options = opts;\n /** @type {number} */\n this.dpr = jm.view.device_pixel_ratio;\n }\n\n /** Take a screenshot of the mind map. */\n shoot() {\n let c = this.create_canvas();\n let ctx = c.getContext('2d');\n ctx.scale(this.dpr, this.dpr);\n Promise.resolve(ctx)\n .then(() => this.draw_background(ctx))\n .then(() => this.draw_lines(ctx))\n .then(() => this.draw_nodes(ctx))\n .then(() => this.draw_watermark(c, ctx))\n .then(() => this.download(c))\n .then(() => this.clear(c));\n }\n\n /**\n * Create canvas for screenshot.\n * @returns {HTMLCanvasElement} Canvas element\n */\n create_canvas() {\n let c = $.c('canvas');\n const w = this.jm.view.size.w;\n const h = this.jm.view.size.h;\n c.width = w * this.dpr;\n c.height = h * this.dpr;\n c.style.width = w + 'px';\n c.style.height = h + 'px';\n\n c.style.visibility = 'hidden';\n this.jm.view.e_panel.appendChild(c);\n return c;\n }\n\n /**\n * Clean up canvas element.\n * @param {HTMLCanvasElement} c - Canvas to remove\n */\n clear(c) {\n c.parentNode.removeChild(c);\n }\n\n /**\n * Draw background on canvas.\n * @param {CanvasRenderingContext2D} ctx - Canvas context\n * @returns {Promise<CanvasRenderingContext2D>} Promise resolving to context\n */\n draw_background(ctx) {\n return new Promise(\n function (resolve, _) {\n const bg = this.options.background;\n if (!!bg && bg !== 'transparent') {\n ctx.fillStyle = this.options.background;\n ctx.fillRect(0, 0, this.jm.view.size.w, this.jm.view.size.h);\n }\n resolve(ctx);\n }.bind(this)\n );\n }\n\n /**\n * Draw connection lines on canvas by copying from view graph.\n * @param {CanvasRenderingContext2D} ctx\n * @returns {Promise<CanvasRenderingContext2D>}\n */\n draw_lines(ctx) {\n return new Promise(\n function (resolve, _) {\n this.jm.view.graph.copy_to(ctx, function () {\n resolve(ctx);\n });\n }.bind(this)\n );\n }\n\n /**\n * Draw node DOM into canvas via SVG snapshot.\n * @param {CanvasRenderingContext2D} ctx\n * @returns {Promise<CanvasRenderingContext2D>}\n */\n draw_nodes(ctx) {\n return domtoimage\n .toSvg(this.jm.view.e_nodes, { style: { zoom: 1 } })\n .then(this.load_image)\n .then(function (img) {\n ctx.drawImage(img, 0, 0);\n return ctx;\n });\n }\n\n /**\n * Draw watermark text on canvas.\n * @param {HTMLCanvasElement} c\n * @param {CanvasRenderingContext2D} ctx\n * @returns {CanvasRenderingContext2D}\n */\n draw_watermark(c, ctx) {\n ctx.textBaseline = 'bottom';\n ctx.fillStyle = '#000';\n ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';\n if (!!this.options.watermark.left) {\n ctx.textAlign = 'left';\n ctx.fillText(this.options.watermark.left, 5.5, c.height - 2.5);\n }\n if (!!this.options.watermark.right) {\n ctx.textAlign = 'right';\n ctx.fillText(this.options.watermark.right, c.width - 5.5, c.height - 2.5);\n }\n return ctx;\n }\n\n /**\n * Load image from URL and resolve img element.\n * @param {string} url\n * @returns {Promise<HTMLImageElement>}\n */\n load_image(url) {\n return new Promise(function (resolve, reject) {\n let img = new Image();\n img.onload = function () {\n resolve(img);\n };\n img.onerror = reject;\n img.src = url;\n });\n }\n\n /**\n * Trigger download of canvas content as PNG.\n * @param {HTMLCanvasElement} c\n */\n download(c) {\n var name = (this.options.filename || this.jm.mind.name) + '.png';\n\n if (navigator.msSaveBlob && !!c.msToBlob) {\n var blob = c.msToBlob();\n navigator.msSaveBlob(blob, name);\n } else {\n var blob_url = c.toDataURL();\n var anchor = $.c('a');\n if ('download' in anchor) {\n anchor.style.visibility = 'hidden';\n anchor.href = blob_url;\n anchor.download = name;\n $.d.body.appendChild(anchor);\n var evt = $.d.createEvent('MouseEvents');\n evt.initEvent('click', true, true);\n anchor.dispatchEvent(evt);\n $.d.body.removeChild(anchor);\n } else {\n location.href = blob_url;\n }\n }\n }\n}\n\n/**\n * Screenshot plugin registration.\n * @type {import('../jsmind.plugin.js').Plugin<Partial<ScreenshotOptions>>}\n */\nexport const screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) {\n var jmss = new JmScreenshot(jm, options);\n jm.screenshot = jmss;\n jm.shoot = function () {\n jmss.shoot();\n };\n});\n\njsMind.register_plugin(screenshot_plugin);\n\nexport default JmScreenshot;\n"],"names":["jsMind","Error","domtoimage","$","DEFAULT_OPTIONS","filename","watermark","left","w","location","right","background","JmScreenshot","constructor","jm","options","opts","util","json","merge","this","version","dpr","view","device_pixel_ratio","shoot","c","create_canvas","ctx","getContext","scale","Promise","resolve","then","draw_background","draw_lines","draw_nodes","draw_watermark","download","clear","size","h","width","height","style","visibility","e_panel","appendChild","parentNode","removeChild","_","bg","fillStyle","fillRect","bind","graph","copy_to","toSvg","e_nodes","zoom","load_image","img","drawImage","textBaseline","font","textAlign","fillText","url","reject","Image","onload","onerror","src","name","mind","navigator","msSaveBlob","msToBlob","blob","blob_url","toDataURL","anchor","href","d","body","evt","createEvent","initEvent","dispatchEvent","screenshot_plugin","plugin","jmss","screenshot","register_plugin"],"mappings":"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"}
package/package.json CHANGED
@@ -1,30 +1,38 @@
1
1
  {
2
2
  "name": "@umbraci/jsmind",
3
- "version": "0.9.8",
3
+ "version": "0.9.10",
4
4
  "description": "jsMind is a pure javascript library for mindmap, it base on html5 canvas. jsMind was released under BSD license, you can embed it in any project, if only you observe the license.",
5
- "main": "es6/jsmind.js",
5
+ "main": "lib/jsmind.js",
6
+ "module": "es/jsmind.js",
7
+ "unpkg": "dist/jsmind.js",
8
+ "jsdelivr": "dist/jsmind.js",
6
9
  "types": "types/generated/index.d.ts",
7
10
  "exports": {
8
11
  ".": {
9
- "import": "./es6/jsmind.esm.js",
10
- "require": "./es6/jsmind.js",
12
+ "import": "./es/jsmind.js",
13
+ "require": "./lib/jsmind.js",
11
14
  "types": "./types/generated/index.d.ts"
12
15
  },
13
16
  "./draggable-node": {
14
- "import": "./es6/jsmind.draggable-node.esm.js",
15
- "require": "./es6/jsmind.draggable-node.js",
17
+ "import": "./es/jsmind.draggable-node.js",
18
+ "require": "./lib/jsmind.draggable-node.js",
16
19
  "types": "./types/generated/plugins/jsmind.draggable-node.d.ts"
17
20
  },
18
21
  "./screenshot": {
19
- "import": "./es6/jsmind.screenshot.esm.js",
20
- "require": "./es6/jsmind.screenshot.js",
22
+ "import": "./es/jsmind.screenshot.js",
23
+ "require": "./lib/jsmind.screenshot.js",
21
24
  "types": "./types/generated/plugins/jsmind.screenshot.d.ts"
22
25
  },
23
26
  "./multiline-text": {
24
- "import": "./es6/jsmind.multiline-text.esm.js",
25
- "require": "./es6/jsmind.multiline-text.js",
27
+ "import": "./es/jsmind.multiline-text.js",
28
+ "require": "./lib/jsmind.multiline-text.js",
26
29
  "types": "./types/generated/plugins/jsmind.multiline-text.d.ts"
27
30
  },
31
+ "./history": {
32
+ "import": "./es/jsmind.history.js",
33
+ "require": "./lib/jsmind.history.js",
34
+ "types": "./types/generated/plugins/jsmind.history.d.ts"
35
+ },
28
36
  "./style/jsmind.css": "./style/jsmind.css"
29
37
  },
30
38
  "directories": {
@@ -32,7 +40,9 @@
32
40
  "example": "example"
33
41
  },
34
42
  "files": [
35
- "es6",
43
+ "es",
44
+ "lib",
45
+ "dist",
36
46
  "style",
37
47
  "types",
38
48
  "LICENSE",
@@ -72,6 +82,8 @@
72
82
  }
73
83
  ],
74
84
  "devDependencies": {
85
+ "@rollup/plugin-commonjs": "^28.0.8",
86
+ "@rollup/plugin-node-resolve": "^16.0.3",
75
87
  "@rollup/plugin-terser": "^0.4.4",
76
88
  "http-server": "^14.1.1",
77
89
  "jest": "^28.1.0",
@@ -88,5 +100,8 @@
88
100
  },
89
101
  "sideEffects": [
90
102
  "./es6/*"
91
- ]
103
+ ],
104
+ "dependencies": {
105
+ "fast-equals": "^5.3.2"
106
+ }
92
107
  }
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @license BSD
3
- * @copyright 2014-2025 hizzgdev@163.com
3
+ * @copyright 2014-2025 UmbraCi
4
4
  *
5
5
  * Project Home:
6
- * https://github.com/hizzgdev/jsmind/
6
+ * https://github.com/UmbraCi/jsmind/
7
7
  */
8
8
  /**
9
9
  * Library version string.
@@ -27,6 +27,8 @@ export namespace EventType {
27
27
  let resize: number;
28
28
  let edit: number;
29
29
  let select: number;
30
+ let reset: number;
31
+ let history_change: number;
30
32
  }
31
33
  export type Key = number;
32
34
  export namespace Key {
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * jsMind runtime: orchestrates data/layout/view/shortcut and exposes public API.
7
7
  */
8
- export default class jsMind {
8
+ declare class jsMind {
9
9
  static mind: typeof Mind;
10
10
  static node: typeof Node;
11
11
  static direction: import("./jsmind.common.js").DirectionType;
@@ -14,6 +14,8 @@ export default class jsMind {
14
14
  resize: number;
15
15
  edit: number;
16
16
  select: number;
17
+ reset: number;
18
+ history_change: number;
17
19
  };
18
20
  static $: {
19
21
  w: Window;
@@ -44,6 +46,22 @@ export default class jsMind {
44
46
  is_empty: (s?: string) => boolean;
45
47
  };
46
48
  };
49
+ static enhanced_plugin: typeof EnhancedPlugin;
50
+ /** @type {Array<import('./jsmind.enhanced-plugin.js').PluginDescriptor>} */
51
+ static enhancedPluginList: Array<import("./jsmind.enhanced-plugin.js").PluginDescriptor>;
52
+ /**
53
+ * Register an enhanced plugin
54
+ * @param {typeof EnhancedPlugin} PluginClass - Plugin class
55
+ * @param {object} [options={}] - Plugin options
56
+ * @returns {typeof jsMind}
57
+ */
58
+ static usePlugin(PluginClass: typeof EnhancedPlugin, options?: object): typeof jsMind;
59
+ /**
60
+ * Check if an enhanced plugin is registered
61
+ * @param {typeof EnhancedPlugin} PluginClass - Plugin class
62
+ * @returns {boolean}
63
+ */
64
+ static hasEnhancedPlugin(PluginClass: typeof EnhancedPlugin): boolean;
47
65
  /**
48
66
  * Deprecated: static show constructor helper.
49
67
  * @param {import('./jsmind.option.js').JsMindRuntimeOptions} options
@@ -64,6 +82,7 @@ export default class jsMind {
64
82
  event_handles: Array<(type: number, data: EventData) => void>;
65
83
  /** Initialize sub-systems and plugins. */
66
84
  init(): void;
85
+ enhancedPluginManager: EnhancedPluginManager;
67
86
  data: DataProvider;
68
87
  layout: LayoutProvider;
69
88
  view: ViewProvider;
@@ -347,7 +366,26 @@ export default class jsMind {
347
366
  invoke_event_handle(type: number, data: EventData): void;
348
367
  /** @param {number} type @param {EventData} data */
349
368
  _invoke_event_handle(type: number, data: EventData): void;
369
+ /**
370
+ * Remove an enhanced plugin
371
+ * @param {typeof EnhancedPlugin} PluginClass - Plugin class
372
+ */
373
+ removePlugin(PluginClass: typeof EnhancedPlugin): void;
374
+ removePlugin(PluginClass: typeof EnhancedPlugin): void;
375
+ /**
376
+ * Get an enhanced plugin instance
377
+ * @param {string} instanceName - Plugin instance name
378
+ * @returns {EnhancedPlugin | undefined}
379
+ */
380
+ getPlugin(instanceName: string): EnhancedPlugin | undefined;
381
+ getPlugin(instanceName: string): EnhancedPlugin | undefined;
382
+ /**
383
+ * Destroy the jsMind instance and clean up resources
384
+ */
385
+ destroy(): void;
386
+ destroy(): void;
350
387
  }
388
+ export default jsMind;
351
389
  /**
352
390
  * Event callback payload
353
391
  */
@@ -356,11 +394,14 @@ export type EventData = {
356
394
  data?: unknown[];
357
395
  node?: string;
358
396
  };
397
+ export namespace jsMind { }
359
398
  import { Mind } from './jsmind.mind.js';
399
+ import { EnhancedPluginManager } from './jsmind.enhanced-plugin.js';
360
400
  import { DataProvider } from './jsmind.data_provider.js';
361
401
  import { LayoutProvider } from './jsmind.layout_provider.js';
362
402
  import { ViewProvider } from './jsmind.view_provider.js';
363
403
  import { ShortcutProvider } from './jsmind.shortcut_provider.js';
404
+ import { EnhancedPlugin } from './jsmind.enhanced-plugin.js';
364
405
  import { Node } from './jsmind.node.js';
365
406
  import { Plugin } from './jsmind.plugin.js';
366
407
  import { register as _register_plugin } from './jsmind.plugin.js';
@@ -1,10 +1,10 @@
1
1
  export const $: Dom;
2
2
  /**
3
3
  * @license BSD
4
- * @copyright 2014-2025 hizzgdev@163.com
4
+ * @copyright 2014-2025 UmbraCi
5
5
  *
6
6
  * Project Home:
7
- * https://github.com/hizzgdev/jsmind/
7
+ * https://github.com/UmbraCi/jsmind/
8
8
  */
9
9
  /**
10
10
  * Lightweight DOM helpers bound to a window.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Enhanced Plugin Manager
3
+ * Manages the lifecycle of enhanced plugins with synchronous initialization,
4
+ * preload support, and lifecycle hooks.
5
+ */
6
+ export class EnhancedPluginManager {
7
+ /**
8
+ * @param {import('./jsmind.js').default} jm - jsMind instance
9
+ */
10
+ constructor(jm: import("./jsmind.js").default);
11
+ jm: import("./jsmind.js").default;
12
+ /** @type {Map<string, EnhancedPlugin>} */
13
+ plugins: Map<string, EnhancedPlugin>;
14
+ /**
15
+ * Initialize preload plugins (before core modules)
16
+ */
17
+ initPreloadPlugins(): void;
18
+ /**
19
+ * Initialize normal plugins (after core modules)
20
+ */
21
+ initNormalPlugins(): void;
22
+ /**
23
+ * Internal method: Initialize a single plugin
24
+ * @param {PluginDescriptor} descriptor
25
+ * @private
26
+ */
27
+ private _initPlugin;
28
+ /**
29
+ * Remove a plugin
30
+ * @param {typeof EnhancedPlugin} PluginClass
31
+ */
32
+ removePlugin(PluginClass: typeof EnhancedPlugin): void;
33
+ /**
34
+ * Destroy all plugins
35
+ */
36
+ destroyAllPlugins(): void;
37
+ /**
38
+ * Get plugin instance by name
39
+ * @param {string} instanceName
40
+ * @returns {EnhancedPlugin | undefined}
41
+ */
42
+ getPlugin(instanceName: string): EnhancedPlugin | undefined;
43
+ }
44
+ /**
45
+ * Enhanced Plugin Base Class
46
+ * Provides standard interface for enhanced plugins
47
+ */
48
+ export class EnhancedPlugin {
49
+ /**
50
+ * Plugin instance name (must be defined by subclass)
51
+ * @type {string}
52
+ */
53
+ static instanceName: string;
54
+ /**
55
+ * Whether to initialize before core modules
56
+ * @type {boolean}
57
+ */
58
+ static preload: boolean;
59
+ /**
60
+ * @param {{ jm: import('./jsmind.js').default, pluginOpt: object }} params
61
+ */
62
+ constructor({ jm, pluginOpt }: {
63
+ jm: import("./jsmind.js").default;
64
+ pluginOpt: object;
65
+ });
66
+ jm: import("./jsmind.js").default;
67
+ options: any;
68
+ /**
69
+ * Called before plugin is removed
70
+ * Override this method to clean up resources
71
+ */
72
+ beforePluginRemove(): void;
73
+ /**
74
+ * Called before jsMind instance is destroyed
75
+ * Override this method to clean up resources
76
+ */
77
+ beforePluginDestroy(): void;
78
+ }
79
+ /**
80
+ * Plugin descriptor
81
+ */
82
+ export type PluginDescriptor = {
83
+ /**
84
+ * - Plugin class
85
+ */
86
+ PluginClass: typeof EnhancedPlugin;
87
+ /**
88
+ * - Plugin instance name
89
+ */
90
+ instanceName: string;
91
+ /**
92
+ * - Whether to preload
93
+ */
94
+ preload: boolean;
95
+ /**
96
+ * - Plugin options
97
+ */
98
+ pluginOpt: object;
99
+ /**
100
+ * - Plugin instance (after initialization)
101
+ */
102
+ instance: EnhancedPlugin | null;
103
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Flatten a NodeTreeFormat/NodeTreeData by id
3
+ * @param {NodeTreeFormat|NodeTreeData} tree
4
+ * @param {{ fields?: string[], includeStructure?: boolean }=} opts
5
+ * @returns {Record<string, any>}
6
+ */
7
+ export function flatten(tree: NodeTreeFormat | NodeTreeData, opts?: {
8
+ fields?: string[];
9
+ includeStructure?: boolean;
10
+ } | undefined): Record<string, any>;
11
+ /**
12
+ * Compute diff between two snapshots
13
+ * @param {NodeTreeFormat|NodeTreeData} a
14
+ * @param {NodeTreeFormat|NodeTreeData} b
15
+ * @param {{ fields?: string[], includeStructure?: boolean, maxSize?: number }=} opts
16
+ * - fields: 可选。指定要参与 diff 的节点属性列表;默认比较所有属性(除 children)。与 includeStructure 组合使用时可同时比较 _parentid/_order。
17
+ * @returns {{
18
+ * created: any[],
19
+ * updated: { id:string, before:any, after:any, changes: { key:string, before:any, after:any }[] }[],
20
+ * deleted: any[],
21
+ * truncated: boolean
22
+ * }}
23
+ */
24
+ export function diff(a: NodeTreeFormat | NodeTreeData, b: NodeTreeFormat | NodeTreeData, opts?: {
25
+ fields?: string[];
26
+ includeStructure?: boolean;
27
+ maxSize?: number;
28
+ } | undefined): {
29
+ created: any[];
30
+ updated: {
31
+ id: string;
32
+ before: any;
33
+ after: any;
34
+ changes: {
35
+ key: string;
36
+ before: any;
37
+ after: any;
38
+ }[];
39
+ }[];
40
+ deleted: any[];
41
+ truncated: boolean;
42
+ };
43
+ export type NodeTreeFormat = {
44
+ meta?: any;
45
+ format?: "node_tree";
46
+ data: NodeTreeData;
47
+ };
48
+ export type NodeTreeData = {
49
+ id: string;
50
+ topic?: string;
51
+ expanded?: boolean;
52
+ direction?: "left" | "right";
53
+ data?: Record<string, any>;
54
+ children?: NodeTreeData[];
55
+ };
@@ -0,0 +1,84 @@
1
+ export default HistoryPlugin;
2
+ export type JsMind = import("../../jsmind.js").default;
3
+ /**
4
+ * HistoryPlugin skeleton (Task 1)
5
+ */
6
+ export class HistoryPlugin extends EnhancedPlugin {
7
+ /**
8
+ * @param {{ jm: JsMind, pluginOpt?: any }} params
9
+ */
10
+ constructor({ jm, pluginOpt }: {
11
+ jm: JsMind;
12
+ pluginOpt?: any;
13
+ });
14
+ options: any;
15
+ _mounted: boolean;
16
+ _core: HistoryCore;
17
+ /**
18
+ * Lifecycle hook: called when jsMind instance is destroyed
19
+ */
20
+ beforePluginDestroy(): void;
21
+ /** Default: call beforePluginRemove on destroy */
22
+ beforePluginDestroy(): void;
23
+ /** Initialize HistoryCore and wire API methods */
24
+ _initCore(): void;
25
+ _listener: (type: any, payload: any) => void;
26
+ /**
27
+ * Inject shortcut options so provider can register mapping in its init phase
28
+ * @param {{ enable?: boolean, handles: Record<string,Function>, mapping: Record<string, number|number[]> }} sc
29
+ * @param {boolean} redoUsesY
30
+ */
31
+ _injectShortcuts(sc: {
32
+ enable?: boolean;
33
+ handles: Record<string, Function>;
34
+ mapping: Record<string, number | number[]>;
35
+ }, redoUsesY: boolean): void;
36
+ /** Mount public API on jm.history (placeholder defaults) */
37
+ _mountAPI(): void;
38
+ }
39
+ import { EnhancedPlugin } from '../../jsmind.enhanced-plugin.js';
40
+ declare class HistoryCore {
41
+ /** @param {JsMind} jm @param {any} options */
42
+ constructor(jm: JsMind, options: any);
43
+ jm: import("../../jsmind.js").default;
44
+ options: any;
45
+ enabled: boolean;
46
+ maxHistory: number;
47
+ throttleMs: number;
48
+ storageMode: any;
49
+ autoSwitchThreshold: number;
50
+ _history: any[];
51
+ _idx: number;
52
+ _paused: boolean;
53
+ _lastAddAt: number;
54
+ _timer: number;
55
+ _pending: boolean;
56
+ _pendingMeta: any;
57
+ _lastSig: any;
58
+ add(reason: string, meta: any): void;
59
+ pause(): void;
60
+ resume(flush?: boolean): void;
61
+ clear(): void;
62
+ canBack(): boolean;
63
+ canForward(): boolean;
64
+ back(steps?: number): boolean;
65
+ forward(steps?: number): boolean;
66
+ length(): number;
67
+ index(): number;
68
+ setMax(count: any): void;
69
+ setThrottle(ms: any): void;
70
+ exportSnapshot(): any;
71
+ importSnapshot(data: any, applyOptions: any): boolean;
72
+ getStackMeta(): {
73
+ items: any[];
74
+ index: number;
75
+ };
76
+ _notifyChange(): void;
77
+ _addNow(_reason: any, _meta: any): void;
78
+ _applyIndex(): boolean;
79
+ _takeSnapshot(): any;
80
+ _applySnapshot(data: any, applyOptions: any): boolean;
81
+ _countNodes(snapshot: any): number;
82
+ _deepFreeze(obj: any): any;
83
+ _cloneSnapshot(obj: any): any;
84
+ }
@@ -13,10 +13,14 @@ export class DraggableNode {
13
13
  jm: import("../jsmind.js").default;
14
14
  /** @type {DraggableNodeOptions} */
15
15
  options: DraggableNodeOptions;
16
- /** @type {HTMLCanvasElement|null} */
17
- e_canvas: HTMLCanvasElement | null;
16
+ /** @type {boolean} */
17
+ is_svg_engine: boolean;
18
+ /** @type {HTMLCanvasElement|SVGSVGElement|null} */
19
+ e_canvas: HTMLCanvasElement | SVGSVGElement | null;
18
20
  /** @type {CanvasRenderingContext2D|null} */
19
21
  canvas_ctx: CanvasRenderingContext2D | null;
22
+ /** @type {SVGPathElement|null} */
23
+ helper_line: SVGPathElement | null;
20
24
  /** @type {HTMLElement|null} */
21
25
  shadow: HTMLElement | null;
22
26
  /** @type {number} */
@@ -57,10 +61,17 @@ export class DraggableNode {
57
61
  view_panel_rect: DOMRect | null;
58
62
  /** Initialize the draggable node plugin. */
59
63
  init(): void;
60
- /** Resize canvas and shadow elements. */
64
+ /** Resize canvas/SVG and shadow elements. */
61
65
  resize(): void;
62
- /** Create canvas for drawing drag lines. */
66
+ /** Create canvas or SVG for drawing drag lines. */
63
67
  create_canvas(): void;
68
+ /**
69
+ * Create SVG element with proper namespace.
70
+ * @param {string} tag - SVG tag name
71
+ * @returns {SVGElement}
72
+ * @private
73
+ */
74
+ private _create_svg_element;
64
75
  create_shadow(): void;
65
76
  /**
66
77
  * Reset shadow element style and cache its size.
@@ -84,16 +95,37 @@ export class DraggableNode {
84
95
  x: number;
85
96
  y: number;
86
97
  }, invalid: boolean): void;
87
- /** Clear helper lines from canvas. */
98
+ /** Clear helper lines from canvas or SVG. */
88
99
  clear_lines(): void;
89
100
  /**
90
- * Draw a straight helper line.
101
+ * Draw a straight helper line on canvas.
91
102
  * @param {number} x1
92
103
  * @param {number} y1
93
104
  * @param {number} x2
94
105
  * @param {number} y2
95
106
  */
96
107
  canvas_lineto(x1: number, y1: number, x2: number, y2: number): void;
108
+ /**
109
+ * Draw a helper line on SVG using bezier curve.
110
+ * Reuses the line drawing logic from SvgGraph.
111
+ * @param {number} x1 - Start x coordinate
112
+ * @param {number} y1 - Start y coordinate
113
+ * @param {number} x2 - End x coordinate
114
+ * @param {number} y2 - End y coordinate
115
+ * @param {string} color - Line color
116
+ */
117
+ svg_draw_line(x1: number, y1: number, x2: number, y2: number, color: string): void;
118
+ /**
119
+ * Draw bezier curve to SVG path element.
120
+ * Reuses logic from SvgGraph._bezier_to.
121
+ * @param {SVGPathElement} path - SVG path element
122
+ * @param {number} x1 - Start x coordinate
123
+ * @param {number} y1 - Start y coordinate
124
+ * @param {number} x2 - End x coordinate
125
+ * @param {number} y2 - End y coordinate
126
+ * @private
127
+ */
128
+ private _svg_bezier_to;
97
129
  /** Bind mouse/touch events for dragging. */
98
130
  event_bind(): void;
99
131
  /**