From c54f0da7378b9d11b393614ad0bb420531716a5b Mon Sep 17 00:00:00 2001
From: Bee <68532117+abeatrix@users.noreply.github.com>
Date: Tue, 29 Jul 2025 10:05:01 -0700
Subject: [PATCH] feat: adds navigation bar component and restructure app
layout (#5220)
---
webview-ui/src/App.tsx | 4 +-
webview-ui/src/components/chat/ChatView.tsx | 37 +++++------
webview-ui/src/components/menu/Navbar.tsx | 61 +++++++++++++++++++
.../src/context/ExtensionStateContext.tsx | 46 +++++++-------
4 files changed, 105 insertions(+), 43 deletions(-)
create mode 100644 webview-ui/src/components/menu/Navbar.tsx
diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx
index 9c2df12e73..9de0a605d3 100644
--- a/webview-ui/src/App.tsx
+++ b/webview-ui/src/App.tsx
@@ -58,7 +58,7 @@ const AppContent = () => {
}
return (
- <>
+
{showSettings && }
{showHistory && }
{showMcp && }
@@ -77,7 +77,7 @@ const AppContent = () => {
showAnnouncement={showAnnouncement}
hideAnnouncement={hideAnnouncement}
/>
- >
+
)
}
diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx
index 531c609fdb..b444671038 100644
--- a/webview-ui/src/components/chat/ChatView.tsx
+++ b/webview-ui/src/components/chat/ChatView.tsx
@@ -1,32 +1,32 @@
-import { useCallback, useEffect, useMemo } from "react"
-import { useMount } from "react-use"
-import { ClineApiReqInfo, ClineMessage } from "@shared/ExtensionMessage"
import { findLast } from "@shared/array"
import { combineApiRequests } from "@shared/combineApiRequests"
import { combineCommandSequences } from "@shared/combineCommandSequences"
+import type { ClineApiReqInfo, ClineMessage } from "@shared/ExtensionMessage"
import { getApiMetrics } from "@shared/getApiMetrics"
+import { BooleanRequest, EmptyRequest, StringRequest } from "@shared/proto/cline/common"
+import { useCallback, useEffect, useMemo } from "react"
+import { useMount } from "react-use"
+import { normalizeApiConfiguration } from "@/components/settings/utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { FileServiceClient, UiServiceClient } from "@/services/grpc-client"
-import { normalizeApiConfiguration } from "@/components/settings/utils/providerUtils"
-import { BooleanRequest, EmptyRequest, StringRequest } from "@shared/proto/cline/common"
-
+import { Navbar } from "../menu/Navbar"
// Import utilities and hooks from the new structure
import {
+ ActionButtons,
+ CHAT_CONSTANTS,
+ ChatLayout,
convertHtmlToMarkdown,
filterVisibleMessages,
groupMessages,
- CHAT_CONSTANTS,
- useChatState,
- useButtonState,
- useScrollBehavior,
- useMessageHandlers,
- useIsStreaming,
- ChatLayout,
- WelcomeSection,
- TaskSection,
- MessagesArea,
- ActionButtons,
InputSection,
+ MessagesArea,
+ TaskSection,
+ useButtonState,
+ useChatState,
+ useIsStreaming,
+ useMessageHandlers,
+ useScrollBehavior,
+ WelcomeSection,
} from "./chat-view"
interface ChatViewProps {
@@ -39,6 +39,8 @@ interface ChatViewProps {
// Use constants from the imported module
const MAX_IMAGES_AND_FILES_PER_MESSAGE = CHAT_CONSTANTS.MAX_IMAGES_AND_FILES_PER_MESSAGE
+const IS_STANDALONE = window?.__is_standalone__ ?? false
+
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
const {
version,
@@ -321,6 +323,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
return (
+ {IS_STANDALONE && }
{task ? (
{
+ const { navigateToHistory, navigateToSettings, navigateToAccount, navigateToMcp, navigateToChat } = useExtensionState()
+
+ const SETTINGS_TABS = useMemo(
+ () => [
+ {
+ id: "chat",
+ name: "Chat",
+ icon: PlusIcon,
+ navigate: navigateToChat,
+ },
+ {
+ id: "mcp",
+ name: "MCP",
+ icon: DatabaseIcon,
+ navigate: navigateToMcp,
+ },
+ {
+ id: "history",
+ name: "History",
+ icon: HistoryIcon,
+ navigate: navigateToHistory,
+ },
+ {
+ id: "account",
+ name: "Account",
+ icon: UserCircleIcon,
+ navigate: navigateToAccount,
+ },
+ {
+ id: "settings",
+ name: "Settings",
+ icon: SettingsIcon,
+ navigate: navigateToSettings,
+ },
+ ],
+ [navigateToAccount, navigateToChat, navigateToHistory, navigateToMcp, navigateToSettings],
+ )
+
+ return (
+
+ )
+}
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx
index f8bb1b3c5f..57227621ff 100644
--- a/webview-ui/src/context/ExtensionStateContext.tsx
+++ b/webview-ui/src/context/ExtensionStateContext.tsx
@@ -1,39 +1,37 @@
-import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"
+import type React from "react"
+import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"
import "../../../src/shared/webview/types"
-import {
- StateServiceClient,
- ModelsServiceClient,
- UiServiceClient,
- FileServiceClient,
- McpServiceClient,
-} from "../services/grpc-client"
+import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
+import { findLastIndex } from "@shared/array"
+import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
+import { type ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
+import { DEFAULT_PLATFORM, type ExtensionState } from "@shared/ExtensionMessage"
+import { DEFAULT_MCP_DISPLAY_MODE } from "@shared/McpDisplayMode"
+import type { UserInfo } from "@shared/proto/cline/account"
import { EmptyRequest, StringRequest } from "@shared/proto/cline/common"
-import { UpdateSettingsRequest } from "@shared/proto/cline/state"
+import type { OpenRouterCompatibleModelInfo } from "@shared/proto/cline/models"
+import { type TerminalProfile, UpdateSettingsRequest } from "@shared/proto/cline/state"
import { WebviewProviderType as WebviewProviderTypeEnum, WebviewProviderTypeRequest } from "@shared/proto/cline/ui"
-import { TerminalProfile } from "@shared/proto/cline/state"
import { convertProtoToClineMessage } from "@shared/proto-conversions/cline-message"
import { convertProtoMcpServersToMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
-import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
-import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
-import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
-import { DEFAULT_PLATFORM, ExtensionState } from "@shared/ExtensionMessage"
-import { findLastIndex } from "@shared/array"
import {
- ModelInfo,
+ groqDefaultModelId,
+ groqModels,
+ type ModelInfo,
openRouterDefaultModelId,
openRouterDefaultModelInfo,
requestyDefaultModelId,
requestyDefaultModelInfo,
- groqDefaultModelId,
- groqModels,
- huggingFaceDefaultModelId,
- huggingFaceModels,
} from "../../../src/shared/api"
-import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
+import type { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
+import {
+ FileServiceClient,
+ McpServiceClient,
+ ModelsServiceClient,
+ StateServiceClient,
+ UiServiceClient,
+} from "../services/grpc-client"
import { convertTextMateToHljs } from "../utils/textMateToHljs"
-import { OpenRouterCompatibleModelInfo } from "@shared/proto/cline/models"
-import { UserInfo } from "@shared/proto/cline/account"
-import { DEFAULT_MCP_DISPLAY_MODE } from "@shared/McpDisplayMode"
interface ExtensionStateContextType extends ExtensionState {
didHydrateState: boolean