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/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'));