Skip to content

Commit d2725f5

Browse files
committed
Review
1 parent ea61a93 commit d2725f5

File tree

9 files changed

+101
-53
lines changed

9 files changed

+101
-53
lines changed
Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
1+
import type { RouteLayoutParams } from '@/app/utils';
12
import { EmbeddableAssistantPage } from '@/components/Embeddable';
3+
import { getEmbeddableDynamicContext } from '@/lib/embeddable';
4+
import { CustomizationAIMode } from '@gitbook/api';
5+
import { redirect } from 'next/navigation';
6+
7+
type PageProps = {
8+
params: Promise<RouteLayoutParams>;
9+
};
210

311
export const dynamic = 'force-static';
412

5-
export default async function Page() {
6-
return <EmbeddableAssistantPage />;
13+
export default async function Page(props: PageProps) {
14+
const params = await props.params;
15+
const { context } = await getEmbeddableDynamicContext(params);
16+
17+
// If the assistant is not enabled, redirect to the docs
18+
if (context.customization.ai.mode !== CustomizationAIMode.Assistant) {
19+
redirect(`${context.linker.toPathInSite('~gitbook/embed/page/')}`);
20+
}
21+
22+
return (
23+
<EmbeddableAssistantPage
24+
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
25+
siteTitle={context.site.title}
26+
/>
27+
);
728
}

packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/embed/assistant/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,10 @@ export default async function Page(props: PageProps) {
1919
redirect(`${context.linker.toPathInSite('~gitbook/embed/page/')}`);
2020
}
2121

22-
return <EmbeddableAssistantPage />;
22+
return (
23+
<EmbeddableAssistantPage
24+
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
25+
siteTitle={context.site.title}
26+
/>
27+
);
2328
}

packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/embed/demo/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export async function GET(
114114
'What can I ask you?',
115115
'Show me tips and tricks',
116116
],
117+
tabs: ['assistant'],
117118
});
118119
window.GitBook('open');
119120
</script>

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,14 @@ export function AIChat() {
114114
*/
115115
export function AIChatDynamicIcon(props: {
116116
trademark: boolean;
117+
className?: string;
117118
}) {
118-
const { trademark } = props;
119+
const { trademark, className } = props;
119120
const chat = useAIChatState();
120121

121122
return (
122123
<AIChatIcon
123-
className="size-5 text-tint"
124+
className={tcls('size-5 text-tint', className)}
124125
trademark={trademark}
125126
state={
126127
chat.error

packages/gitbook/src/components/Embeddable/EmbeddableAIChat.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useAI, useAIChatController, useAIChatState } from '@/components/AI';
44
import {
55
AIChatBody,
66
AIChatControlButton,
7+
AIChatDynamicIcon,
78
AIChatSubtitle,
89
getAIChatName,
910
} from '@/components/AIChat';
@@ -27,10 +28,16 @@ import {
2728
useEmbeddableConfiguration,
2829
} from './EmbeddableIframeAPI';
2930

31+
type EmbeddableAIChatProps = {
32+
baseURL: string;
33+
siteTitle: string;
34+
};
35+
3036
/**
3137
* Embeddable AI chat window in an iframe.
3238
*/
33-
export function EmbeddableAIChat() {
39+
export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
40+
const { baseURL, siteTitle } = props;
3441
const chat = useAIChatState();
3542
const { config } = useAI();
3643
const chatController = useAIChatController();
@@ -55,14 +62,27 @@ export function EmbeddableAIChat() {
5562
);
5663
}, [trackEvent]);
5764

65+
const tabsRef = React.useRef<HTMLDivElement>(null);
66+
5867
return (
5968
<EmbeddableFrame>
6069
<EmbeddableFrameSidebar>
61-
<EmbeddableIframeTabs active="assistant" />
70+
<EmbeddableIframeTabs
71+
ref={tabsRef}
72+
active="assistant"
73+
baseURL={baseURL}
74+
siteTitle={siteTitle}
75+
/>
6276
<EmbeddableIframeButtons />
6377
</EmbeddableFrameSidebar>
6478
<EmbeddableFrameMain>
6579
<EmbeddableFrameHeader>
80+
{!tabsRef.current ? (
81+
<AIChatDynamicIcon
82+
className="animate-blur-in-slow"
83+
trademark={config.trademark}
84+
/>
85+
) : null}
6686
<EmbeddableFrameHeaderMain>
6787
<EmbeddableFrameTitle>
6888
{getAIChatName(language, config.trademark)}
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { EmbeddableAIChat } from './EmbeddableAIChat';
22

3+
type EmbeddableAssistantPageProps = {
4+
baseURL: string;
5+
siteTitle: string;
6+
};
7+
38
/**
49
* Reusable page component for the embed assistant page.
510
*/
6-
export async function EmbeddableAssistantPage() {
7-
return <EmbeddableAIChat />;
11+
export async function EmbeddableAssistantPage(props: EmbeddableAssistantPageProps) {
12+
return <EmbeddableAIChat baseURL={props.baseURL} siteTitle={props.siteTitle} />;
813
}

packages/gitbook/src/components/Embeddable/EmbeddableDocsPage.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ export async function EmbeddableDocsPage(props: EmbeddableDocsPageProps) {
4040
return (
4141
<EmbeddableFrame className="site-background">
4242
<EmbeddableFrameSidebar>
43-
<EmbeddableIframeTabs active="docs" />
43+
<EmbeddableIframeTabs
44+
active="docs"
45+
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
46+
siteTitle={context.site.title}
47+
/>
4448
<EmbeddableIframeButtons />
4549
</EmbeddableFrameSidebar>
4650
<EmbeddableFrameMain>

packages/gitbook/src/components/Embeddable/EmbeddableIframeAPI.tsx

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,25 @@ import { createStore, useStore } from 'zustand';
1111
import { integrationsAssistantTools } from '../Integrations';
1212
import { Button } from '../primitives';
1313

14-
const embeddableConfiguration = createStore<
15-
GitBookEmbeddableConfiguration & { baseURL: string; siteTitle: string }
16-
>(() => ({
14+
const embeddableConfiguration = createStore<GitBookEmbeddableConfiguration>(() => ({
1715
tabs: [],
1816
actions: [],
1917
greeting: { title: '', subtitle: '' },
2018
suggestions: [],
2119
tools: [],
22-
baseURL: '',
23-
siteTitle: '',
2420
}));
2521

2622
/**
2723
* Expose the API to communicate with the parent window.
2824
*/
2925
export function EmbeddableIframeAPI(props: {
3026
baseURL: string;
31-
siteTitle: string;
3227
}) {
33-
const { baseURL, siteTitle } = props;
28+
const { baseURL } = props;
3429

3530
const router = useRouter();
3631
const chatController = useAIChatController();
3732

38-
React.useEffect(() => {
39-
embeddableConfiguration.setState({ baseURL, siteTitle });
40-
}, [baseURL, siteTitle]);
41-
4233
React.useEffect(() => {
4334
return chatController.on('open', () => {
4435
router.push(`${baseURL}/assistant`);
@@ -99,9 +90,7 @@ export function EmbeddableIframeAPI(props: {
9990
/**
10091
* Hook to get the configuration from the parent window.
10192
*/
102-
export function useEmbeddableConfiguration<
103-
T = GitBookEmbeddableConfiguration & { baseURL: string; siteTitle: string },
104-
>(
93+
export function useEmbeddableConfiguration<T = GitBookEmbeddableConfiguration>(
10594
// @ts-expect-error - This is a workaround to allow the function to be optional.
10695
fn: (state: GitBookEmbeddableConfiguration) => T = (state) => state
10796
) {
@@ -146,9 +135,14 @@ export function EmbeddableIframeButtons() {
146135
);
147136
}
148137

149-
export function EmbeddableIframeTabs(props: { active?: string }) {
150-
const { active = 'assistant' } = props;
151-
const { baseURL, siteTitle, tabs: configuredTabs, actions } = useEmbeddableConfiguration();
138+
export function EmbeddableIframeTabs(props: {
139+
ref?: React.RefObject<HTMLDivElement | null>;
140+
active?: string;
141+
baseURL: string;
142+
siteTitle: string;
143+
}) {
144+
const { ref, active = 'assistant', baseURL, siteTitle } = props;
145+
const { tabs: configuredTabs, actions } = useEmbeddableConfiguration();
152146

153147
const { assistants, config } = useAI();
154148

@@ -194,29 +188,27 @@ export function EmbeddableIframeTabs(props: { active?: string }) {
194188
}
195189
}, [tabs, baseURL, router, active]);
196190

197-
return (
198-
<>
199-
{tabs.length > 1 || actions.length > 0
200-
? tabs.map((tab) => (
201-
<Button
202-
key={tab.key}
203-
data-testid={`embed-tab-${tab.key}`}
204-
label={tab.label}
205-
size="default"
206-
variant="blank"
207-
icon={tab.icon}
208-
active={tab.key === active}
209-
className="not-hydrated:animate-blur-in-slow [&_.button-leading-icon]:size-5"
210-
iconOnly
211-
onClick={tab.onClick}
212-
tooltipProps={{
213-
contentProps: {
214-
side: 'right',
215-
},
216-
}}
217-
/>
218-
))
219-
: null}
220-
</>
221-
);
191+
return tabs.length > 1 || actions.length > 0 ? (
192+
<div className="flex flex-col gap-2" ref={ref}>
193+
{tabs.map((tab) => (
194+
<Button
195+
key={tab.key}
196+
data-testid={`embed-tab-${tab.key}`}
197+
label={tab.label}
198+
size="default"
199+
variant="blank"
200+
icon={tab.icon}
201+
active={tab.key === active}
202+
className="not-hydrated:animate-blur-in-slow [&_.button-leading-icon]:size-5"
203+
iconOnly
204+
onClick={tab.onClick}
205+
tooltipProps={{
206+
contentProps: {
207+
side: 'right',
208+
},
209+
}}
210+
/>
211+
))}
212+
</div>
213+
) : null;
222214
}

packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export async function EmbeddableRootLayout({
6565
</div>
6666
<EmbeddableIframeAPI
6767
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
68-
siteTitle={context.site.title}
6968
/>
7069
</SpaceLayoutServerContext>
7170
</AIContextProvider>

0 commit comments

Comments
 (0)