mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-14 22:55:25 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4228625852 | |||
| e9c8ec79d2 | |||
| 6ede6ee7c6 | |||
| 7a11861d36 | |||
| 10754fbaf4 | |||
| 8f32f4fb1e | |||
| 717ad0b300 | |||
| a3eed52f24 | |||
| 27a34b7ee2 | |||
| bf3ad28c7f | |||
| 70c2e90e4b | |||
| d0a378cdb6 | |||
| 81f8ddcf27 | |||
| 9d6457f814 | |||
| 07c02ebcd2 | |||
| 4d3172d13f | |||
| 0aa34cc9a8 | |||
| 551d30cfa4 | |||
| 844a0a8ea7 | |||
| eb8ba66a66 | |||
| d738e118a0 | |||
| c16d08f5d5 | |||
| 61ec9b25b9 | |||
| b57561d395 | |||
| e00d1c5bbd | |||
| 9063b56c89 | |||
| 020d3668d7 | |||
| 4d30cbf058 | |||
| d3f6c38f05 | |||
| 6f1db32682 | |||
| 4ad6b2c067 | |||
| 07449443a0 | |||
| b09cc64fec | |||
| 1eed2004f6 | |||
| 91539b8187 | |||
| 16cfde87ee | |||
| 58b4a978ff | |||
| e34be0e89b | |||
| e60c754343 | |||
| 48ca21e471 | |||
| 3ba9e46f2b | |||
| 58f0cc8287 | |||
| 29e2b22b63 | |||
| 3c7cb09c1b | |||
| 0f1cd6745d | |||
| e90f57b138 | |||
| 7bd5939247 | |||
| 89de966e7a | |||
| 4e2d86318e | |||
| f647af2003 | |||
| 96e24a1f84 | |||
| 2a0f37f4b3 | |||
| 17d935dd4c | |||
| df07fbe242 | |||
| 1e53376151 | |||
| f294818775 | |||
| eed386a133 | |||
| e172b25ba8 | |||
| f7f6f9d06d | |||
| 9e01676fe9 | |||
| f6cf62a686 | |||
| a5ef44ab2a | |||
| b53cb1d2b3 | |||
| 7eeff73080 | |||
| bf63045319 | |||
| 71c2e7f8b4 | |||
| 985c1b45b4 | |||
| 35efc9d6d8 | |||
| 6b1fd43179 | |||
| 7517e987a5 | |||
| 21851d3340 | |||
| ca682362dd | |||
| 56fb754e22 | |||
| 99d788ed9e | |||
| ebf3b2beb7 | |||
| 7b7231602f | |||
| adfb118285 | |||
| a7f58ab829 | |||
| 360b525b4b | |||
| 695e99f466 | |||
| aa87699915 | |||
| d2eb3d9f5a | |||
| 77bc339fca | |||
| e7fb77116e | |||
| 050ca23ac4 | |||
| 9063f97dba | |||
| ed9e8fe93a | |||
| b9ad997fcb |
@@ -2,4 +2,4 @@
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix search results cursor
|
||||
OpenAPI styling tweaks
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix missing properties in allOf/oneOf
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Prevent form submission during IME composition
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Support code themes customization
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Enhance discriminator handling in OpenAPISchema
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Set <html lang> based on translated language
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@gitbook/openapi-parser': patch
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Add x-gitbook-prefix and x-gitbook-token-placeholder for OpenAPI security scheme
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support social links
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/embed": patch
|
||||
---
|
||||
|
||||
Fix signed JWT token usage
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Fix OpenAPI example not showing
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix overflowing section groups
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Serve a markdown version of the page when the "Accept" header contains "text/markdown".
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Align styling of PageLinkItem and PageDocumentItem
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/openapi-parser": patch
|
||||
---
|
||||
|
||||
Update scalar dependencies.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support `defaultExpanded` in Expandable block
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Better conditional display of page actions
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"@gitbook/embed": minor
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Improve Docs Embed with separate Assistant and Docs tabs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Improve OpenAPI server URL validation
|
||||
@@ -2,4 +2,4 @@
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Add Input component
|
||||
Header & TOC tweaks
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/icons": minor
|
||||
---
|
||||
|
||||
Update to FontAwesome 7.1.0
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix ordered list item index calculation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Fix Scalar Try it button justification
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Standardise toggle chevrons across the app
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Add customizable suggested questions
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix OpenAPI basic auth placeholder
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix rendering of images in reusable content from an external space with the new dereferencing in the API.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix links to other spaces and root page in embeddable view.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support `greeting` and fix suggested questions if there are no custom ones defined
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix OpenAPI oneOf/allOf merge
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Grow InlineActionButton to take available width on line
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/icons": patch
|
||||
---
|
||||
|
||||
Simplify installation by using public fontawesome mirror.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Add a RSS feed to all pages with update blocks.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Remove hidden site spaces from navigation
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Fix hidden section not found
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gitbook/browser-types": patch
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Add support for custom cookie banner registration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Better handle huge section groups
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
"@gitbook/embed": patch
|
||||
---
|
||||
|
||||
Fix Embed options in React
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Add OpenAPI 3.1 nullable array support
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Improve OpenAPI circular references
|
||||
@@ -5,8 +5,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- changeset-release/main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -36,13 +34,10 @@ jobs:
|
||||
env:
|
||||
PUPPETEER_SKIP_DOWNLOAD: 1
|
||||
- run: bun unit
|
||||
build-oss:
|
||||
# CI to check that the repository builds correctly on a machine without the credentials
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build (Open Source)
|
||||
name: Build
|
||||
timeout-minutes: 6
|
||||
env:
|
||||
BUN_NPM_TOKEN: ''
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
name: Preview
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
approval:
|
||||
# Ensure that external contributors' PRs are manually approved before deployment
|
||||
name: Approve Preview Deployment
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork }}
|
||||
environment:
|
||||
name: preview-approval
|
||||
steps:
|
||||
- name: Approval gate
|
||||
run: echo "Preview deployment approved."
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (preview)
|
||||
runs-on: ubuntu-latest
|
||||
needs: approval
|
||||
if: ${{ always() && (needs.approval.result == 'success' || needs.approval.result == 'skipped') }}
|
||||
environment:
|
||||
name: 2v-preview
|
||||
url: ${{ steps.deploy.outputs.deployment-url }}
|
||||
@@ -18,6 +28,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Deploy to Vercel
|
||||
id: deploy
|
||||
uses: ./.github/composite/deploy-vercel
|
||||
@@ -31,6 +43,8 @@ jobs:
|
||||
deploy-v2-cloudflare:
|
||||
name: Deploy v2 to Cloudflare Worker (preview)
|
||||
runs-on: ubuntu-latest
|
||||
needs: approval
|
||||
if: ${{ always() && (needs.approval.result == 'success' || needs.approval.result == 'skipped') }}
|
||||
environment:
|
||||
name: 2c-preview
|
||||
url: ${{ steps.deploy.outputs.deployment-url }}
|
||||
@@ -39,6 +53,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Deploy to Cloudflare
|
||||
id: deploy
|
||||
uses: ./.github/composite/deploy-cloudflare
|
||||
@@ -65,7 +81,7 @@ jobs:
|
||||
comment-deployments:
|
||||
runs-on: ubuntu-latest
|
||||
name: Comment Deployments (preview)
|
||||
if: always() && !startsWith(github.ref, 'refs/heads/main')
|
||||
if: always() && github.event_name != 'push'
|
||||
needs:
|
||||
- deploy-v2-vercel
|
||||
- deploy-v2-cloudflare
|
||||
@@ -102,10 +118,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Visual Testing v2
|
||||
needs: deploy-v2-vercel
|
||||
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Setup Bun
|
||||
uses: ./.github/composite/setup-bun
|
||||
- name: Install dependencies
|
||||
@@ -123,10 +142,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Visual Testing v2 (Cloudflare)
|
||||
needs: deploy-v2-cloudflare
|
||||
if: ${{ always() && needs.deploy-v2-cloudflare.result == 'success' }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Setup Bun
|
||||
uses: ./.github/composite/setup-bun
|
||||
- name: Install dependencies
|
||||
@@ -144,10 +166,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Visual Testing Customers v2
|
||||
needs: deploy-v2-vercel
|
||||
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Setup Bun
|
||||
uses: ./.github/composite/setup-bun
|
||||
- name: Install dependencies
|
||||
@@ -165,10 +190,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Visual Testing Customers v2 (Cloudflare)
|
||||
needs: deploy-v2-cloudflare
|
||||
if: ${{ always() && needs.deploy-v2-cloudflare.result == 'success' }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Setup Bun
|
||||
uses: ./.github/composite/setup-bun
|
||||
- name: Install dependencies
|
||||
@@ -186,9 +214,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Browserless Testing v2 (Vercel)
|
||||
needs: deploy-v2-vercel
|
||||
if: ${{ always() && needs.deploy-v2-vercel.result == 'success' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Setup Bun
|
||||
uses: ./.github/composite/setup-bun
|
||||
- name: Install dependencies
|
||||
@@ -217,4 +248,4 @@ jobs:
|
||||
# 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/
|
||||
# SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
|
||||
|
||||
@@ -3,8 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (production)
|
||||
|
||||
@@ -3,8 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (staging)
|
||||
|
||||
@@ -4,9 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
|
||||
@@ -77,23 +77,7 @@ All pull-requests will be tested against both visual and performances testing to
|
||||
|
||||
## Fonts and Icons
|
||||
|
||||
GitBook Open uses fontawesome. During development, your local environment will use the free version. However, only the pro version will be accepted by CI. If you see the following error:
|
||||
|
||||
```
|
||||
The GitBook icon is missing. It indicates that the dependencies were installed without the correct font-awesome package. These changes have probably been persisted in the Bun lockfile. Read the README for more information.
|
||||
```
|
||||
|
||||
It means that you've changed the GBO dependencies and bundled in the free version. Only GitBook staff can help with this - if you're not on the GitBook team, please ping us in the PR and we'll help get things moving.
|
||||
|
||||
If you are GitBook staff, you'll need our NPM token in your local environment.
|
||||
|
||||
```
|
||||
.env.local
|
||||
|
||||
BUN_NPM_TOKEN=xxx
|
||||
```
|
||||
|
||||
and then reinstall dependencies.
|
||||
GitBook Open uses fontawesome. For self-hosting and local development, for licensing reasons, only the icons from the free version should be used.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 622 KiB After Width: | Height: | Size: 491 KiB |
@@ -6,8 +6,8 @@
|
||||
"name": "gitbook",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@changesets/cli": "^2.29.7",
|
||||
"turbo": "^2.6.1",
|
||||
"@changesets/cli": "^2.29.8",
|
||||
"turbo": "^2.7.6",
|
||||
"vercel": "^39.4.2",
|
||||
},
|
||||
},
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"packages/embed": {
|
||||
"name": "@gitbook/embed",
|
||||
"version": "0.1.5",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:",
|
||||
@@ -56,6 +56,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "catalog:",
|
||||
"bun-types": "catalog:",
|
||||
"react": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -108,7 +109,7 @@
|
||||
},
|
||||
"packages/gitbook": {
|
||||
"name": "gitbook",
|
||||
"version": "0.20.0",
|
||||
"version": "0.21.3",
|
||||
"dependencies": {
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
"@gitbook/api": "catalog:",
|
||||
@@ -135,7 +136,6 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@sindresorhus/fnv1a": "^3.1.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tusbar/cache-control": "^1.0.2",
|
||||
"ai": "^4.2.2",
|
||||
"assert-never": "catalog:",
|
||||
@@ -157,7 +157,8 @@
|
||||
"micromark-extension-frontmatter": "^2.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"motion": "^12.23.24",
|
||||
"next": "15.4.10",
|
||||
"negotiator": "^1.0.0",
|
||||
"next": "15.4.11",
|
||||
"next-themes": "^0.4.6",
|
||||
"nuqs": "^2.2.3",
|
||||
"object-hash": "^3.0.0",
|
||||
@@ -175,7 +176,7 @@
|
||||
"remark-rehype": "^11.1.1",
|
||||
"rison": "^0.1.1",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "^3.2.0",
|
||||
"shiki": "^3.21.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwind-shades": "^1.1.2",
|
||||
"unified": "^11.0.5",
|
||||
@@ -196,6 +197,7 @@
|
||||
"@types/jsontoxml": "^1.0.5",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"@types/negotiator": "^0.6.4",
|
||||
"@types/node": "^20",
|
||||
"@types/object-hash": "^3.0.6",
|
||||
"@types/parse-cache-control": "^1.0.4",
|
||||
@@ -218,13 +220,13 @@
|
||||
},
|
||||
"packages/icons": {
|
||||
"name": "@gitbook/icons",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.2",
|
||||
"bin": {
|
||||
"gitbook-icons": "./bin/gitbook-icons.js",
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^7.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||
"@gitbook/fontawesome-pro": "1.0.16",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "catalog:",
|
||||
@@ -233,20 +235,17 @@
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@gitbook/fontawesome-pro": "1.0.16",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
},
|
||||
},
|
||||
"packages/openapi-parser": {
|
||||
"name": "@gitbook/openapi-parser",
|
||||
"version": "3.0.5",
|
||||
"version": "3.0.7",
|
||||
"dependencies": {
|
||||
"@scalar/json-magic": "^0.8.1",
|
||||
"@scalar/openapi-parser": "^0.23.2",
|
||||
"@scalar/openapi-types": "^0.5.1",
|
||||
"@scalar/json-magic": "^0.9.4",
|
||||
"@scalar/openapi-parser": "^0.24.5",
|
||||
"@scalar/openapi-types": "^0.5.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node20": "catalog:",
|
||||
@@ -259,7 +258,7 @@
|
||||
},
|
||||
"packages/react-contentkit": {
|
||||
"name": "@gitbook/react-contentkit",
|
||||
"version": "0.7.8",
|
||||
"version": "0.7.11",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:*",
|
||||
@@ -301,7 +300,7 @@
|
||||
},
|
||||
"packages/react-openapi": {
|
||||
"name": "@gitbook/react-openapi",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.6",
|
||||
"dependencies": {
|
||||
"@gitbook/expr": "workspace:*",
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
@@ -329,7 +328,7 @@
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react": ">=17",
|
||||
"react-dom": "*",
|
||||
},
|
||||
},
|
||||
@@ -346,7 +345,7 @@
|
||||
"react-dom": "catalog:",
|
||||
},
|
||||
"catalog": {
|
||||
"@gitbook/api": "0.154.0",
|
||||
"@gitbook/api": "0.159.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@tsconfig/node20": "^20.1.6",
|
||||
"@tsconfig/strictest": "^2.0.6",
|
||||
@@ -539,21 +538,21 @@
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
||||
|
||||
"@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.13", "", { "dependencies": { "@changesets/config": "^3.1.1", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg=="],
|
||||
"@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.14", "", { "dependencies": { "@changesets/config": "^3.1.2", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA=="],
|
||||
|
||||
"@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.9", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ=="],
|
||||
|
||||
"@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="],
|
||||
|
||||
"@changesets/cli": ["@changesets/cli@2.29.7", "", { "dependencies": { "@changesets/apply-release-plan": "^7.0.13", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.1", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.13", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.5", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.0", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ=="],
|
||||
"@changesets/cli": ["@changesets/cli@2.29.8", "", { "dependencies": { "@changesets/apply-release-plan": "^7.0.14", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.2", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.14", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.6", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.2", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA=="],
|
||||
|
||||
"@changesets/config": ["@changesets/config@3.1.1", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/logger": "^0.1.1", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA=="],
|
||||
"@changesets/config": ["@changesets/config@3.1.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/logger": "^0.1.1", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog=="],
|
||||
|
||||
"@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "^0.1.5" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="],
|
||||
|
||||
"@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ=="],
|
||||
|
||||
"@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.13", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.9", "@changesets/config": "^3.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.5", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg=="],
|
||||
"@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.14", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.9", "@changesets/config": "^3.1.2", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.6", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g=="],
|
||||
|
||||
"@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="],
|
||||
|
||||
@@ -561,11 +560,11 @@
|
||||
|
||||
"@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="],
|
||||
|
||||
"@changesets/parse": ["@changesets/parse@0.4.1", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^3.13.1" } }, "sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q=="],
|
||||
"@changesets/parse": ["@changesets/parse@0.4.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^4.1.1" } }, "sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA=="],
|
||||
|
||||
"@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="],
|
||||
|
||||
"@changesets/read": ["@changesets/read@0.6.5", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.1", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg=="],
|
||||
"@changesets/read": ["@changesets/read@0.6.6", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.2", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg=="],
|
||||
|
||||
"@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="],
|
||||
|
||||
@@ -723,11 +722,9 @@
|
||||
|
||||
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.1.0", "", {}, "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA=="],
|
||||
|
||||
"@fortawesome/fontawesome-free": ["@fortawesome/fontawesome-free@7.1.0", "", {}, "sha512-+WxNld5ZCJHvPQCr/GnzCTVREyStrAJjisUPtUxG5ngDA8TMlPnKp6dddlTpai4+1GNmltAeuk1hJEkBohwZYA=="],
|
||||
|
||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA=="],
|
||||
|
||||
"@gitbook/api": ["@gitbook/api@0.154.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-huEgWWa2H+H4ofYEAQl9r49u0gVjDHHWVNJKhd6SgGCfefjNPHkQj9zaky/5DBQzlu/+SDmutWiOtYdrykQO8w=="],
|
||||
"@gitbook/api": ["@gitbook/api@0.159.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-IRZXT2NAcOlKlCcGnzRH4w82AuBXam5I7lw84qfCRbNDDycvFNBwIVMHu3JMOcGUTCkzj7QDElaNKYirsWDwFQ=="],
|
||||
|
||||
"@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"],
|
||||
|
||||
@@ -873,7 +870,7 @@
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.6", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g=="],
|
||||
|
||||
"@next/env": ["@next/env@15.4.10", "", {}, "sha512-knhmoJ0Vv7VRf6pZEPSnciUG1S4bIhWx+qTYBW/AjxEtlzsiNORPk8sFDCEvqLfmKuey56UB9FL1UdHEV3uBrg=="],
|
||||
"@next/env": ["@next/env@15.4.11", "", {}, "sha512-mIYp/091eYfPFezKX7ZPTWqrmSXq+ih6+LcUyKvLmeLQGhlPtot33kuEOd4U+xAA7sFfj21+OtCpIZx0g5SpvQ=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Pf6zXp7yyQEn7sqMxur6+kYcywx5up1J849psyET7/8pG2gQTVMjU3NzgIt8SeEP5to3If/SaWmaA6H6ysBr1A=="],
|
||||
|
||||
@@ -1287,23 +1284,23 @@
|
||||
|
||||
"@scalar/draggable": ["@scalar/draggable@0.2.0", "", { "dependencies": { "vue": "^3.5.12" } }, "sha512-UetHRB5Bqo5egVYlS21roWBcICmyk8CKh2htsidO+bFGAjl2e7Te+rY0borhNrMclr0xezHlPuLpEs1dvgLS2g=="],
|
||||
|
||||
"@scalar/helpers": ["@scalar/helpers@0.1.1", "", {}, "sha512-eJjuCI5djqU0adTwrHvpDf0xuwNRpwZinCfJ03QjnmIFBM9Ch0u3tn/0EZOhcVNbdEyJ+3yvSVy1dCmmvgtpvQ=="],
|
||||
"@scalar/helpers": ["@scalar/helpers@0.2.9", "", {}, "sha512-Y4ffJF0yELdwZ0BKgonqn3SumIgRn1WKyYCVHD+TDM7qRFChdGRypyt20+efHs26fmJeyBAIIv2laICj5uimiw=="],
|
||||
|
||||
"@scalar/icons": ["@scalar/icons@0.4.7", "", { "dependencies": { "@phosphor-icons/core": "^2.1.1", "@types/node": "^22.9.0", "chalk": "^5.4.1", "vue": "^3.5.17" } }, "sha512-0qXPGRdZ180TMfejWCPYy7ILszBrAraq4KBhPtcM12ghc5qkncFWWpTm5yXI/vrbm10t7wvtTK08CLZ36CnXlQ=="],
|
||||
|
||||
"@scalar/import": ["@scalar/import@0.4.31", "", { "dependencies": { "@scalar/helpers": "0.0.12", "@scalar/openapi-parser": "0.22.3", "yaml": "2.8.0" } }, "sha512-3+3dHz+EvS/C4YEQ8eEihZFVxRVZODsMEHsp48tvXTaIs1Qxb4kf3Nkaf9E3k+6rpM2CUrF59K1P8LvhXob48w=="],
|
||||
|
||||
"@scalar/json-magic": ["@scalar/json-magic@0.8.1", "", { "dependencies": { "@scalar/helpers": "0.1.1", "xxhash-wasm": "^1.1.0", "yaml": "2.8.0" } }, "sha512-PtG0gJxw+iE9492pqy9V+ZbnfRnycIxTAxknKQCOGRyFYfDvYSs0/uyFCHoQPl7HEZoNNA7q/fr0LBZXMRzv7g=="],
|
||||
"@scalar/json-magic": ["@scalar/json-magic@0.9.4", "", { "dependencies": { "@scalar/helpers": "0.2.9", "yaml": "^2.8.0" } }, "sha512-PyfyWrH4ZkW0TM1ColiiHj4NRF8hUM61H0UzAkHLhRNnKFxi6hI+oqNrwqPnyk93hrpkpTRHC7Fl5T0BRwuzVg=="],
|
||||
|
||||
"@scalar/oas-utils": ["@scalar/oas-utils@0.6.3", "", { "dependencies": { "@hyperjump/browser": "^1.1.0", "@hyperjump/json-schema": "^1.9.6", "@scalar/helpers": "0.1.1", "@scalar/json-magic": "0.8.1", "@scalar/object-utils": "1.2.11", "@scalar/openapi-types": "0.5.1", "@scalar/themes": "0.13.23", "@scalar/types": "0.4.0", "@scalar/workspace-store": "0.20.0", "@types/har-format": "^1.2.15", "flatted": "^3.3.3", "js-base64": "^3.7.8", "microdiff": "^1.5.0", "nanoid": "5.1.5", "type-fest": "5.0.0", "yaml": "2.8.0", "zod": "4.1.11" } }, "sha512-+hEMZSLuTeXlrPqpajusZn2R8qs/OrH2Xstllr3GOCnJz7ATwniD69DT7Il49L1j2HJEtX4EpleRX9OMhdoWHg=="],
|
||||
|
||||
"@scalar/object-utils": ["@scalar/object-utils@1.2.11", "", { "dependencies": { "@scalar/helpers": "0.1.1", "flatted": "^3.3.3", "just-clone": "^6.2.0", "ts-deepmerge": "^7.0.1", "type-fest": "5.0.0" } }, "sha512-V44CbfjNuYhoVmVZ36DDlTfyBNF8dMYzLTcDz5zvoSsx+SViXE8ScMd+kgUMHCtSJZV6izV2x3UzIbGQY/pWsQ=="],
|
||||
|
||||
"@scalar/openapi-parser": ["@scalar/openapi-parser@0.23.2", "", { "dependencies": { "@scalar/json-magic": "0.8.1", "@scalar/openapi-types": "0.5.1", "@scalar/openapi-upgrader": "0.1.4", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "2.8.0" } }, "sha512-NzMOWm6sae+viN8luEUqplsc0rO9XdStUM/TY1+o+5gz8KPrDc8/Wh+ksFyfGi0lnwn8GHwi7NVDrBDL5qkXCA=="],
|
||||
"@scalar/openapi-parser": ["@scalar/openapi-parser@0.24.5", "", { "dependencies": { "@scalar/json-magic": "0.9.4", "@scalar/openapi-types": "0.5.3", "@scalar/openapi-upgrader": "0.1.8", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.8.0" } }, "sha512-pTeKnmhVdSIfG3vysgDm6jsKc7Do1vXdy/4aqp7j8AEzXllf8RZjSgRSUhtvFYFQCr27fDZ117V3WPQUYtgmCw=="],
|
||||
|
||||
"@scalar/openapi-types": ["@scalar/openapi-types@0.5.1", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-8g7s9lPolyDFtijyh3Ob459tpezPuZbkXoFgJwBTHjPZ7ap+TvOJTvLk56CFwxVBVz2BxCzWJqxYyy3FUdeLoA=="],
|
||||
"@scalar/openapi-types": ["@scalar/openapi-types@0.5.3", "", { "dependencies": { "zod": "^4.1.11" } }, "sha512-m4n/Su3K01d15dmdWO1LlqecdSPKuNjuokrJLdiQ485kW/hRHbXW1QP6tJL75myhw/XhX5YhYAR+jrwnGjXiMw=="],
|
||||
|
||||
"@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.4", "", { "dependencies": { "@scalar/openapi-types": "0.5.1" } }, "sha512-OKSjey1U99BTg1ZTiNL1xxOEOrP9U4aRTH7Pf6JFXpqFH8kGdhrDAIA0uogYYzNq65BaQwK+h31fSrIf/yCLCg=="],
|
||||
"@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.8", "", { "dependencies": { "@scalar/openapi-types": "0.5.3" } }, "sha512-2xuYLLs0fBadLIk4I1ObjMiCnOyLPEMPf24A1HtHQvhKGDnGlvT63F2rU2Xw8lxCjgHnzveMPnOJEbwIy64RCg=="],
|
||||
|
||||
"@scalar/postman-to-openapi": ["@scalar/postman-to-openapi@0.3.40", "", { "dependencies": { "@scalar/helpers": "0.0.12", "@scalar/oas-utils": "0.5.2", "@scalar/openapi-types": "0.5.0" } }, "sha512-FP1p2/mb0Y5GM9hc+TI9ldDM44VV9GHwdhJQ8xGpArtlt8nxtyKmncOXcgayBD7qk3ohV6W1Eftsr258Eq7gGQ=="],
|
||||
|
||||
@@ -1323,17 +1320,17 @@
|
||||
|
||||
"@scalar/workspace-store": ["@scalar/workspace-store@0.20.0", "", { "dependencies": { "@scalar/code-highlight": "0.2.0", "@scalar/helpers": "0.1.1", "@scalar/json-magic": "0.8.1", "@scalar/openapi-upgrader": "0.1.4", "@scalar/snippetz": "0.5.2", "@scalar/themes": "0.13.23", "@scalar/typebox": "0.1.1", "@scalar/types": "0.4.0", "github-slugger": "^2.0.0", "type-fest": "5.0.0", "vue": "^3.5.21", "yaml": "2.8.0" } }, "sha512-RklQSKjz5V/69rKjxQV70q4u34qzmUZVtWGUJLVvHxPsPxCtxPPRLYY+gdQDkGjTQYol/dR9xyvi2QGk3OWTKA=="],
|
||||
|
||||
"@shikijs/core": ["@shikijs/core@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-+5dPz8q6HgNqfQ28ycm/vA8dIVd2lNFOUqVRFCQLbs0KZ6emYI+1apLpX+wuL/aDSPLOkMgARwNjkA5UjGKS1Q=="],
|
||||
"@shikijs/core": ["@shikijs/core@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA=="],
|
||||
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.1.0" } }, "sha512-1WrYfaz5YT5aTAIMbYQhxlSHc8ArX+hCDNAIdKRqJHzfWQ3xDgh3PTvrAly+RWGuvi5Q4NlvPlTBdlSAXN6Stg=="],
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ=="],
|
||||
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-3V7ko+YUAP02I4rUbDjCgvyM/H85hUIZBQAS19FjDcJMKL5SbjWTiG7TRKxX1V4ddxLxt2RO64wZinElp/3ngQ=="],
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ=="],
|
||||
|
||||
"@shikijs/langs": ["@shikijs/langs@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0" } }, "sha512-Qze5YIsp223AmC69VZDQolcrcYPrVa9wV6cW2kVqsDrSWlwhW2EQZEn1Iw2oQU1tGYVg8Hj/xdp8mOv+9zI0vg=="],
|
||||
"@shikijs/langs": ["@shikijs/langs@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0" } }, "sha512-XfzMSTu6iMl2FZIwKykld2OzFKDDlm4KbZrzW6sbKXEeJ1xq61HX4x4bE4+REBFqbbrvAQM8EAH11m/E3cxYDg=="],
|
||||
"@shikijs/themes": ["@shikijs/themes@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@3.2.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-1uOwfEO0vV+G8n/AO/6Yth7zshNdXvQ1pc4ygTrfE3cyuzVLukrZq72YkFUlsRijam7LvRTvnqL4aT5wx1X2Vw=="],
|
||||
"@shikijs/types": ["@shikijs/types@3.21.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
@@ -1479,8 +1476,6 @@
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="],
|
||||
|
||||
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.10.8", "", {}, "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA=="],
|
||||
|
||||
"@tanstack/vue-virtual": ["@tanstack/vue-virtual@3.10.8", "", { "dependencies": { "@tanstack/virtual-core": "3.10.8" }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" } }, "sha512-DB5QA8c/LfqOqIUCpSs3RdOTVroRRdqeHMqBkYrcashSZtOzIv8xbiqHgg7RYxDfkH5F3Y+e0MkuuyGNDVB0BQ=="],
|
||||
@@ -1535,6 +1530,8 @@
|
||||
|
||||
"@types/ms": ["@types/ms@0.7.34", "", {}, "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="],
|
||||
|
||||
"@types/negotiator": ["@types/negotiator@0.6.4", "", {}, "sha512-elf6BsTq+AkyNsb2h5cGNst2Mc7dPliVoAPm1fXglC/BM3f2pFA40BaSSv3E5lyHteEawVKLP+8TwiY1DMNb3A=="],
|
||||
|
||||
"@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
|
||||
|
||||
"@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
|
||||
@@ -1901,8 +1898,6 @@
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
|
||||
|
||||
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
@@ -2245,7 +2240,7 @@
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
@@ -2323,8 +2318,6 @@
|
||||
|
||||
"locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
||||
|
||||
"lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="],
|
||||
|
||||
"lodash.clonedeep": ["lodash.clonedeep@4.5.0", "", {}, "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="],
|
||||
|
||||
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
|
||||
@@ -2341,8 +2334,6 @@
|
||||
|
||||
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
|
||||
|
||||
"lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="],
|
||||
@@ -2517,7 +2508,7 @@
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"next": ["next@15.4.10", "", { "dependencies": { "@next/env": "15.4.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.8", "@next/swc-darwin-x64": "15.4.8", "@next/swc-linux-arm64-gnu": "15.4.8", "@next/swc-linux-arm64-musl": "15.4.8", "@next/swc-linux-x64-gnu": "15.4.8", "@next/swc-linux-x64-musl": "15.4.8", "@next/swc-win32-arm64-msvc": "15.4.8", "@next/swc-win32-x64-msvc": "15.4.8", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-itVlc79QjpKMFMRhP+kbGKaSG/gZM6RCvwhEbwmCNF06CdDiNaoHcbeg0PqkEa2GOcn8KJ0nnc7+yL7EjoYLHQ=="],
|
||||
"next": ["next@15.4.11", "", { "dependencies": { "@next/env": "15.4.11", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.8", "@next/swc-darwin-x64": "15.4.8", "@next/swc-linux-arm64-gnu": "15.4.8", "@next/swc-linux-arm64-musl": "15.4.8", "@next/swc-linux-x64-gnu": "15.4.8", "@next/swc-linux-x64-musl": "15.4.8", "@next/swc-win32-arm64-msvc": "15.4.8", "@next/swc-win32-x64-msvc": "15.4.8", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-IJRyXal45mIsshZI5XJne/intjusslUP1F+FHVBIyMGEqbYtIq1Irdx5vdWBBg58smviPDycmDeV6txsfkv1RQ=="],
|
||||
|
||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||
|
||||
@@ -2559,9 +2550,9 @@
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"oniguruma-parser": ["oniguruma-parser@0.5.4", "", {}, "sha512-yNxcQ8sKvURiTwP0mV6bLQCYE7NKfKRRWunhbZnXgxSmB1OXa1lHrN3o4DZd+0Si0kU5blidK7BcROO8qv5TZA=="],
|
||||
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.1.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "oniguruma-parser": "^0.5.4", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-SNwG909cSLo4vPyyPbU/VJkEc9WOXqu2ycBlfd1UCXLqk1IijcQktSBb2yRQ2UFPsDhpkaf+C1dtT3PkLK/yWA=="],
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
|
||||
|
||||
"openapi-fetch": ["openapi-fetch@0.13.5", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q=="],
|
||||
|
||||
@@ -2781,7 +2772,7 @@
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.1", "", {}, "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="],
|
||||
|
||||
"shiki": ["shiki@3.2.0", "", { "dependencies": { "@shikijs/core": "3.2.0", "@shikijs/engine-javascript": "3.2.0", "@shikijs/engine-oniguruma": "3.2.0", "@shikijs/langs": "3.2.0", "@shikijs/themes": "3.2.0", "@shikijs/types": "3.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lOF6wkvZCRVQrdfGilyXclTKIjCWKujPAjD6fddLwtQ6eSmgj43pFDbjUmxivtElDRlsGO8G2dLeeRpwNY4wWg=="],
|
||||
"shiki": ["shiki@3.21.0", "", { "dependencies": { "@shikijs/core": "3.21.0", "@shikijs/engine-javascript": "3.21.0", "@shikijs/engine-oniguruma": "3.21.0", "@shikijs/langs": "3.21.0", "@shikijs/themes": "3.21.0", "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w=="],
|
||||
|
||||
"side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="],
|
||||
|
||||
@@ -2915,19 +2906,19 @@
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"turbo": ["turbo@2.6.1", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.1", "turbo-darwin-arm64": "2.6.1", "turbo-linux-64": "2.6.1", "turbo-linux-arm64": "2.6.1", "turbo-windows-64": "2.6.1", "turbo-windows-arm64": "2.6.1" }, "bin": { "turbo": "bin/turbo" } }, "sha512-qBwXXuDT3rA53kbNafGbT5r++BrhRgx3sAo0cHoDAeG9g1ItTmUMgltz3Hy7Hazy1ODqNpR+C7QwqL6DYB52yA=="],
|
||||
"turbo": ["turbo@2.7.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.6", "turbo-darwin-arm64": "2.7.6", "turbo-linux-64": "2.7.6", "turbo-linux-arm64": "2.7.6", "turbo-windows-64": "2.7.6", "turbo-windows-arm64": "2.7.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-PO9AvJLEsNLO+EYhF4zB+v10hOjsJe5kJW+S6tTbRv+TW7gf1Qer4mfjP9h3/y9h8ZiPvOrenxnEgDtFgaM5zw=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.6.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.7.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KCxTf3Y1hgNLYIWRLw8bwH8Zie9RyCGoxAlXYsCBI/YNqBSR+ZZK9KYzFxAqDaVaNvTwLFv3rJRGsXOFWg4+Uw=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.6.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.7.6", "", { "os": "linux", "cpu": "x64" }, "sha512-vjoU8zIfNgvJR3cMitgw7inEoi6bmuVuFawDl5yKtxjAEhDktFdRBpGS3WojD4l3BklBbIK689ssXcGf21LxRA=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.6.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.7.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-TcMpBvTqZf+1DptrVYLbZls7WY1UVNDTGaf0bo7/GCgWYv5eZHCVo4Td7kCJeDU4glbXg67REX0md0S0V6ghMg=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.6.1", "", { "os": "win32", "cpu": "x64" }, "sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.7.6", "", { "os": "win32", "cpu": "x64" }, "sha512-1/MhkYldiihjneY8QnnDMbAkHXn/udTWSVYS94EMlkE9AShozsLTTOT1gDOpX06EfEW5njP09suhMvxbvwuwpQ=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.6.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.7.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-0wDVnUJLFAWm4ZzOQFDkbyyUqaszorTGf3Rdc22IRIyJTTLd6ajqdb+cWD89UZ1RKr953+PZR1gqgWQY4PDuhA=="],
|
||||
|
||||
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
|
||||
|
||||
@@ -3063,7 +3054,7 @@
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
|
||||
"yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
|
||||
|
||||
@@ -3609,8 +3600,6 @@
|
||||
|
||||
"@changesets/logger/picocolors": ["picocolors@1.1.0", "", {}, "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="],
|
||||
|
||||
"@changesets/parse/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||
|
||||
"@codemirror/lang-html/@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA=="],
|
||||
|
||||
"@codemirror/lang-html/@codemirror/lang-css": ["@codemirror/lang-css@6.3.0", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@lezer/common": "^1.0.2", "@lezer/css": "^1.1.7" } }, "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA=="],
|
||||
@@ -3719,8 +3708,6 @@
|
||||
|
||||
"@opennextjs/aws/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||
|
||||
"@opennextjs/aws/yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
|
||||
"@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||
|
||||
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
@@ -3897,6 +3884,8 @@
|
||||
|
||||
"@scalar/api-client/@scalar/workspace-store": ["@scalar/workspace-store@0.17.1", "", { "dependencies": { "@scalar/code-highlight": "0.2.0", "@scalar/helpers": "0.0.12", "@scalar/json-magic": "0.6.1", "@scalar/openapi-upgrader": "0.1.3", "@scalar/snippetz": "0.5.1", "@scalar/typebox": "0.1.1", "@scalar/types": "0.3.2", "github-slugger": "^2.0.0", "type-fest": "5.0.0", "vue": "^3.5.17", "yaml": "2.8.0" } }, "sha512-IWWudWionjVT2JNl+xin9zuoR/I5+f84myt8uzCCKj2PACEjitZvXYwwnqhnLMPPWYI8FQ5dncGKg0zUgRL5zQ=="],
|
||||
|
||||
"@scalar/api-client/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@scalar/api-client/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/api-client-react/@scalar/types": ["@scalar/types@0.3.2", "", { "dependencies": { "@scalar/openapi-types": "0.5.0", "nanoid": "5.1.5", "type-fest": "5.0.0", "zod": "4.1.11" } }, "sha512-+X10CCvG57nAqYbTGteiSzRFQcMYm7DLfCRMeEfiWQ9Bq2ladat17XsMSvkvwcfpOSlsoepWf3P5dErERUSOQQ=="],
|
||||
@@ -3915,8 +3904,20 @@
|
||||
|
||||
"@scalar/import/@scalar/openapi-parser": ["@scalar/openapi-parser@0.22.3", "", { "dependencies": { "@scalar/json-magic": "0.6.1", "@scalar/openapi-types": "0.5.0", "@scalar/openapi-upgrader": "0.1.3", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "2.8.0" } }, "sha512-5Znbx9HVJb7EV9EJXJrUj+cs116QIBwX/hxkyaiLaaDL2w5S+z1rjtV+d0Jv7382FCtzAtfv/9llVuxZYPVqXA=="],
|
||||
|
||||
"@scalar/import/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@scalar/oas-utils/@scalar/helpers": ["@scalar/helpers@0.1.1", "", {}, "sha512-eJjuCI5djqU0adTwrHvpDf0xuwNRpwZinCfJ03QjnmIFBM9Ch0u3tn/0EZOhcVNbdEyJ+3yvSVy1dCmmvgtpvQ=="],
|
||||
|
||||
"@scalar/oas-utils/@scalar/json-magic": ["@scalar/json-magic@0.8.1", "", { "dependencies": { "@scalar/helpers": "0.1.1", "xxhash-wasm": "^1.1.0", "yaml": "2.8.0" } }, "sha512-PtG0gJxw+iE9492pqy9V+ZbnfRnycIxTAxknKQCOGRyFYfDvYSs0/uyFCHoQPl7HEZoNNA7q/fr0LBZXMRzv7g=="],
|
||||
|
||||
"@scalar/oas-utils/@scalar/openapi-types": ["@scalar/openapi-types@0.5.1", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-8g7s9lPolyDFtijyh3Ob459tpezPuZbkXoFgJwBTHjPZ7ap+TvOJTvLk56CFwxVBVz2BxCzWJqxYyy3FUdeLoA=="],
|
||||
|
||||
"@scalar/oas-utils/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@scalar/oas-utils/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/object-utils/@scalar/helpers": ["@scalar/helpers@0.1.1", "", {}, "sha512-eJjuCI5djqU0adTwrHvpDf0xuwNRpwZinCfJ03QjnmIFBM9Ch0u3tn/0EZOhcVNbdEyJ+3yvSVy1dCmmvgtpvQ=="],
|
||||
|
||||
"@scalar/openapi-types/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/postman-to-openapi/@scalar/helpers": ["@scalar/helpers@0.0.12", "", {}, "sha512-4NDmHShyi1hrVRsJCdRZT/FIpy+/5PFbVbQLRYX/pjpu5cYqHBj9s6n5RI6gGDXEBHAIFi63g9FC6Isgr66l1Q=="],
|
||||
@@ -3927,14 +3928,24 @@
|
||||
|
||||
"@scalar/snippetz/@scalar/types": ["@scalar/types@0.3.2", "", { "dependencies": { "@scalar/openapi-types": "0.5.0", "nanoid": "5.1.5", "type-fest": "5.0.0", "zod": "4.1.11" } }, "sha512-+X10CCvG57nAqYbTGteiSzRFQcMYm7DLfCRMeEfiWQ9Bq2ladat17XsMSvkvwcfpOSlsoepWf3P5dErERUSOQQ=="],
|
||||
|
||||
"@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.5.1", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-8g7s9lPolyDFtijyh3Ob459tpezPuZbkXoFgJwBTHjPZ7ap+TvOJTvLk56CFwxVBVz2BxCzWJqxYyy3FUdeLoA=="],
|
||||
|
||||
"@scalar/types/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/use-hooks/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
|
||||
|
||||
"@scalar/use-toasts/vue": ["vue@3.5.12", "", { "dependencies": { "@vue/compiler-dom": "3.5.12", "@vue/compiler-sfc": "3.5.12", "@vue/runtime-dom": "3.5.12", "@vue/server-renderer": "3.5.12", "@vue/shared": "3.5.12" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/helpers": ["@scalar/helpers@0.1.1", "", {}, "sha512-eJjuCI5djqU0adTwrHvpDf0xuwNRpwZinCfJ03QjnmIFBM9Ch0u3tn/0EZOhcVNbdEyJ+3yvSVy1dCmmvgtpvQ=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/json-magic": ["@scalar/json-magic@0.8.1", "", { "dependencies": { "@scalar/helpers": "0.1.1", "xxhash-wasm": "^1.1.0", "yaml": "2.8.0" } }, "sha512-PtG0gJxw+iE9492pqy9V+ZbnfRnycIxTAxknKQCOGRyFYfDvYSs0/uyFCHoQPl7HEZoNNA7q/fr0LBZXMRzv7g=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.4", "", { "dependencies": { "@scalar/openapi-types": "0.5.1" } }, "sha512-OKSjey1U99BTg1ZTiNL1xxOEOrP9U4aRTH7Pf6JFXpqFH8kGdhrDAIA0uogYYzNq65BaQwK+h31fSrIf/yCLCg=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/snippetz": ["@scalar/snippetz@0.5.2", "", { "dependencies": { "@scalar/types": "0.4.0", "js-base64": "^3.7.8", "stringify-object": "^5.0.0" } }, "sha512-DwwowT9si0vrszKbjm8SXL5O6nE6isHbG7Vos85ohd6T4g4h5AYY4+SUyjyRAjzbEISd6A0+VJXaWz/g4PpbZg=="],
|
||||
|
||||
"@scalar/workspace-store/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@shikijs/core/hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
|
||||
"@smithy/chunked-blob-reader-native/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="],
|
||||
@@ -4021,8 +4032,6 @@
|
||||
|
||||
"@tailwindcss/postcss/postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||
|
||||
"@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
|
||||
|
||||
"@ts-morph/common/fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="],
|
||||
|
||||
"@ts-morph/common/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
@@ -4111,6 +4120,8 @@
|
||||
|
||||
"convict/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
|
||||
|
||||
"cosmiconfig/js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"dunder-proto/gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
@@ -4767,8 +4778,6 @@
|
||||
|
||||
"@babel/highlight/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
||||
|
||||
"@changesets/parse/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||
|
||||
"@codemirror/lang-json/@codemirror/language/@codemirror/view": ["@codemirror/view@6.34.1", "", { "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ=="],
|
||||
|
||||
"@codemirror/lang-json/@codemirror/language/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
|
||||
@@ -4909,6 +4918,8 @@
|
||||
|
||||
"@scalar/components/@scalar/oas-utils/@scalar/workspace-store": ["@scalar/workspace-store@0.17.1", "", { "dependencies": { "@scalar/code-highlight": "0.2.0", "@scalar/helpers": "0.0.12", "@scalar/json-magic": "0.6.1", "@scalar/openapi-upgrader": "0.1.3", "@scalar/snippetz": "0.5.1", "@scalar/typebox": "0.1.1", "@scalar/types": "0.3.2", "github-slugger": "^2.0.0", "type-fest": "5.0.0", "vue": "^3.5.17", "yaml": "2.8.0" } }, "sha512-IWWudWionjVT2JNl+xin9zuoR/I5+f84myt8uzCCKj2PACEjitZvXYwwnqhnLMPPWYI8FQ5dncGKg0zUgRL5zQ=="],
|
||||
|
||||
"@scalar/components/@scalar/oas-utils/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@scalar/components/@scalar/oas-utils/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/components/@scalar/themes/@scalar/types": ["@scalar/types@0.3.2", "", { "dependencies": { "@scalar/openapi-types": "0.5.0", "nanoid": "5.1.5", "type-fest": "5.0.0", "zod": "4.1.11" } }, "sha512-+X10CCvG57nAqYbTGteiSzRFQcMYm7DLfCRMeEfiWQ9Bq2ladat17XsMSvkvwcfpOSlsoepWf3P5dErERUSOQQ=="],
|
||||
@@ -4939,6 +4950,8 @@
|
||||
|
||||
"@scalar/postman-to-openapi/@scalar/oas-utils/@scalar/workspace-store": ["@scalar/workspace-store@0.17.1", "", { "dependencies": { "@scalar/code-highlight": "0.2.0", "@scalar/helpers": "0.0.12", "@scalar/json-magic": "0.6.1", "@scalar/openapi-upgrader": "0.1.3", "@scalar/snippetz": "0.5.1", "@scalar/typebox": "0.1.1", "@scalar/types": "0.3.2", "github-slugger": "^2.0.0", "type-fest": "5.0.0", "vue": "^3.5.17", "yaml": "2.8.0" } }, "sha512-IWWudWionjVT2JNl+xin9zuoR/I5+f84myt8uzCCKj2PACEjitZvXYwwnqhnLMPPWYI8FQ5dncGKg0zUgRL5zQ=="],
|
||||
|
||||
"@scalar/postman-to-openapi/@scalar/oas-utils/yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"@scalar/postman-to-openapi/@scalar/oas-utils/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@scalar/postman-to-openapi/@scalar/openapi-types/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
@@ -4957,6 +4970,8 @@
|
||||
|
||||
"@scalar/use-toasts/vue/@vue/shared": ["@vue/shared@3.5.12", "", {}, "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/openapi-upgrader/@scalar/openapi-types": ["@scalar/openapi-types@0.5.1", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-8g7s9lPolyDFtijyh3Ob459tpezPuZbkXoFgJwBTHjPZ7ap+TvOJTvLk56CFwxVBVz2BxCzWJqxYyy3FUdeLoA=="],
|
||||
|
||||
"@shikijs/core/hast-util-to-html/property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="],
|
||||
|
||||
"@smithy/chunked-blob-reader-native/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="],
|
||||
@@ -5331,6 +5346,8 @@
|
||||
|
||||
"@scalar/use-toasts/vue/@vue/compiler-sfc/postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||
|
||||
"@scalar/workspace-store/@scalar/openapi-upgrader/@scalar/openapi-types/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
|
||||
|
||||
"@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[install.scopes]
|
||||
"gitbook" = { token = "$BUN_NPM_TOKEN", url = "https://registry.npmjs.org" }
|
||||
+4
-4
@@ -6,11 +6,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@changesets/cli": "^2.29.7",
|
||||
"turbo": "^2.6.1",
|
||||
"@changesets/cli": "^2.29.8",
|
||||
"turbo": "^2.7.6",
|
||||
"vercel": "^39.4.2"
|
||||
},
|
||||
"packageManager": "bun@1.3.2",
|
||||
"packageManager": "bun@1.3.7",
|
||||
"overrides": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@types/react": "catalog:",
|
||||
@@ -41,7 +41,7 @@
|
||||
"catalog": {
|
||||
"@tsconfig/strictest": "^2.0.6",
|
||||
"@tsconfig/node20": "^20.1.6",
|
||||
"@gitbook/api": "0.154.0",
|
||||
"@gitbook/api": "0.159.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
|
||||
@@ -46,6 +46,11 @@ export type GitBookAssistant = {
|
||||
ui?: boolean;
|
||||
};
|
||||
|
||||
export type GitBookCookieBannerHandler = (options: {
|
||||
onApprove: () => void;
|
||||
onReject: () => void;
|
||||
}) => void;
|
||||
|
||||
export type GitBookGlobal = {
|
||||
/**
|
||||
* Register an event listener.
|
||||
@@ -72,6 +77,18 @@ export type GitBookGlobal = {
|
||||
* Register a custom assistant to be available on the site.
|
||||
*/
|
||||
registerAssistant: (assistant: GitBookAssistant) => () => void;
|
||||
|
||||
/**
|
||||
* Register a custom cookie banner handler.
|
||||
* When registered, the built-in cookie banner will not be displayed.
|
||||
*/
|
||||
registerCookieBanner: (handler: GitBookCookieBannerHandler) => void;
|
||||
|
||||
/**
|
||||
* Whether the user has granted cookie consent.
|
||||
* Returns true if cookies are granted, false if rejected or unknown.
|
||||
*/
|
||||
hasApprovedCookies: () => boolean;
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# @gitbook/embed
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 1430cee: Improve Docs Embed with separate Assistant and Docs tabs
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [62438f4]
|
||||
- @gitbook/icons@0.4.0
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "0.1.5",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:",
|
||||
@@ -23,6 +23,7 @@
|
||||
"react": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -33,6 +34,7 @@
|
||||
"build-lib": "tsdown",
|
||||
"build-standalone": "bun build src/standalone/index.ts --bundle --minify --outdir=standalone",
|
||||
"clean": "rm -rf ./dist",
|
||||
"unit": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "bun run build -- --watch ./src",
|
||||
"publish-to-npm": "../../scripts/publish-if-new.sh"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import { createGitBook } from './createGitBook';
|
||||
|
||||
describe('createGitBook.getFrameURL', () => {
|
||||
it('builds the embed URL when the site URL has no trailing slash', () => {
|
||||
const client = createGitBook({ siteURL: 'https://example.com/docs' });
|
||||
|
||||
const url = new URL(client.getFrameURL({}));
|
||||
|
||||
expect(url.origin).toBe('https://example.com');
|
||||
expect(url.pathname).toBe('/docs/~gitbook/embed');
|
||||
expect(url.searchParams.toString()).toBe('');
|
||||
});
|
||||
|
||||
it('handles a trailing slash and adds visitor parameters', () => {
|
||||
const client = createGitBook({ siteURL: 'https://example.com/docs/' });
|
||||
|
||||
const url = new URL(
|
||||
client.getFrameURL({
|
||||
visitor: {
|
||||
token: 'signed-token',
|
||||
unsignedClaims: {
|
||||
role: 'editor',
|
||||
count: 3,
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(url.pathname).toBe('/docs/~gitbook/embed');
|
||||
expect(url.searchParams.get('jwt_token')).toBe('signed-token');
|
||||
expect(url.searchParams.get('visitor.role')).toBe('editor');
|
||||
expect(url.searchParams.get('visitor.count')).toBe('3');
|
||||
expect(url.searchParams.get('visitor.enabled')).toBe('false');
|
||||
});
|
||||
});
|
||||
@@ -43,7 +43,7 @@ export function createGitBook(options: CreateGitBookOptions) {
|
||||
url.pathname = `${url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`}~gitbook/embed`;
|
||||
|
||||
if (frameOptions.visitor?.token) {
|
||||
url.searchParams.set('token', frameOptions.visitor.token);
|
||||
url.searchParams.set('jwt_token', frameOptions.visitor.token);
|
||||
}
|
||||
|
||||
if (frameOptions.visitor?.unsignedClaims) {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './client';
|
||||
export * from './standalone';
|
||||
|
||||
@@ -11,13 +11,21 @@ import { useGitBook } from './GitBookProvider';
|
||||
export type GitBookFrameProps = {
|
||||
className?: string;
|
||||
} & GetFrameURLOptions &
|
||||
GitBookEmbeddableConfiguration;
|
||||
Partial<GitBookEmbeddableConfiguration>;
|
||||
|
||||
/**
|
||||
* Render a frame with the GitBook Assistant in it.
|
||||
*/
|
||||
export function GitBookFrame(props: GitBookFrameProps) {
|
||||
const { className, visitor, actions, greeting, suggestions, tools } = props;
|
||||
const {
|
||||
className,
|
||||
visitor,
|
||||
actions = [],
|
||||
greeting,
|
||||
suggestions = [],
|
||||
tools = [],
|
||||
tabs = ['assistant', 'docs'],
|
||||
} = props;
|
||||
|
||||
const frameRef = useRef<HTMLIFrameElement>(null);
|
||||
const gitbook = useGitBook();
|
||||
@@ -33,13 +41,13 @@ export function GitBookFrame(props: GitBookFrameProps) {
|
||||
|
||||
useEffect(() => {
|
||||
gitbookFrame?.configure({
|
||||
tabs: ['assistant', 'docs'],
|
||||
tabs,
|
||||
actions,
|
||||
greeting,
|
||||
suggestions,
|
||||
tools,
|
||||
});
|
||||
}, [gitbookFrame, actions, greeting, suggestions, tools]);
|
||||
}, [gitbookFrame, actions, greeting, suggestions, tools, tabs]);
|
||||
|
||||
return (
|
||||
<iframe
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"types": []
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -1,5 +1,73 @@
|
||||
# gitbook
|
||||
|
||||
## 0.21.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bf63045]
|
||||
- @gitbook/react-openapi@1.5.6
|
||||
|
||||
## 0.21.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ca68236: Fix 404 when accessing rss.xml for the root page.
|
||||
- 21851d3: Round page cover corners on extrawide screens
|
||||
- 695e99f: Update button sizing
|
||||
- 7517e98: Clean up button and input across the app
|
||||
- 99d788e: Update page cover styling
|
||||
- ebf3b2b: Fix an issue where content refs to spaces outside of site was resolving with share links
|
||||
- 35efc9d: Fix TOCPageIcons backgrounds on gradient and bold themes
|
||||
- a7f58ab: Fix embed tab button size regression
|
||||
- 360b525: Switch icon masking method to fix visual cutoffs in FA7
|
||||
- Updated dependencies [985c1b4]
|
||||
- Updated dependencies [360b525]
|
||||
- @gitbook/openapi-parser@3.0.7
|
||||
- @gitbook/icons@0.4.2
|
||||
- @gitbook/react-openapi@1.5.5
|
||||
- @gitbook/react-contentkit@0.7.11
|
||||
|
||||
## 0.21.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e7fb771]
|
||||
- @gitbook/icons@0.4.1
|
||||
- @gitbook/react-contentkit@0.7.10
|
||||
|
||||
## 0.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3553db0: Add a RSS feed to all pages with update blocks.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f478ddc: Add Input component
|
||||
- 3817289: Fix overflowing section groups
|
||||
- 86e7aa6: Fix search results cursor
|
||||
- 1430cee: Improve Docs Embed with separate Assistant and Docs tabs
|
||||
- ed9e8fe: Add support for button actions
|
||||
- 9c69cc4: Add customizable suggested questions
|
||||
- c51076e: Fix links to other spaces and root page in embeddable view.
|
||||
- 6278a18: Support `greeting` and fix suggested questions if there are no custom ones defined
|
||||
- 04a4175: Remove hidden site spaces from navigation
|
||||
- 74f8931: Fix hidden section not found
|
||||
- 344842f: Improve OpenAPI circular references
|
||||
- Updated dependencies [4766092]
|
||||
- Updated dependencies [8761cee]
|
||||
- Updated dependencies [461e15f]
|
||||
- Updated dependencies [1430cee]
|
||||
- Updated dependencies [62438f4]
|
||||
- Updated dependencies [3e40b4d]
|
||||
- Updated dependencies [87d68ea]
|
||||
- Updated dependencies [344842f]
|
||||
- @gitbook/react-openapi@1.5.4
|
||||
- @gitbook/openapi-parser@3.0.6
|
||||
- @gitbook/embed@0.2.0
|
||||
- @gitbook/icons@0.4.0
|
||||
- @gitbook/react-contentkit@0.7.9
|
||||
|
||||
## 0.20.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { type TestsCase, getCustomizationURL, runTestCases } from './util';
|
||||
|
||||
const testCases: TestsCase[] = [
|
||||
{
|
||||
name: 'Cookie Banner',
|
||||
contentBaseURL: 'https://gitbook.com/docs/',
|
||||
tests: [
|
||||
{
|
||||
name: 'should show built-in banner when no custom banner is registered',
|
||||
url: getCustomizationURL({
|
||||
privacyPolicy: {
|
||||
url: 'https://policies.gitbook.com/privacy/cookies',
|
||||
},
|
||||
}),
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
// Check that built-in banner is visible
|
||||
const dialog = page.getByTestId('cookies-dialog');
|
||||
await expect(dialog).toBeVisible({ timeout: 5000 });
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should not show built-in banner when custom banner is registered',
|
||||
url: getCustomizationURL({
|
||||
privacyPolicy: {
|
||||
url: 'https://policies.gitbook.com/privacy/cookies',
|
||||
},
|
||||
}),
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
// Register a custom cookie banner handler
|
||||
await page.waitForFunction(() => {
|
||||
return typeof window !== 'undefined' && window.GitBook !== undefined;
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
window.GitBook?.registerCookieBanner(() => {
|
||||
// Custom cookie banner handler - no-op for testing to avoid reload
|
||||
});
|
||||
});
|
||||
|
||||
// Check that built-in banner is not visible
|
||||
const dialog = page.getByTestId('cookies-dialog');
|
||||
await expect(dialog).not.toBeVisible({ timeout: 5000 });
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
runTestCases(testCases);
|
||||
@@ -10,18 +10,18 @@ const testCases: TestsCase[] = [
|
||||
{ name: 'OpenAPI', url: '/snyk-api/reference/apps', run: waitForCookiesDialog },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'Nexthink',
|
||||
// contentBaseURL: 'https://docs.nexthink.com',
|
||||
// tests: [
|
||||
// {
|
||||
// name: 'Home',
|
||||
// url: '/',
|
||||
// screenshot: { waitForTOCScrolling: false },
|
||||
// run: waitForCookiesDialog,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
name: 'Nexthink',
|
||||
contentBaseURL: 'https://docs.nexthink.com',
|
||||
tests: [
|
||||
{
|
||||
name: 'Home',
|
||||
url: '/',
|
||||
screenshot: { waitForTOCScrolling: false },
|
||||
run: waitForCookiesDialog,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'asiksupport-stg.dto.kemkes.go.id',
|
||||
contentBaseURL: 'https://asiksupport-stg.dto.kemkes.go.id',
|
||||
@@ -37,11 +37,6 @@ const testCases: TestsCase[] = [
|
||||
contentBaseURL: 'https://faq.deltaemulator.com',
|
||||
tests: [{ name: 'Home', url: '/' }],
|
||||
},
|
||||
{
|
||||
name: 'docs.dify.ai',
|
||||
contentBaseURL: 'https://docs.dify.ai',
|
||||
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
},
|
||||
{
|
||||
name: 'seeddao.gitbook.io',
|
||||
contentBaseURL: 'https://seeddao.gitbook.io',
|
||||
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
CustomizationIconsStyle,
|
||||
CustomizationSidebarListStyle,
|
||||
CustomizationThemeMode,
|
||||
SiteSocialAccountPlatform,
|
||||
} from '@gitbook/api';
|
||||
import type { GitBookStandalone } from '@gitbook/embed';
|
||||
import { expect } from '@playwright/test';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
@@ -40,7 +42,7 @@ import {
|
||||
const AI_PROMPT = `You're being invoked by the GitBook CI/CD pipeline. To make screenshot testing of the GitBook Assistant visually consistent, look up the title of the first page you find and respond with only EXACTLY its title. To find the page title, invoke the search tool with the query "GitBook". Before invoking the search tool, respond with the exact text: "I'm going to look up 'GitBook' and then respond with only the page title.". Do not execute any other tools or output any other text.`;
|
||||
|
||||
const overrideAIInitialState = () => {
|
||||
const greeting = document.querySelector('[data-testid="ai-chat-time-greeting"]');
|
||||
const greeting = document.querySelector('[data-testid="ai-chat-greeting-title"]');
|
||||
if (greeting) {
|
||||
greeting.textContent = 'Good morning';
|
||||
}
|
||||
@@ -272,10 +274,10 @@ const testCases: TestsCase[] = [
|
||||
await expect(navigationLink).not.toBeVisible();
|
||||
|
||||
// Find and click the chevron element that is next to "Editor" in the TOC
|
||||
// It is a span inside the link
|
||||
// It is a button inside the link
|
||||
const editorChevron = page
|
||||
.getByRole('link', { name: 'Editor' })
|
||||
.locator('span');
|
||||
.locator('button');
|
||||
await editorChevron.click();
|
||||
|
||||
// Verify "Navigation" link becomes visible after expansion
|
||||
@@ -294,10 +296,10 @@ const testCases: TestsCase[] = [
|
||||
await expect(navigationLink).not.toBeVisible();
|
||||
|
||||
// Find and click the chevron element that is next to "Editor" in the TOC
|
||||
// It is a span inside the link
|
||||
// It is a button inside the link
|
||||
const editorChevron = page
|
||||
.getByRole('link', { name: 'Editor' })
|
||||
.locator('span');
|
||||
.locator('button');
|
||||
await editorChevron.click();
|
||||
|
||||
// At this stage the link should still not be visible
|
||||
@@ -306,7 +308,7 @@ const testCases: TestsCase[] = [
|
||||
// Then we click 'Content Structure' chevron to expand further
|
||||
const contentStructureChevron = page
|
||||
.getByRole('link', { name: 'Content Structure' })
|
||||
.locator('span');
|
||||
.locator('button');
|
||||
await contentStructureChevron.click();
|
||||
|
||||
// Verify "Spaces" link becomes visible after expansion
|
||||
@@ -676,70 +678,6 @@ const testCases: TestsCase[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Markdown page',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
tests: [
|
||||
{
|
||||
name: 'Text page',
|
||||
url: 'text-page.md',
|
||||
screenshot: false,
|
||||
run: async (_page, response) => {
|
||||
expect(response?.status()).toBe(200);
|
||||
expect(response?.headers()['content-type']).toContain('text/markdown');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'llms.txt',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
tests: [
|
||||
{
|
||||
name: 'llms.txt',
|
||||
url: 'llms.txt',
|
||||
screenshot: false,
|
||||
run: async (_page, response) => {
|
||||
expect(response?.status()).toBe(200);
|
||||
expect(response?.headers()['content-type']).toContain('text/markdown');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'llms-full.txt',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
tests: [
|
||||
{
|
||||
name: 'llms-full.txt',
|
||||
url: 'llms-full.txt',
|
||||
screenshot: false,
|
||||
run: async (_page, response) => {
|
||||
expect(response?.status()).toBe(200);
|
||||
expect(response?.headers()['content-type']).toContain('text/markdown');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '[page].md',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
tests: [
|
||||
{
|
||||
name: 'blocks.md',
|
||||
url: 'blocks.md',
|
||||
screenshot: false,
|
||||
run: async (_page, response) => {
|
||||
expect(response?.status()).toBe(200);
|
||||
expect(response?.headers()['content-type']).toContain('text/markdown');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Site subdirectory (proxy)',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
@@ -1186,6 +1124,16 @@ const testCases: TestsCase[] = [
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: 'Reusable contents',
|
||||
contentBaseURL: 'https://gitbook-open-e2e-sites.gitbook.io/reusable-contents/',
|
||||
tests: [
|
||||
{
|
||||
name: 'All cases',
|
||||
url: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Page actions',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
@@ -1202,6 +1150,36 @@ const testCases: TestsCase[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Social links',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
tests: [
|
||||
{
|
||||
name: 'With social links',
|
||||
url: getCustomizationURL({
|
||||
socialAccounts: [
|
||||
{
|
||||
platform: SiteSocialAccountPlatform.Github,
|
||||
display: { footer: true },
|
||||
handle: 'GitbookIO',
|
||||
},
|
||||
{
|
||||
platform: SiteSocialAccountPlatform.Linkedin,
|
||||
display: { footer: true },
|
||||
handle: 'gitbook',
|
||||
},
|
||||
{
|
||||
platform: SiteSocialAccountPlatform.Twitter,
|
||||
display: { footer: false },
|
||||
handle: 'GitBookIO',
|
||||
},
|
||||
],
|
||||
}),
|
||||
fullPage: true,
|
||||
run: waitForCookiesDialog,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Ads',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
@@ -1291,7 +1269,7 @@ const testCases: TestsCase[] = [
|
||||
name: 'Redirect to Quickstart page',
|
||||
url: 'sections-2/redirect-test',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('h1')).toHaveText('Quickstart');
|
||||
await expect(page.locator('h1')).toContainText('Quickstart');
|
||||
},
|
||||
screenshot: false,
|
||||
},
|
||||
@@ -1317,6 +1295,46 @@ const testCases: TestsCase[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Navigation of links on a share links site',
|
||||
contentBaseURL: 'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions-share-links/',
|
||||
tests: [
|
||||
{
|
||||
name: 'Link in same site has the share link token preserved',
|
||||
url: '8tNo6MeXg7CkFMzSSz81/link-in-same-site',
|
||||
run: async (page) => {
|
||||
const linkToDifferentPage = page.getByRole('link', { name: 'Other Page' });
|
||||
await linkToDifferentPage.click();
|
||||
await page.waitForURL((url) =>
|
||||
url.pathname.includes(
|
||||
'/api-multi-versions-share-links/8tNo6MeXg7CkFMzSSz81/3.0/other-page'
|
||||
)
|
||||
);
|
||||
await expect(
|
||||
page.getByRole('heading', { level: 1, name: 'Other Page' })
|
||||
).toBeVisible();
|
||||
},
|
||||
screenshot: false,
|
||||
},
|
||||
{
|
||||
name: 'Link to different site should not have the share link token preserved',
|
||||
url: '8tNo6MeXg7CkFMzSSz81/link-in-different-site',
|
||||
run: async (page) => {
|
||||
const linkToDifferentSite = page.getByRole('link', { name: 'Basics' });
|
||||
await linkToDifferentSite.click();
|
||||
await page.waitForURL(
|
||||
(url) =>
|
||||
url.toString() ===
|
||||
'https://gitbook-open-e2e-sites.gitbook.io/sections/sections-4/basics/editor'
|
||||
);
|
||||
await expect(
|
||||
page.getByRole('heading', { level: 1, name: 'Editor' })
|
||||
).toBeVisible();
|
||||
},
|
||||
screenshot: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Visitor Auth - Space',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/gbo-va-space/',
|
||||
@@ -1946,6 +1964,324 @@ const testCases: TestsCase[] = [
|
||||
]),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Docs Embed - Basic',
|
||||
contentBaseURL: 'https://gitbook.com/docs/~gitbook/embed/demo/',
|
||||
tests: [
|
||||
{
|
||||
name: 'Standalone UX',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
const button = page.locator('#gitbook-widget-button');
|
||||
await expect(button).toBeVisible();
|
||||
await expect(button.locator('#gitbook-widget-button-label')).toHaveText('Ask');
|
||||
await expect(button.locator('#gitbook-widget-button-icon')).toHaveAttribute(
|
||||
'data-icon',
|
||||
'assistant'
|
||||
);
|
||||
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
await button.click(); // Toggle the window off for the screenshot
|
||||
await expect(page.locator('#gitbook-widget-window')).not.toBeVisible();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Change standalone button label and icon',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate(() => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('configure', {
|
||||
button: {
|
||||
label: 'Docs',
|
||||
icon: 'book',
|
||||
},
|
||||
});
|
||||
});
|
||||
const button = page.locator('#gitbook-widget-button');
|
||||
await expect(button).toBeVisible();
|
||||
button.click();
|
||||
await expect(page.locator('#gitbook-widget-window')).not.toBeVisible();
|
||||
await expect(page.locator('#gitbook-widget-button-label')).toHaveText('Docs');
|
||||
await expect(page.locator('#gitbook-widget-button-icon')).toHaveAttribute(
|
||||
'data-icon',
|
||||
'book'
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Docs Embed - Assistant + Docs',
|
||||
contentBaseURL: 'https://gitbook.com/docs/~gitbook/embed/demo/',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
tests: [
|
||||
{
|
||||
name: 'Switch between tabs',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await iframe.getByTestId('embed-tab-docs').click(); // Switch to docs tab
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible({
|
||||
timeout: 20000,
|
||||
});
|
||||
|
||||
await iframe.getByTestId('embed-tab-assistant').click(); // Switch to assistant tab
|
||||
await expect(iframe.getByTestId('ai-chat')).toBeVisible();
|
||||
|
||||
await iframe.owner().evaluate(overrideAIInitialState);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'API - navigateToPage',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate(() => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('navigateToPage', '/getting-started/quickstart');
|
||||
});
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible({
|
||||
timeout: 20000,
|
||||
});
|
||||
await expect(iframe.owner()).toHaveAttribute(
|
||||
'src',
|
||||
expect.stringContaining('getting-started/quickstart')
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'API - postUserMessage',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate((aiPrompt) => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('postUserMessage', aiPrompt);
|
||||
}, AI_PROMPT);
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('ai-chat')).toBeVisible();
|
||||
await expect(iframe.getByTestId('ai-chat-message-user').first()).toHaveText(
|
||||
AI_PROMPT
|
||||
);
|
||||
await iframe.owner().evaluate(overrideAIResponse);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Configuration - Suggested questions',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate(() => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('configure', {
|
||||
suggestions: [
|
||||
'What is GitBook?',
|
||||
'How do I get started?',
|
||||
'What can you do?',
|
||||
],
|
||||
});
|
||||
});
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(
|
||||
iframe.getByTestId('ai-chat-suggested-question').nth(0)
|
||||
).toHaveText('What is GitBook?');
|
||||
await expect(
|
||||
iframe.getByTestId('ai-chat-suggested-question').nth(1)
|
||||
).toHaveText('How do I get started?');
|
||||
await expect(
|
||||
iframe.getByTestId('ai-chat-suggested-question').nth(2)
|
||||
).toHaveText('What can you do?');
|
||||
await iframe.owner().evaluate(overrideAIInitialState);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Configuration - Custom action buttons',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate((aiPrompt) => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('configure', {
|
||||
actions: [
|
||||
{
|
||||
label: 'Open internal link',
|
||||
icon: 'bolt',
|
||||
onClick: () => {
|
||||
const GitBook =
|
||||
window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('navigateToPage', '/getting-started/quickstart');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open external link',
|
||||
icon: 'sparkle',
|
||||
onClick: () => {
|
||||
window.open('https://gitbook.com', '_blank');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Post message',
|
||||
icon: 'message',
|
||||
onClick: () => {
|
||||
const GitBook =
|
||||
window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('postUserMessage', aiPrompt);
|
||||
GitBook('navigateToAssistant');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
icon: 'xmark',
|
||||
onClick: () => {
|
||||
const GitBook =
|
||||
window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('close');
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}, AI_PROMPT);
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('embed-action')).toHaveCount(4);
|
||||
const actions = iframe.getByTestId('embed-action');
|
||||
|
||||
await expect(actions.nth(0)).toHaveAccessibleName('Open internal link');
|
||||
await actions.nth(0).click();
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible();
|
||||
await expect(iframe.owner()).toHaveAttribute(
|
||||
'src',
|
||||
expect.stringContaining('getting-started/quickstart')
|
||||
);
|
||||
|
||||
await expect(actions.nth(1)).toHaveAccessibleName('Open external link');
|
||||
// Intercept the new page event without navigating
|
||||
const [newPage] = await Promise.all([
|
||||
page.context().waitForEvent('page', { timeout: 5000 }),
|
||||
actions.nth(1).click(),
|
||||
]);
|
||||
// Verify the new page would have opened with the expected URL
|
||||
expect(newPage.url()).toContain('gitbook.com');
|
||||
// Close it immediately to avoid navigation
|
||||
await newPage.close();
|
||||
|
||||
await expect(actions.nth(2)).toHaveAccessibleName('Post message');
|
||||
await actions.nth(2).click();
|
||||
await expect(iframe.getByTestId('ai-chat')).toBeVisible();
|
||||
await expect(iframe.getByTestId('ai-chat-message-user').first()).toHaveText(
|
||||
AI_PROMPT
|
||||
);
|
||||
|
||||
await expect(actions.nth(3)).toHaveAccessibleName('Close');
|
||||
await actions.nth(3).click();
|
||||
await expect(page.locator('#gitbook-widget-window')).not.toBeVisible();
|
||||
await page.locator('#gitbook-widget-button').click();
|
||||
await iframe.owner().evaluate(overrideAIResponse);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Configuration - Custom tools',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.evaluate(() => {
|
||||
const GitBook = window.GitBook as unknown as GitBookStandalone;
|
||||
GitBook('configure', {
|
||||
tools: [
|
||||
{
|
||||
name: 'contact_support',
|
||||
description: 'Contact support on behalf of the user',
|
||||
execute: async () => {
|
||||
return {
|
||||
output: { message: 'Support message' },
|
||||
summary: {
|
||||
text: 'Contacted support',
|
||||
icon: 'circle-question',
|
||||
},
|
||||
};
|
||||
},
|
||||
confirmation: {
|
||||
icon: 'circle-question',
|
||||
label: 'Contact support',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
GitBook(
|
||||
'postUserMessage',
|
||||
'I want to contact support. Call the tool directly without a preamble. Do not respond with anything else.'
|
||||
);
|
||||
});
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('ai-chat-message-user').first()).toHaveText(
|
||||
'I want to contact support. Call the tool directly without a preamble. Do not respond with anything else.'
|
||||
);
|
||||
const toolConfirmation = iframe
|
||||
.getByTestId('ai-chat-tool-confirmation')
|
||||
.first();
|
||||
await expect(toolConfirmation).toBeVisible({
|
||||
timeout: 30000,
|
||||
});
|
||||
await iframe.owner().evaluate(overrideAIResponse);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Docs Embed - Docs Only',
|
||||
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/~gitbook/embed/demo/',
|
||||
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
|
||||
tests: [
|
||||
{
|
||||
name: 'Docs only',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible({
|
||||
timeout: 20000,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Table of contents',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible({
|
||||
timeout: 20000,
|
||||
});
|
||||
const tocButton = iframe.getByTestId('toc-button');
|
||||
await expect(tocButton).toBeVisible();
|
||||
await tocButton.click();
|
||||
await expect(iframe.getByTestId('table-of-contents')).toBeVisible();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Open in new tab',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('#gitbook-widget-window')).toBeVisible();
|
||||
const iframe = page.frameLocator('#gitbook-widget-iframe');
|
||||
await expect(iframe.getByTestId('embed-docs-page')).toBeVisible({
|
||||
timeout: 20000,
|
||||
});
|
||||
const openInNewTabButton = iframe.getByTestId(
|
||||
'embed-docs-page-open-in-new-tab'
|
||||
);
|
||||
await expect(openInNewTabButton).toBeVisible();
|
||||
// Intercept the new page event without navigating
|
||||
const [newPage] = await Promise.all([
|
||||
page.context().waitForEvent('page', { timeout: 5000 }),
|
||||
openInNewTabButton.click(),
|
||||
]);
|
||||
// Verify the new page would have opened with the expected URL
|
||||
expect(newPage.url()).toContain('gitbook.gitbook.io');
|
||||
// Close it immediately to avoid navigation
|
||||
await newPage.close();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
runTestCases(testCases);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { argosScreenshot } from '@argos-ci/playwright';
|
||||
import {
|
||||
CustomizationAIMode,
|
||||
CustomizationBackground,
|
||||
CustomizationCodeTheme,
|
||||
CustomizationCorners,
|
||||
CustomizationDefaultFont,
|
||||
CustomizationDefaultMonospaceFont,
|
||||
@@ -298,6 +299,16 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
|
||||
background: CustomizationBackground.Plain,
|
||||
icons: CustomizationIconsStyle.Regular,
|
||||
links: CustomizationLinksStyle.Default,
|
||||
codeTheme: {
|
||||
default: {
|
||||
light: CustomizationCodeTheme.DefaultLight,
|
||||
dark: CustomizationCodeTheme.DefaultDark,
|
||||
},
|
||||
openapi: {
|
||||
light: CustomizationCodeTheme.DefaultLight,
|
||||
dark: CustomizationCodeTheme.DefaultDark,
|
||||
},
|
||||
},
|
||||
sidebar: {
|
||||
background: CustomizationSidebarBackgroundStyle.Default,
|
||||
list: CustomizationSidebarListStyle.Default,
|
||||
@@ -355,6 +366,7 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
|
||||
url: 'https://www.gitbook.com/privacy',
|
||||
},
|
||||
socialPreview: {},
|
||||
socialAccounts: [],
|
||||
};
|
||||
|
||||
const encoded = rison.encode_object(deepMerge(DEFAULT_CUSTOMIZATION, partial));
|
||||
@@ -417,14 +429,15 @@ export async function waitForIcons(page: Page) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// url("https://ka-p.fontawesome.com/releases/v6.6.0/svgs/light/moon.svg?v=2&token=a463935e93")
|
||||
const maskImage = window.getComputedStyle(icon).getPropertyValue('mask-image');
|
||||
const urlMatch = maskImage.match(/url\("([^"]+)"\)/);
|
||||
const url = urlMatch?.[1];
|
||||
const maskImage = icon.querySelector('[data-testid="mask-image"]');
|
||||
if (!maskImage) {
|
||||
throw new Error('No mask-image element');
|
||||
}
|
||||
|
||||
const url = maskImage.getAttribute('href');
|
||||
// If URL is invalid we throw an error.
|
||||
if (!url) {
|
||||
throw new Error('No mask-image');
|
||||
throw new Error('No mask-image url');
|
||||
}
|
||||
|
||||
// If the URL is already queued for loading, we return the state.
|
||||
@@ -454,7 +467,8 @@ export async function waitForIcons(page: Page) {
|
||||
*/
|
||||
async function waitForTOCScrolling(page: Page) {
|
||||
const viewport = await page.viewportSize();
|
||||
if (viewport && viewport.width >= 1024) {
|
||||
if (viewport && viewport.width >= 1024 && !page.url().includes('~gitbook/embed/demo')) {
|
||||
// The embed demo is an iframe, which means the viewport is only a fraction of the main document. So there is no open TOC to scroll to.
|
||||
const toc = page.getByTestId('table-of-contents');
|
||||
await expect(toc).toBeVisible();
|
||||
await page.evaluate(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.20.0",
|
||||
"version": "0.21.3",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
@@ -28,7 +28,6 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@sindresorhus/fnv1a": "^3.1.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tusbar/cache-control": "^1.0.2",
|
||||
"ai": "^4.2.2",
|
||||
"assert-never": "catalog:",
|
||||
@@ -50,7 +49,7 @@
|
||||
"micromark-extension-frontmatter": "^2.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"motion": "^12.23.24",
|
||||
"next": "15.4.10",
|
||||
"next": "15.4.11",
|
||||
"next-themes": "^0.4.6",
|
||||
"nuqs": "^2.2.3",
|
||||
"object-hash": "^3.0.0",
|
||||
@@ -68,7 +67,7 @@
|
||||
"remark-rehype": "^11.1.1",
|
||||
"rison": "^0.1.1",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "^3.2.0",
|
||||
"shiki": "^3.21.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwind-shades": "^1.1.2",
|
||||
"unified": "^11.0.5",
|
||||
@@ -78,7 +77,8 @@
|
||||
"usehooks-ts": "catalog:",
|
||||
"warn-once": "^0.1.1",
|
||||
"zod": "^3",
|
||||
"zustand": "^5.0.3"
|
||||
"zustand": "^5.0.3",
|
||||
"negotiator": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@argos-ci/playwright": "^5.0.9",
|
||||
@@ -95,6 +95,7 @@
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/rison": "^0.0.9",
|
||||
"@types/negotiator": "^0.6.4",
|
||||
"bun-types": "catalog:",
|
||||
"deepmerge": "^4.3.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
@@ -119,7 +120,7 @@
|
||||
"dev:cloudflare": "wrangler dev --port 8771 --env preview",
|
||||
"dev:cf:middleware": "wrangler dev --port 8771 --inspector-port 9230 --env dev --config ./openNext/customWorkers/middlewareWrangler.jsonc",
|
||||
"dev:cf:server": "wrangler dev --port 8772 --env dev --config ./openNext/customWorkers/defaultWrangler.jsonc",
|
||||
"e2e": "playwright test e2e/internal.spec.ts e2e/pdf.spec.ts --project=chromium",
|
||||
"e2e": "playwright test e2e/internal.spec.ts e2e/cookie-banner.spec.ts e2e/pdf.spec.ts --project=chromium",
|
||||
"e2e-customers": "playwright test e2e/customers.spec.ts --project=chromium",
|
||||
"unit": "bun test {src,packages} --preload ./tests/preload-bun.ts",
|
||||
"e2e-browserless": "bun test ./tests/",
|
||||
|
||||
@@ -83,6 +83,11 @@ export function getPagePathFromParams(params: RouteParams) {
|
||||
// If decoding the param fails, return a 404 instead of crashing
|
||||
try {
|
||||
const decoded = decodeURIComponent(params.pagePath);
|
||||
|
||||
// For the root page, we encode '/' to avoid an empty param being passed.
|
||||
if (decoded === '/') {
|
||||
return '';
|
||||
}
|
||||
return decoded;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
|
||||
@@ -39,7 +39,10 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
|
||||
const { toolCall, context } = props;
|
||||
|
||||
return (
|
||||
<div className="mt-2 flex origin-top-left animate-blur-in-slow items-start gap-2 text-sm text-tint-subtle">
|
||||
<div
|
||||
data-testid="ai-chat-tool-summary"
|
||||
className="mt-2 flex origin-top-left animate-blur-in-slow items-start gap-2 text-sm text-tint-subtle"
|
||||
>
|
||||
<Icon
|
||||
icon={getIconForToolCall(toolCall)}
|
||||
className="mt-1 size-3 shrink-0 text-tint-subtle/8"
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { GitBookAssistant } from '@gitbook/browser-types';
|
||||
import { useAIChatController, useAIChatState } from '.';
|
||||
import { AIChatIcon, AISearchIcon, getAIChatName } from '../AIChat';
|
||||
import { useIntegrationAssistants } from '../Integrations';
|
||||
import { useSearch } from '../Search/useSearch';
|
||||
import { useSetSearchState } from '../Search/useSearch';
|
||||
|
||||
// Unify assistants configuration context with the assistants hook in one place
|
||||
export type AIConfig = {
|
||||
@@ -84,7 +84,7 @@ export function useAI(): AIContext {
|
||||
const chat = useAIChatState();
|
||||
const chatController = useAIChatController();
|
||||
const language = useLanguage();
|
||||
const [, setSearchState] = useSearch();
|
||||
const setSearchState = useSetSearchState();
|
||||
|
||||
const assistants: Assistant[] = [];
|
||||
|
||||
@@ -96,7 +96,7 @@ export function useAI(): AIContext {
|
||||
<AIChatIcon
|
||||
state={chat.loading ? 'thinking' : 'default'}
|
||||
trademark={config.trademark}
|
||||
className="size-4"
|
||||
className="size-text-lg"
|
||||
/>
|
||||
),
|
||||
open: (query?: string) => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { IconName } from '@gitbook/icons';
|
||||
import * as React from 'react';
|
||||
import { useTrackEvent } from '../Insights';
|
||||
import { integrationsAssistantTools } from '../Integrations';
|
||||
import { useSearch } from '../Search';
|
||||
import { useSetSearchState } from '../Search';
|
||||
import { type RenderAIMessageOptions, streamAIChatResponse } from './server-actions';
|
||||
import { useAIMessageContextRef } from './useAIMessageContext';
|
||||
|
||||
@@ -163,7 +163,7 @@ export function AIChatProvider(props: {
|
||||
|
||||
const messageContextRef = useAIMessageContextRef();
|
||||
const trackEvent = useTrackEvent();
|
||||
const [, setSearchState] = useSearch();
|
||||
const setSearchState = useSetSearchState();
|
||||
const language = useLanguage();
|
||||
|
||||
// Event listeners storage
|
||||
|
||||
@@ -80,14 +80,13 @@ export function AIChat() {
|
||||
chatController.close();
|
||||
}
|
||||
}}
|
||||
data-testid="ai-chat"
|
||||
withScrim={true}
|
||||
withOverlay={true}
|
||||
className={tcls(
|
||||
'ai-chat mx-auto ml-8 not-hydrated:hidden w-96 transition-[width] duration-300 ease-quint lg:max-xl:w-80'
|
||||
)}
|
||||
>
|
||||
<EmbeddableFrame className="relative shrink-0 border-tint-subtle border-l to-tint-base">
|
||||
<EmbeddableFrameMain>
|
||||
<EmbeddableFrameMain data-testid="ai-chat">
|
||||
<EmbeddableFrameHeader>
|
||||
<AIChatDynamicIcon trademark={config.trademark} />
|
||||
<EmbeddableFrameHeaderMain>
|
||||
@@ -104,7 +103,6 @@ export function AIChat() {
|
||||
icon="close"
|
||||
label={tString(language, 'close')}
|
||||
variant="blank"
|
||||
size="default"
|
||||
/>
|
||||
</EmbeddableFrameButtons>
|
||||
</EmbeddableFrameHeader>
|
||||
@@ -248,13 +246,14 @@ export function AIChatBody(props: {
|
||||
<h5
|
||||
className="animate-blur-in-slow font-bold text-lg text-tint-strong leading-tight [@container(min-height:400px)]:text-center"
|
||||
style={{ animationDelay: '.5s' }}
|
||||
data-testid="ai-chat-time-greeting"
|
||||
data-testid="ai-chat-greeting-title"
|
||||
>
|
||||
{greeting?.title || timeGreeting}
|
||||
</h5>
|
||||
<p
|
||||
className="animate-blur-in-slow text-tint leading-tight [@container(min-height:400px)]:text-center"
|
||||
style={{ animationDelay: '.6s' }}
|
||||
data-testid="ai-chat-greeting-subtitle"
|
||||
>
|
||||
{greeting?.subtitle ||
|
||||
t(language, 'ai_chat_assistant_description')}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
import { useLanguage } from '@/intl/client';
|
||||
import { t } from '@/intl/translate';
|
||||
import type { Assistant } from '../AI';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { Button } from '../primitives';
|
||||
import { KeyboardShortcut } from '../primitives/KeyboardShortcut';
|
||||
|
||||
const MOBILE_BREAKPOINT = 672; // 42rem, equal to Tailwind's @max-2xl container breakpoint
|
||||
|
||||
/**
|
||||
* Button to open/close the AI chat.
|
||||
*/
|
||||
@@ -15,15 +18,15 @@ export function AIChatButton(props: {
|
||||
}) {
|
||||
const { assistant, showLabel = true, withShortcut = true } = props;
|
||||
const language = useLanguage();
|
||||
const isMobile = useIsMobile(MOBILE_BREAKPOINT, '#header-content');
|
||||
|
||||
return (
|
||||
<Button
|
||||
icon={assistant.icon}
|
||||
data-testid="ai-chat-button"
|
||||
iconOnly={!showLabel}
|
||||
iconOnly={!showLabel || isMobile}
|
||||
size="medium"
|
||||
variant="header"
|
||||
className="h-9 px-2.5 @max-2xl:[&_.button-content]:hidden"
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
{t(language, 'ai_chat_ask', assistant.label)}
|
||||
|
||||
@@ -22,7 +22,6 @@ export function AIChatControlButton() {
|
||||
icon="trash-can"
|
||||
label={t(language, 'ai_chat_clear_conversation')}
|
||||
variant="blank"
|
||||
size="default"
|
||||
className="animate-blur-in-slow"
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -55,6 +55,7 @@ export function AIChatInput(props: {
|
||||
placeholder={tString(language, 'ai_chat_input_placeholder')}
|
||||
onSubmit={(val) => onSubmit(val as string)}
|
||||
submitButton={{
|
||||
size: 'small',
|
||||
label: tString(language, 'send'),
|
||||
}}
|
||||
className="animate-blur-in-slow bg-tint-base/9 backdrop-blur-lg contrast-more:bg-tint-base"
|
||||
|
||||
@@ -19,13 +19,17 @@ export default function AIChatSuggestedQuestions(props: {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-2 self-start">
|
||||
<div
|
||||
className="flex flex-col items-start gap-2 self-start"
|
||||
data-testid="ai-chat-suggested-questions"
|
||||
>
|
||||
{suggestions.map((question, index) => (
|
||||
<Button
|
||||
data-testid="ai-chat-suggested-question"
|
||||
key={question}
|
||||
variant="blank"
|
||||
size="medium"
|
||||
className="max-w-full animate-blur-in-slow border-none bg-primary-solid/1 px-3 py-1.5 hover:bg-primary-hover"
|
||||
size="small"
|
||||
className="max-w-full animate-blur-in-slow border-none bg-primary-solid/1 hover:bg-primary-hover"
|
||||
style={{
|
||||
animationDelay: `${1000 + index * 100}ms`,
|
||||
}}
|
||||
|
||||
@@ -37,13 +37,14 @@ export function AIChatToolConfirmations(props: {
|
||||
style={{ animationDelay: `${0.5 + index * 0.1}s` }}
|
||||
>
|
||||
<Button
|
||||
data-testid="ai-chat-tool-confirmation"
|
||||
onClick={() => {
|
||||
tool.confirm();
|
||||
}}
|
||||
tabIndex={index}
|
||||
label={tool.label}
|
||||
className="w-full justify-center"
|
||||
size={index === 0 ? 'default' : 'medium'}
|
||||
size={index === 0 ? 'medium' : 'small'}
|
||||
variant={index === 0 ? 'primary' : 'secondary'}
|
||||
icon={tool.icon}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function AIResponseFeedback(props: {
|
||||
'animate-fade-in overflow-hidden text-tint-subtle transition-all',
|
||||
rating !== null && rating !== 1 && 'px-0 text-[0rem] opacity-0'
|
||||
)}
|
||||
size="medium"
|
||||
size="small"
|
||||
style={{ animationDelay: '.5s', animationDuration: '.5s' }}
|
||||
onClick={() => handleRating(1)}
|
||||
disabled={rating !== null}
|
||||
@@ -52,7 +52,7 @@ export function AIResponseFeedback(props: {
|
||||
'animate-fade-in overflow-hidden text-tint-subtle transition-all',
|
||||
rating !== null && rating !== -1 && 'px-0 text-[0rem] opacity-0'
|
||||
)}
|
||||
size="medium"
|
||||
size="small"
|
||||
style={{ animationDelay: '.7s', animationDuration: '.5s' }}
|
||||
onClick={() => handleRating(-1)}
|
||||
disabled={rating !== null}
|
||||
|
||||
@@ -28,8 +28,8 @@ export function AIChatFollowupSuggestions(props: {
|
||||
chatController.postMessage({ message: suggestion });
|
||||
}}
|
||||
label={suggestion}
|
||||
className="starting:h-0 max-w-full origin-left animate-blur-in-slow whitespace-normal border-none bg-primary-11/1 px-3 py-1.5 starting:py-0 text-left transition-all transition-discrete duration-500 *:whitespace-normal hover:bg-primary-hover"
|
||||
size="medium"
|
||||
className="starting:h-0 max-w-full origin-left animate-blur-in-slow whitespace-normal border-none bg-primary-11/1 starting:py-0 text-left transition-all transition-discrete duration-500 *:whitespace-normal hover:bg-primary-hover"
|
||||
size="small"
|
||||
variant="blank"
|
||||
style={{
|
||||
animationDelay: `${250 + Math.min(index * 50, 150)}ms`,
|
||||
|
||||
@@ -14,6 +14,10 @@ const adaptiveVisitorReaderCache = new Map<
|
||||
ReturnType<typeof createResourceReader<AdaptiveVisitorClaims | null>>
|
||||
>();
|
||||
|
||||
export function clearAdaptiveVisitorClaimsCache() {
|
||||
adaptiveVisitorReaderCache.clear();
|
||||
}
|
||||
|
||||
function createResourceReader<T>(promise: Promise<T>) {
|
||||
let result: T | null | undefined;
|
||||
|
||||
|
||||
@@ -35,68 +35,66 @@ export function AnnouncementBanner(props: {
|
||||
data-nosnippet=""
|
||||
>
|
||||
<div className="transition-all duration-300 lg:chat-open:pr-80 xl:chat-open:pr-96">
|
||||
<div className={tcls(CONTAINER_STYLE)}>
|
||||
<div className="relative">
|
||||
<Tag
|
||||
href={contentRef?.href ?? ''}
|
||||
className={tcls(
|
||||
'flex w-full items-start justify-center overflow-hidden circular-corners:rounded-xl rounded-md straight-corners:rounded-none px-4 py-3 text-neutral-strong text-sm theme-bold:ring-1 theme-gradient:ring-1 ring-inset transition-colors',
|
||||
style.container,
|
||||
closeable && 'pr-12',
|
||||
hasLink && style.hover
|
||||
)}
|
||||
insights={
|
||||
announcement.link
|
||||
? {
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: announcement.link.to,
|
||||
position: SiteInsightsLinkPosition.Announcement,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
icon={style.icon as IconName}
|
||||
className={`mt-0.5 mr-3 size-4 shrink-0 ${style.iconColor}`}
|
||||
/>
|
||||
<div>
|
||||
{announcement.message}
|
||||
{hasLink ? (
|
||||
<div className={tcls(LinkStyles, style.link, 'ml-1 inline')}>
|
||||
{contentRef?.icon ? (
|
||||
<span className="mr-1 ml-2 *:inline">
|
||||
{contentRef?.icon}
|
||||
</span>
|
||||
) : null}
|
||||
{announcement.link?.title && (
|
||||
<span className="mr-1">{announcement.link?.title}</span>
|
||||
)}
|
||||
<Icon
|
||||
icon={
|
||||
announcement.link?.to.kind === 'url'
|
||||
? 'arrow-up-right'
|
||||
: 'chevron-right'
|
||||
}
|
||||
className={tcls('mb-0.5 inline size-3')}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Tag>
|
||||
{closeable ? (
|
||||
<Button
|
||||
iconOnly
|
||||
icon="close"
|
||||
label={tString(language, 'close')}
|
||||
variant="blank"
|
||||
size="default"
|
||||
onClick={dismissAnnouncement}
|
||||
className={`absolute top-0 right-0 mt-2 mr-2 circular-corners:rounded-lg rounded-sm straight-corners:rounded-none p-1.5 transition-all hover:ring-1 ${style.close}`}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={tcls('relative', CONTAINER_STYLE)}>
|
||||
<Tag
|
||||
href={contentRef?.href ?? ''}
|
||||
className={tcls(
|
||||
'flex w-full items-start justify-center overflow-hidden circular-corners:rounded-xl rounded-md straight-corners:rounded-none px-4 py-3 text-neutral-strong text-sm theme-bold:ring-1 theme-gradient:ring-1 ring-inset transition-colors',
|
||||
style.container,
|
||||
closeable && 'pr-12',
|
||||
hasLink && style.hover
|
||||
)}
|
||||
insights={
|
||||
announcement.link
|
||||
? {
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: announcement.link.to,
|
||||
position: SiteInsightsLinkPosition.Announcement,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
icon={style.icon as IconName}
|
||||
className={`mt-0.5 mr-3 size-4 shrink-0 ${style.iconColor}`}
|
||||
/>
|
||||
<div>
|
||||
{announcement.message}
|
||||
{hasLink ? (
|
||||
<div className={tcls(LinkStyles, style.link, 'ml-1 inline')}>
|
||||
{contentRef?.icon ? (
|
||||
<span className="mr-1 ml-2 *:inline">
|
||||
{contentRef?.icon}
|
||||
</span>
|
||||
) : null}
|
||||
{announcement.link?.title && (
|
||||
<span className="mr-1">{announcement.link?.title}</span>
|
||||
)}
|
||||
<Icon
|
||||
icon={
|
||||
announcement.link?.to.kind === 'url'
|
||||
? 'arrow-up-right'
|
||||
: 'chevron-right'
|
||||
}
|
||||
className={tcls('mb-0.5 inline size-3')}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Tag>
|
||||
{closeable ? (
|
||||
<Button
|
||||
iconOnly
|
||||
icon="close"
|
||||
label={tString(language, 'close')}
|
||||
variant="blank"
|
||||
size="small"
|
||||
onClick={dismissAnnouncement}
|
||||
className={`absolute top-0 right-4 mt-1.5 mr-1.5 circular-corners:rounded-lg rounded-sm straight-corners:rounded-none p-1.5 transition-all hover:ring-1 sm:right-6 md:right-8 ${style.close}`}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useLanguage } from '@/intl/client';
|
||||
import { t, tString } from '@/intl/translate';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { useCustomCookieBanner, useIntegrationsLoaded } from '@/components/Integrations';
|
||||
import { isCookiesTrackingDisabled, setCookiesTracking } from '../Insights';
|
||||
|
||||
/**
|
||||
@@ -15,10 +16,18 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
|
||||
const { privacyPolicy = 'https://policies.gitbook.com/privacy/cookies' } = props;
|
||||
const [show, setShow] = React.useState(false);
|
||||
const language = useLanguage();
|
||||
const integrationsLoaded = useIntegrationsLoaded();
|
||||
const { hasCustomCookieBanner } = useCustomCookieBanner();
|
||||
|
||||
React.useEffect(() => {
|
||||
// Always wait for integrations to load, and if a custom banner is registered, hide the built-in banner
|
||||
if (!integrationsLoaded || hasCustomCookieBanner) {
|
||||
setShow(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setShow(isCookiesTrackingDisabled() === undefined);
|
||||
}, []);
|
||||
}, [hasCustomCookieBanner, integrationsLoaded]);
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
@@ -61,10 +70,11 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
|
||||
'lg:chat-open:mr-80',
|
||||
'xl:chat-open:mr-100',
|
||||
'transition-all',
|
||||
'duration-300'
|
||||
'duration-300',
|
||||
'text-sm'
|
||||
)}
|
||||
>
|
||||
<p id={describedById} className={tcls('text-sm')}>
|
||||
<p id={describedById}>
|
||||
{t(
|
||||
language,
|
||||
'cookies_prompt',
|
||||
@@ -76,12 +86,13 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
|
||||
<Button
|
||||
iconOnly
|
||||
icon="close"
|
||||
size="small"
|
||||
label={tString(language, 'close')}
|
||||
variant="blank"
|
||||
onClick={() => setShow(false)}
|
||||
className={tcls('absolute', 'top-2', 'right-2', 'hover:bg-tint-hover')}
|
||||
/>
|
||||
<div className={tcls('mt-3', 'flex', 'flex-row', 'gap-2')}>
|
||||
<div className="mt-3 flex flex-row gap-2">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="small"
|
||||
|
||||
@@ -7,18 +7,18 @@ import {
|
||||
import { Card, type CardProps } from '@/components/primitives';
|
||||
import {
|
||||
type ResolvedContentRef,
|
||||
resolveContentRef,
|
||||
resolveContentRefFallback,
|
||||
resolveContentRefInDocument,
|
||||
} from '@/lib/references';
|
||||
|
||||
import type { BlockProps } from './Block';
|
||||
import { NotFoundRefHoverCard } from './NotFoundRefHoverCard';
|
||||
|
||||
export async function BlockContentRef(props: BlockProps<DocumentBlockContentRef>) {
|
||||
const { block, context, style } = props;
|
||||
const { document, block, context, style } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRef(block.data.ref, context.contentContext, {
|
||||
? await resolveContentRefInDocument(document, block.data.ref, context.contentContext, {
|
||||
resolveAnchorText: true,
|
||||
iconStyle: ['text-xl', 'text-tint'],
|
||||
})
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
'use client';
|
||||
|
||||
import type { DocumentBlockCode } from '@gitbook/api';
|
||||
import type { CustomizationThemedCodeTheme, DocumentBlockCode } from '@gitbook/api';
|
||||
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { useAdaptiveVisitor } from '@/components/Adaptive';
|
||||
import { useInViewportListener } from '@/components/hooks/useInViewportListener';
|
||||
import { useScrollListener } from '@/components/hooks/useScrollListener';
|
||||
import { Button } from '@/components/primitives';
|
||||
import { Button, ToggleChevron } from '@/components/primitives';
|
||||
import { t, useLanguage } from '@/intl/client';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
import { useDebounceCallback } from 'usehooks-ts';
|
||||
import type { BlockProps } from '../Block';
|
||||
import { type InlineExpressionVariables, useEvaluateInlineExpression } from '../InlineExpression';
|
||||
import { CodeBlockRenderer } from './CodeBlockRenderer';
|
||||
import type { HighlightLine, RenderedInline } from './highlight';
|
||||
import type { HighlightTheme, RenderedInline } from './highlight';
|
||||
import { plainHighlight } from './plain-highlight';
|
||||
|
||||
type ClientBlockProps = Pick<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
|
||||
inlines: RenderedInline[];
|
||||
inlineExprVariables: InlineExpressionVariables;
|
||||
mode: BlockProps<DocumentBlockCode>['context']['mode'];
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
};
|
||||
|
||||
export const CODE_BLOCK_DEFAULT_COLLAPSED_LINE_COUNT = 10;
|
||||
@@ -29,7 +30,7 @@ export const CODE_BLOCK_DEFAULT_COLLAPSED_LINE_COUNT = 10;
|
||||
* It allows us to defer some load to avoid blocking the rendering of the whole page with block highlighting.
|
||||
*/
|
||||
export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
const { block, mode, style, inlines, inlineExprVariables } = props;
|
||||
const { block, mode, style, inlines, inlineExprVariables, themes } = props;
|
||||
const blockRef = useRef<HTMLDivElement>(null);
|
||||
const isInViewportRef = useRef(false);
|
||||
const [isInViewport, setIsInViewport] = useState(false);
|
||||
@@ -40,17 +41,17 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
visitorClaims,
|
||||
variables: inlineExprVariables,
|
||||
});
|
||||
const plainLines = useMemo(
|
||||
() => plainHighlight(block, inlines, { evaluateInlineExpression }),
|
||||
[block, inlines, evaluateInlineExpression]
|
||||
const plainTheme = useMemo(
|
||||
() => plainHighlight(block, inlines, { evaluateInlineExpression, themes }),
|
||||
[block, inlines, evaluateInlineExpression, themes]
|
||||
);
|
||||
const [lines, setLines] = useState<null | HighlightLine[]>(null);
|
||||
const [theme, setTheme] = useState<null | HighlightTheme>(null);
|
||||
const [highlighting, setHighlighting] = useState(false);
|
||||
|
||||
// Preload the highlighter when the block is mounted.
|
||||
useEffect(() => {
|
||||
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block));
|
||||
}, [block]);
|
||||
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block, themes));
|
||||
}, [block, themes]);
|
||||
|
||||
// When user scrolls, we need to wait for the scroll to finish before running the highlight
|
||||
const isScrollingRef = useRef(false);
|
||||
@@ -99,14 +100,16 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
if (typeof window !== 'undefined') {
|
||||
setHighlighting(true);
|
||||
import('./highlight').then(({ highlight }) => {
|
||||
highlight(block, inlines, { evaluateInlineExpression }).then((lines) => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
highlight(block, inlines, { evaluateInlineExpression, themes }).then(
|
||||
(theme) => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLines(lines);
|
||||
setHighlighting(false);
|
||||
});
|
||||
setTheme(theme);
|
||||
setHighlighting(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -116,12 +119,12 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
}
|
||||
|
||||
// Otherwise if the block is not in viewport, we reset to the plain lines
|
||||
setLines(null);
|
||||
}, [isInViewport, block, inlines, evaluateInlineExpression]);
|
||||
setTheme(null);
|
||||
}, [isInViewport, block, inlines, evaluateInlineExpression, themes]);
|
||||
|
||||
const expandable = block.data.expandable;
|
||||
|
||||
const numberOfLinesOfCode = lines?.length ?? plainLines.length;
|
||||
const numberOfLinesOfCode = theme?.lines.length ?? plainTheme.lines.length;
|
||||
const collapsedLineCount =
|
||||
block.data.collapsedLineCount || CODE_BLOCK_DEFAULT_COLLAPSED_LINE_COUNT;
|
||||
const isExpandable = Boolean(
|
||||
@@ -136,16 +139,17 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
aria-busy={highlighting}
|
||||
block={block}
|
||||
style={style}
|
||||
lines={lines ?? plainLines}
|
||||
theme={theme ?? plainTheme}
|
||||
id={codeBlockBodyId}
|
||||
/>
|
||||
);
|
||||
|
||||
return isExpandable ? (
|
||||
<CodeBlockExpandable
|
||||
lines={lines ?? plainLines}
|
||||
theme={theme ?? plainTheme}
|
||||
controls={codeBlockBodyId}
|
||||
collapsedLineCount={collapsedLineCount}
|
||||
style={style}
|
||||
>
|
||||
{renderer}
|
||||
</CodeBlockExpandable>
|
||||
@@ -156,21 +160,25 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
|
||||
function CodeBlockExpandable(props: {
|
||||
children: React.ReactNode;
|
||||
lines: HighlightLine[];
|
||||
theme: HighlightTheme;
|
||||
collapsedLineCount: number;
|
||||
controls?: string;
|
||||
style?: ClassValue;
|
||||
}) {
|
||||
const { children, controls, lines = [], collapsedLineCount } = props;
|
||||
const { children, controls, theme, collapsedLineCount, style } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const language = useLanguage();
|
||||
return (
|
||||
<div className="group/codeblock-expandable relative">
|
||||
<div
|
||||
className={tcls('group/codeblock-expandable relative', style)}
|
||||
data-follow-color-scheme="true"
|
||||
aria-expanded={isExpanded}
|
||||
>
|
||||
<div
|
||||
className={tcls(
|
||||
isExpanded
|
||||
? '[&_pre]:after:opacity-0'
|
||||
: '[&_pre]:h-[calc(2rem+var(--line-count)*var(--line-height))] [&_pre]:overflow-y-hidden [&_pre]:after:opacity-100',
|
||||
'[&_pre]:after:pointer-events-none [&_pre]:after:absolute [&_pre]:after:inset-0 [&_pre]:after:z-1 [&_pre]:after:bg-gradient-to-t [&_pre]:after:from-0% [&_pre]:after:from-tint-2 [&_pre]:after:to-70% [&_pre]:after:to-transparent [&_pre]:after:content-[""]'
|
||||
!isExpanded
|
||||
? '[&_pre]:h-[calc(2rem+var(--line-count)*var(--line-height))] [&_pre]:overflow-y-hidden'
|
||||
: ''
|
||||
)}
|
||||
style={
|
||||
{
|
||||
@@ -181,9 +189,9 @@ function CodeBlockExpandable(props: {
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className="pointer-events-none absolute bottom-0 flex w-full justify-center">
|
||||
<div className="pointer-events-none absolute bottom-2 flex w-full justify-center">
|
||||
<Button
|
||||
icon={isExpanded ? 'chevron-up' : 'chevron-down'}
|
||||
icon={<ToggleChevron open={isExpanded} />}
|
||||
size="xsmall"
|
||||
variant="blank"
|
||||
type="button"
|
||||
@@ -194,7 +202,7 @@ function CodeBlockExpandable(props: {
|
||||
>
|
||||
{isExpanded
|
||||
? t(language, 'code_block_expanded')
|
||||
: t(language, 'code_block_collapsed', lines.length)}
|
||||
: t(language, 'code_block_collapsed', theme.lines.length)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { DocumentBlockCode } from '@gitbook/api';
|
||||
import type {
|
||||
CustomizationThemedCodeTheme,
|
||||
DocumentBlockCode,
|
||||
SiteCustomizationSettings,
|
||||
} from '@gitbook/api';
|
||||
|
||||
import { getNodeFragmentByType } from '@/lib/document';
|
||||
|
||||
@@ -13,8 +17,21 @@ import { type RenderedInline, getInlines, highlight } from './highlight';
|
||||
/**
|
||||
* Render a code block, can be client-side or server-side.
|
||||
*/
|
||||
export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
|
||||
const { block, document, style, isEstimatedOffscreen, context } = props;
|
||||
export async function CodeBlock(
|
||||
props: BlockProps<DocumentBlockCode> & {
|
||||
themeKey?: keyof SiteCustomizationSettings['styling']['codeTheme'];
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
block,
|
||||
document,
|
||||
style,
|
||||
isEstimatedOffscreen,
|
||||
context,
|
||||
themeKey = 'default',
|
||||
themes: providedThemes,
|
||||
} = props;
|
||||
const inlines = getInlines(block);
|
||||
|
||||
let hasInlineExpression = false;
|
||||
@@ -50,10 +67,19 @@ export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
|
||||
return { inline, body };
|
||||
});
|
||||
|
||||
// Get code themes from customization
|
||||
const themes =
|
||||
providedThemes ??
|
||||
(context.contentContext && 'customization' in context.contentContext
|
||||
? context.contentContext.customization.styling.codeTheme[themeKey]
|
||||
: undefined);
|
||||
|
||||
if (!isEstimatedOffscreen && !hasInlineExpression && !block.data.expandable) {
|
||||
// In v2, we render the code block server-side
|
||||
const lines = await highlight(block, richInlines);
|
||||
return <CodeBlockRenderer block={block} style={style} lines={lines} />;
|
||||
const theme = await highlight(block, richInlines, {
|
||||
themes: themes,
|
||||
});
|
||||
return <CodeBlockRenderer block={block} style={style} theme={theme} />;
|
||||
}
|
||||
|
||||
const variables = context.contentContext
|
||||
@@ -74,6 +100,7 @@ export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
|
||||
inlines={richInlines}
|
||||
inlineExprVariables={variables}
|
||||
mode={context.mode}
|
||||
themes={themes}
|
||||
/>
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
@@ -7,10 +7,10 @@ import { tcls } from '@/lib/tailwind';
|
||||
import { AnnotationPopover } from '../Annotation/AnnotationPopover';
|
||||
import type { BlockProps } from '../Block';
|
||||
import { CopyCodeButton } from './CopyCodeButton';
|
||||
import type { HighlightLine, HighlightToken } from './highlight';
|
||||
import type { HighlightLine, HighlightTheme, HighlightToken } from './highlight';
|
||||
|
||||
type CodeBlockRendererProps = Pick<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
|
||||
lines: HighlightLine[];
|
||||
theme: HighlightTheme;
|
||||
'aria-busy'?: boolean;
|
||||
id?: string;
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
props: CodeBlockRendererProps,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { block, style, lines, 'aria-busy': ariaBusy } = props;
|
||||
const { block, style, theme, 'aria-busy': ariaBusy } = props;
|
||||
|
||||
const withLineNumbers = Boolean(block.data.lineNumbers) && block.nodes.length > 1;
|
||||
const withWrap = block.data.overflow === 'wrap';
|
||||
@@ -30,15 +30,35 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
|
||||
const id = useId();
|
||||
const codeId = props.id || id;
|
||||
|
||||
/* Shiki returns a color + CSS variables in a single string, which isn't supported in React.
|
||||
So we parse the foreground & background into objects that can be passed into React's `style` prop. */
|
||||
const bg = parseShikiColorString(theme.bg);
|
||||
const fg = parseShikiColorString(theme.fg);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
aria-busy={ariaBusy}
|
||||
className={tcls('group/codeblock grid shrink grid-flow-col overflow-hidden', style)}
|
||||
className={tcls(
|
||||
'group/codeblock shiki grid shrink grid-flow-col overflow-hidden',
|
||||
style
|
||||
)}
|
||||
/* Sets the code theme's mode (light or dark) for the site's theme mode (light or dark).
|
||||
* Used to style UI elements (scrollbars, form controls) correctly and apply the right default to "plain" code blocks. */
|
||||
data-color-scheme={`${theme.themes.light.type} ${theme.themes.dark.type}`}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2 text-sm [grid-area:1/1]">
|
||||
{title ? (
|
||||
<div className="relative top-px z-20 inline-flex items-center justify-center rounded-t straight-corners:rounded-t-s border border-tint-subtle border-b-0 bg-tint-subtle theme-muted:bg-tint-base px-3 py-2 text-tint text-xs leading-none tracking-wide contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base">
|
||||
<div
|
||||
className="relative top-px z-20 inline-flex items-center justify-center circular-corners:rounded-t-xl rounded-corners:rounded-t-lg straight-corners:rounded-t-xs border border-tint-subtle border-b-0 bg-tint-subtle theme-bold-tint:bg-tint-base theme-muted:bg-tint-base px-3 py-2 text-tint text-xs leading-none tracking-wide contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base"
|
||||
style={{
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -49,23 +69,32 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
/>
|
||||
<pre
|
||||
className={tcls(
|
||||
'relative overflow-auto border border-tint-subtle bg-tint-subtle theme-muted:bg-tint-base p-2 [grid-area:2/1] contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base',
|
||||
'rounded-md straight-corners:rounded-xs shadow-xs',
|
||||
title && 'rounded-ss-none'
|
||||
'relative overflow-auto border border-tint-subtle bg-tint-subtle theme-bold-tint:bg-tint-base theme-muted:bg-tint-base p-2 text-tint-strong [grid-area:2/1] contrast-more:border-tint contrast-more:bg-tint-base',
|
||||
'circular-corners:rounded-2xl rounded-corners:rounded-xl straight-corners:rounded-xs depth-subtle:shadow-xs',
|
||||
title && 'rounded-ss-none!'
|
||||
)}
|
||||
style={{
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
}}
|
||||
>
|
||||
<code
|
||||
id={codeId}
|
||||
className={tcls(
|
||||
'inline-grid min-w-full grid-cols-[auto_1fr] [count-reset:line] print:whitespace-pre-wrap',
|
||||
withWrap && 'whitespace-pre-wrap'
|
||||
'inline-grid max-h-full min-w-full grid-cols-[auto_1fr] [count-reset:line] print:whitespace-pre-wrap',
|
||||
withWrap && 'whitespace-pre-wrap',
|
||||
'[[aria-expanded=false]_&]:mask-b-from-50%'
|
||||
)}
|
||||
>
|
||||
{lines.map((line, index) => (
|
||||
{theme.lines.map((line, index) => (
|
||||
<CodeHighlightLine
|
||||
bg={bg}
|
||||
fg={fg}
|
||||
key={index}
|
||||
line={line}
|
||||
isLast={index === lines.length - 1}
|
||||
isLast={index === theme.lines.length - 1}
|
||||
withLineNumbers={withLineNumbers}
|
||||
/>
|
||||
))}
|
||||
@@ -77,13 +106,33 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
|
||||
function CodeHighlightLine(props: {
|
||||
line: HighlightLine;
|
||||
bg?: ShikiColorDefinition;
|
||||
fg?: ShikiColorDefinition;
|
||||
isLast: boolean;
|
||||
withLineNumbers: boolean;
|
||||
}) {
|
||||
const { line, isLast, withLineNumbers } = props;
|
||||
const { line, isLast, withLineNumbers, bg, fg } = props;
|
||||
return (
|
||||
<span className={tcls('highlight-line', line.highlighted && 'highlighted')}>
|
||||
{withLineNumbers && <span className="highlight-line-number" />}
|
||||
<span
|
||||
className={tcls('highlight-line', line.highlighted && 'highlighted')}
|
||||
style={{
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
}}
|
||||
>
|
||||
{withLineNumbers && (
|
||||
<span
|
||||
className="highlight-line-number"
|
||||
style={{
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="highlight-line-content">
|
||||
<CodeHighlightTokens tokens={line.tokens} />
|
||||
{!isLast && '\n'}
|
||||
@@ -112,13 +161,53 @@ function CodeHighlightToken(props: { token: HighlightToken }) {
|
||||
return token.content;
|
||||
}
|
||||
case 'shiki': {
|
||||
if (!token.token.color) {
|
||||
if (!token.token.htmlStyle) {
|
||||
return token.token.content;
|
||||
}
|
||||
|
||||
return <span style={{ color: token.token.color }}>{token.token.content}</span>;
|
||||
return <span style={token.token.htmlStyle}>{token.token.content}</span>;
|
||||
}
|
||||
default:
|
||||
assertNever(token);
|
||||
}
|
||||
}
|
||||
|
||||
type ShikiColorDefinition = {
|
||||
color: string | undefined;
|
||||
vars: Record<string, string> | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Shiki color string format into separate color and CSS variables.
|
||||
*
|
||||
* Shiki returns colors in a format that combines a default color with CSS variables:
|
||||
* "defaultColor;--shiki-light:value1;--shiki-dark:value2"
|
||||
*
|
||||
* React does not parse these strings as valid CSS because they contain values & properties in one.
|
||||
* We split these so React can apply them separately via the style prop.
|
||||
*/
|
||||
function parseShikiColorString(
|
||||
originalColor: string | undefined
|
||||
): ShikiColorDefinition | undefined {
|
||||
if (!originalColor) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parts = originalColor.split(';');
|
||||
const color = parts[0];
|
||||
const vars = parts.slice(1).reduce(
|
||||
(acc, item) => {
|
||||
const [key, value] = item.split(':');
|
||||
if (key && value) {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
return {
|
||||
color,
|
||||
vars: Object.keys(vars).length > 0 ? vars : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { JSONDocument } from '@gitbook/api';
|
||||
import type {
|
||||
CustomizationThemedCodeTheme,
|
||||
JSONDocument,
|
||||
SiteCustomizationSettings,
|
||||
} from '@gitbook/api';
|
||||
import { useId } from 'react';
|
||||
|
||||
import { CodeBlock } from './CodeBlock';
|
||||
@@ -8,8 +12,13 @@ import { convertCodeStringToBlock } from './utils';
|
||||
* Plain code block with syntax highlighting.
|
||||
* For simplicity, this is just a wrapper around the CodeBlock component, emulating a document.
|
||||
*/
|
||||
export function PlainCodeBlock(props: { code: string; syntax: string }) {
|
||||
const { code, syntax } = props;
|
||||
export function PlainCodeBlock(props: {
|
||||
code: string;
|
||||
syntax: string;
|
||||
themeKey?: keyof SiteCustomizationSettings['styling']['codeTheme'];
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}) {
|
||||
const { code, syntax, themeKey, themes } = props;
|
||||
const id = useId();
|
||||
|
||||
const block = convertCodeStringToBlock({ key: id, code, syntax });
|
||||
@@ -26,6 +35,8 @@ export function PlainCodeBlock(props: { code: string; syntax: string }) {
|
||||
context={{
|
||||
mode: 'default',
|
||||
}}
|
||||
themeKey={themeKey}
|
||||
themes={themes}
|
||||
block={block}
|
||||
ancestorBlocks={[]}
|
||||
// We optimize perf by default
|
||||
|
||||
@@ -0,0 +1,832 @@
|
||||
import type { ThemeRegistration } from 'shiki/core';
|
||||
|
||||
export const customThemes: Record<string, ThemeRegistration> = {
|
||||
'default-light': createTheme({
|
||||
name: 'default-light',
|
||||
type: 'light',
|
||||
bg: 'inherit',
|
||||
fg: 'inherit',
|
||||
colors: {
|
||||
basic: {
|
||||
comment: 'rgb(var(--neutral-9))',
|
||||
string: 'rgb(var(--success-10))',
|
||||
constant: 'rgb(var(--warning-10))',
|
||||
escape: 'rgb(var(--success-10))',
|
||||
},
|
||||
keyword: {
|
||||
default: 'rgb(var(--danger-10))',
|
||||
operator: 'rgb(var(--tint-11))',
|
||||
regexpOperator: 'rgb(var(--primary-10))',
|
||||
regexpQuantifier: 'rgb(var(--warning-10))',
|
||||
},
|
||||
variable: {
|
||||
default: 'rgb(var(--tint-12))',
|
||||
language: 'rgb(var(--danger-10))',
|
||||
parameter: 'rgb(var(--warning-10))',
|
||||
},
|
||||
function: {
|
||||
default: 'rgb(var(--primary-10))',
|
||||
support: 'rgb(var(--primary-10))',
|
||||
},
|
||||
type: {
|
||||
custom: 'rgb(var(--primary-10))',
|
||||
primitive: 'rgb(var(--warning-10))',
|
||||
other: 'rgb(var(--tint-12))',
|
||||
classMeta: 'rgb(var(--tint-12))',
|
||||
},
|
||||
entity: {
|
||||
tag: 'rgb(var(--warning-10))',
|
||||
attributeId: 'rgb(var(--primary-10))',
|
||||
label: 'rgb(var(--danger-10))',
|
||||
},
|
||||
markup: {
|
||||
default: 'rgb(var(--primary-10))',
|
||||
code: 'rgb(var(--success-10))',
|
||||
link: 'rgb(var(--primary-10))',
|
||||
linkUrl: 'rgb(var(--warning-10))',
|
||||
separator: 'rgb(var(--tint-7))',
|
||||
},
|
||||
diff: {
|
||||
inserted: 'rgb(var(--success-10))',
|
||||
deleted: 'rgb(var(--danger-10))',
|
||||
changed: 'rgb(var(--tint-12))',
|
||||
},
|
||||
other: {
|
||||
punctuation: 'rgb(var(--tint-11))',
|
||||
preprocessor: 'rgb(var(--danger-10))',
|
||||
default: 'rgb(var(--tint-11))',
|
||||
invalid: 'rgb(var(--danger-10))',
|
||||
},
|
||||
},
|
||||
}),
|
||||
'default-dark': createTheme({
|
||||
name: 'default-dark',
|
||||
type: 'dark',
|
||||
bg: 'inherit',
|
||||
fg: 'inherit',
|
||||
colors: {
|
||||
basic: {
|
||||
comment: 'rgb(var(--neutral-9))',
|
||||
string: 'rgb(var(--success-11))',
|
||||
constant: 'rgb(var(--warning-11))',
|
||||
escape: 'rgb(var(--success-11))',
|
||||
},
|
||||
keyword: {
|
||||
default: 'rgb(var(--danger-11))',
|
||||
operator: 'rgb(var(--tint-11))',
|
||||
regexpOperator: 'rgb(var(--primary-11))',
|
||||
regexpQuantifier: 'rgb(var(--warning-11))',
|
||||
},
|
||||
variable: {
|
||||
default: 'rgb(var(--tint-12))',
|
||||
language: 'rgb(var(--danger-11))',
|
||||
parameter: 'rgb(var(--warning-11))',
|
||||
},
|
||||
function: {
|
||||
default: 'rgb(var(--primary-11))',
|
||||
support: 'rgb(var(--primary-11))',
|
||||
},
|
||||
type: {
|
||||
custom: 'rgb(var(--primary-11))',
|
||||
primitive: 'rgb(var(--warning-11))',
|
||||
other: 'rgb(var(--tint-12))',
|
||||
classMeta: 'rgb(var(--tint-12))',
|
||||
},
|
||||
entity: {
|
||||
tag: 'rgb(var(--warning-11))',
|
||||
attributeId: 'rgb(var(--primary-11))',
|
||||
label: 'rgb(var(--danger-11))',
|
||||
},
|
||||
markup: {
|
||||
default: 'rgb(var(--primary-11))',
|
||||
code: 'rgb(var(--success-11))',
|
||||
link: 'rgb(var(--primary-11))',
|
||||
linkUrl: 'rgb(var(--warning-11))',
|
||||
separator: 'rgb(var(--tint-7))',
|
||||
},
|
||||
diff: {
|
||||
inserted: 'rgb(var(--success-11))',
|
||||
deleted: 'rgb(var(--danger-11))',
|
||||
changed: 'rgb(var(--tint-12))',
|
||||
},
|
||||
other: {
|
||||
punctuation: 'rgb(var(--tint-12))',
|
||||
preprocessor: 'rgb(var(--danger-11))',
|
||||
default: 'rgb(var(--tint-12))',
|
||||
invalid: 'rgb(var(--danger-11))',
|
||||
},
|
||||
},
|
||||
}),
|
||||
'monochrome-light': createTheme({
|
||||
name: 'monochrome-light',
|
||||
type: 'light',
|
||||
bg: 'inherit',
|
||||
fg: 'inherit',
|
||||
colors: {
|
||||
basic: {
|
||||
comment: 'rgb(var(--tint-9))',
|
||||
string: 'rgb(var(--primary-12))',
|
||||
constant: 'rgb(var(--primary-11))',
|
||||
escape: 'rgb(var(--tint-12))',
|
||||
},
|
||||
keyword: {
|
||||
default: 'rgb(var(--primary-10))',
|
||||
operator: 'rgb(var(--primary-11))',
|
||||
regexpOperator: 'rgb(var(--primary-10))',
|
||||
regexpQuantifier: 'rgb(var(--primary-11))',
|
||||
},
|
||||
variable: {
|
||||
default: 'rgb(var(--tint-12))',
|
||||
language: 'rgb(var(--primary-10))',
|
||||
parameter: 'rgb(var(--tint-11))',
|
||||
},
|
||||
function: {
|
||||
default: 'rgb(var(--primary-9))',
|
||||
support: 'rgb(var(--primary-9))',
|
||||
},
|
||||
type: {
|
||||
custom: 'rgb(var(--primary-10))',
|
||||
primitive: 'rgb(var(--primary-11))',
|
||||
other: 'rgb(var(--primary-10))',
|
||||
classMeta: 'rgb(var(--tint-12))',
|
||||
},
|
||||
entity: {
|
||||
tag: 'rgb(var(--primary-11))',
|
||||
attributeId: 'rgb(var(--primary-9))',
|
||||
label: 'rgb(var(--primary-10))',
|
||||
},
|
||||
markup: {
|
||||
default: 'rgb(var(--primary-10))',
|
||||
code: 'rgb(var(--tint-12))',
|
||||
link: 'rgb(var(--primary-10))',
|
||||
linkUrl: 'rgb(var(--primary-11))',
|
||||
separator: 'rgb(var(--tint-7))',
|
||||
},
|
||||
diff: {
|
||||
inserted: 'rgb(var(--success-11))',
|
||||
deleted: 'rgb(var(--danger-11))',
|
||||
changed: 'rgb(var(--tint-12))',
|
||||
},
|
||||
other: {
|
||||
punctuation: 'rgb(var(--tint-10))',
|
||||
preprocessor: 'rgb(var(--primary-10))',
|
||||
default: 'rgb(var(--primary-11))',
|
||||
invalid: 'rgb(var(--danger-11))',
|
||||
},
|
||||
},
|
||||
}),
|
||||
'monochrome-dark': createTheme({
|
||||
name: 'monochrome-dark',
|
||||
type: 'dark',
|
||||
bg: 'inherit',
|
||||
fg: 'inherit',
|
||||
colors: {
|
||||
basic: {
|
||||
comment: 'rgb(var(--tint-9))',
|
||||
string: 'rgb(var(--primary-12))',
|
||||
constant: 'rgb(var(--primary-11))',
|
||||
escape: 'rgb(var(--tint-12))',
|
||||
},
|
||||
keyword: {
|
||||
default: 'rgb(var(--primary-11))',
|
||||
operator: 'rgb(var(--primary-11))',
|
||||
regexpOperator: 'rgb(var(--primary-10))',
|
||||
regexpQuantifier: 'rgb(var(--primary-11))',
|
||||
},
|
||||
variable: {
|
||||
default: 'rgb(var(--tint-12))',
|
||||
language: 'rgb(var(--primary-10))',
|
||||
parameter: 'rgb(var(--tint-9))',
|
||||
},
|
||||
function: {
|
||||
default: 'rgb(var(--primary-9))',
|
||||
support: 'rgb(var(--primary-9))',
|
||||
},
|
||||
type: {
|
||||
custom: 'rgb(var(--primary-10))',
|
||||
primitive: 'rgb(var(--primary-11))',
|
||||
other: 'rgb(var(--primary-10))',
|
||||
classMeta: 'rgb(var(--tint-12))',
|
||||
},
|
||||
entity: {
|
||||
tag: 'rgb(var(--primary-11))',
|
||||
attributeId: 'rgb(var(--primary-9))',
|
||||
label: 'rgb(var(--primary-10))',
|
||||
},
|
||||
markup: {
|
||||
default: 'rgb(var(--primary-10))',
|
||||
code: 'rgb(var(--tint-12))',
|
||||
link: 'rgb(var(--primary-10))',
|
||||
linkUrl: 'rgb(var(--primary-11))',
|
||||
separator: 'rgb(var(--tint-7))',
|
||||
},
|
||||
diff: {
|
||||
inserted: 'rgb(var(--success-11))',
|
||||
deleted: 'rgb(var(--danger-11))',
|
||||
changed: 'rgb(var(--tint-12))',
|
||||
},
|
||||
other: {
|
||||
punctuation: 'rgb(var(--tint-10))',
|
||||
preprocessor: 'rgb(var(--primary-10))',
|
||||
default: 'rgb(var(--primary-11))',
|
||||
invalid: 'rgb(var(--danger-11))',
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* Color category mappings for syntax highlighting.
|
||||
* Organized into logical groups for better readability.
|
||||
*/
|
||||
export type ColorCategories = {
|
||||
/** Basic syntax elements */
|
||||
basic: {
|
||||
/** Comments (//, /*, #, etc.) */
|
||||
comment: string;
|
||||
/** String literals ("...", '...', `...`) */
|
||||
string: string;
|
||||
/** Constants: numeric, language constants (true, false, null), character constants */
|
||||
constant: string;
|
||||
/** Escape sequences in strings (\n, \t, etc.) */
|
||||
escape: string;
|
||||
};
|
||||
/** Keywords and operators */
|
||||
keyword: {
|
||||
/** Control flow (if, else, for, while, return), storage (const, let, var, function, class) */
|
||||
default: string;
|
||||
/** Operators (+, -, *, /, =, ==, etc.) */
|
||||
operator: string;
|
||||
/** Regexp operators (|, ^, $) */
|
||||
regexpOperator: string;
|
||||
/** Regexp quantifiers (*, +, ?, {n}) */
|
||||
regexpQuantifier: string;
|
||||
};
|
||||
/** Variables and parameters */
|
||||
variable: {
|
||||
/** Regular variables */
|
||||
default: string;
|
||||
/** Language variables (this, self, super) */
|
||||
language: string;
|
||||
/** Function/method parameters */
|
||||
parameter: string;
|
||||
};
|
||||
/** Functions */
|
||||
function: {
|
||||
/** User-defined function/method invocations */
|
||||
default: string;
|
||||
/** Built-in/library support functions */
|
||||
support: string;
|
||||
};
|
||||
/** Types and classes */
|
||||
type: {
|
||||
/** Custom type names (DocumentBlockCode, HighlightLine, etc.) */
|
||||
custom: string;
|
||||
/** Primitive types (number, boolean, string, etc.) */
|
||||
primitive: string;
|
||||
/** Other types (support.type, support.class, namespaces) */
|
||||
other: string;
|
||||
/** Class meta contexts (meta.class) */
|
||||
classMeta: string;
|
||||
};
|
||||
/** Entities (tags, attributes, labels) */
|
||||
entity: {
|
||||
/** HTML/XML tags and attributes */
|
||||
tag: string;
|
||||
/** Attribute IDs (entity.other.attribute-name.id) */
|
||||
attributeId: string;
|
||||
/** Labels (entity.name.label) */
|
||||
label: string;
|
||||
};
|
||||
/** Markup elements */
|
||||
markup: {
|
||||
/** Markup elements (bold, italic, headings, lists, quotes, etc.) */
|
||||
default: string;
|
||||
/** Inline code in markup */
|
||||
code: string;
|
||||
/** Links in markup */
|
||||
link: string;
|
||||
/** Link URLs in markup */
|
||||
linkUrl: string;
|
||||
/** Separators (meta.separator) */
|
||||
separator: string;
|
||||
};
|
||||
/** Diff highlighting */
|
||||
diff: {
|
||||
/** Inserted lines */
|
||||
inserted: string;
|
||||
/** Deleted lines */
|
||||
deleted: string;
|
||||
/** Changed lines */
|
||||
changed: string;
|
||||
};
|
||||
/** Other syntax elements */
|
||||
other: {
|
||||
/** Punctuation (., ,, ;, :, {}, [], (), etc.) */
|
||||
punctuation: string;
|
||||
/** Preprocessor directives (#include, #define, etc.) */
|
||||
preprocessor: string;
|
||||
/** Other/miscellaneous elements */
|
||||
default: string;
|
||||
/** Invalid syntax/errors */
|
||||
invalid: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Shiki theme registration from semantic color categories.
|
||||
*
|
||||
* Maps our semantic color categories (comment, keyword, function, etc.) to Shiki's TextMate scopes.
|
||||
* TextMate scopes are hierarchical selectors (e.g., "keyword.control", "variable.parameter") that
|
||||
* Shiki uses to identify different syntax elements. This abstraction allows us to define themes
|
||||
* using semantic categories rather than low-level scope patterns.
|
||||
*/
|
||||
function createTheme(options: {
|
||||
name: string;
|
||||
type: 'light' | 'dark';
|
||||
bg: string;
|
||||
fg: string;
|
||||
colors: ColorCategories;
|
||||
}): ThemeRegistration {
|
||||
const { name, type, bg, fg, colors } = options;
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
bg,
|
||||
fg,
|
||||
settings: [
|
||||
// Comments
|
||||
{
|
||||
scope: ['comment'],
|
||||
settings: {
|
||||
foreground: colors.basic.comment,
|
||||
},
|
||||
},
|
||||
// Constants: language constants (true, false, null, etc.)
|
||||
{
|
||||
scope: ['constant.language'],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Constants: numeric
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
'keyword.other.unit',
|
||||
'constant.sha.git-rebase',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Constants: regexp
|
||||
{
|
||||
scope: ['constant.regexp'],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Constants: character
|
||||
{
|
||||
scope: ['constant.character', 'constant.other.option'],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Constants: escape sequences
|
||||
{
|
||||
scope: ['constant.character.escape'],
|
||||
settings: {
|
||||
foreground: colors.basic.escape,
|
||||
},
|
||||
},
|
||||
// Constants: other
|
||||
{
|
||||
scope: [
|
||||
'constant.other',
|
||||
'constant.other.color',
|
||||
'constant.other.symbol',
|
||||
'constant.other.placeholder',
|
||||
'constant.other.character-class.regexp',
|
||||
'constant.character.set.regexp',
|
||||
'constant.character.character-class.regexp',
|
||||
'constant.other.character-class.set.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Constants: variable constants
|
||||
{
|
||||
scope: ['variable.other.constant'],
|
||||
settings: {
|
||||
foreground: colors.basic.constant,
|
||||
},
|
||||
},
|
||||
// Strings
|
||||
{
|
||||
scope: [
|
||||
'string',
|
||||
'meta.embedded.assembly',
|
||||
'string.tag',
|
||||
'string.value',
|
||||
'string.regexp',
|
||||
'meta.preprocessor.string',
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
'punctuation.definition.group.regexp',
|
||||
'punctuation.definition.group.assertion.regexp',
|
||||
'punctuation.definition.character-class.regexp',
|
||||
'punctuation.character.set.begin.regexp',
|
||||
'punctuation.character.set.end.regexp',
|
||||
'keyword.operator.negation.regexp',
|
||||
'support.other.parenthesis.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.basic.string,
|
||||
},
|
||||
},
|
||||
// String expressions (template literals, interpolated)
|
||||
// Only color the punctuation, not the expression content
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.variable.parameter,
|
||||
},
|
||||
},
|
||||
// Keywords: control flow
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'source.cpp keyword.operator.new',
|
||||
'keyword.operator.delete',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
'entity.name.operator',
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.keyword.default,
|
||||
},
|
||||
},
|
||||
// Keywords: storage (const, let, var, function, class, etc.)
|
||||
{
|
||||
scope: [
|
||||
'storage',
|
||||
'storage.type',
|
||||
'storage.modifier',
|
||||
'keyword.operator.noexcept',
|
||||
'storage.type.numeric.go',
|
||||
'storage.type.byte.go',
|
||||
'storage.type.boolean.go',
|
||||
'storage.type.string.go',
|
||||
'storage.type.uintptr.go',
|
||||
'storage.type.error.go',
|
||||
'storage.type.rune.go',
|
||||
'storage.type.cs',
|
||||
'storage.type.generic.cs',
|
||||
'storage.type.modifier.cs',
|
||||
'storage.type.variable.cs',
|
||||
'storage.type.annotation.java',
|
||||
'storage.type.generic.java',
|
||||
'storage.type.java',
|
||||
'storage.type.object.array.java',
|
||||
'storage.type.primitive.array.java',
|
||||
'storage.type.primitive.java',
|
||||
'storage.type.token.java',
|
||||
'storage.type.groovy',
|
||||
'storage.type.annotation.groovy',
|
||||
'storage.type.parameters.groovy',
|
||||
'storage.type.generic.groovy',
|
||||
'storage.type.object.array.groovy',
|
||||
'storage.type.primitive.array.groovy',
|
||||
'storage.type.primitive.groovy',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.keyword.default,
|
||||
},
|
||||
},
|
||||
// Keywords: general
|
||||
{
|
||||
scope: ['keyword'],
|
||||
settings: {
|
||||
foreground: colors.keyword.default,
|
||||
},
|
||||
},
|
||||
// Keywords: operators (default)
|
||||
{
|
||||
scope: ['keyword.operator'],
|
||||
settings: {
|
||||
foreground: colors.keyword.operator,
|
||||
},
|
||||
},
|
||||
// Keywords: regexp operators
|
||||
{
|
||||
scope: ['keyword.operator.or.regexp', 'keyword.control.anchor.regexp'],
|
||||
settings: {
|
||||
foreground: colors.keyword.regexpOperator,
|
||||
},
|
||||
},
|
||||
// Keywords: regexp quantifiers
|
||||
{
|
||||
scope: ['keyword.operator.quantifier.regexp'],
|
||||
settings: {
|
||||
foreground: colors.keyword.regexpQuantifier,
|
||||
},
|
||||
},
|
||||
// Variable language (this, self, super, etc.)
|
||||
{
|
||||
scope: ['variable.language'],
|
||||
settings: {
|
||||
foreground: colors.variable.language,
|
||||
},
|
||||
},
|
||||
// Parameters
|
||||
{
|
||||
scope: ['variable.parameter'],
|
||||
settings: {
|
||||
foreground: colors.variable.parameter,
|
||||
},
|
||||
},
|
||||
// Variables (regular variables, not parameters)
|
||||
{
|
||||
scope: [
|
||||
'variable',
|
||||
'meta.definition.variable.name',
|
||||
'support.variable',
|
||||
'entity.name.variable',
|
||||
'meta.object-literal.key',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.variable.default,
|
||||
},
|
||||
},
|
||||
// Functions: invocations (calls) - user-defined functions
|
||||
{
|
||||
scope: [
|
||||
'meta.function-call',
|
||||
'meta.method-call',
|
||||
'entity.name.function',
|
||||
'entity.name.method',
|
||||
'support.constant.handlebars',
|
||||
'source.powershell variable.other.member',
|
||||
'entity.name.operator.custom-literal',
|
||||
'keyword.other.special-method',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.function.default,
|
||||
},
|
||||
},
|
||||
// Support functions: built-in/library functions
|
||||
{
|
||||
scope: ['support.function', 'support.function.git-rebase'],
|
||||
settings: {
|
||||
foreground: colors.function.support,
|
||||
},
|
||||
},
|
||||
// Types: custom type names (DocumentBlockCode, HighlightLine, etc.) - must come first
|
||||
{
|
||||
scope: ['entity.name.type', 'entity.name.class', 'entity.other.inherited-class'],
|
||||
settings: {
|
||||
foreground: colors.type.custom,
|
||||
},
|
||||
},
|
||||
// Types: primitive (number, boolean, string, etc.) - must come before support.type
|
||||
{
|
||||
scope: [
|
||||
'support.type.primitive',
|
||||
'storage.type.primitive',
|
||||
'support.type.builtin',
|
||||
'entity.name.type.primitive',
|
||||
'support.type',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.type.primitive,
|
||||
},
|
||||
},
|
||||
// Types: other (support.class, etc.)
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'entity.name.namespace',
|
||||
'entity.other.attribute',
|
||||
'entity.name.scope-resolution',
|
||||
'meta.type.cast.expr',
|
||||
'meta.type.new.expr',
|
||||
'support.constant.math',
|
||||
'support.constant.json',
|
||||
'punctuation.separator.namespace.ruby',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.type.other,
|
||||
},
|
||||
},
|
||||
// Class meta contexts
|
||||
{
|
||||
scope: ['meta.class'],
|
||||
settings: {
|
||||
foreground: colors.type.classMeta,
|
||||
},
|
||||
},
|
||||
// DOM constants and namespace references (ReactDOM, window.document, etc.)
|
||||
{
|
||||
scope: [
|
||||
'support.constant.dom',
|
||||
'variable.other.object',
|
||||
'variable.other.object.property',
|
||||
'entity.name.class.jsx',
|
||||
'support.class.builtin',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.variable.parameter,
|
||||
},
|
||||
},
|
||||
// Entity names: tags, attributes
|
||||
{
|
||||
scope: [
|
||||
'entity.name.tag',
|
||||
'entity.other.attribute-name',
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'source.css variable',
|
||||
'source.coffee.embedded',
|
||||
'meta.structure.dictionary.key.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.entity.tag,
|
||||
},
|
||||
},
|
||||
// Attribute IDs (typically styled like functions)
|
||||
{
|
||||
scope: ['entity.other.attribute-name.id', 'punctuation.definition.entity'],
|
||||
settings: {
|
||||
foreground: colors.entity.attributeId,
|
||||
},
|
||||
},
|
||||
// Punctuation
|
||||
{
|
||||
scope: [
|
||||
'punctuation',
|
||||
'punctuation.definition.tag',
|
||||
'punctuation.definition.quote.begin.markdown',
|
||||
'punctuation.definition.list.begin.markdown',
|
||||
'punctuation.section.embedded.begin.php',
|
||||
'punctuation.section.embedded.end.php',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.other.punctuation,
|
||||
},
|
||||
},
|
||||
// Links: link text
|
||||
{
|
||||
scope: [
|
||||
'markup.underline.link',
|
||||
'string.other.link',
|
||||
'punctuation.definition.string.end.markdown',
|
||||
'punctuation.definition.string.begin.markdown',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.markup.link,
|
||||
},
|
||||
},
|
||||
// Links: link URLs
|
||||
{
|
||||
scope: ['meta.link'],
|
||||
settings: {
|
||||
foreground: colors.markup.linkUrl,
|
||||
},
|
||||
},
|
||||
// Preprocessor
|
||||
{
|
||||
scope: [
|
||||
'meta.preprocessor',
|
||||
'entity.name.function.preprocessor',
|
||||
'meta.preprocessor.numeric',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.other.preprocessor,
|
||||
},
|
||||
},
|
||||
// Diff: inserted
|
||||
{
|
||||
scope: ['markup.inserted', 'markup.inserted.diff'],
|
||||
settings: {
|
||||
foreground: colors.diff.inserted,
|
||||
},
|
||||
},
|
||||
// Diff: deleted
|
||||
{
|
||||
scope: ['markup.deleted', 'markup.deleted.diff'],
|
||||
settings: {
|
||||
foreground: colors.diff.deleted,
|
||||
},
|
||||
},
|
||||
// Diff: changed
|
||||
{
|
||||
scope: ['markup.changed', 'markup.changed.diff', 'meta.diff.header'],
|
||||
settings: {
|
||||
foreground: colors.diff.changed,
|
||||
},
|
||||
},
|
||||
// Markup: bold (with bold font style)
|
||||
{
|
||||
scope: ['markup.bold', 'punctuation.definition.bold'],
|
||||
settings: {
|
||||
foreground: colors.markup.default,
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
// Markup: italic (with italic font style)
|
||||
{
|
||||
scope: ['markup.italic', 'punctuation.definition.italic'],
|
||||
settings: {
|
||||
foreground: colors.markup.default,
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
// Markup: inline code
|
||||
{
|
||||
scope: ['markup.raw.inline'],
|
||||
settings: {
|
||||
foreground: colors.markup.code,
|
||||
},
|
||||
},
|
||||
// Markup: headings, lists, quotes, underline, strikethrough
|
||||
{
|
||||
scope: [
|
||||
'markup.heading',
|
||||
'markup.heading punctuation.definition.heading',
|
||||
'entity.name.section',
|
||||
'header',
|
||||
'markup.list',
|
||||
'markup.quote',
|
||||
'markup.underline',
|
||||
'markup.strikethrough',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.markup.default,
|
||||
},
|
||||
},
|
||||
// Separator
|
||||
{
|
||||
scope: ['meta.separator'],
|
||||
settings: {
|
||||
background: colors.markup.separator,
|
||||
foreground: fg,
|
||||
},
|
||||
},
|
||||
// Labels
|
||||
{
|
||||
scope: ['entity.name.label'],
|
||||
settings: {
|
||||
foreground: colors.entity.label,
|
||||
},
|
||||
},
|
||||
// Other
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
'storage.modifier.import.java',
|
||||
'variable.language.wildcard.java',
|
||||
'storage.modifier.package.java',
|
||||
],
|
||||
settings: {
|
||||
foreground: colors.other.default,
|
||||
},
|
||||
},
|
||||
// Invalid
|
||||
{
|
||||
scope: ['invalid'],
|
||||
settings: {
|
||||
foreground: colors.other.invalid,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,8 @@ async function highlightWithInlines(block: DocumentBlockCode) {
|
||||
inline,
|
||||
body: null,
|
||||
}));
|
||||
return highlight(block, inlines);
|
||||
const result = await highlight(block, inlines);
|
||||
return result.lines;
|
||||
}
|
||||
|
||||
it('should parse plain code', async () => {
|
||||
@@ -224,7 +225,19 @@ it('should parse code with an inline on a single line', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"Hello World"',
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -375,7 +388,19 @@ it('should parse code with an inline on a multiple line', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"Hello World"',
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -631,7 +656,13 @@ it('should support multiple code tokens in an annotation', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.world',
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'world',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -645,7 +676,13 @@ it('should support multiple code tokens in an annotation', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: ');',
|
||||
content: ')',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: ';',
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -699,7 +736,13 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.log',
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'log',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -711,7 +754,19 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"Hello"',
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -734,7 +789,13 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.log',
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'log',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -746,7 +807,19 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"World"',
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,20 +1,39 @@
|
||||
import type {
|
||||
DocumentBlockCode,
|
||||
DocumentBlockCodeLine,
|
||||
DocumentInlineAnnotation,
|
||||
import {
|
||||
CustomizationCodeTheme,
|
||||
type CustomizationThemedCodeTheme,
|
||||
type DocumentBlockCode,
|
||||
type DocumentBlockCodeLine,
|
||||
type DocumentInlineAnnotation,
|
||||
} from '@gitbook/api';
|
||||
import {
|
||||
type ThemeRegistrationAny,
|
||||
type ThemedToken,
|
||||
createCssVariablesTheme,
|
||||
createSingletonShorthands,
|
||||
createdBundledHighlighter,
|
||||
} from 'shiki/core';
|
||||
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
|
||||
import { type BundledLanguage, bundledLanguages } from 'shiki/langs';
|
||||
import { bundledThemes } from 'shiki/themes';
|
||||
|
||||
import { nullIfNever } from '@/lib/typescript';
|
||||
import { customThemes } from './customThemes';
|
||||
import { plainHighlight } from './plain-highlight';
|
||||
|
||||
export const DEFAULT_THEMES: CustomizationThemedCodeTheme = {
|
||||
light: CustomizationCodeTheme.DefaultLight,
|
||||
dark: CustomizationCodeTheme.DefaultDark,
|
||||
};
|
||||
|
||||
export type HighlightTheme = {
|
||||
bg?: string;
|
||||
fg?: string;
|
||||
themes: {
|
||||
light: ThemeRegistrationAny;
|
||||
dark: ThemeRegistrationAny;
|
||||
};
|
||||
lines: HighlightLine[];
|
||||
};
|
||||
|
||||
export type HighlightLine = {
|
||||
highlighted: boolean;
|
||||
tokens: HighlightToken[];
|
||||
@@ -36,12 +55,12 @@ export type RenderedInline = {
|
||||
|
||||
const isSafari =
|
||||
typeof navigator !== 'undefined' && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
const theme = createCssVariablesTheme();
|
||||
|
||||
// Merge bundled Shiki themes with our custom themes so both are available to the highlighter
|
||||
const { getSingletonHighlighter } = createSingletonShorthands(
|
||||
createdBundledHighlighter<any, any>({
|
||||
langs: bundledLanguages,
|
||||
themes: {},
|
||||
themes: { ...bundledThemes, ...customThemes },
|
||||
engine: () => createJavaScriptRegexEngine({ forgiving: true, target: 'ES2018' }),
|
||||
})
|
||||
);
|
||||
@@ -49,12 +68,15 @@ const { getSingletonHighlighter } = createSingletonShorthands(
|
||||
/**
|
||||
* Preload the highlighter for a code block.
|
||||
*/
|
||||
export async function preloadHighlight(block: DocumentBlockCode) {
|
||||
export async function preloadHighlight(
|
||||
block: DocumentBlockCode,
|
||||
themes: CustomizationThemedCodeTheme = DEFAULT_THEMES
|
||||
) {
|
||||
const langName = getBlockLang(block);
|
||||
if (langName) {
|
||||
await getSingletonHighlighter({
|
||||
langs: [langName],
|
||||
themes: [theme],
|
||||
themes: [themes.light, themes.dark],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -67,8 +89,9 @@ export async function highlight(
|
||||
inlines: RenderedInline[],
|
||||
options?: {
|
||||
evaluateInlineExpression?: (expr: string) => string;
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
): Promise<HighlightLine[]> {
|
||||
): Promise<HighlightTheme> {
|
||||
const langName = getBlockLang(block);
|
||||
|
||||
if (!langName || (isSafari && ['powershell', 'cpp'].includes(langName))) {
|
||||
@@ -80,13 +103,20 @@ export async function highlight(
|
||||
return plainHighlight(block, inlines, options);
|
||||
}
|
||||
|
||||
const themes = options?.themes ?? DEFAULT_THEMES;
|
||||
|
||||
const code = getPlainCodeBlock(block, undefined, options);
|
||||
|
||||
const highlighter = await getSingletonHighlighter({
|
||||
langs: [langName],
|
||||
themes: [theme],
|
||||
themes: [themes.light, themes.dark],
|
||||
});
|
||||
|
||||
const resolvedThemes = {
|
||||
light: highlighter.getTheme(themes.light),
|
||||
dark: highlighter.getTheme(themes.dark),
|
||||
};
|
||||
|
||||
let tokenizeMaxLineLength = 400;
|
||||
// In some cases, people will use unindented code blocks with a single line.
|
||||
// In this case, we can safely increase the max line length to avoid not highlighting the code.
|
||||
@@ -94,38 +124,47 @@ export async function highlight(
|
||||
tokenizeMaxLineLength = 5000;
|
||||
}
|
||||
|
||||
const lines = highlighter.codeToTokensBase(code, {
|
||||
const result = highlighter.codeToTokens(code, {
|
||||
lang: langName,
|
||||
theme,
|
||||
themes: resolvedThemes,
|
||||
// Shiki's light-dark() CSS function provides different colors for light/dark modes based on the resolved themes
|
||||
defaultColor: 'light-dark()',
|
||||
tokenizeMaxLineLength,
|
||||
});
|
||||
|
||||
const lines = result.tokens;
|
||||
|
||||
let currentIndex = 0;
|
||||
return lines.map((tokens, index) => {
|
||||
const lineBlock = block.nodes[index];
|
||||
const result: HighlightToken[] = [];
|
||||
return {
|
||||
bg: result.bg,
|
||||
fg: result.fg,
|
||||
themes: resolvedThemes,
|
||||
lines: lines.map((tokens, index) => {
|
||||
const lineBlock = block.nodes[index];
|
||||
const result: HighlightToken[] = [];
|
||||
|
||||
const eatToken = (): PositionedToken | null => {
|
||||
const token = tokens.shift();
|
||||
if (token) {
|
||||
currentIndex += token.content.length;
|
||||
const eatToken = (): PositionedToken | null => {
|
||||
const token = tokens.shift();
|
||||
if (token) {
|
||||
currentIndex += token.content.length;
|
||||
}
|
||||
return token
|
||||
? { ...token, start: currentIndex - token.content.length, end: currentIndex }
|
||||
: null;
|
||||
};
|
||||
|
||||
while (tokens.length > 0) {
|
||||
result.push(...matchTokenAndInlines(eatToken, inlines));
|
||||
}
|
||||
return token
|
||||
? { ...token, start: currentIndex - token.content.length, end: currentIndex }
|
||||
: null;
|
||||
};
|
||||
|
||||
while (tokens.length > 0) {
|
||||
result.push(...matchTokenAndInlines(eatToken, inlines));
|
||||
}
|
||||
currentIndex += 1; // for the \n
|
||||
|
||||
currentIndex += 1; // for the \n
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock?.data.highlighted),
|
||||
tokens: result,
|
||||
};
|
||||
});
|
||||
return {
|
||||
highlighted: Boolean(lineBlock?.data.highlighted),
|
||||
tokens: result,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { DocumentBlockCode } from '@gitbook/api';
|
||||
import type { CustomizationThemedCodeTheme, DocumentBlockCode } from '@gitbook/api';
|
||||
|
||||
import { getNodeText } from '@/lib/document';
|
||||
|
||||
import type { HighlightLine, HighlightToken, RenderedInline } from './highlight';
|
||||
import { bundledThemesInfo } from 'shiki/themes';
|
||||
import { customThemes } from './customThemes';
|
||||
import type { HighlightTheme, HighlightToken, RenderedInline } from './highlight';
|
||||
|
||||
/**
|
||||
* Parse a code block without highlighting it.
|
||||
@@ -12,42 +13,59 @@ export function plainHighlight(
|
||||
inlines: RenderedInline[],
|
||||
options?: {
|
||||
evaluateInlineExpression?: (expr: string) => string;
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
): HighlightLine[] {
|
||||
): HighlightTheme {
|
||||
const inlinesCopy = Array.from(inlines);
|
||||
|
||||
return block.nodes.map((lineBlock) => {
|
||||
const tokens: HighlightToken[] = lineBlock.nodes.map((node) => {
|
||||
if (node.object === 'text') {
|
||||
return {
|
||||
type: 'plain',
|
||||
content: getNodeText(node),
|
||||
};
|
||||
}
|
||||
// Resolve theme objects: first check bundled Shiki themes, then custom themes, finally fallback to type-only
|
||||
// The type-only fallback ensures HighlightTheme always has valid theme objects even when theme names don't match
|
||||
const themes = {
|
||||
light: bundledThemesInfo.find((theme) => theme.id === options?.themes?.light) ??
|
||||
Object.values(customThemes).find((theme) => theme.name === options?.themes?.light) ?? {
|
||||
type: 'light',
|
||||
},
|
||||
dark: bundledThemesInfo.find((theme) => theme.id === options?.themes?.dark) ??
|
||||
Object.values(customThemes).find((theme) => theme.name === options?.themes?.dark) ?? {
|
||||
type: 'dark',
|
||||
},
|
||||
};
|
||||
|
||||
if (node.type === 'expression') {
|
||||
return {
|
||||
type: 'plain',
|
||||
content: options?.evaluateInlineExpression?.(node.data.expression) ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
const inline = inlinesCopy.shift();
|
||||
return {
|
||||
type: 'annotation',
|
||||
body: inline?.body ?? null,
|
||||
children: [
|
||||
{
|
||||
return {
|
||||
themes: themes,
|
||||
lines: block.nodes.map((lineBlock) => {
|
||||
const tokens: HighlightToken[] = lineBlock.nodes.map((node) => {
|
||||
if (node.object === 'text') {
|
||||
return {
|
||||
type: 'plain',
|
||||
content: getNodeText(node),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock.data.highlighted),
|
||||
tokens,
|
||||
};
|
||||
});
|
||||
if (node.type === 'expression') {
|
||||
return {
|
||||
type: 'plain',
|
||||
content: options?.evaluateInlineExpression?.(node.data.expression) ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
const inline = inlinesCopy.shift();
|
||||
return {
|
||||
type: 'annotation',
|
||||
body: inline?.body ?? null,
|
||||
children: [
|
||||
{
|
||||
type: 'plain',
|
||||
content: getNodeText(node),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock.data.highlighted),
|
||||
tokens,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DocumentBlockDrawing } from '@gitbook/api';
|
||||
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
|
||||
import { Image } from '../utils';
|
||||
import type { BlockProps } from './Block';
|
||||
@@ -8,11 +8,11 @@ import { Caption } from './Caption';
|
||||
import { imageBlockSizes } from './Images';
|
||||
|
||||
export async function Drawing(props: BlockProps<DocumentBlockDrawing>) {
|
||||
const { block, context } = props;
|
||||
const { document, block, context } = props;
|
||||
|
||||
const resolved =
|
||||
block.data.ref && context.contentContext
|
||||
? await resolveContentRef(block.data.ref, context.contentContext)
|
||||
? await resolveContentRefInDocument(document, block.data.ref, context.contentContext)
|
||||
: null;
|
||||
if (!resolved) {
|
||||
return null;
|
||||
@@ -31,7 +31,7 @@ export async function Drawing(props: BlockProps<DocumentBlockDrawing>) {
|
||||
alt="Drawing"
|
||||
sizes={imageBlockSizes}
|
||||
zoom
|
||||
loading="lazy"
|
||||
loading={context.mode === 'print' ? 'eager' : 'lazy'}
|
||||
/>
|
||||
</Caption>
|
||||
);
|
||||
|
||||
@@ -60,7 +60,7 @@ export async function Embed(props: BlockProps<gitbookAPI.DocumentBlockEmbed>) {
|
||||
sources={{ light: { src: embed.icon } }}
|
||||
sizes={[{ width: 20 }]}
|
||||
resize={context.contentContext.imageResizer}
|
||||
loading="lazy"
|
||||
loading={context.mode === 'print' ? 'eager' : 'lazy'}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Icon } from '@gitbook/icons';
|
||||
import { getNodeFragmentByType } from '@/lib/document';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { ToggleChevron } from '@/components/primitives';
|
||||
import type { BlockProps } from '../Block';
|
||||
import { Blocks } from '../Blocks';
|
||||
import { Inlines } from '../Inlines';
|
||||
@@ -14,6 +15,8 @@ export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
|
||||
|
||||
const title = getNodeFragmentByType(block, 'expandable-title');
|
||||
const body = getNodeFragmentByType(block, 'expandable-body');
|
||||
// @ts-expect-error should be removed once we update the API
|
||||
const isDefaultExpanded = block.data.defaultExpanded ?? false;
|
||||
|
||||
const titleParagraph = title?.nodes[0];
|
||||
|
||||
@@ -27,7 +30,7 @@ export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
|
||||
return (
|
||||
<Details
|
||||
id={id}
|
||||
open={context.mode === 'print'}
|
||||
open={context.mode === 'print' || isDefaultExpanded}
|
||||
className={tcls('scroll-mt-(--content-scroll-margin)', style)}
|
||||
>
|
||||
<summary
|
||||
@@ -46,14 +49,14 @@ export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
|
||||
'[&::-webkit-details-marker]:hidden'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon="chevron-right"
|
||||
<ToggleChevron
|
||||
orientation="right-to-down"
|
||||
open={context.mode === 'print'}
|
||||
className={tcls(
|
||||
'inline-block',
|
||||
'size-3',
|
||||
'mr-3',
|
||||
'mb-1',
|
||||
'transition-transform',
|
||||
'shrink-0',
|
||||
'group-open/expandable:rotate-90'
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type DocumentBlockFile, SiteInsightsLinkPosition } from '@gitbook/api';
|
||||
|
||||
import { t } from '@/intl/translate';
|
||||
import { getSimplifiedContentType } from '@/lib/files';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
|
||||
import { getSpaceLanguage } from '@/intl/server';
|
||||
import { Button, Link } from '../primitives';
|
||||
@@ -12,13 +12,17 @@ import { Caption } from './Caption';
|
||||
import { FileIcon } from './FileIcon';
|
||||
|
||||
export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
const { block, context } = props;
|
||||
const { document, block, context } = props;
|
||||
|
||||
if (!context.contentContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contentRef = await resolveContentRef(block.data.ref, context.contentContext);
|
||||
const contentRef = await resolveContentRefInDocument(
|
||||
document,
|
||||
block.data.ref,
|
||||
context.contentContext
|
||||
);
|
||||
const file = contentRef?.file;
|
||||
|
||||
if (!file) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DocumentBlockImage, DocumentBlockImages, JSONDocument, Length } from '@gitbook/api';
|
||||
|
||||
import { Image, type ImageResponsiveSize } from '@/components/utils';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { BlockProps } from './Block';
|
||||
@@ -81,12 +81,14 @@ async function ImageBlock(props: {
|
||||
isEstimatedOffscreen: boolean;
|
||||
withFrame?: boolean;
|
||||
}) {
|
||||
const { block, context, isEstimatedOffscreen, withFrame } = props;
|
||||
const { document, block, context, isEstimatedOffscreen, withFrame } = props;
|
||||
|
||||
const [src, darkSrc] = await Promise.all([
|
||||
context.contentContext ? resolveContentRef(block.data.ref, context.contentContext) : null,
|
||||
context.contentContext
|
||||
? resolveContentRefInDocument(document, block.data.ref, context.contentContext)
|
||||
: null,
|
||||
block.data.refDark && context.contentContext
|
||||
? resolveContentRef(block.data.refDark, context.contentContext)
|
||||
? resolveContentRefInDocument(document, block.data.refDark, context.contentContext)
|
||||
: null,
|
||||
]);
|
||||
|
||||
@@ -96,36 +98,6 @@ async function ImageBlock(props: {
|
||||
|
||||
return (
|
||||
<div className={tcls('relative', 'overflow-hidden')}>
|
||||
{/* Frame grid */}
|
||||
{withFrame && (
|
||||
<div
|
||||
className={tcls(
|
||||
'absolute',
|
||||
'-top-0.5',
|
||||
'-left-0.5',
|
||||
'right-px',
|
||||
'bottom-px',
|
||||
'opacity-40',
|
||||
'dark:opacity-[0.1]',
|
||||
'bg-[length:24px_24px,24px_24px]',
|
||||
'bg-[linear-gradient(to_right,_rgb(234,235,238)_1px,_transparent_1px),linear-gradient(to_bottom,_rgb(234,235,238)_1px,_transparent_1px)]',
|
||||
'dark:bg-[linear-gradient(to_right,_rgb(122,128,139)_1px,_transparent_1px),linear-gradient(to_bottom,_rgb(122,128,139)_1px,_transparent_1px)]',
|
||||
'bg-repeat'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Shadow overlay */}
|
||||
{withFrame && (
|
||||
<div
|
||||
className={tcls(
|
||||
'pointer-events-none absolute inset-0 rounded-2xl',
|
||||
'shadow-[inset_0_0_10px_10px_rgba(255,255,255,0.9)]',
|
||||
'dark:shadow-[inset_0_0_10px_10px_rgb(29,29,29)]'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Caption {...props} fit>
|
||||
<Image
|
||||
alt={block.data.alt ?? ''}
|
||||
@@ -143,7 +115,7 @@ async function ImageBlock(props: {
|
||||
}
|
||||
: null,
|
||||
}}
|
||||
loading={isEstimatedOffscreen ? 'lazy' : 'eager'}
|
||||
loading={isEstimatedOffscreen && context.mode !== 'print' ? 'lazy' : 'eager'}
|
||||
zoom
|
||||
inlineStyle={{
|
||||
maxWidth: '100%',
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
'use client';
|
||||
import { clearAdaptiveVisitorClaimsCache } from '@/components/Adaptive/AdaptiveVisitorContextProvider';
|
||||
import { tString, useLanguage } from '@/intl/client';
|
||||
import { useAI, useAIChatController, useAIChatState } from '../AI';
|
||||
import { useSetSearchState } from '../Search';
|
||||
import { Button, type ButtonProps, Input } from '../primitives';
|
||||
|
||||
type ClaimInput = {
|
||||
key: string;
|
||||
value: string | number | boolean | null;
|
||||
};
|
||||
|
||||
export function InlineActionButton(
|
||||
props: {
|
||||
action: 'ask' | 'search' | 'set-claim';
|
||||
query?: string;
|
||||
claims?: ClaimInput[];
|
||||
} & { buttonProps: ButtonProps } // TODO: Type this properly: Pick<api.DocumentInlineButton, 'action' | 'query'> & { buttonProps: ButtonProps }
|
||||
) {
|
||||
const { action, query, claims, buttonProps } = props;
|
||||
|
||||
const { assistants } = useAI();
|
||||
const chatController = useAIChatController();
|
||||
const chatState = useAIChatState();
|
||||
const setSearchState = useSetSearchState();
|
||||
const language = useLanguage();
|
||||
|
||||
const handleSubmit = (value: string) => {
|
||||
if (action === 'ask') {
|
||||
chatController.open();
|
||||
if (value ?? query) {
|
||||
chatController.postMessage({ message: value ?? query });
|
||||
}
|
||||
} else if (action === 'search') {
|
||||
setSearchState((prev) => ({
|
||||
...prev,
|
||||
ask: null,
|
||||
scope: 'default',
|
||||
query: value ?? query,
|
||||
open: true,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSetClaimAction = () => {
|
||||
if (!claims?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mergedClaims: Record<string, string | number | boolean | null> = {};
|
||||
for (const claim of claims) {
|
||||
mergedClaims[claim.key] = claim.value;
|
||||
}
|
||||
|
||||
document.cookie = `gitbook-visitor-public=${encodeURIComponent(
|
||||
JSON.stringify(mergedClaims)
|
||||
)}; path=/; SameSite=Lax`;
|
||||
|
||||
clearAdaptiveVisitorClaimsCache();
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const icon =
|
||||
action === 'ask' && buttonProps.icon === 'gitbook-assistant' && assistants.length > 0
|
||||
? assistants[0]?.icon
|
||||
: buttonProps.icon;
|
||||
|
||||
if (action === 'set-claim') {
|
||||
return <Button {...buttonProps} onClick={handleSetClaimAction} />;
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
return (
|
||||
<Input
|
||||
inline
|
||||
label={buttonProps.label as string}
|
||||
sizing="medium"
|
||||
className="inline-flex max-w-full grow"
|
||||
submitButton={{
|
||||
label: tString(language, action === 'ask' ? 'send' : 'search'),
|
||||
}}
|
||||
clearButton={{
|
||||
className: 'text-[1em]',
|
||||
}}
|
||||
maxLength={action === 'ask' ? 2048 : 512}
|
||||
disabled={action === 'ask' && chatState.loading}
|
||||
aria-busy={action === 'ask' && chatState.loading}
|
||||
leading={icon}
|
||||
keyboardShortcut={false}
|
||||
onSubmit={(value) => handleSubmit(value as string)}
|
||||
containerStyle={{
|
||||
width: `${buttonProps.label ? buttonProps.label.toString().length + 10 : 20}ch`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const label = action === 'ask' ? `Ask "${query}"` : `Search for "${query}"`;
|
||||
|
||||
const button = (
|
||||
<Button {...buttonProps} onClick={() => handleSubmit(query)} label={label}>
|
||||
{label !== buttonProps.label ? buttonProps.label : null}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -1,44 +1,85 @@
|
||||
import { resolveContentRef, resolveContentRefFallback } from '@/lib/references';
|
||||
import { resolveContentRefFallback, resolveContentRefInDocument } from '@/lib/references';
|
||||
import * as api from '@gitbook/api';
|
||||
import type { IconName } from '@gitbook/icons';
|
||||
import { Button } from '../primitives';
|
||||
import { Button, type ButtonProps } from '../primitives';
|
||||
import type { InlineProps } from './Inline';
|
||||
import { InlineActionButton } from './InlineActionButton';
|
||||
import { NotFoundRefHoverCard } from './NotFoundRefHoverCard';
|
||||
|
||||
export async function InlineButton(props: InlineProps<api.DocumentInlineButton>) {
|
||||
const { inline, context } = props;
|
||||
export function InlineButton(props: InlineProps<api.DocumentInlineButton>) {
|
||||
const { inline } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRef(inline.data.ref, context.contentContext)
|
||||
: null;
|
||||
const buttonProps: ButtonProps = {
|
||||
label: inline.data.label,
|
||||
variant: inline.data.kind,
|
||||
icon: inline.data.icon as IconName | undefined,
|
||||
size: 'medium',
|
||||
};
|
||||
|
||||
const href = resolved?.href ?? resolveContentRefFallback(inline.data.ref)?.href;
|
||||
const ButtonImplementation = () => {
|
||||
if ('action' in inline.data) {
|
||||
const actionData = inline.data.action;
|
||||
|
||||
return (
|
||||
<InlineActionButton
|
||||
action={actionData.action}
|
||||
query={actionData.query ?? ''}
|
||||
// @ts-expect-error -- Temporary override until `set-claim` is available in @gitbook/api.
|
||||
claims={actionData.claims}
|
||||
buttonProps={buttonProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if ('ref' in inline.data) {
|
||||
return <InlineLinkButton {...props} buttonProps={buttonProps} />;
|
||||
}
|
||||
|
||||
return <Button {...buttonProps} disabled />;
|
||||
};
|
||||
|
||||
const inlineElement = (
|
||||
// Set the leading to have some vertical space between adjacent buttons
|
||||
<span className="inline-button leading-12 [&:has(+.inline-button)]:mr-2">
|
||||
<Button
|
||||
href={href}
|
||||
label={inline.data.label}
|
||||
// TODO: use a variant specifically for user-defined buttons.
|
||||
variant={inline.data.kind}
|
||||
className="leading-normal"
|
||||
disabled={href === undefined}
|
||||
icon={inline.data.icon as IconName | undefined}
|
||||
insights={{
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: inline.data.ref,
|
||||
position: api.SiteInsightsLinkPosition.Content,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<ButtonImplementation />
|
||||
);
|
||||
|
||||
if (!resolved) {
|
||||
return <NotFoundRefHoverCard context={context}>{inlineElement}</NotFoundRefHoverCard>;
|
||||
}
|
||||
|
||||
return inlineElement;
|
||||
}
|
||||
|
||||
export async function InlineLinkButton(
|
||||
props: InlineProps<api.DocumentInlineButton> & { buttonProps: ButtonProps }
|
||||
) {
|
||||
const { document, inline, context, buttonProps } = props;
|
||||
|
||||
if (!('ref' in inline.data)) return;
|
||||
|
||||
const resolved =
|
||||
context.contentContext && inline.data.ref
|
||||
? await resolveContentRefInDocument(document, inline.data.ref, context.contentContext)
|
||||
: null;
|
||||
|
||||
const href =
|
||||
resolved?.href ??
|
||||
(inline.data.ref ? resolveContentRefFallback(inline.data.ref)?.href : undefined);
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
insights={{
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: inline.data.ref,
|
||||
position: api.SiteInsightsLinkPosition.Content,
|
||||
},
|
||||
}}
|
||||
href={href}
|
||||
disabled={href === undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
if (inline.data.ref && !resolved) {
|
||||
return <NotFoundRefHoverCard context={context}>{button}</NotFoundRefHoverCard>;
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
@@ -2,20 +2,22 @@ import type { GitBookBaseContext } from '@/lib/context';
|
||||
import type { DocumentInlineImage } from '@gitbook/api';
|
||||
import assertNever from 'assert-never';
|
||||
|
||||
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
|
||||
import { type ResolvedContentRef, resolveContentRefInDocument } from '@/lib/references';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Image } from '../utils';
|
||||
import type { InlineProps } from './Inline';
|
||||
|
||||
export async function InlineImage(props: InlineProps<DocumentInlineImage>) {
|
||||
const { inline, context, ancestorInlines } = props;
|
||||
const { document, inline, context, ancestorInlines } = props;
|
||||
const { size = 'original' } = inline.data;
|
||||
|
||||
const [src, darkSrc] = await Promise.all([
|
||||
context.contentContext ? resolveContentRef(inline.data.ref, context.contentContext) : null,
|
||||
context.contentContext
|
||||
? resolveContentRefInDocument(document, inline.data.ref, context.contentContext)
|
||||
: null,
|
||||
inline.data.refDark && context.contentContext
|
||||
? resolveContentRef(inline.data.refDark, context.contentContext)
|
||||
? resolveContentRefInDocument(document, inline.data.refDark, context.contentContext)
|
||||
: null,
|
||||
]);
|
||||
|
||||
@@ -49,7 +51,7 @@ export async function InlineImage(props: InlineProps<DocumentInlineImage>) {
|
||||
}
|
||||
: null,
|
||||
}}
|
||||
loading="lazy"
|
||||
loading={context.mode === 'print' ? 'eager' : 'lazy'}
|
||||
style={[size === 'line' ? ['max-h-lh', 'h-lh', 'w-auto'] : null]}
|
||||
inline
|
||||
zoom={!isInLink}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { getSpaceLanguage, tString } from '@/intl/server';
|
||||
import { type TranslationLanguage, languages } from '@/intl/translations';
|
||||
import {
|
||||
type ResolvedContentRef,
|
||||
resolveContentRef,
|
||||
resolveContentRefFallback,
|
||||
resolveContentRefInDocument,
|
||||
} from '@/lib/references';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { StyledLink } from '../../primitives';
|
||||
@@ -15,10 +15,10 @@ import { NotFoundRefHoverCard } from '../NotFoundRefHoverCard';
|
||||
import { InlineLinkTooltip } from './InlineLinkTooltip';
|
||||
|
||||
export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
|
||||
const { inline, document, context, ancestorInlines } = props;
|
||||
const { document, inline, context, ancestorInlines } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRef(inline.data.ref, context.contentContext, {
|
||||
? await resolveContentRefInDocument(document, inline.data.ref, context.contentContext, {
|
||||
// We don't want to resolve the anchor text here, as it can be very expensive and will block rendering if there is a lot of anchors link.
|
||||
resolveAnchorText: false,
|
||||
})
|
||||
|
||||
@@ -182,8 +182,16 @@ function getOrderedListItemPrefixContent(input: {
|
||||
}): string {
|
||||
const { parent, block } = input;
|
||||
const start = parent.data.start ?? 1;
|
||||
const index = parent.nodes.findIndex((node) => node.key === block.key) ?? 0;
|
||||
const value = index + start;
|
||||
|
||||
let index: number;
|
||||
// If the block has a key, use it to find the index. Otherwise, use the object reference.
|
||||
if (block.key) {
|
||||
index = parent.nodes.findIndex((node) => node.key === block.key);
|
||||
} else {
|
||||
index = parent.nodes.findIndex((node) => node === block);
|
||||
}
|
||||
|
||||
const value = index >= 0 ? index + start : Math.max(start, 1);
|
||||
switch (input.depth % 3) {
|
||||
// Use numbers
|
||||
case 0: {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { type DocumentInlineMention, SiteInsightsLinkPosition } from '@gitbook/api';
|
||||
|
||||
import { StyledLink } from '@/components/primitives';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
|
||||
import type { InlineProps } from './Inline';
|
||||
|
||||
export async function Mention(props: InlineProps<DocumentInlineMention>) {
|
||||
const { inline, context } = props;
|
||||
const { document, inline, context } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRef(inline.data.ref, context.contentContext, {
|
||||
? await resolveContentRefInDocument(document, inline.data.ref, context.contentContext, {
|
||||
resolveAnchorText: true,
|
||||
})
|
||||
: null;
|
||||
|
||||
@@ -3,11 +3,8 @@ import { OpenAPIOperation as BaseOpenAPIOperation } from '@gitbook/react-openapi
|
||||
import { resolveOpenAPIOperationBlock } from '@/lib/openapi/resolveOpenAPIOperationBlock';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { AnyOpenAPIOperationsBlock } from '@/lib/openapi/types';
|
||||
import type { BlockProps } from '../Block';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,11 +2,8 @@ import { resolveOpenAPISchemasBlock } from '@/lib/openapi/resolveOpenAPISchemasB
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { OpenAPISchemas as BaseOpenAPISchemas } from '@gitbook/react-openapi';
|
||||
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { OpenAPISchemasBlock } from '@/lib/openapi/types';
|
||||
import type { BlockProps } from '../Block';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user