mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c8c1c528f | |||
| bf8b57ba12 | |||
| 4c39e92351 | |||
| e65e0a98f0 | |||
| eecc00fa4a | |||
| 5654e251a8 | |||
| d9694aabcd | |||
| bc8ef37192 | |||
| 5f141f7712 | |||
| 30e3011cb0 | |||
| 3475d8021b | |||
| 32cd50db2f | |||
| f0a6db936c | |||
| 11c37d8d70 | |||
| 7e1367057a | |||
| 3bbb0c6279 | |||
| eed71c21bd | |||
| 4f46826de9 | |||
| ea50fe4e3c | |||
| b057af8d63 | |||
| fba2218760 | |||
| 6147cbdc18 | |||
| 802acb3c7e | |||
| 376dc21e34 | |||
| 387318385c | |||
| 553f0a0918 | |||
| 0d1e21ae45 | |||
| a885e9e4d2 | |||
| 4c10848e8d | |||
| 1d95b01514 | |||
| cd32b5508c | |||
| 9a3bf0f2aa | |||
| 1d04a83e08 | |||
| 17e9b0fd6a | |||
| 54986c9841 |
@@ -54,7 +54,7 @@ jobs:
|
||||
Hi! I started running the integration tests on your PR. You will receive a comment with the results shortly.
|
||||
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --with dev,test,runtime
|
||||
run: poetry install --with dev,test,runtime,evaluation
|
||||
|
||||
- name: Configure config.toml for testing with Haiku
|
||||
env:
|
||||
@@ -179,8 +179,8 @@ jobs:
|
||||
id: create_comment
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
# if triggered by PR, use PR number, otherwise use 5318 as fallback issue number for manual triggers
|
||||
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || 5318 }}
|
||||
# if triggered by PR, use PR number, otherwise use 9745 as fallback issue number for manual triggers
|
||||
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || 9745 }}
|
||||
unique: false
|
||||
comment: |
|
||||
Trigger by: ${{ github.event_name == 'pull_request' && format('Pull Request (integration-test label on PR #{0})', github.event.pull_request.number) || (github.event_name == 'workflow_dispatch' && format('Manual Trigger: {0}', github.event.inputs.reason)) || 'Nightly Scheduled Run' }}
|
||||
|
||||
@@ -137,3 +137,65 @@ Your specialized knowledge and instructions here...
|
||||
2. Add the setting to the backend:
|
||||
- Add the setting to the `Settings` model in `openhands/storage/data_models/settings.py`
|
||||
- Update any relevant backend code to apply the setting (e.g., in session creation)
|
||||
|
||||
### Adding New LLM Models
|
||||
|
||||
To add a new LLM model to OpenHands, you need to update multiple files across both frontend and backend:
|
||||
|
||||
#### Model Configuration Procedure:
|
||||
|
||||
1. **Frontend Model Arrays** (`frontend/src/utils/verified-models.ts`):
|
||||
- Add the model to `VERIFIED_MODELS` array (main list of all verified models)
|
||||
- Add to provider-specific arrays based on the model's provider:
|
||||
- `VERIFIED_OPENAI_MODELS` for OpenAI models
|
||||
- `VERIFIED_ANTHROPIC_MODELS` for Anthropic models
|
||||
- `VERIFIED_MISTRAL_MODELS` for Mistral models
|
||||
- `VERIFIED_OPENHANDS_MODELS` for models available through OpenHands provider
|
||||
|
||||
2. **Backend CLI Integration** (`openhands/cli/utils.py`):
|
||||
- Add the model to the appropriate `VERIFIED_*_MODELS` arrays
|
||||
- This ensures the model appears in CLI model selection
|
||||
|
||||
3. **Backend Model List** (`openhands/utils/llm.py`):
|
||||
- **CRITICAL**: Add the model to the `openhands_models` list (lines 57-66) if using OpenHands provider
|
||||
- This is required for the model to appear in the frontend model selector
|
||||
- Format: `'openhands/model-name'` (e.g., `'openhands/o3'`)
|
||||
|
||||
4. **Backend LLM Configuration** (`openhands/llm/llm.py`):
|
||||
- Add to feature-specific arrays based on model capabilities:
|
||||
- `FUNCTION_CALLING_SUPPORTED_MODELS` if the model supports function calling
|
||||
- `REASONING_EFFORT_SUPPORTED_MODELS` if the model supports reasoning effort parameters
|
||||
- `CACHE_PROMPT_SUPPORTED_MODELS` if the model supports prompt caching
|
||||
- `MODELS_WITHOUT_STOP_WORDS` if the model doesn't support stop words
|
||||
|
||||
5. **Validation**:
|
||||
- Run backend linting: `pre-commit run --config ./dev_config/python/.pre-commit-config.yaml`
|
||||
- Run frontend linting: `cd frontend && npm run lint:fix`
|
||||
- Run frontend build: `cd frontend && npm run build`
|
||||
|
||||
#### Model Verification Arrays:
|
||||
|
||||
- **VERIFIED_MODELS**: Main array of all verified models shown in the UI
|
||||
- **VERIFIED_OPENAI_MODELS**: OpenAI models (LiteLLM doesn't return provider prefix)
|
||||
- **VERIFIED_ANTHROPIC_MODELS**: Anthropic models (LiteLLM doesn't return provider prefix)
|
||||
- **VERIFIED_MISTRAL_MODELS**: Mistral models (LiteLLM doesn't return provider prefix)
|
||||
- **VERIFIED_OPENHANDS_MODELS**: Models available through OpenHands managed provider
|
||||
|
||||
#### Model Feature Support Arrays:
|
||||
|
||||
- **FUNCTION_CALLING_SUPPORTED_MODELS**: Models that support structured function calling
|
||||
- **REASONING_EFFORT_SUPPORTED_MODELS**: Models that support reasoning effort parameters (like o1, o3)
|
||||
- **CACHE_PROMPT_SUPPORTED_MODELS**: Models that support prompt caching for efficiency
|
||||
- **MODELS_WITHOUT_STOP_WORDS**: Models that don't support stop word parameters
|
||||
|
||||
#### Frontend Model Integration:
|
||||
|
||||
- Models are automatically available in the model selector UI once added to verified arrays
|
||||
- The `extractModelAndProvider` utility automatically detects provider from model arrays
|
||||
- Provider-specific models are grouped and prioritized in the UI selection
|
||||
|
||||
#### CLI Model Integration:
|
||||
|
||||
- Models appear in CLI provider selection based on the verified arrays
|
||||
- The `organize_models_and_providers` function groups models by provider
|
||||
- Default model selection prioritizes verified models for each provider
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
|
||||
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.48-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.49-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -62,17 +62,17 @@ system requirements and more information.
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
```
|
||||
|
||||
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
|
||||
|
||||
+3
-3
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
```
|
||||
|
||||
> **注意**: 如果您在0.44版本之前使用过OpenHands,您可能需要运行 `mv ~/.openhands-state ~/.openhands` 来将对话历史迁移到新位置。
|
||||
|
||||
+3
-3
@@ -42,17 +42,17 @@ OpenHandsはDockerを利用してローカル環境でも実行できます。
|
||||
> 公共ネットワークで実行していますか?[Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)を参照して、ネットワークバインディングの制限や追加のセキュリティ対策を実施してください。
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
```
|
||||
|
||||
**注**: バージョン0.44以前のOpenHandsを使用していた場合は、会話履歴を移行するために `mv ~/.openhands-state ~/.openhands` を実行してください。
|
||||
|
||||
@@ -45,6 +45,7 @@ ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
|
||||
ENV SANDBOX_USER_ID=0
|
||||
ENV FILE_STORE=local
|
||||
ENV FILE_STORE_PATH=/.openhands
|
||||
ENV INIT_GIT_IN_EMPTY_WORKSPACE=1
|
||||
RUN mkdir -p $FILE_STORE_PATH
|
||||
RUN mkdir -p $WORKSPACE_BASE
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
- DOCKER_HOST_ADDR=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.48-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.49-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"usage/cloud/bitbucket-installation",
|
||||
"usage/cloud/github-installation",
|
||||
"usage/cloud/gitlab-installation",
|
||||
"usage/cloud/slack-installation"
|
||||
@@ -66,7 +67,9 @@
|
||||
"usage/llms/groq",
|
||||
"usage/llms/local-llms",
|
||||
"usage/llms/litellm-proxy",
|
||||
"usage/llms/moonshot",
|
||||
"usage/llms/openai-llms",
|
||||
"usage/llms/openhands-llms",
|
||||
"usage/llms/openrouter"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1827,6 +1827,11 @@
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"owner_type": {
|
||||
"type": "string",
|
||||
"enum": ["user", "organization"],
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Bitbucket Integration
|
||||
description: This guide walks you through the process of installing OpenHands Cloud for your Bitbucket repositories. Once
|
||||
set up, it will allow OpenHands to work with your Bitbucket repository.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Signed in to [OpenHands Cloud](https://app.all-hands.dev) with [a Bitbucket account](/usage/cloud/openhands-cloud).
|
||||
|
||||
## Adding Bitbucket Repository Access
|
||||
|
||||
Upon signing into OpenHands Cloud with a Bitbucket account, OpenHands will have access to your repositories.
|
||||
|
||||
## Working With Bitbucket Repos in Openhands Cloud
|
||||
|
||||
After signing in with a Bitbucket account, use the `select a repo` and `select a branch` dropdowns to select the
|
||||
appropriate repository and branch you'd like OpenHands to work on. Then click on `Launch` to start the conversation!
|
||||
|
||||

|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Learn about the Cloud UI](/usage/cloud/cloud-ui).
|
||||
- [Use the Cloud API](/usage/cloud/cloud-api) to programmatically interact with OpenHands.
|
||||
@@ -9,8 +9,9 @@ description: The Cloud UI provides a web interface for interacting with OpenHand
|
||||
The landing page is where you can:
|
||||
|
||||
- [Add GitHub repository access](/usage/cloud/github-installation#adding-github-repository-access) to OpenHands.
|
||||
- [Select a GitHub repo](/usage/cloud/github-installation#working-with-github-repos-in-openhands-cloud) or
|
||||
[a GitLab repo](/usage/cloud/gitlab-installation#working-with-gitlab-repos-in-openhands-cloud) to start working on.
|
||||
- [Select a GitHub repo](/usage/cloud/github-installation#working-with-github-repos-in-openhands-cloud),
|
||||
[a GitLab repo](/usage/cloud/gitlab-installation#working-with-gitlab-repos-in-openhands-cloud) or
|
||||
[a Bitbucket repo](/usage/cloud/bitbucket-installation#working-with-bitbucket-repos-in-openhands-cloud) to start working on.
|
||||
- See `Suggested Tasks` for repositories that OpenHands has access to.
|
||||
- Launch an empty conversation using `Launch from Scratch`.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Upon signing into OpenHands Cloud with a GitLab account, OpenHands will have acc
|
||||
After signing in with a Gitlab account, use the `select a repo` and `select a branch` dropdowns to select the
|
||||
appropriate repository and branch you'd like OpenHands to work on. Then click on `Launch` to start the conversation!
|
||||
|
||||

|
||||

|
||||
|
||||
## Using Tokens with Reduced Scopes
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ description: Getting started with OpenHands Cloud.
|
||||
OpenHands Cloud is the hosted cloud version of All Hands AI's OpenHands. To get started with OpenHands Cloud,
|
||||
visit [app.all-hands.dev](https://app.all-hands.dev).
|
||||
|
||||
You'll be prompted to connect with your GitHub or GitLab account:
|
||||
You'll be prompted to connect with your GitHub, GitLab or Bitbucket account:
|
||||
|
||||
1. Click `Log in with GitHub` or `Log in with GitLab`.
|
||||
1. Click `Log in with GitHub`, `Log in with GitLab` or `Log in with Bitbucket`.
|
||||
2. Review the permissions requested by OpenHands and authorize the application.
|
||||
- OpenHands will require certain permissions from your account. To read more about these permissions,
|
||||
you can click the `Learn more` link on the authorization page.
|
||||
@@ -22,5 +22,6 @@ Once you've connected your account, you can:
|
||||
|
||||
- [Install GitHub Integration](/usage/cloud/github-installation) to use OpenHands with your GitHub repositories.
|
||||
- [Install GitLab Integration](/usage/cloud/gitlab-installation) to use OpenHands with your GitLab repositories.
|
||||
- [Install Bitbucket Integration](/usage/cloud/bitbucket-installation) to use OpenHands with your Bitbucket repositories.
|
||||
- [Learn about the Cloud UI](/usage/cloud/cloud-ui).
|
||||
- [Use the Cloud API](/usage/cloud/cloud-api) to programmatically interact with OpenHands.
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ icon: question
|
||||
[GitHub](/usage/cloud/github-installation), [GitLab](/usage/cloud/gitlab-installation),
|
||||
and [Slack](/usage/cloud/slack-installation) integrations.
|
||||
2. **Run on your own**: If you prefer to run it on your own hardware, follow our [Getting Started guide](/usage/local-setup).
|
||||
3. **First steps**: Complete the [start building tutorial](/usage/getting-started) to learn the basics.
|
||||
3. **First steps**: Read over the [start building guidelines](/usage/getting-started) and
|
||||
[prompting best practices](/usage/prompting/prompting-best-practices) to learn the basics.
|
||||
|
||||
### Can I use OpenHands for production workloads?
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ The conversation history will be saved in `~/.openhands/sessions`.
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -112,7 +112,7 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49 \
|
||||
python -m openhands.cli.main --override-cli-mode true
|
||||
```
|
||||
|
||||
@@ -123,7 +123,8 @@ docker run -it \
|
||||
|
||||
This launches the CLI in Docker, allowing you to interact with OpenHands.
|
||||
|
||||
The `-e SANDBOX_USER_ID=$(id -u)` ensures files created by the agent in your workspace have the correct permissions.
|
||||
The `-e SANDBOX_USER_ID=$(id -u)` is passed to the Docker command to ensure the sandbox user matches the host user’s
|
||||
permissions. This prevents the agent from creating root-owned files in the mounted workspace.
|
||||
|
||||
The conversation history will be saved in `~/.openhands/sessions`.
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ You can use the Settings page at any time to:
|
||||
- Setup the LLM provider and model for OpenHands.
|
||||
- [Setup the search engine](/usage/search-engine-setup).
|
||||
- [Configure MCP servers](/usage/mcp).
|
||||
- [Connect to GitHub](/usage/how-to/gui-mode#github-setup) and [connect to GitLab](/usage/how-to/gui-mode#gitlab-setup).
|
||||
- [Connect to GitHub](/usage/how-to/gui-mode#github-setup), [connect to GitLab](/usage/how-to/gui-mode#gitlab-setup)
|
||||
and [connect to Bitbucket](/usage/how-to/gui-mode#bitbucket-setup).
|
||||
- Set application settings like your preferred language, notifications and other preferences.
|
||||
- [Manage custom secrets](/usage/common-settings#secrets-management).
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export GITHUB_TOKEN="your-token" # Required for repository operations
|
||||
# Run OpenHands
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -73,13 +73,14 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
> **Note**: If you used OpenHands before version 0.44, run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history.
|
||||
|
||||
The `-e SANDBOX_USER_ID=$(id -u)` is passed to the Docker command to ensure the sandbox user matches the host user’s
|
||||
permissions. This prevents the agent from creating root-owned files in the mounted workspace.
|
||||
|
||||
## Additional Options
|
||||
|
||||
@@ -90,6 +91,6 @@ Common command-line options:
|
||||
- `-b 10.0` - Set budget limit (USD)
|
||||
- `--no-auto-continue` - Interactive mode
|
||||
|
||||
Run `poetry run python -m openhands.core.main --help` for all options, or use a [`config.toml` file](https://github.com/All-Hands-AI/OpenHands/blob/main/config.template.toml) for more flexibility.
|
||||
Run `poetry run python -m openhands.core.main --help` for all options.
|
||||
|
||||
Set `export LOG_ALL_EVENTS=true` to log all agent actions.
|
||||
|
||||
@@ -10,7 +10,8 @@ This section is for users who want to connect OpenHands to different LLMs.
|
||||
## Model Recommendations
|
||||
|
||||
Based on our evaluations of language models for coding tasks (using the SWE-bench dataset), we can provide some
|
||||
recommendations for model selection. Our latest benchmarking results can be found in [this spreadsheet](https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0).
|
||||
recommendations for model selection. Our latest benchmarking results can be found in
|
||||
[this spreadsheet](https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0).
|
||||
|
||||
Based on these findings and community feedback, these are the latest models that have been verified to work reasonably well with OpenHands:
|
||||
|
||||
@@ -20,6 +21,7 @@ Based on these findings and community feedback, these are the latest models that
|
||||
- [openai/o4-mini](https://openai.com/index/introducing-o3-and-o4-mini/)
|
||||
- [gemini/gemini-2.5-pro](https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/)
|
||||
- [deepseek/deepseek-chat](https://api-docs.deepseek.com/)
|
||||
- [moonshot/kimi-k2-0711-preview](https://platform.moonshot.ai/docs/pricing/chat#generation-model-kimi-k2)
|
||||
|
||||
If you have successfully run OpenHands with specific providers, we encourage you to open a PR to share your setup process
|
||||
to help others using the same provider!
|
||||
@@ -70,17 +72,20 @@ We have a few guides for running OpenHands with specific model providers:
|
||||
- [Groq](/usage/llms/groq)
|
||||
- [Local LLMs with SGLang or vLLM](/usage/llms/local-llms)
|
||||
- [LiteLLM Proxy](/usage/llms/litellm-proxy)
|
||||
- [Moonshot AI](/usage/llms/moonshot)
|
||||
- [OpenAI](/usage/llms/openai-llms)
|
||||
- [OpenHands](/usage/llms/openhands-llms)
|
||||
- [OpenRouter](/usage/llms/openrouter)
|
||||
|
||||
## Model Customization
|
||||
|
||||
LLM providers have specific settings that can be customized to optimize their performance with OpenHands, such as:
|
||||
|
||||
- **Custom Tokenizers**: For specialized models, you can add a suitable tokenizer
|
||||
- **Native Tool Calling**: Toggle native function/tool calling capabilities
|
||||
- **Custom Tokenizers**: For specialized models, you can add a suitable tokenizer.
|
||||
- **Native Tool Calling**: Toggle native function/tool calling capabilities.
|
||||
|
||||
For detailed information about model customization, see [LLM Configuration Options](configuration-options#llm-customization).
|
||||
For detailed information about model customization, see
|
||||
[LLM Configuration Options](/usage/configuration-options#llm-configuration).
|
||||
|
||||
### API retries and rate limits
|
||||
|
||||
|
||||
@@ -68,23 +68,23 @@ Download and install the LM Studio desktop app from [lmstudio.ai](https://lmstud
|
||||
1. Check [the installation guide](/usage/local-setup) and ensure all prerequisites are met before running OpenHands, then run:
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
```
|
||||
|
||||
2. Wait until the server is running (see log below):
|
||||
```
|
||||
Digest: sha256:e72f9baecb458aedb9afc2cd5bc935118d1868719e55d50da73190d3a85c674f
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Moonshot AI
|
||||
description: How to use Moonshot AI models with OpenHands
|
||||
---
|
||||
|
||||
## Using Moonshot AI with OpenHands
|
||||
|
||||
[Moonshot AI](https://platform.moonshot.ai/) offers several powerful models, including Kimi-K2, which has been verified to work well with OpenHands.
|
||||
|
||||
### Setup
|
||||
|
||||
1. Sign up for an account at [Moonshot AI Platform](https://platform.moonshot.ai/)
|
||||
2. Generate an API key from your account settings
|
||||
3. Configure OpenHands to use Moonshot AI:
|
||||
|
||||
| Setting | Value |
|
||||
| --- | --- |
|
||||
| LLM Provider | `moonshot` |
|
||||
| LLM Model | `kimi-k2-0711-preview` |
|
||||
| API Key | Your Moonshot API key |
|
||||
|
||||
### Recommended Models
|
||||
|
||||
- `moonshot/kimi-k2-0711-preview` - Kimi-K2 is Moonshot's most powerful model with a 131K context window, function calling support, and web search capabilities.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: OpenHands
|
||||
description: OpenHands LLM provider with access to state-of-the-art (SOTA) agentic coding models.
|
||||
---
|
||||
|
||||
## Obtain Your OpenHands LLM API Key
|
||||
|
||||
1. [Log in to OpenHands Cloud](/usage/cloud/openhands-cloud).
|
||||
2. Go to the Settings page and navigate to the `API Keys` tab.
|
||||
3. Copy your `LLM API Key`.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
- `LLM Provider` to `OpenHands`
|
||||
- `LLM Model` to the model you will be using (e.g. claude-sonnet-4-20250514)
|
||||
- `API Key` to your OpenHands LLM API key copied from above
|
||||
|
||||
## Using OpenHands LLM Provider in the CLI
|
||||
|
||||
1. [Run OpenHands CLI](/usage/how-to/cli-mode).
|
||||
2. To select OpenHands as the LLM provider:
|
||||
- If this is your first time running the CLI, choose `openhands` and then select the model that you would like to use.
|
||||
- If you have previously run the CLI, run the `/settings` command and select to modify the `Basic` settings. Then
|
||||
choose `openhands` and finally the model.
|
||||
|
||||

|
||||
|
||||
## Pricing
|
||||
|
||||
Pricing follows official API provider rates.
|
||||
[You can view model prices here.](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json)
|
||||
@@ -67,17 +67,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
||||
### Start the App
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.48-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.48
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.49
|
||||
```
|
||||
|
||||
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
|
||||
|
||||
+46
-1
@@ -29,6 +29,15 @@ sse_servers = [
|
||||
{url="https://secure-example.com/mcp", api_key="your-api-key"}
|
||||
]
|
||||
|
||||
# SHTTP Servers - External servers that communicate via Streamable HTTP
|
||||
shttp_servers = [
|
||||
# Basic SHTTP server with just a URL
|
||||
"http://example.com:8080/mcp",
|
||||
|
||||
# SHTTP server with API key authentication
|
||||
{url="https://secure-example.com/mcp", api_key="your-api-key"}
|
||||
]
|
||||
|
||||
# Stdio Servers - Local processes that communicate via standard input/output
|
||||
stdio_servers = [
|
||||
# Basic stdio server
|
||||
@@ -57,6 +66,22 @@ SSE servers are configured using either a string URL or an object with the follo
|
||||
- Type: `str`
|
||||
- Description: The URL of the SSE server
|
||||
|
||||
- `api_key` (optional)
|
||||
- Type: `str`
|
||||
- Description: API key for authentication
|
||||
|
||||
### SHTTP Servers
|
||||
|
||||
SHTTP (Streamable HTTP) servers are configured using either a string URL or an object with the following properties:
|
||||
|
||||
- `url` (required)
|
||||
- Type: `str`
|
||||
- Description: The URL of the SHTTP server
|
||||
|
||||
- `api_key` (optional)
|
||||
- Type: `str`
|
||||
- Description: API key for authentication
|
||||
|
||||
### Stdio Servers
|
||||
|
||||
Stdio servers are configured using an object with the following properties:
|
||||
@@ -84,7 +109,7 @@ Stdio servers are configured using an object with the following properties:
|
||||
When OpenHands starts, it:
|
||||
|
||||
1. Reads the MCP configuration.
|
||||
2. Connects to any configured SSE servers.
|
||||
2. Connects to any configured SSE and SHTTP servers.
|
||||
3. Starts any configured stdio servers.
|
||||
4. Registers the tools provided by these servers with the agent.
|
||||
|
||||
@@ -93,3 +118,23 @@ The agent can then use these tools just like any built-in tool. When the agent c
|
||||
1. OpenHands routes the call to the appropriate MCP server.
|
||||
2. The server processes the request and returns a response.
|
||||
3. OpenHands converts the response to an observation and presents it to the agent.
|
||||
|
||||
## Transport Protocols
|
||||
|
||||
OpenHands supports three different MCP transport protocols:
|
||||
|
||||
### Server-Sent Events (SSE)
|
||||
SSE is a legacy HTTP-based transport that uses Server-Sent Events for server-to-client communication and HTTP POST requests for client-to-server communication. This transport is suitable for basic streaming scenarios but has limitations in session management and connection resumability.
|
||||
|
||||
### Streamable HTTP (SHTTP)
|
||||
SHTTP is the modern HTTP-based transport protocol that provides enhanced features over SSE:
|
||||
|
||||
- **Improved Session Management**: Supports stateful sessions with session IDs for maintaining context across requests
|
||||
- **Connection Resumability**: Can resume broken connections and replay missed messages using event IDs
|
||||
- **Bidirectional Communication**: Uses HTTP POST for client-to-server and optional SSE streams for server-to-client communication
|
||||
- **Better Error Handling**: Enhanced error reporting and recovery mechanisms
|
||||
|
||||
SHTTP is the recommended transport for HTTP-based MCP servers as it provides better reliability and features compared to the legacy SSE transport.
|
||||
|
||||
### Standard Input/Output (stdio)
|
||||
Stdio transport enables communication through standard input and output streams, making it ideal for local integrations and command-line tools. This transport is used for locally executed MCP servers that run as separate processes.
|
||||
|
||||
@@ -41,6 +41,10 @@ default, it is set to 1.
|
||||
- `language`, the language of your evaluating dataset.
|
||||
- `dataset`, the absolute position of the dataset jsonl.
|
||||
|
||||
**Skipping errors on build**
|
||||
|
||||
For debugging purposes, you can set `export EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED=true` to continue evaluation even when instances reach maximum retries. After evaluation completes, check `maximum_retries_exceeded.jsonl` for a list of failed instances, fix those issues, and then run the evaluation again with `export EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED=false`.
|
||||
|
||||
The results will be generated in evaluation/evaluation_outputs/outputs/XXX/CodeActAgent/YYY/output.jsonl, you can refer to the [example](examples/output.jsonl).
|
||||
|
||||
## Runing evaluation
|
||||
|
||||
@@ -17,6 +17,7 @@ from evaluation.utils.shared import (
|
||||
EvalMetadata,
|
||||
EvalOutput,
|
||||
assert_and_raise,
|
||||
check_maximum_retries_exceeded,
|
||||
codeact_user_response,
|
||||
get_default_sandbox_config_for_eval,
|
||||
get_metrics,
|
||||
@@ -843,3 +844,5 @@ if __name__ == '__main__':
|
||||
timeout_seconds=120 * 60, # 2 hour PER instance should be more than enough
|
||||
max_retries=5,
|
||||
)
|
||||
# Check if any instances reached maximum retries
|
||||
check_maximum_retries_exceeded(metadata.eval_output_dir)
|
||||
|
||||
@@ -38,6 +38,10 @@ Please follow instruction [here](../../README.md#setup) to setup your local deve
|
||||
> - If your LLM config has temperature=0, we will automatically use temperature=0.1 for the 2nd and 3rd attempts
|
||||
>
|
||||
> To enable this iterative protocol, set `export ITERATIVE_EVAL_MODE=true`
|
||||
>
|
||||
> **Skipping errors on build**
|
||||
>
|
||||
> For debugging purposes, you can set `export EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED=true` to continue evaluation even when instances reach maximum retries. After evaluation completes, check `maximum_retries_exceeded.jsonl` for a list of failed instances, fix those issues, and then run the evaluation again with `export EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED=false`.
|
||||
|
||||
|
||||
### Running Locally with Docker
|
||||
|
||||
@@ -28,6 +28,7 @@ from evaluation.utils.shared import (
|
||||
EvalMetadata,
|
||||
EvalOutput,
|
||||
assert_and_raise,
|
||||
check_maximum_retries_exceeded,
|
||||
codeact_user_response,
|
||||
get_default_sandbox_config_for_eval,
|
||||
get_metrics,
|
||||
@@ -968,3 +969,5 @@ if __name__ == '__main__':
|
||||
logger.info(
|
||||
f'Done! Total {len(added_instance_ids)} instances added to {output_file}'
|
||||
)
|
||||
# Check if any instances reached maximum retries
|
||||
check_maximum_retries_exceeded(metadata.eval_output_dir)
|
||||
|
||||
@@ -25,7 +25,8 @@ class Test(BaseIntegrationTest):
|
||||
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
|
||||
|
||||
# git add
|
||||
action = CmdRunAction(command='git add hello.py .vscode/')
|
||||
cmd_str = 'git add hello.py'
|
||||
action = CmdRunAction(command=cmd_str)
|
||||
obs = runtime.run_action(action)
|
||||
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
|
||||
|
||||
@@ -40,15 +41,6 @@ class Test(BaseIntegrationTest):
|
||||
reason=f'Failed to cat /workspace/hello.py: {obs.content}.',
|
||||
)
|
||||
|
||||
# check if the file /workspace/.vscode/settings.json exists
|
||||
action = CmdRunAction(command='cat /workspace/.vscode/settings.json')
|
||||
obs = runtime.run_action(action)
|
||||
if obs.exit_code != 0:
|
||||
return TestResult(
|
||||
success=False,
|
||||
reason=f'Failed to cat /workspace/.vscode/settings.json: {obs.content}.',
|
||||
)
|
||||
|
||||
# check if the staging area is empty
|
||||
action = CmdRunAction(command='git status')
|
||||
obs = runtime.run_action(action)
|
||||
|
||||
@@ -311,6 +311,76 @@ def assert_and_raise(condition: bool, msg: str):
|
||||
raise EvalException(msg)
|
||||
|
||||
|
||||
def log_skipped_maximum_retries_exceeded(instance, metadata, error, max_retries=5):
|
||||
"""Log and skip the instance when maximum retries are exceeded.
|
||||
|
||||
Args:
|
||||
instance: The instance that failed
|
||||
metadata: The evaluation metadata
|
||||
error: The error that occurred
|
||||
max_retries: The maximum number of retries that were attempted
|
||||
|
||||
Returns:
|
||||
EvalOutput with the error information
|
||||
"""
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
|
||||
# Log the error
|
||||
logger.exception(error)
|
||||
logger.error(
|
||||
f'Maximum error retries reached for instance {instance.instance_id}. '
|
||||
f'Check maximum_retries_exceeded.jsonl, fix the issue and run evaluation again. '
|
||||
f'Skipping this instance and continuing with others.'
|
||||
)
|
||||
|
||||
# Add the instance name to maximum_retries_exceeded.jsonl in the same folder as output.jsonl
|
||||
if metadata and metadata.eval_output_dir:
|
||||
retries_file_path = os.path.join(
|
||||
metadata.eval_output_dir,
|
||||
'maximum_retries_exceeded.jsonl',
|
||||
)
|
||||
try:
|
||||
# Write the instance info as a JSON line
|
||||
with open(retries_file_path, 'a') as f:
|
||||
import json
|
||||
|
||||
# No need to get Docker image as we're not including it in the error entry
|
||||
|
||||
error_entry = {
|
||||
'instance_id': instance.instance_id,
|
||||
'error': str(error),
|
||||
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
}
|
||||
f.write(json.dumps(error_entry) + '\n')
|
||||
logger.info(f'Added instance {instance.instance_id} to {retries_file_path}')
|
||||
except Exception as write_error:
|
||||
logger.error(
|
||||
f'Failed to write to maximum_retries_exceeded.jsonl: {write_error}'
|
||||
)
|
||||
|
||||
return EvalOutput(
|
||||
instance_id=instance.instance_id,
|
||||
test_result={},
|
||||
error=f'Maximum retries ({max_retries}) reached: {str(error)}',
|
||||
status='error',
|
||||
)
|
||||
|
||||
|
||||
def check_maximum_retries_exceeded(eval_output_dir):
|
||||
"""Check if maximum_retries_exceeded.jsonl exists and output a message."""
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
|
||||
retries_file_path = os.path.join(eval_output_dir, 'maximum_retries_exceeded.jsonl')
|
||||
if os.path.exists(retries_file_path):
|
||||
logger.info(
|
||||
'ATTENTION: Some instances reached maximum error retries and were skipped.'
|
||||
)
|
||||
logger.info(f'These instances are listed in: {retries_file_path}')
|
||||
logger.info(
|
||||
'Fix these instances and run evaluation again with EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED=false'
|
||||
)
|
||||
|
||||
|
||||
def _process_instance_wrapper(
|
||||
process_instance_func: Callable[[pd.Series, EvalMetadata, bool], EvalOutput],
|
||||
instance: pd.Series,
|
||||
@@ -363,11 +433,26 @@ def _process_instance_wrapper(
|
||||
+ f'[Encountered after {max_retries} retries. Please check the logs and report the issue.]'
|
||||
+ '-' * 10
|
||||
)
|
||||
# Raise an error after all retries & stop the evaluation
|
||||
logger.exception(e)
|
||||
raise RuntimeError(
|
||||
f'Maximum error retries reached for instance {instance.instance_id}'
|
||||
) from e
|
||||
|
||||
# Check if EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED is set to true
|
||||
skip_errors = (
|
||||
os.environ.get(
|
||||
'EVAL_SKIP_MAXIMUM_RETRIES_EXCEEDED', 'false'
|
||||
).lower()
|
||||
== 'true'
|
||||
)
|
||||
|
||||
if skip_errors:
|
||||
# Use the dedicated function to log and skip maximum retries exceeded
|
||||
return log_skipped_maximum_retries_exceeded(
|
||||
instance, metadata, e, max_retries
|
||||
)
|
||||
else:
|
||||
# Raise an error after all retries & stop the evaluation
|
||||
logger.exception(e)
|
||||
raise RuntimeError(
|
||||
f'Maximum error retries reached for instance {instance.instance_id}'
|
||||
) from e
|
||||
msg = (
|
||||
'-' * 10
|
||||
+ '\n'
|
||||
@@ -456,6 +541,10 @@ def run_evaluation(
|
||||
output_fp.close()
|
||||
logger.info('\nEvaluation finished.\n')
|
||||
|
||||
# Check if any instances reached maximum retries
|
||||
if metadata and metadata.eval_output_dir:
|
||||
check_maximum_retries_exceeded(metadata.eval_output_dir)
|
||||
|
||||
|
||||
def reset_logger_for_multiprocessing(
|
||||
logger: logging.Logger, instance_id: str, log_dir: str
|
||||
|
||||
@@ -110,4 +110,29 @@ describe("TaskCard", () => {
|
||||
expect(launchButton).toHaveTextContent(/Loading/i);
|
||||
expect(launchButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should navigate to the conversation page after creating a conversation", async () => {
|
||||
const createConversationSpy = vi.spyOn(OpenHands, "createConversation");
|
||||
createConversationSpy.mockResolvedValue({
|
||||
conversation_id: "test-conversation-id",
|
||||
title: "Test Conversation",
|
||||
selected_repository: "repo1",
|
||||
selected_branch: "main",
|
||||
git_provider: "github",
|
||||
last_updated_at: "2023-01-01T00:00:00Z",
|
||||
created_at: "2023-01-01T00:00:00Z",
|
||||
status: "RUNNING",
|
||||
runtime_status: "STATUS$READY",
|
||||
url: null,
|
||||
session_api_key: null
|
||||
});
|
||||
|
||||
renderTaskCard();
|
||||
|
||||
const launchButton = screen.getByTestId("task-launch-button");
|
||||
await userEvent.click(launchButton);
|
||||
|
||||
// Wait for navigation to the conversation page
|
||||
await screen.findByTestId("conversation-screen");
|
||||
});
|
||||
});
|
||||
|
||||
Generated
+729
-767
File diff suppressed because it is too large
Load Diff
+12
-11
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.48.0",
|
||||
"version": "0.49.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.0-beta.15",
|
||||
"@heroui/react": "^2.8.1",
|
||||
"@microlink/react-json-view": "^1.26.2",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.6.3",
|
||||
"@react-router/serve": "^7.6.3",
|
||||
"@react-router/node": "^7.7.0",
|
||||
"@react-router/serve": "^7.7.0",
|
||||
"@react-types/shared": "^3.29.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.4.0",
|
||||
"@stripe/stripe-js": "^7.5.0",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
@@ -25,12 +25,12 @@
|
||||
"axios": "^1.10.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.23.5",
|
||||
"framer-motion": "^12.23.6",
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"isbot": "^5.1.28",
|
||||
"jose": "^6.0.11",
|
||||
"jose": "^6.0.12",
|
||||
"lucide-react": "^0.525.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.257.0",
|
||||
@@ -42,14 +42,15 @@
|
||||
"react-icons": "^5.5.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.6.3",
|
||||
"react-router": "^7.7.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.0.4",
|
||||
"vite": "^7.0.5",
|
||||
"web-vitals": "^5.0.3",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
@@ -84,14 +85,14 @@
|
||||
"@babel/types": "^7.28.1",
|
||||
"@mswjs/socket.io-binding": "^0.2.0",
|
||||
"@playwright/test": "^1.54.1",
|
||||
"@react-router/dev": "^7.6.3",
|
||||
"@react-router/dev": "^7.7.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/eslint-plugin-query": "^5.81.2",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/node": "^24.0.13",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.10.2'
|
||||
const PACKAGE_VERSION = '2.10.3'
|
||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
@@ -50,9 +50,11 @@ export interface GetConfigResponse {
|
||||
GITHUB_CLIENT_ID: string;
|
||||
POSTHOG_CLIENT_KEY: string;
|
||||
STRIPE_PUBLISHABLE_KEY?: string;
|
||||
PROVIDERS_CONFIGURED?: Provider[];
|
||||
FEATURE_FLAGS: {
|
||||
ENABLE_BILLING: boolean;
|
||||
HIDE_LLM_SETTINGS: boolean;
|
||||
HIDE_MICROAGENT_MANAGEMENT?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import { code } from "../markdown/code";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { ul, ol } from "../markdown/list";
|
||||
@@ -94,7 +95,7 @@ export function ChatMessage({
|
||||
a: anchor,
|
||||
p: paragraph,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
>
|
||||
{message}
|
||||
</Markdown>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { code } from "../markdown/code";
|
||||
import { ol, ul } from "../markdown/list";
|
||||
@@ -46,7 +47,7 @@ export function ErrorMessage({ errorId, defaultMessage }: ErrorMessageProps) {
|
||||
ul,
|
||||
ol,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
>
|
||||
{defaultMessage}
|
||||
</Markdown>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Trans, useTranslation } from "react-i18next";
|
||||
import Markdown from "react-markdown";
|
||||
import { Link } from "react-router";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import ArrowDown from "#/icons/angle-down-solid.svg?react";
|
||||
@@ -199,7 +200,7 @@ export function ExpandableMessage({
|
||||
ol,
|
||||
p: paragraph,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
>
|
||||
{details}
|
||||
</Markdown>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import { code } from "../markdown/code";
|
||||
import { ol, ul } from "../markdown/list";
|
||||
import ArrowDown from "#/icons/angle-down-solid.svg?react";
|
||||
@@ -52,7 +53,7 @@ export function GenericEventMessage({
|
||||
ul,
|
||||
ol,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
>
|
||||
{details}
|
||||
</Markdown>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router";
|
||||
import { SuggestedTask } from "./task.types";
|
||||
import { useIsCreatingConversation } from "#/hooks/use-is-creating-conversation";
|
||||
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
|
||||
@@ -24,17 +25,25 @@ export function TaskCard({ task }: TaskCardProps) {
|
||||
const { mutate: createConversation, isPending } = useCreateConversation();
|
||||
const isCreatingConversation = useIsCreatingConversation();
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLaunchConversation = () => {
|
||||
setOptimisticUserMessage(t("TASK$ADDRESSING_TASK"));
|
||||
|
||||
return createConversation({
|
||||
repository: {
|
||||
name: task.repo,
|
||||
gitProvider: task.git_provider,
|
||||
return createConversation(
|
||||
{
|
||||
repository: {
|
||||
name: task.repo,
|
||||
gitProvider: task.git_provider,
|
||||
},
|
||||
suggestedTask: task,
|
||||
},
|
||||
suggestedTask: task,
|
||||
});
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
navigate(`/conversations/${data.conversation_id}`);
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// Determine the correct URL format based on git provider
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
interface MicroagentManagementAddMicroagentButtonProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function MicroagentManagementAddMicroagentButton({
|
||||
onClick,
|
||||
}: MicroagentManagementAddMicroagentButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="text-sm font-normal text-[#8480FF] cursor-pointer outline-none border-none"
|
||||
onClick={onClick}
|
||||
>
|
||||
{t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
interface MicroagentManagementLearnThisRepoProps {
|
||||
repositoryUrl: string;
|
||||
}
|
||||
|
||||
export function MicroagentManagementLearnThisRepo({
|
||||
repositoryUrl,
|
||||
}: MicroagentManagementLearnThisRepoProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center rounded-lg bg-[#ffffff0d] border border-dashed border-[#ffffff4d] p-4 hover:bg-[#ffffff33] hover:border-[#C9B974] transition-all duration-300 cursor-pointer">
|
||||
<a
|
||||
className="text-[16px] font-normal text-[#8480FF]"
|
||||
href={repositoryUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$LEARN_THIS_REPO)}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
export function MicroagentManagementMain() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagent } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
if (!selectedMicroagent) {
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#F9FBFE] text-[20px] font-bold pb-4">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT)}
|
||||
</div>
|
||||
<div className="text-white text-[14px] font-normal text-center max-w-[455px]">
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
export interface Microagent {
|
||||
id: string;
|
||||
name: string;
|
||||
repositoryUrl: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface MicroagentManagementMicroagentCardProps {
|
||||
microagent: Microagent;
|
||||
}
|
||||
|
||||
export function MicroagentManagementMicroagentCard({
|
||||
microagent,
|
||||
}: MicroagentManagementMicroagentCardProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="rounded-lg bg-[#ffffff0d] border border-[#ffffff33] p-4 cursor-pointer hover:bg-[#ffffff33] hover:border-[#C9B974] transition-all duration-300">
|
||||
<div className="text-white text-[16px] font-semibold">
|
||||
{microagent.name}
|
||||
</div>
|
||||
<div className="text-white text-[14px] font-normal">
|
||||
{microagent.repositoryUrl}
|
||||
</div>
|
||||
<div className="text-white text-[14px] font-normal">
|
||||
{t(I18nKey.COMMON$CREATED_ON)} {microagent.createdAt}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { MicroagentManagementMicroagentCard } from "./microagent-management-microagent-card";
|
||||
import { MicroagentManagementAddMicroagentButton } from "./microagent-management-add-microagent-button";
|
||||
|
||||
export function MicroagentManagementMicroagents() {
|
||||
const microagents = [
|
||||
{
|
||||
id: "no-comments",
|
||||
name: "No comments",
|
||||
repositoryUrl: "fairwinds/polaris/Repo Overview",
|
||||
createdAt: "05/30/2025",
|
||||
},
|
||||
{
|
||||
id: "tell-me-a-joke",
|
||||
name: "Tell me a joke",
|
||||
repositoryUrl: ".openhands/microagents/Repo Overview",
|
||||
createdAt: "05/30/2025",
|
||||
},
|
||||
];
|
||||
|
||||
const numberOfMicroagents = microagents.length;
|
||||
|
||||
if (numberOfMicroagents === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center justify-end pb-4">
|
||||
<MicroagentManagementAddMicroagentButton onClick={() => {}} />
|
||||
</div>
|
||||
{microagents.map((microagent) => (
|
||||
<div key={microagent.id} className="pb-4">
|
||||
<MicroagentManagementMicroagentCard microagent={microagent} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
Microagent,
|
||||
MicroagentManagementMicroagentCard,
|
||||
} from "./microagent-management-microagent-card";
|
||||
import { MicroagentManagementLearnThisRepo } from "./microagent-management-learn-this-repo";
|
||||
import { MicroagentManagementAddMicroagentButton } from "./microagent-management-add-microagent-button";
|
||||
|
||||
export interface RepoMicroagent {
|
||||
id: string;
|
||||
repositoryName: string;
|
||||
repositoryUrl: string;
|
||||
microagents: Microagent[];
|
||||
}
|
||||
|
||||
interface MicroagentManagementRepoMicroagentProps {
|
||||
repoMicroagent: RepoMicroagent;
|
||||
}
|
||||
|
||||
export function MicroagentManagementRepoMicroagent({
|
||||
repoMicroagent,
|
||||
}: MicroagentManagementRepoMicroagentProps) {
|
||||
const { microagents } = repoMicroagent;
|
||||
const numberOfMicroagents = microagents.length;
|
||||
|
||||
return (
|
||||
<div className="pb-12">
|
||||
<div className="flex items-center justify-between pb-4">
|
||||
<div className="text-white text-base font-normal">
|
||||
{repoMicroagent.repositoryName}
|
||||
</div>
|
||||
<MicroagentManagementAddMicroagentButton onClick={() => {}} />
|
||||
</div>
|
||||
{numberOfMicroagents === 0 && (
|
||||
<MicroagentManagementLearnThisRepo
|
||||
repositoryUrl={repoMicroagent.repositoryUrl}
|
||||
/>
|
||||
)}
|
||||
{numberOfMicroagents > 0 && (
|
||||
<>
|
||||
{microagents.map((microagent) => (
|
||||
<div key={microagent.id} className="pb-4 last:pb-0">
|
||||
<MicroagentManagementMicroagentCard microagent={microagent} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { MicroagentManagementRepoMicroagent } from "./microagent-management-repo-microagent";
|
||||
|
||||
export function MicroagentManagementRepoMicroagents() {
|
||||
const repoMicroagents = [
|
||||
{
|
||||
id: "rbren/rss-parser",
|
||||
repositoryName: "rbren/rss-parser",
|
||||
repositoryUrl: "https://github.com/rbren/rss-parser",
|
||||
microagents: [],
|
||||
},
|
||||
{
|
||||
id: "fairwinds/polaris",
|
||||
repositoryName: "fairwinds/polaris",
|
||||
repositoryUrl: "https://github.com/fairwinds/polaris",
|
||||
microagents: [
|
||||
{
|
||||
id: "no-comments",
|
||||
name: "No comments",
|
||||
repositoryUrl: "fairwinds/polaris/Repo Overview",
|
||||
createdAt: "05/30/2025",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const numberOfRepoMicroagents = repoMicroagents.length;
|
||||
|
||||
if (numberOfRepoMicroagents === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{repoMicroagents.map((repoMicroagent) => (
|
||||
<MicroagentManagementRepoMicroagent
|
||||
key={repoMicroagent.id}
|
||||
repoMicroagent={repoMicroagent}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import QuestionCircleIcon from "#/icons/question-circle.svg?react";
|
||||
|
||||
export function MicroagentManagementSidebarHeader() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-white text-[28px] font-bold">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$DESCRIPTION)}
|
||||
</h1>
|
||||
<p className="text-white text-[14px] font-normal leading-[20px] pt-2">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$USE_MICROAGENTS)}
|
||||
<QuestionCircleIcon className="inline-block ml-1" />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { Tab, Tabs } from "@heroui/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MicroagentManagementMicroagents } from "./microagent-management-microagents";
|
||||
import { MicroagentManagementRepoMicroagents } from "./microagent-management-repo-microagents";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
export function MicroagentManagementSidebarTabs() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col">
|
||||
<Tabs
|
||||
aria-label="Options"
|
||||
classNames={{
|
||||
base: "py-6",
|
||||
tabList:
|
||||
"w-full bg-transparent border border-[#ffffff40] rounded-[6px]",
|
||||
tab: "px-2 h-[22px]",
|
||||
tabContent: "text-white text-[12px] font-normal",
|
||||
panel: "py-0",
|
||||
cursor: "bg-[#C9B97480] rounded-sm",
|
||||
}}
|
||||
>
|
||||
<Tab key="personal" title={t(I18nKey.COMMON$PERSONAL)}>
|
||||
<MicroagentManagementMicroagents />
|
||||
</Tab>
|
||||
<Tab key="repositories" title={t(I18nKey.COMMON$REPOSITORIES)}>
|
||||
<MicroagentManagementRepoMicroagents />
|
||||
</Tab>
|
||||
<Tab key="organizations" title={t(I18nKey.COMMON$ORGANIZATIONS)}>
|
||||
<MicroagentManagementMicroagents />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MicroagentManagementSidebarHeader } from "./microagent-management-sidebar-header";
|
||||
import { MicroagentManagementSidebarTabs } from "./microagent-management-sidebar-tabs";
|
||||
|
||||
export function MicroagentManagementSidebar() {
|
||||
return (
|
||||
<div className="w-[418px] h-full border-r border-[#525252] bg-[#24272E] rounded-tl-lg rounded-bl-lg py-10 px-6">
|
||||
<MicroagentManagementSidebarHeader />
|
||||
<MicroagentManagementSidebarTabs />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { ConversationPanelWrapper } from "../conversation-panel/conversation-pan
|
||||
import { useLogout } from "#/hooks/mutation/use-logout";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
import { displayErrorToast } from "#/utils/custom-toast-handlers";
|
||||
import { MicroagentManagementButton } from "#/components/shared/buttons/microagent-management-button";
|
||||
|
||||
export function Sidebar() {
|
||||
const location = useLocation();
|
||||
@@ -36,6 +37,9 @@ export function Sidebar() {
|
||||
const shouldHideLlmSettings =
|
||||
config?.FEATURE_FLAGS.HIDE_LLM_SETTINGS && config?.APP_MODE === "saas";
|
||||
|
||||
const shouldHideMicroagentManagement =
|
||||
config?.FEATURE_FLAGS.HIDE_MICROAGENT_MANAGEMENT;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (shouldHideLlmSettings) return;
|
||||
|
||||
@@ -79,6 +83,11 @@ export function Sidebar() {
|
||||
}
|
||||
disabled={settings?.EMAIL_VERIFIED === false}
|
||||
/>
|
||||
{!shouldHideMicroagentManagement && (
|
||||
<MicroagentManagementButton
|
||||
disabled={settings?.EMAIL_VERIFIED === false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row md:flex-col md:items-center gap-[26px] md:mb-4">
|
||||
|
||||
@@ -10,13 +10,19 @@ import GitLabLogo from "#/assets/branding/gitlab-logo.svg?react";
|
||||
import BitbucketLogo from "#/assets/branding/bitbucket-logo.svg?react";
|
||||
import { useAuthUrl } from "#/hooks/use-auth-url";
|
||||
import { GetConfigResponse } from "#/api/open-hands.types";
|
||||
import { Provider } from "#/types/settings";
|
||||
|
||||
interface AuthModalProps {
|
||||
githubAuthUrl: string | null;
|
||||
appMode?: GetConfigResponse["APP_MODE"] | null;
|
||||
providersConfigured?: Provider[];
|
||||
}
|
||||
|
||||
export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
export function AuthModal({
|
||||
githubAuthUrl,
|
||||
appMode,
|
||||
providersConfigured,
|
||||
}: AuthModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const gitlabAuthUrl = useAuthUrl({
|
||||
@@ -50,6 +56,24 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
}
|
||||
};
|
||||
|
||||
// Only show buttons if providers are configured and include the specific provider
|
||||
const showGithub =
|
||||
providersConfigured &&
|
||||
providersConfigured.length > 0 &&
|
||||
providersConfigured.includes("github");
|
||||
const showGitlab =
|
||||
providersConfigured &&
|
||||
providersConfigured.length > 0 &&
|
||||
providersConfigured.includes("gitlab");
|
||||
const showBitbucket =
|
||||
providersConfigured &&
|
||||
providersConfigured.length > 0 &&
|
||||
providersConfigured.includes("bitbucket");
|
||||
|
||||
// Check if no providers are configured
|
||||
const noProvidersConfigured =
|
||||
!providersConfigured || providersConfigured.length === 0;
|
||||
|
||||
return (
|
||||
<ModalBackdrop>
|
||||
<ModalBody className="border border-tertiary">
|
||||
@@ -61,35 +85,49 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitHubAuth}
|
||||
className="w-full"
|
||||
startContent={<GitHubLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.GITHUB$CONNECT_TO_GITHUB)}
|
||||
</BrandButton>
|
||||
{noProvidersConfigured ? (
|
||||
<div className="text-center p-4 text-muted-foreground">
|
||||
{t(I18nKey.AUTH$NO_PROVIDERS_CONFIGURED)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{showGithub && (
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitHubAuth}
|
||||
className="w-full"
|
||||
startContent={<GitHubLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.GITHUB$CONNECT_TO_GITHUB)}
|
||||
</BrandButton>
|
||||
)}
|
||||
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitLabAuth}
|
||||
className="w-full"
|
||||
startContent={<GitLabLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.GITLAB$CONNECT_TO_GITLAB)}
|
||||
</BrandButton>
|
||||
{showGitlab && (
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitLabAuth}
|
||||
className="w-full"
|
||||
startContent={<GitLabLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.GITLAB$CONNECT_TO_GITLAB)}
|
||||
</BrandButton>
|
||||
)}
|
||||
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleBitbucketAuth}
|
||||
className="w-full"
|
||||
startContent={<BitbucketLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.BITBUCKET$CONNECT_TO_BITBUCKET)}
|
||||
</BrandButton>
|
||||
{showBitbucket && (
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleBitbucketAuth}
|
||||
className="w-full"
|
||||
startContent={<BitbucketLogo width={20} height={20} />}
|
||||
>
|
||||
{t(I18nKey.BITBUCKET$CONNECT_TO_BITBUCKET)}
|
||||
</BrandButton>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { TooltipButton } from "./tooltip-button";
|
||||
import UnionIcon from "#/icons/union.svg?react";
|
||||
|
||||
interface MicroagentManagementButtonProps {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function MicroagentManagementButton({
|
||||
disabled = false,
|
||||
}: MicroagentManagementButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const microagentManagement = t(I18nKey.MICROAGENT_MANAGEMENT$TITLE);
|
||||
|
||||
return (
|
||||
<TooltipButton
|
||||
tooltip={microagentManagement}
|
||||
ariaLabel={microagentManagement}
|
||||
navLinkTo="/microagent-management"
|
||||
testId="microagent-management-button"
|
||||
disabled={disabled}
|
||||
>
|
||||
<UnionIcon />
|
||||
</TooltipButton>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,11 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { mapProvider } from "#/utils/map-provider";
|
||||
import { VERIFIED_MODELS, VERIFIED_PROVIDERS } from "#/utils/verified-models";
|
||||
import {
|
||||
VERIFIED_MODELS,
|
||||
VERIFIED_PROVIDERS,
|
||||
VERIFIED_OPENHANDS_MODELS,
|
||||
} from "#/utils/verified-models";
|
||||
import { extractModelAndProvider } from "#/utils/extract-model-and-provider";
|
||||
|
||||
interface ModelSelectorProps {
|
||||
@@ -29,6 +33,14 @@ export function ModelSelector({
|
||||
);
|
||||
const [selectedModel, setSelectedModel] = React.useState<string | null>(null);
|
||||
|
||||
// Get the appropriate verified models array based on the selected provider
|
||||
const getVerifiedModels = () => {
|
||||
if (selectedProvider === "openhands") {
|
||||
return VERIFIED_OPENHANDS_MODELS;
|
||||
}
|
||||
return VERIFIED_MODELS;
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (currentModel) {
|
||||
// runs when resetting to defaults
|
||||
@@ -151,18 +163,20 @@ export function ModelSelector({
|
||||
}}
|
||||
>
|
||||
<AutocompleteSection title={t(I18nKey.MODEL_SELECTOR$VERIFIED)}>
|
||||
{VERIFIED_MODELS.filter((model) =>
|
||||
models[selectedProvider || ""]?.models?.includes(model),
|
||||
).map((model) => (
|
||||
<AutocompleteItem key={model}>{model}</AutocompleteItem>
|
||||
))}
|
||||
{getVerifiedModels()
|
||||
.filter((model) =>
|
||||
models[selectedProvider || ""]?.models?.includes(model),
|
||||
)
|
||||
.map((model) => (
|
||||
<AutocompleteItem key={model}>{model}</AutocompleteItem>
|
||||
))}
|
||||
</AutocompleteSection>
|
||||
{models[selectedProvider || ""]?.models?.some(
|
||||
(model) => !VERIFIED_MODELS.includes(model),
|
||||
(model) => !getVerifiedModels().includes(model),
|
||||
) ? (
|
||||
<AutocompleteSection title={t(I18nKey.MODEL_SELECTOR$OTHERS)}>
|
||||
{models[selectedProvider || ""]?.models
|
||||
.filter((model) => !VERIFIED_MODELS.includes(model))
|
||||
.filter((model) => !getVerifiedModels().includes(model))
|
||||
.map((model) => (
|
||||
<AutocompleteItem
|
||||
data-testid={`model-item-${model}`}
|
||||
|
||||
@@ -682,8 +682,20 @@ export enum I18nKey {
|
||||
COMMON$OPTIONAL = "COMMON$OPTIONAL",
|
||||
BROWSER$SERVER_MESSAGE = "BROWSER$SERVER_MESSAGE",
|
||||
API$NO_KEY_AVAILABLE = "API$NO_KEY_AVAILABLE",
|
||||
MICROAGENT_MANAGEMENT$TITLE = "MICROAGENT_MANAGEMENT$TITLE",
|
||||
MICROAGENT_MANAGEMENT$DESCRIPTION = "MICROAGENT_MANAGEMENT$DESCRIPTION",
|
||||
MICROAGENT_MANAGEMENT$USE_MICROAGENTS = "MICROAGENT_MANAGEMENT$USE_MICROAGENTS",
|
||||
AUTH$BY_SIGNING_UP_YOU_AGREE_TO_OUR = "AUTH$BY_SIGNING_UP_YOU_AGREE_TO_OUR",
|
||||
AUTH$NO_PROVIDERS_CONFIGURED = "AUTH$NO_PROVIDERS_CONFIGURED",
|
||||
COMMON$TERMS_OF_SERVICE = "COMMON$TERMS_OF_SERVICE",
|
||||
COMMON$AND = "COMMON$AND",
|
||||
COMMON$PRIVACY_POLICY = "COMMON$PRIVACY_POLICY",
|
||||
COMMON$PERSONAL = "COMMON$PERSONAL",
|
||||
COMMON$REPOSITORIES = "COMMON$REPOSITORIES",
|
||||
COMMON$ORGANIZATIONS = "COMMON$ORGANIZATIONS",
|
||||
COMMON$ADD_MICROAGENT = "COMMON$ADD_MICROAGENT",
|
||||
COMMON$CREATED_ON = "COMMON$CREATED_ON",
|
||||
MICROAGENT_MANAGEMENT$LEARN_THIS_REPO = "MICROAGENT_MANAGEMENT$LEARN_THIS_REPO",
|
||||
MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT = "MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT",
|
||||
MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES = "MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES",
|
||||
}
|
||||
|
||||
@@ -10911,6 +10911,54 @@
|
||||
"de": "Kein API-Schlüssel verfügbar",
|
||||
"uk": "Немає доступного API-ключа"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$TITLE": {
|
||||
"en": "Microagent Management",
|
||||
"ja": "マイクロエージェント管理",
|
||||
"zh-CN": "微代理管理",
|
||||
"zh-TW": "微代理管理",
|
||||
"ko-KR": "마이크로에이전트 관리",
|
||||
"no": "Microagent-administrasjon",
|
||||
"it": "Gestione Microagent",
|
||||
"pt": "Gerenciamento de Microagente",
|
||||
"es": "Gestión de microagentes",
|
||||
"ar": "إدارة الميكرووكيل",
|
||||
"fr": "Gestion des microagents",
|
||||
"tr": "Mikroajan Yönetimi",
|
||||
"de": "Microagent-Verwaltung",
|
||||
"uk": "Управління мікроагентами"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$DESCRIPTION": {
|
||||
"en": "Manage Microagents",
|
||||
"ja": "マイクロエージェントを管理する",
|
||||
"zh-CN": "管理微代理",
|
||||
"zh-TW": "管理微代理",
|
||||
"ko-KR": "마이크로에이전트 관리",
|
||||
"no": "Administrer mikroagenter",
|
||||
"it": "Gestisci i Microagent",
|
||||
"pt": "Gerenciar Microagentes",
|
||||
"es": "Gestionar microagentes",
|
||||
"ar": "إدارة الميكرووكلاء",
|
||||
"fr": "Gérer les microagents",
|
||||
"tr": "Mikro ajanları yönet",
|
||||
"de": "Microagents verwalten",
|
||||
"uk": "Керування мікроагентами"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$USE_MICROAGENTS": {
|
||||
"en": "Use microagents to customize the behavior of OpenHands, teach it about your repositories, and help it work faster.",
|
||||
"ja": "マイクロエージェントを使ってOpenHandsの動作をカスタマイズし、リポジトリについて教え、より速く作業できるようにしましょう。",
|
||||
"zh-CN": "使用微代理自定义OpenHands的行为,让其了解您的仓库,并帮助其更快地工作。",
|
||||
"zh-TW": "使用微代理自訂 OpenHands 的行為,讓其了解您的倉庫,並幫助其更快地工作。",
|
||||
"ko-KR": "마이크로에이전트를 사용하여 OpenHands의 동작을 사용자 정의하고, 저장소에 대해 학습시키며, 더 빠르게 작업할 수 있도록 도와주세요.",
|
||||
"no": "Bruk mikroagenter for å tilpasse oppførselen til OpenHands, lære den om dine repositories, og hjelpe den å jobbe raskere.",
|
||||
"it": "Usa i microagent per personalizzare il comportamento di OpenHands, insegnargli i tuoi repository e aiutarlo a lavorare più velocemente.",
|
||||
"pt": "Use microagentes para personalizar o comportamento do OpenHands, ensiná-lo sobre seus repositórios e ajudá-lo a trabalhar mais rápido.",
|
||||
"es": "Utiliza microagentes para personalizar el comportamiento de OpenHands, enseñarle sobre tus repositorios y ayudarle a trabajar más rápido.",
|
||||
"ar": "استخدم الوكلاء المصغرين لتخصيص سلوك OpenHands، وتعليمه حول مستودعاتك، ومساعدته على العمل بشكل أسرع.",
|
||||
"fr": "Utilisez des microagents pour personnaliser le comportement d'OpenHands, lui apprendre vos dépôts et l'aider à travailler plus rapidement.",
|
||||
"tr": "OpenHands'in davranışını özelleştirmek, depolarınızı ona öğretmek ve daha hızlı çalışmasına yardımcı olmak için mikro ajanları kullanın.",
|
||||
"de": "Verwenden Sie Microagents, um das Verhalten von OpenHands anzupassen, ihm Ihre Repositories beizubringen und ihm zu helfen, schneller zu arbeiten.",
|
||||
"uk": "Використовуйте мікроагенти, щоб налаштувати поведінку OpenHands, навчити його про ваші репозиторії та допомогти йому працювати швидше."
|
||||
},
|
||||
"AUTH$BY_SIGNING_UP_YOU_AGREE_TO_OUR": {
|
||||
"en": "By signing up, you agree to our",
|
||||
"ja": "サインアップすることで、あなたは当社の",
|
||||
@@ -10927,6 +10975,22 @@
|
||||
"de": "Mit der Anmeldung stimmen Sie unseren",
|
||||
"uk": "Реєструючись, ви погоджуєтеся з нашими"
|
||||
},
|
||||
"AUTH$NO_PROVIDERS_CONFIGURED": {
|
||||
"en": "At least one identity provider must be configured (e.g., GitHub)",
|
||||
"ja": "少なくとも1つのIDプロバイダーを設定する必要があります(例:GitHub)",
|
||||
"zh-CN": "必须配置至少一个身份提供商(例如 GitHub)",
|
||||
"zh-TW": "必須配置至少一個身份提供商(例如 GitHub)",
|
||||
"ko-KR": "최소 하나의 ID 제공자를 구성해야 합니다 (예: GitHub)",
|
||||
"no": "Minst én identitetsleverandør må konfigureres (f.eks. GitHub)",
|
||||
"it": "Deve essere configurato almeno un provider di identità (ad es. GitHub)",
|
||||
"pt": "Pelo menos um provedor de identidade deve ser configurado (ex: GitHub)",
|
||||
"es": "Debe configurarse al menos un proveedor de identidad (ej: GitHub)",
|
||||
"ar": "يجب تكوين مزود هوية واحد على الأقل (مثل GitHub)",
|
||||
"fr": "Au moins un fournisseur d'identité doit être configuré (ex: GitHub)",
|
||||
"tr": "En az bir kimlik sağlayıcı yapılandırılmalıdır (örn. GitHub)",
|
||||
"de": "Mindestens ein Identitätsanbieter muss konfiguriert werden (z.B. GitHub)",
|
||||
"uk": "Принаймні один постачальник ідентифікації має бути налаштований (наприклад, GitHub)"
|
||||
},
|
||||
"COMMON$TERMS_OF_SERVICE": {
|
||||
"en": "Terms of Service",
|
||||
"ja": "利用規約",
|
||||
@@ -10974,5 +11038,133 @@
|
||||
"tr": "Gizlilik Politikası",
|
||||
"de": "Datenschutzrichtlinie",
|
||||
"uk": "Політика конфіденційності"
|
||||
},
|
||||
"COMMON$PERSONAL": {
|
||||
"en": "Personal",
|
||||
"ja": "個人用",
|
||||
"zh-CN": "个人",
|
||||
"zh-TW": "個人",
|
||||
"ko-KR": "개인",
|
||||
"no": "Personlig",
|
||||
"it": "Personale",
|
||||
"pt": "Pessoal",
|
||||
"es": "Personal",
|
||||
"ar": "شخصي",
|
||||
"fr": "Personnel",
|
||||
"tr": "Kişisel",
|
||||
"de": "Persönlich",
|
||||
"uk": "Особистий"
|
||||
},
|
||||
"COMMON$REPOSITORIES": {
|
||||
"en": "Repositories",
|
||||
"ja": "リポジトリ",
|
||||
"zh-CN": "仓库",
|
||||
"zh-TW": "儲存庫",
|
||||
"ko-KR": "저장소",
|
||||
"no": "Arkiver",
|
||||
"it": "Repository",
|
||||
"pt": "Repositórios",
|
||||
"es": "Repositorios",
|
||||
"ar": "المستودعات",
|
||||
"fr": "Dépôts",
|
||||
"tr": "Depolar",
|
||||
"de": "Repositories",
|
||||
"uk": "Репозиторії"
|
||||
},
|
||||
"COMMON$ORGANIZATIONS": {
|
||||
"en": "Organizations",
|
||||
"ja": "組織",
|
||||
"zh-CN": "组织",
|
||||
"zh-TW": "組織",
|
||||
"ko-KR": "조직",
|
||||
"no": "Organisasjoner",
|
||||
"it": "Organizzazioni",
|
||||
"pt": "Organizações",
|
||||
"es": "Organizaciones",
|
||||
"ar": "المؤسسات",
|
||||
"fr": "Organisations",
|
||||
"tr": "Organizasyonlar",
|
||||
"de": "Organisationen",
|
||||
"uk": "Організації"
|
||||
},
|
||||
"COMMON$ADD_MICROAGENT": {
|
||||
"en": "Add Microagent",
|
||||
"ja": "マイクロエージェントを追加",
|
||||
"zh-CN": "添加微代理",
|
||||
"zh-TW": "新增微代理",
|
||||
"ko-KR": "마이크로에이전트 추가",
|
||||
"no": "Legg til mikroagent",
|
||||
"it": "Aggiungi Microagent",
|
||||
"pt": "Adicionar Microagente",
|
||||
"es": "Agregar microagente",
|
||||
"ar": "إضافة وكيل صغير",
|
||||
"fr": "Ajouter un microagent",
|
||||
"tr": "Mikro ajan ekle",
|
||||
"de": "Microagent hinzufügen",
|
||||
"uk": "Додати мікроагента"
|
||||
},
|
||||
"COMMON$CREATED_ON": {
|
||||
"en": "Created on",
|
||||
"ja": "作成日",
|
||||
"zh-CN": "创建于",
|
||||
"zh-TW": "建立於",
|
||||
"ko-KR": "생성됨",
|
||||
"no": "Opprettet",
|
||||
"it": "Creato",
|
||||
"pt": "Criado",
|
||||
"es": "Creado",
|
||||
"ar": "تم الإنشاء",
|
||||
"fr": "Créé",
|
||||
"tr": "Oluşturuldu",
|
||||
"de": "Erstellt",
|
||||
"uk": "Створено"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$LEARN_THIS_REPO": {
|
||||
"en": "Learn this repo",
|
||||
"ja": "このリポジトリを学習する",
|
||||
"zh-CN": "学习此仓库",
|
||||
"zh-TW": "學習此儲存庫",
|
||||
"ko-KR": "이 저장소 학습",
|
||||
"no": "Lær dette repoet",
|
||||
"it": "Impara questo repository",
|
||||
"pt": "Aprender este repositório",
|
||||
"es": "Aprender este repositorio",
|
||||
"ar": "تعلم هذا المستودع",
|
||||
"fr": "Apprendre ce dépôt",
|
||||
"tr": "Bu depoyu öğren",
|
||||
"de": "Dieses Repository lernen",
|
||||
"uk": "Вивчити цей репозиторій"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT": {
|
||||
"en": "Ready to add a microagent?",
|
||||
"ja": "マイクロエージェントを追加する準備はできましたか?",
|
||||
"zh-CN": "准备好添加微代理了吗?",
|
||||
"zh-TW": "準備好新增微代理了嗎?",
|
||||
"ko-KR": "마이크로에이전트를 추가할 준비가 되셨나요?",
|
||||
"no": "Klar til å legge til en mikroagent?",
|
||||
"it": "Pronto ad aggiungere un microagent?",
|
||||
"pt": "Pronto para adicionar um microagente?",
|
||||
"es": "¿Listo para agregar un microagente?",
|
||||
"ar": "هل أنت مستعد لإضافة وكيل صغير؟",
|
||||
"fr": "Prêt à ajouter un microagent ?",
|
||||
"tr": "Bir mikro ajan eklemeye hazır mısınız?",
|
||||
"de": "Bereit, einen Microagent hinzuzufügen?",
|
||||
"uk": "Готові додати мікроагента?"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES": {
|
||||
"en": "OpenHands can automatically learn about your repositories and store its findings as markdown in a microagent. The microagent will help OpenHands run faster and more accurately in any future conversations.",
|
||||
"ja": "OpenHandsはあなたのリポジトリについて自動的に学習し、その結果をマイクロエージェント内のMarkdownとして保存できます。マイクロエージェントは今後の会話でOpenHandsがより速く、より正確に動作するのに役立ちます。",
|
||||
"zh-CN": "OpenHands 可以自动学习您的仓库,并将其发现以 markdown 形式存储在微代理中。微代理将帮助 OpenHands 在未来的对话中运行得更快、更准确。",
|
||||
"zh-TW": "OpenHands 可以自動學習您的儲存庫,並將其發現以 markdown 形式儲存在微代理中。微代理將幫助 OpenHands 在未來的對話中運行得更快、更準確。",
|
||||
"ko-KR": "OpenHands는 사용자의 저장소를 자동으로 학습하고, 그 결과를 마이크로에이전트의 마크다운으로 저장할 수 있습니다. 마이크로에이전트는 앞으로의 대화에서 OpenHands가 더 빠르고 정확하게 동작하도록 도와줍니다.",
|
||||
"no": "OpenHands kan automatisk lære om dine repositorier og lagre funnene som markdown i en mikroagent. Mikroagenten vil hjelpe OpenHands å kjøre raskere og mer nøyaktig i fremtidige samtaler.",
|
||||
"it": "OpenHands può apprendere automaticamente dai tuoi repository e memorizzare i risultati come markdown in un microagent. Il microagent aiuterà OpenHands a funzionare più velocemente e con maggiore precisione in conversazioni future.",
|
||||
"pt": "O OpenHands pode aprender automaticamente sobre seus repositórios e armazenar suas descobertas como markdown em um microagente. O microagente ajudará o OpenHands a funcionar mais rápido e com mais precisão em conversas futuras.",
|
||||
"es": "OpenHands puede aprender automáticamente sobre tus repositorios y almacenar sus hallazgos como markdown en un microagente. El microagente ayudará a que OpenHands funcione más rápido y con mayor precisión en futuras conversaciones.",
|
||||
"ar": "يمكن لـ OpenHands التعرف تلقائيًا على مستودعاتك وتخزين النتائج كـ markdown في وكيل صغير. سيساعد الوكيل الصغير OpenHands على العمل بشكل أسرع وأكثر دقة في أي محادثات مستقبلية.",
|
||||
"fr": "OpenHands peut automatiquement apprendre à connaître vos dépôts et stocker ses découvertes en markdown dans un microagent. Le microagent aidera OpenHands à fonctionner plus rapidement et plus précisément lors de futures conversations.",
|
||||
"tr": "OpenHands, depolarınızı otomatik olarak öğrenebilir ve bulgularını bir mikro ajan içinde markdown olarak saklayabilir. Mikro ajan, OpenHands'in gelecekteki konuşmalarda daha hızlı ve daha doğru çalışmasına yardımcı olur.",
|
||||
"de": "OpenHands kann automatisch Informationen über Ihre Repositories sammeln und die Ergebnisse als Markdown in einem Microagent speichern. Der Microagent hilft OpenHands, in zukünftigen Gesprächen schneller und genauer zu arbeiten.",
|
||||
"uk": "OpenHands може автоматично вивчати ваші репозиторії та зберігати свої висновки у вигляді markdown у мікроагенті. Мікроагент допоможе OpenHands працювати швидше та точніше у майбутніх розмовах."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 15C4.13401 15 1 11.866 1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8C15 11.866 11.866 15 8 15ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16Z" fill="white"/>
|
||||
<path d="M5.25511 5.78615C5.24752 5.92237 5.3599 6.03271 5.49634 6.03271H6.32082C6.45889 6.03271 6.56868 5.92013 6.58723 5.78331C6.67618 5.12718 7.1265 4.64893 7.92922 4.64893C8.61477 4.64893 9.24318 4.9917 9.24318 5.81689C9.24318 6.45166 8.86867 6.74365 8.27834 7.18799C7.60549 7.67676 7.07229 8.24805 7.11037 9.1748L7.11334 9.39161C7.11521 9.52833 7.22658 9.63818 7.36332 9.63818H8.17434C8.31241 9.63818 8.42434 9.52625 8.42434 9.38818V9.28271C8.42434 8.56543 8.69729 8.35596 9.43361 7.79736C10.043 7.33398 10.6778 6.81982 10.6778 5.74072C10.6778 4.22998 9.40188 3.5 8.00539 3.5C6.73831 3.5 5.34964 4.09061 5.25511 5.78615ZM6.81203 11.5488C6.81203 12.082 7.23732 12.4756 7.82131 12.4756C8.43068 12.4756 8.84963 12.082 8.84963 11.5488C8.84963 10.9966 8.43068 10.6094 7.82131 10.6094C7.23732 10.6094 6.81203 10.9966 6.81203 11.5488Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="21" viewBox="0 0 24 21" fill="none">
|
||||
<path d="M3 1.5C2.17157 1.5 1.5 2.17157 1.5 3V15C1.5 15.8284 2.17157 16.5 3 16.5H11.25C11.6642 16.5 12 16.8358 12 17.25C12 17.6642 11.6642 18 11.25 18H3C1.34315 18 0 16.6569 0 15V3C0 1.34315 1.34315 0 3 0H19.5C21.1569 0 22.5 1.34315 22.5 3V9C22.5 9.41421 22.1642 9.75 21.75 9.75C21.3358 9.75 21 9.41421 21 9V3C21 2.17157 20.3284 1.5 19.5 1.5H3Z" fill="currentColor"/>
|
||||
<path d="M4.71967 4.71967C5.01256 4.42678 5.48744 4.42678 5.78033 4.71967L7.76517 6.7045C8.2045 7.14384 8.2045 7.85616 7.76517 8.2955L5.78033 10.2803C5.48744 10.5732 5.01256 10.5732 4.71967 10.2803C4.42678 9.98744 4.42678 9.51256 4.71967 9.21967L6.43934 7.5L4.71967 5.78033C4.42678 5.48744 4.42678 5.01256 4.71967 4.71967Z" fill="currentColor"/>
|
||||
<path d="M8.25 10.5C8.25 10.0858 8.58579 9.75 9 9.75H12C12.4142 9.75 12.75 10.0858 12.75 10.5C12.75 10.9142 12.4142 11.25 12 11.25H9C8.58579 11.25 8.25 10.9142 8.25 10.5Z" fill="currentColor"/>
|
||||
<path d="M24 15.75C24 18.6495 21.6495 21 18.75 21C15.8505 21 13.5 18.6495 13.5 15.75C13.5 12.8505 15.8505 10.5 18.75 10.5C21.6495 10.5 24 12.8505 24 15.75ZM18.75 12.75C18.3358 12.75 18 13.0858 18 13.5V15H16.5C16.0858 15 15.75 15.3358 15.75 15.75C15.75 16.1642 16.0858 16.5 16.5 16.5H18V18C18 18.4142 18.3358 18.75 18.75 18.75C19.1642 18.75 19.5 18.4142 19.5 18V16.5H21C21.4142 16.5 21.75 16.1642 21.75 15.75C21.75 15.3358 21.4142 15 21 15H19.5V13.5C19.5 13.0858 19.1642 12.75 18.75 12.75Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -27,5 +27,6 @@ export default [
|
||||
route("terminal", "routes/terminal-tab.tsx"),
|
||||
route("vscode", "routes/vscode-tab.tsx"),
|
||||
]),
|
||||
route("microagent-management", "routes/microagent-management.tsx"),
|
||||
]),
|
||||
] satisfies RouteConfig;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { redirect } from "react-router";
|
||||
import { MicroagentManagementSidebar } from "#/components/features/microagent-management/microagent-management-sidebar";
|
||||
import { Route } from "./+types/settings";
|
||||
import { queryClient } from "#/query-client-config";
|
||||
import { GetConfigResponse } from "#/api/open-hands.types";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { MicroagentManagementMain } from "#/components/features/microagent-management/microagent-management-main";
|
||||
|
||||
export const clientLoader = async ({ request }: Route.ClientLoaderArgs) => {
|
||||
const url = new URL(request.url);
|
||||
const { pathname } = url;
|
||||
|
||||
let config = queryClient.getQueryData<GetConfigResponse>(["config"]);
|
||||
if (!config) {
|
||||
config = await OpenHands.getConfig();
|
||||
queryClient.setQueryData<GetConfigResponse>(["config"], config);
|
||||
}
|
||||
|
||||
const shouldHideMicroagentManagement =
|
||||
config?.FEATURE_FLAGS.HIDE_MICROAGENT_MANAGEMENT;
|
||||
|
||||
if (shouldHideMicroagentManagement && pathname === "/microagent-management") {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
function MicroagentManagement() {
|
||||
return (
|
||||
<div className="w-full h-full flex rounded-lg border border-[#525252] bg-[#24272E]">
|
||||
<MicroagentManagementSidebar />
|
||||
<MicroagentManagementMain />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MicroagentManagement;
|
||||
@@ -214,6 +214,7 @@ export default function MainApp() {
|
||||
<AuthModal
|
||||
githubAuthUrl={effectiveGitHubAuthUrl}
|
||||
appMode={config.data?.APP_MODE}
|
||||
providersConfigured={config.data?.PROVIDERS_CONFIGURED}
|
||||
/>
|
||||
)}
|
||||
{renderReAuthModal && <ReauthModal />}
|
||||
|
||||
@@ -66,6 +66,8 @@ function SettingsScreen() {
|
||||
// this is used to determine which settings are available in the UI
|
||||
const navItems = isSaas ? SAAS_NAV_ITEMS : OSS_NAV_ITEMS;
|
||||
|
||||
// THIS IS A TEST
|
||||
|
||||
return (
|
||||
<main
|
||||
data-testid="settings-screen"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
export const microagentManagementSlice = createSlice({
|
||||
name: "microagentManagement",
|
||||
initialState: {
|
||||
selectedMicroagent: null,
|
||||
},
|
||||
reducers: {
|
||||
setSelectedMicroagent: (state, action) => {
|
||||
state.selectedMicroagent = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setSelectedMicroagent } = microagentManagementSlice.actions;
|
||||
|
||||
export default microagentManagementSlice.reducer;
|
||||
@@ -9,6 +9,7 @@ import { jupyterReducer } from "./state/jupyter-slice";
|
||||
import securityAnalyzerReducer from "./state/security-analyzer-slice";
|
||||
import statusReducer from "./state/status-slice";
|
||||
import metricsReducer from "./state/metrics-slice";
|
||||
import microagentManagementReducer from "./state/microagent-management-slice";
|
||||
|
||||
export const rootReducer = combineReducers({
|
||||
fileState: fileStateReducer,
|
||||
@@ -21,6 +22,7 @@ export const rootReducer = combineReducers({
|
||||
securityAnalyzer: securityAnalyzerReducer,
|
||||
status: statusReducer,
|
||||
metrics: metricsReducer,
|
||||
microagentManagement: microagentManagementReducer,
|
||||
});
|
||||
|
||||
const store = configureStore({
|
||||
|
||||
@@ -8,6 +8,7 @@ export const VERIFIED_PROVIDERS = [
|
||||
export const VERIFIED_MODELS = [
|
||||
"o3-mini-2025-01-31",
|
||||
"o3-2025-04-16",
|
||||
"o3",
|
||||
"o4-mini-2025-04-16",
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"claude-3-7-sonnet-20250219",
|
||||
@@ -19,6 +20,7 @@ export const VERIFIED_MODELS = [
|
||||
"devstral-small-2505",
|
||||
"devstral-small-2507",
|
||||
"devstral-medium-2507",
|
||||
"kimi-k2-0711-preview",
|
||||
];
|
||||
|
||||
// LiteLLM does not return OpenAI models with the provider, so we list them here to set them ourselves for consistency
|
||||
@@ -49,9 +51,9 @@ export const VERIFIED_ANTHROPIC_MODELS = [
|
||||
// LiteLLM does not return the compatible Mistral models with the provider, so we list them here to set them ourselves
|
||||
// (e.g., they return `devstral-small-2505` instead of `mistral/devstral-small-2505`)
|
||||
export const VERIFIED_MISTRAL_MODELS = [
|
||||
"devstral-small-2505",
|
||||
"devstral-small-2507",
|
||||
"devstral-medium-2507",
|
||||
"devstral-small-2505",
|
||||
];
|
||||
|
||||
// LiteLLM does not return the compatible OpenHands models with the provider, so we list them here to set them ourselves
|
||||
@@ -60,10 +62,12 @@ export const VERIFIED_OPENHANDS_MODELS = [
|
||||
"claude-sonnet-4-20250514",
|
||||
"claude-opus-4-20250514",
|
||||
"gemini-2.5-pro",
|
||||
"o3",
|
||||
"o4-mini",
|
||||
"devstral-small-2505",
|
||||
"devstral-small-2507",
|
||||
"devstral-medium-2507",
|
||||
"devstral-small-2505",
|
||||
"kimi-k2-0711-preview",
|
||||
];
|
||||
|
||||
// Default model for OpenHands provider
|
||||
|
||||
+123
-41
@@ -8,6 +8,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"focus-trap-react": "^11.0.4",
|
||||
"react-bootstrap-icons": "^1.11.6",
|
||||
"react-select": "^5.10.2",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss": "^4.1.10",
|
||||
@@ -79,12 +81,34 @@
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.28.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/types": "^7.28.0", "debug": "^4.3.1" } }, "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="],
|
||||
"@babel/types": ["@babel/types@7.28.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ=="],
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
|
||||
|
||||
"@chromatic-com/storybook": ["@chromatic-com/storybook@4.0.1", "", { "dependencies": { "@neoconfetti/react": "^1.0.0", "chromatic": "^12.0.0", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^0.0.0-0 || ^9.0.0 || ^9.1.0-0" } }, "sha512-GQXe5lyZl3yLewLJQyFXEpOp2h+mfN2bPrzYaOFNCJjO4Js9deKbRHTOSaiP2FRwZqDLdQwy2+SEGeXPZ94yYw=="],
|
||||
|
||||
"@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="],
|
||||
|
||||
"@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="],
|
||||
|
||||
"@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
|
||||
|
||||
"@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
|
||||
|
||||
"@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="],
|
||||
|
||||
"@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="],
|
||||
|
||||
"@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="],
|
||||
|
||||
"@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="],
|
||||
|
||||
"@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="],
|
||||
|
||||
"@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="],
|
||||
|
||||
"@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.6", "", { "os": "aix", "cpu": "ppc64" }, "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.6", "", { "os": "android", "cpu": "arm" }, "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg=="],
|
||||
@@ -183,45 +207,45 @@
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.2", "", { "os": "android", "cpu": "arm" }, "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q=="],
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.45.1", "", { "os": "android", "cpu": "arm" }, "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.2", "", { "os": "android", "cpu": "arm64" }, "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA=="],
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.45.1", "", { "os": "android", "cpu": "arm64" }, "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA=="],
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.45.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw=="],
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.45.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg=="],
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.45.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA=="],
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.45.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ=="],
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.45.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA=="],
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.45.1", "", { "os": "linux", "cpu": "arm" }, "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A=="],
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.45.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A=="],
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.45.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog=="],
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g=="],
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg=="],
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw=="],
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.45.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg=="],
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg=="],
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.45.1", "", { "os": "linux", "cpu": "none" }, "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw=="],
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.45.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ=="],
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.45.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg=="],
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.45.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw=="],
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.45.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q=="],
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.45.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.2", "", { "os": "win32", "cpu": "x64" }, "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA=="],
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.45.1", "", { "os": "win32", "cpu": "x64" }, "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA=="],
|
||||
|
||||
"@rushstack/node-core-library": ["@rushstack/node-core-library@5.13.1", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-5yXhzPFGEkVc9Fu92wsNJ9jlvdwz4RNb2bMso+/+TH0nMm1jDDDsOIf4l8GAkPxGuwPw5DH24RliWVfSPhlW/Q=="],
|
||||
|
||||
@@ -231,27 +255,27 @@
|
||||
|
||||
"@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.1", "", { "dependencies": { "@rushstack/terminal": "0.15.3", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-bsbUucn41UXrQK7wgM8CNM/jagBytEyJqXw/umtI8d68vFm1Jwxh1OtLrlW7uGZgjCWiiPH6ooUNa1aVsuVr3Q=="],
|
||||
|
||||
"@storybook/addon-a11y": ["@storybook/addon-a11y@9.0.16", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^9.0.16" } }, "sha512-pi9ipxhs9bA2yCHDGp2+yWy6E2LywDFTqWcFh3aw/LRxnlRTf52QiVJkWpJbNFEXgk4QrKVrAruf9LLiXpTcOA=="],
|
||||
"@storybook/addon-a11y": ["@storybook/addon-a11y@9.0.17", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-9cXNK3q/atx3hwJAt9HkJbd9vUxCXfKKiNNuSACbf8h9/j6u3jktulKOf6Xjc3B8lwn6ZpdK/x1HHZN2kTqsvg=="],
|
||||
|
||||
"@storybook/addon-docs": ["@storybook/addon-docs@9.0.16", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "9.0.16", "@storybook/icons": "^1.2.12", "@storybook/react-dom-shim": "9.0.16", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^9.0.16" } }, "sha512-/ZXaxMC/JqL0cnVuyPHXdJhNvgCrKvxcnM3ACdgBLsEIGcIqegPF+Ahkb2f9sjU36sR7ihT81cL/7cUvQwzd4Q=="],
|
||||
"@storybook/addon-docs": ["@storybook/addon-docs@9.0.17", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "9.0.17", "@storybook/icons": "^1.2.12", "@storybook/react-dom-shim": "9.0.17", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-LOX/kKgQGnyulrqZHsvf77+ZoH/nSUaplGr5hvZglW/U6ak6fO9seJyXAzVKEnC6p+F8n02kFBZbi3s+znQhSg=="],
|
||||
|
||||
"@storybook/addon-onboarding": ["@storybook/addon-onboarding@9.0.16", "", { "peerDependencies": { "storybook": "^9.0.16" } }, "sha512-69BPJ9fGNGpDAcGvNJ58V5uQOmpHkQMLcgp/ON3NepoCHiSReUzojB6wV8Ag13PUZmvWXVnE14SWKBZp93xTFQ=="],
|
||||
"@storybook/addon-onboarding": ["@storybook/addon-onboarding@9.0.17", "", { "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-WoZZ8d58gP6uBu6OJ2K1GjBSM4+Kcr0I9lo0z3convzYqxrhfUm9pNEwVm57KCbVVyBbIKmevddCsSFoPC5u6Q=="],
|
||||
|
||||
"@storybook/addon-vitest": ["@storybook/addon-vitest@9.0.16", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^1.4.0", "prompts": "^2.4.0", "ts-dedent": "^2.2.0" }, "peerDependencies": { "@vitest/browser": "^3.0.0", "@vitest/runner": "^3.0.0", "storybook": "^9.0.16", "vitest": "^3.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/runner", "vitest"] }, "sha512-amIJLeAcREF/imEmAhZmsPc3kvtEDVdk7O6uvh8/ql8UYNN5Tnc+ud6CsfIZO82ru+PupoYKrt6SC8EwpQ8YMQ=="],
|
||||
"@storybook/addon-vitest": ["@storybook/addon-vitest@9.0.17", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^1.4.0", "prompts": "^2.4.0", "ts-dedent": "^2.2.0" }, "peerDependencies": { "@vitest/browser": "^3.0.0", "@vitest/runner": "^3.0.0", "storybook": "^9.0.17", "vitest": "^3.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/runner", "vitest"] }, "sha512-eogqcGbACR1sTedBSE2SP/4QV1ruicHYEhYjBtoPIjvYgymN1g5KSuQNysLx4f0SvAzczrcNjX2WVVLX2DVyzA=="],
|
||||
|
||||
"@storybook/builder-vite": ["@storybook/builder-vite@9.0.16", "", { "dependencies": { "@storybook/csf-plugin": "9.0.16", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^9.0.16", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-zXockUexeRy3ABG7DFLEvJqJe4mGL0JkI7FMrpwiKaHCQNaD87vR0xkRVeh0a3B8GKUNxoYtpYKvdzc9DobQHQ=="],
|
||||
"@storybook/builder-vite": ["@storybook/builder-vite@9.0.17", "", { "dependencies": { "@storybook/csf-plugin": "9.0.17", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^9.0.17", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-lyuvgGhb0NaVk1tdB4xwzky6+YXQfxlxfNQqENYZ9uYQZdPfErMa4ZTXVQTV+CQHAa2NL+p/dG2JPAeu39e9UA=="],
|
||||
|
||||
"@storybook/csf-plugin": ["@storybook/csf-plugin@9.0.16", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^9.0.16" } }, "sha512-MSmfPwI0j1mMAc+R3DVkVBQf2KLzaVn2SLdEwweesx63Nh9j3zu9CqKEa0zOuDX1lR2M0DZU0lV6K4sc2EYI4A=="],
|
||||
"@storybook/csf-plugin": ["@storybook/csf-plugin@9.0.17", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-6Q4eo1ObrLlsnB6bIt6T8+45XAb4to2pQGNrI7QPkLQRLrZinrJcNbLY7AGkyIoCOEsEbq08n09/nClQUbu8HA=="],
|
||||
|
||||
"@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="],
|
||||
|
||||
"@storybook/icons": ["@storybook/icons@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-Td73IeJxOyalzvjQL+JXx72jlIYHgs+REaHiREOqfpo3A2AYYG71AUbcv+lg7mEDIweKVCxsMQ0UKo634c8XeA=="],
|
||||
|
||||
"@storybook/react": ["@storybook/react@9.0.16", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/react-dom-shim": "9.0.16" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.16", "typescript": ">= 4.9.x" }, "optionalPeers": ["typescript"] }, "sha512-1jk9fBe8vEoZrba9cK19ZDdZgYMXUNl3Egjj5RsTMYMc1L2mtIu9o56VyK/1V4Q52N9IyawHvmIIuxc5pCZHkQ=="],
|
||||
"@storybook/react": ["@storybook/react@9.0.17", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/react-dom-shim": "9.0.17" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.17", "typescript": ">= 4.9.x" }, "optionalPeers": ["typescript"] }, "sha512-wssao+uXg72OHtEJdQmmQJGdX90x/aU/6avoP3fgVgepWdZXVgciS9mnqHjKRF/vP+vPOlNQcJjojF/zTtq5qg=="],
|
||||
|
||||
"@storybook/react-dom-shim": ["@storybook/react-dom-shim@9.0.16", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.16" } }, "sha512-5aIK+31R41mRUvDB4vmBv8hwh3IVHIk/Zbs6kkWF2a+swOsB2+a06aLX21lma4/0T/AuFVXHWat0+inQ4nrXRg=="],
|
||||
"@storybook/react-dom-shim": ["@storybook/react-dom-shim@9.0.17", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.17" } }, "sha512-ak/x/m6MDDxdE6rCDymTltaiQF3oiKrPHSwfM+YPgQR6MVmzTTs4+qaPfeev7FZEHq23IkfDMTmSTTJtX7Vs9A=="],
|
||||
|
||||
"@storybook/react-vite": ["@storybook/react-vite@9.0.16", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.6.1", "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "9.0.16", "@storybook/react": "9.0.16", "find-up": "^7.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", "resolve": "^1.22.8", "tsconfig-paths": "^4.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.16", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-a+UsoymyvPH4bJJVI+asj02N8U2wlkGyzhUqF6LUM9gXzixRMxoRHkchCKLdqLhE+//STrwC0YFF3GG6Y5oMEg=="],
|
||||
"@storybook/react-vite": ["@storybook/react-vite@9.0.17", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.6.1", "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "9.0.17", "@storybook/react": "9.0.17", "find-up": "^7.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", "resolve": "^1.22.8", "tsconfig-paths": "^4.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.17", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-wx1yKScni4ifOC/ccqpnnpceQbyF2xto+jHGsyua+M4UUCQdS2NYPDR8JFWp1YvBhVt2cQiD6SAltVGM9QLGnQ=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
||||
|
||||
@@ -313,7 +337,9 @@
|
||||
|
||||
"@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="],
|
||||
"@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="],
|
||||
|
||||
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
||||
|
||||
"@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
|
||||
|
||||
@@ -321,6 +347,8 @@
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="],
|
||||
|
||||
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
|
||||
|
||||
"@types/resolve": ["@types/resolve@1.20.6", "", {}, "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ=="],
|
||||
|
||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.6.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ=="],
|
||||
@@ -343,11 +371,11 @@
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||
|
||||
"@volar/language-core": ["@volar/language-core@2.4.18", "", { "dependencies": { "@volar/source-map": "2.4.18" } }, "sha512-G3yYV85ekH4TV0EDS6DsS/dUJWrz675H9UgsxFz5pQbmas51a0Q2fF6Lb2q4RKgytuLZ4E0MBdT5PlVsJXNalw=="],
|
||||
"@volar/language-core": ["@volar/language-core@2.4.19", "", { "dependencies": { "@volar/source-map": "2.4.19" } }, "sha512-i0aLpNA8DYZ2uG05t5K47nUWe+zvvrN9tfz16zS5pCJV9td8F0u+rVAOVSQ1ypufDLUD+ej9BH2/lmug4+lawQ=="],
|
||||
|
||||
"@volar/source-map": ["@volar/source-map@2.4.18", "", {}, "sha512-zaj2V/zo/CHQ/xA75h60jBPgrz+Ou9s6aPl7dX0rT46/uill9aB/ZaDk92ROpJsa/9e2xftCeNAU9ZwVyB/egQ=="],
|
||||
"@volar/source-map": ["@volar/source-map@2.4.19", "", {}, "sha512-ttWmO/Ld7r3ebIPPAYvAuSLrlJ96ZALPka44mD4sWA8bw2n9u7TGnMcaTUkiF0GLG8bq/K09beWmEAB1mqMy/A=="],
|
||||
|
||||
"@volar/typescript": ["@volar/typescript@2.4.18", "", { "dependencies": { "@volar/language-core": "2.4.18", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-xcbsMG8m/yhvO1VIKnTtc+llZxw3YtWkZiV7/F1qNpTORdPExkZRcBxJ5d19MXLpkeiQ+DG5JURHh1SV0bcWRA=="],
|
||||
"@volar/typescript": ["@volar/typescript@2.4.19", "", { "dependencies": { "@volar/language-core": "2.4.19", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Xgo4QLuqusu2fqw4LCeoOY57d5UCn+fNUWZTg4PFubw07jBFFCSJIuJ7BDrRM3EZHDjCqq1RmUO9wkYihnM+8Q=="],
|
||||
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.17", "", { "dependencies": { "@babel/parser": "^7.27.5", "@vue/shared": "3.5.17", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA=="],
|
||||
|
||||
@@ -385,6 +413,8 @@
|
||||
|
||||
"axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="],
|
||||
|
||||
"babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"better-opn": ["better-opn@3.0.2", "", { "dependencies": { "open": "^8.0.4" } }, "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ=="],
|
||||
@@ -397,6 +427,8 @@
|
||||
|
||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="],
|
||||
|
||||
"chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="],
|
||||
@@ -423,6 +455,8 @@
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="],
|
||||
@@ -445,9 +479,11 @@
|
||||
|
||||
"dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
|
||||
|
||||
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.181", "", {}, "sha512-+ISMj8OIQ+0qEeDj14Rt8WwcTOiqHyAB+5bnK1K7xNNLjBJ4hRCQfUkw8RWtcLbfBzDwc15ZnKH0c7SNOfwiyA=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.185", "", {}, "sha512-dYOZfUk57hSMPePoIQ1fZWl1Fkj+OshhEVuPacNKWzC1efe56OsHY3l/jCfiAgIICOU3VgOIdoq7ahg7r7n6MQ=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
@@ -455,6 +491,8 @@
|
||||
|
||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
||||
|
||||
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.6", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.6", "@esbuild/android-arm": "0.25.6", "@esbuild/android-arm64": "0.25.6", "@esbuild/android-x64": "0.25.6", "@esbuild/darwin-arm64": "0.25.6", "@esbuild/darwin-x64": "0.25.6", "@esbuild/freebsd-arm64": "0.25.6", "@esbuild/freebsd-x64": "0.25.6", "@esbuild/linux-arm": "0.25.6", "@esbuild/linux-arm64": "0.25.6", "@esbuild/linux-ia32": "0.25.6", "@esbuild/linux-loong64": "0.25.6", "@esbuild/linux-mips64el": "0.25.6", "@esbuild/linux-ppc64": "0.25.6", "@esbuild/linux-riscv64": "0.25.6", "@esbuild/linux-s390x": "0.25.6", "@esbuild/linux-x64": "0.25.6", "@esbuild/netbsd-arm64": "0.25.6", "@esbuild/netbsd-x64": "0.25.6", "@esbuild/openbsd-arm64": "0.25.6", "@esbuild/openbsd-x64": "0.25.6", "@esbuild/openharmony-arm64": "0.25.6", "@esbuild/sunos-x64": "0.25.6", "@esbuild/win32-arm64": "0.25.6", "@esbuild/win32-ia32": "0.25.6", "@esbuild/win32-x64": "0.25.6" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg=="],
|
||||
@@ -463,6 +501,8 @@
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
@@ -479,6 +519,8 @@
|
||||
|
||||
"filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="],
|
||||
|
||||
"find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
|
||||
|
||||
"find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
|
||||
|
||||
"focus-trap": ["focus-trap@7.6.5", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg=="],
|
||||
@@ -505,12 +547,18 @@
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
|
||||
|
||||
"html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"import-lazy": ["import-lazy@4.0.0", "", {}, "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw=="],
|
||||
|
||||
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||
|
||||
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
||||
@@ -539,6 +587,8 @@
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
@@ -571,6 +621,8 @@
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
|
||||
|
||||
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||
|
||||
"local-pkg": ["local-pkg@1.1.1", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.0.1", "quansync": "^0.2.8" } }, "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg=="],
|
||||
|
||||
"locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
|
||||
@@ -591,6 +643,8 @@
|
||||
|
||||
"make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
|
||||
|
||||
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
||||
|
||||
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
@@ -625,6 +679,10 @@
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
|
||||
|
||||
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
|
||||
|
||||
"path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
||||
@@ -635,19 +693,21 @@
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"pkg-types": ["pkg-types@2.2.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ=="],
|
||||
|
||||
"playwright": ["playwright@1.54.0", "", { "dependencies": { "playwright-core": "1.54.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-y9yzHmXRwEUOpghM7XGcA38GjWuTOUMaTIcm/5rHcYVjh5MSp9qQMRRMc/+p1cx+csoPnX4wkxAF61v5VKirxg=="],
|
||||
"playwright": ["playwright@1.54.1", "", { "dependencies": { "playwright-core": "1.54.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.54.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-uiWpWaJh3R3etpJ0QrpligEMl62Dk1iSAB6NUXylvmQz+e3eipXHDHvOvydDAssb5Oqo0E818qdn0L9GcJSTyA=="],
|
||||
"playwright-core": ["playwright-core@1.54.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
@@ -677,6 +737,10 @@
|
||||
|
||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||
|
||||
"react-select": ["react-select@5.10.2", "", { "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", "@emotion/react": "^11.8.1", "@floating-ui/dom": "^1.0.1", "@types/react-transition-group": "^4.4.0", "memoize-one": "^6.0.0", "prop-types": "^15.6.0", "react-transition-group": "^4.3.0", "use-isomorphic-layout-effect": "^1.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ=="],
|
||||
|
||||
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
|
||||
|
||||
"recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="],
|
||||
|
||||
"redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="],
|
||||
@@ -685,7 +749,9 @@
|
||||
|
||||
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
||||
|
||||
"rollup": ["rollup@4.44.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.2", "@rollup/rollup-android-arm64": "4.44.2", "@rollup/rollup-darwin-arm64": "4.44.2", "@rollup/rollup-darwin-x64": "4.44.2", "@rollup/rollup-freebsd-arm64": "4.44.2", "@rollup/rollup-freebsd-x64": "4.44.2", "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", "@rollup/rollup-linux-arm-musleabihf": "4.44.2", "@rollup/rollup-linux-arm64-gnu": "4.44.2", "@rollup/rollup-linux-arm64-musl": "4.44.2", "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-musl": "4.44.2", "@rollup/rollup-linux-s390x-gnu": "4.44.2", "@rollup/rollup-linux-x64-gnu": "4.44.2", "@rollup/rollup-linux-x64-musl": "4.44.2", "@rollup/rollup-win32-arm64-msvc": "4.44.2", "@rollup/rollup-win32-ia32-msvc": "4.44.2", "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg=="],
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"rollup": ["rollup@4.45.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.45.1", "@rollup/rollup-android-arm64": "4.45.1", "@rollup/rollup-darwin-arm64": "4.45.1", "@rollup/rollup-darwin-x64": "4.45.1", "@rollup/rollup-freebsd-arm64": "4.45.1", "@rollup/rollup-freebsd-x64": "4.45.1", "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", "@rollup/rollup-linux-arm-musleabihf": "4.45.1", "@rollup/rollup-linux-arm64-gnu": "4.45.1", "@rollup/rollup-linux-arm64-musl": "4.45.1", "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-gnu": "4.45.1", "@rollup/rollup-linux-riscv64-musl": "4.45.1", "@rollup/rollup-linux-s390x-gnu": "4.45.1", "@rollup/rollup-linux-x64-gnu": "4.45.1", "@rollup/rollup-linux-x64-musl": "4.45.1", "@rollup/rollup-win32-arm64-msvc": "4.45.1", "@rollup/rollup-win32-ia32-msvc": "4.45.1", "@rollup/rollup-win32-x64-msvc": "4.45.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
@@ -703,6 +769,8 @@
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
"sonner": ["sonner@2.0.6", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
@@ -713,7 +781,7 @@
|
||||
|
||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||
|
||||
"storybook": ["storybook@9.0.16", "", { "dependencies": { "@storybook/global": "^5.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "better-opn": "^3.0.2", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", "esbuild-register": "^3.5.0", "recast": "^0.23.5", "semver": "^7.6.2", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": "./bin/index.cjs" }, "sha512-DzjzeggdzlXKKBK1L9iqNKqqNpyfeaL1hxxeAOmqgeMezwy5d5mCJmjNcZEmx+prsRmvj1OWm4ZZAg6iP/wABg=="],
|
||||
"storybook": ["storybook@9.0.17", "", { "dependencies": { "@storybook/global": "^5.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "better-opn": "^3.0.2", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", "esbuild-register": "^3.5.0", "recast": "^0.23.5", "semver": "^7.6.2", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": "./bin/index.cjs" }, "sha512-O+9jgJ+Trlq9VGD1uY4OBLKQWHHDKM/A/pA8vMW6PVehhGHNvpzcIC1bngr6mL5gGHZP2nBv+9XG8pTMcggMmg=="],
|
||||
|
||||
"string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="],
|
||||
|
||||
@@ -733,6 +801,8 @@
|
||||
|
||||
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
|
||||
|
||||
"stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
@@ -789,6 +859,8 @@
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA=="],
|
||||
|
||||
"vite": ["vite@7.0.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA=="],
|
||||
|
||||
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||
@@ -813,6 +885,8 @@
|
||||
|
||||
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||
|
||||
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="],
|
||||
|
||||
"@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
@@ -821,6 +895,10 @@
|
||||
|
||||
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@emotion/babel-plugin/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
||||
|
||||
"@emotion/babel-plugin/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
|
||||
|
||||
"@microsoft/api-extractor/minimatch": ["minimatch@3.0.8", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q=="],
|
||||
|
||||
"@microsoft/api-extractor/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="],
|
||||
@@ -839,12 +917,14 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
|
||||
|
||||
"@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],
|
||||
@@ -893,6 +973,8 @@
|
||||
|
||||
"@rushstack/node-core-library/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
|
||||
|
||||
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Select, type SelectProps } from "./Select";
|
||||
import type { IOption } from "../../shared/types";
|
||||
import { useState } from "react";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Select",
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const options = [
|
||||
{ label: "Red", value: "red" },
|
||||
{ label: "Green", value: "green" },
|
||||
{ label: "Blue", value: "blue" },
|
||||
{ label: "Yellow", value: "yellow" },
|
||||
{ label: "Purple", value: "purple" },
|
||||
{ label: "Orange", value: "orange" },
|
||||
{ label: "Black", value: "black" },
|
||||
{ label: "White", value: "white" },
|
||||
{ label: "Gray", value: "gray" },
|
||||
{ label: "Pink", value: "pink" },
|
||||
];
|
||||
|
||||
const SelectComponent = (props: Partial<SelectProps<unknown>>) => {
|
||||
const [value, setValue] = useState<IOption<string> | null>(
|
||||
(props.value as IOption<string>) ?? null
|
||||
);
|
||||
return (
|
||||
<div className="min-w-104">
|
||||
<Select
|
||||
{...props}
|
||||
label="Select label"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
placeholder="Select an option"
|
||||
options={options}
|
||||
className="h-64 max-w-128 p-4"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Main: Story = {
|
||||
args: {},
|
||||
render: ({}) => <SelectComponent />,
|
||||
};
|
||||
|
||||
export const Error: Story = {
|
||||
args: {},
|
||||
render: ({}) => <SelectComponent error="This field is required" />,
|
||||
};
|
||||
export const Hint: Story = {
|
||||
args: {},
|
||||
render: ({}) => <SelectComponent hint="This is hint" />,
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {},
|
||||
render: ({}) => <SelectComponent disabled value={options[1]} />,
|
||||
};
|
||||
|
||||
export const ReadOnly: Story = {
|
||||
args: {},
|
||||
render: ({}) => <SelectComponent readOnly value={options[0]} />,
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
import { useId, useMemo, useState } from "react";
|
||||
import type { HTMLProps, IOption } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import ReactSelect, { createFilter } from "react-select";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { DropdownIndicator } from "./components/DropdownIndicator";
|
||||
import { Placeholder } from "./components/Placeholder";
|
||||
import { SingleValue } from "./components/SingleValue";
|
||||
import { Option } from "./components/Option";
|
||||
|
||||
export type SelectProps<T> = Omit<HTMLProps<"input">, "value" | "onChange"> & {
|
||||
error?: string;
|
||||
hint?: string;
|
||||
label: string;
|
||||
value?: IOption<T> | null;
|
||||
options: IOption<T>[];
|
||||
noOptionsText?: string;
|
||||
onChange(value: IOption<T> | null): void;
|
||||
};
|
||||
|
||||
export const Select = <T extends string>(props: SelectProps<T>) => {
|
||||
const {
|
||||
error,
|
||||
hint,
|
||||
options,
|
||||
value,
|
||||
id: propId,
|
||||
autoFocus,
|
||||
placeholder,
|
||||
disabled,
|
||||
label,
|
||||
onChange,
|
||||
readOnly,
|
||||
noOptionsText,
|
||||
} = props;
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const generatedId = useId();
|
||||
const id = propId ?? generatedId;
|
||||
|
||||
const filterOption = useMemo(
|
||||
() =>
|
||||
createFilter({
|
||||
ignoreCase: true,
|
||||
ignoreAccents: true,
|
||||
trim: true,
|
||||
matchFrom: "any",
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<label
|
||||
htmlFor={id}
|
||||
className={cn(
|
||||
"flex flex-col gap-y-2",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
||||
)}
|
||||
>
|
||||
<Typography.Text fontSize="s" className="text-light-neutral-200">
|
||||
{label}
|
||||
</Typography.Text>
|
||||
<ReactSelect
|
||||
inputValue={inputValue}
|
||||
value={value}
|
||||
tabSelectsValue={false}
|
||||
unstyled
|
||||
isSearchable
|
||||
backspaceRemovesValue
|
||||
autoFocus={autoFocus}
|
||||
noOptionsMessage={() => (
|
||||
<Typography.Text className="py-3 block">
|
||||
{noOptionsText ?? "No options"}
|
||||
</Typography.Text>
|
||||
)}
|
||||
isDisabled={disabled ?? readOnly}
|
||||
filterOption={filterOption}
|
||||
customProps={{ error, readOnly, hint }}
|
||||
onInputChange={(value, action) => {
|
||||
if (action.action === "input-change") {
|
||||
setInputValue(value);
|
||||
const val = props.value?.label;
|
||||
if (val != null && val !== value) {
|
||||
props.onChange(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onChange={(option) => {
|
||||
onChange(option as IOption<T>);
|
||||
setInputValue("");
|
||||
}}
|
||||
placeholder={placeholder ?? ""}
|
||||
options={options}
|
||||
menuPortalTarget={document.body}
|
||||
classNames={{
|
||||
indicatorSeparator: (state) =>
|
||||
cn(
|
||||
"bg-light-neutral-500",
|
||||
state.selectProps.customProps.error && "bg-red-400",
|
||||
state.selectProps.customProps.readOnly && "bg-light-neutral-985"
|
||||
),
|
||||
input: () => cn("text-white"),
|
||||
valueContainer: () => cn("py-4.25 px-4"),
|
||||
control: (state) =>
|
||||
cn(
|
||||
"border-light-neutral-500 border-1 bg-light-neutral-950",
|
||||
state.menuIsOpen ? "rounded-t-2xl" : "rounded-2xl",
|
||||
"hover:bg-light-neutral-900",
|
||||
"focus-within:bg-light-neutral-900",
|
||||
state.selectProps.customProps.error && "border-red-400",
|
||||
state.selectProps.customProps.readOnly &&
|
||||
"bg-light-neutral-985 border-none hover:bg-light-neutral-985 cursor-auto"
|
||||
),
|
||||
menu: () =>
|
||||
cn(
|
||||
"border-light-neutral-500 border-1 border-t-0 rounded-b-2xl bg-light-neutral-950"
|
||||
),
|
||||
}}
|
||||
components={{
|
||||
DropdownIndicator,
|
||||
Placeholder,
|
||||
// @ts-ignore
|
||||
Option: Option,
|
||||
SingleValue,
|
||||
}}
|
||||
/>
|
||||
<Typography.Text
|
||||
fontSize="xs"
|
||||
className={cn("text-light-neutral-600 ml-4", error && "text-red-400")}
|
||||
>
|
||||
{error ?? hint}
|
||||
</Typography.Text>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { type DropdownIndicatorProps } from "react-select";
|
||||
import { Icon } from "../../icon/Icon";
|
||||
|
||||
export const DropdownIndicator = ({
|
||||
selectProps,
|
||||
className,
|
||||
}: DropdownIndicatorProps) => (
|
||||
<div className={cn("flex flex-row items-center", "py-3 px-3", className)}>
|
||||
<Icon
|
||||
className={cn(
|
||||
"h-5 w-5 text-light-neutral-300",
|
||||
selectProps.customProps.error && "text-red-400"
|
||||
)}
|
||||
icon="ChevronDown"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,56 @@
|
||||
import { type OptionProps } from "react-select";
|
||||
import type { IOption } from "../../../shared/types";
|
||||
import { Typography } from "../../typography/Typography";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
|
||||
export const Option = ({
|
||||
className,
|
||||
children,
|
||||
isSelected,
|
||||
innerProps,
|
||||
data,
|
||||
selectProps: { options, inputValue, filterOption },
|
||||
}: OptionProps & { value: unknown }) => {
|
||||
// In the current design, last option (which may be really last option or last visible option if the search term is present)
|
||||
// has to be rounded. Therefore, that calculation is applied here by using filterOption function from react-select.
|
||||
// Unfortunately, this function is meant to be applied only internally which means API of this function is not really
|
||||
// user-friendly (look at the __isNew__ property).
|
||||
// Ideally, either design will be changed or another implementation of this functionality will be explored.
|
||||
|
||||
const visibleOptions = filterOption
|
||||
? (options ?? []).filter((o) =>
|
||||
filterOption(
|
||||
{
|
||||
// @ts-ignore
|
||||
...o,
|
||||
data: {
|
||||
__isNew__: false,
|
||||
},
|
||||
},
|
||||
inputValue
|
||||
)
|
||||
)
|
||||
: options;
|
||||
const lastOption = visibleOptions[
|
||||
visibleOptions!.length - 1
|
||||
] as IOption<unknown>;
|
||||
|
||||
const option = data as IOption<unknown>;
|
||||
const isLast = option.value === lastOption?.value;
|
||||
return (
|
||||
<Typography.Text
|
||||
fontSize="m"
|
||||
fontWeight={isSelected ? 600 : 400}
|
||||
className={cn(
|
||||
className,
|
||||
"block px-6 py-3 cursor-pointer",
|
||||
"text-light-neutral-200",
|
||||
"hover:bg-blue/50 hover:font-semibold",
|
||||
isLast && "rounded-b-2xl"
|
||||
)}
|
||||
{...innerProps}
|
||||
>
|
||||
{children}
|
||||
</Typography.Text>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { type PlaceholderProps } from "react-select";
|
||||
import { Typography } from "../../typography/Typography";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
|
||||
export const Placeholder = ({
|
||||
innerProps,
|
||||
children,
|
||||
selectProps: {
|
||||
customProps: { error },
|
||||
},
|
||||
}: PlaceholderProps) => {
|
||||
return (
|
||||
<Typography.Text
|
||||
{...innerProps}
|
||||
fontSize="m"
|
||||
fontWeight={400}
|
||||
style={{
|
||||
gridArea: "1 / 1 / 2 / 3",
|
||||
}}
|
||||
className={cn(
|
||||
innerProps.className,
|
||||
"text-light-neutral-300",
|
||||
error && "text-red-400"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Typography.Text>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Typography } from "../../typography/Typography";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import type { SingleValueProps } from "react-select";
|
||||
|
||||
export const SingleValue = ({
|
||||
innerProps,
|
||||
selectProps: {
|
||||
customProps: { error },
|
||||
},
|
||||
children,
|
||||
}: SingleValueProps) => {
|
||||
return (
|
||||
<Typography.Text
|
||||
{...innerProps}
|
||||
fontSize="m"
|
||||
fontWeight={400}
|
||||
style={{
|
||||
gridArea: "1 / 1 / 2 / 3",
|
||||
}}
|
||||
className={cn("text-white", error && "text-red-400")}
|
||||
>
|
||||
{children}
|
||||
</Typography.Text>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import type {} from "react-select/base";
|
||||
// This import is necessary for module augmentation.
|
||||
// It allows us to extend the 'Props' interface in the 'react-select/base' module
|
||||
// and add our custom property 'myCustomProp' to it.
|
||||
|
||||
declare module "react-select/base" {
|
||||
export interface Props<
|
||||
Option,
|
||||
IsMulti extends boolean,
|
||||
Group extends GroupBase<Option>
|
||||
> {
|
||||
customProps: {
|
||||
error?: string;
|
||||
hint?: string;
|
||||
readOnly: boolean | undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Tabs } from "./Tabs";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Tabs",
|
||||
component: Tabs,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta<typeof Tabs>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Main: Story = {
|
||||
args: {
|
||||
children: null,
|
||||
},
|
||||
render: ({}) => (
|
||||
<Tabs>
|
||||
<Tabs.Item text="Overview" icon="HouseFill">
|
||||
Summary of data
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Analytics" icon="BarChartFill">
|
||||
Traffic and metrics
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Settings" icon="GearFill">
|
||||
Customize profile
|
||||
</Tabs.Item>
|
||||
</Tabs>
|
||||
),
|
||||
};
|
||||
export const Scrollable: Story = {
|
||||
args: {
|
||||
children: null,
|
||||
},
|
||||
render: ({}) => (
|
||||
<div className="max-w-md">
|
||||
<Tabs>
|
||||
<Tabs.Item text="Overview" icon="HouseFill">
|
||||
Summary of data
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Analytics" icon="BarChartFill">
|
||||
Traffic and metrics
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Settings" icon="GearFill">
|
||||
Customize profile
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Billing" icon="CreditCardFill">
|
||||
Manage invoices
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Integrations" icon="PlugFill">
|
||||
Third-party services
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Notifications" icon="BellFill">
|
||||
Set alert preferences
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Reports" icon="FileEarmarkBarGraphFill">
|
||||
Generate PDF reports
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Feedback" icon="ChatDotsFill">
|
||||
Leave your thoughts
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Access Control" icon="ShieldLockFill">
|
||||
Manage roles and permissions
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Activity Log" icon="ClockHistory">
|
||||
Track recent actions
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Support" icon="LifePreserver">
|
||||
Get help and resources
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="API Keys" icon="KeyFill">
|
||||
Generate or revoke keys
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Localization" icon="Translate">
|
||||
Language and region
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Deployments" icon="CloudUploadFill">
|
||||
View deployment history
|
||||
</Tabs.Item>
|
||||
<Tabs.Item text="Audit Trail" icon="FileLockFill">
|
||||
Security and compliance logs
|
||||
</Tabs.Item>
|
||||
</Tabs>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
useRef,
|
||||
useState,
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import React from "react";
|
||||
import {
|
||||
TabItem,
|
||||
type TabItemProps,
|
||||
type TabItemPropsPublic,
|
||||
} from "./components/TabItem";
|
||||
import { useElementOverflow } from "./hooks/use-element-overflow";
|
||||
import { useElementScroll } from "./hooks/use-element-scroll";
|
||||
import { TabScroller } from "./components/TabScroller";
|
||||
|
||||
export type TabsProps = HTMLProps<"div">;
|
||||
|
||||
type TabsType = React.FC<PropsWithChildren<TabsProps>> & {
|
||||
Item: React.FC<PropsWithChildren<TabItemPropsPublic>>;
|
||||
};
|
||||
|
||||
const Tabs: TabsType = ({ children, ...props }) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const tabListRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isOverflowing = useElementOverflow({
|
||||
contentRef: tabListRef,
|
||||
containerRef,
|
||||
});
|
||||
const { canScrollLeft, canScrollRight, scrollLeft, scrollRight } =
|
||||
useElementScroll({
|
||||
containerRef,
|
||||
scrollRef: tabListRef,
|
||||
});
|
||||
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
const tabs =
|
||||
React.Children.map(children, (child, index: number) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = childrenArray.length - 1 === index;
|
||||
return React.cloneElement(
|
||||
child as ReactElement,
|
||||
{
|
||||
index,
|
||||
isFirst,
|
||||
isLast,
|
||||
isActive: index === activeIndex,
|
||||
onSelect: () => setActiveIndex(index),
|
||||
} as TabItemProps
|
||||
);
|
||||
}) ?? [];
|
||||
|
||||
return (
|
||||
<div className={cn("w-full")}>
|
||||
<div className={cn("flex flex-row items-stretch")} ref={containerRef}>
|
||||
{canScrollLeft && isOverflowing && (
|
||||
<TabScroller onScroll={scrollLeft} position="left" />
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn("flex", "overflow-x-auto scrollbar-none")}
|
||||
ref={tabListRef}
|
||||
role="tablist"
|
||||
aria-label="Tabs"
|
||||
>
|
||||
{tabs}
|
||||
</div>
|
||||
|
||||
{canScrollRight && isOverflowing && (
|
||||
<TabScroller onScroll={scrollRight} position="right" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"border-1 border-t-0 rounded-b-2xl border-light-neutral-500",
|
||||
"bg-grey-970 p-4 text-light-neutral-300"
|
||||
)}
|
||||
>
|
||||
{tabs.map((child, index) => {
|
||||
if (index !== activeIndex) {
|
||||
return null;
|
||||
}
|
||||
const tabContent = (child.props as PropsWithChildren).children;
|
||||
return (
|
||||
<div key={index} role="tabpanel" aria-labelledby={`tab-${index}`}>
|
||||
{tabContent}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Tabs.Item = TabItem as React.FC<PropsWithChildren<TabItemPropsPublic>>;
|
||||
|
||||
export { Tabs };
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import React from "react";
|
||||
import { Icon, type IconProps } from "../../icon/Icon";
|
||||
import { Typography } from "../../typography/Typography";
|
||||
|
||||
export type TabItemProps = HTMLProps<"div"> & {
|
||||
icon?: IconProps["icon"];
|
||||
text: string;
|
||||
children: React.ReactNode;
|
||||
index: number;
|
||||
isActive: boolean;
|
||||
isFirst: boolean;
|
||||
isLast: boolean;
|
||||
onSelect: () => void;
|
||||
};
|
||||
export type TabItemPropsPublic = Omit<
|
||||
TabItemProps,
|
||||
"index" | "isActive" | "isFirst" | "isLast" | "onSelect"
|
||||
>;
|
||||
|
||||
export const TabItem = ({
|
||||
text,
|
||||
index,
|
||||
isActive,
|
||||
isFirst,
|
||||
isLast,
|
||||
onSelect,
|
||||
icon,
|
||||
}: TabItemProps) => {
|
||||
return (
|
||||
<button
|
||||
role="tab"
|
||||
id={`tab-${index}`}
|
||||
aria-selected={isActive}
|
||||
aria-controls={`panel-${index}`}
|
||||
className={cn(
|
||||
"flex items-center gap-x-3 cursor-pointer",
|
||||
"px-6 py-3",
|
||||
"text-light-neutral-15 whitespace-nowrap",
|
||||
"border-light-neutral-500 border-b-1 border-t-1 border-r-1",
|
||||
"bg-light-neutral-970 focus:outline-0",
|
||||
"enabled:hover:bg-light-neutral-500",
|
||||
"enabled:focus:bg-grey-970",
|
||||
"enabled:active:bg-grey-970 enabled:active:text-primary-500",
|
||||
isActive && "enabled:text-primary-500",
|
||||
isFirst && "border-l-1 rounded-tl-2xl",
|
||||
isLast && "rounded-tr-2xl"
|
||||
)}
|
||||
onClick={onSelect}
|
||||
>
|
||||
{icon && <Icon icon={icon} className={cn("w-4 h-4 shrink-0")} />}
|
||||
<Typography.Text fontSize={"s"} fontWeight={400}>
|
||||
{text}
|
||||
</Typography.Text>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { Icon, type IconProps } from "../../icon/Icon";
|
||||
|
||||
type TabScrollerProps = {
|
||||
position: "left" | "right";
|
||||
onScroll(): void;
|
||||
};
|
||||
|
||||
const tabScrollMetadata: Record<
|
||||
TabScrollerProps["position"],
|
||||
{ className: string; icon: IconProps["icon"] }
|
||||
> = {
|
||||
left: {
|
||||
className: cn("rounded-tl-2xl"),
|
||||
icon: "ChevronDoubleLeft",
|
||||
},
|
||||
right: {
|
||||
className: cn("rounded-tr-2xl"),
|
||||
icon: "ChevronDoubleRight",
|
||||
},
|
||||
};
|
||||
|
||||
export const TabScroller = ({ position, onScroll }: TabScrollerProps) => {
|
||||
const { className, icon } = tabScrollMetadata[position];
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onScroll}
|
||||
className={cn(
|
||||
"border-1 border-light-neutral-500",
|
||||
"flex flex-row items-center px-4",
|
||||
"bg-light-neutral-970",
|
||||
"enabled:hover:bg-light-neutral-500",
|
||||
"enabled:focus:bg-light-neutral-970",
|
||||
"enabled:active:bg-grey-970",
|
||||
className
|
||||
)}
|
||||
aria-label={`Scroll tabs ${position}`}
|
||||
>
|
||||
<Icon icon={icon} className={cn("w-4.5 h-4.5 text-primary-500")} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
type UseElementOverflowParams = {
|
||||
contentRef: React.RefObject<HTMLDivElement | null>;
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
export function useElementOverflow({
|
||||
contentRef,
|
||||
containerRef,
|
||||
}: UseElementOverflowParams): boolean {
|
||||
const [isOverflowing, setIsOverflowing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkOverflow = () => {
|
||||
const container = containerRef.current;
|
||||
const content = contentRef.current;
|
||||
if (container && content) {
|
||||
setIsOverflowing(content.scrollWidth > container.clientWidth);
|
||||
}
|
||||
};
|
||||
|
||||
checkOverflow();
|
||||
const resizeObserver = new ResizeObserver(checkOverflow);
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
if (contentRef.current) {
|
||||
resizeObserver.observe(contentRef.current);
|
||||
}
|
||||
|
||||
return () => resizeObserver.disconnect();
|
||||
}, [containerRef, contentRef]);
|
||||
|
||||
return isOverflowing;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
const SCROLL_OFFSET = 200;
|
||||
|
||||
type UseElementScrollParams = {
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
scrollRef: React.RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
export function useElementScroll({
|
||||
containerRef,
|
||||
scrollRef,
|
||||
}: UseElementScrollParams) {
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(false);
|
||||
|
||||
const updateScrollState = useCallback(() => {
|
||||
const container = containerRef?.current;
|
||||
const scroll = scrollRef?.current;
|
||||
if (!container || !scroll) {
|
||||
return;
|
||||
}
|
||||
const maxScrollLeft = scroll.scrollWidth - container.clientWidth;
|
||||
|
||||
setCanScrollLeft(scroll.scrollLeft > 0);
|
||||
setCanScrollRight(scroll.scrollLeft < maxScrollLeft);
|
||||
}, [containerRef, scrollRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const scroll = scrollRef?.current;
|
||||
if (!scroll) {
|
||||
return;
|
||||
}
|
||||
updateScrollState();
|
||||
|
||||
const preventScroll = (e: Event) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
scroll.addEventListener("wheel", preventScroll, { passive: false });
|
||||
|
||||
scroll.addEventListener("scroll", updateScrollState);
|
||||
window.addEventListener("resize", updateScrollState);
|
||||
|
||||
return () => {
|
||||
scroll.removeEventListener("wheel", preventScroll);
|
||||
scroll.removeEventListener("scroll", updateScrollState);
|
||||
window.removeEventListener("resize", updateScrollState);
|
||||
};
|
||||
}, [updateScrollState]);
|
||||
|
||||
const scrollLeft = useCallback(() => {
|
||||
scrollRef?.current?.scrollBy?.({
|
||||
left: -SCROLL_OFFSET,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const scrollRight = useCallback(() => {
|
||||
scrollRef?.current?.scrollBy?.({ left: SCROLL_OFFSET, behavior: "smooth" });
|
||||
}, []);
|
||||
|
||||
return {
|
||||
canScrollLeft,
|
||||
canScrollRight,
|
||||
scrollLeft,
|
||||
scrollRight,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Button } from "../button/Button";
|
||||
import { ToastManager } from "./ToastManager";
|
||||
import { toasterMessages } from "./Toast";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Toast",
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const ToastComponent = () => {
|
||||
const notify = () => {
|
||||
toasterMessages.error("Lorem Ipsum");
|
||||
toasterMessages.success("Lorem Ipsum");
|
||||
toasterMessages.info("Lorem Ipsum");
|
||||
toasterMessages.warning("Lorem Ipsum");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastManager>
|
||||
<Button onClick={notify}>Notify</Button>
|
||||
</ToastManager>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Main: Story = {
|
||||
args: {},
|
||||
render: ToastComponent,
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
import { toast as sonnerToast } from "sonner";
|
||||
import { Icon, type IconProps } from "../icon/Icon";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { toastStyles } from "./utils";
|
||||
|
||||
type IBaseToastProps = {
|
||||
id: string | number;
|
||||
text: string;
|
||||
icon: IconProps["icon"];
|
||||
iconClassName: string;
|
||||
};
|
||||
const BaseToast = (props: IBaseToastProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"border-1 border-light-neutral-500 rounded-l-[100px] rounded-r-4xl",
|
||||
"bg-light-neutral-900 px-3 py-3",
|
||||
"max-w-sm min-w-32",
|
||||
"flex flex-row items-center justify-between gap-x-4"
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={props.icon}
|
||||
className={cn("w-6 h-6 flex-shrink-0", props.iconClassName)}
|
||||
/>
|
||||
<Typography.Text fontSize="xs" className="text-white">
|
||||
{props.text}
|
||||
</Typography.Text>
|
||||
<button
|
||||
onClick={() => sonnerToast.dismiss(props.id)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Icon icon="X" className={cn("w-6 h-6 flex-shrink-0 text-white")} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const toasterMessages = {
|
||||
error: (text?: string) => {
|
||||
const styles = toastStyles["error"];
|
||||
sonnerToast.custom((id) => (
|
||||
<BaseToast
|
||||
id={id}
|
||||
icon={styles.icon}
|
||||
iconClassName={cn(styles.iconColor)}
|
||||
text={text!}
|
||||
/>
|
||||
));
|
||||
},
|
||||
success: (text?: string) => {
|
||||
const styles = toastStyles["success"];
|
||||
sonnerToast.custom((id) => (
|
||||
<BaseToast
|
||||
id={id}
|
||||
icon={styles.icon}
|
||||
iconClassName={cn(styles.iconColor)}
|
||||
text={text!}
|
||||
/>
|
||||
));
|
||||
},
|
||||
info: (text?: string) => {
|
||||
const styles = toastStyles["info"];
|
||||
sonnerToast.custom((id) => (
|
||||
<BaseToast
|
||||
id={id}
|
||||
icon={styles.icon}
|
||||
iconClassName={cn(styles.iconColor)}
|
||||
text={text!}
|
||||
/>
|
||||
));
|
||||
},
|
||||
warning: (text?: string) => {
|
||||
const styles = toastStyles["warning"];
|
||||
sonnerToast.custom((id) => (
|
||||
<BaseToast
|
||||
id={id}
|
||||
icon={styles.icon}
|
||||
iconClassName={cn(styles.iconColor)}
|
||||
text={text!}
|
||||
/>
|
||||
));
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { type PropsWithChildren } from "react";
|
||||
import { Toaster, type ToasterProps } from "sonner";
|
||||
|
||||
export const ToastManager = (props: PropsWithChildren<ToasterProps>) => {
|
||||
return (
|
||||
<>
|
||||
<Toaster {...props} />
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { IconProps } from "../icon/Icon";
|
||||
|
||||
type ToastType = "error" | "success" | "info" | "warning";
|
||||
|
||||
export const toastStyles: Record<
|
||||
ToastType,
|
||||
{ icon: IconProps["icon"]; iconColor: string }
|
||||
> = {
|
||||
error: {
|
||||
icon: "ExclamationOctagonFill",
|
||||
iconColor: "text-red-400",
|
||||
},
|
||||
success: {
|
||||
icon: "CheckCircleFill",
|
||||
iconColor: "text-green-500",
|
||||
},
|
||||
info: {
|
||||
icon: "PatchQuestionFill",
|
||||
iconColor: "text-aqua-500",
|
||||
},
|
||||
warning: {
|
||||
icon: "ExclamationTriangleFill",
|
||||
iconColor: "text-primary-500",
|
||||
},
|
||||
};
|
||||
@@ -8,8 +8,12 @@ export { Input } from "./components/input/Input";
|
||||
export { InteractiveChip } from "./components/interactive-chip/InteractiveChip";
|
||||
export { RadioGroup } from "./components/radio-group/RadioGroup";
|
||||
export { RadioOption } from "./components/radio-group/RadioOption";
|
||||
export { Select } from "./components/select/Select";
|
||||
export { Scrollable } from "./components/scrollable/Scrollable";
|
||||
export { ToastManager } from "./components/toast/ToastManager";
|
||||
export { toasterMessages } from "./components/toast/Toast";
|
||||
export { Toggle } from "./components/toggle/Toggle";
|
||||
export { Tabs } from "./components/tabs/Tabs";
|
||||
export { Tooltip } from "./components/tooltip/Tooltip";
|
||||
export { Typography } from "./components/typography/Typography";
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"email": "stephan@all-hands.dev"
|
||||
}
|
||||
],
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"description": "OpenHands UI Components",
|
||||
"keywords": [
|
||||
"openhands",
|
||||
@@ -78,6 +78,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"focus-trap-react": "^11.0.4",
|
||||
"react-bootstrap-icons": "^1.11.6",
|
||||
"react-select": "^5.10.2",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss": "^4.1.10"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% if repository_info %}
|
||||
<REPOSITORY_INFO>
|
||||
At the user's request, repository {{ repository_info.repo_name }} has been cloned to the current working directory {{ repository_info.repo_directory }}.
|
||||
At the user's request, repository {{ repository_info.repo_name }} has been cloned to {{ repository_info.repo_directory }} in the current working directory.
|
||||
</REPOSITORY_INFO>
|
||||
{% endif %}
|
||||
{% if repository_instructions -%}
|
||||
@@ -10,6 +10,9 @@ At the user's request, repository {{ repository_info.repo_name }} has been clone
|
||||
{% endif %}
|
||||
{% if runtime_info -%}
|
||||
<RUNTIME_INFORMATION>
|
||||
{% if runtime_info.working_dir %}
|
||||
The current working directory is {{ runtime_info.working_dir }}
|
||||
{% endif %}
|
||||
{% if runtime_info.available_hosts %}
|
||||
The user has access to the following hosts for accessing a web application,
|
||||
each of which has a corresponding port:
|
||||
|
||||
@@ -14,6 +14,12 @@ Your primary role is to assist users by executing commands, modifying code, and
|
||||
* When a user provides a file path, do NOT assume it's relative to the current working directory. First explore the file system to locate the file before working on it.
|
||||
* If asked to edit a file, edit the file directly, rather than creating a new file with a different filename.
|
||||
* For global search-and-replace operations, consider using `sed` instead of opening file editors multiple times.
|
||||
* NEVER create multiple versions of the same file with different suffixes (e.g., file_test.py, file_fix.py, file_simple.py). Instead:
|
||||
- Always modify the original file directly when making changes
|
||||
- If you need to create a temporary file for testing, delete it once you've confirmed your solution works
|
||||
- If you decide a file you created is no longer useful, delete it instead of creating a new version
|
||||
* Do NOT include documentation files explaining your changes in version control unless the user explicitly requests it
|
||||
* When reproducing bugs or implementing fixes, use a single file rather than creating multiple files with different versions
|
||||
</FILE_SYSTEM_GUIDELINES>
|
||||
|
||||
<CODE_QUALITY>
|
||||
@@ -46,7 +52,10 @@ Your primary role is to assist users by executing commands, modifying code, and
|
||||
* For new features: Consider test-driven development when appropriate
|
||||
* If the repository lacks testing infrastructure and implementing tests would require extensive setup, consult with the user before investing time in building testing infrastructure
|
||||
* If the environment is not set up to run tests, consult with the user first before investing time to install all dependencies
|
||||
4. IMPLEMENTATION: Make focused, minimal changes to address the problem
|
||||
4. IMPLEMENTATION:
|
||||
* Make focused, minimal changes to address the problem
|
||||
* Always modify existing files directly rather than creating new versions with different suffixes
|
||||
* If you create temporary files for testing, delete them after confirming your solution works
|
||||
5. VERIFICATION: If the environment is set up to run tests, test your implementation thoroughly, including edge cases. If the environment is not set up to run tests, consult with the user first before investing time to run tests.
|
||||
</PROBLEM_SOLVING_WORKFLOW>
|
||||
|
||||
@@ -72,3 +81,13 @@ Your primary role is to assist users by executing commands, modifying code, and
|
||||
4. Document your reasoning process
|
||||
* When you run into any major issue while executing a plan from the user, please don't try to directly work around it. Instead, propose a new plan and confirm with the user before proceeding.
|
||||
</TROUBLESHOOTING>
|
||||
|
||||
<DOCUMENTATION>
|
||||
* When explaining changes or solutions to the user:
|
||||
- Include explanations in your conversation responses rather than creating separate documentation files
|
||||
- If you need to create documentation files for reference, do NOT include them in version control unless explicitly requested
|
||||
- Never create multiple versions of documentation files with different suffixes
|
||||
* If the user asks for documentation:
|
||||
- Confirm whether they want it as a separate file or just in the conversation
|
||||
- Ask if they want documentation files to be included in version control
|
||||
</DOCUMENTATION>
|
||||
|
||||
@@ -10,6 +10,9 @@ At the user's request, repository {{ repository_info.repo_name }} has been clone
|
||||
{% endif %}
|
||||
{% if runtime_info and (runtime_info.additional_agent_instructions or runtime_info.date) -%}
|
||||
<RUNTIME_INFORMATION>
|
||||
{% if runtime_info.working_dir %}
|
||||
The current working directory is {{ runtime_info.working_dir }}
|
||||
{% endif %}
|
||||
{% if runtime_info.additional_agent_instructions %}
|
||||
{{ runtime_info.additional_agent_instructions }}
|
||||
{% endif %}
|
||||
|
||||
@@ -277,6 +277,7 @@ async def run_session(
|
||||
selected_repository=config.sandbox.selected_repo,
|
||||
repo_directory=repo_directory,
|
||||
conversation_instructions=conversation_instructions,
|
||||
working_dir=os.getcwd(),
|
||||
)
|
||||
|
||||
# Add MCP tools to the agent
|
||||
|
||||
@@ -182,12 +182,14 @@ VERIFIED_MISTRAL_MODELS = [
|
||||
]
|
||||
|
||||
VERIFIED_OPENHANDS_MODELS = [
|
||||
'claude-sonnet-4-20250514',
|
||||
'claude-opus-4-20250514',
|
||||
'devstral-small-2507',
|
||||
'devstral-medium-2507',
|
||||
'o3',
|
||||
'o4-mini',
|
||||
'claude-sonnet-4-20250514',
|
||||
'gemini-2.5-pro',
|
||||
'kimi-k2-0711-preview',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from pydantic.fields import FieldInfo
|
||||
|
||||
OH_DEFAULT_AGENT = 'CodeActAgent'
|
||||
OH_MAX_ITERATIONS = 500
|
||||
DEFAULT_WORKSPACE_MOUNT_PATH_IN_SANDBOX = '/workspace'
|
||||
|
||||
|
||||
def get_field_info(field: FieldInfo) -> dict[str, Any]:
|
||||
|
||||
@@ -190,7 +190,7 @@ class OpenHandsMCPConfig:
|
||||
@staticmethod
|
||||
def create_default_mcp_server_config(
|
||||
host: str, config: 'OpenHandsConfig', user_id: str | None = None
|
||||
) -> tuple[MCPSHTTPServerConfig, list[MCPStdioServerConfig]]:
|
||||
) -> tuple[MCPSHTTPServerConfig | None, list[MCPStdioServerConfig]]:
|
||||
"""
|
||||
Create a default MCP server configuration.
|
||||
|
||||
@@ -198,7 +198,7 @@ class OpenHandsMCPConfig:
|
||||
host: Host string
|
||||
config: OpenHandsConfig
|
||||
Returns:
|
||||
tuple[MCPSSEServerConfig, list[MCPStdioServerConfig]]: A tuple containing the default SSE server configuration and a list of MCP stdio server configurations
|
||||
tuple[MCPSHTTPServerConfig | None, list[MCPStdioServerConfig]]: A tuple containing the default SHTTP server configuration (or None) and a list of MCP stdio server configurations
|
||||
"""
|
||||
stdio_servers = []
|
||||
search_engine_stdio_server = OpenHandsMCPConfig.add_search_engine(config)
|
||||
|
||||
@@ -7,6 +7,7 @@ from openhands.core import logger
|
||||
from openhands.core.config.agent_config import AgentConfig
|
||||
from openhands.core.config.cli_config import CLIConfig
|
||||
from openhands.core.config.config_utils import (
|
||||
DEFAULT_WORKSPACE_MOUNT_PATH_IN_SANDBOX,
|
||||
OH_DEFAULT_AGENT,
|
||||
OH_MAX_ITERATIONS,
|
||||
model_defaults_to_dict,
|
||||
@@ -78,10 +79,13 @@ class OpenHandsConfig(BaseModel):
|
||||
description='API key for Tavily search engine (https://tavily.com/). Required for search functionality.',
|
||||
)
|
||||
|
||||
workspace_base: str | None = Field(default=None)
|
||||
workspace_mount_path_in_sandbox: str = Field(
|
||||
default=DEFAULT_WORKSPACE_MOUNT_PATH_IN_SANDBOX
|
||||
)
|
||||
|
||||
# Deprecated parameters - will be removed in a future version
|
||||
workspace_base: str | None = Field(default=None, deprecated=True)
|
||||
workspace_mount_path: str | None = Field(default=None, deprecated=True)
|
||||
workspace_mount_path_in_sandbox: str = Field(default='/workspace', deprecated=True)
|
||||
workspace_mount_rewrite: str | None = Field(default=None, deprecated=True)
|
||||
# End of deprecated parameters
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ async def run_controller(
|
||||
selected_repository=config.sandbox.selected_repo,
|
||||
repo_directory=repo_directory,
|
||||
conversation_instructions=conversation_instructions,
|
||||
working_dir=config.workspace_mount_path_in_sandbox,
|
||||
)
|
||||
|
||||
# Add MCP tools to the agent
|
||||
|
||||
@@ -12,6 +12,7 @@ from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
)
|
||||
from openhands.core.config.config_utils import DEFAULT_WORKSPACE_MOUNT_PATH_IN_SANDBOX
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events import EventStream
|
||||
from openhands.events.event import Event
|
||||
@@ -140,6 +141,7 @@ def create_memory(
|
||||
repo_directory: str | None = None,
|
||||
status_callback: Callable | None = None,
|
||||
conversation_instructions: str | None = None,
|
||||
working_dir: str = DEFAULT_WORKSPACE_MOUNT_PATH_IN_SANDBOX,
|
||||
) -> Memory:
|
||||
"""Create a memory for the agent to use.
|
||||
|
||||
@@ -162,7 +164,7 @@ def create_memory(
|
||||
|
||||
if runtime:
|
||||
# sets available hosts
|
||||
memory.set_runtime_info(runtime, {})
|
||||
memory.set_runtime_info(runtime, {}, working_dir)
|
||||
|
||||
# loads microagents from repo/.openhands/microagents
|
||||
microagents: list[BaseMicroagent] = runtime.get_microagents_from_selected_repo(
|
||||
|
||||
@@ -76,6 +76,7 @@ class RecallObservation(Observation):
|
||||
date: str = ''
|
||||
custom_secrets_descriptions: dict[str, str] = field(default_factory=dict)
|
||||
conversation_instructions: str = ''
|
||||
working_dir: str = ''
|
||||
|
||||
# knowledge
|
||||
microagent_knowledge: list[MicroagentKnowledge] = field(default_factory=list)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user