mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 63971b9803 | |||
| 64455b55e4 |
@@ -3,7 +3,7 @@ import type { IconName } from '@gitbook/icons';
|
||||
|
||||
export type GitBookIntegrationEvent = 'load' | 'unload';
|
||||
|
||||
export type GitBookIntegrationEventCallback = (...args: any[]) => void;
|
||||
export type GitBookIntegrationEventCallback = (...args: unknown[]) => void;
|
||||
|
||||
export type GitBookIntegrationTool = AIToolDefinition & {
|
||||
/**
|
||||
@@ -70,8 +70,10 @@ export type GitBookGlobal = {
|
||||
|
||||
/**
|
||||
* Register a custom assistant to be available on the site.
|
||||
* Returns a registration object that lets the integration signal readiness
|
||||
* and dispose the assistant when no longer needed.
|
||||
*/
|
||||
registerAssistant: (assistant: GitBookAssistant) => () => void;
|
||||
registerAssistant: (assistant: GitBookAssistant) => GitBookAssistantRegistration;
|
||||
};
|
||||
|
||||
declare global {
|
||||
@@ -82,3 +84,16 @@ declare global {
|
||||
GitBook?: GitBookGlobal;
|
||||
}
|
||||
}
|
||||
|
||||
export type GitBookAssistantRegistration = ((this: void) => void) & {
|
||||
/**
|
||||
* Call this when the integration has finished initializing and is ready to
|
||||
* receive `open` calls.
|
||||
*/
|
||||
ready: () => void;
|
||||
|
||||
/**
|
||||
* Unregister the assistant.
|
||||
*/
|
||||
dispose: () => void;
|
||||
};
|
||||
|
||||
@@ -38,6 +38,12 @@ export type Assistant = Omit<GitBookAssistant, 'icon'> & {
|
||||
* Icon of the assistant displayed in the UI.
|
||||
*/
|
||||
icon: ReactNode;
|
||||
|
||||
/**
|
||||
* Assistant readiness. Built-in assistants are always ready.
|
||||
* Integrations set this to true via the registration's ready() callback.
|
||||
*/
|
||||
ready?: boolean;
|
||||
};
|
||||
|
||||
const AIContext = React.createContext<AIConfig | null>(null);
|
||||
@@ -92,6 +98,7 @@ export function useAI(): AIContext {
|
||||
},
|
||||
ui: true,
|
||||
mode: 'sidebar',
|
||||
ready: true,
|
||||
});
|
||||
} else if (config.aiMode === CustomizationAIMode.Search) {
|
||||
assistants.push({
|
||||
@@ -116,6 +123,7 @@ export function useAI(): AIContext {
|
||||
},
|
||||
ui: false,
|
||||
mode: 'search',
|
||||
ready: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,17 +47,39 @@ if (typeof window !== 'undefined') {
|
||||
},
|
||||
registerAssistant: (assistant) => {
|
||||
const id = window.crypto.randomUUID();
|
||||
// Add assistant with ready=false until the integration signals readiness
|
||||
integrationAssistants.setState(
|
||||
(state) => [
|
||||
...state,
|
||||
{ ...assistant, id, ui: assistant.ui ?? true, mode: 'overlay' },
|
||||
{ ...assistant, id, ui: assistant.ui ?? true, mode: 'overlay', ready: false },
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
return () => {
|
||||
integrationAssistants.setState((state) => state.filter((a) => a.id !== id), true);
|
||||
const ready = () => {
|
||||
integrationAssistants.setState(
|
||||
(state) =>
|
||||
state.map((assistant) =>
|
||||
assistant.id === id ? { ...assistant, ready: true } : assistant
|
||||
),
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
const dispose = () => {
|
||||
integrationAssistants.setState(
|
||||
(state) => state.filter((assistant) => assistant.id !== id),
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
const registration = (() => dispose()) as ((this: void) => void) & {
|
||||
ready: () => void;
|
||||
dispose: () => void;
|
||||
};
|
||||
registration.ready = ready;
|
||||
registration.dispose = dispose;
|
||||
return registration;
|
||||
},
|
||||
};
|
||||
window.GitBook = gitbookGlobal;
|
||||
@@ -83,6 +105,6 @@ export function LoadIntegrations() {
|
||||
/**
|
||||
* Client function to dispatch a GitBook event.
|
||||
*/
|
||||
function dispatchGitBookIntegrationEvent(type: GitBookIntegrationEvent, ...args: any[]) {
|
||||
function dispatchGitBookIntegrationEvent(type: GitBookIntegrationEvent, ...args: unknown[]) {
|
||||
events.get(type)?.forEach((handler) => handler(...args));
|
||||
}
|
||||
|
||||
@@ -52,11 +52,15 @@ export function SearchContainer(props: SearchContainerProps) {
|
||||
if (assistants.length === 0) return;
|
||||
if (state?.ask === undefined || state?.ask === null) return;
|
||||
|
||||
// Wait for the first assistant to be ready (built-ins are always ready)
|
||||
const first = assistants[0];
|
||||
if (first?.ready === false) return;
|
||||
|
||||
initialRef.current = true;
|
||||
|
||||
// For simplicity we're only triggering the first assistant
|
||||
assistants[0].open(state?.ask ?? undefined);
|
||||
}, [state?.ask, assistants.length, assistants[0]?.open]);
|
||||
first.open(state?.ask ?? undefined);
|
||||
}, [state?.ask, assistants.length, assistants, assistants[0]?.ready]);
|
||||
|
||||
const onClose = React.useCallback(
|
||||
async (to?: string) => {
|
||||
|
||||
Reference in New Issue
Block a user