mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-28 21:18:57 +00:00
Tweaks to AI Chat and AI Actions Dropdown (#3495)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Tweaks to AI Chat and AI Actions Dropdown
|
||||
@@ -23,7 +23,7 @@ export function AIMessageView(
|
||||
<div
|
||||
key={index}
|
||||
className={tcls(
|
||||
'flex animate-[fadeIn_500ms_both] flex-col gap-2',
|
||||
'flex animate-fadeIn-slow flex-col gap-2',
|
||||
step.content.nodes.length > 0 ? 'has-content' : ''
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -38,7 +38,7 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
|
||||
const { toolCall, context } = props;
|
||||
|
||||
return (
|
||||
<div className="flex origin-left animate-[scaleIn_500ms_500ms_both] items-start gap-2 text-sm text-tint-subtle">
|
||||
<div className="flex origin-left animate-scaleIn-slow items-start gap-2 text-sm text-tint-subtle">
|
||||
<Icon
|
||||
icon={getIconForToolCall(toolCall)}
|
||||
className="mt-1 size-3 shrink-0 text-tint-subtle/8"
|
||||
@@ -151,13 +151,13 @@ async function DescriptionForSearchToolCall(props: {
|
||||
<details className={tcls('-ml-5 group flex w-full flex-col', hasResults ? 'gap-2' : '')}>
|
||||
<summary
|
||||
className={tcls(
|
||||
'-mx-2 flex list-none items-center gap-2 rounded-corners:rounded-md pr-4 pl-7 transition-colors marker:hidden',
|
||||
'-mx-2 flex list-none items-center gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md pr-4 pl-7 transition-colors marker:hidden',
|
||||
hasResults ? '-my-2 cursor-pointer py-2 hover:bg-primary-hover' : ''
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col leading-snug">
|
||||
<p>{t(language, 'searched_for', <strong>{toolCall.query}</strong>)}</p>
|
||||
<p className="text-tint-subtle text-xs">
|
||||
<p className="mt-0.5 text-tint-subtle text-xs">
|
||||
{hasResults
|
||||
? t(language, 'search_results_count', toolCall.results.length)
|
||||
: t(language, 'search_no_results')}
|
||||
@@ -175,12 +175,12 @@ async function DescriptionForSearchToolCall(props: {
|
||||
) : null}
|
||||
</summary>
|
||||
{hasResults ? (
|
||||
<div className="max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all duration-500 [transition-behavior:allow-discrete] group-open:max-h-96 group-open:opacity-11">
|
||||
<div className="mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all duration-500 [transition-behavior:allow-discrete] group-open:max-h-96 group-open:opacity-11">
|
||||
<ol className="space-y-1">
|
||||
{searchResultsWithHrefs.map((result, index) => (
|
||||
<li
|
||||
key={`${result.pageId}-${index}`}
|
||||
className="animate-fadeIn"
|
||||
className="animate-fadeIn-slow"
|
||||
style={{
|
||||
animationDelay: `${index * 25}ms`,
|
||||
}}
|
||||
|
||||
@@ -112,7 +112,7 @@ export function AIChatWindow(props: {
|
||||
ref={containerRef}
|
||||
>
|
||||
<div className="relative flex h-full grow flex-col overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-md bg-tint-base text-sm text-tint depth-subtle:shadow-lg shadow-tint ring-1 ring-tint-subtle">
|
||||
<div className="flex items-center gap-2 border-tint-subtle border-b bg-tint-subtle px-4 py-2 text-tint-strong">
|
||||
<div className="flex select-none items-center gap-2 border-tint-subtle border-b bg-tint-subtle px-4 py-2 text-tint-strong">
|
||||
<AIChatIcon
|
||||
className="size-5 text-tint"
|
||||
trademark={trademark}
|
||||
@@ -149,7 +149,7 @@ export function AIChatWindow(props: {
|
||||
onClick={() => {}}
|
||||
iconOnly
|
||||
icon="ellipsis"
|
||||
label={tString(language, 'more')}
|
||||
label={tString(language, 'actions')}
|
||||
className="!px-2"
|
||||
variant="blank"
|
||||
size="default"
|
||||
@@ -162,7 +162,10 @@ export function AIChatWindow(props: {
|
||||
}}
|
||||
disabled={isEmpty}
|
||||
>
|
||||
<Icon icon="broom-wide" className="size-3 text-tint-subtle" />
|
||||
<Icon
|
||||
icon="broom-wide"
|
||||
className="size-3 shrink-0 text-tint-subtle"
|
||||
/>
|
||||
{t(language, 'ai_chat_clear_conversation')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
@@ -186,7 +189,7 @@ export function AIChatWindow(props: {
|
||||
>
|
||||
{isEmpty ? (
|
||||
<div className="flex min-h-full w-full shrink-0 flex-col items-center justify-center gap-6 py-4">
|
||||
<div className="flex size-32 animate-[fadeIn_500ms_both] items-center justify-center rounded-full bg-tint-subtle">
|
||||
<div className="flex size-32 animate-fadeIn-slow items-center justify-center rounded-full bg-tint-subtle">
|
||||
<AIChatIcon
|
||||
state="intro"
|
||||
trademark={trademark}
|
||||
@@ -215,7 +218,7 @@ export function AIChatWindow(props: {
|
||||
</div>
|
||||
<div
|
||||
ref={inputRef}
|
||||
className="absolute inset-x-0 bottom-0 mr-2 flex flex-col gap-4 bg-gradient-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2"
|
||||
className="absolute inset-x-0 bottom-0 mr-2 flex select-none flex-col gap-4 bg-gradient-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2"
|
||||
>
|
||||
{/* Display an error banner when something went wrong. */}
|
||||
{chat.error ? <AIChatError chatController={chatController} /> : null}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function AIChatMessages(props: {
|
||||
<div
|
||||
ref={isLastUserMessage ? lastUserMessageRef : undefined}
|
||||
className={tcls(
|
||||
message.content ? 'animate-[fadeIn_500ms_both]' : '',
|
||||
message.content ? 'animate-fadeIn-slow' : '',
|
||||
'shrink-0',
|
||||
'last:min-h-[calc(100%-5rem)]',
|
||||
'scroll-mt-36',
|
||||
@@ -43,11 +43,11 @@ export function AIChatMessages(props: {
|
||||
{message.content ? message.content : null}
|
||||
|
||||
{isLastMessage && chat.loading ? (
|
||||
<div className="flex w-full animate-[fadeIn_500ms_both] flex-wrap gap-2 group-has-[.has-content]/message:hidden">
|
||||
<div className="flex w-full animate-fadeIn-slow flex-wrap gap-2 group-has-[.has-content]/message:hidden">
|
||||
{Array.from({ length: 7 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-4 animate-[fadeIn_0.5s_ease_both,pulse_1.5s_infinite] rounded-md bg-tint-4"
|
||||
className="h-4 animate-[fadeIn_500ms_ease_both,pulse_1.5s_infinite] circular-corners:rounded-2xl rounded-corners:rounded-md bg-tint-4"
|
||||
style={{
|
||||
width: `calc(${(index % 4) * 20 + 10}% - 4px)`,
|
||||
animationDelay: `${index * 0.1}s`,
|
||||
|
||||
@@ -59,8 +59,8 @@ export function AIResponseFeedback(props: {
|
||||
/>
|
||||
{rating !== null ? (
|
||||
<span
|
||||
className="ml-2 animate-fadeIn text-tint-subtle"
|
||||
style={{ animationDelay: '.3s', animationDuration: '.5s' }}
|
||||
className="ml-2 animate-fadeIn-slow text-tint-subtle"
|
||||
style={{ animationDelay: '.3s' }}
|
||||
>
|
||||
{t(language, 'was_this_helpful_thank_you')}
|
||||
</span>
|
||||
|
||||
@@ -23,7 +23,7 @@ export function AIChatFollowupSuggestions(props: {
|
||||
chatController.postMessage({ message: suggestion });
|
||||
}}
|
||||
label={suggestion}
|
||||
className="max-w-[80%] animate-[present_500ms_both] whitespace-normal text-left ring-1 ring-tint-subtle"
|
||||
className="animate-[present_500ms_both] whitespace-normal text-left ring-1 ring-tint-subtle"
|
||||
size="medium"
|
||||
variant="blank"
|
||||
style={{
|
||||
|
||||
@@ -95,8 +95,9 @@ export function PageAside(props: {
|
||||
'page-api-block:xl:max-2xl:dark:hover:shadow-tint-1/1',
|
||||
'page-api-block:xl:max-2xl:rounded-md',
|
||||
'page-api-block:xl:max-2xl:h-auto',
|
||||
'page-api-block:xl:max-2xl:my-4',
|
||||
'page-api-block:p-2'
|
||||
'page-api-block:xl:max-2xl:my-9',
|
||||
'page-api-block:px-2',
|
||||
'page-api-block:py-1.5'
|
||||
)}
|
||||
>
|
||||
{page.layout.outline ? (
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function PageHeader(props: {
|
||||
(withAIChat || pageActions.markdown || pageActions.externalAI) ? (
|
||||
<div
|
||||
className={tcls(
|
||||
'float-right mb-2 ml-4',
|
||||
'float-right mb-2 ml-4 page-api-block:xl:max-2xl:mr-[14.5rem]',
|
||||
ancestors.length > 0 ? '-mt-2' : 'xs:mt-2'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
||||
import { useState } from 'react';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
|
||||
export function Tooltip(props: {
|
||||
children: React.ReactNode;
|
||||
@@ -22,10 +26,24 @@ export function Tooltip(props: {
|
||||
className,
|
||||
} = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<RadixTooltip.Provider delayDuration={300} {...providerProps}>
|
||||
<RadixTooltip.Root {...rootProps}>
|
||||
<RadixTooltip.Trigger asChild {...triggerProps}>
|
||||
<RadixTooltip.Root open={open} onOpenChange={(e) => setOpen(e)} {...rootProps}>
|
||||
<RadixTooltip.Trigger
|
||||
asChild
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
if (isMobile) {
|
||||
setOpen(true);
|
||||
}
|
||||
setOpen(true);
|
||||
}}
|
||||
onPointerDown={(event) => event.preventDefault()}
|
||||
{...triggerProps}
|
||||
>
|
||||
{children}
|
||||
</RadixTooltip.Trigger>
|
||||
<RadixTooltip.Portal>
|
||||
@@ -35,6 +53,12 @@ export function Tooltip(props: {
|
||||
'z-50 max-w-xs animate-scaleIn circular-corners:rounded-2xl rounded-corners:rounded-md bg-tint-12 px-2 py-1 text-contrast-tint-12 text-sm',
|
||||
className
|
||||
)}
|
||||
onPointerDownOutside={(event) => {
|
||||
event.preventDefault();
|
||||
if (isMobile) {
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
{...contentProps}
|
||||
>
|
||||
{label}
|
||||
|
||||
@@ -8,7 +8,7 @@ export const ButtonStyles = [
|
||||
'gap-2',
|
||||
'rounded-md',
|
||||
'straight-corners:rounded-none',
|
||||
'circular-corners:rounded-full',
|
||||
'circular-corners:rounded-3xl',
|
||||
|
||||
'border',
|
||||
'border-tint',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const de = {
|
||||
ai_chat_context_disclaimer: 'KI-Antworten können Fehler enthalten.',
|
||||
ai_chat_input_placeholder: 'Fragen, suchen oder Aktion ausführen...',
|
||||
send: 'Senden',
|
||||
actions: 'Aktionen',
|
||||
ai_chat_suggested_questions_title: 'Vorgeschlagene Fragen',
|
||||
ai_chat_suggested_questions_about_this_page: 'Worum geht es auf dieser Seite?',
|
||||
ai_chat_suggested_questions_read_next: 'Was sollte ich als nächstes lesen?',
|
||||
|
||||
@@ -86,6 +86,7 @@ export const en = {
|
||||
ai_chat_context_disclaimer: 'AI responses may contain mistakes.',
|
||||
ai_chat_input_placeholder: 'Ask, search, or take action...',
|
||||
send: 'Send',
|
||||
actions: 'Actions',
|
||||
ai_chat_suggested_questions_title: 'Suggested questions',
|
||||
ai_chat_suggested_questions_about_this_page: 'What is this page about?',
|
||||
ai_chat_suggested_questions_read_next: 'What should I read next?',
|
||||
|
||||
@@ -90,6 +90,7 @@ export const es: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: 'Las respuestas de IA pueden contener errores.',
|
||||
ai_chat_input_placeholder: 'Pregunta, busca o realiza una acción...',
|
||||
send: 'Enviar',
|
||||
actions: 'Acciones',
|
||||
ai_chat_suggested_questions_title: 'Preguntas sugeridas',
|
||||
ai_chat_suggested_questions_about_this_page: '¿De qué trata esta página?',
|
||||
ai_chat_suggested_questions_read_next: '¿Qué debería leer después?',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const fr: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: "Les réponses de l'IA peuvent contenir des erreurs.",
|
||||
ai_chat_input_placeholder: 'Demander, rechercher ou effectuer une action...',
|
||||
send: 'Envoyer',
|
||||
actions: 'Actions',
|
||||
ai_chat_suggested_questions_title: 'Questions suggérées',
|
||||
ai_chat_suggested_questions_about_this_page: 'De quoi parle cette page ?',
|
||||
ai_chat_suggested_questions_read_next: 'Que devrais-je lire ensuite ?',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const ja: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: 'AIの回答には誤りが含まれる場合があります。',
|
||||
ai_chat_input_placeholder: '質問、検索、またはアクションを実行...',
|
||||
send: '送信',
|
||||
actions: 'アクション',
|
||||
ai_chat_suggested_questions_title: 'おすすめの質問',
|
||||
ai_chat_suggested_questions_about_this_page: 'このページは何について説明していますか?',
|
||||
ai_chat_suggested_questions_read_next: '次に何を読むべきですか?',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const nl: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: 'AI-antwoorden kunnen fouten bevatten.',
|
||||
ai_chat_input_placeholder: 'Vraag, zoek of voer een actie uit...',
|
||||
send: 'Versturen',
|
||||
actions: 'Acties',
|
||||
ai_chat_suggested_questions_title: 'Voorgestelde vragen',
|
||||
ai_chat_suggested_questions_about_this_page: 'Waar gaat deze pagina over?',
|
||||
ai_chat_suggested_questions_read_next: 'Wat moet ik hierna lezen?',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const no: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: 'AI-svar kan inneholde feil.',
|
||||
ai_chat_input_placeholder: 'Spør, søk eller utfør en handling...',
|
||||
send: 'Send',
|
||||
actions: 'Handlinger',
|
||||
ai_chat_suggested_questions_title: 'Foreslåtte spørsmål',
|
||||
ai_chat_suggested_questions_about_this_page: 'Hva handler denne siden om?',
|
||||
ai_chat_suggested_questions_read_next: 'Hva bør jeg lese neste?',
|
||||
|
||||
@@ -88,6 +88,7 @@ export const pt_br = {
|
||||
ai_chat_context_disclaimer: 'Respostas de IA podem conter erros.',
|
||||
ai_chat_input_placeholder: 'Pergunte, pesquise ou execute uma ação...',
|
||||
send: 'Enviar',
|
||||
actions: 'Ações',
|
||||
ai_chat_suggested_questions_title: 'Perguntas sugeridas',
|
||||
ai_chat_suggested_questions_about_this_page: 'Sobre o que é esta página?',
|
||||
ai_chat_suggested_questions_read_next: 'O que devo ler em seguida?',
|
||||
|
||||
@@ -85,6 +85,7 @@ export const zh: TranslationLanguage = {
|
||||
ai_chat_context_disclaimer: '人工智能的回答可能包含错误。',
|
||||
ai_chat_input_placeholder: '询问、搜索或执行操作...',
|
||||
send: '发送',
|
||||
actions: '操作',
|
||||
ai_chat_suggested_questions_title: '建议的问题',
|
||||
ai_chat_suggested_questions_about_this_page: '这个页面讲的是什么?',
|
||||
ai_chat_suggested_questions_read_next: '我接下来应该读什么?',
|
||||
|
||||
@@ -298,10 +298,16 @@ const config: Config = {
|
||||
},
|
||||
animation: {
|
||||
present: 'present 200ms cubic-bezier(0.25, 1, 0.5, 1) both',
|
||||
'present-slow': 'present 500ms cubic-bezier(0.25, 1, 0.5, 1) both',
|
||||
scaleIn: 'scaleIn 200ms ease both',
|
||||
'scaleIn-slow': 'scaleIn 500ms ease both',
|
||||
scaleOut: 'scaleOut 200ms ease both',
|
||||
'scaleOut-slow': 'scaleOut 500ms ease both',
|
||||
fadeIn: 'fadeIn 200ms ease both',
|
||||
'fadeIn-slow': 'fadeIn 500ms ease both',
|
||||
fadeOut: 'fadeOut 200ms ease both',
|
||||
'fadeOut-slow': 'fadeOut 500ms ease both',
|
||||
|
||||
enterFromLeft: 'enterFromLeft 250ms cubic-bezier(0.83, 0, 0.17, 1) both',
|
||||
enterFromRight: 'enterFromRight 250ms cubic-bezier(0.83, 0, 0.17, 1) both',
|
||||
exitToLeft: 'exitToLeft 250ms cubic-bezier(0.83, 0, 0.17, 1) both',
|
||||
|
||||
Reference in New Issue
Block a user