Skip to content

feat!: migrate i18n to quill-i18n#461

Merged
kagol merged 4 commits intoopentiny:devfrom
zzxming:feat-i18n
Mar 26, 2026
Merged

feat!: migrate i18n to quill-i18n#461
kagol merged 4 commits intoopentiny:devfrom
zzxming:feat-i18n

Conversation

@zzxming
Copy link
Copy Markdown
Collaborator

@zzxming zzxming commented Mar 22, 2026

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

  • 初始化 option 变动: { i18n?: { lang?: string; langText?: Record<string, string>; } } -> { i18n?: { locale?: string; messages?: Record<string, any>; } }
  • 语言切换方法从 changeLanguage({ lang, langText }) 改为 setLocale(locale)
  • 以下旧 API 已被替换:
    • 动态注册方式变动:I18N.register(...) -> 使用 i18n.addMessages(locale, messages)
    • 解析函数变动:I18N.parserText(key, lang) -> 使用 i18n.t(key)。但仍然兼容 quill.getLangText(key)
    • 语言变更触发事件变动:CHANGE_LANGUAGE_EVENT -> I18N_LOCALE_CHANGE
    • I18N 类型 -> 使用 I18n 类型

Summary by CodeRabbit

  • New Features

    • Integrated Emoji Mart picker and improved emoji positioning
    • Added runtime language selector and expanded i18n support across demos
    • Extended table toolbar options and tooltip improvements
  • Bug Fixes

    • Stabilized UI test selectors and interaction flows for editor demos
    • Improved image overlay dismissal and zoom interaction behavior
  • Documentation

    • Updated i18n usage and language-switching guidance
  • Chores

    • Dependency updates for editor-related packages

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 56708a4b-1a31-46b4-96cb-3586ee62dd4e

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2d089 and cb5ab9e.

📒 Files selected for processing (2)
  • packages/docs/fluent-editor/demos/mind-map.spec.ts
  • packages/fluent-editor/src/modules/link/modules/tooltip.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/docs/fluent-editor/demos/mind-map.spec.ts
  • packages/fluent-editor/src/modules/link/modules/tooltip.ts

Walkthrough

The PR replaces the local i18n implementation with the external quill-i18n library, updates editor initialization and i18n APIs (getLocale/setLocale/I18N_LOCALE_CHANGE), adapts modules and demos to the new API, refactors tests/selectors, adjusts styles, and bumps related dependencies.

Changes

Cohort / File(s) Summary
Core i18n migration
packages/fluent-editor/src/fluent-editor.ts, packages/fluent-editor/src/core/fluent-editor.ts, packages/fluent-editor/src/modules/i18n.ts
Removed local I18N module, imported quill-i18n, switched initialization/registration to use the external i18n instance and merged built-in messages into module options; updated FluentEditor lang/getLangText APIs.
Module integrations & event wiring
packages/fluent-editor/src/modules/.../control-panel.ts, .../context-menu.ts, flow-chart/..., mind-map/..., table-up/index.ts, toolbar/toolbar-tip.ts, link/modules/tooltip.ts, shortcut-key/index.ts, themes/snow.ts, modules/counter.ts
Replaced CHANGE_LANGUAGE_EVENT with I18N_LOCALE_CHANGE listeners, switched text resolution from static parser to this.quill.getLangText(...), adapted locale retrieval to i18nModule.getLocale(), and updated registration calls to i18nModule.addMessages(...).
Table-up & toolbar-tip refactor
packages/fluent-editor/src/modules/table-up/index.ts, packages/fluent-editor/src/modules/toolbar/toolbar-tip.ts
Significant refactor of option/text resolution: added resolveOptions/resolveTexts/createTextResolver patterns for table-up and extended toolbar-tip normalization to handle legacy vs new tip configs with localized strings.
Exports & package surfaces
packages/fluent-editor/src/modules/index.ts, packages/fluent-editor/src/modules/custom-image/index.ts
Removed local ./i18n re-export, added re-exports for quill-i18n and quill-shortcut-key; reordered custom-image barrel exports.
Config, types and locales
packages/fluent-editor/src/config/types/editor-modules.interface.ts, .../i18n/en-us.ts, .../i18n/zh-cn.ts, .../editor.config.ts
Updated types to use I18nOptions, changed link entries to nested objects, and adjusted toolbar alignment config.
Demos & tests
packages/docs/fluent-editor/demos/*.vue, packages/docs/fluent-editor/demos/*.spec.ts
Demo changes: emoji picker wired to Emoji Mart + Floating UI, table-up demo adds submodules and language selector, i18n demos use locale/messages and setLocale; Playwright tests updated with more specific/deterministic selectors and adjusted interaction flows.
Styles
packages/fluent-editor/src/assets/link.scss, packages/fluent-editor/src/assets/style.scss
Moved linkTooltip mixin inclusion to central stylesheet and changed tooltip pseudo-content to attr(data-before-title) for dynamic titles.
Minor code quality & casting
packages/fluent-editor/src/modules/custom-image/..., custom-uploader.ts, file/modules/file-module.ts, custom-image/preview/preview-modal.ts, preview.css
Added explicit HTMLElement casts, refined arrow syntax, cast fallback objects, removed trailing blank lines.
Docs & packages
packages/docs/fluent-editor/docs/demo/i18n.md, packages/docs/package.json, packages/fluent-editor/package.json
Documentation updated for setLocale/messages usage; dependency bumps/pinning for quill-table-up, quill-toolbar-tip, quill-shortcut-key, and added quill-i18n.

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant FE as FluentEditor
    participant I18N as quill-i18n
    participant Module as Editor Module
    participant UI as UI Component

    App->>FE: initialize({ locale, messages })
    FE->>FE: merge built-in messages with options
    FE->>I18N: configure i18n instance
    I18N-->>FE: ready (locale set)

    FE->>Module: pass i18nModule instance
    Module->>I18N: addMessages(en-US/zh-CN)
    Module->>Module: subscribe to I18N_LOCALE_CHANGE

    App->>FE: user triggers language change
    FE->>I18N: setLocale(newLocale)
    I18N-->>Module: emit I18N_LOCALE_CHANGE
    Module->>FE: request text via getLangText(key)
    FE->>I18N: get translation
    I18N-->>FE: return localized text
    FE-->>UI: update displayed text
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I nibble through each line,
Old i18n hops away in time,
Quill-i18n finds its place,
Locales dance, translations race,
Widgets hum — a joyous rhyme! 🎋

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat!: migrate i18n to quill-i18n' clearly describes the main change—migrating the i18n system to an external package with breaking changes, which matches the comprehensive refactoring evident across dozens of files in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 the enhancement New feature or request label Mar 22, 2026
Comment on lines +11 to +20
// Temporary backward-compat syntax support: `_i18n"key"`
// TODO: remove this legacy syntax support in a future major version.
const LEGACY_I18N_KEY_REGEX = /^_i18n(?:"([^"]+)"|'([^']+)'|`([^`]+)`)$/

function resolveLegacyI18nKey(name: string): string {
const key = name.trim()
const matched = key.match(LEGACY_I18N_KEY_REGEX)
if (!matched) return name
return matched[1] || matched[2] || matched[3] || name
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

此处对原先的解析方法做了兼容,后续版本变动再放弃兼容旧的使用方式

@zzxming zzxming marked this pull request as ready for review March 22, 2026 09:21
Copy link
Copy Markdown

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts (2)

59-62: ⚠️ Potential issue | 🔴 Critical

Bug: handler.getText('delete') returns undefined.

Same issue as updateContextMenuItems() - should use 'deleteContent' to match the key defined in resolveTexts().

🐛 Proposed fix
   addContextMenuItem(blot, handler.getText('copy'), () => handleCopy(blot))
   addContextMenuItem(blot, handler.getText('cut'), () => handleCut(blot))
   addContextMenuItem(blot, handler.getText('paste'), () => handlePaste(blot))
-  addContextMenuItem(blot, handler.getText('delete'), () => handleDeleteContent(blot))
+  addContextMenuItem(blot, handler.getText('deleteContent'), () => handleDeleteContent(blot))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts` around
lines 59 - 62, The context menu is calling handler.getText('delete') which
returns undefined; change the key to match resolveTexts() by using
'deleteContent' wherever getText is called for the delete action (specifically
update the calls in addContextMenuItem for the delete menu item that invokes
handleDeleteContent(blot)); ensure consistency with updateContextMenuItems() and
resolveTexts() so handler.getText('deleteContent') is used instead of 'delete'.

36-46: ⚠️ Potential issue | 🔴 Critical

Bug: this.texts.delete is undefined; should be this.texts.deleteContent.

The resolveTexts() method (lines 25-34) defines deleteContent, deleteNode, and deleteIcon, but updateContextMenuItems() references this.texts.delete which doesn't exist. This will set the 4th menu item's text to undefined.

🐛 Proposed fix
   updateContextMenuItems() {
     if (!this.blot.contextMenu) return

     const menuItems = this.blot.contextMenu.querySelectorAll('.ql-mind-map-context-menu-item')
     if (menuItems.length >= 4) {
       menuItems[0].textContent = this.texts.copy
       menuItems[1].textContent = this.texts.cut
       menuItems[2].textContent = this.texts.paste
-      menuItems[3].textContent = this.texts.delete
+      menuItems[3].textContent = this.texts.deleteContent
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts` around
lines 36 - 46, The fourth context-menu item is being set from a non-existent
property this.texts.delete causing undefined text; in updateContextMenuItems()
replace the reference to this.texts.delete with the correct property
this.texts.deleteContent (the resolved names are defined in resolveTexts()),
i.e., set menuItems[3].textContent = this.texts.deleteContent so the "delete"
menu entry displays the intended string.
packages/fluent-editor/src/modules/table-up/index.ts (1)

28-48: ⚠️ Potential issue | 🟠 Major

Missing cleanup for I18N_LOCALE_CHANGE event listener.

The event listener attached in the constructor is never removed. This can cause memory leaks if the module is destroyed while the listener remains attached to the quill instance.

Store the handler reference and remove it in a destroy() override:

🛠️ Proposed fix
     constructor(public quill: FluentEditor, options: Partial<any>) {
       super(quill, options)

       if (!this.quill.options['format-painter']) this.quill.options['format-painter'] = {}
       const currentIgnoreFormat = this.quill.options['format-painter'].ignoreFormat || []
       this.quill.options['format-painter'].ignoreFormat = Array.from(
         new Set([
           ...currentIgnoreFormat,
           'table-up-cell-inner',
         ]),
       )

-      this.quill.on(I18N_LOCALE_CHANGE, () => {
+      this.localeChangeHandler = () => {
         this.refreshUI()
         const toolbar = this.quill.getModule('toolbar') as Toolbar
         if (toolbar && (this.quill.theme as QuillTheme).pickers) {
           const [, select] = (toolbar.controls as [string, HTMLElement][] || []).find(([name]) => name === this.statics.toolName) || []
           if (select && select.tagName.toLocaleLowerCase() === 'select') {
             const picker = (this.quill.theme as QuillTheme).pickers.find(picker => picker.select === select)
             if (picker) {
               this.buildCustomSelect(this.options.customSelect, picker)
             }
           }
         }

         Object.keys(this.modules).forEach((key) => {
           if (isFunction(this.modules[key].destroy)) {
             this.modules[key].destroy()
           }
         })
         this.modules = {}
         this.initModules()
-      })
+      }
+      this.quill.on(I18N_LOCALE_CHANGE, this.localeChangeHandler)
     }
+
+    destroy() {
+      this.quill.off(I18N_LOCALE_CHANGE, this.localeChangeHandler)
+      if (super.destroy) {
+        super.destroy()
+      }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/table-up/index.ts` around lines 28 - 48,
The I18N_LOCALE_CHANGE listener registered with this.quill.on in the constructor
is never removed, causing leaks; capture the handler (e.g., assign the arrow
function to this._i18nHandler and call this.quill.on(I18N_LOCALE_CHANGE,
this._i18nHandler)) and then override/detect destroy() (or module teardown) to
call this.quill.off(I18N_LOCALE_CHANGE, this._i18nHandler) (or removeListener)
before cleaning this.modules; update references to the handler name and ensure
it is cleared (this._i18nHandler = null) when removed.
🧹 Nitpick comments (7)
packages/fluent-editor/src/modules/custom-image/preview/preview-modal.ts (1)

113-120: Consider removing document event listeners in the destroy method.

The keydown and wheel event listeners attached to document are not removed in the destroy() method (lines 248-263). While the global singleton pattern means the instance typically lives for the application lifetime, the presence of a destroy() method suggests cleanup is intended. Store references to the listener functions and call removeEventListener in destroy() to prevent potential memory leaks if the modal is ever destroyed and recreated.

♻️ Proposed fix to store and remove event listeners

Store listener references as class properties and remove them in destroy:

 export class ImagePreviewModal {
   private modal: HTMLElement | null = null
   private overlay: HTMLElement | null = null
   private previewImage: HTMLImageElement | null = null
   private scaleTooltip: HTMLElement | null = null
+  private keydownListener: ((e: KeyboardEvent) => void) | null = null
+  private wheelListener: ((e: WheelEvent) => void) | null = null
   private currentScale: number = 1
     // 绑定事件
     this.overlay.addEventListener('click', () => this.hide())
-    document.addEventListener('keydown', (e) => {
+    this.keydownListener = (e: KeyboardEvent) => {
       if (e.key === 'Escape') {
         this.hide()
       }
-    })
+    }
+    document.addEventListener('keydown', this.keydownListener)

     // 绑定滚轮缩放事件
-    document.addEventListener('wheel', e => this.onMouseWheel(e), { passive: false })
+    this.wheelListener = (e: WheelEvent) => this.onMouseWheel(e)
+    document.addEventListener('wheel', this.wheelListener, { passive: false })
   destroy() {
     this.hideScaleTooltip()
+    if (this.keydownListener) {
+      document.removeEventListener('keydown', this.keydownListener)
+      this.keydownListener = null
+    }
+    if (this.wheelListener) {
+      document.removeEventListener('wheel', this.wheelListener)
+      this.wheelListener = null
+    }
     if (this.overlay && this.overlay.parentNode) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/custom-image/preview/preview-modal.ts`
around lines 113 - 120, Store the document event handlers as class properties
(e.g., this.handleKeydown and this.handleWheel) instead of inline arrow
functions so they can be removed later; wire this.handleKeydown to call
this.hide() when e.key === 'Escape' and wire this.handleWheel to call
this.onMouseWheel(e), attach them with document.addEventListener where currently
added, and then remove them in destroy() via
document.removeEventListener('keydown', this.handleKeydown) and
document.removeEventListener('wheel', this.handleWheel, { passive: false }) (or
matching options) to ensure proper cleanup.
packages/fluent-editor/src/modules/custom-uploader.ts (1)

99-99: Prefer typed narrowing over as any in getMaxSize/getMultiple.

At Line 99 and Line 111, as any removes useful compile-time checks and makes future regressions easier to miss. Keep these maps strongly typed (same pattern as getAccept) so config handling stays explicit.

Typed alternative
-    const map = (maxSize || {}) as any
+    const map: Partial<Record<UploadKind, number>> =
+      typeof maxSize === 'number' ? {} : (maxSize ?? {})

-    const map = (multiple || {}) as any
+    const map: Partial<Record<UploadKind, boolean>> =
+      typeof multiple === 'boolean' ? {} : (multiple ?? {})

Also applies to: 111-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/custom-uploader.ts` at line 99, The use of
"as any" on the local map in getMaxSize/getMultiple (e.g., the variable named
map) removes type safety; replace the cast with a properly typed map (mirror the
pattern used in getAccept) such as a typed Record<string, number|null> for
getMaxSize and Record<string, boolean> for getMultiple, or explicitly type the
map variable via an interface/generic so assignments and lookups are checked at
compile time; update the map declaration in getMaxSize and getMultiple
accordingly and adjust any downstream uses to match the new types to preserve
strong typing and avoid the as any cast.
packages/fluent-editor/src/modules/flow-chart/i18n/index.ts (1)

4-6: LGTM!

The migration to addMessages API aligns with quill-i18n. The separate locale registration is clean.

Consider adding type safety by importing and using the I18n type from quill-i18n instead of any, if available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/flow-chart/i18n/index.ts` around lines 4 -
6, Replace the loose any type on the registerFlowChartI18N parameter with the
quill-i18n I18n type to add compile-time safety: import the I18n (or equivalent)
type from 'quill-i18n' and change the function signature
registerFlowChartI18N(i18nModule: any) to use that imported I18n type so calls
to i18nModule.addMessages('en-US', FLOW_CHART_EN_US) and
i18nModule.addMessages('zh-CN', FLOW_CHART_ZH_CN) are type-checked.
packages/docs/package.json (1)

30-31: Version pinning inconsistency after major version jump.

quill-toolbar-tip jumped from ^0.0.13 to 1.1.0 — a significant version change. Additionally, this dependency is now pinned without a caret prefix, unlike quill-table-up (^3.4.0), which may prevent patch updates from being applied.

Consider using ^1.1.0 for consistency unless the pinned version is intentional to avoid potential breaking changes in future releases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/package.json` around lines 30 - 31, The package.json dependency
for quill-toolbar-tip is pinned as "1.1.0" while quill-table-up uses a caret
("^3.4.0"), creating an inconsistency after the major jump; update the
dependency entry for quill-toolbar-tip to use a caret (e.g., "^1.1.0") in
packages/docs/package.json (adjust the "quill-toolbar-tip" value) unless the pin
was intentional, to allow patch/minor updates consistently with
"quill-table-up".
packages/fluent-editor/src/modules/mind-map/i18n/index.ts (1)

4-6: Consider adding type annotation for better maintainability.

The function works correctly with the new addMessages() API. However, using any type for i18nModule reduces type safety.

💡 Suggested type improvement
+import type { I18n } from 'quill-i18n'
+
 import { MIND_MAP_EN_US } from './en-us'
 import { MIND_MAP_ZH_CN } from './zh-cn'

-export function registerMindMapI18N(i18nModule: any) {
+export function registerMindMapI18N(i18nModule: I18n) {
   i18nModule.addMessages('en-US', MIND_MAP_EN_US)
   i18nModule.addMessages('zh-CN', MIND_MAP_ZH_CN)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/mind-map/i18n/index.ts` around lines 4 -
6, The parameter i18nModule in registerMindMapI18N is typed as any which reduces
type safety; replace any with a proper interface/type that includes the
addMessages(locale: string, messages: object) method (or import the existing
i18n module type if available) and update the function signature to use that
type so calls to i18nModule.addMessages('en-US', MIND_MAP_EN_US) and
addMessages('zh-CN', MIND_MAP_ZH_CN) are type-checked.
packages/docs/fluent-editor/demos/collaborative-editing.spec.ts (1)

98-105: Redundant if/else branches - both paths are identical.

The if (lv <= 2) and else branches execute the same code. This appears to be leftover from a refactor.

♻️ Proposed simplification
   for (const lv of levels) {
     await p1.locator('.ql-editor').first().click()
     await selectAll(p1)
-    if (lv <= 2) {
-      await p1.locator('.ql-toolbar .ql-picker.ql-header').click()
-      await p1.locator('.ql-toolbar').getByRole('button', { name: `Heading ${lv}` }).click()
-    }
-    else {
-      await p1.locator('.ql-toolbar .ql-picker.ql-header').click()
-      await p1.locator('.ql-toolbar').getByRole('button', { name: `Heading ${lv}` }).click()
-    }
+    await p1.locator('.ql-toolbar .ql-picker.ql-header').click()
+    await p1.locator('.ql-toolbar').getByRole('button', { name: `Heading ${lv}` }).click()
     await expect.poll(() => headingMatched(p2, lv, 'Title')).toBeTruthy()
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/fluent-editor/demos/collaborative-editing.spec.ts` around lines
98 - 105, The if/else in the collaborative-editing spec is redundant: both
branches perform identical actions for variable lv. Remove the conditional and
replace with a single sequence that clicks the header picker and the
corresponding heading button using the existing p1 locators (e.g.,
p1.locator('.ql-toolbar .ql-picker.ql-header').click() and
p1.locator('.ql-toolbar').getByRole('button', { name: `Heading ${lv}`
}).click()); keep the lv reference and existing locator calls (p1.locator(...),
getByRole(...)) unchanged.
packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts (1)

1-1: Import I18N_LOCALE_CHANGE constant for consistency.

This file uses the string literal 'i18n-locale-change' on line 18, while other modules (e.g., mind-map/control-panel.ts, mind-map/context-menu.ts) import and use the I18N_LOCALE_CHANGE constant from quill-i18n. Using the constant ensures consistency and prevents breakage if the event name changes.

♻️ Proposed fix
-import type { I18n } from 'quill-i18n'
+import { type I18n, I18N_LOCALE_CHANGE } from 'quill-i18n'

Then update line 18:

-    this.quill.on('i18n-locale-change', (event: { locale: string, oldLocale: string }) => {
+    this.quill.on(I18N_LOCALE_CHANGE, (event: { locale: string, oldLocale: string }) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts` at
line 1, Import the I18N_LOCALE_CHANGE constant from 'quill-i18n' and replace the
literal string 'i18n-locale-change' used in this file (in the context-menu event
registration/handler around the current line 18) with the I18N_LOCALE_CHANGE
symbol so the module uses the shared event constant like other modules (e.g.,
mind-map/control-panel.ts, mind-map/context-menu.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/docs/fluent-editor/demos/mind-map.spec.ts`:
- Around line 34-38: Rename the variable miniMapCount to mindMapCount for
consistency and then replace the manual immediate count check with Playwright's
auto-retrying assertion: after clicking mindMapButton and waiting if needed, use
the locator('.ql-mind-map-item') with toHaveCount(mindMapCount + 1) instead of
awaiting page.locator(...).count() to avoid the race condition and leverage
Playwright's built-in waiting.

In `@packages/docs/fluent-editor/demos/table-up-shortcut.vue`:
- Around line 86-89: The watch on lang can call editor methods before the editor
is mounted; guard the watcher to only access editor when defined by checking
editor is truthy (or using optional chaining) inside the watch callback and/or
defer registering the watcher until onMounted completes; update the watcher
around the ref lang to verify editor (the editor variable or instance used in
the demo) before calling getModule('i18n') and setLocale so it no longer throws
if editor is undefined.

In `@packages/docs/fluent-editor/docs/demo/i18n.md`:
- Line 9: Update the docs to use the new API parameter name `locale` rather than
`lang`: change the example reference `setLocale(lang)` to `setLocale(locale)`
and search for other occurrences of `lang` in the i18n.md example to rename them
to `locale` so the docs match the breaking API and the migration contract for
`setLocale`.

In `@packages/fluent-editor/src/config/i18n/en-us.ts`:
- Around line 77-80: The i18n bundle is missing mode-specific keys used by the
dynamic lookup (e.g., link.enter-${mode}); update the 'link' object to include
explicit keys for each mode variant referenced at runtime (for example add
'enter-link-formula' and 'enter-link-video' alongside the existing
'enter-link'), mapping them to the appropriate tooltip text (or same text as
'enter-link') so lookups like link.enter-formula and link.enter-video resolve
correctly when using the dynamic lookup.

In `@packages/fluent-editor/src/modules/custom-image/specs/custom-image-spec.ts`:
- Around line 138-140: The code unsafely casts event.target to HTMLElement when
building imageSrc; add a runtime guard like checking "event.target instanceof
HTMLElement" before calling getAttribute. Locate the target/imageSrc logic
(variables named target and imageSrc) and change it to first verify event.target
is an HTMLElement, then read (target.getAttribute('src') ||
target.getAttribute('data-image')) into imageSrc; if the guard fails set
imageSrc to undefined/null or handle accordingly to avoid calling getAttribute
on non-HTMLElements.

In `@packages/fluent-editor/src/modules/custom-uploader.ts`:
- Around line 87-90: getAccept currently checks .length and can return a string
cast to string[] causing validateFile (which calls accept.some) to crash; update
the getAccept logic (function getAccept, variables mimetypes, map, fromKind) to
explicitly narrow to arrays using Array.isArray before returning — if fromKind
is an array return it, otherwise if map.file is an array return map.file, else
return an empty array (string[]). Ensure the function always returns a proper
string[] so callers like validateFile can safely call Array.prototype methods.

In `@packages/fluent-editor/src/modules/link/modules/tooltip.ts`:
- Around line 38-40: The current setTemplate() call inside the
I18N_LOCALE_CHANGE handler causes listen() to run each locale change,
re-attaching editor/root listeners and producing duplicates; to fix, stop
re-binding listeners on locale updates by either removing the listen()
invocation from setTemplate() so setTemplate() only updates UI strings, or make
listen() idempotent by adding a guard (e.g., a boolean like
this._listenersAttached) inside listen() to early-return if already attached;
update references to I18N_LOCALE_CHANGE, setTemplate(), and listen()
accordingly.
- Line 50: Guard against an empty data-mode before composing the i18n key: when
setting data-before-title on this.root, check this.root.dataset.mode (or fall
back to a safe default like 'default' or an empty string) and only call
this.quill.getLangText with a valid key (e.g., build the key using the guarded
mode variable) so you don't produce "link.enter-undefined"; update the
assignment that sets data-before-title to use the guarded mode value and/or a
conditional lookup.

---

Outside diff comments:
In `@packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts`:
- Around line 59-62: The context menu is calling handler.getText('delete') which
returns undefined; change the key to match resolveTexts() by using
'deleteContent' wherever getText is called for the delete action (specifically
update the calls in addContextMenuItem for the delete menu item that invokes
handleDeleteContent(blot)); ensure consistency with updateContextMenuItems() and
resolveTexts() so handler.getText('deleteContent') is used instead of 'delete'.
- Around line 36-46: The fourth context-menu item is being set from a
non-existent property this.texts.delete causing undefined text; in
updateContextMenuItems() replace the reference to this.texts.delete with the
correct property this.texts.deleteContent (the resolved names are defined in
resolveTexts()), i.e., set menuItems[3].textContent = this.texts.deleteContent
so the "delete" menu entry displays the intended string.

In `@packages/fluent-editor/src/modules/table-up/index.ts`:
- Around line 28-48: The I18N_LOCALE_CHANGE listener registered with
this.quill.on in the constructor is never removed, causing leaks; capture the
handler (e.g., assign the arrow function to this._i18nHandler and call
this.quill.on(I18N_LOCALE_CHANGE, this._i18nHandler)) and then override/detect
destroy() (or module teardown) to call this.quill.off(I18N_LOCALE_CHANGE,
this._i18nHandler) (or removeListener) before cleaning this.modules; update
references to the handler name and ensure it is cleared (this._i18nHandler =
null) when removed.

---

Nitpick comments:
In `@packages/docs/fluent-editor/demos/collaborative-editing.spec.ts`:
- Around line 98-105: The if/else in the collaborative-editing spec is
redundant: both branches perform identical actions for variable lv. Remove the
conditional and replace with a single sequence that clicks the header picker and
the corresponding heading button using the existing p1 locators (e.g.,
p1.locator('.ql-toolbar .ql-picker.ql-header').click() and
p1.locator('.ql-toolbar').getByRole('button', { name: `Heading ${lv}`
}).click()); keep the lv reference and existing locator calls (p1.locator(...),
getByRole(...)) unchanged.

In `@packages/docs/package.json`:
- Around line 30-31: The package.json dependency for quill-toolbar-tip is pinned
as "1.1.0" while quill-table-up uses a caret ("^3.4.0"), creating an
inconsistency after the major jump; update the dependency entry for
quill-toolbar-tip to use a caret (e.g., "^1.1.0") in packages/docs/package.json
(adjust the "quill-toolbar-tip" value) unless the pin was intentional, to allow
patch/minor updates consistently with "quill-table-up".

In `@packages/fluent-editor/src/modules/custom-image/preview/preview-modal.ts`:
- Around line 113-120: Store the document event handlers as class properties
(e.g., this.handleKeydown and this.handleWheel) instead of inline arrow
functions so they can be removed later; wire this.handleKeydown to call
this.hide() when e.key === 'Escape' and wire this.handleWheel to call
this.onMouseWheel(e), attach them with document.addEventListener where currently
added, and then remove them in destroy() via
document.removeEventListener('keydown', this.handleKeydown) and
document.removeEventListener('wheel', this.handleWheel, { passive: false }) (or
matching options) to ensure proper cleanup.

In `@packages/fluent-editor/src/modules/custom-uploader.ts`:
- Line 99: The use of "as any" on the local map in getMaxSize/getMultiple (e.g.,
the variable named map) removes type safety; replace the cast with a properly
typed map (mirror the pattern used in getAccept) such as a typed Record<string,
number|null> for getMaxSize and Record<string, boolean> for getMultiple, or
explicitly type the map variable via an interface/generic so assignments and
lookups are checked at compile time; update the map declaration in getMaxSize
and getMultiple accordingly and adjust any downstream uses to match the new
types to preserve strong typing and avoid the as any cast.

In `@packages/fluent-editor/src/modules/flow-chart/i18n/index.ts`:
- Around line 4-6: Replace the loose any type on the registerFlowChartI18N
parameter with the quill-i18n I18n type to add compile-time safety: import the
I18n (or equivalent) type from 'quill-i18n' and change the function signature
registerFlowChartI18N(i18nModule: any) to use that imported I18n type so calls
to i18nModule.addMessages('en-US', FLOW_CHART_EN_US) and
i18nModule.addMessages('zh-CN', FLOW_CHART_ZH_CN) are type-checked.

In `@packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts`:
- Line 1: Import the I18N_LOCALE_CHANGE constant from 'quill-i18n' and replace
the literal string 'i18n-locale-change' used in this file (in the context-menu
event registration/handler around the current line 18) with the
I18N_LOCALE_CHANGE symbol so the module uses the shared event constant like
other modules (e.g., mind-map/control-panel.ts, mind-map/context-menu.ts).

In `@packages/fluent-editor/src/modules/mind-map/i18n/index.ts`:
- Around line 4-6: The parameter i18nModule in registerMindMapI18N is typed as
any which reduces type safety; replace any with a proper interface/type that
includes the addMessages(locale: string, messages: object) method (or import the
existing i18n module type if available) and update the function signature to use
that type so calls to i18nModule.addMessages('en-US', MIND_MAP_EN_US) and
addMessages('zh-CN', MIND_MAP_ZH_CN) are type-checked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc80e2ff-6eda-4ec6-9b37-05f921754b91

📥 Commits

Reviewing files that changed from the base of the PR and between 270a8cd and 6a2d089.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (40)
  • packages/docs/fluent-editor/demos/collaborative-editing.spec.ts
  • packages/docs/fluent-editor/demos/collaborative-editing.vue
  • packages/docs/fluent-editor/demos/file-upload.spec.ts
  • packages/docs/fluent-editor/demos/file-upload.vue
  • packages/docs/fluent-editor/demos/flow-chart.spec.ts
  • packages/docs/fluent-editor/demos/i18n-custom.vue
  • packages/docs/fluent-editor/demos/i18n.vue
  • packages/docs/fluent-editor/demos/mind-map.spec.ts
  • packages/docs/fluent-editor/demos/table-up-shortcut.vue
  • packages/docs/fluent-editor/docs/demo/i18n.md
  • packages/docs/package.json
  • packages/fluent-editor/package.json
  • packages/fluent-editor/src/assets/link.scss
  • packages/fluent-editor/src/assets/style.scss
  • packages/fluent-editor/src/config/editor.config.ts
  • packages/fluent-editor/src/config/i18n/en-us.ts
  • packages/fluent-editor/src/config/i18n/zh-cn.ts
  • packages/fluent-editor/src/config/types/editor-modules.interface.ts
  • packages/fluent-editor/src/core/fluent-editor.ts
  • packages/fluent-editor/src/fluent-editor.ts
  • packages/fluent-editor/src/modules/counter.ts
  • packages/fluent-editor/src/modules/custom-image/index.ts
  • packages/fluent-editor/src/modules/custom-image/preview/preview-modal.ts
  • packages/fluent-editor/src/modules/custom-image/preview/preview.css
  • packages/fluent-editor/src/modules/custom-image/specs/custom-image-spec.ts
  • packages/fluent-editor/src/modules/custom-uploader.ts
  • packages/fluent-editor/src/modules/file/modules/file-module.ts
  • packages/fluent-editor/src/modules/flow-chart/i18n/index.ts
  • packages/fluent-editor/src/modules/flow-chart/modules/context-menu.ts
  • packages/fluent-editor/src/modules/flow-chart/modules/control-panel.ts
  • packages/fluent-editor/src/modules/i18n.ts
  • packages/fluent-editor/src/modules/index.ts
  • packages/fluent-editor/src/modules/link/modules/tooltip.ts
  • packages/fluent-editor/src/modules/mind-map/i18n/index.ts
  • packages/fluent-editor/src/modules/mind-map/modules/context-menu.ts
  • packages/fluent-editor/src/modules/mind-map/modules/control-panel.ts
  • packages/fluent-editor/src/modules/shortcut-key/index.ts
  • packages/fluent-editor/src/modules/table-up/index.ts
  • packages/fluent-editor/src/modules/toolbar/toolbar-tip.ts
  • packages/fluent-editor/src/themes/snow.ts
💤 Files with no reviewable changes (2)
  • packages/fluent-editor/src/modules/custom-image/preview/preview.css
  • packages/fluent-editor/src/modules/i18n.ts

@kagol kagol merged commit ae991fd into opentiny:dev Mar 26, 2026
1 check 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.

2 participants