mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cfc0c1f99 | |||
| 6ca03e53b4 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix "View activity" disclosure in AI Chat not opening after the Base UI migration.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Keep the breadcrumbs from covering the page actions' hit area.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix host action buttons in the Docs Embed not reaching the assistant.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix suggested question clicks in the embed search not opening the assistant.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Attribute the markdown and ask events tracked from the middleware to their space, and record the goal passed alongside a question.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Mermaid diagrams no longer hijack page scrolling: zooming with the wheel now requires holding Ctrl/Cmd inline, while the fullscreen view keeps free wheel zoom.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Migrate the headless UI primitives from Radix and react-aria to Base UI.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix OpenAPI webhook payload and schema example panels being clipped instead of scrollable.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Align the Previous page navigation button to the left edge and the Next button to the right edge, including when only one of them is present.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Render a site-space custom home page at its placement root while preserving the full space and normal page URLs.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix the docs embed not applying `?theme=light`/`?theme=dark`. The theme was dropped when the embed redirected to its default tab, and the embed tabs couldn't read it because they render statically (their request headers are empty). The middleware now threads a forced embed theme through the embed route context (scoped to the embed, not the main site), so the embed tabs honor it while staying statically rendered, and the redirect forwards it to the default tab. The forced theme is also persisted to the embed's own theme storage so it is remembered across tab navigation instead of only applying while the query string is present.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix code block syntax highlighting so comment delimiters (e.g. `//`, `/*`) use the same color as the rest of the comment. Previously the delimiter fell through to the generic punctuation scope, making it a different color from the comment body (most visible in dark mode).
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix the spacebar being ignored in the search bar, which made multi-word queries impossible.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix chevrons and open-state styling incorrectly reacting to a tooltip opening on the same trigger, by switching from the shared Base UI `data-popup-open` attribute to `aria-expanded`.
|
||||
@@ -21,59 +21,59 @@ outputs:
|
||||
description: 'Deployment URL'
|
||||
value: ${{ steps.deploy_middleware.outputs.deployment-url }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- id: wrangler_status
|
||||
name: Check wrangler deployment status
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: deployments status --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
using: 'composite'
|
||||
steps:
|
||||
- id: wrangler_status
|
||||
name: Check wrangler deployment status
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: deployments status --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
|
||||
# This step is used to get the version ID that is currently deployed to Cloudflare.
|
||||
- id: extract_current_version
|
||||
- id: extract_current_version
|
||||
name: Extract current version
|
||||
shell: bash
|
||||
run: |
|
||||
version_id=$(echo "${{ steps.wrangler_status.outputs.command-output }}" | grep -A 3 "(100%)" | grep -oP '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
||||
echo "version_id=$version_id" >> $GITHUB_OUTPUT
|
||||
|
||||
- id: deploy_server
|
||||
name: Deploy server to Cloudflare at 0%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
- id: deploy_server
|
||||
name: Deploy server to Cloudflare at 0%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
|
||||
# Since we use version overrides headers, we can directly deploy the middleware to 100%.
|
||||
- id: deploy_middleware
|
||||
name: Deploy middleware to Cloudflare at 100%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
|
||||
# Since we use version overrides headers, we can directly deploy the middleware to 100%.
|
||||
- id: deploy_middleware
|
||||
name: Deploy middleware to Cloudflare at 100%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
|
||||
|
||||
- name: Deploy server to Cloudflare at 100%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
- name: Deploy server to Cloudflare at 100%
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
|
||||
- name: Outputs
|
||||
shell: bash
|
||||
|
||||
@@ -67,32 +67,19 @@ runs:
|
||||
run: bun run turbo build:cloudflare
|
||||
env:
|
||||
GITBOOK_RUNTIME: cloudflare
|
||||
VERCEL_TARGET_ENV: ${{ inputs.environment }}
|
||||
GITBOOK_BLOCK_SEARCH_INDEXATION: ${{ inputs.environment == 'preview' && 'true' || '' }}
|
||||
GITBOOK_ALLOW_CUSTOMIZATION_OVERRIDE: ${{ inputs.environment == 'preview' && 'true' || '' }}
|
||||
shell: bash
|
||||
|
||||
- id: extract_deployment_id
|
||||
name: Extract Next deployment ID
|
||||
shell: bash
|
||||
run: |
|
||||
deployment_id=$(bun -e '
|
||||
const buildOutput = await Bun.file("packages/gitbook/.open-next/server-functions/default/packages/gitbook/.next/required-server-files.json").json();
|
||||
const deploymentId = buildOutput.config?.deploymentId;
|
||||
if (!deploymentId) throw new Error("Next build did not emit a deployment ID");
|
||||
console.log(deploymentId);
|
||||
')
|
||||
echo "deployment_id=$deployment_id" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload the DO worker
|
||||
uses: cloudflare/wrangler-action@v3.14.0
|
||||
with:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: ${{ format('deploy --var OPEN_NEXT_BUILD_ID:{0} --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc', steps.extract_deployment_id.outputs.deployment_id) }}
|
||||
command: deploy --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc
|
||||
|
||||
- id: upload_server
|
||||
name: Upload server to Cloudflare
|
||||
@@ -101,7 +88,7 @@ runs:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
|
||||
|
||||
@@ -124,7 +111,7 @@ runs:
|
||||
apiToken: ${{ inputs.apiToken }}
|
||||
accountId: ${{ inputs.accountId }}
|
||||
workingDirectory: ./
|
||||
wranglerVersion: '4.122.0'
|
||||
wranglerVersion: '4.43.0'
|
||||
environment: ${{ inputs.environment }}
|
||||
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
concurrency:
|
||||
|
||||
@@ -112,8 +112,7 @@
|
||||
"name": "gitbook",
|
||||
"version": "0.27.2",
|
||||
"dependencies": {
|
||||
"@base-ui/react": "catalog:",
|
||||
"@cloudflare/workers-types": "^5.20260716.1",
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/browser-types": "workspace:*",
|
||||
"@gitbook/cache-tags": "workspace:*",
|
||||
@@ -132,6 +131,13 @@
|
||||
"@opennextjs/aws": "4.1.0",
|
||||
"@opennextjs/cloudflare": "1.20.2",
|
||||
"@panzoom/panzoom": "^4.6.1",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.3",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@sindresorhus/fnv1a": "^3.1.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tusbar/cache-control": "^1.0.2",
|
||||
@@ -170,6 +176,7 @@
|
||||
"p-retry": "^8.0.0",
|
||||
"quick-lru": "^7.0.1",
|
||||
"react": "19.2.4",
|
||||
"react-aria": "^3.44.0",
|
||||
"react-dom": "19.2.4",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
@@ -223,7 +230,7 @@
|
||||
"ts-essentials": "^10.0.1",
|
||||
"typescript": "catalog:",
|
||||
"vercel": "50.37.3",
|
||||
"wrangler": "^4.122.0",
|
||||
"wrangler": "^4.79.0",
|
||||
},
|
||||
},
|
||||
"packages/icons": {
|
||||
@@ -310,7 +317,7 @@
|
||||
"name": "@gitbook/react-openapi",
|
||||
"version": "1.5.15",
|
||||
"dependencies": {
|
||||
"@base-ui/react": "catalog:",
|
||||
"@base-ui/react": "^1.7.0",
|
||||
"@gitbook/expr": "workspace:*",
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
"@scalar/api-client-react": "catalog:",
|
||||
@@ -345,6 +352,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@radix-ui/react-slot": "1.2.4",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"axios": "1.8.4",
|
||||
@@ -353,7 +361,6 @@
|
||||
"react-dom": "catalog:",
|
||||
},
|
||||
"catalog": {
|
||||
"@base-ui/react": "^1.7.0",
|
||||
"@gitbook/api": "0.195.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@tsconfig/node20": "^20.1.6",
|
||||
@@ -580,21 +587,21 @@
|
||||
|
||||
"@chevrotain/utils": ["@chevrotain/utils@11.1.1", "", {}, "sha512-71eTYMzYXYSFPrbg/ZwftSaSDld7UYlS8OQa3lNnn9jzNtpFbaReRRyghzqS7rI3CDaorqpPJJcXGHK+FE1TVQ=="],
|
||||
|
||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="],
|
||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
|
||||
|
||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="],
|
||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg=="],
|
||||
|
||||
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260811.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-i5jqz+ywtOefr0AJbiAc8qxBLfSim/B0WJG7aW3B+pWnoVfMJdUQvi+BWcFKZJ0MoCci3KadTx6g31VfuEEqpQ=="],
|
||||
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260401.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZSmceM70jH6k+/62VkEcmMNzrpr4kSctkX5Lsgqv38KktfhPY/hsh75y1lRoPWS3H3kgMa4p2pUSlidZR1u2hw=="],
|
||||
|
||||
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260811.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NoOUM/nvaDdm2Onlnz33FikWjtatzulNtvwvy4xs0IrHaTCHwC0c8NwIt6s+AI13FkDs02/vm2I3GTPLCT9+hQ=="],
|
||||
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260401.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7UKWF+IUZ3NXMVPsDg8Cjg0r58b+uYlfvs5Yt8bvtU+geCtW4P2MxRHmRSEo8SryckXOJjb/b8tcncgCykFu8g=="],
|
||||
|
||||
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260811.1", "", { "os": "linux", "cpu": "x64" }, "sha512-sdYq2jL1AD1supa3fsi5O4zTB28wSjvTHj7Migh6/ts8EROPdvrSwv+rdGHhv8HJNAz/wbIAY3wZsi1Rw4uUIg=="],
|
||||
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260401.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MDWUH/0bvL/l9aauN8zEddyYOXId1OueqrUCXXENNJ95R/lSmF6OgGVuXaYhoIhxQkNiEJ/0NOlnVYj9mJq4dw=="],
|
||||
|
||||
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260811.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RIRv4shbu1kg05sD+DHTpSFCNnb5Dl2SkPDMUykqZa508tkPqe7VVw7gO0Q5msTBGyL0FfFrLuRxwwfA8u5Sow=="],
|
||||
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260401.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-UgkzpMzVWM/bwbo3vjCTg2aoKfGcUhiEoQoDdo6RGWvbHRJyLVZ4VQCG9ZcISiztkiS2ICCoYOtPy6M/lV6Gcw=="],
|
||||
|
||||
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260811.1", "", { "os": "win32", "cpu": "x64" }, "sha512-g6VquwjASlYAibcNW/0E6Zszht4qLkmnXOGwIjjRHl2A0Qz48kVeMcGvyH6eA0G9U3OzZojjYFpP+YeyQmmdjw=="],
|
||||
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260401.1", "", { "os": "win32", "cpu": "x64" }, "sha512-HBLzcQF5iF4Qv20tQ++pG7xs3OsCnaIbc+GAi6fmhUKZhvmzvml/jwrQzLJ+MPm0cQo41K5OO/U3T4S8tvJetQ=="],
|
||||
|
||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@5.20260716.1", "", {}, "sha512-LqQPmGAvdpQxzZGAMlDI6fnCsTlr8nRQibWsREaERqD0PucwFl25aXpUskSob70uSY2n6K1sp6Te9xkmzSFgaw=="],
|
||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251011.0", "", {}, "sha512-gQpih+pbq3sP4uXltUeCSbPgZxTNp2gQd8639SaIbQMwgA6oJNHLhIART1fWy6DQACngiRzDVULA2x0ohmkGTQ=="],
|
||||
|
||||
"@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.4", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA=="],
|
||||
|
||||
@@ -722,6 +729,16 @@
|
||||
|
||||
"@floating-ui/vue": ["@floating-ui/vue@1.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.7.4", "@floating-ui/utils": "^0.2.10", "vue-demi": ">=0.13.0" } }, "sha512-BfNqNW6KA83Nexspgb9DZuz578R7HT8MZw1CfK9I6Ah4QReNWEJsXWHN+SdmOVLNGmTPDi+fDT535Df5PzMLbQ=="],
|
||||
|
||||
"@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.2", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.6", "@formatjs/intl-localematcher": "0.5.10", "decimal.js": "10", "tslib": "2" } }, "sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg=="],
|
||||
|
||||
"@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.6", "", { "dependencies": { "tslib": "2" } }, "sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw=="],
|
||||
|
||||
"@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.0", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.2", "@formatjs/icu-skeleton-parser": "1.8.12", "tslib": "2" } }, "sha512-Hp81uTjjdTk3FLh/dggU5NK7EIsVWc5/ZDWrIldmf2rBuPejuZ13CZ/wpVE2SToyi4EiroPTQ1XJcJuZFIxTtw=="],
|
||||
|
||||
"@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.12", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.2", "tslib": "2" } }, "sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg=="],
|
||||
|
||||
"@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="],
|
||||
|
||||
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.2.0", "", {}, "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw=="],
|
||||
|
||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q=="],
|
||||
@@ -832,9 +849,13 @@
|
||||
|
||||
"@inquirer/external-editor": ["@inquirer/external-editor@1.0.2", "", { "dependencies": { "chardet": "^2.1.0", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ=="],
|
||||
|
||||
"@internationalized/date": ["@internationalized/date@3.5.6", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-jLxQjefH9VI5P9UQuqB6qNKnvFt1Ky1TPIzHGsIlCi7sZZoMR8SdYbBGRvM0y+Jtb+ez4ieBzmiAUcpmPYpyOw=="],
|
||||
"@internationalized/date": ["@internationalized/date@3.10.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw=="],
|
||||
|
||||
"@internationalized/number": ["@internationalized/number@3.5.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-h9huwWjNqYyE2FXZZewWqmCdkw1HeFds5q4Siuoms3hUQC5iPJK3aBmkFZoDSLN4UD0Bl8G22L/NdHpeOr+/7A=="],
|
||||
"@internationalized/message": ["@internationalized/message@3.1.8", "", { "dependencies": { "@swc/helpers": "^0.5.0", "intl-messageformat": "^10.1.0" } }, "sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA=="],
|
||||
|
||||
"@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="],
|
||||
|
||||
"@internationalized/string": ["@internationalized/string@3.2.7", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A=="],
|
||||
|
||||
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
|
||||
|
||||
@@ -1082,20 +1103,272 @@
|
||||
|
||||
"@quansync/fs": ["@quansync/fs@0.1.5", "", { "dependencies": { "quansync": "^0.2.11" } }, "sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA=="],
|
||||
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.0", "", {}, "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="],
|
||||
|
||||
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
|
||||
|
||||
"@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-presence": "1.1.1", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw=="],
|
||||
|
||||
"@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="],
|
||||
|
||||
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.1", "@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-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA=="],
|
||||
|
||||
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.12", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VJoMs+BWWE7YhzEQyVwvF9n22Eiyr83HotCVrMQzla/OwRovXCgah7AcaEr4hMNj4gJxSdtIbcHGvmJXOoJVHA=="],
|
||||
|
||||
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
|
||||
|
||||
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA=="],
|
||||
|
||||
"@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
|
||||
|
||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||
|
||||
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.7", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.4", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.4", "@radix-ui/react-portal": "1.1.6", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-roving-focus": "1.1.7", "@radix-ui/react-slot": "1.2.0", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+qYq6LfbiGo97Zz9fioX83HCiIYYFNs8zAsVCMQrIakoNYylIzWuoD/anAD3UzvvR6cnswmfRFJFq/zYYq/k7Q=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.4", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.1", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wUi01RrTDTOoGtjEPHsxlzPtVzVc3R/AZ5wfh0dyqMAqolhHAHvG5iQjBCTi2AjQqa77FWWbA3kE3RkD+bDMgQ=="],
|
||||
|
||||
"@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.1", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.0", "@radix-ui/react-portal": "1.1.2", "@radix-ui/react-presence": "1.1.1", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-slot": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.1.1", "react-remove-scroll": "2.6.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w=="],
|
||||
|
||||
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
|
||||
|
||||
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
|
||||
|
||||
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A=="],
|
||||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.0", "", { "dependencies": { "@radix-ui/react-slot": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-C6oAg451/fQT3EGbWHbCQjYTtbyjNO1uzQgMzwyivcHT3GKNEmu1q3UuREhN+HzHAVtv3ivMVK08QlC+PkYw9Q=="],
|
||||
|
||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "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-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="],
|
||||
|
||||
"@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA=="],
|
||||
|
||||
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
|
||||
|
||||
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og=="],
|
||||
|
||||
"@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
|
||||
|
||||
"@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.1", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.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-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg=="],
|
||||
|
||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||
|
||||
"@react-aria/breadcrumbs": ["@react-aria/breadcrumbs@3.5.29", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/link": "^3.8.6", "@react-aria/utils": "^3.31.0", "@react-types/breadcrumbs": "^3.7.17", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-rKS0dryllaZJqrr3f/EAf2liz8CBEfmL5XACj+Z1TAig6GIYe1QuA3BtkX0cV9OkMugXdX8e3cbA7nD10ORRqg=="],
|
||||
|
||||
"@react-aria/button": ["@react-aria/button@3.14.2", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/toolbar": "3.0.0-beta.21", "@react-aria/utils": "^3.31.0", "@react-stately/toggle": "^3.9.2", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-VbLIA+Kd6f/MDjd+TJBUg2+vNDw66pnvsj2E4RLomjI9dfBuN7d+Yo2UnsqKVyhePjCUZ6xxa2yDuD63IOSIYA=="],
|
||||
|
||||
"@react-aria/calendar": ["@react-aria/calendar@3.9.2", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.31.0", "@react-stately/calendar": "^3.9.0", "@react-types/button": "^3.14.1", "@react-types/calendar": "^3.8.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-uSLxLgOPRnEU4Jg59lAhUVA+uDx/55NBg4lpfsP2ynazyiJ5LCXmYceJi+VuOqMml7d9W0dB87OldOeLdIxYVA=="],
|
||||
|
||||
"@react-aria/checkbox": ["@react-aria/checkbox@3.16.2", "", { "dependencies": { "@react-aria/form": "^3.1.2", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/toggle": "^3.12.2", "@react-aria/utils": "^3.31.0", "@react-stately/checkbox": "^3.7.2", "@react-stately/form": "^3.2.2", "@react-stately/toggle": "^3.9.2", "@react-types/checkbox": "^3.10.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-29Mj9ZqXioJ0bcMnNGooHztnTau5pikZqX3qCRj5bYR3by/ZFFavYoMroh9F7s/MbFm/tsKX+Sf02lYFEdXRjA=="],
|
||||
|
||||
"@react-aria/color": ["@react-aria/color@3.1.2", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/numberfield": "^3.12.2", "@react-aria/slider": "^3.8.2", "@react-aria/spinbutton": "^3.6.19", "@react-aria/textfield": "^3.18.2", "@react-aria/utils": "^3.31.0", "@react-aria/visually-hidden": "^3.8.28", "@react-stately/color": "^3.9.2", "@react-stately/form": "^3.2.2", "@react-types/color": "^3.1.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-jCC+Q7rAQGLQBkHjkPAeDuGYuMbc4neifjlNRiyZ9as1z4gg63H8MteoWYYk6K4vCKKxSixgt8MfI29XWMOWPQ=="],
|
||||
|
||||
"@react-aria/combobox": ["@react-aria/combobox@3.14.0", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/listbox": "^3.15.0", "@react-aria/live-announcer": "^3.4.4", "@react-aria/menu": "^3.19.3", "@react-aria/overlays": "^3.30.0", "@react-aria/selection": "^3.26.0", "@react-aria/textfield": "^3.18.2", "@react-aria/utils": "^3.31.0", "@react-stately/collections": "^3.12.8", "@react-stately/combobox": "^3.12.0", "@react-stately/form": "^3.2.2", "@react-types/button": "^3.14.1", "@react-types/combobox": "^3.13.9", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-z4ro0Hma//p4nL2IJx5iUa7NwxeXbzSoZ0se5uTYjG1rUUMszg+wqQh/AQoL+eiULn7rs18JY9wwNbVIkRNKWA=="],
|
||||
|
||||
"@react-aria/datepicker": ["@react-aria/datepicker@3.15.2", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-aria/focus": "^3.21.2", "@react-aria/form": "^3.1.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/spinbutton": "^3.6.19", "@react-aria/utils": "^3.31.0", "@react-stately/datepicker": "^3.15.2", "@react-stately/form": "^3.2.2", "@react-types/button": "^3.14.1", "@react-types/calendar": "^3.8.0", "@react-types/datepicker": "^3.13.2", "@react-types/dialog": "^3.5.22", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-th078hyNqPf4P2K10su/y32zPDjs3lOYVdHvsL9/+5K1dnTvLHCK5vgUyLuyn8FchhF7cmHV49D+LZVv65PEpQ=="],
|
||||
|
||||
"@react-aria/dialog": ["@react-aria/dialog@3.5.31", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/overlays": "^3.30.0", "@react-aria/utils": "^3.31.0", "@react-types/dialog": "^3.5.22", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-inxQMyrzX0UBW9Mhraq0nZ4HjHdygQvllzloT1E/RlDd61lr3RbmJR6pLsrbKOTtSvDIBJpCso1xEdHCFNmA0Q=="],
|
||||
|
||||
"@react-aria/disclosure": ["@react-aria/disclosure@3.1.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.31.0", "@react-stately/disclosure": "^3.0.8", "@react-types/button": "^3.14.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-5996BeBpnj+yKXYysz+UuhFQxGFPvaZZ3zNBd052wz/i+TVFVGSqqYJ6cwZyO1AfBR8zOT0ZIiK4EC3ETwSvtQ=="],
|
||||
|
||||
"@react-aria/dnd": ["@react-aria/dnd@3.11.3", "", { "dependencies": { "@internationalized/string": "^3.2.7", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/live-announcer": "^3.4.4", "@react-aria/overlays": "^3.30.0", "@react-aria/utils": "^3.31.0", "@react-stately/collections": "^3.12.8", "@react-stately/dnd": "^3.7.1", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-MyTziciik1Owz3rqDghu0K3ZtTFvmj/R2ZsLDwbU9N4hKqGX/BKnrI8SytTn8RDqVv5LmA/GhApLngiupTAsXw=="],
|
||||
|
||||
"@react-aria/focus": ["@react-aria/focus@3.21.2", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ=="],
|
||||
|
||||
"@react-aria/form": ["@react-aria/form@3.1.2", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-stately/form": "^3.2.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-R3i7L7Ci61PqZQvOrnL9xJeWEbh28UkTVgkj72EvBBn39y4h7ReH++0stv7rRs8p5ozETSKezBbGfu4UsBewWw=="],
|
||||
|
||||
"@react-aria/grid": ["@react-aria/grid@3.14.5", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/live-announcer": "^3.4.4", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/collections": "^3.12.8", "@react-stately/grid": "^3.11.6", "@react-stately/selection": "^3.20.6", "@react-types/checkbox": "^3.10.2", "@react-types/grid": "^3.3.6", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-XHw6rgjlTqc85e3zjsWo3U0EVwjN5MOYtrolCKc/lc2ItNdcY3OlMhpsU9+6jHwg/U3VCSWkGvwAz9hg7krd8Q=="],
|
||||
|
||||
"@react-aria/gridlist": ["@react-aria/gridlist@3.14.1", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/grid": "^3.14.5", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/list": "^3.13.1", "@react-stately/tree": "^3.9.3", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-keS03Am07aOn7RuNaRsMOyh0jscyhDn95asCVy4lxhl9A9TFk1Jw0o2L6q6cWRj1gFiKeacj/otG5H8ZKQQ2Wg=="],
|
||||
|
||||
"@react-aria/i18n": ["@react-aria/i18n@3.12.13", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@internationalized/message": "^3.1.8", "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-YTM2BPg0v1RvmP8keHenJBmlx8FXUKsdYIEX7x6QWRd1hKlcDwphfjzvt0InX9wiLiPHsT5EoBTpuUk8SXc0Mg=="],
|
||||
|
||||
"@react-aria/interactions": ["@react-aria/interactions@3.25.6", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.31.0", "@react-stately/flags": "^3.1.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw=="],
|
||||
|
||||
"@react-aria/label": ["@react-aria/label@3.7.22", "", { "dependencies": { "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-jLquJeA5ZNqDT64UpTc9XJ7kQYltUlNcgxZ37/v4mHe0UZ7QohCKdKQhXHONb0h2jjNUpp2HOZI8J9++jOpzxA=="],
|
||||
|
||||
"@react-aria/landmark": ["@react-aria/landmark@3.0.7", "", { "dependencies": { "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-t8c610b8hPLS6Vwv+rbuSyljZosI1s5+Tosfa0Fk4q7d+Ex6Yj7hLfUFy59GxZAufhUYfGX396fT0gPqAbU1tg=="],
|
||||
|
||||
"@react-aria/link": ["@react-aria/link@3.8.6", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-types/link": "^3.6.5", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-7F7UDJnwbU9IjfoAdl6f3Hho5/WB7rwcydUOjUux0p7YVWh/fTjIFjfAGyIir7MJhPapun1D0t97QQ3+8jXVcg=="],
|
||||
|
||||
"@react-aria/listbox": ["@react-aria/listbox@3.15.0", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/collections": "^3.12.8", "@react-stately/list": "^3.13.1", "@react-types/listbox": "^3.7.4", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Ub1Wu79R9sgxM7h4HeEdjOgOKDHwduvYcnDqsSddGXgpkL8ADjsy2YUQ0hHY5VnzA4BxK36bLp4mzSna8Qvj1w=="],
|
||||
|
||||
"@react-aria/live-announcer": ["@react-aria/live-announcer@3.4.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA=="],
|
||||
|
||||
"@react-aria/menu": ["@react-aria/menu@3.19.3", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/overlays": "^3.30.0", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/collections": "^3.12.8", "@react-stately/menu": "^3.9.8", "@react-stately/selection": "^3.20.6", "@react-stately/tree": "^3.9.3", "@react-types/button": "^3.14.1", "@react-types/menu": "^3.10.5", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-52fh8y8b2776R2VrfZPpUBJYC9oTP7XDy+zZuZTxPEd7Ywk0JNUl5F92y6ru22yPkS13sdhrNM/Op+V/KulmAg=="],
|
||||
|
||||
"@react-aria/meter": ["@react-aria/meter@3.4.27", "", { "dependencies": { "@react-aria/progress": "^3.4.27", "@react-types/meter": "^3.4.13", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-andOOdJkgRJF9vBi5VWRmFodK+GT+5X1lLeNUmb4qOX8/MVfX/RbK72LDeIhd7xC7rSCFHj3WvZ198rK4q0k3w=="],
|
||||
|
||||
"@react-aria/numberfield": ["@react-aria/numberfield@3.12.2", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/spinbutton": "^3.6.19", "@react-aria/textfield": "^3.18.2", "@react-aria/utils": "^3.31.0", "@react-stately/form": "^3.2.2", "@react-stately/numberfield": "^3.10.2", "@react-types/button": "^3.14.1", "@react-types/numberfield": "^3.8.15", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-M2b+z0HIXiXpGAWOQkO2kpIjaLNUXJ5Q3/GMa3Fkr+B1piFX0VuOynYrtddKVrmXCe+r5t+XcGb0KS29uqv7nQ=="],
|
||||
|
||||
"@react-aria/overlays": ["@react-aria/overlays@3.30.0", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.31.0", "@react-aria/visually-hidden": "^3.8.28", "@react-stately/overlays": "^3.6.20", "@react-types/button": "^3.14.1", "@react-types/overlays": "^3.9.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-UpjqSjYZx5FAhceWCRVsW6fX1sEwya1fQ/TKkL53FAlLFR8QKuoKqFlmiL43YUFTcGK3UdEOy3cWTleLQwdSmQ=="],
|
||||
|
||||
"@react-aria/progress": ["@react-aria/progress@3.4.27", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/label": "^3.7.22", "@react-aria/utils": "^3.31.0", "@react-types/progress": "^3.5.16", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-0OA1shs1575g1zmO8+rWozdbTnxThFFhOfuoL1m7UV5Dley6FHpueoKB1ECv7B+Qm4dQt6DoEqLg7wsbbQDhmg=="],
|
||||
|
||||
"@react-aria/radio": ["@react-aria/radio@3.12.2", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/form": "^3.1.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/utils": "^3.31.0", "@react-stately/radio": "^3.11.2", "@react-types/radio": "^3.9.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-I11f6I90neCh56rT/6ieAs3XyDKvEfbj/QmbU5cX3p+SJpRRPN0vxQi5D1hkh0uxDpeClxygSr31NmZsd4sqfg=="],
|
||||
|
||||
"@react-aria/searchfield": ["@react-aria/searchfield@3.8.9", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/textfield": "^3.18.2", "@react-aria/utils": "^3.31.0", "@react-stately/searchfield": "^3.5.16", "@react-types/button": "^3.14.1", "@react-types/searchfield": "^3.6.6", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Yt2pj8Wb5/XsUr2T0DQqFv+DlFpzzWIWnNr9cJATUcWV/xw6ok7YFEg9+7EHtBmsCQxFFJtock1QfZzBw6qLtQ=="],
|
||||
|
||||
"@react-aria/select": ["@react-aria/select@3.17.0", "", { "dependencies": { "@react-aria/form": "^3.1.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/listbox": "^3.15.0", "@react-aria/menu": "^3.19.3", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-aria/visually-hidden": "^3.8.28", "@react-stately/select": "^3.8.0", "@react-types/button": "^3.14.1", "@react-types/select": "^3.11.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-q5ZuyAn5jSOeI0Ys99951TaGcF4O7u1SSBVxPMwVVXOU8ZhToCNx+WG3n/JDYHEjqdo7sbsVRaPA7LkBzBGf5w=="],
|
||||
|
||||
"@react-aria/selection": ["@react-aria/selection@3.26.0", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-stately/selection": "^3.20.6", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZBH3EfWZ+RfhTj01dH8L17uT7iNbXWS8u77/fUpHgtrm0pwNVhx0TYVnLU1YpazQ/3WVpvWhmBB8sWwD1FlD/g=="],
|
||||
|
||||
"@react-aria/separator": ["@react-aria/separator@3.4.13", "", { "dependencies": { "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-0NlcrdBfQbcjWEXdHl3+uSY1272n2ljT1gWL2RIf6aQsQWTZ0gz0rTgRHy0MTXN+y+tICItUERJT4vmTLtIzVg=="],
|
||||
|
||||
"@react-aria/slider": ["@react-aria/slider@3.8.2", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/utils": "^3.31.0", "@react-stately/slider": "^3.7.2", "@react-types/shared": "^3.32.1", "@react-types/slider": "^3.8.2", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-6KyUGaVzRE4xAz1LKHbNh1q5wzxe58pdTHFSnxNe6nk1SCoHw7NfI4h2s2m6LgJ0megFxsT0Ir8aHaFyyxmbgg=="],
|
||||
|
||||
"@react-aria/spinbutton": ["@react-aria/spinbutton@3.6.19", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.31.0", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-xOIXegDpts9t3RSHdIN0iYQpdts0FZ3LbpYJIYVvdEHo9OpDS+ElnDzCGtwZLguvZlwc5s1LAKuKopDUsAEMkw=="],
|
||||
|
||||
"@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="],
|
||||
|
||||
"@react-aria/switch": ["@react-aria/switch@3.7.8", "", { "dependencies": { "@react-aria/toggle": "^3.12.2", "@react-stately/toggle": "^3.9.2", "@react-types/shared": "^3.32.1", "@react-types/switch": "^3.5.15", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AfsUq1/YiuoprhcBUD9vDPyWaigAwctQNW1fMb8dROL+i/12B+Zekj8Ml+jbU69/kIVtfL0Jl7/0Bo9KK3X0xQ=="],
|
||||
|
||||
"@react-aria/table": ["@react-aria/table@3.17.8", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/grid": "^3.14.5", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.31.0", "@react-aria/visually-hidden": "^3.8.28", "@react-stately/collections": "^3.12.8", "@react-stately/flags": "^3.1.2", "@react-stately/table": "^3.15.1", "@react-types/checkbox": "^3.10.2", "@react-types/grid": "^3.3.6", "@react-types/shared": "^3.32.1", "@react-types/table": "^3.13.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-bXiZoxTMbsqUJsYDhHPzKc3jw0HFJ/xMsJ49a0f7mp5r9zACxNLeIU0wJ4Uvx37dnYOHKzGliG+rj5l4sph7MA=="],
|
||||
|
||||
"@react-aria/tabs": ["@react-aria/tabs@3.10.8", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/tabs": "^3.8.6", "@react-types/shared": "^3.32.1", "@react-types/tabs": "^3.3.19", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-sPPJyTyoAqsBh76JinBAxStOcbjZvyWFYKpJ9Uqw+XT0ObshAPPFSGeh8DiQemPs02RwJdrfARPMhyqiX8t59A=="],
|
||||
|
||||
"@react-aria/tag": ["@react-aria/tag@3.7.2", "", { "dependencies": { "@react-aria/gridlist": "^3.14.1", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/list": "^3.13.1", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-JV679P5r4DftbqyNBRt7Nw9mP7dxaKPfikjyQuvUoEOa06wBLbM/hU9RJUPRvqK+Un6lgBDAmXD9NNf4N2xpdw=="],
|
||||
|
||||
"@react-aria/textfield": ["@react-aria/textfield@3.18.2", "", { "dependencies": { "@react-aria/form": "^3.1.2", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/utils": "^3.31.0", "@react-stately/form": "^3.2.2", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@react-types/textfield": "^3.12.6", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-G+lM8VYSor6g9Yptc6hLZ6BF+0cq0pYol1z6wdQUQgJN8tg4HPtzq75lsZtlCSIznL3amgRAxJtd0dUrsAnvaQ=="],
|
||||
|
||||
"@react-aria/toast": ["@react-aria/toast@3.0.8", "", { "dependencies": { "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/landmark": "^3.0.7", "@react-aria/utils": "^3.31.0", "@react-stately/toast": "^3.1.2", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-rfJIms6AkMyQ7ZgKrMZgGfPwGcB/t1JoEwbc1PAmXcAvFI/hzF6YF7ZFDXiq38ucFsP9PnHmbXIzM9w4ccl18A=="],
|
||||
|
||||
"@react-aria/toggle": ["@react-aria/toggle@3.12.2", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-stately/toggle": "^3.9.2", "@react-types/checkbox": "^3.10.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-g25XLYqJuJpt0/YoYz2Rab8ax+hBfbssllcEFh0v0jiwfk2gwTWfRU9KAZUvxIqbV8Nm8EBmrYychDpDcvW1kw=="],
|
||||
|
||||
"@react-aria/toolbar": ["@react-aria/toolbar@3.0.0-beta.21", "", { "dependencies": { "@react-aria/focus": "^3.21.2", "@react-aria/i18n": "^3.12.13", "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-yRCk/GD8g+BhdDgxd3I0a0c8Ni4Wyo6ERzfSoBkPkwQ4X2E2nkopmraM9D0fXw4UcIr4bnmvADzkHXtBN0XrBg=="],
|
||||
|
||||
"@react-aria/tooltip": ["@react-aria/tooltip@3.8.8", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-stately/tooltip": "^3.5.8", "@react-types/shared": "^3.32.1", "@react-types/tooltip": "^3.4.21", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-CmHUqtXtFWmG4AHMEr9hIVex+oscK6xcM2V47gq9ijNInxe3M6UBu/dBdkgGP/jYv9N7tzCAjTR8nNIHQXwvWw=="],
|
||||
|
||||
"@react-aria/tree": ["@react-aria/tree@3.1.4", "", { "dependencies": { "@react-aria/gridlist": "^3.14.1", "@react-aria/i18n": "^3.12.13", "@react-aria/selection": "^3.26.0", "@react-aria/utils": "^3.31.0", "@react-stately/tree": "^3.9.3", "@react-types/button": "^3.14.1", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-6pbFeN0dAsCOrFGUKU39CNjft20zCAjLfMqfkRWisL+JkUHI2nq6odUJF5jJTsU1C+1951+3oFOmVxPX+K+akQ=="],
|
||||
|
||||
"@react-aria/utils": ["@react-aria/utils@3.31.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig=="],
|
||||
|
||||
"@react-aria/visually-hidden": ["@react-aria/visually-hidden@3.8.28", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-KRRjbVVob2CeBidF24dzufMxBveEUtUu7IM+hpdZKB+gxVROoh4XRLPv9SFmaH89Z7D9To3QoykVZoWD0lan6Q=="],
|
||||
|
||||
"@react-stately/calendar": ["@react-stately/calendar@3.9.0", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@react-stately/utils": "^3.10.8", "@react-types/calendar": "^3.8.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-U5Nf2kx9gDhJRxdDUm5gjfyUlt/uUfOvM1vDW2UA62cA6+2k2cavMLc2wNlXOb/twFtl6p0joYKHG7T4xnEFkg=="],
|
||||
|
||||
"@react-stately/checkbox": ["@react-stately/checkbox@3.7.2", "", { "dependencies": { "@react-stately/form": "^3.2.2", "@react-stately/utils": "^3.10.8", "@react-types/checkbox": "^3.10.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-j1ycUVz5JmqhaL6mDZgDNZqBilOB8PBW096sDPFaTtuYreDx2HOd1igxiIvwlvPESZwsJP7FVM3mYnaoXtpKPA=="],
|
||||
|
||||
"@react-stately/collections": ["@react-stately/collections@3.12.8", "", { "dependencies": { "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ=="],
|
||||
|
||||
"@react-stately/color": ["@react-stately/color@3.9.2", "", { "dependencies": { "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-stately/form": "^3.2.2", "@react-stately/numberfield": "^3.10.2", "@react-stately/slider": "^3.7.2", "@react-stately/utils": "^3.10.8", "@react-types/color": "^3.1.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-F+6Do8W3yu/4n7MpzZtbXwVukcLTFYYDIUtpoR+Jl52UmAr9Hf1CQgkyTI2azv1ZMzj1mVrTBhpBL0q27kFZig=="],
|
||||
|
||||
"@react-stately/combobox": ["@react-stately/combobox@3.12.0", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/form": "^3.2.2", "@react-stately/list": "^3.13.1", "@react-stately/overlays": "^3.6.20", "@react-stately/utils": "^3.10.8", "@react-types/combobox": "^3.13.9", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-A6q9R/7cEa/qoQsBkdslXWvD7ztNLLQ9AhBhVN9QvzrmrH5B4ymUwcTU8lWl22ykH7RRwfonLeLXJL4C+/L2oQ=="],
|
||||
|
||||
"@react-stately/datepicker": ["@react-stately/datepicker@3.15.2", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@internationalized/string": "^3.2.7", "@react-stately/form": "^3.2.2", "@react-stately/overlays": "^3.6.20", "@react-stately/utils": "^3.10.8", "@react-types/datepicker": "^3.13.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-S5GL+W37chvV8knv9v0JRv0L6hKo732qqabCCHXzOpYxkLIkV4f/y3cHdEzFWzpZ0O0Gkg7WgeYo160xOdBKYg=="],
|
||||
|
||||
"@react-stately/disclosure": ["@react-stately/disclosure@3.0.8", "", { "dependencies": { "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-/Ce/Z76y85eSBZiemfU/uEyXkBBa1RdfLRaKD13rnfUV7/nS3ae1VtNlsXgmwQjWv2pmAiSuEKYMbZfVL7q/lQ=="],
|
||||
|
||||
"@react-stately/dnd": ["@react-stately/dnd@3.7.1", "", { "dependencies": { "@react-stately/selection": "^3.20.6", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-O1JBJ4HI1rVNKuoa5NXiC5FCrCEkr9KVBoKNlTZU8/cnQselhbEsUfMglAakO2EuwIaM1tIXoNF5J/N5P+6lTA=="],
|
||||
|
||||
"@react-stately/flags": ["@react-stately/flags@3.1.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg=="],
|
||||
|
||||
"@react-stately/form": ["@react-stately/form@3.2.2", "", { "dependencies": { "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-soAheOd7oaTO6eNs6LXnfn0tTqvOoe3zN9FvtIhhrErKz9XPc5sUmh3QWwR45+zKbitOi1HOjfA/gifKhZcfWw=="],
|
||||
|
||||
"@react-stately/grid": ["@react-stately/grid@3.11.6", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/selection": "^3.20.6", "@react-types/grid": "^3.3.6", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-vWPAkzpeTIsrurHfMubzMuqEw7vKzFhIJeEK5sEcLunyr1rlADwTzeWrHNbPMl66NAIAi70Dr1yNq+kahQyvMA=="],
|
||||
|
||||
"@react-stately/list": ["@react-stately/list@3.13.1", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/selection": "^3.20.6", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-eHaoauh21twbcl0kkwULhVJ+CzYcy1jUjMikNVMHOQdhr4WIBdExf7PmSgKHKqsSPhpGg6IpTCY2dUX3RycjDg=="],
|
||||
|
||||
"@react-stately/menu": ["@react-stately/menu@3.9.8", "", { "dependencies": { "@react-stately/overlays": "^3.6.20", "@react-types/menu": "^3.10.5", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-bo0NOhofnTHLESiYfsSSw6gyXiPVJJ0UlN2igUXtJk5PmyhWjFzUzTzcnd7B028OB0si9w3LIWM3stqz5271Eg=="],
|
||||
|
||||
"@react-stately/numberfield": ["@react-stately/numberfield@3.10.2", "", { "dependencies": { "@internationalized/number": "^3.6.5", "@react-stately/form": "^3.2.2", "@react-stately/utils": "^3.10.8", "@react-types/numberfield": "^3.8.15", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-jlKVFYaH3RX5KvQ7a+SAMQuPccZCzxLkeYkBE64u1Zvi7YhJ8hkTMHG/fmZMbk1rHlseE2wfBdk0Rlya3MvoNQ=="],
|
||||
|
||||
"@react-stately/overlays": ["@react-stately/overlays@3.6.20", "", { "dependencies": { "@react-stately/utils": "^3.10.8", "@react-types/overlays": "^3.9.2", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-YAIe+uI8GUXX8F/0Pzr53YeC5c/bjqbzDFlV8NKfdlCPa6+Jp4B/IlYVjIooBj9+94QvbQdjylegvYWK/iPwlg=="],
|
||||
|
||||
"@react-stately/radio": ["@react-stately/radio@3.11.2", "", { "dependencies": { "@react-stately/form": "^3.2.2", "@react-stately/utils": "^3.10.8", "@react-types/radio": "^3.9.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-UM7L6AW+k8edhSBUEPZAqiWNRNadfOKK7BrCXyBiG79zTz0zPcXRR+N+gzkDn7EMSawDeyK1SHYUuoSltTactg=="],
|
||||
|
||||
"@react-stately/searchfield": ["@react-stately/searchfield@3.5.16", "", { "dependencies": { "@react-stately/utils": "^3.10.8", "@react-types/searchfield": "^3.6.6", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-MRfqT1lZ24r94GuFNcGJXsfijZoWjSMySCT60T6NXtbOzVPuAF3K+pL70Rayq/EWLJjS2NPHND11VTs0VdcE0Q=="],
|
||||
|
||||
"@react-stately/select": ["@react-stately/select@3.8.0", "", { "dependencies": { "@react-stately/form": "^3.2.2", "@react-stately/list": "^3.13.1", "@react-stately/overlays": "^3.6.20", "@react-stately/utils": "^3.10.8", "@react-types/select": "^3.11.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-A721nlt0DSCDit0wKvhcrXFTG5Vv1qkEVkeKvobmETZy6piKvwh0aaN8iQno5AFuZaj1iOZeNjZ/20TsDJR/4A=="],
|
||||
|
||||
"@react-stately/selection": ["@react-stately/selection@3.20.6", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-a0bjuP2pJYPKEiedz2Us1W1aSz0iHRuyeQEdBOyL6Z6VUa6hIMq9H60kvseir2T85cOa4QggizuRV7mcO6bU5w=="],
|
||||
|
||||
"@react-stately/slider": ["@react-stately/slider@3.7.2", "", { "dependencies": { "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@react-types/slider": "^3.8.2", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-EVBHUdUYwj++XqAEiQg2fGi8Reccznba0uyQ3gPejF0pAc390Q/J5aqiTEDfiCM7uJ6WHxTM6lcCqHQBISk2dQ=="],
|
||||
|
||||
"@react-stately/table": ["@react-stately/table@3.15.1", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/flags": "^3.1.2", "@react-stately/grid": "^3.11.6", "@react-stately/selection": "^3.20.6", "@react-stately/utils": "^3.10.8", "@react-types/grid": "^3.3.6", "@react-types/shared": "^3.32.1", "@react-types/table": "^3.13.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-MhMAgE/LgAzHcAn1P3p/nQErzJ6DiixSJ1AOt2JlnAKEb5YJg4ATKWCb2IjBLwywt9ZCzfm3KMUzkctZqAoxwA=="],
|
||||
|
||||
"@react-stately/tabs": ["@react-stately/tabs@3.8.6", "", { "dependencies": { "@react-stately/list": "^3.13.1", "@react-types/shared": "^3.32.1", "@react-types/tabs": "^3.3.19", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-9RYxmgjVIxUpIsGKPIF7uRoHWOEz8muwaYiStCVeyiYBPmarvZoIYtTXcwSMN/vEs7heVN5uGCL6/bfdY4+WiA=="],
|
||||
|
||||
"@react-stately/toast": ["@react-stately/toast@3.1.2", "", { "dependencies": { "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-HiInm7bck32khFBHZThTQaAF6e6/qm57F4mYRWdTq8IVeGDzpkbUYibnLxRhk0UZ5ybc6me+nqqPkG/lVmM42Q=="],
|
||||
|
||||
"@react-stately/toggle": ["@react-stately/toggle@3.9.2", "", { "dependencies": { "@react-stately/utils": "^3.10.8", "@react-types/checkbox": "^3.10.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-dOxs9wrVXHUmA7lc8l+N9NbTJMAaXcYsnNGsMwfXIXQ3rdq+IjWGNYJ52UmNQyRYFcg0jrzRrU16TyGbNjOdNQ=="],
|
||||
|
||||
"@react-stately/tooltip": ["@react-stately/tooltip@3.5.8", "", { "dependencies": { "@react-stately/overlays": "^3.6.20", "@react-types/tooltip": "^3.4.21", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-gkcUx2ROhCiGNAYd2BaTejakXUUNLPnnoJ5+V/mN480pN+OrO8/2V9pqb/IQmpqxLsso93zkM3A4wFHHLBBmPQ=="],
|
||||
|
||||
"@react-stately/tree": ["@react-stately/tree@3.9.3", "", { "dependencies": { "@react-stately/collections": "^3.12.8", "@react-stately/selection": "^3.20.6", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZngG79nLFxE/GYmpwX6E/Rma2MMkzdoJPRI3iWk3dgqnGMMzpPnUp/cvjDsU3UHF7xDVusC5BT6pjWN0uxCIFQ=="],
|
||||
|
||||
"@react-stately/utils": ["@react-stately/utils@3.10.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g=="],
|
||||
|
||||
"@react-types/breadcrumbs": ["@react-types/breadcrumbs@3.7.17", "", { "dependencies": { "@react-types/link": "^3.6.5", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-IhvVTcfli5o/UDlGACXxjlor2afGlMQA8pNR3faH0bBUay1Fmm3IWktVw9Xwmk+KraV2RTAg9e+E6p8DOQZfiw=="],
|
||||
|
||||
"@react-types/button": ["@react-types/button@3.14.1", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-D8C4IEwKB7zEtiWYVJ3WE/5HDcWlze9mLWQ5hfsBfpePyWCgO3bT/+wjb/7pJvcAocrkXo90QrMm85LcpBtrpg=="],
|
||||
|
||||
"@react-types/calendar": ["@react-types/calendar@3.8.0", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZDZgfZgbz1ydWOFs1mH7QFfX3ioJrmb3Y/lkoubQE0HWXLZzyYNvhhKyFJRS1QJ40IofLSBHriwbQb/tsUnGlw=="],
|
||||
|
||||
"@react-types/checkbox": ["@react-types/checkbox@3.10.2", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ=="],
|
||||
|
||||
"@react-types/color": ["@react-types/color@3.1.2", "", { "dependencies": { "@react-types/shared": "^3.32.1", "@react-types/slider": "^3.8.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-NP0TAY3j4tlMztOp/bBfMlPwC9AQKTjSiTFmc2oQNkx5M4sl3QpPqFPosdt7jZ8M4nItvfCWZrlZGjST4SB83A=="],
|
||||
|
||||
"@react-types/combobox": ["@react-types/combobox@3.13.9", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-G6GmLbzVkLW6VScxPAr/RtliEyPhBClfYaIllK1IZv+Z42SVnOpKzhnoe79BpmiFqy1AaC3+LjZX783mrsHCwA=="],
|
||||
|
||||
"@react-types/datepicker": ["@react-types/datepicker@3.13.2", "", { "dependencies": { "@internationalized/date": "^3.10.0", "@react-types/calendar": "^3.8.0", "@react-types/overlays": "^3.9.2", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-+M6UZxJnejYY8kz0spbY/hP08QJ5rsZ3aNarRQQHc48xV2oelFLX5MhAqizfLEsvyfb0JYrhWoh4z1xZtAmYCg=="],
|
||||
|
||||
"@react-types/dialog": ["@react-types/dialog@3.5.22", "", { "dependencies": { "@react-types/overlays": "^3.9.2", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-smSvzOcqKE196rWk0oqJDnz+ox5JM5+OT0PmmJXiUD4q7P5g32O6W5Bg7hMIFUI9clBtngo8kLaX2iMg+GqAzg=="],
|
||||
|
||||
"@react-types/grid": ["@react-types/grid@3.3.6", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw=="],
|
||||
|
||||
"@react-types/link": ["@react-types/link@3.6.5", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-+I2s3XWBEvLrzts0GnNeA84mUkwo+a7kLUWoaJkW0TOBDG7my95HFYxF9WnqKye7NgpOkCqz4s3oW96xPdIniQ=="],
|
||||
|
||||
"@react-types/listbox": ["@react-types/listbox@3.7.4", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-p4YEpTl/VQGrqVE8GIfqTS5LkT5jtjDTbVeZgrkPnX/fiPhsfbTPiZ6g0FNap4+aOGJFGEEZUv2q4vx+rCORww=="],
|
||||
|
||||
"@react-types/menu": ["@react-types/menu@3.10.5", "", { "dependencies": { "@react-types/overlays": "^3.9.2", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-HBTrKll2hm0VKJNM4ubIv1L9MNo8JuOnm2G3M+wXvb6EYIyDNxxJkhjsqsGpUXJdAOSkacHBDcNh2HsZABNX4A=="],
|
||||
|
||||
"@react-types/meter": ["@react-types/meter@3.4.13", "", { "dependencies": { "@react-types/progress": "^3.5.16" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-EiarfbpHcvmeyXvXcr6XLaHkNHuGc4g7fBVEiDPwssFJKKfbUzqnnknDxPjyspqUVRcXC08CokS98J1jYobqDg=="],
|
||||
|
||||
"@react-types/numberfield": ["@react-types/numberfield@3.8.15", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-97r92D23GKCOjGIGMeW9nt+/KlfM3GeWH39Czcmd2/D5y3k6z4j0avbsfx2OttCtJszrnENjw3GraYGYI2KosQ=="],
|
||||
|
||||
"@react-types/overlays": ["@react-types/overlays@3.9.2", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw=="],
|
||||
|
||||
"@react-types/progress": ["@react-types/progress@3.5.16", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-I9tSdCFfvQ7gHJtm90VAKgwdTWXQgVNvLRStEc0z9h+bXBxdvZb+QuiRPERChwFQ9VkK4p4rDqaFo69nDqWkpw=="],
|
||||
|
||||
"@react-types/radio": ["@react-types/radio@3.9.2", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-3UcJXu37JrTkRyP4GJPDBU7NmDTInrEdOe+bVzA1j4EegzdkJmLBkLg5cLDAbpiEHB+xIsvbJdx6dxeMuc+H3g=="],
|
||||
|
||||
"@react-types/searchfield": ["@react-types/searchfield@3.6.6", "", { "dependencies": { "@react-types/shared": "^3.32.1", "@react-types/textfield": "^3.12.6" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-cl3itr/fk7wbIQc2Gz5Ie8aVeUmPjVX/mRGS5/EXlmzycAKNYTvqf2mlxwObLndtLISmt7IgNjRRhbUUDI8Ang=="],
|
||||
|
||||
"@react-types/select": ["@react-types/select@3.11.0", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-SzIsMFVPCbXE1Z1TLfpdfiwJ1xnIkcL1/CjGilmUKkNk5uT7rYX1xCJqWCjXI0vAU1xM4Qn+T3n8de4fw6HRBg=="],
|
||||
|
||||
"@react-types/shared": ["@react-types/shared@3.32.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w=="],
|
||||
|
||||
"@react-types/slider": ["@react-types/slider@3.8.2", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-MQYZP76OEOYe7/yA2To+Dl0LNb0cKKnvh5JtvNvDnAvEprn1RuLiay8Oi/rTtXmc2KmBa4VdTcsXsmkbbkeN2Q=="],
|
||||
|
||||
"@react-types/switch": ["@react-types/switch@3.5.15", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-r/ouGWQmIeHyYSP1e5luET+oiR7N7cLrAlWsrAfYRWHxqXOSNQloQnZJ3PLHrKFT02fsrQhx2rHaK2LfKeyN3A=="],
|
||||
|
||||
"@react-types/table": ["@react-types/table@3.13.4", "", { "dependencies": { "@react-types/grid": "^3.3.6", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-I/DYiZQl6aNbMmjk90J9SOhkzVDZvyA3Vn3wMWCiajkMNjvubFhTfda5DDf2SgFP5l0Yh6TGGH5XumRv9LqL5Q=="],
|
||||
|
||||
"@react-types/tabs": ["@react-types/tabs@3.3.19", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-fE+qI43yR5pAMpeqPxGqQq9jDHXEPqXskuxNHERMW0PYMdPyem2Cw6goc5F4qeZO3Hf6uPZgHkvJz2OAq7TbBw=="],
|
||||
|
||||
"@react-types/textfield": ["@react-types/textfield@3.12.6", "", { "dependencies": { "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hpEVKE+M3uUkTjw2WrX1NrH/B3rqDJFUa+ViNK2eVranLY4ZwFqbqaYXSzHupOF3ecSjJJv2C103JrwFvx6TPQ=="],
|
||||
|
||||
"@react-types/tooltip": ["@react-types/tooltip@3.4.21", "", { "dependencies": { "@react-types/overlays": "^3.9.2", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ugGHOZU6WbOdeTdbjnaEc+Ms7/WhsUCg+T3PCOIeOT9FG02Ce189yJ/+hd7oqL/tVwIhEMYJIqSCgSELFox+QA=="],
|
||||
|
||||
"@redis/bloom": ["@redis/bloom@1.2.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg=="],
|
||||
|
||||
"@redis/client": ["@redis/client@1.6.1", "", { "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw=="],
|
||||
@@ -1926,6 +2199,8 @@
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.0.2", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg=="],
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
@@ -1950,6 +2225,8 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
|
||||
@@ -2168,6 +2445,8 @@
|
||||
|
||||
"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-own-enumerable-keys": ["get-own-enumerable-keys@1.0.0", "", {}, "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
@@ -2302,6 +2581,10 @@
|
||||
|
||||
"internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
|
||||
|
||||
"intl-messageformat": ["intl-messageformat@10.7.14", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.2", "@formatjs/fast-memoize": "2.2.6", "@formatjs/icu-messageformat-parser": "2.11.0", "tslib": "2" } }, "sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ=="],
|
||||
|
||||
"invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
|
||||
|
||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
@@ -2476,6 +2759,8 @@
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lowlight": ["lowlight@3.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.9.0" } }, "sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
||||
@@ -2612,7 +2897,7 @@
|
||||
|
||||
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"miniflare": ["miniflare@5.20260811.0-alpha", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.29.0", "workerd": "1.20260811.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-sypXsD5fjY88fZNedPqnwrwR1dwfnfbfW7MfvMyIfPJdtRiCCOpUnjWGeFVYYZ+0fQVICye6Juu+vZgzTEx8XA=="],
|
||||
"miniflare": ["miniflare@4.20260401.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.4", "workerd": "1.20260401.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-lngHPzZFN9sxYG/mhzvnWiBMNVAN5MsO/7g32ttJ07rymtiK/ZBalODTKb8Od+BQdlU5DOR4CjVt9NydjnUyYg=="],
|
||||
|
||||
"minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||
|
||||
@@ -2860,10 +3145,18 @@
|
||||
|
||||
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
||||
|
||||
"react-aria": ["react-aria@3.44.0", "", { "dependencies": { "@internationalized/string": "^3.2.7", "@react-aria/breadcrumbs": "^3.5.29", "@react-aria/button": "^3.14.2", "@react-aria/calendar": "^3.9.2", "@react-aria/checkbox": "^3.16.2", "@react-aria/color": "^3.1.2", "@react-aria/combobox": "^3.14.0", "@react-aria/datepicker": "^3.15.2", "@react-aria/dialog": "^3.5.31", "@react-aria/disclosure": "^3.1.0", "@react-aria/dnd": "^3.11.3", "@react-aria/focus": "^3.21.2", "@react-aria/gridlist": "^3.14.1", "@react-aria/i18n": "^3.12.13", "@react-aria/interactions": "^3.25.6", "@react-aria/label": "^3.7.22", "@react-aria/landmark": "^3.0.7", "@react-aria/link": "^3.8.6", "@react-aria/listbox": "^3.15.0", "@react-aria/menu": "^3.19.3", "@react-aria/meter": "^3.4.27", "@react-aria/numberfield": "^3.12.2", "@react-aria/overlays": "^3.30.0", "@react-aria/progress": "^3.4.27", "@react-aria/radio": "^3.12.2", "@react-aria/searchfield": "^3.8.9", "@react-aria/select": "^3.17.0", "@react-aria/selection": "^3.26.0", "@react-aria/separator": "^3.4.13", "@react-aria/slider": "^3.8.2", "@react-aria/ssr": "^3.9.10", "@react-aria/switch": "^3.7.8", "@react-aria/table": "^3.17.8", "@react-aria/tabs": "^3.10.8", "@react-aria/tag": "^3.7.2", "@react-aria/textfield": "^3.18.2", "@react-aria/toast": "^3.0.8", "@react-aria/tooltip": "^3.8.8", "@react-aria/tree": "^3.1.4", "@react-aria/utils": "^3.31.0", "@react-aria/visually-hidden": "^3.8.28", "@react-types/shared": "^3.32.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-2Pq3GQxBgM4/2BlpKYXeaZ47a3tdIcYSW/AYvKgypE3XipxOdQMDG5Sr/NBn7zuJq+thzmtfRb0lB9bTbsmaRw=="],
|
||||
|
||||
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
||||
|
||||
"react-hotkeys-hook": ["react-hotkeys-hook@4.5.1", "", { "peerDependencies": { "react": ">=16.8.1", "react-dom": ">=16.8.1" } }, "sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg=="],
|
||||
|
||||
"react-remove-scroll": ["react-remove-scroll@2.6.0", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.6", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ=="],
|
||||
|
||||
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.6", "", { "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g=="],
|
||||
|
||||
"react-style-singleton": ["react-style-singleton@2.2.1", "", { "dependencies": { "get-nonce": "^1.0.0", "invariant": "^2.2.4", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g=="],
|
||||
|
||||
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
|
||||
|
||||
"read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="],
|
||||
@@ -3200,6 +3493,10 @@
|
||||
|
||||
"urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="],
|
||||
|
||||
"use-callback-ref": ["use-callback-ref@1.3.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA=="],
|
||||
|
||||
"use-sidecar": ["use-sidecar@1.1.2", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw=="],
|
||||
|
||||
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||
|
||||
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
||||
@@ -3260,9 +3557,9 @@
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"workerd": ["workerd@1.20260811.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260811.1", "@cloudflare/workerd-darwin-arm64": "1.20260811.1", "@cloudflare/workerd-linux-64": "1.20260811.1", "@cloudflare/workerd-linux-arm64": "1.20260811.1", "@cloudflare/workerd-windows-64": "1.20260811.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-kh+FFm55JQ4ssxhHZV9VPdMQq3D1nHxNJgwxMtWGD4dGppJvLySdguTRDKgeNTvgq6heSz+6TTXyPSDGj8Yllw=="],
|
||||
"workerd": ["workerd@1.20260401.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260401.1", "@cloudflare/workerd-darwin-arm64": "1.20260401.1", "@cloudflare/workerd-linux-64": "1.20260401.1", "@cloudflare/workerd-linux-arm64": "1.20260401.1", "@cloudflare/workerd-windows-64": "1.20260401.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-mUYCd+ohaWJWF5nhDzxugWaAD/DM8Dw0ze3B7bu8JaA7S70+XQJXcvcvwE8C4qGcxSdCyqjsrFzqxKubECDwzg=="],
|
||||
|
||||
"wrangler": ["wrangler@4.122.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "5.20260811.0-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260811.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260811.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-qkskzgQ76Y1qvVe5JARgvc3RISq6BC2rPoxQhFoKH1dKIwQc3GDFttQ/7m2OfeQ+tmQRzynv2dy/DXnxFCj2Lw=="],
|
||||
"wrangler": ["wrangler@4.80.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.16.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260401.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260401.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260401.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-2ZKF7uPeOZy65BGk3YfvqBCPo/xH1MrAlMmH9mVP+tCNBrTUMnwOHSj1HrZHgR8LttkAqhko0fGz+I4ax1rzyQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -3272,7 +3569,7 @@
|
||||
|
||||
"write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="],
|
||||
|
||||
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||
|
||||
"xdg-app-paths": ["xdg-app-paths@5.1.0", "", { "dependencies": { "xdg-portable": "^7.0.0" } }, "sha512-RAQ3WkPf4KTU1A8RtFx3gWywzVKe00tfOPFfl2NDGqbIFENQO4kqAJp7mhQjNj/33W5x5hiWWUdyfPq/5SU3QA=="],
|
||||
|
||||
@@ -3482,6 +3779,166 @@
|
||||
|
||||
"@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||
|
||||
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-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-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.4", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.1", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.1", "", { "dependencies": { "@radix-ui/react-slot": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.0", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.0", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg=="],
|
||||
|
||||
"@radix-ui/react-popper/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
|
||||
|
||||
"@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.1", "", { "dependencies": { "@radix-ui/react-slot": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg=="],
|
||||
|
||||
"@react-aria/landmark/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
||||
|
||||
"@react-stately/toast/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
||||
|
||||
"@redis/client/generic-pool": ["generic-pool@3.9.0", "", {}, "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="],
|
||||
|
||||
"@redis/client/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
@@ -3830,9 +4287,7 @@
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"miniflare/sharp": ["sharp@0.35.2", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.2", "@img/sharp-darwin-x64": "0.35.2", "@img/sharp-freebsd-wasm32": "0.35.2", "@img/sharp-libvips-darwin-arm64": "1.3.1", "@img/sharp-libvips-darwin-x64": "1.3.1", "@img/sharp-libvips-linux-arm": "1.3.1", "@img/sharp-libvips-linux-arm64": "1.3.1", "@img/sharp-libvips-linux-ppc64": "1.3.1", "@img/sharp-libvips-linux-riscv64": "1.3.1", "@img/sharp-libvips-linux-s390x": "1.3.1", "@img/sharp-libvips-linux-x64": "1.3.1", "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", "@img/sharp-libvips-linuxmusl-x64": "1.3.1", "@img/sharp-linux-arm": "0.35.2", "@img/sharp-linux-arm64": "0.35.2", "@img/sharp-linux-ppc64": "0.35.2", "@img/sharp-linux-riscv64": "0.35.2", "@img/sharp-linux-s390x": "0.35.2", "@img/sharp-linux-x64": "0.35.2", "@img/sharp-linuxmusl-arm64": "0.35.2", "@img/sharp-linuxmusl-x64": "0.35.2", "@img/sharp-webcontainers-wasm32": "0.35.2", "@img/sharp-win32-arm64": "0.35.2", "@img/sharp-win32-ia32": "0.35.2", "@img/sharp-win32-x64": "0.35.2" } }, "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w=="],
|
||||
|
||||
"miniflare/undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="],
|
||||
"miniflare/undici": ["undici@7.24.4", "", {}, "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w=="],
|
||||
|
||||
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
@@ -3862,6 +4317,10 @@
|
||||
|
||||
"radix-vue/@floating-ui/vue": ["@floating-ui/vue@1.1.5", "", { "dependencies": { "@floating-ui/dom": "^1.0.0", "@floating-ui/utils": "^0.2.8", "vue-demi": ">=0.13.0" } }, "sha512-ynL1p5Z+woPVSwgMGqeDrx6HrJfGIDzFyESFkyqJKilGW1+h/8yVY29Khn0LaU6wHBRwZ13ntG6reiHWK6jyzw=="],
|
||||
|
||||
"radix-vue/@internationalized/date": ["@internationalized/date@3.5.6", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-jLxQjefH9VI5P9UQuqB6qNKnvFt1Ky1TPIzHGsIlCi7sZZoMR8SdYbBGRvM0y+Jtb+ez4ieBzmiAUcpmPYpyOw=="],
|
||||
|
||||
"radix-vue/@internationalized/number": ["@internationalized/number@3.5.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-h9huwWjNqYyE2FXZZewWqmCdkw1HeFds5q4Siuoms3hUQC5iPJK3aBmkFZoDSLN4UD0Bl8G22L/NdHpeOr+/7A=="],
|
||||
|
||||
"radix-vue/@vueuse/core": ["@vueuse/core@10.11.1", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "10.11.1", "@vueuse/shared": "10.11.1", "vue-demi": ">=0.14.8" } }, "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww=="],
|
||||
|
||||
"radix-vue/@vueuse/shared": ["@vueuse/shared@10.11.1", "", { "dependencies": { "vue-demi": ">=0.14.8" } }, "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="],
|
||||
@@ -3870,6 +4329,12 @@
|
||||
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"react-remove-scroll/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
|
||||
|
||||
"react-remove-scroll-bar/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
|
||||
|
||||
"react-style-singleton/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
|
||||
|
||||
"read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||
|
||||
"read-yaml-file/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||
@@ -3926,6 +4391,10 @@
|
||||
|
||||
"unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"use-callback-ref/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
|
||||
|
||||
"use-sidecar/tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
|
||||
|
||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"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=="],
|
||||
@@ -4084,6 +4553,84 @@
|
||||
|
||||
"@playwright/test/playwright/playwright-core": ["playwright-core@1.61.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg=="],
|
||||
|
||||
"@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-collection/@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.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@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-hover-card/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-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-menu/react-remove-scroll/react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
||||
"@radix-ui/react-menu/react-remove-scroll/use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.0", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.0", "", { "dependencies": { "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="],
|
||||
|
||||
"@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom": ["@floating-ui/dom@1.6.11", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.8" } }, "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "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-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.0", "", { "dependencies": { "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||
|
||||
"@scalar/api-client-react/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.5.0", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-HJBcLa+/mPP+3TCcQngj/iW5UqynRosOQdEETXjmdy6Ngw8wBjwIcT6C86J5jufJ6sI8++HYnt+e7pAvp5FO6A=="],
|
||||
|
||||
"@scalar/api-client-react/@scalar/types/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
@@ -4310,62 +4857,6 @@
|
||||
|
||||
"micro/raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"miniflare/sharp/@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.1" }, "os": "darwin", "cpu": "arm64" }, "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.1" }, "os": "darwin", "cpu": "x64" }, "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "os": "freebsd" }, "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.1", "", { "os": "linux", "cpu": "none" }, "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.1" }, "os": "linux", "cpu": "arm" }, "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.1" }, "os": "linux", "cpu": "ppc64" }, "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.1" }, "os": "linux", "cpu": "none" }, "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.1" }, "os": "linux", "cpu": "s390x" }, "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "cpu": "none" }, "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.2", "", { "os": "win32", "cpu": "x64" }, "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ=="],
|
||||
|
||||
"miniflare/sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"miniflare/sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
|
||||
|
||||
"next/postcss/nanoid": ["nanoid@3.3.7", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="],
|
||||
|
||||
"next/postcss/picocolors": ["picocolors@1.1.0", "", {}, "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="],
|
||||
@@ -4452,6 +4943,28 @@
|
||||
|
||||
"@opennextjs/aws/express/serve-static/send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-collection/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-hover-card/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom": ["@floating-ui/dom@1.6.11", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.8" } }, "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom": ["@floating-ui/dom@1.6.11", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.8" } }, "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ=="],
|
||||
|
||||
"@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/core": ["@floating-ui/core@1.6.8", "", { "dependencies": { "@floating-ui/utils": "^0.2.8" } }, "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA=="],
|
||||
|
||||
"@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom": ["@floating-ui/dom@1.6.11", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.8" } }, "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ=="],
|
||||
|
||||
"@scalar/components/@scalar/oas-utils/@scalar/workspace-store/@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.3", "", { "dependencies": { "@scalar/openapi-types": "0.5.0" } }, "sha512-iROhcgy3vge6zsviPtoTLHale0nYt1PLhuMmJweQwJ0U23ZYyYhV5xgHtAd0OBEXuqT6rjYbJFvKOJZmJOwpNQ=="],
|
||||
|
||||
"@scalar/components/@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.5.0", "", { "dependencies": { "zod": "4.1.11" } }, "sha512-HJBcLa+/mPP+3TCcQngj/iW5UqynRosOQdEETXjmdy6Ngw8wBjwIcT6C86J5jufJ6sI8++HYnt+e7pAvp5FO6A=="],
|
||||
@@ -4498,10 +5011,6 @@
|
||||
|
||||
"micro/raw-body/http-errors/toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="],
|
||||
|
||||
"rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
@@ -4520,6 +5029,18 @@
|
||||
|
||||
"@opennextjs/aws/express/serve-static/send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/core": ["@floating-ui/core@1.6.8", "", { "dependencies": { "@floating-ui/utils": "^0.2.8" } }, "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/core": ["@floating-ui/core@1.6.8", "", { "dependencies": { "@floating-ui/utils": "^0.2.8" } }, "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/core": ["@floating-ui/core@1.6.8", "", { "dependencies": { "@floating-ui/utils": "^0.2.8" } }, "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="],
|
||||
|
||||
"@scalar/draggable/vue/@vue/compiler-sfc/@babel/parser/@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
|
||||
|
||||
"@scalar/draggable/vue/@vue/compiler-sfc/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||
@@ -4536,10 +5057,6 @@
|
||||
|
||||
"@zenuml/core/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
||||
|
||||
"miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
||||
|
||||
"rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"@opennextjs/aws/express/body-parser/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
+2
-2
@@ -19,7 +19,8 @@
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"esbuild": "0.27.3",
|
||||
"axios": "1.8.4"
|
||||
"axios": "1.8.4",
|
||||
"@radix-ui/react-slot": "1.2.4"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -47,7 +48,6 @@
|
||||
"catalog": {
|
||||
"@tsconfig/strictest": "^2.0.6",
|
||||
"@tsconfig/node20": "^20.1.6",
|
||||
"@base-ui/react": "^1.7.0",
|
||||
"@gitbook/api": "0.195.0",
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@types/react": "^19.0.0",
|
||||
|
||||
@@ -360,9 +360,7 @@ const testCases: TestsCase[] = [
|
||||
{
|
||||
name: 'docs.verifone.com',
|
||||
contentBaseURL: 'https://docs.verifone.com',
|
||||
// Verifone's custom Cookiebot integration races with and suppresses GitBook's built-in
|
||||
// banner, so waitForCookiesDialog is not a stable invariant here.
|
||||
tests: [{ name: 'Home', url: '/' }],
|
||||
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
},
|
||||
// Deactivate it because of a custom Ask AI that causes flakiness.
|
||||
// {
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
waitForAdminToolbar,
|
||||
waitForCookiesDialog,
|
||||
waitForCoverImages,
|
||||
waitForHydration,
|
||||
waitForNotFound,
|
||||
} from './util';
|
||||
import { VISITOR_TOKEN_COOKIE } from '@/lib/visitors';
|
||||
@@ -136,23 +135,6 @@ const searchTestCases: Test[] = [
|
||||
await expect(page.getByTestId('search-input')).toBeFocused();
|
||||
},
|
||||
},
|
||||
{
|
||||
// `fill()` bypasses key events, so it can't catch a swallowed key. RND-12484.
|
||||
name: 'Search - AI Mode: None - Typing multi-word queries',
|
||||
url: getCustomizationURL({
|
||||
ai: {
|
||||
mode: CustomizationAIMode.None,
|
||||
},
|
||||
}),
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
await waitForCookiesDialog(page);
|
||||
const searchInput = page.getByTestId('search-input');
|
||||
await searchInput.focus();
|
||||
await searchInput.pressSequentially('getting started');
|
||||
await expect(searchInput).toHaveValue('getting started');
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Search - AI Mode: None - URL query (Initial)',
|
||||
url: `${getCustomizationURL({
|
||||
@@ -399,7 +381,6 @@ const testCases: TestsCase[] = [
|
||||
name: 'Customized variant titles are displayed',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -427,7 +408,6 @@ const testCases: TestsCase[] = [
|
||||
name: 'Switch variant with alternate link in metadata',
|
||||
url: 'rfcs',
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -466,7 +446,6 @@ const testCases: TestsCase[] = [
|
||||
url: 'api-multi-versions/reference/api-reference/pets',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -493,7 +472,6 @@ const testCases: TestsCase[] = [
|
||||
url: 'api-multi-versions-share-links/8tNo6MeXg7CkFMzSSz81/reference/api-reference/pets',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -535,7 +513,6 @@ const testCases: TestsCase[] = [
|
||||
return `api-multi-versions-va/reference/api-reference/pets?jwt_token=${token}`;
|
||||
},
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = await page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -571,7 +548,6 @@ const testCases: TestsCase[] = [
|
||||
url: 'ecosystem/connection-provider',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -602,7 +578,6 @@ const testCases: TestsCase[] = [
|
||||
url: 'nl/ecosysteem/connection-provider',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
await waitForHydration(page);
|
||||
const spaceDropdown = page
|
||||
.locator('[data-testid="space-dropdown-button"]')
|
||||
.locator('visible=true');
|
||||
@@ -643,14 +618,14 @@ const testCases: TestsCase[] = [
|
||||
url: '',
|
||||
screenshot: false,
|
||||
run: async (page) => {
|
||||
// The menu only opens on `mouseenter`, which cannot fire again once the pointer
|
||||
// is inside: a hover landing before hydration is lost for good.
|
||||
await waitForHydration(page);
|
||||
const trigger = page
|
||||
.locator('[data-gb-sections]')
|
||||
.getByRole('button', { name: 'Test Section Group 1' });
|
||||
await trigger.hover();
|
||||
await expect(page.getByText('Section B')).toBeVisible();
|
||||
const trigger = page.getByRole('button', { name: 'Test Section Group 1' });
|
||||
// Radix NavigationMenu opens the dropdown on a `pointermove`. A single
|
||||
// synthetic hover can land before hydration and be lost, so re-hover
|
||||
// until the dropdown content actually appears.
|
||||
await expect(async () => {
|
||||
await trigger.hover();
|
||||
await expect(page.getByText('Section B')).toBeVisible({ timeout: 1000 });
|
||||
}).toPass({ timeout: 15000 });
|
||||
await page.getByText('Section B').click();
|
||||
await page.waitForURL((url) => url.pathname.includes('/sections/sections-4'));
|
||||
},
|
||||
@@ -2430,7 +2405,7 @@ const testCases: TestsCase[] = [
|
||||
actions.nth(1).click(),
|
||||
]);
|
||||
// Verify the new page would have opened with the expected URL
|
||||
await expect(newPage).toHaveURL(/gitbook\.com/);
|
||||
expect(newPage.url()).toContain('gitbook.com');
|
||||
// Close it immediately to avoid navigation
|
||||
await newPage.close();
|
||||
|
||||
@@ -2578,7 +2553,7 @@ const testCases: TestsCase[] = [
|
||||
openInNewTabButton.click(),
|
||||
]);
|
||||
// Verify the new page would have opened with the expected URL
|
||||
await expect(newPage).toHaveURL(/gitbook\.gitbook\.io/);
|
||||
expect(newPage.url()).toContain('gitbook.gitbook.io');
|
||||
// Close it immediately to avoid navigation
|
||||
await newPage.close();
|
||||
},
|
||||
|
||||
@@ -173,10 +173,6 @@ export async function waitForCookiesDialog(page: Page) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function waitForHydration(page: Page) {
|
||||
await page.locator('html.hydrated').waitFor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the GitBook admin toolbar to be present.
|
||||
*
|
||||
|
||||
@@ -21,7 +21,11 @@ const allowedDevOrigins =
|
||||
]
|
||||
: undefined;
|
||||
|
||||
let deploymentId = process.env.GITBOOK_HEAD_SHA || process.env.GITHUB_SHA || Date.now().toString(); // Needed because we use a custom deployment method i.e. https://vercel.com/docs/skew-protection#custom-deployment-id
|
||||
// We don't use the deployment ID yet on 2c, we need to remove it because of https://github.com/opennextjs/opennextjs-aws/issues/1136
|
||||
let deploymentId =
|
||||
process.env.GITBOOK_RUNTIME === 'cloudflare'
|
||||
? undefined
|
||||
: process.env.GITBOOK_HEAD_SHA || process.env.GITHUB_SHA || Date.now().toString(); // Needed because we use a custom deployment method i.e. https://vercel.com/docs/skew-protection#custom-deployment-id
|
||||
|
||||
const { VERCEL_TARGET_ENV } = process.env;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ export default {
|
||||
proxyExternalRequest: 'fetch',
|
||||
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
|
||||
incrementalCache: () =>
|
||||
import('./openNext/incrementalCache/cacheWorkerClient').then((m) => m.default),
|
||||
tagCache: () => import('./openNext/tagCache/cacheWorkerClient').then((m) => m.default),
|
||||
import('./openNext/incrementalCache/server').then((m) => m.default),
|
||||
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
@@ -20,8 +20,8 @@ export default {
|
||||
proxyExternalRequest: 'fetch',
|
||||
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
|
||||
incrementalCache: () =>
|
||||
import('./openNext/incrementalCache/cacheWorkerClient').then((m) => m.default),
|
||||
tagCache: () => import('./openNext/tagCache/cacheWorkerClient').then((m) => m.default),
|
||||
import('./openNext/incrementalCache/middleware').then((m) => m.default),
|
||||
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
|
||||
},
|
||||
},
|
||||
dangerous: {
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-server-dev",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-dev",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
@@ -74,10 +70,6 @@
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-server-preview",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-preview",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
@@ -114,10 +106,6 @@
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-server-staging",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-staging",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
@@ -162,10 +150,6 @@
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-server-production",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-production",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// This worker only purposes it to host the different DO that we will need in the other workers.
|
||||
import { DurableObject } from 'cloudflare:workers';
|
||||
|
||||
// `use cache` could cause multiple writes to the same key to happen concurrently, there is a limit of 1 write per key/second
|
||||
// so we need to buffer writes to the R2 bucket to avoid hitting this limit.
|
||||
export class R2WriteBuffer extends DurableObject {
|
||||
writePromise;
|
||||
|
||||
async write(cacheKey, value) {
|
||||
// We are already writing to this key
|
||||
if (this.writePromise) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.writePromise = this.env.NEXT_INC_CACHE_R2_BUCKET.put(cacheKey, value);
|
||||
this.ctx.waitUntil(
|
||||
this.writePromise.finally(() => {
|
||||
this.writePromise = undefined;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { DOQueueHandler } from '../../.open-next/.build/durable-objects/queue.js';
|
||||
|
||||
export { DOShardedTagCache } from '../../.open-next/.build/durable-objects/sharded-tag-cache.js';
|
||||
|
||||
export default {
|
||||
async fetch() {
|
||||
// This worker does not handle any requests, it only provides Durable Objects
|
||||
return new Response('This worker is not meant to handle requests directly', {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,144 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
const runWithCloudflareRequestContext = mock(
|
||||
async <T>(_: Request, __: unknown, ___: unknown, operation: () => Promise<T>) => operation()
|
||||
);
|
||||
const get = mock();
|
||||
const getTagsFromValue = mock();
|
||||
const hasBeenRevalidated = mock();
|
||||
|
||||
mock.module('cloudflare:workers', () => ({
|
||||
DurableObject: class {},
|
||||
WorkerEntrypoint: class {},
|
||||
}));
|
||||
mock.module('../../.open-next/cloudflare/init.js', () => ({ runWithCloudflareRequestContext }));
|
||||
mock.module('../incrementalCache/incrementalCache', () => ({
|
||||
GitbookIncrementalCache: class {
|
||||
get = get;
|
||||
},
|
||||
}));
|
||||
mock.module('@opennextjs/aws/utils/cache.js', () => ({ getTagsFromValue }));
|
||||
mock.module('../tagCache/middleware', () => ({
|
||||
default: { hasBeenRevalidated },
|
||||
}));
|
||||
|
||||
const { default: IncrementalCacheWorker } = await import('./do');
|
||||
|
||||
const CACHE_CONTROL = 'public, s-maxage=3600, stale-while-revalidate=86400';
|
||||
const NO_STORE_CACHE_CONTROL = 'private, no-store, max-age=0, must-revalidate';
|
||||
|
||||
const cacheValue = {
|
||||
type: 'page' as const,
|
||||
html: '<p>cached</p>',
|
||||
json: {},
|
||||
revalidate: 60,
|
||||
};
|
||||
|
||||
describe('IncrementalCacheWorker fetch', () => {
|
||||
const selfFetch = mock();
|
||||
|
||||
beforeEach(() => {
|
||||
selfFetch.mockReset();
|
||||
get.mockReset();
|
||||
getTagsFromValue.mockReset();
|
||||
hasBeenRevalidated.mockReset();
|
||||
getTagsFromValue.mockReturnValue(['space:1']);
|
||||
hasBeenRevalidated.mockResolvedValue(false);
|
||||
});
|
||||
|
||||
const fetch = (request: Request) =>
|
||||
IncrementalCacheWorker.prototype.fetch.call(
|
||||
{
|
||||
env: { WORKER_SELF_REFERENCE: { fetch: selfFetch } },
|
||||
ctx: {},
|
||||
},
|
||||
request
|
||||
);
|
||||
|
||||
it('forwards cache reads to the internal endpoint and restores cache metadata', async () => {
|
||||
selfFetch.mockResolvedValue(
|
||||
Response.json(
|
||||
{ value: cacheValue, lastModified: 123 },
|
||||
{
|
||||
headers: {
|
||||
'x-gitbook-cache-control': CACHE_CONTROL,
|
||||
'x-gitbook-cache-tag': 'incremental-cache:entry,space:1',
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
new Request('https://incremental-cache.internal/?key=entry&cacheType=cache')
|
||||
);
|
||||
|
||||
expect(response.headers.get('cache-control')).toBe(CACHE_CONTROL);
|
||||
expect(response.headers.get('cache-tag')).toBe('incremental-cache:entry,space:1');
|
||||
const forwardedRequest = selfFetch.mock.calls[0]?.[0] as Request;
|
||||
const forwardedURL = new URL(forwardedRequest.url);
|
||||
expect(forwardedURL.pathname).toBe('/internal');
|
||||
expect(forwardedURL.searchParams.get('key')).toBe('entry');
|
||||
expect(forwardedURL.searchParams.get('cacheType')).toBe('cache');
|
||||
});
|
||||
|
||||
it('reads and annotates a cache hit only on the internal endpoint', async () => {
|
||||
get.mockResolvedValue({ value: cacheValue, lastModified: Date.now() });
|
||||
|
||||
const response = await fetch(
|
||||
new Request('https://incremental-cache.internal/internal?key=entry&cacheType=cache')
|
||||
);
|
||||
|
||||
expect(get).toHaveBeenCalledWith('entry', 'cache');
|
||||
expect(selfFetch).not.toHaveBeenCalled();
|
||||
expect(response.headers.get('cache-control')).toBe(CACHE_CONTROL);
|
||||
expect(response.headers.get('x-gitbook-cache-control')).toBe(CACHE_CONTROL);
|
||||
expect(response.headers.get('cache-tag')).toBe('incremental-cache:entry,space:1');
|
||||
expect(response.headers.get('x-gitbook-cache-tag')).toBe('incremental-cache:entry,space:1');
|
||||
});
|
||||
|
||||
it('keeps cache misses out of the worker cache', async () => {
|
||||
get.mockResolvedValue(null);
|
||||
|
||||
const response = await fetch(
|
||||
new Request('https://incremental-cache.internal/internal?key=missing')
|
||||
);
|
||||
|
||||
expect(response.headers.get('cache-control')).toBe(NO_STORE_CACHE_CONTROL);
|
||||
expect(response.headers.get('x-gitbook-cache-control')).toBe(NO_STORE_CACHE_CONTROL);
|
||||
expect(await response.json()).toBeNull();
|
||||
});
|
||||
|
||||
it('keeps revalidated and stale entries out of the worker cache', async () => {
|
||||
get.mockResolvedValueOnce({ value: cacheValue, lastModified: Date.now() });
|
||||
hasBeenRevalidated.mockResolvedValueOnce(true);
|
||||
const revalidatedResponse = await fetch(
|
||||
new Request('https://incremental-cache.internal/internal?key=revalidated')
|
||||
);
|
||||
|
||||
get.mockResolvedValueOnce({
|
||||
value: { ...cacheValue, revalidate: 0 },
|
||||
lastModified: Date.now() - 1_000,
|
||||
});
|
||||
const staleResponse = await fetch(
|
||||
new Request('https://incremental-cache.internal/internal?key=stale')
|
||||
);
|
||||
|
||||
expect(revalidatedResponse.headers.get('cache-control')).toBe(NO_STORE_CACHE_CONTROL);
|
||||
expect(revalidatedResponse.headers.get('x-gitbook-cache-revalidated')).toBe('true');
|
||||
expect(staleResponse.headers.get('cache-control')).toBe(NO_STORE_CACHE_CONTROL);
|
||||
expect(staleResponse.headers.get('x-gitbook-cache-revalidated')).toBe('true');
|
||||
});
|
||||
|
||||
it('rejects invalid internal requests and does not forward non-GET requests', async () => {
|
||||
const invalidResponse = await fetch(
|
||||
new Request('https://incremental-cache.internal/internal?key=entry&cacheType=invalid')
|
||||
);
|
||||
const methodResponse = await fetch(
|
||||
new Request('https://incremental-cache.internal/?key=entry', { method: 'POST' })
|
||||
);
|
||||
|
||||
expect(invalidResponse.status).toBe(400);
|
||||
expect(methodResponse.status).toBe(405);
|
||||
expect(selfFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,199 +0,0 @@
|
||||
import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
|
||||
import type {
|
||||
CacheEntryType,
|
||||
CacheValue,
|
||||
NextModeTagCacheWriteInput,
|
||||
} from '@opennextjs/aws/types/overrides.js';
|
||||
import { getTagsFromValue } from '@opennextjs/aws/utils/cache.js';
|
||||
import { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter';
|
||||
|
||||
// @ts-ignore Generated by the Cloudflare build.
|
||||
import { runWithCloudflareRequestContext } from '../../.open-next/cloudflare/init.js';
|
||||
import { GitbookIncrementalCache } from '../incrementalCache/incrementalCache';
|
||||
import tagCache from '../tagCache/middleware';
|
||||
|
||||
type CacheWorkerEnv = {
|
||||
NEXT_INC_CACHE_R2_BUCKET: {
|
||||
put(key: string, value: string): Promise<unknown>;
|
||||
};
|
||||
WORKER_SELF_REFERENCE: {
|
||||
fetch(request: Request): Promise<Response>;
|
||||
};
|
||||
};
|
||||
|
||||
//@ts-ignore - Just to avoid tag cache crashing
|
||||
globalThis.openNextConfig = {
|
||||
dangerous: {
|
||||
enableCacheInterception: true,
|
||||
},
|
||||
};
|
||||
|
||||
const cacheEntryTypes = new Set<CacheEntryType>(['cache', 'fetch', 'composable']);
|
||||
|
||||
const isCacheEntryType = (value: string | null): value is CacheEntryType =>
|
||||
value !== null && cacheEntryTypes.has(value as CacheEntryType);
|
||||
|
||||
const NO_STORE_CACHE_CONTROL = 'private, no-store, max-age=0, must-revalidate';
|
||||
const INTERNAL_PATH = '/internal';
|
||||
const CACHE_CONTROL_HEADER = 'x-gitbook-cache-control';
|
||||
const CACHE_TAG_HEADER = 'x-gitbook-cache-tag';
|
||||
|
||||
const getCacheHeaders = (cacheControl: string, cacheTag?: string): HeadersInit => ({
|
||||
'Cache-Control': cacheControl,
|
||||
[CACHE_CONTROL_HEADER]: cacheControl,
|
||||
...(cacheTag
|
||||
? {
|
||||
'Cache-Tag': cacheTag,
|
||||
[CACHE_TAG_HEADER]: cacheTag,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
const nullCacheResponse = (hasBeenRevalidated = false): Response =>
|
||||
Response.json(null, {
|
||||
headers: {
|
||||
...getCacheHeaders(NO_STORE_CACHE_CONTROL),
|
||||
...(hasBeenRevalidated ? { 'x-gitbook-cache-revalidated': 'true' } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
const restoreCacheHeaders = (response: Response): Response => {
|
||||
const headers = new Headers(response.headers);
|
||||
const cacheControl = headers.get(CACHE_CONTROL_HEADER);
|
||||
const cacheTag = headers.get(CACHE_TAG_HEADER);
|
||||
|
||||
if (cacheControl) {
|
||||
headers.set('Cache-Control', cacheControl);
|
||||
}
|
||||
if (cacheTag) {
|
||||
headers.set('Cache-Tag', cacheTag);
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
|
||||
const isTimeStale = (value: CacheValue<CacheEntryType>, lastModified?: number): boolean => {
|
||||
const revalidate = value.revalidate;
|
||||
if (typeof revalidate !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Date.now() >= (lastModified ?? Date.now()) + revalidate * 1000;
|
||||
};
|
||||
|
||||
const getTagName = (tag: NextModeTagCacheWriteInput): string =>
|
||||
typeof tag === 'string' ? tag : tag.tag;
|
||||
|
||||
// `use cache` can write the same key concurrently, but R2 accepts one write per key per second.
|
||||
export class R2WriteBuffer extends DurableObject<CacheWorkerEnv> {
|
||||
private writePromise: Promise<unknown> | undefined;
|
||||
|
||||
async write(cacheKey: string, value: string): Promise<void> {
|
||||
if (this.writePromise) {
|
||||
return;
|
||||
}
|
||||
|
||||
const writePromise = this.env.NEXT_INC_CACHE_R2_BUCKET.put(cacheKey, value);
|
||||
this.writePromise = writePromise;
|
||||
this.ctx.waitUntil(
|
||||
writePromise.finally(() => {
|
||||
this.writePromise = undefined;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore Generated by the Cloudflare build.
|
||||
export { DOQueueHandler } from '../../.open-next/.build/durable-objects/queue.js';
|
||||
|
||||
// @ts-ignore Generated by the Cloudflare build.
|
||||
export { DOShardedTagCache } from '../../.open-next/.build/durable-objects/sharded-tag-cache.js';
|
||||
|
||||
export default class IncrementalCacheWorker extends WorkerEntrypoint<CacheWorkerEnv> {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
if (request.method !== 'GET') {
|
||||
return new Response('Method not allowed', { status: 405 });
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname !== INTERNAL_PATH) {
|
||||
url.pathname = INTERNAL_PATH;
|
||||
const response = await this.env.WORKER_SELF_REFERENCE.fetch(new Request(url, request));
|
||||
return restoreCacheHeaders(response);
|
||||
}
|
||||
|
||||
const key = url.searchParams.get('key');
|
||||
const cacheType = url.searchParams.get('cacheType');
|
||||
if (!key || (cacheType !== null && !isCacheEntryType(cacheType))) {
|
||||
return new Response('Invalid cache request', { status: 400 });
|
||||
}
|
||||
|
||||
return runWithCloudflareRequestContext(request, this.env, this.ctx, async () => {
|
||||
const value = await new GitbookIncrementalCache().get(key, cacheType ?? undefined);
|
||||
if (!value?.value) {
|
||||
return nullCacheResponse();
|
||||
}
|
||||
|
||||
const tags = getTagsFromValue(value?.value as CacheValue<'cache'> | undefined);
|
||||
if (await tagCache.hasBeenRevalidated(tags, value.lastModified)) {
|
||||
return nullCacheResponse(true);
|
||||
}
|
||||
|
||||
if (isTimeStale(value.value, value.lastModified)) {
|
||||
return nullCacheResponse(true);
|
||||
}
|
||||
|
||||
return Response.json(value, {
|
||||
headers: getCacheHeaders(
|
||||
// 1 hour cache, with a 1 day stale-while-revalidate.
|
||||
'public, s-maxage=3600, stale-while-revalidate=86400',
|
||||
[`incremental-cache:${key}`, ...tags].join(',')
|
||||
),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async set<CacheType extends CacheEntryType>(
|
||||
key: string,
|
||||
value: CacheValue<CacheType>,
|
||||
cacheType?: CacheType
|
||||
): Promise<void> {
|
||||
await this.#runRpcOperation(async () => {
|
||||
await new GitbookIncrementalCache().set(key, value, cacheType);
|
||||
});
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<void> {
|
||||
await this.#runRpcOperation(async () => {
|
||||
await new GitbookIncrementalCache().delete(key);
|
||||
});
|
||||
}
|
||||
|
||||
async writeTags(tags: NextModeTagCacheWriteInput[]): Promise<void> {
|
||||
const tagsToWrite = tags.filter(softTagFilter);
|
||||
if (tagsToWrite.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.#runRpcOperation(async () => {
|
||||
await tagCache.writeTags(tagsToWrite);
|
||||
await this.ctx.cache?.purge({ tags: tagsToWrite.map(getTagName) });
|
||||
});
|
||||
}
|
||||
|
||||
async #runRpcOperation(operation: () => Promise<void>): Promise<void> {
|
||||
await runWithCloudflareRequestContext(
|
||||
new Request('https://incremental-cache.internal'),
|
||||
this.env,
|
||||
this.ctx,
|
||||
async () => {
|
||||
await operation();
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"main": "do.ts",
|
||||
"main": "do.js",
|
||||
"name": "gitbook-open-v2-do",
|
||||
"compatibility_date": "2026-06-14",
|
||||
"compatibility_date": "2025-04-14",
|
||||
"keep_names": false,
|
||||
"compatibility_flags": [
|
||||
"nodejs_compat",
|
||||
@@ -11,54 +11,7 @@
|
||||
"observability": {
|
||||
"enabled": false,
|
||||
},
|
||||
"cache": {
|
||||
"enabled": true,
|
||||
},
|
||||
"placement": {
|
||||
"region": "gcp:us-central1",
|
||||
},
|
||||
"env": {
|
||||
"dev": {
|
||||
"vars": {
|
||||
"STAGE": "dev",
|
||||
"OPEN_NEXT_BUILD_ID": "local",
|
||||
"NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true",
|
||||
},
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_R2_BUCKET",
|
||||
"bucket_name": "gitbook-open-v2-cache-preview",
|
||||
},
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-do-dev",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"name": "NEXT_CACHE_DO_QUEUE",
|
||||
"class_name": "DOQueueHandler",
|
||||
},
|
||||
{
|
||||
"name": "NEXT_TAG_CACHE_DO_SHARDED",
|
||||
"class_name": "DOShardedTagCache",
|
||||
},
|
||||
{
|
||||
"name": "WRITE_BUFFER",
|
||||
"class_name": "R2WriteBuffer",
|
||||
},
|
||||
],
|
||||
},
|
||||
"migrations": [
|
||||
{
|
||||
"tag": "v1",
|
||||
"new_sqlite_classes": ["DOQueueHandler", "DOShardedTagCache", "R2WriteBuffer"],
|
||||
},
|
||||
],
|
||||
},
|
||||
"preview": {
|
||||
"vars": {
|
||||
"STAGE": "preview",
|
||||
@@ -73,7 +26,7 @@
|
||||
"services": [
|
||||
{
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-do-preview",
|
||||
"service": "gitbook-open-v2-preview",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
@@ -98,16 +51,6 @@
|
||||
"new_sqlite_classes": ["DOQueueHandler", "DOShardedTagCache", "R2WriteBuffer"],
|
||||
},
|
||||
],
|
||||
"observability": {
|
||||
"traces": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
},
|
||||
"logs": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
"staging": {
|
||||
"vars": {
|
||||
@@ -128,7 +71,7 @@
|
||||
"services": [
|
||||
{
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-do-staging",
|
||||
"service": "gitbook-open-v2-staging",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
@@ -177,7 +120,7 @@
|
||||
"services": [
|
||||
{
|
||||
"binding": "WORKER_SELF_REFERENCE",
|
||||
"service": "gitbook-open-v2-do-production",
|
||||
"service": "gitbook-open-v2-production",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
"vars": {
|
||||
"STAGE": "dev",
|
||||
"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",
|
||||
"GITBOOK_URL": "http://localhost:8771",
|
||||
},
|
||||
@@ -41,10 +44,6 @@
|
||||
"binding": "DEFAULT_WORKER",
|
||||
"service": "gitbook-open-v2-server-dev",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-dev",
|
||||
},
|
||||
],
|
||||
},
|
||||
"preview": {
|
||||
@@ -68,10 +67,6 @@
|
||||
"binding": "DEFAULT_WORKER",
|
||||
"service": "gitbook-open-v2-server-preview",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-preview",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
@@ -92,16 +87,6 @@
|
||||
},
|
||||
],
|
||||
},
|
||||
"observability": {
|
||||
"traces": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
},
|
||||
"logs": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
"staging": {
|
||||
"vars": {
|
||||
@@ -134,10 +119,6 @@
|
||||
"binding": "DEFAULT_WORKER",
|
||||
"service": "gitbook-open-v2-server-staging",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-staging",
|
||||
},
|
||||
],
|
||||
"tail_consumers": [
|
||||
{
|
||||
@@ -207,10 +188,6 @@
|
||||
"binding": "DEFAULT_WORKER",
|
||||
"service": "gitbook-open-v2-server-production",
|
||||
},
|
||||
{
|
||||
"binding": "NEXT_INC_CACHE_WORKER",
|
||||
"service": "gitbook-open-v2-do-production",
|
||||
},
|
||||
],
|
||||
"tail_consumers": [
|
||||
{
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
const getCloudflareContext = mock();
|
||||
mock.module('@opennextjs/cloudflare', () => ({ getCloudflareContext }));
|
||||
|
||||
const { GitbookIncrementalCache } = await import('./cacheWorkerClient');
|
||||
|
||||
const cacheValue = {
|
||||
type: 'page' as const,
|
||||
html: '<p>cached</p>',
|
||||
json: {},
|
||||
};
|
||||
|
||||
describe('GitbookIncrementalCache cache worker client', () => {
|
||||
const fetch = mock();
|
||||
const set = mock();
|
||||
const remove = mock();
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
beforeEach(() => {
|
||||
fetch.mockReset();
|
||||
set.mockReset();
|
||||
remove.mockReset();
|
||||
getCloudflareContext.mockReturnValue({
|
||||
env: {
|
||||
NEXT_INC_CACHE_WORKER: { fetch, set, delete: remove },
|
||||
},
|
||||
});
|
||||
console.error = mock();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
|
||||
it('gets cache entries through the service binding', async () => {
|
||||
fetch.mockResolvedValue(
|
||||
Response.json({
|
||||
value: cacheValue,
|
||||
lastModified: 123,
|
||||
})
|
||||
);
|
||||
|
||||
const result = await new GitbookIncrementalCache().get('key with / characters', 'cache');
|
||||
|
||||
expect(result).toEqual({ value: cacheValue, lastModified: 123 });
|
||||
const request = fetch.mock.calls[0]?.[0] as Request;
|
||||
const url = new URL(request.url);
|
||||
expect(url.searchParams.get('key')).toBe('key with / characters');
|
||||
expect(url.searchParams.get('cacheType')).toBe('cache');
|
||||
});
|
||||
|
||||
it('returns null for cache misses and failed reads', async () => {
|
||||
fetch.mockResolvedValue(Response.json(null));
|
||||
expect(await new GitbookIncrementalCache().get('missing')).toBeNull();
|
||||
|
||||
fetch.mockResolvedValueOnce(new Response(null, { status: 503 }));
|
||||
expect(await new GitbookIncrementalCache().get('unavailable-response')).toBeNull();
|
||||
|
||||
fetch.mockRejectedValueOnce(new Error('service unavailable'));
|
||||
expect(await new GitbookIncrementalCache().get('unavailable')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses RPC for writes and deletes', async () => {
|
||||
set.mockResolvedValue(undefined);
|
||||
remove.mockResolvedValue(undefined);
|
||||
const cache = new GitbookIncrementalCache();
|
||||
|
||||
await cache.set('entry', cacheValue, 'cache');
|
||||
await cache.delete('entry');
|
||||
|
||||
expect(set).toHaveBeenCalledWith('entry', cacheValue, 'cache');
|
||||
expect(remove).toHaveBeenCalledWith('entry');
|
||||
});
|
||||
|
||||
it('contains mutation failures', async () => {
|
||||
set.mockRejectedValue(new Error('service unavailable'));
|
||||
remove.mockRejectedValue(new Error('service unavailable'));
|
||||
const cache = new GitbookIncrementalCache();
|
||||
|
||||
await expect(cache.set('entry', cacheValue, 'cache')).resolves.toBeUndefined();
|
||||
await expect(cache.delete('entry')).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,78 +0,0 @@
|
||||
import type {
|
||||
CacheEntryType,
|
||||
CacheValue,
|
||||
IncrementalCache,
|
||||
WithLastModified,
|
||||
} from '@opennextjs/aws/types/overrides.js';
|
||||
import { getCloudflareContext } from '@opennextjs/cloudflare';
|
||||
|
||||
export const BINDING_NAME = 'NEXT_INC_CACHE_WORKER';
|
||||
|
||||
type CacheWorker = {
|
||||
fetch(request: Request): Promise<Response>;
|
||||
set<CacheType extends CacheEntryType>(
|
||||
key: string,
|
||||
value: CacheValue<CacheType>,
|
||||
cacheType?: CacheType
|
||||
): Promise<void>;
|
||||
delete(key: string): Promise<void>;
|
||||
};
|
||||
|
||||
export class GitbookIncrementalCache implements IncrementalCache {
|
||||
name = 'GitbookIncrementalCache';
|
||||
|
||||
async get<CacheType extends CacheEntryType = 'cache'>(
|
||||
key: string,
|
||||
cacheType?: CacheType
|
||||
): Promise<WithLastModified<CacheValue<CacheType>> | null> {
|
||||
try {
|
||||
const url = new URL('https://incremental-cache.internal');
|
||||
url.searchParams.set('key', key);
|
||||
if (cacheType) {
|
||||
url.searchParams.set('cacheType', cacheType);
|
||||
}
|
||||
|
||||
const response = await this.getWorker().fetch(new Request(url));
|
||||
if (!response.ok) {
|
||||
console.error('Failed to get from cache worker', response.status);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (await response.json()) as WithLastModified<CacheValue<CacheType>> | null;
|
||||
} catch (error) {
|
||||
console.error('Failed to get from cache worker', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async set<CacheType extends CacheEntryType = 'cache'>(
|
||||
key: string,
|
||||
value: CacheValue<CacheType>,
|
||||
cacheType?: CacheType
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.getWorker().set(key, value, cacheType);
|
||||
} catch (error) {
|
||||
console.error('Failed to set to cache worker', error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<void> {
|
||||
try {
|
||||
await this.getWorker().delete(key);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete from cache worker', error);
|
||||
}
|
||||
}
|
||||
|
||||
private getWorker(): CacheWorker {
|
||||
const env = getCloudflareContext().env as Record<string, unknown>;
|
||||
const worker = env[BINDING_NAME] as CacheWorker | undefined;
|
||||
if (!worker) {
|
||||
throw new Error(`Missing ${BINDING_NAME} service binding`);
|
||||
}
|
||||
return worker;
|
||||
}
|
||||
}
|
||||
|
||||
export default new GitbookIncrementalCache();
|
||||
@@ -1,51 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'bun:test';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
import { DEFAULT_PREFIX, GitbookIncrementalCache } from './incrementalCache';
|
||||
|
||||
const environmentKeys = ['OPEN_NEXT_BUILD_ID', 'DEPLOYMENT_ID', 'NEXT_BUILD_ID'] as const;
|
||||
const originalEnvironment = Object.fromEntries(
|
||||
environmentKeys.map((key) => [key, process.env[key]])
|
||||
);
|
||||
|
||||
const hash = (key: string) => createHash('sha256').update(key).digest('hex');
|
||||
|
||||
afterEach(() => {
|
||||
for (const key of environmentKeys) {
|
||||
const value = originalEnvironment[key];
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('GitbookIncrementalCache cache keys', () => {
|
||||
it('uses the deployment-aware OpenNext build ID', () => {
|
||||
process.env.OPEN_NEXT_BUILD_ID = 'deployment-id';
|
||||
process.env.NEXT_BUILD_ID = 'legacy-build-id';
|
||||
|
||||
expect(new GitbookIncrementalCache().getR2Key('entry')).toBe(
|
||||
`${DEFAULT_PREFIX}/deployment-id/${hash('entry')}.cache`
|
||||
);
|
||||
});
|
||||
|
||||
it('uses the Cloudflare deployment ID when the worker build ID is unavailable', () => {
|
||||
delete process.env.OPEN_NEXT_BUILD_ID;
|
||||
process.env.DEPLOYMENT_ID = 'runtime-deployment-id';
|
||||
|
||||
expect(new GitbookIncrementalCache().getR2Key('entry')).toBe(
|
||||
`${DEFAULT_PREFIX}/runtime-deployment-id/${hash('entry')}.cache`
|
||||
);
|
||||
});
|
||||
|
||||
it('normalizes composable cache keys before applying the deployment namespace', () => {
|
||||
process.env.OPEN_NEXT_BUILD_ID = 'deployment-id';
|
||||
const key = JSON.stringify(['next-build-id', 'cache-key']);
|
||||
|
||||
expect(new GitbookIncrementalCache().getR2Key(key, 'composable')).toBe(
|
||||
`${DEFAULT_PREFIX}/dataCache/${hash(JSON.stringify(['cache-key']))}.composable`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -128,8 +128,7 @@ export class GitbookIncrementalCache implements IncrementalCache {
|
||||
}
|
||||
|
||||
const hash = createHash('sha256').update(key).digest('hex');
|
||||
const buildId = process.env.OPEN_NEXT_BUILD_ID ?? process.env.DEPLOYMENT_ID;
|
||||
return `${DEFAULT_PREFIX}/${cacheType === 'cache' ? buildId : 'dataCache'}/${hash}.${cacheType}`.replace(
|
||||
return `${DEFAULT_PREFIX}/${cacheType === 'cache' ? process.env?.NEXT_BUILD_ID : 'dataCache'}/${hash}.${cacheType}`.replace(
|
||||
/\/+/g,
|
||||
'/'
|
||||
);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
|
||||
import { GitbookIncrementalCache } from './cacheWorkerClient';
|
||||
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
|
||||
|
||||
// export default withRegionalCache(new GitbookIncrementalCache(), {
|
||||
// mode: 'long-lived',
|
||||
// // We can do it because we use our own logic to invalidate the cache
|
||||
// bypassTagCacheOnCacheHit: true,
|
||||
// //TODO: bump it again once I figured out the race condition
|
||||
// defaultLongLivedTtlSec: 5 * 60, // 5 minutes
|
||||
// // We don't want to update the cache entry on every cache hit
|
||||
// shouldLazilyUpdateOnCacheHit: false,
|
||||
// });
|
||||
import { GitbookIncrementalCache } from './incrementalCache';
|
||||
|
||||
export default new GitbookIncrementalCache();
|
||||
export default withRegionalCache(new GitbookIncrementalCache(), {
|
||||
mode: 'long-lived',
|
||||
// We can do it because we use our own logic to invalidate the cache
|
||||
bypassTagCacheOnCacheHit: true,
|
||||
//TODO: bump it again once I figured out the race condition
|
||||
defaultLongLivedTtlSec: 5 * 60, // 5 minutes
|
||||
// We don't want to update the cache entry on every cache hit
|
||||
shouldLazilyUpdateOnCacheHit: false,
|
||||
});
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
// import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
|
||||
import { GitbookIncrementalCache } from './cacheWorkerClient';
|
||||
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
|
||||
|
||||
// // We cannot have regional cache only in the middleware, otherwise it will override things on cache miss
|
||||
// // and cause race conditions. This will be fixed in a future release of OpenNext
|
||||
// export default withRegionalCache(new GitbookIncrementalCache(), {
|
||||
// mode: 'long-lived',
|
||||
// // Because of a race condition, the middleware may have populated the cache entry before `cache.match` had time to run on the server.
|
||||
// // TODO: We should bypass the incremental cache entirely when the interceptor has caught the request. Should be done in OpenNext.
|
||||
// bypassTagCacheOnCacheHit: false,
|
||||
// //TODO: remove, reducing cache ttl of regional cache to help debugging
|
||||
// defaultLongLivedTtlSec: 5 * 60 /* 5 minutes */,
|
||||
// // We don't want to update the cache entry on every cache hit
|
||||
// shouldLazilyUpdateOnCacheHit: false,
|
||||
// });
|
||||
import { GitbookIncrementalCache } from './incrementalCache';
|
||||
|
||||
export default new GitbookIncrementalCache();
|
||||
// We cannot have regional cache only in the middleware, otherwise it will override things on cache miss
|
||||
// and cause race conditions. This will be fixed in a future release of OpenNext
|
||||
export default withRegionalCache(new GitbookIncrementalCache(), {
|
||||
mode: 'long-lived',
|
||||
// Because of a race condition, the middleware may have populated the cache entry before `cache.match` had time to run on the server.
|
||||
// TODO: We should bypass the incremental cache entirely when the interceptor has caught the request. Should be done in OpenNext.
|
||||
bypassTagCacheOnCacheHit: false,
|
||||
//TODO: remove, reducing cache ttl of regional cache to help debugging
|
||||
defaultLongLivedTtlSec: 5 * 60 /* 5 minutes */,
|
||||
// We don't want to update the cache entry on every cache hit
|
||||
shouldLazilyUpdateOnCacheHit: false,
|
||||
});
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
const getCloudflareContext = mock();
|
||||
mock.module('@opennextjs/cloudflare', () => ({ getCloudflareContext }));
|
||||
|
||||
const { default: tagCache } = await import('./cacheWorkerClient');
|
||||
|
||||
describe('GitbookTagCache cache worker client', () => {
|
||||
const writeTags = mock();
|
||||
|
||||
beforeEach(() => {
|
||||
writeTags.mockReset();
|
||||
getCloudflareContext.mockReturnValue({
|
||||
env: {
|
||||
NEXT_INC_CACHE_WORKER: { writeTags },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
getCloudflareContext.mockReset();
|
||||
});
|
||||
|
||||
it('writes hard tags through the cache worker service binding', async () => {
|
||||
writeTags.mockResolvedValue(undefined);
|
||||
|
||||
await tagCache.writeTags([
|
||||
'content',
|
||||
{ tag: 'with-duration', stale: 100, expire: 200 },
|
||||
'_N_T_/soft-tag',
|
||||
]);
|
||||
|
||||
expect(writeTags).toHaveBeenCalledWith([
|
||||
'content',
|
||||
{ tag: 'with-duration', stale: 100, expire: 200 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not call the worker when all tags are soft tags', async () => {
|
||||
await tagCache.writeTags(['_N_T_/soft-tag']);
|
||||
|
||||
expect(writeTags).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { NextModeTagCache, NextModeTagCacheWriteInput } from '@opennextjs/aws/types/overrides';
|
||||
import { getCloudflareContext } from '@opennextjs/cloudflare';
|
||||
import { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter';
|
||||
|
||||
const BINDING_NAME = 'NEXT_INC_CACHE_WORKER';
|
||||
|
||||
type CacheWorker = {
|
||||
writeTags(tags: NextModeTagCacheWriteInput[]): Promise<void>;
|
||||
};
|
||||
|
||||
const getWorker = (): CacheWorker => {
|
||||
const env = getCloudflareContext().env as Record<string, unknown>;
|
||||
const worker = env[BINDING_NAME] as CacheWorker | undefined;
|
||||
if (!worker) {
|
||||
throw new Error(`Missing ${BINDING_NAME} service binding`);
|
||||
}
|
||||
return worker;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'GitbookTagCache',
|
||||
mode: 'nextMode',
|
||||
// Do nothing.
|
||||
getLastRevalidated: async () => {
|
||||
return 0;
|
||||
},
|
||||
// Return false, everything handled at the incremental cache level in the do worker.
|
||||
hasBeenRevalidated: async () => {
|
||||
return false;
|
||||
},
|
||||
writeTags: async (tags: NextModeTagCacheWriteInput[]) => {
|
||||
const tagsToWrite = tags.filter(softTagFilter);
|
||||
if (tagsToWrite.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await getWorker().writeTags(tagsToWrite);
|
||||
},
|
||||
} satisfies NextModeTagCache;
|
||||
@@ -1,7 +1,4 @@
|
||||
import type {
|
||||
NextModeTagCache,
|
||||
NextModeTagCacheWriteInput,
|
||||
} 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 { softTagFilter } from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter';
|
||||
|
||||
@@ -51,7 +48,7 @@ export default {
|
||||
return false; // In case of error, return false
|
||||
}
|
||||
},
|
||||
writeTags: async (tags: NextModeTagCacheWriteInput[]) => {
|
||||
writeTags: async (tags: string[]) => {
|
||||
const tagsToWrite = tags.filter(softTagFilter);
|
||||
if (tagsToWrite.length === 0) {
|
||||
const logger = getLogger().subLogger('gitbookTagCache');
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"version": "0.27.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@base-ui/react": "catalog:",
|
||||
"@cloudflare/workers-types": "^5.20260716.1",
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
"@gitbook/api": "catalog:",
|
||||
"@gitbook/browser-types": "workspace:*",
|
||||
"@gitbook/cache-tags": "workspace:*",
|
||||
@@ -23,6 +22,13 @@
|
||||
"@opennextjs/aws": "4.1.0",
|
||||
"@opennextjs/cloudflare": "1.20.2",
|
||||
"@panzoom/panzoom": "^4.6.1",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.3",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@sindresorhus/fnv1a": "^3.1.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tusbar/cache-control": "^1.0.2",
|
||||
@@ -61,6 +67,7 @@
|
||||
"p-retry": "^8.0.0",
|
||||
"quick-lru": "^7.0.1",
|
||||
"react": "19.2.4",
|
||||
"react-aria": "^3.44.0",
|
||||
"react-dom": "19.2.4",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
@@ -113,7 +120,7 @@
|
||||
"ts-essentials": "^10.0.1",
|
||||
"typescript": "catalog:",
|
||||
"vercel": "50.37.3",
|
||||
"wrangler": "^4.122.0",
|
||||
"wrangler": "^4.79.0",
|
||||
"rss-parser": "^3.13.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -124,14 +131,12 @@
|
||||
"build": "bun run generate:assets && next build --webpack",
|
||||
"build:local": "bun run generate:assets && GITBOOK_URL=http://localhost:3000 next build --webpack",
|
||||
"check:css-browser-compatibility": "bun scripts/check-css-browser-compatibility.ts",
|
||||
"check:css-browser-compatibility:local": "bun run check:css-browser-compatibility --local origin/main",
|
||||
"start": "GITBOOK_URL=http://localhost:3000 next start",
|
||||
"build:cloudflare": "bun run generate:assets && GITBOOK_RUNTIME=cloudflare opennextjs-cloudflare build",
|
||||
"dev:cloudflare": "wrangler dev --port 8771 --env preview",
|
||||
"dev:cf:middleware": "wrangler dev --port 8771 --inspector-port 9230 --env dev --config ./openNext/customWorkers/middlewareWrangler.jsonc",
|
||||
"dev:cf:server": "wrangler dev --port 8772 --env dev --config ./openNext/customWorkers/defaultWrangler.jsonc",
|
||||
"profile:cf:memory": "bun run build:cloudflare && bun ./scripts/profile-opennext-memory.ts",
|
||||
"dev:cf:cache": "wrangler dev --env dev --config ./openNext/customWorkers/doWrangler.jsonc",
|
||||
"e2e": "playwright test e2e/internal.spec.ts e2e/cookie-banner.spec.ts e2e/pdf.spec.ts e2e/select.spec.ts --project=chromium",
|
||||
"e2e-customers": "playwright test e2e/customers.spec.ts --project=chromium",
|
||||
"e2e-style-perf": "playwright test e2e/style-perf.spec.ts --project=chromium --reporter=list",
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import {
|
||||
type CompatibilityDiagnostic,
|
||||
type IssueCommentClient,
|
||||
formatCompatibilityComment,
|
||||
getCompatibilityDiagnostics,
|
||||
upsertCompatibilityComment,
|
||||
} from '../src/lib/cssBrowserCompatibility';
|
||||
|
||||
interface PullRequestEvent {
|
||||
@@ -32,6 +33,12 @@ interface GitBlobResponse {
|
||||
encoding: string;
|
||||
}
|
||||
|
||||
interface IssueComment {
|
||||
body: string;
|
||||
id: number;
|
||||
user: { login: string } | null;
|
||||
}
|
||||
|
||||
class GitHubRequestError extends Error {
|
||||
constructor(
|
||||
readonly status: number,
|
||||
@@ -41,7 +48,7 @@ class GitHubRequestError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
class GitHubApi {
|
||||
class GitHubApi implements IssueCommentClient {
|
||||
constructor(
|
||||
private readonly repository: string,
|
||||
private readonly token: string
|
||||
@@ -107,6 +114,34 @@ class GitHubApi {
|
||||
async getFileAtRef(path: string, ref: string): Promise<string> {
|
||||
return this.getContent(path, ref);
|
||||
}
|
||||
|
||||
async listIssueComments(issueNumber: number): Promise<IssueComment[]> {
|
||||
const comments: IssueComment[] = [];
|
||||
|
||||
for (let page = 1; ; page += 1) {
|
||||
const result = await this.request<IssueComment[]>(
|
||||
`/repos/${this.repository}/issues/${issueNumber}/comments?per_page=100&page=${page}`
|
||||
);
|
||||
comments.push(...result);
|
||||
if (result.length < 100) {
|
||||
return comments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createIssueComment(issueNumber: number, body: string): Promise<void> {
|
||||
await this.request(`/repos/${this.repository}/issues/${issueNumber}/comments`, {
|
||||
body: JSON.stringify({ body }),
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
async updateIssueComment(commentId: number, body: string): Promise<void> {
|
||||
await this.request(`/repos/${this.repository}/issues/comments/${commentId}`, {
|
||||
body: JSON.stringify({ body }),
|
||||
method: 'PATCH',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function getBrowserslist(api: GitHubApi, headSha: string): Promise<string[]> {
|
||||
@@ -140,83 +175,6 @@ async function getBaseContent(
|
||||
}
|
||||
}
|
||||
|
||||
function report(diagnostics: CompatibilityDiagnostic[]): boolean {
|
||||
if (diagnostics.length === 0) {
|
||||
console.log('CSS browser compatibility check passed.');
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`${diagnostics.length} newly added CSS declaration(s) are not fully supported by the configured Browserslist targets:`
|
||||
);
|
||||
for (const diagnostic of diagnostics) {
|
||||
// Workflow command so the failure is annotated on the PR diff.
|
||||
console.error(
|
||||
`::error file=${diagnostic.file},line=${diagnostic.line},col=${diagnostic.column}::${diagnostic.property} is not supported by ${diagnostic.unsupportedBrowsers}`
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Same check as CI, but against a local `git diff` instead of the GitHub API. */
|
||||
async function runLocal(baseRef: string): Promise<boolean> {
|
||||
const git = (...args: string[]) =>
|
||||
execFileSync('git', args, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
||||
const root = git('rev-parse', '--show-toplevel').trim();
|
||||
const mergeBase = git('merge-base', baseRef, 'HEAD').trim();
|
||||
const browsers = (
|
||||
JSON.parse(await readFile(join(root, 'packages/gitbook/package.json'), 'utf8')) as {
|
||||
browserslist?: string[];
|
||||
}
|
||||
).browserslist;
|
||||
|
||||
if (!browsers?.length) {
|
||||
throw new Error('packages/gitbook/package.json must define a Browserslist configuration.');
|
||||
}
|
||||
|
||||
// Working tree, so uncommitted changes are checked too.
|
||||
const changes = git(
|
||||
'diff',
|
||||
'--name-status',
|
||||
'--find-renames',
|
||||
'--diff-filter=ACMR',
|
||||
mergeBase,
|
||||
'--',
|
||||
'*.css'
|
||||
)
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const [status, ...paths] = line.split('\t');
|
||||
const previousPath = paths.length > 1 ? paths[0] : undefined;
|
||||
const path = paths.at(-1) as string;
|
||||
return { added: status?.startsWith('A'), path, previousPath };
|
||||
});
|
||||
|
||||
const diagnostics: CompatibilityDiagnostic[] = [];
|
||||
for (const change of changes) {
|
||||
let base = '';
|
||||
if (!change.added) {
|
||||
try {
|
||||
base = git('show', `${mergeBase}:${change.previousPath ?? change.path}`);
|
||||
} catch {
|
||||
base = '';
|
||||
}
|
||||
}
|
||||
diagnostics.push(
|
||||
...(await getCompatibilityDiagnostics({
|
||||
base,
|
||||
browsers,
|
||||
file: change.path,
|
||||
head: await readFile(join(root, change.path), 'utf8'),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`Checked ${changes.length} changed CSS file(s) against ${baseRef}.`);
|
||||
return report(diagnostics);
|
||||
}
|
||||
|
||||
async function run(): Promise<boolean> {
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repository = process.env.GITHUB_REPOSITORY;
|
||||
@@ -254,19 +212,34 @@ async function run(): Promise<boolean> {
|
||||
);
|
||||
}
|
||||
|
||||
return report(diagnostics);
|
||||
const comment = formatCompatibilityComment({
|
||||
diagnostics,
|
||||
headSha: pullRequest.head.sha,
|
||||
repository,
|
||||
});
|
||||
await upsertCompatibilityComment({
|
||||
body: comment,
|
||||
createIfMissing: diagnostics.length > 0,
|
||||
client: api,
|
||||
issueNumber: pullRequest.number,
|
||||
});
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
console.log('CSS browser compatibility check passed.');
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error('Unsupported CSS declarations found:');
|
||||
for (const diagnostic of diagnostics) {
|
||||
console.error(
|
||||
`${diagnostic.file}:${diagnostic.line} ${diagnostic.property} — ${diagnostic.unsupportedBrowsers}`
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const localFlagIndex = process.argv.indexOf('--local');
|
||||
|
||||
try {
|
||||
let success: boolean;
|
||||
if (localFlagIndex === -1) {
|
||||
success = await run();
|
||||
} else {
|
||||
success = await runLocal(process.argv[localFlagIndex + 1] ?? 'origin/main');
|
||||
}
|
||||
process.exitCode = success ? 0 : 1;
|
||||
process.exitCode = (await run()) ? 0 : 1;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
/**
|
||||
* Usage:
|
||||
* bun packages/gitbook/scripts/test-cache-revalidation.ts benchmark
|
||||
* bun packages/gitbook/scripts/test-cache-revalidation.ts revalidate
|
||||
*/
|
||||
|
||||
import { createHmac } from 'node:crypto';
|
||||
|
||||
// Edit this section for the local site under test. `bun dev` exposes published
|
||||
// sites at /url/<published-site-host>/<path>.
|
||||
const PAGE_URLS = [
|
||||
'http://localhost:3000/url/example.gitbook.io/docs',
|
||||
'http://localhost:3000/url/example.gitbook.io/docs/getting-started',
|
||||
];
|
||||
|
||||
// The revalidation route must use the same /url/<published-site-host>/ prefix.
|
||||
// Leave REVALIDATE_SECRET empty when the deployment does not set GITBOOK_SECRET.
|
||||
const REVALIDATE_URL = 'http://localhost:3000/url/example.gitbook.io/~gitbook/revalidate';
|
||||
const REVALIDATE_SECRET = '';
|
||||
const REVALIDATE_BODY = '{"tags":["space:YOUR_SPACE_ID"]}';
|
||||
|
||||
// Next sets x-nextjs-cache to HIT or MISS. Change this when testing through a
|
||||
// proxy that exposes the result under a different header.
|
||||
const CACHE_STATUS_HEADER = 'x-nextjs-cache';
|
||||
const BENCHMARK_RUNS = 3;
|
||||
const WARMUP_REQUESTS = 2;
|
||||
const WAIT_AFTER_REVALIDATION_MS = 1_000;
|
||||
|
||||
type RequestResult = {
|
||||
cacheStatus: string | null;
|
||||
status: number;
|
||||
totalMs: number;
|
||||
ttfbMs: number;
|
||||
url: string;
|
||||
};
|
||||
|
||||
function printTableHeader() {
|
||||
console.log(
|
||||
`${'request'.padEnd(13)} ${'HTTP'.padEnd(4)} ${CACHE_STATUS_HEADER.padEnd(16)} ${'TTFB / total'.padEnd(22)} URL`
|
||||
);
|
||||
}
|
||||
|
||||
function printResult(label: string, result: RequestResult) {
|
||||
const cacheStatus = result.cacheStatus ?? '<missing>';
|
||||
const timings = `${result.ttfbMs.toFixed(1)} / ${result.totalMs.toFixed(1)} ms`;
|
||||
console.log(
|
||||
`${label.padEnd(13)} ${String(result.status).padEnd(4)} ${cacheStatus.padEnd(16)} ${timings.padEnd(22)} ${result.url}`
|
||||
);
|
||||
}
|
||||
|
||||
async function requestPage(url: string): Promise<RequestResult> {
|
||||
const start = performance.now();
|
||||
const response = await fetch(url);
|
||||
const ttfbMs = performance.now() - start;
|
||||
|
||||
// Consume the response body so totalMs measures the entire transfer.
|
||||
await response.arrayBuffer();
|
||||
|
||||
return {
|
||||
cacheStatus: response.headers.get(CACHE_STATUS_HEADER),
|
||||
status: response.status,
|
||||
totalMs: performance.now() - start,
|
||||
ttfbMs,
|
||||
url: response.url,
|
||||
};
|
||||
}
|
||||
|
||||
function isSuccessful(result: RequestResult) {
|
||||
return result.status >= 200 && result.status < 400;
|
||||
}
|
||||
|
||||
async function benchmark(): Promise<number> {
|
||||
let failures = 0;
|
||||
printTableHeader();
|
||||
|
||||
for (let run = 1; run <= BENCHMARK_RUNS; run += 1) {
|
||||
for (const url of PAGE_URLS) {
|
||||
try {
|
||||
const result = await requestPage(url);
|
||||
printResult(`benchmark-${run}`, result);
|
||||
if (!isSuccessful(result)) {
|
||||
failures += 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`benchmark-${run} ERR ${url}: ${String(error)}`);
|
||||
failures += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
function validateRevalidationConfig() {
|
||||
if (
|
||||
REVALIDATE_URL.includes('example.gitbook.io') ||
|
||||
REVALIDATE_BODY.includes('YOUR_') ||
|
||||
PAGE_URLS.some((url) => url.includes('example.gitbook.io'))
|
||||
) {
|
||||
throw new Error(
|
||||
'Set PAGE_URLS, REVALIDATE_URL, and REVALIDATE_BODY before revalidate mode.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createSignature() {
|
||||
if (!REVALIDATE_SECRET) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return createHmac('sha256', REVALIDATE_SECRET).update(REVALIDATE_BODY).digest('hex');
|
||||
}
|
||||
|
||||
async function revalidate(): Promise<number> {
|
||||
validateRevalidationConfig();
|
||||
let failures = 0;
|
||||
|
||||
console.log(`Warming each page (${WARMUP_REQUESTS} request(s) each)...`);
|
||||
printTableHeader();
|
||||
for (let run = 1; run <= WARMUP_REQUESTS; run += 1) {
|
||||
for (const url of PAGE_URLS) {
|
||||
try {
|
||||
const result = await requestPage(url);
|
||||
printResult(`warmup-${run}`, result);
|
||||
if (!isSuccessful(result)) {
|
||||
failures += 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`warmup-${run} ERR ${url}: ${String(error)}`);
|
||||
failures += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const headers = new Headers({ 'content-type': 'application/json' });
|
||||
const signature = createSignature();
|
||||
if (signature) {
|
||||
headers.set('x-gitbook-signature', signature);
|
||||
}
|
||||
|
||||
console.log(`Revalidating ${REVALIDATE_BODY}...`);
|
||||
const revalidationResponse = await fetch(REVALIDATE_URL, {
|
||||
body: REVALIDATE_BODY,
|
||||
headers,
|
||||
method: 'POST',
|
||||
});
|
||||
const revalidationResponseBody = await revalidationResponse.text();
|
||||
if (!revalidationResponse.ok) {
|
||||
throw new Error(
|
||||
`Revalidation failed with ${revalidationResponse.status}: ${revalidationResponseBody}`
|
||||
);
|
||||
}
|
||||
console.log(revalidationResponseBody);
|
||||
|
||||
await Bun.sleep(WAIT_AFTER_REVALIDATION_MS);
|
||||
|
||||
console.log('Checking that the next response is a MISS...');
|
||||
printTableHeader();
|
||||
for (const url of PAGE_URLS) {
|
||||
try {
|
||||
const result = await requestPage(url);
|
||||
printResult('revalidated', result);
|
||||
if (!isSuccessful(result) || result.cacheStatus?.toUpperCase() !== 'MISS') {
|
||||
failures += 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`revalidated ERR ${url}: ${String(error)}`);
|
||||
failures += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const mode = process.argv[2] ?? 'benchmark';
|
||||
let failures: number;
|
||||
|
||||
try {
|
||||
switch (mode) {
|
||||
case 'benchmark':
|
||||
failures = await benchmark();
|
||||
break;
|
||||
case 'revalidate':
|
||||
failures = await revalidate();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Usage: bun ${process.argv[1]} {benchmark|revalidate}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(String(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (failures > 0) {
|
||||
console.error(`${failures} request(s) failed.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main().then((exitCode) => {
|
||||
process.exitCode = exitCode;
|
||||
});
|
||||
+15
-6
@@ -2,7 +2,6 @@ import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { type RouteLayoutParams, getDynamicSiteContext } from '@/app/utils';
|
||||
import { resolveUpstreamAuthURL } from '@/lib/site-auth-urls';
|
||||
|
||||
/**
|
||||
* Redirect to the upstream auth provider login URL of site, or to the site root when not configured.
|
||||
@@ -13,10 +12,20 @@ export async function GET(
|
||||
) {
|
||||
const { context } = await getDynamicSiteContext(await params);
|
||||
const noLoginFallbackURL = context.linker.toAbsoluteURL(context.linker.toPathInSite(''));
|
||||
const loginURL = resolveUpstreamAuthURL({
|
||||
siteAuthURL: context.site.urls.login,
|
||||
location: request.nextUrl.searchParams.get('location'),
|
||||
});
|
||||
|
||||
return NextResponse.redirect(loginURL ?? noLoginFallbackURL);
|
||||
if (!context.site.urls.login) {
|
||||
return NextResponse.redirect(noLoginFallbackURL);
|
||||
}
|
||||
|
||||
try {
|
||||
const loginURL = new URL(context.site.urls.login);
|
||||
const location = request.nextUrl.searchParams.get('location');
|
||||
if (location) {
|
||||
loginURL.searchParams.set('location', location);
|
||||
}
|
||||
|
||||
return NextResponse.redirect(loginURL);
|
||||
} catch (_error) {
|
||||
return NextResponse.redirect(noLoginFallbackURL);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-13
@@ -8,12 +8,10 @@ import {
|
||||
getSiteURLDataFromParams,
|
||||
} from '@/app/utils';
|
||||
import { getVisitorAuthBasePath } from '@/lib/data';
|
||||
import { resolveUpstreamAuthURL } from '@/lib/site-auth-urls';
|
||||
import { getVisitorAuthCookieName } from '@/lib/visitors';
|
||||
|
||||
/**
|
||||
* Clear the site-scoped auth session cookies and redirect to the upstream auth provider
|
||||
* logout URL of the site, or to the site root when not configured.
|
||||
* Clear the site-scoped auth session cookies and redirect to the site root.
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
@@ -32,14 +30,8 @@ export async function GET(
|
||||
)
|
||||
);
|
||||
|
||||
const noLogoutFallbackURL = context.linker.toAbsoluteURL(context.linker.toPathInSite(''));
|
||||
const logoutURL = resolveUpstreamAuthURL({
|
||||
siteAuthURL: context.site.urls.logout,
|
||||
// Default the location to the site root, so the upstream logout can send the visitor back
|
||||
// to the site. On a site behind visitor auth, coming back re-enters the login flow and
|
||||
// surfaces the upstream login page, as the visitor no longer has a session on either side.
|
||||
location: request.nextUrl.searchParams.get('location') ?? '/',
|
||||
});
|
||||
|
||||
return NextResponse.redirect(logoutURL ?? noLogoutFallbackURL);
|
||||
// TODO: Redirect to the site root for now. Once the API supports it,
|
||||
// optionally redirect to a logoutURL (e.g when needing to logout from upstream auth too)
|
||||
// when defined in visitor auth settings.
|
||||
return NextResponse.redirect(context.linker.toAbsoluteURL(context.linker.toPathInSite('')));
|
||||
}
|
||||
|
||||
+4
-6
@@ -1,12 +1,13 @@
|
||||
import { headers } from 'next/headers';
|
||||
|
||||
import { type RouteLayoutParams, getSiteURLDataFromParams } from '@/app/utils';
|
||||
import type { RouteLayoutParams } from '@/app/utils';
|
||||
import {
|
||||
EmbeddableRootLayout,
|
||||
generateEmbeddableMetadata,
|
||||
generateEmbeddableViewport,
|
||||
} from '@/components/Embeddable';
|
||||
import { getEmbeddableStaticContext } from '@/lib/embeddable';
|
||||
import { getThemeFromMiddleware } from '@/lib/middleware';
|
||||
import { shouldTrackEvents } from '@/lib/tracking';
|
||||
|
||||
interface SiteStaticLayoutProps {
|
||||
@@ -17,12 +18,9 @@ export default async function RootLayout({
|
||||
params,
|
||||
children,
|
||||
}: React.PropsWithChildren<SiteStaticLayoutProps>) {
|
||||
const resolvedParams = await params;
|
||||
const { context, visitorAuthClaims } = await getEmbeddableStaticContext(resolvedParams);
|
||||
const { context, visitorAuthClaims } = await getEmbeddableStaticContext(await params);
|
||||
const withTracking = shouldTrackEvents(await headers());
|
||||
// The forced theme (`?theme=`) comes through the route context (set by the middleware), not a
|
||||
// request header, so the embed can honor it while staying statically rendered. RND-11571
|
||||
const forcedTheme = getSiteURLDataFromParams(resolvedParams).embedTheme ?? null;
|
||||
const forcedTheme = await getThemeFromMiddleware();
|
||||
|
||||
return (
|
||||
<EmbeddableRootLayout
|
||||
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { type RouteLayoutParams, getSiteURLDataFromParams } from '@/app/utils';
|
||||
import type { RouteLayoutParams } from '@/app/utils';
|
||||
import { isAIChatEnabled } from '@/components/utils/isAIChatEnabled';
|
||||
import { getEmbeddableDynamicContext } from '@/lib/embeddable';
|
||||
|
||||
@@ -12,15 +12,11 @@ export default async function Page(props: PageProps) {
|
||||
const params = await props.params;
|
||||
const { context } = await getEmbeddableDynamicContext(params);
|
||||
const baseURL = context.linker.toPathInSite('~gitbook/embed/');
|
||||
// Forward the forced theme to the default tab so `?theme=` isn't lost on the redirect. It comes
|
||||
// from the route context (set by the middleware), not the query, so the embed stays static. RND-11571
|
||||
const embedTheme = getSiteURLDataFromParams(params).embedTheme;
|
||||
const query = embedTheme ? `?theme=${embedTheme}` : '';
|
||||
|
||||
// If assistant is enabled, redirect to assistant, otherwise to docs
|
||||
if (isAIChatEnabled(context.customization.ai.mode)) {
|
||||
redirect(`${baseURL}/assistant${query}`);
|
||||
redirect(`${baseURL}/assistant`);
|
||||
} else {
|
||||
redirect(`${baseURL}/page/${query}`);
|
||||
redirect(`${baseURL}/page/`);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-11
@@ -9,8 +9,9 @@ import { isAIEnabled } from '@/components/utils/isAIChatEnabled';
|
||||
import { renderAskSourcesMarkdown, streamSiteAskAnswer } from '@/lib/ask';
|
||||
import { getExposableError, throwIfDataError } from '@/lib/data';
|
||||
import { fromPageMarkdown, getMarkdownForPageInSpace, toPageMarkdown } from '@/lib/markdownPage';
|
||||
import { resolvePagePath } from '@/lib/pages';
|
||||
import { joinPathWithBaseURL } from '@/lib/paths';
|
||||
import { findSiteSpaceBy, findSiteSpaceByUrl, resolveSiteSpacePagePath } from '@/lib/sites';
|
||||
import { findSiteSpaceBy, findSiteSpaceByUrl } from '@/lib/sites';
|
||||
import { trackServerInsightsEvents } from '@/lib/tracking';
|
||||
import { waitUntil } from '@/lib/waitUntil';
|
||||
|
||||
@@ -198,11 +199,7 @@ export async function handleMcpRequest(
|
||||
})
|
||||
);
|
||||
|
||||
const resolved = resolveSiteSpacePagePath(
|
||||
match.siteSpace,
|
||||
revision.pages,
|
||||
match.pagePath
|
||||
);
|
||||
const resolved = resolvePagePath(revision.pages, match.pagePath ?? '');
|
||||
if (!resolved) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Page not found: "${url}"` }],
|
||||
@@ -413,11 +410,7 @@ export async function handleMcpRequest(
|
||||
})
|
||||
);
|
||||
|
||||
const resolved = resolveSiteSpacePagePath(
|
||||
match.siteSpace,
|
||||
revision.pages,
|
||||
match.pagePath
|
||||
);
|
||||
const resolved = resolvePagePath(revision.pages, match.pagePath ?? '');
|
||||
if (!resolved) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Page not found: "${pageUrl}"` }],
|
||||
|
||||
+1
-9
@@ -127,15 +127,7 @@ export async function GET(
|
||||
'Show me tips and tricks',
|
||||
],
|
||||
tabs: ['assistant', 'search', 'docs'],
|
||||
closeButton: useCustomTrigger,
|
||||
// Host-page action: posts a message into the chat, like an embedding site would.
|
||||
actions: [
|
||||
{
|
||||
icon: 'rocket',
|
||||
label: 'Ask about pricing',
|
||||
onClick: () => window.GitBook('postUserMessage', 'How much does GitBook cost?'),
|
||||
},
|
||||
],
|
||||
closeButton: useCustomTrigger
|
||||
});
|
||||
|
||||
if (useCustomTrigger) {
|
||||
|
||||
+2
-7
@@ -1,4 +1,4 @@
|
||||
import { type RouteLayoutParams, getSiteURLDataFromParams } from '@/app/utils';
|
||||
import type { RouteLayoutParams } from '@/app/utils';
|
||||
import {
|
||||
EmbeddableRootLayout,
|
||||
generateEmbeddableMetadata,
|
||||
@@ -15,19 +15,14 @@ export default async function RootLayout({
|
||||
params,
|
||||
children,
|
||||
}: React.PropsWithChildren<SiteStaticLayoutProps>) {
|
||||
const resolvedParams = await params;
|
||||
const { context, visitorAuthClaims } = await getEmbeddableStaticContext(resolvedParams);
|
||||
const { context, visitorAuthClaims } = await getEmbeddableStaticContext(await params);
|
||||
const withTracking = shouldTrackEvents();
|
||||
// The forced theme (`?theme=`) is threaded through the route context by the middleware so the
|
||||
// embed stays statically rendered — read it from the params rather than a request header. RND-11571
|
||||
const forcedTheme = getSiteURLDataFromParams(resolvedParams).embedTheme ?? null;
|
||||
|
||||
return (
|
||||
<EmbeddableRootLayout
|
||||
context={context}
|
||||
withTracking={withTracking}
|
||||
visitorAuthClaims={visitorAuthClaims}
|
||||
forcedTheme={forcedTheme}
|
||||
>
|
||||
{children}
|
||||
</EmbeddableRootLayout>
|
||||
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { type RouteLayoutParams, getSiteURLDataFromParams } from '@/app/utils';
|
||||
import type { RouteLayoutParams } from '@/app/utils';
|
||||
import { isAIChatEnabled } from '@/components/utils/isAIChatEnabled';
|
||||
import { getEmbeddableStaticContext } from '@/lib/embeddable';
|
||||
|
||||
@@ -14,15 +14,11 @@ export default async function Page(props: PageProps) {
|
||||
const params = await props.params;
|
||||
const { context } = await getEmbeddableStaticContext(params);
|
||||
const baseURL = context.linker.toPathInSite('~gitbook/embed/');
|
||||
// Forward the forced theme to the default tab so `?theme=` isn't lost on the redirect. It comes
|
||||
// from the route context (set by the middleware), so reading it keeps this route static. RND-11571
|
||||
const embedTheme = getSiteURLDataFromParams(params).embedTheme;
|
||||
const query = embedTheme ? `?theme=${embedTheme}` : '';
|
||||
|
||||
// If assistant is enabled, redirect to assistant, otherwise to docs
|
||||
if (isAIChatEnabled(context.customization.ai.mode)) {
|
||||
redirect(`${baseURL}/assistant${query}`);
|
||||
redirect(`${baseURL}/assistant`);
|
||||
} else {
|
||||
redirect(`${baseURL}/page/${query}`);
|
||||
redirect(`${baseURL}/page/`);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-8
@@ -13,7 +13,6 @@ import { isPageIndexable } from '@/lib/seo';
|
||||
import {
|
||||
findSiteSpaceBy,
|
||||
getFallbackSiteSpacePath,
|
||||
getLinkerForSiteSpace,
|
||||
getLocalizedTitle,
|
||||
listAllSiteSpaces,
|
||||
} from '@/lib/sites';
|
||||
@@ -113,13 +112,9 @@ export async function GET(
|
||||
for (let i = 0; i < visibleSpaces.length; i++) {
|
||||
const siteSpace = visibleSpaces[i]!;
|
||||
const revision = revisions[i]!;
|
||||
const forkedLinker = getLinkerForSiteSpace(
|
||||
linker.withOtherSiteSpace({
|
||||
spaceBasePath: getFallbackSiteSpacePath(context, siteSpace),
|
||||
}),
|
||||
siteSpace,
|
||||
revision.pages
|
||||
);
|
||||
const forkedLinker = linker.withOtherSiteSpace({
|
||||
spaceBasePath: getFallbackSiteSpacePath(context, siteSpace),
|
||||
});
|
||||
|
||||
const lang = siteSpace.space.language ?? undefined;
|
||||
const sectionInfo = findSiteSpaceBy(structure, (ss) => ss.id === siteSpace.id);
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from '@gitbook/api';
|
||||
|
||||
import { getInsightsSession, useTrackEvent } from '../Insights';
|
||||
import { type UpdateSearchState, useSetSearchState } from '../Search';
|
||||
import { useSetSearchState } from '../Search';
|
||||
import { addRecentSearchQuery } from '../Search/recent-queries';
|
||||
import type { AnyAIControl } from './controls';
|
||||
import { ConfirmControlDef, ConfirmControlOutputSchema } from './controls/ConfirmControl';
|
||||
@@ -32,8 +32,6 @@ import { useCurrentContent } from '@/components/hooks';
|
||||
import { useLanguage } from '@/intl/client';
|
||||
import { tString } from '@/intl/translate';
|
||||
|
||||
const noopSetSearchState: UpdateSearchState = () => Promise.resolve(new URLSearchParams());
|
||||
|
||||
export type AIChatMessage = {
|
||||
role: AIMessageRole;
|
||||
content: React.ReactNode;
|
||||
@@ -237,7 +235,7 @@ export function AIChatProvider(props: {
|
||||
|
||||
const messageContextRef = useAIMessageContextRef();
|
||||
const trackEvent = useTrackEvent();
|
||||
const setSearchStateInURL = useSetSearchState();
|
||||
const setSearchState = useSetSearchState();
|
||||
const { siteSpaceId } = useCurrentContent();
|
||||
const language = useLanguage();
|
||||
|
||||
@@ -245,11 +243,6 @@ export function AIChatProvider(props: {
|
||||
? SiteInsightsDisplayContext.Embed
|
||||
: SiteInsightsDisplayContext.Site;
|
||||
|
||||
// The embed keeps its state in its own routes, not in URL search params.
|
||||
const setSearchState = renderMessageOptions?.asEmbeddable
|
||||
? noopSetSearchState
|
||||
: setSearchStateInURL;
|
||||
|
||||
// The assistant response the user is reacting to. Snapshotted when a new user turn begins
|
||||
// (before it overwrites the store's responseId/query), so the self-feedback tool rates that
|
||||
// previous response rather than the one this reaction turn produces.
|
||||
@@ -617,12 +610,14 @@ export function AIChatProvider(props: {
|
||||
// Post a message to the AI chat
|
||||
const onPostMessage = React.useCallback(
|
||||
async (input: { message: string }) => {
|
||||
const { query, messages, control, references, responding, opened } =
|
||||
globalState.getState();
|
||||
const { query, messages, control, references, responding } = globalState.getState();
|
||||
|
||||
// Still streaming, or waiting on a control (e.g. a tool confirmation): queue this
|
||||
// follow-up instead of dropping it (flushed in order in `streamResponse`).
|
||||
if (responding || control) {
|
||||
if (control) {
|
||||
throw new Error("We can't post a message when a control is active");
|
||||
}
|
||||
|
||||
// Still streaming: queue this follow-up instead of dropping it (flushed in order in `streamResponse`).
|
||||
if (responding) {
|
||||
globalState.setState((state) => ({
|
||||
...state,
|
||||
queuedMessages: [...state.queuedMessages, input.message],
|
||||
@@ -648,7 +643,9 @@ export function AIChatProvider(props: {
|
||||
|
||||
notify(eventsRef.current.get('postMessage'), { message: input.message });
|
||||
|
||||
if (query === input.message && references.length === 0 && !opened) {
|
||||
if (query === input.message && references.length === 0) {
|
||||
// Return early if the message is the same as the previous message
|
||||
// (unless new references are staged, which change the payload)
|
||||
globalState.setState((state) => ({
|
||||
...state,
|
||||
opened: true,
|
||||
|
||||
@@ -124,7 +124,7 @@ export function AIChatInput(props: {
|
||||
/>
|
||||
}
|
||||
trailing={
|
||||
<HoverCardRoot>
|
||||
<HoverCardRoot openDelay={500}>
|
||||
<HoverCard
|
||||
className="max-w-xs bg-tint p-2 text-sm text-tint"
|
||||
arrow={{ className: 'fill-tint-3' }}
|
||||
@@ -150,7 +150,7 @@ export function AIChatInput(props: {
|
||||
<p>{t(language, 'ai_chat_context_disclaimer')}</p>
|
||||
</div>
|
||||
</HoverCard>
|
||||
<HoverCardTrigger openDelay={500}>
|
||||
<HoverCardTrigger>
|
||||
{/* Negative margin to compensate for Input's padding, so the badge appears flush with the cursor */}
|
||||
<div className="-ml-1 flex cursor-help items-center gap-1 px-2.5 py-1.5 text-xs text-tint/7 transition-all hover:bg-tint rounded-corners:rounded-md circular-corners:rounded-2xl">
|
||||
<span className="-ml-1 bg-tint-11/7 px-1 py-0.5 font-mono text-[0.65rem] font-semibold leading-none text-contrast-tint-11 rounded-corners:rounded-sm circular-corners:rounded-2xl">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Fragment, useState } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
import { AIMessageRole } from '@gitbook/api';
|
||||
|
||||
@@ -18,14 +18,13 @@ import { useLanguage } from '@/intl/client';
|
||||
import { t, tString } from '@/intl/translate';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
type MessageGroup = { message: AIChatMessage; originalIndex: number };
|
||||
|
||||
export function AIChatMessages(props: { chat: AIChatState; chatController: AIChatController }) {
|
||||
const { chat, chatController } = props;
|
||||
const status = getAIChatStatus(chat);
|
||||
const showLoadingShim = chat.responding && status !== 'working' && status !== 'done';
|
||||
|
||||
// Group messages: user messages start a new group, all following messages until next user message belong to that group
|
||||
type MessageGroup = { message: AIChatMessage; originalIndex: number };
|
||||
const messageGroups: MessageGroup[][] = [];
|
||||
let currentGroup: MessageGroup[] = [];
|
||||
|
||||
@@ -47,6 +46,8 @@ export function AIChatMessages(props: { chat: AIChatState; chatController: AICha
|
||||
messageGroups.push(currentGroup);
|
||||
}
|
||||
|
||||
const language = useLanguage();
|
||||
|
||||
return messageGroups.map((group, groupIndex) => {
|
||||
const isLastGroup = group === messageGroups[messageGroups.length - 1];
|
||||
|
||||
@@ -111,141 +112,103 @@ export function AIChatMessages(props: { chat: AIChatState; chatController: AICha
|
||||
))}
|
||||
|
||||
{assistantItems.length > 0 ? (
|
||||
<AIChatAssistantGroup
|
||||
chat={chat}
|
||||
chatController={chatController}
|
||||
assistantItems={assistantItems}
|
||||
hasCommentary={hasCommentary}
|
||||
hasFinalAnswer={hasFinalAnswer}
|
||||
toolCount={toolCount}
|
||||
firstAssistantIndex={firstAssistantIndex}
|
||||
isLastMessage={isLastMessage}
|
||||
showLoadingShim={showLoadingShim}
|
||||
/>
|
||||
<Collapsible
|
||||
open={!hasFinalAnswer}
|
||||
disabled={!hasFinalAnswer}
|
||||
data-testid="ai-chat-message-assistant"
|
||||
id={`message-${firstAssistantIndex}`}
|
||||
className={tcls(
|
||||
'flex flex-col gap-2',
|
||||
'break-words',
|
||||
'group/message',
|
||||
'animate-blur-in-slow',
|
||||
'origin-top-left text-tint-strong',
|
||||
isLastMessage ? 'grow' : ''
|
||||
)}
|
||||
style={{
|
||||
animationDelay: `${Math.min(firstAssistantIndex * 0.1, 0.6)}s`,
|
||||
}}
|
||||
>
|
||||
{hasCommentary && hasFinalAnswer ? (
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant="blank"
|
||||
size="small"
|
||||
label={tString(language, 'ai_chat_view_activity')}
|
||||
className="group/dropdown animate-blur-in-display-slow -mx-3 -my-1.5 self-start"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span data-testid="ai-chat-activity-summary">
|
||||
{toolCount > 0
|
||||
? t(
|
||||
language,
|
||||
'ai_chat_explored_with',
|
||||
tString(
|
||||
language,
|
||||
toolCount === 1
|
||||
? 'tool_count'
|
||||
: 'tool_count_plural',
|
||||
toolCount.toString()
|
||||
)
|
||||
)
|
||||
: t(language, 'ai_chat_explored')}
|
||||
</span>
|
||||
<ToggleChevron orientation="right-to-down" />
|
||||
</div>
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
) : null}
|
||||
|
||||
{assistantItems.map(({ message, originalIndex }) => (
|
||||
<Fragment key={originalIndex}>{message.content}</Fragment>
|
||||
))}
|
||||
|
||||
{isLastMessage ? (
|
||||
<div
|
||||
className={tcls(
|
||||
'mt-4 flex w-full shrink-0 flex-col gap-2 overflow-hidden starting:opacity-0 transition-all transition-discrete duration-300',
|
||||
showLoadingShim
|
||||
? 'max-h-48 opacity-11'
|
||||
: 'pointer-events-none max-h-0 opacity-0'
|
||||
)}
|
||||
>
|
||||
<HoldMessage
|
||||
className={
|
||||
assistantItems.some(({ message }) => message.content)
|
||||
? 'hidden'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<LoadingSkeleton />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isLastMessage ? (
|
||||
<>
|
||||
{!chat.responding &&
|
||||
!chat.error &&
|
||||
chat.query &&
|
||||
chat.responseId &&
|
||||
!chat.control ? (
|
||||
<AIResponseFeedback
|
||||
responseId={chat.responseId}
|
||||
query={chat.query}
|
||||
className="-ml-1.5 -mt-4 mb-2"
|
||||
/>
|
||||
) : null}
|
||||
<AIChatFollowupSuggestions
|
||||
chat={chat}
|
||||
chatController={chatController}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</Collapsible>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function AIChatAssistantGroup(props: {
|
||||
chat: AIChatState;
|
||||
chatController: AIChatController;
|
||||
assistantItems: MessageGroup[];
|
||||
hasCommentary: boolean;
|
||||
hasFinalAnswer: boolean;
|
||||
toolCount: number;
|
||||
firstAssistantIndex: number;
|
||||
isLastMessage: boolean;
|
||||
showLoadingShim: boolean;
|
||||
}) {
|
||||
const {
|
||||
chat,
|
||||
chatController,
|
||||
assistantItems,
|
||||
hasCommentary,
|
||||
hasFinalAnswer,
|
||||
toolCount,
|
||||
firstAssistantIndex,
|
||||
isLastMessage,
|
||||
showLoadingShim,
|
||||
} = props;
|
||||
const language = useLanguage();
|
||||
// Base UI locks controlled/uncontrolled mode on first render, so once `hasFinalAnswer`
|
||||
// flips the trigger over to being interactive, we need our own state to track manual toggles.
|
||||
const [manuallyOpened, setManuallyOpened] = useState(false);
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
open={!hasFinalAnswer || manuallyOpened}
|
||||
onOpenChange={setManuallyOpened}
|
||||
disabled={!hasFinalAnswer}
|
||||
data-testid="ai-chat-message-assistant"
|
||||
id={`message-${firstAssistantIndex}`}
|
||||
className={tcls(
|
||||
'flex flex-col gap-2',
|
||||
'break-words',
|
||||
'group/message',
|
||||
'animate-blur-in-slow',
|
||||
'origin-top-left text-tint-strong',
|
||||
isLastMessage ? 'grow' : ''
|
||||
)}
|
||||
style={{
|
||||
animationDelay: `${Math.min(firstAssistantIndex * 0.1, 0.6)}s`,
|
||||
}}
|
||||
>
|
||||
{hasCommentary && hasFinalAnswer ? (
|
||||
<CollapsibleTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="blank"
|
||||
size="small"
|
||||
label={tString(language, 'ai_chat_view_activity')}
|
||||
className="group/dropdown animate-blur-in-display-slow -mx-3 -my-1.5 self-start"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span data-testid="ai-chat-activity-summary">
|
||||
{toolCount > 0
|
||||
? t(
|
||||
language,
|
||||
'ai_chat_explored_with',
|
||||
tString(
|
||||
language,
|
||||
toolCount === 1 ? 'tool_count' : 'tool_count_plural',
|
||||
toolCount.toString()
|
||||
)
|
||||
)
|
||||
: t(language, 'ai_chat_explored')}
|
||||
</span>
|
||||
<ToggleChevron orientation="right-to-down" />
|
||||
</div>
|
||||
</CollapsibleTrigger>
|
||||
) : null}
|
||||
|
||||
{assistantItems.map(({ message, originalIndex }) => (
|
||||
<Fragment key={originalIndex}>{message.content}</Fragment>
|
||||
))}
|
||||
|
||||
{isLastMessage ? (
|
||||
<div
|
||||
className={tcls(
|
||||
'mt-4 flex w-full shrink-0 flex-col gap-2 overflow-hidden starting:opacity-0 transition-all transition-discrete duration-300',
|
||||
showLoadingShim
|
||||
? 'max-h-48 opacity-11'
|
||||
: 'pointer-events-none max-h-0 opacity-0'
|
||||
)}
|
||||
>
|
||||
<HoldMessage
|
||||
className={
|
||||
assistantItems.some(({ message }) => message.content) ? 'hidden' : ''
|
||||
}
|
||||
/>
|
||||
<LoadingSkeleton />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isLastMessage ? (
|
||||
<>
|
||||
{!chat.responding &&
|
||||
!chat.error &&
|
||||
chat.query &&
|
||||
chat.responseId &&
|
||||
!chat.control ? (
|
||||
<AIResponseFeedback
|
||||
responseId={chat.responseId}
|
||||
query={chat.query}
|
||||
className="-ml-1.5 -mt-4 mb-2"
|
||||
/>
|
||||
) : null}
|
||||
<AIChatFollowupSuggestions chat={chat} chatController={chatController} />
|
||||
</>
|
||||
) : null}
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
export function HoldMessage({
|
||||
breakLines = false,
|
||||
className,
|
||||
|
||||
@@ -10,12 +10,12 @@ export function AnnotationPopover(props: { children: React.ReactNode; body: Reac
|
||||
return (
|
||||
<Tooltip
|
||||
label={body}
|
||||
pinOnClick
|
||||
popupProps={{
|
||||
contentProps={{
|
||||
role: 'definition',
|
||||
}}
|
||||
className="bg-tint-base px-4 py-3 text-sm text-tint-strong shadow-lg shadow-tint-12/4 ring-1 ring-tint-subtle dark:shadow-tint-1"
|
||||
arrow={{ className: 'fill-tint-1' }}
|
||||
arrow={true}
|
||||
arrowProps={{ className: 'fill-tint-1' }}
|
||||
>
|
||||
<dfn
|
||||
data-annotation=""
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { Dialog } from '@base-ui/react/dialog';
|
||||
import Panzoom from '@panzoom/panzoom';
|
||||
import type { RenderResult } from 'mermaid';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FocusScope, usePreventScroll } from 'react-aria';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { type ClientBlockProps, ClientCodeBlock } from './ClientCodeBlock';
|
||||
@@ -14,6 +14,9 @@ import { useHasBeenInViewport } from '@/components/hooks/useHasBeenInViewport';
|
||||
import { Loading } from '@/components/primitives/Loading';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
/** Duration of the fullscreen dialog enter/exit animation, must match `animate-blur-in/out`. */
|
||||
const DIALOG_ANIMATION_MS = 200;
|
||||
|
||||
/**
|
||||
* Used to render a Mermaid diagram from a CodeBlock.
|
||||
*/
|
||||
@@ -42,13 +45,11 @@ export function MermaidCodeBlock(
|
||||
const [panZoom, setPanZoom] = useState<ReturnType<typeof Panzoom> | null>(null);
|
||||
const [error, setError] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
// `isPresent` stays true through the closing animation, until the panel hands the host back.
|
||||
// `isFullscreen` is the open intent; `isExiting` keeps the dialog mounted while it
|
||||
// animates closed. `isPresent` is true whenever the diagram lives in the dialog.
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const [isPresent, setIsPresent] = useState(false);
|
||||
// Read by the wheel handler (registered once in `initPanzoom`) so it always knows
|
||||
// whether the diagram is currently in the fullscreen dialog.
|
||||
const isPresentRef = useRef(isPresent);
|
||||
isPresentRef.current = isPresent;
|
||||
const [isExiting, setIsExiting] = useState(false);
|
||||
const isPresent = isFullscreen || isExiting;
|
||||
const { resolvedTheme } = useTheme();
|
||||
const darkMode = resolvedTheme === 'dark';
|
||||
const id = useSafeId();
|
||||
@@ -99,7 +100,6 @@ export function MermaidCodeBlock(
|
||||
cleanupPanZoom = initPanzoom({
|
||||
container,
|
||||
wrapper,
|
||||
isFullscreenRef: isPresentRef,
|
||||
onInit: setPanZoom,
|
||||
});
|
||||
})
|
||||
@@ -123,6 +123,9 @@ export function MermaidCodeBlock(
|
||||
};
|
||||
}, [source, id, darkMode, mermaidRuntimeURL, shouldRender]);
|
||||
|
||||
// Lock the page scroll while the dialog is on screen (handles scrollbar width and iOS).
|
||||
usePreventScroll({ isDisabled: !isPresent });
|
||||
|
||||
const openFullscreen = useCallback(() => {
|
||||
// Reserve the inline slot's current height before the diagram is detached, so the
|
||||
// page layout does not jump. Measured here while still inline and un-restyled.
|
||||
@@ -130,7 +133,7 @@ export function MermaidCodeBlock(
|
||||
if (root) {
|
||||
root.style.minHeight = `${root.offsetHeight}px`;
|
||||
}
|
||||
setIsPresent(true);
|
||||
setIsExiting(false);
|
||||
setIsFullscreen(true);
|
||||
// Re-center the diagram for the larger view.
|
||||
panZoom?.reset();
|
||||
@@ -138,8 +141,38 @@ export function MermaidCodeBlock(
|
||||
|
||||
const closeFullscreen = useCallback(() => {
|
||||
setIsFullscreen(false);
|
||||
setIsExiting(true);
|
||||
}, []);
|
||||
|
||||
// Keep the dialog mounted until the exit animation finishes, then unmount it.
|
||||
useEffect(() => {
|
||||
if (!isExiting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = window.setTimeout(() => {
|
||||
setIsExiting(false);
|
||||
panZoom?.reset();
|
||||
}, DIALOG_ANIMATION_MS);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [isExiting, panZoom]);
|
||||
|
||||
// Allow Escape to close the dialog.
|
||||
useEffect(() => {
|
||||
if (!isFullscreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
closeFullscreen();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
return () => document.removeEventListener('keydown', onKeyDown);
|
||||
}, [isFullscreen, closeFullscreen]);
|
||||
|
||||
// Keep the diagram host in the inline slot on mount (and whenever it isn't in the dialog).
|
||||
useLayoutEffect(() => {
|
||||
const host = diagramHostRef.current;
|
||||
@@ -150,7 +183,8 @@ export function MermaidCodeBlock(
|
||||
}, []);
|
||||
|
||||
// Move the diagram host into the dialog panel (and back) as the panel mounts/unmounts.
|
||||
// The inline slot's reserved height (set in openFullscreen) is cleared on return.
|
||||
// Done in the panel's ref callback so it happens during commit, before FocusScope reads
|
||||
// focus. The inline slot's reserved height (set in openFullscreen) is cleared on return.
|
||||
const setPanel = useCallback((panel: HTMLDivElement | null) => {
|
||||
panelRef.current = panel;
|
||||
const host = diagramHostRef.current;
|
||||
@@ -164,7 +198,6 @@ export function MermaidCodeBlock(
|
||||
} else if (root) {
|
||||
root.appendChild(host);
|
||||
root.style.minHeight = '';
|
||||
setIsPresent(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -220,20 +253,36 @@ export function MermaidCodeBlock(
|
||||
{/* Inline slot: hosts the diagram in the document flow until it goes fullscreen. */}
|
||||
<div ref={rootRef} className={tcls('relative', style)} contentEditable={false} />
|
||||
{diagramHostRef.current ? createPortal(diagram, diagramHostRef.current) : null}
|
||||
<Dialog.Root
|
||||
open={isFullscreen}
|
||||
onOpenChange={(open) => !open && closeFullscreen()}
|
||||
onOpenChangeComplete={(open) => !open && panZoom?.reset()}
|
||||
>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Backdrop className="data-closed:animate-fade-out data-open:animate-fade-in fixed inset-0 z-40 bg-tint-base/3 backdrop-blur-md dark:bg-tint-base/6" />
|
||||
<Dialog.Popup
|
||||
aria-label="Mermaid diagram"
|
||||
render={<div ref={setPanel} />}
|
||||
className="outline-hidden data-closed:animate-blur-out data-open:animate-blur-in fixed inset-3 z-40 mx-auto flex max-w-[110rem] flex-col overflow-hidden rounded-2xl border border-tint-subtle bg-tint-base shadow-2xl sm:inset-5 lg:inset-8"
|
||||
/>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
{isPresent
|
||||
? createPortal(
|
||||
<FocusScope contain restoreFocus>
|
||||
{/* Backdrop: dims and blurs the page, closes on click. */}
|
||||
{/* oxlint-disable-next-line jsx-a11y/click-events-have-key-events */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={tcls(
|
||||
'fixed inset-0 z-40 bg-tint-base/3 backdrop-blur-md dark:bg-tint-base/6',
|
||||
isFullscreen ? 'animate-fade-in' : 'animate-fade-out'
|
||||
)}
|
||||
onClick={closeFullscreen}
|
||||
/>
|
||||
{/* Centered panel. The padding area lets clicks fall through to the backdrop. */}
|
||||
<div className="pointer-events-none fixed inset-0 z-40 flex items-center justify-center p-3 sm:p-5 lg:p-8">
|
||||
<div
|
||||
ref={setPanel}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Mermaid diagram"
|
||||
className={tcls(
|
||||
'pointer-events-auto relative flex h-full w-full max-w-[110rem] flex-col overflow-hidden rounded-2xl border border-tint-subtle bg-tint-base shadow-2xl',
|
||||
isFullscreen ? 'animate-blur-in' : 'animate-blur-out'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</FocusScope>,
|
||||
document.body
|
||||
)
|
||||
: null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -372,10 +421,9 @@ type WindowWithIdleCallback = Window & {
|
||||
function initPanzoom(args: {
|
||||
container: HTMLElement;
|
||||
wrapper: HTMLElement;
|
||||
isFullscreenRef: { current: boolean };
|
||||
onInit: (instance: ReturnType<typeof Panzoom> | null) => void;
|
||||
}): () => void {
|
||||
const { container, wrapper, isFullscreenRef, onInit } = args;
|
||||
const { container, wrapper, onInit } = args;
|
||||
|
||||
const instance = Panzoom(container, {
|
||||
maxScale: 5,
|
||||
@@ -386,21 +434,10 @@ function initPanzoom(args: {
|
||||
|
||||
onInit(instance);
|
||||
|
||||
// Inline, the wheel must scroll the page — only zoom when the user holds Ctrl/Cmd, so
|
||||
// scrolling past a diagram isn't hijacked (mirrors the modifier-to-zoom behavior of
|
||||
// embedded maps; trackpad pinch also arrives as a Ctrl+wheel event). In the fullscreen
|
||||
// dialog the page scroll is locked, so the wheel always zooms.
|
||||
const onWheel = (event: WheelEvent) => {
|
||||
if (!isFullscreenRef.current && !event.ctrlKey && !event.metaKey) {
|
||||
return;
|
||||
}
|
||||
instance.zoomWithWheel(event);
|
||||
};
|
||||
|
||||
wrapper.addEventListener('wheel', onWheel, { passive: false });
|
||||
wrapper.addEventListener('wheel', instance.zoomWithWheel, { passive: false });
|
||||
|
||||
return () => {
|
||||
wrapper.removeEventListener('wheel', onWheel);
|
||||
wrapper.removeEventListener('wheel', instance.zoomWithWheel);
|
||||
instance.destroy();
|
||||
onInit(null);
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@ import dynamic from 'next/dynamic';
|
||||
|
||||
import type { ClientBlockProps } from './ClientCodeBlock';
|
||||
|
||||
// Keeps @panzoom/panzoom out of the entry chunk for the many pages that have code blocks but no
|
||||
// diagram. `ssr: true` so real diagram blocks still server-render their skeleton.
|
||||
// Keeps react-aria and @panzoom/panzoom out of the entry chunk for the many pages that have code
|
||||
// blocks but no diagram. `ssr: true` so real diagram blocks still server-render their skeleton.
|
||||
const MermaidCodeBlock = dynamic(
|
||||
() => import('./MermaidCodeBlock').then((mod) => mod.MermaidCodeBlock),
|
||||
{ ssr: true }
|
||||
|
||||
@@ -20,9 +20,10 @@ export function MermaidPanZoomControls(props: {
|
||||
size: 'xsmall' as const,
|
||||
iconOnly: true,
|
||||
className: tcls('p-1 [&_svg]:size-3.5 opacity-90'),
|
||||
// Non-hoverable so the tooltip can't steal the hover that reveals these controls.
|
||||
// Non-interactive tooltips: the Tooltip primitive makes their popper wrapper
|
||||
// pointer-transparent so it can't steal the hover that reveals these controls.
|
||||
tooltipProps: {
|
||||
disableHoverablePopup: true,
|
||||
rootProps: { disableHoverableContent: true },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -357,9 +357,7 @@ function createTheme(options: {
|
||||
settings: [
|
||||
// Comments
|
||||
{
|
||||
// Include the comment delimiter punctuation (e.g. `//`, `/*`) so it matches
|
||||
// the comment body instead of falling through to the generic `punctuation` scope.
|
||||
scope: ['comment', 'punctuation.definition.comment'],
|
||||
scope: ['comment'],
|
||||
settings: {
|
||||
foreground: colors.basic.comment,
|
||||
},
|
||||
|
||||
@@ -585,18 +585,12 @@ button.openapi-mcp {
|
||||
@apply bg-transparent!
|
||||
}
|
||||
|
||||
.openapi-panel {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
.openapi-panel-heading {
|
||||
@apply font-medium px-4 py-2 text-xs uppercase shrink-0;
|
||||
@apply font-medium px-4 py-2 text-xs uppercase;
|
||||
}
|
||||
|
||||
.openapi-panel-body {
|
||||
/* Mirrors .openapi-section-body: the sticky preview column caps the height, so the body must be
|
||||
able to shrink for the code block's own overflow-auto to kick in instead of being clipped. */
|
||||
@apply relative flex flex-col shrink min-h-0 overflow-clip [overflow-clip-margin:0.75rem];
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.openapi-panel-footer,
|
||||
|
||||
@@ -74,9 +74,6 @@ export function EmbeddableIframeAPI(props: { baseURL: string }) {
|
||||
break;
|
||||
}
|
||||
case 'postUserMessage': {
|
||||
// The answer renders in the assistant, so bring it up if another tab is showing.
|
||||
navToken.current++;
|
||||
router.push(`${baseURL}/assistant`);
|
||||
chatController.postMessage({
|
||||
message: message.message,
|
||||
});
|
||||
@@ -182,7 +179,11 @@ export function EmbeddableIframeButtons() {
|
||||
onClick={() => {
|
||||
action.onClick?.();
|
||||
}}
|
||||
tooltipProps={{ side: 'right' }}
|
||||
tooltipProps={{
|
||||
contentProps: {
|
||||
side: 'right',
|
||||
},
|
||||
}}
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
/>
|
||||
))}
|
||||
@@ -266,7 +267,11 @@ export function EmbeddableIframeTabs(props: {
|
||||
}
|
||||
router.push(tab.href);
|
||||
}}
|
||||
tooltipProps={{ side: 'right' }}
|
||||
tooltipProps={{
|
||||
contentProps: {
|
||||
side: 'right',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -294,7 +299,11 @@ export function EmbeddableIframeCloseButton(props: { onClose?: () => void }) {
|
||||
onClose?.();
|
||||
getChannel()?.send({ type: 'close' });
|
||||
}}
|
||||
tooltipProps={{ side: 'right' }}
|
||||
tooltipProps={{
|
||||
contentProps: {
|
||||
side: 'right',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { SpaceLayoutServerContext } from '../SpaceLayout';
|
||||
import { Trademark } from '../TableOfContents/Trademark';
|
||||
import { EmbeddableAIContextProvider } from './EmbeddableAIContextProvider';
|
||||
import { EmbeddableIframeAPI } from './EmbeddableIframeAPI';
|
||||
import { EmbeddableThemeSync } from './EmbeddableThemeSync';
|
||||
import { IfEmbeddableTrademark } from './EmbeddableTrademark';
|
||||
import { CustomizationRootLayout } from '@/components/RootLayout';
|
||||
import {
|
||||
@@ -37,11 +36,6 @@ export async function EmbeddableRootLayout({
|
||||
}: React.PropsWithChildren<EmbeddableRootLayoutProps>) {
|
||||
const theme = resolveEmbeddableTheme(context.customization, forcedTheme);
|
||||
|
||||
// Only remember a theme the visitor explicitly requested via `?theme=`, and only on
|
||||
// multi-theme sites that actually honor the override. Single-theme sites ignore it (and would
|
||||
// otherwise persist their own default on every plain visit — see PR #4380 review). RND-11571
|
||||
const rememberedTheme = context.customization.themes.toggeable ? forcedTheme : null;
|
||||
|
||||
return (
|
||||
<CustomizationRootLayout
|
||||
context={context}
|
||||
@@ -57,8 +51,6 @@ export async function EmbeddableRootLayout({
|
||||
contextId={context.contextId}
|
||||
proxyOrigin={context.site.proxy?.origin}
|
||||
>
|
||||
{/* Persist an explicit ?theme= override so it survives tab navigation. RND-11571 */}
|
||||
<EmbeddableThemeSync forcedTheme={rememberedTheme} />
|
||||
<EmbeddableAIContextProvider
|
||||
aiMode={context.customization.ai.mode}
|
||||
suggestions={context.customization.ai.suggestions}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import type { CustomizationDefaultThemeMode } from '@gitbook/api';
|
||||
|
||||
/**
|
||||
* Persist a theme forced via the URL (`?theme=light`/`?theme=dark`) into the embed's own
|
||||
* theme storage, so it is remembered across tab navigation and reloads instead of only
|
||||
* applying while the query string is present (RND-11571).
|
||||
*
|
||||
* The server still forces the theme on the first load (avoiding a flash); this just writes it
|
||||
* to next-themes' storage so subsequent embed pages — which are loaded without the query —
|
||||
* pick it up from storage. An explicit theme on a later load overrides the remembered one.
|
||||
*/
|
||||
export function EmbeddableThemeSync({
|
||||
forcedTheme,
|
||||
}: {
|
||||
forcedTheme?: CustomizationDefaultThemeMode | null;
|
||||
}) {
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
// Ref so the effect only runs when `forcedTheme` changes: next-themes recreates `setTheme` on
|
||||
// every theme change, and depending on it would re-write storage each time — causing a cross-tab
|
||||
// ping-pong between two same-site embeds opened at different `?theme=` values.
|
||||
const setThemeRef = useRef(setTheme);
|
||||
setThemeRef.current = setTheme;
|
||||
|
||||
useEffect(() => {
|
||||
if (forcedTheme) {
|
||||
setThemeRef.current(forcedTheme);
|
||||
}
|
||||
}, [forcedTheme]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import { tcls } from '@/lib/tailwind';
|
||||
|
||||
/**
|
||||
* Client component wrapping the DropdownMenu for header links.
|
||||
* Accepts serializable props to avoid Flight lazy chunk wrappers reaching the trigger's `render` prop.
|
||||
* Accepts serializable props to avoid Flight lazy chunk wrappers reaching Radix's Slot.
|
||||
*/
|
||||
export function HeaderLinkDropdown(props: {
|
||||
headerPreset: CustomizationHeaderPreset;
|
||||
@@ -59,7 +59,6 @@ export function HeaderLinkDropdown(props: {
|
||||
/>
|
||||
)
|
||||
}
|
||||
nativeButton={false}
|
||||
openOnHover={true}
|
||||
>
|
||||
{children}
|
||||
@@ -146,12 +145,12 @@ function getHeaderLinkClassName(_props: { headerPreset: CustomizationHeaderPrese
|
||||
|
||||
'text-tint',
|
||||
'links-default:hover:text-primary',
|
||||
'links-default:aria-expanded:text-primary',
|
||||
'links-default:data-[state=open]:text-primary',
|
||||
'links-default:tint:hover:text-tint-strong',
|
||||
'links-default:tint:aria-expanded:text-tint-strong',
|
||||
'links-default:tint:data-[state=open]:text-tint-strong',
|
||||
'underline-offset-2',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:aria-expanded:underline',
|
||||
'links-accent:data-[state=open]:underline',
|
||||
'links-accent:underline-offset-4',
|
||||
'links-accent:decoration-primary-subtle',
|
||||
'links-accent:decoration-[3px]',
|
||||
|
||||
@@ -10,8 +10,7 @@ import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
/**
|
||||
* Client component for the "More" header link dropdown.
|
||||
* Creates the trigger button internally to avoid Flight lazy chunk wrappers reaching the trigger's
|
||||
* `render` prop.
|
||||
* Creates the trigger button internally to avoid Flight lazy chunk wrappers reaching Radix's Slot.
|
||||
*/
|
||||
export function HeaderLinkMoreDropdown(props: {
|
||||
label: React.ReactNode;
|
||||
|
||||
@@ -8,8 +8,8 @@ import { SpacesDropdownMenuItems } from './SpacesDropdownMenuItem';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
/**
|
||||
* Client component that creates the Button trigger and DropdownMenu internally, avoiding
|
||||
* server→client element serialization through the trigger's `render` prop.
|
||||
* Client component that creates the Button trigger and DropdownMenu internally,
|
||||
* avoiding server→client element serialization through Radix's Slot.
|
||||
*/
|
||||
export function SpacesDropdownClient(props: {
|
||||
title: string;
|
||||
|
||||
@@ -3,8 +3,7 @@ import { describe, expect, it } from 'bun:test';
|
||||
import { RevisionPageType } from '@gitbook/api';
|
||||
|
||||
import { createPDFLinker } from './linker';
|
||||
import { createLinker, linkerWithAbsoluteURLs } from '@/lib/links';
|
||||
import { getLinkerForSiteSpace } from '@/lib/sites';
|
||||
import { createLinker } from '@/lib/links';
|
||||
|
||||
function createDocumentPage(id: string, path: string) {
|
||||
return {
|
||||
@@ -24,17 +23,6 @@ function createGroupPage(id: string, path: string) {
|
||||
} as any;
|
||||
}
|
||||
|
||||
function createPublishedLinker() {
|
||||
return linkerWithAbsoluteURLs(
|
||||
createLinker({
|
||||
protocol: 'https:',
|
||||
host: 'docs.vectra.ai',
|
||||
siteBasePath: '/deployment',
|
||||
spaceBasePath: '/deployment',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
describe('createPDFLinker', () => {
|
||||
it('creates anchor links for pages included in the PDF export', () => {
|
||||
const linker = createPDFLinker(
|
||||
@@ -83,7 +71,7 @@ describe('createPDFLinker', () => {
|
||||
const linker = createPDFLinker(
|
||||
baseLinker,
|
||||
[{ page: createDocumentPage('included', '') }],
|
||||
createPublishedLinker()
|
||||
'https://docs.vectra.ai/deployment/'
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -107,7 +95,7 @@ describe('createPDFLinker', () => {
|
||||
const linker = createPDFLinker(
|
||||
baseLinker,
|
||||
[{ page: createDocumentPage('included', '') }],
|
||||
createPublishedLinker()
|
||||
'https://docs.vectra.ai/deployment/'
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -122,53 +110,6 @@ describe('createPDFLinker', () => {
|
||||
).toBe('https://docs.vectra.ai/deployment/respond#faq');
|
||||
});
|
||||
|
||||
it('keeps the explicit path for the former first page with a custom home page', () => {
|
||||
const firstPage = createDocumentPage('first', 'getting-started');
|
||||
const includedPage = createDocumentPage('included', 'included');
|
||||
const publishedLinker = getLinkerForSiteSpace(
|
||||
createPublishedLinker(),
|
||||
{ pageId: 'first' } as any,
|
||||
[firstPage, includedPage]
|
||||
);
|
||||
const linker = createPDFLinker(
|
||||
createLinker({
|
||||
host: 'open-2v.gitbook.com',
|
||||
siteBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
spaceBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
}),
|
||||
[{ page: includedPage }],
|
||||
publishedLinker
|
||||
);
|
||||
|
||||
expect(
|
||||
linker.toPathForPage({
|
||||
pages: [firstPage, includedPage],
|
||||
page: firstPage,
|
||||
anchor: 'intro',
|
||||
})
|
||||
).toBe('https://docs.vectra.ai/deployment/getting-started#intro');
|
||||
});
|
||||
|
||||
it('links the first page of an ordinary space to the published root', () => {
|
||||
const firstPage = createDocumentPage('first', '');
|
||||
const linker = createPDFLinker(
|
||||
createLinker({
|
||||
host: 'open-2v.gitbook.com',
|
||||
siteBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
spaceBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
}),
|
||||
[{ page: createDocumentPage('included', 'included') }],
|
||||
createPublishedLinker()
|
||||
);
|
||||
|
||||
expect(
|
||||
linker.toPathForPage({
|
||||
pages: [firstPage],
|
||||
page: firstPage,
|
||||
})
|
||||
).toBe('https://docs.vectra.ai/deployment');
|
||||
});
|
||||
|
||||
it('returns a local placeholder link for group pages not included in the PDF export', () => {
|
||||
const baseLinker = createLinker({
|
||||
host: 'docs.vectra.ai',
|
||||
@@ -188,23 +129,4 @@ describe('createPDFLinker', () => {
|
||||
})
|
||||
).toBe('#');
|
||||
});
|
||||
|
||||
it('falls back to an absolute base link when no published linker is provided', () => {
|
||||
const baseLinker = createLinker({
|
||||
host: 'open-2v.gitbook.com',
|
||||
siteBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
spaceBasePath: '/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf',
|
||||
});
|
||||
const linker = createPDFLinker(baseLinker, [{ page: createDocumentPage('included', '') }]);
|
||||
|
||||
expect(
|
||||
linker.toPathForPage({
|
||||
pages: [
|
||||
createDocumentPage('included', ''),
|
||||
createDocumentPage('outside', 'respond'),
|
||||
],
|
||||
page: createDocumentPage('outside', 'respond'),
|
||||
})
|
||||
).toBe('https://open-2v.gitbook.com/~space/HJ1ltuWFvsArFWtevnRn~gitbook/pdf/respond');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,10 +25,8 @@ import { tString } from '@/intl/translate';
|
||||
import { sanitizeGitBookAppURL } from '@/lib/app';
|
||||
import type { GitBookSiteContext, GitBookSpaceContext } from '@/lib/context';
|
||||
import { getPageDocument } from '@/lib/data';
|
||||
import { type GitBookLinker, createLinker, linkerWithAbsoluteURLs } from '@/lib/links';
|
||||
import './pdf.css';
|
||||
import { resolvePageId } from '@/lib/pages';
|
||||
import { getLinkerForSiteSpace } from '@/lib/sites';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { defaultCustomization } from '@/lib/utils';
|
||||
|
||||
@@ -68,7 +66,7 @@ export async function PDFPage(props: {
|
||||
);
|
||||
|
||||
// Build a linker that create anchor links for the pages rendered in the PDF page.
|
||||
const linker = createPDFLinker(baseContext.linker, pages, getPublishedLinker(baseContext));
|
||||
const linker = createPDFLinker(baseContext.linker, pages, baseContext.space.urls.published);
|
||||
|
||||
const context: GitBookSpaceContext = {
|
||||
...baseContext,
|
||||
@@ -149,31 +147,6 @@ export async function PDFPage(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function getPublishedLinker(
|
||||
context: GitBookSpaceContext | GitBookSiteContext
|
||||
): GitBookLinker | undefined {
|
||||
const publishedURL =
|
||||
('siteSpace' in context ? context.siteSpace.urls.published : undefined) ??
|
||||
context.space.urls.published;
|
||||
if (!publishedURL) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const url = new URL(publishedURL);
|
||||
const publishedLinker = linkerWithAbsoluteURLs(
|
||||
createLinker({
|
||||
protocol: url.protocol,
|
||||
host: url.host,
|
||||
siteBasePath: url.pathname,
|
||||
spaceBasePath: url.pathname,
|
||||
})
|
||||
);
|
||||
|
||||
return 'siteSpace' in context
|
||||
? getLinkerForSiteSpace(publishedLinker, context.siteSpace, context.revision.pages)
|
||||
: publishedLinker;
|
||||
}
|
||||
|
||||
async function PDFSpaceIntro(props: { space: Space; customization: SiteCustomizationSettings }) {
|
||||
const { space, customization } = props;
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { Revision, RevisionPageDocument, RevisionPageGroup } from '@gitbook
|
||||
import { RevisionPageType } from '@gitbook/api';
|
||||
|
||||
import type { GitBookLinker } from '@/lib/links';
|
||||
import { getPagePath } from '@/lib/pages';
|
||||
import { withTrailingSlash } from '@/lib/paths';
|
||||
|
||||
/**
|
||||
* Create the HTML ID for the container of a page or a given anchor in it.
|
||||
@@ -23,19 +25,19 @@ export function getPagePDFContainerId(
|
||||
* This linker generates in-document anchor links for pages that are included
|
||||
* in the current PDF export, using `getPagePDFContainerId` to build the
|
||||
* target element ID. For pages that are not part of the exported PDF, it
|
||||
* falls back to URLs pointing to the published site if `publishedLinker` is
|
||||
* falls back to URLs pointing to the published site if `publishedSpaceURL` is
|
||||
* provided, preserving navigability for external content. Otherwise, it uses
|
||||
* absolute URLs from the base linker.
|
||||
*
|
||||
* @param baseLinker - The base GitBook linker used to resolve standard paths and URLs.
|
||||
* @param pages - The list of pages that are included in the current PDF export.
|
||||
* @param publishedLinker - Optional linker for the published space for external page links.
|
||||
* @param publishedSpaceURL - Optional URL of the published space for external page links.
|
||||
* @returns A `GitBookLinker` configured to generate PDF-friendly links.
|
||||
*/
|
||||
export function createPDFLinker(
|
||||
baseLinker: GitBookLinker,
|
||||
pages: { page: Revision['pages'][number] }[],
|
||||
publishedLinker?: GitBookLinker
|
||||
publishedSpaceURL?: string
|
||||
): GitBookLinker {
|
||||
return {
|
||||
...baseLinker,
|
||||
@@ -48,8 +50,14 @@ export function createPDFLinker(
|
||||
}
|
||||
|
||||
// For pages that are not embedded in this PDF export, keep links on the published site.
|
||||
if (publishedLinker) {
|
||||
return publishedLinker.toPathForPage(input);
|
||||
if (publishedSpaceURL) {
|
||||
const pagePath = getPagePath(input.pages, input.page);
|
||||
const pageURL = new URL(pagePath, withTrailingSlash(publishedSpaceURL));
|
||||
if (input.anchor) {
|
||||
pageURL.hash = input.anchor;
|
||||
}
|
||||
|
||||
return pageURL.toString();
|
||||
}
|
||||
|
||||
return baseLinker.toAbsoluteURL(baseLinker.toPathForPage(input));
|
||||
|
||||
@@ -46,10 +46,7 @@ export function BreadcrumbItemDropdown(props: {
|
||||
const content = (
|
||||
<>
|
||||
{emoji || icon ? (
|
||||
<PageIcon
|
||||
page={{ emoji, icon }}
|
||||
style="mb-0.5 mr-1 inline-block size-[1em] shrink-0"
|
||||
/>
|
||||
<PageIcon page={{ emoji, icon }} style="mr-1 inline size-[1em] shrink-0" />
|
||||
) : null}
|
||||
{label}
|
||||
</>
|
||||
@@ -60,7 +57,7 @@ export function BreadcrumbItemDropdown(props: {
|
||||
// normalize "" to "/" the way the table of contents does.
|
||||
const trigger =
|
||||
href !== undefined ? (
|
||||
<StyledLink href={href || '/'} role="link" className={tcls(linkClassName)}>
|
||||
<StyledLink href={href || '/'} className={tcls(linkClassName)}>
|
||||
{content}
|
||||
</StyledLink>
|
||||
) : (
|
||||
@@ -78,7 +75,6 @@ export function BreadcrumbItemDropdown(props: {
|
||||
<DropdownMenu
|
||||
openOnHover
|
||||
openDelay={200}
|
||||
nativeButton={false}
|
||||
button={trigger}
|
||||
className="max-h-72 overflow-auto text-sm"
|
||||
>
|
||||
|
||||
@@ -27,7 +27,17 @@ export async function PageFooterNavigation(props: {
|
||||
const nextHref = next ? linker.toPathForPage({ pages: revision.pages, page: next }) : '';
|
||||
|
||||
return (
|
||||
<div className={tcls(CONTENT_STYLE, 'mt-6 flex @xl:flex-row flex-col gap-3 text-tint')}>
|
||||
<div
|
||||
className={tcls(
|
||||
CONTENT_STYLE,
|
||||
'flex',
|
||||
'flex-col',
|
||||
'md:flex-row',
|
||||
'mt-6',
|
||||
'gap-2',
|
||||
'text-tint'
|
||||
)}
|
||||
>
|
||||
{previous ? (
|
||||
<NavigationCard
|
||||
icon="chevron-left"
|
||||
@@ -44,6 +54,7 @@ export async function PageFooterNavigation(props: {
|
||||
position: SiteInsightsLinkPosition.Content,
|
||||
},
|
||||
}}
|
||||
reversed
|
||||
/>
|
||||
) : null}
|
||||
{next ? (
|
||||
@@ -62,7 +73,6 @@ export async function PageFooterNavigation(props: {
|
||||
position: SiteInsightsLinkPosition.Content,
|
||||
},
|
||||
}}
|
||||
reversed
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -85,24 +95,46 @@ function NavigationCard(
|
||||
href={href}
|
||||
insights={insights}
|
||||
className={tcls(
|
||||
'group flex flex-1 items-center @xl:gap-3 gap-2 text-pretty circular-corners:rounded-2xl rounded-corners:rounded-xl border border-tint-subtle @xl:p-4 p-3 @xl:text-base text-sm text-tint outline-tint-12 transition-colors hover:border-primary-hover contrast-more:border-tint-12 contrast-more:text-tint-strong contrast-more:hover:border-primary-12 contrast-more:hover:outline-2',
|
||||
reversed ? 'flex-row-reverse text-end' : 'text-start'
|
||||
'group',
|
||||
'text-sm',
|
||||
'p-2.5',
|
||||
'flex',
|
||||
'gap-4 ',
|
||||
'flex-1',
|
||||
reversed ? 'flex-row-reverse' : 'flex-row',
|
||||
'items-center',
|
||||
reversed ? null : 'pr-4',
|
||||
reversed ? 'pl-4' : null,
|
||||
'border',
|
||||
'border-tint-subtle',
|
||||
'rounded-sm',
|
||||
'circular-corners:rounded-2xl',
|
||||
'straight-corners:rounded-none',
|
||||
'hover:border-primary',
|
||||
'text-pretty',
|
||||
'md:p-4',
|
||||
'md:text-base'
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} className="size-3" />
|
||||
<div className="flex flex-col">
|
||||
<span className={tcls('flex', 'flex-col', 'flex-1', reversed ? 'text-right' : null)}>
|
||||
<span className={tcls('text-xs')}>{label}</span>
|
||||
<span
|
||||
className={tcls(
|
||||
'flex items-center gap-1 text-xs',
|
||||
reversed ? 'flex-row-reverse' : 'flex-row'
|
||||
)}
|
||||
className={tcls('text-tint-strong', 'group-hover:text-primary', 'line-clamp-2')}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
<span className="line-clamp-2 leading-snug text-tint-strong transition-colors group-hover:text-primary contrast-more:group-hover:text-primary-strong">
|
||||
{title}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<Icon
|
||||
icon={icon}
|
||||
className={tcls(
|
||||
'hidden',
|
||||
'size-4',
|
||||
'text-tint-subtle',
|
||||
'contrast-more:text-tint-strong',
|
||||
'group-hover:text-primary',
|
||||
'md:block'
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ export async function PageHeader(props: {
|
||||
// overlaps it: mark it here so it is never briefly unstyled before hydration.
|
||||
data-cover-aware-text
|
||||
data-over-cover
|
||||
className="page-cover-background:text-contrast-cover flow-root text-xs leading-relaxed text-tint layout-wide:page-no-toc:hidden page-cover-background:opacity-9"
|
||||
className="page-cover-background:text-contrast-cover text-xs leading-relaxed text-tint layout-wide:page-no-toc:hidden page-cover-background:opacity-9"
|
||||
>
|
||||
<ol className="inline">
|
||||
{contextCrumbs.map((crumb, index) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Tooltip } from '@base-ui/react/tooltip';
|
||||
import { TooltipProvider } from '@radix-ui/react-tooltip';
|
||||
import type React from 'react';
|
||||
|
||||
import { NavigationStatusProvider, ScrollPage } from '../hooks';
|
||||
@@ -19,12 +19,12 @@ export function RootLayoutClientContexts(props: {
|
||||
|
||||
return (
|
||||
<TranslateContext.Provider value={language}>
|
||||
<Tooltip.Provider delay={200}>
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<NavigationStatusProvider>
|
||||
<LoadingStateProvider>{children}</LoadingStateProvider>
|
||||
<ScrollPage />
|
||||
</NavigationStatusProvider>
|
||||
</Tooltip.Provider>
|
||||
</TooltipProvider>
|
||||
</TranslateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Radix renders tooltip content inside a portaled `[data-radix-popper-content-wrapper]` attached
|
||||
to <body>. A non-interactive tooltip (Tooltip with `disableHoverableContent`) marks its content
|
||||
with `data-non-interactive`; without this rule the pointer-interactive wrapper could sit over
|
||||
the trigger and steal its hover (e.g. breaking hover-revealed controls). We can't style the
|
||||
wrapper from React, so let pointer events pass through it for these tooltips only.
|
||||
*/
|
||||
[data-radix-popper-content-wrapper]:has([data-non-interactive]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@utility linear-mask-gradient {
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
@@ -540,15 +551,3 @@ html.dark .highlight-line.diff-deleted .highlight-line-content::before {
|
||||
@apply pl-2;
|
||||
}
|
||||
}
|
||||
|
||||
/* An open dropdown injects focus guards as siblings of its trigger, so `:last-child` is not the
|
||||
last item of a button group: match the buttons and links themselves. */
|
||||
.button-group > :is(button, a):not(:only-child):first-child {
|
||||
@apply rounded-r-none;
|
||||
}
|
||||
.button-group > :is(button, a):not(:first-child):not(:has(~ :is(button, a))) {
|
||||
@apply rounded-l-none;
|
||||
}
|
||||
.button-group > :is(button, a):not(:first-child):has(~ :is(button, a)) {
|
||||
@apply rounded-none!;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,42 @@ export function SearchContainer({
|
||||
aria-controls={resultsId}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Popover
|
||||
content={searchFrame}
|
||||
rootProps={{
|
||||
open: isSearchOpen,
|
||||
modal: false,
|
||||
}}
|
||||
contentProps={{
|
||||
onOpenAutoFocus: (event) => event.preventDefault(),
|
||||
align: 'start',
|
||||
className: tcls(
|
||||
'@container flex flex-col overflow-hidden bg-tint-base has-[.empty]:hidden w-128 p-0 max-w-[min(var(--radix-popover-content-available-width),32rem)]',
|
||||
shouldFillHeight
|
||||
? 'h-[min(32rem,var(--radix-popover-content-available-height))]'
|
||||
: 'max-h-[min(32rem,var(--radix-popover-content-available-height))]'
|
||||
),
|
||||
onInteractOutside: (event) => {
|
||||
// Don't close if clicking on the search input itself
|
||||
if (searchInputRef.current?.contains(event.target as Node)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
close();
|
||||
},
|
||||
sideOffset: 8,
|
||||
collisionPadding: {
|
||||
top: 16,
|
||||
right: 16,
|
||||
bottom: 32,
|
||||
left: 16,
|
||||
},
|
||||
hideWhenDetached: true,
|
||||
}}
|
||||
triggerProps={{
|
||||
asChild: true,
|
||||
}}
|
||||
>
|
||||
<SearchInput
|
||||
ref={searchInputRef}
|
||||
aria-activedescendant={searchResultsActiveDescendant}
|
||||
@@ -224,54 +259,7 @@ export function SearchContainer({
|
||||
showing={Boolean(searchValue) && !fetching}
|
||||
/>
|
||||
</SearchInput>
|
||||
{/* Anchored, not triggered: a trigger gives the input button semantics and
|
||||
swallows the space bar. */}
|
||||
<Popover
|
||||
content={searchFrame}
|
||||
anchor={searchInputRef}
|
||||
rootProps={{
|
||||
open: isSearchOpen,
|
||||
onOpenChange: (nextOpen, eventDetails) => {
|
||||
if (nextOpen) {
|
||||
open();
|
||||
return;
|
||||
}
|
||||
// Without a trigger, the input counts as outside the popover.
|
||||
if (
|
||||
eventDetails.reason === 'outside-press' &&
|
||||
searchInputRef.current?.contains(
|
||||
eventDetails.event.target as Node
|
||||
)
|
||||
) {
|
||||
eventDetails.cancel();
|
||||
return;
|
||||
}
|
||||
close();
|
||||
},
|
||||
}}
|
||||
positionerProps={{
|
||||
align: 'start',
|
||||
sideOffset: 8,
|
||||
collisionPadding: {
|
||||
top: 16,
|
||||
right: 16,
|
||||
bottom: 32,
|
||||
left: 16,
|
||||
},
|
||||
}}
|
||||
popupProps={{
|
||||
initialFocus: false,
|
||||
// Restoring focus to the input would re-fire `onFocus` and reopen it.
|
||||
finalFocus: false,
|
||||
className: tcls(
|
||||
'@container flex flex-col overflow-hidden bg-tint-base has-[.empty]:hidden w-128 p-0 max-w-[min(var(--available-width),32rem)]',
|
||||
shouldFillHeight
|
||||
? 'h-[min(32rem,var(--available-height))]'
|
||||
: 'max-h-[min(32rem,var(--available-height))]'
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Popover>
|
||||
)}
|
||||
{usesSideSheet ? (
|
||||
<SideSheet
|
||||
|
||||
@@ -177,7 +177,7 @@ const Breadcrumbs = (props: { breadcrumbs: PageItem['breadcrumbs']; withOverflow
|
||||
<Tooltip
|
||||
key="breadcrumbs-overflow"
|
||||
label={formatCrumbs(crumbs.slice(3, -1))}
|
||||
delay={0}
|
||||
rootProps={{ delayDuration: 0 }}
|
||||
arrow
|
||||
>
|
||||
<span>
|
||||
|
||||
@@ -163,16 +163,16 @@ export function useSearchLink(): (
|
||||
return {
|
||||
href: `?${searchParams.toString()}`,
|
||||
prefetch: false,
|
||||
onClick: async (event) => {
|
||||
onClick: (event) => {
|
||||
event.preventDefault();
|
||||
await setSearchState((prev) => ({
|
||||
callback?.();
|
||||
setSearchState((prev) => ({
|
||||
...prev,
|
||||
query: params.query !== undefined ? params.query : null,
|
||||
ask: params.ask !== undefined ? params.ask : null,
|
||||
scope: params.scope !== undefined ? params.scope : 'default',
|
||||
open: params.open !== undefined ? params.open : false,
|
||||
}));
|
||||
callback?.();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -271,7 +271,7 @@ export function useSearchController(
|
||||
}, [abort, siteSpace.id]);
|
||||
|
||||
const askInAssistant = React.useCallback(
|
||||
async (assistantIndex = 0) => {
|
||||
(assistantIndex = 0) => {
|
||||
const assistant = assistants[assistantIndex];
|
||||
if (!assistant || !normalizedQuery) {
|
||||
return;
|
||||
@@ -282,13 +282,13 @@ export function useSearchController(
|
||||
}
|
||||
|
||||
abort();
|
||||
await setSearchState({
|
||||
assistant.open(normalizedQuery);
|
||||
setSearchState({
|
||||
ask: normalizedQuery,
|
||||
query: null,
|
||||
scope: state?.scope ?? 'default',
|
||||
open: assistant.mode === 'search',
|
||||
});
|
||||
assistant.open(normalizedQuery);
|
||||
},
|
||||
[abort, assistants, normalizedQuery, setSearchState, siteSpace.id, state?.scope]
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ export function ConsentForm(props: {
|
||||
<Checkbox
|
||||
id="site-oauth-trusted"
|
||||
checked={trusted}
|
||||
onCheckedChange={setTrusted}
|
||||
onCheckedChange={(value) => setTrusted(value === true)}
|
||||
/>
|
||||
<span>I recognize and trust this client</span>
|
||||
</label>
|
||||
|
||||
@@ -27,11 +27,7 @@ import { getResizedImageURL } from '@/lib/images';
|
||||
import { getPagePath } from '@/lib/pages';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { isPageIndexable, isSiteIndexable } from '@/lib/seo';
|
||||
import {
|
||||
getSiteSpacePagePaths,
|
||||
getSiteStructureTitle,
|
||||
resolveSiteSpaceCustomHomePage,
|
||||
} from '@/lib/sites';
|
||||
import { getSiteStructureTitle } from '@/lib/sites';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { getDocumentFilterableTags } from '@/lib/updates';
|
||||
import { getPageRSSURL } from '@/routes/rss';
|
||||
@@ -269,25 +265,13 @@ export async function getSitePageData(props: SitePageProps) {
|
||||
}
|
||||
notFound();
|
||||
}
|
||||
} else {
|
||||
const customHomePage = resolveSiteSpaceCustomHomePage(
|
||||
context.siteSpace,
|
||||
context.revision.pages
|
||||
} else if (getPagePath(context.revision.pages, pageTarget.page) !== rawPathname) {
|
||||
redirect(
|
||||
context.linker.toPathForPage({
|
||||
pages: context.revision.pages,
|
||||
page: pageTarget.page,
|
||||
})
|
||||
);
|
||||
// Without a custom home page, a page has a single canonical path. With one, the custom
|
||||
// home page also gains the site space's root as a valid alias.
|
||||
const expectedPagePaths = customHomePage
|
||||
? getSiteSpacePagePaths(context.siteSpace, context.revision.pages, pageTarget.page)
|
||||
: [getPagePath(context.revision.pages, pageTarget.page)];
|
||||
|
||||
if (!expectedPagePaths.includes(rawPathname)) {
|
||||
redirect(
|
||||
context.linker.toPathForPage({
|
||||
pages: context.revision.pages,
|
||||
page: pageTarget.page,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { customization, visibleSections } = context;
|
||||
|
||||
@@ -7,9 +7,8 @@ import {
|
||||
|
||||
import type { GitBookSiteContext } from '@/lib/context';
|
||||
import { getDataOrNull } from '@/lib/data';
|
||||
import { resolvePageId } from '@/lib/pages';
|
||||
import { resolvePageId, resolvePagePath } from '@/lib/pages';
|
||||
import { withLeadingSlash } from '@/lib/paths';
|
||||
import { resolveSiteSpacePagePath } from '@/lib/sites';
|
||||
|
||||
export interface PagePathParams {
|
||||
pathname?: string | string[];
|
||||
@@ -52,7 +51,7 @@ async function resolvePage(context: GitBookSiteContext, params: PagePathParams |
|
||||
const pathname = rawPathname.toLowerCase();
|
||||
|
||||
// When resolving a page, we use the lowercased pathname
|
||||
const page = resolveSiteSpacePagePath(context.siteSpace, revision.pages, pathname);
|
||||
const page = resolvePagePath(revision.pages, pathname);
|
||||
if (page) {
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { NavigationMenu } from '@base-ui/react/navigation-menu';
|
||||
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
|
||||
import React from 'react';
|
||||
|
||||
import type { IconName } from '@gitbook/icons';
|
||||
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { CONTAINER_STYLE } from '../layout';
|
||||
import { ScrollContainer } from '../primitives/ScrollContainer';
|
||||
import type {
|
||||
@@ -17,10 +18,8 @@ import { Button, Link, ToggleChevron } from '@/components/primitives';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { findSectionInGroup } from '@/lib/utils';
|
||||
|
||||
const DESKTOP_BREAKPOINT = 768;
|
||||
const SCREEN_OFFSET = 16; // 1rem
|
||||
const POPUP_OFFSET = 4;
|
||||
const OPEN_DELAY_MS = 200;
|
||||
const MOTION = 'duration-300 ease-[cubic-bezier(0.83,0,0.17,1)]';
|
||||
const MAX_ITEMS_PER_COLUMN = 10; // number of items per column
|
||||
const GROUP_MASONRY_THRESHOLD = 3; // if a section group has more than this many child groups, it will be shown in a masonry grid
|
||||
const COLUMN_WIDTH = '18rem';
|
||||
@@ -41,25 +40,40 @@ export function SiteSectionTabs(props: {
|
||||
children,
|
||||
} = props;
|
||||
|
||||
// Portalled into the tabs container rather than <body>, to keep inheriting the header's theming.
|
||||
const containerRef = React.useRef<HTMLElement>(null);
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const currentTriggerRef = React.useRef<HTMLButtonElement | null>(null);
|
||||
const [value, setValue] = React.useState<string | undefined>();
|
||||
|
||||
const isMobile = useIsMobile(DESKTOP_BREAKPOINT);
|
||||
const offset = useNavigationMenuViewportOffset({
|
||||
value,
|
||||
isMobile,
|
||||
triggerRef: currentTriggerRef,
|
||||
containerRef,
|
||||
});
|
||||
const viewportLeft =
|
||||
!isMobile && offset !== null
|
||||
? `clamp(0px, calc(${offset - SCREEN_OFFSET}px - var(--radix-navigation-menu-viewport-width, 0px)/2), calc(100% - var(--radix-navigation-menu-viewport-width, 0px)))`
|
||||
: '0px';
|
||||
|
||||
return structure.length > 0 ? (
|
||||
<NavigationMenu.Root
|
||||
ref={containerRef}
|
||||
className={tcls(
|
||||
CONTAINER_STYLE,
|
||||
'relative z-10 flex w-full flex-nowrap items-end',
|
||||
'page-default-width:2xl:px-[calc((100%-1536px+4rem)/2)]',
|
||||
className
|
||||
)}
|
||||
ref={containerRef}
|
||||
style={
|
||||
{
|
||||
'--site-section-column-width': COLUMN_WIDTH,
|
||||
'--site-section-column-gap': COLUMN_GAP,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
delay={OPEN_DELAY_MS}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
skipDelayDuration={500}
|
||||
>
|
||||
<ScrollContainer
|
||||
orientation="horizontal"
|
||||
@@ -98,45 +112,49 @@ export function SiteSectionTabs(props: {
|
||||
{isGroup && structureItem.children.length > 0 ? (
|
||||
<>
|
||||
<NavigationMenu.Trigger
|
||||
render={
|
||||
<SectionTab
|
||||
isActive={isActive}
|
||||
title={title}
|
||||
icon={icon as IconName}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<NavigationMenu.Content
|
||||
className={tcls(
|
||||
'h-full w-[calc(100vw-2rem)] overflow-y-auto overflow-x-hidden md:w-max md:max-w-(--available-width)',
|
||||
`transition-[opacity,translate] ${MOTION}`,
|
||||
'data-ending-style:opacity-0 data-starting-style:opacity-0',
|
||||
'data-starting-style:data-[activation-direction=left]:-translate-x-1/2 data-ending-style:data-[activation-direction=left]:translate-x-1/2',
|
||||
'data-ending-style:data-[activation-direction=right]:-translate-x-1/2 data-starting-style:data-[activation-direction=right]:translate-x-1/2'
|
||||
)}
|
||||
asChild
|
||||
ref={value === id ? currentTriggerRef : undefined}
|
||||
onClick={(e) => {
|
||||
// Prevent clicking the trigger from closing when the viewport is open
|
||||
if (value === id) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SectionGroupTileList
|
||||
items={structureItem.children}
|
||||
currentSection={currentSection}
|
||||
<SectionTab
|
||||
isActive={isActive}
|
||||
title={title}
|
||||
icon={icon as IconName}
|
||||
/>
|
||||
</NavigationMenu.Trigger>
|
||||
<NavigationMenu.Content
|
||||
className={tcls([
|
||||
'absolute top-0 left-0 w-full md:w-auto',
|
||||
'data-[motion=from-start]:*:animate-[enterFromLeft_300ms_ease_both] data-[motion=to-end]:*:animate-[exitToRight_300ms_ease_both] data-[motion=to-start]:*:animate-[exitToLeft_300ms_ease_both] motion-safe:data-[motion=from-end]:*:animate-[enterFromRight_300ms_ease_both]',
|
||||
])}
|
||||
>
|
||||
<div className="max-h-[calc(100vh-8rem)] w-full overflow-y-auto overflow-x-hidden rounded-corners:rounded-xl circular-corners:rounded-3xl">
|
||||
<SectionGroupTileList
|
||||
items={structureItem.children}
|
||||
currentSection={currentSection}
|
||||
/>
|
||||
</div>
|
||||
</NavigationMenu.Content>
|
||||
</>
|
||||
) : (
|
||||
<NavigationMenu.Link
|
||||
active={isActive}
|
||||
render={
|
||||
<SectionTab
|
||||
url={
|
||||
structureItem.object === 'site-section'
|
||||
? structureItem.url
|
||||
: undefined
|
||||
}
|
||||
isActive={isActive}
|
||||
title={title}
|
||||
icon={icon ? (icon as IconName) : undefined}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<NavigationMenu.Link asChild>
|
||||
<SectionTab
|
||||
url={
|
||||
structureItem.object === 'site-section'
|
||||
? structureItem.url
|
||||
: undefined
|
||||
}
|
||||
isActive={isActive}
|
||||
title={title}
|
||||
icon={icon ? (icon as IconName) : undefined}
|
||||
/>
|
||||
</NavigationMenu.Link>
|
||||
)}
|
||||
</NavigationMenu.Item>
|
||||
);
|
||||
@@ -146,36 +164,70 @@ export function SiteSectionTabs(props: {
|
||||
|
||||
{children}
|
||||
|
||||
<NavigationMenu.Portal container={containerRef}>
|
||||
<NavigationMenu.Positioner
|
||||
<div
|
||||
className="absolute left-0 top-full z-20 flex w-full"
|
||||
style={{
|
||||
paddingInline: `${SCREEN_OFFSET}px`,
|
||||
// Force a stacking context on this wrapper (rather than on the Viewport) to fix a rendering bug on
|
||||
// Safari. Keeping it here — off the clipped Viewport — avoids a Chromium bug where a clipped,
|
||||
// composited layer drops its text inside an iframe (empty dropdowns on embedded sites).
|
||||
transform: 'translateZ(0)',
|
||||
}}
|
||||
>
|
||||
<NavigationMenu.Viewport
|
||||
className={tcls(
|
||||
'z-20 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) outline-hidden',
|
||||
`transition-[top,left,right,bottom] ${MOTION} data-instant:transition-none`
|
||||
// `overflow-hidden` clips the content to this animating box, keeping it bounded by the rounded
|
||||
// container as the Viewport resizes/scales. The stacking context Safari needs lives on the
|
||||
// parent wrapper (translateZ), deliberately not here — see the note above.
|
||||
'relative origin-[center_top] overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-xl border border-tint bg-tint-base shadow-lg',
|
||||
'-mt-0.5 h-(--radix-navigation-menu-viewport-height) w-full max-w-full md:w-(--radix-navigation-menu-viewport-width)',
|
||||
'max-h-[calc(100vh-8rem)] data-[state=closed]:animate-scale-out data-[state=open]:animate-scale-in',
|
||||
'ease has-[&[data-motion]]:transition-[left,width,height] has-[&[data-motion]]:duration-300'
|
||||
)}
|
||||
sideOffset={POPUP_OFFSET}
|
||||
collisionPadding={SCREEN_OFFSET}
|
||||
// Flipping above the tabs would hide the popup behind the header.
|
||||
collisionAvoidance={{ side: 'none' }}
|
||||
>
|
||||
<NavigationMenu.Popup
|
||||
className={tcls(
|
||||
'relative h-(--popup-height) max-h-[calc(100vh-8rem)] w-(--popup-width) origin-(--transform-origin) overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-xl border border-tint bg-tint-base shadow-lg outline-hidden',
|
||||
// `scale` rather than `transform`: that is what `scale-95` sets.
|
||||
`transition-[opacity,scale,width,height] ${MOTION}`,
|
||||
// The size vars reset on close, so animating them out collapses the
|
||||
// panel and grows it back as it fades.
|
||||
'data-ending-style:transition-[opacity,scale]',
|
||||
'data-ending-style:scale-95 data-starting-style:scale-95 data-ending-style:opacity-0 data-starting-style:opacity-0'
|
||||
)}
|
||||
>
|
||||
<NavigationMenu.Viewport className="relative h-full w-full overflow-hidden" />
|
||||
</NavigationMenu.Popup>
|
||||
</NavigationMenu.Positioner>
|
||||
</NavigationMenu.Portal>
|
||||
style={{
|
||||
left: viewportLeft,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</NavigationMenu.Root>
|
||||
) : null;
|
||||
}
|
||||
|
||||
function useNavigationMenuViewportOffset(args: {
|
||||
value: string | undefined;
|
||||
isMobile: boolean;
|
||||
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
}) {
|
||||
const { value, isMobile, triggerRef, containerRef } = args;
|
||||
const [offset, setOffset] = React.useState<number | null>(null);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (isMobile) {
|
||||
setOffset(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = triggerRef.current;
|
||||
const container = containerRef.current;
|
||||
if (!trigger || !container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const containerLeft = container.getBoundingClientRect().left;
|
||||
const triggerWidth = trigger.getBoundingClientRect().width;
|
||||
const triggerLeft = trigger.getBoundingClientRect().left - containerLeft;
|
||||
|
||||
setOffset(triggerLeft + triggerWidth / 2);
|
||||
}, [containerRef, isMobile, triggerRef, value]);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tab representing a section or section group
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,7 @@ export function ToggleableLinkItem(
|
||||
{tag ? (
|
||||
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
|
||||
{children}
|
||||
{tag}
|
||||
<div className="flex shrink-0 items-center">{tag}</div>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
@@ -73,12 +73,17 @@ export function ToggleableLinkItem(
|
||||
{tag ? (
|
||||
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
|
||||
{children}
|
||||
{tag}
|
||||
<div className="flex shrink-0 items-center">
|
||||
{tag}
|
||||
{toggler}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
<>
|
||||
{children}
|
||||
{toggler}
|
||||
</>
|
||||
)}
|
||||
{toggler}
|
||||
</LinkItem>
|
||||
{descendants}
|
||||
</>
|
||||
|
||||
@@ -4,9 +4,8 @@ import type { ContentRef, RevisionPage, RevisionPageTag, RevisionTag } from '@gi
|
||||
|
||||
import type { GitBookSiteContext } from '@/lib/context';
|
||||
import { getOpenAPIOperationPageProps } from '@/lib/openapi/computedSourceProps';
|
||||
import { hasPageVisibleDescendant } from '@/lib/pages';
|
||||
import { getPagePaths, hasPageVisibleDescendant } from '@/lib/pages';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { getSiteSpacePagePaths } from '@/lib/sites';
|
||||
import { getRevisionTags, resolveTag } from '@/lib/tags';
|
||||
import { removeUndefined } from '@/lib/typescript';
|
||||
|
||||
@@ -85,7 +84,7 @@ export async function encodeClientTableOfContents(
|
||||
href,
|
||||
emoji: page.emoji,
|
||||
icon: page.icon,
|
||||
pathnames: getSiteSpacePagePaths(context.siteSpace, rootPages, page),
|
||||
pathnames: getPagePaths(rootPages, page),
|
||||
descendants,
|
||||
primaryTag,
|
||||
openAPIOperation: getOpenAPIOperationPageProps(page),
|
||||
|
||||
@@ -17,7 +17,7 @@ export function Tag(props: { tag: RevisionTag; className?: string }) {
|
||||
data-tag=""
|
||||
title={tag.label}
|
||||
className={tcls(
|
||||
'inline-flex max-w-full shrink-0 items-center gap-1 rounded-full px-2 py-1 font-medium text-xs leading-normal contrast-more:ring-1 contrast-more:ring-tint',
|
||||
'inline-flex items-center gap-1 rounded-full px-2 py-1 font-medium text-xs leading-normal contrast-more:ring-1 contrast-more:ring-tint',
|
||||
hasColor
|
||||
? [backgroundColorToStyle[tag.color], textColorToStyle[tag.color]]
|
||||
: 'bg-tint-5 text-tint-strong',
|
||||
|
||||
@@ -212,7 +212,15 @@ export const Button = React.forwardRef<
|
||||
);
|
||||
|
||||
return (children || iconOnly) && label ? (
|
||||
<Tooltip label={label} disabled={disabled} {...tooltipProps}>
|
||||
<Tooltip
|
||||
rootProps={{
|
||||
open: disabled === true ? false : undefined,
|
||||
...tooltipProps?.rootProps,
|
||||
}}
|
||||
label={label}
|
||||
triggerProps={{ disabled, ...tooltipProps?.triggerProps }}
|
||||
contentProps={tooltipProps?.contentProps}
|
||||
>
|
||||
{button}
|
||||
</Tooltip>
|
||||
) : (
|
||||
@@ -231,13 +239,7 @@ export const ButtonGroup = React.forwardRef<
|
||||
className={tcls(
|
||||
'flex h-fit items-stretch justify-start overflow-hidden',
|
||||
combinedShape
|
||||
? [
|
||||
// Per-segment rounding lives in `globals.css`: Tailwind cannot express
|
||||
// "last button" once focus guards are injected.
|
||||
'button-group',
|
||||
'*:translate-y-0! *:shadow-none!',
|
||||
'[&>*:not(:first-child)]:border-l-0',
|
||||
]
|
||||
? '*: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'
|
||||
: '',
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import React from 'react';
|
||||
|
||||
import { Icon, IconStyle } from '@gitbook/icons';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export type CheckboxProps = Omit<BaseCheckbox.Root.Props, 'className'> & {
|
||||
className?: string;
|
||||
export type CheckboxProps = React.ComponentProps<typeof CheckboxPrimitive.Root> & {
|
||||
/**
|
||||
* The size of the checkbox.
|
||||
* @default medium
|
||||
@@ -15,31 +15,36 @@ export type CheckboxProps = Omit<BaseCheckbox.Root.Props, 'className'> & {
|
||||
size?: 'small' | 'medium';
|
||||
};
|
||||
|
||||
export function Checkbox({ className, size = 'medium', ...props }: CheckboxProps) {
|
||||
return (
|
||||
<BaseCheckbox.Root
|
||||
className={tcls(
|
||||
'peer',
|
||||
'shrink-0',
|
||||
'rounded-xs',
|
||||
'straight-corners:rounded-none',
|
||||
'ring-1',
|
||||
'ring-tint-12/4',
|
||||
'ring-inset',
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'data-checked:bg-primary-original',
|
||||
'data-checked:text-contrast-primary-original',
|
||||
'contrast-more:ring-tint-12',
|
||||
{ small: 'size-4', medium: 'size-5' }[size],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<BaseCheckbox.Indicator className="relative text-current">
|
||||
<Icon icon="check" iconStyle={IconStyle.Solid} className="size-3" />
|
||||
</BaseCheckbox.Indicator>
|
||||
</BaseCheckbox.Root>
|
||||
);
|
||||
}
|
||||
export const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
CheckboxProps
|
||||
>(({ className, size = 'medium', ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={tcls(
|
||||
'peer',
|
||||
'shrink-0',
|
||||
'rounded-xs',
|
||||
'straight-corners:rounded-none',
|
||||
'ring-1',
|
||||
'ring-tint-12/4',
|
||||
'ring-inset',
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'data-[state=checked]:bg-primary-original',
|
||||
'data-[state=checked]:text-contrast-primary-original',
|
||||
'contrast-more:ring-tint-12',
|
||||
{ small: 'size-4', medium: 'size-5' }[size],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator className="relative text-current">
|
||||
{props.checked ? (
|
||||
<Icon icon="check" iconStyle={IconStyle.Solid} className={'size-3'} />
|
||||
) : null}
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||
|
||||
@@ -1,48 +1,63 @@
|
||||
'use client';
|
||||
|
||||
import { Collapsible as BaseCollapsible } from '@base-ui/react/collapsible';
|
||||
import * as RadixCollapsible from '@radix-ui/react-collapsible';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export function Collapsible(
|
||||
props: Omit<BaseCollapsible.Root.Props, 'className'> & {
|
||||
props: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
} & RadixCollapsible.CollapsibleProps
|
||||
) {
|
||||
const { children, className, ...rest } = props;
|
||||
const { children, open, defaultOpen, onOpenChange, className, ...rest } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (open !== undefined) {
|
||||
setOpenState(open);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const [openState, setOpenState] = useState<boolean>(open ?? defaultOpen ?? false);
|
||||
|
||||
return (
|
||||
<BaseCollapsible.Root {...rest} className={tcls('group/collapsible', className)}>
|
||||
<RadixCollapsible.Root
|
||||
{...rest}
|
||||
className={tcls('group/collapsible', className)}
|
||||
open={openState}
|
||||
onOpenChange={(nextOpen) => {
|
||||
setOpenState(nextOpen);
|
||||
onOpenChange?.(nextOpen);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BaseCollapsible.Root>
|
||||
</RadixCollapsible.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export function CollapsibleTrigger(
|
||||
props: BaseCollapsible.Trigger.Props & {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
props: {
|
||||
children: React.ReactNode;
|
||||
} & RadixCollapsible.CollapsibleTriggerProps
|
||||
) {
|
||||
return <BaseCollapsible.Trigger {...props} />;
|
||||
return <RadixCollapsible.Trigger {...props}>{props.children}</RadixCollapsible.Trigger>;
|
||||
}
|
||||
|
||||
export function CollapsibleContent(
|
||||
props: Omit<BaseCollapsible.Panel.Props, 'className'> & {
|
||||
props: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
} & RadixCollapsible.CollapsibleContentProps
|
||||
) {
|
||||
const { children, className, ...rest } = props;
|
||||
return (
|
||||
<BaseCollapsible.Panel
|
||||
{...rest}
|
||||
<RadixCollapsible.Content
|
||||
className={tcls(
|
||||
'data-closed:animate-[blurOut_300ms,heightOut_300ms] data-open:animate-[blurIn_300ms,heightIn_300ms]',
|
||||
'data-[state=closed]:animate-[blurOut_300ms,heightOut_300ms] data-[state=open]:animate-[blurIn_300ms,heightIn_300ms]',
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</BaseCollapsible.Panel>
|
||||
</RadixCollapsible.Content>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { Menu } from '@base-ui/react/menu';
|
||||
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
||||
import { createContext, useCallback, useContext, useState } from 'react';
|
||||
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { assert } from 'ts-essentials';
|
||||
|
||||
import { Icon, type IconName } from '@gitbook/icons';
|
||||
@@ -25,20 +25,31 @@ const DropdownMenuContext = createContext<{
|
||||
setOpen: () => {},
|
||||
});
|
||||
|
||||
const DROPDOWN_POSITIONER_CLASS =
|
||||
'z-50 flex max-h-(--available-height) min-w-28 xs:min-w-40 max-w-(--available-width) flex-col pt-2 outline-hidden data-anchor-hidden:hidden';
|
||||
// The exit animation has to live on the popup: Base UI keeps it mounted until *its* animations end.
|
||||
const DROPDOWN_POPUP_CLASS =
|
||||
'flex origin-(--transform-origin) animate-scale-in flex-col gap-1 overflow-auto circular-corners:rounded-xl rounded-md straight-corners:rounded-none border border-tint bg-tint-base p-2 shadow-lg outline-hidden data-closed:animate-scale-out';
|
||||
const DROPDOWN_CONTENT_OUTER_CLASS =
|
||||
'z-50 flex max-h-(--radix-dropdown-menu-content-available-height) min-w-28 xs:min-w-40 max-w-(--radix-dropdown-menu-content-available-width) origin-top animate-scale-in flex-col pt-2 data-[state=closed]:animate-scale-out';
|
||||
const DROPDOWN_CONTENT_INNER_CLASS =
|
||||
'flex flex-col gap-1 overflow-auto circular-corners:rounded-xl rounded-md straight-corners:rounded-none border border-tint bg-tint-base p-2 shadow-lg';
|
||||
|
||||
/**
|
||||
* Grace period before a hover-opened menu closes, so the pointer can travel from the trigger to the
|
||||
* (portalled) menu without it collapsing. Only used when `openDelay` is set.
|
||||
*/
|
||||
const HOVER_CLOSE_DELAY_MS = 150;
|
||||
|
||||
/** Clear a pending timeout ref, if any. */
|
||||
function clearTimer(ref: { current: ReturnType<typeof setTimeout> | null }) {
|
||||
if (ref.current) {
|
||||
clearTimeout(ref.current);
|
||||
ref.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Button with a dropdown.
|
||||
*/
|
||||
export function DropdownMenu(props: {
|
||||
/** Content of the button */
|
||||
button: React.ReactElement<Record<string, unknown>>;
|
||||
button: React.ReactNode;
|
||||
/** Tooltip label for the button */
|
||||
buttonTooltip?: React.ReactNode;
|
||||
/** Content of the dropdown */
|
||||
@@ -52,22 +63,21 @@ export function DropdownMenu(props: {
|
||||
* @default 0
|
||||
*/
|
||||
openDelay?: number;
|
||||
nativeButton?: boolean;
|
||||
/**
|
||||
* Side of the dropdown
|
||||
* @default "bottom"
|
||||
*/
|
||||
side?: Menu.Positioner.Props['side'];
|
||||
side?: RadixDropdownMenu.DropdownMenuContentProps['side'];
|
||||
/**
|
||||
* Alignment of the dropdown
|
||||
* @default "start"
|
||||
*/
|
||||
align?: Menu.Positioner.Props['align'];
|
||||
align?: RadixDropdownMenu.DropdownMenuContentProps['align'];
|
||||
/**
|
||||
* Distance between the trigger and the dropdown.
|
||||
* @default 0
|
||||
*/
|
||||
sideOffset?: Menu.Positioner.Props['sideOffset'];
|
||||
sideOffset?: RadixDropdownMenu.DropdownMenuContentProps['sideOffset'];
|
||||
}) {
|
||||
const {
|
||||
button,
|
||||
@@ -76,52 +86,101 @@ export function DropdownMenu(props: {
|
||||
className,
|
||||
openOnHover = false,
|
||||
openDelay = 0,
|
||||
nativeButton,
|
||||
side = 'bottom',
|
||||
align = 'start',
|
||||
sideOffset = 0,
|
||||
} = props;
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Timers used to delay opening on hover (and to keep the menu open while the pointer travels
|
||||
// between the trigger and the menu).
|
||||
const openTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const closeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
// Clear any pending timers when unmounting.
|
||||
useEffect(
|
||||
() => () => {
|
||||
clearTimer(openTimeout);
|
||||
clearTimer(closeTimeout);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleHoverEnter = useCallback(() => {
|
||||
clearTimer(closeTimeout);
|
||||
if (openDelay > 0) {
|
||||
if (!openTimeout.current) {
|
||||
openTimeout.current = setTimeout(() => {
|
||||
openTimeout.current = null;
|
||||
setHovered(true);
|
||||
}, openDelay);
|
||||
}
|
||||
} else {
|
||||
setHovered(true);
|
||||
}
|
||||
}, [openDelay]);
|
||||
|
||||
const handleHoverLeave = useCallback(() => {
|
||||
clearTimer(openTimeout);
|
||||
clearTimer(closeTimeout);
|
||||
if (openDelay > 0) {
|
||||
// Grace period so moving between the trigger and the menu doesn't close it.
|
||||
closeTimeout.current = setTimeout(() => {
|
||||
closeTimeout.current = null;
|
||||
setHovered(false);
|
||||
}, HOVER_CLOSE_DELAY_MS);
|
||||
} else {
|
||||
setHovered(false);
|
||||
}
|
||||
}, [openDelay]);
|
||||
|
||||
const isOpen = openOnHover ? open || hovered : open;
|
||||
|
||||
const trigger = (
|
||||
<Menu.Trigger
|
||||
render={button}
|
||||
nativeButton={nativeButton}
|
||||
openOnHover={openOnHover}
|
||||
delay={openDelay}
|
||||
closeDelay={HOVER_CLOSE_DELAY_MS}
|
||||
<RadixDropdownMenu.Trigger
|
||||
asChild
|
||||
onMouseEnter={handleHoverEnter}
|
||||
onMouseLeave={handleHoverLeave}
|
||||
onClick={() => (openOnHover ? setOpen(!open) : null)}
|
||||
className="group/dropdown"
|
||||
/>
|
||||
>
|
||||
{button}
|
||||
</RadixDropdownMenu.Trigger>
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenuContext.Provider value={{ open, setOpen }}>
|
||||
<Menu.Root modal={false} open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuContext.Provider value={{ open: isOpen, setOpen }}>
|
||||
<RadixDropdownMenu.Root modal={false} open={isOpen} onOpenChange={setOpen}>
|
||||
{buttonTooltip ? (
|
||||
<Tooltip label={buttonTooltip} disableHoverablePopup>
|
||||
<Tooltip
|
||||
label={buttonTooltip}
|
||||
pinOnClick={false}
|
||||
rootProps={{ disableHoverableContent: true }}
|
||||
>
|
||||
{trigger}
|
||||
</Tooltip>
|
||||
) : (
|
||||
trigger
|
||||
)}
|
||||
|
||||
<Menu.Portal>
|
||||
<Menu.Positioner
|
||||
<RadixDropdownMenu.Portal>
|
||||
<RadixDropdownMenu.Content
|
||||
data-testid="dropdown-menu"
|
||||
hideWhenDetached
|
||||
collisionPadding={8}
|
||||
onMouseEnter={handleHoverEnter}
|
||||
onMouseLeave={handleHoverLeave}
|
||||
align={align}
|
||||
side={side}
|
||||
sideOffset={sideOffset}
|
||||
className={DROPDOWN_POSITIONER_CLASS}
|
||||
className={DROPDOWN_CONTENT_OUTER_CLASS}
|
||||
>
|
||||
<Menu.Popup
|
||||
data-testid="dropdown-menu"
|
||||
className={tcls(DROPDOWN_POPUP_CLASS, className)}
|
||||
>
|
||||
<div className={tcls(DROPDOWN_CONTENT_INNER_CLASS, className)}>
|
||||
{children}
|
||||
</Menu.Popup>
|
||||
</Menu.Positioner>
|
||||
</Menu.Portal>
|
||||
</Menu.Root>
|
||||
</div>
|
||||
</RadixDropdownMenu.Content>
|
||||
</RadixDropdownMenu.Portal>
|
||||
</RadixDropdownMenu.Root>
|
||||
</DropdownMenuContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -152,7 +211,7 @@ export function DropdownMenuItem(
|
||||
children: React.ReactNode;
|
||||
leadingIcon?: IconName | React.ReactNode;
|
||||
} & LinkInsightsProps &
|
||||
Omit<Menu.Item.Props, 'className' | 'render'>
|
||||
RadixDropdownMenu.DropdownMenuItemProps
|
||||
) {
|
||||
const {
|
||||
children,
|
||||
@@ -188,22 +247,20 @@ export function DropdownMenuItem(
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Menu.Item
|
||||
{...rest}
|
||||
className={itemClassName}
|
||||
render={<Link href={href} insights={insights} target={target} />}
|
||||
>
|
||||
{icon}
|
||||
{children}
|
||||
</Menu.Item>
|
||||
<RadixDropdownMenu.Item {...rest} asChild>
|
||||
<Link href={href} insights={insights} className={itemClassName} target={target}>
|
||||
{icon}
|
||||
{children}
|
||||
</Link>
|
||||
</RadixDropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu.Item {...rest} className={tcls('px-3 py-1', itemClassName, className)}>
|
||||
<RadixDropdownMenu.Item {...rest} className={tcls('px-3 py-1', itemClassName, className)}>
|
||||
{icon}
|
||||
{children}
|
||||
</Menu.Item>
|
||||
</RadixDropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -211,24 +268,28 @@ export function DropdownSubMenu(props: { children: React.ReactNode; label: React
|
||||
const { children, label } = props;
|
||||
|
||||
return (
|
||||
<Menu.SubmenuRoot>
|
||||
<Menu.SubmenuTrigger className="straight-corners:rounded-xs focus:outline-hidden data-highlighted:bg-tint-hover flex cursor-pointer items-center justify-between rounded-sm px-3 py-1 text-sm">
|
||||
<RadixDropdownMenu.Sub>
|
||||
<RadixDropdownMenu.SubTrigger className="straight-corners:rounded-xs focus:outline-hidden data-highlighted:bg-tint-hover flex cursor-pointer items-center justify-between rounded-sm px-3 py-1 text-sm">
|
||||
{label}
|
||||
<Icon icon="chevron-right" className="size-3 shrink-0 opacity-6" />
|
||||
</Menu.SubmenuTrigger>
|
||||
<Menu.Portal>
|
||||
<Menu.Positioner collisionPadding={8} className={DROPDOWN_POSITIONER_CLASS}>
|
||||
<Menu.Popup className={DROPDOWN_POPUP_CLASS}>{children}</Menu.Popup>
|
||||
</Menu.Positioner>
|
||||
</Menu.Portal>
|
||||
</Menu.SubmenuRoot>
|
||||
</RadixDropdownMenu.SubTrigger>
|
||||
<RadixDropdownMenu.Portal>
|
||||
<RadixDropdownMenu.SubContent
|
||||
hideWhenDetached
|
||||
collisionPadding={8}
|
||||
className={DROPDOWN_CONTENT_OUTER_CLASS}
|
||||
>
|
||||
<div className={DROPDOWN_CONTENT_INNER_CLASS}>{children}</div>
|
||||
</RadixDropdownMenu.SubContent>
|
||||
</RadixDropdownMenu.Portal>
|
||||
</RadixDropdownMenu.Sub>
|
||||
);
|
||||
}
|
||||
|
||||
export function DropdownMenuSeparator(props: { className?: ClassValue }) {
|
||||
const { className } = props;
|
||||
return (
|
||||
<Menu.Separator
|
||||
<RadixDropdownMenu.Separator
|
||||
className={tcls('my-1 h-px w-full border-tint-subtle border-t', className)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,64 +1,51 @@
|
||||
'use client';
|
||||
import { PreviewCard } from '@base-ui/react/preview-card';
|
||||
import * as RadixHoverCard from '@radix-ui/react-hover-card';
|
||||
|
||||
import { PopupArrow } from './PopupArrow';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export function HoverCardRoot(props: PreviewCard.Root.Props) {
|
||||
return <PreviewCard.Root {...props} />;
|
||||
export function HoverCardRoot(props: RadixHoverCard.HoverCardProps) {
|
||||
return (
|
||||
<RadixHoverCard.Root
|
||||
openDelay={props.openDelay ?? 200}
|
||||
closeDelay={props.closeDelay ?? 100}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Wrapped in a span so the render prop never receives a lazily-loaded component.
|
||||
export function HoverCardTrigger(props: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
openDelay?: number;
|
||||
closeDelay?: number;
|
||||
}) {
|
||||
const { children, className, openDelay = 200, closeDelay = 100 } = props;
|
||||
// We wrap the Trigger child in a span to avoid react slot seeing a lazy chunk
|
||||
export function HoverCardTrigger(props: RadixHoverCard.HoverCardTriggerProps) {
|
||||
const { children, ...rest } = props;
|
||||
return (
|
||||
<PreviewCard.Trigger
|
||||
render={<span className={className} />}
|
||||
delay={openDelay}
|
||||
closeDelay={closeDelay}
|
||||
>
|
||||
{children}
|
||||
</PreviewCard.Trigger>
|
||||
<RadixHoverCard.Trigger asChild {...rest}>
|
||||
<span>{children}</span>
|
||||
</RadixHoverCard.Trigger>
|
||||
);
|
||||
}
|
||||
|
||||
export function HoverCard(
|
||||
props: Omit<PreviewCard.Popup.Props, 'className'> & {
|
||||
className?: string;
|
||||
side?: PreviewCard.Positioner.Props['side'];
|
||||
arrow?: { className?: string };
|
||||
}
|
||||
props: RadixHoverCard.HoverCardContentProps & { arrow?: RadixHoverCard.HoverCardArrowProps }
|
||||
) {
|
||||
const { arrow, side = 'top', className, children, ...popupProps } = props;
|
||||
const { arrow, ...cardProps } = props;
|
||||
return (
|
||||
<PreviewCard.Portal>
|
||||
<PreviewCard.Positioner
|
||||
side={side}
|
||||
className="pointer-events-none z-50 w-screen max-w-md px-4 sm:w-auto"
|
||||
<RadixHoverCard.Portal>
|
||||
<RadixHoverCard.Content
|
||||
side={props.side ?? 'top'}
|
||||
className="animate-scale-in data-[state='closed']:animate-scale-out pointer-events-none z-50 w-screen max-w-md px-4 sm:w-auto"
|
||||
>
|
||||
<PreviewCard.Popup
|
||||
{...popupProps}
|
||||
className="animate-scale-in data-closed:animate-scale-out"
|
||||
<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',
|
||||
cardProps.className
|
||||
)}
|
||||
>
|
||||
<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',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<PopupArrow
|
||||
arrow={PreviewCard.Arrow}
|
||||
className={arrow?.className ?? 'fill-tint-1'}
|
||||
/>
|
||||
</PreviewCard.Popup>
|
||||
</PreviewCard.Positioner>
|
||||
</PreviewCard.Portal>
|
||||
{cardProps.children}
|
||||
</div>
|
||||
<RadixHoverCard.Arrow
|
||||
className={tcls('fill-tint-1', arrow?.className)}
|
||||
{...arrow}
|
||||
/>
|
||||
</RadixHoverCard.Content>
|
||||
</RadixHoverCard.Portal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import { Popover as BasePopover } from '@base-ui/react/popover';
|
||||
import * as RadixPopover from '@radix-ui/react-popover';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export function Popover(props: {
|
||||
anchor: BasePopover.Positioner.Props['anchor'];
|
||||
children: React.ReactNode;
|
||||
content?: string | React.ReactNode;
|
||||
rootProps?: Omit<BasePopover.Root.Props, 'children'>;
|
||||
positionerProps?: Omit<BasePopover.Positioner.Props, 'children' | 'className' | 'anchor'> & {
|
||||
className?: string;
|
||||
};
|
||||
popupProps?: Omit<BasePopover.Popup.Props, 'children' | 'className'> & { className?: string };
|
||||
rootProps?: RadixPopover.PopoverProps;
|
||||
triggerProps?: RadixPopover.PopoverTriggerProps;
|
||||
contentProps?: RadixPopover.PopoverContentProps;
|
||||
arrow?: boolean;
|
||||
}) {
|
||||
const { anchor, content, rootProps, positionerProps, popupProps } = props;
|
||||
const { children, content, rootProps, triggerProps, contentProps, arrow = false } = props;
|
||||
|
||||
return (
|
||||
<BasePopover.Root {...rootProps}>
|
||||
<BasePopover.Portal>
|
||||
<BasePopover.Positioner
|
||||
{...positionerProps}
|
||||
anchor={anchor}
|
||||
className={tcls('z-50 data-anchor-hidden:hidden', positionerProps?.className)}
|
||||
collisionPadding={positionerProps?.collisionPadding ?? 16}
|
||||
sideOffset={positionerProps?.sideOffset ?? 4}
|
||||
<RadixPopover.Root {...rootProps}>
|
||||
<RadixPopover.Trigger {...triggerProps}>{children}</RadixPopover.Trigger>
|
||||
<RadixPopover.Portal>
|
||||
<RadixPopover.Content
|
||||
{...contentProps}
|
||||
collisionPadding={contentProps?.collisionPadding ?? 16}
|
||||
sideOffset={contentProps?.sideOffset ?? 4}
|
||||
className={tcls(
|
||||
'z-50 max-h-(--radix-popover-content-available-height) max-w-xs animate-scale-in overflow-y-auto overflow-x-hidden circular-corners:rounded-3xl rounded-corners:rounded-xl bg-tint px-4 py-3 text-sm text-tint depth-subtle:shadow-xl shadow-tint-12/4 outline-hidden ring-1 ring-tint transition-all empty:hidden data-[state="closed"]:animate-scale-out motion-reduce:transition-none dark:shadow-tint-1/6',
|
||||
contentProps?.className
|
||||
)}
|
||||
style={{
|
||||
...contentProps?.style,
|
||||
}}
|
||||
>
|
||||
<BasePopover.Popup
|
||||
{...popupProps}
|
||||
className={tcls(
|
||||
'max-h-(--available-height) max-w-xs animate-scale-in overflow-y-auto overflow-x-hidden circular-corners:rounded-3xl rounded-corners:rounded-xl bg-tint px-4 py-3 text-sm text-tint depth-subtle:shadow-xl shadow-tint-12/4 outline-hidden ring-1 ring-tint transition-all empty:hidden data-closed:animate-scale-out motion-reduce:transition-none dark:shadow-tint-1/6',
|
||||
popupProps?.className
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</BasePopover.Popup>
|
||||
</BasePopover.Positioner>
|
||||
</BasePopover.Portal>
|
||||
</BasePopover.Root>
|
||||
{content}
|
||||
{arrow && (
|
||||
<RadixPopover.Arrow className="-mb-px h-2 w-4 fill-tint-3 stroke-tint-7 stroke-2" />
|
||||
)}
|
||||
</RadixPopover.Content>
|
||||
</RadixPopover.Portal>
|
||||
</RadixPopover.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
// Base UI positions the arrow against the anchor but never rotates it, so it is drawn pointing up
|
||||
// and turned per `data-side` here.
|
||||
export function PopupArrow(props: {
|
||||
arrow: React.ComponentType<{ className?: string; children?: React.ReactNode }>;
|
||||
className?: string;
|
||||
}) {
|
||||
const { arrow: Arrow, className } = props;
|
||||
|
||||
return (
|
||||
<Arrow
|
||||
className={tcls(
|
||||
'h-2 w-4',
|
||||
'data-[side=bottom]:-top-2 data-[side=top]:-bottom-2 data-[side=top]:rotate-180',
|
||||
'data-[side=left]:-right-3 data-[side=left]:rotate-90',
|
||||
'data-[side=right]:-left-3 data-[side=right]:-rotate-90'
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 20 10"
|
||||
preserveAspectRatio="none"
|
||||
className={tcls('block h-full w-full', className)}
|
||||
>
|
||||
<path d="M0 10 10 0l10 10z" />
|
||||
</svg>
|
||||
</Arrow>
|
||||
);
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import { Icon, type IconName } from '@gitbook/icons';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
/**
|
||||
* Animated chevron to display in dropdowns and other toggleable elements. Picks up the open state
|
||||
* from the enclosing `group/dropdown` or `<details>`, unless an `open` prop is passed.
|
||||
* Animated chevron to display in dropdowns and other toggleable elements.
|
||||
* Uses the `data-[state=open]` attribute to animate the chevron, or you can pass it an `open` prop to control the state.
|
||||
*/
|
||||
export function ToggleChevron(props: {
|
||||
open?: boolean;
|
||||
@@ -21,14 +21,12 @@ export function ToggleChevron(props: {
|
||||
'down-to-up': {
|
||||
icon: 'chevron-down',
|
||||
animation: 'rotate-180',
|
||||
autoAnimation:
|
||||
'group-aria-expanded/dropdown:rotate-180 group-data-panel-open/dropdown:rotate-180 group-open:rotate-180',
|
||||
autoAnimation: 'group-data-[state=open]/dropdown:rotate-180 group-open:rotate-180',
|
||||
},
|
||||
'right-to-down': {
|
||||
icon: 'chevron-right',
|
||||
animation: 'rotate-90',
|
||||
autoAnimation:
|
||||
'group-aria-expanded/dropdown:rotate-90 group-data-panel-open/dropdown:rotate-90 group-open:rotate-90',
|
||||
autoAnimation: 'group-data-[state=open]/dropdown:rotate-90 group-open:rotate-90',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -37,7 +35,7 @@ export function ToggleChevron(props: {
|
||||
icon={classes[orientation].icon as IconName}
|
||||
className={tcls(
|
||||
'shrink-0',
|
||||
getRotationClassName(open, classes[orientation]),
|
||||
open ? classes[orientation].animation : classes[orientation].autoAnimation,
|
||||
'size-3',
|
||||
'transition-all',
|
||||
className
|
||||
@@ -45,16 +43,3 @@ export function ToggleChevron(props: {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function getRotationClassName(
|
||||
open: boolean | undefined,
|
||||
classes: { animation: string; autoAnimation: string }
|
||||
): string {
|
||||
if (open === undefined) {
|
||||
return classes.autoAnimation;
|
||||
}
|
||||
if (open) {
|
||||
return classes.animation;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1,96 +1,86 @@
|
||||
'use client';
|
||||
|
||||
import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip';
|
||||
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { PopupArrow } from './PopupArrow';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export type TooltipProps = {
|
||||
side?: BaseTooltip.Positioner.Props['side'];
|
||||
align?: BaseTooltip.Positioner.Props['align'];
|
||||
sideOffset?: number;
|
||||
delay?: number;
|
||||
disabled?: boolean;
|
||||
disableHoverablePopup?: boolean;
|
||||
pinOnClick?: boolean;
|
||||
popupProps?: Omit<BaseTooltip.Popup.Props, 'className' | 'children' | 'render'>;
|
||||
rootProps?: RadixTooltip.TooltipProps;
|
||||
triggerProps?: RadixTooltip.TooltipTriggerProps;
|
||||
contentProps?: RadixTooltip.TooltipContentProps;
|
||||
};
|
||||
|
||||
export function Tooltip(
|
||||
props: TooltipProps & {
|
||||
children: React.ReactElement<Record<string, unknown>>;
|
||||
label?: string | React.ReactNode;
|
||||
arrow?: boolean | { className?: string };
|
||||
className?: string;
|
||||
}
|
||||
) {
|
||||
export function Tooltip(props: {
|
||||
children: React.ReactNode;
|
||||
label?: string | React.ReactNode;
|
||||
triggerProps?: RadixTooltip.TooltipTriggerProps;
|
||||
contentProps?: RadixTooltip.TooltipContentProps;
|
||||
portalProps?: RadixTooltip.TooltipPortalProps;
|
||||
rootProps?: RadixTooltip.TooltipProps;
|
||||
arrowProps?: RadixTooltip.TooltipArrowProps;
|
||||
arrow?: boolean;
|
||||
className?: string;
|
||||
pinOnClick?: boolean;
|
||||
}) {
|
||||
const {
|
||||
children,
|
||||
label,
|
||||
side,
|
||||
align,
|
||||
sideOffset = 4,
|
||||
delay,
|
||||
disabled,
|
||||
disableHoverablePopup,
|
||||
pinOnClick = false,
|
||||
popupProps,
|
||||
triggerProps,
|
||||
contentProps,
|
||||
portalProps,
|
||||
rootProps,
|
||||
arrowProps,
|
||||
arrow = false,
|
||||
className,
|
||||
pinOnClick = true,
|
||||
} = props;
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [pinned, setPinned] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [clicked, setClicked] = useState(false);
|
||||
|
||||
// When hoverable content is disabled, the content is purely informational: make it
|
||||
// non-interactive so its (portaled) popper wrapper can't steal hover/clicks from the
|
||||
// trigger. The `data-non-interactive` marker lets a scoped global rule (globals.css)
|
||||
// set `pointer-events: none` on the wrapper, which we can't reach from React.
|
||||
const nonInteractive = rootProps?.disableHoverableContent ?? false;
|
||||
const resolvedContentProps = nonInteractive
|
||||
? {
|
||||
...contentProps,
|
||||
'data-non-interactive': '',
|
||||
style: {
|
||||
...contentProps?.style,
|
||||
// Forced last so the non-interactive guarantee can't be overridden.
|
||||
pointerEvents: 'none' as const,
|
||||
userSelect: 'none' as const,
|
||||
},
|
||||
}
|
||||
: contentProps;
|
||||
|
||||
return (
|
||||
<BaseTooltip.Root
|
||||
open={hovered || pinned}
|
||||
onOpenChange={(nextOpen, eventDetails) => {
|
||||
setHovered(nextOpen);
|
||||
if (
|
||||
eventDetails.reason === 'outside-press' ||
|
||||
eventDetails.reason === 'escape-key'
|
||||
) {
|
||||
setPinned(false);
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
disableHoverablePopup={disableHoverablePopup}
|
||||
>
|
||||
<BaseTooltip.Trigger
|
||||
render={children}
|
||||
delay={delay}
|
||||
onClick={pinOnClick ? () => setPinned(true) : undefined}
|
||||
/>
|
||||
<BaseTooltip.Portal>
|
||||
<BaseTooltip.Positioner
|
||||
className="z-50"
|
||||
side={side}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
<RadixTooltip.Root open={open || clicked} onOpenChange={setOpen} {...rootProps}>
|
||||
<RadixTooltip.Trigger
|
||||
asChild
|
||||
onClick={pinOnClick ? () => setClicked(true) : undefined}
|
||||
{...triggerProps}
|
||||
>
|
||||
{children}
|
||||
</RadixTooltip.Trigger>
|
||||
<RadixTooltip.Portal {...portalProps}>
|
||||
<RadixTooltip.Content
|
||||
sideOffset={4}
|
||||
collisionPadding={8}
|
||||
className={tcls(
|
||||
'z-50 max-w-xs circular-corners:rounded-2xl rounded-corners:rounded-md bg-tint-12 px-2 py-1 text-contrast-tint-12 text-sm data-[state$="closed"]:animate-scale-out data-[state$="open"]:animate-scale-in',
|
||||
className
|
||||
)}
|
||||
onPointerDownOutside={() => setClicked(false)}
|
||||
{...resolvedContentProps}
|
||||
>
|
||||
<BaseTooltip.Popup
|
||||
{...popupProps}
|
||||
className={tcls(
|
||||
'max-w-xs circular-corners:rounded-2xl rounded-corners:rounded-md bg-tint-12 px-2 py-1 text-contrast-tint-12 text-sm data-closed:animate-scale-out data-open:animate-scale-in',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
{arrow ? (
|
||||
<PopupArrow
|
||||
arrow={BaseTooltip.Arrow}
|
||||
className={
|
||||
(typeof arrow === 'object' ? arrow.className : null) ??
|
||||
'fill-tint-12'
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</BaseTooltip.Popup>
|
||||
</BaseTooltip.Positioner>
|
||||
</BaseTooltip.Portal>
|
||||
</BaseTooltip.Root>
|
||||
{label}
|
||||
{arrow && <RadixTooltip.Arrow {...arrowProps} />}
|
||||
</RadixTooltip.Content>
|
||||
</RadixTooltip.Portal>
|
||||
</RadixTooltip.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const ButtonStyles = [
|
||||
|
||||
'hover:depth-subtle:-translate-y-px',
|
||||
'focus-visible:depth-subtle:-translate-y-px',
|
||||
'aria-expanded:depth-subtle:-translate-y-px',
|
||||
'data-[state=open]:depth-subtle:-translate-y-px',
|
||||
'active:depth-subtle:translate-y-0',
|
||||
'transition-all',
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { assert } from 'ts-essentials';
|
||||
|
||||
import type {
|
||||
ChangeRequest,
|
||||
CustomizationDefaultThemeMode,
|
||||
PublishedSiteContent,
|
||||
Revision,
|
||||
RevisionPageDocument,
|
||||
@@ -32,7 +31,6 @@ import { getLogger } from '@/lib/logger';
|
||||
import {
|
||||
findSiteSpaceBy,
|
||||
getFallbackSiteSpacePath,
|
||||
getLinkerForSiteSpace,
|
||||
getLocalizedTitle,
|
||||
getSiteStructureSections,
|
||||
} from '@/lib/sites';
|
||||
@@ -86,13 +84,6 @@ export type SiteURLData = Pick<
|
||||
* Defaults to true when undefined.
|
||||
*/
|
||||
displayAgentInstructions?: boolean;
|
||||
|
||||
/**
|
||||
* Theme forced via `?theme=` for the docs embed only. Passed through the route context (rather
|
||||
* than a request header) so the embed routes can honor it while staying statically rendered.
|
||||
* Should never be set for the main site. RND-11571.
|
||||
*/
|
||||
embedTheme?: CustomizationDefaultThemeMode;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -402,14 +393,12 @@ export async function fetchSiteContextByIds(
|
||||
: {}),
|
||||
};
|
||||
|
||||
const siteLinker = site.urls.published
|
||||
? linkerForPublishedURL(spaceContext.linker, site.urls.published)
|
||||
: spaceContext.linker;
|
||||
|
||||
return {
|
||||
...spaceContext,
|
||||
locale: siteSpace.space.language ?? spaceContext.locale,
|
||||
linker: getLinkerForSiteSpace(siteLinker, siteSpace, spaceContext.revision.pages),
|
||||
linker: site.urls.published
|
||||
? linkerForPublishedURL(spaceContext.linker, site.urls.published)
|
||||
: spaceContext.linker,
|
||||
organizationId: ids.organization,
|
||||
site,
|
||||
siteSpaces,
|
||||
@@ -455,15 +444,13 @@ export async function fetchSiteContextForSiteSpace(
|
||||
? baseContext.structure.structure
|
||||
: (found.siteSection?.siteSpaces ?? baseContext.siteSpaces);
|
||||
|
||||
const siteSpaceLinker = baseContext.linker.withOtherSiteSpace({
|
||||
spaceBasePath: getFallbackSiteSpacePath(baseContext, siteSpace),
|
||||
});
|
||||
|
||||
return {
|
||||
...baseContext,
|
||||
...spaceContext,
|
||||
locale: siteSpace.space.language ?? spaceContext.locale,
|
||||
linker: getLinkerForSiteSpace(siteSpaceLinker, siteSpace, spaceContext.revision.pages),
|
||||
linker: baseContext.linker.withOtherSiteSpace({
|
||||
spaceBasePath: getFallbackSiteSpacePath(baseContext, siteSpace),
|
||||
}),
|
||||
siteSpace,
|
||||
siteSpaces,
|
||||
visibleSiteSpaces: filterHiddenSiteSpaces(siteSpaces),
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import {
|
||||
COMMENT_MARKER,
|
||||
type IssueCommentClient,
|
||||
formatCompatibilityComment,
|
||||
getAddedDeclarations,
|
||||
getChangedLines,
|
||||
getCompatibilityDiagnostics,
|
||||
upsertCompatibilityComment,
|
||||
} from './cssBrowserCompatibility';
|
||||
|
||||
const baseCSS = `.card {
|
||||
@@ -113,3 +117,95 @@ describe('getCompatibilityDiagnostics', () => {
|
||||
expect(diagnostics[0]?.property).toBe('appearance');
|
||||
});
|
||||
});
|
||||
|
||||
class FakeCommentClient implements IssueCommentClient {
|
||||
created: string[] = [];
|
||||
comments: {
|
||||
body: string;
|
||||
id: number;
|
||||
user: { login: string } | null;
|
||||
}[] = [];
|
||||
updated: { body: string; id: number }[] = [];
|
||||
|
||||
async createIssueComment(_issueNumber: number, body: string): Promise<void> {
|
||||
this.created.push(body);
|
||||
}
|
||||
|
||||
async listIssueComments(): Promise<typeof this.comments> {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
async updateIssueComment(commentId: number, body: string): Promise<void> {
|
||||
this.updated.push({ body, id: commentId });
|
||||
}
|
||||
}
|
||||
|
||||
describe('PR comments', () => {
|
||||
const diagnostics = [
|
||||
{
|
||||
column: 5,
|
||||
feature: 'css-container-queries',
|
||||
file: 'packages/gitbook/src/example.css',
|
||||
line: 3,
|
||||
property: 'container-type',
|
||||
unsupportedBrowsers: 'Safari 12',
|
||||
},
|
||||
];
|
||||
|
||||
it('formats an actionable compatibility report', () => {
|
||||
const comment = formatCompatibilityComment({
|
||||
diagnostics,
|
||||
headSha: 'abc123',
|
||||
repository: 'GitbookIO/gitbook',
|
||||
});
|
||||
|
||||
expect(comment).toContain(COMMENT_MARKER);
|
||||
expect(comment).toContain('container-type');
|
||||
expect(comment).toContain('Safari 12');
|
||||
expect(comment).toContain('example.css:3');
|
||||
});
|
||||
|
||||
it('creates a failure comment, updates it on rerun, and marks it clean', async () => {
|
||||
const client = new FakeCommentClient();
|
||||
const failureComment = formatCompatibilityComment({
|
||||
diagnostics,
|
||||
headSha: 'abc123',
|
||||
repository: 'GitbookIO/gitbook',
|
||||
});
|
||||
|
||||
await upsertCompatibilityComment({ body: failureComment, client, issueNumber: 42 });
|
||||
expect(client.created).toEqual([failureComment]);
|
||||
|
||||
client.comments = [{ body: failureComment, id: 9, user: { login: 'github-actions[bot]' } }];
|
||||
const passingComment = formatCompatibilityComment({
|
||||
diagnostics: [],
|
||||
headSha: 'def456',
|
||||
repository: 'GitbookIO/gitbook',
|
||||
});
|
||||
await upsertCompatibilityComment({
|
||||
body: passingComment,
|
||||
createIfMissing: false,
|
||||
client,
|
||||
issueNumber: 42,
|
||||
});
|
||||
|
||||
expect(client.updated).toEqual([{ body: passingComment, id: 9 }]);
|
||||
});
|
||||
|
||||
it('does not add a passing comment when no compatibility comment exists', async () => {
|
||||
const client = new FakeCommentClient();
|
||||
await upsertCompatibilityComment({
|
||||
body: formatCompatibilityComment({
|
||||
diagnostics: [],
|
||||
headSha: 'abc123',
|
||||
repository: 'GitbookIO/gitbook',
|
||||
}),
|
||||
createIfMissing: false,
|
||||
client,
|
||||
issueNumber: 42,
|
||||
});
|
||||
|
||||
expect(client.created).toEqual([]);
|
||||
expect(client.updated).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,9 @@ import { diffLines } from 'diff';
|
||||
import postcss, { type Declaration } from 'postcss';
|
||||
import stylelint from 'stylelint';
|
||||
|
||||
export const COMMENT_MARKER = '<!-- gitbook-css-browser-compatibility -->';
|
||||
const COMPATIBILITY_RULE = 'plugin/no-unsupported-browser-features';
|
||||
const MAX_COMMENT_DIAGNOSTICS = 50;
|
||||
|
||||
export interface ChangedLines {
|
||||
added: Set<number>;
|
||||
@@ -21,6 +23,18 @@ export interface CompatibilityDiagnostic extends AddedDeclaration {
|
||||
unsupportedBrowsers: string;
|
||||
}
|
||||
|
||||
interface Comment {
|
||||
body: string;
|
||||
id: number;
|
||||
user: { login: string } | null;
|
||||
}
|
||||
|
||||
export interface IssueCommentClient {
|
||||
createIssueComment(issueNumber: number, body: string): Promise<void>;
|
||||
listIssueComments(issueNumber: number): Promise<Comment[]>;
|
||||
updateIssueComment(commentId: number, body: string): Promise<void>;
|
||||
}
|
||||
|
||||
function lineCount(value: string): number {
|
||||
return value === '' ? 0 : value.split('\n').length - (value.endsWith('\n') ? 1 : 0);
|
||||
}
|
||||
@@ -191,3 +205,70 @@ export async function getCompatibilityDiagnostics({
|
||||
).values()
|
||||
);
|
||||
}
|
||||
|
||||
function escapeTableCell(value: string): string {
|
||||
return value.replaceAll('\\', '\\\\').replaceAll('|', '\\|').replaceAll('\n', ' ');
|
||||
}
|
||||
|
||||
function blobUrl(repository: string, headSha: string, file: string, line: number): string {
|
||||
const encodedFile = file.split('/').map(encodeURIComponent).join('/');
|
||||
return `https://github.com/${repository}/blob/${headSha}/${encodedFile}#L${line}`;
|
||||
}
|
||||
|
||||
export function formatCompatibilityComment({
|
||||
diagnostics,
|
||||
headSha,
|
||||
repository,
|
||||
}: {
|
||||
diagnostics: CompatibilityDiagnostic[];
|
||||
headSha: string;
|
||||
repository: string;
|
||||
}): string {
|
||||
if (diagnostics.length === 0) {
|
||||
return `${COMMENT_MARKER}\n## CSS browser compatibility\n\n✅ No newly added CSS declarations have browser-compatibility issues.`;
|
||||
}
|
||||
|
||||
const visibleDiagnostics = diagnostics.slice(0, MAX_COMMENT_DIAGNOSTICS);
|
||||
const rows = visibleDiagnostics.map((diagnostic) => {
|
||||
const location = `[${escapeTableCell(diagnostic.file)}:${diagnostic.line}](${blobUrl(repository, headSha, diagnostic.file, diagnostic.line)})`;
|
||||
return `| ${location} | \`${escapeTableCell(diagnostic.property)}\` | ${escapeTableCell(diagnostic.unsupportedBrowsers)} |`;
|
||||
});
|
||||
const remaining = diagnostics.length - visibleDiagnostics.length;
|
||||
const heading = diagnostics.length === 1 ? 'declaration is' : 'declarations are';
|
||||
|
||||
return [
|
||||
COMMENT_MARKER,
|
||||
'## CSS browser compatibility',
|
||||
'',
|
||||
`❌ ${diagnostics.length} newly added CSS ${heading} not fully supported by the configured Browserslist targets.`,
|
||||
'',
|
||||
'| Location | Property | Unsupported browsers |',
|
||||
'| --- | --- | --- |',
|
||||
...rows,
|
||||
...(remaining > 0 ? ['', `_${remaining} additional finding(s) omitted._`] : []),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export async function upsertCompatibilityComment({
|
||||
body,
|
||||
createIfMissing = true,
|
||||
client,
|
||||
issueNumber,
|
||||
}: {
|
||||
body: string;
|
||||
createIfMissing?: boolean;
|
||||
client: IssueCommentClient;
|
||||
issueNumber: number;
|
||||
}): Promise<void> {
|
||||
const comments = await client.listIssueComments(issueNumber);
|
||||
const existingComment = comments.find(
|
||||
(comment) =>
|
||||
comment.user?.login === 'github-actions[bot]' && comment.body.includes(COMMENT_MARKER)
|
||||
);
|
||||
|
||||
if (existingComment) {
|
||||
await client.updateIssueComment(existingComment.id, body);
|
||||
} else if (createIfMissing) {
|
||||
await client.createIssueComment(issueNumber, body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import type { RevisionPageDocument } from '@gitbook/api';
|
||||
|
||||
import { getEmbeddableLinker } from './embeddable-linker';
|
||||
import { createLinker, linkerWithDirectPagePaths } from './links';
|
||||
|
||||
describe('getEmbeddableLinker', () => {
|
||||
it('supports direct page paths inside the embed namespace', () => {
|
||||
const root = createLinker({
|
||||
host: 'docs.company.com',
|
||||
spaceBasePath: '/api/js',
|
||||
siteBasePath: '/',
|
||||
});
|
||||
const linker = linkerWithDirectPagePaths(getEmbeddableLinker(root));
|
||||
const pages = [{ id: 'intro', type: 'document', path: 'introduction', pages: [] }] as [
|
||||
RevisionPageDocument,
|
||||
];
|
||||
|
||||
expect(linker.toPathForPage({ pages, page: pages[0] })).toBe(
|
||||
'/api/js/~gitbook/embed/page/introduction'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -41,17 +41,12 @@ export function toEmbeddableLinkForPublishedContent(
|
||||
* Get a linker to generate links in the embeddable context.
|
||||
*/
|
||||
export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {
|
||||
const embeddableLinker: GitBookLinker = {
|
||||
return {
|
||||
...linker,
|
||||
toPathForPage({ pages, page, anchor }) {
|
||||
return embeddableLinker.toPathForPagePath({
|
||||
path: getPagePath(pages, page),
|
||||
anchor,
|
||||
});
|
||||
},
|
||||
const pagePath = getPagePath(pages, page);
|
||||
const embedPagePath = joinPath(EMBED_PAGE_PATH, pagePath);
|
||||
|
||||
toPathForPagePath({ path, anchor }) {
|
||||
const embedPagePath = joinPath(EMBED_PAGE_PATH, path);
|
||||
return `${linker.toPathInSpace(embedPagePath)}${anchor ? `#${anchor}` : ''}`;
|
||||
},
|
||||
|
||||
@@ -78,6 +73,4 @@ export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
return embeddableLinker;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user