diff --git a/.changeset/tough-pots-help.md b/.changeset/tough-pots-help.md new file mode 100644 index 000000000..e811de0a3 --- /dev/null +++ b/.changeset/tough-pots-help.md @@ -0,0 +1,5 @@ +--- +'@gitbook/integration-gurubase': major +--- + +Add Gurubase (https://gurubase.io/) integration to Gitbook diff --git a/bun.lock b/bun.lock index 714b7623b..efdbd8b27 100644 --- a/bun.lock +++ b/bun.lock @@ -188,6 +188,18 @@ "@gitbook/tsconfig": "packages/tsconfig", }, }, + "integrations/gurubase": { + "name": "@gitbook/integration-gurubase", + "version": "0.1.0", + "dependencies": { + "@gitbook/api": "*", + "@gitbook/runtime": "*", + }, + "devDependencies": { + "@gitbook/cli": "workspace:*", + "@gitbook/tsconfig": "workspace:*", + }, + }, "integrations/heap": { "name": "@gitbook/integration-heap", "version": "0.5.2", @@ -863,6 +875,8 @@ "@gitbook/integration-googleanalytics": ["@gitbook/integration-googleanalytics@workspace:integrations/googleanalytics"], + "@gitbook/integration-gurubase": ["@gitbook/integration-gurubase@workspace:integrations/gurubase"], + "@gitbook/integration-heap": ["@gitbook/integration-heap@workspace:integrations/heap"], "@gitbook/integration-helpscout": ["@gitbook/integration-helpscout@workspace:integrations/helpscout"], diff --git a/integrations/gurubase/CHANGELOG.md b/integrations/gurubase/CHANGELOG.md new file mode 100644 index 000000000..874cc6e85 --- /dev/null +++ b/integrations/gurubase/CHANGELOG.md @@ -0,0 +1,5 @@ +# @gitbook/integration-gurubase + +## 0.1.0 + +Initial release of the Gurubase integration. diff --git a/integrations/gurubase/README.md b/integrations/gurubase/README.md new file mode 100644 index 000000000..ace40eae5 --- /dev/null +++ b/integrations/gurubase/README.md @@ -0,0 +1,14 @@ +# Gurubase + +```bash +curl -fsSL https://bun.sh/install | bash + +cd packages/api +bun run build + +cd ../../integrations/gurubase +bun install +gitbook publish +cd ../../ +bun changeset +``` diff --git a/integrations/gurubase/assets/gurubase-preview.png b/integrations/gurubase/assets/gurubase-preview.png new file mode 100644 index 000000000..5f198ba81 Binary files /dev/null and b/integrations/gurubase/assets/gurubase-preview.png differ diff --git a/integrations/gurubase/assets/icon.png b/integrations/gurubase/assets/icon.png new file mode 100644 index 000000000..bc6b6a914 Binary files /dev/null and b/integrations/gurubase/assets/icon.png differ diff --git a/integrations/gurubase/gitbook-manifest.yaml b/integrations/gurubase/gitbook-manifest.yaml new file mode 100644 index 000000000..0b7a96c2f --- /dev/null +++ b/integrations/gurubase/gitbook-manifest.yaml @@ -0,0 +1,107 @@ +name: gurubase +title: Gurubase +icon: ./assets/icon.png +previewImages: + - ./assets/gurubase-preview.png +description: Add the Gurubase widget to your published GitBook content. +externalLinks: + - label: Website + url: https://gurubase.io/ +visibility: public +script: ./src/index.ts +# The following scope(s) are available only to GitBook Staff +# See https://developer.gitbook.com/integrations/configurations#scopes +scopes: + - site:script:inject +organization: hY1Io78AJIno02pEZkOX +contentSecurityPolicy: + font-src: | + *.gurubase.io; + script-src: | + *.gurubase.io; + style-src: | + 'self' + 'unsafe-inline' + fonts.googleapis.com + *.gurubase.io + unpkg.com + cdnjs.cloudflare.com; + frame-src: | + *.gurubase.io; + child-src: | + *.gurubase.io; + img-src: | + *.gurubase.io; + connect-src: | + *.gurubase.io; + media-src: | + *.gurubase.io; +summary: | + # Overview + Add an AI-powered chat widget to your GitBook documentation using Gurubase. This integration enables real-time AI assistance for your readers directly within your documentation pages. + + ## Prerequisites + - You need to have a Guru created on Gurubase.io + - You need a Widget ID from your Guru's settings page + 1. Go to "My Gurus" page + 2. Select your Guru + 3. Click "Integrations" then "Web Widget" + 4. Create a new widget and copy the Widget ID + + ## Features + - Seamless integration with GitBook pages + - Customizable widget appearance + - Light/Dark mode support + - Custom positioning + - Custom branding options + + # Configuration + 1. Install the integration on your GitBook site + 2. Add your Widget ID from Gurubase dashboard + 3. Customize the widget appearance (optional) + +categories: + - support +configurations: + site: + properties: + widgetId: + type: string + title: Gurubase Widget ID + description: "Available in your Guru's settings page under Integrations > Web Widget" + text: + type: string + title: Text (Optional) + description: "Text to display in the widget button. (default: 'Ask AI')" + bottomMargin: + type: string + title: Bottom Margin (Optional) + description: 'Bottom margin of the widget. Examples: 20px, 1rem. (default: 20px)' + rightMargin: + type: string + title: Right Margin (Optional) + description: 'Right margin of the widget. Examples: 20px, 1rem. (default: 20px)' + lightMode: + type: boolean + title: Light Mode + description: Whether to use light mode + bgColor: + type: string + title: Main Color (Optional) + description: "Main color of the widget. Examples: #000000, #ffffff. (default: Guru's main color)" + iconUrl: + type: string + title: Icon URL (Optional) + description: "URL of the icon to display in the widget. (default: Guru's avatar)" + name: + type: string + title: Name (Optional) + description: "Name of the widget. (default: Guru's name)" + baseUrl: + type: string + title: Base Backend URL (Optional) + description: 'Base URL for Gurubase backend. (default: https://api.gurubase.io)' + + required: + - widgetId +target: site diff --git a/integrations/gurubase/package.json b/integrations/gurubase/package.json new file mode 100644 index 000000000..b11e0f14c --- /dev/null +++ b/integrations/gurubase/package.json @@ -0,0 +1,19 @@ +{ + "name": "@gitbook/integration-gurubase", + "version": "0.1.0", + "private": true, + "dependencies": { + "@gitbook/api": "*", + "@gitbook/runtime": "*" + }, + "devDependencies": { + "@gitbook/cli": "workspace:*", + "@gitbook/tsconfig": "workspace:*" + }, + "scripts": { + "typecheck": "tsc --noEmit", + "publish-integrations-staging": "gitbook publish .", + "check": "gitbook check", + "publish-integrations": "gitbook publish ." + } +} diff --git a/integrations/gurubase/src/index.ts b/integrations/gurubase/src/index.ts new file mode 100644 index 000000000..5a130d4c4 --- /dev/null +++ b/integrations/gurubase/src/index.ts @@ -0,0 +1,69 @@ +import { + createIntegration, + FetchPublishScriptEventCallback, + RuntimeContext, + RuntimeEnvironment, +} from '@gitbook/runtime'; + +import script from './script.raw.js'; + +type GurubaseRuntimeContext = RuntimeContext< + RuntimeEnvironment< + {}, + { + widgetId?: string; + text?: string; + bottomMargin?: string; + rightMargin?: string; + lightMode?: boolean; + bgColor?: string; + iconUrl?: string; + name?: string; + baseUrl?: string; + } + > +>; + +export const handleFetchEvent: FetchPublishScriptEventCallback = async ( + event, + { environment }: GurubaseRuntimeContext, +) => { + const config = environment.siteInstallation?.configuration || {}; + const widgetId = config.widgetId; + + if (!widgetId) { + throw new Error( + `The Gurubase Widget ID is missing from the configuration (ID: ${ + 'spaceId' in event ? event.spaceId : event.siteId + }).`, + ); + } + + const scriptConfig = { + widgetId, + text: config.text, + margins: JSON.stringify({ + bottom: config.bottomMargin || '20px', + right: config.rightMargin || '20px', + }), + lightMode: config.lightMode, + bgColor: config.bgColor, + iconUrl: config.iconUrl, + name: config.name, + baseUrl: config.baseUrl, + }; + + // Properly escape the config JSON for safe insertion into JavaScript + const escapedConfig = JSON.stringify(scriptConfig).replace(/'/g, "\\'").replace(/"/g, '\\"'); + + return new Response((script as string).replace("''", `'${escapedConfig}'`), { + headers: { + 'Content-Type': 'application/javascript', + 'Cache-Control': 'max-age=604800', + }, + }); +}; + +export default createIntegration({ + fetch_published_script: handleFetchEvent, +}); diff --git a/integrations/gurubase/src/script.raw.js b/integrations/gurubase/src/script.raw.js new file mode 100644 index 000000000..30e077e08 --- /dev/null +++ b/integrations/gurubase/src/script.raw.js @@ -0,0 +1,38 @@ +(function (d, s) { + const config = JSON.parse(''); + const widgetId = config.widgetId; + + window.$gurubase = []; + window.GURUBASE_WIDGET_ID = widgetId; + + d = document; + s = d.createElement('script'); + s.src = 'https://widget.gurubase.io/widget.latest.min.js'; + s.async = 1; + s.setAttribute('data-widget-id', widgetId); + + if (config.text) { + s.setAttribute('data-text', config.text); + } + if (config.margins) { + s.setAttribute('data-margins', config.margins); + } + if (config.lightMode !== undefined) { + s.setAttribute('data-light-mode', config.lightMode.toString()); + } + if (config.bgColor) { + s.setAttribute('data-bg-color', config.bgColor); + } + if (config.iconUrl) { + s.setAttribute('data-icon-url', config.iconUrl); + } + if (config.name) { + s.setAttribute('data-name', config.name); + } + if (config.baseUrl) { + s.setAttribute('data-baseUrl', config.baseUrl); + } + + s.id = 'guru-widget-id'; + d.getElementsByTagName('head')[0].appendChild(s); +})(window, document); diff --git a/integrations/gurubase/tsconfig.json b/integrations/gurubase/tsconfig.json new file mode 100644 index 000000000..1a48f875b --- /dev/null +++ b/integrations/gurubase/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@gitbook/tsconfig/integration.json" +}