From dcf8308c8f969330b8864daf31ce5777c0dd97b8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 2 Mar 2026 15:14:21 +0000 Subject: [PATCH] refactor(ui): share channel config extras and hint types --- src/config/schema.hints.ts | 15 +---- src/shared/config-ui-hints-types.ts | 13 +++++ ui/src/ui/types.ts | 15 +---- ui/src/ui/views/agents-panels-status-files.ts | 56 ++----------------- ui/src/ui/views/channel-config-extras.ts | 49 ++++++++++++++++ ui/src/ui/views/channels.config.ts | 27 +-------- 6 files changed, 74 insertions(+), 101 deletions(-) create mode 100644 src/shared/config-ui-hints-types.ts create mode 100644 ui/src/ui/views/channel-config-extras.ts diff --git a/src/config/schema.hints.ts b/src/config/schema.hints.ts index 05b31d695..64b7cd1f7 100644 --- a/src/config/schema.hints.ts +++ b/src/config/schema.hints.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { createSubsystemLogger } from "../logging/subsystem.js"; +import type { ConfigUiHints } from "../shared/config-ui-hints-types.js"; import { FIELD_HELP } from "./schema.help.js"; import { FIELD_LABELS } from "./schema.labels.js"; import { applyDerivedTags } from "./schema.tags.js"; @@ -7,19 +8,7 @@ import { sensitive } from "./zod-schema.sensitive.js"; const log = createSubsystemLogger("config/schema"); -export type ConfigUiHint = { - label?: string; - help?: string; - tags?: string[]; - group?: string; - order?: number; - advanced?: boolean; - sensitive?: boolean; - placeholder?: string; - itemTemplate?: unknown; -}; - -export type ConfigUiHints = Record; +export type { ConfigUiHint, ConfigUiHints } from "../shared/config-ui-hints-types.js"; const GROUP_LABELS: Record = { wizard: "Wizard", diff --git a/src/shared/config-ui-hints-types.ts b/src/shared/config-ui-hints-types.ts new file mode 100644 index 000000000..3994842d9 --- /dev/null +++ b/src/shared/config-ui-hints-types.ts @@ -0,0 +1,13 @@ +export type ConfigUiHint = { + label?: string; + help?: string; + tags?: string[]; + group?: string; + order?: number; + advanced?: boolean; + sensitive?: boolean; + placeholder?: string; + itemTemplate?: unknown; +}; + +export type ConfigUiHints = Record; diff --git a/ui/src/ui/types.ts b/ui/src/ui/types.ts index 0432efa93..3138759ef 100644 --- a/ui/src/ui/types.ts +++ b/ui/src/ui/types.ts @@ -1,4 +1,5 @@ export type UpdateAvailable = import("../../../src/infra/update-startup.js").UpdateAvailable; +import type { ConfigUiHints } from "../../../src/shared/config-ui-hints-types.js"; export type ChannelsStatusSnapshot = { ts: number; @@ -283,20 +284,6 @@ export type ConfigSnapshot = { issues?: ConfigSnapshotIssue[] | null; }; -export type ConfigUiHint = { - label?: string; - help?: string; - tags?: string[]; - group?: string; - order?: number; - advanced?: boolean; - sensitive?: boolean; - placeholder?: string; - itemTemplate?: unknown; -}; - -export type ConfigUiHints = Record; - export type ConfigSchemaResponse = { schema: unknown; uiHints: ConfigUiHints; diff --git a/ui/src/ui/views/agents-panels-status-files.ts b/ui/src/ui/views/agents-panels-status-files.ts index 23de4cb96..53b4a079a 100644 --- a/ui/src/ui/views/agents-panels-status-files.ts +++ b/ui/src/ui/views/agents-panels-status-files.ts @@ -15,6 +15,7 @@ import type { CronStatus, } from "../types.ts"; import { formatBytes, type AgentContext } from "./agents-utils.ts"; +import { resolveChannelExtras as resolveChannelExtrasFromConfig } from "./channel-config-extras.ts"; function renderAgentContextCard(context: AgentContext, subtitle: string) { return html` @@ -100,55 +101,6 @@ function resolveChannelEntries(snapshot: ChannelsStatusSnapshot | null): Channel const CHANNEL_EXTRA_FIELDS = ["groupPolicy", "streamMode", "dmPolicy"] as const; -function resolveChannelConfigValue( - configForm: Record | null, - channelId: string, -): Record | null { - if (!configForm) { - return null; - } - const channels = (configForm.channels ?? {}) as Record; - const fromChannels = channels[channelId]; - if (fromChannels && typeof fromChannels === "object") { - return fromChannels as Record; - } - const fallback = configForm[channelId]; - if (fallback && typeof fallback === "object") { - return fallback as Record; - } - return null; -} - -function formatChannelExtraValue(raw: unknown): string { - if (raw == null) { - return "n/a"; - } - if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean") { - return String(raw); - } - try { - return JSON.stringify(raw); - } catch { - return "n/a"; - } -} - -function resolveChannelExtras( - configForm: Record | null, - channelId: string, -): Array<{ label: string; value: string }> { - const value = resolveChannelConfigValue(configForm, channelId); - if (!value) { - return []; - } - return CHANNEL_EXTRA_FIELDS.flatMap((field) => { - if (!(field in value)) { - return []; - } - return [{ label: field, value: formatChannelExtraValue(value[field]) }]; - }); -} - function summarizeChannelAccounts(accounts: ChannelAccountSnapshot[]) { let connected = 0; let configured = 0; @@ -234,7 +186,11 @@ export function renderAgentChannels(params: { ? `${summary.configured} configured` : "not configured"; const enabled = summary.total ? `${summary.enabled} enabled` : "disabled"; - const extras = resolveChannelExtras(params.configForm, entry.id); + const extras = resolveChannelExtrasFromConfig({ + configForm: params.configForm, + channelId: entry.id, + fields: CHANNEL_EXTRA_FIELDS, + }); return html`
diff --git a/ui/src/ui/views/channel-config-extras.ts b/ui/src/ui/views/channel-config-extras.ts new file mode 100644 index 000000000..bd444d452 --- /dev/null +++ b/ui/src/ui/views/channel-config-extras.ts @@ -0,0 +1,49 @@ +export function resolveChannelConfigValue( + configForm: Record | null | undefined, + channelId: string, +): Record | null { + if (!configForm) { + return null; + } + const channels = (configForm.channels ?? {}) as Record; + const fromChannels = channels[channelId]; + if (fromChannels && typeof fromChannels === "object") { + return fromChannels as Record; + } + const fallback = configForm[channelId]; + if (fallback && typeof fallback === "object") { + return fallback as Record; + } + return null; +} + +export function formatChannelExtraValue(raw: unknown): string { + if (raw == null) { + return "n/a"; + } + if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean") { + return String(raw); + } + try { + return JSON.stringify(raw); + } catch { + return "n/a"; + } +} + +export function resolveChannelExtras(params: { + configForm: Record | null | undefined; + channelId: string; + fields: readonly string[]; +}): Array<{ label: string; value: string }> { + const value = resolveChannelConfigValue(params.configForm, params.channelId); + if (!value) { + return []; + } + return params.fields.flatMap((field) => { + if (!(field in value)) { + return []; + } + return [{ label: field, value: formatChannelExtraValue(value[field]) }]; + }); +} diff --git a/ui/src/ui/views/channels.config.ts b/ui/src/ui/views/channels.config.ts index b94b75013..303756899 100644 --- a/ui/src/ui/views/channels.config.ts +++ b/ui/src/ui/views/channels.config.ts @@ -1,5 +1,6 @@ import { html } from "lit"; import type { ConfigUiHints } from "../types.ts"; +import { formatChannelExtraValue, resolveChannelConfigValue } from "./channel-config-extras.ts"; import type { ChannelsProps } from "./channels.types.ts"; import { analyzeConfigSchema, renderNode, schemaType, type JsonSchema } from "./config-form.ts"; @@ -52,33 +53,11 @@ function resolveChannelValue( config: Record, channelId: string, ): Record { - const channels = (config.channels ?? {}) as Record; - const fromChannels = channels[channelId]; - const fallback = config[channelId]; - const resolved = - (fromChannels && typeof fromChannels === "object" - ? (fromChannels as Record) - : null) ?? - (fallback && typeof fallback === "object" ? (fallback as Record) : null); - return resolved ?? {}; + return resolveChannelConfigValue(config, channelId) ?? {}; } const EXTRA_CHANNEL_FIELDS = ["groupPolicy", "streamMode", "dmPolicy"] as const; -function formatExtraValue(raw: unknown): string { - if (raw == null) { - return "n/a"; - } - if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean") { - return String(raw); - } - try { - return JSON.stringify(raw); - } catch { - return "n/a"; - } -} - function renderExtraChannelFields(value: Record) { const entries = EXTRA_CHANNEL_FIELDS.flatMap((field) => { if (!(field in value)) { @@ -95,7 +74,7 @@ function renderExtraChannelFields(value: Record) { ([field, raw]) => html`
${field} - ${formatExtraValue(raw)} + ${formatChannelExtraValue(raw)}
`, )}