mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
feat(tray): color the mode name in the panel with its theme color
The app rows in the tray panel now show Direct / Route / Aggregation in the same mode colors as the main window (direct blue, route teal, aggregation purple, with the dimmed dark-theme variants), so the modes are easy to tell apart at a glance. The panel model carries a machine-readable modeKind next to the localized mode name.
This commit is contained in:
1 parent
e003829be8
commit
e2af66ca61
4 files changed
+48
-4
No files matched your search
@@ -2056,6 +2056,8 @@ pub struct PanelApp {
|
||||
pub current: Option<String>,
|
||||
/// 直连 / 路由 / 聚合。
|
||||
pub mode: &'static str,
|
||||
/// 同一模式的机器可读值(`direct` / `route` / `stack`),面板按它给模式名上主题色。
|
||||
pub mode_kind: &'static str,
|
||||
/// 子面板标题下的说明(「路由 · 故障转移开启中」等)。
|
||||
pub header: Option<String>,
|
||||
pub attention: Option<&'static str>,
|
||||
@@ -2280,6 +2282,11 @@ pub(crate) fn collect_panel_model(app_state: &AppState) -> Result<PanelModel, Ap
|
||||
TrayMode::Route | TrayMode::Failover => texts.mode_route,
|
||||
TrayMode::Stack => texts.mode_stack,
|
||||
},
|
||||
mode_kind: match snapshot.mode {
|
||||
TrayMode::Direct => "direct",
|
||||
TrayMode::Route | TrayMode::Failover => "route",
|
||||
TrayMode::Stack => "stack",
|
||||
},
|
||||
header,
|
||||
attention: snapshot.needs_attention.then_some(texts.needs_attention),
|
||||
quota,
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import { AppGlyph } from "@/components/shell/AppGlyph";
|
||||
import { ProviderIcon } from "@/components/ProviderIcon";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { AppMode } from "@/types/proxy";
|
||||
import {
|
||||
CHANGED_EVENT,
|
||||
SHOW_EVENT,
|
||||
@@ -167,6 +168,13 @@ function QuotaBlock({
|
||||
|
||||
// ─── 主面板的应用行 ───────────────────────────────────────────────────────────
|
||||
|
||||
// 模式名用主界面的模式色(与 ModeTabs 的 MODE_TONE.text 同一组 token),一眼分出直连 / 路由 / 聚合。
|
||||
const MODE_TEXT: Record<AppMode, string> = {
|
||||
direct: "text-direct-text",
|
||||
route: "text-route-text",
|
||||
stack: "text-stack-text",
|
||||
};
|
||||
|
||||
function AppRow({
|
||||
app,
|
||||
texts,
|
||||
@@ -185,9 +193,6 @@ function AppRow({
|
||||
onOpen: () => void;
|
||||
}) {
|
||||
const Chevron = side === "left" ? ChevronLeft : ChevronRight;
|
||||
const subtitle = app.addId
|
||||
? app.addText
|
||||
: [app.current, app.mode].filter(Boolean).join(" · ");
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
@@ -222,7 +227,16 @@ function AppRow({
|
||||
)}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-muted-foreground">
|
||||
{subtitle}
|
||||
{app.addId ? (
|
||||
app.addText
|
||||
) : (
|
||||
<>
|
||||
{app.current && `${app.current} · `}
|
||||
<span className={cn("font-medium", MODE_TEXT[app.modeKind])}>
|
||||
{app.mode}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
{app.quota && <QuotaBlock quota={app.quota} texts={texts} />}
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type { AppId } from "@/lib/api/types";
|
||||
import type { AppMode } from "@/types/proxy";
|
||||
|
||||
/** 托盘面板的数据(后端 `tray_panel::TrayPanelState`)。 */
|
||||
export interface PanelTexts {
|
||||
@@ -63,6 +64,7 @@ export interface PanelApp {
|
||||
name: string;
|
||||
current: string | null;
|
||||
mode: string;
|
||||
modeKind: AppMode;
|
||||
header: string | null;
|
||||
attention: string | null;
|
||||
quota: PanelQuota | null;
|
||||
|
||||
@@ -54,6 +54,7 @@ const state: TrayPanelState = {
|
||||
name: "Claude Code",
|
||||
current: "default",
|
||||
mode: "Direct",
|
||||
modeKind: "direct",
|
||||
header: null,
|
||||
attention: null,
|
||||
quota: null,
|
||||
@@ -242,6 +243,26 @@ describe("TrayPanel window layout", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["direct", "Direct", "text-direct-text"],
|
||||
["route", "Route", "text-route-text"],
|
||||
["stack", "Aggregation", "text-stack-text"],
|
||||
] as const)(
|
||||
"colors the %s mode name with its theme color",
|
||||
async (modeKind, mode, className) => {
|
||||
vi.mocked(trayPanelApi.state).mockResolvedValue({
|
||||
...state,
|
||||
apps: [{ ...state.apps[0], mode, modeKind }],
|
||||
});
|
||||
vi.mocked(trayPanelApi.placement).mockResolvedValue(windows);
|
||||
renderPanel();
|
||||
await screen.findByRole("button", {
|
||||
name: `Claude Code default · ${mode}`,
|
||||
});
|
||||
expect(screen.getByText(mode)).toHaveClass(className);
|
||||
},
|
||||
);
|
||||
|
||||
it("opens Windows top/left taskbars toward the opposite edges", async () => {
|
||||
const { container } = await openPanel(windows);
|
||||
await act(async () => {
|
||||
|
||||
Reference in new issue
Block a user