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: 2 additions & 2 deletions README.jp.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ DeepChatはアクティブなオープンソースコミュニティプロジェ

deepchatへの貢献をご検討いただきありがとうございます!貢献ガイドは[貢献ガイドライン](./CONTRIBUTING.md)でご確認いただけます。

<a href="https://github.com/ThinkInAIXYZ/deepchat/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ThinkInAIXYZ/deepchat" alt="DeepChatプロジェクト貢献者" />
<a href="https://openomy.com/thinkinaixyz/deepchat" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.com/svg?repo=thinkinaixyz/deepchat&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>

## 🙏🏻 謝辞
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,8 @@ Check the [Contribution Guidelines](./CONTRIBUTING.md) to learn more about ways

Thank you for considering contributing to deepchat! The contribution guide can be found in the [Contribution Guidelines](./CONTRIBUTING.md).

<a href="https://github.com/ThinkInAIXYZ/deepchat/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ThinkInAIXYZ/deepchat" alt="DeepChat project contributors" />
<a href="https://openomy.com/thinkinaixyz/deepchat" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.com/svg?repo=thinkinaixyz/deepchat&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
Comment on lines +326 to 328
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

Verify consistency across localized READMEs and avoid fragile attributes.

Ensure the same fixes (no inline styles, no target on <img>, rel added on <a>) are applied in README.zh.md and README.jp.md and in any other leaderboard block.

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List localized READMEs
fd -a '^README.*\.md$'

# Find leaderboard usages
rg -n 'openomy\.com' README*.md

# Flag invalid target on <img>
rg -nP '<img[^>]*\btarget="_blank"' README*.md || true

# Flag inline styles
rg -n 'style=' README*.md || true

# Anchors with target but missing rel
rg -nP '<a[^>]*\btarget="_blank"(?![^>]*\brel=)' README*.md || true

Length of output: 3793


Fix leaderboard blocks in all localized READMEs

  • Remove inline style attributes
  • Drop target="_blank" from the <img> tag
  • Add rel="noopener noreferrer" to the <a> tag

Applies to the leaderboard snippet in:

  • README.md (lines 326–328)
  • README.zh.md (320–322)
  • README.jp.md (320–322)
🤖 Prompt for AI Agents
In README.md (lines 326–328) and the corresponding leaderboard snippets in
README.zh.md (lines 320–322) and README.jp.md (lines 320–322), remove inline
style attributes from both the <a> and <img> tags, remove target="_blank" from
the <img> tag, and add rel="noopener noreferrer" to the <a> tag; ensure the <a>
retains target="_blank" only if needed (prefer not to have it on the image), or
remove target entirely from the image and leave the link without inline styling
while adding the rel attribute to the anchor.


## 🙏🏻 Thanks
Expand Down
4 changes: 2 additions & 2 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ DeepChat是一个活跃的开源社区项目,我们欢迎各种形式的贡献

感谢您考虑为deepchat做出贡献!贡献指南可以在[贡献指南](./CONTRIBUTING.md)中找到。

<a href="https://github.com/ThinkInAIXYZ/deepchat/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ThinkInAIXYZ/deepchat" alt="DeepChat项目贡献者" />
<a href="https://openomy.com/thinkinaixyz/deepchat" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.com/svg?repo=thinkinaixyz/deepchat&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>

## 🙏🏻 致谢
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "DeepChat",
"version": "0.3.4",
"version": "0.3.5",
"description": "DeepChat,一个简单易用的AI客户端",
"main": "./out/main/index.js",
"author": "ThinkInAIXYZ",
Expand Down
12 changes: 12 additions & 0 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ interface IAppSettings {
floatingButtonEnabled?: boolean // 悬浮按钮是否启用
default_system_prompt?: string // 默认系统提示词
webContentLengthLimit?: number // 网页内容截断长度限制,默认3000字符
updateChannel?: string // 更新渠道:'stable' | 'canary'
[key: string]: unknown // 允许任意键,使用unknown类型替代any
}

Expand Down Expand Up @@ -122,6 +123,7 @@ export class ConfigPresenter implements IConfigPresenter {
floatingButtonEnabled: false,
default_system_prompt: '',
webContentLengthLimit: 3000,
updateChannel: 'stable', // 默认使用正式版
appVersion: this.currentAppVersion
}
})
Expand Down Expand Up @@ -1192,6 +1194,16 @@ export class ConfigPresenter implements IConfigPresenter {
this.setSetting('default_system_prompt', '')
}

// 获取更新渠道
getUpdateChannel(): string {
return this.getSetting<string>('updateChannel') || 'stable'
}

// 设置更新渠道
setUpdateChannel(channel: string): void {
this.setSetting('updateChannel', channel)
}
Comment on lines +1197 to +1205
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

Validate and broadcast update-channel changes; return a typed value

  • Return a literal UpdateChannel, sanitize unexpected store values.
  • Validate/clamp input at runtime.
  • Broadcast a dedicated event to all windows so other UIs/processes react immediately.
  • Log a structured INFO record.
-  // 获取更新渠道
-  getUpdateChannel(): string {
-    return this.getSetting<string>('updateChannel') || 'stable'
-  }
+  // Get update channel
+  getUpdateChannel(): UpdateChannel {
+    const val = this.getSetting<UpdateChannel>('updateChannel')
+    return val === 'canary' ? 'canary' : 'stable'
+  }
 
-  // 设置更新渠道
-  setUpdateChannel(channel: string): void {
-    this.setSetting('updateChannel', channel)
-  }
+  // Set update channel
+  setUpdateChannel(channel: UpdateChannel): void {
+    const prev = this.getUpdateChannel()
+    const next: UpdateChannel = channel === 'canary' ? 'canary' : 'stable'
+    if (prev === next) return
+    this.setSetting('updateChannel', next)
+    // Notify all windows so UI/logic can react
+    eventBus.send(CONFIG_EVENTS.UPDATE_CHANNEL_CHANGED, SendTarget.ALL_WINDOWS, next)
+    console.info(
+      '[Config]',
+      JSON.stringify({
+        level: 'INFO',
+        event: 'UPDATE_CHANNEL_CHANGED',
+        prev,
+        next,
+        at: new Date().toISOString()
+      })
+    )
+  }

Follow-ups:

  • Add UPDATE_CHANNEL_CHANGED to CONFIG_EVENTS in @/events.
  • Update IConfigPresenter in src/shared/presenter.d.ts to use UpdateChannel for both methods.

You can verify repo-wide typing and usages with:

I can open a small PR to add the event constant and d.ts updates if you want.


🏁 Script executed:

#!/bin/bash
# Verify typing and usages for update-channel API
rg -n -C2 --type=ts "type\s+UpdateChannel\s*=\s*'stable'\s*\|\s*'canary'"
rg -n -C2 --type=ts "getUpdateChannel\s*\("
rg -n -C2 --type=ts "setUpdateChannel\s*\("
rg -n -C2 --type=ts "'UPDATE_CHANNEL_CHANGED'|UPDATE_CHANNEL_CHANGED"

Length of output: 2087


I’m running additional checks to confirm the presence of the UpdateChannel type alias, the UPDATE_CHANNEL_CHANGED constant, and necessary imports for eventBus/CONFIG_EVENTS.


🏁 Script executed:

#!/bin/bash
# Search for UpdateChannel type alias
rg -n -C2 --type=ts "type\s+UpdateChannel"
# Search for any UpdateChannel mentions
rg -n -C2 --type=ts "UpdateChannel"
# Search for eventBus usage
rg -n -C2 --type=ts "eventBus"
# Search for CONFIG_EVENTS usage
rg -n -C2 --type=ts "CONFIG_EVENTS"
# Search for SendTarget usage
rg -n -C2 --type=ts "SendTarget"
# Search for eventBus import statements
rg -n -C2 --type=ts "import.*eventBus"

Length of output: 234042


Define UpdateChannel type and broadcast changes

  • In src/shared/presenter.d.ts, add
    export type UpdateChannel = 'stable' | 'canary'
    interface IConfigPresenter {
      getUpdateChannel(): UpdateChannel
      setUpdateChannel(channel: UpdateChannel): void
      // …
    }
  • In src/main/events.ts (and mirror in src/renderer/src/events.ts), extend CONFIG_EVENTS with
    UPDATE_CHANNEL_CHANGED: 'config:update-channel-changed'
  • In src/main/presenter/configPresenter/index.ts, refactor:
    getUpdateChannel(): UpdateChannel — read store value, return 'canary' or fallback 'stable'.
    setUpdateChannel(channel: UpdateChannel): void — clamp input, no-op if unchanged, persist via this.setSetting(), call
    eventBus.send(CONFIG_EVENTS.UPDATE_CHANNEL_CHANGED, SendTarget.ALL_WINDOWS, next)
    console.info('[Config]', JSON.stringify({ level: 'INFO', event: 'UPDATE_CHANNEL_CHANGED', prev, next, at: new Date().toISOString() }))
🤖 Prompt for AI Agents
In src/main/presenter/configPresenter/index.ts around lines 1197-1205, implement
the reviewer changes: use the new UpdateChannel type ('stable'|'canary') from
src/shared/presenter.d.ts for getUpdateChannel and setUpdateChannel; update
getUpdateChannel to read the stored value and return 'canary' if set to canary
otherwise fallback to 'stable'; update setUpdateChannel to clamp the input to
the allowed values, return early if the value is unchanged, persist via
this.setSetting(...), then broadcast the change using
eventBus.send(CONFIG_EVENTS.UPDATE_CHANNEL_CHANGED, SendTarget.ALL_WINDOWS,
next) and log the change with console.info containing prev, next, event name and
ISO timestamp. Also ensure CONFIG_EVENTS.UPDATE_CHANNEL_CHANGED is declared in
src/main/events.ts (and mirrored in src/renderer/src/events.ts) and add the
UpdateChannel type and IConfigPresenter signatures to src/shared/presenter.d.ts.


// 获取默认快捷键
getDefaultShortcutKey(): ShortcutKeySetting {
return {
Expand Down
2 changes: 1 addition & 1 deletion src/main/presenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class Presenter implements IPresenter {
this.configPresenter
)
this.mcpPresenter = new McpPresenter(this.configPresenter)
this.upgradePresenter = new UpgradePresenter()
this.upgradePresenter = new UpgradePresenter(this.configPresenter)
this.shortcutPresenter = new ShortcutPresenter(this.configPresenter)
this.filePresenter = new FilePresenter()
this.syncPresenter = new SyncPresenter(this.configPresenter, this.sqlitePresenter)
Expand Down
25 changes: 19 additions & 6 deletions src/main/presenter/upgradePresenter/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { app, shell } from 'electron'
import { IUpgradePresenter, UpdateStatus, UpdateProgress } from '@shared/presenter'
import {
IUpgradePresenter,
UpdateStatus,
UpdateProgress,
IConfigPresenter
} from '@shared/presenter'
import { eventBus, SendTarget } from '@/eventbus'
import { UPDATE_EVENTS, WINDOW_EVENTS } from '@/events'
import electronUpdater from 'electron-updater'
Expand Down Expand Up @@ -38,7 +43,7 @@ const getPlatformInfo = () => {

// 获取版本检查的基础URL
const getVersionCheckBaseUrl = () => {
return 'https://cdn.deepchatai.cn/upgrade'
return 'https://cdn.deepchatai.cn'
}

// 获取自动更新状态文件路径
Expand All @@ -56,8 +61,10 @@ export class UpgradePresenter implements IUpgradePresenter {
private _lastCheckTime: number = 0 // 上次检查更新的时间戳
private _updateMarkerPath: string
private _previousUpdateFailed: boolean = false // 标记上次更新是否失败
private _configPresenter: IConfigPresenter // 配置presenter

constructor() {
constructor(configPresenter: IConfigPresenter) {
this._configPresenter = configPresenter
this._baseUrl = getVersionCheckBaseUrl()
this._updateMarkerPath = getUpdateMarkerFilePath()

Expand Down Expand Up @@ -248,10 +255,13 @@ export class UpgradePresenter implements IUpgradePresenter {

// 首先获取版本信息文件
const platformString = getPlatformInfo()
const rawChannel = this._configPresenter.getUpdateChannel()
const updateChannel = rawChannel === 'canary' ? 'canary' : 'upgrade' // Sanitize channel
const randomId = Math.floor(Date.now() / 3600000) // Timestamp truncated to hour
const versionUrl = `${this._baseUrl}/${platformString}.json?noCache=${randomId}`
const versionPath = updateChannel
const versionUrl = `${this._baseUrl}/${versionPath}/${platformString}.json?noCache=${randomId}`
console.log('versionUrl', versionUrl)
const response = await axios.get<VersionInfo>(versionUrl)
const response = await axios.get<VersionInfo>(versionUrl, { timeout: 60000 }) // Add network timeout
const remoteVersion = response.data
const currentVersion = app.getVersion()

Expand Down Expand Up @@ -288,7 +298,10 @@ export class UpgradePresenter implements IUpgradePresenter {
}

// 设置自动更新的URL
const autoUpdateUrl = `${this._baseUrl}/v${remoteVersion.version}/${platformString}`
const autoUpdateUrl =
updateChannel === 'canary'
? `${this._baseUrl}/canary/${platformString}`
: `${this._baseUrl}/upgrade/v${remoteVersion.version}/${platformString}`
console.log('设置自动更新URL:', autoUpdateUrl)
autoUpdater.setFeedURL(autoUpdateUrl)

Expand Down
40 changes: 40 additions & 0 deletions src/renderer/src/components/settings/AboutUsSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@
</div>
</div>

<!-- 更新渠道选择 -->
<div class="flex items-center gap-4 mt-4">
<label class="text-sm font-medium">{{ t('about.updateChannel') }}:</label>
<div class="min-w-32 max-w-48">
<Select v-model="updateChannel" @update:model-value="setUpdateChannel">
<SelectTrigger>
<SelectValue :placeholder="t('about.updateChannel')" />
</SelectTrigger>
<SelectContent>
<SelectItem value="stable">
{{ t('about.stableChannel') }}
</SelectItem>
<SelectItem value="canary">
{{ t('about.canaryChannel') }}
</SelectItem>
</SelectContent>
</Select>
</div>
</div>

<!-- 操作按钮区域 -->
<div class="flex gap-2 mt-2">
<!-- 免责声明按钮 -->
Expand Down Expand Up @@ -147,13 +167,21 @@ import {
DialogHeader,
DialogTitle
} from '@/components/ui/dialog'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@/components/ui/select'
import { renderMarkdown, getCommonMarkdown } from 'vue-renderer-markdown'
import { useUpgradeStore } from '@/stores/upgrade'
import { useLanguageStore } from '@/stores/language'

const { t } = useI18n()
const languageStore = useLanguageStore()
const devicePresenter = usePresenter('devicePresenter')
const configPresenter = usePresenter('configPresenter')
const deviceInfo = ref<{
platform: string
arch: string
Expand All @@ -169,6 +197,7 @@ const deviceInfo = ref<{
})
const appVersion = ref('')
const upgrade = useUpgradeStore()
const updateChannel = ref('stable')

// 免责声明对话框状态
const isDisclaimerOpen = ref(false)
Expand All @@ -178,6 +207,16 @@ const openDisclaimerDialog = () => {
isDisclaimerOpen.value = true
}

// 设置更新渠道
const setUpdateChannel = async (channel: string) => {
try {
await configPresenter.setUpdateChannel(channel)
// v-model 会自动更新 updateChannel.value,不需要手动设置
} catch (error) {
console.error('updateChannelSetError:', error)
}
}

// 检查更新
const handleCheckUpdate = async () => {
// 如果已下载完成,直接打开更新对话框
Expand All @@ -198,6 +237,7 @@ const disclaimerContent = computed(() => renderMarkdown(md, t('searchDisclaimer'
onMounted(async () => {
deviceInfo.value = await devicePresenter.getDeviceInfo()
appVersion.value = await devicePresenter.getAppVersion()
updateChannel.value = await configPresenter.getUpdateChannel()
console.log(deviceInfo.value)
})
</script>
5 changes: 4 additions & 1 deletion src/renderer/src/i18n/en-US/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
},
"disclaimerButton": "Disclaimer",
"disclaimerTitle": "Terms of Use Statement",
"checkUpdateButton": "Check for Updates"
"checkUpdateButton": "Check for Updates",
"updateChannel": "Update Channel",
"stableChannel": "Stable",
"canaryChannel": "Canary"
}
5 changes: 4 additions & 1 deletion src/renderer/src/i18n/fa-IR/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
},
"disclaimerButton": "رد مسئولیت",
"disclaimerTitle": "بیانیه شرایط استفاده",
"checkUpdateButton": "بررسی به‌روزرسانی"
"checkUpdateButton": "بررسی به‌روزرسانی",
"updateChannel": "کانال به‌روزرسانی",
"stableChannel": "پایدار",
"canaryChannel": "کاناری"
}
5 changes: 4 additions & 1 deletion src/renderer/src/i18n/fr-FR/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
},
"disclaimerButton": "Avertissement",
"disclaimerTitle": "Conditions d'utilisation",
"checkUpdateButton": "Vérifier les mises à jour"
"checkUpdateButton": "Vérifier les mises à jour",
"updateChannel": "Canal de mise à jour",
"stableChannel": "Stable",
"canaryChannel": "Canary"
}
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/ja-JP/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "免責事項",
"disclaimerTitle": "利用規約",
"checkUpdateButton": "アップデートを確認",
"updateChannel": "アップデートチャンネル",
"stableChannel": "安定版",
"canaryChannel": "テスト版",
"deviceInfo": {
"title": "デバイス情報",
"platform": "プラットフォーム",
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/ko-KR/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "면책 조항",
"disclaimerTitle": "이용 약관 면책 조항",
"checkUpdateButton": "업데이트 확인",
"updateChannel": "업데이트 채널",
"stableChannel": "안정 버전",
"canaryChannel": "테스트 버전",
"deviceInfo": {
"title": "장치 정보",
"platform": "플랫폼",
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/ru-RU/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "Отказ от ответственности",
"disclaimerTitle": "Условия использования",
"checkUpdateButton": "Проверить обновления",
"updateChannel": "Канал обновлений",
"stableChannel": "Стабильный",
"canaryChannel": "Канареечный",
"deviceInfo": {
"title": "Сведения об устройстве",
"platform": "Платформа",
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/zh-CN/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "免责声明",
"disclaimerTitle": "使用条款声明",
"checkUpdateButton": "检查更新",
"updateChannel": "更新渠道",
"stableChannel": "正式版",
"canaryChannel": "内测版",
"deviceInfo": {
"title": "设备信息",
"platform": "平台",
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/zh-HK/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "免責聲明",
"disclaimerTitle": "使用條款聲明",
"checkUpdateButton": "檢查更新",
"updateChannel": "更新頻道",
"stableChannel": "正式版",
"canaryChannel": "測試版",
"deviceInfo": {
"title": "設備信息",
"platform": "平台",
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/i18n/zh-TW/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"disclaimerButton": "免責聲明",
"disclaimerTitle": "使用條款聲明",
"checkUpdateButton": "檢查更新",
"updateChannel": "更新頻道",
"stableChannel": "正式版",
"canaryChannel": "測試版",
"deviceInfo": {
"title": "裝置資訊",
"platform": "平台",
Expand Down
3 changes: 3 additions & 0 deletions src/shared/presenter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@ export interface IConfigPresenter {
// 悬浮按钮设置
getFloatingButtonEnabled(): boolean
setFloatingButtonEnabled(enabled: boolean): void
// 更新渠道设置
getUpdateChannel(): string
setUpdateChannel(channel: string): void
// 日志设置
getLoggingEnabled(): boolean
setLoggingEnabled(enabled: boolean): void
Expand Down