Skip to content

Conversation

@kagol
Copy link
Member

@kagol kagol commented Oct 21, 2025

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Added Mind Map editor with customizable themes, backgrounds, and resize capabilities.
    • Added Flow Chart editor with grid and background configuration options.
  • Documentation

    • Added comprehensive guides for Mind Map and Flow Chart features with configuration details.
  • Tests

    • Added test coverage for Mind Map and Flow Chart functionality.
  • Chores

    • Added internationalization support (English and Chinese).
    • Updated dependencies for new editor features.

shenyaofeng and others added 11 commits August 7, 2025 12:05
* feat: add insert flowChart and mindChart

* fix: update mindmap style

* fix: update mindmap ability

* fix: update mindmap node error

* fix:flow content delete and edit

* fix: add mindmap adjust size

* fix:node content delete

* fix(mind): 调整思维导图文件结构

* fix:删除防抖函数

* fix:更新mind

* fix:添加右键删除节点内容

* fix:快捷键添加思维导图

* fix:删除工具栏

* fix(mind):更新快捷键插入思维导图图标

* fix(mind):取消硬编码

* fix:更新依赖包

* fix(mind):完善国际化

* fix(mind-map):更新模块名称与将国际化和样式放入mind-map文件夹中

* fix(mind-map):规范思维导图相关变量名称

* fix(mind-map):删除思维导图多余样式文件

* fix(mind-map):规范思维导图相关变量名称

* fix(mind-map):规范思维导图相关类名称

* fix(mind-map):删除打印

* fix(mind-map):规范思维导图样式文件名称

* fix(mind-map):调整思维导图控制面板图标与样式
* feat(flow-chart):为编辑器添加插入流程图与调整大小布局功能

* fix(flow-chart):修正中文的国际化

* fix(flow-chart):规范样式的类名

* fix(flow-chart):修改图标格式

* fix(flow-chart):规范元素的类名

* fix(flow-chart):修复国际化问题
* feat(mind-map):为思维导图添加调整大小与布局功能

* fix(mind-map):改变思维导图右键菜单dom节点位置

* fix(mind-map):优化思维导图编辑节点内容方式

* fix(mind-map):设置输入框样式

* fix(mind-map):优化输入框

* fix(mind-map):删除多余导入的模块文件

* fix(mind-map):规范元素样式的类名
* feat(mind-map):新增插入图表改变结构控制面板显隐功能

* fix(mind-map):更新样式

* fix(mind-map):优化全屏状态

* fix(mind-mapt):优化样式代码减少重复样式

* fix(mind-map):规范类名

* fix(mind-map):规范元素样式

* fix(mind-map):减少硬编码

* fix(mind-map):封装相同代码

* fix(mind-map):优化对齐方式

* fix(mind-map):修复对齐方式

* fix(mind-map):修复国际化
* fix(flow-chart):优化样式

* fix(flow-chart):删除多余代码

* feat(flow-chart):为流程图添加设置边类型功能

* fix(flow-chart):更换图标

* fix(flow-chart):更改导出类型

* fix(flow-chart):删除左下控制面板

* fix(flow-chart):优化控制面板出现方式

* fix(flow-chart):增加右上控制面板

* feat(flow-chart):增加全面显示功能

* fix(flow-chart):规范变量名

* fix(flow-chart):解决光标问题

* fix(flow-chart):优化控制面板的显隐功能

* fix(flow-chart):优化全屏功能

* fix(flow-chart):改变最小高度

* fix(flow-chart):优化控制面板显隐功能

* fix(flow-chart):优化全屏功能

* fix(flow-chart):修复撤回功能

* fix(flow-chart):封装重复代码

* fix(flow-chart):简化样式代码

* fix(flow-chart):规范类名

* fix(flow-chart):优化样式代码减少重复样式

* fix(flow-chart):规范类名

* fix(flow-chart):优化对齐方式

* fix(flow-chart):优化国际化
* docs(flow-chart):更新文档

* docs(flow-chart):更新文档

* docs(flow-chart):更新文档

* docs(flow-chart):更新文档

* docs(flow-chart):更新文档

* fix(flow-chart):更改初始化流程图调用位置

* docs(flow-chart):更新文档
* docs(mind-map):更新文档

* fix(mind-map):修复警告

* docs(mind-map):更新文档

* fix(mind-map):解决菜单问题

* fix(mind-map):修复全屏功能

* docs(mind-map):更新文档

* fix(mind-map):更改初始化思维导图

* fix(mind-map):优化初始思维导图

* docs(flow-chart):更新文档
* test(mind-map):为思维导图添加自动化测试用例

* fix(mind-map):优化思维导图以及样例工具栏

* fix(mind-map):删除多余注释
* test(flow-chart):为流程图添加自动化测试用例

* fix(flow-chart):更新编辑器工具栏并更新流程图容器类名
* feat(mind-map):删除多余代码

* 为projects扩展功能
@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

This PR introduces two major new rich-editing features to FluentEditor: Mind Map and Flow Chart modules. It adds complete implementations including interactive blots, configuration utilities, i18n translations, control panels, context menus, resizing handlers, Vue demo components, and comprehensive documentation with four new runtime dependencies.

Changes

Cohort / File(s) Summary
Mind Map Module Core
packages/fluent-editor/src/modules/mind-map/index.ts, packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts
Introduces MindMapModule class to integrate Mind Map embeds into Quill with blot registration, and MindMapPlaceholderBlot class extending BlockEmbed for rendering interactive mind-map instances with lifecycle management, resize/context-menu support, and DOM data binding.
Flow Chart Module Core
packages/fluent-editor/src/modules/flow-chart/index.ts, packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts
Adds FlowChartModule for Quill integration and FlowChartPlaceholderBlot class as a BlockEmbed with LogicFlow editor initialization, grid/background configuration, resize handling, and text-editing UI.
Mind Map Configuration & UI
packages/fluent-editor/src/modules/mind-map/config-utils.ts, packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts, packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts, packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts
Configuration extraction helpers, control panel creation with toolbar items (zoom, fit, layout, insertion), context menu with copy/cut/paste/delete, and interactive resize action with four draggable corner handles.
Flow Chart Configuration & UI
packages/fluent-editor/src/modules/flow-chart/config-utils.ts, packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts, packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts, packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts
Configuration utilities for grid/background/resize, control panel with zoom/fit/undo/redo/edge-type controls, context menu for nodes/edges, and resize action with corner handles.
Mind Map Type Definitions & Options
packages/fluent-editor/src/modules/mind-map/options.ts
Introduces BackgroundConfig, LineConfig, MindMapDeps, and MindMapOptions interfaces for type-safe configuration.
Flow Chart Type Definitions & Options
packages/fluent-editor/src/modules/flow-chart/options.ts
Introduces GridOptions, BackgroundConfig, FlowChartDeps, and FlowChartOptions interfaces for type-safe configuration.
Mind Map I18N
packages/fluent-editor/src/modules/mind-map/i18n/en-us.ts, packages/fluent-editor/src/modules/mind-map/i18n/zh-cn.ts, packages/fluent-editor/src/modules/mind-map/i18n/index.ts
English and Chinese translation maps for mind-map UI controls, layouts, and context menu items, plus registration helper.
Flow Chart I18N
packages/fluent-editor/src/modules/flow-chart/i18n/en-us.ts, packages/fluent-editor/src/modules/flow-chart/i18n/zh-cn.ts, packages/fluent-editor/src/modules/flow-chart/i18n/index.ts
English and Chinese translation maps for flow-chart UI controls, panels, and context menu items, plus registration helper.
Mind Map Styling
packages/fluent-editor/src/modules/mind-map/style/mind-map.scss
SCSS with mixins and selectors for mind-map container, control panels, icons, layout panels, context menu, and hover effects.
Flow Chart Styling
packages/fluent-editor/src/modules/flow-chart/style/flow-chart.scss
SCSS with mixins and selectors for flow-chart container, control panels, edge-type panels, icons, shape items, and disabled/hover states.
Flow Chart Icons
packages/fluent-editor/src/modules/flow-chart/icons.ts
SVG icon constants for back, forward, zoom, fit, screen, resize, and edge-type controls.
Main Module Registration
packages/fluent-editor/src/modules/index.ts, packages/fluent-editor/src/fluent-editor.ts
Re-exports for new modules and registration of MindMapModule and FlowChartModule in FluentEditor registry.
Global Type Definitions
packages/fluent-editor/src/config/types/editor-modules.interface.ts
Extends IEditorModules interface with optional mind-map and flow-chart properties.
Global I18N
packages/fluent-editor/src/config/i18n/en-us.ts, packages/fluent-editor/src/config/i18n/zh-cn.ts
Adds "Mind Map" and "思维导图" / "Flow Chart" and "流程图" translations to global i18n.
UI Icons Configuration
packages/fluent-editor/src/ui/icons.config.ts, packages/fluent-editor/src/ui/icons.ts
Adds MIND_MAP_ICON and FLOW_CHART_ICON constants and registers them in ICONS_CONFIG mapping.
Toolbar Integration
packages/fluent-editor/src/modules/shortcut-key/index.ts
Adds mind-map and flow-chart toolbar menu items with icons and click handlers.
Mind Map Demo Components
packages/docs/fluent-editor/demos/mind-map.vue, packages/docs/fluent-editor/demos/mind-map-background.vue, packages/docs/fluent-editor/demos/mind-map-line.vue, packages/docs/fluent-editor/demos/mind-map-resize.vue, packages/docs/fluent-editor/demos/mind-map-theme.vue, packages/docs/fluent-editor/demos/mind-map.spec.ts
Six Vue demo components showcasing basic, background, line styling, resize, and theme features; includes Playwright test suite.
Flow Chart Demo Components
packages/docs/fluent-editor/demos/flow-chart.vue, packages/docs/fluent-editor/demos/flow-chart-background.vue, packages/docs/fluent-editor/demos/flow-chart-grid.vue, packages/docs/fluent-editor/demos/flow-chart-resize.vue, packages/docs/fluent-editor/demos/flow-chart.spec.ts
Four Vue demo components for basic, background, grid, and resize features; includes Playwright test suite.
Documentation
packages/docs/fluent-editor/docs/demo/mind-map.md, packages/docs/fluent-editor/docs/demo/flow-chart.md, packages/docs/fluent-editor/.vitepress/sidebar.ts
Comprehensive documentation pages for both features with configuration references and demo links; sidebar updated with new menu items.
Dependencies
packages/docs/package.json, packages/fluent-editor/package.json
Adds @logicflow/core, @logicflow/extension, simple-mind-map, simple-mind-map-plugin-themes as dependencies.
Other Updates
packages/docs/types/global.d.ts, packages/projects/src/views/yuque/YuQue.vue, packages/fluent-editor/vite.config.ts, packages/fluent-editor/scripts/pre-release.cjs
Global type extensions for SimpleMindMap/Themes/Drag/Export; YuQue component toolbar updated with new modules; vite config asset naming logic adjusted; pre-release script formatting.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Toolbar
    participant Module as MindMapModule/<br/>FlowChartModule
    participant Blot as MindMapPlaceholderBlot/<br/>FlowChartPlaceholderBlot
    participant Editor as SimpleMindMap/<br/>LogicFlow
    participant UI as ControlPanel/<br/>ContextMenu

    User->>Toolbar: Click mind-map/flow-chart button
    Toolbar->>Module: insertMindMapEditor()/insertFlowChartEditor()
    Module->>Blot: Create and embed blot instance
    Blot->>Blot: Mount & initialize editor
    Blot->>Editor: Initialize with config & data
    Blot->>UI: Create control panel & context menu
    Editor->>UI: Render UI elements
    User->>UI: Interact (zoom, edit, resize)
    UI->>Editor: Apply changes
    Editor->>Blot: Update graph/tree state
    Blot->>Blot: Sync data to DOM data-* attribute
Loading
sequenceDiagram
    participant Init as Initialization
    participant Config as Config Utils
    participant Quill
    participant Module as Module Instance
    
    Init->>Module: Create with options
    Module->>Quill: Store Quill reference
    Module->>Config: getBackgroundConfig(quill)
    Module->>Config: getGridConfig(quill) / getLineConfig(quill)
    Module->>Config: getResizeConfig(quill)
    Config->>Config: Extract from module options
    Config->>Module: Return normalized config
    Module->>Quill: Register blot class
    Module->>Toolbar: Wire toolbar handler
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • MindMapPlaceholderBlot (packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts) — Dense lifecycle management with MutationObserver, double-click text editing, alignment handling, and data persistence; complex DOM manipulation and event wiring.
  • FlowChartPlaceholderBlot (packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts) — Similar complexity with LogicFlow integration, pattern items, UI panel registration, floating textarea editing, and graph synchronization.
  • Control Panels (packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts, packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts) — Complex UI construction with dynamic panel creation, language binding, state management for multiple controls, and interactions (zoom, layout, fullscreen).
  • Resize Actions (packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts, packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts) — Multiple corner handles, drag lifecycle, dimension calculations, dependent element updates, and fullscreen state handling.
  • Context Menus (packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts, packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts) — Event subscription patterns, menu visibility toggling, item action handlers, and WeakMap-based handler tracking.
  • Integration Points — Verify proper registration in FluentEditor main module, i18n wiring, and dependency injection (deps field).
  • Demo Tests — Playwright test suites rely on multiple DOM selectors and timing; verify waitFor conditions are robust.

Poem

🧠 A mind unfolds in flowcharts bright,
New maps of thought take vibrant flight,
With Logic's dance and nodes that sing,
This editor brings the gift of spring! 🌸
Resize, edit, dream, and scheme,
Building better with each beam. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: add flowchart and mindmap" directly and clearly describes the main objective of the changeset. The PR adds two major new feature modules (Flow Chart and Mind Map) to the FluentEditor, including comprehensive implementations with configuration options, UI controls, documentation, demos, internationalization support, and styling. The title is concise (31 characters), uses standard commit message format, avoids vague terminology or noise, and is specific enough that a developer scanning git history would immediately understand the primary changes being introduced.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kagol/main-flowchart-mindmap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added enhancement New feature or request e2e-test labels Oct 21, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 35

🧹 Nitpick comments (55)
packages/docs/package.json (1)

31-32: Inconsistent version pinning strategy between simple-mind-map and its plugin.

simple-mind-map is pinned to exact version 0.14.0-fix.1 (per best practices for reproducible builds), but simple-mind-map-plugin-themes uses caret ^1.0.1, which allows minor/patch version drift. This creates inconsistent reproducibility guarantees.

Consider pinning simple-mind-map-plugin-themes to a specific version to match the reproducibility discipline applied to simple-mind-map:

-    "simple-mind-map-plugin-themes": "^1.0.1",
+    "simple-mind-map-plugin-themes": "1.0.1",

Alternatively, if allowing minor/patch updates for the plugin is intentional (e.g., to receive backwards-compatible bug fixes), document why the strategies differ. Based on learnings, please verify: (1) whether simple-mind-map-plugin-themes has a stable release matching 1.x that is compatible with [email protected], and (2) whether the caret range is necessary or if an exact version provides better stability.

packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (2)

169-171: Extract hardcoded hover color to a variable for maintainability.

The color #efefef is hardcoded inline. Define it as a CSS variable or SCSS variable to enable consistent theming and reduce duplication if this color is used elsewhere.

+$hover-background-color: #efefef;
+
  .ql-mind-map-control-item:hover {
-   background-color: #efefef;
+   background-color: $hover-background-color;
  }

92-111: Consider adding responsive breakpoints for mobile/tablet layouts.

The styling uses fixed dimensions (e.g., width: 200px on line 23, width: 80px on line 130) without media queries. Mind-map controls may overflow or become unusable on smaller viewports. Consider adding responsive breakpoints to ensure usability across device sizes.

Would you like me to suggest responsive breakpoints (e.g., for tablets and mobile devices) to ensure the mind-map UI adapts gracefully across screen sizes?

Also applies to: 113-145

packages/fluent-editor/src/modules/flow-chart/types/images.d.ts (1)

1-4: Move this PNG type declaration to a shared location for better code organization.

The ambient module declaration currently works across the project—it's already being imported by the mind-map module (12+ PNG imports across 2 files). However, keeping module-specific type declarations in a deeply nested module directory contradicts the global scope they provide. Move this to a shared location like packages/fluent-editor/types/images.d.ts or include it in a root-level types directory for clarity and maintainability. This prevents future developers from assuming the declaration is only for flow-chart and improves discoverability.

Additionally, verify that your bundler (Webpack/Vite/etc.) is configured to process PNG imports and return them as string URLs, as this declaration assumes that behavior.

packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (5)

16-16: Remove dead code: unused targetRatio and rect.

These aren’t used; keep code lean and avoid lint warnings.

-  targetRatio: number = 0
@@
-    const rect = container.getBoundingClientRect()
@@
-    this.targetRatio = rect.height / rect.width
+    // (ratio no longer tracked)

Also applies to: 64-65, 102-103


84-90: Guard non‑left clicks and non‑HTMLElement targets.

Prevents unexpected drags with right/middle click or when event target is not the handle.

-  onMouseDown = (event: MouseEvent) => {
+  onMouseDown = (event: MouseEvent) => {
     if (this.isFullscreen()) {
       return
     }
-    if (!(event.target instanceof HTMLElement)) {
+    if (event.button !== 0 || !(event.target instanceof HTMLElement)) {
       return
     }

84-107: Optional: Prefer Pointer Events for unified mouse/touch/pen support.

Switch to pointerdown/pointermove/pointerup with setPointerCapture for better device coverage.

High-level sketch:

box.addEventListener('pointerdown', this.onPointerDown)
// in handler:
(el as HTMLElement).setPointerCapture(event.pointerId)
document.addEventListener('pointermove', this.onPointerMove)
document.addEventListener('pointerup', this.onPointerUp)

Keep current mouse listeners if you need legacy fallback.

Also applies to: 120-159


168-174: Ensure destroy() also detaches any document listeners and resets cursor.

Prevents lingering handlers if destroy is called mid‑drag.

   destroy() {
     const container = this.blot.domNode
+    document.body.style.cursor = ''
+    document.removeEventListener('mousemove', this.onDrag)
+    document.removeEventListener('mouseup', this.onMouseUp)
     container.removeChild(this.topLeftHandle)
     container.removeChild(this.topRightHandle)
     container.removeChild(this.bottomRightHandle)
     container.removeChild(this.bottomLeftHandle)
   }

158-159: Storing full JSON in a data‑attribute can bloat DOM and trigger heavy mutations.

Consider a lightweight marker (id) and keep the data in JS state.

Do consumers read data-mind-map from DOM? If not, drop or replace with a small identifier and use a WeakMap to associate state.

packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1)

4-9: Consider strengthening the type annotation.

The I18N parameter is typed as any, which reduces type safety. Consider importing the I18N class type from the i18n module to provide better type checking and IDE support.

Apply this diff to improve type safety:

+import type { I18N } from '../../i18n'
 import { MIND_MAP_EN_US } from './en-us'
 import { MIND_MAP_ZH_CN } from './zh-cn'

-export function registerMindMapI18N(I18N: any) {
+export function registerMindMapI18N(I18N: typeof I18N) {
   I18N.register({
     'en-US': MIND_MAP_EN_US,
     'zh-CN': MIND_MAP_ZH_CN,
   }, false)
 }
packages/docs/fluent-editor/demos/flow-chart-background.vue (1)

35-36: Consider extracting the inline ops array to improve readability.

The ops array on line 35 is very long and contains nested data structures, making it difficult to read and maintain. Consider extracting it to a named constant above the onMounted block.

Apply this refactor:

 const TOOLBAR_CONFIG = [
   [{ header: [] }],
   ['bold', 'italic', 'underline', 'link'],
   [{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
   ['clean'],
   ['flow-chart'],
 ]
+
+const DEFAULT_FLOW_CHART_DATA = [
+  { insert: '\n' },
+  {
+    insert: {
+      'flow-chart': {
+        nodes: [
+          { id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' },
+          { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' },
+        ],
+        edges: [
+          { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' },
+        ],
+      },
+    },
+  },
+  { insert: '\n\n' },
+]

 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'flow-chart': {
           background: {
             color: '#98FB98',
             // image: 'url(path/to/image.png)',
             repeat: 'repeat',
             position: 'center',
             size: 'auto',
             opacity: 0.1,
           },
         },
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'flow-chart': { nodes: [{ id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' }, { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }], edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }] } } }, { insert: '\n\n' }]
-    editor.setContents(ops)
+    editor.setContents(DEFAULT_FLOW_CHART_DATA)
   })
 })
packages/docs/fluent-editor/demos/mind-map-resize.vue (1)

28-29: Consider extracting the inline ops array to improve readability.

Similar to the flow-chart demo, the ops array on line 28 is extremely long with deeply nested data structures, making it difficult to read and maintain. Consider extracting it to a named constant above the onMounted block.

Apply this refactor:

 const TOOLBAR_CONFIG = [
   [{ header: [] }],
   ['bold', 'italic', 'underline', 'link'],
   [{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
   ['clean'],
   ['mind-map'],
 ]
+
+const DEFAULT_MIND_MAP_DATA = [
+  { insert: '\n' },
+  {
+    insert: {
+      'mind-map': {
+        layout: 'logicalStructure',
+        root: {
+          data: {
+            text: '根节点',
+            expand: true,
+            uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a',
+            isActive: false,
+          },
+          children: [
+            {
+              data: {
+                text: '二级节点',
+                uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d',
+                expand: true,
+                richText: false,
+                isActive: false,
+              },
+              children: [],
+            },
+          ],
+          smmVersion: '0.14.0-fix.1',
+        },
+        theme: { template: 'default', config: {} },
+        view: {
+          transform: {
+            scaleX: 1,
+            scaleY: 1,
+            shear: 0,
+            rotate: 0,
+            translateX: 0,
+            translateY: 0,
+            originX: 0,
+            originY: 0,
+            a: 1,
+            b: 0,
+            c: 0,
+            d: 1,
+            e: 0,
+            f: 0,
+          },
+          state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 },
+        },
+      },
+    },
+  },
+  { insert: '\n\n' },
+]

 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'mind-map': {
           resize: true,
         },
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
-    editor.setContents(ops)
+    editor.setContents(DEFAULT_MIND_MAP_DATA)
   })
 })
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1)

4-9: Consider improving type safety for the I18N parameter.

The any type for the I18N parameter reduces type safety. Based on the I18N class structure shown in the relevant snippets, you could define a more specific type or interface.

Example refactor:

-export function registerFlowChartI18N(I18N: any) {
+export function registerFlowChartI18N(I18N: { register: (langs: Record<string, Record<string, string>>, isCover?: boolean) => void }) {
   I18N.register({
     'en-US': FLOW_CHART_EN_US,
     'zh-CN': FLOW_CHART_ZH_CN,
   }, false)
 }
packages/docs/fluent-editor/demos/mind-map.vue (2)

26-27: Extract inline mind-map data to improve readability.

The mind-map initialization data is a large, complex object literal that spans the entire line, making the code difficult to read and maintain.

Apply this diff:

+const INITIAL_MIND_MAP_DATA = {
+  layout: 'logicalStructure',
+  root: {
+    data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false },
+    children: [{
+      data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false },
+      children: []
+    }],
+    smmVersion: '0.14.0-fix.1'
+  },
+  theme: { template: 'default', config: {} },
+  view: {
+    transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
+    state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 }
+  }
+}
+
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'mind-map': true,
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
+    const ops = [{ insert: '\n' }, { insert: { 'mind-map': INITIAL_MIND_MAP_DATA } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
 })

16-29: Add error handling for the dynamic import.

The dynamic import lacks error handling, which could result in silent failures or unhandled promise rejections.

Apply this diff:

 onMounted(() => {
-  import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
+  import('@opentiny/fluent-editor')
+    .then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'mind-map': true,
       },
     })
     const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
+    .catch((error) => {
+      console.error('Failed to load FluentEditor:', error)
+    })
 })
packages/docs/fluent-editor/demos/flow-chart-resize.vue (2)

28-29: Extract inline flow-chart data to improve readability.

Similar to the mind-map demo, the flow-chart initialization data is inlined, reducing readability.

Apply this diff:

+const INITIAL_FLOW_CHART_DATA = {
+  nodes: [
+    { id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' },
+    { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }
+  ],
+  edges: [
+    { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }
+  ]
+}
+
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'flow-chart': {
           resize: true,
         },
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'flow-chart': { nodes: [{ id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' }, { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }], edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }] } } }, { insert: '\n\n' }]
+    const ops = [{ insert: '\n' }, { insert: { 'flow-chart': INITIAL_FLOW_CHART_DATA } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
 })

16-31: Add error handling for the dynamic import.

The dynamic import lacks error handling.

Apply this diff:

 onMounted(() => {
-  import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
+  import('@opentiny/fluent-editor')
+    .then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'flow-chart': {
           resize: true,
         },
       },
     })
     const ops = [{ insert: '\n' }, { insert: { 'flow-chart': { nodes: [{ id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' }, { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }], edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }] } } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
+    .catch((error) => {
+      console.error('Failed to load FluentEditor:', error)
+    })
 })
packages/docs/fluent-editor/demos/mind-map-line.vue (2)

33-34: Extract inline mind-map data to improve readability.

The inline mind-map data with line configuration is extremely long and difficult to read.

Apply this diff:

+const INITIAL_MIND_MAP_DATA = {
+  layout: 'logicalStructure',
+  root: {
+    data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false },
+    children: [{
+      data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false },
+      children: []
+    }],
+    smmVersion: '0.14.0-fix.1'
+  },
+  theme: {
+    template: 'default',
+    config: {
+      lineColor: '#009CE0',
+      lineWidth: 3,
+      lineDasharray: '15, 10, 5, 10, 15',
+      lineStyle: 'curve'
+    }
+  },
+  view: {
+    transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
+    state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 }
+  }
+}
+
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'mind-map': {
           line: {
             color: '#009CE0',
             width: 3,
             dasharray: '15, 10, 5, 10, 15',
             style: 'curve',
           },
         },
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: { lineColor: '#009CE0', lineWidth: 3, lineDasharray: '15, 10, 5, 10, 15', lineStyle: 'curve' } }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
+    const ops = [{ insert: '\n' }, { insert: { 'mind-map': INITIAL_MIND_MAP_DATA } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
 })

16-36: Add error handling for the dynamic import.

The dynamic import lacks error handling.

Apply this diff:

 onMounted(() => {
-  import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
+  import('@opentiny/fluent-editor')
+    .then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'mind-map': {
           line: {
             color: '#009CE0',
             width: 3,
             dasharray: '15, 10, 5, 10, 15',
             style: 'curve',
           },
         },
       },
     })
     const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: { lineColor: '#009CE0', lineWidth: 3, lineDasharray: '15, 10, 5, 10, 15', lineStyle: 'curve' } }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
+    .catch((error) => {
+      console.error('Failed to load FluentEditor:', error)
+    })
 })
packages/docs/fluent-editor/demos/flow-chart.vue (2)

26-27: Extract inline flow-chart data to improve readability.

The flow-chart initialization data should be extracted to a constant for better readability and maintainability.

Apply this diff:

+const INITIAL_FLOW_CHART_DATA = {
+  nodes: [
+    { id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' },
+    { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }
+  ],
+  edges: [
+    { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }
+  ]
+}
+
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'flow-chart': true,
       },
     })
-    const ops = [{ insert: '\n' }, { insert: { 'flow-chart': { nodes: [{ id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' }, { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }], edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }] } } }, { insert: '\n\n' }]
+    const ops = [{ insert: '\n' }, { insert: { 'flow-chart': INITIAL_FLOW_CHART_DATA } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
 })

16-29: Add error handling for the dynamic import.

The dynamic import lacks error handling, which could result in silent failures.

Apply this diff:

 onMounted(() => {
-  import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
+  import('@opentiny/fluent-editor')
+    .then(({ default: FluentEditor }) => {
     if (!editorRef.value) return
     editor = new FluentEditor(editorRef.value, {
       theme: 'snow',
       modules: {
         'toolbar': TOOLBAR_CONFIG,
         'flow-chart': true,
       },
     })
     const ops = [{ insert: '\n' }, { insert: { 'flow-chart': { nodes: [{ id: 'node1', type: 'rect', x: 100, y: 150, text: '开始' }, { id: 'node2', type: 'rect', x: 300, y: 150, text: '结束' }], edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', type: 'polyline' }] } } }, { insert: '\n\n' }]
     editor.setContents(ops)
   })
+    .catch((error) => {
+      console.error('Failed to load FluentEditor:', error)
+    })
 })
packages/fluent-editor/src/ui/icons.config.ts (1)

362-363: Normalize icon sizing and theming to match existing set.

Both new icons hardcode width/height=200 and fill colors. For consistent toolbar rendering and theming, prefer using only viewBox, remove fixed sizes, and use currentColor fills.

Example adjustment:

-export const MIND_MAP_ICON = `<svg ... width="200" height="200"><path ... fill="#2c2c2c" .../></svg>`
+export const MIND_MAP_ICON = `<svg viewBox="0 0 1024 1024"><path fill="currentColor" d="..."/></svg>`
 
-export const FLOW_CHART_ICON = `<svg ... width="200" height="200"><path ... fill="#2c2c2c" .../></svg>`
+export const FLOW_CHART_ICON = `<svg viewBox="0 0 1024 1024"><path fill="currentColor" d="..."/></svg>`

Confirm icons.ts registers these two exports in the UI registry so toolbar buttons render correctly.

packages/docs/fluent-editor/demos/mind-map-background.vue (1)

16-37: Add cleanup on unmount to avoid leaks.

Destroy the editor instance when the component unmounts.

-import { onMounted, ref } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
@@
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
@@
   })
 })
+
+onBeforeUnmount(() => {
+  // destroy/dispose if available
+  (editor as any)?.destroy?.()
+})

Optional: add data-testid="mind-map-background-demo" to the root div to stabilize e2e.

packages/docs/fluent-editor/demos/flow-chart-grid.vue (1)

16-37: Add cleanup on unmount to avoid leaks.

-import { onMounted, ref } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
@@
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
@@
   })
 })
+
+onBeforeUnmount(() => {
+  (editor as any)?.destroy?.()
+})

Optional: add data-testid="flow-chart-grid-demo" on the container for robust e2e scoping.

packages/fluent-editor/src/modules/mind-map/index.ts (1)

27-54: Embed insertion offsets are fine; consider minimal data for better defaults.

If the blot expects layout/theme/view/smmVersion, align default data with demos to avoid render differences.

-      const defaultData = {
-        data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false },
-        children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }],
-      }
+      const defaultData = {
+        layout: 'logicalStructure',
+        root: {
+          data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false },
+          children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }],
+          smmVersion: '0.14.0-fix.1'
+        },
+        theme: { template: 'default', config: {} },
+        view: { transform: { a:1,b:0,c:0,d:1,e:0,f:0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } }
+      }
packages/docs/fluent-editor/demos/mind-map-theme.vue (1)

16-37: Add cleanup on unmount to avoid leaks.

-import { onMounted, ref } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
@@
 onMounted(() => {
   import('@opentiny/fluent-editor').then(({ default: FluentEditor }) => {
@@
   })
 })
+
+onBeforeUnmount(() => {
+  (editor as any)?.destroy?.()
+})

Optional: add data-testid="mind-map-theme-demo" to stabilize e2e.

packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (3)

14-24: Avoid repeated i18n registration per blot.

registerFlowChartI18N(I18N) is called for every context menu; move to module scope with a guard to prevent redundant registrations.

+let __flowChartI18nRegistered = false
@@
-    registerFlowChartI18N(I18N)
+    if (!__flowChartI18nRegistered) {
+      registerFlowChartI18N(I18N)
+      __flowChartI18nRegistered = true
+    }

125-129: Avoid innerHTML mutation; clear children safely.

While innerHTML = '' is safe here, prefer node removal to dodge XSS scanners and microtask reflows.

-  if (blot.contextMenu) {
-    blot.contextMenu.innerHTML = ''
-  }
+  if (blot.contextMenu) {
+    while (blot.contextMenu.firstChild) {
+      blot.contextMenu.removeChild(blot.contextMenu.firstChild)
+    }
+  }

139-155: De-dupe menu item styles via CSS class.

Inline styles make theme overrides hard. Add a CSS class (e.g., .ql-flow-chart-context-menu-item) to your stylesheet and drop JS-assigned styles, keeping only behavior wiring here.

packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (3)

164-181: Accessibility: use button semantics + keyboard activation

Control items are

with mouse-only interaction. Add ARIA role, focus, and keyboard handling for Enter/Space. Also reflect disabled state.

 function createControlItem(iconClass: string, title: string, onClick: () => void, disabled = false) {
   const controlItem = document.createElement('div')
   controlItem.className = 'ql-mind-map-control-item'
   controlItem.title = title
   controlItem.dataset.controlType = iconClass
-  controlItem.style.cursor = disabled ? 'not-allowed' : 'pointer'
-  controlItem.style.opacity = disabled ? DISABLED_OPACITY : ENABLED_OPACITY
+  controlItem.style.cursor = disabled ? 'not-allowed' : 'pointer'
+  controlItem.style.opacity = disabled ? DISABLED_OPACITY : ENABLED_OPACITY
+  controlItem.setAttribute('role', 'button')
+  controlItem.setAttribute('aria-disabled', disabled ? 'true' : 'false')
+  controlItem.tabIndex = disabled ? -1 : 0
@@
-  if (!disabled) {
-    controlItem.addEventListener('click', onClick)
-  }
+  if (!disabled) {
+    controlItem.addEventListener('click', onClick)
+    controlItem.addEventListener('keydown', (e) => {
+      if (e.key === 'Enter' || e.key === ' ') {
+        e.preventDefault()
+        onClick()
+      }
+    })
+  }

3-3: Avoid deep import from simple-mind-map/src

Importing from simple-mind-map/src/svg/icons relies on internal paths that may change. Prefer the package’s public ESM/UMD exports or vendor the assets you need.

Can you confirm a stable public export for icons in your current pinned version (e.g., 0.14.0-fix.1)? If not, consider copying the minimal set you need into this repo.


30-35: Language-change listener needs cleanup

The handler is registered on quill.emitter but never removed. Over time, detached blots can leak listeners.

Store the callback and remove it when blot is destroyed/unmounted:

class MindMapControlPanelHandler {
  private onLangChange = (lang: string) => { /* update */ }
  constructor(private quill: FluentEditor, private blot: MindMapPlaceholderBlot) {
    // ...
    this.quill.emitter.on(CHANGE_LANGUAGE_EVENT, this.onLangChange)
  }
  dispose() {
    this.quill.emitter.off(CHANGE_LANGUAGE_EVENT, this.onLangChange)
  }
}

Hook dispose() from the blot’s teardown (if available). Please confirm if there is a blot/module lifecycle hook we can use.

packages/fluent-editor/src/modules/mind-map/options.ts (1)

1-20: Types look good; consider documenting value formats

Interfaces are clear. Optionally, document expected units/ranges:

  • BackgroundConfig.size/position: CSS background-size/position strings.
  • LineConfig.dasharray: SVG stroke-dasharray string.
  • LineConfig.style: verify against SimpleMindMap supported values.

If the library uses different naming (e.g., 'curve' vs 'bezier'), align the union type accordingly.

packages/fluent-editor/src/modules/flow-chart/index.ts (2)

17-27: Strongly type options; use Quill sources constant

Minor polish:

  • Type the constructor options as FlowChartOptions.
  • Use Quills.sources.USER instead of string literal 'user'.
-  constructor(quill: Quill, options: any) {
+  constructor(quill: Quill, options: FlowChartOptions) {
@@
-      this.quill.insertText(range.index, '\n', 'user')
-      this.quill.insertEmbed(range.index + 1, 'flow-chart', defaultData, 'user')
-      this.quill.insertText(range.index + 2, '\n', 'user')
+      this.quill.insertText(range.index, '\n', Quills.sources.USER)
+      this.quill.insertEmbed(range.index + 1, 'flow-chart', defaultData, Quills.sources.USER)
+      this.quill.insertText(range.index + 2, '\n', Quills.sources.USER)

33-40: Default node text and i18n

The default labels are Chinese (“开始/结束”). If the editor language differs, consider mapping to i18n strings.

packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (2)

19-23: Language-change listener needs cleanup

Same pattern as the control panel: store the handler and call off on teardown to avoid leaks.


50-54: Add ARIA roles for better accessibility

Assign roles to the menu container and items for screen readers.

-  blot.contextMenu = document.createElement('div')
+  blot.contextMenu = document.createElement('div')
   blot.contextMenu.className = 'ql-mind-map-context-menu'
+  blot.contextMenu.setAttribute('role', 'menu')
@@
   const item = document.createElement('div')
   item.className = 'ql-mind-map-context-menu-item'
   item.textContent = text
+  item.setAttribute('role', 'menuitem')

Also applies to: 95-110

packages/fluent-editor/src/modules/flow-chart/options.ts (1)

1-23: Looks good; minor docs on numeric ranges

Consider documenting:

  • GridOptions.size/thickness units (px)
  • BackgroundConfig.opacity range (0–1)
packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (3)

14-15: Remove unused fields to reduce noise.

targetRatio is assigned but never used; consider removing the field and assignment.

-  targetRatio: number = 0
@@
-    this.targetRatio = rect.height / rect.width

Also applies to: 96-101


60-63: Minor: unused local.

rect is computed but not used in repositionHandles(). Remove it.

-    const rect = container.getBoundingClientRect()

1-2: Consider making min sizes configurable.

Hardcoded MIN_WIDTH/MIN_HEIGHT could be read from module options for flexibility.

packages/fluent-editor/src/modules/mind-map/config-utils.ts (4)

55-60: Preserve valid zero values.

if (typedLine.width) drops 0. Check against undefined instead.

-      if (typedLine.width) {
+      if (typedLine.width !== undefined) {
         lineConfig.lineWidth = typedLine.width
       }

61-66: Allow empty string dash patterns.

An empty dasharray string is valid to mean solid; current truthy check drops it.

-      if (typedLine.dasharray) {
+      if (typedLine.dasharray !== undefined) {
         lineConfig.lineDasharray = typedLine.dasharray
       }

19-35: Return a typed config instead of object.

Replace any/object with a typed shape to improve DX and catch mistakes.


4-8: Optional: treat boolean true as “use defaults”.

Currently only objects are honored; true yields false. Consider returning a sensible default when background/line is true.

Also applies to: 40-44

packages/fluent-editor/src/modules/flow-chart/config-utils.ts (3)

29-35: Use nullish coalescing to keep valid zeros.

|| overwrites 0. Switch to ?? and explicit checks.

-        size: typedGrid.size || defaultGrid.size,
+        size: typedGrid.size ?? defaultGrid.size,
         visible: typedGrid.visible !== undefined ? typedGrid.visible : defaultGrid.visible,
-        type: typedGrid.type || defaultGrid.type,
+        type: typedGrid.type ?? defaultGrid.type,
         config: {
-          color: typedGrid.config?.color || defaultGrid.config.color,
-          thickness: typedGrid.config?.thickness || defaultGrid.config.thickness,
+          color: typedGrid.config?.color ?? defaultGrid.config.color,
+          thickness: typedGrid.config?.thickness ?? defaultGrid.config.thickness,
         },

75-77: Preserve opacity=0.

Truthiness check drops zero; use !== undefined.

-      if (typedBackground.opacity) {
+      if (typedBackground.opacity !== undefined) {
         backgroundConfig.opacity = typedBackground.opacity
       }

4-13: Type the return values.

Avoid any/false | object where possible; export explicit types for GridConfig, BackgroundStyleConfig, and the aggregate.

Also applies to: 96-105

packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (4)

48-58: Guard against malformed JSON.

JSON.parse can throw when data-mind-map is missing/corrupt.

-    const dataStr = JSON.parse(domNode.getAttribute('data-mind-map'))
-    const value = dataStr
+    const raw = domNode.getAttribute('data-mind-map')
+    let value: any = {}
+    try {
+      value = raw ? JSON.parse(raw) : {}
+    } catch {
+      value = {}
+    }
@@
-    return dataStr
+    return value

371-384: Scope DOM queries to this blot.

document.querySelectorAll('.smm-node-edit-wrap') risks touching other editors. Limit to the blot container if possible.

-      const editWraps = document.querySelectorAll('.smm-node-edit-wrap')
+      const root: ParentNode = this.domNode ?? document
+      const editWraps = root.querySelectorAll('.smm-node-edit-wrap')

146-148: Manage resize action lifecycle.

You instantiate MindMapResizeAction but don’t retain or destroy it.

-    if (resizeConfig) {
-      new MindMapResizeAction(this)
-    }
+    if (resizeConfig) {
+      this._resizeAction = new MindMapResizeAction(this)
+    }

And in class fields and remove():

+  _resizeAction: MindMapResizeAction | null = null
@@
   remove() {
+    this._resizeAction?.destroy()
+    this._resizeAction = null
     this.mindMap.destroy()
     ...
   }

Also applies to: 390-397


275-286: Avoid mutating parent element id.

Setting an auto-generated id on the parent can conflict with host app. Prefer isConnected checks or track the element via a WeakRef without touching attributes.

packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (1)

21-31: Cleanup i18n listener on dispose.

emitter.on is never unsubscribed; add a dispose() or return cleanup from createControlPanel to prevent leaks when blots are removed.

packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts (2)

93-106: Avoid observing the entire document just to detect connection.

Observing document.body subtree is heavy. Consider deferring editor init to attach() (when quill is available) and use requestAnimationFrame or a short microtask instead of a global MutationObserver.


137-156: Localize shape labels; don’t hardcode Chinese strings.

Replace '矩形/圆形/椭圆/菱形' with i18n lookups to match the rest of the module’s i18n approach.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71314b3 and a260797.

⛔ Files ignored due to path filters (39)
  • packages/fluent-editor/src/modules/flow-chart/icons/backIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/bezierIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/circleIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/contractIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/diamondIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/ellipseIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/expandIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/fitIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/forwardIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/fullScreenIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/lineIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/polyLineIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/rectangleIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/selectRegionIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/smallScreenIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/zoomInIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/flow-chart/icons/zoomOutIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/backIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/catalogOrganizationIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/childNodeIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/contractIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/deleteNodeIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/expandIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/fishboneIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/fitIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/forwardIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/fullScreenIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/insertIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/levelNodeIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/logicalStructureIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/mindMapIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/organizationStructureIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/parentNodeIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/setLayoutIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/smallScreenIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/timelineIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/zoomInIcon.png is excluded by !**/*.png
  • packages/fluent-editor/src/modules/mind-map/icons/zoomOutIcon.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (50)
  • README.md (1 hunks)
  • README.zh-CN.md (1 hunks)
  • packages/docs/fluent-editor/.vitepress/sidebar.ts (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.spec.ts (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-line.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-theme.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.spec.ts (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.vue (1 hunks)
  • packages/docs/fluent-editor/docs/demo/flow-chart.md (1 hunks)
  • packages/docs/fluent-editor/docs/demo/mind-map.md (1 hunks)
  • packages/docs/package.json (2 hunks)
  • packages/fluent-editor/package.json (2 hunks)
  • packages/fluent-editor/src/config/i18n/en-us.ts (1 hunks)
  • packages/fluent-editor/src/config/i18n/zh-cn.ts (1 hunks)
  • packages/fluent-editor/src/config/types/editor-modules.interface.ts (2 hunks)
  • packages/fluent-editor/src/fluent-editor.ts (2 hunks)
  • packages/fluent-editor/src/modules/flow-chart/config-utils.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/i18n/en-us.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/i18n/zh-cn.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/options.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/style/flow-chart.scss (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/types/images.d.ts (1 hunks)
  • packages/fluent-editor/src/modules/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/config-utils.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/i18n/en-us.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/i18n/zh-cn.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/options.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (1 hunks)
  • packages/fluent-editor/src/modules/shortcut-key/index.ts (1 hunks)
  • packages/fluent-editor/src/ui/icons.config.ts (1 hunks)
  • packages/fluent-editor/src/ui/icons.ts (3 hunks)
  • packages/projects/src/views/yuque/YuQue.vue (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (15)
packages/fluent-editor/src/modules/mind-map/i18n/index.ts (3)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/mind-map/i18n/en-us.ts (1)
  • MIND_MAP_EN_US (1-25)
packages/fluent-editor/src/modules/mind-map/i18n/zh-cn.ts (1)
  • MIND_MAP_ZH_CN (1-25)
packages/fluent-editor/src/ui/icons.ts (1)
packages/fluent-editor/src/ui/icons.config.ts (2)
  • MIND_MAP_ICON (362-362)
  • FLOW_CHART_ICON (363-363)
packages/fluent-editor/src/config/types/editor-modules.interface.ts (2)
packages/fluent-editor/src/modules/mind-map/options.ts (1)
  • MindMapOptions (15-20)
packages/fluent-editor/src/modules/flow-chart/options.ts (1)
  • FlowChartOptions (19-23)
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (3)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/flow-chart/i18n/en-us.ts (1)
  • FLOW_CHART_EN_US (1-26)
packages/fluent-editor/src/modules/flow-chart/i18n/zh-cn.ts (1)
  • FLOW_CHART_ZH_CN (1-26)
packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (4)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1)
  • registerFlowChartI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/mind-map/index.ts (1)
packages/fluent-editor/src/modules/mind-map/options.ts (1)
  • MindMapOptions (15-20)
packages/fluent-editor/src/fluent-editor.ts (2)
packages/fluent-editor/src/modules/mind-map/index.ts (1)
  • MindMapModule (6-55)
packages/fluent-editor/src/modules/flow-chart/index.ts (1)
  • FlowChartModule (8-46)
packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (5)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1)
  • registerMindMapI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1)
  • isFullscreen (28-31)
packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (4)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1)
  • registerMindMapI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/mind-map/config-utils.ts (1)
packages/fluent-editor/src/modules/mind-map/options.ts (2)
  • BackgroundConfig (1-7)
  • LineConfig (9-14)
packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (5)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1)
  • registerFlowChartI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (1)
  • isFullscreen (26-29)
packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts (4)
packages/fluent-editor/src/modules/flow-chart/config-utils.ts (1)
  • getAllConfigs (96-106)
packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (1)
  • FlowChartResizeAction (4-164)
packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (1)
  • createControlPanel (74-127)
packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (1)
  • initContextMenu (54-104)
packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (4)
packages/fluent-editor/src/modules/mind-map/config-utils.ts (1)
  • getAllConfigs (97-109)
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1)
  • MindMapResizeAction (6-175)
packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (1)
  • createControlPanel (103-162)
packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (1)
  • initContextMenu (50-93)
packages/fluent-editor/src/modules/flow-chart/config-utils.ts (1)
packages/fluent-editor/src/modules/flow-chart/options.ts (2)
  • GridOptions (1-9)
  • BackgroundConfig (10-17)
packages/fluent-editor/src/modules/flow-chart/index.ts (1)
packages/fluent-editor/src/modules/flow-chart/options.ts (1)
  • FlowChartOptions (19-23)
🪛 ast-grep (0.39.6)
packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts

[warning] 126-126: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: blot.contextMenu.innerHTML = ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts

[warning] 256-256: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconItem.innerHTML = icon.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 256-256: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconItem.innerHTML = icon.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

🪛 GitHub Actions: Playwright Tests
packages/docs/fluent-editor/demos/mind-map.spec.ts

[error] 9-12: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple elements; expected a single editor for render check.


[error] 14-17: Playwright test failure: strict mode violation. locator('.ql-toolbar') resolved to multiple toolbars; expected a single toolbar for visibility check.


[error] 22-25: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple elements for initialize content check.


[error] 32-35: Playwright test failure: strict mode violation. locator('.ql-toolbar .ql-mind-map') resolved to multiple buttons; expected a single button for visibility check.

packages/docs/fluent-editor/demos/flow-chart.spec.ts

[error] 8-10: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple elements; expected a single element for visibility check.


[error] 13-16: Playwright test failure: strict mode violation.locator('.ql-toolbar') resolved to multiple toolbars; expected a single element for visibility check.


[error] 21-24: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple editor instances for initial content check.


[error] 33-36: Playwright test failure: strict mode violation. locator('.ql-flow-chart-item') resolved to multiple items; expected a single item for visibility check.


[error] 44-49: Playwright test failure: strict mode violation. locator('.ql-toolbar .ql-flow-chart') resolved to multiple buttons; expected a single button for visibility check.

🪛 LanguageTool
packages/docs/fluent-editor/docs/demo/flow-chart.md

[uncategorized] ~13-~13: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...标悬停在节点上出现在四个角的手柄可以调整节点大小,右上方的手柄可以旋转节点,上下左右方向上的的手柄可以添加连线。 4. 该流程图模块支持配置网格样式,背景样式,调整大小...

(wa5)

Comment on lines 8 to 11
test('should render the editor', async ({ page }) => {
const editor = page.locator('.ql-editor')
await expect(editor).toBeVisible()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix strict-mode locator conflicts; scope to one editor container and remove arbitrary waits.

Multiple editors/toolbars exist on the docs page, so generic class selectors break in Playwright strict mode. Scope all queries to a single .ql-container (or a dedicated test id), and replace waitForTimeout with expectations.

Apply this diff:

@@
-  test('should render the editor', async ({ page }) => {
-    const editor = page.locator('.ql-editor')
-    await expect(editor).toBeVisible()
-  })
+  test('should render the editor', async ({ page }) => {
+    const container = page.locator('.ql-container').first()
+    const editor = container.locator('.ql-editor')
+    await expect(editor).toBeVisible()
+  })
@@
-  test('should have flow-chart button in toolbar', async ({ page }) => {
-    const toolbar = page.locator('.ql-toolbar')
-    await expect(toolbar).toBeVisible()
-
-    const flowChartButton = toolbar.locator('.ql-flow-chart')
-    await expect(flowChartButton).toBeVisible()
-  })
+  test('should have flow-chart button in toolbar', async ({ page }) => {
+    const container = page.locator('.ql-container').first()
+    const toolbar = container.locator('.ql-toolbar')
+    await expect(toolbar).toBeVisible()
+    await expect(toolbar.locator('.ql-flow-chart')).toBeVisible()
+  })
@@
-  test('should initialize editor with flow chart content', async ({ page }) => {
-    const editor = page.locator('.ql-editor')
-    await expect(editor).toBeVisible()
-
-    await page.waitForTimeout(1000)
-
-    const flowChartElement = editor.locator('.ql-flow-chart-item')
-    await expect(flowChartElement).toBeVisible()
-  })
+  test('should initialize editor with flow chart content', async ({ page }) => {
+    const container = page.locator('.ql-container').first()
+    const editor = container.locator('.ql-editor')
+    await expect(editor).toBeVisible()
+    await expect(container.locator('.ql-flow-chart-item').first()).toBeVisible()
+  })
@@
-  test('should contain initial flow chart nodes and edges', async ({ page }) => {
-    await page.waitForTimeout(2000)
-
-    const flowChartContainer = page.locator('.ql-flow-chart-item')
-    await expect(flowChartContainer).toBeVisible()
-
-    const nodes = page.locator('.lf-node')
-    await expect(nodes).toHaveCount(2)
-
-    const edges = page.locator('.lf-edge')
-    await expect(edges).toHaveCount(1)
-  })
+  test('should contain initial flow chart nodes and edges', async ({ page }) => {
+    const container = page.locator('.ql-container').first()
+    const flowChart = container.locator('.ql-flow-chart-item').first()
+    await expect(flowChart).toBeVisible()
+    await expect(flowChart.locator('.lf-node')).toHaveCount(2, { timeout: 5000 })
+    await expect(flowChart.locator('.lf-edge')).toHaveCount(1, { timeout: 5000 })
+  })
@@
-  test('should activate flow-chart when button is clicked', async ({ page }) => {
-    const flowChartButton = page.locator('.ql-toolbar .ql-flow-chart')
-    await expect(flowChartButton).toBeVisible()
-
-    await flowChartButton.click()
-
-    await page.waitForTimeout(500)
-
-    const editor = page.locator('.ql-editor')
-    await expect(editor).toBeVisible()
-  })
+  test('should activate flow-chart when button is clicked', async ({ page }) => {
+    const container = page.locator('.ql-container').first()
+    const flowChartButton = container.locator('.ql-toolbar .ql-flow-chart')
+    await expect(flowChartButton).toBeVisible()
+    await flowChartButton.click()
+    await expect(container.locator('.ql-editor')).toBeVisible()
+  })

Optionally, add data-testid hooks to the demo page and scope to page.getByTestId('flow-chart-demo') for maximum stability.

Also applies to: 13-19, 21-29, 31-42, 44-54

🧰 Tools
🪛 GitHub Actions: Playwright Tests

[error] 8-10: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple elements; expected a single element for visibility check.

🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/flow-chart.spec.ts around lines 8-11 (and
similarly for ranges 13-19, 21-29, 31-42, 44-54), the tests use generic
selectors that conflict in Playwright strict mode and may rely on arbitrary
waits; scope all locators to a single editor container (e.g.,
page.locator('.ql-container').first() or better
page.getByTestId('flow-chart-demo') if you add a data-testid to the demo),
replace any waitForTimeout calls with explicit expectations (e.g., await
expect(scopedLocator.locator('.ql-editor')).toBeVisible()) and update all other
queries in the file to use the same scoped container so the test targets only
one editor instance and becomes stable.

Comment on lines 22 to 30
test('should initialize editor with mind map content', async ({ page }) => {
const editor = page.locator('.ql-editor')
await expect(editor).toBeVisible()

await page.waitForTimeout(1000)

const mindMapElement = page.locator('.ql-mind-map-item')
await expect(mindMapElement).toBeVisible()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix strict mode violation and replace waitForTimeout with deterministic wait.

The locator .ql-editor resolved to multiple elements. Additionally, page.waitForTimeout(1000) is non-deterministic and discouraged in Playwright.

Apply this diff:

   test('should initialize editor with mind map content', async ({ page }) => {
-    const editor = page.locator('.ql-editor')
-    await expect(editor).toBeVisible()
-
-    await page.waitForTimeout(1000)
-
-    const mindMapElement = page.locator('.ql-mind-map-item')
+    const mindMapElement = page.locator('.ql-mind-map-item').first()
     await expect(mindMapElement).toBeVisible()
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should initialize editor with mind map content', async ({ page }) => {
const editor = page.locator('.ql-editor')
await expect(editor).toBeVisible()
await page.waitForTimeout(1000)
const mindMapElement = page.locator('.ql-mind-map-item')
await expect(mindMapElement).toBeVisible()
})
test('should initialize editor with mind map content', async ({ page }) => {
const mindMapElement = page.locator('.ql-mind-map-item').first()
await expect(mindMapElement).toBeVisible()
})
🧰 Tools
🪛 GitHub Actions: Playwright Tests

[error] 22-25: Playwright test failure: strict mode violation. locator('.ql-editor') resolved to multiple elements for initialize content check.

🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map.spec.ts around lines 22-30, the
test uses page.locator('.ql-editor') which resolves to multiple elements and
uses page.waitForTimeout(1000) which is non-deterministic; change the editor
locator to a specific element (e.g., const editor =
page.locator('.ql-editor').first() or use a more specific selector) and remove
the hard timeout—instead wait deterministically for the mind map element, e.g.,
const mindMapElement = page.locator('.ql-mind-map-item').first(); await
mindMapElement.waitFor({ state: 'visible', timeout: 5000 }); then assert await
expect(mindMapElement).toBeVisible().

Comment on lines 32 to 42
test('should activate mind-map when button is clicked', async ({ page }) => {
const mindMapButton = page.locator('.ql-toolbar .ql-mind-map')
await expect(mindMapButton).toBeVisible()

await mindMapButton.click()

await page.waitForTimeout(500)

const editor = page.locator('.ql-editor')
await expect(editor).toBeVisible()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix strict mode violation and replace waitForTimeout with deterministic wait.

The locator .ql-toolbar .ql-mind-map resolved to multiple buttons. The arbitrary timeout is also fragile.

Apply this diff:

   test('should activate mind-map when button is clicked', async ({ page }) => {
-    const mindMapButton = page.locator('.ql-toolbar .ql-mind-map')
+    const mindMapButton = page.locator('.ql-toolbar .ql-mind-map').first()
     await expect(mindMapButton).toBeVisible()
 
     await mindMapButton.click()
-
-    await page.waitForTimeout(500)
-
-    const editor = page.locator('.ql-editor')
-    await expect(editor).toBeVisible()
+    
+    // Assert on a meaningful state change after activation
+    await expect(page.locator('.ql-editor').first()).toBeVisible()
   })
🧰 Tools
🪛 GitHub Actions: Playwright Tests

[error] 32-35: Playwright test failure: strict mode violation. locator('.ql-toolbar .ql-mind-map') resolved to multiple buttons; expected a single button for visibility check.

🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map.spec.ts around lines 32-42, the
locator '.ql-toolbar .ql-mind-map' resolves to multiple elements and the test
uses an arbitrary waitForTimeout; update the test to target a single button
(e.g. use .first() or a more specific selector) before clicking, remove
waitForTimeout, and replace it with a deterministic wait such as waiting for the
editor to become visible or focused (await expect(editor).toBeVisible() or await
editor.waitFor({ state: 'visible' }) or await
expect(editor).toHaveJSProperty('isContentEditable', true) /
expect(editor).toBeFocused()), ensuring the click acts on a single element and
the subsequent assertion waits for a concrete editor state instead of a fixed
timeout.

Comment on lines +58 to +59
box.addEventListener('mousedown', this.onMouseDown.bind(this))
return box
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Event listeners added with bind() can’t be removed — leaks and duplicate handlers.

addEventListener(..., this.onDrag.bind(this)) creates a new function each time; removeEventListener(..., this.onDrag.bind(this)) won’t match, so listeners persist and accumulate.

Apply stable handler references (arrow properties) and drop bind():

@@
-  box.addEventListener('mousedown', this.onMouseDown.bind(this))
+  box.addEventListener('mousedown', this.onMouseDown)
@@
-  onMouseDown(event: MouseEvent) {
+  onMouseDown = (event: MouseEvent) => {
@@
-    document.addEventListener('mousemove', this.onDrag.bind(this))
-    document.addEventListener('mouseup', this.onMouseUp.bind(this))
+    document.addEventListener('mousemove', this.onDrag)
+    document.addEventListener('mouseup', this.onMouseUp)
@@
-  }
+  }
@@
-  onDrag(event: MouseEvent) {
+  onDrag = (event: MouseEvent) => {
@@
-  }
+  }
@@
-  onMouseUp() {
+  onMouseUp = () => {
@@
-    document.removeEventListener('mousemove', this.onDrag.bind(this))
-    document.removeEventListener('mouseup', this.onMouseUp.bind(this))
+    document.removeEventListener('mousemove', this.onDrag)
+    document.removeEventListener('mouseup', this.onMouseUp)
     this.dragHandle = null
-  }
+  }

Also applies to: 105-107, 161-164

🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts
around lines 58-59 (and similarly at 105-107 and 161-164), event listeners are
added with this.onX.bind(this) which creates new function objects so
removeEventListener can't remove them; replace bound calls by creating stable
handler properties (e.g., arrow-function class fields like onMouseDown = (e) =>
{ ... }) and use those properties directly in
addEventListener/removeEventListener; update all places that currently use
.bind(this) to reference the persistent arrow property and ensure you register
and unregister the same function reference.

Comment on lines +109 to +118
updateDependentElementsHeight(newHeight: number) {
const iconPanel = this.blot.domNode.querySelector('.ql-mind-map-icon-panel') as HTMLElement
const layoutPanel = this.blot.domNode.querySelector('.ql-mind-map-layout-panel') as HTMLElement
if (iconPanel && newHeight < HEIGHT_THRESHOLD) {
iconPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
}
if (layoutPanel && newHeight < HEIGHT_THRESHOLD) {
layoutPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Panel heights never reset when resizing back above threshold.

Heights are only set when < HEIGHT_THRESHOLD; they remain stuck afterward.

-    if (iconPanel && newHeight < HEIGHT_THRESHOLD) {
-      iconPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
-    }
-    if (layoutPanel && newHeight < HEIGHT_THRESHOLD) {
-      layoutPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
-    }
+    const computed = Math.max(0, newHeight - PANEL_HEIGHT_OFFSET)
+    if (iconPanel) {
+      iconPanel.style.height = newHeight < HEIGHT_THRESHOLD ? `${computed}px` : ''
+    }
+    if (layoutPanel) {
+      layoutPanel.style.height = newHeight < HEIGHT_THRESHOLD ? `${computed}px` : ''
+    }
🤖 Prompt for AI Agents
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts
around lines 109 to 118: the current updateDependentElementsHeight only sets
panel heights when newHeight < HEIGHT_THRESHOLD, so once set they never revert
when resizing back above the threshold; modify the method so that for each panel
(iconPanel and layoutPanel) you both set the reduced height when newHeight <
HEIGHT_THRESHOLD and clear/reset the inline height when newHeight >=
HEIGHT_THRESHOLD (e.g., set style.height = '' or remove the style) — keep null
checks for the panels and apply the reset in the else branch so panels return to
their default styling when above the threshold.

Comment on lines +147 to +151
.ql-mind-map-control-item.disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add focus/focus-visible states for keyboard accessibility.

Interactive elements (.ql-mind-map-control-item, .ql-mind-map-layout-item) lack :focus or :focus-visible indicators, breaking keyboard navigation. This is an accessibility blocker that prevents users from identifying focused elements when tabbing.

  .ql-mind-map-control-item:hover {
    background-color: #efefef;
  }
+
+.ql-mind-map-control-item:focus-visible {
+  outline: 2px solid #0066cc;
+  outline-offset: 2px;
+}
+
+.ql-mind-map-layout-item:focus-visible {
+  outline: 2px solid #0066cc;
+  outline-offset: 2px;
+}

Also applies to: 169-171

🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/mind-map/style/mind-map.scss around lines
147-151 (and similarly 169-171), add keyboard focus indicators for
.ql-mind-map-control-item and .ql-mind-map-layout-item by defining
:focus-visible and fallback :focus rules that provide a clear, high-contrast
outline or box-shadow (e.g., 2px solid or 3px offset shadow) so focused elements
are visually identifiable when tabbing; ensure these focus styles are not
applied when the element has the .disabled class (use
:not(.disabled):focus-visible / :not(.disabled):focus) and keep the styling
consistent with the theme and existing spacing.

@kagol kagol force-pushed the kagol/main-flowchart-mindmap branch from a260797 to ff076c4 Compare October 21, 2025 03:42
@kagol
Copy link
Member Author

kagol commented Oct 21, 2025

@shenyaofeng 还有以下几处需要优化:

  1. 自动化测试用例失败,需要修复
  2. 执行 pnpm build:lib 构建失败,需要修复
  3. mind-map-blot.ts 文件中不应该引入 simple-mind-map 等依赖,应该通过 mind-map 模块的 options 参数从外部导入,以实现该模块的按需引入,避免包体积过大,然后 demo 文档中需要引入 simple-mind-map 等依赖,projects 项目同 docs 项目

* 改变导入方式

* 删除响应图标使用svg格式

* 更改导入方式

* 删除打印

* 删除类型

* 删除多余样式

* 修改错误类名

* 添加类型

* 修改自动化测试用例
* 更换图标为svg格式

* 更改导入方式

* 更改样式文件

* 修改自动化测试用例

* 删除多余样式

* 修改文档

* 添加国际化

* 添加定位

* 修改自动化测试用例
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (9)
packages/docs/fluent-editor/docs/demo/flow-chart.md (1)

11-11: Address remaining Chinese wording improvements from previous review.

I see that some corrections from the previous review have been applied (e.g., "撤退" → "撤销", removal of duplicated "的的"). However, the following suggested improvements still need to be implemented:

  • Line 11: Remove redundant "功能" at the end: "隐藏控制面板功能" → "隐藏控制面板"
  • Line 13: Improve phrasing for better grammar and flow:
    • "出现在四个角的手柄" → "出现的四个角手柄"
    • "右上方的手柄可以旋转" → "右上方手柄可旋转"
    • "方向上的手柄可以添加" → "方向上的手柄可添加"

The static analysis tool also flags a grammar issue about missing classifiers (量词) between numerals and nouns on line 13, which the suggested rewording resolves.

Apply this diff to align with the previous review's suggestions:

-1. 通过左边控制面板可以拖拽节点添加到流程图中,也可以改变连线类型。通过右上控制面板可以缩小,放大,适应,全屏/恢复大小,撤销,恢复,隐藏控制面板功能。
+1. 通过左侧控制面板可以拖拽节点添加到流程图中,也可以改变连线类型。通过右上控制面板可以缩小、放大、适应、全屏/恢复大小、撤销、恢复、隐藏控制面板。
-3. 鼠标悬停在节点上出现在四个角的手柄可以调整节点大小,右上方的手柄可以旋转节点,上下左右方向上的手柄可以添加连线。
+3. 鼠标悬停在节点上出现的四个角手柄可以调整节点大小,右上方手柄可旋转节点,上下左右方向上的手柄可添加连线。

Also applies to: 13-13

packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (4)

98-101: Keep selectedNodes on the blot in sync with node_active events

The icon panel later reads (blot as any).selectedNodes, but the listener only updates the local selectedNodes variable. If the selection changes while the panel is open, icon clicks operate on stale nodes. Update the blot alongside the local cache.

   blot.mindMap.on('node_active', (...args: unknown[]) => {
     selectedNodes = Array.isArray(args[1]) ? args[1] : []
+    ;(blot as any).selectedNodes = selectedNodes
   })

304-320: Reuse the outside-click handler instead of creating throwaway functions

Each open defines a brand-new handleOutsideClick; the premature removeEventListener call can’t unhook the previous listener, so handlers pile up until a click finally fires. Store the handler reference (e.g., on the blot) so you detach the same function before re-registering it.

-  const handleOutsideClick = (e: MouseEvent) => {
+  const handleOutsideClick = (e: MouseEvent) => {
@@
-      document.removeEventListener('click', handleOutsideClick)
+      document.removeEventListener('click', (blot as any).__iconOutsideClick)
+      ;(blot as any).__iconOutsideClick = null
     }
   }
-  document.removeEventListener('click', handleOutsideClick)
-  document.addEventListener('click', handleOutsideClick)
+  if ((blot as any).__iconOutsideClick) {
+    document.removeEventListener('click', (blot as any).__iconOutsideClick)
+  }
+  ;(blot as any).__iconOutsideClick = handleOutsideClick
+  document.addEventListener('click', handleOutsideClick)

261-285: Stop injecting third-party SVG via innerHTML

The icons in nodeIconList come from an external package. Dropping the raw SVG into the DOM via innerHTML is an XSS sink if the upstream package ever adds scripts or handlers. Parse and sanitize before attaching.

+function parseSafeSvg(svgString: string): SVGElement | null {
+  const doc = new DOMParser().parseFromString(svgString, 'image/svg+xml')
+  const svg = doc.documentElement as SVGElement
+  if (!svg || svg.tagName.toLowerCase() !== 'svg') return null
+  svg.querySelectorAll('script').forEach((n) => n.remove())
+  svg.querySelectorAll('*').forEach((el) => {
+    ;[...el.attributes].forEach((attr) => {
+      if (attr.name.startsWith('on')) el.removeAttribute(attr.name)
+    })
+  })
+  return svg
+}
@@
-        iconItem.innerHTML = icon.icon
+        const svgEl = parseSafeSvg(icon.icon)
+        if (svgEl) iconItem.appendChild(svgEl)

445-472: Restore original positioning (top/left/z-index) when exiting fullscreen

Exiting fullscreen only resets position/width/height and hard-sets zIndex to '0', leaving any existing top/left/zIndex overrides stuck. Persist these properties before entering fullscreen and reinstate them on exit.

-    const originalPosition = mindMapContainer.getAttribute('data-original-position')
-    const originalWidth = mindMapContainer.getAttribute('data-original-width')
-    const originalHeight = mindMapContainer.getAttribute('data-original-height')
-    if (originalWidth && originalHeight) {
-      mindMapContainer.style.position = originalPosition
-      mindMapContainer.style.width = originalWidth
-      mindMapContainer.style.height = originalHeight
-      mindMapContainer.style.zIndex = '0'
+    const originalPosition = mindMapContainer.getAttribute('data-original-position')
+    const originalWidth = mindMapContainer.getAttribute('data-original-width')
+    const originalHeight = mindMapContainer.getAttribute('data-original-height')
+    const originalTop = mindMapContainer.getAttribute('data-original-top')
+    const originalLeft = mindMapContainer.getAttribute('data-original-left')
+    const originalZ = mindMapContainer.getAttribute('data-original-zindex')
+    if (originalWidth && originalHeight) {
+      mindMapContainer.style.position = originalPosition || ''
+      mindMapContainer.style.width = originalWidth
+      mindMapContainer.style.height = originalHeight
+      mindMapContainer.style.top = originalTop || ''
+      mindMapContainer.style.left = originalLeft || ''
+      mindMapContainer.style.zIndex = originalZ || ''
@@
-    mindMapContainer.setAttribute('data-original-width', mindMapContainer.style.width || '')
-    mindMapContainer.setAttribute('data-original-height', mindMapContainer.style.height || '')
+    mindMapContainer.setAttribute('data-original-width', mindMapContainer.style.width || '')
+    mindMapContainer.setAttribute('data-original-height', mindMapContainer.style.height || '')
+    mindMapContainer.setAttribute('data-original-top', mindMapContainer.style.top || '')
+    mindMapContainer.setAttribute('data-original-left', mindMapContainer.style.left || '')
+    mindMapContainer.setAttribute('data-original-zindex', mindMapContainer.style.zIndex || '')
packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (2)

95-106: Fix undo availability check

historyData.data.undos.length < 0 is always false, so undo never enables even when history data exists. Flip to > 0 so the control reflects actual availability.

-    const isUndoAvailable = historyData.data.undoAble || historyData.data.undos.length < 0
+    const isUndoAvailable = historyData.data.undoAble || historyData.data.undos.length > 0

113-116: Null-check .lf-dndpanel before appending

querySelector('.lf-dndpanel') can return null (e.g., when the left panel is hidden). Calling .append on null throws; guard the reference before appending.

   setTimeout(() => {
     const controlLeftUpPanel = blot.domNode.querySelector('.lf-dndpanel') as HTMLElement | null
-    controlLeftUpPanel.append(setEdgeTypeBtn)
+    if (controlLeftUpPanel) {
+      controlLeftUpPanel.append(setEdgeTypeBtn)
+    }
   }, 0)
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (2)

58-107: Use stable handler references for drag lifecycle

addEventListener(..., this.onDrag.bind(this)) / removeEventListener(..., this.onDrag.bind(this)) never match, so the listeners stay registered forever. Convert these handlers to class properties (arrow functions) and register/remove the same reference.

-    box.addEventListener('mousedown', this.onMouseDown.bind(this))
+    box.addEventListener('mousedown', this.onMouseDown)
@@
-  onMouseDown(event: MouseEvent) {
+  onMouseDown = (event: MouseEvent) => {
@@
-    document.addEventListener('mousemove', this.onDrag.bind(this))
-    document.addEventListener('mouseup', this.onMouseUp.bind(this))
+    document.addEventListener('mousemove', this.onDrag)
+    document.addEventListener('mouseup', this.onMouseUp)
   }
@@
-  onDrag(event: MouseEvent) {
+  onDrag = (event: MouseEvent) => {
@@
-  onMouseUp() {
+  onMouseUp = () => {
@@
-    document.removeEventListener('mousemove', this.onDrag.bind(this))
-    document.removeEventListener('mouseup', this.onMouseUp.bind(this))
+    document.removeEventListener('mousemove', this.onDrag)
+    document.removeEventListener('mouseup', this.onMouseUp)

Also applies to: 161-164


109-117: Reset panel heights when resizing back above the threshold

Once height drops below HEIGHT_THRESHOLD, the panels get a reduced style.height that never clears, so returning above the threshold leaves them stuck short. Reapply the default (empty string) when the new height exceeds the threshold.

-    if (iconPanel && newHeight < HEIGHT_THRESHOLD) {
-      iconPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
-    }
-    if (layoutPanel && newHeight < HEIGHT_THRESHOLD) {
-      layoutPanel.style.height = `${newHeight - PANEL_HEIGHT_OFFSET}px`
-    }
+    const computed = Math.max(0, newHeight - PANEL_HEIGHT_OFFSET)
+    if (iconPanel) {
+      iconPanel.style.height = newHeight < HEIGHT_THRESHOLD ? `${computed}px` : ''
+    }
+    if (layoutPanel) {
+      layoutPanel.style.height = newHeight < HEIGHT_THRESHOLD ? `${computed}px` : ''
+    }
🧹 Nitpick comments (2)
packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (2)

197-235: Clarify intent of commented-out icon background declarations.

Lines 197–235 contain 14 commented-out CSS rules mapping icon classes to background images. Either remove these if no longer needed, or add a brief comment explaining the intent (e.g., "// TODO: Enable icon backgrounds once image assets are available").


194-195: Justify or remove the !important flag on .smm-node-edit-wrap.

The !important declaration used here suggests fighting specificity elsewhere in the cascade. Either document why this override is necessary, or refactor to avoid the flag by adjusting selector specificity or HTML/class structure.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2d55c and c760050.

📒 Files selected for processing (30)
  • packages/docs/fluent-editor/demos/flow-chart-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.spec.ts (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-line.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-theme.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.spec.ts (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.vue (1 hunks)
  • packages/docs/fluent-editor/docs/demo/flow-chart.md (1 hunks)
  • packages/docs/types/global.d.ts (1 hunks)
  • packages/fluent-editor/src/config/i18n/en-us.ts (1 hunks)
  • packages/fluent-editor/src/config/i18n/zh-cn.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/config-utils.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/icons.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/options.ts (1 hunks)
  • packages/fluent-editor/src/modules/flow-chart/style/flow-chart.scss (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/config-utils.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/index.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/options.ts (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (1 hunks)
  • packages/fluent-editor/vite.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/docs/fluent-editor/demos/mind-map-resize.vue
  • packages/docs/fluent-editor/demos/flow-chart.spec.ts
  • packages/docs/fluent-editor/demos/flow-chart.vue
  • packages/docs/fluent-editor/demos/flow-chart-background.vue
  • packages/docs/fluent-editor/demos/mind-map.spec.ts
  • packages/fluent-editor/src/modules/flow-chart/index.ts
  • packages/fluent-editor/src/modules/flow-chart/style/flow-chart.scss
  • packages/fluent-editor/src/modules/mind-map/options.ts
  • packages/docs/fluent-editor/demos/mind-map-line.vue
  • packages/fluent-editor/src/config/i18n/zh-cn.ts
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue
  • packages/fluent-editor/src/modules/flow-chart/config-utils.ts
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue
🧰 Additional context used
🧬 Code graph analysis (6)
packages/fluent-editor/src/modules/mind-map/config-utils.ts (1)
packages/fluent-editor/src/modules/mind-map/options.ts (2)
  • BackgroundConfig (1-7)
  • LineConfig (9-14)
packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (5)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1)
  • registerMindMapI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1)
  • isFullscreen (28-31)
packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (5)
packages/fluent-editor/src/modules/mind-map/config-utils.ts (1)
  • getAllConfigs (97-112)
packages/fluent-editor/src/modules/mind-map/modules/custom-resize-action.ts (1)
  • MindMapResizeAction (6-175)
packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts (1)
  • createControlPanel (94-153)
packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (1)
  • initContextMenu (50-93)
packages/fluent-editor/src/modules/mind-map/icons.ts (2)
  • expandIcon (21-21)
  • contractIcon (20-20)
packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts (5)
packages/fluent-editor/src/modules/flow-chart/config-utils.ts (1)
  • getAllConfigs (96-109)
packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (1)
  • FlowChartResizeAction (4-164)
packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (1)
  • createControlPanel (68-121)
packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (1)
  • initContextMenu (54-104)
packages/fluent-editor/src/modules/flow-chart/icons.ts (2)
  • expandIcon (9-9)
  • contractIcon (8-8)
packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts (6)
packages/fluent-editor/src/modules/i18n.ts (1)
  • I18N (10-64)
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1)
  • registerFlowChartI18N (4-9)
packages/fluent-editor/src/config/editor.config.ts (1)
  • CHANGE_LANGUAGE_EVENT (4-4)
packages/fluent-editor/src/core/fluent-editor.ts (1)
  • lang (17-20)
packages/fluent-editor/src/modules/flow-chart/icons.ts (11)
  • backIcon (1-1)
  • forwardIcon (2-2)
  • zoomOutIcon (3-3)
  • zoomInIcon (4-4)
  • fitIcon (5-5)
  • screenTypeIcon (6-6)
  • contractIcon (8-8)
  • lineIcon (12-12)
  • polyLineIcon (10-10)
  • bezierIcon (11-11)
  • screenReduceIcon (7-7)
packages/fluent-editor/src/modules/flow-chart/modules/custom-resize-action.ts (1)
  • isFullscreen (26-29)
packages/fluent-editor/src/modules/mind-map/index.ts (1)
packages/fluent-editor/src/modules/mind-map/options.ts (1)
  • MindMapOptions (23-29)
🪛 ast-grep (0.39.6)
packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts

[warning] 179-179: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: icon.innerHTML = iconMap[iconClass] || ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 263-263: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconItem.innerHTML = icon.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 375-375: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: img.innerHTML = layout.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 456-456: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 471-471: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 179-179: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: icon.innerHTML = iconMap[iconClass] || ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 263-263: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconItem.innerHTML = icon.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 375-375: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: img.innerHTML = layout.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 456-456: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 471-471: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)

packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts

[warning] 192-192: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = expandIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 204-204: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = contractIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 192-192: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = expandIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 204-204: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = contractIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)

packages/fluent-editor/src/modules/flow-chart/formats/flow-chart-blot.ts

[warning] 206-206: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = expandIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 218-218: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = contractIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 206-206: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = expandIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 218-218: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = contractIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts

[warning] 171-171: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: icon.innerHTML = iconMap[iconClass] || ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 230-230: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: edgeIcon.innerHTML = edgeType.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 297-297: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 312-312: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 171-171: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: icon.innerHTML = iconMap[iconClass] || ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 230-230: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: edgeIcon.innerHTML = edgeType.icon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 297-297: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 312-312: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

🪛 LanguageTool
packages/docs/fluent-editor/docs/demo/flow-chart.md

[uncategorized] ~13-~13: 数词与名词之间一般应存在量词,可能缺少量词。
Context: ...标悬停在节点上出现在四个角的手柄可以调整节点大小,右上方的手柄可以旋转节点,上下左右方向上的手柄可以添加连线。 4. 该流程图模块支持配置网格样式,背景样式,调整大小。...

(wa5)

🔇 Additional comments (5)
packages/fluent-editor/vite.config.ts (1)

39-39: Good fix for asset naming!

Returning an empty string for asset file names would cause naming collisions and build failures. The updated pattern '[name][extname]' correctly preserves the original asset name and extension, which is the standard approach for Rollup/Vite configurations. This change likely addresses the pnpm build:lib failure mentioned in the PR objectives.

packages/docs/types/global.d.ts (2)

14-17: Window interface extensions declare types that aren't populated at runtime—clarify if this is intentional.

The Vue demo files use direct module imports (import SimpleMindMap from 'simple-mind-map') rather than accessing window globals. No runtime assignments to window.SimpleMindMap, window.Themes, window.Drag, or window.Export exist in the codebase. The type declarations in lines 14-17 don't correspond to actual runtime behavior in this repository.

This may be intentional if these types are meant for external consumers or CDN injection. However, if the code is meant to work within this codebase, either:

  • Remove the Window interface extensions, or
  • Explicitly populate these properties at runtime

Verify the intended usage pattern.


4-7: These imports align with the library's official documented export paths—no changes needed.

The imports on lines 4-7 correctly follow the simple-mind-map library's documented API. According to official documentation, importing from simple-mind-map/src/plugins/ with .js extensions is the recommended approach for accessing plugins directly. These are stable, officially supported export paths, not internal fragile dependencies.

packages/fluent-editor/src/config/i18n/en-us.ts (1)

75-76: LGTM! i18n entries added for both features.

Both 'mind-map' and 'flow-chart' i18n entries are now present, resolving the concern from the previous review. The toolbar buttons will now have proper tooltips.

packages/fluent-editor/src/modules/mind-map/formats/mind-map-blot.ts (1)

110-113: Correct dependency injection pattern implemented.

The mind-map module correctly receives dependencies (SimpleMindMap, Themes, Drag, Export) via the options parameter rather than importing them directly. This follows the pattern requested in the PR objectives (point 3) and allows on-demand importing to avoid increasing bundle size.

Comment on lines +175 to +177
if (!disabled) {
controlItem.addEventListener('click', onClick)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard listener registration when there’s no click handler

panel-status is created without an onClick, so this addEventListener call throws and aborts mounting. Only bind the listener when a handler is supplied.

-  if (!disabled) {
-    controlItem.addEventListener('click', onClick)
-  }
+  if (!disabled && onClick) {
+    controlItem.addEventListener('click', onClick)
+  }

Also applies to: 153-178

🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts around
lines 175-177 (and generally for the block covering 153-178), the code
unconditionally calls controlItem.addEventListener('click', onClick) which
throws when onClick is undefined; update the guard to only register the listener
when a click handler exists and is a function (e.g. if (!disabled && typeof
onClick === 'function') { controlItem.addEventListener('click', onClick); }) and
apply the same conditional guard to the other similar listener registrations in
lines 153-178.

Comment on lines +286 to +314
if (isFullscreen) {
const originalPosition = flowChartContainer.getAttribute('data-original-position')
const originalWidth = flowChartContainer.getAttribute('data-original-width')
const originalHeight = flowChartContainer.getAttribute('data-original-height')
if (originalWidth && originalHeight) {
flowChartContainer.style.position = originalPosition
flowChartContainer.style.width = originalWidth
flowChartContainer.style.height = originalHeight
flowChartContainer.style.zIndex = '0'
}
const iconElement = screenTypeBtn.querySelector('i')
if (iconElement) {
iconElement.innerHTML = screenTypeIcon
}
}
else {
flowChartContainer.setAttribute('data-original-position', flowChartContainer.style.position || '')
flowChartContainer.setAttribute('data-original-width', flowChartContainer.style.width || '')
flowChartContainer.setAttribute('data-original-height', flowChartContainer.style.height || '')
flowChartContainer.style.position = 'fixed'
flowChartContainer.style.top = '0'
flowChartContainer.style.left = '0'
flowChartContainer.style.width = '100vw'
flowChartContainer.style.height = '100vh'
flowChartContainer.style.zIndex = '100'
const iconElement = screenTypeBtn.querySelector('i')
if (iconElement) {
iconElement.innerHTML = screenReduceIcon
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore original positioning when leaving fullscreen

Fullscreen exit currently resets only size and forces zIndex to '0', leaving top/left/zIndex dirty. Persist and restore these styles to avoid layout corruption.

-    const originalPosition = flowChartContainer.getAttribute('data-original-position')
-    const originalWidth = flowChartContainer.getAttribute('data-original-width')
-    const originalHeight = flowChartContainer.getAttribute('data-original-height')
-    if (originalWidth && originalHeight) {
-      flowChartContainer.style.position = originalPosition
-      flowChartContainer.style.width = originalWidth
-      flowChartContainer.style.height = originalHeight
-      flowChartContainer.style.zIndex = '0'
+    const originalPosition = flowChartContainer.getAttribute('data-original-position')
+    const originalWidth = flowChartContainer.getAttribute('data-original-width')
+    const originalHeight = flowChartContainer.getAttribute('data-original-height')
+    const originalTop = flowChartContainer.getAttribute('data-original-top')
+    const originalLeft = flowChartContainer.getAttribute('data-original-left')
+    const originalZ = flowChartContainer.getAttribute('data-original-zindex')
+    if (originalWidth && originalHeight) {
+      flowChartContainer.style.position = originalPosition || ''
+      flowChartContainer.style.width = originalWidth
+      flowChartContainer.style.height = originalHeight
+      flowChartContainer.style.top = originalTop || ''
+      flowChartContainer.style.left = originalLeft || ''
+      flowChartContainer.style.zIndex = originalZ || ''
@@
-    flowChartContainer.setAttribute('data-original-width', flowChartContainer.style.width || '')
-    flowChartContainer.setAttribute('data-original-height', flowChartContainer.style.height || '')
+    flowChartContainer.setAttribute('data-original-width', flowChartContainer.style.width || '')
+    flowChartContainer.setAttribute('data-original-height', flowChartContainer.style.height || '')
+    flowChartContainer.setAttribute('data-original-top', flowChartContainer.style.top || '')
+    flowChartContainer.setAttribute('data-original-left', flowChartContainer.style.left || '')
+    flowChartContainer.setAttribute('data-original-zindex', flowChartContainer.style.zIndex || '')
🧰 Tools
🪛 ast-grep (0.39.6)

[warning] 297-297: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 312-312: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 297-297: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenTypeIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 312-312: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: iconElement.innerHTML = screenReduceIcon
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts around
lines 286 to 314, exiting fullscreen only restores width/height/position and
forces zIndex to '0' while leaving top/left/zIndex dirty; update the fullscreen
toggle to also save original top, left and zIndex when entering fullscreen (set
data-original-top, data-original-left, data-original-zindex from style or ''),
and when exiting fullscreen restore those attributes back onto
flowChartContainer.style.top, .left and .zIndex (only if the saved values exist)
so the element returns to its exact original layout.

Comment on lines +19 to +24
export interface FlowChartDeps {
SimpleMindMap: any
Themes: any
Drag: any
Export: any
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix FlowChartDeps interface to match actual usage.

The FlowChartDeps interface declares properties (SimpleMindMap, Themes, Drag, Export) that don't match the dependencies actually destructured in flow-chart-blot.ts line 106: { LogicFlow, DndPanel, SelectionSelect, Snapshot }. This appears to be copy-pasted from MindMapDeps without updating for flow-chart.

Apply this diff to fix the interface:

 export interface FlowChartDeps {
-  SimpleMindMap: any
-  Themes: any
-  Drag: any
-  Export: any
+  LogicFlow: any
+  DndPanel: any
+  SelectionSelect: any
+  Snapshot: any
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface FlowChartDeps {
SimpleMindMap: any
Themes: any
Drag: any
Export: any
}
export interface FlowChartDeps {
LogicFlow: any
DndPanel: any
SelectionSelect: any
Snapshot: any
}
🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/flow-chart/options.ts around lines 19 to
24, the FlowChartDeps interface is incorrect (still lists SimpleMindMap, Themes,
Drag, Export); replace it with the actual dependencies used in
flow-chart-blot.ts — declare properties LogicFlow, DndPanel, SelectionSelect,
and Snapshot (use appropriate types or any) and remove the outdated properties
so the interface matches the destructured deps at line 106 of
flow-chart-blot.ts.

Comment on lines +183 to +185
if (!disabled) {
controlItem.addEventListener('click', onClick)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Prevent TypeError when no click handler is provided

createControlItem always registers addEventListener('click', onClick) even when onClick is undefined (e.g., panel-status). This throws immediately and prevents the control panel from mounting. Guard the registration by ensuring a handler exists.

-  if (!disabled) {
-    controlItem.addEventListener('click', onClick)
-  }
+  if (!disabled && onClick) {
+    controlItem.addEventListener('click', onClick)
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!disabled) {
controlItem.addEventListener('click', onClick)
}
if (!disabled && onClick) {
controlItem.addEventListener('click', onClick)
}
🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts around
lines 183 to 185, the code always calls controlItem.addEventListener('click',
onClick) even when onClick is undefined, causing a TypeError; update the guard
so the click listener is only registered when a handler exists (e.g., check
typeof onClick === 'function' or if (onClick) before calling addEventListener)
while keeping the existing disabled check.

Comment on lines +168 to +170
.ql-mind-map-control-item:hover {
background-color: #efefef;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add keyboard focus indicators for interactive elements — critical accessibility issue.

This file lacks :focus-visible states for all interactive controls (.ql-mind-map-control-item, .ql-mind-map-layout-item, .ql-mind-map-icon-item). Only hover states are defined, breaking keyboard navigation and violating WCAG accessibility requirements. Users tabbing through the interface cannot identify the currently focused element.

This was flagged in a previous review but remains unaddressed.

Apply this diff to add focus indicators to interactive elements:

 .ql-mind-map-control-item:hover {
   background-color: #efefef;
 }
+
+.ql-mind-map-control-item:focus-visible {
+  outline: 2px solid #0066cc;
+  outline-offset: 2px;
+}
+
+.ql-mind-map-control-item:not(.disabled):focus-visible {
+  outline: 2px solid #0066cc;
+  outline-offset: 2px;
+}

 .ql-mind-map-layout-panel {
   display: flex;
   flex-wrap: wrap;
   gap: 6px;
   @include mind-map-panel;
   .ql-mind-map-layout-item {
     display: flex;
     flex-direction: column;
     align-items: center;
     cursor: pointer;
     border: 1px solid #eee;
     border-radius: inherit;
     padding: 2px;
     width: 100%;
     min-width: 100px;
     @include hover-shadow-effect;
+    
+    &:focus-visible {
+      outline: 2px solid #0066cc;
+      outline-offset: 2px;
+    }
   }
 }

 .ql-mind-map-icon-panel {
   @include mind-map-panel;
   .ql-mind-map-icon-group-container {
     display: flex;
     flex-wrap: wrap;
     gap: 6px;
     .ql-mind-map-icon-item {
       width: 25px;
       height: 25px;
       display: flex;
       align-items: center;
       justify-content: center;
       cursor: pointer;
       border: 1px solid #eee;
       border-radius: inherit;
       padding: 2px;
       @include hover-shadow-effect;
+      
+      &:focus-visible {
+        outline: 2px solid #0066cc;
+        outline-offset: 2px;
+      }
     }
   }
 }

Also applies to: 117-127, 97-108

🤖 Prompt for AI Agents
In packages/fluent-editor/src/modules/mind-map/style/mind-map.scss around lines
168-170 (and similarly at 117-127 and 97-108), the interactive controls only
define :hover styles and lack keyboard focus-visible states; add :focus-visible
rules for .ql-mind-map-control-item, .ql-mind-map-layout-item, and
.ql-mind-map-icon-item that mirror the hover visual (e.g., background-color:
#efefef) and include a high-contrast visible indicator (outline or box-shadow)
so keyboard users can see focus; use :focus-visible rather than :focus to avoid
showing for mouse interactions and keep specificity consistent with existing
selectors.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (1)
packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (1)

168-170: Keyboard focus indicators still missing — critical accessibility issue remains unaddressed.

Interactive elements (.ql-mind-map-control-item, .ql-mind-map-layout-item at lines 117-127, .ql-mind-map-icon-item at lines 97-108) still lack :focus-visible states. Only :hover states are defined, breaking keyboard navigation and violating WCAG accessibility requirements.

This issue was flagged in previous reviews but remains unresolved.

Apply this diff to add focus indicators:

 .ql-mind-map-control-item:hover {
   background-color: #efefef;
 }
+
+.ql-mind-map-control-item:focus-visible {
+  outline: 2px solid #0066cc;
+  outline-offset: 2px;
+  background-color: #efefef;
+}

 .ql-mind-map-layout-panel {
   // ...
   .ql-mind-map-layout-item {
     // ...
     @include hover-shadow-effect;
+
+    &:focus-visible {
+      outline: 2px solid #0066cc;
+      outline-offset: 2px;
+    }
   }
 }

 .ql-mind-map-icon-panel {
   // ...
   .ql-mind-map-icon-group-container {
     // ...
     .ql-mind-map-icon-item {
       // ...
       @include hover-shadow-effect;
+
+      &:focus-visible {
+        outline: 2px solid #0066cc;
+        outline-offset: 2px;
+      }
     }
   }
 }
🧹 Nitpick comments (2)
packages/docs/fluent-editor/demos/mind-map-resize.vue (1)

47-48: Consider extracting mind-map seed data for readability.

The inline ops definition is over 1000 characters on a single line, making it hard to read and maintain. Consider extracting to a constant or separate file.

Example:

+const INITIAL_MIND_MAP_OPS = [
+  { insert: '\n' },
+  {
+    insert: {
+      'mind-map': {
+        layout: 'logicalStructure',
+        root: { /* ... */ },
+        theme: { template: 'default', config: {} },
+        view: { /* ... */ }
+      }
+    }
+  },
+  { insert: '\n\n' }
+]
+
-    const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
-    editor.setContents(ops)
+    editor.setContents(INITIAL_MIND_MAP_OPS)
packages/docs/fluent-editor/demos/mind-map.vue (1)

16-48: Consider extracting common mind-map initialization logic.

The initialization pattern (dynamic imports, editor setup, content seeding) is duplicated across all five mind-map demo files (mind-map.vue, mind-map-resize.vue, mind-map-line.vue, mind-map-background.vue, mind-map-theme.vue). This creates maintenance burden.

Consider creating a shared composable or helper function:

// composables/useMindMapEditor.ts
export function useMindMapEditor(options: MindMapOptions) {
  const editorRef = ref<HTMLElement>()
  const editor = ref<FluentEditor>()
  
  onMounted(async () => {
    const [
      { default: FluentEditor },
      { default: SimpleMindMap },
      // ...
    ] = await Promise.all([/* imports */])
    
    if (!editorRef.value) return
    editor.value = new FluentEditor(editorRef.value, {
      theme: 'snow',
      modules: {
        toolbar: TOOLBAR_CONFIG,
        'mind-map': { deps: { SimpleMindMap, /* ... */ }, ...options }
      }
    })
    // seed content...
  })
  
  return { editorRef, editor }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c760050 and 30d896d.

📒 Files selected for processing (11)
  • packages/docs/fluent-editor/demos/flow-chart-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-line.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-theme.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.vue (1 hunks)
  • packages/docs/fluent-editor/docs/demo/mind-map.md (1 hunks)
  • packages/fluent-editor/src/modules/mind-map/style/mind-map.scss (1 hunks)
🧰 Additional context used
🪛 ESLint
packages/docs/fluent-editor/demos/flow-chart.vue

[error] 3-3: 'LogicFlow' is defined but never used.

(unused-imports/no-unused-imports)


[error] 4-4: 'DndPanel' is defined but never used.

(unused-imports/no-unused-imports)


[error] 4-4: 'SelectionSelect' is defined but never used.

(unused-imports/no-unused-imports)


[error] 4-4: 'Snapshot' is defined but never used.

(unused-imports/no-unused-imports)


[error] 21-21: Expected indentation of 2 spaces but found 4.

(style/indent)

packages/docs/fluent-editor/demos/mind-map-background.vue

[error] 24-55: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 31-31: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 32-32: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 33-33: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 34-34: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 35-35: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 36-36: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 37-37: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 45-45: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 46-46: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 47-47: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 48-48: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 49-49: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 50-50: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 51-51: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 52-52: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 53-53: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 54-54: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 55-55: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/flow-chart-background.vue

[error] 24-54: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 29-29: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 30-30: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 31-31: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 32-32: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 33-33: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 34-34: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 35-35: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 36-36: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 37-37: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 45-45: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 46-46: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 47-47: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 48-48: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 49-49: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 50-50: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 51-51: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 52-52: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 53-53: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 54-54: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/flow-chart-grid.vue

[error] 24-55: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 29-29: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 30-30: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 31-31: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 32-32: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 33-33: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 34-34: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 35-35: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 36-36: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 37-37: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 16 spaces but found 14.

(style/indent)


[error] 41-41: Expected indentation of 16 spaces but found 14.

(style/indent)


[error] 42-42: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 45-45: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 46-46: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 47-47: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 48-48: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 49-49: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 50-50: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 51-51: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 52-52: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 53-53: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 54-54: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 55-55: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/flow-chart-resize.vue

[error] 24-47: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 29-29: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 30-30: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 31-31: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 32-32: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 33-33: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 34-34: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 35-35: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 36-36: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 37-37: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 42-42: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 43-43: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 44-44: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 45-45: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 46-46: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 47-47: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/mind-map-line.vue

[error] 24-54: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 31-31: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 32-32: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 33-33: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 34-34: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 35-35: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 36-36: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 37-37: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 45-45: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 46-46: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 47-47: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 48-48: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 49-49: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 50-50: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 51-51: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 52-52: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 53-53: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 54-54: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/mind-map-resize.vue

[error] 24-49: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 31-31: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 32-32: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 33-33: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 34-34: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 35-35: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 36-36: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 37-37: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 45-45: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 46-46: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 47-47: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 48-48: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 49-49: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/mind-map-theme.vue

[error] 24-50: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 31-31: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 32-32: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 33-33: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 34-34: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 35-35: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 36-36: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 37-37: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 44-44: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 45-45: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 46-46: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 48-48: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 49-49: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 50-50: Expected indentation of 4 spaces but found 2.

(style/indent)

packages/docs/fluent-editor/demos/mind-map.vue

[error] 24-48: Should have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)


[error] 31-31: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 32-32: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 33-33: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 34-34: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 35-35: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 36-36: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 37-37: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 38-38: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 39-39: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 40-40: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 41-41: Expected indentation of 14 spaces but found 12.

(style/indent)


[error] 42-42: Expected indentation of 12 spaces but found 10.

(style/indent)


[error] 43-43: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 44-44: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 45-45: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 46-46: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 47-47: Expected indentation of 6 spaces but found 4.

(style/indent)


[error] 48-48: Expected indentation of 4 spaces but found 2.

(style/indent)

🔇 Additional comments (1)
packages/docs/fluent-editor/docs/demo/mind-map.md (1)

1-86: All verification confirms the documentation is accurate and complete.

The demo file references and configuration properties have been verified:

  • Demo files: All 5 files exist at packages/docs/fluent-editor/demos/. The src=demos/ path follows the standard convention used throughout the documentation; the build system resolves these paths.
  • Configuration properties: All tables are verified against options.ts and config-utils.ts. Background, line, resize, and theme configurations match their implementation types exactly.

No corrections or updates are needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
packages/docs/fluent-editor/demos/mind-map-resize.vue (1)

24-49: Fix indentation to match project style.

This is a duplicate of the previously reported ESLint indentation violations.

🧹 Nitpick comments (2)
packages/docs/fluent-editor/demos/mind-map-resize.vue (2)

5-5: Consider more precise typing for the editor variable.

The editor variable is declared but not initialized, so its type should reflect that it can be undefined until the Promise resolves.

Apply this diff for more precise typing:

-let editor: FluentEditor
+let editor: FluentEditor | undefined

16-51: Add cleanup for the editor instance.

The editor instance is created in onMounted but never cleaned up. This can lead to memory leaks or unexpected behavior if the component is unmounted and remounted.

Add cleanup using onBeforeUnmount:

import { onBeforeUnmount, onMounted, ref } from 'vue'

// ... existing code ...

onBeforeUnmount(() => {
  if (editor) {
    // Assuming FluentEditor has a destroy or cleanup method
    // Adjust based on actual API
    editor = undefined
  }
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30d896d and c59a0d7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • packages/docs/fluent-editor/.vitepress/sidebar.ts (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/flow-chart.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-background.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-line.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-resize.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map-theme.vue (1 hunks)
  • packages/docs/fluent-editor/demos/mind-map.vue (1 hunks)
  • packages/docs/package.json (2 hunks)
  • packages/docs/types/global.d.ts (1 hunks)
  • packages/fluent-editor/package.json (2 hunks)
  • packages/fluent-editor/scripts/pre-release.cjs (1 hunks)
  • packages/fluent-editor/src/modules/index.ts (1 hunks)
  • packages/fluent-editor/vite.config.ts (1 hunks)
  • packages/projects/src/views/yuque/YuQue.vue (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/docs/fluent-editor/demos/flow-chart-resize.vue
  • packages/fluent-editor/vite.config.ts
  • packages/fluent-editor/package.json
  • packages/docs/fluent-editor/demos/flow-chart.vue
  • packages/docs/types/global.d.ts
  • packages/fluent-editor/src/modules/index.ts
  • packages/docs/fluent-editor/demos/mind-map-line.vue
  • packages/docs/fluent-editor/demos/flow-chart-grid.vue
  • packages/docs/fluent-editor/demos/mind-map-background.vue
  • packages/docs/fluent-editor/demos/flow-chart-background.vue
  • packages/projects/src/views/yuque/YuQue.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (2)
packages/docs/fluent-editor/demos/mind-map-resize.vue (2)

8-14: LGTM!

The toolbar configuration is clean and correctly includes the mind-map button.


54-56: LGTM!

The template is clean and correctly binds the editor container reference.

Comment on lines +17 to +50
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
resize: true,
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for module loading failures.

The Promise.all has no .catch() handler. If any of the dynamic imports fail, the error will be silently swallowed, leaving the user with a non-functional editor and no feedback.

Apply this diff to add error handling:

   Promise.all([
     import('@opentiny/fluent-editor'),
     import('simple-mind-map'),
     import('simple-mind-map/src/plugins/Drag.js'),
     import('simple-mind-map/src/plugins/Export.js'),
     import('simple-mind-map-plugin-themes'),
   ]).then(
     ([
       { default: FluentEditor },
       { default: SimpleMindMap },
       { default: Drag },
       { default: Export },
       { default: Themes },
     ]) => {
       if (!editorRef.value) return
       editor = new FluentEditor(editorRef.value, {
         theme: 'snow',
         modules: {
           'toolbar': TOOLBAR_CONFIG,
           'mind-map': {
             deps: {
               SimpleMindMap,
               Themes,
               Drag,
               Export,
             },
             resize: true,
           },
         },
       })
       const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
       editor.setContents(ops)
     },
-  )
+  ).catch((error) => {
+    console.error('Failed to load mind-map dependencies:', error)
+  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
resize: true,
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
resize: true,
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
).catch((error) => {
console.error('Failed to load mind-map dependencies:', error)
})
🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map-resize.vue around lines 17 to 50,
the Promise.all dynamic imports lack error handling which will silently swallow
failures; add a .catch handler on the Promise chain to capture any import
errors, log the error (use console.error or the project logger) and surface a
user-friendly message or fallback behavior (e.g., show an error notice or
disable the editor UI) so the failure does not leave the page silently broken;
ensure the .catch runs when imports fail and prevents further initialization
when necessary.

},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract the ops array to improve readability.

Line 47 contains an 800+ character array literal that makes the code extremely difficult to read and maintain. Extract this to a constant or helper function.

Apply this diff to extract the ops array:

+const INITIAL_MIND_MAP_OPS = [
+  { insert: '\n' },
+  {
+    insert: {
+      'mind-map': {
+        layout: 'logicalStructure',
+        root: {
+          data: {
+            text: '根节点',
+            expand: true,
+            uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a',
+            isActive: false,
+          },
+          children: [
+            {
+              data: {
+                text: '二级节点',
+                uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d',
+                expand: true,
+                richText: false,
+                isActive: false,
+              },
+              children: [],
+            },
+          ],
+          smmVersion: '0.14.0-fix.1',
+        },
+        theme: {
+          template: 'default',
+          config: {},
+        },
+        view: {
+          transform: {
+            scaleX: 1,
+            scaleY: 1,
+            shear: 0,
+            rotate: 0,
+            translateX: 0,
+            translateY: 0,
+            originX: 0,
+            originY: 0,
+            a: 1,
+            b: 0,
+            c: 0,
+            d: 1,
+            e: 0,
+            f: 0,
+          },
+          state: {
+            scale: 1,
+            x: 0,
+            y: 0,
+            sx: 0,
+            sy: 0,
+          },
+        },
+      },
+    },
+  },
+  { insert: '\n\n' },
+]
+
 onMounted(() => {
   Promise.all([
     import('@opentiny/fluent-editor'),
     import('simple-mind-map'),
     import('simple-mind-map/src/plugins/Drag.js'),
     import('simple-mind-map/src/plugins/Export.js'),
     import('simple-mind-map-plugin-themes'),
   ]).then(
     ([
       { default: FluentEditor },
       { default: SimpleMindMap },
       { default: Drag },
       { default: Export },
       { default: Themes },
     ]) => {
       if (!editorRef.value) return
       editor = new FluentEditor(editorRef.value, {
         theme: 'snow',
         modules: {
           'toolbar': TOOLBAR_CONFIG,
           'mind-map': {
             deps: {
               SimpleMindMap,
               Themes,
               Drag,
               Export,
             },
             resize: true,
           },
         },
       })
-      const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
-      editor.setContents(ops)
+      editor.setContents(INITIAL_MIND_MAP_OPS)
     },
   )
 })

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map-resize.vue around line 47 the very
large inline ops array literal makes the file hard to read; extract that array
to a named constant (e.g., DEFAULT_MIND_MAP_OPS) or a helper function placed
near the top of the file (or in a small local module if reused), then replace
the inline literal with a simple reference (const ops = DEFAULT_MIND_MAP_OPS) in
the demo; ensure the constant is clearly named, typed/annotated if relevant, and
keep the original structure/contents intact.

Comment on lines +3 to +52
import { onMounted, ref } from 'vue'

let editor: FluentEditor
const editorRef = ref<HTMLElement>()

const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]

onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
theme: 'minions',
},
},
})

const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'minions', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Destroy the FluentEditor instance during component teardown.

Same lifecycle gap here: the editor is created on mount but never released, so every visit leaves listeners hanging around. Please mirror the fix from the base demo—import onBeforeUnmount and call editor?.destroy() there.

-import { onMounted, ref } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
@@
   )
 })
+
+onBeforeUnmount(() => {
+  editor?.destroy()
+})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { onMounted, ref } from 'vue'
let editor: FluentEditor
const editorRef = ref<HTMLElement>()
const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]
onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
theme: 'minions',
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'minions', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
import { onMounted, onBeforeUnmount, ref } from 'vue'
let editor: FluentEditor
const editorRef = ref<HTMLElement>()
const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]
onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
theme: 'minions',
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'minions', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
onBeforeUnmount(() => {
editor?.destroy()
})
🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map-theme.vue around lines 3 to 52,
the FluentEditor instance is created on mount but never destroyed; import
onBeforeUnmount from 'vue' and add an onBeforeUnmount handler that calls
editor?.destroy() (and optionally clears editorRef.value) to remove listeners
and free resources when the component is torn down.

Comment on lines +3 to +50
import { onMounted, ref } from 'vue'

let editor: FluentEditor
const editorRef = ref<HTMLElement>()

const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]

onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Ensure FluentEditor is destroyed on unmount.

The demo instantiates FluentEditor during onMounted but never disposes it. When the user navigates away (VitePress tears down the component), the instance keeps its window/document listeners alive, so returning to the page stacks duplicate handlers and leaks memory. Add an onBeforeUnmount hook that calls editor?.destroy() and include the import.

-import { onMounted, ref } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
@@
 onMounted(() => {
   Promise.all([
@@
   )
 })
+
+onBeforeUnmount(() => {
+  editor?.destroy()
+})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { onMounted, ref } from 'vue'
let editor: FluentEditor
const editorRef = ref<HTMLElement>()
const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]
onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
import { onMounted, onBeforeUnmount, ref } from 'vue'
let editor: FluentEditor
const editorRef = ref<HTMLElement>()
const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
['clean'],
['mind-map'],
]
onMounted(() => {
Promise.all([
import('@opentiny/fluent-editor'),
import('simple-mind-map'),
import('simple-mind-map/src/plugins/Drag.js'),
import('simple-mind-map/src/plugins/Export.js'),
import('simple-mind-map-plugin-themes'),
]).then(
([
{ default: FluentEditor },
{ default: SimpleMindMap },
{ default: Drag },
{ default: Export },
{ default: Themes },
]) => {
if (!editorRef.value) return
editor = new FluentEditor(editorRef.value, {
theme: 'snow',
modules: {
'toolbar': TOOLBAR_CONFIG,
'mind-map': {
deps: {
SimpleMindMap,
Themes,
Drag,
Export,
},
},
},
})
const ops = [{ insert: '\n' }, { insert: { 'mind-map': { layout: 'logicalStructure', root: { data: { text: '根节点', expand: true, uid: '36bae545-da0b-4c08-be14-ff05f7f05d0a', isActive: false }, children: [{ data: { text: '二级节点', uid: 'ef0895d2-b5cc-4214-b0ee-e29f8f02420d', expand: true, richText: false, isActive: false }, children: [] }], smmVersion: '0.14.0-fix.1' }, theme: { template: 'default', config: {} }, view: { transform: { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0, a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, state: { scale: 1, x: 0, y: 0, sx: 0, sy: 0 } } } } }, { insert: '\n\n' }]
editor.setContents(ops)
},
)
})
onBeforeUnmount(() => {
editor?.destroy()
})
🤖 Prompt for AI Agents
In packages/docs/fluent-editor/demos/mind-map.vue around lines 3 to 50, the
FluentEditor instance created in onMounted is never disposed which leaks event
listeners; import onBeforeUnmount from 'vue' and add an onBeforeUnmount hook
that calls editor?.destroy() (guarded for undefined) to clean up the editor when
the component unmounts.

@kagol kagol merged commit 2f102e2 into main Oct 30, 2025
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-test enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants