Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export const CONFIG_EVENTS = {
OAUTH_LOGIN_START: 'config:oauth-login-start', // OAuth登录开始
OAUTH_LOGIN_SUCCESS: 'config:oauth-login-success', // OAuth登录成功
OAUTH_LOGIN_ERROR: 'config:oauth-login-error', // OAuth登录失败
THEME_CHANGED: 'config:theme-changed' // 主题变更事件
THEME_CHANGED: 'config:theme-changed', // 主题变更事件
FONT_SIZE_CHANGED: 'config:font-size-changed' // 字体大小变更事件
}

// 会话相关事件
Expand Down
5 changes: 5 additions & 0 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ export class ConfigPresenter implements IConfigPresenter {
this.store.set(key, value)
// 触发设置变更事件(仅主进程内部使用)
eventBus.sendToMain(CONFIG_EVENTS.SETTING_CHANGED, key, value)

// 特殊处理:字体大小设置需要通知所有标签页
if (key === 'fontSizeLevel') {
eventBus.sendToRenderer(CONFIG_EVENTS.FONT_SIZE_CHANGED, SendTarget.ALL_WINDOWS, value)
}
} catch (error) {
console.error(`[Config] Failed to set setting ${key}:`, error)
}
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const CONFIG_EVENTS = {
LANGUAGE_CHANGED: 'config:language-changed', // 新增:语言变更事件
SOUND_ENABLED_CHANGED: 'config:sound-enabled-changed', // 新增:声音启用状态变更事件
COPY_WITH_COT_CHANGED: 'config:copy-with-cot-enabled-changed',
THEME_CHANGED: 'config:theme-changed'
THEME_CHANGED: 'config:theme-changed',
FONT_SIZE_CHANGED: 'config:font-size-changed'
}

// 会话相关事件
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/src/stores/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,9 @@ export const useSettingsStore = defineStore('settings', () => {

// 设置拷贝事件监听器
setupCopyWithCotEnabledListener()

// 设置字体大小事件监听器
setupFontSizeListener()
}
Comment on lines +695 to 697
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Duplicate listener registration path; move or guard setupFontSizeListener to avoid double handlers.

setupProviderListener() is invoked inside initSettings() and again in onMounted(), which means setupFontSizeListener() is currently registered at least twice. This will cause duplicate event handling and jitter when FONT_SIZE_CHANGED fires. The AI summary also states it’s called directly during initialization, but in code it’s nested under setupProviderListener().

Apply this diff to prevent duplicate registration here (call it once from initSettings instead; see snippet below):

-    // 设置字体大小事件监听器
-    setupFontSizeListener()
+    // Font-size listener is initialized once during initSettings()

Additionally, update the initialization and lifecycle (outside this hunk) as follows:

  • In initSettings(), after setupCopyWithCotEnabledListener(), call setupFontSizeListener().
  • In onMounted(), remove the second call to setupProviderListener() to avoid double-binding.

Example (outside changed ranges):

// inside initSettings(), near other listener setup
setupContentProtectionListener()
setupCopyWithCotEnabledListener()
setupFontSizeListener()  // add this

// onMounted
onMounted(async () => {
  await initSettings()
  // remove: await setupProviderListener()
})

Optionally, add cleanup:

cleanup = () => {
  window.electron?.ipcRenderer?.removeAllListeners(CONFIG_EVENTS.FONT_SIZE_CHANGED)
  // ...existing cleanup
}
🤖 Prompt for AI Agents
In src/renderer/src/stores/settings.ts around lines 695-697,
setupFontSizeListener is being registered twice because setupProviderListener
(which calls setupFontSizeListener) is invoked both from initSettings and again
in onMounted; to fix, remove the second registration by calling
setupFontSizeListener once from initSettings (after
setupCopyWithCotEnabledListener) and delete the extra await
setupProviderListener() call in onMounted, and additionally either add a guard
inside setupFontSizeListener to no-op if already registered or add cleanup logic
that removes CONFIG_EVENTS.FONT_SIZE_CHANGED listeners during teardown to
prevent duplicate handlers.


// 更新本地模型状态,不触发后端请求
Expand Down Expand Up @@ -1434,6 +1437,15 @@ export const useSettingsStore = defineStore('settings', () => {
)
}

const setupFontSizeListener = () => {
window.electron.ipcRenderer.on(
CONFIG_EVENTS.FONT_SIZE_CHANGED,
(_event, newFontSizeLevel: number) => {
fontSizeLevel.value = newFontSizeLevel
}
)
}

Comment on lines +1440 to +1448
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Make the listener idempotent and validate/clamp payload before applying.

Guard against multiple registrations and out-of-range/invalid payloads. This avoids leaks under HMR and prevents invalid class indexing.

-const setupFontSizeListener = () => {
-  window.electron.ipcRenderer.on(
-    CONFIG_EVENTS.FONT_SIZE_CHANGED,
-    (_event, newFontSizeLevel: number) => {
-      fontSizeLevel.value = newFontSizeLevel
-    }
-  )
-}
+const setupFontSizeListener = () => {
+  // Ensure idempotency: remove any existing handlers before binding
+  window.electron?.ipcRenderer?.removeAllListeners(CONFIG_EVENTS.FONT_SIZE_CHANGED)
+  window.electron.ipcRenderer.on(
+    CONFIG_EVENTS.FONT_SIZE_CHANGED,
+    (_event, newFontSizeLevel: number) => {
+      // Validate and clamp to valid range
+      const parsed =
+        typeof newFontSizeLevel === 'number' ? newFontSizeLevel : Number(newFontSizeLevel)
+      const clamped = Number.isFinite(parsed)
+        ? Math.max(0, Math.min(parsed, FONT_SIZE_CLASSES.length - 1))
+        : DEFAULT_FONT_SIZE_LEVEL
+      fontSizeLevel.value = clamped
+    }
+  )
+}

After applying, trigger a few rapid font-size changes in one tab and confirm only one handler fires by temporarily logging inside the listener once. You should see a single log per event across tabs.


🏁 Script executed:

#!/bin/bash
# Search for the setupFontSizeListener implementation
rg -n "setupFontSizeListener" -C3 --type=ts

# Check for FONT_SIZE_CLASSES definition or usage
rg -n "FONT_SIZE_CLASSES" -C3 --type=ts

# Check for DEFAULT_FONT_SIZE_LEVEL definition or usage
rg -n "DEFAULT_FONT_SIZE_LEVEL" -C3 --type=ts

# Inspect ipcRenderer usage patterns
rg -n "ipcRenderer" -C3 --type=ts

Length of output: 29703


Implement Idempotent Font-Size Listener with Input Validation

To avoid duplicate handlers during HMR and prevent out-of-range values from corrupting the UI, update the listener in src/renderer/src/stores/settings.ts as follows:

• Before registering, clear any existing "FONT_SIZE_CHANGED" handlers.
• Inside the callback, coerce non-numeric payloads, clamp to [0, FONT_SIZE_CLASSES.length-1], and default on invalid input.

Apply this diff at lines ~1439–1448:

 const setupFontSizeListener = () => {
-  window.electron.ipcRenderer.on(
-    CONFIG_EVENTS.FONT_SIZE_CHANGED,
-    (_event, newFontSizeLevel: number) => {
-      fontSizeLevel.value = newFontSizeLevel
-    }
-  )
+  // Ensure single registration (idempotent under HMR)
+  window.electron.ipcRenderer.removeAllListeners(CONFIG_EVENTS.FONT_SIZE_CHANGED)
+  window.electron.ipcRenderer.on(
+    CONFIG_EVENTS.FONT_SIZE_CHANGED,
+    (_event, payload: unknown) => {
+      // Parse and clamp the new level
+      const raw = typeof payload === 'number' ? payload : Number(payload)
+      const valid = Number.isFinite(raw)
+        ? Math.min(Math.max(0, raw), FONT_SIZE_CLASSES.length - 1)
+        : DEFAULT_FONT_SIZE_LEVEL
+      fontSizeLevel.value = valid
+    }
+  )
 }

Locations to update:

  • src/renderer/src/stores/settings.ts around setupFontSizeListener (lines 1439–1448).
📝 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
const setupFontSizeListener = () => {
window.electron.ipcRenderer.on(
CONFIG_EVENTS.FONT_SIZE_CHANGED,
(_event, newFontSizeLevel: number) => {
fontSizeLevel.value = newFontSizeLevel
}
)
}
const setupFontSizeListener = () => {
// Ensure single registration (idempotent under HMR)
window.electron.ipcRenderer.removeAllListeners(CONFIG_EVENTS.FONT_SIZE_CHANGED)
window.electron.ipcRenderer.on(
CONFIG_EVENTS.FONT_SIZE_CHANGED,
(_event, payload: unknown) => {
// Parse and clamp the new level
const raw = typeof payload === 'number' ? payload : Number(payload)
const valid = Number.isFinite(raw)
? Math.min(Math.max(0, raw), FONT_SIZE_CLASSES.length - 1)
: DEFAULT_FONT_SIZE_LEVEL
fontSizeLevel.value = valid
}
)
}
🤖 Prompt for AI Agents
In src/renderer/src/stores/settings.ts around lines 1439–1448, the
FONT_SIZE_CHANGED listener can be registered multiple times during HMR and
accepts unvalidated payloads; before registering call
window.electron.ipcRenderer.removeAllListeners(CONFIG_EVENTS.FONT_SIZE_CHANGED)
(or the equivalent off/unsubscribe API) to ensure idempotence, then in the
callback coerce the payload to a number (e.g. Number(newFontSizeLevel)), check
for NaN and fall back to a sane default (e.g. 0), clamp the value to the valid
range [0, FONT_SIZE_CLASSES.length - 1], and only then assign
fontSizeLevel.value to the clamped value.

///////////////////////////////////////////////////////////////////////////////////////
const findModelByIdOrName = (
modelId: string
Expand Down