From 9af0de0056c2c98eb8a67acd2858e8dff7788c7d Mon Sep 17 00:00:00 2001 From: kostazol Date: Sun, 24 May 2026 01:48:56 +0700 Subject: [PATCH] Add mobile context notes tab (#1355) * Add mobile context notes tab * Fix bot comments --------- Co-authored-by: Konstantin Zolin Co-authored-by: Bohdan Triapitsyn --- packages/ui/src/components/layout/Header.tsx | 10 ++++++-- .../ui/src/components/layout/MainLayout.tsx | 23 ++++++++++--------- .../components/layout/RightSidebarTabs.tsx | 6 ++--- .../session/sidebar/SidebarProjectsList.tsx | 3 ++- .../hooks/useProjectSessionSelection.ts | 3 ++- .../sidebar/hooks/useSessionActions.ts | 3 ++- packages/ui/src/lib/i18n/messages/en.ts | 1 + packages/ui/src/lib/i18n/messages/es.ts | 1 + packages/ui/src/lib/i18n/messages/ko.ts | 1 + packages/ui/src/lib/i18n/messages/pl.ts | 1 + packages/ui/src/lib/i18n/messages/pt-BR.ts | 1 + packages/ui/src/lib/i18n/messages/uk.ts | 1 + packages/ui/src/lib/i18n/messages/zh-CN.ts | 1 + packages/ui/src/stores/useUIStore.ts | 2 +- scripts/convert-vscode-theme.cjs | 2 +- 15 files changed, 38 insertions(+), 21 deletions(-) diff --git a/packages/ui/src/components/layout/Header.tsx b/packages/ui/src/components/layout/Header.tsx index f98a461e..1dfd17bd 100644 --- a/packages/ui/src/components/layout/Header.tsx +++ b/packages/ui/src/components/layout/Header.tsx @@ -655,6 +655,7 @@ export const Header: React.FC = ({ const { t } = useI18n(); const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen); const toggleSidebar = useUIStore((state) => state.toggleSidebar); + const setRightSidebarOpen = useUIStore((state) => state.setRightSidebarOpen); const toggleBottomTerminal = useUIStore((state) => state.toggleBottomTerminal); const toggleRightSidebar = useUIStore((state) => state.toggleRightSidebar); const openContextOverview = useUIStore((state) => state.openContextOverview); @@ -1506,6 +1507,7 @@ export const Header: React.FC = ({ { id: 'diff', label: t('layout.mainTab.diff'), icon: 'diff' }, { id: 'files', label: t('layout.mainTab.files'), icon: "folder-6" }, { id: 'terminal', label: t('layout.mainTab.terminal'), icon: "terminal-box" }, + { id: 'context', label: t('layout.mainTab.context'), icon: "file-list-2" }, ); return base; @@ -1520,7 +1522,7 @@ export const Header: React.FC = ({ }, [shortcutOverrides]); useEffect(() => { - if (!isMobile && (activeMainTab === 'git' || activeMainTab === 'terminal' || activeMainTab === 'diff' || activeMainTab === 'files')) { + if (!isMobile && (activeMainTab === 'git' || activeMainTab === 'terminal' || activeMainTab === 'diff' || activeMainTab === 'files' || activeMainTab === 'context')) { setActiveMainTab('chat'); } }, [activeMainTab, isMobile, setActiveMainTab]); @@ -1626,13 +1628,16 @@ export const Header: React.FC = ({ const num = parseInt(e.key, 10); if (num >= 1 && num <= tabs.length) { e.preventDefault(); + if (isMobile) { + setRightSidebarOpen(false); + } setActiveMainTab(tabs[num - 1].id); } } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, [tabs, setActiveMainTab]); + }, [isMobile, setActiveMainTab, setRightSidebarOpen, tabs]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -1995,6 +2000,7 @@ export const Header: React.FC = ({ onClick={() => { if (isMobile) { blurActiveElement(); + setRightSidebarOpen(false); } setActiveMainTab(tab.id); }} diff --git a/packages/ui/src/components/layout/MainLayout.tsx b/packages/ui/src/components/layout/MainLayout.tsx index b0e58256..52a8bb75 100644 --- a/packages/ui/src/components/layout/MainLayout.tsx +++ b/packages/ui/src/components/layout/MainLayout.tsx @@ -4,7 +4,7 @@ import { Header } from './Header'; import { BottomTerminalDock } from './BottomTerminalDock'; import { Sidebar, SIDEBAR_CONTENT_WIDTH } from './Sidebar'; import { RightSidebar, RIGHT_SIDEBAR_CONTENT_WIDTH } from './RightSidebar'; -import { RightSidebarTabs } from './RightSidebarTabs'; +import { ProjectContextPanel, RightSidebarTabs } from './RightSidebarTabs'; import { ContextPanel } from './ContextPanel'; import { ErrorBoundary } from '../ui/ErrorBoundary'; import { CommandPalette } from '../ui/CommandPalette'; @@ -308,6 +308,13 @@ export const MainLayout: React.FC = () => { }; }, [isMobile, isTablet, setBottomTerminalOpen, setRightSidebarOpen]); + const handleToggleMobileRightDrawer = React.useCallback(() => { + if (mobileLeftDrawerOpen) { + setMobileLeftDrawerOpen(false); + } + setRightSidebarOpen(!isRightSidebarOpen); + }, [isRightSidebarOpen, mobileLeftDrawerOpen, setRightSidebarOpen]); + const secondaryView = React.useMemo(() => { switch (activeMainTab) { case 'plan': @@ -320,6 +327,8 @@ export const MainLayout: React.FC = () => { return ; case 'files': return ; + case 'context': + return ; default: return null; } @@ -361,12 +370,7 @@ export const MainLayout: React.FC = () => { } setMobileLeftDrawerOpen(!mobileLeftDrawerOpen); }, - toggleRightDrawer: () => { - if (mobileLeftDrawerOpen) { - setMobileLeftDrawerOpen(false); - } - setRightSidebarOpen(!isRightSidebarOpen); - }, + toggleRightDrawer: handleToggleMobileRightDrawer, leftDrawerX, rightDrawerX, leftDrawerWidth, @@ -383,10 +387,7 @@ export const MainLayout: React.FC = () => { setMobileLeftDrawerOpen(!mobileLeftDrawerOpen); }} onToggleRightDrawer={() => { - if (mobileLeftDrawerOpen) { - setMobileLeftDrawerOpen(false); - } - setRightSidebarOpen(!isRightSidebarOpen); + handleToggleMobileRightDrawer(); }} leftDrawerOpen={mobileLeftDrawerOpen} rightDrawerOpen={isRightSidebarOpen} diff --git a/packages/ui/src/components/layout/RightSidebarTabs.tsx b/packages/ui/src/components/layout/RightSidebarTabs.tsx index 7fef5f82..3bc5c26a 100644 --- a/packages/ui/src/components/layout/RightSidebarTabs.tsx +++ b/packages/ui/src/components/layout/RightSidebarTabs.tsx @@ -40,7 +40,7 @@ function useRightSidebarGitSync(directory: string | undefined, isSidebarOpen: bo }, [directory, git, isSidebarOpen, ensureStatus]); } -const ContextSidebarPanel: React.FC = () => { +export const ProjectContextPanel: React.FC = () => { const activeProjectId = useProjectsStore((state) => state.activeProjectId); const projects = useProjectsStore((state) => state.projects); const homeDirectory = useDirectoryStore((state) => state.homeDirectory); @@ -113,7 +113,7 @@ export const RightSidebarTabs: React.FC = () => { { id: 'context', label: t('layout.rightSidebar.context'), - icon: , + icon: , }, ], [t]); @@ -133,7 +133,7 @@ export const RightSidebarTabs: React.FC = () => {
{rightSidebarTab === 'git' && } {rightSidebarTab === 'files' && } - {rightSidebarTab === 'context' && } + {rightSidebarTab === 'context' && }
); diff --git a/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx b/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx index 955a9646..6d7bbdf3 100644 --- a/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx +++ b/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx @@ -16,6 +16,7 @@ import type { SortableDragHandleProps } from './sortableItems'; import { SortableGroupItem, SortableProjectItem } from './sortableItems'; import { formatProjectLabel } from './utils'; import { useI18n } from '@/lib/i18n'; +import type { MainTab } from '@/stores/useUIStore'; type ProjectSection = { project: { @@ -50,7 +51,7 @@ type Props = { alwaysShowActions: boolean; toggleProject: (id: string) => void; setActiveProjectIdOnly: (id: string) => void; - setActiveMainTab: (tab: 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files') => void; + setActiveMainTab: (tab: MainTab) => void; setSessionSwitcherOpen: (open: boolean) => void; openNewSessionDraft: (options?: { directoryOverride?: string | null }) => void; openNewWorktreeDialog: () => void; diff --git a/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts b/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts index 6a33bde3..b4d4e46b 100644 --- a/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts +++ b/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts @@ -2,6 +2,7 @@ import React from 'react'; import type { Session } from '@opencode-ai/sdk/v2'; import type { SessionGroup, SessionNode } from '../types'; import { normalizePath } from '../utils'; +import type { MainTab } from '@/stores/useUIStore'; type ProjectSection = { project: { id: string; normalizedPath: string }; @@ -18,7 +19,7 @@ type Args = { newSessionDraftOpen: boolean; mobileVariant: boolean; openNewSessionDraft: (options?: { directoryOverride?: string | null }) => void; - setActiveMainTab: (tab: 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files') => void; + setActiveMainTab: (tab: MainTab) => void; setSessionSwitcherOpen: (open: boolean) => void; sessions: Session[]; worktreeMetadata: Map; diff --git a/packages/ui/src/components/session/sidebar/hooks/useSessionActions.ts b/packages/ui/src/components/session/sidebar/hooks/useSessionActions.ts index b8400fe9..7b4bc9ac 100644 --- a/packages/ui/src/components/session/sidebar/hooks/useSessionActions.ts +++ b/packages/ui/src/components/session/sidebar/hooks/useSessionActions.ts @@ -3,6 +3,7 @@ import type { Session } from '@opencode-ai/sdk/v2'; import { toast } from '@/components/ui'; import { copyTextToClipboard } from '@/lib/clipboard'; import { useI18n } from '@/lib/i18n'; +import type { MainTab } from '@/stores/useUIStore'; type DeleteSessionConfirmSetter = React.Dispatch void; setActiveProjectIdOnly: (id: string) => void; setDirectory: (directory: string, options?: { showOverlay?: boolean }) => void; - setActiveMainTab: (tab: 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files') => void; + setActiveMainTab: (tab: MainTab) => void; setSessionSwitcherOpen: (open: boolean) => void; setCurrentSession: (sessionId: string | null, directoryHint?: string | null) => void; updateSessionTitle: (id: string, title: string) => Promise; diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index 16d82376..8d3008c1 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -24,6 +24,7 @@ export const dict = { 'layout.mainTab.diff': 'Diff', 'layout.mainTab.files': 'Files', 'layout.mainTab.terminal': 'Terminal', + 'layout.mainTab.context': 'Context', 'layout.rightSidebar.git': 'Git', 'layout.rightSidebar.files': 'Files', 'layout.rightSidebar.context': 'Context', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index 7591f05e..7ff2f98e 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -25,6 +25,7 @@ export const dict: Record = { "layout.mainTab.diff": "Diff", "layout.mainTab.files": "Archivos", "layout.mainTab.terminal": "Terminal", + "layout.mainTab.context": "Contexto", "layout.rightSidebar.git": "Git", "layout.rightSidebar.files": "Archivos", "layout.rightSidebar.context": "Contexto", diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index aed0ee11..9b97ad5d 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -25,6 +25,7 @@ export const dict: Record = { 'layout.mainTab.diff': '변경사항', 'layout.mainTab.files': '파일', 'layout.mainTab.terminal': '터미널', + 'layout.mainTab.context': '컨텍스트', 'layout.rightSidebar.git': 'Git', 'layout.rightSidebar.files': '파일', 'layout.rightSidebar.context': '컨텍스트', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index 2e30829f..a8ed24e0 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -26,6 +26,7 @@ export const dict: Record = { 'layout.mainTab.diff': 'Różnice', 'layout.mainTab.files': 'Pliki', 'layout.mainTab.terminal': 'Terminal', + 'layout.mainTab.context': 'Kontekst', 'layout.rightSidebar.git': 'Git', 'layout.rightSidebar.files': 'Pliki', 'layout.rightSidebar.context': 'Kontekst', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index 68f3871a..6118313e 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -25,6 +25,7 @@ export const dict: Record = { "layout.mainTab.diff": "Diff", "layout.mainTab.files": "Arquivos", "layout.mainTab.terminal": "Terminal", + "layout.mainTab.context": "Contexto", "layout.rightSidebar.git": "Git", "layout.rightSidebar.files": "Arquivos", "layout.rightSidebar.context": "Contexto", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index 95a8e4b5..ad3d4347 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -25,6 +25,7 @@ export const dict: Record = { "layout.mainTab.diff": "Diff", "layout.mainTab.files": "Файли", "layout.mainTab.terminal": "Термінал", + "layout.mainTab.context": "Контекст", "layout.rightSidebar.git": "Git", "layout.rightSidebar.files": "Файли", "layout.rightSidebar.context": "Контекст", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 09d1fd53..2dc378bc 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -25,6 +25,7 @@ export const dict: Record = { 'layout.mainTab.diff': '差异', 'layout.mainTab.files': '文件', 'layout.mainTab.terminal': '终端', + 'layout.mainTab.context': '上下文', 'layout.rightSidebar.git': 'Git', 'layout.rightSidebar.files': '文件', 'layout.rightSidebar.context': '上下文', diff --git a/packages/ui/src/stores/useUIStore.ts b/packages/ui/src/stores/useUIStore.ts index c1e49fe3..23c22af2 100644 --- a/packages/ui/src/stores/useUIStore.ts +++ b/packages/ui/src/stores/useUIStore.ts @@ -7,7 +7,7 @@ import type { ShortcutCombo } from '@/lib/shortcuts'; import { DEFAULT_MONO_FONT, DEFAULT_UI_FONT, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions'; import { getStoredMobileKeyboardMode, type MobileKeyboardMode } from '@/lib/mobileKeyboardMode'; -export type MainTab = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files'; +export type MainTab = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files' | 'context'; export type RightSidebarTab = 'git' | 'files' | 'context'; export type ContextPanelMode = 'diff' | 'file' | 'context' | 'plan' | 'chat' | 'preview' | 'browser'; export type MermaidRenderingMode = 'svg' | 'ascii'; diff --git a/scripts/convert-vscode-theme.cjs b/scripts/convert-vscode-theme.cjs index c4201f88..eb2ef057 100644 --- a/scripts/convert-vscode-theme.cjs +++ b/scripts/convert-vscode-theme.cjs @@ -108,7 +108,7 @@ function convertVsCodeTheme(vscodeThemePath) { const semanticTokenColors = normalizedTheme.semanticTokenColors || {}; // Determine variant - const isDark = normalizedTheme.type === 'dark' || + const isDark = normalizedTheme.type === 'dark' || colors['editor.background']?.toLowerCase() < '#808080' || colors['workbench.colorTheme']?.includes('dark');