Merge pull request #88 from omdsh-dev/feat/title-bar-compat

feat: 侧边栏新增位置兼容模式(v0.12.2)
This commit is contained in:
Atlas Zeng
2026-08-15 15:53:35 +08:00
committed by GitHub
17 changed files with 300 additions and 23 deletions
+5 -1
View File
@@ -54,6 +54,10 @@ describe('dsh-better-sidebar plugin export shape', () => {
// and 13px.
expect(resolved.terminalFontFamily).toBe('')
expect(resolved.terminalFontSize).toBe(13)
// The position-compat mode defaults OFF (the normal layout is default),
// with the strip defaulting to 40px.
expect(resolved.titleBarCompat).toBe(false)
expect(resolved.titleBarStripPx).toBe(40)
// The enable-switch maps resolve to {} (everything on) for old documents.
expect(resolved.tabsEnabled).toEqual({})
expect(resolved.viewersEnabled).toEqual({})
@@ -63,6 +67,6 @@ describe('dsh-better-sidebar plugin export shape', () => {
const overridden = (PrefsSchema as unknown as {
(input: Record<string, unknown> | undefined): Record<string, unknown>
})({ openByDefault: false, defaultWidthPercent: 45 })
expect(overridden).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
expect(overridden).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
})
})
+47
View File
@@ -151,6 +151,28 @@ describe('SideCardSection declarative inventory', () => {
const html = renderSection(store, service)
expect(html).not.toContain('Feature settings')
})
it('renders the position-compat mode general row: off by default, checked when the pref is on', () => {
const { store, service } = mount()
let html = renderSection(store, service)
// The general row renders its title and description.
expect(html).toContain('Position compatibility mode')
expect(html).toContain('Reserve space for the native Windows title bar')
// Three general rows now: openByDefault + interceptOpenPath checked,
// the new titleBarCompat row UNCHECKED (default off) — the checked
// checkbox count stays at 2 while the total checkbox count is 3.
expect(html.match(/type="checkbox"/g)?.length).toBe(3)
expect(html.match(/checked=""/g)?.length).toBe(2)
// The row's gear (customize the shift distance) is dormant while the
// mode is off — the feature-card convention.
expect(html).not.toContain('Position compatibility mode Feature settings')
// When the pref is on, the new switch is checked and the gear appears.
store.setPrefs({ ...store.getPrefs(), titleBarCompat: true })
html = renderSection(store, service)
expect(html.match(/checked=""/g)?.length).toBe(3)
expect(html).toContain('aria-label="Position compatibility mode Feature settings"')
})
})
describe('FeatureSettingsRows (the secondary settings popup body)', () => {
@@ -229,6 +251,31 @@ describe('FeatureSettingsRows (the secondary settings popup body)', () => {
expect(html).toContain('px')
expect(html).not.toContain('type="checkbox"')
})
it('renders the title-bar strip row: the pref value, the 0–120 bounds and the px suffix', () => {
const html = renderToString(createElement(FeatureSettingsRows, {
toggles: [{
key: 'titleBarStripPx',
type: 'number',
title: () => 'Shift distance',
desc: () => 'Title-bar strip height in px',
min: 0,
max: 120,
unit: 'px',
}],
prefs: { ...prefs, titleBarStripPx: 64 },
onToggle: () => {},
onCommit: () => '64',
}))
expect(html).toContain('Shift distance')
expect(html).toContain('Title-bar strip height in px')
expect(html).toContain('type="number"')
expect(html).toContain('value="64"')
expect(html).toContain('min="0"')
expect(html).toContain('max="120"')
expect(html).toContain('px')
expect(html).not.toContain('type="checkbox"')
})
})
describe('mergePluginSetting (v0.12.0, codex review fix)', () => {
+2
View File
@@ -548,6 +548,8 @@ describe('side card settings routes', () => {
terminalFontFamily: '',
terminalFontSize: 13,
interceptOpenPath: true,
titleBarCompat: false,
titleBarStripPx: 40,
htmlViewerNoSandbox: false,
htmlViewerDefaultUnsafe: false,
browserNoSandbox: false,
+33 -4
View File
@@ -1161,6 +1161,8 @@ describe('side card preferences', () => {
terminalFontFamily: '',
terminalFontSize: 13,
interceptOpenPath: true,
titleBarCompat: false,
titleBarStripPx: 40,
htmlViewerNoSandbox: false,
htmlViewerDefaultUnsafe: false,
browserNoSandbox: false,
@@ -1185,6 +1187,8 @@ describe('side card preferences', () => {
terminalFontFamily: '',
terminalFontSize: 13,
interceptOpenPath: true,
titleBarCompat: false,
titleBarStripPx: 40,
htmlViewerNoSandbox: false,
htmlViewerDefaultUnsafe: false,
browserNoSandbox: false,
@@ -1209,6 +1213,8 @@ describe('side card preferences', () => {
terminalFontFamily: '',
terminalFontSize: 13,
interceptOpenPath: true,
titleBarCompat: false,
titleBarStripPx: 40,
htmlViewerNoSandbox: false,
htmlViewerDefaultUnsafe: false,
browserNoSandbox: false,
@@ -1245,6 +1251,29 @@ describe('side card preferences', () => {
expect((await loadPrefs(wire({ interceptOpenPath: true }))).interceptOpenPath).toBe(true)
})
it('defaults titleBarCompat to false; only an explicit true turns the position-compat mode on', async () => {
// Absent or malformed → off (the normal layout is the default).
expect((await loadPrefs(wire({}))).titleBarCompat).toBe(false)
expect((await loadPrefs(wire({ titleBarCompat: 'yes' }))).titleBarCompat).toBe(false)
expect((await loadPrefs(wire({ titleBarCompat: 1 }))).titleBarCompat).toBe(false)
// Explicit booleans survive verbatim.
expect((await loadPrefs(wire({ titleBarCompat: false }))).titleBarCompat).toBe(false)
expect((await loadPrefs(wire({ titleBarCompat: true }))).titleBarCompat).toBe(true)
})
it('defaults titleBarStripPx to 40 and clamps stored values into the contract range', async () => {
// Absent or malformed → 40 (the strip default).
expect((await loadPrefs(wire({}))).titleBarStripPx).toBe(40)
expect((await loadPrefs(wire({ titleBarStripPx: 'yes' }))).titleBarStripPx).toBe(40)
// Out-of-range numbers clamp into 0–120.
expect((await loadPrefs(wire({ titleBarStripPx: -5 }))).titleBarStripPx).toBe(0)
expect((await loadPrefs(wire({ titleBarStripPx: 200 }))).titleBarStripPx).toBe(120)
expect((await loadPrefs(wire({ titleBarStripPx: 47.6 }))).titleBarStripPx).toBe(48)
// In-range values survive verbatim.
expect((await loadPrefs(wire({ titleBarStripPx: 0 }))).titleBarStripPx).toBe(0)
expect((await loadPrefs(wire({ titleBarStripPx: 64 }))).titleBarStripPx).toBe(64)
})
it('defaults the link-takeover protocol flags: http on, https off, master on', async () => {
// Absent or malformed → the per-protocol defaults.
expect((await loadPrefs(wire({}))).browserInterceptLinks).toBe(true)
@@ -1294,9 +1323,9 @@ describe('side card preferences', () => {
const store = createSidebarStore()
// Node environment: no window → the width falls back to PANEL_DEFAULT,
// while the open flag still follows the preference.
store.setPrefs({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
store.setPrefs({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
store.setSession('fresh-session')
expect(store.getPrefs()).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
expect(store.getPrefs()).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
const snapshot = store.getSnapshot()
expect(snapshot.sessionId).toBe('fresh-session')
expect(snapshot.state?.panelOpen).toBe(false)
@@ -1332,7 +1361,7 @@ describe('side card preferences', () => {
it('skips the default explorer tab when the explorer type is disabled', () => {
const store = createSidebarStore()
store.setPrefs({ openByDefault: true, defaultWidthPercent: 30, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: { explorer: false }, viewersEnabled: {}, pluginSettings: {} })
store.setPrefs({ openByDefault: true, defaultWidthPercent: 30, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: { explorer: false }, viewersEnabled: {}, pluginSettings: {} })
store.setSession('no-explorer')
const state = store.getSnapshot().state!
const tabs = allLeaves(state.splits).flatMap(leaf => leaf.tabs)
@@ -1340,7 +1369,7 @@ describe('side card preferences', () => {
expect(state.splits.kind).toBe('leaf')
// Re-enabling seeds the explorer tab again.
const openStore = createSidebarStore()
openStore.setPrefs({ openByDefault: true, defaultWidthPercent: 30, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
openStore.setPrefs({ openByDefault: true, defaultWidthPercent: 30, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} })
openStore.setSession('with-explorer')
const openTabs = allLeaves(openStore.getSnapshot().state!.splits).flatMap(leaf => leaf.tabs)
expect(openTabs.map(tab => tab.type)).toEqual(['explorer'])