Compare commits

...

23 Commits

Author SHA1 Message Date
Samy Pessé 885264f132 Version Packages (#3506)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-05 11:03:50 +02:00
Greg Bergé f1a6dec951 Less strict OpenAPI parser (#3529)
Co-authored-by: Nolann Biron <biron.nolann@gmail.com>
2025-08-05 08:45:45 +00:00
Claire Chabas 44f41510fa Fix close buttons tooltips (#3525) 2025-08-04 15:22:59 +02:00
spastorelli f220229e5e Fix stepper title misalignment (#3524) 2025-08-04 09:34:22 +02:00
Greg Bergé 61b4cb8d7c Upgrade Argos (#3522) 2025-08-01 16:07:35 +02:00
Greg Bergé 185cdb4883 Fix scrolling to anchor positionning (#3521) 2025-08-01 15:44:27 +02:00
Zeno Kapitein 2cdba53451 Upgrade to Tailwind v4 (#3206) 2025-08-01 13:03:44 +02:00
Greg Bergé 950f6c36cf greg/rnd 6997 fix long openapi responses label (#3518) 2025-07-31 12:08:57 +00:00
Zeno Kapitein ea37977b1e Fix inconsistent state between q= and ask= and keyboard navigation (#3519) 2025-07-31 13:20:27 +02:00
Greg Bergé 81da82aed7 Increase tests max duration (#3517) 2025-07-30 18:33:56 +00:00
Greg Bergé 39a3518651 Show a HoverCard when an internal link is broken (#3516) 2025-07-30 18:09:57 +02:00
spastorelli d903273759 Strip visitor.* param when normalizing URL (#3512) 2025-07-30 17:22:09 +02:00
Taran Vohra d130532f69 Suggest questions in the current space context (#3514) 2025-07-30 20:36:40 +05:30
Zeno Kapitein 94fcd21f34 Improve ?ask= init handling (#3513) 2025-07-30 13:17:15 +02:00
conico974 1d2db4ae2b Fix cookie handling for AI assistant server actions (#3509)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-30 12:29:51 +02:00
Steven H d270b4af9b Don't track events if a special middleware header is set. (#3511) 2025-07-30 09:26:01 +01:00
Zeno Kapitein ccfbd3c1a8 Fix AI Search Query (#3510) 2025-07-30 07:44:37 +00:00
Claire Chabas 5c4923557f Temporarily remove highlighting for PowerShell and C++ in Safari to avoid page crash (#3508) 2025-07-29 20:39:26 +02:00
Zeno Kapitein b1f608b865 Implement basic URL scheme for Assistant & AI Search (#3503) 2025-07-29 16:13:44 +02:00
conico974 c7993756dd Fix regional caching of tag cache (#3504)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-07-29 13:27:56 +02:00
Nolann B. d02465823f Prevent page breaks inside blocks when printing (#3507) 2025-07-28 18:08:08 +02:00
Greg Bergé 6b9f6b5b79 Improve french translations (#3501) 2025-07-28 14:05:04 +02:00
Taran Vohra df7de8fb0c Fix an issue where links were not rendering properly in Ask AI answers (#3505) 2025-07-28 16:43:18 +05:30
147 changed files with 4142 additions and 1239 deletions
+4 -4
View File
@@ -102,7 +102,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Visual Testing v2 name: Visual Testing v2
needs: deploy-v2-vercel needs: deploy-v2-vercel
timeout-minutes: 10 timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -123,7 +123,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Visual Testing v2 (Cloudflare) name: Visual Testing v2 (Cloudflare)
needs: deploy-v2-cloudflare needs: deploy-v2-cloudflare
timeout-minutes: 10 timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -144,7 +144,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Visual Testing Customers v2 name: Visual Testing Customers v2
needs: deploy-v2-vercel needs: deploy-v2-vercel
timeout-minutes: 10 timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -165,7 +165,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Visual Testing Customers v2 (Cloudflare) name: Visual Testing Customers v2 (Cloudflare)
needs: deploy-v2-cloudflare needs: deploy-v2-cloudflare
timeout-minutes: 10 timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
+2 -1
View File
@@ -21,7 +21,8 @@
"packages/openapi-parser/src/fixtures/**/*", "packages/openapi-parser/src/fixtures/**/*",
"packages/emoji-codepoints/index.ts", "packages/emoji-codepoints/index.ts",
"packages/icons/src/data/*.json", "packages/icons/src/data/*.json",
"packages/gitbook/worker-configuration.d.ts" "packages/gitbook/worker-configuration.d.ts",
"**/*.css"
] ]
}, },
"formatter": { "formatter": {
+201 -67
View File
@@ -48,7 +48,7 @@
}, },
"packages/gitbook": { "packages/gitbook": {
"name": "gitbook", "name": "gitbook",
"version": "0.14.0", "version": "0.14.1",
"dependencies": { "dependencies": {
"@gitbook/api": "catalog:", "@gitbook/api": "catalog:",
"@gitbook/cache-tags": "workspace:*", "@gitbook/cache-tags": "workspace:*",
@@ -60,7 +60,7 @@
"@gitbook/react-contentkit": "workspace:*", "@gitbook/react-contentkit": "workspace:*",
"@gitbook/react-math": "workspace:*", "@gitbook/react-math": "workspace:*",
"@gitbook/react-openapi": "workspace:*", "@gitbook/react-openapi": "workspace:*",
"@opennextjs/cloudflare": "^1.4.0", "@opennextjs/cloudflare": "^1.6.2",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.1.12", "@radix-ui/react-dropdown-menu": "^2.1.12",
"@radix-ui/react-navigation-menu": "^1.2.3", "@radix-ui/react-navigation-menu": "^1.2.3",
@@ -118,8 +118,9 @@
"zustand": "^5.0.3", "zustand": "^5.0.3",
}, },
"devDependencies": { "devDependencies": {
"@argos-ci/playwright": "^5.0.5", "@argos-ci/playwright": "^5.0.9",
"@playwright/test": "^1.51.1", "@playwright/test": "^1.54.2",
"@tailwindcss/postcss": "^4.1.11",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/jsontoxml": "^1.0.5", "@types/jsontoxml": "^1.0.5",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
@@ -131,14 +132,13 @@
"@types/react": "18.3.13", "@types/react": "18.3.13",
"@types/react-dom": "18.3.1", "@types/react-dom": "18.3.1",
"@types/rison": "^0.0.9", "@types/rison": "^0.0.9",
"autoprefixer": "^10",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"env-cmd": "^10.1.0", "env-cmd": "^10.1.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"postcss": "^8", "postcss": "^8",
"psi": "^4.1.0", "psi": "^4.1.0",
"stylelint": "^16.16.0", "stylelint": "^16.16.0",
"tailwindcss": "^3.4.0", "tailwindcss": "^4.1.11",
"ts-essentials": "^10.0.1", "ts-essentials": "^10.0.1",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"vercel": "^39.3.0", "vercel": "^39.3.0",
@@ -166,7 +166,7 @@
}, },
"packages/openapi-parser": { "packages/openapi-parser": {
"name": "@gitbook/openapi-parser", "name": "@gitbook/openapi-parser",
"version": "2.2.1", "version": "2.2.2",
"dependencies": { "dependencies": {
"@scalar/openapi-parser": "^0.18.0", "@scalar/openapi-parser": "^0.18.0",
"@scalar/openapi-types": "^0.1.9", "@scalar/openapi-types": "^0.1.9",
@@ -213,7 +213,7 @@
}, },
"packages/react-openapi": { "packages/react-openapi": {
"name": "@gitbook/react-openapi", "name": "@gitbook/react-openapi",
"version": "1.3.3", "version": "1.3.4",
"dependencies": { "dependencies": {
"@gitbook/openapi-parser": "workspace:*", "@gitbook/openapi-parser": "workspace:*",
"@scalar/api-client-react": "^1.3.16", "@scalar/api-client-react": "^1.3.16",
@@ -246,7 +246,7 @@
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
}, },
"catalog": { "catalog": {
"@gitbook/api": "^0.129.0", "@gitbook/api": "^0.130.0",
}, },
"packages": { "packages": {
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="], "@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="],
@@ -259,15 +259,17 @@
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@argos-ci/api-client": ["@argos-ci/api-client@0.8.1", "", { "dependencies": { "debug": "^4.4.0", "openapi-fetch": "0.13.5" } }, "sha512-3IHv7ANSPNO6OwWgwULlHbP9/tFV9kQDu6+nL9jysfPkGj0GgtrOsyBb+iU931c7wSMo1OD+XNujCnRzDD968w=="], "@argos-ci/api-client": ["@argos-ci/api-client@0.8.1", "", { "dependencies": { "debug": "^4.4.0", "openapi-fetch": "0.13.5" } }, "sha512-3IHv7ANSPNO6OwWgwULlHbP9/tFV9kQDu6+nL9jysfPkGj0GgtrOsyBb+iU931c7wSMo1OD+XNujCnRzDD968w=="],
"@argos-ci/browser": ["@argos-ci/browser@4.1.2", "", {}, "sha512-OljPFzxSNndWSwMBxKGtN3p8lSbt73z+/0CIXbBVTQOPjPMErXWjgtrFL23xqtTq5wdds3uxGv7tjVDWJbgBYg=="], "@argos-ci/browser": ["@argos-ci/browser@4.2.0", "", {}, "sha512-xztv7hMNJyAYw2FXZDm4OCtw+jotJv0BPp5/9dljKrJ0us/2dDEEYKAvVcxzdzRpSe7VOtXSdsoFf0XCCPmxtQ=="],
"@argos-ci/core": ["@argos-ci/core@3.2.0", "", { "dependencies": { "@argos-ci/api-client": "0.8.1", "@argos-ci/util": "2.3.1", "axios": "^1.8.4", "convict": "^6.2.4", "debug": "^4.4.0", "fast-glob": "^3.3.3", "sharp": "^0.33.5", "tmp": "^0.2.3" } }, "sha512-wraH5RGFpoU7zcKV2BVi4LvY+5bWR/Ds6OVhUtq3jtd7I3qJRFVFq2IX1TfPPvBBA7wX0OqcrqgIWAQwy+vsoA=="], "@argos-ci/core": ["@argos-ci/core@3.2.1", "", { "dependencies": { "@argos-ci/api-client": "0.8.1", "@argos-ci/util": "2.3.2", "axios": "^1.8.4", "convict": "^6.2.4", "debug": "^4.4.0", "fast-glob": "^3.3.3", "sharp": "^0.33.5", "tmp": "^0.2.3" } }, "sha512-P+tGofNLAtH0+e87M8sZc+juAtbOcnV6z2nA2MwB2OzUVVXGINJHAF2cK0ZUyXC9d8a7RL0+rQWkP4vXDA/gBw=="],
"@argos-ci/playwright": ["@argos-ci/playwright@5.0.5", "", { "dependencies": { "@argos-ci/browser": "4.1.2", "@argos-ci/core": "3.2.0", "@argos-ci/util": "2.3.1", "chalk": "^5.4.1", "debug": "^4.4.0" } }, "sha512-ZqaqptjI8rVUjD2KQzpvI1olFDiiMjgDaIyFOMWo8531cB7CMCwPXDNsFw2psUfsz3a3LP91eH2OrJopmevEkQ=="], "@argos-ci/playwright": ["@argos-ci/playwright@5.0.9", "", { "dependencies": { "@argos-ci/browser": "4.2.0", "@argos-ci/core": "3.2.1", "@argos-ci/util": "2.3.2", "chalk": "^5.4.1", "debug": "^4.4.0" } }, "sha512-FbUPjNVm+Qer9aUU56oGNiPeynqDCh33GOYKekzjikBlxWFZfhiW4NA+sOXF0VWzGb5ni9l1U7fy6ycOTSalAA=="],
"@argos-ci/util": ["@argos-ci/util@2.3.1", "", {}, "sha512-kE61HU2480fbAnimmA4x9HK45ZJvkoqLdW5GxT5uvwhkclQykVd2S6WfGFUr3JokTXfZ5LZEEfoWgtGA316KSQ=="], "@argos-ci/util": ["@argos-ci/util@2.3.2", "", {}, "sha512-xtNHJxpWYNst/sMNn4Jv/vkODuFsJ+APr4FBeoFUdIa+Izjl4ZFHsYA2PUyu+ygIpQCkof8yZLL9U1/VpiyyIw=="],
"@ast-grep/napi": ["@ast-grep/napi@0.35.0", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.35.0", "@ast-grep/napi-darwin-x64": "0.35.0", "@ast-grep/napi-linux-arm64-gnu": "0.35.0", "@ast-grep/napi-linux-arm64-musl": "0.35.0", "@ast-grep/napi-linux-x64-gnu": "0.35.0", "@ast-grep/napi-linux-x64-musl": "0.35.0", "@ast-grep/napi-win32-arm64-msvc": "0.35.0", "@ast-grep/napi-win32-ia32-msvc": "0.35.0", "@ast-grep/napi-win32-x64-msvc": "0.35.0" } }, "sha512-3ucaaSxV6fxXoqHrE/rxAvP1THnDdY5jNzGlnvx+JvnY9C/dSRKc0jlRMRz59N3El572+/yNRUUpAV1T9aBJug=="], "@ast-grep/napi": ["@ast-grep/napi@0.35.0", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.35.0", "@ast-grep/napi-darwin-x64": "0.35.0", "@ast-grep/napi-linux-arm64-gnu": "0.35.0", "@ast-grep/napi-linux-arm64-musl": "0.35.0", "@ast-grep/napi-linux-x64-gnu": "0.35.0", "@ast-grep/napi-linux-x64-musl": "0.35.0", "@ast-grep/napi-win32-arm64-msvc": "0.35.0", "@ast-grep/napi-win32-ia32-msvc": "0.35.0", "@ast-grep/napi-win32-x64-msvc": "0.35.0" } }, "sha512-3ucaaSxV6fxXoqHrE/rxAvP1THnDdY5jNzGlnvx+JvnY9C/dSRKc0jlRMRz59N3El572+/yNRUUpAV1T9aBJug=="],
@@ -609,7 +611,7 @@
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="], "@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.129.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-Uh+k/BiDgdXj5a8BIlvwfEXdZNNlQMFDlGKmivUYH1gvFySqrTO6PjR/HtUOZEdNauAeWZmGoB8CLuwrVZS+YA=="], "@gitbook/api": ["@gitbook/api@0.130.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-nfAPfTISUou+hJPZLrBsuUSSaHEeWB6eZ4adapWAagmaeeA3CpfopDROXcqGJPBHYPfvfLZIGdgyYhyYC3Objw=="],
"@gitbook/cache-tags": ["@gitbook/cache-tags@workspace:packages/cache-tags"], "@gitbook/cache-tags": ["@gitbook/cache-tags@workspace:packages/cache-tags"],
@@ -787,9 +789,9 @@
"@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="], "@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
"@opennextjs/aws": ["@opennextjs/aws@3.7.0", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "cookie": "^1.0.2", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-JXUZice+CedEQW20hnBVqzBEj+jfr4Oe2LVYSE4RNKdfHVIeYG+WJAop14TxRJ+NugKWGcJx6opf944l+ZG7XQ=="], "@opennextjs/aws": ["@opennextjs/aws@3.7.1", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "cookie": "^1.0.2", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-ryV5cQucSJDU0TF3+Jzqn9g0/+zhUZgjNopSnchPvH0SxSAbKkNaNH7SlnWBhykjusqHaFzQ8dfEjcjAq7TJSQ=="],
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.4.0", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.7.0", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.19.1" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-nEtxmSo6/y83Qw2TSU8rBpljaGl3NUl5GenD/c71h6YqgbP4QURSxMh7ySe7KK+vtQbilD7WePXovqL4Y6D6dw=="], "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.6.2", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.7.1", "cloudflare": "^4.4.1", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6", "yargs": "^18.0.0" }, "peerDependencies": { "wrangler": "^4.24.4" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-N96OTbp1970NwLP6dNkAzBDrxjthLQ3VXHcjKl8edqNbJ/iT75H60ZYHU/ZZ7gSZQeJluPvPb1n0ZPQ08EooCQ=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
@@ -797,7 +799,7 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@playwright/test": ["@playwright/test@1.51.1", "", { "dependencies": { "playwright": "1.51.1" }, "bin": { "playwright": "cli.js" } }, "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q=="], "@playwright/test": ["@playwright/test@1.54.2", "", { "dependencies": { "playwright": "1.54.2" }, "bin": { "playwright": "cli.js" } }, "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA=="],
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], "@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
@@ -1247,6 +1249,36 @@
"@tailwindcss/container-queries": ["@tailwindcss/container-queries@0.1.1", "", { "peerDependencies": { "tailwindcss": ">=3.2.0" } }, "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA=="], "@tailwindcss/container-queries": ["@tailwindcss/container-queries@0.1.1", "", { "peerDependencies": { "tailwindcss": ">=3.2.0" } }, "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA=="],
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="],
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="], "@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.10.8", "", {}, "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA=="], "@tanstack/virtual-core": ["@tanstack/virtual-core@3.10.8", "", {}, "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA=="],
@@ -1301,6 +1333,8 @@
"@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="], "@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
"@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
"@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
"@types/object-hash": ["@types/object-hash@3.0.6", "", {}, "sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w=="], "@types/object-hash": ["@types/object-hash@3.0.6", "", {}, "sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w=="],
@@ -1407,6 +1441,8 @@
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"ai": ["ai@4.3.0", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "@ai-sdk/provider-utils": "2.2.4", "@ai-sdk/react": "1.2.6", "@ai-sdk/ui-utils": "1.2.5", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-PxyQYKhWaU3LiZEpeKRaekVonZIbWdKAwgnqm0CSAxy1MFufmYEC5SM5Mc9uiK2DoHcbAL3d1jyaQ2fSDAJL8w=="], "ai": ["ai@4.3.0", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "@ai-sdk/provider-utils": "2.2.4", "@ai-sdk/react": "1.2.6", "@ai-sdk/ui-utils": "1.2.5", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-PxyQYKhWaU3LiZEpeKRaekVonZIbWdKAwgnqm0CSAxy1MFufmYEC5SM5Mc9uiK2DoHcbAL3d1jyaQ2fSDAJL8w=="],
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
@@ -1429,7 +1465,7 @@
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], "arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
@@ -1453,8 +1489,6 @@
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"autoprefixer": ["autoprefixer@10.4.20", "", { "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g=="],
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
"axios": ["axios@1.8.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw=="], "axios": ["axios@1.8.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw=="],
@@ -1489,8 +1523,6 @@
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"browserslist": ["browserslist@4.24.0", "", { "dependencies": { "caniuse-lite": "^1.0.30001663", "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A=="],
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
@@ -1549,8 +1581,12 @@
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
"cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
"clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="], "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="],
"cloudflare": ["cloudflare@4.5.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"code-block-writer": ["code-block-writer@10.1.1", "", {}, "sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw=="], "code-block-writer": ["code-block-writer@10.1.1", "", {}, "sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw=="],
@@ -1653,7 +1689,7 @@
"detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="],
"detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
@@ -1697,8 +1733,6 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"electron-to-chromium": ["electron-to-chromium@1.5.36", "", {}, "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw=="],
"emoji-assets": ["emoji-assets@8.0.0", "", {}, "sha512-Y2M8Y5N6AUxKtE7AYj61+V3tU1CAcLQLX4kCO7HJEH1K+o2lgFd3ieiGSbZ8/qfz6qYxr0NUY+5Ds0WQer85og=="], "emoji-assets": ["emoji-assets@8.0.0", "", {}, "sha512-Y2M8Y5N6AUxKtE7AYj61+V3tU1CAcLQLX4kCO7HJEH1K+o2lgFd3ieiGSbZ8/qfz6qYxr0NUY+5Ds0WQer85og=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@@ -1709,6 +1743,8 @@
"end-of-stream": ["end-of-stream@1.1.0", "", { "dependencies": { "once": "~1.3.0" } }, "sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ=="], "end-of-stream": ["end-of-stream@1.1.0", "", { "dependencies": { "once": "~1.3.0" } }, "sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ=="],
"enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
@@ -1823,11 +1859,13 @@
"form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="], "form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"framer-motion": ["framer-motion@10.18.0", "", { "dependencies": { "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w=="], "framer-motion": ["framer-motion@10.18.0", "", { "dependencies": { "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w=="],
@@ -1851,6 +1889,10 @@
"generic-pool": ["generic-pool@3.4.2", "", {}, "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag=="], "generic-pool": ["generic-pool@3.4.2", "", {}, "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
"get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="],
"get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="], "get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
@@ -1865,7 +1907,7 @@
"glob": ["glob@11.0.1", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw=="], "glob": ["glob@11.0.1", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
@@ -1979,6 +2021,8 @@
"human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="], "human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="],
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
"humanize-url": ["humanize-url@2.1.1", "", { "dependencies": { "normalize-url": "^4.5.1" } }, "sha512-V4nxsPGNE7mPjr1qDp471YfW8nhBiTRWrG/4usZlpvFU8I7gsV7Jvrrzv/snbLm5dWO3dr1ennu2YqnhTWFmYA=="], "humanize-url": ["humanize-url@2.1.1", "", { "dependencies": { "normalize-url": "^4.5.1" } }, "sha512-V4nxsPGNE7mPjr1qDp471YfW8nhBiTRWrG/4usZlpvFU8I7gsV7Jvrrzv/snbLm5dWO3dr1ennu2YqnhTWFmYA=="],
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
@@ -2059,7 +2103,7 @@
"jackspeak": ["jackspeak@4.0.2", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw=="], "jackspeak": ["jackspeak@4.0.2", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw=="],
"jiti": ["jiti@1.21.6", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w=="], "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
"js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="], "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="],
@@ -2117,6 +2161,28 @@
"leven": ["leven@4.0.0", "", {}, "sha512-puehA3YKku3osqPlNuzGDUHq8WpwXupUg1V6NXdV38G+gr+gkBwFC8g1b/+YcIvp8gnqVIus+eJCH/eGsRmJNw=="], "leven": ["leven@4.0.0", "", {}, "sha512-puehA3YKku3osqPlNuzGDUHq8WpwXupUg1V6NXdV38G+gr+gkBwFC8g1b/+YcIvp8gnqVIus+eJCH/eGsRmJNw=="],
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
@@ -2163,7 +2229,7 @@
"lru-queue": ["lru-queue@0.1.0", "", { "dependencies": { "es5-ext": "~0.10.2" } }, "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ=="], "lru-queue": ["lru-queue@0.1.0", "", { "dependencies": { "es5-ext": "~0.10.2" } }, "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ=="],
"magic-string": ["magic-string@0.30.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw=="], "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="],
@@ -2331,22 +2397,20 @@
"next-tick": ["next-tick@1.1.0", "", {}, "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="], "next-tick": ["next-tick@1.1.0", "", {}, "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="], "node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
"node-forge": ["node-forge@0.10.0", "", {}, "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="], "node-forge": ["node-forge@0.10.0", "", {}, "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="],
"node-gyp-build": ["node-gyp-build@4.8.2", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw=="], "node-gyp-build": ["node-gyp-build@4.8.2", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw=="],
"node-releases": ["node-releases@2.0.18", "", {}, "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="],
"nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], "nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="],
"normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], "normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
"normalize-url": ["normalize-url@4.5.1", "", {}, "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="], "normalize-url": ["normalize-url@4.5.1", "", {}, "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="],
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
@@ -2723,7 +2787,9 @@
"tailwind-shades": ["tailwind-shades@1.1.2", "", {}, "sha512-gsyyr9NtfPS1QNWV/YQMcoO5tLStTwp/So2I6jnPWmCFmqY8JEM1csBoq0nbFtuRsjYypC/Bm09pprUyLL/Zjg=="], "tailwind-shades": ["tailwind-shades@1.1.2", "", {}, "sha512-gsyyr9NtfPS1QNWV/YQMcoO5tLStTwp/So2I6jnPWmCFmqY8JEM1csBoq0nbFtuRsjYypC/Bm09pprUyLL/Zjg=="],
"tailwindcss": ["tailwindcss@3.4.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw=="], "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
"tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
"tar": ["tar@4.4.18", "", { "dependencies": { "chownr": "^1.1.4", "fs-minipass": "^1.2.7", "minipass": "^2.9.0", "minizlib": "^1.3.3", "mkdirp": "^0.5.5", "safe-buffer": "^5.2.1", "yallist": "^3.1.1" } }, "sha512-ZuOtqqmkV9RE1+4odd+MhBpibmCxNP6PJhH/h2OqNuotTX7/XHPZQJv2pKvWMplFH9SIZZhitehh6vBH6LO8Pg=="], "tar": ["tar@4.4.18", "", { "dependencies": { "chownr": "^1.1.4", "fs-minipass": "^1.2.7", "minipass": "^2.9.0", "minizlib": "^1.3.3", "mkdirp": "^0.5.5", "safe-buffer": "^5.2.1", "yallist": "^3.1.1" } }, "sha512-ZuOtqqmkV9RE1+4odd+MhBpibmCxNP6PJhH/h2OqNuotTX7/XHPZQJv2pKvWMplFH9SIZZhitehh6vBH6LO8Pg=="],
@@ -2841,8 +2907,6 @@
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"update-browserslist-db": ["update-browserslist-db@1.1.1", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A=="],
"update-notifier": ["update-notifier@4.1.3", "", { "dependencies": { "boxen": "^4.2.0", "chalk": "^3.0.0", "configstore": "^5.0.1", "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", "is-ci": "^2.0.0", "is-installed-globally": "^0.3.1", "is-npm": "^4.0.0", "is-yarn-global": "^0.3.0", "latest-version": "^5.0.0", "pupa": "^2.0.1", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" } }, "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A=="], "update-notifier": ["update-notifier@4.1.3", "", { "dependencies": { "boxen": "^4.2.0", "chalk": "^3.0.0", "configstore": "^5.0.1", "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", "is-ci": "^2.0.0", "is-installed-globally": "^0.3.1", "is-npm": "^4.0.0", "is-yarn-global": "^0.3.0", "latest-version": "^5.0.0", "pupa": "^2.0.1", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" } }, "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
@@ -2897,6 +2961,8 @@
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
"web-vitals": ["web-vitals@0.2.4", "", {}, "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg=="], "web-vitals": ["web-vitals@0.2.4", "", {}, "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
@@ -2913,7 +2979,7 @@
"wrangler": ["wrangler@4.10.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.1", "blake3-wasm": "2.1.5", "esbuild": "0.24.2", "miniflare": "4.20250409.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.15", "workerd": "1.20250409.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250409.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-fTE4hZ79msEUt8+HEjl/8Q72haCyzPLu4PgrU3L81ysmjrMEdiYfUPqnvCkBUVtJvrDNdctTEimkufT1Y0ipNg=="], "wrangler": ["wrangler@4.10.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.1", "blake3-wasm": "2.1.5", "esbuild": "0.24.2", "miniflare": "4.20250409.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.15", "workerd": "1.20250409.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250409.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-fTE4hZ79msEUt8+HEjl/8Q72haCyzPLu4PgrU3L81ysmjrMEdiYfUPqnvCkBUVtJvrDNdctTEimkufT1Y0ipNg=="],
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
@@ -2929,10 +2995,14 @@
"xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="], "xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], "yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="],
"yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
"yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], "yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
@@ -3607,12 +3677,16 @@
"@formatjs/intl-localematcher/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@formatjs/intl-localematcher/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@headlessui/tailwindcss/tailwindcss": ["tailwindcss@3.4.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw=="],
"@hyperjump/json-schema/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@hyperjump/json-schema/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
"@lezer/css/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="], "@lezer/css/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
"@lezer/highlight/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="], "@lezer/highlight/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
@@ -3637,6 +3711,8 @@
"@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
"@mapbox/node-pre-gyp/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"@mapbox/node-pre-gyp/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=="], "@mapbox/node-pre-gyp/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=="],
"@mapbox/node-pre-gyp/tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], "@mapbox/node-pre-gyp/tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
@@ -3647,6 +3723,8 @@
"@opennextjs/aws/path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], "@opennextjs/aws/path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
"@playwright/test/playwright": ["playwright@1.54.2", "", { "dependencies": { "playwright-core": "1.54.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw=="],
"@radix-ui/react-collection/@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-collection/@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-collection/@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-collection/@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=="],
@@ -3965,6 +4043,20 @@
"@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tailwindcss/oxide/tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" }, "bundled": true }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], "@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
"@ts-morph/common/fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="], "@ts-morph/common/fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="],
@@ -4013,6 +4105,8 @@
"@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"@vue/compiler-sfc/magic-string": ["magic-string@0.30.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw=="],
"@vue/compiler-sfc/postcss": ["postcss@8.4.47", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" } }, "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ=="], "@vue/compiler-sfc/postcss": ["postcss@8.4.47", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" } }, "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ=="],
"@vueuse/integrations/@vueuse/core": ["@vueuse/core@11.2.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "11.2.0", "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" } }, "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA=="], "@vueuse/integrations/@vueuse/core": ["@vueuse/core@11.2.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "11.2.0", "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" } }, "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA=="],
@@ -4049,6 +4143,14 @@
"camelcase-keys/quick-lru": ["quick-lru@4.0.1", "", {}, "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="], "camelcase-keys/quick-lru": ["quick-lru@4.0.1", "", {}, "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="],
"cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"cloudflare/@types/node": ["@types/node@18.19.121", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ=="],
"cloudflare/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=="],
"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/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=="], "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=="],
@@ -4085,8 +4187,6 @@
"express/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], "express/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
@@ -4155,8 +4255,6 @@
"meow/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], "meow/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
"micro/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
"micro/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="], "micro/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="],
"micromark/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "micromark/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
@@ -4241,6 +4339,8 @@
"send/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "send/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"sharp/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], "sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], "simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
@@ -4255,14 +4355,6 @@
"sucrase/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=="], "sucrase/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=="],
"tailwindcss/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=="],
"tailwindcss/fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="],
"tailwindcss/postcss": ["postcss@8.4.47", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" } }, "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ=="],
"tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
"tar/minipass": ["minipass@2.9.0", "", { "dependencies": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg=="], "tar/minipass": ["minipass@2.9.0", "", { "dependencies": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg=="],
"terminal-link/supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="], "terminal-link/supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="],
@@ -4271,8 +4363,6 @@
"ts-node/acorn": ["acorn@8.12.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="], "ts-node/acorn": ["acorn@8.12.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="],
"ts-node/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
"unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"update-notifier/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], "update-notifier/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
@@ -4287,10 +4377,14 @@
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"yargs/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"yargs/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
"youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"@argos-ci/core/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "@argos-ci/core/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
@@ -4331,6 +4425,8 @@
"@argos-ci/core/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], "@argos-ci/core/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"@argos-ci/core/sharp/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"@aws-crypto/crc32/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="], "@aws-crypto/crc32/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="],
"@aws-crypto/crc32c/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="], "@aws-crypto/crc32c/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="],
@@ -4765,10 +4861,26 @@
"@dotenvx/dotenvx/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
"@headlessui/tailwindcss/tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
"@headlessui/tailwindcss/tailwindcss/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=="],
"@headlessui/tailwindcss/tailwindcss/fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="],
"@headlessui/tailwindcss/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"@headlessui/tailwindcss/tailwindcss/jiti": ["jiti@1.21.6", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w=="],
"@headlessui/tailwindcss/tailwindcss/postcss": ["postcss@8.4.47", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" } }, "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ=="],
"@headlessui/tailwindcss/tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"@mapbox/node-pre-gyp/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "@mapbox/node-pre-gyp/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"@mapbox/node-pre-gyp/tar/minizlib": ["minizlib@3.0.1", "", { "dependencies": { "minipass": "^7.0.4", "rimraf": "^5.0.5" } }, "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg=="], "@mapbox/node-pre-gyp/tar/minizlib": ["minizlib@3.0.1", "", { "dependencies": { "minipass": "^7.0.4", "rimraf": "^5.0.5" } }, "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg=="],
@@ -4783,6 +4895,8 @@
"@node-minify/core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "@node-minify/core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"@playwright/test/playwright/playwright-core": ["playwright-core@1.54.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA=="],
"@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-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-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=="],
@@ -4871,7 +4985,15 @@
"@smithy/util-endpoints/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="], "@smithy/util-endpoints/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="],
"@ts-morph/common/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
"@tailwindcss/oxide/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"@tailwindcss/oxide/tar/minizlib": ["minizlib@3.0.1", "", { "dependencies": { "minipass": "^7.0.4", "rimraf": "^5.0.5" } }, "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg=="],
"@tailwindcss/oxide/tar/mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
"@tailwindcss/oxide/tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
@@ -4909,6 +5031,12 @@
"bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"cliui/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
"cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"codemirror/@codemirror/autocomplete/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="], "codemirror/@codemirror/autocomplete/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
"codemirror/@codemirror/commands/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="], "codemirror/@codemirror/commands/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
@@ -4929,8 +5057,6 @@
"gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"globby/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"google-auth-library/jws/jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="], "google-auth-library/jws/jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="],
"gtoken/jws/jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="], "gtoken/jws/jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="],
@@ -4969,16 +5095,6 @@
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"tailwindcss/chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
"tailwindcss/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"tailwindcss/postcss/nanoid": ["nanoid@3.3.7", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="],
"wrangler/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "wrangler/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
"wrangler/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], "wrangler/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
@@ -5017,10 +5133,16 @@
"wrangler/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], "wrangler/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "wrangler/sharp/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"yargs/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
"yargs/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"@argos-ci/core/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.3.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw=="], "@argos-ci/core/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.3.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw=="],
"@aws-sdk/client-dynamodb/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-sdk/client-dynamodb/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
@@ -5157,6 +5279,16 @@
"@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], "@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
"@headlessui/tailwindcss/tailwindcss/chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"@headlessui/tailwindcss/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"@headlessui/tailwindcss/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
"@headlessui/tailwindcss/tailwindcss/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"@headlessui/tailwindcss/tailwindcss/postcss/nanoid": ["nanoid@3.3.7", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="],
"@node-minify/core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@node-minify/core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"@node-minify/core/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], "@node-minify/core/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
@@ -5181,6 +5313,8 @@
"wrangler/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.3.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw=="], "wrangler/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.3.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw=="],
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"@argos-ci/core/sharp/@img/sharp-wasm32/@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@argos-ci/core/sharp/@img/sharp-wasm32/@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="], "@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="],
+1 -1
View File
@@ -34,7 +34,7 @@
"workspaces": { "workspaces": {
"packages": ["packages/*"], "packages": ["packages/*"],
"catalog": { "catalog": {
"@gitbook/api": "^0.129.0" "@gitbook/api": "^0.130.0"
} }
}, },
"patchedDependencies": { "patchedDependencies": {
+26
View File
@@ -1,5 +1,31 @@
# gitbook # gitbook
## 0.15.0
### Minor Changes
- d130532: Suggest questions in the current space context
### Patch Changes
- 44f4151: Fix close buttons tooltips
- d903273: Strip visitor params from URL
- df7de8f: Fix an issue where links were not rendering properly in Ask AI answers
- d270b4a: All tracking is now disabled for dynamic routes such as the site preview.
- b1f608b: Implement basic URL scheme for assistant with ask
- d024658: Prevent page breaks inside blocks when printing
- 185cdb4: Fix scrolling to anchor positionning
- 5c49235: Remove highlighting in Safari for PowerShell and C++ to avoid page crash until next version with bug fix is released
- f1a6dec: Update OpenAPI parser
- 2cdba53: Upgrade to Tailwind v4
- Updated dependencies [2cdba53]
- Updated dependencies [f1a6dec]
- @gitbook/react-math@0.6.1
- @gitbook/icons@0.2.2
- @gitbook/openapi-parser@3.0.0
- @gitbook/react-contentkit@0.7.3
- @gitbook/react-openapi@1.3.5
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+248 -56
View File
@@ -1,4 +1,5 @@
import { import {
CustomizationAIMode,
CustomizationBackground, CustomizationBackground,
CustomizationCorners, CustomizationCorners,
CustomizationDefaultMonospaceFont, CustomizationDefaultMonospaceFont,
@@ -19,6 +20,7 @@ import {
import { getSiteAPIToken } from '../tests/utils'; import { getSiteAPIToken } from '../tests/utils';
import { import {
type Test,
type TestsCase, type TestsCase,
allDeprecatedThemePresets, allDeprecatedThemePresets,
allLocales, allLocales,
@@ -34,6 +36,250 @@ import {
waitForNotFound, waitForNotFound,
} from './util'; } from './util';
const searchTestCases: Test[] = [
{
name: 'Search - AI Mode: None - Complete flow',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.None,
},
}),
screenshot: false,
run: async (page) => {
const searchInput = page.getByTestId('search-input');
await searchInput.focus();
await expect(page.getByTestId('search-results')).toHaveCount(0); // No pop-up yet because there's no recommended questions.
// Fill search input, expecting search results
await searchInput.fill('gitbook');
await expect(page.getByTestId('search-results')).toBeVisible();
const pageResults = await page.getByTestId('search-page-result').all();
await expect(pageResults.length).toBeGreaterThan(2);
const pageSectionResults = await page.getByTestId('search-page-section-result').all();
await expect(pageSectionResults.length).toBeGreaterThan(2);
await expect(page.getByTestId('search-ask-question')).toHaveCount(0); // No AI search results with aiMode=None.
},
},
{
name: 'Search - AI Mode: None - Keyboard shortcut',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.None,
},
}),
screenshot: false,
run: async (page) => {
await page.keyboard.press('ControlOrMeta+K');
await expect(page.getByTestId('search-input')).toBeFocused();
},
},
{
name: 'Search - AI Mode: None - URL query (Initial)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.None,
},
})}&q=`,
run: async (page) => {
await expect(page.getByTestId('search-results')).toHaveCount(0); // No pop-up yet because there's no recommended questions.
},
},
{
name: 'Search - AI Mode: None - URL query (Results)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.None,
},
})}&q=gitbook`,
run: async (page) => {
await expect(page.getByTestId('search-input')).toBeFocused();
await expect(page.getByTestId('search-input')).toHaveValue('gitbook');
await expect(page.getByTestId('search-results')).toBeVisible();
},
},
{
name: 'Search - AI Mode: Search - Complete flow',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.Search,
},
}),
screenshot: false,
run: async (page) => {
const searchInput = page.getByTestId('search-input');
// Focus search input, expecting recommended questions
await searchInput.focus();
await expect(page.getByTestId('search-results')).toBeVisible();
const recommendedQuestions = await page
.getByTestId('search-recommended-question')
.all();
await expect(recommendedQuestions.length).toBeGreaterThan(2); // Expect at least 3 questions
// Fill search input, expecting AI search option
await searchInput.fill('What is gitbook?');
await expect(page.getByTestId('search-results')).toBeVisible();
const aiSearchResult = page.getByTestId('search-ask-question');
await expect(aiSearchResult).toBeVisible();
await aiSearchResult.click();
await expect(page.getByTestId('search-ask-answer')).toBeVisible({
timeout: 15_000,
});
},
},
{
name: 'Search - AI Mode: Search - URL query (Initial)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Search,
},
})}&q=`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).toBeFocused();
await expect(page.getByTestId('search-results')).toBeVisible();
const recommendedQuestions = await page
.getByTestId('search-recommended-question')
.all();
await expect(recommendedQuestions.length).toBeGreaterThan(2); // Expect at least 3 questions
},
},
{
name: 'Search - AI Mode: Search - URL query (Results)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Search,
},
})}&q=gitbook`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).toBeFocused();
await expect(page.getByTestId('search-input')).toHaveValue('gitbook');
await expect(page.getByTestId('search-results')).toBeVisible();
},
},
{
name: 'Ask - AI Mode: Search - URL query (Ask initial)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Search,
},
})}&ask=`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).toBeFocused();
await expect(page.getByTestId('search-results')).toBeVisible();
const recommendedQuestions = await page
.getByTestId('search-recommended-question')
.all();
await expect(recommendedQuestions.length).toBeGreaterThan(2); // Expect at least 3 questions
},
},
{
name: 'Ask - AI Mode: Search - URL query (Ask results)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Search,
},
})}&ask=What+is+GitBook%3F`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).toBeFocused();
await expect(page.getByTestId('search-input')).toHaveValue('What is GitBook?');
await expect(page.getByTestId('search-ask-answer')).toBeVisible();
},
},
{
name: 'Ask - AI Mode: Assistant - Complete flow',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.Assistant,
},
}),
screenshot: false,
run: async (page) => {
const searchInput = page.locator('css=[data-testid="search-input"]');
// Focus search input, expecting recommended questions
await searchInput.focus();
await expect(page.getByTestId('search-results')).toBeVisible();
const recommendedQuestions = await page
.getByTestId('search-recommended-question')
.all();
await expect(recommendedQuestions.length).toBeGreaterThan(2); // Expect at least 3 questions
// Fill search input, expecting AI search option
await searchInput.fill('What is gitbook?');
const aiSearchResult = page.getByTestId('search-ask-question');
await expect(aiSearchResult).toBeVisible();
await aiSearchResult.click();
await expect(page.getByTestId('ai-chat')).toBeVisible();
},
},
{
name: 'Ask - AI Mode: Assistant - Keyboard shortcut',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.Assistant,
},
}),
screenshot: false,
run: async (page) => {
await page.keyboard.press('ControlOrMeta+J');
await expect(page.getByTestId('ai-chat')).toBeVisible();
await expect(page.getByTestId('ai-chat-input')).toBeFocused();
},
},
{
name: 'Ask - AI Mode: Assistant - Button',
url: getCustomizationURL({
ai: {
mode: CustomizationAIMode.Assistant,
},
}),
screenshot: false,
run: async (page) => {
await page.getByTestId('ai-chat-button').click();
await expect(page.getByTestId('ai-chat')).toBeVisible();
await expect(page.getByTestId('ai-chat-input')).toBeFocused();
},
},
{
name: 'Ask - AI Mode: Assistant - URL query (Initial)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Assistant,
},
})}&ask=`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).not.toBeFocused();
await expect(page.getByTestId('search-input')).not.toHaveValue('What is GitBook?');
await expect(page.getByTestId('ai-chat')).toBeVisible();
await expect(page.getByTestId('ai-chat-input')).toBeFocused();
},
},
{
name: 'Ask - AI Mode: Assistant - URL query (Results)',
url: `${getCustomizationURL({
ai: {
mode: CustomizationAIMode.Assistant,
},
})}&ask=What+is+GitBook%3F`,
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-input')).not.toBeFocused();
await expect(page.getByTestId('search-input')).not.toHaveValue('What is GitBook?');
await expect(page.getByTestId('ai-chat')).toBeVisible({
timeout: 15_000,
});
await expect(page.getByTestId('ai-chat-message').first()).toHaveText(
'What is GitBook?'
);
},
},
];
const testCases: TestsCase[] = [ const testCases: TestsCase[] = [
{ {
name: 'GitBook Site (Single Variant)', name: 'GitBook Site (Single Variant)',
@@ -53,34 +299,7 @@ const testCases: TestsCase[] = [
); );
}, },
}, },
{ ...searchTestCases,
name: 'Search',
url: '?q=',
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-results')).toBeVisible();
const allItems = await page.getByTestId('search-result-item').all();
// Expect at least 3 questions
await expect(allItems.length).toBeGreaterThan(2);
},
},
{
name: 'Search Results',
url: '?q=gitbook',
run: async (page) => {
await expect(page.getByTestId('search-results')).toBeVisible();
},
},
{
name: 'AI Search',
url: '?q=What+is+GitBook%3F&ask=true',
run: async (page) => {
await expect(page.getByTestId('search-ask-answer')).toBeVisible({
timeout: 15_000,
});
},
screenshot: false,
},
{ {
name: 'Not found', name: 'Not found',
url: 'content-not-found', url: 'content-not-found',
@@ -279,34 +498,7 @@ const testCases: TestsCase[] = [
url: '', url: '',
run: waitForCookiesDialog, run: waitForCookiesDialog,
}, },
{ ...searchTestCases,
name: 'Search',
url: '?q=',
screenshot: false,
run: async (page) => {
await expect(page.getByTestId('search-results')).toBeVisible();
const allItems = await page.getByTestId('search-result-item').all();
// Expect at least 3 questions
await expect(allItems.length).toBeGreaterThan(2);
},
},
{
name: 'Search Results',
url: '?q=gitbook',
run: async (page) => {
await expect(page.getByTestId('search-results')).toBeVisible();
},
},
{
name: 'AI Search',
url: '?q=What+is+GitBook%3F&ask=true',
run: async (page) => {
await expect(page.getByTestId('search-ask-answer')).toBeVisible({
timeout: 15_000,
});
},
screenshot: false,
},
{ {
name: 'Not found', name: 'Not found',
url: 'content-not-found', url: 'content-not-found',
@@ -2,6 +2,9 @@ import { runWithCloudflareRequestContext } from '../../.open-next/cloudflare/ini
import { DurableObject } from 'cloudflare:workers'; import { DurableObject } from 'cloudflare:workers';
//Only needed to run locally, in prod we'll use the one from do.js
export { DOShardedTagCache } from '../../.open-next/.build/durable-objects/sharded-tag-cache.js';
// Only needed to run locally, in prod we'll use the one from do.js // Only needed to run locally, in prod we'll use the one from do.js
export class R2WriteBuffer extends DurableObject { export class R2WriteBuffer extends DurableObject {
writePromise; writePromise;
@@ -36,13 +36,17 @@
{ {
"name": "WRITE_BUFFER", "name": "WRITE_BUFFER",
"class_name": "R2WriteBuffer" "class_name": "R2WriteBuffer"
},
{
"name": "NEXT_TAG_CACHE_DO_SHARDED",
"class_name": "DOShardedTagCache"
} }
] ]
}, },
"migrations": [ "migrations": [
{ {
"tag": "v1", "tag": "v1",
"new_sqlite_classes": ["R2WriteBuffer"] "new_sqlite_classes": ["R2WriteBuffer", "DOShardedTagCache"]
} }
] ]
}, },
@@ -22,6 +22,9 @@
"vars": { "vars": {
"STAGE": "dev", "STAGE": "dev",
"NEXT_PRIVATE_DEBUG_CACHE": "true", "NEXT_PRIVATE_DEBUG_CACHE": "true",
// When deployed locally, we don't have access to the tag cache here,
// we should just bypass the cache to go to the server directly
"SHOULD_BYPASS_CACHE": "true",
"OPEN_NEXT_REQUEST_ID_HEADER": "true" "OPEN_NEXT_REQUEST_ID_HEADER": "true"
}, },
"r2_buckets": [ "r2_buckets": [
@@ -36,6 +36,11 @@ class GitbookIncrementalCache implements IncrementalCache {
const r2 = getCloudflareContext().env[BINDING_NAME]; const r2 = getCloudflareContext().env[BINDING_NAME];
const localCache = await this.getCacheInstance(); const localCache = await this.getCacheInstance();
if (!r2) throw new Error('No R2 bucket'); if (!r2) throw new Error('No R2 bucket');
if (process.env.SHOULD_BYPASS_CACHE === 'true') {
// We are in a local middleware environment, we should bypass the cache
// and go directly to the server.
return null;
}
try { try {
// Check local cache first if available // Check local cache first if available
const localCacheEntry = await localCache.match(this.getCacheUrlKey(cacheKey)); const localCacheEntry = await localCache.match(this.getCacheUrlKey(cacheKey));
@@ -134,6 +139,7 @@ class GitbookIncrementalCache implements IncrementalCache {
} }
async writeToR2(key: string, value: string): Promise<void> { async writeToR2(key: string, value: string): Promise<void> {
try {
const env = getCloudflareContext().env as { const env = getCloudflareContext().env as {
WRITE_BUFFER: DurableObjectNamespace< WRITE_BUFFER: DurableObjectNamespace<
Rpc.DurableObjectBranded & { Rpc.DurableObjectBranded & {
@@ -147,6 +153,13 @@ class GitbookIncrementalCache implements IncrementalCache {
const stub = env.WRITE_BUFFER.get(id); const stub = env.WRITE_BUFFER.get(id);
await stub.write(key, value); await stub.write(key, value);
} catch {
// We fallback to writing directly to R2
// it can fail locally because the limit is 1Mb per args
// It is 32Mb in production, so we should be fine
const r2 = getCloudflareContext().env[BINDING_NAME];
r2?.put(key, value);
}
} }
async getCacheInstance(): Promise<Cache> { async getCacheInstance(): Promise<Cache> {
@@ -1,4 +1,4 @@
import { getLogger } from '@/lib/logger'; import { createLogger, getLogger } from '@/lib/logger';
import type { NextModeTagCache } from '@opennextjs/aws/types/overrides.js'; import type { NextModeTagCache } from '@opennextjs/aws/types/overrides.js';
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache'; import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
import { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter'; import { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter';
@@ -6,8 +6,9 @@ import { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-ca
const originalTagCache = doShardedTagCache({ const originalTagCache = doShardedTagCache({
baseShardSize: 12, baseShardSize: 12,
regionalCache: true, regionalCache: true,
// We can probably increase this value even further regionalCacheTtlSec: 60 * 5 /* 5 minutes */,
regionalCacheTtlSec: 60, // Because we invalidate the Cache API on update, we can safely set this to true
regionalCacheDangerouslyPersistMissingTags: true,
shardReplication: { shardReplication: {
numberOfSoftReplicas: 2, numberOfSoftReplicas: 2,
numberOfHardReplicas: 1, numberOfHardReplicas: 1,
@@ -20,6 +21,7 @@ const originalTagCache = doShardedTagCache({
export default { export default {
name: 'GitbookTagCache', name: 'GitbookTagCache',
mode: 'nextMode', mode: 'nextMode',
// We don't really use this one, as of now it is only used for soft tags
getLastRevalidated: async (tags: string[]) => { getLastRevalidated: async (tags: string[]) => {
const tagsToCheck = tags.filter(softTagFilter); const tagsToCheck = tags.filter(softTagFilter);
if (tagsToCheck.length === 0) { if (tagsToCheck.length === 0) {
@@ -29,12 +31,20 @@ export default {
return await originalTagCache.getLastRevalidated(tagsToCheck); return await originalTagCache.getLastRevalidated(tagsToCheck);
}, },
hasBeenRevalidated: async (tags: string[], lastModified?: number) => { hasBeenRevalidated: async (tags: string[], lastModified?: number) => {
try {
const tagsToCheck = tags.filter(softTagFilter); const tagsToCheck = tags.filter(softTagFilter);
if (tagsToCheck.length === 0) { if (tagsToCheck.length === 0) {
return false; // If no tags to check, return false return false; // If no tags to check, return false
} }
return await originalTagCache.hasBeenRevalidated(tagsToCheck, lastModified); return await originalTagCache.hasBeenRevalidated(tagsToCheck, lastModified);
} catch (e) {
createLogger('gitbookTagCache', {}).error(
`hasBeenRevalidated - Error checking tags ${tags.join(', ')}`,
e
);
return false; // In case of error, return false
}
}, },
writeTags: async (tags: string[]) => { writeTags: async (tags: string[]) => {
const tagsToWrite = tags.filter(softTagFilter); const tagsToWrite = tags.filter(softTagFilter);
@@ -43,7 +53,7 @@ export default {
logger.warn('writeTags - No valid tags to write'); logger.warn('writeTags - No valid tags to write');
return; // If no tags to write, exit early return; // If no tags to write, exit early
} }
// Write only the filtered tags
await originalTagCache.writeTags(tagsToWrite); await originalTagCache.writeTags(tagsToWrite);
}, },
} satisfies NextModeTagCache; } satisfies NextModeTagCache;
+10 -10
View File
@@ -1,6 +1,6 @@
{ {
"name": "gitbook", "name": "gitbook",
"version": "0.14.1", "version": "0.15.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@gitbook/api": "catalog:", "@gitbook/api": "catalog:",
@@ -13,7 +13,7 @@
"@gitbook/react-contentkit": "workspace:*", "@gitbook/react-contentkit": "workspace:*",
"@gitbook/react-math": "workspace:*", "@gitbook/react-math": "workspace:*",
"@gitbook/react-openapi": "workspace:*", "@gitbook/react-openapi": "workspace:*",
"@opennextjs/cloudflare": "^1.4.0", "@opennextjs/cloudflare": "^1.6.2",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.1.12", "@radix-ui/react-dropdown-menu": "^2.1.12",
"@radix-ui/react-navigation-menu": "^1.2.3", "@radix-ui/react-navigation-menu": "^1.2.3",
@@ -22,6 +22,7 @@
"@sindresorhus/fnv1a": "^3.1.0", "@sindresorhus/fnv1a": "^3.1.0",
"@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tusbar/cache-control": "^1.0.2",
"ai": "^4.2.2", "ai": "^4.2.2",
"assert-never": "^1.2.1", "assert-never": "^1.2.1",
"bun-types": "^1.1.20", "bun-types": "^1.1.20",
@@ -49,7 +50,7 @@
"object-identity": "^0.1.2", "object-identity": "^0.1.2",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"p-map": "^7.0.3", "p-map": "^7.0.3",
"@tusbar/cache-control": "^1.0.2", "quick-lru": "^7.0.1",
"react-hotkeys-hook": "^4.4.1", "react-hotkeys-hook": "^4.4.1",
"rehype-sanitize": "^6.0.0", "rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
@@ -67,12 +68,12 @@
"url-join": "^5.0.0", "url-join": "^5.0.0",
"usehooks-ts": "^3.1.0", "usehooks-ts": "^3.1.0",
"warn-once": "^0.1.1", "warn-once": "^0.1.1",
"zustand": "^5.0.3", "zustand": "^5.0.3"
"quick-lru": "^7.0.1"
}, },
"devDependencies": { "devDependencies": {
"@argos-ci/playwright": "^5.0.5", "@argos-ci/playwright": "^5.0.9",
"@playwright/test": "^1.51.1", "@playwright/test": "^1.54.2",
"@tailwindcss/postcss": "^4.1.11",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/jsontoxml": "^1.0.5", "@types/jsontoxml": "^1.0.5",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
@@ -84,14 +85,13 @@
"@types/react": "18.3.13", "@types/react": "18.3.13",
"@types/react-dom": "18.3.1", "@types/react-dom": "18.3.1",
"@types/rison": "^0.0.9", "@types/rison": "^0.0.9",
"autoprefixer": "^10",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"env-cmd": "^10.1.0", "env-cmd": "^10.1.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"postcss": "^8", "postcss": "^8",
"psi": "^4.1.0", "psi": "^4.1.0",
"stylelint": "^16.16.0", "stylelint": "^16.16.0",
"tailwindcss": "^3.4.0", "tailwindcss": "^4.1.11",
"ts-essentials": "^10.0.1", "ts-essentials": "^10.0.1",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"vercel": "^39.3.0" "vercel": "^39.3.0"
@@ -100,7 +100,7 @@
"generate": "gitbook-icons ./public/~gitbook/static/icons custom-icons && gitbook-math ./public/~gitbook/static/math && wrangler types", "generate": "gitbook-icons ./public/~gitbook/static/icons custom-icons && gitbook-math ./public/~gitbook/static/math && wrangler types",
"copy:icons": "gitbook-icons ./public/~gitbook/static/icons", "copy:icons": "gitbook-icons ./public/~gitbook/static/icons",
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math", "clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math",
"dev": "env-cmd --silent -f ../../.env.local next --turbopack", "dev": "env-cmd --silent -f ../../.env.local next",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"build:cloudflare": "opennextjs-cloudflare build", "build:cloudflare": "opennextjs-cloudflare build",
+1 -2
View File
@@ -1,6 +1,5 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, '@tailwindcss/postcss': {},
autoprefixer: {},
}, },
}; };
@@ -5,8 +5,9 @@ import {
generateSiteLayoutMetadata, generateSiteLayoutMetadata,
generateSiteLayoutViewport, generateSiteLayoutViewport,
} from '@/components/SiteLayout'; } from '@/components/SiteLayout';
import { GITBOOK_DISABLE_TRACKING } from '@/lib/env';
import { getThemeFromMiddleware } from '@/lib/middleware'; import { getThemeFromMiddleware } from '@/lib/middleware';
import { shouldTrackEvents } from '@/lib/tracking';
import { headers } from 'next/headers';
interface SiteDynamicLayoutProps { interface SiteDynamicLayoutProps {
params: Promise<RouteLayoutParams>; params: Promise<RouteLayoutParams>;
@@ -18,13 +19,14 @@ export default async function SiteDynamicLayout({
}: React.PropsWithChildren<SiteDynamicLayoutProps>) { }: React.PropsWithChildren<SiteDynamicLayoutProps>) {
const { context, visitorAuthClaims } = await getDynamicSiteContext(await params); const { context, visitorAuthClaims } = await getDynamicSiteContext(await params);
const forcedTheme = await getThemeFromMiddleware(); const forcedTheme = await getThemeFromMiddleware();
const withTracking = shouldTrackEvents(await headers());
return ( return (
<CustomizationRootLayout forcedTheme={forcedTheme} customization={context.customization}> <CustomizationRootLayout forcedTheme={forcedTheme} customization={context.customization}>
<SiteLayout <SiteLayout
context={context} context={context}
forcedTheme={forcedTheme} forcedTheme={forcedTheme}
withTracking={!GITBOOK_DISABLE_TRACKING} withTracking={withTracking}
visitorAuthClaims={visitorAuthClaims} visitorAuthClaims={visitorAuthClaims}
> >
{children} {children}
@@ -5,7 +5,7 @@ import {
generateSiteLayoutMetadata, generateSiteLayoutMetadata,
generateSiteLayoutViewport, generateSiteLayoutViewport,
} from '@/components/SiteLayout'; } from '@/components/SiteLayout';
import { GITBOOK_DISABLE_TRACKING } from '@/lib/env'; import { shouldTrackEvents } from '@/lib/tracking';
interface SiteStaticLayoutProps { interface SiteStaticLayoutProps {
params: Promise<RouteLayoutParams>; params: Promise<RouteLayoutParams>;
@@ -16,12 +16,13 @@ export default async function SiteStaticLayout({
children, children,
}: React.PropsWithChildren<SiteStaticLayoutProps>) { }: React.PropsWithChildren<SiteStaticLayoutProps>) {
const { context, visitorAuthClaims } = await getStaticSiteContext(await params); const { context, visitorAuthClaims } = await getStaticSiteContext(await params);
const withTracking = shouldTrackEvents();
return ( return (
<CustomizationRootLayout customization={context.customization}> <CustomizationRootLayout customization={context.customization}>
<SiteLayout <SiteLayout
context={context} context={context}
withTracking={!GITBOOK_DISABLE_TRACKING} withTracking={withTracking}
visitorAuthClaims={visitorAuthClaims} visitorAuthClaims={visitorAuthClaims}
> >
{children} {children}
@@ -23,7 +23,7 @@ export function AIMessageView(
<div <div
key={index} key={index}
className={tcls( className={tcls(
'flex animate-fadeIn-slow flex-col gap-2', 'flex animate-fade-in-slow flex-col gap-2',
step.content.nodes.length > 0 ? 'has-content' : '' step.content.nodes.length > 0 ? 'has-content' : ''
)} )}
> >
@@ -38,7 +38,7 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
const { toolCall, context } = props; const { toolCall, context } = props;
return ( return (
<div className="flex origin-left animate-scaleIn-slow items-start gap-2 text-sm text-tint-subtle"> <div className="flex origin-left animate-scale-in-slow items-start gap-2 text-sm text-tint-subtle">
<Icon <Icon
icon={getIconForToolCall(toolCall)} icon={getIconForToolCall(toolCall)}
className="mt-1 size-3 shrink-0 text-tint-subtle/8" className="mt-1 size-3 shrink-0 text-tint-subtle/8"
@@ -175,12 +175,12 @@ async function DescriptionForSearchToolCall(props: {
) : null} ) : null}
</summary> </summary>
{hasResults ? ( {hasResults ? (
<div className="mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all duration-500 [transition-behavior:allow-discrete] group-open:max-h-96 group-open:opacity-11"> <div className="mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all transition-discrete duration-500 group-open:max-h-96 group-open:opacity-11">
<ol className="space-y-1"> <ol className="space-y-1">
{searchResultsWithHrefs.map((result, index) => ( {searchResultsWithHrefs.map((result, index) => (
<li <li
key={`${result.pageId}-${index}`} key={`${result.pageId}-${index}`}
className="animate-fadeIn-slow" className="animate-fade-in-slow"
style={{ style={{
animationDelay: `${index * 25}ms`, animationDelay: `${index * 25}ms`,
}} }}
+132 -26
View File
@@ -5,6 +5,7 @@ import * as zustand from 'zustand';
import { AIMessageRole } from '@gitbook/api'; import { AIMessageRole } from '@gitbook/api';
import * as React from 'react'; import * as React from 'react';
import { useTrackEvent } from '../Insights'; import { useTrackEvent } from '../Insights';
import { useSearch } from '../Search';
import { streamAIChatResponse } from './server-actions'; import { streamAIChatResponse } from './server-actions';
import { useAIMessageContextRef } from './useAIMessageContext'; import { useAIMessageContextRef } from './useAIMessageContext';
@@ -30,6 +31,11 @@ export type AIChatState = {
*/ */
query: string | null; query: string | null;
/**
* The first query sent to the AI. This is appended to the URL when the AI chat is opened.
*/
initialQuery: string | null;
/** /**
* Messages in the session. * Messages in the session.
*/ */
@@ -56,20 +62,15 @@ export type AIChatState = {
export type AIChatController = { export type AIChatController = {
/** Open the dialog */ /** Open the dialog */
open: () => void; open: () => void;
/** Close the dialog */ /** Close the dialog */
close: () => void; close: () => void;
/** Post a message to the session */ /** Post a message to the session */
postMessage: (input: { postMessage: (input: { message: string }) => void;
/** The message to post to the session. it can be markdown formatted. */
message: string;
}) => void;
/** Clear the conversation */ /** Clear the conversation */
clear: () => void; clear: () => void;
}; };
// Global state store for AI chat
const globalState = zustand.create<{ const globalState = zustand.create<{
state: AIChatState; state: AIChatState;
setState: (fn: (state: AIChatState) => Partial<AIChatState>) => void; setState: (fn: (state: AIChatState) => Partial<AIChatState>) => void;
@@ -83,6 +84,7 @@ const globalState = zustand.create<{
followUpSuggestions: [], followUpSuggestions: [],
loading: false, loading: false,
error: false, error: false,
initialQuery: null,
}, },
setState: (fn) => set((state) => ({ state: { ...state.state, ...fn(state.state) } })), setState: (fn) => set((state) => ({ state: { ...state.state, ...fn(state.state) } })),
}; };
@@ -98,45 +100,76 @@ export function useAIChatState(): AIChatState {
/** /**
* Get the controller to interact with the AI chat. * Get the controller to interact with the AI chat.
* Integrates with search state to synchronize ?ask= parameter.
*/ */
export function useAIChatController(): AIChatController { export function useAIChatController(): AIChatController {
const messageContextRef = useAIMessageContextRef(); const messageContextRef = useAIMessageContextRef();
const setState = zustand.useStore(globalState, (state) => state.setState); const setState = zustand.useStore(globalState, (state) => state.setState);
const trackEvent = useTrackEvent(); const trackEvent = useTrackEvent();
const [searchState, setSearchState] = useSearch(true);
// Open AI chat and sync with search state
const onOpen = React.useCallback(() => {
const { initialQuery } = globalState.getState().state;
setState((state) => ({ ...state, opened: true }));
// Update search state to show ask mode with first message or current ask value
setSearchState((prev) => ({
ask: prev?.ask ?? initialQuery ?? '',
query: prev?.query ?? null,
global: prev?.global ?? false,
open: false, // Close search popover when opening chat
}));
}, [setState, setSearchState]);
// Close AI chat and clear ask parameter
const onClose = React.useCallback(() => {
setState((state) => ({ ...state, opened: false }));
// Clear ask parameter but keep other search state
setSearchState((prev) => ({
ask: null,
query: prev?.query ?? null,
global: prev?.global ?? false,
open: false,
}));
}, [setState, setSearchState]);
// Post a message to the AI chat
const onPostMessage = React.useCallback(
async (input: { message: string }) => {
const { messages } = globalState.getState().state;
// For first message, update the ask parameter in URL
if (messages.length === 0) {
setSearchState((prev) => ({
ask: input.message,
query: prev?.query ?? null,
global: prev?.global ?? false,
open: false,
}));
}
return React.useMemo(() => {
return {
open: () => setState((state) => ({ ...state, opened: true })),
close: () => setState((state) => ({ ...state, opened: false })),
clear: () =>
setState((state) => ({
opened: state.opened,
loading: false,
messages: [],
query: null,
followUpSuggestions: [],
responseId: null,
error: false,
})),
postMessage: async (input: { message: string }) => {
trackEvent({ type: 'ask_question', query: input.message }); trackEvent({ type: 'ask_question', query: input.message });
// Add user message and placeholder for AI response
setState((state) => { setState((state) => {
return { return {
...state, ...state,
messages: [ messages: [
...state.messages, ...state.messages,
{ {
// TODO: how to handle markdown here?
// to avoid rendering as plain text
role: AIMessageRole.User, role: AIMessageRole.User,
content: input.message, content: input.message,
query: input.message,
}, },
{ {
role: AIMessageRole.Assistant, role: AIMessageRole.Assistant,
content: null, content: null, // Placeholder for streaming response
}, },
], ],
query: input.message, query: input.message,
responseId: null,
followUpSuggestions: [], followUpSuggestions: [],
loading: true, loading: true,
error: false, error: false,
@@ -150,6 +183,7 @@ export function useAIChatController(): AIChatController {
previousResponseId: globalState.getState().state.responseId ?? undefined, previousResponseId: globalState.getState().state.responseId ?? undefined,
}); });
// Process streaming response
for await (const data of stream) { for await (const data of stream) {
if (!data) continue; if (!data) continue;
@@ -179,6 +213,7 @@ export function useAIChatController(): AIChatController {
} }
} }
// Update the assistant message with streamed content
setState((state) => ({ setState((state) => ({
...state, ...state,
messages: [ messages: [
@@ -204,6 +239,77 @@ export function useAIChatController(): AIChatController {
})); }));
} }
}, },
[messageContextRef.current, setState, setSearchState, trackEvent]
);
// Clear the conversation and reset ask parameter
const onClear = React.useCallback(() => {
setState((state) => ({
opened: state.opened,
loading: false,
messages: [],
query: null,
followUpSuggestions: [],
responseId: null,
error: false,
initialQuery: null,
}));
// Reset ask parameter to empty string (keeps chat open but clears content)
setSearchState((prev) => ({
ask: '',
query: prev?.query ?? null,
global: prev?.global ?? false,
open: false,
}));
}, [setState, setSearchState]);
// Auto-trigger AI chat when ?ask= parameter appears in URL (only once)
React.useEffect(() => {
const hasNoAsk = searchState?.ask === undefined || searchState?.ask === null;
const hasQuery = searchState?.query !== null;
// Don't trigger if we have a regular search query active
if (hasNoAsk) return;
if (hasQuery && searchState.open === false) return;
// Open the chat when ask parameter appears
onOpen();
// Auto-post the message if ask has content
if (searchState?.ask?.trim()) {
const trimmedAsk = searchState.ask.trim();
const { loading, initialQuery } = globalState.getState().state;
// Don't trigger if we're already posting a message
if (loading) return;
// Only initialize once per URL ask value
if (initialQuery === trimmedAsk) return;
// Wait for messageContextRef to be defined before proceeding
if (!messageContextRef.current?.location) return;
// Mark this ask value as processed
setState((state) => ({ ...state, initialQuery: trimmedAsk }));
onPostMessage({ message: trimmedAsk });
}
}, [
searchState?.ask,
searchState?.query,
searchState?.open,
messageContextRef,
onOpen,
setState,
onPostMessage,
]);
return React.useMemo(() => {
return {
open: onOpen,
close: onClose,
clear: onClear,
postMessage: onPostMessage,
}; };
}, [messageContextRef, setState, trackEvent]); }, [onOpen, onClose, onClear, onPostMessage]);
} }
@@ -93,6 +93,15 @@ export function AIChatWindow(props: {
block: 'start', block: 'start',
}); });
const timeout = setTimeout(() => {
if (lastUserMessageRef.current) {
lastUserMessageRef.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
}, 100);
// We want the chat messages to scroll underneath the input, but they should scroll past the input when scrolling all the way down. // We want the chat messages to scroll underneath the input, but they should scroll past the input when scrolling all the way down.
// The best way to do this is to observe the input height and adjust the padding bottom of the scroll container accordingly. // The best way to do this is to observe the input height and adjust the padding bottom of the scroll container accordingly.
const observer = new ResizeObserver((entries) => { const observer = new ResizeObserver((entries) => {
@@ -103,12 +112,16 @@ export function AIChatWindow(props: {
if (inputRef.current) { if (inputRef.current) {
observer.observe(inputRef.current); observer.observe(inputRef.current);
} }
return () => observer.disconnect(); return () => {
}, [chat.opened]); observer.disconnect();
clearTimeout(timeout);
};
}, []);
return ( return (
<div <div
className="ai-chat inset-y-0 right-0 z-40 mx-auto flex max-w-3xl animate-present scroll-mt-36 px-4 py-4 transition-all duration-300 sm:px-6 lg:fixed lg:w-80 lg:animate-enterFromRight lg:pr-4 lg:pl-0 xl:w-96" data-testid="ai-chat"
className="ai-chat inset-y-0 right-0 z-40 mx-auto flex max-w-3xl animate-present scroll-mt-36 px-4 py-4 transition-all duration-300 sm:px-6 lg:fixed lg:w-80 lg:animate-enter-from-right lg:pr-4 lg:pl-0 xl:w-96"
ref={containerRef} ref={containerRef}
> >
<div className="relative flex h-full grow flex-col overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-md bg-tint-base text-sm text-tint depth-subtle:shadow-lg shadow-tint ring-1 ring-tint-subtle"> <div className="relative flex h-full grow flex-col overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-md bg-tint-base text-sm text-tint depth-subtle:shadow-lg shadow-tint ring-1 ring-tint-subtle">
@@ -150,7 +163,6 @@ export function AIChatWindow(props: {
iconOnly iconOnly
icon="ellipsis" icon="ellipsis"
label={tString(language, 'actions')} label={tString(language, 'actions')}
className="!px-2"
variant="blank" variant="blank"
size="default" size="default"
/> />
@@ -174,7 +186,6 @@ export function AIChatWindow(props: {
iconOnly iconOnly
icon="close" icon="close"
label={tString(language, 'close')} label={tString(language, 'close')}
className="!px-2"
variant="blank" variant="blank"
size="default" size="default"
/> />
@@ -189,7 +200,7 @@ export function AIChatWindow(props: {
> >
{isEmpty ? ( {isEmpty ? (
<div className="flex min-h-full w-full shrink-0 flex-col items-center justify-center gap-6 py-4"> <div className="flex min-h-full w-full shrink-0 flex-col items-center justify-center gap-6 py-4">
<div className="flex size-32 animate-fadeIn-slow items-center justify-center rounded-full bg-tint-subtle"> <div className="flex size-32 animate-fade-in-slow items-center justify-center rounded-full bg-tint-subtle">
<AIChatIcon <AIChatIcon
state="intro" state="intro"
trademark={trademark} trademark={trademark}
@@ -218,7 +229,7 @@ export function AIChatWindow(props: {
</div> </div>
<div <div
ref={inputRef} ref={inputRef}
className="absolute inset-x-0 bottom-0 mr-2 flex select-none flex-col gap-4 bg-gradient-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2" className="absolute inset-x-0 bottom-0 mr-2 flex select-none flex-col gap-4 bg-linear-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2"
> >
{/* Display an error banner when something went wrong. */} {/* Display an error banner when something went wrong. */}
{chat.error ? <AIChatError chatController={chatController} /> : null} {chat.error ? <AIChatError chatController={chatController} /> : null}
@@ -244,7 +255,7 @@ function AIChatError(props: { chatController: AIChatController }) {
const { chatController } = props; const { chatController } = props;
return ( return (
<div className="flex animate-scaleIn flex-wrap justify-between gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md bg-danger px-3 py-2 text-danger text-sm ring-1 ring-danger"> <div className="flex animate-scale-in flex-wrap justify-between gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md bg-danger px-3 py-2 text-danger text-sm ring-1 ring-danger">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Icon icon="exclamation-triangle" className="size-3.5" /> <Icon icon="exclamation-triangle" className="size-3.5" />
<span className="flex items-center gap-1">{t(language, 'ai_chat_error')}</span> <span className="flex items-center gap-1">{t(language, 'ai_chat_error')}</span>
@@ -258,7 +269,7 @@ function AIChatError(props: { chatController: AIChatController }) {
onClick={() => { onClick={() => {
chatController.clear(); chatController.clear();
}} }}
className="!text-danger hover:bg-danger-5" className="text-danger! hover:bg-danger-5"
/> />
</div> </div>
</div> </div>
@@ -20,6 +20,7 @@ export function AIChatButton(props: { trademark: boolean }) {
return ( return (
<Button <Button
icon={<AIChatIcon trademark={trademark} />} icon={<AIChatIcon trademark={trademark} />}
data-testid="ai-chat-button"
iconOnly iconOnly
size="default" size="default"
variant="header" variant="header"
@@ -38,7 +38,7 @@ export function AIChatInput(props: {
// This fixes inconsistent focus behaviour across browsers // This fixes inconsistent focus behaviour across browsers
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
inputRef.current?.focus(); inputRef.current?.focus();
}, 50); }, 150);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
} }
@@ -59,9 +59,10 @@ export function AIChatInput(props: {
ref={inputRef} ref={inputRef}
disabled={disabled || loading} disabled={disabled || loading}
data-loading={loading} data-loading={loading}
data-testid="ai-chat-input"
className={tcls( className={tcls(
'resize-none', 'resize-none',
'focus:outline-none', 'focus:outline-hidden',
'focus:ring-0', 'focus:ring-0',
'w-full', 'w-full',
'px-3', 'px-3',
@@ -126,7 +127,7 @@ export function AIChatInput(props: {
arrow arrow
> >
<div className="flex cursor-help items-center gap-1 circular-corners:rounded-2xl rounded-corners:rounded-md px-2 py-1 text-tint/7 text-xs transition-all hover:bg-tint"> <div className="flex cursor-help items-center gap-1 circular-corners:rounded-2xl rounded-corners:rounded-md px-2 py-1 text-tint/7 text-xs transition-all hover:bg-tint">
<span className="-ml-1 rounded-corners:rounded circular-corners:rounded-2xl bg-tint-11/7 px-1 py-0.5 font-mono font-semibold text-[0.65rem] text-contrast-tint-11 leading-none"> <span className="-ml-1 circular-corners:rounded-2xl rounded-corners:rounded-sm bg-tint-11/7 px-1 py-0.5 font-mono font-semibold text-[0.65rem] text-contrast-tint-11 leading-none">
{t(language, 'ai_chat_context_badge')} {t(language, 'ai_chat_context_badge')}
</span>{' '} </span>{' '}
<span>{t(language, 'ai_chat_context_title')}</span> <span>{t(language, 'ai_chat_context_title')}</span>
@@ -23,8 +23,9 @@ export function AIChatMessages(props: {
return ( return (
<div <div
ref={isLastUserMessage ? lastUserMessageRef : undefined} ref={isLastUserMessage ? lastUserMessageRef : undefined}
data-testid="ai-chat-message"
className={tcls( className={tcls(
message.content ? 'animate-fadeIn-slow' : '', message.content ? 'animate-fade-in-slow' : '',
'shrink-0', 'shrink-0',
'last:min-h-[calc(100%-5rem)]', 'last:min-h-[calc(100%-5rem)]',
'scroll-mt-36', 'scroll-mt-36',
@@ -43,7 +44,7 @@ export function AIChatMessages(props: {
{message.content ? message.content : null} {message.content ? message.content : null}
{isLastMessage && chat.loading ? ( {isLastMessage && chat.loading ? (
<div className="flex w-full animate-fadeIn-slow flex-wrap gap-2 group-has-[.has-content]/message:hidden"> <div className="flex w-full animate-fade-in-slow flex-wrap gap-2 group-has-[.has-content]/message:hidden">
{Array.from({ length: 7 }).map((_, index) => ( {Array.from({ length: 7 }).map((_, index) => (
<div <div
key={index} key={index}
@@ -31,8 +31,8 @@ export function AIResponseFeedback(props: {
label={tString(language, 'was_this_helpful_positive_label')} label={tString(language, 'was_this_helpful_positive_label')}
variant="blank" variant="blank"
className={tcls( className={tcls(
'animate-fadeIn overflow-hidden text-tint-subtle transition-all', 'animate-fade-in overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== 1 && 'px-0 text-[0] opacity-0' rating !== null && rating !== 1 && 'px-0 text-[0rem] opacity-0'
)} )}
size="medium" size="medium"
style={{ animationDuration: '.5s' }} style={{ animationDuration: '.5s' }}
@@ -47,8 +47,8 @@ export function AIResponseFeedback(props: {
label={tString(language, 'was_this_helpful_negative_label')} label={tString(language, 'was_this_helpful_negative_label')}
variant="blank" variant="blank"
className={tcls( className={tcls(
'animate-fadeIn overflow-hidden text-tint-subtle transition-all', 'animate-fade-in overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== -1 && 'px-0 text-[0] opacity-0' rating !== null && rating !== -1 && 'px-0 text-[0rem] opacity-0'
)} )}
size="medium" size="medium"
style={{ animationDelay: '.2s', animationDuration: '.5s' }} style={{ animationDelay: '.2s', animationDuration: '.5s' }}
@@ -59,7 +59,7 @@ export function AIResponseFeedback(props: {
/> />
{rating !== null ? ( {rating !== null ? (
<span <span
className="ml-2 animate-fadeIn-slow text-tint-subtle" className="ml-2 animate-fade-in-slow text-tint-subtle"
style={{ animationDelay: '.3s' }} style={{ animationDelay: '.3s' }}
> >
{t(language, 'was_this_helpful_thank_you')} {t(language, 'was_this_helpful_thank_you')}
@@ -23,7 +23,7 @@ export function AIChatFollowupSuggestions(props: {
chatController.postMessage({ message: suggestion }); chatController.postMessage({ message: suggestion });
}} }}
label={suggestion} label={suggestion}
className="!whitespace-normal max-w-full animate-[present_500ms_both] text-left ring-1 ring-tint-subtle" className="whitespace-normal! max-w-full animate-[present_500ms_both] text-left ring-1 ring-tint-subtle"
size="medium" size="medium"
variant="blank" variant="blank"
style={{ style={{
@@ -49,7 +49,7 @@ export async function AdCoverRendering({
'rounded-lg', 'rounded-lg',
'p-4', 'p-4',
'overflow-hidden', 'overflow-hidden',
'shadow-sm' 'shadow-xs'
)} )}
style={{ backgroundColor: ad.backgroundColor, color: ad.textColor ?? '#ffffff' }} style={{ backgroundColor: ad.backgroundColor, color: ad.textColor ?? '#ffffff' }}
href={ad.statlink} href={ad.statlink}
@@ -68,7 +68,7 @@ export async function AdCoverRendering({
}} }}
/> />
<div className={tcls('z-[2]')}> <div className={tcls('z-2')}>
<img <img
alt="Large cover" alt="Large cover"
src={largeImage} src={largeImage}
@@ -81,10 +81,10 @@ export async function AdCoverRendering({
)} )}
/> />
</div> </div>
<div className={tcls('z-[2]')}> <div className={tcls('z-2')}>
<img alt={ad.company} src={ad.logo} className={tcls('max-w-36', 'max-h-12')} /> <img alt={ad.company} src={ad.logo} className={tcls('max-w-36', 'max-h-12')} />
</div> </div>
<div className={tcls('flex', 'flex-col', 'z-[2]')}> <div className={tcls('flex', 'flex-col', 'z-2')}>
<div className={tcls('text-sm', 'font-semibold', 'mb-2')}>{ad.companyTagline}</div> <div className={tcls('text-sm', 'font-semibold', 'mb-2')}>{ad.companyTagline}</div>
<div <div
className={tcls( className={tcls(
@@ -99,7 +99,7 @@ export async function AdCoverRendering({
{ad.description} {ad.description}
</div> </div>
</div> </div>
<div className={tcls('z-[2]')}> <div className={tcls('z-2')}>
<span <span
className={tcls( className={tcls(
'text-sm', 'text-sm',
@@ -119,7 +119,7 @@ export async function AdCoverRendering({
</span> </span>
</div> </div>
<div <div
className={tcls('absolute', 'inset-0', 'backdrop-blur', 'z-[1]')} className={tcls('absolute', 'inset-0', 'backdrop-blur-sm', 'z-1')}
style={{ style={{
backgroundColor: hexToRgba(ad.backgroundColor, 0.8), backgroundColor: hexToRgba(ad.backgroundColor, 0.8),
}} }}
@@ -7,7 +7,7 @@ import { tcls } from '@/lib/tailwind';
import { type CustomizationAnnouncement, SiteInsightsLinkPosition } from '@gitbook/api'; import { type CustomizationAnnouncement, SiteInsightsLinkPosition } from '@gitbook/api';
import { Icon, type IconName } from '@gitbook/icons'; import { Icon, type IconName } from '@gitbook/icons';
import { CONTAINER_STYLE } from '../layout'; import { CONTAINER_STYLE } from '../layout';
import { Link } from '../primitives'; import { Button, Link } from '../primitives';
import { LinkStyles } from '../primitives/styles'; import { LinkStyles } from '../primitives/styles';
import { ANNOUNCEMENT_CSS_CLASS, ANNOUNCEMENT_STORAGE_KEY } from './constants'; import { ANNOUNCEMENT_CSS_CLASS, ANNOUNCEMENT_STORAGE_KEY } from './constants';
@@ -86,14 +86,15 @@ export function AnnouncementBanner(props: {
</div> </div>
</Tag> </Tag>
{closeable ? ( {closeable ? (
<button <Button
className={`absolute top-0 right-4 mt-2 mr-2 rounded circular-corners:rounded-lg straight-corners:rounded-none p-1.5 transition-all hover:ring-1 sm:right-6 md:right-8 ${style.close}`} iconOnly
type="button" icon="close"
label={tString(language, 'close')}
variant="blank"
size="default"
onClick={dismissAnnouncement} onClick={dismissAnnouncement}
title={tString(language, 'close')} className={`absolute top-0 right-4 mt-2 mr-2 circular-corners:rounded-lg rounded-sm straight-corners:rounded-none p-1.5 transition-all hover:ring-1 sm:right-6 md:right-8 ${style.close}`}
> />
<Icon icon="close" className="size-4" />
</button>
) : null} ) : null}
</div> </div>
</div> </div>
@@ -130,7 +131,7 @@ const BANNER_STYLES = {
icon: 'circle-exclamation', icon: 'circle-exclamation',
iconColor: 'text-warning-subtle', iconColor: 'text-warning-subtle',
close: 'hover:bg-tint-base hover:ring-warning-subtle', close: 'hover:bg-tint-base hover:ring-warning-subtle',
link: 'links-default:text-warning links-default:hover:text-warning-strong links-default:decoration-warning/6 links-accent:decoration-warning', link: 'links-default:text-warning hover:links-default:text-warning-strong links-default:decoration-warning/6 links-accent:decoration-warning',
}, },
danger: { danger: {
container: 'bg-danger decoration-danger/6 ring-danger-subtle', container: 'bg-danger decoration-danger/6 ring-danger-subtle',
@@ -138,7 +139,7 @@ const BANNER_STYLES = {
icon: 'triangle-exclamation', icon: 'triangle-exclamation',
iconColor: 'text-danger-subtle', iconColor: 'text-danger-subtle',
close: 'hover:bg-tint-base hover:ring-danger-subtle', close: 'hover:bg-tint-base hover:ring-danger-subtle',
link: 'links-default:text-danger links-default:hover:text-danger-strong links-default:decoration-danger/6 links-accent:decoration-danger', link: 'links-default:text-danger hover:links-default:text-danger-strong links-default:decoration-danger/6 links-accent:decoration-danger',
}, },
success: { success: {
container: 'bg-success decoration-success/6 ring-success-subtle', container: 'bg-success decoration-success/6 ring-success-subtle',
@@ -146,6 +147,6 @@ const BANNER_STYLES = {
icon: 'circle-check', icon: 'circle-check',
iconColor: 'text-success-subtle', iconColor: 'text-success-subtle',
close: 'hover:bg-tint-base hover:ring-success-subtle', close: 'hover:bg-tint-base hover:ring-success-subtle',
link: 'links-default:text-success links-default:hover:text-success-strong links-default:decoration-success/6 links-accent:decoration-success', link: 'links-default:text-success hover:links-default:text-success-strong links-default:decoration-success/6 links-accent:decoration-success',
}, },
}; };
@@ -1,6 +1,4 @@
'use client'; 'use client';
import { Icon } from '@gitbook/icons';
import * as React from 'react'; import * as React from 'react';
import { Button, StyledLink } from '@/components/primitives'; import { Button, StyledLink } from '@/components/primitives';
@@ -45,7 +43,7 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
'fixed', 'fixed',
'z-10', 'z-10',
'bg-tint-base', 'bg-tint-base',
'rounded', 'rounded-sm',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-2xl', 'circular-corners:rounded-2xl',
'ring-1', 'ring-1',
@@ -61,7 +59,7 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
'text-balance', 'text-balance',
'sm:left-auto', 'sm:left-auto',
'lg:chat-open:mr-80', 'lg:chat-open:mr-80',
'xl:chat-open:mr-[25rem]', 'xl:chat-open:mr-100',
'transition-all', 'transition-all',
'duration-300' 'duration-300'
)} )}
@@ -75,27 +73,14 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
</StyledLink> </StyledLink>
)} )}
</p> </p>
<button <Button
type="button" iconOnly
icon="close"
label={tString(language, 'close')}
variant="blank"
onClick={() => setShow(false)} onClick={() => setShow(false)}
aria-label={tString(language, 'close')} className={tcls('absolute', 'top-2', 'right-2', 'hover:bg-tint-hover')}
className={tcls( />
'absolute',
'top-3',
'right-3',
'w-6',
'h-6',
'flex',
'justify-center',
'items-center',
'rounded-sm',
'circular-corners:rounded-full',
'hover:bg-tint-hover'
)}
title={tString(language, 'close')}
>
<Icon icon="xmark" className={tcls('size-4')} />
</button>
<div className={tcls('mt-3', 'flex', 'flex-row', 'gap-2')}> <div className={tcls('mt-3', 'flex', 'flex-row', 'gap-2')}>
<Button <Button
variant="primary" variant="primary"
@@ -34,7 +34,7 @@ export function AnnotationPopover(props: { children: React.ReactNode; body: Reac
'bg-tint', 'bg-tint',
'ring-1', 'ring-1',
'ring-tint', 'ring-tint',
'rounded', 'rounded-sm',
'shadow-1xs', 'shadow-1xs',
'shadow-tint-12/1', 'shadow-tint-12/1',
'dark:shadow-tint-1/2', 'dark:shadow-tint-1/2',
@@ -53,7 +53,7 @@ export function AnnotationPopover(props: { children: React.ReactNode; body: Reac
viewBox="0 0 8 5" viewBox="0 0 8 5"
className={tcls( className={tcls(
'relative', 'relative',
'z-[2]', 'z-2',
'fill-tint-3', // Same as bg-tint 'fill-tint-3', // Same as bg-tint
'stroke-tint-7', // Same as ring-tint 'stroke-tint-7', // Same as ring-tint
'[paint-order:stroke_fill]' '[paint-order:stroke_fill]'
@@ -85,9 +85,9 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
key={node.key || `${node.type}-${index}`} key={node.key || `${node.type}-${index}`}
block={node} block={node}
style={[ style={[
'mx-auto page-width-wide:mx-0 w-full decoration-primary/6', 'mx-auto page-width-wide:mx-0 w-full decoration-primary/6 print:break-inside-avoid',
node.data && 'fullWidth' in node.data && node.data.fullWidth node.data && 'fullWidth' in node.data && node.data.fullWidth
? 'max-w-screen-2xl' ? 'max-w-screen-xl'
: 'max-w-3xl', : 'max-w-3xl',
FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-screen-2xl', FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-screen-2xl',
blockStyle, blockStyle,
@@ -38,11 +38,11 @@ export function Caption(
'overflow-hidden', 'overflow-hidden',
'after:block', 'after:block',
'after:absolute', 'after:absolute',
'after:-inset-[0]', 'after:-inset-0',
'after:pointer-events-none', 'after:pointer-events-none',
fit ? 'w-fit' : null, fit ? 'w-fit' : null,
withBorder withBorder
? 'rounded circular-corners:rounded-2xl straight-corners:rounded-none after:border-tint-subtle after:border after:rounded circular-corners:after:rounded-2xl straight-corners:after:rounded-none dark:after:mix-blend-plus-lighter after:pointer-events-none' ? 'rounded-corners:rounded-sm circular-corners:rounded-2xl after:border-tint-subtle after:border after:rounded circular-corners:after:rounded-2xl rounded-corners:after:rounded-sm dark:after:mix-blend-plus-lighter after:pointer-events-none'
: null, : null,
], ],
style, style,
@@ -43,12 +43,12 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
</div> </div>
<CopyCodeButton <CopyCodeButton
codeId={id} codeId={id}
style="z-[2] mt-2 mr-2 self-start justify-self-end rounded-md bg-transparent p-1 text-tint text-xs leading-none opacity-0 ring-1 ring-tint backdrop-blur-md transition-opacity duration-75 [grid-area:2/1] hover:ring-tint-hover group-hover/codeblock:opacity-[1]" style="z-2 mt-2 mr-2 self-start justify-self-end rounded-md bg-transparent p-1 text-tint text-xs leading-none opacity-0 ring-1 ring-tint backdrop-blur-md transition-opacity duration-75 [grid-area:2/1] hover:ring-tint-hover group-hover/codeblock:opacity-11"
/> />
<pre <pre
className={tcls( className={tcls(
'relative overflow-auto border border-tint-subtle bg-tint-subtle theme-muted:bg-tint-base p-2 [grid-area:2/1] contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base', 'relative overflow-auto border border-tint-subtle bg-tint-subtle theme-muted:bg-tint-base p-2 [grid-area:2/1] contrast-more:border-tint contrast-more:bg-tint-base [html.theme-bold.sidebar-filled_&]:bg-tint-base',
'rounded-md straight-corners:rounded-sm shadow-sm', 'rounded-md straight-corners:rounded-xs shadow-xs',
title && 'rounded-ss-none' title && 'rounded-ss-none'
)} )}
> >
@@ -34,6 +34,8 @@ export type RenderedInline = {
body: React.ReactNode; body: React.ReactNode;
}; };
const isSafari =
typeof navigator !== 'undefined' && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const theme = createCssVariablesTheme(); const theme = createCssVariablesTheme();
const { getSingletonHighlighter } = createSingletonShorthands( const { getSingletonHighlighter } = createSingletonShorthands(
@@ -65,8 +67,13 @@ export async function highlight(
inlines: RenderedInline[] inlines: RenderedInline[]
): Promise<HighlightLine[]> { ): Promise<HighlightLine[]> {
const langName = getBlockLang(block); const langName = getBlockLang(block);
if (!langName) {
// Language not found, fallback to plain highlighting if (!langName || (isSafari && ['powershell', 'cpp'].includes(langName))) {
// Fallback to plain highlighting if
// - language is not found
// - TEMP : language is PowerShell or C++ and browser is Safari:
// RegExp#[Symbol.search] throws TypeError when `lastIndex` isnt writable
// Fixed in upcoming Safari 18.6, remove when it'll be released - RND-7772
return plainHighlight(block, inlines); return plainHighlight(block, inlines);
} }
@@ -51,7 +51,7 @@ function transformLengthToCSS(length: Length | undefined) {
if (length.unit === '%') { if (length.unit === '%') {
return { return {
className: [ className: [
'md:flex-shrink-0', 'md:shrink-0',
COLUMN_WIDTHS[Math.round(length.value * 0.01 * (COLUMN_WIDTHS.length - 1))], COLUMN_WIDTHS[Math.round(length.value * 0.01 * (COLUMN_WIDTHS.length - 1))],
], ],
}; };
@@ -50,12 +50,12 @@ export function Details(props: {
'border-b-0', 'border-b-0',
'border-tint-subtle', 'border-tint-subtle',
//all //all
'[&]:mt-[0px]', 'mt-0!',
//select first child //select first child
'[&:first-child]:mt-5', 'first:mt-5!',
'[&:first-child]:rounded-t-lg', 'first:rounded-t-lg',
//select first in group //select first in group
'[:not(&)_+&]:mt-5', '[:not(&)_+&]:mt-5!',
'[:not(&)_+&]:rounded-t-lg', '[:not(&)_+&]:rounded-t-lg',
//select last in group //select last in group
'[&:not(:has(+_&))]:mb-5', '[&:not(:has(+_&))]:mb-5',
@@ -30,8 +30,8 @@ export function HashLinkButton(props: {
'opacity-0', 'opacity-0',
'group-hover/hash:opacity-[0]', 'group-hover/hash:opacity-[0]',
'group-focus/hash:opacity-[0]', 'group-focus/hash:opacity-[0]',
'md:group-hover/hash:md:opacity-[1]', 'md:group-hover/hash:opacity-[1]',
'md:group-focus/hash:md:opacity-[1]', 'md:group-focus/hash:opacity-[1]',
className className
)} )}
> >
@@ -26,7 +26,7 @@ export function Heading(props: BlockProps<DocumentBlockHeading>) {
'heading', 'heading',
'flex', 'flex',
'items-baseline', 'items-baseline',
'scroll-m-12', 'scroll-mt-(--content-scroll-margin)',
getTextAlignment(block.data.align), getTextAlignment(block.data.align),
hashLinkButtonWrapperStyles, hashLinkButtonWrapperStyles,
style, style,
@@ -44,7 +44,7 @@ export function Heading(props: BlockProps<DocumentBlockHeading>) {
<div <div
className={tcls( className={tcls(
'flex-1', 'flex-1',
'z-[1]', 'z-1',
'justify-self-start', 'justify-self-start',
'max-w-full', 'max-w-full',
'break-words', 'break-words',
@@ -21,7 +21,7 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
'hint', 'hint',
'transition-colors', 'transition-colors',
'rounded-md', 'rounded-md',
hasHeading ? 'rounded-l' : null, hasHeading ? 'rounded-l-sm' : null,
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-xl', 'circular-corners:rounded-xl',
'overflow-hidden', 'overflow-hidden',
@@ -52,7 +52,7 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
{hasHeading ? ( {hasHeading ? (
<Block <Block
style={tcls( style={tcls(
'!py-4 w-full items-start pl-3 text-[1em] *:flex-none', 'w-full items-start py-4! pl-3 text-[1em] *:flex-none',
// Heading hash styles // Heading hash styles
'flip-heading-hash pr-8', 'flip-heading-hash pr-8',
hintStyle.header hintStyle.header
@@ -19,7 +19,7 @@ export async function InlineButton(props: InlineProps<api.DocumentInlineButton>)
return ( return (
// Set the leading to have some vertical space between adjacent buttons // Set the leading to have some vertical space between adjacent buttons
<span className="inline-button leading-[3rem] [&:has(+.inline-button)]:mr-2"> <span className="inline-button leading-12 [&:has(+.inline-button)]:mr-2">
<Button <Button
href={resolved.href} href={resolved.href}
label={inline.data.label} label={inline.data.label}
@@ -51,7 +51,7 @@ export async function InlineImage(props: InlineProps<DocumentInlineImage>) {
}} }}
priority="lazy" priority="lazy"
preload preload
style={[size === 'line' ? ['max-h-[1lh]', 'h-[1lh]', 'w-auto'] : null]} style={[size === 'line' ? ['max-h-lh', 'h-lh', 'w-auto'] : null]}
inline inline
zoom={!isInLink} zoom={!isInLink}
/> />
@@ -1,11 +1,10 @@
import { type DocumentInlineLink, SiteInsightsLinkPosition } from '@gitbook/api'; import { type DocumentInlineLink, SiteInsightsLinkPosition } from '@gitbook/api';
import { getSpaceLanguage, tString } from '@/intl/server'; import { getSpaceLanguage, tString } from '@/intl/server';
import { languages } from '@/intl/translations'; import { type TranslationLanguage, languages } from '@/intl/translations';
import type { GitBookAnyContext } from '@/lib/context';
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references'; import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
import { Icon } from '@gitbook/icons'; import { Icon } from '@gitbook/icons';
import { StyledLink } from '../../primitives'; import { HoverCard, HoverCardRoot, HoverCardTrigger, StyledLink } from '../../primitives';
import type { InlineProps } from '../Inline'; import type { InlineProps } from '../Inline';
import { Inlines } from '../Inlines'; import { Inlines } from '../Inlines';
import { InlineLinkTooltip } from './InlineLinkTooltip'; import { InlineLinkTooltip } from './InlineLinkTooltip';
@@ -19,10 +18,18 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
resolveAnchorText: false, resolveAnchorText: false,
}) })
: null; : null;
const { contentContext } = context;
if (!context.contentContext || !resolved) { const language =
contentContext && 'customization' in contentContext
? getSpaceLanguage(contentContext.customization)
: languages.en;
if (!contentContext || !resolved) {
return ( return (
<span title="Broken link" className="underline"> <HoverCardRoot>
<HoverCardTrigger>
<span className="cursor-not-allowed underline">
<Inlines <Inlines
context={context} context={context}
document={document} document={document}
@@ -30,6 +37,15 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
ancestorInlines={[...ancestorInlines, inline]} ancestorInlines={[...ancestorInlines, inline]}
/> />
</span> </span>
</HoverCardTrigger>
<HoverCard className="flex flex-col gap-1 p-4">
<div className="flex items-center gap-2">
<Icon icon="ban" className="size-4 text-tint-subtle" />
<h5 className="font-semibold">{tString(language, 'notfound_title')}</h5>
</div>
<p className="text-sm text-tint">{tString(language, 'notfound_link')}</p>
</HoverCard>
</HoverCardRoot>
); );
} }
const isExternal = inline.data.ref.kind === 'url'; const isExternal = inline.data.ref.kind === 'url';
@@ -61,11 +77,7 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
if (context.shouldRenderLinkPreviews) { if (context.shouldRenderLinkPreviews) {
return ( return (
<InlineLinkTooltipWrapper <InlineLinkTooltipWrapper inline={inline} language={language} resolved={resolved}>
inline={inline}
context={context.contentContext}
resolved={resolved}
>
{content} {content}
</InlineLinkTooltipWrapper> </InlineLinkTooltipWrapper>
); );
@@ -80,15 +92,13 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
*/ */
function InlineLinkTooltipWrapper(props: { function InlineLinkTooltipWrapper(props: {
inline: DocumentInlineLink; inline: DocumentInlineLink;
context: GitBookAnyContext;
children: React.ReactNode; children: React.ReactNode;
resolved: ResolvedContentRef; resolved: ResolvedContentRef;
language: TranslationLanguage;
}) { }) {
const { inline, context, resolved, children } = props; const { inline, language, resolved, children } = props;
let breadcrumbs = resolved.ancestors ?? []; let breadcrumbs = resolved.ancestors ?? [];
const language =
'customization' in context ? getSpaceLanguage(context.customization) : languages.en;
const isExternal = inline.data.ref.kind === 'url'; const isExternal = inline.data.ref.kind === 'url';
const isSamePage = inline.data.ref.kind === 'anchor' && inline.data.ref.page === undefined; const isSamePage = inline.data.ref.kind === 'anchor' && inline.data.ref.page === undefined;
if (isExternal) { if (isExternal) {
@@ -1,9 +1,8 @@
'use client'; 'use client';
import { tcls } from '@/lib/tailwind'; import { tcls } from '@/lib/tailwind';
import { Icon } from '@gitbook/icons'; import { Icon } from '@gitbook/icons';
import * as Tooltip from '@radix-ui/react-tooltip';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { Button, StyledLink } from '../../primitives'; import { Button, HoverCard, HoverCardRoot, HoverCardTrigger, StyledLink } from '../../primitives';
export function InlineLinkTooltipImpl(props: { export function InlineLinkTooltipImpl(props: {
isSamePage: boolean; isSamePage: boolean;
@@ -21,13 +20,9 @@ export function InlineLinkTooltipImpl(props: {
const { isSamePage, isExternal, openInNewTabLabel, target, breadcrumbs, children } = props; const { isSamePage, isExternal, openInNewTabLabel, target, breadcrumbs, children } = props;
return ( return (
<Tooltip.Provider delayDuration={200}> <HoverCardRoot>
<Tooltip.Root> <HoverCardTrigger>{children}</HoverCardTrigger>
<Tooltip.Trigger asChild>{children}</Tooltip.Trigger> <HoverCard className="p-4">
<Tooltip.Portal>
<Tooltip.Content className="z-40 w-screen max-w-md animate-present px-4 sm:w-auto">
<div className="overflow-hidden rounded-md straight-corners:rounded-none shadow-lg shadow-tint-12/4 ring-1 ring-tint-subtle dark:shadow-tint-1 ">
<div className="bg-tint-base p-4">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="flex flex-col"> <div className="flex flex-col">
{breadcrumbs && breadcrumbs.length > 0 ? ( {breadcrumbs && breadcrumbs.length > 0 ? (
@@ -66,7 +61,7 @@ export function InlineLinkTooltipImpl(props: {
<div <div
className={tcls( className={tcls(
'flex gap-2 leading-snug', 'flex gap-2 leading-snug',
isExternal && 'text-sm [overflow-wrap:anywhere]' isExternal && 'wrap-anywhere text-sm'
)} )}
> >
{target.icon ? ( {target.icon ? (
@@ -81,9 +76,7 @@ export function InlineLinkTooltipImpl(props: {
<Button <Button
className={tcls( className={tcls(
'-mx-2 -my-2 ml-auto', '-mx-2 -my-2 ml-auto',
breadcrumbs?.length === 0 breadcrumbs?.length === 0 ? 'place-self-center' : null
? 'place-self-center'
: null
)} )}
variant="blank" variant="blank"
href={target.href} href={target.href}
@@ -95,15 +88,8 @@ export function InlineLinkTooltipImpl(props: {
/> />
) : null} ) : null}
</div> </div>
{target.subText ? ( {target.subText ? <p className="mt-1 text-sm text-tint">{target.subText}</p> : null}
<p className="mt-1 text-sm text-tint">{target.subText}</p> </HoverCard>
) : null} </HoverCardRoot>
</div>
</div>
<Tooltip.Arrow className="fill-tint-1" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
); );
} }
@@ -1,3 +1,5 @@
@reference '../../RootLayout/globals.css';
/** Stacks */ /** Stacks */
.contentkit-stack { .contentkit-stack {
@apply flex gap-4; @apply flex gap-4;
@@ -114,7 +116,7 @@
/** Modals */ /** Modals */
.contentkit-modal-backdrop { .contentkit-modal-backdrop {
@apply fixed inset-0 z-40 px-7 pb-11 pt-4 md:pt-[min(8vw,_6rem)] flex items-start justify-center backdrop-blur-2xl bg-tint-12/4 dark:bg-tint-1/6; @apply fixed inset-0 z-40 px-7 pb-11 pt-4 md:pt-[min(8vw,6rem)] flex items-start justify-center backdrop-blur-2xl bg-tint-12/4 dark:bg-tint-1/6;
} }
.contentkit-modal { .contentkit-modal {
@apply flex flex-col gap-6 relative z-50 bg-tint @apply flex flex-col gap-6 relative z-50 bg-tint
@@ -31,13 +31,13 @@ export function ListItem(props: BlockProps<DocumentBlockListItem>) {
nodes={block.nodes} nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]} ancestorBlocks={[...ancestorBlocks, block]}
blockStyle={tcls( blockStyle={tcls(
'min-h-[1lh]', 'min-h-lh',
// flip heading hash icon if list item is a heading // flip heading hash icon if list item is a heading
'flip-heading-hash', 'flip-heading-hash',
// remove margin-top for the first heading in a list // remove margin-top for the first heading in a list
'[&:is(h2)]:pt-0', '[h2]:pt-0',
'[&:is(h3)]:pt-0', '[h3]:pt-0',
'[&:is(h4)]:pt-0', '[h4]:pt-0',
// Override the "mx-auto" class from UnwrappedBlocks // Override the "mx-auto" class from UnwrappedBlocks
'mx-0' 'mx-0'
)} )}
@@ -137,7 +137,7 @@ function ListItemPrefix(props: { block: DocumentBlockListItem; children: React.R
className={tcls( className={tcls(
textStyle.textSize, textStyle.textSize,
textStyle.lineHeight, textStyle.lineHeight,
'mr-1 flex min-h-[1lh] min-w-6 items-center justify-center text-tint' 'mr-1 flex min-h-lh min-w-6 items-center justify-center text-tint'
)} )}
> >
{props.children} {props.children}
@@ -165,7 +165,7 @@ function PseudoBefore(props: {
}) { }) {
return ( return (
<div <div
className="before:font-var before:content-[--pseudoBefore--content]" className="before:font-var before:content-(--pseudoBefore--content)"
style={ style={
{ {
'--pseudoBefore--content': `'${props.content}'`, '--pseudoBefore--content': `'${props.content}'`,
@@ -1,4 +1,5 @@
@import "@scalar/api-client-react/style.css"; @import "@scalar/api-client-react/style.css";
@reference "../../RootLayout/globals.css";
html, html,
body { body {
@@ -1,3 +1,5 @@
@reference "../../RootLayout/globals.css";
/* Layout Components */ /* Layout Components */
.openapi-operation, .openapi-operation,
.openapi-schemas, .openapi-schemas,
@@ -32,7 +34,7 @@
.openapi-deprecated, .openapi-deprecated,
.openapi-stability { .openapi-stability {
@apply py-0.5 px-1.5 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-sm leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none; @apply py-0.5 px-1.5 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-sm leading-[calc(max(1.20em,1.25rem))] before:content-none! after:!content-none;
} }
.openapi-stability-alpha { .openapi-stability-alpha {
@@ -70,7 +72,7 @@
} }
.openapi-markdown code { .openapi-markdown code {
@apply py-px px-1 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-sm leading-[calc(max(1.20em,1.25rem))] before:!content-none after:!content-none; @apply py-px px-1 min-w-[1.625rem] font-normal w-fit justify-center items-center ring-1 ring-inset ring-tint bg-tint rounded text-sm leading-[calc(max(1.20em,1.25rem))] before:content-none! after:!content-none;
} }
.openapi-markdown pre code { .openapi-markdown pre code {
@@ -79,7 +81,7 @@
/* Markdown Base */ /* Markdown Base */
.openapi-markdown { .openapi-markdown {
@apply prose text-tint-strong prose-p:!my-2 dark:prose-invert max-w-none whitespace-normal; @apply prose text-tint-strong prose-p:my-2! dark:prose-invert max-w-none whitespace-normal;
} }
.openapi-markdown > *:first-child { .openapi-markdown > *:first-child {
@@ -158,7 +160,7 @@
} }
.openapi-schema-root-description.openapi-markdown { .openapi-schema-root-description.openapi-markdown {
@apply prose-sm text-balance mt-1.5 !text-[0.813rem] text-tint overflow-hidden !font-normal select-text prose-strong:font-semibold prose-strong:text-inherit; @apply prose-sm text-balance mt-1.5 text-[0.813rem]! text-tint overflow-hidden font-normal! select-text prose-strong:font-semibold prose-strong:text-inherit;
} }
.openapi-section-schemas > .openapi-section-body > .openapi-schema-root-description { .openapi-section-schemas > .openapi-section-body > .openapi-schema-root-description {
@@ -273,7 +275,7 @@
/* Schema Description */ /* Schema Description */
.openapi-schema-description.openapi-markdown { .openapi-schema-description.openapi-markdown {
@apply prose-sm text-tint overflow-hidden text-pretty !font-normal select-text prose-strong:font-semibold prose-strong:text-inherit; @apply prose-sm text-tint overflow-hidden text-pretty font-normal! select-text prose-strong:font-semibold prose-strong:text-inherit;
} }
.openapi-schema-description.openapi-markdown pre:has(code) { .openapi-schema-description.openapi-markdown pre:has(code) {
@@ -302,7 +304,7 @@
.openapi-schema-pattern code, .openapi-schema-pattern code,
.openapi-schema-enum-value code, .openapi-schema-enum-value code,
.openapi-schema-default 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-subtle 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 */ /* Authentication */
@@ -335,7 +337,7 @@
} }
.openapi-securities-description.openapi-markdown { .openapi-securities-description.openapi-markdown {
@apply prose-sm text-tint !font-normal select-text text-pretty prose-strong:font-semibold prose-strong:text-inherit; @apply prose-sm text-tint font-normal! select-text text-pretty prose-strong:font-semibold prose-strong:text-inherit;
} }
.openapi-securities-label { .openapi-securities-label {
@@ -366,7 +368,7 @@
} }
.openapi-requestbody-description.openapi-markdown { .openapi-requestbody-description.openapi-markdown {
@apply prose-sm text-tint !font-normal text-pretty select-text prose-strong:font-semibold prose-strong:text-inherit; @apply prose-sm text-tint font-normal! text-pretty select-text prose-strong:font-semibold prose-strong:text-inherit;
} }
/* Responses */ /* Responses */
@@ -379,21 +381,23 @@
} }
.openapi-response-tab-content { .openapi-response-tab-content {
@apply flex items-baseline truncate grow shrink max-w-max basis-[60%] mr-auto; @apply flex items-baseline truncate grow shrink max-w-max basis-full mr-auto;
@apply text-left text-pretty relative leading-tight text-tint select-text; @apply text-left text-pretty relative leading-tight text-tint select-text;
} }
.openapi-response-description.openapi-markdown { .openapi-response-description.openapi-markdown {
@apply text-left truncate prose-sm text-sm leading-tight text-tint select-text prose-strong:font-semibold prose-strong:text-inherit; @apply text-left prose-sm text-sm leading-tight text-tint select-text prose-strong:font-semibold prose-strong:text-inherit;
} }
.openapi-disclosure-group-trigger[aria-expanded="true"] .openapi-response-tab-content { .openapi-disclosure-group-trigger[aria-expanded="false"] {
@apply basis-full;
}
.openapi-disclosure-group-trigger[aria-expanded="true"]
.openapi-response-description.openapi-markdown { .openapi-response-description.openapi-markdown {
@apply whitespace-normal; @apply truncate;
@apply [&>*:not(:first-child)]:hidden *:truncate *:!p-0 *:!m-0;
}
.openapi-response-tab-content {
@apply basis-[60%]
}
} }
.openapi-response-body { .openapi-response-body {
@@ -465,7 +469,7 @@
} }
.openapi-path-variable { .openapi-path-variable {
@apply p-px 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-sm leading-none before:!content-none after:!content-none; @apply p-px 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-sm leading-none before:content-none! after:!content-none;
} }
.openapi-path-server { .openapi-path-server {
@@ -551,6 +555,10 @@
@apply text-[0.813rem] truncate text-tint font-normal; @apply text-[0.813rem] truncate text-tint font-normal;
} }
.openapi-response-examples-statuscode-label {
@apply truncate;
}
.openapi-response-examples-panel, .openapi-response-examples-panel,
.openapi-codesample-panel { .openapi-codesample-panel {
@apply flex-1 text-sm relative focus-visible:outline-none; @apply flex-1 text-sm relative focus-visible:outline-none;
@@ -583,10 +591,6 @@ body:has(.openapi-select-popover) {
@apply shrink truncate flex items-center; @apply shrink truncate flex items-center;
} }
.openapi-select > button > span.react-aria-SelectValue span:not(.openapi-statuscode) {
@apply truncate;
}
.openapi-select > button .openapi-markdown { .openapi-select > button .openapi-markdown {
@apply *:leading-none; @apply *:leading-none;
} }
@@ -610,18 +614,18 @@ body:has(.openapi-select-popover) {
.openapi-select-item { .openapi-select-item {
@apply text-sm flex items-center cursor-pointer px-1.5 overflow-hidden py-1 *:truncate text-tint ring-0 border-none rounded !outline-none; @apply text-sm flex items-center cursor-pointer px-1.5 overflow-hidden py-1 *:truncate text-tint ring-0 border-none rounded !outline-none;
@apply hover:bg-tint-hover theme-gradient:hover:bg-tint-12/1 hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-inset contrast-more:hover:ring-current; @apply hover:bg-tint-hover hover:theme-gradient:bg-tint-12/1 hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-inset contrast-more:hover:ring-current;
} }
.openapi-select button .openapi-markdown, .openapi-select button .openapi-markdown,
.openapi-select-item .openapi-markdown { .openapi-select-item .openapi-markdown {
@apply text-[0.813rem] *:truncate; @apply text-[0.813rem] *:truncate *:!p-0 *:!m-0 [&>*:not(:first-child)]:hidden;
} }
.openapi-select-item-selected, .openapi-select-item-selected,
.openapi-select-item-selected .openapi-markdown { .openapi-select-item-selected .openapi-markdown {
@apply text-primary-subtle hover:text-primary hover:bg-primary-hover; @apply text-primary-subtle hover:text-primary hover:bg-primary-hover;
@apply theme-muted:hover:bg-primary-active theme-gradient:hover:bg-primary-active tint:font-semibold; @apply hover:theme-muted:bg-primary-active hover:theme-gradient:bg-primary-active tint:font-semibold;
@apply contrast-more:text-primary contrast-more:hover:text-primary-strong contrast-more:font-semibold; @apply contrast-more:text-primary contrast-more:hover:text-primary-strong contrast-more:font-semibold;
} }
@@ -693,7 +697,7 @@ body:has(.openapi-select-popover) {
} }
.openapi-tabs-tab[aria-selected="true"] { .openapi-tabs-tab[aria-selected="true"] {
@apply !text-primary-subtle after:absolute after:-bottom-[calc(0.375rem_+_1px)] after:z-20 after:left-0 after:w-full after:h-px after:bg-primary-solid after:transition-all; @apply text-primary-subtle! after:absolute after:-bottom-[calc(0.375rem+1px)] after:z-20 after:left-0 after:w-full after:h-px after:bg-primary-solid after:transition-all;
} }
.openapi-tabs-panel { .openapi-tabs-panel {
@@ -769,7 +773,7 @@ body:has(.openapi-select-popover) {
} }
.openapi-schema .openapi-schema-properties .openapi-schema { .openapi-schema .openapi-schema-properties .openapi-schema {
@apply animate-fadeIn [animation-fill-mode:both]; animation: fadeIn 0.2s ease-in-out both;
} }
.openapi-schemas-disclosure > .openapi-disclosure-trigger[aria-expanded="true"] > svg { .openapi-schemas-disclosure > .openapi-disclosure-trigger[aria-expanded="true"] > svg {
@@ -830,7 +834,8 @@ body:has(.openapi-select-popover) {
.openapi-disclosure-trigger:hover > .openapi-disclosure-trigger-label span, .openapi-disclosure-trigger:hover > .openapi-disclosure-trigger-label span,
.openapi-disclosure-trigger[aria-expanded="true"] > .openapi-disclosure-trigger-label span { .openapi-disclosure-trigger[aria-expanded="true"] > .openapi-disclosure-trigger-label span {
@apply block animate-fadeIn; @apply block;
animation: fadeIn 0.2s ease-in-out both;
} }
@media (hover: none) { @media (hover: none) {
@@ -26,7 +26,11 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
} }
const { reusableContent } = resolved; const { reusableContent } = resolved;
if (!reusableContent || !reusableContent.revisionReusableContent.document) { if (
!reusableContent ||
!('document' in reusableContent.revisionReusableContent) ||
!reusableContent.revisionReusableContent.document
) {
return null; return null;
} }
@@ -24,6 +24,8 @@ export function StepperStep(props: BlockProps<DocumentBlockStepperStep>) {
return '-mt-9'; return '-mt-9';
case 'heading-2': case 'heading-2':
return '-mt-[calc(1.25rem+1px)]'; return '-mt-[calc(1.25rem+1px)]';
case 'heading-3':
return '-mt-[calc(0.50rem+1px)]';
default: default:
return ''; return '';
} }
@@ -40,7 +42,7 @@ export function StepperStep(props: BlockProps<DocumentBlockStepperStep>) {
> >
{index + 1} {index + 1}
</div> </div>
<div className="can-override-bg absolute top-9 bottom-2 left-[0.875rem] w-px bg-primary-7 theme-muted:bg-primary-subtle" /> <div className="can-override-bg absolute top-9 bottom-2 left-3.5 w-px bg-primary-7 theme-muted:bg-primary-subtle" />
</div> </div>
<Blocks <Blocks
{...contextProps} {...contextProps}
@@ -50,8 +50,8 @@ export async function RecordCard(
'bg-tint-base', 'bg-tint-base',
'w-[calc(100%+2px)]', 'w-[calc(100%+2px)]',
'h-[calc(100%+2px)]', 'h-[calc(100%+2px)]',
'inset-[-1px]', '-inset-px',
'rounded', 'rounded-sm',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-xl', 'circular-corners:rounded-xl',
'overflow-hidden', 'overflow-hidden',
@@ -65,11 +65,11 @@ export async function RecordCard(
// - If the file is square or portrait, we display it left with 40% of the card width // - If the file is square or portrait, we display it left with 40% of the card width
coverIsSquareOrPortrait coverIsSquareOrPortrait
? [ ? [
'grid-cols-[40%,_1fr]', 'grid-cols-[40%__1fr]',
'min-[432px]:grid-cols-none', 'min-[432px]:grid-cols-none',
'min-[432px]:grid-rows-[auto,1fr]', 'min-[432px]:grid-rows-[auto_1fr]',
] ]
: 'grid-rows-[auto,1fr]' : 'grid-rows-[auto_1fr]'
)} )}
> >
{cover ? ( {cover ? (
@@ -23,9 +23,9 @@ import type { TableRecordKV } from './Table';
import { type VerticalAlignment, getColumnAlignment } from './utils'; import { type VerticalAlignment, getColumnAlignment } from './utils';
const alignmentMap: Record<'text-left' | 'text-center' | 'text-right', string> = { const alignmentMap: Record<'text-left' | 'text-center' | 'text-right', string> = {
'text-left': '[&_*]:text-left text-left', 'text-left': '**:text-left text-left',
'text-center': '[&_*]:text-center text-center', 'text-center': '**:text-center text-center',
'text-right': '[&_*]:text-right text-right', 'text-right': '**:text-right text-right',
}; };
/** /**
@@ -193,7 +193,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
> >
{contentType === 'image' ? ( {contentType === 'image' ? (
<Image <Image
style={['max-h-[1lh]', 'h-[1lh]']} style={['max-h-lh', 'h-lh']}
alt={ref.text} alt={ref.text}
sizes={[{ width: 24 }]} sizes={[{ width: 24 }]}
resize={context.contentContext?.imageResizer} resize={context.contentContext?.imageResizer}
@@ -314,7 +314,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
className={tcls( className={tcls(
'text-sm', 'text-sm',
'whitespace-pre', 'whitespace-pre',
'rounded', 'rounded-sm',
'py-1', 'py-1',
'px-2', 'px-2',
'bg-primary', 'bg-primary',
@@ -6,7 +6,7 @@ export const RecordCardStyles = [
'shadow-1xs', 'shadow-1xs',
'shadow-tint-9/1', 'shadow-tint-9/1',
'depth-flat:shadow-none', 'depth-flat:shadow-none',
'rounded', 'rounded-sm',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-xl', 'circular-corners:rounded-xl',
'dark:shadow-transparent', 'dark:shadow-transparent',
@@ -1,3 +1,5 @@
@reference "../../RootLayout/globals.css";
/* Detect whether a scrollbar exists on the table */ /* Detect whether a scrollbar exists on the table */
@keyframes detect-scroll { @keyframes detect-scroll {
from, from,
@@ -144,7 +144,7 @@ export function DynamicTabs(
<div <div
className={tcls( className={tcls(
'rounded-lg', 'rounded-lg',
'straight-corners:rounded-sm', 'straight-corners:rounded-xs',
'ring-1', 'ring-1',
'ring-inset', 'ring-inset',
'ring-tint-subtle', 'ring-tint-subtle',
@@ -161,7 +161,7 @@ export function DynamicTabs(
'inline-flex', 'inline-flex',
'flex-row', 'flex-row',
'self-stretch', 'self-stretch',
'after:flex-[1]', 'after:flex-1',
'after:bg-tint-12/1', 'after:bg-tint-12/1',
// if last tab is selected, apply rounded to :after element // if last tab is selected, apply rounded to :after element
'[&:has(button.active-tab:last-of-type):after]:rounded-bl-md' '[&:has(button.active-tab:last-of-type):after]:rounded-bl-md'
@@ -180,7 +180,7 @@ export function DynamicTabs(
'[&:has(+_.active-tab)]:rounded-br-md', '[&:has(+_.active-tab)]:rounded-br-md',
//next from active-tab //next from active-tab
'[.active-tab_+_&]:rounded-bl-md', '[.active-tab+&]:rounded-bl-md',
//next from active-tab //next from active-tab
'[.active-tab_+_:after]:rounded-br-md', '[.active-tab_+_:after]:rounded-br-md',
@@ -193,7 +193,7 @@ export function DynamicTabs(
'after:border-tint', 'after:border-tint',
'after:top-[15%]', 'after:top-[15%]',
'after:h-[70%]', 'after:h-[70%]',
'after:w-[1px]', 'after:w-px',
'px-3.5', 'px-3.5',
'py-2', 'py-2',
@@ -212,9 +212,9 @@ export function DynamicTabs(
'active-tab', 'active-tab',
'text-tint-strong', 'text-tint-strong',
'bg-transparent', 'bg-transparent',
'after:[&.active-tab]:border-transparent', '[&.active-tab]:after:border-transparent',
'after:[:has(+_&.active-tab)]:border-transparent', '[:has(+_&.active-tab)]:after:border-transparent',
'after:[:has(&_+)]:border-transparent', '[:has(&_+)]:after:border-transparent',
] ]
: null : null
)} )}
@@ -232,7 +232,7 @@ export function DynamicTabs(
'inline-block', 'inline-block',
'text-sm', 'text-sm',
'transition-[color]', 'transition-[color]',
'font-[500]', 'font-medium',
'relative', 'relative',
'max-w-full', 'max-w-full',
'truncate' 'truncate'
@@ -71,11 +71,7 @@ function Strikethrough(props: MarkedLeafProps<DocumentMarkStrikethrough>) {
} }
function Keyboard(props: MarkedLeafProps<DocumentMarkKeyboard>) { function Keyboard(props: MarkedLeafProps<DocumentMarkKeyboard>) {
return ( return <kbd className="rounded-xs border border-b-2 px-1 font-mono">{props.children}</kbd>;
<kbd className="rounded border px-1 font-mono shadow-[0_1px_0_0_theme(borderColor.DEFAULT)]">
{props.children}
</kbd>
);
} }
function Superscript(props: MarkedLeafProps<DocumentMarkSuperscript>) { function Superscript(props: MarkedLeafProps<DocumentMarkSuperscript>) {
@@ -90,9 +86,9 @@ function Code(props: MarkedLeafProps<DocumentMarkCode>) {
return ( return (
<code <code
className={tcls( className={tcls(
'py-[1px]', 'py-px',
'px-1.5', 'px-1.5',
'min-w-[1.625rem]', 'min-w-6.5',
'justify-center', 'justify-center',
'items-center', 'items-center',
'leading-normal', 'leading-normal',
@@ -100,7 +96,7 @@ function Code(props: MarkedLeafProps<DocumentMarkCode>) {
'ring-inset', 'ring-inset',
'ring-tint', 'ring-tint',
'bg-tint', 'bg-tint',
'rounded', 'rounded-sm',
// Text size is proportional to the font-size of the parent element // Text size is proportional to the font-size of the parent element
'text-[.875em]', 'text-[.875em]',
// We ensure that the code is not making the parent bigger, especially in headings // We ensure that the code is not making the parent bigger, especially in headings
@@ -36,16 +36,16 @@ export function Footer(props: { context: GitBookSiteContext }) {
<div className={tcls(CONTAINER_STYLE, 'px-4', 'py-8', 'lg:py-12', 'mx-auto')}> <div className={tcls(CONTAINER_STYLE, 'px-4', 'py-8', 'lg:py-12', 'mx-auto')}>
<div <div
className={tcls( className={tcls(
'lg:!max-w-none mx-auto grid max-w-3xl site-width-wide:max-w-screen-2xl justify-between gap-12', 'mx-auto grid max-w-3xl site-width-wide:max-w-screen-2xl justify-between gap-12 lg:max-w-none!',
'grid-cols-[auto_auto]', 'grid-cols-[auto_auto]',
'lg:grid-cols-[18rem_minmax(auto,_48rem)_auto]', 'lg:grid-cols-[18rem_minmax(auto,48rem)_auto]',
'xl:grid-cols-[18rem_minmax(auto,_48rem)_14rem]', 'xl:grid-cols-[18rem_minmax(auto,48rem)_14rem]',
'site-width-wide:lg:grid-cols-[18rem_minmax(auto,_80rem)_auto]', 'lg:site-width-wide:grid-cols-[18rem_minmax(auto,80rem)_auto]',
'site-width-wide:xl:grid-cols-[18rem_minmax(auto,_80rem)_14rem]', 'xl:site-width-wide:grid-cols-[18rem_minmax(auto,80rem)_14rem]',
'page-no-toc:lg:grid-cols-[minmax(auto,_48rem)_auto]', 'lg:page-no-toc:grid-cols-[minmax(auto,48rem)_auto]',
'page-no-toc:xl:grid-cols-[14rem_minmax(auto,_48rem)_14rem]', 'xl:page-no-toc:grid-cols-[14rem_minmax(auto,48rem)_14rem]',
'[body:has(.site-width-wide,.page-no-toc)_&]:lg:grid-cols-[minmax(auto,_90rem)_auto]', 'lg:[body:has(.site-width-wide,.page-no-toc)_&]:grid-cols-[minmax(auto,90rem)_auto]',
'[body:has(.site-width-wide,.page-no-toc)_&]:xl:grid-cols-[14rem_minmax(auto,_90rem)_14rem]' 'xl:[body:has(.site-width-wide,.page-no-toc)_&]:grid-cols-[14rem_minmax(auto,90rem)_14rem]'
)} )}
> >
{ {
@@ -74,8 +74,8 @@ export function Footer(props: { context: GitBookSiteContext }) {
'lg:max-h-12', 'lg:max-h-12',
'object-contain', 'object-contain',
'object-left', 'object-left',
'rounded', 'rounded-sm',
'straight-corners:rounded-sm', 'straight-corners:rounded-xs',
]} ]}
sizes={[ sizes={[
{ {
@@ -103,7 +103,7 @@ export function Footer(props: { context: GitBookSiteContext }) {
customization.footer.groups?.length > 0 ? ( customization.footer.groups?.length > 0 ? (
<div <div
className={tcls( className={tcls(
'col-span-2 page-has-toc:lg:col-span-1 page-has-toc:lg:col-start-2 page-no-toc:xl:col-span-1 page-no-toc:xl:col-start-2' 'col-span-2 lg:page-has-toc:col-span-1 lg:page-has-toc:col-start-2 xl:page-no-toc:col-span-1 xl:page-no-toc:col-start-2'
)} )}
> >
<div className="mx-auto flex max-w-3xl site-width-wide:max-w-screen-2xl flex-col gap-10 sm:flex-row sm:gap-6"> <div className="mx-auto flex max-w-3xl site-width-wide:max-w-screen-2xl flex-col gap-10 sm:flex-row sm:gap-6">
@@ -85,7 +85,7 @@ export function Header(props: {
'text-tint-strong', 'text-tint-strong',
'theme-bold:text-header-link', 'theme-bold:text-header-link',
'hover:bg-tint-hover', 'hover:bg-tint-hover',
'theme-bold:hover:bg-header-link/3', 'hover:theme-bold:bg-header-link/3',
'page-no-toc:hidden' 'page-no-toc:hidden'
)} )}
/> />
@@ -110,7 +110,7 @@ export function Header(props: {
'lg:max-w-lg', 'lg:max-w-lg',
'lg:ml-[max(calc((100%-18rem-48rem)/2),1.5rem)]', // container (100%) - sidebar (18rem) - content (48rem) 'lg:ml-[max(calc((100%-18rem-48rem)/2),1.5rem)]', // container (100%) - sidebar (18rem) - content (48rem)
'xl:ml-[max(calc((100%-18rem-48rem-14rem-3rem)/2),1.5rem)]', // container (100%) - sidebar (18rem) - content (48rem) - outline (14rem) - margin (3rem) 'xl:ml-[max(calc((100%-18rem-48rem-14rem-3rem)/2),1.5rem)]', // container (100%) - sidebar (18rem) - content (48rem) - outline (14rem) - margin (3rem)
'page-no-toc:lg:ml-[max(calc((100%-18rem-48rem-18rem-3rem)/2),0rem)]', 'lg:page-no-toc:ml-[max(calc((100%-18rem-48rem-18rem-3rem)/2),0rem)]',
'md:mr-auto', 'md:mr-auto',
'order-last', 'order-last',
'md:order-[unset]', 'md:order-[unset]',
@@ -119,7 +119,7 @@ function HeaderItemButton(
className={tcls( className={tcls(
{ {
'button-primary': 'button-primary':
'theme-bold:bg-header-link theme-bold:text-header-background theme-bold:shadow-none theme-bold:hover:bg-header-link theme-bold:hover:text-header-background theme-bold:hover:shadow-none', 'theme-bold:bg-header-link theme-bold:text-header-background theme-bold:shadow-none hover:theme-bold:bg-header-link hover:theme-bold:text-header-background hover:theme-bold:shadow-none',
'button-secondary': '', 'button-secondary': '',
}[linkStyle] }[linkStyle]
)} )}
@@ -157,7 +157,7 @@ function getHeaderLinkClassName(_props: { headerPreset: CustomizationHeaderPrese
'links-accent:py-0.5', // Prevent underline from being cut off at the bottom 'links-accent:py-0.5', // Prevent underline from being cut off at the bottom
'theme-bold:text-header-link', 'theme-bold:text-header-link',
'theme-bold:hover:!text-header-link/7' 'hover:theme-bold:text-header-link/7!'
); );
} }
@@ -53,7 +53,7 @@ export async function HeaderLogo(props: HeaderLogoProps) {
'min-w-0', 'min-w-0',
'max-w-40', 'max-w-40',
'lg:max-w-64', 'lg:max-w-64',
'site-header-none:page-no-toc:lg:max-w-56', 'lg:site-header-none:page-no-toc:max-w-56',
'max-h-10', 'max-h-10',
'lg:max-h-12', 'lg:max-h-12',
'h-full', 'h-full',
@@ -47,7 +47,7 @@ export function HeaderMobileMenu(props: Partial<React.ButtonHTMLAttributes<HTMLB
aria-label={tString(language, 'table_of_contents_button_label')} aria-label={tString(language, 'table_of_contents_button_label')}
onClick={toggleNavigation} onClick={toggleNavigation}
className={tcls( className={tcls(
'flex flex-row items-center rounded straight-corners:rounded-sm px-2 py-1', 'flex flex-row items-center rounded-sm straight-corners:rounded-xs px-2 py-1',
props.className props.className
)} )}
> >
@@ -107,7 +107,7 @@ export async function PDFPage(props: {
'p-4', 'p-4',
'rounded-full', 'rounded-full',
'bg-white', 'bg-white',
'shadow-sm', 'shadow-xs',
'hover:shadow-md', 'hover:shadow-md',
'border-slate-300', 'border-slate-300',
'border' 'border'
@@ -132,7 +132,7 @@ export async function PDFPage(props: {
'p-4', 'p-4',
'rounded-full', 'rounded-full',
'bg-white', 'bg-white',
'shadow-sm', 'shadow-xs',
'hover:shadow-md', 'hover:shadow-md',
'border-slate-300', 'border-slate-300',
'border' 'border'
@@ -280,7 +280,7 @@ function PrintPage(
'print:p-0', 'print:p-0',
'shadow-xl', 'shadow-xl',
'print:shadow-none', 'print:shadow-none',
'rounded-sm', 'rounded-xs',
'bg-white', 'bg-white',
'min-h-[29.7cm]', 'min-h-[29.7cm]',
'print:min-h-0', 'print:min-h-0',
@@ -99,7 +99,7 @@ export function PageControlButtons(props: {
'bg-yellow-100', 'bg-yellow-100',
'border-yellow-400', 'border-yellow-400',
'text-yellow-800', 'text-yellow-800',
'shadow-sm', 'shadow-xs',
'border', 'border',
'rounded-md', 'rounded-md',
'p-4', 'p-4',
@@ -140,7 +140,7 @@ export function PageControlButtons(props: {
'py-2', 'py-2',
'bg-slate-100', 'bg-slate-100',
'rounded-full', 'rounded-full',
'shadow-sm', 'shadow-xs',
'border-slate-300', 'border-slate-300',
'border' 'border'
)} )}
@@ -48,15 +48,15 @@ export function PageAside(props: {
'group/aside', 'group/aside',
'hidden', 'hidden',
'xl:flex', 'xl:flex',
'chat-open:xl:max-3xl:hidden', 'xl:max-3xl:chat-open:hidden',
'chat-open:xl:max-3xl:opacity-0', 'xl:max-3xl:chat-open:opacity-0',
'max-w-56', 'max-w-56',
'chat-open:xl:max-3xl:*:w-56', 'xl:max-3xl:*:chat-open:w-56',
'chat-open:xl:max-3xl:max-w-0', 'xl:max-3xl:chat-open:max-w-0',
'chat-open:xl:max-3xl:ml-0', 'xl:max-3xl:chat-open:ml-0',
'motion-safe:xl:transition-all motion-safe:xl:duration-300', 'motion-safe:xl:transition-all motion-safe:xl:duration-300',
'motion-safe:[transition-behavior:allow-discrete]', 'motion-safe:transition-discrete',
'flex-col', 'flex-col',
'basis-56', 'basis-56',
@@ -73,29 +73,29 @@ export function PageAside(props: {
'lg:max-h-screen', 'lg:max-h-screen',
// With header // With header
'site-header:lg:top-16', 'lg:site-header:top-16',
'site-header:lg:max-h-[calc(100vh_-_4rem)]', 'lg:site-header:max-h-[calc(100vh-4rem)]',
// With header & sections // With header & sections
'site-header-sections:lg:top-[6.75rem]', 'lg:site-header-sections:top-27',
'site-header-sections:lg:max-h-[calc(100vh_-_6.75rem)]', 'lg:site-header-sections:max-h-[calc(100vh-6.75rem)]',
// When in api page mode, we display it as an overlay on non-large resolutions // When in api page mode, we display it as an overlay on non-large resolutions
'page-api-block:xl:max-2xl:z-10', 'xl:max-2xl:page-api-block:z-10',
'page-api-block:xl:max-2xl:fixed', 'xl:max-2xl:page-api-block:fixed',
'page-api-block:xl:max-2xl:right-8', 'xl:max-2xl:page-api-block:right-8',
'page-api-block:xl:max-2xl:w-56', 'xl:max-2xl:page-api-block:w-56',
'page-api-block:xl:max-2xl:bg-tint/9', 'xl:max-2xl:page-api-block:bg-tint/9',
'page-api-block:xl:max-2xl:contrast-more:bg-tint', 'xl:max-2xl:contrast-more:page-api-block:bg-tint',
'page-api-block:xl:max-2xl:backdrop-blur-lg', 'xl:max-2xl:page-api-block:backdrop-blur-lg',
'page-api-block:xl:max-2xl:border', 'xl:max-2xl:page-api-block:border',
'page-api-block:xl:max-2xl:border-tint', 'xl:max-2xl:page-api-block:border-tint',
'page-api-block:xl:max-2xl:hover:shadow-lg', 'xl:max-2xl:page-api-block:hover:shadow-lg',
'page-api-block:xl:max-2xl:hover:shadow-tint-12/1', 'xl:max-2xl:page-api-block:hover:shadow-tint-12/1',
'page-api-block:xl:max-2xl:dark:hover:shadow-tint-1/1', 'xl:max-2xl:dark:page-api-block:hover:shadow-tint-1/1',
'page-api-block:xl:max-2xl:rounded-md', 'xl:max-2xl:page-api-block:rounded-md',
'page-api-block:xl:max-2xl:h-auto', 'xl:max-2xl:page-api-block:h-auto',
'page-api-block:xl:max-2xl:my-9', 'xl:max-2xl:page-api-block:my-9',
'page-api-block:px-2', 'page-api-block:px-2',
'page-api-block:py-1.5' 'page-api-block:py-1.5'
)} )}
@@ -105,7 +105,7 @@ export function PageAside(props: {
<div <div
className={tcls( className={tcls(
'hidden', 'hidden',
'page-api-block:xl:max-2xl:flex', 'xl:max-2xl:page-api-block:flex',
'text-xs', 'text-xs',
'tracking-wide', 'tracking-wide',
'font-semibold', 'font-semibold',
@@ -124,7 +124,7 @@ export function PageAside(props: {
'size-3', 'size-3',
'opacity-6', 'opacity-6',
'ml-auto', 'ml-auto',
'page-api-block:xl:max-2xl:group-hover/aside:hidden' 'xl:max-2xl:page-api-block:group-hover/aside:hidden'
)} )}
/> />
</div> </div>
@@ -140,16 +140,16 @@ export function PageAside(props: {
'sticky', 'sticky',
'lg:top:0', 'lg:top:0',
'site-header:lg:top-16', 'lg:site-header:top-16',
'site-header-sections:lg:top-[6.75rem]', 'lg:site-header-sections:top-27',
'gap-6', 'gap-6',
'pt-8', 'pt-10',
'page-api-block:xl:max-2xl:py-0', 'xl:max-2xl:page-api-block:py-0',
// Hide it for api page, until hovered // Hide it for api page, until hovered
'page-api-block:xl:max-2xl:hidden', 'xl:max-2xl:page-api-block:hidden',
'page-api-block:xl:max-2xl:group-hover/aside:flex' 'xl:max-2xl:page-api-block:group-hover/aside:flex'
)} )}
> >
{document ? ( {document ? (
@@ -166,15 +166,15 @@ export function PageAside(props: {
'border-t', 'border-t',
'first:border-none', 'first:border-none',
'border-tint-subtle', 'border-tint-subtle',
'py-4', 'py-6',
'first:pt-0', 'first:pt-0',
'page-api-block:xl:max-2xl:px-3', 'xl:max-2xl:page-api-block:px-3',
'empty:hidden' 'empty:hidden'
)} )}
> >
{withPageFeedback ? ( {withPageFeedback ? (
<React.Suspense fallback={null}> <React.Suspense fallback={null}>
<PageFeedbackForm pageId={page.id} className={tcls('mt-2')} /> <PageFeedbackForm pageId={page.id} />
</React.Suspense> </React.Suspense>
) : null} ) : null}
{customization.git.showEditLink && space.gitSync?.url && page.git ? ( {customization.git.showEditLink && space.gitSync?.url && page.git ? (
@@ -237,8 +237,8 @@ export function PageAside(props: {
) : null} ) : null}
<div <div
className={tcls( 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', '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 xl:max-2xl:page-api-block:hidden xl:max-2xl:page-api-block:pb-0 xl:max-2xl:page-api-block:group-hover/aside:flex [html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
'page-api-block:xl:max-2xl:bg-transparent' 'xl:max-2xl:page-api-block:bg-transparent'
)} )}
> >
{/* Mode Switcher */} {/* Mode Switcher */}
@@ -128,7 +128,7 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
> >
{section.tag ? ( {section.tag ? (
<span <span
className={`-mt-0.5 openapi-method !text-xs openapi-method-${section.tag.toLowerCase()}`} className={`-mt-0.5 openapi-method text-xs! openapi-method-${section.tag.toLowerCase()}`}
> >
{section.tag} {section.tag}
</span> </span>
@@ -50,7 +50,7 @@ export function PageBody(props: {
<main <main
className={tcls( className={tcls(
'relative min-w-0 flex-1', 'relative min-w-0 flex-1',
'mx-auto max-w-screen-2xl py-8', 'mx-auto max-w-screen-2xl py-11',
// Allow words to break if they are too long. // Allow words to break if they are too long.
'break-anywhere', 'break-anywhere',
pageWidthWide ? 'page-width-wide 2xl:px-8' : 'page-width-default', pageWidthWide ? 'page-width-wide 2xl:px-8' : 'page-width-default',
@@ -77,7 +77,7 @@ export async function PageBodyBlankslate(props: {
'gap-4', 'gap-4',
'grid-cols-1', 'grid-cols-1',
'sm:grid-cols-2', 'sm:grid-cols-2',
'page-width-wide:md:grid-cols-3' 'md:page-width-wide:grid-cols-3'
)} )}
> >
{pageElements} {pageElements}
@@ -89,7 +89,7 @@ export async function PageCover(props: {
'lg:-ml-12', 'lg:-ml-12',
!page.layout.tableOfContents && !page.layout.tableOfContents &&
context.customization.header.preset !== 'none' context.customization.header.preset !== 'none'
? 'xl:-ml-[19rem]' ? 'xl:-ml-76'
: null, : null,
] ]
: [ : [
@@ -108,7 +108,7 @@ function NavigationCard(
reversed ? 'pl-4' : null, reversed ? 'pl-4' : null,
'border', 'border',
'border-tint-subtle', 'border-tint-subtle',
'rounded', 'rounded-sm',
'circular-corners:rounded-2xl', 'circular-corners:rounded-2xl',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'hover:border-primary', 'hover:border-primary',
@@ -45,8 +45,8 @@ export async function PageHeader(props: {
(withAIChat || pageActions.markdown || pageActions.externalAI) ? ( (withAIChat || pageActions.markdown || pageActions.externalAI) ? (
<div <div
className={tcls( className={tcls(
'float-right mb-2 ml-4 page-api-block:xl:max-2xl:mr-[14.5rem]', 'float-right mb-2 ml-4 xl:max-2xl:page-api-block:mr-58',
ancestors.length > 0 ? '-mt-2' : 'xs:mt-2' ancestors.length > 0 ? '-mt-2' : '-mt-3 xs:mt-2'
)} )}
> >
<AIActionsDropdown <AIActionsDropdown
@@ -94,7 +94,7 @@ export function PageFeedbackForm(props: {
<textarea <textarea
ref={inputRef} ref={inputRef}
name="comment" name="comment"
className="max-h-40 min-h-16 grow rounded straight-corners:rounded-none bg-tint-base p-2 ring-1 ring-tint ring-inset placeholder:text-sm placeholder:text-tint contrast-more:ring-tint-12 contrast-more:placeholder:text-tint-strong" className="max-h-40 min-h-16 grow rounded-sm straight-corners:rounded-none bg-tint-base p-2 ring-1 ring-tint ring-inset placeholder:text-sm placeholder:text-tint contrast-more:ring-tint-12 contrast-more:placeholder:text-tint-strong"
placeholder={tString(languages, 'was_this_helpful_comment')} placeholder={tString(languages, 'was_this_helpful_comment')}
aria-label={tString(languages, 'was_this_helpful_comment')} aria-label={tString(languages, 'was_this_helpful_comment')}
onChange={(e) => setComment(e.target.value)} onChange={(e) => setComment(e.target.value)}
@@ -151,11 +151,11 @@ function RatingButton(
<button <button
className={tcls( className={tcls(
'p-2 first:rounded-l-full first:pl-2.5 last:rounded-r-full last:pr-2.5 hover:bg-primary-hover hover:text-primary-strong', 'p-2 first:rounded-l-full first:pl-2.5 last:rounded-r-full last:pr-2.5 hover:bg-primary-hover hover:text-primary-strong',
'disabled:cursor-not-allowed disabled:hover:bg-inherit disabled:hover:text-inherit disabled:dark:hover:text-inherit', 'disabled:cursor-not-allowed disabled:hover:bg-inherit disabled:hover:text-inherit dark:disabled:hover:text-inherit',
'ring-tint contrast-more:hover:ring-1', 'ring-tint contrast-more:hover:ring-1',
active active
? 'bg-primary-active text-primary-strong disabled:hover:bg-primary-active disabled:hover:text-primary-strong contrast-more:ring-2 contrast-more:hover:ring-2' ? 'bg-primary-active text-primary-strong disabled:hover:bg-primary-active disabled:hover:text-primary-strong contrast-more:ring-2 contrast-more:hover:ring-2'
: 'disabled:opacity-7 disabled:contrast-more:ring-0' : 'disabled:opacity-7 contrast-more:disabled:ring-0'
)} )}
type="button" type="button"
{...attr} {...attr}
@@ -4,6 +4,7 @@ import type React from 'react';
import { TranslateContext } from '@/intl/client'; import { TranslateContext } from '@/intl/client';
import type { TranslationLanguage } from '@/intl/translations'; import type { TranslationLanguage } from '@/intl/translations';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import { LoadingStateProvider } from '../primitives/LoadingStateProvider'; import { LoadingStateProvider } from '../primitives/LoadingStateProvider';
export function ClientContexts(props: { export function ClientContexts(props: {
@@ -14,7 +15,9 @@ export function ClientContexts(props: {
return ( return (
<TranslateContext.Provider value={language}> <TranslateContext.Provider value={language}>
<TooltipProvider delayDuration={200}>
<LoadingStateProvider>{children}</LoadingStateProvider> <LoadingStateProvider>{children}</LoadingStateProvider>
</TooltipProvider>
</TranslateContext.Provider> </TranslateContext.Provider>
); );
} }
@@ -1,6 +1,213 @@
@tailwind base; @import "tailwindcss";
@tailwind components;
@tailwind utilities; @config '../../../tailwind.config.ts';
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@utility hide-scroll {
/* hide scroll bar but still scroll */
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
@utility linear-mask-gradient {
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 96px, rgba(0, 0, 0, 0));
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 96px, rgba(0, 0, 0, 0));
}
@utility linear-mask-util {
mask-image: linear-gradient(to bottom, white, white);
mask-image: linear-gradient(to bottom, white, white);
}
@utility grid-area-1-1 {
grid-area: 1 / 1;
grid-area: 1 / 1;
}
@utility gutter-stable {
scrollbar-gutter: stable;
scrollbar-gutter: stable;
}
@utility triangle {
position: relative;
background-color: orange;
text-align: left;
transform: rotate(-60deg) skewX(-30deg) scale(1, 0.866);
&:before {
content: "";
position: absolute;
background-color: inherit;
}
&:after {
content: "";
position: absolute;
background-color: inherit;
}
width: inherit;
height: inherit;
border-top-right-radius: 30%;
&:before {
width: inherit;
height: inherit;
border-top-right-radius: 30%;
}
&:after {
width: inherit;
height: inherit;
border-top-right-radius: 30%;
}
&:before {
transform: rotate(-135deg) skewX(-45deg) scale(1.414, 0.707) translate(0, -50%);
}
&:after {
transform: rotate(135deg) skewY(-45deg) scale(0.707, 1.414) translate(50%);
}
position: relative;
background-color: orange;
text-align: left;
transform: rotate(-60deg) skewX(-30deg) scale(1, 0.866);
&:before {
content: "";
position: absolute;
background-color: inherit;
}
&:after {
content: "";
position: absolute;
background-color: inherit;
}
width: inherit;
height: inherit;
border-top-right-radius: 30%;
&:before {
width: inherit;
height: inherit;
border-top-right-radius: 30%;
}
&:after {
width: inherit;
height: inherit;
border-top-right-radius: 30%;
}
&:before {
transform: rotate(-135deg) skewX(-45deg) scale(1.414, 0.707) translate(0, -50%);
}
&:after {
transform: rotate(135deg) skewY(-45deg) scale(0.707, 1.414) translate(50%);
}
}
@utility break-anywhere {
word-break: break-word;
@supports (overflow-wrap: anywhere) {
word-break: break-word;
overflow-wrap: anywhere;
}
word-break: break-word;
@supports (overflow-wrap: anywhere) {
word-break: break-word;
overflow-wrap: anywhere;
}
}
@utility bg-gradient-primary {
@apply bg-linear-to-bl from-primary-4 to-tint-base to-60% bg-fixed;
@apply bg-linear-to-bl from-primary-4 to-tint-base to-60% bg-fixed;
}
@utility bg-gradient-tint {
@apply bg-linear-to-bl from-tint-4 to-tint-base to-60% bg-fixed;
@apply bg-linear-to-bl from-tint-4 to-tint-base to-60% bg-fixed;
}
@utility scroll-nojump {
/**
* Utility to prevent horizontal jumps when the vertical scrollbar appears
* It should be used on main containers of the layout, while preserving horizontal delimiters.
* It basically adds a margin-left with the width of the scrollbar, and a width that compensates for it; when a scrollbar is displayed.
* Inspired by https://aykevl.nl/2014/09/fix-jumping-scrollbar, but adapted to work on child elements of "html"
*/
width: 100%;
@media screen and (min-width: 1536px) {
margin-left: var(--scrollbar-width);
margin-right: 0;
width: calc(100% - var(--scrollbar-width));
body:has(.page-full-width) & {
margin-left: 0;
width: 100%;
}
}
/**
* Utility to prevent horizontal jumps when the vertical scrollbar appears
* It should be used on main containers of the layout, while preserving horizontal delimiters.
* It basically adds a margin-left with the width of the scrollbar, and a width that compensates for it; when a scrollbar is displayed.
* Inspired by https://aykevl.nl/2014/09/fix-jumping-scrollbar, but adapted to work on child elements of "html"
*/
width: 100%;
@media screen and (min-width: 1536px) {
margin-left: var(--scrollbar-width);
margin-right: 0;
width: calc(100% - var(--scrollbar-width));
}
}
@utility page-full-width {
@media screen and (min-width: 1536px) {
body:has(&) .scroll-nojump {
margin-left: 0;
width: 100%;
}
}
}
@utility elevate-link {
& a[href]:not(.link-overlay) {
position: relative;
z-index: 20;
}
}
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@layer base { @layer base {
:root { :root {
@@ -28,7 +235,12 @@
button, button,
input, input,
textarea { textarea {
@apply outline-2 outline-primary; @apply focus-visible:outline-2 focus-visible:outline-primary;
}
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
} }
code, code,
@@ -54,92 +266,10 @@
} }
.flip-heading-hash { .flip-heading-hash {
@apply [&:is(h1,h2,h3,h4)]:flex-row-reverse; @apply [h1,h2,h3,h4]:content-start;
@apply [&:is(h1,h2,h3,h4)]:[justify-content:start]; @apply [h1,h2,h3,h4]:auto-cols-[auto_1fr];
@apply [&:is(h1,h2,h3,h4)>div:first-child]:ml-2; @apply [&:is(h1,h2,h3,h4)>div:first-child]:[grid-area:1/2];
} @apply [&:is(h1,h2,h3,h4)>div:first-child]:ml-1;
}
@layer utilities {
.linear-mask-gradient {
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 96px, rgba(0, 0, 0, 0));
}
.linear-mask-util {
mask-image: linear-gradient(to bottom, white, white);
}
.grid-area-1-1 {
grid-area: 1 / 1;
}
.gutter-stable {
scrollbar-gutter: stable;
}
.triangle {
position: relative;
background-color: orange;
text-align: left;
transform: rotate(-60deg) skewX(-30deg) scale(1, 0.866);
}
.triangle:before,
.triangle:after {
content: "";
position: absolute;
background-color: inherit;
}
.triangle,
.triangle:before,
.triangle:after {
width: inherit;
height: inherit;
border-top-right-radius: 30%;
}
.triangle:before {
transform: rotate(-135deg) skewX(-45deg) scale(1.414, 0.707) translate(0, -50%);
}
.triangle:after {
transform: rotate(135deg) skewY(-45deg) scale(0.707, 1.414) translate(50%);
}
.break-anywhere {
word-break: break-word;
}
@supports (overflow-wrap: anywhere) {
.break-anywhere {
word-break: break-word;
overflow-wrap: anywhere;
}
}
.bg-gradient-primary {
@apply bg-gradient-to-bl from-primary-4 to-tint-base to-60% bg-fixed;
}
.bg-gradient-tint {
@apply bg-gradient-to-bl from-tint-4 to-tint-base to-60% bg-fixed;
}
/**
* Utility to prevent horizontal jumps when the vertical scrollbar appears
* It should be used on main containers of the layout, while preserving horizontal delimiters.
* It basically adds a margin-left with the width of the scrollbar, and a width that compensates for it; when a scrollbar is displayed.
* Inspired by https://aykevl.nl/2014/09/fix-jumping-scrollbar, but adapted to work on child elements of "html"
*/
.scroll-nojump {
width: 100%;
}
@media screen and (min-width: 1536px) {
.scroll-nojump {
margin-left: var(--scrollbar-width);
margin-right: 0;
width: calc(100% - var(--scrollbar-width));
}
}
.elevate-link {
& a[href]:not(.link-overlay) {
position: relative;
z-index: 20;
}
} }
} }
@@ -160,83 +290,83 @@ html.announcement-hidden #announcement-banner {
/* Shiki theme */ /* Shiki theme */
:root { :root {
--shiki-color-text: theme("colors.tint.11"); --shiki-color-text: rgb(var(--tint-11));
--shiki-token-punctuation: theme("colors.tint.11"); --shiki-token-punctuation: rgb(var(--tint-11));
--shiki-token-comment: theme("colors.neutral.9/.7"); --shiki-token-comment: rgb(var(--neutral-9));
--shiki-token-link: theme("colors.primary.10"); --shiki-token-link: rgb(var(--primary-10));
--shiki-token-constant: theme("colors.warning.10"); --shiki-token-constant: rgb(var(--warning-10));
--shiki-token-string: theme("colors.warning.10"); --shiki-token-string: rgb(var(--warning-10));
--shiki-token-string-expression: theme("colors.success.10"); --shiki-token-string-expression: rgb(var(--success-10));
--shiki-token-keyword: theme("colors.danger.10"); --shiki-token-keyword: rgb(var(--danger-10));
--shiki-token-parameter: theme("colors.warning.10"); --shiki-token-parameter: rgb(var(--warning-10));
--shiki-token-function: theme("colors.primary.10"); --shiki-token-function: rgb(var(--primary-10));
--shiki-token-inserted: theme("colors.success.10"); --shiki-token-inserted: rgb(var(--success-10));
--shiki-token-deleted: theme("colors.danger.10"); --shiki-token-deleted: rgb(var(--danger-10));
--shiki-token-changed: theme("colors.tint.12"); --shiki-token-changed: rgb(var(--tint-12));
} }
@media (prefers-contrast: more) { @media (prefers-contrast: more) {
:root { :root {
--shiki-color-text: theme("colors.tint.12"); --shiki-color-text: rgb(var(--tint-12));
--shiki-token-punctuation: theme("colors.tint.12"); --shiki-token-punctuation: rgb(var(--tint-12));
--shiki-token-comment: theme("colors.neutral.11"); --shiki-token-comment: rgb(var(--neutral-11));
--shiki-token-link: theme("colors.primary.11"); --shiki-token-link: rgb(var(--primary-11));
--shiki-token-constant: theme("colors.warning.11"); --shiki-token-constant: rgb(var(--warning-11));
--shiki-token-string: theme("colors.warning.11"); --shiki-token-string: rgb(var(--warning-11));
--shiki-token-string-expression: theme("colors.success.11"); --shiki-token-string-expression: rgb(var(--success-11));
--shiki-token-keyword: theme("colors.danger.11"); --shiki-token-keyword: rgb(var(--danger-11));
--shiki-token-parameter: theme("colors.warning.11"); --shiki-token-parameter: rgb(var(--warning-11));
--shiki-token-function: theme("colors.primary.11"); --shiki-token-function: rgb(var(--primary-11));
--shiki-token-inserted: theme("colors.success.11"); --shiki-token-inserted: rgb(var(--success-11));
--shiki-token-deleted: theme("colors.danger.11"); --shiki-token-deleted: rgb(var(--danger-11));
--shiki-token-changed: theme("colors.tint.12"); --shiki-token-changed: rgb(var(--tint-12));
} }
} }
html.dark { html.dark {
/* Override select colors to have more contrast */ /* Override select colors to have more contrast */
--shiki-token-comment: theme("colors.neutral.9"); --shiki-token-comment: rgb(var(--neutral-9));
--shiki-token-constant: theme("colors.warning.11"); --shiki-token-constant: rgb(var(--warning-11));
--shiki-token-string: theme("colors.warning.11"); --shiki-token-string: rgb(var(--warning-11));
--shiki-token-string-expression: theme("colors.success.11"); --shiki-token-string-expression: rgb(var(--success-11));
--shiki-token-keyword: theme("colors.danger.11"); --shiki-token-keyword: rgb(var(--danger-11));
--shiki-token-parameter: theme("colors.warning.11"); --shiki-token-parameter: rgb(var(--warning-11));
--shiki-token-function: theme("colors.primary.11"); --shiki-token-function: rgb(var(--primary-11));
} }
.code-monochrome { .code-monochrome {
--shiki-token-constant: theme("colors.tint.11"); --shiki-token-constant: rgb(var(--tint-11));
--shiki-token-string: theme("colors.tint.12"); --shiki-token-string: rgb(var(--tint-12));
--shiki-token-string-expression: theme("colors.tint.12"); --shiki-token-string-expression: rgb(var(--tint-12));
--shiki-token-keyword: theme("colors.primary.10"); --shiki-token-keyword: rgb(var(--primary-10));
--shiki-token-parameter: theme("colors.tint.9"); --shiki-token-parameter: rgb(var(--tint-9));
--shiki-token-function: theme("colors.primary.9"); --shiki-token-function: rgb(var(--primary-9));
} }
html.dark.code-monochrome { html.dark.code-monochrome {
--shiki-token-constant: theme("colors.tint.11"); --shiki-token-constant: rgb(var(--tint-11));
--shiki-token-string: theme("colors.tint.12"); --shiki-token-string: rgb(var(--tint-12));
--shiki-token-string-expression: theme("colors.tint.12"); --shiki-token-string-expression: rgb(var(--tint-12));
--shiki-token-keyword: theme("colors.primary.11"); --shiki-token-keyword: rgb(var(--primary-11));
--shiki-token-parameter: theme("colors.tint.10"); --shiki-token-parameter: rgb(var(--tint-10));
--shiki-token-function: theme("colors.primary.10"); --shiki-token-function: rgb(var(--primary-10));
} }
/* Code blocks */ /* Code blocks */
.highlight-line { .highlight-line {
@apply grid [grid-template-columns:subgrid] col-span-2 relative ring-1 ring-transparent hover:ring-tint-hover hover:z-[1] rounded; @apply grid grid-cols-subgrid col-span-2 relative ring-1 ring-transparent hover:ring-tint-hover hover:z-1 rounded-sm;
@apply only:hover:ring-transparent; @apply only:hover:ring-transparent;
@apply [counter-increment:line]; @apply [counter-increment:line];
&.highlighted { &.highlighted {
@apply bg-tint-active; @apply bg-tint-active;
@apply first:rounded-t-md *:first:mt-1; @apply first:rounded-t-md first:*:mt-1;
@apply last:rounded-b-md *:last:mb-1; @apply last:rounded-b-md last:*:mb-1;
@apply rounded-none; @apply rounded-none;
} }
@@ -254,11 +384,11 @@ html.dark.code-monochrome {
} }
.highlight-line-number { .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-subtle contrast-more:from-tint-base theme-muted:from-tint-base [html.theme-bold.sidebar-filled_&]:from-tint-base to-transparent; @apply text-sm text-right pr-3.5 rounded-l pl-2 sticky left-[-3px] bg-linear-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)]; @apply before:text-tint before:content-[counter(line)];
.highlight-line.highlighted > & { .highlight-line.highlighted > & {
@apply before:text-tint bg-gradient-to-r from-80% from-tint-active to-transparent; @apply before:text-tint bg-linear-to-r from-80% from-tint-active to-transparent;
} }
} }
@@ -21,8 +21,8 @@ export function HighlightQuery(props: {
'px-0.5', 'px-0.5',
'-mx-0.5', '-mx-0.5',
'py-0.5', 'py-0.5',
'rounded', 'rounded-sm',
'straight-corners:rounded-sm', 'straight-corners:rounded-xs',
'transition-colors', 'transition-colors',
'group-hover:bg-primary-active', 'group-hover:bg-primary-active',
'group-hover:text-contrast-primary-active', 'group-hover:text-contrast-primary-active',
@@ -15,7 +15,7 @@ import { useTrackEvent } from '../Insights';
import { Link } from '../primitives'; import { Link } from '../primitives';
import { useSearchAskContext } from './SearchAskContext'; import { useSearchAskContext } from './SearchAskContext';
import { type AskAnswerResult, type AskAnswerSource, streamAskQuestion } from './server-actions'; import { type AskAnswerResult, type AskAnswerSource, streamAskQuestion } from './server-actions';
import { useSearch, useSearchLink } from './useSearch'; import { useSearchLink } from './useSearch';
export type SearchAskState = export type SearchAskState =
| { | {
@@ -37,7 +37,6 @@ export function SearchAskAnswer(props: { query: string }) {
const language = useLanguage(); const language = useLanguage();
const trackEvent = useTrackEvent(); const trackEvent = useTrackEvent();
const [, setSearchState] = useSearch();
const [askState, setAskState] = useSearchAskContext(); const [askState, setAskState] = useSearchAskContext();
React.useEffect(() => { React.useEffect(() => {
@@ -51,10 +50,6 @@ export function SearchAskAnswer(props: { query: string }) {
query, query,
}); });
// When we pass in "ask" mode, the query could still be updated by the client
// we ensure that the query is up-to-date before starting the stream.
setSearchState((prev) => (prev ? { ...prev, query, ask: true } : null));
const { stream } = await streamAskQuestion({ question: query }); const { stream } = await streamAskQuestion({ question: query });
for await (const chunk of readStreamableValue(stream)) { for await (const chunk of readStreamableValue(stream)) {
if (cancelled) { if (cancelled) {
@@ -80,7 +75,7 @@ export function SearchAskAnswer(props: { query: string }) {
cancelled = true; cancelled = true;
} }
}; };
}, [query, setAskState, setSearchState, trackEvent]); }, [query, setAskState, trackEvent]);
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
@@ -186,7 +181,7 @@ function AnswerFollowupQuestions(props: { followupQuestions: string[] }) {
'px-4', 'px-4',
'-mx-4', '-mx-4',
'py-2', 'py-2',
'rounded', 'rounded-sm',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-full', 'circular-corners:rounded-full',
'text-tint', 'text-tint',
@@ -194,8 +189,7 @@ function AnswerFollowupQuestions(props: { followupQuestions: string[] }) {
'focus-within:bg-tint-hover' 'focus-within:bg-tint-hover'
)} )}
{...getSearchLinkProps({ {...getSearchLinkProps({
query: question, ask: question,
ask: true,
})} })}
> >
<Icon <Icon
@@ -16,6 +16,7 @@ import { SearchScopeToggle } from './SearchScopeToggle';
import { useSearch } from './useSearch'; import { useSearch } from './useSearch';
interface SearchContainerProps { interface SearchContainerProps {
spaceId: string;
spaceTitle: string; spaceTitle: string;
isMultiVariants: boolean; isMultiVariants: boolean;
aiMode: CustomizationAIMode; aiMode: CustomizationAIMode;
@@ -26,9 +27,10 @@ interface SearchContainerProps {
* Client component to render the search input and results. * Client component to render the search input and results.
*/ */
export function SearchContainer(props: SearchContainerProps) { export function SearchContainer(props: SearchContainerProps) {
const { spaceTitle, isMultiVariants, aiMode, className } = props; const { spaceId, spaceTitle, isMultiVariants, aiMode, className } = props;
const [state, setSearchState] = useSearch(); const withAIChat = aiMode === CustomizationAIMode.Assistant;
const [state, setSearchState] = useSearch(withAIChat);
const searchAsk = useSearchAskState(); const searchAsk = useSearchAskState();
const router = useRouter(); const router = useRouter();
const trackEvent = useTrackEvent(); const trackEvent = useTrackEvent();
@@ -40,17 +42,32 @@ export function SearchContainer(props: SearchContainerProps) {
// Derive open state from search state // Derive open state from search state
const open = state?.open ?? false; const open = state?.open ?? false;
const onClose = async (to?: string) => { React.useEffect(() => {
if (state?.query === '') { if (state?.ask && aiMode === CustomizationAIMode.Assistant) {
await setSearchState(null); // Close the pop-over when we switch to “ask” mode
} else if (state) { setSearchState((prev) => (prev ? { ...prev, open: !state.ask } : prev));
await setSearchState({ ...state, open: false }); } else if (state?.ask && aiMode === CustomizationAIMode.None) {
// Rewrite ask to query when the site doesn't support AI
setSearchState((prev) => (prev ? { ...prev, query: prev.ask, ask: null } : prev));
}
}, [state?.ask, setSearchState, aiMode]);
const onClose = React.useCallback(
async (to?: string) => {
if (state) {
await setSearchState({
...state,
open: false,
query: state.query === '' ? null : state.query,
});
} }
if (to) { if (to) {
router.push(to); router.push(to);
} }
}; },
[state, setSearchState, router]
);
useHotkeys( useHotkeys(
'mod+k', 'mod+k',
@@ -61,21 +78,21 @@ export function SearchContainer(props: SearchContainerProps) {
[] []
); );
const onOpen = () => { const onOpen = React.useCallback(() => {
if (open) { if (open) {
return; return;
} }
setSearchState((prev) => ({ setSearchState((prev) => ({
ask: prev?.ask ?? false, ask: aiMode === CustomizationAIMode.Assistant && prev ? prev.ask : null,
global: prev?.global ?? false, global: prev?.global ?? false,
query: prev?.query ?? '', query: (!withAIChat ? prev?.ask : null) ?? prev?.query ?? '',
open: true, open: true,
})); }));
trackEvent({ trackEvent({
type: 'search_open', type: 'search_open',
}); });
}; }, [open, setSearchState, trackEvent, withAIChat]);
React.useEffect(() => { React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
@@ -105,7 +122,7 @@ export function SearchContainer(props: SearchContainerProps) {
const onChange = (value: string) => { const onChange = (value: string) => {
setSearchState((prev) => ({ setSearchState((prev) => ({
ask: false, // When typing, we go back to the default search mode ask: aiMode === CustomizationAIMode.Assistant && prev ? prev.ask : null, // When typing, we go back to the default search mode
query: value, query: value,
global: prev?.global ?? false, global: prev?.global ?? false,
open: true, open: true,
@@ -113,7 +130,10 @@ export function SearchContainer(props: SearchContainerProps) {
}; };
// We trim the query to avoid invalidating the search when the user is typing between words. // We trim the query to avoid invalidating the search when the user is typing between words.
const normalizedQuery = state?.query.trim() ?? ''; const normalizedQuery = state?.query?.trim() ?? '';
const normalizedAsk = state?.ask?.trim() ?? '';
const showAsk = aiMode === CustomizationAIMode.Search && normalizedAsk;
return ( return (
<SearchAskProvider value={searchAsk}> <SearchAskProvider value={searchAsk}>
@@ -122,18 +142,19 @@ export function SearchContainer(props: SearchContainerProps) {
// Only show content if there's a query or Ask is enabled // Only show content if there's a query or Ask is enabled
(state?.query || aiMode !== CustomizationAIMode.None) && open ? ( (state?.query || aiMode !== CustomizationAIMode.None) && open ? (
<React.Suspense fallback={null}> <React.Suspense fallback={null}>
{isMultiVariants && !state?.ask ? ( {isMultiVariants && !showAsk ? (
<SearchScopeToggle spaceTitle={spaceTitle} /> <SearchScopeToggle spaceTitle={spaceTitle} />
) : null} ) : null}
{state !== null && !state.ask ? ( {state !== null && !showAsk ? (
<SearchResults <SearchResults
ref={resultsRef} ref={resultsRef}
query={normalizedQuery} query={normalizedQuery}
global={state?.global ?? false} global={state?.global ?? false}
aiMode={aiMode} aiMode={aiMode}
spaceId={spaceId}
/> />
) : null} ) : null}
{state?.ask ? <SearchAskAnswer query={normalizedQuery} /> : null} {showAsk ? <SearchAskAnswer query={normalizedAsk} /> : null}
</React.Suspense> </React.Suspense>
) : null ) : null
} }
@@ -145,7 +166,7 @@ export function SearchContainer(props: SearchContainerProps) {
onOpenAutoFocus: (event) => event.preventDefault(), onOpenAutoFocus: (event) => event.preventDefault(),
align: 'start', align: 'start',
className: className:
'bg-tint-base has-[.empty]:hidden scroll-py-2 w-[32rem] p-2 max-h-[min(32rem,var(--radix-popover-content-available-height))] max-w-[min(var(--radix-popover-content-available-width),32rem)]', 'bg-tint-base has-[.empty]:hidden scroll-py-2 w-128 p-2 max-h-[min(32rem,var(--radix-popover-content-available-height))] max-w-[min(var(--radix-popover-content-available-width),32rem)]',
onInteractOutside: (event) => { onInteractOutside: (event) => {
// Don't close if clicking on the search input itself // Don't close if clicking on the search input itself
if (searchInputRef.current?.contains(event.target as Node)) { if (searchInputRef.current?.contains(event.target as Node)) {
@@ -168,7 +189,7 @@ export function SearchContainer(props: SearchContainerProps) {
> >
<SearchInput <SearchInput
ref={searchInputRef} ref={searchInputRef}
value={state?.query ?? ''} value={(!withAIChat ? state?.ask : null) ?? state?.query ?? ''}
onFocus={onOpen} onFocus={onOpen}
onChange={onChange} onChange={onChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
@@ -59,8 +59,8 @@ export const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(
variantClasses.header, variantClasses.header,
sizeClasses, sizeClasses,
// Additional custom styles // Additional custom styles
'has-[input:focus]:-translate-y-px h-9 grow cursor-pointer px-2.5 has-[input:focus]:bg-tint-base depth-subtle:has-[input:focus]:shadow-lg depth-subtle:has-[input:focus]:shadow-primary-subtle has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-primary-hover md:cursor-text', 'has-[input:focus]:-translate-y-px h-9 grow cursor-pointer px-2.5 has-[input:focus]:bg-tint-base has-[input:focus]:depth-subtle:shadow-lg has-[input:focus]:depth-subtle:shadow-primary-subtle has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-primary-hover md:cursor-text',
'theme-bold:border-header-link/3 theme-bold:has-[input:focus-visible]:border-header-link/5 theme-bold:has-[input:focus-visible]:bg-header-link/3 theme-bold:has-[input:focus-visible]:ring-header-link/5', 'theme-bold:border-header-link/3 has-[input:focus-visible]:theme-bold:border-header-link/5 has-[input:focus-visible]:theme-bold:bg-header-link/3 has-[input:focus-visible]:theme-bold:ring-header-link/5',
'theme-bold:before:absolute theme-bold:before:inset-0 theme-bold:before:bg-header-background/7 theme-bold:before:backdrop-blur-xl ', // Special overlay to make the transparent colors of theme-bold visible. 'theme-bold:before:absolute theme-bold:before:inset-0 theme-bold:before:bg-header-background/7 theme-bold:before:backdrop-blur-xl ', // Special overlay to make the transparent colors of theme-bold visible.
'relative z-30 shrink grow justify-start max-md:absolute max-md:right-0', 'relative z-30 shrink grow justify-start max-md:absolute max-md:right-0',
isOpen ? 'max-md:w-56' : 'max-md:w-[38px]', isOpen ? 'max-md:w-56' : 'max-md:w-[38px]',
@@ -74,14 +74,17 @@ export const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(
size="medium" size="medium"
iconOnly iconOnly
icon="circle-xmark" icon="circle-xmark"
className="-ml-1.5 -mr-1 animate-scaleIn px-1.5" className="-ml-1.5 -mr-1 animate-scale-in px-1.5"
onClick={() => { onClick={() => {
onChange(''); onChange('');
inputRef.current?.focus(); inputRef.current?.focus();
}} }}
/> />
) : ( ) : (
<Icon icon="magnifying-glass" className="size-4 shrink-0 animate-scaleIn" /> <Icon
icon="magnifying-glass"
className="size-4 shrink-0 animate-scale-in"
/>
)} )}
<input <input
@@ -91,11 +94,12 @@ export const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(
onChange={(event) => onChange(event.target.value)} onChange={(event) => onChange(event.target.value)}
value={value} value={value}
// We only show "search or ask" if the search input actually handles both search and ask. // We only show "search or ask" if the search input actually handles both search and ask.
placeholder={`${tString(language, aiMode === CustomizationAIMode.Search ? 'search_or_ask' : 'search')}...`} placeholder={`${tString(language, aiMode === CustomizationAIMode.Search ? 'search_or_ask' : 'search')}`}
maxLength={512} maxLength={512}
size={10} size={10}
data-testid="search-input"
className={tcls( className={tcls(
'peer z-10 min-w-0 grow bg-transparent py-0.5 text-tint-strong theme-bold:text-header-link outline-none transition-[width] duration-300 contain-paint placeholder:text-tint theme-bold:placeholder:text-current theme-bold:placeholder:opacity-7', 'peer z-10 min-w-0 grow bg-transparent py-0.5 text-tint-strong theme-bold:text-header-link outline-hidden transition-[width] duration-300 contain-paint placeholder:text-tint theme-bold:placeholder:text-current theme-bold:placeholder:opacity-7',
isOpen ? '' : 'max-md:opacity-0' isOpen ? '' : 'max-md:opacity-0'
)} )}
ref={inputRef} ref={inputRef}
@@ -127,18 +131,18 @@ function Shortcut() {
<div <div
aria-busy={operatingSystem === null ? 'true' : undefined} aria-busy={operatingSystem === null ? 'true' : undefined}
className={tcls( className={tcls(
`shortcut -mr-1 relative z-10 hidden justify-end gap-0.5 whitespace-nowrap text-xs [font-feature-settings:"calt",_"case"] after:absolute after:right-full after:z-20 after:h-full after:w-8 after:bg-gradient-to-r after:from-transparent after:to-tint-base theme-bold:after:to-transparent after:content-[''] contrast-more:text-tint-strong md:flex`, `shortcut -mr-1 relative z-10 hidden justify-end gap-0.5 whitespace-nowrap text-xs [font-feature-settings:"calt","case"] after:absolute after:right-full after:z-20 after:h-full after:w-8 after:bg-linear-to-r after:from-transparent after:to-tint-base theme-bold:after:to-transparent after:content-[''] contrast-more:text-tint-strong md:flex`,
operatingSystem operatingSystem
? 'motion-safe:animate-fadeIn motion-reduce:opacity-11' ? 'motion-safe:animate-fade-in motion-reduce:opacity-11'
: 'opacity-0' : 'opacity-0'
)} )}
> >
<kbd <kbd
className={`flex h-5 min-w-5 items-center justify-center rounded border border-tint-subtle theme-bold:border-header-link/5 bg-tint-base theme-bold:bg-header-background px-1 ${operatingSystem === 'mac' ? 'text-sm' : ''}`} className={`flex h-5 min-w-5 items-center justify-center rounded-sm border border-tint-subtle theme-bold:border-header-link/5 bg-tint-base theme-bold:bg-header-background px-1 ${operatingSystem === 'mac' ? 'text-sm' : ''}`}
> >
{operatingSystem === 'mac' ? '⌘' : 'Ctrl'} {operatingSystem === 'mac' ? '⌘' : 'Ctrl'}
</kbd> </kbd>
<kbd className="flex size-5 items-center justify-center rounded border border-tint-subtle theme-bold:border-header-link/5 bg-tint-base theme-bold:bg-header-background px-1"> <kbd className="flex size-5 items-center justify-center rounded-sm border border-tint-subtle theme-bold:border-header-link/5 bg-tint-base theme-bold:bg-header-background px-1">
K K
</kbd> </kbd>
</div> </div>
@@ -30,6 +30,7 @@ export const SearchPageResultItem = React.forwardRef(function SearchPageResultIt
ref={ref} ref={ref}
href={item.href} href={item.href}
active={active} active={active}
data-testid="search-page-result"
action={tString(language, 'view')} action={tString(language, 'view')}
leadingIcon={<Icon icon="memo" className="size-4" />} leadingIcon={<Icon icon="memo" className="size-4" />}
insights={{ insights={{
@@ -2,10 +2,9 @@ import React from 'react';
import { t, tString, useLanguage } from '@/intl/client'; import { t, tString, useLanguage } from '@/intl/client';
import { Icon } from '@gitbook/icons'; import { Icon } from '@gitbook/icons';
import { useAIChatController } from '../AI';
import { AIChatIcon } from '../AIChat/'; import { AIChatIcon } from '../AIChat/';
import { SearchResultItem } from './SearchResultItem'; import { SearchResultItem } from './SearchResultItem';
import { useSearch, useSearchLink } from './useSearch'; import { useSearchLink } from './useSearch';
export const SearchQuestionResultItem = React.forwardRef(function SearchQuestionResultItem( export const SearchQuestionResultItem = React.forwardRef(function SearchQuestionResultItem(
props: { props: {
@@ -19,31 +18,19 @@ export const SearchQuestionResultItem = React.forwardRef(function SearchQuestion
const { question, recommended = false, active, withAIChat } = props; const { question, recommended = false, active, withAIChat } = props;
const language = useLanguage(); const language = useLanguage();
const getLinkProp = useSearchLink(); const getLinkProp = useSearchLink();
const chatController = useAIChatController();
const [, setSearchState] = useSearch();
return ( return (
<SearchResultItem <SearchResultItem
size={recommended ? 'small' : 'medium'} size={recommended ? 'small' : 'medium'}
action={tString(language, 'ask', '')} action={tString(language, 'ask', '')}
ref={ref} ref={ref}
data-testid="search-result-item" data-testid={recommended ? 'search-recommended-question' : 'search-ask-question'}
scroll={false} scroll={false}
{...(withAIChat {...getLinkProp({
? { href: '#' } ask: question,
: getLinkProp({ open: false,
ask: true, query: null,
open: true, })}
query: question,
}))}
onClick={() => {
if (withAIChat) {
// If AI Chat is enabled, hijack to open the chat and post the question
chatController.open();
chatController.postMessage({ message: question });
setSearchState(null); // Close the search modal
}
}}
active={active} active={active}
leadingIcon={ leadingIcon={
recommended ? ( recommended ? (
@@ -56,13 +56,7 @@ export const SearchResultItem = React.forwardRef(function SearchResultItem(
<div className="size-4 shrink-0 text-tint-subtle">{leadingIcon}</div> <div className="size-4 shrink-0 text-tint-subtle">{leadingIcon}</div>
<div className="grow">{children}</div> <div className="grow">{children}</div>
{active ? ( {active ? (
<Button <Button label={action} iconOnly variant="primary" icon="arrow-turn-down-left" />
label={action}
iconOnly
variant="primary"
className="p-2"
icon="arrow-turn-down-left"
/>
) : ( ) : (
<div className="flex size-8 shrink-0 items-center justify-center text-tint-subtle"> <div className="flex size-8 shrink-0 items-center justify-center text-tint-subtle">
<Icon icon="chevron-right" className="size-3" /> <Icon icon="chevron-right" className="size-3" />
@@ -8,6 +8,7 @@ import { t, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind'; import { tcls } from '@/lib/tailwind';
import { CustomizationAIMode } from '@gitbook/api'; import { CustomizationAIMode } from '@gitbook/api';
import { assert } from 'ts-essentials';
import { useTrackEvent } from '../Insights'; import { useTrackEvent } from '../Insights';
import { Loading } from '../primitives'; import { Loading } from '../primitives';
import { SearchPageResultItem } from './SearchPageResultItem'; import { SearchPageResultItem } from './SearchPageResultItem';
@@ -33,9 +34,11 @@ type ResultType =
/** /**
* We cache the recommended questions globally to avoid calling the API multiple times * We cache the recommended questions globally to avoid calling the API multiple times
* when re-opening the search modal. * when re-opening the search modal. The cache is per space, so that we can
* have different recommended questions for different spaces of the same site.
* It should not be used outside of an useEffect.
*/ */
let cachedRecommendedQuestions: null | ResultType[] = null; const cachedRecommendedQuestions: Map<string, ResultType[]> = new Map();
/** /**
* Fetch the results of the keyboard navigable elements to display for a query: * Fetch the results of the keyboard navigable elements to display for a query:
@@ -49,10 +52,11 @@ export const SearchResults = React.forwardRef(function SearchResults(
query: string; query: string;
global: boolean; global: boolean;
aiMode: CustomizationAIMode; aiMode: CustomizationAIMode;
spaceId: string;
}, },
ref: React.Ref<SearchResultsRef> ref: React.Ref<SearchResultsRef>
) { ) {
const { children, query, aiMode, global } = props; const { children, query, aiMode, global, spaceId } = props;
const language = useLanguage(); const language = useLanguage();
const trackEvent = useTrackEvent(); const trackEvent = useTrackEvent();
@@ -73,8 +77,10 @@ export const SearchResults = React.forwardRef(function SearchResults(
return; return;
} }
if (cachedRecommendedQuestions) { if (cachedRecommendedQuestions.has(spaceId)) {
setResultsState({ results: cachedRecommendedQuestions, fetching: false }); const results = cachedRecommendedQuestions.get(spaceId);
assert(results, `Cached recommended questions should be set for space ${spaceId}`);
setResultsState({ results, fetching: false });
return; return;
} }
@@ -92,7 +98,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
return; return;
} }
const response = await streamRecommendedQuestions(); const response = await streamRecommendedQuestions(spaceId);
for await (const entry of readStreamableValue(response.stream)) { for await (const entry of readStreamableValue(response.stream)) {
if (!entry) { if (!entry) {
continue; continue;
@@ -109,7 +115,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
id: question, id: question,
question, question,
}); });
cachedRecommendedQuestions = recommendedQuestions; cachedRecommendedQuestions.set(spaceId, recommendedQuestions);
if (!cancelled) { if (!cancelled) {
setResultsState({ results: [...recommendedQuestions], fetching: false }); setResultsState({ results: [...recommendedQuestions], fetching: false });
@@ -23,6 +23,7 @@ export const SearchSectionResultItem = React.forwardRef(function SearchSectionRe
size="small" size="small"
active={active} active={active}
action={tString(language, 'view')} action={tString(language, 'view')}
data-testid="search-page-section-result"
insights={{ insights={{
type: 'search_open_result', type: 'search_open_result',
query, query,
@@ -49,7 +50,7 @@ function highlightQueryInBody(body: string, query: string) {
// Ensure the query to be highlighted is visible in the body. // Ensure the query to be highlighted is visible in the body.
return ( return (
<p className="relative line-clamp-3 text-sm [overflow-wrap:anywhere]"> <p className="wrap-anywhere relative line-clamp-3 text-sm">
<HighlightQuery query={query} text={idx < 20 ? body : `...${body.slice(idx - 10)}`} /> <HighlightQuery query={query} text={idx < 20 ? body : `...${body.slice(idx - 10)}`} />
</p> </p>
); );
@@ -1,2 +1,3 @@
export * from './SearchInput'; export * from './SearchInput';
export * from './SearchContainer'; export * from './SearchContainer';
export * from './useSearch';
@@ -185,8 +185,9 @@ export async function streamAskQuestion({
/** /**
* Stream a list of suggested questions for the site. * Stream a list of suggested questions for the site.
* Optionally scoped to a specific space.
*/ */
export async function streamRecommendedQuestions() { export async function streamRecommendedQuestions(spaceId?: string) {
const siteURLData = await getSiteURLDataFromMiddleware(); const siteURLData = await getSiteURLDataFromMiddleware();
const context = await getServerActionBaseContext(); const context = await getServerActionBaseContext();
@@ -196,7 +197,10 @@ export async function streamRecommendedQuestions() {
const apiClient = await context.dataFetcher.api(); const apiClient = await context.dataFetcher.api();
const apiStream = apiClient.orgs.streamRecommendedQuestionsInSite( const apiStream = apiClient.orgs.streamRecommendedQuestionsInSite(
siteURLData.organization, siteURLData.organization,
siteURLData.site siteURLData.site,
{
spaceId,
}
); );
for await (const chunk of apiStream) { for await (const chunk of apiStream) {
@@ -338,7 +342,7 @@ async function transformAnswer(
document={answer.answer.document} document={answer.answer.document}
context={{ context={{
mode: 'default', mode: 'default',
contentContext: undefined, contentContext: context,
wrapBlocksInSuspense: false, wrapBlocksInSuspense: false,
shouldRenderLinkPreviews: false, // We don't want to render link previews in the AI answer. shouldRenderLinkPreviews: false, // We don't want to render link previews in the AI answer.
}} }}
@@ -5,8 +5,8 @@ import type { LinkProps } from '../primitives';
export interface SearchState { export interface SearchState {
// URL-backed state // URL-backed state
query: string; query: string | null;
ask: boolean; ask: string | null;
global: boolean; global: boolean;
// Local UI state // Local UI state
@@ -16,7 +16,7 @@ export interface SearchState {
// KeyMap needs to be statically defined to avoid `setRawState` being redefined on every render. // KeyMap needs to be statically defined to avoid `setRawState` being redefined on every render.
const keyMap = { const keyMap = {
q: parseAsString, q: parseAsString,
ask: parseAsBoolean, ask: parseAsString,
global: parseAsBoolean, global: parseAsBoolean,
}; };
@@ -27,25 +27,37 @@ export type UpdateSearchState = (
/** /**
* Hook to access the current search query and update it. * Hook to access the current search query and update it.
*/ */
export function useSearch(): [SearchState | null, UpdateSearchState] { export function useSearch(withAIChat = false): [SearchState | null, UpdateSearchState] {
const [rawState, setRawState] = useQueryStates(keyMap, { const [rawState, setRawState] = useQueryStates(keyMap, {
history: 'replace', history: 'replace',
}); });
// Handle legacy ask=true format by converting it to the new format
React.useEffect(() => {
if (rawState?.ask === 'true' && rawState?.q) {
// Convert legacy format: q=query&ask=true -> ask=query&q=null
setRawState({
q: null,
ask: rawState.q,
global: rawState.global,
});
}
}, [rawState, setRawState]);
// Separate local state for open (not synchronized with URL) // Separate local state for open (not synchronized with URL)
// Default to true if there's already a query in the URL // Default to true if there's already a query in the URL
const [open, setIsOpen] = React.useState(() => { const [open, setIsOpen] = React.useState(() => {
return rawState?.q !== null; return rawState?.q !== null || (!withAIChat && rawState?.ask !== null);
}); });
const state = React.useMemo<SearchState | null>(() => { const state = React.useMemo<SearchState | null>(() => {
if (rawState === null || rawState.q === null) { if (rawState === null || (rawState.q === null && rawState.ask === null)) {
return null; return null;
} }
return { return {
query: rawState.q, query: rawState.q,
ask: !!rawState.ask, ask: rawState.ask,
global: !!rawState.global, global: !!rawState.global,
open: open, open: open,
}; };
@@ -78,7 +90,7 @@ export function useSearch(): [SearchState | null, UpdateSearchState] {
return setRawState({ return setRawState({
q: update.query, q: update.query,
ask: update.ask ? true : null, ask: update.ask,
global: update.global ? true : null, global: update.global ? true : null,
}); });
}, },
@@ -97,8 +109,8 @@ export function useSearchLink(): (query: Partial<SearchState>) => LinkProps {
return React.useCallback( return React.useCallback(
(query) => { (query) => {
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.set('q', query.query ?? ''); query.query ? searchParams.set('q', query.query) : searchParams.delete('q');
query.ask ? searchParams.set('ask', 'true') : searchParams.delete('ask'); query.ask ? searchParams.set('ask', query.ask) : searchParams.delete('ask');
query.global ? searchParams.set('global', 'true') : searchParams.delete('global'); query.global ? searchParams.set('global', 'true') : searchParams.delete('global');
return { return {
href: `?${searchParams.toString()}`, href: `?${searchParams.toString()}`,
@@ -106,10 +118,10 @@ export function useSearchLink(): (query: Partial<SearchState>) => LinkProps {
onClick: (event) => { onClick: (event) => {
event.preventDefault(); event.preventDefault();
setSearch((prev) => ({ setSearch((prev) => ({
query: '', query: null,
ask: false, ask: null,
global: false, global: false,
open: true, open: query.open ?? prev?.open ?? false,
...(prev ?? {}), ...(prev ?? {}),
...query, ...query,
})); }));
@@ -30,7 +30,9 @@ export async function SiteLayout(props: {
}) { }) {
const { context, nonce, forcedTheme, withTracking, visitorAuthClaims, children } = props; const { context, nonce, forcedTheme, withTracking, visitorAuthClaims, children } = props;
const { scripts, customization } = context; const { customization } = context;
// Scripts are disabled when tracking is disabled
const scripts = withTracking ? context.scripts : [];
ReactDOM.preconnect(GITBOOK_API_PUBLIC_URL); ReactDOM.preconnect(GITBOOK_API_PUBLIC_URL);
ReactDOM.preconnect(GITBOOK_ICONS_URL); ReactDOM.preconnect(GITBOOK_ICONS_URL);
@@ -8,10 +8,10 @@ import { useScrollPage } from '@/components/hooks';
/** /**
* Client component to initialize interactivity for a page. * Client component to initialize interactivity for a page.
*/ */
export function PageClientLayout(props: { withSections?: boolean }) { export function PageClientLayout() {
// We use this hook in the page layout to ensure the elements for the blocks // We use this hook in the page layout to ensure the elements for the blocks
// are rendered before we scroll to a hash or to the top of the page // are rendered before we scroll to a hash or to the top of the page
useScrollPage({ scrollMarginTop: props.withSections ? 108 : 64 }); useScrollPage();
useStripFallbackQueryParam(); useStripFallbackQueryParam();
return null; return null;
@@ -11,6 +11,7 @@ import { getPagePath } from '@/lib/pages';
import { isPageIndexable, isSiteIndexable } from '@/lib/seo'; import { isPageIndexable, isSiteIndexable } from '@/lib/seo';
import { getResizedImageURL } from '@/lib/images'; import { getResizedImageURL } from '@/lib/images';
import { tcls } from '@/lib/tailwind';
import { PageContextProvider } from '../PageContext'; import { PageContextProvider } from '../PageContext';
import { PageClientLayout } from './PageClientLayout'; import { PageClientLayout } from './PageClientLayout';
import { type PagePathParams, fetchPageData, getPathnameParam } from './fetch'; import { type PagePathParams, fetchPageData, getPathnameParam } from './fetch';
@@ -70,7 +71,14 @@ export async function SitePage(props: SitePageProps) {
<PageCover as="full" page={page} cover={page.cover} context={context} /> <PageCover as="full" page={page} cover={page.cover} context={context} />
) : null} ) : null}
{/* We use a flex row reverse to render the aside first because the page is streamed. */} {/* 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"> <div
className={tcls(
'flex grow flex-row-reverse justify-end',
withSections
? '[--content-scroll-margin:calc(var(--spacing)*27)]'
: '[--content-scroll-margin:calc(var(--spacing)*16)]'
)}
>
<PageAside <PageAside
page={page} page={page}
document={document} document={document}
@@ -88,7 +96,7 @@ export async function SitePage(props: SitePageProps) {
/> />
</div> </div>
<React.Suspense fallback={null}> <React.Suspense fallback={null}>
<PageClientLayout withSections={withSections} /> <PageClientLayout />
</React.Suspense> </React.Suspense>
</PageContextProvider> </PageContextProvider>
); );
@@ -32,7 +32,7 @@ export function SiteSectionList(props: { sections: ClientSiteSections; className
<nav <nav
aria-label="Sections" aria-label="Sections"
className={tcls( className={tcls(
'-mx-5 before:contents[] relative border-tint-subtle border-b from-transparent sidebar-filled:to-tint-subtle theme-muted:to-tint-subtle to-tint-base text-sm text-tint before:pointer-events-none before:absolute before:right-2 before:bottom-0 before:left-0 before:h-12 before:bg-gradient-to-b [html.sidebar-filled.theme-bold.tint_&]:to-tint-base [html.sidebar-filled.theme-bold.tint_&]:to-tint-subtle [html.sidebar-filled.theme-muted_&]:to-tint-base', '-mx-5 before:contents[] relative border-tint-subtle border-b from-transparent sidebar-filled:to-tint-subtle theme-muted:to-tint-subtle to-tint-base text-sm text-tint before:pointer-events-none before:absolute before:right-2 before:bottom-0 before:left-0 before:h-12 before:bg-linear-to-b [html.sidebar-filled.theme-bold.tint_&]:to-tint-base [html.sidebar-filled.theme-bold.tint_&]:to-tint-subtle [html.sidebar-filled.theme-muted_&]:to-tint-base',
className className
)} )}
> >
@@ -108,7 +108,7 @@ export function SiteSectionListItem(props: {
> >
<div <div
className={tcls( className={tcls(
'flex size-8 shrink-0 items-center justify-center rounded-md straight-corners:rounded-none bg-tint-subtle text-lg text-tint leading-none shadow-sm shadow-tint ring-1 ring-tint-subtle transition-transform group-hover/section-link:scale-110 group-hover/section-link:ring-tint-hover group-active/section-link:scale-90 group-active/section-link:shadow-none contrast-more:text-tint-strong dark:shadow-none', 'flex size-8 shrink-0 items-center justify-center rounded-md straight-corners:rounded-none bg-tint-subtle text-lg text-tint leading-none shadow-tint shadow-xs ring-1 ring-tint-subtle transition-transform group-hover/section-link:scale-110 group-hover/section-link:ring-tint-hover group-active/section-link:scale-90 group-active/section-link:shadow-none contrast-more:text-tint-strong dark:shadow-none',
isActive isActive
? 'bg-primary tint:bg-primary-solid text-primary-subtle tint:text-contrast-primary-solid shadow-md shadow-primary ring-primary group-hover/section-link:ring-primary-hover, contrast-more:text-primary contrast-more:ring-2 contrast-more:ring-primary' ? 'bg-primary tint:bg-primary-solid text-primary-subtle tint:text-contrast-primary-solid shadow-md shadow-primary ring-primary group-hover/section-link:ring-primary-hover, contrast-more:text-primary contrast-more:ring-2 contrast-more:ring-primary'
: null : null
@@ -162,7 +162,7 @@ export function SiteSectionGroupItem(props: {
> >
<div <div
className={tcls( className={tcls(
'flex size-8 shrink-0 items-center justify-center rounded-md straight-corners:rounded-none bg-tint-subtle text-lg text-tint leading-none shadow-sm shadow-tint ring-1 ring-tint-subtle transition-transform group-hover/section-link:scale-110 group-hover/section-link:ring-tint-hover group-active/section-link:scale-90 group-active/section-link:shadow-none contrast-more:text-tint-strong dark:shadow-none', 'flex size-8 shrink-0 items-center justify-center rounded-md straight-corners:rounded-none bg-tint-subtle text-lg text-tint leading-none shadow-tint shadow-xs ring-1 ring-tint-subtle transition-transform group-hover/section-link:scale-110 group-hover/section-link:ring-tint-hover group-active/section-link:scale-90 group-active/section-link:shadow-none contrast-more:text-tint-strong dark:shadow-none',
isActiveGroup isActiveGroup
? 'bg-primary tint:bg-primary-solid text-primary tint:text-contrast-primary-solid shadow-md shadow-primary ring-primary group-hover/section-link:ring-primary-hover, contrast-more:text-primary-strong contrast-more:ring-2 contrast-more:ring-primary' ? 'bg-primary tint:bg-primary-solid text-primary tint:text-contrast-primary-solid shadow-md shadow-primary ring-primary group-hover/section-link:ring-primary-hover, contrast-more:text-primary-strong contrast-more:ring-2 contrast-more:ring-primary'
: null : null
@@ -183,7 +183,7 @@ export function SiteSectionGroupItem(props: {
'group', 'group',
'relative', 'relative',
'rounded-full', 'rounded-full',
'straight-corners:rounded-sm', 'straight-corners:rounded-xs',
'w-5', 'w-5',
'h-5', 'h-5',
'after:grid-area-1-1', 'after:grid-area-1-1',
@@ -202,10 +202,10 @@ export function SiteSectionGroupItem(props: {
icon="chevron-right" icon="chevron-right"
className={tcls( className={tcls(
'grid', 'grid',
'flex-shrink-0', 'shrink-0',
'size-3', 'size-3',
'm-1', 'm-1',
'transition-[opacity]', 'transition-opacity',
'text-current', 'text-current',
'transition-transform', 'transition-transform',
'opacity-6', 'opacity-6',
@@ -96,7 +96,7 @@ export function SiteSectionTabs(props: { sections: ClientSiteSections }) {
icon={icon as IconName} icon={icon as IconName}
/> />
</NavigationMenu.Trigger> </NavigationMenu.Trigger>
<NavigationMenu.Content className="absolute top-0 left-0 z-20 w-full data-[motion=from-end]:motion-safe:animate-enterFromRight data-[motion=from-start]:motion-safe:animate-enterFromLeft data-[motion=to-end]:motion-safe:animate-exitToRight data-[motion=to-start]:motion-safe:animate-exitToLeft md:w-max"> <NavigationMenu.Content className="absolute top-0 left-0 z-20 w-full motion-safe:data-[motion=from-end]:animate-enter-from-right motion-safe:data-[motion=from-start]:animate-enter-from-left motion-safe:data-[motion=to-end]:animate-exit-to-right motion-safe:data-[motion=to-start]:animate-exit-to-left md:w-max">
<SectionGroupTileList <SectionGroupTileList
sections={sectionOrGroup.sections} sections={sectionOrGroup.sections}
currentSection={currentSection} currentSection={currentSection}
@@ -118,7 +118,7 @@ export function SiteSectionTabs(props: { sections: ClientSiteSections }) {
); );
})} })}
<NavigationMenu.Indicator <NavigationMenu.Indicator
className="fixed top-full z-50 flex h-3 items-end justify-center duration-150 motion-safe:transition-[width,_transform] data-[state=hidden]:motion-safe:animate-fadeOut data-[state=visible]:motion-safe:animate-fadeIn" className="fixed top-full z-50 flex h-3 items-end justify-center duration-150 motion-safe:transition-[width,transform] motion-safe:data-[state=hidden]:animate-fade-out motion-safe:data-[state=visible]:animate-fade-in"
aria-hidden aria-hidden
> >
<div className="relative top-1/2 size-3 rotate-45 rounded-tl-sm border-tint-subtle border-t border-l bg-tint-base" /> <div className="relative top-1/2 size-3 rotate-45 rounded-tl-sm border-tint-subtle border-t border-l bg-tint-base" />
@@ -133,7 +133,7 @@ export function SiteSectionTabs(props: { sections: ClientSiteSections }) {
}} }}
> >
<NavigationMenu.Viewport <NavigationMenu.Viewport
className="relative mt-3 h-[var(--radix-navigation-menu-viewport-height)] w-[calc(100vw_-_2rem)] origin-[top_center] overflow-hidden rounded-lg straight-corners:rounded-sm bg-tint-base depth-flat:shadow-none shadow-lg shadow-tint-10/6 ring-1 ring-tint-subtle duration-250 data-[state=closed]:duration-150 motion-safe:transition-[width,_height,_transform] data-[state=closed]:motion-safe:animate-scaleOut data-[state=open]:motion-safe:animate-scaleIn md:mx-0 md:w-[var(--radix-navigation-menu-viewport-width)] dark:shadow-tint-1/6" className="relative mt-3 h-(--radix-navigation-menu-viewport-height) w-[calc(100vw-2rem)] origin-[top_center] overflow-hidden rounded-lg straight-corners:rounded-xs bg-tint-base depth-flat:shadow-none shadow-lg shadow-tint-10/6 ring-1 ring-tint-subtle duration-250 data-[state=closed]:duration-150 motion-safe:transition-[width,height,transform] motion-safe:data-[state=closed]:animate-scale-out motion-safe:data-[state=open]:animate-scale-in md:mx-0 md:w-(--radix-navigation-menu-viewport-width) dark:shadow-tint-1/6"
style={{ style={{
translate: translate:
undefined /* don't move this to a Tailwind class as Radix renders viewport incorrectly for a few frames */, undefined /* don't move this to a Tailwind class as Radix renders viewport incorrectly for a few frames */,
@@ -157,7 +157,7 @@ const SectionTab = React.forwardRef(function SectionTab(
ref={ref} ref={ref}
{...rest} {...rest}
className={tcls( className={tcls(
'group relative my-2 flex select-none items-center justify-between rounded circular-corners:rounded-full straight-corners:rounded-none px-3 py-1', 'group relative my-2 flex select-none items-center justify-between circular-corners:rounded-full rounded-corners:rounded-xs px-3 py-1',
isActive isActive
? 'text-primary-subtle' ? 'text-primary-subtle'
: 'text-tint hover:bg-tint-hover hover:text-tint-strong' : 'text-tint hover:bg-tint-hover hover:text-tint-strong'
@@ -186,7 +186,7 @@ const SectionGroupTab = React.forwardRef(function SectionGroupTab(
ref={ref} ref={ref}
{...rest} {...rest}
className={tcls( className={tcls(
'group relative my-2 flex select-none items-center justify-between rounded circular-corners:rounded-full straight-corners:rounded-none px-3 py-1 transition-colors hover:cursor-default', 'group relative my-2 flex select-none items-center justify-between circular-corners:rounded-full rounded-sm straight-corners:rounded-none px-3 py-1 transition-colors hover:cursor-default',
isActive isActive
? 'text-primary-subtle' ? 'text-primary-subtle'
: 'text-tint hover:bg-tint-hover hover:text-tint-strong' : 'text-tint hover:bg-tint-hover hover:text-tint-strong'
@@ -252,7 +252,7 @@ function SectionGroupTile(props: { section: ClientSiteSection; isActive: boolean
<Link <Link
href={url} href={url}
className={tcls( className={tcls(
'flex w-full select-none flex-col gap-1 rounded straight-corners:rounded-none px-3 py-2 transition-colors hover:bg-tint-hover', 'flex w-full select-none flex-col gap-1 rounded-sm straight-corners:rounded-none px-3 py-2 transition-colors hover:bg-tint-hover',
isActive ? 'text-primary' : 'text-tint-strong' isActive ? 'text-primary' : 'text-tint-strong'
)} )}
> >
@@ -58,6 +58,7 @@ export function SpaceLayout(props: {
aiMode={aiMode} aiMode={aiMode}
isMultiVariants={siteSpaces.length > 1} isMultiVariants={siteSpaces.length > 1}
spaceTitle={siteSpace.title} spaceTitle={siteSpace.title}
spaceId={siteSpace.space.id}
/> />
</React.Suspense> </React.Suspense>
{aiMode === CustomizationAIMode.Assistant ? ( {aiMode === CustomizationAIMode.Assistant ? (
@@ -144,7 +145,7 @@ export function SpaceLayout(props: {
className={tcls( className={tcls(
'w-full', 'w-full',
'page-no-toc:hidden', 'page-no-toc:hidden',
'site-header-none:page-no-toc:flex' 'page-no-toc:site-header-none:flex'
)} )}
/> />
)} )}
@@ -14,11 +14,11 @@ export function PageGroupItem(props: { page: ClientTOCPageGroup }) {
<li className="group/page-group-item flex flex-col"> <li className="group/page-group-item flex flex-col">
<div <div
className={tcls( className={tcls(
'-top-5 group-first/page-group-item:-mt-5 sticky z-[1] flex items-center gap-3 px-3 pt-6', '-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', 'font-semibold text-xs uppercase tracking-wide',
'pb-3', // Add extra padding to make the header fade a bit nicer '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. '-mb-1.5', // Then pull the page items a bit closer, effective bottom padding is 1.5 units / 6px.
'[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. 'mask-[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', 'bg-tint-base',
'sidebar-filled:bg-tint-subtle', 'sidebar-filled:bg-tint-subtle',
'theme-muted:bg-tint-subtle', 'theme-muted:bg-tint-subtle',
@@ -13,14 +13,14 @@ export function TOCPageIcon({ page }: { page: Pick<RevisionPage, 'emoji' | 'icon
page={page} page={page}
style={tcls( style={tcls(
'text-base', 'text-base',
'[.toclink_&]:text-tint-strong/6', 'in-[.toclink]:text-tint-strong/6',
'group-aria-current-page/toclink:text-primary-subtle', 'group-aria-current-page/toclink:text-primary-subtle',
'contrast-more:group-aria-current-page/toclink:text-primary', 'contrast-more:group-aria-current-page/toclink:text-primary',
!page.emoji !page.emoji
? [ ? [
'theme-gradient:bg-gradient-to-b', 'theme-gradient:bg-linear-to-b',
'theme-bold:bg-gradient-to-b', 'theme-bold:bg-linear-to-b',
'theme-gradient:bg-fixed', 'theme-gradient:bg-fixed',
'theme-bold:bg-fixed', 'theme-bold:bg-fixed',
@@ -29,7 +29,7 @@ export function TOCPageIcon({ page }: { page: Pick<RevisionPage, 'emoji' | 'icon
'tint:from-tint-7', 'tint:from-tint-7',
'tint:to-tint-10', 'tint:to-tint-10',
'group-aria-current-page/toclink:bg-none', 'group-aria-current-page/toclink:bg-none!',
] ]
: '', : '',
@@ -33,41 +33,42 @@ export async function TableOfContents(props: {
'shrink-0', 'shrink-0',
'basis-full', 'basis-full',
'lg:basis-72', 'lg:basis-72',
'page-no-toc:lg:basis-56', 'lg:page-no-toc:basis-56',
'relative', 'relative',
'z-[1]', 'z-1',
'lg:sticky', 'lg:sticky',
'lg:mr-12', 'lg:mr-12',
// Server-side static positioning // Server-side static positioning
'lg:top-0', 'lg:top-0',
'lg:h-screen', 'lg:h-screen',
'announcement:lg:h-[calc(100vh-4.25rem)]', 'lg:announcement:h-[calc(100vh-4.25rem)]',
'site-header:lg:top-16', // With header
'site-header:lg:h-[calc(100vh-4rem)]', 'lg:site-header:top-16',
'announcement:site-header:lg:h-[calc(100vh-4rem-4.25rem)]', 'lg:site-header:h-[calc(100vh-4rem)]',
'lg:announcement:site-header:h-[calc(100vh-4rem-4.25rem)]',
'site-header-sections:lg:top-[6.75rem]', 'lg:site-header-sections:top-27',
'site-header-sections:lg:h-[calc(100vh-6.75rem)]', 'lg:site-header-sections:h-[calc(100vh-6.75rem)]',
'announcement:site-header-sections:lg:h-[calc(100vh-6.75rem-4.25rem)]', 'lg:site-header-sections:announcement:h-[calc(100vh-6.75rem-4.25rem)]',
// Client-side dynamic positioning (CSS vars applied by script) // Client-side dynamic positioning (CSS vars applied by script)
'[html[style*="--toc-top-offset"]_&]:lg:!top-[var(--toc-top-offset)]', 'lg:[html[style*="--toc-top-offset"]_&]:top-(--toc-top-offset)!',
'[html[style*="--toc-height"]_&]:lg:!h-[var(--toc-height)]', 'lg:[html[style*="--toc-height"]_&]:h-(--toc-height)!',
'pt-6', 'pt-6',
'pb-4', 'pb-4',
'sidebar-filled:lg:pr-6', 'lg:sidebar-filled:pr-6',
'page-no-toc:lg:pr-0', 'lg:page-no-toc:pr-0',
'hidden', 'hidden',
'navigation-open:!flex', 'navigation-open:flex!',
'lg:flex', 'lg:flex',
'page-no-toc:lg:hidden', 'lg:page-no-toc:hidden',
'page-no-toc:xl:flex', 'xl:page-no-toc:flex',
'site-header-none:page-no-toc:lg:flex', 'lg:site-header-none:page-no-toc:flex',
'flex-col', 'flex-col',
'gap-4', 'gap-4',
@@ -79,7 +80,7 @@ export async function TableOfContents(props: {
<div // The actual sidebar, either shown with a filled bg or transparent. <div // The actual sidebar, either shown with a filled bg or transparent.
className={tcls( className={tcls(
'lg:-ms-5', 'lg:-ms-5',
'relative flex flex-grow flex-col overflow-hidden border-tint-subtle', 'relative flex grow flex-col overflow-hidden border-tint-subtle',
'sidebar-filled:bg-tint-subtle', 'sidebar-filled:bg-tint-subtle',
'theme-muted:bg-tint-subtle', 'theme-muted:bg-tint-subtle',
@@ -87,25 +88,25 @@ export async function TableOfContents(props: {
'[html.sidebar-filled.theme-muted_&]:bg-tint-base', '[html.sidebar-filled.theme-muted_&]:bg-tint-base',
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base', '[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
'[html.sidebar-filled.theme-gradient_&]:border', '[html.sidebar-filled.theme-gradient_&]:border',
'page-no-toc:!bg-transparent', 'page-no-toc:bg-transparent!',
'page-no-toc:!border-none', 'page-no-toc:border-none!',
'sidebar-filled:rounded-xl', 'sidebar-filled:rounded-xl',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'[html.sidebar-filled.circular-corners_&]:page-has-toc:rounded-3xl' 'page-has-toc:[html.sidebar-filled.circular-corners_&]:rounded-3xl'
)} )}
> >
{innerHeader && <div className="px-5 *:my-4">{innerHeader}</div>} {innerHeader && <div className="px-5 *:my-4">{innerHeader}</div>}
<TOCScrollContainer // The scrollview inside the sidebar <TOCScrollContainer // The scrollview inside the sidebar
className={tcls( className={tcls(
'flex flex-grow flex-col p-2', 'flex grow flex-col p-2',
customization.trademark.enabled && 'lg:pb-20', customization.trademark.enabled && 'lg:pb-20',
'lg:gutter-stable overflow-y-auto', 'lg:gutter-stable overflow-y-auto',
'[&::-webkit-scrollbar]:bg-transparent', '[&::-webkit-scrollbar]:bg-transparent',
'[&::-webkit-scrollbar-thumb]:bg-transparent', '[&::-webkit-scrollbar-thumb]:bg-transparent',
'group-hover:[&::-webkit-scrollbar]:bg-tint-subtle', '[&::-webkit-scrollbar]:group-hover:bg-tint-subtle',
'group-hover:[&::-webkit-scrollbar-thumb]:bg-tint-7', '[&::-webkit-scrollbar-thumb]:group-hover:bg-tint-7',
'group-hover:[&::-webkit-scrollbar-thumb:hover]:bg-tint-8' '[&::-webkit-scrollbar-thumb:hover]:group-hover:bg-tint-8'
)} )}
> >
<PagesList <PagesList
@@ -145,7 +145,7 @@ function Toggler(props: {
'group', 'group',
'relative', 'relative',
'rounded-full', 'rounded-full',
'straight-corners:rounded-sm', 'straight-corners:rounded-xs',
'w-5', 'w-5',
'h-5', 'h-5',
'after:grid-area-1-1', 'after:grid-area-1-1',
@@ -168,7 +168,7 @@ function Toggler(props: {
<Icon <Icon
icon="chevron-right" icon="chevron-right"
className={tcls( className={tcls(
'm-1 grid size-3 flex-shrink-0 text-current opacity-6 transition', 'm-1 grid size-3 shrink-0 text-current opacity-6 transition',
'group-hover:opacity-11 contrast-more:opacity-11', 'group-hover:opacity-11 contrast-more:opacity-11',
isOpen ? 'rotate-90' : 'rotate-0' isOpen ? 'rotate-90' : 'rotate-0'
)} )}
@@ -22,7 +22,7 @@ export function Trademark(props: {
<div <div
className={tcls( className={tcls(
'relative', 'relative',
'z-[2]', 'z-2',
'lg:absolute', 'lg:absolute',
'left-0', 'left-0',
@@ -51,7 +51,7 @@ export function Trademark(props: {
'before:inset-x-0', 'before:inset-x-0',
'before:bottom-full', 'before:bottom-full',
'before:h-8', 'before:h-8',
'before:bg-gradient-to-b', 'before:bg-linear-to-b',
'before:from-transparent', 'before:from-transparent',
'before:to-tint-base', 'before:to-tint-base',
'sidebar-filled:before:to-tint-subtle', 'sidebar-filled:before:to-tint-subtle',
@@ -59,7 +59,7 @@ export function Trademark(props: {
'[html.sidebar-filled.theme-bold.tint_&]:before:to-tint-subtle', '[html.sidebar-filled.theme-bold.tint_&]:before:to-tint-subtle',
'[html.sidebar-filled.theme-muted_&]:before:to-tint-base', '[html.sidebar-filled.theme-muted_&]:before:to-tint-base',
'[html.sidebar-filled.theme-bold.tint_&]:before:to-tint-base', '[html.sidebar-filled.theme-bold.tint_&]:before:to-tint-base',
'page-no-toc:before:!to-transparent' 'page-no-toc:before:to-transparent!'
)} )}
> >
<TrademarkLink {...props} /> <TrademarkLink {...props} />
@@ -89,9 +89,9 @@ export function TrademarkLink(props: {
href={url.toString()} href={url.toString()}
className={tcls( className={tcls(
'text-sm', 'text-sm',
// 'page-no-toc:lg:max-xl:text-xs', // 'lg:max-xl:page-no-toc:text-xs',
// 'page-no-toc:lg:max-xl:px-3', // 'lg:max-xl:page-no-toc:px-3',
// 'page-no-toc:lg:max-xl:py-3', // 'lg:max-xl:page-no-toc:py-3',
'font-semibold', 'font-semibold',
'text-tint', 'text-tint',
@@ -102,7 +102,7 @@ export function TrademarkLink(props: {
'py-4', 'py-4',
'sidebar-filled:px-3', 'sidebar-filled:px-3',
'sidebar-filled:page-no-toc:lg:px-5', 'lg:sidebar-filled:page-no-toc:px-5',
'rounded-lg', 'rounded-lg',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
@@ -128,14 +128,14 @@ export function TrademarkLink(props: {
icon="gitbook" icon="gitbook"
className={tcls( className={tcls(
'size-5', 'size-5',
// 'page-no-toc:lg:max-xl:size-4', // 'lg:max-xl:page-no-toc:size-4',
'shrink-0' 'shrink-0'
)} )}
/> />
<span <span
className={tcls( className={tcls(
'ml-3' 'ml-3'
// 'page-no-toc:lg:max-xl:ml-2' // 'lg:max-xl:page-no-toc:ml-2'
)} )}
> >
{t(language, 'powered_by_gitbook')} {t(language, 'powered_by_gitbook')}
@@ -23,9 +23,9 @@ export const ToggleableLinkItemStyles = [
'flex flex-row justify-between', 'flex flex-row justify-between',
'circular-corners:rounded-2xl rounded-md straight-corners:rounded-none p-1.5 pl-3', 'circular-corners:rounded-2xl 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', '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', 'contrast-more:hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-tint-12',
'before:contents[] before:-left-px before:absolute before:inset-y-0', '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', 'sidebar-list-line:rounded-l-none sidebar-list-line:before:w-px [&+div_a]:sidebar-list-default:rounded-l-none [&+div_a]:pl-5 [&+div_a]:sidebar-list-default:before:w-px',
]; ];
export const ToggleableLinkItemActiveStyles = [ export const ToggleableLinkItemActiveStyles = [
@@ -45,7 +45,7 @@ export const ToggleableLinkItemActiveStyles = [
'hover:bg-primary-hover', 'hover:bg-primary-hover',
'hover:text-primary', 'hover:text-primary',
'hover:before:bg-primary-solid-hover', 'hover:before:bg-primary-solid-hover',
'sidebar-list-pill:hover:bg-primary-hover', 'hover:sidebar-list-pill:bg-primary-hover',
'contrast-more:text-primary', 'contrast-more:text-primary',
'contrast-more:hover:text-primary-strong', 'contrast-more:hover:text-primary-strong',
@@ -67,7 +67,7 @@ function ThemeButton(props: {
aria-checked={active} aria-checked={active}
className={tcls( className={tcls(
'p-2', 'p-2',
'rounded', 'rounded-sm',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'circular-corners:rounded-full', 'circular-corners:rounded-full',
'transition-all', 'transition-all',
@@ -10,16 +10,13 @@ import { useHash } from './useHash';
* to the top of the page when navigating between pages (pathname) * to the top of the page when navigating between pages (pathname)
* or sections of a page (hash). * or sections of a page (hash).
*/ */
export function useScrollPage(props: { scrollMarginTop?: number }) { export function useScrollPage() {
const hash = useHash(); const hash = useHash();
const pathname = usePathname(); const pathname = usePathname();
React.useLayoutEffect(() => { React.useLayoutEffect(() => {
if (hash) { if (hash) {
const element = document.getElementById(hash); const element = document.getElementById(hash);
if (element) { if (element) {
if (props.scrollMarginTop) {
element.style.scrollMarginTop = `${props.scrollMarginTop}px`;
}
element.scrollIntoView({ element.scrollIntoView({
block: 'start', block: 'start',
behavior: 'smooth', behavior: 'smooth',
@@ -36,5 +33,5 @@ export function useScrollPage(props: { scrollMarginTop?: number }) {
} }
} }
}; };
}, [hash, pathname, props.scrollMarginTop]); }, [hash, pathname]);
} }
@@ -30,7 +30,7 @@ export const variantClasses = {
'hover:bg-primary-solid-hover', 'hover:bg-primary-solid-hover',
'hover:text-contrast-primary-solid-hover', 'hover:text-contrast-primary-solid-hover',
'border-0', 'border-0',
'contrast-more:border-1', 'contrast-more:border',
'disabled:bg-tint', 'disabled:bg-tint',
'disabled:text-tint/8', 'disabled:text-tint/8',
], ],
@@ -38,12 +38,11 @@ export const variantClasses = {
'bg-transparent', 'bg-transparent',
'text-tint', 'text-tint',
'border-0', 'border-0',
'!shadow-none', 'shadow-none!',
'hover:bg-primary-hover', 'hover:bg-primary-hover',
'hover:text-primary-strong', 'hover:text-primary-strong',
'hover:scale-1',
'contrast-more:bg-tint-subtle', 'contrast-more:bg-tint-subtle',
'depth-subtle:hover:translate-y-0', 'hover:depth-subtle:translate-y-0',
'disabled:text-tint/8', 'disabled:text-tint/8',
'disabled:bg-transparent', 'disabled:bg-transparent',
], ],
@@ -52,7 +51,7 @@ export const variantClasses = {
'depth-flat:bg-transparent', 'depth-flat:bg-transparent',
'text-tint', 'text-tint',
'hover:bg-tint-hover', 'hover:bg-tint-hover',
'depth-flat:hover:bg-tint-hover', 'hover:depth-flat:bg-tint-hover',
'hover:text-primary', 'hover:text-primary',
'contrast-more:bg-tint-subtle', 'contrast-more:bg-tint-subtle',
'disabled:bg-transparent', 'disabled:bg-transparent',
@@ -60,22 +59,22 @@ export const variantClasses = {
], ],
header: [ header: [
'bg-tint-base text-tint', 'bg-tint-base text-tint',
'theme-clean:hover:bg-tint-subtle', 'hover:theme-clean:bg-tint-subtle',
'theme-bold:bg-header-link/2', 'theme-bold:bg-header-link/2',
'theme-bold:text-header-link', 'theme-bold:text-header-link',
'theme-bold:!shadow-none', 'theme-bold:shadow-none!',
'theme-bold:border-header-link/4', 'theme-bold:border-header-link/4',
'theme-bold:hover:bg-header-link/3', 'hover:theme-bold:bg-header-link/3',
'theme-bold:hover:text-header-link', 'hover:theme-bold:text-header-link',
'theme-bold:hover:shadow-none', 'hover:theme-bold:shadow-none',
'theme-bold:hover:border-header-link/5', 'hover:theme-bold:border-header-link/5',
'theme-bold:contrast-more:bg-header-background', 'contrast-more:theme-bold:bg-header-background',
'theme-bold:contrast-more:text-header-link', 'contrast-more:theme-bold:text-header-link',
'theme-bold:contrast-more:border-header-link', 'contrast-more:theme-bold:border-header-link',
'theme-bold:contrast-more:hover:border-header-link', 'contrast-more:hover:theme-bold:border-header-link',
], ],
}; };
@@ -111,7 +110,7 @@ export const Button = React.forwardRef<
ref ref
) => { ) => {
const sizes = { const sizes = {
default: ['text-base', 'font-semibold', 'px-5', 'py-2'], default: ['text-base', 'font-semibold', iconOnly ? 'px-2' : 'px-5', 'py-2'],
medium: ['text-sm', iconOnly ? 'px-2' : 'px-3.5', 'py-1.5'], medium: ['text-sm', iconOnly ? 'px-2' : 'px-3.5', 'py-1.5'],
small: ['text-xs', 'py-2', iconOnly ? 'px-2' : 'px-3'], small: ['text-xs', 'py-2', iconOnly ? 'px-2' : 'px-3'],
xsmall: ['text-xs', 'py-1', iconOnly ? 'px-1.5' : 'px-2'], xsmall: ['text-xs', 'py-1', iconOnly ? 'px-1.5' : 'px-2'],
@@ -191,7 +190,7 @@ export const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonProps>(({ chil
<div <div
ref={ref} ref={ref}
className={tcls( className={tcls(
'*:!transform-none *:!shadow-none flex h-fit items-stretch justify-start', 'flex h-fit items-stretch justify-start *:translate-y-0! *:shadow-none!',
'[&>*:not(:first-child)]:border-l-0 [&>*:not(:first-child,:last-child)]:rounded-none [&>*:not(:only-child):first-child]:rounded-r-none [&>*:not(:only-child):last-child]:rounded-l-none' '[&>*:not(:first-child)]:border-l-0 [&>*:not(:first-child,:last-child)]:rounded-none [&>*:not(:only-child):first-child]:rounded-r-none [&>*:not(:only-child):last-child]:rounded-l-none'
)} )}
> >
@@ -23,7 +23,7 @@ export const Checkbox = React.forwardRef<
className={tcls( className={tcls(
'peer', 'peer',
'shrink-0', 'shrink-0',
'rounded-sm', 'rounded-xs',
'straight-corners:rounded-none', 'straight-corners:rounded-none',
'ring-1', 'ring-1',
'ring-tint-12/4', 'ring-tint-12/4',
@@ -82,7 +82,7 @@ export function DropdownMenu(props: {
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
align={align} align={align}
side={side} side={side}
className="z-40 animate-scaleIn border-tint pt-2" className="z-40 animate-scale-in border-tint pt-2"
> >
<div <div
className={tcls( className={tcls(
@@ -153,11 +153,11 @@ export function DropdownMenuItem(
const { children, active = false, href, className, insights, target, ...rest } = props; const { children, active = false, href, className, insights, target, ...rest } = props;
const itemClassName = tcls( const itemClassName = tcls(
'rounded straight-corners:rounded-sm circular-corners:rounded-lg px-3 py-1 text-sm flex gap-2 items-center', 'rounded-xs straight-corners:rounded-xs circular-corners:rounded-lg px-3 py-1 text-sm flex gap-2 items-center',
active active
? 'bg-primary text-primary-strong data-[highlighted]:bg-primary-hover' ? 'bg-primary text-primary-strong data-highlighted:bg-primary-hover'
: 'data-[highlighted]:bg-tint-hover', : 'data-highlighted:bg-tint-hover',
'focus:outline-none', 'focus:outline-hidden',
props.disabled ? 'opacity-7 cursor-not-allowed' : 'cursor-pointer', props.disabled ? 'opacity-7 cursor-not-allowed' : 'cursor-pointer',
className className
); );
@@ -184,7 +184,7 @@ export function DropdownSubMenu(props: { children: React.ReactNode; label: React
return ( return (
<RadixDropdownMenu.Sub> <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"> <RadixDropdownMenu.SubTrigger className="flex cursor-pointer items-center justify-between rounded-sm straight-corners:rounded-xs px-3 py-1 text-sm focus:outline-hidden data-highlighted:bg-tint-hover">
{label} {label}
<Icon icon="chevron-right" className="size-3 shrink-0 opacity-6" /> <Icon icon="chevron-right" className="size-3 shrink-0 opacity-6" />
</RadixDropdownMenu.SubTrigger> </RadixDropdownMenu.SubTrigger>
@@ -0,0 +1,32 @@
'use client';
import { tcls } from '@/lib/tailwind';
import * as Tooltip from '@radix-ui/react-tooltip';
export function HoverCardRoot(props: Tooltip.TooltipProps) {
return <Tooltip.Root delayDuration={200} {...props} />;
}
export function HoverCardTrigger(props: { children: React.ReactNode }) {
return <Tooltip.Trigger asChild>{props.children}</Tooltip.Trigger>;
}
export function HoverCard(props: {
children: React.ReactNode;
className?: string;
}) {
return (
<Tooltip.Portal>
<Tooltip.Content className="z-40 w-screen max-w-md animate-present px-4 sm:w-auto">
<div
className={tcls(
'overflow-hidden rounded-md straight-corners:rounded-none bg-tint-base shadow-lg shadow-tint-12/4 ring-1 ring-tint-subtle dark:shadow-tint-1',
props.className
)}
>
{props.children}
</div>
<Tooltip.Arrow className="fill-tint-1" />
</Tooltip.Content>
</Tooltip.Portal>
);
}

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