mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
49 Commits
implement-
...
ray/remove
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
588e838dc4 | ||
|
|
2550c08749 | ||
|
|
0651c51901 | ||
|
|
3ce19993bc | ||
|
|
26a9abbe82 | ||
|
|
240017add1 | ||
|
|
b5958b069e | ||
|
|
59b8009d7a | ||
|
|
b8b4f58a79 | ||
|
|
fcb190281c | ||
|
|
9fcf900a23 | ||
|
|
06ad5e30c9 | ||
|
|
739044087b | ||
|
|
fa041537c3 | ||
|
|
079f423a4b | ||
|
|
f6060f9c53 | ||
|
|
b7f234641c | ||
|
|
4ac0af699f | ||
|
|
fb9a941722 | ||
|
|
c05339cb2d | ||
|
|
2ef518f063 | ||
|
|
fbd9280239 | ||
|
|
45ac6b839c | ||
|
|
8b59143174 | ||
|
|
c7b8f5d0d1 | ||
|
|
09533d3cb9 | ||
|
|
00582a487c | ||
|
|
7a168b9b5f | ||
|
|
556ec9ab1a | ||
|
|
d567d22748 | ||
|
|
e045b757fa | ||
|
|
38ffc85470 | ||
|
|
58ea7b5248 | ||
|
|
f62ed911d2 | ||
|
|
d13e32bcec | ||
|
|
b978b71c47 | ||
|
|
dc2f5cd1b0 | ||
|
|
07041e057d | ||
|
|
6e91d19f80 | ||
|
|
936510e219 | ||
|
|
7af35ab827 | ||
|
|
a7245f2de2 | ||
|
|
6d7ab8a022 | ||
|
|
bbfa37fd97 | ||
|
|
d0cf12e474 | ||
|
|
78306b1ee7 | ||
|
|
f6d99234f1 | ||
|
|
19ca52f954 | ||
|
|
df75116184 |
121
.github/workflows/run-eval.yml
vendored
121
.github/workflows/run-eval.yml
vendored
@@ -1,56 +1,135 @@
|
||||
# Run evaluation on a PR
|
||||
# Run evaluation on a PR, after releases, or manually
|
||||
name: Run Eval
|
||||
|
||||
# Runs when a PR is labeled with one of the "run-eval-" labels
|
||||
# Runs when a PR is labeled with one of the "run-eval-" labels, after releases, or manually triggered
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch to evaluate'
|
||||
required: true
|
||||
default: 'main'
|
||||
eval_instances:
|
||||
description: 'Number of evaluation instances'
|
||||
required: true
|
||||
default: '50'
|
||||
type: choice
|
||||
options:
|
||||
- '1'
|
||||
- '2'
|
||||
- '50'
|
||||
- '100'
|
||||
reason:
|
||||
description: 'Reason for manual trigger'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
# Environment variable for the master GitHub issue number where all evaluation results will be commented
|
||||
# This should be set to the issue number where you want all evaluation results to be posted
|
||||
MASTER_EVAL_ISSUE_NUMBER: ${{ vars.MASTER_EVAL_ISSUE_NUMBER || '0' }}
|
||||
|
||||
jobs:
|
||||
trigger-job:
|
||||
name: Trigger remote eval job
|
||||
if: ${{ github.event.label.name == 'run-eval-1' || github.event.label.name == 'run-eval-2' || github.event.label.name == 'run-eval-50' || github.event.label.name == 'run-eval-100' }}
|
||||
if: ${{ (github.event_name == 'pull_request' && (github.event.label.name == 'run-eval-1' || github.event.label.name == 'run-eval-2' || github.event.label.name == 'run-eval-50' || github.event.label.name == 'run-eval-100')) || github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2204
|
||||
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
ref: ${{ github.event_name == 'pull_request' && github.head_ref || (github.event_name == 'workflow_dispatch' && github.event.inputs.branch) || github.ref }}
|
||||
|
||||
- name: Trigger remote job
|
||||
env:
|
||||
PR_BRANCH: ${{ github.head_ref }}
|
||||
- name: Set evaluation parameters
|
||||
id: eval_params
|
||||
run: |
|
||||
REPO_URL="https://github.com/${{ github.repository }}"
|
||||
echo "Repository URL: $REPO_URL"
|
||||
echo "PR Branch: $PR_BRANCH"
|
||||
|
||||
if [[ "${{ github.event.label.name }}" == "run-eval-1" ]]; then
|
||||
EVAL_INSTANCES="1"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-2" ]]; then
|
||||
EVAL_INSTANCES="2"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-50" ]]; then
|
||||
# Determine branch based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
EVAL_BRANCH="${{ github.head_ref }}"
|
||||
echo "PR Branch: $EVAL_BRANCH"
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
EVAL_BRANCH="${{ github.event.inputs.branch }}"
|
||||
echo "Manual Branch: $EVAL_BRANCH"
|
||||
else
|
||||
# For release events, use the tag name or main branch
|
||||
EVAL_BRANCH="${{ github.ref_name }}"
|
||||
echo "Release Branch/Tag: $EVAL_BRANCH"
|
||||
fi
|
||||
|
||||
# Determine evaluation instances based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
if [[ "${{ github.event.label.name }}" == "run-eval-1" ]]; then
|
||||
EVAL_INSTANCES="1"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-2" ]]; then
|
||||
EVAL_INSTANCES="2"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-50" ]]; then
|
||||
EVAL_INSTANCES="50"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-100" ]]; then
|
||||
EVAL_INSTANCES="100"
|
||||
fi
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
EVAL_INSTANCES="${{ github.event.inputs.eval_instances }}"
|
||||
else
|
||||
# For release events, default to 50 instances
|
||||
EVAL_INSTANCES="50"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-100" ]]; then
|
||||
EVAL_INSTANCES="100"
|
||||
fi
|
||||
|
||||
echo "Evaluation instances: $EVAL_INSTANCES"
|
||||
echo "repo_url=$REPO_URL" >> $GITHUB_OUTPUT
|
||||
echo "eval_branch=$EVAL_BRANCH" >> $GITHUB_OUTPUT
|
||||
echo "eval_instances=$EVAL_INSTANCES" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Trigger remote job
|
||||
run: |
|
||||
# Determine PR number for the remote evaluation system
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
else
|
||||
# For non-PR triggers, use the master issue number as PR number
|
||||
PR_NUMBER="${{ env.MASTER_EVAL_ISSUE_NUMBER }}"
|
||||
fi
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{ secrets.PAT_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-d "{\"ref\": \"main\", \"inputs\": {\"github-repo\": \"${REPO_URL}\", \"github-branch\": \"${PR_BRANCH}\", \"pr-number\": \"${{ github.event.pull_request.number }}\", \"eval-instances\": \"${EVAL_INSTANCES}\"}}" \
|
||||
-d "{\"ref\": \"main\", \"inputs\": {\"github-repo\": \"${{ steps.eval_params.outputs.repo_url }}\", \"github-branch\": \"${{ steps.eval_params.outputs.eval_branch }}\", \"pr-number\": \"${PR_NUMBER}\", \"eval-instances\": \"${{ steps.eval_params.outputs.eval_instances }}\"}}" \
|
||||
https://api.github.com/repos/All-Hands-AI/evaluation/actions/workflows/create-branch.yml/dispatches
|
||||
|
||||
# Send Slack message
|
||||
PR_URL="https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
|
||||
slack_text="PR $PR_URL has triggered evaluation on $EVAL_INSTANCES instances..."
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
TRIGGER_URL="https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
|
||||
slack_text="PR $TRIGGER_URL has triggered evaluation on ${{ steps.eval_params.outputs.eval_instances }} instances..."
|
||||
elif [[ "${{ github.event_name }}" == "release" ]]; then
|
||||
TRIGGER_URL="https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}"
|
||||
slack_text="Release $TRIGGER_URL has triggered evaluation on ${{ steps.eval_params.outputs.eval_instances }} instances..."
|
||||
else
|
||||
TRIGGER_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
slack_text="Manual trigger (${{ github.event.inputs.reason || 'No reason provided' }}) has triggered evaluation on ${{ steps.eval_params.outputs.eval_instances }} instances for branch ${{ steps.eval_params.outputs.eval_branch }}..."
|
||||
fi
|
||||
|
||||
curl -X POST -H 'Content-type: application/json' --data '{"text":"'"$slack_text"'"}' \
|
||||
https://hooks.slack.com/services/${{ secrets.SLACK_TOKEN }}
|
||||
|
||||
- name: Comment on PR
|
||||
- name: Comment on issue/PR
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
# For PR triggers, comment on the PR. For other triggers, comment on the master issue
|
||||
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || env.MASTER_EVAL_ISSUE_NUMBER }}
|
||||
unique: false
|
||||
comment: |
|
||||
Running evaluation on the PR. Once eval is done, the results will be posted.
|
||||
**Evaluation Triggered**
|
||||
|
||||
**Trigger:** ${{ github.event_name == 'pull_request' && format('Pull Request #{0}', github.event.pull_request.number) || (github.event_name == 'release' && 'Release') || format('Manual Trigger: {0}', github.event.inputs.reason || 'No reason provided') }}
|
||||
**Branch:** ${{ steps.eval_params.outputs.eval_branch }}
|
||||
**Instances:** ${{ steps.eval_params.outputs.eval_instances }}
|
||||
**Commit:** ${{ github.sha }}
|
||||
|
||||
Running evaluation on the specified branch. Once eval is done, the results will be posted here.
|
||||
|
||||
@@ -15,8 +15,6 @@ make build && make run FRONTEND_PORT=12000 FRONTEND_HOST=0.0.0.0 BACKEND_HOST=0.
|
||||
|
||||
IMPORTANT: Before making any changes to the codebase, ALWAYS run `make install-pre-commit-hooks` to ensure pre-commit hooks are properly installed.
|
||||
|
||||
|
||||
|
||||
Before pushing any changes, you MUST ensure that any lint errors or simple test errors have been fixed.
|
||||
|
||||
* If you've made changes to the backend, you should run `pre-commit run --config ./dev_config/python/.pre-commit-config.yaml` (this will run on staged files).
|
||||
@@ -32,6 +30,12 @@ then re-run the command to ensure it passes. Common issues include:
|
||||
- Trailing whitespace
|
||||
- Missing newlines at end of files
|
||||
|
||||
## Git Best Practices
|
||||
|
||||
- Prefer specific `git add <filename>` instead of `git add .` to avoid accidentally staging unintended files
|
||||
- Be especially careful with `git reset --hard` after staging files, as it will remove accidentally staged files
|
||||
- When remote has new changes, use `git fetch upstream && git rebase upstream/<branch>` on the same branch
|
||||
|
||||
## Repository Structure
|
||||
Backend:
|
||||
- Located in the `openhands` directory
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Running OpenHands pre-commit hook..."
|
||||
echo "This hook runs 'make lint' to ensure code quality before committing."
|
||||
|
||||
# Store the exit code to return at the end
|
||||
# This allows us to be additive to existing pre-commit hooks
|
||||
EXIT_CODE=0
|
||||
|
||||
# Run make lint to check both frontend and backend code
|
||||
echo "Running linting checks with 'make lint'..."
|
||||
make lint
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Linting failed. Please fix the issues before committing."
|
||||
EXIT_CODE=1
|
||||
else
|
||||
echo "Linting checks passed!"
|
||||
fi
|
||||
|
||||
# Check if frontend directory has changed
|
||||
frontend_changes=$(git diff --cached --name-only | grep "^frontend/")
|
||||
if [ -n "$frontend_changes" ]; then
|
||||
echo "Frontend changes detected. Running frontend checks..."
|
||||
echo "Frontend changes detected. Running additional frontend checks..."
|
||||
|
||||
# Check if frontend directory exists
|
||||
if [ -d "frontend" ]; then
|
||||
# Change to frontend directory
|
||||
cd frontend || exit 1
|
||||
|
||||
# Run lint:fix
|
||||
echo "Running npm lint:fix..."
|
||||
npm run lint:fix
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Frontend linting failed. Please fix the issues before committing."
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# Run build
|
||||
echo "Running npm build..."
|
||||
npm run build
|
||||
@@ -50,7 +53,7 @@ if [ -n "$frontend_changes" ]; then
|
||||
echo "Frontend directory not found. Skipping frontend checks."
|
||||
fi
|
||||
else
|
||||
echo "No frontend changes detected. Skipping frontend checks."
|
||||
echo "No frontend changes detected. Skipping additional frontend checks."
|
||||
fi
|
||||
|
||||
# Run any existing pre-commit hooks that might have been installed by the user
|
||||
|
||||
@@ -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.49-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.50-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -62,17 +62,17 @@ system requirements and more information.
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
```
|
||||
|
||||
> **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.
|
||||
|
||||
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
```
|
||||
|
||||
> **注意**: 如果您在0.44版本之前使用过OpenHands,您可能需要运行 `mv ~/.openhands-state ~/.openhands` 来将对话历史迁移到新位置。
|
||||
|
||||
@@ -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.49-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
```
|
||||
|
||||
**注**: バージョン0.44以前のOpenHandsを使用していた場合は、会話履歴を移行するために `mv ~/.openhands-state ~/.openhands` を実行してください。
|
||||
|
||||
@@ -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.49-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.50-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -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.49-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.50-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:
|
||||
|
||||
@@ -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.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50 \
|
||||
python -m openhands.cli.main --override-cli-mode true
|
||||
```
|
||||
|
||||
@@ -153,6 +153,7 @@ You can use the following commands whenever the prompt (`>`) is displayed:
|
||||
| `/new` | Start a new conversation |
|
||||
| `/settings` | View and modify current LLM/agent settings |
|
||||
| `/resume` | Resume the agent if paused |
|
||||
| `/mcp` | Manage MCP server configuration and view connection errors |
|
||||
|
||||
#### Settings and Configuration
|
||||
|
||||
@@ -162,7 +163,7 @@ follow the prompts:
|
||||
- **Basic settings**: Choose a model/provider and enter your API key.
|
||||
- **Advanced settings**: Set custom endpoints, enable or disable confirmation mode, and configure memory condensation.
|
||||
|
||||
Settings can also be managed via the `config.toml` file.
|
||||
Settings can also be managed via the `config.toml` file in the current directory or `~/.openhands/config.toml`.
|
||||
|
||||
#### Repository Initialization
|
||||
|
||||
@@ -174,6 +175,41 @@ project details and structure. Use this when onboarding the agent to a new codeb
|
||||
You can pause the agent while it is running by pressing `Ctrl-P`. To continue the conversation after pausing, simply
|
||||
type `/resume` at the prompt.
|
||||
|
||||
#### MCP Server Management
|
||||
|
||||
To configure Model Context Protocol (MCP) servers, you can refer to the documentation on [MCP servers](../mcp) and use the `/mcp` command in the CLI. This command provides an interactive interface for managing Model Context Protocol (MCP) servers:
|
||||
|
||||
- **List configured servers**: View all currently configured MCP servers (SSE, Stdio, and SHTTP)
|
||||
- **Add new server**: Interactively add a new MCP server with guided prompts
|
||||
- **Remove server**: Remove an existing MCP server from your configuration
|
||||
- **View errors**: Display any connection errors that occurred during MCP server startup
|
||||
|
||||
This command modifies your `~/.openhands/config.toml` file and will prompt you to restart OpenHands for changes to take effect.
|
||||
|
||||
To enable the [Tavily MCP server](https://github.com/tavily-ai/tavily-mcp) search engine, you can set the `search_api_key` under the `[core]` section in the `~/.openhands/config.toml` file.
|
||||
|
||||
##### Example of the `config.toml` file with MCP server configuration:
|
||||
|
||||
```toml
|
||||
[core]
|
||||
search_api_key = "tvly-your-api-key-here"
|
||||
|
||||
[mcp]
|
||||
stdio_servers = [
|
||||
{name="fetch", command="uvx", args=["mcp-server-fetch"]},
|
||||
]
|
||||
|
||||
sse_servers = [
|
||||
# Basic SSE server with just a URL
|
||||
"http://example.com:8080/sse",
|
||||
]
|
||||
|
||||
shttp_servers = [
|
||||
# Streamable HTTP server with API key authentication
|
||||
{url="https://secure-example.com/mcp", api_key="your-api-key"}
|
||||
]
|
||||
```
|
||||
|
||||
## Tips and Troubleshooting
|
||||
|
||||
- Use `/help` at any time to see the list of available commands.
|
||||
|
||||
@@ -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.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -73,7 +73,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.49 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -39,6 +39,12 @@ limits and monitor usage.
|
||||
- [mistralai/devstral-small](https://www.all-hands.dev/blog/devstral-a-new-state-of-the-art-open-model-for-coding-agents) (20 May 2025) -- also available through [OpenRouter](https://openrouter.ai/mistralai/devstral-small:free)
|
||||
- [all-hands/openhands-lm-32b-v0.1](https://www.all-hands.dev/blog/introducing-openhands-lm-32b----a-strong-open-coding-agent-model) (31 March 2025) -- also available through [OpenRouter](https://openrouter.ai/all-hands/openhands-lm-32b-v0.1)
|
||||
|
||||
### Known Issues
|
||||
|
||||
<Warning>
|
||||
As of July 2025, there are known issues with Gemini 2.5 Pro conversations taking longer than normal with OpenHands. We are continuing to investigate.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Most current local and open source models are not as powerful. When using such models, you may see long
|
||||
wait times between messages, poor responses, or errors about malformed JSON. OpenHands can only be as powerful as the
|
||||
|
||||
@@ -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.49-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
```
|
||||
|
||||
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.49
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -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.49-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.50-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.49-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.50-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.49
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.50
|
||||
```
|
||||
|
||||
> **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.
|
||||
|
||||
@@ -10,6 +10,25 @@ Model Context Protocol (MCP) is a mechanism that allows OpenHands to communicate
|
||||
servers can provide additional functionality to the agent, such as specialized data processing, external API access,
|
||||
or custom tools. MCP is based on the open standard defined at [modelcontextprotocol.io](https://modelcontextprotocol.io).
|
||||
|
||||
<Note>
|
||||
MCP is currently not available on OpenHands Cloud. This feature is only available when running OpenHands locally.
|
||||
</Note>
|
||||
|
||||
### How MCP Works
|
||||
|
||||
When OpenHands starts, it:
|
||||
|
||||
1. Reads the MCP configuration.
|
||||
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.
|
||||
|
||||
The agent can then use these tools just like any built-in tool. When the agent calls an MCP tool:
|
||||
|
||||
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.
|
||||
|
||||
## Configuration
|
||||
|
||||
MCP configuration can be defined in:
|
||||
@@ -104,21 +123,6 @@ Stdio servers are configured using an object with the following properties:
|
||||
- Default: `{}`
|
||||
- Description: Environment variables to set for the server process
|
||||
|
||||
## How MCP Works
|
||||
|
||||
When OpenHands starts, it:
|
||||
|
||||
1. Reads the MCP configuration.
|
||||
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.
|
||||
|
||||
The agent can then use these tools just like any built-in tool. When the agent calls an MCP tool:
|
||||
|
||||
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:
|
||||
|
||||
@@ -117,6 +117,7 @@ def get_config(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
enable_browser=RUN_WITH_BROWSING,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
# do not mount workspace
|
||||
|
||||
@@ -345,6 +345,7 @@ def get_config(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
enable_browser=RUN_WITH_BROWSING,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
# do not mount workspace
|
||||
|
||||
@@ -226,6 +226,7 @@ def get_config(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
enable_browser=RUN_WITH_BROWSING,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
# do not mount workspace
|
||||
|
||||
@@ -203,6 +203,7 @@ def get_config(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
enable_browser=RUN_WITH_BROWSING,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
# do not mount workspace
|
||||
|
||||
@@ -164,6 +164,7 @@ def get_config(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
enable_browser=RUN_WITH_BROWSING,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
# do not mount workspace
|
||||
|
||||
@@ -19,7 +19,13 @@ describe("AuthModal", () => {
|
||||
});
|
||||
|
||||
it("should render the GitHub and GitLab buttons", () => {
|
||||
render(<AuthModal githubAuthUrl="mock-url" appMode="saas" />);
|
||||
render(
|
||||
<AuthModal
|
||||
githubAuthUrl="mock-url"
|
||||
appMode="saas"
|
||||
providersConfigured={["github", "gitlab"]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const githubButton = screen.getByRole("button", {
|
||||
name: "GITHUB$CONNECT_TO_GITHUB",
|
||||
@@ -35,7 +41,13 @@ describe("AuthModal", () => {
|
||||
it("should redirect to GitHub auth URL when GitHub button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const mockUrl = "https://github.com/login/oauth/authorize";
|
||||
render(<AuthModal githubAuthUrl={mockUrl} appMode="saas" />);
|
||||
render(
|
||||
<AuthModal
|
||||
githubAuthUrl={mockUrl}
|
||||
appMode="saas"
|
||||
providersConfigured={["github"]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const githubButton = screen.getByRole("button", {
|
||||
name: "GITHUB$CONNECT_TO_GITHUB",
|
||||
@@ -52,7 +64,6 @@ describe("AuthModal", () => {
|
||||
const termsSection = screen.getByTestId("auth-modal-terms-of-service");
|
||||
expect(termsSection).toBeInTheDocument();
|
||||
|
||||
|
||||
// Check that all text content is present in the paragraph
|
||||
expect(termsSection).toHaveTextContent(
|
||||
"AUTH$BY_SIGNING_UP_YOU_AGREE_TO_OUR",
|
||||
|
||||
@@ -16,8 +16,6 @@ import { ConversationCard } from "#/components/features/conversation-panel/conve
|
||||
import { clickOnEditButton } from "./utils";
|
||||
|
||||
// We'll use the actual i18next implementation but override the translation function
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import i18n from "i18next";
|
||||
|
||||
// Mock the t function to return our custom translations
|
||||
vi.mock("react-i18next", async () => {
|
||||
@@ -124,7 +122,8 @@ describe("ConversationCard", () => {
|
||||
|
||||
it("should toggle a context menu when clicking the ellipsis button", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(
|
||||
const onContextMenuToggle = vi.fn();
|
||||
const { rerender } = renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
onChangeTitle={onChangeTitle}
|
||||
@@ -132,6 +131,8 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen={false}
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -140,15 +141,32 @@ describe("ConversationCard", () => {
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
expect(onContextMenuToggle).toHaveBeenCalledWith(true);
|
||||
|
||||
// Simulate context menu being opened by parent
|
||||
rerender(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
onChangeTitle={onChangeTitle}
|
||||
isActive
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
screen.getByTestId("context-menu");
|
||||
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
expect(screen.queryByTestId("context-menu")).not.toBeInTheDocument();
|
||||
expect(onContextMenuToggle).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call onDelete when the delete button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -157,18 +175,18 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
const menu = screen.getByTestId("context-menu");
|
||||
const deleteButton = within(menu).getByTestId("delete-button");
|
||||
|
||||
await user.click(deleteButton);
|
||||
|
||||
expect(onDelete).toHaveBeenCalled();
|
||||
expect(onContextMenuToggle).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
test("clicking the selectedRepository should not trigger the onClick handler", async () => {
|
||||
@@ -198,7 +216,11 @@ describe("ConversationCard", () => {
|
||||
|
||||
test("conversation title should call onChangeTitle when changed and blurred", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(
|
||||
let menuOpen = true;
|
||||
const onContextMenuToggle = vi.fn((isOpen: boolean) => {
|
||||
menuOpen = isOpen;
|
||||
});
|
||||
const { rerender } = renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
isActive
|
||||
@@ -206,10 +228,27 @@ describe("ConversationCard", () => {
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
onChangeTitle={onChangeTitle}
|
||||
contextMenuOpen={menuOpen}
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
await clickOnEditButton(user);
|
||||
|
||||
// Re-render with updated state
|
||||
rerender(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
isActive
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
onChangeTitle={onChangeTitle}
|
||||
contextMenuOpen={menuOpen}
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const title = screen.getByTestId("conversation-card-title");
|
||||
|
||||
expect(title).toBeEnabled();
|
||||
@@ -227,6 +266,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
it("should reset title and not call onChangeTitle when the title is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -235,6 +275,8 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -271,6 +313,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
test("clicking the title should not trigger the onClick handler if edit mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -279,6 +322,8 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -292,6 +337,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
test("clicking the delete button should not trigger the onClick handler", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -300,12 +346,11 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
const menu = screen.getByTestId("context-menu");
|
||||
const deleteButton = within(menu).getByTestId("delete-button");
|
||||
|
||||
@@ -315,7 +360,7 @@ describe("ConversationCard", () => {
|
||||
});
|
||||
|
||||
it("should show display cost button only when showOptions is true", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
const { rerender } = renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -324,21 +369,17 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
// Wait for context menu to appear
|
||||
const menu = await screen.findByTestId("context-menu");
|
||||
expect(
|
||||
within(menu).queryByTestId("display-cost-button"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Close menu
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
rerender(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -348,12 +389,11 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Open menu again
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
// Wait for context menu to appear and check for display cost button
|
||||
const newMenu = await screen.findByTestId("context-menu");
|
||||
within(newMenu).getByTestId("display-cost-button");
|
||||
@@ -361,6 +401,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
it("should show metrics modal when clicking the display cost button", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
renderWithProviders(
|
||||
<ConversationCard
|
||||
onDelete={onDelete}
|
||||
@@ -370,12 +411,11 @@ describe("ConversationCard", () => {
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
showOptions
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
const menu = screen.getByTestId("context-menu");
|
||||
const displayCostButton = within(menu).getByTestId("display-cost-button");
|
||||
|
||||
@@ -386,7 +426,7 @@ describe("ConversationCard", () => {
|
||||
});
|
||||
|
||||
it("should not display the edit or delete options if the handler is not provided", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onContextMenuToggle = vi.fn();
|
||||
const { rerender } = renderWithProviders(
|
||||
<ConversationCard
|
||||
onClick={onClick}
|
||||
@@ -394,19 +434,15 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
const ellipsisButton = screen.getByTestId("ellipsis-button");
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
const menu = await screen.findByTestId("context-menu");
|
||||
expect(within(menu).queryByTestId("edit-button")).toBeInTheDocument();
|
||||
expect(within(menu).queryByTestId("delete-button")).not.toBeInTheDocument();
|
||||
|
||||
// toggle to hide the context menu
|
||||
await user.click(ellipsisButton);
|
||||
|
||||
rerender(
|
||||
<ConversationCard
|
||||
onClick={onClick}
|
||||
@@ -414,10 +450,11 @@ describe("ConversationCard", () => {
|
||||
title="Conversation 1"
|
||||
selectedRepository={null}
|
||||
lastUpdatedAt="2021-10-01T12:00:00Z"
|
||||
contextMenuOpen
|
||||
onContextMenuToggle={onContextMenuToggle}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(ellipsisButton);
|
||||
const newMenu = await screen.findByTestId("context-menu");
|
||||
expect(
|
||||
within(newMenu).queryByTestId("edit-button"),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
413
frontend/package-lock.json
generated
413
frontend/package-lock.json
generated
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.49.0",
|
||||
"version": "0.50.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.49.0",
|
||||
"version": "0.50.0",
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.1",
|
||||
"@microlink/react-json-view": "^1.26.2",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.7.0",
|
||||
"@react-router/serve": "^7.7.0",
|
||||
"@react-types/shared": "^3.29.1",
|
||||
"@react-router/node": "^7.7.1",
|
||||
"@react-router/serve": "^7.7.1",
|
||||
"@react-types/shared": "^3.31.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.5.0",
|
||||
"@stripe/react-stripe-js": "^3.8.0",
|
||||
"@stripe/stripe-js": "^7.6.1",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"axios": "^1.10.0",
|
||||
"axios": "^1.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.23.6",
|
||||
"framer-motion": "^12.23.9",
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
@@ -34,16 +34,16 @@
|
||||
"jose": "^6.0.12",
|
||||
"lucide-react": "^0.525.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.257.0",
|
||||
"posthog-js": "^1.258.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-hot-toast": "^2.5.1",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.7.0",
|
||||
"react-router": "^7.7.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"remark-breaks": "^4.0.0",
|
||||
@@ -51,24 +51,24 @@
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.0.5",
|
||||
"vite": "^7.0.6",
|
||||
"web-vitals": "^5.0.3",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@babel/types": "^7.28.1",
|
||||
"@babel/types": "^7.28.2",
|
||||
"@mswjs/socket.io-binding": "^0.2.0",
|
||||
"@playwright/test": "^1.54.1",
|
||||
"@react-router/dev": "^7.7.0",
|
||||
"@react-router/dev": "^7.7.1",
|
||||
"@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.14",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -82,11 +82,11 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-i18next": "^6.1.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-i18next": "^6.1.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
@@ -630,9 +630,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
|
||||
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
|
||||
"version": "7.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
|
||||
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1"
|
||||
@@ -1399,6 +1399,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/accordion/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/alert": {
|
||||
"version": "2.2.23",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/alert/-/alert-2.2.23.tgz",
|
||||
@@ -1433,6 +1441,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/aria-utils/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/autocomplete": {
|
||||
"version": "2.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/autocomplete/-/autocomplete-2.3.25.tgz",
|
||||
@@ -1463,6 +1479,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/autocomplete/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/avatar": {
|
||||
"version": "2.2.19",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/avatar/-/avatar-2.2.19.tgz",
|
||||
@@ -1537,6 +1561,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/button/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/calendar": {
|
||||
"version": "2.2.23",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/calendar/-/calendar-2.2.23.tgz",
|
||||
@@ -1570,6 +1602,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/calendar/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/card": {
|
||||
"version": "2.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/card/-/card-2.2.22.tgz",
|
||||
@@ -1591,6 +1631,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/card/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/checkbox": {
|
||||
"version": "2.3.23",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/checkbox/-/checkbox-2.3.23.tgz",
|
||||
@@ -1616,6 +1664,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/checkbox/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/chip": {
|
||||
"version": "2.2.19",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/chip/-/chip-2.2.19.tgz",
|
||||
@@ -1671,6 +1727,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/date-input/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/date-picker": {
|
||||
"version": "2.3.24",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/date-picker/-/date-picker-2.3.24.tgz",
|
||||
@@ -1701,6 +1765,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/date-picker/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/divider": {
|
||||
"version": "2.2.16",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/divider/-/divider-2.2.16.tgz",
|
||||
@@ -1716,6 +1788,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/divider/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/dom-animation": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/dom-animation/-/dom-animation-2.1.10.tgz",
|
||||
@@ -1783,6 +1863,14 @@
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/form/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/framer-utils": {
|
||||
"version": "2.1.19",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/framer-utils/-/framer-utils-2.1.19.tgz",
|
||||
@@ -1861,6 +1949,14 @@
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/input/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/kbd": {
|
||||
"version": "2.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/kbd/-/kbd-2.2.18.tgz",
|
||||
@@ -1919,6 +2015,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/listbox/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/menu": {
|
||||
"version": "2.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/menu/-/menu-2.2.22.tgz",
|
||||
@@ -1943,6 +2047,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/menu/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/modal": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/modal/-/modal-2.2.20.tgz",
|
||||
@@ -2024,6 +2136,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/number-input/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/pagination": {
|
||||
"version": "2.2.21",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/pagination/-/pagination-2.2.21.tgz",
|
||||
@@ -2116,6 +2236,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/radio/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/react": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/react/-/react-2.8.1.tgz",
|
||||
@@ -2263,6 +2391,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/select/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/shared-icons": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/shared-icons/-/shared-icons-2.1.10.tgz",
|
||||
@@ -2415,6 +2551,14 @@
|
||||
"react": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/system-rsc/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/system-rsc/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
@@ -2474,6 +2618,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/tabs/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/theme": {
|
||||
"version": "2.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.19.tgz",
|
||||
@@ -2573,6 +2725,14 @@
|
||||
"react": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-accordion/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-button": {
|
||||
"version": "2.2.17",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/use-aria-button/-/use-aria-button-2.2.17.tgz",
|
||||
@@ -2588,6 +2748,14 @@
|
||||
"react": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-button/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-link": {
|
||||
"version": "2.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/use-aria-link/-/use-aria-link-2.2.18.tgz",
|
||||
@@ -2603,6 +2771,14 @@
|
||||
"react": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-link/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-modal-overlay": {
|
||||
"version": "2.2.16",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/use-aria-modal-overlay/-/use-aria-modal-overlay-2.2.16.tgz",
|
||||
@@ -2642,6 +2818,14 @@
|
||||
"react-dom": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-multiselect/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-overlay": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/use-aria-overlay/-/use-aria-overlay-2.0.1.tgz",
|
||||
@@ -2657,6 +2841,14 @@
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-aria-overlay/node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/use-callback-ref": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/use-callback-ref/-/use-callback-ref-2.1.8.tgz",
|
||||
@@ -4103,9 +4295,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/dev": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.7.0.tgz",
|
||||
"integrity": "sha512-z6tJ0US20pS/YpaPz59SJgSH+1BJ6xvQmQ/u4Y4HM1uLOa4b3Mleg3KujqAvwGP5wkMkNFz3Ae2g6/kDTFyuCA==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.7.1.tgz",
|
||||
"integrity": "sha512-ByfgHmAyfx/JQYN/QwUx1sFJlBA5Z3HQAZ638wHSb+m6khWtHqSaKCvPqQh1P00wdEAeV3tX5L1aUM/ceCF6+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.27.7",
|
||||
@@ -4116,7 +4308,7 @@
|
||||
"@babel/traverse": "^7.27.7",
|
||||
"@babel/types": "^7.27.7",
|
||||
"@npmcli/package-json": "^4.0.1",
|
||||
"@react-router/node": "7.7.0",
|
||||
"@react-router/node": "7.7.1",
|
||||
"arg": "^5.0.1",
|
||||
"babel-dead-code-elimination": "^1.0.6",
|
||||
"chokidar": "^4.0.0",
|
||||
@@ -4128,7 +4320,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"pathe": "^1.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier": "^3.6.2",
|
||||
"react-refresh": "^0.14.0",
|
||||
"semver": "^7.3.7",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
@@ -4143,8 +4335,8 @@
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-router/serve": "^7.7.0",
|
||||
"react-router": "^7.7.0",
|
||||
"@react-router/serve": "^7.7.1",
|
||||
"react-router": "^7.7.1",
|
||||
"typescript": "^5.1.0",
|
||||
"vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
|
||||
"wrangler": "^3.28.2 || ^4.0.0"
|
||||
@@ -4174,26 +4366,10 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/dev/node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/node": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.7.0.tgz",
|
||||
"integrity": "sha512-PTl4C+QjWsbTfp+9mybOzIH10ZM/pjZrAlcoxc/KGYxcfWDEe2GDFFBQN6nGZgJe/0SwSjHsVwqo2haMKgTbvQ==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.7.1.tgz",
|
||||
"integrity": "sha512-EHd6PEcw2nmcJmcYTPA0MmRWSqOaJ/meycfCp0ADA9T/6b7+fUHfr9XcNyf7UeZtYwu4zGyuYfPmLU5ic6Ugyg==",
|
||||
"dependencies": {
|
||||
"@mjackson/node-fetch-server": "^0.2.0"
|
||||
},
|
||||
@@ -4201,7 +4377,7 @@
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-router": "7.7.0",
|
||||
"react-router": "7.7.1",
|
||||
"typescript": "^5.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -4211,12 +4387,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/serve": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.7.0.tgz",
|
||||
"integrity": "sha512-XvJAY4Sgv7HxdSuLgkBP8bFXxfI97HJSk+p2BisdtK6JT/nSZugEe0gju4xAkgtsncNJJBVndJTcfUtTDNLTUQ==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.7.1.tgz",
|
||||
"integrity": "sha512-LyAiX+oI+6O6j2xWPUoKW+cgayUf3USBosSMv73Jtwi99XUhSDu2MUhM+BB+AbrYRubauZ83QpZTROiXoaf8jA==",
|
||||
"dependencies": {
|
||||
"@react-router/express": "7.7.0",
|
||||
"@react-router/node": "7.7.0",
|
||||
"@react-router/express": "7.7.1",
|
||||
"@react-router/node": "7.7.1",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.19.2",
|
||||
"get-port": "5.1.1",
|
||||
@@ -4230,22 +4406,22 @@
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-router": "7.7.0"
|
||||
"react-router": "7.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/serve/node_modules/@react-router/express": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.7.0.tgz",
|
||||
"integrity": "sha512-R86v1qAbj3i/tG00gFM90P3nXR+B66qkp3bbaqm9VnTkbkqUCcHnVaQn64qBOl5g34FdJUMt84UsLS6v2mT/iQ==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.7.1.tgz",
|
||||
"integrity": "sha512-OEZwIM7i/KPSDjwVRg3LqeNIwG41U+SeFOwMjhZRFfyrnwghHfvWsDajf73r4ccMh+RRHcP1GIN6VSU3XZk7MA==",
|
||||
"dependencies": {
|
||||
"@react-router/node": "7.7.0"
|
||||
"@react-router/node": "7.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.17.1 || ^5",
|
||||
"react-router": "7.7.0",
|
||||
"react-router": "7.7.1",
|
||||
"typescript": "^5.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -4802,10 +4978,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-types/shared": {
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"license": "Apache-2.0",
|
||||
"version": "3.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.31.0.tgz",
|
||||
"integrity": "sha512-ua5U6V66gDcbLZe4P2QeyNgPp4YWD1ymGA6j3n+s8CGExtrCPe64v+g4mvpT8Bnb985R96e4zFT61+m0YCwqMg==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
@@ -5238,10 +5413,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@stripe/react-stripe-js": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.7.0.tgz",
|
||||
"integrity": "sha512-PYls/2S9l0FF+2n0wHaEJsEU8x7CmBagiH7zYOsxbBlLIHEsqUIQ4MlIAbV9Zg6xwT8jlYdlRIyBTHmO3yM7kQ==",
|
||||
"license": "MIT",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.8.0.tgz",
|
||||
"integrity": "sha512-yd130NpwvJpXK+Tleg5P3iYcg/pwFmPDMbrpBOi++S0h/dQtztsCktJswdAcaBMW143V7mj4PBhBANePP5nAzA==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
@@ -5252,9 +5426,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.5.0.tgz",
|
||||
"integrity": "sha512-Cq3KKe+G1o7PSBMbmrgpT2JgBeyH2THHr3RdIX2MqF7AnBuspIMgtZ3ktcCgP7kZsTMvnmWymr7zZCT1zeWbMw==",
|
||||
"version": "7.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.6.1.tgz",
|
||||
"integrity": "sha512-BUDj5gujbtx53/Cexws0+aPrEBsKAN8ExPf9UfuTCivVU6ug2PjqI0zUeL1jon3795eOLlyqvCDjp6VNknjE0A==",
|
||||
"engines": {
|
||||
"node": ">=12.16"
|
||||
}
|
||||
@@ -6160,9 +6334,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz",
|
||||
"integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==",
|
||||
"version": "24.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
|
||||
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
@@ -7213,8 +7387,7 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.21",
|
||||
@@ -7281,13 +7454,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||
"license": "MIT",
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
|
||||
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@@ -7908,7 +8080,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@@ -8375,7 +8546,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -9017,11 +9187,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "10.1.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz",
|
||||
"integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==",
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -9083,11 +9252,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-i18next": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-6.1.2.tgz",
|
||||
"integrity": "sha512-hvTmws4kouNHkk314+9MHNj+RQmsqrkejWhTXGlRC0j8H+EXq2qDRLe6UqIjrFZo7/ogyd4btuqsnKCBi8wHbw==",
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-6.1.3.tgz",
|
||||
"integrity": "sha512-z/h4oBRd9wI1ET60HqcLSU6XPeAh/EPOrBBTyCdkWeMoYrWAaUVA+DOQkWTiNIyCltG4NTmy62SQisVXxoXurw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"requireindex": "~1.1.0"
|
||||
@@ -9252,11 +9420,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz",
|
||||
"integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==",
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz",
|
||||
"integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
"synckit": "^0.11.7"
|
||||
@@ -9893,10 +10060,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
|
||||
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
|
||||
"license": "MIT",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
@@ -9939,11 +10105,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.6.tgz",
|
||||
"integrity": "sha512-dsJ389QImVE3lQvM8Mnk99/j8tiZDM/7706PCqvkQ8sSCnpmWxsgX+g0lj7r5OBVL0U36pIecCTBoIWcM2RuKw==",
|
||||
"version": "12.23.9",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.9.tgz",
|
||||
"integrity": "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.6",
|
||||
"motion-dom": "^12.23.9",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
@@ -13414,9 +13580,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.6.tgz",
|
||||
"integrity": "sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==",
|
||||
"version": "12.23.9",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.9.tgz",
|
||||
"integrity": "sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
@@ -14268,10 +14434,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/posthog-js": {
|
||||
"version": "1.257.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.257.0.tgz",
|
||||
"integrity": "sha512-Ujg9RGtWVCu+4tmlRpALSy2ZOZI6JtieSYXIDDdgMWm167KYKvTtbMPHdoBaPWcNu0Km+1hAIBnQFygyn30KhA==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"version": "1.258.2",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.258.2.tgz",
|
||||
"integrity": "sha512-XBSeiN4HjiYsy3tW5zss8WOJF2JXTQXAYw2wZ+zjqQuzzi7kkLEXjIgsVrBnt5Opwhqn0krZVsb0ZBw34dIiyQ==",
|
||||
"dependencies": {
|
||||
"core-js": "^3.38.1",
|
||||
"fflate": "^0.4.8",
|
||||
@@ -14635,10 +14800,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.0.tgz",
|
||||
"integrity": "sha512-W135dB0rDfiFmbMipC17nOhGdttO5mzH8BivY+2ybsQBbXvxWIwl3cmeH3T9d+YPBSJu/ouyJKFJTtkK7rJofw==",
|
||||
"license": "MIT",
|
||||
"version": "15.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.1.tgz",
|
||||
"integrity": "sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
@@ -14743,9 +14907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz",
|
||||
"integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.1.tgz",
|
||||
"integrity": "sha512-jVKHXoWRIsD/qS6lvGveckwb862EekvapdHJN/cGmzw40KnJH5gg53ujOJ4qX6EKIK9LSBfFed/xiQ5yeXNrUA==",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0"
|
||||
@@ -17193,13 +17357,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
|
||||
"integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
|
||||
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.6",
|
||||
"picomatch": "^4.0.2",
|
||||
"picomatch": "^4.0.3",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.40.0",
|
||||
"tinyglobby": "^0.2.14"
|
||||
@@ -17359,10 +17523,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"license": "MIT",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.49.0",
|
||||
"version": "0.50.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
@@ -10,22 +10,22 @@
|
||||
"@heroui/react": "^2.8.1",
|
||||
"@microlink/react-json-view": "^1.26.2",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.7.0",
|
||||
"@react-router/serve": "^7.7.0",
|
||||
"@react-types/shared": "^3.29.1",
|
||||
"@react-router/node": "^7.7.1",
|
||||
"@react-router/serve": "^7.7.1",
|
||||
"@react-types/shared": "^3.31.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.5.0",
|
||||
"@stripe/react-stripe-js": "^3.8.0",
|
||||
"@stripe/stripe-js": "^7.6.1",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"axios": "^1.10.0",
|
||||
"axios": "^1.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.23.6",
|
||||
"framer-motion": "^12.23.9",
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
@@ -33,16 +33,16 @@
|
||||
"jose": "^6.0.12",
|
||||
"lucide-react": "^0.525.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.257.0",
|
||||
"posthog-js": "^1.258.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-hot-toast": "^2.5.1",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.7.0",
|
||||
"react-router": "^7.7.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"remark-breaks": "^4.0.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.0.5",
|
||||
"vite": "^7.0.6",
|
||||
"web-vitals": "^5.0.3",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
@@ -82,17 +82,17 @@
|
||||
"devDependencies": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@babel/types": "^7.28.1",
|
||||
"@babel/types": "^7.28.2",
|
||||
"@mswjs/socket.io-binding": "^0.2.0",
|
||||
"@playwright/test": "^1.54.1",
|
||||
"@react-router/dev": "^7.7.0",
|
||||
"@react-router/dev": "^7.7.1",
|
||||
"@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.14",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -106,11 +106,11 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-i18next": "^6.1.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-i18next": "^6.1.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
|
||||
@@ -13,11 +13,13 @@ import {
|
||||
GitChange,
|
||||
GetMicroagentsResponse,
|
||||
GetMicroagentPromptResponse,
|
||||
CreateMicroagent,
|
||||
} from "./open-hands.types";
|
||||
import { openHands } from "./open-hands-axios";
|
||||
import { ApiSettings, PostApiSettings, Provider } from "#/types/settings";
|
||||
import { GitUser, GitRepository, Branch } from "#/types/git";
|
||||
import { SuggestedTask } from "#/components/features/home/tasks/task.types";
|
||||
import { RepositoryMicroagent } from "#/types/microagent-management";
|
||||
|
||||
class OpenHands {
|
||||
private static currentConversation: Conversation | null = null;
|
||||
@@ -250,6 +252,28 @@ class OpenHands {
|
||||
return data.results;
|
||||
}
|
||||
|
||||
static async searchConversations(
|
||||
selectedRepository?: string,
|
||||
conversationTrigger?: string,
|
||||
limit: number = 20,
|
||||
): Promise<Conversation[]> {
|
||||
const params = new URLSearchParams();
|
||||
params.append("limit", limit.toString());
|
||||
|
||||
if (selectedRepository) {
|
||||
params.append("selected_repository", selectedRepository);
|
||||
}
|
||||
|
||||
if (conversationTrigger) {
|
||||
params.append("conversation_trigger", conversationTrigger);
|
||||
}
|
||||
|
||||
const { data } = await openHands.get<ResultSet<Conversation>>(
|
||||
`/api/conversations?${params.toString()}`,
|
||||
);
|
||||
return data.results;
|
||||
}
|
||||
|
||||
static async deleteUserConversation(conversationId: string): Promise<void> {
|
||||
await openHands.delete(`/api/conversations/${conversationId}`);
|
||||
}
|
||||
@@ -261,6 +285,7 @@ class OpenHands {
|
||||
suggested_task?: SuggestedTask,
|
||||
selected_branch?: string,
|
||||
conversationInstructions?: string,
|
||||
createMicroagent?: CreateMicroagent,
|
||||
): Promise<Conversation> {
|
||||
const body = {
|
||||
repository: selectedRepository,
|
||||
@@ -269,6 +294,7 @@ class OpenHands {
|
||||
initial_user_msg: initialUserMsg,
|
||||
suggested_task,
|
||||
conversation_instructions: conversationInstructions,
|
||||
create_microagent: createMicroagent,
|
||||
};
|
||||
|
||||
const { data } = await openHands.post<Conversation>(
|
||||
@@ -464,6 +490,22 @@ class OpenHands {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available microagents for a specific repository
|
||||
* @param owner The repository owner
|
||||
* @param repo The repository name
|
||||
* @returns The available microagents for the repository
|
||||
*/
|
||||
static async getRepositoryMicroagents(
|
||||
owner: string,
|
||||
repo: string,
|
||||
): Promise<RepositoryMicroagent[]> {
|
||||
const { data } = await openHands.get<RepositoryMicroagent[]>(
|
||||
`/api/user/repository/${owner}/${repo}/microagents`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
static async getMicroagentPrompt(
|
||||
conversationId: string,
|
||||
eventId: number,
|
||||
@@ -489,24 +531,6 @@ class OpenHands {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GitHub user installation IDs
|
||||
* @returns List of GitHub installation IDs
|
||||
*/
|
||||
static async getGitHubUserInstallationIds(): Promise<string[]> {
|
||||
const { data } = await openHands.get<string[]>("/github/installations");
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the BitBucket workspaces
|
||||
* @returns List of BitBucket workspaces
|
||||
*/
|
||||
static async getBitBucketWorkspaces(): Promise<string[]> {
|
||||
const { data } = await openHands.get<string[]>("/bitbucket/installations");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenHands;
|
||||
|
||||
@@ -79,7 +79,11 @@ export interface RepositorySelection {
|
||||
git_provider: Provider | null;
|
||||
}
|
||||
|
||||
export type ConversationTrigger = "resolver" | "gui" | "suggested_task";
|
||||
export type ConversationTrigger =
|
||||
| "resolver"
|
||||
| "gui"
|
||||
| "suggested_task"
|
||||
| "microagent_management";
|
||||
|
||||
export interface Conversation {
|
||||
conversation_id: string;
|
||||
@@ -94,6 +98,7 @@ export interface Conversation {
|
||||
trigger?: ConversationTrigger;
|
||||
url: string | null;
|
||||
session_api_key: string | null;
|
||||
pr_number?: number[] | null;
|
||||
}
|
||||
|
||||
export interface ResultSet<T> {
|
||||
@@ -133,3 +138,9 @@ export interface GetMicroagentPromptResponse {
|
||||
status: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export interface CreateMicroagent {
|
||||
repo: string;
|
||||
git_provider?: Provider;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ interface ControlsProps {
|
||||
|
||||
export function Controls({ setSecurityOpen, showSecurityLock }: ControlsProps) {
|
||||
const { data: conversation } = useActiveConversation();
|
||||
const [contextMenuOpen, setContextMenuOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 md:items-center md:justify-between md:flex-row">
|
||||
@@ -37,6 +38,8 @@ export function Controls({ setSecurityOpen, showSecurityLock }: ControlsProps) {
|
||||
}}
|
||||
conversationStatus={conversation?.status}
|
||||
conversationId={conversation?.conversation_id}
|
||||
contextMenuOpen={contextMenuOpen}
|
||||
onContextMenuToggle={setContextMenuOpen}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -35,6 +35,8 @@ interface ConversationCardProps {
|
||||
conversationStatus?: ConversationStatus;
|
||||
variant?: "compact" | "default";
|
||||
conversationId?: string; // Optional conversation ID for VS Code URL
|
||||
contextMenuOpen?: boolean;
|
||||
onContextMenuToggle?: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
const MAX_TIME_BETWEEN_CREATION_AND_UPDATE = 1000 * 60 * 30; // 30 minutes
|
||||
@@ -55,10 +57,11 @@ export function ConversationCard({
|
||||
conversationStatus = "STOPPED",
|
||||
variant = "default",
|
||||
conversationId,
|
||||
contextMenuOpen = false,
|
||||
onContextMenuToggle,
|
||||
}: ConversationCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const { parsedEvents } = useWsClient();
|
||||
const [contextMenuVisible, setContextMenuVisible] = React.useState(false);
|
||||
const [titleMode, setTitleMode] = React.useState<"view" | "edit">("view");
|
||||
const [metricsModalVisible, setMetricsModalVisible] = React.useState(false);
|
||||
const [systemModalVisible, setSystemModalVisible] = React.useState(false);
|
||||
@@ -101,21 +104,21 @@ export function ConversationCard({
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onDelete?.();
|
||||
setContextMenuVisible(false);
|
||||
onContextMenuToggle?.(false);
|
||||
};
|
||||
|
||||
const handleStop = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onStop?.();
|
||||
setContextMenuVisible(false);
|
||||
onContextMenuToggle?.(false);
|
||||
};
|
||||
|
||||
const handleEdit = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setTitleMode("edit");
|
||||
setContextMenuVisible(false);
|
||||
onContextMenuToggle?.(false);
|
||||
};
|
||||
|
||||
const handleDownloadViaVSCode = async (
|
||||
@@ -141,7 +144,7 @@ export function ConversationCard({
|
||||
}
|
||||
}
|
||||
|
||||
setContextMenuVisible(false);
|
||||
onContextMenuToggle?.(false);
|
||||
};
|
||||
|
||||
const handleDisplayCost = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
@@ -224,15 +227,15 @@ export function ConversationCard({
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setContextMenuVisible((prev) => !prev);
|
||||
onContextMenuToggle?.(!contextMenuOpen);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
{contextMenuVisible && (
|
||||
{contextMenuOpen && (
|
||||
<ConversationCardContextMenu
|
||||
onClose={() => setContextMenuVisible(false)}
|
||||
onClose={() => onContextMenuToggle?.(false)}
|
||||
onDelete={onDelete && handleDelete}
|
||||
onStop={
|
||||
conversationStatus !== "STOPPED"
|
||||
|
||||
@@ -36,6 +36,9 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
const [selectedConversationId, setSelectedConversationId] = React.useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [openContextMenuId, setOpenContextMenuId] = React.useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
const { data: conversations, isFetching, error } = useUserConversations();
|
||||
|
||||
@@ -144,6 +147,10 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
createdAt={project.created_at}
|
||||
conversationStatus={project.status}
|
||||
conversationId={project.conversation_id}
|
||||
contextMenuOpen={openContextMenuId === project.conversation_id}
|
||||
onContextMenuToggle={(isOpen) =>
|
||||
setOpenContextMenuId(isOpen ? project.conversation_id : null)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</NavLink>
|
||||
|
||||
@@ -10,9 +10,6 @@ import { BrandButton } from "../settings/brand-button";
|
||||
import { useSearchRepositories } from "#/hooks/query/use-search-repositories";
|
||||
import { useDebounce } from "#/hooks/use-debounce";
|
||||
import { sanitizeQuery } from "#/utils/sanitize-query";
|
||||
import { useUserProviders } from "#/hooks/use-user-providers";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { SettingsDropdownInput } from "../settings/settings-dropdown-input";
|
||||
import {
|
||||
RepositoryDropdown,
|
||||
RepositoryLoadingState,
|
||||
@@ -35,10 +32,8 @@ export function RepositorySelectionForm({
|
||||
const [selectedBranch, setSelectedBranch] = React.useState<Branch | null>(
|
||||
null,
|
||||
);
|
||||
const [selectedProvider, setSelectedProvider] = React.useState<Provider | null>(null);
|
||||
// Add a ref to track if the branch was manually cleared by the user
|
||||
const branchManuallyClearedRef = React.useRef<boolean>(false);
|
||||
const { providers } = useUserProviders();
|
||||
const {
|
||||
data: repositories,
|
||||
isLoading: isLoadingRepositories,
|
||||
@@ -61,13 +56,6 @@ export function RepositorySelectionForm({
|
||||
const debouncedSearchQuery = useDebounce(searchQuery, 300);
|
||||
const { data: searchedRepos } = useSearchRepositories(debouncedSearchQuery);
|
||||
|
||||
// Auto-select provider if there's only one
|
||||
React.useEffect(() => {
|
||||
if (providers.length === 1 && !selectedProvider) {
|
||||
setSelectedProvider(providers[0]);
|
||||
}
|
||||
}, [providers, selectedProvider]);
|
||||
|
||||
// Auto-select main or master branch if it exists, but only if the branch wasn't manually cleared
|
||||
React.useEffect(() => {
|
||||
if (
|
||||
@@ -95,10 +83,8 @@ export function RepositorySelectionForm({
|
||||
const isCreatingConversation =
|
||||
isPending || isSuccess || isCreatingConversationElsewhere;
|
||||
|
||||
// Use all repositories without filtering by provider for now
|
||||
const allRepositories = repositories?.concat(searchedRepos || []);
|
||||
|
||||
const repositoriesItems = (allRepositories || []).map((repo) => ({
|
||||
const repositoriesItems = allRepositories?.map((repo) => ({
|
||||
key: repo.id,
|
||||
label: decodeURIComponent(repo.full_name),
|
||||
}));
|
||||
@@ -108,14 +94,6 @@ export function RepositorySelectionForm({
|
||||
label: branch.name,
|
||||
}));
|
||||
|
||||
// Create provider dropdown items
|
||||
const providerItems = React.useMemo(() => {
|
||||
return providers.map(provider => ({
|
||||
key: provider,
|
||||
label: provider.charAt(0).toUpperCase() + provider.slice(1), // Capitalize first letter
|
||||
}));
|
||||
}, [providers]);
|
||||
|
||||
const handleRepoSelection = (key: React.Key | null) => {
|
||||
const selectedRepo = allRepositories?.find((repo) => repo.id === key);
|
||||
if (selectedRepo) onRepoSelection(selectedRepo);
|
||||
@@ -124,14 +102,6 @@ export function RepositorySelectionForm({
|
||||
branchManuallyClearedRef.current = false; // Reset the flag when repo changes
|
||||
};
|
||||
|
||||
const handleProviderSelection = (key: React.Key | null) => {
|
||||
const provider = key as Provider | null;
|
||||
setSelectedProvider(provider);
|
||||
setSelectedRepository(null); // Reset repository selection when provider changes
|
||||
setSelectedBranch(null); // Reset branch selection when provider changes
|
||||
onRepoSelection(null); // Reset parent component's selected repo
|
||||
};
|
||||
|
||||
const handleBranchSelection = (key: React.Key | null) => {
|
||||
const selectedBranchObj = branches?.find((branch) => branch.name === key);
|
||||
setSelectedBranch(selectedBranchObj || null);
|
||||
@@ -163,26 +133,6 @@ export function RepositorySelectionForm({
|
||||
}
|
||||
};
|
||||
|
||||
// Render the provider dropdown
|
||||
const renderProviderSelector = () => {
|
||||
// Only render if there are multiple providers
|
||||
if (providers.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsDropdownInput
|
||||
testId="provider-dropdown"
|
||||
name="provider-dropdown"
|
||||
placeholder="Select Provider"
|
||||
items={providerItems}
|
||||
wrapperClassName="max-w-[500px]"
|
||||
onSelectionChange={handleProviderSelection}
|
||||
selectedKey={selectedProvider || undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Render the appropriate UI based on the loading/error state
|
||||
const renderRepositorySelector = () => {
|
||||
if (isLoadingRepositories) {
|
||||
@@ -193,15 +143,11 @@ export function RepositorySelectionForm({
|
||||
return <RepositoryErrorState />;
|
||||
}
|
||||
|
||||
// For now, don't disable the repo dropdown based on provider selection
|
||||
const isDisabled = false;
|
||||
|
||||
return (
|
||||
<RepositoryDropdown
|
||||
items={repositoriesItems || []}
|
||||
onSelectionChange={handleRepoSelection}
|
||||
onInputChange={handleRepoInputChange}
|
||||
isDisabled={isDisabled}
|
||||
defaultFilter={(textValue, inputValue) => {
|
||||
if (!inputValue) return true;
|
||||
|
||||
@@ -249,8 +195,8 @@ export function RepositorySelectionForm({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{renderProviderSelector()}
|
||||
{renderRepositorySelector()}
|
||||
|
||||
{renderBranchSelector()}
|
||||
|
||||
<BrandButton
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import React, { ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SettingsDropdownInput } from "../../settings/settings-dropdown-input";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
export interface BranchDropdownProps {
|
||||
items: { key: React.Key; label: string }[];
|
||||
@@ -9,6 +10,8 @@ export interface BranchDropdownProps {
|
||||
onInputChange: (value: string) => void;
|
||||
isDisabled: boolean;
|
||||
selectedKey?: string;
|
||||
wrapperClassName?: string;
|
||||
label?: ReactNode;
|
||||
}
|
||||
|
||||
export function BranchDropdown({
|
||||
@@ -17,6 +20,8 @@ export function BranchDropdown({
|
||||
onInputChange,
|
||||
isDisabled,
|
||||
selectedKey,
|
||||
wrapperClassName,
|
||||
label,
|
||||
}: BranchDropdownProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -26,11 +31,12 @@ export function BranchDropdown({
|
||||
name="branch-dropdown"
|
||||
placeholder={t(I18nKey.REPOSITORY$SELECT_BRANCH)}
|
||||
items={items}
|
||||
wrapperClassName="max-w-[500px]"
|
||||
wrapperClassName={cn("max-w-[500px]", wrapperClassName)}
|
||||
onSelectionChange={onSelectionChange}
|
||||
onInputChange={onInputChange}
|
||||
isDisabled={isDisabled}
|
||||
selectedKey={selectedKey}
|
||||
label={label}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
export function BranchErrorState() {
|
||||
interface BranchErrorStateProps {
|
||||
wrapperClassName?: string;
|
||||
}
|
||||
|
||||
export function BranchErrorState({ wrapperClassName }: BranchErrorStateProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
data-testid="branch-dropdown-error"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm text-red-500"
|
||||
className={cn(
|
||||
"flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm text-red-500",
|
||||
wrapperClassName,
|
||||
)}
|
||||
>
|
||||
<span className="text-sm">{t("HOME$FAILED_TO_LOAD_BRANCHES")}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Spinner } from "@heroui/react";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
export function BranchLoadingState() {
|
||||
interface BranchLoadingStateProps {
|
||||
wrapperClassName?: string;
|
||||
}
|
||||
|
||||
export function BranchLoadingState({
|
||||
wrapperClassName,
|
||||
}: BranchLoadingStateProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
data-testid="branch-dropdown-loading"
|
||||
className="flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm"
|
||||
className={cn(
|
||||
"flex items-center gap-2 max-w-[500px] h-10 px-3 bg-tertiary border border-[#717888] rounded-sm",
|
||||
wrapperClassName,
|
||||
)}
|
||||
>
|
||||
<Spinner size="sm" />
|
||||
<span className="text-sm">{t("HOME$LOADING_BRANCHES")}</span>
|
||||
|
||||
@@ -8,7 +8,6 @@ export interface RepositoryDropdownProps {
|
||||
onSelectionChange: (key: React.Key | null) => void;
|
||||
onInputChange: (value: string) => void;
|
||||
defaultFilter?: (textValue: string, inputValue: string) => boolean;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
export function RepositoryDropdown({
|
||||
@@ -16,7 +15,6 @@ export function RepositoryDropdown({
|
||||
onSelectionChange,
|
||||
onInputChange,
|
||||
defaultFilter,
|
||||
isDisabled = false,
|
||||
}: RepositoryDropdownProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -24,13 +22,12 @@ export function RepositoryDropdown({
|
||||
<SettingsDropdownInput
|
||||
testId="repo-dropdown"
|
||||
name="repo-dropdown"
|
||||
placeholder={isDisabled ? t("Please select a provider first") : t(I18nKey.REPOSITORY$SELECT_REPO)}
|
||||
placeholder={t(I18nKey.REPOSITORY$SELECT_REPO)}
|
||||
items={items}
|
||||
wrapperClassName="max-w-[500px]"
|
||||
onSelectionChange={onSelectionChange}
|
||||
onInputChange={onInputChange}
|
||||
defaultFilter={defaultFilter}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { GitProviderIcon } from "#/components/shared/git-provider-icon";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { MicroagentManagementAddMicroagentButton } from "./microagent-management-add-microagent-button";
|
||||
import { TooltipButton } from "#/components/shared/buttons/tooltip-button";
|
||||
|
||||
interface MicroagentManagementAccordionTitleProps {
|
||||
repository: GitRepository;
|
||||
}
|
||||
|
||||
export function MicroagentManagementAccordionTitle({
|
||||
repository,
|
||||
}: MicroagentManagementAccordionTitleProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<GitProviderIcon gitProvider={repository.git_provider} />
|
||||
<TooltipButton
|
||||
tooltip={repository.full_name}
|
||||
ariaLabel={repository.full_name}
|
||||
className="text-white text-base font-normal bg-transparent p-0 min-w-0 h-auto cursor-pointer truncate max-w-[232px]"
|
||||
testId="repository-name-tooltip"
|
||||
placement="bottom"
|
||||
>
|
||||
<span>{repository.full_name}</span>
|
||||
</TooltipButton>
|
||||
</div>
|
||||
<MicroagentManagementAddMicroagentButton repository={repository} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,22 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { setAddMicroagentModalVisible } from "#/state/microagent-management-slice";
|
||||
import {
|
||||
setAddMicroagentModalVisible,
|
||||
setSelectedRepository,
|
||||
} from "#/state/microagent-management-slice";
|
||||
import { RootState } from "#/store";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import PlusIcon from "#/icons/plus.svg?react";
|
||||
import { TooltipButton } from "#/components/shared/buttons/tooltip-button";
|
||||
|
||||
export function MicroagentManagementAddMicroagentButton() {
|
||||
interface MicroagentManagementAddMicroagentButtonProps {
|
||||
repository: GitRepository;
|
||||
}
|
||||
|
||||
export function MicroagentManagementAddMicroagentButton({
|
||||
repository,
|
||||
}: MicroagentManagementAddMicroagentButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { addMicroagentModalVisible } = useSelector(
|
||||
@@ -13,17 +25,23 @@ export function MicroagentManagementAddMicroagentButton() {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleClick = () => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
dispatch(setAddMicroagentModalVisible(!addMicroagentModalVisible));
|
||||
dispatch(setSelectedRepository(repository));
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="text-sm font-normal text-[#8480FF] cursor-pointer"
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
</button>
|
||||
<div onClick={handleClick}>
|
||||
<TooltipButton
|
||||
tooltip={t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
ariaLabel={t(I18nKey.COMMON$ADD_MICROAGENT)}
|
||||
className="p-0 min-w-0 h-6 w-6 flex items-center justify-center bg-transparent cursor-pointer"
|
||||
testId="add-microagent-button"
|
||||
placement="bottom"
|
||||
>
|
||||
<PlusIcon width={22} height={22} />
|
||||
</TooltipButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { FaCircleInfo } from "react-icons/fa6";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { RootState } from "#/store";
|
||||
import XIcon from "#/icons/x.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { BadgeInput } from "#/components/shared/inputs/badge-input";
|
||||
|
||||
interface MicroagentManagementAddMicroagentModalProps {
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export function MicroagentManagementAddMicroagentModal({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: MicroagentManagementAddMicroagentModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [triggers, setTriggers] = useState<string[]>([]);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const modalTitle = selectedRepository
|
||||
? `${t(I18nKey.MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT_TO)} ${selectedRepository}`
|
||||
: t(I18nKey.MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT);
|
||||
|
||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalBackdrop>
|
||||
<ModalBody className="items-start rounded-[12px] p-6 min-w-[611px]">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-white text-xl font-medium">{modalTitle}</h2>
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/prompting/microagents-overview#microagents-overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
<button type="button" onClick={onCancel} className="cursor-pointer">
|
||||
<XIcon width={24} height={24} color="#F9FBFE" />
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-white text-sm font-normal">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$ADD_MICROAGENT_MODAL_DESCRIPTION)}
|
||||
</span>
|
||||
</div>
|
||||
<form
|
||||
data-testid="add-microagent-modal"
|
||||
onSubmit={onSubmit}
|
||||
className="flex flex-col gap-6 w-full"
|
||||
>
|
||||
<label
|
||||
htmlFor="query-input"
|
||||
className="flex flex-col gap-2 w-full text-sm font-normal"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$WHAT_TO_DO)}
|
||||
<textarea
|
||||
required
|
||||
data-testid="query-input"
|
||||
name="query-input"
|
||||
placeholder={t(I18nKey.MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_DO)}
|
||||
rows={6}
|
||||
className={cn(
|
||||
"bg-tertiary border border-[#717888] bg-[#454545] w-full rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt resize-none",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center gap-2 text-[11px] font-normal text-white leading-[16px]">
|
||||
<span className="font-semibold">
|
||||
{t(I18nKey.COMMON$FOR_EXAMPLE)}:
|
||||
</span>
|
||||
<span className="underline">
|
||||
{t(I18nKey.COMMON$TEST_DB_MIGRATION)}
|
||||
</span>
|
||||
<span className="underline">{t(I18nKey.COMMON$RUN_TEST)}</span>
|
||||
<span className="underline">{t(I18nKey.COMMON$RUN_APP)}</span>
|
||||
<span className="underline">
|
||||
{t(I18nKey.COMMON$LEARN_FILE_STRUCTURE)}
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="trigger-input"
|
||||
className="flex flex-col gap-2.5 w-full text-sm"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$ADD_TRIGGERS)}
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/prompting/microagents-keyword"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
<BadgeInput
|
||||
name="trigger-input"
|
||||
value={triggers}
|
||||
placeholder={t("MICROAGENT$TYPE_TRIGGER_SPACE")}
|
||||
onChange={setTriggers}
|
||||
/>
|
||||
<span className="text-xs text-[#ffffff80] font-normal">
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$HELP_TEXT_DESCRIBING_VALID_TRIGGERS,
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
<div
|
||||
className="flex items-center justify-end gap-2 w-full"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onCancel}
|
||||
data-testid="cancel-button"
|
||||
>
|
||||
{t(I18nKey.BUTTON$CANCEL)}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={onConfirm}
|
||||
data-testid="confirm-button"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT$LAUNCH)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { MicroagentManagementSidebar } from "./microagent-management-sidebar";
|
||||
import { MicroagentManagementMain } from "./microagent-management-main";
|
||||
import { MicroagentManagementUpsertMicroagentModal } from "./microagent-management-upsert-microagent-modal";
|
||||
import { RootState } from "#/store";
|
||||
import {
|
||||
setAddMicroagentModalVisible,
|
||||
setUpdateMicroagentModalVisible,
|
||||
setLearnThisRepoModalVisible,
|
||||
} from "#/state/microagent-management-slice";
|
||||
import { useCreateConversationAndSubscribeMultiple } from "#/hooks/use-create-conversation-and-subscribe-multiple";
|
||||
import {
|
||||
LearnThisRepoFormData,
|
||||
MicroagentFormData,
|
||||
} from "#/types/microagent-management";
|
||||
import { AgentState } from "#/types/agent-state";
|
||||
import { getPR, getProviderName, getPRShort } from "#/utils/utils";
|
||||
import {
|
||||
isOpenHandsEvent,
|
||||
isAgentStateChangeObservation,
|
||||
isFinishAction,
|
||||
} from "#/types/core/guards";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { queryClient } from "#/query-client-config";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { MicroagentManagementLearnThisRepoModal } from "./microagent-management-learn-this-repo-modal";
|
||||
|
||||
// Handle error events
|
||||
const isErrorEvent = (evt: unknown): evt is { error: true; message: string } =>
|
||||
typeof evt === "object" &&
|
||||
evt !== null &&
|
||||
"error" in evt &&
|
||||
evt.error === true;
|
||||
|
||||
const isAgentStatusError = (evt: unknown): boolean =>
|
||||
isOpenHandsEvent(evt) &&
|
||||
isAgentStateChangeObservation(evt) &&
|
||||
evt.extras.agent_state === AgentState.ERROR;
|
||||
|
||||
const shouldInvalidateConversationsList = (currentSocketEvent: unknown) => {
|
||||
const hasError =
|
||||
isErrorEvent(currentSocketEvent) || isAgentStatusError(currentSocketEvent);
|
||||
const hasStateChanged =
|
||||
isOpenHandsEvent(currentSocketEvent) &&
|
||||
isAgentStateChangeObservation(currentSocketEvent);
|
||||
const hasFinished =
|
||||
isOpenHandsEvent(currentSocketEvent) && isFinishAction(currentSocketEvent);
|
||||
|
||||
return hasError || hasStateChanged || hasFinished;
|
||||
};
|
||||
|
||||
const getConversationInstructions = (
|
||||
repositoryName: string,
|
||||
formData: MicroagentFormData,
|
||||
pr: string,
|
||||
prShort: string,
|
||||
gitProvider: Provider,
|
||||
) => `Create a microagent for the repository ${repositoryName} by following the steps below:
|
||||
|
||||
- Step 1: Create a markdown file inside the .openhands/microagents folder with the name of the microagent (The microagent must be created in the .openhands/microagents folder and should be able to perform the described task when triggered).
|
||||
|
||||
- This is the instructions about what the microagent should do: ${formData.query}
|
||||
|
||||
${
|
||||
formData.triggers && formData.triggers.length > 0
|
||||
? `
|
||||
- This is the triggers of the microagent: ${formData.triggers.join(", ")}
|
||||
`
|
||||
: "- Please be noted that the microagent doesn't have any triggers."
|
||||
}
|
||||
|
||||
- Step 2: Create a new branch for the repository ${repositoryName}, must avoid duplicated branches.
|
||||
|
||||
- Step 3: Please push the changes to your branch on ${getProviderName(gitProvider)} and create a ${pr}. Please create a meaningful branch name that describes the changes. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.
|
||||
`;
|
||||
|
||||
const getUpdateConversationInstructions = (
|
||||
repositoryName: string,
|
||||
formData: MicroagentFormData,
|
||||
pr: string,
|
||||
prShort: string,
|
||||
gitProvider: Provider,
|
||||
) => `Update the microagent for the repository ${repositoryName} by following the steps below:
|
||||
|
||||
|
||||
- Step 1: Update the microagent. This is the path of the microagent: ${formData.microagentPath} (The updated microagent must be in the .openhands/microagents folder and should be able to perform the described task when triggered).
|
||||
|
||||
- This is the updated instructions about what the microagent should do: ${formData.query}
|
||||
|
||||
${
|
||||
formData.triggers && formData.triggers.length > 0
|
||||
? `
|
||||
- This is the triggers of the microagent: ${formData.triggers.join(", ")}
|
||||
`
|
||||
: "- Please be noted that the microagent doesn't have any triggers."
|
||||
}
|
||||
|
||||
- Step 2: Create a new branch for the repository ${repositoryName}, must avoid duplicated branches.
|
||||
|
||||
- Step 3: Please push the changes to your branch on ${getProviderName(gitProvider)} and create a ${pr}. Please create a meaningful branch name that describes the changes. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.
|
||||
`;
|
||||
|
||||
export function MicroagentManagementContent() {
|
||||
// Responsive width state
|
||||
const [width, setWidth] = useState(window.innerWidth);
|
||||
|
||||
const {
|
||||
addMicroagentModalVisible,
|
||||
updateMicroagentModalVisible,
|
||||
selectedRepository,
|
||||
learnThisRepoModalVisible,
|
||||
} = useSelector((state: RootState) => state.microagentManagement);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { createConversationAndSubscribe, isPending } =
|
||||
useCreateConversationAndSubscribeMultiple();
|
||||
|
||||
function handleResize() {
|
||||
setWidth(window.innerWidth);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const hideUpsertMicroagentModal = (isUpdate: boolean = false) => {
|
||||
if (isUpdate) {
|
||||
dispatch(setUpdateMicroagentModalVisible(false));
|
||||
} else {
|
||||
dispatch(setAddMicroagentModalVisible(false));
|
||||
}
|
||||
};
|
||||
|
||||
// Reusable function to invalidate conversations list for a repository
|
||||
const invalidateConversationsList = React.useCallback(
|
||||
(repositoryName: string) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
"conversations",
|
||||
"search",
|
||||
repositoryName,
|
||||
"microagent_management",
|
||||
],
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleMicroagentEvent = React.useCallback(
|
||||
(socketEvent: unknown) => {
|
||||
// Get repository name from selectedRepository for invalidation
|
||||
const repositoryName =
|
||||
selectedRepository && typeof selectedRepository === "object"
|
||||
? (selectedRepository as GitRepository).full_name
|
||||
: "";
|
||||
|
||||
if (shouldInvalidateConversationsList(socketEvent)) {
|
||||
invalidateConversationsList(repositoryName);
|
||||
}
|
||||
},
|
||||
[invalidateConversationsList, selectedRepository],
|
||||
);
|
||||
|
||||
const handleUpsertMicroagent = (
|
||||
formData: MicroagentFormData,
|
||||
isUpdate: boolean = false,
|
||||
) => {
|
||||
if (!selectedRepository || typeof selectedRepository !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the GitRepository properties
|
||||
const repository = selectedRepository as GitRepository;
|
||||
const repositoryName = repository.full_name;
|
||||
const gitProvider = repository.git_provider;
|
||||
|
||||
const isGitLab = gitProvider === "gitlab";
|
||||
|
||||
const pr = getPR(isGitLab);
|
||||
const prShort = getPRShort(isGitLab);
|
||||
|
||||
// Create conversation instructions for microagent generation or update
|
||||
const conversationInstructions = isUpdate
|
||||
? getUpdateConversationInstructions(
|
||||
repositoryName,
|
||||
formData,
|
||||
pr,
|
||||
prShort,
|
||||
gitProvider,
|
||||
)
|
||||
: getConversationInstructions(
|
||||
repositoryName,
|
||||
formData,
|
||||
pr,
|
||||
prShort,
|
||||
gitProvider,
|
||||
);
|
||||
|
||||
// Create the CreateMicroagent object
|
||||
const createMicroagent = {
|
||||
repo: repositoryName,
|
||||
git_provider: gitProvider,
|
||||
title: formData.query,
|
||||
};
|
||||
|
||||
createConversationAndSubscribe({
|
||||
query: conversationInstructions,
|
||||
conversationInstructions,
|
||||
repository: {
|
||||
name: repositoryName,
|
||||
branch: formData.selectedBranch,
|
||||
gitProvider,
|
||||
},
|
||||
createMicroagent,
|
||||
onSuccessCallback: () => {
|
||||
// Invalidate conversations list to fetch the latest conversations for this repository
|
||||
invalidateConversationsList(repositoryName);
|
||||
|
||||
// Also invalidate microagents list to fetch the latest microagents
|
||||
// Extract owner and repo from full_name (format: "owner/repo")
|
||||
const [owner, repo] = repositoryName.split("/");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["repository-microagents", owner, repo],
|
||||
});
|
||||
|
||||
hideUpsertMicroagentModal(isUpdate);
|
||||
},
|
||||
onEventCallback: (event: unknown) => {
|
||||
// Handle conversation events for real-time status updates
|
||||
handleMicroagentEvent(event);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const hideLearnThisRepoModal = () => {
|
||||
dispatch(setLearnThisRepoModalVisible(false));
|
||||
};
|
||||
|
||||
const handleLearnThisRepoConfirm = (formData: LearnThisRepoFormData) => {
|
||||
if (!selectedRepository || typeof selectedRepository !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
const repository = selectedRepository as GitRepository;
|
||||
const repositoryName = repository.full_name;
|
||||
const gitProvider = repository.git_provider;
|
||||
|
||||
// Launch a new conversation to help the user understand the repo
|
||||
createConversationAndSubscribe({
|
||||
query: formData.query,
|
||||
conversationInstructions: formData.query,
|
||||
repository: {
|
||||
name: repositoryName,
|
||||
branch: formData.selectedBranch,
|
||||
gitProvider,
|
||||
},
|
||||
onSuccessCallback: () => {
|
||||
hideLearnThisRepoModal();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderModals = () => (
|
||||
<>
|
||||
{(addMicroagentModalVisible || updateMicroagentModalVisible) && (
|
||||
<MicroagentManagementUpsertMicroagentModal
|
||||
onConfirm={(formData) =>
|
||||
handleUpsertMicroagent(formData, updateMicroagentModalVisible)
|
||||
}
|
||||
onCancel={() =>
|
||||
hideUpsertMicroagentModal(updateMicroagentModalVisible)
|
||||
}
|
||||
isLoading={isPending}
|
||||
isUpdate={updateMicroagentModalVisible}
|
||||
/>
|
||||
)}
|
||||
{learnThisRepoModalVisible && (
|
||||
<MicroagentManagementLearnThisRepoModal
|
||||
onCancel={hideLearnThisRepoModal}
|
||||
onConfirm={handleLearnThisRepoConfirm}
|
||||
isLoading={isPending}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (width < 1024) {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col gap-6">
|
||||
<div className="w-full rounded-lg border border-[#525252] bg-[#24272E] max-h-[494px] min-h-[494px]">
|
||||
<MicroagentManagementSidebar isSmallerScreen />
|
||||
</div>
|
||||
<div className="w-full rounded-lg border border-[#525252] bg-[#24272E] flex-1 min-h-[494px]">
|
||||
<MicroagentManagementMain />
|
||||
</div>
|
||||
{renderModals()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex rounded-lg border border-[#525252] bg-[#24272E] overflow-hidden">
|
||||
<MicroagentManagementSidebar />
|
||||
<div className="flex-1">
|
||||
<MicroagentManagementMain />
|
||||
</div>
|
||||
{renderModals()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { Loader } from "#/components/shared/loader";
|
||||
|
||||
export function MicroagentManagementConversationStopped() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { conversation } = selectedMicroagentItem ?? {};
|
||||
|
||||
const { conversation_id: conversationId } = conversation ?? {};
|
||||
|
||||
if (!conversationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#ffffff99] text-[22px] font-bold pb-[22px] text-center max-w-[455px]">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$CONVERSATION_STOPPED)}
|
||||
</div>
|
||||
<Loader size="small" className="pb-[22px]" />
|
||||
<a
|
||||
href={`/conversations/${conversationId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
testId="view-conversation-button"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT$VIEW_CONVERSATION)}
|
||||
</BrandButton>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
export function MicroagentManagementDefault() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#F9FBFE] text-xl font-bold pb-4">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT)}
|
||||
</div>
|
||||
<div className="text-white text-sm font-normal text-center max-w-[455px]">
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { Loader } from "#/components/shared/loader";
|
||||
|
||||
export function MicroagentManagementError() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { conversation } = selectedMicroagentItem ?? {};
|
||||
|
||||
const { conversation_id: conversationId } = conversation ?? {};
|
||||
|
||||
if (!conversationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#ffffff99] text-[22px] font-bold pb-[22px] text-center max-w-[455px]">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$ERROR)}
|
||||
</div>
|
||||
<Loader size="small" className="pb-[22px]" />
|
||||
<a
|
||||
href={`/conversations/${conversationId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
testId="view-conversation-button"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT$VIEW_CONVERSATION)}
|
||||
</BrandButton>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { FaCircleInfo } from "react-icons/fa6";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { RootState } from "#/store";
|
||||
import XIcon from "#/icons/x.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { LearnThisRepoFormData } from "#/types/microagent-management";
|
||||
import { Branch } from "#/types/git";
|
||||
import { useRepositoryBranches } from "#/hooks/query/use-repository-branches";
|
||||
import {
|
||||
BranchDropdown,
|
||||
BranchLoadingState,
|
||||
BranchErrorState,
|
||||
} from "../home/repository-selection";
|
||||
|
||||
interface MicroagentManagementLearnThisRepoModalProps {
|
||||
onConfirm: (formData: LearnThisRepoFormData) => void;
|
||||
onCancel: () => void;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export function MicroagentManagementLearnThisRepoModal({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
isLoading = false,
|
||||
}: MicroagentManagementLearnThisRepoModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
// Add a ref to track if the branch was manually cleared by the user
|
||||
const branchManuallyClearedRef = useRef<boolean>(false);
|
||||
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isLoadingBranches,
|
||||
isError: isBranchesError,
|
||||
} = useRepositoryBranches(selectedRepository?.full_name || null);
|
||||
|
||||
const branchesItems = branches?.map((branch) => ({
|
||||
key: branch.name,
|
||||
label: branch.name,
|
||||
}));
|
||||
|
||||
// Auto-select main or master branch if it exists.
|
||||
useEffect(() => {
|
||||
if (
|
||||
branches &&
|
||||
branches.length > 0 &&
|
||||
!selectedBranch &&
|
||||
!isLoadingBranches
|
||||
) {
|
||||
// Look for main or master branch
|
||||
const mainBranch = branches.find((branch) => branch.name === "main");
|
||||
const masterBranch = branches.find((branch) => branch.name === "master");
|
||||
|
||||
// Select main if it exists, otherwise select master if it exists
|
||||
if (mainBranch) {
|
||||
setSelectedBranch(mainBranch);
|
||||
} else if (masterBranch) {
|
||||
setSelectedBranch(masterBranch);
|
||||
}
|
||||
}
|
||||
}, [branches, isLoadingBranches, selectedBranch]);
|
||||
|
||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!query.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm({
|
||||
query: query.trim(),
|
||||
selectedBranch: selectedBranch?.name || "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!query.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm({
|
||||
query: query.trim(),
|
||||
selectedBranch: selectedBranch?.name || "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleBranchSelection = (key: React.Key | null) => {
|
||||
const selectedBranchObj = branches?.find((branch) => branch.name === key);
|
||||
setSelectedBranch(selectedBranchObj || null);
|
||||
// Reset the manually cleared flag when a branch is explicitly selected
|
||||
branchManuallyClearedRef.current = false;
|
||||
};
|
||||
|
||||
const handleBranchInputChange = (value: string) => {
|
||||
// Clear the selected branch if the input is empty or contains only whitespace
|
||||
// This fixes the issue where users can't delete the entire default branch name
|
||||
if (value === "" || value.trim() === "") {
|
||||
setSelectedBranch(null);
|
||||
// Set the flag to indicate that the branch was manually cleared
|
||||
branchManuallyClearedRef.current = true;
|
||||
} else {
|
||||
// Reset the flag when the user starts typing again
|
||||
branchManuallyClearedRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Render the appropriate UI for branch selector based on the loading/error state
|
||||
const renderBranchSelector = () => {
|
||||
if (!selectedRepository) {
|
||||
return (
|
||||
<BranchDropdown
|
||||
items={[]}
|
||||
onSelectionChange={() => {}}
|
||||
onInputChange={() => {}}
|
||||
isDisabled
|
||||
wrapperClassName="max-w-full w-full"
|
||||
label={t(I18nKey.REPOSITORY$SELECT_BRANCH)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoadingBranches) {
|
||||
return <BranchLoadingState wrapperClassName="max-w-full w-full" />;
|
||||
}
|
||||
|
||||
if (isBranchesError) {
|
||||
return <BranchErrorState wrapperClassName="max-w-full w-full" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<BranchDropdown
|
||||
items={branchesItems || []}
|
||||
onSelectionChange={handleBranchSelection}
|
||||
onInputChange={handleBranchInputChange}
|
||||
isDisabled={false}
|
||||
selectedKey={selectedBranch?.name}
|
||||
wrapperClassName="max-w-full w-full"
|
||||
label={t(I18nKey.REPOSITORY$SELECT_BRANCH)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalBackdrop onClose={onCancel}>
|
||||
<ModalBody
|
||||
className="items-start rounded-[12px] p-6 min-w-[611px]"
|
||||
data-testid="learn-this-repo-modal"
|
||||
>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2
|
||||
className="text-white text-xl font-medium"
|
||||
data-testid="modal-title"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_TITLE)}
|
||||
</h2>
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/prompting/microagents-overview#microagents-overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-testid="modal-info-link"
|
||||
>
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="cursor-pointer"
|
||||
data-testid="modal-close-button"
|
||||
>
|
||||
<XIcon width={24} height={24} color="#F9FBFE" />
|
||||
</button>
|
||||
</div>
|
||||
<span
|
||||
className="text-white text-sm font-normal"
|
||||
data-testid="modal-description"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_DESCRIPTION)}
|
||||
</span>
|
||||
</div>
|
||||
<form
|
||||
data-testid="learn-this-repo-form"
|
||||
onSubmit={onSubmit}
|
||||
className="flex flex-col gap-6 w-full"
|
||||
>
|
||||
<div data-testid="branch-selector-container">
|
||||
{renderBranchSelector()}
|
||||
</div>
|
||||
<label
|
||||
htmlFor="query-input"
|
||||
className="flex flex-col gap-2 w-full text-sm font-normal"
|
||||
>
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO,
|
||||
)}
|
||||
<textarea
|
||||
required
|
||||
data-testid="query-input"
|
||||
name="query-input"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO,
|
||||
)}
|
||||
rows={6}
|
||||
className={cn(
|
||||
"bg-tertiary border border-[#717888] bg-[#454545] w-full rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt resize-none",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
<div
|
||||
className="flex items-center justify-end gap-2 w-full"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
data-testid="modal-actions"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onCancel}
|
||||
testId="cancel-button"
|
||||
>
|
||||
{t(I18nKey.BUTTON$CANCEL)}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleConfirm}
|
||||
testId="confirm-button"
|
||||
isDisabled={
|
||||
!query.trim() ||
|
||||
isLoading ||
|
||||
isLoadingBranches ||
|
||||
!selectedBranch ||
|
||||
isBranchesError
|
||||
}
|
||||
>
|
||||
{isLoading || isLoadingBranches
|
||||
? t(I18nKey.HOME$LOADING)
|
||||
: t(I18nKey.MICROAGENT$LAUNCH)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +1,36 @@
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import {
|
||||
setLearnThisRepoModalVisible,
|
||||
setSelectedRepository,
|
||||
} from "#/state/microagent-management-slice";
|
||||
import { GitRepository } from "#/types/git";
|
||||
|
||||
interface MicroagentManagementLearnThisRepoProps {
|
||||
repositoryUrl: string;
|
||||
repository: GitRepository;
|
||||
}
|
||||
|
||||
export function MicroagentManagementLearnThisRepo({
|
||||
repositoryUrl,
|
||||
repository,
|
||||
}: MicroagentManagementLearnThisRepoProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClick = () => {
|
||||
dispatch(setLearnThisRepoModalVisible(true));
|
||||
dispatch(setSelectedRepository(repository));
|
||||
};
|
||||
|
||||
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"
|
||||
>
|
||||
<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"
|
||||
onClick={handleClick}
|
||||
data-testid="learn-this-repo-trigger"
|
||||
>
|
||||
<span className="text-[16px] font-normal text-[#8480FF]">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$LEARN_THIS_REPO)}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { MicroagentManagementDefault } from "./microagent-management-default";
|
||||
import { MicroagentManagementOpeningPr } from "./microagent-management-opening-pr";
|
||||
import { MicroagentManagementReviewPr } from "./microagent-management-review-pr";
|
||||
import { MicroagentManagementViewMicroagent } from "./microagent-management-view-microagent";
|
||||
import { MicroagentManagementError } from "./microagent-management-error";
|
||||
import { MicroagentManagementConversationStopped } from "./microagent-management-conversation-stopped";
|
||||
|
||||
export function MicroagentManagementMain() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagent } = useSelector(
|
||||
const { selectedMicroagentItem } = 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-xl font-bold pb-4">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$READY_TO_ADD_MICROAGENT)}
|
||||
</div>
|
||||
<div className="text-white text-sm font-normal text-center max-w-[455px]">
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const { microagent, conversation } = selectedMicroagentItem ?? {};
|
||||
|
||||
if (microagent) {
|
||||
return <MicroagentManagementViewMicroagent />;
|
||||
}
|
||||
|
||||
return null;
|
||||
if (conversation) {
|
||||
if (conversation.pr_number && conversation.pr_number.length > 0) {
|
||||
return <MicroagentManagementReviewPr />;
|
||||
}
|
||||
|
||||
const isConversationStarting =
|
||||
conversation.status === "STARTING" ||
|
||||
conversation.runtime_status === "STATUS$STARTING_RUNTIME";
|
||||
const isConversationOpeningPr =
|
||||
conversation.status === "RUNNING" &&
|
||||
conversation.runtime_status === "STATUS$READY";
|
||||
|
||||
if (isConversationStarting || isConversationOpeningPr) {
|
||||
return <MicroagentManagementOpeningPr />;
|
||||
}
|
||||
|
||||
if (conversation.runtime_status === "STATUS$ERROR") {
|
||||
return <MicroagentManagementError />;
|
||||
}
|
||||
|
||||
if (
|
||||
conversation.status === "STOPPED" ||
|
||||
conversation.runtime_status === "STATUS$STOPPED"
|
||||
) {
|
||||
return <MicroagentManagementConversationStopped />;
|
||||
}
|
||||
|
||||
return <MicroagentManagementDefault />;
|
||||
}
|
||||
|
||||
return <MicroagentManagementDefault />;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,142 @@
|
||||
import { useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
export interface Microagent {
|
||||
id: string;
|
||||
name: string;
|
||||
repositoryUrl: string;
|
||||
createdAt: string;
|
||||
}
|
||||
import { formatDateMMDDYYYY } from "#/utils/format-time-delta";
|
||||
import { RepositoryMicroagent } from "#/types/microagent-management";
|
||||
import { Conversation } from "#/api/open-hands.types";
|
||||
import {
|
||||
setSelectedMicroagentItem,
|
||||
setSelectedRepository,
|
||||
} from "#/state/microagent-management-slice";
|
||||
import { RootState } from "#/store";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { GitRepository } from "#/types/git";
|
||||
|
||||
interface MicroagentManagementMicroagentCardProps {
|
||||
microagent: Microagent;
|
||||
microagent?: RepositoryMicroagent;
|
||||
conversation?: Conversation;
|
||||
repository: GitRepository;
|
||||
}
|
||||
|
||||
export function MicroagentManagementMicroagentCard({
|
||||
microagent,
|
||||
conversation,
|
||||
repository,
|
||||
}: MicroagentManagementMicroagentCardProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
status: conversationStatus,
|
||||
runtime_status: runtimeStatus,
|
||||
pr_number: prNumber,
|
||||
} = conversation ?? {};
|
||||
|
||||
// Format the repository URL to point to the microagent file
|
||||
const microagentFilePath = microagent
|
||||
? `.openhands/microagents/${microagent.name}`
|
||||
: "";
|
||||
|
||||
// Format the createdAt date using MM/DD/YYYY format
|
||||
const formattedCreatedAt = useMemo(() => {
|
||||
if (microagent) {
|
||||
return formatDateMMDDYYYY(new Date(microagent.created_at));
|
||||
}
|
||||
if (conversation) {
|
||||
return formatDateMMDDYYYY(new Date(conversation.created_at));
|
||||
}
|
||||
return "";
|
||||
}, [microagent, conversation]);
|
||||
|
||||
const hasPr = !!(prNumber && prNumber.length > 0);
|
||||
|
||||
// Helper function to get status text
|
||||
const statusText = useMemo(() => {
|
||||
if (hasPr) {
|
||||
return t(I18nKey.COMMON$READY_FOR_REVIEW);
|
||||
}
|
||||
if (
|
||||
conversationStatus === "STARTING" ||
|
||||
runtimeStatus === "STATUS$STARTING_RUNTIME"
|
||||
) {
|
||||
return t(I18nKey.COMMON$STARTING);
|
||||
}
|
||||
if (
|
||||
conversationStatus === "STOPPED" ||
|
||||
runtimeStatus === "STATUS$STOPPED"
|
||||
) {
|
||||
return t(I18nKey.COMMON$STOPPED);
|
||||
}
|
||||
if (runtimeStatus === "STATUS$ERROR") {
|
||||
return t(I18nKey.MICROAGENT$STATUS_ERROR);
|
||||
}
|
||||
if (conversationStatus === "RUNNING" && runtimeStatus === "STATUS$READY") {
|
||||
return t(I18nKey.MICROAGENT$STATUS_OPENING_PR);
|
||||
}
|
||||
return "";
|
||||
}, [conversationStatus, runtimeStatus, t, hasPr]);
|
||||
|
||||
const cardTitle = microagent?.name ?? conversation?.title;
|
||||
|
||||
const isCardSelected = useMemo(() => {
|
||||
if (microagent && selectedMicroagentItem?.microagent) {
|
||||
return selectedMicroagentItem.microagent.name === microagent.name;
|
||||
}
|
||||
if (conversation && selectedMicroagentItem?.conversation) {
|
||||
return (
|
||||
selectedMicroagentItem.conversation.conversation_id ===
|
||||
conversation.conversation_id
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}, [microagent, conversation, selectedMicroagentItem]);
|
||||
|
||||
const onMicroagentCardClicked = () => {
|
||||
dispatch(
|
||||
setSelectedMicroagentItem(
|
||||
microagent
|
||||
? {
|
||||
microagent,
|
||||
conversation: null,
|
||||
}
|
||||
: {
|
||||
microagent: null,
|
||||
conversation,
|
||||
},
|
||||
),
|
||||
);
|
||||
dispatch(setSelectedRepository(repository));
|
||||
};
|
||||
|
||||
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-sm font-normal">
|
||||
{microagent.repositoryUrl}
|
||||
</div>
|
||||
<div className="text-white text-sm font-normal">
|
||||
{t(I18nKey.COMMON$CREATED_ON)} {microagent.createdAt}
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-lg bg-[#ffffff0d] border border-[#ffffff33] p-4 cursor-pointer hover:bg-[#ffffff33] hover:border-[#C9B974] transition-all duration-300",
|
||||
isCardSelected && "bg-[#ffffff33] border-[#C9B974]",
|
||||
)}
|
||||
onClick={onMicroagentCardClicked}
|
||||
>
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{statusText && (
|
||||
<div className="px-[6px] py-[2px] text-[11px] font-medium bg-[#C9B97433] text-white rounded-2xl">
|
||||
{statusText}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-white text-[16px] font-semibold">{cardTitle}</div>
|
||||
{!!microagent && (
|
||||
<div className="text-white text-sm font-normal">
|
||||
{microagentFilePath}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-white text-sm font-normal">
|
||||
{t(I18nKey.COMMON$CREATED_ON)} {formattedCreatedAt}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
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 />
|
||||
</div>
|
||||
{microagents.map((microagent) => (
|
||||
<div key={microagent.id} className="pb-4">
|
||||
<MicroagentManagementMicroagentCard microagent={microagent} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { FaCircleInfo } from "react-icons/fa6";
|
||||
|
||||
interface MicroagentManagementNoRepositoriesProps {
|
||||
title: string;
|
||||
documentationUrl: string;
|
||||
}
|
||||
|
||||
export function MicroagentManagementNoRepositories({
|
||||
title,
|
||||
documentationUrl,
|
||||
}: MicroagentManagementNoRepositoriesProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-center pt-10">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-white text-sm font-medium">{title}</h2>
|
||||
<a href={documentationUrl} target="_blank" rel="noopener noreferrer">
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { Loader } from "#/components/shared/loader";
|
||||
|
||||
export function MicroagentManagementOpeningPr() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { conversation } = selectedMicroagentItem ?? {};
|
||||
|
||||
const { conversation_id: conversationId } = conversation ?? {};
|
||||
|
||||
if (!conversationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#ffffff99] text-[22px] font-semibold pb-2">
|
||||
{t(I18nKey.COMMON$WORKING_ON_IT)}!
|
||||
</div>
|
||||
<div className="text-[#ffffff99] text-[18px] font-normal text-center max-w-[518px] pb-[22px]">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$WE_ARE_WORKING_ON_IT)}
|
||||
</div>
|
||||
<Loader size="small" className="pb-[22px]" />
|
||||
<a
|
||||
href={`/conversations/${conversationId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
testId="view-conversation-button"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT$VIEW_CONVERSATION)}
|
||||
</BrandButton>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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 />
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +1,124 @@
|
||||
import { MicroagentManagementRepoMicroagent } from "./microagent-management-repo-microagent";
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Spinner } from "@heroui/react";
|
||||
import { MicroagentManagementMicroagentCard } from "./microagent-management-microagent-card";
|
||||
import { MicroagentManagementLearnThisRepo } from "./microagent-management-learn-this-repo";
|
||||
import { useRepositoryMicroagents } from "#/hooks/query/use-repository-microagents";
|
||||
import { useSearchConversations } from "#/hooks/query/use-search-conversations";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { RootState } from "#/store";
|
||||
import { setSelectedMicroagentItem } from "#/state/microagent-management-slice";
|
||||
|
||||
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",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
interface MicroagentManagementRepoMicroagentsProps {
|
||||
repository: GitRepository;
|
||||
}
|
||||
|
||||
const numberOfRepoMicroagents = repoMicroagents.length;
|
||||
export function MicroagentManagementRepoMicroagents({
|
||||
repository,
|
||||
}: MicroagentManagementRepoMicroagentsProps) {
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
if (numberOfRepoMicroagents === 0) {
|
||||
return null;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { full_name: repositoryName } = repository;
|
||||
|
||||
// Extract owner and repo from repositoryName (format: "owner/repo")
|
||||
const [owner, repo] = repositoryName.split("/");
|
||||
|
||||
const {
|
||||
data: microagents,
|
||||
isLoading: isLoadingMicroagents,
|
||||
isError: isErrorMicroagents,
|
||||
} = useRepositoryMicroagents(owner, repo, true);
|
||||
|
||||
const {
|
||||
data: conversations,
|
||||
isLoading: isLoadingConversations,
|
||||
isError: isErrorConversations,
|
||||
} = useSearchConversations(
|
||||
repositoryName,
|
||||
"microagent_management",
|
||||
1000,
|
||||
true,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hasConversations = conversations && conversations.length > 0;
|
||||
const selectedConversation = selectedMicroagentItem?.conversation;
|
||||
|
||||
if (hasConversations && selectedConversation) {
|
||||
// get the latest selected conversation.
|
||||
const latestSelectedConversation = conversations.find(
|
||||
(conversation) =>
|
||||
conversation.conversation_id === selectedConversation.conversation_id,
|
||||
);
|
||||
if (latestSelectedConversation) {
|
||||
dispatch(
|
||||
setSelectedMicroagentItem({
|
||||
microagent: null,
|
||||
conversation: latestSelectedConversation,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [conversations]);
|
||||
|
||||
// Show loading only when both queries are loading
|
||||
const isLoading = isLoadingMicroagents || isLoadingConversations;
|
||||
|
||||
// Show error UI.
|
||||
const isError = isErrorMicroagents || isErrorConversations;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="pb-4 flex justify-center">
|
||||
<Spinner size="sm" data-testid="loading-spinner" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If there's an error with microagents, show the learn this repo component
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="pb-4">
|
||||
<MicroagentManagementLearnThisRepo repository={repository} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const numberOfMicroagents = microagents?.length || 0;
|
||||
const numberOfConversations = conversations?.length || 0;
|
||||
const totalItems = numberOfMicroagents + numberOfConversations;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{repoMicroagents.map((repoMicroagent) => (
|
||||
<MicroagentManagementRepoMicroagent
|
||||
key={repoMicroagent.id}
|
||||
repoMicroagent={repoMicroagent}
|
||||
/>
|
||||
))}
|
||||
<div className="pb-4">
|
||||
{totalItems === 0 && (
|
||||
<MicroagentManagementLearnThisRepo repository={repository} />
|
||||
)}
|
||||
|
||||
{/* Render microagents */}
|
||||
{numberOfMicroagents > 0 &&
|
||||
microagents?.map((microagent) => (
|
||||
<div key={microagent.name} className="pb-4 last:pb-0">
|
||||
<MicroagentManagementMicroagentCard
|
||||
microagent={microagent}
|
||||
repository={repository}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Render conversations */}
|
||||
{numberOfConversations > 0 &&
|
||||
conversations?.map((conversation) => (
|
||||
<div key={conversation.conversation_id} className="pb-4 last:pb-0">
|
||||
<MicroagentManagementMicroagentCard
|
||||
conversation={conversation}
|
||||
repository={repository}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Accordion, AccordionItem } from "@heroui/react";
|
||||
import { MicroagentManagementRepoMicroagents } from "./microagent-management-repo-microagents";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { TabType } from "#/types/microagent-management";
|
||||
import { MicroagentManagementNoRepositories } from "./microagent-management-no-repositories";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { DOCUMENTATION_URL } from "#/utils/constants";
|
||||
import { MicroagentManagementAccordionTitle } from "./microagent-management-accordion-title";
|
||||
import { sanitizeQuery } from "#/utils/sanitize-query";
|
||||
|
||||
type MicroagentManagementRepositoriesProps = {
|
||||
repositories: GitRepository[];
|
||||
tabType: TabType;
|
||||
};
|
||||
|
||||
export function MicroagentManagementRepositories({
|
||||
repositories,
|
||||
tabType,
|
||||
}: MicroagentManagementRepositoriesProps) {
|
||||
const { t } = useTranslation();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const numberOfRepoMicroagents = repositories.length;
|
||||
|
||||
// Filter repositories based on search query
|
||||
const filteredRepositories = useMemo(() => {
|
||||
if (!searchQuery.trim()) {
|
||||
return repositories;
|
||||
}
|
||||
|
||||
const sanitizedQuery = sanitizeQuery(searchQuery);
|
||||
return repositories.filter((repository) => {
|
||||
const sanitizedRepoName = sanitizeQuery(repository.full_name);
|
||||
return sanitizedRepoName.includes(sanitizedQuery);
|
||||
});
|
||||
}, [repositories, searchQuery]);
|
||||
|
||||
if (numberOfRepoMicroagents === 0) {
|
||||
if (tabType === "personal") {
|
||||
return (
|
||||
<MicroagentManagementNoRepositories
|
||||
title={t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS,
|
||||
)}
|
||||
documentationUrl={DOCUMENTATION_URL.MICROAGENTS.MICROAGENTS_OVERVIEW}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (tabType === "repositories") {
|
||||
return (
|
||||
<MicroagentManagementNoRepositories
|
||||
title={t(I18nKey.MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS)}
|
||||
documentationUrl={DOCUMENTATION_URL.MICROAGENTS.MICROAGENTS_OVERVIEW}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (tabType === "organizations") {
|
||||
return (
|
||||
<MicroagentManagementNoRepositories
|
||||
title={t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS,
|
||||
)}
|
||||
documentationUrl={
|
||||
DOCUMENTATION_URL.MICROAGENTS.ORGANIZATION_AND_USER_MICROAGENTS
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
{/* Search Input */}
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<label htmlFor="repository-search" className="sr-only">
|
||||
{t(I18nKey.COMMON$SEARCH_REPOSITORIES)}
|
||||
</label>
|
||||
<input
|
||||
id="repository-search"
|
||||
name="repository-search"
|
||||
type="text"
|
||||
placeholder={`${t(I18nKey.COMMON$SEARCH_REPOSITORIES)}...`}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className={cn(
|
||||
"bg-tertiary border border-[#717888] bg-[#454545] w-full rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Repositories Accordion */}
|
||||
<Accordion
|
||||
variant="splitted"
|
||||
className="w-full px-0 gap-3"
|
||||
itemClasses={{
|
||||
base: "shadow-none bg-transparent border border-[#ffffff40] rounded-[6px] cursor-pointer",
|
||||
trigger: "cursor-pointer gap-1",
|
||||
}}
|
||||
selectionMode="multiple"
|
||||
>
|
||||
{filteredRepositories.map((repository) => (
|
||||
<AccordionItem
|
||||
key={repository.id}
|
||||
aria-label={repository.full_name}
|
||||
title={
|
||||
<MicroagentManagementAccordionTitle repository={repository} />
|
||||
}
|
||||
>
|
||||
<MicroagentManagementRepoMicroagents repository={repository} />
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { getProviderName, constructPullRequestUrl } from "#/utils/utils";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { RootState } from "#/store";
|
||||
|
||||
export function MicroagentManagementReviewPr() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { conversation } = selectedMicroagentItem ?? {};
|
||||
|
||||
const {
|
||||
conversation_id: conversationId,
|
||||
selected_repository: selectedRepository,
|
||||
git_provider: gitProvider,
|
||||
pr_number: prNumber,
|
||||
} = conversation ?? {};
|
||||
|
||||
if (!conversationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full items-center justify-center">
|
||||
<div className="text-[#ffffff99] text-[22px] font-bold pb-[22px] text-center max-w-[455px]">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$YOUR_MICROAGENT_IS_READY)}
|
||||
</div>
|
||||
<div className="flex gap-[22px]">
|
||||
<a
|
||||
href={`/conversations/${conversationId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
testId="view-conversation-button"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT$VIEW_CONVERSATION)}
|
||||
</BrandButton>
|
||||
</a>
|
||||
<a
|
||||
href={
|
||||
selectedRepository && gitProvider && prNumber && prNumber.length > 0
|
||||
? constructPullRequestUrl(
|
||||
prNumber[0],
|
||||
gitProvider,
|
||||
selectedRepository,
|
||||
)
|
||||
: "/#"
|
||||
}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
testId="view-conversation-button"
|
||||
>
|
||||
{`${t(I18nKey.COMMON$REVIEW_PR_IN)} ${getProviderName(
|
||||
gitProvider as Provider,
|
||||
)}`}
|
||||
</BrandButton>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import QuestionCircleIcon from "#/icons/question-circle.svg?react";
|
||||
import { DOCUMENTATION_URL } from "#/utils/constants";
|
||||
|
||||
export function MicroagentManagementSidebarHeader() {
|
||||
const { t } = useTranslation();
|
||||
@@ -12,7 +13,13 @@ export function MicroagentManagementSidebarHeader() {
|
||||
</h1>
|
||||
<p className="text-white text-sm font-normal leading-[20px] pt-2">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$USE_MICROAGENTS)}
|
||||
<QuestionCircleIcon className="inline-block ml-1" />
|
||||
<a
|
||||
href={DOCUMENTATION_URL.MICROAGENTS.MICROAGENTS_OVERVIEW}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<QuestionCircleIcon className="inline-block ml-1" />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
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 { useSelector } from "react-redux";
|
||||
import { MicroagentManagementRepositories } from "./microagent-management-repositories";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { RootState } from "#/store";
|
||||
|
||||
export function MicroagentManagementSidebarTabs() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { repositories, personalRepositories, organizationRepositories } =
|
||||
useSelector((state: RootState) => state.microagentManagement);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col">
|
||||
<Tabs
|
||||
@@ -17,18 +21,27 @@ export function MicroagentManagementSidebarTabs() {
|
||||
"w-full bg-transparent border border-[#ffffff40] rounded-[6px]",
|
||||
tab: "px-2 h-[22px]",
|
||||
tabContent: "text-white text-[12px] font-normal",
|
||||
panel: "py-0",
|
||||
panel: "p-0",
|
||||
cursor: "bg-[#C9B97480] rounded-sm",
|
||||
}}
|
||||
>
|
||||
<Tab key="personal" title={t(I18nKey.COMMON$PERSONAL)}>
|
||||
<MicroagentManagementMicroagents />
|
||||
<MicroagentManagementRepositories
|
||||
repositories={personalRepositories}
|
||||
tabType="personal"
|
||||
/>
|
||||
</Tab>
|
||||
<Tab key="repositories" title={t(I18nKey.COMMON$REPOSITORIES)}>
|
||||
<MicroagentManagementRepoMicroagents />
|
||||
<MicroagentManagementRepositories
|
||||
repositories={repositories}
|
||||
tabType="repositories"
|
||||
/>
|
||||
</Tab>
|
||||
<Tab key="organizations" title={t(I18nKey.COMMON$ORGANIZATIONS)}>
|
||||
<MicroagentManagementMicroagents />
|
||||
<MicroagentManagementRepositories
|
||||
repositories={organizationRepositories}
|
||||
tabType="organizations"
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,71 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Spinner } from "@heroui/react";
|
||||
import { MicroagentManagementSidebarHeader } from "./microagent-management-sidebar-header";
|
||||
import { MicroagentManagementSidebarTabs } from "./microagent-management-sidebar-tabs";
|
||||
import { useUserRepositories } from "#/hooks/query/use-user-repositories";
|
||||
import {
|
||||
setPersonalRepositories,
|
||||
setOrganizationRepositories,
|
||||
setRepositories,
|
||||
} from "#/state/microagent-management-slice";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface MicroagentManagementSidebarProps {
|
||||
isSmallerScreen?: boolean;
|
||||
}
|
||||
|
||||
export function MicroagentManagementSidebar({
|
||||
isSmallerScreen = false,
|
||||
}: MicroagentManagementSidebarProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { data: repositories, isLoading } = useUserRepositories();
|
||||
|
||||
useEffect(() => {
|
||||
if (repositories) {
|
||||
const personalRepos: GitRepository[] = [];
|
||||
const organizationRepos: GitRepository[] = [];
|
||||
const otherRepos: GitRepository[] = [];
|
||||
|
||||
repositories.forEach((repo: GitRepository) => {
|
||||
const hasOpenHandsSuffix = repo.full_name.endsWith("/.openhands");
|
||||
|
||||
if (repo.owner_type === "user" && hasOpenHandsSuffix) {
|
||||
personalRepos.push(repo);
|
||||
} else if (repo.owner_type === "organization" && hasOpenHandsSuffix) {
|
||||
organizationRepos.push(repo);
|
||||
} else {
|
||||
otherRepos.push(repo);
|
||||
}
|
||||
});
|
||||
|
||||
dispatch(setPersonalRepositories(personalRepos));
|
||||
dispatch(setOrganizationRepositories(organizationRepos));
|
||||
dispatch(setRepositories(otherRepos));
|
||||
}
|
||||
}, [repositories, dispatch]);
|
||||
|
||||
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">
|
||||
<div
|
||||
className={cn(
|
||||
"w-[418px] h-full max-h-full overflow-y-auto overflow-x-hidden border-r border-[#525252] bg-[#24272E] rounded-tl-lg rounded-bl-lg py-10 px-6 flex flex-col",
|
||||
isSmallerScreen && "w-full border-none",
|
||||
)}
|
||||
>
|
||||
<MicroagentManagementSidebarHeader />
|
||||
<MicroagentManagementSidebarTabs />
|
||||
{isLoading ? (
|
||||
<div className="flex flex-col items-center justify-center gap-4 flex-1">
|
||||
<Spinner size="sm" />
|
||||
<span className="text-sm text-white">
|
||||
{t("HOME$LOADING_REPOSITORIES")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<MicroagentManagementSidebarTabs />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
import { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { FaCircleInfo } from "react-icons/fa6";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { RootState } from "#/store";
|
||||
import XIcon from "#/icons/x.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { BadgeInput } from "#/components/shared/inputs/badge-input";
|
||||
import { MicroagentFormData } from "#/types/microagent-management";
|
||||
import { Branch, GitRepository } from "#/types/git";
|
||||
import { useRepositoryBranches } from "#/hooks/query/use-repository-branches";
|
||||
import {
|
||||
BranchDropdown,
|
||||
BranchLoadingState,
|
||||
BranchErrorState,
|
||||
} from "../home/repository-selection";
|
||||
|
||||
interface MicroagentManagementUpsertMicroagentModalProps {
|
||||
onConfirm: (formData: MicroagentFormData) => void;
|
||||
onCancel: () => void;
|
||||
isLoading: boolean;
|
||||
isUpdate?: boolean;
|
||||
}
|
||||
|
||||
export function MicroagentManagementUpsertMicroagentModal({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
isLoading = false,
|
||||
isUpdate = false,
|
||||
}: MicroagentManagementUpsertMicroagentModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [triggers, setTriggers] = useState<string[]>([]);
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { microagent } = selectedMicroagentItem ?? {};
|
||||
|
||||
// Add a ref to track if the branch was manually cleared by the user
|
||||
const branchManuallyClearedRef = useRef<boolean>(false);
|
||||
|
||||
// Populate form fields with existing microagent data when updating
|
||||
useEffect(() => {
|
||||
if (isUpdate && microagent) {
|
||||
setQuery(microagent.content);
|
||||
setTriggers(microagent.triggers || []);
|
||||
}
|
||||
}, [isUpdate, microagent]);
|
||||
|
||||
const {
|
||||
data: branches,
|
||||
isLoading: isLoadingBranches,
|
||||
isError: isBranchesError,
|
||||
} = useRepositoryBranches(selectedRepository?.full_name || null);
|
||||
|
||||
const branchesItems = branches?.map((branch) => ({
|
||||
key: branch.name,
|
||||
label: branch.name,
|
||||
}));
|
||||
|
||||
// Auto-select main or master branch if it exists.
|
||||
useEffect(() => {
|
||||
if (
|
||||
branches &&
|
||||
branches.length > 0 &&
|
||||
!selectedBranch &&
|
||||
!isLoadingBranches
|
||||
) {
|
||||
// Look for main or master branch
|
||||
const mainBranch = branches.find((branch) => branch.name === "main");
|
||||
const masterBranch = branches.find((branch) => branch.name === "master");
|
||||
|
||||
// Select main if it exists, otherwise select master if it exists
|
||||
if (mainBranch) {
|
||||
setSelectedBranch(mainBranch);
|
||||
} else if (masterBranch) {
|
||||
setSelectedBranch(masterBranch);
|
||||
}
|
||||
}
|
||||
}, [branches, isLoadingBranches, selectedBranch]);
|
||||
|
||||
const modalTitle = useMemo(() => {
|
||||
if (isUpdate) {
|
||||
return t(I18nKey.MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT);
|
||||
}
|
||||
|
||||
if (selectedRepository) {
|
||||
return `${t(I18nKey.MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT_TO)} ${(selectedRepository as GitRepository).full_name}`;
|
||||
}
|
||||
|
||||
return t(I18nKey.MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT);
|
||||
}, [isUpdate, selectedRepository, t]);
|
||||
|
||||
const modalDescription = useMemo(() => {
|
||||
if (isUpdate) {
|
||||
return t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION,
|
||||
);
|
||||
}
|
||||
|
||||
return t(I18nKey.MICROAGENT_MANAGEMENT$ADD_MICROAGENT_MODAL_DESCRIPTION);
|
||||
}, [isUpdate, t]);
|
||||
|
||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!query.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm({
|
||||
query: query.trim(),
|
||||
triggers,
|
||||
selectedBranch: selectedBranch?.name || "",
|
||||
microagentPath: microagent?.path || "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!query.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm({
|
||||
query: query.trim(),
|
||||
triggers,
|
||||
selectedBranch: selectedBranch?.name || "",
|
||||
microagentPath: microagent?.path || "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleBranchSelection = (key: React.Key | null) => {
|
||||
const selectedBranchObj = branches?.find((branch) => branch.name === key);
|
||||
setSelectedBranch(selectedBranchObj || null);
|
||||
// Reset the manually cleared flag when a branch is explicitly selected
|
||||
branchManuallyClearedRef.current = false;
|
||||
};
|
||||
|
||||
const handleBranchInputChange = (value: string) => {
|
||||
// Clear the selected branch if the input is empty or contains only whitespace
|
||||
// This fixes the issue where users can't delete the entire default branch name
|
||||
if (value === "" || value.trim() === "") {
|
||||
setSelectedBranch(null);
|
||||
// Set the flag to indicate that the branch was manually cleared
|
||||
branchManuallyClearedRef.current = true;
|
||||
} else {
|
||||
// Reset the flag when the user starts typing again
|
||||
branchManuallyClearedRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Render the appropriate UI for branch selector based on the loading/error state
|
||||
const renderBranchSelector = () => {
|
||||
if (!selectedRepository) {
|
||||
return (
|
||||
<BranchDropdown
|
||||
items={[]}
|
||||
onSelectionChange={() => {}}
|
||||
onInputChange={() => {}}
|
||||
isDisabled
|
||||
wrapperClassName="max-w-full w-full"
|
||||
label={t(I18nKey.REPOSITORY$SELECT_BRANCH)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoadingBranches) {
|
||||
return <BranchLoadingState wrapperClassName="max-w-full w-full" />;
|
||||
}
|
||||
|
||||
if (isBranchesError) {
|
||||
return <BranchErrorState wrapperClassName="max-w-full w-full" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<BranchDropdown
|
||||
items={branchesItems || []}
|
||||
onSelectionChange={handleBranchSelection}
|
||||
onInputChange={handleBranchInputChange}
|
||||
isDisabled={false}
|
||||
selectedKey={selectedBranch?.name}
|
||||
wrapperClassName="max-w-full w-full"
|
||||
label={t(I18nKey.REPOSITORY$SELECT_BRANCH)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalBackdrop onClose={onCancel}>
|
||||
<ModalBody className="items-start rounded-[12px] p-6 min-w-[611px]">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-white text-xl font-medium">{modalTitle}</h2>
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/prompting/microagents-overview#microagents-overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
<button type="button" onClick={onCancel} className="cursor-pointer">
|
||||
<XIcon width={24} height={24} color="#F9FBFE" />
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-white text-sm font-normal">
|
||||
{modalDescription}
|
||||
</span>
|
||||
</div>
|
||||
<form
|
||||
data-testid="add-microagent-modal"
|
||||
onSubmit={onSubmit}
|
||||
className="flex flex-col gap-6 w-full"
|
||||
>
|
||||
{renderBranchSelector()}
|
||||
<label
|
||||
htmlFor="query-input"
|
||||
className="flex flex-col gap-2 w-full text-sm font-normal"
|
||||
>
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$WHAT_TO_DO)}
|
||||
<textarea
|
||||
required
|
||||
data-testid="query-input"
|
||||
name="query-input"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t(I18nKey.MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_DO)}
|
||||
rows={6}
|
||||
className={cn(
|
||||
"bg-tertiary border border-[#717888] bg-[#454545] w-full rounded-sm p-2 placeholder:italic placeholder:text-tertiary-alt resize-none",
|
||||
"disabled:bg-[#2D2F36] disabled:border-[#2D2F36] disabled:cursor-not-allowed",
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="trigger-input"
|
||||
className="flex flex-col gap-2.5 w-full text-sm"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{t(I18nKey.MICROAGENT_MANAGEMENT$ADD_TRIGGERS)}
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/prompting/microagents-keyword"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaCircleInfo className="text-primary" />
|
||||
</a>
|
||||
</div>
|
||||
<BadgeInput
|
||||
name="trigger-input"
|
||||
value={triggers}
|
||||
placeholder={t("MICROAGENT$TYPE_TRIGGER_SPACE")}
|
||||
onChange={setTriggers}
|
||||
/>
|
||||
<span className="text-xs text-[#ffffff80] font-normal">
|
||||
{t(
|
||||
I18nKey.MICROAGENT_MANAGEMENT$HELP_TEXT_DESCRIBING_VALID_TRIGGERS,
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
<div
|
||||
className="flex items-center justify-end gap-2 w-full"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onCancel}
|
||||
testId="cancel-button"
|
||||
>
|
||||
{t(I18nKey.BUTTON$CANCEL)}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleConfirm}
|
||||
testId="confirm-button"
|
||||
isDisabled={
|
||||
!query.trim() ||
|
||||
isLoading ||
|
||||
isLoadingBranches ||
|
||||
!selectedBranch ||
|
||||
isBranchesError
|
||||
}
|
||||
>
|
||||
{isLoading || isLoadingBranches
|
||||
? t(I18nKey.HOME$LOADING)
|
||||
: t(I18nKey.MICROAGENT$LAUNCH)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import { code } from "../markdown/code";
|
||||
import { ul, ol } from "../markdown/list";
|
||||
import { paragraph } from "../markdown/paragraph";
|
||||
import { anchor } from "../markdown/anchor";
|
||||
import { RootState } from "#/store";
|
||||
|
||||
export function MicroagentManagementViewMicroagentContent() {
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { microagent } = selectedMicroagentItem ?? {};
|
||||
|
||||
const transformMicroagentContent = (): string => {
|
||||
if (!microagent) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// If no triggers exist, return the content as-is
|
||||
if (!microagent.triggers || microagent.triggers.length === 0) {
|
||||
return microagent.content;
|
||||
}
|
||||
|
||||
// Create the triggers frontmatter
|
||||
const triggersFrontmatter = `
|
||||
---
|
||||
|
||||
triggers:
|
||||
${microagent.triggers.map((trigger) => ` - ${trigger}`).join("\n")}
|
||||
|
||||
---
|
||||
`;
|
||||
|
||||
// Prepend the frontmatter to the content
|
||||
return `
|
||||
${triggersFrontmatter}
|
||||
|
||||
${microagent.content}
|
||||
`;
|
||||
};
|
||||
|
||||
if (!microagent || !selectedRepository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Transform the content to include triggers frontmatter if applicable
|
||||
const transformedContent = transformMicroagentContent();
|
||||
|
||||
return (
|
||||
<div className="w-full h-full p-6 bg-[#ffffff1a] rounded-2xl text-white text-sm">
|
||||
<Markdown
|
||||
components={{
|
||||
code,
|
||||
ul,
|
||||
ol,
|
||||
a: anchor,
|
||||
p: paragraph,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
>
|
||||
{transformedContent}
|
||||
</Markdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RootState } from "#/store";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import { getProviderName, constructMicroagentUrl } from "#/utils/utils";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { setUpdateMicroagentModalVisible } from "#/state/microagent-management-slice";
|
||||
|
||||
export function MicroagentManagementViewMicroagentHeader() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { microagent } = selectedMicroagentItem ?? {};
|
||||
|
||||
if (!microagent || !selectedRepository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Construct the microagent URL
|
||||
const microagentUrl = constructMicroagentUrl(
|
||||
selectedRepository.git_provider,
|
||||
selectedRepository.full_name,
|
||||
microagent.path,
|
||||
);
|
||||
|
||||
const handleLearnSomethingNew = () => {
|
||||
dispatch(setUpdateMicroagentModalVisible(true));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between pb-2">
|
||||
<span className="text-sm text-[#ffffff99]">
|
||||
{selectedRepository.full_name}
|
||||
</span>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<a href={microagentUrl} target="_blank" rel="noopener noreferrer">
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="secondary"
|
||||
testId="edit-in-git-button"
|
||||
className="py-1 px-2"
|
||||
>
|
||||
{`${t(I18nKey.COMMON$EDIT_IN)} ${getProviderName(selectedRepository.git_provider)}`}
|
||||
</BrandButton>
|
||||
</a>
|
||||
<BrandButton
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleLearnSomethingNew}
|
||||
testId="learn-button"
|
||||
className="py-1 px-2"
|
||||
>
|
||||
{t(I18nKey.COMMON$LEARN_SOMETHING_NEW)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { MicroagentManagementViewMicroagentHeader } from "./microagent-management-view-microagent-header";
|
||||
import { MicroagentManagementViewMicroagentContent } from "./microagent-management-view-microagent-content";
|
||||
|
||||
export function MicroagentManagementViewMicroagent() {
|
||||
const { selectedMicroagentItem } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const { microagent } = selectedMicroagentItem ?? {};
|
||||
|
||||
if (!microagent || !selectedRepository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full p-6 overflow-auto">
|
||||
<MicroagentManagementViewMicroagentHeader />
|
||||
<span className="text-white text-2xl font-medium pb-2">
|
||||
{microagent.name}
|
||||
</span>
|
||||
<span className="text-white text-lg font-medium pb-6">
|
||||
{microagent.path}
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<MicroagentManagementViewMicroagentContent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { useTranslation } from "react-i18next";
|
||||
import SettingsIcon from "#/icons/settings.svg?react";
|
||||
import { TooltipButton } from "./tooltip-button";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
|
||||
interface SettingsButtonProps {
|
||||
onClick?: () => void;
|
||||
@@ -13,6 +14,12 @@ export function SettingsButton({
|
||||
disabled = false,
|
||||
}: SettingsButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data: config } = useConfig();
|
||||
|
||||
// Determine the correct settings path based on app mode
|
||||
// In SaaS mode, navigate directly to user settings to avoid the LLM settings page
|
||||
const settingsPath =
|
||||
config?.APP_MODE === "saas" ? "/settings/user" : "/settings";
|
||||
|
||||
return (
|
||||
<TooltipButton
|
||||
@@ -20,7 +27,7 @@ export function SettingsButton({
|
||||
tooltip={t(I18nKey.SETTINGS$TITLE)}
|
||||
ariaLabel={t(I18nKey.SETTINGS$TITLE)}
|
||||
onClick={onClick}
|
||||
navLinkTo="/settings"
|
||||
navLinkTo={settingsPath}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SettingsIcon width={28} height={28} />
|
||||
|
||||
16
frontend/src/components/shared/git-provider-icon.tsx
Normal file
16
frontend/src/components/shared/git-provider-icon.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { FaBitbucket, FaGithub, FaGitlab } from "react-icons/fa6";
|
||||
import { Provider } from "#/types/settings";
|
||||
|
||||
interface GitProviderIconProps {
|
||||
gitProvider: Provider;
|
||||
}
|
||||
|
||||
export function GitProviderIcon({ gitProvider }: GitProviderIconProps) {
|
||||
return (
|
||||
<>
|
||||
{gitProvider === "github" && <FaGithub size={14} />}
|
||||
{gitProvider === "gitlab" && <FaGitlab />}
|
||||
{gitProvider === "bitbucket" && <FaBitbucket />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
25
frontend/src/components/shared/loader.tsx
Normal file
25
frontend/src/components/shared/loader.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface LoaderProps {
|
||||
size?: "small" | "medium" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Loader({ size = "medium", className }: LoaderProps) {
|
||||
const sizeClasses = {
|
||||
small: "w-3 h-3",
|
||||
medium: "w-4 h-4",
|
||||
large: "w-5 h-5",
|
||||
};
|
||||
|
||||
const dotSize = sizeClasses[size];
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="loader"
|
||||
className={cn("flex items-center justify-center", className)}
|
||||
>
|
||||
<div className={cn("loader rounded-full", dotSize)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import posthog from "posthog-js";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { SuggestedTask } from "#/components/features/home/tasks/task.types";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { CreateMicroagent } from "#/api/open-hands.types";
|
||||
|
||||
interface CreateConversationVariables {
|
||||
query?: string;
|
||||
@@ -13,6 +14,7 @@ interface CreateConversationVariables {
|
||||
};
|
||||
suggestedTask?: SuggestedTask;
|
||||
conversationInstructions?: string;
|
||||
createMicroagent?: CreateMicroagent;
|
||||
}
|
||||
|
||||
export const useCreateConversation = () => {
|
||||
@@ -21,8 +23,13 @@ export const useCreateConversation = () => {
|
||||
return useMutation({
|
||||
mutationKey: ["create-conversation"],
|
||||
mutationFn: async (variables: CreateConversationVariables) => {
|
||||
const { query, repository, suggestedTask, conversationInstructions } =
|
||||
variables;
|
||||
const {
|
||||
query,
|
||||
repository,
|
||||
suggestedTask,
|
||||
conversationInstructions,
|
||||
createMicroagent,
|
||||
} = variables;
|
||||
|
||||
return OpenHands.createConversation(
|
||||
repository?.name,
|
||||
@@ -31,6 +38,7 @@ export const useCreateConversation = () => {
|
||||
suggestedTask,
|
||||
repository?.branch,
|
||||
conversationInstructions,
|
||||
createMicroagent,
|
||||
);
|
||||
},
|
||||
onSuccess: async (_, { query, repository }) => {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConfig } from "./use-config";
|
||||
import { useIsAuthed } from "./use-is-authed";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useUserProviders } from "../use-user-providers";
|
||||
|
||||
export const useAppInstallations = () => {
|
||||
const { data: config } = useConfig();
|
||||
const { data: userIsAuthenticated } = useIsAuthed();
|
||||
const { providers } = useUserProviders();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["installations", providers, config?.GITHUB_CLIENT_ID],
|
||||
queryFn: OpenHands.getGitHubUserInstallationIds,
|
||||
enabled:
|
||||
userIsAuthenticated &&
|
||||
providers.includes("github") &&
|
||||
!!config?.GITHUB_CLIENT_ID &&
|
||||
config?.APP_MODE === "saas",
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConfig } from "./use-config";
|
||||
import { useIsAuthed } from "./use-is-authed";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useUserProviders } from "../use-user-providers";
|
||||
|
||||
export const useBitbucketWorkspaces = () => {
|
||||
const { data: config } = useConfig();
|
||||
const { data: userIsAuthenticated } = useIsAuthed();
|
||||
const { providers } = useUserProviders();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["workspaces", providers],
|
||||
queryFn: OpenHands.getBitBucketWorkspaces,
|
||||
enabled:
|
||||
userIsAuthenticated &&
|
||||
providers.includes("bitbucket") &&
|
||||
config?.APP_MODE === "saas",
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
};
|
||||
15
frontend/src/hooks/query/use-repository-microagents.ts
Normal file
15
frontend/src/hooks/query/use-repository-microagents.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
export const useRepositoryMicroagents = (
|
||||
owner: string,
|
||||
repo: string,
|
||||
cacheDisabled: boolean = false,
|
||||
) =>
|
||||
useQuery({
|
||||
queryKey: ["repository", "microagents", owner, repo],
|
||||
queryFn: () => OpenHands.getRepositoryMicroagents(owner, repo),
|
||||
enabled: !!owner && !!repo,
|
||||
staleTime: cacheDisabled ? 0 : 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: cacheDisabled ? 0 : 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
27
frontend/src/hooks/query/use-search-conversations.ts
Normal file
27
frontend/src/hooks/query/use-search-conversations.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
export const useSearchConversations = (
|
||||
selectedRepository?: string,
|
||||
conversationTrigger?: string,
|
||||
limit: number = 20,
|
||||
cacheDisabled: boolean = false,
|
||||
) =>
|
||||
useQuery({
|
||||
queryKey: [
|
||||
"conversations",
|
||||
"search",
|
||||
selectedRepository,
|
||||
conversationTrigger,
|
||||
limit,
|
||||
],
|
||||
queryFn: () =>
|
||||
OpenHands.searchConversations(
|
||||
selectedRepository,
|
||||
conversationTrigger,
|
||||
limit,
|
||||
),
|
||||
enabled: true, // Always enabled since parameters are optional
|
||||
staleTime: cacheDisabled ? 0 : 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: cacheDisabled ? 0 : 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { useCreateConversation } from "./mutation/use-create-conversation";
|
||||
import { useUserProviders } from "./use-user-providers";
|
||||
import { useConversationSubscriptions } from "#/context/conversation-subscriptions-provider";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { CreateMicroagent } from "#/api/open-hands.types";
|
||||
|
||||
/**
|
||||
* Custom hook to create a conversation and subscribe to it, supporting multiple subscriptions.
|
||||
@@ -24,6 +25,7 @@ export const useCreateConversationAndSubscribeMultiple = () => {
|
||||
query,
|
||||
conversationInstructions,
|
||||
repository,
|
||||
createMicroagent,
|
||||
onSuccessCallback,
|
||||
onEventCallback,
|
||||
}: {
|
||||
@@ -34,6 +36,7 @@ export const useCreateConversationAndSubscribeMultiple = () => {
|
||||
branch: string;
|
||||
gitProvider: Provider;
|
||||
};
|
||||
createMicroagent?: CreateMicroagent;
|
||||
onSuccessCallback?: (conversationId: string) => void;
|
||||
onEventCallback?: (event: unknown, conversationId: string) => void;
|
||||
}) => {
|
||||
@@ -42,6 +45,7 @@ export const useCreateConversationAndSubscribeMultiple = () => {
|
||||
query,
|
||||
conversationInstructions,
|
||||
repository,
|
||||
createMicroagent,
|
||||
},
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
|
||||
@@ -12,6 +12,7 @@ export enum I18nKey {
|
||||
MICROAGENT$VIEW_CONVERSATION = "MICROAGENT$VIEW_CONVERSATION",
|
||||
MICROAGENT$SUCCESS_PR_READY = "MICROAGENT$SUCCESS_PR_READY",
|
||||
MICROAGENT$STATUS_CREATING = "MICROAGENT$STATUS_CREATING",
|
||||
MICROAGENT$STATUS_OPENING_PR = "MICROAGENT$STATUS_OPENING_PR",
|
||||
MICROAGENT$STATUS_COMPLETED = "MICROAGENT$STATUS_COMPLETED",
|
||||
MICROAGENT$STATUS_ERROR = "MICROAGENT$STATUS_ERROR",
|
||||
MICROAGENT$VIEW_YOUR_PR = "MICROAGENT$VIEW_YOUR_PR",
|
||||
@@ -698,6 +699,7 @@ export enum I18nKey {
|
||||
MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES = "MICROAGENT_MANAGEMENT$OPENHANDS_CAN_LEARN_ABOUT_REPOSITORIES",
|
||||
MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT_TO = "MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT_TO",
|
||||
MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT = "MICROAGENT_MANAGEMENT$ADD_A_MICROAGENT",
|
||||
MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT = "MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT",
|
||||
MICROAGENT_MANAGEMENT$ADD_MICROAGENT_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$ADD_MICROAGENT_MODAL_DESCRIPTION",
|
||||
MICROAGENT_MANAGEMENT$WHAT_TO_DO = "MICROAGENT_MANAGEMENT$WHAT_TO_DO",
|
||||
MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_DO = "MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_DO",
|
||||
@@ -708,4 +710,27 @@ export enum I18nKey {
|
||||
COMMON$RUN_TEST = "COMMON$RUN_TEST",
|
||||
COMMON$RUN_APP = "COMMON$RUN_APP",
|
||||
COMMON$LEARN_FILE_STRUCTURE = "COMMON$LEARN_FILE_STRUCTURE",
|
||||
MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS",
|
||||
MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS",
|
||||
MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS",
|
||||
COMMON$SEARCH_REPOSITORIES = "COMMON$SEARCH_REPOSITORIES",
|
||||
COMMON$READY_FOR_REVIEW = "COMMON$READY_FOR_REVIEW",
|
||||
COMMON$COMPLETED = "COMMON$COMPLETED",
|
||||
COMMON$COMPLETED_PARTIALLY = "COMMON$COMPLETED_PARTIALLY",
|
||||
COMMON$STOPPED = "COMMON$STOPPED",
|
||||
COMMON$WORKING_ON_IT = "COMMON$WORKING_ON_IT",
|
||||
MICROAGENT_MANAGEMENT$WE_ARE_WORKING_ON_IT = "MICROAGENT_MANAGEMENT$WE_ARE_WORKING_ON_IT",
|
||||
MICROAGENT_MANAGEMENT$YOUR_MICROAGENT_IS_READY = "MICROAGENT_MANAGEMENT$YOUR_MICROAGENT_IS_READY",
|
||||
COMMON$REVIEW_PR_IN = "COMMON$REVIEW_PR_IN",
|
||||
COMMON$EDIT_IN = "COMMON$EDIT_IN",
|
||||
COMMON$LEARN = "COMMON$LEARN",
|
||||
COMMON$LEARN_SOMETHING_NEW = "COMMON$LEARN_SOMETHING_NEW",
|
||||
COMMON$STARTING = "COMMON$STARTING",
|
||||
MICROAGENT_MANAGEMENT$ERROR = "MICROAGENT_MANAGEMENT$ERROR",
|
||||
MICROAGENT_MANAGEMENT$CONVERSATION_STOPPED = "MICROAGENT_MANAGEMENT$CONVERSATION_STOPPED",
|
||||
MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_TITLE = "MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_TITLE",
|
||||
MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_DESCRIPTION",
|
||||
MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO",
|
||||
MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO",
|
||||
MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION",
|
||||
}
|
||||
|
||||
@@ -191,6 +191,22 @@
|
||||
"de": "Microagent wird geändert...",
|
||||
"uk": "Зміна мікроагента..."
|
||||
},
|
||||
"MICROAGENT$STATUS_OPENING_PR": {
|
||||
"en": "Opening PR",
|
||||
"ja": "PRを開いています",
|
||||
"zh-CN": "正在打开PR",
|
||||
"zh-TW": "正在打開PR",
|
||||
"ko-KR": "PR 열는 중",
|
||||
"no": "Åpner PR",
|
||||
"it": "Apertura PR",
|
||||
"pt": "Abrindo PR",
|
||||
"es": "Abriendo PR",
|
||||
"ar": "فتح PR",
|
||||
"fr": "Ouverture de la PR",
|
||||
"tr": "PR açılıyor",
|
||||
"de": "PR wird geöffnet",
|
||||
"uk": "Відкриття PR"
|
||||
},
|
||||
"MICROAGENT$STATUS_COMPLETED": {
|
||||
"en": "View microagent update",
|
||||
"ja": "マイクロエージェントの更新を表示",
|
||||
@@ -11167,6 +11183,22 @@
|
||||
"de": "Microagent hinzufügen",
|
||||
"uk": "Додати мікроагента"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT": {
|
||||
"en": "Update microagent",
|
||||
"ja": "マイクロエージェントを更新",
|
||||
"zh-CN": "更新微代理",
|
||||
"zh-TW": "更新微代理",
|
||||
"ko-KR": "마이크로에이전트 업데이트",
|
||||
"no": "Oppdater mikroagent",
|
||||
"it": "Aggiorna microagent",
|
||||
"pt": "Atualizar microagente",
|
||||
"es": "Actualizar microagente",
|
||||
"ar": "تحديث الوكيل الصغير",
|
||||
"fr": "Mettre à jour le microagent",
|
||||
"tr": "Mikro ajanı güncelle",
|
||||
"de": "Microagent aktualisieren",
|
||||
"uk": "Оновити мікроагента"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$ADD_MICROAGENT_MODAL_DESCRIPTION": {
|
||||
"en": "OpenHands will create a new microagent based on your instructions.",
|
||||
"ja": "OpenHandsはあなたの指示に基づいて新しいマイクロエージェントを作成します。",
|
||||
@@ -11197,7 +11229,7 @@
|
||||
"fr": "Que souhaitez-vous que le microagent fasse ?",
|
||||
"tr": "Mikro ajanın ne yapmasını istersiniz?",
|
||||
"de": "Was soll der Microagent tun?",
|
||||
"uk": "Що ви хочете, щоб зробив мікроагент?"
|
||||
"uk": "Що в,и хочете, щоб зробив мікроагент?"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_DO": {
|
||||
"en": "Describe what you would like the Microagent to do.",
|
||||
@@ -11326,5 +11358,373 @@
|
||||
"tr": "Dosya yapısını öğren",
|
||||
"de": "Dateistruktur lernen",
|
||||
"uk": "Вивчити структуру файлів"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS": {
|
||||
"en": "You do not have user-level microagents",
|
||||
"ja": "ユーザーレベルのマイクロエージェントがありません",
|
||||
"zh-CN": "您没有用户级微代理",
|
||||
"zh-TW": "您沒有使用者層級的微代理",
|
||||
"ko-KR": "사용자 수준의 마이크로에이전트가 없습니다",
|
||||
"no": "Du har ikke mikroagenter på brukernivå",
|
||||
"it": "Non hai microagenti a livello utente",
|
||||
"pt": "Você não possui microagentes de nível de usuário",
|
||||
"es": "No tienes microagentes a nivel de usuario",
|
||||
"ar": "ليس لديك وكلاء دقيقون على مستوى المستخدم",
|
||||
"fr": "Vous n'avez pas de microagents au niveau utilisateur",
|
||||
"tr": "Kullanıcı düzeyinde mikro ajanınız yok",
|
||||
"de": "Sie haben keine Mikroagenten auf Benutzerebene",
|
||||
"uk": "У вас немає мікроагентів на рівні користувача"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS": {
|
||||
"en": "You do not have microagents",
|
||||
"ja": "マイクロエージェントがありません",
|
||||
"zh-CN": "您没有微代理",
|
||||
"zh-TW": "您沒有微代理",
|
||||
"ko-KR": "마이크로에이전트가 없습니다",
|
||||
"no": "Du har ingen mikroagenter",
|
||||
"it": "Non hai microagenti",
|
||||
"pt": "Você não possui microagentes",
|
||||
"es": "No tienes microagentes",
|
||||
"ar": "ليس لديك وكلاء دقيقون",
|
||||
"fr": "Vous n'avez pas de microagents",
|
||||
"tr": "Mikro ajanınız yok",
|
||||
"de": "Sie haben keine Mikroagenten",
|
||||
"uk": "У вас немає мікроагентів"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS": {
|
||||
"en": "You do not have organization-level microagents",
|
||||
"ja": "組織レベルのマイクロエージェントがありません",
|
||||
"zh-CN": "您没有组织级微代理",
|
||||
"zh-TW": "您沒有組織層級的微代理",
|
||||
"ko-KR": "조직 수준의 마이크로에이전트가 없습니다",
|
||||
"no": "Du har ikke mikroagenter på organisasjonsnivå",
|
||||
"it": "Non hai microagenti a livello organizzazione",
|
||||
"pt": "Você não possui microagentes de nível organizacional",
|
||||
"es": "No tienes microagentes a nivel de organización",
|
||||
"ar": "ليس لديك وكلاء دقيقون على مستوى المؤسسة",
|
||||
"fr": "Vous n'avez pas de microagents au niveau organisation",
|
||||
"tr": "Organizasyon düzeyinde mikro ajanınız yok",
|
||||
"de": "Sie haben keine Mikroagenten auf Organisationsebene",
|
||||
"uk": "У вас немає мікроагентів на рівні організації"
|
||||
},
|
||||
"COMMON$SEARCH_REPOSITORIES": {
|
||||
"en": "Search repositories",
|
||||
"ja": "リポジトリを検索",
|
||||
"zh-CN": "搜索仓库",
|
||||
"zh-TW": "搜尋存儲庫",
|
||||
"ko-KR": "저장소 검색",
|
||||
"no": "Søk i repositories",
|
||||
"it": "Cerca repository",
|
||||
"pt": "Pesquisar repositórios",
|
||||
"es": "Buscar repositorios",
|
||||
"ar": "البحث في المستودعات",
|
||||
"fr": "Rechercher des dépôts",
|
||||
"tr": "Depo ara",
|
||||
"de": "Repositorys durchsuchen",
|
||||
"uk": "Пошук репозиторіїв"
|
||||
},
|
||||
"COMMON$READY_FOR_REVIEW": {
|
||||
"en": "Ready for review",
|
||||
"ja": "レビューの準備ができました",
|
||||
"zh-CN": "准备好审核",
|
||||
"zh-TW": "已準備好審查",
|
||||
"ko-KR": "검토 준비 완료",
|
||||
"no": "Klar for gjennomgang",
|
||||
"it": "Pronto per la revisione",
|
||||
"pt": "Pronto para revisão",
|
||||
"es": "Listo para revisión",
|
||||
"ar": "جاهز للمراجعة",
|
||||
"fr": "Prêt pour la relecture",
|
||||
"tr": "İncelemeye hazır",
|
||||
"de": "Bereit zur Überprüfung",
|
||||
"uk": "Готово до перегляду"
|
||||
},
|
||||
"COMMON$COMPLETED": {
|
||||
"en": "Completed",
|
||||
"ja": "完了",
|
||||
"zh-CN": "已完成",
|
||||
"zh-TW": "已完成",
|
||||
"ko-KR": "완료됨",
|
||||
"no": "Fullført",
|
||||
"it": "Completato",
|
||||
"pt": "Concluído",
|
||||
"es": "Completado",
|
||||
"ar": "مكتمل",
|
||||
"fr": "Terminé",
|
||||
"tr": "Tamamlandı",
|
||||
"de": "Abgeschlossen",
|
||||
"uk": "Завершено"
|
||||
},
|
||||
"COMMON$COMPLETED_PARTIALLY": {
|
||||
"en": "Completed partially",
|
||||
"ja": "一部完了",
|
||||
"zh-CN": "部分完成",
|
||||
"zh-TW": "部分完成",
|
||||
"ko-KR": "부분적으로 완료됨",
|
||||
"no": "Delvis fullført",
|
||||
"it": "Completato parzialmente",
|
||||
"pt": "Concluído parcialmente",
|
||||
"es": "Completado parcialmente",
|
||||
"ar": "مكتمل جزئيًا",
|
||||
"fr": "Partiellement terminé",
|
||||
"tr": "Kısmen tamamlandı",
|
||||
"de": "Teilweise abgeschlossen",
|
||||
"uk": "Частково завершено"
|
||||
},
|
||||
"COMMON$STOPPED": {
|
||||
"en": "Stopped",
|
||||
"ja": "停止しました",
|
||||
"zh-CN": "已停止",
|
||||
"zh-TW": "已停止",
|
||||
"ko-KR": "중지됨",
|
||||
"no": "Stoppet",
|
||||
"it": "Interrotto",
|
||||
"pt": "Parado",
|
||||
"es": "Detenido",
|
||||
"ar": "متوقف",
|
||||
"fr": "Arrêté",
|
||||
"tr": "Durduruldu",
|
||||
"de": "Gestoppt",
|
||||
"uk": "Зупинено"
|
||||
},
|
||||
"COMMON$WORKING_ON_IT": {
|
||||
"en": "Working on it",
|
||||
"ja": "作業中",
|
||||
"zh-CN": "正在处理",
|
||||
"zh-TW": "正在處理",
|
||||
"ko-KR": "작업 중",
|
||||
"no": "Jobber med det",
|
||||
"it": "Ci sto lavorando",
|
||||
"pt": "Trabalhando nisso",
|
||||
"es": "Trabajando en ello",
|
||||
"ar": "يتم العمل عليه",
|
||||
"fr": "En cours",
|
||||
"tr": "Üzerinde çalışılıyor",
|
||||
"de": "Wird bearbeitet",
|
||||
"uk": "В процесі виконання"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$WE_ARE_WORKING_ON_IT": {
|
||||
"en": "We're working on it! Once OpenHands is done investigating, you'll be able to review its pull request before merging your new microagent.",
|
||||
"ja": "作業中です!OpenHandsの調査が完了すると、新しいマイクロエージェントをマージする前にプルリクエストを確認できます。",
|
||||
"zh-CN": "我们正在处理!OpenHands 调查完成后,您将能够在合并新微代理之前审查其拉取请求。",
|
||||
"zh-TW": "我們正在處理!OpenHands 調查完成後,您將能在合併新微代理前審查其拉取請求。",
|
||||
"ko-KR": "작업 중입니다! OpenHands의 조사가 끝나면 새 마이크로에이전트를 병합하기 전에 풀 리퀘스트를 검토할 수 있습니다.",
|
||||
"no": "Vi jobber med det! Når OpenHands er ferdig med å undersøke, kan du gjennomgå pull requesten før du slår sammen din nye mikroagent.",
|
||||
"it": "Ci stiamo lavorando! Una volta che OpenHands avrà terminato l'analisi, potrai rivedere la pull request prima di unire il tuo nuovo microagent.",
|
||||
"pt": "Estamos trabalhando nisso! Assim que o OpenHands terminar a investigação, você poderá revisar o pull request antes de mesclar seu novo microagente.",
|
||||
"es": "¡Estamos trabajando en ello! Una vez que OpenHands termine de investigar, podrás revisar su pull request antes de fusionar tu nuevo microagente.",
|
||||
"ar": "نحن نعمل على ذلك! بمجرد أن ينتهي OpenHands من التحقيق، ستتمكن من مراجعة طلب السحب قبل دمج وكيلك الدقيق الجديد.",
|
||||
"fr": "Nous y travaillons ! Une fois qu'OpenHands aura terminé l'investigation, vous pourrez examiner sa pull request avant de fusionner votre nouveau microagent.",
|
||||
"tr": "Üzerinde çalışıyoruz! OpenHands incelemeyi bitirdiğinde, yeni mikro ajanınızı birleştirmeden önce pull request'i gözden geçirebileceksiniz.",
|
||||
"de": "Wir arbeiten daran! Sobald OpenHands die Untersuchung abgeschlossen hat, können Sie den Pull Request überprüfen, bevor Sie Ihren neuen Microagenten zusammenführen.",
|
||||
"uk": "Ми працюємо над цим! Після завершення розслідування OpenHands ви зможете переглянути його pull request перед об'єднанням нового мікроагента."
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$YOUR_MICROAGENT_IS_READY": {
|
||||
"en": "Your microagent is ready! Merge the PR in GitHub to start using it.",
|
||||
"ja": "マイクロエージェントの準備ができました!GitHubでPRをマージして使い始めましょう。",
|
||||
"zh-CN": "您的微代理已准备就绪!在 GitHub 上合并 PR 即可开始使用。",
|
||||
"zh-TW": "您的微代理已準備就緒!在 GitHub 上合併 PR 即可開始使用。",
|
||||
"ko-KR": "마이크로에이전트가 준비되었습니다! GitHub에서 PR을 병합하여 사용을 시작하세요.",
|
||||
"no": "Din mikroagent er klar! Slå sammen PR-en i GitHub for å begynne å bruke den.",
|
||||
"it": "Il tuo microagente è pronto! Unisci la PR su GitHub per iniziare a usarlo.",
|
||||
"pt": "Seu microagente está pronto! Faça o merge do PR no GitHub para começar a usá-lo.",
|
||||
"es": "¡Tu microagente está listo! Haz merge del PR en GitHub para empezar a usarlo.",
|
||||
"ar": "وكيلك المصغر جاهز! ادمج طلب السحب في GitHub لبدء استخدامه.",
|
||||
"fr": "Votre micro-agent est prêt ! Fusionnez la PR sur GitHub pour commencer à l'utiliser.",
|
||||
"tr": "Mikro ajanınız hazır! Kullanmak için GitHub'da PR'ı birleştirin.",
|
||||
"de": "Ihr Microagent ist bereit! Führen Sie den PR in GitHub zusammen, um ihn zu verwenden.",
|
||||
"uk": "Ваш мікроагент готовий! Злийте PR у GitHub, щоб почати ним користуватися."
|
||||
},
|
||||
"COMMON$REVIEW_PR_IN": {
|
||||
"en": "Review PR in",
|
||||
"ja": "でPRをレビュー",
|
||||
"zh-CN": "在中审查PR",
|
||||
"zh-TW": "在中審查PR",
|
||||
"ko-KR": "에서 PR 검토",
|
||||
"no": "Se gjennom PR i",
|
||||
"it": "Revisiona la PR su",
|
||||
"pt": "Revisar PR em",
|
||||
"es": "Revisar PR en",
|
||||
"ar": "مراجعة PR في",
|
||||
"fr": "Examiner la PR sur",
|
||||
"tr": "PR'ı şurada gözden geçir:",
|
||||
"de": "PR überprüfen in",
|
||||
"uk": "Переглянути PR у"
|
||||
},
|
||||
"COMMON$EDIT_IN": {
|
||||
"en": "Edit in",
|
||||
"ja": "で編集",
|
||||
"zh-CN": "在中编辑",
|
||||
"zh-TW": "在中編輯",
|
||||
"ko-KR": "에서 편집",
|
||||
"no": "Rediger i",
|
||||
"it": "Modifica su",
|
||||
"pt": "Editar em",
|
||||
"es": "Editar en",
|
||||
"ar": "تعديل في",
|
||||
"fr": "Modifier dans",
|
||||
"tr": "Şurada düzenle:",
|
||||
"de": "Bearbeiten in",
|
||||
"uk": "Редагувати у"
|
||||
},
|
||||
"COMMON$LEARN": {
|
||||
"en": "Learn",
|
||||
"ja": "学ぶ",
|
||||
"zh-CN": "学习",
|
||||
"zh-TW": "學習",
|
||||
"ko-KR": "학습",
|
||||
"no": "Lær",
|
||||
"it": "Impara",
|
||||
"pt": "Aprender",
|
||||
"es": "Aprender",
|
||||
"ar": "تعلم",
|
||||
"fr": "Apprendre",
|
||||
"tr": "Öğren",
|
||||
"de": "Lernen",
|
||||
"uk": "Вчитися"
|
||||
},
|
||||
"COMMON$LEARN_SOMETHING_NEW": {
|
||||
"en": "Learn something new",
|
||||
"ja": "新しいことを学ぶ",
|
||||
"zh-CN": "学习新东西",
|
||||
"zh-TW": "學習新東西",
|
||||
"ko-KR": "새로운 것을 배우기",
|
||||
"no": "Lær noe nytt",
|
||||
"it": "Impara qualcosa di nuovo",
|
||||
"pt": "Aprender algo novo",
|
||||
"es": "Aprender algo nuevo",
|
||||
"ar": "تعلم شيئًا جديدًا",
|
||||
"fr": "Apprendre quelque chose de nouveau",
|
||||
"tr": "Yeni bir şey öğren",
|
||||
"de": "Etwas Neues lernen",
|
||||
"uk": "Вивчити щось нове"
|
||||
},
|
||||
"COMMON$STARTING": {
|
||||
"en": "Starting",
|
||||
"ja": "開始中",
|
||||
"zh-CN": "启动中",
|
||||
"zh-TW": "啟動中",
|
||||
"ko-KR": "시작 중",
|
||||
"no": "Starter",
|
||||
"it": "Avvio",
|
||||
"pt": "Iniciando",
|
||||
"es": "Iniciando",
|
||||
"ar": "جارٍ البدء",
|
||||
"fr": "Démarrage",
|
||||
"tr": "Başlatılıyor",
|
||||
"de": "Wird gestartet",
|
||||
"uk": "Запуск"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$ERROR": {
|
||||
"en": "The system has encountered an error. Please try again later.",
|
||||
"ja": "システムでエラーが発生しました。後でもう一度お試しください。",
|
||||
"zh-CN": "系统遇到错误。请稍后再试。",
|
||||
"zh-TW": "系統發生錯誤。請稍後再試。",
|
||||
"ko-KR": "시스템에 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||
"no": "Systemet har oppdaget en feil. Prøv igjen senere.",
|
||||
"it": "Il sistema ha riscontrato un errore. Riprova più tardi.",
|
||||
"pt": "O sistema encontrou um erro. Por favor, tente novamente mais tarde.",
|
||||
"es": "El sistema ha encontrado un error. Por favor, inténtalo de nuevo más tarde.",
|
||||
"ar": "واجه النظام خطأ. يرجى المحاولة مرة أخرى لاحقًا.",
|
||||
"fr": "Le système a rencontré une erreur. Veuillez réessayer plus tard.",
|
||||
"tr": "Sistem bir hata ile karşılaştı. Lütfen daha sonra tekrar deneyin.",
|
||||
"de": "Das System hat einen Fehler festgestellt. Bitte versuchen Sie es später erneut.",
|
||||
"uk": "Система зіткнулася з помилкою. Будь ласка, спробуйте пізніше."
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$CONVERSATION_STOPPED": {
|
||||
"en": "The conversation has been stopped.",
|
||||
"ja": "会話が停止されました。",
|
||||
"zh-CN": "对话已被停止。",
|
||||
"zh-TW": "對話已被停止。",
|
||||
"ko-KR": "대화가 중단되었습니다.",
|
||||
"no": "Samtalen har blitt stoppet.",
|
||||
"it": "La conversazione è stata interrotta.",
|
||||
"pt": "A conversa foi interrompida.",
|
||||
"es": "La conversación ha sido detenida.",
|
||||
"ar": "تم إيقاف المحادثة.",
|
||||
"fr": "La conversation a été arrêtée.",
|
||||
"tr": "Konuşma durduruldu.",
|
||||
"de": "Das Gespräch wurde gestoppt.",
|
||||
"uk": "Розмову зупинено."
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_TITLE": {
|
||||
"en": "Learn this repository?",
|
||||
"ja": "このリポジトリを学習しますか?",
|
||||
"zh-CN": "要学习此存储库吗?",
|
||||
"zh-TW": "要學習此存儲庫嗎?",
|
||||
"ko-KR": "이 저장소를 학습하시겠습니까?",
|
||||
"no": "Vil du lære dette depotet?",
|
||||
"it": "Vuoi imparare questo repository?",
|
||||
"pt": "Aprender este repositório?",
|
||||
"es": "¿Aprender este repositorio?",
|
||||
"ar": "هل تريد تعلم هذا المستودع؟",
|
||||
"fr": "Apprendre ce dépôt ?",
|
||||
"tr": "Bu depoyu öğrenmek ister misiniz?",
|
||||
"de": "Dieses Repository lernen?",
|
||||
"uk": "Вивчити цей репозиторій?"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$LEARN_THIS_REPO_MODAL_DESCRIPTION": {
|
||||
"en": "Do you want OpenHands to launch a new conversation to help you understand this repository?",
|
||||
"ja": "OpenHandsがこのリポジトリを理解するための新しい会話を開始しますか?",
|
||||
"zh-CN": "您想让OpenHands启动一个新对话来帮助您了解此存储库吗?",
|
||||
"zh-TW": "您想讓OpenHands啟動一個新對話來幫助您了解此存儲庫嗎?",
|
||||
"ko-KR": "OpenHands가 이 저장소를 이해하는 데 도움이 되는 새 대화를 시작하도록 하시겠습니까?",
|
||||
"no": "Vil du at OpenHands skal starte en ny samtale for å hjelpe deg med å forstå dette depotet?",
|
||||
"it": "Vuoi che OpenHands avvii una nuova conversazione per aiutarti a capire questo repository?",
|
||||
"pt": "Você quer que o OpenHands inicie uma nova conversa para ajudá-lo a entender este repositório?",
|
||||
"es": "¿Quieres que OpenHands inicie una nueva conversación para ayudarte a entender este repositorio?",
|
||||
"ar": "هل تريد أن يبدأ OpenHands محادثة جديدة لمساعدتك على فهم هذا المستودع؟",
|
||||
"fr": "Voulez-vous qu'OpenHands lance une nouvelle conversation pour vous aider à comprendre ce dépôt ?",
|
||||
"tr": "OpenHands'in bu depoyu anlamanıza yardımcı olacak yeni bir konuşma başlatmasını ister misiniz?",
|
||||
"de": "Möchten Sie, dass OpenHands eine neue Unterhaltung startet, um Ihnen dieses Repository zu erklären?",
|
||||
"uk": "Бажаєте, щоб OpenHands розпочав нову розмову, щоб допомогти вам зрозуміти цей репозиторій?"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO": {
|
||||
"en": "What would you like to know about this repository?",
|
||||
"ja": "このリポジトリについて何を知りたいですか?",
|
||||
"zh-CN": "您想了解此存储库的哪些内容?",
|
||||
"zh-TW": "您想了解此存儲庫的哪些內容?",
|
||||
"ko-KR": "이 저장소에 대해 무엇을 알고 싶으신가요?",
|
||||
"no": "Hva vil du vite om dette depotet?",
|
||||
"it": "Cosa vorresti sapere su questo repository?",
|
||||
"pt": "O que você gostaria de saber sobre este repositório?",
|
||||
"es": "¿Qué te gustaría saber sobre este repositorio?",
|
||||
"ar": "ماذا تريد أن تعرف عن هذا المستودع؟",
|
||||
"fr": "Que souhaitez-vous savoir sur ce dépôt ?",
|
||||
"tr": "Bu depo hakkında ne bilmek istersiniz?",
|
||||
"de": "Was möchten Sie über dieses Repository wissen?",
|
||||
"uk": "Що ви хотіли б дізнатися про цей репозиторій?"
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO": {
|
||||
"en": "Describe what you would like to know about this repository.",
|
||||
"ja": "このリポジトリについて知りたいことを説明してください。",
|
||||
"zh-CN": "请描述您想了解此存储库的内容。",
|
||||
"zh-TW": "請描述您想了解此存儲庫的內容。",
|
||||
"ko-KR": "이 저장소에 대해 알고 싶은 내용을 설명하세요.",
|
||||
"no": "Beskriv hva du vil vite om dette depotet.",
|
||||
"it": "Descrivi cosa vorresti sapere su questo repository.",
|
||||
"pt": "Descreva o que você gostaria de saber sobre este repositório.",
|
||||
"es": "Describe lo que te gustaría saber sobre este repositorio.",
|
||||
"ar": "صف ما ترغب في معرفته عن هذا المستودع.",
|
||||
"fr": "Décrivez ce que vous souhaitez savoir sur ce dépôt.",
|
||||
"tr": "Bu depo hakkında ne bilmek istediğinizi açıklayın.",
|
||||
"de": "Beschreiben Sie, was Sie über dieses Repository wissen möchten.",
|
||||
"uk": "Опишіть, що ви хотіли б дізнатися про цей репозиторій."
|
||||
},
|
||||
"MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION": {
|
||||
"en": "OpenHands will update the microagent based on your instructions.",
|
||||
"ja": "OpenHandsはあなたの指示に基づいてマイクロエージェントを更新します。",
|
||||
"zh-CN": "OpenHands 将根据您的指示更新微代理。",
|
||||
"zh-TW": "OpenHands 將根據您的指示更新微代理。",
|
||||
"ko-KR": "OpenHands가 귀하의 지시에 따라 마이크로에이전트를 업데이트합니다.",
|
||||
"no": "OpenHands vil oppdatere mikroagenten basert på dine instruksjoner.",
|
||||
"it": "OpenHands aggiornerà il microagent in base alle tue istruzioni.",
|
||||
"pt": "O OpenHands atualizará o microagente com base nas suas instruções.",
|
||||
"es": "OpenHands actualizará el microagente según tus instrucciones.",
|
||||
"ar": "سيقوم OpenHands بتحديث الوكيل الصغير بناءً على تعليماتك.",
|
||||
"fr": "OpenHands mettra à jour le microagent selon vos instructions.",
|
||||
"tr": "OpenHands, talimatlarınıza göre mikro ajanı güncelleyecektir.",
|
||||
"de": "OpenHands aktualisiert den Microagenten basierend auf Ihren Anweisungen.",
|
||||
"uk": "OpenHands оновить мікроагента відповідно до ваших інструкцій."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { redirect } from "react-router";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
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";
|
||||
import { MicroagentManagementAddMicroagentModal } from "#/components/features/microagent-management/microagent-management-add-microagent-modal";
|
||||
import { RootState } from "#/store";
|
||||
import { setAddMicroagentModalVisible } from "#/state/microagent-management-slice";
|
||||
import { MicroagentManagementContent } from "#/components/features/microagent-management/microagent-management-content";
|
||||
import { ConversationSubscriptionsProvider } from "#/context/conversation-subscriptions-provider";
|
||||
import { EventHandler } from "#/wrapper/event-handler";
|
||||
|
||||
export const clientLoader = async ({ request }: Route.ClientLoaderArgs) => {
|
||||
const url = new URL(request.url);
|
||||
@@ -31,31 +28,12 @@ export const clientLoader = async ({ request }: Route.ClientLoaderArgs) => {
|
||||
};
|
||||
|
||||
function MicroagentManagement() {
|
||||
const { addMicroagentModalVisible } = useSelector(
|
||||
(state: RootState) => state.microagentManagement,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const hideAddMicroagentModal = () => {
|
||||
dispatch(setAddMicroagentModalVisible(false));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex rounded-lg border border-[#525252] bg-[#24272E]">
|
||||
<MicroagentManagementSidebar />
|
||||
<MicroagentManagementMain />
|
||||
{addMicroagentModalVisible && (
|
||||
<MicroagentManagementAddMicroagentModal
|
||||
onConfirm={() => {
|
||||
hideAddMicroagentModal();
|
||||
}}
|
||||
onCancel={() => {
|
||||
hideAddMicroagentModal();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<ConversationSubscriptionsProvider>
|
||||
<EventHandler>
|
||||
<MicroagentManagementContent />
|
||||
</EventHandler>
|
||||
</ConversationSubscriptionsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,56 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { GitRepository } from "#/types/git";
|
||||
import { IMicroagentItem } from "#/types/microagent-management";
|
||||
|
||||
export const microagentManagementSlice = createSlice({
|
||||
name: "microagentManagement",
|
||||
initialState: {
|
||||
selectedMicroagent: null,
|
||||
addMicroagentModalVisible: false,
|
||||
selectedRepository: null,
|
||||
updateMicroagentModalVisible: false,
|
||||
selectedRepository: null as GitRepository | null,
|
||||
personalRepositories: [] as GitRepository[],
|
||||
organizationRepositories: [] as GitRepository[],
|
||||
repositories: [] as GitRepository[],
|
||||
selectedMicroagentItem: null as IMicroagentItem | null,
|
||||
learnThisRepoModalVisible: false,
|
||||
},
|
||||
reducers: {
|
||||
setSelectedMicroagent: (state, action) => {
|
||||
state.selectedMicroagent = action.payload;
|
||||
},
|
||||
setAddMicroagentModalVisible: (state, action) => {
|
||||
state.addMicroagentModalVisible = action.payload;
|
||||
},
|
||||
setUpdateMicroagentModalVisible: (state, action) => {
|
||||
state.updateMicroagentModalVisible = action.payload;
|
||||
},
|
||||
setSelectedRepository: (state, action) => {
|
||||
state.selectedRepository = action.payload;
|
||||
},
|
||||
setPersonalRepositories: (state, action) => {
|
||||
state.personalRepositories = action.payload;
|
||||
},
|
||||
setOrganizationRepositories: (state, action) => {
|
||||
state.organizationRepositories = action.payload;
|
||||
},
|
||||
setRepositories: (state, action) => {
|
||||
state.repositories = action.payload;
|
||||
},
|
||||
setSelectedMicroagentItem: (state, action) => {
|
||||
state.selectedMicroagentItem = action.payload;
|
||||
},
|
||||
setLearnThisRepoModalVisible: (state, action) => {
|
||||
state.learnThisRepoModalVisible = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setSelectedMicroagent,
|
||||
setAddMicroagentModalVisible,
|
||||
setUpdateMicroagentModalVisible,
|
||||
setSelectedRepository,
|
||||
setPersonalRepositories,
|
||||
setOrganizationRepositories,
|
||||
setRepositories,
|
||||
setSelectedMicroagentItem,
|
||||
setLearnThisRepoModalVisible,
|
||||
} = microagentManagementSlice.actions;
|
||||
|
||||
export default microagentManagementSlice.reducer;
|
||||
|
||||
@@ -20,3 +20,27 @@
|
||||
.heading {
|
||||
@apply text-[28px] leading-8 -tracking-[0.02em] font-bold text-content-2;
|
||||
}
|
||||
|
||||
.loader {
|
||||
background: #C9B974;
|
||||
animation: l5 1s infinite linear alternate;
|
||||
}
|
||||
|
||||
@keyframes l5 {
|
||||
0% {
|
||||
box-shadow: 20px 0 #C9B974, -20px 0 rgba(201,185,116,0.1);
|
||||
background: #C9B974;
|
||||
}
|
||||
33% {
|
||||
box-shadow: 20px 0 #C9B974, -20px 0 rgba(201,185,116,0.1);
|
||||
background: rgba(201,185,116,0.1);
|
||||
}
|
||||
66% {
|
||||
box-shadow: 20px 0 rgba(201,185,116,0.1), -20px 0 #C9B974;
|
||||
background: rgba(201,185,116,0.1);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 20px 0 rgba(201,185,116,0.1), -20px 0 #C9B974;
|
||||
background: #C9B974;
|
||||
}
|
||||
}
|
||||
|
||||
1
frontend/src/types/git.d.ts
vendored
1
frontend/src/types/git.d.ts
vendored
@@ -30,6 +30,7 @@ interface GitRepository {
|
||||
stargazers_count?: number;
|
||||
link_header?: string;
|
||||
pushed_at?: string;
|
||||
owner_type?: "user" | "organization";
|
||||
}
|
||||
|
||||
interface GitHubCommit {
|
||||
|
||||
32
frontend/src/types/microagent-management.tsx
Normal file
32
frontend/src/types/microagent-management.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Conversation } from "#/api/open-hands.types";
|
||||
|
||||
export type TabType = "personal" | "repositories" | "organizations";
|
||||
|
||||
export interface RepositoryMicroagent {
|
||||
name: string;
|
||||
type: "repo" | "knowledge";
|
||||
content: string;
|
||||
triggers: string[];
|
||||
inputs: string[];
|
||||
tools: string[];
|
||||
created_at: string;
|
||||
git_provider: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface IMicroagentItem {
|
||||
microagent?: RepositoryMicroagent;
|
||||
conversation?: Conversation;
|
||||
}
|
||||
|
||||
export interface MicroagentFormData {
|
||||
query: string;
|
||||
triggers: string[];
|
||||
selectedBranch: string;
|
||||
microagentPath: string;
|
||||
}
|
||||
|
||||
export interface LearnThisRepoFormData {
|
||||
query: string;
|
||||
selectedBranch: string;
|
||||
}
|
||||
@@ -28,3 +28,12 @@ export const JSON_VIEW_THEME = {
|
||||
base0E: "#c792ea", // keywords, purple
|
||||
base0F: "#ff5370", // deprecated, red
|
||||
};
|
||||
|
||||
export const DOCUMENTATION_URL = {
|
||||
MICROAGENTS: {
|
||||
MICROAGENTS_OVERVIEW:
|
||||
"https://docs.all-hands.dev/usage/prompting/microagents-overview",
|
||||
ORGANIZATION_AND_USER_MICROAGENTS:
|
||||
"https://docs.all-hands.dev/usage/prompting/microagents-org",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,3 +26,19 @@ export const formatTimeDelta = (date: Date) => {
|
||||
if (months < 12) return `${months}mo`;
|
||||
return `${years}y`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a date into a MM/DD/YYYY string format.
|
||||
* @param date The date to format
|
||||
* @returns A string in MM/DD/YYYY format
|
||||
*
|
||||
* @example
|
||||
* formatDateMMDDYYYY(new Date("2025-05-30T00:15:08")); // "05/30/2025"
|
||||
* formatDateMMDDYYYY(new Date("2024-12-25T10:30:00")); // "12/25/2024"
|
||||
*/
|
||||
export const formatDateMMDDYYYY = (date: Date) =>
|
||||
date.toLocaleDateString("en-US", {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Provider } from "#/types/settings";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
@@ -102,3 +103,107 @@ export const formatTimestamp = (timestamp: string) =>
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
});
|
||||
|
||||
export const getGitProviderBaseUrl = (gitProvider: Provider): string => {
|
||||
switch (gitProvider) {
|
||||
case "github":
|
||||
return "https://github.com";
|
||||
case "gitlab":
|
||||
return "https://gitlab.com";
|
||||
case "bitbucket":
|
||||
return "https://bitbucket.org";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of the git provider
|
||||
* @param gitProvider The git provider
|
||||
* @returns The name of the git provider
|
||||
*/
|
||||
export const getProviderName = (gitProvider: Provider) => {
|
||||
if (gitProvider === "gitlab") return "GitLab";
|
||||
if (gitProvider === "bitbucket") return "Bitbucket";
|
||||
return "GitHub";
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of the PR
|
||||
* @param isGitLab Whether the git provider is GitLab
|
||||
* @returns The name of the PR
|
||||
*/
|
||||
export const getPR = (isGitLab: boolean) =>
|
||||
isGitLab ? "merge request" : "pull request";
|
||||
|
||||
/**
|
||||
* Get the short name of the PR
|
||||
* @param isGitLab Whether the git provider is GitLab
|
||||
* @returns The short name of the PR
|
||||
*/
|
||||
export const getPRShort = (isGitLab: boolean) => (isGitLab ? "MR" : "PR");
|
||||
|
||||
/**
|
||||
* Construct the pull request (merge request) URL for different providers
|
||||
* @param prNumber The pull request number
|
||||
* @param provider The git provider
|
||||
* @param repositoryName The repository name in format "owner/repo"
|
||||
* @returns The pull request URL
|
||||
*
|
||||
* @example
|
||||
* constructPullRequestUrl(123, "github", "owner/repo") // "https://github.com/owner/repo/pull/123"
|
||||
* constructPullRequestUrl(456, "gitlab", "owner/repo") // "https://gitlab.com/owner/repo/-/merge_requests/456"
|
||||
* constructPullRequestUrl(789, "bitbucket", "owner/repo") // "https://bitbucket.org/owner/repo/pull-requests/789"
|
||||
*/
|
||||
export const constructPullRequestUrl = (
|
||||
prNumber: number,
|
||||
provider: Provider,
|
||||
repositoryName: string,
|
||||
): string => {
|
||||
const baseUrl = getGitProviderBaseUrl(provider);
|
||||
|
||||
switch (provider) {
|
||||
case "github":
|
||||
return `${baseUrl}/${repositoryName}/pull/${prNumber}`;
|
||||
case "gitlab":
|
||||
return `${baseUrl}/${repositoryName}/-/merge_requests/${prNumber}`;
|
||||
case "bitbucket":
|
||||
return `${baseUrl}/${repositoryName}/pull-requests/${prNumber}`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct the microagent URL for different providers
|
||||
* @param gitProvider The git provider
|
||||
* @param repositoryName The repository name in format "owner/repo"
|
||||
* @param microagentPath The path to the microagent in the repository
|
||||
* @returns The URL to the microagent file in the Git provider
|
||||
*
|
||||
* @example
|
||||
* constructMicroagentUrl("github", "owner/repo", ".openhands/microagents/tell-me-a-joke.md")
|
||||
* // "https://github.com/owner/repo/blob/main/.openhands/microagents/tell-me-a-joke.md"
|
||||
* constructMicroagentUrl("gitlab", "owner/repo", "microagents/git-helper.md")
|
||||
* // "https://gitlab.com/owner/repo/-/blob/main/microagents/git-helper.md"
|
||||
* constructMicroagentUrl("bitbucket", "owner/repo", ".openhands/microagents/docker-helper.md")
|
||||
* // "https://bitbucket.org/owner/repo/src/main/.openhands/microagents/docker-helper.md"
|
||||
*/
|
||||
export const constructMicroagentUrl = (
|
||||
gitProvider: Provider,
|
||||
repositoryName: string,
|
||||
microagentPath: string,
|
||||
): string => {
|
||||
const baseUrl = getGitProviderBaseUrl(gitProvider);
|
||||
|
||||
switch (gitProvider) {
|
||||
case "github":
|
||||
return `${baseUrl}/${repositoryName}/blob/main/${microagentPath}`;
|
||||
case "gitlab":
|
||||
return `${baseUrl}/${repositoryName}/-/blob/main/${microagentPath}`;
|
||||
case "bitbucket":
|
||||
return `${baseUrl}/${repositoryName}/src/main/${microagentPath}`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.27.12",
|
||||
"clsx": "^2.1.1",
|
||||
"deep-equal": "^2.2.3",
|
||||
"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",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^4.0.0",
|
||||
@@ -23,6 +22,7 @@
|
||||
"@storybook/react-vite": "^9.0.12",
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"@types/bun": "latest",
|
||||
"@types/deep-equal": "^1.0.4",
|
||||
"@vitejs/plugin-react": "^4.5.2",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
@@ -33,8 +33,10 @@
|
||||
"vitest": "^3.2.4",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
"react": ">=19.1.0",
|
||||
"react-dom": ">=19.1.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.10",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -67,7 +69,7 @@
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.27.6", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.6" } }, "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug=="],
|
||||
"@babel/helpers": ["@babel/helpers@7.28.2", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.2" } }, "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="],
|
||||
|
||||
@@ -75,13 +77,13 @@
|
||||
|
||||
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="],
|
||||
"@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
||||
|
||||
"@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.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ=="],
|
||||
"@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
|
||||
|
||||
@@ -109,57 +111,57 @@
|
||||
|
||||
"@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/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.6", "", { "os": "android", "cpu": "arm" }, "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg=="],
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.6", "", { "os": "android", "cpu": "arm64" }, "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA=="],
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.6", "", { "os": "android", "cpu": "x64" }, "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A=="],
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA=="],
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg=="],
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.6", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg=="],
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ=="],
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.6", "", { "os": "linux", "cpu": "arm" }, "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw=="],
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ=="],
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.6", "", { "os": "linux", "cpu": "ia32" }, "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw=="],
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg=="],
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw=="],
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.6", "", { "os": "linux", "cpu": "ppc64" }, "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw=="],
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w=="],
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.6", "", { "os": "linux", "cpu": "s390x" }, "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw=="],
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.6", "", { "os": "linux", "cpu": "x64" }, "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig=="],
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q=="],
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.6", "", { "os": "none", "cpu": "x64" }, "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g=="],
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.6", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg=="],
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.8", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.6", "", { "os": "openbsd", "cpu": "x64" }, "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw=="],
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA=="],
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.6", "", { "os": "sunos", "cpu": "x64" }, "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA=="],
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q=="],
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ=="],
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA=="],
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="],
|
||||
|
||||
"@floating-ui/core": ["@floating-ui/core@1.7.2", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw=="],
|
||||
|
||||
@@ -189,9 +191,9 @@
|
||||
|
||||
"@mdx-js/react": ["@mdx-js/react@3.1.0", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ=="],
|
||||
|
||||
"@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.8", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.6", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.1", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.3", "@rushstack/ts-command-line": "5.0.1", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-cszYIcjiNscDoMB1CIKZ3My61+JOhpERGlGr54i6bocvGLrcL/wo9o+RNXMBrb7XgLtKaizZWUpqRduQuHQLdg=="],
|
||||
"@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.9", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.7", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.14.0", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.4", "@rushstack/ts-command-line": "5.0.2", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-313nyhc6DSSMVKD43jZK6Yp5XbliGw5vjN7QOw1FHzR1V6DQ67k4dzkd3BSxMtWcm+cEs1Ux8rmDqots6EABFA=="],
|
||||
|
||||
"@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.6", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.1" } }, "sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg=="],
|
||||
"@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.7", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.14.0" } }, "sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ=="],
|
||||
|
||||
"@microsoft/tsdoc": ["@microsoft/tsdoc@0.15.1", "", {}, "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw=="],
|
||||
|
||||
@@ -203,7 +205,7 @@
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.19", "", {}, "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA=="],
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -247,35 +249,35 @@
|
||||
|
||||
"@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=="],
|
||||
"@rushstack/node-core-library": ["@rushstack/node-core-library@5.14.0", "", { "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-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg=="],
|
||||
|
||||
"@rushstack/rig-package": ["@rushstack/rig-package@0.5.3", "", { "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow=="],
|
||||
|
||||
"@rushstack/terminal": ["@rushstack/terminal@0.15.3", "", { "dependencies": { "@rushstack/node-core-library": "5.13.1", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-DGJ0B2Vm69468kZCJkPj3AH5nN+nR9SPmC0rFHtzsS4lBQ7/dgOwtwVxYP7W9JPDMuRBkJ4KHmWKr036eJsj9g=="],
|
||||
"@rushstack/terminal": ["@rushstack/terminal@0.15.4", "", { "dependencies": { "@rushstack/node-core-library": "5.14.0", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-OQSThV0itlwVNHV6thoXiAYZlQh4Fgvie2CzxFABsbO2MWQsI4zOh3LRNigYSTrmS+ba2j0B3EObakPzf/x6Zg=="],
|
||||
|
||||
"@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=="],
|
||||
"@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.2", "", { "dependencies": { "@rushstack/terminal": "0.15.4", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ=="],
|
||||
|
||||
"@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-a11y": ["@storybook/addon-a11y@9.0.18", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^9.0.18" } }, "sha512-msbsTI9TmePQ5ElVclLi7ns5WaAntouJFaj9ElNugFWME21k68RiyXnioDjDfEoi/+y8tthQNNqjsHoX/Ev0Og=="],
|
||||
|
||||
"@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-docs": ["@storybook/addon-docs@9.0.18", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "9.0.18", "@storybook/icons": "^1.2.12", "@storybook/react-dom-shim": "9.0.18", "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.18" } }, "sha512-1mLhaRDx8s1JAF51o56OmwMnIsg4BOQJ8cn+4wbMjh14pDFALrovlFl/BpAXnV1VaZqHjCB4ZWuP+y5CwXEpeQ=="],
|
||||
|
||||
"@storybook/addon-onboarding": ["@storybook/addon-onboarding@9.0.17", "", { "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-WoZZ8d58gP6uBu6OJ2K1GjBSM4+Kcr0I9lo0z3convzYqxrhfUm9pNEwVm57KCbVVyBbIKmevddCsSFoPC5u6Q=="],
|
||||
"@storybook/addon-onboarding": ["@storybook/addon-onboarding@9.0.18", "", { "peerDependencies": { "storybook": "^9.0.18" } }, "sha512-A079BfJ3g3wYOtAuq9cPf2l6JHo+6UzEw1A2AbSNBBNP4hKfXpHcLadIVwuyOxuKjDUWzY5f4dJa3hCMurHXGQ=="],
|
||||
|
||||
"@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/addon-vitest": ["@storybook/addon-vitest@9.0.18", "", { "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.18", "vitest": "^3.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/runner", "vitest"] }, "sha512-uPLh9H7kRho+raxyIBCm8Ymd3j0VPuWIQ1HSAkdx8itmNafNqs4HE67Z8Cfl259YzdWU/j5BhZqoiT62BCbIDw=="],
|
||||
|
||||
"@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/builder-vite": ["@storybook/builder-vite@9.0.18", "", { "dependencies": { "@storybook/csf-plugin": "9.0.18", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^9.0.18", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-lfbrozA6UPVizDrgbPEe04WMtxIraESwUkmwW3+Lxh8rKEUj5cXngcrJUW+meQNNaggdZZWEqeEtweuaLIR+Hg=="],
|
||||
|
||||
"@storybook/csf-plugin": ["@storybook/csf-plugin@9.0.17", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^9.0.17" } }, "sha512-6Q4eo1ObrLlsnB6bIt6T8+45XAb4to2pQGNrI7QPkLQRLrZinrJcNbLY7AGkyIoCOEsEbq08n09/nClQUbu8HA=="],
|
||||
"@storybook/csf-plugin": ["@storybook/csf-plugin@9.0.18", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^9.0.18" } }, "sha512-MQ3WwXnMua5sX0uYyuO7dC5WOWuJCLqf8CsOn3zQ2ptNoH6hD7DFx5ZOa1uD6VxIuJ3LkA+YqfSRBncomJoRnA=="],
|
||||
|
||||
"@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.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": ["@storybook/react@9.0.18", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/react-dom-shim": "9.0.18" }, "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.18", "typescript": ">= 4.9.x" }, "optionalPeers": ["typescript"] }, "sha512-CCH6Vj/O6I07PrhCHxc1pvCWYMfZhRzK7CVHAtrBP9xxnYA7OoXhM2wymuDogml5HW1BKtyVMeQ3oWZXFNgDXQ=="],
|
||||
|
||||
"@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-dom-shim": ["@storybook/react-dom-shim@9.0.18", "", { "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.18" } }, "sha512-qGR/d9x9qWRRxITaBVQkMnb73kwOm+N8fkbZRxc7U4lxupXRvkMIDh247nn71SYVBnvbh6//AL7P6ghiPWZYjA=="],
|
||||
|
||||
"@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=="],
|
||||
"@storybook/react-vite": ["@storybook/react-vite@9.0.18", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.6.1", "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "9.0.18", "@storybook/react": "9.0.18", "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.18", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-dHzUoeY0/S35TvSYxCkPuBlNQZx4Zj9QDhAZ0qdv+nSll++uPgqSe2y2vF+2p+XVYhjDn+YX5LORv00YtuQezg=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -325,19 +327,21 @@
|
||||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
|
||||
"@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
|
||||
|
||||
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
|
||||
|
||||
"@types/deep-equal": ["@types/deep-equal@1.0.4", "", {}, "sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA=="],
|
||||
|
||||
"@types/doctrine": ["@types/doctrine@0.0.9", "", {}, "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="],
|
||||
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
||||
|
||||
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
||||
|
||||
@@ -351,7 +355,7 @@
|
||||
|
||||
"@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=="],
|
||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@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" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
||||
|
||||
"@vitest/browser": ["@vitest/browser@3.2.4", "", { "dependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.6.1", "@vitest/mocker": "3.2.4", "@vitest/utils": "3.2.4", "magic-string": "^0.30.17", "sirv": "^3.0.1", "tinyrainbow": "^2.0.0", "ws": "^8.18.2" }, "peerDependencies": { "playwright": "*", "vitest": "3.2.4", "webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0" }, "optionalPeers": ["playwright", "webdriverio"] }, "sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw=="],
|
||||
|
||||
@@ -371,21 +375,21 @@
|
||||
|
||||
"@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.19", "", { "dependencies": { "@volar/source-map": "2.4.19" } }, "sha512-i0aLpNA8DYZ2uG05t5K47nUWe+zvvrN9tfz16zS5pCJV9td8F0u+rVAOVSQ1ypufDLUD+ej9BH2/lmug4+lawQ=="],
|
||||
"@volar/language-core": ["@volar/language-core@2.4.20", "", { "dependencies": { "@volar/source-map": "2.4.20" } }, "sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA=="],
|
||||
|
||||
"@volar/source-map": ["@volar/source-map@2.4.19", "", {}, "sha512-ttWmO/Ld7r3ebIPPAYvAuSLrlJ96ZALPka44mD4sWA8bw2n9u7TGnMcaTUkiF0GLG8bq/K09beWmEAB1mqMy/A=="],
|
||||
"@volar/source-map": ["@volar/source-map@2.4.20", "", {}, "sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg=="],
|
||||
|
||||
"@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=="],
|
||||
"@volar/typescript": ["@volar/typescript@2.4.20", "", { "dependencies": { "@volar/language-core": "2.4.20", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ=="],
|
||||
|
||||
"@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=="],
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw=="],
|
||||
|
||||
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.17", "", { "dependencies": { "@vue/compiler-core": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ=="],
|
||||
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.18", "", { "dependencies": { "@vue/compiler-core": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A=="],
|
||||
|
||||
"@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="],
|
||||
|
||||
"@vue/language-core": ["@vue/language-core@2.2.0", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^0.4.9", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw=="],
|
||||
|
||||
"@vue/shared": ["@vue/shared@3.5.17", "", {}, "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="],
|
||||
"@vue/shared": ["@vue/shared@3.5.18", "", {}, "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
@@ -405,12 +409,16 @@
|
||||
|
||||
"aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
|
||||
|
||||
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
||||
|
||||
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||
|
||||
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
||||
|
||||
"ast-v8-to-istanbul": ["ast-v8-to-istanbul@0.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } }, "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
|
||||
"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=="],
|
||||
@@ -423,10 +431,16 @@
|
||||
|
||||
"browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
|
||||
"bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="],
|
||||
@@ -469,8 +483,14 @@
|
||||
|
||||
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||
|
||||
"deep-equal": ["deep-equal@2.2.3", "", { "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", "es-get-iterator": "^1.1.3", "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", "which-typed-array": "^1.1.13" } }, "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
|
||||
"define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
@@ -481,9 +501,11 @@
|
||||
|
||||
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.185", "", {}, "sha512-dYOZfUk57hSMPePoIQ1fZWl1Fkj+OshhEVuPacNKWzC1efe56OsHY3l/jCfiAgIICOU3VgOIdoq7ahg7r7n6MQ=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.191", "", {}, "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
@@ -493,9 +515,17 @@
|
||||
|
||||
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-get-iterator": ["es-get-iterator@1.1.3", "", { "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", "is-string": "^1.0.7", "isarray": "^2.0.5", "stop-iteration-iterator": "^1.0.0" } }, "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw=="],
|
||||
|
||||
"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=="],
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="],
|
||||
|
||||
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||
|
||||
@@ -527,6 +557,8 @@
|
||||
|
||||
"focus-trap-react": ["focus-trap-react@11.0.4", "", { "dependencies": { "focus-trap": "^7.6.5", "tabbable": "^6.2.0" }, "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-tC7jC/yqeAqhe4irNIzdyDf9XCtGSeECHiBSYJBO/vIN0asizbKZCt8TarB6/XqIceu42ajQ/U4lQJ9pZlWjrg=="],
|
||||
|
||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="],
|
||||
@@ -535,14 +567,30 @@
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
@@ -557,16 +605,50 @@
|
||||
|
||||
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
|
||||
|
||||
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
||||
|
||||
"is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="],
|
||||
|
||||
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||
|
||||
"is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
|
||||
|
||||
"is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
|
||||
|
||||
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||
|
||||
"is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
|
||||
|
||||
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
|
||||
|
||||
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
||||
|
||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||
|
||||
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
||||
|
||||
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
|
||||
|
||||
"is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
|
||||
|
||||
"is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
|
||||
|
||||
"is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
|
||||
|
||||
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
|
||||
|
||||
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
||||
|
||||
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
|
||||
@@ -579,7 +661,7 @@
|
||||
|
||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
||||
"jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
|
||||
|
||||
"jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="],
|
||||
|
||||
@@ -631,7 +713,7 @@
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="],
|
||||
"loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
@@ -643,6 +725,8 @@
|
||||
|
||||
"make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
|
||||
|
||||
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
||||
@@ -671,6 +755,14 @@
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="],
|
||||
|
||||
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
||||
|
||||
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
||||
|
||||
"open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||
|
||||
"p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],
|
||||
@@ -709,6 +801,8 @@
|
||||
|
||||
"playwright-core": ["playwright-core@1.54.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA=="],
|
||||
|
||||
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
|
||||
@@ -745,6 +839,8 @@
|
||||
|
||||
"redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="],
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"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=="],
|
||||
@@ -753,14 +849,28 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||
|
||||
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||
|
||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||
|
||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
@@ -781,7 +891,9 @@
|
||||
|
||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||
|
||||
"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=="],
|
||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||
|
||||
"storybook": ["storybook@9.0.18", "", { "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-ruxpEpizwoYQTt1hBOrWyp9trPYWD9Apt1TJ37rs1rzmNQWpSNGJDMg91JV4mUhBChzRvnid/oRBFFCWJz/dfw=="],
|
||||
|
||||
"string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="],
|
||||
|
||||
@@ -861,7 +973,7 @@
|
||||
|
||||
"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": ["vite@7.0.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.3", "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-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -875,6 +987,12 @@
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
||||
|
||||
"which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
|
||||
|
||||
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
|
||||
|
||||
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
@@ -911,11 +1029,11 @@
|
||||
|
||||
"@rushstack/terminal/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.4", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" }, "bundled": true }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
||||
|
||||
@@ -923,8 +1041,6 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
type AccordionItemPropsPublic,
|
||||
} from "./components/AccordionItem";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
|
||||
export type AccordionProps = HTMLProps<"div"> & {
|
||||
expandedKeys: string[];
|
||||
type?: "multi" | "single";
|
||||
setExpandedKeys(keys: string[]): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
type AccordionType = React.FC<PropsWithChildren<AccordionProps>> & {
|
||||
Item: React.FC<PropsWithChildren<AccordionItemPropsPublic>>;
|
||||
@@ -23,6 +23,7 @@ const Accordion: AccordionType = ({
|
||||
setExpandedKeys,
|
||||
children,
|
||||
type = "multi",
|
||||
testId,
|
||||
...props
|
||||
}) => {
|
||||
const onChange = useCallback(
|
||||
@@ -54,6 +55,7 @@ const Accordion: AccordionType = ({
|
||||
return (
|
||||
<div
|
||||
className={cn("flex flex-col gap-y-2.5 items-start", className)}
|
||||
data-testid={testId}
|
||||
{...props}
|
||||
>
|
||||
{items}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { Icon, type IconProps } from "../../icon/Icon";
|
||||
import { Typography } from "../../typography/Typography";
|
||||
@@ -10,7 +10,7 @@ export type AccordionHeaderProps = Omit<
|
||||
> & {
|
||||
icon: IconProps["icon"];
|
||||
expanded: boolean;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const AccordionHeader = ({
|
||||
className,
|
||||
@@ -43,7 +43,8 @@ export const AccordionHeader = ({
|
||||
// hover modifier
|
||||
"data-[expanded=true]:hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"data-[expanded=false]:focus:bg-light-neutral-900"
|
||||
"data-[expanded=false]:focus:bg-light-neutral-900",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} className={cn(iconCss, "w-6 h-6")} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
import { type IconProps } from "../../icon/Icon";
|
||||
import { AccordionHeader } from "./AccordionHeader";
|
||||
@@ -11,10 +11,10 @@ export type AccordionItemProps = HTMLProps<"div"> & {
|
||||
value: string;
|
||||
label: React.ReactNode;
|
||||
onExpandedChange(value: boolean): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
export type AccordionItemPropsPublic = Omit<
|
||||
AccordionItemProps,
|
||||
"expanded" | "onExpandedChange"
|
||||
"expanded" | "onExpandedChange" | "className" | "style" | "testId"
|
||||
>;
|
||||
|
||||
export const AccordionItem = ({
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../../shared/types";
|
||||
import { cn } from "../../../shared/utils/cn";
|
||||
|
||||
export type AccordionPanelProps = Omit<HTMLProps<"div">, "aria-expanded"> & {
|
||||
expanded: boolean;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const AccordionPanel = ({
|
||||
className,
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import type { PropsWithChildren, ReactElement } from "react";
|
||||
import type { ComponentVariant, HTMLProps } from "../../shared/types";
|
||||
import {
|
||||
useEffect,
|
||||
useRef,
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type {
|
||||
BaseProps,
|
||||
ComponentVariant,
|
||||
HTMLProps,
|
||||
} from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { invariant } from "../../shared/utils/invariant";
|
||||
import { buttonStyles } from "./utils";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { buttonStyles, useAndApplyBoldTextWidth } from "./utils";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
import "./index.css";
|
||||
|
||||
export type ButtonProps = Omit<HTMLProps<"button">, "aria-disabled"> & {
|
||||
size?: "small" | "large";
|
||||
variant?: ComponentVariant;
|
||||
start?: ReactElement<HTMLProps<"svg">>;
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Button = ({
|
||||
size = "small",
|
||||
@@ -20,39 +28,42 @@ export const Button = ({
|
||||
children,
|
||||
start,
|
||||
end,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ButtonProps>) => {
|
||||
invariant(typeof children === "string", "Children must be string");
|
||||
const buttonClassNames = buttonStyles[variant];
|
||||
|
||||
const iconCss = "w-6 h-6";
|
||||
const hasIcons = start || end;
|
||||
const textRef = useAndApplyBoldTextWidth(children, "text-increase-size");
|
||||
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
aria-disabled={props.disabled ? "true" : "false"}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
size === "small" ? "px-3 py-1.5 min-w-32" : "px-3 py-3 min-w-64",
|
||||
size === "small" ? "px-2 py-3 min-w-32" : "px-3 py-4 min-w-64",
|
||||
"flex flex-row items-center gap-x-8",
|
||||
hasIcons ? " justify-between" : " justify-center",
|
||||
"group enabled:cursor-pointer focus:outline-0",
|
||||
buttonClassNames.button
|
||||
buttonClassNames.button,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{cloneIcon(start, {
|
||||
className: cn(iconCss, buttonClassNames.icon),
|
||||
})}
|
||||
|
||||
<Typography.Text
|
||||
fontSize="l"
|
||||
<span
|
||||
ref={textRef}
|
||||
className={cn(
|
||||
"text-center font-size-l font-normal",
|
||||
buttonClassNames.text
|
||||
"tg-family-outfit tg-lg text-center font-normal leading-[100%]",
|
||||
buttonClassNames.text,
|
||||
!props.disabled && `button-bold-text`
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Typography.Text>
|
||||
</span>
|
||||
{cloneIcon(end, {
|
||||
className: cn(iconCss, buttonClassNames.icon),
|
||||
})}
|
||||
|
||||
15
openhands-ui/components/button/index.css
Normal file
15
openhands-ui/components/button/index.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.group .button-bold-text::before,
|
||||
.group .button-bold-text::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: var(--text-increase-size);
|
||||
transition: width 0.2s ease;
|
||||
transition: font-weight 0.2s ease;
|
||||
}
|
||||
|
||||
.group:hover .button-bold-text::before,
|
||||
.group:hover .button-bold-text::after,
|
||||
.group:focus .button-bold-text::before,
|
||||
.group:focus .button-bold-text::after {
|
||||
width: 0;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useRef, type ReactNode } from "react";
|
||||
import type { ComponentVariant } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
@@ -14,14 +15,22 @@ export const buttonStyles: Record<ComponentVariant, ButtonStyle> = {
|
||||
// hover modifier
|
||||
"enabled:hover:bg-grey-900 enabled:hover:ring-[1.5px]",
|
||||
// focus modifier
|
||||
"enabled:hover:bg-grey-900 enabled:focus:ring-[1.5px]",
|
||||
"enabled:hover:bg-grey-900 enabled:focus-visible:ring-[1.5px]",
|
||||
// active modifier
|
||||
"enabled:active:ring-1",
|
||||
// disabled modifier
|
||||
"disabled:opacity-50",
|
||||
]),
|
||||
icon: cn(["text-primary-500"]),
|
||||
text: cn(["text-primary-500"]),
|
||||
text: cn([
|
||||
"text-primary-500",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus-visible:font-semibold",
|
||||
// active modifier
|
||||
"group-enabled:group-active:font-normal",
|
||||
]),
|
||||
},
|
||||
secondary: {
|
||||
button: cn([
|
||||
@@ -29,14 +38,22 @@ export const buttonStyles: Record<ComponentVariant, ButtonStyle> = {
|
||||
// hover modifier
|
||||
"enabled:hover:bg-light-neutral-900 enabled:hover:ring-[1.5px]",
|
||||
// focus modifier
|
||||
"enabled:focus:bg-light-neutral-900 enabled:focus:ring-[1.5px]",
|
||||
"enabled:focus-visible:bg-light-neutral-900 enabled:focus-visible:ring-[1.5px]",
|
||||
// active modifier
|
||||
"enabled:active:ring-1",
|
||||
// disabled modifier
|
||||
"disabled:opacity-50",
|
||||
]),
|
||||
icon: cn(["text-light-neutral-300"]),
|
||||
text: cn(["text-light-neutral-300"]),
|
||||
text: cn([
|
||||
"text-light-neutral-300",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus-visible:font-semibold",
|
||||
// active modifier
|
||||
"group-enabled:group-active:font-normal",
|
||||
]),
|
||||
},
|
||||
tertiary: {
|
||||
button: cn([
|
||||
@@ -44,7 +61,7 @@ export const buttonStyles: Record<ComponentVariant, ButtonStyle> = {
|
||||
// hover modifier
|
||||
"enabled:hover:bg-grey-900",
|
||||
// focus modifier
|
||||
"enabled:focus:bg-grey-900",
|
||||
"enabled:focus-visible:bg-grey-900",
|
||||
// active modifier
|
||||
"enabled:active:bg-grey-970",
|
||||
// disabled modifier
|
||||
@@ -53,8 +70,38 @@ export const buttonStyles: Record<ComponentVariant, ButtonStyle> = {
|
||||
icon: cn(["text-primary-500"]),
|
||||
text: cn([
|
||||
"text-primary-500 underline",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus-visible:font-semibold",
|
||||
// disabled modifier
|
||||
"group-disabled:no-underline",
|
||||
// active modifier
|
||||
"group-enabled:group-active:font-normal",
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook that calculates and applies a CSS custom property (variable)
|
||||
* based on the length of a text node. Useful for adjusting spacing or layout
|
||||
* to account for changes in font weight, such as bold text rendering wider.
|
||||
*/
|
||||
const BOLD_TEXT_INCREASE = 0.15;
|
||||
export const useAndApplyBoldTextWidth = (
|
||||
textNode: ReactNode,
|
||||
varName: string
|
||||
) => {
|
||||
const textRef = useRef<HTMLSpanElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (textRef) {
|
||||
const charCount =
|
||||
typeof textNode === "string" ? (textNode as string).length : 0;
|
||||
const textIncrease = charCount * BOLD_TEXT_INCREASE;
|
||||
textRef.current!.style.setProperty(`--${varName}`, `${textIncrease}px`);
|
||||
}
|
||||
}, [textRef.current]);
|
||||
|
||||
return textRef;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const CheckboxComponent = (props: CheckboxProps) => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [checked, setChecked] = useState(props.checked);
|
||||
return (
|
||||
<Checkbox
|
||||
{...props}
|
||||
@@ -28,6 +28,7 @@ const CheckboxComponent = (props: CheckboxProps) => {
|
||||
|
||||
export const Enabled: Story = {
|
||||
args: {
|
||||
checked: false,
|
||||
label:
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
},
|
||||
@@ -37,6 +38,7 @@ export const Enabled: Story = {
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
checked: false,
|
||||
label:
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
},
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useId } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { Icon } from "../icon/Icon";
|
||||
|
||||
export type CheckboxProps = HTMLProps<"input"> & {
|
||||
export type CheckboxProps = Omit<
|
||||
HTMLProps<"input">,
|
||||
"checked" | "defaultChecked"
|
||||
> & {
|
||||
label: React.ReactNode;
|
||||
labelClassName?: string;
|
||||
};
|
||||
checked: boolean;
|
||||
} & BaseProps;
|
||||
|
||||
export const Checkbox = ({
|
||||
className,
|
||||
@@ -17,6 +21,7 @@ export const Checkbox = ({
|
||||
disabled,
|
||||
checked,
|
||||
onChange,
|
||||
testId,
|
||||
...props
|
||||
}: CheckboxProps) => {
|
||||
const generatedId = useId();
|
||||
@@ -26,8 +31,10 @@ export const Checkbox = ({
|
||||
htmlFor={id}
|
||||
className={cn(
|
||||
"flex items-center gap-2 cursor-pointer",
|
||||
disabled && "cursor-not-allowed"
|
||||
disabled && "cursor-not-allowed",
|
||||
className
|
||||
)}
|
||||
data-testid={testId}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import { type PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { chipStyles, type ChipColor, type ChipVariant } from "./utils";
|
||||
import { invariant } from "../../shared/utils/invariant";
|
||||
|
||||
export type ChipProps = Omit<HTMLProps<"div">, "label"> & {
|
||||
color?: ChipColor;
|
||||
variant?: ChipVariant;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Chip = ({
|
||||
className,
|
||||
color = "gray",
|
||||
variant = "pill",
|
||||
children,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ChipProps>) => {
|
||||
invariant(typeof children === "string", "Children must be string");
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"flex flex-row items-center px-1.5 py-1",
|
||||
variant === "pill" ? "rounded-full" : "rounded-lg",
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import {
|
||||
useEffect,
|
||||
useId,
|
||||
useRef,
|
||||
useState,
|
||||
type PropsWithChildren,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import { useId, type PropsWithChildren } from "react";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Icon } from "../icon/Icon";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
FloatingOverlay,
|
||||
FloatingPortal,
|
||||
@@ -24,13 +17,14 @@ import { FocusTrap } from "focus-trap-react";
|
||||
export type DialogProps = HTMLProps<"div"> & {
|
||||
open: boolean;
|
||||
onOpenChange(value: boolean): void;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Dialog = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
className,
|
||||
children,
|
||||
testId,
|
||||
}: PropsWithChildren<DialogProps>) => {
|
||||
const id = useId();
|
||||
|
||||
@@ -80,6 +74,7 @@ export const Dialog = ({
|
||||
aria-describedby={`${id}-description`}
|
||||
{...getFloatingProps()}
|
||||
style={styles}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"rounded-4xl border-1 border-light-neutral-500 outline-none",
|
||||
"transition-all will-change-transform",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
export type DividerProps = Omit<
|
||||
@@ -6,11 +6,12 @@ export type DividerProps = Omit<
|
||||
"role" | "aria-orientation"
|
||||
> & {
|
||||
type?: "horizontal" | "vertical";
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Divider = ({
|
||||
type = "horizontal",
|
||||
className,
|
||||
testId,
|
||||
...props
|
||||
}: DividerProps) => {
|
||||
return (
|
||||
@@ -23,6 +24,7 @@ export const Divider = ({
|
||||
)}
|
||||
role="separator"
|
||||
aria-orientation={type}
|
||||
data-testid={testId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
@@ -19,7 +19,7 @@ export type InputProps = Omit<
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
error?: string;
|
||||
hint?: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const Input = ({
|
||||
className,
|
||||
@@ -34,6 +34,7 @@ export const Input = ({
|
||||
type,
|
||||
hint,
|
||||
readOnly,
|
||||
testId,
|
||||
...props
|
||||
}: InputProps) => {
|
||||
const generatedId = useId();
|
||||
@@ -45,65 +46,64 @@ export const Input = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor={id}
|
||||
<label
|
||||
htmlFor={id}
|
||||
data-testid={testId}
|
||||
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>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col gap-y-2",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
|
||||
"flex flex-row items-center gap-x-2.5",
|
||||
"py-4.25 px-4",
|
||||
"border-light-neutral-500 border-1 rounded-2xl",
|
||||
// base
|
||||
"bg-light-neutral-950",
|
||||
// hover modifier
|
||||
"hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"focus-within:bg-light-neutral-900",
|
||||
// error state
|
||||
error && " border-red-400 bg-light-neutral-970",
|
||||
readOnly &&
|
||||
"bg-light-neutral-985 border-none hover:bg-light-neutral-985 cursor-auto",
|
||||
// disabled modifier
|
||||
disabled && "hover:bg-light-neutral-950"
|
||||
)}
|
||||
>
|
||||
<Typography.Text fontSize="s" className="text-light-neutral-200">
|
||||
{label}
|
||||
</Typography.Text>
|
||||
<div
|
||||
{cloneIcon(start, {
|
||||
className: iconCss,
|
||||
})}
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
aria-invalid={error ? "true" : "false"}
|
||||
readOnly={readOnly}
|
||||
className={cn(
|
||||
"flex flex-row items-center gap-x-2.5",
|
||||
"py-4.25 px-4",
|
||||
"border-light-neutral-500 border-1 rounded-2xl",
|
||||
// base
|
||||
"bg-light-neutral-950",
|
||||
// hover modifier
|
||||
"hover:bg-light-neutral-900",
|
||||
// focus modifier
|
||||
"focus-within:bg-light-neutral-900",
|
||||
// error state
|
||||
error && " border-red-400 bg-light-neutral-970",
|
||||
readOnly &&
|
||||
"bg-light-neutral-985 border-none hover:bg-light-neutral-985 cursor-auto",
|
||||
// disabled modifier
|
||||
disabled && "hover:bg-light-neutral-950"
|
||||
"flex-1 outline-none caret-primary-500 text-white",
|
||||
"placeholder:text-light-neutral-300",
|
||||
error && "text-red-400"
|
||||
)}
|
||||
>
|
||||
{cloneIcon(start, {
|
||||
className: iconCss,
|
||||
})}
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
aria-invalid={error ? "true" : "false"}
|
||||
readOnly={readOnly}
|
||||
className={cn(
|
||||
"flex-1 outline-none caret-primary-500 text-white",
|
||||
"placeholder:text-light-neutral-300",
|
||||
error && "text-red-400"
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{cloneIcon(end, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</div>
|
||||
<Typography.Text
|
||||
fontSize="xs"
|
||||
className={cn("text-light-neutral-600 ml-4", error && "text-red-400")}
|
||||
>
|
||||
{error ?? hint}
|
||||
</Typography.Text>
|
||||
</label>
|
||||
</div>
|
||||
{...props}
|
||||
/>
|
||||
{cloneIcon(end, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</div>
|
||||
<Typography.Text
|
||||
fontSize="xs"
|
||||
className={cn("text-light-neutral-600 ml-4", error && "text-red-400")}
|
||||
>
|
||||
{error ?? hint}
|
||||
</Typography.Text>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ const InteractiveChipComponent = (props: InteractiveChipProps) => {
|
||||
|
||||
export const Elevated: Story = {
|
||||
args: {
|
||||
type: "elevated",
|
||||
chipType: "elevated",
|
||||
disabled: false,
|
||||
},
|
||||
render: InteractiveChipComponent,
|
||||
@@ -37,7 +37,7 @@ export const Elevated: Story = {
|
||||
|
||||
export const Filled: Story = {
|
||||
args: {
|
||||
type: "filled",
|
||||
chipType: "filled",
|
||||
disabled: false,
|
||||
},
|
||||
render: InteractiveChipComponent,
|
||||
|
||||
@@ -1,84 +1,67 @@
|
||||
import {
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import { type PropsWithChildren, type ReactElement } from "react";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { Typography } from "../typography/Typography";
|
||||
|
||||
import { invariant } from "../../shared/utils/invariant";
|
||||
import { interactiveChipStyles, type InteractiveChipType } from "./utils";
|
||||
import { cloneIcon } from "../../shared/utils/clone-icon";
|
||||
import "./index.css";
|
||||
import {
|
||||
buttonStyles,
|
||||
useAndApplyBoldTextWidth,
|
||||
type InteractiveChipType,
|
||||
} from "./utils";
|
||||
|
||||
export type InteractiveChipProps = Omit<
|
||||
HTMLProps<"div">,
|
||||
"label" | "aria-disabled" | "tabIndex"
|
||||
HTMLProps<"button">,
|
||||
"aria-disabled"
|
||||
> & {
|
||||
chipType?: InteractiveChipType;
|
||||
start?: ReactElement<HTMLProps<"svg">>;
|
||||
onStartClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
end?: ReactElement<HTMLProps<"svg">>;
|
||||
onEndClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
type?: InteractiveChipType;
|
||||
disabled?: boolean;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const InteractiveChip = ({
|
||||
chipType = "elevated",
|
||||
className,
|
||||
children,
|
||||
start,
|
||||
end,
|
||||
type = "elevated",
|
||||
children,
|
||||
disabled = false,
|
||||
onStartClick,
|
||||
onEndClick,
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<InteractiveChipProps>) => {
|
||||
invariant(typeof children === "string", "Children must be string");
|
||||
|
||||
const iconCss = cn("w-4 h-4 text-inherit");
|
||||
const buttonCss = cn(disabled ? "cursor-not-allowed" : "cursor-pointer");
|
||||
|
||||
const interactiveChipClassName = interactiveChipStyles[type];
|
||||
const buttonClassNames = buttonStyles[chipType];
|
||||
const iconCss = "w-6 h-6";
|
||||
const hasIcons = start || end;
|
||||
const textRef = useAndApplyBoldTextWidth(children, "text-increase-size");
|
||||
|
||||
return (
|
||||
<div
|
||||
<button
|
||||
{...props}
|
||||
data-disabled={disabled ? "true" : "false"}
|
||||
aria-disabled={disabled ? "true" : "false"}
|
||||
data-testid={testId}
|
||||
aria-disabled={props.disabled ? "true" : "false"}
|
||||
className={cn(
|
||||
"flex flex-row items-center p-1 gap-x-1 rounded-lg",
|
||||
"active:data-[disabled=false]:scale-90 transition",
|
||||
interactiveChipClassName,
|
||||
className
|
||||
"px-1.5 py-1 min-w-32",
|
||||
"flex flex-row items-center gap-x-2",
|
||||
hasIcons ? " justify-between" : " justify-center",
|
||||
"group enabled:cursor-pointer focus:outline-0",
|
||||
buttonClassNames.button
|
||||
)}
|
||||
>
|
||||
{start && isValidElement(start) ? (
|
||||
<button
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
onClick={onStartClick}
|
||||
className={cn(buttonCss)}
|
||||
>
|
||||
{cloneElement(start, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</button>
|
||||
) : null}
|
||||
<Typography.Text fontSize="xs" className="text-inherit">
|
||||
{children}
|
||||
</Typography.Text>
|
||||
{cloneIcon(start, {
|
||||
className: cn(iconCss, buttonClassNames.icon),
|
||||
})}
|
||||
|
||||
{end && isValidElement(end) ? (
|
||||
<button
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
onClick={onEndClick}
|
||||
className={cn(buttonCss)}
|
||||
>
|
||||
{cloneElement(end, {
|
||||
className: iconCss,
|
||||
})}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<span
|
||||
ref={textRef}
|
||||
className={cn(
|
||||
"tg-family-outfit tg-xs text-center font-normal line-1",
|
||||
buttonClassNames.text,
|
||||
!props.disabled && `button-bold-text`
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
{cloneIcon(end, {
|
||||
className: cn(iconCss, buttonClassNames.icon),
|
||||
})}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
15
openhands-ui/components/interactive-chip/index.css
Normal file
15
openhands-ui/components/interactive-chip/index.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.group .button-bold-text::before,
|
||||
.group .button-bold-text::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: var(--text-increase-size);
|
||||
transition: width 0.2s ease;
|
||||
transition: font-weight 0.2s ease;
|
||||
}
|
||||
|
||||
.group:hover .button-bold-text::before,
|
||||
.group:hover .button-bold-text::after,
|
||||
.group:focus .button-bold-text::before,
|
||||
.group:focus .button-bold-text::after {
|
||||
width: 0;
|
||||
}
|
||||
@@ -1,30 +1,98 @@
|
||||
import { useEffect, useRef, type ReactNode } from "react";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
export type InteractiveChipType = "elevated" | "filled";
|
||||
|
||||
export const interactiveChipStyles: Record<InteractiveChipType, string> = {
|
||||
elevated: cn([
|
||||
// base
|
||||
"data-[disabled=false]:border-1 border-light-neutral-400 text-light-neutral-400 bg-light-neutral-950 font-normal",
|
||||
// hover modifier
|
||||
"hover:border-light-neutral-100 hover:data-[disabled=false]:text-light-neutral-100 hover:data-[disabled=false]:font-semibold hover:data-[disabled=false]:bg-light-neutral-800",
|
||||
// focus modifier
|
||||
"focus:border-light-neutral-100 focus:text-light-neutral-100 focus:font-semibold focus:bg-light-neutral-800",
|
||||
// active modifier
|
||||
"active:data-[disabled=false]:border-primary-500 active:data-[disabled=false]:text-primary-500",
|
||||
// disabled modifier
|
||||
"data-[disabled=true]:opacity-50 data-[disabled=true]:bg-light-neutral-900",
|
||||
]),
|
||||
filled: cn([
|
||||
// base
|
||||
"text-grey-985 bg-light-neutral-600 font-normal",
|
||||
// hover modifier
|
||||
"hover:data-[disabled=false]:font-semibold hover:data-[disabled=false]:bg-light-neutral-300",
|
||||
// focus modifier
|
||||
"focus:data-[disabled=false]:font-semibold focus:data-[disabled=false]:bg-light-neutral-300",
|
||||
// active modifier
|
||||
"active:data-[disabled=false]:bg-primary-100",
|
||||
// disabled modifier
|
||||
"data-[disabled=true]:opacity-40 data-[disabled=true]:bg-light-neutral-400",
|
||||
]),
|
||||
type ButtonStyle = {
|
||||
button: string;
|
||||
icon: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const buttonStyles: Record<InteractiveChipType, ButtonStyle> = {
|
||||
elevated: {
|
||||
button: cn([
|
||||
"ring-1 ring-solid ring-light-neutral-400 rounded-xl bg-light-neutral-950 transition-scale duration-200",
|
||||
// hover modifier
|
||||
"enabled:hover:bg-light-neutral-800 enabled:hover:ring-light-neutral-15",
|
||||
// focus modifier
|
||||
"enabled:focus:bg-light-neutral-800 enabled:focus:ring-light-neutral-15",
|
||||
// active modifier
|
||||
"enabled:active:ring-primary-500 enabled:active:scale-90 enabled:active:bg-light-neutral-900",
|
||||
// disabled modifier
|
||||
"disabled:opacity-40 disabled:bg-light-neutral-900 disabled:ring-0 disabled:font-medium",
|
||||
]),
|
||||
icon: cn([
|
||||
"text-light-neutral-400",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold group-enabled:group-hover:text-light-neutral-15",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus:font-semibold group-enabled:group-focus:text-light-neutral-15",
|
||||
// active modifier
|
||||
"group-enabled:group-active:text-primary-500",
|
||||
]),
|
||||
text: cn([
|
||||
"text-light-neutral-400",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold group-enabled:group-hover:text-light-neutral-15",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus:font-semibold group-enabled:group-focus:text-light-neutral-15",
|
||||
// active modifier
|
||||
"group-enabled:group-active:text-primary-500",
|
||||
]),
|
||||
},
|
||||
filled: {
|
||||
button: cn([
|
||||
"rounded-xl bg-light-neutral-600 transition-scale duration-200",
|
||||
// hover modifier
|
||||
"enabled:hover:bg-light-neutral-300",
|
||||
// focus modifier
|
||||
"enabled:focus:bg-light-neutral-300",
|
||||
// active modifier
|
||||
"enabled:active:scale-90 enabled:active:bg-primary-100",
|
||||
// disabled modifier
|
||||
"disabled:opacity-40 disabled:bg-light-neutral-400 disabled:font-medium",
|
||||
]),
|
||||
icon: cn([
|
||||
"text-light-neutral-985",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus:font-semibold",
|
||||
// active modifier
|
||||
"group-enabled:group-active:text-light-neutral-970",
|
||||
]),
|
||||
text: cn([
|
||||
"text-light-neutral-985",
|
||||
// hover modifier
|
||||
"group-enabled:group-hover:font-semibold",
|
||||
// focus modifier
|
||||
"group-enabled:group-focus:font-semibold",
|
||||
// active modifier
|
||||
"group-enabled:group-active:text-light-neutral-970",
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook that calculates and applies a CSS custom property (variable)
|
||||
* based on the length of a text node. Useful for adjusting spacing or layout
|
||||
* to account for changes in font weight, such as bold text rendering wider.
|
||||
*/
|
||||
const BOLD_TEXT_INCREASE = 0.15;
|
||||
export const useAndApplyBoldTextWidth = (
|
||||
textNode: ReactNode,
|
||||
varName: string
|
||||
) => {
|
||||
const textRef = useRef<HTMLSpanElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (textRef) {
|
||||
const charCount =
|
||||
typeof textNode === "string" ? (textNode as string).length : 0;
|
||||
const textIncrease = charCount * BOLD_TEXT_INCREASE;
|
||||
textRef.current!.style.setProperty(`--${varName}`, `${textIncrease}px`);
|
||||
}
|
||||
}, [textRef.current]);
|
||||
|
||||
return textRef;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useId } from "react";
|
||||
import type { HTMLProps, IOption } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps, IOption } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
import { RadioOption } from "./RadioOption";
|
||||
|
||||
@@ -11,7 +11,7 @@ export type RadioGroupProps<T extends string> = Omit<
|
||||
value: T;
|
||||
onChange: (option: IOption<T>) => void;
|
||||
labelClassName?: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const RadioGroup = <T extends string>({
|
||||
value,
|
||||
@@ -21,13 +21,17 @@ export const RadioGroup = <T extends string>({
|
||||
labelClassName,
|
||||
disabled,
|
||||
id: propId,
|
||||
testId,
|
||||
...props
|
||||
}: RadioGroupProps<T>) => {
|
||||
const generatedId = useId();
|
||||
const id = propId ?? generatedId;
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-y-1", className)}>
|
||||
<div
|
||||
data-testid={testId}
|
||||
className={cn("flex flex-col gap-y-1", className)}
|
||||
>
|
||||
{options.map((o) => (
|
||||
<RadioOption
|
||||
{...props}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useId } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { Typography } from "../typography/Typography";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
@@ -7,7 +7,7 @@ type RadioOptionProps = Omit<HTMLProps<"input">, "id" | "checked"> & {
|
||||
label: React.ReactNode;
|
||||
labelClassName?: string;
|
||||
id: string;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
export const RadioOption = ({
|
||||
className,
|
||||
@@ -17,6 +17,7 @@ export const RadioOption = ({
|
||||
id: propId,
|
||||
disabled,
|
||||
onChange,
|
||||
testId,
|
||||
...props
|
||||
}: RadioOptionProps) => {
|
||||
const generatedId = useId();
|
||||
@@ -25,6 +26,7 @@ export const RadioOption = ({
|
||||
return (
|
||||
<label
|
||||
htmlFor={id}
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"flex items-center gap-x-4",
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLProps } from "../../shared/types";
|
||||
import type { BaseProps, HTMLProps } from "../../shared/types";
|
||||
import { cn } from "../../shared/utils/cn";
|
||||
|
||||
export type ScrollableMode = "auto" | "scroll";
|
||||
@@ -8,7 +8,7 @@ export type ScrollableType = "horizontal" | "vertical";
|
||||
export type ScrollableProps = HTMLProps<"div"> & {
|
||||
mode?: ScrollableMode;
|
||||
type?: ScrollableType;
|
||||
};
|
||||
} & BaseProps;
|
||||
|
||||
const scrollableStyles: Record<
|
||||
ScrollableType,
|
||||
@@ -30,11 +30,13 @@ export const Scrollable = ({
|
||||
tabIndex,
|
||||
mode = "auto",
|
||||
type = "vertical",
|
||||
testId,
|
||||
...props
|
||||
}: PropsWithChildren<ScrollableProps>) => {
|
||||
const style = scrollableStyles[type][mode];
|
||||
return (
|
||||
<div
|
||||
data-testid={testId}
|
||||
tabIndex={tabIndex ?? 0}
|
||||
{...props}
|
||||
className={cn(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user