@raingor/pi-web-switch 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.css CHANGED
@@ -188,7 +188,11 @@ body {
188
188
  .light .bg-amber-500\/10 {
189
189
  background-color: #fffbeb;
190
190
  }
191
- .light .hover\:bg-gray-800 {
191
+ .light .hover\:bg-gray-800,
192
+ .light .hover\:bg-gray-800\/70,
193
+ .light .hover\:bg-gray-800\/60,
194
+ .light .hover\:bg-gray-800\/40,
195
+ .light .hover\:bg-gray-800\/30 {
192
196
  &:hover {
193
197
  background-color: #e5e7eb;
194
198
  }
package/src/lib/config.ts CHANGED
@@ -4,6 +4,19 @@
4
4
 
5
5
  import type { PiConfig, ExportPayload } from "@/types";
6
6
 
7
+ // ─── File System Access API type shims ──────────────────
8
+ // These APIs are Chromium-only; we gracefully fall back.
9
+
10
+ declare global {
11
+ interface Window {
12
+ showDirectoryPicker?(): Promise<FileSystemDirectoryHandle>;
13
+ showOpenFilePicker?(options?: {
14
+ types?: { description?: string; accept: Record<string, string[]> }[];
15
+ multiple?: boolean;
16
+ }): Promise<FileSystemFileHandle[]>;
17
+ }
18
+ }
19
+
7
20
  const STORAGE_KEY = "pi-web-switch-config";
8
21
 
9
22
  export function saveLocalBackup(config: PiConfig): void {
@@ -39,6 +52,54 @@ export function exportConfig(config: PiConfig, filename?: string): void {
39
52
  URL.revokeObjectURL(url);
40
53
  }
41
54
 
55
+ export async function exportConfigToDirectory(config: PiConfig): Promise<{ ok: boolean; cancelled: boolean }> {
56
+ if (!("showDirectoryPicker" in window)) return { ok: false, cancelled: false };
57
+ try {
58
+ const dirHandle = await window.showDirectoryPicker!();
59
+ const fileName = `pi-web-switch-config-${new Date().toISOString().split("T")[0]}.json`;
60
+ const fileHandle = await dirHandle.getFileHandle(fileName, { create: true });
61
+ const writable = await fileHandle.createWritable();
62
+ const payload: ExportPayload = {
63
+ version: "0.1.0",
64
+ exportedAt: new Date().toISOString(),
65
+ config,
66
+ };
67
+ await writable.write(JSON.stringify(payload, null, 2));
68
+ await writable.close();
69
+ return { ok: true, cancelled: false };
70
+ } catch (err) {
71
+ if (err instanceof DOMException && err.name === "AbortError") {
72
+ return { ok: false, cancelled: true };
73
+ }
74
+ return { ok: false, cancelled: false };
75
+ }
76
+ }
77
+
78
+ export async function importConfigFromFile(): Promise<{ config: PiConfig | null; cancelled: boolean }> {
79
+ if (!("showOpenFilePicker" in window)) return { config: null, cancelled: false };
80
+ try {
81
+ const handles = await window.showOpenFilePicker!({
82
+ types: [
83
+ {
84
+ description: "JSON Files",
85
+ accept: { "application/json": [".json"] },
86
+ },
87
+ ],
88
+ multiple: false,
89
+ });
90
+ const fileHandle = handles[0];
91
+ if (!fileHandle) return { config: null, cancelled: false };
92
+ const file = await fileHandle.getFile();
93
+ const text = await file.text();
94
+ return { config: parseImportFile(text), cancelled: false };
95
+ } catch (err) {
96
+ if (err instanceof DOMException && err.name === "AbortError") {
97
+ return { config: null, cancelled: true };
98
+ }
99
+ return { config: null, cancelled: false };
100
+ }
101
+ }
102
+
42
103
  export function parseImportFile(json: string): PiConfig | null {
43
104
  try {
44
105
  const payload = JSON.parse(json) as ExportPayload;
@@ -7,6 +7,7 @@ const en: Record<string, string> = {
7
7
  "nav.models": "Models",
8
8
  "nav.providers": "Providers",
9
9
  "nav.providers_models": "Providers & Models",
10
+ "nav.subagents": "Subagents",
10
11
  "providers_models.subtitle": "Manage model providers and models. Changes are written to ~/.pi/agent/.",
11
12
  "providers_models.custom_providers": "Custom Providers",
12
13
  "providers_models.model_list": "Model List",
@@ -23,8 +24,18 @@ const en: Record<string, string> = {
23
24
  "providers_models.test_ok": "Connected ({0} ms)",
24
25
  "providers_models.test_fail": "Failed: {0}",
25
26
  "providers_models.invalid_url": "Enter a valid http(s) URL",
27
+ "providers_models.baseurl_override": "This URL overrides the built-in default (saved to models.json)",
26
28
  "providers_models.enable_all": "Enable All",
29
+ "providers_models.sort_by": "Sort",
30
+ "providers_models.sort_default": "Default",
31
+ "providers_models.sort_family": "By family",
32
+ "providers_models.sort_price_asc": "Price ↑",
33
+ "providers_models.sort_price_desc": "Price ↓",
27
34
  "providers_models.disable_all": "Disable All",
35
+ "providers_models.enabled_models_title": "Enabled Models",
36
+ "providers_models.no_enabled_models": "No enabled models yet. Enable models in a provider's model list and they'll appear here.",
37
+ "providers_models.api_key_env": "Uses the value of environment variable {0} as the key (not stored in plaintext)",
38
+ "providers_models.test_all": "Test All",
28
39
  "providers_models.id_exists": "ID “{0}” already exists, pick another name",
29
40
  "providers_models.id_preview": "Will be saved as ID “{0}”",
30
41
  "providers_models.import": "Import",
@@ -33,13 +44,61 @@ const en: Record<string, string> = {
33
44
  "providers_models.import_placeholder": "tokenrouter baseurl: https://api.example.com/v1 key: sk-xxxx\nmodelid: vendor/model-name",
34
45
  "providers_models.import_empty": "No valid content detected in the text",
35
46
  "providers_models.import_merge": "Provider “{0}” already exists — models will be merged into it",
36
- "providers_models.import_builtin_conflict": "Conflicts with builtin provider “{0}” — please change the name",
47
+ "providers_models.import_builtin_conflict": "Conflicts with builtin provider “{0}” — please change the name",
48
+ "providers_models.fetch_models": "Fetch Models",
49
+ "providers_models.fetch_no_endpoint": "This provider has no usable /models endpoint",
50
+ "providers_models.fetching": "Fetching...",
51
+ "providers_models.fetch_error": "Failed: {0}",
52
+ "providers_models.fetch_empty": "No models returned by this provider",
53
+ "providers_models.modality_vision": "Vision",
54
+ "providers_models.modality_text": "Text",
55
+ "providers_models.id_invalid": "Cannot derive a valid ID from the name or URL — fill in the base URL or use an ASCII name",
56
+ "providers_models.fetch_title": "Import Models",
57
+ "providers_models.fetch_desc": "Fetch available models from {0}'s /models endpoint and select models to import.",
58
+ "providers_models.select_all": "Select All",
59
+ "providers_models.deselect_all": "Deselect All",
60
+ "providers_models.selected_count": "{0}/{1} models selected",
61
+ "providers_models.confirm_import": "Import Selected",
62
+
63
+ "providers_models.import_done": "Imported {0} models",
64
+
37
65
  "nav.sessions": "Sessions",
38
66
  "nav.memory": "Memory",
39
67
  "nav.settings": "Settings",
40
68
  "app.subtitle": "Configuration Manager",
41
69
  "app.version": "pi-switch v0.1.0",
42
70
 
71
+ // Subagents
72
+ "subagents.summary": "{0} agents · {1} chains",
73
+ "subagents.tab_agents": "Agents",
74
+ "subagents.tab_chains": "Chains",
75
+ "subagents.tab_history": "Run History",
76
+ "subagents.no_agents": "No agents found",
77
+ "subagents.no_agents_desc": "Create agent .md files in ~/.pi/agent/agents/ to get started.",
78
+ "subagents.no_chains": "No chains found",
79
+ "subagents.no_chains_desc": "Create chain .chain.md files in ~/.pi/agent/chains/ to get started.",
80
+ "subagents.no_history": "No run history",
81
+ "subagents.no_history_desc": "Subagent run history will appear here after agents are executed.",
82
+ "subagents.model": "Model",
83
+ "subagents.thinking": "Thinking Level",
84
+ "subagents.tools": "Tools",
85
+ "subagents.system_prompt_mode": "System Prompt Mode",
86
+ "subagents.input": "Input Types",
87
+ "subagents.system_prompt": "System Prompt",
88
+ "subagents.empty_prompt": "(empty)",
89
+ "subagents.steps_count": "steps",
90
+ "subagents.pipeline": "Pipeline",
91
+ "subagents.phase": "Phase",
92
+ "subagents.label": "Label",
93
+ "subagents.output": "Output",
94
+ "subagents.agent": "Agent",
95
+ "subagents.time": "Time",
96
+ "subagents.status": "Status",
97
+ "subagents.duration": "Duration",
98
+ "subagents.status_ok": "OK",
99
+ "subagents.status_error": "Error",
100
+ "subagents.showing_recent": "Showing 100 most recent records",
101
+
43
102
  // Dashboard
44
103
  "dashboard.title": "Usage Statistics",
45
104
  "dashboard.requests_count": "{0} requests · {1} total cost",
@@ -90,6 +149,8 @@ const en: Record<string, string> = {
90
149
  // Models
91
150
  "models.search_placeholder": "Search models...",
92
151
  "models.add_model": "Add Model",
152
+
153
+ "models.test_model": "Test Model",
93
154
  "models.edit_model": "Edit Model",
94
155
  "models.delete_model": "Delete Model",
95
156
  "models.no_models": "No models found",
@@ -109,10 +170,36 @@ const en: Record<string, string> = {
109
170
  "models.save": "Save",
110
171
  "models.cancel": "Cancel",
111
172
  "models.delete_confirm": "Are you sure you want to delete this model?",
173
+ "models.pick_preset": "Pick from catalog",
174
+ "models.pick_preset_placeholder": "Search popular models (Claude, GPT, Qwen, GLM, DeepSeek…)",
175
+ "models.detected_catalog": "Matched: {0}",
176
+ "models.detected_heuristic": "Guessed from model ID",
177
+ "models.manual_fill": "Manual",
178
+ "models.preset_claude": "Claude-style",
179
+ "models.preset_gpt": "GPT-style",
180
+ "models.preset_reasoning": "Reasoning",
181
+ "models.preset_small_local": "Local small",
182
+ "models.audio_input": "Audio input",
183
+ "models.quick_add": "Quick add",
184
+ "models.quick_add_placeholder": "Type a model ID and press Enter (params auto-detected)",
185
+ "models.copy_params": "Copy params",
186
+ "models.params_copied": "Params copied",
187
+ "models.catalog_hint": "Can't find your model? Just type the ID below — common parameters are detected automatically.",
188
+
189
+ // Compat
190
+ "compat.title": "API Compatibility",
191
+ "compat.desc": "Override default behavior for OpenAI-compatible providers that don't support certain features.",
192
+ "compat.supports_developer_role": "Supports developer role",
193
+ "compat.supports_developer_role_desc": "Send system prompt as 'developer' role (for reasoning models). Disable if the API only accepts 'system' role.",
194
+ "compat.supports_reasoning_effort": "Supports reasoning_effort",
195
+ "compat.supports_reasoning_effort_desc": "Send reasoning_effort parameter. Disable if the API does not support it.",
112
196
 
113
197
  // Providers
114
198
  "providers.add_provider": "Add Provider",
115
199
  "providers.delete_provider": "Delete Provider",
200
+ "providers_models.expand": "Expand ({0} more)",
201
+ "providers_models.collapse": "Collapse",
202
+ "providers.duplicate_provider": "Duplicate Provider",
116
203
  "providers.custom": "Custom",
117
204
  "providers.builtin": "Built-in",
118
205
  "providers.configured": "Configured",
@@ -199,6 +286,9 @@ const en: Record<string, string> = {
199
286
  "settings.theme_desc": "Pick a color scheme for the interface",
200
287
  "settings.font_size": "Font Size",
201
288
  "settings.font_size_desc": "Adjust the overall interface font size",
289
+ "settings.ui_zoom": "Interface Zoom",
290
+ "settings.ui_zoom_desc": "Scale the entire interface by percentage (50%–200%)",
291
+ "settings.ui_zoom_reset": "Reset",
202
292
  "settings.enabled_models_desc": "Click a tag to enable or disable a model",
203
293
  "settings.updates_title": "Update Check",
204
294
  "settings.updates_desc": "Check npm for new versions of pi core and installed extensions.",
@@ -5,6 +5,7 @@ const ja: Record<string, string> = {
5
5
  "nav.models": "モデル",
6
6
  "nav.providers": "プロバイダー",
7
7
  "nav.providers_models": "プロバイダーとモデル",
8
+ "nav.subagents": "サブエージェント",
8
9
  "providers_models.subtitle": "モデルプロバイダーとモデルを管理します。設定は ~/.pi/agent/ に保存されます。",
9
10
  "providers_models.custom_providers": "カスタムプロバイダー",
10
11
  "providers_models.model_list": "モデルリスト",
@@ -21,8 +22,18 @@ const ja: Record<string, string> = {
21
22
  "providers_models.test_ok": "接続成功 ({0} ms)",
22
23
  "providers_models.test_fail": "接続失敗:{0}",
23
24
  "providers_models.invalid_url": "有効な http(s) URL を入力してください",
25
+ "providers_models.baseurl_override": "この URL は内蔵デフォルトを上書きします(models.json に保存)",
24
26
  "providers_models.enable_all": "すべて有効化",
27
+ "providers_models.sort_by": "並び順",
28
+ "providers_models.sort_default": "デフォルト",
29
+ "providers_models.sort_family": "ファミリー順",
30
+ "providers_models.sort_price_asc": "価格昇順",
31
+ "providers_models.sort_price_desc": "価格降順",
25
32
  "providers_models.disable_all": "すべて無効化",
33
+ "providers_models.enabled_models_title": "有効なモデル",
34
+ "providers_models.no_enabled_models": "有効なモデルはまだありません。プロバイダーのモデルリストでモデルを有効にすると、ここに表示されます。",
35
+ "providers_models.api_key_env": "環境変数 {0} の値をキーとして使用します(平文保存されません)",
36
+ "providers_models.test_all": "すべてテスト",
26
37
  "providers_models.id_exists": "ID “{0}” は既に存在します。別の名前を使用してください",
27
38
  "providers_models.id_preview": "ID “{0}” として保存されます",
28
39
  "providers_models.import": "インポート",
@@ -31,13 +42,61 @@ const ja: Record<string, string> = {
31
42
  "providers_models.import_placeholder": "tokenrouter baseurl: https://api.example.com/v1 key: sk-xxxx\nmodelid: vendor/model-name",
32
43
  "providers_models.import_empty": "テキストから有効な内容を認識できませんでした",
33
44
  "providers_models.import_merge": "プロバイダー “{0}” は既に存在します。モデルをマージします",
34
- "providers_models.import_builtin_conflict": "組み込みプロバイダー “{0}” と競合しています。名前を変更してください",
45
+ "providers_models.import_builtin_conflict": "組み込みプロバイダー “{0}” と競合しています。名前を変更してください",
46
+ "providers_models.fetch_models": "モデルを取得",
47
+ "providers_models.fetch_no_endpoint": "このプロバイダーには利用可能な /models エンドポイントがありません",
48
+ "providers_models.fetching": "取得中...",
49
+ "providers_models.fetch_error": "エラー:{0}",
50
+ "providers_models.fetch_empty": "モデルが返されませんでした",
51
+ "providers_models.modality_vision": "画像対応",
52
+ "providers_models.modality_text": "テキスト",
53
+ "providers_models.id_invalid": "名前または URL から有効な ID を生成できません。ベース URL を入力するか英字の名前を使用してください",
54
+ "providers_models.fetch_title": "モデルをインポート",
55
+ "providers_models.fetch_desc": "{0} の /models エンドポイントからモデルを取得し、インポートするモデルを選択します。",
56
+ "providers_models.select_all": "すべて選択",
57
+ "providers_models.deselect_all": "選択解除",
58
+ "providers_models.selected_count": "{0}/{1} モデルを選択",
59
+ "providers_models.confirm_import": "選択モデルをインポート",
60
+
61
+ "providers_models.import_done": "{0} モデルをインポートしました",
62
+
35
63
  "nav.sessions": "セッション",
36
64
  "nav.memory": "メモリ",
37
65
  "nav.settings": "設定",
38
66
  "app.subtitle": "設定マネージャー",
39
67
  "app.version": "pi-switch v0.1.0",
40
68
 
69
+ // Subagents
70
+ "subagents.summary": "{0} エージェント · {1} チェーン",
71
+ "subagents.tab_agents": "エージェント",
72
+ "subagents.tab_chains": "チェーン",
73
+ "subagents.tab_history": "実行履歴",
74
+ "subagents.no_agents": "エージェントがありません",
75
+ "subagents.no_agents_desc": "~/.pi/agent/agents/ に .md ファイルを作成してください。",
76
+ "subagents.no_chains": "チェーンがありません",
77
+ "subagents.no_chains_desc": "~/.pi/agent/chains/ に .chain.md ファイルを作成してください。",
78
+ "subagents.no_history": "実行履歴がありません",
79
+ "subagents.no_history_desc": "エージェントを実行すると、履歴がここに表示されます。",
80
+ "subagents.model": "モデル",
81
+ "subagents.thinking": "思考レベル",
82
+ "subagents.tools": "ツール",
83
+ "subagents.system_prompt_mode": "システムプロンプトモード",
84
+ "subagents.input": "入力タイプ",
85
+ "subagents.system_prompt": "システムプロンプト",
86
+ "subagents.empty_prompt": "(空)",
87
+ "subagents.steps_count": "ステップ",
88
+ "subagents.pipeline": "パイプライン",
89
+ "subagents.phase": "フェーズ",
90
+ "subagents.label": "ラベル",
91
+ "subagents.output": "出力",
92
+ "subagents.agent": "エージェント",
93
+ "subagents.time": "時間",
94
+ "subagents.status": "状態",
95
+ "subagents.duration": "所要時間",
96
+ "subagents.status_ok": "成功",
97
+ "subagents.status_error": "エラー",
98
+ "subagents.showing_recent": "最新 100 件を表示",
99
+
41
100
  "dashboard.title": "使用統計",
42
101
  "dashboard.requests_count": "{0} リクエスト · 総コスト {1}",
43
102
  "dashboard.range.today": "今日",
@@ -86,6 +145,8 @@ const ja: Record<string, string> = {
86
145
 
87
146
  "models.search_placeholder": "モデルを検索...",
88
147
  "models.add_model": "モデル追加",
148
+
149
+ "models.test_model": "モデルをテスト",
89
150
  "models.edit_model": "モデル編集",
90
151
  "models.delete_model": "モデル削除",
91
152
  "models.no_models": "モデルが見つかりません",
@@ -105,9 +166,35 @@ const ja: Record<string, string> = {
105
166
  "models.save": "保存",
106
167
  "models.cancel": "キャンセル",
107
168
  "models.delete_confirm": "このモデルを削除してもよろしいですか?",
169
+ "models.pick_preset": "カタログから選ぶ",
170
+ "models.pick_preset_placeholder": "人気モデルを検索(Claude、GPT、Qwen、GLM、DeepSeek…)",
171
+ "models.detected_catalog": "一致: {0}",
172
+ "models.detected_heuristic": "ID から推測されたパラメータ",
173
+ "models.manual_fill": "手動",
174
+ "models.preset_claude": "Claude スタイル",
175
+ "models.preset_gpt": "GPT スタイル",
176
+ "models.preset_reasoning": "推論モデル",
177
+ "models.preset_small_local": "ローカル小型",
178
+ "models.audio_input": "音声入力",
179
+ "models.quick_add": "クイック追加",
180
+ "models.quick_add_placeholder": "モデル ID を入力して Enter(パラメータは自動検出)",
181
+ "models.copy_params": "パラメータをコピー",
182
+ "models.params_copied": "コピーしました",
183
+ "models.catalog_hint": "見つからない場合は ID を直接入力してください。一般的なパラメータは自動検出されます。",
184
+
185
+ // Compat
186
+ "compat.title": "API 互換性",
187
+ "compat.desc": "OpenAI 互換 API が一部の機能をサポートしていない場合の動作を上書きします。",
188
+ "compat.supports_developer_role": "developer ロールをサポート",
189
+ "compat.supports_developer_role_desc": "システムプロンプトを 'developer' ロールで送信(推論モデル)。API が 'system' ロールのみ受け付ける場合は無効にしてください。",
190
+ "compat.supports_reasoning_effort": "reasoning_effort をサポート",
191
+ "compat.supports_reasoning_effort_desc": "reasoning_effort パラメータを送信します。API がサポートしていない場合は無効にしてください。",
108
192
 
109
193
  "providers.add_provider": "プロバイダー追加",
110
194
  "providers.delete_provider": "プロバイダー削除",
195
+ "providers_models.expand": "さらに {0} 件を表示",
196
+ "providers_models.collapse": "折りたたむ",
197
+ "providers.duplicate_provider": "プロバイダー複製",
111
198
  "providers.custom": "カスタム",
112
199
  "providers.builtin": "ビルトイン",
113
200
  "providers.configured": "設定済み",
@@ -191,6 +278,9 @@ const ja: Record<string, string> = {
191
278
  "settings.theme_desc": "インターフェースの配色を選択",
192
279
  "settings.font_size": "フォントサイズ",
193
280
  "settings.font_size_desc": "インターフェース全体のフォントサイズを調整",
281
+ "settings.ui_zoom": "インターフェース拡大",
282
+ "settings.ui_zoom_desc": "インターフェース全体をパーセントで拡大縮小(50%–200%)",
283
+ "settings.ui_zoom_reset": "リセット",
194
284
  "settings.enabled_models_desc": "タグをクリックしてモデルを切り替え",
195
285
  "settings.updates_title": "アップデート確認",
196
286
  "settings.updates_desc": "pi 本体と拡張に npm 上の新バージョンがあるか確認します。",
@@ -5,6 +5,7 @@ const zhCN: Record<string, string> = {
5
5
  "nav.models": "模型",
6
6
  "nav.providers": "提供商",
7
7
  "nav.providers_models": "提供商与模型",
8
+ "nav.subagents": "子代理",
8
9
  "providers_models.subtitle": "管理模型供应商与模型,配置后写入 ~/.pi/agent/。",
9
10
  "providers_models.custom_providers": "自定义供应商",
10
11
  "providers_models.model_list": "模型列表",
@@ -21,8 +22,18 @@ const zhCN: Record<string, string> = {
21
22
  "providers_models.test_ok": "连接正常 ({0} ms)",
22
23
  "providers_models.test_fail": "连接失败:{0}",
23
24
  "providers_models.invalid_url": "请输入合法的 http(s) 地址",
25
+ "providers_models.baseurl_override": "此地址将覆盖内置默认地址(写入 models.json)",
24
26
  "providers_models.enable_all": "全部启用",
27
+ "providers_models.sort_by": "排序",
28
+ "providers_models.sort_default": "默认",
29
+ "providers_models.sort_family": "按厂商",
30
+ "providers_models.sort_price_asc": "价格升序",
31
+ "providers_models.sort_price_desc": "价格降序",
25
32
  "providers_models.disable_all": "全部禁用",
33
+ "providers_models.enabled_models_title": "已启用模型",
34
+ "providers_models.no_enabled_models": "暂无启用的模型。在供应商的模型列表中开启模型后,会显示在这里。",
35
+ "providers_models.api_key_env": "将以环境变量 {0} 的值作为密钥(不会明文写入)",
36
+ "providers_models.test_all": "测试全部",
26
37
  "providers_models.id_exists": "ID “{0}” 已存在,请换一个名称",
27
38
  "providers_models.id_preview": "将以 ID “{0}” 保存",
28
39
  "providers_models.import": "导入",
@@ -31,13 +42,61 @@ const zhCN: Record<string, string> = {
31
42
  "providers_models.import_placeholder": "tokenrouter baseurl:https://api.example.com/v1 key:sk-xxxx\nmodelid:vendor/model-name",
32
43
  "providers_models.import_empty": "未能从文本中识别出有效内容",
33
44
  "providers_models.import_merge": "供应商 “{0}” 已存在,将合并模型到该供应商",
34
- "providers_models.import_builtin_conflict": "与内置供应商 “{0}” 冲突,请修改名称",
45
+ "providers_models.import_builtin_conflict": "与内置供应商 “{0}” 冲突,请修改名称",
46
+ "providers_models.fetch_models": "获取模型列表",
47
+ "providers_models.fetch_no_endpoint": "该供应商未提供可用的 /models 端点",
48
+ "providers_models.fetching": "正在获取...",
49
+ "providers_models.fetch_error": "获取失败:{0}",
50
+ "providers_models.fetch_empty": "该供应商未返回可用模型",
51
+ "providers_models.modality_vision": "图文",
52
+ "providers_models.modality_text": "文本",
53
+ "providers_models.id_invalid": "无法从名称或地址生成有效 ID,请先填写基础地址或使用英文名称",
54
+ "providers_models.fetch_title": "导入模型",
55
+ "providers_models.fetch_desc": "从 {0} 的 /models 端点获取可用模型,选择要导入的模型。",
56
+ "providers_models.select_all": "全选",
57
+ "providers_models.deselect_all": "取消全选",
58
+ "providers_models.selected_count": "已选择 {0}/{1} 个模型",
59
+ "providers_models.confirm_import": "导入选中模型",
60
+
61
+ "providers_models.import_done": "已导入 {0} 个模型",
62
+
35
63
  "nav.sessions": "会话",
36
64
  "nav.memory": "记忆",
37
65
  "nav.settings": "设置",
38
66
  "app.subtitle": "配置管理器",
39
67
  "app.version": "pi-switch v0.1.0",
40
68
 
69
+ // Subagents
70
+ "subagents.summary": "{0} 个代理 · {1} 个流水线",
71
+ "subagents.tab_agents": "代理",
72
+ "subagents.tab_chains": "流水线",
73
+ "subagents.tab_history": "运行记录",
74
+ "subagents.no_agents": "暂无代理",
75
+ "subagents.no_agents_desc": "在 ~/.pi/agent/agents/ 目录下创建 .md 文件即可添加子代理。",
76
+ "subagents.no_chains": "暂无流水线",
77
+ "subagents.no_chains_desc": "在 ~/.pi/agent/chains/ 目录下创建 .chain.md 文件即可添加流水线。",
78
+ "subagents.no_history": "暂无运行记录",
79
+ "subagents.no_history_desc": "运行子代理后,运行记录将显示在这里。",
80
+ "subagents.model": "模型",
81
+ "subagents.thinking": "思考级别",
82
+ "subagents.tools": "工具",
83
+ "subagents.system_prompt_mode": "系统提示模式",
84
+ "subagents.input": "输入类型",
85
+ "subagents.system_prompt": "系统提示词",
86
+ "subagents.empty_prompt": "(空)",
87
+ "subagents.steps_count": "步骤",
88
+ "subagents.pipeline": "执行流程",
89
+ "subagents.phase": "阶段",
90
+ "subagents.label": "标签",
91
+ "subagents.output": "输出",
92
+ "subagents.agent": "代理",
93
+ "subagents.time": "时间",
94
+ "subagents.status": "状态",
95
+ "subagents.duration": "耗时",
96
+ "subagents.status_ok": "成功",
97
+ "subagents.status_error": "失败",
98
+ "subagents.showing_recent": "显示最近 100 条记录",
99
+
41
100
  "dashboard.title": "使用统计",
42
101
  "dashboard.requests_count": "{0} 次请求 · 总成本 {1}",
43
102
  "dashboard.range.today": "当天",
@@ -86,6 +145,8 @@ const zhCN: Record<string, string> = {
86
145
 
87
146
  "models.search_placeholder": "搜索模型...",
88
147
  "models.add_model": "添加模型",
148
+
149
+ "models.test_model": "测试模型",
89
150
  "models.edit_model": "编辑模型",
90
151
  "models.delete_model": "删除模型",
91
152
  "models.no_models": "未找到模型",
@@ -105,9 +166,35 @@ const zhCN: Record<string, string> = {
105
166
  "models.save": "保存",
106
167
  "models.cancel": "取消",
107
168
  "models.delete_confirm": "确定要删除此模型吗?",
169
+ "models.pick_preset": "从目录选择",
170
+ "models.pick_preset_placeholder": "搜索常用模型(Claude、GPT、Qwen、GLM、DeepSeek…)",
171
+ "models.detected_catalog": "已匹配:{0}",
172
+ "models.detected_heuristic": "已根据 ID 推断参数",
173
+ "models.manual_fill": "手动填写",
174
+ "models.preset_claude": "Claude 风格",
175
+ "models.preset_gpt": "GPT 风格",
176
+ "models.preset_reasoning": "推理模型",
177
+ "models.preset_small_local": "本地小模型",
178
+ "models.audio_input": "音频输入",
179
+ "models.quick_add": "快速添加",
180
+ "models.quick_add_placeholder": "输入模型 ID 回车即加(自动识别参数)",
181
+ "models.copy_params": "复制参数",
182
+ "models.params_copied": "参数已复制",
183
+ "models.catalog_hint": "找不到你的模型?直接在下方输入 ID,会自动推断常见参数。",
184
+
185
+ // Compat
186
+ "compat.title": "API 兼容性",
187
+ "compat.desc": "当 OpenAI 兼容 API 不支持某些功能时,覆盖默认行为。",
188
+ "compat.supports_developer_role": "支持 developer 角色",
189
+ "compat.supports_developer_role_desc": "使用 'developer' 角色发送系统提示(推理模型)。如果不支持,请关闭以改用 'system' 角色。",
190
+ "compat.supports_reasoning_effort": "支持 reasoning_effort",
191
+ "compat.supports_reasoning_effort_desc": "发送 reasoning_effort 参数。如果 API 不支持,请关闭。",
108
192
 
109
193
  "providers.add_provider": "添加提供商",
110
194
  "providers.delete_provider": "删除提供商",
195
+ "providers_models.expand": "展开(还有 {0} 个)",
196
+ "providers_models.collapse": "收起",
197
+ "providers.duplicate_provider": "复制提供商",
111
198
  "providers.custom": "自定义",
112
199
  "providers.builtin": "内置",
113
200
  "providers.configured": "已配置",
@@ -191,6 +278,9 @@ const zhCN: Record<string, string> = {
191
278
  "settings.theme_desc": "选择界面配色方案",
192
279
  "settings.font_size": "字体大小",
193
280
  "settings.font_size_desc": "调整界面整体字体大小",
281
+ "settings.ui_zoom": "界面缩放",
282
+ "settings.ui_zoom_desc": "按百分比缩放整个界面(50%–200%)",
283
+ "settings.ui_zoom_reset": "重置",
194
284
  "settings.enabled_models_desc": "点击标签以启用或禁用模型",
195
285
  "settings.updates_title": "更新检测",
196
286
  "settings.updates_desc": "检查 pi 本体与扩展在 npm 上是否有新版本。",