Compare commits

...

35 Commits

Author SHA1 Message Date
openhands 8c8c1c528f Merge origin/main into test/replicate-many-changes 2025-07-17 19:00:39 +00:00
amanape bf8b57ba12 Add comment 2025-07-17 22:53:01 +04:00
mamoodi 4c39e92351 Docs for OpenHands LLM Provider (#9751) 2025-07-17 18:51:34 +00:00
Engel Nyst e65e0a98f0 Remove/reduce unused content in a CmdOutputObservation (#7404)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 19:34:46 +02:00
Hiep Le eecc00fa4a feat(backend): API to get the microagents for the selected repository. (#9749) 2025-07-17 21:00:45 +04:00
sp.wack 5654e251a8 chore: bump to 1.0.0-beta.5 (#9770) 2025-07-17 16:44:01 +00:00
Rohit Malhotra d9694aabcd Add conditional rendering of auth providers based on server config (#9752)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 16:42:57 +00:00
Ray Myers bc8ef37192 fix - Avoid building debug log message when not logged (#9600) 2025-07-17 11:42:06 -05:00
Ray Myers 5f141f7712 Fix type hint: add | None to first element of create_default_mcp_server_config return tuple (#9754)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 12:10:16 -04:00
Hiep Le 30e3011cb0 feat(backend): Include owner_type in the Get Repositories API response. (#9763) 2025-07-17 11:45:05 -04:00
Xingyao Wang 3475d8021b Fix file duplication in system prompt (#9741)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 15:29:44 +00:00
dependabot[bot] 32cd50db2f chore(deps): bump the version-all group in /frontend with 6 updates (#9762)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 15:13:44 +00:00
Graham Neubig f0a6db936c Fix: Add navigation to conversation page after clicking Launch button on task suggestions (#9760)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 17:43:13 +04:00
Peter Hamilton 11c37d8d70 Update llm constants to match on unpinned claude-sonnet-4 (#9681) 2025-07-17 13:48:35 +02:00
Hiep Le 7e1367057a feat(frontend): Build Microagent Management Sidebar UI. (#9717) 2025-07-17 15:45:24 +04:00
dependabot[bot] 3bbb0c6279 chore(deps): bump the version-all group in /frontend with 2 updates (#9739)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 11:40:08 +00:00
Xingyao Wang eed71c21bd Add kimi-k2-0711-preview model to OpenHands provider (#9755)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-17 15:25:31 +04:00
Graham Neubig 4f46826de9 Add Moonshot AI Kimi-K2 model to recommended models (#9706)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: mamoodi <mamoodiha@gmail.com>
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
2025-07-17 04:43:03 +00:00
juanmichelini ea50fe4e3c Fix: Continue evaluation when an instance fails after max retries (#8868)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Xingyao Wang <xingyaoww@gmail.com>
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
2025-07-16 22:42:44 +00:00
Tim O'Farrell b057af8d63 Feat: Add current working directory to LLM instructions (#9718) 2025-07-16 21:10:03 +00:00
Engel Nyst fba2218760 Fix integration tests (#9746) 2025-07-16 22:16:40 +02:00
mamoodi 6147cbdc18 Update OpenHands Cloud with Bitbucket docs (#9740) 2025-07-16 15:10:12 -04:00
Mislav Lukach 802acb3c7e feat(ui): select component (#9712)
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-07-16 17:28:01 +00:00
Xingyao Wang 376dc21e34 (llm): Add Kimi K2 to function calling supported model (#9747) 2025-07-16 17:19:10 +00:00
Mislav Lukach 387318385c feat(ui): tab component (#9673)
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-07-16 16:38:51 +00:00
Mislav Lukach 553f0a0918 feat(ui): toast component (#9632)
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-07-16 16:33:31 +00:00
mamoodi 0d1e21ae45 Release 0.49.0 (#9691)
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
Co-authored-by: Tim O'Farrell <tofarr@gmail.com>
2025-07-16 08:46:41 -04:00
Xingyao Wang a885e9e4d2 Fix newline display in frontend UI (#9729) 2025-07-15 20:59:56 -04:00
Graham Neubig 4c10848e8d Fix dictionary changed size during iteration error in override_provider_tokens_with_custom_secret (#9728)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-15 19:03:28 -04:00
Tim O'Farrell 1d95b01514 Fix: Keep the existing behavior in the docker command. (#9724) 2025-07-15 19:34:00 +00:00
Xingyao Wang cd32b5508c Add OpenAI o3 model support to verified models and OpenHands provider (#9720)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-15 18:19:44 +00:00
Xingyao Wang 9a3bf0f2aa chore(cli): make sonnet first in openhands provider model choice (#9719) 2025-07-15 17:38:08 +00:00
Ryan H. Tran 1d04a83e08 docs: Add SHTTP transport documentation to MCP usage guide (#9701)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-07-15 23:18:05 +07:00
Hiep Le 17e9b0fd6a chore(Microagent Management UI): Set up the feature flag for the Microagent Management page. (#9704) 2025-07-15 19:49:35 +04:00
dependabot[bot] 54986c9841 chore(deps): bump the version-all group in /frontend with 3 updates (#9709)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 14:50:20 +00:00
129 changed files with 4949 additions and 1047 deletions
+3 -3
View File
@@ -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' }}
+62
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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` を実行してください。
+1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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:
+3
View File
@@ -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"
]
}
+5
View File
@@ -1827,6 +1827,11 @@
"updated_at": {
"type": "string",
"format": "date-time"
},
"owner_type": {
"type": "string",
"enum": ["user", "organization"],
"nullable": true
}
}
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

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!
![Connect Repo](/static/img/connect-repo-no-github.png)
## Next Steps
- [Learn about the Cloud UI](/usage/cloud/cloud-ui).
- [Use the Cloud API](/usage/cloud/cloud-api) to programmatically interact with OpenHands.
+3 -2
View File
@@ -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`.
+1 -1
View File
@@ -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!
![Connect Repo](/static/img/connect-repo.png)
![Connect Repo](/static/img/connect-repo-no-github.png)
## Using Tokens with Reduced Scopes
+3 -2
View File
@@ -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
View File
@@ -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?
+4 -3
View File
@@ -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 users
permissions. This prevents the agent from creating root-owned files in the mounted workspace.
The conversation history will be saved in `~/.openhands/sessions`.
+2 -1
View File
@@ -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).
+4 -3
View File
@@ -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 users
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.
+9 -4
View File
@@ -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
+4 -4
View File
@@ -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
+25
View File
@@ -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.
+34
View File
@@ -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`.
![OpenHands LLM API Key](/static/img/openhands-llm-api-key.png)
## 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.
![OpenHands Provider in CLI](/static/img/openhands-provider-cli.png)
## 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)
+3 -3
View File
@@ -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
View File
@@ -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)
+94 -5
View File
@@ -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");
});
});
+729 -767
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -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",
+1 -1
View File
@@ -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()
+2
View File
@@ -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
@@ -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>
);
}
@@ -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;
}
@@ -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>
);
}
@@ -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>
);
}
@@ -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>
);
}
@@ -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>
);
}
@@ -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>
);
}
@@ -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>
);
}
@@ -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}`}
+12
View File
@@ -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",
}
+192
View File
@@ -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 працювати швидше та точніше у майбутніх розмовах."
}
}
+4
View File
@@ -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

+6
View File
@@ -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

+1
View File
@@ -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;
+1
View File
@@ -214,6 +214,7 @@ export default function MainApp() {
<AuthModal
githubAuthUrl={effectiveGitHubAuthUrl}
appMode={config.data?.APP_MODE}
providersConfigured={config.data?.PROVIDERS_CONFIGURED}
/>
)}
{renderReAuthModal && <ReauthModal />}
+2
View File
@@ -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;
+2
View File
@@ -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({
+6 -2
View File
@@ -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
View File
@@ -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]} />,
};
+134
View File
@@ -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>
);
};
+18
View File
@@ -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>
),
};
+102
View File
@@ -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,
};
+85
View File
@@ -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}
</>
);
};
+25
View File
@@ -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",
},
};
+4
View File
@@ -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";
+3 -1
View File
@@ -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 %}
+1
View File
@@ -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
+3 -1
View File
@@ -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',
]
+1
View File
@@ -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]:
+2 -2
View File
@@ -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)
+6 -2
View File
@@ -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
+1
View File
@@ -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
+3 -1
View File
@@ -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(
+1
View File
@@ -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