Skip to content

Commit

Permalink
[AI Chat]: Add UI for Tab Informer
Browse files Browse the repository at this point in the history
  • Loading branch information
fallaciousreasoning committed Jan 14, 2025
1 parent e0839bc commit 41d03a3
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 128 deletions.
4 changes: 4 additions & 0 deletions components/ai_chat/core/browser/constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ base::span<const webui::LocalizedString> GetLocalizedStrings() {
{"searchQueries", IDS_CHAT_UI_SEARCH_QUERIES},
{"learnMore", IDS_CHAT_UI_LEARN_MORE},
{"closeNotice", IDS_CHAT_UI_CLOSE_NOTICE},
{"attachmentsTitle", IDS_CHAT_UI_ATTACHMENTS_TITLE},
{"attachmentsDescription", IDS_CHAT_UI_ATTACHMENTS_DESCRIPTION},
{"attachmentsBrowserTabsTitle",
IDS_CHAT_UI_ATTACHMENTS_BROWSER_TABS_TITLE},
{"noticeConversationHistoryBody",
IDS_CHAT_UI_NOTICE_CONVERSATION_HISTORY_BODY},
{"noticeConversationHistoryEmpty",
Expand Down
56 changes: 56 additions & 0 deletions components/ai_chat/resources/page/components/attachments/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2025 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
import * as React from 'react'
import styles from './style.module.scss'
import Button from '@brave/leo/react/button'
import Icon from '@brave/leo/react/icon'
import Input from '@brave/leo/react/input'
import RadioButton from '@brave/leo/react/radioButton'
import Flex from '$web-common/Flex'
import { useAIChat } from '../../state/ai_chat_context'
import { Tab } from 'components/ai_chat/resources/common/mojom'
import { useConversation } from '../../state/conversation_context'
import { getLocale } from '$web-common/locale'

function TabItem({ tab }: { tab: Tab }) {
const aiChat = useAIChat()
const { conversationUuid, associatedContentInfo } = useConversation()
return <RadioButton className={styles.tabItem} currentValue={associatedContentInfo?.contentId} value={tab.contentId} name='tab-item' onChange={() => {
aiChat.uiHandler?.associateTab(tab, conversationUuid!)
}}>
<span className={styles.title}>{tab.title}</span>
<img className={styles.icon} src={`chrome://favicon2/?size=20&pageUrl=${encodeURIComponent(tab.url.url)}`} />
</RadioButton>
}

export default function Attachments() {
const aiChat = useAIChat()
const conversation = useConversation()
const [search, setSearch] = React.useState('')

const tabs = aiChat.windows.flatMap(w => w.tabs).filter(t => t.title.toLowerCase().includes(search.toLowerCase()))
return <div className={styles.root}>
<div className={styles.header}>
<Flex direction='row' justify='space-between' align='center'>
<h4>{getLocale('attachmentsTitle')}</h4>
<Button fab kind='plain-faint' size='small' onClick={() => conversation.setShowAttachments(false)}>
<Icon name='close' />
</Button>
</Flex>
<span className={styles.description}>{getLocale('attachmentsDescription')}</span>
</div>
<div className={styles.tabSearchContainer}>
<Flex direction='row' justify='space-between' align='center'>
<h5>{getLocale('attachmentsBrowserTabsTitle')}</h5>
</Flex>
<Input placeholder='Search tabs' value={search} onInput={e => setSearch(e.value)}>
<Icon name='search' slot='icon-after' />
</Input>
<div className={styles.tabList}>
{tabs.map(t => <TabItem key={t.id} tab={t} />)}
</div>
</div>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.root {
background: var(--leo-color-container-highlight);
padding: var(--leo-spacing-m);
margin-top: var(--leo-spacing-m);
border-radius: var(--leo-radius-l);
box-shadow: var(--leo-effect-elevation-01);

& leo-button[fab] {
flex: 0;
}

& h4 {
margin: 0;
color: var(--leo-color-text-secondary);
font: var(--leo-font-heading-h4);
}

& h5 {
margin: 0;
color: var(--leo-color-text-secondary);
font: var(--leo-font-default-semibold);
}
}

.header {
padding: var(--leo-spacing-l) var(--leo-spacing-xl);
}

.tabSearchContainer {
background: var(--leo-color-container-background);
padding: var(--leo-spacing-l) var(--leo-spacing-xl);
border-radius: var(--leo-radius-l);
display: flex;
flex-direction: column;
gap: var(--leo-spacing-m);
}

.tabList {
display: flex;
flex-direction: column;
gap: var(--leo-spacing-s);

border: 1px solid var(--leo-color-divider-subtle);
border-radius: var(--leo-radius-m);

padding: var(--leo-spacing-m);
}

.description {
font: var(--leo-font-small-regular);
color: var(--leo-color-text-tertiary);
}

.tabItem {
--leo-radiobutton-flex-direction: row-reverse;

padding: var(--leo-spacing-m);
display: flex;
gap: var(--leo-spacing-m);
align-items: center;
justify-content: space-between;

& .icon {
flex-shrink: 0;
}

& .title {
text-wrap: nowrap;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;

color: var(--leo-color-text-primary);
font: var(--leo-font-default-regular);
}
}
13 changes: 12 additions & 1 deletion components/ai_chat/resources/page/components/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import getAPI from '../../api'
import FeatureButtonMenu, { Props as FeatureButtonMenuProps } from '../feature_button_menu'
import styles from './style.module.scss'
import { useAIChat, useIsSmall } from '../../state/ai_chat_context'
import { useConversation } from '../../state/conversation_context'
import { useConversation, useSupportsAttachments } from '../../state/conversation_context'
import { getLocale } from '$web-common/locale'
import { tabAssociatedChatId, useActiveChat } from '../../state/active_chat_context'

Expand Down Expand Up @@ -42,6 +42,8 @@ export const ConversationHeader = React.forwardRef(function (props: FeatureButto
const activeConversation = aiChatContext.visibleConversations.find(c => c.uuid === conversationContext.conversationUuid)
const showTitle = (!isTabAssociated || aiChatContext.isStandalone) && !isMobile
const canShowFullScreenButton = aiChatContext.isHistoryFeatureEnabled && !isMobile && !aiChatContext.isStandalone && conversationContext.conversationUuid
const supportsAttachments = useSupportsAttachments()

return (
<div className={styles.header} ref={ref}>
{showTitle ? (
Expand Down Expand Up @@ -97,6 +99,15 @@ export const ConversationHeader = React.forwardRef(function (props: FeatureButto
>
<Icon name='expand' />
</Button>}
{supportsAttachments && <Button
fab
kind={conversationContext.showAttachments ? 'plain' : 'plain-faint'}
aria-label={getLocale('attachmentsTitle')}
title={getLocale('attachmentsTitle')}
onClick={() => conversationContext.setShowAttachments(!conversationContext.showAttachments)}
>
<Icon name='attachment' />
</Button>}
<FeatureButtonMenu {...props} />
{!aiChatContext.isStandalone &&
<Button
Expand Down
Loading

0 comments on commit 41d03a3

Please sign in to comment.