feat(webui): improve slash command actions
This commit is contained in:
@@ -8,6 +8,20 @@ import { resources } from "@/i18n";
|
||||
|
||||
const QUICK_ACTION_KEYS = ["plan", "analyze", "brainstorm", "code", "summarize", "more"];
|
||||
const IMAGE_QUICK_ACTION_KEYS = ["icon", "sticker", "poster", "product", "portrait", "edit"];
|
||||
const SLASH_COMMAND_KEYS = [
|
||||
"new",
|
||||
"stop",
|
||||
"restart",
|
||||
"status",
|
||||
"model",
|
||||
"history",
|
||||
"dream",
|
||||
"dream_log",
|
||||
"dream_restore",
|
||||
"goal",
|
||||
"help",
|
||||
"pairing",
|
||||
];
|
||||
const SETTINGS_NAV_KEYS = [
|
||||
"overview",
|
||||
"appearance",
|
||||
@@ -18,6 +32,33 @@ const SETTINGS_NAV_KEYS = [
|
||||
"advanced",
|
||||
];
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function flattenResource(value: unknown, prefix = ""): Map<string, unknown> {
|
||||
const out = new Map<string, unknown>();
|
||||
if (!isRecord(value)) return out;
|
||||
for (const [key, child] of Object.entries(value)) {
|
||||
const path = prefix ? `${prefix}.${key}` : key;
|
||||
if (isRecord(child)) {
|
||||
for (const [childPath, childValue] of flattenResource(child, path)) {
|
||||
out.set(childPath, childValue);
|
||||
}
|
||||
} else {
|
||||
out.set(path, child);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function interpolationKeys(value: unknown): string[] {
|
||||
if (typeof value !== "string") return [];
|
||||
return Array.from(value.matchAll(/{{\s*([\w.-]+)\s*}}/g))
|
||||
.map((match) => match[1])
|
||||
.sort();
|
||||
}
|
||||
|
||||
describe("webui i18n", () => {
|
||||
it("switches UI copy and document locale through the language switcher", async () => {
|
||||
const user = userEvent.setup();
|
||||
@@ -72,6 +113,46 @@ describe("webui i18n", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps every locale aligned with the English resource shape", () => {
|
||||
const reference = flattenResource(resources.en.common);
|
||||
for (const [locale, resource] of Object.entries(resources)) {
|
||||
if (locale === "en") continue;
|
||||
const current = flattenResource(resource.common);
|
||||
const missing = Array.from(reference.keys()).filter((key) => !current.has(key));
|
||||
const extra = Array.from(current.keys()).filter((key) => !reference.has(key));
|
||||
const interpolationMismatches = Array.from(reference.entries())
|
||||
.filter(([key]) => current.has(key))
|
||||
.filter(([key, value]) =>
|
||||
interpolationKeys(value).join(",") !== interpolationKeys(current.get(key)).join(",")
|
||||
)
|
||||
.map(([key]) => key);
|
||||
|
||||
expect({ locale, missing, extra, interpolationMismatches }).toEqual({
|
||||
locale,
|
||||
missing: [],
|
||||
extra: [],
|
||||
interpolationMismatches: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps slash commands localized for every registered locale", () => {
|
||||
for (const resource of Object.values(resources)) {
|
||||
const slash = resource.common.thread.composer.slash;
|
||||
expect(slash.badges.current).toBeTruthy();
|
||||
expect(slash.badges.recent).toBeTruthy();
|
||||
expect(slash.details.goalActive).toBeTruthy();
|
||||
expect(slash.details.goalReady).toBeTruthy();
|
||||
expect(slash.details.history).toBeTruthy();
|
||||
expect(slash.details.stopRunning).toBeTruthy();
|
||||
for (const key of SLASH_COMMAND_KEYS) {
|
||||
const command = slash.commands[key as keyof typeof slash.commands];
|
||||
expect(command.title).toBeTruthy();
|
||||
expect(command.description).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps settings navigation localized for every registered locale", () => {
|
||||
for (const resource of Object.values(resources)) {
|
||||
const common = resource.common;
|
||||
|
||||
Reference in New Issue
Block a user