feat: merge red and orange prose palettes into vermilion orange

This commit is contained in:
tianma-if committed 2026-10-13 13:49:49 +08:00
1 parent be93432e78
commit eb618af80a
7 files changed
+36 -33

No files matched your search

+1 -2
View File
@@ -1022,8 +1022,7 @@ export const enUS = {
editorBodyPaletteTitle: "Text colors",
editorBodyPalettes: {
native: "Native",
clay: "Red",
orange: "Orange",
clay: "Vermilion orange",
dawn: "Gold",
teal: "Teal",
azure: "Blue",
+1 -2
View File
@@ -1011,8 +1011,7 @@ export const ja = {
editorBodyPaletteTitle: "本文の配色",
editorBodyPalettes: {
native: "標準",
clay: "赤",
orange: "オレンジ",
clay: "朱橙",
dawn: "金",
teal: "青緑",
azure: "青",
+1 -2
View File
@@ -1011,8 +1011,7 @@ export const pl = {
editorBodyPaletteTitle: "Kolory tekstu",
editorBodyPalettes: {
native: "Natywne",
clay: "Czerwony",
orange: "Pomarańczowy",
clay: "Cynobrowy pomarańcz",
dawn: "Złoty",
teal: "Morski",
azure: "Niebieski",
+1 -2
View File
@@ -1022,8 +1022,7 @@ export const zhCN = {
editorBodyPaletteTitle: "正文配色",
editorBodyPalettes: {
native: "原生",
clay: "红色",
orange: "橙色",
clay: "朱橙",
dawn: "金色",
teal: "青色",
azure: "蓝色",
+21 -4
View File
@@ -2,7 +2,10 @@ import { describe, expect, test } from "bun:test";
import {
MAX_NOTE_PROSE_CSS_BYTES,
NOTE_PROSE_PALETTES,
NOTE_PROSE_PALETTE_CHOICES,
NOTE_PROSE_DARK_ACCENTS,
accountNoteProseFromRow,
publicNoteProseFromAccount,
noteProseCssVariables,
NoteProseUpdateSchema,
noteProseCodeFontSize,
@@ -24,7 +27,21 @@ const emptyAccount = () => ({
});
describe("note prose", () => {
test("checks reading contrast with an intentional bright teal emphasis exception", () => {
test("merges old red and orange choices without dropping account or public share preferences", () => {
expect(NOTE_PROSE_PALETTE_CHOICES).toContain("clay");
expect(NOTE_PROSE_PALETTE_CHOICES).not.toContain("orange");
for (const oldPalette of ["clay", "orange"]) {
const account = accountNoteProseFromRow({ note_prose_palette: oldPalette });
expect(account.palette).toBe("clay");
expect(resolveNoteProse(account).palette).toBe("clay");
expect(publicNoteProseFromAccount(account).palette).toBe("clay");
expect(NoteProseUpdateSchema.parse({ palette: oldPalette }).palette).toBe("clay");
}
expect(noteProseMigrationPatch(emptyAccount(), { palette: "orange", customCss: null, customColors: null }))
.toEqual({ palette: "clay" });
});
test("checks reading contrast with intentional bright teal and vermilion emphasis exceptions", () => {
const luminance = (hex) => {
const channels = [1, 3, 5].map((offset) => {
const value = parseInt(hex.slice(offset, offset + 2), 16) / 255;
@@ -38,9 +55,9 @@ describe("note prose", () => {
};
for (const [id, colors] of Object.entries(NOTE_PROSE_PALETTES)) {
for (const background of ["#FFFFFF", "#F8FAFB"]) {
// Teal emphasis intentionally stays close to the original bright hue.
// It does not meet normal-text AA; links and code retain the 4.5 minimum.
expect(contrast(colors.accent, background)).toBeGreaterThanOrEqual(id === "teal" ? 2.5 : 4.5);
// Bright emphasis is intentional; links and code retain normal-text AA.
const minimumAccentContrast = id === "teal" ? 2.5 : id === "clay" ? 3 : 4.5;
expect(contrast(colors.accent, background)).toBeGreaterThanOrEqual(minimumAccentContrast);
for (const foreground of [colors.link, colors.codeText]) {
expect(contrast(foreground, background)).toBeGreaterThanOrEqual(4.5);
}
+9 -20
View File
@@ -8,7 +8,6 @@ export const DEFAULT_NOTE_PROSE_LINE_HEIGHT: NoteProseLineHeight = 1.65;
export const NOTE_PROSE_PALETTE_IDS = [
"clay",
"orange",
"dawn",
"teal",
"azure",
@@ -110,25 +109,14 @@ const palette = (colors: NoteProsePaletteColors): NoteProsePaletteColors => colo
export const NOTE_PROSE_PALETTES: Record<NoteProsePaletteId, NoteProsePaletteColors> = {
clay: palette({
accent: "#C13C42",
text: "#374151",
muted: "#374151",
surface: "#FFF7F7",
divider: "#FFD6D6",
link: "#B42318",
codeBackground: "#FFF1F1",
codeText: "#B42318",
background: "#FFFFFF",
}),
orange: palette({
accent: "#A65B24",
accent: "#FF4C01",
text: "#3A2E28",
muted: "#746055",
surface: "#FFF4EA",
divider: "#F6D7BE",
link: "#B45309",
codeBackground: "#FFF4EA",
codeText: "#B45309",
surface: "#FFF4EE",
divider: "#F6D5C6",
link: "#C63C00",
codeBackground: "#FFF4EE",
codeText: "#C63C00",
background: "#FFFFFF",
}),
dawn: palette({
@@ -201,8 +189,7 @@ export const NOTE_PROSE_PALETTES: Record<NoteProsePaletteId, NoteProsePaletteCol
// Dark reading surfaces need lighter accents than the white-background palette.
export const NOTE_PROSE_DARK_ACCENTS: Record<NoteProsePaletteId, string> = {
clay: "#F08B8E",
orange: "#E7A16A",
clay: "#FF996E",
dawn: "#D9B65C",
teal: "#70C7B8",
azure: "#8CB4F0",
@@ -262,6 +249,8 @@ export const parseNoteProseLineHeight = (value: unknown): NoteProseLineHeight |
};
export const parseNoteProsePalette = (value: unknown): NoteProsePaletteChoice | null => {
// Red and orange now share the existing red identifier for older clients.
if (value === "orange") return "clay";
// Removed palettes and the custom color editor read as native.
if (value === "emerald" || value === "green" || value === "plum" || value === "custom") return "native";
return typeof value === "string" && (NOTE_PROSE_PALETTE_CHOICES as readonly string[]).includes(value)
+2 -1
View File
@@ -444,7 +444,8 @@ const noteProseCustomColorSetSchema = z.object({
export const NoteProseUpdateSchema = z.object({
fontSize: z.union([z.literal(14), z.literal(16), z.literal(18), z.literal(20)]).nullable().optional(),
lineHeight: z.union([z.literal(1.5), z.literal(1.65), z.literal(2)]).nullable().optional(),
palette: z.enum(NOTE_PROSE_PALETTE_CHOICES).nullable().optional(),
palette: z.union([z.enum(NOTE_PROSE_PALETTE_CHOICES), z.literal("orange")])
.transform((value) => value === "orange" ? "clay" as const : value).nullable().optional(),
customCss: z.string().refine((value) => new TextEncoder().encode(value).byteLength <= MAX_NOTE_PROSE_CSS_BYTES).nullable().optional(),
customColors: z.object({
light: noteProseCustomColorSetSchema,