Skip to content
Merged
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
180 changes: 114 additions & 66 deletions packages/gitbook/src/components/AIChat/AIChatIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { tcls } from '@/lib/tailwind';
import { Icon, IconStyle } from '@gitbook/icons';
import type React from 'react';

Expand All @@ -21,12 +20,18 @@ export function AIChatIcon({
<Icon
icon="sparkle"
{...props}
className={tcls(
className,
(state === 'thinking' || state === 'working') &&
'animate-[spin_2s_infinite_forwards_cubic-bezier(0.16,1,0.3,1)]',
state === 'intro' && 'animate-[spin_2s_forwards_cubic-bezier(0.16,1,0.3,1)]'
)}
className={className}
style={{
animation: {
intro: 'spin 2s forwards cubic-bezier(0.16,1,0.3,1)',
thinking: 'spin 2s infinite forwards cubic-bezier(0.16,1,0.3,1)',
working: 'spin 2s infinite forwards cubic-bezier(0.16,1,0.3,1)',
done: '',
confirm: '',
default: '',
error: '',
}[state],
}}
/>
);
}
Expand All @@ -50,28 +55,36 @@ export function AIChatIcon({
<path
d="M12.8916 1.06265C12.921 0.979101 13.0392 0.979127 13.0685 1.06267C13.239 1.5478 13.3439 1.84646 13.516 2.1032C13.6683 2.33042 13.8578 2.53033 14.0766 2.6945C14.3239 2.88 14.6165 3.00068 15.0919 3.19671C15.1761 3.23142 15.1761 3.3506 15.0919 3.38531C14.6165 3.58134 14.3239 3.70203 14.0766 3.88752C13.8578 4.05169 13.6683 4.2516 13.516 4.47882C13.3439 4.73556 13.239 5.03423 13.0685 5.51937C13.0392 5.60291 12.921 5.60292 12.8916 5.51938C12.7212 5.03423 12.6162 4.73557 12.4442 4.47882C12.2919 4.2516 12.1023 4.05169 11.8835 3.88752C11.6363 3.70202 11.3436 3.58134 10.8682 3.38531C10.7841 3.3506 10.7841 3.23141 10.8683 3.1967C11.3436 3.00067 11.6363 2.87999 11.8835 2.6945C12.1023 2.53033 12.2919 2.33042 12.4442 2.1032C12.6162 1.84646 12.7212 1.54779 12.8916 1.06265Z"
stroke="currentColor"
strokeWidth="1.2"
strokeWidth={state === 'intro' ? '1.2' : '1'}
strokeLinejoin="round"
className={tcls(
state === 'intro' &&
'animate-[fadeIn_.5s_.7s_both,spin_2s_1s_forwards_cubic-bezier(.43,1.54,.64,1)]',
(state === 'working' || state === 'thinking') &&
'animate-[fadeIn_.5s_.3s_both,spin_2s_1s_infinite_forwards_cubic-bezier(0.16,1,0.3,1)]',
state === 'done' && 'animate-[fadeOut_.5s_both]',
state === 'confirm' && 'animate-[fadeOut_.5s_both]',
state === 'default' && 'animate-[fadeIn_0s_both]',
state === 'error' && 'hidden'
)}
style={{ transformOrigin: '13px 3.5px' }}
shapeRendering="crispEdges"
className="transition-opacity duration-300"
style={{
animation: {
intro: 'fadeIn .5s .5s backwards, spin 2s .5s forwards cubic-bezier(0.43,1.54,0.64,1)',
thinking: 'spin 2s 1s infinite forwards cubic-bezier(0.16,1,0.3,1)',
working: 'spin 2s 1s infinite forwards cubic-bezier(0.16,1,0.3,1)',
done: '',
confirm: '',
default: '',
error: '',
}[state],
transitionDelay:
state === 'default' || state === 'thinking' ? '.3s' : undefined,
opacity: ['done', 'confirm', 'error'].includes(state) ? 0 : 1,
transformOrigin: '13px 3.5px',
}}
/>

{/* Error */}
<g
clipPath="url(#clip0_153_2034)"
className={tcls(
'text-danger-subtle',
state === 'error' ? 'animate-[fadeIn_.5s_.3s_both]' : 'hidden'
)}
className="transition-opacity duration-300"
style={{
color: 'rgb(var(--danger-9))',
opacity: state === 'error' ? 1 : 0,
transitionDelay: state === 'error' ? '.3s' : undefined,
}}
>
<path
d="M13.0312 1.42059L13.0312 3.95184"
Expand All @@ -94,37 +107,52 @@ export function AIChatIcon({
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
className={tcls(
state === 'done'
? 'animate-[fadeIn_.5s_.3s_both]'
: 'animate-[fadeOut_.5s_both]',
state === 'intro' && 'hidden',
state === 'confirm' && 'hidden'
)}
className="transition-opacity duration-300"
style={{
opacity: state === 'done' ? 1 : 0,
transitionDelay: state === 'done' ? '.3s' : undefined,
}}
/>

{/* Confirm */}
<path
className={tcls(
'fill-primary-9',
state === 'confirm'
? 'animate-[fadeIn_.5s_.3s_both,bounceSmall_1s_infinite_both]'
: state === 'thinking'
? 'animate-[fadeOut_.5s_both]'
: 'hidden'
)}
className="transition-opacity duration-300"
style={{
fill: 'rgb(var(--primary-9))',
opacity: state === 'confirm' ? 1 : 0,
transitionDelay: state === 'confirm' ? '.3s' : undefined,
animation: {
intro: '',
thinking: '',
working: '',
done: '',
confirm: 'bounceSmall 1s infinite both',
default: '',
error: '',
}[state],
}}
d="M12.9463 5.24512C13.3688 5.24422 13.713 5.58625 13.7139 6.00879C13.7146 6.43114 13.3725 6.77338 12.9502 6.77441C12.5279 6.77505 12.1845 6.43408 12.1836 6.01172C12.1828 5.58953 12.5242 5.24649 12.9463 5.24512ZM13.0391 0.0751953C14.0688 0.0730893 14.9049 0.90586 14.9072 1.93555C14.9084 2.5063 14.6484 3.04679 14.2012 3.40137L13.7773 3.7373C13.6151 3.86604 13.5201 4.06239 13.5205 4.26953V4.30371C13.5211 4.62139 13.2639 4.879 12.9463 4.87988C12.6288 4.88032 12.3701 4.62417 12.3691 4.30664V4.27246C12.3679 3.71272 12.6238 3.18263 13.0625 2.83496L13.4854 2.49902C13.6565 2.36341 13.7562 2.1568 13.7559 1.93848C13.755 1.54463 13.4358 1.22503 13.042 1.22559H12.9385C12.488 1.22679 12.1225 1.59352 12.123 2.04395L12.124 2.21875C12.1245 2.53649 11.8676 2.79522 11.5498 2.7959C11.2321 2.79653 10.9746 2.53928 10.9736 2.22168L10.9727 2.04688C10.9706 0.960578 11.8493 0.0778178 12.9355 0.0751953H13.0391Z"
/>

{/* Background */}
{/* Background */}
<path
d="M3.5625 8.78512L7.26347 10.9219C7.88227 11.2791 8.64467 11.2791 9.26347 10.9219L14.25 8.0429C14.5833 7.85045 15 8.09101 15 8.47591V10.2777C15 10.4563 14.9047 10.6214 14.75 10.7107L9.26347 13.8784C8.64467 14.2356 7.88228 14.2356 7.26347 13.8784L3.5625 11.7416C2.70833 11.2978 1 9.93199 1 8.01949M1 8.01949C1 6.6448 1.84765 5.98698 2.62903 5.71701C3.15426 5.53555 3.71577 5.70568 4.19701 5.98353L7.26347 7.75395C7.88228 8.11122 8.64467 8.11122 9.26347 7.75395L10.9095 6.80362M1 8.01949C1 6.4945 2.03973 5.30731 2.5596 4.90434L7.37937 2.12165C7.79013 1.88449 8.26417 1.80476 8.71747 1.88245"
stroke="currentColor"
strokeOpacity="0.25"
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
className={tcls(state === 'intro' && 'animate-[fadeIn_2s_forwards]')}
style={{
animation: {
intro: 'fadeIn 2s forwards',
thinking: '',
working: '',
done: '',
confirm: '',
default: '',
error: '',
}[state],
}}
/>

{/* Logo */}
Expand Down Expand Up @@ -154,45 +182,65 @@ export function AIChatIcon({
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
className={tcls(
(state === 'thinking' || state === 'working') &&
'animate-[pathLoading_2s_infinite_both]',
state === 'intro' && 'animate-[pathEnter_2s_both]',
state === 'done' && 'animate-[pathEnter_1s_forwards_ease]'
)}
style={{
animation: {
intro: 'pathEnter 1.5s both ease-out',
thinking: 'pathLoading 2s infinite both',
working: 'pathLoading 2s infinite both',
done: 'pathEnter 1s forwards ease',
confirm: '',
default: '',
error: '',
}[state],
}}
/>
</g>
</svg>
);
}

export function AISearchIcon({
className = 'size-4',
state = 'default',
}: Pick<AIChatIconProps, 'className' | 'state'>) {
interface AISearchIconProps extends React.SVGProps<SVGSVGElement> {
className?: string;
state?: 'default' | 'intro' | 'thinking' | 'working' | 'done' | 'error' | 'confirm';
}

export function AISearchIcon({ className = 'size-4', state = 'default' }: AISearchIconProps) {
return (
<div
className={tcls(
'relative',
state === 'intro' && 'animate-[fadeIn_1s_both,orbit_1s_cubic-bezier(0.16,1,0.3,1)]',
state === 'thinking' || state === 'working'
? 'animate-[fadeIn_1s_both,orbit_1s_ease-out,orbit_2s_1s_infinite_forwards_linear]'
: ''
)}
style={{
position: 'relative',
animation: {
intro: 'fadeIn 1s both, orbit 1s cubic-bezier(0.16,1,0.3,1)',
thinking:
'fadeIn 1s both, orbit 1s orbit 2s 1s infinite forwards linear ease-out,',
working: '',
done: '',
confirm: '',
default: '',
error: '',
}[state],
}}
>
<Icon icon="search" className={className} />
<Icon
icon="sparkle"
iconStyle={IconStyle.Solid}
className={tcls(
'absolute top-[15.7%] left-[15.6%] size-[50%]',
state === 'thinking' || state === 'working'
? 'animate-[spin_2s_infinite_forwards_cubic-bezier(0.16,1,0.3,1)]'
: '',
state === 'intro'
? 'animate-[spin_2s_.5s_forwards_cubic-bezier(0.16,1,0.3,1)]'
: ''
)}
style={{
position: 'absolute',
top: '15.7%',
left: '15.6%',
width: '50%',
height: '50%',
animation: {
intro: 'spin 2s .5s forwards cubic-bezier(0.16,1,0.3,1)',
thinking: 'spin 2s infinite forwards cubic-bezier(0.16,1,0.3,1)',
working: 'spin 2s infinite forwards cubic-bezier(0.16,1,0.3,1)',
done: '',
confirm: '',
default: '',
error: '',
}[state],
}}
/>
</div>
);
Expand Down