Skip to content

Commit 48da4f0

Browse files
deepagent-aiclaude
andcommitted
feat: 智谱 provider 官方化 + 离散档位缩放 + plan 消息隐藏
provider(官方化与隔离): - 四个智谱/GLM provider 迁移为官方 provider,OFFICIAL_PROVIDER_IDS 单一来源 - 一方运行时设置(wishModel/agentMode/selfLearning/promptMode)与官方 provider 传输设置(headerTimeout/chunkTimeout/timeout/maxRetries)移出 配置文件,落独立 SettingsStore(~/.deepagent/code/settings.json,0600) - config 用 overlay 模式把 settings 回注 provider.deepagent.options,后端读者无需改动 - GLM-5.2 思考模式对齐上游:移除仅 coding-plan 的收窄,zhipuai/zai 全品牌面均启用 - 连接弹窗高级折叠区配置官方 provider 传输设置;wish 选择器改为 provider 小标签 - llm.onError 收敛日志:APICallError 只记关键字段 + 截断 responseBody, 不再 JSON.stringify 整个请求体;maxRetries 支持 provider 覆盖 feat(app,desktop): 桌面端离散档位缩放,50%–250% 每 25% 一档,设置"显示"分区下拉 fix(ui): plan 工具消息在两条渲染路径隐藏 - message-part.tsx 时间线卡片 + session-turn.tsx 执行状态摘要(hidden 集合补 plan) fix(i18n): 修复 br/bs/es/pl/tr 五个语言文件 zoom key 插入错位导致的语法错误 (该错位使 renderer 编译失败、应用无法启动) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent abbfeb1 commit 48da4f0

41 files changed

Lines changed: 861 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"exports": {
77
".": "./src/index.ts",
88
"./desktop-menu": "./src/desktop-menu.ts",
9+
"./zoom-levels": "./src/zoom-levels.ts",
910
"./updater": "./src/updater.ts",
1011
"./wsl/types": "./src/wsl/types.ts",
1112
"./vite": "./vite.js",

packages/app/src/components/dialog-connect-provider.tsx

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ProviderAuthAuthorization, ProviderAuthMethod } from "@deepagent-code/sdk/v2/client"
22
import { Button } from "@deepagent-code/ui/button"
3+
import { Collapsible } from "@deepagent-code/ui/collapsible"
34
import { useDialog } from "@deepagent-code/ui/context/dialog"
45
import { Dialog } from "@deepagent-code/ui/dialog"
56
import { Icon } from "@deepagent-code/ui/icon"
@@ -29,6 +30,9 @@ const providerBaseURL: Record<string, string> = {
2930
deepseek: "https://api.deepseek.com",
3031
anthropic: "https://api.anthropic.com/v1",
3132
zhipuai: "https://open.bigmodel.cn/api/paas/v4",
33+
"zhipuai-coding-plan": "https://open.bigmodel.cn/api/coding/paas/v4",
34+
zai: "https://api.z.ai/api/paas/v4",
35+
"zai-coding-plan": "https://api.z.ai/api/coding/paas/v4",
3236
xai: "https://api.x.ai/v1",
3337
google: "https://generativelanguage.googleapis.com/v1beta",
3438
}
@@ -427,6 +431,13 @@ export function DialogConnectProvider(props: { provider: string }) {
427431
id: model.id,
428432
name: model.name || model.id,
429433
}))
434+
// Official-provider transport settings are surfaced by the config overlay under
435+
// provider.<id>.options (backed by SettingsStore, not the config file). Seed the fields from there.
436+
const transportOption = (key: string): string => {
437+
const value = serverSync.data.config.provider?.[props.provider]?.options?.[key]
438+
if (value === false) return "0"
439+
return typeof value === "number" ? String(value) : ""
440+
}
430441
const [formStore, setFormStore] = createStore({
431442
value: "",
432443
baseURL:
@@ -438,8 +449,42 @@ export function DialogConnectProvider(props: { provider: string }) {
438449
discovered: [] as DiscoveredProviderModel[],
439450
selectedModel: undefined as string | undefined,
440451
discovering: false,
452+
headerTimeout: transportOption("headerTimeout"),
453+
chunkTimeout: transportOption("chunkTimeout"),
454+
timeout: transportOption("timeout"),
455+
maxRetries: transportOption("maxRetries"),
456+
transportError: undefined as string | undefined,
441457
})
442458

459+
// Parse a transport field: empty → undefined (use default); "0" for header/request timeout →
460+
// false (disabled); otherwise a positive integer. Returns { ok, value } so callers can reject
461+
// malformed input.
462+
function parseTransport(raw: string, allowFalse: boolean): { ok: boolean; value: number | false | undefined } {
463+
const trimmed = raw.trim()
464+
if (trimmed === "") return { ok: true, value: undefined }
465+
const n = Number(trimmed)
466+
if (!Number.isInteger(n) || n < 0) return { ok: false, value: undefined }
467+
if (n === 0) return allowFalse ? { ok: true, value: false } : { ok: false, value: undefined }
468+
return { ok: true, value: n }
469+
}
470+
471+
// Build the SettingsStore-bound options patch for an official provider (routed via the config
472+
// overlay's intercept). Returns undefined when a field is malformed.
473+
function transportOptions(): Record<string, number | false> | undefined | "invalid" {
474+
const header = parseTransport(formStore.headerTimeout, true)
475+
const chunk = parseTransport(formStore.chunkTimeout, false)
476+
const request = parseTransport(formStore.timeout, true)
477+
const retries = parseTransport(formStore.maxRetries, false)
478+
if (!header.ok || !chunk.ok || !request.ok || !retries.ok) return "invalid"
479+
const options: Record<string, number | false> = {}
480+
if (header.value !== undefined) options.headerTimeout = header.value
481+
if (chunk.value !== undefined && chunk.value !== false) options.chunkTimeout = chunk.value
482+
if (request.value !== undefined) options.timeout = request.value
483+
if (retries.value !== undefined && retries.value !== false) options.maxRetries = retries.value
484+
return options
485+
}
486+
487+
443488
async function handleSubmit(e: SubmitEvent) {
444489
e.preventDefault()
445490

@@ -466,6 +511,12 @@ export function DialogConnectProvider(props: { provider: string }) {
466511
setFormStore("baseURLError", undefined)
467512

468513
if (isBuiltinProvider(props.provider)) {
514+
const transport = transportOptions()
515+
if (transport === "invalid") {
516+
setFormStore("transportError", language.t("provider.connect.transport.error.number"))
517+
return
518+
}
519+
setFormStore("transportError", undefined)
469520
if (key) {
470521
await serverSDK.client.auth.set({
471522
providerID: props.provider,
@@ -475,8 +526,16 @@ export function DialogConnectProvider(props: { provider: string }) {
475526
},
476527
})
477528
}
529+
// Route transport tuning through the config overlay's intercept → SettingsStore. The
530+
// remaining official-provider fields are ignored by the backend loader; only options.<transport>
531+
// is picked up (and stripped from the config file). Always send the key so cleared fields reset.
478532
const disabled = (serverSync.data.config.disabled_providers ?? []).filter((id) => id !== props.provider)
479-
await serverSync.updateConfig({ disabled_providers: disabled })
533+
await serverSync.updateConfig({
534+
disabled_providers: disabled,
535+
provider: {
536+
[props.provider]: { options: transport },
537+
},
538+
})
480539
await serverSDK.client.global.dispose()
481540
showToast({
482541
variant: "success",
@@ -581,6 +640,55 @@ export function DialogConnectProvider(props: { provider: string }) {
581640
validationState={formStore.error ? "invalid" : undefined}
582641
error={formStore.error}
583642
/>
643+
<Show when={isBuiltinProvider(props.provider)}>
644+
<Collapsible class="w-full">
645+
<Collapsible.Trigger class="flex w-full items-center justify-between gap-2 text-13-medium text-text-base">
646+
<span>{language.t("provider.connect.transport.title")}</span>
647+
<Collapsible.Arrow />
648+
</Collapsible.Trigger>
649+
<Collapsible.Content>
650+
<div class="flex flex-col gap-4 pt-4">
651+
<div class="text-12-regular text-text-weak">
652+
{language.t("provider.connect.transport.description")}
653+
</div>
654+
<TextField
655+
type="text"
656+
inputmode="numeric"
657+
label={language.t("provider.connect.transport.headerTimeout.label")}
658+
placeholder={language.t("provider.connect.transport.headerTimeout.placeholder")}
659+
value={formStore.headerTimeout}
660+
onChange={(v) => setFormStore("headerTimeout", v)}
661+
/>
662+
<TextField
663+
type="text"
664+
inputmode="numeric"
665+
label={language.t("provider.connect.transport.chunkTimeout.label")}
666+
placeholder={language.t("provider.connect.transport.chunkTimeout.placeholder")}
667+
value={formStore.chunkTimeout}
668+
onChange={(v) => setFormStore("chunkTimeout", v)}
669+
/>
670+
<TextField
671+
type="text"
672+
inputmode="numeric"
673+
label={language.t("provider.connect.transport.timeout.label")}
674+
placeholder={language.t("provider.connect.transport.timeout.placeholder")}
675+
value={formStore.timeout}
676+
onChange={(v) => setFormStore("timeout", v)}
677+
/>
678+
<TextField
679+
type="text"
680+
inputmode="numeric"
681+
label={language.t("provider.connect.transport.maxRetries.label")}
682+
placeholder={language.t("provider.connect.transport.maxRetries.placeholder")}
683+
value={formStore.maxRetries}
684+
onChange={(v) => setFormStore("maxRetries", v)}
685+
validationState={formStore.transportError ? "invalid" : undefined}
686+
error={formStore.transportError}
687+
/>
688+
</div>
689+
</Collapsible.Content>
690+
</Collapsible>
691+
</Show>
584692
<Button class="w-auto" type="submit" size="large" variant="primary" disabled={formStore.discovering}>
585693
{language.t("common.continue")}
586694
</Button>

packages/app/src/components/settings-v2/general.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useParams } from "@solidjs/router"
1010
import { useLanguage } from "@/context/language"
1111
import { usePermission } from "@/context/permission"
1212
import { usePlatform, type DisplayBackend } from "@/context/platform"
13+
import { ZOOM_LEVELS } from "@/zoom-levels"
1314
import { useServerSync } from "@/context/server-sync"
1415
import { useServerSDK } from "@/context/server-sdk"
1516
import { useUpdaterAction } from "../updater-action"
@@ -284,6 +285,10 @@ export const SettingsGeneralV2: Component = () => {
284285
})),
285286
)
286287

288+
const zoomOptions = createMemo(() =>
289+
ZOOM_LEVELS.map((level) => ({ value: String(level), label: `${Math.round(level * 100)}%` })),
290+
)
291+
287292
const noneSound = { id: "none", label: "sound.option.none" } as const
288293
const soundOptions = [noneSound, ...SOUND_OPTIONS]
289294
const mono = () => monoInput(settings.appearance.font())
@@ -418,7 +423,18 @@ export const SettingsGeneralV2: Component = () => {
418423
if (option.value === deepAgentWishModel()) return
419424
void updateDeepAgentOptions(serverSync, { wishModel: option.value })
420425
}}
421-
/>
426+
>
427+
{(o) => (
428+
<span class="flex items-center gap-2">
429+
<span>{o.label}</span>
430+
<Show when={o.description}>
431+
<span class="rounded-sm bg-background-strong px-1.5 py-0.5 text-11-regular text-text-weak">
432+
{o.description}
433+
</span>
434+
</Show>
435+
</span>
436+
)}
437+
</SelectV2>
422438
</SettingsRowV2>
423439

424440
<SettingsRowV2
@@ -811,6 +827,23 @@ export const SettingsGeneralV2: Component = () => {
811827
</div>
812828
</SettingsRowV2>
813829

830+
<SettingsRowV2
831+
title={language.t("settings.general.row.zoom.title")}
832+
description={language.t("settings.general.row.zoom.description")}
833+
>
834+
<SelectV2
835+
appearance="inline"
836+
data-action="settings-zoom"
837+
options={zoomOptions()}
838+
current={zoomOptions().find((o) => o.value === String(platform.webviewZoom?.()))}
839+
placement="bottom-end"
840+
gutter={6}
841+
value={(o) => o.value}
842+
label={(o) => o.label}
843+
onSelect={(option) => option && platform.setWebviewZoom?.(Number(option.value))}
844+
/>
845+
</SettingsRowV2>
846+
814847
<Show when={linux()}>
815848
<SettingsRowV2
816849
title={

packages/app/src/components/windows-app-menu.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { IconButtonV2 } from "@deepagent-code/ui/v2/icon-button-v2"
66
import { Icon as IconV2 } from "@deepagent-code/ui/v2/icon"
77

88
import { useCommand } from "@/context/command"
9-
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
9+
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry, type DesktopMenuItem } from "@/desktop-menu"
1010
import { usePlatform } from "@/context/platform"
11+
import { canZoomIn, canZoomOut, zoomPercent } from "@/zoom-levels"
1112

1213
export function WindowsAppMenu(props: {
1314
command: ReturnType<typeof useCommand>
@@ -46,6 +47,18 @@ export function WindowsAppMenu(props: {
4647
if (entry.href) props.platform.openLink(entry.href)
4748
}
4849

50+
const zoom = () => props.platform.webviewZoom?.() ?? 1
51+
const isZoomAction = (action: DesktopMenuAction | undefined) =>
52+
action === "view.zoomIn" || action === "view.zoomOut" || action === "view.resetZoom"
53+
const zoomLabel = (entry: DesktopMenuItem) =>
54+
isZoomAction(entry.action) ? `${entry.label ?? ""} ${zoomPercent(zoom())}%` : entry.label ?? ""
55+
const zoomDisabled = (entry: DesktopMenuItem) => {
56+
if (entry.command) return commandDisabled(entry.command)
57+
if (entry.action === "view.zoomIn") return !canZoomIn(zoom())
58+
if (entry.action === "view.zoomOut") return !canZoomOut(zoom())
59+
return false
60+
}
61+
4962
return (
5063
<DropdownMenu gutter={4} modal={false} placement="bottom-start">
5164
{props.variant === "v2" ? (
@@ -87,9 +100,9 @@ export function WindowsAppMenu(props: {
87100
<DropdownMenu.Separator />
88101
) : (
89102
<DesktopMenuItem
90-
label={entry.label ?? ""}
103+
label={zoomLabel(entry)}
91104
keybind={entry.command ? props.command.keybind(entry.command) : entry.accelerator?.windows}
92-
disabled={entry.command ? commandDisabled(entry.command) : false}
105+
disabled={zoomDisabled(entry)}
93106
onSelect={() => runEntry(entry)}
94107
/>
95108
),

packages/app/src/context/platform.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ type PlatformBase = {
113113
/** Webview zoom level (desktop only) */
114114
webviewZoom?: Accessor<number>
115115

116+
/** Set the webview zoom level (desktop only) */
117+
setWebviewZoom?(level: number): void
118+
116119
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
117120
getPinchZoomEnabled?(): Promise<boolean> | boolean
118121

packages/app/src/i18n/ar.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,8 @@ export const dict = {
894894
"تفعيل التخطيط والصفحة الرئيسية والمحرر وواجهة الجلسة المعاد تصميمها",
895895
"settings.general.row.pinchZoom.title": "التكبير بالقرص",
896896
"settings.general.row.pinchZoom.description": "السماح بإيماءات قرص لوحة اللمس و Ctrl-تمرير للتكبير",
897+
"settings.general.row.zoom.title": "Zoom level",
898+
"settings.general.row.zoom.description": "Set the interface zoom level",
897899
"settings.updates.action.downloading": "جار التنزيل...",
898900
"settings.updates.action.installing": "جار التثبيت...",
899901
"settings.providers.section.deepagentBackends": "واجهات نماذج DeepAgent الخلفية",

packages/app/src/i18n/br.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,8 @@ export const dict = {
918918
"settings.general.row.pinchZoom.title": "Pinçar para zoom",
919919
"settings.general.row.pinchZoom.description":
920920
"Permitir gestos de pellizco en trackpad y Ctrl-desplazamiento para ampliar",
921+
"settings.general.row.zoom.title": "Zoom level",
922+
"settings.general.row.zoom.description": "Set the interface zoom level",
921923
"settings.updates.action.downloading": "Baixando...",
922924
"settings.updates.action.installing": "Instalando...",
923925
"settings.providers.section.deepagentBackends": "Backends de modelos DeepAgent",

packages/app/src/i18n/bs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,8 @@ export const dict = {
991991
"settings.general.row.pinchZoom.title": "Powiększanie gestem szczypania",
992992
"settings.general.row.pinchZoom.description":
993993
"Zezwól na powiększanie gestem szczypania na trackpadzie i Ctrl-przewijaniem",
994+
"settings.general.row.zoom.title": "Zoom level",
995+
"settings.general.row.zoom.description": "Set the interface zoom level",
994996
"settings.updates.action.downloading": "Preuzimanje...",
995997
"settings.updates.action.installing": "Instaliranje...",
996998
"settings.providers.section.deepagentBackends": "DeepAgent model backendi",

packages/app/src/i18n/da.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,8 @@ export const dict = {
980980
"settings.general.row.newLayoutDesigns.description": "Aktivér det redesignede layout, hjem, composer og sessions-UI",
981981
"settings.general.row.pinchZoom.title": "Knib for at zoome",
982982
"settings.general.row.pinchZoom.description": "Tillad trackpad-knib og Ctrl-scroll til zoom",
983+
"settings.general.row.zoom.title": "Zoom level",
984+
"settings.general.row.zoom.description": "Set the interface zoom level",
983985
"settings.updates.action.downloading": "Downloader...",
984986
"settings.updates.action.installing": "Installerer...",
985987
"settings.providers.section.deepagentBackends": "DeepAgent-modelbackends",

packages/app/src/i18n/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,8 @@ export const dict = {
925925
"Überarbeitetes Layout, Startseite, Composer und Sitzungs-UI aktivieren",
926926
"settings.general.row.pinchZoom.title": "Zum Zoomen kneifen",
927927
"settings.general.row.pinchZoom.description": "Trackpad-Kneifen und Strg-Scrollen zum Zoomen erlauben",
928+
"settings.general.row.zoom.title": "Zoom level",
929+
"settings.general.row.zoom.description": "Set the interface zoom level",
928930
"settings.updates.action.downloading": "Wird heruntergeladen...",
929931
"settings.updates.action.installing": "Wird installiert...",
930932
"settings.providers.section.deepagentBackends": "DeepAgent-Modell-Backends",

0 commit comments

Comments
 (0)