fix(ui): avoid toSorted in cron suggestions (#31775)

* Control UI: avoid toSorted in cron suggestions

* Control UI: make sortLocaleStrings legacy-safe

* fix(ui): use sort fallback in locale string helper

* fix(ui): remove toSorted from locale helper

* fix(ui): remove toSorted from locale helper

* fix(ui): remove toSorted from locale helper

* fix(ui): remove toSorted from locale helper

* fix(ui): remove toSorted from locale helper

* fix(ui): avoid sort in locale helper for browser compatibility

* ui: avoid unnecessary assertions in locale sort

* changelog: credit browser-compat cron fix PR

* fix(ui): use native locale sort in compatibility helper

* ui: use compat merge-sort for locale strings

* style: format locale sort helper

* style: fix oxfmt ordering in agents utils

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Mark L
2026-03-03 06:41:01 +08:00
committed by GitHub
parent 0743463b88
commit 5b5ccb0769
4 changed files with 55 additions and 6 deletions

View File

@@ -66,7 +66,7 @@ import {
import { buildExternalLinkRel, EXTERNAL_LINK_TARGET } from "./external-link.ts";
import { icons } from "./icons.ts";
import { normalizeBasePath, TAB_GROUPS, subtitleForTab, titleForTab } from "./navigation.ts";
import { resolveConfiguredCronModelSuggestions } from "./views/agents-utils.ts";
import { resolveConfiguredCronModelSuggestions, sortLocaleStrings } from "./views/agents-utils.ts";
import { renderAgents } from "./views/agents.ts";
import { renderChannels } from "./views/channels.ts";
import { renderChat } from "./views/chat.ts";
@@ -166,7 +166,7 @@ export function renderApp(state: AppViewState) {
state.agentsList?.defaultId ??
state.agentsList?.agents?.[0]?.id ??
null;
const cronAgentSuggestions = Array.from(
const cronAgentSuggestions = sortLocaleStrings(
new Set(
[
...(state.agentsList?.agents?.map((entry) => entry.id.trim()) ?? []),
@@ -175,8 +175,8 @@ export function renderApp(state: AppViewState) {
.filter(Boolean),
].filter(Boolean),
),
).toSorted((a, b) => a.localeCompare(b));
const cronModelSuggestions = Array.from(
);
const cronModelSuggestions = sortLocaleStrings(
new Set(
[
...state.cronModelSuggestions,
@@ -191,7 +191,7 @@ export function renderApp(state: AppViewState) {
.filter(Boolean),
].filter(Boolean),
),
).toSorted((a, b) => a.localeCompare(b));
);
const visibleCronJobs = getVisibleCronJobs(state);
const selectedDeliveryChannel =
state.cronForm.deliveryChannel && state.cronForm.deliveryChannel.trim()

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
resolveConfiguredCronModelSuggestions,
resolveEffectiveModelFallbacks,
sortLocaleStrings,
} from "./agents-utils.ts";
describe("resolveEffectiveModelFallbacks", () => {
@@ -87,3 +88,13 @@ describe("resolveConfiguredCronModelSuggestions", () => {
);
});
});
describe("sortLocaleStrings", () => {
it("sorts values using localeCompare without relying on Array.prototype.toSorted", () => {
expect(sortLocaleStrings(["z", "b", "a"])).toEqual(["a", "b", "z"]);
});
it("accepts any iterable input, including sets", () => {
expect(sortLocaleStrings(new Set(["beta", "alpha"]))).toEqual(["alpha", "beta"]);
});
});

View File

@@ -288,6 +288,43 @@ function addModelConfigIds(target: Set<string>, modelConfig: unknown) {
}
}
export function sortLocaleStrings(values: Iterable<string>): string[] {
const sorted = Array.from(values);
const buffer = new Array<string>(sorted.length);
const merge = (left: number, middle: number, right: number): void => {
let i = left;
let j = middle;
let k = left;
while (i < middle && j < right) {
buffer[k++] = sorted[i].localeCompare(sorted[j]) <= 0 ? sorted[i++] : sorted[j++];
}
while (i < middle) {
buffer[k++] = sorted[i++];
}
while (j < right) {
buffer[k++] = sorted[j++];
}
for (let idx = left; idx < right; idx += 1) {
sorted[idx] = buffer[idx];
}
};
const sortRange = (left: number, right: number): void => {
if (right - left <= 1) {
return;
}
const middle = (left + right) >>> 1;
sortRange(left, middle);
sortRange(middle, right);
merge(left, middle, right);
};
sortRange(0, sorted.length);
return sorted;
}
export function resolveConfiguredCronModelSuggestions(
configForm: Record<string, unknown> | null,
): string[] {
@@ -319,7 +356,7 @@ export function resolveConfiguredCronModelSuggestions(
addModelConfigIds(out, (entry as Record<string, unknown>).model);
}
}
return [...out].toSorted((a, b) => a.localeCompare(b));
return sortLocaleStrings(out);
}
export function parseFallbackList(value: string): string[] {