fix(ui): sanitize agent emoji display values
This commit is contained in:
parent
7943602d0c
commit
a0575539a0
@ -3,6 +3,7 @@ import {
|
||||
agentLogoUrl,
|
||||
resolveConfiguredCronModelSuggestions,
|
||||
resolveAgentAvatarUrl,
|
||||
resolveAgentEmoji,
|
||||
resolveEffectiveModelFallbacks,
|
||||
sortLocaleStrings,
|
||||
} from "./agents-utils.ts";
|
||||
@ -131,3 +132,17 @@ describe("resolveAgentAvatarUrl", () => {
|
||||
expect(resolveAgentAvatarUrl({ identity: { avatar: "🦞" } })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAgentEmoji", () => {
|
||||
it("strips bidi controls from emoji values", () => {
|
||||
expect(resolveAgentEmoji({ identity: { emoji: "\u202E🤖" } })).toBe("🤖");
|
||||
});
|
||||
|
||||
it("keeps only the first grapheme cluster", () => {
|
||||
expect(resolveAgentEmoji({ identity: { emoji: "🤖😈" } })).toBe("🤖");
|
||||
});
|
||||
|
||||
it("falls back to empty for control-only emoji strings", () => {
|
||||
expect(resolveAgentEmoji({ identity: { emoji: "\u202E\u2066" } })).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
@ -221,6 +221,23 @@ export function agentLogoUrl(basePath: string): string {
|
||||
return base ? `${base}/favicon.svg` : "favicon.svg";
|
||||
}
|
||||
|
||||
const EMOJI_CONTROL_CHARS_RE = /[\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/g;
|
||||
|
||||
function sanitizeEmojiValue(value: string): string {
|
||||
const cleaned = value.replaceAll(EMOJI_CONTROL_CHARS_RE, "").trim();
|
||||
if (!cleaned) {
|
||||
return "";
|
||||
}
|
||||
if (typeof Intl.Segmenter !== "function") {
|
||||
return cleaned;
|
||||
}
|
||||
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
||||
const first = segmenter.segment(cleaned)[Symbol.iterator]().next().value as
|
||||
| { segment?: string }
|
||||
| undefined;
|
||||
return first?.segment?.trim() || "";
|
||||
}
|
||||
|
||||
function isLikelyEmoji(value: string) {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
@ -245,24 +262,35 @@ function isLikelyEmoji(value: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function pickEmoji(value?: string | null): string {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
const sanitized = sanitizeEmojiValue(value);
|
||||
if (!sanitized) {
|
||||
return "";
|
||||
}
|
||||
return isLikelyEmoji(sanitized) ? sanitized : "";
|
||||
}
|
||||
|
||||
export function resolveAgentEmoji(
|
||||
agent: { identity?: { emoji?: string; avatar?: string } },
|
||||
agentIdentity?: AgentIdentityResult | null,
|
||||
) {
|
||||
const identityEmoji = agentIdentity?.emoji?.trim();
|
||||
if (identityEmoji && isLikelyEmoji(identityEmoji)) {
|
||||
const identityEmoji = pickEmoji(agentIdentity?.emoji);
|
||||
if (identityEmoji) {
|
||||
return identityEmoji;
|
||||
}
|
||||
const agentEmoji = agent.identity?.emoji?.trim();
|
||||
if (agentEmoji && isLikelyEmoji(agentEmoji)) {
|
||||
const agentEmoji = pickEmoji(agent.identity?.emoji);
|
||||
if (agentEmoji) {
|
||||
return agentEmoji;
|
||||
}
|
||||
const identityAvatar = agentIdentity?.avatar?.trim();
|
||||
if (identityAvatar && isLikelyEmoji(identityAvatar)) {
|
||||
const identityAvatar = pickEmoji(agentIdentity?.avatar);
|
||||
if (identityAvatar) {
|
||||
return identityAvatar;
|
||||
}
|
||||
const avatar = agent.identity?.avatar?.trim();
|
||||
if (avatar && isLikelyEmoji(avatar)) {
|
||||
const avatar = pickEmoji(agent.identity?.avatar);
|
||||
if (avatar) {
|
||||
return avatar;
|
||||
}
|
||||
return "";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user