Skip to content

Commit 60f0749

Browse files
authored
refactor(ui): remove duplicate helper paths (#101647)
1 parent 2ba622c commit 60f0749

9 files changed

Lines changed: 59 additions & 195 deletions

File tree

ui/src/app-navigation.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
subtitleForRoute,
88
titleForRoute,
99
} from "./app-navigation.ts";
10+
import { normalizePath } from "./app-route-paths.ts";
1011
import {
1112
inferBasePathFromPathname,
1213
normalizeBasePath,
13-
normalizePath,
1414
pathForRoute,
1515
routeIdFromPath,
1616
type RouteId,

ui/src/app-routes.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,11 @@ export async function startApplicationRouter(
8383
}
8484

8585
export {
86-
APP_ROUTE_DEFINITIONS,
8786
APP_ROUTE_IDS,
8887
inferBasePathFromPathname,
8988
isRouteId,
9089
locationForRoute,
9190
normalizeBasePath,
92-
normalizePath,
9391
pathForRoute,
9492
routeIdFromPath,
9593
type RouteId,

ui/src/app/user-identity.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@ export function normalizeLocalUserIdentity(
3939
};
4040
}
4141

42-
export function hasLocalUserIdentity(identity: LocalUserIdentity): boolean {
43-
return Boolean(identity.name || identity.avatar);
44-
}
45-
4642
export function resolveLocalUserName(
4743
input?: Partial<LocalUserIdentity> | null,
4844
fallback = "You",

ui/src/lib/agents/display.test.ts

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import {
99
assistantAvatarFallbackUrl,
1010
buildAgentContext,
1111
formatBytes,
12-
resolveConfiguredCronModelSuggestions,
1312
resolveEffectiveModelFallbacks,
14-
sortLocaleStrings,
1513
} from "./display.ts";
1614

1715
describe("formatBytes", () => {
@@ -64,60 +62,6 @@ describe("resolveEffectiveModelFallbacks", () => {
6462
});
6563
});
6664

67-
describe("resolveConfiguredCronModelSuggestions", () => {
68-
it("collects defaults primary/fallbacks, alias map keys, and per-agent model entries", () => {
69-
const result = resolveConfiguredCronModelSuggestions({
70-
agents: {
71-
defaults: {
72-
model: {
73-
primary: "openai/gpt-5.2",
74-
fallbacks: ["google/gemini-2.5-pro", "openai/gpt-5.2-mini"],
75-
},
76-
models: {
77-
"anthropic/claude-sonnet-4-5": { alias: "smart" },
78-
"openai/gpt-5.2": { alias: "main" },
79-
},
80-
},
81-
list: {
82-
writer: {
83-
model: { primary: "xai/grok-4", fallbacks: ["openai/gpt-5.2-mini"] },
84-
},
85-
planner: {
86-
model: "google/gemini-2.5-flash",
87-
},
88-
},
89-
},
90-
});
91-
92-
expect(result).toEqual([
93-
"anthropic/claude-sonnet-4-5",
94-
"google/gemini-2.5-flash",
95-
"google/gemini-2.5-pro",
96-
"openai/gpt-5.2",
97-
"openai/gpt-5.2-mini",
98-
"xai/grok-4",
99-
]);
100-
});
101-
102-
it("returns empty array for invalid or missing config shape", () => {
103-
expect(resolveConfiguredCronModelSuggestions(null)).toStrictEqual([]);
104-
expect(resolveConfiguredCronModelSuggestions({})).toStrictEqual([]);
105-
expect(
106-
resolveConfiguredCronModelSuggestions({ agents: { defaults: { model: "" } } }),
107-
).toStrictEqual([]);
108-
});
109-
});
110-
111-
describe("sortLocaleStrings", () => {
112-
it("sorts values using localeCompare without relying on Array.prototype.toSorted", () => {
113-
expect(sortLocaleStrings(["z", "b", "a"])).toEqual(["a", "b", "z"]);
114-
});
115-
116-
it("accepts any iterable input, including sets", () => {
117-
expect(sortLocaleStrings(new Set(["beta", "alpha"]))).toEqual(["alpha", "beta"]);
118-
});
119-
});
120-
12165
describe("assistantAvatarFallbackUrl", () => {
12266
it("uses the bundled Molty png for assistant profile fallbacks", () => {
12367
expect(assistantAvatarFallbackUrl("/ui")).toBe("/ui/apple-touch-icon.png");

ui/src/lib/agents/display.ts

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const FALLBACK_TOOL_SECTIONS: AgentToolSection[] = [
121121
},
122122
];
123123

124-
export const PROFILE_OPTIONS = [
124+
const PROFILE_OPTIONS = [
125125
{ id: "minimal", label: "Minimal" },
126126
{ id: "coding", label: "Coding" },
127127
{ id: "messaging", label: "Messaging" },
@@ -208,8 +208,6 @@ export function assistantAvatarFallbackUrl(basePath: string): string {
208208
return controlUiPublicAssetPath("apple-touch-icon.png", basePath);
209209
}
210210

211-
export { resolveAssistantTextAvatar };
212-
213211
function resolveAgentTextAvatar(
214212
agent: { identity?: { emoji?: string; avatar?: string } },
215213
agentIdentity?: AgentIdentityResult | null,
@@ -392,114 +390,6 @@ export function resolveEffectiveModelFallbacks(
392390
return resolveModelFallbacks(entryModel) ?? resolveModelFallbacks(defaultModel);
393391
}
394392

395-
function addModelId(target: Set<string>, value: unknown) {
396-
if (typeof value !== "string") {
397-
return;
398-
}
399-
const trimmed = value.trim();
400-
if (!trimmed) {
401-
return;
402-
}
403-
target.add(trimmed);
404-
}
405-
406-
function addModelConfigIds(target: Set<string>, modelConfig: unknown) {
407-
if (!modelConfig) {
408-
return;
409-
}
410-
if (typeof modelConfig === "string") {
411-
addModelId(target, modelConfig);
412-
return;
413-
}
414-
if (typeof modelConfig !== "object") {
415-
return;
416-
}
417-
const record = modelConfig as Record<string, unknown>;
418-
addModelId(target, record.primary);
419-
addModelId(target, record.model);
420-
addModelId(target, record.id);
421-
addModelId(target, record.value);
422-
const fallbacks = Array.isArray(record.fallbacks)
423-
? record.fallbacks
424-
: Array.isArray(record.fallback)
425-
? record.fallback
426-
: [];
427-
for (const fallback of fallbacks) {
428-
addModelId(target, fallback);
429-
}
430-
}
431-
432-
export function sortLocaleStrings(values: Iterable<string>): string[] {
433-
const sorted = Array.from(values);
434-
const buffer = Array.from({ length: sorted.length }, () => "");
435-
436-
const merge = (left: number, middle: number, right: number): void => {
437-
let i = left;
438-
let j = middle;
439-
let k = left;
440-
while (i < middle && j < right) {
441-
buffer[k++] = sorted[i].localeCompare(sorted[j]) <= 0 ? sorted[i++] : sorted[j++];
442-
}
443-
while (i < middle) {
444-
buffer[k++] = sorted[i++];
445-
}
446-
while (j < right) {
447-
buffer[k++] = sorted[j++];
448-
}
449-
for (let idx = left; idx < right; idx += 1) {
450-
sorted[idx] = buffer[idx];
451-
}
452-
};
453-
454-
const sortRange = (left: number, right: number): void => {
455-
if (right - left <= 1) {
456-
return;
457-
}
458-
459-
const middle = (left + right) >>> 1;
460-
sortRange(left, middle);
461-
sortRange(middle, right);
462-
merge(left, middle, right);
463-
};
464-
465-
sortRange(0, sorted.length);
466-
return sorted;
467-
}
468-
469-
export function resolveConfiguredCronModelSuggestions(
470-
configForm: Record<string, unknown> | null,
471-
): string[] {
472-
if (!configForm || typeof configForm !== "object") {
473-
return [];
474-
}
475-
const agents = (configForm as { agents?: unknown }).agents;
476-
if (!agents || typeof agents !== "object") {
477-
return [];
478-
}
479-
const out = new Set<string>();
480-
const defaults = (agents as { defaults?: unknown }).defaults;
481-
if (defaults && typeof defaults === "object") {
482-
const defaultsRecord = defaults as Record<string, unknown>;
483-
addModelConfigIds(out, defaultsRecord.model);
484-
const defaultsModels = defaultsRecord.models;
485-
if (defaultsModels && typeof defaultsModels === "object") {
486-
for (const modelId of Object.keys(defaultsModels as Record<string, unknown>)) {
487-
addModelId(out, modelId);
488-
}
489-
}
490-
}
491-
const list = (agents as { list?: unknown }).list;
492-
if (list && typeof list === "object") {
493-
for (const entry of Object.values(list as Record<string, unknown>)) {
494-
if (!entry || typeof entry !== "object") {
495-
continue;
496-
}
497-
addModelConfigIds(out, (entry as Record<string, unknown>).model);
498-
}
499-
}
500-
return sortLocaleStrings(out);
501-
}
502-
503393
export function parseFallbackList(value: string): string[] {
504394
return value
505395
.split(",")

ui/src/lib/cron/index.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
loadCronRuns,
1010
loadMoreCronRuns,
1111
normalizeCronFormState,
12+
resolveConfiguredCronModelSuggestions,
1213
runCronJob,
1314
startCronEdit,
1415
startCronClone,
@@ -108,6 +109,48 @@ type EmptyCronListResponse = {
108109
};
109110

110111
describe("cron controller", () => {
112+
it("collects configured model suggestions from defaults and per-agent entries", () => {
113+
expect(
114+
resolveConfiguredCronModelSuggestions({
115+
agents: {
116+
defaults: {
117+
model: {
118+
primary: "openai/gpt-5.2",
119+
fallbacks: ["google/gemini-2.5-pro", "openai/gpt-5.2-mini"],
120+
},
121+
models: {
122+
"anthropic/claude-sonnet-4-5": { alias: "smart" },
123+
"openai/gpt-5.2": { alias: "main" },
124+
},
125+
},
126+
list: {
127+
writer: {
128+
model: { primary: "xai/grok-4", fallbacks: ["openai/gpt-5.2-mini"] },
129+
},
130+
planner: {
131+
model: "google/gemini-2.5-flash",
132+
},
133+
},
134+
},
135+
}),
136+
).toEqual([
137+
"anthropic/claude-sonnet-4-5",
138+
"google/gemini-2.5-flash",
139+
"google/gemini-2.5-pro",
140+
"openai/gpt-5.2",
141+
"openai/gpt-5.2-mini",
142+
"xai/grok-4",
143+
]);
144+
});
145+
146+
it("returns no configured model suggestions for invalid or missing config", () => {
147+
expect(resolveConfiguredCronModelSuggestions(null)).toStrictEqual([]);
148+
expect(resolveConfiguredCronModelSuggestions({})).toStrictEqual([]);
149+
expect(
150+
resolveConfiguredCronModelSuggestions({ agents: { defaults: { model: "" } } }),
151+
).toStrictEqual([]);
152+
});
153+
111154
it("loads model suggestions from the configured model view", async () => {
112155
const request = vi.fn(async () => ({
113156
models: [

ui/src/pages/chat/chat-avatar.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ import {
88
resolveLocalUserAvatarUrl,
99
resolveLocalUserName,
1010
} from "../../app/user-identity.ts";
11-
import {
12-
assistantAvatarFallbackUrl,
13-
resolveAssistantTextAvatar,
14-
} from "../../lib/agents/display.ts";
11+
import { assistantAvatarFallbackUrl } from "../../lib/agents/display.ts";
1512
import type { AssistantIdentity } from "../../lib/assistant-identity.ts";
16-
import { isRenderableControlUiAvatarUrl } from "../../lib/avatar.ts";
13+
import { isRenderableControlUiAvatarUrl, resolveAssistantTextAvatar } from "../../lib/avatar.ts";
1714
import { normalizeRoleForGrouping } from "../../lib/chat/message-normalizer.ts";
1815
import {
1916
DEFAULT_AGENT_ID,

ui/src/pages/chat/components/chat-message.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { html, nothing } from "lit";
33
import { unsafeHTML } from "lit/directives/unsafe-html.js";
44
import { until } from "lit/directives/until.js";
55
import { resolveLocalUserName } from "../../../app/user-identity.ts";
6+
import { renderCopyAsMarkdownButton } from "../../../components/copy-button.ts";
67
import { icons, type IconName } from "../../../components/icons.ts";
78
import {
89
toSanitizedMarkdownHtml,
@@ -18,17 +19,6 @@ import type {
1819
NormalizedMessage,
1920
ToolCard,
2021
} from "../../../lib/chat/chat-types.ts";
21-
import type { EmbedSandboxMode } from "../../../lib/chat/tool-display.ts";
22-
import { resolveToolDisplay } from "../../../lib/chat/tool-display.ts";
23-
import { resolveUiHourCycleOptions } from "../../../lib/format.ts";
24-
import { openExternalUrlSafe } from "../../../lib/open-external-url.ts";
25-
import { stripThinkingTags } from "../../../lib/strip-thinking-tags.ts";
26-
import { detectTextDirection } from "../../../lib/text-direction.ts";
27-
import { getSafeLocalStorage } from "../../../local-storage.ts";
28-
import type { SidebarContent } from "./chat-sidebar.ts";
29-
export { resolveAssistantTextAvatar } from "../../../lib/agents/display.ts";
30-
import { renderCopyAsMarkdownButton } from "../../../components/copy-button.ts";
31-
import "../../../components/tooltip.ts";
3222
import {
3323
extractThinkingCached,
3424
formatReasoningMarkdown,
@@ -45,8 +35,17 @@ import {
4535
formatCollapsedToolSummaryText,
4636
isToolCardError,
4737
} from "../../../lib/chat/tool-cards.ts";
38+
import type { EmbedSandboxMode } from "../../../lib/chat/tool-display.ts";
39+
import { resolveToolDisplay } from "../../../lib/chat/tool-display.ts";
40+
import { resolveUiHourCycleOptions } from "../../../lib/format.ts";
4841
import { formatCompactTokenCount } from "../../../lib/format.ts";
42+
import "../../../components/tooltip.ts";
43+
import { openExternalUrlSafe } from "../../../lib/open-external-url.ts";
44+
import { stripThinkingTags } from "../../../lib/strip-thinking-tags.ts";
45+
import { detectTextDirection } from "../../../lib/text-direction.ts";
46+
import { getSafeLocalStorage } from "../../../local-storage.ts";
4947
import { renderChatAvatar } from "../chat-avatar.ts";
48+
import type { SidebarContent } from "./chat-sidebar.ts";
5049
import {
5150
renderExpandedToolCardContent,
5251
renderRawOutputToggle,

ui/src/pages/chat/components/chat-welcome.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
// Control UI chat module implements chat welcome behavior.
22
import { html } from "lit";
33
import { t } from "../../../i18n/index.ts";
4-
import {
5-
assistantAvatarFallbackUrl,
6-
resolveAssistantTextAvatar,
7-
} from "../../../lib/agents/display.ts";
8-
import { resolveChatAvatarRenderUrl } from "../../../lib/avatar.ts";
4+
import { assistantAvatarFallbackUrl } from "../../../lib/agents/display.ts";
5+
import { resolveAssistantTextAvatar, resolveChatAvatarRenderUrl } from "../../../lib/avatar.ts";
96

107
type ChatWelcomeProps = {
118
assistantName: string;

0 commit comments

Comments
 (0)