Fix settings navigation routing

Replace hash links in the settings side navigation with button-driven in-page scrolling so the hash router does not leave the settings page.
This commit is contained in:
Eyejoker
2026-05-02 03:11:15 +09:00
committed by GitHub
parent 1a2f879d11
commit 921d738bb0
3 changed files with 34 additions and 27 deletions

View File

@@ -37,7 +37,9 @@ describe('SettingsPanel', () => {
expect(html).toContain('MoA 참조 모델'); expect(html).toContain('MoA 참조 모델');
expect(html).toContain('패스트 모드'); expect(html).toContain('패스트 모드');
expect(html).toContain('Codex 실험 기능'); expect(html).toContain('Codex 실험 기능');
expect(html).toContain('href="#settings-codex"'); expect(html).toContain('data-settings-target="settings-codex"');
expect(html).toContain('aria-controls="settings-codex"');
expect(html).not.toContain('href="#settings-codex"');
expect(html).toContain('/goal'); expect(html).toContain('/goal');
expect(html).toContain('변경 적용'); expect(html).toContain('변경 적용');
expect(html).toContain('불러오는 중'); expect(html).toContain('불러오는 중');

View File

@@ -32,31 +32,15 @@ interface AccountData {
} }
const SETTINGS_NAV_ITEMS = [ const SETTINGS_NAV_ITEMS = [
{ targetId: 'settings-general', title: '일반', detail: '표시 · 언어' },
{ {
href: '#settings-general', targetId: 'settings-models',
title: '일반',
detail: '표시 · 언어',
},
{
href: '#settings-models',
title: '모델', title: '모델',
detail: 'owner · reviewer · arbiter', detail: 'owner · reviewer · arbiter',
}, },
{ { targetId: 'settings-moa', title: 'MoA', detail: '참조 모델 · 연결 테스트' },
href: '#settings-moa', { targetId: 'settings-codex', title: 'Codex', detail: 'fast mode · /goal' },
title: 'MoA', { targetId: 'settings-accounts', title: '계정', detail: 'Claude · Codex' },
detail: '참조 모델 · 연결 테스트',
},
{
href: '#settings-codex',
title: 'Codex',
detail: 'fast mode · /goal',
},
{
href: '#settings-accounts',
title: '계정',
detail: 'Claude · Codex',
},
] as const; ] as const;
export interface SettingsPanelProps { export interface SettingsPanelProps {
@@ -171,13 +155,25 @@ function SettingsHero() {
} }
function SettingsNav() { function SettingsNav() {
const scrollToSection = (targetId: string) => {
document
.getElementById(targetId)
?.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
return ( return (
<aside className="settings-nav" aria-label="설정 섹션"> <aside className="settings-nav" aria-label="설정 섹션">
{SETTINGS_NAV_ITEMS.map((item) => ( {SETTINGS_NAV_ITEMS.map((item) => (
<a href={item.href} key={item.href}> <button
aria-controls={item.targetId}
data-settings-target={item.targetId}
key={item.targetId}
onClick={() => scrollToSection(item.targetId)}
type="button"
>
<strong>{item.title}</strong> <strong>{item.title}</strong>
<small>{item.detail}</small> <small>{item.detail}</small>
</a> </button>
))} ))}
</aside> </aside>
); );

View File

@@ -734,14 +734,17 @@ button:disabled {
box-shadow: var(--card-shadow); box-shadow: var(--card-shadow);
} }
.settings-nav a { .settings-nav button {
display: grid; display: grid;
gap: 2px; gap: 2px;
padding: 10px 12px; padding: 10px 12px;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 12px; border-radius: 12px;
background: transparent;
color: var(--muted); color: var(--muted);
text-decoration: none; font: inherit;
text-align: left;
cursor: pointer;
transition: transition:
background 140ms ease, background 140ms ease,
border-color 140ms ease, border-color 140ms ease,
@@ -749,13 +752,19 @@ button:disabled {
transform 140ms ease; transform 140ms ease;
} }
.settings-nav a:hover { .settings-nav button:hover,
.settings-nav button:focus-visible {
transform: translateX(2px); transform: translateX(2px);
border-color: rgba(214, 130, 88, 0.24); border-color: rgba(214, 130, 88, 0.24);
background: rgba(214, 130, 88, 0.08); background: rgba(214, 130, 88, 0.08);
color: var(--bg-ink); color: var(--bg-ink);
} }
.settings-nav button:focus-visible {
outline: 2px solid rgba(214, 130, 88, 0.34);
outline-offset: 2px;
}
.settings-nav strong { .settings-nav strong {
color: currentColor; color: currentColor;
font-size: 13px; font-size: 13px;