Compare commits

...

360 Commits

Author SHA1 Message Date
Claire Chabas e1190dd55c Merge branch 'main' into claire/RND-9736-fix-inline-expressions-wrapping
* main:
  Update @gitbook/api to 0.163.0 (#4039)
  Prepare search for upcoming records (#4037)
2026-02-23 18:16:03 +00:00
Claire Chabas ec36369400 Prevent inline code to break to multiple lines 2026-02-23 18:13:36 +00:00
Samy Pessé 4808f36718 Update @gitbook/api to 0.163.0 (#4039) 2026-02-23 19:00:02 +01:00
Claire Chabas d86d43db81 Prevent inline expressions from wrapping 2026-02-23 17:56:06 +00:00
Samy Pessé a100c5a6c8 Prepare search for upcoming records (#4037) 2026-02-23 18:34:01 +01:00
Zeno Kapitein 4962662fb1 Improve search breadcrumbs (#4034) 2026-02-23 13:40:57 +00:00
Zeno Kapitein f7c251e2d2 Improve Reddit social account URL handling (#4033) 2026-02-23 11:49:26 +01:00
Greg Bergé 40524d4d4a Upgrade Argos & Playwright (#4031) 2026-02-23 10:04:59 +01:00
Nolann B. 1a7ef78fe2 Handle non-array required field in OpenAPI schema (#4030) 2026-02-21 13:34:12 +01:00
Greg Bergé 5f3f4da2d2 Stabilize E2E (#4028) 2026-02-20 15:35:50 +01:00
Nolann B. 93eea0b6d9 Strip trailing slash from OpenAPI server URLs (#4029) 2026-02-20 14:40:36 +01:00
Viktor Renkema 9dfa9c2db0 Add support for rendering Tags (#4006) 2026-02-20 10:03:49 +01:00
spastorelli d9ff25d28e Integrate sites OAuth server flow in GBO for protected resources like MCP (#4015) 2026-02-19 15:48:49 +01:00
Samy Pessé ae30a576e8 Integrate Ask AI with upcoming insights by passing visitor session IDs (#4027) 2026-02-19 14:36:23 +01:00
Nolann B. 2e495cb28a Add Global Privacy Control (GPC) support (#4023) 2026-02-18 21:32:56 +01:00
Nolann B. f517035e38 Remove available scopes list from OAuth2 endpoints (#4022) 2026-02-18 21:20:58 +01:00
spastorelli 83bd0d74cd Fix issue where customizations stored in cookie is shared between different site previews (#4025) 2026-02-18 19:10:54 +01:00
Greg Bergé 3cfefa40fd Support rendering Mermaid blocks (#4018) 2026-02-18 17:00:34 +01:00
Claire Chabas 59ffb95417 Support hint block custom icon (#4007) 2026-02-18 15:02:33 +01:00
Nolann B. 2ebb18dc5a Make OpenAPI Response structure clearer (#4021) 2026-02-18 14:32:07 +01:00
Claire Chabas 1deb897de1 Bump GitBook API (#4019) 2026-02-18 12:53:29 +01:00
Nolann B. 11d9b80e77 Use precedence for OpenAPI servers (#4020) 2026-02-18 01:20:39 +01:00
John Cashman e73d9afd86 fix(pdf): keep headings together across page breaks (#4016) 2026-02-17 15:59:42 +01:00
Greg Bergé 729501c977 Fix language dropdown contrast (#4017) 2026-02-17 14:27:09 +01:00
Greg Bergé 414866cbec Fix external links logic for proxy (#4012) 2026-02-17 11:41:30 +01:00
Claire Chabas 9d5e397574 Remove code syntax highlighting patch for Safari (#4014) 2026-02-16 19:09:19 +01:00
Nolann B. 92c9b8b056 Cache isAIUserAgent result (#4011) 2026-02-16 13:11:43 +00:00
Greg Bergé a73caf69ff Allow to pass a token to the embed script (#4005) 2026-02-16 12:48:02 +01:00
conico974 f72f250b16 Add deploymentId to nextConfig for custom deployment method (#4010) 2026-02-16 12:34:35 +01:00
Viktor Renkema bd330ac133 Remove tracking-wider in search breadcrumbs (#4009) 2026-02-16 11:34:14 +00:00
Nolann B. e31a8f4275 Hide cookie banner for AI user agents (#3999) 2026-02-16 11:49:03 +01:00
Greg Bergé cc38ec2098 Fix regression showing page actions on page without title + description (#4004) 2026-02-13 10:53:25 +01:00
Nolann B. eab11e2d6f Increase timeout for recursive parse test (#4003) 2026-02-13 09:10:25 +00:00
Zeno Kapitein 152490cd67 Normalize undefined site-space language to English in variant categorization (fix language selector) (#4002) 2026-02-13 09:43:45 +01:00
conico974 833b07b284 Bump Vercel version to 50.15.1 (#4001) 2026-02-13 09:43:27 +01:00
Nolann B. 5f668605c2 Use isCookiesTrackingDisabled for cookie consent integrations (#3997)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-12 20:40:18 +01:00
conico974 9c762065f2 Bump Vercel CLI version (#4000) 2026-02-12 18:52:40 +01:00
Viktor Renkema 4af7df8902 Updates to style and interactions of toolbar (#3998) 2026-02-12 16:08:05 +01:00
Zeno Kapitein a3f854e1a8 Better localise French ask label (#3996) 2026-02-12 15:02:43 +01:00
Zeno Kapitein 4c54820055 Improve Docs Embed layout (#3994) 2026-02-12 14:06:37 +01:00
Zeno Kapitein 22f574da9c Fix followup & suggested questions overflow (#3995) 2026-02-12 14:06:28 +01:00
Zeno Kapitein 426aabe39a Fix sidebar on bold themes (#3993) 2026-02-12 11:48:43 +01:00
Taran Vohra 64de8d2cf6 Add e2e test for language variant navigation (#3992) 2026-02-12 15:07:27 +05:30
Greg Bergé b4b471fab6 Fix unexpected error thrown when using the embed in standalone mode (#3985) 2026-02-11 15:15:00 +01:00
Samy Pessé 17b32994f6 Version Packages (#3990)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-11 14:37:35 +01:00
Greg Bergé bcc0ef8666 Fix links handling in embedded Assistant (#3987) 2026-02-11 13:57:48 +01:00
Greg Bergé 6ede6ee7c6 Add Korean translations (#3986) 2026-02-10 15:41:28 +01:00
Nolann B. 7a11861d36 Add support for custom cookie banner registration (#3925) 2026-02-10 11:28:24 +01:00
Greg Bergé 10754fbaf4 Fix GitBook icon not displayed in trademark (#3984) 2026-02-10 10:58:30 +01:00
Zeno Kapitein 8f32f4fb1e Better handle huge section groups (#3978) 2026-02-09 15:13:01 +01:00
Nolann B. 717ad0b300 Fix OpenAPI try it button justify (#3975) 2026-02-05 11:16:58 +01:00
Nolann B. a3eed52f24 Refine OpenAPI schema alternatives/allOf handling (#3973) 2026-02-05 11:00:59 +01:00
Greg Bergé 27a34b7ee2 Support defaultExpanded in Expandable block (#3974) 2026-02-04 11:49:14 +01:00
Zeno Kapitein bf3ad28c7f Add margin to TOC items after page group (#3972) 2026-02-03 16:10:32 +01:00
Zeno Kapitein 70c2e90e4b Handle empty page section search results (#3971) 2026-02-03 16:10:13 +01:00
Greg Bergé d0a378cdb6 Disallow Markdown pages crawling (#3970) 2026-02-03 09:40:17 +01:00
Viktor Renkema 81f8ddcf27 Fix flaky Updates blocks in visual tests (#3969) 2026-02-02 17:32:46 +01:00
Zeno Kapitein 9d6457f814 OpenAPI styling tweaks (#3968) 2026-01-30 19:02:30 +01:00
Zeno Kapitein 07c02ebcd2 Header & TOC styling tweaks (#3967) 2026-01-30 15:23:16 +01:00
Samy Pessé 4d3172d13f Rollout to 100% the dereferencing of reusable content at the API level (#3966) 2026-01-30 09:01:12 +01:00
Zeno Kapitein 0aa34cc9a8 Fix Embed options in React (#3964) 2026-01-29 17:32:56 +01:00
Greg Bergé 551d30cfa4 Fix loading of images in PDF export (#3958)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2026-01-29 15:55:42 +01:00
Samy Pessé 844a0a8ea7 Fix CI for visual testing 2026-01-29 15:26:41 +01:00
Samy Pessé eb8ba66a66 Ensure deployments to preview start when approval job is skipped 2026-01-29 14:06:48 +01:00
Hirotaka Miyagi d738e118a0 Fix: Prevent form submission during IME composition (#3942)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Addison <42930383+addisonschultz@users.noreply.github.com>
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2026-01-29 12:46:52 +00:00
Samy Pessé c16d08f5d5 Add approval step for external contributors' PRs (#3963) 2026-01-29 11:33:13 +01:00
Samy Pessé 61ec9b25b9 Enable approved PR deployments for preview workflows (#3954) 2026-01-29 11:16:00 +01:00
Samy Pessé b57561d395 Fix rendering of images in reusable content from an external space (#3960) 2026-01-29 10:42:25 +01:00
Zeno Kapitein e00d1c5bbd Support code themes customization option, refactor built-in code themes (#3949) 2026-01-28 13:48:56 +01:00
conico974 9063b56c89 Fix for the assistant (#3948) 2026-01-28 11:16:50 +01:00
Greg Bergé 020d3668d7 Make the button icon validation safer (#3955) 2026-01-28 10:44:52 +01:00
Greg Bergé 4d30cbf058 Fix social links not appearing on desktop (#3950) 2026-01-28 10:44:40 +01:00
Samy Pessé d3f6c38f05 Update scalar dependencies (#3956) 2026-01-28 10:29:56 +01:00
Samy Pessé 6f1db32682 Simplify local development and contributions (#3952) 2026-01-28 09:32:56 +01:00
Samy Pessé 4ad6b2c067 Increase rollout of reusable content deref in the API to 80% (#3951) 2026-01-28 09:22:19 +01:00
Viktor Renkema 07449443a0 Fix rendering of Expandables within Update blocks (#3947) 2026-01-27 20:26:33 +01:00
Greg Bergé b09cc64fec Fix GIF rendering (#3946) 2026-01-27 17:34:02 +01:00
Greg Bergé 1eed2004f6 Avoid keyboard-shortcut flicker by adding OS class and using CSS (#3944) 2026-01-27 17:02:49 +01:00
Greg Bergé 91539b8187 Fix navigation loader (#3945) 2026-01-27 17:01:06 +01:00
Samy Pessé 16cfde87ee Increase rollout of reusable content deref in the API to 50% (#3943) 2026-01-27 12:22:09 +01:00
conico974 58b4a978ff Update Next.js dependency to version 15.4.11 (#3940) 2026-01-27 09:26:11 +01:00
Viktor Renkema e34be0e89b Fix logo sizing bug during navigation (#3928) 2026-01-27 08:46:37 +01:00
Samy Pessé e60c754343 Increase rollout of reusable content deref in the API to 30% (#3939) 2026-01-26 16:22:13 +01:00
Nolann B. 48ca21e471 Add OpenAPI 3.1 nullable array support (#3938) 2026-01-26 14:05:14 +01:00
Nolann B. 3ba9e46f2b Improve OpenAPI server URL validation (#3937) 2026-01-26 13:45:03 +01:00
Greg Bergé 58f0cc8287 Fix search field clear button (#3936) 2026-01-24 16:26:03 +01:00
Nolann B. 29e2b22b63 Fix ordered list item index calculation (#3935) 2026-01-23 16:01:33 +01:00
Greg Bergé 3c7cb09c1b Respect DPR to ensure a good image quality (#3931) 2026-01-23 11:45:00 +01:00
Greg Bergé 0f1cd6745d Refactor search (#3932) 2026-01-23 11:13:20 +01:00
Samy Pessé e90f57b138 Deference reusable content at the API level (#3847) 2026-01-23 10:34:44 +01:00
conico974 7bd5939247 Bust data cache (#3927) 2026-01-22 13:59:35 +01:00
Zeno Kapitein 89de966e7a Support social accounts customization option (#3916) 2026-01-22 13:57:40 +01:00
Greg Bergé 4e2d86318e Fix OpenAPI example not showing (#3930)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-22 12:11:55 +01:00
Nolann B. f647af2003 Prevent duplicate Content-Type headers in OpenAPI code samples (#3924) 2026-01-21 14:51:46 +01:00
Greg Bergé 96e24a1f84 Fix JWT token usage in GitBook embed SDK (#3922) 2026-01-21 12:05:14 +01:00
Viktor Renkema 2a0f37f4b3 Always display page actions even if there is no toc/title/description (#3921) 2026-01-21 10:54:42 +01:00
Zeno Kapitein 17d935dd4c Upgrade API to 0.158.0 (#3918) 2026-01-20 17:55:47 +01:00
Greg Bergé df07fbe242 Remove development from package.exports before publishing (#3917) 2026-01-20 17:22:24 +01:00
Zeno Kapitein 1e53376151 Add SideSheet component, refactor TOC and AIChat to use it (#3835) 2026-01-19 21:19:54 +01:00
Samy Pessé f294818775 Serve markdown version of the page depending on the accept type (#3914) 2026-01-19 15:11:02 -05:00
Greg Bergé eed386a133 Fix custom tools usage with GitBook Embed SDK (#3913) 2026-01-19 13:14:23 +01:00
Zeno Kapitein e172b25ba8 Grow InlineActionButton to available space on line (#3912) 2026-01-19 12:55:03 +01:00
Zeno Kapitein f7f6f9d06d Set <html lang> based on translated language (#3909) 2026-01-16 12:00:39 +01:00
Claire Chabas 9e01676fe9 Remove grid from framed images (#3907) 2026-01-15 14:06:26 +01:00
Zeno Kapitein f6cf62a686 Standardise toggle chevrons across the app (#3904) 2026-01-14 13:46:47 +01:00
Zeno Kapitein a5ef44ab2a Better conditional display of page actions (#3900)
Co-authored-by: Claire Chabas <claire.chabas@gmail.com>
2026-01-14 13:21:47 +01:00
Zeno Kapitein b53cb1d2b3 Align styling of PageLinkItem and PageDocumentItem (#3903) 2026-01-14 13:07:45 +01:00
Samy Pessé 7eeff73080 Version Packages (#3902)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-13 16:13:41 +00:00
Greg Bergé bf63045319 Fix OpenAPI JSX compilation (#3901) 2026-01-13 16:56:51 +01:00
Samy Pessé 71c2e7f8b4 Version Packages (#3886)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-13 13:40:37 +00:00
Samy Pessé 985c1b45b4 Fix OpenAPI dereferencing for $ref to itself (#3899) 2026-01-13 13:34:08 +00:00
Zeno Kapitein 35efc9d6d8 Fix TOC icon backgrounds on gradient and bold themes (#3898) 2026-01-12 15:45:41 +01:00
Nolann B. 6b1fd43179 Lazy load OpenAPI styles and remove Tailwind Typography (#3883) 2026-01-12 15:09:02 +01:00
Zeno Kapitein 7517e987a5 Clean up button and input across the app (#3897) 2026-01-12 12:41:57 +01:00
Zeno Kapitein 21851d3340 Round page cover corners on extrawide screens (#3892) 2026-01-12 11:58:17 +01:00
Samy Pessé ca682362dd Fix 404 when accessing rss.xml for the root page (#3896) 2026-01-09 16:05:31 +01:00
David Hughes 56fb754e22 Update published-site.png (#3894) 2026-01-08 09:47:37 +08:00
Zeno Kapitein 99d788ed9e Update page cover styling (#3890) 2026-01-07 11:55:17 +01:00
Taran Vohra ebf3b2beb7 Fix an issue where content refs to spaces outside of site was resolving with share links (#3889) 2026-01-07 11:59:02 +05:30
Zeno Kapitein 7b7231602f Add e2e tests for embed frame (#3830) 2026-01-06 17:30:14 +01:00
Addison adfb118285 Merge branch 'main' of github.com:GitbookIO/gitbook 2026-01-06 13:22:44 +08:00
Zeno Kapitein a7f58ab829 Fix embed tab button size regression (#3887) 2026-01-05 14:01:02 +01:00
Zeno Kapitein 360b525b4b Fix FontAwesome 7 icon overflows (#3885) 2026-01-05 13:56:08 +01:00
Zeno Kapitein 695e99f466 Update button sizing (#3868) 2026-01-05 11:18:00 +01:00
Zeno Kapitein aa87699915 Fix block alignment (#3880) 2025-12-18 21:26:10 +01:00
Zeno Kapitein d2eb3d9f5a Fix disabled button from not showing up (#3879) 2025-12-18 21:13:14 +01:00
Samy Pessé 77bc339fca Version Packages (#3878)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-18 14:47:36 +01:00
Zeno Kapitein e7fb77116e Fix @gitbook/icons icon rendering (#3877) 2025-12-18 14:27:34 +01:00
Samy Pessé 050ca23ac4 Version Packages (#3818)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-18 11:10:53 +00:00
Zeno Kapitein 9063f97dba Switch main DocumentView to flex layout (#3875) 2025-12-18 09:43:42 +01:00
Zeno Kapitein ed9e8fe93a Support button actions (#3866) 2025-12-18 09:29:08 +01:00
Zeno Kapitein f478ddc2ed Create <Input> component (#3857) 2025-12-16 19:43:43 +01:00
Zeno Kapitein bd11c0d21b Fix trademark rounded corners in embed (#3874) 2025-12-16 15:10:47 +01:00
Zeno Kapitein 381728970e Fix overflowing section groups (#3873) 2025-12-15 20:41:00 +01:00
Taran Vohra 4690e2087a Roll out meta links resolution to all sites (#3872) 2025-12-15 21:19:34 +05:30
John Cashman 224a44daf8 Fix priority assignment in sitemap generation (#3854) 2025-12-15 15:41:29 +00:00
Nolann B. 4766092ff9 Fix missing properties in allOf/oneOf (#3871) 2025-12-13 12:38:09 +01:00
conico974 3e4e0b0bf0 bump next again (#3870) 2025-12-12 20:01:11 +01:00
Samy Pessé 6b985051f3 Use response.id instead of responseId (#3867) 2025-12-12 13:52:05 +01:00
Johan Preynat 2d08a6e6de Return not found when decodeURIComponent from params fail (#3863) 2025-12-12 13:27:19 +01:00
conico974 d539409fc7 Bump Next.js version to 15.4.9 (#3864) 2025-12-12 08:46:04 +01:00
Taran Vohra c30ed46021 Bump meta links resolution percentage to 50% (#3807) 2025-12-11 21:58:16 +05:30
Zeno Kapitein fc98a4a881 Fix rounded corners on list-line TOC style (#3861) 2025-12-11 10:51:03 +00:00
Zeno Kapitein 6278a1844e Support greeting and fix suggested questions if there are no custom questions (#3856) 2025-12-11 11:45:42 +01:00
Zeno Kapitein 86e7aa6c5e Fix search results cursor not updating (#3858) 2025-12-11 11:45:33 +01:00
Nolann B. 8761cee1bb Enhance discriminator handling in OpenAPISchema (#3855) 2025-12-11 08:11:14 +01:00
Zeno Kapitein 44feb3b140 Rework dynamic icons (AIChatIcon, AISearchIcon) (#3853) 2025-12-09 12:15:05 +01:00
conico974 6b43773752 chore: update dependencies and remove Suspense on ISR requests (#3852) 2025-12-08 09:26:15 +01:00
Zeno Kapitein 62438f447e Update FontAwesome to 7.1.0 (#3851) 2025-12-06 12:54:04 +01:00
Nolann B. 87d68ea59e Fix OpenAPI oneOf/allOf merge (#3844) 2025-12-05 17:26:18 +01:00
Claire Chabas 5f9c80e4f2 Use link title for footer links and page cards (#3849) 2025-12-05 14:25:43 +01:00
Zeno Kapitein 9c69cc413f Add customizable suggested questions (#3789) 2025-12-05 13:25:26 +01:00
Claire Chabas b77623b9f3 Fix link title fallback in ToC item (#3845) 2025-12-04 12:58:20 +01:00
Zeno Kapitein 1430ceebea Update Docs Embed with new styling and tabs (#3823) 2025-12-04 10:01:25 +01:00
Zeno Kapitein 3fbd6f0d50 Hide empty Footer correctly (#3842) 2025-12-03 12:21:38 +00:00
Claire Chabas 8f0b97906b Support new "link title" page option (#3798) 2025-12-03 13:20:22 +01:00
Zeno Kapitein d460dc4b39 Update Stepper styling for theme-muted (#3841) 2025-12-03 12:18:44 +00:00
Zeno Kapitein 27b9f7817b Remove corner radius of hint block with heading (#3839) 2025-12-03 10:43:02 +00:00
Zeno Kapitein 9e062b10d3 Make semantic text colors more vibrant (#3838) 2025-12-03 10:39:41 +01:00
Nolann B. 74f89316d9 Fix hidden section not found (#3836) 2025-12-02 11:37:55 +01:00
Zeno Kapitein 9e1d7b296b Update AIChat context card open delay (#3834) 2025-12-02 10:24:18 +01:00
Nolann B. b49caafa9c Support for hidden sections (#3831) 2025-12-01 14:48:48 +01:00
Zeno Kapitein f372eec393 Make annotations visible on hover, fix accessibility and outro animations (#3822) 2025-11-28 17:15:29 +01:00
Zeno Kapitein 65ae941687 Replace AI Chat generated content in e2e (#3833) 2025-11-28 16:46:04 +01:00
Viktor Renkema 75b8efa155 Remove sticky prop on Updates block (#3832) 2025-11-28 10:08:21 +01:00
Utku Ufuk 34b0c67c91 Stop filtering by language when site space language is unspecified (#3825) 2025-11-27 17:57:57 +03:00
Zeno Kapitein e0ace99707 Fix flaky tests in AI Chat greeting (#3828) 2025-11-27 09:50:03 +01:00
Viktor Renkema 3553db0989 Add RSS feed for pages with update blocks (#3824)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-11-26 20:34:25 +01:00
Zeno Kapitein 5c14948cf3 Prevent flaky tests in AI Chat followup suggestions (#3827) 2025-11-26 18:39:51 +01:00
conico974 8e4e2a2333 Update wranglerVersion to 4.43.0 (#3826) 2025-11-26 18:35:18 +01:00
Zeno Kapitein 0f4c53cfbd Update Assistant e2e tests (#3814) 2025-11-26 17:09:48 +00:00
Zeno Kapitein c156e946b0 Update Assistant styling (#3797) 2025-11-24 12:41:00 +01:00
Nolann B. 461e15fd00 Add x-gitbook-prefix and x-gitbook-token-placeholder for OpenAPI security scheme (#3820) 2025-11-22 10:44:05 +01:00
conico974 2989f79708 Add 'use server' directive to markdown renderer (#3815) 2025-11-20 09:28:00 +01:00
Nolann B. 344842f5ce Improve OpenAPI circular references (#3821) 2025-11-19 22:44:32 +01:00
Samy Pessé c51076efc4 Fix links to other spaces in embeddable view (#3819) 2025-11-19 10:38:53 +00:00
Zeno Kapitein 38e34dcce3 Optimise page outline (#3813) 2025-11-19 11:26:16 +01:00
Nolann B. 04a4175678 Remove hidden site spaces from navigation (#3795) 2025-11-18 22:49:41 +01:00
Nolann B. 3e40b4db50 Fix OpenAPI basic auth placeholder (#3816) 2025-11-18 20:18:59 +01:00
Greg Bergé 854e612e5d Fix handling of broken links in buttons (#3812) 2025-11-18 11:12:27 +01:00
Samy Pessé 27a461be9d Fix NPM publishing once and for all (#3811) 2025-11-16 02:16:20 +01:00
Samy Pessé 3d83dadb12 New attempt at fixing NPM publishing with trusted publisher (#3810) 2025-11-16 01:34:57 +01:00
Samy Pessé 8dc5c18b9f Pass proper GITHUB_TOKEN to the publish script 2025-11-16 00:35:16 +01:00
Samy Pessé e4756c23a7 Use dedicated step to publish to NPM 2025-11-16 00:28:37 +01:00
Samy Pessé 79ad7bcb59 Fix publishing to NPM (#3809) 2025-11-16 00:25:26 +01:00
Samy Pessé 5b0917a13a Remove --dry-run in publish script 2025-11-15 23:36:35 +01:00
Samy Pessé 6d6f4ef6dd Fix the script to publish npm packages and make it more robust 2025-11-15 23:31:14 +01:00
Samy Pessé fa69a64a2b Merge branch 'main' of https://github.com/GitbookIO/gitbook 2025-11-15 19:47:52 +01:00
Samy Pessé d1a85d1f91 Try fixing the publishing workflow 2025-11-15 19:47:36 +01:00
Samy Pessé 05bf9d7a59 Version Packages (#3733)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-15 19:34:29 +01:00
Samy Pessé 2e0b3370c8 Fix permissions for publish workflow 2025-11-15 19:32:41 +01:00
Samy Pessé 10995e09ca Use trusted publishing for publishing NPM packages (#3808) 2025-11-15 19:26:26 +01:00
Samy Pessé e7cc3ff61f Revert "Use NPM dependency for eval-estree-expression"
This reverts commit 59d259074b.
2025-11-15 18:13:10 +01:00
Samy Pessé 59d259074b Use NPM dependency for eval-estree-expression 2025-11-15 17:58:54 +01:00
Zeno Kapitein 96c4ae4f2d Fix Heading in hint blocks are huge in published content (#3806) 2025-11-14 15:26:46 +01:00
Taran Vohra 64f3596df2 Output generic alternates for page metadata (#3804) 2025-11-14 16:32:19 +05:30
Taran Vohra 27d6c3af39 Use an available alternate when switching variants in a page (#3796) 2025-11-14 15:19:40 +05:30
Nolann B. 1de4eb7973 Fix @modelcontextprotocol/sdk version (#3805) 2025-11-13 15:38:33 +01:00
Nolann B. df1966df25 Bump Scalar (#3803) 2025-11-13 15:29:25 +01:00
conico974 1001aaf94e Refactor DynamicTabs to use NavigationStatusContext (#3800) 2025-11-13 14:37:09 +01:00
Zeno Kapitein 155e448c8f Support primary link in logo (#3802) 2025-11-13 12:59:17 +01:00
Viktor Renkema ca2a94ee1a Support sticky date + new third date format (#3801) 2025-11-13 12:09:53 +01:00
Nolann B. a512c90bda Re-arrange OpenAPI Scopes for OAuth2 (#3799) 2025-11-12 19:40:21 +01:00
conico974 9cbefa89c7 Filter invalid events sent to the API (#3792) 2025-11-12 11:54:13 +01:00
Taran Vohra c8bba7bfe1 Resolve page meta links for metadata (#3793) 2025-11-12 16:12:27 +05:30
Viktor Renkema 4e04db8a1b Support rendering upcoming Update blocks (#3794) 2025-11-12 10:16:02 +01:00
Nolann B. b45feafeff Disable OpenAPI "Try it" when no servers are defined (#3791) 2025-11-10 17:25:22 +01:00
Zeno Kapitein 3876f7081e Edgeless Assistant window on depth: flat sites (#3788) 2025-11-10 14:37:28 +01:00
Nolann B. b4a021a192 Add heredoc support for cURL JSON body (#3790) 2025-11-10 11:20:53 +01:00
Zeno Kapitein 1d92dc1350 Update site layout and blocks with container queries (#3780) 2025-11-10 10:55:03 +01:00
Nolann B. e4ba35fdae Rework OpenAPI Authorizations + scopes (#3786) 2025-11-06 21:20:09 +01:00
Viktor Renkema 35dcaca971 Support custom cover heights (#3779) 2025-11-05 14:21:47 +01:00
Zeno Kapitein 2aa4f2e0b8 Update DropdownMenu styling (#3787) 2025-11-05 08:40:53 +01:00
Zeno Kapitein 6d56df7c10 Treat pages without visible TOC items as no-toc (#3783) 2025-11-05 08:40:15 +01:00
Zeno Kapitein 4534ea83c3 Implement unique title tags for sections & variants (#3768) 2025-11-04 11:36:28 +01:00
Zeno Kapitein 9022d87ec7 Add page breadcrumbs to search results (#3784) 2025-11-04 00:31:32 +01:00
Brett Jephson a49781e200 Expand code block when printing (#3782) 2025-11-03 15:26:18 +00:00
Steven H 065a5e6e55 Disable Workers Observability (#3781) 2025-11-03 11:17:33 +00:00
Zeno Kapitein e27986b17a Reword "Everywhere" search scope to "All docs" (#3776) 2025-11-03 09:57:21 +01:00
Zeno Kapitein 8606f222c9 Add padding to TOC page group items (#3777) 2025-11-03 09:56:39 +01:00
Nolann B. 2c3066e3ae Improve OAuth2 scopes handling in OpenAPI (#3775) 2025-10-30 15:46:29 +01:00
Zeno Kapitein 55c0b03a41 Support translations and generic variants together (#3772) 2025-10-30 15:03:50 +01:00
conico974 3e548e4186 Initialize hash on mount and improve navigation handling (#3774) 2025-10-30 14:33:26 +01:00
Viktor Renkema 3aea302648 Revert "Support cover image heights and positioning changes" (#3771) 2025-10-30 10:24:22 +01:00
Zeno Kapitein 1d1f0a7c6a Fix search container shadow (#3770) 2025-10-29 11:14:31 +01:00
conico974 590729cce5 Redirect to space base path on fallback true (#3745) 2025-10-29 10:55:27 +01:00
conico974 b554d7d13f Add isRoot prop to PagesList for root header offset (#3759) 2025-10-29 10:50:53 +01:00
conico974 9568b8a7c8 Add keep_names option to wrangler configurations (#3769) 2025-10-29 10:00:19 +01:00
Zeno Kapitein 683bb8f634 Add confirmation to "Copy MCP URL" page action (#3763) 2025-10-28 12:42:02 +01:00
Zeno Kapitein 7d6a31627f Revert contentref anchor title (#3767) 2025-10-28 12:27:59 +01:00
Brett Jephson c31e5648f5 RND-8495: page group emoji and icon resolution (#3766) 2025-10-28 10:23:08 +00:00
Zeno Kapitein 0c01832684 SearchInput fixes (#3762) 2025-10-28 09:50:31 +01:00
Zeno Kapitein 6b1c9fef86 Fix opacity of ScrollContainer buttons on bold themes (#3764) 2025-10-28 09:37:23 +01:00
Claire Chabas cc976995cc Fix image frame width (#3765) 2025-10-27 20:01:12 +01:00
Nolann B. ebbb476589 Improve ancestor label in references (#3761) 2025-10-27 16:49:48 +01:00
Greg Bergé 89655cb428 Fix reset scroll issue (#3760) 2025-10-27 16:41:39 +01:00
Zeno Kapitein 3676d64bdc Update search scope UI (#3750) 2025-10-27 15:57:47 +01:00
Viktor Renkema f682df1e01 Support cover image heights and positioning changes (#3758) 2025-10-27 15:55:50 +01:00
Greg Bergé 6c9893945d Fix scrolling hash into tabs / expandable (#3757) 2025-10-25 15:30:52 +02:00
Greg Bergé a2fb627ae1 Fix scroll jump and scroll reset (#3756) 2025-10-25 13:04:34 +02:00
Nolann B. f9f80117f0 Add alt text support to card covers (#3715) 2025-10-24 16:27:23 +02:00
conico974 63bf4dbb16 Fix cross-space content reference resolution and enhance anchor text rendering (#3754) 2025-10-24 15:26:59 +02:00
Brett Jephson b3fa503aff Expandable code block (#3753) 2025-10-24 14:01:31 +01:00
Zeno Kapitein 0ae7cfd790 Improve AI Search branding (#3752) 2025-10-24 14:36:31 +02:00
Brett Jephson 8fdc5c89dc Fix: height issues with webframe (#3749) 2025-10-24 10:29:46 +01:00
Zeno Kapitein 758b34d46b Move the "Ask <query>" to the bottom of search results if query is not a question (#3751) 2025-10-24 10:58:51 +02:00
Zeno Kapitein 1b0613e482 Tweak language dropdown styling (#3748) 2025-10-22 22:00:52 +02:00
conico974 3164f1a99d Fix preloading images (#3747) 2025-10-22 15:09:42 +02:00
Greg Bergé 6b588af07d Clarify CONTRIBUTING.md (#3742) 2025-10-22 12:43:28 +00:00
Greg Bergé d436b04ff4 Fix jump when clicking tabs (#3746) 2025-10-21 18:30:33 +02:00
conico974 d159788d0b Add end-to-end tests for table of contents navigation (#3741) 2025-10-21 12:39:23 +02:00
Nolann B. 3355960d95 Fix ToggeableLinkItem display (#3744)
Co-authored-by: Zeno Kapitein <zeno@gitbook.io>
2025-10-21 10:32:17 +02:00
Nolann B. c32d4494f2 Fix OpenAPI path display (#3743) 2025-10-19 21:44:26 +02:00
conico974 1dcf4df51f Fix table of contents display issue (#3740) 2025-10-17 19:20:33 +00:00
conico974 3c0e49d56b Fix cache race condition (#3738) 2025-10-17 19:42:16 +02:00
Greg Bergé c35d0e8954 Run tests automatically when "Version packages" PR is opened (#3737) 2025-10-17 14:28:38 +02:00
Greg Bergé c0d73d2f76 Improve tabs (#3735) 2025-10-17 12:13:09 +02:00
Viktor Renkema 454175ab86 Change copy of toolbar button (#3736) 2025-10-17 07:17:39 +00:00
Greg Bergé 6feb8fcd6a Support emojis in outline (#3734) 2025-10-16 23:49:12 +02:00
conico974 b8d3cb7ee5 Improve search error handling and token redirection (#3732) 2025-10-16 15:39:32 +02:00
Nolann B. 8ce73229c3 Add OpenAPI servers selection (#3726) 2025-10-16 14:27:42 +02:00
Greg Bergé 8eae6e970b Upgrade to bun 1.3 (#3731) 2025-10-16 10:22:52 +02:00
Greg Bergé a48b886dfd Add button to download file (#3730) 2025-10-15 17:46:47 +02:00
Greg Bergé 17de0838c5 Allow to split pages between list items (#3729) 2025-10-15 14:02:41 +02:00
Greg Bergé baf3496980 Improve inline tooltip performances (#3728) 2025-10-15 13:30:38 +02:00
Samy Pessé ae33c09aa7 Version Packages (#3724)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-15 13:30:24 +02:00
Greg Bergé 7ff69c3b72 Fix bun token (#3727) 2025-10-15 10:43:44 +02:00
Greg Bergé 6142d6bf80 Build all packages using tsdown (#3722) 2025-10-14 14:39:19 +02:00
Brett Jephson 75543b140f Update Node version to work with globalThis.process.getBuiltinModule (#3725) 2025-10-14 11:13:26 +01:00
Nolann B. 511f1cfbe5 Add scroll margin to Expandable (#3723) 2025-10-13 15:54:32 +02:00
Samy Pessé 46fa9d954c Version Packages (#3720)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-13 08:42:09 +02:00
Greg Bergé 5e1ed5358e Add npm token to publish with bun (#3721) 2025-10-13 11:38:57 +05:30
Greg Bergé 295f03db49 Force republishing packages (#3719) 2025-10-11 07:36:58 +00:00
Claire Chabas bdde3929fc Fix web frame height (#3709) 2025-10-10 17:41:54 +02:00
conico974 4f4210d24a Update @opennextjs/cloudflare to version 1.9.2 (#3710)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-10-10 17:20:11 +02:00
Greg Bergé c038ec8f81 Fix package publishing (#3718) 2025-10-10 13:39:39 +00:00
Samy Pessé 75b52eac28 Version Packages (#3651)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-10 15:11:33 +02:00
Greg Bergé ad4d07dc06 Support inline icon colors (#3717) 2025-10-10 14:51:40 +02:00
Greg Bergé 75086740f8 Fix bundling of embed package (#3716) 2025-10-10 10:57:06 +02:00
Nolann B. eea8f1e00f Enhance OpenAPI security scopes handling (#3712) 2025-10-09 10:16:12 +02:00
Greg Bergé 4f9abfb6f2 Display headings from reusable content in TOC (#3708) 2025-10-08 12:17:05 +02:00
conico974 252afceab0 Store customization in a cookie for preview route (#3707)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-10-08 10:37:52 +02:00
Viktor Renkema bef191660a Allow hiding toolbar for one session or persistent (#3698) 2025-10-08 09:48:38 +02:00
Zeno Kapitein 145625142d Unify section display condition (#3706) 2025-10-07 15:12:08 +02:00
Greg Bergé f70a2aa310 Disable flashing when navigating between sections (#3705) 2025-10-07 12:00:42 +02:00
Samy Pessé 56e46ce8ab Add Italian translation (#3704) 2025-10-06 17:07:30 +02:00
Nolann B. 754cc11e0b Fix merge logic for allOf alternatives (#3515) 2025-10-06 14:37:51 +02:00
Nolann B. 3551d147e9 Move to urls for page actions (#3703) 2025-10-06 09:42:37 +02:00
Zeno Kapitein 162cfe4163 Hide sections if only one is present (#3701) 2025-10-04 20:16:02 +02:00
Nolann B. 6815dd197e Fix markdownPageURL for PageActions (#3702) 2025-10-04 19:02:30 +02:00
Zeno Kapitein d74f8a7098 Improve wide page width (#3692) 2025-10-03 18:52:05 +02:00
Zeno Kapitein 1e53f46521 Fix button content truncate (#3693) 2025-10-03 18:51:32 +02:00
Claire Chabas 1a6216df4f Fix URL used for Ask to LLM (#3697) 2025-10-03 14:24:24 +02:00
Nolann B. a629900324 Add dev script for @gitbook/expr (#3700) 2025-10-03 13:23:15 +02:00
Nolann B. a4c33997fe Fix OpenAPI alternatives not showing (#3699) 2025-10-03 13:06:10 +02:00
Brett Jephson badf3a92f6 RND-8165: make sure we get alt text coming through on InlineImage (#3696) 2025-10-03 09:40:34 +01:00
Claire Chabas e0fb9ac3ca Support image frame (#3695) 2025-10-02 10:17:48 +02:00
spastorelli f3e40410c4 Adapt OpenAPI code samples to prefill API key using visitor data (#3666) 2025-09-30 11:09:05 +02:00
spastorelli 8e99871004 Highlight discriminator properties in oneOf, allOf, anyOf objects (#3691) 2025-09-29 17:09:21 +02:00
Zeno Kapitein 1e4e54e752 Fix rounded outline items (#3694) 2025-09-29 13:33:40 +02:00
spastorelli 50c1be3447 Move inline expression evaluation from API to GBO (#3687) 2025-09-26 10:55:09 +02:00
spastorelli 319a1e536c Ensure operation security reqs inherits from spec-level when not defined (#3690) 2025-09-26 09:11:52 +02:00
conico974 50cb0c6e15 Fix NavigationLoader on search and cmd/click (#3689)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-26 08:34:41 +02:00
Zeno Kapitein ceb32b1a2e Redo search scopes (#3688)
Co-authored-by: Claire Chabas <claire.chabas@gmail.com>
2025-09-25 16:49:53 +00:00
Zeno Kapitein f8c54b4d34 Improve assistant demo (#3686) 2025-09-25 17:07:07 +02:00
Greg Bergé c06ad70f8a Improve fonts loading (#3682) 2025-09-25 13:17:21 +02:00
Brett Jephson 0ef5dc8538 Update column width sizing (#3676) 2025-09-25 08:14:15 +01:00
Zeno Kapitein 28adc9b5f1 Add graphic to embed assistant demo page (#3685) 2025-09-24 14:37:41 +00:00
Zeno Kapitein 912d1693f5 Fix section group alignment (#3684) 2025-09-24 13:58:34 +00:00
Samy Pessé c23d4ef331 Fix crash for card cover defined without objectFit (#3683) 2025-09-24 11:01:06 +02:00
Zeno Kapitein c21693fe50 Improve screen reader accessibility for hints (#3680) 2025-09-24 10:23:34 +02:00
Zeno Kapitein c06b3dd60d Improve default site icon (#3679) 2025-09-24 10:22:49 +02:00
conico974 f173e014db Use cloudflare regional cache instead of our own implementation. (#3673)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-24 10:11:11 +02:00
conico974 1b458c436e Fix NavigationLoader not visible with bold header (#3681)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-24 09:54:57 +02:00
conico974 b42b0b3910 Add navigation loader and rename context provider (#3642)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
Co-authored-by: Zeno Kapitein <zeno@gitbook.io>
2025-09-23 15:00:27 +02:00
Brett Jephson 2fc21272ff RND-7985: accessible search (#3637) 2025-09-23 11:36:50 +01:00
Zeno Kapitein 379d486488 Expose "Best match" when site has translations (#3670) 2025-09-22 22:31:17 +02:00
Zeno Kapitein cba583a31c Use siteSpace title instead of space title in search (#3677) 2025-09-22 18:03:50 +02:00
Zeno Kapitein 7a504b7cc5 Fix OpenAPI response dropdown & vertical overflow (#3678) 2025-09-22 18:03:43 +02:00
Addison 8ec5fd0986 Add blob: to style source for integrations (#3675) 2025-09-22 18:41:16 +09:00
Addison b9ad997fcb Add blob to style source for integrations 2025-09-22 17:01:16 +09:00
spastorelli 529f940a45 Fix OpenAPISecurities and code sample not using operation security requirements (#3671) 2025-09-22 10:00:46 +02:00
Viktor Renkema 8a3c15911f Update tooltip labels in docs sites toolbars (#3674) 2025-09-22 09:28:42 +02:00
spastorelli d7948e34b2 Fix OpenAPI response showing as JSON instead of YAML (#3669) 2025-09-18 18:40:24 +02:00
conico974 9f42211993 Enhance tracking with geolocation headers and continent info in analytics proxy (#3667)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-18 18:31:21 +02:00
conico974 65e62307bf Update @opennextjs/cloudflare to version 1.8.3 (#3668)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-18 18:04:38 +02:00
spastorelli 469b332da2 Add support for inline expressions rendering with visitor data on GBO side (#3660) 2025-09-18 16:30:52 +02:00
Zeno Kapitein e434442f1e Support site section groups (#3664) 2025-09-18 15:28:39 +02:00
Samy Pessé 9344431bfc Fix API URL we proxy insights events to (#3665) 2025-09-18 13:14:25 +00:00
conico974 622ecbec1d Handle broken links in header and footer (#3663)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-18 11:22:57 +02:00
conico974 fa1382b289 Add responsive image sizes for RecordCard component (#3654)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-18 08:55:11 +02:00
Zeno Kapitein b8388e5a2a Add ScrollContainer component, use for section tabs (#3661) 2025-09-17 22:20:32 +02:00
Zeno Kapitein 2bce12798f Fix section group dropdown offset on big screens (#3662) 2025-09-17 19:10:52 +02:00
Greg Bergé f89e889d18 Convert relative URL to absolute in Markdown (page and copy) (#3659) 2025-09-17 18:12:01 +02:00
Zeno Kapitein 2b8a2d24b6 Add stable scroll gutter to search container (#3656) 2025-09-17 17:27:47 +02:00
Zeno Kapitein b85eccdff5 Refactor site section tabs (#3655) 2025-09-17 17:27:34 +02:00
Viktor Renkema d924e655c0 Add visibility label and smoothen magnification (#3657) 2025-09-16 16:34:21 +02:00
Zeno Kapitein b932e4e2e9 Scope search across sections and variants (#3640)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-16 12:07:29 +02:00
Samy Pessé ca4a3e8c9a Track utm on links from the toolbar (#3653) 2025-09-15 16:51:31 +02:00
John Cashman b498521196 Refactor robots.txt generation logic (#3621)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-09-15 14:37:02 +02:00
Samy Pessé 8a8cf2025d Skip using __session cookie if it's in the old format (#3652) 2025-09-15 12:10:51 +02:00
Samy Pessé ed7d47d9b8 Toolbar for authenticated GitBook users (#3650)
Co-authored-by: Viktor Renkema <vrenkema@gmail.com>
2025-09-15 10:32:23 +02:00
Viktor Renkema b5494cc04b Restyle the toolbar in CR and revision preview links (#3631)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-09-15 08:33:23 +02:00
Samy Pessé 229f2ba9d5 Version Packages (#3646)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-12 16:40:28 +02:00
spastorelli 6da36558f6 Fix exports in gitbook/expr package.json after removing src (#3648) 2025-09-12 16:20:29 +02:00
Samy Pessé ca71c1fad6 Fix MCP in production (#3647) 2025-09-12 14:59:47 +02:00
Samy Pessé 262afa357b Implement an MCP server on each docs site (#3641) 2025-09-12 13:19:16 +02:00
Samy Pessé 7375d3c597 Version Packages (#3645)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-12 11:37:22 +02:00
spastorelli 3548fa6dff Fix eval estree expr cjs named import when gitbook/expr is imported in playwright tests (#3643) 2025-09-12 11:20:33 +02:00
conico974 cb73040e0f Bump Next.js to version 15.4.0 (#3644)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-12 11:17:43 +02:00
Samy Pessé 872d36b64f Version Packages (#3629)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-11 18:06:31 +02:00
spastorelli e1ff17e655 Fix bundling of @gitbook/expr package (#3639) 2025-09-11 17:24:06 +02:00
Utku Ufuk f3010bd28a Stop using deprecated search API param (#3638) 2025-09-11 15:26:35 +03:00
spastorelli 8ff1e3b619 Port latest changes from gbx util-expr (#3636) 2025-09-09 18:55:41 +02:00
Viktor Renkema d7596bf454 Don't show toolbar when rendering within GitBook app preview (#3635) 2025-09-09 15:09:52 +02:00
Zeno Kapitein aea5eb10ae Persist language choice across sections (#3633) 2025-09-09 15:06:28 +02:00
Zeno Kapitein 1165a81cf5 Language selector fixes (#3632) 2025-09-08 17:22:42 +02:00
conico974 61d1a0192e Fix null contentRef handling in RecordColumnValue (#3566)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-08 13:54:58 +02:00
Zeno Kapitein f9a2977621 Better handling for external link "mailto:" in Hovered Card in GBO (#3630) 2025-09-08 10:31:30 +00:00
spastorelli 24f601d594 Small optim in resolveTryItPrefillForOperation and remove uneeded dep (#3628) 2025-09-08 10:29:59 +02:00
499 changed files with 24732 additions and 7812 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Handle non-array required field in OpenAPI schema to fix TypeError with specs using boolean required values
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Strip trailing slash from OpenAPI server URLs to avoid double slashes in rendered paths
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Remove code syntax highlighting patch for Safari
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Prevent inline expression from wrapping to the next line
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/embed": patch
---
Fix unexpected error thrown when using the embed in standalone mode
+6
View File
@@ -0,0 +1,6 @@
---
"@gitbook/browser-types": patch
"gitbook": patch
---
Use isCookiesTrackingDisabled for cookie consent integrations
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Fix TOC header logo on bold themes
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Integrate Ask AI with upcoming insights by passing visitor session IDs.
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Update Reddit social account rendering
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Make OpenAPI Response structure clearer
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Use precedence for OpenAPI servers
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Hide cookie banner for AI user agents
+6
View File
@@ -0,0 +1,6 @@
---
"gitbook": patch
"@gitbook/browser-types": patch
---
Add Global Privacy Control (GPC) support
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Remove available scopes list from OAuth2 endpoints when no scopes are required
+3 -4
View File
@@ -54,10 +54,9 @@ After forking this repository, you'll want to [create a branch](https://docs.git
#### 3. Install dependencies and run the project locally
##### Prerequisites:
- Node.js (Version: >=20.6)
- Use `nvm` for easy Node management
- [Bun](https://bun.sh/) (Version: >=1.2.15)
- We use a text-based lockfile which isn't supported below 1.2.15
- [Node.js](https://nodejs.org/en) (see "engines" in `package.json`)
- [Bun](https://bun.sh/) (see "packageManager" in `package.json`)
##### Setup steps:
@@ -30,7 +30,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: deployments status --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
@@ -49,7 +49,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
@@ -61,7 +61,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
@@ -71,7 +71,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
@@ -52,6 +52,7 @@ runs:
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
GITBOOK_OAUTH_SERVER_URL: ${{ inputs.opItem }}/GITBOOK_OAUTH_SERVER_URL
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
GITBOOK_IMAGE_RESIZE_URL: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_URL
@@ -70,7 +71,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: deploy --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc
@@ -81,7 +82,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
@@ -104,7 +105,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '4.43.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
@@ -51,6 +51,7 @@ runs:
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
GITBOOK_OAUTH_SERVER_URL: ${{ inputs.opItem }}/GITBOOK_OAUTH_SERVER_URL
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
GITBOOK_IMAGE_RESIZE_URL: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_URL
+3 -7
View File
@@ -4,8 +4,7 @@ on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
- changeset-release/main
jobs:
format:
runs-on: ubuntu-latest
@@ -35,13 +34,10 @@ jobs:
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- run: bun unit
build-oss:
# CI to check that the repository builds correctly on a machine without the credentials
build:
runs-on: ubuntu-latest
name: Build (Open Source)
name: Build
timeout-minutes: 6
env:
NPM_TOKEN_READONLY: ''
steps:
- name: Checkout
uses: actions/checkout@v4
+64 -15
View File
@@ -1,15 +1,25 @@
name: Preview
on:
pull_request:
pull_request_target:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
branches:
- main
jobs:
approval:
# Ensure that external contributors' PRs are manually approved before deployment
name: Approve Preview Deployment
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork }}
environment:
name: preview-approval
steps:
- name: Approval gate
run: echo "Preview deployment approved."
deploy-v2-vercel:
name: Deploy v2 to Vercel (preview)
runs-on: ubuntu-latest
needs: approval
if: ${{ always() && (needs.approval.result == 'success' || needs.approval.result == 'skipped') }}
environment:
name: 2v-preview
url: ${{ steps.deploy.outputs.deployment-url }}
@@ -18,6 +28,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Deploy to Vercel
id: deploy
uses: ./.github/composite/deploy-vercel
@@ -31,6 +43,8 @@ jobs:
deploy-v2-cloudflare:
name: Deploy v2 to Cloudflare Worker (preview)
runs-on: ubuntu-latest
needs: approval
if: ${{ always() && (needs.approval.result == 'success' || needs.approval.result == 'skipped') }}
environment:
name: 2c-preview
url: ${{ steps.deploy.outputs.deployment-url }}
@@ -39,6 +53,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Deploy to Cloudflare
id: deploy
uses: ./.github/composite/deploy-cloudflare
@@ -65,7 +81,7 @@ jobs:
comment-deployments:
runs-on: ubuntu-latest
name: Comment Deployments (preview)
if: always() && !startsWith(github.ref, 'refs/heads/main')
if: always() && github.event_name != 'push'
needs:
- deploy-v2-vercel
- deploy-v2-cloudflare
@@ -102,10 +118,13 @@ jobs:
runs-on: ubuntu-latest
name: Visual Testing v2
needs: deploy-v2-vercel
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
@@ -118,15 +137,18 @@ jobs:
BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'v2-vercel'
ARGOS_BUILD_NAME: v2-vercel
visual-testing-v2-cloudflare:
runs-on: ubuntu-latest
name: Visual Testing v2 (Cloudflare)
needs: deploy-v2-cloudflare
if: ${{ always() && needs.deploy-v2-cloudflare.result == 'success' }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
@@ -139,15 +161,18 @@ jobs:
BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'v2-cloudflare'
ARGOS_BUILD_NAME: v2-cloudflare
visual-testing-customers-v2:
runs-on: ubuntu-latest
name: Visual Testing Customers v2
needs: deploy-v2-vercel
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
@@ -160,15 +185,18 @@ jobs:
BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'customers-v2'
ARGOS_BUILD_NAME: customers-v2-vercel
visual-testing-customers-v2-cloudflare:
runs-on: ubuntu-latest
name: Visual Testing Customers v2 (Cloudflare)
needs: deploy-v2-cloudflare
if: ${{ always() && needs.deploy-v2-cloudflare.result == 'success' }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
@@ -181,22 +209,43 @@ jobs:
BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'customers-v2'
pagespeed-testing-v2:
ARGOS_BUILD_NAME: customers-v2-cloudflare
browserless-testing-v2-vercel:
runs-on: ubuntu-latest
name: PageSpeed Testing v1
name: Browserless Testing v2 (Vercel)
needs: deploy-v2-vercel
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run pagespeed tests
run: bun ./packages/gitbook/tests/pagespeed-testing.ts
- name: Run tests
run: cd ./packages/gitbook && bun e2e-browserless
env:
BASE_URL: ${{needs.deploy-v2-vercel.outputs.deployment-url}}
PAGESPEED_API_KEY: ${{ secrets.PAGESPEED_API_KEY }}
SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/
# browserless-testing-v2-cloudflare:
# runs-on: ubuntu-latest
# name: Browserless Testing v2 (Cloudflare)
# needs: deploy-v2-cloudflare
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Setup Bun
# uses: ./.github/composite/setup-bun
# - name: Install dependencies
# run: bun install --frozen-lockfile
# env:
# PUPPETEER_SKIP_DOWNLOAD: 1
# - name: Run tests
# run: cd ./packages/gitbook && bun e2e-browserless
# env:
# BASE_URL: ${{needs.deploy-v2-cloudflare.outputs.deployment-url}}
# SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
-2
View File
@@ -3,8 +3,6 @@ on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v2-vercel:
name: Deploy v2 to Vercel (production)
-2
View File
@@ -3,8 +3,6 @@ on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v2-vercel:
name: Deploy v2 to Vercel (staging)
+15 -9
View File
@@ -4,9 +4,6 @@ on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
concurrency: ${{ github.workflow }}-${{ github.ref }}
@@ -14,29 +11,38 @@ jobs:
publish:
name: Publish
runs-on: ubuntu-latest
permissions:
id-token: write
pull-requests: write
contents: write
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
registry-url: 'https://registry.npmjs.org'
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Create Release Pull Request or Publish to npm
- name: Create Release Pull Request
id: changesets
uses: changesets/action@v1
with:
publish: npm run release
version: npm run changeset-version
publish: bun run publish-all-packages
version: bun run changeset-version
env:
# Using a PAT instead of GITHUB_TOKEN because we need to run workflows when releases are created
# https://github.com/orgs/community/discussions/26875#discussioncomment-3253761
GITHUB_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+4
View File
@@ -25,3 +25,7 @@ yarn-error.log*
# TypeScript
*.tsbuildinfo
.npmrc
# Bun pack artifacts
packages/*/*.tgz
+1 -1
View File
@@ -1 +1 @@
v20.6
v22.3
+2 -18
View File
@@ -35,7 +35,7 @@ To run a local version of this project, please follow these simple steps.
### Prerequisites
- Node.js (Version: >=20.6)
- Node.js (Version: >= 22.3)
- Use nvm for easy Node management
- [Bun](https://bun.sh/) (Version: >=1.2.15)
- We use a text-based lockfile which isn't supported below 1.2.15
@@ -77,23 +77,7 @@ All pull-requests will be tested against both visual and performances testing to
## Fonts and Icons
GitBook Open uses fontawesome. During development, your local environment will use the free version. However, only the pro version will be accepted by CI. If you see the following error:
```
The GitBook icon is missing. It indicates that the dependencies were installed without the correct font-awesome package. These changes have probably been persisted in the Bun lockfile. Read the README for more information.
```
It means that you've changed the GBO dependencies and bundled in the free version. Only GitBook staff can help with this - if you're not on the GitBook team, please ping us in the PR and we'll help get things moving.
If you are GitBook staff, you'll need our NPM token in your local environment.
```
.env.local
NPM_TOKEN_READONLY=xxx
```
and then reinstall dependencies.
GitBook Open uses fontawesome. For self-hosting and local development, for licensing reasons, only the icons from the free version should be used.
## Contributing
Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 KiB

After

Width:  |  Height:  |  Size: 491 KiB

+1553 -1514
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
[install.scopes]
"gitbook" = { token = "$NPM_TOKEN_READONLY", url = "https://registry.npmjs.org" }
+29 -11
View File
@@ -1,22 +1,27 @@
{
"name": "gitbook",
"version": "0.1.0",
"engines": {
"node": "^22.3.0"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@changesets/cli": "^2.27.12",
"turbo": "^2.5.0",
"vercel": "^39.3.0"
"@changesets/cli": "^2.29.8",
"turbo": "^2.8.6",
"vercel": "^50.15.1"
},
"packageManager": "bun@1.2.15",
"packageManager": "bun@1.3.7",
"overrides": {
"@codemirror/state": "6.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"esbuild": "0.24.2"
},
"private": true,
"scripts": {
"dev": "turbo run dev",
"dev": "turbo run dev --concurrency 20",
"build": "turbo run build",
"clean-deps": "rm -rf node_modules && rm -rf packages/*/node_modules",
"typecheck": "turbo run typecheck",
@@ -26,16 +31,29 @@
"e2e": "turbo run e2e",
"e2e-customers": "turbo run e2e-customers",
"changeset": "changeset",
"changeset-version": "changeset version && bun run format",
"release": "turbo run release && changeset publish",
"changeset-version": "changeset version && bun run format && bun update",
"publish-all-packages": "turbo run publish-to-npm --continue=dependencies-successful",
"download:env": "op read op://gitbook-x-dev/gitbook-open/.env.local >> .env.local",
"clean": "turbo run clean"
},
"workspaces": {
"packages": ["packages/*"],
"catalog": {
"@gitbook/api": "^0.139.0",
"bidc": "^0.0.2"
"@tsconfig/strictest": "^2.0.6",
"@tsconfig/node20": "^20.1.6",
"@gitbook/api": "0.163.0",
"@scalar/api-client-react": "^1.3.46",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"assert-never": "^1.4.0",
"bidc": "^0.0.2",
"bun-types": "^1.1.20",
"classnames": "^2.5.1",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"tsdown": "^0.15.6",
"typescript": "^5.5.3",
"usehooks-ts": "^3.1.1"
}
},
"patchedDependencies": {
+32
View File
@@ -1,5 +1,37 @@
# @gitbook/browser-types
## 0.1.4
### Patch Changes
- 7a11861: Add support for custom cookie banner registration
- Updated dependencies [6f1db32]
- @gitbook/icons@0.4.3
## 0.1.3
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
- Updated dependencies [10995e0]
- @gitbook/icons@0.3.4
## 0.1.2
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
- Updated dependencies [6142d6b]
- @gitbook/icons@0.3.3
## 0.1.1
### Patch Changes
- 295f03d: Republish packages
- Updated dependencies [295f03d]
- @gitbook/icons@0.3.2
## 0.1.0
### Minor Changes
+17 -5
View File
@@ -8,17 +8,29 @@
"default": "./dist/index.js"
}
},
"version": "0.1.0",
"sideEffects": false,
"version": "0.1.4",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:"
},
"devDependencies": {
"typescript": "^5.5.3"
"bun-types": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit"
"build": "tsdown",
"typecheck": "tsc --noEmit",
"dev": "bun run build -- --watch ./src",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "README.md", "CHANGELOG.md"]
"files": ["dist", "README.md", "CHANGELOG.md"],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
+24
View File
@@ -46,6 +46,11 @@ export type GitBookAssistant = {
ui?: boolean;
};
export type GitBookCookieBannerHandler = (options: {
onApprove: () => void;
onReject: () => void;
}) => void;
export type GitBookGlobal = {
/**
* Register an event listener.
@@ -72,6 +77,25 @@ export type GitBookGlobal = {
* Register a custom assistant to be available on the site.
*/
registerAssistant: (assistant: GitBookAssistant) => () => void;
/**
* Register a custom cookie banner handler.
* When registered, the built-in cookie banner will not be displayed.
*/
registerCookieBanner: (handler: GitBookCookieBannerHandler) => void;
/**
* Indicates whether cookies tracking is disabled for the current user.
* Returns `true` when tracking is disabled, `false` when tracking is enabled,
* and `undefined` when the tracking preference is unknown or not yet determined.
*/
isCookiesTrackingDisabled: () => boolean | undefined;
/**
* Indicates whether global privacy control is enabled for the current user.
* Returns `true` when global privacy control is enabled, `false` when it is disabled.
*/
isGlobalPrivacyControlEnabled: () => boolean;
};
declare global {
+18
View File
@@ -1,5 +1,23 @@
# @gitbook/cache-tags
## 0.3.4
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
## 0.3.3
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
## 0.3.2
### Patch Changes
- 295f03d: Republish packages
## 0.3.1
### Patch Changes
+17 -7
View File
@@ -4,22 +4,32 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"version": "0.3.1",
"sideEffects": false,
"version": "0.3.4",
"dependencies": {
"@gitbook/api": "catalog:",
"assert-never": "^1.2.1"
"assert-never": "catalog:"
},
"devDependencies": {
"typescript": "^5.5.3"
"bun-types": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"scripts": {
"build": "tsc",
"build": "tsdown",
"typecheck": "tsc --noEmit",
"dev": "tsc -w"
"dev": "bun run build -- --watch ./src",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
"files": ["dist", "README.md", "CHANGELOG.md"],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
+18
View File
@@ -1,5 +1,23 @@
# @gitbook/colors
## 0.4.3
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
## 0.4.2
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
## 0.4.1
### Patch Changes
- 295f03d: Republish packages
## 0.4.0
### Minor Changes
+16 -6
View File
@@ -4,18 +4,28 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"version": "0.4.0",
"sideEffects": false,
"version": "0.4.3",
"devDependencies": {
"typescript": "^5.5.3"
"bun-types": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"scripts": {
"build": "tsc",
"build": "tsdown",
"typecheck": "tsc --noEmit",
"dev": "tsc -w"
"dev": "bun run build -- --watch ./src",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
"files": ["dist", "README.md", "CHANGELOG.md"],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
+50
View File
@@ -1,5 +1,55 @@
# @gitbook/embed
## 0.2.1
### Patch Changes
- 96e24a1: Fix signed JWT token usage
- 0aa34cc: Fix Embed options in React
- Updated dependencies [6f1db32]
- @gitbook/icons@0.4.3
## 0.2.0
### Minor Changes
- 1430cee: Improve Docs Embed with separate Assistant and Docs tabs
### Patch Changes
- Updated dependencies [62438f4]
- @gitbook/icons@0.4.0
## 0.1.5
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
- Updated dependencies [10995e0]
- @gitbook/icons@0.3.4
## 0.1.4
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
- Updated dependencies [6142d6b]
- @gitbook/icons@0.3.3
## 0.1.3
### Patch Changes
- 295f03d: Republish packages
- Updated dependencies [295f03d]
- @gitbook/icons@0.3.2
## 0.1.2
### Patch Changes
- 7508674: Fix bundling of packages
## 0.1.1
### Patch Changes
+370 -8
View File
@@ -1,24 +1,61 @@
# `@gitbook/embed`
# GitBook Docs Embed (`@gitbook/embed`)
Embed the GitBook Docs Assistant in your product or website.
Embed your GitBook docs in your product or website.
The Docs Embed can contain two tabs:
- **Assistant**: The [GitBook Assistant](https://gitbook.com/docs/publishing-documentation/gitbook-ai-assistant) - an AI-powered chat interface to help users find answers
- **Docs**: A browser for navigating your documentation site
The embed is set up automatically based on your site's configuration. You can optionally customize and override the configuration with custom actions, tools, suggested questions, [Authenticated Access](https://gitbook.com/docs/publishing-documentation/authenticated-access), and more. See the [Configuration](#configuration) section for all available options.
# Usage
## As a script from your docs site
## As a standalone script from your docs site
All GitBook docs site includes a script to easily embed the docs assistant as a widget on your website.
All GitBook docs sites include a script to easily add the Docs Embed as a widget on your website.
The script is served at `https://docs.company.com/~gitbook/embed/script.js`.
You can find the embed script from your docs site settings, or you can copy the following and replace the `docs.company.com` by your docs site hostname.
You can find the embed script from your docs site settings, or you can copy the following and replace `docs.company.com` with your docs site hostname.
```html
<script src="https://docs.company.com/~gitbook/embed/script.js"></script>
<script>
// Initialize with Authenticated Access (optional)
window.GitBook('init',
{ siteURL: 'https://docs.company.com' },
{ visitor: { token: 'your-jwt-token' } }
);
window.GitBook('show');
</script>
```
The standalone script provides a global `GitBook` function. See the [API Reference](#api-reference) section for all available methods.
### Example: Configuring the widget
```javascript
GitBook('configure', {
button: {
label: 'Ask',
icon: 'assistant' // 'assistant' | 'sparkle' | 'help' | 'book'
},
tabs: ['assistant', 'docs'],
actions: [
{
icon: 'circle-question',
label: 'Contact Support',
onClick: () => window.open('https://support.example.com', '_blank')
}
],
greeting: { title: 'Welcome!', subtitle: 'How can I help?' },
suggestions: ['What is GitBook?', 'How do I get started?'],
tools: [/* ... */]
});
```
See the [Configuration](#configuration) section for all available options.
## As a package from NPM
Install the package: `npm install @gitbook/embed` and import it in your web application:
@@ -30,10 +67,46 @@ const gitbook = createGitBook({
siteURL: 'https://docs.company.com'
});
// Create an iframe and get its URL
const iframe = document.createElement('iframe');
iframe.src = gitbook.getFrameURL();
iframe.src = gitbook.getFrameURL({
visitor: {
token: 'your-jwt-token', // Optional: for Adaptive Content or Authenticated Access
unsignedClaims: { // Optional: custom claims for dynamic expressions
userId: '123',
plan: 'premium'
}
}
});
// Create a frame client to communicate with the iframe
const frame = gitbook.createFrame(iframe);
// Use the frame client methods
frame.navigateToPage('/getting-started'); // Navigate to a page in the docs tab
frame.navigateToAssistant(); // Switch to the assistant tab
frame.postUserMessage('How do I get started?');
frame.clearChat();
// Configure the embed (see Configuration section for all options)
frame.configure({
tabs: ['assistant', 'docs'],
actions: [
{
icon: 'circle-question',
label: 'Contact Support',
onClick: () => window.open('https://support.example.com', '_blank')
}
],
greeting: { title: 'Welcome!', subtitle: 'How can I help?' },
suggestions: ['What is GitBook?', 'How do I get started?'],
tools: [/* ... */]
});
// Listen to events
frame.on('close', () => {
console.log('Frame closed');
});
```
## As React components
@@ -41,9 +114,298 @@ const frame = gitbook.createFrame(iframe);
After installing the NPM package, you can import prebuilt React components:
```tsx
import { GitBookProvider, GitBookAssistantFrame } from '@gitbook/embed/react';
import { GitBookProvider, GitBookFrame } from '@gitbook/embed/react';
<GitBookProvider siteURL="https://docs.company.com">
<GitBookAssistantFrame />
<GitBookFrame
visitor={{
token: 'your-jwt-token', // Optional: for Adaptive Content or Authenticated Access
unsignedClaims: { userId: '123' } // Optional: custom claims for dynamic expressions
}}
tabs={['assistant', 'docs']}
greeting={{ title: 'Welcome!', subtitle: 'How can I help?' }}
suggestions={['What is GitBook?', 'How do I get started?']}
actions={[
{
icon: 'circle-question',
label: 'Contact Support',
onClick: () => window.open('https://support.example.com', '_blank')
}
]}
tools={[/* ... */]}
/>
</GitBookProvider>
```
You can also use the `useGitBook` hook to access the client:
```tsx
import { useGitBook } from '@gitbook/embed/react';
function MyComponent() {
const gitbook = useGitBook();
const frameURL = gitbook.getFrameURL({ visitor: { token: '...' } });
// ...
}
```
# API Reference
## Method Comparison
| Method | Standalone Script | NPM Package | React Components |
|--------|------------------|-------------|------------------|
| **Initialize** | `GitBook('init', options, frameOptions)` | `createGitBook(options)` | `<GitBookProvider siteURL="...">` |
| **Get frame URL** | ❌ (handled internally) | `client.getFrameURL(options)` | `useGitBook().getFrameURL(options)` |
| **Create frame client** | ❌ (handled internally) | `client.createFrame(iframe)` | `useGitBook().createFrame(iframe)` |
| **Show/Hide widget** | `GitBook('show')` / `GitBook('hide')` | ❌ | ❌ |
| **Open/Close window** | `GitBook('open')` / `GitBook('close')` / `GitBook('toggle')` | ❌ | ❌ |
| **Navigate to page** | `GitBook('navigateToPage', path)` | `frame.navigateToPage(path)` | Via frame client |
| **Navigate to assistant** | `GitBook('navigateToAssistant')` | `frame.navigateToAssistant()` | Via frame client |
| **Post message** | `GitBook('postUserMessage', message)` | `frame.postUserMessage(message)` | Via frame client |
| **Clear chat** | `GitBook('clearChat')` | `frame.clearChat()` | Via frame client |
| **Configure** | `GitBook('configure', settings)` | `frame.configure(settings)` | Props on `<GitBookFrame>` |
| **Event listeners** | ❌ | `frame.on(event, listener)` | Via frame client |
| **Unload** | `GitBook('unload')` | ❌ | ❌ |
## Method Signatures
### Standalone Script
- `GitBook('init', options: { siteURL: string }, frameOptions?: { visitor?: {...} })` - Initialize widget
- `GitBook('show')` - Show widget button
- `GitBook('hide')` - Hide widget button
- `GitBook('open')` - Open widget window
- `GitBook('close')` - Close widget window
- `GitBook('toggle')` - Toggle widget window
- `GitBook('navigateToPage', path: string)` - Navigate to page
- `GitBook('navigateToAssistant')` - Navigate to assistant tab
- `GitBook('postUserMessage', message: string)` - Post message to chat
- `GitBook('clearChat')` - Clear chat history
- `GitBook('configure', settings: {...})` - Configure widget
- `GitBook('unload')` - Unload widget
### NPM Package
**Client Factory:**
- `createGitBook(options: { siteURL: string })``GitBookClient`
- `client.getFrameURL(options?: { visitor?: {...} })``string`
- `client.createFrame(iframe: HTMLIFrameElement)``GitBookFrameClient`
**Frame Client:**
- `frame.navigateToPage(path: string)``void`
- `frame.navigateToAssistant()``void`
- `frame.postUserMessage(message: string)``void`
- `frame.clearChat()``void`
- `frame.configure(settings: Partial<GitBookEmbeddableConfiguration>)``void`
- `frame.on(event: string, listener: Function)``() => void` (unsubscribe)
### React Components
**Components:**
- `<GitBookProvider siteURL: string>` - Provider component
- `<GitBookFrame {...props}>` - Frame component (accepts all config options as props)
**Hooks:**
- `useGitBook()``GitBookClient` (must be used within `<GitBookProvider>`)
# Configuration
Configuration options are available across usage methods as follows:
- **Standalone script**: via `GitBook('configure', {...})`
- **NPM package**: via `frame.configure({...})`
- **React components**: via props on `<GitBookFrame>`
### `tabs`
Available in: Standalone script, NPM package, React components
Override which tabs are displayed. Defaults to your site's configuration.
- **Type**: `('assistant' | 'docs')[]`
- **Options**:
- `['assistant', 'docs']` - Show both tabs
- `['assistant']` - Show only the assistant tab
- `['docs']` - Show only the docs tab
```javascript
tabs: ['assistant', 'docs']
```
### `actions`
Available in: Standalone script, NPM package, React components
Custom action buttons rendered in the sidebar alongside tabs. Each action button triggers a callback when clicked.
**Note**: This prop was previously named `buttons`. Use `actions` instead, it has the same functionality.
- **Type**: `GitBookEmbeddableActionDefinition[]`
- **Properties**:
- `icon`: `string` - Icon name. Any [FontAwesome icon](https://fontawesome.com/search) is supported. (e.g., `'rocket'`, `'comments'`, `'user-circle'`, ...)
- `label`: `string` - Button label text
- `onClick`: `() => void | Promise<void>` - Callback function when clicked
```javascript
actions: [
{
icon: 'comments',
label: 'Contact Support',
onClick: () => window.open('https://support.example.com', '_blank')
},
{
icon: 'rocket',
label: 'Get started',
onClick: () => {
GitBook('navigateToPage', '/getting-started');
}
}
]
```
### `greeting`
Available in: Standalone script, NPM package, React components
Welcome message displayed in the [Assistant](https://gitbook.com/docs/publishing-documentation/gitbook-ai-assistant) tab.
- **Type**: `{ title: string, subtitle: string }`
```javascript
greeting: {
title: 'Welcome!',
subtitle: 'How can I help you today?'
}
```
### `suggestions`
Available in: Standalone script, NPM package, React components
Suggested questions displayed in the [Assistant](https://gitbook.com/docs/publishing-documentation/gitbook-ai-assistant) welcome screen.
- **Type**: `string[]`
```javascript
suggestions: [
'What is GitBook?',
'How do I get started?',
'What can you do?'
]
```
### `tools`
Available in: Standalone script, NPM package, React components
Custom AI tools to extend the [Assistant](https://gitbook.com/docs/publishing-documentation/gitbook-ai-assistant). Tools allow the assistant to execute functions and integrate with your own systems.
**Note**: In addition to custom tools, the Assistant will always have access to any [MCP servers you define](https://gitbook.com/docs/publishing-documentation/gitbook-ai-assistant#extend-gitbook-assistant-with-mcp-servers) in your site's AI settings.
- **Type**: `GitBookToolDefinition[]`
- **Properties**:
- `name`: `string` - Unique tool identifier
- `description`: `string` - Description of what the tool does (used by the AI to decide when and how to use it).
- `inputSchema`: `object` - JSON schema defining the tool's input parameters
- `execute`: `(input: object) => Promise<{ output: any, summary: string }>` - Async function that executes the tool.
- `output`: The result of the tool execution, provided to the AI to continue working with. Not shown to the user.
- `summary`: The visual summary of the tool execution, shown in the user's chat window.
- `confirmation`: `{ icon?: string, label: string }` (optional) - Confirmation button shown before execution, useful for actions that require the user's express approval.
```javascript
tools: [
{
name: 'get_user_info',
description: 'Get information about the current user',
inputSchema: {
type: 'object',
properties: {
userId: {
type: 'string',
description: 'The user ID to look up'
}
},
required: ['userId']
},
execute: async (input) => {
const user = await fetch(`/api/users/${input.userId}`).then(r => r.json());
return {
output: { name: user.name, plan: user.plan },
summary: `Retrieved info for user ${user.name}`
};
}
},
{
name: 'create_ticket',
description: 'Create a support ticket',
confirmation: {
icon: 'circle-question',
label: 'Create support ticket?'
},
inputSchema: {
type: 'object',
properties: {
subject: { type: 'string' },
description: { type: 'string' }
},
required: ['subject', 'description']
},
execute: async (input) => {
const ticket = await fetch('/api/tickets', {
method: 'POST',
body: JSON.stringify(input)
}).then(r => r.json());
return {
output: { ticketId: ticket.id },
summary: `Created ticket #${ticket.id}`
};
}
}
]
```
### `visitor` (Authenticated Access)
Available in: Standalone script (via `init`), NPM package (via `getFrameURL()`), React components (as prop)
[Authenticated Access](https://gitbook.com/docs/publishing-documentation/authenticated-access) options passed when creating the frame URL. Used for [Adaptive Content](https://gitbook.com/docs/publishing-documentation/adaptive-content) and [Authenticated Access](https://gitbook.com/docs/publishing-documentation/authenticated-access).
**Note**: This is not a configuration option but rather a parameter when initializing the frame or creating the frame URL.
**Standalone script**: Pass as the second argument to `GitBook('init', options, frameOptions)`
**NPM package**: Pass to `getFrameURL({ visitor: {...} })`
**React components**: Pass as the `visitor` prop on `<GitBookFrame>`
- **Type**: `{ token?: string, unsignedClaims?: Record<string, unknown> }`
- **Properties**:
- `token`: `string` (optional) - Signed JWT token for [Adaptive Content](https://gitbook.com/docs/publishing-documentation/adaptive-content) or [Authenticated Access](https://gitbook.com/docs/publishing-documentation/authenticated-access)
- `unsignedClaims`: `Record<string, unknown>` (optional) - Unsigned claims that can be used in dynamic expressions via `visitor.claims.unsigned.<claim-name>`
```javascript
visitor: {
token: 'your-jwt-token',
unsignedClaims: {
userId: '123',
plan: 'premium',
role: 'admin'
}
}
```
### `button`
Available in: Standalone script only
Configure the widget button for the standalone script. This option is not available when using the NPM package or React components, since they can be customized completely.
- **Type**: `{ label: string, icon: 'assistant' | 'sparkle' | 'help' | 'book' }`
- **Properties**:
- `label`: `string` - Button label text
- `icon`: `'assistant' | 'sparkle' | 'help' | 'book'` - Icon displayed on the button. Choose from one of 4 presets.
```javascript
button: {
label: 'Ask',
icon: 'assistant'
}
```
+29 -10
View File
@@ -5,27 +5,46 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js",
"standalone": "./dist/standalone/index.js",
"react": "./dist/react/index.js"
"default": "./dist/index.js"
},
"./react": {
"types": "./dist/react/index.d.ts",
"default": "./dist/react/index.js"
}
},
"version": "0.1.1",
"sideEffects": false,
"version": "0.2.1",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:",
"bidc": "catalog:"
},
"peerDependencies": {
"react": "^18.0.0"
"react": "*"
},
"devDependencies": {
"typescript": "^5.5.3",
"react": "^19.0.0"
"bun-types": "catalog:",
"@types/react": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"react": "catalog:"
},
"scripts": {
"build": "tsc && bun build src/standalone/index.ts --bundle --minify --outdir=standalone",
"typecheck": "tsc --noEmit"
"build": "bun run build-lib && bun run build-standalone",
"build-lib": "tsdown",
"build-standalone": "bun build src/standalone/index.ts --bundle --minify --outdir=standalone",
"clean": "rm -rf ./dist",
"unit": "bun test",
"typecheck": "tsc --noEmit",
"dev": "bun run build -- --watch ./src",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "README.md", "CHANGELOG.md", "standalone"]
"files": ["dist", "README.md", "CHANGELOG.md"],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'bun:test';
import { createGitBook } from './createGitBook';
describe('createGitBook.getFrameURL', () => {
it('builds the embed URL when the site URL has no trailing slash', () => {
const client = createGitBook({ siteURL: 'https://example.com/docs' });
const url = new URL(client.getFrameURL({}));
expect(url.origin).toBe('https://example.com');
expect(url.pathname).toBe('/docs/~gitbook/embed');
expect(url.searchParams.toString()).toBe('');
});
it('handles a trailing slash and adds visitor parameters', () => {
const client = createGitBook({ siteURL: 'https://example.com/docs/' });
const url = new URL(
client.getFrameURL({
visitor: {
token: 'signed-token',
unsignedClaims: {
role: 'editor',
count: 3,
enabled: false,
},
},
})
);
expect(url.pathname).toBe('/docs/~gitbook/embed');
expect(url.searchParams.get('jwt_token')).toBe('signed-token');
expect(url.searchParams.get('visitor.role')).toBe('editor');
expect(url.searchParams.get('visitor.count')).toBe('3');
expect(url.searchParams.get('visitor.enabled')).toBe('false');
});
});
+2 -2
View File
@@ -40,10 +40,10 @@ export function createGitBook(options: CreateGitBookOptions) {
const client: GitBookClient = {
getFrameURL: (frameOptions) => {
const url = new URL(options.siteURL);
url.pathname = `${url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`}~gitbook/embed/assistant`;
url.pathname = `${url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`}~gitbook/embed`;
if (frameOptions.visitor?.token) {
url.searchParams.set('token', frameOptions.visitor.token);
url.searchParams.set('jwt_token', frameOptions.visitor.token);
}
if (frameOptions.visitor?.unsignedClaims) {
@@ -64,8 +64,9 @@ export function createGitBookFrame(iframe: HTMLIFrameElement): GitBookFrameClien
const events = new Map<string, Array<(...args: any[]) => void>>();
const configuration: GitBookEmbeddableConfiguration = {
buttons: [],
welcomeMessage: '',
tabs: ['assistant', 'docs'],
actions: [],
greeting: { title: '', subtitle: '' },
suggestions: [],
tools: [],
};
+15 -5
View File
@@ -23,7 +23,7 @@ export type GitBookToolDefinition = AIToolDefinition & {
/**
* Custom button definition to be passed to the embeddable GitBook.
*/
export type GitBookEmbeddableButtonDefinition = {
export type GitBookEmbeddableActionDefinition = {
/**
* Icon to be displayed in the button.
*/
@@ -41,16 +41,26 @@ export type GitBookEmbeddableButtonDefinition = {
};
/**
* Overall configuration for the layout of the embeddable GitBook.
* Overall configuration for the layout of the GitBook embed.
*/
export type GitBookEmbeddableConfiguration = {
/** Tabs to display in the embed (if enabled on the site). */
tabs: ('assistant' | 'docs')[];
/** Additional buttons to be displayed in the header of the GitBook embed. */
actions: GitBookEmbeddableActionDefinition[];
/**
* Buttons to be displayed in the header of the embeddable GitBook.
* Additional buttons to be displayed in the header of the GitBook embed.
* @deprecated Use `actions` instead.
*/
buttons: GitBookEmbeddableButtonDefinition[];
buttons?: GitBookEmbeddableActionDefinition[];
/** Message to be displayed in the welcome page. */
welcomeMessage: string;
greeting: {
title: string;
subtitle: string;
};
/** Suggestions of questions to be displayed in the welcome page. */
suggestions: string[];
+1
View File
@@ -1 +1,2 @@
export * from './client';
export * from './standalone';
+22 -11
View File
@@ -1,4 +1,6 @@
import React from 'react';
'use client';
import { useEffect, useMemo, useRef, useState } from 'react';
import type {
GetFrameURLOptions,
GitBookEmbeddableConfiguration,
@@ -9,34 +11,43 @@ import { useGitBook } from './GitBookProvider';
export type GitBookFrameProps = {
className?: string;
} & GetFrameURLOptions &
GitBookEmbeddableConfiguration;
Partial<GitBookEmbeddableConfiguration>;
/**
* Render a frame with the GitBook Assistant in it.
*/
export function GitBookFrame(props: GitBookFrameProps) {
const { className, visitor, buttons, welcomeMessage, suggestions, tools } = props;
const {
className,
visitor,
actions = [],
greeting,
suggestions = [],
tools = [],
tabs = ['assistant', 'docs'],
} = props;
const frameRef = React.useRef<HTMLIFrameElement>(null);
const frameRef = useRef<HTMLIFrameElement>(null);
const gitbook = useGitBook();
const [gitbookFrame, setGitbookFrame] = React.useState<GitBookFrameClient | null>(null);
const [gitbookFrame, setGitbookFrame] = useState<GitBookFrameClient | null>(null);
const frameURL = React.useMemo(() => gitbook.getFrameURL({ visitor }), [gitbook, visitor]);
const frameURL = useMemo(() => gitbook.getFrameURL({ visitor }), [gitbook, visitor]);
React.useEffect(() => {
useEffect(() => {
if (frameRef.current) {
setGitbookFrame(gitbook.createFrame(frameRef.current));
}
}, [gitbook]);
React.useEffect(() => {
useEffect(() => {
gitbookFrame?.configure({
buttons,
welcomeMessage,
tabs,
actions,
greeting,
suggestions,
tools,
});
}, [gitbookFrame, buttons, welcomeMessage, suggestions, tools]);
}, [gitbookFrame, actions, greeting, suggestions, tools, tabs]);
return (
<iframe
+47 -30
View File
@@ -31,24 +31,50 @@ type StandaloneCalls =
// Clear the chat
| ['clearChat']
// Configure the embed
| ['configure', Partial<GitBookEmbeddableConfiguration>]
| ['configure', Partial<GitBookEmbeddableConfiguration & StandaloneConfiguration>]
// Navigate to a page
| ['navigateToPage', string]
// Navigate to the assistant
| ['navigateToAssistant'];
type StandaloneConfiguration = {
/** Configure the button to open the embed */
button: {
/** Label to be displayed in the button. */
label: string;
/** Icon to be displayed in the button. */
icon: 'assistant' | 'sparkle' | 'help' | 'book';
};
};
export type GitBookStandalone = ((...args: StandaloneCalls) => void) & {
q?: StandaloneCalls[];
};
let widgetIframe: HTMLIFrameElement | undefined;
let _client: GitBookClient | undefined;
let _frame: GitBookFrameClient | undefined;
let frameOptions: GetFrameURLOptions | undefined;
let frameConfiguration: GitBookEmbeddableConfiguration & StandaloneConfiguration = {
button: {
label: 'Ask',
icon: 'assistant',
},
actions: [],
greeting: { title: '', subtitle: '' },
suggestions: [],
tools: [],
tabs: ['assistant', 'docs'],
};
const widgetButton = document.createElement('button');
widgetButton.id = 'gitbook-widget-button';
widgetButton.addEventListener('click', () => {
GitBook('toggle');
});
widgetButton.innerHTML = `
<span id="gitbook-widget-button-icon"></span>
<span id="gitbook-widget-button-label">Ask</span>
<span id="gitbook-widget-button-icon" data-icon="${frameConfiguration.button.icon}"></span>
<span id="gitbook-widget-button-label">${frameConfiguration.button.label}</span>
`;
const widgetWindow = document.createElement('div');
@@ -58,17 +84,6 @@ widgetWindow.classList.add('hidden');
document.body.appendChild(widgetButton);
document.body.appendChild(widgetWindow);
let widgetIframe: HTMLIFrameElement | undefined;
let _client: GitBookClient | undefined;
let _frame: GitBookFrameClient | undefined;
let frameOptions: GetFrameURLOptions | undefined;
let frameConfiguration: GitBookEmbeddableConfiguration = {
buttons: [],
welcomeMessage: '',
suggestions: [],
tools: [],
};
function getClient() {
if (!_client) {
throw new Error(
@@ -135,27 +150,31 @@ const GitBook = (...args: StandaloneCalls) => {
case 'postUserMessage':
getIframe().frame.postUserMessage(args[1]);
break;
case 'configure':
case 'configure': {
const settings = args[1];
frameConfiguration = {
...frameConfiguration,
...args[1],
...settings,
};
// Update the button label and icon
if (settings.button?.label) {
const label = widgetButton.querySelector('#gitbook-widget-button-label');
if (label) {
label.textContent = settings.button.label;
}
}
if (settings.button?.icon) {
const icon = widgetButton.querySelector('#gitbook-widget-button-icon');
if (icon) {
icon.setAttribute('data-icon', settings.button.icon);
}
}
getIframe().frame.configure({
...frameConfiguration,
buttons: [
...frameConfiguration.buttons,
// Always include a close button
{
icon: 'close',
label: 'Close',
onClick: () => {
GitBook('close');
},
},
],
});
break;
}
case 'clearChat':
getIframe().frame.clearChat();
break;
@@ -174,5 +193,3 @@ const precalls = (window.GitBook as GitBookStandalone | undefined)?.q ?? [];
// @ts-expect-error - GitBook is not defined in the global scope
window.GitBook = GitBook;
precalls.forEach((call) => GitBook(...call));
GitBook('configure', {});
+24 -1
View File
@@ -29,6 +29,16 @@
--gitbook-widget-easing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (prefers-color-scheme: dark) {
:root {
--gitbook-widget-text-color: #FFFFFF;
--gitbook-widget-border-color: #202020;
--gitbook-widget-background-translucent: rgba(15, 15, 15, 0.9);
--gitbook-widget-background-translucent-hover: rgba(20, 20, 20, 0.9);
--gitbook-widget-background-solid: #f0f0f0;
}
}
* {
box-sizing: border-box;
}
@@ -103,6 +113,18 @@
background-color: currentColor;
}
#gitbook-widget-button-icon[data-icon="sparkle"] {
mask-image: url("https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/sparkle.svg?v=2&token=a463935e93");
}
#gitbook-widget-button-icon[data-icon="help"] {
mask-image: url("https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/circle-question.svg?v=2&token=a463935e93");
}
#gitbook-widget-button-icon[data-icon="book"] {
mask-image: url("https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/book-open.svg?v=2&token=a463935e93");
}
#gitbook-widget-button.open #gitbook-widget-button-icon {
mask-image: url('https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/close.svg?v=2&token=a463935e93');
}
@@ -120,7 +142,8 @@
z-index: 9998;
width: calc(min(var(--gitbook-widget-window-width), calc(100vw - var(--gitbook-widget-right) - var(--gitbook-widget-left))));
height: calc(min(var(--gitbook-widget-window-height), calc(100vh - var(--gitbook-widget-window-bottom) - var(--gitbook-widget-top))));
background-color: var(--gitbook-widget-background-solid);
background-color: var(--gitbook-widget-background-translucent);
backdrop-filter: blur(48px);
border: 1px solid var(--gitbook-widget-border-color);
border-radius: var(--gitbook-widget-radius);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+3 -1
View File
@@ -15,7 +15,9 @@
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": []
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
+13
View File
@@ -0,0 +1,13 @@
import { defineConfig } from 'tsdown';
export default defineConfig([
{
entry: 'src/index.ts',
outDir: 'dist',
},
{
entry: 'src/react/index.ts',
outDir: 'dist/react',
unbundle: true,
},
]);
+14 -2
View File
@@ -1,13 +1,25 @@
# @gitbook/emoji-codepoints
## 0.2.2
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
## 0.2.1
### Patch Changes
- 295f03d: Republish packages
## 0.2.0
### Minor Changes
- 57adb3e: Second release to fix publishing with changeset
- 57adb3e: Second release to fix publishing with changeset
## 0.1.0
### Minor Changes
- 5f8a8fe: Initial release
- 5f8a8fe: Initial release
+3 -3
View File
@@ -1,12 +1,12 @@
{
"name": "@gitbook/emoji-codepoints",
"description": "Optimized mapping of codepoints to the fully qualified emoji codepoints",
"version": "0.2.0",
"version": "0.2.2",
"private": true,
"exports": "./dist/index.ts",
"dependencies": {},
"sideEffects": false,
"devDependencies": {
"emoji-assets": "^8.0.0"
"emoji-assets": "^9.0.0"
},
"scripts": {
"generate": "bun ./build.ts",
+47
View File
@@ -1,5 +1,52 @@
# @gitbook/expr
## 1.2.4
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
- 10995e0: Use NPM dependency for eval-estree-expression
## 1.2.3
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
## 1.2.2
### Patch Changes
- 295f03d: Republish packages
## 1.2.1
### Patch Changes
- a629900: Add dev script for @gitbook/expr
## 1.2.0
### Minor Changes
- 6da3655: Fix exports in gitbook/expr package.json
## 1.1.1
### Patch Changes
- 3548fa6: Fix eval-estree-expr named import
## 1.1.0
### Minor Changes
- e1ff17e: Fix bundling of gitbook/expr package
### Patch Changes
- 8ff1e3b: Add support for every/some array methods
## 1.0.0
### Major Changes
+25 -12
View File
@@ -1,34 +1,47 @@
{
"name": "@gitbook/expr",
"description": "Safely evaluate & parse user-defined GitBook expressions.",
"version": "1.0.0",
"version": "1.2.4",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"sideEffects": false,
"dependencies": {
"eval-estree-expression": "^2.0.3",
"acorn": "^8.14.0",
"acorn-loose": "8.4.0",
"acorn": "^8.15.0",
"acorn-loose": "^8.5.2",
"acorn-walk": "^8.3.4",
"assert-never": "^1.2.1"
"assert-never": "catalog:",
"escodegen": "^2.1.0",
"eval-estree-expression": "github:jonschlinkert/eval-estree-expression#fb0246a"
},
"devDependencies": {
"bun-types": "^1.1.20",
"@tsconfig/strictest": "catalog:",
"@tsconfig/node20": "catalog:",
"@types/escodegen": "^0.0.10",
"@types/estree": "^1.0.6",
"@babel/types": "^7.26.0",
"@types/json-schema": "^7.0.15"
"@types/json-schema": "^7.0.15",
"bun-types": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"build": "tsdown",
"typecheck": "tsc --noEmit",
"unit": "bun test",
"clean": "rm -rf ./dist"
"clean": "rm -rf ./dist",
"dev": "bun run build -- --watch ./src",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
"files": ["dist", "README.md", "CHANGELOG.md"],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
@@ -210,6 +210,8 @@ describe('autocomplete', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
@@ -268,6 +270,8 @@ describe('autocomplete', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
+20 -2
View File
@@ -42,11 +42,29 @@ describe('ExpressionRuntime', () => {
},
expectedResult: true,
},
{
scenario: 'array method',
condition: 'reviews.every(review => !!review.status)',
inputs: { reviews: [{ status: 'approved' }, { status: 'approved' }] },
expectedResult: true,
},
{
scenario: 'array every',
condition: 'reviews.every(review => review.status === "approved")',
inputs: { reviews: [{ status: 'approved' }, { status: 'approved' }] },
expectedResult: true,
},
{
scenario: 'array map',
condition: '[1, 2, 3].map(n => n * x)',
inputs: { x: 2 },
expectedResult: [2, 4, 6],
},
])(
'should properly evaluate/safeEvaluate a valid conditional expression: $scenario',
({ condition, inputs, expectedResult }) => {
expect(runtime.evaluate(condition, inputs)).toBe(expectedResult);
expect(runtime.safeEvaluate(condition, inputs).value).toBe(expectedResult);
expect(runtime.evaluate(condition, inputs)).toEqual(expectedResult);
expect(runtime.safeEvaluate(condition, inputs).value).toEqual(expectedResult);
}
);
-1
View File
@@ -1,6 +1,5 @@
export * from './errors';
export * from './input-values';
export * from './input-values';
export * from './runtime';
export * from './symbols';
export * from './template';
+4 -1
View File
@@ -9,7 +9,9 @@ import {
tokenizer,
} from 'acorn';
import { parse as parseLoose } from 'acorn-loose';
import { evaluate } from 'eval-estree-expression';
import escodegen from 'escodegen';
import evalESTreeExpr from 'eval-estree-expression';
const { evaluate } = evalESTreeExpr;
import { AutoComplete } from './autocomplete';
import { ExpressionError } from './errors';
@@ -49,6 +51,7 @@ export class ExpressionRuntime {
return evaluate.sync<Expression>(parsed.result, inputs, {
functions: true,
withMembers: true,
generate: escodegen.generate,
});
} catch (error) {
throw error instanceof Error
@@ -338,6 +338,8 @@ describe('ExpressionRuntime', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
});
});
+54
View File
@@ -259,6 +259,60 @@ const StandardLibrary: Partial<
'true if the value searchElement is found within the array (or the part of the array indicated by the index fromIndex, if specified).',
}),
}),
SymbolFunction({
name: 'some',
description:
'Tests whether at least one element in the array passes the test implemented by the provided function.',
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some',
args: [
SymbolFunction({
name: 'callback',
description: 'A function that tests each element of the array.',
args: [
{
...arraySymbolDef.items,
name: 'element',
description: 'The current element being processed in the array.',
},
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for at least one element in the array.',
}),
}),
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for at least one element in the array.',
}),
}),
SymbolFunction({
name: 'every',
description:
'Tests whether all elements in the array pass the test implemented by the provided function.',
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every',
args: [
SymbolFunction({
name: 'callback',
description: 'A function that tests each element of the array.',
args: [
{
...arraySymbolDef.items,
name: 'element',
description: 'The current element being processed in the array.',
},
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for all elements in the array.',
}),
}),
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for all elements in the array.',
}),
}),
],
}),
};
-10
View File
@@ -1,10 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": ["./tsconfig.json"],
"exclude": ["**/*.test.ts"],
"compilerOptions": {
"declaration": true,
"noEmit": false,
"outDir": "dist"
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ declare module 'eval-estree-expression' {
/**
* Enable support for function statements and expressions by enabling the functions option AND by passing the .generate() function from the escodegen library. Default: undefined
*/
generate?: boolean;
generate?: boolean | ((node: any) => string);
/**
* Enable the =~ regex operator to support testing values without using functions (example name =~ /^a.*c$/). Default: true
*/
+18
View File
@@ -1,5 +1,23 @@
# @gitbook/fonts
## 0.1.3
### Patch Changes
- 10995e0: Use NPM Trusted publishing for publishing the package.
## 0.1.2
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
## 0.1.1
### Patch Changes
- 295f03d: Republish packages
## 0.1.0
### Minor Changes
+18 -8
View File
@@ -4,28 +4,38 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"version": "0.1.0",
"sideEffects": false,
"version": "0.1.3",
"dependencies": {
"@gitbook/api": "catalog:"
},
"devDependencies": {
"google-font-metadata": "^6.0.3",
"typescript": "^5.5.3"
"bun-types": "catalog:",
"google-font-metadata": "^6.0.7",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"scripts": {
"generate": "bun ./bin/generate.js",
"build": "tsc --project tsconfig.build.json",
"build": "tsdown",
"typecheck": "tsc --noEmit",
"dev": "tsc -w",
"dev": "bun run build -- --watch ./src",
"clean": "rm -rf ./dist && rm -rf ./src/data",
"unit": "bun test"
"unit": "bun test",
"publish-to-npm": "../../scripts/publish-if-new.sh"
},
"files": ["dist", "src", "bin", "README.md", "CHANGELOG.md"],
"files": ["dist", "bin", "README.md", "CHANGELOG.md"],
"engines": {
"node": ">=20.0.0"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"url": "https://github.com/GitbookIO/gitbook"
}
}
@@ -1,57 +1,57 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
exports[`getDefaultFont should return correct object for Latin text 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
}
`;
exports[`getDefaultFont should return correct object for Cyrillic text 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff",
}
`;
exports[`getDefaultFont should return correct object for Greek text 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZxhjp-Ek-_0ewmM.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZxhjp-Ek-_0ewmM.woff",
}
`;
exports[`getDefaultFont should handle mixed script text 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff",
}
`;
exports[`getDefaultFont should handle different font weights: regular 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
}
`;
exports[`getDefaultFont should handle different font weights: bold 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuFuYAZ9hjp-Ek-_0ew.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuFuYAZ9hjp-Ek-_0ew.woff",
}
`;
exports[`getDefaultFont should handle different fonts: inter 1`] = `
{
"font": "Inter",
"url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
"url": "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff",
}
`;
exports[`getDefaultFont should handle different fonts: roboto 1`] = `
{
"font": "Roboto",
"url": "https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxMKTU1Kg.woff",
"url": "https://fonts.gstatic.com/s/roboto/v49/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAz0klQm_20.woff",
}
`;
-4
View File
@@ -1,4 +0,0 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "src/**/*.test.ts"]
}
+284
View File
@@ -1,5 +1,289 @@
# gitbook
## 0.22.0
### Minor Changes
- e00d1c5: Support code themes customization
- f294818: Serve a markdown version of the page when the "Accept" header contains "text/markdown".
### Patch Changes
- 9d6457f: OpenAPI styling tweaks
- d738e11: Prevent form submission during IME composition
- 1e53376: Add sidesheet component, use it for TOC and AIChat
- f7f6f9d: Set <html lang> based on translated language
- 89de966: Support social links
- b53cb1d: Align styling of PageLinkItem and PageDocumentItem
- 27a34b7: Support `defaultExpanded` in Expandable block
- a5ef44a: Better conditional display of page actions
- 07c02eb: Header & TOC tweaks
- 29e2b22: Fix ordered list item index calculation
- f6cf62a: Standardise toggle chevrons across the app
- b57561d: Fix rendering of images in reusable content from an external space with the new dereferencing in the API.
- e172b25: Grow InlineActionButton to take available width on line
- 7a11861: Add support for custom cookie banner registration
- 8f32f4f: Better handle huge section groups
- 0aa34cc: Fix Embed options in React
- Updated dependencies [e00d1c5]
- Updated dependencies [96e24a1]
- Updated dependencies [4e2d863]
- Updated dependencies [d3f6c38]
- Updated dependencies [3ba9e46]
- Updated dependencies [717ad0b]
- Updated dependencies [6f1db32]
- Updated dependencies [7a11861]
- Updated dependencies [0aa34cc]
- Updated dependencies [48ca21e]
- @gitbook/react-openapi@1.5.7
- @gitbook/embed@0.2.1
- @gitbook/openapi-parser@3.0.8
- @gitbook/icons@0.4.3
- @gitbook/browser-types@0.1.4
- @gitbook/react-contentkit@0.7.12
## 0.21.3
### Patch Changes
- Updated dependencies [bf63045]
- @gitbook/react-openapi@1.5.6
## 0.21.2
### Patch Changes
- ca68236: Fix 404 when accessing rss.xml for the root page.
- 21851d3: Round page cover corners on extrawide screens
- 695e99f: Update button sizing
- 7517e98: Clean up button and input across the app
- 99d788e: Update page cover styling
- ebf3b2b: Fix an issue where content refs to spaces outside of site was resolving with share links
- 35efc9d: Fix TOCPageIcons backgrounds on gradient and bold themes
- a7f58ab: Fix embed tab button size regression
- 360b525: Switch icon masking method to fix visual cutoffs in FA7
- Updated dependencies [985c1b4]
- Updated dependencies [360b525]
- @gitbook/openapi-parser@3.0.7
- @gitbook/icons@0.4.2
- @gitbook/react-openapi@1.5.5
- @gitbook/react-contentkit@0.7.11
## 0.21.1
### Patch Changes
- Updated dependencies [e7fb771]
- @gitbook/icons@0.4.1
- @gitbook/react-contentkit@0.7.10
## 0.21.0
### Minor Changes
- 3553db0: Add a RSS feed to all pages with update blocks.
### Patch Changes
- f478ddc: Add Input component
- 3817289: Fix overflowing section groups
- 86e7aa6: Fix search results cursor
- 1430cee: Improve Docs Embed with separate Assistant and Docs tabs
- ed9e8fe: Add support for button actions
- 9c69cc4: Add customizable suggested questions
- c51076e: Fix links to other spaces and root page in embeddable view.
- 6278a18: Support `greeting` and fix suggested questions if there are no custom ones defined
- 04a4175: Remove hidden site spaces from navigation
- 74f8931: Fix hidden section not found
- 344842f: Improve OpenAPI circular references
- Updated dependencies [4766092]
- Updated dependencies [8761cee]
- Updated dependencies [461e15f]
- Updated dependencies [1430cee]
- Updated dependencies [62438f4]
- Updated dependencies [3e40b4d]
- Updated dependencies [87d68ea]
- Updated dependencies [344842f]
- @gitbook/react-openapi@1.5.4
- @gitbook/openapi-parser@3.0.6
- @gitbook/embed@0.2.0
- @gitbook/icons@0.4.0
- @gitbook/react-contentkit@0.7.9
## 0.20.0
### Minor Changes
- 1d92dc1: Update site layout and blocks with container queries and better transitions
### Patch Changes
- 8fdc5c8: Fix webframe height issue
- 9022d87: Add breadcrumbs to search results
- 0c01832: SearchInput fixes — Translate Clear button, Bold theme color, "Esc" keyboard shortcut
- 1d1f0a7: Fix search container shadow
- 2aa4f2e: Update dropdown menu styling
- a49781e: Printable collapsed code block
- 7d6a316: Revert contentref anchor title
- e27986b: Reword "Everywhere" search scope to "All docs"
- 758b34d: Move the "Ask <query>" to the bottom of search results if query is not a question
- 0ae7cfd: Improve AI Search branding
- f9f8011: Add alt text support to card covers
- ebbb476: Improve ancestor label in references
- 1b0613e: Tweak language dropdown styling
- 3355960: Fix ToggeableLinkItem display
- c31e564: Fix icon resolution for page group
- 55c0b03: Support translations and generic variants together
- 3876f70: Edgeless Assistant window on `depth: flat` sites
- 683bb8f: Add confirmation to "Copy MCP URL" page action
- 4534ea8: Implement unique title tags for sections & variants
- 6d56df7: Treat pages without visible TOC items as `no-toc`
- 8606f22: Add padding to TOC page group items
- 3676d64: Update search scope UI
- c32d449: Fix OpenAPI path display
- 8ce7322: Add OpenAPI servers selection
- 2c3066e: Improve OAuth2 scopes handling in OpenAPI
- 6b1c9fe: Fix opacity of ScrollContainer buttons on bold themes
- Updated dependencies [b4a021a]
- Updated dependencies [a512c90]
- Updated dependencies [df1966d]
- Updated dependencies [b45feaf]
- Updated dependencies [10995e0]
- Updated dependencies [f9f8011]
- Updated dependencies [10995e0]
- Updated dependencies [8ce7322]
- Updated dependencies [2c3066e]
- @gitbook/react-openapi@1.5.3
- @gitbook/openapi-parser@3.0.5
- @gitbook/react-contentkit@0.7.8
- @gitbook/browser-types@0.1.3
- @gitbook/cache-tags@0.3.4
- @gitbook/react-math@0.6.4
- @gitbook/colors@0.4.3
- @gitbook/embed@0.1.5
- @gitbook/fonts@0.1.3
- @gitbook/icons@0.3.4
- @gitbook/expr@1.2.4
## 0.19.2
### Patch Changes
- 6142d6b: Mark as sideEffects, fix all package bundles
- 75543b1: Update minimum Node version in .npmrc
- 511f1cf: Add scroll margin to Expandable
- Updated dependencies [6142d6b]
- @gitbook/emoji-codepoints@0.2.2
- @gitbook/react-contentkit@0.7.7
- @gitbook/openapi-parser@3.0.4
- @gitbook/browser-types@0.1.2
- @gitbook/react-openapi@1.5.2
- @gitbook/cache-tags@0.3.3
- @gitbook/react-math@0.6.3
- @gitbook/colors@0.4.2
- @gitbook/fonts@0.1.2
- @gitbook/icons@0.3.3
- @gitbook/expr@1.2.3
## 0.19.1
### Patch Changes
- 295f03d: Republish packages
- Updated dependencies [bdde392]
- Updated dependencies [295f03d]
- @gitbook/react-contentkit@0.7.6
- @gitbook/browser-types@0.1.1
- @gitbook/cache-tags@0.3.2
- @gitbook/colors@0.4.1
- @gitbook/emoji-codepoints@0.2.1
- @gitbook/expr@1.2.2
- @gitbook/fonts@0.1.1
- @gitbook/icons@0.3.2
- @gitbook/openapi-parser@3.0.3
- @gitbook/react-math@0.6.2
- @gitbook/react-openapi@1.5.1
## 0.19.0
### Minor Changes
- ed7d47d: Show a toolbar for authenticated users with access to the current site.
- 50c1be3: Move inline expression evaluation from API to GBO
- 56e46ce: Add Italian translation
- b932e4e: Scope search across sections and variants
### Patch Changes
- 162cfe4: Hide sections if only one is present
- 1456251: Unify section display condition
- 9344431: Fix missing geo data in site insights because of incorrect proxying of events.
- eea8f1e: Enhance OpenAPI security scopes handling
- 2b8a2d2: Add stable scroll gutter to search container
- b498521: Fix robots.txt preventing favicon from being indexed.
- ceb32b1: Redo search scope UI
- 8e99871: Highlight discriminator properties in oneOf, allOf, anyOf objects
- 2fc2127: Make search accessible
- f3e4041: Adapt OpenAPI code samples to prefill API key using visitor data
- 6815dd1: Fix markdownPageURL for PageActions
- c06b3dd: Improve default site icon
- cba583a: Use sitespace title instead of space title in search
- e434442: Support nested site section groups
- 469b332: Add support for inline expressions rendering with visitor data on GBO side
- 0ef5dc8: Update to column width sizing
- 1e4e54e: Fix rounded outline items
- 379d486: Expose "Best match" when site has translations
- c21693f: Improve screen reader accessibility for hints
- badf3a9: Add alt text to inline image
- c23d4ef: Fix crash for card cover defined without objectFit
- d74f8a7: Improve wide page width layout
- b8388e5: Add scrollcontainer component
- b85eccd: Refactor section tabs
- 1e53f46: Fix button content truncate
- 7a504b7: Fix OpenAPI response dropdown & vertical overflow
- a4c3399: Fix OpenAPI alternatives not showing
- Updated dependencies [a629900]
- Updated dependencies [eea8f1e]
- Updated dependencies [8e99871]
- Updated dependencies [f3e4041]
- Updated dependencies [319a1e5]
- Updated dependencies [529f940]
- Updated dependencies [754cc11]
- Updated dependencies [d7948e3]
- Updated dependencies [a4c3399]
- @gitbook/expr@1.2.1
- @gitbook/react-openapi@1.5.0
## 0.18.0
### Minor Changes
- 262afa3: Expose a MCP server for the docs site under /~gitbook/mcp
### Patch Changes
- Updated dependencies [262afa3]
- @gitbook/icons@0.3.1
- @gitbook/react-openapi@1.4.3
- @gitbook/react-contentkit@0.7.5
## 0.17.2
### Patch Changes
- @gitbook/react-openapi@1.4.2
## 0.17.1
### Patch Changes
- 24f601d: Small optim in resolveTryItPrefillForOperation
- aea5eb1: Persist language choice across sections if possible
- 1165a81: Language selector edge cases
- f9a2977: Better handling for external link "mailto:" in Hovered Card in GBO
- Updated dependencies [24f601d]
- @gitbook/react-openapi@1.4.1
## 0.17.0
### Minor Changes
@@ -0,0 +1,76 @@
import { expect } from '@playwright/test';
import { type TestsCase, getCustomizationURL, runTestCases } from './util';
const testCases: TestsCase[] = [
{
name: 'Cookie Banner',
contentBaseURL: 'https://gitbook.com/docs/',
tests: [
{
name: 'should show built-in banner when no custom banner is registered',
url: getCustomizationURL({
privacyPolicy: {
url: 'https://policies.gitbook.com/privacy/cookies',
},
}),
screenshot: false,
run: async (page) => {
// Check that built-in banner is visible
const dialog = page.getByTestId('cookies-dialog');
await expect(dialog).toBeVisible({ timeout: 5000 });
},
},
{
name: 'should not show built-in banner when UserAgent is AI',
url: getCustomizationURL({
privacyPolicy: {
url: 'https://policies.gitbook.com/privacy/cookies',
},
}),
screenshot: false,
run: async (page) => {
// Override navigator.userAgent to simulate AI crawler (affects isAIUserAgent())
await page.addInitScript(() => {
Object.defineProperty(navigator, 'userAgent', {
get: () =>
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot',
configurable: true,
});
});
await page.reload();
// Check that built-in banner is not visible for AI UserAgent
const dialog = page.getByTestId('cookies-dialog');
await expect(dialog).not.toBeVisible({ timeout: 5000 });
},
},
{
name: 'should not show built-in banner when custom banner is registered',
url: getCustomizationURL({
privacyPolicy: {
url: 'https://policies.gitbook.com/privacy/cookies',
},
}),
screenshot: false,
run: async (page) => {
// Register a custom cookie banner handler
await page.waitForFunction(() => {
return typeof window !== 'undefined' && window.GitBook !== undefined;
});
await page.evaluate(() => {
window.GitBook?.registerCookieBanner(() => {
// Custom cookie banner handler - no-op for testing to avoid reload
});
});
// Check that built-in banner is not visible
const dialog = page.getByTestId('cookies-dialog');
await expect(dialog).not.toBeVisible({ timeout: 5000 });
},
},
],
},
];
runTestCases(testCases);
+22 -33
View File
@@ -10,18 +10,18 @@ const testCases: TestsCase[] = [
{ name: 'OpenAPI', url: '/snyk-api/reference/apps', run: waitForCookiesDialog },
],
},
// {
// name: 'Nexthink',
// contentBaseURL: 'https://docs.nexthink.com',
// tests: [
// {
// name: 'Home',
// url: '/',
// screenshot: { waitForTOCScrolling: false },
// run: waitForCookiesDialog,
// },
// ],
// },
{
name: 'Nexthink',
contentBaseURL: 'https://docs.nexthink.com',
tests: [
{
name: 'Home',
url: '/',
screenshot: { waitForTOCScrolling: false },
run: waitForCookiesDialog,
},
],
},
{
name: 'asiksupport-stg.dto.kemkes.go.id',
contentBaseURL: 'https://asiksupport-stg.dto.kemkes.go.id',
@@ -37,11 +37,6 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://faq.deltaemulator.com',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.dify.ai',
contentBaseURL: 'https://docs.dify.ai',
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
},
{
name: 'seeddao.gitbook.io',
contentBaseURL: 'https://seeddao.gitbook.io',
@@ -62,11 +57,11 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://docs.gmgn.ai',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.spicychat.ai',
contentBaseURL: 'https://docs.spicychat.ai',
tests: [{ name: 'Home', url: '/' }],
},
// {
// name: 'docs.spicychat.ai',
// contentBaseURL: 'https://docs.spicychat.ai',
// tests: [{ name: 'Home', url: '/' }],
// },
{
name: 'docs.portainer.io',
contentBaseURL: 'https://docs.portainer.io',
@@ -112,11 +107,11 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://adiblar.gitbook.io',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.gradient.network',
contentBaseURL: 'https://docs.gradient.network',
tests: [{ name: 'Home', url: '/' }],
},
// {
// name: 'docs.gradient.network',
// contentBaseURL: 'https://docs.gradient.network',
// tests: [{ name: 'Home', url: '/' }],
// },
// {
// name: 'mygate-network.gitbook.io',
// contentBaseURL: 'https://mygate-network.gitbook.io',
@@ -172,11 +167,6 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://docs.realapp.link',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.plaza.finance',
contentBaseURL: 'https://docs.plaza.finance',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.publicai.io',
contentBaseURL: 'https://docs.publicai.io',
@@ -242,7 +232,6 @@ const testCases: TestsCase[] = [
{
name: 'run-ai-docs.nvidia.com',
contentBaseURL: 'https://run-ai-docs.nvidia.com',
skip: process.env.ARGOS_BUILD_NAME !== 'customers-v2',
tests: [
{ name: 'Home', url: '/' },
{ name: 'OG Image', url: '/~gitbook/ogimage/h17zQIFwy3MaafVNmItO', mode: 'image' },
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -29,7 +29,7 @@ test.describe('PDF export', () => {
const printBtn = page.getByTestId('print-button');
await expect(printBtn).toBeVisible();
await argosScreenshot(page, 'pdf - all pages', {
await argosScreenshot(page, 'pdf - e2e - all pages', {
viewports: ['macbook-13'],
argosCSS: `
/* Hide Intercom */
@@ -67,7 +67,7 @@ test.describe('PDF export', () => {
const printBtn = page.getByTestId('print-button');
await expect(printBtn).toBeVisible();
await argosScreenshot(page, 'pdf - all pages', {
await argosScreenshot(page, 'pdf - gb-docs - all pages', {
viewports: ['macbook-13'],
argosCSS: `
/* Hide Intercom */
@@ -109,7 +109,7 @@ test.describe('PDF export', () => {
const printBtn = page.getByTestId('print-button');
await expect(printBtn).toBeVisible();
await argosScreenshot(page, 'pdf - all pages', {
await argosScreenshot(page, 'pdf - e2e - single-page', {
viewports: ['macbook-13'],
argosCSS: `
/* Hide Intercom */
@@ -151,7 +151,7 @@ test.describe('PDF export', () => {
const printBtn = page.getByTestId('print-button');
await expect(printBtn).toBeVisible();
await argosScreenshot(page, 'pdf - all pages', {
await argosScreenshot(page, 'pdf - gb-docs - single-page', {
viewports: ['macbook-13'],
argosCSS: `
/* Hide Intercom */
+42 -7
View File
@@ -2,6 +2,7 @@ import { argosScreenshot } from '@argos-ci/playwright';
import {
CustomizationAIMode,
CustomizationBackground,
CustomizationCodeTheme,
CustomizationCorners,
CustomizationDefaultFont,
CustomizationDefaultMonospaceFont,
@@ -143,7 +144,10 @@ export const headerLinks: CustomizationHeaderItem[] = [
export async function waitForCookiesDialog(page: Page) {
const dialog = page.getByTestId('cookies-dialog');
await expect(dialog).toBeVisible();
await expect(dialog).toBeVisible({
// Cookies dialog may take some times to appear
timeout: 10_000,
});
}
export async function waitForNotFound(_page: Page, response: Response | null) {
@@ -151,6 +155,23 @@ export async function waitForNotFound(_page: Page, response: Response | null) {
expect(response?.status()).toBe(404);
}
export async function setTimeToMorning(page: Page) {
const now = new Date();
now.setHours(8, 0, 0, 0); // 8:00:00.000 AM (local time)
await page.clock.install({ time: now });
}
export async function waitForCoverImages(page: Page, options?: { darkMode?: boolean }) {
const selector = options?.darkMode
? 'img[alt="Page cover"].dark\\:inline'
: 'img[alt="Page cover"]:not(.dark\\:inline)';
// Wait for cover images to exist (not the shimmer placeholder)
await expect(page.locator(selector)).toBeVisible({
timeout: 10_000,
});
}
/**
* Transform test cases into Playwright tests and run it.
*/
@@ -288,6 +309,16 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
background: CustomizationBackground.Plain,
icons: CustomizationIconsStyle.Regular,
links: CustomizationLinksStyle.Default,
codeTheme: {
default: {
light: CustomizationCodeTheme.DefaultLight,
dark: CustomizationCodeTheme.DefaultDark,
},
openapi: {
light: CustomizationCodeTheme.DefaultLight,
dark: CustomizationCodeTheme.DefaultDark,
},
},
sidebar: {
background: CustomizationSidebarBackgroundStyle.Default,
list: CustomizationSidebarListStyle.Default,
@@ -336,6 +367,7 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
pageActions: {
externalAI: true,
markdown: true,
mcp: true,
},
trademark: {
enabled: true,
@@ -344,6 +376,7 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
url: 'https://www.gitbook.com/privacy',
},
socialPreview: {},
socialAccounts: [],
};
const encoded = rison.encode_object(deepMerge(DEFAULT_CUSTOMIZATION, partial));
@@ -406,14 +439,15 @@ export async function waitForIcons(page: Page) {
return true;
}
// url("https://ka-p.fontawesome.com/releases/v6.6.0/svgs/light/moon.svg?v=2&token=a463935e93")
const maskImage = window.getComputedStyle(icon).getPropertyValue('mask-image');
const urlMatch = maskImage.match(/url\("([^"]+)"\)/);
const url = urlMatch?.[1];
const maskImage = icon.querySelector('[data-testid="mask-image"]');
if (!maskImage) {
throw new Error('No mask-image element');
}
const url = maskImage.getAttribute('href');
// If URL is invalid we throw an error.
if (!url) {
throw new Error('No mask-image');
throw new Error('No mask-image url');
}
// If the URL is already queued for loading, we return the state.
@@ -443,7 +477,8 @@ export async function waitForIcons(page: Page) {
*/
async function waitForTOCScrolling(page: Page) {
const viewport = await page.viewportSize();
if (viewport && viewport.width >= 1024) {
if (viewport && viewport.width >= 1024 && !page.url().includes('~gitbook/embed/demo')) {
// The embed demo is an iframe, which means the viewport is only a fraction of the main document. So there is no open TOC to scroll to.
const toc = page.getByTestId('table-of-contents');
await expect(toc).toBeVisible();
await page.evaluate(() => {
+2
View File
@@ -4,6 +4,7 @@
* @type {import('next').NextConfig}
*/
const nextConfig = {
deploymentId: process.env.GITHUB_SHA || Date.now().toString(), // Needed because we use a custom deployment method i.e. https://vercel.com/docs/skew-protection#custom-deployment-id
experimental: {
// This is needed to throw "forbidden" when the api token expired during revalidation
authInterrupts: true,
@@ -25,6 +26,7 @@ const nextConfig = {
// GitBook envs
GITBOOK_API_URL: process.env.GITBOOK_API_URL,
GITBOOK_APP_URL: process.env.GITBOOK_APP_URL,
GITBOOK_OAUTH_SERVER_URL: process.env.GITBOOK_OAUTH_SERVER_URL,
GITBOOK_INTEGRATIONS_HOST: process.env.GITBOOK_INTEGRATIONS_HOST,
GITBOOK_IMAGE_RESIZE_URL: process.env.GITBOOK_IMAGE_RESIZE_URL,
GITBOOK_ICONS_URL: process.env.GITBOOK_ICONS_URL,
+4 -2
View File
@@ -7,7 +7,8 @@ export default {
converter: 'edge',
proxyExternalRequest: 'fetch',
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
incrementalCache: () =>
import('./openNext/incrementalCache/server').then((m) => m.default),
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
},
},
@@ -18,7 +19,8 @@ export default {
converter: 'edge',
proxyExternalRequest: 'fetch',
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
incrementalCache: () =>
import('./openNext/incrementalCache/middleware').then((m) => m.default),
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
},
},
@@ -1,15 +1,13 @@
{
"main": "default.js",
"name": "gitbook-open-v2-server",
"keep_names": false,
"compatibility_date": "2025-04-14",
"compatibility_flags": [
"nodejs_compat",
"allow_importable_env",
"global_fetch_strictly_public"
],
"observability": {
"enabled": true
},
"vars": {
"NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true"
},
@@ -17,7 +15,8 @@
"dev": {
"vars": {
"STAGE": "dev",
"OPEN_NEXT_REQUEST_ID_HEADER": "true"
"OPEN_NEXT_REQUEST_ID_HEADER": "true",
"GITBOOK_URL": "http://localhost:8771"
},
"r2_buckets": [
{
@@ -2,14 +2,12 @@
"main": "do.js",
"name": "gitbook-open-v2-do",
"compatibility_date": "2025-04-14",
"keep_names": false,
"compatibility_flags": [
"nodejs_compat",
"allow_importable_env",
"global_fetch_strictly_public"
],
"observability": {
"enabled": true
},
"env": {
"preview": {
"vars": {
@@ -26,8 +26,10 @@ export default class extends WorkerEntrypoint {
async fetch(request) {
return runWithCloudflareRequestContext(request, this.env, this.ctx, async () => {
const startTime = Date.now();
const middlewareRequest = new Request(request.url, request);
middlewareRequest.headers.set('x-open-next-continent', request.cf?.continent || '');
// - `Request`s are handled by the Next server
const reqOrResp = await middlewareHandler(request, this.env, this.ctx);
const reqOrResp = await middlewareHandler(middlewareRequest, this.env, this.ctx);
if (reqOrResp instanceof Response) {
const duration = Date.now() - startTime;
const logMessage = formatLog(
@@ -2,6 +2,7 @@
"main": "middleware.js",
"name": "gitbook-open-v2",
"compatibility_date": "2025-04-14",
"keep_names": false,
"compatibility_flags": [
"nodejs_compat",
"allow_importable_env",
@@ -11,9 +12,6 @@
"directory": "../../.open-next/assets",
"binding": "ASSETS"
},
"observability": {
"enabled": true
},
"vars": {
"NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true"
},
@@ -18,15 +18,12 @@ export type KeyOptions = {
/**
*
* It is very similar to the `R2IncrementalCache` in the `@opennextjs/cloudflare` package, but it allow us to trace
* the cache operations. It also integrates both R2 and Cache API in a single class.
* Having our own, will allow us to customize it in the future if needed.
* It is very similar to the `R2IncrementalCache` in the `@opennextjs/cloudflare` package, but it has an additional
* R2WriteBuffer Durable Object to handle writes to R2. Given how we set up cache, we often end up writing to the same key too fast.
*/
class GitbookIncrementalCache implements IncrementalCache {
export class GitbookIncrementalCache implements IncrementalCache {
name = 'GitbookIncrementalCache';
protected localCache: Cache | undefined;
async get<CacheType extends CacheEntryType = 'cache'>(
key: string,
cacheType?: CacheType
@@ -34,7 +31,6 @@ class GitbookIncrementalCache implements IncrementalCache {
const cacheKey = this.getR2Key(key, cacheType);
const r2 = getCloudflareContext().env[BINDING_NAME];
const localCache = await this.getCacheInstance();
if (!r2) throw new Error('No R2 bucket');
if (process.env.SHOULD_BYPASS_CACHE === 'true') {
// We are in a local middleware environment, we should bypass the cache
@@ -42,27 +38,17 @@ class GitbookIncrementalCache implements IncrementalCache {
return null;
}
try {
// Check local cache first if available
const localCacheEntry = await localCache.match(this.getCacheUrlKey(cacheKey));
if (localCacheEntry) {
const result = (await localCacheEntry.json()) as WithLastModified<
CacheValue<CacheType>
>;
return this.returnNullOn404({
...result,
// Because we use tag cache and also invalidate them every time,
// if we get a cache hit, we don't need to check the tag cache as we already know it's not been revalidated
// this should improve performance even further, and reduce costs
shouldBypassTagCache: true,
});
}
const r2Object = await r2.get(cacheKey);
if (!r2Object) return null;
const json = (await r2Object.json()) as CacheValue<CacheType>;
const lastModified = r2Object.uploaded.getTime();
if (!json) return null;
return this.returnNullOn404({
value: await r2Object.json(),
lastModified: r2Object.uploaded.getTime(),
value: json,
lastModified,
});
} catch (e) {
console.error('Failed to get from cache', e);
@@ -89,39 +75,8 @@ class GitbookIncrementalCache implements IncrementalCache {
): Promise<void> {
const cacheKey = this.getR2Key(key, cacheType);
const localCache = await this.getCacheInstance();
try {
await this.writeToR2(cacheKey, JSON.stringify(value));
//TODO: Check if there is any places where we don't have tags
// Ideally we should always have tags, but in case we don't, we need to decide how to handle it
// For now we default to a build ID tag, which allow us to invalidate the cache in case something is wrong in this deployment
const tags = this.getTagsFromCacheEntry(value) ?? [
`build_id/${process.env.NEXT_BUILD_ID}`,
];
// We consider R2 as the source of truth, so we update the local cache
// only after a successful R2 write
await localCache.put(
this.getCacheUrlKey(cacheKey),
new Response(
JSON.stringify({
value,
// Note: `Date.now()` returns the time of the last IO rather than the actual time.
// See https://developers.cloudflare.com/workers/reference/security-model/
lastModified: Date.now(),
}),
{
headers: {
// Cache-Control default to 30 minutes, will be overridden by `revalidate`
// In theory we should always get the `revalidate` value
'cache-control': `max-age=${value.revalidate ?? 60 * 30}`,
'cache-tag': tags.join(','),
},
}
)
);
} catch (e) {
console.error('Failed to set to cache', e);
}
@@ -131,14 +86,10 @@ class GitbookIncrementalCache implements IncrementalCache {
const cacheKey = this.getR2Key(key);
const r2 = getCloudflareContext().env[BINDING_NAME];
const localCache = await this.getCacheInstance();
if (!r2) throw new Error('No R2 bucket');
try {
await r2.delete(cacheKey);
// Here again R2 is the source of truth, so we delete from local cache first
await localCache.delete(this.getCacheUrlKey(cacheKey));
} catch (e) {
console.error('Failed to delete from cache', e);
}
@@ -168,12 +119,6 @@ class GitbookIncrementalCache implements IncrementalCache {
}
}
async getCacheInstance(): Promise<Cache> {
if (this.localCache) return this.localCache;
this.localCache = await caches.open('incremental-cache');
return this.localCache;
}
// Utility function to generate keys for R2/Cache API
getR2Key(initialKey: string, cacheType: CacheEntryType = 'cache'): string {
let key = initialKey;
@@ -189,28 +134,4 @@ class GitbookIncrementalCache implements IncrementalCache {
'/'
);
}
getCacheUrlKey(cacheKey: string): string {
return `http://cache.local/${cacheKey}`;
}
getTagsFromCacheEntry<CacheType extends CacheEntryType>(
entry: CacheValue<CacheType>
): string[] | undefined {
if ('tags' in entry && entry.tags) {
return entry.tags;
}
if ('meta' in entry && entry.meta && 'headers' in entry.meta && entry.meta.headers) {
const rawTags = entry.meta.headers['x-next-cache-tags'];
if (typeof rawTags === 'string') {
return rawTags.split(',');
}
}
if ('value' in entry) {
return entry.tags;
}
}
}
export default new GitbookIncrementalCache();
@@ -0,0 +1,11 @@
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
import { GitbookIncrementalCache } from './incrementalCache';
export default withRegionalCache(new GitbookIncrementalCache(), {
mode: 'long-lived',
// We can do it because we use our own logic to invalidate the cache
bypassTagCacheOnCacheHit: true,
defaultLongLivedTtlSec: 60 * 60 * 24 /* 24 hours */,
// We don't want to update the cache entry on every cache hit
shouldLazilyUpdateOnCacheHit: false,
});
@@ -0,0 +1,12 @@
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
import { GitbookIncrementalCache } from './incrementalCache';
export default withRegionalCache(new GitbookIncrementalCache(), {
mode: 'long-lived',
// Because of a race condition, the middleware may have populated the cache entry before `cache.match` had time to run on the server.
// TODO: We should bypass the incremental cache entirely when the interceptor has caught the request. Should be done in OpenNext.
bypassTagCacheOnCacheHit: false,
defaultLongLivedTtlSec: 60 * 60 * 24 /* 24 hours */,
// We don't want to update the cache entry on every cache hit
shouldLazilyUpdateOnCacheHit: false,
});
+40 -24
View File
@@ -1,21 +1,26 @@
{
"name": "gitbook",
"version": "0.17.0",
"version": "0.22.0",
"private": true,
"dependencies": {
"@cloudflare/workers-types": "^4.20251011.0",
"@gitbook/api": "catalog:",
"@gitbook/browser-types": "workspace:*",
"@gitbook/cache-tags": "workspace:*",
"@gitbook/colors": "workspace:*",
"@gitbook/expr": "workspace:*",
"@gitbook/embed": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/expr": "workspace:*",
"@gitbook/fonts": "workspace:*",
"@gitbook/icons": "workspace:*",
"@gitbook/openapi-parser": "workspace:*",
"@gitbook/react-contentkit": "workspace:*",
"@gitbook/react-math": "workspace:*",
"@gitbook/react-openapi": "workspace:*",
"@opennextjs/cloudflare": "^1.7.1",
"@mermaid-js/mermaid-zenuml": "^0.2.2",
"@modelcontextprotocol/sdk": "1.17.5",
"@opennextjs/aws": "^3.8.5",
"@opennextjs/cloudflare": "^1.14.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.1.12",
"@radix-ui/react-hover-card": "^1.1.15",
@@ -24,37 +29,39 @@
"@radix-ui/react-tooltip": "^1.1.8",
"@sindresorhus/fnv1a": "^3.1.0",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/typography": "^0.5.16",
"@tusbar/cache-control": "^1.0.2",
"ai": "^4.2.2",
"assert-never": "^1.2.1",
"assert-never": "catalog:",
"bidc": "catalog:",
"bun-types": "^1.1.20",
"classnames": "^2.5.1",
"classnames": "catalog:",
"direction": "^2.0.1",
"event-iterator": "^2.0.0",
"framer-motion": "^10.16.14",
"feed": "^5.1.0",
"image-size": "^2.0.2",
"js-cookie": "^3.0.5",
"jsontoxml": "^1.0.1",
"jwt-decode": "^4.0.0",
"katex": "^0.16.9",
"mathjax": "^3.2.2",
"mcp-handler": "^1.0.2",
"mdast-util-from-markdown": "^2.0.2",
"mdast-util-frontmatter": "^2.0.1",
"mdast-util-gfm": "^3.1.0",
"mdast-util-to-markdown": "^2.1.2",
"memoizee": "^0.4.17",
"mermaid": "^11.12.3",
"micromark-extension-frontmatter": "^2.0.0",
"micromark-extension-gfm": "^3.0.0",
"next": "15.3.5",
"next-themes": "^0.2.1",
"motion": "^12.23.24",
"negotiator": "^1.0.0",
"next": "15.4.11",
"next-themes": "^0.4.6",
"nuqs": "^2.2.3",
"object-hash": "^3.0.0",
"object-identity": "^0.1.2",
"openapi-types": "^12.1.3",
"p-map": "^7.0.3",
"quick-lru": "^7.0.1",
"react": "catalog:",
"react-dom": "catalog:",
"react-hotkeys-hook": "^4.4.1",
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1",
@@ -63,20 +70,22 @@
"remark-rehype": "^11.1.1",
"rison": "^0.1.1",
"server-only": "^0.0.1",
"shiki": "^3.2.0",
"shiki": "^3.21.0",
"tailwind-merge": "^2.2.0",
"tailwind-shades": "^1.1.2",
"unified": "^11.0.5",
"unist-util-remove": "^4.0.0",
"unist-util-visit": "^5.0.0",
"url-join": "^5.0.0",
"usehooks-ts": "^3.1.0",
"usehooks-ts": "catalog:",
"warn-once": "^0.1.1",
"zod": "^3",
"zustand": "^5.0.3"
},
"devDependencies": {
"@argos-ci/playwright": "^5.0.9",
"@playwright/test": "^1.54.2",
"@argos-ci/playwright": "^6.4.2",
"@playwright/test": "^1.58.2",
"@scalar/api-client-react": "catalog:",
"@tailwindcss/postcss": "^4.1.11",
"@types/js-cookie": "^3.0.6",
"@types/jsontoxml": "^1.0.5",
@@ -85,20 +94,22 @@
"@types/node": "^20",
"@types/object-hash": "^3.0.6",
"@types/parse-cache-control": "^1.0.4",
"@types/psi": "^4.1.6",
"@types/react": "18.3.13",
"@types/react-dom": "18.3.1",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/rison": "^0.0.9",
"@types/negotiator": "^0.6.4",
"bun-types": "catalog:",
"deepmerge": "^4.3.1",
"env-cmd": "^10.1.0",
"jsonwebtoken": "^9.0.2",
"postcss": "^8",
"psi": "^4.1.0",
"stylelint": "^16.16.0",
"tailwindcss": "^4.1.11",
"ts-essentials": "^10.0.1",
"typescript": "^5.5.3",
"vercel": "^39.3.0"
"typescript": "catalog:",
"vercel": "^50.15.1",
"wrangler": "^4.43.0",
"rss-parser": "^3.13.0"
},
"scripts": {
"generate": "./scripts/generate.sh",
@@ -111,12 +122,17 @@
"dev:cloudflare": "wrangler dev --port 8771 --env preview",
"dev:cf:middleware": "wrangler dev --port 8771 --inspector-port 9230 --env dev --config ./openNext/customWorkers/middlewareWrangler.jsonc",
"dev:cf:server": "wrangler dev --port 8772 --env dev --config ./openNext/customWorkers/defaultWrangler.jsonc",
"e2e": "playwright test e2e/internal.spec.ts e2e/pdf.spec.ts --project=chromium",
"e2e": "playwright test e2e/internal.spec.ts e2e/cookie-banner.spec.ts e2e/pdf.spec.ts --project=chromium",
"e2e-customers": "playwright test e2e/customers.spec.ts --project=chromium",
"unit": "bun test {src,packages} --preload ./tests/preload-bun.ts",
"e2e-browserless": "bun test ./tests/",
"typecheck": "tsc --noEmit"
},
"browserslist": [
">0.3%, chrome >= 64, edge >= 79, firefox >= 67, opera >= 51, safari >= 12 and not dead"
]
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
@@ -16,7 +16,7 @@ export default async function Page(props: PageProps) {
const { context } = await getDynamicSiteContext(params);
const pathname = getPagePathFromParams(params);
return <SitePage context={context} pageParams={{ pathname }} />;
return <SitePage context={context} pageParams={{ pathname }} staticRoute={false} />;
}
export async function generateViewport(props: PageProps): Promise<Viewport> {
@@ -22,7 +22,12 @@ export default async function SiteDynamicLayout({
const withTracking = shouldTrackEvents(await headers());
return (
<CustomizationRootLayout forcedTheme={forcedTheme} context={context}>
<CustomizationRootLayout
htmlClassName="sheet-open:gutter-stable"
bodyClassName="site-background"
forcedTheme={forcedTheme}
context={context}
>
<SiteLayout
context={context}
forcedTheme={forcedTheme}
@@ -1,16 +1,28 @@
import type { RouteLayoutParams } from '@/app/utils';
import { EmbeddableAssistantPage } from '@/components/Embeddable';
import { getEmbeddableDynamicContext } from '@/lib/embeddable';
export const dynamic = 'force-static';
import { CustomizationAIMode } from '@gitbook/api';
import { redirect } from 'next/navigation';
type PageProps = {
params: Promise<RouteLayoutParams>;
};
export const dynamic = 'force-static';
export default async function Page(props: PageProps) {
const params = await props.params;
const { context } = await getEmbeddableDynamicContext(params);
return <EmbeddableAssistantPage context={context} />;
// If the assistant is not enabled, redirect to the docs
if (context.customization.ai.mode !== CustomizationAIMode.Assistant) {
redirect(`${context.linker.toPathInSite('~gitbook/embed/page/')}`);
}
return (
<EmbeddableAssistantPage
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
siteTitle={context.site.title}
/>
);
}
@@ -0,0 +1,21 @@
import type { RouteLayoutParams } from '@/app/utils';
import { getEmbeddableDynamicContext } from '@/lib/embeddable';
import { CustomizationAIMode } from '@gitbook/api';
import { redirect } from 'next/navigation';
type PageProps = {
params: Promise<RouteLayoutParams>;
};
export default async function Page(props: PageProps) {
const params = await props.params;
const { context } = await getEmbeddableDynamicContext(params);
const baseURL = context.linker.toPathInSite('~gitbook/embed/');
// If assistant is enabled, redirect to assistant, otherwise to docs
if (context.customization.ai.mode === CustomizationAIMode.Assistant) {
redirect(`${baseURL}/assistant`);
} else {
redirect(`${baseURL}/page/`);
}
}
@@ -12,7 +12,7 @@ export default async function Page(props: PageProps) {
const { context } = await getEmbeddableDynamicContext(params);
const pathname = getPagePathFromParams(params);
return <EmbeddableDocsPage context={context} pageParams={{ pathname }} />;
return <EmbeddableDocsPage context={context} pageParams={{ pathname }} staticRoute={false} />;
}
export async function generateMetadata(props: PageProps): Promise<Metadata> {
@@ -18,7 +18,7 @@ export default async function Page(props: PageProps) {
const { context } = await getStaticSiteContext(params);
const pathname = getPagePathFromParams(params);
return <SitePage context={context} pageParams={{ pathname }} />;
return <SitePage context={context} pageParams={{ pathname }} staticRoute />;
}
export async function generateViewport(props: PageProps): Promise<Viewport> {
@@ -19,7 +19,11 @@ export default async function SiteStaticLayout({
const withTracking = shouldTrackEvents();
return (
<CustomizationRootLayout context={context}>
<CustomizationRootLayout
htmlClassName="sheet-open:gutter-stable"
bodyClassName="site-background"
context={context}
>
<SiteLayout
context={context}
withTracking={withTracking}
@@ -1,16 +1,28 @@
import type { RouteLayoutParams } from '@/app/utils';
import type { RouteParams } from '@/app/utils';
import { EmbeddableAssistantPage } from '@/components/Embeddable';
import { getEmbeddableStaticContext } from '@/lib/embeddable';
import { CustomizationAIMode } from '@gitbook/api';
import { redirect } from 'next/navigation';
export const dynamic = 'force-static';
type PageProps = {
params: Promise<RouteLayoutParams>;
params: Promise<RouteParams>;
};
export default async function Page(props: PageProps) {
const params = await props.params;
const { context } = await getEmbeddableStaticContext(params);
return <EmbeddableAssistantPage context={context} />;
// If the assistant is not enabled, redirect to the docs
if (context.customization.ai.mode !== CustomizationAIMode.Assistant) {
redirect(`${context.linker.toPathInSite('~gitbook/embed/page/')}`);
}
return (
<EmbeddableAssistantPage
baseURL={context.linker.toPathInSite('~gitbook/embed/')}
siteTitle={context.site.title}
/>
);
}
@@ -16,9 +16,107 @@ export async function GET(
return new Response(
`
<html>
<head></head>
<body></body>
<head>
<meta name="color-scheme" content="light dark">
</head>
<body>
<svg style="position: absolute; bottom: 6rem; right: 4rem;" width="719" height="644" viewBox="0 0 719 644" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.1" x="0.566895" y="208" width="529.567" height="228" rx="16" fill="#A4A7B0"/>
<g opacity="0.2">
<line x1="141.567" y1="472" x2="4.56689" y2="472" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="416.067" y1="472" x2="165.567" y2="472" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="472" x2="440.067" y2="472" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="194.567" y1="496" x2="4.56689" y2="496" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="324.067" y1="496" x2="218.567" y2="496" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="473.567" y1="496" x2="348.067" y2="496" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="231.901" y1="520" x2="4.56689" y2="520" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="291.186" y1="520" x2="255.901" y2="520" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="442.058" y1="520" x2="315.186" y2="520" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="520" x2="466.058" y2="520" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="141.567" y1="544" x2="4.56689" y2="544" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="348.191" y1="544" x2="165.567" y2="544" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="184.017" y1="592" x2="4.56689" y2="592" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="312.848" y1="592" x2="208.017" y2="592" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="386.574" y1="592" x2="336.848" y2="592" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="448.401" y1="592" x2="410.574" y2="592" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="126.633" y1="616" x2="4.56689" y2="616" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="401.133" y1="616" x2="150.633" y2="616" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="616" x2="425.133" y2="616" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="179.457" y1="640" x2="4.56689" y2="640" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="329.569" y1="640" x2="203.457" y2="640" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="141.567" y1="4" x2="4.56689" y2="4" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="416.067" y1="4" x2="165.567" y2="4" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="4" x2="440.067" y2="4" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="194.567" y1="28" x2="4.56689" y2="28" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="324.067" y1="28" x2="218.567" y2="28" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="473.567" y1="28" x2="348.067" y2="28" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="231.901" y1="52" x2="4.56689" y2="52" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="291.186" y1="52" x2="255.901" y2="52" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="442.058" y1="52" x2="315.186" y2="52" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="52" x2="466.058" y2="52" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="141.567" y1="76" x2="4.56689" y2="76" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="348.191" y1="76" x2="165.567" y2="76" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="184.017" y1="124" x2="4.56689" y2="124" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="312.848" y1="124" x2="208.017" y2="124" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="386.574" y1="124" x2="336.848" y2="124" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="448.401" y1="124" x2="410.574" y2="124" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="126.633" y1="148" x2="4.56689" y2="148" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="401.133" y1="148" x2="150.633" y2="148" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="526.567" y1="148" x2="425.133" y2="148" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="179.457" y1="172" x2="4.56689" y2="172" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="329.569" y1="172" x2="203.457" y2="172" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<rect x="563" y="208" width="156" height="256" rx="16" fill="#A4A7B0" opacity="0.05"/>
<g opacity="0.2">
<line x1="682.214" y1="228" x2="583" y2="228" stroke="#656973" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="638.472" y1="252" x2="583" y2="252" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="681.396" y1="252" x2="654.472" y2="252" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="617.989" y1="276" x2="583" y2="276" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="699" y1="276" x2="633.989" y2="276" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="603.168" y1="300" x2="583" y2="300" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="657.873" y1="300" x2="619.168" y2="300" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="637" y1="324" x2="583" y2="324" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="688.836" y1="324" x2="653" y2="324" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="657.474" y1="372" x2="583" y2="372" stroke="#656973" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="633" y1="396" x2="583" y2="396" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="699" y1="396" x2="649" y2="396" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="585.416" y1="420" x2="583" y2="420" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="621.731" y1="420" x2="601.416" y2="420" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="699" y1="420" x2="637.731" y2="420" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
<g opacity="0.2">
<line x1="619.471" y1="444" x2="583" y2="444" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
<line x1="658.186" y1="444" x2="635.471" y2="444" stroke="#A4A7B0" stroke-width="8" stroke-linecap="round"/>
</g>
</svg>
</body>
<script src="${context.linker.toAbsoluteURL(context.linker.toPathInSite('~gitbook/embed/script.js'))}"></script>
<script>
window.GitBook('configure', {
suggestions: [
'Help me get started',
'What can I ask you?',
'Show me tips and tricks',
],
});
window.GitBook('open');
</script>
</html>
`,
{
@@ -0,0 +1,23 @@
import type { RouteLayoutParams } from '@/app/utils';
import { getEmbeddableStaticContext } from '@/lib/embeddable';
import { CustomizationAIMode } from '@gitbook/api';
import { redirect } from 'next/navigation';
export const dynamic = 'force-static';
type PageProps = {
params: Promise<RouteLayoutParams>;
};
export default async function Page(props: PageProps) {
const params = await props.params;
const { context } = await getEmbeddableStaticContext(params);
const baseURL = context.linker.toPathInSite('~gitbook/embed/');
// If assistant is enabled, redirect to assistant, otherwise to docs
if (context.customization.ai.mode === CustomizationAIMode.Assistant) {
redirect(`${baseURL}/assistant`);
} else {
redirect(`${baseURL}/page/`);
}
}
@@ -14,7 +14,7 @@ export default async function Page(props: PageProps) {
const { context } = await getEmbeddableStaticContext(params);
const pathname = getPagePathFromParams(params);
return <EmbeddableDocsPage context={context} pageParams={{ pathname }} />;
return <EmbeddableDocsPage context={context} pageParams={{ pathname }} staticRoute />;
}
export async function generateMetadata(props: PageProps): Promise<Metadata> {
@@ -24,10 +24,22 @@ export async function GET(
(function () {
const w = window;
const gb = w.GitBook;
function getScriptSearchParams() {
const script = document.currentScript;
if (!script) return new URLSearchParams();
const url = new URL(script.src);
return url.searchParams;
}
const searchParams = getScriptSearchParams()
const token = searchParams.get('jwt_token');
const initOptions = window.gitbookSettings || ${JSON.stringify(initOptions)};
const initFrameOptions = token ? { visitor: { token } } : undefined;
if (typeof gb === "function") {
gb('init', initOptions);
gb('init', initOptions, initFrameOptions);
} else {
var d = document;
@@ -40,7 +52,7 @@ export async function GET(
};
w.GitBook = g;
g('init', initOptions);
g('init', initOptions, initFrameOptions);
const load = function () {
const style = document.createElement('link');
@@ -0,0 +1,105 @@
import { type RouteLayoutParams, getStaticSiteContext } from '@/app/utils';
import { throwIfDataError } from '@/lib/data';
import { joinPathWithBaseURL } from '@/lib/paths';
import { findSiteSpaceBy } from '@/lib/sites';
import { createMcpHandler } from 'mcp-handler';
import type { NextRequest } from 'next/server';
import { z } from 'zod';
async function handler(
nextRequest: NextRequest,
{ params }: { params: Promise<RouteLayoutParams> }
) {
const { context } = await getStaticSiteContext(await params);
const { dataFetcher, linker, site } = context;
const mcpHandler = createMcpHandler(
(server) => {
server.tool(
'searchDocumentation',
`Search across the documentation to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about ${site.title}, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages.`,
{
query: z.string(),
},
async ({ query }) => {
const results = await throwIfDataError(
dataFetcher.searchSiteContent({
organizationId: context.organizationId,
siteId: site.id,
query,
scope: { mode: 'all' },
})
);
return {
content: results.flatMap((result) => {
if (result.type === 'record') {
return {
type: 'text',
text: [
`Title: ${result.title}`,
`Link: ${result.url}`,
result.description ? `Content: ${result.description}` : '',
]
.filter(Boolean)
.join('\n'),
};
}
const found = findSiteSpaceBy(
context.structure,
(siteSpace) => siteSpace.space.id === result.id
);
const spaceURL = found?.siteSpace.urls.published;
if (!spaceURL) {
return [];
}
return result.pages.map((pageResult) => {
const pageURL = linker.toAbsoluteURL(
linker.toLinkForContent(
joinPathWithBaseURL(spaceURL, pageResult.path)
)
);
const body = pageResult.sections
?.map((section) => section.body)
.join('\n');
return {
type: 'text',
text: [
`Title: ${pageResult.title}`,
`Link: ${pageURL}`,
body ? `Content: ${body}` : '',
]
.filter(Boolean)
.join('\n'),
};
});
}),
};
}
);
},
{},
{
basePath: context.linker.toPathInSite('~gitbook/'),
streamableHttpEndpoint: '/mcp',
maxDuration: 60,
verboseLogs: true,
disableSse: true,
}
);
// Next.js request.url is the original URL and not the rewritten one from the middleware
const requestURL = new URL(
context.linker.toAbsoluteURL(context.linker.toPathInSite('~gitbook/mcp'))
);
requestURL.search = nextRequest.nextUrl.search;
const request = new Request(requestURL, nextRequest);
return mcpHandler(request);
}
export { handler as GET, handler as POST };
@@ -0,0 +1,12 @@
import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@/app/utils';
import { servePageRSS } from '@/routes/rss';
import type { NextRequest } from 'next/server';
export const dynamic = 'force-static';
export async function GET(_request: NextRequest, { params }: { params: Promise<RouteParams> }) {
const { context } = await getStaticSiteContext(await params);
const pathname = getPagePathFromParams(await params);
return servePageRSS(context, pathname);
}
+33 -8
View File
@@ -3,7 +3,7 @@ import { type SiteURLData, fetchSiteContextByURLLookup, getBaseContext } from '@
import { getDynamicCustomizationSettings } from '@/lib/customization';
import type { SiteAPIToken } from '@gitbook/api';
import { jwtDecode } from 'jwt-decode';
import { forbidden } from 'next/navigation';
import { forbidden, notFound } from 'next/navigation';
import rison from 'rison';
export type RouteParamMode = 'url-host' | 'url';
@@ -80,14 +80,32 @@ export async function getDynamicSiteContext(params: RouteLayoutParams) {
* Get the decoded page path from the params.
*/
export function getPagePathFromParams(params: RouteParams) {
const decoded = decodeURIComponent(params.pagePath);
return decoded;
// If decoding the param fails, return a 404 instead of crashing
try {
const decoded = decodeURIComponent(params.pagePath);
// For the root page, we encode '/' to avoid an empty param being passed.
if (decoded === '/') {
return '';
}
return decoded;
} catch (error) {
console.error(
`Returning 404 after failing to decode page path ${params.pagePath}: ${error}`
);
notFound();
}
}
function getSiteURLFromParams(params: RouteLayoutParams) {
const decoded = decodeURIComponent(params.siteURL);
const url = new URL(`https://${decoded}`);
return url;
try {
const decoded = decodeURIComponent(params.siteURL);
const url = new URL(`https://${decoded}`);
return url;
} catch (error) {
console.error(`Returning 404 after failing to decode site URL ${params.siteURL}: ${error}`);
notFound();
}
}
function getModeFromParams(mode: string): RouteParamMode {
@@ -102,6 +120,13 @@ function getModeFromParams(mode: string): RouteParamMode {
* Get the decoded site data from the params.
*/
function getSiteURLDataFromParams(params: RouteLayoutParams): SiteURLData {
const decoded = decodeURIComponent(params.siteData);
return rison.decode(decoded);
try {
const decoded = decodeURIComponent(params.siteData);
return rison.decode(decoded);
} catch (error) {
console.error(
`Returning 404 after failing to decode site data ${params.siteData}: ${error}`
);
notFound();
}
}
+2
View File
@@ -11,6 +11,7 @@ import {
GITBOOK_ICONS_URL,
GITBOOK_IMAGE_RESIZE_SIGNING_KEY,
GITBOOK_INTEGRATIONS_HOST,
GITBOOK_OAUTH_SERVER_URL,
GITBOOK_SECRET,
GITBOOK_URL,
GITBOOK_USER_AGENT,
@@ -25,6 +26,7 @@ export async function GET(_req: NextRequest) {
GITBOOK_APP_URL,
GITBOOK_API_URL,
GITBOOK_API_PUBLIC_URL,
GITBOOK_OAUTH_SERVER_URL,
GITBOOK_ASSETS_URL,
GITBOOK_FONTS_URL,
GITBOOK_ICONS_URL,
@@ -16,14 +16,14 @@ export function AIMessageView(
) {
const { message, context, withToolCalls = true, withLinkPreviews = true } = props;
return (
return message.steps.length > 0 ? (
<div className="flex flex-col gap-2">
{message.steps.map((step, index) => {
return (
<div
key={index}
className={tcls(
'flex animate-fade-in-slow flex-col gap-2',
'flex flex-col gap-2',
step.content.nodes.length > 0 ? 'has-content' : ''
)}
>
@@ -35,7 +35,7 @@ export function AIMessageView(
wrapBlocksInSuspense: false,
withLinkPreviews,
}}
style="mt-2 space-y-4 empty:hidden"
style="ai-response-document mt-2 space-y-4 *:origin-top-left *:animate-blur-in-slow"
/>
{withToolCalls && step.toolCalls && step.toolCalls.length > 0 ? (
@@ -45,5 +45,5 @@ export function AIMessageView(
);
})}
</div>
);
) : null;
}
@@ -15,6 +15,7 @@ import type {
ContentRef,
} from '@gitbook/api';
import { Icon, type IconName } from '@gitbook/icons';
import assertNever from 'assert-never';
import type * as React from 'react';
/**
@@ -39,7 +40,10 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
const { toolCall, context } = props;
return (
<div className="flex origin-left animate-scale-in-slow items-start gap-2 text-sm text-tint-subtle">
<div
data-testid="ai-chat-tool-summary"
className="mt-2 flex origin-top-left animate-blur-in-slow items-start gap-2 text-sm text-tint-subtle"
>
<Icon
icon={getIconForToolCall(toolCall)}
className="mt-1 size-3 shrink-0 text-tint-subtle/8"
@@ -135,32 +139,44 @@ async function DescriptionForSearchToolCall(props: {
// Resolve all hrefs for search results in parallel
const searchResultsWithHrefs = await Promise.all(
toolCall.results.map(async (result) => {
const resolved = await resolveContentRef(
result.anchor
? {
kind: 'anchor',
page: result.pageId,
space: result.spaceId,
anchor: result.anchor,
}
: {
kind: 'page',
page: result.pageId,
space: result.spaceId,
},
context
);
return {
...result,
href: resolved?.href || '#',
};
switch (result.type) {
case 'page': {
const resolved = await resolveContentRef(
result.anchor
? {
kind: 'anchor',
page: result.pageId,
space: result.spaceId,
anchor: result.anchor,
}
: {
kind: 'page',
page: result.pageId,
space: result.spaceId,
},
context
);
return {
...result,
href: resolved?.href || '#',
};
}
case 'record':
return {
...result,
href: result.url ?? '#',
};
default:
assertNever(result);
}
})
);
const hasResults = toolCall.results.length > 0;
return (
<details className={tcls('-ml-5 group flex w-full flex-col', hasResults ? 'gap-2' : '')}>
<details className="-ml-5 group flex w-full flex-col">
<summary
className={tcls(
'-mx-2 flex list-none items-center gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md pr-4 pl-7 transition-colors marker:hidden',
@@ -187,47 +203,60 @@ async function DescriptionForSearchToolCall(props: {
) : null}
</summary>
{hasResults ? (
<div className="hide-scrollbar mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all transition-discrete duration-500 group-open:max-h-96 group-open:opacity-11">
<div className="hide-scrollbar mt-4 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all transition-discrete duration-500 group-open:max-h-96 group-open:opacity-11">
<ol className="space-y-1">
{searchResultsWithHrefs.map((result, index) => (
<li
key={`${result.pageId}-${index}`}
className="animate-fade-in-slow"
style={{
animationDelay: `${index * 25}ms`,
}}
>
<Link
href={result.href}
className="flex items-start gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md px-3 py-2 transition-colors hover:bg-primary-hover"
{searchResultsWithHrefs.map((result, index) => {
const resultKey = (() => {
switch (result.type) {
case 'page':
return result.pageId;
case 'record':
return result.recordId;
default:
assertNever(result);
}
})();
return (
<li
key={`${resultKey}-${index}`}
className="animate-fade-in-slow"
style={{
animationDelay: `${index * 25}ms`,
}}
>
<Icon
icon="memo"
className="mt-1 size-3 shrink-0 text-tint-subtle"
/>
<div className="flex flex-col gap-1 text-tint">
<h3 className="line-clamp-2 font-medium text-sm text-tint">
<HighlightQuery
query={toolCall.query}
text={result.title}
/>
</h3>
{result.description && (
<p className="line-clamp-2 text-tint-subtle text-xs">
<Link
href={result.href}
className="flex items-start gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md px-3 py-2 transition-colors hover:bg-primary-hover"
>
<Icon
icon="memo"
className="mt-1 size-3 shrink-0 text-tint-subtle"
/>
<div className="flex flex-col gap-1 text-tint">
<h3 className="line-clamp-2 font-medium text-sm text-tint">
<HighlightQuery
query={toolCall.query}
text={result.description}
text={result.title}
/>
</p>
)}
</div>
<Icon
icon="chevron-right"
className="ml-auto size-3 shrink-0 self-center"
/>
</Link>
</li>
))}
</h3>
{result.description && (
<p className="line-clamp-2 text-tint-subtle text-xs">
<HighlightQuery
query={toolCall.query}
text={result.description}
/>
</p>
)}
</div>
<Icon
icon="chevron-right"
className="ml-auto size-3 shrink-0 self-center"
/>
</Link>
</li>
);
})}
</ol>
</div>
) : null}

Some files were not shown because too many files have changed in this diff Show More