fix(qa-lab): keep saved view text truncation UTF-16 safe (#103789)

* fix(qa-lab): keep saved view text truncation UTF-16 safe

* refactor(qa-lab): use semantic truncation helper

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
xingzhou
2026-07-10 23:10:53 +01:00
committed by GitHub
co-authored by Peter Steinberger
parent 3f402f2c48
commit 4257875448
2 changed files with 20 additions and 3 deletions
@@ -103,4 +103,20 @@ describe("capture saved views", () => {
expect(normalizeCaptureSavedViews(views)).toHaveLength(12);
});
it("keeps bounded saved view text on UTF-16 boundaries", () => {
const view = normalizeCaptureSavedView({
id: `${"i".repeat(255)}😀after`,
name: `${"n".repeat(79)}😀after`,
sessionIds: [`${"s".repeat(255)}😀after`],
searchText: `${"q".repeat(499)}😀after`,
});
expect(view).toMatchObject({
id: "i".repeat(255),
name: "n".repeat(79),
sessionIds: ["s".repeat(255)],
searchText: "q".repeat(499),
});
});
});
@@ -1,4 +1,5 @@
// Qa Lab plugin module implements capture saved view behavior.
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import type { CaptureSavedView } from "./ui-render.js";
const MAX_SAVED_VIEWS = 12;
@@ -38,7 +39,7 @@ function readString(value: unknown, maxLength: number): string | null {
return null;
}
const trimmed = value.trim();
return trimmed ? trimmed.slice(0, maxLength) : null;
return trimmed ? truncateUtf16Safe(trimmed, maxLength) : null;
}
function readStringArray(value: unknown): string[] {
@@ -47,7 +48,7 @@ function readStringArray(value: unknown): string[] {
}
return value
.filter((item): item is string => typeof item === "string")
.map((item) => item.trim().slice(0, MAX_FILTER_VALUE_LENGTH))
.map((item) => truncateUtf16Safe(item.trim(), MAX_FILTER_VALUE_LENGTH))
.filter(Boolean)
.slice(0, MAX_FILTER_ITEMS);
}
@@ -86,7 +87,7 @@ export function normalizeCaptureSavedView(value: unknown): CaptureSavedView | nu
hostFilter: readStringArray(record.hostFilter),
searchText:
typeof record.searchText === "string"
? record.searchText.slice(0, MAX_SEARCH_TEXT_LENGTH)
? truncateUtf16Safe(record.searchText, MAX_SEARCH_TEXT_LENGTH)
: "",
headerMode: readEnum(record.headerMode, headerModes, "key"),
viewMode: readEnum(record.viewMode, viewModes, "list"),