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
61 changes: 61 additions & 0 deletions src/main/presenter/deeplinkPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ export class DeeplinkPresenter implements IDeeplinkPresenter {
presenter.windowPresenter.show()
}

const windowId = focusedWindow?.id || 1
await this.ensureChatTabActive(windowId)
eventBus.sendToRenderer(DEEPLINK_EVENTS.START, SendTarget.DEFAULT_TAB, {
msg,
modelId,
Expand All @@ -217,6 +219,65 @@ export class DeeplinkPresenter implements IDeeplinkPresenter {
})
}

/**
* 确保有一个活动的 chat 标签页
* @param windowId 窗口ID
*/
private async ensureChatTabActive(windowId: number): Promise<void> {
try {
const tabPresenter = presenter.tabPresenter
const tabsData = await tabPresenter.getWindowTabsData(windowId)
const chatTab = tabsData.find(
(tab) =>
tab.url === 'local://chat' || tab.url.includes('#/chat') || tab.url.endsWith('/chat')
)
if (chatTab) {
if (!chatTab.isActive) {
await tabPresenter.switchTab(chatTab.id)
await new Promise((resolve) => setTimeout(resolve, 100))
}
} else {
const newTabId = await tabPresenter.createTab(windowId, 'local://chat', { active: true })
if (newTabId) {
console.log(`[Deeplink] Waiting for tab ${newTabId} renderer to be ready`)
await this.waitForTabReady(newTabId)
}
}
} catch (error) {
console.error('Error ensuring chat tab active:', error)
}
}

/**
* 等待标签页渲染进程准备就绪
* @param tabId 标签页ID
*/
private async waitForTabReady(tabId: number): Promise<void> {
return new Promise((resolve) => {
let resolved = false
const onTabReady = (readyTabId: number) => {
if (readyTabId === tabId && !resolved) {
resolved = true
console.log(`[Deeplink] Tab ${tabId} renderer is ready`)
eventBus.off('tab:renderer-ready', onTabReady)
clearTimeout(timeoutId)
resolve()
}
}

eventBus.on('tab:renderer-ready', onTabReady)

const timeoutId = setTimeout(() => {
if (!resolved) {
resolved = true
eventBus.off('tab:renderer-ready', onTabReady)
console.log(`[Deeplink] Timeout waiting for tab ${tabId}, proceeding anyway`)
resolve()
}
}, 3000)
})
}

async handleMcpInstall(params: URLSearchParams): Promise<void> {
console.log('Processing mcp/install command, parameters:', Object.fromEntries(params.entries()))

Expand Down
42 changes: 31 additions & 11 deletions src/renderer/src/components/NewThread.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ import ModelSelect from './ModelSelect.vue'
import { useChatStore } from '@/stores/chat'
import { MODEL_META } from '@shared/presenter'
import { useSettingsStore } from '@/stores/settings'
import { computed, ref, watch, onMounted } from 'vue'
import { computed, nextTick, ref, watch, onMounted } from 'vue'
import { UserMessageContent } from '@shared/chat'
import ChatConfig from './ChatConfig.vue'
import { usePresenter } from '@/composables/usePresenter'
Expand Down Expand Up @@ -302,17 +302,37 @@ watch(
handleModelUpdate(matchedModel.model, matchedModel.providerId)
}
}
if (chatInputRef.value && (newCache.msg || newCache.mentions)) {
const chatInput = chatInputRef.value // 将引用存储在局部变量中
chatInput.clearContent()
if (newCache.mentions) {
// 优先处理 mentions
newCache.mentions.forEach((mention) => {
chatInput.appendMention(mention)
})
if (newCache.msg || newCache.mentions) {
const setInputContent = () => {
if (chatInputRef.value) {
console.log('[NewThread] Setting input content, msg:', newCache.msg)
const chatInput = chatInputRef.value
chatInput.clearContent()
if (newCache.mentions) {
newCache.mentions.forEach((mention) => {
chatInput.appendMention(mention)
})
}
if (newCache.msg) {
console.log('[NewThread] Appending text:', newCache.msg)
chatInput.appendText(newCache.msg)
}
return true
}
return false
}
if (newCache.msg) {
chatInput.appendText(newCache.msg)

if (!setInputContent()) {
console.log('[NewThread] ChatInput ref not ready, retrying...')
nextTick(() => {
if (!setInputContent()) {
setTimeout(() => {
if (!setInputContent()) {
console.warn('[NewThread] Failed to set input content after retries')
}
}, 100)
}
})
}
}
if (newCache.systemPrompt) {
Expand Down
11 changes: 6 additions & 5 deletions src/renderer/src/stores/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -963,15 +963,15 @@ export const useChatStore = defineStore('chat', () => {
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 注册消息编辑事件处理
// 注册 deeplink 事件处理
window.electron.ipcRenderer.on(DEEPLINK_EVENTS.START, async (_, data) => {
console.log('DEEPLINK_EVENTS.START', data)
// 检查当前路由,如果不在新会话页面,则跳转
console.log(`[Renderer] Tab ${getTabId()} received DEEPLINK_EVENTS.START:`, data)
// 确保路由正确
const currentRoute = router.currentRoute.value
if (currentRoute.name !== 'chat') {
await router.push({ name: 'chat' })
}
// 检查是否存在 activeThreadId,如果存在则创建新会话
// 如果存在活动会话,创建新会话
if (getActiveThreadId()) {
await clearActiveThread()
}
Expand Down Expand Up @@ -1102,12 +1102,13 @@ export const useChatStore = defineStore('chat', () => {
}

onMounted(() => {
console.log('Chat store is mounted. Setting up event listeners.')
console.log(`[Chat Store] Tab ${getTabId()} is mounted. Setting up event listeners.`)

// store现在是被动的,等待主进程推送数据
setupEventListeners()

// 在 store 初始化完成后,通过usePresenter发送就绪信号
console.log(`[Chat Store] Tab ${getTabId()} sending ready signal`)
tabP.onRendererTabReady(getTabId())
})

Expand Down