Skip to content

Commit 52ab368

Browse files
authored
Reposition AI Actions dropdown (#3460)
1 parent 9cc5a78 commit 52ab368

File tree

8 files changed

+86
-55
lines changed

8 files changed

+86
-55
lines changed

.changeset/khaki-insects-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Reposition AI Actions dropdown

packages/gitbook/src/components/AIActions/AIActions.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useAIChatState } from '@/components/AI/useAIChat';
55
import { ChatGPTIcon } from '@/components/AIActions/assets/ChatGPTIcon';
66
import { ClaudeIcon } from '@/components/AIActions/assets/ClaudeIcon';
77
import { MarkdownIcon } from '@/components/AIActions/assets/MarkdownIcon';
8+
import { getAIChatName } from '@/components/AIChat';
89
import AIChatIcon from '@/components/AIChat/AIChatIcon';
910
import { Button } from '@/components/primitives/Button';
1011
import { DropdownMenuItem } from '@/components/primitives/DropdownMenu';
@@ -21,23 +22,21 @@ type AIActionType = 'button' | 'dropdown-menu-item';
2122
/**
2223
* Opens our AI Docs Assistant.
2324
*/
24-
export function OpenDocsAssistant(props: { type: AIActionType }) {
25-
const { type } = props;
25+
export function OpenDocsAssistant(props: { type: AIActionType; trademark: boolean }) {
26+
const { type, trademark } = props;
2627
const chatController = useAIChatController();
2728
const chat = useAIChatState();
2829
const language = useLanguage();
2930

3031
return (
3132
<AIActionWrapper
3233
type={type}
33-
icon={<AIChatIcon state={chat.loading ? 'thinking' : 'default'} />}
34-
label={tString(language, 'ai_chat_ask', tString(language, 'ai_chat_assistant_name'))}
34+
icon={
35+
<AIChatIcon state={chat.loading ? 'thinking' : 'default'} trademark={trademark} />
36+
}
37+
label={tString(language, 'ai_chat_ask', getAIChatName(trademark))}
3538
shortLabel={tString(language, 'ask')}
36-
description={tString(
37-
language,
38-
'ai_chat_ask_about_page',
39-
tString(language, 'ai_chat_assistant_name')
40-
)}
39+
description={tString(language, 'ai_chat_ask_about_page', getAIChatName(trademark))}
4140
disabled={chat.loading}
4241
onClick={() => {
4342
// Open the chat if it's not already open
@@ -130,6 +129,7 @@ export function CopyMarkdown(props: {
130129
type={type}
131130
icon={copied ? 'check' : 'copy'}
132131
label={copied ? tString(language, 'code_copied') : tString(language, 'copy_page')}
132+
shortLabel={copied ? tString(language, 'code_copied') : tString(language, 'code_copy')}
133133
description={tString(language, 'copy_page_markdown')}
134134
onClick={onClick}
135135
/>
@@ -207,7 +207,7 @@ function AIActionWrapper(props: {
207207
return (
208208
<Button
209209
icon={icon}
210-
size="small"
210+
size="xsmall"
211211
variant="secondary"
212212
label={shortLabel || label}
213213
className="hover:!scale-100 !shadow-none !rounded-r-none border-r-0 bg-tint-base text-sm"

packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from '@/components/AIActions/AIActions';
99
import { Button } from '@/components/primitives/Button';
1010
import { DropdownMenu } from '@/components/primitives/DropdownMenu';
11+
import { Icon } from '@gitbook/icons';
1112
import { useRef } from 'react';
1213

1314
/**
@@ -21,20 +22,26 @@ export function AIActionsDropdown(props: {
2122
*/
2223
withAIChat?: boolean;
2324
pageURL: string;
25+
trademark: boolean;
2426
}) {
2527
const ref = useRef<HTMLDivElement>(null);
2628

2729
return (
28-
<div ref={ref} className="hidden h-fit items-stretch justify-start sm:flex">
30+
<div ref={ref} className="flex h-fit items-stretch justify-start">
2931
<DefaultAction {...props} />
3032
<DropdownMenu
3133
align="end"
3234
className="!min-w-60 max-w-max"
3335
button={
3436
<Button
35-
icon="chevron-down"
37+
icon={
38+
<Icon
39+
icon="chevron-down"
40+
className="size-3 transition-transform group-data-[state=open]/button:rotate-180"
41+
/>
42+
}
3643
iconOnly
37-
size="small"
44+
size="xsmall"
3845
variant="secondary"
3946
className="hover:!scale-100 !shadow-none !rounded-l-none bg-tint-base text-sm"
4047
/>
@@ -54,12 +61,15 @@ function AIActionsDropdownMenuContent(props: {
5461
markdownPageUrl: string;
5562
withAIChat?: boolean;
5663
pageURL: string;
64+
trademark: boolean;
5765
}) {
58-
const { markdown, markdownPageUrl, withAIChat, pageURL } = props;
66+
const { markdown, markdownPageUrl, withAIChat, pageURL, trademark } = props;
5967

6068
return (
6169
<>
62-
{withAIChat ? <OpenDocsAssistant type="dropdown-menu-item" /> : null}
70+
{withAIChat ? (
71+
<OpenDocsAssistant trademark={trademark} type="dropdown-menu-item" />
72+
) : null}
6373
{markdown ? (
6474
<>
6575
<CopyMarkdown
@@ -84,11 +94,12 @@ function DefaultAction(props: {
8494
withAIChat?: boolean;
8595
pageURL: string;
8696
markdownPageUrl: string;
97+
trademark: boolean;
8798
}) {
88-
const { markdown, withAIChat, pageURL, markdownPageUrl } = props;
99+
const { markdown, withAIChat, pageURL, markdownPageUrl, trademark } = props;
89100

90101
if (withAIChat) {
91-
return <OpenDocsAssistant type="button" />;
102+
return <OpenDocsAssistant trademark={trademark} type="button" />;
92103
}
93104

94105
if (markdown) {

packages/gitbook/src/components/AIChat/AIChat.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,7 @@ export function AIChatWindow(props: {
129129
}
130130
/>
131131
<div className="flex flex-col">
132-
<div className="font-bold">
133-
{trademark
134-
? tString(language, 'ai_chat_assistant_name')
135-
: tString(language, 'ai_chat_assistant_name_unbranded')}
136-
</div>
132+
<div className="font-bold">{getAIChatName(trademark)}</div>
137133
<div
138134
className={`text-tint text-xs leading-none transition-all duration-500 ${
139135
chat.loading ? 'h-3 opacity-11' : 'h-0 opacity-0'
@@ -265,3 +261,11 @@ function AIChatError(props: { chatController: AIChatController }) {
265261
</div>
266262
);
267263
}
264+
265+
export function getAIChatName(trademark: boolean) {
266+
const language = useLanguage();
267+
268+
return trademark
269+
? tString(language, 'ai_chat_assistant_name')
270+
: tString(language, 'ai_chat_assistant_name_unbranded');
271+
}

packages/gitbook/src/components/PageBody/PageHeader.tsx

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ export async function PageHeader(props: {
3838
'page-api-block:max-w-full'
3939
)}
4040
>
41+
{page.layout.tableOfContents ? (
42+
<div
43+
className={tcls(
44+
'float-right mb-2 ml-4',
45+
ancestors.length > 0 ? '-mt-2' : 'xs:mt-2'
46+
)}
47+
>
48+
<AIActionsDropdown
49+
markdown={markdownResult.data}
50+
markdownPageUrl={context.linker.toPathInSpace(page.path)}
51+
pageURL={context.linker.toAbsoluteURL(
52+
context.linker.toPathForPage({
53+
pages: context.revision.pages,
54+
page,
55+
})
56+
)}
57+
withAIChat={withAIChat}
58+
trademark={context.customization.trademark.enabled}
59+
/>
60+
</div>
61+
) : null}
4162
{ancestors.length > 0 && (
4263
<nav>
4364
<ol className={tcls('flex', 'flex-wrap', 'items-center', 'gap-2', 'text-tint')}>
@@ -84,39 +105,26 @@ export async function PageHeader(props: {
84105
</ol>
85106
</nav>
86107
)}
87-
<div className="flex items-start justify-between gap-4">
88-
{page.layout.title ? (
89-
<h1
90-
className={tcls(
91-
'text-4xl',
92-
'font-bold',
93-
'flex',
94-
'items-center',
95-
'gap-4',
96-
'w-fit',
97-
'text-pretty'
98-
)}
99-
>
100-
<PageIcon page={page} style={['text-tint-subtle ', 'shrink-0']} />
101-
{page.title}
102-
</h1>
103-
) : null}
104-
{page.layout.tableOfContents ? (
105-
<AIActionsDropdown
106-
markdown={markdownResult.data}
107-
markdownPageUrl={context.linker.toPathInSpace(page.path)}
108-
pageURL={context.linker.toAbsoluteURL(
109-
context.linker.toPathForPage({
110-
pages: context.revision.pages,
111-
page,
112-
})
113-
)}
114-
withAIChat={withAIChat}
115-
/>
116-
) : null}
117-
</div>
108+
{page.layout.title ? (
109+
<h1
110+
className={tcls(
111+
'text-4xl',
112+
'font-bold',
113+
'flex',
114+
'items-center',
115+
'gap-4',
116+
'grow',
117+
'text-pretty',
118+
'clear-right',
119+
'xs:clear-none'
120+
)}
121+
>
122+
<PageIcon page={page} style={['text-tint-subtle ', 'shrink-0']} />
123+
{page.title}
124+
</h1>
125+
) : null}
118126
{page.description && page.layout.description ? (
119-
<p className={tcls('text-lg', 'text-tint')}>{page.description}</p>
127+
<p className={tcls('text-lg', 'text-tint', 'clear-both')}>{page.description}</p>
120128
) : null}
121129
</header>
122130
);

packages/gitbook/src/components/primitives/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type ButtonProps = {
1414
variant?: 'primary' | 'secondary' | 'blank';
1515
icon?: IconName | React.ReactNode;
1616
iconOnly?: boolean;
17-
size?: 'default' | 'medium' | 'small';
17+
size?: 'default' | 'medium' | 'small' | 'xsmall';
1818
className?: ClassValue;
1919
label?: string | React.ReactNode;
2020
} & LinkInsightsProps &
@@ -67,6 +67,7 @@ export function Button({
6767
default: ['text-base', 'font-semibold', 'px-5', 'py-2', 'circular-corners:px-6'],
6868
medium: ['text-sm', 'px-3.5', 'py-1.5', 'circular-corners:px-4'],
6969
small: ['text-xs', 'py-2', iconOnly ? 'px-2' : 'px-3'],
70+
xsmall: ['text-xs', 'py-1', iconOnly ? 'px-1.5' : 'px-2'],
7071
};
7172

7273
const sizeClasses = sizes[size] || sizes.default;

packages/gitbook/src/components/primitives/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ClassValue } from '@/lib/tailwind';
22

33
export const ButtonStyles = [
44
'button',
5+
'group/button',
56
'inline-flex',
67
'items-center',
78
'gap-2',

packages/gitbook/tailwind.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ const config: Config = {
474474
},
475475
opacity: opacity(),
476476
screens: {
477+
xs: '480px',
477478
sm: '640px',
478479
md: '768px',
479480
lg: '1024px',

0 commit comments

Comments
 (0)