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
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"i18n-ally.localesPaths": ["src/renderer/src/i18n"],
"i18n-ally.localesPaths": [
"src/renderer/src/i18n"
],
"i18n-ally.keystyle": "nested",
"i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.namespace": true,
Expand Down
2 changes: 2 additions & 0 deletions electron-builder-macx64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ electronLanguages:
- pt
- da-DK
- da
- he-IL
- he
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
Expand Down
2 changes: 2 additions & 0 deletions electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ electronLanguages:
- pt
- da-DK
- da
- he-IL
- he
win:
executableName: DeepChat
nsis:
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "DeepChat",
"version": "0.5.2",
"version": "0.5.3",
"description": "DeepChat,一个简单易用的AI客户端",
"main": "./out/main/index.js",
"author": "ThinkInAIXYZ",
Expand Down Expand Up @@ -81,6 +81,7 @@
"electron-window-state": "^5.0.3",
"es-mime-types": "^0.1.4",
"fflate": "^0.8.2",
"font-list": "^2.0.1",
"glob": "^11.0.3",
"https-proxy-agent": "^7.0.6",
"jsonrepair": "^3.13.1",
Expand Down Expand Up @@ -146,7 +147,7 @@
"katex": "^0.16.25",
"lint-staged": "^16.2.6",
"lucide-vue-next": "^0.544.0",
"markstream-vue": "0.0.2-beta.8",
"markstream-vue": "0.0.3-beta.3",
"mermaid": "^11.12.1",
"minimatch": "^10.1.1",
"monaco-editor": "^0.52.2",
Expand All @@ -156,7 +157,7 @@
"prettier": "^3.7.1",
"reka-ui": "^2.5.1",
"simple-git-hooks": "^2.13.1",
"stream-monaco": "^0.0.7",
"stream-monaco": "^0.0.8",
"tailwind-merge": "^3.4.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.1.17",
Expand Down
6 changes: 6 additions & 0 deletions src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export const CONFIG_EVENTS = {
MODEL_CONFIG_CHANGED: 'config:model-config-changed', // 模型配置变更事件
MODEL_CONFIG_RESET: 'config:model-config-reset', // 模型配置重置事件
MODEL_CONFIGS_IMPORTED: 'config:model-configs-imported', // 模型配置批量导入事件
FONT_FAMILY_CHANGED: 'config:font-family-changed',
CODE_FONT_FAMILY_CHANGED: 'config:code-font-family-changed',
// OAuth相关事件
OAUTH_LOGIN_START: 'config:oauth-login-start', // OAuth登录开始
OAUTH_LOGIN_SUCCESS: 'config:oauth-login-success', // OAuth登录成功
Expand Down Expand Up @@ -238,3 +240,7 @@ export const ACP_WORKSPACE_EVENTS = {
FILES_CHANGED: 'acp-workspace:files-changed', // File tree changed
SESSION_MODES_READY: 'acp-workspace:session-modes-ready' // Session modes available
}

export const ACP_DEBUG_EVENTS = {
EVENT: 'acp-debug:event'
}
33 changes: 31 additions & 2 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ interface IAppSettings {
default_system_prompt?: string // Default system prompt
webContentLengthLimit?: number // Web content truncation length limit, default 3000 characters
updateChannel?: string // Update channel: 'stable' | 'canary'
fontFamily?: string // Custom UI font
codeFontFamily?: string // Custom code font
[key: string]: unknown // Allow arbitrary keys, using unknown type instead of any
}

Expand Down Expand Up @@ -131,6 +133,8 @@ export class ConfigPresenter implements IConfigPresenter {
copyWithCotEnabled: true,
loggingEnabled: false,
floatingButtonEnabled: false,
fontFamily: '',
codeFontFamily: '',
default_system_prompt: '',
webContentLengthLimit: 3000,
updateChannel: 'stable', // Default to stable version
Expand Down Expand Up @@ -686,7 +690,8 @@ export class ConfigPresenter implements IConfigPresenter {
'fr-FR',
'fa-IR',
'pt-BR',
'da-DK'
'da-DK',
'he-IL'
]

// Exact match
Expand Down Expand Up @@ -862,6 +867,30 @@ export class ConfigPresenter implements IConfigPresenter {
this.uiSettingsHelper.setTraceDebugEnabled(enabled)
}

getFontFamily(): string {
return this.uiSettingsHelper.getFontFamily()
}

setFontFamily(fontFamily?: string | null): void {
this.uiSettingsHelper.setFontFamily(fontFamily)
}

getCodeFontFamily(): string {
return this.uiSettingsHelper.getCodeFontFamily()
}

setCodeFontFamily(fontFamily?: string | null): void {
this.uiSettingsHelper.setCodeFontFamily(fontFamily)
}

resetFontSettings(): void {
this.uiSettingsHelper.resetFontSettings()
}

async getSystemFonts(): Promise<string[]> {
return this.uiSettingsHelper.getSystemFonts()
}

// Get floating button switch status
getFloatingButtonEnabled(): boolean {
const value = this.getSetting<boolean>('floatingButtonEnabled') ?? false
Expand Down Expand Up @@ -1163,7 +1192,7 @@ export class ConfigPresenter implements IConfigPresenter {

private refreshAcpProviderAgents(agentIds?: string[]): void {
try {
const providerInstance = presenter?.llmproviderPresenter?.getProviderInstance('acp')
const providerInstance = presenter?.llmproviderPresenter?.getProviderInstance?.('acp')
if (!providerInstance) {
return
}
Expand Down
108 changes: 108 additions & 0 deletions src/main/presenter/configPresenter/uiSettingsHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import { eventBus, SendTarget } from '@/eventbus'
import { CONFIG_EVENTS } from '@/events'
import fontList from 'font-list'

const normalizeFontNameValue = (name: string): string => {
const trimmed = name
.replace(/\(.*?\)/g, '')
.replace(/\s+/g, ' ')
.trim()
if (!trimmed) return ''

const stripped = trimmed
.replace(
/\b(Regular|Italic|Oblique|Bold|Light|Medium|Semi\s*Bold|Black|Narrow|Condensed|Extended|Book|Roman)\b/gi,
''
)
.replace(/\s+/g, ' ')
.trim()

return stripped || trimmed
}

type SetSetting = <T>(key: string, value: T) => void
type GetSetting = <T>(key: string) => T | undefined
Expand All @@ -12,6 +31,7 @@ interface UiSettingsHelperOptions {
export class UiSettingsHelper {
private readonly getSetting: GetSetting
private readonly setSetting: SetSetting
private systemFontsCache: string[] | null = null

constructor(options: UiSettingsHelperOptions) {
this.getSetting = options.getSetting
Expand Down Expand Up @@ -66,4 +86,92 @@ export class UiSettingsHelper {
this.setSetting('notificationsEnabled', enabled)
eventBus.send(CONFIG_EVENTS.NOTIFICATIONS_CHANGED, SendTarget.ALL_WINDOWS, Boolean(enabled))
}

getFontFamily(): string {
return this.normalizeStoredFont(this.getSetting<string>('fontFamily'))
}

setFontFamily(fontFamily?: string | null): void {
const normalized = this.normalizeStoredFont(fontFamily)
this.setSetting('fontFamily', normalized)
eventBus.send(CONFIG_EVENTS.FONT_FAMILY_CHANGED, SendTarget.ALL_WINDOWS, normalized)
}

getCodeFontFamily(): string {
return this.normalizeStoredFont(this.getSetting<string>('codeFontFamily'))
}

setCodeFontFamily(fontFamily?: string | null): void {
const normalized = this.normalizeStoredFont(fontFamily)
this.setSetting('codeFontFamily', normalized)
eventBus.send(CONFIG_EVENTS.CODE_FONT_FAMILY_CHANGED, SendTarget.ALL_WINDOWS, normalized)
}

resetFontSettings(): void {
this.setFontFamily('')
this.setCodeFontFamily('')
}

async getSystemFonts(): Promise<string[]> {
if (this.systemFontsCache) {
return this.systemFontsCache
}

const fonts = await this.loadSystemFonts()
this.systemFontsCache = fonts
return fonts
}

private normalizeStoredFont(value?: string | null): string {
if (typeof value !== 'string') return ''
const cleaned = value
.replace(/[\r\n\t]/g, ' ')
.replace(/[;:{}()[\]<>]/g, '')
.replace(/['"`\\]/g, '')
.trim()
if (!cleaned) return ''

const collapsed = cleaned.replace(/\s+/g, ' ').slice(0, 100)

// If we already have detected system fonts cached, prefer an exact match from that list
if (this.systemFontsCache?.length) {
const match = this.systemFontsCache.find(
(font) => font.toLowerCase() === collapsed.toLowerCase()
)
if (match) return match
}

return collapsed
}

private async loadSystemFonts(): Promise<string[]> {
try {
const detected = await fontList.getFonts()
const normalized = detected
.map((font) => this.normalizeFontName(font))
.filter((font): font is string => Boolean(font))
return this.uniqueFonts(normalized)
} catch (error) {
console.warn('Failed to detect system fonts with font-list:', error)
return []
}
}

private uniqueFonts(fonts: string[]): string[] {
const seen = new Set<string>()
const result: string[] = []
fonts.forEach((font) => {
const name = font.trim()
if (!name) return
const key = name.toLowerCase()
if (seen.has(key)) return
seen.add(key)
result.push(name)
})
return result
}

private normalizeFontName(name: string): string {
return normalizeFontNameValue(name)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Ensure ACP-related processes/PTYs are terminated during shutdown
*/

import { LifecycleHook, LifecycleContext } from '@shared/presenter'
import { LifecyclePhase } from '@shared/lifecycle'
import { presenter } from '@/presenter'
import { killTerminal } from '../../../configPresenter/acpInitHelper'

export const acpCleanupHook: LifecycleHook = {
name: 'acp-cleanup',
phase: LifecyclePhase.BEFORE_QUIT,
priority: 6,
critical: false,
execute: async (_context: LifecycleContext) => {
console.log('[Lifecycle][ACP] acpCleanupHook: shutting down ACP resources')

try {
killTerminal()
} catch (error) {
console.warn('[Lifecycle][ACP] acpCleanupHook: failed to kill ACP init terminal:', error)
}

try {
const llmPresenter = presenter?.llmproviderPresenter
// Avoid instantiating ACP provider during shutdown; only clean up if already created
const acpProvider = llmPresenter?.getExistingProviderInstance?.('acp') as
| { cleanup?: () => Promise<void> }
| undefined
if (acpProvider?.cleanup) {
await acpProvider.cleanup()
}
} catch (error) {
console.warn('[Lifecycle][ACP] acpCleanupHook: failed to cleanup ACP provider:', error)
}
}
}
1 change: 1 addition & 0 deletions src/main/presenter/lifecyclePresenter/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export { floatingDestroyHook } from './beforeQuit/floatingDestroyHook'
export { presenterDestroyHook } from './beforeQuit/presenterDestroyHook'
export { builtinKnowledgeDestroyHook } from './beforeQuit/builtinKnowledgeDestroyHook'
export { windowQuittingHook } from './beforeQuit/windowQuittingHook'
export { acpCleanupHook } from './beforeQuit/acpCleanupHook'
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,13 @@ export class AcpContentMapper {
payload.events.push(createStreamEvent.text(text))
payload.blocks.push(this.createBlock('content', text))
} else {
const timestamp = now()
payload.events.push(createStreamEvent.reasoning(text))
payload.blocks.push(this.createBlock('reasoning_content', text))
payload.blocks.push(
this.createBlock('reasoning_content', text, {
reasoning_time: { start: timestamp, end: timestamp }
})
)
}
}

Expand Down
Loading