refactor(ui): share channel config extras and hint types

This commit is contained in:
Peter Steinberger
2026-03-02 15:14:21 +00:00
parent d212721df1
commit dcf8308c8f
6 changed files with 74 additions and 101 deletions

View File

@@ -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<string, ConfigUiHint>;
export type { ConfigUiHint, ConfigUiHints } from "../shared/config-ui-hints-types.js";
const GROUP_LABELS: Record<string, string> = {
wizard: "Wizard",

View File

@@ -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<string, ConfigUiHint>;

View File

@@ -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<string, ConfigUiHint>;
export type ConfigSchemaResponse = {
schema: unknown;
uiHints: ConfigUiHints;

View File

@@ -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<string, unknown> | null,
channelId: string,
): Record<string, unknown> | null {
if (!configForm) {
return null;
}
const channels = (configForm.channels ?? {}) as Record<string, unknown>;
const fromChannels = channels[channelId];
if (fromChannels && typeof fromChannels === "object") {
return fromChannels as Record<string, unknown>;
}
const fallback = configForm[channelId];
if (fallback && typeof fallback === "object") {
return fallback as Record<string, unknown>;
}
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<string, unknown> | 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`
<div class="list-item">
<div class="list-main">

View File

@@ -0,0 +1,49 @@
export function resolveChannelConfigValue(
configForm: Record<string, unknown> | null | undefined,
channelId: string,
): Record<string, unknown> | null {
if (!configForm) {
return null;
}
const channels = (configForm.channels ?? {}) as Record<string, unknown>;
const fromChannels = channels[channelId];
if (fromChannels && typeof fromChannels === "object") {
return fromChannels as Record<string, unknown>;
}
const fallback = configForm[channelId];
if (fallback && typeof fallback === "object") {
return fallback as Record<string, unknown>;
}
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<string, unknown> | 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]) }];
});
}

View File

@@ -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<string, unknown>,
channelId: string,
): Record<string, unknown> {
const channels = (config.channels ?? {}) as Record<string, unknown>;
const fromChannels = channels[channelId];
const fallback = config[channelId];
const resolved =
(fromChannels && typeof fromChannels === "object"
? (fromChannels as Record<string, unknown>)
: null) ??
(fallback && typeof fallback === "object" ? (fallback as Record<string, unknown>) : 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<string, unknown>) {
const entries = EXTRA_CHANNEL_FIELDS.flatMap((field) => {
if (!(field in value)) {
@@ -95,7 +74,7 @@ function renderExtraChannelFields(value: Record<string, unknown>) {
([field, raw]) => html`
<div>
<span class="label">${field}</span>
<span>${formatExtraValue(raw)}</span>
<span>${formatChannelExtraValue(raw)}</span>
</div>
`,
)}