diff --git a/src/renderer/src/components/cmd-j/palette-results.ts b/src/renderer/src/components/cmd-j/palette-results.ts
index f6295856569..aac89d2c39c 100644
--- a/src/renderer/src/components/cmd-j/palette-results.ts
+++ b/src/renderer/src/components/cmd-j/palette-results.ts
@@ -1,5 +1,4 @@
-import type { LucideIcon } from 'lucide-react'
-import type { SettingsNavSection } from '@/lib/settings-navigation-types'
+import type { SettingsNavIcon, SettingsNavSection } from '@/lib/settings-navigation-types'
import type { CmdJQuickAction } from './quick-actions'
export type CmdJSettingsResult = {
@@ -7,7 +6,7 @@ export type CmdJSettingsResult = {
kind: 'settings'
title: string
description: string
- icon: LucideIcon
+ icon: SettingsNavIcon
sectionId: string
targetSectionId?: string
order: number
diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx b/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx
index 9f3797ac5dc..e7615f28cd0 100644
--- a/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx
+++ b/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx
@@ -224,7 +224,7 @@ export function FeatureWallSetupChecklist(
const setupSteps = getFeatureWallSetupStepsForSection('setup')
return (
-
+
({
...group,
sections: generalNavSections.filter((section) => section.group === group.id)
- })).filter((group) => group.sections.length > 0)
+ })).filter((group) => group.sections.length > 0 || group.id === 'setup')
const repoNavSections = visibleNavSections
.filter((section) => section.id.startsWith('repo-'))
.map((section) => {
@@ -910,8 +910,6 @@ function Settings(): React.JSX.Element {
) : (
- {settingsSearchQuery.trim() === '' ? : null}
-
) : null}
+
+ {isSectionMounted('setup-guide') ? : null}
+
+
s.openModal)
- const progress = useSetupGuideProgress(true, false, false)
- const setupSteps = useMemo(() => getFeatureWallSetupSteps(), [])
- const unfinishedSteps = useMemo(
- () => setupSteps.filter((step) => !progress.stepDone[step.id]),
- [progress.stepDone, setupSteps]
- )
- const completedStepCount = useMemo(
- () => setupSteps.filter((step) => progress.stepDone[step.id]).length,
- [progress.stepDone, setupSteps]
- )
- const firstUnfinishedStepId: FeatureWallSetupStepId = getFirstIncompleteFeatureWallSetupStepId(
- progress.stepDone
- )
- const setupComplete = unfinishedSteps.length === 0
-
- return (
-
-
-
-
-
-
-
-
-
- Onboarding checklist
-
-
- {completedStepCount}/{setupSteps.length} setup tasks complete
-
-
-
-
- {setupComplete ? 'Status' : 'To do'}
-
-
- {setupComplete ? (
-
- All core workflows complete
-
- ) : (
- <>
- {unfinishedSteps.slice(0, 3).map((step) => (
-
- {step.name}
-
- ))}
- {unfinishedSteps.length > 3 ? (
-
- +{unfinishedSteps.length - 3} more
-
- ) : null}
- >
- )}
-
-
-
-
-
-
-
- )
-}
diff --git a/src/renderer/src/components/settings/SettingsSetupGuidePane.tsx b/src/renderer/src/components/settings/SettingsSetupGuidePane.tsx
new file mode 100644
index 00000000000..b2e106554a1
--- /dev/null
+++ b/src/renderer/src/components/settings/SettingsSetupGuidePane.tsx
@@ -0,0 +1,58 @@
+import { useEffect, useMemo, useState } from 'react'
+import {
+ getFeatureWallSetupSteps,
+ getFirstIncompleteFeatureWallSetupStepId
+} from '../../../../shared/feature-wall-setup-steps'
+import type { FeatureWallSetupStepId } from '../../../../shared/feature-wall-setup-steps'
+import { FeatureWallSetupChecklist } from '../feature-wall/FeatureWallSetupChecklist'
+import { useSetupGuideProgress } from '../setup-guide/use-setup-guide-progress'
+
+export function SettingsSetupGuidePane(): React.JSX.Element {
+ const setupSteps = useMemo(() => getFeatureWallSetupSteps(), [])
+ const [activeStepId, setActiveStepId] = useState(
+ () => setupSteps[0]?.id ?? 'default-agent'
+ )
+ const [userSelectedStep, setUserSelectedStep] = useState(false)
+ const [orchestrationSkillInstalled, setOrchestrationSkillInstalled] = useState(false)
+ const [browserUseSkillInstalled, setBrowserUseSkillInstalled] = useState(false)
+ const progress = useSetupGuideProgress(
+ true,
+ orchestrationSkillInstalled,
+ browserUseSkillInstalled
+ )
+ const activeStep = setupSteps.find((step) => step.id === activeStepId) ?? setupSteps[0] ?? null
+
+ useEffect(() => {
+ if (userSelectedStep) {
+ return
+ }
+ setActiveStepId(getFirstIncompleteFeatureWallSetupStepId(progress.stepDone))
+ }, [progress.stepDone, userSelectedStep])
+
+ useEffect(() => {
+ if (!activeStep || userSelectedStep || !progress.stepDone[activeStep.id]) {
+ return
+ }
+ const nextUnfinishedStepId = getFirstIncompleteFeatureWallSetupStepId(progress.stepDone)
+ if (nextUnfinishedStepId !== activeStep.id) {
+ setActiveStepId(nextUnfinishedStepId)
+ }
+ }, [activeStep, progress.stepDone, userSelectedStep])
+
+ const handleSelectStep = (id: FeatureWallSetupStepId): void => {
+ setUserSelectedStep(true)
+ setActiveStepId(id)
+ }
+
+ return (
+
+
+
+ )
+}
diff --git a/src/renderer/src/components/settings/SettingsSidebar.test.tsx b/src/renderer/src/components/settings/SettingsSidebar.test.tsx
index 9f4a9278e00..0b22d486578 100644
--- a/src/renderer/src/components/settings/SettingsSidebar.test.tsx
+++ b/src/renderer/src/components/settings/SettingsSidebar.test.tsx
@@ -2,6 +2,7 @@ import { renderToStaticMarkup } from 'react-dom/server'
import { Bot, Mic, Network } from 'lucide-react'
import { describe, expect, it, vi } from 'vitest'
import { SettingsSidebar } from './SettingsSidebar'
+import { TooltipProvider } from '../ui/tooltip'
vi.mock('@/hooks/useShortcutLabel', () => ({
useShortcutLabel: () => '⌘F'
@@ -9,52 +10,54 @@ vi.mock('@/hooks/useShortcutLabel', () => ({
function renderSidebar(): string {
return renderToStaticMarkup(
-
+
+
+
)
}
diff --git a/src/renderer/src/components/settings/SettingsSidebar.tsx b/src/renderer/src/components/settings/SettingsSidebar.tsx
index 1890394981c..f572ffc4902 100644
--- a/src/renderer/src/components/settings/SettingsSidebar.tsx
+++ b/src/renderer/src/components/settings/SettingsSidebar.tsx
@@ -1,17 +1,21 @@
import type { RefObject } from 'react'
-import { ArrowLeft, Search, Server, type LucideIcon, type LucideProps } from 'lucide-react'
+import { ArrowLeft, Search, Server } from 'lucide-react'
+import logo from '../../../../../resources/logo.svg'
import type { RepoIcon } from '../../../../shared/repo-icon'
-import type { SettingsNavInstallStatus } from '@/lib/settings-navigation-types'
+import type { SettingsNavIcon, SettingsNavInstallStatus } from '@/lib/settings-navigation-types'
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
import { cn } from '@/lib/utils'
import { RepoIconGlyph } from '../repo/repo-icon'
import { Button } from '../ui/button'
import { Input } from '../ui/input'
+import { SetupGuideProgressRing } from '../setup-guide/SetupGuideProgressRing'
+import { useSettingsSetupGuideProgress } from './settings-setup-guide-progress'
+import type { SettingsSetupGuideProgress } from './settings-setup-guide-progress'
type NavSection = {
id: string
title: string
- icon: LucideIcon | ((props: LucideProps) => React.JSX.Element)
+ icon: SettingsNavIcon
badge?: string
installStatus?: SettingsNavInstallStatus
}
@@ -48,6 +52,64 @@ type SettingsSidebarProps = {
) => void
}
+type SettingsSetupGuideRowProps = {
+ progress: SettingsSetupGuideProgress
+ setupActive: boolean
+ onSelect: (modifiers: {
+ metaKey: boolean
+ ctrlKey: boolean
+ shiftKey: boolean
+ altKey: boolean
+ }) => void
+}
+
+function SettingsSetupGuideNavRow({
+ progress,
+ setupActive,
+ onSelect
+}: SettingsSetupGuideRowProps): React.JSX.Element {
+ return (
+
+ )
+}
+
export function SettingsSidebar({
activeSectionId,
generalGroups,
@@ -59,6 +121,9 @@ export function SettingsSidebar({
onSearchChange,
onSelectSection
}: SettingsSidebarProps): React.JSX.Element {
+ const setupGuideProgress = useSettingsSetupGuideProgress(true)
+ const setupActive = activeSectionId === 'setup-guide'
+ const showSetupGuideTopRow = setupGuideProgress.doneCount < setupGuideProgress.total
const searchShortcutHint = useShortcutLabel('settings.search')
const navItemClassName = (isActive: boolean): string =>
cn(
@@ -119,6 +184,16 @@ export function SettingsSidebar({
+ {showSetupGuideTopRow ? (
+
+ onSelectSection('setup-guide', modifiers)}
+ />
+
+ ) : null}
+
{generalGroups.map((group) => (
@@ -127,9 +202,11 @@ export function SettingsSidebar({
{group.title}
- {group.sections.map((section) => {
- const Icon = section.icon
- const isActive = activeSectionId === section.id
+ {group.sections
+ .filter((section) => section.id !== 'setup-guide')
+ .map((section) => {
+ const Icon = section.icon
+ const isActive = activeSectionId === section.id
return (