manigent-server 0.1.0__py3-none-any.whl
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.
- manigent/_catalog/mcps/chess/README.md +38 -0
- manigent/_catalog/mcps/chess/mcp.json +9 -0
- manigent/_catalog/mcps/registry.json +9 -0
- manigent/_catalog/skills/brief/SKILL.md +339 -0
- manigent/_catalog/skills/broaden-options/SKILL.md +133 -0
- manigent/_catalog/skills/calendar/SKILL.md +44 -0
- manigent/_catalog/skills/calendar/starter_config.jsonc +25 -0
- manigent/_catalog/skills/gmail/SKILL.md +73 -0
- manigent/_catalog/skills/google-calendar/SKILL.md +44 -0
- manigent/_catalog/skills/google-drive/SKILL.md +42 -0
- manigent/_catalog/skills/google-drive-write/SKILL.md +45 -0
- manigent/_catalog/skills/google-drive-write/starter_config.jsonc +45 -0
- manigent/_catalog/skills/grill-me/SKILL.md +173 -0
- manigent/_catalog/skills/homekit/SKILL.md +37 -0
- manigent/_catalog/skills/http-api/SKILL.md +117 -0
- manigent/_catalog/skills/ib/SKILL.md +89 -0
- manigent/_catalog/skills/ib/starter_config.jsonc +35 -0
- manigent/_catalog/skills/mailbox/SKILL.md +47 -0
- manigent/_catalog/skills/mailbox/starter_config.jsonc +38 -0
- manigent/_catalog/skills/media/SKILL.md +93 -0
- manigent/_catalog/skills/media/starter_config.jsonc +63 -0
- manigent/_catalog/skills/osxphotos/SKILL.md +41 -0
- manigent/_catalog/skills/photo-story/SKILL.md +285 -0
- manigent/_catalog/skills/playwright-browser/SKILL.md +291 -0
- manigent/_catalog/skills/playwright-browser/starter_config.jsonc +44 -0
- manigent/_catalog/skills/playwright-save-skill/SKILL.md +149 -0
- manigent/_catalog/skills/registry.json +166 -0
- manigent/_catalog/skills/web-artifacts/SKILL.md +194 -0
- manigent/api/action_executor.py +404 -0
- manigent/api/action_validator.py +531 -0
- manigent/api/actions.py +221 -0
- manigent/api/client.py +558 -0
- manigent/api/control.py +806 -0
- manigent/api/edge_protocol.py +123 -0
- manigent/api/fs_protocol.py +450 -0
- manigent/api/host_protocol.py +298 -0
- manigent/api/managed_job_client.py +124 -0
- manigent/api/responses.py +342 -0
- manigent/api/retry_transport.py +178 -0
- manigent/api/turn_protocol.py +78 -0
- manigent/cli_lifecycle.py +467 -0
- manigent/cli_server.py +978 -0
- manigent/doctor.py +1167 -0
- manigent/hub_protocol/canonical.py +375 -0
- manigent/hub_protocol/codes.py +171 -0
- manigent/hub_protocol/constants.py +71 -0
- manigent/hub_protocol/envelope.py +218 -0
- manigent/hub_protocol/frames.py +788 -0
- manigent/hub_protocol/http.py +260 -0
- manigent/hub_protocol/ids.py +170 -0
- manigent/hub_protocol/keys.py +195 -0
- manigent/hub_protocol/text.py +75 -0
- manigent/llm/base.py +1072 -0
- manigent/llm/prompt_builder.py +2450 -0
- manigent/llm/triggers.py +68 -0
- manigent/models/agent.py +2905 -0
- manigent/models/agent_bundle.py +298 -0
- manigent/models/brief_tab.py +311 -0
- manigent/models/chat_message.py +231 -0
- manigent/models/chatroom.py +1108 -0
- manigent/models/context.py +1159 -0
- manigent/models/desk_label.py +836 -0
- manigent/models/desk_read_state.py +220 -0
- manigent/models/desk_sign_off.py +267 -0
- manigent/models/desk_sop.py +965 -0
- manigent/models/desk_todo.py +1603 -0
- manigent/models/desk_todo_link.py +404 -0
- manigent/models/file_cache.py +75 -0
- manigent/models/host.py +732 -0
- manigent/models/install_token.py +401 -0
- manigent/models/invitation_code.py +162 -0
- manigent/models/knowledge_pack.py +552 -0
- manigent/models/managed_job.py +295 -0
- manigent/models/model_config.py +564 -0
- manigent/models/model_defaults.py +238 -0
- manigent/models/onboarding_plan.py +161 -0
- manigent/models/participant.py +449 -0
- manigent/models/persistable.py +892 -0
- manigent/models/plan_markdown.py +1774 -0
- manigent/models/project.py +1343 -0
- manigent/models/project_plan.py +8104 -0
- manigent/models/project_report.py +145 -0
- manigent/models/provenance.py +261 -0
- manigent/models/provider_agents.py +101 -0
- manigent/models/public_request.py +108 -0
- manigent/models/reflection_schedule.py +191 -0
- manigent/models/scheduled_message.py +285 -0
- manigent/models/share_token.py +190 -0
- manigent/models/skill_version.py +31 -0
- manigent/models/sop_template.py +187 -0
- manigent/models/thinking_partner_plan.py +187 -0
- manigent/models/tunnel_binding.py +129 -0
- manigent/models/user.py +1276 -0
- manigent/models/work_tracker.py +510 -0
- manigent/runner/invocation_registry.py +187 -0
- manigent/server/agent_capabilities.py +119 -0
- manigent/server/agent_visibility.py +119 -0
- manigent/server/announcements.py +119 -0
- manigent/server/app.py +807 -0
- manigent/server/auth.py +355 -0
- manigent/server/batch_timeout.py +94 -0
- manigent/server/channels/access.py +194 -0
- manigent/server/channels/adapter.py +295 -0
- manigent/server/channels/config.py +693 -0
- manigent/server/channels/contacts.py +39 -0
- manigent/server/channels/email_adapter.py +436 -0
- manigent/server/channels/email_address.py +145 -0
- manigent/server/channels/email_form.py +242 -0
- manigent/server/channels/email_format.py +285 -0
- manigent/server/channels/email_mail.py +427 -0
- manigent/server/channels/email_mailgun.py +164 -0
- manigent/server/channels/email_text.py +280 -0
- manigent/server/channels/file_links.py +127 -0
- manigent/server/channels/gateway.py +2998 -0
- manigent/server/channels/hub_delivery.py +211 -0
- manigent/server/channels/hub_store.py +1730 -0
- manigent/server/channels/line.py +645 -0
- manigent/server/channels/line_format.py +589 -0
- manigent/server/channels/log_redaction.py +145 -0
- manigent/server/channels/store.py +3316 -0
- manigent/server/channels/telegram.py +422 -0
- manigent/server/channels/telegram_format.py +312 -0
- manigent/server/channels/webchat.py +261 -0
- manigent/server/channels/webchat_widget.js +1310 -0
- manigent/server/channels/whatsapp.py +707 -0
- manigent/server/channels/whatsapp_format.py +511 -0
- manigent/server/chat_search_index.py +1203 -0
- manigent/server/config.py +61 -0
- manigent/server/context.py +121 -0
- manigent/server/coordinator_rule.py +115 -0
- manigent/server/desk_todo_notify.py +300 -0
- manigent/server/edge_hub.py +577 -0
- manigent/server/email.py +354 -0
- manigent/server/errors.py +83 -0
- manigent/server/home_fs_relay.py +392 -0
- manigent/server/hub/adapter.py +1395 -0
- manigent/server/hub/agent_send_guard.py +196 -0
- manigent/server/hub/avatar.py +342 -0
- manigent/server/hub/browser.py +778 -0
- manigent/server/hub/client.py +859 -0
- manigent/server/hub/demand.py +49 -0
- manigent/server/hub/directory.py +385 -0
- manigent/server/hub/enroll.py +400 -0
- manigent/server/hub/http.py +134 -0
- manigent/server/hub/identity.py +61 -0
- manigent/server/hub/keys.py +588 -0
- manigent/server/hub/mirror.py +802 -0
- manigent/server/hub/operator.py +817 -0
- manigent/server/hub/retain.py +361 -0
- manigent/server/hub/room_bridge.py +741 -0
- manigent/server/hub/runtime.py +691 -0
- manigent/server/hub/trust.py +182 -0
- manigent/server/hub/views.py +316 -0
- manigent/server/idempotency.py +205 -0
- manigent/server/in_flight_restore.py +181 -0
- manigent/server/inspection.py +816 -0
- manigent/server/isolation_gate.py +516 -0
- manigent/server/isolation_notice.py +310 -0
- manigent/server/managed_job_events.py +242 -0
- manigent/server/mcp_registry.py +118 -0
- manigent/server/message_forward.py +78 -0
- manigent/server/message_scheduler.py +176 -0
- manigent/server/notification_assets.py +164 -0
- manigent/server/oauth_login/cli_state.py +120 -0
- manigent/server/oauth_login/config.py +90 -0
- manigent/server/oauth_login/errors.py +78 -0
- manigent/server/oauth_login/flow.py +223 -0
- manigent/server/oauth_login/handoff.py +63 -0
- manigent/server/oauth_login/providers.py +45 -0
- manigent/server/oauth_state.py +81 -0
- manigent/server/owner_isolation.py +466 -0
- manigent/server/participant_liveness.py +82 -0
- manigent/server/public_profile.py +149 -0
- manigent/server/public_requests/candidates.py +116 -0
- manigent/server/public_requests/dispatch.py +121 -0
- manigent/server/public_requests/ranking.py +153 -0
- manigent/server/recent_activity.py +250 -0
- manigent/server/reflection_completion_subscriber.py +151 -0
- manigent/server/reflection_scheduler.py +239 -0
- manigent/server/requester_key_migration.py +261 -0
- manigent/server/room_flow.py +139 -0
- manigent/server/routes/_batch.py +218 -0
- manigent/server/routes/_fd_routing.py +155 -0
- manigent/server/routes/_mentions.py +70 -0
- manigent/server/routes/agent_env.py +188 -0
- manigent/server/routes/agent_fs.py +450 -0
- manigent/server/routes/agents.py +1206 -0
- manigent/server/routes/announcements.py +79 -0
- manigent/server/routes/auth.py +387 -0
- manigent/server/routes/brief_tabs.py +404 -0
- manigent/server/routes/changelog.py +237 -0
- manigent/server/routes/channels.py +993 -0
- manigent/server/routes/chatrooms.py +871 -0
- manigent/server/routes/desk_labels.py +298 -0
- manigent/server/routes/desk_latest_messages.py +116 -0
- manigent/server/routes/desk_read_state.py +110 -0
- manigent/server/routes/desk_sign_off.py +88 -0
- manigent/server/routes/desk_sops.py +216 -0
- manigent/server/routes/desk_todos.py +692 -0
- manigent/server/routes/desk_tour.py +44 -0
- manigent/server/routes/files.py +572 -0
- manigent/server/routes/hosts.py +484 -0
- manigent/server/routes/hub.py +534 -0
- manigent/server/routes/hub_channels.py +234 -0
- manigent/server/routes/inbox.py +581 -0
- manigent/server/routes/install.ps1 +436 -0
- manigent/server/routes/install.py +324 -0
- manigent/server/routes/install.sh +566 -0
- manigent/server/routes/isolation_check.py +145 -0
- manigent/server/routes/knowledge_packs.py +477 -0
- manigent/server/routes/managed_jobs.py +196 -0
- manigent/server/routes/mcps.py +271 -0
- manigent/server/routes/messages.py +1972 -0
- manigent/server/routes/model_defaults.py +279 -0
- manigent/server/routes/oauth.py +237 -0
- manigent/server/routes/oauth_login.py +351 -0
- manigent/server/routes/owner_isolation.py +91 -0
- manigent/server/routes/project_plans.py +1521 -0
- manigent/server/routes/project_reports.py +235 -0
- manigent/server/routes/projects.py +2047 -0
- manigent/server/routes/public_agents.py +151 -0
- manigent/server/routes/public_requests.py +181 -0
- manigent/server/routes/reflection.py +93 -0
- manigent/server/routes/scheduled_messages.py +263 -0
- manigent/server/routes/search.py +94 -0
- manigent/server/routes/sharing.py +156 -0
- manigent/server/routes/skills.py +406 -0
- manigent/server/routes/templates.py +219 -0
- manigent/server/routes/turn_tokens.py +119 -0
- manigent/server/routes/webchat.py +406 -0
- manigent/server/routes/websocket.py +431 -0
- manigent/server/serving_only.py +200 -0
- manigent/server/signup_policy.py +120 -0
- manigent/server/skill_registry.py +164 -0
- manigent/server/socket_events.py +858 -0
- manigent/server/timeout_checker.py +446 -0
- manigent/server/tunnel_subscriber.py +1000 -0
- manigent/server/turn_tokens.py +285 -0
- manigent/server/ws_hub.py +556 -0
- manigent/server_cli.py +31 -0
- manigent/sync/changelog.py +885 -0
- manigent/sync/console_subscriber.py +222 -0
- manigent/sync/runloop.py +619 -0
- manigent/sync/runloop_manager.py +197 -0
- manigent/sync/subscriber.py +98 -0
- manigent/utils/agent_processes.py +276 -0
- manigent/utils/agent_storage.py +125 -0
- manigent/utils/email.py +563 -0
- manigent/utils/env_store.py +174 -0
- manigent/utils/extra_dirs.py +85 -0
- manigent/utils/file_io.py +485 -0
- manigent/utils/invoke_timeout.py +87 -0
- manigent/utils/jsonc.py +78 -0
- manigent/utils/knowledge_index.py +148 -0
- manigent/utils/nofollow_fs.py +279 -0
- manigent/utils/notification_center.py +67 -0
- manigent/utils/version.py +57 -0
- manigent/web/frontend/.design-sync-entry.tsx +74 -0
- manigent/web/frontend/.design-sync-provider.tsx +46 -0
- manigent/web/frontend/.eslintrc.cjs +183 -0
- manigent/web/frontend/.gitignore +6 -0
- manigent/web/frontend/app/page.tsx +39 -0
- manigent/web/frontend/hub-terms.html +192 -0
- manigent/web/frontend/index.html +15 -0
- manigent/web/frontend/landing.html +15 -0
- manigent/web/frontend/package-lock.json +8028 -0
- manigent/web/frontend/package.json +55 -0
- manigent/web/frontend/postcss.config.js +6 -0
- manigent/web/frontend/privacy.html +217 -0
- manigent/web/frontend/private-beta-announcement.html +19 -0
- manigent/web/frontend/public/apple-touch-icon.png +0 -0
- manigent/web/frontend/public/brand-hero-banner.png +0 -0
- manigent/web/frontend/public/brand-hero.png +0 -0
- manigent/web/frontend/public/favicon.png +0 -0
- manigent/web/frontend/public/favicon.svg +8 -0
- manigent/web/frontend/public/icon-transparent-sm.png +0 -0
- manigent/web/frontend/public/icon.png +0 -0
- manigent/web/frontend/public/manigent-mark.svg +7 -0
- manigent/web/frontend/public/vite.svg +5 -0
- manigent/web/frontend/public.html +15 -0
- manigent/web/frontend/src/App.activeAccountRedirect.test.tsx +236 -0
- manigent/web/frontend/src/App.hubHostDm.test.tsx +119 -0
- manigent/web/frontend/src/App.hubHostProject.test.tsx +142 -0
- manigent/web/frontend/src/App.hubRoutes.test.tsx +187 -0
- manigent/web/frontend/src/App.oauthAddCallback.test.tsx +263 -0
- manigent/web/frontend/src/App.tsx +246 -0
- manigent/web/frontend/src/api/attachments.ts +32 -0
- manigent/web/frontend/src/api/batchLoader.test.ts +212 -0
- manigent/web/frontend/src/api/batchLoader.ts +124 -0
- manigent/web/frontend/src/api/chatroomsBatch.test.tsx +392 -0
- manigent/web/frontend/src/api/client.ts +321 -0
- manigent/web/frontend/src/api/crossAccountAuth.test.ts +388 -0
- manigent/web/frontend/src/api/endpoints/agentEnv.ts +24 -0
- manigent/web/frontend/src/api/endpoints/agentFs.ts +125 -0
- manigent/web/frontend/src/api/endpoints/agentSearch.ts +19 -0
- manigent/web/frontend/src/api/endpoints/agents.ts +66 -0
- manigent/web/frontend/src/api/endpoints/announcements.ts +26 -0
- manigent/web/frontend/src/api/endpoints/auth.ts +361 -0
- manigent/web/frontend/src/api/endpoints/authLogin.test.ts +236 -0
- manigent/web/frontend/src/api/endpoints/batch.ts +64 -0
- manigent/web/frontend/src/api/endpoints/briefTabs.ts +81 -0
- manigent/web/frontend/src/api/endpoints/channels.ts +285 -0
- manigent/web/frontend/src/api/endpoints/computers.ts +212 -0
- manigent/web/frontend/src/api/endpoints/deskLabels.ts +151 -0
- manigent/web/frontend/src/api/endpoints/deskLatestMessages.ts +23 -0
- manigent/web/frontend/src/api/endpoints/deskReadState.ts +41 -0
- manigent/web/frontend/src/api/endpoints/deskSignOffs.ts +74 -0
- manigent/web/frontend/src/api/endpoints/deskSops.ts +53 -0
- manigent/web/frontend/src/api/endpoints/deskTodoAttachments.ts +69 -0
- manigent/web/frontend/src/api/endpoints/deskTodoProjects.test.ts +99 -0
- manigent/web/frontend/src/api/endpoints/deskTodoProjects.ts +114 -0
- manigent/web/frontend/src/api/endpoints/deskTodos.ts +164 -0
- manigent/web/frontend/src/api/endpoints/deskTour.ts +16 -0
- manigent/web/frontend/src/api/endpoints/dm.ts +137 -0
- manigent/web/frontend/src/api/endpoints/files.ts +55 -0
- manigent/web/frontend/src/api/endpoints/hub.contract.test.ts +94 -0
- manigent/web/frontend/src/api/endpoints/hub.keys.ts +260 -0
- manigent/web/frontend/src/api/endpoints/hub.test.ts +203 -0
- manigent/web/frontend/src/api/endpoints/hub.ts +654 -0
- manigent/web/frontend/src/api/endpoints/index.ts +15 -0
- manigent/web/frontend/src/api/endpoints/install.ts +74 -0
- manigent/web/frontend/src/api/endpoints/isolationCheck.ts +36 -0
- manigent/web/frontend/src/api/endpoints/knowledgePacks.ts +179 -0
- manigent/web/frontend/src/api/endpoints/mcps.ts +36 -0
- manigent/web/frontend/src/api/endpoints/messages.held.test.ts +43 -0
- manigent/web/frontend/src/api/endpoints/messages.ts +95 -0
- manigent/web/frontend/src/api/endpoints/modelConfigs.ts +232 -0
- manigent/web/frontend/src/api/endpoints/oauthApi.test.ts +110 -0
- manigent/web/frontend/src/api/endpoints/projectPlans.ts +608 -0
- manigent/web/frontend/src/api/endpoints/projectReports.ts +36 -0
- manigent/web/frontend/src/api/endpoints/projects.ts +124 -0
- manigent/web/frontend/src/api/endpoints/publicRequests.ts +75 -0
- manigent/web/frontend/src/api/endpoints/reflection.ts +32 -0
- manigent/web/frontend/src/api/endpoints/roomFlow.ts +14 -0
- manigent/web/frontend/src/api/endpoints/scheduledMessages.ts +60 -0
- manigent/web/frontend/src/api/endpoints/search.ts +27 -0
- manigent/web/frontend/src/api/endpoints/sharing.ts +42 -0
- manigent/web/frontend/src/api/endpoints/skills.ts +67 -0
- manigent/web/frontend/src/api/endpoints/templates.ts +72 -0
- manigent/web/frontend/src/api/loaders.ts +22 -0
- manigent/web/frontend/src/api/singleIssuer.test.ts +148 -0
- manigent/web/frontend/src/api/transientRetry.test.ts +176 -0
- manigent/web/frontend/src/api/transientRetry.ts +140 -0
- manigent/web/frontend/src/api/websocket.resync.test.ts +111 -0
- manigent/web/frontend/src/api/websocket.ts +335 -0
- manigent/web/frontend/src/components/agent/AgentNameLink.tsx +24 -0
- manigent/web/frontend/src/components/agent/AgentViewActions.test.tsx +41 -0
- manigent/web/frontend/src/components/agent/AgentViewActions.tsx +89 -0
- manigent/web/frontend/src/components/agent/CreateAgentPage.tsx +535 -0
- manigent/web/frontend/src/components/agent/JsonConfigEditor.tsx +33 -0
- manigent/web/frontend/src/components/agent/KnowledgePackList.tsx +103 -0
- manigent/web/frontend/src/components/agent/McpCatalog.tsx +261 -0
- manigent/web/frontend/src/components/agent/McpConfigModal.tsx +224 -0
- manigent/web/frontend/src/components/agent/PreConsentModal.tsx +172 -0
- manigent/web/frontend/src/components/agent/SkillCatalog.tsx +364 -0
- manigent/web/frontend/src/components/agent/SkillConfigModal.tsx +221 -0
- manigent/web/frontend/src/components/auth/AddAccountPage.test.tsx +181 -0
- manigent/web/frontend/src/components/auth/AddAccountPage.tsx +201 -0
- manigent/web/frontend/src/components/auth/AuthShell.tsx +462 -0
- manigent/web/frontend/src/components/auth/LoginPage.test.tsx +384 -0
- manigent/web/frontend/src/components/auth/LoginPage.tsx +129 -0
- manigent/web/frontend/src/components/auth/OAuthCallbackPage.test.tsx +185 -0
- manigent/web/frontend/src/components/auth/OAuthCallbackPage.tsx +154 -0
- manigent/web/frontend/src/components/auth/OAuthLinkedBanner.tsx +44 -0
- manigent/web/frontend/src/components/auth/ProviderButtons.tsx +94 -0
- manigent/web/frontend/src/components/auth/SignupPage.tsx +437 -0
- manigent/web/frontend/src/components/auth/StepProgress.tsx +60 -0
- manigent/web/frontend/src/components/auth/VerifyEmailPage.test.tsx +155 -0
- manigent/web/frontend/src/components/auth/VerifyEmailPage.tsx +211 -0
- manigent/web/frontend/src/components/auth/index.ts +3 -0
- manigent/web/frontend/src/components/auth/oauthErrors.test.ts +107 -0
- manigent/web/frontend/src/components/auth/oauthErrors.ts +82 -0
- manigent/web/frontend/src/components/auth/useUsernameCheck.test.tsx +68 -0
- manigent/web/frontend/src/components/auth/useUsernameCheck.ts +86 -0
- manigent/web/frontend/src/components/beta/BetaPage.tsx +21 -0
- manigent/web/frontend/src/components/beta/DayAtMyDesk.tsx +120 -0
- manigent/web/frontend/src/components/beta/Footer.tsx +82 -0
- manigent/web/frontend/src/components/beta/Hero.tsx +224 -0
- manigent/web/frontend/src/components/beta/Nav.tsx +51 -0
- manigent/web/frontend/src/components/beta/Pillars.tsx +172 -0
- manigent/web/frontend/src/components/beta/TwoModels.tsx +146 -0
- manigent/web/frontend/src/components/beta/constants.ts +3 -0
- manigent/web/frontend/src/components/briefing/BriefingFrame.test.tsx +208 -0
- manigent/web/frontend/src/components/briefing/BriefingFrame.tsx +151 -0
- manigent/web/frontend/src/components/briefing/BriefingPage.test.tsx +176 -0
- manigent/web/frontend/src/components/briefing/BriefingPage.tsx +157 -0
- manigent/web/frontend/src/components/briefing/BriefingPage.wsDelete.test.tsx +177 -0
- manigent/web/frontend/src/components/briefing/briefing.css +147 -0
- manigent/web/frontend/src/components/briefing/heightBootstrap.test.ts +121 -0
- manigent/web/frontend/src/components/briefing/heightBootstrap.ts +224 -0
- manigent/web/frontend/src/components/briefing/parseBriefMessage.test.ts +90 -0
- manigent/web/frontend/src/components/briefing/sampleBriefing.ts +148 -0
- manigent/web/frontend/src/components/channels/ChannelBadge.tsx +35 -0
- manigent/web/frontend/src/components/chatroom/ChannelThreadControls.tsx +127 -0
- manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.test.tsx +207 -0
- manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.tsx +80 -0
- manigent/web/frontend/src/components/chatroom/ChatroomView.channel.test.tsx +550 -0
- manigent/web/frontend/src/components/chatroom/ChatroomView.flow.test.tsx +186 -0
- manigent/web/frontend/src/components/chatroom/ChatroomView.isolation.test.tsx +70 -0
- manigent/web/frontend/src/components/chatroom/ChatroomView.tsx +450 -0
- manigent/web/frontend/src/components/chatroom/DelegationRow.test.tsx +170 -0
- manigent/web/frontend/src/components/chatroom/DelegationRow.tsx +217 -0
- manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.test.tsx +33 -0
- manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.tsx +46 -0
- manigent/web/frontend/src/components/chatroom/ForwardModal.test.tsx +109 -0
- manigent/web/frontend/src/components/chatroom/ForwardModal.tsx +304 -0
- manigent/web/frontend/src/components/chatroom/HeldNotice.tsx +77 -0
- manigent/web/frontend/src/components/chatroom/MentionDropdown.tsx +97 -0
- manigent/web/frontend/src/components/chatroom/MessageInput.tsx +408 -0
- manigent/web/frontend/src/components/chatroom/MessageItem.tsx +425 -0
- manigent/web/frontend/src/components/chatroom/MessageList.outside.test.tsx +98 -0
- manigent/web/frontend/src/components/chatroom/MessageList.test.tsx +245 -0
- manigent/web/frontend/src/components/chatroom/MessageList.tsx +406 -0
- manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.test.tsx +211 -0
- manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.tsx +182 -0
- manigent/web/frontend/src/components/chatroom/OutsideBadge.tsx +18 -0
- manigent/web/frontend/src/components/chatroom/RoomFlowControl.tsx +49 -0
- manigent/web/frontend/src/components/chatroom/RoomFlowStrip.test.tsx +47 -0
- manigent/web/frontend/src/components/chatroom/RoomFlowStrip.tsx +25 -0
- manigent/web/frontend/src/components/chatroom/SaveToPackModal.tsx +350 -0
- manigent/web/frontend/src/components/chatroom/StagedAttachments.test.tsx +56 -0
- manigent/web/frontend/src/components/chatroom/StagedAttachments.tsx +160 -0
- manigent/web/frontend/src/components/chatroom/SystemNote.tsx +18 -0
- manigent/web/frontend/src/components/chatroom/contactSender.test.tsx +130 -0
- manigent/web/frontend/src/components/chatroom/index.ts +3 -0
- manigent/web/frontend/src/components/chatroom/recipientStatus.test.ts +344 -0
- manigent/web/frontend/src/components/chatroom/recipientStatus.ts +197 -0
- manigent/web/frontend/src/components/common/AnnouncementBanners.test.tsx +221 -0
- manigent/web/frontend/src/components/common/AnnouncementBanners.tsx +180 -0
- manigent/web/frontend/src/components/common/Avatar.test.tsx +50 -0
- manigent/web/frontend/src/components/common/Avatar.tsx +102 -0
- manigent/web/frontend/src/components/common/Badge.tsx +30 -0
- manigent/web/frontend/src/components/common/BrandName.tsx +16 -0
- manigent/web/frontend/src/components/common/ClearHistoryButton.tsx +74 -0
- manigent/web/frontend/src/components/common/CollapsibleHeader.tsx +64 -0
- manigent/web/frontend/src/components/common/ConnectionStatus.test.tsx +56 -0
- manigent/web/frontend/src/components/common/ConnectionStatus.tsx +28 -0
- manigent/web/frontend/src/components/common/DropOverlay.tsx +27 -0
- manigent/web/frontend/src/components/common/InlineEditableTitle.test.tsx +369 -0
- manigent/web/frontend/src/components/common/InlineEditableTitle.tsx +287 -0
- manigent/web/frontend/src/components/common/IsolationUnavailablePanel.tsx +19 -0
- manigent/web/frontend/src/components/common/Loading.tsx +22 -0
- manigent/web/frontend/src/components/common/ModelSelector.tsx +248 -0
- manigent/web/frontend/src/components/common/Overlay.tsx +15 -0
- manigent/web/frontend/src/components/common/ResizeHandle.tsx +52 -0
- manigent/web/frontend/src/components/common/TagAutocompleteInput.test.tsx +92 -0
- manigent/web/frontend/src/components/common/TagAutocompleteInput.tsx +246 -0
- manigent/web/frontend/src/components/common/TagPillInput.tsx +181 -0
- manigent/web/frontend/src/components/common/TeamAndAgentPicker.tsx +83 -0
- manigent/web/frontend/src/components/common/Timestamp.tsx +61 -0
- manigent/web/frontend/src/components/common/VerifiedBadge.tsx +20 -0
- manigent/web/frontend/src/components/common/index.ts +8 -0
- manigent/web/frontend/src/components/composer/PhPopover.tsx +239 -0
- manigent/web/frontend/src/components/composer/PromptEditor.test.tsx +472 -0
- manigent/web/frontend/src/components/composer/PromptEditor.tsx +540 -0
- manigent/web/frontend/src/components/composer/caret.ts +159 -0
- manigent/web/frontend/src/components/composer/placeholders.test.ts +352 -0
- manigent/web/frontend/src/components/composer/placeholders.ts +354 -0
- manigent/web/frontend/src/components/composer/promptEditor.css +161 -0
- manigent/web/frontend/src/components/computer/ComputerPage.test.tsx +452 -0
- manigent/web/frontend/src/components/computer/ComputerPage.tsx +811 -0
- manigent/web/frontend/src/components/computer/ConnectComputerModal.tsx +135 -0
- manigent/web/frontend/src/components/computer/CopyCommand.tsx +77 -0
- manigent/web/frontend/src/components/computer/DisconnectComputerModal.tsx +121 -0
- manigent/web/frontend/src/components/computer/SoftwareCard.tsx +145 -0
- manigent/web/frontend/src/components/computer/TerminalCard.test.tsx +114 -0
- manigent/web/frontend/src/components/computer/TerminalCard.tsx +131 -0
- manigent/web/frontend/src/components/computer/TerminalPanel.tsx +226 -0
- manigent/web/frontend/src/components/computer/TrustPanel.test.tsx +58 -0
- manigent/web/frontend/src/components/computer/TrustPanel.tsx +143 -0
- manigent/web/frontend/src/components/computer/computerCopy.test.ts +322 -0
- manigent/web/frontend/src/components/computer/computerCopy.ts +413 -0
- manigent/web/frontend/src/components/computer/sections.ts +21 -0
- manigent/web/frontend/src/components/computer/terminalProtocol.test.ts +135 -0
- manigent/web/frontend/src/components/computer/terminalProtocol.ts +170 -0
- manigent/web/frontend/src/components/context/ContextGuide.test.tsx +26 -0
- manigent/web/frontend/src/components/context/ContextGuide.tsx +144 -0
- manigent/web/frontend/src/components/context/ContextLanding.tsx +46 -0
- manigent/web/frontend/src/components/desk/CardBand.a11y.test.tsx +138 -0
- manigent/web/frontend/src/components/desk/CardBand.tsx +132 -0
- manigent/web/frontend/src/components/desk/DashTile.tsx +81 -0
- manigent/web/frontend/src/components/desk/DeskAgentPicker.retained.test.tsx +79 -0
- manigent/web/frontend/src/components/desk/DeskAgentPicker.tsx +151 -0
- manigent/web/frontend/src/components/desk/DeskComposer.detachTodo.test.tsx +233 -0
- manigent/web/frontend/src/components/desk/DeskComposer.dispatch.test.tsx +208 -0
- manigent/web/frontend/src/components/desk/DeskComposer.loadTodoDraft.test.tsx +242 -0
- manigent/web/frontend/src/components/desk/DeskComposer.restage.test.tsx +258 -0
- manigent/web/frontend/src/components/desk/DeskComposer.saveTodo.test.tsx +265 -0
- manigent/web/frontend/src/components/desk/DeskComposer.tsx +828 -0
- manigent/web/frontend/src/components/desk/DeskPage.crossHighlight.test.tsx +427 -0
- manigent/web/frontend/src/components/desk/DeskPage.link.test.tsx +271 -0
- manigent/web/frontend/src/components/desk/DeskPage.tsx +1066 -0
- manigent/web/frontend/src/components/desk/DeskPage.welcomeTour.test.tsx +262 -0
- manigent/web/frontend/src/components/desk/DeskSideRail.tsx +100 -0
- manigent/web/frontend/src/components/desk/DeskWelcomeTour.tsx +513 -0
- manigent/web/frontend/src/components/desk/DeskZeroState.tsx +45 -0
- manigent/web/frontend/src/components/desk/LabelChip.tsx +147 -0
- manigent/web/frontend/src/components/desk/LabelFilter.tsx +474 -0
- manigent/web/frontend/src/components/desk/LabelInput.test.tsx +439 -0
- manigent/web/frontend/src/components/desk/LabelInput.tsx +477 -0
- manigent/web/frontend/src/components/desk/LabelManageSheet.test.tsx +384 -0
- manigent/web/frontend/src/components/desk/LabelManageSheet.tsx +498 -0
- manigent/web/frontend/src/components/desk/PhaseRail.test.tsx +170 -0
- manigent/web/frontend/src/components/desk/PhaseRail.tsx +120 -0
- manigent/web/frontend/src/components/desk/PlanBand.test.tsx +655 -0
- manigent/web/frontend/src/components/desk/PlanBand.tsx +216 -0
- manigent/web/frontend/src/components/desk/SaveTodoModal.tsx +126 -0
- manigent/web/frontend/src/components/desk/SopEditor.tsx +280 -0
- manigent/web/frontend/src/components/desk/SopItem.tsx +171 -0
- manigent/web/frontend/src/components/desk/SopRail.test.tsx +517 -0
- manigent/web/frontend/src/components/desk/SopRail.tsx +250 -0
- manigent/web/frontend/src/components/desk/TicketChip.tsx +51 -0
- manigent/web/frontend/src/components/desk/TodoItem.attachments.test.tsx +96 -0
- manigent/web/frontend/src/components/desk/TodoItem.menu.test.tsx +142 -0
- manigent/web/frontend/src/components/desk/TodoItem.tsx +573 -0
- manigent/web/frontend/src/components/desk/TodoRail.longpress.test.tsx +396 -0
- manigent/web/frontend/src/components/desk/TodoRail.test.tsx +1155 -0
- manigent/web/frontend/src/components/desk/TodoRail.tsx +918 -0
- manigent/web/frontend/src/components/desk/TodoRowMenu.tsx +147 -0
- manigent/web/frontend/src/components/desk/UpdateCard.bands.test.tsx +308 -0
- manigent/web/frontend/src/components/desk/UpdateCard.sendBack.test.tsx +444 -0
- manigent/web/frontend/src/components/desk/UpdateCard.tsx +943 -0
- manigent/web/frontend/src/components/desk/UpdateCard.untrusted.test.tsx +161 -0
- manigent/web/frontend/src/components/desk/desk.css +1921 -0
- manigent/web/frontend/src/components/desk/desk.css.test.ts +582 -0
- manigent/web/frontend/src/components/desk/deskBands.test.ts +389 -0
- manigent/web/frontend/src/components/desk/deskBands.ts +226 -0
- manigent/web/frontend/src/components/desk/m16Surfaces.test.ts +134 -0
- manigent/web/frontend/src/components/desk/sopSyntax.ts +73 -0
- manigent/web/frontend/src/components/discovery/AgentDirectory.tsx +71 -0
- manigent/web/frontend/src/components/discovery/AgentFilterBar.tsx +46 -0
- manigent/web/frontend/src/components/discovery/AgentSearchBar.tsx +56 -0
- manigent/web/frontend/src/components/dm/AgentSummaryCard.tsx +154 -0
- manigent/web/frontend/src/components/dm/ComposeFields.tsx +145 -0
- manigent/web/frontend/src/components/dm/DMChatView.flow.test.tsx +374 -0
- manigent/web/frontend/src/components/dm/DMChatView.tsx +841 -0
- manigent/web/frontend/src/components/dm/DMZeroState.tsx +204 -0
- manigent/web/frontend/src/components/dm/DmComposeModal.tsx +301 -0
- manigent/web/frontend/src/components/dm/PersonalizeModal.tsx +366 -0
- manigent/web/frontend/src/components/dm/index.ts +1 -0
- manigent/web/frontend/src/components/files/FileItem.tsx +138 -0
- manigent/web/frontend/src/components/files/FileList.outside.test.tsx +29 -0
- manigent/web/frontend/src/components/files/FileList.tsx +48 -0
- manigent/web/frontend/src/components/files/FilePreview.test.tsx +75 -0
- manigent/web/frontend/src/components/files/FilePreview.tsx +325 -0
- manigent/web/frontend/src/components/files/FileViewerModal.test.tsx +157 -0
- manigent/web/frontend/src/components/files/FileViewerModal.tsx +261 -0
- manigent/web/frontend/src/components/files/HighlightedCode.tsx +39 -0
- manigent/web/frontend/src/components/files/SaveToAgentPanel.test.tsx +156 -0
- manigent/web/frontend/src/components/files/SaveToAgentPanel.tsx +545 -0
- manigent/web/frontend/src/components/files/index.ts +3 -0
- manigent/web/frontend/src/components/hero/AgentAvatar.tsx +66 -0
- manigent/web/frontend/src/components/hero/Button.tsx +66 -0
- manigent/web/frontend/src/components/hero/Hero.tsx +133 -0
- manigent/web/frontend/src/components/homefs/HomeFolderBrowser.test.tsx +393 -0
- manigent/web/frontend/src/components/homefs/HomeFolderBrowser.tsx +792 -0
- manigent/web/frontend/src/components/hub/BridgeDeliveryLine.test.tsx +40 -0
- manigent/web/frontend/src/components/hub/BridgeDeliveryLine.tsx +36 -0
- manigent/web/frontend/src/components/hub/HubAvatar.test.tsx +93 -0
- manigent/web/frontend/src/components/hub/HubAvatar.tsx +33 -0
- manigent/web/frontend/src/components/hub/HubChatView.test.tsx +695 -0
- manigent/web/frontend/src/components/hub/HubChatView.tsx +504 -0
- manigent/web/frontend/src/components/hub/HubContactLabel.test.tsx +92 -0
- manigent/web/frontend/src/components/hub/HubContactLabel.tsx +47 -0
- manigent/web/frontend/src/components/hub/HubDeliveryLine.tsx +49 -0
- manigent/web/frontend/src/components/hub/HubDisclosure.tsx +49 -0
- manigent/web/frontend/src/components/hub/HubEnrollmentDialog.test.tsx +144 -0
- manigent/web/frontend/src/components/hub/HubEnrollmentDialog.tsx +244 -0
- manigent/web/frontend/src/components/hub/HubMessageBody.test.tsx +26 -0
- manigent/web/frontend/src/components/hub/HubMessageBody.tsx +14 -0
- manigent/web/frontend/src/components/hub/HubOfflineConfirm.tsx +45 -0
- manigent/web/frontend/src/components/hub/HubThreadBanner.tsx +33 -0
- manigent/web/frontend/src/components/hub/HubThreadGuard.tsx +59 -0
- manigent/web/frontend/src/components/hub/RemoteAgentCard.test.tsx +50 -0
- manigent/web/frontend/src/components/hub/RemoteAgentCard.tsx +174 -0
- manigent/web/frontend/src/components/hub/RemoteAgentSection.test.tsx +446 -0
- manigent/web/frontend/src/components/hub/RemoteAgentSection.tsx +219 -0
- manigent/web/frontend/src/components/hub/ReportDialog.test.tsx +96 -0
- manigent/web/frontend/src/components/hub/ReportDialog.tsx +162 -0
- manigent/web/frontend/src/components/hub/RetainButton.test.tsx +103 -0
- manigent/web/frontend/src/components/hub/RetainButton.tsx +114 -0
- manigent/web/frontend/src/components/hub/RetainDialog.test.tsx +68 -0
- manigent/web/frontend/src/components/hub/RetainDialog.tsx +115 -0
- manigent/web/frontend/src/components/hub/RetainedList.test.tsx +94 -0
- manigent/web/frontend/src/components/hub/RetainedList.tsx +32 -0
- manigent/web/frontend/src/components/hub/RetainedRow.tsx +129 -0
- manigent/web/frontend/src/components/hub/StandInChip.test.tsx +37 -0
- manigent/web/frontend/src/components/hub/StandInChip.tsx +43 -0
- manigent/web/frontend/src/components/hub/UnusualNameChip.test.tsx +81 -0
- manigent/web/frontend/src/components/hub/UnusualNameChip.tsx +22 -0
- manigent/web/frontend/src/components/hub/hubLint.test.ts +265 -0
- manigent/web/frontend/src/components/landing/ComparisonSection.tsx +92 -0
- manigent/web/frontend/src/components/landing/ConsoleSection.tsx +402 -0
- manigent/web/frontend/src/components/landing/CrewSection.tsx +224 -0
- manigent/web/frontend/src/components/landing/FAQ.tsx +68 -0
- manigent/web/frontend/src/components/landing/Footer.tsx +58 -0
- manigent/web/frontend/src/components/landing/HowItWorks.tsx +117 -0
- manigent/web/frontend/src/components/landing/KnowledgeSection.tsx +91 -0
- manigent/web/frontend/src/components/landing/LandingPage.test.tsx +134 -0
- manigent/web/frontend/src/components/landing/LearningSection.tsx +486 -0
- manigent/web/frontend/src/components/landing/Nav.tsx +68 -0
- manigent/web/frontend/src/components/landing/Pricing.tsx +263 -0
- manigent/web/frontend/src/components/landing/Section.tsx +98 -0
- manigent/web/frontend/src/components/landing/StaffingSection.tsx +122 -0
- manigent/web/frontend/src/components/landing/WhyNowSection.tsx +92 -0
- manigent/web/frontend/src/components/landing/WhyTeamSection.tsx +73 -0
- manigent/web/frontend/src/components/landing/earlyAccess.ts +23 -0
- manigent/web/frontend/src/components/layout/AppLayout.tsx +77 -0
- manigent/web/frontend/src/components/layout/Sidebar.tsx +172 -0
- manigent/web/frontend/src/components/layout/index.ts +2 -0
- manigent/web/frontend/src/components/project/ActivityFeed.tsx +247 -0
- manigent/web/frontend/src/components/project/CreateProjectPage.tsx +543 -0
- manigent/web/frontend/src/components/project/DeleteProjectButton.tsx +39 -0
- manigent/web/frontend/src/components/project/ProjectPlanTab.phase.test.tsx +107 -0
- manigent/web/frontend/src/components/project/ProjectPlanTab.test.tsx +994 -0
- manigent/web/frontend/src/components/project/ProjectPlanTab.tsx +166 -0
- manigent/web/frontend/src/components/project/ProjectReportTab.tsx +139 -0
- manigent/web/frontend/src/components/project/ProjectView.tsx +390 -0
- manigent/web/frontend/src/components/project/index.ts +2 -0
- manigent/web/frontend/src/components/project/plan/PlanAcceptance.test.tsx +460 -0
- manigent/web/frontend/src/components/project/plan/PlanAcceptance.tsx +111 -0
- manigent/web/frontend/src/components/project/plan/PlanBlockEdit.tsx +111 -0
- manigent/web/frontend/src/components/project/plan/PlanBlockEditing.test.tsx +1132 -0
- manigent/web/frontend/src/components/project/plan/PlanBody.test.tsx +495 -0
- manigent/web/frontend/src/components/project/plan/PlanBody.tsx +992 -0
- manigent/web/frontend/src/components/project/plan/PlanCollide.tsx +254 -0
- manigent/web/frontend/src/components/project/plan/PlanCommentList.test.tsx +420 -0
- manigent/web/frontend/src/components/project/plan/PlanCommentList.tsx +391 -0
- manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.test.tsx +226 -0
- manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.tsx +128 -0
- manigent/web/frontend/src/components/project/plan/PlanDestructiveAccept.test.tsx +209 -0
- manigent/web/frontend/src/components/project/plan/PlanDiff.test.tsx +126 -0
- manigent/web/frontend/src/components/project/plan/PlanDiff.tsx +131 -0
- manigent/web/frontend/src/components/project/plan/PlanDiffSeries.test.tsx +500 -0
- manigent/web/frontend/src/components/project/plan/PlanDiffSeries.tsx +125 -0
- manigent/web/frontend/src/components/project/plan/PlanEditor.mounts.test.tsx +504 -0
- manigent/web/frontend/src/components/project/plan/PlanEditor.tsx +2159 -0
- manigent/web/frontend/src/components/project/plan/PlanHistory.tsx +69 -0
- manigent/web/frontend/src/components/project/plan/PlanInlineNotes.test.tsx +2398 -0
- manigent/web/frontend/src/components/project/plan/PlanNoteBase.test.tsx +215 -0
- manigent/web/frontend/src/components/project/plan/PlanNoteRow.test.tsx +562 -0
- manigent/web/frontend/src/components/project/plan/PlanNoteRow.tsx +908 -0
- manigent/web/frontend/src/components/project/plan/PlanQuotePop.tsx +254 -0
- manigent/web/frontend/src/components/project/plan/PlanStaleReconcile.test.tsx +244 -0
- manigent/web/frontend/src/components/project/plan/PlanSubmitWindow.test.tsx +281 -0
- manigent/web/frontend/src/components/project/plan/PlanTray.tsx +184 -0
- manigent/web/frontend/src/components/project/plan/PlanTrayMirror.test.tsx +242 -0
- manigent/web/frontend/src/components/project/plan/plan.css +1151 -0
- manigent/web/frontend/src/components/project/plan/planAnchor.test.tsx +459 -0
- manigent/web/frontend/src/components/project/plan/planAnchor.tsx +277 -0
- manigent/web/frontend/src/components/project/plan/planBlocks.test.tsx +470 -0
- manigent/web/frontend/src/components/project/plan/planBlocks.ts +242 -0
- manigent/web/frontend/src/components/project/plan/planContrast.test.ts +229 -0
- manigent/web/frontend/src/components/project/plan/planDiffs.test.ts +455 -0
- manigent/web/frontend/src/components/project/plan/planDiffs.ts +358 -0
- manigent/web/frontend/src/components/project/plan/planTestServer.ts +741 -0
- manigent/web/frontend/src/components/project/plan/planTransport.test.ts +160 -0
- manigent/web/frontend/src/components/project/plan/planTrayLogic.test.ts +1201 -0
- manigent/web/frontend/src/components/project/plan/planTrayLogic.ts +1164 -0
- manigent/web/frontend/src/components/publicAgents/PublicAgentsAccount.tsx +162 -0
- manigent/web/frontend/src/components/publicAgents/api.ts +55 -0
- manigent/web/frontend/src/components/publicAgents/parts.tsx +54 -0
- manigent/web/frontend/src/components/publicAgents/publicAgents.css +53 -0
- manigent/web/frontend/src/components/publicAgents/publicAgents.test.tsx +86 -0
- manigent/web/frontend/src/components/publicAgents/widget.test.ts +148 -0
- manigent/web/frontend/src/components/publicAgents/widget.ts +40 -0
- manigent/web/frontend/src/components/publicRequests/AgentDrawer.tsx +101 -0
- manigent/web/frontend/src/components/publicRequests/AgentPanel.tsx +173 -0
- manigent/web/frontend/src/components/publicRequests/AgentRankList.tsx +61 -0
- manigent/web/frontend/src/components/publicRequests/MatchRetainButton.tsx +67 -0
- manigent/web/frontend/src/components/publicRequests/ProgressBoard.tsx +74 -0
- manigent/web/frontend/src/components/publicRequests/PublicRequestsPage.tsx +57 -0
- manigent/web/frontend/src/components/publicRequests/RequestHome.tsx +99 -0
- manigent/web/frontend/src/components/publicRequests/RequestRoom.tsx +126 -0
- manigent/web/frontend/src/components/publicRequests/agentView.ts +65 -0
- manigent/web/frontend/src/components/publicRequests/publicRequests.css +209 -0
- manigent/web/frontend/src/components/publicRequests/publicRequests.test.tsx +172 -0
- manigent/web/frontend/src/components/publicRequests/requestCopy.ts +121 -0
- manigent/web/frontend/src/components/sample-request/SampleIdeaList.test.tsx +190 -0
- manigent/web/frontend/src/components/sample-request/SampleIdeaList.tsx +180 -0
- manigent/web/frontend/src/components/sample-request/SampleRequestBody.tsx +276 -0
- manigent/web/frontend/src/components/sample-request/assistantStarterSamples.ts +62 -0
- manigent/web/frontend/src/components/sample-request/sampleIdeaList.css +106 -0
- manigent/web/frontend/src/components/scheduled/CronPicker.tsx +80 -0
- manigent/web/frontend/src/components/scheduled/ScheduleMessageModal.tsx +341 -0
- manigent/web/frontend/src/components/scheduled/ScheduledMessagesList.tsx +157 -0
- manigent/web/frontend/src/components/settings/AgentSettingsPage.tsx +366 -0
- manigent/web/frontend/src/components/settings/ConnectedAccounts.tsx +277 -0
- manigent/web/frontend/src/components/settings/DefaultModelSection.test.tsx +96 -0
- manigent/web/frontend/src/components/settings/DefaultModelSection.tsx +212 -0
- manigent/web/frontend/src/components/settings/DisconnectConfirmModal.tsx +93 -0
- manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.isolation.test.tsx +66 -0
- manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.tsx +134 -0
- manigent/web/frontend/src/components/settings/InvitableAgentsPicker.tsx +182 -0
- manigent/web/frontend/src/components/settings/KnowledgePacksSection.tsx +867 -0
- manigent/web/frontend/src/components/settings/ModelConfigDialog.catalogDriven.test.tsx +79 -0
- manigent/web/frontend/src/components/settings/ModelConfigDialog.tsx +343 -0
- manigent/web/frontend/src/components/settings/ModelConfigSection.smartRouting.test.tsx +109 -0
- manigent/web/frontend/src/components/settings/ModelConfigSection.tsx +599 -0
- manigent/web/frontend/src/components/settings/ReflectionScheduleSection.tsx +170 -0
- manigent/web/frontend/src/components/settings/UserSettingsPage.tsx +168 -0
- manigent/web/frontend/src/components/settings/agent/AgentStatusStrip.tsx +71 -0
- manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.test.tsx +81 -0
- manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.tsx +62 -0
- manigent/web/frontend/src/components/settings/agent/ChannelIsolationLine.tsx +21 -0
- manigent/web/frontend/src/components/settings/agent/ChannelsSection.test.tsx +879 -0
- manigent/web/frontend/src/components/settings/agent/ChannelsSection.tsx +1124 -0
- manigent/web/frontend/src/components/settings/agent/EnvVarsCard.test.ts +15 -0
- manigent/web/frontend/src/components/settings/agent/EnvVarsCard.tsx +301 -0
- manigent/web/frontend/src/components/settings/agent/ExtraDirsList.tsx +85 -0
- manigent/web/frontend/src/components/settings/agent/GeneralSection.tsx +88 -0
- manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.test.tsx +32 -0
- manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.tsx +30 -0
- manigent/web/frontend/src/components/settings/agent/HubChannelCard.test.tsx +449 -0
- manigent/web/frontend/src/components/settings/agent/HubChannelCard.tsx +312 -0
- manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.test.tsx +83 -0
- manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.tsx +64 -0
- manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.test.tsx +69 -0
- manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.tsx +42 -0
- manigent/web/frontend/src/components/settings/agent/HubTurnOffDialog.tsx +88 -0
- manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.test.tsx +26 -0
- manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.tsx +68 -0
- manigent/web/frontend/src/components/settings/agent/KnowledgeSection.tsx +68 -0
- manigent/web/frontend/src/components/settings/agent/LineChannelCard.test.tsx +428 -0
- manigent/web/frontend/src/components/settings/agent/LineChannelCard.tsx +343 -0
- manigent/web/frontend/src/components/settings/agent/RuntimeSection.test.tsx +164 -0
- manigent/web/frontend/src/components/settings/agent/RuntimeSection.tsx +118 -0
- manigent/web/frontend/src/components/settings/agent/StorageSection.tsx +78 -0
- manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.test.tsx +253 -0
- manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.tsx +401 -0
- manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.test.tsx +462 -0
- manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.tsx +289 -0
- manigent/web/frontend/src/components/settings/agent/agentSettings.test.tsx +109 -0
- manigent/web/frontend/src/components/settings/agent/channelSetupGuides.test.tsx +162 -0
- manigent/web/frontend/src/components/settings/agent/channelSetupGuides.tsx +119 -0
- manigent/web/frontend/src/components/settings/agent/channelsShared.ts +83 -0
- manigent/web/frontend/src/components/settings/agent/fields.tsx +67 -0
- manigent/web/frontend/src/components/settings/agent/sections.ts +36 -0
- manigent/web/frontend/src/components/settings/agent/useAgentSettingsDraft.ts +129 -0
- manigent/web/frontend/src/components/settings/agent/useInstallSelection.ts +38 -0
- manigent/web/frontend/src/components/settings/primitives.tsx +98 -0
- manigent/web/frontend/src/components/shared/ExpandToggle.tsx +45 -0
- manigent/web/frontend/src/components/shared/MarkdownBody.tsx +218 -0
- manigent/web/frontend/src/components/shared/MarkdownBody.untrusted.test.tsx +97 -0
- manigent/web/frontend/src/components/shared/RenderBody.tsx +90 -0
- manigent/web/frontend/src/components/shared/rehypeHideComments.test.tsx +115 -0
- manigent/web/frontend/src/components/shared/rehypeHideComments.ts +124 -0
- manigent/web/frontend/src/components/sharing/AcceptSharePage.tsx +141 -0
- manigent/web/frontend/src/components/sharing/ShareLandingPage.tsx +89 -0
- manigent/web/frontend/src/components/sidebar/AccountChip.test.tsx +337 -0
- manigent/web/frontend/src/components/sidebar/AccountChip.tsx +407 -0
- manigent/web/frontend/src/components/sidebar/ActiveWorkDot.tsx +9 -0
- manigent/web/frontend/src/components/sidebar/AgentProfile.tsx +44 -0
- manigent/web/frontend/src/components/sidebar/AgentRow.tsx +71 -0
- manigent/web/frontend/src/components/sidebar/AgentsPanel.retained.test.tsx +105 -0
- manigent/web/frontend/src/components/sidebar/AgentsPanel.tsx +231 -0
- manigent/web/frontend/src/components/sidebar/AssistantHero.tsx +48 -0
- manigent/web/frontend/src/components/sidebar/ChatroomItem.tsx +48 -0
- manigent/web/frontend/src/components/sidebar/CommandPalette.channelThreads.test.tsx +76 -0
- manigent/web/frontend/src/components/sidebar/CommandPalette.reopen.test.tsx +206 -0
- manigent/web/frontend/src/components/sidebar/CommandPalette.test.tsx +725 -0
- manigent/web/frontend/src/components/sidebar/CommandPalette.tsx +774 -0
- manigent/web/frontend/src/components/sidebar/ComputersPanel.tsx +144 -0
- manigent/web/frontend/src/components/sidebar/DmIcon.tsx +46 -0
- manigent/web/frontend/src/components/sidebar/DmsPanel.tsx +226 -0
- manigent/web/frontend/src/components/sidebar/FrontDeskPanel.channels.test.tsx +582 -0
- manigent/web/frontend/src/components/sidebar/FrontDeskPanel.test.tsx +151 -0
- manigent/web/frontend/src/components/sidebar/FrontDeskPanel.tsx +454 -0
- manigent/web/frontend/src/components/sidebar/HotkeysHelp.tsx +101 -0
- manigent/web/frontend/src/components/sidebar/MessageHitRow.test.tsx +84 -0
- manigent/web/frontend/src/components/sidebar/MessageHitRow.tsx +57 -0
- manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.crossHighlight.test.tsx +247 -0
- manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.delete.test.tsx +273 -0
- manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.tsx +351 -0
- manigent/web/frontend/src/components/sidebar/PanelShell.tsx +48 -0
- manigent/web/frontend/src/components/sidebar/ProjectItem.tsx +161 -0
- manigent/web/frontend/src/components/sidebar/ProjectRow.tsx +151 -0
- manigent/web/frontend/src/components/sidebar/ProjectsPanel.tsx +80 -0
- manigent/web/frontend/src/components/sidebar/PublicPanel.counterpart.test.tsx +85 -0
- manigent/web/frontend/src/components/sidebar/PublicPanel.hub.test.tsx +147 -0
- manigent/web/frontend/src/components/sidebar/PublicPanel.tsx +271 -0
- manigent/web/frontend/src/components/sidebar/RecencyHeader.tsx +17 -0
- manigent/web/frontend/src/components/sidebar/ScheduledPanel.tsx +58 -0
- manigent/web/frontend/src/components/sidebar/SidebarAvatar.tsx +128 -0
- manigent/web/frontend/src/components/sidebar/SidebarRail.tsx +163 -0
- manigent/web/frontend/src/components/sidebar/TeamGroup.tsx +67 -0
- manigent/web/frontend/src/components/sidebar/dmPrefill.test.ts +69 -0
- manigent/web/frontend/src/components/sidebar/dmPrefill.ts +80 -0
- manigent/web/frontend/src/components/sidebar/index.ts +18 -0
- manigent/web/frontend/src/components/sidebar/railShortcuts.ts +18 -0
- manigent/web/frontend/src/components/today/lib.test.ts +70 -0
- manigent/web/frontend/src/components/today/lib.ts +296 -0
- manigent/web/frontend/src/components/today/today.css +113 -0
- manigent/web/frontend/src/components/welcome/RunnerSetup.css +253 -0
- manigent/web/frontend/src/components/welcome/RunnerSetup.tsx +148 -0
- manigent/web/frontend/src/components/welcome/WelcomePage.css +383 -0
- manigent/web/frontend/src/components/welcome/WelcomePage.tsx +329 -0
- manigent/web/frontend/src/components/welcome/welcomeCopy.test.ts +382 -0
- manigent/web/frontend/src/components/welcome/welcomeCopy.ts +313 -0
- manigent/web/frontend/src/config/llmProviders.test.ts +161 -0
- manigent/web/frontend/src/config/llmProviders.ts +109 -0
- manigent/web/frontend/src/config/providerIds.fixture.json +13 -0
- manigent/web/frontend/src/hooks/accountNavigation.test.tsx +138 -0
- manigent/web/frontend/src/hooks/index.ts +10 -0
- manigent/web/frontend/src/hooks/useActiveWorkBootstrap.ts +86 -0
- manigent/web/frontend/src/hooks/useAgentAccess.test.tsx +30 -0
- manigent/web/frontend/src/hooks/useAgentAccess.ts +204 -0
- manigent/web/frontend/src/hooks/useAgentSampleRequests.ts +27 -0
- manigent/web/frontend/src/hooks/useAgentSearch.ts +10 -0
- manigent/web/frontend/src/hooks/useAgents.ts +46 -0
- manigent/web/frontend/src/hooks/useAnnouncements.test.ts +112 -0
- manigent/web/frontend/src/hooks/useAnnouncements.ts +100 -0
- manigent/web/frontend/src/hooks/useAssistantAgent.ts +24 -0
- manigent/web/frontend/src/hooks/useAutoResizeTextarea.ts +18 -0
- manigent/web/frontend/src/hooks/useBridgeDeliveries.test.tsx +79 -0
- manigent/web/frontend/src/hooks/useBriefTabs.ts +122 -0
- manigent/web/frontend/src/hooks/useChannelThreads.ts +69 -0
- manigent/web/frontend/src/hooks/useComposeForm.ts +80 -0
- manigent/web/frontend/src/hooks/useComputers.ts +123 -0
- manigent/web/frontend/src/hooks/useContextFirstItem.ts +118 -0
- manigent/web/frontend/src/hooks/useCreateProject.ts +78 -0
- manigent/web/frontend/src/hooks/useDebouncedValue.ts +16 -0
- manigent/web/frontend/src/hooks/useDeleteProject.ts +27 -0
- manigent/web/frontend/src/hooks/useDeskDispatch.test.tsx +170 -0
- manigent/web/frontend/src/hooks/useDeskDispatch.ts +135 -0
- manigent/web/frontend/src/hooks/useDeskReadState.test.tsx +85 -0
- manigent/web/frontend/src/hooks/useDeskReadState.ts +150 -0
- manigent/web/frontend/src/hooks/useDeskRecentlyDone.test.tsx +222 -0
- manigent/web/frontend/src/hooks/useDeskRecentlyDone.ts +225 -0
- manigent/web/frontend/src/hooks/useDeskTour.ts +37 -0
- manigent/web/frontend/src/hooks/useDeskUpdates.channelThreads.test.tsx +91 -0
- manigent/web/frontend/src/hooks/useDeskUpdates.hubThreads.test.tsx +93 -0
- manigent/web/frontend/src/hooks/useDeskUpdates.ts +353 -0
- manigent/web/frontend/src/hooks/useDisconnectIntegration.ts +23 -0
- manigent/web/frontend/src/hooks/useDmThreads.ts +99 -0
- manigent/web/frontend/src/hooks/useFileDropzone.ts +93 -0
- manigent/web/frontend/src/hooks/useFileUpload.ts +38 -0
- manigent/web/frontend/src/hooks/useFiles.ts +98 -0
- manigent/web/frontend/src/hooks/useHub.test.tsx +237 -0
- manigent/web/frontend/src/hooks/useHub.ts +502 -0
- manigent/web/frontend/src/hooks/useInstall.ts +82 -0
- manigent/web/frontend/src/hooks/useLabels.test.tsx +294 -0
- manigent/web/frontend/src/hooks/useLabels.ts +287 -0
- manigent/web/frontend/src/hooks/useLogout.ts +49 -0
- manigent/web/frontend/src/hooks/useMentionDetection.ts +90 -0
- manigent/web/frontend/src/hooks/useMessageAttachments.deskLimit.test.ts +264 -0
- manigent/web/frontend/src/hooks/useMessageAttachments.ts +283 -0
- manigent/web/frontend/src/hooks/useMessageDeepLink.test.tsx +70 -0
- manigent/web/frontend/src/hooks/useMessageDeepLink.ts +32 -0
- manigent/web/frontend/src/hooks/useMessageSearch.ts +78 -0
- manigent/web/frontend/src/hooks/useModelConfigs.ts +178 -0
- manigent/web/frontend/src/hooks/useMyAgents.ts +18 -0
- manigent/web/frontend/src/hooks/usePointerReorder.ts +270 -0
- manigent/web/frontend/src/hooks/usePrefersReducedMotion.ts +18 -0
- manigent/web/frontend/src/hooks/useProjectPlan.ts +36 -0
- manigent/web/frontend/src/hooks/useProjectReport.ts +19 -0
- manigent/web/frontend/src/hooks/useProjects.ts +104 -0
- manigent/web/frontend/src/hooks/useProjectsWithChatrooms.ts +97 -0
- manigent/web/frontend/src/hooks/usePublicRequests.ts +89 -0
- manigent/web/frontend/src/hooks/useRecentAgents.ts +38 -0
- manigent/web/frontend/src/hooks/useRenameProject.test.tsx +185 -0
- manigent/web/frontend/src/hooks/useRenameProject.ts +192 -0
- manigent/web/frontend/src/hooks/useRoomTrust.ts +56 -0
- manigent/web/frontend/src/hooks/useSharing.ts +46 -0
- manigent/web/frontend/src/hooks/useSopRecents.ts +55 -0
- manigent/web/frontend/src/hooks/useSopSchedules.ts +25 -0
- manigent/web/frontend/src/hooks/useSops.ts +99 -0
- manigent/web/frontend/src/hooks/useSwitchAccount.ts +41 -0
- manigent/web/frontend/src/hooks/useTeamGroups.ts +48 -0
- manigent/web/frontend/src/hooks/useThreadOutbox.ts +68 -0
- manigent/web/frontend/src/hooks/useTodos.add.test.tsx +89 -0
- manigent/web/frontend/src/hooks/useTodos.linkProject.test.tsx +115 -0
- manigent/web/frontend/src/hooks/useTodos.saveDraft.test.tsx +188 -0
- manigent/web/frontend/src/hooks/useTodos.ts +396 -0
- manigent/web/frontend/src/hooks/useUntrustedBodies.test.tsx +87 -0
- manigent/web/frontend/src/hooks/useUntrustedBodies.ts +51 -0
- manigent/web/frontend/src/hooks/useWebSocket.briefTab.test.tsx +148 -0
- manigent/web/frontend/src/hooks/useWebSocket.channelThreads.test.tsx +82 -0
- manigent/web/frontend/src/hooks/useWebSocket.hostSync.test.tsx +92 -0
- manigent/web/frontend/src/hooks/useWebSocket.hub.test.tsx +114 -0
- manigent/web/frontend/src/hooks/useWebSocket.labelSync.test.tsx +168 -0
- manigent/web/frontend/src/hooks/useWebSocket.plan.test.tsx +168 -0
- manigent/web/frontend/src/hooks/useWebSocket.reconnect.test.tsx +166 -0
- manigent/web/frontend/src/hooks/useWebSocket.runnerVersions.test.tsx +167 -0
- manigent/web/frontend/src/hooks/useWebSocket.ts +495 -0
- manigent/web/frontend/src/hooks/useWindowFileDropGuard.ts +33 -0
- manigent/web/frontend/src/landing.tsx +11 -0
- manigent/web/frontend/src/main.tsx +17 -0
- manigent/web/frontend/src/private-beta.tsx +10 -0
- manigent/web/frontend/src/public.tsx +42 -0
- manigent/web/frontend/src/queryClient.ts +16 -0
- manigent/web/frontend/src/stores/activeWorkStore.ts +88 -0
- manigent/web/frontend/src/stores/announcementStore.test.ts +217 -0
- manigent/web/frontend/src/stores/announcementStore.ts +169 -0
- manigent/web/frontend/src/stores/authStore.multiSession.test.ts +304 -0
- manigent/web/frontend/src/stores/authStore.test.ts +235 -0
- manigent/web/frontend/src/stores/authStore.ts +693 -0
- manigent/web/frontend/src/stores/deskLabelViewStore.test.ts +162 -0
- manigent/web/frontend/src/stores/deskLabelViewStore.ts +234 -0
- manigent/web/frontend/src/stores/deskLinkStore.ts +54 -0
- manigent/web/frontend/src/stores/deskStore.test.ts +53 -0
- manigent/web/frontend/src/stores/deskStore.ts +93 -0
- manigent/web/frontend/src/stores/heldPostsStore.ts +78 -0
- manigent/web/frontend/src/stores/mcpAuthStore.ts +52 -0
- manigent/web/frontend/src/stores/modelSelectionStore.ts +33 -0
- manigent/web/frontend/src/stores/perUserStores.ts +79 -0
- manigent/web/frontend/src/stores/runnerVersionStore.test.ts +81 -0
- manigent/web/frontend/src/stores/runnerVersionStore.ts +60 -0
- manigent/web/frontend/src/stores/skillAuthStore.ts +54 -0
- manigent/web/frontend/src/stores/switchReset.test.ts +236 -0
- manigent/web/frontend/src/stores/tokenExp.ts +32 -0
- manigent/web/frontend/src/stores/uiStore.ts +186 -0
- manigent/web/frontend/src/stores/unreadStore.ts +204 -0
- manigent/web/frontend/src/stores/unsavedGuard.ts +22 -0
- manigent/web/frontend/src/styles/design/fonts/JetBrainsMono-Latin.woff2 +0 -0
- manigent/web/frontend/src/styles/design/fonts/Nunito-Latin.woff2 +0 -0
- manigent/web/frontend/src/styles/design/fonts/Nunito-LatinExt.woff2 +0 -0
- manigent/web/frontend/src/styles/design/tokens.css +160 -0
- manigent/web/frontend/src/styles/homepage.css +242 -0
- manigent/web/frontend/src/styles/index.css +126 -0
- manigent/web/frontend/src/styles/landing.css +52 -0
- manigent/web/frontend/src/test/authFixtures.ts +154 -0
- manigent/web/frontend/src/test/fetchHarness.ts +203 -0
- manigent/web/frontend/src/test/fixtures/channelThreads.smoke.json +42 -0
- manigent/web/frontend/src/test/hubFixtures.ts +212 -0
- manigent/web/frontend/src/test/queryWrapper.tsx +9 -0
- manigent/web/frontend/src/test/suiteHygiene.test.ts +212 -0
- manigent/web/frontend/src/types/channelThreadName.test.ts +16 -0
- manigent/web/frontend/src/types/index.ts +903 -0
- manigent/web/frontend/src/utils/agentAccessCopy.test.ts +53 -0
- manigent/web/frontend/src/utils/agentAccessCopy.ts +231 -0
- manigent/web/frontend/src/utils/agentDisplay.ts +81 -0
- manigent/web/frontend/src/utils/briefSchedule.ts +25 -0
- manigent/web/frontend/src/utils/channelThreads.test.ts +109 -0
- manigent/web/frontend/src/utils/channelThreads.ts +179 -0
- manigent/web/frontend/src/utils/chatroomDisplay.ts +11 -0
- manigent/web/frontend/src/utils/deskLinks.test.ts +155 -0
- manigent/web/frontend/src/utils/deskLinks.ts +115 -0
- manigent/web/frontend/src/utils/deskRecipient.ts +29 -0
- manigent/web/frontend/src/utils/formatDate.ts +102 -0
- manigent/web/frontend/src/utils/frontDesk.ts +65 -0
- manigent/web/frontend/src/utils/hubChannelView.test.ts +178 -0
- manigent/web/frontend/src/utils/hubChannelView.ts +307 -0
- manigent/web/frontend/src/utils/hubDelivery.test.ts +105 -0
- manigent/web/frontend/src/utils/hubDelivery.ts +137 -0
- manigent/web/frontend/src/utils/hubDisplay.test.ts +157 -0
- manigent/web/frontend/src/utils/hubDisplay.ts +221 -0
- manigent/web/frontend/src/utils/hubThread.test.ts +184 -0
- manigent/web/frontend/src/utils/hubThread.ts +247 -0
- manigent/web/frontend/src/utils/labelErrors.test.ts +127 -0
- manigent/web/frontend/src/utils/labelErrors.ts +123 -0
- manigent/web/frontend/src/utils/labelView.test.ts +846 -0
- manigent/web/frontend/src/utils/labelView.ts +760 -0
- manigent/web/frontend/src/utils/legalUrls.ts +11 -0
- manigent/web/frontend/src/utils/messageSender.test.ts +32 -0
- manigent/web/frontend/src/utils/messageSender.ts +29 -0
- manigent/web/frontend/src/utils/oauthScopeCopy.ts +73 -0
- manigent/web/frontend/src/utils/personalize.ts +49 -0
- manigent/web/frontend/src/utils/reachableAgents.test.ts +36 -0
- manigent/web/frontend/src/utils/reachableAgents.ts +24 -0
- manigent/web/frontend/src/utils/recencyBuckets.ts +61 -0
- manigent/web/frontend/src/utils/roomTrust.test.ts +94 -0
- manigent/web/frontend/src/utils/roomTrust.ts +111 -0
- manigent/web/frontend/src/utils/snippetHighlight.test.ts +63 -0
- manigent/web/frontend/src/utils/snippetHighlight.ts +42 -0
- manigent/web/frontend/src/utils/sopRecipient.ts +37 -0
- manigent/web/frontend/src/utils/sopRequest.test.ts +91 -0
- manigent/web/frontend/src/utils/sopRequest.ts +68 -0
- manigent/web/frontend/src/utils/sopSchedule.ts +26 -0
- manigent/web/frontend/src/utils/sopSection.test.ts +148 -0
- manigent/web/frontend/src/utils/sopSection.ts +143 -0
- manigent/web/frontend/src/utils/textSearch.test.ts +60 -0
- manigent/web/frontend/src/utils/textSearch.ts +43 -0
- manigent/web/frontend/src/utils/thoughtPartner.ts +26 -0
- manigent/web/frontend/src/utils/threadName.test.ts +131 -0
- manigent/web/frontend/src/utils/threadName.ts +122 -0
- manigent/web/frontend/src/utils/ticketView.test.ts +296 -0
- manigent/web/frontend/src/utils/ticketView.ts +175 -0
- manigent/web/frontend/src/utils/todoAttachments.desiredState.test.ts +180 -0
- manigent/web/frontend/src/utils/todoAttachments.ts +126 -0
- manigent/web/frontend/src/utils/todoDraft.test.ts +84 -0
- manigent/web/frontend/src/utils/todoDraft.ts +46 -0
- manigent/web/frontend/src/utils/todoSearch.test.ts +82 -0
- manigent/web/frontend/src/utils/todoSearch.ts +43 -0
- manigent/web/frontend/src/utils/versionMatch.test.ts +295 -0
- manigent/web/frontend/src/utils/versionMatch.ts +245 -0
- manigent/web/frontend/src/vite-env.d.ts +1 -0
- manigent/web/frontend/tailwind.config.js +86 -0
- manigent/web/frontend/tos.html +188 -0
- manigent/web/frontend/tsconfig.dts.json +25 -0
- manigent/web/frontend/tsconfig.json +26 -0
- manigent/web/frontend/tsconfig.node.json +11 -0
- manigent/web/frontend/vite.config.ts +41 -0
- manigent/web/frontend/vitest.config.ts +16 -0
- manigent/ws_edge/app.py +514 -0
- manigent/ws_edge/auth.py +104 -0
- manigent/ws_edge/link.py +282 -0
- manigent/ws_edge/registry.py +206 -0
- manigent/ws_edge/terminal_relay.py +269 -0
- manigent_server-0.1.0.dist-info/METADATA +138 -0
- manigent_server-0.1.0.dist-info/RECORD +1004 -0
- manigent_server-0.1.0.dist-info/WHEEL +4 -0
- manigent_server-0.1.0.dist-info/entry_points.txt +2 -0
- manigent_server-0.1.0.dist-info/licenses/LICENSE +68 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type ChangeEvent,
|
|
8
|
+
} from 'react'
|
|
9
|
+
import { createPortal } from 'react-dom'
|
|
10
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
11
|
+
import { ArrowLeft, ArrowRight, ArrowUp, Check, CornerUpRight, FileText, Paperclip, Send } from 'lucide-react'
|
|
12
|
+
import Avatar from '../common/Avatar'
|
|
13
|
+
import AgentNameLink from '../agent/AgentNameLink'
|
|
14
|
+
import MarkdownBody from '../shared/MarkdownBody'
|
|
15
|
+
import { useRoomTrust } from '../../hooks/useRoomTrust'
|
|
16
|
+
import { RenderBody } from '../shared/RenderBody'
|
|
17
|
+
import FileViewerModal from '../files/FileViewerModal'
|
|
18
|
+
import DropOverlay from '../common/DropOverlay'
|
|
19
|
+
import StagedAttachments from '../chatroom/StagedAttachments'
|
|
20
|
+
import ForwardModal from '../chatroom/ForwardModal'
|
|
21
|
+
import PromptEditor, { type PromptEditorHandle } from '../composer/PromptEditor'
|
|
22
|
+
import { useMessageAttachments } from '../../hooks/useMessageAttachments'
|
|
23
|
+
import { useFileDropzone } from '../../hooks/useFileDropzone'
|
|
24
|
+
import { sendUserMessage } from '../../api/endpoints/messages'
|
|
25
|
+
import { sendHubDraft } from '../../hooks/useHub'
|
|
26
|
+
import { completeProject } from '../../api/endpoints/projects'
|
|
27
|
+
import { useProjectReport } from '../../hooks/useProjectReport'
|
|
28
|
+
import { PlanBand } from './PlanBand'
|
|
29
|
+
import { type PlanSendState } from '../project/plan/PlanEditor'
|
|
30
|
+
import { PhaseRail } from './PhaseRail'
|
|
31
|
+
import { CardBand } from './CardBand'
|
|
32
|
+
import {
|
|
33
|
+
bandsPresent,
|
|
34
|
+
defaultOpenBands,
|
|
35
|
+
isGated,
|
|
36
|
+
planFoldMeta,
|
|
37
|
+
type BandId,
|
|
38
|
+
} from './deskBands'
|
|
39
|
+
import { useMarkDeskRead } from '../../hooks/useDeskReadState'
|
|
40
|
+
import { type DeskFile, type DeskUpdate } from '../../hooks/useDeskUpdates'
|
|
41
|
+
import { AgentStatus } from '../../types'
|
|
42
|
+
|
|
43
|
+
// The card leads with WHAT (the title) and follows with WHO (the agent byline):
|
|
44
|
+
// scanning the desk is a scan of subjects, not of names. No lane pill and no
|
|
45
|
+
// source subtitle — the desk section a card sits in already says its lane
|
|
46
|
+
// (Review / Working / Recently done), and the source repeated the title.
|
|
47
|
+
|
|
48
|
+
// Imperative surface so DeskPage's keyboard shortcuts can drive a card's own
|
|
49
|
+
// behaviors without hoisting all of its local state.
|
|
50
|
+
export interface UpdateCardHandle {
|
|
51
|
+
toggleExpand: () => void
|
|
52
|
+
toggleReply: () => void
|
|
53
|
+
dismiss: () => void
|
|
54
|
+
/** Opens the Forward dialog; no-op on a card with nothing forwardable. */
|
|
55
|
+
forward: () => void
|
|
56
|
+
open: () => void
|
|
57
|
+
scrollIntoView: () => void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// One update: review + reply-in-place, mark done, or open the thread. A card is
|
|
61
|
+
// a DM or Project; "Send Back" posts straight to its user-communication room.
|
|
62
|
+
const UpdateCard = forwardRef<
|
|
63
|
+
UpdateCardHandle,
|
|
64
|
+
{
|
|
65
|
+
item: DeskUpdate
|
|
66
|
+
/** `tab` is the destination surface within the project — omitted for the
|
|
67
|
+
* ordinary "Open project", `'plan'` for the PLAN band's release control,
|
|
68
|
+
* which must land the user on the document their answer is owed to. */
|
|
69
|
+
onOpen: (item: DeskUpdate, tab?: 'plan') => void
|
|
70
|
+
onToast: (msg: string) => void
|
|
71
|
+
// Fired synchronously the instant the one removing action (Done) actually
|
|
72
|
+
// REMOVES the card — after the complete POST has succeeded, before the
|
|
73
|
+
// read-state write or any refetch. Lets the desk snapshot the current card
|
|
74
|
+
// order for cursor recovery. No-op unless this card is selected.
|
|
75
|
+
//
|
|
76
|
+
// It moved behind the POST when Done started completing the project: a
|
|
77
|
+
// failed complete leaves the card on the desk, and a snapshot taken for a
|
|
78
|
+
// removal that never happened would sit stale until some later action
|
|
79
|
+
// consumed it and recovered the cursor to the wrong row.
|
|
80
|
+
onActStart?: (projectId: string) => void
|
|
81
|
+
// Done (read-lane) cards only: hand this card up to the desk, which
|
|
82
|
+
// drafts a "turn this into an SOP" request to the owner's assistant in the
|
|
83
|
+
// top composer. Omitted ⇒ the button is not rendered.
|
|
84
|
+
onSaveAsSop?: (item: DeskUpdate) => void
|
|
85
|
+
selected?: boolean
|
|
86
|
+
/** A hovered to-do on the rail lists this card's `projectId` in its
|
|
87
|
+
* `project_ids`. Paints the cross-highlight. Orthogonal to `selected` —
|
|
88
|
+
* both can be true at once, and they paint on different CSS channels
|
|
89
|
+
* (outline vs border+ring) so they stack instead of fighting. */
|
|
90
|
+
linked?: boolean
|
|
91
|
+
/** Pointer entered / left this card. Hands up `item.projectId` on enter and
|
|
92
|
+
* `null` on leave; the DESK decides which side of the pair lights, because
|
|
93
|
+
* it is the only place holding both the cards and the to-dos.
|
|
94
|
+
*
|
|
95
|
+
* Wired to mouseenter/mouseleave, NOT the pointer events: this is a mouse
|
|
96
|
+
* affordance by design. Touch fires an enter with no matching leave, which
|
|
97
|
+
* would strand the highlight lit with nothing left to clear it. */
|
|
98
|
+
onHover?: (projectId: string | null) => void
|
|
99
|
+
}
|
|
100
|
+
>(function UpdateCard(
|
|
101
|
+
{ item, onOpen, onToast, onActStart, onSaveAsSop, selected, linked, onHover },
|
|
102
|
+
ref,
|
|
103
|
+
) {
|
|
104
|
+
const { markRead } = useMarkDeskRead()
|
|
105
|
+
const queryClient = useQueryClient()
|
|
106
|
+
const [replying, setReplying] = useState(false)
|
|
107
|
+
const [text, setText] = useState('')
|
|
108
|
+
const [acted, setActed] = useState(false)
|
|
109
|
+
const [expanded, setExpanded] = useState(false)
|
|
110
|
+
const [forwarding, setForwarding] = useState(false)
|
|
111
|
+
// Same trust rule as the room: a Hub thread whole, and the quote's author
|
|
112
|
+
// per author (a Telegram or email contact, a requester, another account's
|
|
113
|
+
// agent). The quote's files rode in with it, so they share its answer.
|
|
114
|
+
const trust = useRoomTrust(item.projectId, 'user-communication')
|
|
115
|
+
const untrusted = item.quoteOutsideIsolated || (item.quoteAuthorId === null
|
|
116
|
+
? trust.unknownAuthorUntrusted
|
|
117
|
+
: trust.isUntrusted(item.quoteAuthorId))
|
|
118
|
+
// Forwardable = the card quotes an agent's message. The user's own pending
|
|
119
|
+
// request is not something to hand on, and an empty thread has nothing.
|
|
120
|
+
// Forwarding is additive — it never marks the card done — so every lane
|
|
121
|
+
// carries it, history included. Never for untrusted content: the server
|
|
122
|
+
// quotes the body word for word into the owner's DM, which draws it trusted.
|
|
123
|
+
const canForward = !!item.latestMessageId && !item.quoteIsYours && !untrusted
|
|
124
|
+
// Index into item.files of the file open in the viewer; null = closed.
|
|
125
|
+
// Owned by the card (not per pill) so ← / → can step across its files.
|
|
126
|
+
const [openFileIdx, setOpenFileIdx] = useState<number | null>(null)
|
|
127
|
+
const openFile = openFileIdx !== null ? item.files[openFileIdx] : undefined
|
|
128
|
+
const editorRef = useRef<PromptEditorHandle | null>(null)
|
|
129
|
+
const rootRef = useRef<HTMLDivElement | null>(null)
|
|
130
|
+
const fileInputRef = useRef<HTMLInputElement | null>(null)
|
|
131
|
+
|
|
132
|
+
// Attachment staging for the inline reply — same primitives (and 20 MB cap)
|
|
133
|
+
// as the top-of-desk DeskComposer, so a dropped/picked file rides the reply's
|
|
134
|
+
// atomic send. Each open card owns its own instance; staged files never leak
|
|
135
|
+
// between cards.
|
|
136
|
+
const { stagedFiles, files, addFiles, removeStagedFile, clearStagedFiles, attachmentError } =
|
|
137
|
+
useMessageAttachments()
|
|
138
|
+
|
|
139
|
+
const isProgress = item.lane === 'progress'
|
|
140
|
+
// "Recently done" cards are historical — Open-only. Re-marking a card done
|
|
141
|
+
// is nonsense, so Done / Send Back are suppressed (M1 ruling 3); the card
|
|
142
|
+
// keeps the same layout as its live siblings, minus the removing actions.
|
|
143
|
+
const isRead = item.lane === 'read'
|
|
144
|
+
const actionable = !isProgress && !isRead
|
|
145
|
+
|
|
146
|
+
// THE THREE BANDS, in the user's fixed order: REPORT, PLAN, RESPONSE. Absent
|
|
147
|
+
// bands are OMITTED ROWS, never placeholders.
|
|
148
|
+
//
|
|
149
|
+
// `opened` is seeded on mount from `defaultOpenBands`, which opens at most
|
|
150
|
+
// ONE fetching band (report or plan) — two would be two fetches per card —
|
|
151
|
+
// plus RESPONSE alongside a report, which costs nothing. Folding is never
|
|
152
|
+
// destructive and every band is reachable by click.
|
|
153
|
+
const present = bandsPresent(item)
|
|
154
|
+
const [opened, setOpened] = useState<Partial<Record<BandId, boolean>>>(() =>
|
|
155
|
+
Object.fromEntries(defaultOpenBands(item).map((id) => [id, true])),
|
|
156
|
+
)
|
|
157
|
+
const toggleBand = (id: BandId) => setOpened((o) => ({ ...o, [id]: !o[id] }))
|
|
158
|
+
|
|
159
|
+
// A REPORT THAT ARRIVES AFTER MOUNT re-opens REPORT and RESPONSE. The seed
|
|
160
|
+
// above runs once, so without this a card mounted while the project was
|
|
161
|
+
// still running keeps its earlier choice and the new report stays folded
|
|
162
|
+
// until a reload. Keyed on the publish stamp, so a republished report opens
|
|
163
|
+
// again too; any other fold the user made is left alone.
|
|
164
|
+
const reportAt = item.reportPublishedAt
|
|
165
|
+
const prevReportAt = useRef(reportAt)
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (reportAt && reportAt !== prevReportAt.current) {
|
|
168
|
+
setOpened((o) => ({ ...o, report: true, response: true }))
|
|
169
|
+
}
|
|
170
|
+
prevReportAt.current = reportAt
|
|
171
|
+
}, [reportAt])
|
|
172
|
+
|
|
173
|
+
// THE PLAN EDITOR'S OPEN SIGNAL — A COUNTER, NOT A BOOLEAN. The compact
|
|
174
|
+
// editor is UNMOUNTED while the PLAN band is folded (that is the request-cost
|
|
175
|
+
// invariant two comments down), so it reads this ON MOUNT; a boolean would
|
|
176
|
+
// need someone to reset it and could not fire a second time, and unfolding
|
|
177
|
+
// the band a second time is an ordinary thing to do.
|
|
178
|
+
//
|
|
179
|
+
// TWO MECHANISMS IN ONE ORDER, and they cannot race because the second only
|
|
180
|
+
// ever runs inside the first: UNFOLDING the PLAN band MOUNTS the editor (the
|
|
181
|
+
// precondition), and the counter SELECTS the deviation's section (the
|
|
182
|
+
// action). Incrementing on the unfold is what makes that ordering structural
|
|
183
|
+
// rather than a convention two call sites have to keep.
|
|
184
|
+
const [planOpenSignal, setPlanOpenSignal] = useState(0)
|
|
185
|
+
const togglePlanBand = () => {
|
|
186
|
+
if (!opened.plan) setPlanOpenSignal((n) => n + 1)
|
|
187
|
+
toggleBand('plan')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** THE BATCH THE PLAN EDITOR IS HOLDING, reported up from inside the PLAN
|
|
191
|
+
* band so this card's own action row can send it. `null` whenever there is
|
|
192
|
+
* no batch — including while the band is folded, because the editor is
|
|
193
|
+
* genuinely unmounted then and says so on the way out.
|
|
194
|
+
*
|
|
195
|
+
* THIS IS THE ONE PIECE OF REAL PLUMBING IN THE CHANGE, and it buys the rule
|
|
196
|
+
* the row below depends on: **exactly one primary action per card, and it is
|
|
197
|
+
* always in the same place.** `Send review` used to live four inches up,
|
|
198
|
+
* inside a box that flows in the card's scroll, while the row that never
|
|
199
|
+
* moves carried `Done` and `Send Back` — so on any card with a real review
|
|
200
|
+
* the two terminal verbs were the visible ones and the review's own button
|
|
201
|
+
* was the one you had to go find.
|
|
202
|
+
*
|
|
203
|
+
* It holds no rows and no notes. See `PlanSendState`: a card that could read
|
|
204
|
+
* the batch could start deciding it, and the editor is the one write funnel. */
|
|
205
|
+
const [planSend, setPlanSend] = useState<PlanSendState | null>(null)
|
|
206
|
+
|
|
207
|
+
/** ARMED = `Mark Done` has been pressed once while a review was staged, and
|
|
208
|
+
* the button is now asking whether to throw that review away.
|
|
209
|
+
*
|
|
210
|
+
* THE ONE STATE WHERE MARK DONE DESTROYS SOMETHING THE USER TYPED, and so
|
|
211
|
+
* the one place it asks twice. Everything else it clears is recoverable:
|
|
212
|
+
* completing is undone by replying into the thread, and an unanswered plan
|
|
213
|
+
* note is still there when the project reopens. Staged rows are not — they
|
|
214
|
+
* live in the editor and nothing has posted them.
|
|
215
|
+
*
|
|
216
|
+
* It is not a dialog and not a second surface: the button relabels itself in
|
|
217
|
+
* place, and one more press does what the label now says. */
|
|
218
|
+
const [armedDiscard, setArmedDiscard] = useState(false)
|
|
219
|
+
|
|
220
|
+
// The question expires with its subject. If the batch empties — sent,
|
|
221
|
+
// discarded in the tray, or the PLAN band folded and unmounted the editor —
|
|
222
|
+
// an armed button would be asking about a review that no longer exists.
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
if (!planSend) setArmedDiscard(false)
|
|
225
|
+
}, [planSend])
|
|
226
|
+
|
|
227
|
+
// THE DEVIATION PREDICATE, WRITTEN ONCE PER CARD and passed down from here.
|
|
228
|
+
// All five carriers that replace the reference design's promotion-to-focus
|
|
229
|
+
// fire off this and nothing else: the card's urgent left rule, the rail's
|
|
230
|
+
// reddened hint, the PLAN band's warn tone, the PLAN band being
|
|
231
|
+
// default-expanded, and the `.plan-dev` alarm itself — which the editor now
|
|
232
|
+
// renders off its own notes, so this card no longer feeds it anything.
|
|
233
|
+
const offContract = !!item.plan?.deviation
|
|
234
|
+
|
|
235
|
+
// THE HALTED CONJUNCTION, computed here — at its ONE display site — rather
|
|
236
|
+
// than carried as a flag. There is no `halted` member on the wire and none is
|
|
237
|
+
// invented here or anywhere else.
|
|
238
|
+
const gated = isGated(item.plan)
|
|
239
|
+
|
|
240
|
+
/** HOW MANY PLAN NOTES ARE WAITING ON THE USER. Read off the summary the card
|
|
241
|
+
* already carries — `plan.open_notes_for_you`, the same integer the server's
|
|
242
|
+
* execution gate refuses on — and nothing is added to it. A DM has no plan
|
|
243
|
+
* and answers 0, which is right: there is nothing to review, so `Mark Done`
|
|
244
|
+
* is the primary.
|
|
245
|
+
*
|
|
246
|
+
* IT DECIDES WHICH BUTTON IS PRIMARY, NOT WHICH BUTTONS EXIST. `Mark Done`
|
|
247
|
+
* is on every actionable card; while a review is waiting it simply is not
|
|
248
|
+
* the loudest thing in the row. There is no `accepted` check anywhere here,
|
|
249
|
+
* deliberately: a second condition that happened to agree with the count is
|
|
250
|
+
* what the whole change removed from the server, and re-deriving it here
|
|
251
|
+
* would put it back. */
|
|
252
|
+
const planOpenForYou = item.plan?.open_notes_for_you ?? 0
|
|
253
|
+
|
|
254
|
+
// REQUEST COST — this is the design, not an optimisation. A folded band
|
|
255
|
+
// issues NO request: the folded row renders entirely from `PlanSummary` and
|
|
256
|
+
// the report stamp, both already on the card off the project list. The desk
|
|
257
|
+
// makes zero plan requests today and at most ONE report request per card
|
|
258
|
+
// here, and only once the user opens that band.
|
|
259
|
+
//
|
|
260
|
+
// WAVE 2 DEPENDS ON THIS INVARIANT HOLDING: the compact `PlanEditor` mounts
|
|
261
|
+
// only while the PLAN band is expanded, so a folded PLAN band costs zero
|
|
262
|
+
// `GET …/plan`. If that is ever removed, a desk of twelve plan-bearing cards
|
|
263
|
+
// fires twelve plan requests on load. It is also why the editor's open signal
|
|
264
|
+
// must be a COUNTER rather than a boolean — an unmounted editor reads it on
|
|
265
|
+
// mount.
|
|
266
|
+
const reportBand = present.includes('report')
|
|
267
|
+
const { data: report } = useProjectReport(
|
|
268
|
+
reportBand && opened.report ? item.projectId : undefined,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (replying) editorRef.current?.focusEnd()
|
|
273
|
+
}, [replying])
|
|
274
|
+
|
|
275
|
+
const dest = item.kind === 'project' ? `#${item.sourceName}` : `@${item.agentName}`
|
|
276
|
+
|
|
277
|
+
// Play the card's leave animation, then mark the card done: optimistically
|
|
278
|
+
// write the watermark to the store (so useDeskUpdates drops the card) and PUT
|
|
279
|
+
// it to the server, which broadcasts to the owner's other sessions. On a
|
|
280
|
+
// failed PUT the store rolls back — the card reappears — and we toast the
|
|
281
|
+
// error.
|
|
282
|
+
//
|
|
283
|
+
// `dismiss()` is the ONLY caller. That is what makes "Recently done"
|
|
284
|
+
// honest: every row in the read-state store is a Done, never a Send Back.
|
|
285
|
+
const animateOut = (toastMsg: string) => {
|
|
286
|
+
setActed(true)
|
|
287
|
+
onToast(toastMsg)
|
|
288
|
+
window.setTimeout(() => markRead(item, onToast), 340)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Send Back does NOT mark the card done and does not animate it away. The POST
|
|
292
|
+
// synchronously creates pending work for the coordinator (a desk reply carries
|
|
293
|
+
// no @mention, so messages.py fills in the project's coordinator as the
|
|
294
|
+
// implicit responder), and the resulting `active_participants` broadcast flips
|
|
295
|
+
// the card's lane to `progress` — the first branch in useDeskUpdates, so it
|
|
296
|
+
// beats every other lane. The card stays on the desk and re-lanes from
|
|
297
|
+
// *Review* to *Working*, which is where the work actually is.
|
|
298
|
+
//
|
|
299
|
+
// A hub thread is written only through the hub send route (the user-message
|
|
300
|
+
// route answers `use_hub_send`), so its Send Back goes there.
|
|
301
|
+
const sendMutation = useMutation({
|
|
302
|
+
mutationFn: async ({ content, files }: { content: string; files: File[] }) => {
|
|
303
|
+
if (item.hubThread) await sendHubDraft(queryClient, item.projectId, content, files)
|
|
304
|
+
else await sendUserMessage(item.projectId, 'user-communication', { content, files })
|
|
305
|
+
},
|
|
306
|
+
onSuccess: () => {
|
|
307
|
+
setReplying(false)
|
|
308
|
+
setText('')
|
|
309
|
+
editorRef.current?.clear()
|
|
310
|
+
clearStagedFiles()
|
|
311
|
+
onToast(`Sent back to ${dest} — @${item.agentName} is on it.`)
|
|
312
|
+
},
|
|
313
|
+
onError: (err: Error) => onToast(err.message || 'Failed to send back'),
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// Drag files onto the reply block → same staging path as the paperclip.
|
|
317
|
+
// Disabled while a send is in flight.
|
|
318
|
+
const { isDragging, dragHandlers } = useFileDropzone({
|
|
319
|
+
onFiles: addFiles,
|
|
320
|
+
disabled: sendMutation.isPending,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
// Send text + staged files atomically. A file-only reply (no text) is allowed,
|
|
324
|
+
// so the guard passes when EITHER text OR at least one file is present.
|
|
325
|
+
const send = () => {
|
|
326
|
+
const content = text.trim()
|
|
327
|
+
if ((!content && files.length === 0) || sendMutation.isPending) return
|
|
328
|
+
// Never ship a half-filled template: pulse the empty blanks and open the
|
|
329
|
+
// first instead of sending "[Deadline]" to an agent.
|
|
330
|
+
if (editorRef.current?.nudgeEmpty()) return
|
|
331
|
+
// No onActStart here: Send Back no longer removes the card, so the cursor
|
|
332
|
+
// never has to recover from it. Freezing the order now would leave a stale
|
|
333
|
+
// snapshot to be consumed by whatever removes the card later.
|
|
334
|
+
sendMutation.mutate({ content, files })
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// <input type=file> → stage picked files, then reset the value so the same
|
|
338
|
+
// file can be re-picked immediately.
|
|
339
|
+
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
340
|
+
if (e.target.files && e.target.files.length > 0) addFiles(e.target.files)
|
|
341
|
+
e.target.value = ''
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Cancel: drop the draft AND any staged files for this card.
|
|
345
|
+
const cancelReply = () => {
|
|
346
|
+
setReplying(false)
|
|
347
|
+
setText('')
|
|
348
|
+
editorRef.current?.clear()
|
|
349
|
+
clearStagedFiles()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* DONE = COMPLETE THE PROJECT, THEN CLEAR THE CARD. Two writes, in that
|
|
354
|
+
* order, and the order is the whole design.
|
|
355
|
+
*
|
|
356
|
+
* The button used to write only the read watermark. The card left the desk,
|
|
357
|
+
* but the project stayed `active` and the ticket chip on any to-do linked to
|
|
358
|
+
* it still read Working — two surfaces on one screen disagreeing about
|
|
359
|
+
* whether the thing was finished. The completion is the missing half.
|
|
360
|
+
*
|
|
361
|
+
* COMPLETE FIRST. `markRead` is what makes the card disappear, and it already
|
|
362
|
+
* rolls back and restores the card if its PUT fails. If the completion ran
|
|
363
|
+
* second and failed, the card would be gone with the chip still reading
|
|
364
|
+
* Working and nothing left on screen to say so. Failing first means the card
|
|
365
|
+
* stays put and the user sees the error.
|
|
366
|
+
*
|
|
367
|
+
* NO KIND GATE. Both `dm` and `project` cards complete — a card is exactly
|
|
368
|
+
* one project (the desk keys them `${projectId}|user-communication`), and the
|
|
369
|
+
* lane gate above already withholds Done from cards whose agents are
|
|
370
|
+
* mid-turn (`progress`) and from history (`read`).
|
|
371
|
+
*
|
|
372
|
+
* Done is not terminal: replying into the thread reactivates the project
|
|
373
|
+
* server-side, which is what the toast promises.
|
|
374
|
+
*/
|
|
375
|
+
const completeMutation = useMutation({
|
|
376
|
+
mutationFn: () => completeProject(item.projectId),
|
|
377
|
+
onSuccess: () => {
|
|
378
|
+
// THE 200 IS PROOF, AND IT USED TO BE THROWN AWAY. The response says
|
|
379
|
+
// this project is now `completed`, which is exactly the input the
|
|
380
|
+
// to-do's ticket state is derived from — yet the plate was refreshed
|
|
381
|
+
// ONLY by the `desk_todo_sync` frame the server pushes a moment later.
|
|
382
|
+
// One transport, no fallback: if that frame lands on a socket that is
|
|
383
|
+
// down or reconnecting, the chip keeps reading Working with nothing
|
|
384
|
+
// scheduled to correct it. Invalidating here makes the plate converge on
|
|
385
|
+
// the round trip the client ALREADY made, whatever the socket is doing.
|
|
386
|
+
//
|
|
387
|
+
// Deliberately an invalidate and not a local write of `state`: the field
|
|
388
|
+
// is derived from ALL of a to-do's associations (useTodos: "no verb
|
|
389
|
+
// writes `state`, and none can"), so the browser cannot compute it — a
|
|
390
|
+
// to-do linked to this project AND another still-running one is still
|
|
391
|
+
// Working. Only the server knows that; we just ask it again.
|
|
392
|
+
queryClient.invalidateQueries({ queryKey: ['desk-todos'] })
|
|
393
|
+
onActStart?.(item.projectId) // parity with send() — snapshot before removal
|
|
394
|
+
animateOut('Marked done — cleared from your desk. Reply to reopen.')
|
|
395
|
+
},
|
|
396
|
+
onError: (err: Error) =>
|
|
397
|
+
onToast(err.message || 'Couldn’t mark this done — try again.'),
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
/** MARK DONE IS ALWAYS AVAILABLE, and the one thing standing in front of it
|
|
401
|
+
* is the staged batch.
|
|
402
|
+
*
|
|
403
|
+
* Open plan notes do NOT hold it back. "I'm finished with this thread" is a
|
|
404
|
+
* decision the user is entitled to make over the top of unanswered
|
|
405
|
+
* questions — the coordinator asking something does not oblige an answer,
|
|
406
|
+
* and the questions survive the completion: replying reopens the project
|
|
407
|
+
* with every note still open. Withholding the button in that state left a
|
|
408
|
+
* card with an unconfirmed plan and no way off the desk at all.
|
|
409
|
+
*
|
|
410
|
+
* A STAGED BATCH IS THE EXCEPTION because it is the only thing here that
|
|
411
|
+
* cannot come back. See `armedDiscard`: the first press relabels the button
|
|
412
|
+
* to say what the second one will throw away.
|
|
413
|
+
*
|
|
414
|
+
* THE RULE LIVES HERE RATHER THAN ONLY IN THE JSX. `DeskPage`'s `x` shortcut
|
|
415
|
+
* calls straight through the imperative handle, so a question asked only by
|
|
416
|
+
* a button's label would be skipped entirely by the keyboard — which is why
|
|
417
|
+
* the arming press also toasts. */
|
|
418
|
+
const dismiss = () => {
|
|
419
|
+
if (!actionable || acted || completeMutation.isPending) return
|
|
420
|
+
if (planSend && !armedDiscard) {
|
|
421
|
+
setArmedDiscard(true)
|
|
422
|
+
const n = planSend.count
|
|
423
|
+
onToast(
|
|
424
|
+
`${n} ${n === 1 ? 'reply is' : 'replies are'} staged and unsent — Mark Done again to discard ${n === 1 ? 'it' : 'them'} and complete the project.`,
|
|
425
|
+
)
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
completeMutation.mutate()
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const toggleReply = () => {
|
|
432
|
+
if (!actionable || acted) return
|
|
433
|
+
setReplying((r) => !r)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* START REVIEW — NAVIGATION, AND IT CANNOT SEND.
|
|
438
|
+
*
|
|
439
|
+
* The state it exists for is *comments are open and you have decided none of
|
|
440
|
+
* them*: there is nothing to send, so a Send button would be disabled chrome,
|
|
441
|
+
* and `Mark Done` would offer to clear a card whose whole content is
|
|
442
|
+
* unanswered questions. What the user actually needs is to get to the list.
|
|
443
|
+
*
|
|
444
|
+
* It unfolds the PLAN band if it is folded and scrolls the band to the top of
|
|
445
|
+
* the viewport. The comment list is the first thing the editor renders, so
|
|
446
|
+
* landing on the band lands on the list — which is why this does not reach
|
|
447
|
+
* inside the editor for a target it does not own.
|
|
448
|
+
*
|
|
449
|
+
* `setTimeout` rather than a layout effect: the band mounts the editor on
|
|
450
|
+
* this same click, so at call time the element either does not exist or is
|
|
451
|
+
* about to change height under us. One frame is enough and a wrong scroll
|
|
452
|
+
* position is the entire cost of being early.
|
|
453
|
+
*/
|
|
454
|
+
const startReview = () => {
|
|
455
|
+
if (!opened.plan) togglePlanBand()
|
|
456
|
+
window.setTimeout(() => {
|
|
457
|
+
rootRef.current
|
|
458
|
+
?.querySelector('[data-testid="plan-band"]')
|
|
459
|
+
?.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
|
460
|
+
}, 0)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
useImperativeHandle(
|
|
464
|
+
ref,
|
|
465
|
+
() => ({
|
|
466
|
+
toggleExpand: () => setExpanded((e) => !e),
|
|
467
|
+
toggleReply,
|
|
468
|
+
dismiss,
|
|
469
|
+
forward: () => canForward && setForwarding(true),
|
|
470
|
+
open: () => onOpen(item),
|
|
471
|
+
scrollIntoView: () =>
|
|
472
|
+
rootRef.current?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }),
|
|
473
|
+
}),
|
|
474
|
+
// `completeMutation.isPending` is in the deps because `dismiss` now reads
|
|
475
|
+
// it as its re-entrancy guard: without it the keyboard shortcut would hold
|
|
476
|
+
// a closure that still sees `false` and could fire a second POST.
|
|
477
|
+
// `planSend` and `armedDiscard` join the deps because `dismiss` now reads
|
|
478
|
+
// both: a stale closure would let the keyboard shortcut discard a staged
|
|
479
|
+
// review on the first press, which is the one thing the arming step exists
|
|
480
|
+
// to stop.
|
|
481
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
482
|
+
[item, isProgress, acted, completeMutation.isPending, planSend, armedDiscard, canForward],
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
/** `Mark Done` IS ONE BUTTON WITH TWO HOMES IN THE ROW, not two buttons. It
|
|
486
|
+
* leads when there is nothing else to do and trails `Reply Back` otherwise,
|
|
487
|
+
* so it is never the neighbour of the primary it used to be mistaken for —
|
|
488
|
+
* written once here and placed twice below, because a row is an order and
|
|
489
|
+
* order is not something a className can express. */
|
|
490
|
+
const donePrimary = !planSend && planOpenForYou === 0
|
|
491
|
+
const markDoneButton = (
|
|
492
|
+
<button
|
|
493
|
+
className={donePrimary ? 'md-btn md-btn--primary' : 'md-btn'}
|
|
494
|
+
data-testid="card-mark-done"
|
|
495
|
+
onClick={dismiss}
|
|
496
|
+
disabled={completeMutation.isPending}
|
|
497
|
+
>
|
|
498
|
+
<Check size={14} />
|
|
499
|
+
{armedDiscard ? `Discard ${planSend?.count ?? 0} & mark done` : 'Mark Done'}
|
|
500
|
+
</button>
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
<div
|
|
505
|
+
ref={rootRef}
|
|
506
|
+
className={
|
|
507
|
+
'md-card md-card--' +
|
|
508
|
+
item.lane +
|
|
509
|
+
// Carrier 1 of 5. The red left rule is what replaces the reference
|
|
510
|
+
// design's promotion-to-focus, scanning-wise: it is visible in the feed
|
|
511
|
+
// before the card is opened.
|
|
512
|
+
(offContract ? ' md-card--urgent' : '') +
|
|
513
|
+
(acted ? ' md-card--acted' : '') +
|
|
514
|
+
(selected ? ' md-card--selected' : '') +
|
|
515
|
+
// Never set by this card for ITSELF: hovering card A lights A's
|
|
516
|
+
// to-dos, not A's neighbours. The desk only ever hands `linked` back
|
|
517
|
+
// down to the OTHER side of the pair.
|
|
518
|
+
(linked ? ' md-card--linked' : '')
|
|
519
|
+
}
|
|
520
|
+
onMouseEnter={onHover ? () => onHover(item.projectId) : undefined}
|
|
521
|
+
onMouseLeave={onHover ? () => onHover(null) : undefined}
|
|
522
|
+
>
|
|
523
|
+
<div className="md-card-h">
|
|
524
|
+
<div className="md-card-title">{item.title}</div>
|
|
525
|
+
<span className="md-when">{relativeShort(item.ts)}</span>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
<div className="md-card-by">
|
|
529
|
+
<Avatar
|
|
530
|
+
name={item.agentName}
|
|
531
|
+
status={(item.agent?.status as AgentStatus) ?? undefined}
|
|
532
|
+
size="xs"
|
|
533
|
+
/>
|
|
534
|
+
{item.agent ? (
|
|
535
|
+
<AgentNameLink agentName={item.agent.name} className="md-card-agent">
|
|
536
|
+
@{item.agentName}
|
|
537
|
+
</AgentNameLink>
|
|
538
|
+
) : (
|
|
539
|
+
<span className="md-card-agent">@{item.agentName}</span>
|
|
540
|
+
)}
|
|
541
|
+
</div>
|
|
542
|
+
|
|
543
|
+
{/* Whenever the rail renders it is the first element under the byline and
|
|
544
|
+
it never moves. The rail being the fixed thing is what makes an omitted
|
|
545
|
+
band read as an omission rather than as a missing card — and its third
|
|
546
|
+
step, `is-next` and un-filled, is already the "no report yet"
|
|
547
|
+
affordance, which is why an absent REPORT band needs no placeholder.
|
|
548
|
+
A DM (`plan === null`) renders no rail at all: it has no plan to be
|
|
549
|
+
missing, so the card starts straight at its response band. */}
|
|
550
|
+
<PhaseRail plan={item.plan} hostName={item.agentName} offContract={offContract} />
|
|
551
|
+
|
|
552
|
+
{item.files.length > 0 && item.lane !== 'report' && (
|
|
553
|
+
<div className="md-card-files">
|
|
554
|
+
{item.files.map((f, i) => (
|
|
555
|
+
<DeskFilePill
|
|
556
|
+
key={f.filename}
|
|
557
|
+
filename={f.filename}
|
|
558
|
+
entryType={f.entryType}
|
|
559
|
+
onOpen={() => setOpenFileIdx(i)}
|
|
560
|
+
/>
|
|
561
|
+
))}
|
|
562
|
+
</div>
|
|
563
|
+
)}
|
|
564
|
+
{/* Portaled over the desk, for the card's user-communication room — the
|
|
565
|
+
room every desk card is anchored to (deskRoomKey). */}
|
|
566
|
+
{openFile && openFileIdx !== null &&
|
|
567
|
+
createPortal(
|
|
568
|
+
<FileViewerModal
|
|
569
|
+
projectId={item.projectId}
|
|
570
|
+
chatroomName="user-communication"
|
|
571
|
+
filename={openFile.filename}
|
|
572
|
+
untrusted={untrusted}
|
|
573
|
+
onClose={() => setOpenFileIdx(null)}
|
|
574
|
+
nav={{
|
|
575
|
+
index: openFileIdx,
|
|
576
|
+
total: item.files.length,
|
|
577
|
+
onPrev: () => setOpenFileIdx(openFileIdx - 1),
|
|
578
|
+
onNext: () => setOpenFileIdx(openFileIdx + 1),
|
|
579
|
+
}}
|
|
580
|
+
/>,
|
|
581
|
+
document.body,
|
|
582
|
+
)}
|
|
583
|
+
|
|
584
|
+
{/* THE THREE BANDS, FIXED ORDER, EVERY CARD: report, plan, response.
|
|
585
|
+
`bandsPresent` is the only thing that decides which of them render, so
|
|
586
|
+
the order cannot be re-derived per lane or per phase. */}
|
|
587
|
+
{present.map((id) => {
|
|
588
|
+
if (id === 'report') {
|
|
589
|
+
return (
|
|
590
|
+
<CardBand
|
|
591
|
+
key="report"
|
|
592
|
+
id="report"
|
|
593
|
+
expanded={!!opened.report}
|
|
594
|
+
onToggle={() => toggleBand('report')}
|
|
595
|
+
label="Report"
|
|
596
|
+
definition="the outcome, measured against the Plan — no action needed, just your read"
|
|
597
|
+
rev={<span className="md-cs-rev mono">{shortStamp(item.reportPublishedAt)}</span>}
|
|
598
|
+
foldMeta={report?.title || 'the completion report'}
|
|
599
|
+
foldCta="Read"
|
|
600
|
+
>
|
|
601
|
+
{report ? (
|
|
602
|
+
<div className="max-h-[420px] overflow-y-auto scrollbar-thin rounded-md border border-[var(--line-2)] px-3 py-2.5">
|
|
603
|
+
<RenderBody
|
|
604
|
+
renderCodeJs={report.render_code_js}
|
|
605
|
+
data={report.data}
|
|
606
|
+
runKey={`${item.projectId}::${report.generated_at}`}
|
|
607
|
+
active
|
|
608
|
+
/>
|
|
609
|
+
</div>
|
|
610
|
+
) : (
|
|
611
|
+
<div className="md-cs-def">Loading the report…</div>
|
|
612
|
+
)}
|
|
613
|
+
</CardBand>
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (id === 'plan') {
|
|
618
|
+
const fold = planFoldMeta(item.plan!)
|
|
619
|
+
return (
|
|
620
|
+
<CardBand
|
|
621
|
+
key="plan"
|
|
622
|
+
id="plan"
|
|
623
|
+
expanded={!!opened.plan}
|
|
624
|
+
onToggle={togglePlanBand}
|
|
625
|
+
// Carrier 3 of 5 — and `tone` is set from the deviation predicate
|
|
626
|
+
// and from nothing else. A merely-waiting card is `attn`, never
|
|
627
|
+
// red; nothing missing is ever red.
|
|
628
|
+
tone={fold.tone}
|
|
629
|
+
label="Plan · the contract"
|
|
630
|
+
definition="what you and the agents agreed to build — every deviation comes back here for your call"
|
|
631
|
+
foldMeta={fold.meta}
|
|
632
|
+
foldCta={fold.cta}
|
|
633
|
+
>
|
|
634
|
+
<PlanBand
|
|
635
|
+
plan={item.plan}
|
|
636
|
+
projectId={item.projectId}
|
|
637
|
+
// The editor mounts ONLY here. `CardBand` renders no children
|
|
638
|
+
// while folded, so this is belt-and-braces at one call site
|
|
639
|
+
// rather than a second source of truth — and it is what keeps
|
|
640
|
+
// a twelve-card desk at one `GET …/plan` instead of twelve.
|
|
641
|
+
expanded={!!opened.plan}
|
|
642
|
+
openSignal={planOpenSignal}
|
|
643
|
+
// THE CARD DRAWS THE SEND. Passing this is also what tells the
|
|
644
|
+
// editor to stop drawing its own — one primary per card.
|
|
645
|
+
onSendState={setPlanSend}
|
|
646
|
+
// NO `onOpenEditor`. The band's `Open the full Plan` button is
|
|
647
|
+
// gone: it NAVIGATED to `…?tab=plan`, unmounting this card and
|
|
648
|
+
// taking the user out of the desk, on cards where the in-band
|
|
649
|
+
// editor was already mounted directly below it. The one thing
|
|
650
|
+
// the tab still had that the band did not was the acceptance
|
|
651
|
+
// strip, which now renders inside `PlanEditor` at both densities.
|
|
652
|
+
/>
|
|
653
|
+
</CardBand>
|
|
654
|
+
)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
<CardBand
|
|
659
|
+
key="response"
|
|
660
|
+
id="response"
|
|
661
|
+
expanded={!!opened.response}
|
|
662
|
+
onToggle={() => toggleBand('response')}
|
|
663
|
+
// Until the agent answers, the newest message is the user's own —
|
|
664
|
+
// calling that a "Response" misreads it as the agent's reply.
|
|
665
|
+
label={item.quoteIsYours || item.quoteFromContact ? 'Request' : 'Response'}
|
|
666
|
+
definition={
|
|
667
|
+
item.quoteIsYours
|
|
668
|
+
? `what you asked @${item.agentName} — no response yet`
|
|
669
|
+
: item.quoteFromContact
|
|
670
|
+
? `what ${item.quoteFromContact} asked @${item.agentName} — no response yet`
|
|
671
|
+
: `what @${item.agentName} is telling you right now`
|
|
672
|
+
}
|
|
673
|
+
foldMeta={item.quote || 'no message yet'}
|
|
674
|
+
foldCta={item.lane === 'reply' ? 'Answer' : 'Read'}
|
|
675
|
+
>
|
|
676
|
+
{item.quote && (
|
|
677
|
+
<div
|
|
678
|
+
className={
|
|
679
|
+
'md-card-quote' +
|
|
680
|
+
(isProgress ? ' md-card-quote--flat' : '') +
|
|
681
|
+
(!expanded ? ' md-card-quote--clickable' : '')
|
|
682
|
+
}
|
|
683
|
+
onClick={!expanded ? () => setExpanded(true) : undefined}
|
|
684
|
+
>
|
|
685
|
+
<MarkdownBody
|
|
686
|
+
content={item.quote}
|
|
687
|
+
untrusted={untrusted}
|
|
688
|
+
clampable
|
|
689
|
+
collapsed={!expanded}
|
|
690
|
+
onToggle={() => setExpanded((e) => !e)}
|
|
691
|
+
collapsedHeightPx={120}
|
|
692
|
+
/>
|
|
693
|
+
</div>
|
|
694
|
+
)}
|
|
695
|
+
</CardBand>
|
|
696
|
+
)
|
|
697
|
+
})}
|
|
698
|
+
|
|
699
|
+
{/* THE LIVE METER. `.md-prog` is a SIBLING of the card body, not a child
|
|
700
|
+
of the Response band, so band fold state suppresses nothing — which is
|
|
701
|
+
why the gate is the halted conjunction itself rather than a layout
|
|
702
|
+
outcome. A gated card draws the static held line in the PLAN band
|
|
703
|
+
instead, and this is a stronger guarantee than suppression-by-fold:
|
|
704
|
+
it is one boolean, at one display site. */}
|
|
705
|
+
{isProgress && !gated && (
|
|
706
|
+
<div className="md-prog">
|
|
707
|
+
<span className="md-prog-dot" />
|
|
708
|
+
<span className="md-prog-lbl">Your team is on it</span>
|
|
709
|
+
</div>
|
|
710
|
+
)}
|
|
711
|
+
|
|
712
|
+
{/* THE ACTION ROW — EVERY ACTION IS ALWAYS THERE; WHAT MOVES IS WHICH ONE
|
|
713
|
+
IS PRIMARY, and the rule that decides is a single sentence: **the
|
|
714
|
+
primary is the next thing to do.**
|
|
715
|
+
|
|
716
|
+
rows staged -> Send review · N | Reply Back · Mark Done
|
|
717
|
+
comments open -> Start review | Reply Back · Mark Done
|
|
718
|
+
nothing open -> Mark Done | Reply Back
|
|
719
|
+
|
|
720
|
+
WHY PROMOTION RATHER THAN WITHHOLDING. The complaint this answers is
|
|
721
|
+
that a user who has just replied to four agent comments reads `Done`
|
|
722
|
+
as the next step — a reasonable reading, because `Done` was where the
|
|
723
|
+
eye ended up: it was the primary, it never moved, and `Send review`
|
|
724
|
+
was inside a box that scrolls with the card. Both halves of that are
|
|
725
|
+
fixed by the review's own button being the primary in this row. Taking
|
|
726
|
+
`Mark Done` off the card as well solved the same complaint twice, and
|
|
727
|
+
charged for it: a user who wants a thread off their desk while the
|
|
728
|
+
coordinator has questions open had no button at all, on the one card
|
|
729
|
+
type that always has a question open.
|
|
730
|
+
|
|
731
|
+
So it stays, demoted, and it sits AFTER `Reply Back` — the far end of
|
|
732
|
+
the row from the primary — so the two are never adjacent under a
|
|
733
|
+
thumb. When nothing is open it takes the primary slot, which is the
|
|
734
|
+
first render site below rather than a style swap in place.
|
|
735
|
+
|
|
736
|
+
The one press it does not honour immediately is with a batch staged:
|
|
737
|
+
see `armedDiscard`. Nothing else here is lossy — completing is undone
|
|
738
|
+
by replying, and an unanswered note is still open when the project
|
|
739
|
+
reopens.
|
|
740
|
+
|
|
741
|
+
`Done` IS NOW `Mark Done` and `Send Back` IS NOW `Reply Back`. The
|
|
742
|
+
first because the button removes the card and does not complete the
|
|
743
|
+
work, and "Done" is a review word on a screen full of review; the
|
|
744
|
+
second because it opens a free-text reply, so "send back" oversold it
|
|
745
|
+
as a routing decision competing with the user's actual review — and at
|
|
746
|
+
22 characters `Reply to @some-coordinator` broke the row on a phone.
|
|
747
|
+
`Reply Back` holds the line beside `Send review · 4` at 330px. */}
|
|
748
|
+
<div className="md-actions">
|
|
749
|
+
{actionable && !acted && donePrimary && markDoneButton}
|
|
750
|
+
{actionable && !acted && planSend && (
|
|
751
|
+
<button
|
|
752
|
+
className="md-btn md-btn--primary"
|
|
753
|
+
data-testid="card-send-review"
|
|
754
|
+
disabled={planSend.submitting}
|
|
755
|
+
onClick={planSend.submit}
|
|
756
|
+
>
|
|
757
|
+
<Send size={14} />
|
|
758
|
+
{planSend.submitting ? 'Sending…' : `Send review · ${planSend.count}`}
|
|
759
|
+
</button>
|
|
760
|
+
)}
|
|
761
|
+
{actionable && !acted && !planSend && planOpenForYou > 0 && (
|
|
762
|
+
<button
|
|
763
|
+
className="md-btn md-btn--primary"
|
|
764
|
+
data-testid="card-start-review"
|
|
765
|
+
onClick={startReview}
|
|
766
|
+
>
|
|
767
|
+
<ArrowUp size={14} />
|
|
768
|
+
Start review
|
|
769
|
+
</button>
|
|
770
|
+
)}
|
|
771
|
+
{actionable && !acted && (
|
|
772
|
+
<button className="md-btn" data-testid="card-reply-back" onClick={toggleReply}>
|
|
773
|
+
<ArrowLeft size={14} />
|
|
774
|
+
Reply Back
|
|
775
|
+
</button>
|
|
776
|
+
)}
|
|
777
|
+
{actionable && !acted && !donePrimary && markDoneButton}
|
|
778
|
+
{/* History cards are Open-only, with one exception: distilling a
|
|
779
|
+
finished project into a reusable SOP is additive — it removes
|
|
780
|
+
nothing from the desk and re-marks nothing done — so it is the one
|
|
781
|
+
action the read lane carries. Rendered before "Open project" so the
|
|
782
|
+
row reads [Save as SOP] [Open project]. */}
|
|
783
|
+
{isRead && onSaveAsSop && (
|
|
784
|
+
<button className="md-btn" onClick={() => onSaveAsSop(item)}>
|
|
785
|
+
<FileText size={14} />
|
|
786
|
+
Save as SOP
|
|
787
|
+
</button>
|
|
788
|
+
)}
|
|
789
|
+
{canForward && (
|
|
790
|
+
<button className="md-btn" data-testid="card-forward" onClick={() => setForwarding(true)}>
|
|
791
|
+
<CornerUpRight size={14} />
|
|
792
|
+
Forward
|
|
793
|
+
</button>
|
|
794
|
+
)}
|
|
795
|
+
<button className="md-btn md-btn--ghost" onClick={() => onOpen(item)}>
|
|
796
|
+
Open {item.kind === 'project' ? 'project' : 'thread'}
|
|
797
|
+
<ArrowRight size={13} />
|
|
798
|
+
</button>
|
|
799
|
+
</div>
|
|
800
|
+
|
|
801
|
+
{forwarding && canForward && item.latestMessageId && (
|
|
802
|
+
<ForwardModal
|
|
803
|
+
target={{
|
|
804
|
+
projectId: item.projectId,
|
|
805
|
+
chatroomName: 'user-communication',
|
|
806
|
+
messageIds: [item.latestMessageId],
|
|
807
|
+
// An outside contact's words are forwarded under their name, never
|
|
808
|
+
// re-attributed to the agent fielding the thread.
|
|
809
|
+
senderName: item.quoteFromContact ?? item.agentName,
|
|
810
|
+
senderId: item.quoteFromContact ? undefined : item.agent?.id,
|
|
811
|
+
content: item.quote,
|
|
812
|
+
files: item.files.map((f) => f.filename),
|
|
813
|
+
untrusted,
|
|
814
|
+
}}
|
|
815
|
+
onClose={() => setForwarding(false)}
|
|
816
|
+
onSent={(names) => onToast(`Forwarded to ${names.join(', ')}`)}
|
|
817
|
+
/>
|
|
818
|
+
)}
|
|
819
|
+
|
|
820
|
+
{replying && (
|
|
821
|
+
<div className="md-reply relative" {...dragHandlers}>
|
|
822
|
+
<DropOverlay visible={isDragging} />
|
|
823
|
+
<div className="md-reply-to">
|
|
824
|
+
<Avatar name="You" size="xs" />
|
|
825
|
+
<span>you</span>
|
|
826
|
+
<ArrowRight size={11} />
|
|
827
|
+
<span className="md-reply-dest">{dest}</span>
|
|
828
|
+
</div>
|
|
829
|
+
{/* No `agents` prop: a desk reply has no roster context, so an
|
|
830
|
+
`{{X|agent}}` blank degrades to free-text entry — right here. */}
|
|
831
|
+
<PromptEditor
|
|
832
|
+
ref={editorRef}
|
|
833
|
+
className="md-reply-in"
|
|
834
|
+
minHeight={56}
|
|
835
|
+
maxHeight={160}
|
|
836
|
+
placeholder={`Reply to @${item.agentName}…`}
|
|
837
|
+
disabled={sendMutation.isPending}
|
|
838
|
+
onChange={(t) => setText(t)}
|
|
839
|
+
onSubmit={send}
|
|
840
|
+
/>
|
|
841
|
+
<StagedAttachments
|
|
842
|
+
files={stagedFiles}
|
|
843
|
+
onRemove={removeStagedFile}
|
|
844
|
+
disabled={sendMutation.isPending}
|
|
845
|
+
/>
|
|
846
|
+
{attachmentError && <p className="md-reply-err">{attachmentError}</p>}
|
|
847
|
+
<div className="md-reply-bar">
|
|
848
|
+
<span className="md-reply-hint">
|
|
849
|
+
Drop files or attach · <kbd>⌘</kbd>
|
|
850
|
+
<kbd>↵</kbd>
|
|
851
|
+
</span>
|
|
852
|
+
<div className="md-reply-actions">
|
|
853
|
+
<input
|
|
854
|
+
ref={fileInputRef}
|
|
855
|
+
type="file"
|
|
856
|
+
multiple
|
|
857
|
+
className="hidden"
|
|
858
|
+
onChange={handleFileChange}
|
|
859
|
+
/>
|
|
860
|
+
<button
|
|
861
|
+
className="md-reply-attach"
|
|
862
|
+
title="Attach file"
|
|
863
|
+
disabled={sendMutation.isPending}
|
|
864
|
+
onClick={() => fileInputRef.current?.click()}
|
|
865
|
+
>
|
|
866
|
+
<Paperclip size={14} />
|
|
867
|
+
</button>
|
|
868
|
+
<button className="md-btn md-btn--quiet" onClick={cancelReply}>
|
|
869
|
+
Cancel
|
|
870
|
+
</button>
|
|
871
|
+
<button
|
|
872
|
+
className="md-btn md-btn--primary"
|
|
873
|
+
disabled={(!text.trim() && stagedFiles.length === 0) || sendMutation.isPending}
|
|
874
|
+
onClick={send}
|
|
875
|
+
>
|
|
876
|
+
<Send size={13} />
|
|
877
|
+
command
|
|
878
|
+
</button>
|
|
879
|
+
</div>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
)}
|
|
883
|
+
</div>
|
|
884
|
+
)
|
|
885
|
+
})
|
|
886
|
+
|
|
887
|
+
export default UpdateCard
|
|
888
|
+
|
|
889
|
+
// Desk-styled file pill; the card owns the FileViewerModal it opens. Click
|
|
890
|
+
// stops propagation so it doesn't also toggle the card's expand-on-click quote.
|
|
891
|
+
function DeskFilePill({
|
|
892
|
+
filename,
|
|
893
|
+
entryType,
|
|
894
|
+
onOpen,
|
|
895
|
+
}: {
|
|
896
|
+
filename: string
|
|
897
|
+
entryType: DeskFile['entryType']
|
|
898
|
+
onOpen: () => void
|
|
899
|
+
}) {
|
|
900
|
+
const action = entryType === 'file_created' ? 'created' : 'updated'
|
|
901
|
+
return (
|
|
902
|
+
<button
|
|
903
|
+
type="button"
|
|
904
|
+
className="md-file-pill"
|
|
905
|
+
title={`${action}: ${filename} — click to view`}
|
|
906
|
+
onClick={(e) => {
|
|
907
|
+
e.stopPropagation()
|
|
908
|
+
onOpen()
|
|
909
|
+
}}
|
|
910
|
+
>
|
|
911
|
+
<FileText size={12} />
|
|
912
|
+
<span className="truncate">{filename}</span>
|
|
913
|
+
</button>
|
|
914
|
+
)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// The REPORT band's mono trailer: when the report was published. A pass-through
|
|
918
|
+
// of the stamp the project list already carries — the band is present because
|
|
919
|
+
// that stamp is non-null, so printing it says nothing the presence did not
|
|
920
|
+
// already earn.
|
|
921
|
+
function shortStamp(iso: string | null): string {
|
|
922
|
+
if (!iso) return ''
|
|
923
|
+
const d = new Date(iso)
|
|
924
|
+
if (isNaN(d.getTime())) return ''
|
|
925
|
+
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Terse relative label (mirrors formatRelativeTime but always includes the "ago"
|
|
929
|
+
// framing the design uses on cards).
|
|
930
|
+
function relativeShort(iso: string): string {
|
|
931
|
+
if (!iso) return ''
|
|
932
|
+
const then = new Date(iso).getTime()
|
|
933
|
+
if (isNaN(then)) return ''
|
|
934
|
+
const sec = Math.floor((Date.now() - then) / 1000)
|
|
935
|
+
if (sec < 60) return 'just now'
|
|
936
|
+
const min = Math.floor(sec / 60)
|
|
937
|
+
if (min < 60) return `${min}m ago`
|
|
938
|
+
const hr = Math.floor(min / 60)
|
|
939
|
+
if (hr < 24) return `${hr}h ago`
|
|
940
|
+
const day = Math.floor(hr / 24)
|
|
941
|
+
if (day < 7) return `${day}d ago`
|
|
942
|
+
return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
|
|
943
|
+
}
|