From 7e0fee6c8cd037ab262460f60401043f94d6067e Mon Sep 17 00:00:00 2001 From: jaylfc Date: Wed, 15 Jul 2026 16:35:26 +0100 Subject: [PATCH 01/12] feat(projects): doc-review stamp store + routes (#1802 slice 3) (#1835) * fix(projects): show consent-flow external agents in the External section (#1784) Approved external CLI agents (grok, kilo) were appearing in the plain Members list instead of under "External / Connected agents" next to the other connected agents. The Members panel only classified a member as external when its member_id matched a registry agent's handle, but the consent flow registers these agents with an empty handle and adds the project member row keyed by the canonical id. So the match never fired and they fell through to the main list. Match external registry agents by canonical id as well as handle (older identities like the assistant reference by handle, consent-flow agents by canonical id), and map the "grok" framework, not only "grok-build", to the Grok label so the badge reads correctly. * test(secrets): add coverage for the Secrets app (#1785) Covers the mount-time /api/secrets fetch and loading state, masked value rendering, the empty and failed-fetch fallbacks, reveal and hide via the per-secret API, add and delete through the dialog, and category filtering. The GitHub integration is mocked so its on-mount identity fetch does not interfere with the secrets assertions. * test(notes): add vitest coverage for NotesApp/TodoApp mounted behavior (#1787) Render NotesApp and TodoApp, mock the /api/notes fetch on mount, and assert real behavior: kind filtering, empty states, detail load on select, and the create flow. * test(chess): add vitest coverage for ChessApp (#1788) Cover render, legal moves, turn changes, checkmate status, new game reset, and vs-agent mode, with the on-mount agents fetch mocked. * test(imageviewer): add vitest coverage for ImageViewerApp (#1789) Render the app and assert real behavior: empty state, file load, zoom in/out with min/max clamps, 90-degree rotation, reset on new image, and object URL revocation. Stub fetch and URL.createObjectURL so on-mount integrations do not interfere. * fix(desktop): Registry poll no longer resets scroll (#1761) (#1786) * fix(desktop): keep Registry panel scroll stable across 5s polls (#1761) Quiet background polls no longer flip loading (which unmounted the list) and setEntries is a no-op when id/content are unchanged, so scroll and in-progress interaction are preserved. Add registryEntriesEqual helper and vitest coverage for the poll no-op path. * fix(desktop): guard registryEntriesEqual index access and drop em dashes The poll no-op comparison read a[i]/b[i] without a guard, which fails the strict noUncheckedIndexedAccess build (spa-build). Add an explicit undefined guard, and remove the em dashes from the added comments. * chore(deps): bump the python-deps group with 3 updates (#1790) Updates the requirements on [uvicorn[standard]](https://github.com/Kludex/uvicorn), [croniter](https://github.com/pallets-eco/croniter) and [litellm[proxy]](https://github.com/BerriAI/litellm) to permit the latest version. Updates `uvicorn[standard]` to 0.51.0 - [Release notes](https://github.com/Kludex/uvicorn/releases) - [Changelog](https://github.com/Kludex/uvicorn/blob/main/docs/release-notes.md) - [Commits](https://github.com/Kludex/uvicorn/compare/0.50.0...0.51.0) Updates `croniter` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/pallets-eco/croniter/releases) - [Changelog](https://github.com/pallets-eco/croniter/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pallets-eco/croniter/compare/6.2.3...6.2.4) Updates `litellm[proxy]` to 1.92.0 - [Release notes](https://github.com/BerriAI/litellm/releases) - [Commits](https://github.com/BerriAI/litellm/commits) --- updated-dependencies: - dependency-name: uvicorn[standard] dependency-version: 0.51.0 dependency-type: direct:production dependency-group: python-deps - dependency-name: croniter dependency-version: 6.2.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-deps - dependency-name: litellm[proxy] dependency-version: 1.92.0 dependency-type: direct:production dependency-group: python-deps ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the spa-deps group in /desktop with 16 updates (#1791) --- updated-dependencies: - dependency-name: "@codemirror/state" dependency-version: 6.7.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@codemirror/view" dependency-version: 6.43.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-dialog" dependency-version: 1.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.20 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-select" dependency-version: 2.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-switch" dependency-version: 1.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.17 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@radix-ui/react-tooltip" dependency-version: 1.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@tiptap/extension-link" dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@tiptap/extension-underline" dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@tiptap/pm" dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@tiptap/react" dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@tiptap/starter-kit" dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: "@types/three" dependency-version: 0.185.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: vite dependency-version: 8.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: spa-deps - dependency-name: vitest dependency-version: 4.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: spa-deps ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs(design): account model, free username plus paid chosen subdomains (#1792) * docs(design): Hailo-10H LLM backend, zero-touch install parity with RK3588 (#1793) * docs(design): hub.taos.my local-first P2P social network foundation (#1794) * feat(hailo): reserve port 7836 and map hailo-ollama llm-chat capability (#1795) Slice 1 of the Hailo-10H LLM backend design (docs/design/hailo-llm-backend.md): add 7836 to RESERVED_PORTS so apps cannot squat the NPU backend port, and register hailo-ollama with llm-chat in BACKEND_CAPABILITIES. Closes the first shippable piece of #1771. * feat(account): controller proxy actions for subdomain check/claim/release (slice 3) (#1796) * feat(account): proxy subdomain check/claim/release actions (slice 3) Add /api/account/subdomains/{check,claim,release} routes to account_proxy.py that forward to the taos.my subdomain claims service with the session cookie passthrough. The name field is validated rid-style before it can reach the upstream URL, so a crafted name cannot inject path/query (SSRF/path-traversal guard). Implements account design doc slice 3. * test(account): cover subdomain proxy forwarding, 503, and name validation Add tests alongside the existing account_proxy suite: forwarding of check (query name) and claim/release (body name) with cookie passthrough, 503 when the account service is unconfigured, and 400 on an invalid name with no upstream call. * docs(design): Projects app nested elements, one project with typed elements (#1797) * fix(models): VRAM reservation TTL sweep + #1766 acceptance coverage (#1798) Completes the remaining #1766 work after the #1767 hotfix (fail-open on no-probe hardware, backend-level min_ram_mb gate, asyncio.to_thread probe). - Reclaim VramReservation entries older than a configurable TTL (default 1h) so a hung installer cannot hold capacity until controller restart. Sweep runs from reserve(), available_vram(), stats(), and public sweep_stale(). - Extract _estimated_vram_mb() so the rkllama pull gate clearly reads requires.backends[].min_ram_mb (max across backends, variant fallback). - Tests: no-probe + real backend min_ram proceeds (no 503), concurrent large reserves on NVIDIA still atomic, TTL reclaim, and route-level 503 when free VRAM is measurable and insufficient. Closes #1766 * feat(account): frontend types + Account panel split for username/subdomains (slice 4) (#1801) Implements slice 4 of docs/design/account-username-subdomain-model.md. - account-client: add SubdomainClaim/SubdomainCheck types, Account.username and Account.subdomains, deprecate Account.handle; add checkSubdomain, claimSubdomain, releaseSubdomain helpers with the same degrade-to-state (AuthError, never throw) error style as the auth actions. - AccountPanel: split the old ReserveHandleCard into a free UsernameCard (no taOSgo mention, no .taos.my suffix) and a SubdomainsCard (claim list with active/grace badges, inline availability check, release, disabled claim UI when unsubscribed). Update the section intro copy. - Tests: account-client subdomain helper coverage (mocked website endpoints); AccountPanel coverage for free-username copy, claim list rendering, disabled claim when unsubscribed, and grace badge. * feat(hailo): slice 2 hailo-ollama installer (#1771) (#1803) * feat(hailo): slice 2 hailo-ollama installer (#1771) Implements docs/design/hailo-llm-backend.md slice S2 (section C): scripts/install-hailo.sh mirrors scripts/install-rknpu.sh structure and safety contract. Detects Hailo-10H via /dev/hailo0 + lspci/hailortcli, installs HailoRT (>= 5.1.0 firmware floor) on Raspberry Pi OS, clones hailo-ollama at a pinned ref remapped to port 7836, installs a systemd unit with orphan-reap ExecStartPre, and health-waits on /api/tags. Verification: bash -n, shellcheck, and a non-Hailo host prints the no-detection notice and exits 0 without touching the system. * chore(hailo): doc-gate trailer for install-hailo.sh The installer is specified line by line in docs/design/hailo-llm-backend.md section C (slice S2), which is already merged on dev. Docs-Reviewed: implements the merged design doc docs/design/hailo-llm-backend.md section C, no separate doc change needed * feat(hailo): slice 3 hailo-ollama managed service manifest (#1804) * feat(hailo): slice 3 hailo-ollama managed service manifest Add app-catalog/services/hailo-ollama/manifest.yaml per the managed-backend contract (lifecycle.auto_manage, unit, scope=system, health on 7836). The backend flows through load_managed_backends() with no new plumbing. Adds a unit test that loads the real manifest and asserts the backend is returned by load_managed_backends(). Docs-Reviewed: implements the merged design doc docs/design/hailo-llm-backend.md * fix(hailo): declare the proprietary license in the hailo-ollama manifest test_services_manifests requires a license key on every service manifest. The runtime is Hailo proprietary software behind an install-time EULA acceptance, so the manifest now says exactly that. Docs-Reviewed: license posture is specified in docs/design/hailo-llm-backend.md security and licensing section * feat(account): onboarding free username claim step (slice 5) (#1805) Adds a free taOS username claim step to OnboardingScreen, shown right after local account creation. Clearly labeled free, never gated behind taOSgo, and the user can always finish (claim is optional and failures degrade to a Settings pointer). Public subdomain publishing is deferred to Settings so onboarding never dead-ends on the paid path. Bounded to OnboardingScreen.tsx plus its test, per the slice plan. * feat(hub): identity keypair keystore + directory registration proxy (slice 1) (#1806) Implements slice 1 of the hub.taos.my own-your-posts social network design (docs/design/hub-social-network-foundation.md). Controller side, the taos.my directory endpoints are the contract (mocked in tests): - tinyagentos/hub/identity.py: node keystore that mints an Ed25519 signing key and an X25519 encryption key on first use, persists them 0600 under /hub/identity.json (mesh_credentials.py pattern: atomic write, allowlisted fields, TAOS_DATA_DIR override), and exposes the registerable public view, the SHA-256 author fingerprint, and a challenge-proof signer plus verifier. - account_proxy.py: _ACTIONS additions and same-origin routes for hub identity register / lookup / rotate, forwarding to /api/hub/identity/* with session cookie pass-through; lookup validates the username as an rid-style token before it can reach the upstream URL. Tests: keystore round-trip + 0600 perms + stable fingerprint, proxy forwarding + 503 (unconfigured and unreachable), challenge proof rejects a wrong key, lookup relays the append-only key log. * feat(hailo): slice 4 install-time gates for Hailo-10H (per design doc) (#1807) Mirror the Rockchip RKNPU install-time gates for the Hailo-10H NPU across install.sh, scripts/install-server.sh and scripts/install-worker.sh: detect /dev/hailo0 with 10H vs 8L discrimination (lspci/hailortcli, with TAOS_FORCE_HAILO override), chain into scripts/install-hailo.sh under TAOS_HAILO_SETUP=1, fail-soft on chain failure, and document the new env vars in each script header. Part of #1771. Docs-Reviewed: implements the merged design doc (doc-gate requires it) * feat(hailo): slice 5 runtime detection + provider adapter for Hailo-10H (per design doc) (#1808) Add the hailo-ollama backend end to end with no new hardware needed for CI: - worker probe candidate on the taOS remap port 7836, Ollama-compatible - OllamaCompatAdapter entry for hailo-ollama - litellm_config ollama-compat membership extended to hailo-ollama - provider type registered so auto_register_from_manifest seeds local-hailo-ollama on Hailo-10H hardware (mirrors rkllama local-rkllama) - tests for detection, LiteLLM ollama/ prefix, and seed * feat(hub): profile object + local hub store (slice 2) (#1809) * feat(hub): profile object + local hub store (slice 2) Implements slice 2 of the hub.taos.my own-your-posts social network design (docs/design/hub-social-network-foundation.md). - tinyagentos/hub/store.py: canonical-JSON encode/hash/sign/verify helpers and a SQLite HubStore (objects, blobs, authors tables). Objects are canonical-JSON encoded, content-addressed by SHA-256 of the canonical bytes excluding the signature, and signed by the slice-1 Ed25519 keystore. Profiles are the one mutable object with highest-version-wins semantics; put_profile ignores a stale (lower-or-equal version) replica so it can never clobber a newer one. - tinyagentos/routes/hub.py: local API the Hub app consumes. Render the node's own profile and create/update it with a version bump, each response carrying an explicit degrade state (no-identity / no-profile / ok). The store is opened lazily and colocated with the identity keystore under the data dir. Wired into routes/__init__.py. No peer networking; directory calls stay in account_proxy. Tests: canonicalization vectors, sign/verify (good sig verifies, tamper and wrong key do not), version-wins, object/blob/author store round-trip, and the profile routes end to end (degrade states, create + version bump, kind validation, signature check). * chore(hub): doc-gate trailer for the hub store slice The profile object and local hub store are specified in docs/design/hub-social-network-foundation.md (slice 2), merged on dev. Docs-Reviewed: implements the merged design doc docs/design/hub-social-network-foundation.md slice 2, no separate doc change needed * feat(hub): follow / friend / circle model + request brokering (slice 3) (#1810) Implement hub social slice 3 from docs/design/hub-social-network-foundation.md: - signed follow and cache-grant statements (cache-grant stored, not yet acted on; the cache worker lands in slice 6), - friend-request send/accept/decline flows that broker through the directory and record the local accepted edge, - local block (severs every edge to the peer and asks the hub to revoke the server-side edge) and mute operations, - a presence gate that denies lookup without an accepted edge, - directory proxy entries for requests, presence, and edge revoke. Tests cover edge authorization (presence denied without an accepted edge), rate-limit behavior, and block severing the edge. Docs-Reviewed: implements the merged design doc (doc-gate requires it) * feat(projects): nested element store, CRUD routes, and task element tags (slice 1) (#1811) * feat(projects): implement nested element store, CRUD routes, and task element tags (slice 1) Adds project elements (one level of nesting per the design) with a dedicated store and owner-gated CRUD routes, an element_id tag on tasks with create and update validation plus list and ready filtering, and the Beads snapshot carrying the tag. Group/promote and assignment are later slices. Docs-Reviewed: implements the merged design doc docs/design/projects-nested-elements.md slice 1. * test(projects): slice 1 element store, CRUD route, and task tag coverage Adds the element store unit tests and route-level coverage for element CRUD, tag validation, the 409 delete guard, untag mode, and element_id filtering on list/ready. Proves an external agent token filters by element with no auth change. Docs-Reviewed: implements the merged design doc docs/design/projects-nested-elements.md slice 1. * feat(projects): kanban element filter bar (slice 2) (#1812) Add a persistent element axis to the kanban board: element client API and element_id on task types, a pure element filter in boardFiltering, a new ElementFilterBar rendered from the toolbar (All | element chips | Project- level), an element badge on cards when the board is unfiltered, and element fetching wired through useBoardData. Zero-element projects stay untouched (the bar does not render). Tests added and existing board tests updated. Docs-Reviewed: implements the merged design doc (doc-gate requires it for scripts/, app-catalog/, tinyagentos/ changes) * feat(hub): post objects, chain logic, image ingest, composer + own-timeline (slice 4) (#1813) Implements slice 4 of docs/design/hub-social-network-foundation.md: a per-author hash chain (seq/prev), signed append plus verify and tamper detection, signed tombstones that drop content while keeping the chain verifiable, and image ingest that re-encodes and strips EXIF. Adds the local post, timeline, and delete routes plus the Hub app: a composer with a loud friends-only-by-default visibility switch and an own-timeline read from the local store. No peer sync yet (that is slice 5). Tests cover chain append/verify, tamper detection, tombstone drops content and keeps the chain verifiable, and EXIF stripped. Docs-Reviewed: implements the merged design doc (doc-gate requires it for scripts/, app-catalog/, tinyagentos/ changes) * fix: map violet and red note colors to valid tldraw palette names (#1815) Canvas notes with payload.color violet or red fell back to yellow because the tldraw COLOR_MAP in NoteShape.tsx was missing those entries. Added violet, red, and light-blue mappings so the note shape renders with the correct background color. Added tests to verify the color strings survive element-to-shape coercion and that COLOR_MAP has the expected entries. * docs(readme): External Coding Agents section (bring your own AI team) (#1816) * docs(readme): External Coding Agents section (registry, consent onboarding, kanban + a2a work loop) The external-agent collaboration flow (access requests approved from the phone, scoped registry identities, board claim/PR/close loop, a2a coordination) had no README presence despite being live and proven. Docs-Reviewed: readme-only change describing the shipped flow documented in docs/design/external-agent-onboarding.md * docs(readme): reference only docs that exist on dev doc-gate verifies every mentioned path exists; the project-invite design doc lives on a branch, so the section links only the onboarding doc. Docs-Reviewed: readme-only change describing the shipped flow documented in docs/design/external-agent-onboarding.md * fix(hub): serialize chain appends so racing posts are not orphaned (#1817) next_chain_position read the chain head and put_chain_object inserted with INSERT OR IGNORE, so two concurrent appends for the same author computed the same seq and the loser was silently dropped from the chain index while its body stayed in hub_objects. A per-store asyncio lock now serializes the read-position-then-insert section in append_post and delete_post; the local node is the only writer of its own chain, so this closes the race. Regression test proves three concurrent appends land as seq 1,2,3. Docs-Reviewed: hardening of the merged design doc docs/design/hub-social-network-foundation.md slice 4, no doc change needed * fix(canvas): map text elements to visible taos-text shapes (#1819) * fix(canvas): map text elements to visible taos-text shapes element-to-shape.ts only mapped note/link/image to custom shape types; text (and mermaid label) fell through to taos-generic whose props only carry geometry, so the payload never reached a visible label. Add a taos-text shape util and map kind=text to it, coercing payload.text to a string with empty-string default so imperfect agent writes still render. Add tests for text kind mapping, payload coercion, and fallback behavior. * chore(canvas): doc-gate trailer for text shape New desktop source (TextShape.tsx) rendering the canvas text kind; no behavioral doc needed, the canvas element kinds are covered by the design. Docs-Reviewed: frontend-only canvas fix for the boarded canvas text render bug, no doc change needed * feat(projects): element overview grid, creation flow, and drill-in navigation (slice 3) (#1820) * feat(projects): element overview grid, creation flow, and drill-in navigation (slice 3) Implements slice 3 of docs/design/projects-nested-elements.md on the frontend: - New elements/ registry (types.ts) with the seven known types, their icons, and type-driven landing-tab order. - ElementGrid: overview grid shown when a project has elements, with a fixed Project card and an Add element tile; zero-element projects keep today's workspace pane untouched (back-compat invariant). - ElementCard: type icon, name, type label, open/total task counts, owner chip, and a recent-activity line. - ElementCreateDialog: create a single nested element (name, slug, type, optional owner) within an existing project. - CreateProjectDialog: optional second step to seed nested elements; skipping yields a project identical to today's. - ProjectWorkspace: element drill-in scopes the board to the element and lands on the type's preferred tab, with a breadcrumb and the element id carried on the URL for deep links. - ProjectBoard/BoardToolbar: accept a scoped element id and hide the element filter bar while scoped. Tests cover the grid, card, create dialog, the zero-element regression, drill-in with breadcrumb, and the creation-flow step two. * chore(projects): doc-gate trailer for elements slice 3 UI New desktop sources under ProjectsApp/elements/ implementing slice 3 of the merged nested-elements design; frontend-only, no behavioral doc change. Docs-Reviewed: implements the merged design doc docs/design/projects-nested-elements.md slice 3 * Add confirm guard before video delete (#1821) Docs-Reviewed: frontend change to the shipped Video Studio surface. * Projects elements slice 4: element-scoped canvas + files (#1822) Add an element_id tag to canvas items (store column + ALTER migration, element-filtered list and create on the canvas routes), an adopt-existing element files subfolder helper, untag-on-delete for canvas items, and wire the frontend so the canvas honors the active element filter and the Files tab mounts the element subfolder. Docs-Reviewed: implements the boarded task, frontend/backend change to shipped surface. * feat(projects): doc-review stamp store + routes (#1802 slice 3) Add per-document review_state machine (awaiting_review/approved/changes_requested) with actor recording and timestamps, project-scoped agent-token gated routes, and the desktop stamp badge column plus typed API client. Docs-Reviewed: doc-review stamp store + routes slice per document-review-surface.md * feat(projects): add doc-review stamp badge to FilesApp and fix projects.ts types - Add ReviewBadge component with onClick support for cycling review state - Add cycleDocReview callback for in-place state transitions - Wire badge into FileRow (list view) and grid cards (project: locations only) - Fetch review states on project-location navigation via projectsApi.docReviews.list - Remove duplicate DocReviewState/DocReview type definitions from projects.ts - Remove duplicate docReview API block; keep single canonical docReviews namespace - Use DocReview | DocReviewMissing union for GET response type - Apply encodeURIComponent to state filter query parameter Docs-Reviewed: doc-review stamp store + routes slice per document-review-surface.md --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- README.md | 11 + .../services/hailo-ollama/manifest.yaml | 29 + desktop/package-lock.json | 965 +++++++++++------- desktop/package.json | 30 +- desktop/src/apps/ChessApp.test.tsx | 176 ++++ desktop/src/apps/FilesApp.tsx | 130 ++- desktop/src/apps/HubApp/HubApp.test.tsx | 146 +++ desktop/src/apps/HubApp/HubApp.tsx | 378 +++++++ desktop/src/apps/ImageViewerApp.test.tsx | 199 ++++ desktop/src/apps/NotesApp.test.tsx | 204 ++++ .../apps/ProjectsApp/CreateProjectDialog.tsx | 243 ++++- .../apps/ProjectsApp/ProjectMembers.test.tsx | 76 ++ .../src/apps/ProjectsApp/ProjectMembers.tsx | 15 +- .../src/apps/ProjectsApp/ProjectWorkspace.tsx | 167 ++- .../apps/ProjectsApp/ProjectsApp.module.css | 29 + .../CreateProjectDialog.elements.test.tsx | 80 ++ .../ProjectWorkspace.elements.test.tsx | 144 +++ .../ProjectsApp/__tests__/canvas-api.test.ts | 34 + .../__tests__/element-to-shape.test.ts | 37 + .../apps/ProjectsApp/board/BoardToolbar.tsx | 30 + .../board/ElementFilterBar.module.css | 28 + .../ProjectsApp/board/ElementFilterBar.tsx | 45 + .../apps/ProjectsApp/board/ProjectBoard.tsx | 27 +- .../ProjectsApp/board/TaskCard.module.css | 1 + .../src/apps/ProjectsApp/board/TaskCard.tsx | 4 +- .../board/__tests__/BoardToolbar.test.tsx | 6 + .../board/__tests__/ElementFilterBar.test.tsx | 63 ++ .../board/__tests__/ProjectBoard.test.tsx | 1 + .../board/__tests__/TaskCard.test.tsx | 1 + .../board/__tests__/TaskModal.test.tsx | 1 + .../board/__tests__/boardDnd.test.ts | 1 + .../board/__tests__/boardFiltering.test.ts | 28 + .../board/__tests__/boardGrouping.test.ts | 1 + .../board/__tests__/useBoardData.test.tsx | 14 +- .../apps/ProjectsApp/board/boardFiltering.ts | 8 + desktop/src/apps/ProjectsApp/board/types.ts | 9 +- .../apps/ProjectsApp/board/useBoardData.ts | 8 +- .../apps/ProjectsApp/canvas/CanvasBoard.tsx | 39 +- .../apps/ProjectsApp/canvas/CanvasView.tsx | 6 +- .../src/apps/ProjectsApp/canvas/canvas-api.ts | 7 +- .../ProjectsApp/canvas/element-to-shape.ts | 14 +- .../ProjectsApp/canvas/shapes/NoteShape.tsx | 2 + .../ProjectsApp/canvas/shapes/TextShape.tsx | 68 ++ .../apps/ProjectsApp/elements/ElementCard.tsx | 43 + .../elements/ElementCreateDialog.tsx | 152 +++ .../apps/ProjectsApp/elements/ElementGrid.tsx | 75 ++ .../ProjectsApp/elements/Elements.module.css | 117 +++ .../elements/__tests__/ElementCard.test.tsx | 47 + .../__tests__/ElementCreateDialog.test.tsx | 87 ++ .../elements/__tests__/ElementGrid.test.tsx | 110 ++ .../src/apps/ProjectsApp/elements/types.ts | 83 ++ desktop/src/apps/SecretsApp.test.tsx | 227 ++++ .../apps/SettingsApp/AccountPanel.test.tsx | 121 ++- desktop/src/apps/SettingsApp/AccountPanel.tsx | 197 +++- desktop/src/apps/VideoStudioApp.test.tsx | 85 ++ desktop/src/apps/VideoStudioApp.tsx | 3 +- .../src/apps/agents/RegistryPanel.test.tsx | 76 +- desktop/src/apps/agents/RegistryPanel.tsx | 73 +- .../src/components/OnboardingScreen.test.tsx | 121 +++ desktop/src/components/OnboardingScreen.tsx | 170 ++- desktop/src/lib/account-client.test.ts | 120 ++- desktop/src/lib/account-client.ts | 124 ++- desktop/src/lib/projects.ts | 125 +++ desktop/src/registry/app-registry.ts | 1 + .../account-username-subdomain-model.md | 310 ++++++ docs/design/hailo-llm-backend.md | 417 ++++++++ docs/design/hub-social-network-foundation.md | 602 +++++++++++ docs/design/projects-nested-elements.md | 650 ++++++++++++ install.sh | 30 + pyproject.toml | 6 +- scripts/install-hailo.sh | 479 +++++++++ scripts/install-server.sh | 81 ++ scripts/install-worker.sh | 56 + tests/conftest.py | 10 + tests/installers/test_port_allocator.py | 12 + tests/projects/test_canvas_store.py | 85 ++ tests/projects/test_element_store.py | 169 +++ tests/test_backend_catalog_lifecycle.py | 12 +- tests/test_backend_services.py | 15 + tests/test_config.py | 58 ++ tests/test_doc_review.py | 358 +++++++ tests/test_hub_identity.py | 118 +++ tests/test_hub_posts.py | 234 +++++ tests/test_hub_relationships.py | 168 +++ tests/test_hub_store.py | 187 ++++ tests/test_litellm_config.py | 31 + tests/test_project_folders.py | 34 +- tests/test_routes_account_proxy.py | 322 ++++++ tests/test_routes_account_proxy_hub_slice3.py | 276 +++++ tests/test_routes_hub.py | 91 ++ tests/test_routes_hub_slice3.py | 277 +++++ tests/test_routes_hub_slice4.py | 108 ++ tests/test_routes_models.py | 101 +- tests/test_routes_project_canvas.py | 68 ++ tests/test_routes_projects.py | 238 +++++ tests/test_routes_projects_agent_tasks.py | 62 ++ tests/test_vram_reservation.py | 150 ++- tests/test_worker.py | 30 + tinyagentos/app.py | 12 + tinyagentos/auth_middleware.py | 21 + tinyagentos/backend_adapters.py | 3 +- tinyagentos/hub/__init__.py | 10 + tinyagentos/hub/identity.py | 233 +++++ tinyagentos/hub/posts.py | 275 +++++ tinyagentos/hub/relationships.py | 106 ++ tinyagentos/hub/store.py | 537 ++++++++++ tinyagentos/installers/port_allocator.py | 1 + tinyagentos/litellm_config.py | 4 +- tinyagentos/projects/beads_format.py | 1 + tinyagentos/projects/canvas/store.py | 56 +- tinyagentos/projects/doc_review_store.py | 149 +++ tinyagentos/projects/element_store.py | 246 +++++ tinyagentos/projects/folders.py | 19 + tinyagentos/projects/ids.py | 2 +- tinyagentos/projects/task_store.py | 76 +- tinyagentos/providers/__init__.py | 3 + tinyagentos/routes/__init__.py | 10 + tinyagentos/routes/account_proxy.py | 160 ++- tinyagentos/routes/hub.py | 606 +++++++++++ tinyagentos/routes/models.py | 30 +- tinyagentos/routes/project_canvas.py | 9 +- tinyagentos/routes/project_doc_review.py | 112 ++ tinyagentos/routes/projects.py | 264 ++++- tinyagentos/scheduler/backend_catalog.py | 1 + tinyagentos/vram_reservation.py | 74 +- tinyagentos/worker/agent.py | 5 +- uv.lock | 42 +- 127 files changed, 13964 insertions(+), 610 deletions(-) create mode 100644 app-catalog/services/hailo-ollama/manifest.yaml create mode 100644 desktop/src/apps/ChessApp.test.tsx create mode 100644 desktop/src/apps/HubApp/HubApp.test.tsx create mode 100644 desktop/src/apps/HubApp/HubApp.tsx create mode 100644 desktop/src/apps/ImageViewerApp.test.tsx create mode 100644 desktop/src/apps/NotesApp.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/ProjectMembers.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/__tests__/CreateProjectDialog.elements.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/__tests__/ProjectWorkspace.elements.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/board/ElementFilterBar.module.css create mode 100644 desktop/src/apps/ProjectsApp/board/ElementFilterBar.tsx create mode 100644 desktop/src/apps/ProjectsApp/board/__tests__/ElementFilterBar.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/canvas/shapes/TextShape.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/ElementCard.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/ElementCreateDialog.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/ElementGrid.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/Elements.module.css create mode 100644 desktop/src/apps/ProjectsApp/elements/__tests__/ElementCard.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/__tests__/ElementCreateDialog.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/__tests__/ElementGrid.test.tsx create mode 100644 desktop/src/apps/ProjectsApp/elements/types.ts create mode 100644 desktop/src/apps/SecretsApp.test.tsx create mode 100644 desktop/src/components/OnboardingScreen.test.tsx create mode 100644 docs/design/account-username-subdomain-model.md create mode 100644 docs/design/hailo-llm-backend.md create mode 100644 docs/design/hub-social-network-foundation.md create mode 100644 docs/design/projects-nested-elements.md create mode 100755 scripts/install-hailo.sh create mode 100644 tests/projects/test_element_store.py create mode 100644 tests/test_doc_review.py create mode 100644 tests/test_hub_identity.py create mode 100644 tests/test_hub_posts.py create mode 100644 tests/test_hub_relationships.py create mode 100644 tests/test_hub_store.py create mode 100644 tests/test_routes_account_proxy_hub_slice3.py create mode 100644 tests/test_routes_hub.py create mode 100644 tests/test_routes_hub_slice3.py create mode 100644 tests/test_routes_hub_slice4.py create mode 100644 tinyagentos/hub/__init__.py create mode 100644 tinyagentos/hub/identity.py create mode 100644 tinyagentos/hub/posts.py create mode 100644 tinyagentos/hub/relationships.py create mode 100644 tinyagentos/hub/store.py create mode 100644 tinyagentos/projects/doc_review_store.py create mode 100644 tinyagentos/projects/element_store.py create mode 100644 tinyagentos/routes/hub.py create mode 100644 tinyagentos/routes/project_doc_review.py diff --git a/README.md b/README.md index 225ebee0a..2cc104962 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,17 @@ Create shared file spaces for agents, groups, and departments. The design team s - **Memory Browser.** Keyword + semantic vector search across all agents - **Agent Export/Import.** Portable JSON export of agent config, channels, and group memberships +### External Coding Agents (Bring Your Own AI Team) +Connect any CLI coding agent (Claude Code, grok, kilo, opencode, aider, or your own) to a taOS project as a real team member. No shared passwords, no glue scripts: agents join through a consent flow and work through the same board and messaging surfaces your other agents use. + +- **Access requests + phone approval.** An agent (or its harness) fires `POST /api/agents/auth-requests` with an identity claim and the scopes it wants. You approve or deny from the taOS notification on your phone, pick which project it joins, and taOS mints it a scoped identity (Ed25519 registry JWT, canonical id). Narrow-not-widen: you can grant less than requested, never more. +- **Agent registry.** Every connected agent is a first-class registry identity with its own token, scopes, and project membership. Revoke or re-scope at any time from the Agents app. +- **Kanban work loop.** Bounded tasks live on the project board; a connected agent claims a task with its token, works in its own branch, opens a PR, comments the PR link back on the task, and closes it. Your reviewer (human or a stronger agent) gates every merge. +- **A2A coordination bus.** Agents coordinate on shared channels (`general`, `decisions`, project channels) so a fleet of different models and harnesses can hand work to each other without stepping on toes. +- **Proven in production.** taOS itself is developed this way: a fleet of consent-minted CLI agents on free models works the taOS board daily, with every PR reviewed before merge. + +More on the flow in `docs/design/external-agent-onboarding.md`. + ### Authentication Password-protected dashboard with persistent sessions. Per-agent API keys. Exempt paths for cluster workers and health checks. diff --git a/app-catalog/services/hailo-ollama/manifest.yaml b/app-catalog/services/hailo-ollama/manifest.yaml new file mode 100644 index 000000000..9d91186e4 --- /dev/null +++ b/app-catalog/services/hailo-ollama/manifest.yaml @@ -0,0 +1,29 @@ +id: hailo-ollama +# The hailo-ollama runtime is Hailo proprietary software; the install flow +# presents Hailo's EULA for acceptance before download (see the design doc). +license: Proprietary (Hailo EULA, accepted at install) +name: hailo-ollama (Hailo-10H NPU LLM) +type: service +category: llm-runtime +description: "Ollama-compatible LLM server on the Hailo-10H NPU (Raspberry Pi 5 + AI HAT+2)" +requires: + ram_mb: 1024 + disk_mb: 500 + ports: [7836] +install: + method: script + script: scripts/install-hailo.sh +hardware_tiers: + arm-npu-8gb: full + arm-npu-16gb: full + cpu-only: unsupported +lifecycle: + backend_type: hailo-ollama + default_url: http://localhost:7836 + auto_manage: true + unit: hailo-ollama.service + scope: system + health: + url: "http://localhost:7836/api/tags" + expect: '"models"' + startup_timeout_seconds: 120 diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 9e054e6bb..6b2072f97 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -10,9 +10,9 @@ "dependencies": { "@codemirror/lang-markdown": "^6.5.0", "@codemirror/language-data": "^6.5.2", - "@codemirror/state": "^6.7.0", + "@codemirror/state": "^6.7.1", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.43.5", + "@codemirror/view": "^6.43.6", "@excalidraw/excalidraw": "^0.18.1", "@excalidraw/mermaid-to-excalidraw": "^2.2.2", "@fortune-sheet/core": "^1.0.4", @@ -20,19 +20,19 @@ "@milkdown/kit": "^7.21.2", "@milkdown/react": "^7.21.2", "@milkdown/theme-nord": "^7.21.2", - "@radix-ui/react-dialog": "^1.1.18", - "@radix-ui/react-dropdown-menu": "^2.1.19", + "@radix-ui/react-dialog": "^1.1.19", + "@radix-ui/react-dropdown-menu": "^2.1.20", "@radix-ui/react-label": "^2.1.11", - "@radix-ui/react-select": "^2.3.2", + "@radix-ui/react-select": "^2.3.3", "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-switch": "^1.3.2", - "@radix-ui/react-tabs": "^1.1.16", - "@radix-ui/react-tooltip": "^1.2.11", - "@tiptap/extension-link": "^3.27.1", - "@tiptap/extension-underline": "^3.27.1", - "@tiptap/pm": "^3.27.1", + "@radix-ui/react-switch": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.17", + "@radix-ui/react-tooltip": "^1.2.12", + "@tiptap/extension-link": "^3.27.3", + "@tiptap/extension-underline": "^3.27.3", + "@tiptap/pm": "^3.27.3", "@tiptap/react": "^3.26.1", - "@tiptap/starter-kit": "^3.27.1", + "@tiptap/starter-kit": "^3.27.3", "@tldraw/assets": "4.5.12", "@tldraw/tldraw": "^4.5.10", "@tonejs/midi": "^2.0.28", @@ -77,13 +77,13 @@ "@testing-library/react": "^16.3.2", "@types/react": "^19.2.17", "@types/react-dom": "^19.0.0", - "@types/three": "0.185.0", + "@types/three": "0.185.1", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "tailwindcss": "^4.0.0", "typescript": "^6.0.3", - "vite": "^8.1.3", - "vitest": "^4.1.8" + "vite": "^8.1.4", + "vitest": "^4.1.10" } }, "node_modules/@adobe/css-tools": { @@ -664,9 +664,9 @@ } }, "node_modules/@codemirror/state": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.0.tgz", - "integrity": "sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", + "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" @@ -685,9 +685,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.43.5", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.5.tgz", - "integrity": "sha512-7uT/vUgH6dfXWn3WqOe23KneILMvGy5wQjNMEcRXLKzziJ9NOktpW6tGoyQpwVkBgE5Gj6hKkCcsddbnkaWrOQ==", + "version": "6.43.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.6.tgz", + "integrity": "sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.7.0", @@ -2895,20 +2895,20 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.18.tgz", - "integrity": "sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.19.tgz", + "integrity": "sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", @@ -2930,13 +2930,34 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-dialog/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.14.tgz", - "integrity": "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.15.tgz", + "integrity": "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -2958,9 +2979,9 @@ } }, "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.11.tgz", - "integrity": "sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.12.tgz", + "integrity": "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", @@ -3006,6 +3027,29 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.7.tgz", + "integrity": "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==", + "license": "MIT", + "dependencies": { + "@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-dialog/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -3072,16 +3116,16 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.19.tgz", - "integrity": "sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.20.tgz", + "integrity": "sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-menu": "2.1.19", + "@radix-ui/react-menu": "2.1.20", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, @@ -3100,6 +3144,12 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-arrow": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.11.tgz", @@ -3124,13 +3174,13 @@ } }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-collection": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.11.tgz", - "integrity": "sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.12.tgz", + "integrity": "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, @@ -3149,13 +3199,28 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-dropdown-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.14.tgz", - "integrity": "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.15.tgz", + "integrity": "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -3177,9 +3242,9 @@ } }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.11.tgz", - "integrity": "sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.12.tgz", + "integrity": "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", @@ -3202,25 +3267,25 @@ } }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-menu": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.19.tgz", - "integrity": "sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.20.tgz", + "integrity": "sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.5", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", + "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", + "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", "aria-hidden": "^1.2.4", @@ -3242,15 +3307,15 @@ } }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-popper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.2.tgz", - "integrity": "sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.3.tgz", + "integrity": "sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", @@ -3297,6 +3362,29 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-presence": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.7.tgz", + "integrity": "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==", + "license": "MIT", + "dependencies": { + "@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-dropdown-menu/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -3321,20 +3409,22 @@ } }, "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.14.tgz", - "integrity": "sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.15.tgz", + "integrity": "sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.5", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -4021,24 +4111,24 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.2.tgz", - "integrity": "sha512-brXD6C/V0fVK0DDbscLVw6LsXrjQ+ay8jdOBaN+tLb4vsHsAMm6Gt6eT77wHX1Eq8GPtD5rJ+RxFtfDozsb4+Q==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.3.tgz", + "integrity": "sha512-L5RQTXz6Anxsf9CCv+pTgiAsUpyVj7rJxsGtmhFaEOJ++cVfXucv4qWfsIO0AIB4NAhi3yovWGVMKKS1Xf1Wrg==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.5", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.11", + "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", + "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -4064,6 +4154,12 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-arrow": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.11.tgz", @@ -4088,13 +4184,13 @@ } }, "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-collection": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.11.tgz", - "integrity": "sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.12.tgz", + "integrity": "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, @@ -4113,13 +4209,28 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-select/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.14.tgz", - "integrity": "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.15.tgz", + "integrity": "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -4141,9 +4252,9 @@ } }, "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.11.tgz", - "integrity": "sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.12.tgz", + "integrity": "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", @@ -4166,15 +4277,15 @@ } }, "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.2.tgz", - "integrity": "sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.3.tgz", + "integrity": "sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", @@ -4221,6 +4332,29 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-presence": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.7.tgz", + "integrity": "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==", + "license": "MIT", + "dependencies": { + "@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/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -4342,14 +4476,14 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.2.tgz", - "integrity": "sha512-tgRBI3DdNwAJYE4BBZyZcz/HRRCvAsPkRvG1wvKc+41tBGMxPn/a87T/wikXAvyDypNQ9kaZwHbeZe+veHCGpA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.3.tgz", + "integrity": "sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", @@ -4370,6 +4504,27 @@ } } }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-switch/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -4394,18 +4549,18 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.16.tgz", - "integrity": "sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.17.tgz", + "integrity": "sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/primitive": "1.1.5", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", - "@radix-ui/react-roving-focus": "1.1.14", + "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { @@ -4423,14 +4578,20 @@ } } }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.11.tgz", - "integrity": "sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.12.tgz", + "integrity": "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, @@ -4449,6 +4610,44 @@ } } }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.7.tgz", + "integrity": "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==", + "license": "MIT", + "dependencies": { + "@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-tabs/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -4473,20 +4672,22 @@ } }, "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.14.tgz", - "integrity": "sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.15.tgz", + "integrity": "sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.11", + "@radix-ui/primitive": "1.1.5", + "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-is-hydrated": "0.1.1", + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -4621,19 +4822,19 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.11.tgz", - "integrity": "sha512-8XZ6Py3y3W2nEzAUGCN5cfVKaUi+CVApcz1d6lrNVVf2hvYEixMRkq8k9ggPKnQUpRRuOV5avt8uvxViH2jLwA==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.12.tgz", + "integrity": "sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.14", + "@radix-ui/react-context": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.2", + "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", - "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", @@ -4654,6 +4855,12 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.5.tgz", + "integrity": "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==", + "license": "MIT" + }, "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.11.tgz", @@ -4677,13 +4884,28 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.0.tgz", + "integrity": "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==", + "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-tooltip/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.14.tgz", - "integrity": "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.15.tgz", + "integrity": "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", + "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", @@ -4705,15 +4927,15 @@ } }, "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.2.tgz", - "integrity": "sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.3.tgz", + "integrity": "sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", @@ -4760,6 +4982,29 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.7.tgz", + "integrity": "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==", + "license": "MIT", + "dependencies": { + "@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-tooltip/node_modules/@radix-ui/react-primitive": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.7.tgz", @@ -5672,48 +5917,48 @@ } }, "node_modules/@tiptap/core": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.27.1.tgz", - "integrity": "sha512-rV6Qn4wmC6BxfF+4mu6bqGWj9vA4oXXhsrpXaJL2uhjxeHAGofjwcHof2X84VYzeyXgdlsGmqKie4TAppVXZUQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.27.3.tgz", + "integrity": "sha512-TJj5929M96C1KlH796wS8MywfHDh49RhmakOyzyMMc9pFmRj9UXi1gj0TCXgsZtjEOG7B+m/DRvNOvnuvR9kmg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/pm": "3.27.1" + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-blockquote": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.27.1.tgz", - "integrity": "sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.27.3.tgz", + "integrity": "sha512-VGoVMcqcGkkoduzEqQ70ZK5OOHBDn5iYeHJSgkvNoSHzYHl0CaKoWxoDKJjSWC5DAM4mBU+tXTlsiUp3evRMfA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-bold": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.27.1.tgz", - "integrity": "sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.27.3.tgz", + "integrity": "sha512-pchbycFppBqBmvk+OxrQLIPZLNd3rNbcm7zOa+OTjluphpAcsJ6AmHBmiRhJUYWitV6/SA+0nujcaWxBp5hNcQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.27.1.tgz", - "integrity": "sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.27.3.tgz", + "integrity": "sha512-PKSM9g8BXzO0Lxm2gOin7FAFvxO6T0QvlXTXVke/GJn/sR1tEz7w5ybsvweytUPmSliUc601pfIUOHKEhIVC2Q==", "license": "MIT", "optional": true, "dependencies": { @@ -5724,80 +5969,80 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.27.1.tgz", - "integrity": "sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.27.3.tgz", + "integrity": "sha512-owQW6mcgnYgQU1z5BqmslcjZFBUm1SGM/Ax++4R6XelIt/7ol6uSTjzjAzum+ASW7UlBbuJHuSgRjl8cA+RGkg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "3.27.1" + "@tiptap/extension-list": "3.27.3" } }, "node_modules/@tiptap/extension-code": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.27.1.tgz", - "integrity": "sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.27.3.tgz", + "integrity": "sha512-T7JF10Y3k7fyVujXXLVubdUh7fctgBUoiuxbvjotpltg1U/mPbOcT0UW/uif9j3H8TFvAwNApVw3x8bwwFUakA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-code-block": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.27.1.tgz", - "integrity": "sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.27.3.tgz", + "integrity": "sha512-3CVnzkpGoqqI5KaXI9l9PMwLkx34SYY63tpeo0I0QjDk4LU+JTAQDTPJFSwB4zTsHZ7ZFMU0jjasFOZ1kZRVgw==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-document": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.27.1.tgz", - "integrity": "sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.27.3.tgz", + "integrity": "sha512-PR25MRv56Nm4ETVLwA8nAh/0RrVdvD4D2nxnAC8kUkxz1WOYDaqTfPqqEeSWDoUUTUp1I1xzDkD4x0gEnAlmZA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.27.1.tgz", - "integrity": "sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.27.3.tgz", + "integrity": "sha512-XsL0Ziual+ynXyx5JgLb0KIOShlMN3MW7+GQvf9PGRb1vOB7IOTRFDxMPrxDWcFIZ6WWMWSrbkCLOKHStt1BWg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extensions": "3.27.1" + "@tiptap/extensions": "3.27.3" } }, "node_modules/@tiptap/extension-floating-menu": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.27.1.tgz", - "integrity": "sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.27.3.tgz", + "integrity": "sha512-qL+g1Z6MqZvYG4mHTPwub9II2fWZY+2I5VFfV8fzqy1HchBOyY6Df0mRKDNb0bmjBdFabIXnswz3iqLx3mDTbw==", "license": "MIT", "optional": true, "funding": { @@ -5806,93 +6051,93 @@ }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.27.1.tgz", - "integrity": "sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.27.3.tgz", + "integrity": "sha512-MVBqzwYrDOttkn2GOrvvKTDCymfSS2VGSKpahcKKhge5mweSDiqko2X1vBDnMF8aVW3WxIFfDMmO4VMmh8HDZA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extensions": "3.27.1" + "@tiptap/extensions": "3.27.3" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.27.1.tgz", - "integrity": "sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.27.3.tgz", + "integrity": "sha512-cySObJITR2CRrWII87mI2LJ12wEETTnxt2p1vvC6ZtUmKnB50fOu+ux8ZEt6KYNocIOtrEZthNFzTOx7R7UtrQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-heading": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.27.1.tgz", - "integrity": "sha512-SrC4l1kEIyv9ZXFaI/8LQqU2MyMmjczw7XXsWUQOTN4YXv0JyVgMNR3cI/wz0d2xsTfBdZ1N85Tdng+Ga1t0Sg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.27.3.tgz", + "integrity": "sha512-QHXnsNic6iId8pnsFZ8z4PkX5L+HCHa/D7rAi3nNWtPlSIAOxo4nKrALcB5/tHmY+XL8kEXKH3nsNLNEDLCYPg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-highlight": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.27.1.tgz", - "integrity": "sha512-IeMIUKbW4oyRkgn92BPYQ0fifkbTwVigKwa107nGDHMokz+pHQPFHy8xG3U7gtUOra/8OUo57bFgNGuP0TaH4A==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.27.3.tgz", + "integrity": "sha512-99QYxMx8F/NhdvznM8/Zhm4XiCdMU0OEiLwPvLNObJiKV8UBqjdA68cJLX6FRuOi2nqD7xfJq2KuSsFKBldi2A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.27.1.tgz", - "integrity": "sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.27.3.tgz", + "integrity": "sha512-sNCuo0+Q001B5XTVWY+ULpWXQqtBNRmAqXxAexZl44bx3rDqHG8OzjwT4EM0LIj2+BYVbdqwaJ1vfQbZVEEbig==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-italic": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.27.1.tgz", - "integrity": "sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.27.3.tgz", + "integrity": "sha512-7VpVi2vn8kGFAc/HLrt96J/E6+LXOGKn8xLhJS863t150yLIG3EPQLA1h+G5O6eo+/w+9YokvJAAJoeDihj1Jg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-link": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.27.1.tgz", - "integrity": "sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.27.3.tgz", + "integrity": "sha512-iXmu1tJ/3vP60c9d+zfO3+X64vod2EyBrM1qeufecAtWA6t0bT9tYeka0Zq6qDRGsZK5zViO9F2lsY7eHXiYuA==", "license": "MIT", "dependencies": { "linkifyjs": "^4.3.3" @@ -5902,148 +6147,148 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-list": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.27.1.tgz", - "integrity": "sha512-c2Upru7lj0/ZV/Ibww6cNz6sUS8m6Dp/9uygFhYcZOd3X8M0xBIEk42c6m6SQehkPziVA8QOgNJz7sMqsbz1OQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.27.3.tgz", + "integrity": "sha512-52rcaSzYjtVGUrNkdB8k1Bw/rId2lBirrWMvmnBLsieoeFd8zo8ow5zkwV057BYSgWXfnMAV7GOpDyq2IoSD9A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/extension-list-item": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.27.1.tgz", - "integrity": "sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.27.3.tgz", + "integrity": "sha512-UddhVaQ7+V9IfQVVE8AJLNzSmyaaNpZPQsoPQxFNlJyOFxD6RwSTT8L4rb/TwbUctxSFWpyd1J7exzf33Ony/g==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "3.27.1" + "@tiptap/extension-list": "3.27.3" } }, "node_modules/@tiptap/extension-list-keymap": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.27.1.tgz", - "integrity": "sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.27.3.tgz", + "integrity": "sha512-GZPVg4QCFm41yTMPEPeTlH2odNeQwRuWHY/pt3pI5LsfipQ4kDBv6h9we928vJQUnU2Gg9Y3cL5iRbmXIWWTUA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "3.27.1" + "@tiptap/extension-list": "3.27.3" } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.27.1.tgz", - "integrity": "sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.27.3.tgz", + "integrity": "sha512-oHYoE65WfRSM5tCcXi8MtQmzPxz83E2qw0pa2yHBKwr9xCCQUu8vJHJE+EJGduWdaNImv43+6/6kVMeYYbXRKg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "3.27.1" + "@tiptap/extension-list": "3.27.3" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.27.1.tgz", - "integrity": "sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.27.3.tgz", + "integrity": "sha512-G5XPCN7lm0nULYPelJC2eUbKbt1q97j67e/HSWxwMYEXhceec5eNAtVfpDCRyXNO9osyBi2kXqLv/IQtxbch9A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-strike": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.27.1.tgz", - "integrity": "sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.27.3.tgz", + "integrity": "sha512-dp6Rs3I4zLVJPvAw2Q9yrNuYKcX5LTONH3/oyULvGsrqq5DKz7pqscfwhwnDSzrgm/3aCz6B9r1BVHlYI/DXlA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-text": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.27.1.tgz", - "integrity": "sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.27.3.tgz", + "integrity": "sha512-wSatZ/bop0pleeC18nF90zvWWEoRe/ewZncvm8LjcMrXwq41kTEs3j7nZflMYAh7X8fZvu00UIMeEyCNJl+5Yg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extension-underline": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.27.1.tgz", - "integrity": "sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.27.3.tgz", + "integrity": "sha512-YH7ka/Rp7ZwlzTuQCcs0OZ4CMSx/P/MzbseQKxKebEIZreWaKMZAaED97Fnu0dA66VlreD4/1C7xHJy84ovamg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1" + "@tiptap/core": "3.27.3" } }, "node_modules/@tiptap/extensions": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.27.1.tgz", - "integrity": "sha512-1Tdx9faw8k0/83V6X+xCDVhV8yElGt95JxeW3YMkKQJI56QdlPz0xOdJPlMiSGJKinPyVier+x9LJD/YZUZIaw==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.27.3.tgz", + "integrity": "sha512-IA2QKUVJgzUPEhhkUfr6P5u5GFVfhiApiEaaHXBz07saL2c4HNoVs2RC18QlZDCtLNKrPR1mUScr72u7uYs+YQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1" + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3" } }, "node_modules/@tiptap/pm": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.27.1.tgz", - "integrity": "sha512-Ffjx+vimmBU7zH/KrpXzJid3+pziCe/VL2aexSTP63cyQwKQ65LkFkCKaIsSpFdQQuakVZBGWjCA5RoBV852pw==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.27.3.tgz", + "integrity": "sha512-ppiG57RxM3HSHHgcHT0hP6Ib4P56Sd5itxdV4w0hIGHKPGyLLKiAkYp+htIHa9T7IvjMdaqxIlsfyV3ZKsS1sw==", "license": "MIT", "dependencies": { - "prosemirror-changeset": "^2.3.0", - "prosemirror-commands": "^1.6.2", - "prosemirror-dropcursor": "^1.8.1", - "prosemirror-gapcursor": "^1.3.2", - "prosemirror-history": "^1.4.1", - "prosemirror-inputrules": "^1.4.0", + "prosemirror-changeset": "^2.4.1", + "prosemirror-commands": "^1.7.1", + "prosemirror-dropcursor": "^1.8.2", + "prosemirror-gapcursor": "^1.4.1", + "prosemirror-history": "^1.5.0", + "prosemirror-inputrules": "^1.5.1", "prosemirror-keymap": "^1.2.3", - "prosemirror-model": "^1.25.7", - "prosemirror-schema-list": "^1.5.0", + "prosemirror-model": "^1.25.9", + "prosemirror-schema-list": "^1.5.1", "prosemirror-state": "^1.4.4", - "prosemirror-tables": "^1.8.0", + "prosemirror-tables": "^1.8.5", "prosemirror-transform": "^1.12.0", - "prosemirror-view": "^1.41.8" + "prosemirror-view": "^1.41.9" }, "funding": { "type": "github", @@ -6051,9 +6296,9 @@ } }, "node_modules/@tiptap/react": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.27.1.tgz", - "integrity": "sha512-/Wn2fc9zMtX08MXYScDFsm4wJ8lzfhfPEdbtls7WCDlbtrop48PWlkHDBBJrywARfAQTB2mFs9KiFy9yrQm5Lg==", + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.27.3.tgz", + "integrity": "sha512-lanXxMScw/LevbRFuFw5MoQJ1grmHUDszwph2VIk4l2U5e/EDIw3rr4CeDME6Z4isVReD6zccMFVShkHqsv+jg==", "license": "MIT", "dependencies": { "@types/use-sync-external-store": "^0.0.6", @@ -6065,12 +6310,12 @@ "url": "https://github.com/sponsors/ueberdosis" }, "optionalDependencies": { - "@tiptap/extension-bubble-menu": "^3.27.1", - "@tiptap/extension-floating-menu": "^3.27.1" + "@tiptap/extension-bubble-menu": "^3.27.3", + "@tiptap/extension-floating-menu": "^3.27.3" }, "peerDependencies": { - "@tiptap/core": "3.27.1", - "@tiptap/pm": "3.27.1", + "@tiptap/core": "3.27.3", + "@tiptap/pm": "3.27.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -6078,44 +6323,44 @@ } }, "node_modules/@tiptap/react/node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz", + "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==", "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@tiptap/starter-kit": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.27.1.tgz", - "integrity": "sha512-vfxRsqW8rCc0k4pzo0ilU3wobVi2wqVj88VZI2SlgZlNnUAkrDGDIAph7CTa9k9fshV+O1ivpEgPC5yC046jow==", - "license": "MIT", - "dependencies": { - "@tiptap/core": "^3.27.1", - "@tiptap/extension-blockquote": "^3.27.1", - "@tiptap/extension-bold": "^3.27.1", - "@tiptap/extension-bullet-list": "^3.27.1", - "@tiptap/extension-code": "^3.27.1", - "@tiptap/extension-code-block": "^3.27.1", - "@tiptap/extension-document": "^3.27.1", - "@tiptap/extension-dropcursor": "^3.27.1", - "@tiptap/extension-gapcursor": "^3.27.1", - "@tiptap/extension-hard-break": "^3.27.1", - "@tiptap/extension-heading": "^3.27.1", - "@tiptap/extension-horizontal-rule": "^3.27.1", - "@tiptap/extension-italic": "^3.27.1", - "@tiptap/extension-link": "^3.27.1", - "@tiptap/extension-list": "^3.27.1", - "@tiptap/extension-list-item": "^3.27.1", - "@tiptap/extension-list-keymap": "^3.27.1", - "@tiptap/extension-ordered-list": "^3.27.1", - "@tiptap/extension-paragraph": "^3.27.1", - "@tiptap/extension-strike": "^3.27.1", - "@tiptap/extension-text": "^3.27.1", - "@tiptap/extension-underline": "^3.27.1", - "@tiptap/extensions": "^3.27.1", - "@tiptap/pm": "^3.27.1" + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.27.3.tgz", + "integrity": "sha512-xX3baFqiC30skntdhxUUvyJo755ON9c1pE83+2Wiq2g+Qnffg9knUuLCZStHoZZ318yB0aBsKAMvHWLtYDo8AA==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.27.3", + "@tiptap/extension-blockquote": "^3.27.3", + "@tiptap/extension-bold": "^3.27.3", + "@tiptap/extension-bullet-list": "^3.27.3", + "@tiptap/extension-code": "^3.27.3", + "@tiptap/extension-code-block": "^3.27.3", + "@tiptap/extension-document": "^3.27.3", + "@tiptap/extension-dropcursor": "^3.27.3", + "@tiptap/extension-gapcursor": "^3.27.3", + "@tiptap/extension-hard-break": "^3.27.3", + "@tiptap/extension-heading": "^3.27.3", + "@tiptap/extension-horizontal-rule": "^3.27.3", + "@tiptap/extension-italic": "^3.27.3", + "@tiptap/extension-link": "^3.27.3", + "@tiptap/extension-list": "^3.27.3", + "@tiptap/extension-list-item": "^3.27.3", + "@tiptap/extension-list-keymap": "^3.27.3", + "@tiptap/extension-ordered-list": "^3.27.3", + "@tiptap/extension-paragraph": "^3.27.3", + "@tiptap/extension-strike": "^3.27.3", + "@tiptap/extension-text": "^3.27.3", + "@tiptap/extension-underline": "^3.27.3", + "@tiptap/extensions": "^3.27.3", + "@tiptap/pm": "^3.27.3" }, "funding": { "type": "github", @@ -7181,9 +7426,9 @@ "license": "MIT" }, "node_modules/@types/three": { - "version": "0.185.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.185.0.tgz", - "integrity": "sha512-O2Uy8Cj4Nonr8dWUUbifMdPe8B0Mq7EdOHb89S4+kjUw/KhbjTZrUuYlrQ1bpUKG+EP9QJnN7qNxbHGlGoLHMA==", + "version": "0.185.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.185.1.tgz", + "integrity": "sha512-db1xTb+EgYF2didW+eudSvVPtn75zo+fGsY8ShQrJY/B5ZBmC2Fiaykv3aImHAlCNEGuMPkPGXBJGLwzu5mC7A==", "dev": true, "license": "MIT", "dependencies": { @@ -7282,16 +7527,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -7300,13 +7545,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -7327,9 +7572,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7340,13 +7585,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -7354,14 +7599,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -7370,9 +7615,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -7380,13 +7625,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -11245,9 +11490,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -11513,9 +11758,9 @@ } }, "node_modules/prosemirror-model": { - "version": "1.25.8", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.8.tgz", - "integrity": "sha512-BswA4BLSFEiORV6Vjj/yZBXDbos1zTEnhyeSSgT8psGFhstQS7UJ8/WOLiDos9Byaee27+tml0/DuMNxYR84zg==", + "version": "1.25.11", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.11.tgz", + "integrity": "sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==", "license": "MIT", "dependencies": { "orderedmap": "^2.0.0" @@ -13214,16 +13459,16 @@ } }, "node_modules/vite": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", - "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", + "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", + "picomatch": "^4.0.5", "postcss": "^8.5.16", - "rolldown": "~1.1.3", + "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "bin": { @@ -13307,19 +13552,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -13347,12 +13592,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/desktop/package.json b/desktop/package.json index 5b40d5264..12f34bcd0 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -14,9 +14,9 @@ "dependencies": { "@codemirror/lang-markdown": "^6.5.0", "@codemirror/language-data": "^6.5.2", - "@codemirror/state": "^6.7.0", + "@codemirror/state": "^6.7.1", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.43.5", + "@codemirror/view": "^6.43.6", "@excalidraw/excalidraw": "^0.18.1", "@excalidraw/mermaid-to-excalidraw": "^2.2.2", "@fortune-sheet/core": "^1.0.4", @@ -24,19 +24,19 @@ "@milkdown/kit": "^7.21.2", "@milkdown/react": "^7.21.2", "@milkdown/theme-nord": "^7.21.2", - "@radix-ui/react-dialog": "^1.1.18", - "@radix-ui/react-dropdown-menu": "^2.1.19", + "@radix-ui/react-dialog": "^1.1.19", + "@radix-ui/react-dropdown-menu": "^2.1.20", "@radix-ui/react-label": "^2.1.11", - "@radix-ui/react-select": "^2.3.2", + "@radix-ui/react-select": "^2.3.3", "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-switch": "^1.3.2", - "@radix-ui/react-tabs": "^1.1.16", - "@radix-ui/react-tooltip": "^1.2.11", - "@tiptap/extension-link": "^3.27.1", - "@tiptap/extension-underline": "^3.27.1", - "@tiptap/pm": "^3.27.1", + "@radix-ui/react-switch": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.17", + "@radix-ui/react-tooltip": "^1.2.12", + "@tiptap/extension-link": "^3.27.3", + "@tiptap/extension-underline": "^3.27.3", + "@tiptap/pm": "^3.27.3", "@tiptap/react": "^3.26.1", - "@tiptap/starter-kit": "^3.27.1", + "@tiptap/starter-kit": "^3.27.3", "@tldraw/assets": "4.5.12", "@tldraw/tldraw": "^4.5.10", "@tonejs/midi": "^2.0.28", @@ -81,13 +81,13 @@ "@testing-library/react": "^16.3.2", "@types/react": "^19.2.17", "@types/react-dom": "^19.0.0", - "@types/three": "0.185.0", + "@types/three": "0.185.1", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "tailwindcss": "^4.0.0", "typescript": "^6.0.3", - "vite": "^8.1.3", - "vitest": "^4.1.8" + "vite": "^8.1.4", + "vitest": "^4.1.10" }, "overrides": { "dompurify": "^3.4.0", diff --git a/desktop/src/apps/ChessApp.test.tsx b/desktop/src/apps/ChessApp.test.tsx new file mode 100644 index 000000000..4285b2d81 --- /dev/null +++ b/desktop/src/apps/ChessApp.test.tsx @@ -0,0 +1,176 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; +import { ChessApp } from "./ChessApp"; + +function mockAgentsFetch(agents: Array<{ name: string }> = []) { + return vi.fn().mockImplementation((input: RequestInfo | URL) => { + const url = String(input); + if (url.includes("/api/agents")) { + return Promise.resolve({ + ok: true, + json: () => Promise.resolve(agents), + }); + } + return Promise.resolve({ + ok: false, + json: () => Promise.resolve({}), + }); + }); +} + +async function flush() { + await act(async () => { + await new Promise((r) => setTimeout(r, 0)); + }); +} + +function square(label: string | RegExp) { + return screen.getByRole("button", { name: label }); +} + +describe("ChessApp", () => { + beforeEach(() => { + vi.stubGlobal("fetch", mockAgentsFetch()); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); + }); + + it("renders the board, status, and controls on mount", async () => { + render(); + await flush(); + + expect(screen.getByRole("grid", { name: /chess board/i })).toBeTruthy(); + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + expect(screen.getByRole("log", { name: /move history/i })).toBeTruthy(); + expect(screen.getByText(/no moves yet/i)).toBeTruthy(); + expect(screen.getByRole("button", { name: /new game/i })).toBeTruthy(); + expect(screen.getByRole("button", { name: /undo move/i })).toBeTruthy(); + expect(screen.getByLabelText(/game mode/i)).toBeTruthy(); + // Starting pieces present + expect(square(/e2 white p/i)).toBeTruthy(); + expect(square(/e8 black k/i)).toBeTruthy(); + }); + + it("mocks the on-mount agents fetch", async () => { + const fetchMock = mockAgentsFetch([{ name: "alpha-bot" }]); + vi.stubGlobal("fetch", fetchMock); + + render(); + await flush(); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalled(); + }); + const agentCalls = fetchMock.mock.calls.filter((c) => + String(c[0]).includes("/api/agents"), + ); + expect(agentCalls.length).toBeGreaterThanOrEqual(1); + }); + + it("updates board state after a legal pawn move (e2-e4)", async () => { + render(); + await flush(); + + fireEvent.click(square(/e2 white p/i)); + fireEvent.click(square(/^e4$/i)); + + // Turn flips and the pawn is on e4 + expect(screen.getByRole("status").textContent).toMatch(/black to move/i); + expect(square(/e4 white p/i)).toBeTruthy(); + expect(square(/^e2$/i)).toBeTruthy(); + }); + + it("switches turn after each legal move", async () => { + render(); + await flush(); + + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + + fireEvent.click(square(/e2 white p/i)); + fireEvent.click(square(/^e4$/i)); + expect(screen.getByRole("status").textContent).toMatch(/black to move/i); + expect(square(/e4 white p/i)).toBeTruthy(); + + fireEvent.click(square(/e7 black p/i)); + fireEvent.click(square(/^e5$/i)); + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + expect(square(/e5 black p/i)).toBeTruthy(); + }); + + it("does not move when clicking an illegal target", async () => { + render(); + await flush(); + + fireEvent.click(square(/e2 white p/i)); + // e5 is not a legal first move for the e2 pawn + fireEvent.click(square(/^e5$/i)); + + // Still white to move; pawn remains on e2 + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + expect(square(/e2 white p/i)).toBeTruthy(); + }); + + it("shows checkmate status after fool's mate", async () => { + render(); + await flush(); + + // 1. f2-f3 + fireEvent.click(square(/f2 white p/i)); + fireEvent.click(square(/^f3$/i)); + // 1... e7-e5 + fireEvent.click(square(/e7 black p/i)); + fireEvent.click(square(/^e5$/i)); + // 2. g2-g4 + fireEvent.click(square(/g2 white p/i)); + fireEvent.click(square(/^g4$/i)); + // 2... d8-h4# + fireEvent.click(square(/d8 black q/i)); + fireEvent.click(square(/^h4$/i)); + + const status = screen.getByRole("status").textContent ?? ""; + expect(status).toMatch(/checkmate/i); + expect(status).toMatch(/black wins/i); + }); + + it("resets the board with New Game", async () => { + render(); + await flush(); + + fireEvent.click(square(/e2 white p/i)); + fireEvent.click(square(/^e4$/i)); + expect(screen.getByRole("status").textContent).toMatch(/black to move/i); + + fireEvent.click(screen.getByRole("button", { name: /new game/i })); + + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + expect(square(/e2 white p/i)).toBeTruthy(); + // e4 should be empty again + expect(square(/^e4$/i)).toBeTruthy(); + }); + + it("shows agent selector when switching to vs-agent mode", async () => { + const fetchMock = mockAgentsFetch([{ name: "alpha-bot" }, { name: "beta-bot" }]); + vi.stubGlobal("fetch", fetchMock); + + render(); + await flush(); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalled(); + }); + + fireEvent.change(screen.getByLabelText(/game mode/i), { + target: { value: "vs-agent" }, + }); + + // Mode change starts a fresh game + expect(screen.getByRole("status").textContent).toMatch(/white to move/i); + await waitFor(() => { + expect(screen.getByLabelText(/select agent opponent/i)).toBeTruthy(); + }); + expect(screen.getByText("alpha-bot")).toBeTruthy(); + }); +}); diff --git a/desktop/src/apps/FilesApp.tsx b/desktop/src/apps/FilesApp.tsx index df8d9fa85..26a14f28f 100644 --- a/desktop/src/apps/FilesApp.tsx +++ b/desktop/src/apps/FilesApp.tsx @@ -34,6 +34,7 @@ import { ContextMenu, type MenuItem } from "@/components/ContextMenu"; import { MobileSplitView } from "@/components/mobile/MobileSplitView"; import { useIsMobile } from "@/hooks/use-is-mobile"; import { resolveAgentEmoji } from "@/lib/agent-emoji"; +import { projectsApi, type DocReviewState } from "@/lib/projects"; import { useDragSource } from "@/shell/dnd/use-drag-source"; /* ------------------------------------------------------------------ */ @@ -297,6 +298,50 @@ function getFileIcon(name: string, isDir: boolean) { return EXT_ICONS[ext] ?? File; } +const REVIEW_BADGE_STYLES: Record = { + approved: "bg-emerald-500/15 text-emerald-400 border-emerald-500/20", + changes_requested: "bg-amber-500/15 text-amber-400 border-amber-500/20", + awaiting_review: "bg-zinc-500/10 text-zinc-400 border-zinc-500/15", +}; + +const REVIEW_BADGE_LABEL: Record = { + approved: "Approved", + changes_requested: "Changes requested", + awaiting_review: "Awaiting review", +}; + +function ReviewBadge({ + state, + onClick, +}: { + state: string | null; + onClick?: () => void; +}) { + if (!state) return null; + const style = REVIEW_BADGE_STYLES[state] ?? REVIEW_BADGE_STYLES.awaiting_review; + const label = REVIEW_BADGE_LABEL[state] ?? state; + if (onClick) { + return ( + + ); + } + return ( + + {label} + + ); +} + /* ------------------------------------------------------------------ */ /* API helpers */ /* ------------------------------------------------------------------ */ @@ -329,6 +374,8 @@ interface FileRowProps { setDeleteConfirm: (path: string | null) => void; onContextMenu: (e: React.MouseEvent, f: FileEntry) => void; isCut: boolean; + reviewState: string | null; + onDocReviewClick?: () => void; } function FileRow({ @@ -342,6 +389,8 @@ function FileRow({ setDeleteConfirm, onContextMenu, isCut, + reviewState, + onDocReviewClick, }: FileRowProps) { const Icon = getFileIcon(f.name, f.is_dir); const relPath = f.path || (currentPath ? `${currentPath}/${f.name}` : f.name); @@ -410,6 +459,11 @@ function FileRow({ {formatDate(f.modified)} + + {!f.is_dir && isProjectLocation(location) && ( + + )} +
{!f.is_dir && isWritable && ( @@ -502,9 +556,50 @@ export function FilesApp({ const [workspaceTrashLoading, setWorkspaceTrashLoading] = useState(false); const [workspaceTrashError, setWorkspaceTrashError] = useState(null); + // Doc-review stamp state (project: locations only) + const [reviewStates, setReviewStates] = useState>({}); + const fileInputRef = useRef(null); const dragCounter = useRef(0); + const fetchReviewStates = useCallback(async (loc: string) => { + if (!isProjectLocation(loc)) { + setReviewStates({}); + return; + } + const pid = projectSlug(loc); + try { + const items = await projectsApi.docReviews.list(pid); + const map: Record = {}; + for (const item of items) { + if (item.review_state) map[item.doc_path] = item.review_state; + } + setReviewStates(map); + } catch { + setReviewStates({}); + } + }, []); + + const cycleDocReview = useCallback( + async (docPath: string, current: string | null) => { + if (!isProjectLocation(location)) return; + const pid = projectSlug(location); + const order: DocReviewState[] = ["awaiting_review", "approved", "changes_requested"]; + const idx = current ? order.indexOf(current as DocReviewState) : 0; + const next = order[(idx + 1) % order.length]!; + try { + await projectsApi.docReviews.set(pid, docPath, next); + setReviewStates((prev) => { + const nextMap: Record = { ...prev, [docPath]: next }; + return nextMap; + }); + } catch { + /* write rejected (forbidden / offline): keep the current stamp */ + } + }, + [location], + ); + // Workspace locations (user + per-agent + project) allow mutations and the stats // endpoint. Shared folders and the recycle bin are read-only here. const isWritable = location === "workspace" || isAgentLocation(location) || isProjectLocation(location); @@ -611,6 +706,11 @@ export function FilesApp({ .catch(() => setStats(null)); }, [location]); + // Refresh doc-review badges whenever we switch project locations. + useEffect(() => { + fetchReviewStates(location); + }, [location, fetchReviewStates]); + /* ---- Recycle bin ---- */ const fetchRecycle = useCallback(async () => { setRecycleLoading(true); @@ -722,9 +822,10 @@ export function FilesApp({ await apiFetch("/api/workspace/trash", { method: "DELETE" }); fetchWorkspaceTrash(); } catch (e: unknown) { - setWorkspaceTrashError(e instanceof Error ? e.message : "Empty trash failed"); + const msg = e instanceof Error ? e.message : "Empty trash failed"; + setError(msg); } - }, [fetchWorkspaceTrash, workspaceTrashItems.length]); + }, [workspaceTrashItems.length, fetchWorkspaceTrash]); /* ---- Actions ---- */ const handleNewFolder = useCallback(async () => { @@ -1602,6 +1703,20 @@ export function FilesApp({ {f.name} + {!f.is_dir && isProjectLocation(location) && ( + + cycleDocReview( + f.path || f.name, + reviewStates[f.path || f.name] ?? null, + ) + : undefined + } + /> + )} {!f.is_dir && ( {formatSize(f.size)} )} @@ -1656,6 +1771,7 @@ export function FilesApp({ Name Size Modified + Review Actions @@ -1677,6 +1793,16 @@ export function FilesApp({ setMenu({ x: e.clientX, y: e.clientY, file }); }} isCut={clipboard?.mode === "cut" && clipboard.location === location && clipboard.path === f.path} + reviewState={ + !f.is_dir && isProjectLocation(location) + ? reviewStates[f.path || f.name] ?? null + : null + } + onDocReviewClick={ + !f.is_dir && isProjectLocation(location) + ? () => cycleDocReview(f.path || f.name, reviewStates[f.path || f.name] ?? null) + : undefined + } /> ))} diff --git a/desktop/src/apps/HubApp/HubApp.test.tsx b/desktop/src/apps/HubApp/HubApp.test.tsx new file mode 100644 index 000000000..a798f2a63 --- /dev/null +++ b/desktop/src/apps/HubApp/HubApp.test.tsx @@ -0,0 +1,146 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import React from "react"; + +vi.mock("@/components/ui", () => ({ + Button: ({ + children, + onClick, + disabled, + ...rest + }: React.ButtonHTMLAttributes & { children?: React.ReactNode }) => ( + + ), + Textarea: ({ + onChange, + value, + placeholder, + ...rest + }: React.TextareaHTMLAttributes) => ( +