feat: 优化更新检查逻辑 + 修复按量余额显示(v0.6.5)
This commit is contained in:
1 parent
f60305fbeb
commit
2464500ef5
12 files changed
+141
-70
No files matched your search
@@ -110,6 +110,15 @@ export function SettingsModal({
|
||||
invoke("open_installer", { path: downloadedPath }).catch(() => {});
|
||||
};
|
||||
|
||||
// Open an external link in the system browser. Falls back to a new tab
|
||||
// when the opener plugin call fails, so a broken click never stays silent.
|
||||
const openExternal = (url: string) => {
|
||||
openUrl(url).catch(() => {
|
||||
const w = window.open(url, "_blank");
|
||||
if (!w) console.error(`failed to open ${url}`);
|
||||
});
|
||||
};
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const themeOptions: { value: Theme; icon: ReactNode; label: string }[] = [
|
||||
@@ -253,26 +262,66 @@ export function SettingsModal({
|
||||
type="button"
|
||||
onClick={onCheckUpdate}
|
||||
disabled={checking}
|
||||
className="px-3 py-1.5 text-sm rounded border border-border hover:bg-hover-2 disabled:opacity-50 transition-colors"
|
||||
className="px-3 py-1.5 text-sm rounded border border-border hover:bg-hover-2 disabled:opacity-50 transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={checking ? "animate-spin" : ""}
|
||||
>
|
||||
<path d="M21 12a9 9 0 1 1-2.64-6.36" />
|
||||
<polyline points="21 3 21 9 15 9" />
|
||||
</svg>
|
||||
{checking ? t("checking") : t("checkUpdate")}
|
||||
</button>
|
||||
{updateInfo?.updateAvailable && downloadProgress === null && !downloadedPath && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
className="px-3 py-1.5 text-sm rounded bg-green-600 text-white hover:bg-green-500 transition-colors"
|
||||
className="px-3 py-1.5 text-sm rounded bg-green-600 text-white hover:bg-green-500 transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
{t("downloadUpdate")} ↓
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
{t("downloadUpdate")}
|
||||
</button>
|
||||
)}
|
||||
{downloadedPath && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleInstall}
|
||||
className="px-3 py-1.5 text-sm rounded bg-blue-600 text-white hover:bg-blue-500 transition-colors"
|
||||
className="px-3 py-1.5 text-sm rounded bg-blue-600 text-white hover:bg-blue-500 transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
{t("install")} ⏎
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polygon points="6 3 20 12 6 21 6 3" />
|
||||
</svg>
|
||||
{t("install")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -323,18 +372,25 @@ export function SettingsModal({
|
||||
<div className="font-medium text-content-primary">Kimi Switch</div>
|
||||
<div>{t("appDescription")}</div>
|
||||
<div className="text-xs text-content-muted">v{appVersion} · MIT License</div>
|
||||
<div className="pt-2">
|
||||
<span className="text-content-muted">{t("referenceProject")}: </span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openUrl("https://github.com/JochenYang/kimicode-dashboard")
|
||||
}
|
||||
className="text-blue-500 hover:text-blue-400 underline bg-transparent p-0 border-0 cursor-pointer"
|
||||
>
|
||||
kimicode-dashboard
|
||||
</button>
|
||||
<span className="text-content-muted"> (MIT, © JochenYang)</span>
|
||||
<div className="pt-2 space-y-1">
|
||||
<div>
|
||||
{t("attributionPorted")}{" "}
|
||||
<span className="font-medium text-content-primary">kimicode-dashboard</span>
|
||||
<span className="text-content-muted"> {t("attributionSuffix")}</span>
|
||||
</div>
|
||||
<div className="pt-1">
|
||||
<span className="text-content-muted">{t("referenceProject")}: </span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openExternal("https://github.com/farion1231/cc-switch")
|
||||
}
|
||||
className="text-blue-500 hover:text-blue-400 underline bg-transparent p-0 border-0 cursor-pointer"
|
||||
>
|
||||
cc-switch
|
||||
</button>
|
||||
<span className="text-content-muted"> (MIT, © Jason Young)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -245,7 +245,13 @@ export function UsageFooter({
|
||||
const compactSummary = (): { icon: string; text: string; color: string } | null => {
|
||||
const d = data[0];
|
||||
if (!d) return null;
|
||||
const isPlan = !!d.planName && d.planName !== "NewAPI";
|
||||
// A "plan" is a quota tier (used/total, e.g. 5h / weekly limit). Balance
|
||||
// entries carry only remaining + unit — their plan_name is a currency /
|
||||
// brand label and must render as an amount, not a percentage.
|
||||
const isPlan =
|
||||
!!d.planName &&
|
||||
d.planName !== "NewAPI" &&
|
||||
(d.total != null || d.used != null);
|
||||
if (d.isValid === false) {
|
||||
return { icon: "⚡", text: t("usageInvalidKey"), color: "text-red-500 dark:text-red-400" };
|
||||
}
|
||||
@@ -360,7 +366,12 @@ export function UsageFooter({
|
||||
}`}
|
||||
>
|
||||
{data.map((d, i) => {
|
||||
const isPlan = !!d.planName;
|
||||
// Same plan-vs-balance rule as compactSummary: balance rows have no
|
||||
// quota fields (total/used), so they render as an amount.
|
||||
const isPlan =
|
||||
!!d.planName &&
|
||||
d.planName !== "NewAPI" &&
|
||||
(d.total != null || d.used != null);
|
||||
const pct = isPlan ? percentOf(d) : null;
|
||||
const color =
|
||||
pct == null
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { useDashboard, type DashboardRange } from "../../hooks/useDashboard";
|
||||
import { useTranslation } from "../../i18n";
|
||||
import { fmtInt, fmtPct, fmtTime, fmtTokens, fmtUsd } from "../../lib/dashboard-format";
|
||||
@@ -420,23 +419,12 @@ export function DashboardPage() {
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Footer — meta + attribution at the end of the content flow */}
|
||||
{/* Footer — meta at the end of the content flow */}
|
||||
<div className="text-xs text-content-muted pb-2">
|
||||
<div>
|
||||
{t("footerScanned", { n: data.meta.filesScanned, m: fmtInt(data.meta.recordCount) })} ·{" "}
|
||||
{data.meta.home}
|
||||
</div>
|
||||
<div className="mt-0.5">
|
||||
{t("dashboardAttribution")}{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUrl("https://github.com/JochenYang/kimicode-dashboard")}
|
||||
className="text-blue-500 hover:text-blue-400 underline bg-transparent p-0 border-0 cursor-pointer"
|
||||
>
|
||||
kimicode-dashboard
|
||||
</button>{" "}
|
||||
{t("attributionSuffix")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { useSessions } from "../../hooks/useSessions";
|
||||
import { fmtInt, fmtTime } from "../../lib/dashboard-format";
|
||||
import { useTranslation } from "../../i18n";
|
||||
@@ -547,16 +546,6 @@ export function SessionsPage() {
|
||||
{/* Attribution */}
|
||||
<div className="text-xs text-content-muted pb-2">
|
||||
{data?.home && <>{data.home}</>}
|
||||
<br />
|
||||
{t("sessionsAttribution")}{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUrl("https://github.com/JochenYang/kimicode-dashboard")}
|
||||
className="text-blue-500 hover:text-blue-400 underline bg-transparent p-0 border-0 cursor-pointer"
|
||||
>
|
||||
kimicode-dashboard
|
||||
</button>{" "}
|
||||
{t("attributionSuffix")}
|
||||
</div>
|
||||
|
||||
{/* Confirm dialog */}
|
||||
|
||||
+1
-2
@@ -318,8 +318,7 @@ export const enTranslations: Record<TranslationKey, string> = {
|
||||
|
||||
// Dashboard — footer / attribution
|
||||
footerScanned: "Scanned {n} files · {m} records",
|
||||
dashboardAttribution: "Dashboard ported from",
|
||||
sessionsAttribution: "Session manager ported from",
|
||||
attributionPorted: "Dashboard & session manager ported from",
|
||||
attributionSuffix: "(MIT, © JochenYang)",
|
||||
|
||||
// Sessions — preview
|
||||
|
||||
+1
-2
@@ -313,8 +313,7 @@ export const zhTranslations = {
|
||||
|
||||
// Dashboard — footer / attribution
|
||||
footerScanned: "扫描 {n} 个文件 · {m} 条记录",
|
||||
dashboardAttribution: "仪表盘功能基于",
|
||||
sessionsAttribution: "会话管理功能基于",
|
||||
attributionPorted: "仪表盘与会话管理功能基于",
|
||||
attributionSuffix: "(MIT,© JochenYang)移植",
|
||||
|
||||
// Sessions — preview
|
||||
|
||||
Reference in new issue
Block a user