diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 3f239de9..69add25c 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -635,6 +635,14 @@ Jira uses rotating refresh tokens — each refresh invalidates the previous toke --- +## Settings Navigation + +The Settings page organizes its sections into four groups: **Data Sources** (Orgs & Repos, Tracked Users, Refresh, API Usage), **Display** (Appearance, Tabs, Custom Tabs), **Integrations** (GitHub Actions, Notifications, MCP Relay, Jira), and **Account** (Data). + +On desktop (1024px+), a sticky sidebar on the left shows a table of contents with all sections grouped and highlighted as you scroll. Click any item to jump to that section. On smaller screens, a dropdown bar appears below the header showing the current section name — tap it to open a section picker. + +The header shrinks as you scroll to maximize content space. + ## Settings Reference Settings are saved automatically to `localStorage` and persist across sessions. All settings can be exported as a JSON file via **Settings > Data > Export**. diff --git a/src/app/components/settings/Section.tsx b/src/app/components/settings/Section.tsx index 061a4cf1..6046153c 100644 --- a/src/app/components/settings/Section.tsx +++ b/src/app/components/settings/Section.tsx @@ -1,8 +1,8 @@ import { JSX, Show } from "solid-js"; -export default function Section(props: { title: string; description?: string; children: JSX.Element }) { +export default function Section(props: { id?: string; title: string; description?: string; children: JSX.Element }) { return ( -
+

{props.title}

diff --git a/src/app/components/settings/SettingsPage.tsx b/src/app/components/settings/SettingsPage.tsx index f58976c9..74a611d3 100644 --- a/src/app/components/settings/SettingsPage.tsx +++ b/src/app/components/settings/SettingsPage.tsx @@ -18,6 +18,7 @@ import { getUsageSnapshot, getUsageResetAt, resetUsageData, checkAndResetIfExpir import OrgSelector from "../onboarding/OrgSelector"; import RepoSelector from "../onboarding/RepoSelector"; import Section from "./Section"; +import SettingsTOC from "./SettingsTOC"; import SettingRow from "./SettingRow"; import ThemePicker from "./ThemePicker"; import DensityPicker from "./DensityPicker"; @@ -31,6 +32,22 @@ import type { RepoRef } from "../../services/api"; const VALID_JIRA_CLIENT_ID_RE = /^[A-Za-z0-9_-]+$/; +export const SETTINGS_PAGE_SECTION_IDS = [ + "orgs-repos", + "tracked-users", + "refresh", + "api-usage", + "appearance", + "tabs", + "custom-tabs", + "actions", + "notifications", + "mcp-relay", + "jira", + "dependencies", + "data", +] as const; + export default function SettingsPage() { const navigate = useNavigate(); @@ -64,6 +81,18 @@ export default function SettingsPage() { } }); + const [scrolled, setScrolled] = createSignal(false); + onMount(() => { + const onScroll = () => { + if (document.documentElement.dataset.scrollLock) return; + const y = window.scrollY; + if (scrolled() && y < 10) setScrolled(false); + else if (!scrolled() && y > 50) setScrolled(true); + }; + window.addEventListener("scroll", onScroll, { passive: true }); + onCleanup(() => window.removeEventListener("scroll", onScroll)); + }); + onMount(() => checkAndResetIfExpired()); const usageSnapshot = createMemo(() => getUsageSnapshot()); @@ -365,8 +394,8 @@ export default function SettingsPage() { return (
{/* Page header */} -
-
+
+
-
+
+
+ +
+ {/* ── Data Sources ─────────────────────────────────────────────────── */} +

Data Sources

{/* Section 1: Orgs & Repos */} -
+
@@ -492,7 +526,7 @@ export default function SettingsPage() {
{/* Section 2: Tracked Users */} -
+

Track another GitHub user's issues and pull requests alongside yours. @@ -505,7 +539,7 @@ export default function SettingsPage() {

{/* Section 3: Refresh */} -
+
{/* Section 4: API Usage */} -
+
0} @@ -608,8 +642,105 @@ export default function SettingsPage() {
- {/* Section 5: GitHub Actions */} -
+ {/* ── Display ────────────────────────────────────────────────────── */} +

Display

+ {/* Section 5: Appearance */} +
+
+

Theme

+ +
+
+

View density

+ +
+ + + +
+ + {/* Section 6: Tabs */} +
+ + + + + saveWithFeedback({ rememberLastTab: e.currentTarget.checked })} + class="toggle toggle-primary" + /> + + + { + const val = e.currentTarget.checked; + saveWithFeedback({ + enableTracking: val, + ...(!val && config.defaultTab === "tracked" ? { defaultTab: "issues" as const } : {}), + }); + if (!val && viewState.lastActiveTab === "tracked") { + updateViewState({ lastActiveTab: "issues" }); + } + }} + class="toggle toggle-primary" + /> + +
+ + {/* Section 7: Custom Tabs */} +
+ r.owner))]} + availableRepos={config.selectedRepos} + /> +
+ + {/* ── Integrations ──────────────────────────────────────────────── */} +

Integrations

+ {/* Section 8: GitHub Actions */} +
- {/* Section 6: Notifications */} -
+ {/* Section 9: Notifications */} +
- {/* Section 7: Appearance */} -
-
-

Theme

- -
-
-

View density

- -
- - - -
- - {/* Section 8: Tabs */} -
- - - - - saveWithFeedback({ rememberLastTab: e.currentTarget.checked })} - class="toggle toggle-primary" - /> - - - { - const val = e.currentTarget.checked; - saveWithFeedback({ - enableTracking: val, - ...(!val && config.defaultTab === "tracked" ? { defaultTab: "issues" as const } : {}), - }); - if (!val && viewState.lastActiveTab === "tracked") { - updateViewState({ lastActiveTab: "issues" }); - } - }} - class="toggle toggle-primary" - /> - -
- - {/* Section 9: Custom Tabs */} -
- r.owner))]} - availableRepos={config.selectedRepos} - /> -
- {/* Section 10: MCP Server Relay */}
@@ -920,7 +959,7 @@ export default function SettingsPage() {
{/* Section 11: Jira Cloud Integration */} -
+
- {/* Dependencies */} -
+ {/* Section 12: Dependencies */} +
- {/* Data */} -
+ {/* ── Account ─────────────────────────────────────────────────── */} +

Account

+ {/* Section 13: Data */} +
{/* Authentication method */}
+
+