mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-14 14:45:17 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 365f60d2ca | |||
| c716df39ba | |||
| 8ee84f130f | |||
| aa86711d5a | |||
| 5347917a47 | |||
| 43e8deca0f | |||
| 4cdab37eac | |||
| 8a887fd890 | |||
| 2f6ea5b1f0 | |||
| dcd03554bd | |||
| 9281b5e3b7 | |||
| ec0b6075b0 | |||
| e372dae552 | |||
| e329b41764 | |||
| c8956720d0 | |||
| 843635225d | |||
| f78e9ca34e | |||
| 72a06718e2 | |||
| 0a7979ede2 | |||
| fb49a27cc1 | |||
| 555a1da3ab | |||
| b9f9623970 | |||
| a7b80c3c4f | |||
| e05f262f46 | |||
| f53e0427e4 | |||
| 0d44191666 | |||
| 9e669f162a |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix missing properties in allOf/oneOf
|
||||
@@ -2,4 +2,4 @@
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support social links
|
||||
Add Input component
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Support code themes customization
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Enhance discriminator handling in OpenAPISchema
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Set <html lang> based on translated language
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/openapi-parser': patch
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Add x-gitbook-prefix and x-gitbook-token-placeholder for OpenAPI security scheme
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/embed": patch
|
||||
---
|
||||
|
||||
Fix signed JWT token usage
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Fix OpenAPI example not showing
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix overflowing section groups
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Serve a markdown version of the page when the "Accept" header contains "text/markdown".
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Align styling of PageLinkItem and PageDocumentItem
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix search results cursor
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/openapi-parser": patch
|
||||
---
|
||||
|
||||
Update scalar dependencies.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Better conditional display of page actions
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gitbook/embed": minor
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Improve Docs Embed with separate Assistant and Docs tabs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Improve OpenAPI server URL validation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/icons": minor
|
||||
---
|
||||
|
||||
Update to FontAwesome 7.1.0
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix ordered list item index calculation
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Standardise toggle chevrons across the app
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Add customizable suggested questions
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix OpenAPI basic auth placeholder
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix links to other spaces and root page in embeddable view.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support `greeting` and fix suggested questions if there are no custom ones defined
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
---
|
||||
|
||||
Fix OpenAPI oneOf/allOf merge
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Grow InlineActionButton to take available width on line
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/icons": patch
|
||||
---
|
||||
|
||||
Simplify installation by using public fontawesome mirror.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Add a RSS feed to all pages with update blocks.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Remove hidden site spaces from navigation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Fix hidden section not found
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Add OpenAPI 3.1 nullable array support
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Improve OpenAPI circular references
|
||||
@@ -5,6 +5,8 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- changeset-release/main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -34,10 +36,13 @@ jobs:
|
||||
env:
|
||||
PUPPETEER_SKIP_DOWNLOAD: 1
|
||||
- run: bun unit
|
||||
build:
|
||||
build-oss:
|
||||
# CI to check that the repository builds correctly on a machine without the credentials
|
||||
runs-on: ubuntu-latest
|
||||
name: Build
|
||||
name: Build (Open Source)
|
||||
timeout-minutes: 6
|
||||
env:
|
||||
BUN_NPM_TOKEN: ''
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (preview)
|
||||
|
||||
@@ -3,6 +3,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (production)
|
||||
|
||||
@@ -3,6 +3,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
jobs:
|
||||
deploy-v2-vercel:
|
||||
name: Deploy v2 to Vercel (staging)
|
||||
|
||||
@@ -4,6 +4,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
BUN_NPM_TOKEN: ${{ secrets.NPM_TOKEN_READONLY }}
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
|
||||
@@ -77,7 +77,23 @@ All pull-requests will be tested against both visual and performances testing to
|
||||
|
||||
## Fonts and Icons
|
||||
|
||||
GitBook Open uses fontawesome. For self-hosting and local development, for licensing reasons, only the icons from the free version should be used.
|
||||
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.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 491 KiB After Width: | Height: | Size: 622 KiB |
@@ -6,8 +6,8 @@
|
||||
"name": "gitbook",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@changesets/cli": "^2.29.8",
|
||||
"turbo": "^2.7.6",
|
||||
"@changesets/cli": "^2.29.7",
|
||||
"turbo": "^2.6.1",
|
||||
"vercel": "^39.4.2",
|
||||
},
|
||||
},
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"packages/embed": {
|
||||
"name": "@gitbook/embed",
|
||||
"version": "0.2.0",
|
||||
"version": "0.1.5",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:",
|
||||
@@ -56,7 +56,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "catalog:",
|
||||
"bun-types": "catalog:",
|
||||
"react": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -109,7 +108,7 @@
|
||||
},
|
||||
"packages/gitbook": {
|
||||
"name": "gitbook",
|
||||
"version": "0.21.3",
|
||||
"version": "0.20.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
"@gitbook/api": "catalog:",
|
||||
@@ -136,6 +135,7 @@
|
||||
"@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,8 +157,7 @@
|
||||
"micromark-extension-frontmatter": "^2.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"motion": "^12.23.24",
|
||||
"negotiator": "^1.0.0",
|
||||
"next": "15.4.11",
|
||||
"next": "15.4.10",
|
||||
"next-themes": "^0.4.6",
|
||||
"nuqs": "^2.2.3",
|
||||
"object-hash": "^3.0.0",
|
||||
@@ -176,7 +175,7 @@
|
||||
"remark-rehype": "^11.1.1",
|
||||
"rison": "^0.1.1",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "^3.21.0",
|
||||
"shiki": "^3.2.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwind-shades": "^1.1.2",
|
||||
"unified": "^11.0.5",
|
||||
@@ -197,7 +196,6 @@
|
||||
"@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",
|
||||
@@ -220,13 +218,13 @@
|
||||
},
|
||||
"packages/icons": {
|
||||
"name": "@gitbook/icons",
|
||||
"version": "0.4.2",
|
||||
"version": "0.3.4",
|
||||
"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:",
|
||||
@@ -235,17 +233,20 @@
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@gitbook/fontawesome-pro": "1.0.16",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
},
|
||||
},
|
||||
"packages/openapi-parser": {
|
||||
"name": "@gitbook/openapi-parser",
|
||||
"version": "3.0.7",
|
||||
"version": "3.0.5",
|
||||
"dependencies": {
|
||||
"@scalar/json-magic": "^0.9.4",
|
||||
"@scalar/openapi-parser": "^0.24.5",
|
||||
"@scalar/openapi-types": "^0.5.3",
|
||||
"@scalar/json-magic": "^0.8.1",
|
||||
"@scalar/openapi-parser": "^0.23.2",
|
||||
"@scalar/openapi-types": "^0.5.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node20": "catalog:",
|
||||
@@ -258,7 +259,7 @@
|
||||
},
|
||||
"packages/react-contentkit": {
|
||||
"name": "@gitbook/react-contentkit",
|
||||
"version": "0.7.11",
|
||||
"version": "0.7.8",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:*",
|
||||
@@ -300,7 +301,7 @@
|
||||
},
|
||||
"packages/react-openapi": {
|
||||
"name": "@gitbook/react-openapi",
|
||||
"version": "1.5.6",
|
||||
"version": "1.5.3",
|
||||
"dependencies": {
|
||||
"@gitbook/expr": "workspace:*",
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
@@ -328,7 +329,7 @@
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react": "*",
|
||||
"react-dom": "*",
|
||||
},
|
||||
},
|
||||
@@ -345,7 +346,7 @@
|
||||
"react-dom": "catalog:",
|
||||
},
|
||||
"catalog": {
|
||||
"@gitbook/api": "0.159.0",
|
||||
"@gitbook/api": "0.154.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@tsconfig/node20": "^20.1.6",
|
||||
"@tsconfig/strictest": "^2.0.6",
|
||||
@@ -538,21 +539,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.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/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/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.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/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/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/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/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.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-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-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="],
|
||||
|
||||
@@ -560,11 +561,11 @@
|
||||
|
||||
"@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="],
|
||||
|
||||
"@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/parse": ["@changesets/parse@0.4.1", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^3.13.1" } }, "sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q=="],
|
||||
|
||||
"@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.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/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/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=="],
|
||||
|
||||
@@ -722,9 +723,11 @@
|
||||
|
||||
"@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.159.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-IRZXT2NAcOlKlCcGnzRH4w82AuBXam5I7lw84qfCRbNDDycvFNBwIVMHu3JMOcGUTCkzj7QDElaNKYirsWDwFQ=="],
|
||||
"@gitbook/api": ["@gitbook/api@0.154.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-huEgWWa2H+H4ofYEAQl9r49u0gVjDHHWVNJKhd6SgGCfefjNPHkQj9zaky/5DBQzlu/+SDmutWiOtYdrykQO8w=="],
|
||||
|
||||
"@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"],
|
||||
|
||||
@@ -870,7 +873,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.11", "", {}, "sha512-mIYp/091eYfPFezKX7ZPTWqrmSXq+ih6+LcUyKvLmeLQGhlPtot33kuEOd4U+xAA7sFfj21+OtCpIZx0g5SpvQ=="],
|
||||
"@next/env": ["@next/env@15.4.10", "", {}, "sha512-knhmoJ0Vv7VRf6pZEPSnciUG1S4bIhWx+qTYBW/AjxEtlzsiNORPk8sFDCEvqLfmKuey56UB9FL1UdHEV3uBrg=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Pf6zXp7yyQEn7sqMxur6+kYcywx5up1J849psyET7/8pG2gQTVMjU3NzgIt8SeEP5to3If/SaWmaA6H6ysBr1A=="],
|
||||
|
||||
@@ -1284,23 +1287,23 @@
|
||||
|
||||
"@scalar/draggable": ["@scalar/draggable@0.2.0", "", { "dependencies": { "vue": "^3.5.12" } }, "sha512-UetHRB5Bqo5egVYlS21roWBcICmyk8CKh2htsidO+bFGAjl2e7Te+rY0borhNrMclr0xezHlPuLpEs1dvgLS2g=="],
|
||||
|
||||
"@scalar/helpers": ["@scalar/helpers@0.2.9", "", {}, "sha512-Y4ffJF0yELdwZ0BKgonqn3SumIgRn1WKyYCVHD+TDM7qRFChdGRypyt20+efHs26fmJeyBAIIv2laICj5uimiw=="],
|
||||
"@scalar/helpers": ["@scalar/helpers@0.1.1", "", {}, "sha512-eJjuCI5djqU0adTwrHvpDf0xuwNRpwZinCfJ03QjnmIFBM9Ch0u3tn/0EZOhcVNbdEyJ+3yvSVy1dCmmvgtpvQ=="],
|
||||
|
||||
"@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.9.4", "", { "dependencies": { "@scalar/helpers": "0.2.9", "yaml": "^2.8.0" } }, "sha512-PyfyWrH4ZkW0TM1ColiiHj4NRF8hUM61H0UzAkHLhRNnKFxi6hI+oqNrwqPnyk93hrpkpTRHC7Fl5T0BRwuzVg=="],
|
||||
"@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/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.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-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-types": ["@scalar/openapi-types@0.5.3", "", { "dependencies": { "zod": "^4.1.11" } }, "sha512-m4n/Su3K01d15dmdWO1LlqecdSPKuNjuokrJLdiQ485kW/hRHbXW1QP6tJL75myhw/XhX5YhYAR+jrwnGjXiMw=="],
|
||||
"@scalar/openapi-types": ["@scalar/openapi-types@0.5.1", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-8g7s9lPolyDFtijyh3Ob459tpezPuZbkXoFgJwBTHjPZ7ap+TvOJTvLk56CFwxVBVz2BxCzWJqxYyy3FUdeLoA=="],
|
||||
|
||||
"@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.8", "", { "dependencies": { "@scalar/openapi-types": "0.5.3" } }, "sha512-2xuYLLs0fBadLIk4I1ObjMiCnOyLPEMPf24A1HtHQvhKGDnGlvT63F2rU2Xw8lxCjgHnzveMPnOJEbwIy64RCg=="],
|
||||
"@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.4", "", { "dependencies": { "@scalar/openapi-types": "0.5.1" } }, "sha512-OKSjey1U99BTg1ZTiNL1xxOEOrP9U4aRTH7Pf6JFXpqFH8kGdhrDAIA0uogYYzNq65BaQwK+h31fSrIf/yCLCg=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -1320,17 +1323,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.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/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/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-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-oniguruma": ["@shikijs/engine-oniguruma@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ=="],
|
||||
"@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/langs": ["@shikijs/langs@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA=="],
|
||||
"@shikijs/langs": ["@shikijs/langs@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0" } }, "sha512-Qze5YIsp223AmC69VZDQolcrcYPrVa9wV6cW2kVqsDrSWlwhW2EQZEn1Iw2oQU1tGYVg8Hj/xdp8mOv+9zI0vg=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw=="],
|
||||
"@shikijs/themes": ["@shikijs/themes@3.2.0", "", { "dependencies": { "@shikijs/types": "3.2.0" } }, "sha512-XfzMSTu6iMl2FZIwKykld2OzFKDDlm4KbZrzW6sbKXEeJ1xq61HX4x4bE4+REBFqbbrvAQM8EAH11m/E3cxYDg=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@3.21.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA=="],
|
||||
"@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/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
@@ -1476,6 +1479,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -1530,8 +1535,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -1898,6 +1901,8 @@
|
||||
|
||||
"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=="],
|
||||
@@ -2240,7 +2245,7 @@
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
@@ -2318,6 +2323,8 @@
|
||||
|
||||
"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=="],
|
||||
@@ -2334,6 +2341,8 @@
|
||||
|
||||
"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=="],
|
||||
@@ -2508,7 +2517,7 @@
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"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": ["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-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=="],
|
||||
|
||||
@@ -2550,9 +2559,9 @@
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
||||
"oniguruma-parser": ["oniguruma-parser@0.5.4", "", {}, "sha512-yNxcQ8sKvURiTwP0mV6bLQCYE7NKfKRRWunhbZnXgxSmB1OXa1lHrN3o4DZd+0Si0kU5blidK7BcROO8qv5TZA=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"openapi-fetch": ["openapi-fetch@0.13.5", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q=="],
|
||||
|
||||
@@ -2772,7 +2781,7 @@
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.1", "", {}, "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -2906,19 +2915,19 @@
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"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": ["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-darwin-64": ["turbo-darwin-64@2.7.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.6.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KCxTf3Y1hgNLYIWRLw8bwH8Zie9RyCGoxAlXYsCBI/YNqBSR+ZZK9KYzFxAqDaVaNvTwLFv3rJRGsXOFWg4+Uw=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.7.6", "", { "os": "linux", "cpu": "x64" }, "sha512-vjoU8zIfNgvJR3cMitgw7inEoi6bmuVuFawDl5yKtxjAEhDktFdRBpGS3WojD4l3BklBbIK689ssXcGf21LxRA=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.6.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.7.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-TcMpBvTqZf+1DptrVYLbZls7WY1UVNDTGaf0bo7/GCgWYv5eZHCVo4Td7kCJeDU4glbXg67REX0md0S0V6ghMg=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.6.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.7.6", "", { "os": "win32", "cpu": "x64" }, "sha512-1/MhkYldiihjneY8QnnDMbAkHXn/udTWSVYS94EMlkE9AShozsLTTOT1gDOpX06EfEW5njP09suhMvxbvwuwpQ=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.6.1", "", { "os": "win32", "cpu": "x64" }, "sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.7.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-0wDVnUJLFAWm4ZzOQFDkbyyUqaszorTGf3Rdc22IRIyJTTLd6ajqdb+cWD89UZ1RKr953+PZR1gqgWQY4PDuhA=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.6.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q=="],
|
||||
|
||||
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
|
||||
|
||||
@@ -3054,7 +3063,7 @@
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
"yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -3600,6 +3609,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -3708,6 +3719,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -3884,8 +3897,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -3904,20 +3915,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -3928,24 +3927,14 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -4032,6 +4021,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -4120,8 +4111,6 @@
|
||||
|
||||
"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=="],
|
||||
@@ -4778,6 +4767,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -4918,8 +4909,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -4950,8 +4939,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -4970,8 +4957,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -5346,8 +5331,6 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[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.8",
|
||||
"turbo": "^2.7.6",
|
||||
"@changesets/cli": "^2.29.7",
|
||||
"turbo": "^2.6.1",
|
||||
"vercel": "^39.4.2"
|
||||
},
|
||||
"packageManager": "bun@1.3.7",
|
||||
"packageManager": "bun@1.3.2",
|
||||
"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.159.0",
|
||||
"@gitbook/api": "0.154.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
# @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.2.0",
|
||||
"version": "0.1.5",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/icons": "workspace:",
|
||||
@@ -23,7 +23,6 @@
|
||||
"react": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -34,7 +33,6 @@
|
||||
"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"
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
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('jwt_token', frameOptions.visitor.token);
|
||||
url.searchParams.set('token', frameOptions.visitor.token);
|
||||
}
|
||||
|
||||
if (frameOptions.visitor?.unsignedClaims) {
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './client';
|
||||
export * from './standalone';
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -1,73 +1,5 @@
|
||||
# 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
|
||||
|
||||
@@ -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,6 +37,11 @@ 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,9 +8,7 @@ import {
|
||||
CustomizationIconsStyle,
|
||||
CustomizationSidebarListStyle,
|
||||
CustomizationThemeMode,
|
||||
SiteSocialAccountPlatform,
|
||||
} from '@gitbook/api';
|
||||
import type { GitBookStandalone } from '@gitbook/embed';
|
||||
import { expect } from '@playwright/test';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
@@ -42,7 +40,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-greeting-title"]');
|
||||
const greeting = document.querySelector('[data-testid="ai-chat-time-greeting"]');
|
||||
if (greeting) {
|
||||
greeting.textContent = 'Good morning';
|
||||
}
|
||||
@@ -274,10 +272,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 button inside the link
|
||||
// It is a span inside the link
|
||||
const editorChevron = page
|
||||
.getByRole('link', { name: 'Editor' })
|
||||
.locator('button');
|
||||
.locator('span');
|
||||
await editorChevron.click();
|
||||
|
||||
// Verify "Navigation" link becomes visible after expansion
|
||||
@@ -296,10 +294,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 button inside the link
|
||||
// It is a span inside the link
|
||||
const editorChevron = page
|
||||
.getByRole('link', { name: 'Editor' })
|
||||
.locator('button');
|
||||
.locator('span');
|
||||
await editorChevron.click();
|
||||
|
||||
// At this stage the link should still not be visible
|
||||
@@ -308,7 +306,7 @@ const testCases: TestsCase[] = [
|
||||
// Then we click 'Content Structure' chevron to expand further
|
||||
const contentStructureChevron = page
|
||||
.getByRole('link', { name: 'Content Structure' })
|
||||
.locator('button');
|
||||
.locator('span');
|
||||
await contentStructureChevron.click();
|
||||
|
||||
// Verify "Spaces" link becomes visible after expansion
|
||||
@@ -678,6 +676,70 @@ 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',
|
||||
@@ -1124,16 +1186,6 @@ 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/',
|
||||
@@ -1150,36 +1202,6 @@ 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/',
|
||||
@@ -1269,7 +1291,7 @@ const testCases: TestsCase[] = [
|
||||
name: 'Redirect to Quickstart page',
|
||||
url: 'sections-2/redirect-test',
|
||||
run: async (page) => {
|
||||
await expect(page.locator('h1')).toContainText('Quickstart');
|
||||
await expect(page.locator('h1')).toHaveText('Quickstart');
|
||||
},
|
||||
screenshot: false,
|
||||
},
|
||||
@@ -1295,46 +1317,6 @@ 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/',
|
||||
@@ -1964,324 +1946,6 @@ 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,7 +2,6 @@ import { argosScreenshot } from '@argos-ci/playwright';
|
||||
import {
|
||||
CustomizationAIMode,
|
||||
CustomizationBackground,
|
||||
CustomizationCodeTheme,
|
||||
CustomizationCorners,
|
||||
CustomizationDefaultFont,
|
||||
CustomizationDefaultMonospaceFont,
|
||||
@@ -299,16 +298,6 @@ 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,
|
||||
@@ -366,7 +355,6 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
|
||||
url: 'https://www.gitbook.com/privacy',
|
||||
},
|
||||
socialPreview: {},
|
||||
socialAccounts: [],
|
||||
};
|
||||
|
||||
const encoded = rison.encode_object(deepMerge(DEFAULT_CUSTOMIZATION, partial));
|
||||
@@ -429,15 +417,14 @@ export async function waitForIcons(page: Page) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const maskImage = icon.querySelector('[data-testid="mask-image"]');
|
||||
if (!maskImage) {
|
||||
throw new Error('No mask-image element');
|
||||
}
|
||||
// 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 url = maskImage.getAttribute('href');
|
||||
// If URL is invalid we throw an error.
|
||||
if (!url) {
|
||||
throw new Error('No mask-image url');
|
||||
throw new Error('No mask-image');
|
||||
}
|
||||
|
||||
// If the URL is already queued for loading, we return the state.
|
||||
@@ -467,8 +454,7 @@ export async function waitForIcons(page: Page) {
|
||||
*/
|
||||
async function waitForTOCScrolling(page: Page) {
|
||||
const viewport = await page.viewportSize();
|
||||
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.
|
||||
if (viewport && viewport.width >= 1024) {
|
||||
const toc = page.getByTestId('table-of-contents');
|
||||
await expect(toc).toBeVisible();
|
||||
await page.evaluate(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.21.3",
|
||||
"version": "0.20.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
@@ -28,6 +28,7 @@
|
||||
"@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:",
|
||||
@@ -49,7 +50,7 @@
|
||||
"micromark-extension-frontmatter": "^2.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"motion": "^12.23.24",
|
||||
"next": "15.4.11",
|
||||
"next": "15.4.10",
|
||||
"next-themes": "^0.4.6",
|
||||
"nuqs": "^2.2.3",
|
||||
"object-hash": "^3.0.0",
|
||||
@@ -67,7 +68,7 @@
|
||||
"remark-rehype": "^11.1.1",
|
||||
"rison": "^0.1.1",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "^3.21.0",
|
||||
"shiki": "^3.2.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwind-shades": "^1.1.2",
|
||||
"unified": "^11.0.5",
|
||||
@@ -77,8 +78,7 @@
|
||||
"usehooks-ts": "catalog:",
|
||||
"warn-once": "^0.1.1",
|
||||
"zod": "^3",
|
||||
"zustand": "^5.0.3",
|
||||
"negotiator": "^1.0.0"
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@argos-ci/playwright": "^5.0.9",
|
||||
@@ -95,7 +95,6 @@
|
||||
"@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",
|
||||
|
||||
@@ -83,11 +83,6 @@ 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,10 +39,7 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
|
||||
const { toolCall, context } = props;
|
||||
|
||||
return (
|
||||
<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"
|
||||
>
|
||||
<div 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 { useSetSearchState } from '../Search/useSearch';
|
||||
import { useSearch } 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 = useSetSearchState();
|
||||
const [, setSearchState] = useSearch();
|
||||
|
||||
const assistants: Assistant[] = [];
|
||||
|
||||
@@ -96,7 +96,7 @@ export function useAI(): AIContext {
|
||||
<AIChatIcon
|
||||
state={chat.loading ? 'thinking' : 'default'}
|
||||
trademark={config.trademark}
|
||||
className="size-text-lg"
|
||||
className="size-4"
|
||||
/>
|
||||
),
|
||||
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 { useSetSearchState } from '../Search';
|
||||
import { useSearch } 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 = useSetSearchState();
|
||||
const [, setSearchState] = useSearch();
|
||||
const language = useLanguage();
|
||||
|
||||
// Event listeners storage
|
||||
|
||||
@@ -80,13 +80,14 @@ export function AIChat() {
|
||||
chatController.close();
|
||||
}
|
||||
}}
|
||||
withOverlay={true}
|
||||
data-testid="ai-chat"
|
||||
withScrim={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 data-testid="ai-chat">
|
||||
<EmbeddableFrameMain>
|
||||
<EmbeddableFrameHeader>
|
||||
<AIChatDynamicIcon trademark={config.trademark} />
|
||||
<EmbeddableFrameHeaderMain>
|
||||
@@ -103,6 +104,7 @@ export function AIChat() {
|
||||
icon="close"
|
||||
label={tString(language, 'close')}
|
||||
variant="blank"
|
||||
size="default"
|
||||
/>
|
||||
</EmbeddableFrameButtons>
|
||||
</EmbeddableFrameHeader>
|
||||
@@ -246,14 +248,13 @@ 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-greeting-title"
|
||||
data-testid="ai-chat-time-greeting"
|
||||
>
|
||||
{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,12 +2,9 @@
|
||||
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.
|
||||
*/
|
||||
@@ -18,15 +15,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 || isMobile}
|
||||
iconOnly={!showLabel}
|
||||
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,6 +22,7 @@ export function AIChatControlButton() {
|
||||
icon="trash-can"
|
||||
label={t(language, 'ai_chat_clear_conversation')}
|
||||
variant="blank"
|
||||
size="default"
|
||||
className="animate-blur-in-slow"
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -55,7 +55,6 @@ 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,17 +19,13 @@ export default function AIChatSuggestedQuestions(props: {
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-start gap-2 self-start"
|
||||
data-testid="ai-chat-suggested-questions"
|
||||
>
|
||||
<div className="flex flex-col items-start gap-2 self-start">
|
||||
{suggestions.map((question, index) => (
|
||||
<Button
|
||||
data-testid="ai-chat-suggested-question"
|
||||
key={question}
|
||||
variant="blank"
|
||||
size="small"
|
||||
className="max-w-full animate-blur-in-slow border-none bg-primary-solid/1 hover:bg-primary-hover"
|
||||
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"
|
||||
style={{
|
||||
animationDelay: `${1000 + index * 100}ms`,
|
||||
}}
|
||||
|
||||
@@ -37,14 +37,13 @@ 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 ? 'medium' : 'small'}
|
||||
size={index === 0 ? 'default' : 'medium'}
|
||||
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="small"
|
||||
size="medium"
|
||||
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="small"
|
||||
size="medium"
|
||||
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 starting:py-0 text-left transition-all transition-discrete duration-500 *:whitespace-normal hover:bg-primary-hover"
|
||||
size="small"
|
||||
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"
|
||||
variant="blank"
|
||||
style={{
|
||||
animationDelay: `${250 + Math.min(index * 50, 150)}ms`,
|
||||
|
||||
@@ -35,66 +35,68 @@ 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('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 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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -61,11 +61,10 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
|
||||
'lg:chat-open:mr-80',
|
||||
'xl:chat-open:mr-100',
|
||||
'transition-all',
|
||||
'duration-300',
|
||||
'text-sm'
|
||||
'duration-300'
|
||||
)}
|
||||
>
|
||||
<p id={describedById}>
|
||||
<p id={describedById} className={tcls('text-sm')}>
|
||||
{t(
|
||||
language,
|
||||
'cookies_prompt',
|
||||
@@ -77,13 +76,12 @@ 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="mt-3 flex flex-row gap-2">
|
||||
<div className={tcls('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 { document, block, context, style } = props;
|
||||
const { block, context, style } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRefInDocument(document, block.data.ref, context.contentContext, {
|
||||
? await resolveContentRef(block.data.ref, context.contentContext, {
|
||||
resolveAnchorText: true,
|
||||
iconStyle: ['text-xl', 'text-tint'],
|
||||
})
|
||||
|
||||
@@ -84,11 +84,6 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
|
||||
ancestorBlocks: props.ancestorBlocks,
|
||||
});
|
||||
|
||||
// Determine if this block should expand to full width or use readable width
|
||||
// Text blocks (paragraphs, headings, lists) use a readable max-width
|
||||
// Visual blocks (code, tables, images, etc.) expand to full width
|
||||
const isFullWidthBlock = FULL_WIDTH_BLOCKS.includes(node.type);
|
||||
|
||||
return (
|
||||
<Block
|
||||
key={node.key || `${node.type}-${index}`}
|
||||
@@ -99,12 +94,7 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
|
||||
? 'max-w-screen-xl'
|
||||
: 'max-w-3xl',
|
||||
!LIST_BLOCKS.includes(node.type) && 'print:break-inside-avoid',
|
||||
isFullWidthBlock && 'page-width-wide:max-w-full',
|
||||
// In OpenAPI mode, all blocks expand to full width
|
||||
// In full-width mode, content is capped at 64rem (1024px) and centered
|
||||
'layout-openapi:max-w-full',
|
||||
'layout-full-width:max-w-5xl',
|
||||
'layout-full-width:mx-auto',
|
||||
FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-full',
|
||||
blockStyle,
|
||||
]}
|
||||
isEstimatedOffscreen={isOffscreen}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import type { CustomizationThemedCodeTheme, DocumentBlockCode } from '@gitbook/api';
|
||||
import type { 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, ToggleChevron } from '@/components/primitives';
|
||||
import { Button } from '@/components/primitives';
|
||||
import { t, useLanguage } from '@/intl/client';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
import { 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 { HighlightTheme, RenderedInline } from './highlight';
|
||||
import type { HighlightLine, 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;
|
||||
@@ -30,7 +29,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, themes } = props;
|
||||
const { block, mode, style, inlines, inlineExprVariables } = props;
|
||||
const blockRef = useRef<HTMLDivElement>(null);
|
||||
const isInViewportRef = useRef(false);
|
||||
const [isInViewport, setIsInViewport] = useState(false);
|
||||
@@ -41,17 +40,17 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
visitorClaims,
|
||||
variables: inlineExprVariables,
|
||||
});
|
||||
const plainTheme = useMemo(
|
||||
() => plainHighlight(block, inlines, { evaluateInlineExpression, themes }),
|
||||
[block, inlines, evaluateInlineExpression, themes]
|
||||
const plainLines = useMemo(
|
||||
() => plainHighlight(block, inlines, { evaluateInlineExpression }),
|
||||
[block, inlines, evaluateInlineExpression]
|
||||
);
|
||||
const [theme, setTheme] = useState<null | HighlightTheme>(null);
|
||||
const [lines, setLines] = useState<null | HighlightLine[]>(null);
|
||||
const [highlighting, setHighlighting] = useState(false);
|
||||
|
||||
// Preload the highlighter when the block is mounted.
|
||||
useEffect(() => {
|
||||
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block, themes));
|
||||
}, [block, themes]);
|
||||
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block));
|
||||
}, [block]);
|
||||
|
||||
// When user scrolls, we need to wait for the scroll to finish before running the highlight
|
||||
const isScrollingRef = useRef(false);
|
||||
@@ -100,16 +99,14 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
if (typeof window !== 'undefined') {
|
||||
setHighlighting(true);
|
||||
import('./highlight').then(({ highlight }) => {
|
||||
highlight(block, inlines, { evaluateInlineExpression, themes }).then(
|
||||
(theme) => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTheme(theme);
|
||||
setHighlighting(false);
|
||||
highlight(block, inlines, { evaluateInlineExpression }).then((lines) => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
setLines(lines);
|
||||
setHighlighting(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,12 +116,12 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
}
|
||||
|
||||
// Otherwise if the block is not in viewport, we reset to the plain lines
|
||||
setTheme(null);
|
||||
}, [isInViewport, block, inlines, evaluateInlineExpression, themes]);
|
||||
setLines(null);
|
||||
}, [isInViewport, block, inlines, evaluateInlineExpression]);
|
||||
|
||||
const expandable = block.data.expandable;
|
||||
|
||||
const numberOfLinesOfCode = theme?.lines.length ?? plainTheme.lines.length;
|
||||
const numberOfLinesOfCode = lines?.length ?? plainLines.length;
|
||||
const collapsedLineCount =
|
||||
block.data.collapsedLineCount || CODE_BLOCK_DEFAULT_COLLAPSED_LINE_COUNT;
|
||||
const isExpandable = Boolean(
|
||||
@@ -139,17 +136,16 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
aria-busy={highlighting}
|
||||
block={block}
|
||||
style={style}
|
||||
theme={theme ?? plainTheme}
|
||||
lines={lines ?? plainLines}
|
||||
id={codeBlockBodyId}
|
||||
/>
|
||||
);
|
||||
|
||||
return isExpandable ? (
|
||||
<CodeBlockExpandable
|
||||
theme={theme ?? plainTheme}
|
||||
lines={lines ?? plainLines}
|
||||
controls={codeBlockBodyId}
|
||||
collapsedLineCount={collapsedLineCount}
|
||||
style={style}
|
||||
>
|
||||
{renderer}
|
||||
</CodeBlockExpandable>
|
||||
@@ -160,25 +156,21 @@ export function ClientCodeBlock(props: ClientBlockProps) {
|
||||
|
||||
function CodeBlockExpandable(props: {
|
||||
children: React.ReactNode;
|
||||
theme: HighlightTheme;
|
||||
lines: HighlightLine[];
|
||||
collapsedLineCount: number;
|
||||
controls?: string;
|
||||
style?: ClassValue;
|
||||
}) {
|
||||
const { children, controls, theme, collapsedLineCount, style } = props;
|
||||
const { children, controls, lines = [], collapsedLineCount } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const language = useLanguage();
|
||||
return (
|
||||
<div
|
||||
className={tcls('group/codeblock-expandable relative', style)}
|
||||
data-follow-color-scheme="true"
|
||||
aria-expanded={isExpanded}
|
||||
>
|
||||
<div className="group/codeblock-expandable relative">
|
||||
<div
|
||||
className={tcls(
|
||||
!isExpanded
|
||||
? '[&_pre]:h-[calc(2rem+var(--line-count)*var(--line-height))] [&_pre]:overflow-y-hidden'
|
||||
: ''
|
||||
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-[""]'
|
||||
)}
|
||||
style={
|
||||
{
|
||||
@@ -189,9 +181,9 @@ function CodeBlockExpandable(props: {
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className="pointer-events-none absolute bottom-2 flex w-full justify-center">
|
||||
<div className="pointer-events-none absolute bottom-0 flex w-full justify-center">
|
||||
<Button
|
||||
icon={<ToggleChevron open={isExpanded} />}
|
||||
icon={isExpanded ? 'chevron-up' : 'chevron-down'}
|
||||
size="xsmall"
|
||||
variant="blank"
|
||||
type="button"
|
||||
@@ -202,7 +194,7 @@ function CodeBlockExpandable(props: {
|
||||
>
|
||||
{isExpanded
|
||||
? t(language, 'code_block_expanded')
|
||||
: t(language, 'code_block_collapsed', theme.lines.length)}
|
||||
: t(language, 'code_block_collapsed', lines.length)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type {
|
||||
CustomizationThemedCodeTheme,
|
||||
DocumentBlockCode,
|
||||
SiteCustomizationSettings,
|
||||
} from '@gitbook/api';
|
||||
import type { DocumentBlockCode } from '@gitbook/api';
|
||||
|
||||
import { getNodeFragmentByType } from '@/lib/document';
|
||||
|
||||
@@ -17,21 +13,8 @@ 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> & {
|
||||
themeKey?: keyof SiteCustomizationSettings['styling']['codeTheme'];
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
block,
|
||||
document,
|
||||
style,
|
||||
isEstimatedOffscreen,
|
||||
context,
|
||||
themeKey = 'default',
|
||||
themes: providedThemes,
|
||||
} = props;
|
||||
export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
|
||||
const { block, document, style, isEstimatedOffscreen, context } = props;
|
||||
const inlines = getInlines(block);
|
||||
|
||||
let hasInlineExpression = false;
|
||||
@@ -67,19 +50,10 @@ export async function CodeBlock(
|
||||
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 theme = await highlight(block, richInlines, {
|
||||
themes: themes,
|
||||
});
|
||||
return <CodeBlockRenderer block={block} style={style} theme={theme} />;
|
||||
const lines = await highlight(block, richInlines);
|
||||
return <CodeBlockRenderer block={block} style={style} lines={lines} />;
|
||||
}
|
||||
|
||||
const variables = context.contentContext
|
||||
@@ -100,7 +74,6 @@ export async function CodeBlock(
|
||||
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, HighlightTheme, HighlightToken } from './highlight';
|
||||
import type { HighlightLine, HighlightToken } from './highlight';
|
||||
|
||||
type CodeBlockRendererProps = Pick<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
|
||||
theme: HighlightTheme;
|
||||
lines: HighlightLine[];
|
||||
'aria-busy'?: boolean;
|
||||
id?: string;
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
props: CodeBlockRendererProps,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { block, style, theme, 'aria-busy': ariaBusy } = props;
|
||||
const { block, style, lines, 'aria-busy': ariaBusy } = props;
|
||||
|
||||
const withLineNumbers = Boolean(block.data.lineNumbers) && block.nodes.length > 1;
|
||||
const withWrap = block.data.overflow === 'wrap';
|
||||
@@ -30,35 +30,15 @@ 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 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}`}
|
||||
className={tcls('group/codeblock grid shrink grid-flow-col overflow-hidden', style)}
|
||||
>
|
||||
<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 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-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,
|
||||
}}
|
||||
>
|
||||
<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">
|
||||
{title}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -69,32 +49,23 @@ 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 text-tint-strong [grid-area:2/1] contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base',
|
||||
'circular-corners:rounded-2xl rounded-corners:rounded-xl straight-corners:rounded-xs shadow-xs',
|
||||
title && 'rounded-ss-none!'
|
||||
'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'
|
||||
)}
|
||||
style={{
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
}}
|
||||
>
|
||||
<code
|
||||
id={codeId}
|
||||
className={tcls(
|
||||
'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%'
|
||||
'inline-grid min-w-full grid-cols-[auto_1fr] [count-reset:line] print:whitespace-pre-wrap',
|
||||
withWrap && 'whitespace-pre-wrap'
|
||||
)}
|
||||
>
|
||||
{theme.lines.map((line, index) => (
|
||||
{lines.map((line, index) => (
|
||||
<CodeHighlightLine
|
||||
bg={bg}
|
||||
fg={fg}
|
||||
key={index}
|
||||
line={line}
|
||||
isLast={index === theme.lines.length - 1}
|
||||
isLast={index === lines.length - 1}
|
||||
withLineNumbers={withLineNumbers}
|
||||
/>
|
||||
))}
|
||||
@@ -106,35 +77,13 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
|
||||
function CodeHighlightLine(props: {
|
||||
line: HighlightLine;
|
||||
bg?: ShikiColorDefinition;
|
||||
fg?: ShikiColorDefinition;
|
||||
isLast: boolean;
|
||||
withLineNumbers: boolean;
|
||||
}) {
|
||||
const { line, isLast, withLineNumbers, bg, fg } = props;
|
||||
const { line, isLast, withLineNumbers } = props;
|
||||
return (
|
||||
<span
|
||||
className={tcls('highlight-line', line.highlighted && 'highlighted')}
|
||||
style={
|
||||
line.highlighted
|
||||
? {
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{withLineNumbers && (
|
||||
<span
|
||||
className="highlight-line-number"
|
||||
style={{
|
||||
color: fg?.color,
|
||||
...fg?.vars,
|
||||
backgroundColor: bg?.color,
|
||||
...bg?.vars,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className={tcls('highlight-line', line.highlighted && 'highlighted')}>
|
||||
{withLineNumbers && <span className="highlight-line-number" />}
|
||||
<span className="highlight-line-content">
|
||||
<CodeHighlightTokens tokens={line.tokens} />
|
||||
{!isLast && '\n'}
|
||||
@@ -163,53 +112,13 @@ function CodeHighlightToken(props: { token: HighlightToken }) {
|
||||
return token.content;
|
||||
}
|
||||
case 'shiki': {
|
||||
if (!token.token.htmlStyle) {
|
||||
if (!token.token.color) {
|
||||
return token.token.content;
|
||||
}
|
||||
|
||||
return <span style={token.token.htmlStyle}>{token.token.content}</span>;
|
||||
return <span style={{ color: token.token.color }}>{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,8 +1,4 @@
|
||||
import type {
|
||||
CustomizationThemedCodeTheme,
|
||||
JSONDocument,
|
||||
SiteCustomizationSettings,
|
||||
} from '@gitbook/api';
|
||||
import type { JSONDocument } from '@gitbook/api';
|
||||
import { useId } from 'react';
|
||||
|
||||
import { CodeBlock } from './CodeBlock';
|
||||
@@ -12,13 +8,8 @@ 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;
|
||||
themeKey?: keyof SiteCustomizationSettings['styling']['codeTheme'];
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}) {
|
||||
const { code, syntax, themeKey, themes } = props;
|
||||
export function PlainCodeBlock(props: { code: string; syntax: string }) {
|
||||
const { code, syntax } = props;
|
||||
const id = useId();
|
||||
|
||||
const block = convertCodeStringToBlock({ key: id, code, syntax });
|
||||
@@ -35,8 +26,6 @@ export function PlainCodeBlock(props: {
|
||||
context={{
|
||||
mode: 'default',
|
||||
}}
|
||||
themeKey={themeKey}
|
||||
themes={themes}
|
||||
block={block}
|
||||
ancestorBlocks={[]}
|
||||
// We optimize perf by default
|
||||
|
||||
@@ -1,832 +0,0 @@
|
||||
import type { ThemeRegistration } from 'shiki/core';
|
||||
|
||||
export const customThemes: Record<string, ThemeRegistration> = {
|
||||
'default-light': createTheme({
|
||||
name: 'default-light',
|
||||
type: 'light',
|
||||
bg: 'rgb(var(--tint-2))',
|
||||
fg: 'rgb(var(--tint-12))',
|
||||
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: 'rgb(var(--tint-2))',
|
||||
fg: 'rgb(var(--tint-12))',
|
||||
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: 'rgb(var(--tint-2))',
|
||||
fg: 'rgb(var(--tint-12))',
|
||||
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: 'rgb(var(--tint-2))',
|
||||
fg: 'rgb(var(--tint-12))',
|
||||
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,8 +8,7 @@ async function highlightWithInlines(block: DocumentBlockCode) {
|
||||
inline,
|
||||
body: null,
|
||||
}));
|
||||
const result = await highlight(block, inlines);
|
||||
return result.lines;
|
||||
return highlight(block, inlines);
|
||||
}
|
||||
|
||||
it('should parse plain code', async () => {
|
||||
@@ -225,19 +224,7 @@ it('should parse code with an inline on a single line', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
content: '"Hello World"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -388,19 +375,7 @@ it('should parse code with an inline on a multiple line', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
content: '"Hello World"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -656,13 +631,7 @@ it('should support multiple code tokens in an annotation', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'world',
|
||||
content: '.world',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -676,13 +645,7 @@ it('should support multiple code tokens in an annotation', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: ')',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: ';',
|
||||
content: ');',
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -736,13 +699,7 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'log',
|
||||
content: '.log',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -754,19 +711,7 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
content: '"Hello"',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -789,13 +734,7 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '.',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'log',
|
||||
content: '.log',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -807,19 +746,7 @@ it('should handle \\r', async () => {
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: 'World',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shiki',
|
||||
token: {
|
||||
content: '"',
|
||||
content: '"World"',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,39 +1,20 @@
|
||||
import {
|
||||
CustomizationCodeTheme,
|
||||
type CustomizationThemedCodeTheme,
|
||||
type DocumentBlockCode,
|
||||
type DocumentBlockCodeLine,
|
||||
type DocumentInlineAnnotation,
|
||||
import type {
|
||||
DocumentBlockCode,
|
||||
DocumentBlockCodeLine,
|
||||
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[];
|
||||
@@ -55,12 +36,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: { ...bundledThemes, ...customThemes },
|
||||
themes: {},
|
||||
engine: () => createJavaScriptRegexEngine({ forgiving: true, target: 'ES2018' }),
|
||||
})
|
||||
);
|
||||
@@ -68,15 +49,12 @@ const { getSingletonHighlighter } = createSingletonShorthands(
|
||||
/**
|
||||
* Preload the highlighter for a code block.
|
||||
*/
|
||||
export async function preloadHighlight(
|
||||
block: DocumentBlockCode,
|
||||
themes: CustomizationThemedCodeTheme = DEFAULT_THEMES
|
||||
) {
|
||||
export async function preloadHighlight(block: DocumentBlockCode) {
|
||||
const langName = getBlockLang(block);
|
||||
if (langName) {
|
||||
await getSingletonHighlighter({
|
||||
langs: [langName],
|
||||
themes: [themes.light, themes.dark],
|
||||
themes: [theme],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -89,9 +67,8 @@ export async function highlight(
|
||||
inlines: RenderedInline[],
|
||||
options?: {
|
||||
evaluateInlineExpression?: (expr: string) => string;
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
): Promise<HighlightTheme> {
|
||||
): Promise<HighlightLine[]> {
|
||||
const langName = getBlockLang(block);
|
||||
|
||||
if (!langName || (isSafari && ['powershell', 'cpp'].includes(langName))) {
|
||||
@@ -103,20 +80,13 @@ 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: [themes.light, themes.dark],
|
||||
themes: [theme],
|
||||
});
|
||||
|
||||
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.
|
||||
@@ -124,47 +94,38 @@ export async function highlight(
|
||||
tokenizeMaxLineLength = 5000;
|
||||
}
|
||||
|
||||
const result = highlighter.codeToTokens(code, {
|
||||
const lines = highlighter.codeToTokensBase(code, {
|
||||
lang: langName,
|
||||
themes: resolvedThemes,
|
||||
// Shiki's light-dark() CSS function provides different colors for light/dark modes based on the resolved themes
|
||||
defaultColor: 'light-dark()',
|
||||
theme,
|
||||
tokenizeMaxLineLength,
|
||||
});
|
||||
|
||||
const lines = result.tokens;
|
||||
|
||||
let currentIndex = 0;
|
||||
return {
|
||||
bg: result.bg,
|
||||
fg: result.fg,
|
||||
themes: resolvedThemes,
|
||||
lines: lines.map((tokens, index) => {
|
||||
const lineBlock = block.nodes[index];
|
||||
const result: HighlightToken[] = [];
|
||||
return 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;
|
||||
}
|
||||
return token
|
||||
? { ...token, start: currentIndex - token.content.length, end: currentIndex }
|
||||
: null;
|
||||
};
|
||||
|
||||
while (tokens.length > 0) {
|
||||
result.push(...matchTokenAndInlines(eatToken, inlines));
|
||||
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;
|
||||
};
|
||||
|
||||
currentIndex += 1; // for the \n
|
||||
while (tokens.length > 0) {
|
||||
result.push(...matchTokenAndInlines(eatToken, inlines));
|
||||
}
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock?.data.highlighted),
|
||||
tokens: result,
|
||||
};
|
||||
}),
|
||||
};
|
||||
currentIndex += 1; // for the \n
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock?.data.highlighted),
|
||||
tokens: result,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { CustomizationThemedCodeTheme, DocumentBlockCode } from '@gitbook/api';
|
||||
import type { DocumentBlockCode } from '@gitbook/api';
|
||||
|
||||
import { getNodeText } from '@/lib/document';
|
||||
import { bundledThemesInfo } from 'shiki/themes';
|
||||
import { customThemes } from './customThemes';
|
||||
import type { HighlightTheme, HighlightToken, RenderedInline } from './highlight';
|
||||
|
||||
import type { HighlightLine, HighlightToken, RenderedInline } from './highlight';
|
||||
|
||||
/**
|
||||
* Parse a code block without highlighting it.
|
||||
@@ -13,59 +12,42 @@ export function plainHighlight(
|
||||
inlines: RenderedInline[],
|
||||
options?: {
|
||||
evaluateInlineExpression?: (expr: string) => string;
|
||||
themes?: CustomizationThemedCodeTheme;
|
||||
}
|
||||
): HighlightTheme {
|
||||
): HighlightLine[] {
|
||||
const inlinesCopy = Array.from(inlines);
|
||||
|
||||
// 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',
|
||||
},
|
||||
};
|
||||
return block.nodes.map((lineBlock) => {
|
||||
const tokens: HighlightToken[] = lineBlock.nodes.map((node) => {
|
||||
if (node.object === 'text') {
|
||||
return {
|
||||
type: 'plain',
|
||||
content: getNodeText(node),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
themes: themes,
|
||||
lines: block.nodes.map((lineBlock) => {
|
||||
const tokens: HighlightToken[] = lineBlock.nodes.map((node) => {
|
||||
if (node.object === 'text') {
|
||||
return {
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
highlighted: Boolean(lineBlock.data.highlighted),
|
||||
tokens,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,8 +71,6 @@ export function DocumentView(
|
||||
style,
|
||||
// Preserve adjacent whitespace and new lines.
|
||||
'whitespace-pre-wrap',
|
||||
// In OpenAPI mode, add left padding for sidebar offset
|
||||
'layout-openapi:pl-12',
|
||||
]}
|
||||
context={context}
|
||||
isOffscreen={isOffscreen}
|
||||
@@ -87,7 +85,7 @@ export function DocumentViewSkeleton(props: { document: JSONDocument; blockStyle
|
||||
const { document, blockStyle } = props;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 layout-openapi:pl-12">
|
||||
<div className="flex flex-col gap-4">
|
||||
{document.nodes.map((block) => (
|
||||
<BlockSkeleton
|
||||
key={block.key!}
|
||||
@@ -97,10 +95,6 @@ export function DocumentViewSkeleton(props: { document: JSONDocument; blockStyle
|
||||
block.data && 'fullWidth' in block.data && block.data.fullWidth
|
||||
? 'max-w-screen-xl'
|
||||
: 'max-w-3xl',
|
||||
// Expand in OpenAPI mode, cap at 64rem in full-width mode
|
||||
'layout-openapi:max-w-full',
|
||||
'layout-full-width:max-w-5xl',
|
||||
'layout-full-width:mx-auto',
|
||||
blockStyle,
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DocumentBlockDrawing } from '@gitbook/api';
|
||||
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } 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 { document, block, context } = props;
|
||||
const { block, context } = props;
|
||||
|
||||
const resolved =
|
||||
block.data.ref && context.contentContext
|
||||
? await resolveContentRefInDocument(document, block.data.ref, context.contentContext)
|
||||
? await resolveContentRef(block.data.ref, context.contentContext)
|
||||
: null;
|
||||
if (!resolved) {
|
||||
return null;
|
||||
|
||||
@@ -4,7 +4,6 @@ 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';
|
||||
@@ -47,14 +46,14 @@ export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
|
||||
'[&::-webkit-details-marker]:hidden'
|
||||
)}
|
||||
>
|
||||
<ToggleChevron
|
||||
orientation="right-to-down"
|
||||
open={context.mode === 'print'}
|
||||
<Icon
|
||||
icon="chevron-right"
|
||||
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 { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
|
||||
import { getSpaceLanguage } from '@/intl/server';
|
||||
import { Button, Link } from '../primitives';
|
||||
@@ -12,17 +12,13 @@ import { Caption } from './Caption';
|
||||
import { FileIcon } from './FileIcon';
|
||||
|
||||
export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
const { document, block, context } = props;
|
||||
const { block, context } = props;
|
||||
|
||||
if (!context.contentContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contentRef = await resolveContentRefInDocument(
|
||||
document,
|
||||
block.data.ref,
|
||||
context.contentContext
|
||||
);
|
||||
const contentRef = await resolveContentRef(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 { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { BlockProps } from './Block';
|
||||
@@ -81,14 +81,12 @@ async function ImageBlock(props: {
|
||||
isEstimatedOffscreen: boolean;
|
||||
withFrame?: boolean;
|
||||
}) {
|
||||
const { document, block, context, isEstimatedOffscreen, withFrame } = props;
|
||||
const { block, context, isEstimatedOffscreen, withFrame } = props;
|
||||
|
||||
const [src, darkSrc] = await Promise.all([
|
||||
context.contentContext
|
||||
? resolveContentRefInDocument(document, block.data.ref, context.contentContext)
|
||||
: null,
|
||||
context.contentContext ? resolveContentRef(block.data.ref, context.contentContext) : null,
|
||||
block.data.refDark && context.contentContext
|
||||
? resolveContentRefInDocument(document, block.data.refDark, context.contentContext)
|
||||
? resolveContentRef(block.data.refDark, context.contentContext)
|
||||
: null,
|
||||
]);
|
||||
|
||||
@@ -98,6 +96,36 @@ 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 ?? ''}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
'use client';
|
||||
import { tString, useLanguage } from '@/intl/client';
|
||||
import { useAI, useAIChatController, useAIChatState } from '../AI';
|
||||
import { useSetSearchState } from '../Search';
|
||||
import { Button, type ButtonProps, Input } from '../primitives';
|
||||
|
||||
export function InlineActionButton(
|
||||
props: { action: 'ask' | 'search'; query?: string } & { buttonProps: ButtonProps } // TODO: Type this properly: Pick<api.DocumentInlineButton, 'action' | 'query'> & { buttonProps: ButtonProps }
|
||||
) {
|
||||
const { action, query, 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 icon =
|
||||
action === 'ask' && buttonProps.icon === 'gitbook-assistant' && assistants.length > 0
|
||||
? assistants[0]?.icon
|
||||
: buttonProps.icon;
|
||||
|
||||
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,81 +1,44 @@
|
||||
import { resolveContentRefFallback, resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef, resolveContentRefFallback } from '@/lib/references';
|
||||
import * as api from '@gitbook/api';
|
||||
import type { IconName } from '@gitbook/icons';
|
||||
import { Button, type ButtonProps } from '../primitives';
|
||||
import { Button } from '../primitives';
|
||||
import type { InlineProps } from './Inline';
|
||||
import { InlineActionButton } from './InlineActionButton';
|
||||
import { NotFoundRefHoverCard } from './NotFoundRefHoverCard';
|
||||
|
||||
export function InlineButton(props: InlineProps<api.DocumentInlineButton>) {
|
||||
const { inline } = props;
|
||||
export async function InlineButton(props: InlineProps<api.DocumentInlineButton>) {
|
||||
const { inline, context } = props;
|
||||
|
||||
const buttonProps: ButtonProps = {
|
||||
label: inline.data.label,
|
||||
variant: inline.data.kind,
|
||||
icon: inline.data.icon as IconName | undefined,
|
||||
size: 'medium',
|
||||
};
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRef(inline.data.ref, context.contentContext)
|
||||
: null;
|
||||
|
||||
const ButtonImplementation = () => {
|
||||
if ('action' in inline.data && 'query' in inline.data.action) {
|
||||
return (
|
||||
<InlineActionButton
|
||||
action={inline.data.action.action}
|
||||
query={inline.data.action.query ?? ''}
|
||||
buttonProps={buttonProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if ('ref' in inline.data) {
|
||||
return <InlineLinkButton {...props} buttonProps={buttonProps} />;
|
||||
}
|
||||
|
||||
return <Button {...buttonProps} disabled />;
|
||||
};
|
||||
const href = resolved?.href ?? resolveContentRefFallback(inline.data.ref)?.href;
|
||||
|
||||
const inlineElement = (
|
||||
// Set the leading to have some vertical space between adjacent buttons
|
||||
<ButtonImplementation />
|
||||
<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>
|
||||
);
|
||||
|
||||
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,22 +2,20 @@ import type { GitBookBaseContext } from '@/lib/context';
|
||||
import type { DocumentInlineImage } from '@gitbook/api';
|
||||
import assertNever from 'assert-never';
|
||||
|
||||
import { type ResolvedContentRef, resolveContentRefInDocument } from '@/lib/references';
|
||||
import { type ResolvedContentRef, resolveContentRef } 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 { document, inline, context, ancestorInlines } = props;
|
||||
const { inline, context, ancestorInlines } = props;
|
||||
const { size = 'original' } = inline.data;
|
||||
|
||||
const [src, darkSrc] = await Promise.all([
|
||||
context.contentContext
|
||||
? resolveContentRefInDocument(document, inline.data.ref, context.contentContext)
|
||||
: null,
|
||||
context.contentContext ? resolveContentRef(inline.data.ref, context.contentContext) : null,
|
||||
inline.data.refDark && context.contentContext
|
||||
? resolveContentRefInDocument(document, inline.data.refDark, context.contentContext)
|
||||
? resolveContentRef(inline.data.refDark, context.contentContext)
|
||||
: null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -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 { document, inline, context, ancestorInlines } = props;
|
||||
const { inline, document, context, ancestorInlines } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRefInDocument(document, inline.data.ref, context.contentContext, {
|
||||
? await resolveContentRef(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,16 +182,8 @@ function getOrderedListItemPrefixContent(input: {
|
||||
}): string {
|
||||
const { parent, block } = input;
|
||||
const start = parent.data.start ?? 1;
|
||||
|
||||
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);
|
||||
const index = parent.nodes.findIndex((node) => node.key === block.key) ?? 0;
|
||||
const value = index + start;
|
||||
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 { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
|
||||
import type { InlineProps } from './Inline';
|
||||
|
||||
export async function Mention(props: InlineProps<DocumentInlineMention>) {
|
||||
const { document, inline, context } = props;
|
||||
const { inline, context } = props;
|
||||
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRefInDocument(document, inline.data.ref, context.contentContext, {
|
||||
? await resolveContentRef(inline.data.ref, context.contentContext, {
|
||||
resolveAnchorText: true,
|
||||
})
|
||||
: null;
|
||||
|
||||
@@ -3,8 +3,11 @@ import { OpenAPIOperation as BaseOpenAPIOperation } from '@gitbook/react-openapi
|
||||
import { resolveOpenAPIOperationBlock } from '@/lib/openapi/resolveOpenAPIOperationBlock';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { AnyOpenAPIOperationsBlock } from '@/lib/openapi/types';
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { AnyOpenAPIOperationsBlock } from '@/lib/openapi/types';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,11 @@ import { resolveOpenAPISchemasBlock } from '@/lib/openapi/resolveOpenAPISchemasB
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { OpenAPISchemas as BaseOpenAPISchemas } from '@gitbook/react-openapi';
|
||||
|
||||
import type { OpenAPISchemasBlock } from '@/lib/openapi/types';
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { OpenAPISchemasBlock } from '@/lib/openapi/types';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,11 @@ import { OpenAPIWebhook as BaseOpenAPIWebhook } from '@gitbook/react-openapi';
|
||||
import { resolveOpenAPIWebhookBlock } from '@/lib/openapi/resolveOpenAPIWebhookBlock';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { OpenAPIWebhookBlock } from '@/lib/openapi/types';
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { OpenAPIWebhookBlock } from '@/lib/openapi/types';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import { PlainCodeBlock } from '../CodeBlock';
|
||||
import { DocumentView } from '../DocumentView';
|
||||
import { Heading } from '../Heading';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import { DEFAULT_LOCALE, getSpaceLocale } from '@/intl/server';
|
||||
import type { GitBookAnyContext } from '@/lib/context';
|
||||
@@ -42,17 +43,7 @@ export function getOpenAPIContext(args: {
|
||||
check: <Icon icon="check" />,
|
||||
lock: <Icon icon="lock" />,
|
||||
},
|
||||
renderCodeBlock: (codeProps) => (
|
||||
<PlainCodeBlock
|
||||
{...codeProps}
|
||||
themeKey="openapi"
|
||||
themes={
|
||||
context && 'customization' in context
|
||||
? context.customization.styling.codeTheme.openapi
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
),
|
||||
renderCodeBlock: (codeProps) => <PlainCodeBlock {...codeProps} />,
|
||||
renderDocument: (documentProps) => (
|
||||
<DocumentView
|
||||
document={documentProps.document as JSONDocument}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@reference "../../RootLayout/globals.css";
|
||||
@import './scalar.css';
|
||||
|
||||
/* Layout Components */
|
||||
.openapi-operation,
|
||||
@@ -82,7 +81,7 @@
|
||||
|
||||
/* Markdown Base */
|
||||
.openapi-markdown {
|
||||
@apply prose [&_p]:my-2! text-tint-strong dark:prose-invert max-w-none whitespace-normal;
|
||||
@apply prose text-tint-strong prose-p:my-2! dark:prose-invert max-w-none whitespace-normal;
|
||||
}
|
||||
|
||||
.openapi-markdown > *:first-child {
|
||||
@@ -161,7 +160,7 @@
|
||||
}
|
||||
|
||||
.openapi-schema-root-description.openapi-markdown {
|
||||
@apply prose-sm text-balance mt-1.5 text-[0.813rem]! text-tint overflow-hidden font-normal! select-text [&_strong]:font-semibold [&_strong]:text-inherit;
|
||||
@apply prose-sm text-balance mt-1.5 text-[0.813rem]! text-tint overflow-hidden font-normal! select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
.openapi-section-schemas > .openapi-section-body > .openapi-schema-root-description {
|
||||
@@ -280,7 +279,7 @@
|
||||
|
||||
/* Schema Description */
|
||||
.openapi-schema-description.openapi-markdown {
|
||||
@apply prose-sm text-tint overflow-hidden text-pretty font-normal! select-text [&_strong]:font-semibold [&_strong]:text-inherit;
|
||||
@apply prose-sm text-tint overflow-hidden text-pretty font-normal! select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
.openapi-schema-description.openapi-markdown pre:has(code) {
|
||||
@@ -323,7 +322,7 @@
|
||||
|
||||
.openapi-securities-oauth-content,
|
||||
.openapi-securities-scopes {
|
||||
@apply prose *:prose-sm! *:text-tint;
|
||||
@apply prose *:!prose-sm *:text-tint;
|
||||
}
|
||||
|
||||
.openapi-securities-oauth-content {
|
||||
@@ -347,7 +346,7 @@
|
||||
}
|
||||
|
||||
.openapi-securities-description.openapi-markdown {
|
||||
@apply prose-sm text-tint font-normal! select-text text-pretty;
|
||||
@apply prose-sm text-tint font-normal! select-text text-pretty prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
.openapi-securities-label {
|
||||
@@ -378,7 +377,7 @@
|
||||
}
|
||||
|
||||
.openapi-requestbody-description.openapi-markdown {
|
||||
@apply prose-sm text-tint font-normal! text-pretty select-text [&_strong]:font-semibold [&_strong]:text-inherit;
|
||||
@apply prose-sm text-tint font-normal! text-pretty select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
/* Responses */
|
||||
@@ -396,7 +395,7 @@
|
||||
}
|
||||
|
||||
.openapi-response-description.openapi-markdown {
|
||||
@apply text-left prose-sm leading-tight text-tint select-text [&_strong]:font-semibold [&_strong]:text-inherit;
|
||||
@apply text-left prose-sm text-sm leading-tight text-tint select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger[aria-expanded="false"] .openapi-response-description.openapi-markdown {
|
||||
@@ -527,7 +526,7 @@
|
||||
.openapi-panel,
|
||||
.openapi-codesample,
|
||||
.openapi-response-examples {
|
||||
@apply border shrink min-h-32 overflow-hidden rounded-lg straight-corners:rounded-none circular-corners:rounded-xl bg-tint-subtle border-tint-subtle shadow-sm;
|
||||
@apply border shrink min-h-40 overflow-hidden rounded-lg straight-corners:rounded-none circular-corners:rounded-xl bg-tint-subtle border-tint-subtle shadow-sm;
|
||||
}
|
||||
|
||||
.openapi-response-examples-panel {
|
||||
@@ -541,9 +540,7 @@
|
||||
.openapi-panel pre,
|
||||
.openapi-codesample pre,
|
||||
.openapi-response-examples pre {
|
||||
@apply border-none rounded-none shrink shadow-none;
|
||||
/* Override the code block's background to transparent so it matches with the header and footer */
|
||||
@apply bg-transparent!
|
||||
@apply bg-transparent border-none rounded-none shrink shadow-none;
|
||||
}
|
||||
|
||||
.openapi-panel-heading {
|
||||
|
||||
@@ -9,12 +9,8 @@ import { getTextAlignment } from './utils';
|
||||
export function Paragraph(props: BlockProps<DocumentBlockParagraph>) {
|
||||
const { block, style, ...contextProps } = props;
|
||||
|
||||
// InlineActionButtons use flex-grow to take the available width. This requires the parent to be a flex container.
|
||||
const inlineButtonStyle =
|
||||
'has-[.button,input]:flex has-[.button,input]:flex-wrap has-[.button,input]:gap-2 has-[.button,input]:items-center';
|
||||
|
||||
return (
|
||||
<p className={tcls(inlineButtonStyle, style, getTextAlignment(block.data?.align))}>
|
||||
<p className={tcls(style, getTextAlignment(block.data?.align))}>
|
||||
<Inlines {...contextProps} nodes={block.nodes} ancestorInlines={[]} />
|
||||
</p>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { DocumentBlockReusableContent } from '@gitbook/api';
|
||||
|
||||
import { getDataOrNull } from '@/lib/data';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import type { BlockProps } from './Block';
|
||||
import { UnwrappedBlocks } from './Blocks';
|
||||
|
||||
// TODO-DEREF: Remove this once we have rolled out the new reusable content deref in the API.
|
||||
export async function ReusableContent(props: BlockProps<DocumentBlockReusableContent>) {
|
||||
const { document, block, context, ancestorBlocks } = props;
|
||||
const { block, context, ancestorBlocks } = props;
|
||||
|
||||
if (!context.contentContext) {
|
||||
throw new Error('Expected a content context to render a reusable content block');
|
||||
@@ -17,7 +16,7 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
||||
? context.contentContext.dataFetcher.withToken({ apiToken: block.meta.token })
|
||||
: context.contentContext.dataFetcher;
|
||||
|
||||
const resolved = await resolveContentRefInDocument(document, block.data.ref, {
|
||||
const resolved = await resolveContentRef(block.data.ref, {
|
||||
...context.contentContext,
|
||||
dataFetcher,
|
||||
});
|
||||
@@ -31,7 +30,7 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
||||
return null;
|
||||
}
|
||||
|
||||
const reusableContentDocument = await getDataOrNull(
|
||||
const document = await getDataOrNull(
|
||||
dataFetcher.getRevisionReusableContentDocument({
|
||||
spaceId: reusableContent.context.space.id,
|
||||
revisionId: reusableContent.context.revisionId,
|
||||
@@ -39,14 +38,14 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
||||
})
|
||||
);
|
||||
|
||||
if (!reusableContentDocument) {
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<UnwrappedBlocks
|
||||
nodes={reusableContentDocument.nodes}
|
||||
document={reusableContentDocument}
|
||||
nodes={document.nodes}
|
||||
document={document}
|
||||
ancestorBlocks={[...ancestorBlocks, block]}
|
||||
context={{
|
||||
...context,
|
||||
|
||||
@@ -32,16 +32,7 @@ export function StepperStep(props: BlockProps<DocumentBlockStepperStep>) {
|
||||
})();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={tcls(
|
||||
'mx-auto flex w-full max-w-3xl flex-row gap-4 md:gap-8',
|
||||
// Expand in OpenAPI mode, cap at 64rem in full-width mode
|
||||
'layout-openapi:max-w-full',
|
||||
'layout-full-width:max-w-5xl',
|
||||
'layout-full-width:mx-auto',
|
||||
style
|
||||
)}
|
||||
>
|
||||
<div className={tcls('mx-auto flex w-full max-w-3xl flex-row gap-4 md:gap-8', style)}>
|
||||
<div className="relative select-none">
|
||||
<div
|
||||
className={tcls(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LinkBox, LinkOverlay } from '@/components/primitives';
|
||||
import { Image } from '@/components/utils';
|
||||
import { type ResolvedContentRef, resolveContentRefInDocument } from '@/lib/references';
|
||||
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import {
|
||||
CardsImageObjectFit,
|
||||
@@ -18,7 +18,7 @@ export async function RecordCard(
|
||||
record: TableRecordKV;
|
||||
}
|
||||
) {
|
||||
const { view, record, context, block, isOffscreen, document } = props;
|
||||
const { view, record, context, block, isOffscreen } = props;
|
||||
|
||||
const { dark, light } = getRecordCardCovers(record[1], view);
|
||||
const targetRef = view.targetDefinition
|
||||
@@ -27,13 +27,13 @@ export async function RecordCard(
|
||||
|
||||
const [lightCover, darkCover, target] = await Promise.all([
|
||||
light.contentRef && context.contentContext
|
||||
? resolveContentRefInDocument(document, light.contentRef, context.contentContext)
|
||||
? resolveContentRef(light.contentRef, context.contentContext)
|
||||
: null,
|
||||
dark.contentRef && context.contentContext
|
||||
? resolveContentRefInDocument(document, dark.contentRef, context.contentContext)
|
||||
? resolveContentRef(dark.contentRef, context.contentContext)
|
||||
: null,
|
||||
targetRef && context.contentContext
|
||||
? resolveContentRefInDocument(document, targetRef, context.contentContext)
|
||||
? resolveContentRef(targetRef, context.contentContext)
|
||||
: null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { StyledLink } from '@/components/primitives';
|
||||
import { Image } from '@/components/utils';
|
||||
import { getNodeFragmentByName } from '@/lib/document';
|
||||
import { getSimplifiedContentType } from '@/lib/files';
|
||||
import { resolveContentRefInDocument } from '@/lib/references';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { filterOutNullable } from '@/lib/typescript';
|
||||
import type { BlockProps } from '../Block';
|
||||
@@ -176,8 +176,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
const files = await Promise.all(
|
||||
value.map((fileId) =>
|
||||
context.contentContext
|
||||
? resolveContentRefInDocument(
|
||||
document,
|
||||
? resolveContentRef(
|
||||
{
|
||||
kind: 'file',
|
||||
file: fileId,
|
||||
@@ -251,7 +250,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
}
|
||||
const resolved =
|
||||
value && context.contentContext
|
||||
? await resolveContentRefInDocument(document, value, context.contentContext, {
|
||||
? await resolveContentRef(value, context.contentContext, {
|
||||
resolveAnchorText: true,
|
||||
iconStyle: ['mr-2', 'text-tint-subtle'],
|
||||
})
|
||||
@@ -294,11 +293,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
user: userId,
|
||||
};
|
||||
const resolved = context.contentContext
|
||||
? await resolveContentRefInDocument(
|
||||
document,
|
||||
contentRef,
|
||||
context.contentContext
|
||||
)
|
||||
? await resolveContentRef(contentRef, context.contentContext)
|
||||
: null;
|
||||
if (!resolved) {
|
||||
return null;
|
||||
@@ -371,8 +366,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
}
|
||||
|
||||
const image = context.contentContext
|
||||
? await resolveContentRefInDocument(
|
||||
document,
|
||||
? await resolveContentRef(
|
||||
'ref' in value ? value.ref : value,
|
||||
context.contentContext
|
||||
)
|
||||
|
||||
@@ -224,7 +224,7 @@ const TabPanel = memo(function TabPanel(props: {
|
||||
aria-labelledby={getTabButtonId(tab.id)}
|
||||
className="scroll-mt-[calc(var(--content-scroll-margin)+var(--spacing)*20)]"
|
||||
>
|
||||
<div className={tcls('p-4')} hidden={!isActive}>
|
||||
<div className="p-4" hidden={!isActive}>
|
||||
{tab.body}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,8 @@ export function Update(props: BlockProps<DocumentBlockUpdate>) {
|
||||
{...contextProps}
|
||||
nodes={block.nodes}
|
||||
ancestorBlocks={[...ancestorBlocks, block]}
|
||||
style="[&>*:first-child]:!pt-0 flex flex-1 flex-col [&>*+*]:mt-5"
|
||||
// Remove padding-top from headings when they're the first child (similar to column-first-of-type pattern)
|
||||
style="[&>*:first-child]:!pt-0 flex-1 space-y-4"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,11 +9,11 @@ export function getTextAlignment(textAlignment: TextAlignment | undefined): Clas
|
||||
switch (textAlignment) {
|
||||
case undefined:
|
||||
case TextAlignment.Start:
|
||||
return ['text-start', 'self-start', 'justify-start'];
|
||||
return ['text-start', 'justify-self-start'];
|
||||
case TextAlignment.Center:
|
||||
return ['text-center', 'self-center', 'justify-center'];
|
||||
return ['text-center', 'justify-self-center'];
|
||||
case TextAlignment.End:
|
||||
return ['text-end', 'self-end', 'justify-end'];
|
||||
return ['text-end', 'justify-self-end'];
|
||||
default:
|
||||
return nullIfNever(textAlignment);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user