Refine appearance settings and syntax highlighting

Keep parsed chat and tool detail content in sync with appearance token changes. Hide font-family controls on native, use One as the default syntax theme, add Swift highlighting, and give Nord a distinct light palette.
This commit is contained in:
Mohamed Boudra
2026-05-31 21:40:38 +07:00
parent 4cc72a84e8
commit 9f88b4e69c
21 changed files with 310 additions and 53 deletions

View File

@@ -25,6 +25,8 @@
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@codemirror/language": "^6.12.3",
"@codemirror/legacy-modes": "^6.5.3",
"@lezer/common": "^1.5.0",
"@lezer/cpp": "^1.1.5",
"@lezer/css": "^1.3.0",

View File

@@ -30,6 +30,22 @@ describe("highlightCode", () => {
expect(stringToken?.style).toBe("string");
});
it("highlights Swift code", () => {
const code = 'struct Greeter {\n let message = "hello"\n}';
const result = highlightCode(code, "test.swift");
expect(result).toHaveLength(3);
const structToken = result[0].find((t) => t.text === "struct");
expect(structToken?.style).toBe("keyword");
const typeToken = result[0].find((t) => t.text === "Greeter");
expect(typeToken?.style).toBe("definition");
const stringToken = result[1].find((t) => t.text.includes("hello"));
expect(stringToken?.style).toBe("string");
});
it("highlights TSX code with correct dialect", () => {
const code = 'const el = <div className="test">hello</div>;';
const result = highlightCode(code, "test.tsx");

View File

@@ -13,6 +13,7 @@ describe("isLanguageSupported", () => {
expect(isLanguageSupported("test.css")).toBe(true);
expect(isLanguageSupported("test.html")).toBe(true);
expect(isLanguageSupported("test.java")).toBe(true);
expect(isLanguageSupported("test.swift")).toBe(true);
expect(isLanguageSupported("test.ex")).toBe(true);
});
@@ -49,6 +50,7 @@ describe("getSupportedExtensions", () => {
expect(extensions).toContain("py");
expect(extensions).toContain("go");
expect(extensions).toContain("rs");
expect(extensions).toContain("swift");
expect(extensions).toContain("json");
});
});

View File

@@ -59,10 +59,19 @@ describe("resolveSyntaxColors", () => {
});
it("dark-only themes ignore the color scheme", () => {
for (const id of ["dracula", "nord"] as const) {
for (const id of ["dracula"] as const) {
expect(resolveSyntaxColors(id, "light")).toEqual(resolveSyntaxColors(id, "dark"));
}
});
it("nord uses a dark text palette in light mode", () => {
const light = resolveSyntaxColors("nord", "light");
const dark = resolveSyntaxColors("nord", "dark");
expect(light).not.toEqual(dark);
expect(light.variable).toBe("#2e3440");
expect(light.comment).toBe("#6b7280");
});
});
describe("isSyntaxThemeId", () => {

View File

@@ -1,3 +1,5 @@
import { StreamLanguage } from "@codemirror/language";
import { swift } from "@codemirror/legacy-modes/mode/swift";
import { parser as jsParser } from "@lezer/javascript";
import { parser as jsonParser } from "@lezer/json";
import { parser as cssParser } from "@lezer/css";
@@ -55,6 +57,8 @@ const parsersByExtension: Record<string, Parser> = {
yml: yamlParser,
// Rust
rs: rustParser,
// Swift
swift: StreamLanguage.define(swift).parser,
// Elixir
ex: elixirParser,
exs: elixirParser,

View File

@@ -180,8 +180,20 @@ const oneDark: RolePalette = {
operator: "#56b6c2",
};
// --- Nord (dark only) ----------------------------------------------------
const nord: RolePalette = {
// --- Nord (Snow Storm light / Polar Night dark) ---------------------------
const nordLight: RolePalette = {
base: "#2e3440",
keyword: "#5e81ac",
comment: "#6b7280",
string: "#4f6f3a",
number: "#8f5e91",
function: "#2e6f8e",
type: "#3b7f87",
tag: "#5e81ac",
attribute: "#8f5e91",
operator: "#5e81ac",
};
const nordDark: RolePalette = {
base: "#d8dee9",
keyword: "#81a1c1",
comment: "#616e88",
@@ -269,7 +281,7 @@ export function resolveSyntaxColors(
case "one":
return expandRolePalette(dark ? oneDark : oneLight);
case "nord":
return expandRolePalette(nord);
return expandRolePalette(dark ? nordDark : nordLight);
case "gruvbox":
return expandRolePalette(dark ? gruvboxDark : gruvboxLight);
case "solarized":