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 @@ -35,7 +35,8 @@ export const CONFIG_EVENTS = {
OAUTH_LOGIN_ERROR: 'config:oauth-login-error', // OAuth登录失败
THEME_CHANGED: 'config:theme-changed', // 主题变更事件
FONT_SIZE_CHANGED: 'config:font-size-changed', // 字体大小变更事件
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed' // 默认系统提示词变更事件
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed', // Default system prompt changed event
CUSTOM_PROMPTS_CHANGED: 'config:custom-prompts-changed' // 自定义提示词变更事件
}

// Provider DB(聚合 JSON)相关事件
Expand Down
8 changes: 4 additions & 4 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1302,14 +1302,17 @@ export class ConfigPresenter implements IConfigPresenter {
await this.customPromptsStore.set('prompts', prompts)
this.clearCustomPromptsCache()
console.log(`[Config] Custom prompts cache updated: ${prompts.length} prompts`)
// Notify all windows about custom prompts change
eventBus.sendToRenderer(CONFIG_EVENTS.CUSTOM_PROMPTS_CHANGED, SendTarget.ALL_WINDOWS, {
count: prompts.length
})
}

// 添加单个 prompt (optimized with cache)
async addCustomPrompt(prompt: Prompt): Promise<void> {
const prompts = await this.getCustomPrompts()
const updatedPrompts = [...prompts, prompt] // Create new array
await this.setCustomPrompts(updatedPrompts)
this.clearCustomPromptsCache()
console.log(`[Config] Added custom prompt: ${prompt.name}`)
}

Expand All @@ -1321,8 +1324,6 @@ export class ConfigPresenter implements IConfigPresenter {
const updatedPrompts = [...prompts] // Create new array
updatedPrompts[index] = { ...updatedPrompts[index], ...updates }
await this.setCustomPrompts(updatedPrompts)
// remove cache
this.clearCustomPromptsCache()
console.log(`[Config] Updated custom prompt: ${promptId}`)
} else {
console.warn(`[Config] Custom prompt not found for update: ${promptId}`)
Expand All @@ -1341,7 +1342,6 @@ export class ConfigPresenter implements IConfigPresenter {
}

await this.setCustomPrompts(filteredPrompts)
this.clearCustomPromptsCache()
console.log(`[Config] Deleted custom prompt: ${promptId}`)
}

Expand Down
20 changes: 17 additions & 3 deletions src/main/presenter/windowPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1276,10 +1276,19 @@ export class WindowPresenter implements IWindowPresenter {
// Choose icon based on platform
const iconFile = nativeImage.createFromPath(process.platform === 'win32' ? iconWin : icon)

// Create Settings Window with fixed size (no state persistence)
// Initialize window state manager to remember position and size
const settingsWindowState = windowStateManager({
file: 'settings-window-state.json',
defaultWidth: 900,
defaultHeight: 600
})

// Create Settings Window with state persistence
const settingsWindow = new BrowserWindow({
width: 900,
height: 600,
x: settingsWindowState.x,
y: settingsWindowState.y,
width: settingsWindowState.width,
height: settingsWindowState.height,
show: false,
autoHideMenuBar: true,
fullscreenable: false,
Expand Down Expand Up @@ -1312,6 +1321,9 @@ export class WindowPresenter implements IWindowPresenter {
this.settingsWindow = settingsWindow
const windowId = settingsWindow.id

// Manage window state to track position and size changes
settingsWindowState.manage(settingsWindow)

// Ensure links with target="_blank" open in the user's default browser
settingsWindow.webContents.setWindowOpenHandler(({ url }) => {
try {
Expand Down Expand Up @@ -1345,6 +1357,8 @@ export class WindowPresenter implements IWindowPresenter {

settingsWindow.on('closed', () => {
console.log(`Settings window ${windowId} closed.`)
// Unmanage window state when window is closed
settingsWindowState.unmanage()
this.settingsWindow = null
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const getContent = (prompt: PromptItem) => prompt.content ?? ''
const loadPrompts = async () => {
await promptsStore.loadPrompts()
prompts.value = promptsStore.prompts.map((prompt) => ({ ...prompt }))
// Note: Main window will be notified via CONFIG_EVENTS.CUSTOM_PROMPTS_CHANGED event
}

const isExpanded = (id: string) => expandedPrompts.value.has(id)
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/components/ChatView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ChatInput
ref="chatInput"
variant="chat"
:context-length="chatStore.chatConfig.contextLength"
:disabled="!chatStore.getActiveThreadId() || isGenerating"
@send="handleSend"
@file-upload="handleFileUpload"
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/components/chat-input/ChatInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@

<script setup lang="ts">
// === Vue Core ===
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, onUnmounted, ref, toRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'

// === Types ===
Expand Down Expand Up @@ -435,15 +435,15 @@ editor.on('update', editorComposable.onEditorUpdate)
const contextLengthTracker = useContextLength({
inputText: editorComposable.inputText,
selectedFiles: files.selectedFiles,
contextLength: props.contextLength
contextLength: toRef(props, 'contextLength')
})

// Initialize send button state
const sendButtonState = useSendButtonState({
variant: props.variant,
inputText: editorComposable.inputText,
currentContextLength: contextLengthTracker.currentContextLength,
contextLength: props.contextLength
contextLength: toRef(props, 'contextLength')
})

// Only initialize config for chat variant
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// === Vue Core ===
import { computed, Ref } from 'vue'
import { computed, Ref, unref } from 'vue'

// === Types ===
import type { MessageFile } from '@shared/chat'
import type { MaybeRef } from 'vue'

// === Utils ===
import { approximateTokenSize } from 'tokenx'

interface ContextLengthOptions {
inputText: Ref<string>
selectedFiles: Ref<MessageFile[]>
contextLength?: number
contextLength?: MaybeRef<number | undefined>
}

/**
Expand All @@ -30,15 +31,17 @@ export function useContextLength(options: ContextLengthOptions) {
})

const currentContextLengthPercentage = computed(() => {
return currentContextLength.value / (contextLength ?? 1000)
const length = unref(contextLength)
return currentContextLength.value / (length ?? 1000)
})

const currentContextLengthText = computed(() => {
return `${Math.round(currentContextLengthPercentage.value * 100)}%`
})

const shouldShowContextLength = computed(() => {
return contextLength && contextLength > 0 && currentContextLengthPercentage.value > 0.5
const length = unref(contextLength)
return length && length > 0 && currentContextLengthPercentage.value > 0.5
})

const contextLengthStatusClass = computed(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// === Vue Core ===
import { computed, Ref } from 'vue'
import { computed, Ref, unref } from 'vue'
import type { MaybeRef } from 'vue'

// === Stores ===
import { useChatStore } from '@/stores/chat'
Expand All @@ -8,22 +9,23 @@ interface SendButtonStateOptions {
variant: 'chat' | 'newThread'
inputText: Ref<string>
currentContextLength: Ref<number>
contextLength?: number
contextLength?: MaybeRef<number | undefined>
}

/**
* Manages send button disabled state and streaming status
*/
export function useSendButtonState(options: SendButtonStateOptions) {
const { variant, inputText, currentContextLength, contextLength } = options

// === Stores ===
const chatStore = useChatStore()

// === Computed ===
const disabledSend = computed(() => {
const length = unref(contextLength)

if (variant === 'newThread') {
return inputText.value.length <= 0 || currentContextLength.value > (contextLength ?? 200000)
return inputText.value.length <= 0 || currentContextLength.value > (length ?? 200000)
}

// chat variant
Expand All @@ -32,7 +34,7 @@ export function useSendButtonState(options: SendButtonStateOptions) {
return (
chatStore.generatingThreadIds.has(activeThreadId) ||
inputText.value.length <= 0 ||
currentContextLength.value > (contextLength ?? chatStore.chatConfig.contextLength)
currentContextLength.value > (length ?? chatStore.chatConfig.contextLength)
)
}
return false
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 @@ -25,7 +25,8 @@ export const CONFIG_EVENTS = {
COPY_WITH_COT_CHANGED: 'config:copy-with-cot-enabled-changed',
THEME_CHANGED: 'config:theme-changed',
FONT_SIZE_CHANGED: 'config:font-size-changed',
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed'
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed',
CUSTOM_PROMPTS_CHANGED: 'config:custom-prompts-changed'
}

// 会话相关事件
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/en-US/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "Tool call error: {error}",
"mcpDisabled": "MCP is disabled",
"getPromptFailed": "Failed to get prompt",
"readResourceFailed": "Failed to read resource"
"readResourceFailed": "Failed to read resource",
"promptNotFound": "Prompt '{name}' not found",
"emptyPromptContent": "Prompt '{name}' has no content",
"missingParameters": "Missing required parameters: {params}",
"invalidParameters": "Invalid parameters for: {params}"
},
"market": {
"browseBuiltin": "Browse Built-in MCP Market",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/fa-IR/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "خطای فراخوانی ابزار: {error}",
"mcpDisabled": "MCP خاموش است",
"getPromptFailed": "دریافت دستورکار ناموفق بود",
"readResourceFailed": "خواندن منبع ناموفق بود"
"readResourceFailed": "خواندن منبع ناموفق بود",
"promptNotFound": "دستورکار '{name}' یافت نشد",
"emptyPromptContent": "درخواست '{name}' خالی است",
"missingParameters": "پارامترهای از دست رفته: {params}",
"invalidParameters": "پارامترهای نامعتبر: {params}"
},
"market": {
"apiKeyPlaceholder": "کلید API MCPROUTER را وارد کنید",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/fr-FR/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "Erreur d'appel d'outil : {error}",
"mcpDisabled": "MCP est désactivé",
"getPromptFailed": "Échec de l'obtention du prompt",
"readResourceFailed": "Échec de la lecture de la ressource"
"readResourceFailed": "Échec de la lecture de la ressource",
"promptNotFound": "Prompt '{name}' introuvable",
"emptyPromptContent": "L'invite '{name}' est vide",
"missingParameters": "Paramètres manquants : {params}",
"invalidParameters": "Paramètres non valides : {params}"
},
"market": {
"apiKeyPlaceholder": "Entrez la clé de l'API McProuter",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/ja-JP/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "ツール呼び出しエラー: {error}",
"mcpDisabled": "MCPは無効になっています",
"getPromptFailed": "プロンプトの取得に失敗しました",
"readResourceFailed": "リソースの読み込みに失敗しました"
"readResourceFailed": "リソースの読み込みに失敗しました",
"promptNotFound": "プロンプト '{name}' が見つかりませんでした",
"emptyPromptContent": "'{name}' プロンプトが空です",
"missingParameters": "パラメータがありません: {params}",
"invalidParameters": "無効なパラメータ: {params}"
},
"market": {
"browseBuiltin": "内蔵MCPマーケットを閲覧",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/ko-KR/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "도구 호출 오류: {error}",
"mcpDisabled": "MCP가 비활성화되어 있습니다",
"getPromptFailed": "프롬프트 가져오기 실패",
"readResourceFailed": "리소스 읽기 실패"
"readResourceFailed": "리소스 읽기 실패",
"promptNotFound": "프롬프트 '{name}'을(를) 찾을 수 없습니다",
"emptyPromptContent": "'{name}' 프롬프트가 비어 있습니다.",
"missingParameters": "누락된 매개변수: {params}",
"invalidParameters": "잘못된 매개변수: {params}"
},
"market": {
"apiKeyPlaceholder": "McProuter API 키를 입력하십시오",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/pt-BR/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "Erro na chamada da ferramenta: {error}",
"mcpDisabled": "MCP está desativado",
"getPromptFailed": "Falha ao obter prompt",
"readResourceFailed": "Falha ao ler recurso"
"readResourceFailed": "Falha ao ler recurso",
"promptNotFound": "Prompt '{name}' não encontrado",
"emptyPromptContent": "O prompt '{name}' está vazio",
"missingParameters": "Parâmetros ausentes: {params}",
"invalidParameters": "Parâmetros inválidos: {params}"
},
"market": {
"browseBuiltin": "Navegar no Mercado MCP Integrado",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/ru-RU/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "Ошибка вызова инструмента: {error}",
"mcpDisabled": "MCP отключен",
"getPromptFailed": "Не удалось получить промпт",
"readResourceFailed": "Не удалось прочитать ресурс"
"readResourceFailed": "Не удалось прочитать ресурс",
"promptNotFound": "Промпт '{name}' не найден",
"emptyPromptContent": "Приглашение '{name}' пусто.",
"missingParameters": "Отсутствующие параметры: {params}",
"invalidParameters": "Неверные параметры: {params}"
},
"market": {
"apiKeyPlaceholder": "Введите ключ McProuter API",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/zh-CN/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"toolCallError": "工具调用错误: {error}",
"mcpDisabled": "MCP功能已禁用",
"getPromptFailed": "获取提示模板失败",
"readResourceFailed": "读取资源失败"
"readResourceFailed": "读取资源失败",
"promptNotFound": "未找到提示模板 '{name}'",
"emptyPromptContent": "'{name}' Prompt 是空的",
"missingParameters": "缺少参数: {params}",
"invalidParameters": "无效参数: {params}"
},
"tabs": {
"servers": "服务器",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/zh-HK/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@
"toolCallError": "工具呼叫錯誤: {error}",
"mcpDisabled": "MCP功能已禁用",
"getPromptFailed": "獲取提示模板失敗",
"readResourceFailed": "讀取資源失敗"
"readResourceFailed": "讀取資源失敗",
"promptNotFound": "未找到提示模板 '{name}'",
"emptyPromptContent": "'{name}' Prompt 是空的",
"missingParameters": "缺少參數: {params}",
"invalidParameters": "無效參數: {params}"
},
"market": {
"apiKeyPlaceholder": "輸入 MCPRouter API Key",
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/i18n/zh-TW/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"toolCallError": "工具呼叫錯誤: {error}",
"mcpDisabled": "MCP功能已禁用",
"getPromptFailed": "獲取提示模板失敗",
"readResourceFailed": "讀取資源失敗"
"readResourceFailed": "讀取資源失敗",
"promptNotFound": "未找到提示模板 '{name}'",
"emptyPromptContent": "'{name}' Prompt 是空的",
"missingParameters": "缺少參數: {params}",
"invalidParameters": "無效參數: {params}"
},
"tabs": {
"servers": "伺服器",
Expand Down
Loading