mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8cfb10974 | |||
| 3cdba0ae8e | |||
| 83e02b621a | |||
| cb2cc52c26 | |||
| 0e15229c52 | |||
| 8547867c6d | |||
| 5a69692f54 | |||
| 42ca7e1580 | |||
| c4ebb3fecd | |||
| 2a805cc082 | |||
| 89a5816ee4 | |||
| c3f6b8c003 | |||
| 0e201d5899 | |||
| ae5f1abc58 | |||
| 580101d04e | |||
| 81b4a4db53 | |||
| 8339e91e2b | |||
| 3119066728 | |||
| 326e28e9b0 | |||
| 90f0127ada | |||
| 0353a8186c | |||
| dd043df00f | |||
| 97b7c79bc6 | |||
| 634e0b4302 | |||
| 2a23f1f003 | |||
| 88ffdcc303 | |||
| b6b09d42af | |||
| ebc39e9db9 | |||
| 0df3849ea2 | |||
| 8ed1bda2f2 | |||
| d67699a1f1 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Fix images in reusable content across spaces.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Fix openapi-select hover in responses
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Show sections tabs only if there is at least two sections
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/colors": patch
|
||||
---
|
||||
|
||||
Update chroma ratio per step
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Fix Python code example for JSON payload
|
||||
@@ -3,4 +3,4 @@
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Improve OpenAPIResponses examples and schemas
|
||||
Fix openapi CR preview
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Merge simple alternatives
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Adds AI sidebar with recommendations based on browsing behaviour
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gitbook/react-openapi": minor
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Design tweaks to code blocks and OpenAPI pages
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"gitbook-v2": minor
|
||||
"gitbook": minor
|
||||
---
|
||||
|
||||
Add support for reusable content across spaces.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Bump scalar packages
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Change OpenAPI schema-optional from `info` to `tint` color
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Fix schemas disclosure label causing client error
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook-v2": patch
|
||||
---
|
||||
|
||||
Improve error messages around undefined site sections.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"gitbook-v2": patch
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Increase logging around caching behaviour causing page crashes.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Change `Dropdown`s to use Radix's `DropdownMenu`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Add border to filled sidebar on gradient theme
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
"gitbook-v2": patch
|
||||
---
|
||||
|
||||
Revert investigation work around URL caches.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix OpenAPI disclosure label ("Show properties") misalignment on mobile
|
||||
Vendored
+1
@@ -9,6 +9,7 @@
|
||||
],
|
||||
"tailwindCSS.classAttributes": ["class", "className", "style", ".*Style"],
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports.biome": "explicit",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"name": "@gitbook/cache-tags",
|
||||
"version": "0.3.1",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "^0.109.0",
|
||||
"@gitbook/api": "^0.111.0",
|
||||
"assert-never": "^1.2.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -49,7 +49,7 @@
|
||||
},
|
||||
"packages/gitbook": {
|
||||
"name": "gitbook",
|
||||
"version": "0.10.1",
|
||||
"version": "0.11.1",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "*",
|
||||
"@gitbook/cache-do": "workspace:*",
|
||||
@@ -62,6 +62,7 @@
|
||||
"@gitbook/react-math": "workspace:*",
|
||||
"@gitbook/react-openapi": "workspace:*",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.3",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
@@ -89,8 +90,8 @@
|
||||
"p-map": "^7.0.0",
|
||||
"parse-cache-control": "^1.0.1",
|
||||
"partial-json": "^0.1.7",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
@@ -111,7 +112,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@argos-ci/playwright": "^5.0.3",
|
||||
"@cloudflare/next-on-pages": "1.13.7",
|
||||
"@cloudflare/next-on-pages": "1.13.12",
|
||||
"@cloudflare/workers-types": "^4.20241230.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
@@ -231,7 +232,7 @@
|
||||
},
|
||||
"packages/react-openapi": {
|
||||
"name": "@gitbook/react-openapi",
|
||||
"version": "1.1.10",
|
||||
"version": "1.2.1",
|
||||
"dependencies": {
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
"@scalar/api-client-react": "^1.2.19",
|
||||
@@ -259,9 +260,9 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@gitbook/api": "0.109.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"@gitbook/api": "0.113.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
},
|
||||
"packages": {
|
||||
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="],
|
||||
@@ -472,7 +473,7 @@
|
||||
|
||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
||||
|
||||
"@cloudflare/next-on-pages": ["@cloudflare/next-on-pages@1.13.7", "", { "dependencies": { "acorn": "^8.8.0", "ast-types": "^0.14.2", "chalk": "^5.2.0", "chokidar": "^3.5.3", "commander": "^11.1.0", "cookie": "^0.5.0", "esbuild": "^0.15.3", "js-yaml": "^4.1.0", "miniflare": "^3.20231218.1", "package-manager-manager": "^0.2.0", "pcre-to-regexp": "^1.1.0", "semver": "^7.5.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240208.0", "vercel": ">=30.0.0", "wrangler": "^3.28.2" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "next-on-pages": "bin/index.js" } }, "sha512-TSMVy+1fmxzeyykOC9guMEj7G2FgENw1T8V1sIFnah6piaJNBmybdyikPdQSdikP5w6v9eQhBt/TrXDPMDw0dw=="],
|
||||
"@cloudflare/next-on-pages": ["@cloudflare/next-on-pages@1.13.12", "", { "dependencies": { "acorn": "^8.8.0", "ast-types": "^0.14.2", "chalk": "^5.2.0", "chokidar": "^3.5.3", "commander": "^11.1.0", "cookie": "^0.5.0", "esbuild": "^0.15.3", "js-yaml": "^4.1.0", "miniflare": "^3.20231218.1", "package-manager-manager": "^0.2.0", "pcre-to-regexp": "^1.1.0", "semver": "^7.5.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240208.0", "vercel": ">=30.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "next-on-pages": "bin/index.js" } }, "sha512-rPy7x9c2+0RDDdJ5o0TeRUwXJ1b7N1epnqF6qKSp5Wz1r9KHOyvaZh1ACoOC6Vu5k9su5WZOgy+8fPLIyrldMQ=="],
|
||||
|
||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.15", "workerd": "^1.20250320.0" }, "optionalPeers": ["workerd"] }, "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg=="],
|
||||
|
||||
@@ -546,10 +547,6 @@
|
||||
|
||||
"@emotion/memoize": ["@emotion/memoize@0.7.4", "", {}, "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="],
|
||||
|
||||
"@esbuild-plugins/node-globals-polyfill": ["@esbuild-plugins/node-globals-polyfill@0.2.3", "", { "peerDependencies": { "esbuild": "*" } }, "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw=="],
|
||||
|
||||
"@esbuild-plugins/node-modules-polyfill": ["@esbuild-plugins/node-modules-polyfill@0.2.2", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "rollup-plugin-node-polyfills": "^0.2.1" }, "peerDependencies": { "esbuild": "*" } }, "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="],
|
||||
@@ -628,7 +625,7 @@
|
||||
|
||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
|
||||
|
||||
"@gitbook/api": ["@gitbook/api@0.109.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-nzhxmiNR2MMHh+HoU7fFdOJ+u4BFyXjtsVquQf3VhZMjeI71yeLx5JEbjkOcijVd/GMAtvpYlYR2qcPqzq27gw=="],
|
||||
"@gitbook/api": ["@gitbook/api@0.113.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-PWMeAkdm4bHSl3b5OmtcmskZ6qRkkDhauCPybo8sGnjS03O14YAUtubAQiNCKX/uwbs+yiQ8KRPyeIwn+g42yw=="],
|
||||
|
||||
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
|
||||
|
||||
@@ -822,11 +819,15 @@
|
||||
|
||||
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.1", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "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-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.12", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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-VJoMs+BWWE7YhzEQyVwvF9n22Eiyr83HotCVrMQzla/OwRovXCgah7AcaEr4hMNj4gJxSdtIbcHGvmJXOoJVHA=="],
|
||||
|
||||
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
|
||||
|
||||
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-callback-ref": "1.1.0" }, "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-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA=="],
|
||||
|
||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||
|
||||
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.7", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.4", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.4", "@radix-ui/react-portal": "1.1.6", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-roving-focus": "1.1.7", "@radix-ui/react-slot": "1.2.0", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.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-+qYq6LfbiGo97Zz9fioX83HCiIYYFNs8zAsVCMQrIakoNYylIzWuoD/anAD3UzvvR6cnswmfRFJFq/zYYq/k7Q=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.4", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.1", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.1" }, "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-wUi01RrTDTOoGtjEPHsxlzPtVzVc3R/AZ5wfh0dyqMAqolhHAHvG5iQjBCTi2AjQqa77FWWbA3kE3RkD+bDMgQ=="],
|
||||
|
||||
@@ -840,6 +841,8 @@
|
||||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.0", "", { "dependencies": { "@radix-ui/react-slot": "1.1.0" }, "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-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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-C6oAg451/fQT3EGbWHbCQjYTtbyjNO1uzQgMzwyivcHT3GKNEmu1q3UuREhN+HzHAVtv3ivMVK08QlC+PkYw9Q=="],
|
||||
|
||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw=="],
|
||||
|
||||
"@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "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-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA=="],
|
||||
@@ -848,6 +851,8 @@
|
||||
|
||||
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
|
||||
|
||||
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
@@ -1094,7 +1099,7 @@
|
||||
|
||||
"@scalar/object-utils": ["@scalar/object-utils@1.1.13", "", { "dependencies": { "flatted": "^3.3.1", "just-clone": "^6.2.0", "ts-deepmerge": "^7.0.1" } }, "sha512-311eTykIXgOtjCs4VTELj9UMT97jHTWc5qkGNoIzZ5nxjCcvOVe7kDQobIkE8dGT+ybOgHz5qly02Eu7nVHeZQ=="],
|
||||
|
||||
"@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.10", "", { "dependencies": { "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.4.5" } }, "sha512-6MSgvpNKu/anZy96dn8tXQZo1PuDCoeB4m2ZLLDS4vC2zaTnuNBvvQHx+gjwXNKWhTbIVy8bQpYBzlMAYnFNcQ=="],
|
||||
"@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.14", "", { "dependencies": { "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.4.5" } }, "sha512-VXr979NMx6wZ+kpFKor2eyCJZOjyMwcBRc6c4Gc92ZMOC7ZNYqjwbw+Ubh2ELJyP5cWAjOFSrNwtylema0pw5w=="],
|
||||
|
||||
"@scalar/openapi-types": ["@scalar/openapi-types@0.1.9", "", {}, "sha512-HQQudOSQBU7ewzfnBW9LhDmBE2XOJgSfwrh5PlUB7zJup/kaRkBGNgV2wMjNz9Af/uztiU/xNrO179FysmUT+g=="],
|
||||
|
||||
@@ -1390,7 +1395,7 @@
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"acorn": ["acorn@8.12.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="],
|
||||
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
|
||||
|
||||
@@ -1510,8 +1515,6 @@
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001668", "", {}, "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw=="],
|
||||
|
||||
"capnp-ts": ["capnp-ts@0.7.0", "", { "dependencies": { "debug": "^4.3.1", "tslib": "^2.2.0" } }, "sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
@@ -1564,8 +1567,6 @@
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"configstore": ["configstore@5.0.1", "", { "dependencies": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", "make-dir": "^3.0.0", "unique-string": "^2.0.0", "write-file-atomic": "^3.0.0", "xdg-basedir": "^4.0.0" } }, "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA=="],
|
||||
|
||||
"consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="],
|
||||
@@ -1756,7 +1757,7 @@
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"esniff": ["esniff@2.0.1", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg=="],
|
||||
|
||||
@@ -2308,8 +2309,6 @@
|
||||
|
||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||
|
||||
"mlly": ["mlly@1.7.4", "", { "dependencies": { "acorn": "^8.14.0", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="],
|
||||
|
||||
"mnemonist": ["mnemonist@0.38.3", "", { "dependencies": { "obliterator": "^1.6.1" } }, "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw=="],
|
||||
|
||||
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
||||
@@ -2454,8 +2453,6 @@
|
||||
|
||||
"pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="],
|
||||
|
||||
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"playwright": ["playwright@1.51.1", "", { "dependencies": { "playwright-core": "1.51.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.51.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw=="],
|
||||
@@ -2518,13 +2515,13 @@
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
||||
|
||||
"react-aria": ["react-aria@3.37.0", "", { "dependencies": { "@internationalized/string": "^3.2.5", "@react-aria/breadcrumbs": "^3.5.20", "@react-aria/button": "^3.11.1", "@react-aria/calendar": "^3.7.0", "@react-aria/checkbox": "^3.15.1", "@react-aria/color": "^3.0.3", "@react-aria/combobox": "^3.11.1", "@react-aria/datepicker": "^3.13.0", "@react-aria/dialog": "^3.5.21", "@react-aria/disclosure": "^3.0.1", "@react-aria/dnd": "^3.8.1", "@react-aria/focus": "^3.19.1", "@react-aria/gridlist": "^3.10.1", "@react-aria/i18n": "^3.12.5", "@react-aria/interactions": "^3.23.0", "@react-aria/label": "^3.7.14", "@react-aria/link": "^3.7.8", "@react-aria/listbox": "^3.14.0", "@react-aria/menu": "^3.17.0", "@react-aria/meter": "^3.4.19", "@react-aria/numberfield": "^3.11.10", "@react-aria/overlays": "^3.25.0", "@react-aria/progress": "^3.4.19", "@react-aria/radio": "^3.10.11", "@react-aria/searchfield": "^3.8.0", "@react-aria/select": "^3.15.1", "@react-aria/selection": "^3.22.0", "@react-aria/separator": "^3.4.5", "@react-aria/slider": "^3.7.15", "@react-aria/ssr": "^3.9.7", "@react-aria/switch": "^3.6.11", "@react-aria/table": "^3.16.1", "@react-aria/tabs": "^3.9.9", "@react-aria/tag": "^3.4.9", "@react-aria/textfield": "^3.16.0", "@react-aria/tooltip": "^3.7.11", "@react-aria/utils": "^3.27.0", "@react-aria/visually-hidden": "^3.8.19", "@react-types/shared": "^3.27.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-u3WUEMTcbQFaoHauHO3KhPaBYzEv1o42EdPcLAs05GBw9Q6Axlqwo73UFgMrsc2ElwLAZ4EKpSdWHLo1R5gfiw=="],
|
||||
|
||||
"react-aria-components": ["react-aria-components@1.6.0", "", { "dependencies": { "@internationalized/date": "^3.7.0", "@internationalized/string": "^3.2.5", "@react-aria/autocomplete": "3.0.0-alpha.37", "@react-aria/collections": "3.0.0-alpha.7", "@react-aria/color": "^3.0.3", "@react-aria/disclosure": "^3.0.1", "@react-aria/dnd": "^3.8.1", "@react-aria/focus": "^3.19.1", "@react-aria/interactions": "^3.23.0", "@react-aria/live-announcer": "^3.4.1", "@react-aria/menu": "^3.17.0", "@react-aria/toolbar": "3.0.0-beta.12", "@react-aria/tree": "3.0.0-beta.3", "@react-aria/utils": "^3.27.0", "@react-aria/virtualizer": "^4.1.1", "@react-stately/autocomplete": "3.0.0-alpha.0", "@react-stately/color": "^3.8.2", "@react-stately/disclosure": "^3.0.1", "@react-stately/layout": "^4.1.1", "@react-stately/menu": "^3.9.1", "@react-stately/selection": "^3.19.0", "@react-stately/table": "^3.13.1", "@react-stately/utils": "^3.10.5", "@react-stately/virtualizer": "^4.2.1", "@react-types/color": "^3.0.2", "@react-types/form": "^3.7.9", "@react-types/grid": "^3.2.11", "@react-types/shared": "^3.27.0", "@react-types/table": "^3.10.4", "@swc/helpers": "^0.5.0", "client-only": "^0.0.1", "react-aria": "^3.37.0", "react-stately": "^3.35.0", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-YfG9PUE7XrXtDDAqT4pLTGyYQaiHHTBFdAK/wNgGsypVnQSdzmyYlV3Ty8aHlZJI6hP9RWkbywvosXkU7KcPHg=="],
|
||||
|
||||
"react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
|
||||
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
||||
|
||||
"react-hotkeys-hook": ["react-hotkeys-hook@4.5.1", "", { "peerDependencies": { "react": ">=16.8.1", "react-dom": ">=16.8.1" } }, "sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg=="],
|
||||
|
||||
@@ -2598,12 +2595,6 @@
|
||||
|
||||
"rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="],
|
||||
|
||||
"rollup-plugin-inject": ["rollup-plugin-inject@3.0.2", "", { "dependencies": { "estree-walker": "^0.6.1", "magic-string": "^0.25.3", "rollup-pluginutils": "^2.8.1" } }, "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w=="],
|
||||
|
||||
"rollup-plugin-node-polyfills": ["rollup-plugin-node-polyfills@0.2.1", "", { "dependencies": { "rollup-plugin-inject": "^3.0.0" } }, "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA=="],
|
||||
|
||||
"rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="],
|
||||
|
||||
"router": ["router@2.0.0", "", { "dependencies": { "array-flatten": "3.0.0", "is-promise": "4.0.0", "methods": "~1.1.2", "parseurl": "~1.3.3", "path-to-regexp": "^8.0.0", "setprototypeof": "1.2.0", "utils-merge": "1.0.1" } }, "sha512-dIM5zVoG8xhC6rnSN8uoAgFARwTE7BQs8YwHEvK0VCmfxQXMaOuA1uiR1IPwsW7JyK5iTt7Od/TC9StasS2NPQ=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
@@ -2612,7 +2603,7 @@
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
"secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="],
|
||||
|
||||
@@ -2660,8 +2651,6 @@
|
||||
|
||||
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||
|
||||
"sourcemap-codec": ["sourcemap-codec@1.4.8", "", {}, "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="],
|
||||
|
||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="],
|
||||
@@ -3558,15 +3547,13 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@cloudflare/next-on-pages/chalk": ["chalk@5.3.0", "", {}, "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="],
|
||||
|
||||
"@cloudflare/next-on-pages/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/esbuild": ["esbuild@0.15.18", "", { "optionalDependencies": { "@esbuild/android-arm": "0.15.18", "@esbuild/linux-loong64": "0.15.18", "esbuild-android-64": "0.15.18", "esbuild-android-arm64": "0.15.18", "esbuild-darwin-64": "0.15.18", "esbuild-darwin-arm64": "0.15.18", "esbuild-freebsd-64": "0.15.18", "esbuild-freebsd-arm64": "0.15.18", "esbuild-linux-32": "0.15.18", "esbuild-linux-64": "0.15.18", "esbuild-linux-arm": "0.15.18", "esbuild-linux-arm64": "0.15.18", "esbuild-linux-mips64le": "0.15.18", "esbuild-linux-ppc64le": "0.15.18", "esbuild-linux-riscv64": "0.15.18", "esbuild-linux-s390x": "0.15.18", "esbuild-netbsd-64": "0.15.18", "esbuild-openbsd-64": "0.15.18", "esbuild-sunos-64": "0.15.18", "esbuild-windows-32": "0.15.18", "esbuild-windows-64": "0.15.18", "esbuild-windows-arm64": "0.15.18" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare": ["miniflare@3.20241018.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "^8.8.0", "acorn-walk": "^8.2.0", "capnp-ts": "^0.7.0", "exit-hook": "^2.2.1", "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", "workerd": "1.20241018.1", "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-g7i5oGAoJOk8+hJp77A5/wAdu7PEvi5hQc+0wzwzjhUNM2I5DHd2Cc29ACPhAe1kIXvCCVkxs3+REF52qnX0aw=="],
|
||||
"@cloudflare/next-on-pages/miniflare": ["miniflare@3.20250214.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250214.0", "ws": "8.18.0", "youch": "3.2.3", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-t+lT4p2lbOcKv4PS3sx1F/wcDAlbEYZCO2VooLp4H7JErWWYIi9yjD3UillC3CGOpiBahVg5nrPCoFltZf6UlA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler": ["wrangler@3.112.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250214.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.1", "workerd": "1.20250214.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250214.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-PNQWGze3ODlWwG33LPr8kNhbht3eB3L9fogv+fapk2fjaqj0kNweRapkwmvtz46ojcqWzsxmTe4nOC0hIVUfPA=="],
|
||||
"@cloudflare/next-on-pages/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -3676,24 +3663,88 @@
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.1", "", { "dependencies": { "@radix-ui/react-slot": "1.1.1" }, "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-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "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-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0" }, "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-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "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-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1" }, "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-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.4", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "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-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "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-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "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-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "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-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "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-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.1", "", { "dependencies": { "@radix-ui/react-slot": "1.1.1" }, "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-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "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-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0" }, "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-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "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-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "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-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "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-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "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-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
|
||||
@@ -3706,6 +3757,8 @@
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "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-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q=="],
|
||||
|
||||
"@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.1", "", { "dependencies": { "@radix-ui/react-slot": "1.1.1" }, "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-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg=="],
|
||||
|
||||
"@react-aria/focus/clsx": ["clsx@2.0.0", "", {}, "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="],
|
||||
@@ -3714,14 +3767,10 @@
|
||||
|
||||
"@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||
|
||||
"@scalar/api-client/@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.14", "", { "dependencies": { "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.4.5" } }, "sha512-VXr979NMx6wZ+kpFKor2eyCJZOjyMwcBRc6c4Gc92ZMOC7ZNYqjwbw+Ubh2ELJyP5cWAjOFSrNwtylema0pw5w=="],
|
||||
|
||||
"@scalar/api-client/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
|
||||
|
||||
"@scalar/api-client/pretty-ms": ["pretty-ms@8.0.0", "", { "dependencies": { "parse-ms": "^3.0.0" } }, "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q=="],
|
||||
|
||||
"@scalar/import/@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.14", "", { "dependencies": { "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.4.5" } }, "sha512-VXr979NMx6wZ+kpFKor2eyCJZOjyMwcBRc6c4Gc92ZMOC7ZNYqjwbw+Ubh2ELJyP5cWAjOFSrNwtylema0pw5w=="],
|
||||
|
||||
"@scalar/oas-utils/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
|
||||
|
||||
"@scalar/object-utils/flatted": ["flatted@3.3.1", "", {}, "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="],
|
||||
@@ -3922,8 +3971,6 @@
|
||||
|
||||
"@vercel/gatsby-plugin-vercel-builder/fs-extra": ["fs-extra@11.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw=="],
|
||||
|
||||
"@vercel/nft/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"@vercel/nft/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
|
||||
"@vercel/nft/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||
@@ -3980,8 +4027,6 @@
|
||||
|
||||
"cacheable-request/lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
|
||||
|
||||
"capnp-ts/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"codemirror/@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/@codemirror/commands": ["@codemirror/commands@6.7.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-+cduIZ2KbesDhbykV02K25A5xIVrquSPz4UxxYBemRlAT2aW8dhwUgLDwej7q/RJUHKk4nALYcR1puecDvbdqw=="],
|
||||
@@ -4032,7 +4077,7 @@
|
||||
|
||||
"gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
|
||||
"gitbook-v2/next": ["next@15.3.1-canary.7", "", { "dependencies": { "@next/env": "15.3.1-canary.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.7", "@next/swc-darwin-x64": "15.3.1-canary.7", "@next/swc-linux-arm64-gnu": "15.3.1-canary.7", "@next/swc-linux-arm64-musl": "15.3.1-canary.7", "@next/swc-linux-x64-gnu": "15.3.1-canary.7", "@next/swc-linux-x64-musl": "15.3.1-canary.7", "@next/swc-win32-arm64-msvc": "15.3.1-canary.7", "@next/swc-win32-x64-msvc": "15.3.1-canary.7", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "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-RblvC7A7k8cKpSa6JSCbaatb6JAncv2niUSH7TqXt6xHUoItnxa3Fu7xkducFJQdcW2Hv2h0+NBu6kaKf0S4zw=="],
|
||||
"gitbook-v2/next": ["next@15.4.0-canary.7", "", { "dependencies": { "@next/env": "15.4.0-canary.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.7", "@next/swc-darwin-x64": "15.4.0-canary.7", "@next/swc-linux-arm64-gnu": "15.4.0-canary.7", "@next/swc-linux-arm64-musl": "15.4.0-canary.7", "@next/swc-linux-x64-gnu": "15.4.0-canary.7", "@next/swc-linux-x64-musl": "15.4.0-canary.7", "@next/swc-win32-arm64-msvc": "15.4.0-canary.7", "@next/swc-win32-x64-msvc": "15.4.0-canary.7", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "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-ZYjT0iu+4osz8XIlr31MuoXaNQKRU75UcwEgNBt93gftoh6tzV2Mebz6sOGeVReYuYUvYlLJJksMBTNcFcPbSA=="],
|
||||
|
||||
"global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="],
|
||||
|
||||
@@ -4070,8 +4115,6 @@
|
||||
|
||||
"make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"mdast-util-gfm/mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ=="],
|
||||
|
||||
"mdast-util-gfm-footnote/mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ=="],
|
||||
@@ -4090,8 +4133,6 @@
|
||||
|
||||
"micromark/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"miniflare/undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="],
|
||||
|
||||
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
||||
@@ -4102,8 +4143,6 @@
|
||||
|
||||
"minizlib/minipass": ["minipass@2.9.0", "", { "dependencies": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg=="],
|
||||
|
||||
"mlly/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"next/@swc/helpers": ["@swc/helpers@0.5.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A=="],
|
||||
|
||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
@@ -4156,12 +4195,6 @@
|
||||
|
||||
"rimraf/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||
|
||||
"rollup-plugin-inject/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
|
||||
|
||||
"rollup-plugin-inject/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="],
|
||||
|
||||
"rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
|
||||
|
||||
"router/is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"router/path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
|
||||
@@ -4200,10 +4233,10 @@
|
||||
|
||||
"terminal-link/supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="],
|
||||
|
||||
"terser/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"ts-node/acorn": ["acorn@8.12.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="],
|
||||
|
||||
"ts-node/acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="],
|
||||
|
||||
"ts-node/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
|
||||
@@ -4650,21 +4683,13 @@
|
||||
|
||||
"@cloudflare/next-on-pages/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.15.18", "", { "os": "linux", "cpu": "none" }, "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="],
|
||||
"@cloudflare/next-on-pages/miniflare/undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd": ["workerd@1.20241018.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20241018.1", "@cloudflare/workerd-darwin-arm64": "1.20241018.1", "@cloudflare/workerd-linux-64": "1.20241018.1", "@cloudflare/workerd-linux-arm64": "1.20241018.1", "@cloudflare/workerd-windows-64": "1.20241018.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-JPW2oAbYOnJj1c5boyDOdjl/Yvur45jhVE8lf+I9oxR6myyAvuH2tdXO62kye68jRluJOMUeyssLes+JRwLmaA=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd": ["workerd@1.20250214.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250214.0", "@cloudflare/workerd-darwin-arm64": "1.20250214.0", "@cloudflare/workerd-linux-64": "1.20250214.0", "@cloudflare/workerd-linux-arm64": "1.20250214.0", "@cloudflare/workerd-windows-64": "1.20250214.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-QWcqXZLiMpV12wiaVnb3nLmfs/g4ZsFQq2mX85z546r3AX4CTIkXl0VP50W3CwqLADej3PGYiRDOTelDOwVG1g=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
|
||||
"@cloudflare/next-on-pages/miniflare/youch": ["youch@3.2.3", "", { "dependencies": { "cookie": "^0.5.0", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-ZBcWz/uzZaQVdCvfV4uk616Bbpf2ee+F/AvuKDR5EwX/Y4v06xWdtMluqTD7+KlZdM93lLm9gMZYo0sKBS0pgw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/miniflare": ["miniflare@3.20250214.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250214.0", "ws": "8.18.0", "youch": "3.2.3", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-t+lT4p2lbOcKv4PS3sx1F/wcDAlbEYZCO2VooLp4H7JErWWYIi9yjD3UillC3CGOpiBahVg5nrPCoFltZf6UlA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/unenv": ["unenv@2.0.0-rc.1", "", { "dependencies": { "defu": "^6.1.4", "mlly": "^1.7.4", "ohash": "^1.1.4", "pathe": "^1.1.2", "ufo": "^1.5.4" } }, "sha512-PU5fb40H8X149s117aB4ytbORcCvlASdtF97tfls4BPIyj4PeVxvpSuy1jAptqYHqB0vb2w2sHvzM0XWcp2OKg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd": ["workerd@1.20250214.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250214.0", "@cloudflare/workerd-darwin-arm64": "1.20250214.0", "@cloudflare/workerd-linux-64": "1.20250214.0", "@cloudflare/workerd-linux-arm64": "1.20250214.0", "@cloudflare/workerd-windows-64": "1.20250214.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-QWcqXZLiMpV12wiaVnb3nLmfs/g4ZsFQq2mX85z546r3AX4CTIkXl0VP50W3CwqLADej3PGYiRDOTelDOwVG1g=="],
|
||||
"@cloudflare/next-on-pages/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -4742,8 +4767,44 @@
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0" }, "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-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "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-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||
@@ -4908,23 +4969,23 @@
|
||||
|
||||
"gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"gitbook-v2/next/@next/env": ["@next/env@15.3.1-canary.7", "", {}, "sha512-QSzAlzxaGrQYj3Nzgxd35WM6pmxmNk9lOCn0nsNLPidvc3H6wj/fV3Pl8/tVCYQP61luGuA0Ib0VwZc8ja119w=="],
|
||||
"gitbook-v2/next/@next/env": ["@next/env@15.4.0-canary.7", "", {}, "sha512-q8S7f2lQti3Y3gcAPzE8Pj8y0EwiWHVyyilMzoLbDPXGVfxlQhXLRiFdy2cDkKN4DyjGZWDeehEtw4huvJAa3Q=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.1-canary.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fs7JYU0MKtpgsDBIvbT/wn1MwKP8C+bqcb6IjWnDtVcciFKzYDs/tPCBW6CtYg1wpHqm9horZDA1ytb/sAghxA=="],
|
||||
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+TMxUu5CAWNe+UFRc47BZAXQxCRqZfVbGyCldddiog4MorvL7kBxSd1qlmrwI73fRRKtXkHIH1TaeItyxzC9rQ=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.1-canary.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Efl6es/IEXJf5groF78fc1PrTEwK9bOyi6fRjp1bsa/AWOHp8rTc3l0T6EeY5T0SWeYilisZZa9rax7nTgw4bw=="],
|
||||
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-veXp8lg/X/7O+pG9BDQ3OizFz3B40v29jsvEWj+ULY/W8Z6+dCSd5XPP2M8fG/gKKKA0D6L0CnnM2Mj0RRSUJw=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.1-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-IS71I8z2wlecRZRHXqWTtxVozZt1h9IH/2MX1P/WkCjQH98acFmCkw0jo/y70mrZ4lw6tRGTNkSBMun+5P5P+w=="],
|
||||
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-KxNGfW7BO0Z5B9rJyl9p7YVjNrxAhu06mH6h1PSdouZG7YMYpdRCconVXeuBI0PEu6g3ywNrOVxZUk1V6G5u0Q=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.1-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uJ4env9fZxIFM+aRBCjNeG91HHZ2cRXiF25xJqGmCC1Y5jT5oYAB/tIBCFRSj6NRzdt4pZAamCMRS41WzzVVUQ=="],
|
||||
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-THgXgmP/cC4DsNwvC6uqB90CebB7Ep1KyZajQL3fYKT5V4SWr46yngKLyoyJVeAYWJH908MrWddf7Ya/Zq7cyg=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.1-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-/+efTL2TxoSAYCxdf+g6WvDKIJDoaETvF3I+lxpixhctfrIwpr2qnG0e6u2M55k1iYiiyFIOA63fpIsGnIMJZA=="],
|
||||
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-kpLB3Jj7fProynQYj2ahFyZlJs0xwm71VzCVrNRu6u7qJGXn6dK5h7+hro8y/y1iqjXWgCLSdxWSHahhWK8XdQ=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.1-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-Hb+agfULOjEhJAmq/r9OTVAo8KxFlnuCT7BEUbSuVP/Gxn883al6juSG7ZZfk7P4a+ZU6iVYyHKIFAKCarZmUA=="],
|
||||
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-rnGAKvl4cWPVV9D+SybWOGijm0VmKXyqQ+IN0A6WDgdlYZAZP0ZnJv/rq7DSvuOh19AXS8UpQc88SelXV/3j3Q=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.1-canary.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-gNd5Vq7k9xIEQj85JoavHGRsyvu1zKOjVoPUYtsZRxXw6kcBg84x2g8NeT/3SmwH1PiFU/sjQXtRzr2Kuc2j3A=="],
|
||||
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-/PRbn//EuR3UGiquk050gqvjxLliEgGBy1Cx9KkpAT7szaHOBj1mDDQmxMTEhRex4i3YfKGJXWn5mLMCveya6Q=="],
|
||||
|
||||
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.1-canary.7", "", { "os": "win32", "cpu": "x64" }, "sha512-D5i+KinvNNmspXfgIvEZ1RFOKVhOgdtoSC7/xEUm55hEkcWFVHOtVVPvgV7WUeo1elNxbgxytzmyKbLaifP03g=="],
|
||||
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7a92XL+DlrbWyycCpQjjQMHOrsA0p+VvS7iA2dyi89Xsq0qtOPzFH0Gb56fsjh6M6BQGFhboOSzjmpjlkMTilQ=="],
|
||||
|
||||
"gitbook-v2/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
@@ -5114,81 +5175,15 @@
|
||||
|
||||
"@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20241018.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-CRySEzjNRoR8frP5AbtJXd1tgVJa5v7bZon9Dh6nljYlhG+piDv8jvOVEUqF3cXXS+M5aXwr4NlozdMvl5g5mg=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250214.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cDvvedWDc5zrgDnuXe2qYcz/TwBvzmweO55C7XpPuAWJ9Oqxv81PkdekYxD8mH989aQ/GI5YD0Fe6fDYlM+T3Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20241018.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y63yWJNTgETDFkY3Ony71/k/G1HRDFIhEzwbT+OWmg1Qbsqa4TquHPVFkgv+OJhpmD3HV9gTBcn/M2QJ/+pGmg=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250214.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NytCvRveVzu0mRKo+tvZo3d/gCUway3B2ZVqSi/TS6NXDGBYIJo7g6s3BnTLS74kgyzeDOjhu9j/RBJBS809qw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20241018.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a2AbSAXNMMiREvN+PwjHdJ5zOzI4+qf8+rb6H/y4HcVbPZN5C2fanxv5Bx7NUHLiMD/W0FrGug1aU+RPUVZC9Q=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250214.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pQ7+aHNHj8SiYEs4d/6cNoimE5xGeCMfgU1yfDFtA9YGN9Aj2BITZgOWPec+HW7ZkOy9oWlNrO6EvVjGgB4tbQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20241018.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-iCJ7bjD/+zhlp3IWnkiry180DwdNvak/sVoS98pIAS41aR3gJVzE5BCz/2yTWFdCoUVZ5yKJrv1HhSKgQRBIEw=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250214.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vhlfah6Yd9ny1npNQjNgElLIjR6OFdEbuR3LCfbLDCwzWEBFhIf7yC+Tpp/a0Hq7kLz3sLdktaP7xl3PJhyOjA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20241018.1", "", { "os": "win32", "cpu": "x64" }, "sha512-qwDVh/KrwEPY82h6tZ1O4BXBAKeGy30BeTr9wvTUVeY9eX/KT73GuEG+ttwiashRfqjOa0Gcqjsfpd913ITFyg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/miniflare/undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/miniflare/youch": ["youch@3.2.3", "", { "dependencies": { "cookie": "^0.5.0", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-ZBcWz/uzZaQVdCvfV4uk616Bbpf2ee+F/AvuKDR5EwX/Y4v06xWdtMluqTD7+KlZdM93lLm9gMZYo0sKBS0pgw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/unenv/ohash": ["ohash@1.1.4", "", {}, "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/unenv/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250214.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cDvvedWDc5zrgDnuXe2qYcz/TwBvzmweO55C7XpPuAWJ9Oqxv81PkdekYxD8mH989aQ/GI5YD0Fe6fDYlM+T3Q=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250214.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NytCvRveVzu0mRKo+tvZo3d/gCUway3B2ZVqSi/TS6NXDGBYIJo7g6s3BnTLS74kgyzeDOjhu9j/RBJBS809qw=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250214.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pQ7+aHNHj8SiYEs4d/6cNoimE5xGeCMfgU1yfDFtA9YGN9Aj2BITZgOWPec+HW7ZkOy9oWlNrO6EvVjGgB4tbQ=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250214.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vhlfah6Yd9ny1npNQjNgElLIjR6OFdEbuR3LCfbLDCwzWEBFhIf7yC+Tpp/a0Hq7kLz3sLdktaP7xl3PJhyOjA=="],
|
||||
|
||||
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250214.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GMwMyFbkjBKjYJoKDhGX8nuL4Gqe3IbVnVWf2Q6086CValyIknupk5J6uQWGw2EBU3RGO3x4trDXT5WphQJZDQ=="],
|
||||
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250214.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GMwMyFbkjBKjYJoKDhGX8nuL4Gqe3IbVnVWf2Q6086CValyIknupk5J6uQWGw2EBU3RGO3x4trDXT5WphQJZDQ=="],
|
||||
|
||||
"@node-minify/core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
|
||||
+3
-3
@@ -10,9 +10,9 @@
|
||||
"packageManager": "bun@1.2.8",
|
||||
"overrides": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"@gitbook/api": "0.109.0"
|
||||
"@gitbook/api": "0.113.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"version": "0.3.1",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "^0.109.0",
|
||||
"@gitbook/api": "^0.111.0",
|
||||
"assert-never": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -220,7 +220,21 @@ export function colorScale(
|
||||
continue;
|
||||
}
|
||||
|
||||
const chromaRatio = index === 8 || index === 9 ? 1 : index * 0.05;
|
||||
const chromaRatio = (() => {
|
||||
switch (index) {
|
||||
// Step 9 and 10 have max chroma, meaning they are fully saturated.
|
||||
case 8:
|
||||
case 9:
|
||||
return 1;
|
||||
// Step 11 and 12 have a reduced chroma
|
||||
case 10:
|
||||
return 0.4;
|
||||
case 11:
|
||||
return 0.1;
|
||||
default:
|
||||
return index * 0.05;
|
||||
}
|
||||
})();
|
||||
|
||||
const shade = {
|
||||
L: targetL, // Blend lightness
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
getDataOrNull,
|
||||
throwIfDataError,
|
||||
} from '@v2/lib/data';
|
||||
import assertNever from 'assert-never';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { assert } from 'ts-essentials';
|
||||
import { GITBOOK_URL } from './env';
|
||||
@@ -242,19 +243,45 @@ export async function fetchSiteContextByIds(
|
||||
? parseSiteSectionsAndGroups(siteStructure, ids.siteSection)
|
||||
: null;
|
||||
|
||||
const siteSpace = (
|
||||
siteStructure.type === 'siteSpaces' && siteStructure.structure
|
||||
? siteStructure.structure
|
||||
: sections?.current.siteSpaces
|
||||
)?.find((siteSpace) => siteSpace.id === ids.siteSpace);
|
||||
if (!siteSpace) {
|
||||
throw new Error('Site space not found');
|
||||
}
|
||||
// Parse the current siteSpace and siteSpaces based on the site structure type.
|
||||
const { siteSpaces, siteSpace }: { siteSpaces: SiteSpace[]; siteSpace: SiteSpace } = (() => {
|
||||
if (siteStructure.type === 'siteSpaces') {
|
||||
const siteSpaces = siteStructure.structure;
|
||||
const siteSpace = siteSpaces.find((siteSpace) => siteSpace.id === ids.siteSpace);
|
||||
|
||||
const siteSpaces =
|
||||
siteStructure.type === 'siteSpaces'
|
||||
? siteStructure.structure
|
||||
: (sections?.current.siteSpaces ?? []);
|
||||
if (!siteSpace) {
|
||||
throw new Error(
|
||||
`Site space "${ids.siteSpace}" not found in structure type="siteSpaces"`
|
||||
);
|
||||
}
|
||||
|
||||
return { siteSpaces, siteSpace };
|
||||
}
|
||||
|
||||
if (siteStructure.type === 'sections') {
|
||||
assert(
|
||||
sections,
|
||||
`cannot find site space "${ids.siteSpace}" because parsed sections are missing siteStructure.type="sections" siteSection="${ids.siteSection}"`
|
||||
);
|
||||
|
||||
const currentSection = sections.current;
|
||||
const siteSpaces = currentSection.siteSpaces;
|
||||
const siteSpace = currentSection.siteSpaces.find(
|
||||
(siteSpace) => siteSpace.id === ids.siteSpace
|
||||
);
|
||||
|
||||
if (!siteSpace) {
|
||||
throw new Error(
|
||||
`Site space "${ids.siteSpace}" not found in structure type="sections" currentSection="${currentSection.id}"`
|
||||
);
|
||||
}
|
||||
|
||||
return { siteSpaces, siteSpace };
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
assertNever(siteStructure, `cannot handle site structure of type ${siteStructure.type}`);
|
||||
})();
|
||||
|
||||
const customization = (() => {
|
||||
if (ids.siteSpace) {
|
||||
@@ -380,7 +407,7 @@ export function checkIsRootSiteContext(context: GitBookSiteContext): boolean {
|
||||
function parseSiteSectionsAndGroups(structure: SiteStructure, siteSectionId: string) {
|
||||
const sectionsAndGroups = getSiteStructureSections(structure, { ignoreGroups: false });
|
||||
const section = parseCurrentSection(structure, siteSectionId);
|
||||
assert(section, 'A section must be defined when there are multiple sections');
|
||||
assert(section, `couldn't find section "${siteSectionId}" in site structure`);
|
||||
return { list: sectionsAndGroups, current: section } satisfies SiteSections;
|
||||
}
|
||||
|
||||
|
||||
@@ -1394,6 +1394,7 @@ async function* streamAIResponse(
|
||||
input: params.input,
|
||||
output: params.output,
|
||||
model: params.model,
|
||||
tools: params.tools,
|
||||
});
|
||||
|
||||
for await (const event of res) {
|
||||
|
||||
@@ -189,5 +189,6 @@ export interface GitBookDataFetcher {
|
||||
input: api.AIMessageInput[];
|
||||
output: api.AIOutputFormat;
|
||||
model: api.AIModel;
|
||||
tools?: api.AIToolCapabilities;
|
||||
}): AsyncGenerator<api.AIStreamResponse, void, unknown>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# gitbook
|
||||
|
||||
## 0.11.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ebc39e9]
|
||||
- Updated dependencies [b6b09d4]
|
||||
- @gitbook/react-openapi@1.2.1
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- d67699a: Add OpenAPI Webhook block
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4b8a621: Show sections tabs only if there is at least two sections
|
||||
- 8ed1bda: Translate OpenAPI blocks
|
||||
- 7588cfe: Improve OpenAPIResponses examples and schemas
|
||||
- Updated dependencies [eeb977f]
|
||||
- Updated dependencies [3363a18]
|
||||
- Updated dependencies [d67699a]
|
||||
- Updated dependencies [8ed1bda]
|
||||
- Updated dependencies [7588cfe]
|
||||
- Updated dependencies [ad1dc0b]
|
||||
- @gitbook/react-openapi@1.2.0
|
||||
|
||||
## 0.10.1
|
||||
|
||||
### Patch 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',
|
||||
@@ -157,11 +157,11 @@ const testCases: TestsCase[] = [
|
||||
contentBaseURL: 'https://wiki.redmodding.org',
|
||||
tests: [{ name: 'Home', url: '/' }],
|
||||
},
|
||||
{
|
||||
name: 'docs.cherry-ai.com',
|
||||
contentBaseURL: 'https://docs.cherry-ai.com',
|
||||
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
},
|
||||
// {
|
||||
// name: 'docs.cherry-ai.com',
|
||||
// contentBaseURL: 'https://docs.cherry-ai.com',
|
||||
// tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
// },
|
||||
{
|
||||
name: 'docs.snyk.io',
|
||||
contentBaseURL: 'https://docs.snyk.io',
|
||||
|
||||
@@ -111,24 +111,24 @@ const testCases: TestsCase[] = [
|
||||
name: 'Customized variant titles are displayed',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
const spaceDrowpdown = page
|
||||
const spaceDropdown = page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
await spaceDrowpdown.click();
|
||||
await spaceDropdown.click();
|
||||
|
||||
const variantSelectionDropdown = page.locator(
|
||||
'css=[data-testid="space-dropdown-button"] + div'
|
||||
'css=[data-testid="dropdown-menu"]'
|
||||
);
|
||||
// the customized space title
|
||||
await expect(
|
||||
variantSelectionDropdown.getByRole('link', {
|
||||
variantSelectionDropdown.getByRole('menuitem', {
|
||||
name: 'Multi-Variants',
|
||||
})
|
||||
).toBeVisible();
|
||||
|
||||
// the NON-customized space title
|
||||
await expect(
|
||||
variantSelectionDropdown.getByRole('link', {
|
||||
variantSelectionDropdown.getByRole('menuitem', {
|
||||
name: 'RFCs',
|
||||
})
|
||||
).toBeVisible();
|
||||
@@ -145,14 +145,17 @@ const testCases: TestsCase[] = [
|
||||
url: 'api-multi-versions/reference/api-reference/pets',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
const spaceDrowpdown = await page
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
await spaceDrowpdown.click();
|
||||
await spaceDropdown.click();
|
||||
|
||||
const variantSelectionDropdown = page.locator(
|
||||
'css=[data-testid="dropdown-menu"]'
|
||||
);
|
||||
// Click the second variant in the dropdown
|
||||
await page
|
||||
.getByRole('link', {
|
||||
await variantSelectionDropdown
|
||||
.getByRole('menuitem', {
|
||||
name: '2.0',
|
||||
})
|
||||
.click();
|
||||
@@ -168,14 +171,18 @@ const testCases: TestsCase[] = [
|
||||
url: 'api-multi-versions-share-links/8tNo6MeXg7CkFMzSSz81/reference/api-reference/pets',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
const spaceDrowpdown = await page
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
await spaceDrowpdown.click();
|
||||
await spaceDropdown.click();
|
||||
|
||||
const variantSelectionDropdown = page.locator(
|
||||
'css=[data-testid="dropdown-menu"]'
|
||||
);
|
||||
|
||||
// Click the second variant in the dropdown
|
||||
await page
|
||||
.getByRole('link', {
|
||||
await variantSelectionDropdown
|
||||
.getByRole('menuitem', {
|
||||
name: '2.0',
|
||||
})
|
||||
.click();
|
||||
@@ -205,14 +212,18 @@ const testCases: TestsCase[] = [
|
||||
return `api-multi-versions-va/reference/api-reference/pets?jwt_token=${token}`;
|
||||
},
|
||||
run: async (page) => {
|
||||
const spaceDrowpdown = await page
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
await spaceDrowpdown.click();
|
||||
await spaceDropdown.click();
|
||||
|
||||
const variantSelectionDropdown = page.locator(
|
||||
'css=[data-testid="dropdown-menu"]'
|
||||
);
|
||||
|
||||
// Click the second variant in the dropdown
|
||||
await page
|
||||
.getByRole('link', {
|
||||
await variantSelectionDropdown
|
||||
.getByRole('menuitem', {
|
||||
name: '2.0',
|
||||
})
|
||||
.click();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.10.1",
|
||||
"version": "0.11.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "env-cmd --silent -f ../../.env.local next dev",
|
||||
@@ -27,6 +27,7 @@
|
||||
"@gitbook/react-math": "workspace:*",
|
||||
"@gitbook/react-openapi": "workspace:*",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.3",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
@@ -37,6 +38,7 @@
|
||||
"assert-never": "^1.2.1",
|
||||
"bun-types": "^1.1.20",
|
||||
"classnames": "^2.5.1",
|
||||
"event-iterator": "^2.0.0",
|
||||
"framer-motion": "^10.16.14",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsontoxml": "^1.0.1",
|
||||
@@ -52,8 +54,9 @@
|
||||
"openapi-types": "^12.1.3",
|
||||
"p-map": "^7.0.0",
|
||||
"parse-cache-control": "^1.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"partial-json": "^0.1.7",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
@@ -70,13 +73,11 @@
|
||||
"usehooks-ts": "^3.1.0",
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.24.5",
|
||||
"event-iterator": "^2.0.0",
|
||||
"partial-json": "^0.1.7",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@argos-ci/playwright": "^5.0.3",
|
||||
"@cloudflare/next-on-pages": "1.13.7",
|
||||
"@cloudflare/next-on-pages": "1.13.12",
|
||||
"@cloudflare/workers-types": "^4.20241230.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
'use client';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { Icon, type IconName } from '@gitbook/icons';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useVisitedPages } from '../Insights';
|
||||
import { usePageContext } from '../PageContext';
|
||||
import { Emoji } from '../primitives';
|
||||
import { type SuggestedPage, useAdaptiveContext } from './AdaptiveContext';
|
||||
import { streamNextPageSuggestions } from './server-actions/streamNextPageSuggestions';
|
||||
|
||||
export function AINextPageSuggestions() {
|
||||
const { selectedJourney, open } = useAdaptiveContext();
|
||||
|
||||
const currentPage = usePageContext();
|
||||
const visitedPages = useVisitedPages((state) => state.pages);
|
||||
|
||||
const [pages, setPages] = useState<SuggestedPage[]>(selectedJourney?.pages ?? []);
|
||||
const [suggestedPages, setSuggestedPages] = useState<SuggestedPage[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let canceled = false;
|
||||
|
||||
if (selectedJourney?.pages && selectedJourney.pages.length > 0) {
|
||||
setPages(selectedJourney.pages);
|
||||
} else {
|
||||
setPages(suggestedPages);
|
||||
}
|
||||
|
||||
if (suggestedPages.length === 0) {
|
||||
(async () => {
|
||||
const stream = await streamNextPageSuggestions({
|
||||
currentPage: {
|
||||
id: currentPage.pageId,
|
||||
title: currentPage.title,
|
||||
},
|
||||
currentSpace: {
|
||||
id: currentPage.spaceId,
|
||||
},
|
||||
visitedPages: visitedPages,
|
||||
});
|
||||
|
||||
for await (const page of stream) {
|
||||
if (canceled) return;
|
||||
|
||||
setPages((prev) => [...prev, page]);
|
||||
setSuggestedPages((prev) => [...prev, page]);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
return () => {
|
||||
canceled = true;
|
||||
};
|
||||
}, [
|
||||
selectedJourney,
|
||||
currentPage.pageId,
|
||||
currentPage.spaceId,
|
||||
currentPage.title,
|
||||
visitedPages,
|
||||
suggestedPages,
|
||||
]);
|
||||
|
||||
return (
|
||||
open && (
|
||||
<div className="animate-fadeIn">
|
||||
<motion.div className="mb-2 flex flex-row items-start gap-3">
|
||||
<AnimatePresence mode="wait">
|
||||
{selectedJourney?.icon ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
key={selectedJourney.icon}
|
||||
>
|
||||
<Icon
|
||||
icon={selectedJourney.icon as IconName}
|
||||
className="left-0 mt-2 size-6 shrink-0 text-tint-subtle"
|
||||
/>
|
||||
</motion.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
<motion.div className={tcls('flex flex-col')} layout="position">
|
||||
<div className="font-semibold text-tint text-xs uppercase tracking-wide">
|
||||
Suggested pages
|
||||
</div>
|
||||
<AnimatePresence mode="wait">
|
||||
{selectedJourney?.label ? (
|
||||
<motion.h5
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
key={selectedJourney.label}
|
||||
className="font-semibold text-base"
|
||||
>
|
||||
{selectedJourney.label}
|
||||
</motion.h5>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
<div className="-mb-1.5 flex flex-col gap-1">
|
||||
{Object.assign(Array.from({ length: 5 }), pages).map(
|
||||
(page: SuggestedPage | undefined, index) =>
|
||||
page ? (
|
||||
<Link
|
||||
key={`${selectedJourney?.label}-${page.id}`}
|
||||
className="-mx-2 flex animate-fadeIn gap-2 rounded px-2.5 py-1 transition-all hover:bg-tint-hover hover:text-tint-strong"
|
||||
href={page.href}
|
||||
style={{ animationDelay: `${0.2 + index * 0.05}s` }}
|
||||
>
|
||||
{page.icon ? (
|
||||
<Icon
|
||||
icon={page.icon as IconName}
|
||||
className="mt-0.5 size-4 shrink-0 text-tint-subtle"
|
||||
/>
|
||||
) : null}
|
||||
{page.emoji ? <Emoji code={page.emoji} /> : null}
|
||||
{page.title}
|
||||
</Link>
|
||||
) : (
|
||||
<div
|
||||
key={index}
|
||||
className="my-1 h-5 animate-pulse rounded bg-tint-hover"
|
||||
style={{
|
||||
animationDelay: `${index * 0.2}s`,
|
||||
width: `${((index * 17) % 50) + 50}%`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
'use client';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { Icon, type IconName } from '@gitbook/icons';
|
||||
import { JOURNEY_COUNT, type Journey, useAdaptiveContext } from './AdaptiveContext';
|
||||
|
||||
export function AIPageJourneySuggestions() {
|
||||
const { journeys, selectedJourney, setSelectedJourney, open } = useAdaptiveContext();
|
||||
|
||||
return (
|
||||
open && (
|
||||
<div className="animate-fadeIn">
|
||||
<div className="mb-2 flex flex-row items-center gap-1 font-semibold text-tint text-xs uppercase tracking-wide">
|
||||
More to explore
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{Object.assign(Array.from({ length: JOURNEY_COUNT }), journeys).map(
|
||||
(journey: Journey | undefined, index) => {
|
||||
const isSelected =
|
||||
journey?.label && journey.label === selectedJourney?.label;
|
||||
const isLoading = !journey || journey?.label === undefined;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={journey?.label === undefined}
|
||||
className={tcls(
|
||||
'flex flex-col items-center justify-center gap-2 rounded bg-tint px-2 py-4 text-center ring-1 ring-tint-subtle ring-inset transition-all',
|
||||
isLoading
|
||||
? 'h-24 scale-90 animate-pulse'
|
||||
: 'hover:bg-tint-hover hover:text-tint-strong hover:ring-tint active:scale-95',
|
||||
isSelected &&
|
||||
'bg-primary-active text-primary-strong ring-2 ring-primary hover:bg-primary-active hover:ring-primary'
|
||||
)}
|
||||
style={{
|
||||
animationDelay: `${index * 0.2}s`,
|
||||
}}
|
||||
onClick={() =>
|
||||
setSelectedJourney(isSelected ? undefined : journey)
|
||||
}
|
||||
>
|
||||
{journey?.icon ? (
|
||||
<Icon
|
||||
icon={journey.icon as IconName}
|
||||
className="size-4 animate-fadeIn text-tint-subtle [animation-delay:300ms]"
|
||||
/>
|
||||
) : null}
|
||||
{journey?.label ? (
|
||||
<span className="animate-fadeIn leading-tight [animation-delay:400ms]">
|
||||
{journey.label}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { useVisitedPages } from '../Insights';
|
||||
import { usePageContext } from '../PageContext';
|
||||
import { streamPageJourneySuggestions } from './server-actions';
|
||||
|
||||
export type SuggestedPage = {
|
||||
id: string;
|
||||
title: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
emoji?: string;
|
||||
};
|
||||
|
||||
export type Journey = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
pages?: Array<SuggestedPage>;
|
||||
};
|
||||
|
||||
type AdaptiveContextType = {
|
||||
journeys: Journey[];
|
||||
selectedJourney: Journey | undefined;
|
||||
setSelectedJourney: (journey: Journey | undefined) => void;
|
||||
loading: boolean;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const AdaptiveContext = React.createContext<AdaptiveContextType | null>(null);
|
||||
|
||||
export const JOURNEY_COUNT = 4;
|
||||
|
||||
/**
|
||||
* Client side context provider to pass information about the current page.
|
||||
*/
|
||||
export function JourneyContextProvider({
|
||||
children,
|
||||
spaces,
|
||||
}: { children: React.ReactNode; spaces: { id: string; title: string }[] }) {
|
||||
const [journeys, setJourneys] = React.useState<Journey[]>([]);
|
||||
const [selectedJourney, setSelectedJourney] = React.useState<Journey | undefined>(undefined);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [open, setOpen] = React.useState(true);
|
||||
|
||||
const currentPage = usePageContext();
|
||||
const visitedPages = useVisitedPages((state) => state.pages);
|
||||
|
||||
useEffect(() => {
|
||||
let canceled = false;
|
||||
|
||||
setJourneys([]);
|
||||
|
||||
(async () => {
|
||||
const stream = await streamPageJourneySuggestions({
|
||||
count: JOURNEY_COUNT,
|
||||
currentPage: {
|
||||
id: currentPage.pageId,
|
||||
title: currentPage.title,
|
||||
},
|
||||
currentSpace: {
|
||||
id: currentPage.spaceId,
|
||||
},
|
||||
allSpaces: spaces,
|
||||
visitedPages,
|
||||
});
|
||||
|
||||
for await (const journey of stream) {
|
||||
if (canceled) return;
|
||||
|
||||
setJourneys((prev) => [...prev, journey]);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
canceled = true;
|
||||
};
|
||||
}, [currentPage.pageId, currentPage.spaceId, currentPage.title, visitedPages, spaces]);
|
||||
|
||||
return (
|
||||
<AdaptiveContext.Provider
|
||||
value={{ journeys, selectedJourney, setSelectedJourney, loading, open, setOpen }}
|
||||
>
|
||||
{children}
|
||||
</AdaptiveContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to use the adaptive context.
|
||||
*/
|
||||
export function useAdaptiveContext() {
|
||||
const context = React.useContext(AdaptiveContext);
|
||||
if (!context) {
|
||||
throw new Error('useAdaptiveContext must be used within a AdaptiveContextProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { AINextPageSuggestions } from './AINextPageSuggestions';
|
||||
import { AIPageJourneySuggestions } from './AIPageJourneySuggestions';
|
||||
import { useAdaptiveContext } from './AdaptiveContext';
|
||||
import { AdaptivePaneHeader } from './AdaptivePaneHeader';
|
||||
export function AdaptivePane() {
|
||||
const { open } = useAdaptiveContext();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={tcls(
|
||||
'flex flex-col gap-4 rounded-md straight-corners:rounded-none bg-tint-subtle ring-1 ring-tint-subtle ring-inset transition-all duration-300',
|
||||
open ? 'w-72 px-4 py-4' : 'w-56 px-4 py-3'
|
||||
)}
|
||||
>
|
||||
<AdaptivePaneHeader />
|
||||
<AIPageJourneySuggestions />
|
||||
<AINextPageSuggestions />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
'use client';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { Button, Loading } from '../primitives';
|
||||
import { useAdaptiveContext } from './AdaptiveContext';
|
||||
|
||||
export function AdaptivePaneHeader() {
|
||||
const { loading, open, setOpen } = useAdaptiveContext();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-3 rounded-md straight-corners:rounded-none transition-all duration-500">
|
||||
<div className="flex grow flex-col">
|
||||
<h4 className="flex items-center gap-1.5 font-semibold ">
|
||||
<Loading className="size-4 text-tint-subtle" busy={loading} />
|
||||
For you
|
||||
</h4>
|
||||
<AnimatePresence initial={false} mode="wait">
|
||||
<motion.h5
|
||||
key={loading ? 'loading' : 'loaded'}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="text-tint-subtle text-xs"
|
||||
>
|
||||
{loading ? 'Basing on your context...' : 'Based on your context'}
|
||||
</motion.h5>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<Button
|
||||
variant="blank"
|
||||
className={tcls('px-2 *:transition-transform', !open && '*:-rotate-45')}
|
||||
iconOnly
|
||||
label="Close"
|
||||
icon="close"
|
||||
onClick={() => setOpen(!open)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
export * from './AIPageLinkSummary';
|
||||
export * from './AdaptiveContext';
|
||||
export * from './AdaptivePane';
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
'use server';
|
||||
import { type AIMessageInput, AIModel, type AIStreamResponse } from '@gitbook/api';
|
||||
import {
|
||||
type AIMessageInput,
|
||||
AIModel,
|
||||
type AIStreamResponse,
|
||||
type AIToolCapabilities,
|
||||
} from '@gitbook/api';
|
||||
import type { GitBookBaseContext } from '@v2/lib/context';
|
||||
import { EventIterator } from 'event-iterator';
|
||||
import type { MaybePromise } from 'p-map';
|
||||
@@ -47,11 +52,13 @@ export async function streamGenerateObject<T>(
|
||||
schema,
|
||||
messages,
|
||||
model = AIModel.Fast,
|
||||
tools = {},
|
||||
}: {
|
||||
schema: z.ZodSchema<T>;
|
||||
messages: AIMessageInput[];
|
||||
model?: AIModel;
|
||||
previousResponseId?: string;
|
||||
tools?: AIToolCapabilities;
|
||||
}
|
||||
) {
|
||||
const rawStream = context.dataFetcher.streamAIResponse({
|
||||
@@ -62,12 +69,13 @@ export async function streamGenerateObject<T>(
|
||||
type: 'object',
|
||||
schema: zodToJsonSchema(schema),
|
||||
},
|
||||
tools,
|
||||
model,
|
||||
});
|
||||
|
||||
let json = '';
|
||||
return parseResponse<DeepPartial<T>>(rawStream, (event) => {
|
||||
if (event.type === 'response_object') {
|
||||
if (event.type === 'response_object' && event.jsonChunk) {
|
||||
json += event.jsonChunk;
|
||||
|
||||
const parsed = partialJson.parse(json, partialJson.ALL);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './streamLinkPageSummary';
|
||||
export * from './streamPageJourneySuggestions';
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
'use server';
|
||||
import { resolvePageId } from '@/lib/pages';
|
||||
import { getV1BaseContext } from '@/lib/v1';
|
||||
import { isV2 } from '@/lib/v2';
|
||||
import { AIMessageRole } from '@gitbook/api';
|
||||
import { getSiteURLDataFromMiddleware } from '@v2/lib/middleware';
|
||||
import { fetchServerActionSiteContext, getServerActionBaseContext } from '@v2/lib/server-actions';
|
||||
import { z } from 'zod';
|
||||
import { streamGenerateObject } from './api';
|
||||
|
||||
/**
|
||||
* Get a list of pages to read next
|
||||
*/
|
||||
export async function* streamNextPageSuggestions({
|
||||
currentPage,
|
||||
currentSpace,
|
||||
visitedPages,
|
||||
}: {
|
||||
currentPage: {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
currentSpace: {
|
||||
id: string;
|
||||
// title: string;
|
||||
};
|
||||
visitedPages?: Array<{ spaceId: string; pageId: string }>;
|
||||
}) {
|
||||
const baseContext = isV2() ? await getServerActionBaseContext() : await getV1BaseContext();
|
||||
const siteURLData = await getSiteURLDataFromMiddleware();
|
||||
|
||||
const [{ stream }, context] = await Promise.all([
|
||||
streamGenerateObject(
|
||||
baseContext,
|
||||
{
|
||||
organizationId: siteURLData.organization,
|
||||
siteId: siteURLData.site,
|
||||
},
|
||||
{
|
||||
schema: z.object({
|
||||
pages: z
|
||||
.array(z.string().describe('The IDs of the page to read next.'))
|
||||
.min(5)
|
||||
.max(5),
|
||||
}),
|
||||
tools: {
|
||||
getPages: true,
|
||||
getPageContent: true,
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content:
|
||||
"You are a knowledge navigator. Given the user's visited pages and the documentation's table of contents, suggest a list of pages to read next.",
|
||||
},
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The user is in space (ID ${currentSpace.id})`,
|
||||
},
|
||||
// {
|
||||
// role: AIMessageRole.Developer,
|
||||
// content: `Other spaces in the documentation are: ${allSpaces
|
||||
// .map(
|
||||
// (space) => `
|
||||
// - "${space.title}" (ID ${space.id})`
|
||||
// )
|
||||
// .join('\n')}
|
||||
|
||||
// Feel free to create journeys across spaces.`,
|
||||
// },
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The current page is: "${currentPage.title}" (ID ${currentPage.id}). You can use the getPageContent tool to get the content of any relevant links to include in the journey. Only follow links to pages.`,
|
||||
attachments: [
|
||||
{
|
||||
type: 'page' as const,
|
||||
spaceId: currentSpace.id,
|
||||
pageId: currentPage.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
...(visitedPages && visitedPages.length > 0
|
||||
? [
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The user's visited pages are: ${visitedPages.map((page) => page.pageId).join(', ')}. The content of the last 5 pages are included below.`,
|
||||
attachments: visitedPages.slice(0, 5).map((page) => ({
|
||||
type: 'page' as const,
|
||||
spaceId: page.spaceId,
|
||||
pageId: page.pageId,
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
}
|
||||
),
|
||||
fetchServerActionSiteContext(baseContext),
|
||||
]);
|
||||
|
||||
const emitted = new Set<string>();
|
||||
for await (const value of stream) {
|
||||
const pages = value.pages;
|
||||
|
||||
if (!pages) continue;
|
||||
|
||||
for (const pageId of pages) {
|
||||
if (!pageId) continue;
|
||||
if (emitted.has(pageId)) continue;
|
||||
|
||||
emitted.add(pageId);
|
||||
|
||||
const resolvedPage = resolvePageId(context.pages, pageId);
|
||||
if (!resolvedPage) continue;
|
||||
|
||||
yield {
|
||||
id: resolvedPage.page.id,
|
||||
title: resolvedPage.page.title,
|
||||
icon: resolvedPage.page.icon,
|
||||
emoji: resolvedPage.page.emoji,
|
||||
href: context.linker.toPathForPage({
|
||||
pages: context.pages,
|
||||
page: resolvedPage.page,
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
'use server';
|
||||
import { type AncestorRevisionPage, resolvePageId } from '@/lib/pages';
|
||||
import { getV1BaseContext } from '@/lib/v1';
|
||||
import { isV2 } from '@/lib/v2';
|
||||
import { AIMessageRole, type RevisionPageDocument } from '@gitbook/api';
|
||||
import { getSiteURLDataFromMiddleware } from '@v2/lib/middleware';
|
||||
import { fetchServerActionSiteContext, getServerActionBaseContext } from '@v2/lib/server-actions';
|
||||
import { z } from 'zod';
|
||||
import { streamGenerateObject } from './api';
|
||||
|
||||
/**
|
||||
* Get a summary of a page, in the context of another page
|
||||
*/
|
||||
export async function* streamPageJourneySuggestions({
|
||||
currentPage,
|
||||
currentSpace,
|
||||
allSpaces,
|
||||
visitedPages,
|
||||
count,
|
||||
}: {
|
||||
currentPage: {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
currentSpace: {
|
||||
id: string;
|
||||
// title: string;
|
||||
};
|
||||
allSpaces: {
|
||||
id: string;
|
||||
title: string;
|
||||
}[];
|
||||
visitedPages?: Array<{ spaceId: string; pageId: string }>;
|
||||
count: number;
|
||||
}) {
|
||||
const baseContext = isV2() ? await getServerActionBaseContext() : await getV1BaseContext();
|
||||
const siteURLData = await getSiteURLDataFromMiddleware();
|
||||
|
||||
const [{ stream }, context] = await Promise.all([
|
||||
streamGenerateObject(
|
||||
baseContext,
|
||||
{
|
||||
organizationId: siteURLData.organization,
|
||||
siteId: siteURLData.site,
|
||||
},
|
||||
{
|
||||
schema: z.object({
|
||||
journeys: z
|
||||
.array(
|
||||
z.object({
|
||||
label: z.string().describe('The label of the journey.'),
|
||||
icon: z
|
||||
.string()
|
||||
.describe(
|
||||
'The icon of the journey. Use an icon from FontAwesome, stripping the `fa-`. Examples: rocket-launch, tennis-ball, cat'
|
||||
),
|
||||
pages: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
)
|
||||
.describe(
|
||||
'A list of pages in the journey, excluding the current page. Try to avoid duplicate content that is very similar.'
|
||||
)
|
||||
.min(5)
|
||||
.max(10),
|
||||
})
|
||||
)
|
||||
.describe('The possible journeys to take through the documentation.')
|
||||
.min(count)
|
||||
.max(count),
|
||||
}),
|
||||
tools: {
|
||||
getPages: true,
|
||||
getPageContent: true,
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content:
|
||||
"You are a knowledge navigator. Given the user's visited pages and the documentation's table of contents, suggest a named journey through the documentation. A journey is a list of pages that are related to each other. A journey's label starts with a verb and has a clear subject. Use sentence case (so only capitalize the first letter of the first word). Be concise and use short words to fit in the label. For example, use 'docs' instead of 'documentation'. Try to pick out specific journeys, not too generic.",
|
||||
},
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The user is in space "${allSpaces.find((space) => space.id === currentSpace.id)?.title}" (ID ${currentSpace.id})`,
|
||||
},
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `Other spaces in the documentation are: ${allSpaces
|
||||
.map(
|
||||
(space) => `
|
||||
- "${space.title}" (ID ${space.id})`
|
||||
)
|
||||
.join('\n')}
|
||||
|
||||
Feel free to create journeys across spaces.`,
|
||||
},
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The current page is: "${currentPage.title}" (ID ${currentPage.id}). You can use the getPageContent tool to get the content of any relevant links to include in the journey. Only follow links to pages.`,
|
||||
attachments: [
|
||||
{
|
||||
type: 'page' as const,
|
||||
spaceId: currentSpace.id,
|
||||
pageId: currentPage.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
...(visitedPages && visitedPages.length > 0
|
||||
? [
|
||||
{
|
||||
role: AIMessageRole.Developer,
|
||||
content: `The user's visited pages are: ${visitedPages.map((page) => page.pageId).join(', ')}. The content of the last 5 pages are included below.`,
|
||||
attachments: visitedPages.slice(0, 5).map((page) => ({
|
||||
type: 'page' as const,
|
||||
spaceId: page.spaceId,
|
||||
pageId: page.pageId,
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
}
|
||||
),
|
||||
fetchServerActionSiteContext(baseContext),
|
||||
]);
|
||||
|
||||
const emitted: { label: string; pageIds: string[] }[] = [];
|
||||
const allEmittedPageIds = new Set<string>();
|
||||
|
||||
for await (const value of stream) {
|
||||
const journeys = value.journeys;
|
||||
|
||||
if (!journeys) continue;
|
||||
|
||||
for (const journey of journeys) {
|
||||
if (!journey?.label) continue;
|
||||
if (!journey?.pages || journey.pages?.length === 0) continue;
|
||||
if (emitted.find((item) => item.label === journey.label)) continue;
|
||||
|
||||
const pageIds: string[] = [];
|
||||
const resolvedPages: {
|
||||
page: RevisionPageDocument;
|
||||
ancestors: AncestorRevisionPage[];
|
||||
}[] = [];
|
||||
for (const page of journey.pages) {
|
||||
if (!page) continue;
|
||||
if (!page.id) continue;
|
||||
if (pageIds.includes(page.id)) continue;
|
||||
|
||||
pageIds.push(page.id);
|
||||
|
||||
const resolvedPage = resolvePageId(context.pages, page.id);
|
||||
if (!resolvedPage) continue;
|
||||
|
||||
resolvedPages.push(resolvedPage);
|
||||
}
|
||||
|
||||
emitted.push({
|
||||
label: journey.label,
|
||||
pageIds: pageIds,
|
||||
});
|
||||
|
||||
// Deduplicate pages before yielding
|
||||
const uniquePages = resolvedPages.filter((page) => {
|
||||
if (allEmittedPageIds.has(page.page.id)) {
|
||||
return false;
|
||||
}
|
||||
allEmittedPageIds.add(page.page.id);
|
||||
return true;
|
||||
});
|
||||
|
||||
yield {
|
||||
label: journey.label,
|
||||
icon: journey.icon,
|
||||
pages: uniquePages.map((page) => ({
|
||||
id: page.page.id,
|
||||
title: page.page.title,
|
||||
icon: page.page.icon,
|
||||
emoji: page.page.emoji,
|
||||
href: context.linker.toPathForPage({
|
||||
pages: context.pages,
|
||||
page: page.page,
|
||||
}),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import { IntegrationBlock } from './Integration';
|
||||
import { List } from './List';
|
||||
import { ListItem } from './ListItem';
|
||||
import { BlockMath } from './Math';
|
||||
import { OpenAPIOperation, OpenAPISchemas } from './OpenAPI';
|
||||
import { OpenAPIOperation, OpenAPISchemas, OpenAPIWebhook } from './OpenAPI';
|
||||
import { Paragraph } from './Paragraph';
|
||||
import { Quote } from './Quote';
|
||||
import { ReusableContent } from './ReusableContent';
|
||||
@@ -85,6 +85,8 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
|
||||
return <OpenAPIOperation {...props} block={block} />;
|
||||
case 'openapi-schemas':
|
||||
return <OpenAPISchemas {...props} block={block} />;
|
||||
case 'openapi-webhook':
|
||||
return <OpenAPIWebhook {...props} block={block} />;
|
||||
case 'embed':
|
||||
return <Embed {...props} block={block} />;
|
||||
case 'blockquote':
|
||||
@@ -159,6 +161,7 @@ export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }
|
||||
case 'swagger':
|
||||
case 'openapi-operation':
|
||||
case 'openapi-schemas':
|
||||
case 'openapi-webhook':
|
||||
case 'math':
|
||||
case 'divider':
|
||||
case 'content-ref':
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
.highlight-line-number {
|
||||
@apply text-sm text-right pr-3.5 rounded-l pl-2 sticky left-[-3px] bg-gradient-to-r from-80% from-tint to-transparent;
|
||||
@apply text-sm text-right pr-3.5 rounded-l pl-2 sticky left-[-3px] bg-gradient-to-r from-80% from-tint-subtle contrast-more:from-tint-base theme-muted:from-tint-base [html.theme-bold.sidebar-filled_&]:from-tint-base to-transparent;
|
||||
@apply before:text-tint before:content-[counter(line)];
|
||||
|
||||
.highlight-line.highlighted > & {
|
||||
|
||||
@@ -39,7 +39,7 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2 text-sm [grid-area:1/1]">
|
||||
{title ? (
|
||||
<div className="inline-flex items-center justify-center rounded-t straight-corners:rounded-t-s bg-tint px-3 py-2 text-tint text-xs leading-none tracking-wide">
|
||||
<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}
|
||||
@@ -50,8 +50,8 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
|
||||
/>
|
||||
<pre
|
||||
className={tcls(
|
||||
'hide-scroll relative overflow-auto bg-tint theme-gradient:bg-tint-12/1 ring-tint-subtle [grid-area:2/1]',
|
||||
'rounded-md straight-corners:rounded-sm',
|
||||
'hide-scroll relative overflow-auto border border-tint-subtle bg-tint-subtle theme-muted:bg-tint-base [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-sm shadow-sm',
|
||||
title && 'rounded-ss-none'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,31 +1,67 @@
|
||||
:root {
|
||||
--shiki-color-text: theme("colors.tint.11");
|
||||
--shiki-token-constant: #0a6355;
|
||||
--shiki-token-string: #8b6d32;
|
||||
--shiki-token-comment: theme("colors.teal.700/.64");
|
||||
--shiki-token-keyword: theme("colors.pomegranate.600");
|
||||
--shiki-token-parameter: #0a3069;
|
||||
--shiki-token-function: #8250df;
|
||||
--shiki-token-string-expression: #6a4906;
|
||||
--shiki-token-punctuation: theme("colors.pomegranate.700/.92");
|
||||
--shiki-token-link: theme("colors.tint.12");
|
||||
--shiki-token-inserted: #22863a;
|
||||
--shiki-token-deleted: #b31d28;
|
||||
--shiki-token-changed: #8250df;
|
||||
--shiki-token-punctuation: theme("colors.tint.11");
|
||||
--shiki-token-comment: theme("colors.neutral.9/.7");
|
||||
--shiki-token-link: theme("colors.primary.10");
|
||||
|
||||
--shiki-token-constant: theme("colors.warning.10");
|
||||
--shiki-token-string: theme("colors.success.10");
|
||||
--shiki-token-string-expression: theme("colors.success.10");
|
||||
--shiki-token-keyword: theme("colors.danger.10");
|
||||
--shiki-token-parameter: theme("colors.warning.10");
|
||||
--shiki-token-function: theme("colors.primary.10");
|
||||
|
||||
--shiki-token-inserted: theme("colors.success.10");
|
||||
--shiki-token-deleted: theme("colors.danger.10");
|
||||
--shiki-token-changed: theme("colors.tint.12");
|
||||
}
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
:root {
|
||||
--shiki-color-text: theme("colors.tint.12");
|
||||
--shiki-token-punctuation: theme("colors.tint.12");
|
||||
--shiki-token-comment: theme("colors.neutral.11");
|
||||
--shiki-token-link: theme("colors.primary.11");
|
||||
|
||||
--shiki-token-constant: theme("colors.warning.11");
|
||||
--shiki-token-string: theme("colors.success.11");
|
||||
--shiki-token-string-expression: theme("colors.success.11");
|
||||
--shiki-token-keyword: theme("colors.danger.11");
|
||||
--shiki-token-parameter: theme("colors.warning.11");
|
||||
--shiki-token-function: theme("colors.primary.11");
|
||||
|
||||
--shiki-token-inserted: theme("colors.success.11");
|
||||
--shiki-token-deleted: theme("colors.danger.11");
|
||||
--shiki-token-changed: theme("colors.tint.12");
|
||||
}
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--shiki-color-text: theme("colors.tint.11");
|
||||
--shiki-token-constant: #d19a66;
|
||||
--shiki-token-string: theme("colors.pomegranate.300");
|
||||
--shiki-token-comment: theme("colors.teal.300/.64");
|
||||
--shiki-token-keyword: theme("colors.pomegranate.400");
|
||||
--shiki-token-parameter: theme("colors.yellow.500");
|
||||
--shiki-token-function: #56b6c2;
|
||||
--shiki-token-string-expression: theme("colors.tint.11");
|
||||
--shiki-token-punctuation: #acc6ee;
|
||||
--shiki-token-link: theme("colors.pomegranate.400");
|
||||
--shiki-token-inserted: #85e89d;
|
||||
--shiki-token-deleted: #fdaeb7;
|
||||
--shiki-token-changed: #56b6c2;
|
||||
/* Override select colors to have more contrast */
|
||||
--shiki-token-comment: theme("colors.neutral.9");
|
||||
|
||||
--shiki-token-constant: theme("colors.warning.11");
|
||||
--shiki-token-string: theme("colors.success.11");
|
||||
--shiki-token-string-expression: theme("colors.success.11");
|
||||
--shiki-token-keyword: theme("colors.danger.11");
|
||||
--shiki-token-parameter: theme("colors.warning.11");
|
||||
--shiki-token-function: theme("colors.primary.11");
|
||||
}
|
||||
|
||||
.code-monochrome {
|
||||
--shiki-token-constant: theme("colors.tint.11");
|
||||
--shiki-token-string: theme("colors.tint.12");
|
||||
--shiki-token-string-expression: theme("colors.tint.12");
|
||||
--shiki-token-keyword: theme("colors.primary.10");
|
||||
--shiki-token-parameter: theme("colors.tint.9");
|
||||
--shiki-token-function: theme("colors.primary.9");
|
||||
}
|
||||
|
||||
html.dark.code-monochrome {
|
||||
--shiki-token-constant: theme("colors.tint.11");
|
||||
--shiki-token-string: theme("colors.tint.12");
|
||||
--shiki-token-string-expression: theme("colors.tint.12");
|
||||
--shiki-token-keyword: theme("colors.primary.11");
|
||||
--shiki-token-parameter: theme("colors.tint.10");
|
||||
--shiki-token-function: theme("colors.primary.10");
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ export const contentKitServerContext: ContentKitServerContext = {
|
||||
'link-external': (props) => <Icon icon="arrow-up-right-from-square" {...props} />,
|
||||
eye: (props) => <Icon icon="eye" {...props} />,
|
||||
lock: (props) => <Icon icon="lock" {...props} />,
|
||||
check: (props) => <Icon icon="check" {...props} />,
|
||||
'check-circle': (props) => <Icon icon="check-circle" {...props} />,
|
||||
},
|
||||
codeBlock: (props) => {
|
||||
return <PlainCodeBlock code={props.code} syntax={props.syntax} />;
|
||||
|
||||
@@ -51,7 +51,7 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
|
||||
return (
|
||||
<BaseOpenAPIOperation
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl })}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
|
||||
<BaseOpenAPISchemas
|
||||
schemas={data.schemas}
|
||||
grouped={block.data.grouped}
|
||||
context={getOpenAPIContext({ props, specUrl })}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { OpenAPIWebhook as BaseOpenAPIWebhook } from '@gitbook/react-openapi';
|
||||
|
||||
import { resolveOpenAPIWebhookBlock } from '@/lib/openapi/resolveOpenAPIWebhookBlock';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { BlockProps } from '../Block';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { OpenAPIWebhookBlock } from '@/lib/openapi/types';
|
||||
import { getOpenAPIContext } from './context';
|
||||
|
||||
/**
|
||||
* Render an openapi block or an openapi-webhook block.
|
||||
*/
|
||||
export async function OpenAPIWebhook(props: BlockProps<OpenAPIWebhookBlock>) {
|
||||
const { style } = props;
|
||||
return (
|
||||
<div className={tcls('flex w-full min-w-0', style, 'max-w-full')}>
|
||||
<OpenAPIWebhookBody {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
|
||||
const { block, context } = props;
|
||||
|
||||
if (!context.contentContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, specUrl, error } = await resolveOpenAPIWebhookBlock({
|
||||
block,
|
||||
context: context.contentContext,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="hidden">
|
||||
<p>
|
||||
Error with {specUrl}: {error.message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data || !specUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseOpenAPIWebhook
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { JSONDocument } from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { OpenAPIContext } from '@gitbook/react-openapi';
|
||||
import { type OpenAPIContextInput, checkIsValidLocale } from '@gitbook/react-openapi';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
@@ -11,17 +11,31 @@ import { Heading } from '../Heading';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import type { AnyOpenAPIOperationsBlock, OpenAPISchemasBlock } from '@/lib/openapi/types';
|
||||
import { DEFAULT_LOCALE, getCustomizationLocale } from '@/intl/server';
|
||||
import type {
|
||||
AnyOpenAPIOperationsBlock,
|
||||
OpenAPISchemasBlock,
|
||||
OpenAPIWebhookBlock,
|
||||
} from '@/lib/openapi/types';
|
||||
import type { GitBookAnyContext } from '@v2/lib/context';
|
||||
|
||||
/**
|
||||
* Get the OpenAPI context to render a block.
|
||||
*/
|
||||
export function getOpenAPIContext(args: {
|
||||
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock>;
|
||||
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
|
||||
specUrl: string;
|
||||
}): OpenAPIContext {
|
||||
const { props, specUrl } = args;
|
||||
context: GitBookAnyContext | undefined;
|
||||
}): OpenAPIContextInput {
|
||||
const { props, specUrl, context } = args;
|
||||
const { block } = props;
|
||||
|
||||
const customization = context && 'customization' in context ? context.customization : null;
|
||||
const customizationLocale = customization
|
||||
? getCustomizationLocale(customization)
|
||||
: DEFAULT_LOCALE;
|
||||
const locale = checkIsValidLocale(customizationLocale) ? customizationLocale : DEFAULT_LOCALE;
|
||||
|
||||
return {
|
||||
specUrl,
|
||||
icons: {
|
||||
@@ -69,5 +83,6 @@ export function getOpenAPIContext(args: {
|
||||
defaultInteractiveOpened: props.context.mode === 'print',
|
||||
id: block.meta?.id,
|
||||
blockKey: block.key,
|
||||
locale,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './OpenAPIOperation';
|
||||
export * from './OpenAPISchemas';
|
||||
export * from './OpenAPIWebhook';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* Layout Components */
|
||||
.openapi-operation,
|
||||
.openapi-schemas {
|
||||
.openapi-schemas,
|
||||
.openapi-webhook {
|
||||
@apply flex-1 flex flex-col gap-8 mb-14 min-w-0;
|
||||
}
|
||||
|
||||
@@ -168,41 +169,18 @@
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
.openapi-schema {
|
||||
.openapi-schema,
|
||||
.openapi-disclosure {
|
||||
@apply py-2.5 flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.openapi-section-body .openapi-schema-properties {
|
||||
@apply divide-y divide-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-panel > .openapi-schema-properties > *:first-child > .openapi-schema {
|
||||
@apply pt-0;
|
||||
}
|
||||
|
||||
.openapi-responsebody > .openapi-schema-properties > .openapi-schema:last-child {
|
||||
@apply pb-0;
|
||||
}
|
||||
|
||||
.openapi-responsebody > .openapi-schema-properties > .openapi-schema:only-child {
|
||||
@apply py-0;
|
||||
}
|
||||
|
||||
.openapi-schema-properties .openapi-schema:last-child {
|
||||
@apply border-b-0;
|
||||
}
|
||||
|
||||
.openapi-schema-properties .openapi-schema-opened {
|
||||
@apply pb-3;
|
||||
}
|
||||
|
||||
.openapi-schema > .openapi-schema-properties {
|
||||
@apply mt-3;
|
||||
}
|
||||
|
||||
/* Schema Presentation */
|
||||
.openapi-schema-presentation {
|
||||
@apply flex flex-col gap-1.5 font-normal;
|
||||
@apply flex flex-col gap-1 font-normal;
|
||||
}
|
||||
|
||||
.openapi-schema-properties:last-child {
|
||||
@@ -220,7 +198,7 @@
|
||||
}
|
||||
|
||||
.openapi-schema-propertyname {
|
||||
@apply select-all font-mono font-normal text-tint-strong;
|
||||
@apply select-all font-mono font-semibold text-tint-strong;
|
||||
}
|
||||
|
||||
.openapi-schema-propertyname[data-deprecated="true"] {
|
||||
@@ -228,19 +206,23 @@
|
||||
}
|
||||
|
||||
.openapi-schema-required {
|
||||
@apply text-warning-subtle text-[0.813rem];
|
||||
@apply text-warning-subtle text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-optional {
|
||||
@apply text-info-subtle text-[0.813rem];
|
||||
@apply text-tint-subtle text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-readonly {
|
||||
@apply text-primary-subtle/9 text-[0.813rem];
|
||||
@apply text-primary-subtle/9 text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-writeonly {
|
||||
@apply text-success dark:text-success-subtle/9 text-[0.813rem];
|
||||
@apply text-success dark:text-success-subtle/9 text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-types {
|
||||
@apply flex items-baseline flex-wrap gap-1;
|
||||
}
|
||||
|
||||
.openapi-schema-type {
|
||||
@@ -320,7 +302,7 @@
|
||||
.openapi-schema-pattern code,
|
||||
.openapi-schema-enum-value code,
|
||||
.openapi-schema-default code {
|
||||
@apply py-px px-1 min-w-[1.625rem] text-tint-strong font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-xs leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none;
|
||||
@apply py-px px-1 min-w-[1.625rem] text-tint-strong font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint-subtle bg-tint rounded text-xs leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none;
|
||||
}
|
||||
|
||||
/* Authentication */
|
||||
@@ -372,28 +354,25 @@
|
||||
}
|
||||
|
||||
.openapi-response-tab-content {
|
||||
@apply overflow-hidden max-w-full flex items-baseline;
|
||||
@apply text-left text-pretty relative leading-[1.125rem] text-tint !font-normal truncate select-text;
|
||||
@apply flex items-baseline truncate grow shrink max-w-max basis-[60%] mr-auto;
|
||||
@apply text-left text-pretty relative leading-tight text-tint select-text;
|
||||
}
|
||||
|
||||
.openapi-response-description.openapi-markdown {
|
||||
@apply text-left prose-sm text-[0.813rem] text-pretty h-auto relative leading-[1.125rem] text-tint !font-normal truncate select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
@apply text-left truncate prose-sm text-sm leading-tight text-tint select-text prose-strong:font-semibold prose-strong:text-inherit;
|
||||
}
|
||||
|
||||
.openapi-response-description.openapi-markdown::-webkit-scrollbar {
|
||||
display: none;
|
||||
.openapi-disclosure-group-trigger[aria-expanded="true"] .openapi-response-tab-content {
|
||||
@apply basis-full;
|
||||
}
|
||||
|
||||
.openapi-response-description p {
|
||||
@apply truncate max-w-full inline pr-1;
|
||||
}
|
||||
|
||||
.openapi-response-content-type {
|
||||
@apply text-xs text-tint-8 ml-auto shrink-0;
|
||||
.openapi-disclosure-group-trigger[aria-expanded="true"]
|
||||
.openapi-response-description.openapi-markdown {
|
||||
@apply whitespace-normal;
|
||||
}
|
||||
|
||||
.openapi-response-body {
|
||||
@apply flex flex-col gap-3;
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
/* Response Body and Headers */
|
||||
@@ -410,16 +389,7 @@
|
||||
@apply px-3 py-1;
|
||||
}
|
||||
|
||||
.openapi-responsebody-header-content,
|
||||
.openapi-responseheaders-header-content {
|
||||
/* unstyled */
|
||||
}
|
||||
|
||||
/* Code Sample */
|
||||
.openapi-codesample {
|
||||
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-codesample-header {
|
||||
@apply flex flex-row items-center;
|
||||
}
|
||||
@@ -428,6 +398,12 @@
|
||||
@apply flex flex-row items-center gap-2.5;
|
||||
}
|
||||
|
||||
.openapi-panel-heading,
|
||||
.openapi-codesample-header,
|
||||
.openapi-response-examples-header {
|
||||
@apply border-b border-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-response-examples-header .openapi-select > button {
|
||||
@apply max-w-full overflow-hidden shrink pl-0.5 py-0.5;
|
||||
}
|
||||
@@ -502,8 +478,16 @@
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
.openapi-panel {
|
||||
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
|
||||
.openapi-panel,
|
||||
.openapi-codesample,
|
||||
.openapi-response-examples {
|
||||
@apply border rounded-md straight-corners:rounded-none bg-tint-subtle border-tint-subtle shadow-sm;
|
||||
}
|
||||
|
||||
.openapi-panel pre,
|
||||
.openapi-codesample pre,
|
||||
.openapi-response-examples pre {
|
||||
@apply bg-transparent border-none rounded-none shadow-none;
|
||||
}
|
||||
|
||||
.openapi-panel-heading {
|
||||
@@ -512,12 +496,11 @@
|
||||
|
||||
.openapi-panel-body {
|
||||
@apply relative;
|
||||
@apply before:w-full before:h-px before:absolute before:bg-tint-6 before:-top-px before:z-10;
|
||||
}
|
||||
|
||||
.openapi-panel-footer,
|
||||
.openapi-codesample-footer {
|
||||
@apply px-3 py-2 pt-2.5 border-t border-tint-subtle text-[0.813rem] text-tint;
|
||||
@apply px-3 py-2 pt-2.5 border-t border-tint-subtle text-[0.813rem] text-tint empty:hidden;
|
||||
}
|
||||
|
||||
.openapi-panel-footer .openapi-markdown {
|
||||
@@ -525,10 +508,6 @@
|
||||
}
|
||||
|
||||
/* Example */
|
||||
.openapi-response-examples {
|
||||
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-response-examples-header {
|
||||
@apply flex flex-row items-center p-2.5;
|
||||
}
|
||||
@@ -550,7 +529,6 @@
|
||||
.openapi-response-examples-panel,
|
||||
.openapi-codesample-panel {
|
||||
@apply flex-1 text-sm relative focus-visible:outline-none;
|
||||
@apply before:w-full before:h-px before:absolute before:bg-tint-6 before:-top-px before:z-10;
|
||||
}
|
||||
|
||||
.openapi-example-empty {
|
||||
@@ -702,29 +680,33 @@ body:has(.openapi-select-popover) {
|
||||
|
||||
/* Disclosure group */
|
||||
.openapi-disclosure-group {
|
||||
@apply border-tint-subtle border-b border-x overflow-auto last:rounded-b-md straight-corners:last:rounded-none first:rounded-t-md straight-corners:first:rounded-none first:border-t relative;
|
||||
@apply border-tint-subtle transition-all border-b border-x overflow-auto last:rounded-b-md straight-corners:last:rounded-none first:rounded-t-md straight-corners:first:rounded-none first:border-t relative;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-header {
|
||||
@apply flex flex-row items-baseline justify-between gap-3 relative;
|
||||
.openapi-disclosure-group:has(.openapi-disclosure-group-trigger:hover) {
|
||||
@apply bg-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group:has(.openapi-disclosure-group-trigger:hover):has(.openapi-select:hover) {
|
||||
@apply !bg-transparent;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger {
|
||||
@apply flex items-baseline transition-all hover:bg-tint-subtle relative flex-1 gap-2.5 p-3 truncate -outline-offset-1;
|
||||
@apply flex w-full cursor-pointer items-baseline gap-3 transition-all relative flex-1 p-3 -outline-offset-1;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger:disabled {
|
||||
.openapi-disclosure-group-label {
|
||||
@apply flex flex-wrap items-baseline gap-x-3 gap-y-1 flex-1 truncate;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger[aria-disabled="true"] {
|
||||
@apply cursor-default hover:bg-inherit;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger:disabled .openapi-disclosure-group-icon {
|
||||
.openapi-disclosure-group-trigger[aria-disabled="true"] .openapi-disclosure-group-icon {
|
||||
@apply invisible;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger[aria-expanded="true"] .openapi-response-description {
|
||||
@apply whitespace-normal;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-icon > svg {
|
||||
@apply size-3 text-tint-subtle transition-all duration-300;
|
||||
}
|
||||
@@ -734,29 +716,20 @@ body:has(.openapi-select-popover) {
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-panel {
|
||||
@apply p-3 pt-1 transition-all;
|
||||
@apply px-3 transition-all;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-trigger[aria-expanded="true"] > .openapi-disclosure-group-icon > svg {
|
||||
@apply rotate-90;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group:hover .openapi-disclosure-group-mediatype,
|
||||
.openapi-disclosure-group-mediatype:has(> .openapi-select[data-open="true"]) {
|
||||
@apply opacity-11 visible flex;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-mediatype {
|
||||
@apply opacity-0 invisible text-xs transition-opacity duration-300 shrink-0 absolute right-2.5 top-2.5;
|
||||
}
|
||||
|
||||
.openapi-disclosure-group-mediatype > span {
|
||||
@apply px-1 bg-tint-6 text-tint-12 rounded-full straight-corners:rounded-md;
|
||||
.openapi-disclosure-group-mediatype:not(:has(.openapi-select)) {
|
||||
@apply text-[0.625rem] font-mono shrink-0 grow-0 text-tint-subtle contrast-more:text-tint;
|
||||
}
|
||||
|
||||
/* Disclosure */
|
||||
.openapi-schemas-disclosure > .openapi-disclosure-trigger {
|
||||
@apply flex items-center font-mono !w-full transition-all text-tint-strong !text-sm hover:bg-tint-subtle relative flex-1 gap-2.5 p-3 truncate -outline-offset-1;
|
||||
@apply flex items-center font-mono transition-all text-tint-strong !text-sm hover:bg-tint-subtle relative flex-1 gap-2.5 p-3 truncate -outline-offset-1;
|
||||
}
|
||||
|
||||
.openapi-schemas-disclosure > .openapi-disclosure-trigger,
|
||||
@@ -764,51 +737,97 @@ body:has(.openapi-select-popover) {
|
||||
@apply straight-corners:!rounded-none;
|
||||
}
|
||||
|
||||
.openapi-disclosure-panel {
|
||||
@apply ml-1.5 pl-3 border-l border-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-schema .openapi-schema-properties .openapi-schema {
|
||||
@apply animate-fadeIn [animation-fill-mode:both];
|
||||
}
|
||||
|
||||
.openapi-schemas-disclosure > .openapi-disclosure-trigger[aria-expanded="true"] > svg {
|
||||
@apply rotate-90;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger {
|
||||
@apply transition-all truncate duration-300 max-w-full hover:text-tint-strong rounded-2xl straight-corners:rounded border border-tint-subtle px-2.5 py-1 text-[0.813rem] text-tint flex flex-row items-center gap-1.5 -outline-offset-1;
|
||||
@apply flex flex-row justify-between flex-wrap relative items-start gap-2 text-left -mx-3 px-3 -my-2.5 py-2.5 pr-10;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger span {
|
||||
@apply truncate;
|
||||
.openapi-disclosure {
|
||||
@apply -mx-3 px-3 py-2.5 transition-all flex flex-col ring-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-disclosure svg {
|
||||
@apply size-3 shrink-0 transition-transform duration-300;
|
||||
.openapi-disclosure:not(
|
||||
.openapi-disclosure-group .openapi-disclosure,
|
||||
.openapi-schema-alternatives .openapi-disclosure,
|
||||
.openapi-schemas-disclosure .openapi-schema.openapi-disclosure
|
||||
) {
|
||||
@apply rounded-xl;
|
||||
}
|
||||
|
||||
.openapi-disclosure .openapi-schemas-disclosure .openapi-schema.openapi-disclosure {
|
||||
@apply !rounded-none;
|
||||
}
|
||||
|
||||
.openapi-disclosure:has(> .openapi-disclosure-trigger:hover) {
|
||||
@apply bg-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-disclosure:has(> .openapi-disclosure-trigger:hover),
|
||||
.openapi-disclosure[data-expanded="true"] {
|
||||
@apply ring-1 shadow-sm;
|
||||
}
|
||||
|
||||
.openapi-disclosure[data-expanded="true"]:not(:first-child) {
|
||||
@apply mt-2;
|
||||
}
|
||||
.openapi-disclosure[data-expanded="true"]:not(:last-child) {
|
||||
@apply mb-2;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger-label {
|
||||
@apply absolute right-3 px-2 h-5 justify-end shrink-0 ring-tint-subtle truncate text-tint duration-300 transition-all rounded straight-corners:rounded-none flex flex-row gap-1 items-center text-xs;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger-label span {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger-label svg {
|
||||
@apply size-3 shrink-0 transition-transform duration-300 text-tint-subtle;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger:hover > .openapi-disclosure-trigger-label,
|
||||
.openapi-disclosure-trigger[aria-expanded="true"] > .openapi-disclosure-trigger-label {
|
||||
@apply shadow ring-1 bg-tint-base;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger:hover > .openapi-disclosure-trigger-label span,
|
||||
.openapi-disclosure-trigger[aria-expanded="true"] > .openapi-disclosure-trigger-label span {
|
||||
@apply block animate-fadeIn;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
/* Make button label always visible on non-hover devices like phones */
|
||||
.openapi-disclosure-trigger-label {
|
||||
@apply relative ring-1 bg-tint-base right-0;
|
||||
}
|
||||
.openapi-disclosure-trigger-label span {
|
||||
@apply block;
|
||||
}
|
||||
.openapi-disclosure-trigger {
|
||||
@apply pr-3;
|
||||
}
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger[aria-expanded="true"] svg {
|
||||
@apply rotate-45;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger[aria-expanded="true"] {
|
||||
@apply w-full rounded-lg border-b rounded-b-none straight-corners:rounded-b-none;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger[aria-expanded="false"] {
|
||||
@apply w-auto;
|
||||
}
|
||||
|
||||
.openapi-disclosure-panel[aria-hidden="false"] {
|
||||
@apply border-b border-x border-tint-subtle rounded-b-lg straight-corners:rounded-b;
|
||||
}
|
||||
|
||||
.openapi-disclosure-panel .openapi-schema {
|
||||
@apply p-2.5;
|
||||
}
|
||||
|
||||
.openapi-disclosure .openapi-schema-properties .openapi-schema:only-child,
|
||||
.openapi-disclosure .openapi-schema-properties .openapi-schema:only-child .openapi-schema-name {
|
||||
@apply !m-0;
|
||||
}
|
||||
|
||||
.openapi-disclosure .openapi-schema-properties .openapi-schema-enum {
|
||||
@apply pt-0 mt-0;
|
||||
}
|
||||
|
||||
.openapi-section-body.openapi-schema.openapi-schema-root {
|
||||
@apply space-y-2.5;
|
||||
}
|
||||
@@ -818,6 +837,16 @@ body:has(.openapi-select-popover) {
|
||||
@apply p-2.5;
|
||||
}
|
||||
|
||||
.openapi-schema-alternatives {
|
||||
@apply ml-1.5 pl-3 border-l border-tint-subtle;
|
||||
}
|
||||
.openapi-schema-alternative {
|
||||
@apply relative;
|
||||
}
|
||||
.openapi-schema-alternative-separator {
|
||||
@apply p-0.5 tracking-wide leading-none uppercase text-[0.625rem] text-tint-subtle whitespace-nowrap absolute -left-3 -bottom-2.5 -translate-x-1/2 z-10 bg-tint-base border-y border-tint-subtle -rotate-6;
|
||||
}
|
||||
|
||||
.openapi-tooltip {
|
||||
@apply flex items-center gap-1 bg-tint-base border border-tint-subtle text-tint-strong rounded-md straight-corners:rounded-none font-medium px-1.5 py-0.5 shadow-sm text-[13px];
|
||||
}
|
||||
@@ -864,11 +893,11 @@ body:has(.openapi-select-popover) {
|
||||
}
|
||||
|
||||
@keyframes popover-leave {
|
||||
0% {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
100% {
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(4px) scale(0.95);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { DocumentBlockReusableContent } from '@gitbook/api';
|
||||
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
|
||||
import type { GitBookSpaceContext } from '@v2/lib/context';
|
||||
import { getDataOrNull } from '@v2/lib/data';
|
||||
import type { BlockProps } from './Block';
|
||||
import { UnwrappedBlocks } from './Blocks';
|
||||
@@ -13,15 +14,28 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
||||
throw new Error('Expected a content context to render a reusable content block');
|
||||
}
|
||||
|
||||
const resolved = await resolveContentRef(block.data.ref, context.contentContext);
|
||||
if (!resolved?.reusableContent?.document) {
|
||||
const dataFetcher = block.meta?.token
|
||||
? context.contentContext.dataFetcher.withToken({ apiToken: block.meta.token })
|
||||
: context.contentContext.dataFetcher;
|
||||
|
||||
const resolved = await resolveContentRef(block.data.ref, {
|
||||
...context.contentContext,
|
||||
dataFetcher,
|
||||
});
|
||||
|
||||
if (!resolved?.reusableContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const reusableContent = resolved.reusableContent.revisionReusableContent;
|
||||
if (!reusableContent.document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const document = await getDataOrNull(
|
||||
context.contentContext.dataFetcher.getDocument({
|
||||
spaceId: context.contentContext.space.id,
|
||||
documentId: resolved.reusableContent.document,
|
||||
dataFetcher.getDocument({
|
||||
spaceId: resolved.reusableContent.space.id,
|
||||
documentId: reusableContent.document,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -29,12 +43,27 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new context for reusable content block, including
|
||||
// the data fetcher with the token from the block meta and the correct
|
||||
// space and revision pointers.
|
||||
const reusableContentContext: GitBookSpaceContext = {
|
||||
...context.contentContext,
|
||||
dataFetcher,
|
||||
space: resolved.reusableContent.space,
|
||||
revisionId: resolved.reusableContent.revision,
|
||||
pages: [],
|
||||
shareKey: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<UnwrappedBlocks
|
||||
nodes={document.nodes}
|
||||
document={document}
|
||||
ancestorBlocks={[...ancestorBlocks, block]}
|
||||
context={context}
|
||||
context={{
|
||||
...context,
|
||||
contentContext: reusableContentContext,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
return <Tag className={tcls(['w-full', verticalAlignment])}>{''}</Tag>;
|
||||
}
|
||||
|
||||
const alignment = getColumnAlignment(definition);
|
||||
const horizontalAlignment = `[&_*]:${getColumnAlignment(definition)} ${getColumnAlignment(definition)}`;
|
||||
|
||||
return (
|
||||
<Blocks
|
||||
@@ -130,8 +130,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
||||
'lg:space-y-3',
|
||||
'leading-normal',
|
||||
verticalAlignment,
|
||||
alignment === 'right' ? 'text-right' : null,
|
||||
alignment === 'center' ? 'text-center' : null,
|
||||
horizontalAlignment,
|
||||
]}
|
||||
context={context}
|
||||
blockStyle={['w-full', 'max-w-[unset]']}
|
||||
|
||||
@@ -42,32 +42,28 @@ export function ViewGrid(props: TableViewProps<DocumentTableViewGrid>) {
|
||||
)}
|
||||
>
|
||||
<div role="row" className={tcls('flex', 'w-full')}>
|
||||
{view.columns.map((column) => {
|
||||
const alignment = getColumnAlignment(block.data.definition[column]);
|
||||
return (
|
||||
<div
|
||||
key={column}
|
||||
role="columnheader"
|
||||
className={tcls(
|
||||
styles.columnHeader,
|
||||
alignment === 'right' ? 'text-right' : null,
|
||||
alignment === 'center' ? 'text-center' : null
|
||||
)}
|
||||
style={{
|
||||
width: getColumnWidth({
|
||||
column,
|
||||
columnWidths,
|
||||
autoSizedColumns,
|
||||
fixedColumns,
|
||||
}),
|
||||
minWidth: columnWidths?.[column] || '100px',
|
||||
}}
|
||||
title={block.data.definition[column].title}
|
||||
>
|
||||
{block.data.definition[column].title}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{view.columns.map((column) => (
|
||||
<div
|
||||
key={column}
|
||||
role="columnheader"
|
||||
className={tcls(
|
||||
styles.columnHeader,
|
||||
getColumnAlignment(block.data.definition[column])
|
||||
)}
|
||||
style={{
|
||||
width: getColumnWidth({
|
||||
column,
|
||||
columnWidths,
|
||||
autoSizedColumns,
|
||||
fixedColumns,
|
||||
}),
|
||||
minWidth: columnWidths?.[column] || '100px',
|
||||
}}
|
||||
title={block.data.definition[column].title}
|
||||
>
|
||||
{block.data.definition[column].title}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
.columnHeader {
|
||||
@apply text-sm font-medium py-2 px-4 text-tint-strong;
|
||||
@apply text-sm font-medium py-2 px-3 text-tint-strong;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ContentRef, DocumentTableDefinition, DocumentTableRecord } from '@gitbook/api';
|
||||
import assertNever from 'assert-never';
|
||||
|
||||
/**
|
||||
* Get the value for a column in a record.
|
||||
@@ -14,11 +15,24 @@ export function getRecordValue<T extends number | string | boolean | string[] |
|
||||
/**
|
||||
* Get the text alignment for a column.
|
||||
*/
|
||||
export function getColumnAlignment(column: DocumentTableDefinition): 'left' | 'right' | 'center' {
|
||||
export function getColumnAlignment(column: DocumentTableDefinition) {
|
||||
const defaultAlignment = 'text-left';
|
||||
|
||||
if (column.type === 'text') {
|
||||
return column.textAlignment ?? 'left';
|
||||
switch (column.textAlignment) {
|
||||
case undefined:
|
||||
case 'left':
|
||||
return defaultAlignment;
|
||||
case 'center':
|
||||
return 'text-center';
|
||||
case 'right':
|
||||
return 'text-right';
|
||||
default:
|
||||
assertNever(column.textAlignment);
|
||||
}
|
||||
}
|
||||
return 'left';
|
||||
|
||||
return defaultAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { type DetailedHTMLProps, type HTMLAttributes, useId } from 'react';
|
||||
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Link, type LinkInsightsProps } from '../primitives';
|
||||
|
||||
export type DropdownButtonProps<E extends HTMLElement = HTMLElement> = Omit<
|
||||
Partial<DetailedHTMLProps<HTMLAttributes<E>, E>>,
|
||||
'ref'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Button with a dropdown.
|
||||
*/
|
||||
export function Dropdown<E extends HTMLElement>(props: {
|
||||
/** Content of the button */
|
||||
button: (buttonProps: DropdownButtonProps<E>) => React.ReactNode;
|
||||
/** Content of the dropdown */
|
||||
children: React.ReactNode;
|
||||
/** Custom styles */
|
||||
className?: ClassValue;
|
||||
}) {
|
||||
const { button, children, className } = props;
|
||||
const dropdownId = useId();
|
||||
|
||||
return (
|
||||
<div className={tcls('group/dropdown', 'relative flex min-w-0 shrink')}>
|
||||
{button({
|
||||
id: dropdownId,
|
||||
tabIndex: 0,
|
||||
'aria-expanded': true,
|
||||
'aria-haspopup': true,
|
||||
})}
|
||||
<div
|
||||
tabIndex={-1}
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby={dropdownId}
|
||||
className={tcls(
|
||||
'w-52',
|
||||
'max-h-80',
|
||||
'flex',
|
||||
'absolute',
|
||||
'top-full',
|
||||
'left-0',
|
||||
'origin-top-left',
|
||||
'invisible',
|
||||
'transition-opacity',
|
||||
'duration-1000',
|
||||
'group-hover/dropdown:visible',
|
||||
'group-focus-within/dropdown:visible',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="fixed z-50 w-52">
|
||||
<div
|
||||
className={tcls(
|
||||
'mt-2',
|
||||
'w-full',
|
||||
'max-h-80',
|
||||
'bg-tint-base',
|
||||
'rounded-lg',
|
||||
'straight-corners:rounded-sm',
|
||||
'p-2',
|
||||
'shadow-1xs',
|
||||
'overflow-auto',
|
||||
'ring-1',
|
||||
'ring-tint-subtle',
|
||||
'focus:outline-none'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animated chevron to display in the dropdown button.
|
||||
*/
|
||||
export function DropdownChevron() {
|
||||
return (
|
||||
<Icon
|
||||
icon="chevron-down"
|
||||
className={tcls(
|
||||
'shrink-0',
|
||||
'opacity-6',
|
||||
'size-3',
|
||||
'ms-1',
|
||||
'transition-all',
|
||||
'group-hover/dropdown:opacity-11',
|
||||
'group-focus-within/dropdown:rotate-180'
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group of menu items in a dropdown.
|
||||
*/
|
||||
export function DropdownMenu(props: { children: React.ReactNode }) {
|
||||
const { children } = props;
|
||||
|
||||
return <div className={tcls('flex', 'flex-col', 'gap-1')}>{children}</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item in a dropdown.
|
||||
*/
|
||||
export function DropdownMenuItem(
|
||||
props: {
|
||||
href: string | null;
|
||||
active?: boolean;
|
||||
className?: ClassValue;
|
||||
children: React.ReactNode;
|
||||
} & LinkInsightsProps
|
||||
) {
|
||||
const { children, active = false, href, className, insights } = props;
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
insights={insights}
|
||||
className={tcls(
|
||||
'rounded straight-corners:rounded-sm px-3 py-1 text-sm',
|
||||
active ? 'bg-primary text-primary-strong' : null,
|
||||
'hover:bg-tint-hover',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={tcls('px-3 py-1 font-medium text-tint text-xs', className)}>{children}</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
'use client';
|
||||
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
|
||||
import { Link, type LinkInsightsProps } from '../primitives';
|
||||
|
||||
export type DropdownButtonProps<E extends HTMLElement = HTMLElement> = Omit<
|
||||
Partial<DetailedHTMLProps<HTMLAttributes<E>, E>>,
|
||||
'ref'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Button with a dropdown.
|
||||
*/
|
||||
export function DropdownMenu(props: {
|
||||
/** Content of the button */
|
||||
button: React.ReactNode;
|
||||
/** Content of the dropdown */
|
||||
children: React.ReactNode;
|
||||
/** Custom styles */
|
||||
className?: ClassValue;
|
||||
/** Open the dropdown on hover */
|
||||
openOnHover?: boolean;
|
||||
}) {
|
||||
const { button, children, className, openOnHover = false } = props;
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [clicked, setClicked] = useState(false);
|
||||
|
||||
return (
|
||||
<RadixDropdownMenu.Root
|
||||
modal={false}
|
||||
open={openOnHover ? clicked || hovered : clicked}
|
||||
onOpenChange={setClicked}
|
||||
>
|
||||
<RadixDropdownMenu.Trigger
|
||||
asChild
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onClick={() => (openOnHover ? setClicked(!clicked) : null)}
|
||||
className="group/dropdown"
|
||||
>
|
||||
{button}
|
||||
</RadixDropdownMenu.Trigger>
|
||||
|
||||
<RadixDropdownMenu.Portal>
|
||||
<RadixDropdownMenu.Content
|
||||
data-testid="dropdown-menu"
|
||||
hideWhenDetached
|
||||
collisionPadding={8}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
align="start"
|
||||
className="z-40 animate-present pt-2"
|
||||
>
|
||||
<div
|
||||
className={tcls(
|
||||
'flex max-h-80 min-w-40 max-w-[40vw] flex-col gap-1 overflow-auto rounded-lg straight-corners:rounded-sm bg-tint-base p-2 shadow-lg ring-1 ring-tint-subtle sm:min-w-52 sm:max-w-80',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</RadixDropdownMenu.Content>
|
||||
</RadixDropdownMenu.Portal>
|
||||
</RadixDropdownMenu.Root>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animated chevron to display in the dropdown button.
|
||||
*/
|
||||
export function DropdownChevron() {
|
||||
return (
|
||||
<Icon
|
||||
icon="chevron-down"
|
||||
className={tcls(
|
||||
'shrink-0',
|
||||
'opacity-6',
|
||||
'size-3',
|
||||
'ms-1',
|
||||
'transition-all',
|
||||
'group-hover/dropdown:opacity-11',
|
||||
'group-data-[state=open]/dropdown:opacity-11',
|
||||
'group-data-[state=open]/dropdown:rotate-180'
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Button with a chevron for use in dropdowns.
|
||||
*/
|
||||
export function DropdownButton(props: {
|
||||
children: React.ReactNode;
|
||||
className?: ClassValue;
|
||||
}) {
|
||||
const { children, className } = props;
|
||||
|
||||
return (
|
||||
<div className={tcls('group/dropdown', 'flex', 'items-center', className)}>
|
||||
{children}
|
||||
<DropdownChevron />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item in a dropdown.
|
||||
*/
|
||||
export function DropdownMenuItem(
|
||||
props: {
|
||||
href: string | null;
|
||||
active?: boolean;
|
||||
className?: ClassValue;
|
||||
children: React.ReactNode;
|
||||
} & LinkInsightsProps
|
||||
) {
|
||||
const { children, active = false, href, className, insights } = props;
|
||||
|
||||
const itemClassName = tcls(
|
||||
'rounded straight-corners:rounded-sm px-3 py-1 text-sm',
|
||||
active
|
||||
? 'bg-primary text-primary-strong data-[highlighted]:bg-primary-hover'
|
||||
: 'data-[highlighted]:bg-tint-hover',
|
||||
'focus:outline-none',
|
||||
className
|
||||
);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<RadixDropdownMenu.Item asChild>
|
||||
<Link href={href} insights={insights} className={itemClassName}>
|
||||
{children}
|
||||
</Link>
|
||||
</RadixDropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RadixDropdownMenu.Item
|
||||
className={tcls('px-3 py-1 font-medium text-tint text-xs', className)}
|
||||
>
|
||||
{children}
|
||||
</RadixDropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
export function DropdownSubMenu(props: { children: React.ReactNode; label: React.ReactNode }) {
|
||||
const { children, label } = props;
|
||||
|
||||
return (
|
||||
<RadixDropdownMenu.Sub>
|
||||
<RadixDropdownMenu.SubTrigger className="flex cursor-pointer items-center justify-between rounded straight-corners:rounded-sm px-3 py-1 text-sm focus:outline-none data-[highlighted]:bg-tint-hover">
|
||||
{label}
|
||||
<Icon icon="chevron-right" className="size-3 shrink-0 opacity-6" />
|
||||
</RadixDropdownMenu.SubTrigger>
|
||||
<RadixDropdownMenu.Portal>
|
||||
<RadixDropdownMenu.SubContent
|
||||
hideWhenDetached
|
||||
collisionPadding={8}
|
||||
className="z-40 animate-present"
|
||||
>
|
||||
<div className="flex max-h-80 min-w-40 max-w-[40vw] flex-col gap-1 overflow-auto rounded-lg straight-corners:rounded-sm bg-tint-base p-2 shadow-lg ring-1 ring-tint-subtle sm:min-w-52 sm:max-w-80">
|
||||
{children}
|
||||
</div>
|
||||
</RadixDropdownMenu.SubContent>
|
||||
</RadixDropdownMenu.Portal>
|
||||
</RadixDropdownMenu.Sub>
|
||||
);
|
||||
}
|
||||
@@ -13,12 +13,11 @@ import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Button, Link } from '../primitives';
|
||||
import {
|
||||
Dropdown,
|
||||
type DropdownButtonProps,
|
||||
DropdownChevron,
|
||||
DropdownMenu,
|
||||
DropdownMenuItem,
|
||||
} from './Dropdown';
|
||||
} from './DropdownMenu';
|
||||
|
||||
export async function HeaderLink(props: {
|
||||
context: GitBookSiteContext;
|
||||
@@ -33,21 +32,13 @@ export async function HeaderLink(props: {
|
||||
|
||||
if (link.links && link.links.length > 0) {
|
||||
return (
|
||||
<Dropdown
|
||||
<DropdownMenu
|
||||
className={`shrink ${customization.styling.search === 'prominent' ? 'right-0 left-auto' : null}`}
|
||||
button={(buttonProps) => {
|
||||
if (!target || !link.to) {
|
||||
return (
|
||||
<HeaderItemDropdown
|
||||
{...buttonProps}
|
||||
headerPreset={headerPreset}
|
||||
title={link.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
button={
|
||||
!target || !link.to ? (
|
||||
<HeaderItemDropdown headerPreset={headerPreset} title={link.title} />
|
||||
) : (
|
||||
<HeaderLinkNavItem
|
||||
{...buttonProps}
|
||||
linkTarget={link.to}
|
||||
linkStyle={linkStyle}
|
||||
headerPreset={headerPreset}
|
||||
@@ -55,15 +46,14 @@ export async function HeaderLink(props: {
|
||||
isDropdown
|
||||
href={target?.href}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
)
|
||||
}
|
||||
openOnHover={true}
|
||||
>
|
||||
<DropdownMenu>
|
||||
{link.links.map((subLink, index) => (
|
||||
<SubHeaderLink key={index} {...props} link={subLink} />
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
{link.links.map((subLink, index) => (
|
||||
<SubHeaderLink key={index} {...props} link={subLink} />
|
||||
))}
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,10 +147,12 @@ function getHeaderLinkClassName(_props: { headerPreset: CustomizationHeaderPrese
|
||||
|
||||
'text-tint',
|
||||
'links-default:hover:text-primary',
|
||||
'links-default:data-[state=open]:text-primary',
|
||||
'links-default:tint:hover:text-tint-strong',
|
||||
|
||||
'links-default:tint:data-[state=open]:text-tint-strong',
|
||||
'underline-offset-2',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:data-[state=open]:underline',
|
||||
'links-accent:underline-offset-4',
|
||||
'links-accent:decoration-primary-subtle',
|
||||
'links-accent:decoration-[3px]',
|
||||
|
||||
@@ -10,7 +10,7 @@ import type React from 'react';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Dropdown, DropdownChevron, DropdownMenu, DropdownMenuItem } from './Dropdown';
|
||||
import { DropdownChevron, DropdownMenu, DropdownMenuItem, DropdownSubMenu } from './DropdownMenu';
|
||||
import styles from './headerLinks.module.css';
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ export function HeaderLinkMore(props: {
|
||||
}) {
|
||||
const { label, links, context } = props;
|
||||
|
||||
const renderButton = () => (
|
||||
const renderButton = (
|
||||
<button
|
||||
type="button"
|
||||
className={tcls(
|
||||
@@ -45,19 +45,18 @@ export function HeaderLinkMore(props: {
|
||||
|
||||
return (
|
||||
<div className={`${styles.linkEllipsis} z-20 items-center`}>
|
||||
<Dropdown
|
||||
<DropdownMenu
|
||||
button={renderButton}
|
||||
openOnHover={true}
|
||||
className={tcls(
|
||||
'max-md:right-0 max-md:left-auto',
|
||||
context.customization.styling.search === 'prominent' && 'right-0 left-auto'
|
||||
)}
|
||||
>
|
||||
<DropdownMenu>
|
||||
{links.map((link, index) => (
|
||||
<MoreMenuLink key={index} link={link} context={context} />
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
{links.map((link, index) => (
|
||||
<MoreMenuLink key={index} link={link} context={context} />
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -70,32 +69,28 @@ async function MoreMenuLink(props: {
|
||||
|
||||
const target = link.to ? await resolveContentRef(link.to, context) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{'links' in link && link.links.length > 0 && (
|
||||
<hr className="-mx-2 my-1 border-tint border-t first:hidden" />
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
href={target?.href ?? null}
|
||||
insights={
|
||||
link.to
|
||||
? {
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: link.to,
|
||||
position: SiteInsightsLinkPosition.Header,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{link.title}
|
||||
</DropdownMenuItem>
|
||||
{'links' in link
|
||||
? link.links.map((subLink, index) => (
|
||||
<MoreMenuLink key={index} {...props} link={subLink} />
|
||||
))
|
||||
: null}
|
||||
</>
|
||||
return 'links' in link && link.links.length > 0 ? (
|
||||
<DropdownSubMenu label={link.title}>
|
||||
{link.links.map((subLink, index) => {
|
||||
return <MoreMenuLink key={index} {...props} link={subLink} />;
|
||||
})}
|
||||
</DropdownSubMenu>
|
||||
) : (
|
||||
<DropdownMenuItem
|
||||
href={target?.href ?? null}
|
||||
insights={
|
||||
link.to
|
||||
? {
|
||||
type: 'link_click',
|
||||
link: {
|
||||
target: link.to,
|
||||
position: SiteInsightsLinkPosition.Header,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{link.title}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SiteSpace } from '@gitbook/api';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import { Dropdown, DropdownChevron, DropdownMenu } from './Dropdown';
|
||||
import { DropdownChevron, DropdownMenu } from './DropdownMenu';
|
||||
import { SpacesDropdownMenuItem } from './SpacesDropdownMenuItem';
|
||||
|
||||
export function SpacesDropdown(props: {
|
||||
@@ -16,14 +16,13 @@ export function SpacesDropdown(props: {
|
||||
const { linker } = context;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
<DropdownMenu
|
||||
className={tcls(
|
||||
'group-hover/dropdown:invisible', // Prevent hover from opening the dropdown, as it's annoying in this context
|
||||
'group-focus-within/dropdown:group-hover/dropdown:visible' // When the dropdown is already open, it should remain visible when hovered
|
||||
)}
|
||||
button={(buttonProps) => (
|
||||
button={
|
||||
<div
|
||||
{...buttonProps}
|
||||
data-testid="space-dropdown-button"
|
||||
className={tcls(
|
||||
'flex',
|
||||
@@ -40,25 +39,24 @@ export function SpacesDropdown(props: {
|
||||
'straight-corners:rounded-none',
|
||||
|
||||
'bg-tint-base',
|
||||
'group-hover/dropdown:bg-tint-base',
|
||||
'group-focus-within/dropdown:bg-tint-base',
|
||||
|
||||
'text-sm',
|
||||
'text-tint',
|
||||
'group-hover/dropdown:text-tint-strong',
|
||||
'group-focus-within/dropdown:text-tint-strong',
|
||||
'hover:text-tint-strong',
|
||||
'data-[state=open]:text-tint-strong',
|
||||
|
||||
'ring-1',
|
||||
'ring-tint-subtle',
|
||||
'group-hover/dropdown:ring-tint-hover',
|
||||
'group-focus-within/dropdown:ring-tint-hover',
|
||||
'hover:ring-tint-hover',
|
||||
'data-[state=open]:ring-tint-hover',
|
||||
|
||||
'contrast-more:bg-tint-base',
|
||||
'contrast-more:ring-1',
|
||||
'contrast-more:group-hover/dropdown:ring-2',
|
||||
'contrast-more:hover:ring-2',
|
||||
'contrast-more:data-[state=open]:ring-2',
|
||||
'contrast-more:ring-tint',
|
||||
'contrast-more:group-hover/dropdown:ring-tint-hover',
|
||||
'contrast-more:group-focus-within/dropdown:ring-tint-hover',
|
||||
'contrast-more:hover:ring-tint-hover',
|
||||
'contrast-more:data-[state=open]:ring-tint-hover',
|
||||
|
||||
className
|
||||
)}
|
||||
@@ -66,23 +64,21 @@ export function SpacesDropdown(props: {
|
||||
<span className={tcls('line-clamp-1', 'grow')}>{siteSpace.title}</span>
|
||||
<DropdownChevron />
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
>
|
||||
<DropdownMenu>
|
||||
{siteSpaces.map((otherSiteSpace, index) => (
|
||||
<SpacesDropdownMenuItem
|
||||
key={`${otherSiteSpace.id}-${index}`}
|
||||
variantSpace={{
|
||||
id: otherSiteSpace.id,
|
||||
title: otherSiteSpace.title,
|
||||
url: otherSiteSpace.urls.published
|
||||
? linker.toLinkForContent(otherSiteSpace.urls.published)
|
||||
: otherSiteSpace.space.urls.app,
|
||||
}}
|
||||
active={otherSiteSpace.id === siteSpace.id}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
{siteSpaces.map((otherSiteSpace, index) => (
|
||||
<SpacesDropdownMenuItem
|
||||
key={`${otherSiteSpace.id}-${index}`}
|
||||
variantSpace={{
|
||||
id: otherSiteSpace.id,
|
||||
title: otherSiteSpace.title,
|
||||
url: otherSiteSpace.urls.published
|
||||
? linker.toLinkForContent(otherSiteSpace.urls.published)
|
||||
: otherSiteSpace.space.urls.app,
|
||||
}}
|
||||
active={otherSiteSpace.id === siteSpace.id}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Space } from '@gitbook/api';
|
||||
|
||||
import { joinPath } from '@/lib/paths';
|
||||
import { useCurrentPagePath } from '../hooks';
|
||||
import { DropdownMenuItem } from './Dropdown';
|
||||
import { DropdownMenuItem } from './DropdownMenu';
|
||||
|
||||
function useVariantSpaceHref(variantSpaceUrl: string) {
|
||||
const currentPathname = useCurrentPagePath();
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { getSpaceLanguage, t } from '@/intl/server';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import type { RevisionPageDocument, Space } from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import React from 'react';
|
||||
import urlJoin from 'url-join';
|
||||
import { getPDFURLSearchParams } from '../PDF';
|
||||
import { PageFeedbackForm } from '../PageFeedback';
|
||||
|
||||
export function PageActions(props: {
|
||||
page: RevisionPageDocument;
|
||||
context: GitBookSiteContext;
|
||||
withPageFeedback: boolean;
|
||||
}) {
|
||||
const { page, withPageFeedback, context } = props;
|
||||
const { customization, space } = context;
|
||||
const language = getSpaceLanguage(customization);
|
||||
|
||||
const pdfHref = context.linker.toPathInSpace(
|
||||
`~gitbook/pdf?${getPDFURLSearchParams({
|
||||
page: page.id,
|
||||
only: true,
|
||||
limit: 100,
|
||||
}).toString()}`
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'gap-2',
|
||||
'sidebar-list-default:px-3',
|
||||
'page-api-block:xl:max-2xl:px-3',
|
||||
'empty:hidden'
|
||||
)}
|
||||
>
|
||||
{withPageFeedback ? (
|
||||
<React.Suspense fallback={null}>
|
||||
<PageFeedbackForm pageId={page.id} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
{customization.git.showEditLink && space.gitSync?.url && page.git ? (
|
||||
<div>
|
||||
<a
|
||||
href={urlJoin(space.gitSync.url, page.git.path)}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
space.gitSync.installationProvider === 'gitlab'
|
||||
? 'gitlab'
|
||||
: 'github'
|
||||
}
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'edit_on_git', getGitSyncName(space))}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{customization.pdf.enabled ? (
|
||||
<div>
|
||||
<a
|
||||
href={pdfHref}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle'
|
||||
)}
|
||||
>
|
||||
<Icon icon="file-pdf" className={tcls('size-4', 'mr-1.5')} />
|
||||
{t(language, 'pdf_download')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getGitSyncName(space: Space): string {
|
||||
if (space.gitSync?.installationProvider === 'github') {
|
||||
return 'GitHub';
|
||||
}
|
||||
if (space.gitSync?.installationProvider === 'gitlab') {
|
||||
return 'GitLab';
|
||||
}
|
||||
|
||||
return 'Git';
|
||||
}
|
||||
@@ -3,22 +3,17 @@ import {
|
||||
type RevisionPageDocument,
|
||||
SiteAdsStatus,
|
||||
SiteInsightsAdPlacement,
|
||||
type Space,
|
||||
} from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import React from 'react';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { getSpaceLanguage, t } from '@/intl/server';
|
||||
import { getDocumentSections } from '@/lib/document-sections';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { AdaptivePane } from '../Adaptive/AdaptivePane';
|
||||
import { Ad } from '../Ads';
|
||||
import { getPDFURLSearchParams } from '../PDF';
|
||||
import { PageFeedbackForm } from '../PageFeedback';
|
||||
import { ThemeToggler } from '../ThemeToggler';
|
||||
import { ScrollSectionsList } from './ScrollSectionsList';
|
||||
import { PageActions } from './PageActions';
|
||||
import { PageOutline } from './PageOutline';
|
||||
|
||||
/**
|
||||
* Aside listing the headings in the document.
|
||||
@@ -33,33 +28,26 @@ export function PageAside(props: {
|
||||
}) {
|
||||
const { page, document, withPageFeedback, context } = props;
|
||||
const { customization, site, space } = context;
|
||||
const language = getSpaceLanguage(customization);
|
||||
|
||||
const pdfHref = context.linker.toPathInSpace(
|
||||
`~gitbook/pdf?${getPDFURLSearchParams({
|
||||
page: page.id,
|
||||
only: true,
|
||||
limit: 100,
|
||||
}).toString()}`
|
||||
);
|
||||
const useAdaptivePane = true;
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={tcls(
|
||||
'group/aside',
|
||||
'hidden',
|
||||
'xl:flex',
|
||||
// 'page-no-toc:lg:flex',
|
||||
'flex-col',
|
||||
'basis-56',
|
||||
// 'page-no-toc:basis-40',
|
||||
// 'page-no-toc:xl:basis-56',
|
||||
'grow-0',
|
||||
'shrink-0',
|
||||
'break-anywhere', // To prevent long words in headings from breaking the layout
|
||||
|
||||
'text-tint',
|
||||
'contrast-more:text-tint-strong',
|
||||
'text-sm',
|
||||
'sticky',
|
||||
|
||||
// Without header
|
||||
'lg:top-0',
|
||||
'lg:max-h-screen',
|
||||
@@ -91,141 +79,19 @@ export function PageAside(props: {
|
||||
'page-api-block:p-2'
|
||||
)}
|
||||
>
|
||||
{page.layout.outline ? (
|
||||
<>
|
||||
<div
|
||||
className={tcls(
|
||||
'hidden',
|
||||
'page-api-block:xl:max-2xl:flex',
|
||||
'text-xs',
|
||||
'tracking-wide',
|
||||
'font-semibold',
|
||||
'uppercase',
|
||||
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-2'
|
||||
)}
|
||||
>
|
||||
<Icon icon="block-quote" className={tcls('size-3')} />
|
||||
{t(language, 'on_this_page')}
|
||||
<Icon
|
||||
icon="chevron-down"
|
||||
className={tcls(
|
||||
'size-3',
|
||||
'opacity-6',
|
||||
'ml-auto',
|
||||
'page-api-block:xl:max-2xl:group-hover/aside:hidden'
|
||||
)}
|
||||
<div className="lg:top:0 sticky flex grow flex-col gap-6 overflow-y-auto overflow-x-visible border-none py-8 *:border-tint-subtle site-header-sections:lg:top-[6.75rem] site-header:lg:top-16 [&>*:not(:first-child)]:border-t [&>*:not(:first-child)]:pt-6">
|
||||
{useAdaptivePane ? <AdaptivePane /> : null}
|
||||
{page.layout.outline ? (
|
||||
<>
|
||||
<PageOutline document={document} context={context} />
|
||||
<PageActions
|
||||
page={page}
|
||||
context={context}
|
||||
withPageFeedback={withPageFeedback}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'overflow-y-auto',
|
||||
'overflow-x-visible',
|
||||
|
||||
'flex',
|
||||
'flex-col',
|
||||
'shrink',
|
||||
'pb-12',
|
||||
|
||||
'sticky',
|
||||
'lg:top:0',
|
||||
'site-header:lg:top-16',
|
||||
'site-header-sections:lg:top-[6.75rem]',
|
||||
|
||||
'gap-6',
|
||||
'pt-8',
|
||||
|
||||
'page-api-block:xl:max-2xl:py-0',
|
||||
// Hide it for api page, until hovered
|
||||
'page-api-block:xl:max-2xl:hidden',
|
||||
'page-api-block:xl:max-2xl:group-hover/aside:flex'
|
||||
)}
|
||||
>
|
||||
{document ? (
|
||||
<React.Suspense fallback={null}>
|
||||
<PageAsideSections document={document} context={context} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'gap-3',
|
||||
'sidebar-list-default:px-3',
|
||||
'border-t',
|
||||
'first:border-none',
|
||||
'border-tint-subtle',
|
||||
'py-4',
|
||||
'first:pt-0',
|
||||
'page-api-block:xl:max-2xl:px-3',
|
||||
'empty:hidden'
|
||||
)}
|
||||
>
|
||||
{withPageFeedback ? (
|
||||
<React.Suspense fallback={null}>
|
||||
<PageFeedbackForm pageId={page.id} className={tcls('mt-2')} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
{customization.git.showEditLink && space.gitSync?.url && page.git ? (
|
||||
<div>
|
||||
<a
|
||||
href={urlJoin(space.gitSync.url, page.git.path)}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
space.gitSync.installationProvider === 'gitlab'
|
||||
? 'gitlab'
|
||||
: 'github'
|
||||
}
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'edit_on_git', getGitSyncName(space))}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{customization.pdf.enabled ? (
|
||||
<div>
|
||||
<a
|
||||
href={pdfHref}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon="file-pdf"
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'pdf_download')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'sticky bottom-0 z-10 mt-auto flex flex-col bg-tint-base theme-gradient-tint:bg-gradient-tint theme-gradient:bg-gradient-primary theme-muted:bg-tint-subtle pb-4 page-api-block:xl:max-2xl:hidden page-api-block:xl:max-2xl:pb-0 page-api-block:xl:max-2xl:group-hover/aside:flex [html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
|
||||
@@ -254,22 +120,3 @@ export function PageAside(props: {
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
async function PageAsideSections(props: { document: JSONDocument; context: GitBookSiteContext }) {
|
||||
const { document, context } = props;
|
||||
|
||||
const sections = await getDocumentSections(context, document);
|
||||
|
||||
return sections.length > 1 ? <ScrollSectionsList sections={sections} /> : null;
|
||||
}
|
||||
|
||||
function getGitSyncName(space: Space): string {
|
||||
if (space.gitSync?.installationProvider === 'github') {
|
||||
return 'GitHub';
|
||||
}
|
||||
if (space.gitSync?.installationProvider === 'gitlab') {
|
||||
return 'GitLab';
|
||||
}
|
||||
|
||||
return 'Git';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { getSpaceLanguage, t } from '@/intl/server';
|
||||
import { getDocumentSections } from '@/lib/document-sections';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import type { JSONDocument } from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import React from 'react';
|
||||
import { ScrollSectionsList } from './ScrollSectionsList';
|
||||
|
||||
export async function PageOutline(props: {
|
||||
document: JSONDocument | null;
|
||||
context: GitBookSiteContext;
|
||||
}) {
|
||||
const { document, context } = props;
|
||||
const { customization } = context;
|
||||
const language = getSpaceLanguage(customization);
|
||||
|
||||
if (!document) return;
|
||||
|
||||
const sections = await getDocumentSections(context, document);
|
||||
|
||||
return document && sections.length > 1 ? (
|
||||
<div>
|
||||
<div className="mb-1 flex flex-row items-center gap-2 font-semibold text-xs uppercase tracking-wide">
|
||||
<Icon icon="block-quote" className={tcls('size-3')} />
|
||||
{t(language, 'on_this_page')}
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col'
|
||||
|
||||
// 'page-api-block:xl:max-2xl:py-0',
|
||||
// // Hide it for api page, until hovered
|
||||
// 'page-api-block:xl:max-2xl:hidden',
|
||||
// 'page-api-block:xl:max-2xl:group-hover/aside:flex'
|
||||
)}
|
||||
>
|
||||
<React.Suspense fallback={null}>
|
||||
<ScrollSectionsList sections={sections} />
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
import { CustomizationHeaderPreset, CustomizationThemeMode } from '@gitbook/api';
|
||||
import {
|
||||
CustomizationHeaderPreset,
|
||||
CustomizationThemeMode,
|
||||
type SiteStructure,
|
||||
} from '@gitbook/api';
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import { getPageDocument } from '@v2/lib/data';
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
@@ -11,6 +15,7 @@ import { getPagePath } from '@/lib/pages';
|
||||
import { isPageIndexable, isSiteIndexable } from '@/lib/seo';
|
||||
|
||||
import { getResizedImageURL } from '@v2/lib/images';
|
||||
import { JourneyContextProvider } from '../Adaptive/AdaptiveContext';
|
||||
import { PageContextProvider } from '../PageContext';
|
||||
import { PageClientLayout } from './PageClientLayout';
|
||||
import { type PagePathParams, fetchPageData, getPathnameParam } from './fetch';
|
||||
@@ -66,30 +71,32 @@ export async function SitePage(props: SitePageProps) {
|
||||
|
||||
return (
|
||||
<PageContextProvider pageId={page.id} spaceId={context.space.id} title={page.title}>
|
||||
{withFullPageCover && page.cover ? (
|
||||
<PageCover as="full" page={page} cover={page.cover} context={context} />
|
||||
) : null}
|
||||
{/* We use a flex row reverse to render the aside first because the page is streamed. */}
|
||||
<div className="flex grow flex-row-reverse justify-end">
|
||||
<PageAside
|
||||
page={page}
|
||||
document={document}
|
||||
withHeaderOffset={headerOffset}
|
||||
withFullPageCover={withFullPageCover}
|
||||
withPageFeedback={withPageFeedback}
|
||||
context={context}
|
||||
/>
|
||||
<PageBody
|
||||
context={context}
|
||||
page={page}
|
||||
ancestors={ancestors}
|
||||
document={document}
|
||||
withPageFeedback={withPageFeedback}
|
||||
/>
|
||||
</div>
|
||||
<React.Suspense fallback={null}>
|
||||
<PageClientLayout withSections={withSections} />
|
||||
</React.Suspense>
|
||||
<JourneyContextProvider spaces={getSpaces(context.structure)}>
|
||||
{withFullPageCover && page.cover ? (
|
||||
<PageCover as="full" page={page} cover={page.cover} context={context} />
|
||||
) : null}
|
||||
{/* We use a flex row reverse to render the aside first because the page is streamed. */}
|
||||
<div className="flex grow flex-row-reverse justify-end">
|
||||
<PageAside
|
||||
page={page}
|
||||
document={document}
|
||||
withHeaderOffset={headerOffset}
|
||||
withFullPageCover={withFullPageCover}
|
||||
withPageFeedback={withPageFeedback}
|
||||
context={context}
|
||||
/>
|
||||
<PageBody
|
||||
context={context}
|
||||
page={page}
|
||||
ancestors={ancestors}
|
||||
document={document}
|
||||
withPageFeedback={withPageFeedback}
|
||||
/>
|
||||
</div>
|
||||
<React.Suspense fallback={null}>
|
||||
<PageClientLayout withSections={withSections} />
|
||||
</React.Suspense>
|
||||
</JourneyContextProvider>
|
||||
</PageContextProvider>
|
||||
);
|
||||
}
|
||||
@@ -163,3 +170,23 @@ async function getPageDataWithFallback(args: {
|
||||
pageTarget,
|
||||
};
|
||||
}
|
||||
|
||||
function getSpaces(structure: SiteStructure) {
|
||||
if (structure.type === 'siteSpaces') {
|
||||
return structure.structure.map((siteSpace) => ({
|
||||
id: siteSpace.space.id,
|
||||
title: siteSpace.space.title,
|
||||
}));
|
||||
}
|
||||
|
||||
const sections = structure.structure.flatMap((item) =>
|
||||
item.object === 'site-section-group' ? item.sections : item
|
||||
);
|
||||
|
||||
return sections.flatMap((section) =>
|
||||
section.siteSpaces.map((siteSpace) => ({
|
||||
id: siteSpace.space.id,
|
||||
title: siteSpace.space.title,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,13 +75,13 @@ export function SiteSectionListItem(props: {
|
||||
const isMounted = useIsMounted();
|
||||
React.useEffect(() => {}, [isMounted]); // This updates the useScrollToActiveTOCItem hook once we're mounted, so we can actually scroll to the this item
|
||||
|
||||
const linkRef = React.createRef<HTMLAnchorElement>();
|
||||
useScrollToActiveTOCItem({ linkRef, isActive });
|
||||
const anchorRef = React.createRef<HTMLAnchorElement>();
|
||||
useScrollToActiveTOCItem({ anchorRef, isActive });
|
||||
|
||||
return (
|
||||
<Link
|
||||
ref={anchorRef}
|
||||
href={section.url}
|
||||
ref={linkRef}
|
||||
aria-current={isActive && 'page'}
|
||||
className={tcls(
|
||||
'group/section-link flex flex-row items-center gap-3 rounded-md straight-corners:rounded-none px-3 py-2 transition-all hover:bg-tint-hover hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-tint',
|
||||
@@ -121,18 +121,15 @@ export function SiteSectionGroupItem(props: {
|
||||
|
||||
const hasDescendants = group.sections.length > 0;
|
||||
const isActiveGroup = group.sections.some((section) => section.id === currentSection.id);
|
||||
const [isVisible, setIsVisible] = React.useState(isActiveGroup);
|
||||
const shouldOpen = hasDescendants && isActiveGroup;
|
||||
const [isOpen, setIsOpen] = React.useState(shouldOpen);
|
||||
|
||||
// Update the visibility of the children, if we are navigating to a descendant.
|
||||
// Update the visibility of the children if the group becomes active.
|
||||
React.useEffect(() => {
|
||||
if (!hasDescendants) {
|
||||
return;
|
||||
if (shouldOpen) {
|
||||
setIsOpen(shouldOpen);
|
||||
}
|
||||
|
||||
setIsVisible((prev) => prev || isActiveGroup);
|
||||
}, [isActiveGroup, hasDescendants]);
|
||||
|
||||
const { show, hide, scope } = useToggleAnimation({ hasDescendants, isVisible });
|
||||
}, [shouldOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -141,7 +138,7 @@ export function SiteSectionGroupItem(props: {
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setIsVisible((prev) => !prev);
|
||||
setIsOpen((prev) => !prev);
|
||||
}}
|
||||
className={`group/section-link flex w-full flex-row items-center gap-3 rounded-md straight-corners:rounded-none px-3 py-2 text-left transition-all hover:bg-tint-hover hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-tint ${
|
||||
isActiveGroup
|
||||
@@ -184,7 +181,7 @@ export function SiteSectionGroupItem(props: {
|
||||
'after:h-7',
|
||||
'hover:bg-tint-active',
|
||||
'hover:text-current',
|
||||
isActiveGroup ? ['hover:bg-tint-hover'] : []
|
||||
isActiveGroup && 'hover:bg-tint-hover'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
@@ -201,17 +198,13 @@ export function SiteSectionGroupItem(props: {
|
||||
'group-hover:opacity-11',
|
||||
'contrast-more:opacity-11',
|
||||
|
||||
isVisible ? ['rotate-90'] : ['rotate-0']
|
||||
isOpen ? 'rotate-90' : 'rotate-0'
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
{hasDescendants ? (
|
||||
<motion.div
|
||||
ref={scope}
|
||||
className={tcls(isVisible ? null : '[&_ul>li]:opacity-1')}
|
||||
initial={isVisible ? show : hide}
|
||||
>
|
||||
<Descendants isVisible={isOpen}>
|
||||
{group.sections.map((section) => (
|
||||
<SiteSectionListItem
|
||||
section={section}
|
||||
@@ -220,8 +213,25 @@ export function SiteSectionGroupItem(props: {
|
||||
className="pl-5"
|
||||
/>
|
||||
))}
|
||||
</motion.div>
|
||||
</Descendants>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Descendants(props: {
|
||||
isVisible: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { isVisible, children } = props;
|
||||
const { show, hide, scope } = useToggleAnimation(isVisible);
|
||||
return (
|
||||
<motion.div
|
||||
ref={scope}
|
||||
className={isVisible ? undefined : '[&_ul>li]:opacity-1'}
|
||||
initial={isVisible ? show : hide}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function PageDocumentItem(props: {
|
||||
const href = context.linker.toPathForPage({ pages: rootPages, page });
|
||||
|
||||
return (
|
||||
<li className={tcls('flex', 'flex-col')}>
|
||||
<li className="flex flex-col">
|
||||
<ToggleableLinkItem
|
||||
href={href}
|
||||
pathname={getPagePath(rootPages, page)}
|
||||
@@ -52,7 +52,7 @@ export async function PageDocumentItem(props: {
|
||||
}
|
||||
>
|
||||
{page.emoji || page.icon ? (
|
||||
<span className={tcls('flex', 'gap-3', 'items-center')}>
|
||||
<span className="flex items-center gap-3">
|
||||
<TOCPageIcon page={page} />
|
||||
{page.title}
|
||||
</span>
|
||||
|
||||
@@ -15,27 +15,13 @@ export function PageGroupItem(props: {
|
||||
const { rootPages, page, context } = props;
|
||||
|
||||
return (
|
||||
<li className={tcls('flex', 'flex-col', 'group/page-group-item')}>
|
||||
<li className="group/page-group-item flex flex-col">
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'items-center',
|
||||
|
||||
'gap-3',
|
||||
'px-3',
|
||||
'z-[1]',
|
||||
'sticky',
|
||||
'-top-5',
|
||||
'pt-6',
|
||||
'group-first/page-group-item:-mt-5',
|
||||
'-top-5 group-first/page-group-item:-mt-5 sticky z-[1] flex items-center gap-3 px-3 pt-6',
|
||||
'font-semibold text-xs uppercase tracking-wide',
|
||||
'pb-3', // Add extra padding to make the header fade a bit nicer
|
||||
'-mb-1.5', // Then pull the page items a bit closer, effective bottom padding is 1.5 units / 6px.
|
||||
|
||||
'text-xs',
|
||||
'tracking-wide',
|
||||
'font-semibold',
|
||||
'uppercase',
|
||||
|
||||
'[mask-image:linear-gradient(rgba(0,0,0,1)_70%,rgba(0,0,0,0))]', // Fade out effect of fixed page items. We want the fade to start past the header, this is a good approximation.
|
||||
'bg-tint-base',
|
||||
'sidebar-filled:bg-tint-subtle',
|
||||
|
||||
@@ -55,9 +55,9 @@ export async function PageLinkItem(props: { page: RevisionPageLink; context: Git
|
||||
'shrink-0',
|
||||
'text-current',
|
||||
'transition-colors',
|
||||
'[&>path]:transition-[opacity]',
|
||||
'[&>path]:[opacity:0.40]',
|
||||
'group-hover:[&>path]:[opacity:1]'
|
||||
'[&>path]:transition-opacity',
|
||||
'[&>path]:opacity-[0.4]',
|
||||
'group-hover:[&>path]:opacity-11'
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { type RevisionPage, RevisionPageType } from '@gitbook/api';
|
||||
import type { RevisionPage } from '@gitbook/api';
|
||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import assertNever from 'assert-never';
|
||||
import { PageDocumentItem } from './PageDocumentItem';
|
||||
import { PageGroupItem } from './PageGroupItem';
|
||||
import { PageLinkItem } from './PageLinkItem';
|
||||
@@ -16,41 +17,45 @@ export function PagesList(props: {
|
||||
const { rootPages, pages, context, style } = props;
|
||||
|
||||
return (
|
||||
<ul className={tcls('flex', 'flex-col', 'gap-y-0.5', style)}>
|
||||
<ul className={tcls('flex flex-col gap-y-0.5', style)}>
|
||||
{pages.map((page) => {
|
||||
if (page.type === RevisionPageType.Computed) {
|
||||
if (page.type === 'computed') {
|
||||
throw new Error(
|
||||
'Unexpected computed page, it should have been computed in the API'
|
||||
);
|
||||
}
|
||||
|
||||
if (page.type === RevisionPageType.Link) {
|
||||
return <PageLinkItem key={page.id} page={page} context={context} />;
|
||||
}
|
||||
|
||||
if (page.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (page.type === RevisionPageType.Group) {
|
||||
return (
|
||||
<PageGroupItem
|
||||
key={page.id}
|
||||
rootPages={rootPages}
|
||||
page={page}
|
||||
context={context}
|
||||
/>
|
||||
);
|
||||
}
|
||||
switch (page.type) {
|
||||
case 'document':
|
||||
return (
|
||||
<PageDocumentItem
|
||||
key={page.id}
|
||||
rootPages={rootPages}
|
||||
page={page}
|
||||
context={context}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<PageDocumentItem
|
||||
key={page.id}
|
||||
rootPages={rootPages}
|
||||
page={page}
|
||||
context={context}
|
||||
/>
|
||||
);
|
||||
case 'link':
|
||||
return <PageLinkItem key={page.id} page={page} context={context} />;
|
||||
|
||||
case 'group':
|
||||
return (
|
||||
<PageGroupItem
|
||||
key={page.id}
|
||||
rootPages={rootPages}
|
||||
page={page}
|
||||
context={context}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
assertNever(page);
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
|
||||
@@ -1,40 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
type ComponentPropsWithoutRef,
|
||||
} from 'react';
|
||||
import { assert } from 'ts-essentials';
|
||||
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
interface TOCScrollContainerContextType {
|
||||
onContainerMount: (listener: (element: HTMLDivElement) => void) => () => void;
|
||||
}
|
||||
|
||||
const TOCScrollContainerRefContext = React.createContext<React.RefObject<HTMLDivElement> | null>(
|
||||
null
|
||||
);
|
||||
const TOCScrollContainerContext = React.createContext<TOCScrollContainerContextType | null>(null);
|
||||
|
||||
function useTOCScrollContainerRefContext() {
|
||||
const ctx = React.useContext(TOCScrollContainerRefContext);
|
||||
if (!ctx) {
|
||||
throw new Error('Context `TOCScrollContainerRefContext` must be used within Provider');
|
||||
}
|
||||
function useTOCScrollContainerContext() {
|
||||
const ctx = React.useContext(TOCScrollContainerContext);
|
||||
assert(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function TOCScrollContainer(props: {
|
||||
children: React.ReactNode;
|
||||
className?: ClassValue;
|
||||
style?: React.CSSProperties;
|
||||
}) {
|
||||
const { children, className, style } = props;
|
||||
const scrollContainerRef = React.createRef<HTMLDivElement>();
|
||||
/**
|
||||
* Table of contents scroll container.
|
||||
*/
|
||||
export function TOCScrollContainer(props: ComponentPropsWithoutRef<'div'>) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const listeners = useRef<((element: HTMLDivElement) => void)[]>([]);
|
||||
const onContainerMount: TOCScrollContainerContextType['onContainerMount'] = useCallback(
|
||||
(listener) => {
|
||||
if (ref.current) {
|
||||
listener(ref.current);
|
||||
return () => {};
|
||||
}
|
||||
listeners.current.push(listener);
|
||||
return () => {
|
||||
listeners.current = listeners.current.filter((l) => l !== listener);
|
||||
};
|
||||
},
|
||||
[]
|
||||
);
|
||||
const value: TOCScrollContainerContextType = useMemo(
|
||||
() => ({ onContainerMount }),
|
||||
[onContainerMount]
|
||||
);
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
listeners.current.forEach((listener) => listener(element));
|
||||
return () => {
|
||||
listeners.current = [];
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TOCScrollContainerRefContext.Provider value={scrollContainerRef}>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
data-testid="toc-scroll-container"
|
||||
className={tcls(className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</TOCScrollContainerRefContext.Provider>
|
||||
<TOCScrollContainerContext.Provider value={value}>
|
||||
<div ref={ref} data-testid="toc-scroll-container" {...props} />
|
||||
</TOCScrollContainerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,39 +66,31 @@ export function TOCScrollContainer(props: {
|
||||
const TOC_ITEM_OFFSET = 100;
|
||||
|
||||
/**
|
||||
* Scrolls the table of contents container to the page item when it becomes active
|
||||
* Scrolls the table of contents container to the page item when it's initially active.
|
||||
*/
|
||||
export function useScrollToActiveTOCItem(props: {
|
||||
anchorRef: React.RefObject<HTMLAnchorElement>;
|
||||
isActive: boolean;
|
||||
linkRef: React.RefObject<HTMLAnchorElement>;
|
||||
}) {
|
||||
const { isActive, linkRef } = props;
|
||||
const scrollContainerRef = useTOCScrollContainerRefContext();
|
||||
const isScrolled = React.useRef(false);
|
||||
React.useLayoutEffect(() => {
|
||||
if (!isActive) {
|
||||
isScrolled.current = false;
|
||||
return;
|
||||
const { isActive, anchorRef } = props;
|
||||
const isInitialActiveRef = useRef(isActive);
|
||||
const { onContainerMount } = useTOCScrollContainerContext();
|
||||
useEffect(() => {
|
||||
const anchor = anchorRef.current;
|
||||
if (isInitialActiveRef.current && anchor) {
|
||||
return onContainerMount((container) => {
|
||||
if (isOutOfView(anchor, container)) {
|
||||
container.scrollTo({ top: anchor.offsetTop - TOC_ITEM_OFFSET });
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isScrolled.current) {
|
||||
return;
|
||||
}
|
||||
const tocItem = linkRef.current;
|
||||
const tocContainer = scrollContainerRef.current;
|
||||
if (!tocItem || !tocContainer || !isOutOfView(tocItem, tocContainer)) {
|
||||
return;
|
||||
}
|
||||
tocContainer?.scrollTo({
|
||||
top: tocItem.offsetTop - TOC_ITEM_OFFSET,
|
||||
});
|
||||
isScrolled.current = true;
|
||||
}, [isActive, linkRef, scrollContainerRef]);
|
||||
}, [onContainerMount, anchorRef]);
|
||||
}
|
||||
|
||||
function isOutOfView(tocItem: HTMLElement, tocContainer: HTMLElement) {
|
||||
const tocItemTop = tocItem.offsetTop;
|
||||
const containerTop = tocContainer.scrollTop;
|
||||
const containerBottom = containerTop + tocContainer.clientHeight;
|
||||
function isOutOfView(element: HTMLElement, container: HTMLElement) {
|
||||
const tocItemTop = element.offsetTop;
|
||||
const containerTop = container.scrollTop;
|
||||
const containerBottom = containerTop + container.clientHeight;
|
||||
return (
|
||||
tocItemTop < containerTop + TOC_ITEM_OFFSET ||
|
||||
tocItemTop > containerBottom - TOC_ITEM_OFFSET
|
||||
|
||||
@@ -66,36 +66,26 @@ export function TableOfContents(props: {
|
||||
<div // The actual sidebar, either shown with a filled bg or transparent.
|
||||
className={tcls(
|
||||
'lg:-ms-5',
|
||||
'overflow-hidden',
|
||||
'relative',
|
||||
|
||||
'flex',
|
||||
'flex-col',
|
||||
'flex-grow',
|
||||
'relative flex flex-grow flex-col overflow-hidden border-tint-subtle',
|
||||
|
||||
'sidebar-filled:bg-tint-subtle',
|
||||
'theme-muted:bg-tint-subtle',
|
||||
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
|
||||
'[html.sidebar-filled.theme-muted_&]:bg-tint-base',
|
||||
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
|
||||
'[html.sidebar-filled.theme-gradient_&]:border',
|
||||
'page-no-toc:!bg-transparent',
|
||||
|
||||
'sidebar-filled:rounded-xl',
|
||||
'straight-corners:rounded-none'
|
||||
)}
|
||||
>
|
||||
{innerHeader && <div className={tcls('px-5 *:my-4')}>{innerHeader}</div>}
|
||||
{innerHeader && <div className="px-5 *:my-4">{innerHeader}</div>}
|
||||
<TOCScrollContainer // The scrollview inside the sidebar
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-grow',
|
||||
'flex-col',
|
||||
|
||||
'p-2',
|
||||
'flex flex-grow flex-col p-2',
|
||||
customization.trademark.enabled && 'lg:pb-20',
|
||||
|
||||
'overflow-y-auto',
|
||||
'lg:gutter-stable',
|
||||
'lg:gutter-stable overflow-y-auto',
|
||||
'[&::-webkit-scrollbar]:bg-transparent',
|
||||
'[&::-webkit-scrollbar-thumb]:bg-transparent',
|
||||
'group-hover:[&::-webkit-scrollbar]:bg-tint-subtle',
|
||||
@@ -107,11 +97,7 @@ export function TableOfContents(props: {
|
||||
rootPages={pages}
|
||||
pages={pages}
|
||||
context={context}
|
||||
style={tcls(
|
||||
'page-no-toc:hidden',
|
||||
'sidebar-list-line:border-l',
|
||||
'border-tint-subtle'
|
||||
)}
|
||||
style="page-no-toc:hidden border-tint-subtle sidebar-list-line:border-l"
|
||||
/>
|
||||
{customization.trademark.enabled ? (
|
||||
<Trademark
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { motion } from 'framer-motion';
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { useCurrentPagePath, useToggleAnimation } from '../hooks';
|
||||
import { Link, type LinkInsightsProps } from '../primitives';
|
||||
import { useCurrentPagePath } from '../hooks';
|
||||
import { Link, type LinkInsightsProps, type LinkProps } from '../primitives';
|
||||
import { useScrollToActiveTOCItem } from './TOCScroller';
|
||||
|
||||
/**
|
||||
@@ -24,128 +24,190 @@ export function ToggleableLinkItem(
|
||||
const { href, children, descendants, pathname, insights } = props;
|
||||
|
||||
const currentPagePath = useCurrentPagePath();
|
||||
|
||||
const isActive = currentPagePath === pathname;
|
||||
const hasDescendants = !!descendants;
|
||||
const hasActiveDescendant =
|
||||
hasDescendants && (isActive || currentPagePath.startsWith(`${pathname}/`));
|
||||
|
||||
const [isVisible, setIsVisible] = React.useState(hasActiveDescendant);
|
||||
|
||||
// Update the visibility of the children, if we are navigating to a descendant.
|
||||
React.useEffect(() => {
|
||||
if (!hasDescendants) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsVisible((prev) => prev || hasActiveDescendant);
|
||||
}, [hasActiveDescendant, hasDescendants]);
|
||||
|
||||
const { show, hide, scope } = useToggleAnimation({ hasDescendants, isVisible });
|
||||
|
||||
const linkRef = React.createRef<HTMLAnchorElement>();
|
||||
useScrollToActiveTOCItem({ linkRef, isActive });
|
||||
if (!descendants) {
|
||||
return (
|
||||
<LinkItem href={href} insights={insights} isActive={isActive}>
|
||||
{children}
|
||||
</LinkItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
ref={linkRef}
|
||||
href={href}
|
||||
insights={insights}
|
||||
{...(isActive ? { 'aria-current': 'page' } : {})}
|
||||
className={tcls(
|
||||
'group/toclink relative transition-colors',
|
||||
'flex flex-row justify-between',
|
||||
'rounded-md straight-corners:rounded-none p-1.5 pl-3',
|
||||
'text-balance font-normal text-sm text-tint-strong/7 hover:bg-tint-hover hover:text-tint-strong contrast-more:text-tint-strong',
|
||||
'hover:contrast-more:text-tint-strong hover:contrast-more:ring-1 hover:contrast-more:ring-tint-12',
|
||||
'before:contents[] before:-left-px before:absolute before:inset-y-0',
|
||||
'sidebar-list-line:rounded-l-none sidebar-list-line:before:w-px sidebar-list-default:[&+div_a]:rounded-l-none [&+div_a]:pl-5 sidebar-list-default:[&+div_a]:before:w-px',
|
||||
|
||||
isActive && [
|
||||
'font-semibold',
|
||||
'sidebar-list-line:before:w-0.5',
|
||||
|
||||
'before:bg-primary-solid',
|
||||
'text-primary-subtle',
|
||||
'contrast-more:text-primary',
|
||||
|
||||
'sidebar-list-pill:bg-primary',
|
||||
'[html.sidebar-list-pill.theme-muted_&]:bg-primary-hover',
|
||||
'[html.sidebar-list-pill.theme-bold.tint_&]:bg-primary-hover',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-muted_&]:bg-primary',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-bold.tint_&]:bg-primary',
|
||||
|
||||
'hover:bg-primary-hover',
|
||||
'hover:text-primary',
|
||||
'hover:before:bg-primary-solid-hover',
|
||||
'sidebar-list-pill:hover:bg-primary-hover',
|
||||
|
||||
'contrast-more:text-primary',
|
||||
'contrast-more:hover:text-primary-strong',
|
||||
'contrast-more:bg-primary',
|
||||
'contrast-more:ring-1',
|
||||
'contrast-more:ring-primary',
|
||||
'contrast-more:hover:ring-primary-hover',
|
||||
]
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{hasDescendants ? (
|
||||
<span
|
||||
className={tcls(
|
||||
'group',
|
||||
'relative',
|
||||
'rounded-full',
|
||||
'straight-corners:rounded-sm',
|
||||
'w-5',
|
||||
'h-5',
|
||||
'after:grid-area-1-1',
|
||||
'after:absolute',
|
||||
'after:-top-1',
|
||||
'after:grid',
|
||||
'after:-left-1',
|
||||
'after:w-7',
|
||||
'after:h-7',
|
||||
'hover:bg-tint-active',
|
||||
'hover:text-current',
|
||||
isActive ? ['hover:bg-tint-hover'] : []
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setIsVisible((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="chevron-right"
|
||||
className={tcls(
|
||||
'grid',
|
||||
'flex-shrink-0',
|
||||
'size-3',
|
||||
'm-1',
|
||||
'transition-[opacity]',
|
||||
'text-current',
|
||||
'transition-transform',
|
||||
'opacity-6',
|
||||
'group-hover:opacity-11',
|
||||
'contrast-more:opacity-11',
|
||||
|
||||
isVisible ? ['rotate-90'] : ['rotate-0']
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</Link>
|
||||
{hasDescendants ? (
|
||||
<motion.div
|
||||
ref={scope}
|
||||
className={tcls(isVisible ? null : '[&_ul>li]:opacity-1')}
|
||||
initial={isVisible ? show : hide}
|
||||
>
|
||||
<DescendantsRenderer
|
||||
descendants={descendants}
|
||||
defaultIsOpen={isActive || currentPagePath.startsWith(`${pathname}/`)}
|
||||
>
|
||||
{({ descendants, toggler }) => (
|
||||
<>
|
||||
<LinkItem href={href} insights={insights} isActive={isActive}>
|
||||
{children}
|
||||
{toggler}
|
||||
</LinkItem>
|
||||
{descendants}
|
||||
</motion.div>
|
||||
) : null}
|
||||
</>
|
||||
</>
|
||||
)}
|
||||
</DescendantsRenderer>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkItem(
|
||||
props: Pick<LinkProps, 'href' | 'insights' | 'children'> & {
|
||||
isActive: boolean;
|
||||
}
|
||||
) {
|
||||
const { isActive, href, insights, children } = props;
|
||||
const anchorRef = useRef<HTMLAnchorElement>(null);
|
||||
useScrollToActiveTOCItem({ anchorRef, isActive });
|
||||
return (
|
||||
<Link
|
||||
ref={anchorRef}
|
||||
href={href}
|
||||
insights={insights}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
className={tcls(
|
||||
'group/toclink relative transition-colors',
|
||||
'flex flex-row justify-between',
|
||||
'rounded-md straight-corners:rounded-none p-1.5 pl-3',
|
||||
'text-balance font-normal text-sm text-tint-strong/7 hover:bg-tint-hover hover:text-tint-strong contrast-more:text-tint-strong',
|
||||
'hover:contrast-more:text-tint-strong hover:contrast-more:ring-1 hover:contrast-more:ring-tint-12',
|
||||
'before:contents[] before:-left-px before:absolute before:inset-y-0',
|
||||
'sidebar-list-line:rounded-l-none sidebar-list-line:before:w-px sidebar-list-default:[&+div_a]:rounded-l-none [&+div_a]:pl-5 sidebar-list-default:[&+div_a]:before:w-px',
|
||||
|
||||
isActive && [
|
||||
'font-semibold',
|
||||
'sidebar-list-line:before:w-0.5',
|
||||
|
||||
'before:bg-primary-solid',
|
||||
'text-primary-subtle',
|
||||
'contrast-more:text-primary',
|
||||
|
||||
'sidebar-list-pill:bg-primary',
|
||||
'[html.sidebar-list-pill.theme-muted_&]:bg-primary-hover',
|
||||
'[html.sidebar-list-pill.theme-bold.tint_&]:bg-primary-hover',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-muted_&]:bg-primary',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-bold.tint_&]:bg-primary',
|
||||
|
||||
'hover:bg-primary-hover',
|
||||
'hover:text-primary',
|
||||
'hover:before:bg-primary-solid-hover',
|
||||
'sidebar-list-pill:hover:bg-primary-hover',
|
||||
|
||||
'contrast-more:text-primary',
|
||||
'contrast-more:hover:text-primary-strong',
|
||||
'contrast-more:bg-primary',
|
||||
'contrast-more:ring-1',
|
||||
'contrast-more:ring-primary',
|
||||
'contrast-more:hover:ring-primary-hover',
|
||||
]
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function DescendantsRenderer(props: {
|
||||
defaultIsOpen: boolean;
|
||||
descendants: React.ReactNode;
|
||||
children: (renderProps: {
|
||||
descendants: React.ReactNode;
|
||||
toggler: React.ReactNode;
|
||||
}) => React.ReactNode;
|
||||
}) {
|
||||
const { defaultIsOpen, children, descendants } = props;
|
||||
const [isOpen, setIsOpen] = React.useState(defaultIsOpen);
|
||||
|
||||
// Update the visibility of the children if one of the descendants becomes active.
|
||||
React.useEffect(() => {
|
||||
if (defaultIsOpen) {
|
||||
setIsOpen(defaultIsOpen);
|
||||
}
|
||||
}, [defaultIsOpen]);
|
||||
|
||||
return children({
|
||||
toggler: (
|
||||
<Toggler
|
||||
isLinkActive={isOpen}
|
||||
isOpen={isOpen}
|
||||
onToggle={() => {
|
||||
setIsOpen((prev) => !prev);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
descendants: <Descendants isVisible={isOpen}>{descendants}</Descendants>,
|
||||
});
|
||||
}
|
||||
|
||||
function Toggler(props: {
|
||||
isLinkActive: boolean;
|
||||
isOpen: boolean;
|
||||
onToggle: () => void;
|
||||
}) {
|
||||
const { isLinkActive, isOpen, onToggle } = props;
|
||||
return (
|
||||
<span
|
||||
className={tcls(
|
||||
'group',
|
||||
'relative',
|
||||
'rounded-full',
|
||||
'straight-corners:rounded-sm',
|
||||
'w-5',
|
||||
'h-5',
|
||||
'after:grid-area-1-1',
|
||||
'after:absolute',
|
||||
'after:-top-1',
|
||||
'after:grid',
|
||||
'after:-left-1',
|
||||
'after:w-7',
|
||||
'after:h-7',
|
||||
'hover:bg-tint-active',
|
||||
'hover:text-current',
|
||||
isLinkActive && 'hover:bg-tint-hover'
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onToggle();
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="chevron-right"
|
||||
className={tcls(
|
||||
'm-1 grid size-3 flex-shrink-0 text-current opacity-6 transition',
|
||||
'group-hover:opacity-11 contrast-more:opacity-11',
|
||||
isOpen ? 'rotate-90' : 'rotate-0'
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const show = {
|
||||
opacity: 1,
|
||||
height: 'auto',
|
||||
};
|
||||
const hide = {
|
||||
opacity: 0,
|
||||
height: 0,
|
||||
transitionEnd: {
|
||||
display: 'none',
|
||||
},
|
||||
};
|
||||
|
||||
function Descendants(props: {
|
||||
isVisible: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { isVisible, children } = props;
|
||||
return (
|
||||
<motion.div
|
||||
className="overflow-hidden"
|
||||
animate={isVisible ? show : hide}
|
||||
initial={isVisible ? show : hide}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useParams, useSelectedLayoutSegment } from 'next/navigation';
|
||||
|
||||
import { removeLeadingSlash } from '@/lib/paths';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
/**
|
||||
* Return the page of the current page being rendered.
|
||||
@@ -14,9 +15,11 @@ export function useCurrentPagePath() {
|
||||
// For V1, we use the selected layout segment.
|
||||
const rawActiveSegment = useSelectedLayoutSegment() ?? '';
|
||||
|
||||
if (typeof params.pagePath === 'string') {
|
||||
return removeLeadingSlash(decodeURIComponent(params.pagePath));
|
||||
}
|
||||
return useMemo(() => {
|
||||
if (typeof params.pagePath === 'string') {
|
||||
return removeLeadingSlash(decodeURIComponent(params.pagePath));
|
||||
}
|
||||
|
||||
return decodeURIComponent(rawActiveSegment);
|
||||
return decodeURIComponent(rawActiveSegment);
|
||||
}, [params.pagePath, rawActiveSegment]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { stagger, useAnimate } from 'framer-motion';
|
||||
import React from 'react';
|
||||
'use client';
|
||||
|
||||
import { useIsMounted } from '.';
|
||||
import { stagger, useAnimate } from 'framer-motion';
|
||||
import React, { useLayoutEffect } from 'react';
|
||||
|
||||
const show = {
|
||||
opacity: 1,
|
||||
@@ -19,44 +19,35 @@ const hide = {
|
||||
|
||||
const staggerMenuItems = stagger(0.02, { ease: (p) => p ** 2 });
|
||||
|
||||
export function useToggleAnimation({
|
||||
hasDescendants,
|
||||
isVisible,
|
||||
}: {
|
||||
hasDescendants: boolean;
|
||||
isVisible: boolean;
|
||||
}) {
|
||||
const isMounted = useIsMounted();
|
||||
export function useToggleAnimation(isVisible: boolean) {
|
||||
const [scope, animate] = useAnimate();
|
||||
const previousIsVisibleRef = React.useRef<boolean | undefined>(undefined);
|
||||
useLayoutEffect(() => {
|
||||
previousIsVisibleRef.current = isVisible;
|
||||
});
|
||||
const previousIsVisible = previousIsVisibleRef.current;
|
||||
|
||||
// Animate the visibility of the children
|
||||
// only after the initial state.
|
||||
React.useEffect(() => {
|
||||
if (!isMounted || !hasDescendants) {
|
||||
if (previousIsVisible === undefined || previousIsVisible === isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
animate(scope.current, isVisible ? show : hide, {
|
||||
duration: 0.1,
|
||||
});
|
||||
animate(scope.current, isVisible ? show : hide, { duration: 0.1 });
|
||||
|
||||
const selector = '& > ul > li';
|
||||
if (isVisible)
|
||||
animate(
|
||||
selector,
|
||||
{ opacity: 1 },
|
||||
{
|
||||
delay: staggerMenuItems,
|
||||
}
|
||||
);
|
||||
else {
|
||||
if (isVisible) {
|
||||
animate(selector, { opacity: 1 }, { delay: staggerMenuItems });
|
||||
} else {
|
||||
animate(selector, { opacity: 0 });
|
||||
}
|
||||
} catch (error) {
|
||||
// The selector can crash in some browsers, we ignore it as the animation is not critical.
|
||||
console.error(error);
|
||||
}
|
||||
}, [isVisible, isMounted, hasDescendants, animate, scope]);
|
||||
}, [previousIsVisible, isVisible, animate, scope]);
|
||||
|
||||
return { show, hide, scope };
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export function Button({
|
||||
'contrast-more:hover:ring-2',
|
||||
'contrast-more:hover:ring-tint-12',
|
||||
|
||||
'hover:scale-105',
|
||||
'hover:scale-104',
|
||||
'active:scale-100',
|
||||
'transition-all',
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
multiWeight: {
|
||||
@@ -81,6 +84,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
multiSource: {
|
||||
@@ -99,6 +105,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
missingFormat: {
|
||||
@@ -117,6 +126,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
empty: {
|
||||
@@ -124,6 +136,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
custom: true,
|
||||
fontFamily: 'Empty Font',
|
||||
fontFaces: [],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
specialChars: {
|
||||
@@ -136,6 +151,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
complex: {
|
||||
@@ -158,6 +176,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
variousURLs: {
|
||||
@@ -174,6 +195,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { CustomizationFontDefinition } from '@gitbook/api';
|
||||
import type { CustomizationFontDefinitionInput } from '@gitbook/api';
|
||||
|
||||
/**
|
||||
* Define the custom font faces and set the --font-custom to the custom font name
|
||||
*/
|
||||
export function generateFontFacesCSS(customFont: CustomizationFontDefinition): string {
|
||||
export function generateFontFacesCSS(customFont: CustomizationFontDefinitionInput): string {
|
||||
const { fontFaces } = customFont;
|
||||
|
||||
// Generate font face declarations for all weights
|
||||
@@ -45,7 +45,7 @@ export function generateFontFacesCSS(customFont: CustomizationFontDefinition): s
|
||||
/**
|
||||
* Get a list of font sources to preload (only 400 and 700 weights)
|
||||
*/
|
||||
export function getFontSourcesToPreload(customFont: CustomizationFontDefinition) {
|
||||
export function getFontSourcesToPreload(customFont: CustomizationFontDefinitionInput) {
|
||||
return customFont.fontFaces.filter(
|
||||
(face): face is typeof face & { weight: 400 | 700 } =>
|
||||
face.weight === 400 || face.weight === 700
|
||||
|
||||
@@ -4,17 +4,26 @@ import { type TranslationLanguage, languages } from './translations';
|
||||
|
||||
export * from './translate';
|
||||
|
||||
export const DEFAULT_LOCALE = 'en';
|
||||
|
||||
/**
|
||||
* Get the locale of the customization.
|
||||
*/
|
||||
export function getCustomizationLocale(customization: SiteCustomizationSettings): string {
|
||||
return customization.internationalization.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the translation context for a space to use in the server components.
|
||||
*/
|
||||
export function getSpaceLanguage(customization: SiteCustomizationSettings): TranslationLanguage {
|
||||
const fallback = languages.en;
|
||||
const fallback = languages[DEFAULT_LOCALE];
|
||||
|
||||
const { locale } = customization.internationalization;
|
||||
const locale = getCustomizationLocale(customization);
|
||||
|
||||
let language = fallback;
|
||||
// @ts-ignore
|
||||
if (locale !== 'en' && languages[locale]) {
|
||||
if (locale !== DEFAULT_LOCALE && languages[locale]) {
|
||||
// @ts-ignore
|
||||
language = languages[locale];
|
||||
}
|
||||
|
||||
@@ -242,6 +242,7 @@ export const getLatestOpenAPISpecVersionContent = cache({
|
||||
* Resolve a URL to the content to render.
|
||||
*/
|
||||
export const getPublishedContentByUrl = cache({
|
||||
timeout: 30 * 1000,
|
||||
name: 'api.getPublishedContentByUrl.v7',
|
||||
tag: (url) => getCacheTagForURL(url),
|
||||
get: async (
|
||||
@@ -267,6 +268,7 @@ export const getPublishedContentByUrl = cache({
|
||||
|
||||
const parsed = parseCacheResponse(response);
|
||||
|
||||
// biome-ignore lint/suspicious/noConsole: log the ttl of the token
|
||||
console.log(
|
||||
`Parsed ttl: ${parsed.ttl} at ${Date.now()}, for ${'apiToken' in response.data ? response.data.apiToken : '<no-token>'}`
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { type CacheFunctionOptions, cache, noCacheFetchOptions } from '@/lib/cac
|
||||
import type {
|
||||
AnyOpenAPIOperationsBlock,
|
||||
OpenAPISchemasBlock,
|
||||
OpenAPIWebhookBlock,
|
||||
ResolveOpenAPIBlockArgs,
|
||||
} from '@/lib/openapi/types';
|
||||
import { getCloudflareRequestGlobal } from '@v2/lib/data/cloudflare';
|
||||
@@ -16,7 +17,7 @@ import { isV2 } from '../v2';
|
||||
import { enrichFilesystem } from './enrich';
|
||||
import type { FetchOpenAPIFilesystemResult } from './types';
|
||||
|
||||
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock;
|
||||
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock;
|
||||
|
||||
/**
|
||||
* Fetch OpenAPI block.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { fetchOpenAPIFilesystem } from '@/lib/openapi/fetch';
|
||||
import { OpenAPIParseError } from '@gitbook/openapi-parser';
|
||||
import { type OpenAPIWebhookData, resolveOpenAPIWebhook } from '@gitbook/react-openapi';
|
||||
import type {
|
||||
OpenAPIWebhookBlock,
|
||||
ResolveOpenAPIBlockArgs,
|
||||
ResolveOpenAPIBlockResult,
|
||||
} from './types';
|
||||
|
||||
type ResolveOpenAPIWebhookBlockResult = ResolveOpenAPIBlockResult<OpenAPIWebhookData>;
|
||||
|
||||
const weakmap = new WeakMap<OpenAPIWebhookBlock, Promise<ResolveOpenAPIWebhookBlockResult>>();
|
||||
|
||||
/**
|
||||
* Cache the result of resolving an OpenAPI block.
|
||||
* It is important because the resolve is called in sections and in the block itself.
|
||||
*/
|
||||
export function resolveOpenAPIWebhookBlock(
|
||||
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
|
||||
): Promise<ResolveOpenAPIWebhookBlockResult> {
|
||||
if (weakmap.has(args.block)) {
|
||||
return weakmap.get(args.block)!;
|
||||
}
|
||||
|
||||
const result = baseResolveOpenAPIWebhookBlock(args);
|
||||
weakmap.set(args.block, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve OpenAPI webhook block.
|
||||
*/
|
||||
async function baseResolveOpenAPIWebhookBlock(
|
||||
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
|
||||
): Promise<ResolveOpenAPIWebhookBlockResult> {
|
||||
const { context, block } = args;
|
||||
if (!block.data.name || !block.data.method) {
|
||||
return { data: null, specUrl: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
|
||||
|
||||
if (!filesystem) {
|
||||
return { data: null, specUrl: null };
|
||||
}
|
||||
|
||||
const data = await resolveOpenAPIWebhook(filesystem, {
|
||||
name: block.data.name,
|
||||
method: block.data.method,
|
||||
});
|
||||
|
||||
return { data, specUrl };
|
||||
} catch (error) {
|
||||
if (error instanceof OpenAPIParseError) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import type {
|
||||
DocumentBlockOpenAPI,
|
||||
DocumentBlockOpenAPIOperation,
|
||||
DocumentBlockOpenAPISchemas,
|
||||
DocumentBlockOpenAPIWebhook,
|
||||
} from '@gitbook/api';
|
||||
import type { Filesystem, OpenAPIParseError, OpenAPIV3xDocument } from '@gitbook/openapi-parser';
|
||||
import type { GitBookAnyContext } from '@v2/lib/context';
|
||||
@@ -16,6 +17,11 @@ export type AnyOpenAPIOperationsBlock = DocumentBlockOpenAPI | DocumentBlockOpen
|
||||
*/
|
||||
export type OpenAPISchemasBlock = DocumentBlockOpenAPISchemas;
|
||||
|
||||
/**
|
||||
* Type for OpenAPI Webhook block
|
||||
*/
|
||||
export type OpenAPIWebhookBlock = DocumentBlockOpenAPIWebhook;
|
||||
|
||||
/**
|
||||
* Arguments for resolving OpenAPI block.
|
||||
*/
|
||||
|
||||
@@ -41,8 +41,12 @@ export interface ResolvedContentRef {
|
||||
file?: RevisionFile;
|
||||
/** Page document resolved from the content ref */
|
||||
page?: RevisionPageDocument;
|
||||
/** Resolved reusable content, if the ref points to reusable content on a revision. */
|
||||
reusableContent?: RevisionReusableContent;
|
||||
/** Resolved reusable content, if the ref points to reusable content on a revision. Also contains the space and revision used for resolution. */
|
||||
reusableContent?: {
|
||||
revisionReusableContent: RevisionReusableContent;
|
||||
space: Space;
|
||||
revision: string;
|
||||
};
|
||||
/** Resolve OpenAPI spec filesystem. */
|
||||
openAPIFilesystem?: Filesystem;
|
||||
}
|
||||
@@ -231,21 +235,52 @@ export async function resolveContentRef(
|
||||
}
|
||||
|
||||
case 'reusable-content': {
|
||||
// Figure out which space and revision the reusable content is in.
|
||||
const container: { space: Space; revision: string } | null = await (async () => {
|
||||
// without a space on the content ref, or if the space is the same as the current one, we can use the current revision.
|
||||
if (!contentRef.space || contentRef.space === context.space.id) {
|
||||
return { space: context.space, revision: revisionId };
|
||||
}
|
||||
|
||||
const space = await getDataOrNull(
|
||||
dataFetcher.getSpace({
|
||||
spaceId: contentRef.space,
|
||||
shareKey: undefined,
|
||||
})
|
||||
);
|
||||
|
||||
if (!space) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { space, revision: space.revision };
|
||||
})();
|
||||
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const reusableContent = await getDataOrNull(
|
||||
dataFetcher.getReusableContent({
|
||||
spaceId: space.id,
|
||||
revisionId,
|
||||
spaceId: container.space.id,
|
||||
revisionId: container.revision,
|
||||
reusableContentId: contentRef.reusableContent,
|
||||
})
|
||||
);
|
||||
|
||||
if (!reusableContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
href: getGitBookAppHref(`/s/${space.id}`),
|
||||
href: getGitBookAppHref(`/s/${container.space}/~/reusable/${reusableContent.id}`),
|
||||
text: reusableContent.title,
|
||||
active: false,
|
||||
reusableContent,
|
||||
reusableContent: {
|
||||
revisionReusableContent: reusableContent,
|
||||
space: container.space,
|
||||
revision: container.revision,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+193
-124
@@ -8,6 +8,7 @@ import type { GitBookDataFetcher } from '@v2/lib/data/types';
|
||||
import { createImageResizer } from '@v2/lib/images';
|
||||
import { createLinker } from '@v2/lib/links';
|
||||
|
||||
import { GitBookAPI } from '@gitbook/api';
|
||||
import { DataFetcherError, wrapDataFetcherError } from '@v2/lib/data';
|
||||
import { headers } from 'next/headers';
|
||||
import {
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
getUserById,
|
||||
renderIntegrationUi,
|
||||
searchSiteContent,
|
||||
withAPI as withAPIV1,
|
||||
} from './api';
|
||||
import { getDynamicCustomizationSettings } from './customization';
|
||||
import { withLeadingSlash, withTrailingSlash } from './paths';
|
||||
@@ -58,7 +60,7 @@ export async function getV1BaseContext(): Promise<GitBookBaseContext> {
|
||||
return url;
|
||||
};
|
||||
|
||||
const dataFetcher = await getDataFetcherV1();
|
||||
const dataFetcher = getDataFetcherV1();
|
||||
|
||||
const imageResizer = createImageResizer({
|
||||
imagesContextId: host,
|
||||
@@ -82,77 +84,121 @@ export async function getV1BaseContext(): Promise<GitBookBaseContext> {
|
||||
* Try not to use this as much as possible, and instead take the data fetcher from the props.
|
||||
* This data fetcher should only be used at the top of the tree.
|
||||
*/
|
||||
async function getDataFetcherV1(): Promise<GitBookDataFetcher> {
|
||||
function getDataFetcherV1(apiTokenOverride?: string): GitBookDataFetcher {
|
||||
let apiClient: GitBookAPI | undefined;
|
||||
|
||||
/**
|
||||
* Run a function with the correct API client. If an API token is provided, we
|
||||
* create a new API client with the token. Otherwise, we use the default API client.
|
||||
*/
|
||||
async function withAPI<T>(fn: () => Promise<T>): Promise<T> {
|
||||
// No token override - we can use the default API client.
|
||||
if (!apiTokenOverride) {
|
||||
return fn();
|
||||
}
|
||||
|
||||
const client = await api();
|
||||
|
||||
if (!apiClient) {
|
||||
// New client uses same endpoint and user agent as the default client.
|
||||
apiClient = new GitBookAPI({
|
||||
endpoint: client.client.endpoint,
|
||||
authToken: apiTokenOverride,
|
||||
userAgent: client.client.userAgent,
|
||||
});
|
||||
}
|
||||
|
||||
return withAPIV1(
|
||||
{
|
||||
client: apiClient,
|
||||
contextId: client.contextId,
|
||||
},
|
||||
fn
|
||||
);
|
||||
}
|
||||
|
||||
const dataFetcher: GitBookDataFetcher = {
|
||||
async api() {
|
||||
const result = await api();
|
||||
return result.client;
|
||||
return withAPI(async () => {
|
||||
const result = await api();
|
||||
return result.client;
|
||||
});
|
||||
},
|
||||
|
||||
withToken() {
|
||||
// In v1, the token is global and controlled by the middleware.
|
||||
// We don't need to do anything special here.
|
||||
return dataFetcher;
|
||||
withToken({ apiToken }) {
|
||||
return getDataFetcherV1(apiToken);
|
||||
},
|
||||
|
||||
getUserById(userId) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const user = await getUserById(userId);
|
||||
if (!user) {
|
||||
throw new DataFetcherError('User not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const user = await getUserById(userId);
|
||||
if (!user) {
|
||||
throw new DataFetcherError('User not found', 404);
|
||||
}
|
||||
|
||||
return user;
|
||||
});
|
||||
return user;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getPublishedContentSite(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getPublishedContentSite(params);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getPublishedContentSite(params);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getSpace(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getSpace(params.spaceId, params.shareKey);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getSpace(params.spaceId, params.shareKey);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getChangeRequest(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const changeRequest = await getChangeRequest(
|
||||
params.spaceId,
|
||||
params.changeRequestId
|
||||
);
|
||||
if (!changeRequest) {
|
||||
throw new DataFetcherError('Change request not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const changeRequest = await getChangeRequest(
|
||||
params.spaceId,
|
||||
params.changeRequestId
|
||||
);
|
||||
if (!changeRequest) {
|
||||
throw new DataFetcherError('Change request not found', 404);
|
||||
}
|
||||
|
||||
return changeRequest;
|
||||
});
|
||||
return changeRequest;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevision(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getRevision(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getRevision(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionFile(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const revisionFile = await getRevisionFile(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.fileId
|
||||
);
|
||||
if (!revisionFile) {
|
||||
throw new DataFetcherError('Revision file not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const revisionFile = await getRevisionFile(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.fileId
|
||||
);
|
||||
if (!revisionFile) {
|
||||
throw new DataFetcherError('Revision file not found', 404);
|
||||
}
|
||||
|
||||
return revisionFile;
|
||||
});
|
||||
return revisionFile;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPageMarkdown() {
|
||||
@@ -160,117 +206,140 @@ async function getDataFetcherV1(): Promise<GitBookDataFetcher> {
|
||||
},
|
||||
|
||||
getDocument(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const document = await getDocument(params.spaceId, params.documentId);
|
||||
if (!document) {
|
||||
throw new DataFetcherError('Document not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const document = await getDocument(params.spaceId, params.documentId);
|
||||
if (!document) {
|
||||
throw new DataFetcherError('Document not found', 404);
|
||||
}
|
||||
|
||||
return document;
|
||||
});
|
||||
return document;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getComputedDocument(params) {
|
||||
return wrapDataFetcherError(() => {
|
||||
return getComputedDocument(
|
||||
params.organizationId,
|
||||
params.spaceId,
|
||||
params.source,
|
||||
params.seed
|
||||
);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(() => {
|
||||
return getComputedDocument(
|
||||
params.organizationId,
|
||||
params.spaceId,
|
||||
params.source,
|
||||
params.seed
|
||||
);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPages(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getRevisionPages(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getRevisionPages(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPageByPath(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const revisionPage = await getRevisionPageByPath(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.path
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const revisionPage = await getRevisionPageByPath(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.path
|
||||
);
|
||||
|
||||
if (!revisionPage) {
|
||||
throw new DataFetcherError('Revision page not found', 404);
|
||||
}
|
||||
if (!revisionPage) {
|
||||
throw new DataFetcherError('Revision page not found', 404);
|
||||
}
|
||||
|
||||
return revisionPage;
|
||||
});
|
||||
return revisionPage;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getReusableContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const reusableContent = await getReusableContent(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.reusableContentId
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const reusableContent = await getReusableContent(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.reusableContentId
|
||||
);
|
||||
|
||||
if (!reusableContent) {
|
||||
throw new DataFetcherError('Reusable content not found', 404);
|
||||
}
|
||||
if (!reusableContent) {
|
||||
throw new DataFetcherError('Reusable content not found', 404);
|
||||
}
|
||||
|
||||
return reusableContent;
|
||||
});
|
||||
return reusableContent;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getLatestOpenAPISpecVersionContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
||||
params.organizationId,
|
||||
params.slug
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
||||
params.organizationId,
|
||||
params.slug
|
||||
);
|
||||
|
||||
if (!openAPISpecVersionContent) {
|
||||
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
||||
}
|
||||
if (!openAPISpecVersionContent) {
|
||||
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
||||
}
|
||||
|
||||
return openAPISpecVersionContent;
|
||||
});
|
||||
return openAPISpecVersionContent;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getSiteRedirectBySource(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const siteRedirect = await getSiteRedirectBySource(params);
|
||||
if (!siteRedirect) {
|
||||
throw new DataFetcherError('Site redirect not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const siteRedirect = await getSiteRedirectBySource(params);
|
||||
if (!siteRedirect) {
|
||||
throw new DataFetcherError('Site redirect not found', 404);
|
||||
}
|
||||
|
||||
return siteRedirect;
|
||||
});
|
||||
return siteRedirect;
|
||||
})
|
||||
);
|
||||
},
|
||||
getEmbedByUrl(params) {
|
||||
return wrapDataFetcherError(() => {
|
||||
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(() => {
|
||||
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
searchSiteContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const { organizationId, siteId, query, cacheBust, scope } = params;
|
||||
const result = await searchSiteContent(
|
||||
organizationId,
|
||||
siteId,
|
||||
query,
|
||||
scope,
|
||||
cacheBust
|
||||
);
|
||||
return result.items;
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const { organizationId, siteId, query, cacheBust, scope } = params;
|
||||
const result = await searchSiteContent(
|
||||
organizationId,
|
||||
siteId,
|
||||
query,
|
||||
scope,
|
||||
cacheBust
|
||||
);
|
||||
return result.items;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
renderIntegrationUi(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const result = await renderIntegrationUi(params.integrationName, params.request);
|
||||
return result;
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const result = await renderIntegrationUi(
|
||||
params.integrationName,
|
||||
params.request
|
||||
);
|
||||
return result;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
streamAIResponse() {
|
||||
|
||||
@@ -8,20 +8,23 @@ export async function serveRobotsTxt(context: GitBookSiteContext) {
|
||||
const { linker } = context;
|
||||
|
||||
const isRoot = checkIsRootSiteContext(context);
|
||||
const lines = [
|
||||
'User-agent: *',
|
||||
// Disallow dynamic routes / search queries
|
||||
'Disallow: /*?',
|
||||
...((await isSiteIndexable(context))
|
||||
? [
|
||||
'Allow: /',
|
||||
`Sitemap: ${linker.toAbsoluteURL(linker.toPathInSpace(isRoot ? '/sitemap.xml' : '/sitemap-pages.xml'))}`,
|
||||
]
|
||||
: ['Disallow: /']),
|
||||
];
|
||||
const content = lines.join('\n');
|
||||
const isIndexable = await isSiteIndexable(context);
|
||||
|
||||
return new Response(content, {
|
||||
const lines = isIndexable
|
||||
? [
|
||||
'User-agent: *',
|
||||
// Allow image resizing and icon generation routes for favicons and search results
|
||||
'Allow: /~gitbook/image?*',
|
||||
'Allow: /~gitbook/icon?*',
|
||||
// Disallow other dynamic routes / search queries
|
||||
'Disallow: /*?',
|
||||
'Allow: /',
|
||||
`Sitemap: ${linker.toAbsoluteURL(linker.toPathInSpace(isRoot ? '/sitemap.xml' : '/sitemap-pages.xml'))}`,
|
||||
]
|
||||
: ['User-agent: *', 'Disallow: /'];
|
||||
|
||||
const robotsTxt = lines.join('\n');
|
||||
return new Response(robotsTxt, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
|
||||
@@ -296,14 +296,14 @@ const config: Config = {
|
||||
},
|
||||
animation: {
|
||||
present: 'present 200ms cubic-bezier(0.25, 1, 0.5, 1) both',
|
||||
scaleIn: 'scaleIn 200ms ease',
|
||||
scaleOut: 'scaleOut 200ms ease',
|
||||
fadeIn: 'fadeIn 200ms ease forwards',
|
||||
fadeOut: 'fadeOut 200ms ease forwards',
|
||||
enterFromLeft: 'enterFromLeft 250ms ease',
|
||||
enterFromRight: 'enterFromRight 250ms ease',
|
||||
exitToLeft: 'exitToLeft 250ms ease',
|
||||
exitToRight: 'exitToRight 250ms ease',
|
||||
scaleIn: 'scaleIn 200ms ease both',
|
||||
scaleOut: 'scaleOut 200ms ease both',
|
||||
fadeIn: 'fadeIn 200ms ease both',
|
||||
fadeOut: 'fadeOut 200ms ease both',
|
||||
enterFromLeft: 'enterFromLeft 250ms ease both',
|
||||
enterFromRight: 'enterFromRight 250ms ease both',
|
||||
exitToLeft: 'exitToLeft 250ms ease both',
|
||||
exitToRight: 'exitToRight 250ms ease both',
|
||||
},
|
||||
keyframes: {
|
||||
pulseAlt: {
|
||||
@@ -443,6 +443,7 @@ const config: Config = {
|
||||
scale: {
|
||||
'98': '0.98',
|
||||
'102': '1.02',
|
||||
'104': '1.04',
|
||||
},
|
||||
},
|
||||
opacity: opacity(),
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# @gitbook/react-openapi
|
||||
|
||||
## 1.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ebc39e9: Missing select icon
|
||||
- b6b09d4: Fix OpenAPI responses select placement and icon
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- d67699a: Add OpenAPI Webhook block
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- eeb977f: Fix Python code example for JSON payload
|
||||
- 3363a18: Merge simple alternatives
|
||||
- 8ed1bda: Translate OpenAPI blocks
|
||||
- 7588cfe: Improve OpenAPIResponses examples and schemas
|
||||
- ad1dc0b: Bump scalar packages
|
||||
|
||||
## 1.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"version": "1.1.10",
|
||||
"version": "1.2.1",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
|
||||
@@ -102,6 +102,7 @@ export function InteractiveSection(props: {
|
||||
) : null}
|
||||
{header}
|
||||
</SectionHeaderContent>
|
||||
{/* biome-ignore lint/a11y/useKeyWithClickEvents: we prevent default here */}
|
||||
<div
|
||||
className={clsx(
|
||||
'openapi-section-header-controls',
|
||||
|
||||
@@ -6,9 +6,10 @@ import {
|
||||
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
|
||||
import { ScalarApiButton } from './ScalarApiButton';
|
||||
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
|
||||
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
||||
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
import { getDefaultServerURL } from './util/server';
|
||||
import { checkIsReference } from './utils';
|
||||
|
||||
@@ -22,7 +23,7 @@ export function OpenAPICodeSample(props: {
|
||||
data: OpenAPIOperationData;
|
||||
context: OpenAPIContext;
|
||||
}) {
|
||||
const { data } = props;
|
||||
const { data, context } = props;
|
||||
|
||||
// If code samples are disabled at operation level, we don't display the code samples.
|
||||
if (data.operation['x-codeSamples'] === false) {
|
||||
@@ -43,7 +44,14 @@ export function OpenAPICodeSample(props: {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <OpenAPICodeSampleBody data={data} items={samples} />;
|
||||
return (
|
||||
<OpenAPICodeSampleBody
|
||||
context={getOpenAPIClientContext(context)}
|
||||
data={data}
|
||||
items={samples}
|
||||
selectIcon={context.icons.chevronDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +154,7 @@ function generateCodeSamples(props: {
|
||||
method={data.method}
|
||||
path={data.path}
|
||||
renderers={renderers}
|
||||
blockKey={context.blockKey}
|
||||
/>
|
||||
),
|
||||
footer: (
|
||||
@@ -207,11 +216,19 @@ function OpenAPICodeSampleFooter(props: {
|
||||
path={data.path}
|
||||
renderers={renderers}
|
||||
selectIcon={context.icons.chevronDown}
|
||||
blockKey={context.blockKey}
|
||||
/>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
{!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl} />}
|
||||
{!hideTryItPanel && (
|
||||
<ScalarApiButton
|
||||
context={getOpenAPIClientContext(context)}
|
||||
method={method}
|
||||
path={path}
|
||||
specUrl={specUrl}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
import clsx from 'clsx';
|
||||
import type { MediaTypeRenderer } from './OpenAPICodeSample';
|
||||
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
||||
import { createStateKey } from './utils';
|
||||
|
||||
export function OpenAPIMediaTypeExamplesSelector(props: {
|
||||
method: string;
|
||||
path: string;
|
||||
renderers: MediaTypeRenderer[];
|
||||
selectIcon?: React.ReactNode;
|
||||
blockKey?: string;
|
||||
}) {
|
||||
const { method, path, renderers, selectIcon } = props;
|
||||
const { method, path, renderers, selectIcon, blockKey } = props;
|
||||
if (!renderers[0]) {
|
||||
throw new Error('No renderers provided');
|
||||
}
|
||||
const stateKey = `media-type-${method}-${path}`;
|
||||
const stateKey = createStateKey('request-body-media-type', blockKey);
|
||||
const state = useSelectState(stateKey, renderers[0].mediaType);
|
||||
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
|
||||
|
||||
@@ -102,13 +104,17 @@ export function OpenAPIMediaTypeExamplesBody(props: {
|
||||
method: string;
|
||||
path: string;
|
||||
renderers: MediaTypeRenderer[];
|
||||
blockKey?: string;
|
||||
}) {
|
||||
const { renderers, method, path } = props;
|
||||
const { renderers, method, path, blockKey } = props;
|
||||
if (!renderers[0]) {
|
||||
throw new Error('No renderers provided');
|
||||
}
|
||||
|
||||
const mediaTypeState = useSelectState(`media-type-${method}-${path}`, renderers[0].mediaType);
|
||||
const mediaTypeState = useSelectState(
|
||||
createStateKey('request-body-media-type', blockKey),
|
||||
renderers[0].mediaType
|
||||
);
|
||||
const selected = renderers.find((r) => r.mediaType === mediaTypeState.key) ?? renderers[0];
|
||||
if (selected.examples.length === 0) {
|
||||
return selected.element;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useStore } from 'zustand';
|
||||
import { OpenAPIPath } from './OpenAPIPath';
|
||||
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
||||
import { StaticSection } from './StaticSection';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
|
||||
@@ -26,12 +27,13 @@ function OpenAPICodeSampleHeader(props: {
|
||||
items: CodeSampleItem[];
|
||||
data: OpenAPIOperationData;
|
||||
selectIcon?: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { data, items, selectIcon } = props;
|
||||
const { data, items, selectIcon, context } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<OpenAPIPath canCopy={false} withServer={false} data={data} />
|
||||
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
|
||||
{items.length > 1 ? (
|
||||
<OpenAPISelect
|
||||
icon={selectIcon}
|
||||
@@ -56,8 +58,9 @@ export function OpenAPICodeSampleBody(props: {
|
||||
items: CodeSampleItem[];
|
||||
data: OpenAPIOperationData;
|
||||
selectIcon?: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { items, data, selectIcon } = props;
|
||||
const { items, data, selectIcon, context } = props;
|
||||
if (!items[0]) {
|
||||
throw new Error('No items provided');
|
||||
}
|
||||
@@ -72,7 +75,14 @@ export function OpenAPICodeSampleBody(props: {
|
||||
|
||||
return (
|
||||
<StaticSection
|
||||
header={<OpenAPICodeSampleHeader selectIcon={selectIcon} data={data} items={items} />}
|
||||
header={
|
||||
<OpenAPICodeSampleHeader
|
||||
context={context}
|
||||
selectIcon={selectIcon}
|
||||
data={data}
|
||||
items={items}
|
||||
/>
|
||||
}
|
||||
className="openapi-codesample"
|
||||
>
|
||||
<div id={selected.key as string} className="openapi-codesample-panel">
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
|
||||
export function OpenAPICopyButton(
|
||||
props: ButtonProps & {
|
||||
value: string;
|
||||
children: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
label?: string;
|
||||
/**
|
||||
* Whether to show a tooltip.
|
||||
@@ -15,7 +18,7 @@ export function OpenAPICopyButton(
|
||||
withTooltip?: boolean;
|
||||
}
|
||||
) {
|
||||
const { value, label, children, onPress, className, withTooltip = true } = props;
|
||||
const { value, label, children, onPress, className, context, withTooltip = true } = props;
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -60,7 +63,9 @@ export function OpenAPICopyButton(
|
||||
offset={4}
|
||||
className="openapi-tooltip"
|
||||
>
|
||||
{copied ? 'Copied' : label || 'Copy to clipboard'}
|
||||
{copied
|
||||
? t(context.translation, 'copied')
|
||||
: label || t(context.translation, 'copy_to_clipboard')}
|
||||
</Tooltip>
|
||||
</TooltipTrigger>
|
||||
);
|
||||
|
||||
@@ -9,11 +9,12 @@ import { Button, Disclosure, DisclosurePanel } from 'react-aria-components';
|
||||
*/
|
||||
export function OpenAPIDisclosure(props: {
|
||||
icon: React.ReactNode;
|
||||
header: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
label: string | ((isExpanded: boolean) => string);
|
||||
className?: string;
|
||||
}): React.JSX.Element {
|
||||
const { icon, children, label, className } = props;
|
||||
const { icon, header, label, children, className } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -31,8 +32,11 @@ export function OpenAPIDisclosure(props: {
|
||||
: 'none',
|
||||
})}
|
||||
>
|
||||
{icon}
|
||||
<span>{typeof label === 'function' ? label(isExpanded) : label}</span>
|
||||
{header}
|
||||
<div className="openapi-disclosure-trigger-label">
|
||||
<span>{typeof label === 'function' ? label(isExpanded) : label}</span>
|
||||
{icon}
|
||||
</div>
|
||||
</Button>
|
||||
<DisclosurePanel className="openapi-disclosure-panel">
|
||||
{isExpanded ? children : null}
|
||||
|
||||
@@ -76,7 +76,7 @@ function DisclosureItem(props: {
|
||||
});
|
||||
|
||||
const panelRef = useRef<HTMLDivElement | null>(null);
|
||||
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
||||
const triggerRef = useRef<HTMLDivElement | null>(null);
|
||||
const isDisabled = groupState?.isDisabled || !group.tabs?.length || false;
|
||||
const { buttonProps: triggerProps, panelProps } = useDisclosure(
|
||||
{
|
||||
@@ -96,54 +96,56 @@ function DisclosureItem(props: {
|
||||
|
||||
return (
|
||||
<div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
|
||||
<div className="openapi-disclosure-group-header">
|
||||
<button
|
||||
slot="trigger"
|
||||
ref={triggerRef}
|
||||
{...mergeProps(buttonProps, focusProps)}
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
outline: isFocusVisible
|
||||
? '2px solid rgb(var(--primary-color-500)/0.4)'
|
||||
: 'none',
|
||||
}}
|
||||
className="openapi-disclosure-group-trigger"
|
||||
>
|
||||
<div className="openapi-disclosure-group-icon">
|
||||
{icon || (
|
||||
<svg viewBox="0 0 24 24" className="openapi-disclosure-group-icon">
|
||||
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
slot="trigger"
|
||||
ref={triggerRef}
|
||||
{...mergeProps(buttonProps, focusProps)}
|
||||
aria-disabled={isDisabled}
|
||||
style={{
|
||||
outline: isFocusVisible
|
||||
? '2px solid rgb(var(--primary-color-500)/0.4)'
|
||||
: 'none',
|
||||
}}
|
||||
className="openapi-disclosure-group-trigger"
|
||||
>
|
||||
<div className="openapi-disclosure-group-icon">
|
||||
{icon || (
|
||||
<svg viewBox="0 0 24 24" className="openapi-disclosure-group-icon">
|
||||
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="openapi-disclosure-group-label">
|
||||
{group.label}
|
||||
</button>
|
||||
{group.tabs ? (
|
||||
<div
|
||||
className="openapi-disclosure-group-mediatype"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{group.tabs?.length > 1 ? (
|
||||
<OpenAPISelect
|
||||
icon={selectIcon}
|
||||
stateKey={selectStateKey}
|
||||
onSelectionChange={() => {
|
||||
state.expand();
|
||||
}}
|
||||
items={group.tabs}
|
||||
>
|
||||
{group.tabs.map((tab) => (
|
||||
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
||||
{tab.label}
|
||||
</OpenAPISelectItem>
|
||||
))}
|
||||
</OpenAPISelect>
|
||||
) : group.tabs[0]?.label ? (
|
||||
<span>{group.tabs[0].label}</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{group.tabs ? (
|
||||
<div
|
||||
className="openapi-disclosure-group-mediatype"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{group.tabs?.length > 1 ? (
|
||||
<OpenAPISelect
|
||||
icon={selectIcon}
|
||||
stateKey={selectStateKey}
|
||||
onSelectionChange={() => {
|
||||
state.expand();
|
||||
}}
|
||||
items={group.tabs}
|
||||
placement="bottom end"
|
||||
>
|
||||
{group.tabs.map((tab) => (
|
||||
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
||||
{tab.label}
|
||||
</OpenAPISelectItem>
|
||||
))}
|
||||
</OpenAPISelect>
|
||||
) : group.tabs[0]?.label ? (
|
||||
<span>{group.tabs[0].label}</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state.isExpanded && selectedTab && (
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { generateSchemaExample } from './generateSchemaExample';
|
||||
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
|
||||
import { json2xml } from './json2xml';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import type { OpenAPIContext } from './types';
|
||||
import { checkIsReference } from './utils';
|
||||
import { t } from './translate';
|
||||
|
||||
/**
|
||||
* Display an example.
|
||||
@@ -17,7 +16,7 @@ export function OpenAPIExample(props: {
|
||||
const code = stringifyExample({ example, xml: syntax === 'xml' });
|
||||
|
||||
if (code === null) {
|
||||
return <OpenAPIEmptyExample />;
|
||||
return <OpenAPIEmptyExample context={context} />;
|
||||
}
|
||||
|
||||
return context.renderCodeBlock({ code, syntax });
|
||||
@@ -44,86 +43,13 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
|
||||
/**
|
||||
* Empty response example.
|
||||
*/
|
||||
export function OpenAPIEmptyExample() {
|
||||
export function OpenAPIEmptyExample(props: {
|
||||
context: OpenAPIUniversalContext;
|
||||
}) {
|
||||
const { context } = props;
|
||||
return (
|
||||
<pre className="openapi-example-empty">
|
||||
<p>No Content</p>
|
||||
<p>{t(context.translation, 'no_content')}</p>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an example from a reference object.
|
||||
*/
|
||||
export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
|
||||
return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get examples from a media type object.
|
||||
*/
|
||||
export function getExamplesFromMediaTypeObject(args: {
|
||||
mediaType: string;
|
||||
mediaTypeObject: OpenAPIV3.MediaTypeObject;
|
||||
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
|
||||
const { mediaTypeObject, mediaType } = args;
|
||||
if (mediaTypeObject.examples) {
|
||||
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
|
||||
return {
|
||||
key,
|
||||
example: checkIsReference(example) ? getExampleFromReference(example) : example,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (mediaTypeObject.example) {
|
||||
return [{ key: 'default', example: { value: mediaTypeObject.example } }];
|
||||
}
|
||||
|
||||
if (mediaTypeObject.schema) {
|
||||
if (mediaType === 'application/xml') {
|
||||
// @TODO normally we should use the name of the schema but we don't have it
|
||||
// fix it when we got the reference name
|
||||
const root = mediaTypeObject.schema.xml?.name ?? 'object';
|
||||
return [
|
||||
{
|
||||
key: 'default',
|
||||
example: {
|
||||
value: {
|
||||
[root]: generateSchemaExample(mediaTypeObject.schema, {
|
||||
xml: mediaType === 'application/xml',
|
||||
mode: 'read',
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: 'default',
|
||||
example: {
|
||||
value: generateSchemaExample(mediaTypeObject.schema, {
|
||||
mode: 'read',
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get example from a schema object.
|
||||
*/
|
||||
export function getExampleFromSchema(args: {
|
||||
schema: OpenAPIV3.SchemaObject;
|
||||
}): OpenAPIV3.ExampleObject {
|
||||
const { schema } = args;
|
||||
|
||||
if (schema.example) {
|
||||
return { value: schema.example };
|
||||
}
|
||||
|
||||
return { value: generateSchemaExample(schema, { mode: 'read' }) };
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user