From 3b0e934eb99412c22dfb53e9b3fc2cd459f77079 Mon Sep 17 00:00:00 2001 From: qer Date: Tue, 21 Jul 2026 22:14:57 +0800 Subject: [PATCH] fix(web): keep transparent images readable over a checkerboard canvas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit White-on-transparent images disappeared on light surfaces and black-on-transparent ones on dark surfaces (markdown inline images, tool result media, and the file preview's large view). Add a shared --media-alpha-canvas token — a checkerboard of two mid-gray squares mixed from --color-bg/--color-text, each >=3:1 against both white and black — and paint it directly on so opaque images stay pixel-identical and only transparent pixels reveal the canvas. Also let check-style's no-gradient-text skip custom-property definitions (a token is never rendered text), and register the new tokens in the design system view. --- .changeset/fix-web-media-alpha-canvas.md | 5 +++++ apps/kimi-web/scripts/check-style.mjs | 5 +++-- apps/kimi-web/src/components/FilePreview.vue | 1 + apps/kimi-web/src/components/chat/Markdown.vue | 6 ++++++ .../src/components/chat/tool-calls/MediaTool.vue | 1 + apps/kimi-web/src/style.css | 12 ++++++++++++ apps/kimi-web/src/views/DesignSystemView.vue | 2 ++ 7 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-web-media-alpha-canvas.md diff --git a/.changeset/fix-web-media-alpha-canvas.md b/.changeset/fix-web-media-alpha-canvas.md new file mode 100644 index 0000000000..828f67ae0a --- /dev/null +++ b/.changeset/fix-web-media-alpha-canvas.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +web: Show transparent images over a checkerboard canvas so white and black content stays visible in both light and dark themes. diff --git a/apps/kimi-web/scripts/check-style.mjs b/apps/kimi-web/scripts/check-style.mjs index 81480efe96..43a0048494 100644 --- a/apps/kimi-web/scripts/check-style.mjs +++ b/apps/kimi-web/scripts/check-style.mjs @@ -115,8 +115,9 @@ function checkFile(abs) { const trimmed = raw.trim(); const isTokenDef = /^\s*--[\w-]+\s*:/.test(raw); - // no-gradient-text - if (/\b(?:linear|radial|conic)-gradient\s*\(/i.test(raw)) { + // no-gradient-text (a custom-property definition is never rendered text + // itself, so gradient tokens like --media-alpha-canvas don't count) + if (!isTokenDef && /\b(?:linear|radial|conic)-gradient\s*\(/i.test(raw)) { add('no-gradient-text', file, line, trimmed.slice(0, 80)); } diff --git a/apps/kimi-web/src/components/FilePreview.vue b/apps/kimi-web/src/components/FilePreview.vue index 1888c2127d..1f3f2f546a 100644 --- a/apps/kimi-web/src/components/FilePreview.vue +++ b/apps/kimi-web/src/components/FilePreview.vue @@ -922,6 +922,7 @@ function truncatePath(path: string, maxLen = 55): string { object-fit: contain; border: 1px solid var(--line); border-radius: 4px; + background: var(--media-alpha-canvas); } .fp-image.actual { max-width: none; diff --git a/apps/kimi-web/src/components/chat/Markdown.vue b/apps/kimi-web/src/components/chat/Markdown.vue index 3e20657ddc..5075ad4d06 100644 --- a/apps/kimi-web/src/components/chat/Markdown.vue +++ b/apps/kimi-web/src/components/chat/Markdown.vue @@ -533,6 +533,12 @@ function copyDiff(code: string, idx: number) { font-size: var(--content-font-size); } +/* Themed surfaces swallow white-on-transparent (light) or black-on-transparent + (dark) images; the checkerboard canvas keeps both visible. */ +.md :deep(.markdown-renderer img) { + background: var(--media-alpha-canvas); +} + /* Emphasis — keep the weight strong, but soften the ink slightly. */ .md :deep(strong) { color: color-mix(in srgb, var(--color-text) 86%, var(--color-text-muted)); diff --git a/apps/kimi-web/src/components/chat/tool-calls/MediaTool.vue b/apps/kimi-web/src/components/chat/tool-calls/MediaTool.vue index 36ab629af1..99f43ab966 100644 --- a/apps/kimi-web/src/components/chat/tool-calls/MediaTool.vue +++ b/apps/kimi-web/src/components/chat/tool-calls/MediaTool.vue @@ -89,6 +89,7 @@ function openMediaPreview(): void { display: block; max-width: 100%; border-radius: var(--radius-md); + background: var(--media-alpha-canvas); } .media-video, .media-audio { diff --git a/apps/kimi-web/src/style.css b/apps/kimi-web/src/style.css index da20f80734..153d6e4a22 100644 --- a/apps/kimi-web/src/style.css +++ b/apps/kimi-web/src/style.css @@ -396,6 +396,18 @@ html[data-color-scheme="dark"][data-accent="mono"] { --color-surface: #fafbfc; --color-surface-raised: #ffffff; --color-surface-sunken: #f3f5f8; + /* Checkerboard canvas behind so transparent PNGs keep white/black + content visible and read as transparent. Squares are mixed from the + theme's bg/text (≈#858585/#6b6b6b light, ≈#676b72/#7a7e85 dark), each + ≥3:1 against both white and black; opaque images simply cover it. */ + --color-media-alpha-bg-1: color-mix(in srgb, var(--color-bg) 52%, var(--color-text) 48%); + --color-media-alpha-bg-2: color-mix(in srgb, var(--color-bg) 42%, var(--color-text) 58%); + --media-alpha-canvas: conic-gradient( + var(--color-media-alpha-bg-1) 25%, + var(--color-media-alpha-bg-2) 0 50%, + var(--color-media-alpha-bg-1) 0 75%, + var(--color-media-alpha-bg-2) 0 + ) 0 0 / 16px 16px; /* Foreground text colours. `--color-text` is the default solid body / UI colour (headings and emphasis share it); muted / faint step down for secondary and tertiary copy; on-accent is text drawn on the accent fill. */ diff --git a/apps/kimi-web/src/views/DesignSystemView.vue b/apps/kimi-web/src/views/DesignSystemView.vue index 0ae9897861..404f445b6f 100644 --- a/apps/kimi-web/src/views/DesignSystemView.vue +++ b/apps/kimi-web/src/views/DesignSystemView.vue @@ -194,6 +194,8 @@ onUnmounted(() => { --color-line#e7eaee#2d333bDivider / card border --color-selected#00000014#ffffff14Neutral selected fill (sidebar rows, list pickers) — translucent, never accent-tinted --color-hover#0000000d#ffffff0dRow hover wash — lighter than the selected fill (hover < selected); translucent, sits on any surface + --color-media-alpha-bg-1≈#858585≈#676b72Checkerboard square A of the <img> alpha canvas — color-mix of --color-bg/--color-text (52/48); applied via --media-alpha-canvas (16px period) + --color-media-alpha-bg-2≈#6b6b6b≈#7a7e85Checkerboard square B (42/58) — both squares stay ≥3:1 against white and black; opaque images cover the canvas --color-sidebar-bg#fbfaf9#181817Sidebar surface — one step off --color-bg so the session column reads as its own plane --color-accent#1783ff#58a6ffPrimary action / link / focus --color-success#0e7a38#3fb950Success / pass