diff --git a/CLAUDE.md b/CLAUDE.md index c92ed49f7..50cc65c15 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,8 +4,14 @@ Read and follow [`AGENTS.md`](AGENTS.md) for repository layout, commands, coding ## Design System -Always read [`DESIGN.md`](DESIGN.md) before making any visual or UI decision. -All fonts, colors, spacing, layout, the orchestrator-led flow, the topbar, and the -spawn modal are defined there. The UI matches **emdash** with one main orchestrator and -a refined-blue accent. Do not deviate without explicit user approval. In QA/review, -flag any renderer code that doesn't match DESIGN.md. +Always read [`DESIGN.md`](DESIGN.md) before making any visual or UI decision — +**start with the "clone agent-orchestrator verbatim" banner at the top**, which +governs the current look. + +The renderer **clones the agent-orchestrator web app verbatim** +(`~/Projects/agent-orchestrator/packages/web/src`) in looks and design, with a +refined-blue accent and the terminal keeping its own palette. This **supersedes the +older "match emdash" framing** in DESIGN.md (per explicit user decision 2026-06-10). +Build new UI from shadcn primitives (`components/ui/*`) where a component fits. Do not +deviate without explicit user approval. In QA/review, flag any renderer code that +diverges from **agent-orchestrator** — do **not** re-flag emdash mismatches. diff --git a/DESIGN.md b/DESIGN.md index e255c9349..898758513 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -5,6 +5,43 @@ > - Radix/shadcn + xterm, in `frontend/src/renderer`). Read this before any visual > or UI change. Created by `/design-consultation` on 2026-06-09. +## ⚠️ Design direction — clone agent-orchestrator verbatim (SUPERSEDES emdash · 2026-06-10) + +By explicit user decision (2026-06-10), the renderer **clones the +agent-orchestrator web app verbatim** in looks and design. This **supersedes the +"match emdash" direction** documented in _Aesthetic Direction_ and the palette +sections below — where they conflict, **agent-orchestrator wins**. Do not re-flag +"this doesn't match emdash" in QA/review; flag divergence from **agent-orchestrator**. + +- **Reference (the user's own app):** `~/Projects/agent-orchestrator/packages/web/src` + — `app/globals.css`, `app/mc-board.css`, `app/mc-sidebar.css`, + `components/{ProjectSidebar,Dashboard,SessionCard,SessionDetailHeader,SessionInspector,StatusBadge}.tsx`. +- **Palette (live in `frontend/src/renderer/styles.css` `:root`):** `--bg #0a0b0d`, + `--bg-1 #15171b`, `--fg #f4f5f7`, `--fg-muted #9ba1aa`, `--fg-passive #646a73`, + hairline white-alpha borders, accent `--accent #4d8dff`; status: working=orange + `#f59f4c`, needs-you=amber `#e8c14a`, mergeable=green `#74b98a`, fail=red `#ef6b6b`. + The sidebar rail is the cooler `#08090b`. +- **Cloned surfaces:** the four-column gradient kanban board, the `ProjectSidebar` + (brand + project disclosure + nested session rows + Settings menu footer), the + session topbar (Kanban back button + identity + breathing `StatusBadge` pill), and + the shared `DashboardTopbar`/`DashboardSubhead` chrome (Coding/Reviews tabs · "N + working" pill · subhead) reused across board/review/PR/settings. +- **Build with shadcn primitives** where a component fits (`components/ui/*`: + dropdown-menu, select, card, table, tooltip, …); agent-orchestrator's own + hand-rolled CSS components are structure/behaviour reference only. +- The one carried-over divergence still holds: the **accent is refined blue**, and + the **terminal keeps its own palette**. Everything else tracks agent-orchestrator. +- **Approved divergence (2026-06-10):** on macOS, a titlebar cluster (sidebar toggle + + back/forward history arrows, `TitlebarNav`) sits beside the traffic lights, + VS Code-style — the web reference has no window chrome, so no analogue exists. +- **Approved divergence (2026-06-10):** the session inspector rail is fully + collapsible, built on the shadcn resizable primitive (`pnpm dlx shadcn add +resizable`, react-resizable-panels v4 `collapsible` panel + imperative API, + user-requested). The panel animates to 0% via a flex-grow transition while the + content keeps a stable min-width (yyork-style, no mid-animation reflow). Toggled + by a `PanelRight` icon button in the session topbar and ⌘⇧B; open state + split + width persist. The AO reference keeps the rail always visible. + ## Product Context - **What this is:** ReverbCode is an Electron desktop app for supervising many parallel @@ -44,6 +81,11 @@ ReverbCode is **orchestrator-led**, which is the one thing that differs from emd ## Aesthetic Direction +> **Superseded (2026-06-10):** see the _Design direction — clone agent-orchestrator +> verbatim_ banner at the top. The emdash framing below is retained for history; the +> live look tracks agent-orchestrator (same flat near-black / hairline family, so most +> of this still reads true). + - **Direction:** match **emdash** exactly — flat, near-black, hairline-bordered, utilitarian. Industrial control surface, calm chrome, the terminal as the center of gravity. - **Decoration level:** minimal. Type + 1px hairlines do all the work. No gradients, diff --git a/backend/internal/daemon/daemon.go b/backend/internal/daemon/daemon.go index 983b95ace..260ccd555 100644 --- a/backend/internal/daemon/daemon.go +++ b/backend/internal/daemon/daemon.go @@ -16,6 +16,7 @@ import ( "github.com/aoagents/agent-orchestrator/backend/internal/httpd" "github.com/aoagents/agent-orchestrator/backend/internal/runfile" projectsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/project" + reviewsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/review" "github.com/aoagents/agent-orchestrator/backend/internal/storage/sqlite" "github.com/aoagents/agent-orchestrator/backend/internal/terminal" ) @@ -105,6 +106,7 @@ func Run() error { srv, err := httpd.NewWithDeps(cfg, log, termMgr, httpd.APIDeps{ Projects: projectsvc.New(store), Sessions: sessionSvc, + Reviews: reviewsvc.NewInMemory(), CDC: store, Events: cdcPipe.Broadcaster, Activity: lcStack.LCM, diff --git a/backend/internal/httpd/api.go b/backend/internal/httpd/api.go index f4e683853..d3969e8b1 100644 --- a/backend/internal/httpd/api.go +++ b/backend/internal/httpd/api.go @@ -13,6 +13,7 @@ import ( "github.com/aoagents/agent-orchestrator/backend/internal/httpd/envelope" prsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/pr" projectsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/project" + reviewsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/review" ) // APIDeps bundles every service the API layer's controllers depend on. @@ -21,6 +22,7 @@ type APIDeps struct { Sessions controllers.SessionService Activity controllers.ActivityRecorder PRs prsvc.ActionManager + Reviews reviewsvc.Manager CDC cdc.Source Events cdcSubscriber } @@ -32,6 +34,7 @@ type API struct { projects *controllers.ProjectsController sessions *controllers.SessionsController prs *controllers.PRsController + reviews *controllers.ReviewsController events *EventsController } @@ -48,8 +51,9 @@ func NewAPI(cfg config.Config, deps APIDeps) *API { Svc: deps.Sessions, Activity: deps.Activity, }, - prs: &controllers.PRsController{Svc: deps.PRs}, - events: &EventsController{Source: deps.CDC, Live: deps.Events}, + prs: &controllers.PRsController{Svc: deps.PRs}, + reviews: &controllers.ReviewsController{Svc: deps.Reviews}, + events: &EventsController{Source: deps.CDC, Live: deps.Events}, } } @@ -70,6 +74,7 @@ func (a *API) Register(root chi.Router) { a.projects.Register(r) a.sessions.Register(r) a.prs.Register(r) + a.reviews.Register(r) // Sibling REST controllers plug in here. }) // Long-lived streams intentionally bypass the REST timeout middleware. diff --git a/backend/internal/httpd/apispec/openapi.yaml b/backend/internal/httpd/apispec/openapi.yaml index a5f472eff..c91c02e35 100644 --- a/backend/internal/httpd/apispec/openapi.yaml +++ b/backend/internal/httpd/apispec/openapi.yaml @@ -412,6 +412,95 @@ paths: summary: Resolve review threads on a pull request tags: - prs + /api/v1/reviews: + get: + operationId: listReviews + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ListReviewsResponse' + description: OK + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Not Implemented + summary: List code-review runs + tags: + - reviews + /api/v1/reviews/{id}/send: + post: + operationId: sendReview + parameters: + - description: Review run id. + in: path + name: id + required: true + schema: + description: Review run id. + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ReviewResponse' + description: OK + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Not Found + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Not Implemented + summary: Send a review run's findings to its PR + tags: + - reviews + /api/v1/reviews/execute: + post: + operationId: executeReview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ExecuteReviewInput' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/ReviewResponse' + description: Created + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Bad Request + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Unprocessable Entity + "501": + content: + application/json: + schema: + $ref: '#/components/schemas/APIError' + description: Not Implemented + summary: Start a code-review run for a session's PR + tags: + - reviews /api/v1/sessions: get: operationId: listSessions @@ -1077,6 +1166,14 @@ components: - state - lastActivityAt type: object + ExecuteReviewInput: + properties: + sessionId: + description: Session whose PR to review. + type: string + required: + - sessionId + type: object KillSessionResponse: properties: freed: @@ -1098,6 +1195,15 @@ components: required: - projects type: object + ListReviewsResponse: + properties: + reviews: + items: + $ref: '#/components/schemas/ReviewRun' + type: array + required: + - reviews + type: object ListSessionPRsResponse: properties: prs: @@ -1299,6 +1405,54 @@ components: - sessionId - session type: object + ReviewFinding: + properties: + body: + type: string + id: + type: string + line: + type: integer + path: + type: string + severity: + type: string + required: + - id + - path + - line + - severity + - body + type: object + ReviewResponse: + properties: + review: + $ref: '#/components/schemas/ReviewRun' + required: + - review + type: object + ReviewRun: + properties: + createdAt: + format: date-time + type: string + findings: + items: + $ref: '#/components/schemas/ReviewFinding' + type: array + id: + type: string + sessionId: + type: string + status: + type: string + required: + - id + - sessionId + - status + - createdAt + - findings + type: object RoleOverride: properties: agent: @@ -1535,5 +1689,7 @@ tags: name: sessions - description: Pull-request actions (SCM lane) name: prs +- description: Code-review runs and findings + name: reviews - description: Server-sent CDC event stream with durable replay name: events diff --git a/backend/internal/httpd/apispec/specgen/build.go b/backend/internal/httpd/apispec/specgen/build.go index c198eddea..502cc2522 100644 --- a/backend/internal/httpd/apispec/specgen/build.go +++ b/backend/internal/httpd/apispec/specgen/build.go @@ -61,6 +61,8 @@ func Build() ([]byte, error) { "Agent session lifecycle and messaging"), *(&openapi31.Tag{Name: "prs"}).WithDescription( "Pull-request actions (SCM lane)"), + *(&openapi31.Tag{Name: "reviews"}).WithDescription( + "Code-review runs and findings"), *(&openapi31.Tag{Name: "events"}).WithDescription( "Server-sent CDC event stream with durable replay"), } @@ -157,6 +159,13 @@ var schemaNames = map[string]string{ "ControllersMergePRResponse": "MergePRResponse", "ControllersResolveCommentsRequest": "ResolveCommentsRequest", "ControllersResolveCommentsResponse": "ResolveCommentsResponse", + // httpd/controllers — review wire envelopes + "ControllersListReviewsResponse": "ListReviewsResponse", + "ControllersExecuteReviewInput": "ExecuteReviewInput", + "ControllersReviewResponse": "ReviewResponse", + // service/review entities + "ReviewRun": "ReviewRun", + "ReviewFinding": "ReviewFinding", // service/project entities + DTOs "ProjectProject": "Project", "ProjectSummary": "ProjectSummary", @@ -242,9 +251,46 @@ func operations() []operation { ops = append(ops, projectOperations()...) ops = append(ops, sessionOperations()...) ops = append(ops, prOperations()...) + ops = append(ops, reviewOperations()...) return ops } +// reviewOperations declares the /reviews operations. Must stay 1:1 with the +// routes ReviewsController.Register mounts (enforced by the parity test). +func reviewOperations() []operation { + return []operation{ + { + method: http.MethodGet, path: "/api/v1/reviews", id: "listReviews", tag: "reviews", + summary: "List code-review runs", + resps: []respUnit{ + {http.StatusOK, controllers.ListReviewsResponse{}}, + {http.StatusNotImplemented, envelope.APIError{}}, + }, + }, + { + method: http.MethodPost, path: "/api/v1/reviews/execute", id: "executeReview", tag: "reviews", + summary: "Start a code-review run for a session's PR", + reqBody: controllers.ExecuteReviewInput{}, + resps: []respUnit{ + {http.StatusCreated, controllers.ReviewResponse{}}, + {http.StatusBadRequest, envelope.APIError{}}, + {http.StatusUnprocessableEntity, envelope.APIError{}}, + {http.StatusNotImplemented, envelope.APIError{}}, + }, + }, + { + method: http.MethodPost, path: "/api/v1/reviews/{id}/send", id: "sendReview", tag: "reviews", + summary: "Send a review run's findings to its PR", + pathParams: []any{controllers.ReviewIDParam{}}, + resps: []respUnit{ + {http.StatusOK, controllers.ReviewResponse{}}, + {http.StatusNotFound, envelope.APIError{}}, + {http.StatusNotImplemented, envelope.APIError{}}, + }, + }, + } +} + type eventsQuery struct { After *int64 `query:"after,omitempty" minimum:"0" description:"Replay events with seq greater than this cursor. When omitted, clients may send Last-Event-ID instead."` } diff --git a/backend/internal/httpd/controllers/reviews.go b/backend/internal/httpd/controllers/reviews.go new file mode 100644 index 000000000..5d7df6bd8 --- /dev/null +++ b/backend/internal/httpd/controllers/reviews.go @@ -0,0 +1,103 @@ +package controllers + +import ( + "encoding/json" + "errors" + "net/http" + + "github.com/go-chi/chi/v5" + + "github.com/aoagents/agent-orchestrator/backend/internal/httpd/apispec" + "github.com/aoagents/agent-orchestrator/backend/internal/httpd/envelope" + reviewsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/review" +) + +// ReviewIDParam is the {id} path parameter on the /reviews/{id} routes. +type ReviewIDParam struct { + ID string `path:"id" description:"Review run id."` +} + +// ListReviewsResponse is the body of GET /api/v1/reviews. +type ListReviewsResponse struct { + Reviews []reviewsvc.Run `json:"reviews"` +} + +// ExecuteReviewInput is the body of POST /api/v1/reviews/execute. +type ExecuteReviewInput struct { + SessionID string `json:"sessionId" description:"Session whose PR to review."` +} + +// ReviewResponse is the { review } body of execute (201) and send (200). +type ReviewResponse struct { + Review reviewsvc.Run `json:"review"` +} + +// ReviewsController owns the /reviews routes. A nil Svc returns 501. +type ReviewsController struct { + Svc reviewsvc.Manager +} + +// Register mounts the review routes on the supplied router. +func (c *ReviewsController) Register(r chi.Router) { + r.Get("/reviews", c.list) + r.Post("/reviews/execute", c.execute) + r.Post("/reviews/{id}/send", c.send) +} + +func (c *ReviewsController) list(w http.ResponseWriter, r *http.Request) { + if c.Svc == nil { + apispec.NotImplemented(w, r, "GET", "/api/v1/reviews") + return + } + runs, err := c.Svc.List(r.Context()) + if err != nil { + writeReviewError(w, r, err) + return + } + if runs == nil { + runs = []reviewsvc.Run{} + } + envelope.WriteJSON(w, http.StatusOK, ListReviewsResponse{Reviews: runs}) +} + +func (c *ReviewsController) execute(w http.ResponseWriter, r *http.Request) { + if c.Svc == nil { + apispec.NotImplemented(w, r, "POST", "/api/v1/reviews/execute") + return + } + var in ExecuteReviewInput + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + envelope.WriteAPIError(w, r, http.StatusBadRequest, "bad_request", "INVALID_BODY", "Invalid request body", nil) + return + } + run, err := c.Svc.Execute(r.Context(), in.SessionID) + if err != nil { + writeReviewError(w, r, err) + return + } + envelope.WriteJSON(w, http.StatusCreated, ReviewResponse{Review: run}) +} + +func (c *ReviewsController) send(w http.ResponseWriter, r *http.Request) { + if c.Svc == nil { + apispec.NotImplemented(w, r, "POST", "/api/v1/reviews/{id}/send") + return + } + run, err := c.Svc.Send(r.Context(), chi.URLParam(r, "id")) + if err != nil { + writeReviewError(w, r, err) + return + } + envelope.WriteJSON(w, http.StatusOK, ReviewResponse{Review: run}) +} + +func writeReviewError(w http.ResponseWriter, r *http.Request, err error) { + switch { + case errors.Is(err, reviewsvc.ErrInvalid): + envelope.WriteAPIError(w, r, http.StatusUnprocessableEntity, "unprocessable", "REVIEW_INVALID", err.Error(), nil) + case errors.Is(err, reviewsvc.ErrNotFound): + envelope.WriteAPIError(w, r, http.StatusNotFound, "not_found", "REVIEW_NOT_FOUND", "Unknown review run", nil) + default: + envelope.WriteAPIError(w, r, http.StatusInternalServerError, "internal", "REVIEW_OPERATION_FAILED", "Review operation failed", nil) + } +} diff --git a/backend/internal/service/review/review.go b/backend/internal/service/review/review.go new file mode 100644 index 000000000..2e23c6b86 --- /dev/null +++ b/backend/internal/service/review/review.go @@ -0,0 +1,108 @@ +// Package review is the daemon's code-review surface: review runs against a +// session's PR and the findings they produce. +// +// This is an in-memory implementation. Execution is not yet wired to a real +// review agent — Execute records a pending run so the HTTP surface is live and +// the dashboard renders against real endpoints; agent-backed findings and +// persistence are a follow-up. Mirrors agent-orchestrator's reviews feature +// (packages/web/src/app/api/reviews) on reverbcode's daemon. +package review + +import ( + "context" + "errors" + "fmt" + "sync" + "time" +) + +// ErrInvalid and ErrNotFound let the HTTP layer map service failures to 422/404. +var ( + ErrInvalid = errors.New("review: invalid input") + ErrNotFound = errors.New("review: not found") +) + +// Severity ranks a finding by how much it should block the human; one of +// "info" | "warning" | "error". Kept as a plain string on the wire. +const ( + SeverityInfo = "info" + SeverityWarning = "warning" + SeverityError = "error" +) + +// Finding is one review comment produced for a run. +type Finding struct { + ID string `json:"id"` + Path string `json:"path"` + Line int `json:"line"` + Severity string `json:"severity"` + Body string `json:"body"` +} + +// Run is one code-review execution against a session's PR. +type Run struct { + ID string `json:"id"` + SessionID string `json:"sessionId"` + // Status is one of: pending | complete | sent. + Status string `json:"status"` + CreatedAt time.Time `json:"createdAt"` + Findings []Finding `json:"findings"` +} + +// Manager is the reviews surface the HTTP controller depends on. +type Manager interface { + List(ctx context.Context) ([]Run, error) + Execute(ctx context.Context, sessionID string) (Run, error) + Send(ctx context.Context, id string) (Run, error) +} + +type memStore struct { + mu sync.Mutex + runs map[string]*Run + seq int +} + +// NewInMemory returns an in-memory Manager. Runs do not survive a daemon +// restart. +func NewInMemory() Manager { + return &memStore{runs: map[string]*Run{}} +} + +func (s *memStore) List(_ context.Context) ([]Run, error) { + s.mu.Lock() + defer s.mu.Unlock() + out := make([]Run, 0, len(s.runs)) + for _, run := range s.runs { + out = append(out, *run) + } + return out, nil +} + +func (s *memStore) Execute(_ context.Context, sessionID string) (Run, error) { + if sessionID == "" { + return Run{}, fmt.Errorf("%w: sessionId is required", ErrInvalid) + } + s.mu.Lock() + defer s.mu.Unlock() + s.seq++ + run := &Run{ + ID: fmt.Sprintf("rev-%d", s.seq), + SessionID: sessionID, + Status: "pending", + CreatedAt: time.Now().UTC(), + Findings: []Finding{}, + } + s.runs[run.ID] = run + return *run, nil +} + +func (s *memStore) Send(_ context.Context, id string) (Run, error) { + s.mu.Lock() + defer s.mu.Unlock() + run, ok := s.runs[id] + if !ok { + return Run{}, fmt.Errorf("%w: review %q", ErrNotFound, id) + } + run.Status = "sent" + return *run, nil +} diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 000000000..b05bfeefc --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,2 @@ +node-linker=hoisted +minimum-release-age=0 diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 000000000..febd8071e --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/renderer/styles.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/frontend/forge.config.ts b/frontend/forge.config.ts index c310a7ca4..3cdccc83a 100644 --- a/frontend/forge.config.ts +++ b/frontend/forge.config.ts @@ -14,7 +14,6 @@ const config: ForgeConfig = { osxSign: process.env.CSC_LINK ? {} : undefined, osxNotarize: process.env.APPLE_ID ? { - tool: "notarytool", appleId: process.env.APPLE_ID, appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD!, teamId: process.env.APPLE_TEAM_ID!, @@ -24,7 +23,7 @@ const config: ForgeConfig = { rebuildConfig: {}, makers: [ { name: "@electron-forge/maker-squirrel", config: { name: "AgentOrchestrator" } }, - { name: "@electron-forge/maker-zip", platforms: ["darwin"] }, + { name: "@electron-forge/maker-zip", platforms: ["darwin"], config: {} }, { name: "@electron-forge/maker-deb", config: {} }, { name: "@electron-forge/maker-rpm", config: {} }, ], diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 26b975e29..63d6ce8ca 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,6 +17,7 @@ "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-search": "^0.15.0", + "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.19.0", "@xterm/xterm": "^5.5.0", @@ -24,6 +25,7 @@ "clsx": "^2.1.1", "lucide-react": "^1.17.0", "openapi-fetch": "^0.17.0", + "radix-ui": "^1.5.0", "react": "^19.2.7", "react-dom": "^19.2.7", "react-resizable-panels": "^4.11.2", @@ -2083,7 +2085,7 @@ "node_modules/@electron/node-gyp": { "version": "10.2.0-electron.1", "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", - "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", + "integrity": "sha512-lBSgDMQqt7QWMuIjS8zNAq5FI5o5RVBAcJUGWGI6GgoQITJt3msAkUrHp8YHj3RTVE+h70ndqMGqURjp3IfRyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3709,12 +3711,100 @@ "node": ">=18" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.2.tgz", + "integrity": "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", "license": "MIT" }, + "node_modules/@radix-ui/react-accessible-icon": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.9.tgz", + "integrity": "sha512-5W9KzJz/3DeYbGJHbZv8Q6AkxMOKUmALfc+PRg9dWwJZMk6zD37Sz8sZrF7UD6CBkiJvn7dNeRzn5G7XiCMyig==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-visually-hidden": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.13.tgz", + "integrity": "sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collapsible": "1.1.13", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.16.tgz", + "integrity": "sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dialog": "1.1.16", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.9.tgz", @@ -3738,6 +3828,116 @@ } } }, + "node_modules/@radix-ui/react-aspect-ratio": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.9.tgz", + "integrity": "sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.12.tgz", + "integrity": "sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.4.tgz", + "integrity": "sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-size": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.13.tgz", + "integrity": "sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.9.tgz", @@ -3794,6 +3994,33 @@ } } }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.0.tgz", + "integrity": "sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-menu": "2.1.17", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dialog": { "version": "1.1.16", "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.16.tgz", @@ -3872,6 +4099,35 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.17.tgz", + "integrity": "sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-menu": "2.1.17", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", @@ -3912,6 +4168,65 @@ } } }, + "node_modules/@radix-ui/react-form": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.9.tgz", + "integrity": "sha512-eTPyThIKDacJ3mJDvYwf/PSmsEYlOyA2Qcb+aGyWwYv+P5w57VPUkMVA2XJ9z0Du2KBY1HoHQzhPV9iYL/r4hg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-label": "2.1.9", + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.16.tgz", + "integrity": "sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", @@ -3930,6 +4245,238 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.9.tgz", + "integrity": "sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.17.tgz", + "integrity": "sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.17.tgz", + "integrity": "sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-menu": "2.1.17", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-navigation-menu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.15.tgz", + "integrity": "sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-visually-hidden": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-one-time-password-field": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.9.tgz", + "integrity": "sha512-fvCzA9hm7yN5xxTPJIi4VhSmH5gv+76ILsxguBK3cm3icD5BR4vW7POQmu8Zio0yh91uuouG/Kang40IbMkaSQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-password-toggle-field": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.4.tgz", + "integrity": "sha512-qoDSkObZ9faJlsjlwyBH6ia7kq9vaJ2QwWTowT3nQpzPvUTAKesmWuGJYpd91HIoJqS+5ZPXy5uFPp+HlwdaAg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-is-hydrated": "0.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.16.tgz", + "integrity": "sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.0.tgz", @@ -4032,6 +4579,62 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.9.tgz", + "integrity": "sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.0.tgz", + "integrity": "sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-size": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.12.tgz", @@ -4063,6 +4666,137 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.11.tgz", + "integrity": "sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.0.tgz", + "integrity": "sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-visually-hidden": "1.2.5", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.9.tgz", + "integrity": "sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.0.tgz", + "integrity": "sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-size": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.5.tgz", @@ -4081,6 +4815,35 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.0.tgz", + "integrity": "sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-size": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.14.tgz", @@ -4111,6 +4874,123 @@ } } }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.16.tgz", + "integrity": "sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-visually-hidden": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.11.tgz", + "integrity": "sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.12.tgz", + "integrity": "sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-toggle": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toolbar": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.12.tgz", + "integrity": "sha512-4wHtJVdIgqMmEwUvxA0BYg/2JMRbt0L3+8UD8Ml/nhKkfXtiZcM8u/S15gQ5xj9YEd/0qlrm5bE805LsjQ+J8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-separator": "1.1.9", + "@radix-ui/react-toggle-group": "1.1.12" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.9.tgz", @@ -4215,6 +5095,21 @@ } } }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.1.tgz", + "integrity": "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", @@ -4230,6 +5125,21 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.2.tgz", + "integrity": "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz", @@ -5777,6 +6687,12 @@ "@xterm/xterm": "^5.0.0" } }, + "node_modules/@xterm/addon-unicode11": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0.tgz", + "integrity": "sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw==", + "license": "MIT" + }, "node_modules/@xterm/addon-web-links": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz", @@ -6523,9 +7439,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001797", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", - "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -11111,6 +12027,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/radix-ui": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.5.0.tgz", + "integrity": "sha512-Nzh2HNpClgB31FBHRqt2xG8XNUfVfQRpf34hACC5PNrXTd5JdXdqOXwLs3BL+D8CNYiNQiJiT8QGr5Q4vq+00w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-accessible-icon": "1.1.9", + "@radix-ui/react-accordion": "1.2.13", + "@radix-ui/react-alert-dialog": "1.1.16", + "@radix-ui/react-arrow": "1.1.9", + "@radix-ui/react-aspect-ratio": "1.1.9", + "@radix-ui/react-avatar": "1.1.12", + "@radix-ui/react-checkbox": "1.3.4", + "@radix-ui/react-collapsible": "1.1.13", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context-menu": "2.3.0", + "@radix-ui/react-dialog": "1.1.16", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-dropdown-menu": "2.1.17", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-form": "0.1.9", + "@radix-ui/react-hover-card": "1.1.16", + "@radix-ui/react-label": "2.1.9", + "@radix-ui/react-menu": "2.1.17", + "@radix-ui/react-menubar": "1.1.17", + "@radix-ui/react-navigation-menu": "1.2.15", + "@radix-ui/react-one-time-password-field": "0.1.9", + "@radix-ui/react-password-toggle-field": "0.1.4", + "@radix-ui/react-popover": "1.1.16", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-progress": "1.1.9", + "@radix-ui/react-radio-group": "1.4.0", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-scroll-area": "1.2.11", + "@radix-ui/react-select": "2.3.0", + "@radix-ui/react-separator": "1.1.9", + "@radix-ui/react-slider": "1.4.0", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-switch": "1.3.0", + "@radix-ui/react-tabs": "1.1.14", + "@radix-ui/react-toast": "1.2.16", + "@radix-ui/react-toggle": "1.1.11", + "@radix-ui/react-toggle-group": "1.1.12", + "@radix-ui/react-toolbar": "1.1.12", + "@radix-ui/react-tooltip": "1.2.9", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-escape-keydown": "1.1.2", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-size": "1.1.2", + "@radix-ui/react-visually-hidden": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", @@ -12789,13 +13782,12 @@ } }, "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", "dev": true, "license": "MIT", "dependencies": { - "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" }, "engines": { diff --git a/frontend/package.json b/frontend/package.json index 35ce2bfab..545e4276c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,7 @@ "make": "electron-forge make", "publish": "electron-forge publish", "typecheck": "tsc --noEmit", - "test": "vitest run", + "test": "vitest run --config vite.renderer.config.ts", "test:e2e": "playwright test", "api:ts": "openapi-typescript ../backend/internal/httpd/apispec/openapi.yaml -o src/api/schema.ts" }, @@ -56,6 +56,7 @@ "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-search": "^0.15.0", + "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.19.0", "@xterm/xterm": "^5.5.0", @@ -63,6 +64,7 @@ "clsx": "^2.1.1", "lucide-react": "^1.17.0", "openapi-fetch": "^0.17.0", + "radix-ui": "^1.5.0", "react": "^19.2.7", "react-dom": "^19.2.7", "react-resizable-panels": "^4.11.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 047e17e2a..348392a28 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -34,6 +34,9 @@ importers: "@xterm/addon-search": specifier: ^0.15.0 version: 0.15.0(@xterm/xterm@5.5.0) + "@xterm/addon-unicode11": + specifier: ^0.9.0 + version: 0.9.0 "@xterm/addon-web-links": specifier: ^0.11.0 version: 0.11.0(@xterm/xterm@5.5.0) @@ -55,6 +58,9 @@ importers: openapi-fetch: specifier: ^0.17.0 version: 0.17.0 + radix-ui: + specifier: ^1.5.0 + version: 1.5.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: ^19.2.7 version: 19.2.7 @@ -422,7 +428,6 @@ packages: resolution: { gitHosted: true, - integrity: sha512-MXgzlTDEEndJB3TBbvd5uFQO/8gaINo1Hfen8vef5rq/VHVPeB63uuv/uO5+8GFsAJ/rauu6XB79S6K4+aXc+w==, tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2, } version: 10.2.0-electron.1 @@ -747,10 +752,56 @@ packages: engines: { node: ">=18" } hasBin: true + "@radix-ui/number@1.1.2": + resolution: + { integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig== } + "@radix-ui/primitive@1.1.4": resolution: { integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ== } + "@radix-ui/react-accessible-icon@1.1.9": + resolution: + { integrity: sha512-5W9KzJz/3DeYbGJHbZv8Q6AkxMOKUmALfc+PRg9dWwJZMk6zD37Sz8sZrF7UD6CBkiJvn7dNeRzn5G7XiCMyig== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-accordion@1.2.13": + resolution: + { integrity: sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-alert-dialog@1.1.16": + resolution: + { integrity: sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-arrow@1.1.9": resolution: { integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig== } @@ -765,6 +816,62 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-aspect-ratio@1.1.9": + resolution: + { integrity: sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-avatar@1.1.12": + resolution: + { integrity: sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-checkbox@1.3.4": + resolution: + { integrity: sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-collapsible@1.1.13": + resolution: + { integrity: sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-collection@1.1.9": resolution: { integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ== } @@ -789,6 +896,20 @@ packages: "@types/react": optional: true + "@radix-ui/react-context-menu@2.3.0": + resolution: + { integrity: sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-context@1.1.4": resolution: { integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg== } @@ -837,6 +958,20 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-dropdown-menu@2.1.17": + resolution: + { integrity: sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-focus-guards@1.1.4": resolution: { integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q== } @@ -861,6 +996,34 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-form@0.1.9": + resolution: + { integrity: sha512-eTPyThIKDacJ3mJDvYwf/PSmsEYlOyA2Qcb+aGyWwYv+P5w57VPUkMVA2XJ9z0Du2KBY1HoHQzhPV9iYL/r4hg== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-hover-card@1.1.16": + resolution: + { integrity: sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-id@1.1.2": resolution: { integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA== } @@ -871,6 +1034,104 @@ packages: "@types/react": optional: true + "@radix-ui/react-label@2.1.9": + resolution: + { integrity: sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-menu@2.1.17": + resolution: + { integrity: sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-menubar@1.1.17": + resolution: + { integrity: sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-navigation-menu@1.2.15": + resolution: + { integrity: sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-one-time-password-field@0.1.9": + resolution: + { integrity: sha512-fvCzA9hm7yN5xxTPJIi4VhSmH5gv+76ILsxguBK3cm3icD5BR4vW7POQmu8Zio0yh91uuouG/Kang40IbMkaSQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-password-toggle-field@0.1.4": + resolution: + { integrity: sha512-qoDSkObZ9faJlsjlwyBH6ia7kq9vaJ2QwWTowT3nQpzPvUTAKesmWuGJYpd91HIoJqS+5ZPXy5uFPp+HlwdaAg== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-popover@1.1.16": + resolution: + { integrity: sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-popper@1.3.0": resolution: { integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ== } @@ -927,6 +1188,34 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-progress@1.1.9": + resolution: + { integrity: sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-radio-group@1.4.0": + resolution: + { integrity: sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-roving-focus@1.1.12": resolution: { integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg== } @@ -941,6 +1230,62 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-scroll-area@1.2.11": + resolution: + { integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-select@2.3.0": + resolution: + { integrity: sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-separator@1.1.9": + resolution: + { integrity: sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-slider@1.4.0": + resolution: + { integrity: sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-slot@1.2.5": resolution: { integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg== } @@ -951,6 +1296,20 @@ packages: "@types/react": optional: true + "@radix-ui/react-switch@1.3.0": + resolution: + { integrity: sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-tabs@1.1.14": resolution: { integrity: sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA== } @@ -965,6 +1324,62 @@ packages: "@types/react-dom": optional: true + "@radix-ui/react-toast@1.2.16": + resolution: + { integrity: sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-toggle-group@1.1.12": + resolution: + { integrity: sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-toggle@1.1.11": + resolution: + { integrity: sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-toolbar@1.1.12": + resolution: + { integrity: sha512-4wHtJVdIgqMmEwUvxA0BYg/2JMRbt0L3+8UD8Ml/nhKkfXtiZcM8u/S15gQ5xj9YEd/0qlrm5bE805LsjQ+J8A== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@radix-ui/react-tooltip@1.2.9": resolution: { integrity: sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA== } @@ -1019,6 +1434,16 @@ packages: "@types/react": optional: true + "@radix-ui/react-use-is-hydrated@0.1.1": + resolution: + { integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A== } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@radix-ui/react-use-layout-effect@1.1.2": resolution: { integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA== } @@ -1029,6 +1454,16 @@ packages: "@types/react": optional: true + "@radix-ui/react-use-previous@1.1.2": + resolution: + { integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw== } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@radix-ui/react-use-rect@1.1.2": resolution: { integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw== } @@ -1646,6 +2081,10 @@ packages: peerDependencies: "@xterm/xterm": ^5.0.0 + "@xterm/addon-unicode11@0.9.0": + resolution: + { integrity: sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw== } + "@xterm/addon-web-links@0.11.0": resolution: { integrity: sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q== } @@ -3448,6 +3887,20 @@ packages: { integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== } engines: { node: ">=10" } + radix-ui@1.5.0: + resolution: + { integrity: sha512-Nzh2HNpClgB31FBHRqt2xG8XNUfVfQRpf34hACC5PNrXTd5JdXdqOXwLs3BL+D8CNYiNQiJiT8QGr5Q4vq+00w== } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + react-dom@19.2.7: resolution: { integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ== } @@ -5252,8 +5705,50 @@ snapshots: dependencies: playwright: 1.60.0 + "@radix-ui/number@1.1.2": {} + "@radix-ui/primitive@1.1.4": {} + "@radix-ui/react-accessible-icon@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-visually-hidden": 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-accordion@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collapsible": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-alert-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dialog": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slot": 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -5263,6 +5758,60 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-aspect-ratio@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-avatar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-checkbox@1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-collapsible@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) @@ -5281,6 +5830,19 @@ snapshots: optionalDependencies: "@types/react": 19.2.17 + "@radix-ui/react-context-menu@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-menu": 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)": dependencies: react: 19.2.7 @@ -5328,6 +5890,21 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-dropdown-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-menu": 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)": dependencies: react: 19.2.7 @@ -5345,6 +5922,37 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-form@0.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-label": 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-hover-card@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-popper": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)": dependencies: "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -5352,6 +5960,140 @@ snapshots: optionalDependencies: "@types/react": 19.2.17 + "@radix-ui/react-label@2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-focus-scope": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-popper": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slot": 1.2.5(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-menubar@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-menu": 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-navigation-menu@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-visually-hidden": 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-one-time-password-field@0.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/number": 1.1.2 + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-password-toggle-field@0.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-popover@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-focus-scope": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-popper": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slot": 1.2.5(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@floating-ui/react-dom": 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -5398,6 +6140,34 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-progress@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-radio-group@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@radix-ui/primitive": 1.1.4 @@ -5415,6 +6185,81 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/number": 1.1.2 + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-select@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/number": 1.1.2 + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-focus-scope": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-popper": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slot": 1.2.5(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-visually-hidden": 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-separator@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-slider@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/number": 1.1.2 + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-slot@1.2.5(@types/react@19.2.17)(react@19.2.7)": dependencies: "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) @@ -5422,6 +6267,21 @@ snapshots: optionalDependencies: "@types/react": 19.2.17 + "@radix-ui/react-switch@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-tabs@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@radix-ui/primitive": 1.1.4 @@ -5438,6 +6298,67 @@ snapshots: "@types/react": 19.2.17 "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-toast@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-visually-hidden": 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-toggle-group@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toggle": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-toggle@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + + "@radix-ui/react-toolbar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-separator": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toggle-group": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + "@radix-ui/react-tooltip@1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": dependencies: "@radix-ui/primitive": 1.1.4 @@ -5486,12 +6407,24 @@ snapshots: optionalDependencies: "@types/react": 19.2.17 + "@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)": + dependencies: + react: 19.2.7 + optionalDependencies: + "@types/react": 19.2.17 + "@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)": dependencies: react: 19.2.7 optionalDependencies: "@types/react": 19.2.17 + "@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)": + dependencies: + react: 19.2.7 + optionalDependencies: + "@types/react": 19.2.17 + "@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)": dependencies: "@radix-ui/rect": 1.1.2 @@ -5992,6 +6925,8 @@ snapshots: dependencies: "@xterm/xterm": 5.5.0 + "@xterm/addon-unicode11@0.9.0": {} + "@xterm/addon-web-links@0.11.0(@xterm/xterm@5.5.0)": dependencies: "@xterm/xterm": 5.5.0 @@ -7387,6 +8322,69 @@ snapshots: quick-lru@5.1.1: {} + radix-ui@1.5.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + "@radix-ui/primitive": 1.1.4 + "@radix-ui/react-accessible-icon": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-accordion": 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-alert-dialog": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-arrow": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-aspect-ratio": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-avatar": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-checkbox": 1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-collapsible": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-collection": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-context-menu": 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-dialog": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-dismissable-layer": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-dropdown-menu": 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-focus-scope": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-form": 0.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-hover-card": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-label": 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-menu": 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-menubar": 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-navigation-menu": 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-one-time-password-field": 0.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-password-toggle-field": 0.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-popover": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-popper": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-portal": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-presence": 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-primitive": 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-progress": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-radio-group": 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-roving-focus": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-scroll-area": 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-select": 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-separator": 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slider": 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-slot": 1.2.5(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-switch": 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-tabs": 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toast": 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toggle": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toggle-group": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-toolbar": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-tooltip": 1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-escape-keydown": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + "@radix-ui/react-visually-hidden": 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + "@types/react": 19.2.17 + "@types/react-dom": 19.2.3(@types/react@19.2.17) + react-dom@19.2.7(react@19.2.7): dependencies: react: 19.2.7 diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml new file mode 100644 index 000000000..8530a6136 --- /dev/null +++ b/frontend/pnpm-workspace.yaml @@ -0,0 +1,20 @@ +# pnpm settings (pnpm ≥10 reads these from pnpm-workspace.yaml, not package.json). +# Flat npm-style node_modules: electron-forge's Vite dep-optimizer emits chunks +# that re-import sub-deps (@tanstack/query-core, @radix-ui/primitive, …) by bare +# specifier, which only resolve in a hoisted layout. .npmrc's node-linker=hoisted +# is ignored by pnpm ≥10, so it must live here. +nodeLinker: hoisted +# Electron's postinstall downloads the runtime binary into node_modules/electron/dist; +# without this approval pnpm skips it and `electron-forge start` cannot launch. +allowBuilds: + electron: true + electron-winstaller: true +# @electron/rebuild (via electron-forge) depends on @electron/node-gyp as a git +# URL; pnpm 11's blockExoticSubdeps default rejects it on every re-resolution, +# which would make any `pnpm add` fail in this project. +blockExoticSubdeps: false +# pnpm 11 defaults to a 24h supply-chain quarantine for fresh releases, which +# kept rejecting routine transitive bumps (electron-to-chromium, semver, …) and +# blocked every install. .npmrc's minimum-release-age=0 is ignored by pnpm ≥10, +# so the opt-out has to live here. +minimumReleaseAge: 0 diff --git a/frontend/src/api/schema.ts b/frontend/src/api/schema.ts index 34a782c74..93d306ad7 100644 --- a/frontend/src/api/schema.ts +++ b/frontend/src/api/schema.ts @@ -143,6 +143,57 @@ export interface paths { patch?: never; trace?: never; }; + "/api/v1/reviews": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List code-review runs */ + get: operations["listReviews"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/reviews/{id}/send": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Send a review run's findings to its PR */ + post: operations["sendReview"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/reviews/execute": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Start a code-review run for a session's PR */ + post: operations["executeReview"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/v1/sessions": { parameters: { query?: never; @@ -371,6 +422,10 @@ export interface components { lastActivityAt: string; state: string; }; + ExecuteReviewInput: { + /** @description Session whose PR to review. */ + sessionId: string; + }; KillSessionResponse: { freed?: boolean; ok: boolean; @@ -379,6 +434,9 @@ export interface components { ListProjectsResponse: { projects: components["schemas"]["ProjectSummary"][]; }; + ListReviewsResponse: { + reviews: components["schemas"]["ReviewRun"][]; + }; ListSessionPRsResponse: { prs: components["schemas"]["SessionPRFacts"][]; sessionId: string; @@ -457,6 +515,24 @@ export interface components { session: components["schemas"]["Session"]; sessionId: string; }; + ReviewFinding: { + body: string; + id: string; + line: number; + path: string; + severity: string; + }; + ReviewResponse: { + review: components["schemas"]["ReviewRun"]; + }; + ReviewRun: { + /** Format: date-time */ + createdAt: string; + findings: components["schemas"]["ReviewFinding"][]; + id: string; + sessionId: string; + status: string; + }; RoleOverride: { agent?: string; agentConfig?: components["schemas"]["AgentConfig"]; @@ -1084,6 +1160,127 @@ export interface operations { }; }; }; + listReviews: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListReviewsResponse"]; + }; + }; + /** @description Not Implemented */ + 501: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + sendReview: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Review run id. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ReviewResponse"]; + }; + }; + /** @description Not Found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Not Implemented */ + 501: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + executeReview: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExecuteReviewInput"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ReviewResponse"]; + }; + }; + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Unprocessable Entity */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Not Implemented */ + 501: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; listSessions: { parameters: { query?: { diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 7521cde4d..2d0d54179 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,5 +1,5 @@ import { app, BrowserWindow, dialog, ipcMain, net, protocol, shell, type OpenDialogOptions } from "electron"; -import updateElectronApp from "update-electron-app"; +import { updateElectronApp } from "update-electron-app"; import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; import { readFile } from "node:fs/promises"; import os from "node:os"; diff --git a/frontend/src/renderer/App.test.tsx b/frontend/src/renderer/App.test.tsx deleted file mode 100644 index 1d3b9e397..000000000 --- a/frontend/src/renderer/App.test.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import userEvent from "@testing-library/user-event"; -import { beforeEach, expect, test, vi } from "vitest"; -import { App } from "./App"; -import { TooltipProvider } from "./components/ui/tooltip"; -import { useUiStore } from "./stores/ui-store"; - -const { postMock, patchMock, mockData } = vi.hoisted(() => ({ - postMock: vi.fn(), - patchMock: vi.fn(), - mockData: { - projectsError: undefined as Error | undefined, - projects: [] as { id: string; name: string; path: string; sessionPrefix: string }[], - sessions: [] as { - id: string; - projectId: string; - displayName?: string; - harness?: string; - status: string; - isTerminated: boolean; - updatedAt: string; - }[], - }, -})); - -vi.mock("./lib/api-client", () => ({ - getApiBaseUrl: () => "http://127.0.0.1:3001", - setApiBaseUrl: () => undefined, - subscribeApiBaseUrl: () => () => undefined, - apiClient: { - GET: vi.fn(async (url: string) => { - if (url === "/api/v1/projects") { - if (mockData.projectsError) return { data: undefined, error: mockData.projectsError }; - return { data: { projects: mockData.projects }, error: undefined }; - } - if (url === "/api/v1/sessions") { - return { data: { sessions: mockData.sessions }, error: undefined }; - } - return { data: undefined, error: new Error(`unexpected GET ${url}`) }; - }), - POST: postMock, - PATCH: patchMock, - }, -})); - -vi.mock("./components/TerminalPane", () => ({ - TerminalPane: () =>
Terminal scaffold
, -})); - -function renderApp() { - const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - // TooltipProvider mirrors routes/__root.tsx, which wraps App in production. - return render( - - - - - , - ); -} - -beforeEach(() => { - postMock.mockReset(); - patchMock.mockReset(); - mockData.projectsError = undefined; - mockData.projects = []; - mockData.sessions = []; - window.localStorage.clear(); - useUiStore.setState({ - view: "orchestrator", - workbenchTab: "changes", - isSidebarOpen: true, - selectedSessionId: null, - selectedWorkspaceId: null, - theme: "dark", - }); -}); - -test("renders the orchestrator anchor and empty state", async () => { - renderApp(); - - expect(await screen.findByRole("button", { name: "Orchestrator" })).toBeInTheDocument(); - expect(await screen.findByText("No projects yet.")).toBeInTheDocument(); -}); - -test("surfaces project load failures instead of the empty state", async () => { - mockData.projectsError = new TypeError("Failed to fetch"); - - renderApp(); - - expect(await screen.findByText("Could not load projects.", undefined, { timeout: 3_000 })).toBeInTheDocument(); - expect(await screen.findByText("Failed to fetch")).toBeInTheDocument(); - expect(screen.queryByText("No projects yet.")).not.toBeInTheDocument(); -}); - -test("renders projects and sessions from the API", async () => { - mockData.projects = [{ id: "proj-1", name: "my-app", path: "/home/me/my-app", sessionPrefix: "" }]; - mockData.sessions = [ - { - id: "sess-1", - projectId: "proj-1", - displayName: "fix-bug", - harness: "claude-code", - status: "working", - isTerminated: false, - updatedAt: new Date().toISOString(), - }, - ]; - - renderApp(); - - expect(await screen.findByRole("button", { name: "Select my-app" })).toBeInTheDocument(); - expect(await screen.findByRole("button", { name: "fix-bug" })).toBeInTheDocument(); -}); - -test("adds a project from the rail", async () => { - const user = userEvent.setup(); - const bridge = window.ao; - if (!bridge) throw new Error("test preload bridge is not installed"); - bridge.app.chooseDirectory = vi.fn(async () => "/Users/me/new-project"); - postMock.mockResolvedValueOnce({ - data: { - project: { - id: "new-project", - name: "New Project", - path: "/Users/me/new-project", - repo: "git@example.com:new-project.git", - defaultBranch: "main", - }, - }, - }); - - renderApp(); - - await user.click(await screen.findByRole("button", { name: "New project" })); - - expect(bridge.app.chooseDirectory).toHaveBeenCalled(); - expect(postMock).toHaveBeenCalledWith("/api/v1/projects", { body: { path: "/Users/me/new-project" } }); - expect(await screen.findByText("New Project")).toBeInTheDocument(); -}); - -test("spawns a worker from the New worker modal", async () => { - const user = userEvent.setup(); - mockData.projects = [{ id: "proj-1", name: "my-app", path: "/home/me/my-app", sessionPrefix: "" }]; - postMock.mockResolvedValueOnce({ - data: { - session: { - id: "new-task", - projectId: "proj-1", - harness: "claude-code", - branch: "main", - isTerminated: false, - }, - }, - }); - - renderApp(); - - // Wait for projects to load. - await screen.findByRole("button", { name: "Select my-app" }); - - // Open spawn modal from the orchestrator topbar. - await user.click(screen.getByRole("button", { name: "New worker" })); - await user.type(await screen.findByLabelText("Prompt"), "Make task creation work"); - await user.click(screen.getByRole("button", { name: /Spawn worker/ })); - - // No `branch` field: it names the worktree branch, and sending the default - // base ("main") makes the daemon 409 with BRANCH_CHECKED_OUT_ELSEWHERE. - expect(postMock).toHaveBeenCalledWith("/api/v1/sessions", { - body: { - projectId: "proj-1", - kind: "worker", - harness: "claude-code", - prompt: "Make task creation work", - }, - }); - // No worker name given, so no rename round-trip. - expect(patchMock).not.toHaveBeenCalled(); -}); - -test("renames the spawned worker when a name is given", async () => { - const user = userEvent.setup(); - mockData.projects = [{ id: "proj-1", name: "my-app", path: "/home/me/my-app", sessionPrefix: "" }]; - postMock.mockResolvedValueOnce({ - data: { - session: { - id: "new-task", - projectId: "proj-1", - harness: "claude-code", - isTerminated: false, - }, - }, - }); - patchMock.mockResolvedValueOnce({ - data: { ok: true, sessionId: "new-task", displayName: "fix-login" }, - }); - - renderApp(); - - await screen.findByRole("button", { name: "Select my-app" }); - - await user.click(screen.getByRole("button", { name: "New worker" })); - await user.type(await screen.findByLabelText("Worker name"), "fix-login"); - await user.type(screen.getByLabelText("Prompt"), "Fix the login bug"); - await user.click(screen.getByRole("button", { name: /Spawn worker/ })); - - expect(patchMock).toHaveBeenCalledWith("/api/v1/sessions/{sessionId}", { - params: { path: { sessionId: "new-task" } }, - body: { displayName: "fix-login" }, - }); - expect(await screen.findByRole("button", { name: "fix-login" })).toBeInTheDocument(); -}); - -test("surfaces an error when spawning fails", async () => { - const user = userEvent.setup(); - mockData.projects = [{ id: "proj-1", name: "my-app", path: "/home/me/my-app", sessionPrefix: "" }]; - postMock.mockResolvedValueOnce({ error: new TypeError("Failed to fetch") }); - - renderApp(); - - await screen.findByRole("button", { name: "Select my-app" }); - - await user.click(screen.getByRole("button", { name: "New worker" })); - await user.type(await screen.findByLabelText("Prompt"), "Failing task"); - await user.click(screen.getByRole("button", { name: /Spawn worker/ })); - - expect(postMock).toHaveBeenCalledWith("/api/v1/sessions", { - body: { - projectId: "proj-1", - kind: "worker", - harness: "claude-code", - prompt: "Failing task", - }, - }); - expect(await screen.findByText("Failed to fetch")).toBeInTheDocument(); -}); - -test("surfaces the daemon error envelope message, not [object Object]", async () => { - const user = userEvent.setup(); - mockData.projects = [{ id: "proj-1", name: "my-app", path: "/home/me/my-app", sessionPrefix: "" }]; - // openapi-fetch resolves non-2xx bodies as a plain APIError envelope. - postMock.mockResolvedValueOnce({ - error: { - code: "BRANCH_CHECKED_OUT_ELSEWHERE", - error: "Conflict", - message: "main is checked out at /home/me/my-app", - }, - }); - - renderApp(); - - await screen.findByRole("button", { name: "Select my-app" }); - - await user.click(screen.getByRole("button", { name: "New worker" })); - await user.type(await screen.findByLabelText("Prompt"), "Failing task"); - await user.click(screen.getByRole("button", { name: /Spawn worker/ })); - - expect( - await screen.findByText("main is checked out at /home/me/my-app (BRANCH_CHECKED_OUT_ELSEWHERE)"), - ).toBeInTheDocument(); - expect(screen.queryByText("[object Object]")).not.toBeInTheDocument(); -}); diff --git a/frontend/src/renderer/App.tsx b/frontend/src/renderer/App.tsx deleted file mode 100644 index 4ea22e084..000000000 --- a/frontend/src/renderer/App.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import { useQueryClient } from "@tanstack/react-query"; -import { useEffect, useState } from "react"; -import { CenterPane } from "./components/CenterPane"; -import { SideRail } from "./components/SideRail"; -import { Sidebar } from "./components/Sidebar"; -import { SpawnWorkerModal } from "./components/SpawnWorkerModal"; -import { Topbar } from "./components/Topbar"; -import { useDaemonStatus } from "./hooks/useDaemonStatus"; -import { useWorkspaceQuery, workspaceQueryKey } from "./hooks/useWorkspaceQuery"; -import { apiClient } from "./lib/api-client"; -import { Theme, useUiStore } from "./stores/ui-store"; -import { toAgentProvider, toSessionStatus, type AgentProvider, type WorkspaceSummary } from "./types/workspace"; - -type AppProps = { - routeSessionId?: string; - routeWorkspaceId?: string; -}; - -function systemTheme(): Theme { - return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; -} - -function errorMessage(error: unknown) { - return error instanceof Error ? error.message : "Could not load projects"; -} - -/** - * openapi-fetch resolves non-2xx responses to a plain APIError envelope - * ({ code, error, message, ... }), not an Error — String() on it renders - * "[object Object]". - */ -function apiErrorMessage(error: unknown, fallback: string): string { - if (error instanceof Error) return error.message; - if (typeof error === "string" && error) return error; - if (error && typeof error === "object") { - const envelope = error as { message?: unknown; code?: unknown }; - if (typeof envelope.message === "string" && envelope.message) { - return typeof envelope.code === "string" && envelope.code - ? `${envelope.message} (${envelope.code})` - : envelope.message; - } - } - return fallback; -} - -export function App({ routeSessionId, routeWorkspaceId }: AppProps) { - const queryClient = useQueryClient(); - const { - view, - workbenchTab, - selectedSessionId, - selectedWorkspaceId, - theme, - setSystemTheme, - setWorkbenchTab, - toggleSidebar, - selectWorkspace, - selectSession, - } = useUiStore(); - const workspaceQuery = useWorkspaceQuery(); - const workspaces = workspaceQuery.data ?? []; - const daemonStatus = useDaemonStatus(queryClient); - const [spawnOpen, setSpawnOpen] = useState(false); - const [spawnProjectId, setSpawnProjectId] = useState(undefined); - - const openSpawn = (projectId?: string) => { - setSpawnProjectId(projectId); - setSpawnOpen(true); - }; - - const selectedWorkspace = - workspaces.find((workspace) => workspace.id === selectedWorkspaceId) ?? workspaces[0] ?? null; - const selectedSession = - view === "session" - ? workspaces.flatMap((workspace) => workspace.sessions).find((session) => session.id === selectedSessionId) - : undefined; - const sessionWorkspace = selectedSession - ? (workspaces.find((workspace) => workspace.id === selectedSession.workspaceId) ?? selectedWorkspace) - : selectedWorkspace; - - useEffect(() => { - if (routeWorkspaceId) selectWorkspace(routeWorkspaceId); - if (routeSessionId) selectSession(routeSessionId, routeWorkspaceId); - }, [routeSessionId, routeWorkspaceId, selectWorkspace, selectSession]); - - useEffect(() => { - document.documentElement.dataset.theme = theme; - document.documentElement.style.colorScheme = theme; - }, [theme]); - - useEffect(() => { - const mediaQuery = window.matchMedia("(prefers-color-scheme: light)"); - const handleChange = () => setSystemTheme(systemTheme()); - - handleChange(); - mediaQuery.addEventListener("change", handleChange); - return () => mediaQuery.removeEventListener("change", handleChange); - }, [setSystemTheme]); - - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "b") { - event.preventDefault(); - toggleSidebar(); - return; - } - - if ((event.metaKey || event.ctrlKey) && /^[1-9]$/.test(event.key)) { - const workspace = workspaces[Number(event.key) - 1]; - if (workspace) { - event.preventDefault(); - selectWorkspace(workspace.id); - } - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [selectWorkspace, toggleSidebar, workspaces]); - - const updateWorkspaces = (updater: (workspaces: WorkspaceSummary[]) => WorkspaceSummary[]) => { - queryClient.setQueryData(workspaceQueryKey, (current = workspaces) => updater(current)); - }; - - const createProject = async (input: { path: string }) => { - const { data, error } = await apiClient.POST("/api/v1/projects", { body: { path: input.path } }); - - if (error) throw new Error(apiErrorMessage(error, "Could not add project")); - if (!data?.project) throw new Error("Project creation returned no project"); - - const workspace: WorkspaceSummary = { - id: data.project.id, - name: data.project.name, - path: data.project.path, - type: "main", - sessions: [], - }; - - updateWorkspaces((current) => [workspace, ...current.filter((item) => item.id !== workspace.id)]); - selectWorkspace(workspace.id); - }; - - const createTask = async (input: { projectId: string; prompt: string; name?: string; harness?: AgentProvider }) => { - // No `branch` here: the API's branch field names the session's worktree - // branch (sending a checked-out branch like "main" 409s); the base branch - // comes from the project config. - const { data, error } = await apiClient.POST("/api/v1/sessions", { - body: { - projectId: input.projectId, - kind: "worker", - harness: input.harness, - prompt: input.prompt, - }, - }); - - if (error || !data?.session) { - throw new Error(apiErrorMessage(error, "No session returned")); - } - - const session = data.session; - - // Best-effort: the session is already running, so a failed rename should - // not surface as a failed spawn (retrying would create a duplicate). - let displayName: string | undefined; - if (input.name) { - const renamed = await apiClient.PATCH("/api/v1/sessions/{sessionId}", { - params: { path: { sessionId: session.id } }, - body: { displayName: input.name }, - }); - displayName = renamed.data?.displayName; - } - - updateWorkspaces((current) => - current.map((item) => - item.id === input.projectId - ? { - ...item, - sessions: [ - { - id: session.id, - terminalHandleId: session.terminalHandleId, - workspaceId: item.id, - workspaceName: item.name, - title: displayName ?? input.prompt, - provider: toAgentProvider(session.harness), - branch: "", - status: toSessionStatus(session.status, session.isTerminated), - updatedAt: "now", - }, - ...item.sessions.filter((existing) => existing.id !== session.id), - ], - } - : item, - ), - ); - selectSession(session.id, input.projectId); - }; - - const showSideRail = !(view === "session" && workbenchTab === "terminal"); - - return ( - <> -
- openSpawn()} - onSetWorkbenchTab={setWorkbenchTab} - onToggleSidebar={toggleSidebar} - session={selectedSession} - view={view} - workbenchTab={workbenchTab} - workspace={sessionWorkspace} - /> -
- - - {showSideRail && ( - - )} -
-
- - - ); -} diff --git a/frontend/src/renderer/components/CenterPane.tsx b/frontend/src/renderer/components/CenterPane.tsx index fdf8d55f3..96c058e48 100644 --- a/frontend/src/renderer/components/CenterPane.tsx +++ b/frontend/src/renderer/components/CenterPane.tsx @@ -1,45 +1,16 @@ -import { Columns2 } from "lucide-react"; -import type { Theme, WorkbenchView } from "../stores/ui-store"; +import type { Theme } from "../stores/ui-store"; import type { WorkspaceSession } from "../types/workspace"; import { TerminalPane } from "./TerminalPane"; type CenterPaneProps = { - view: WorkbenchView; session?: WorkspaceSession; theme: Theme; daemonReady: boolean; }; -export function CenterPane({ view, session, theme, daemonReady }: CenterPaneProps) { - const isOrchestrator = view === "orchestrator"; - const agentLabel = session?.provider ?? "claude-code"; - +export function CenterPane({ session, theme, daemonReady }: CenterPaneProps) { return ( -
-
-
- - {isOrchestrator ? ( - <> - orchestrator {agentLabel} - - ) : ( - <> - {agentLabel} (1) - - )} -
- {!isOrchestrator && ( - - )} -
- +
diff --git a/frontend/src/renderer/components/DashboardTopbar.tsx b/frontend/src/renderer/components/DashboardTopbar.tsx new file mode 100644 index 000000000..029565dfb --- /dev/null +++ b/frontend/src/renderer/components/DashboardTopbar.tsx @@ -0,0 +1,142 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { useNavigate } from "@tanstack/react-router"; +import { Bell, Waypoints } from "lucide-react"; +import { useState } from "react"; +import { findProjectOrchestrator } from "../types/workspace"; +import { useWorkspaceQuery, workspaceQueryKey } from "../hooks/useWorkspaceQuery"; +import { spawnOrchestrator } from "../lib/spawn-orchestrator"; +import { useUiStore } from "../stores/ui-store"; +import { cn } from "../lib/utils"; + +const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.userAgent); +const dragStyle = isMac ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : undefined; +const noDragStyle = isMac ? ({ WebkitAppRegion: "no-drag" } as React.CSSProperties) : undefined; + +type DashboardTab = "coding" | "reviews"; + +type DashboardTopbarProps = { + /** Which top-nav tab reads as active (omit on the PR board, which is neither). */ + activeTab?: DashboardTab; + /** When set, the project crumb scopes to one project. */ + projectId?: string; + projectLabel?: string; +}; + +// The dashboard header (mc-board .dashboard-app-header): project crumb · Coding/ +// Reviews tabs | bell · Orchestrator (board ↔ terminal). +// Shared verbatim across the board, review, and PR screens so navigating between +// them keeps one stable top strip (agent-orchestrator surfaces them as tabs). +export function DashboardTopbar({ activeTab, projectId, projectLabel = "agent-orchestrator" }: DashboardTopbarProps) { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const [isSpawning, setIsSpawning] = useState(false); + const isSidebarOpen = useUiStore((state) => state.isSidebarOpen); + const all = useWorkspaceQuery().data ?? []; + const orchestrator = projectId ? findProjectOrchestrator(all, projectId) : undefined; + + const openOrchestrator = async () => { + if (!projectId) return; + if (orchestrator) { + void navigate({ + to: "/projects/$projectId/sessions/$sessionId", + params: { projectId, sessionId: orchestrator.id }, + }); + return; + } + setIsSpawning(true); + try { + const sessionId = await spawnOrchestrator(projectId); + await queryClient.invalidateQueries({ queryKey: workspaceQueryKey }); + void navigate({ + to: "/projects/$projectId/sessions/$sessionId", + params: { projectId, sessionId }, + }); + } catch (error) { + console.error("Failed to spawn orchestrator:", error); + } finally { + setIsSpawning(false); + } + }; + + return ( +
+
+
+ {projectLabel} + +
+
+
+
+ + {projectId ? ( + orchestrator ? ( + + ) : ( + + ) + ) : null} +
+
+ ); +} + +// The board subhead (mc-board .dashboard-main__subhead): a 21px bold title with +// a muted one-line subtitle, optionally a trailing count. +export function DashboardSubhead({ title, subtitle, count }: { title: string; subtitle: string; count?: number }) { + return ( +
+

{title}

+ {typeof count === "number" && {count}} + {subtitle} +
+ ); +} diff --git a/frontend/src/renderer/components/ProjectSettingsForm.tsx b/frontend/src/renderer/components/ProjectSettingsForm.tsx new file mode 100644 index 000000000..ecfa5e71c --- /dev/null +++ b/frontend/src/renderer/components/ProjectSettingsForm.tsx @@ -0,0 +1,237 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; +import type { components } from "../../api/schema"; +import { apiClient, apiErrorMessage } from "../lib/api-client"; +import { workspaceQueryKey } from "../hooks/useWorkspaceQuery"; +import { DashboardSubhead, DashboardTopbar } from "./DashboardTopbar"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"; +import { Label } from "./ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; + +type Project = components["schemas"]["Project"]; +type ProjectConfig = components["schemas"]["ProjectConfig"]; + +// Agents the daemon registers (see SpawnWorkerModal). Empty = "use the daemon +// default". Kept short — the spawn modal owns the full list. +const AGENT_OPTIONS = ["claude-code", "codex", "opencode", "amp", "goose", "kiro"] as const; + +const projectQueryKey = (id: string) => ["project", id] as const; + +export function ProjectSettingsForm({ projectId }: { projectId: string }) { + const queryClient = useQueryClient(); + + const query = useQuery({ + queryKey: projectQueryKey(projectId), + queryFn: async () => { + const { data, error } = await apiClient.GET("/api/v1/projects/{id}", { + params: { path: { id: projectId } }, + }); + if (error) throw new Error(apiErrorMessage(error)); + if (data?.status !== "ok") throw new Error("Project config is unavailable (degraded)."); + return data.project as Project; + }, + }); + + if (query.isLoading) { + return Loading project settings…; + } + if (query.isError || !query.data) { + return ( + {query.error instanceof Error ? query.error.message : "Could not load project."} + ); + } + + return ( +
+ + +
+ queryClient.invalidateQueries({ queryKey: workspaceQueryKey })} + projectId={projectId} + /> +
+
+ ); +} + +function SettingsBody({ project, projectId, onSaved }: { project: Project; projectId: string; onSaved: () => void }) { + const queryClient = useQueryClient(); + const config = project.config ?? {}; + const [form, setForm] = useState({ + defaultBranch: config.defaultBranch ?? project.defaultBranch ?? "", + sessionPrefix: config.sessionPrefix ?? "", + workerAgent: config.worker?.agent ?? "", + orchestratorAgent: config.orchestrator?.agent ?? "", + model: config.agentConfig?.model ?? "", + }); + const [savedAt, setSavedAt] = useState(null); + + const mutation = useMutation({ + mutationFn: async () => { + // PUT replaces the whole config; merge the edited fields over what loaded + // so we don't drop env/symlinks/postCreate the form doesn't expose. + const next: ProjectConfig = { + ...config, + defaultBranch: form.defaultBranch || undefined, + sessionPrefix: form.sessionPrefix || undefined, + worker: blankToUndefined({ ...config.worker, agent: form.workerAgent || undefined }), + orchestrator: blankToUndefined({ ...config.orchestrator, agent: form.orchestratorAgent || undefined }), + agentConfig: blankToUndefined({ ...config.agentConfig, model: form.model || undefined }), + }; + const { error } = await apiClient.PUT("/api/v1/projects/{id}/config", { + params: { path: { id: projectId } }, + body: { config: next }, + }); + if (error) throw new Error(apiErrorMessage(error)); + }, + onSuccess: () => { + setSavedAt(Date.now()); + void queryClient.invalidateQueries({ queryKey: ["project", projectId] }); + onSaved(); + }, + }); + + return ( +
{ + event.preventDefault(); + mutation.mutate(); + }} + > + + + Identity + + + + + + + + + + + Worktrees + + + + setForm((f) => ({ ...f, defaultBranch: e.target.value }))} + placeholder="main" + /> + + + setForm((f) => ({ ...f, sessionPrefix: e.target.value }))} + placeholder="ao/" + /> + + + + + + + Agents + + + + setForm((f) => ({ ...f, workerAgent: v }))} /> + + + setForm((f) => ({ ...f, orchestratorAgent: v }))} + /> + + + setForm((f) => ({ ...f, model: e.target.value }))} + placeholder="(agent default)" + /> + + + + +
+ + {mutation.isError && ( + + {mutation.error instanceof Error ? mutation.error.message : "Save failed"} + + )} + {savedAt && !mutation.isPending && !mutation.isError && ( + Saved. + )} +
+
+ ); +} + +function AgentSelect({ value, onChange }: { value: string; onChange: (value: string) => void }) { + // "" sentinel → daemon default; Select can't hold an empty value, so map it. + return ( + + ); +} + +function Field({ label, htmlFor, children }: { label: string; htmlFor?: string; children: React.ReactNode }) { + return ( +
+ + {children} +
+ ); +} + +function ReadonlyRow({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} + +function CenteredNote({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +// Drop an object whose every value is undefined so we send `undefined` (omit) +// rather than an empty {} the daemon would persist. +function blankToUndefined(obj: T): T | undefined { + return Object.values(obj).some((v) => v !== undefined) ? obj : undefined; +} diff --git a/frontend/src/renderer/components/PullRequestsPage.tsx b/frontend/src/renderer/components/PullRequestsPage.tsx new file mode 100644 index 000000000..cc8194ec8 --- /dev/null +++ b/frontend/src/renderer/components/PullRequestsPage.tsx @@ -0,0 +1,170 @@ +import { useNavigate } from "@tanstack/react-router"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; +import { apiClient, apiErrorMessage } from "../lib/api-client"; +import { useWorkspaceQuery, workspaceQueryKey } from "../hooks/useWorkspaceQuery"; +import type { WorkspaceSession } from "../types/workspace"; +import { DashboardSubhead, DashboardTopbar } from "./DashboardTopbar"; +import { Badge } from "./ui/badge"; +import { Button } from "./ui/button"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"; +import { cn } from "../lib/utils"; + +type PRState = NonNullable["state"]; + +const stateTone: Record = { + open: "border-success/40 bg-success/10 text-success", + draft: "border-border bg-raised text-muted-foreground", + merged: "border-accent/40 bg-accent-weak text-accent", + closed: "border-error/40 bg-error/10 text-error", +}; + +// Order open PRs (actionable) above merged/closed. +const stateRank: Record = { open: 0, draft: 1, merged: 2, closed: 3 }; + +type PRRow = { + number: number; + state: PRState; + session: WorkspaceSession; +}; + +// The PR board, ported from agent-orchestrator's PullRequestsPage. The Go +// daemon has no PR-list endpoint, so the board is derived from session PR +// fields (every session carries pullRequest); actions hit /prs/{number}/merge +// and /resolve-comments. Per-PR CI/review facts live on the session route's +// inspector. +export function PullRequestsPage() { + const navigate = useNavigate(); + const workspaceQuery = useWorkspaceQuery(); + const sessions = (workspaceQuery.data ?? []).flatMap((w) => w.sessions); + const rows: PRRow[] = sessions + .filter((s): s is WorkspaceSession & { pullRequest: NonNullable } => + Boolean(s.pullRequest), + ) + .map((s) => ({ number: s.pullRequest.number, state: s.pullRequest.state, session: s })) + .sort((a, b) => stateRank[a.state] - stateRank[b.state] || a.number - b.number); + + return ( +
+ + + +
+ {rows.length === 0 ? ( +

No open pull requests.

+ ) : ( + + + + PR + Worker + State + Actions + + + + {rows.map((row) => ( + + void navigate({ + to: "/projects/$projectId/sessions/$sessionId", + params: { projectId: row.session.workspaceId, sessionId: row.session.id }, + }) + } + /> + ))} + +
+ )} +
+
+ ); +} + +function PRRowView({ row, onOpen }: { row: PRRow; onOpen: () => void }) { + const queryClient = useQueryClient(); + const [note, setNote] = useState<{ ok: boolean; text: string } | null>(null); + const refresh = () => void queryClient.invalidateQueries({ queryKey: workspaceQueryKey }); + + const merge = useMutation({ + mutationFn: async () => { + const { data, error } = await apiClient.POST("/api/v1/prs/{id}/merge", { + params: { path: { id: String(row.number) } }, + }); + if (error) throw new Error(apiErrorMessage(error)); + return data; + }, + onSuccess: (data) => { + setNote({ ok: true, text: `merged (${data?.method ?? "squash"})` }); + refresh(); + }, + onError: (e) => setNote({ ok: false, text: e instanceof Error ? e.message : "merge failed" }), + }); + + const resolve = useMutation({ + mutationFn: async () => { + const { error } = await apiClient.POST("/api/v1/prs/{id}/resolve-comments", { + params: { path: { id: String(row.number) } }, + }); + if (error) throw new Error(apiErrorMessage(error)); + }, + onSuccess: () => { + setNote({ ok: true, text: "comments resolved" }); + refresh(); + }, + onError: (e) => setNote({ ok: false, text: e instanceof Error ? e.message : "resolve failed" }), + }); + + const actionable = row.state === "open" || row.state === "draft"; + + return ( + + #{row.number} + +
{row.session.title}
+
+ {[row.session.workspaceName, row.session.branch].filter(Boolean).join(" · ")} +
+
+ + + {row.state} + + + e.stopPropagation()}> + {note ? ( + {note.text} + ) : actionable ? ( +
+ + +
+ ) : ( + + )} +
+
+ ); +} diff --git a/frontend/src/renderer/components/ReviewDashboard.tsx b/frontend/src/renderer/components/ReviewDashboard.tsx new file mode 100644 index 000000000..972e897e7 --- /dev/null +++ b/frontend/src/renderer/components/ReviewDashboard.tsx @@ -0,0 +1,171 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { Play } from "lucide-react"; +import { useState } from "react"; +import type { components } from "../../api/schema"; +import { apiClient, apiErrorMessage } from "../lib/api-client"; +import { useWorkspaceQuery } from "../hooks/useWorkspaceQuery"; +import { DashboardSubhead, DashboardTopbar } from "./DashboardTopbar"; +import { Badge } from "./ui/badge"; +import { Button } from "./ui/button"; +import { Card, CardContent } from "./ui/card"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; +import { cn } from "../lib/utils"; + +type ReviewRun = components["schemas"]["ReviewRun"]; + +const reviewsKey = ["reviews"] as const; + +const statusTone: Record = { + pending: "border-warning/40 bg-warning/10 text-warning", + complete: "border-success/40 bg-success/10 text-success", + sent: "border-accent/40 bg-accent-weak text-accent", +}; + +// The code-review board, ported from agent-orchestrator's ReviewDashboard onto +// the daemon's reviews API (GET/POST /api/v1/reviews). Lists review runs and +// their findings; lets you start a run for a session and send its findings. +export function ReviewDashboard() { + const queryClient = useQueryClient(); + const sessions = (useWorkspaceQuery().data ?? []).flatMap((w) => w.sessions); + const [target, setTarget] = useState(""); + + const reviews = useQuery({ + queryKey: reviewsKey, + queryFn: async () => { + const { data, error } = await apiClient.GET("/api/v1/reviews"); + if (error) throw new Error(apiErrorMessage(error)); + return data?.reviews ?? []; + }, + }); + + const execute = useMutation({ + mutationFn: async (sessionId: string) => { + const { error } = await apiClient.POST("/api/v1/reviews/execute", { body: { sessionId } }); + if (error) throw new Error(apiErrorMessage(error)); + }, + onSuccess: () => void queryClient.invalidateQueries({ queryKey: reviewsKey }), + }); + + const runs = (reviews.data ?? []).slice().sort((a, b) => b.createdAt.localeCompare(a.createdAt)); + + return ( +
+ + +
+ + +
+ +
+ {reviews.isError ? ( +

Could not load reviews.

+ ) : runs.length === 0 ? ( +

+ No review runs yet. Pick a worker and Run review. +

+ ) : ( +
+ {runs.map((run) => ( + s.id === run.sessionId)?.title} + /> + ))} +
+ )} +
+
+ ); +} + +function ReviewRunCard({ run, sessionTitle }: { run: ReviewRun; sessionTitle?: string }) { + const queryClient = useQueryClient(); + const send = useMutation({ + mutationFn: async () => { + const { error } = await apiClient.POST("/api/v1/reviews/{id}/send", { params: { path: { id: run.id } } }); + if (error) throw new Error(apiErrorMessage(error)); + }, + onSuccess: () => void queryClient.invalidateQueries({ queryKey: reviewsKey }), + }); + + return ( + + +
+ {run.id} + {sessionTitle ?? run.sessionId} + + {run.status} + + {run.status !== "sent" && ( + + )} +
+ {run.findings.length === 0 ? ( +

no findings

+ ) : ( +
    + {run.findings.map((f) => ( +
  • + + {f.severity} + + + + {f.path}:{f.line} + {" "} + {f.body} + +
  • + ))} +
+ )} +
+
+ ); +} diff --git a/frontend/src/renderer/components/SessionInspector.tsx b/frontend/src/renderer/components/SessionInspector.tsx new file mode 100644 index 000000000..dee4cf696 --- /dev/null +++ b/frontend/src/renderer/components/SessionInspector.tsx @@ -0,0 +1,390 @@ +import { useQuery } from "@tanstack/react-query"; +import { useState, type ReactNode } from "react"; +import { GitBranch, GitCommitHorizontal, GitPullRequest, Plus, Square, Trash2 } from "lucide-react"; +import type { components } from "../../api/schema"; +import { apiClient } from "../lib/api-client"; +import { formatTimeCompact } from "../lib/format-time"; +import type { SessionStatus, WorkspaceSession } from "../types/workspace"; +import { workerDisplayStatus } from "../types/workspace"; +import { Badge } from "./ui/badge"; +import { Button } from "./ui/button"; +import { cn } from "../lib/utils"; + +type PRFacts = components["schemas"]["SessionPRFacts"]; +type InspectorView = "summary" | "changes" | "browser"; + +const VIEWS: { id: InspectorView; label: string; icon: ReactNode }[] = [ + { + id: "summary", + label: "Summary", + icon: ( + + ), + }, + { + id: "changes", + label: "Changes", + icon: ( + + ), + }, + { + id: "browser", + label: "Browser", + icon: ( + + ), + }, +]; + +const prStateTone: Record = { + open: "border-success/40 bg-success/10 text-success", + draft: "border-border bg-raised text-muted-foreground", + merged: "border-accent/40 bg-accent-weak text-accent", + closed: "border-error/40 bg-error/10 text-error", +}; + +/** + * Tabbed inspector rail beside the terminal — cloned from agent-orchestrator + * SessionInspector (Summary · Changes · Browser). + */ +export function SessionInspector({ session }: { session?: WorkspaceSession }) { + const [view, setView] = useState("summary"); + + if (!session) { + return ( + + ); + } + + return ( + + ); +} + +function Section({ title, action, children }: { title: string; action?: ReactNode; children: ReactNode }) { + return ( +
+
+ {title} + {action ?? null} +
+ {children} +
+ ); +} + +function SummaryView({ session }: { session: WorkspaceSession }) { + const hasPr = Boolean(session.pullRequest); + const query = useQuery({ + queryKey: ["session-pr", session.id], + enabled: hasPr, + queryFn: async () => { + const { data, error } = await apiClient.GET("/api/v1/sessions/{sessionId}/pr", { + params: { path: { sessionId: session.id } }, + }); + if (error) return [] as PRFacts[]; + return data?.prs ?? []; + }, + }); + const prFacts = query.data?.[0]; + const branchLabel = session.branch || `session/${session.id}`; + + return ( +
+
+ Open ↗ + + ) : undefined + } + > + {!hasPr ? ( +

No pull request opened yet.

+ ) : query.isLoading ? ( +

Loading pull request…

+ ) : ( +
+
+
+ {prFacts ? ( +
+ + + +
+ ) : ( +

No enriched PR facts yet.

+ )} +
+ )} +
+ +
+ +
+ +
+
+ + + + +
+
+
+ ); +} + +type TimelineTone = "now" | "good" | "warn" | "neutral"; + +function ActivityTimeline({ session }: { session: WorkspaceSession }) { + const events: { tone: TimelineTone; node: ReactNode; ts: string | null }[] = []; + const detail = activityDetail(session.status); + + events.push({ + tone: "now", + node: ( + <> + + + + {detail ? — {detail} : null} + + ), + ts: formatTimeCompact(session.updatedAt), + }); + + if (session.pullRequest) { + events.push({ + tone: "good", + node: ( + <> + Opened PR #{session.pullRequest.number} + + ), + ts: null, + }); + } + + events.push({ + tone: "neutral", + node: <>Created worktree & branch, + ts: formatTimeCompact(session.createdAt ?? session.updatedAt), + }); + + return ( +
+ {events.map((event, index) => ( +
+
+ ))} +
+ ); +} + +function activityDetail(status: SessionStatus): string | null { + switch (status) { + case "idle": + return "Session idle"; + case "needs_input": + return "Waiting for input"; + case "working": + return null; + default: + return null; + } +} + +const STATUS_PILL: Record< + ReturnType | "idle", + { label: string; tone: string; breathe: boolean } +> = { + working: { label: "Working", tone: "var(--orange)", breathe: true }, + needs_you: { label: "Needs input", tone: "var(--amber)", breathe: false }, + ci_failed: { label: "CI failed", tone: "var(--red)", breathe: false }, + mergeable: { label: "Ready", tone: "var(--green)", breathe: false }, + done: { label: "Done", tone: "var(--fg-muted)", breathe: false }, + idle: { label: "Idle", tone: "var(--fg-muted)", breathe: false }, +}; + +function InspectorStatusPill({ session }: { session: WorkspaceSession }) { + const key = session.status === "idle" ? "idle" : workerDisplayStatus(session); + const { label, tone, breathe } = STATUS_PILL[key]; + return ( + + + {label} + + ); +} + +function ChangesView({ session }: { session: WorkspaceSession }) { + const files = session.changedFiles ?? []; + + return ( +
+
+ Changed + {files.length} +
+ +
+ + + +
+ +
+ {files.length === 0 ? ( +

No changes yet.

+ ) : ( + files.map((file) => ( +
+ {file.path} + +{file.additions} + −{file.deletions} +
+ )) + )} +
+ +
+ +