Files
NetMesh/domain/hostIcon.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

188 lines
6.6 KiB
TypeScript

import type { Host, HostIconColorId, HostIconColorMode, HostIconId, HostIconMode } from "./models";
export const DEFAULT_HOST_ICON_ID: HostIconId = "server";
export const DEFAULT_HOST_ICON_COLOR: HostIconColorId = "blue";
export const HOST_ICON_IDS = [
"server",
"terminal",
"database",
"cloud",
"router",
"shield",
"code",
"box",
"globe",
"cpu",
"hard-drive",
"network",
"wifi",
"lock",
"key",
"monitor",
"container",
"activity",
"zap",
"server-cog",
] as const satisfies readonly HostIconId[];
export const HOST_ICON_COLORS = [
{ id: "blue", hex: "#2563EB" },
{ id: "green", hex: "#16A34A" },
{ id: "red", hex: "#DC2626" },
{ id: "amber", hex: "#B45309" },
{ id: "purple", hex: "#9333EA" },
{ id: "cyan", hex: "#0891B2" },
{ id: "orange", hex: "#EA580C" },
{ id: "slate", hex: "#475569" },
{ id: "violet", hex: "#7C3AED" },
{ id: "pink", hex: "#DB2777" },
{ id: "rose", hex: "#E11D48" },
{ id: "lime", hex: "#65A30D" },
{ id: "teal", hex: "#0D9488" },
{ id: "sky", hex: "#0284C7" },
{ id: "indigo", hex: "#4F46E5" },
{ id: "zinc", hex: "#52525B" },
] as const satisfies readonly { id: HostIconColorId; hex: string }[];
export const HOST_ICON_DEFAULT_COLORS = {
"server": "blue",
"terminal": "slate",
"database": "cyan",
"cloud": "sky",
"router": "orange",
"shield": "green",
"code": "violet",
"box": "amber",
"globe": "teal",
"cpu": "indigo",
"hard-drive": "zinc",
"network": "lime",
"wifi": "purple",
"lock": "rose",
"key": "amber",
"monitor": "sky",
"container": "teal",
"activity": "red",
"zap": "orange",
"server-cog": "slate",
} as const satisfies Record<HostIconId, HostIconColorId>;
export type HostIconAppearance = {
iconId: HostIconId;
colorId?: HostIconColorId;
colorHex: string;
};
export type HostIconColorAppearance = {
colorId?: HostIconColorId;
colorHex: string;
};
export const isHostIconMode = (value: unknown): value is HostIconMode =>
value === "auto" || value === "custom";
export const isHostIconId = (value: unknown): value is HostIconId =>
typeof value === "string" && (HOST_ICON_IDS as readonly string[]).includes(value);
export const isHostIconColorId = (value: unknown): value is HostIconColorId =>
typeof value === "string" && HOST_ICON_COLORS.some((color) => color.id === value);
export const isHostIconColorMode = (value: unknown): value is HostIconColorMode =>
value === "auto" || value === "manual";
export const isHostIconCustomColor = (value: unknown): value is string =>
typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value);
const resolveColorHex = (colorId: HostIconColorId): string =>
HOST_ICON_COLORS.find((color) => color.id === colorId)?.hex || HOST_ICON_COLORS[0].hex;
export const resolveHostIconDefaultColorHex = (iconId: HostIconId): string =>
resolveColorHex(HOST_ICON_DEFAULT_COLORS[iconId] || DEFAULT_HOST_ICON_COLOR);
export const resolveHostIconColorAppearance = (
host: Partial<Pick<Host, "iconColorMode" | "iconColor" | "iconColorCustom">>,
): HostIconColorAppearance | null => {
const manualColor = host.iconColorMode === "manual" ||
(host.iconColorMode !== "auto" && (isHostIconColorId(host.iconColor) || isHostIconCustomColor(host.iconColorCustom)));
if (!manualColor) return null;
if (isHostIconCustomColor(host.iconColorCustom)) {
return {
colorHex: host.iconColorCustom,
};
}
return {
colorId: isHostIconColorId(host.iconColor) ? host.iconColor : DEFAULT_HOST_ICON_COLOR,
colorHex: resolveColorHex(isHostIconColorId(host.iconColor) ? host.iconColor : DEFAULT_HOST_ICON_COLOR),
};
};
export const resolveHostIconAppearance = (
host: Partial<Pick<Host, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom">>,
): HostIconAppearance | null => {
if (host.iconMode !== "custom") return null;
if (!isHostIconId(host.iconId)) return null;
const color = resolveHostIconColorAppearance(host);
return {
iconId: host.iconId,
...(color?.colorId ? { colorId: color.colorId } : {}),
colorHex: color?.colorHex || resolveHostIconDefaultColorHex(host.iconId),
};
};
export const normalizeHostIconSelection = <T extends Partial<Pick<Host, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom">>>(
host: T,
): Pick<Host, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom"> => {
const iconColorMode = host.iconColorMode === "manual" ||
(host.iconColorMode !== "auto" && (isHostIconColorId(host.iconColor) || isHostIconCustomColor(host.iconColorCustom)))
? "manual"
: undefined;
const iconColor = iconColorMode === "manual" && isHostIconColorId(host.iconColor) ? host.iconColor : undefined;
const iconColorCustom = iconColorMode === "manual" && isHostIconCustomColor(host.iconColorCustom) ? host.iconColorCustom : undefined;
const colorFields = {
...(iconColorMode ? { iconColorMode } : {}),
...(iconColor ? { iconColor } : {}),
...(iconColorCustom ? { iconColorCustom } : {}),
};
if (host.iconMode !== "custom") {
return iconColorMode ? { iconMode: "auto", ...colorFields } : {};
}
const iconId = isHostIconId(host.iconId) ? host.iconId : DEFAULT_HOST_ICON_ID;
return { iconMode: "custom", iconId, ...colorFields };
};
export const sanitizeHostIconFields = <T extends Partial<Pick<Host, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom">>>(
host: T,
): Pick<Host, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom"> => {
const iconColorMode = host.iconColorMode === "manual" ||
(host.iconColorMode !== "auto" && (isHostIconColorId(host.iconColor) || isHostIconCustomColor(host.iconColorCustom)))
? "manual"
: undefined;
const iconColor = iconColorMode === "manual" && isHostIconColorId(host.iconColor) ? host.iconColor : undefined;
const iconColorCustom = iconColorMode === "manual" && isHostIconCustomColor(host.iconColorCustom) ? host.iconColorCustom : undefined;
const colorFields = {
...(iconColorMode ? { iconColorMode } : {}),
...(iconColor ? { iconColor } : {}),
...(iconColorCustom ? { iconColorCustom } : {}),
};
if (host.iconMode !== "custom") {
return iconColorMode ? { iconMode: "auto", ...colorFields } : {};
}
if (!isHostIconId(host.iconId)) return {};
return { iconMode: "custom", iconId: host.iconId, ...colorFields };
};
export const clearHostIconAppearance = <T extends Record<string, unknown>>(
host: T,
): Omit<T, "iconMode" | "iconId" | "iconColorMode" | "iconColor" | "iconColorCustom"> => {
const {
iconMode: _iconMode,
iconId: _iconId,
iconColorMode: _iconColorMode,
iconColor: _iconColor,
iconColorCustom: _iconColorCustom,
...rest
} = host;
return rest;
};