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.
Files changed (1004) hide show
  1. manigent/_catalog/mcps/chess/README.md +38 -0
  2. manigent/_catalog/mcps/chess/mcp.json +9 -0
  3. manigent/_catalog/mcps/registry.json +9 -0
  4. manigent/_catalog/skills/brief/SKILL.md +339 -0
  5. manigent/_catalog/skills/broaden-options/SKILL.md +133 -0
  6. manigent/_catalog/skills/calendar/SKILL.md +44 -0
  7. manigent/_catalog/skills/calendar/starter_config.jsonc +25 -0
  8. manigent/_catalog/skills/gmail/SKILL.md +73 -0
  9. manigent/_catalog/skills/google-calendar/SKILL.md +44 -0
  10. manigent/_catalog/skills/google-drive/SKILL.md +42 -0
  11. manigent/_catalog/skills/google-drive-write/SKILL.md +45 -0
  12. manigent/_catalog/skills/google-drive-write/starter_config.jsonc +45 -0
  13. manigent/_catalog/skills/grill-me/SKILL.md +173 -0
  14. manigent/_catalog/skills/homekit/SKILL.md +37 -0
  15. manigent/_catalog/skills/http-api/SKILL.md +117 -0
  16. manigent/_catalog/skills/ib/SKILL.md +89 -0
  17. manigent/_catalog/skills/ib/starter_config.jsonc +35 -0
  18. manigent/_catalog/skills/mailbox/SKILL.md +47 -0
  19. manigent/_catalog/skills/mailbox/starter_config.jsonc +38 -0
  20. manigent/_catalog/skills/media/SKILL.md +93 -0
  21. manigent/_catalog/skills/media/starter_config.jsonc +63 -0
  22. manigent/_catalog/skills/osxphotos/SKILL.md +41 -0
  23. manigent/_catalog/skills/photo-story/SKILL.md +285 -0
  24. manigent/_catalog/skills/playwright-browser/SKILL.md +291 -0
  25. manigent/_catalog/skills/playwright-browser/starter_config.jsonc +44 -0
  26. manigent/_catalog/skills/playwright-save-skill/SKILL.md +149 -0
  27. manigent/_catalog/skills/registry.json +166 -0
  28. manigent/_catalog/skills/web-artifacts/SKILL.md +194 -0
  29. manigent/api/action_executor.py +404 -0
  30. manigent/api/action_validator.py +531 -0
  31. manigent/api/actions.py +221 -0
  32. manigent/api/client.py +558 -0
  33. manigent/api/control.py +806 -0
  34. manigent/api/edge_protocol.py +123 -0
  35. manigent/api/fs_protocol.py +450 -0
  36. manigent/api/host_protocol.py +298 -0
  37. manigent/api/managed_job_client.py +124 -0
  38. manigent/api/responses.py +342 -0
  39. manigent/api/retry_transport.py +178 -0
  40. manigent/api/turn_protocol.py +78 -0
  41. manigent/cli_lifecycle.py +467 -0
  42. manigent/cli_server.py +978 -0
  43. manigent/doctor.py +1167 -0
  44. manigent/hub_protocol/canonical.py +375 -0
  45. manigent/hub_protocol/codes.py +171 -0
  46. manigent/hub_protocol/constants.py +71 -0
  47. manigent/hub_protocol/envelope.py +218 -0
  48. manigent/hub_protocol/frames.py +788 -0
  49. manigent/hub_protocol/http.py +260 -0
  50. manigent/hub_protocol/ids.py +170 -0
  51. manigent/hub_protocol/keys.py +195 -0
  52. manigent/hub_protocol/text.py +75 -0
  53. manigent/llm/base.py +1072 -0
  54. manigent/llm/prompt_builder.py +2450 -0
  55. manigent/llm/triggers.py +68 -0
  56. manigent/models/agent.py +2905 -0
  57. manigent/models/agent_bundle.py +298 -0
  58. manigent/models/brief_tab.py +311 -0
  59. manigent/models/chat_message.py +231 -0
  60. manigent/models/chatroom.py +1108 -0
  61. manigent/models/context.py +1159 -0
  62. manigent/models/desk_label.py +836 -0
  63. manigent/models/desk_read_state.py +220 -0
  64. manigent/models/desk_sign_off.py +267 -0
  65. manigent/models/desk_sop.py +965 -0
  66. manigent/models/desk_todo.py +1603 -0
  67. manigent/models/desk_todo_link.py +404 -0
  68. manigent/models/file_cache.py +75 -0
  69. manigent/models/host.py +732 -0
  70. manigent/models/install_token.py +401 -0
  71. manigent/models/invitation_code.py +162 -0
  72. manigent/models/knowledge_pack.py +552 -0
  73. manigent/models/managed_job.py +295 -0
  74. manigent/models/model_config.py +564 -0
  75. manigent/models/model_defaults.py +238 -0
  76. manigent/models/onboarding_plan.py +161 -0
  77. manigent/models/participant.py +449 -0
  78. manigent/models/persistable.py +892 -0
  79. manigent/models/plan_markdown.py +1774 -0
  80. manigent/models/project.py +1343 -0
  81. manigent/models/project_plan.py +8104 -0
  82. manigent/models/project_report.py +145 -0
  83. manigent/models/provenance.py +261 -0
  84. manigent/models/provider_agents.py +101 -0
  85. manigent/models/public_request.py +108 -0
  86. manigent/models/reflection_schedule.py +191 -0
  87. manigent/models/scheduled_message.py +285 -0
  88. manigent/models/share_token.py +190 -0
  89. manigent/models/skill_version.py +31 -0
  90. manigent/models/sop_template.py +187 -0
  91. manigent/models/thinking_partner_plan.py +187 -0
  92. manigent/models/tunnel_binding.py +129 -0
  93. manigent/models/user.py +1276 -0
  94. manigent/models/work_tracker.py +510 -0
  95. manigent/runner/invocation_registry.py +187 -0
  96. manigent/server/agent_capabilities.py +119 -0
  97. manigent/server/agent_visibility.py +119 -0
  98. manigent/server/announcements.py +119 -0
  99. manigent/server/app.py +807 -0
  100. manigent/server/auth.py +355 -0
  101. manigent/server/batch_timeout.py +94 -0
  102. manigent/server/channels/access.py +194 -0
  103. manigent/server/channels/adapter.py +295 -0
  104. manigent/server/channels/config.py +693 -0
  105. manigent/server/channels/contacts.py +39 -0
  106. manigent/server/channels/email_adapter.py +436 -0
  107. manigent/server/channels/email_address.py +145 -0
  108. manigent/server/channels/email_form.py +242 -0
  109. manigent/server/channels/email_format.py +285 -0
  110. manigent/server/channels/email_mail.py +427 -0
  111. manigent/server/channels/email_mailgun.py +164 -0
  112. manigent/server/channels/email_text.py +280 -0
  113. manigent/server/channels/file_links.py +127 -0
  114. manigent/server/channels/gateway.py +2998 -0
  115. manigent/server/channels/hub_delivery.py +211 -0
  116. manigent/server/channels/hub_store.py +1730 -0
  117. manigent/server/channels/line.py +645 -0
  118. manigent/server/channels/line_format.py +589 -0
  119. manigent/server/channels/log_redaction.py +145 -0
  120. manigent/server/channels/store.py +3316 -0
  121. manigent/server/channels/telegram.py +422 -0
  122. manigent/server/channels/telegram_format.py +312 -0
  123. manigent/server/channels/webchat.py +261 -0
  124. manigent/server/channels/webchat_widget.js +1310 -0
  125. manigent/server/channels/whatsapp.py +707 -0
  126. manigent/server/channels/whatsapp_format.py +511 -0
  127. manigent/server/chat_search_index.py +1203 -0
  128. manigent/server/config.py +61 -0
  129. manigent/server/context.py +121 -0
  130. manigent/server/coordinator_rule.py +115 -0
  131. manigent/server/desk_todo_notify.py +300 -0
  132. manigent/server/edge_hub.py +577 -0
  133. manigent/server/email.py +354 -0
  134. manigent/server/errors.py +83 -0
  135. manigent/server/home_fs_relay.py +392 -0
  136. manigent/server/hub/adapter.py +1395 -0
  137. manigent/server/hub/agent_send_guard.py +196 -0
  138. manigent/server/hub/avatar.py +342 -0
  139. manigent/server/hub/browser.py +778 -0
  140. manigent/server/hub/client.py +859 -0
  141. manigent/server/hub/demand.py +49 -0
  142. manigent/server/hub/directory.py +385 -0
  143. manigent/server/hub/enroll.py +400 -0
  144. manigent/server/hub/http.py +134 -0
  145. manigent/server/hub/identity.py +61 -0
  146. manigent/server/hub/keys.py +588 -0
  147. manigent/server/hub/mirror.py +802 -0
  148. manigent/server/hub/operator.py +817 -0
  149. manigent/server/hub/retain.py +361 -0
  150. manigent/server/hub/room_bridge.py +741 -0
  151. manigent/server/hub/runtime.py +691 -0
  152. manigent/server/hub/trust.py +182 -0
  153. manigent/server/hub/views.py +316 -0
  154. manigent/server/idempotency.py +205 -0
  155. manigent/server/in_flight_restore.py +181 -0
  156. manigent/server/inspection.py +816 -0
  157. manigent/server/isolation_gate.py +516 -0
  158. manigent/server/isolation_notice.py +310 -0
  159. manigent/server/managed_job_events.py +242 -0
  160. manigent/server/mcp_registry.py +118 -0
  161. manigent/server/message_forward.py +78 -0
  162. manigent/server/message_scheduler.py +176 -0
  163. manigent/server/notification_assets.py +164 -0
  164. manigent/server/oauth_login/cli_state.py +120 -0
  165. manigent/server/oauth_login/config.py +90 -0
  166. manigent/server/oauth_login/errors.py +78 -0
  167. manigent/server/oauth_login/flow.py +223 -0
  168. manigent/server/oauth_login/handoff.py +63 -0
  169. manigent/server/oauth_login/providers.py +45 -0
  170. manigent/server/oauth_state.py +81 -0
  171. manigent/server/owner_isolation.py +466 -0
  172. manigent/server/participant_liveness.py +82 -0
  173. manigent/server/public_profile.py +149 -0
  174. manigent/server/public_requests/candidates.py +116 -0
  175. manigent/server/public_requests/dispatch.py +121 -0
  176. manigent/server/public_requests/ranking.py +153 -0
  177. manigent/server/recent_activity.py +250 -0
  178. manigent/server/reflection_completion_subscriber.py +151 -0
  179. manigent/server/reflection_scheduler.py +239 -0
  180. manigent/server/requester_key_migration.py +261 -0
  181. manigent/server/room_flow.py +139 -0
  182. manigent/server/routes/_batch.py +218 -0
  183. manigent/server/routes/_fd_routing.py +155 -0
  184. manigent/server/routes/_mentions.py +70 -0
  185. manigent/server/routes/agent_env.py +188 -0
  186. manigent/server/routes/agent_fs.py +450 -0
  187. manigent/server/routes/agents.py +1206 -0
  188. manigent/server/routes/announcements.py +79 -0
  189. manigent/server/routes/auth.py +387 -0
  190. manigent/server/routes/brief_tabs.py +404 -0
  191. manigent/server/routes/changelog.py +237 -0
  192. manigent/server/routes/channels.py +993 -0
  193. manigent/server/routes/chatrooms.py +871 -0
  194. manigent/server/routes/desk_labels.py +298 -0
  195. manigent/server/routes/desk_latest_messages.py +116 -0
  196. manigent/server/routes/desk_read_state.py +110 -0
  197. manigent/server/routes/desk_sign_off.py +88 -0
  198. manigent/server/routes/desk_sops.py +216 -0
  199. manigent/server/routes/desk_todos.py +692 -0
  200. manigent/server/routes/desk_tour.py +44 -0
  201. manigent/server/routes/files.py +572 -0
  202. manigent/server/routes/hosts.py +484 -0
  203. manigent/server/routes/hub.py +534 -0
  204. manigent/server/routes/hub_channels.py +234 -0
  205. manigent/server/routes/inbox.py +581 -0
  206. manigent/server/routes/install.ps1 +436 -0
  207. manigent/server/routes/install.py +324 -0
  208. manigent/server/routes/install.sh +566 -0
  209. manigent/server/routes/isolation_check.py +145 -0
  210. manigent/server/routes/knowledge_packs.py +477 -0
  211. manigent/server/routes/managed_jobs.py +196 -0
  212. manigent/server/routes/mcps.py +271 -0
  213. manigent/server/routes/messages.py +1972 -0
  214. manigent/server/routes/model_defaults.py +279 -0
  215. manigent/server/routes/oauth.py +237 -0
  216. manigent/server/routes/oauth_login.py +351 -0
  217. manigent/server/routes/owner_isolation.py +91 -0
  218. manigent/server/routes/project_plans.py +1521 -0
  219. manigent/server/routes/project_reports.py +235 -0
  220. manigent/server/routes/projects.py +2047 -0
  221. manigent/server/routes/public_agents.py +151 -0
  222. manigent/server/routes/public_requests.py +181 -0
  223. manigent/server/routes/reflection.py +93 -0
  224. manigent/server/routes/scheduled_messages.py +263 -0
  225. manigent/server/routes/search.py +94 -0
  226. manigent/server/routes/sharing.py +156 -0
  227. manigent/server/routes/skills.py +406 -0
  228. manigent/server/routes/templates.py +219 -0
  229. manigent/server/routes/turn_tokens.py +119 -0
  230. manigent/server/routes/webchat.py +406 -0
  231. manigent/server/routes/websocket.py +431 -0
  232. manigent/server/serving_only.py +200 -0
  233. manigent/server/signup_policy.py +120 -0
  234. manigent/server/skill_registry.py +164 -0
  235. manigent/server/socket_events.py +858 -0
  236. manigent/server/timeout_checker.py +446 -0
  237. manigent/server/tunnel_subscriber.py +1000 -0
  238. manigent/server/turn_tokens.py +285 -0
  239. manigent/server/ws_hub.py +556 -0
  240. manigent/server_cli.py +31 -0
  241. manigent/sync/changelog.py +885 -0
  242. manigent/sync/console_subscriber.py +222 -0
  243. manigent/sync/runloop.py +619 -0
  244. manigent/sync/runloop_manager.py +197 -0
  245. manigent/sync/subscriber.py +98 -0
  246. manigent/utils/agent_processes.py +276 -0
  247. manigent/utils/agent_storage.py +125 -0
  248. manigent/utils/email.py +563 -0
  249. manigent/utils/env_store.py +174 -0
  250. manigent/utils/extra_dirs.py +85 -0
  251. manigent/utils/file_io.py +485 -0
  252. manigent/utils/invoke_timeout.py +87 -0
  253. manigent/utils/jsonc.py +78 -0
  254. manigent/utils/knowledge_index.py +148 -0
  255. manigent/utils/nofollow_fs.py +279 -0
  256. manigent/utils/notification_center.py +67 -0
  257. manigent/utils/version.py +57 -0
  258. manigent/web/frontend/.design-sync-entry.tsx +74 -0
  259. manigent/web/frontend/.design-sync-provider.tsx +46 -0
  260. manigent/web/frontend/.eslintrc.cjs +183 -0
  261. manigent/web/frontend/.gitignore +6 -0
  262. manigent/web/frontend/app/page.tsx +39 -0
  263. manigent/web/frontend/hub-terms.html +192 -0
  264. manigent/web/frontend/index.html +15 -0
  265. manigent/web/frontend/landing.html +15 -0
  266. manigent/web/frontend/package-lock.json +8028 -0
  267. manigent/web/frontend/package.json +55 -0
  268. manigent/web/frontend/postcss.config.js +6 -0
  269. manigent/web/frontend/privacy.html +217 -0
  270. manigent/web/frontend/private-beta-announcement.html +19 -0
  271. manigent/web/frontend/public/apple-touch-icon.png +0 -0
  272. manigent/web/frontend/public/brand-hero-banner.png +0 -0
  273. manigent/web/frontend/public/brand-hero.png +0 -0
  274. manigent/web/frontend/public/favicon.png +0 -0
  275. manigent/web/frontend/public/favicon.svg +8 -0
  276. manigent/web/frontend/public/icon-transparent-sm.png +0 -0
  277. manigent/web/frontend/public/icon.png +0 -0
  278. manigent/web/frontend/public/manigent-mark.svg +7 -0
  279. manigent/web/frontend/public/vite.svg +5 -0
  280. manigent/web/frontend/public.html +15 -0
  281. manigent/web/frontend/src/App.activeAccountRedirect.test.tsx +236 -0
  282. manigent/web/frontend/src/App.hubHostDm.test.tsx +119 -0
  283. manigent/web/frontend/src/App.hubHostProject.test.tsx +142 -0
  284. manigent/web/frontend/src/App.hubRoutes.test.tsx +187 -0
  285. manigent/web/frontend/src/App.oauthAddCallback.test.tsx +263 -0
  286. manigent/web/frontend/src/App.tsx +246 -0
  287. manigent/web/frontend/src/api/attachments.ts +32 -0
  288. manigent/web/frontend/src/api/batchLoader.test.ts +212 -0
  289. manigent/web/frontend/src/api/batchLoader.ts +124 -0
  290. manigent/web/frontend/src/api/chatroomsBatch.test.tsx +392 -0
  291. manigent/web/frontend/src/api/client.ts +321 -0
  292. manigent/web/frontend/src/api/crossAccountAuth.test.ts +388 -0
  293. manigent/web/frontend/src/api/endpoints/agentEnv.ts +24 -0
  294. manigent/web/frontend/src/api/endpoints/agentFs.ts +125 -0
  295. manigent/web/frontend/src/api/endpoints/agentSearch.ts +19 -0
  296. manigent/web/frontend/src/api/endpoints/agents.ts +66 -0
  297. manigent/web/frontend/src/api/endpoints/announcements.ts +26 -0
  298. manigent/web/frontend/src/api/endpoints/auth.ts +361 -0
  299. manigent/web/frontend/src/api/endpoints/authLogin.test.ts +236 -0
  300. manigent/web/frontend/src/api/endpoints/batch.ts +64 -0
  301. manigent/web/frontend/src/api/endpoints/briefTabs.ts +81 -0
  302. manigent/web/frontend/src/api/endpoints/channels.ts +285 -0
  303. manigent/web/frontend/src/api/endpoints/computers.ts +212 -0
  304. manigent/web/frontend/src/api/endpoints/deskLabels.ts +151 -0
  305. manigent/web/frontend/src/api/endpoints/deskLatestMessages.ts +23 -0
  306. manigent/web/frontend/src/api/endpoints/deskReadState.ts +41 -0
  307. manigent/web/frontend/src/api/endpoints/deskSignOffs.ts +74 -0
  308. manigent/web/frontend/src/api/endpoints/deskSops.ts +53 -0
  309. manigent/web/frontend/src/api/endpoints/deskTodoAttachments.ts +69 -0
  310. manigent/web/frontend/src/api/endpoints/deskTodoProjects.test.ts +99 -0
  311. manigent/web/frontend/src/api/endpoints/deskTodoProjects.ts +114 -0
  312. manigent/web/frontend/src/api/endpoints/deskTodos.ts +164 -0
  313. manigent/web/frontend/src/api/endpoints/deskTour.ts +16 -0
  314. manigent/web/frontend/src/api/endpoints/dm.ts +137 -0
  315. manigent/web/frontend/src/api/endpoints/files.ts +55 -0
  316. manigent/web/frontend/src/api/endpoints/hub.contract.test.ts +94 -0
  317. manigent/web/frontend/src/api/endpoints/hub.keys.ts +260 -0
  318. manigent/web/frontend/src/api/endpoints/hub.test.ts +203 -0
  319. manigent/web/frontend/src/api/endpoints/hub.ts +654 -0
  320. manigent/web/frontend/src/api/endpoints/index.ts +15 -0
  321. manigent/web/frontend/src/api/endpoints/install.ts +74 -0
  322. manigent/web/frontend/src/api/endpoints/isolationCheck.ts +36 -0
  323. manigent/web/frontend/src/api/endpoints/knowledgePacks.ts +179 -0
  324. manigent/web/frontend/src/api/endpoints/mcps.ts +36 -0
  325. manigent/web/frontend/src/api/endpoints/messages.held.test.ts +43 -0
  326. manigent/web/frontend/src/api/endpoints/messages.ts +95 -0
  327. manigent/web/frontend/src/api/endpoints/modelConfigs.ts +232 -0
  328. manigent/web/frontend/src/api/endpoints/oauthApi.test.ts +110 -0
  329. manigent/web/frontend/src/api/endpoints/projectPlans.ts +608 -0
  330. manigent/web/frontend/src/api/endpoints/projectReports.ts +36 -0
  331. manigent/web/frontend/src/api/endpoints/projects.ts +124 -0
  332. manigent/web/frontend/src/api/endpoints/publicRequests.ts +75 -0
  333. manigent/web/frontend/src/api/endpoints/reflection.ts +32 -0
  334. manigent/web/frontend/src/api/endpoints/roomFlow.ts +14 -0
  335. manigent/web/frontend/src/api/endpoints/scheduledMessages.ts +60 -0
  336. manigent/web/frontend/src/api/endpoints/search.ts +27 -0
  337. manigent/web/frontend/src/api/endpoints/sharing.ts +42 -0
  338. manigent/web/frontend/src/api/endpoints/skills.ts +67 -0
  339. manigent/web/frontend/src/api/endpoints/templates.ts +72 -0
  340. manigent/web/frontend/src/api/loaders.ts +22 -0
  341. manigent/web/frontend/src/api/singleIssuer.test.ts +148 -0
  342. manigent/web/frontend/src/api/transientRetry.test.ts +176 -0
  343. manigent/web/frontend/src/api/transientRetry.ts +140 -0
  344. manigent/web/frontend/src/api/websocket.resync.test.ts +111 -0
  345. manigent/web/frontend/src/api/websocket.ts +335 -0
  346. manigent/web/frontend/src/components/agent/AgentNameLink.tsx +24 -0
  347. manigent/web/frontend/src/components/agent/AgentViewActions.test.tsx +41 -0
  348. manigent/web/frontend/src/components/agent/AgentViewActions.tsx +89 -0
  349. manigent/web/frontend/src/components/agent/CreateAgentPage.tsx +535 -0
  350. manigent/web/frontend/src/components/agent/JsonConfigEditor.tsx +33 -0
  351. manigent/web/frontend/src/components/agent/KnowledgePackList.tsx +103 -0
  352. manigent/web/frontend/src/components/agent/McpCatalog.tsx +261 -0
  353. manigent/web/frontend/src/components/agent/McpConfigModal.tsx +224 -0
  354. manigent/web/frontend/src/components/agent/PreConsentModal.tsx +172 -0
  355. manigent/web/frontend/src/components/agent/SkillCatalog.tsx +364 -0
  356. manigent/web/frontend/src/components/agent/SkillConfigModal.tsx +221 -0
  357. manigent/web/frontend/src/components/auth/AddAccountPage.test.tsx +181 -0
  358. manigent/web/frontend/src/components/auth/AddAccountPage.tsx +201 -0
  359. manigent/web/frontend/src/components/auth/AuthShell.tsx +462 -0
  360. manigent/web/frontend/src/components/auth/LoginPage.test.tsx +384 -0
  361. manigent/web/frontend/src/components/auth/LoginPage.tsx +129 -0
  362. manigent/web/frontend/src/components/auth/OAuthCallbackPage.test.tsx +185 -0
  363. manigent/web/frontend/src/components/auth/OAuthCallbackPage.tsx +154 -0
  364. manigent/web/frontend/src/components/auth/OAuthLinkedBanner.tsx +44 -0
  365. manigent/web/frontend/src/components/auth/ProviderButtons.tsx +94 -0
  366. manigent/web/frontend/src/components/auth/SignupPage.tsx +437 -0
  367. manigent/web/frontend/src/components/auth/StepProgress.tsx +60 -0
  368. manigent/web/frontend/src/components/auth/VerifyEmailPage.test.tsx +155 -0
  369. manigent/web/frontend/src/components/auth/VerifyEmailPage.tsx +211 -0
  370. manigent/web/frontend/src/components/auth/index.ts +3 -0
  371. manigent/web/frontend/src/components/auth/oauthErrors.test.ts +107 -0
  372. manigent/web/frontend/src/components/auth/oauthErrors.ts +82 -0
  373. manigent/web/frontend/src/components/auth/useUsernameCheck.test.tsx +68 -0
  374. manigent/web/frontend/src/components/auth/useUsernameCheck.ts +86 -0
  375. manigent/web/frontend/src/components/beta/BetaPage.tsx +21 -0
  376. manigent/web/frontend/src/components/beta/DayAtMyDesk.tsx +120 -0
  377. manigent/web/frontend/src/components/beta/Footer.tsx +82 -0
  378. manigent/web/frontend/src/components/beta/Hero.tsx +224 -0
  379. manigent/web/frontend/src/components/beta/Nav.tsx +51 -0
  380. manigent/web/frontend/src/components/beta/Pillars.tsx +172 -0
  381. manigent/web/frontend/src/components/beta/TwoModels.tsx +146 -0
  382. manigent/web/frontend/src/components/beta/constants.ts +3 -0
  383. manigent/web/frontend/src/components/briefing/BriefingFrame.test.tsx +208 -0
  384. manigent/web/frontend/src/components/briefing/BriefingFrame.tsx +151 -0
  385. manigent/web/frontend/src/components/briefing/BriefingPage.test.tsx +176 -0
  386. manigent/web/frontend/src/components/briefing/BriefingPage.tsx +157 -0
  387. manigent/web/frontend/src/components/briefing/BriefingPage.wsDelete.test.tsx +177 -0
  388. manigent/web/frontend/src/components/briefing/briefing.css +147 -0
  389. manigent/web/frontend/src/components/briefing/heightBootstrap.test.ts +121 -0
  390. manigent/web/frontend/src/components/briefing/heightBootstrap.ts +224 -0
  391. manigent/web/frontend/src/components/briefing/parseBriefMessage.test.ts +90 -0
  392. manigent/web/frontend/src/components/briefing/sampleBriefing.ts +148 -0
  393. manigent/web/frontend/src/components/channels/ChannelBadge.tsx +35 -0
  394. manigent/web/frontend/src/components/chatroom/ChannelThreadControls.tsx +127 -0
  395. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.test.tsx +207 -0
  396. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.tsx +80 -0
  397. manigent/web/frontend/src/components/chatroom/ChatroomView.channel.test.tsx +550 -0
  398. manigent/web/frontend/src/components/chatroom/ChatroomView.flow.test.tsx +186 -0
  399. manigent/web/frontend/src/components/chatroom/ChatroomView.isolation.test.tsx +70 -0
  400. manigent/web/frontend/src/components/chatroom/ChatroomView.tsx +450 -0
  401. manigent/web/frontend/src/components/chatroom/DelegationRow.test.tsx +170 -0
  402. manigent/web/frontend/src/components/chatroom/DelegationRow.tsx +217 -0
  403. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.test.tsx +33 -0
  404. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.tsx +46 -0
  405. manigent/web/frontend/src/components/chatroom/ForwardModal.test.tsx +109 -0
  406. manigent/web/frontend/src/components/chatroom/ForwardModal.tsx +304 -0
  407. manigent/web/frontend/src/components/chatroom/HeldNotice.tsx +77 -0
  408. manigent/web/frontend/src/components/chatroom/MentionDropdown.tsx +97 -0
  409. manigent/web/frontend/src/components/chatroom/MessageInput.tsx +408 -0
  410. manigent/web/frontend/src/components/chatroom/MessageItem.tsx +425 -0
  411. manigent/web/frontend/src/components/chatroom/MessageList.outside.test.tsx +98 -0
  412. manigent/web/frontend/src/components/chatroom/MessageList.test.tsx +245 -0
  413. manigent/web/frontend/src/components/chatroom/MessageList.tsx +406 -0
  414. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.test.tsx +211 -0
  415. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.tsx +182 -0
  416. manigent/web/frontend/src/components/chatroom/OutsideBadge.tsx +18 -0
  417. manigent/web/frontend/src/components/chatroom/RoomFlowControl.tsx +49 -0
  418. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.test.tsx +47 -0
  419. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.tsx +25 -0
  420. manigent/web/frontend/src/components/chatroom/SaveToPackModal.tsx +350 -0
  421. manigent/web/frontend/src/components/chatroom/StagedAttachments.test.tsx +56 -0
  422. manigent/web/frontend/src/components/chatroom/StagedAttachments.tsx +160 -0
  423. manigent/web/frontend/src/components/chatroom/SystemNote.tsx +18 -0
  424. manigent/web/frontend/src/components/chatroom/contactSender.test.tsx +130 -0
  425. manigent/web/frontend/src/components/chatroom/index.ts +3 -0
  426. manigent/web/frontend/src/components/chatroom/recipientStatus.test.ts +344 -0
  427. manigent/web/frontend/src/components/chatroom/recipientStatus.ts +197 -0
  428. manigent/web/frontend/src/components/common/AnnouncementBanners.test.tsx +221 -0
  429. manigent/web/frontend/src/components/common/AnnouncementBanners.tsx +180 -0
  430. manigent/web/frontend/src/components/common/Avatar.test.tsx +50 -0
  431. manigent/web/frontend/src/components/common/Avatar.tsx +102 -0
  432. manigent/web/frontend/src/components/common/Badge.tsx +30 -0
  433. manigent/web/frontend/src/components/common/BrandName.tsx +16 -0
  434. manigent/web/frontend/src/components/common/ClearHistoryButton.tsx +74 -0
  435. manigent/web/frontend/src/components/common/CollapsibleHeader.tsx +64 -0
  436. manigent/web/frontend/src/components/common/ConnectionStatus.test.tsx +56 -0
  437. manigent/web/frontend/src/components/common/ConnectionStatus.tsx +28 -0
  438. manigent/web/frontend/src/components/common/DropOverlay.tsx +27 -0
  439. manigent/web/frontend/src/components/common/InlineEditableTitle.test.tsx +369 -0
  440. manigent/web/frontend/src/components/common/InlineEditableTitle.tsx +287 -0
  441. manigent/web/frontend/src/components/common/IsolationUnavailablePanel.tsx +19 -0
  442. manigent/web/frontend/src/components/common/Loading.tsx +22 -0
  443. manigent/web/frontend/src/components/common/ModelSelector.tsx +248 -0
  444. manigent/web/frontend/src/components/common/Overlay.tsx +15 -0
  445. manigent/web/frontend/src/components/common/ResizeHandle.tsx +52 -0
  446. manigent/web/frontend/src/components/common/TagAutocompleteInput.test.tsx +92 -0
  447. manigent/web/frontend/src/components/common/TagAutocompleteInput.tsx +246 -0
  448. manigent/web/frontend/src/components/common/TagPillInput.tsx +181 -0
  449. manigent/web/frontend/src/components/common/TeamAndAgentPicker.tsx +83 -0
  450. manigent/web/frontend/src/components/common/Timestamp.tsx +61 -0
  451. manigent/web/frontend/src/components/common/VerifiedBadge.tsx +20 -0
  452. manigent/web/frontend/src/components/common/index.ts +8 -0
  453. manigent/web/frontend/src/components/composer/PhPopover.tsx +239 -0
  454. manigent/web/frontend/src/components/composer/PromptEditor.test.tsx +472 -0
  455. manigent/web/frontend/src/components/composer/PromptEditor.tsx +540 -0
  456. manigent/web/frontend/src/components/composer/caret.ts +159 -0
  457. manigent/web/frontend/src/components/composer/placeholders.test.ts +352 -0
  458. manigent/web/frontend/src/components/composer/placeholders.ts +354 -0
  459. manigent/web/frontend/src/components/composer/promptEditor.css +161 -0
  460. manigent/web/frontend/src/components/computer/ComputerPage.test.tsx +452 -0
  461. manigent/web/frontend/src/components/computer/ComputerPage.tsx +811 -0
  462. manigent/web/frontend/src/components/computer/ConnectComputerModal.tsx +135 -0
  463. manigent/web/frontend/src/components/computer/CopyCommand.tsx +77 -0
  464. manigent/web/frontend/src/components/computer/DisconnectComputerModal.tsx +121 -0
  465. manigent/web/frontend/src/components/computer/SoftwareCard.tsx +145 -0
  466. manigent/web/frontend/src/components/computer/TerminalCard.test.tsx +114 -0
  467. manigent/web/frontend/src/components/computer/TerminalCard.tsx +131 -0
  468. manigent/web/frontend/src/components/computer/TerminalPanel.tsx +226 -0
  469. manigent/web/frontend/src/components/computer/TrustPanel.test.tsx +58 -0
  470. manigent/web/frontend/src/components/computer/TrustPanel.tsx +143 -0
  471. manigent/web/frontend/src/components/computer/computerCopy.test.ts +322 -0
  472. manigent/web/frontend/src/components/computer/computerCopy.ts +413 -0
  473. manigent/web/frontend/src/components/computer/sections.ts +21 -0
  474. manigent/web/frontend/src/components/computer/terminalProtocol.test.ts +135 -0
  475. manigent/web/frontend/src/components/computer/terminalProtocol.ts +170 -0
  476. manigent/web/frontend/src/components/context/ContextGuide.test.tsx +26 -0
  477. manigent/web/frontend/src/components/context/ContextGuide.tsx +144 -0
  478. manigent/web/frontend/src/components/context/ContextLanding.tsx +46 -0
  479. manigent/web/frontend/src/components/desk/CardBand.a11y.test.tsx +138 -0
  480. manigent/web/frontend/src/components/desk/CardBand.tsx +132 -0
  481. manigent/web/frontend/src/components/desk/DashTile.tsx +81 -0
  482. manigent/web/frontend/src/components/desk/DeskAgentPicker.retained.test.tsx +79 -0
  483. manigent/web/frontend/src/components/desk/DeskAgentPicker.tsx +151 -0
  484. manigent/web/frontend/src/components/desk/DeskComposer.detachTodo.test.tsx +233 -0
  485. manigent/web/frontend/src/components/desk/DeskComposer.dispatch.test.tsx +208 -0
  486. manigent/web/frontend/src/components/desk/DeskComposer.loadTodoDraft.test.tsx +242 -0
  487. manigent/web/frontend/src/components/desk/DeskComposer.restage.test.tsx +258 -0
  488. manigent/web/frontend/src/components/desk/DeskComposer.saveTodo.test.tsx +265 -0
  489. manigent/web/frontend/src/components/desk/DeskComposer.tsx +828 -0
  490. manigent/web/frontend/src/components/desk/DeskPage.crossHighlight.test.tsx +427 -0
  491. manigent/web/frontend/src/components/desk/DeskPage.link.test.tsx +271 -0
  492. manigent/web/frontend/src/components/desk/DeskPage.tsx +1066 -0
  493. manigent/web/frontend/src/components/desk/DeskPage.welcomeTour.test.tsx +262 -0
  494. manigent/web/frontend/src/components/desk/DeskSideRail.tsx +100 -0
  495. manigent/web/frontend/src/components/desk/DeskWelcomeTour.tsx +513 -0
  496. manigent/web/frontend/src/components/desk/DeskZeroState.tsx +45 -0
  497. manigent/web/frontend/src/components/desk/LabelChip.tsx +147 -0
  498. manigent/web/frontend/src/components/desk/LabelFilter.tsx +474 -0
  499. manigent/web/frontend/src/components/desk/LabelInput.test.tsx +439 -0
  500. manigent/web/frontend/src/components/desk/LabelInput.tsx +477 -0
  501. manigent/web/frontend/src/components/desk/LabelManageSheet.test.tsx +384 -0
  502. manigent/web/frontend/src/components/desk/LabelManageSheet.tsx +498 -0
  503. manigent/web/frontend/src/components/desk/PhaseRail.test.tsx +170 -0
  504. manigent/web/frontend/src/components/desk/PhaseRail.tsx +120 -0
  505. manigent/web/frontend/src/components/desk/PlanBand.test.tsx +655 -0
  506. manigent/web/frontend/src/components/desk/PlanBand.tsx +216 -0
  507. manigent/web/frontend/src/components/desk/SaveTodoModal.tsx +126 -0
  508. manigent/web/frontend/src/components/desk/SopEditor.tsx +280 -0
  509. manigent/web/frontend/src/components/desk/SopItem.tsx +171 -0
  510. manigent/web/frontend/src/components/desk/SopRail.test.tsx +517 -0
  511. manigent/web/frontend/src/components/desk/SopRail.tsx +250 -0
  512. manigent/web/frontend/src/components/desk/TicketChip.tsx +51 -0
  513. manigent/web/frontend/src/components/desk/TodoItem.attachments.test.tsx +96 -0
  514. manigent/web/frontend/src/components/desk/TodoItem.menu.test.tsx +142 -0
  515. manigent/web/frontend/src/components/desk/TodoItem.tsx +573 -0
  516. manigent/web/frontend/src/components/desk/TodoRail.longpress.test.tsx +396 -0
  517. manigent/web/frontend/src/components/desk/TodoRail.test.tsx +1155 -0
  518. manigent/web/frontend/src/components/desk/TodoRail.tsx +918 -0
  519. manigent/web/frontend/src/components/desk/TodoRowMenu.tsx +147 -0
  520. manigent/web/frontend/src/components/desk/UpdateCard.bands.test.tsx +308 -0
  521. manigent/web/frontend/src/components/desk/UpdateCard.sendBack.test.tsx +444 -0
  522. manigent/web/frontend/src/components/desk/UpdateCard.tsx +943 -0
  523. manigent/web/frontend/src/components/desk/UpdateCard.untrusted.test.tsx +161 -0
  524. manigent/web/frontend/src/components/desk/desk.css +1921 -0
  525. manigent/web/frontend/src/components/desk/desk.css.test.ts +582 -0
  526. manigent/web/frontend/src/components/desk/deskBands.test.ts +389 -0
  527. manigent/web/frontend/src/components/desk/deskBands.ts +226 -0
  528. manigent/web/frontend/src/components/desk/m16Surfaces.test.ts +134 -0
  529. manigent/web/frontend/src/components/desk/sopSyntax.ts +73 -0
  530. manigent/web/frontend/src/components/discovery/AgentDirectory.tsx +71 -0
  531. manigent/web/frontend/src/components/discovery/AgentFilterBar.tsx +46 -0
  532. manigent/web/frontend/src/components/discovery/AgentSearchBar.tsx +56 -0
  533. manigent/web/frontend/src/components/dm/AgentSummaryCard.tsx +154 -0
  534. manigent/web/frontend/src/components/dm/ComposeFields.tsx +145 -0
  535. manigent/web/frontend/src/components/dm/DMChatView.flow.test.tsx +374 -0
  536. manigent/web/frontend/src/components/dm/DMChatView.tsx +841 -0
  537. manigent/web/frontend/src/components/dm/DMZeroState.tsx +204 -0
  538. manigent/web/frontend/src/components/dm/DmComposeModal.tsx +301 -0
  539. manigent/web/frontend/src/components/dm/PersonalizeModal.tsx +366 -0
  540. manigent/web/frontend/src/components/dm/index.ts +1 -0
  541. manigent/web/frontend/src/components/files/FileItem.tsx +138 -0
  542. manigent/web/frontend/src/components/files/FileList.outside.test.tsx +29 -0
  543. manigent/web/frontend/src/components/files/FileList.tsx +48 -0
  544. manigent/web/frontend/src/components/files/FilePreview.test.tsx +75 -0
  545. manigent/web/frontend/src/components/files/FilePreview.tsx +325 -0
  546. manigent/web/frontend/src/components/files/FileViewerModal.test.tsx +157 -0
  547. manigent/web/frontend/src/components/files/FileViewerModal.tsx +261 -0
  548. manigent/web/frontend/src/components/files/HighlightedCode.tsx +39 -0
  549. manigent/web/frontend/src/components/files/SaveToAgentPanel.test.tsx +156 -0
  550. manigent/web/frontend/src/components/files/SaveToAgentPanel.tsx +545 -0
  551. manigent/web/frontend/src/components/files/index.ts +3 -0
  552. manigent/web/frontend/src/components/hero/AgentAvatar.tsx +66 -0
  553. manigent/web/frontend/src/components/hero/Button.tsx +66 -0
  554. manigent/web/frontend/src/components/hero/Hero.tsx +133 -0
  555. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.test.tsx +393 -0
  556. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.tsx +792 -0
  557. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.test.tsx +40 -0
  558. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.tsx +36 -0
  559. manigent/web/frontend/src/components/hub/HubAvatar.test.tsx +93 -0
  560. manigent/web/frontend/src/components/hub/HubAvatar.tsx +33 -0
  561. manigent/web/frontend/src/components/hub/HubChatView.test.tsx +695 -0
  562. manigent/web/frontend/src/components/hub/HubChatView.tsx +504 -0
  563. manigent/web/frontend/src/components/hub/HubContactLabel.test.tsx +92 -0
  564. manigent/web/frontend/src/components/hub/HubContactLabel.tsx +47 -0
  565. manigent/web/frontend/src/components/hub/HubDeliveryLine.tsx +49 -0
  566. manigent/web/frontend/src/components/hub/HubDisclosure.tsx +49 -0
  567. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.test.tsx +144 -0
  568. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.tsx +244 -0
  569. manigent/web/frontend/src/components/hub/HubMessageBody.test.tsx +26 -0
  570. manigent/web/frontend/src/components/hub/HubMessageBody.tsx +14 -0
  571. manigent/web/frontend/src/components/hub/HubOfflineConfirm.tsx +45 -0
  572. manigent/web/frontend/src/components/hub/HubThreadBanner.tsx +33 -0
  573. manigent/web/frontend/src/components/hub/HubThreadGuard.tsx +59 -0
  574. manigent/web/frontend/src/components/hub/RemoteAgentCard.test.tsx +50 -0
  575. manigent/web/frontend/src/components/hub/RemoteAgentCard.tsx +174 -0
  576. manigent/web/frontend/src/components/hub/RemoteAgentSection.test.tsx +446 -0
  577. manigent/web/frontend/src/components/hub/RemoteAgentSection.tsx +219 -0
  578. manigent/web/frontend/src/components/hub/ReportDialog.test.tsx +96 -0
  579. manigent/web/frontend/src/components/hub/ReportDialog.tsx +162 -0
  580. manigent/web/frontend/src/components/hub/RetainButton.test.tsx +103 -0
  581. manigent/web/frontend/src/components/hub/RetainButton.tsx +114 -0
  582. manigent/web/frontend/src/components/hub/RetainDialog.test.tsx +68 -0
  583. manigent/web/frontend/src/components/hub/RetainDialog.tsx +115 -0
  584. manigent/web/frontend/src/components/hub/RetainedList.test.tsx +94 -0
  585. manigent/web/frontend/src/components/hub/RetainedList.tsx +32 -0
  586. manigent/web/frontend/src/components/hub/RetainedRow.tsx +129 -0
  587. manigent/web/frontend/src/components/hub/StandInChip.test.tsx +37 -0
  588. manigent/web/frontend/src/components/hub/StandInChip.tsx +43 -0
  589. manigent/web/frontend/src/components/hub/UnusualNameChip.test.tsx +81 -0
  590. manigent/web/frontend/src/components/hub/UnusualNameChip.tsx +22 -0
  591. manigent/web/frontend/src/components/hub/hubLint.test.ts +265 -0
  592. manigent/web/frontend/src/components/landing/ComparisonSection.tsx +92 -0
  593. manigent/web/frontend/src/components/landing/ConsoleSection.tsx +402 -0
  594. manigent/web/frontend/src/components/landing/CrewSection.tsx +224 -0
  595. manigent/web/frontend/src/components/landing/FAQ.tsx +68 -0
  596. manigent/web/frontend/src/components/landing/Footer.tsx +58 -0
  597. manigent/web/frontend/src/components/landing/HowItWorks.tsx +117 -0
  598. manigent/web/frontend/src/components/landing/KnowledgeSection.tsx +91 -0
  599. manigent/web/frontend/src/components/landing/LandingPage.test.tsx +134 -0
  600. manigent/web/frontend/src/components/landing/LearningSection.tsx +486 -0
  601. manigent/web/frontend/src/components/landing/Nav.tsx +68 -0
  602. manigent/web/frontend/src/components/landing/Pricing.tsx +263 -0
  603. manigent/web/frontend/src/components/landing/Section.tsx +98 -0
  604. manigent/web/frontend/src/components/landing/StaffingSection.tsx +122 -0
  605. manigent/web/frontend/src/components/landing/WhyNowSection.tsx +92 -0
  606. manigent/web/frontend/src/components/landing/WhyTeamSection.tsx +73 -0
  607. manigent/web/frontend/src/components/landing/earlyAccess.ts +23 -0
  608. manigent/web/frontend/src/components/layout/AppLayout.tsx +77 -0
  609. manigent/web/frontend/src/components/layout/Sidebar.tsx +172 -0
  610. manigent/web/frontend/src/components/layout/index.ts +2 -0
  611. manigent/web/frontend/src/components/project/ActivityFeed.tsx +247 -0
  612. manigent/web/frontend/src/components/project/CreateProjectPage.tsx +543 -0
  613. manigent/web/frontend/src/components/project/DeleteProjectButton.tsx +39 -0
  614. manigent/web/frontend/src/components/project/ProjectPlanTab.phase.test.tsx +107 -0
  615. manigent/web/frontend/src/components/project/ProjectPlanTab.test.tsx +994 -0
  616. manigent/web/frontend/src/components/project/ProjectPlanTab.tsx +166 -0
  617. manigent/web/frontend/src/components/project/ProjectReportTab.tsx +139 -0
  618. manigent/web/frontend/src/components/project/ProjectView.tsx +390 -0
  619. manigent/web/frontend/src/components/project/index.ts +2 -0
  620. manigent/web/frontend/src/components/project/plan/PlanAcceptance.test.tsx +460 -0
  621. manigent/web/frontend/src/components/project/plan/PlanAcceptance.tsx +111 -0
  622. manigent/web/frontend/src/components/project/plan/PlanBlockEdit.tsx +111 -0
  623. manigent/web/frontend/src/components/project/plan/PlanBlockEditing.test.tsx +1132 -0
  624. manigent/web/frontend/src/components/project/plan/PlanBody.test.tsx +495 -0
  625. manigent/web/frontend/src/components/project/plan/PlanBody.tsx +992 -0
  626. manigent/web/frontend/src/components/project/plan/PlanCollide.tsx +254 -0
  627. manigent/web/frontend/src/components/project/plan/PlanCommentList.test.tsx +420 -0
  628. manigent/web/frontend/src/components/project/plan/PlanCommentList.tsx +391 -0
  629. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.test.tsx +226 -0
  630. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.tsx +128 -0
  631. manigent/web/frontend/src/components/project/plan/PlanDestructiveAccept.test.tsx +209 -0
  632. manigent/web/frontend/src/components/project/plan/PlanDiff.test.tsx +126 -0
  633. manigent/web/frontend/src/components/project/plan/PlanDiff.tsx +131 -0
  634. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.test.tsx +500 -0
  635. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.tsx +125 -0
  636. manigent/web/frontend/src/components/project/plan/PlanEditor.mounts.test.tsx +504 -0
  637. manigent/web/frontend/src/components/project/plan/PlanEditor.tsx +2159 -0
  638. manigent/web/frontend/src/components/project/plan/PlanHistory.tsx +69 -0
  639. manigent/web/frontend/src/components/project/plan/PlanInlineNotes.test.tsx +2398 -0
  640. manigent/web/frontend/src/components/project/plan/PlanNoteBase.test.tsx +215 -0
  641. manigent/web/frontend/src/components/project/plan/PlanNoteRow.test.tsx +562 -0
  642. manigent/web/frontend/src/components/project/plan/PlanNoteRow.tsx +908 -0
  643. manigent/web/frontend/src/components/project/plan/PlanQuotePop.tsx +254 -0
  644. manigent/web/frontend/src/components/project/plan/PlanStaleReconcile.test.tsx +244 -0
  645. manigent/web/frontend/src/components/project/plan/PlanSubmitWindow.test.tsx +281 -0
  646. manigent/web/frontend/src/components/project/plan/PlanTray.tsx +184 -0
  647. manigent/web/frontend/src/components/project/plan/PlanTrayMirror.test.tsx +242 -0
  648. manigent/web/frontend/src/components/project/plan/plan.css +1151 -0
  649. manigent/web/frontend/src/components/project/plan/planAnchor.test.tsx +459 -0
  650. manigent/web/frontend/src/components/project/plan/planAnchor.tsx +277 -0
  651. manigent/web/frontend/src/components/project/plan/planBlocks.test.tsx +470 -0
  652. manigent/web/frontend/src/components/project/plan/planBlocks.ts +242 -0
  653. manigent/web/frontend/src/components/project/plan/planContrast.test.ts +229 -0
  654. manigent/web/frontend/src/components/project/plan/planDiffs.test.ts +455 -0
  655. manigent/web/frontend/src/components/project/plan/planDiffs.ts +358 -0
  656. manigent/web/frontend/src/components/project/plan/planTestServer.ts +741 -0
  657. manigent/web/frontend/src/components/project/plan/planTransport.test.ts +160 -0
  658. manigent/web/frontend/src/components/project/plan/planTrayLogic.test.ts +1201 -0
  659. manigent/web/frontend/src/components/project/plan/planTrayLogic.ts +1164 -0
  660. manigent/web/frontend/src/components/publicAgents/PublicAgentsAccount.tsx +162 -0
  661. manigent/web/frontend/src/components/publicAgents/api.ts +55 -0
  662. manigent/web/frontend/src/components/publicAgents/parts.tsx +54 -0
  663. manigent/web/frontend/src/components/publicAgents/publicAgents.css +53 -0
  664. manigent/web/frontend/src/components/publicAgents/publicAgents.test.tsx +86 -0
  665. manigent/web/frontend/src/components/publicAgents/widget.test.ts +148 -0
  666. manigent/web/frontend/src/components/publicAgents/widget.ts +40 -0
  667. manigent/web/frontend/src/components/publicRequests/AgentDrawer.tsx +101 -0
  668. manigent/web/frontend/src/components/publicRequests/AgentPanel.tsx +173 -0
  669. manigent/web/frontend/src/components/publicRequests/AgentRankList.tsx +61 -0
  670. manigent/web/frontend/src/components/publicRequests/MatchRetainButton.tsx +67 -0
  671. manigent/web/frontend/src/components/publicRequests/ProgressBoard.tsx +74 -0
  672. manigent/web/frontend/src/components/publicRequests/PublicRequestsPage.tsx +57 -0
  673. manigent/web/frontend/src/components/publicRequests/RequestHome.tsx +99 -0
  674. manigent/web/frontend/src/components/publicRequests/RequestRoom.tsx +126 -0
  675. manigent/web/frontend/src/components/publicRequests/agentView.ts +65 -0
  676. manigent/web/frontend/src/components/publicRequests/publicRequests.css +209 -0
  677. manigent/web/frontend/src/components/publicRequests/publicRequests.test.tsx +172 -0
  678. manigent/web/frontend/src/components/publicRequests/requestCopy.ts +121 -0
  679. manigent/web/frontend/src/components/sample-request/SampleIdeaList.test.tsx +190 -0
  680. manigent/web/frontend/src/components/sample-request/SampleIdeaList.tsx +180 -0
  681. manigent/web/frontend/src/components/sample-request/SampleRequestBody.tsx +276 -0
  682. manigent/web/frontend/src/components/sample-request/assistantStarterSamples.ts +62 -0
  683. manigent/web/frontend/src/components/sample-request/sampleIdeaList.css +106 -0
  684. manigent/web/frontend/src/components/scheduled/CronPicker.tsx +80 -0
  685. manigent/web/frontend/src/components/scheduled/ScheduleMessageModal.tsx +341 -0
  686. manigent/web/frontend/src/components/scheduled/ScheduledMessagesList.tsx +157 -0
  687. manigent/web/frontend/src/components/settings/AgentSettingsPage.tsx +366 -0
  688. manigent/web/frontend/src/components/settings/ConnectedAccounts.tsx +277 -0
  689. manigent/web/frontend/src/components/settings/DefaultModelSection.test.tsx +96 -0
  690. manigent/web/frontend/src/components/settings/DefaultModelSection.tsx +212 -0
  691. manigent/web/frontend/src/components/settings/DisconnectConfirmModal.tsx +93 -0
  692. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.isolation.test.tsx +66 -0
  693. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.tsx +134 -0
  694. manigent/web/frontend/src/components/settings/InvitableAgentsPicker.tsx +182 -0
  695. manigent/web/frontend/src/components/settings/KnowledgePacksSection.tsx +867 -0
  696. manigent/web/frontend/src/components/settings/ModelConfigDialog.catalogDriven.test.tsx +79 -0
  697. manigent/web/frontend/src/components/settings/ModelConfigDialog.tsx +343 -0
  698. manigent/web/frontend/src/components/settings/ModelConfigSection.smartRouting.test.tsx +109 -0
  699. manigent/web/frontend/src/components/settings/ModelConfigSection.tsx +599 -0
  700. manigent/web/frontend/src/components/settings/ReflectionScheduleSection.tsx +170 -0
  701. manigent/web/frontend/src/components/settings/UserSettingsPage.tsx +168 -0
  702. manigent/web/frontend/src/components/settings/agent/AgentStatusStrip.tsx +71 -0
  703. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.test.tsx +81 -0
  704. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.tsx +62 -0
  705. manigent/web/frontend/src/components/settings/agent/ChannelIsolationLine.tsx +21 -0
  706. manigent/web/frontend/src/components/settings/agent/ChannelsSection.test.tsx +879 -0
  707. manigent/web/frontend/src/components/settings/agent/ChannelsSection.tsx +1124 -0
  708. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.test.ts +15 -0
  709. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.tsx +301 -0
  710. manigent/web/frontend/src/components/settings/agent/ExtraDirsList.tsx +85 -0
  711. manigent/web/frontend/src/components/settings/agent/GeneralSection.tsx +88 -0
  712. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.test.tsx +32 -0
  713. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.tsx +30 -0
  714. manigent/web/frontend/src/components/settings/agent/HubChannelCard.test.tsx +449 -0
  715. manigent/web/frontend/src/components/settings/agent/HubChannelCard.tsx +312 -0
  716. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.test.tsx +83 -0
  717. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.tsx +64 -0
  718. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.test.tsx +69 -0
  719. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.tsx +42 -0
  720. manigent/web/frontend/src/components/settings/agent/HubTurnOffDialog.tsx +88 -0
  721. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.test.tsx +26 -0
  722. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.tsx +68 -0
  723. manigent/web/frontend/src/components/settings/agent/KnowledgeSection.tsx +68 -0
  724. manigent/web/frontend/src/components/settings/agent/LineChannelCard.test.tsx +428 -0
  725. manigent/web/frontend/src/components/settings/agent/LineChannelCard.tsx +343 -0
  726. manigent/web/frontend/src/components/settings/agent/RuntimeSection.test.tsx +164 -0
  727. manigent/web/frontend/src/components/settings/agent/RuntimeSection.tsx +118 -0
  728. manigent/web/frontend/src/components/settings/agent/StorageSection.tsx +78 -0
  729. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.test.tsx +253 -0
  730. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.tsx +401 -0
  731. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.test.tsx +462 -0
  732. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.tsx +289 -0
  733. manigent/web/frontend/src/components/settings/agent/agentSettings.test.tsx +109 -0
  734. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.test.tsx +162 -0
  735. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.tsx +119 -0
  736. manigent/web/frontend/src/components/settings/agent/channelsShared.ts +83 -0
  737. manigent/web/frontend/src/components/settings/agent/fields.tsx +67 -0
  738. manigent/web/frontend/src/components/settings/agent/sections.ts +36 -0
  739. manigent/web/frontend/src/components/settings/agent/useAgentSettingsDraft.ts +129 -0
  740. manigent/web/frontend/src/components/settings/agent/useInstallSelection.ts +38 -0
  741. manigent/web/frontend/src/components/settings/primitives.tsx +98 -0
  742. manigent/web/frontend/src/components/shared/ExpandToggle.tsx +45 -0
  743. manigent/web/frontend/src/components/shared/MarkdownBody.tsx +218 -0
  744. manigent/web/frontend/src/components/shared/MarkdownBody.untrusted.test.tsx +97 -0
  745. manigent/web/frontend/src/components/shared/RenderBody.tsx +90 -0
  746. manigent/web/frontend/src/components/shared/rehypeHideComments.test.tsx +115 -0
  747. manigent/web/frontend/src/components/shared/rehypeHideComments.ts +124 -0
  748. manigent/web/frontend/src/components/sharing/AcceptSharePage.tsx +141 -0
  749. manigent/web/frontend/src/components/sharing/ShareLandingPage.tsx +89 -0
  750. manigent/web/frontend/src/components/sidebar/AccountChip.test.tsx +337 -0
  751. manigent/web/frontend/src/components/sidebar/AccountChip.tsx +407 -0
  752. manigent/web/frontend/src/components/sidebar/ActiveWorkDot.tsx +9 -0
  753. manigent/web/frontend/src/components/sidebar/AgentProfile.tsx +44 -0
  754. manigent/web/frontend/src/components/sidebar/AgentRow.tsx +71 -0
  755. manigent/web/frontend/src/components/sidebar/AgentsPanel.retained.test.tsx +105 -0
  756. manigent/web/frontend/src/components/sidebar/AgentsPanel.tsx +231 -0
  757. manigent/web/frontend/src/components/sidebar/AssistantHero.tsx +48 -0
  758. manigent/web/frontend/src/components/sidebar/ChatroomItem.tsx +48 -0
  759. manigent/web/frontend/src/components/sidebar/CommandPalette.channelThreads.test.tsx +76 -0
  760. manigent/web/frontend/src/components/sidebar/CommandPalette.reopen.test.tsx +206 -0
  761. manigent/web/frontend/src/components/sidebar/CommandPalette.test.tsx +725 -0
  762. manigent/web/frontend/src/components/sidebar/CommandPalette.tsx +774 -0
  763. manigent/web/frontend/src/components/sidebar/ComputersPanel.tsx +144 -0
  764. manigent/web/frontend/src/components/sidebar/DmIcon.tsx +46 -0
  765. manigent/web/frontend/src/components/sidebar/DmsPanel.tsx +226 -0
  766. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.channels.test.tsx +582 -0
  767. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.test.tsx +151 -0
  768. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.tsx +454 -0
  769. manigent/web/frontend/src/components/sidebar/HotkeysHelp.tsx +101 -0
  770. manigent/web/frontend/src/components/sidebar/MessageHitRow.test.tsx +84 -0
  771. manigent/web/frontend/src/components/sidebar/MessageHitRow.tsx +57 -0
  772. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.crossHighlight.test.tsx +247 -0
  773. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.delete.test.tsx +273 -0
  774. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.tsx +351 -0
  775. manigent/web/frontend/src/components/sidebar/PanelShell.tsx +48 -0
  776. manigent/web/frontend/src/components/sidebar/ProjectItem.tsx +161 -0
  777. manigent/web/frontend/src/components/sidebar/ProjectRow.tsx +151 -0
  778. manigent/web/frontend/src/components/sidebar/ProjectsPanel.tsx +80 -0
  779. manigent/web/frontend/src/components/sidebar/PublicPanel.counterpart.test.tsx +85 -0
  780. manigent/web/frontend/src/components/sidebar/PublicPanel.hub.test.tsx +147 -0
  781. manigent/web/frontend/src/components/sidebar/PublicPanel.tsx +271 -0
  782. manigent/web/frontend/src/components/sidebar/RecencyHeader.tsx +17 -0
  783. manigent/web/frontend/src/components/sidebar/ScheduledPanel.tsx +58 -0
  784. manigent/web/frontend/src/components/sidebar/SidebarAvatar.tsx +128 -0
  785. manigent/web/frontend/src/components/sidebar/SidebarRail.tsx +163 -0
  786. manigent/web/frontend/src/components/sidebar/TeamGroup.tsx +67 -0
  787. manigent/web/frontend/src/components/sidebar/dmPrefill.test.ts +69 -0
  788. manigent/web/frontend/src/components/sidebar/dmPrefill.ts +80 -0
  789. manigent/web/frontend/src/components/sidebar/index.ts +18 -0
  790. manigent/web/frontend/src/components/sidebar/railShortcuts.ts +18 -0
  791. manigent/web/frontend/src/components/today/lib.test.ts +70 -0
  792. manigent/web/frontend/src/components/today/lib.ts +296 -0
  793. manigent/web/frontend/src/components/today/today.css +113 -0
  794. manigent/web/frontend/src/components/welcome/RunnerSetup.css +253 -0
  795. manigent/web/frontend/src/components/welcome/RunnerSetup.tsx +148 -0
  796. manigent/web/frontend/src/components/welcome/WelcomePage.css +383 -0
  797. manigent/web/frontend/src/components/welcome/WelcomePage.tsx +329 -0
  798. manigent/web/frontend/src/components/welcome/welcomeCopy.test.ts +382 -0
  799. manigent/web/frontend/src/components/welcome/welcomeCopy.ts +313 -0
  800. manigent/web/frontend/src/config/llmProviders.test.ts +161 -0
  801. manigent/web/frontend/src/config/llmProviders.ts +109 -0
  802. manigent/web/frontend/src/config/providerIds.fixture.json +13 -0
  803. manigent/web/frontend/src/hooks/accountNavigation.test.tsx +138 -0
  804. manigent/web/frontend/src/hooks/index.ts +10 -0
  805. manigent/web/frontend/src/hooks/useActiveWorkBootstrap.ts +86 -0
  806. manigent/web/frontend/src/hooks/useAgentAccess.test.tsx +30 -0
  807. manigent/web/frontend/src/hooks/useAgentAccess.ts +204 -0
  808. manigent/web/frontend/src/hooks/useAgentSampleRequests.ts +27 -0
  809. manigent/web/frontend/src/hooks/useAgentSearch.ts +10 -0
  810. manigent/web/frontend/src/hooks/useAgents.ts +46 -0
  811. manigent/web/frontend/src/hooks/useAnnouncements.test.ts +112 -0
  812. manigent/web/frontend/src/hooks/useAnnouncements.ts +100 -0
  813. manigent/web/frontend/src/hooks/useAssistantAgent.ts +24 -0
  814. manigent/web/frontend/src/hooks/useAutoResizeTextarea.ts +18 -0
  815. manigent/web/frontend/src/hooks/useBridgeDeliveries.test.tsx +79 -0
  816. manigent/web/frontend/src/hooks/useBriefTabs.ts +122 -0
  817. manigent/web/frontend/src/hooks/useChannelThreads.ts +69 -0
  818. manigent/web/frontend/src/hooks/useComposeForm.ts +80 -0
  819. manigent/web/frontend/src/hooks/useComputers.ts +123 -0
  820. manigent/web/frontend/src/hooks/useContextFirstItem.ts +118 -0
  821. manigent/web/frontend/src/hooks/useCreateProject.ts +78 -0
  822. manigent/web/frontend/src/hooks/useDebouncedValue.ts +16 -0
  823. manigent/web/frontend/src/hooks/useDeleteProject.ts +27 -0
  824. manigent/web/frontend/src/hooks/useDeskDispatch.test.tsx +170 -0
  825. manigent/web/frontend/src/hooks/useDeskDispatch.ts +135 -0
  826. manigent/web/frontend/src/hooks/useDeskReadState.test.tsx +85 -0
  827. manigent/web/frontend/src/hooks/useDeskReadState.ts +150 -0
  828. manigent/web/frontend/src/hooks/useDeskRecentlyDone.test.tsx +222 -0
  829. manigent/web/frontend/src/hooks/useDeskRecentlyDone.ts +225 -0
  830. manigent/web/frontend/src/hooks/useDeskTour.ts +37 -0
  831. manigent/web/frontend/src/hooks/useDeskUpdates.channelThreads.test.tsx +91 -0
  832. manigent/web/frontend/src/hooks/useDeskUpdates.hubThreads.test.tsx +93 -0
  833. manigent/web/frontend/src/hooks/useDeskUpdates.ts +353 -0
  834. manigent/web/frontend/src/hooks/useDisconnectIntegration.ts +23 -0
  835. manigent/web/frontend/src/hooks/useDmThreads.ts +99 -0
  836. manigent/web/frontend/src/hooks/useFileDropzone.ts +93 -0
  837. manigent/web/frontend/src/hooks/useFileUpload.ts +38 -0
  838. manigent/web/frontend/src/hooks/useFiles.ts +98 -0
  839. manigent/web/frontend/src/hooks/useHub.test.tsx +237 -0
  840. manigent/web/frontend/src/hooks/useHub.ts +502 -0
  841. manigent/web/frontend/src/hooks/useInstall.ts +82 -0
  842. manigent/web/frontend/src/hooks/useLabels.test.tsx +294 -0
  843. manigent/web/frontend/src/hooks/useLabels.ts +287 -0
  844. manigent/web/frontend/src/hooks/useLogout.ts +49 -0
  845. manigent/web/frontend/src/hooks/useMentionDetection.ts +90 -0
  846. manigent/web/frontend/src/hooks/useMessageAttachments.deskLimit.test.ts +264 -0
  847. manigent/web/frontend/src/hooks/useMessageAttachments.ts +283 -0
  848. manigent/web/frontend/src/hooks/useMessageDeepLink.test.tsx +70 -0
  849. manigent/web/frontend/src/hooks/useMessageDeepLink.ts +32 -0
  850. manigent/web/frontend/src/hooks/useMessageSearch.ts +78 -0
  851. manigent/web/frontend/src/hooks/useModelConfigs.ts +178 -0
  852. manigent/web/frontend/src/hooks/useMyAgents.ts +18 -0
  853. manigent/web/frontend/src/hooks/usePointerReorder.ts +270 -0
  854. manigent/web/frontend/src/hooks/usePrefersReducedMotion.ts +18 -0
  855. manigent/web/frontend/src/hooks/useProjectPlan.ts +36 -0
  856. manigent/web/frontend/src/hooks/useProjectReport.ts +19 -0
  857. manigent/web/frontend/src/hooks/useProjects.ts +104 -0
  858. manigent/web/frontend/src/hooks/useProjectsWithChatrooms.ts +97 -0
  859. manigent/web/frontend/src/hooks/usePublicRequests.ts +89 -0
  860. manigent/web/frontend/src/hooks/useRecentAgents.ts +38 -0
  861. manigent/web/frontend/src/hooks/useRenameProject.test.tsx +185 -0
  862. manigent/web/frontend/src/hooks/useRenameProject.ts +192 -0
  863. manigent/web/frontend/src/hooks/useRoomTrust.ts +56 -0
  864. manigent/web/frontend/src/hooks/useSharing.ts +46 -0
  865. manigent/web/frontend/src/hooks/useSopRecents.ts +55 -0
  866. manigent/web/frontend/src/hooks/useSopSchedules.ts +25 -0
  867. manigent/web/frontend/src/hooks/useSops.ts +99 -0
  868. manigent/web/frontend/src/hooks/useSwitchAccount.ts +41 -0
  869. manigent/web/frontend/src/hooks/useTeamGroups.ts +48 -0
  870. manigent/web/frontend/src/hooks/useThreadOutbox.ts +68 -0
  871. manigent/web/frontend/src/hooks/useTodos.add.test.tsx +89 -0
  872. manigent/web/frontend/src/hooks/useTodos.linkProject.test.tsx +115 -0
  873. manigent/web/frontend/src/hooks/useTodos.saveDraft.test.tsx +188 -0
  874. manigent/web/frontend/src/hooks/useTodos.ts +396 -0
  875. manigent/web/frontend/src/hooks/useUntrustedBodies.test.tsx +87 -0
  876. manigent/web/frontend/src/hooks/useUntrustedBodies.ts +51 -0
  877. manigent/web/frontend/src/hooks/useWebSocket.briefTab.test.tsx +148 -0
  878. manigent/web/frontend/src/hooks/useWebSocket.channelThreads.test.tsx +82 -0
  879. manigent/web/frontend/src/hooks/useWebSocket.hostSync.test.tsx +92 -0
  880. manigent/web/frontend/src/hooks/useWebSocket.hub.test.tsx +114 -0
  881. manigent/web/frontend/src/hooks/useWebSocket.labelSync.test.tsx +168 -0
  882. manigent/web/frontend/src/hooks/useWebSocket.plan.test.tsx +168 -0
  883. manigent/web/frontend/src/hooks/useWebSocket.reconnect.test.tsx +166 -0
  884. manigent/web/frontend/src/hooks/useWebSocket.runnerVersions.test.tsx +167 -0
  885. manigent/web/frontend/src/hooks/useWebSocket.ts +495 -0
  886. manigent/web/frontend/src/hooks/useWindowFileDropGuard.ts +33 -0
  887. manigent/web/frontend/src/landing.tsx +11 -0
  888. manigent/web/frontend/src/main.tsx +17 -0
  889. manigent/web/frontend/src/private-beta.tsx +10 -0
  890. manigent/web/frontend/src/public.tsx +42 -0
  891. manigent/web/frontend/src/queryClient.ts +16 -0
  892. manigent/web/frontend/src/stores/activeWorkStore.ts +88 -0
  893. manigent/web/frontend/src/stores/announcementStore.test.ts +217 -0
  894. manigent/web/frontend/src/stores/announcementStore.ts +169 -0
  895. manigent/web/frontend/src/stores/authStore.multiSession.test.ts +304 -0
  896. manigent/web/frontend/src/stores/authStore.test.ts +235 -0
  897. manigent/web/frontend/src/stores/authStore.ts +693 -0
  898. manigent/web/frontend/src/stores/deskLabelViewStore.test.ts +162 -0
  899. manigent/web/frontend/src/stores/deskLabelViewStore.ts +234 -0
  900. manigent/web/frontend/src/stores/deskLinkStore.ts +54 -0
  901. manigent/web/frontend/src/stores/deskStore.test.ts +53 -0
  902. manigent/web/frontend/src/stores/deskStore.ts +93 -0
  903. manigent/web/frontend/src/stores/heldPostsStore.ts +78 -0
  904. manigent/web/frontend/src/stores/mcpAuthStore.ts +52 -0
  905. manigent/web/frontend/src/stores/modelSelectionStore.ts +33 -0
  906. manigent/web/frontend/src/stores/perUserStores.ts +79 -0
  907. manigent/web/frontend/src/stores/runnerVersionStore.test.ts +81 -0
  908. manigent/web/frontend/src/stores/runnerVersionStore.ts +60 -0
  909. manigent/web/frontend/src/stores/skillAuthStore.ts +54 -0
  910. manigent/web/frontend/src/stores/switchReset.test.ts +236 -0
  911. manigent/web/frontend/src/stores/tokenExp.ts +32 -0
  912. manigent/web/frontend/src/stores/uiStore.ts +186 -0
  913. manigent/web/frontend/src/stores/unreadStore.ts +204 -0
  914. manigent/web/frontend/src/stores/unsavedGuard.ts +22 -0
  915. manigent/web/frontend/src/styles/design/fonts/JetBrainsMono-Latin.woff2 +0 -0
  916. manigent/web/frontend/src/styles/design/fonts/Nunito-Latin.woff2 +0 -0
  917. manigent/web/frontend/src/styles/design/fonts/Nunito-LatinExt.woff2 +0 -0
  918. manigent/web/frontend/src/styles/design/tokens.css +160 -0
  919. manigent/web/frontend/src/styles/homepage.css +242 -0
  920. manigent/web/frontend/src/styles/index.css +126 -0
  921. manigent/web/frontend/src/styles/landing.css +52 -0
  922. manigent/web/frontend/src/test/authFixtures.ts +154 -0
  923. manigent/web/frontend/src/test/fetchHarness.ts +203 -0
  924. manigent/web/frontend/src/test/fixtures/channelThreads.smoke.json +42 -0
  925. manigent/web/frontend/src/test/hubFixtures.ts +212 -0
  926. manigent/web/frontend/src/test/queryWrapper.tsx +9 -0
  927. manigent/web/frontend/src/test/suiteHygiene.test.ts +212 -0
  928. manigent/web/frontend/src/types/channelThreadName.test.ts +16 -0
  929. manigent/web/frontend/src/types/index.ts +903 -0
  930. manigent/web/frontend/src/utils/agentAccessCopy.test.ts +53 -0
  931. manigent/web/frontend/src/utils/agentAccessCopy.ts +231 -0
  932. manigent/web/frontend/src/utils/agentDisplay.ts +81 -0
  933. manigent/web/frontend/src/utils/briefSchedule.ts +25 -0
  934. manigent/web/frontend/src/utils/channelThreads.test.ts +109 -0
  935. manigent/web/frontend/src/utils/channelThreads.ts +179 -0
  936. manigent/web/frontend/src/utils/chatroomDisplay.ts +11 -0
  937. manigent/web/frontend/src/utils/deskLinks.test.ts +155 -0
  938. manigent/web/frontend/src/utils/deskLinks.ts +115 -0
  939. manigent/web/frontend/src/utils/deskRecipient.ts +29 -0
  940. manigent/web/frontend/src/utils/formatDate.ts +102 -0
  941. manigent/web/frontend/src/utils/frontDesk.ts +65 -0
  942. manigent/web/frontend/src/utils/hubChannelView.test.ts +178 -0
  943. manigent/web/frontend/src/utils/hubChannelView.ts +307 -0
  944. manigent/web/frontend/src/utils/hubDelivery.test.ts +105 -0
  945. manigent/web/frontend/src/utils/hubDelivery.ts +137 -0
  946. manigent/web/frontend/src/utils/hubDisplay.test.ts +157 -0
  947. manigent/web/frontend/src/utils/hubDisplay.ts +221 -0
  948. manigent/web/frontend/src/utils/hubThread.test.ts +184 -0
  949. manigent/web/frontend/src/utils/hubThread.ts +247 -0
  950. manigent/web/frontend/src/utils/labelErrors.test.ts +127 -0
  951. manigent/web/frontend/src/utils/labelErrors.ts +123 -0
  952. manigent/web/frontend/src/utils/labelView.test.ts +846 -0
  953. manigent/web/frontend/src/utils/labelView.ts +760 -0
  954. manigent/web/frontend/src/utils/legalUrls.ts +11 -0
  955. manigent/web/frontend/src/utils/messageSender.test.ts +32 -0
  956. manigent/web/frontend/src/utils/messageSender.ts +29 -0
  957. manigent/web/frontend/src/utils/oauthScopeCopy.ts +73 -0
  958. manigent/web/frontend/src/utils/personalize.ts +49 -0
  959. manigent/web/frontend/src/utils/reachableAgents.test.ts +36 -0
  960. manigent/web/frontend/src/utils/reachableAgents.ts +24 -0
  961. manigent/web/frontend/src/utils/recencyBuckets.ts +61 -0
  962. manigent/web/frontend/src/utils/roomTrust.test.ts +94 -0
  963. manigent/web/frontend/src/utils/roomTrust.ts +111 -0
  964. manigent/web/frontend/src/utils/snippetHighlight.test.ts +63 -0
  965. manigent/web/frontend/src/utils/snippetHighlight.ts +42 -0
  966. manigent/web/frontend/src/utils/sopRecipient.ts +37 -0
  967. manigent/web/frontend/src/utils/sopRequest.test.ts +91 -0
  968. manigent/web/frontend/src/utils/sopRequest.ts +68 -0
  969. manigent/web/frontend/src/utils/sopSchedule.ts +26 -0
  970. manigent/web/frontend/src/utils/sopSection.test.ts +148 -0
  971. manigent/web/frontend/src/utils/sopSection.ts +143 -0
  972. manigent/web/frontend/src/utils/textSearch.test.ts +60 -0
  973. manigent/web/frontend/src/utils/textSearch.ts +43 -0
  974. manigent/web/frontend/src/utils/thoughtPartner.ts +26 -0
  975. manigent/web/frontend/src/utils/threadName.test.ts +131 -0
  976. manigent/web/frontend/src/utils/threadName.ts +122 -0
  977. manigent/web/frontend/src/utils/ticketView.test.ts +296 -0
  978. manigent/web/frontend/src/utils/ticketView.ts +175 -0
  979. manigent/web/frontend/src/utils/todoAttachments.desiredState.test.ts +180 -0
  980. manigent/web/frontend/src/utils/todoAttachments.ts +126 -0
  981. manigent/web/frontend/src/utils/todoDraft.test.ts +84 -0
  982. manigent/web/frontend/src/utils/todoDraft.ts +46 -0
  983. manigent/web/frontend/src/utils/todoSearch.test.ts +82 -0
  984. manigent/web/frontend/src/utils/todoSearch.ts +43 -0
  985. manigent/web/frontend/src/utils/versionMatch.test.ts +295 -0
  986. manigent/web/frontend/src/utils/versionMatch.ts +245 -0
  987. manigent/web/frontend/src/vite-env.d.ts +1 -0
  988. manigent/web/frontend/tailwind.config.js +86 -0
  989. manigent/web/frontend/tos.html +188 -0
  990. manigent/web/frontend/tsconfig.dts.json +25 -0
  991. manigent/web/frontend/tsconfig.json +26 -0
  992. manigent/web/frontend/tsconfig.node.json +11 -0
  993. manigent/web/frontend/vite.config.ts +41 -0
  994. manigent/web/frontend/vitest.config.ts +16 -0
  995. manigent/ws_edge/app.py +514 -0
  996. manigent/ws_edge/auth.py +104 -0
  997. manigent/ws_edge/link.py +282 -0
  998. manigent/ws_edge/registry.py +206 -0
  999. manigent/ws_edge/terminal_relay.py +269 -0
  1000. manigent_server-0.1.0.dist-info/METADATA +138 -0
  1001. manigent_server-0.1.0.dist-info/RECORD +1004 -0
  1002. manigent_server-0.1.0.dist-info/WHEEL +4 -0
  1003. manigent_server-0.1.0.dist-info/entry_points.txt +2 -0
  1004. manigent_server-0.1.0.dist-info/licenses/LICENSE +68 -0
@@ -0,0 +1,1310 @@
1
+ /* SPDX-License-Identifier: BUSL-1.1
2
+ * manigent/server/channels/webchat_widget.js
3
+ * The Manigent web chat widget, served at /api/channels/widget.js. Plain
4
+ * JavaScript, no dependencies, no build step: it works on any website.
5
+ *
6
+ * <script src="https://<server>/api/channels/widget.js" data-key="cmw_..." async></script>
7
+ *
8
+ * Attributes:
9
+ * data-key / data-keys one agent's key, or several comma-separated. Every
10
+ * script tag on a page shares ONE dock, so pasting two
11
+ * agents' snippets gives two chat heads.
12
+ * data-mode "dock" (default): round chat heads at the bottom right;
13
+ * each opens a chat window, several side by side, each
14
+ * minimisable back to its head.
15
+ * "fullpage": one chat filling the page (the hosted chat).
16
+ * data-open open the first key's window at once.
17
+ * data-color accent color (any CSS color).
18
+ *
19
+ * Pages that drive it themselves use window.ManigentChat:
20
+ * add(key) list an agent in the compose picker (no window)
21
+ * open(key) open (or bring forward) its window
22
+ * minimize(key) back to a chat head
23
+ * close(key) window and head gone; still in the picker
24
+ * list() [{key, state, name}]
25
+ * Calls made before the script loads may be queued:
26
+ * window.ManigentChat = window.ManigentChat || []; ManigentChat.push(['open', key])
27
+ *
28
+ * Everything renders inside a closed shadow root, so the host page's CSS cannot
29
+ * reach it and its CSS cannot reach the page. Every call is a CORS "simple"
30
+ * request (text/plain body, no custom headers, no cookies). Each key's visitor
31
+ * id, token and transcript live in localStorage, so a reload continues the
32
+ * conversation. Agent text is rendered as markdown by building DOM nodes; it is
33
+ * never inserted as HTML, and only http(s)/mailto links become anchors.
34
+ */
35
+ (function () {
36
+ 'use strict'
37
+
38
+ var script = document.currentScript
39
+ var existing = window.ManigentChat
40
+ if (existing && existing.__manager) {
41
+ existing.__manager.fromScript(script)
42
+ return
43
+ }
44
+ var queued = Array.isArray(existing) ? existing.slice() : []
45
+
46
+ // -- small helpers -----------------------------------------------------
47
+
48
+ var MAX_KEPT = 200
49
+ var PENDING_MS = 3 * 60 * 1000
50
+ var SEND_ERROR = 'Sorry \u2014 I couldn\u2019t reply just now. Please try again.'
51
+ var GENERIC_STARTERS = [
52
+ 'Who are you and what can you do?',
53
+ 'What kinds of requests can you help with?',
54
+ 'How do I get started?',
55
+ ]
56
+ var AV = ['#e63b8a', '#0fb3a2', '#2fbf5a', '#d48a1c', '#4b58c2', '#6f6f7a']
57
+ var SVG_NS = 'http://www.w3.org/2000/svg'
58
+ var PATHS = {
59
+ reset: 'M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5',
60
+ copy: 'M9 9h11v11H9zM5 15H4V4h11v1',
61
+ arrow: 'M5 12h14M13 6l6 6-6 6',
62
+ up: 'M12 19V5M5 12l7-7 7 7',
63
+ min: 'M5 12h14',
64
+ close: 'M6 6l12 12M18 6L6 18',
65
+ compose: 'M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z',
66
+ back: 'M15 18l-6-6 6-6',
67
+ search: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3',
68
+ }
69
+
70
+ // Same hash as the app's SidebarAvatar, so an agent keeps its color everywhere.
71
+ function colorFor(seed) {
72
+ var hash = 0
73
+ for (var i = 0; i < seed.length; i++) hash = seed.charCodeAt(i) + ((hash << 5) - hash)
74
+ return AV[Math.abs(hash) % AV.length]
75
+ }
76
+
77
+ function h(tag, attrs, children) {
78
+ var el = document.createElement(tag)
79
+ if (attrs) {
80
+ Object.keys(attrs).forEach(function (k) {
81
+ var v = attrs[k]
82
+ if (v === null || v === undefined || v === false) return
83
+ if (k === 'class') el.className = v
84
+ else if (k === 'text') el.textContent = v
85
+ else if (k.slice(0, 2) === 'on') el.addEventListener(k.slice(2), v)
86
+ else el.setAttribute(k, v === true ? '' : v)
87
+ })
88
+ }
89
+ ;(children || []).forEach(function (c) {
90
+ if (c === null || c === undefined || c === false) return
91
+ el.appendChild(typeof c === 'string' ? document.createTextNode(c) : c)
92
+ })
93
+ return el
94
+ }
95
+
96
+ function icon(name, size, stroke) {
97
+ var svg = document.createElementNS(SVG_NS, 'svg')
98
+ svg.setAttribute('width', size || 16)
99
+ svg.setAttribute('height', size || 16)
100
+ svg.setAttribute('viewBox', '0 0 24 24')
101
+ svg.setAttribute('fill', 'none')
102
+ svg.setAttribute('stroke', 'currentColor')
103
+ svg.setAttribute('stroke-width', stroke || 2)
104
+ svg.setAttribute('stroke-linecap', 'round')
105
+ svg.setAttribute('stroke-linejoin', 'round')
106
+ svg.setAttribute('aria-hidden', 'true')
107
+ var path = document.createElementNS(SVG_NS, 'path')
108
+ path.setAttribute('d', PATHS[name])
109
+ svg.appendChild(path)
110
+ return svg
111
+ }
112
+
113
+ function avatar(name, seed, cls) {
114
+ return h('div', { class: 'av' + (cls ? ' ' + cls : ''), style: 'background:' + colorFor(seed || name), 'aria-hidden': 'true' },
115
+ [((name || '?').trim()[0] || '?').toUpperCase()])
116
+ }
117
+
118
+ function loadJSON(key) {
119
+ try { return JSON.parse(localStorage.getItem(key) || 'null') } catch (e) { return null }
120
+ }
121
+ function saveJSON(key, value) {
122
+ try { localStorage.setItem(key, JSON.stringify(value)) } catch (e) { /* full or blocked */ }
123
+ }
124
+
125
+ function newId() {
126
+ var bytes = new Uint8Array(12)
127
+ ;(window.crypto || window.msCrypto).getRandomValues(bytes)
128
+ return Array.prototype.map.call(bytes, function (b) { return ('0' + b.toString(16)).slice(-2) }).join('')
129
+ }
130
+
131
+ // -- markdown, as DOM nodes ----------------------------------------------
132
+ // A CommonMark/GFM subset. Agent text only ever lands in text nodes or in
133
+ // attributes whose values are checked (href) or fixed (style alignments).
134
+
135
+ var PUNCT = /[!-\/:-@\[-`{-~]/
136
+ var SPACE = /\s/
137
+
138
+ function safeUrl(url) {
139
+ return /^(https?:\/\/|mailto:)/i.test(url)
140
+ }
141
+
142
+ function link(href) {
143
+ return h('a', { href: href, target: '_blank', rel: 'noopener noreferrer nofollow' })
144
+ }
145
+
146
+ function unescape(s) {
147
+ return s.replace(/\\([!-\/:-@\[-`{-~])/g, '$1')
148
+ }
149
+
150
+ function lead(line) {
151
+ return line.match(/^ */)[0].length
152
+ }
153
+
154
+ // Where a backtick run exactly `len` long starts, at or after `from`; -1 if none.
155
+ function closingTicks(text, len, from) {
156
+ for (var j = text.indexOf('`', from); j >= 0; j = text.indexOf('`', j)) {
157
+ var k = j
158
+ while (text[k] === '`') k++
159
+ if (k - j === len) return j
160
+ j = k
161
+ }
162
+ return -1
163
+ }
164
+
165
+ // [label](url "title") with text[i] === '['. Brackets nest in the label and
166
+ // parentheses must balance in the url, so wiki/Foo_(bar) stays whole.
167
+ function parseLink(text, i) {
168
+ var depth = 0
169
+ var j = i
170
+ for (; j < text.length; j++) {
171
+ if (text[j] === '\\') j++
172
+ else if (text[j] === '[') depth++
173
+ else if (text[j] === ']' && !--depth) break
174
+ }
175
+ if (j >= text.length || text[j + 1] !== '(') return null
176
+ var k = j + 2
177
+ var url
178
+ while (text[k] === ' ') k++
179
+ if (text[k] === '<') {
180
+ var gt = text.indexOf('>', k)
181
+ if (gt < 0) return null
182
+ url = text.slice(k + 1, gt)
183
+ k = gt + 1
184
+ } else {
185
+ var start = k
186
+ var parens = 0
187
+ for (; k < text.length && !SPACE.test(text[k]); k++) {
188
+ if (text[k] === '\\') k++
189
+ else if (text[k] === '(') parens++
190
+ else if (text[k] === ')') { if (!parens) break; parens-- }
191
+ }
192
+ if (parens) return null
193
+ url = text.slice(start, k)
194
+ }
195
+ var tail = text.slice(k).match(/^\s*(?:"[^"]*"|'[^']*'|\([^)]*\))?\s*\)/)
196
+ if (!tail) return null
197
+ return { label: text.slice(i + 1, j), url: unescape(url).trim(), end: k + tail[0].length }
198
+ }
199
+
200
+ // A bare URL loses trailing punctuation, and a ')' it never opened (GFM).
201
+ function trimUrl(url) {
202
+ for (;;) {
203
+ var last = url.slice(-1)
204
+ if (/[.,;:!?*_~]/.test(last)) url = url.slice(0, -1)
205
+ else if (last === ')' && url.split(')').length > url.split('(').length) url = url.slice(0, -1)
206
+ else return url
207
+ }
208
+ }
209
+
210
+ // CommonMark's emphasis pass (without its openers_bottom speed-up): each
211
+ // closing run pairs with the nearest opener of the same character, and the
212
+ // tokens between them move into <em>, <strong> or <del>.
213
+ function pairDelims(toks) {
214
+ var c = 0
215
+ while (c < toks.length) {
216
+ var cl = toks[c]
217
+ var o = cl.ch && cl.close ? c - 1 : -1
218
+ for (; o >= 0; o--) {
219
+ var op = toks[o]
220
+ if (op.ch !== cl.ch || !op.open) continue
221
+ // the rule of three: in *a**b* the ** pairs with nothing
222
+ if ((op.close || cl.open) && (op.run + cl.run) % 3 === 0 && (op.run % 3 || cl.run % 3)) continue
223
+ break
224
+ }
225
+ if (o < 0) { c++; continue }
226
+ op = toks[o]
227
+ var use = cl.ch === '~' ? 2 : Math.min(op.len, cl.len, 2)
228
+ var el = h(cl.ch === '~' ? 'del' : use === 2 ? 'strong' : 'em')
229
+ emit(el, toks.splice(o + 1, c - o - 1, { node: el }))
230
+ op.len -= use
231
+ cl.len -= use
232
+ c = o + 2
233
+ if (!op.len) toks.splice(o, 1), c--
234
+ if (!cl.len) toks.splice(c, 1)
235
+ }
236
+ }
237
+
238
+ function emit(parent, toks) {
239
+ toks.forEach(function (t) {
240
+ parent.appendChild(t.node || document.createTextNode(t.ch ? Array(t.len + 1).join(t.ch) : t.text))
241
+ })
242
+ }
243
+
244
+ // Inline markdown: escapes, code spans, links, autolinks, then emphasis.
245
+ // Inside a link label (inLink) nothing becomes a further link.
246
+ function inline(parent, text, inLink) {
247
+ var toks = []
248
+ var buf = ''
249
+ var push = function (tok) {
250
+ if (buf) toks.push({ text: buf })
251
+ buf = ''
252
+ toks.push(tok)
253
+ }
254
+ var i = 0
255
+ var m
256
+ while (i < text.length) {
257
+ var c = text[i]
258
+ var prev = text[i - 1] || ' '
259
+ if (c === '\\' && PUNCT.test(text[i + 1] || '')) {
260
+ buf += text[i + 1]
261
+ i += 2
262
+ } else if (c === '\n') {
263
+ push({ node: h('br') })
264
+ i++
265
+ } else if (c === '`') {
266
+ var run = text.slice(i).match(/^`+/)[0].length
267
+ var end = closingTicks(text, run, i + run)
268
+ if (end < 0) {
269
+ buf += text.substr(i, run)
270
+ i += run
271
+ } else {
272
+ var code = text.slice(i + run, end).replace(/\n/g, ' ')
273
+ if (/^ [\s\S]*\S[\s\S]* $/.test(code)) code = code.slice(1, -1)
274
+ push({ node: h('code', { text: code }) })
275
+ i = end + run
276
+ }
277
+ } else if (!inLink && (c === '[' || (c === '!' && text[i + 1] === '[')) &&
278
+ (m = parseLink(text, c === '!' ? i + 1 : i))) {
279
+ var safe = safeUrl(m.url)
280
+ var a = safe ? link(m.url) : document.createDocumentFragment()
281
+ // an image is only ever a link labelled with its alt text, never an <img>
282
+ if (c === '!') a.appendChild(document.createTextNode(unescape(m.label) || (safe ? m.url : '')))
283
+ else inline(a, m.label, true)
284
+ push({ node: a })
285
+ i = m.end
286
+ } else if (!inLink && c === '<' && (m = text.slice(i).match(/^<((?:https?:\/\/|mailto:)[^\s<>]+)>/i))) {
287
+ push({ node: link(m[1]) })
288
+ toks[toks.length - 1].node.textContent = m[1]
289
+ i += m[0].length
290
+ } else if (!inLink && (c === 'h' || c === 'H') && !/[A-Za-z0-9]/.test(prev) &&
291
+ (m = text.slice(i).match(/^https?:\/\/[^\s<>"'\]]+/i))) {
292
+ var url = trimUrl(m[0])
293
+ push({ node: link(url) })
294
+ toks[toks.length - 1].node.textContent = url
295
+ i += url.length
296
+ } else if (c === '*' || c === '_' || c === '~') {
297
+ var len = 1
298
+ while (text[i + len] === c) len++
299
+ var next = text[i + len] || ' '
300
+ var left = !SPACE.test(next) && (!PUNCT.test(next) || SPACE.test(prev) || PUNCT.test(prev))
301
+ var right = !SPACE.test(prev) && (!PUNCT.test(prev) || SPACE.test(next) || PUNCT.test(next))
302
+ if (c === '~' && len !== 2) buf += text.substr(i, len)
303
+ else push({ ch: c, len: len, run: len,
304
+ open: c === '_' ? left && (!right || PUNCT.test(prev)) : left,
305
+ close: c === '_' ? right && (!left || PUNCT.test(next)) : right })
306
+ i += len
307
+ } else {
308
+ buf += c
309
+ i++
310
+ }
311
+ }
312
+ if (buf) toks.push({ text: buf })
313
+ pairDelims(toks)
314
+ emit(parent, toks)
315
+ }
316
+
317
+ var FENCE_RE = /^( {0,3})(`{3,}(?=[^`]*$)|~{3,})(.*)$/
318
+ var HR_RE = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/
319
+ var HEAD_RE = /^ {0,3}#{1,6}(?:[ \t]+(.*?))?(?:[ \t]+#+)?[ \t]*$/
320
+ var QUOTE_RE = /^ {0,3}> ?/
321
+ var LIST_RE = /^( *)([-*+]|\d{1,9}[.)])([ \t]+)(.*)$/
322
+ var SEP_RE = /^ {0,3}\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/
323
+
324
+ // A table row's cells; an escaped \| is a literal pipe, not a separator.
325
+ function cells(line) {
326
+ var t = line.trim()
327
+ if (t[0] === '|') t = t.slice(1)
328
+ if (t.slice(-1) === '|' && t.slice(-2, -1) !== '\\') t = t.slice(0, -1)
329
+ var out = []
330
+ var cur = ''
331
+ for (var k = 0; k < t.length; k++) {
332
+ if (t[k] === '\\' && k + 1 < t.length) cur += t[k + 1] === '|' ? '|' : t[k] + t[k + 1], k++
333
+ else if (t[k] === '|') out.push(cur.trim()), cur = ''
334
+ else cur += t[k]
335
+ }
336
+ out.push(cur.trim())
337
+ return out
338
+ }
339
+
340
+ // A GFM table: a header row, then a |---|:--:| row with as many cells.
341
+ function isTable(lines, i) {
342
+ return i + 1 < lines.length && lines[i].indexOf('|') >= 0 && lines[i + 1].indexOf('|') >= 0 &&
343
+ SEP_RE.test(lines[i + 1]) && cells(lines[i]).length === cells(lines[i + 1]).length
344
+ }
345
+
346
+ function startsBlock(lines, i) {
347
+ var l = lines[i]
348
+ return FENCE_RE.test(l) || HR_RE.test(l) || HEAD_RE.test(l) || QUOTE_RE.test(l) || LIST_RE.test(l) || isTable(lines, i)
349
+ }
350
+
351
+ function table(parent, lines, i) {
352
+ var head = cells(lines[i])
353
+ var align = cells(lines[i + 1]).map(function (c) {
354
+ return /^:-+:$/.test(c) ? 'center' : /-:$/.test(c) ? 'right' : /^:/.test(c) ? 'left' : ''
355
+ })
356
+ var row = function (tag, values) {
357
+ return h('tr', null, head.map(function (_, k) {
358
+ var cell = h(tag, { style: align[k] ? 'text-align:' + align[k] : null })
359
+ inline(cell, values[k] || '')
360
+ return cell
361
+ }))
362
+ }
363
+ var rows = []
364
+ for (i += 2; i < lines.length && lines[i].indexOf('|') >= 0 && !startsBlock(lines, i); i++) rows.push(row('td', cells(lines[i])))
365
+ parent.appendChild(h('div', { class: 'tbl' }, [
366
+ h('table', null, [h('thead', null, [row('th', head)]), h('tbody', null, rows)]),
367
+ ]))
368
+ return i
369
+ }
370
+
371
+ // One list from lines[i]. A line indented past an item's marker belongs to
372
+ // the item (continuation paragraphs, nested lists, code), so a list survives
373
+ // them and keeps its numbering; each item's lines are parsed as blocks.
374
+ function list(parent, lines, i) {
375
+ var first = lines[i].match(LIST_RE)
376
+ var ordered = /\d/.test(first[2])
377
+ var el = parent.appendChild(h(ordered ? 'ol' : 'ul'))
378
+ if (ordered && parseInt(first[2], 10) !== 1) el.setAttribute('start', parseInt(first[2], 10))
379
+ var m
380
+ while (i < lines.length && (m = lines[i].match(LIST_RE)) && /\d/.test(m[2]) === ordered && !HR_RE.test(lines[i])) {
381
+ var indent = m[1].length
382
+ var width = indent + m[2].length + (m[3].length > 4 ? 1 : m[3].length)
383
+ var body = [m[4]]
384
+ for (i++; i < lines.length; i++) {
385
+ var l = lines[i]
386
+ if (!l.trim()) body.push('')
387
+ else if (lead(l) > indent) body.push(l.slice(Math.min(lead(l), width)))
388
+ else if (body[body.length - 1] && !startsBlock(lines, i)) body.push(l.trim()) // lazy continuation
389
+ else break
390
+ }
391
+ while (body.length > 1 && !body[body.length - 1]) body.pop()
392
+ // a blank line inside the item makes it loose: its paragraphs get <p>s
393
+ blocks(el.appendChild(h('li')), body, body.indexOf('') < 0)
394
+ }
395
+ return i
396
+ }
397
+
398
+ // Block markdown into `parent`. `tight` (list items) writes paragraph text
399
+ // straight into the parent instead of wrapping it in <p>.
400
+ function blocks(parent, lines, tight) {
401
+ var para = []
402
+ var flush = function () {
403
+ if (!para.length) return
404
+ inline(tight ? parent : parent.appendChild(h('p')), para.join('\n'))
405
+ para = []
406
+ }
407
+ var i = 0
408
+ var m
409
+ while (i < lines.length) {
410
+ var line = lines[i]
411
+ if ((m = line.match(FENCE_RE))) {
412
+ // closed only by the same character, at least as long, then nothing but spaces
413
+ flush()
414
+ var fence = m[2]
415
+ var dedent = new RegExp('^ {0,' + m[1].length + '}')
416
+ var body = []
417
+ for (i++; i < lines.length; i++) {
418
+ var close = lines[i].match(/^ {0,3}(`+|~+)[ \t]*$/)
419
+ if (close && close[1][0] === fence[0] && close[1].length >= fence.length) break
420
+ body.push(lines[i].replace(dedent, ''))
421
+ }
422
+ i++
423
+ parent.appendChild(h('pre', null, [h('code', { text: body.join('\n') })]))
424
+ } else if (HR_RE.test(line)) {
425
+ flush()
426
+ parent.appendChild(h('hr'))
427
+ i++
428
+ } else if (LIST_RE.test(line)) {
429
+ flush()
430
+ i = list(parent, lines, i)
431
+ } else if ((m = line.match(HEAD_RE))) {
432
+ flush()
433
+ inline(parent.appendChild(h('p')).appendChild(h('strong')), m[1] || '')
434
+ i++
435
+ } else if (QUOTE_RE.test(line)) {
436
+ flush()
437
+ var quoted = []
438
+ while (i < lines.length && QUOTE_RE.test(lines[i])) quoted.push(lines[i++].replace(QUOTE_RE, ''))
439
+ blocks(parent.appendChild(h('blockquote')), quoted)
440
+ } else if (isTable(lines, i)) {
441
+ flush()
442
+ i = table(parent, lines, i)
443
+ } else {
444
+ if (line.trim()) para.push(line.trim())
445
+ else flush()
446
+ i++
447
+ }
448
+ }
449
+ flush()
450
+ }
451
+
452
+ function markdown(text) {
453
+ var frag = document.createDocumentFragment()
454
+ var lines = String(text || '').replace(/\r\n?/g, '\n').split('\n').map(function (l) {
455
+ return l.replace(/^[ \t]+/, function (ws) { return ws.replace(/\t/g, ' ') })
456
+ })
457
+ blocks(frag, lines)
458
+ return frag
459
+ }
460
+
461
+ // -- styles -------------------------------------------------------------
462
+
463
+ var CSS = [
464
+ ':host{all:initial}',
465
+ '.root{--accent:#6b2aa0;--accent-deep:#52217b;--accent-soft:#e4d6ef;--accent-softer:#f1e6fa;',
466
+ '--ink-1:#1f1826;--ink-2:#433859;--ink-3:#7a6e8c;--ink-4:#a79bb7;--line-1:#ece5f1;--line-2:#dfd4e6;',
467
+ '--line-strong:#c9bcd3;--pill:#f1edf5;--proposal:#f6f0fc;--go-fg:#0d6d57;--go-bg:#d9efe5;--online:#2fbf5a;',
468
+ 'font-family:"Nunito",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:14px;',
469
+ 'line-height:1.45;font-weight:500;color:var(--ink-1);-webkit-font-smoothing:antialiased}',
470
+ '.root.custom{--accent-deep:var(--accent);--accent-soft:color-mix(in srgb,var(--accent) 20%,#fff);',
471
+ '--accent-softer:color-mix(in srgb,var(--accent) 9%,#fff);--proposal:color-mix(in srgb,var(--accent) 6%,#fff)}',
472
+ '*{box-sizing:border-box}button{font-family:inherit}',
473
+ 'a{color:var(--accent-deep)}a:hover{color:var(--accent)}',
474
+ ':focus-visible{outline:2px solid var(--accent);outline-offset:2px}',
475
+ '.av{width:36px;height:36px;border-radius:11px;color:#fff;font-weight:800;font-size:15px;display:flex;',
476
+ 'align-items:center;justify-content:center;flex:none}',
477
+ '.av.xl{width:72px;height:72px;border-radius:20px;font-size:28px}.av.sm{width:28px;height:28px;border-radius:8px;font-size:12px}',
478
+ '.av.lg{width:56px;height:56px;border-radius:16px;font-size:23px}',
479
+ '.mono{font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;color:var(--ink-3)}',
480
+ '.badge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;border-radius:999px;',
481
+ 'padding:2px 9px;background:var(--pill);color:var(--ink-2);white-space:nowrap}',
482
+ '.badge.go{background:var(--go-bg);color:var(--go-fg)}.badge i{width:7px;height:7px;border-radius:50%;background:currentColor}',
483
+ // one chat
484
+ '.chat{display:flex;flex-direction:column;background:#fff;min-height:0;overflow:hidden}',
485
+ '.head{display:flex;gap:12px;align-items:center;padding:12px 18px;border-bottom:1px solid var(--line-1)}',
486
+ '.who{flex:1;min-width:0}.who .n{font-size:15.5px;font-weight:800;display:flex;gap:8px;align-items:center;flex-wrap:wrap}',
487
+ '.who .s{font-size:12.5px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
488
+ '.who .s a{color:var(--ink-2);font-weight:700;text-decoration:none}.who .s a:hover{color:var(--accent-deep)}',
489
+ '.ib{border:0;background:transparent;color:var(--ink-3);width:34px;height:34px;border-radius:9px;display:flex;',
490
+ 'align-items:center;justify-content:center;cursor:pointer;flex:none}.ib:hover{background:var(--pill);color:var(--ink-1)}',
491
+ '.scroll{flex:1;overflow:auto;padding:26px 22px 10px;min-height:0}',
492
+ '.intro{display:flex;flex-direction:column;align-items:center;text-align:center;padding:30px 10px 10px;gap:10px}',
493
+ '.intro h1{font-size:22px;font-weight:800;margin:6px 0 0;letter-spacing:-.01em;color:var(--ink-1)}',
494
+ '.intro p{margin:0;font-size:14.5px;color:var(--ink-2);line-height:1.55;max-width:500px}',
495
+ '.note{font-size:13px;color:var(--ink-2);background:var(--proposal);border:1px solid var(--line-1);border-radius:10px;',
496
+ 'padding:10px 14px;max-width:500px;text-align:left;line-height:1.5}.note b{color:var(--ink-1)}',
497
+ '.starters{display:flex;flex-direction:column;gap:8px;width:100%;max-width:440px;margin-top:10px}',
498
+ '.starters button{border:1px solid var(--line-2);background:#fff;border-radius:12px;padding:10px 14px;font-size:14px;',
499
+ 'font-weight:600;color:var(--ink-1);text-align:left;cursor:pointer;display:flex;justify-content:space-between;',
500
+ 'align-items:center;gap:10px}.starters button:hover{border-color:var(--accent-soft);background:var(--accent-softer)}',
501
+ '.starters button svg{color:var(--ink-4);flex:none}.starters button:disabled{cursor:default;opacity:.6}',
502
+ '.msgs{display:flex;flex-direction:column;gap:22px}',
503
+ '.me{align-self:flex-end;max-width:78%;background:var(--accent);color:#fff;font-size:14.5px;line-height:1.5;',
504
+ 'padding:9px 14px;border-radius:18px 18px 5px 18px;white-space:pre-wrap;overflow-wrap:anywhere}',
505
+ '.bot{display:flex;gap:12px;align-items:flex-start;max-width:100%}',
506
+ '.md{flex:1;min-width:0;font-size:14.5px;line-height:1.6;color:var(--ink-1);padding-top:4px;overflow-wrap:anywhere}',
507
+ '.md p{margin:0 0 10px}.md>:last-child,.md>.acts{margin-bottom:0}',
508
+ '.md ul,.md ol{margin:0 0 10px;padding-left:20px;display:flex;flex-direction:column;gap:3px}',
509
+ '.md strong{font-weight:800}.md code{font-family:"JetBrains Mono",ui-monospace,Menlo,monospace;font-size:12.5px;',
510
+ 'background:var(--pill);border-radius:4px;padding:1px 5px}',
511
+ '.md pre{background:var(--pill);border-radius:8px;padding:10px 12px;overflow:auto;margin:0 0 10px}.md pre code{padding:0;background:none}',
512
+ '.md blockquote{margin:0 0 10px;padding-left:12px;border-left:3px solid var(--line-2);color:var(--ink-2)}',
513
+ '.md li>ul,.md li>ol{margin:3px 0 0}.md li>p{margin:0 0 6px}.md li>:last-child,.md blockquote>:last-child{margin-bottom:0}',
514
+ '.md hr{border:0;border-top:1px solid var(--line-2);margin:14px 0}.md del{color:var(--ink-3)}',
515
+ '.md .tbl{overflow-x:auto;margin:0 0 10px}.md table{border-collapse:collapse;font-size:13.5px;line-height:1.45}',
516
+ '.md th,.md td{border:1px solid var(--line-2);padding:5px 10px;text-align:left;vertical-align:top;overflow-wrap:normal}',
517
+ '.md th{background:var(--pill);font-weight:800}',
518
+ '.md.err{color:var(--ink-2)}',
519
+ '.acts{display:flex;gap:2px;margin-top:6px;opacity:0;transition:opacity .15s}.bot:hover .acts,.acts:focus-within{opacity:1}',
520
+ '.acts button{border:0;background:transparent;color:var(--ink-3);font-size:12px;font-weight:700;padding:4px 8px;',
521
+ 'border-radius:6px;cursor:pointer;display:inline-flex;gap:5px;align-items:center}.acts button:hover{background:var(--pill);color:var(--ink-1)}',
522
+ '.typing{display:flex;gap:4px;padding:12px 0}.typing i{width:7px;height:7px;border-radius:50%;background:var(--ink-4);',
523
+ 'animation:cmb 1.1s infinite}.typing i:nth-child(2){animation-delay:.15s}.typing i:nth-child(3){animation-delay:.3s}',
524
+ '@keyframes cmb{30%{transform:translateY(-4px);background:var(--accent)}}',
525
+ '.off{padding:40px 20px;text-align:center;color:var(--ink-2);font-size:14px}',
526
+ '.comp{padding:8px 18px 0}',
527
+ '.box{display:flex;align-items:flex-end;gap:8px;border:1.5px solid var(--line-2);border-radius:16px;padding:6px 6px 6px 14px;',
528
+ 'background:#fff;transition:border-color .15s}.box:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-softer)}',
529
+ '.box textarea{flex:1;border:0;outline:0;resize:none;font:inherit;font-size:15px;line-height:1.45;padding:7px 0;',
530
+ 'max-height:160px;background:transparent;color:var(--ink-1);min-width:0}.box textarea::placeholder{color:var(--ink-4)}',
531
+ '.send{width:38px;height:38px;border-radius:11px;border:0;background:var(--accent);color:#fff;display:flex;',
532
+ 'align-items:center;justify-content:center;cursor:pointer;flex:none}.send:disabled{background:var(--line-2);cursor:default}',
533
+ '.send:not(:disabled):hover{background:var(--accent-deep)}',
534
+ '.hint{font-size:11.5px;color:var(--ink-3);padding:6px 4px 0;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}',
535
+ '.hint kbd{font-family:"JetBrains Mono",ui-monospace,Menlo,monospace;font-size:11px;background:#fff;border:1px solid var(--line-2);',
536
+ 'border-bottom-width:2px;border-radius:4px;padding:1px 5px;color:var(--ink-2);line-height:1}',
537
+ '.foot{text-align:center;font-size:11.5px;color:var(--ink-3);padding:6px 0 12px}.foot a{color:var(--ink-2);font-weight:700;text-decoration:none}',
538
+ // full page
539
+ '.full{position:fixed;inset:0;display:flex;justify-content:center;background:#fff}',
540
+ '.full .chat{width:100%;max-width:780px;height:100%;border-left:1px solid var(--line-1);border-right:1px solid var(--line-1)}',
541
+ // dock
542
+ '.dock .chat{position:relative;width:340px;height:min(520px,calc(100vh - 32px));border-radius:14px 14px 0 0;',
543
+ 'box-shadow:0 12px 40px rgba(31,24,38,.18);border:1px solid var(--line-2);border-bottom:0;pointer-events:auto}',
544
+ '.dock .head{padding:9px 8px 9px 12px;gap:10px}.dock .who .n{font-size:14.5px;gap:6px}.dock .who .s{font-size:12px}',
545
+ '.dock .ib{width:30px;height:30px}.dock .scroll{padding:16px 14px 8px}.dock .intro{padding:14px 4px 4px;gap:8px}',
546
+ '.dock .intro h1{font-size:18px}.dock .intro p{font-size:13.5px}.dock .note{font-size:12.5px;padding:8px 12px}',
547
+ '.dock .starters button{font-size:13px;padding:8px 12px}.dock .msgs{gap:16px}.dock .me,.dock .md{font-size:14px}',
548
+ '.dock .me{max-width:85%}.dock .comp{padding:6px 10px 0}.dock .box textarea{font-size:14px;padding:6px 0}',
549
+ '.dock .send{width:34px;height:34px;border-radius:10px}.dock .hint{display:none}.dock .foot{padding:5px 0 7px;font-size:11px}',
550
+ '.dock .badge.slow{display:none}',
551
+ '.wins{position:fixed;right:84px;bottom:0;display:flex;flex-direction:row-reverse;align-items:flex-end;gap:12px;',
552
+ 'z-index:2147483000;pointer-events:none}',
553
+ '.heads{position:fixed;right:16px;bottom:16px;display:flex;flex-direction:column;align-items:center;gap:10px;z-index:2147483001}',
554
+ '.hd{position:relative;width:52px;height:52px;border-radius:50%;border:0;padding:0;cursor:pointer;background:none;',
555
+ 'box-shadow:0 4px 14px rgba(31,24,38,.22);transition:transform .12s}.hd:hover{transform:scale(1.05)}',
556
+ '.hd .av{width:52px;height:52px;border-radius:50%;font-size:21px}',
557
+ '.hw{position:relative;width:52px;height:52px}.hw.active .hd{outline:3px solid var(--accent-soft);outline-offset:2px}',
558
+ '.hw .dot{position:absolute;right:1px;bottom:1px;width:13px;height:13px;border-radius:50%;background:var(--online);border:2.5px solid #fff}',
559
+ '.hw .un{position:absolute;top:-3px;right:-3px;min-width:19px;height:19px;border-radius:10px;background:#dc2626;color:#fff;',
560
+ 'font-size:11px;font-weight:800;line-height:19px;text-align:center;padding:0 5px;border:2px solid #fff}',
561
+ '.hw .x{position:absolute;top:-4px;left:-4px;width:20px;height:20px;border-radius:50%;background:#fff;color:var(--ink-2);',
562
+ 'display:none;align-items:center;justify-content:center;box-shadow:0 1px 4px rgba(0,0,0,.25);border:0;padding:0;cursor:pointer}',
563
+ '.hw:hover .x,.hw:focus-within .x{display:flex}',
564
+ '.hd.more{background:#4b4453;color:#fff;font-weight:800;font-size:15px}',
565
+ '.hd.compose{background:#fff;color:var(--ink-1)}.hd.compose svg{display:block;margin:auto}',
566
+ '.hd.active{outline:3px solid var(--accent-soft);outline-offset:2px}',
567
+ '.picker{position:fixed;right:80px;bottom:16px;width:300px;max-height:min(440px,calc(100vh - 32px));background:#fff;',
568
+ 'border:1px solid var(--line-2);border-radius:14px;box-shadow:0 12px 40px rgba(31,24,38,.18);display:flex;',
569
+ 'flex-direction:column;overflow:hidden;z-index:2147483002}',
570
+ '.picker .ph{padding:12px 14px 8px;font-size:14px;font-weight:800}',
571
+ '.picker .ps{margin:0 10px 6px;display:flex;align-items:center;gap:6px;border:1px solid var(--line-2);border-radius:9px;',
572
+ 'padding:0 10px;color:var(--ink-3)}.picker .ps input{flex:1;border:0;outline:0;font:inherit;font-size:13px;padding:7px 0;min-width:0}',
573
+ '.picker .pl{overflow:auto;padding:0 6px 8px}',
574
+ '.picker .row{display:flex;gap:10px;align-items:center;width:100%;border:0;background:none;padding:8px;border-radius:10px;',
575
+ 'cursor:pointer;text-align:left;color:inherit}.picker .row:hover{background:var(--accent-softer)}',
576
+ '.picker .row .t{flex:1;min-width:0}.picker .row b{display:flex;align-items:center;gap:6px;font-size:13.5px;font-weight:800}',
577
+ '.picker .row b i{width:7px;height:7px;border-radius:50%;background:var(--online)}',
578
+ '.picker .row span{display:block;font-size:12px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
579
+ '.picker .empty{padding:12px 14px;color:var(--ink-3);font-size:13px}',
580
+ '@media (max-width:600px){',
581
+ '.scroll{padding:18px 14px 8px}.me{max-width:88%}.acts{opacity:1}.head{padding:12px 14px}.comp{padding:8px 14px 0}',
582
+ '.wins{inset:0;right:0;display:block}.dock .chat{position:fixed;inset:0;width:auto;height:auto;border-radius:0;border:0}',
583
+ '.dock.has-open .heads,.dock.has-open .picker{display:none}.picker{right:16px;bottom:84px;width:calc(100vw - 32px)}}',
584
+ '@media (hover:none){.acts{opacity:1}}',
585
+ ].join('')
586
+
587
+ // -- one chat (one key) --------------------------------------------------
588
+
589
+ function Chat(manager, key, base) {
590
+ this.m = manager
591
+ this.key = key
592
+ this.base = base
593
+ this.storeKey = 'manigent-webchat:' + key
594
+ var saved = loadJSON(this.storeKey)
595
+ this.state = saved && typeof saved === 'object' ? saved : {}
596
+ this.state.transcript = Array.isArray(this.state.transcript) ? this.state.transcript : []
597
+ this.state.after = this.state.after || 0
598
+ this.profile = this.state.profile || null
599
+ this.maxChars = 4000
600
+ this.busy = false
601
+ this.unavailable = false
602
+ this.view = null
603
+ this.source = null
604
+ this.pollTimer = null
605
+ this.pollEvery = 0
606
+ this.noStream = false
607
+ this.ready = null
608
+ this.profileLoad = null
609
+ this.pendingTimer = null
610
+ }
611
+
612
+ Chat.prototype.save = function () {
613
+ this.state.transcript = this.state.transcript.slice(-MAX_KEPT)
614
+ this.state.profile = this.profile
615
+ saveJSON(this.storeKey, this.state)
616
+ }
617
+
618
+ Chat.prototype.call = function (method, path, body) {
619
+ var init = { method: method, credentials: 'omit', mode: 'cors' }
620
+ if (body) {
621
+ init.headers = { 'Content-Type': 'text/plain;charset=UTF-8' }
622
+ init.body = JSON.stringify(body)
623
+ }
624
+ return fetch(this.base + path, init).then(function (r) {
625
+ return r.json().catch(function () { return {} }).then(function (data) {
626
+ if (!r.ok) {
627
+ var err = new Error(data.error || 'http_' + r.status)
628
+ err.status = r.status
629
+ throw err
630
+ }
631
+ return data
632
+ })
633
+ })
634
+ }
635
+
636
+ Chat.prototype.qs = function () {
637
+ return '?key=' + encodeURIComponent(this.key) + '&visitor=' + encodeURIComponent(this.state.visitor) +
638
+ '&token=' + encodeURIComponent(this.state.token) + '&after=' + this.state.after
639
+ }
640
+
641
+ Chat.prototype.name = function () {
642
+ return (this.profile && this.profile.display_name) || 'Chat'
643
+ }
644
+
645
+ Chat.prototype.isOpen = function () {
646
+ return this.m.mode === 'fullpage' || this.m.state[this.key] === 'open'
647
+ }
648
+
649
+ Chat.prototype.setProfile = function (profile, maxChars) {
650
+ if (profile) this.profile = profile
651
+ if (maxChars) this.maxChars = maxChars
652
+ this.save()
653
+ if (this.view) this.renderHeader()
654
+ if (this.view) this.renderBody()
655
+ this.m.render()
656
+ }
657
+
658
+ Chat.prototype.loadProfile = function () {
659
+ var self = this
660
+ if (this.profileLoad) return this.profileLoad
661
+ this.profileLoad = this.call('GET', '/api/channels/webchat/profile?key=' + encodeURIComponent(this.key))
662
+ .then(function (data) {
663
+ self.unavailable = false
664
+ self.setProfile(data.profile, data.max_chars)
665
+ })
666
+ .catch(function (err) {
667
+ self.profileLoad = null
668
+ if (err.status === 403 || err.status === 404) {
669
+ self.unavailable = true
670
+ self.m.render()
671
+ if (self.view) self.renderBody()
672
+ }
673
+ })
674
+ return this.profileLoad
675
+ }
676
+
677
+ Chat.prototype.start = function () {
678
+ var self = this
679
+ if (this.ready) return this.ready
680
+ this.ready = this.call('POST', '/api/channels/webchat/session', {
681
+ key: this.key, visitor: this.state.visitor || '', token: this.state.token || '',
682
+ }).then(function (data) {
683
+ // A stored visitor the server replaced: that old thread is gone. (No
684
+ // stored visitor: keep the message being sent.)
685
+ if (self.state.visitor && data.visitor !== self.state.visitor) {
686
+ self.state.after = 0
687
+ self.state.transcript = []
688
+ }
689
+ self.state.visitor = data.visitor
690
+ self.state.token = data.token
691
+ self.unavailable = false
692
+ self.setProfile(data.profile, data.max_chars)
693
+ self.sync()
694
+ }).catch(function (err) {
695
+ self.ready = null
696
+ if (err.status === 403 || err.status === 404) {
697
+ self.unavailable = true
698
+ if (self.view) self.renderBody()
699
+ self.m.render()
700
+ }
701
+ throw err
702
+ })
703
+ return this.ready
704
+ }
705
+
706
+ // An open window (or a reply on its way) streams; a minimised chat with a
707
+ // history polls slowly, so its head can show unread replies without holding
708
+ // a connection per agent.
709
+ Chat.prototype.sync = function () {
710
+ if (!this.state.visitor || this.unavailable) return this.stop()
711
+ var live = this.isOpen() || this.busy
712
+ if (live && !this.noStream && window.EventSource) {
713
+ this.stopPoll()
714
+ if (!this.source) this.stream()
715
+ } else {
716
+ this.closeStream()
717
+ this.poll(live ? 5000 : 20000)
718
+ }
719
+ }
720
+
721
+ Chat.prototype.stop = function () {
722
+ this.closeStream()
723
+ this.stopPoll()
724
+ }
725
+
726
+ Chat.prototype.closeStream = function () {
727
+ if (this.source) {
728
+ this.source.close()
729
+ this.source = null
730
+ }
731
+ }
732
+
733
+ Chat.prototype.stopPoll = function () {
734
+ clearInterval(this.pollTimer)
735
+ this.pollTimer = null
736
+ this.pollEvery = 0
737
+ }
738
+
739
+ Chat.prototype.stream = function () {
740
+ var self = this
741
+ var failures = 0
742
+ var source = new EventSource(this.base + '/api/channels/webchat/stream' + this.qs())
743
+ this.source = source
744
+ source.onmessage = function (event) {
745
+ failures = 0
746
+ try { self.receive(JSON.parse(event.data)) } catch (e) { /* a bad frame */ }
747
+ }
748
+ source.onerror = function () {
749
+ failures += 1
750
+ // The browser retries by itself; after repeated failures (a proxy that
751
+ // buffers event streams, say) fall back to polling.
752
+ if (source.readyState === 2 || failures >= 3) {
753
+ self.noStream = true
754
+ self.closeStream()
755
+ self.sync()
756
+ }
757
+ }
758
+ }
759
+
760
+ Chat.prototype.poll = function (every) {
761
+ var self = this
762
+ if (this.pollTimer && this.pollEvery === every) return
763
+ this.stopPoll()
764
+ var tick = function () {
765
+ self.call('GET', '/api/channels/webchat/messages' + self.qs()).then(function (data) {
766
+ (data.messages || []).forEach(function (m) { self.receive(m) })
767
+ }).catch(function () { /* next tick */ })
768
+ }
769
+ tick()
770
+ this.pollTimer = setInterval(tick, every)
771
+ this.pollEvery = every
772
+ }
773
+
774
+ Chat.prototype.receive = function (message) {
775
+ if (!message || message.seq <= this.state.after) return
776
+ this.state.after = message.seq
777
+ this.state.transcript.push({ from: 'agent', text: message.text, at: message.at })
778
+ if (!this.isOpen()) this.state.unread = (this.state.unread || 0) + 1
779
+ this.settle()
780
+ this.save()
781
+ if (this.view) this.renderBody()
782
+ this.m.render()
783
+ }
784
+
785
+ Chat.prototype.settle = function () {
786
+ clearTimeout(this.pendingTimer)
787
+ if (!this.busy) return
788
+ this.busy = false
789
+ this.sync()
790
+ if (this.view) this.renderComposer()
791
+ }
792
+
793
+ Chat.prototype.send = function (raw) {
794
+ var self = this
795
+ var text = (raw || '').trim()
796
+ if (!text || this.busy) return
797
+ if (text.length > this.maxChars) text = text.slice(0, this.maxChars)
798
+ this.state.transcript.push({ from: 'me', text: text, at: Date.now() / 1000 })
799
+ this.busy = true
800
+ this.save()
801
+ this.renderBody()
802
+ this.renderComposer()
803
+ var id = newId()
804
+ this.start().then(function () {
805
+ return self.call('POST', '/api/channels/webchat/messages', {
806
+ key: self.key, visitor: self.state.visitor, token: self.state.token, id: id, text: text,
807
+ })
808
+ }).then(function () {
809
+ self.sync()
810
+ clearTimeout(self.pendingTimer)
811
+ self.pendingTimer = setTimeout(function () { self.settle() }, PENDING_MS)
812
+ }).catch(function () {
813
+ self.state.transcript.push({ from: 'error', text: SEND_ERROR })
814
+ self.save()
815
+ self.settle()
816
+ self.renderBody()
817
+ })
818
+ }
819
+
820
+ Chat.prototype.reset = function () {
821
+ this.stop()
822
+ this.ready = null
823
+ clearTimeout(this.pendingTimer)
824
+ this.busy = false
825
+ this.state = { transcript: [], after: 0 }
826
+ this.save()
827
+ if (this.view) {
828
+ this.renderBody()
829
+ this.renderComposer()
830
+ this.view.ta.focus()
831
+ }
832
+ this.m.render()
833
+ }
834
+
835
+ // -- one chat's view -----------------------------------------------------
836
+
837
+ Chat.prototype.buildView = function () {
838
+ var self = this
839
+ var docked = this.m.mode !== 'fullpage'
840
+ var ta = h('textarea', { rows: 1 })
841
+ var sendBtn = h('button', { type: 'button', class: 'send', 'aria-label': 'Send', disabled: true }, [icon('up', 18, 2.2)])
842
+ var grow = function () {
843
+ ta.style.height = 'auto'
844
+ ta.style.height = ta.scrollHeight + 'px'
845
+ self.renderComposer()
846
+ }
847
+ ta.addEventListener('input', grow)
848
+ ta.addEventListener('keydown', function (e) {
849
+ if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
850
+ e.preventDefault()
851
+ self.submit()
852
+ }
853
+ })
854
+ sendBtn.addEventListener('click', function () { self.submit() })
855
+ var buttons = [
856
+ h('button', { type: 'button', class: 'ib', title: 'New conversation', 'aria-label': 'New conversation',
857
+ onclick: function () { self.reset() } }, [icon('reset', 17)]),
858
+ ]
859
+ if (docked) {
860
+ buttons.push(
861
+ h('button', { type: 'button', class: 'ib', title: 'Minimize', 'aria-label': 'Minimize chat',
862
+ onclick: function () { self.m.minimize(self.key) } }, [icon('min', 17)]),
863
+ h('button', { type: 'button', class: 'ib', title: 'Close', 'aria-label': 'Close chat',
864
+ onclick: function () { self.m.close(self.key) } }, [icon('close', 17)])
865
+ )
866
+ }
867
+ var header = h('header', { class: 'head' })
868
+ var scroll = h('div', { class: 'scroll' })
869
+ var root = h('section', { class: 'chat', role: docked ? 'dialog' : 'main', 'data-key': this.key }, [
870
+ header, scroll,
871
+ h('div', { class: 'comp' }, [
872
+ h('div', { class: 'box' }, [ta, sendBtn]),
873
+ h('div', { class: 'hint' }, [
874
+ h('span', { text: 'AI agent \u2014 replies can be wrong. Don\u2019t share passwords or payment details.' }),
875
+ h('span', null, [h('kbd', { text: 'Enter' }), ' to send']),
876
+ ]),
877
+ ]),
878
+ h('div', { class: 'foot' }, ['Powered by ', h('a', { href: this.base + '/', target: '_blank', rel: 'noopener', text: 'Manigent' })]),
879
+ ])
880
+ this.view = { root: root, header: header, buttons: buttons, scroll: scroll, ta: ta, sendBtn: sendBtn }
881
+ this.renderHeader()
882
+ this.renderBody()
883
+ return root
884
+ }
885
+
886
+ Chat.prototype.submit = function (text) {
887
+ var value = text === undefined ? this.view.ta.value : text
888
+ if (this.busy || !value.trim()) return
889
+ if (text === undefined) {
890
+ this.view.ta.value = ''
891
+ this.view.ta.style.height = 'auto'
892
+ }
893
+ this.send(value)
894
+ }
895
+
896
+ Chat.prototype.renderHeader = function () {
897
+ var v = this.view
898
+ var p = this.profile || {}
899
+ var name = this.name()
900
+ var docked = this.m.mode !== 'fullpage'
901
+ var badge = p.online
902
+ ? h('span', { class: 'badge go' }, [h('i'), 'Online'])
903
+ : (p.display_name ? h('span', { class: 'badge slow' }, [h('i'), 'Offline \u2014 replies may be slow']) : null)
904
+ var sub = []
905
+ if (p.account) {
906
+ var acct = p.account.url
907
+ ? h('a', { href: p.account.url, target: docked ? '_blank' : null, rel: docked ? 'noopener' : null, text: p.account.display_name })
908
+ : document.createTextNode(p.account.display_name)
909
+ sub.push('by ', acct, ' \u00b7 ', h('span', { class: 'mono', text: p.handle || '' }))
910
+ }
911
+ v.header.textContent = ''
912
+ v.header.appendChild(avatar(name, p.handle || this.key, docked ? 'sm' : ''))
913
+ v.header.appendChild(h('div', { class: 'who' }, [h('div', { class: 'n' }, [name, badge]), h('div', { class: 's' }, sub)]))
914
+ v.buttons.forEach(function (b) { v.header.appendChild(b) })
915
+ v.ta.placeholder = 'Message ' + name
916
+ v.ta.setAttribute('aria-label', 'Message ' + name)
917
+ if (!docked && p.display_name && p.account) document.title = name + ' \u00b7 ' + p.account.display_name
918
+ }
919
+
920
+ Chat.prototype.renderIntro = function () {
921
+ var self = this
922
+ var p = this.profile || {}
923
+ var name = this.name()
924
+ var docked = this.m.mode !== 'fullpage'
925
+ var starters = (p.starter_questions && p.starter_questions.length) ? p.starter_questions : GENERIC_STARTERS
926
+ if (docked) starters = starters.slice(0, 3)
927
+ var note = null
928
+ if (p.display_name && p.takes_direct_requests === false) {
929
+ var t = p.request_taker
930
+ note = h('div', { class: 'note' }, [h('b', { text: 'Answers questions only.' })].concat(t ? [
931
+ ' To start a request, talk to the ',
932
+ h('a', { href: t.chat_url, target: docked ? '_blank' : null, rel: docked ? 'noopener' : null, text: t.display_name }),
933
+ '.',
934
+ ] : []))
935
+ }
936
+ return h('div', { class: 'intro' }, [
937
+ avatar(name, p.handle || this.key, docked ? 'lg' : 'xl'),
938
+ h('h1', { text: name }),
939
+ p.description ? h('p', { text: p.description }) : null,
940
+ note,
941
+ h('div', { class: 'starters' }, starters.map(function (s) {
942
+ return h('button', { type: 'button', disabled: self.busy, onclick: function () { self.submit(s) } }, [s, icon('arrow')])
943
+ })),
944
+ ])
945
+ }
946
+
947
+ Chat.prototype.renderEntry = function (entry, name, seed) {
948
+ if (entry.from === 'me') return h('div', { class: 'me', text: entry.text })
949
+ var body = h('div', { class: 'md' + (entry.from === 'error' ? ' err' : '') })
950
+ body.appendChild(markdown(entry.text))
951
+ if (entry.from === 'agent') {
952
+ var label = document.createTextNode('Copy')
953
+ body.appendChild(h('div', { class: 'acts' }, [
954
+ h('button', { type: 'button', onclick: function () {
955
+ if (navigator.clipboard) navigator.clipboard.writeText(entry.text).then(function () { label.textContent = 'Copied' }, function () {})
956
+ } }, [icon('copy', 13), label]),
957
+ ]))
958
+ }
959
+ return h('div', { class: 'bot' }, [avatar(name, seed, 'sm'), body])
960
+ }
961
+
962
+ Chat.prototype.renderBody = function () {
963
+ var self = this
964
+ var v = this.view
965
+ if (!v) return
966
+ v.scroll.textContent = ''
967
+ if (this.unavailable) {
968
+ v.scroll.appendChild(h('div', { class: 'off', text: 'This chat is not available right now.' }))
969
+ this.renderComposer()
970
+ return
971
+ }
972
+ var name = this.name()
973
+ var seed = (this.profile && this.profile.handle) || this.key
974
+ if (!this.state.transcript.length) v.scroll.appendChild(this.renderIntro())
975
+ var msgs = h('div', { class: 'msgs', 'aria-live': 'polite' })
976
+ this.state.transcript.forEach(function (entry) { msgs.appendChild(self.renderEntry(entry, name, seed)) })
977
+ if (this.busy) {
978
+ msgs.appendChild(h('div', { class: 'bot' }, [
979
+ avatar(name, seed, 'sm'),
980
+ h('div', { class: 'typing', role: 'status', 'aria-label': name + ' is typing' }, [h('i'), h('i'), h('i')]),
981
+ ]))
982
+ }
983
+ v.scroll.appendChild(msgs)
984
+ v.scroll.scrollTop = v.scroll.scrollHeight
985
+ }
986
+
987
+ Chat.prototype.renderComposer = function () {
988
+ var v = this.view
989
+ if (!v) return
990
+ v.sendBtn.disabled = this.busy || this.unavailable || !v.ta.value.trim()
991
+ v.ta.disabled = this.unavailable
992
+ }
993
+
994
+ // -- the page's one manager ----------------------------------------------
995
+
996
+ function Manager(base) {
997
+ this.base = base
998
+ this.mode = null
999
+ this.chats = {}
1000
+ this.order = [] // keys, in the order they were added (the picker)
1001
+ this.state = {} // key -> 'listed' | 'min' | 'open'
1002
+ this.openOrder = [] // open windows, oldest first
1003
+ this.headOrder = [] // heads, most recent first
1004
+ this.color = null
1005
+ this.pickerOpen = false
1006
+ this.query = ''
1007
+ this.dockKey = 'manigent-webchat-dock:' + location.pathname
1008
+ this.saved = loadJSON(this.dockKey) || {}
1009
+ this.rootEl = null
1010
+ }
1011
+
1012
+ Manager.prototype.narrow = function () {
1013
+ return window.innerWidth <= 600
1014
+ }
1015
+
1016
+ Manager.prototype.limit = function () {
1017
+ if (this.narrow()) return 1
1018
+ return Math.max(1, Math.min(3, Math.floor((window.innerWidth - 100) / 352)))
1019
+ }
1020
+
1021
+ Manager.prototype.fromScript = function (el) {
1022
+ if (!el) return
1023
+ var base = el.src ? new URL(el.src, location.href).origin : this.base
1024
+ var keys = [el.getAttribute('data-key')].concat((el.getAttribute('data-keys') || '').split(','))
1025
+ .map(function (k) { return (k || '').trim() })
1026
+ .filter(Boolean)
1027
+ if (el.getAttribute('data-color')) this.color = el.getAttribute('data-color')
1028
+ if (el.getAttribute('data-mode') === 'fullpage') {
1029
+ if (keys[0]) this.mountFullpage(keys[0], base)
1030
+ return
1031
+ }
1032
+ if (!this.mode) this.mode = 'dock'
1033
+ var self = this
1034
+ keys.forEach(function (k) { self.add(k, { head: true, base: base }) })
1035
+ if (el.hasAttribute('data-open') && keys[0]) this.open(keys[0])
1036
+ this.render()
1037
+ }
1038
+
1039
+ Manager.prototype.chat = function (key, base) {
1040
+ if (!this.chats[key]) {
1041
+ this.chats[key] = new Chat(this, key, base || this.base)
1042
+ this.order.push(key)
1043
+ }
1044
+ return this.chats[key]
1045
+ }
1046
+
1047
+ Manager.prototype.mountFullpage = function (key, base) {
1048
+ this.mode = 'fullpage'
1049
+ var chat = this.chat(key, base)
1050
+ this.ensureRoot()
1051
+ this.rootEl.className = 'root full' + (this.color ? ' custom' : '')
1052
+ this.rootEl.textContent = ''
1053
+ this.rootEl.appendChild(chat.buildView())
1054
+ chat.loadProfile()
1055
+ chat.start().catch(function () {})
1056
+ setTimeout(function () { chat.view.ta.focus() }, 0)
1057
+ }
1058
+
1059
+ Manager.prototype.add = function (key, opts) {
1060
+ if (!key || this.mode === 'fullpage') return
1061
+ if (!this.mode) this.mode = 'dock'
1062
+ opts = opts || {}
1063
+ var chat = this.chat(key, opts.base)
1064
+ if (!(key in this.state)) {
1065
+ var saved = this.saved[key]
1066
+ this.state[key] = 'listed'
1067
+ if (saved === 'open' && !this.narrow()) this.setOpen(key)
1068
+ else if (saved === 'open' || saved === 'min' || opts.head) this.setHead(key, 'min')
1069
+ } else if (opts.head && this.state[key] === 'listed') {
1070
+ this.setHead(key, 'min')
1071
+ }
1072
+ chat.loadProfile()
1073
+ if (chat.state.visitor) chat.start().catch(function () {})
1074
+ this.render()
1075
+ }
1076
+
1077
+ Manager.prototype.setHead = function (key, state) {
1078
+ this.state[key] = state
1079
+ this.headOrder = [key].concat(this.headOrder.filter(function (k) { return k !== key }))
1080
+ }
1081
+
1082
+ Manager.prototype.setOpen = function (key) {
1083
+ this.setHead(key, 'open')
1084
+ this.openOrder = this.openOrder.filter(function (k) { return k !== key }).concat([key])
1085
+ while (this.openOrder.length > this.limit()) {
1086
+ var oldest = this.openOrder.shift()
1087
+ this.state[oldest] = 'min'
1088
+ this.chats[oldest].sync()
1089
+ }
1090
+ }
1091
+
1092
+ Manager.prototype.open = function (key) {
1093
+ if (!key) return
1094
+ if (this.mode === 'fullpage') return
1095
+ if (!(key in this.state)) this.add(key)
1096
+ var chat = this.chats[key]
1097
+ this.setOpen(key)
1098
+ chat.state.unread = 0
1099
+ chat.save()
1100
+ this.pickerOpen = false
1101
+ this.persist()
1102
+ this.render()
1103
+ if (chat.state.visitor) chat.sync()
1104
+ if (chat.view) setTimeout(function () { chat.view.ta.focus() }, 0)
1105
+ }
1106
+
1107
+ Manager.prototype.minimize = function (key) {
1108
+ if (this.state[key] !== 'open') return
1109
+ this.state[key] = 'min'
1110
+ this.openOrder = this.openOrder.filter(function (k) { return k !== key })
1111
+ this.chats[key].sync()
1112
+ this.persist()
1113
+ this.render()
1114
+ }
1115
+
1116
+ Manager.prototype.close = function (key) {
1117
+ if (!(key in this.state)) return
1118
+ this.state[key] = 'listed'
1119
+ this.openOrder = this.openOrder.filter(function (k) { return k !== key })
1120
+ this.headOrder = this.headOrder.filter(function (k) { return k !== key })
1121
+ this.chats[key].sync()
1122
+ this.persist()
1123
+ this.render()
1124
+ }
1125
+
1126
+ Manager.prototype.persist = function () {
1127
+ var out = {}
1128
+ var self = this
1129
+ Object.keys(this.state).forEach(function (k) {
1130
+ if (self.state[k] !== 'listed') out[k] = self.state[k]
1131
+ })
1132
+ this.saved = out
1133
+ saveJSON(this.dockKey, out)
1134
+ }
1135
+
1136
+ Manager.prototype.list = function () {
1137
+ var self = this
1138
+ return this.order.map(function (k) {
1139
+ return { key: k, state: self.state[k] || 'listed', name: self.chats[k].name() }
1140
+ })
1141
+ }
1142
+
1143
+ Manager.prototype.ensureRoot = function () {
1144
+ if (this.rootEl) return
1145
+ var host = document.createElement('div')
1146
+ host.setAttribute('data-manigent-webchat', '')
1147
+ var testing = script && script.hasAttribute('data-test-open-shadow')
1148
+ var shadow = host.attachShadow({ mode: testing ? 'open' : 'closed' })
1149
+ shadow.appendChild(h('style', { text: CSS }))
1150
+ this.rootEl = h('div', { class: 'root' })
1151
+ if (this.color) this.rootEl.style.setProperty('--accent', this.color)
1152
+ shadow.appendChild(this.rootEl)
1153
+ var self = this
1154
+ var mount = function () { document.body.appendChild(host) }
1155
+ if (document.body) mount()
1156
+ else document.addEventListener('DOMContentLoaded', mount)
1157
+ window.addEventListener('resize', function () {
1158
+ if (self.mode !== 'dock') return
1159
+ while (self.openOrder.length > self.limit()) self.minimize(self.openOrder[0])
1160
+ self.render()
1161
+ })
1162
+ // A click anywhere outside the widget closes the picker (clicks inside the
1163
+ // shadow root reach the document retargeted to the host).
1164
+ document.addEventListener('click', function (e) {
1165
+ if (self.pickerOpen && e.target !== host) {
1166
+ self.pickerOpen = false
1167
+ self.render()
1168
+ }
1169
+ })
1170
+ document.addEventListener('keydown', function (e) {
1171
+ if (e.key === 'Escape' && self.pickerOpen) {
1172
+ self.pickerOpen = false
1173
+ self.render()
1174
+ }
1175
+ })
1176
+ this.shadow = shadow
1177
+ }
1178
+
1179
+ Manager.prototype.render = function () {
1180
+ if (this.mode !== 'dock') return
1181
+ this.ensureRoot()
1182
+ var self = this
1183
+ var root = this.rootEl
1184
+ if (this.color) root.style.setProperty('--accent', this.color)
1185
+ root.className = 'root dock' + (this.color ? ' custom' : '') + (this.openOrder.length ? ' has-open' : '')
1186
+
1187
+ // windows: newest at the right, next to the heads
1188
+ var wins = this.winsEl || (this.winsEl = h('div', { class: 'wins' }))
1189
+ var wanted = this.openOrder.slice().reverse().map(function (k) {
1190
+ var chat = self.chats[k]
1191
+ if (!chat.view) chat.buildView()
1192
+ return chat.view.root
1193
+ })
1194
+ Array.prototype.slice.call(wins.children).forEach(function (el) {
1195
+ if (wanted.indexOf(el) < 0) wins.removeChild(el)
1196
+ })
1197
+ wanted.forEach(function (el, i) {
1198
+ if (wins.children[i] !== el) wins.insertBefore(el, wins.children[i] || null)
1199
+ })
1200
+
1201
+ // heads: the compose button at the bottom, then the most recent upwards
1202
+ var heads = h('div', { class: 'heads', role: 'toolbar', 'aria-label': 'Chats' })
1203
+ var visible = this.headOrder.filter(function (k) { return !self.chats[k].unavailable })
1204
+ var maxHeads = this.narrow() ? 4 : 6
1205
+ var overflow = visible.length > maxHeads ? visible.slice(maxHeads - 1) : []
1206
+ var shown = overflow.length ? visible.slice(0, maxHeads - 1) : visible
1207
+ var available = this.order.filter(function (k) { return !self.chats[k].unavailable })
1208
+ var showCompose = available.length > 1 || available.length > visible.length
1209
+ var col = []
1210
+ if (overflow.length) {
1211
+ col.push(h('button', { type: 'button', class: 'hd more', title: overflow.length + ' more chats',
1212
+ 'aria-label': overflow.length + ' more chats', onclick: function () { self.togglePicker() } }, ['+' + overflow.length]))
1213
+ }
1214
+ shown.slice().reverse().forEach(function (k) { col.push(self.headButton(k)) })
1215
+ if (showCompose) {
1216
+ col.push(h('button', { type: 'button', class: 'hd compose' + (this.pickerOpen ? ' active' : ''), title: 'New message',
1217
+ 'aria-label': 'Start a chat', 'aria-expanded': this.pickerOpen ? 'true' : 'false',
1218
+ onclick: function () { self.togglePicker() } }, [icon('compose', 20)]))
1219
+ }
1220
+ col.forEach(function (el) { heads.appendChild(el) })
1221
+
1222
+ root.textContent = ''
1223
+ root.appendChild(wins)
1224
+ if (col.length) root.appendChild(heads)
1225
+ if (this.pickerOpen) root.appendChild(this.pickerEl(available))
1226
+ }
1227
+
1228
+ Manager.prototype.headButton = function (key) {
1229
+ var self = this
1230
+ var chat = this.chats[key]
1231
+ var p = chat.profile || {}
1232
+ var name = chat.name()
1233
+ var unread = chat.state.unread || 0
1234
+ var open = this.state[key] === 'open'
1235
+ var label = name + (unread ? ', ' + unread + ' unread' : '') + (open ? ' (open)' : '')
1236
+ return h('div', { class: 'hw' + (open ? ' active' : ''), title: name }, [
1237
+ h('button', { type: 'button', class: 'hd', 'aria-label': label,
1238
+ onclick: function () { if (open) self.minimize(key); else self.open(key) } }, [avatar(name, p.handle || key)]),
1239
+ p.online ? h('span', { class: 'dot', 'aria-hidden': 'true' }) : null,
1240
+ unread ? h('span', { class: 'un', 'aria-hidden': 'true', text: unread > 9 ? '9+' : String(unread) }) : null,
1241
+ h('button', { type: 'button', class: 'x', 'aria-label': 'Close chat with ' + name, title: 'Close',
1242
+ onclick: function (e) { e.stopPropagation(); self.close(key) } }, [icon('close', 11, 3)]),
1243
+ ])
1244
+ }
1245
+
1246
+ Manager.prototype.togglePicker = function () {
1247
+ this.pickerOpen = !this.pickerOpen
1248
+ this.query = ''
1249
+ this.render()
1250
+ if (this.pickerOpen && this.searchEl) this.searchEl.focus()
1251
+ }
1252
+
1253
+ Manager.prototype.pickerEl = function (keys) {
1254
+ var self = this
1255
+ var list = h('div', { class: 'pl', role: 'list' })
1256
+ var draw = function () {
1257
+ var q = self.query.trim().toLowerCase()
1258
+ list.textContent = ''
1259
+ var hits = keys.filter(function (k) {
1260
+ var p = self.chats[k].profile || {}
1261
+ return !q || [p.display_name, p.handle, p.description].join(' ').toLowerCase().indexOf(q) >= 0
1262
+ })
1263
+ if (!hits.length) list.appendChild(h('div', { class: 'empty', text: 'No agents match.' }))
1264
+ hits.forEach(function (k) {
1265
+ var chat = self.chats[k]
1266
+ var p = chat.profile || {}
1267
+ list.appendChild(h('button', { type: 'button', class: 'row', role: 'listitem', onclick: function () { self.open(k) } }, [
1268
+ avatar(chat.name(), p.handle || k),
1269
+ h('div', { class: 't' }, [
1270
+ h('b', null, [chat.name(), p.online ? h('i', { 'aria-label': 'online' }) : null]),
1271
+ h('span', { text: p.description || (p.account ? 'by ' + p.account.display_name : '') }),
1272
+ ]),
1273
+ ]))
1274
+ })
1275
+ }
1276
+ var children = [h('div', { class: 'ph', text: 'Chat with an agent' })]
1277
+ if (keys.length > 6) {
1278
+ this.searchEl = h('input', { placeholder: 'Search agents', 'aria-label': 'Search agents', value: this.query,
1279
+ oninput: function (e) { self.query = e.target.value; draw() } })
1280
+ children.push(h('label', { class: 'ps' }, [icon('search', 14), this.searchEl]))
1281
+ } else {
1282
+ this.searchEl = null
1283
+ }
1284
+ children.push(list)
1285
+ draw()
1286
+ return h('div', { class: 'picker', role: 'dialog', 'aria-label': 'Start a chat' }, children)
1287
+ }
1288
+
1289
+ // -- boot ----------------------------------------------------------------
1290
+
1291
+ var base = script && script.src ? new URL(script.src, location.href).origin : location.origin
1292
+ var manager = new Manager(base)
1293
+ var run = function (cmd) {
1294
+ if (!Array.isArray(cmd) || typeof api[cmd[0]] !== 'function' || cmd[0] === 'push') return
1295
+ api[cmd[0]].apply(null, cmd.slice(1))
1296
+ }
1297
+ var api = {
1298
+ add: function (key) { manager.add(key) },
1299
+ open: function (key) { manager.open(key) },
1300
+ minimize: function (key) { manager.minimize(key) },
1301
+ close: function (key) { manager.close(key) },
1302
+ list: function () { return manager.list() },
1303
+ push: function (cmd) { run(cmd) },
1304
+ __manager: manager,
1305
+ __markdown: markdown,
1306
+ }
1307
+ window.ManigentChat = api
1308
+ manager.fromScript(script)
1309
+ queued.forEach(run)
1310
+ })()