diff --git a/desktop-tauri/i18n/i18n-src/en/assistant.json b/desktop-tauri/i18n/i18n-src/en/assistant.json index bb9e31da..0077d848 100644 --- a/desktop-tauri/i18n/i18n-src/en/assistant.json +++ b/desktop-tauri/i18n/i18n-src/en/assistant.json @@ -1,5 +1,6 @@ { "CodeArts 的账号余额还剩多少?": "How much credit is left on the CodeArts account?", + "{n} 家 · {m} 个模型": "{n} providers · {m} models", "修改模型能力": "Edit model capabilities", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "Assistant settings could not be read, so the chat model can't be saved yet — see the hint below the input box.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "Assistant settings could not be read, so messages can't be sent yet — see the hint below the input box.", @@ -21,6 +22,7 @@ "禁用会把账号移出转发队列,线路可能因此断供。": "Disabling removes the account from the forwarding queue; the route may run dry as a result.", "获取模型列表": "List models", "设置模型映射 ID": "Set model mapping ID", + "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "Reads every provider’s model list (including custom ones), mapping names, and enabled state.", "读各提供商的模型清单、映射名与启用状态。": "Reads each provider's model list, mapping names, and enabled state.", "读各账号的额度 / 剩余用量。": "Reads each account's quota and remaining usage.", "调整提供商顺位": "Reorder providers", diff --git a/desktop-tauri/i18n/i18n-src/ja/assistant.json b/desktop-tauri/i18n/i18n-src/ja/assistant.json index 0b3d1a20..72aa9fec 100644 --- a/desktop-tauri/i18n/i18n-src/ja/assistant.json +++ b/desktop-tauri/i18n/i18n-src/ja/assistant.json @@ -1,5 +1,6 @@ { "CodeArts 的账号余额还剩多少?": "CodeArts のアカウント残高はあといくら?", + "{n} 家 · {m} 个模型": "{n} プロバイダー · {m} モデル", "修改模型能力": "モデル能力を変更", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、対話モデルをまだ保存できません。入力欄の下のヒントをご確認ください。", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、まだメッセージを送信できません。入力欄の下のヒントをご確認ください。", @@ -21,6 +22,7 @@ "禁用会把账号移出转发队列,线路可能因此断供。": "無効化するとアカウントは転送キューから外れ、その経路が供給停止になる可能性があります。", "获取模型列表": "モデル一覧を取得", "设置模型映射 ID": "モデルマッピング ID を設定", + "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "全プロバイダー(カスタム含む)のモデル一覧・マッピング名・有効状態を読み取ります。", "读各提供商的模型清单、映射名与启用状态。": "各プロバイダーのモデル一覧・マッピング名・有効状態を読み取ります。", "读各账号的额度 / 剩余用量。": "各アカウントの枠 / 残量を読み取ります。", "调整提供商顺位": "プロバイダーの順位を調整", diff --git a/desktop-tauri/i18n/i18n-src/ko/assistant.json b/desktop-tauri/i18n/i18n-src/ko/assistant.json index 817c4b66..e8a611ea 100644 --- a/desktop-tauri/i18n/i18n-src/ko/assistant.json +++ b/desktop-tauri/i18n/i18n-src/ko/assistant.json @@ -1,5 +1,6 @@ { "CodeArts 的账号余额还剩多少?": "CodeArts 계정 잔액이 얼마나 남았나요?", + "{n} 家 · {m} 个模型": "{n}개 제공자 · {m}개 모델", "修改模型能力": "모델 기능 수정", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 대화 모델을 아직 저장할 수 없습니다. 입력창 아래의 안내를 확인하세요.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 아직 메시지를 보낼 수 없습니다. 입력창 아래의 안내를 확인하세요.", @@ -21,6 +22,7 @@ "禁用会把账号移出转发队列,线路可能因此断供。": "비활성화하면 계정이 포워딩 큐에서 빠지며, 이 경로가 끊길 수 있습니다.", "获取模型列表": "모델 목록 조회", "设置模型映射 ID": "모델 매핑 ID 설정", + "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "모든 제공자(사용자 지정 포함)의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各提供商的模型清单、映射名与启用状态。": "각 제공자의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各账号的额度 / 剩余用量。": "각 계정의 할당량 / 남은 사용량을 읽습니다.", "调整提供商顺位": "제공자 순위 조정", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json b/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json index aa04c2c7..14fd73ae 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json @@ -1,5 +1,6 @@ { "CodeArts 的账号余额还剩多少?": "Quanto crédito ainda resta na conta CodeArts?", + "{n} 家 · {m} 个模型": "{n} provedores · {m} modelos", "修改模型能力": "Editar capacidades do modelo", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então o modelo de chat ainda não pode ser salvo. Veja a dica abaixo da caixa de entrada.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então ainda não é possível enviar mensagens. Veja a dica abaixo da caixa de entrada.", @@ -21,6 +22,7 @@ "禁用会把账号移出转发队列,线路可能因此断供。": "Desabilitar remove a conta da fila de encaminhamento; a rota pode ficar sem fornecimento.", "获取模型列表": "Listar modelos", "设置模型映射 ID": "Definir ID de mapeamento do modelo", + "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "Lê a lista de modelos de todos os provedores (incluindo personalizados), nomes de mapeamento e estado.", "读各提供商的模型清单、映射名与启用状态。": "Lê a lista de modelos, os nomes de mapeamento e o estado de cada provedor.", "读各账号的额度 / 剩余用量。": "Lê a cota e o uso restante de cada conta.", "调整提供商顺位": "Ajustar ordem dos provedores", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json b/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json index 130022dd..8a46454a 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json @@ -1,5 +1,6 @@ { "CodeArts 的账号余额还剩多少?": "", + "{n} 家 · {m} 个模型": "{n} 家 · {m} 個模型", "修改模型能力": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "", @@ -21,6 +22,7 @@ "禁用会把账号移出转发队列,线路可能因此断供。": "", "获取模型列表": "", "设置模型映射 ID": "", + "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "讀取全部提供商(含自訂家)的模型清單、對映名與啟用狀態。", "读各提供商的模型清单、映射名与启用状态。": "", "读各账号的额度 / 剩余用量。": "", "调整提供商顺位": "", diff --git a/desktop-tauri/ui-islands/src/islands/assistant-tools.ts b/desktop-tauri/ui-islands/src/islands/assistant-tools.ts index bbfb22e6..302e2da0 100644 --- a/desktop-tauri/ui-islands/src/islands/assistant-tools.ts +++ b/desktop-tauri/ui-islands/src/islands/assistant-tools.ts @@ -22,6 +22,7 @@ */ import { t } from '../i18n' +import { buildView, isCustom, setCapabilities as setCustomCapabilities } from './models-custom-source' /** 工具结果:`text` 回给模型(走 role:"tool" 的消息体),`summary` 给卡片显示 */ export type ToolResult = { @@ -107,6 +108,9 @@ type ModelRow = { providerLabel?: unknown enabled?: unknown aliases?: unknown + capabilities?: unknown + capSources?: unknown + reasoningLevels?: unknown } type AccountRow = { id?: unknown @@ -117,14 +121,50 @@ type AccountRow = { enabled?: unknown } +/** 自定义提供商目录(providers.js 的缓存;模型管理页读的也是这一份) */ +function customDirectory(): Array<{ id?: unknown }> { + const providers = (window as unknown as { + wbProviders?: { customList?: () => Array<{ id?: unknown }>; refreshCustom?: () => Promise } + }).wbProviders + return providers?.customList?.() || [] +} + +/** + * 全部模型行:内置家(`/api/models/manage`)+ **自定义家**。 + * + * ── 为什么必须自己并自定义家 ───────────────────────────────── + * `manage` 只走编译期注册表(`catalog::manage_view` → `PROVIDERS`),自定义家 + * 存在运行期的 `customProviders` 键里、不进那张表 —— 只读 manage 的话助手眼里 + * 永远只有内置家,「查提供商列表」会漏掉用户自建的全部家(而且模型看不到数据 + * 时会自己编归属)。自定义家的取数与表格渲染同源(`buildView`,见 + * `models-custom-source` 模块头),这里只做拼接、不另写一套适配。 + */ async function readModels(): Promise { const data = (await bridge().getModelManage?.()) as { models?: ModelRow[] } | undefined - return Array.isArray(data?.models) ? data.models : [] + // 拷一份再追加:桥那边若回了同一份缓存对象,就地 push 会让下一次调用 + // 看到上一轮追加过的自定义家行(同一批行重复计数) + const rows = Array.isArray(data?.models) ? data.models.slice() : [] + const directory = (window as unknown as { + wbProviders?: { refreshCustom?: () => Promise } + }).wbProviders + // 目录是 providers.js 在加载期异步拉的,首屏那几秒可能还没到 —— 空时补一次 + if (!customDirectory().length) await directory?.refreshCustom?.().catch(() => {}) + for (const provider of customDirectory()) { + const view = buildView(String(provider?.id || '')) + if (view) rows.push(...view.models) + } + return rows } async function readAccounts(): Promise { - const state = (await bridge().getState?.()) as { accounts?: AccountRow[] } | undefined - const rows = Array.isArray(state?.accounts) ? state.accounts : [] + const state = (await bridge().getState?.()) as + | { accounts?: { accounts?: AccountRow[] } | AccountRow[] } + | undefined + // `/api/session` 的 `accounts` 是**对象**(`{accounts, currentAccountId, currentAccountIds, + // providers}`,见 `api::session::get_session`),不是数组 —— 直接 `Array.isArray(state.accounts)` + // 恒为 false,四只账号类工具会全部退化成「没有找到账号」。旧版形态(裸数组)一并接住。 + const block = state?.accounts + const rows = Array.isArray(block) ? block : Array.isArray(block?.accounts) ? block.accounts : [] // 全局队列 = priority 升序(后端 priority 0 起、越小越靠前) return rows .slice() @@ -162,38 +202,87 @@ const listModels: AssistantTool = { id: 'list_models', label: t('获取模型列表'), kind: 'read', - desc: '读取各提供商的模型清单(模型 ID、所属提供商、是否启用、已挂的映射名)以及全部映射。回答「有哪些模型」「哪些模型在多家重复」「某个名字挂在哪」这类问题之前先调用它。', + desc: '读取全部提供商的模型清单(模型 ID、所属提供商 id 与展示名、是否启用、已挂的映射名、**能力位**:上下文窗口 / 最大输出 Token / 工具调用 / 图片 / 视频 / 深度推理,以及每个能力值的来源)以及全部映射。内置家与用户自建的自定义家都在里面。回答「有哪些提供商 / 几家」「有哪些模型」「上下文窗口多大」「支不支持工具调用 / 图片 / 推理」「哪些模型在多家重复」「某个名字挂在哪」这类问题之前先调用它。', params: { type: 'object', properties: { - provider: { type: 'string', description: '只看某一家(提供商 id,如 codearts / workbuddy);不填 = 全部' }, - keyword: { type: 'string', description: '按模型 ID 或映射名做子串过滤;不填 = 不过滤' }, + provider: { + type: 'string', + description: '只看某一家:提供商 id(如 codearts、custom-3f2a91b04c7e)或展示名(如「商汤日日新」);不填 = 全部', + }, + keyword: { type: 'string', description: '按模型 ID、模型名、映射名或提供商名做子串过滤;不填 = 不过滤' }, enabledOnly: { type: 'boolean', description: 'true = 只看已启用的模型' }, }, }, run: async args => { - const provider = str(args.provider, 64).toLowerCase() + const wanted = str(args.provider, 64).toLowerCase() const keyword = str(args.keyword, 128).toLowerCase() const enabledOnly = bool(args.enabledOnly) === true - const data = (await bridge().getModelManage?.()) as { models?: ModelRow[]; mappings?: Array> } | undefined - const rows = (Array.isArray(data?.models) ? data.models : []).filter(row => { - if (provider && str(row.provider).toLowerCase() !== provider) return false + const data = (await bridge().getModelManage?.()) as { mappings?: Array> } | undefined + const all = await readModels() + // 提供商点名:id 与展示名都认(自定义家的 id 是 custom- + 随机串,用户只会说名字) + const rowProvider = (row: ModelRow) => { + const id = str(row.provider) + return { id, label: str(row.providerLabel) || id } + } + const matchesProvider = (row: ModelRow) => { + if (!wanted) return true + const { id, label } = rowProvider(row) + return id.toLowerCase() === wanted || label.toLowerCase() === wanted + || id.toLowerCase().includes(wanted) || label.toLowerCase().includes(wanted) + } + const rows = all.filter(row => { + if (!matchesProvider(row)) return false if (enabledOnly && row.enabled !== true) return false if (keyword) { - const hay = [str(row.id), str(row.name), ...(Array.isArray(row.aliases) ? row.aliases.map(item => str(item)) : [])].join(' ').toLowerCase() + const hay = [str(row.id), str(row.name), str(row.provider), str(row.providerLabel), + ...(Array.isArray(row.aliases) ? row.aliases.map(item => str(item)) : [])].join(' ').toLowerCase() if (!hay.includes(keyword)) return false } return true }) - const slim = rows.map(row => ({ - provider: str(row.provider), - id: str(row.id), - enabled: row.enabled === true, - aliases: Array.isArray(row.aliases) ? row.aliases.map(item => str(item)) : [], - })) + const slim = rows.map(row => { + // 能力位:**稀疏**投影(只写有值的键,`null` = 未声明就不出现)。 + // 六键齐全地写会带一堆 null(实测 payload +351%),而「键缺失 = 未声明」 + // 与前端 `ManageModel.capabilities` 的约定一致;稀疏只 +134%。 + const capabilities: Record = {} + const raw = (row.capabilities && typeof row.capabilities === 'object' ? row.capabilities : {}) as Record + for (const key of CAPABILITY_KEYS) { + if (raw[key] === null || raw[key] === undefined) continue + capabilities[key] = raw[key] + } + return { + provider: str(row.provider), + providerLabel: str(row.providerLabel) || str(row.provider), + id: str(row.id), + enabled: row.enabled === true, + aliases: Array.isArray(row.aliases) ? row.aliases.map(item => str(item)) : [], + // 空对象 = 这一家一个字都没声明(自定义家的常见形态:用户还没填) + capabilities, + // 每个能力键的来源(`upstream` / `modelsdev` / `user`);缺失的键 = 无来源。 + // 它决定这行数字该不该信 —— 上游实测与用户手填长得一样,可信度差着量级 + capSources: row.capSources && typeof row.capSources === 'object' ? row.capSources : {}, + // 模型自己能配哪些思考档(缺失 = 未声明,界面/调用方回落通用档位表) + reasoningLevels: Array.isArray(row.reasoningLevels) ? row.reasoningLevels : [], + } + }) + // 提供商汇总一并给出:模型要回答「有几家 / 都是谁」时不必自己从模型行里去重, + // 也就不会再漏掉「一家都没登记的模型」的家 + const providers: Record = {} + for (const row of slim) { + const entry = providers[row.provider] || (providers[row.provider] = { label: row.providerLabel, models: 0 }) + entry.models += 1 + } return { - text: JSON.stringify({ total: slim.length, models: slim.slice(0, 400) }), - summary: t('{n} 个模型', { n: slim.length }), + text: JSON.stringify({ + providerCount: Object.keys(providers).length, + modelCount: slim.length, + providers, + models: slim.slice(0, 400), + // 全部映射(含指向别家的别名):判断「某个名字挂在哪」时要它 + mappings: Array.isArray(data?.mappings) ? data.mappings.slice(0, 400) : [], + }), + summary: t('{n} 家 · {m} 个模型', { n: Object.keys(providers).length, m: slim.length }), } }, } @@ -369,11 +458,11 @@ const setModelCapability: AssistantTool = { id: 'set_model_capability', label: t('修改模型能力'), kind: 'write', - desc: '修改某个模型对下游声明的能力位:上下文窗口、最大输出 Token、工具调用 / 图片 / 视频 / 深度推理四个布尔位。只传要改的键;布尔位给 null 表示恢复上游原值。', + desc: '修改某个模型对下游声明的能力位:上下文窗口、最大输出 Token、工具调用 / 图片 / 视频 / 深度推理四个布尔位。只传要改的键;null = 清除这一项(内置家恢复上游原值,自定义家删掉这条用户手填的覆盖)。**内置家与自定义家都支持**。改动前请先用 `list_models` 读出当前值,别凭印象改。', params: { type: 'object', properties: { - provider: { type: 'string', description: '提供商 id' }, + provider: { type: 'string', description: '提供商 id(内置家如 codearts,自定义家如 custom-3f2a91b04c7e)' }, id: { type: 'string', description: '模型 ID' }, capabilities: { type: 'object', @@ -426,7 +515,13 @@ const setModelCapability: AssistantTool = { } } if (!provider || !id || !Object.keys(caps).length) throw new Error(t('参数不完整(需要 provider / id / capabilities)')) - await bridge().setModelCapabilities?.(provider, id, caps) + // 两条写路径(与模型管理页的 `writeCapabilities` 同一分流): + // · 自定义家:能力位存在提供商记录自己的 `models[].capabilities` 里,只能整表替换 + // (`POST /api/custom-providers/models`)—— 走桥的 `/api/models/capabilities` + // 会被后端以「未知的内置提供商」400 拒掉; + // · 内置家:覆盖层落在 `modelRules.capabilities`,走桥的既有接口。 + if (isCustom(provider)) await setCustomCapabilities(provider, id, caps as Record) + else await bridge().setModelCapabilities?.(provider, id, caps) refreshApp() return { text: JSON.stringify({ ok: true, provider, id, capabilities: caps }), summary: t('已修改 {n} 项能力', { n: Object.keys(caps).length }) } }, @@ -656,7 +751,7 @@ export function toolsPayload(enabled: Record): Array = [ - { id: 'list_models', label: t('获取模型列表'), desc: t('读各提供商的模型清单、映射名与启用状态。') }, + { id: 'list_models', label: t('获取模型列表'), desc: t('读全部提供商(含自定义家)的模型清单、映射名与启用状态。') }, { id: 'list_account_balance', label: t('查询账号余额'), desc: t('读各账号的额度 / 剩余用量。') }, { id: 'set_model_enabled', label: t('启用 / 禁用模型'), desc: t('开关模型的对外可用性,不改其它属性。') }, { id: 'set_model_mapping', label: t('设置模型映射 ID'), desc: t('新增 / 删除映射名,支持批量加前缀与同名合并到多家。') }, diff --git a/desktop-tauri/ui/css/responsive.css b/desktop-tauri/ui/css/responsive.css new file mode 100644 index 00000000..4a2a1f9a --- /dev/null +++ b/desktop-tauri/ui/css/responsive.css @@ -0,0 +1,222 @@ +/* ───────────────────────────────────────────────────────────── + 移动端 / 窄视口适配(headless 面板经 LAN 被手机、平板访问) + + ── 为什么需要这个文件 ────────────────────────────────────── + 面板原本只为**桌面窗口**设计:`tokens.css` 给 body 写死了 + `min-width: 720px`(护栏,配合 src-tauri 的 WIN_MIN_WIDTH=820), + 而 `.content` / `.page` 是 `overflow: hidden`。于是当视口窄于 720px, + 多出来的宽度既画不出来也**滚不到** —— 不是"难看",是**功能不可达**。 + 实测 390px 下:59 个功能控件里 44 个点不到(页面被固定在 720px 宽)。 + + ── 桌面不受影响 ──────────────────────────────────────────── + 全部规则包在 `max-width: 720px` 内,而桌面窗口最小 820px, + 永远进不来。桌面形态逐字节不变(已在 1280px 实测无回归)。 + + ── 加载位置 ──────────────────────────────────────────────── + 排在 islands/ui.css 之后(最后一个):组件库工具类带 !important, + 少数规则需要用它压制;全部断点内的声明都不会外泄到桌面。 + ───────────────────────────────────────────────────────────── */ + +@media (max-width: 720px) { + /* ── 1. 解开为桌面窗口写死的 720px 下限(本文件存在的根本原因) ── */ + body { min-width: 0; } + + :root { + /* 900px 断点把侧栏收到 68px,手机上仍偏宽(占 390px 的 17%) */ + --sidebar-w: 56px; + --pad: 12px; + /* 80vw 限宽在窄屏等于"再压窄一次":390px 下内容只剩 312px。 + 移动端铺满,把宽度全让给表格与日志正文。 */ + --content-max: none; + } + + .topbar { padding: 0 12px; } + + /* ── 2. 表格页脚:定宽控件在窄屏必须能换行/收缩 ────────────── + 五张表共用的 `.table-foot-ctl` 是 `flex: 0 0 auto` 的定宽块 + (实测 465px,日志页含"跳至页"达 564px)。父级 `.panel` 是 + `overflow: clip`,超出部分被**硬裁**且滚不到 —— 翻页器整块消失。 + 与是否移动端无关,临界点约 530px(日志页约 630px)。 */ + .panel-foot { flex-wrap: wrap; } + .table-foot-ctl { + flex: 1 1 100%; + min-width: 0; + margin-left: 0; + justify-content: flex-start; + flex-wrap: wrap; + } + .table-foot-bar { flex-wrap: wrap; } + /* 页脚内带 Tailwind `min-w-[...]` 的读数块,允许收缩 */ + .table-foot-ctl > *, + .table-foot-count, + .table-foot-size, + .table-foot-jump { min-width: 0; } + + /* ── 3. 网关页筛选行:搜索框硬编码 240px 且不换行 ─────────── */ + .mm-filters { flex-wrap: wrap; } + #models-search { + flex: 1 1 160px; + width: auto; + min-width: 0; + } + /* 页头动作区(列设置 / 新建 / 添加模型…)。 + page-gateway.css:72 有一条 `.page[data-page="gateway"] .panel-head .head-actions + { flex: 0 0 auto; flex-wrap: nowrap; }`(特异性 0,4,0),会压过普通的 + `.panel-head .head-actions`(0,2,0)—— 必须用同等特异性才盖得住, + 否则「列设置」按钮在 390px 下稳稳越界 40px(实测 r=430 vs vw=390)。 */ + .page[data-page="gateway"] .panel-head .head-actions { + flex: 1 1 100%; + min-width: 0; + flex-wrap: wrap; + margin-left: 0; + justify-content: flex-start; + } + + /* ── 4. 设置页分类导航:横滚条会把一半分类藏起来 ───────────── + 900px 断点把它转成横排 + `overflow-x: auto`,实测 10 个分类共 + 881px,而可视宽只有 310px —— 后 7 个(路由/重试/超时/安全/数据/ + 反馈/更新)在手机上**看不见也想不到要横滑**。 + 改成两列网格一次铺开,全部可见可点。 */ + .settings-layout { gap: 10px; } + .settings-nav { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px; + overflow: visible; + width: 100%; + } + .settings-nav-item { + width: auto; + min-width: 0; + padding: 0 10px; + justify-content: flex-start; + } + + /* ── 5. 触控目标:30px 对触屏偏小 ───────────────────────────── + 组件库按钮的 30px 来自 Tailwind 工具类 `.h-\[30px\]{height:30px!important}` + (在 islands/ui.css 的 `@layer utilities` 内)。 + ⚠️ 直接写 `height:38px!important` **不管用**:按 CSS Cascade 5,同为 + important 时**分层声明胜过未分层声明** → 只有不含该工具类的按钮生效, + 实测 115/278 变高、163 个不动,「同一界面两种按钮高度」比不改更糟。 + ✅ 正确做法是用 **`min-height`**:它与 `height:30px` 是**不同属性**, + 本就不冲突,无需对轰 important,也不需要包 `@layer`(已实测层外层内 + 均 278/278 生效)。见文件末尾第 5b 节。 */ + /* 侧栏与设置页分类导航不是组件库按钮,无 Tailwind 高度类,直接设即可 */ + .nav-item { height: 40px; } + .settings-nav-item { height: 40px; } + .log-filters select, + .log-filters input, + .toolbar input, + .toolbar select { height: 36px; } + + /* ── 6. 日志与请求正文:确保长内容换行而非被裁 ─────────────── + `.log-row` 是 `grid-template-columns: 10px minmax(0,1fr) auto`, + 正文列本就是 minmax(0,1fr),配 `word-break: break-word` 默认已 + 完整换行显示(实测 50 行日志零截断)。这里只是显式兜底: + 等宽正文里的长 token/URL 强制断行,保证"信息齐全"。 */ + .log-row .msg, + .log-row .extra, + .req-row .msg { overflow-wrap: anywhere; } + + /* 页头/工具条内侧留白收窄,把宽度还给内容 */ + .panel-head { padding: 10px 12px; } + .panel-body { padding: 12px 12px; } + .toolbar { padding: 10px 12px; } + .content { padding: var(--pad) var(--pad) 0; } + + /* ── 7. 签到中心:有真实账号数据后才会暴露的横向溢出 ────────── + 空数据实例测不出来(踩过的坑):接入真实账号后,`.ck-tabs` 的 + 分段控件 + 右侧读数、以及 `.ck-prov-main` 里「提供商名 + 操作 + + 折叠箭头」的 min-content 会把 `.ck-main` 的**隐式网格列**撑到 + 478px(实测生产 3459:`.ck-main` sw=478 / cw=310)。 + 父级 `.panel` 是 `overflow: clip` → 右侧控件被硬裁,滚也滚不到 + (实测 16 处不可达)。 + 对策:把网格列钉成 `minmax(0,1fr)` 不让它按内容膨胀, + 再让这几行 flex 允许换行/收缩。 */ + .ck-main { grid-template-columns: minmax(0, 1fr); } + .ck-main > * { min-width: 0; } + .ck-tabs { flex-wrap: wrap; } + .ck-prov-main { flex-wrap: wrap; } + .ck-prov-info { flex: 1 1 140px; min-width: 0; } + .ck-prov-right { flex: 0 1 auto; flex-wrap: wrap; } + /* 页头读数默认 nowrap,窄屏允许折行(否则它单方面顶宽面板) */ + .panel-head .panel-sub { white-space: normal; } + + /* 签到中心三个 tab(分段控件):整体 357px 塞不进 310px,且它是 + `inline-flex` 不换行 —— 只会把页面顶出横向滚动(实测 pageSW=359)。 + 两个方案取舍: + · 强行等分 → 每项仅 100px,「Scheduled check-in」被省略号截断, + 违背"信息齐全"; + · 让分段控件**内部换行** → 三个 tab 折成两行,文字全部完整可读 + (实测 clipped=false,三项均在 310px 内)。 + 选后者:手机上有的是纵向空间,缺的是横向。滑块的 thumb 在换行后 + 会错位,故隐藏(选中态本就由 seg-item 自身的高亮体现)。 */ + .ck-tabs > [data-slot="segmented-control"] { + flex: 1 1 100%; + min-width: 0; + flex-wrap: wrap; + } + .ck-tabs [data-slot="segmented-item"] { + flex: 0 1 auto; + min-width: 0; + } + .ck-tabs [data-slot="segmented-thumb"] { display: none; } +} + +/* ── 6b. 极窄屏(320px 档,iPhone SE 一代等)─────────────── + 分段控件、批量条、列设置按钮仍有内在最小宽度。让工具条各组 + 整行铺开,避免被 `.panel` 的 clip 裁掉。 */ +@media (max-width: 380px) { + .toolbar .group, + .toolbar .actions, + .mm-filters .group, + .mm-filters .input-group, + .batch-actions { flex: 1 1 100%; min-width: 0; } + .panel-head .head-actions { flex: 1 1 100%; margin-left: 0; } + :root { --pad: 10px; } + + /* 网关页搜索框:外层是组件库产物 `w-[240px] !important` 的包裹层, + 内层 #models-search 由 page-gateway.css 定为 `flex: 0 0 auto; width:240px` + —— 两层都是 240px 死宽,320px 视口下必然顶出面板右缘(r=323 vs vw=320)。 + 这里收窄 mm-filters 内边距给足空间(16px → 10px,省出 12px), + 实测正好把 240px 收进 320px 边界(r=317)。 */ + .mm-filters { padding: 6px 10px 10px; } +} + +/* ───────────────────────────────────────────────────────────── + 5b. 触控目标放大 + + ── 为什么用 min-height 而不是 height ─────────────────────── + 组件库按钮的 30px 来自 Tailwind 工具类(在 islands/ui.css 的 + `@layer utilities` 内):`.h-\[30px\]{height:30px!important}`。 + 想用 `height:38px!important` 盖掉它**会失败**:按 CSS Cascade 5, + 同为 important 时**分层声明胜过未分层声明** → 实测 278 个按钮里 + 只有 115 个变高(那些不含该工具类的),163 个仍是原值, + 表现为「同一界面两种按钮高度」,比不改更糟。 + + `min-height` 则与 `height:30px` 是**不同属性**,本就不冲突 —— + 无需 !important,无需对轰,也不需要包 `@layer` + (实测层外/层内写法均 278/278 生效)。 + + 实测(生产 3459,gateway 页): + · `height:38px!important`(层外)→ 115/278 生效(分裂,弃用) + · `min-height:38px`(层外) → 278/278 生效(采用) + + ── 副作用检查 ────────────────────────────────────────────── + 表格行高由内容撑开,不会被压扁;实测 accounts 首行 33→39px、 + 行内按钮 38px、**无重叠**;页面无横向滚动。 + + ── 桌面不受影响 ──────────────────────────────────────────── + 包在 max-width:720px 内;桌面最小窗宽 820px,永远进不来。 + 实测桌面按钮高度分布与改动前一致(18/22/24/26/30px)。 + ───────────────────────────────────────────────────────────── */ +@media (max-width: 720px) { + /* 排除拖拽手柄(.col-grip 是 9px 宽的表头把手,放大没有意义) */ + .page.active button:not(.col-grip) { min-height: 38px; } + /* 搜索框/文本输入同样放宽(30px → 34px)。分页「跳至页」那个 26px 的 + 小数字框**故意不动**:它与翻页按钮同一行,放大反而挤掉页码读数。 */ + .page.active input[type="search"], + .page.active input[type="text"], + .page.active input[type="number"], + .page.active input[type="password"] { min-height: 34px; } +} diff --git a/desktop-tauri/ui/i18n/en/assistant.js b/desktop-tauri/ui/i18n/en/assistant.js index 707785cc..2abe6504 100644 --- a/desktop-tauri/ui/i18n/en/assistant.js +++ b/desktop-tauri/ui/i18n/en/assistant.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:en/assistant —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "How much credit is left on the CodeArts account?", "修改模型能力": "Edit model capabilities", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "Assistant settings could not be read, so the chat model can't be saved yet — see the hint below the input box.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "Assistant settings could not be read, so messages can't be sent yet — see the hint below the input box.", "发起模型测试": "Run a model test", "启用 / 禁用模型": "Enable / disable models", "启用 / 禁用账号": "Enable / disable accounts", "对指定模型发一次真实请求,回报连通性与耗时。": "Sends one real request to the given model and reports reachability and latency.", "开关模型的对外可用性,不改其它属性。": "Toggles a model's outward availability without touching other attributes.", "把重复的模型加上家名前缀区分开": "Prefix duplicated models with their provider name to tell them apart", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edits capability flags such as context window, max output tokens, and tool calls.", "改某一家的整组顺位(先试哪一家)。": "Reorders a provider's whole group (which provider is tried first).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Changes an account's rank in the global queue, deciding primary/backup and failover order.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adds/removes mapping names, with bulk prefixing and merging a shared name across providers.", "查询账号余额": "Query account balance", "没有可用的模型": "No models available", "测一下当前最快的模型通不通": "Check whether the currently fastest model is reachable", "深度推理": "Deep reasoning", "看看哪些模型在多家重复了": "Show which models are duplicated across providers", "禁用会把账号移出转发队列,线路可能因此断供。": "Disabling removes the account from the forwarding queue; the route may run dry as a result.", "获取模型列表": "List models", "设置模型映射 ID": "Set model mapping ID", "读各提供商的模型清单、映射名与启用状态。": "Reads each provider's model list, mapping names, and enabled state.", "读各账号的额度 / 剩余用量。": "Reads each account's quota and remaining usage.", "调整提供商顺位": "Reorder providers", "调整账号优先级": "Adjust account priority", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "This gateway build doesn't have the AI assistant API yet (HTTP 404): the backend is older. Rebuild or restart the client and try again. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "This turn has reached the {n}-round-trip limit, so I stopped for your input (try rephrasing, or split it into a few steps).", "连接网关失败({base}):{message}": "Failed to reach the gateway ({base}): {message}", "配置响应不完整": "Incomplete config response" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "How much credit is left on the CodeArts account?", "{n} 家 · {m} 个模型": "{n} providers · {m} models", "修改模型能力": "Edit model capabilities", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "Assistant settings could not be read, so the chat model can't be saved yet — see the hint below the input box.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "Assistant settings could not be read, so messages can't be sent yet — see the hint below the input box.", "发起模型测试": "Run a model test", "启用 / 禁用模型": "Enable / disable models", "启用 / 禁用账号": "Enable / disable accounts", "对指定模型发一次真实请求,回报连通性与耗时。": "Sends one real request to the given model and reports reachability and latency.", "开关模型的对外可用性,不改其它属性。": "Toggles a model's outward availability without touching other attributes.", "把重复的模型加上家名前缀区分开": "Prefix duplicated models with their provider name to tell them apart", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edits capability flags such as context window, max output tokens, and tool calls.", "改某一家的整组顺位(先试哪一家)。": "Reorders a provider's whole group (which provider is tried first).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Changes an account's rank in the global queue, deciding primary/backup and failover order.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adds/removes mapping names, with bulk prefixing and merging a shared name across providers.", "查询账号余额": "Query account balance", "没有可用的模型": "No models available", "测一下当前最快的模型通不通": "Check whether the currently fastest model is reachable", "深度推理": "Deep reasoning", "看看哪些模型在多家重复了": "Show which models are duplicated across providers", "禁用会把账号移出转发队列,线路可能因此断供。": "Disabling removes the account from the forwarding queue; the route may run dry as a result.", "获取模型列表": "List models", "设置模型映射 ID": "Set model mapping ID", "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "Reads every provider’s model list (including custom ones), mapping names, and enabled state.", "读各提供商的模型清单、映射名与启用状态。": "Reads each provider's model list, mapping names, and enabled state.", "读各账号的额度 / 剩余用量。": "Reads each account's quota and remaining usage.", "调整提供商顺位": "Reorder providers", "调整账号优先级": "Adjust account priority", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "This gateway build doesn't have the AI assistant API yet (HTTP 404): the backend is older. Rebuild or restart the client and try again. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "This turn has reached the {n}-round-trip limit, so I stopped for your input (try rephrasing, or split it into a few steps).", "连接网关失败({base}):{message}": "Failed to reach the gateway ({base}): {message}", "配置响应不完整": "Incomplete config response" }); diff --git a/desktop-tauri/ui/i18n/ja/assistant.js b/desktop-tauri/ui/i18n/ja/assistant.js index 71850669..bbe77420 100644 --- a/desktop-tauri/ui/i18n/ja/assistant.js +++ b/desktop-tauri/ui/i18n/ja/assistant.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ja/assistant —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts のアカウント残高はあといくら?", "修改模型能力": "モデル能力を変更", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、対話モデルをまだ保存できません。入力欄の下のヒントをご確認ください。", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、まだメッセージを送信できません。入力欄の下のヒントをご確認ください。", "发起模型测试": "モデルテストを実行", "启用 / 禁用模型": "モデルの有効化 / 無効化", "启用 / 禁用账号": "アカウントの有効化 / 無効化", "对指定模型发一次真实请求,回报连通性与耗时。": "指定したモデルに実際のリクエストを 1 回送り、到達性と所要時間を報告します。", "开关模型的对外可用性,不改其它属性。": "モデルの対外的な可用性だけを切り替え、他の属性は変更しません。", "把重复的模型加上家名前缀区分开": "重複しているモデルにプロバイダー名の接頭辞を付けて区別する", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "コンテキストウィンドウ / 最大出力トークン / ツール呼び出しなどの能力フラグを変更します。", "改某一家的整组顺位(先试哪一家)。": "あるプロバイダーのグループ全体の順位を変更します(どちらを先に試すか)。", "改账号在全局队列里的名次,决定主备与切换顺序。": "グローバルキュー内のアカウント順位を変更し、主系 / 予備と切り替え順を決めます。", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "マッピング名の追加 / 削除。一括での接頭辞付与や、同名を複数プロバイダーへ統合できます。", "查询账号余额": "アカウント残高を照会", "没有可用的模型": "利用可能なモデルがありません", "测一下当前最快的模型通不通": "現在いちばん速いモデルが通るか試す", "深度推理": "深い推論", "看看哪些模型在多家重复了": "複数のプロバイダーで重複しているモデルを見る", "禁用会把账号移出转发队列,线路可能因此断供。": "無効化するとアカウントは転送キューから外れ、その経路が供給停止になる可能性があります。", "获取模型列表": "モデル一覧を取得", "设置模型映射 ID": "モデルマッピング ID を設定", "读各提供商的模型清单、映射名与启用状态。": "各プロバイダーのモデル一覧・マッピング名・有効状態を読み取ります。", "读各账号的额度 / 剩余用量。": "各アカウントの枠 / 残量を読み取ります。", "调整提供商顺位": "プロバイダーの順位を調整", "调整账号优先级": "アカウントの優先度を調整", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "このバージョンのゲートウェイには AI アシスタント API がまだありません(HTTP 404):バックエンドが古いため、再ビルド / クライアント再起動後にお試しください。{message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "このターンは往復 {n} 回の上限に達したため、指示を待って停止しました(言い換えるか、いくつかの手順に分けてください)。", "连接网关失败({base}):{message}": "ゲートウェイへの接続に失敗({base}):{message}", "配置响应不完整": "設定レスポンスが不完全です" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts のアカウント残高はあといくら?", "{n} 家 · {m} 个模型": "{n} プロバイダー · {m} モデル", "修改模型能力": "モデル能力を変更", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、対話モデルをまだ保存できません。入力欄の下のヒントをご確認ください。", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、まだメッセージを送信できません。入力欄の下のヒントをご確認ください。", "发起模型测试": "モデルテストを実行", "启用 / 禁用模型": "モデルの有効化 / 無効化", "启用 / 禁用账号": "アカウントの有効化 / 無効化", "对指定模型发一次真实请求,回报连通性与耗时。": "指定したモデルに実際のリクエストを 1 回送り、到達性と所要時間を報告します。", "开关模型的对外可用性,不改其它属性。": "モデルの対外的な可用性だけを切り替え、他の属性は変更しません。", "把重复的模型加上家名前缀区分开": "重複しているモデルにプロバイダー名の接頭辞を付けて区別する", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "コンテキストウィンドウ / 最大出力トークン / ツール呼び出しなどの能力フラグを変更します。", "改某一家的整组顺位(先试哪一家)。": "あるプロバイダーのグループ全体の順位を変更します(どちらを先に試すか)。", "改账号在全局队列里的名次,决定主备与切换顺序。": "グローバルキュー内のアカウント順位を変更し、主系 / 予備と切り替え順を決めます。", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "マッピング名の追加 / 削除。一括での接頭辞付与や、同名を複数プロバイダーへ統合できます。", "查询账号余额": "アカウント残高を照会", "没有可用的模型": "利用可能なモデルがありません", "测一下当前最快的模型通不通": "現在いちばん速いモデルが通るか試す", "深度推理": "深い推論", "看看哪些模型在多家重复了": "複数のプロバイダーで重複しているモデルを見る", "禁用会把账号移出转发队列,线路可能因此断供。": "無効化するとアカウントは転送キューから外れ、その経路が供給停止になる可能性があります。", "获取模型列表": "モデル一覧を取得", "设置模型映射 ID": "モデルマッピング ID を設定", "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "全プロバイダー(カスタム含む)のモデル一覧・マッピング名・有効状態を読み取ります。", "读各提供商的模型清单、映射名与启用状态。": "各プロバイダーのモデル一覧・マッピング名・有効状態を読み取ります。", "读各账号的额度 / 剩余用量。": "各アカウントの枠 / 残量を読み取ります。", "调整提供商顺位": "プロバイダーの順位を調整", "调整账号优先级": "アカウントの優先度を調整", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "このバージョンのゲートウェイには AI アシスタント API がまだありません(HTTP 404):バックエンドが古いため、再ビルド / クライアント再起動後にお試しください。{message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "このターンは往復 {n} 回の上限に達したため、指示を待って停止しました(言い換えるか、いくつかの手順に分けてください)。", "连接网关失败({base}):{message}": "ゲートウェイへの接続に失敗({base}):{message}", "配置响应不完整": "設定レスポンスが不完全です" }); diff --git a/desktop-tauri/ui/i18n/ko/assistant.js b/desktop-tauri/ui/i18n/ko/assistant.js index b42fd512..e3c38aa3 100644 --- a/desktop-tauri/ui/i18n/ko/assistant.js +++ b/desktop-tauri/ui/i18n/ko/assistant.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ko/assistant —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts 계정 잔액이 얼마나 남았나요?", "修改模型能力": "모델 기능 수정", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 대화 모델을 아직 저장할 수 없습니다. 입력창 아래의 안내를 확인하세요.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 아직 메시지를 보낼 수 없습니다. 입력창 아래의 안내를 확인하세요.", "发起模型测试": "모델 테스트 실행", "启用 / 禁用模型": "모델 활성화 / 비활성화", "启用 / 禁用账号": "계정 활성화 / 비활성화", "对指定模型发一次真实请求,回报连通性与耗时。": "지정한 모델에 실제 요청을 한 번 보내 연결성과 소요 시간을 보고합니다.", "开关模型的对外可用性,不改其它属性。": "모델의 외부 가용성만 전환하고 다른 속성은 변경하지 않습니다.", "把重复的模型加上家名前缀区分开": "중복된 모델에 제공자 이름 접두사를 붙여 구분하기", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "컨텍스트 창 / 최대 출력 토큰 / 도구 호출 등의 기능 플래그를 수정합니다.", "改某一家的整组顺位(先试哪一家)。": "특정 제공자의 그룹 전체 순위를 변경합니다(어느 제공자를 먼저 시도할지).", "改账号在全局队列里的名次,决定主备与切换顺序。": "전역 큐에서 계정 순위를 변경해 주/예비와 전환 순서를 결정합니다.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "매핑 이름 추가 / 삭제. 일괄 접두사 추가와 같은 이름을 여러 제공자에 통합하는 것을 지원합니다.", "查询账号余额": "계정 잔액 조회", "没有可用的模型": "사용 가능한 모델이 없습니다", "测一下当前最快的模型通不通": "현재 가장 빠른 모델이 연결되는지 테스트하기", "深度推理": "심층 추론", "看看哪些模型在多家重复了": "여러 제공자에서 중복된 모델 확인하기", "禁用会把账号移出转发队列,线路可能因此断供。": "비활성화하면 계정이 포워딩 큐에서 빠지며, 이 경로가 끊길 수 있습니다.", "获取模型列表": "모델 목록 조회", "设置模型映射 ID": "모델 매핑 ID 설정", "读各提供商的模型清单、映射名与启用状态。": "각 제공자의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各账号的额度 / 剩余用量。": "각 계정의 할당량 / 남은 사용량을 읽습니다.", "调整提供商顺位": "제공자 순위 조정", "调整账号优先级": "계정 우선순위 조정", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "이 버전의 게이트웨이에는 AI 어시스턴트 API가 아직 없습니다 (HTTP 404): 백엔드가 오래되었습니다. 다시 빌드하거나 클라이언트를 재시작한 후 시도하세요. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "이번 턴이 왕복 {n}회 한도에 도달해 지시를 기다리며 멈췄습니다(다르게 표현하거나 몇 단계로 나눠 주세요).", "连接网关失败({base}):{message}": "게이트웨이 연결 실패 ({base}): {message}", "配置响应不完整": "구성 응답이 불완전합니다" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts 계정 잔액이 얼마나 남았나요?", "{n} 家 · {m} 个模型": "{n}개 제공자 · {m}개 모델", "修改模型能力": "모델 기능 수정", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 대화 모델을 아직 저장할 수 없습니다. 입력창 아래의 안내를 확인하세요.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 아직 메시지를 보낼 수 없습니다. 입력창 아래의 안내를 확인하세요.", "发起模型测试": "모델 테스트 실행", "启用 / 禁用模型": "모델 활성화 / 비활성화", "启用 / 禁用账号": "계정 활성화 / 비활성화", "对指定模型发一次真实请求,回报连通性与耗时。": "지정한 모델에 실제 요청을 한 번 보내 연결성과 소요 시간을 보고합니다.", "开关模型的对外可用性,不改其它属性。": "모델의 외부 가용성만 전환하고 다른 속성은 변경하지 않습니다.", "把重复的模型加上家名前缀区分开": "중복된 모델에 제공자 이름 접두사를 붙여 구분하기", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "컨텍스트 창 / 최대 출력 토큰 / 도구 호출 등의 기능 플래그를 수정합니다.", "改某一家的整组顺位(先试哪一家)。": "특정 제공자의 그룹 전체 순위를 변경합니다(어느 제공자를 먼저 시도할지).", "改账号在全局队列里的名次,决定主备与切换顺序。": "전역 큐에서 계정 순위를 변경해 주/예비와 전환 순서를 결정합니다.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "매핑 이름 추가 / 삭제. 일괄 접두사 추가와 같은 이름을 여러 제공자에 통합하는 것을 지원합니다.", "查询账号余额": "계정 잔액 조회", "没有可用的模型": "사용 가능한 모델이 없습니다", "测一下当前最快的模型通不通": "현재 가장 빠른 모델이 연결되는지 테스트하기", "深度推理": "심층 추론", "看看哪些模型在多家重复了": "여러 제공자에서 중복된 모델 확인하기", "禁用会把账号移出转发队列,线路可能因此断供。": "비활성화하면 계정이 포워딩 큐에서 빠지며, 이 경로가 끊길 수 있습니다.", "获取模型列表": "모델 목록 조회", "设置模型映射 ID": "모델 매핑 ID 설정", "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "모든 제공자(사용자 지정 포함)의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各提供商的模型清单、映射名与启用状态。": "각 제공자의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各账号的额度 / 剩余用量。": "각 계정의 할당량 / 남은 사용량을 읽습니다.", "调整提供商顺位": "제공자 순위 조정", "调整账号优先级": "계정 우선순위 조정", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "이 버전의 게이트웨이에는 AI 어시스턴트 API가 아직 없습니다 (HTTP 404): 백엔드가 오래되었습니다. 다시 빌드하거나 클라이언트를 재시작한 후 시도하세요. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "이번 턴이 왕복 {n}회 한도에 도달해 지시를 기다리며 멈췄습니다(다르게 표현하거나 몇 단계로 나눠 주세요).", "连接网关失败({base}):{message}": "게이트웨이 연결 실패 ({base}): {message}", "配置响应不完整": "구성 응답이 불완전합니다" }); diff --git a/desktop-tauri/ui/i18n/pt-BR/assistant.js b/desktop-tauri/ui/i18n/pt-BR/assistant.js index 7e06eef7..39e89800 100644 --- a/desktop-tauri/ui/i18n/pt-BR/assistant.js +++ b/desktop-tauri/ui/i18n/pt-BR/assistant.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:pt-BR/assistant —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "Quanto crédito ainda resta na conta CodeArts?", "修改模型能力": "Editar capacidades do modelo", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então o modelo de chat ainda não pode ser salvo. Veja a dica abaixo da caixa de entrada.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então ainda não é possível enviar mensagens. Veja a dica abaixo da caixa de entrada.", "发起模型测试": "Executar teste de modelo", "启用 / 禁用模型": "Habilitar / desabilitar modelos", "启用 / 禁用账号": "Habilitar / desabilitar contas", "对指定模型发一次真实请求,回报连通性与耗时。": "Envia uma requisição real ao modelo indicado e informa conectividade e latência.", "开关模型的对外可用性,不改其它属性。": "Alterna a disponibilidade externa do modelo sem alterar outros atributos.", "把重复的模型加上家名前缀区分开": "Prefixar modelos duplicados com o nome do provedor para diferenciá-los", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edita flags de capacidade como janela de contexto, máximo de tokens de saída e chamadas de ferramentas.", "改某一家的整组顺位(先试哪一家)。": "Reordena o grupo inteiro de um provedor (qual deles é tentado primeiro).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Altera a posição da conta na fila global, definindo titular/reserva e ordem de failover.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adiciona/remove nomes de mapeamento, com prefixação em lote e fusão de um mesmo nome em vários provedores.", "查询账号余额": "Consultar saldo da conta", "没有可用的模型": "Nenhum modelo disponível", "测一下当前最快的模型通不通": "Testar se o modelo mais rápido no momento responde", "深度推理": "Raciocínio profundo", "看看哪些模型在多家重复了": "Ver quais modelos se repetem em vários provedores", "禁用会把账号移出转发队列,线路可能因此断供。": "Desabilitar remove a conta da fila de encaminhamento; a rota pode ficar sem fornecimento.", "获取模型列表": "Listar modelos", "设置模型映射 ID": "Definir ID de mapeamento do modelo", "读各提供商的模型清单、映射名与启用状态。": "Lê a lista de modelos, os nomes de mapeamento e o estado de cada provedor.", "读各账号的额度 / 剩余用量。": "Lê a cota e o uso restante de cada conta.", "调整提供商顺位": "Ajustar ordem dos provedores", "调整账号优先级": "Ajustar prioridade da conta", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "Esta versão do gateway ainda não tem a API do assistente de IA (HTTP 404): o backend está desatualizado. Recompile / reinicie o cliente e tente de novo. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "Este turno atingiu o limite de {n} idas e voltas, então parei para aguardar sua instrução (reformule, ou divida em algumas etapas).", "连接网关失败({base}):{message}": "Falha ao conectar ao gateway ({base}): {message}", "配置响应不完整": "Resposta de configuração incompleta" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "Quanto crédito ainda resta na conta CodeArts?", "{n} 家 · {m} 个模型": "{n} provedores · {m} modelos", "修改模型能力": "Editar capacidades do modelo", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então o modelo de chat ainda não pode ser salvo. Veja a dica abaixo da caixa de entrada.", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então ainda não é possível enviar mensagens. Veja a dica abaixo da caixa de entrada.", "发起模型测试": "Executar teste de modelo", "启用 / 禁用模型": "Habilitar / desabilitar modelos", "启用 / 禁用账号": "Habilitar / desabilitar contas", "对指定模型发一次真实请求,回报连通性与耗时。": "Envia uma requisição real ao modelo indicado e informa conectividade e latência.", "开关模型的对外可用性,不改其它属性。": "Alterna a disponibilidade externa do modelo sem alterar outros atributos.", "把重复的模型加上家名前缀区分开": "Prefixar modelos duplicados com o nome do provedor para diferenciá-los", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edita flags de capacidade como janela de contexto, máximo de tokens de saída e chamadas de ferramentas.", "改某一家的整组顺位(先试哪一家)。": "Reordena o grupo inteiro de um provedor (qual deles é tentado primeiro).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Altera a posição da conta na fila global, definindo titular/reserva e ordem de failover.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adiciona/remove nomes de mapeamento, com prefixação em lote e fusão de um mesmo nome em vários provedores.", "查询账号余额": "Consultar saldo da conta", "没有可用的模型": "Nenhum modelo disponível", "测一下当前最快的模型通不通": "Testar se o modelo mais rápido no momento responde", "深度推理": "Raciocínio profundo", "看看哪些模型在多家重复了": "Ver quais modelos se repetem em vários provedores", "禁用会把账号移出转发队列,线路可能因此断供。": "Desabilitar remove a conta da fila de encaminhamento; a rota pode ficar sem fornecimento.", "获取模型列表": "Listar modelos", "设置模型映射 ID": "Definir ID de mapeamento do modelo", "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "Lê a lista de modelos de todos os provedores (incluindo personalizados), nomes de mapeamento e estado.", "读各提供商的模型清单、映射名与启用状态。": "Lê a lista de modelos, os nomes de mapeamento e o estado de cada provedor.", "读各账号的额度 / 剩余用量。": "Lê a cota e o uso restante de cada conta.", "调整提供商顺位": "Ajustar ordem dos provedores", "调整账号优先级": "Ajustar prioridade da conta", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "Esta versão do gateway ainda não tem a API do assistente de IA (HTTP 404): o backend está desatualizado. Recompile / reinicie o cliente e tente de novo. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "Este turno atingiu o limite de {n} idas e voltas, então parei para aguardar sua instrução (reformule, ou divida em algumas etapas).", "连接网关失败({base}):{message}": "Falha ao conectar ao gateway ({base}): {message}", "配置响应不完整": "Resposta de configuração incompleta" }); diff --git a/desktop-tauri/ui/i18n/zh-Hant/assistant.js b/desktop-tauri/ui/i18n/zh-Hant/assistant.js index 8d7553d6..5ce05553 100644 --- a/desktop-tauri/ui/i18n/zh-Hant/assistant.js +++ b/desktop-tauri/ui/i18n/zh-Hant/assistant.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:zh-Hant/assistant —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts 的帳號餘額還剩多少?", "修改模型能力": "修改模型能力", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "助手設定沒讀出來,暫時存不了對話模型 —— 看輸入框下方的提示。", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "助手設定沒讀出來,現在還不能發訊息 —— 看輸入框下方的提示。", "发起模型测试": "發起模型測試", "启用 / 禁用模型": "啟用 / 停用模型", "启用 / 禁用账号": "啟用 / 停用帳號", "对指定模型发一次真实请求,回报连通性与耗时。": "對指定模型發一次真實請求,回報連通性與耗時。", "开关模型的对外可用性,不改其它属性。": "開關模型的對外可用性,不改其它屬性。", "把重复的模型加上家名前缀区分开": "把重複的模型加上家名字首區分開", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "改上下文視窗 / 最大輸出 Token / 工具呼叫等能力位。", "改某一家的整组顺位(先试哪一家)。": "改某一家的整組順位(先試哪一家)。", "改账号在全局队列里的名次,决定主备与切换顺序。": "改帳號在全域佇列裡的名次,決定主備與切換順序。", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "新增 / 刪除對映名,支援批次加字首與同名合併到多家。", "查询账号余额": "查詢帳號餘額", "没有可用的模型": "沒有可用的模型", "测一下当前最快的模型通不通": "測一下當前最快的模型通不通", "深度推理": "深度推理", "看看哪些模型在多家重复了": "看看哪些模型在多家重複了", "禁用会把账号移出转发队列,线路可能因此断供。": "停用會把帳號移出轉發佇列,線路可能因此斷供。", "获取模型列表": "獲取模型列表", "设置模型映射 ID": "設定模型對映 ID", "读各提供商的模型清单、映射名与启用状态。": "讀各提供商的模型清單、對映名與啟用狀態。", "读各账号的额度 / 剩余用量。": "讀各帳號的額度 / 剩餘用量。", "调整提供商顺位": "調整提供商順位", "调整账号优先级": "調整帳號優先順序", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "這一版閘道器還沒有 AI 助手介面(HTTP 404):後端還是舊的,重新編譯 / 重啟客戶端後再試。{message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "這一輪來回已達 {n} 次上限,我先停下等你指示(可以換個說法,或拆成幾步讓我做)。", "连接网关失败({base}):{message}": "連線閘道器失敗({base}):{message}", "配置响应不完整": "配置響應不完整" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { "CodeArts 的账号余额还剩多少?": "CodeArts 的帳號餘額還剩多少?", "{n} 家 · {m} 个模型": "{n} 家 · {m} 個模型", "修改模型能力": "修改模型能力", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "助手設定沒讀出來,暫時存不了對話模型 —— 看輸入框下方的提示。", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "助手設定沒讀出來,現在還不能發訊息 —— 看輸入框下方的提示。", "发起模型测试": "發起模型測試", "启用 / 禁用模型": "啟用 / 停用模型", "启用 / 禁用账号": "啟用 / 停用帳號", "对指定模型发一次真实请求,回报连通性与耗时。": "對指定模型發一次真實請求,回報連通性與耗時。", "开关模型的对外可用性,不改其它属性。": "開關模型的對外可用性,不改其它屬性。", "把重复的模型加上家名前缀区分开": "把重複的模型加上家名字首區分開", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "改上下文視窗 / 最大輸出 Token / 工具呼叫等能力位。", "改某一家的整组顺位(先试哪一家)。": "改某一家的整組順位(先試哪一家)。", "改账号在全局队列里的名次,决定主备与切换顺序。": "改帳號在全域佇列裡的名次,決定主備與切換順序。", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "新增 / 刪除對映名,支援批次加字首與同名合併到多家。", "查询账号余额": "查詢帳號餘額", "没有可用的模型": "沒有可用的模型", "测一下当前最快的模型通不通": "測一下當前最快的模型通不通", "深度推理": "深度推理", "看看哪些模型在多家重复了": "看看哪些模型在多家重複了", "禁用会把账号移出转发队列,线路可能因此断供。": "停用會把帳號移出轉發佇列,線路可能因此斷供。", "获取模型列表": "獲取模型列表", "设置模型映射 ID": "設定模型對映 ID", "读全部提供商(含自定义家)的模型清单、映射名与启用状态。": "讀取全部提供商(含自訂家)的模型清單、對映名與啟用狀態。", "读各提供商的模型清单、映射名与启用状态。": "讀各提供商的模型清單、對映名與啟用狀態。", "读各账号的额度 / 剩余用量。": "讀各帳號的額度 / 剩餘用量。", "调整提供商顺位": "調整提供商順位", "调整账号优先级": "調整帳號優先順序", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "這一版閘道器還沒有 AI 助手介面(HTTP 404):後端還是舊的,重新編譯 / 重啟客戶端後再試。{message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "這一輪來回已達 {n} 次上限,我先停下等你指示(可以換個說法,或拆成幾步讓我做)。", "连接网关失败({base}):{message}": "連線閘道器失敗({base}):{message}", "配置响应不完整": "配置響應不完整" }); diff --git a/desktop-tauri/ui/index.html b/desktop-tauri/ui/index.html index 9e8fdc8d..1aab42b9 100644 --- a/desktop-tauri/ui/index.html +++ b/desktop-tauri/ui/index.html @@ -95,6 +95,11 @@ 排在最后:工具类带 !important(原因见 ui-kit/styles/globals.css 顶部), 顺序其实已不影响结果,但保持「后加载的是更新的体系」这条阅读线索。 --> + +