jsmind icon indicating copy to clipboard operation
jsmind copied to clipboard

feat(plugin): multiline text editing plugin with dynamic sizing

Open UmbraCi opened this issue 5 months ago • 9 comments

Overview

This PR refactors the multiline text editing functionality into an independent plugin form, following the maintainer's recommendations.

Key Changes

  • Plugin Refactoring: Refactored multiline editing functionality into an independent jsmind.multiline-text.js plugin
  • Optimized Editor: Improved editor text processing logic, fixed multiline text encoding/decoding issues
  • Browser Compatibility: Enhanced HTML entity processing and browser compatibility
  • Build Configuration: Updated rollup configuration to support new plugin structure
  • Example Updates: Simplified example page to demonstrate basic plugin usage

Technical Implementation

  • Uses contenteditable for multiline editing
  • Supports Shift+Enter for line breaks, Enter to finish editing
  • Automatically adjusts node size to accommodate multiline content
  • Maintains compatibility with existing jsMind API

Usage

// Load the plugin
import 'jsmind.multiline-text.js';

// Plugin automatically registers when creating jsMind instance
const jm = new jsMind(options);

// Plugin automatically handles multiline text editing and display

Backward Compatibility

  • Fully backward compatible, does not affect existing functionality
  • Plugin uses non-invasive design, won't impact core jsMind functionality

Testing

  • Tested multiline text editing and display
  • Tested compatibility with drag-and-drop plugin
  • Tested cross-browser compatibility

UmbraCi avatar Sep 21 '25 05:09 UmbraCi

@hizzgdev 当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

UmbraCi avatar Oct 01 '25 16:10 UmbraCi

@hizzgdev 当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

hizzgdev avatar Oct 01 '25 17:10 hizzgdev

@hizzgdev 当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

UmbraCi avatar Oct 01 '25 17:10 UmbraCi

当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

目前的可以支持多行文本,只需要按 html 的方式去支持就好了。 如果你需要用不同的渲染逻辑来支持多行文本,那其实是在定义另一种数据格式,如果是这样的话,你可以使用 custom_node_render,并且在你的插件里对这种节点进行更好的编辑支持。

hizzgdev avatar Oct 01 '25 17:10 hizzgdev

当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

目前的可以支持多行文本,只需要按 html 的方式去支持就好了。 如果你需要用不同的渲染逻辑来支持多行文本,那其实是在定义另一种数据格式,如果是这样的话,你可以使用 custom_node_render,并且在你的插件里对这种节点进行更好的编辑支持。

好的,我按照这个思路再重构一下这个PR的插件,先不用CR代码

UmbraCi avatar Oct 01 '25 17:10 UmbraCi

当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

目前的可以支持多行文本,只需要按 html 的方式去支持就好了。 如果你需要用不同的渲染逻辑来支持多行文本,那其实是在定义另一种数据格式,如果是这样的话,你可以使用 custom_node_render,并且在你的插件里对这种节点进行更好的编辑支持。

还有个问题,目前jsmind的插件流程如下:

  1. jsMind 实例创建
  2. 调用 show() 方法
  3. 数据加载、布局计算、节点渲染
  4. 异步调用插件初始化(setTimeout)
  5. 插件初始化完成

因为插件异步且在脑图初始化渲染后才会被注册,那么自定义渲染函数custom_node_render或者html的逻辑,如果我写在插件中,是不是首次渲染还是走的ViewProvider中的默认单行渲染函数render,如果我要初始化脑图的时候就支持多行渲染,岂不是还需要用户自己再去jsmind的custom_node_render入参传入一个自定义渲染函数才行; 或者我的插件在加载完以后使用插件中编写的custom_node_render方法再重绘一遍脑图(渲染了两次,是不是有性能问题);

UmbraCi avatar Oct 01 '25 17:10 UmbraCi

当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

目前的可以支持多行文本,只需要按 html 的方式去支持就好了。 如果你需要用不同的渲染逻辑来支持多行文本,那其实是在定义另一种数据格式,如果是这样的话,你可以使用 custom_node_render,并且在你的插件里对这种节点进行更好的编辑支持。

还有个问题,目前jsmind的插件流程如下:

  1. jsMind 实例创建
  2. 调用 show() 方法
  3. 数据加载、布局计算、节点渲染
  4. 异步调用插件初始化(setTimeout)
  5. 插件初始化完成

因为插件异步且在脑图初始化渲染后才会被注册,那么自定义渲染函数custom_node_render或者html的逻辑,如果我写在插件中,是不是首次渲染还是走的ViewProvider中的默认单行渲染函数render,如果我要初始化脑图的时候就支持多行渲染,岂不是还需要用户自己再去jsmind的custom_node_render入参传入一个自定义渲染函数才行; 或者我的插件在加载完以后使用插件中编写的custom_node_render方法再重绘一遍脑图(渲染了两次,是不是有性能问题);

你说的确实如此。我上面说的思路其实是这样的:

  1. 你想要的其实并不只是个编辑器,而是想要自定义渲染逻辑。这样的话不需要做插件,而是应该用 custom_node_render;
  2. 这种节点并不是jsmind能直接支持的格式,所以它的数据格式并不是普通的文本;为了支持更方便地编辑这种节点,你才需要实现一个插件;

这是两步操作。

但我理解你希望的是做一个插件,同时支持编辑器和渲染逻辑。那么你可以做一个独立的library,让它包装一下jsmind,比如 multiline-jsmind 在构造你的类时,自动在option里添加 custom_node_render参数和插件。

另一种思路是你让编辑器生成jsmind能渲染的格式,这样就只需要做editor,为了能够实现你说的保存和再次编辑,你可以在插件里同时把你的特殊的格式保存到节点的 data 里,这样相当于node的topic里保存的是jsmind能直接渲染的数据,data里保存的是editor要使用的格式,相当于把生成的数据和源码一起存起来了。

hizzgdev avatar Oct 01 '25 22:10 hizzgdev

当前插件系统异步初始化, 插件在渲染后才初始化,无法直接影响初始渲染,初始化渲染的逻辑在ViewProvider 类中,且打包出来的UMD 模块有作用域隔离。 在实现多行文本功能,我需要覆写ViewProvider的render函数以及edit相关逻辑,目前想到的方案是:

  • 通过原型链覆盖ViewProvider中的render方法;
  • 脑图被原始render函数初始化后,使用覆盖后的render方法再渲染一次; 但是这带来了额外的性能开销,请问是否考虑增加一个参数来让插件从【运行时动态加载插件】改为,初始化jsmind类时【同步加载插件】呢,这个逻辑可以由我来写;

这个插件是一个 editor 吧,它为什么需要在初始化的时候加载呢?在脑图没有画出来之前就已经需要用到 editor 了吗?还是说这个插件其实也修改了 render 逻辑?如果修改了,那为什么需要修改呢?

因为如果需要脑图的节点支持多行文本,那么需要1.渲染多行文本功能;2.编辑多行文本功能;3.编辑后的保存多行文本功能; 插件的加载是异步的,且init函数在首次渲染之前,此时,show()函数依赖的是ViewProvider的render方法,我的设想是这个插件也会覆写默认的render方法,从而让节点支持显示多行文本。 我在想,是不是可以让插件系统支持同步的方式加载插件,从而把覆写核心类的权限也开放给插件,并且让插件支持【预加载】(在show方法之前)

目前的可以支持多行文本,只需要按 html 的方式去支持就好了。 如果你需要用不同的渲染逻辑来支持多行文本,那其实是在定义另一种数据格式,如果是这样的话,你可以使用 custom_node_render,并且在你的插件里对这种节点进行更好的编辑支持。

还有个问题,目前jsmind的插件流程如下:

  1. jsMind 实例创建
  2. 调用 show() 方法
  3. 数据加载、布局计算、节点渲染
  4. 异步调用插件初始化(setTimeout)
  5. 插件初始化完成

因为插件异步且在脑图初始化渲染后才会被注册,那么自定义渲染函数custom_node_render或者html的逻辑,如果我写在插件中,是不是首次渲染还是走的ViewProvider中的默认单行渲染函数render,如果我要初始化脑图的时候就支持多行渲染,岂不是还需要用户自己再去jsmind的custom_node_render入参传入一个自定义渲染函数才行; 或者我的插件在加载完以后使用插件中编写的custom_node_render方法再重绘一遍脑图(渲染了两次,是不是有性能问题);

你说的确实如此。我上面说的思路其实是这样的:

  1. 你想要的其实并不只是个编辑器,而是想要自定义渲染逻辑。这样的话不需要做插件,而是应该用 custom_node_render;
  2. 这种节点并不是jsmind能直接支持的格式,所以它的数据格式并不是普通的文本;为了支持更方便地编辑这种节点,你才需要实现一个插件;

这是两步操作。 但我理解你希望的是做一个插件,同时支持编辑器和渲染逻辑。那么你可以做一个独立的library,让它包装一下jsmind,比如 multiline-jsmind 在构造你的类时,自动在option里添加 custom_node_render参数和插件。 另一种思路是你让编辑器生成jsmind能渲染的格式,这样就只需要做editor,为了能够实现你说的保存和再次编辑,你可以在插件里同时把你的特殊的格式保存到节点的 data 里,这样相当于node的topic里保存的是jsmind能直接渲染的数据,data里保存的是editor要使用的格式,相当于把生成的数据和源码一起存起来了。

考虑了下,其实目前无法直接通过一个插件就增加自定义节点显示和编辑功能,本质是卡在插件加载时序上导致无法覆写jsmind依赖的view等class中的渲染、编辑方法,为了解决这个问题,也可以为了后续其他插件的拓展能力,我能否提个PR给库增加一个同步注册插件方法usePlugin呢,区别于之前的插件在实例化后的异步注册,这样可以让编写的插件可以在实例化之前就对原本的class覆写。我看了下其他两个库例Mind-Elixir-Core、Simple-Mind-Map都是同步式的插件加载,并且也支持插件移除 @hizzgdev

UmbraCi avatar Oct 02 '25 08:10 UmbraCi

@hizzgdev 重构了下,目前导出一个自定义节点渲染函数,插件init函数修改默认edit行为改成多行文本。

UmbraCi avatar Oct 10 '25 11:10 UmbraCi