Compare commits

..

324 Commits

Author SHA1 Message Date
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
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
Samy Pessé 7b34537bbe Version Packages (#3599)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-06 11:40:05 +02:00
conico974 145f385fdb Add pagination for llms-full.txt file (#3619)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-05 17:20:25 +02:00
Greg Bergé 360aa1c6d6 Upgrade Scalar dependencies (#3626) 2025-09-05 16:57:21 +02:00
conico974 ccc0975240 Fix error on parameters being an object instead of an array (#3623)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-05 16:17:50 +02:00
Zeno Kapitein 791135040a Add language selector to site header (#3622) 2025-09-05 12:15:07 +00:00
spastorelli 12c9d765ff Adapt OpenAPI to eval adaptive exprs & prefill TryIt config (#3607) 2025-09-05 13:39:12 +02:00
Greg Bergé 45207288a1 Support bold in headings (#3609) 2025-09-05 11:47:39 +02:00
Viktor Renkema 177afa5828 Handle non-defined objectFit on card cover images (#3625) 2025-09-05 10:42:57 +02:00
Zeno Kapitein d51b79edbb Fix "Search bar is broken on site with sections when header is disabled" (#3624) 2025-09-04 13:48:08 +02:00
conico974 a2720ac49d Update @opennextjs/cloudflare to version 1.7.1 (#3620)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-03 16:09:27 +02:00
Viktor Renkema 5f6853d812 Support new object-fit property on individual card cover image (#3594) 2025-09-03 09:11:17 +02:00
Zeno Kapitein 17dd382dc3 Add "original" color step and apply to bg-primary-solid elements (#3618) 2025-09-02 16:08:21 +02:00
Nolann B. 98e42cfe79 Handle OpenAPI ignored responses (#3614) 2025-09-01 14:25:35 +02:00
Samy Pessé c890e01004 Fix order in robots.txt preventing indexation of images by Google (#3617) 2025-09-01 11:21:11 +00:00
Samy Pessé 659be551bb Track insight event when embedded assistant is displayed (#3616) 2025-09-01 12:59:40 +02:00
Zeno Kapitein 78a632b7fe Fix Edit on GitHub button doesn't take you to GitHub (#3615) 2025-09-01 12:01:36 +02:00
spastorelli 4927e964b7 Add support for YAML content type in request body/example (#3612) 2025-09-01 10:56:09 +02:00
Samy Pessé 262a9b198b Fix embed script caching (#3611) 2025-08-29 22:35:14 +02:00
Samy Pessé 4f3588240c Unify layout and fix tracking of ask_question (#3610) 2025-08-29 16:10:28 +02:00
Nolann B. abbae3ec4d Remove scroll-nojump utility and update layout structure (#3494)
Co-authored-by: Zeno Kapitein <zeno@gitbook.io>
2025-08-29 11:22:55 +02:00
conico974 1b8609ac60 fix next start not working (#3608)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-28 16:36:06 +02:00
Samy Pessé 193d591e9f Use space.language as source of truth (#3606) 2025-08-27 18:27:29 +02:00
Zeno Kapitein 1edc5d622a Assistant hold message (#3603) 2025-08-27 13:48:57 +00:00
Zeno Kapitein 61b8507996 OpenAPI: Make empty responses clickable (#3605) 2025-08-27 13:38:24 +00:00
Zeno Kapitein 6f368b5cf3 Fix embed assistant window width on small screens (#3604) 2025-08-27 15:33:37 +02:00
Brett Jephson 9201e2cf52 Add vertical align property to column block (#3596)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-08-27 13:58:26 +01:00
Zeno Kapitein 2e0d706d43 Fix corner radius of active section on "Line" sidebar style (#3601) 2025-08-27 13:15:07 +02:00
Nolann B. 7fefe4919c Catch OpenAPI validation errors (#3602) 2025-08-27 12:29:50 +02:00
Zeno Kapitein 729921f338 Move "Edit on Git" and "Export PDF" actions into page actions dropdown (#3574) 2025-08-27 11:32:55 +02:00
Zeno Kapitein 1839ea2678 Fix content min-height with sections (#3597) 2025-08-27 11:30:22 +02:00
Zeno Kapitein f08dd29364 Fix Search results are not clickable (#3595) 2025-08-26 20:33:37 +00:00
conico974 ca2dfa3065 Rollback to Next.js 15.3 (#3600)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-26 18:26:32 +02:00
Utku Ufuk 10ded437f0 Turn on the "noUncheckedIndexedAccess" flag in all TS config files (#3587) 2025-08-26 19:25:54 +03:00
Zeno Kapitein d1fdc13441 Remove ligatures from Lato font (#3592) 2025-08-26 15:04:22 +00:00
Zeno Kapitein a8fca0e033 Fix custom assistant keyboard shortcut (#3598) 2025-08-26 14:57:08 +00:00
Samy Pessé 9251b33959 Version Packages (#3535)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-26 14:21:26 +02:00
Samy Pessé 81a6bd756a Embeddable GitBook API (#3593) 2025-08-26 14:08:24 +02:00
spastorelli ada195d329 Add gitbook/expr package (#3586) 2025-08-25 13:10:19 +02:00
conico974 1211ebea9b Proxy analytics events (#3570)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-25 11:34:54 +02:00
Zeno Kapitein 2e6e28eb54 Fix: Long strings overflow out of message bubble in docs assistant (#3588) 2025-08-22 19:23:05 +02:00
Zeno Kapitein 854c448bad Custom assistants followup (#3584) 2025-08-22 17:32:10 +02:00
conico974 64a0b0f169 Add logging for request duration and response details in 2c (#3583)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-22 17:15:00 +02:00
Zeno Kapitein 13ff22b6a8 Fix AI Search follow-up question closing search (#3585) 2025-08-22 14:51:21 +02:00
Samy Pessé 8927e8fbbb Embeddable Docs Assistant (#3572)
Co-authored-by: Zeno Kapitein <zeno@gitbook.io>
2025-08-22 09:44:51 +02:00
Samy Pessé 25e2b40d47 Update icons to latest version (#3582) 2025-08-21 15:31:13 +02:00
Zeno Kapitein a60af8ff9c Simplify tooltip implementation, add HoverCard (#3580) 2025-08-21 11:14:08 +02:00
Zeno Kapitein 27b328cfd7 Fix pageaside height on pages with hero cover (#3581)
Co-authored-by: Claire Chabas <claire.chabas@gmail.com>
2025-08-21 10:58:13 +02:00
conico974 fa6ef0c01c Remove motion animations from ScrollSectionList (#3573)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-20 19:14:01 +02:00
Zeno Kapitein 6217a2ed3b Page outline: scroll to active item (#3578) 2025-08-20 15:55:16 +02:00
Zeno Kapitein ffa866cb99 Fixes to search modal (#3577) 2025-08-20 10:50:58 +00:00
Zeno Kapitein 011aa931cf Integration tools styling (#3575)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-08-20 09:48:55 +00:00
Taran Vohra 68308154b5 Register custom assistant (#3569)
Co-authored-by: Zeno Kapitein <zeno@gitbook.io>
2025-08-19 12:07:47 +02:00
Zeno Kapitein 36af03f4d5 Pageaside fixes (#3567) 2025-08-18 18:50:00 +02:00
Samy Pessé cbc71a56b6 API to let integrations define custom tools for Docs Assistant (#3568) 2025-08-17 10:03:54 +02:00
Zeno Kapitein 1420180220 Assistant: Prevent sending queries multiple times (#3565) 2025-08-15 08:44:31 +02:00
Zeno Kapitein e1b2cf6d81 Fix page outline scroll (#3563) 2025-08-14 17:52:56 +02:00
conico974 c0e9c49679 Optimize cache retrieval performance and reduce costs on 2C (#3542)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-14 09:14:40 +02:00
conico974 afc4bd4415 Improve image resizing logic in fetchImage function (#3553)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-13 15:35:14 +02:00
Zeno Kapitein bcfa8d8b19 Improve vertical alignment of site items and fix floating page aside (#3560) 2025-08-13 14:36:07 +02:00
Zeno Kapitein 43766d6519 Fix custom logo not rendering on the published site (#3557) 2025-08-13 14:29:49 +02:00
Greg Bergé 42c17f5c74 Improve OpenAPI parsing errors (#3555) 2025-08-13 13:38:14 +02:00
Greg Bergé d655d3eece Support "objectivec" as alias for Objective C syntax (#3556) 2025-08-13 13:37:06 +02:00
conico974 3500de95e8 Never follow redirect in the middleware (#3552)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-13 12:58:43 +02:00
Taran Vohra 7f03b94949 Fix an issue where recommended questions werent using the current siteSpace context (#3559) 2025-08-13 11:06:05 +05:30
Zeno Kapitein f0cf060191 Disable AI Search tests for now (#3561) 2025-08-12 22:12:51 +02:00
Zeno Kapitein 5a137e7b14 Always show placeholder while generating (#3554) 2025-08-12 15:43:29 +02:00
conico974 28f7fbaa8a Fix image format handling and enhance PageCoverImage component (#3527)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-08 18:30:00 +02:00
Nolann B. ff96bb5787 Support new coverDefinitionDark for cards & image type (#3547) 2025-08-08 18:08:11 +02:00
conico974 87841124e0 Add HashContext and HashProvider for managing URL hash state (#3541)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-08 17:01:21 +02:00
conico974 a650b58276 feat: add tracing to server actions (#3543)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-08 16:10:06 +02:00
Zeno Kapitein 388b20d44f Clear AI chat stream properly (#3544) 2025-08-07 19:07:25 +02:00
Claire Chabas ea7e94fc51 Fix search bar layout shit caused by the ToC (#3545) 2025-08-07 15:19:20 +02:00
Zeno Kapitein d30bcbabdb Improve Button and ButtonGroup styling (#3540) 2025-08-06 16:10:55 +02:00
Zeno Kapitein fb858a1941 Tweaks to AIChatButton and AIChatInput (#3539) 2025-08-06 16:10:05 +02:00
Samy Pessé cc2e615b9a Emit a <link rel="alternate"> for the markdown version of a page (#3528) 2025-08-06 08:55:02 +02:00
Samy Pessé a862cde6b4 Use new API to fetch the reusable content document (#3536) 2025-08-06 08:54:43 +02:00
Zeno Kapitein e2b7bec162 Add linewrap to AI actions dropdown (#3534) 2025-08-05 12:17:00 +00:00
Zeno Kapitein c1b577ecc9 Updates to RU translation (#3532) 2025-08-05 14:10:45 +02:00
Zeno Kapitein ba7ec14f76 Fix z-index of search highlight (#3533) 2025-08-05 14:08:46 +02:00
Zeno Kapitein 9f0117723c Add russian translation (#3531) 2025-08-05 12:52:30 +02:00
conico974 f72b6a20db Validate 'next-action' header format in server action requests (#3523)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-05 11:31:43 +02:00
conico974 2f798209bb Bump OpenNext to 1.6.4 (#3520)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-05 11:18:56 +02:00
conico974 eec8f16424 Increase timeout for search answer visibility check (#3530)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-08-05 11:18:12 +02:00
Samy Pessé 885264f132 Version Packages (#3506)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-05 11:03:50 +02:00
Greg Bergé f1a6dec951 Less strict OpenAPI parser (#3529)
Co-authored-by: Nolann Biron <biron.nolann@gmail.com>
2025-08-05 08:45:45 +00:00
Claire Chabas 44f41510fa Fix close buttons tooltips (#3525) 2025-08-04 15:22:59 +02:00
spastorelli f220229e5e Fix stepper title misalignment (#3524) 2025-08-04 09:34:22 +02:00
Greg Bergé 61b4cb8d7c Upgrade Argos (#3522) 2025-08-01 16:07:35 +02:00
Greg Bergé 185cdb4883 Fix scrolling to anchor positionning (#3521) 2025-08-01 15:44:27 +02:00
Zeno Kapitein 2cdba53451 Upgrade to Tailwind v4 (#3206) 2025-08-01 13:03:44 +02:00
Greg Bergé 950f6c36cf greg/rnd 6997 fix long openapi responses label (#3518) 2025-07-31 12:08:57 +00:00
Zeno Kapitein ea37977b1e Fix inconsistent state between q= and ask= and keyboard navigation (#3519) 2025-07-31 13:20:27 +02:00
Greg Bergé 81da82aed7 Increase tests max duration (#3517) 2025-07-30 18:33:56 +00:00
Greg Bergé 39a3518651 Show a HoverCard when an internal link is broken (#3516) 2025-07-30 18:09:57 +02:00
spastorelli d903273759 Strip visitor.* param when normalizing URL (#3512) 2025-07-30 17:22:09 +02:00
Taran Vohra d130532f69 Suggest questions in the current space context (#3514) 2025-07-30 20:36:40 +05:30
Zeno Kapitein 94fcd21f34 Improve ?ask= init handling (#3513) 2025-07-30 13:17:15 +02:00
conico974 1d2db4ae2b Fix cookie handling for AI assistant server actions (#3509)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-30 12:29:51 +02:00
Steven H d270b4af9b Don't track events if a special middleware header is set. (#3511) 2025-07-30 09:26:01 +01:00
Zeno Kapitein ccfbd3c1a8 Fix AI Search Query (#3510) 2025-07-30 07:44:37 +00:00
Claire Chabas 5c4923557f Temporarily remove highlighting for PowerShell and C++ in Safari to avoid page crash (#3508) 2025-07-29 20:39:26 +02:00
Zeno Kapitein b1f608b865 Implement basic URL scheme for Assistant & AI Search (#3503) 2025-07-29 16:13:44 +02:00
conico974 c7993756dd Fix regional caching of tag cache (#3504)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-29 13:27:56 +02:00
Nolann B. d02465823f Prevent page breaks inside blocks when printing (#3507) 2025-07-28 18:08:08 +02:00
Greg Bergé 6b9f6b5b79 Improve french translations (#3501) 2025-07-28 14:05:04 +02:00
Taran Vohra df7de8fb0c Fix an issue where links were not rendering properly in Ask AI answers (#3505) 2025-07-28 16:43:18 +05:30
Samy Pessé 72f5ce3985 Version Packages (#3480)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-24 13:20:46 +02:00
conico974 04c80262e2 Make sure to use the correct version for adaptive content (#3487)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-24 11:40:12 +02:00
Valentino Hudhra 56d1b8fe2d Fix overflowing recommended follow up questions in assistant (#3502) 2025-07-24 11:03:04 +02:00
Zeno Kapitein 054a1634ab Improve AI Actions dropdown (#3499) 2025-07-24 10:40:13 +02:00
conico974 6ff63f2db6 Don't throw on >500 API error (#3500)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-24 09:41:47 +02:00
Greg Bergé b5ad0ce1e5 Fix OpenAPI v2 parsing (#3498) 2025-07-23 17:21:05 +02:00
Claire Chabas 2ba7e54c81 Fix headings styles in hint block (#3497) 2025-07-23 15:46:44 +02:00
Zeno Kapitein 0a858e74ad Fix React warning (#3496) 2025-07-23 10:56:12 +02:00
Zeno Kapitein b0c534fc2c Tweaks to AI Chat and AI Actions Dropdown (#3495) 2025-07-22 19:18:35 +02:00
Steven H 250c194a02 Add nosnippet to announcement banner. (#3490) 2025-07-22 15:03:35 +01:00
Zeno Kapitein 5b10cde936 Fix background corners showing through Powered through GitBook badge (#3493) 2025-07-22 15:15:38 +02:00
Zeno Kapitein 4aeb81bfa5 Fix search input styling & text cutoff (#3491) 2025-07-22 14:58:20 +02:00
conico974 955cebf944 Add cache tags for fetchFileSystem (#3489)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-22 13:41:31 +02:00
Greg Bergé 7c951ef437 Support new colors in text formatting (#3492) 2025-07-22 13:40:03 +02:00
Brett Jephson 68f0dbcc35 RND-7464: confirm in content kit button (#3488) 2025-07-22 10:51:25 +01:00
Zeno Kapitein 39c4f76aea Add fullwidth page option (#3344) 2025-07-21 11:20:17 +02:00
Zeno Kapitein 2fe58e8cbb Page actions customisation (#3485)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-07-18 21:10:38 +02:00
Zeno Kapitein 765141a4d8 Make "last updated" section optional (#3486) 2025-07-18 18:46:11 +00:00
Zeno Kapitein eb1bd3aa01 AI response feedback buttons (#3482) 2025-07-18 15:40:04 +00:00
Zeno Kapitein b7284b0223 Small tweaks to assistant styling (#3481) 2025-07-17 14:31:53 +00:00
conico974 5bbd254742 Support webp and avif formats for og image (#3478)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-17 11:36:07 +02:00
Nolann B. 611e28626a Remove padding on first Heading in Columns (#3479) 2025-07-16 12:51:37 +02:00
Samy Pessé 2a3bb0eb22 Version Packages (#3453)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-15 17:40:24 +02:00
conico974 6816f0fa5a Add browserslist and improve CSS masks for compatibility (#3471)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-15 17:15:56 +02:00
Greg Bergé 17386776ec Add missing changeset to release @gitbook/openapi-parser (#3477) 2025-07-15 12:01:40 +00:00
Nolann B. 9ee9082614 Improvement for CopyMarkdown action (#3465) 2025-07-15 13:41:25 +02:00
Nolann B. 334cfdd522 Fix responsive class for SearchInput button (#3476) 2025-07-15 12:50:57 +02:00
Greg Bergé 8c961a5dbd Fix OpenAPI spec validation (#3473) 2025-07-15 08:56:08 +02:00
conico974 4708956e77 Ensure schema.security is always an array (#3472)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-14 22:01:06 +02:00
Zeno Kapitein 813dd03dbb Fix AIChatInput focus & styling (#3470)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-07-14 11:48:34 +01:00
conico974 0522dbc61e Handle markdown errors gracefully (#3468)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-11 19:11:09 +02:00
Zeno Kapitein acb9f53156 Inline search modal (#3416) 2025-07-11 16:29:03 +01:00
conico974 6016846156 Add cache life inference from API response headers (#3467)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-11 17:10:00 +02:00
Greg Bergé cf1fae5837 Fix using CMD or CTRL to open in a new tab (#3466) 2025-07-11 14:25:37 +00:00
Nolann B. 7212345466 Fix AI Actions dropdown and LLM integration (#3464) 2025-07-11 12:14:06 +02:00
conico974 e38caf78ff Implement consistent logging across the application (#3368)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-11 10:36:03 +02:00
Greg Bergé 0a8dd67541 Better aria for breadcrumbs (#3461) 2025-07-11 09:45:14 +02:00
Nolann B. ed684c19a9 Move AI Actions markdown fetching to client-side (#3459) 2025-07-10 16:05:53 +02:00
Zeno Kapitein 52ab3681e3 Reposition AI Actions dropdown (#3460) 2025-07-09 14:38:23 +00:00
Zeno Kapitein 9cc5a787e9 Generalise keyboard shortcuts, add Cmd+J to AI Chat (#3457) 2025-07-09 12:13:36 +00:00
Nolann B. 0003030bf5 Implement AI actions dropdown (#3431) 2025-07-08 23:33:36 +02:00
Zeno Kapitein 9ad8f45d38 Rebrand assistant (#3458) 2025-07-08 21:31:14 +02:00
conico974 a3855257c8 Gracefully handle OpenAPI fetch errors (#3455)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-08 17:35:23 +02:00
conico974 9169c2f053 Fix site redirect resolution with length check for source paths (#3454)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-08 17:15:57 +02:00
Greg Bergé abe028bd38 Fix Link behaviour reloading the page (#3456) 2025-07-08 14:30:12 +00:00
conico974 12c2451f4f Bump OpenNext to latest versions (#3451)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-08 15:06:23 +02:00
Samy Pessé 8daede5427 Ensure all content links are resolved relatively to preview (#3450) 2025-07-08 13:05:31 +02:00
Samy Pessé 4c89aa60eb Adapt cache header parsing to support s-maxage (#3452) 2025-07-08 12:55:00 +02:00
Greg Bergé d0a3f64ae4 Always open header logo link in the current tab (#3445) 2025-07-08 09:02:49 +00:00
Samy Pessé 433d66482d Version Packages (#3446)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-08 11:01:57 +02:00
Greg Bergé bd553bcf9e Expose isJson and isYaml in @gitbook/openapi-parser (#3447) 2025-07-08 10:41:46 +02:00
Nolann B. f5894bcf74 Fix incorrect revision ID in Markdown fetch logic (#3448) 2025-07-08 09:41:12 +02:00
Zeno Kapitein 1b59e7ce36 Styling improvements to AI Chat Followup questions (#3444) 2025-07-07 19:02:01 +02:00
Samy Pessé 32d6f5845d Version Packages (#3247)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-07 12:49:53 +00:00
Greg Bergé 4f5cbfe732 Upgrade @scalar/openapi-parser (#3442) 2025-07-07 14:33:40 +02:00
Steven H afba8fcb34 Rating columns in tables now use a solid icons instead of unfilled. (#3434) 2025-07-07 10:05:45 +01:00
conico974 377a4899e4 Adjust cache life for error responses (#3423)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-07 09:38:48 +02:00
Greg Bergé 938bdeb34b Fix visual tests (#3440) 2025-07-06 09:02:31 +02:00
Greg Bergé d216eaa3f6 Upgrade Argos & disable bad plugin (#3438) 2025-07-05 21:37:20 +02:00
conico974 5ca5da0f21 Fix handling of bad requests to prevent unnecessary 500 errors (#3432)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-04 17:06:37 +02:00
Greg Bergé ebe6eb3b39 Support custom monospace fonts (#3430) 2025-07-04 13:13:36 +00:00
Zeno Kapitein 3bfe347096 Show section tabs when there is a single section group present (#3387) 2025-07-04 14:55:40 +02:00
Samy Pessé 7b38f89078 Update API client and condition for AI assistant (#3429) 2025-07-03 20:34:26 +02:00
Greg Bergé 09b689b0ea Fix table display in print (#3428) 2025-07-03 17:44:55 +02:00
Nolann B. 2350baa75f Support for OpenAPI Array request body (#3420) 2025-07-03 17:18:26 +02:00
Nolann B. bc1eca815e Error handling for AI chat (#3411) 2025-07-03 14:26:40 +02:00
conico974 78c10340e7 Increase max line length for code highlighting (#3415)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-03 13:05:42 +02:00
Valentino Hudhra c16890a3a6 Align README with CONTRIBUTING guides regarding bun version (#3427) 2025-07-03 12:40:08 +02:00
Addison 3b4fe2827a Update community ad on sponsored/community sites (#3421) 2025-07-03 11:03:16 +02:00
Samy Pessé 7e807aabb5 Don't prevent sending message in AI chat when loading followup suggestions (#3426) 2025-07-03 10:16:42 +02:00
Samy Pessé 973c74ee69 Adapt AI assistant to upcoming API changes (#3422) 2025-07-02 20:58:23 +02:00
Zeno Kapitein efed0b0617 Turn on Docs Assistant for another site (#3425) 2025-07-02 18:02:12 +02:00
Zeno Kapitein ca3b9aca5d Improve AI Chat context popup (#3424) 2025-07-02 18:01:24 +02:00
Taran Vohra e2afc07ab2 Fix resolution of page by resolving site redirects before space redirects (#3414) 2025-07-02 17:30:18 +05:30
Brett Jephson e8fb84d362 Remove flip hash from columns (#3413) 2025-07-02 09:41:53 +01:00
Samy Pessé 57f951a7d9 Remove AI page link summaries (#3418) 2025-07-02 08:40:55 +02:00
conico974 28008667ed Make Emoji component synchronous (#3417)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-01 17:10:16 +02:00
Samy Pessé f3affc3034 Add support for MCP tools (#3410) 2025-07-01 09:58:59 +02:00
conico974 59da30f3c6 Add support for cover image repositioning (#3404)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-30 19:07:48 +02:00
Greg Bergé 2db721112a Fix OpenAPI error (#3408) 2025-06-30 15:56:22 +00:00
Samy Pessé b60039b7d5 Fix links to other spaces in a section (#3409)
Co-authored-by: Steven H <steven@gitbook.io>
2025-06-30 17:47:23 +02:00
Steven H 8fe9c9afea Fix an issue where same-site references across spaces created invalid links. (#3407) 2025-06-30 16:03:27 +01:00
Zeno Kapitein 216ba7a556 Add analytics on AI chat (#3405) 2025-06-30 08:41:12 +00:00
Samy Pessé a2ff57081b Fix changesets 2025-06-29 10:41:30 +02:00
Zeno Kapitein 40dbd9a14b AI Chat followup (#3403) 2025-06-28 17:55:56 +02:00
Nolann B. e5bac69c5c Fix markdown page generation for groups (#3402) 2025-06-27 23:32:35 +02:00
Zeno Kapitein 8d6598393f AI Chat UI (#3397)
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-06-27 17:43:14 +02:00
Greg Bergé 59b86eb9dc Correctly handle OpenAPI errors (#3401) 2025-06-27 11:15:21 +02:00
Steven H 52c9f6da2f Fix an issue with link resolution in cross-space reusable content. (#3392)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-06-27 11:14:38 +02:00
Nolann B. dd65987c94 Include page group children under the .md route (#3400) 2025-06-27 09:53:18 +02:00
conico974 ace61901a4 Fix ScrollSectionsList with suspended component (#3390)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-26 15:37:51 +02:00
conico974 e6c3c7635d refactor toc components to client (#3394)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
Co-authored-by: Samy Pessé <samypesse@gmail.com>
2025-06-26 11:35:02 +02:00
conico974 5134d9e7f8 Avoid unnecessary proxy URL lookups (#3377)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-25 17:21:09 +02:00
conico974 6821fb2937 Downgrade esbuild to fix wrangler dev issues (#3396)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-25 16:50:09 +02:00
Taran Vohra cfe4812045 Replace all usage of getPublishedContentByUrl (#3395) 2025-06-25 18:55:02 +05:30
Samy Pessé 14843ac343 Fix changesets (#3393) 2025-06-25 14:42:49 +02:00
Samy Pessé 8fb6d7465f Remove v1 and replace by v2 (#3391)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-25 13:28:04 +02:00
Samy Pessé 0ef647586f Hooks and APIs for AI v2 (#3385) 2025-06-25 09:48:28 +02:00
Nolann B. b4039627d9 Handle nullish OpenAPI mediaTypeObject (#3389) 2025-06-24 17:25:38 +00:00
Addison 4a295e6db4 Update contributing guide (#3381) 2025-06-24 17:55:24 +02:00
Greg Bergé 5726999a5a Fix viewing a page from a revision (#3388) 2025-06-24 15:31:06 +00:00
conico974 58d7f3c140 Fix revision ID handling for computed content (#3386)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-24 15:00:33 +02:00
Samy Pessé 07eab986d5 Optimize fetching of revisions (#3382) 2025-06-23 19:15:17 +02:00
Samy Pessé 711cf38f9b Use getRevision in getRevisionFile (#3379) 2025-06-23 18:43:41 +02:00
Zeno Kapitein 4f7c0eea9f Clicking an active TOC item toggles its descendants (#3378) 2025-06-23 17:27:34 +02:00
conico974 f3448de2da Improve efficiency of the queue (#3373)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-23 15:09:29 +02:00
Viktor Renkema ec92075ef8 Set tw classes correctly for horizontal alignment in tables (#3375) 2025-06-23 14:35:32 +02:00
Nolann B. 1e013cd87c Optional label in OpenAPI x-codeSamples (#3376) 2025-06-23 13:54:15 +02:00
Samy Pessé f033734308 Support for site customization option to change how external links open (#3372) 2025-06-22 15:49:15 +02:00
Nolann B. 50c05164f7 Bump @scalar/api-client-react (#3371) 2025-06-22 12:03:10 +02:00
conico974 7fb9004b43 Properly redirect to the root page (#3369)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-20 19:15:21 +02:00
Greg Bergé 4721403986 Hide scrollbar on sections (#3367) 2025-06-20 12:31:18 +00:00
Samy Pessé df848ef64e Icon in buttons and text alignment (#3365) 2025-06-20 12:08:28 +02:00
conico974 0b40ccd286 Remove buildId from composable cache key generation (#3366)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-20 12:06:44 +02:00
Greg Bergé caaa692f0b Allow to zoom image on mobile if relevant (#3364) 2025-06-20 10:25:58 +02:00
Greg Bergé 42d43e09c4 Always show scrollbars (#3363) 2025-06-20 08:41:21 +02:00
Samy Pessé 7ccbf55ba7 Set myself as codeowner for data cache (#3355) 2025-06-20 08:41:02 +02:00
conico974 81f5bfc2f9 Filter out malicious request (#3357)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-19 17:00:37 +02:00
Greg Bergé a7a713bca2 Scroll to active TOC when clicking a link (#3362) 2025-06-19 16:50:13 +02:00
conico974 d6613c787f Make skeletons client component (#3361)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-19 16:46:31 +02:00
Greg Bergé 427f748e1c Add metadata for adding site to Apple devices home (#3360) 2025-06-19 16:35:07 +02:00
Greg Bergé 4f5fec7213 Fix CodeBlock rendering (#3359) 2025-06-19 15:55:19 +02:00
Greg Bergé 8c0a53ab40 Fix page group not expanded by default (#3358) 2025-06-19 15:08:59 +02:00
conico974 2dfd5aab20 Update SpacesDropdown to manage active state based on current space (#3356)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-19 13:39:59 +02:00
conico974 8d3c6562a2 Implement a StylesProvider (#3352)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-18 19:02:32 +02:00
Zeno Kapitein dae019c115 Consistently show variant selector in section bar if site has sections (#3349) 2025-06-18 17:48:49 +02:00
Steven H 73e0cbb2d6 Fix an issue where PDF export URLs were not bringing their query params. (#3351) 2025-06-18 13:26:20 +01:00
Steven H 392f59450c Improve performance of InlineLinkTooltip (#3339) 2025-06-18 12:15:45 +01:00
conico974 b4918f60ce fix variant space change when pathname equals space-path (#3343)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-06-17 22:31:32 +02:00
Samy Pessé 8f7c304d58 Fix crash when rendering ogimage with invalid icon (#3348) 2025-06-17 20:25:21 +02:00
Samy Pessé a3a944d7dd Fix crash during rendering of ogimage for VA sites with default icon (#3347) 2025-06-17 19:38:47 +02:00
Samy Pessé 88a35ed057 Fix crash when integration is triggering invalid requests (#3346) 2025-06-17 19:23:52 +02:00
Samy Pessé 67998b6f15 Fix ogimage generation failing with some JPEG images (#3345) 2025-06-17 19:10:15 +02:00
Samy Pessé ff6d1150a5 Use getRevisionPageDocument on v2 (#3342) 2025-06-17 16:12:58 +02:00
Samy Pessé 500c8cb649 Don't crash ogimage generation on RTL text (#3341) 2025-06-17 13:04:50 +02:00
691 changed files with 31136 additions and 15476 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix UX issue about highlighting the search term in search result sections
-9
View File
@@ -1,9 +0,0 @@
---
"gitbook": patch
---
Fix three small visual issues
- Fix sidebar showing on `no-toc` pages in the gradient theme
- Fix variant selector truncating incorrectly in header when sections are present
- Fix page cover alignment on `lg` screens without TOC
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Respect fullWidth and defaultWidth for images
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
Add docs.testgitbook.com to ADAPTIVE_CONTENT_HOSTS list
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
fix nested a tag causing hydration error
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
fix href being empty in TOC
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix navigation between sections/variants when previewing a site in v2
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Add support for inline icons.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Add circular corners and depth styling
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix crash when integration script fails to render block.
-5
View File
@@ -1,5 +0,0 @@
---
'@gitbook/react-openapi': patch
---
Add authorization header for OAuth2
-6
View File
@@ -1,6 +0,0 @@
---
'@gitbook/react-openapi': patch
'gitbook': patch
---
Improve support for OAuth2 security type
-6
View File
@@ -1,6 +0,0 @@
---
"gitbook-v2": patch
"gitbook": patch
---
Adds Columns layout block to GBO
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Support dark-mode specific page cover image
-5
View File
@@ -1,5 +0,0 @@
---
'gitbook': patch
---
Update Models page styling
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
Optimize performances by using a smarter per-request cache arround data cached functions
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
add a force-revalidate api route to force bust the cache in case of errors
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Fix rendering of ogimage with SVG logos.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Best effort at preserving current variant when navigating between sections by matching the pathname against site spaces in the new section.
-6
View File
@@ -1,6 +0,0 @@
---
"gitbook-v2": patch
"gitbook": patch
---
encode customization header
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": minor
---
Rework full-width layout, add support for full-width page option
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
fix ISR on preview env
-6
View File
@@ -1,6 +0,0 @@
---
"gitbook-v2": patch
"gitbook": patch
---
Fix concurrent execution in Vercel causing pages to not be attached to the proper tags.
-5
View File
@@ -1,5 +0,0 @@
---
"@gitbook/react-contentkit": patch
---
Add basic error handling when transitioning between states.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
Generate a llms-full.txt version of the docs site
-5
View File
@@ -1,5 +0,0 @@
---
"@gitbook/fonts": minor
---
Initial version of the package
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix rendering of ogimage when logo or icon are AVIF images.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Add margin to adjacent buttons
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
Don't cache unexpected API errors for more than a few minutes.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
add a global error boundary
-5
View File
@@ -1,5 +0,0 @@
---
'@gitbook/react-openapi': patch
---
Indent JSON python code sample
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix missing title on button to close the announcement banner.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Make icons for page groups more contrasting
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Ignore case while highlighting search results.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Make TOC height dynamic based on visible header and footer elements
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix bold header links hover color
-6
View File
@@ -1,6 +0,0 @@
---
"gitbook": patch
"gitbook-v2": patch
---
cache fonts and static image used in OGImage in memory
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix border being added to cards
-5
View File
@@ -1,5 +0,0 @@
---
'@gitbook/react-openapi': patch
---
Handle nested deprecated properties in generateSchemaExample
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
Add `urlObject.hash` to `linker.toLinkForContent` to pass through URL fragment identifiers, used in search
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
apply customization for dynamic context
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Reverse order of feedback smileys
-5
View File
@@ -1,5 +0,0 @@
---
'@gitbook/react-openapi': patch
---
Deduplicate path parameters from OpenAPI spec
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook": patch
---
Fix ogimage using incorrect Google Font depending on language.
-5
View File
@@ -1,5 +0,0 @@
---
"gitbook-v2": patch
---
remove trailing slash from linker
+3
View File
@@ -0,0 +1,3 @@
# Changes to the API data cache functions can invalidate all existing data cache
# causing a massive amount of revalidation, impacting our API.
packages/gitbook/src/lib/data/api.ts @SamyPesse
+4 -9
View File
@@ -71,14 +71,9 @@ nvm use
bun install
```
3. Build the project:
3. Start the development server:
```bash
bun build:v2
```
4. Start the development server:
```bash
bun dev:v2
bun dev
```
Additional development commands:
@@ -89,9 +84,9 @@ Additional development commands:
#### 4. Preview your changes
When running the development server, published GitBook sites can be rendered through your local version at `http://localhost:3000/`.
When running the development server, published GitBook sites can be rendered through your local version at `http://localhost:3000/url`.
For example, our published docs can be viewed using the local version by visiting `http://localhost:3000/gitbook.com/docs` after running the development server.
For example, our published docs can be viewed using the local version by visiting `http://localhost:3000/url/gitbook.com/docs` after running the development server.
You can visit any published GitBook site behind your development server. Please make sure your site is [published publicly](https://gitbook.com/docs/published-documentation/publish-your-content-as-a-docs-site) to ensure you can view the site correctly in your development version.
@@ -32,7 +32,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: deployments status --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
command: deployments status --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
# This step is used to get the version ID that is currently deployed to Cloudflare.
- id: extract_current_version
@@ -51,7 +51,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
# Since we use version overrides headers, we can directly deploy the middleware to 100%.
- id: deploy_middleware
@@ -63,7 +63,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook-v2/openNext/customWorkers/middlewareWrangler.jsonc
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
- name: Deploy server to Cloudflare at 100%
uses: cloudflare/wrangler-action@v3.14.0
@@ -73,7 +73,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
- name: Outputs
shell: bash
@@ -1,37 +0,0 @@
name: 'Setup Playwright'
description: 'Install Playwright and dependencies'
runs:
using: 'composite'
steps:
# Run npm ci and get Playwright version
- name: 🏗 Prepare Playwright env
shell: bash
run: |
PLAYWRIGHT_VERSION=$(npm ls --json @playwright/test | jq --raw-output '.dependencies["gitbook"].dependencies["@playwright/test"].version')
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
# Cache browser binaries, cache key is based on Playwright version and OS
- name: 🧰 Cache Playwright browser binaries
uses: actions/cache@v4
id: playwright-cache
with:
path: '~/.cache/ms-playwright'
key: '${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}'
restore-keys: |
${{ runner.os }}-playwright-
# Install browser binaries & OS dependencies if cache missed
- name: 🏗 Install Playwright browser binaries & OS dependencies
if: steps.playwright-cache.outputs.cache-hit != 'true'
shell: bash
working-directory: packages/gitbook
run: |
bun x playwright install --with-deps chromium
# Install only the OS dependencies if cache hit
- name: 🏗 Install Playwright OS dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
shell: bash
working-directory: packages/gitbook
run: |
bun x playwright install-deps
@@ -59,7 +59,7 @@ runs:
GITBOOK_ASSETS_PREFIX: ${{ inputs.opItem }}/GITBOOK_ASSETS_PREFIX
GITBOOK_FONTS_URL: ${{ inputs.opItem }}/GITBOOK_FONTS_URL
- name: Build worker
run: bun run turbo build:v2:cloudflare
run: bun run turbo build:cloudflare
env:
GITBOOK_RUNTIME: cloudflare
shell: bash
@@ -72,7 +72,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: deploy --config ./packages/gitbook-v2/openNext/customWorkers/doWrangler.jsonc
command: deploy --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc
- id: upload_server
name: Upload server to Cloudflare
@@ -83,7 +83,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/openNext/customWorkers/defaultWrangler.jsonc
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
- name: Extract server version worker ID
shell: bash
@@ -95,7 +95,7 @@ runs:
- name: Run updateWrangler scripts
shell: bash
run: |
bun run ./packages/gitbook-v2/openNext/customWorkers/script/updateWrangler.ts ${{ steps.extract_server_version_id.outputs.version_id }}
bun run ./packages/gitbook/openNext/customWorkers/script/updateWrangler.ts ${{ steps.extract_server_version_id.outputs.version_id }}
- id: upload_middleware
name: Upload middleware to Cloudflare
@@ -106,7 +106,7 @@ runs:
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/openNext/customWorkers/middlewareWrangler.jsonc
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
- name: Extract middleware version worker ID
shell: bash
+56 -134
View File
@@ -7,53 +7,6 @@ on:
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v1-cloudflare:
name: Deploy v1 to Cloudflare Pages
runs-on: ubuntu-latest
environment:
name: ${{ github.ref == 'refs/heads/main' && '1c-production' || '1c-preview' }}
url: ${{ steps.deploy.outputs.deployment-url }}
permissions:
contents: read
deployments: write
issues: write
pull-requests: write
checks: write
statuses: write
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
deployment-alias-url: ${{ steps.deploy.outputs.deployment-alias-url }}
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: Sets env vars for production
if: github.ref == 'refs/heads/main'
run: |
echo "GITBOOK_ASSETS_PREFIX=https://static.gitbook.com" >> $GITHUB_ENV
- name: Build Next.js with next-on-pages
run: bun run turbo gitbook#build:cloudflare
env:
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: ${{ secrets.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY }}
GITBOOK_RUNTIME: cloudflare
- id: deploy
name: Deploy to Cloudflare
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: ./
wranglerVersion: '3.112.0'
command: pages deploy ./packages/gitbook/.vercel/output/static --project-name=${{ vars.CLOUDFLARE_PROJECT_NAME }} --branch=${{ github.ref == 'refs/heads/main' && 'main' || format('pr{0}', github.event.pull_request.number) }}
- name: Outputs
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
echo "Alias URL: ${{ steps.deploy.outputs.deployment-alias-url }}"
deploy-v2-vercel:
name: Deploy v2 to Vercel (preview)
runs-on: ubuntu-latest
@@ -114,7 +67,6 @@ jobs:
name: Comment Deployments (preview)
if: always() && !startsWith(github.ref, 'refs/heads/main')
needs:
- deploy-v1-cloudflare
- deploy-v2-vercel
- deploy-v2-cloudflare
steps:
@@ -134,15 +86,6 @@ jobs:
body: |
Summary of the deployments:
### Version 1
| Version | URL | Status |
| --- | --- | --- |
| Latest commit | [${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}](${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}) | ${{ needs.deploy-v1-cloudflare.result == 'success' && '✅' || '❌' }} |
| PR | [${{ needs.deploy-v1-cloudflare.outputs.deployment-alias-url }}](${{ needs.deploy-v1-cloudflare.outputs.deployment-alias-url }}) | ${{ needs.deploy-v1-cloudflare.result == 'success' && '✅' || '❌' }} |
### Version 2
| Version | URL | Status |
| --- | --- | --- |
| Vercel | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}](${{ needs.deploy-v2-vercel.outputs.deployment-url }}) | ${{ needs.deploy-v2-vercel.result == 'success' && '✅' || '❌' }} |
@@ -150,35 +93,16 @@ jobs:
### Test content
| Site | `v1` | `2v` | `2c` |
| --- | --- | --- | --- |
| GitBook | [${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}/gitbook.com/docs](${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}/gitbook.com/docs) | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs) |
| E2E | [${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}/gitbook.gitbook.io/test-gitbook-open) | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) |
| Site | `2v` | `2c` |
| --- | --- | --- |
| GitBook | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs) |
| E2E | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) |
edit-mode: replace
visual-testing-v1:
runs-on: ubuntu-latest
name: Visual Testing v1
needs: deploy-v1-cloudflare
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Run Playwright tests
run: bun e2e
env:
BASE_URL: ${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
visual-testing-v2-vercel:
runs-on: ubuntu-latest
name: Visual Testing v2
needs: deploy-v2-vercel
timeout-minutes: 10
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -186,8 +110,8 @@ jobs:
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e
env:
@@ -199,7 +123,7 @@ jobs:
runs-on: ubuntu-latest
name: Visual Testing v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 10
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -207,8 +131,8 @@ jobs:
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e
env:
@@ -216,31 +140,11 @@ jobs:
SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'v2-cloudflare'
visual-testing-customers-v1:
runs-on: ubuntu-latest
name: Visual Testing Customers v1
needs: deploy-v1-cloudflare
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Run Playwright tests
run: bun e2e-customers
env:
BASE_URL: ${{ needs.deploy-v1-cloudflare.outputs.deployment-url }}
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'customers-v1'
visual-testing-customers-v2:
runs-on: ubuntu-latest
name: Visual Testing Customers v2
needs: deploy-v2-vercel
timeout-minutes: 10
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -248,8 +152,8 @@ jobs:
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e-customers
env:
@@ -261,27 +165,7 @@ jobs:
runs-on: ubuntu-latest
name: Visual Testing Customers v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Run Playwright tests
run: bun e2e-customers
env:
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-v1:
runs-on: ubuntu-latest
name: PageSpeed Testing v1
needs: deploy-v1-cloudflare
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -291,8 +175,46 @@ jobs:
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run pagespeed tests
run: bun ./packages/gitbook/tests/pagespeed-testing.ts
- name: Run Playwright tests
run: bun e2e-customers
env:
BASE_URL: ${{needs.deploy-v1-cloudflare.outputs.deployment-url}}
PAGESPEED_API_KEY: ${{ secrets.PAGESPEED_API_KEY }}
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'
browserless-testing-v2-vercel:
runs-on: ubuntu-latest
name: Browserless Testing v2 (Vercel)
needs: deploy-v2-vercel
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-vercel.outputs.deployment-url}}
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/
+1 -19
View File
@@ -39,22 +39,4 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
release-preview:
# For now it releases the cache-do to both preview and production
# Once we changed to deploy the app only on release, we should change `release:preview` in `cache-do`
name: Release Preview
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Release preview packages
run: bun run release:preview
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+8 -14
View File
@@ -1,7 +1,7 @@
<h1 align="center">GitBook</h1>
<p align="center">
<a href="https://gitbook.com/docs/">Docs</a> - <a href="https://github.com/GitbookIO/community">Community</a> - <a href="https://developer.gitbook.com/">Developer Docs</a> - <a href="https://changelog.gitbook.com/">Changelog</a> - <a href="https://github.com/GitbookIO/gitbook/issues/new?assignees=&labels=bug&template=bug_report.md">Bug reports</a>
<a href="https://gitbook.com/docs/">Docs</a> - <a href="https://github.com/GitbookIO/community">Community</a> - <a href="https://developer.gitbook.com/">Developer Docs</a> - <a href="https://changelog.gitbook.com/">Changelog</a> - <a href="https://github.com/GitbookIO/gitbook/issues/new?assignees=&labels=bug&template=bug_report.md">Bug reports</a> - <a href="https://github.com/orgs/GitbookIO/discussions/categories/feature-requests">Feature requests</a>
</p>
<p align="center">
@@ -35,10 +35,10 @@ To run a local version of this project, please follow these simple steps.
### Prerequisites
- Node.js (Version: >=20.6)
- Use nvm for easy Node management
- Bun (Version: >=1.2.1)
- We use a text-based lockfile which isn't supported below 1.2.1
- 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
### Set up
@@ -56,19 +56,13 @@ git clone https://github.com/gitbookIO/gitbook.git
bun install
```
4. Run build.
4. Start your local development server.
```
bun build:v2
bun dev
```
5. Start your local development server.
```
bun dev:v2
```
6. Open a published GitBook space in your web browser, prefixing it with `http://localhost:3000/`.
6. Open a published GitBook space in your web browser, prefixing it with `http://localhost:3000/url`.
examples:
+3 -2
View File
@@ -18,10 +18,12 @@
"**/.vercel/**/*",
"**/.cache/**/*",
"**/.wrangler/**/*",
"packages/embed/standalone/**/*",
"packages/openapi-parser/src/fixtures/**/*",
"packages/emoji-codepoints/index.ts",
"packages/icons/src/data/*.json",
"packages/cache-do/worker-configuration.d.ts"
"packages/gitbook/worker-configuration.d.ts",
"**/*.css"
]
},
"formatter": {
@@ -127,7 +129,6 @@
{
"include": [
"packages/gitbook/**/*",
"packages/gitbook-v2/**/*",
"packages/react-openapi/**/*",
"packages/react-math/**/*",
"packages/react-contentkit/**/*",
+1116 -859
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -3,22 +3,21 @@
"version": "0.1.0",
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@changesets/cli": "^2.27.12",
"turbo": "^2.5.0",
"vercel": "^39.3.0"
"@changesets/cli": "^2.29.7",
"turbo": "^2.5.8",
"vercel": "^39.4.2"
},
"packageManager": "bun@1.2.15",
"overrides": {
"@codemirror/state": "6.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"esbuild": "0.24.2"
},
"private": true,
"scripts": {
"dev": "turbo run dev",
"dev:v2": "turbo run dev:v2",
"dev": "turbo run dev --concurrency 20",
"build": "turbo run build",
"build:v2": "turbo run build:v2",
"clean-deps": "rm -rf node_modules && rm -rf packages/*/node_modules",
"typecheck": "turbo run typecheck",
"format": "biome check --write ./",
@@ -27,16 +26,18 @@
"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",
"release:preview": "turbo run release:preview",
"changeset-version": "changeset version && bun run format && bun update",
"release": "turbo run release && bun run publish-all-packages",
"publish-all-packages": "for dir in packages/*; do (cd \"$dir\" && bun publish || true); done && changeset tag",
"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.121.0"
"@gitbook/api": "0.143.2",
"bidc": "^0.0.2",
"tsdown": "^0.15.6"
}
},
"patchedDependencies": {
+1
View File
@@ -0,0 +1 @@
dist/
+13
View File
@@ -0,0 +1,13 @@
# @gitbook/browser-types
## 0.1.0
### Minor Changes
- cbc71a5: First version of the public package for typing script integrations.
### Patch Changes
- 854c448: Custom assistants followup
- Updated dependencies [25e2b40]
- @gitbook/icons@0.3.0
+3
View File
@@ -0,0 +1,3 @@
# `@gitbook/browser-types`
Typescript types for the global variables available in a GitBook website. These types can be used by integrations embedding scripts.
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@gitbook/browser-types",
"description": "Typescript types for the global variables available in a GitBook website. These types can be used by integrations embedding scripts.",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"version": "0.1.0",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:"
},
"devDependencies": {
"typescript": "^5.5.3"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit"
},
"files": ["dist", "README.md", "CHANGELOG.md"]
}
+84
View File
@@ -0,0 +1,84 @@
import type { AIToolCallResult, AIToolDefinition } from '@gitbook/api';
import type { IconName } from '@gitbook/icons';
export type GitBookIntegrationEvent = 'load' | 'unload';
export type GitBookIntegrationEventCallback = (...args: any[]) => void;
export type GitBookIntegrationTool = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
*/
confirmation?: {
icon?: IconName;
label: string;
};
/**
* Callback when the tool is executed.
* The input is provided by the AI assistant following the input schema of the tool.
*/
execute: (input: object) => Promise<Pick<AIToolCallResult, 'output' | 'summary'>>;
};
export type GitBookAssistant = {
/**
* Name of the assistant displayed in the UI.
*/
label: string;
/**
* Icon of the assistant displayed in the UI.
* Any FontAwesome icon name is supported.
* @example 'sparkle'
*/
icon: string;
/**
* Callback when the assistant is opened.
*/
open: (query?: string) => void;
/**
* Whether to display the triggers for this assistant in the UI.
* @default true
*/
ui?: boolean;
};
export type GitBookGlobal = {
/**
* Register an event listener.
*/
addEventListener: (
type: GitBookIntegrationEvent,
func: GitBookIntegrationEventCallback
) => void;
/**
* Remove an event listener.
*/
removeEventListener: (
type: GitBookIntegrationEvent,
func: GitBookIntegrationEventCallback
) => void;
/**
* Register a custom tool to be exposed to the AI assistant.
*/
registerTool: (tool: GitBookIntegrationTool) => void;
/**
* Register a custom assistant to be available on the site.
*/
registerAssistant: (assistant: GitBookAssistant) => () => void;
};
declare global {
interface Window {
/**
* Global `window.GitBook` object accessible by integrations.
*/
GitBook?: GitBookGlobal;
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
-3
View File
@@ -1,3 +0,0 @@
.wrangler
worker-configuration.d.ts
dist/
-18
View File
@@ -1,18 +0,0 @@
# @gitbook/cache-do
## 0.1.1
### Patch Changes
- b7a5106: Disable cloudflare observability in production
## 0.1.0
### Minor Changes
- 9b8d519: Experiment with optimizing billable duration in Cloudflare by using multiple RPC sessions instead of one
- 636b868: First version of a new cache backend powered by Cloudflare Durable Objects
### Patch Changes
- 56f5fa1: Enable Workers observability with a sampling of 0.1
-22
View File
@@ -1,22 +0,0 @@
# `@gitbook/cache-do`
Cache backend, powered by Cloudflare Durable Objects. The cache is optimized for GitBook use-cases.
### Performances
The cache backend is optimized for performances by being distributed and accessible close to the worker locations that are reading it.
### Geo-distribution
To achieve a good balance between **performances** and **consistency**, cache objects are distributed over 7 locations, representing continents.
It makes it possible to purge all 7 locations in one go and achieve fast consistency.
### Concepts
**Cache tag**: unique tag in the cache environment. A cache tag groups multiple keys that should be purged together in one operation.
Cache tags should not contain a large set of unique keys. Exceeding thousands could lead to performances or reliability issues.
**Cache key**: unique key in the cache environment. Each key should be assigned to a `tag`.
**Location**: cache is distributed over 7 unique locations, one for each continent.
-35
View File
@@ -1,35 +0,0 @@
{
"name": "@gitbook/cache-do",
"type": "module",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
},
"./api": {
"types": "./dist/api.d.ts",
"development": "./src/api.ts",
"default": "./dist/api.js"
}
},
"version": "0.1.1",
"dependencies": {
"@msgpack/msgpack": "^3.0.0-beta2",
"lru_map": "^0.4.1"
},
"devDependencies": {
"typescript": "^5.5.3",
"wrangler": "^4.10.0"
},
"scripts": {
"generate": "wrangler types",
"build": "tsc",
"typecheck": "tsc --noEmit",
"dev": "tsc -w",
"release": "wrangler deploy",
"release:preview": "wrangler deploy && wrangler deploy --env preview"
},
"files": ["dist", "src", "bin", "data", "README.md", "CHANGELOG.md"]
}
-292
View File
@@ -1,292 +0,0 @@
import { DurableObject } from 'cloudflare:workers';
import { decode, encode } from '@msgpack/msgpack';
import { LRUMap } from 'lru_map';
export interface CacheObjectDescriptor {
get: <Value = unknown>(key: string) => Promise<Value | undefined>;
set: <Value = unknown>(key: string, value: Value, expiresAt: number) => Promise<void>;
}
/**
* Value stored in a chunked binary msgpack format.
* Stored under the key `prop.${key}.${index}`.
*/
interface CacheObjectProp<Value = unknown> {
value: Value;
expiresAt: number;
}
/**
* Expiration clock stored under the key `exp.${expiresAt}.${key}`.
*/
interface CacheObjectExp {
/** Key of the property */
k: string;
/** Number of chunks */
c: number;
}
/**
* Durable Object class being deployed as a distributed cache.
*/
export class CacheObject extends DurableObject {
private lru = new LRUMap<string, { match: CacheObjectProp | undefined }>(500);
/**
* Open a descriptor to access the cache object.
* The goal is to minimize the amount of RPC sessions between the client and the cache object.
* One session is opened per request on the client side and used to perform multiple operations.
* https://developers.cloudflare.com/workers/runtime-apis/rpc/#return-functions-from-rpc-methods
*/
public open(): CacheObjectDescriptor {
return {
get: async <Value = unknown>(key: string) => {
return this.get<Value>(key);
},
set: async <Value = unknown>(key: string, value: Value, expiresAt: number) => {
await this.set(key, value, expiresAt);
},
};
}
/**
* Get the value of a property.
*/
public async get<Value = unknown>(key: string) {
return this.logOperation({ operation: 'get', key }, async (setLog) => {
// Try the memory state first.
const memoryEntry = this.lru.get(key);
if (memoryEntry) {
setLog({ memory: true });
setLog({ memoryMatch: !!memoryEntry.match });
if (!memoryEntry.match) {
return;
}
const isExpired = memoryEntry.match.expiresAt < Date.now();
setLog({ memoryExpired: isExpired });
if (!isExpired) {
return memoryEntry.match.value as Value;
}
}
return await this.getFromStorage<Value>(key);
});
}
/**
* Get the value of a property from the DO storage.
*/
public async getFromStorage<Value = unknown>(key: string) {
return this.logOperation({ operation: 'getFromStorage', key }, async (setLog) => {
const entries = await this.ctx.storage.list<Uint8Array>({
prefix: getStoragePropKey(key),
noCache: true,
});
if (entries.size) {
const entry = decodeChunks<CacheObjectProp<Value>>(entries);
setLog({ chunks: entries.size, chunksSize: entry?.size ?? 0 });
if (entry && entry.value.expiresAt > Date.now()) {
// Found
this.lru.set(key, { match: entry.value });
return entry.value.value;
}
}
// Not found
this.lru.set(key, { match: undefined });
});
}
/**
* Set a value in the cache object.
*/
public async set<Value = unknown>(key: string, value: Value, expiresAt: number) {
return this.logOperation({ operation: 'set', key }, async (setLog) => {
const prop: CacheObjectProp<Value> = {
value,
expiresAt,
};
this.lru.set(key, { match: prop });
await this.ctx.storage.transaction(async (tx) => {
const entries = encodeChunks(key, prop);
const chunks = Object.keys(entries).length;
setLog({ chunks });
const clockValue: CacheObjectExp = {
k: key,
c: chunks,
};
await tx.put(getGCClockKey(key, expiresAt), clockValue);
await tx.put(entries);
const currentAlarm = await tx.getAlarm();
if (!currentAlarm) {
// Set an alarm to garbage collect all entries that have expired in 12h.
await tx.setAlarm(Date.now() + 12 * 60 * 60 * 1000);
}
});
});
}
/**
* Purge all keys in the cache object.
*/
public async purge() {
return this.logOperation({ operation: 'purge' }, async (setLog) => {
const result = new Set<string>();
try {
// List all the keys in the cache object.
const entries = await this.ctx.storage.list<CacheObjectExp>({
prefix: 'exp.',
noCache: true,
});
setLog({ entries: entries.size });
entries.forEach((exp) => {
result.add(exp.k);
});
} catch (_error) {}
await this.reset();
return Array.from(result);
});
}
/**
* Alarm to garbage collect all entries that have expired.
*/
async alarm() {
return this.logOperation({ operation: 'alarm' }, async (setLog) => {
try {
const entries = await this.ctx.storage.list<CacheObjectExp>({
prefix: 'exp.',
noCache: true,
});
setLog({ entries: entries.size });
const toDeleteSet = new Set<string>();
for (const [key, exp] of entries) {
const timestamp = Number.parseInt(key.split('.')[1]);
if (timestamp < Date.now()) {
toDeleteSet.add(key);
for (let i = 0; i < exp.c; i++) {
toDeleteSet.add(getStoragePropChunkKey(exp.k, i));
}
}
}
// Delete the keys by batch of 128.
const toDelete = Array.from(toDeleteSet);
setLog({ toDelete: toDelete.length });
for (let i = 0; i < toDelete.length; i += 128) {
await this.ctx.storage.delete(toDelete.slice(i, i + 128));
}
// If there are still keys to delete, set an alarm to continue the deletion in 12h.
if (toDelete.length) {
await this.ctx.storage.setAlarm(Date.now() + 12 * 60 * 60 * 1000);
}
} catch (_error) {
await this.reset();
}
});
}
/**
* Reset the cache object.
*/
async reset() {
return this.logOperation({ operation: 'reset' }, async () => {
this.lru.clear();
await this.ctx.storage.deleteAll();
});
}
/**
* Time and log an operation.
*/
async logOperation<T>(
log: Record<string, unknown>,
fn: (update: (log: Record<string, unknown>) => void) => Promise<T>
): Promise<T> {
const objectId = this.ctx.id.name ?? this.ctx.id.toString();
const update: Record<string, unknown> = {};
const start = performance.now();
try {
return await fn((arg) => {
Object.assign(update, arg);
});
} finally {
const duration = performance.now() - start;
console.log({ ...log, ...update, objectId, duration });
}
}
}
function getStoragePropKey(key: string): string {
return `prop.${key}.`;
}
function getStoragePropChunkKey(key: string, index: number): string {
return `${getStoragePropKey(key)}${index}`;
}
function getGCClockRootKey(timestamp: number): string {
return `exp.${timestamp}.`;
}
function getGCClockKey(key: string, expiresAt: number): string {
return `${getGCClockRootKey(expiresAt)}${key}`;
}
function encodeChunks<T>(key: string, value: T): Record<string, Uint8Array> {
const buf = encode(value);
const entries: Record<string, Uint8Array> = {};
const chunks = chunkUint8Array(buf, 128 * 1024);
for (let index = 0; index < chunks.length; index++) {
entries[getStoragePropChunkKey(key, index)] = chunks[index];
}
return entries;
}
function decodeChunks<T>(entries: Map<string, Uint8Array>): { value: T; size: number } | undefined {
const chunks = Array.from(entries.entries())
.map(([key, value]) => {
const index = Number.parseInt(key.split('.').pop()!);
return [index, value] as const;
})
.sort(([a], [b]) => a - b)
.map(([, value]) => value);
if (chunks.length === 0) {
return;
}
const buf = mergeUint8Array(chunks);
return { value: decode(buf) as T, size: buf.length };
}
function chunkUint8Array(input: Uint8Array, chunkSize: number): Uint8Array[] {
const chunks: Uint8Array[] = [];
for (let i = 0; i < input.length; i += chunkSize) {
chunks.push(input.slice(i, i + chunkSize));
}
return chunks;
}
function mergeUint8Array(chunks: Uint8Array[]): Uint8Array {
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
-97
View File
@@ -1,97 +0,0 @@
import type { CacheObject } from './CacheObject';
export type CacheLocationId = ContinentCode;
const allLocations: CacheLocationId[] = ['AF', 'AS', 'NA', 'SA', 'AN', 'EU', 'OC'];
/**
* Location hint for the CacheObject durable object.
*/
const doLocationHints: {
[key in CacheLocationId]: DurableObjectLocationHint;
} = {
AF: 'afr',
AS: 'apac',
NA: 'wnam',
SA: 'sam',
AN: 'oc',
EU: 'weur',
OC: 'oc',
};
/**
* Client to access a cache tag.
*/
export class CacheObjectStub {
private stub: DurableObjectStub<CacheObject>;
constructor(
/** Binding to the CacheObject durable object */
private doNamespace: DurableObjectNamespace<CacheObject>,
/** ID of the location to target */
private locationId: CacheLocationId,
/** Name of the tag */
private tag: string
) {
const groupId = getCacheObjectIdName(this.locationId, this.tag);
this.stub = this.doNamespace.get(this.doNamespace.idFromName(groupId), {
// Initialize the object with a locaiton hint,
// as we might want to purge all locations before the object is created.
// https://developers.cloudflare.com/durable-objects/reference/data-location/
locationHint: doLocationHints[this.locationId],
});
}
/**
* Open a descriptor to the cache object.
* It can be used to perform multiple operations in a single RPC session.
* Ex:
* ```ts
* using desc = cache.open();
* await desc.set('key', 'value', Date.now() + 1000);
* await desc.get('key');
* ```
*/
async open() {
return await this.stub.open();
}
/**
* Get a value from the cache.
*/
async get<Value = unknown>(key: string) {
return (await this.stub.get(key)) as Value | undefined;
}
/**
* Set a value in the cache.
*/
async set<Value = unknown>(key: string, value: Value, expiresAt: number) {
return await this.stub.set(key, value, expiresAt);
}
/**
* Purge all keys in the cache tag.
*/
async purge() {
const keys = new Set<string>();
await Promise.all(
allLocations.map(async (locationId) => {
const groupId = getCacheObjectIdName(locationId, this.tag);
const cacheGroup = this.doNamespace.get(this.doNamespace.idFromName(groupId), {
// Initialize the object with a locaiton hint,
// as we might want to purge all locations before the object is created.
// https://developers.cloudflare.com/durable-objects/reference/data-location/
locationHint: doLocationHints[this.locationId],
});
const locationkeys = await cacheGroup.purge();
locationkeys.forEach((key) => keys.add(key));
})
);
return keys;
}
}
function getCacheObjectIdName(locationId: CacheLocationId, tag: string): string {
return `${locationId}:${tag}`;
}
-1
View File
@@ -1 +0,0 @@
export * from './CacheObjectStub';
-9
View File
@@ -1,9 +0,0 @@
import { WorkerEntrypoint } from 'cloudflare:workers';
export * from './CacheObject';
export default class Worker extends WorkerEntrypoint {
fetch() {
return new Response('Hello, world!');
}
}
-8
View File
@@ -1,8 +0,0 @@
{
"extends": ["//"],
"tasks": {
"generate": {
"outputs": ["worker-configuration.d.ts"]
}
}
}
-27
View File
@@ -1,27 +0,0 @@
main = "./src/index.ts"
name = "gitbook-open-cache"
compatibility_date = "2024-09-02"
durable_objects.bindings = [
{name = "CACHE", class_name = "CacheObject"}
]
migrations = [
{tag = "v1", new_classes = ["CacheObject"]}
]
[observability]
enabled = false
[env.preview]
name = "gitbook-open-cache-preview"
durable_objects.bindings = [
{name = "CACHE", class_name = "CacheObject"}
]
migrations = [
{tag = "v1", new_classes = ["CacheObject"]}
]
[env.preview.observability]
enabled = true
head_sampling_rate = 0.1
+10 -18
View File
@@ -90,7 +90,7 @@ export function getCacheTag(
| {
tag: 'translation';
organization: string;
translationSettings: string;
translation: string;
}
): string {
switch (spec.tag) {
@@ -115,23 +115,12 @@ export function getCacheTag(
case 'openapi':
return `organization:${spec.organization}:openapi:${spec.openAPISpec}`;
case 'translation':
return `organization:${spec.organization}:translation:${spec.translationSettings}`;
return `organization:${spec.organization}:translation:${spec.translation}`;
default:
assertNever(spec);
}
}
/**
* Get the cache tag for a given URL.
*/
export function getCacheTagForURL(url: string | URL) {
const parsedURL = url instanceof URL ? url : new URL(url);
return getCacheTag({
tag: 'url',
hostname: parsedURL.hostname,
});
}
/**
* Get the tags for a computed content source.
*/
@@ -144,6 +133,10 @@ export function getComputedContentSourceCacheTags(
) {
const tags: string[] = [];
if (!('dependencies' in source)) {
return tags;
}
// We add the dependencies as tags, to ensure that the computed content is invalidated
// when the dependencies are updated.
const dependencies = Object.values(source.dependencies ?? {});
@@ -167,18 +160,18 @@ export function getComputedContentSourceCacheTags(
})
);
break;
case 'translation-language':
case 'translation':
tags.push(
getCacheTag({
tag: 'translation',
organization: inContext.organizationId,
translationSettings: dependency.ref.translationSettings,
translation: dependency.ref.translation,
})
);
break;
default:
// Do not throw for unknown dependency types
// as it might mean we are lacking behind the API version
// as it might mean we are lagging behind the API version
break;
}
});
@@ -194,9 +187,8 @@ export function getComputedContentSourceCacheTags(
}
// We invalidate the computed content when a new version of the integration is deployed.
if (source.type.startsWith('integration:')) {
const integration = source.type.split(':')[1];
const integration = source.type.split(':')[1]!;
tags.push(
getCacheTag({
tag: 'integration',
+1
View File
@@ -5,6 +5,7 @@
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
+10
View File
@@ -1,5 +1,15 @@
# @gitbook/colors
## 0.4.0
### Minor Changes
- 17dd382: Add `original` background color step
### Patch Changes
- 193d591: Fix return type for `colorContrast`
## 0.3.3
### Patch Changes
+1 -1
View File
@@ -8,7 +8,7 @@
"default": "./dist/index.js"
}
},
"version": "0.3.3",
"version": "0.4.0",
"devDependencies": {
"typescript": "^5.5.3"
},
+16 -11
View File
@@ -8,7 +8,7 @@ type RGBColor = [number, number, number];
type OKLABColor = { L: number; A: number; B: number };
type OKLCHColor = { L: number; C: number; H: number };
const D65 = [95.047, 100.0, 108.883]; // Reference white (D65)
const D65 = [95.047, 100.0, 108.883] as const; // Reference white (D65)
export enum ColorCategory {
backgrounds = 'backgrounds',
@@ -19,7 +19,7 @@ export enum ColorCategory {
}
type ColorSubScale = {
[key: string]: number;
[key: string]: number | string;
};
/**
@@ -56,6 +56,8 @@ export const scale: Record<ColorCategory, ColorSubScale> = {
solid: 9,
/** Hovered solid backgrounds */
'solid-hover': 10,
/** Original color */
original: 'original',
},
[ColorCategory.text]: {
/** Very low-contrast text
@@ -211,8 +213,8 @@ export function colorScale(
const result = [];
for (let index = 0; index < mapping.length; index++) {
const targetL =
foregroundColor.L * mapping[index] + backgroundColor.L * (1 - mapping[index]);
const step = mapping[index]!;
const targetL = foregroundColor.L * step + backgroundColor.L * (1 - step);
if (
index === 8 &&
@@ -295,7 +297,7 @@ export function rgbArrayToHex(rgb: RGBColor): string {
export function getColor(percentage: number, start: RGBColor, end: RGBColor) {
const rgb = end.map((channel, index) => {
return Math.round(channel + percentage * (start[index] - channel));
return Math.round(channel + percentage * (start[index]! - channel));
});
return rgbArrayToHex(rgb as RGBColor);
@@ -392,14 +394,14 @@ export function xyzToLab65(xyz: [number, number, number]): {
B: number;
} {
const [x, y, z] = xyz.map((v, i) => {
const scaled = v / D65[i];
const scaled = v / D65[i]!;
return scaled > 0.008856 ? Math.cbrt(scaled) : 7.787 * scaled + 16 / 116;
});
return {
L: 116 * y - 16,
A: 500 * (x - y),
B: 200 * (y - z),
L: 116 * y! - 16,
A: 500 * (x! - y!),
B: 200 * (y! - z!),
};
}
@@ -419,7 +421,10 @@ export function dpsContrast(a: RGBColor, b: RGBColor) {
return contrast < 7.5 ? 0 : contrast;
}
export function colorContrast(background: string, foreground: string[] = [LIGHT_BASE, DARK_BASE]) {
export function colorContrast(
background: string,
foreground: string[] = [LIGHT_BASE, DARK_BASE]
): string {
const bg = hexToRgbArray(background);
const best: { color?: RGBColor; contrast: number } = {
@@ -436,5 +441,5 @@ export function colorContrast(background: string, foreground: string[] = [LIGHT_
}
}
return best.color ? rgbArrayToHex(best.color) : foreground[0];
return best.color ? rgbArrayToHex(best.color) : foreground[0] || LIGHT_BASE;
}
+1
View File
@@ -5,6 +5,7 @@
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
+2
View File
@@ -0,0 +1,2 @@
dist/
standalone/
+25
View File
@@ -0,0 +1,25 @@
# @gitbook/embed
## 0.1.2
### Patch Changes
- 7508674: Fix bundling of packages
## 0.1.1
### Patch Changes
- 6f368b5: Fix embed assistant window width on small screens
## 0.1.0
### Minor Changes
- 81a6bd7: Improve API to control the GitBook embed
### Patch Changes
- 8927e8f: Initial version of the embed SDK.
- Updated dependencies [25e2b40]
- @gitbook/icons@0.3.0
+49
View File
@@ -0,0 +1,49 @@
# `@gitbook/embed`
Embed the GitBook Docs Assistant in your product or website.
# Usage
## As a script from your docs site
All GitBook docs site includes a script to easily embed the docs assistant 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.
```html
<script src="https://docs.company.com/~gitbook/embed/script.js"></script>
<script>
window.GitBook('show');
</script>
```
## As a package from NPM
Install the package: `npm install @gitbook/embed` and import it in your web application:
```tsx
import { createGitBook } from '@gitbook/embed';
const gitbook = createGitBook({
siteURL: 'https://docs.company.com'
});
const iframe = document.createElement('iframe');
iframe.src = gitbook.getFrameURL();
const frame = gitbook.createFrame(iframe);
```
## As React components
After installing the NPM package, you can import prebuilt React components:
```tsx
import { GitBookProvider, GitBookAssistantFrame } from '@gitbook/embed/react';
<GitBookProvider siteURL="https://docs.company.com">
<GitBookAssistantFrame />
</GitBookProvider>
```
+37
View File
@@ -0,0 +1,37 @@
{
"name": "@gitbook/embed",
"description": "Embeddable components for GitBook",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./react": {
"types": "./dist/react/index.d.ts",
"default": "./dist/react/index.js"
}
},
"version": "0.1.2",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:",
"bidc": "catalog:"
},
"peerDependencies": {
"react": "^18.0.0"
},
"devDependencies": {
"tsdown": "catalog:",
"typescript": "^5.5.3",
"react": "^19.0.0"
},
"scripts": {
"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",
"typecheck": "tsc --noEmit"
},
"files": ["dist", "README.md", "CHANGELOG.md"]
}
@@ -0,0 +1,61 @@
import { type GitBookFrameClient, createGitBookFrame } from './createGitBookFrame';
export type CreateGitBookOptions = {
/**
* URL of the GitBook site to embed.
*/
siteURL: string;
};
export type GetFrameURLOptions = {
/**
* Authentication to use for the frame.
*/
visitor?: {
/**
* Signed JWT token for Adaptive Content or Visitor Authentication to use.
*/
token?: string;
/**
* Unsigned claims to pass to the frame.
* You can use these claims in dynamic expressions using `visitor.claims.unsigned.<claim-name>`.
*/
unsignedClaims?: Record<string, unknown>;
};
};
export type GitBookClient = {
/**
* Get the URL for a GitBook frame.
*/
getFrameURL: (options: GetFrameURLOptions) => string;
/**
* Create a new GitBook frame.
*/
createFrame: (iframe: HTMLIFrameElement) => GitBookFrameClient;
};
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`;
if (frameOptions.visitor?.token) {
url.searchParams.set('token', frameOptions.visitor.token);
}
if (frameOptions.visitor?.unsignedClaims) {
Object.entries(frameOptions.visitor.unsignedClaims).forEach(([key, value]) => {
url.searchParams.set(`visitor.${key}`, String(value));
});
}
return url.toString();
},
createFrame: (iframe) => createGitBookFrame(iframe),
};
return client;
}
@@ -0,0 +1,98 @@
import { createChannel } from 'bidc';
import type {
FrameToParentMessage,
GitBookEmbeddableConfiguration,
ParentToFrameMessage,
} from './protocol';
export type GitBookFrameClient = {
/**
* Navigate to a page by its path.
*/
navigateToPage: (path: string) => void;
/**
* Navigate to the assistant.
*/
navigateToAssistant: () => void;
/**
* Post a message to the chat.
*/
postUserMessage: (message: string) => void;
/**
* Clear the chat.
*/
clearChat: () => void;
/**
* Set the placeholder settings.
*/
configure: (settings: Partial<GitBookEmbeddableConfiguration>) => void;
/**
* Register an event listener.
*/
on: (event: string, listener: (...args: any[]) => void) => () => void;
};
/**
* Create a client to communicate with the GitBook Assistant frame.
*/
export function createGitBookFrame(iframe: HTMLIFrameElement): GitBookFrameClient {
if (!iframe.contentWindow) {
throw new Error('Iframe must have a content window');
}
const channel = createChannel(iframe.contentWindow);
channel.receive((message: FrameToParentMessage) => {
console.log('[gitbook:embed] received message', message);
if (message.type === 'close') {
const listeners = events.get('close') || [];
if (listeners) {
listeners.forEach((listener) => listener());
}
}
});
const sendToFrame = (message: ParentToFrameMessage) => {
console.log('[gitbook:embed] send message', message);
channel.send(message);
};
const events = new Map<string, Array<(...args: any[]) => void>>();
const configuration: GitBookEmbeddableConfiguration = {
buttons: [],
welcomeMessage: '',
suggestions: [],
tools: [],
};
return {
navigateToPage: (pagePath) => {
sendToFrame({ type: 'navigateToPage', pagePath });
},
navigateToAssistant: () => {
sendToFrame({ type: 'navigateToAssistant' });
},
postUserMessage: (message) => sendToFrame({ type: 'postUserMessage', message }),
configure: (settings) => {
Object.assign(configuration, settings);
sendToFrame({ type: 'configure', settings: configuration });
},
clearChat: () => sendToFrame({ type: 'clearChat' }),
on: (event, listener) => {
const listeners = events.get(event) || [];
listeners.push(listener);
events.set(event, listeners);
return () => {
events.set(
event,
listeners.filter((l) => l !== listener)
);
};
},
};
}
+3
View File
@@ -0,0 +1,3 @@
export * from './createGitBook';
export * from './createGitBookFrame';
export * from './protocol';
+90
View File
@@ -0,0 +1,90 @@
import type { AIToolCallResult, AIToolDefinition } from '@gitbook/api';
import type { IconName } from '@gitbook/icons';
/**
* Custom tool definition to be passed to the AI assistant.
*/
export type GitBookToolDefinition = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
*/
confirmation?: {
icon?: IconName;
label: string;
};
/**
* Callback when the tool is executed.
* The input is provided by the AI assistant following the input schema of the tool.
*/
execute: (input: object) => Promise<Pick<AIToolCallResult, 'output' | 'summary'>>;
};
/**
* Custom button definition to be passed to the embeddable GitBook.
*/
export type GitBookEmbeddableButtonDefinition = {
/**
* Icon to be displayed in the button.
*/
icon: IconName;
/**
* Label to be displayed in the button.
*/
label: string;
/**
* Callback when the button is clicked.
*/
onClick: () => void | Promise<void>;
};
/**
* Overall configuration for the layout of the embeddable GitBook.
*/
export type GitBookEmbeddableConfiguration = {
/**
* Buttons to be displayed in the header of the embeddable GitBook.
*/
buttons: GitBookEmbeddableButtonDefinition[];
/** Message to be displayed in the welcome page. */
welcomeMessage: string;
/** Suggestions of questions to be displayed in the welcome page. */
suggestions: string[];
/** Tools to be provided to the assistant. */
tools: GitBookToolDefinition[];
};
/**
* Messages sent from the parent to the frame.
*/
export type ParentToFrameMessage =
| {
type: 'postUserMessage';
message: string;
}
| {
type: 'clearChat';
}
| {
type: 'configure';
settings: GitBookEmbeddableConfiguration;
}
| {
type: 'navigateToPage';
pagePath: string;
}
| {
type: 'navigateToAssistant';
};
/**
* Messages sent from the frame to the parent.
*/
export type FrameToParentMessage = {
type: 'close';
};
+1
View File
@@ -0,0 +1 @@
export * from './client';
+51
View File
@@ -0,0 +1,51 @@
import React from 'react';
import type {
GetFrameURLOptions,
GitBookEmbeddableConfiguration,
GitBookFrameClient,
} from '../client';
import { useGitBook } from './GitBookProvider';
export type GitBookFrameProps = {
className?: string;
} & GetFrameURLOptions &
GitBookEmbeddableConfiguration;
/**
* Render a frame with the GitBook Assistant in it.
*/
export function GitBookFrame(props: GitBookFrameProps) {
const { className, visitor, buttons, welcomeMessage, suggestions, tools } = props;
const frameRef = React.useRef<HTMLIFrameElement>(null);
const gitbook = useGitBook();
const [gitbookFrame, setGitbookFrame] = React.useState<GitBookFrameClient | null>(null);
const frameURL = React.useMemo(() => gitbook.getFrameURL({ visitor }), [gitbook, visitor]);
React.useEffect(() => {
if (frameRef.current) {
setGitbookFrame(gitbook.createFrame(frameRef.current));
}
}, [gitbook]);
React.useEffect(() => {
gitbookFrame?.configure({
buttons,
welcomeMessage,
suggestions,
tools,
});
}, [gitbookFrame, buttons, welcomeMessage, suggestions, tools]);
return (
<iframe
title="GitBook"
ref={frameRef}
src={frameURL}
width="100%"
height="100%"
className={className}
/>
);
}
@@ -0,0 +1,36 @@
'use client';
import * as React from 'react';
import { type CreateGitBookOptions, createGitBook } from '../client';
import { GitBookContext } from './context';
/**
* Provider for the GitBook client.
*/
export function GitBookProvider(props: React.PropsWithChildren<CreateGitBookOptions>) {
const { siteURL, children } = props;
const options = React.useMemo(
() => ({
siteURL,
}),
[siteURL]
);
const client = React.useMemo(() => createGitBook(options), [options]);
return <GitBookContext.Provider value={client}>{children}</GitBookContext.Provider>;
}
/**
* Hook to access the GitBook client.
*/
export function useGitBook() {
const context = React.useContext(GitBookContext);
if (!context) {
throw new Error('This component must be used within a <GitBookProvider />');
}
return context;
}
+6
View File
@@ -0,0 +1,6 @@
'use client';
import * as React from 'react';
import type { GitBookClient } from '../client';
export const GitBookContext = React.createContext<GitBookClient | null>(null);
+2
View File
@@ -0,0 +1,2 @@
export * from './GitBookProvider';
export * from './GitBookFrame';
+178
View File
@@ -0,0 +1,178 @@
import './style.css';
import {
type CreateGitBookOptions,
type GetFrameURLOptions,
type GitBookClient,
type GitBookEmbeddableConfiguration,
type GitBookFrameClient,
createGitBook,
} from '../client';
export type GitBook = () => void;
type StandaloneCalls =
// Initialize the widget
| ['init', CreateGitBookOptions, GetFrameURLOptions]
// Unload the widget
| ['unload']
// Show the widget
| ['show']
// Hide the widget
| ['hide']
// Open the window
| ['open']
// Close the window
| ['close']
// Toggle the window
| ['toggle']
// Post a user message
| ['postUserMessage', string]
// Clear the chat
| ['clearChat']
// Configure the embed
| ['configure', Partial<GitBookEmbeddableConfiguration>]
// Navigate to a page
| ['navigateToPage', string]
// Navigate to the assistant
| ['navigateToAssistant'];
export type GitBookStandalone = ((...args: StandaloneCalls) => void) & {
q?: StandaloneCalls[];
};
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>
`;
const widgetWindow = document.createElement('div');
widgetWindow.id = 'gitbook-widget-window';
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(
'GitBook client not initialized. Call GitBook("init", { siteURL: "..." }) first.'
);
}
return _client;
}
function getIframe() {
if (!widgetIframe || !_frame) {
const client = getClient();
widgetIframe?.remove();
widgetIframe = document.createElement('iframe');
widgetIframe.id = 'gitbook-widget-iframe';
widgetIframe.src = client.getFrameURL({
...frameOptions,
});
widgetWindow.appendChild(widgetIframe);
_frame = client.createFrame(widgetIframe);
}
return { iframe: widgetIframe, frame: _frame };
}
const GitBook = (...args: StandaloneCalls) => {
switch (args[0]) {
case 'init':
if (_client) {
throw new Error(
'GitBook client already initialized. Call GitBook("unload") first.'
);
}
_client = createGitBook(args[1]);
frameOptions = args[2];
break;
case 'unload':
_client = undefined;
_frame = undefined;
widgetIframe?.remove();
widgetWindow.classList.add('hidden');
break;
case 'show':
widgetButton.classList.remove('hidden');
break;
case 'hide':
widgetButton.classList.add('hidden');
break;
case 'open':
widgetWindow.classList.remove('hidden');
widgetButton.classList.add('open');
getIframe();
break;
case 'toggle':
widgetWindow.classList.toggle('hidden');
widgetButton.classList.toggle('open');
getIframe();
break;
case 'close':
widgetWindow.classList.add('hidden');
widgetButton.classList.remove('open');
break;
case 'postUserMessage':
getIframe().frame.postUserMessage(args[1]);
break;
case 'configure':
frameConfiguration = {
...frameConfiguration,
...args[1],
};
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;
case 'navigateToPage':
getIframe().frame.navigateToPage(args[1]);
break;
case 'navigateToAssistant':
getIframe().frame.navigateToAssistant();
break;
}
};
// @ts-expect-error - GitBook is not defined in the global scope
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', {});
+171
View File
@@ -0,0 +1,171 @@
:root {
--gitbook-widget-top: 1rem;
--gitbook-widget-bottom: 1rem;
--gitbook-widget-right: 1rem;
--gitbook-widget-left: 1rem;
--gitbook-widget-button-height: 46px;
--gitbook-widget-radius: .5rem;
--gitbook-widget-text-size: 1rem;
--gitbook-widget-text-color: #656973;
--gitbook-widget-border-color: #e5e5e5;
--gitbook-widget-background-translucent: rgba(255, 255, 255, 0.9);
--gitbook-widget-background-translucent-hover: rgba(250, 250, 250, 0.9);
--gitbook-widget-background-solid: #FFFFFF;
--gitbook-widget-background-solid-hover: #FBFBFB;
--gitbook-widget-icon-size: 1.25rem;
--gitbook-widget-window-width: 28rem; /* 448px */
--gitbook-widget-window-height: 40rem; /* 640px */
--gitbook-widget-window-spacing: .5rem; /* Spacing between the button and the window */
--gitbook-widget-window-bottom: calc(var(--gitbook-widget-bottom) + var(--gitbook-widget-button-height) + var(--gitbook-widget-window-spacing));
--gitbook-widget-transition-duration-fast: 0.2s;
--gitbook-widget-transition-duration-slow: 0.5s;
--gitbook-widget-easing: cubic-bezier(0.25, 1, 0.5, 1);
--gitbook-widget-easing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
}
* {
box-sizing: border-box;
}
/* Button */
#gitbook-widget-button {
position: fixed;
bottom: var(--gitbook-widget-bottom);
right: var(--gitbook-widget-right);
height: var(--gitbook-widget-button-height);
z-index: 9999;
display: flex;
align-items: center;
flex-direction: row-reverse;
gap: .5rem;
padding: .75rem 1rem;
border-radius: 100px;
border: 1px solid var(--gitbook-widget-border-color);
background-color: var(--gitbook-widget-background-translucent);
backdrop-filter: blur(16px);
font-size: var(--gitbook-widget-text-size);
color: var(--gitbook-widget-text-color);
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.05), 0 1px 2px -1px rgba(0,0,0,0.05);
transition: all var(--gitbook-widget-transition-duration-fast) var(--gitbook-widget-easing);
cursor:pointer;
animation: gitbook-widget-present var(--gitbook-widget-transition-duration-slow) var(--gitbook-widget-easing-bounce);
}
#gitbook-widget-button:hover, #gitbook-widget-button:focus-visible {
background-color: var(--gitbook-widget-background-translucent-hover);
}
#gitbook-widget-button:hover, #gitbook-widget-button:focus-visible {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
}
#gitbook-widget-button:active {
transform: translateY(0);
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.05), 0 1px 2px -1px rgba(0,0,0,0.05);
}
@media (prefers-contrast: more) {
#gitbook-widget-button {
background-color: var(--gitbook-widget-background-solid);
}
#gitbook-widget-button:hover, #gitbook-widget-button:focus-visible {
background-color: var(--gitbook-widget-background-solid-hover);
}
}
#gitbook-widget-button.hidden {
display: none;
}
#gitbook-widget-button.open {
padding: .75rem;
gap: 0;
}
/* Button: Icon */
#gitbook-widget-button-icon {
width: var(--gitbook-widget-icon-size);
height: var(--gitbook-widget-icon-size);
mask-image: url("https://static-2v.gitbook.com/~gitbook/static/icons/svgs/custom-icons/gitbook-assistant.svg?v=2");
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
background-color: currentColor;
}
#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');
}
/* Button: Label */
#gitbook-widget-button.open #gitbook-widget-button-label {
display: none;
}
/* Window */
#gitbook-widget-window {
position: fixed;
bottom: var(--gitbook-widget-window-bottom);
right: var(--gitbook-widget-right);
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);
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);
overflow: hidden;
transition-property: transform, opacity, display;
transition-duration: var(--gitbook-widget-transition-duration-slow);
transition-timing-function: var(--gitbook-widget-easing-bounce);
transform-origin: bottom right;
transition-behavior: allow-discrete;
}
@starting-style {
#gitbook-widget-window {
opacity: 0;
transform: scale(0.9);
}
}
body:has(#gitbook-widget-button.hidden) #gitbook-widget-window {
bottom: var(--gitbook-widget-bottom);
}
#gitbook-widget-window.hidden {
transition-property: transform, opacity, display;
transition-duration: var(--gitbook-widget-transition-duration-fast);
transition-timing-function: var(--gitbook-widget-easing);
transition-behavior: allow-discrete;
display: none;
opacity: 0;
transform: scale(0.9);
}
#gitbook-widget-iframe {
width: 100%;
height: 100%;
border: none;
}
@keyframes gitbook-widget-present {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@@ -13,9 +13,10 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": ["./worker-configuration.d.ts"]
"types": []
},
"include": ["src/**/*.ts"],
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsdown';
export default defineConfig([
{
entry: ['src/index.ts', 'src/react/index.ts'],
dts: true,
format: ['esm'],
},
]);
+1
View File
@@ -0,0 +1 @@
dist
+35
View File
@@ -0,0 +1,35 @@
# @gitbook/expr
## 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
- ada195d: Publish gitbook/expr package to help evaluate user defined expressions.
+3
View File
@@ -0,0 +1,3 @@
# `@gitbook/expr`
Safely evaluate & parse user-defined GitBook expressions.
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@gitbook/expr",
"description": "Safely evaluate & parse user-defined GitBook expressions.",
"version": "1.2.1",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"sideEffects": false,
"dependencies": {
"eval-estree-expression": "github:jonschlinkert/eval-estree-expression#9cf28d2",
"acorn": "^8.14.0",
"acorn-loose": "8.4.0",
"acorn-walk": "^8.3.4",
"escodegen": "^2.1.0",
"assert-never": "^1.2.1"
},
"devDependencies": {
"bun-types": "^1.1.20",
"tsdown": "catalog:",
"@types/estree": "^1.0.6",
"@babel/types": "^7.26.0",
"@types/json-schema": "^7.0.15",
"@types/escodegen": "^0.0.10"
},
"scripts": {
"build": "tsdown",
"typecheck": "tsc --noEmit",
"unit": "bun test",
"clean": "rm -rf ./dist",
"dev": "bun run build -- --watch"
},
"files": ["dist", "README.md", "CHANGELOG.md"]
}
@@ -0,0 +1,889 @@
import { describe, expect, it } from 'bun:test';
import { ExpressionRuntime } from '../runtime';
import {
SymbolArray,
SymbolBoolean,
SymbolNumber,
SymbolObject,
SymbolString,
SymbolType,
SymbolsTable,
} from '../symbols';
import {
type AutocompleteSuggestions,
type AutocompleteSymbolSuggestion,
SUPPORTED_BINARY_OPERATORS,
SUPPORTED_CONDITIONAL_OPERATORS,
SUPPORTED_LOGICAL_OPERATORS,
} from '../types';
describe('autocomplete', () => {
const runtime = new ExpressionRuntime();
const visitorClaimsHelloArraySymbol = SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
});
const symbols = {
visitor: SymbolObject({
name: 'visitor',
properties: {
claims: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: visitorClaimsHelloArraySymbol,
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
},
methods: [],
}),
};
const context = new SymbolsTable(symbols);
const SCENARIOS: Array<{
expressionWithCursor: string;
expectedSuggestions: AutocompleteSuggestions;
}> = [
{
expressionWithCursor: 'visit<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'visitor',
properties: {
claims: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
},
methods: [],
}),
ref: 'visitor',
parentRef: undefined,
childrenRefs: ['visitor.claims'],
},
},
],
},
{
expressionWithCursor: 'visitor<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
ref: 'visitor.claims',
parentRef: 'visitor',
childrenRefs: [
'visitor.claims.key',
'visitor.claims.flags',
'visitor.claims.hello',
'visitor.claims.role',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolString({ name: 'key' }),
ref: 'visitor.claims.key',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.key.length',
'visitor.claims.key.at',
'visitor.claims.key.endsWith',
'visitor.claims.key.includes',
],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
ref: 'visitor.claims.flags',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.flags.FLAG1',
'visitor.claims.flags.FLAG2',
'visitor.claims.flags.FLAG3',
'visitor.claims.flags.FLAG4',
],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
ref: 'visitor.claims.hello',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
ref: 'visitor.claims.role',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.role.length',
'visitor.claims.role.at',
'visitor.claims.role.endsWith',
'visitor.claims.role.includes',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.ke<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolString({ name: 'key' }),
ref: 'visitor.claims.key',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.key.length',
'visitor.claims.key.at',
'visitor.claims.key.endsWith',
'visitor.claims.key.includes',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.h<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
ref: 'visitor.claims.hello',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.hello.<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolNumber({
name: 'length',
description: `The length data property of an Array instance represents the number of elements in that array.
The value is an unsigned, 32-bit integer that is always numerically greater than the highest index in the array.`,
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length',
}),
ref: 'visitor.claims.hello.length',
parentRef: 'visitor.claims.hello',
childrenRefs: [],
},
},
...visitorClaimsHelloArraySymbol.methods.map<AutocompleteSymbolSuggestion>(
(method) => ({
type: 'symbol',
symbol: {
definition: method,
ref: `visitor.claims.hello.${method.name}`,
parentRef: 'visitor.claims.hello',
childrenRefs: [],
},
})
),
],
},
{
expressionWithCursor: 'visitor.claims.f<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
ref: 'visitor.claims.flags',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.flags.FLAG1',
'visitor.claims.flags.FLAG2',
'visitor.claims.flags.FLAG3',
'visitor.claims.flags.FLAG4',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.fl<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
ref: 'visitor.claims.flags',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.flags.FLAG1',
'visitor.claims.flags.FLAG2',
'visitor.claims.flags.FLAG3',
'visitor.claims.flags.FLAG4',
],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG1' }),
ref: 'visitor.claims.flags.FLAG1',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG2' }),
ref: 'visitor.claims.flags.FLAG2',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG3' }),
ref: 'visitor.claims.flags.FLAG3',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG4' }),
ref: 'visitor.claims.flags.FLAG4',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FL<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG1' }),
ref: 'visitor.claims.flags.FLAG1',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG2' }),
ref: 'visitor.claims.flags.FLAG2',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG3' }),
ref: 'visitor.claims.flags.FLAG3',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
{
type: 'symbol',
symbol: {
definition: SymbolBoolean({ name: 'FLAG4' }),
ref: 'visitor.claims.flags.FLAG4',
parentRef: 'visitor.claims.flags',
childrenRefs: [],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.key <cur>',
expectedSuggestions: [...SUPPORTED_BINARY_OPERATORS].map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 <cur>',
expectedSuggestions: [
...[...SUPPORTED_BINARY_OPERATORS, ...SUPPORTED_LOGICAL_OPERATORS].filter((op) =>
['==', '!=', '===', '!==', '&&', '||'].includes(op.operator)
),
...SUPPORTED_CONDITIONAL_OPERATORS,
].map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 =<cur>',
expectedSuggestions: SUPPORTED_BINARY_OPERATORS.filter((op) =>
['==', '==='].includes(op.operator)
).map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 ==<cur>',
expectedSuggestions: SUPPORTED_BINARY_OPERATORS.filter(
(op) => op.operator === '==='
).map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 !<cur>',
expectedSuggestions: SUPPORTED_BINARY_OPERATORS.filter((op) =>
['!=', '!=='].includes(op.operator)
).map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 !=<cur>',
expectedSuggestions: SUPPORTED_BINARY_OPERATORS.filter(
(op) => op.operator === '!=='
).map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == <cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: true },
},
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: false },
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == t<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: true },
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == tr<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: true },
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == true<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == f<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: false },
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == fa<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.Boolean, data: false },
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == non<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == false<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.role == <cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'admin' },
},
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'editor' },
},
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'reader' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == ad<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'admin' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == admin<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'admin' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == "ad<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'admin' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == "admin<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'admin' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == "admin"<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.role == edit<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'editor' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == "edit<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'editor' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == editor<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: { kind: 'direct', type: SymbolType.String, data: 'editor' },
},
],
},
{
expressionWithCursor: 'visitor.claims.role == "editor"<cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.hello == <cur>',
expectedSuggestions: [],
},
{
expressionWithCursor: 'visitor.claims.hello[1] == <cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: {
kind: 'in-array',
srcSymbol: visitorClaimsHelloArraySymbol,
matchedLiteralString: '',
},
},
],
},
{
expressionWithCursor: 'visitor.claims.hello[1] == "test<cur>',
expectedSuggestions: [
{
type: 'literal-value',
value: {
kind: 'in-array',
srcSymbol: visitorClaimsHelloArraySymbol,
matchedLiteralString: '"test',
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 == true <cur>',
expectedSuggestions: [
...SUPPORTED_LOGICAL_OPERATORS.filter((op) => ['&&', '||'].includes(op.operator)),
...SUPPORTED_CONDITIONAL_OPERATORS,
].map((op) => ({
type: 'operator',
...op,
})),
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 !== true && v<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'visitor',
properties: {
claims: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
},
methods: [],
}),
ref: 'visitor',
parentRef: undefined,
childrenRefs: ['visitor.claims'],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 ? <cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'visitor',
properties: {
claims: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
},
methods: [],
}),
ref: 'visitor',
parentRef: undefined,
childrenRefs: ['visitor.claims'],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 ? visit<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'visitor',
properties: {
claims: SymbolObject({
name: 'claims',
description: 'The claims contained in the visitor JWT token',
properties: {
key: SymbolString({ name: 'key' }),
flags: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
hello: SymbolArray({
name: 'hello',
description: 'An array of string',
items: SymbolString(),
}),
role: SymbolString({
name: 'role',
enum: ['admin', 'editor', 'reader'],
}),
},
methods: [],
}),
},
methods: [],
}),
ref: 'visitor',
parentRef: undefined,
childrenRefs: ['visitor.claims'],
},
},
],
},
{
expressionWithCursor: 'visitor.claims.flags.FLAG1 ? visitor.claims.fl<cur>',
expectedSuggestions: [
{
type: 'symbol',
symbol: {
definition: SymbolObject({
name: 'flags',
properties: {
FLAG1: SymbolBoolean({ name: 'FLAG1' }),
FLAG2: SymbolBoolean({ name: 'FLAG2' }),
FLAG3: SymbolBoolean({ name: 'FLAG3' }),
FLAG4: SymbolBoolean({ name: 'FLAG4' }),
},
methods: [],
}),
ref: 'visitor.claims.flags',
parentRef: 'visitor.claims',
childrenRefs: [
'visitor.claims.flags.FLAG1',
'visitor.claims.flags.FLAG2',
'visitor.claims.flags.FLAG3',
'visitor.claims.flags.FLAG4',
],
},
},
],
},
];
it.each(SCENARIOS)(
'should provide matching suggestion for expression with cursor: $expressionWithCursor',
({ expressionWithCursor, expectedSuggestions }) => {
const { expression, cursorOffset } = extractCursorPosition(
expressionWithCursor,
'<cur>'
);
const { suggestions } = runtime.autocomplete(expression, cursorOffset, context);
expect(suggestions).toStrictEqual(expectedSuggestions);
}
);
});
function extractCursorPosition(
expressionWithCursor: string,
cursorPlaceholder: string
): { expression: string; cursorOffset: number } {
const cursorOffset = expressionWithCursor.indexOf(cursorPlaceholder);
if (cursorOffset === -1) {
throw new Error(
`Cursor position (${cursorPlaceholder}) not found in the expression string.`
);
}
const expression = expressionWithCursor.replace(cursorPlaceholder, '');
return { expression, cursorOffset };
}

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