2026-02-24 03:28:30 +02:00
import type { SidebarSection } from '@/constants/sidebar' ;
2026-08-07 09:01:31 +03:00
import type { IconName } from '@/components/icon/icons' ;
2026-02-24 03:28:30 +02:00
export type SettingsPageSlug =
| 'home'
2026-07-18 00:11:05 +03:00
| 'general'
2026-02-24 03:28:30 +02:00
| 'projects'
2026-02-26 20:26:12 +02:00
| 'remote-instances'
2026-02-24 03:28:30 +02:00
| 'providers'
| 'usage'
| 'agents'
2026-04-30 14:39:55 -07:00
| 'behavior'
2026-02-24 03:28:30 +02:00
| 'commands'
| 'mcp'
2026-05-25 12:20:04 -04:00
| 'plugins'
2026-02-24 03:28:30 +02:00
| 'skills.installed'
| 'skills.catalog'
| 'git'
| 'appearance'
| 'chat'
| 'shortcuts'
| 'sessions'
2026-04-07 22:51:14 +03:00
| 'magic-prompts'
2026-05-21 20:00:35 +03:00
| 'snippets'
2026-02-24 03:28:30 +02:00
| 'notifications'
2026-02-28 04:21:46 +02:00
| 'voice'
2026-06-10 12:00:10 +03:00
| 'tunnel'
2026-08-12 08:57:09 +00:00
| 'about'
| 'integrations' ;
2026-02-24 03:28:30 +02:00
2026-06-26 19:27:53 +03:00
type SettingsPageGroup =
2026-02-24 03:28:30 +02:00
| 'general'
2026-07-18 00:11:05 +03:00
| 'projects'
2026-02-24 03:28:30 +02:00
| 'opencode'
2026-07-18 00:11:05 +03:00
| 'content' ;
2026-02-24 03:28:30 +02:00
export interface SettingsRuntimeContext {
isVSCode : boolean ;
isWeb : boolean ;
isDesktop : boolean ;
2026-06-13 01:59:33 +03:00
isMobile : boolean ;
2026-02-24 03:28:30 +02:00
}
export interface SettingsPageMeta {
slug : SettingsPageSlug ;
title : string ;
group : SettingsPageGroup ;
kind : 'single' | 'split' ;
description? : string ;
keywords? : string [];
isAvailable ?: ( ctx : SettingsRuntimeContext ) => boolean ;
}
export const SETTINGS_PAGE_METADATA : readonly SettingsPageMeta [] = [
{
slug : 'home' ,
title : 'Settings' ,
group : 'general' ,
kind : 'single' ,
description : 'Search and jump to common pages.' ,
keywords : [ 'search' , 'settings' ],
},
2026-07-18 00:11:05 +03:00
{
slug : 'general' ,
title : 'General' ,
group : 'general' ,
kind : 'single' ,
keywords : [ 'general' , 'startup' , 'launch at login' , 'autostart' , 'tray' , 'password' , 'passkey' , 'security' , 'privacy' , 'telemetry' , 'transport' , 'network' , 'lan' , 'binary' , 'cli' ],
},
2026-02-24 03:28:30 +02:00
{
slug : 'projects' ,
title : 'Projects' ,
group : 'projects' ,
kind : 'split' ,
keywords : [ 'project' , 'projects' , 'worktree' , 'worktrees' , 'repo' , 'repository' , 'directory' ],
},
2026-02-26 20:26:12 +02:00
{
slug : 'remote-instances' ,
title : 'Remote Instances' ,
group : 'projects' ,
2026-06-02 00:43:05 +03:00
kind : 'single' ,
2026-02-26 20:26:12 +02:00
keywords : [ 'ssh' , 'remote' , 'instances' , 'tunnels' , 'forwarding' , 'connection' ],
2026-06-02 00:43:05 +03:00
isAvailable : ( ctx ) => ! ctx . isVSCode ,
2026-02-26 20:26:12 +02:00
},
2026-02-24 03:28:30 +02:00
{
slug : 'providers' ,
title : 'Providers' ,
group : 'opencode' ,
kind : 'split' ,
keywords : [ 'provider' , 'providers' , 'models' , 'model' , 'api key' , 'api keys' , 'openai' , 'anthropic' , 'ollama' , 'credentials' ],
},
{
slug : 'usage' ,
title : 'Usage' ,
2026-07-18 00:11:05 +03:00
group : 'general' ,
2026-02-24 03:28:30 +02:00
kind : 'split' ,
keywords : [ 'quota' , 'billing' , 'tokens' , 'usage' , 'limits' ],
},
{
slug : 'agents' ,
title : 'Agents' ,
group : 'opencode' ,
kind : 'split' ,
keywords : [ 'agent' , 'agents' , 'prompts' , 'tools' , 'permissions' ],
},
2026-04-30 14:39:55 -07:00
{
slug : 'behavior' ,
title : 'Behavior' ,
group : 'opencode' ,
kind : 'single' ,
keywords : [ 'behavior' , 'agents.md' , 'system prompt' , 'global rules' , 'instructions' , 'override' ],
},
2026-02-24 03:28:30 +02:00
{
slug : 'commands' ,
title : 'Commands' ,
group : 'opencode' ,
kind : 'split' ,
keywords : [ 'command' , 'commands' , 'slash' , 'macros' , 'automation' ],
},
{
slug : 'mcp' ,
title : 'MCP' ,
group : 'opencode' ,
kind : 'split' ,
keywords : [ 'mcp' , 'model context protocol' , 'servers' , 'tools' , 'remote' , 'stdio' ],
},
2026-05-25 12:20:04 -04:00
{
slug : 'plugins' ,
title : 'Plugins' ,
group : 'opencode' ,
kind : 'split' ,
keywords : [ 'plugin' , 'plugins' , 'extensions' , 'addons' , 'npm' , 'opencode-wakatime' ],
},
2026-02-24 03:28:30 +02:00
{
slug : 'skills.installed' ,
title : 'Skills' ,
2026-07-18 00:11:05 +03:00
group : 'content' ,
2026-02-24 03:28:30 +02:00
kind : 'split' ,
keywords : [ 'skill' , 'skills' , 'instructions' , 'install' , 'catalog' ],
},
{
slug : 'skills.catalog' ,
title : 'Skills Catalog' ,
2026-07-18 00:11:05 +03:00
group : 'content' ,
2026-02-24 03:28:30 +02:00
kind : 'single' ,
keywords : [ 'install' , 'catalog' , 'external' , 'repository' , 'skills catalog' ],
},
{
slug : 'git' ,
title : 'Git' ,
2026-07-18 00:11:05 +03:00
group : 'projects' ,
2026-02-24 03:28:30 +02:00
kind : 'single' ,
keywords : [ 'git' , 'github' , 'identity' , 'identities' , 'ssh' , 'profiles' , 'credentials' , 'keys' , 'commit' , 'gitmoji' , 'oauth' , 'prs' , 'issues' ],
isAvailable : ( ctx ) => ! ctx . isVSCode ,
},
{
slug : 'appearance' ,
title : 'Appearance' ,
2026-07-18 00:11:05 +03:00
group : 'general' ,
2026-02-24 03:28:30 +02:00
kind : 'single' ,
2026-05-05 16:26:34 +07:00
keywords : [ 'theme' , 'font' , 'spacing' , 'padding' , 'corner radius' , 'radius' , 'input bar' , 'keyboard' , 'viewport' , 'mobile' , 'terminal' , 'pwa' , 'install name' , 'app shortcuts' ],
2026-02-24 03:28:30 +02:00
},
{
slug : 'chat' ,
title : 'Chat' ,
group : 'general' ,
kind : 'single' ,
2026-04-27 15:41:14 +07:00
keywords : [ 'tools' , 'diff' , 'reasoning' , 'dotfiles' , 'draft' , 'queue' , 'output' , 'copy' , 'image' , 'split messages' , 'message actions' ],
2026-02-24 03:28:30 +02:00
},
{
slug : 'shortcuts' ,
title : 'Shortcuts' ,
group : 'general' ,
kind : 'single' ,
keywords : [ 'keyboard' , 'hotkeys' , 'shortcuts' , 'bindings' ],
2026-03-04 12:15:12 +02:00
isAvailable : ( ctx ) => ! ctx . isVSCode ,
2026-02-24 03:28:30 +02:00
},
{
slug : 'sessions' ,
title : 'Sessions' ,
group : 'general' ,
kind : 'single' ,
keywords : [ 'defaults' , 'default agent' , 'default model' , 'retention' , 'memory' , 'limits' , 'zen' ],
},
2026-04-07 22:51:14 +03:00
{
slug : 'magic-prompts' ,
title : 'Magic Prompts' ,
2026-07-18 00:11:05 +03:00
group : 'content' ,
2026-04-07 22:51:14 +03:00
kind : 'split' ,
keywords : [ 'prompts' , 'templates' , 'git' , 'github' , 'review' , 'commit' , 'pull request' ],
2026-04-07 23:10:08 +03:00
isAvailable : ( ctx ) => ! ctx . isVSCode ,
2026-04-07 22:51:14 +03:00
},
2026-05-21 09:35:42 -04:00
{
2026-05-21 20:00:35 +03:00
slug : 'snippets' ,
title : 'Snippets' ,
2026-07-18 00:11:05 +03:00
group : 'content' ,
2026-05-21 09:35:42 -04:00
kind : 'split' ,
keywords : [ 'prompt' , 'templates' , 'multi-run' , 'strategy' , 'approach' ],
},
2026-02-24 03:28:30 +02:00
{ slug : 'notifications' , title : 'Notifications' , group : 'general' , kind : 'single' , keywords : [ 'alerts' , 'native' , 'summary' , 'summarization' ], },
2026-07-18 00:11:05 +03:00
{ slug : 'voice' , title : 'Voice' , group : 'general' , kind : 'single' , keywords : [ 'tts' , 'speech' , 'voice' ], isAvailable : ( ctx ) => ! ctx . isVSCode },
{ slug : 'tunnel' , title : 'External Tunnel' , group : 'projects' , kind : 'single' , keywords : [ 'tunnel' , 'external' , 'cloudflare' , 'qr' , 'remote' , 'mobile' , 'share' ], isAvailable : ( ctx ) => ! ctx . isVSCode },
{ slug : 'about' , title : 'About' , group : 'general' , kind : 'single' , keywords : [ 'about' , 'version' , 'updates' , 'release' , 'changelog' ], isAvailable : ( ctx ) => ctx . isMobile && ! ctx . isVSCode },
2026-08-12 10:14:20 +00:00
{ slug : 'integrations' , title : 'Integrations' , group : 'general' , kind : 'single' , keywords : [ 'integration' , 'plugin' , 'provider' , 'oauth' , 'claude' , 'cursor' , 'command code' , 'connect' , 'discord' , 'telegram' , 'messenger' ] },
2026-02-24 03:28:30 +02:00
] as const ;
2026-06-26 19:27:53 +03:00
const LEGACY_SIDEBAR_SECTION_TO_SETTINGS_SLUG : Record < SidebarSection , SettingsPageSlug > = {
2026-02-24 03:28:30 +02:00
sessions : 'sessions' ,
agents : 'agents' ,
commands : 'commands' ,
mcp : 'mcp' ,
skills : 'skills.installed' ,
providers : 'providers' ,
usage : 'usage' ,
'git-identities' : 'git' ,
settings : 'home' ,
};
export function getSettingsPageMeta ( slug : string ) : SettingsPageMeta | null {
const normalized = slug . trim (). toLowerCase ();
return ( SETTINGS_PAGE_METADATA as readonly SettingsPageMeta []). find (( page ) => page . slug === normalized ) ?? null ;
}
export function resolveSettingsSlug ( value : string | null | undefined ) : SettingsPageSlug {
const normalized = ( value ?? '' ). trim (). toLowerCase ();
if ( ! normalized ) {
return 'home' ;
}
const legacy = ( LEGACY_SIDEBAR_SECTION_TO_SETTINGS_SLUG as Record < string , SettingsPageSlug >)[ normalized ];
if ( legacy ) {
return legacy ;
}
const direct = getSettingsPageMeta ( normalized );
if ( direct ) {
return direct . slug ;
}
return 'home' ;
}
2026-08-07 09:01:31 +03:00
// Lives here (not in SettingsView) so light consumers such as the command
// palette can render settings entries without statically importing the whole
// settings surface into the eager startup graph.
export function getSettingsNavIcon ( slug : SettingsPageSlug ) : IconName | null {
switch ( slug ) {
case 'general' :
return 'settings-3' ;
case 'projects' :
return 'folders' ;
case 'remote-instances' :
return 'computer' ;
case 'appearance' :
return 'palette' ;
case 'chat' :
return 'chat-ai-3' ;
case 'magic-prompts' :
return 'ai-generate-2' ;
case 'snippets' :
return 'chat-thread' ;
case 'notifications' :
return 'notification-3' ;
case 'shortcuts' :
return 'command' ;
case 'sessions' :
return 'chat-history' ;
case 'providers' :
return 'cloud' ;
case 'agents' :
return 'ai-agent' ;
case 'behavior' :
return 'brain' ;
case 'commands' :
return 'slash-commands-2' ;
case 'mcp' :
return null ;
case 'plugins' :
return 'plug-2' ;
case 'skills.installed' :
return 'book-open' ;
case 'skills.catalog' :
return 'book' ;
case 'git' :
return 'git-branch' ;
2026-08-12 08:57:09 +00:00
case 'integrations' :
return 'plug' ;
2026-08-07 09:01:31 +03:00
case 'usage' :
return 'bar-chart-2' ;
case 'voice' :
return 'mic' ;
case 'tunnel' :
return 'home-office' ;
case 'about' :
return 'information' ;
case 'home' :
return null ;
default :
return 'robot-2' ;
}
}