Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/gni/devtools_grd_files.gni
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ grd_files_bundled_sources = [
"front_end/Images/breakpoint-circle.svg",
"front_end/Images/breakpoint-crossed-filled.svg",
"front_end/Images/breakpoint-crossed.svg",
"front_end/Images/browser-operator-logo.svg",
"front_end/Images/browser-operator-logo.png",
"front_end/Images/brush-2.svg",
"front_end/Images/brush-filled.svg",
Expand Down Expand Up @@ -646,6 +647,7 @@ grd_files_bundled_sources = [
"front_end/panels/ai_assistance/ai_assistance.js",
"front_end/panels/ai_chat/ui/AIChatPanel.js",
"front_end/panels/ai_chat/ui/ChatView.js",
"front_end/panels/ai_chat/ui/ConnectorsView.js",
"front_end/panels/ai_chat/ui/LiveAgentSessionComponent.js",
"front_end/panels/ai_chat/ui/ToolCallComponent.js",
"front_end/panels/ai_chat/ui/ToolResultComponent.js",
Expand Down Expand Up @@ -867,7 +869,8 @@ grd_files_bundled_sources = [
"front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgentV0.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgentV1.js",
"front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgentV2.js",
"front_end/panels/ai_chat/common/MarkdownViewerUtil.js",
"front_end/panels/ai_chat/evaluation/runner/VisionAgentEvaluationRunner.js",
Expand Down
3 changes: 2 additions & 1 deletion config/gni/devtools_image_files.gni
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ devtools_image_files = [
"touchCursor.png",
"gdp-logo-light.png",
"gdp-logo-dark.png",
"browser-operator-logo.png",
"demo.gif",
"browser-operator-logo.png",
]

devtools_svg_sources = [
"3d-center.svg",
"browser-operator-logo.svg",
"3d-pan.svg",
"3d-rotate.svg",
"accelerometer-back.svg",
Expand Down
Binary file modified front_end/Images/browser-operator-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions front_end/Images/src/browser-operator-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion front_end/entrypoints/main/main-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ Common.Settings.registerSettingExtension({
category: Common.Settings.SettingCategory.GLOBAL,
settingName: 'currentDockState',
settingType: Common.Settings.SettingType.ENUM,
defaultValue: 'right',
defaultValue: 'left',
options: [
{
value: 'right',
Expand Down
20 changes: 20 additions & 0 deletions front_end/panels/ai_chat/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ devtools_module("ai_chat") {
"core/AgentTestRunner.ts",
"ui/AIChatPanel.ts",
"ui/ChatView.ts",
"ui/ConnectorsView.ts",
"ui/HistoryView.ts",
"ui/EvaluationsView.ts",
"ui/message/MessageList.ts",
"ui/message/UserMessage.ts",
"ui/message/ModelMessage.ts",
Expand All @@ -34,6 +37,13 @@ devtools_module("ai_chat") {
"ui/input/InputBar.ts",
"ui/oauth/OAuthConnectPanel.ts",
"ui/version/VersionBanner.ts",
"ui/SidebarNav.ts",
"ui/ConnectorsDropdown.ts",
"ui/AgentDropdown.ts",
"ui/common/Switch.ts",
"ui/common/Button.ts",
"ui/common/SearchInput.ts",
"ui/common/Dropdown.ts",
"ui/LiveAgentSessionComponent.ts",
"ui/ToolCallComponent.ts",
"ui/ToolResultComponent.ts",
Expand Down Expand Up @@ -306,6 +316,9 @@ _ai_chat_sources = [
"core/AgentTestRunner.ts",
"ui/AIChatPanel.ts",
"ui/ChatView.ts",
"ui/ConnectorsView.ts",
"ui/HistoryView.ts",
"ui/EvaluationsView.ts",
"ui/message/MessageList.ts",
"ui/message/UserMessage.ts",
"ui/message/ModelMessage.ts",
Expand All @@ -318,6 +331,13 @@ _ai_chat_sources = [
"ui/input/InputBar.ts",
"ui/oauth/OAuthConnectPanel.ts",
"ui/version/VersionBanner.ts",
"ui/SidebarNav.ts",
"ui/ConnectorsDropdown.ts",
"ui/AgentDropdown.ts",
"ui/common/Switch.ts",
"ui/common/Button.ts",
"ui/common/SearchInput.ts",
"ui/common/Dropdown.ts",
"ui/LiveAgentSessionComponent.ts",
"ui/ToolCallComponent.ts",
"ui/ToolResultComponent.ts",
Expand Down
1 change: 1 addition & 0 deletions front_end/panels/ai_chat/core/Version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export const VERSION_INFO = {
} as const;

export const CURRENT_VERSION = VERSION_INFO.version;
export const RELEASE_URL = `https://github.com/tysonthomas9/browser-operator-devtools-frontend/releases/tag/v${CURRENT_VERSION}`;
130 changes: 111 additions & 19 deletions front_end/panels/ai_chat/ui/AIChatPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { isEvaluationEnabled, getEvaluationConfig } from '../common/EvaluationCo
import { EvaluationAgent } from '../evaluation/remote/EvaluationAgent.js';
import { BUILD_CONFIG } from '../core/BuildConfig.js';
import { OnboardingDialog, createSetupRequiredBanner } from './OnboardingDialog.js';
import { HistoryViewEvents } from './HistoryView.js';
// Import of LiveAgentSessionComponent is not required here; the element is
// registered by ChatView where it is used.

Expand Down Expand Up @@ -84,6 +85,7 @@ localStorage.removeItem = (key: string) => {

import chatViewStyles from './chatView.css.js';
import { ChatView } from './ChatView.js';
import type { SidebarNavItem } from './SidebarNav.js';
import { type ChatMessage, ChatMessageEntity, type ImageInputData, type ModelChatMessage, State as ChatViewState } from '../models/ChatTypes.js';
import { HelpDialog } from './HelpDialog.js';
import { SettingsDialog } from './SettingsDialog.js';
Expand Down Expand Up @@ -167,7 +169,7 @@ const UIStrings = {
/**
* @description Default text shown in the chat input
*/
inputPlaceholder: 'Ask a question...',
inputPlaceholder: 'Ask me anything...',
/**
* @description Placeholder when OpenAI API key is missing
*/
Expand Down Expand Up @@ -509,7 +511,7 @@ export class AIChatPanel extends UI.Panel.Panel {
this.#toolbarContainer = document.createElement('div');
this.#toolbarContainer.classList.add('toolbar-container');
this.#toolbarContainer.setAttribute('role', 'toolbar');
this.#toolbarContainer.style.cssText = 'display: flex; justify-content: space-between; width: 100%; padding: 0 4px; box-sizing: border-box; margin: 0 0 10px 0;';
this.#toolbarContainer.style.cssText = 'display: flex; justify-content: space-between; width: calc(100% - 36px); margin-left: 36px; padding: 0 4px; box-sizing: border-box; margin-top: 0; margin-bottom: 0; background: white;';
this.contentElement.appendChild(this.#toolbarContainer);

// Create left toolbar using DOM method (not constructor)
Expand All @@ -519,6 +521,9 @@ export class AIChatPanel extends UI.Panel.Panel {
this.#rightToolbar = this.#toolbarContainer.createChild('devtools-toolbar', 'ai-chat-right-toolbar') as UI.Toolbar.Toolbar;
this.#rightToolbar.style.cssText = 'overflow: visible;';

// Note: "What's new" pill is now rendered inside ChatView's centered content (per Figma design)
// Removed duplicate toolbar pill to match Figma specifications

// Create toolbar buttons ONCE
this.#newChatButton = new UI.Toolbar.ToolbarButton(
i18nString(UIStrings.newChat),
Expand All @@ -544,8 +549,6 @@ export class AIChatPanel extends UI.Panel.Panel {
this
);

this.#settingsMenuButton = this.#createSettingsMenuButton();

this.#closeButton = new UI.Toolbar.ToolbarButton(
'Close Chat Window',
'cross',
Expand All @@ -560,7 +563,6 @@ export class AIChatPanel extends UI.Panel.Panel {

// Add buttons to toolbars ONCE (order matters for right toolbar)
this.#leftToolbar.appendToolbarItem(this.#newChatButton);
this.#rightToolbar.appendToolbarItem(this.#settingsMenuButton);
this.#rightToolbar.appendToolbarItem(this.#closeButton);

// Create container for the chat view
Expand All @@ -577,6 +579,13 @@ export class AIChatPanel extends UI.Panel.Panel {

// Add event listener for manual setup requests from ChatView
this.#chatView.addEventListener('manual-setup-requested', this.#handleManualSetupRequest.bind(this));
// Wire sidebar navigation actions
this.#chatView.addEventListener('sidebar-nav', this.#handleSidebarNavEvent.bind(this));

// Wire HistoryView events
this.#chatView.addEventListener(HistoryViewEvents.REQUEST_DATA, this.#handleHistoryRequestData.bind(this));
this.#chatView.addEventListener(HistoryViewEvents.LOAD_CONVERSATION, this.#handleHistoryLoadConversation.bind(this));
this.#chatView.addEventListener(HistoryViewEvents.DELETE_CONVERSATION, this.#handleHistoryDeleteConversation.bind(this));
}

/**
Expand Down Expand Up @@ -1210,6 +1219,37 @@ export class AIChatPanel extends UI.Panel.Panel {
this.#onSettingsClick();
}

#handleSidebarNavEvent(event: Event): void {
const detail = (event as CustomEvent<{item: SidebarNavItem}>).detail;
if (!detail?.item) {
return;
}
this.#handleSidebarNavigation(detail.item);
}

#handleSidebarNavigation(item: SidebarNavItem): void {
// Only show new chat button in chat mode
const showNewChatButton = item === 'chat';
this.#newChatButton.setVisible(showNewChatButton);

switch (item) {
case 'help':
this.#onHelpClick();
break;
case 'agents':
this.#onAgentStudioClick();
break;
case 'chat':
case 'connectors': // Let ChatView handle routing to ConnectorsView
case 'settings': // Let ChatView handle routing to SettingsDialog
case 'history': // Let ChatView handle routing to HistoryView
case 'evaluations': // Let ChatView handle routing to EvaluationsView
default:
// ChatView will handle these via its internal routing
break;
}
}

/**
* Update the settings button highlight based on credentials state
*/
Expand Down Expand Up @@ -1731,6 +1771,29 @@ export class AIChatPanel extends UI.Panel.Panel {
onOAuthLogin: this.#handleOAuthLogin.bind(this),
// Add example prompt model switching
onExamplePromptModelSwitch: this.#handleExamplePromptModelSwitch.bind(this),
// Settings callbacks for inline SettingsDialog
onSettingsSaved: this.refreshCredentials.bind(this),
fetchLiteLLMModels: this.#fetchLiteLLMModels.bind(this),
updateModelOptions: (models: Array<{value: string, label: string}>, hadWildcard: boolean) => {
// Convert to ModelOption format if needed
const modelOptions: ModelOption[] = models.map(m => ({
value: m.value,
label: m.label,
type: (localStorage.getItem(PROVIDER_SELECTION_KEY) || 'openai') as ProviderType
}));
AIChatPanel.updateModelOptions(modelOptions, hadWildcard);
this.performUpdate();
},
getModelOptions: () => getModelOptions(),
addCustomModelOption: (model: {value: string, label: string}) => {
const currentProvider = (localStorage.getItem(PROVIDER_SELECTION_KEY) || 'openai') as ProviderType;
AIChatPanel.addCustomModelOption(model.value, currentProvider);
this.performUpdate();
},
removeCustomModelOption: (modelValue: string) => {
AIChatPanel.removeCustomModelOption(modelValue);
this.performUpdate();
},
};
} catch (error) {
logger.error('Error updating ChatView state:', error);
Expand Down Expand Up @@ -1941,6 +2004,45 @@ export class AIChatPanel extends UI.Panel.Panel {
logger.info('Conversation history dialog opened');
}

/**
* Handle request for history data from HistoryView
*/
async #handleHistoryRequestData(): Promise<void> {
const conversations = await this.#agentService.listConversations();
const currentId = this.#agentService.getCurrentConversationId();

// Find the history view and set its data
const historyView = this.#chatView.querySelector('ai-history-view') as any;
if (historyView) {
historyView.conversations = conversations;
historyView.currentConversationId = currentId;
}
}

/**
* Handle loading a conversation from HistoryView
*/
#handleHistoryLoadConversation(event: Event): void {
const customEvent = event as CustomEvent<{conversationId: string}>;
const conversationId = customEvent.detail?.conversationId;
if (conversationId) {
this.#loadConversation(conversationId);
}
}

/**
* Handle deleting a conversation from HistoryView
*/
#handleHistoryDeleteConversation(event: Event): void {
const customEvent = event as CustomEvent<{conversationId: string}>;
const conversationId = customEvent.detail?.conversationId;
if (conversationId) {
this.#deleteConversation(conversationId);
// Refresh the history view data after deletion
void this.#handleHistoryRequestData();
}
}

#onHelpClick(): void {
// Open external getting started docs in a new tab
UI.UIUtils.openInNewTab('https://browseroperator.io/docs/getting-started/');
Expand Down Expand Up @@ -1971,22 +2073,12 @@ export class AIChatPanel extends UI.Panel.Panel {
}

/**
* Handles the settings button click event and shows the settings dialog
* Handles the settings button click event - navigates to settings view
*/
#onSettingsClick(): void {
SettingsDialog.show(
this.#selectedModel,
this.#miniModel,
this.#nanoModel,
async () => {
await this.#handleSettingsChanged();
},
this.#fetchLiteLLMModels.bind(this),
(providerModels, hadWildcard) => { AIChatPanel.updateModelOptions(providerModels, hadWildcard); },
AIChatPanel.getModelOptions,
AIChatPanel.addCustomModelOption,
AIChatPanel.removeCustomModelOption
);
// Navigate to settings view
this.#handleSidebarNavigation('settings');
this.#chatView.setActiveSidebarItem('settings');
}

/**
Expand Down
Loading