@mndrk/agx 2.4.5 → 2.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cloud-runtime/standalone/apps/local/.next/BUILD_ID +1 -1
- package/cloud-runtime/standalone/apps/local/.next/app-path-routes-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/build-manifest.json +2 -2
- package/cloud-runtime/standalone/apps/local/.next/prerender-manifest.json +3 -3
- package/cloud-runtime/standalone/apps/local/.next/routes-manifest.json +33 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents/[id]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents/[id]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_full.segment.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents/__PAGE__.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/agent-specs/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/agents/[id]/profile/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/agents/export/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/chat/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/file-search/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/participants/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/activities/route.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/activities/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/notes/[noteId]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/notes/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/app-paths-manifest.json +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/build-manifest.json +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route/server-reference-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js +13 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js.map +5 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route.js.nft.json +1 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/program/route_client-reference-manifest.js +2 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/scheduled-tasks/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/validate/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/objectives/[objectiveId]/worker/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/search/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/teams/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/app-paths-manifest.json +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/build-manifest.json +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route/server-reference-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js.map +5 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route.js.nft.json +1 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/export/route_client-reference-manifest.js +2 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/app-paths-manifest.json +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/build-manifest.json +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route/server-reference-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js.map +5 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route.js.nft.json +1 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/[id]/workspace/import/route_client-reference-manifest.js +2 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/prompt-jobs/poll/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/providers/check/[id]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/providers/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/schedules/debug/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/schedules/poll/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/assign/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/available/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/detail/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/history/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/learn/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/skills/unlearn/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/status/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/summarize/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/comments/[commentId]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/comments/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/nodes/[nodeId]/verify/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/threads/knowledge/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/threads/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/recap/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/scripted/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/status/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/api/update-check/route.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_full.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github/select-repos/__PAGE__.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github/select-repos.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations/github.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/integrations/github/select-repos.segments/integrations.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/automations/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/automations/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/env-vars/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/env-vars/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/folders/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/folders/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/graph/[taskId]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/graph/[taskId]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/notifications/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/notifications/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/[objectiveId]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/[objectiveId]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/prs/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/prs/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/agents/[agentId]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/agents/[agentId]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/adopt/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/adopt/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/new/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/new/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/replace/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/replace/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/terminal/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/terminal/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/[threadId]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/[threadId]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/app-paths-manifest.json +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/build-manifest.json +18 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/next-font-manifest.json +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/react-loadable-manifest.json +1 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page/server-reference-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js.map +5 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page.js.nft.json +1 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/page_client-reference-manifest.js +2 -0
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/[tracker]/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/[tracker]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/connect/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/connect/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_full.segment.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup/page.js.nft.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_full.segment.rsc +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/status/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_full.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_head.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_index.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_tree.segment.rsc +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status/__PAGE__.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status.segment.rsc +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/app-paths-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/30bdd_server_app_api_projects_[id]_workspace_export_route_actions_e161f108.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/30bdd_server_app_api_projects_[id]_workspace_import_route_actions_10d2bce2.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/7255d_app_api_projects_[id]_objectives_[objectiveId]_program_route_actions_e6cdbc76.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__1563ebff._.js +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__3fdd57cb._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__45af4365._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__683b7ad7._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__736d5859._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__96b5ea56._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__ba72d342._.js +5 -5
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__c3188470._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__eb69343e._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_1d99e7a5._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_4d88e4dc._.js +91 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_4dec65a2._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_50e3a6e3._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_56a2544c._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_5843a1a1._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_a15e73a6._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_bd72806a._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_c101ba9e._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_c530bd77._.js +11 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_d69937f2._.js +91 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_e2867b32._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_e32b0cab._.js +91 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_lib_5ab8f104._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_lib_db_ts_77c23d6c._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/0da96_local__next-internal_server_app_projects_[slug]_thread_page_actions_3ceeb59b.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__1e0fd816._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__42499ecf._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__5f31e40b._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__636799fb._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{[root-of-the-server]__4bf7d5b9._.js → [root-of-the-server]__74436f61._.js} +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__9434d6d7._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__bd339da0._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__d08d7899._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__e02a457c._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__e3a1fef0._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__f802e7d1._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__ff96bb77._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_6a2ba8f0._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_7cb6b08a._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_a0f42923._.js +7 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_a93c1059._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_components_PromptJobBoard_tsx_281b2873._.js → apps_local_04cb9fd5._.js} +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_15502af8._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_16eaf7ae._.js +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_3c11c068._.js +8 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_89b49aad._.js +7 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_988d29c0._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_ae1a134f._.js +7 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_agents_page_tsx_2a02508d._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_folders_page_tsx_72fb68e5._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_layout_tsx_3bb31889._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_page_tsx_76330306._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_app_projects_[slug]_teams_[teamId]_page_tsx_6dcfdd52._.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_b8e580cc._.js +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_TrackerBoard_tsx_98970bab._.js +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_app_projects_[slug]_thread_[threadId]_page_tsx_2a1d1d5e._.js → apps_local_components_chat-ui_ChatContainer_tsx_47371955._.js} +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_projects_ProjectObjectivesWorkspace_tsx_751ab3d3._.js +3 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_eb223411._.js +18 -0
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/{apps_local_7d1abfbf._.js → apps_local_f1c9872a._.js} +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/functions-config-manifest.json +3 -0
- package/cloud-runtime/standalone/apps/local/.next/server/middleware-manifest.json +5 -5
- package/cloud-runtime/standalone/apps/local/.next/server/next-font-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/next-font-manifest.json +4 -0
- package/cloud-runtime/standalone/apps/local/.next/server/pages/404.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/pages/500.html +2 -2
- package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/08e75b35d6c52269.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/0d22a723b9b96e65.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{cb19d71dd7ffb935.js → 1585dd5f9ec4866a.js} +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/1bc8cc9cbb99c1ed.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/2586c0baa16e9a0d.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/2aa24a251622fd06.js +5 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/39513207112d6828.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{74e3a3e93be78db8.js → 40857295bb9226c4.js} +7 -7
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{8b5d6f5bade8c4ea.js → 41ae15220fc19bf7.js} +2 -2
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/5092f14c2bf43204.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/5159169343ce6d2f.css +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/67b36c70b72e0e10.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/7bfbfcd072f8d61e.js +20 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/7cf26dd04166f32f.js +6 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/90dcb743d23feeca.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/93749b45e2700808.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{48feca51cf97c8a7.js → 9bb8f1406365af81.js} +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/b4aa71020acae46f.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/c352706a0ad7e35d.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/c5436b2b7e8f0ca9.js +16 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{1600dd6ff8607d8f.js → c9a2c4b55bb76f4f.js} +6 -6
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/d6a9cc66254693b8.js +1 -0
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{9e703dd5d95e9557.js → e3bdd1e0f385114e.js} +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{93b2b6aa0c9593f6.js → e6d4118b7c32d11c.js} +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/{6ffcd12eabd7c65e.js → f71d73895d228547.js} +1 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/f750d2f1882d31a8.js +5 -0
- package/cloud-runtime/standalone/apps/local/app/api/automations/route.ts +2 -10
- package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/objectives/[objectiveId]/program/route.ts +77 -0
- package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/workspace/export/route.ts +35 -0
- package/cloud-runtime/standalone/apps/local/app/api/projects/[id]/workspace/import/route.ts +91 -0
- package/cloud-runtime/standalone/apps/local/app/api/prompt-jobs/route.ts +1 -8
- package/cloud-runtime/standalone/apps/local/app/projects/[slug]/layout.tsx +1 -1
- package/cloud-runtime/standalone/apps/local/app/projects/[slug]/teams/[teamId]/page.tsx +58 -2
- package/cloud-runtime/standalone/apps/local/app/projects/[slug]/thread/page.tsx +14 -0
- package/cloud-runtime/standalone/apps/local/components/AutomationsBoard.tsx +15 -15
- package/cloud-runtime/standalone/apps/local/components/PromptJobBoard.tsx +80 -45
- package/cloud-runtime/standalone/apps/local/components/projects/FoldersSummaryCard.tsx +51 -196
- package/cloud-runtime/standalone/apps/local/components/projects/FoldersView.tsx +662 -784
- package/cloud-runtime/standalone/apps/local/components/projects/ObjectiveScheduledTasksPanel.tsx +85 -249
- package/cloud-runtime/standalone/apps/local/components/projects/ProjectHome.tsx +4 -4
- package/cloud-runtime/standalone/apps/local/components/projects/ProjectObjectivesWorkspace.tsx +118 -0
- package/cloud-runtime/standalone/apps/local/components/projects/ProjectSettings.tsx +81 -7
- package/cloud-runtime/standalone/apps/local/components/thread/WorkspaceSidebar.tsx +15 -5
- package/cloud-runtime/standalone/apps/local/hooks/useProjectWorkspace.ts +117 -0
- package/cloud-runtime/standalone/apps/local/lib/db/projects.ts +14 -10
- package/cloud-runtime/standalone/apps/local/lib/project-workspace.ts +80 -0
- package/cloud-runtime/standalone/apps/local/lib/task-context.ts +39 -4
- package/cloud-runtime/standalone/apps/local/lib/workspace-yaml.ts +164 -0
- package/cloud-runtime/standalone/apps/local/src/objectives/program/index.ts +7 -0
- package/cloud-runtime/standalone/apps/local/src/objectives/program/repository.ts +60 -0
- package/cloud-runtime/standalone/apps/local/src/prompt-scheduler/objective-worker.ts +12 -0
- package/cloud-runtime/standalone/apps/local/src/scheduling/status.ts +42 -0
- package/cloud-runtime/standalone/apps/local/worker/index.js +15 -10
- package/lib/commands/workspace.js +145 -7
- package/package.json +1 -1
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_09d85861._.js +0 -91
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_25136564._.js +0 -11
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_2caf4398._.js +0 -91
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_31ca7a35._.js +0 -91
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/9fb8c_lucide-react_dist_esm_icons_8c8ee1bc._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__3b4708d5._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__49cbf624._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__49cdd178._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__55ed4ff9._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__7a31a76d._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__7e3c2ea1._.js +0 -7
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__bb42490f._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__dbda910b._.js +0 -7
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__ee9351f9._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_347d036d._.js +0 -7
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_917d90b4._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/_f99c22fd._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_3b90cb76._.js +0 -18
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_9f36c765._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_a2835b49._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_8cc1a335._.js +0 -3
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/1d8b2726979134e5.js +0 -16
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/55a31773f8c2833f.js +0 -20
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/6952b44e247d594a.js +0 -16
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/6bfdc40a24e65ca2.js +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/7c8515a24aeea102.js +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/7ff071e8a33d2fd8.js +0 -16
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/80446e085a3aad56.css +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/87e4fb11329a358d.js +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/941f7f3626ec442c.js +0 -16
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/94a66d2d19268d25.js +0 -5
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/9c29ef447bef1576.js +0 -5
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/a9111ccc979d4933.js +0 -5
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/b35865f64b56d6f4.js +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/d2b34ade19d9d8da.js +0 -1
- package/cloud-runtime/standalone/apps/local/.next/static/chunks/f154e2ed7753f8ee.js +0 -16
- /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_buildManifest.js +0 -0
- /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_clientMiddlewareManifest.json +0 -0
- /package/cloud-runtime/standalone/apps/local/.next/static/{C3-tP4djbU34_g7VoIFac → xVMVGrzDjtD1H0bGL76NJ}/_ssgManifest.js +0 -0
|
@@ -1,855 +1,733 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { ChangeEvent } from "react";
|
|
4
|
+
import { useCallback, useRef, useState } from "react";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
Copy,
|
|
7
|
-
Check,
|
|
8
|
-
ExternalLink,
|
|
6
|
+
Download,
|
|
9
7
|
FileText,
|
|
8
|
+
Folder,
|
|
10
9
|
FolderGit2,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
GitBranch,
|
|
14
|
-
HardDrive,
|
|
10
|
+
FolderPlus,
|
|
11
|
+
Loader2,
|
|
15
12
|
Pencil,
|
|
16
13
|
Plus,
|
|
17
14
|
Save,
|
|
15
|
+
Settings,
|
|
16
|
+
TerminalSquare,
|
|
18
17
|
Trash2,
|
|
18
|
+
Upload,
|
|
19
19
|
X,
|
|
20
20
|
} from "lucide-react";
|
|
21
|
-
import
|
|
22
|
-
import
|
|
21
|
+
import type { WorkspaceEntry } from "@/lib/db/types";
|
|
22
|
+
import { useProjectWorkspace } from "@/hooks/useProjectWorkspace";
|
|
23
|
+
import { buildWorkspaceCategoryGroups } from "@/lib/project-workspace";
|
|
24
|
+
import { DEFAULT_WORKSPACE_CATEGORIES } from "@/lib/workspace-categories";
|
|
23
25
|
|
|
24
26
|
interface FoldersViewProps {
|
|
25
27
|
projectId: string;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
type
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
branch?: string;
|
|
38
|
-
status?: { modified: number; untracked: number; staged: number };
|
|
39
|
-
languages: Record<string, number>;
|
|
30
|
+
type StatusTone = "success" | "error" | "neutral";
|
|
31
|
+
|
|
32
|
+
interface EntryDraft {
|
|
33
|
+
category: string;
|
|
34
|
+
name: string;
|
|
35
|
+
path: string;
|
|
36
|
+
purpose: string;
|
|
37
|
+
error: string | null;
|
|
38
|
+
isSaving: boolean;
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
function getCategoryEntryLabel(categoryId: string, label: string): string {
|
|
42
|
+
switch (categoryId) {
|
|
43
|
+
case "repositories":
|
|
44
|
+
return "Repository";
|
|
45
|
+
case "docs":
|
|
46
|
+
return "Doc";
|
|
47
|
+
case "config":
|
|
48
|
+
return "Config";
|
|
49
|
+
case "scripts":
|
|
50
|
+
return "Script";
|
|
51
|
+
default:
|
|
52
|
+
return label;
|
|
53
|
+
}
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
setTimeout(() => setCopied(false), 1500);
|
|
57
|
-
}}
|
|
58
|
-
className="rounded p-1 text-[var(--app-shell-soft-text)] transition-colors hover:text-[var(--foreground)]"
|
|
59
|
-
title="Copy path"
|
|
60
|
-
>
|
|
61
|
-
{copied ? <Check className="w-3 h-3 text-green-400" /> : <Copy className="w-3 h-3" />}
|
|
62
|
-
</button>
|
|
63
|
-
);
|
|
56
|
+
function createEntryDraft(category: string, entry?: WorkspaceEntry): EntryDraft {
|
|
57
|
+
return {
|
|
58
|
+
category,
|
|
59
|
+
name: entry?.name ?? "",
|
|
60
|
+
path: entry?.path ?? "",
|
|
61
|
+
purpose: entry?.purpose ?? "",
|
|
62
|
+
error: null,
|
|
63
|
+
isSaving: false,
|
|
64
|
+
};
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
function getCategoryIcon(categoryId: string) {
|
|
68
|
+
switch (categoryId) {
|
|
69
|
+
case "repositories":
|
|
70
|
+
return FolderGit2;
|
|
71
|
+
case "docs":
|
|
72
|
+
return FileText;
|
|
73
|
+
case "config":
|
|
74
|
+
return Settings;
|
|
75
|
+
case "scripts":
|
|
76
|
+
return TerminalSquare;
|
|
77
|
+
default:
|
|
78
|
+
return Folder;
|
|
79
|
+
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
function
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
export function FoldersView({ projectId }: FoldersViewProps) {
|
|
83
|
+
const { workspace, entryCount, isLoading, error, refetch, createEntry, updateEntry, deleteEntry } = useProjectWorkspace(projectId);
|
|
84
|
+
const [customCategories, setCustomCategories] = useState<string[]>([]);
|
|
85
|
+
const [addingCategoryId, setAddingCategoryId] = useState<string | null>(null);
|
|
86
|
+
const [addDraft, setAddDraft] = useState<EntryDraft>(createEntryDraft("repositories"));
|
|
87
|
+
const [editingEntryId, setEditingEntryId] = useState<string | null>(null);
|
|
88
|
+
const [editDraft, setEditDraft] = useState<EntryDraft | null>(null);
|
|
89
|
+
const [isCreatingCategory, setIsCreatingCategory] = useState(false);
|
|
90
|
+
const [categoryDraft, setCategoryDraft] = useState("");
|
|
91
|
+
const [status, setStatus] = useState<{ tone: StatusTone; message: string } | null>(null);
|
|
92
|
+
const importInputRef = useRef<HTMLInputElement>(null);
|
|
93
|
+
|
|
94
|
+
const groups = buildWorkspaceCategoryGroups(workspace, customCategories);
|
|
95
|
+
const showEmptyState = entryCount === 0 && customCategories.length === 0 && !addingCategoryId;
|
|
96
|
+
|
|
97
|
+
const setFeedback = useCallback((message: string, tone: StatusTone = "neutral") => {
|
|
98
|
+
setStatus({ tone, message });
|
|
99
|
+
window.setTimeout(() => setStatus(null), 3500);
|
|
100
|
+
}, []);
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<div className="flex items-center gap-3">
|
|
89
|
-
<div className="flex items-center gap-1.5">
|
|
90
|
-
<GitBranch className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
|
|
91
|
-
{analysis.isGit ? (
|
|
92
|
-
<span className="text-xs text-[var(--foreground)]">
|
|
93
|
-
{analysis.branch ?? "detached"}
|
|
94
|
-
</span>
|
|
95
|
-
) : (
|
|
96
|
-
<span className="text-xs italic text-[var(--muted-foreground)]">Not a git repo</span>
|
|
97
|
-
)}
|
|
98
|
-
</div>
|
|
99
|
-
{analysis.isGit && analysis.status && (
|
|
100
|
-
<div className="flex items-center gap-2 text-xs text-[var(--muted-foreground)]">
|
|
101
|
-
{analysis.status.staged > 0 && (
|
|
102
|
-
<span className="text-[var(--status-completed-text)]">{analysis.status.staged} staged</span>
|
|
103
|
-
)}
|
|
104
|
-
{analysis.status.modified > 0 && (
|
|
105
|
-
<span className="text-[var(--status-blocked-text)]">{analysis.status.modified} modified</span>
|
|
106
|
-
)}
|
|
107
|
-
{analysis.status.untracked > 0 && (
|
|
108
|
-
<span className="text-[var(--muted-foreground)]">{analysis.status.untracked} untracked</span>
|
|
109
|
-
)}
|
|
110
|
-
{analysis.status.staged === 0 && analysis.status.modified === 0 && analysis.status.untracked === 0 && (
|
|
111
|
-
<span className="text-[var(--app-shell-soft-text)]">clean</span>
|
|
112
|
-
)}
|
|
113
|
-
</div>
|
|
114
|
-
)}
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
{/* Languages */}
|
|
118
|
-
{topLangs.length > 0 && (
|
|
119
|
-
<div className="flex flex-wrap gap-1.5">
|
|
120
|
-
{topLangs.map(([lang, count]) => (
|
|
121
|
-
<LanguageBadge key={lang} lang={lang} count={count} />
|
|
122
|
-
))}
|
|
123
|
-
</div>
|
|
124
|
-
)}
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
102
|
+
const pickFolder = useCallback(async () => {
|
|
103
|
+
const response = await fetch("/api/filesystem/pick-folder", { method: "POST" });
|
|
104
|
+
const data = await response.json().catch(() => ({}));
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(typeof data.error === "string" ? data.error : "Failed to open folder picker");
|
|
107
|
+
}
|
|
108
|
+
return typeof data.path === "string" ? data.path : null;
|
|
109
|
+
}, []);
|
|
129
110
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}: {
|
|
135
|
-
repo: ProjectRepo;
|
|
136
|
-
isSelected: boolean;
|
|
137
|
-
onSelect: () => void;
|
|
138
|
-
}) {
|
|
139
|
-
return (
|
|
140
|
-
<button
|
|
141
|
-
type="button"
|
|
142
|
-
onClick={onSelect}
|
|
143
|
-
className={`w-full text-left flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors group ${
|
|
144
|
-
isSelected
|
|
145
|
-
? "border border-blue-500/20 bg-blue-500/10"
|
|
146
|
-
: "border border-transparent hover:bg-[var(--secondary)]"
|
|
147
|
-
}`}
|
|
148
|
-
>
|
|
149
|
-
{isSelected ? (
|
|
150
|
-
<FolderOpen className="w-4 h-4 text-blue-400 flex-shrink-0" />
|
|
151
|
-
) : (
|
|
152
|
-
<FolderGit2 className="h-4 w-4 flex-shrink-0 text-[var(--muted-foreground)]" />
|
|
153
|
-
)}
|
|
154
|
-
<div className="min-w-0 flex-1">
|
|
155
|
-
<div className={`truncate text-sm font-medium ${isSelected ? "text-blue-500" : "text-[var(--foreground)]"}`}>
|
|
156
|
-
{repo.name}
|
|
157
|
-
</div>
|
|
158
|
-
<div className="truncate text-xs text-[var(--muted-foreground)]">
|
|
159
|
-
{repo.path || repo.git_url || "No path set"}
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
162
|
-
<ChevronRight
|
|
163
|
-
className={`w-3.5 h-3.5 flex-shrink-0 transition-colors ${
|
|
164
|
-
isSelected ? "text-blue-500" : "text-[var(--app-shell-soft-text)] group-hover:text-[var(--muted-foreground)]"
|
|
165
|
-
}`}
|
|
166
|
-
/>
|
|
167
|
-
</button>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
111
|
+
const closeAddForm = useCallback(() => {
|
|
112
|
+
setAddingCategoryId(null);
|
|
113
|
+
setAddDraft(createEntryDraft("repositories"));
|
|
114
|
+
}, []);
|
|
170
115
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
noteSaving,
|
|
178
|
-
onEdit,
|
|
179
|
-
onDelete,
|
|
180
|
-
}: {
|
|
181
|
-
repo: ProjectRepo;
|
|
182
|
-
systemNote: SystemNote | null;
|
|
183
|
-
analysis: RepoAnalysis | null;
|
|
184
|
-
analysisLoading: boolean;
|
|
185
|
-
onNoteSave: (repoId: string, content: string) => Promise<void>;
|
|
186
|
-
noteSaving: boolean;
|
|
187
|
-
onEdit: (repo: ProjectRepo) => void;
|
|
188
|
-
onDelete: (repoId: string) => void;
|
|
189
|
-
}) {
|
|
190
|
-
const [notes, setNotes] = useState(repo.notes ?? "");
|
|
191
|
-
const [editingNotes, setEditingNotes] = useState(false);
|
|
192
|
-
const [localNotes, setLocalNotes] = useState(notes);
|
|
193
|
-
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
setNotes(repo.notes ?? "");
|
|
196
|
-
setLocalNotes(repo.notes ?? "");
|
|
197
|
-
setEditingNotes(false);
|
|
198
|
-
}, [repo.id, repo.notes]);
|
|
116
|
+
const openAddForm = useCallback((category: string) => {
|
|
117
|
+
setAddingCategoryId(category);
|
|
118
|
+
setAddDraft(createEntryDraft(category));
|
|
119
|
+
setEditingEntryId(null);
|
|
120
|
+
setEditDraft(null);
|
|
121
|
+
}, []);
|
|
199
122
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<FolderOpen className="w-5 h-5 text-blue-400 flex-shrink-0" />
|
|
207
|
-
<h3 className="truncate text-lg font-semibold text-[var(--foreground)]">{repo.name}</h3>
|
|
208
|
-
</div>
|
|
209
|
-
{repo.path && (
|
|
210
|
-
<div className="flex items-center gap-1.5 ml-7">
|
|
211
|
-
<code className="truncate font-mono text-xs text-[var(--muted-foreground)]">{repo.path}</code>
|
|
212
|
-
<CopyPathButton path={repo.path} />
|
|
213
|
-
</div>
|
|
214
|
-
)}
|
|
215
|
-
{repo.git_url && (
|
|
216
|
-
<div className="flex items-center gap-1.5 ml-7 mt-1">
|
|
217
|
-
<ExternalLink className="h-3 w-3 flex-shrink-0 text-[var(--app-shell-soft-text)]" />
|
|
218
|
-
<a
|
|
219
|
-
href={repo.git_url}
|
|
220
|
-
target="_blank"
|
|
221
|
-
rel="noopener noreferrer"
|
|
222
|
-
className="text-xs text-blue-400/70 hover:text-blue-400 truncate"
|
|
223
|
-
>
|
|
224
|
-
{repo.git_url}
|
|
225
|
-
</a>
|
|
226
|
-
</div>
|
|
227
|
-
)}
|
|
228
|
-
</div>
|
|
229
|
-
<div className="flex items-center gap-1 flex-shrink-0">
|
|
230
|
-
<button
|
|
231
|
-
type="button"
|
|
232
|
-
onClick={() => onEdit(repo)}
|
|
233
|
-
className="rounded-lg p-1.5 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
|
|
234
|
-
title="Edit folder"
|
|
235
|
-
>
|
|
236
|
-
<Pencil className="w-3.5 h-3.5" />
|
|
237
|
-
</button>
|
|
238
|
-
<button
|
|
239
|
-
type="button"
|
|
240
|
-
onClick={() => onDelete(repo.id)}
|
|
241
|
-
className="rounded-lg p-1.5 text-[var(--app-shell-soft-text)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--destructive)]"
|
|
242
|
-
title="Delete folder"
|
|
243
|
-
>
|
|
244
|
-
<Trash2 className="w-3.5 h-3.5" />
|
|
245
|
-
</button>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
123
|
+
const openEditForm = useCallback((entry: WorkspaceEntry) => {
|
|
124
|
+
setEditingEntryId(entry.id);
|
|
125
|
+
setEditDraft(createEntryDraft(entry.category, entry));
|
|
126
|
+
setAddingCategoryId(null);
|
|
127
|
+
setAddDraft(createEntryDraft(entry.category));
|
|
128
|
+
}, []);
|
|
248
129
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
</div>
|
|
254
|
-
)}
|
|
255
|
-
{!analysisLoading && analysis && <AnalysisSummary analysis={analysis} />}
|
|
256
|
-
|
|
257
|
-
{/* Notes section */}
|
|
258
|
-
<div className="overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--card-bg)]">
|
|
259
|
-
<div className="flex items-center justify-between border-b border-[var(--border)] px-4 py-2.5">
|
|
260
|
-
<div className="flex items-center gap-2">
|
|
261
|
-
<FileText className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
|
|
262
|
-
<span className="text-xs font-medium uppercase tracking-wider text-[var(--muted-foreground)]">Notes</span>
|
|
263
|
-
</div>
|
|
264
|
-
{!editingNotes ? (
|
|
265
|
-
<button
|
|
266
|
-
type="button"
|
|
267
|
-
onClick={() => {
|
|
268
|
-
setLocalNotes(notes);
|
|
269
|
-
setEditingNotes(true);
|
|
270
|
-
}}
|
|
271
|
-
className="flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
272
|
-
>
|
|
273
|
-
<Pencil className="w-3 h-3" />
|
|
274
|
-
Edit
|
|
275
|
-
</button>
|
|
276
|
-
) : (
|
|
277
|
-
<div className="flex items-center gap-2">
|
|
278
|
-
<span className="text-[10px] text-[var(--app-shell-soft-text)]">Cmd+Enter to save</span>
|
|
279
|
-
<button
|
|
280
|
-
type="button"
|
|
281
|
-
onClick={() => {
|
|
282
|
-
setEditingNotes(false);
|
|
283
|
-
setLocalNotes(notes);
|
|
284
|
-
}}
|
|
285
|
-
className="flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
286
|
-
>
|
|
287
|
-
<X className="w-3 h-3" />
|
|
288
|
-
Cancel
|
|
289
|
-
</button>
|
|
290
|
-
<button
|
|
291
|
-
type="button"
|
|
292
|
-
disabled={noteSaving}
|
|
293
|
-
onClick={async () => {
|
|
294
|
-
await onNoteSave(repo.id, localNotes);
|
|
295
|
-
setNotes(localNotes);
|
|
296
|
-
setEditingNotes(false);
|
|
297
|
-
}}
|
|
298
|
-
className="flex items-center gap-1 text-xs text-blue-500 transition-colors hover:text-blue-600 disabled:opacity-50"
|
|
299
|
-
>
|
|
300
|
-
<Save className="w-3 h-3" />
|
|
301
|
-
Save
|
|
302
|
-
</button>
|
|
303
|
-
</div>
|
|
304
|
-
)}
|
|
305
|
-
</div>
|
|
306
|
-
<div className="px-4 py-3 min-h-[80px]">
|
|
307
|
-
{editingNotes ? (
|
|
308
|
-
<textarea
|
|
309
|
-
value={localNotes}
|
|
310
|
-
onChange={(e) => setLocalNotes(e.target.value)}
|
|
311
|
-
onKeyDown={(e) => {
|
|
312
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
313
|
-
e.preventDefault();
|
|
314
|
-
void (async () => {
|
|
315
|
-
await onNoteSave(repo.id, localNotes);
|
|
316
|
-
setNotes(localNotes);
|
|
317
|
-
setEditingNotes(false);
|
|
318
|
-
})();
|
|
319
|
-
} else if (e.key === "Escape") {
|
|
320
|
-
setEditingNotes(false);
|
|
321
|
-
setLocalNotes(notes);
|
|
322
|
-
}
|
|
323
|
-
}}
|
|
324
|
-
className="min-h-[120px] w-full resize-none bg-transparent text-sm text-[var(--foreground)] outline-none placeholder:text-[var(--app-shell-soft-text)]"
|
|
325
|
-
placeholder="Add notes about this folder... (architecture, conventions, important files, etc.)"
|
|
326
|
-
autoFocus
|
|
327
|
-
/>
|
|
328
|
-
) : notes ? (
|
|
329
|
-
<p className="whitespace-pre-wrap text-sm text-[var(--foreground)]">{notes}</p>
|
|
330
|
-
) : (
|
|
331
|
-
<p className="text-sm italic text-[var(--muted-foreground)]">No notes yet. Click edit to describe this folder.</p>
|
|
332
|
-
)}
|
|
333
|
-
</div>
|
|
334
|
-
</div>
|
|
130
|
+
const closeEditForm = useCallback(() => {
|
|
131
|
+
setEditingEntryId(null);
|
|
132
|
+
setEditDraft(null);
|
|
133
|
+
}, []);
|
|
335
134
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Generated Knowledge
|
|
343
|
-
</span>
|
|
344
|
-
{systemNote.updated_at && (
|
|
345
|
-
<span className="ml-auto text-xs text-[var(--app-shell-soft-text)]">
|
|
346
|
-
{new Date(systemNote.updated_at).toLocaleDateString()}
|
|
347
|
-
</span>
|
|
348
|
-
)}
|
|
349
|
-
</div>
|
|
350
|
-
<div className="whitespace-pre-wrap px-4 py-3 text-sm leading-relaxed text-[var(--muted-foreground)]">
|
|
351
|
-
{systemNote.content}
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
)}
|
|
355
|
-
</div>
|
|
356
|
-
);
|
|
357
|
-
}
|
|
135
|
+
const handleAddEntry = useCallback(async () => {
|
|
136
|
+
const name = addDraft.name.trim();
|
|
137
|
+
if (!name) {
|
|
138
|
+
setAddDraft((current) => ({ ...current, error: "Name is required" }));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
358
141
|
|
|
359
|
-
|
|
360
|
-
function AddFolderPanel({
|
|
361
|
-
onAdd,
|
|
362
|
-
onCancel,
|
|
363
|
-
}: {
|
|
364
|
-
onAdd: (name: string, path: string) => Promise<void>;
|
|
365
|
-
onCancel: () => void;
|
|
366
|
-
}) {
|
|
367
|
-
const [name, setName] = useState("");
|
|
368
|
-
const [selectedPath, setSelectedPath] = useState<string | null>(null);
|
|
369
|
-
const [saving, setSaving] = useState(false);
|
|
370
|
-
const [pickingNative, setPickingNative] = useState(false);
|
|
371
|
-
const [analysis, setAnalysis] = useState<RepoAnalysis | null>(null);
|
|
372
|
-
const [analyzing, setAnalyzing] = useState(false);
|
|
373
|
-
|
|
374
|
-
const analyzePath = useCallback(async (dirPath: string) => {
|
|
375
|
-
setAnalyzing(true);
|
|
142
|
+
setAddDraft((current) => ({ ...current, isSaving: true, error: null }));
|
|
376
143
|
try {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
144
|
+
await createEntry({
|
|
145
|
+
category: addDraft.category,
|
|
146
|
+
name,
|
|
147
|
+
path: addDraft.path.trim() || null,
|
|
148
|
+
purpose: addDraft.purpose.trim() || null,
|
|
381
149
|
});
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
150
|
+
closeAddForm();
|
|
151
|
+
setFeedback(`Added ${name} to ${groups.find((group) => group.id === addDraft.category)?.label ?? "workspace map"}`, "success");
|
|
152
|
+
} catch (err) {
|
|
153
|
+
setAddDraft((current) => ({
|
|
154
|
+
...current,
|
|
155
|
+
isSaving: false,
|
|
156
|
+
error: err instanceof Error ? err.message : "Failed to add workspace entry",
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
}, [addDraft, closeAddForm, createEntry, groups, setFeedback]);
|
|
160
|
+
|
|
161
|
+
const handleSaveEdit = useCallback(async () => {
|
|
162
|
+
if (!editingEntryId || !editDraft) return;
|
|
163
|
+
const name = editDraft.name.trim();
|
|
164
|
+
if (!name) {
|
|
165
|
+
setEditDraft((current) => (current ? { ...current, error: "Name is required" } : current));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
389
168
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
169
|
+
setEditDraft((current) => (current ? { ...current, isSaving: true, error: null } : current));
|
|
170
|
+
try {
|
|
171
|
+
await updateEntry(editingEntryId, {
|
|
172
|
+
name,
|
|
173
|
+
path: editDraft.path.trim() || null,
|
|
174
|
+
purpose: editDraft.purpose.trim() || null,
|
|
175
|
+
});
|
|
176
|
+
closeEditForm();
|
|
177
|
+
setFeedback("Workspace entry updated", "success");
|
|
178
|
+
} catch (err) {
|
|
179
|
+
setEditDraft((current) => ({
|
|
180
|
+
...(current ?? createEntryDraft("")),
|
|
181
|
+
isSaving: false,
|
|
182
|
+
error: err instanceof Error ? err.message : "Failed to update workspace entry",
|
|
183
|
+
}));
|
|
396
184
|
}
|
|
397
|
-
|
|
398
|
-
}, [name, analyzePath]);
|
|
185
|
+
}, [closeEditForm, editDraft, editingEntryId, setFeedback, updateEntry]);
|
|
399
186
|
|
|
400
|
-
const
|
|
401
|
-
|
|
187
|
+
const handleDeleteEntry = useCallback(async (entry: WorkspaceEntry) => {
|
|
188
|
+
if (!window.confirm(`Delete ${entry.name} from ${entry.category}?`)) return;
|
|
402
189
|
try {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
handleSelectPath(data.path);
|
|
190
|
+
await deleteEntry(entry.id);
|
|
191
|
+
if (editingEntryId === entry.id) {
|
|
192
|
+
closeEditForm();
|
|
407
193
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
194
|
+
setFeedback(`Deleted ${entry.name}`, "success");
|
|
195
|
+
} catch (err) {
|
|
196
|
+
setFeedback(err instanceof Error ? err.message : "Failed to delete workspace entry", "error");
|
|
197
|
+
}
|
|
198
|
+
}, [closeEditForm, deleteEntry, editingEntryId, setFeedback]);
|
|
411
199
|
|
|
412
|
-
const
|
|
413
|
-
if (!selectedPath || !name.trim()) return;
|
|
414
|
-
setSaving(true);
|
|
200
|
+
const handleBrowseForAdd = useCallback(async () => {
|
|
415
201
|
try {
|
|
416
|
-
await
|
|
417
|
-
|
|
418
|
-
|
|
202
|
+
const path = await pickFolder();
|
|
203
|
+
if (path) {
|
|
204
|
+
setAddDraft((current) => ({ ...current, path, error: null }));
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
setAddDraft((current) => ({
|
|
208
|
+
...current,
|
|
209
|
+
error: err instanceof Error ? err.message : "Failed to pick folder",
|
|
210
|
+
}));
|
|
419
211
|
}
|
|
420
|
-
}, [
|
|
421
|
-
|
|
422
|
-
return (
|
|
423
|
-
<div className="space-y-4">
|
|
424
|
-
<div className="flex items-center justify-between">
|
|
425
|
-
<h3 className="text-sm font-medium text-[var(--foreground)]">Add Folder</h3>
|
|
426
|
-
<button
|
|
427
|
-
type="button"
|
|
428
|
-
onClick={onCancel}
|
|
429
|
-
className="rounded-lg p-1 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
|
|
430
|
-
>
|
|
431
|
-
<X className="w-4 h-4" />
|
|
432
|
-
</button>
|
|
433
|
-
</div>
|
|
434
|
-
|
|
435
|
-
{/* Name input */}
|
|
436
|
-
<div className="space-y-1.5">
|
|
437
|
-
<label className="text-xs font-medium text-[var(--muted-foreground)]">Name</label>
|
|
438
|
-
<input
|
|
439
|
-
value={name}
|
|
440
|
-
onChange={(e) => setName(e.target.value)}
|
|
441
|
-
className="input w-full text-sm"
|
|
442
|
-
placeholder="Auto-detected from path"
|
|
443
|
-
/>
|
|
444
|
-
</div>
|
|
212
|
+
}, [pickFolder]);
|
|
445
213
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
<div className="rounded-lg border border-blue-500/20 bg-blue-500/5 px-3 py-2 flex items-center gap-2">
|
|
449
|
-
<FolderOpen className="w-4 h-4 text-blue-400 flex-shrink-0" />
|
|
450
|
-
<code className="text-xs text-blue-300 font-mono truncate flex-1">{selectedPath}</code>
|
|
451
|
-
<button
|
|
452
|
-
type="button"
|
|
453
|
-
onClick={() => { setSelectedPath(null); setAnalysis(null); }}
|
|
454
|
-
className="p-0.5 rounded text-blue-400/50 hover:text-blue-300 transition-colors flex-shrink-0"
|
|
455
|
-
>
|
|
456
|
-
<X className="w-3 h-3" />
|
|
457
|
-
</button>
|
|
458
|
-
</div>
|
|
459
|
-
)}
|
|
460
|
-
|
|
461
|
-
{/* Analysis preview */}
|
|
462
|
-
{analyzing && (
|
|
463
|
-
<div className="px-1 text-xs text-[var(--muted-foreground)] animate-pulse">Analyzing...</div>
|
|
464
|
-
)}
|
|
465
|
-
{!analyzing && analysis && <AnalysisSummary analysis={analysis} />}
|
|
466
|
-
|
|
467
|
-
{/* Directory browser or native picker */}
|
|
468
|
-
{!selectedPath && (
|
|
469
|
-
<div className="space-y-2">
|
|
470
|
-
<div className="flex items-center gap-2">
|
|
471
|
-
<span className="text-xs font-medium text-[var(--muted-foreground)]">Choose a folder</span>
|
|
472
|
-
<button
|
|
473
|
-
type="button"
|
|
474
|
-
onClick={() => void handleNativePick()}
|
|
475
|
-
disabled={pickingNative}
|
|
476
|
-
className="ml-auto inline-flex items-center gap-1.5 rounded-lg border border-[var(--border)] bg-[var(--secondary)] px-2.5 py-1 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:border-[var(--card-hover-border)] hover:text-[var(--foreground)] disabled:opacity-50"
|
|
477
|
-
>
|
|
478
|
-
<FolderSearch className="w-3 h-3" />
|
|
479
|
-
{pickingNative ? "Opening..." : "System Picker"}
|
|
480
|
-
</button>
|
|
481
|
-
</div>
|
|
482
|
-
<DirectoryBrowser
|
|
483
|
-
initialPath=""
|
|
484
|
-
onSelect={handleSelectPath}
|
|
485
|
-
onCancel={onCancel}
|
|
486
|
-
/>
|
|
487
|
-
</div>
|
|
488
|
-
)}
|
|
489
|
-
|
|
490
|
-
{/* Save action */}
|
|
491
|
-
{selectedPath && (
|
|
492
|
-
<div className="flex items-center gap-2 pt-1">
|
|
493
|
-
<button
|
|
494
|
-
type="button"
|
|
495
|
-
onClick={() => void handleSave()}
|
|
496
|
-
disabled={!name.trim() || saving}
|
|
497
|
-
className="btn-primary px-4 py-1.5 text-sm disabled:opacity-40"
|
|
498
|
-
>
|
|
499
|
-
{saving ? "Adding..." : "Add Folder"}
|
|
500
|
-
</button>
|
|
501
|
-
<button
|
|
502
|
-
type="button"
|
|
503
|
-
onClick={onCancel}
|
|
504
|
-
className="px-4 py-1.5 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
505
|
-
>
|
|
506
|
-
Cancel
|
|
507
|
-
</button>
|
|
508
|
-
</div>
|
|
509
|
-
)}
|
|
510
|
-
</div>
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/** Edit-folder panel: name + path with directory browser */
|
|
515
|
-
function EditFolderPanel({
|
|
516
|
-
repo,
|
|
517
|
-
onSave,
|
|
518
|
-
onCancel,
|
|
519
|
-
}: {
|
|
520
|
-
repo: ProjectRepo;
|
|
521
|
-
onSave: (name: string, path: string) => Promise<void>;
|
|
522
|
-
onCancel: () => void;
|
|
523
|
-
}) {
|
|
524
|
-
const [name, setName] = useState(repo.name);
|
|
525
|
-
const [selectedPath, setSelectedPath] = useState(repo.path ?? "");
|
|
526
|
-
const [showBrowser, setShowBrowser] = useState(false);
|
|
527
|
-
const [saving, setSaving] = useState(false);
|
|
528
|
-
const [pickingNative, setPickingNative] = useState(false);
|
|
529
|
-
|
|
530
|
-
const handleNativePick = useCallback(async () => {
|
|
531
|
-
setPickingNative(true);
|
|
214
|
+
const handleBrowseForEdit = useCallback(async () => {
|
|
215
|
+
if (!editDraft) return;
|
|
532
216
|
try {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
setSelectedPath(data.path);
|
|
537
|
-
setShowBrowser(false);
|
|
217
|
+
const path = await pickFolder();
|
|
218
|
+
if (path) {
|
|
219
|
+
setEditDraft((current) => (current ? { ...current, path, error: null } : current));
|
|
538
220
|
}
|
|
539
|
-
} catch {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
<button
|
|
548
|
-
type="button"
|
|
549
|
-
onClick={onCancel}
|
|
550
|
-
className="rounded-lg p-1 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--secondary)] hover:text-[var(--foreground)]"
|
|
551
|
-
>
|
|
552
|
-
<X className="w-4 h-4" />
|
|
553
|
-
</button>
|
|
554
|
-
</div>
|
|
221
|
+
} catch (err) {
|
|
222
|
+
setEditDraft((current) => (
|
|
223
|
+
current
|
|
224
|
+
? { ...current, error: err instanceof Error ? err.message : "Failed to pick folder" }
|
|
225
|
+
: current
|
|
226
|
+
));
|
|
227
|
+
}
|
|
228
|
+
}, [editDraft, pickFolder]);
|
|
555
229
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
230
|
+
const handleQuickBrowse = useCallback(async (entry: WorkspaceEntry) => {
|
|
231
|
+
try {
|
|
232
|
+
const path = await pickFolder();
|
|
233
|
+
if (!path) return;
|
|
234
|
+
await updateEntry(entry.id, { path });
|
|
235
|
+
setFeedback(`Updated path for ${entry.name}`, "success");
|
|
236
|
+
} catch (err) {
|
|
237
|
+
setFeedback(err instanceof Error ? err.message : "Failed to update path", "error");
|
|
238
|
+
}
|
|
239
|
+
}, [pickFolder, setFeedback, updateEntry]);
|
|
566
240
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
<div className="flex items-center gap-1.5">
|
|
571
|
-
<button
|
|
572
|
-
type="button"
|
|
573
|
-
onClick={() => setShowBrowser(!showBrowser)}
|
|
574
|
-
className="text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
575
|
-
>
|
|
576
|
-
{showBrowser ? "Type path" : "Browse"}
|
|
577
|
-
</button>
|
|
578
|
-
<button
|
|
579
|
-
type="button"
|
|
580
|
-
onClick={() => void handleNativePick()}
|
|
581
|
-
disabled={pickingNative}
|
|
582
|
-
className="inline-flex items-center gap-1 text-xs text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)] disabled:opacity-50"
|
|
583
|
-
>
|
|
584
|
-
<FolderSearch className="w-3 h-3" />
|
|
585
|
-
{pickingNative ? "Opening..." : "System Picker"}
|
|
586
|
-
</button>
|
|
587
|
-
</div>
|
|
588
|
-
</div>
|
|
589
|
-
{showBrowser ? (
|
|
590
|
-
<DirectoryBrowser
|
|
591
|
-
initialPath={selectedPath}
|
|
592
|
-
onSelect={(path) => { setSelectedPath(path); setShowBrowser(false); }}
|
|
593
|
-
onCancel={() => setShowBrowser(false)}
|
|
594
|
-
/>
|
|
595
|
-
) : (
|
|
596
|
-
<input
|
|
597
|
-
value={selectedPath}
|
|
598
|
-
onChange={(e) => setSelectedPath(e.target.value)}
|
|
599
|
-
className="input w-full text-sm font-mono"
|
|
600
|
-
placeholder="/path/to/folder"
|
|
601
|
-
/>
|
|
602
|
-
)}
|
|
603
|
-
</div>
|
|
241
|
+
const handleCreateCategory = useCallback(() => {
|
|
242
|
+
const nextCategory = categoryDraft.trim();
|
|
243
|
+
if (!nextCategory) return;
|
|
604
244
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
setSaving(true);
|
|
611
|
-
try { await onSave(name.trim(), selectedPath.trim()); }
|
|
612
|
-
finally { setSaving(false); }
|
|
613
|
-
}}
|
|
614
|
-
disabled={!name.trim() || !selectedPath.trim() || saving}
|
|
615
|
-
className="btn-primary px-4 py-1.5 text-sm disabled:opacity-40"
|
|
616
|
-
>
|
|
617
|
-
{saving ? "Saving..." : "Save"}
|
|
618
|
-
</button>
|
|
619
|
-
<button
|
|
620
|
-
type="button"
|
|
621
|
-
onClick={onCancel}
|
|
622
|
-
className="px-4 py-1.5 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
623
|
-
>
|
|
624
|
-
Cancel
|
|
625
|
-
</button>
|
|
626
|
-
</div>
|
|
627
|
-
</div>
|
|
628
|
-
);
|
|
629
|
-
}
|
|
245
|
+
const existingGroup = groups.find(
|
|
246
|
+
(group) =>
|
|
247
|
+
group.id.toLowerCase() === nextCategory.toLowerCase() ||
|
|
248
|
+
group.label.toLowerCase() === nextCategory.toLowerCase(),
|
|
249
|
+
);
|
|
630
250
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const [addingRepo, setAddingRepo] = useState(false);
|
|
637
|
-
const [editingRepo, setEditingRepo] = useState<ProjectRepo | null>(null);
|
|
638
|
-
const [repoStatus, setRepoStatus] = useState<string | null>(null);
|
|
639
|
-
const [noteSaving, setNoteSaving] = useState(false);
|
|
640
|
-
const [systemNotes, setSystemNotes] = useState<Record<string, SystemNote | null>>({});
|
|
641
|
-
const [analyses, setAnalyses] = useState<Record<string, RepoAnalysis>>({});
|
|
642
|
-
const [analysisLoading, setAnalysisLoading] = useState<Record<string, boolean>>({});
|
|
643
|
-
|
|
644
|
-
const repos = project?.repos ?? [];
|
|
645
|
-
const selectedRepo = repos.find((r) => r.id === selectedRepoId) ?? null;
|
|
646
|
-
|
|
647
|
-
// Auto-select first folder
|
|
648
|
-
useEffect(() => {
|
|
649
|
-
if (!selectedRepoId && repos.length > 0) {
|
|
650
|
-
setSelectedRepoId(repos[0].id);
|
|
651
|
-
}
|
|
652
|
-
if (selectedRepoId && !repos.find((r) => r.id === selectedRepoId) && repos.length > 0) {
|
|
653
|
-
setSelectedRepoId(repos[0].id);
|
|
251
|
+
if (existingGroup) {
|
|
252
|
+
setCategoryDraft("");
|
|
253
|
+
setIsCreatingCategory(false);
|
|
254
|
+
openAddForm(existingGroup.id);
|
|
255
|
+
return;
|
|
654
256
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const data = res.ok ? await res.json() : { note: null };
|
|
667
|
-
return [repo.id, mapNote(data.note)] as const;
|
|
668
|
-
})
|
|
669
|
-
);
|
|
670
|
-
if (!cancelled) setSystemNotes(Object.fromEntries(entries));
|
|
671
|
-
} catch {
|
|
672
|
-
if (!cancelled) setSystemNotes({});
|
|
673
|
-
}
|
|
257
|
+
|
|
258
|
+
setCustomCategories((current) => [...current, nextCategory]);
|
|
259
|
+
setCategoryDraft("");
|
|
260
|
+
setIsCreatingCategory(false);
|
|
261
|
+
openAddForm(nextCategory);
|
|
262
|
+
}, [categoryDraft, groups, openAddForm]);
|
|
263
|
+
|
|
264
|
+
const handleRemoveCustomCategory = useCallback((categoryId: string) => {
|
|
265
|
+
setCustomCategories((current) => current.filter((category) => category !== categoryId));
|
|
266
|
+
if (addingCategoryId === categoryId) {
|
|
267
|
+
closeAddForm();
|
|
674
268
|
}
|
|
675
|
-
|
|
676
|
-
return () => { cancelled = true; };
|
|
677
|
-
}, [repos]);
|
|
678
|
-
|
|
679
|
-
// Analyze selected repo on selection
|
|
680
|
-
useEffect(() => {
|
|
681
|
-
if (!selectedRepo?.path || analyses[selectedRepo.id]) return;
|
|
682
|
-
const repoId = selectedRepo.id;
|
|
683
|
-
const repoPath = selectedRepo.path;
|
|
684
|
-
setAnalysisLoading((prev) => ({ ...prev, [repoId]: true }));
|
|
685
|
-
fetch("/api/filesystem/analyze", {
|
|
686
|
-
method: "POST",
|
|
687
|
-
headers: { "Content-Type": "application/json" },
|
|
688
|
-
body: JSON.stringify({ path: repoPath }),
|
|
689
|
-
})
|
|
690
|
-
.then((res) => (res.ok ? res.json() : null))
|
|
691
|
-
.then((data) => {
|
|
692
|
-
if (data?.analysis) {
|
|
693
|
-
setAnalyses((prev) => ({ ...prev, [repoId]: data.analysis }));
|
|
694
|
-
}
|
|
695
|
-
})
|
|
696
|
-
.catch(() => {})
|
|
697
|
-
.finally(() => setAnalysisLoading((prev) => ({ ...prev, [repoId]: false })));
|
|
698
|
-
}, [selectedRepo?.id, selectedRepo?.path, analyses]);
|
|
699
|
-
|
|
700
|
-
const setFeedback = useCallback((msg: string) => {
|
|
701
|
-
setRepoStatus(msg);
|
|
702
|
-
setTimeout(() => setRepoStatus(null), 2500);
|
|
703
|
-
}, []);
|
|
269
|
+
}, [addingCategoryId, closeAddForm]);
|
|
704
270
|
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
);
|
|
271
|
+
const handleExportYaml = useCallback(() => {
|
|
272
|
+
window.open(`/api/projects/${projectId}/workspace/export`, "_blank");
|
|
273
|
+
}, [projectId]);
|
|
709
274
|
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
setAddingRepo(false);
|
|
714
|
-
setFeedback("Folder added");
|
|
715
|
-
} catch { setFeedback("Failed to add"); }
|
|
716
|
-
}, [updateProject, projectId, toInputs, setFeedback]);
|
|
717
|
-
|
|
718
|
-
const handleEditSave = useCallback(async (name: string, path: string) => {
|
|
719
|
-
if (!editingRepo) return;
|
|
720
|
-
const updated = toInputs().map((r) =>
|
|
721
|
-
r.id === editingRepo.id ? { ...r, name, path } : r
|
|
722
|
-
);
|
|
723
|
-
try {
|
|
724
|
-
await updateProject(projectId, { repos: updated });
|
|
725
|
-
setEditingRepo(null);
|
|
726
|
-
// Invalidate analysis cache for this repo since path may have changed
|
|
727
|
-
setAnalyses((prev) => {
|
|
728
|
-
const next = { ...prev };
|
|
729
|
-
delete next[editingRepo.id];
|
|
730
|
-
return next;
|
|
731
|
-
});
|
|
732
|
-
setFeedback("Folder updated");
|
|
733
|
-
} catch { setFeedback("Failed to update"); }
|
|
734
|
-
}, [editingRepo, updateProject, projectId, toInputs, setFeedback]);
|
|
275
|
+
const handleImportYaml = useCallback(async (event: ChangeEvent<HTMLInputElement>) => {
|
|
276
|
+
const file = event.target.files?.[0];
|
|
277
|
+
if (!file) return;
|
|
735
278
|
|
|
736
|
-
const handleDelete = useCallback(async (repoId: string) => {
|
|
737
279
|
try {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
setFeedback("
|
|
752
|
-
} catch {
|
|
753
|
-
setFeedback("Failed to save notes");
|
|
280
|
+
const content = await file.text();
|
|
281
|
+
const response = await fetch(`/api/projects/${projectId}/workspace/import`, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: { "Content-Type": "application/x-yaml" },
|
|
284
|
+
body: content,
|
|
285
|
+
});
|
|
286
|
+
if (!response.ok) {
|
|
287
|
+
const data = await response.json().catch(() => ({}));
|
|
288
|
+
throw new Error(typeof data.error === "string" ? data.error : "Failed to import workspace YAML");
|
|
289
|
+
}
|
|
290
|
+
await refetch();
|
|
291
|
+
setFeedback("Imported workspace YAML", "success");
|
|
292
|
+
} catch (err) {
|
|
293
|
+
setFeedback(err instanceof Error ? err.message : "Failed to import workspace YAML", "error");
|
|
754
294
|
} finally {
|
|
755
|
-
|
|
295
|
+
if (importInputRef.current) importInputRef.current.value = "";
|
|
756
296
|
}
|
|
757
|
-
}, [
|
|
758
|
-
|
|
759
|
-
if (!project) {
|
|
760
|
-
return (
|
|
761
|
-
<div className="flex h-full items-center justify-center text-sm text-[var(--muted-foreground)]">
|
|
762
|
-
Loading...
|
|
763
|
-
</div>
|
|
764
|
-
);
|
|
765
|
-
}
|
|
297
|
+
}, [projectId, refetch, setFeedback]);
|
|
766
298
|
|
|
767
299
|
return (
|
|
768
|
-
<div className="h-full
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
className="
|
|
785
|
-
title="Add folder"
|
|
786
|
-
>
|
|
787
|
-
<Plus className="w-4 h-4" />
|
|
788
|
-
</button>
|
|
789
|
-
</div>
|
|
790
|
-
|
|
791
|
-
<div className="flex-1 overflow-y-auto px-2 py-2 space-y-0.5">
|
|
792
|
-
{repos.length === 0 && !addingRepo && (
|
|
793
|
-
<div className="px-3 py-8 text-center">
|
|
794
|
-
<FolderGit2 className="mx-auto mb-3 h-8 w-8 text-[var(--app-shell-soft-text)]" />
|
|
795
|
-
<p className="mb-1 text-sm text-[var(--muted-foreground)]">No folders yet</p>
|
|
796
|
-
<p className="mb-4 text-xs text-[var(--app-shell-soft-text)]">Link local repos or project directories</p>
|
|
300
|
+
<div className="h-full overflow-y-auto">
|
|
301
|
+
<div className="mx-auto flex max-w-5xl flex-col gap-6 px-6 py-8">
|
|
302
|
+
<section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-6 shadow-sm">
|
|
303
|
+
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
304
|
+
<div className="max-w-2xl">
|
|
305
|
+
<div className="mb-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.2em] text-[var(--muted-foreground)]">
|
|
306
|
+
<FolderGit2 className="h-3.5 w-3.5" />
|
|
307
|
+
Project Setup
|
|
308
|
+
</div>
|
|
309
|
+
<h1 className="text-2xl font-semibold text-[var(--foreground)]">Workspace Map</h1>
|
|
310
|
+
<p className="mt-2 text-sm leading-6 text-[var(--muted-foreground)]">
|
|
311
|
+
Map your project's folders so agents know where things live. Group repositories, docs, config, and
|
|
312
|
+
scripts into a workspace that people and automation can navigate without guesswork.
|
|
313
|
+
</p>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
797
317
|
<button
|
|
798
318
|
type="button"
|
|
799
|
-
onClick={() =>
|
|
800
|
-
className="inline-flex items-center gap-
|
|
319
|
+
onClick={() => setIsCreatingCategory((current) => !current)}
|
|
320
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
801
321
|
>
|
|
802
|
-
<
|
|
803
|
-
Add
|
|
322
|
+
<FolderPlus className="h-4 w-4" />
|
|
323
|
+
Add Category
|
|
804
324
|
</button>
|
|
325
|
+
<button
|
|
326
|
+
type="button"
|
|
327
|
+
onClick={handleExportYaml}
|
|
328
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
329
|
+
>
|
|
330
|
+
<Download className="h-4 w-4" />
|
|
331
|
+
Export YAML
|
|
332
|
+
</button>
|
|
333
|
+
<label className="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]">
|
|
334
|
+
<Upload className="h-4 w-4" />
|
|
335
|
+
Import YAML
|
|
336
|
+
<input
|
|
337
|
+
ref={importInputRef}
|
|
338
|
+
type="file"
|
|
339
|
+
accept=".yaml,.yml"
|
|
340
|
+
onChange={handleImportYaml}
|
|
341
|
+
className="hidden"
|
|
342
|
+
/>
|
|
343
|
+
</label>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
{isCreatingCategory && (
|
|
348
|
+
<div className="mt-4 flex flex-col gap-2 rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/30 p-4 md:flex-row md:items-center">
|
|
349
|
+
<input
|
|
350
|
+
value={categoryDraft}
|
|
351
|
+
onChange={(event) => setCategoryDraft(event.target.value)}
|
|
352
|
+
placeholder="Custom category name"
|
|
353
|
+
className="flex-1 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
|
|
354
|
+
/>
|
|
355
|
+
<div className="flex items-center gap-2">
|
|
356
|
+
<button
|
|
357
|
+
type="button"
|
|
358
|
+
onClick={handleCreateCategory}
|
|
359
|
+
disabled={!categoryDraft.trim()}
|
|
360
|
+
className="rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
|
|
361
|
+
>
|
|
362
|
+
Create Category
|
|
363
|
+
</button>
|
|
364
|
+
<button
|
|
365
|
+
type="button"
|
|
366
|
+
onClick={() => {
|
|
367
|
+
setIsCreatingCategory(false);
|
|
368
|
+
setCategoryDraft("");
|
|
369
|
+
}}
|
|
370
|
+
className="rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
371
|
+
>
|
|
372
|
+
Cancel
|
|
373
|
+
</button>
|
|
374
|
+
</div>
|
|
805
375
|
</div>
|
|
806
376
|
)}
|
|
807
377
|
|
|
808
|
-
{
|
|
809
|
-
<
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
378
|
+
{status && (
|
|
379
|
+
<p
|
|
380
|
+
className={`mt-4 text-sm ${
|
|
381
|
+
status.tone === "error"
|
|
382
|
+
? "text-[var(--destructive)]"
|
|
383
|
+
: status.tone === "success"
|
|
384
|
+
? "text-emerald-500"
|
|
385
|
+
: "text-[var(--muted-foreground)]"
|
|
386
|
+
}`}
|
|
387
|
+
>
|
|
388
|
+
{status.message}
|
|
389
|
+
</p>
|
|
390
|
+
)}
|
|
391
|
+
</section>
|
|
392
|
+
|
|
393
|
+
{isLoading && (
|
|
394
|
+
<section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-8">
|
|
395
|
+
<div className="flex items-center gap-3 text-sm text-[var(--muted-foreground)]">
|
|
396
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
397
|
+
Loading workspace map...
|
|
398
|
+
</div>
|
|
399
|
+
</section>
|
|
400
|
+
)}
|
|
401
|
+
|
|
402
|
+
{!isLoading && error && (
|
|
403
|
+
<section className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-8">
|
|
404
|
+
<div className="space-y-3">
|
|
405
|
+
<p className="text-sm text-[var(--destructive)]">{error.message || "Failed to load workspace map"}</p>
|
|
406
|
+
<button
|
|
407
|
+
type="button"
|
|
408
|
+
onClick={() => void refetch()}
|
|
409
|
+
className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
410
|
+
>
|
|
411
|
+
Try Again
|
|
412
|
+
</button>
|
|
413
|
+
</div>
|
|
414
|
+
</section>
|
|
822
415
|
)}
|
|
823
|
-
</div>
|
|
824
416
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
417
|
+
{!isLoading && !error && showEmptyState && (
|
|
418
|
+
<section className="rounded-3xl border border-dashed border-[var(--border)] bg-[var(--card-bg)] p-10 text-center">
|
|
419
|
+
<div className="mx-auto max-w-2xl">
|
|
420
|
+
<h2 className="text-xl font-semibold text-[var(--foreground)]">Tell your agents where things live.</h2>
|
|
421
|
+
<p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">
|
|
422
|
+
Add folders to your workspace so agents can find and work in the right places. Start with a repository,
|
|
423
|
+
docs, or config, or import a shared workspace definition.
|
|
424
|
+
</p>
|
|
425
|
+
<div className="mt-6 flex flex-wrap items-center justify-center gap-3">
|
|
426
|
+
{DEFAULT_WORKSPACE_CATEGORIES.slice(0, 3).map((category) => {
|
|
427
|
+
const Icon = getCategoryIcon(category.id);
|
|
428
|
+
return (
|
|
429
|
+
<button
|
|
430
|
+
key={category.id}
|
|
431
|
+
type="button"
|
|
432
|
+
onClick={() => openAddForm(category.id)}
|
|
433
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-4 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
434
|
+
>
|
|
435
|
+
<Icon className="h-4 w-4" />
|
|
436
|
+
Add {getCategoryEntryLabel(category.id, category.label)}
|
|
437
|
+
</button>
|
|
438
|
+
);
|
|
439
|
+
})}
|
|
440
|
+
<label className="inline-flex cursor-pointer items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-4 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]">
|
|
441
|
+
<Upload className="h-4 w-4" />
|
|
442
|
+
Import YAML
|
|
443
|
+
<input
|
|
444
|
+
type="file"
|
|
445
|
+
accept=".yaml,.yml"
|
|
446
|
+
onChange={handleImportYaml}
|
|
447
|
+
className="hidden"
|
|
448
|
+
/>
|
|
449
|
+
</label>
|
|
450
|
+
</div>
|
|
850
451
|
</div>
|
|
851
|
-
|
|
852
|
-
|
|
452
|
+
</section>
|
|
453
|
+
)}
|
|
454
|
+
|
|
455
|
+
{!isLoading && !error && !showEmptyState && (
|
|
456
|
+
<div className="space-y-4">
|
|
457
|
+
{groups.map((group) => {
|
|
458
|
+
const Icon = getCategoryIcon(group.id);
|
|
459
|
+
|
|
460
|
+
return (
|
|
461
|
+
<section
|
|
462
|
+
key={group.id}
|
|
463
|
+
className="rounded-3xl border border-[var(--card-border)] bg-[var(--card-bg)] p-6"
|
|
464
|
+
>
|
|
465
|
+
<div className="flex flex-col gap-4 border-b border-[var(--border)] pb-4 md:flex-row md:items-start md:justify-between">
|
|
466
|
+
<div className="flex items-start gap-3">
|
|
467
|
+
<div className="rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/40 p-3">
|
|
468
|
+
<Icon className="h-5 w-5 text-[var(--muted-foreground)]" />
|
|
469
|
+
</div>
|
|
470
|
+
<div>
|
|
471
|
+
<h2 className="text-lg font-semibold text-[var(--foreground)]">{group.label}</h2>
|
|
472
|
+
<p className="text-sm text-[var(--muted-foreground)]">
|
|
473
|
+
{group.entries.length > 0
|
|
474
|
+
? `${group.entries.length} location${group.entries.length === 1 ? "" : "s"} mapped`
|
|
475
|
+
: group.isPreset
|
|
476
|
+
? `No ${group.label.toLowerCase()} mapped yet`
|
|
477
|
+
: "Custom category ready for entries"}
|
|
478
|
+
</p>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
482
|
+
{!group.isPreset && group.isEmpty && (
|
|
483
|
+
<button
|
|
484
|
+
type="button"
|
|
485
|
+
onClick={() => handleRemoveCustomCategory(group.id)}
|
|
486
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--destructive)]"
|
|
487
|
+
>
|
|
488
|
+
<Trash2 className="h-4 w-4" />
|
|
489
|
+
Remove Empty Category
|
|
490
|
+
</button>
|
|
491
|
+
)}
|
|
492
|
+
<button
|
|
493
|
+
type="button"
|
|
494
|
+
onClick={() => openAddForm(group.id)}
|
|
495
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
496
|
+
>
|
|
497
|
+
<Plus className="h-4 w-4" />
|
|
498
|
+
Add to {group.label}
|
|
499
|
+
</button>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
|
|
503
|
+
<div className="mt-4 space-y-3">
|
|
504
|
+
{group.entries.length === 0 && addingCategoryId !== group.id && (
|
|
505
|
+
<p className="rounded-2xl border border-dashed border-[var(--border)] px-4 py-5 text-sm text-[var(--muted-foreground)]">
|
|
506
|
+
{group.isPreset
|
|
507
|
+
? `Start this section by adding a ${getCategoryEntryLabel(group.id, group.label).toLowerCase()} and selecting a folder on disk.`
|
|
508
|
+
: "This custom category is empty until you add its first folder."}
|
|
509
|
+
</p>
|
|
510
|
+
)}
|
|
511
|
+
|
|
512
|
+
{group.entries.map((entry) => {
|
|
513
|
+
const isEditing = editingEntryId === entry.id && editDraft;
|
|
514
|
+
return (
|
|
515
|
+
<div
|
|
516
|
+
key={entry.id}
|
|
517
|
+
className="rounded-2xl border border-[var(--border)] bg-[var(--secondary)]/20 p-4"
|
|
518
|
+
>
|
|
519
|
+
{!isEditing && (
|
|
520
|
+
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
521
|
+
<div className="min-w-0 flex-1">
|
|
522
|
+
<div className="flex items-center gap-2">
|
|
523
|
+
<p className="text-base font-medium text-[var(--foreground)]">{entry.name}</p>
|
|
524
|
+
</div>
|
|
525
|
+
<p className="mt-2 break-all font-mono text-xs text-[var(--muted-foreground)]">
|
|
526
|
+
{entry.path || "No path selected yet"}
|
|
527
|
+
</p>
|
|
528
|
+
<p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">
|
|
529
|
+
{entry.purpose || "No purpose documented yet."}
|
|
530
|
+
</p>
|
|
531
|
+
</div>
|
|
532
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
533
|
+
<button
|
|
534
|
+
type="button"
|
|
535
|
+
onClick={() => void handleQuickBrowse(entry)}
|
|
536
|
+
className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
537
|
+
>
|
|
538
|
+
Browse
|
|
539
|
+
</button>
|
|
540
|
+
<button
|
|
541
|
+
type="button"
|
|
542
|
+
onClick={() => openEditForm(entry)}
|
|
543
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
544
|
+
>
|
|
545
|
+
<Pencil className="h-4 w-4" />
|
|
546
|
+
Edit
|
|
547
|
+
</button>
|
|
548
|
+
<button
|
|
549
|
+
type="button"
|
|
550
|
+
onClick={() => void handleDeleteEntry(entry)}
|
|
551
|
+
className="inline-flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--destructive)]"
|
|
552
|
+
>
|
|
553
|
+
<Trash2 className="h-4 w-4" />
|
|
554
|
+
Delete
|
|
555
|
+
</button>
|
|
556
|
+
</div>
|
|
557
|
+
</div>
|
|
558
|
+
)}
|
|
559
|
+
|
|
560
|
+
{isEditing && editDraft && (
|
|
561
|
+
<div className="space-y-3">
|
|
562
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
563
|
+
<label className="block">
|
|
564
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
565
|
+
Name
|
|
566
|
+
</span>
|
|
567
|
+
<input
|
|
568
|
+
value={editDraft.name}
|
|
569
|
+
onChange={(event) =>
|
|
570
|
+
setEditDraft((current) =>
|
|
571
|
+
current ? { ...current, name: event.target.value, error: null } : current,
|
|
572
|
+
)
|
|
573
|
+
}
|
|
574
|
+
className="w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
|
|
575
|
+
/>
|
|
576
|
+
</label>
|
|
577
|
+
<label className="block">
|
|
578
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
579
|
+
Path
|
|
580
|
+
</span>
|
|
581
|
+
<div className="flex items-center gap-2">
|
|
582
|
+
<input
|
|
583
|
+
value={editDraft.path}
|
|
584
|
+
onChange={(event) =>
|
|
585
|
+
setEditDraft((current) =>
|
|
586
|
+
current ? { ...current, path: event.target.value, error: null } : current,
|
|
587
|
+
)
|
|
588
|
+
}
|
|
589
|
+
className="w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)]"
|
|
590
|
+
placeholder="/Users/you/Projects/repo"
|
|
591
|
+
/>
|
|
592
|
+
<button
|
|
593
|
+
type="button"
|
|
594
|
+
onClick={() => void handleBrowseForEdit()}
|
|
595
|
+
className="rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
596
|
+
>
|
|
597
|
+
Browse
|
|
598
|
+
</button>
|
|
599
|
+
</div>
|
|
600
|
+
</label>
|
|
601
|
+
</div>
|
|
602
|
+
<label className="block">
|
|
603
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
604
|
+
Purpose
|
|
605
|
+
</span>
|
|
606
|
+
<textarea
|
|
607
|
+
value={editDraft.purpose}
|
|
608
|
+
onChange={(event) =>
|
|
609
|
+
setEditDraft((current) =>
|
|
610
|
+
current ? { ...current, purpose: event.target.value, error: null } : current,
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
className="min-h-24 w-full rounded-xl border border-[var(--border)] bg-[var(--background)] px-3 py-3 text-sm text-[var(--foreground)]"
|
|
614
|
+
placeholder="What should agents or teammates expect in this folder?"
|
|
615
|
+
/>
|
|
616
|
+
</label>
|
|
617
|
+
{editDraft.error && (
|
|
618
|
+
<p className="text-sm text-[var(--destructive)]">{editDraft.error}</p>
|
|
619
|
+
)}
|
|
620
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
621
|
+
<button
|
|
622
|
+
type="button"
|
|
623
|
+
onClick={() => void handleSaveEdit()}
|
|
624
|
+
disabled={editDraft.isSaving}
|
|
625
|
+
className="inline-flex items-center gap-2 rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
|
|
626
|
+
>
|
|
627
|
+
{editDraft.isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
|
628
|
+
Save Changes
|
|
629
|
+
</button>
|
|
630
|
+
<button
|
|
631
|
+
type="button"
|
|
632
|
+
onClick={closeEditForm}
|
|
633
|
+
className="inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
634
|
+
>
|
|
635
|
+
<X className="h-4 w-4" />
|
|
636
|
+
Cancel
|
|
637
|
+
</button>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
)}
|
|
641
|
+
</div>
|
|
642
|
+
);
|
|
643
|
+
})}
|
|
644
|
+
|
|
645
|
+
{addingCategoryId === group.id && (
|
|
646
|
+
<div className="rounded-2xl border border-[var(--border)] bg-[var(--background)] p-4">
|
|
647
|
+
<div className="mb-3 flex items-center gap-2">
|
|
648
|
+
<Plus className="h-4 w-4 text-[var(--muted-foreground)]" />
|
|
649
|
+
<p className="text-sm font-medium text-[var(--foreground)]">Add to {group.label}</p>
|
|
650
|
+
</div>
|
|
651
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
652
|
+
<label className="block">
|
|
653
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
654
|
+
Name
|
|
655
|
+
</span>
|
|
656
|
+
<input
|
|
657
|
+
value={addDraft.name}
|
|
658
|
+
onChange={(event) =>
|
|
659
|
+
setAddDraft((current) => ({ ...current, name: event.target.value, error: null }))
|
|
660
|
+
}
|
|
661
|
+
className="w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)]"
|
|
662
|
+
placeholder="backend, handbook, deploy"
|
|
663
|
+
/>
|
|
664
|
+
</label>
|
|
665
|
+
<label className="block">
|
|
666
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
667
|
+
Path
|
|
668
|
+
</span>
|
|
669
|
+
<div className="flex items-center gap-2">
|
|
670
|
+
<input
|
|
671
|
+
value={addDraft.path}
|
|
672
|
+
onChange={(event) =>
|
|
673
|
+
setAddDraft((current) => ({ ...current, path: event.target.value, error: null }))
|
|
674
|
+
}
|
|
675
|
+
className="w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)]"
|
|
676
|
+
placeholder="/Users/you/Projects/repo"
|
|
677
|
+
/>
|
|
678
|
+
<button
|
|
679
|
+
type="button"
|
|
680
|
+
onClick={() => void handleBrowseForAdd()}
|
|
681
|
+
className="rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-2 text-sm text-[var(--foreground)] transition-colors hover:bg-[var(--secondary)]"
|
|
682
|
+
>
|
|
683
|
+
Browse
|
|
684
|
+
</button>
|
|
685
|
+
</div>
|
|
686
|
+
</label>
|
|
687
|
+
</div>
|
|
688
|
+
<label className="mt-3 block">
|
|
689
|
+
<span className="mb-1 block text-[11px] font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
|
|
690
|
+
Purpose
|
|
691
|
+
</span>
|
|
692
|
+
<textarea
|
|
693
|
+
value={addDraft.purpose}
|
|
694
|
+
onChange={(event) =>
|
|
695
|
+
setAddDraft((current) => ({ ...current, purpose: event.target.value, error: null }))
|
|
696
|
+
}
|
|
697
|
+
className="min-h-24 w-full rounded-xl border border-[var(--border)] bg-[var(--card-bg)] px-3 py-3 text-sm text-[var(--foreground)]"
|
|
698
|
+
placeholder="Describe what lives here and why it matters."
|
|
699
|
+
/>
|
|
700
|
+
</label>
|
|
701
|
+
{addDraft.error && (
|
|
702
|
+
<p className="mt-3 text-sm text-[var(--destructive)]">{addDraft.error}</p>
|
|
703
|
+
)}
|
|
704
|
+
<div className="mt-4 flex flex-wrap items-center gap-2">
|
|
705
|
+
<button
|
|
706
|
+
type="button"
|
|
707
|
+
onClick={() => void handleAddEntry()}
|
|
708
|
+
disabled={addDraft.isSaving}
|
|
709
|
+
className="inline-flex items-center gap-2 rounded-xl bg-[var(--foreground)] px-4 py-2 text-sm font-medium text-[var(--background)] disabled:opacity-40"
|
|
710
|
+
>
|
|
711
|
+
{addDraft.isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
|
712
|
+
Save Entry
|
|
713
|
+
</button>
|
|
714
|
+
<button
|
|
715
|
+
type="button"
|
|
716
|
+
onClick={closeAddForm}
|
|
717
|
+
className="inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
|
718
|
+
>
|
|
719
|
+
<X className="h-4 w-4" />
|
|
720
|
+
Cancel
|
|
721
|
+
</button>
|
|
722
|
+
</div>
|
|
723
|
+
</div>
|
|
724
|
+
)}
|
|
725
|
+
</div>
|
|
726
|
+
</section>
|
|
727
|
+
);
|
|
728
|
+
})}
|
|
729
|
+
</div>
|
|
730
|
+
)}
|
|
853
731
|
</div>
|
|
854
732
|
</div>
|
|
855
733
|
);
|