From 23adfa38473c85da6458a3daedb27454aef2fe66 Mon Sep 17 00:00:00 2001 From: tommy0103 Date: Mon, 3 Aug 2026 21:26:36 +0800 Subject: [PATCH] fix(app): align Settings controls and version metadata The editor select referenced an undefined style class, and the renderer hard-coded 0.1.0 instead of reading Electron app metadata. --- app/src/main/index.ts | 1 + app/src/renderer/src/views/Settings.vue | 20 ++++++++++++--- app/tests/electron-file-references.mjs | 33 ++++++++++++++++++++++++- tests/app-main-settings.test.mjs | 9 ++++++- tests/settings-view.test.mjs | 19 ++++++++++++++ 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 tests/settings-view.test.mjs diff --git a/app/src/main/index.ts b/app/src/main/index.ts index 35a901c..8694833 100644 --- a/app/src/main/index.ts +++ b/app/src/main/index.ts @@ -885,6 +885,7 @@ ipcMain.handle('settings:get', () => { const connected = sources.some((source) => source.status !== 'error'); return { + version: app.getVersion(), providerRoots, claudeDir, codexDir, diff --git a/app/src/renderer/src/views/Settings.vue b/app/src/renderer/src/views/Settings.vue index 98b7d55..d5d7b77 100644 --- a/app/src/renderer/src/views/Settings.vue +++ b/app/src/renderer/src/views/Settings.vue @@ -11,7 +11,7 @@ const editorScheme = ref('vscode'); const editorSchemes = ['vscode', 'vscode-insiders', 'cursor', 'windsurf', 'zed']; const memoryCount = ref(0); const rebuilding = ref(false); -const version = ref('0.1.0'); +const version = ref(''); onMounted(async () => { await loadSettings(); @@ -26,6 +26,7 @@ async function loadSettings() { autoRefresh.value = s.autoRefresh !== false; editorScheme.value = s.editorScheme || 'vscode'; memoryCount.value = s.memoryCount || 0; + version.value = s.version || ''; } async function saveEditorScheme(value) { @@ -187,8 +188,8 @@ function fmtRelative(iso) {
Editor URL scheme
Clicking src/app.ts:42 jumps to that line.
-
-
@@ -339,6 +340,19 @@ function fmtRelative(iso) { .path-field:focus { outline: 0; border-color: var(--accent); background: rgba(0,0,0,0.4); box-shadow: 0 0 0 2px rgba(167,139,250,0.12); } .path-field.error { border-color: rgba(248,113,113,0.4); } .path-field.error:focus { border-color: #f87171; box-shadow: 0 0 0 2px rgba(248,113,113,0.12); } +.select-control { position: relative; } +.select-control::after { + content: ''; position: absolute; right: 11px; top: 9px; + width: 6px; height: 6px; pointer-events: none; + border-right: 1px solid var(--muted); border-bottom: 1px solid var(--muted); + transform: rotate(45deg); +} +.select-field { + width: 100%; padding-right: 30px; cursor: pointer; + appearance: none; -webkit-appearance: none; +} +.select-field:hover { border-color: var(--hairline-vivid); } +.select-field option { background: var(--surface-strong); color: var(--fg); } .tz-field { max-width: 240px; } .btn { diff --git a/app/tests/electron-file-references.mjs b/app/tests/electron-file-references.mjs index ac2ee99..220ed7d 100644 --- a/app/tests/electron-file-references.mjs +++ b/app/tests/electron-file-references.mjs @@ -109,7 +109,10 @@ function registerHandlers() { ipcMain.handle('db:getMemories', () => []); ipcMain.handle('db:getProjects', () => [{ project: 'quiet-zero', count: 1 }]); ipcMain.handle('db:getStats', () => ({})); - ipcMain.handle('settings:get', () => ({})); + ipcMain.handle('settings:get', () => ({ + editorScheme: 'vscode', + version: '9.8.7-test', + })); ipcMain.handle('file-ref:open', (_event, ref) => { openCalls.push(ref); return { opened: false }; @@ -188,6 +191,34 @@ async function run() { assert(openCalls[0]?.sessionId === sessionId, 'click sends the session id'); assert(navigatedAway.length === 0, 'clicking a reference never navigates the window'); + await win.webContents.executeJavaScript(`window.location.hash = '#/settings'`, true); + await waitFor( + win.webContents, + `document.body.textContent.includes('Editor URL scheme')`, + 'settings editor control', + ); + + const settingsState = await win.webContents.executeJavaScript(`(() => { + const select = document.querySelector('select.select-field'); + const style = select ? getComputedStyle(select) : null; + return { + exists: Boolean(select), + appearance: style?.appearance || style?.webkitAppearance || null, + backgroundColor: style?.backgroundColor || null, + color: style?.color || null, + version: document.querySelector('.version-text')?.textContent?.trim() || null, + }; + })()`, true); + + assert(settingsState.exists, 'Settings renders the themed editor selector'); + assert(settingsState.appearance === 'none', `editor selector disables native appearance (${settingsState.appearance})`); + assert( + settingsState.backgroundColor !== 'rgb(255, 255, 255)', + `editor selector keeps the dark Settings surface (${settingsState.backgroundColor})`, + ); + assert(settingsState.color !== 'rgb(0, 0, 0)', `editor selector keeps themed text (${settingsState.color})`); + assert(settingsState.version === 'Obelisk 9.8.7-test', `Settings renders the IPC app version (${settingsState.version})`); + win.destroy(); } diff --git a/tests/app-main-settings.test.mjs b/tests/app-main-settings.test.mjs index 0ec25ca..dfa816d 100644 --- a/tests/app-main-settings.test.mjs +++ b/tests/app-main-settings.test.mjs @@ -388,6 +388,12 @@ test('session IPC hides Codex rows by default and supports explicit source opt-i const restore = registerMocks([ [ELECTRON_URL, { namedExports: electronNamespace({ + app: { + whenReady: () => Promise.resolve(), + on() {}, + quit() {}, + getVersion: () => '9.8.7-test', + }, BrowserWindow: FakeBrowserWindow, ipcMain: { handle(channel, handler) { @@ -419,7 +425,8 @@ test('session IPC hides Codex rows by default and supports explicit source opt-i assert.match(queries.at(-1).sql, /COALESCE\(source, 'claude'\) = \?/); assert.ok(queries.at(-1).params.includes('codex')); - await ipcHandlers.get('settings:get')(); + const settings = await ipcHandlers.get('settings:get')(); + assert.equal(settings.version, '9.8.7-test'); assert.ok( queries.some(q => /GROUP BY COALESCE\(source, 'claude'\)/.test(q.sql)), ); diff --git a/tests/settings-view.test.mjs b/tests/settings-view.test.mjs new file mode 100644 index 0000000..601182d --- /dev/null +++ b/tests/settings-view.test.mjs @@ -0,0 +1,19 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const source = readFileSync( + new URL('../app/src/renderer/src/views/Settings.vue', import.meta.url), + 'utf8', +); + +test('Settings reads the displayed version from the settings payload', () => { + assert.doesNotMatch(source, /const version = ref\(['"]0\.1\.0['"]\)/); + assert.match(source, /version\.value = s\.version/); +}); + +test('Editor selector uses the themed Settings control vocabulary', () => { + assert.match(source, /