@raingor/pi-web-switch 0.4.0 → 0.4.2
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/README.ja.md +32 -8
- package/README.md +58 -10
- package/README.zh-CN.md +31 -7
- package/dist-electron/main/main.cjs +2453 -0
- package/package.json +22 -5
- package/pi-package/index.ts +228 -4
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/pi.svg +6 -41
- package/public/sw.js +28 -5
- package/public/trayIconTemplate.png +0 -0
- package/server/pi-reader.ts +707 -27
- package/src/components/dashboard/DashboardPage.tsx +88 -16
- package/src/components/layout/AppShell.tsx +12 -5
- package/src/components/layout/Sidebar.tsx +15 -4
- package/src/components/providers/ProvidersModelsPage.tsx +84 -14
- package/src/components/sessions/SessionsPage.tsx +501 -149
- package/src/components/settings/SettingsPage.tsx +72 -0
- package/src/index.css +24 -0
- package/src/lib/translations/en.ts +31 -0
- package/src/lib/translations/ja.ts +31 -0
- package/src/lib/translations/zh-CN.ts +31 -0
- package/src/lib/translations/zh-TW.ts +31 -0
- package/src/main.tsx +31 -5
- package/src/store/config-store.ts +41 -0
- package/src/types/chat.ts +217 -0
- package/src/types/index.ts +2 -0
- package/vite.config.ts +240 -1
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
CloudDownload,
|
|
28
28
|
RefreshCw,
|
|
29
29
|
ZoomIn,
|
|
30
|
+
KeyRound,
|
|
30
31
|
} from "lucide-react";
|
|
31
32
|
|
|
32
33
|
type SettingsTab = "appearance" | "models" | "advanced";
|
|
@@ -116,6 +117,37 @@ export function SettingsPage() {
|
|
|
116
117
|
const [updateError, setUpdateError] = useState(false);
|
|
117
118
|
const [applying, setApplying] = useState(false);
|
|
118
119
|
const [applyMessage, setApplyMessage] = useState<{ ok: number; failNames: string[] } | null>(null);
|
|
120
|
+
// GitHub Copilot usage API config (username + classic PAT).
|
|
121
|
+
const [copilotCfg, setCopilotCfg] = useState<{ username: string; token: string }>({ username: "", token: "" });
|
|
122
|
+
const [copilotSaving, setCopilotSaving] = useState(false);
|
|
123
|
+
const [copilotMsg, setCopilotMsg] = useState<{ ok: boolean; text: string } | null>(null);
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
fetch("/api/pi/copilot-config")
|
|
127
|
+
.then((r) => r.json())
|
|
128
|
+
.then((cfg: { username?: string; token?: string }) =>
|
|
129
|
+
setCopilotCfg({ username: cfg.username ?? "", token: cfg.token ?? "" })
|
|
130
|
+
)
|
|
131
|
+
.catch(() => {});
|
|
132
|
+
}, []);
|
|
133
|
+
|
|
134
|
+
const handleSaveCopilot = async () => {
|
|
135
|
+
setCopilotSaving(true);
|
|
136
|
+
setCopilotMsg(null);
|
|
137
|
+
try {
|
|
138
|
+
const res = await fetch("/api/pi/copilot-config", {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: { "Content-Type": "application/json" },
|
|
141
|
+
body: JSON.stringify(copilotCfg),
|
|
142
|
+
});
|
|
143
|
+
const { success } = (await res.json()) as { success: boolean };
|
|
144
|
+
setCopilotMsg({ ok: !!success, text: success ? t("settings.copilot_saved") : t("settings.copilot_save_failed") });
|
|
145
|
+
} catch {
|
|
146
|
+
setCopilotMsg({ ok: false, text: t("settings.copilot_save_failed") });
|
|
147
|
+
} finally {
|
|
148
|
+
setCopilotSaving(false);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
119
151
|
|
|
120
152
|
const handleCheckUpdates = async () => {
|
|
121
153
|
setCheckingUpdates(true);
|
|
@@ -563,6 +595,46 @@ export function SettingsPage() {
|
|
|
563
595
|
})()}
|
|
564
596
|
</Card>
|
|
565
597
|
|
|
598
|
+
<Card icon={KeyRound} title={t("settings.copilot_title")} desc={t("settings.copilot_desc")}>
|
|
599
|
+
<div className="max-w-md space-y-3">
|
|
600
|
+
<div>
|
|
601
|
+
<label className="mb-1 block text-xs text-gray-500">{t("settings.copilot_username")}</label>
|
|
602
|
+
<input
|
|
603
|
+
type="text"
|
|
604
|
+
value={copilotCfg.username}
|
|
605
|
+
onChange={(e) => setCopilotCfg({ ...copilotCfg, username: e.target.value })}
|
|
606
|
+
placeholder="octocat"
|
|
607
|
+
className="w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-sm text-white placeholder-gray-600"
|
|
608
|
+
/>
|
|
609
|
+
</div>
|
|
610
|
+
<div>
|
|
611
|
+
<label className="mb-1 block text-xs text-gray-500">{t("settings.copilot_token")}</label>
|
|
612
|
+
<input
|
|
613
|
+
type="password"
|
|
614
|
+
value={copilotCfg.token}
|
|
615
|
+
onChange={(e) => setCopilotCfg({ ...copilotCfg, token: e.target.value })}
|
|
616
|
+
placeholder="ghp_…"
|
|
617
|
+
className="w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-sm text-white placeholder-gray-600"
|
|
618
|
+
/>
|
|
619
|
+
</div>
|
|
620
|
+
<div className="flex items-center gap-3">
|
|
621
|
+
<button
|
|
622
|
+
onClick={handleSaveCopilot}
|
|
623
|
+
disabled={copilotSaving}
|
|
624
|
+
className="flex items-center gap-1.5 rounded-lg border border-gray-700 bg-gray-800 px-4 py-1.5 text-sm text-gray-300 hover:bg-gray-700 disabled:opacity-60"
|
|
625
|
+
>
|
|
626
|
+
{copilotSaving ? t("settings.copilot_saving") : t("settings.copilot_save")}
|
|
627
|
+
</button>
|
|
628
|
+
{copilotMsg && (
|
|
629
|
+
<span className={cn("text-xs", copilotMsg.ok ? "text-emerald-400" : "text-red-400")}>
|
|
630
|
+
{copilotMsg.text}
|
|
631
|
+
</span>
|
|
632
|
+
)}
|
|
633
|
+
</div>
|
|
634
|
+
<p className="text-[11px] leading-relaxed text-gray-500">{t("settings.copilot_token_hint")}</p>
|
|
635
|
+
</div>
|
|
636
|
+
</Card>
|
|
637
|
+
|
|
566
638
|
<Card icon={Package} title={t("settings.packages")}>
|
|
567
639
|
{(settings?.packages ?? []).length > 0 && (
|
|
568
640
|
<div className="mb-4 flex flex-wrap gap-1.5">
|
package/src/index.css
CHANGED
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
--sidebar-hover-text: #374151;
|
|
29
29
|
--scrollbar-thumb: #d1d5db;
|
|
30
30
|
--scrollbar-thumb-hover: #9ca3af;
|
|
31
|
+
|
|
32
|
+
/* Chat component variables */
|
|
33
|
+
--bg: #ffffff;
|
|
34
|
+
--bg-panel: #f9fafb;
|
|
35
|
+
--bg-hover: #f3f4f6;
|
|
36
|
+
--bg-selected: #eff6ff;
|
|
37
|
+
--border: #e5e7eb;
|
|
38
|
+
--text: #111827;
|
|
39
|
+
--text-muted: #6b7280;
|
|
40
|
+
--text-dim: #9ca3af;
|
|
41
|
+
--accent: #2563eb;
|
|
42
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
.dark {
|
|
@@ -55,6 +67,18 @@
|
|
|
55
67
|
--sidebar-hover-text: #e5e7eb;
|
|
56
68
|
--scrollbar-thumb: #4b5563;
|
|
57
69
|
--scrollbar-thumb-hover: #6b7280;
|
|
70
|
+
|
|
71
|
+
/* Chat component variables */
|
|
72
|
+
--bg: #030712;
|
|
73
|
+
--bg-panel: rgba(17, 24, 39, 0.5);
|
|
74
|
+
--bg-hover: rgba(31, 41, 55, 0.5);
|
|
75
|
+
--bg-selected: rgba(37, 99, 235, 0.1);
|
|
76
|
+
--border: #1f2937;
|
|
77
|
+
--text: #f3f4f6;
|
|
78
|
+
--text-muted: #9ca3af;
|
|
79
|
+
--text-dim: #6b7280;
|
|
80
|
+
--accent: #2563eb;
|
|
81
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
58
82
|
}
|
|
59
83
|
|
|
60
84
|
:root {
|
|
@@ -101,6 +101,18 @@ const en: Record<string, string> = {
|
|
|
101
101
|
|
|
102
102
|
// Dashboard
|
|
103
103
|
"dashboard.title": "Usage Statistics",
|
|
104
|
+
"dashboard.source_pi": "Pi",
|
|
105
|
+
"dashboard.source_cindy_pi": "Cindy-Pi",
|
|
106
|
+
"dashboard.source_claude": "Claude",
|
|
107
|
+
"dashboard.source_codex": "Codex",
|
|
108
|
+
"dashboard.source_all": "All",
|
|
109
|
+
"dashboard.source_opencode": "OpenCode",
|
|
110
|
+
"dashboard.source_gemini": "Gemini",
|
|
111
|
+
"dashboard.source_grok": "Grok",
|
|
112
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
113
|
+
"dashboard.source_copilot": "Copilot",
|
|
114
|
+
"dashboard.copilot_not_configured": "GitHub Copilot not configured — add username & token in Settings → Advanced",
|
|
115
|
+
"dashboard.copilot_api_error": "Failed to fetch Copilot usage from GitHub API — check token and billing platform",
|
|
104
116
|
"dashboard.requests_count": "{0} requests · {1} total cost",
|
|
105
117
|
"dashboard.range.today": "Today",
|
|
106
118
|
"dashboard.range.7d": "7 Days",
|
|
@@ -191,6 +203,8 @@ const en: Record<string, string> = {
|
|
|
191
203
|
"compat.desc": "Override default behavior for OpenAI-compatible providers that don't support certain features.",
|
|
192
204
|
"compat.supports_developer_role": "Supports developer role",
|
|
193
205
|
"compat.supports_developer_role_desc": "Send system prompt as 'developer' role (for reasoning models). Disable if the API only accepts 'system' role.",
|
|
206
|
+
"compat.supports_finish_reason": "Strict finish_reason check",
|
|
207
|
+
"compat.supports_finish_reason_desc": "Require streamed responses to include finish_reason, otherwise fail. Disable to infer the stop reason automatically when the upstream drops the stream (common with free models).",
|
|
194
208
|
"compat.supports_reasoning_effort": "Supports reasoning_effort",
|
|
195
209
|
"compat.supports_reasoning_effort_desc": "Send reasoning_effort parameter. Disable if the API does not support it.",
|
|
196
210
|
|
|
@@ -213,6 +227,7 @@ const en: Record<string, string> = {
|
|
|
213
227
|
"sessions.summary": "{0} sessions across {1} projects",
|
|
214
228
|
"sessions.filter_placeholder": "Search projects or sessions...",
|
|
215
229
|
"sessions.no_sessions": "No sessions found",
|
|
230
|
+
"sessions.no_search_results": "No sessions match your search",
|
|
216
231
|
"sessions.last_active": "Last active {0}",
|
|
217
232
|
"sessions.sessions_count": "{0} sessions",
|
|
218
233
|
"sessions.messages": "{0} messages",
|
|
@@ -227,6 +242,13 @@ const en: Record<string, string> = {
|
|
|
227
242
|
"sessions.delete_error": "Error deleting session",
|
|
228
243
|
"sessions.load_failed": "Failed to load sessions",
|
|
229
244
|
"sessions.refresh": "Refresh",
|
|
245
|
+
"sessions.auto_expire": "Auto-Expire",
|
|
246
|
+
"sessions.auto_expire_tooltip": "Move sessions inactive for {0} days to trash",
|
|
247
|
+
"sessions.auto_expire_success": "Moved {0} expired sessions to trash",
|
|
248
|
+
"sessions.auto_expire_none": "No expired sessions found",
|
|
249
|
+
"sessions.auto_expire_error": "Auto-expire failed: {0}",
|
|
250
|
+
"sessions.expand_all": "Expand All",
|
|
251
|
+
"sessions.collapse_all": "Collapse All",
|
|
230
252
|
"sessions.more_count": "{0} more",
|
|
231
253
|
"sessions.tab_sessions": "Sessions",
|
|
232
254
|
"sessions.tab_trash": "Trash",
|
|
@@ -290,6 +312,15 @@ const en: Record<string, string> = {
|
|
|
290
312
|
"settings.ui_zoom_desc": "Scale the entire interface by percentage (50%–200%)",
|
|
291
313
|
"settings.ui_zoom_reset": "Reset",
|
|
292
314
|
"settings.enabled_models_desc": "Click a tag to enable or disable a model",
|
|
315
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
316
|
+
"settings.copilot_desc": "Track Copilot usage from the GitHub billing API (premium requests / AI credits)",
|
|
317
|
+
"settings.copilot_username": "GitHub username",
|
|
318
|
+
"settings.copilot_token": "Token (classic PAT)",
|
|
319
|
+
"settings.copilot_save": "Save",
|
|
320
|
+
"settings.copilot_saving": "Saving…",
|
|
321
|
+
"settings.copilot_saved": "Saved — usage will refresh on next dashboard load",
|
|
322
|
+
"settings.copilot_save_failed": "Save failed",
|
|
323
|
+
"settings.copilot_token_hint": "Create a classic personal access token at github.com/settings/tokens (fine-grained tokens are not supported by the billing usage endpoints). Copilot usage only appears for accounts on the enhanced billing platform.",
|
|
293
324
|
"settings.updates_title": "Update Check",
|
|
294
325
|
"settings.updates_desc": "Check npm for new versions of pi core and installed extensions.",
|
|
295
326
|
"settings.check_updates": "Check for Updates",
|
|
@@ -98,6 +98,18 @@ const ja: Record<string, string> = {
|
|
|
98
98
|
"subagents.showing_recent": "最新 100 件を表示",
|
|
99
99
|
|
|
100
100
|
"dashboard.title": "使用統計",
|
|
101
|
+
"dashboard.source_pi": "Pi",
|
|
102
|
+
"dashboard.source_cindy_pi": "Cindy-Pi",
|
|
103
|
+
"dashboard.source_claude": "Claude",
|
|
104
|
+
"dashboard.source_codex": "Codex",
|
|
105
|
+
"dashboard.source_all": "すべて",
|
|
106
|
+
"dashboard.source_opencode": "OpenCode",
|
|
107
|
+
"dashboard.source_gemini": "Gemini",
|
|
108
|
+
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未設定 — 設定 → 詳細設定 でユーザー名とトークンを入力してください",
|
|
112
|
+
"dashboard.copilot_api_error": "GitHub API から Copilot 使用量を取得できませんでした — トークンと請求プラットフォームを確認してください",
|
|
101
113
|
"dashboard.requests_count": "{0} リクエスト · 総コスト {1}",
|
|
102
114
|
"dashboard.range.today": "今日",
|
|
103
115
|
"dashboard.range.7d": "7日",
|
|
@@ -187,6 +199,8 @@ const ja: Record<string, string> = {
|
|
|
187
199
|
"compat.desc": "OpenAI 互換 API が一部の機能をサポートしていない場合の動作を上書きします。",
|
|
188
200
|
"compat.supports_developer_role": "developer ロールをサポート",
|
|
189
201
|
"compat.supports_developer_role_desc": "システムプロンプトを 'developer' ロールで送信(推論モデル)。API が 'system' ロールのみ受け付ける場合は無効にしてください。",
|
|
202
|
+
"compat.supports_finish_reason": "finish_reason の厳格な検証",
|
|
203
|
+
"compat.supports_finish_reason_desc": "ストリーム応答に finish_reason の含有を必須にし、無ければエラーにします。上流がストリームを途中で切断する場合(無料モデルに多い)は無効にして終了理由の自動判定に切り替えてください。",
|
|
190
204
|
"compat.supports_reasoning_effort": "reasoning_effort をサポート",
|
|
191
205
|
"compat.supports_reasoning_effort_desc": "reasoning_effort パラメータを送信します。API がサポートしていない場合は無効にしてください。",
|
|
192
206
|
|
|
@@ -207,6 +221,7 @@ const ja: Record<string, string> = {
|
|
|
207
221
|
"sessions.summary": "合計 {0} セッション、{1} プロジェクト",
|
|
208
222
|
"sessions.filter_placeholder": "プロジェクトまたはセッションを検索...",
|
|
209
223
|
"sessions.no_sessions": "セッションが見つかりません",
|
|
224
|
+
"sessions.no_search_results": "検索条件に一致するセッションが見つかりません",
|
|
210
225
|
"sessions.last_active": "最終アクティブ {0}",
|
|
211
226
|
"sessions.sessions_count": "{0} セッション",
|
|
212
227
|
"sessions.messages": "{0} メッセージ",
|
|
@@ -221,6 +236,13 @@ const ja: Record<string, string> = {
|
|
|
221
236
|
"sessions.delete_error": "削除中にエラーが発生しました",
|
|
222
237
|
"sessions.load_failed": "セッションの読み込みに失敗しました",
|
|
223
238
|
"sessions.refresh": "更新",
|
|
239
|
+
"sessions.auto_expire": "自動期限切れ",
|
|
240
|
+
"sessions.auto_expire_tooltip": "{0}日間非活跃のセッションをゴミ箱に移動",
|
|
241
|
+
"sessions.auto_expire_success": "{0}件の期限切れセッションをゴミ箱に移動しました",
|
|
242
|
+
"sessions.auto_expire_none": "期限切れのセッションはありません",
|
|
243
|
+
"sessions.auto_expire_error": "自動期限切れに失敗:{0}",
|
|
244
|
+
"sessions.expand_all": "すべて展開",
|
|
245
|
+
"sessions.collapse_all": "すべて折りたたむ",
|
|
224
246
|
"sessions.more_count": "あと {0} 件",
|
|
225
247
|
"sessions.tab_sessions": "セッション",
|
|
226
248
|
"sessions.tab_trash": "ゴミ箱",
|
|
@@ -282,6 +304,15 @@ const ja: Record<string, string> = {
|
|
|
282
304
|
"settings.ui_zoom_desc": "インターフェース全体をパーセントで拡大縮小(50%–200%)",
|
|
283
305
|
"settings.ui_zoom_reset": "リセット",
|
|
284
306
|
"settings.enabled_models_desc": "タグをクリックしてモデルを切り替え",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "GitHub 課金 API から Copilot の使用量を取得(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub ユーザー名",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "保存",
|
|
312
|
+
"settings.copilot_saving": "保存中…",
|
|
313
|
+
"settings.copilot_saved": "保存しました — 次回ダッシュボード読み込み時に反映されます",
|
|
314
|
+
"settings.copilot_save_failed": "保存に失敗しました",
|
|
315
|
+
"settings.copilot_token_hint": "github.com/settings/tokens で classic personal access token を作成してください(課金 usage エンドポイントは fine-grained 非対応)。Copilot 使用量は拡張課金プラットフォームのアカウントのみ表示されます。",
|
|
285
316
|
"settings.updates_title": "アップデート確認",
|
|
286
317
|
"settings.updates_desc": "pi 本体と拡張に npm 上の新バージョンがあるか確認します。",
|
|
287
318
|
"settings.check_updates": "更新を確認",
|
|
@@ -98,6 +98,18 @@ const zhCN: Record<string, string> = {
|
|
|
98
98
|
"subagents.showing_recent": "显示最近 100 条记录",
|
|
99
99
|
|
|
100
100
|
"dashboard.title": "使用统计",
|
|
101
|
+
"dashboard.source_pi": "Pi 程序",
|
|
102
|
+
"dashboard.source_cindy_pi": "Cindy-Pi",
|
|
103
|
+
"dashboard.source_claude": "Claude",
|
|
104
|
+
"dashboard.source_codex": "Codex",
|
|
105
|
+
"dashboard.source_all": "全部",
|
|
106
|
+
"dashboard.source_opencode": "OpenCode",
|
|
107
|
+
"dashboard.source_gemini": "Gemini",
|
|
108
|
+
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未配置 — 请在 设置 → 高级 中填写用户名与 Token",
|
|
112
|
+
"dashboard.copilot_api_error": "从 GitHub API 获取 Copilot 用量失败 — 请检查 Token 与计费平台",
|
|
101
113
|
"dashboard.requests_count": "{0} 次请求 · 总成本 {1}",
|
|
102
114
|
"dashboard.range.today": "当天",
|
|
103
115
|
"dashboard.range.7d": "7天",
|
|
@@ -187,6 +199,8 @@ const zhCN: Record<string, string> = {
|
|
|
187
199
|
"compat.desc": "当 OpenAI 兼容 API 不支持某些功能时,覆盖默认行为。",
|
|
188
200
|
"compat.supports_developer_role": "支持 developer 角色",
|
|
189
201
|
"compat.supports_developer_role_desc": "使用 'developer' 角色发送系统提示(推理模型)。如果不支持,请关闭以改用 'system' 角色。",
|
|
202
|
+
"compat.supports_finish_reason": "严格校验 finish_reason",
|
|
203
|
+
"compat.supports_finish_reason_desc": "要求流式响应必须包含 finish_reason,否则报错。上游偶尔掐断流(免费模型常见)时,请关闭以改为自动推断结束原因。",
|
|
190
204
|
"compat.supports_reasoning_effort": "支持 reasoning_effort",
|
|
191
205
|
"compat.supports_reasoning_effort_desc": "发送 reasoning_effort 参数。如果 API 不支持,请关闭。",
|
|
192
206
|
|
|
@@ -207,6 +221,7 @@ const zhCN: Record<string, string> = {
|
|
|
207
221
|
"sessions.summary": "共 {0} 个会话,分布在 {1} 个项目",
|
|
208
222
|
"sessions.filter_placeholder": "搜索项目或会话...",
|
|
209
223
|
"sessions.no_sessions": "未找到会话",
|
|
224
|
+
"sessions.no_search_results": "没有找到匹配的会话",
|
|
210
225
|
"sessions.last_active": "最近活跃 {0}",
|
|
211
226
|
"sessions.sessions_count": "{0} 个会话",
|
|
212
227
|
"sessions.messages": "{0} 条消息",
|
|
@@ -221,6 +236,13 @@ const zhCN: Record<string, string> = {
|
|
|
221
236
|
"sessions.delete_error": "删除时发生错误",
|
|
222
237
|
"sessions.load_failed": "加载会话失败",
|
|
223
238
|
"sessions.refresh": "刷新",
|
|
239
|
+
"sessions.auto_expire": "自动清理",
|
|
240
|
+
"sessions.auto_expire_tooltip": "将 {0} 天未活跃的会话移入回收站",
|
|
241
|
+
"sessions.auto_expire_success": "已将 {0} 个过期会话移入回收站",
|
|
242
|
+
"sessions.auto_expire_none": "没有找到过期会话",
|
|
243
|
+
"sessions.auto_expire_error": "自动清理失败:{0}",
|
|
244
|
+
"sessions.expand_all": "全部展开",
|
|
245
|
+
"sessions.collapse_all": "全部折叠",
|
|
224
246
|
"sessions.more_count": "还有 {0} 条",
|
|
225
247
|
"sessions.tab_sessions": "会话",
|
|
226
248
|
"sessions.tab_trash": "回收站",
|
|
@@ -282,6 +304,15 @@ const zhCN: Record<string, string> = {
|
|
|
282
304
|
"settings.ui_zoom_desc": "按百分比缩放整个界面(50%–200%)",
|
|
283
305
|
"settings.ui_zoom_reset": "重置",
|
|
284
306
|
"settings.enabled_models_desc": "点击标签以启用或禁用模型",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "通过 GitHub 计费 API 统计 Copilot 用量(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub 用户名",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "保存",
|
|
312
|
+
"settings.copilot_saving": "保存中…",
|
|
313
|
+
"settings.copilot_saved": "已保存 — 下次打开仪表盘时刷新用量",
|
|
314
|
+
"settings.copilot_save_failed": "保存失败",
|
|
315
|
+
"settings.copilot_token_hint": "在 github.com/settings/tokens 创建 classic 个人访问令牌(计费用量接口不支持 fine-grained token)。仅增强计费平台的账号会显示 Copilot 用量。",
|
|
285
316
|
"settings.updates_title": "更新检测",
|
|
286
317
|
"settings.updates_desc": "检查 pi 本体与扩展在 npm 上是否有新版本。",
|
|
287
318
|
"settings.check_updates": "检查更新",
|
|
@@ -98,6 +98,18 @@ const zhTW: Record<string, string> = {
|
|
|
98
98
|
"subagents.showing_recent": "顯示最近 100 條記錄",
|
|
99
99
|
|
|
100
100
|
"dashboard.title": "使用統計",
|
|
101
|
+
"dashboard.source_pi": "Pi 程式",
|
|
102
|
+
"dashboard.source_cindy_pi": "Cindy-Pi",
|
|
103
|
+
"dashboard.source_claude": "Claude",
|
|
104
|
+
"dashboard.source_codex": "Codex",
|
|
105
|
+
"dashboard.source_all": "全部",
|
|
106
|
+
"dashboard.source_opencode": "OpenCode",
|
|
107
|
+
"dashboard.source_gemini": "Gemini",
|
|
108
|
+
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未設定 — 請在 設定 → 進階 中填寫使用者名稱與 Token",
|
|
112
|
+
"dashboard.copilot_api_error": "從 GitHub API 取得 Copilot 用量失敗 — 請檢查 Token 與計費平台",
|
|
101
113
|
"dashboard.requests_count": "{0} 次請求 · 總成本 {1}",
|
|
102
114
|
"dashboard.range.today": "當天",
|
|
103
115
|
"dashboard.range.7d": "7天",
|
|
@@ -187,6 +199,8 @@ const zhTW: Record<string, string> = {
|
|
|
187
199
|
"compat.desc": "當 OpenAI 相容 API 不支援某些功能時,覆蓋預設行為。",
|
|
188
200
|
"compat.supports_developer_role": "支援 developer 角色",
|
|
189
201
|
"compat.supports_developer_role_desc": "使用 'developer' 角色發送系統提示(推理模型)。如果不支援,請關閉以改用 'system' 角色。",
|
|
202
|
+
"compat.supports_finish_reason": "嚴格校驗 finish_reason",
|
|
203
|
+
"compat.supports_finish_reason_desc": "要求串流回應必須包含 finish_reason,否則報錯。上游偶爾掐斷串流(免費模型常見)時,請關閉以改為自動推斷結束原因。",
|
|
190
204
|
"compat.supports_reasoning_effort": "支援 reasoning_effort",
|
|
191
205
|
"compat.supports_reasoning_effort_desc": "發送 reasoning_effort 參數。如果 API 不支援,請關閉。",
|
|
192
206
|
|
|
@@ -207,6 +221,7 @@ const zhTW: Record<string, string> = {
|
|
|
207
221
|
"sessions.summary": "共 {0} 個會話,分佈在 {1} 個專案",
|
|
208
222
|
"sessions.filter_placeholder": "搜尋專案或會話...",
|
|
209
223
|
"sessions.no_sessions": "未找到會話",
|
|
224
|
+
"sessions.no_search_results": "沒有找到符合的會話",
|
|
210
225
|
"sessions.last_active": "最近活躍 {0}",
|
|
211
226
|
"sessions.sessions_count": "{0} 個會話",
|
|
212
227
|
"sessions.messages": "{0} 條訊息",
|
|
@@ -221,6 +236,13 @@ const zhTW: Record<string, string> = {
|
|
|
221
236
|
"sessions.delete_error": "刪除時發生錯誤",
|
|
222
237
|
"sessions.load_failed": "載入會話失敗",
|
|
223
238
|
"sessions.refresh": "重新整理",
|
|
239
|
+
"sessions.auto_expire": "自動清理",
|
|
240
|
+
"sessions.auto_expire_tooltip": "將 {0} 天未活躍的會話移入回收筒",
|
|
241
|
+
"sessions.auto_expire_success": "已將 {0} 個過期會話移入回收筒",
|
|
242
|
+
"sessions.auto_expire_none": "沒有找到過期會話",
|
|
243
|
+
"sessions.auto_expire_error": "自動清理失敗:{0}",
|
|
244
|
+
"sessions.expand_all": "全部展開",
|
|
245
|
+
"sessions.collapse_all": "全部收合",
|
|
224
246
|
"sessions.more_count": "還有 {0} 條",
|
|
225
247
|
"sessions.tab_sessions": "會話",
|
|
226
248
|
"sessions.tab_trash": "回收筒",
|
|
@@ -282,6 +304,15 @@ const zhTW: Record<string, string> = {
|
|
|
282
304
|
"settings.ui_zoom_desc": "依百分比縮放整個介面(50%–200%)",
|
|
283
305
|
"settings.ui_zoom_reset": "重設",
|
|
284
306
|
"settings.enabled_models_desc": "點擊標籤以啟用或停用模型",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "透過 GitHub 計費 API 統計 Copilot 用量(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub 使用者名稱",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "儲存",
|
|
312
|
+
"settings.copilot_saving": "儲存中…",
|
|
313
|
+
"settings.copilot_saved": "已儲存 — 下次開啟儀表板時重新整理用量",
|
|
314
|
+
"settings.copilot_save_failed": "儲存失敗",
|
|
315
|
+
"settings.copilot_token_hint": "在 github.com/settings/tokens 建立 classic 個人存取權杖(計費用量 API 不支援 fine-grained token)。僅增強計費平台的帳號會顯示 Copilot 用量。",
|
|
285
316
|
"settings.updates_title": "更新檢測",
|
|
286
317
|
"settings.updates_desc": "檢查 pi 本體與擴充在 npm 上是否有新版本。",
|
|
287
318
|
"settings.check_updates": "檢查更新",
|
package/src/main.tsx
CHANGED
|
@@ -123,11 +123,37 @@ createRoot(document.getElementById("root")!).render(
|
|
|
123
123
|
</StrictMode>
|
|
124
124
|
);
|
|
125
125
|
|
|
126
|
-
// ─── PWA: register service worker
|
|
126
|
+
// ─── PWA: register service worker (production only — dev must not
|
|
127
|
+
// cache HTML pages, or SPA routes like /providers can return stale HTML
|
|
128
|
+
// to API fetches and break JSON parsing) ─────────────────────
|
|
127
129
|
if ("serviceWorker" in navigator) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
if (import.meta.env.PROD) {
|
|
131
|
+
window.addEventListener("load", () => {
|
|
132
|
+
navigator.serviceWorker.register("/sw.js").catch((err) => {
|
|
133
|
+
console.warn("SW registration failed:", err);
|
|
134
|
+
});
|
|
131
135
|
});
|
|
132
|
-
}
|
|
136
|
+
} else {
|
|
137
|
+
// Dev: proactively unregister any leftover service worker (e.g. from a
|
|
138
|
+
// previous `vite preview` or production build on the same origin) and
|
|
139
|
+
// clear its caches. Dev module URLs are not hashed, so a stale SW would
|
|
140
|
+
// serve old code on every plain reload until a hard refresh.
|
|
141
|
+
window.addEventListener("load", () => {
|
|
142
|
+
navigator.serviceWorker
|
|
143
|
+
.getRegistrations()
|
|
144
|
+
.then((registrations) =>
|
|
145
|
+
Promise.all(registrations.map((reg) => reg.unregister()))
|
|
146
|
+
)
|
|
147
|
+
.then(() =>
|
|
148
|
+
window.caches
|
|
149
|
+
? window.caches.keys().then((keys) =>
|
|
150
|
+
Promise.all(keys.map((k) => window.caches.delete(k)))
|
|
151
|
+
)
|
|
152
|
+
: undefined
|
|
153
|
+
)
|
|
154
|
+
.catch((err) => {
|
|
155
|
+
console.warn("SW cleanup failed:", err);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
133
159
|
}
|
|
@@ -180,6 +180,7 @@ interface ConfigState {
|
|
|
180
180
|
// Custom provider CRUD
|
|
181
181
|
addCustomProvider: (id: string, cfg: CustomProviderConfig) => Promise<boolean>;
|
|
182
182
|
updateCustomProvider: (id: string, cfg: Partial<CustomProviderConfig>) => Promise<boolean>;
|
|
183
|
+
renameCustomProvider: (oldId: string, newId: string, cfg?: Partial<CustomProviderConfig>) => Promise<boolean>;
|
|
183
184
|
removeCustomProvider: (id: string) => Promise<boolean>;
|
|
184
185
|
|
|
185
186
|
// Import/Export (to localStorage for backup, writes back to pi files)
|
|
@@ -464,6 +465,46 @@ export const useConfigStore = create<ConfigState>((set, get) => ({
|
|
|
464
465
|
return ok;
|
|
465
466
|
},
|
|
466
467
|
|
|
468
|
+
renameCustomProvider: async (oldId, newId, cfg) => {
|
|
469
|
+
const { modelsJson, settings } = get();
|
|
470
|
+
if (!modelsJson || oldId === newId) return false;
|
|
471
|
+
const existing = modelsJson.providers[oldId];
|
|
472
|
+
if (!existing || modelsJson.providers[newId]) return false;
|
|
473
|
+
// Re-key in place, preserving provider order in models.json
|
|
474
|
+
const newProviders: PiModelsJson["providers"] = {};
|
|
475
|
+
for (const [k, v] of Object.entries(modelsJson.providers)) {
|
|
476
|
+
newProviders[k === oldId ? newId : k] = k === oldId ? { ...existing, ...cfg } : v;
|
|
477
|
+
}
|
|
478
|
+
const updated = { providers: newProviders };
|
|
479
|
+
const ok = await apiPost("/models", updated);
|
|
480
|
+
if (!ok) return false;
|
|
481
|
+
set({ modelsJson: updated });
|
|
482
|
+
// Rewrite provider references in settings (defaultProvider, defaultModel,
|
|
483
|
+
// enabledModels entries are "providerId/modelId" strings)
|
|
484
|
+
if (settings) {
|
|
485
|
+
const prefix = `${oldId}/`;
|
|
486
|
+
const patch: Partial<PiSettings> = {};
|
|
487
|
+
if (settings.defaultProvider === oldId) patch.defaultProvider = newId;
|
|
488
|
+
if (settings.defaultModel?.startsWith(prefix))
|
|
489
|
+
patch.defaultModel = `${newId}/${settings.defaultModel.slice(prefix.length)}`;
|
|
490
|
+
if (settings.enabledModels?.some((r) => r.startsWith(prefix)))
|
|
491
|
+
patch.enabledModels = settings.enabledModels.map((r) =>
|
|
492
|
+
r.startsWith(prefix) ? `${newId}/${r.slice(prefix.length)}` : r
|
|
493
|
+
);
|
|
494
|
+
if (Object.keys(patch).length > 0) {
|
|
495
|
+
const updatedSettings = { ...settings, ...patch };
|
|
496
|
+
if (await apiPost("/settings", updatedSettings)) set({ settings: updatedSettings });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const { auth, builtinProviders } = get();
|
|
500
|
+
const newAllProviders = mergeProviders(builtinProviders, auth ?? {}, updated);
|
|
501
|
+
const newAllModels = newAllProviders.flatMap((p) =>
|
|
502
|
+
p.models.map((m) => ({ ...m, providerId: p.id, providerName: p.name }))
|
|
503
|
+
);
|
|
504
|
+
set({ allProviders: newAllProviders, allModels: newAllModels });
|
|
505
|
+
return true;
|
|
506
|
+
},
|
|
507
|
+
|
|
467
508
|
removeCustomProvider: async (id) => {
|
|
468
509
|
const { modelsJson } = get();
|
|
469
510
|
if (!modelsJson) return false;
|