From fe3eab5ccc4d821f8149b189534e814950a798e0 Mon Sep 17 00:00:00 2001 From: liuxuezhuo Date: Tue, 29 Sep 2026 23:10:05 +0800 Subject: [PATCH 1/2] feat(icons): adopt the new Qoder mark introduced with the new Qoder app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the old QoderIDE glyph (dark rounded-square backdrop, green extruded body, white front ring) with the monochrome mark shipped with the new Qoder app. The mark is lifted from the app's official vector artwork with its invisible masked detail path and duplicated fill path removed. Because the new mark is single-tone, the old two-tone rendering hacks go away entirely: the dimmed-body opacity trick, the light-theme white-fill patch, and the model selector backdrop stripping. The header logo and model selector icons now follow the theme's ink tone — dark on light themes, light on dark themes — matching how the new app icon adapts. --- CHANGELOG.md | 7 ++++++ manifest.json | 2 +- src/assets/qoder-icon.svg | 5 ++++- src/shared/icons.ts | 33 +++++++++------------------- src/style/base/variables.css | 7 ------ src/style/components/header.css | 4 +++- src/style/toolbar/model-selector.css | 9 ++++---- tests/unit/shared/icons.test.ts | 23 +++++++++---------- 8 files changed, 40 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a9a94..b0e2e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ version with its date and start a fresh empty `[Unreleased]` above it. ## [Unreleased] +### Changed + +- The ribbon icon, view header logo, and model selector rows now use the new + Qoder mark introduced with the new Qoder app, replacing the old QoderIDE + glyph. Like the new app icon, the monochrome mark follows the theme's ink + tone — dark on light themes, light on dark themes — instead of brand green. + ## [1.0.15] - 2026-09-21 ### Added diff --git a/manifest.json b/manifest.json index 92bbb9e..b04c3aa 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "qoderian", "name": "Qoderian", - "version": "1.0.15", + "version": "1.0.16", "minAppVersion": "1.7.2", "description": "Embeds Qoder CLI as an AI collaborator in your vault. Your vault becomes the working directory, giving qodercli capabilities for file reads and writes, search, bash commands, and multi-step workflows.", "author": "Qoder", diff --git a/src/assets/qoder-icon.svg b/src/assets/qoder-icon.svg index 829ec79..942cb84 100644 --- a/src/assets/qoder-icon.svg +++ b/src/assets/qoder-icon.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/src/shared/icons.ts b/src/shared/icons.ts index 998a7ca..5a6060b 100644 --- a/src/shared/icons.ts +++ b/src/shared/icons.ts @@ -61,7 +61,7 @@ export function appendCheckIcon(container: HTMLElement): void { export const QODER_ICON: IconSvg = { kind: 'raw', - viewBox: '0 0 180 180', + viewBox: '0 0 100 100', svg: qoderIconSvg, }; @@ -96,29 +96,22 @@ export const CHECK_ICON: IconSvg = { export const QODERIAN_ICON_ID = 'qoderian-logo'; -// Obsidian draws registered icons inside a `0 0 100 100` viewport, so the mark -// of the 180x180 app artwork (bounding box 32.4..147.6 x 30.2..148.4) is -// re-centered and scaled down to leave the padding ribbon icons expect. -const QODER_MARK_TRANSFORM = 'translate(-14.8 -14.29) scale(0.72)'; +// Obsidian draws registered icons inside a `0 0 100 100` viewport. The mark +// fills 15.2..84.8 x 15.0..86.1 of the 100x100 artwork, so it is scaled up +// and re-centered to leave the padding ribbon icons expect. +const QODER_MARK_TRANSFORM = 'translate(-9.61 -10.31) scale(1.19)'; // `addIcon` takes markup as a string and the artwork is a build-time asset, so // the mark paths are lifted textually instead of through a DOM round-trip. -const QODER_MARK_PATH_PATTERN = /]*\sd="([^"]+)"/g; -// Ribbon and tab icons follow a single theme color, so the artwork's white -// front ring and green extruded body both become `currentColor`. Drawing them -// at the same strength collapses the mark into one blob, so the body behind -// the ring is dimmed to keep the depth the two-tone original conveys. -const QODER_MARK_BODY_OPACITY = '0.32'; +const QODER_MARK_PATH_PATTERN = /]*>/g; export function createQoderianIconContent(sourceSvg: string = qoderIconSvg): string { - // The artwork draws the extruded body first and the front ring second; that - // order is preserved so the ring stays on top of the dimmed body. + // The artwork fills the outline body first and strokes the contour second; + // that order is preserved so the stroke stays on top of the fill. const paths = Array.from(sourceSvg.matchAll(QODER_MARK_PATH_PATTERN)) - .map(([, pathData], index) => (index === 0 - ? `` - : ``)) + .map(([path]) => path) .join(''); - return `${paths}`; + return `${paths}`; } export interface CreateIconSvgOptions { @@ -158,12 +151,6 @@ export function createIconSvg( if (sourceSvg) { for (const child of Array.from(sourceSvg.children)) { const clone = ownerDocument.importNode(child, true) as SVGElement; - // The source artwork is an app-icon variant with a dark rounded-square - // backdrop. In a compact model selector that backdrop turns into the - // grey block seen on every option, so keep only the Qoder mark. - if (clone.tagName.toLowerCase() === 'g') { - clone.querySelector(':scope > rect')?.remove(); - } svg.appendChild(clone); } } diff --git a/src/style/base/variables.css b/src/style/base/variables.css index 147c81e..a5bd694 100644 --- a/src/style/base/variables.css +++ b/src/style/base/variables.css @@ -11,10 +11,3 @@ body.theme-light .qoderian-container { --qoderian-brand: #35CC5F; --qoderian-brand-rgb: 53, 204, 95; } - -/* The Qoder glyph has a white half for dark surfaces. On light Obsidian - themes that half disappears into the canvas, so use the same ink tone as - the plugin title while retaining the native green half. */ -body.theme-light .qoderian-icon path[fill="#FFFFFF"] { - fill: #202124; -} diff --git a/src/style/components/header.css b/src/style/components/header.css index 5b65bc0..614fde4 100644 --- a/src/style/components/header.css +++ b/src/style/components/header.css @@ -23,7 +23,9 @@ .qoderian-logo { display: flex; align-items: center; - color: var(--qoderian-brand); + /* The Qoder mark is monochrome, so it follows the theme's ink tone like the + app icon does: dark on light themes, light on dark themes. */ + color: var(--text-normal); } /* Update badge: an icon button in the header actions, shown only while a newer diff --git a/src/style/toolbar/model-selector.css b/src/style/toolbar/model-selector.css index 432dfe5..7c07fae 100644 --- a/src/style/toolbar/model-selector.css +++ b/src/style/toolbar/model-selector.css @@ -214,10 +214,11 @@ opacity: 0.7; } -/* Qoder's dual-green mark stays branded on the active option, while inactive - options inherit the muted selector text just like PR #973. */ -.qoderian-model-option:not(.selected) .qoderian-model-icon * { - fill: currentColor; +/* The mark is monochrome and follows the theme's ink tone, so the selected + row's green label must not bleed into the icon. */ +.qoderian-model-option.selected .qoderian-model-icon { + color: var(--text-normal); + opacity: 1; } .qoderian-model-option { diff --git a/tests/unit/shared/icons.test.ts b/tests/unit/shared/icons.test.ts index efa4c43..e75f6ce 100644 --- a/tests/unit/shared/icons.test.ts +++ b/tests/unit/shared/icons.test.ts @@ -1,31 +1,28 @@ import { createQoderianIconContent, QODERIAN_ICON_ID } from '@/shared/icons'; -const SOURCE_SVG = `` - + `` - + `` - + ``; +const SOURCE_SVG = `` + + `` + + ``; describe('createQoderianIconContent', () => { - it('keeps only the mark paths, dropping the artwork backdrop and clip ids', () => { + it('lifts the mark paths verbatim, keeping their paint attributes', () => { const content = createQoderianIconContent(SOURCE_SVG); - expect(content).toContain('d="M10 20 L30 40 Z"'); - expect(content).toContain(''); + expect(content).toContain(''); + expect(content).toContain(''); expect(content).not.toContain('rect'); expect(content).not.toContain('clip-path'); }); - it('renders the mark in one theme color, dimming the body behind the front ring', () => { + it('renders the mark in one theme color inside the ribbon viewport', () => { const content = createQoderianIconContent(SOURCE_SVG); expect(content).toContain('fill="currentColor"'); - expect(content).toContain(''); - expect(content).toMatch(/transform="translate\([-\d. ]+\) scale\(0?\.\d+\)"/); - expect(content).not.toContain('#2ADB5C'); - expect(content).not.toContain('#FFFFFF'); + expect(content).toContain('stroke="currentColor"'); + expect(content).toMatch(/^/); }); - it('draws the front ring after the body so it stays on top', () => { + it('draws the filled body before the outlined contour', () => { const content = createQoderianIconContent(SOURCE_SVG); expect(content.indexOf('M10 20')).toBeLessThan(content.indexOf('M50 60')); From d655a582cea891db6e653c508b0a799e9197af39 Mon Sep 17 00:00:00 2001 From: liuxuezhuo Date: Tue, 29 Sep 2026 23:12:50 +0800 Subject: [PATCH 2/2] chore: drop the version bump from the feature branch Releases bump manifest.json in their own chore: release commit, so the feature PR stays on 1.0.15 until the next release. --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index b04c3aa..92bbb9e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "qoderian", "name": "Qoderian", - "version": "1.0.16", + "version": "1.0.15", "minAppVersion": "1.7.2", "description": "Embeds Qoder CLI as an AI collaborator in your vault. Your vault becomes the working directory, giving qodercli capabilities for file reads and writes, search, bash commands, and multi-step workflows.", "author": "Qoder",