mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-17 16:15:22 +00:00
Docs Embed: Make trademark optional (#4079)
Co-authored-by: Greg Bergé <berge.greg@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@gitbook/embed": minor
|
||||||
|
"gitbook": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Docs Embed: Make trademark optional
|
||||||
@@ -269,6 +269,19 @@ Notes:
|
|||||||
- The close button is rendered in the same sidebar area as tabs/actions.
|
- The close button is rendered in the same sidebar area as tabs/actions.
|
||||||
- If your UI hides that sidebar or doesn't render it, the button will not be visible.
|
- If your UI hides that sidebar or doesn't render it, the button will not be visible.
|
||||||
|
|
||||||
|
### `trademark`
|
||||||
|
|
||||||
|
Available in: Standalone script, NPM package, React components
|
||||||
|
|
||||||
|
Display GitBook branding in the embed. Defaults to true.
|
||||||
|
|
||||||
|
- **Type**: `boolean`
|
||||||
|
- **Default**: `true`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
trademark: true
|
||||||
|
```
|
||||||
|
|
||||||
### `actions`
|
### `actions`
|
||||||
|
|
||||||
Available in: Standalone script, NPM package, React components
|
Available in: Standalone script, NPM package, React components
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export function createGitBookFrame(iframe: HTMLIFrameElement): GitBookFrameClien
|
|||||||
greeting: { title: '', subtitle: '' },
|
greeting: { title: '', subtitle: '' },
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
tools: [],
|
tools: [],
|
||||||
|
trademark: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ export type GitBookEmbeddableConfiguration = {
|
|||||||
/** Tools to be provided to the assistant. */
|
/** Tools to be provided to the assistant. */
|
||||||
tools: GitBookToolDefinition[];
|
tools: GitBookToolDefinition[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display GitBook branding in the embed.
|
||||||
|
*/
|
||||||
|
trademark?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a close button inside the assistant.
|
* Display a close button inside the assistant.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function GitBookFrame(props: GitBookFrameProps) {
|
|||||||
suggestions = [],
|
suggestions = [],
|
||||||
tools = [],
|
tools = [],
|
||||||
tabs = ['assistant', 'docs'],
|
tabs = ['assistant', 'docs'],
|
||||||
|
trademark = true,
|
||||||
closeButton = false,
|
closeButton = false,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -48,8 +49,9 @@ export function GitBookFrame(props: GitBookFrameProps) {
|
|||||||
suggestions,
|
suggestions,
|
||||||
tools,
|
tools,
|
||||||
closeButton,
|
closeButton,
|
||||||
|
trademark,
|
||||||
});
|
});
|
||||||
}, [gitbookFrame, actions, greeting, suggestions, tools, tabs, closeButton]);
|
}, [gitbookFrame, actions, greeting, suggestions, tools, tabs, closeButton, trademark]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ let frameConfiguration: GitBookEmbeddableConfiguration & StandaloneConfiguration
|
|||||||
suggestions: [],
|
suggestions: [],
|
||||||
tools: [],
|
tools: [],
|
||||||
tabs: ['assistant', 'docs'],
|
tabs: ['assistant', 'docs'],
|
||||||
|
trademark: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const widgetButton = document.createElement('button');
|
const widgetButton = document.createElement('button');
|
||||||
@@ -156,6 +157,19 @@ const GitBook = (...args: StandaloneCalls) => {
|
|||||||
break;
|
break;
|
||||||
case 'configure': {
|
case 'configure': {
|
||||||
const settings = args[1];
|
const settings = args[1];
|
||||||
|
|
||||||
|
// If trademark is disabled, change the (branded) icon to the sparkle icon
|
||||||
|
if (
|
||||||
|
settings.trademark === false &&
|
||||||
|
!settings.button?.icon &&
|
||||||
|
frameConfiguration.button.icon === 'assistant'
|
||||||
|
) {
|
||||||
|
settings.button = {
|
||||||
|
label: frameConfiguration.button.label,
|
||||||
|
icon: 'sparkle',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
frameConfiguration = {
|
frameConfiguration = {
|
||||||
...frameConfiguration,
|
...frameConfiguration,
|
||||||
...settings,
|
...settings,
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export function AIChat() {
|
|||||||
chatController={chatController}
|
chatController={chatController}
|
||||||
chat={chat}
|
chat={chat}
|
||||||
suggestions={config.suggestions}
|
suggestions={config.suggestions}
|
||||||
|
trademark={config.trademark}
|
||||||
/>
|
/>
|
||||||
</EmbeddableFrameBody>
|
</EmbeddableFrameBody>
|
||||||
</EmbeddableFrameMain>
|
</EmbeddableFrameMain>
|
||||||
@@ -199,13 +200,13 @@ export function AIChatBody(props: {
|
|||||||
chat: AIChatState;
|
chat: AIChatState;
|
||||||
welcomeMessage?: string;
|
welcomeMessage?: string;
|
||||||
suggestions?: string[];
|
suggestions?: string[];
|
||||||
|
trademark?: boolean;
|
||||||
greeting?: {
|
greeting?: {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
const { chatController, chat, suggestions, greeting } = props;
|
const { chatController, chat, suggestions, greeting, trademark } = props;
|
||||||
const { trademark } = useAI().config;
|
|
||||||
|
|
||||||
const language = useLanguage();
|
const language = useLanguage();
|
||||||
const now = useNow(60 * 60 * 1000); // Refresh every hour for greeting
|
const now = useNow(60 * 60 * 1000); // Refresh every hour for greeting
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ type EmbeddableAIChatProps = {
|
|||||||
export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
|
export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
|
||||||
const { baseURL, siteTitle } = props;
|
const { baseURL, siteTitle } = props;
|
||||||
const chat = useAIChatState();
|
const chat = useAIChatState();
|
||||||
const { config } = useAI();
|
const { config: siteConfig } = useAI();
|
||||||
const chatController = useAIChatController();
|
const chatController = useAIChatController();
|
||||||
const configuration = useEmbeddableConfiguration();
|
const embedConfig = useEmbeddableConfiguration();
|
||||||
const language = useLanguage();
|
const language = useLanguage();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -65,7 +65,8 @@ export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
|
|||||||
}, [trackEvent]);
|
}, [trackEvent]);
|
||||||
|
|
||||||
const tabsRef = React.useRef<HTMLDivElement>(null);
|
const tabsRef = React.useRef<HTMLDivElement>(null);
|
||||||
const hasDocsTab = configuration.tabs.includes('docs');
|
const hasDocsTab = embedConfig.tabs.includes('docs');
|
||||||
|
const trademark = siteConfig.trademark && (embedConfig.trademark ?? true);
|
||||||
const currentLinkContext = use(LinkContext);
|
const currentLinkContext = use(LinkContext);
|
||||||
const linkContext: LinkContextType = useMemo(
|
const linkContext: LinkContextType = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -95,14 +96,11 @@ export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
|
|||||||
<EmbeddableFrameMain data-testid="ai-chat">
|
<EmbeddableFrameMain data-testid="ai-chat">
|
||||||
<EmbeddableFrameHeader>
|
<EmbeddableFrameHeader>
|
||||||
{!tabsRef.current ? (
|
{!tabsRef.current ? (
|
||||||
<AIChatDynamicIcon
|
<AIChatDynamicIcon className="animate-blur-in-slow" trademark={trademark} />
|
||||||
className="animate-blur-in-slow"
|
|
||||||
trademark={config.trademark}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
<EmbeddableFrameHeaderMain>
|
<EmbeddableFrameHeaderMain>
|
||||||
<EmbeddableFrameTitle>
|
<EmbeddableFrameTitle>
|
||||||
{getAIChatName(language, config.trademark)}
|
{getAIChatName(language, trademark)}
|
||||||
</EmbeddableFrameTitle>
|
</EmbeddableFrameTitle>
|
||||||
<AIChatSubtitle chat={chat} />
|
<AIChatSubtitle chat={chat} />
|
||||||
</EmbeddableFrameHeaderMain>
|
</EmbeddableFrameHeaderMain>
|
||||||
@@ -115,8 +113,9 @@ export function EmbeddableAIChat(props: EmbeddableAIChatProps) {
|
|||||||
<AIChatBody
|
<AIChatBody
|
||||||
chatController={chatController}
|
chatController={chatController}
|
||||||
chat={chat}
|
chat={chat}
|
||||||
suggestions={configuration.suggestions}
|
suggestions={embedConfig.suggestions}
|
||||||
greeting={configuration.greeting}
|
greeting={embedConfig.greeting}
|
||||||
|
trademark={trademark}
|
||||||
/>
|
/>
|
||||||
</LinkContext>
|
</LinkContext>
|
||||||
</EmbeddableFrameBody>
|
</EmbeddableFrameBody>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const embeddableConfiguration = createStore<GitBookEmbeddableConfiguration>(() =
|
|||||||
greeting: { title: '', subtitle: '' },
|
greeting: { title: '', subtitle: '' },
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
tools: [],
|
tools: [],
|
||||||
|
trademark: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: expected
|
// biome-ignore lint/suspicious/noExplicitAny: expected
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { SpaceLayoutServerContext } from '../SpaceLayout';
|
|||||||
import { Trademark } from '../TableOfContents/Trademark';
|
import { Trademark } from '../TableOfContents/Trademark';
|
||||||
import { NavigationLoader } from '../primitives/NavigationLoader';
|
import { NavigationLoader } from '../primitives/NavigationLoader';
|
||||||
import { EmbeddableIframeAPI } from './EmbeddableIframeAPI';
|
import { EmbeddableIframeAPI } from './EmbeddableIframeAPI';
|
||||||
|
import { IfEmbeddableTrademark } from './EmbeddableTrademark';
|
||||||
|
|
||||||
type EmbeddableRootLayoutProps = {
|
type EmbeddableRootLayoutProps = {
|
||||||
context: GitBookSiteContext;
|
context: GitBookSiteContext;
|
||||||
@@ -58,11 +59,13 @@ export async function EmbeddableRootLayout({
|
|||||||
<div className="fixed inset-0 flex flex-col">
|
<div className="fixed inset-0 flex flex-col">
|
||||||
{children}
|
{children}
|
||||||
{context.customization.trademark.enabled ? (
|
{context.customization.trademark.enabled ? (
|
||||||
<Trademark
|
<IfEmbeddableTrademark>
|
||||||
className="rounded-none! border-x-0 border-t border-b-0 bg-tint-solid/1 depth-flat:bg-tint-solid/1 px-4 py-2.5 text-tint/8"
|
<Trademark
|
||||||
context={context}
|
className="rounded-none! border-x-0 border-t border-b-0 bg-tint-solid/1 depth-flat:bg-tint-solid/1 px-4 py-2.5 text-tint/8"
|
||||||
placement={SiteInsightsTrademarkPlacement.Embed}
|
context={context}
|
||||||
/>
|
placement={SiteInsightsTrademarkPlacement.Embed}
|
||||||
|
/>
|
||||||
|
</IfEmbeddableTrademark>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<EmbeddableIframeAPI
|
<EmbeddableIframeAPI
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import type React from 'react';
|
||||||
|
import { useEmbeddableConfiguration } from './EmbeddableIframeAPI';
|
||||||
|
|
||||||
|
export function IfEmbeddableTrademark(props: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const { children } = props;
|
||||||
|
const embedTrademarkEnabled = useEmbeddableConfiguration((state) => state.trademark ?? true);
|
||||||
|
|
||||||
|
return embedTrademarkEnabled ? children : null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user