mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-14 14:45:17 +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 GitBookIntegrationEvent = 'load' | 'unload';
|
||||||
|
|
||||||
export type GitBookIntegrationEventCallback = (...args: any[]) => void;
|
export type GitBookIntegrationEventCallback = (...args: unknown[]) => void;
|
||||||
|
|
||||||
export type GitBookIntegrationTool = AIToolDefinition & {
|
export type GitBookIntegrationTool = AIToolDefinition & {
|
||||||
/**
|
/**
|
||||||
@@ -70,8 +70,10 @@ export type GitBookGlobal = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a custom assistant to be available on the site.
|
* 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 {
|
declare global {
|
||||||
@@ -82,3 +84,16 @@ declare global {
|
|||||||
GitBook?: GitBookGlobal;
|
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 of the assistant displayed in the UI.
|
||||||
*/
|
*/
|
||||||
icon: ReactNode;
|
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);
|
const AIContext = React.createContext<AIConfig | null>(null);
|
||||||
@@ -92,6 +98,7 @@ export function useAI(): AIContext {
|
|||||||
},
|
},
|
||||||
ui: true,
|
ui: true,
|
||||||
mode: 'sidebar',
|
mode: 'sidebar',
|
||||||
|
ready: true,
|
||||||
});
|
});
|
||||||
} else if (config.aiMode === CustomizationAIMode.Search) {
|
} else if (config.aiMode === CustomizationAIMode.Search) {
|
||||||
assistants.push({
|
assistants.push({
|
||||||
@@ -116,6 +123,7 @@ export function useAI(): AIContext {
|
|||||||
},
|
},
|
||||||
ui: false,
|
ui: false,
|
||||||
mode: 'search',
|
mode: 'search',
|
||||||
|
ready: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,17 +47,39 @@ if (typeof window !== 'undefined') {
|
|||||||
},
|
},
|
||||||
registerAssistant: (assistant) => {
|
registerAssistant: (assistant) => {
|
||||||
const id = window.crypto.randomUUID();
|
const id = window.crypto.randomUUID();
|
||||||
|
// Add assistant with ready=false until the integration signals readiness
|
||||||
integrationAssistants.setState(
|
integrationAssistants.setState(
|
||||||
(state) => [
|
(state) => [
|
||||||
...state,
|
...state,
|
||||||
{ ...assistant, id, ui: assistant.ui ?? true, mode: 'overlay' },
|
{ ...assistant, id, ui: assistant.ui ?? true, mode: 'overlay', ready: false },
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
const ready = () => {
|
||||||
integrationAssistants.setState((state) => state.filter((a) => a.id !== id), true);
|
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;
|
window.GitBook = gitbookGlobal;
|
||||||
@@ -83,6 +105,6 @@ export function LoadIntegrations() {
|
|||||||
/**
|
/**
|
||||||
* Client function to dispatch a GitBook event.
|
* 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));
|
events.get(type)?.forEach((handler) => handler(...args));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,11 +52,15 @@ export function SearchContainer(props: SearchContainerProps) {
|
|||||||
if (assistants.length === 0) return;
|
if (assistants.length === 0) return;
|
||||||
if (state?.ask === undefined || state?.ask === null) 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;
|
initialRef.current = true;
|
||||||
|
|
||||||
// For simplicity we're only triggering the first assistant
|
// For simplicity we're only triggering the first assistant
|
||||||
assistants[0].open(state?.ask ?? undefined);
|
first.open(state?.ask ?? undefined);
|
||||||
}, [state?.ask, assistants.length, assistants[0]?.open]);
|
}, [state?.ask, assistants.length, assistants, assistants[0]?.ready]);
|
||||||
|
|
||||||
const onClose = React.useCallback(
|
const onClose = React.useCallback(
|
||||||
async (to?: string) => {
|
async (to?: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user