mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
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:
@@ -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()
|
||||
|
||||
@@ -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"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user