mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-15 19:21:08 +00:00
refactor: dedupe provider ui trimmed readers
This commit is contained in:
@@ -16,7 +16,7 @@ import { loadSessions } from "./controllers/sessions.ts";
|
||||
import { icons } from "./icons.ts";
|
||||
import { iconForTab, pathForTab, titleForTab, type Tab } from "./navigation.ts";
|
||||
import { parseAgentSessionKey } from "./session-key.ts";
|
||||
import { normalizeLowercaseStringOrEmpty } from "./string-coerce.ts";
|
||||
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "./string-coerce.ts";
|
||||
import type { ThemeMode } from "./theme.ts";
|
||||
import {
|
||||
listThinkingLevelLabels,
|
||||
@@ -34,11 +34,11 @@ function resolveSidebarChatSessionKey(state: AppViewState): string {
|
||||
const snapshot = state.hello?.snapshot as
|
||||
| { sessionDefaults?: SessionDefaultsSnapshot }
|
||||
| undefined;
|
||||
const mainSessionKey = snapshot?.sessionDefaults?.mainSessionKey?.trim();
|
||||
const mainSessionKey = normalizeOptionalString(snapshot?.sessionDefaults?.mainSessionKey);
|
||||
if (mainSessionKey) {
|
||||
return mainSessionKey;
|
||||
}
|
||||
const mainKey = snapshot?.sessionDefaults?.mainKey?.trim();
|
||||
const mainKey = normalizeOptionalString(snapshot?.sessionDefaults?.mainKey);
|
||||
if (mainKey) {
|
||||
return mainKey;
|
||||
}
|
||||
@@ -857,8 +857,8 @@ export function resolveSessionDisplayName(
|
||||
key: string,
|
||||
row?: SessionsListResult["sessions"][number],
|
||||
): string {
|
||||
const label = row?.label?.trim() || "";
|
||||
const displayName = row?.displayName?.trim() || "";
|
||||
const label = normalizeOptionalString(row?.label) ?? "";
|
||||
const displayName = normalizeOptionalString(row?.displayName) ?? "";
|
||||
const { prefix, fallbackName } = parseSessionKey(key);
|
||||
|
||||
const applyTypedPrefix = (name: string): string => {
|
||||
@@ -951,7 +951,7 @@ export function resolveSessionOptionGroups(
|
||||
resolveAgentGroupLabel(state, parsed.agentId),
|
||||
)
|
||||
: ensureGroup("other", "Other Sessions");
|
||||
const scopeLabel = parsed?.rest?.trim() || key;
|
||||
const scopeLabel = normalizeOptionalString(parsed?.rest) ?? key;
|
||||
const label = resolveSessionScopedOptionLabel(key, row, parsed?.rest);
|
||||
group.options.push({
|
||||
key,
|
||||
@@ -1065,7 +1065,8 @@ function resolveAgentGroupLabel(state: AppViewState, agentIdRaw: string): string
|
||||
const agent = (state.agentsList?.agents ?? []).find(
|
||||
(entry) => normalizeLowercaseStringOrEmpty(entry.id) === normalized,
|
||||
);
|
||||
const name = agent?.identity?.name?.trim() || agent?.name?.trim() || "";
|
||||
const name =
|
||||
normalizeOptionalString(agent?.identity?.name) ?? normalizeOptionalString(agent?.name) ?? "";
|
||||
return name && name !== agentIdRaw ? `${name} (${agentIdRaw})` : agentIdRaw;
|
||||
}
|
||||
|
||||
@@ -1074,13 +1075,13 @@ function resolveSessionScopedOptionLabel(
|
||||
row?: SessionsListResult["sessions"][number],
|
||||
rest?: string,
|
||||
) {
|
||||
const base = rest?.trim() || key;
|
||||
const base = normalizeOptionalString(rest) ?? key;
|
||||
if (!row) {
|
||||
return base;
|
||||
}
|
||||
|
||||
const label = row.label?.trim() || "";
|
||||
const displayName = row.displayName?.trim() || "";
|
||||
const label = normalizeOptionalString(row.label) ?? "";
|
||||
const displayName = normalizeOptionalString(row.displayName) ?? "";
|
||||
if ((label && label !== key) || (displayName && displayName !== key)) {
|
||||
return resolveSessionDisplayName(key, row);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,11 @@ import {
|
||||
parseAgentSessionKey,
|
||||
resolveAgentIdFromSessionKey,
|
||||
} from "./session-key.ts";
|
||||
import { normalizeLowercaseStringOrEmpty } from "./string-coerce.ts";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
normalizeStringifiedOptionalString,
|
||||
} from "./string-coerce.ts";
|
||||
import { agentLogoUrl } from "./views/agents-utils.ts";
|
||||
import {
|
||||
resolveAgentConfig,
|
||||
@@ -208,7 +212,7 @@ function isHttpUrl(value: string): boolean {
|
||||
}
|
||||
|
||||
function normalizeSuggestionValue(value: unknown): string {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
return normalizeOptionalString(value) ?? "";
|
||||
}
|
||||
|
||||
function uniquePreserveOrder(values: string[]): string[] {
|
||||
@@ -408,9 +412,7 @@ export function renderApp(state: AppViewState) {
|
||||
new Set(
|
||||
[
|
||||
...(state.agentsList?.agents?.map((entry) => entry.id.trim()) ?? []),
|
||||
...state.cronJobs
|
||||
.map((job) => (typeof job.agentId === "string" ? job.agentId.trim() : ""))
|
||||
.filter(Boolean),
|
||||
...state.cronJobs.map((job) => normalizeOptionalString(job.agentId) ?? "").filter(Boolean),
|
||||
].filter(Boolean),
|
||||
),
|
||||
);
|
||||
@@ -424,7 +426,7 @@ export function renderApp(state: AppViewState) {
|
||||
if (job.payload.kind !== "agentTurn" || typeof job.payload.model !== "string") {
|
||||
return "";
|
||||
}
|
||||
return job.payload.model.trim();
|
||||
return normalizeOptionalString(job.payload.model) ?? "";
|
||||
})
|
||||
.filter(Boolean),
|
||||
].filter(Boolean),
|
||||
@@ -1289,7 +1291,7 @@ export function renderApp(state: AppViewState) {
|
||||
const entry = Array.isArray(list)
|
||||
? (list[index] as { skills?: unknown })
|
||||
: undefined;
|
||||
const normalizedSkill = skillName.trim();
|
||||
const normalizedSkill = normalizeOptionalString(skillName) ?? "";
|
||||
if (!normalizedSkill) {
|
||||
return;
|
||||
}
|
||||
@@ -1297,7 +1299,9 @@ export function renderApp(state: AppViewState) {
|
||||
state.agentSkillsReport?.skills?.map((skill) => skill.name).filter(Boolean) ??
|
||||
[];
|
||||
const existing = Array.isArray(entry?.skills)
|
||||
? entry.skills.map((name) => String(name).trim()).filter(Boolean)
|
||||
? entry.skills
|
||||
.map((name) => normalizeStringifiedOptionalString(name) ?? "")
|
||||
.filter(Boolean)
|
||||
: undefined;
|
||||
const base = existing ?? allSkills;
|
||||
const next = new Set(base);
|
||||
|
||||
@@ -6,6 +6,16 @@ export function normalizeOptionalString(value: unknown): string | undefined {
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function normalizeStringifiedOptionalString(value: unknown): string | undefined {
|
||||
if (typeof value === "string") {
|
||||
return normalizeOptionalString(value);
|
||||
}
|
||||
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
||||
return normalizeOptionalString(String(value));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function normalizeOptionalLowercaseString(value: unknown): string | undefined {
|
||||
return normalizeOptionalString(value)?.toLowerCase();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
normalizeToolName,
|
||||
resolveToolProfilePolicy,
|
||||
} from "../../../../src/agents/tool-policy-shared.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../string-coerce.ts";
|
||||
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "../string-coerce.ts";
|
||||
import type {
|
||||
AgentIdentityResult,
|
||||
AgentsFilesListResult,
|
||||
@@ -193,7 +193,9 @@ export function normalizeAgentLabel(agent: {
|
||||
name?: string;
|
||||
identity?: { name?: string };
|
||||
}) {
|
||||
return agent.name?.trim() || agent.identity?.name?.trim() || agent.id;
|
||||
return (
|
||||
normalizeOptionalString(agent.name) ?? normalizeOptionalString(agent.identity?.name) ?? agent.id
|
||||
);
|
||||
}
|
||||
|
||||
const AVATAR_URL_RE = /^(https?:\/\/|data:image\/|\/)/i;
|
||||
@@ -203,9 +205,9 @@ export function resolveAgentAvatarUrl(
|
||||
agentIdentity?: AgentIdentityResult | null,
|
||||
): string | null {
|
||||
const candidates = [
|
||||
agentIdentity?.avatar?.trim(),
|
||||
agent.identity?.avatarUrl?.trim(),
|
||||
agent.identity?.avatar?.trim(),
|
||||
normalizeOptionalString(agentIdentity?.avatar),
|
||||
normalizeOptionalString(agent.identity?.avatarUrl),
|
||||
normalizeOptionalString(agent.identity?.avatar),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate) {
|
||||
@@ -219,7 +221,7 @@ export function resolveAgentAvatarUrl(
|
||||
}
|
||||
|
||||
export function agentLogoUrl(basePath: string): string {
|
||||
const base = basePath?.trim() ? basePath.replace(/\/$/, "") : "";
|
||||
const base = normalizeOptionalString(basePath)?.replace(/\/$/, "") ?? "";
|
||||
return base ? `${base}/favicon.svg` : "favicon.svg";
|
||||
}
|
||||
|
||||
@@ -251,19 +253,19 @@ export function resolveAgentEmoji(
|
||||
agent: { identity?: { emoji?: string; avatar?: string } },
|
||||
agentIdentity?: AgentIdentityResult | null,
|
||||
) {
|
||||
const identityEmoji = agentIdentity?.emoji?.trim();
|
||||
const identityEmoji = normalizeOptionalString(agentIdentity?.emoji);
|
||||
if (identityEmoji && isLikelyEmoji(identityEmoji)) {
|
||||
return identityEmoji;
|
||||
}
|
||||
const agentEmoji = agent.identity?.emoji?.trim();
|
||||
const agentEmoji = normalizeOptionalString(agent.identity?.emoji);
|
||||
if (agentEmoji && isLikelyEmoji(agentEmoji)) {
|
||||
return agentEmoji;
|
||||
}
|
||||
const identityAvatar = agentIdentity?.avatar?.trim();
|
||||
const identityAvatar = normalizeOptionalString(agentIdentity?.avatar);
|
||||
if (identityAvatar && isLikelyEmoji(identityAvatar)) {
|
||||
return identityAvatar;
|
||||
}
|
||||
const avatar = agent.identity?.avatar?.trim();
|
||||
const avatar = normalizeOptionalString(agent.identity?.avatar);
|
||||
if (avatar && isLikelyEmoji(avatar)) {
|
||||
return avatar;
|
||||
}
|
||||
@@ -341,9 +343,9 @@ export function buildAgentContext(
|
||||
? resolveModelLabel(config.defaults?.model)
|
||||
: resolveModelLabel(agent.model);
|
||||
const identityName =
|
||||
agentIdentity?.name?.trim() ||
|
||||
agent.identity?.name?.trim() ||
|
||||
agent.name?.trim() ||
|
||||
normalizeOptionalString(agentIdentity?.name) ||
|
||||
normalizeOptionalString(agent.identity?.name) ||
|
||||
normalizeOptionalString(agent.name) ||
|
||||
config.entry?.name ||
|
||||
agent.id;
|
||||
const identityAvatar = resolveAgentAvatarUrl(agent, agentIdentity) ? "custom" : "—";
|
||||
@@ -364,11 +366,11 @@ export function resolveModelLabel(model?: unknown): string {
|
||||
return "-";
|
||||
}
|
||||
if (typeof model === "string") {
|
||||
return model.trim() || "-";
|
||||
return normalizeOptionalString(model) || "-";
|
||||
}
|
||||
if (typeof model === "object" && model) {
|
||||
const record = model as { primary?: string; fallbacks?: string[] };
|
||||
const primary = record.primary?.trim();
|
||||
const primary = normalizeOptionalString(record.primary);
|
||||
if (primary) {
|
||||
const fallbackCount = Array.isArray(record.fallbacks) ? record.fallbacks.length : 0;
|
||||
return fallbackCount > 0 ? `${primary} (+${fallbackCount} fallback)` : primary;
|
||||
@@ -387,7 +389,7 @@ export function resolveModelPrimary(model?: unknown): string | null {
|
||||
return null;
|
||||
}
|
||||
if (typeof model === "string") {
|
||||
const trimmed = model.trim();
|
||||
const trimmed = normalizeOptionalString(model);
|
||||
return trimmed || null;
|
||||
}
|
||||
if (typeof model === "object" && model) {
|
||||
@@ -402,7 +404,7 @@ export function resolveModelPrimary(model?: unknown): string | null {
|
||||
: typeof record.value === "string"
|
||||
? record.value
|
||||
: null;
|
||||
const primary = candidate?.trim();
|
||||
const primary = normalizeOptionalString(candidate);
|
||||
return primary || null;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
} from "../controllers/devices.ts";
|
||||
import type { ExecApprovalsFile, ExecApprovalsSnapshot } from "../controllers/exec-approvals.ts";
|
||||
import { formatRelativeTimestamp, formatList } from "../format.ts";
|
||||
import { normalizeOptionalString } from "../string-coerce.ts";
|
||||
import { renderExecApprovals, resolveExecApprovalsState } from "./nodes-exec-approvals.ts";
|
||||
import { resolveConfigAgents, resolveNodeTargets, type NodeTargetOption } from "./nodes-shared.ts";
|
||||
export type NodesProps = {
|
||||
@@ -111,9 +112,9 @@ function renderDevices(props: NodesProps) {
|
||||
}
|
||||
|
||||
function renderPendingDevice(req: PendingDevice, props: NodesProps) {
|
||||
const name = req.displayName?.trim() || req.deviceId;
|
||||
const name = normalizeOptionalString(req.displayName) || req.deviceId;
|
||||
const age = typeof req.ts === "number" ? formatRelativeTimestamp(req.ts) : t("common.na");
|
||||
const roleValue = req.role?.trim() || formatList(req.roles);
|
||||
const roleValue = normalizeOptionalString(req.role) || formatList(req.roles);
|
||||
const scopesValue = formatList(req.scopes);
|
||||
const repair = req.isRepair ? " · repair" : "";
|
||||
const ip = req.remoteIp ? ` · ${req.remoteIp}` : "";
|
||||
@@ -141,7 +142,7 @@ function renderPendingDevice(req: PendingDevice, props: NodesProps) {
|
||||
}
|
||||
|
||||
function renderPairedDevice(device: PairedDevice, props: NodesProps) {
|
||||
const name = device.displayName?.trim() || device.deviceId;
|
||||
const name = normalizeOptionalString(device.displayName) || device.deviceId;
|
||||
const ip = device.remoteIp ? ` · ${device.remoteIp}` : "";
|
||||
const roles = `roles: ${formatList(device.roles)}`;
|
||||
const scopes = `scopes: ${formatList(device.scopes)}`;
|
||||
|
||||
Reference in New Issue
Block a user