agent-relay-server 0.125.0 → 0.127.0

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 (156) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +9 -5
  3. package/public/assets/{MessageBubble-BYeYFteG.js → MessageBubble-D2cnxyTy.js} +2 -2
  4. package/public/assets/{MessageBubble-BYeYFteG.js.map → MessageBubble-D2cnxyTy.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-iAXvkZKW.js → PlanGraphPanel-Cnxt-x7R.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-iAXvkZKW.js.map → PlanGraphPanel-Cnxt-x7R.js.map} +1 -1
  7. package/public/assets/{activity-Du9SRJt5.js → activity-BEm1BQB1.js} +2 -2
  8. package/public/assets/{activity-Du9SRJt5.js.map → activity-BEm1BQB1.js.map} +1 -1
  9. package/public/assets/{agent-profiles-DXDQnrlH.js → agent-profiles-C8V_oCiM.js} +2 -2
  10. package/public/assets/{agent-profiles-DXDQnrlH.js.map → agent-profiles-C8V_oCiM.js.map} +1 -1
  11. package/public/assets/{agent-type-icon-B3i0rndK.js → agent-type-icon-BmBR-9CH.js} +2 -2
  12. package/public/assets/{agent-type-icon-B3i0rndK.js.map → agent-type-icon-BmBR-9CH.js.map} +1 -1
  13. package/public/assets/{agents-CWOdKrxP.js → agents-D8mc9X1j.js} +2 -2
  14. package/public/assets/{agents-CWOdKrxP.js.map → agents-D8mc9X1j.js.map} +1 -1
  15. package/public/assets/{analytics-YcY59Hv9.js → analytics-BZtRycyL.js} +2 -2
  16. package/public/assets/{analytics-YcY59Hv9.js.map → analytics-BZtRycyL.js.map} +1 -1
  17. package/public/assets/{automation-CItTse7F.js → automation-BEhJZkNQ.js} +2 -2
  18. package/public/assets/{automation-CItTse7F.js.map → automation-BEhJZkNQ.js.map} +1 -1
  19. package/public/assets/{badge-XEru6wto.js → badge-BCvp7LK3.js} +2 -2
  20. package/public/assets/{badge-XEru6wto.js.map → badge-BCvp7LK3.js.map} +1 -1
  21. package/public/assets/{branch-state-badge-QjTktTYj.js → branch-state-badge-Cscjqf-E.js} +2 -2
  22. package/public/assets/{branch-state-badge-QjTktTYj.js.map → branch-state-badge-Cscjqf-E.js.map} +1 -1
  23. package/public/assets/{button-C3CQSr4p.js → button-DyHjsHY3.js} +2 -2
  24. package/public/assets/{button-C3CQSr4p.js.map → button-DyHjsHY3.js.map} +1 -1
  25. package/public/assets/{card-C5XTIPvq.js → card-BoqYxpVW.js} +2 -2
  26. package/public/assets/{card-C5XTIPvq.js.map → card-BoqYxpVW.js.map} +1 -1
  27. package/public/assets/{channels-BSFTsbhT.js → channels-DAtOnA6o.js} +2 -2
  28. package/public/assets/{channels-BSFTsbhT.js.map → channels-DAtOnA6o.js.map} +1 -1
  29. package/public/assets/chat-BAH9bEPx.js +2 -0
  30. package/public/assets/chat-BAH9bEPx.js.map +1 -0
  31. package/public/assets/{connectors-C7d5dlYz.js → connectors-B2grC-hl.js} +2 -2
  32. package/public/assets/{connectors-C7d5dlYz.js.map → connectors-B2grC-hl.js.map} +1 -1
  33. package/public/assets/{copy-button-Bil2CjzT.js → copy-button-C67FUG3E.js} +2 -2
  34. package/public/assets/{copy-button-Bil2CjzT.js.map → copy-button-C67FUG3E.js.map} +1 -1
  35. package/public/assets/{dist-V3wu3BHw.js → dist-BD4Xc13v.js} +2 -2
  36. package/public/assets/{dist-V3wu3BHw.js.map → dist-BD4Xc13v.js.map} +1 -1
  37. package/public/assets/{dist-C5z05Vkz.js → dist-BFDfbfNC.js} +2 -2
  38. package/public/assets/{dist-C5z05Vkz.js.map → dist-BFDfbfNC.js.map} +1 -1
  39. package/public/assets/{dist-CjyfQThq.js → dist-BJKxqIQd.js} +2 -2
  40. package/public/assets/{dist-CjyfQThq.js.map → dist-BJKxqIQd.js.map} +1 -1
  41. package/public/assets/{dist-B8tD9ZcR.js → dist-BNB3fTO-.js} +2 -2
  42. package/public/assets/{dist-B8tD9ZcR.js.map → dist-BNB3fTO-.js.map} +1 -1
  43. package/public/assets/{dist-CFtFIhu3.js → dist-CHKra2JV.js} +2 -2
  44. package/public/assets/{dist-CFtFIhu3.js.map → dist-CHKra2JV.js.map} +1 -1
  45. package/public/assets/{dist-r8wyUTkK.js → dist-CQMQ5iku.js} +2 -2
  46. package/public/assets/{dist-r8wyUTkK.js.map → dist-CQMQ5iku.js.map} +1 -1
  47. package/public/assets/{dist-DKmTXfpP.js → dist-C_VOBSaK.js} +2 -2
  48. package/public/assets/{dist-DKmTXfpP.js.map → dist-C_VOBSaK.js.map} +1 -1
  49. package/public/assets/{dist-CGaHpxnN.js → dist-D5w04z0l.js} +2 -2
  50. package/public/assets/{dist-CGaHpxnN.js.map → dist-D5w04z0l.js.map} +1 -1
  51. package/public/assets/{dist-4VwhnLtj.js → dist-DG75bvPp.js} +2 -2
  52. package/public/assets/{dist-4VwhnLtj.js.map → dist-DG75bvPp.js.map} +1 -1
  53. package/public/assets/{dist-CrUl2aQp.js → dist-G8YBD-wp.js} +2 -2
  54. package/public/assets/{dist-CrUl2aQp.js.map → dist-G8YBD-wp.js.map} +1 -1
  55. package/public/assets/{dist-BJaV2BD4.js → dist-f6MW74eX.js} +2 -2
  56. package/public/assets/{dist-BJaV2BD4.js.map → dist-f6MW74eX.js.map} +1 -1
  57. package/public/assets/{es2015-5DTJe35n.js → es2015-a6fysEVz.js} +2 -2
  58. package/public/assets/{es2015-5DTJe35n.js.map → es2015-a6fysEVz.js.map} +1 -1
  59. package/public/assets/formatted-body-Rv9RDPsQ.js +3 -0
  60. package/public/assets/{formatted-body-BuLfVMeD.js.map → formatted-body-Rv9RDPsQ.js.map} +1 -1
  61. package/public/assets/{formatted-body-impl-DO-8giS6.js → formatted-body-impl-XFU4tPeT.js} +2 -2
  62. package/public/assets/{formatted-body-impl-DO-8giS6.js.map → formatted-body-impl-XFU4tPeT.js.map} +1 -1
  63. package/public/assets/{index-De1tR7J4.js → index-Bhy_jnX4.js} +6 -6
  64. package/public/assets/{index-De1tR7J4.js.map → index-Bhy_jnX4.js.map} +1 -1
  65. package/public/assets/index-DHzXsQot.css +2 -0
  66. package/public/assets/{input-CXMbJ848.js → input-Rc4oWv4q.js} +2 -2
  67. package/public/assets/{input-CXMbJ848.js.map → input-Rc4oWv4q.js.map} +1 -1
  68. package/public/assets/{insights-Ci3VuA5Y.js → insights-DpXPAEwH.js} +2 -2
  69. package/public/assets/{insights-Ci3VuA5Y.js.map → insights-DpXPAEwH.js.map} +1 -1
  70. package/public/assets/{integrations-BboxWQQD.js → integrations-9c6IdRU4.js} +2 -2
  71. package/public/assets/{integrations-BboxWQQD.js.map → integrations-9c6IdRU4.js.map} +1 -1
  72. package/public/assets/{lib-BcUW1l-W.js → lib-BZ6WPg2t.js} +2 -2
  73. package/public/assets/{lib-BcUW1l-W.js.map → lib-BZ6WPg2t.js.map} +1 -1
  74. package/public/assets/lucide-react-NXHPIMsX.js +10 -0
  75. package/public/assets/lucide-react-NXHPIMsX.js.map +1 -0
  76. package/public/assets/{maintenance-CO1miJIJ.js → maintenance-BrkqfGwx.js} +2 -2
  77. package/public/assets/{maintenance-CO1miJIJ.js.map → maintenance-BrkqfGwx.js.map} +1 -1
  78. package/public/assets/{managed-agents-r4oYK3Fm.js → managed-agents-CLANxOAN.js} +2 -2
  79. package/public/assets/{managed-agents-r4oYK3Fm.js.map → managed-agents-CLANxOAN.js.map} +1 -1
  80. package/public/assets/{markdown-preview-impl-5CCM9iyL.js → markdown-preview-impl-CcZFkjn-.js} +2 -2
  81. package/public/assets/{markdown-preview-impl-5CCM9iyL.js.map → markdown-preview-impl-CcZFkjn-.js.map} +1 -1
  82. package/public/assets/{memory-BJJS07W-.js → memory-BoQw8q-i.js} +2 -2
  83. package/public/assets/{memory-BJJS07W-.js.map → memory-BoQw8q-i.js.map} +1 -1
  84. package/public/assets/{messages-RT_dfEsL.js → messages-DfKNuph2.js} +2 -2
  85. package/public/assets/{messages-RT_dfEsL.js.map → messages-DfKNuph2.js.map} +1 -1
  86. package/public/assets/{orchestrators-8hyl8hW1.js → orchestrators-sQh8e7-V.js} +2 -2
  87. package/public/assets/{orchestrators-8hyl8hW1.js.map → orchestrators-sQh8e7-V.js.map} +1 -1
  88. package/public/assets/{overview-CUJ9QmW_.js → overview-B0z8ZNtp.js} +2 -2
  89. package/public/assets/{overview-CUJ9QmW_.js.map → overview-B0z8ZNtp.js.map} +1 -1
  90. package/public/assets/{pairs-CzkzMGZq.js → pairs-B3FhGmn-.js} +2 -2
  91. package/public/assets/{pairs-CzkzMGZq.js.map → pairs-B3FhGmn-.js.map} +1 -1
  92. package/public/assets/{projects-CAuLWBU5.js → projects-iDkxAGBW.js} +2 -2
  93. package/public/assets/{projects-CAuLWBU5.js.map → projects-iDkxAGBW.js.map} +1 -1
  94. package/public/assets/{prompt-settings-BnB90R7F.js → prompt-settings-CCj_s008.js} +2 -2
  95. package/public/assets/{prompt-settings-BnB90R7F.js.map → prompt-settings-CCj_s008.js.map} +1 -1
  96. package/public/assets/{react-dom-BQICbUuF.js → react-dom-8yaD9iJG.js} +2 -2
  97. package/public/assets/{react-dom-BQICbUuF.js.map → react-dom-8yaD9iJG.js.map} +1 -1
  98. package/public/assets/{registry-B8TInqru.js → registry-mD_y06Ru.js} +2 -2
  99. package/public/assets/{registry-B8TInqru.js.map → registry-mD_y06Ru.js.map} +1 -1
  100. package/public/assets/{security-DG_VVZUW.js → security-CTSA9niL.js} +2 -2
  101. package/public/assets/{security-DG_VVZUW.js.map → security-CTSA9niL.js.map} +1 -1
  102. package/public/assets/{select-BJ8G38_V.js → select-gEQEHCIf.js} +2 -2
  103. package/public/assets/{select-BJ8G38_V.js.map → select-gEQEHCIf.js.map} +1 -1
  104. package/public/assets/{settings-CK_T9z5w.js → settings-ByAR-TYj.js} +2 -2
  105. package/public/assets/{settings-CK_T9z5w.js.map → settings-ByAR-TYj.js.map} +1 -1
  106. package/public/assets/store-D-Frk_EC.js +9 -0
  107. package/public/assets/store-D-Frk_EC.js.map +1 -0
  108. package/public/assets/{task-chip-BHM-e3I8.js → task-chip-CeujtXYT.js} +2 -2
  109. package/public/assets/{task-chip-BHM-e3I8.js.map → task-chip-CeujtXYT.js.map} +1 -1
  110. package/public/assets/{tasks-Briui9Mp.js → tasks-CZyJX881.js} +2 -2
  111. package/public/assets/{tasks-Briui9Mp.js.map → tasks-CZyJX881.js.map} +1 -1
  112. package/public/assets/{teams-Ck4EVV3R.js → teams-BrL4sEL7.js} +2 -2
  113. package/public/assets/{teams-Ck4EVV3R.js.map → teams-BrL4sEL7.js.map} +1 -1
  114. package/public/assets/{terminal-viewer-impl-66ZjVN1v.js → terminal-viewer-impl-LjIph8ql.js} +2 -2
  115. package/public/assets/{terminal-viewer-impl-66ZjVN1v.js.map → terminal-viewer-impl-LjIph8ql.js.map} +1 -1
  116. package/public/assets/types-BqeSkWrv.js.map +1 -1
  117. package/public/assets/{use-keyboard-viewport-CAa7IQTm.js → use-keyboard-viewport-cGEmrGE7.js} +2 -2
  118. package/public/assets/{use-keyboard-viewport-CAa7IQTm.js.map → use-keyboard-viewport-cGEmrGE7.js.map} +1 -1
  119. package/public/assets/{user-avatar-BOy9o2xK.js → user-avatar-qbo-B3dQ.js} +2 -2
  120. package/public/assets/{user-avatar-BOy9o2xK.js.map → user-avatar-qbo-B3dQ.js.map} +1 -1
  121. package/public/assets/{work-queue-CnghFRG2.js → work-queue-CSu5kRsw.js} +2 -2
  122. package/public/assets/{work-queue-CnghFRG2.js.map → work-queue-CSu5kRsw.js.map} +1 -1
  123. package/public/assets/{workspaces-BzmQnzSm.js → workspaces-CoDz-iZd.js} +2 -2
  124. package/public/assets/{workspaces-BzmQnzSm.js.map → workspaces-CoDz-iZd.js.map} +1 -1
  125. package/public/index.html +24 -24
  126. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  127. package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +2 -2
  128. package/runner/src/adapters/codex-agent-message-capture.ts +38 -8
  129. package/runner/src/relay-mcp-proxy.ts +1 -3
  130. package/runner/src/runner-core.ts +33 -13
  131. package/runner/src/runner-helpers.ts +31 -0
  132. package/src/db/index.ts +1 -0
  133. package/src/db/messages.ts +38 -2
  134. package/src/db/task-deliverables.ts +206 -0
  135. package/src/db/task-entity.ts +4 -8
  136. package/src/mcp/index.ts +4 -4
  137. package/src/mcp/tools-workspace.ts +3 -36
  138. package/src/mcp-task-deliver-tools.ts +143 -0
  139. package/src/notification-rules.ts +1 -0
  140. package/src/notification-types.ts +2 -0
  141. package/src/prompt-resolver.ts +6 -6
  142. package/src/routes/agent-sessions.ts +10 -7
  143. package/src/routes/orchestrator.ts +5 -2
  144. package/src/routes/settings.ts +31 -2
  145. package/src/services/restart-instructions.ts +36 -0
  146. package/src/services/task-delivery.ts +275 -0
  147. package/src/settings/registry.ts +14 -3
  148. package/src/workspace-phase.ts +5 -5
  149. package/public/assets/chat-CRzFSFWe.js +0 -2
  150. package/public/assets/chat-CRzFSFWe.js.map +0 -1
  151. package/public/assets/formatted-body-BuLfVMeD.js +0 -3
  152. package/public/assets/index-C9X_0QBt.css +0 -2
  153. package/public/assets/lucide-react-CoU_Xts2.js +0 -10
  154. package/public/assets/lucide-react-CoU_Xts2.js.map +0 -1
  155. package/public/assets/store-Dl9Z8YUS.js +0 -9
  156. package/public/assets/store-Dl9Z8YUS.js.map +0 -1
package/docs/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Relay API",
5
- "version": "0.125.0",
5
+ "version": "0.127.0",
6
6
  "description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
7
7
  "license": {
8
8
  "name": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.125.0",
3
+ "version": "0.127.0",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -37,9 +37,9 @@
37
37
  "CONTRIBUTING.md"
38
38
  ],
39
39
  "dependencies": {
40
- "agent-relay-channels-host": "0.125.0",
40
+ "agent-relay-channels-host": "0.127.0",
41
41
  "agent-relay-providers": "0.104.4",
42
- "agent-relay-sdk": "0.2.116",
42
+ "agent-relay-sdk": "0.2.118",
43
43
  "ajv": "^8.20.0"
44
44
  },
45
45
  "scripts": {
@@ -54,7 +54,7 @@
54
54
  "build:dashboard": "bun run build:dashboard:bundle && bun run deploy:dashboard",
55
55
  "build:dashboard:bundle": "bun run build:sdk && cd dashboard && bun run build",
56
56
  "deploy:dashboard": "bun run scripts/deploy-dashboard.ts",
57
- "ci:land": "bun run build:dashboard:bundle && bun run build:claude-monitor:check && bun run lint:import-boundaries && bun run lint:provider-contract && bun run typecheck && bun run docs:api:check && bun test",
57
+ "ci:land": "bun run build:dashboard:bundle && bun run build:claude-monitor:check && bun run lint:import-boundaries && bun run lint:provider-contract && bun run typecheck && bun run docs:api:check && bun run test",
58
58
  "ci": "bun run ci:land",
59
59
  "test": "bun test",
60
60
  "smoke:spawn": "bun run scripts/orchestrator-spawn-smoke.ts",
@@ -68,7 +68,9 @@
68
68
  "docs:api:check": "bun run scripts/extract-api-spec.ts --check",
69
69
  "release": "bun run scripts/release.ts",
70
70
  "publish:ci": "bun run scripts/publish-ci.ts",
71
- "publish:bootstrap": "bun run scripts/publish-bootstrap.ts"
71
+ "publish:bootstrap": "bun run scripts/publish-bootstrap.ts",
72
+ "e2e:scroll": "playwright test --config=dashboard/e2e/playwright.config.ts",
73
+ "e2e:scroll:install": "playwright install --with-deps chromium webkit"
72
74
  },
73
75
  "keywords": [
74
76
  "agent-relay",
@@ -107,8 +109,10 @@
107
109
  "license": "AGPL-3.0-or-later",
108
110
  "author": "Edin Mujkanovic",
109
111
  "devDependencies": {
112
+ "@playwright/test": "^1.55.0",
110
113
  "@types/bun": "latest",
111
114
  "@types/madge": "^5.0.3",
115
+ "@types/node": "^22",
112
116
  "callmux": "0.23.0",
113
117
  "madge": "^8.0.0",
114
118
  "typescript": "^5"
@@ -1,2 +1,2 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{Cn as t,Kn as n,S as r,Wt as i,a,ar as o,gr as s,l as c,mn as l,mt as u,qn as d,r as f,rr as p,tn as m,tr as h,xn as g}from"./lucide-react-CoU_Xts2.js";import{b as _,t as v}from"./store-Dl9Z8YUS.js";import{c as y}from"./types-BqeSkWrv.js";import{V as b,_ as x,f as S,k as C,v as ee}from"./display-aTnwsqQh.js";import{t as w}from"./agent-type-icon-B3i0rndK.js";import{t as T}from"./formatted-body-BuLfVMeD.js";import{A as te,D as E,E as D,I as ne,M as O,P as k,h as A,j,l as M,n as N,o as P,t as F,z as I}from"./index-De1tR7J4.js";import{t as L}from"./user-avatar-BOy9o2xK.js";var R=e(s(),1);function re(){return(0,R.useSyncExternalStore)(e=>_.subscribe(e),()=>_.getPlayingKey(),()=>null)}var z=p();function ie({msg:e,peer:t}){return(0,z.jsx)(A,{msg:e,issueRepo:v(e=>e.agentsById[t]?.issueRepo)})}function B({agent:e,onClick:t}){let n=!!(t&&e);return(0,z.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:h(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:e})})}function ae({agentId:e,onOpen:t}){let n=v(t=>t.agentsById[e]??null);return(0,z.jsx)(B,{agent:n,onClick:n?()=>t(n):void 0})}function V({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[s,c]=(0,R.useState)(null),[l,d]=(0,R.useState)(null),[p,_]=(0,R.useState)(!1),[v,y]=(0,R.useState)(!1);(0,R.useEffect)(()=>{let t=!1,n=null;async function r(){_(!0),d(null),c(null);try{let r=await o(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),c(n)}catch(e){t||d(e.message)}finally{t||_(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function b(){if(!s)return;let e=document.createElement(`a`);e.href=s,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`button`,{type:`button`,onClick:()=>s&&y(!0),disabled:!s,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:s?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`img`,{src:s,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,z.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,z.jsx)(f,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,z.jsxs)(`span`,{className:h(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,l?`text-red-400`:`text-muted-foreground`),children:[p?(0,z.jsx)(u,{className:`h-4 w-4 animate-spin`}):(0,z.jsx)(g,{className:`h-4 w-4`}),l||`Loading image`]})}),s&&(0,z.jsx)(F,{open:v,onOpenChange:y,children:(0,z.jsxs)(N,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,z.jsx)(P,{className:`sr-only`,children:r}),(0,z.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,z.jsx)(`img`,{src:s,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,z.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,z.jsx)(`button`,{type:`button`,onClick:b,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,z.jsx)(m,{className:`w-4 h-4`})}),(0,z.jsx)(`button`,{type:`button`,onClick:()=>y(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,z.jsx)(a,{className:`w-4 h-4`})})]})]})]})})]}):(0,z.jsxs)(`button`,{type:`button`,onClick:b,disabled:!s,className:h(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[p?(0,z.jsx)(u,{className:`h-4 w-4 shrink-0 animate-spin`}):l?(0,z.jsx)(g,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,z.jsx)(i,{className:`h-4 w-4 shrink-0`}),(0,z.jsx)(`span`,{className:h(`min-w-0 flex-1 truncate`,l&&`text-red-400`),children:l||r}),(0,z.jsx)(m,{className:`h-3.5 w-3.5 shrink-0`})]})}function H({open:e,onToggle:t,onReact:n}){return(0,z.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,z.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:h(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,z.jsx)(r,{className:`h-3 w-3`})}),e&&(0,z.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:D.map(e=>(0,z.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function U({msg:e,peer:r}){let[i,a]=(0,R.useState)(!1),o=v(e=>e.agentsById),s=v(e=>e.openAgentDetail),c=e.from===r,l=(c?e.to:e.from)||``,u=o[c?r:l]?.issueRepo??o[r]?.issueRepo,f=!l||l===`broadcast`||l.includes(`:`),p=C(e),m=E(e.occurredAt??e.createdAt),g=c?n:d,_=o[e.from??``]??null;function y(e){e.stopPropagation(),_&&s(_)}return(0,z.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,z.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,z.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>a(e=>!e),children:[(0,z.jsx)(t,{className:h(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,i&&`rotate-90`)}),(0,z.jsx)(`button`,{type:`button`,title:_?_.label||_.name||_.id:void 0,disabled:!_,onClick:y,className:h(`shrink-0 transition-opacity`,_?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:_,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,z.jsx)(g,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,z.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,c?`to`:`from`]}),f?(0,z.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:S(l,o)}):(0,z.jsx)(M,{agentId:l,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!i&&(0,z.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:p}),(0,z.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:m})]}),i&&(0,z.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,z.jsx)(T,{text:p,rawBody:e.body,issueRepo:u,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var W=(0,R.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(!y(e.from)&&!y(e.to))return(0,z.jsx)(U,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,z.jsx)(ie,{msg:e,peer:t});let s=y(e.from),u=v(e=>e.reactToMessage),d=v(e=>e.voiceTtsEnabled),f=re(),p=v(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),m=v(e=>e.agentsById[t]?.issueRepo),g=v(e=>e.usersById),S=v(e=>e.users.length>1),w=s?b(e.from,g):void 0,D=v(e=>e.openAgentDetail);v(e=>e.openTaskDetailById);let[A,M]=(0,R.useState)(!1),N=(0,R.useRef)(null),P=(0,R.useRef)(null),F=(0,R.useRef)(!1),B=C(e),W=E(e.occurredAt??e.createdAt),G=k(e),K=te(e),q=s?ne(e,t):null,J=q?.icon,Y=f===String(e.id),oe=d&&!s&&!O(e)&&B.trim().length>0;function se(t){t.stopPropagation(),Y?_.bargeIn():_.speak(e.body,String(e.id))}(0,R.useEffect)(()=>{if(!A)return;function e(e){j()||N.current&&!N.current.contains(e.target)&&M(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[A]);function X(e){return I(p||`/`,e)}function ce(e,t){let n=X(e);r?.(n,t)}function Z(e,t,n){let r=X(e);i?.(r,t,n)}function Q(t){u(e,t),M(!1)}function le(e){e.button===0&&(P.current={x:e.clientX,y:e.clientY},F.current=!1)}function ue(e){let t=P.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(F.current=!0)}function de(t){if(!(O(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(F.current||j()){F.current=!1;return}M(e=>!e)}}let $=n&&!O(e);return(0,z.jsxs)(`div`,{"data-msg-id":e.id,className:h(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,$&&`gap-1.5`),children:[!s&&$&&(0,z.jsx)(ae,{agentId:t,onOpen:D}),(0,z.jsxs)(`div`,{ref:N,className:h(`relative`,$?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[oe&&(0,z.jsx)(`button`,{type:`button`,title:Y?`Stop playback`:`Play aloud`,onClick:se,className:h(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,Y?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:Y?(0,z.jsx)(l,{className:`h-3.5 w-3.5 animate-pulse`}):(0,z.jsx)(c,{className:`h-3.5 w-3.5`})}),(0,z.jsxs)(`div`,{className:h(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!O(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:le,onPointerMove:ue,onClick:de,children:[s&&S&&(0,z.jsx)(`p`,{className:h(`text-[11px] font-semibold mb-0.5 text-right`,x(e.from)),children:ee(e.from,g)}),e.subject&&(0,z.jsx)(`p`,{className:h(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,z.jsx)(T,{text:B,rawBody:e.body,issueRepo:m,className:`leading-relaxed`,onPrimary:s,onOpenPath:ce,onPreviewPath:Z,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${X(e)}${t?`:${t}`:``}`}),G.map(t=>(0,z.jsx)(V,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,z.jsxs)(`p`,{className:h(`text-xs mt-1 flex items-center gap-1.5`,s?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,z.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,z.jsx)(`span`,{children:W}),q&&J&&(0,z.jsxs)(`span`,{className:h(`inline-flex items-center gap-1`,q.tone===`ok`&&`text-emerald-300`,q.tone===`warn`&&`text-amber-200`,q.tone===`danger`&&`text-red-200`),title:q.title,children:[(0,z.jsx)(J,{className:`h-3 w-3`}),q.label]})]})]}),!O(e)&&K.length>0&&(0,z.jsxs)(`div`,{className:h(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,z.jsx)(H,{open:A,onToggle:()=>M(e=>!e),onReact:Q}),K.map(e=>(0,z.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>Q(e.emoji),className:h(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,z.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,z.jsx)(`span`,{children:e.count})]},e.emoji))]}),!O(e)&&!s&&K.length===0&&(0,z.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,z.jsx)(H,{open:A,onToggle:()=>M(e=>!e),onReact:Q})})]}),s&&$&&(0,z.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,z.jsx)(L,{user:w,address:e.from,distinct:S})})]})});export{W as t};
2
- //# sourceMappingURL=MessageBubble-BYeYFteG.js.map
1
+ import{r as e}from"./chunk-CilyBKbf.js";import{Cn as t,Kn as n,S as r,Wt as i,a,ar as o,gr as s,l as c,mn as l,mt as u,qn as d,r as f,rr as p,tn as m,tr as h,xn as g}from"./lucide-react-NXHPIMsX.js";import{b as _,t as v}from"./store-D-Frk_EC.js";import{c as y}from"./types-BqeSkWrv.js";import{V as b,_ as x,f as S,k as C,v as ee}from"./display-aTnwsqQh.js";import{t as w}from"./agent-type-icon-BmBR-9CH.js";import{t as T}from"./formatted-body-Rv9RDPsQ.js";import{A as te,D as E,E as D,I as ne,M as O,P as k,h as A,j,l as M,n as N,o as P,t as F,z as I}from"./index-Bhy_jnX4.js";import{t as L}from"./user-avatar-qbo-B3dQ.js";var R=e(s(),1);function re(){return(0,R.useSyncExternalStore)(e=>_.subscribe(e),()=>_.getPlayingKey(),()=>null)}var z=p();function ie({msg:e,peer:t}){return(0,z.jsx)(A,{msg:e,issueRepo:v(e=>e.agentsById[t]?.issueRepo)})}function B({agent:e,onClick:t}){let n=!!(t&&e);return(0,z.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:h(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:e})})}function ae({agentId:e,onOpen:t}){let n=v(t=>t.agentsById[e]??null);return(0,z.jsx)(B,{agent:n,onClick:n?()=>t(n):void 0})}function V({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[s,c]=(0,R.useState)(null),[l,d]=(0,R.useState)(null),[p,_]=(0,R.useState)(!1),[v,y]=(0,R.useState)(!1);(0,R.useEffect)(()=>{let t=!1,n=null;async function r(){_(!0),d(null),c(null);try{let r=await o(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),c(n)}catch(e){t||d(e.message)}finally{t||_(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function b(){if(!s)return;let e=document.createElement(`a`);e.href=s,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`button`,{type:`button`,onClick:()=>s&&y(!0),disabled:!s,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:s?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`img`,{src:s,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,z.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,z.jsx)(f,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,z.jsxs)(`span`,{className:h(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,l?`text-red-400`:`text-muted-foreground`),children:[p?(0,z.jsx)(u,{className:`h-4 w-4 animate-spin`}):(0,z.jsx)(g,{className:`h-4 w-4`}),l||`Loading image`]})}),s&&(0,z.jsx)(F,{open:v,onOpenChange:y,children:(0,z.jsxs)(N,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,z.jsx)(P,{className:`sr-only`,children:r}),(0,z.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,z.jsx)(`img`,{src:s,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,z.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,z.jsx)(`button`,{type:`button`,onClick:b,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,z.jsx)(m,{className:`w-4 h-4`})}),(0,z.jsx)(`button`,{type:`button`,onClick:()=>y(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,z.jsx)(a,{className:`w-4 h-4`})})]})]})]})})]}):(0,z.jsxs)(`button`,{type:`button`,onClick:b,disabled:!s,className:h(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[p?(0,z.jsx)(u,{className:`h-4 w-4 shrink-0 animate-spin`}):l?(0,z.jsx)(g,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,z.jsx)(i,{className:`h-4 w-4 shrink-0`}),(0,z.jsx)(`span`,{className:h(`min-w-0 flex-1 truncate`,l&&`text-red-400`),children:l||r}),(0,z.jsx)(m,{className:`h-3.5 w-3.5 shrink-0`})]})}function H({open:e,onToggle:t,onReact:n}){return(0,z.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,z.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:h(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,z.jsx)(r,{className:`h-3 w-3`})}),e&&(0,z.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:D.map(e=>(0,z.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function U({msg:e,peer:r}){let[i,a]=(0,R.useState)(!1),o=v(e=>e.agentsById),s=v(e=>e.openAgentDetail),c=e.from===r,l=(c?e.to:e.from)||``,u=o[c?r:l]?.issueRepo??o[r]?.issueRepo,f=!l||l===`broadcast`||l.includes(`:`),p=C(e),m=E(e.occurredAt??e.createdAt),g=c?n:d,_=o[e.from??``]??null;function y(e){e.stopPropagation(),_&&s(_)}return(0,z.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,z.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,z.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>a(e=>!e),children:[(0,z.jsx)(t,{className:h(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,i&&`rotate-90`)}),(0,z.jsx)(`button`,{type:`button`,title:_?_.label||_.name||_.id:void 0,disabled:!_,onClick:y,className:h(`shrink-0 transition-opacity`,_?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,z.jsx)(w,{agent:_,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,z.jsx)(g,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,z.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,c?`to`:`from`]}),f?(0,z.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:S(l,o)}):(0,z.jsx)(M,{agentId:l,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!i&&(0,z.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:p}),(0,z.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:m})]}),i&&(0,z.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,z.jsx)(T,{text:p,rawBody:e.body,issueRepo:u,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var W=(0,R.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(!y(e.from)&&!y(e.to))return(0,z.jsx)(U,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,z.jsx)(ie,{msg:e,peer:t});let s=y(e.from),u=v(e=>e.reactToMessage),d=v(e=>e.voiceTtsEnabled),f=re(),p=v(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),m=v(e=>e.agentsById[t]?.issueRepo),g=v(e=>e.usersById),S=v(e=>e.users.length>1),w=s?b(e.from,g):void 0,D=v(e=>e.openAgentDetail);v(e=>e.openTaskDetailById);let[A,M]=(0,R.useState)(!1),N=(0,R.useRef)(null),P=(0,R.useRef)(null),F=(0,R.useRef)(!1),B=C(e),W=E(e.occurredAt??e.createdAt),G=k(e),K=te(e),q=s?ne(e,t):null,J=q?.icon,Y=f===String(e.id),oe=d&&!s&&!O(e)&&B.trim().length>0;function se(t){t.stopPropagation(),Y?_.bargeIn():_.speak(e.body,String(e.id))}(0,R.useEffect)(()=>{if(!A)return;function e(e){j()||N.current&&!N.current.contains(e.target)&&M(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[A]);function X(e){return I(p||`/`,e)}function ce(e,t){let n=X(e);r?.(n,t)}function Z(e,t,n){let r=X(e);i?.(r,t,n)}function Q(t){u(e,t),M(!1)}function le(e){e.button===0&&(P.current={x:e.clientX,y:e.clientY},F.current=!1)}function ue(e){let t=P.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(F.current=!0)}function de(t){if(!(O(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(F.current||j()){F.current=!1;return}M(e=>!e)}}let $=n&&!O(e);return(0,z.jsxs)(`div`,{"data-msg-id":e.id,className:h(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,$&&`gap-1.5`),children:[!s&&$&&(0,z.jsx)(ae,{agentId:t,onOpen:D}),(0,z.jsxs)(`div`,{ref:N,className:h(`relative`,$?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[oe&&(0,z.jsx)(`button`,{type:`button`,title:Y?`Stop playback`:`Play aloud`,onClick:se,className:h(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,Y?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:Y?(0,z.jsx)(l,{className:`h-3.5 w-3.5 animate-pulse`}):(0,z.jsx)(c,{className:`h-3.5 w-3.5`})}),(0,z.jsxs)(`div`,{className:h(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!O(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:le,onPointerMove:ue,onClick:de,children:[s&&S&&(0,z.jsx)(`p`,{className:h(`text-[11px] font-semibold mb-0.5 text-right`,x(e.from)),children:ee(e.from,g)}),e.subject&&(0,z.jsx)(`p`,{className:h(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,z.jsx)(T,{text:B,rawBody:e.body,issueRepo:m,className:`leading-relaxed`,onPrimary:s,onOpenPath:ce,onPreviewPath:Z,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${X(e)}${t?`:${t}`:``}`}),G.map(t=>(0,z.jsx)(V,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,z.jsxs)(`p`,{className:h(`text-xs mt-1 flex items-center gap-1.5`,s?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,z.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,z.jsx)(`span`,{children:W}),q&&J&&(0,z.jsxs)(`span`,{className:h(`inline-flex items-center gap-1`,q.tone===`ok`&&`text-emerald-300`,q.tone===`warn`&&`text-amber-200`,q.tone===`danger`&&`text-red-200`),title:q.title,children:[(0,z.jsx)(J,{className:`h-3 w-3`}),q.label]})]})]}),!O(e)&&K.length>0&&(0,z.jsxs)(`div`,{className:h(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,z.jsx)(H,{open:A,onToggle:()=>M(e=>!e),onReact:Q}),K.map(e=>(0,z.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>Q(e.emoji),className:h(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,z.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,z.jsx)(`span`,{children:e.count})]},e.emoji))]}),!O(e)&&!s&&K.length===0&&(0,z.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,z.jsx)(H,{open:A,onToggle:()=>M(e=>!e),onReact:Q})})]}),s&&$&&(0,z.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,z.jsx)(L,{user:w,address:e.from,distinct:S})})]})});export{W as t};
2
+ //# sourceMappingURL=MessageBubble-D2cnxyTy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageBubble-BYeYFteG.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useTtsPlayingKey.ts","../../dashboard/src/components/chat/MessageBubble.tsx"],"sourcesContent":["\nimport { useSyncExternalStore } from 'react'\nimport { voiceTts } from '@/lib/voice'\n\nexport function useTtsPlayingKey(): string | null {\n return useSyncExternalStore(\n (cb) => voiceTts.subscribe(cb),\n () => voiceTts.getPlayingKey(),\n () => null,\n )\n}\n","\nimport { memo, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'\nimport { AlertCircle, ArrowLeft, ArrowRight, ChevronRight, CircleStop, Download, FileText, Loader2, SmilePlus, Volume2, X, ZoomIn } from 'lucide-react'\nimport { voiceTts } from '@/lib/voice'\nimport { useRelayStore } from '@/store'\nimport { FormattedBody } from '@/components/shared/formatted-body'\nimport { AgentLink, InitialPromptBubble } from './TraceView'\nimport { AgentTypeIcon } from '@/components/shared/agent-type-icon'\nimport { Button } from '@/components/ui/button'\nimport { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'\nimport { apiBlob } from '@/lib/api'\nimport { displayTarget, humanAccentClass, humanDisplayName, messageBody, resolveUser } from '@/lib/display'\nimport { isHumanAgentId } from 'agent-relay-sdk/types'\nimport { UserAvatar } from '@/components/shared/user-avatar'\nimport type { Agent, AttachmentRef, Message } from '@/types'\nimport { cn } from '@/lib/utils'\nimport type { ReferencedPathInfo } from './types'\nimport {\n chatTimestamp,\n groupedReactions,\n hasActiveTextSelection,\n isReactionEvent,\n messageAttachments,\n outboundReceipt,\n QUICK_REACTIONS,\n resolveRelativePath,\n} from './helpers'\nimport { useTtsPlayingKey } from './hooks/useTtsPlayingKey'\n\n// Spawn prompts (origin:\"provider\") are system-initiated — reuse InitialPromptBubble's\n// panel style rather than masquerading as an operator prompt bubble (Tier A, #420).\nfunction SpawnPromptBubble({ msg, peer }: { msg: Message; peer: string }) {\n const issueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n return <InitialPromptBubble msg={msg} issueRepo={issueRepo} />\n}\n\n// Small tappable sender avatar placed at the edge of a bubble (Tier B, #420).\n// Uses AgentTypeIcon so provider icon resolution stays in one place (DRY ratchet).\nfunction BubbleAvatar({ agent, onClick }: { agent: Agent | null; onClick?: () => void }) {\n const canClick = Boolean(onClick && agent)\n return (\n <button\n type=\"button\"\n title={agent ? (agent.label || agent.name || agent.id) : undefined}\n onClick={onClick}\n disabled={!canClick}\n className={cn('shrink-0 self-end transition-opacity', canClick ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={agent} />\n </button>\n )\n}\n\n// Subscribes to the agent store independently so MessageBubble's text body\n// doesn't re-render (and lose text selection) when agentsById[peer] gets a new\n// reference from a lastSeen/status poll update (#691).\nfunction ConnectedBubbleAvatar({ agentId, onOpen }: { agentId: string; onOpen: (agent: Agent) => void }) {\n const agent = useRelayStore((s) => s.agentsById[agentId] ?? null)\n return <BubbleAvatar agent={agent} onClick={agent ? () => onOpen(agent) : undefined} />\n}\n\nfunction AttachmentPreview({ attachment, outbound }: { attachment: AttachmentRef; outbound: boolean }) {\n const isImage = attachment.kind === 'image'\n const title = attachment.title || attachment.artifactId\n const [objectUrl, setObjectUrl] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [lightboxOpen, setLightboxOpen] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n\n async function load() {\n setLoading(true)\n setError(null)\n setObjectUrl(null)\n try {\n const blob = await apiBlob(`/artifacts/${encodeURIComponent(attachment.artifactId)}/content`)\n if (cancelled) return\n url = URL.createObjectURL(blob)\n setObjectUrl(url)\n } catch (e) {\n if (!cancelled) setError((e as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n\n void load()\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [attachment.artifactId])\n\n function download() {\n if (!objectUrl) return\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = title\n document.body.appendChild(a)\n a.click()\n a.remove()\n }\n\n if (isImage) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => objectUrl && setLightboxOpen(true)}\n disabled={!objectUrl}\n className=\"group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative\"\n >\n {objectUrl ? (\n <>\n <img src={objectUrl} alt={title} loading=\"lazy\" className=\"block max-h-72 max-w-full object-contain\" />\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors\">\n <ZoomIn className=\"w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md\" />\n </span>\n </>\n ) : (\n <span className={cn('flex min-h-20 items-center gap-2 px-3 py-2 text-xs', error ? 'text-red-400' : 'text-muted-foreground')}>\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <AlertCircle className=\"h-4 w-4\" />}\n {error || 'Loading image'}\n </span>\n )}\n </button>\n {objectUrl && (\n <Dialog open={lightboxOpen} onOpenChange={setLightboxOpen}>\n <DialogContent className=\"max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden\" showCloseButton={false}>\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <div className=\"relative flex items-center justify-center min-h-[200px]\">\n <img src={objectUrl} alt={title} className=\"max-w-[93vw] max-h-[90vh] object-contain\" />\n <div className=\"absolute top-2 right-2 flex gap-1\">\n <button type=\"button\" onClick={download} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Download\">\n <Download className=\"w-4 h-4\" />\n </button>\n <button type=\"button\" onClick={() => setLightboxOpen(false)} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Close\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n </>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={download}\n disabled={!objectUrl}\n className={cn(\n 'mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70',\n outbound ? 'border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15' : 'border-border bg-background hover:bg-muted'\n )}\n >\n {loading ? <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" /> : error ? <AlertCircle className=\"h-4 w-4 shrink-0 text-red-400\" /> : <FileText className=\"h-4 w-4 shrink-0\" />}\n <span className={cn('min-w-0 flex-1 truncate', error && 'text-red-400')}>{error || title}</span>\n <Download className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n )\n}\n\nfunction AddReaction({ open, onToggle, onReact }: { open: boolean; onToggle: () => void; onReact: (emoji: string) => void }) {\n return (\n <div className=\"relative shrink-0\">\n <button\n type=\"button\"\n title=\"Add reaction\"\n onClick={onToggle}\n className={cn(\n 'inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground',\n open ? 'opacity-100' : 'opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto'\n )}\n >\n <SmilePlus className=\"h-3 w-3\" />\n </button>\n {open && (\n <div className=\"absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md\">\n {QUICK_REACTIONS.map((emoji) => (\n <button\n key={emoji}\n type=\"button\"\n title={`React ${emoji}`}\n onClick={() => onReact(emoji)}\n className=\"inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted\"\n >\n {emoji}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// Inter-agent (peer↔peer) messages render as a compact, foldable one-liner — distinct\n// from the user↔agent bubbles, never persisted-open (#508). The other party is shown via\n// AgentLink (label-preferred + clickable, #522); fan-out targets (broadcast/tag:/cap:/…)\n// fall back to a plain displayTarget label since they don't resolve to one agent.\nfunction PeerMessageLine({ msg, peer }: { msg: Message; peer: string }) {\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const isOutbound = msg.from === peer\n const otherParty = (isOutbound ? msg.to : msg.from) || ''\n // Per-project #NNN linkify (#533/#543): outbound folded lines use the selected\n // peer's repo; inbound inter-agent lines use the originating agent's repo.\n const issueRepo = agentsById[isOutbound ? peer : otherParty]?.issueRepo ?? agentsById[peer]?.issueRepo\n const isSelector = !otherParty || otherParty === 'broadcast' || otherParty.includes(':')\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const DirIcon = isOutbound ? ArrowRight : ArrowLeft\n const senderAgent = agentsById[msg.from ?? ''] ?? null\n function openSenderDetail(e: ReactMouseEvent<HTMLButtonElement>) {\n e.stopPropagation()\n if (senderAgent) openAgentDetail(senderAgent)\n }\n\n return (\n <div data-msg-id={msg.id} className=\"flex justify-center mb-1.5\">\n <div className=\"w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1\">\n <div className=\"flex items-center gap-1.5 text-xs cursor-pointer select-none\" onClick={() => setExpanded((v) => !v)}>\n <ChevronRight className={cn('h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform', expanded && 'rotate-90')} />\n <button\n type=\"button\"\n title={senderAgent ? (senderAgent.label || senderAgent.name || senderAgent.id) : undefined}\n disabled={!senderAgent}\n onClick={openSenderDetail}\n className={cn('shrink-0 transition-opacity', senderAgent ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={senderAgent} className=\"!w-4 !h-4 !rounded-sm opacity-70\" />\n </button>\n <DirIcon className=\"h-3 w-3 shrink-0 text-muted-foreground/70\" />\n <span className=\"shrink-0 text-muted-foreground\">Message {isOutbound ? 'to' : 'from'}</span>\n {isSelector\n ? <span className=\"shrink-0 font-medium text-muted-foreground\">{displayTarget(otherParty, agentsById)}</span>\n : <AgentLink agentId={otherParty} className=\"shrink-0 px-0.5 text-foreground/80\" noIcon />}\n {!expanded && <span className=\"min-w-0 flex-1 truncate text-muted-foreground/50\">{body}</span>}\n <span className=\"ml-auto shrink-0 text-muted-foreground/40\">{time}</span>\n </div>\n {expanded && (\n <div className=\"mt-1.5 pl-5\">\n <FormattedBody text={body} rawBody={msg.body} issueRepo={issueRepo} className=\"text-sm leading-relaxed text-foreground/90\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport const MessageBubble = memo(function MessageBubble({\n msg,\n peer,\n showAvatar = false,\n onOpenReferencedPath,\n onPreviewReferencedPath,\n onPreviewReferencedPathEnd,\n}: {\n msg: Message\n peer: string\n /** Show the sender avatar on this bubble (last in a consecutive same-sender run). */\n showAvatar?: boolean\n onOpenReferencedPath?: (path: string, line?: number, type?: ReferencedPathInfo['type']) => void\n onPreviewReferencedPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewReferencedPathEnd?: () => void\n}) {\n // Inter-agent (peer↔peer) messages get a folded one-liner instead of a bubble (#508).\n // This early-return precedes all hooks, which is safe here: the timeline keys each\n // MessageBubble by msg id (VirtualTimeline.timelineEntryKey), and from/to are immutable\n // for a given id — so a mounted instance never flips between the two render paths.\n // De-singletoned human addressing (#390): any human side — legacy bare `'user'` or a\n // per-user `user:<id>` sink — renders as a user↔agent bubble; only agent↔agent stays a\n // folded peer line. isHumanAgentId keeps multi-human threads from collapsing to peer lines.\n if (!isHumanAgentId(msg.from) && !isHumanAgentId(msg.to)) {\n return <PeerMessageLine msg={msg} peer={peer} />\n }\n // Spawn prompts (kind:\"session\", type:\"prompt\", origin:\"provider\") are system-initiated\n // and must NOT masquerade as operator-typed prompts. Render with the InitialPromptBubble\n // panel style instead (#420 Tier A). Immutable payload — safe as a pre-hook early return.\n const sessionPayload = msg.kind === 'session'\n ? (msg.payload?.session as { type?: string; origin?: string } | undefined)\n : undefined\n if (sessionPayload?.type === 'prompt' && sessionPayload?.origin === 'provider') {\n return <SpawnPromptBubble msg={msg} peer={peer} />\n }\n const isOutbound = isHumanAgentId(msg.from)\n const reactToMessage = useRelayStore((s) => s.reactToMessage)\n const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled)\n const ttsPlayingKey = useTtsPlayingKey()\n const peerCwd = useRelayStore((s) => {\n const cwd = s.agentsById[peer]?.meta?.cwd\n return typeof cwd === 'string' ? cwd : ''\n })\n // Per-project #NNN linkify (#533): the peer agent's resolved project repo.\n const peerIssueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n // Human attribution (#390): resolve which user sent an outbound bubble, and whether more\n // than one human exists. With the single seeded user:default, multiHuman is false and the\n // name tag / colored avatar stay off — the bubble renders exactly as before.\n const usersById = useRelayStore((s) => s.usersById)\n const multiHuman = useRelayStore((s) => s.users.length > 1)\n const sendingUser = isOutbound ? resolveUser(msg.from, usersById) : undefined\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const [showQuickReact, setShowQuickReact] = useState(false)\n const bubbleRef = useRef<HTMLDivElement>(null)\n const pointerStartRef = useRef<{ x: number; y: number } | null>(null)\n const suppressBubbleClickRef = useRef(false)\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const attachments = messageAttachments(msg)\n const reactions = groupedReactions(msg)\n const receipt = isOutbound ? outboundReceipt(msg, peer) : null\n const ReceiptIcon = receipt?.icon\n // On-demand TTS: replay any agent response through the same Kokoro/browser\n // pipeline. Only when the speaker is on, and only for inbound non-reaction text.\n const isTtsPlaying = ttsPlayingKey === String(msg.id)\n const showPlayButton = voiceTtsEnabled && !isOutbound && !isReactionEvent(msg) && body.trim().length > 0\n function togglePlay(e: React.MouseEvent) {\n e.stopPropagation()\n if (isTtsPlaying) voiceTts.bargeIn()\n else voiceTts.speak(msg.body, String(msg.id))\n }\n useEffect(() => {\n if (!showQuickReact) return\n function dismiss(e: MouseEvent | TouchEvent) {\n if (hasActiveTextSelection()) return\n if (bubbleRef.current && !bubbleRef.current.contains(e.target as Node)) setShowQuickReact(false)\n }\n document.addEventListener('mousedown', dismiss)\n document.addEventListener('touchstart', dismiss)\n return () => { document.removeEventListener('mousedown', dismiss); document.removeEventListener('touchstart', dismiss) }\n }, [showQuickReact])\n\n function resolvedReferencedPath(path: string): string {\n return resolveRelativePath(peerCwd || '/', path)\n }\n\n function openReferencedPath(path: string, line?: number) {\n const absolute = resolvedReferencedPath(path)\n onOpenReferencedPath?.(absolute, line)\n }\n\n function previewReferencedPath(path: string, line?: number, anchor?: DOMRect) {\n const absolute = resolvedReferencedPath(path)\n onPreviewReferencedPath?.(absolute, line, anchor)\n }\n\n function handleReact(emoji: string) {\n reactToMessage(msg, emoji)\n setShowQuickReact(false)\n }\n\n function handleBubblePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button !== 0) return\n pointerStartRef.current = { x: e.clientX, y: e.clientY }\n suppressBubbleClickRef.current = false\n }\n\n function handleBubblePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const start = pointerStartRef.current\n if (!start) return\n if (Math.abs(e.clientX - start.x) > 3 || Math.abs(e.clientY - start.y) > 3) {\n suppressBubbleClickRef.current = true\n }\n }\n\n function handleBubbleClick(e: React.MouseEvent<HTMLDivElement>) {\n if (isReactionEvent(msg) || isOutbound) return\n const target = e.target as HTMLElement\n if (target.closest('a,button,input,textarea,select,[role=\"button\"]')) return\n if (suppressBubbleClickRef.current || hasActiveTextSelection()) {\n suppressBubbleClickRef.current = false\n return\n }\n setShowQuickReact((v) => !v)\n }\n\n const hasAvatar = showAvatar && !isReactionEvent(msg)\n\n return (\n <div data-msg-id={msg.id} className={cn('group/msg flex mb-3 items-end', isOutbound ? 'justify-end' : 'justify-start', hasAvatar && 'gap-1.5')}>\n {!isOutbound && hasAvatar && <ConnectedBubbleAvatar agentId={peer} onOpen={openAgentDetail} />}\n <div ref={bubbleRef} className={cn('relative', hasAvatar ? 'max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]' : 'max-w-[85%] md:max-w-[75%]')}>\n {showPlayButton && (\n <button\n type=\"button\"\n title={isTtsPlaying ? 'Stop playback' : 'Play aloud'}\n onClick={togglePlay}\n className={cn(\n 'absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition',\n isTtsPlaying\n ? 'border-primary/50 text-primary opacity-100'\n // Hide-until-hover only on hover-capable devices; touch screens (iPad)\n // have no hover, so keep the button visible there instead of opacity-0.\n : 'border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100'\n )}\n >\n {isTtsPlaying ? <CircleStop className=\"h-3.5 w-3.5 animate-pulse\" /> : <Volume2 className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <div\n className={cn(\n 'rounded-2xl px-3.5 py-2 text-sm select-text',\n !isOutbound && !isReactionEvent(msg) && 'cursor-pointer',\n isOutbound\n ? 'bg-primary text-primary-foreground rounded-br-sm'\n : 'bg-card ring-1 ring-foreground/10 rounded-bl-sm'\n )}\n onPointerDown={handleBubblePointerDown}\n onPointerMove={handleBubblePointerMove}\n onClick={handleBubbleClick}\n >\n {isOutbound && multiHuman && (\n <p className={cn('text-[11px] font-semibold mb-0.5 text-right', humanAccentClass(msg.from))}>\n {humanDisplayName(msg.from, usersById)}\n </p>\n )}\n {msg.subject && (\n <p className={cn('text-xs font-semibold mb-1', isOutbound ? 'text-primary-foreground/70' : 'text-muted-foreground')}>\n {msg.subject}\n </p>\n )}\n <FormattedBody\n text={body}\n rawBody={msg.body}\n issueRepo={peerIssueRepo}\n className=\"leading-relaxed\"\n onPrimary={isOutbound}\n onOpenPath={openReferencedPath}\n onPreviewPath={previewReferencedPath}\n onPreviewPathEnd={onPreviewReferencedPathEnd}\n resolvePathTitle={(path, line) => `${resolvedReferencedPath(path)}${line ? `:${line}` : ''}`}\n />\n {attachments.map((attachment) => (\n <AttachmentPreview key={`${msg.id}-${attachment.artifactId}-${attachment.role || ''}`} attachment={attachment} outbound={isOutbound} />\n ))}\n <p className={cn('text-xs mt-1 flex items-center gap-1.5', isOutbound ? 'justify-end text-primary-foreground/60' : 'justify-end text-muted-foreground')}>\n <span className=\"opacity-40\">#{msg.id}</span>\n <span>{time}</span>\n {receipt && ReceiptIcon && (\n <span\n className={cn(\n 'inline-flex items-center gap-1',\n receipt.tone === 'ok' && 'text-emerald-300',\n receipt.tone === 'warn' && 'text-amber-200',\n receipt.tone === 'danger' && 'text-red-200'\n )}\n title={receipt.title}\n >\n <ReceiptIcon className=\"h-3 w-3\" />\n {receipt.label}\n </span>\n )}\n </p>\n </div>\n {!isReactionEvent(msg) && reactions.length > 0 && (\n <div className={cn('flex flex-wrap items-center gap-0.5 -mt-1 relative z-10', isOutbound ? 'justify-end pr-2' : 'justify-start pl-2')}>\n {!isOutbound && <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />}\n {reactions.map((reaction) => (\n <button\n key={reaction.emoji}\n type=\"button\"\n title={reaction.actors.join(', ')}\n onClick={() => handleReact(reaction.emoji)}\n className={cn(\n 'inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors',\n reaction.mine\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted text-foreground'\n )}\n >\n <span>{reaction.emoji}</span>\n {reaction.count > 1 && <span>{reaction.count}</span>}\n </button>\n ))}\n </div>\n )}\n {!isReactionEvent(msg) && !isOutbound && reactions.length === 0 && (\n <div className=\"absolute -bottom-2.5 left-1 z-20\">\n <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />\n </div>\n )}\n </div>\n {isOutbound && hasAvatar && (\n <span className=\"shrink-0 self-end opacity-60\">\n <UserAvatar user={sendingUser} address={msg.from} distinct={multiHuman} />\n </span>\n )}\n </div>\n )\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessageBubble-D2cnxyTy.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useTtsPlayingKey.ts","../../dashboard/src/components/chat/MessageBubble.tsx"],"sourcesContent":["\nimport { useSyncExternalStore } from 'react'\nimport { voiceTts } from '@/lib/voice'\n\nexport function useTtsPlayingKey(): string | null {\n return useSyncExternalStore(\n (cb) => voiceTts.subscribe(cb),\n () => voiceTts.getPlayingKey(),\n () => null,\n )\n}\n","\nimport { memo, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'\nimport { AlertCircle, ArrowLeft, ArrowRight, ChevronRight, CircleStop, Download, FileText, Loader2, SmilePlus, Volume2, X, ZoomIn } from 'lucide-react'\nimport { voiceTts } from '@/lib/voice'\nimport { useRelayStore } from '@/store'\nimport { FormattedBody } from '@/components/shared/formatted-body'\nimport { AgentLink, InitialPromptBubble } from './TraceView'\nimport { AgentTypeIcon } from '@/components/shared/agent-type-icon'\nimport { Button } from '@/components/ui/button'\nimport { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'\nimport { apiBlob } from '@/lib/api'\nimport { displayTarget, humanAccentClass, humanDisplayName, messageBody, resolveUser } from '@/lib/display'\nimport { isHumanAgentId } from 'agent-relay-sdk/types'\nimport { UserAvatar } from '@/components/shared/user-avatar'\nimport type { Agent, AttachmentRef, Message } from '@/types'\nimport { cn } from '@/lib/utils'\nimport type { ReferencedPathInfo } from './types'\nimport {\n chatTimestamp,\n groupedReactions,\n hasActiveTextSelection,\n isReactionEvent,\n messageAttachments,\n outboundReceipt,\n QUICK_REACTIONS,\n resolveRelativePath,\n} from './helpers'\nimport { useTtsPlayingKey } from './hooks/useTtsPlayingKey'\n\n// Spawn prompts (origin:\"provider\") are system-initiated — reuse InitialPromptBubble's\n// panel style rather than masquerading as an operator prompt bubble (Tier A, #420).\nfunction SpawnPromptBubble({ msg, peer }: { msg: Message; peer: string }) {\n const issueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n return <InitialPromptBubble msg={msg} issueRepo={issueRepo} />\n}\n\n// Small tappable sender avatar placed at the edge of a bubble (Tier B, #420).\n// Uses AgentTypeIcon so provider icon resolution stays in one place (DRY ratchet).\nfunction BubbleAvatar({ agent, onClick }: { agent: Agent | null; onClick?: () => void }) {\n const canClick = Boolean(onClick && agent)\n return (\n <button\n type=\"button\"\n title={agent ? (agent.label || agent.name || agent.id) : undefined}\n onClick={onClick}\n disabled={!canClick}\n className={cn('shrink-0 self-end transition-opacity', canClick ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={agent} />\n </button>\n )\n}\n\n// Subscribes to the agent store independently so MessageBubble's text body\n// doesn't re-render (and lose text selection) when agentsById[peer] gets a new\n// reference from a lastSeen/status poll update (#691).\nfunction ConnectedBubbleAvatar({ agentId, onOpen }: { agentId: string; onOpen: (agent: Agent) => void }) {\n const agent = useRelayStore((s) => s.agentsById[agentId] ?? null)\n return <BubbleAvatar agent={agent} onClick={agent ? () => onOpen(agent) : undefined} />\n}\n\nfunction AttachmentPreview({ attachment, outbound }: { attachment: AttachmentRef; outbound: boolean }) {\n const isImage = attachment.kind === 'image'\n const title = attachment.title || attachment.artifactId\n const [objectUrl, setObjectUrl] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [lightboxOpen, setLightboxOpen] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n\n async function load() {\n setLoading(true)\n setError(null)\n setObjectUrl(null)\n try {\n const blob = await apiBlob(`/artifacts/${encodeURIComponent(attachment.artifactId)}/content`)\n if (cancelled) return\n url = URL.createObjectURL(blob)\n setObjectUrl(url)\n } catch (e) {\n if (!cancelled) setError((e as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n\n void load()\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [attachment.artifactId])\n\n function download() {\n if (!objectUrl) return\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = title\n document.body.appendChild(a)\n a.click()\n a.remove()\n }\n\n if (isImage) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => objectUrl && setLightboxOpen(true)}\n disabled={!objectUrl}\n className=\"group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative\"\n >\n {objectUrl ? (\n <>\n <img src={objectUrl} alt={title} loading=\"lazy\" className=\"block max-h-72 max-w-full object-contain\" />\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors\">\n <ZoomIn className=\"w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md\" />\n </span>\n </>\n ) : (\n <span className={cn('flex min-h-20 items-center gap-2 px-3 py-2 text-xs', error ? 'text-red-400' : 'text-muted-foreground')}>\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <AlertCircle className=\"h-4 w-4\" />}\n {error || 'Loading image'}\n </span>\n )}\n </button>\n {objectUrl && (\n <Dialog open={lightboxOpen} onOpenChange={setLightboxOpen}>\n <DialogContent className=\"max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden\" showCloseButton={false}>\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <div className=\"relative flex items-center justify-center min-h-[200px]\">\n <img src={objectUrl} alt={title} className=\"max-w-[93vw] max-h-[90vh] object-contain\" />\n <div className=\"absolute top-2 right-2 flex gap-1\">\n <button type=\"button\" onClick={download} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Download\">\n <Download className=\"w-4 h-4\" />\n </button>\n <button type=\"button\" onClick={() => setLightboxOpen(false)} className=\"rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors\" title=\"Close\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n </>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={download}\n disabled={!objectUrl}\n className={cn(\n 'mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70',\n outbound ? 'border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15' : 'border-border bg-background hover:bg-muted'\n )}\n >\n {loading ? <Loader2 className=\"h-4 w-4 shrink-0 animate-spin\" /> : error ? <AlertCircle className=\"h-4 w-4 shrink-0 text-red-400\" /> : <FileText className=\"h-4 w-4 shrink-0\" />}\n <span className={cn('min-w-0 flex-1 truncate', error && 'text-red-400')}>{error || title}</span>\n <Download className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n )\n}\n\nfunction AddReaction({ open, onToggle, onReact }: { open: boolean; onToggle: () => void; onReact: (emoji: string) => void }) {\n return (\n <div className=\"relative shrink-0\">\n <button\n type=\"button\"\n title=\"Add reaction\"\n onClick={onToggle}\n className={cn(\n 'inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground',\n open ? 'opacity-100' : 'opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto'\n )}\n >\n <SmilePlus className=\"h-3 w-3\" />\n </button>\n {open && (\n <div className=\"absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md\">\n {QUICK_REACTIONS.map((emoji) => (\n <button\n key={emoji}\n type=\"button\"\n title={`React ${emoji}`}\n onClick={() => onReact(emoji)}\n className=\"inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted\"\n >\n {emoji}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// Inter-agent (peer↔peer) messages render as a compact, foldable one-liner — distinct\n// from the user↔agent bubbles, never persisted-open (#508). The other party is shown via\n// AgentLink (label-preferred + clickable, #522); fan-out targets (broadcast/tag:/cap:/…)\n// fall back to a plain displayTarget label since they don't resolve to one agent.\nfunction PeerMessageLine({ msg, peer }: { msg: Message; peer: string }) {\n const [expanded, setExpanded] = useState(false)\n const agentsById = useRelayStore((s) => s.agentsById)\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const isOutbound = msg.from === peer\n const otherParty = (isOutbound ? msg.to : msg.from) || ''\n // Per-project #NNN linkify (#533/#543): outbound folded lines use the selected\n // peer's repo; inbound inter-agent lines use the originating agent's repo.\n const issueRepo = agentsById[isOutbound ? peer : otherParty]?.issueRepo ?? agentsById[peer]?.issueRepo\n const isSelector = !otherParty || otherParty === 'broadcast' || otherParty.includes(':')\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const DirIcon = isOutbound ? ArrowRight : ArrowLeft\n const senderAgent = agentsById[msg.from ?? ''] ?? null\n function openSenderDetail(e: ReactMouseEvent<HTMLButtonElement>) {\n e.stopPropagation()\n if (senderAgent) openAgentDetail(senderAgent)\n }\n\n return (\n <div data-msg-id={msg.id} className=\"flex justify-center mb-1.5\">\n <div className=\"w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1\">\n <div className=\"flex items-center gap-1.5 text-xs cursor-pointer select-none\" onClick={() => setExpanded((v) => !v)}>\n <ChevronRight className={cn('h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform', expanded && 'rotate-90')} />\n <button\n type=\"button\"\n title={senderAgent ? (senderAgent.label || senderAgent.name || senderAgent.id) : undefined}\n disabled={!senderAgent}\n onClick={openSenderDetail}\n className={cn('shrink-0 transition-opacity', senderAgent ? 'cursor-pointer hover:opacity-80' : 'cursor-default opacity-60')}\n >\n <AgentTypeIcon agent={senderAgent} className=\"!w-4 !h-4 !rounded-sm opacity-70\" />\n </button>\n <DirIcon className=\"h-3 w-3 shrink-0 text-muted-foreground/70\" />\n <span className=\"shrink-0 text-muted-foreground\">Message {isOutbound ? 'to' : 'from'}</span>\n {isSelector\n ? <span className=\"shrink-0 font-medium text-muted-foreground\">{displayTarget(otherParty, agentsById)}</span>\n : <AgentLink agentId={otherParty} className=\"shrink-0 px-0.5 text-foreground/80\" noIcon />}\n {!expanded && <span className=\"min-w-0 flex-1 truncate text-muted-foreground/50\">{body}</span>}\n <span className=\"ml-auto shrink-0 text-muted-foreground/40\">{time}</span>\n </div>\n {expanded && (\n <div className=\"mt-1.5 pl-5\">\n <FormattedBody text={body} rawBody={msg.body} issueRepo={issueRepo} className=\"text-sm leading-relaxed text-foreground/90\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport const MessageBubble = memo(function MessageBubble({\n msg,\n peer,\n showAvatar = false,\n onOpenReferencedPath,\n onPreviewReferencedPath,\n onPreviewReferencedPathEnd,\n}: {\n msg: Message\n peer: string\n /** Show the sender avatar on this bubble (last in a consecutive same-sender run). */\n showAvatar?: boolean\n onOpenReferencedPath?: (path: string, line?: number, type?: ReferencedPathInfo['type']) => void\n onPreviewReferencedPath?: (path: string, line?: number, anchor?: DOMRect) => void\n onPreviewReferencedPathEnd?: () => void\n}) {\n // Inter-agent (peer↔peer) messages get a folded one-liner instead of a bubble (#508).\n // This early-return precedes all hooks, which is safe here: the timeline keys each\n // MessageBubble by msg id (VirtualTimeline.timelineEntryKey), and from/to are immutable\n // for a given id — so a mounted instance never flips between the two render paths.\n // De-singletoned human addressing (#390): any human side — legacy bare `'user'` or a\n // per-user `user:<id>` sink — renders as a user↔agent bubble; only agent↔agent stays a\n // folded peer line. isHumanAgentId keeps multi-human threads from collapsing to peer lines.\n if (!isHumanAgentId(msg.from) && !isHumanAgentId(msg.to)) {\n return <PeerMessageLine msg={msg} peer={peer} />\n }\n // Spawn prompts (kind:\"session\", type:\"prompt\", origin:\"provider\") are system-initiated\n // and must NOT masquerade as operator-typed prompts. Render with the InitialPromptBubble\n // panel style instead (#420 Tier A). Immutable payload — safe as a pre-hook early return.\n const sessionPayload = msg.kind === 'session'\n ? (msg.payload?.session as { type?: string; origin?: string } | undefined)\n : undefined\n if (sessionPayload?.type === 'prompt' && sessionPayload?.origin === 'provider') {\n return <SpawnPromptBubble msg={msg} peer={peer} />\n }\n const isOutbound = isHumanAgentId(msg.from)\n const reactToMessage = useRelayStore((s) => s.reactToMessage)\n const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled)\n const ttsPlayingKey = useTtsPlayingKey()\n const peerCwd = useRelayStore((s) => {\n const cwd = s.agentsById[peer]?.meta?.cwd\n return typeof cwd === 'string' ? cwd : ''\n })\n // Per-project #NNN linkify (#533): the peer agent's resolved project repo.\n const peerIssueRepo = useRelayStore((s) => s.agentsById[peer]?.issueRepo)\n // Human attribution (#390): resolve which user sent an outbound bubble, and whether more\n // than one human exists. With the single seeded user:default, multiHuman is false and the\n // name tag / colored avatar stay off — the bubble renders exactly as before.\n const usersById = useRelayStore((s) => s.usersById)\n const multiHuman = useRelayStore((s) => s.users.length > 1)\n const sendingUser = isOutbound ? resolveUser(msg.from, usersById) : undefined\n const openAgentDetail = useRelayStore((s) => s.openAgentDetail)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const [showQuickReact, setShowQuickReact] = useState(false)\n const bubbleRef = useRef<HTMLDivElement>(null)\n const pointerStartRef = useRef<{ x: number; y: number } | null>(null)\n const suppressBubbleClickRef = useRef(false)\n const body = messageBody(msg)\n const time = chatTimestamp(msg.occurredAt ?? (msg.createdAt as string))\n const attachments = messageAttachments(msg)\n const reactions = groupedReactions(msg)\n const receipt = isOutbound ? outboundReceipt(msg, peer) : null\n const ReceiptIcon = receipt?.icon\n // On-demand TTS: replay any agent response through the same Kokoro/browser\n // pipeline. Only when the speaker is on, and only for inbound non-reaction text.\n const isTtsPlaying = ttsPlayingKey === String(msg.id)\n const showPlayButton = voiceTtsEnabled && !isOutbound && !isReactionEvent(msg) && body.trim().length > 0\n function togglePlay(e: React.MouseEvent) {\n e.stopPropagation()\n if (isTtsPlaying) voiceTts.bargeIn()\n else voiceTts.speak(msg.body, String(msg.id))\n }\n useEffect(() => {\n if (!showQuickReact) return\n function dismiss(e: MouseEvent | TouchEvent) {\n if (hasActiveTextSelection()) return\n if (bubbleRef.current && !bubbleRef.current.contains(e.target as Node)) setShowQuickReact(false)\n }\n document.addEventListener('mousedown', dismiss)\n document.addEventListener('touchstart', dismiss)\n return () => { document.removeEventListener('mousedown', dismiss); document.removeEventListener('touchstart', dismiss) }\n }, [showQuickReact])\n\n function resolvedReferencedPath(path: string): string {\n return resolveRelativePath(peerCwd || '/', path)\n }\n\n function openReferencedPath(path: string, line?: number) {\n const absolute = resolvedReferencedPath(path)\n onOpenReferencedPath?.(absolute, line)\n }\n\n function previewReferencedPath(path: string, line?: number, anchor?: DOMRect) {\n const absolute = resolvedReferencedPath(path)\n onPreviewReferencedPath?.(absolute, line, anchor)\n }\n\n function handleReact(emoji: string) {\n reactToMessage(msg, emoji)\n setShowQuickReact(false)\n }\n\n function handleBubblePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button !== 0) return\n pointerStartRef.current = { x: e.clientX, y: e.clientY }\n suppressBubbleClickRef.current = false\n }\n\n function handleBubblePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const start = pointerStartRef.current\n if (!start) return\n if (Math.abs(e.clientX - start.x) > 3 || Math.abs(e.clientY - start.y) > 3) {\n suppressBubbleClickRef.current = true\n }\n }\n\n function handleBubbleClick(e: React.MouseEvent<HTMLDivElement>) {\n if (isReactionEvent(msg) || isOutbound) return\n const target = e.target as HTMLElement\n if (target.closest('a,button,input,textarea,select,[role=\"button\"]')) return\n if (suppressBubbleClickRef.current || hasActiveTextSelection()) {\n suppressBubbleClickRef.current = false\n return\n }\n setShowQuickReact((v) => !v)\n }\n\n const hasAvatar = showAvatar && !isReactionEvent(msg)\n\n return (\n <div data-msg-id={msg.id} className={cn('group/msg flex mb-3 items-end', isOutbound ? 'justify-end' : 'justify-start', hasAvatar && 'gap-1.5')}>\n {!isOutbound && hasAvatar && <ConnectedBubbleAvatar agentId={peer} onOpen={openAgentDetail} />}\n <div ref={bubbleRef} className={cn('relative', hasAvatar ? 'max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]' : 'max-w-[85%] md:max-w-[75%]')}>\n {showPlayButton && (\n <button\n type=\"button\"\n title={isTtsPlaying ? 'Stop playback' : 'Play aloud'}\n onClick={togglePlay}\n className={cn(\n 'absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition',\n isTtsPlaying\n ? 'border-primary/50 text-primary opacity-100'\n // Hide-until-hover only on hover-capable devices; touch screens (iPad)\n // have no hover, so keep the button visible there instead of opacity-0.\n : 'border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100'\n )}\n >\n {isTtsPlaying ? <CircleStop className=\"h-3.5 w-3.5 animate-pulse\" /> : <Volume2 className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <div\n className={cn(\n 'rounded-2xl px-3.5 py-2 text-sm select-text',\n !isOutbound && !isReactionEvent(msg) && 'cursor-pointer',\n isOutbound\n ? 'bg-primary text-primary-foreground rounded-br-sm'\n : 'bg-card ring-1 ring-foreground/10 rounded-bl-sm'\n )}\n onPointerDown={handleBubblePointerDown}\n onPointerMove={handleBubblePointerMove}\n onClick={handleBubbleClick}\n >\n {isOutbound && multiHuman && (\n <p className={cn('text-[11px] font-semibold mb-0.5 text-right', humanAccentClass(msg.from))}>\n {humanDisplayName(msg.from, usersById)}\n </p>\n )}\n {msg.subject && (\n <p className={cn('text-xs font-semibold mb-1', isOutbound ? 'text-primary-foreground/70' : 'text-muted-foreground')}>\n {msg.subject}\n </p>\n )}\n <FormattedBody\n text={body}\n rawBody={msg.body}\n issueRepo={peerIssueRepo}\n className=\"leading-relaxed\"\n onPrimary={isOutbound}\n onOpenPath={openReferencedPath}\n onPreviewPath={previewReferencedPath}\n onPreviewPathEnd={onPreviewReferencedPathEnd}\n resolvePathTitle={(path, line) => `${resolvedReferencedPath(path)}${line ? `:${line}` : ''}`}\n />\n {attachments.map((attachment) => (\n <AttachmentPreview key={`${msg.id}-${attachment.artifactId}-${attachment.role || ''}`} attachment={attachment} outbound={isOutbound} />\n ))}\n <p className={cn('text-xs mt-1 flex items-center gap-1.5', isOutbound ? 'justify-end text-primary-foreground/60' : 'justify-end text-muted-foreground')}>\n <span className=\"opacity-40\">#{msg.id}</span>\n <span>{time}</span>\n {receipt && ReceiptIcon && (\n <span\n className={cn(\n 'inline-flex items-center gap-1',\n receipt.tone === 'ok' && 'text-emerald-300',\n receipt.tone === 'warn' && 'text-amber-200',\n receipt.tone === 'danger' && 'text-red-200'\n )}\n title={receipt.title}\n >\n <ReceiptIcon className=\"h-3 w-3\" />\n {receipt.label}\n </span>\n )}\n </p>\n </div>\n {!isReactionEvent(msg) && reactions.length > 0 && (\n <div className={cn('flex flex-wrap items-center gap-0.5 -mt-1 relative z-10', isOutbound ? 'justify-end pr-2' : 'justify-start pl-2')}>\n {!isOutbound && <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />}\n {reactions.map((reaction) => (\n <button\n key={reaction.emoji}\n type=\"button\"\n title={reaction.actors.join(', ')}\n onClick={() => handleReact(reaction.emoji)}\n className={cn(\n 'inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors',\n reaction.mine\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted text-foreground'\n )}\n >\n <span>{reaction.emoji}</span>\n {reaction.count > 1 && <span>{reaction.count}</span>}\n </button>\n ))}\n </div>\n )}\n {!isReactionEvent(msg) && !isOutbound && reactions.length === 0 && (\n <div className=\"absolute -bottom-2.5 left-1 z-20\">\n <AddReaction open={showQuickReact} onToggle={() => setShowQuickReact((v) => !v)} onReact={handleReact} />\n </div>\n )}\n </div>\n {isOutbound && hasAvatar && (\n <span className=\"shrink-0 self-end opacity-60\">\n <UserAvatar user={sendingUser} address={msg.from} distinct={multiHuman} />\n </span>\n )}\n </div>\n )\n})\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{$n as t,Nt as n,Qn as r,Sn as i,Tn as a,gr as o,ht as s,ir as c,rr as l,tr as u}from"./lucide-react-CoU_Xts2.js";import{i as d,r as f,t as p}from"./store-Dl9Z8YUS.js";import{Q as m}from"./display-aTnwsqQh.js";import{t as h}from"./task-chip-BHM-e3I8.js";import{X as g}from"./index-De1tR7J4.js";var _=e(o(),1),v=`(max-width: 767px)`;function y(e){if(typeof window>`u`)return()=>{};let t=window.matchMedia(v);return t.addEventListener(`change`,e),()=>t.removeEventListener(`change`,e)}function b(){return(0,_.useSyncExternalStore)(y,()=>typeof window<`u`?window.matchMedia(v).matches:!1,()=>!1)}var x=l(),S=196,C=84,w=52,T=16,E=14,D=[`landed`,`in-progress`,`pending`,`blocked`],O={landed:{label:`landed`,dot:`bg-emerald-400`,accent:`border-l-emerald-400`,text:`text-emerald-300`,wire:`#34d399`},"in-progress":{label:`in progress`,dot:`bg-sky-400`,accent:`border-l-sky-400`,text:`text-sky-300`,wire:`#38bdf8`},pending:{label:`pending`,dot:`bg-amber-400`,accent:`border-l-amber-400`,text:`text-amber-300`,wire:`#fbbf24`},blocked:{label:`blocked`,dot:`bg-red-400`,accent:`border-l-red-400`,text:`text-red-300`,wire:`#f87171`}},k=new Set([...m,`completed`]);function A(e){return k.has(e.status)}function j(e,t){return t?e:e.filter(e=>!A(e))}function M(e){return e?e.label||(e.policyName?`policy:${e.policyName}`:e.agentId||``):``}function N(e){let t=new Map(e.nodes.map(e=>[e.id,e])),n=new Map(e.nodes.map((e,t)=>[e.id,t])),r=new Map,i=new Map(e.nodes.map(e=>[e.id,0]));for(let n of e.edges)!t.has(n.from)||!t.has(n.to)||(r.set(n.from,[...r.get(n.from)??[],n.to]),i.set(n.to,(i.get(n.to)??0)+1));let a=new Map(e.nodes.map(e=>[e.id,0])),o=e.nodes.filter(e=>(i.get(e.id)??0)===0).map(e=>e.id),s=new Set;for(;o.length;){let e=o.shift();s.add(e);let t=a.get(e)??0;for(let n of r.get(e)??[])a.set(n,Math.max(a.get(n)??0,t+1)),i.set(n,(i.get(n)??0)-1),(i.get(n)??0)===0&&o.push(n)}let c=Math.max(0,...Array.from(a.values()))+1;for(let t of e.nodes)s.has(t.id)||a.set(t.id,c++);let l=new Map;for(let t of e.nodes){let e=a.get(t.id)??0;l.set(e,[...l.get(e)??[],t])}let u=[...l.entries()].sort(([e],[t])=>e-t),d=Math.max(1,...u.map(([,e])=>e.length)),f=C+T,p=[];u.forEach(([e,t],r)=>{let i=[...t].sort((e,t)=>(e.lane||``).localeCompare(t.lane||``)||(n.get(e.id)??0)-(n.get(t.id)??0)),a=(d-i.length)*f/2;i.forEach((t,n)=>{p.push({node:t,layer:e,x:E+r*(S+w),y:E+a+n*f})})});let m=u.length;return{nodes:p,width:Math.max(320,E*2+m*S+Math.max(0,m-1)*w),height:Math.max(C+E*2,E*2+d*C+Math.max(0,d-1)*T)}}function P(e,t){let n=e.x+S,r=e.y+C/2,i=t.x,a=t.y+C/2,o=n+Math.max(20,(i-n)/2);return`M ${n} ${r} C ${o} ${r}, ${o} ${a}, ${i} ${a}`}function F({threadId:e,teamId:o,channel:s}){let c=o?d(o,s):e?f(e,s):``,l=p(e=>o?e.plansByTeam[c]:c?e.plansByThread[c]:null),m=p(e=>e.fetchPlan),v=p(e=>e.setPlanArchived),y=p(e=>e.planCollapsed),w=p(e=>e.setPlanCollapsed),T=p(e=>e.issueRefsById),E=p(e=>e.deliverableTasks),k=p(e=>e.openTaskDetailById),A=b(),[j,F]=(0,_.useState)(!1),I=y??A;(0,_.useEffect)(()=>{if(o){m({teamId:o,channel:s});return}e&&m({threadId:e,channel:s})},[s,m,o,e]);let L=(0,_.useMemo)(()=>l?N(l):null,[l]),R=(0,_.useMemo)(()=>new Map(L?.nodes.map(e=>[e.node.id,e])??[]),[L]),z=(0,_.useMemo)(()=>{if(!l)return[];let e=new Map;for(let t of l.nodes)e.set(t.status,(e.get(t.status)??0)+1);return D.filter(t=>e.has(t)).map(t=>({status:t,count:e.get(t)??0}))},[l]);if(!l||!L)return null;if(l.archived&&!j)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`button`,{type:`button`,onClick:()=>F(!0),className:`flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground`,"aria-expanded":`false`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3`})}),(0,x.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider`,children:`Show archived plan (1)`}),(0,x.jsx)(a,{className:`h-3.5 w-3.5 shrink-0`})]})});if(l.archived)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-muted-foreground`,children:l.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:`Archived plan`})]}),(0,x.jsx)(`span`,{className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground`,children:`archived`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>F(!1),className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground`,"aria-expanded":`true`,children:`Hide`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(l.id,!1),title:`Unarchive plan`,"aria-label":`Unarchive plan`,className:`flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(t,{className:`h-3.5 w-3.5`})})]})});let B=l.nodes.length,V=z.find(e=>e.status===`landed`)?.count??0,H=`${B} node${B===1?``:`s`}${V>0?` · ${V} landed`:``}`;return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`div`,{className:u(`flex min-w-0 items-center gap-2`,!I&&`mb-2`),children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex min-w-0 flex-1 items-center gap-2 text-left`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(n,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-foreground`,children:l.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:I?H:l.objective||H})]})]}),z.length>0&&(0,x.jsx)(`div`,{className:`hidden shrink-0 items-center gap-2.5 sm:flex`,children:z.map(({status:e,count:t})=>(0,x.jsxs)(`span`,{className:`flex items-center gap-1 text-[10px] text-muted-foreground`,children:[(0,x.jsx)(`span`,{className:u(`h-1.5 w-1.5 rounded-full`,O[e].dot)}),(0,x.jsx)(`span`,{className:u(`font-medium tabular-nums`,O[e].text),children:t}),(0,x.jsx)(`span`,{children:O[e].label})]},e))}),(0,x.jsxs)(`span`,{className:`shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground`,children:[`v`,l.version]}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(l.id,!0),title:`Archive plan`,"aria-label":`Archive plan`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:I?(0,x.jsx)(a,{className:`h-3.5 w-3.5`}):(0,x.jsx)(i,{className:`h-3.5 w-3.5`})})]}),I?null:l.nodes.length===0?(0,x.jsx)(`div`,{className:`rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground`,children:`No nodes yet — the plan graph fills in as the team is composed.`}):(0,x.jsx)(`div`,{className:`max-h-[min(46vh,360px)] overflow-auto`,children:(0,x.jsxs)(`div`,{className:`relative`,style:{width:L.width,height:L.height},children:[(0,x.jsxs)(`svg`,{className:`pointer-events-none absolute inset-0`,width:L.width,height:L.height,"aria-hidden":`true`,children:[(0,x.jsx)(`defs`,{children:D.map(e=>(0,x.jsx)(`marker`,{id:`plan-arrow-${e}`,viewBox:`0 0 8 8`,refX:`6.5`,refY:`4`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,x.jsx)(`path`,{d:`M0 0 L8 4 L0 8 z`,fill:O[e].wire})},e))}),l.edges.map(e=>{let t=R.get(e.from),n=R.get(e.to);if(!t||!n)return null;let r=O[t.node.status];return(0,x.jsx)(`path`,{d:P(t,n),stroke:r.wire,strokeOpacity:.7,strokeWidth:2,fill:`none`,strokeLinecap:`round`,markerEnd:`url(#plan-arrow-${t.node.status})`},`${e.from}->${e.to}`)})]}),L.nodes.map(({node:e,x:t,y:n})=>{let r=O[e.status],i=M(e.ownerRef),a=e.refs?.issueId?T[e.refs.issueId]:null,o=e.refs?.taskId?parseInt(e.refs.taskId,10):null,s=o==null?void 0:E.find(e=>e.id===o)?.title;return(0,x.jsxs)(`article`,{className:u(`group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2`,`shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background`,r.accent),style:{left:t,top:n,width:S,height:C},title:e.scopeBlurb||e.label,children:[(0,x.jsx)(`div`,{className:`truncate text-xs font-semibold leading-tight text-foreground`,children:e.label}),(e.scopeBlurb||e.id)&&(0,x.jsx)(`div`,{className:`truncate text-[10px] leading-tight text-muted-foreground`,children:e.scopeBlurb||e.id}),(a||o!=null)&&(0,x.jsxs)(`div`,{className:`flex min-w-0 flex-wrap gap-1`,children:[a&&(0,x.jsx)(g,{issueRef:a,compact:!0,className:`max-w-full`}),o!=null&&(0,x.jsx)(h,{id:o,title:s,onClick:()=>k(o),className:`max-w-full`})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center justify-between gap-1.5`,children:[(0,x.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1.5`,children:[(0,x.jsx)(`span`,{className:u(`h-1.5 w-1.5 shrink-0 rounded-full`,r.dot)}),(0,x.jsx)(`span`,{className:u(`text-[10px] font-medium`,r.text),children:r.label})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1.5`,children:[i&&(0,x.jsx)(`span`,{className:`truncate text-[10px] text-muted-foreground`,children:i}),e.lane&&(0,x.jsx)(`span`,{className:`shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground`,children:e.lane})]})]})]},e.id)})]})})]})}function I({agentId:e}){let t=p(e=>e.openTaskDetailById),n=p(e=>e.sessionTasksCollapsed),r=p(e=>e.setSessionTasksCollapsed),[o,l]=(0,_.useState)([]),[d,f]=(0,_.useState)(!1);if((0,_.useEffect)(()=>{c(`GET`,`/task-entities?createdBy=`+encodeURIComponent(e)).then(e=>l(e)).catch(()=>{})},[e]),o.length===0)return null;let m=o.filter(A).length,g=j(o,d);return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>r(!n),"aria-expanded":!n,"aria-label":n?`Expand session tasks`:`Collapse session tasks`,className:u(`flex w-full min-w-0 items-center gap-2 text-left`,!n&&`mb-1.5`),children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(s,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`span`,{className:`flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground`,children:[`Tasks from this session (`,g.length,`)`]}),(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:n?(0,x.jsx)(a,{className:`h-3.5 w-3.5`}):(0,x.jsx)(i,{className:`h-3.5 w-3.5`})})]}),!n&&(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:g.map(e=>(0,x.jsx)(h,{id:e.id,title:e.title,status:e.status,onClick:()=>t(e.id),compact:!0},e.id))}),m>0&&(0,x.jsx)(`button`,{type:`button`,onClick:()=>f(!d),className:`mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground`,"aria-pressed":d,children:d?`Hide ${m} closed`:`+ ${m} closed`})]})]})}export{I as n,b as r,F as t};
2
- //# sourceMappingURL=PlanGraphPanel-iAXvkZKW.js.map
1
+ import{r as e}from"./chunk-CilyBKbf.js";import{$n as t,Nt as n,Qn as r,Sn as i,Tn as a,gr as o,ht as s,ir as c,rr as l,tr as u}from"./lucide-react-NXHPIMsX.js";import{i as d,r as f,t as p}from"./store-D-Frk_EC.js";import{Q as m}from"./display-aTnwsqQh.js";import{t as h}from"./task-chip-CeujtXYT.js";import{X as g}from"./index-Bhy_jnX4.js";var _=e(o(),1),v=`(max-width: 767px)`;function y(e){if(typeof window>`u`)return()=>{};let t=window.matchMedia(v);return t.addEventListener(`change`,e),()=>t.removeEventListener(`change`,e)}function b(){return(0,_.useSyncExternalStore)(y,()=>typeof window<`u`?window.matchMedia(v).matches:!1,()=>!1)}var x=l(),S=196,C=84,w=52,T=16,E=14,D=[`landed`,`in-progress`,`pending`,`blocked`],O={landed:{label:`landed`,dot:`bg-emerald-400`,accent:`border-l-emerald-400`,text:`text-emerald-300`,wire:`#34d399`},"in-progress":{label:`in progress`,dot:`bg-sky-400`,accent:`border-l-sky-400`,text:`text-sky-300`,wire:`#38bdf8`},pending:{label:`pending`,dot:`bg-amber-400`,accent:`border-l-amber-400`,text:`text-amber-300`,wire:`#fbbf24`},blocked:{label:`blocked`,dot:`bg-red-400`,accent:`border-l-red-400`,text:`text-red-300`,wire:`#f87171`}},k=new Set([...m,`completed`]);function A(e){return k.has(e.status)}function j(e,t){return t?e:e.filter(e=>!A(e))}function M(e){return e?e.label||(e.policyName?`policy:${e.policyName}`:e.agentId||``):``}function N(e){let t=new Map(e.nodes.map(e=>[e.id,e])),n=new Map(e.nodes.map((e,t)=>[e.id,t])),r=new Map,i=new Map(e.nodes.map(e=>[e.id,0]));for(let n of e.edges)!t.has(n.from)||!t.has(n.to)||(r.set(n.from,[...r.get(n.from)??[],n.to]),i.set(n.to,(i.get(n.to)??0)+1));let a=new Map(e.nodes.map(e=>[e.id,0])),o=e.nodes.filter(e=>(i.get(e.id)??0)===0).map(e=>e.id),s=new Set;for(;o.length;){let e=o.shift();s.add(e);let t=a.get(e)??0;for(let n of r.get(e)??[])a.set(n,Math.max(a.get(n)??0,t+1)),i.set(n,(i.get(n)??0)-1),(i.get(n)??0)===0&&o.push(n)}let c=Math.max(0,...Array.from(a.values()))+1;for(let t of e.nodes)s.has(t.id)||a.set(t.id,c++);let l=new Map;for(let t of e.nodes){let e=a.get(t.id)??0;l.set(e,[...l.get(e)??[],t])}let u=[...l.entries()].sort(([e],[t])=>e-t),d=Math.max(1,...u.map(([,e])=>e.length)),f=C+T,p=[];u.forEach(([e,t],r)=>{let i=[...t].sort((e,t)=>(e.lane||``).localeCompare(t.lane||``)||(n.get(e.id)??0)-(n.get(t.id)??0)),a=(d-i.length)*f/2;i.forEach((t,n)=>{p.push({node:t,layer:e,x:E+r*(S+w),y:E+a+n*f})})});let m=u.length;return{nodes:p,width:Math.max(320,E*2+m*S+Math.max(0,m-1)*w),height:Math.max(C+E*2,E*2+d*C+Math.max(0,d-1)*T)}}function P(e,t){let n=e.x+S,r=e.y+C/2,i=t.x,a=t.y+C/2,o=n+Math.max(20,(i-n)/2);return`M ${n} ${r} C ${o} ${r}, ${o} ${a}, ${i} ${a}`}function F({threadId:e,teamId:o,channel:s}){let c=o?d(o,s):e?f(e,s):``,l=p(e=>o?e.plansByTeam[c]:c?e.plansByThread[c]:null),m=p(e=>e.fetchPlan),v=p(e=>e.setPlanArchived),y=p(e=>e.planCollapsed),w=p(e=>e.setPlanCollapsed),T=p(e=>e.issueRefsById),E=p(e=>e.deliverableTasks),k=p(e=>e.openTaskDetailById),A=b(),[j,F]=(0,_.useState)(!1),I=y??A;(0,_.useEffect)(()=>{if(o){m({teamId:o,channel:s});return}e&&m({threadId:e,channel:s})},[s,m,o,e]);let L=(0,_.useMemo)(()=>l?N(l):null,[l]),R=(0,_.useMemo)(()=>new Map(L?.nodes.map(e=>[e.node.id,e])??[]),[L]),z=(0,_.useMemo)(()=>{if(!l)return[];let e=new Map;for(let t of l.nodes)e.set(t.status,(e.get(t.status)??0)+1);return D.filter(t=>e.has(t)).map(t=>({status:t,count:e.get(t)??0}))},[l]);if(!l||!L)return null;if(l.archived&&!j)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`button`,{type:`button`,onClick:()=>F(!0),className:`flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground`,"aria-expanded":`false`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3`})}),(0,x.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider`,children:`Show archived plan (1)`}),(0,x.jsx)(a,{className:`h-3.5 w-3.5 shrink-0`})]})});if(l.archived)return(0,x.jsx)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(r,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-muted-foreground`,children:l.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:`Archived plan`})]}),(0,x.jsx)(`span`,{className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground`,children:`archived`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>F(!1),className:`shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground`,"aria-expanded":`true`,children:`Hide`}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(l.id,!1),title:`Unarchive plan`,"aria-label":`Unarchive plan`,className:`flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(t,{className:`h-3.5 w-3.5`})})]})});let B=l.nodes.length,V=z.find(e=>e.status===`landed`)?.count??0,H=`${B} node${B===1?``:`s`}${V>0?` · ${V} landed`:``}`;return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`div`,{className:u(`flex min-w-0 items-center gap-2`,!I&&`mb-2`),children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex min-w-0 flex-1 items-center gap-2 text-left`,children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(n,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,x.jsx)(`div`,{className:`truncate text-[13px] font-semibold leading-tight text-foreground`,children:l.title||`Plan`}),(0,x.jsx)(`div`,{className:`truncate text-[11px] leading-tight text-muted-foreground`,children:I?H:l.objective||H})]})]}),z.length>0&&(0,x.jsx)(`div`,{className:`hidden shrink-0 items-center gap-2.5 sm:flex`,children:z.map(({status:e,count:t})=>(0,x.jsxs)(`span`,{className:`flex items-center gap-1 text-[10px] text-muted-foreground`,children:[(0,x.jsx)(`span`,{className:u(`h-1.5 w-1.5 rounded-full`,O[e].dot)}),(0,x.jsx)(`span`,{className:u(`font-medium tabular-nums`,O[e].text),children:t}),(0,x.jsx)(`span`,{children:O[e].label})]},e))}),(0,x.jsxs)(`span`,{className:`shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground`,children:[`v`,l.version]}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>void v(l.id,!0),title:`Archive plan`,"aria-label":`Archive plan`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:(0,x.jsx)(r,{className:`h-3.5 w-3.5`})}),(0,x.jsx)(`button`,{type:`button`,onClick:()=>w(!I),"aria-expanded":!I,"aria-label":I?`Expand plan graph`:`Collapse plan graph`,className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:I?(0,x.jsx)(a,{className:`h-3.5 w-3.5`}):(0,x.jsx)(i,{className:`h-3.5 w-3.5`})})]}),I?null:l.nodes.length===0?(0,x.jsx)(`div`,{className:`rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground`,children:`No nodes yet — the plan graph fills in as the team is composed.`}):(0,x.jsx)(`div`,{className:`max-h-[min(46vh,360px)] overflow-auto`,children:(0,x.jsxs)(`div`,{className:`relative`,style:{width:L.width,height:L.height},children:[(0,x.jsxs)(`svg`,{className:`pointer-events-none absolute inset-0`,width:L.width,height:L.height,"aria-hidden":`true`,children:[(0,x.jsx)(`defs`,{children:D.map(e=>(0,x.jsx)(`marker`,{id:`plan-arrow-${e}`,viewBox:`0 0 8 8`,refX:`6.5`,refY:`4`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,x.jsx)(`path`,{d:`M0 0 L8 4 L0 8 z`,fill:O[e].wire})},e))}),l.edges.map(e=>{let t=R.get(e.from),n=R.get(e.to);if(!t||!n)return null;let r=O[t.node.status];return(0,x.jsx)(`path`,{d:P(t,n),stroke:r.wire,strokeOpacity:.7,strokeWidth:2,fill:`none`,strokeLinecap:`round`,markerEnd:`url(#plan-arrow-${t.node.status})`},`${e.from}->${e.to}`)})]}),L.nodes.map(({node:e,x:t,y:n})=>{let r=O[e.status],i=M(e.ownerRef),a=e.refs?.issueId?T[e.refs.issueId]:null,o=e.refs?.taskId?parseInt(e.refs.taskId,10):null,s=o==null?void 0:E.find(e=>e.id===o)?.title;return(0,x.jsxs)(`article`,{className:u(`group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2`,`shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background`,r.accent),style:{left:t,top:n,width:S,height:C},title:e.scopeBlurb||e.label,children:[(0,x.jsx)(`div`,{className:`truncate text-xs font-semibold leading-tight text-foreground`,children:e.label}),(e.scopeBlurb||e.id)&&(0,x.jsx)(`div`,{className:`truncate text-[10px] leading-tight text-muted-foreground`,children:e.scopeBlurb||e.id}),(a||o!=null)&&(0,x.jsxs)(`div`,{className:`flex min-w-0 flex-wrap gap-1`,children:[a&&(0,x.jsx)(g,{issueRef:a,compact:!0,className:`max-w-full`}),o!=null&&(0,x.jsx)(h,{id:o,title:s,onClick:()=>k(o),className:`max-w-full`})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center justify-between gap-1.5`,children:[(0,x.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1.5`,children:[(0,x.jsx)(`span`,{className:u(`h-1.5 w-1.5 shrink-0 rounded-full`,r.dot)}),(0,x.jsx)(`span`,{className:u(`text-[10px] font-medium`,r.text),children:r.label})]}),(0,x.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1.5`,children:[i&&(0,x.jsx)(`span`,{className:`truncate text-[10px] text-muted-foreground`,children:i}),e.lane&&(0,x.jsx)(`span`,{className:`shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground`,children:e.lane})]})]})]},e.id)})]})})]})}function I({agentId:e}){let t=p(e=>e.openTaskDetailById),n=p(e=>e.sessionTasksCollapsed),r=p(e=>e.setSessionTasksCollapsed),[o,l]=(0,_.useState)([]),[d,f]=(0,_.useState)(!1);if((0,_.useEffect)(()=>{c(`GET`,`/task-entities?createdBy=`+encodeURIComponent(e)).then(e=>l(e)).catch(()=>{})},[e]),o.length===0)return null;let m=o.filter(A).length,g=j(o,d);return(0,x.jsxs)(`section`,{className:`shrink-0 border-b border-border bg-card/45 px-3 py-2`,children:[(0,x.jsxs)(`button`,{type:`button`,onClick:()=>r(!n),"aria-expanded":!n,"aria-label":n?`Expand session tasks`:`Collapse session tasks`,className:u(`flex w-full min-w-0 items-center gap-2 text-left`,!n&&`mb-1.5`),children:[(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60`,children:(0,x.jsx)(s,{className:`h-3 w-3 text-muted-foreground`})}),(0,x.jsxs)(`span`,{className:`flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground`,children:[`Tasks from this session (`,g.length,`)`]}),(0,x.jsx)(`span`,{className:`flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground`,children:n?(0,x.jsx)(a,{className:`h-3.5 w-3.5`}):(0,x.jsx)(i,{className:`h-3.5 w-3.5`})})]}),!n&&(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:g.map(e=>(0,x.jsx)(h,{id:e.id,title:e.title,status:e.status,onClick:()=>t(e.id),compact:!0},e.id))}),m>0&&(0,x.jsx)(`button`,{type:`button`,onClick:()=>f(!d),className:`mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground`,"aria-pressed":d,children:d?`Hide ${m} closed`:`+ ${m} closed`})]})]})}export{I as n,b as r,F as t};
2
+ //# sourceMappingURL=PlanGraphPanel-Cnxt-x7R.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlanGraphPanel-iAXvkZKW.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useIsMobile.ts","../../dashboard/src/components/chat/PlanGraphPanel.tsx"],"sourcesContent":["import { useSyncExternalStore } from 'react'\n\n// Reactive narrow-viewport flag. Subscribes to the same `max-width: 767px`\n// breakpoint the chat single-pane layout uses, via useSyncExternalStore — an\n// external-store subscription, not effect-driven derived state — so it stays\n// correct across resize/rotate without a useEffect.\nconst QUERY = '(max-width: 767px)'\n\nfunction subscribe(cb: () => void): () => void {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(QUERY)\n mql.addEventListener('change', cb)\n return () => mql.removeEventListener('change', cb)\n}\n\nexport function useIsMobile(): boolean {\n return useSyncExternalStore(\n subscribe,\n () => (typeof window !== 'undefined' ? window.matchMedia(QUERY).matches : false),\n () => false,\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { Archive, ArchiveRestore, ChevronDown, ChevronUp, GitBranch, ListTodo } from 'lucide-react'\nimport { useRelayStore } from '@/store'\nimport type { Plan, PlanNode, PlanOwnerRef, PlanStatus, Task } from '@/types'\nimport { cn } from '@/lib/utils'\nimport { CLOSED_TASK_STATUSES } from '@/lib/constants'\nimport { planLookupKey, planTeamLookupKey } from '@/store/slices/plans'\nimport { IssueRefChip } from '@/components/shared/issue-ref-chip'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { useIsMobile } from './hooks/useIsMobile'\nimport { api } from '@/lib/api'\n\n// Left-to-right DAG: dependency depth flows along x (sequential gates), nodes\n// that share a depth stack along y (parallel lanes). This keeps the panel SHORT\n// — it's an operator overlay above the chat, not a full view — and lets the\n// gate-vs-lane structure read at a glance. Height is capped + scrollable below.\nconst NODE_W = 196\nconst NODE_H = 84\nconst COL_GAP = 52\nconst ROW_GAP = 16\nconst PAD = 14\n\nconst STATUS_ORDER: PlanStatus[] = ['landed', 'in-progress', 'pending', 'blocked']\n\nconst STATUS_STYLE: Record<\n PlanStatus,\n { label: string; dot: string; accent: string; text: string; wire: string }\n> = {\n landed: { label: 'landed', dot: 'bg-emerald-400', accent: 'border-l-emerald-400', text: 'text-emerald-300', wire: '#34d399' },\n 'in-progress': { label: 'in progress', dot: 'bg-sky-400', accent: 'border-l-sky-400', text: 'text-sky-300', wire: '#38bdf8' },\n pending: { label: 'pending', dot: 'bg-amber-400', accent: 'border-l-amber-400', text: 'text-amber-300', wire: '#fbbf24' },\n blocked: { label: 'blocked', dot: 'bg-red-400', accent: 'border-l-red-400', text: 'text-red-300', wire: '#f87171' },\n}\n\n// CLOSED_TASK_STATUSES (done/failed/canceled) is the shared terminal-status\n// set; 'completed' is a legacy status value some older records still carry.\nconst COMPLETED_TASK_STATUSES = new Set([...CLOSED_TASK_STATUSES, 'completed'])\n\nexport function isCompletedSessionTask(task: Pick<Task, 'status'>): boolean {\n return COMPLETED_TASK_STATUSES.has(task.status)\n}\n\nexport function visibleSessionTasks(tasks: Task[], showCompleted: boolean): Task[] {\n return showCompleted ? tasks : tasks.filter((task) => !isCompletedSessionTask(task))\n}\n\ninterface LayoutNode {\n node: PlanNode\n x: number\n y: number\n layer: number\n}\n\nfunction ownerLabel(owner?: PlanOwnerRef): string {\n if (!owner) return ''\n return owner.label || (owner.policyName ? `policy:${owner.policyName}` : owner.agentId || '')\n}\n\nfunction layerPlan(plan: Plan): { nodes: LayoutNode[]; width: number; height: number } {\n const nodeById = new Map(plan.nodes.map((node) => [node.id, node]))\n const indexById = new Map(plan.nodes.map((node, index) => [node.id, index]))\n const outgoing = new Map<string, string[]>()\n const indegree = new Map(plan.nodes.map((node) => [node.id, 0]))\n for (const edge of plan.edges) {\n if (!nodeById.has(edge.from) || !nodeById.has(edge.to)) continue\n outgoing.set(edge.from, [...(outgoing.get(edge.from) ?? []), edge.to])\n indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1)\n }\n\n const layerById = new Map(plan.nodes.map((node) => [node.id, 0]))\n const queue = plan.nodes\n .filter((node) => (indegree.get(node.id) ?? 0) === 0)\n .map((node) => node.id)\n const visited = new Set<string>()\n\n while (queue.length) {\n const id = queue.shift()!\n visited.add(id)\n const fromLayer = layerById.get(id) ?? 0\n for (const to of outgoing.get(id) ?? []) {\n layerById.set(to, Math.max(layerById.get(to) ?? 0, fromLayer + 1))\n indegree.set(to, (indegree.get(to) ?? 0) - 1)\n if ((indegree.get(to) ?? 0) === 0) queue.push(to)\n }\n }\n\n let fallbackLayer = Math.max(0, ...Array.from(layerById.values())) + 1\n for (const node of plan.nodes) {\n if (visited.has(node.id)) continue\n layerById.set(node.id, fallbackLayer++)\n }\n\n const layers = new Map<number, PlanNode[]>()\n for (const node of plan.nodes) {\n const layer = layerById.get(node.id) ?? 0\n layers.set(layer, [...(layers.get(layer) ?? []), node])\n }\n\n const orderedLayers = [...layers.entries()].sort(([a], [b]) => a - b)\n const maxRows = Math.max(1, ...orderedLayers.map(([, nodes]) => nodes.length))\n const rowPitch = NODE_H + ROW_GAP\n\n const layoutNodes: LayoutNode[] = []\n orderedLayers.forEach(([layer, nodes], col) => {\n const ordered = [...nodes].sort((a, b) => {\n const lane = (a.lane || '').localeCompare(b.lane || '')\n return lane || ((indexById.get(a.id) ?? 0) - (indexById.get(b.id) ?? 0))\n })\n // Center each column vertically against the tallest column so the spine\n // runs through the middle and the parallel fan reads as a fan.\n const offset = ((maxRows - ordered.length) * rowPitch) / 2\n ordered.forEach((node, row) => {\n layoutNodes.push({\n node,\n layer,\n x: PAD + col * (NODE_W + COL_GAP),\n y: PAD + offset + row * rowPitch,\n })\n })\n })\n\n const cols = orderedLayers.length\n return {\n nodes: layoutNodes,\n width: Math.max(320, PAD * 2 + cols * NODE_W + Math.max(0, cols - 1) * COL_GAP),\n height: Math.max(NODE_H + PAD * 2, PAD * 2 + maxRows * NODE_H + Math.max(0, maxRows - 1) * ROW_GAP),\n }\n}\n\nfunction edgePath(from: LayoutNode, to: LayoutNode): string {\n const x1 = from.x + NODE_W\n const y1 = from.y + NODE_H / 2\n const x2 = to.x\n const y2 = to.y + NODE_H / 2\n const midX = x1 + Math.max(20, (x2 - x1) / 2)\n return `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`\n}\n\nexport function PlanGraphPanel({ threadId, teamId, channel }: { threadId?: string | number | null; teamId?: string | null; channel?: string }) {\n const key = teamId ? planTeamLookupKey(teamId, channel) : threadId ? planLookupKey(threadId, channel) : ''\n const plan = useRelayStore((s) => teamId ? s.plansByTeam[key] : key ? s.plansByThread[key] : null)\n const fetchPlan = useRelayStore((s) => s.fetchPlan)\n const setPlanArchived = useRelayStore((s) => s.setPlanArchived)\n const planCollapsed = useRelayStore((s) => s.planCollapsed)\n const setPlanCollapsed = useRelayStore((s) => s.setPlanCollapsed)\n const issueRefsById = useRelayStore((s) => s.issueRefsById)\n const deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const isMobile = useIsMobile()\n const [showArchivedPlan, setShowArchivedPlan] = useState(false)\n // null = no manual toggle yet → follow the viewport default. The graph\n // dominates a phone screen, so it starts collapsed there and expanded on\n // desktop; an explicit user toggle (persisted) overrides the default.\n const collapsed = planCollapsed ?? isMobile\n\n useEffect(() => {\n if (teamId) {\n void fetchPlan({ teamId, channel })\n return\n }\n if (threadId) void fetchPlan({ threadId, channel })\n }, [channel, fetchPlan, teamId, threadId])\n\n const layout = useMemo(() => plan ? layerPlan(plan) : null, [plan])\n const nodeById = useMemo(() => new Map(layout?.nodes.map((node) => [node.node.id, node]) ?? []), [layout])\n\n const legend = useMemo(() => {\n if (!plan) return []\n const counts = new Map<PlanStatus, number>()\n for (const node of plan.nodes) counts.set(node.status, (counts.get(node.status) ?? 0) + 1)\n return STATUS_ORDER.filter((status) => counts.has(status)).map((status) => ({\n status,\n count: counts.get(status) ?? 0,\n }))\n }, [plan])\n\n if (!plan || !layout) return null\n\n if (plan.archived && !showArchivedPlan) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(true)}\n className=\"flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground\"\n aria-expanded=\"false\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3\" />\n </span>\n <span className=\"min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider\">\n Show archived plan (1)\n </span>\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n </section>\n )\n }\n\n if (plan.archived) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-muted-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">Archived plan</div>\n </div>\n <span className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n archived\n </span>\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(false)}\n className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n aria-expanded=\"true\"\n >\n Hide\n </button>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, false)}\n title=\"Unarchive plan\"\n aria-label=\"Unarchive plan\"\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <ArchiveRestore className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </section>\n )\n }\n\n const nodeCount = plan.nodes.length\n const landedCount = legend.find((entry) => entry.status === 'landed')?.count ?? 0\n // Thin summary for the collapsed bar (the legend dots are hidden on mobile),\n // e.g. \"5 nodes · 4 landed\".\n const summary = `${nodeCount} node${nodeCount === 1 ? '' : 's'}${landedCount > 0 ? ` · ${landedCount} landed` : ''}`\n\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className={cn('flex min-w-0 items-center gap-2', !collapsed && 'mb-2')}>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <GitBranch className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">\n {collapsed ? summary : (plan.objective || summary)}\n </div>\n </div>\n </button>\n {legend.length > 0 && (\n <div className=\"hidden shrink-0 items-center gap-2.5 sm:flex\">\n {legend.map(({ status, count }) => (\n <span key={status} className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <span className={cn('h-1.5 w-1.5 rounded-full', STATUS_STYLE[status].dot)} />\n <span className={cn('font-medium tabular-nums', STATUS_STYLE[status].text)}>{count}</span>\n <span>{STATUS_STYLE[status].label}</span>\n </span>\n ))}\n </div>\n )}\n <span className=\"shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground\">\n v{plan.version}\n </span>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, true)}\n title=\"Archive plan\"\n aria-label=\"Archive plan\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <Archive className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n {collapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </button>\n </div>\n {collapsed ? null : plan.nodes.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground\">\n No nodes yet — the plan graph fills in as the team is composed.\n </div>\n ) : (\n <div className=\"max-h-[min(46vh,360px)] overflow-auto\">\n <div className=\"relative\" style={{ width: layout.width, height: layout.height }}>\n <svg className=\"pointer-events-none absolute inset-0\" width={layout.width} height={layout.height} aria-hidden=\"true\">\n <defs>\n {STATUS_ORDER.map((status) => (\n <marker\n key={status}\n id={`plan-arrow-${status}`}\n viewBox=\"0 0 8 8\"\n refX=\"6.5\"\n refY=\"4\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n >\n <path d=\"M0 0 L8 4 L0 8 z\" fill={STATUS_STYLE[status].wire} />\n </marker>\n ))}\n </defs>\n {plan.edges.map((edge) => {\n const from = nodeById.get(edge.from)\n const to = nodeById.get(edge.to)\n if (!from || !to) return null\n const wire = STATUS_STYLE[from.node.status]\n return (\n <path\n key={`${edge.from}->${edge.to}`}\n d={edgePath(from, to)}\n stroke={wire.wire}\n strokeOpacity={0.7}\n strokeWidth={2}\n fill=\"none\"\n strokeLinecap=\"round\"\n markerEnd={`url(#plan-arrow-${from.node.status})`}\n />\n )\n })}\n </svg>\n {layout.nodes.map(({ node, x, y }) => {\n const status = STATUS_STYLE[node.status]\n const owner = ownerLabel(node.ownerRef)\n const issueRef = node.refs?.issueId ? issueRefsById[node.refs.issueId] : null\n const taskNumId = node.refs?.taskId ? parseInt(node.refs.taskId, 10) : null\n const taskTitle = taskNumId != null ? deliverableTasks.find((t) => t.id === taskNumId)?.title : undefined\n return (\n <article\n key={node.id}\n className={cn(\n 'group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2',\n 'shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background',\n status.accent,\n )}\n style={{ left: x, top: y, width: NODE_W, height: NODE_H }}\n title={node.scopeBlurb || node.label}\n >\n <div className=\"truncate text-xs font-semibold leading-tight text-foreground\">{node.label}</div>\n {(node.scopeBlurb || node.id) && (\n <div className=\"truncate text-[10px] leading-tight text-muted-foreground\">{node.scopeBlurb || node.id}</div>\n )}\n {(issueRef || taskNumId != null) && (\n <div className=\"flex min-w-0 flex-wrap gap-1\">\n {issueRef && <IssueRefChip issueRef={issueRef} compact className=\"max-w-full\" />}\n {taskNumId != null && (\n <TaskChip id={taskNumId} title={taskTitle} onClick={() => openTaskDetailById(taskNumId)} className=\"max-w-full\" />\n )}\n </div>\n )}\n <div className=\"flex min-w-0 items-center justify-between gap-1.5\">\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <span className={cn('h-1.5 w-1.5 shrink-0 rounded-full', status.dot)} />\n <span className={cn('text-[10px] font-medium', status.text)}>{status.label}</span>\n </div>\n <div className=\"flex min-w-0 items-center gap-1.5\">\n {owner && <span className=\"truncate text-[10px] text-muted-foreground\">{owner}</span>}\n {node.lane && (\n <span className=\"shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground\">\n {node.lane}\n </span>\n )}\n </div>\n </div>\n </article>\n )\n })}\n </div>\n </div>\n )}\n </section>\n )\n}\n\n// Tasks created/triggered from a session (#856, #860). Fetches server-side via createdBy filter.\nexport function SessionTasksPanel({ agentId }: { agentId: string }) {\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const sessionTasksCollapsed = useRelayStore((s) => s.sessionTasksCollapsed)\n const setSessionTasksCollapsed = useRelayStore((s) => s.setSessionTasksCollapsed)\n const [sessionTasks, setSessionTasks] = useState<Task[]>([])\n const [showCompletedTasks, setShowCompletedTasks] = useState(false)\n useEffect(() => {\n void api<import('@/types').Task[]>('GET', '/task-entities?createdBy=' + encodeURIComponent(agentId))\n .then((tasks) => setSessionTasks(tasks))\n .catch(() => {})\n }, [agentId])\n if (sessionTasks.length === 0) return null\n const completedCount = sessionTasks.filter(isCompletedSessionTask).length\n const shownTasks = visibleSessionTasks(sessionTasks, showCompletedTasks)\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setSessionTasksCollapsed(!sessionTasksCollapsed)}\n aria-expanded={!sessionTasksCollapsed}\n aria-label={sessionTasksCollapsed ? 'Expand session tasks' : 'Collapse session tasks'}\n className={cn('flex w-full min-w-0 items-center gap-2 text-left', !sessionTasksCollapsed && 'mb-1.5')}\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <ListTodo className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <span className=\"flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n Tasks from this session ({shownTasks.length})\n </span>\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\">\n {sessionTasksCollapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </span>\n </button>\n {!sessionTasksCollapsed && (\n <>\n <div className=\"flex flex-wrap gap-1\">\n {shownTasks.map((task) => (\n <TaskChip\n key={task.id}\n id={task.id}\n title={task.title}\n status={task.status}\n onClick={() => openTaskDetailById(task.id)}\n compact\n />\n ))}\n </div>\n {completedCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowCompletedTasks(!showCompletedTasks)}\n className=\"mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n aria-pressed={showCompletedTasks}\n >\n {showCompletedTasks ? `Hide ${completedCount} closed` : `+ ${completedCount} closed`}\n </button>\n )}\n </>\n )}\n </section>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlanGraphPanel-Cnxt-x7R.js","names":[],"sources":["../../dashboard/src/components/chat/hooks/useIsMobile.ts","../../dashboard/src/components/chat/PlanGraphPanel.tsx"],"sourcesContent":["import { useSyncExternalStore } from 'react'\n\n// Reactive narrow-viewport flag. Subscribes to the same `max-width: 767px`\n// breakpoint the chat single-pane layout uses, via useSyncExternalStore — an\n// external-store subscription, not effect-driven derived state — so it stays\n// correct across resize/rotate without a useEffect.\nconst QUERY = '(max-width: 767px)'\n\nfunction subscribe(cb: () => void): () => void {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(QUERY)\n mql.addEventListener('change', cb)\n return () => mql.removeEventListener('change', cb)\n}\n\nexport function useIsMobile(): boolean {\n return useSyncExternalStore(\n subscribe,\n () => (typeof window !== 'undefined' ? window.matchMedia(QUERY).matches : false),\n () => false,\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { Archive, ArchiveRestore, ChevronDown, ChevronUp, GitBranch, ListTodo } from 'lucide-react'\nimport { useRelayStore } from '@/store'\nimport type { Plan, PlanNode, PlanOwnerRef, PlanStatus, Task } from '@/types'\nimport { cn } from '@/lib/utils'\nimport { CLOSED_TASK_STATUSES } from '@/lib/constants'\nimport { planLookupKey, planTeamLookupKey } from '@/store/slices/plans'\nimport { IssueRefChip } from '@/components/shared/issue-ref-chip'\nimport { TaskChip } from '@/components/shared/task-chip'\nimport { useIsMobile } from './hooks/useIsMobile'\nimport { api } from '@/lib/api'\n\n// Left-to-right DAG: dependency depth flows along x (sequential gates), nodes\n// that share a depth stack along y (parallel lanes). This keeps the panel SHORT\n// — it's an operator overlay above the chat, not a full view — and lets the\n// gate-vs-lane structure read at a glance. Height is capped + scrollable below.\nconst NODE_W = 196\nconst NODE_H = 84\nconst COL_GAP = 52\nconst ROW_GAP = 16\nconst PAD = 14\n\nconst STATUS_ORDER: PlanStatus[] = ['landed', 'in-progress', 'pending', 'blocked']\n\nconst STATUS_STYLE: Record<\n PlanStatus,\n { label: string; dot: string; accent: string; text: string; wire: string }\n> = {\n landed: { label: 'landed', dot: 'bg-emerald-400', accent: 'border-l-emerald-400', text: 'text-emerald-300', wire: '#34d399' },\n 'in-progress': { label: 'in progress', dot: 'bg-sky-400', accent: 'border-l-sky-400', text: 'text-sky-300', wire: '#38bdf8' },\n pending: { label: 'pending', dot: 'bg-amber-400', accent: 'border-l-amber-400', text: 'text-amber-300', wire: '#fbbf24' },\n blocked: { label: 'blocked', dot: 'bg-red-400', accent: 'border-l-red-400', text: 'text-red-300', wire: '#f87171' },\n}\n\n// CLOSED_TASK_STATUSES (done/failed/canceled) is the shared terminal-status\n// set; 'completed' is a legacy status value some older records still carry.\nconst COMPLETED_TASK_STATUSES = new Set([...CLOSED_TASK_STATUSES, 'completed'])\n\nexport function isCompletedSessionTask(task: Pick<Task, 'status'>): boolean {\n return COMPLETED_TASK_STATUSES.has(task.status)\n}\n\nexport function visibleSessionTasks(tasks: Task[], showCompleted: boolean): Task[] {\n return showCompleted ? tasks : tasks.filter((task) => !isCompletedSessionTask(task))\n}\n\ninterface LayoutNode {\n node: PlanNode\n x: number\n y: number\n layer: number\n}\n\nfunction ownerLabel(owner?: PlanOwnerRef): string {\n if (!owner) return ''\n return owner.label || (owner.policyName ? `policy:${owner.policyName}` : owner.agentId || '')\n}\n\nfunction layerPlan(plan: Plan): { nodes: LayoutNode[]; width: number; height: number } {\n const nodeById = new Map(plan.nodes.map((node) => [node.id, node]))\n const indexById = new Map(plan.nodes.map((node, index) => [node.id, index]))\n const outgoing = new Map<string, string[]>()\n const indegree = new Map(plan.nodes.map((node) => [node.id, 0]))\n for (const edge of plan.edges) {\n if (!nodeById.has(edge.from) || !nodeById.has(edge.to)) continue\n outgoing.set(edge.from, [...(outgoing.get(edge.from) ?? []), edge.to])\n indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1)\n }\n\n const layerById = new Map(plan.nodes.map((node) => [node.id, 0]))\n const queue = plan.nodes\n .filter((node) => (indegree.get(node.id) ?? 0) === 0)\n .map((node) => node.id)\n const visited = new Set<string>()\n\n while (queue.length) {\n const id = queue.shift()!\n visited.add(id)\n const fromLayer = layerById.get(id) ?? 0\n for (const to of outgoing.get(id) ?? []) {\n layerById.set(to, Math.max(layerById.get(to) ?? 0, fromLayer + 1))\n indegree.set(to, (indegree.get(to) ?? 0) - 1)\n if ((indegree.get(to) ?? 0) === 0) queue.push(to)\n }\n }\n\n let fallbackLayer = Math.max(0, ...Array.from(layerById.values())) + 1\n for (const node of plan.nodes) {\n if (visited.has(node.id)) continue\n layerById.set(node.id, fallbackLayer++)\n }\n\n const layers = new Map<number, PlanNode[]>()\n for (const node of plan.nodes) {\n const layer = layerById.get(node.id) ?? 0\n layers.set(layer, [...(layers.get(layer) ?? []), node])\n }\n\n const orderedLayers = [...layers.entries()].sort(([a], [b]) => a - b)\n const maxRows = Math.max(1, ...orderedLayers.map(([, nodes]) => nodes.length))\n const rowPitch = NODE_H + ROW_GAP\n\n const layoutNodes: LayoutNode[] = []\n orderedLayers.forEach(([layer, nodes], col) => {\n const ordered = [...nodes].sort((a, b) => {\n const lane = (a.lane || '').localeCompare(b.lane || '')\n return lane || ((indexById.get(a.id) ?? 0) - (indexById.get(b.id) ?? 0))\n })\n // Center each column vertically against the tallest column so the spine\n // runs through the middle and the parallel fan reads as a fan.\n const offset = ((maxRows - ordered.length) * rowPitch) / 2\n ordered.forEach((node, row) => {\n layoutNodes.push({\n node,\n layer,\n x: PAD + col * (NODE_W + COL_GAP),\n y: PAD + offset + row * rowPitch,\n })\n })\n })\n\n const cols = orderedLayers.length\n return {\n nodes: layoutNodes,\n width: Math.max(320, PAD * 2 + cols * NODE_W + Math.max(0, cols - 1) * COL_GAP),\n height: Math.max(NODE_H + PAD * 2, PAD * 2 + maxRows * NODE_H + Math.max(0, maxRows - 1) * ROW_GAP),\n }\n}\n\nfunction edgePath(from: LayoutNode, to: LayoutNode): string {\n const x1 = from.x + NODE_W\n const y1 = from.y + NODE_H / 2\n const x2 = to.x\n const y2 = to.y + NODE_H / 2\n const midX = x1 + Math.max(20, (x2 - x1) / 2)\n return `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`\n}\n\nexport function PlanGraphPanel({ threadId, teamId, channel }: { threadId?: string | number | null; teamId?: string | null; channel?: string }) {\n const key = teamId ? planTeamLookupKey(teamId, channel) : threadId ? planLookupKey(threadId, channel) : ''\n const plan = useRelayStore((s) => teamId ? s.plansByTeam[key] : key ? s.plansByThread[key] : null)\n const fetchPlan = useRelayStore((s) => s.fetchPlan)\n const setPlanArchived = useRelayStore((s) => s.setPlanArchived)\n const planCollapsed = useRelayStore((s) => s.planCollapsed)\n const setPlanCollapsed = useRelayStore((s) => s.setPlanCollapsed)\n const issueRefsById = useRelayStore((s) => s.issueRefsById)\n const deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const isMobile = useIsMobile()\n const [showArchivedPlan, setShowArchivedPlan] = useState(false)\n // null = no manual toggle yet → follow the viewport default. The graph\n // dominates a phone screen, so it starts collapsed there and expanded on\n // desktop; an explicit user toggle (persisted) overrides the default.\n const collapsed = planCollapsed ?? isMobile\n\n useEffect(() => {\n if (teamId) {\n void fetchPlan({ teamId, channel })\n return\n }\n if (threadId) void fetchPlan({ threadId, channel })\n }, [channel, fetchPlan, teamId, threadId])\n\n const layout = useMemo(() => plan ? layerPlan(plan) : null, [plan])\n const nodeById = useMemo(() => new Map(layout?.nodes.map((node) => [node.node.id, node]) ?? []), [layout])\n\n const legend = useMemo(() => {\n if (!plan) return []\n const counts = new Map<PlanStatus, number>()\n for (const node of plan.nodes) counts.set(node.status, (counts.get(node.status) ?? 0) + 1)\n return STATUS_ORDER.filter((status) => counts.has(status)).map((status) => ({\n status,\n count: counts.get(status) ?? 0,\n }))\n }, [plan])\n\n if (!plan || !layout) return null\n\n if (plan.archived && !showArchivedPlan) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(true)}\n className=\"flex min-w-0 items-center gap-2 text-left text-muted-foreground hover:text-foreground\"\n aria-expanded=\"false\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3\" />\n </span>\n <span className=\"min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wider\">\n Show archived plan (1)\n </span>\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0\" />\n </button>\n </section>\n )\n }\n\n if (plan.archived) {\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <Archive className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-muted-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">Archived plan</div>\n </div>\n <span className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n archived\n </span>\n <button\n type=\"button\"\n onClick={() => setShowArchivedPlan(false)}\n className=\"shrink-0 rounded border border-border bg-muted/40 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n aria-expanded=\"true\"\n >\n Hide\n </button>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, false)}\n title=\"Unarchive plan\"\n aria-label=\"Unarchive plan\"\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <ArchiveRestore className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </section>\n )\n }\n\n const nodeCount = plan.nodes.length\n const landedCount = legend.find((entry) => entry.status === 'landed')?.count ?? 0\n // Thin summary for the collapsed bar (the legend dots are hidden on mobile),\n // e.g. \"5 nodes · 4 landed\".\n const summary = `${nodeCount} node${nodeCount === 1 ? '' : 's'}${landedCount > 0 ? ` · ${landedCount} landed` : ''}`\n\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <div className={cn('flex min-w-0 items-center gap-2', !collapsed && 'mb-2')}>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <GitBranch className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-[13px] font-semibold leading-tight text-foreground\">{plan.title || 'Plan'}</div>\n <div className=\"truncate text-[11px] leading-tight text-muted-foreground\">\n {collapsed ? summary : (plan.objective || summary)}\n </div>\n </div>\n </button>\n {legend.length > 0 && (\n <div className=\"hidden shrink-0 items-center gap-2.5 sm:flex\">\n {legend.map(({ status, count }) => (\n <span key={status} className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <span className={cn('h-1.5 w-1.5 rounded-full', STATUS_STYLE[status].dot)} />\n <span className={cn('font-medium tabular-nums', STATUS_STYLE[status].text)}>{count}</span>\n <span>{STATUS_STYLE[status].label}</span>\n </span>\n ))}\n </div>\n )}\n <span className=\"shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground\">\n v{plan.version}\n </span>\n <button\n type=\"button\"\n onClick={() => void setPlanArchived(plan.id, true)}\n title=\"Archive plan\"\n aria-label=\"Archive plan\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n <Archive className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setPlanCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? 'Expand plan graph' : 'Collapse plan graph'}\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n >\n {collapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </button>\n </div>\n {collapsed ? null : plan.nodes.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border px-3 py-3 text-center text-xs text-muted-foreground\">\n No nodes yet — the plan graph fills in as the team is composed.\n </div>\n ) : (\n <div className=\"max-h-[min(46vh,360px)] overflow-auto\">\n <div className=\"relative\" style={{ width: layout.width, height: layout.height }}>\n <svg className=\"pointer-events-none absolute inset-0\" width={layout.width} height={layout.height} aria-hidden=\"true\">\n <defs>\n {STATUS_ORDER.map((status) => (\n <marker\n key={status}\n id={`plan-arrow-${status}`}\n viewBox=\"0 0 8 8\"\n refX=\"6.5\"\n refY=\"4\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n >\n <path d=\"M0 0 L8 4 L0 8 z\" fill={STATUS_STYLE[status].wire} />\n </marker>\n ))}\n </defs>\n {plan.edges.map((edge) => {\n const from = nodeById.get(edge.from)\n const to = nodeById.get(edge.to)\n if (!from || !to) return null\n const wire = STATUS_STYLE[from.node.status]\n return (\n <path\n key={`${edge.from}->${edge.to}`}\n d={edgePath(from, to)}\n stroke={wire.wire}\n strokeOpacity={0.7}\n strokeWidth={2}\n fill=\"none\"\n strokeLinecap=\"round\"\n markerEnd={`url(#plan-arrow-${from.node.status})`}\n />\n )\n })}\n </svg>\n {layout.nodes.map(({ node, x, y }) => {\n const status = STATUS_STYLE[node.status]\n const owner = ownerLabel(node.ownerRef)\n const issueRef = node.refs?.issueId ? issueRefsById[node.refs.issueId] : null\n const taskNumId = node.refs?.taskId ? parseInt(node.refs.taskId, 10) : null\n const taskTitle = taskNumId != null ? deliverableTasks.find((t) => t.id === taskNumId)?.title : undefined\n return (\n <article\n key={node.id}\n className={cn(\n 'group absolute flex flex-col justify-between rounded-md border border-l-[3px] bg-background/95 p-2',\n 'shadow-sm transition-all hover:-translate-y-px hover:border-border hover:shadow-md hover:bg-background',\n status.accent,\n )}\n style={{ left: x, top: y, width: NODE_W, height: NODE_H }}\n title={node.scopeBlurb || node.label}\n >\n <div className=\"truncate text-xs font-semibold leading-tight text-foreground\">{node.label}</div>\n {(node.scopeBlurb || node.id) && (\n <div className=\"truncate text-[10px] leading-tight text-muted-foreground\">{node.scopeBlurb || node.id}</div>\n )}\n {(issueRef || taskNumId != null) && (\n <div className=\"flex min-w-0 flex-wrap gap-1\">\n {issueRef && <IssueRefChip issueRef={issueRef} compact className=\"max-w-full\" />}\n {taskNumId != null && (\n <TaskChip id={taskNumId} title={taskTitle} onClick={() => openTaskDetailById(taskNumId)} className=\"max-w-full\" />\n )}\n </div>\n )}\n <div className=\"flex min-w-0 items-center justify-between gap-1.5\">\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <span className={cn('h-1.5 w-1.5 shrink-0 rounded-full', status.dot)} />\n <span className={cn('text-[10px] font-medium', status.text)}>{status.label}</span>\n </div>\n <div className=\"flex min-w-0 items-center gap-1.5\">\n {owner && <span className=\"truncate text-[10px] text-muted-foreground\">{owner}</span>}\n {node.lane && (\n <span className=\"shrink-0 rounded-sm bg-muted/70 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-muted-foreground\">\n {node.lane}\n </span>\n )}\n </div>\n </div>\n </article>\n )\n })}\n </div>\n </div>\n )}\n </section>\n )\n}\n\n// Tasks created/triggered from a session (#856, #860). Fetches server-side via createdBy filter.\nexport function SessionTasksPanel({ agentId }: { agentId: string }) {\n const openTaskDetailById = useRelayStore((s) => s.openTaskDetailById)\n const sessionTasksCollapsed = useRelayStore((s) => s.sessionTasksCollapsed)\n const setSessionTasksCollapsed = useRelayStore((s) => s.setSessionTasksCollapsed)\n const [sessionTasks, setSessionTasks] = useState<Task[]>([])\n const [showCompletedTasks, setShowCompletedTasks] = useState(false)\n useEffect(() => {\n void api<import('@/types').Task[]>('GET', '/task-entities?createdBy=' + encodeURIComponent(agentId))\n .then((tasks) => setSessionTasks(tasks))\n .catch(() => {})\n }, [agentId])\n if (sessionTasks.length === 0) return null\n const completedCount = sessionTasks.filter(isCompletedSessionTask).length\n const shownTasks = visibleSessionTasks(sessionTasks, showCompletedTasks)\n return (\n <section className=\"shrink-0 border-b border-border bg-card/45 px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => setSessionTasksCollapsed(!sessionTasksCollapsed)}\n aria-expanded={!sessionTasksCollapsed}\n aria-label={sessionTasksCollapsed ? 'Expand session tasks' : 'Collapse session tasks'}\n className={cn('flex w-full min-w-0 items-center gap-2 text-left', !sessionTasksCollapsed && 'mb-1.5')}\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted/60\">\n <ListTodo className=\"h-3 w-3 text-muted-foreground\" />\n </span>\n <span className=\"flex-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n Tasks from this session ({shownTasks.length})\n </span>\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted/60 hover:text-foreground\">\n {sessionTasksCollapsed ? <ChevronDown className=\"h-3.5 w-3.5\" /> : <ChevronUp className=\"h-3.5 w-3.5\" />}\n </span>\n </button>\n {!sessionTasksCollapsed && (\n <>\n <div className=\"flex flex-wrap gap-1\">\n {shownTasks.map((task) => (\n <TaskChip\n key={task.id}\n id={task.id}\n title={task.title}\n status={task.status}\n onClick={() => openTaskDetailById(task.id)}\n compact\n />\n ))}\n </div>\n {completedCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowCompletedTasks(!showCompletedTasks)}\n className=\"mt-1.5 rounded border border-border bg-muted/30 px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n aria-pressed={showCompletedTasks}\n >\n {showCompletedTasks ? `Hide ${completedCount} closed` : `+ ${completedCount} closed`}\n </button>\n )}\n </>\n )}\n </section>\n )\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{In as t,Kt as n,Vt as r,_t as i,at as a,d as o,dn as s,er as c,gr as l,hn as u,it as d,rr as f,tr as p,z as m}from"./lucide-react-CoU_Xts2.js";import{p as h,t as g}from"./store-Dl9Z8YUS.js";import{K as _,f as v,h as y,rt as b}from"./display-aTnwsqQh.js";import{t as x}from"./badge-XEru6wto.js";import{t as S}from"./button-C3CQSr4p.js";import{t as C}from"./task-chip-BHM-e3I8.js";import{G as w,c as T,f as E,g as D,s as O,x as k,y as A}from"./index-De1tR7J4.js";import{r as j,t as M}from"./card-C5XTIPvq.js";import{t as N}from"./MessageBubble-BYeYFteG.js";var P=e(l(),1),F=f(),I={question:u,reply:d,message:a,operator:o,pair:i,task:s,state:m},L={question:`bg-blue-500/10 text-blue-400 border-blue-500/20`,reply:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,message:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,operator:`bg-purple-500/10 text-purple-400 border-purple-500/20`,pair:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,task:`bg-orange-500/10 text-orange-400 border-orange-500/20`,state:`bg-cyan-500/10 text-cyan-400 border-cyan-500/20`},R=[`question`,`reply`,`message`,`operator`,`pair`,`task`,`state`],z=[`chat`,`session`,`system`,`task`];function B(e){let t=e?.surface;if(!t||typeof t!=`object`)return null;let n=t,r=[];return typeof n.component==`string`&&r.push(n.component),typeof n.view==`string`&&n.view!==n.component&&r.push(n.view),typeof n.session==`string`&&r.push(`tab ${n.session.slice(0,6)}`),typeof n.client==`string`&&r.push(`client ${n.client.slice(0,6)}`),r.length?r.join(` · `):null}function V({entry:e,agentsById:t}){if(e.type===`notice`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(D,{msg:e.msg})});if(e.type===`agent-action`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(T,{entry:e})});if(e.type===`activity`)return(0,F.jsx)(`div`,{className:`px-1 opacity-60`,children:(0,F.jsx)(O,{steps:e.steps,showReasoning:!1})});if(e.type===`status`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(A,{event:e.event})});if(e.type===`date`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(E,{date:e.date})});if(e.type===`created`||e.type===`import-boundary`||e.type===`imported-message`||e.type!==`message`)return null;let{msg:n}=e,r=v(n.from,t),i=v(n.to,t);return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-1.5 px-3 mb-0.5`,children:[(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.from,children:r}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0`,children:`→`}),(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.to,children:i}),(0,F.jsx)(x,{variant:`outline`,className:`text-[10px] px-1 py-0 shrink-0 ml-1`,children:n.kind})]}),(0,F.jsx)(N,{msg:n,peer:b})]})}function H(){let e=g(e=>e.messages),t=g(e=>e.agentsById),[n,r]=(0,P.useState)(``),[i,a]=(0,P.useState)(``),o=(0,P.useMemo)(()=>{let t=e;if(n){let e=n.toLowerCase();t=t.filter(t=>t.from.toLowerCase().includes(e)||t.to.toLowerCase().includes(e))}return i&&(t=t.filter(e=>e.kind===i)),t},[e,n,i]),s=(0,P.useMemo)(()=>k(o,[]),[o]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`input`,{type:`text`,className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm w-44`,placeholder:`Filter by agent…`,value:n,onChange:e=>r(e.target.value)}),(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:i,onChange:e=>a(e.target.value),children:[(0,F.jsx)(`option`,{value:``,children:`All kinds`}),z.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:o.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-1 pr-2`,children:[s.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No messages yet`}),s.map((e,n)=>(0,F.jsx)(V,{entry:e,agentsById:t},n))]})})]})}function U(){let e=h(),r=g(e=>e.activityFilter),i=g(e=>e.activityEvents),a=g(e=>e.operatorActivity),o=g(e=>e.set),s=g(e=>e.openActivityItem),l=g(e=>e.openTaskDetailById),u=g(e=>e.exportActivity),d=(0,P.useMemo)(()=>{let e=[...i,...a],t=new Set,n=[];for(let r of e){let e=r.clientId||String(r.id);t.has(e)||(t.add(e),n.push(r))}return n.map(e=>({...e,ts:e.ts??new Date(e.createdAt).getTime()})).sort((e,t)=>(t.ts??0)-(e.ts??0))},[i,a]),f=(0,P.useMemo)(()=>r?d.filter(e=>e.kind===r):d,[d,r]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r,onChange:e=>o({activityFilter:e.target.value}),children:[(0,F.jsx)(`option`,{value:``,children:`All types`}),R.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`markdown`),children:[(0,F.jsx)(n,{className:`w-4 h-4 mr-1`}),` Markdown`]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`json`),children:[(0,F.jsx)(t,{className:`w-4 h-4 mr-1`}),` JSON`]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:f.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-2 pr-2`,children:[f.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No activity yet`}),f.map(t=>{let n=I[t.kind]||c,r=L[t.kind]||L.message,i=B(t.metadata);return(0,F.jsx)(M,{className:`cursor-pointer hover:bg-accent/40 transition-colors`,onClick:()=>s(t),children:(0,F.jsxs)(j,{className:`p-3 flex gap-3`,children:[(0,F.jsx)(`span`,{className:`inline-flex items-center justify-center w-7 h-7 rounded-md border shrink-0 mt-0.5 ${r}`,children:(0,F.jsx)(n,{className:`w-3.5 h-3.5`})}),(0,F.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`span`,{className:`font-medium text-sm truncate`,children:t.title}),(0,F.jsx)(x,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${r}`,children:t.kind}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground ml-auto shrink-0`,title:y(t.ts??t.createdAt),children:_(e,t.ts??t.createdAt)})]}),t.body&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground mt-0.5 line-clamp-2`,children:t.body}),t.meta&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 font-mono truncate`,children:t.meta}),i&&(0,F.jsxs)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 truncate`,title:i,children:[(0,F.jsx)(`span`,{className:`opacity-70`,children:`via`}),` `,i]}),t.taskId!=null&&(0,F.jsx)(`div`,{className:`mt-1`,children:(0,F.jsx)(C,{id:t.taskId,onClick:()=>l(t.taskId)})})]})]})},t.clientId||t.id)})]})})]})}function W(){let[e,t]=(0,P.useState)(`firehose`);return(0,F.jsxs)(`div`,{className:`space-y-4`,children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[e===`firehose`?(0,F.jsx)(r,{className:`w-5 h-5 text-muted-foreground`}):(0,F.jsx)(c,{className:`w-5 h-5 text-muted-foreground`}),(0,F.jsx)(`h2`,{className:`text-lg font-semibold`,children:e===`firehose`?`Firehose`:`Events`})]}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1 rounded-md border border-input overflow-hidden`,children:[(0,F.jsx)(`button`,{className:p(`px-3 py-1.5 text-sm transition-colors`,e===`firehose`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>t(`firehose`),children:`Firehose`}),(0,F.jsx)(`button`,{className:p(`px-3 py-1.5 text-sm transition-colors`,e===`events`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>t(`events`),children:`Events`})]})]}),e===`firehose`?(0,F.jsx)(H,{}):(0,F.jsx)(U,{})]})}export{W as ActivityView};
2
- //# sourceMappingURL=activity-Du9SRJt5.js.map
1
+ import{r as e}from"./chunk-CilyBKbf.js";import{In as t,Kt as n,Vt as r,_t as i,at as a,d as o,dn as s,er as c,gr as l,hn as u,it as d,rr as f,tr as p,z as m}from"./lucide-react-NXHPIMsX.js";import{p as h,t as g}from"./store-D-Frk_EC.js";import{K as _,f as v,h as y,rt as b}from"./display-aTnwsqQh.js";import{t as x}from"./badge-BCvp7LK3.js";import{t as S}from"./button-DyHjsHY3.js";import{t as C}from"./task-chip-CeujtXYT.js";import{G as w,c as T,f as E,g as D,s as O,x as k,y as A}from"./index-Bhy_jnX4.js";import{r as j,t as M}from"./card-BoqYxpVW.js";import{t as N}from"./MessageBubble-D2cnxyTy.js";var P=e(l(),1),F=f(),I={question:u,reply:d,message:a,operator:o,pair:i,task:s,state:m},L={question:`bg-blue-500/10 text-blue-400 border-blue-500/20`,reply:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,message:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,operator:`bg-purple-500/10 text-purple-400 border-purple-500/20`,pair:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,task:`bg-orange-500/10 text-orange-400 border-orange-500/20`,state:`bg-cyan-500/10 text-cyan-400 border-cyan-500/20`},R=[`question`,`reply`,`message`,`operator`,`pair`,`task`,`state`],z=[`chat`,`session`,`system`,`task`];function B(e){let t=e?.surface;if(!t||typeof t!=`object`)return null;let n=t,r=[];return typeof n.component==`string`&&r.push(n.component),typeof n.view==`string`&&n.view!==n.component&&r.push(n.view),typeof n.session==`string`&&r.push(`tab ${n.session.slice(0,6)}`),typeof n.client==`string`&&r.push(`client ${n.client.slice(0,6)}`),r.length?r.join(` · `):null}function V({entry:e,agentsById:t}){if(e.type===`notice`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(D,{msg:e.msg})});if(e.type===`agent-action`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(T,{entry:e})});if(e.type===`activity`)return(0,F.jsx)(`div`,{className:`px-1 opacity-60`,children:(0,F.jsx)(O,{steps:e.steps,showReasoning:!1})});if(e.type===`status`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(A,{event:e.event})});if(e.type===`date`)return(0,F.jsx)(`div`,{className:`px-1`,children:(0,F.jsx)(E,{date:e.date})});if(e.type===`created`||e.type===`import-boundary`||e.type===`imported-message`||e.type!==`message`)return null;let{msg:n}=e,r=v(n.from,t),i=v(n.to,t);return(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-1.5 px-3 mb-0.5`,children:[(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.from,children:r}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0`,children:`→`}),(0,F.jsx)(`span`,{className:`text-xs font-mono text-muted-foreground truncate max-w-[140px]`,title:n.to,children:i}),(0,F.jsx)(x,{variant:`outline`,className:`text-[10px] px-1 py-0 shrink-0 ml-1`,children:n.kind})]}),(0,F.jsx)(N,{msg:n,peer:b})]})}function H(){let e=g(e=>e.messages),t=g(e=>e.agentsById),[n,r]=(0,P.useState)(``),[i,a]=(0,P.useState)(``),o=(0,P.useMemo)(()=>{let t=e;if(n){let e=n.toLowerCase();t=t.filter(t=>t.from.toLowerCase().includes(e)||t.to.toLowerCase().includes(e))}return i&&(t=t.filter(e=>e.kind===i)),t},[e,n,i]),s=(0,P.useMemo)(()=>k(o,[]),[o]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`input`,{type:`text`,className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm w-44`,placeholder:`Filter by agent…`,value:n,onChange:e=>r(e.target.value)}),(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:i,onChange:e=>a(e.target.value),children:[(0,F.jsx)(`option`,{value:``,children:`All kinds`}),z.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:o.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-1 pr-2`,children:[s.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No messages yet`}),s.map((e,n)=>(0,F.jsx)(V,{entry:e,agentsById:t},n))]})})]})}function U(){let e=h(),r=g(e=>e.activityFilter),i=g(e=>e.activityEvents),a=g(e=>e.operatorActivity),o=g(e=>e.set),s=g(e=>e.openActivityItem),l=g(e=>e.openTaskDetailById),u=g(e=>e.exportActivity),d=(0,P.useMemo)(()=>{let e=[...i,...a],t=new Set,n=[];for(let r of e){let e=r.clientId||String(r.id);t.has(e)||(t.add(e),n.push(r))}return n.map(e=>({...e,ts:e.ts??new Date(e.createdAt).getTime()})).sort((e,t)=>(t.ts??0)-(e.ts??0))},[i,a]),f=(0,P.useMemo)(()=>r?d.filter(e=>e.kind===r):d,[d,r]);return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r,onChange:e=>o({activityFilter:e.target.value}),children:[(0,F.jsx)(`option`,{value:``,children:`All types`}),R.map(e=>(0,F.jsx)(`option`,{value:e,children:e},e))]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`markdown`),children:[(0,F.jsx)(n,{className:`w-4 h-4 mr-1`}),` Markdown`]}),(0,F.jsxs)(S,{size:`sm`,variant:`outline`,onClick:()=>u(`json`),children:[(0,F.jsx)(t,{className:`w-4 h-4 mr-1`}),` JSON`]}),(0,F.jsx)(x,{variant:`secondary`,className:`ml-auto`,children:f.length})]}),(0,F.jsx)(w,{className:`h-[calc(100dvh-13rem)]`,children:(0,F.jsxs)(`div`,{className:`space-y-2 pr-2`,children:[f.length===0&&(0,F.jsx)(`div`,{className:`text-center text-muted-foreground py-16 text-sm`,children:`No activity yet`}),f.map(t=>{let n=I[t.kind]||c,r=L[t.kind]||L.message,i=B(t.metadata);return(0,F.jsx)(M,{className:`cursor-pointer hover:bg-accent/40 transition-colors`,onClick:()=>s(t),children:(0,F.jsxs)(j,{className:`p-3 flex gap-3`,children:[(0,F.jsx)(`span`,{className:`inline-flex items-center justify-center w-7 h-7 rounded-md border shrink-0 mt-0.5 ${r}`,children:(0,F.jsx)(n,{className:`w-3.5 h-3.5`})}),(0,F.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,F.jsx)(`span`,{className:`font-medium text-sm truncate`,children:t.title}),(0,F.jsx)(x,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${r}`,children:t.kind}),(0,F.jsx)(`span`,{className:`text-xs text-muted-foreground ml-auto shrink-0`,title:y(t.ts??t.createdAt),children:_(e,t.ts??t.createdAt)})]}),t.body&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground mt-0.5 line-clamp-2`,children:t.body}),t.meta&&(0,F.jsx)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 font-mono truncate`,children:t.meta}),i&&(0,F.jsxs)(`p`,{className:`text-xs text-muted-foreground/60 mt-0.5 truncate`,title:i,children:[(0,F.jsx)(`span`,{className:`opacity-70`,children:`via`}),` `,i]}),t.taskId!=null&&(0,F.jsx)(`div`,{className:`mt-1`,children:(0,F.jsx)(C,{id:t.taskId,onClick:()=>l(t.taskId)})})]})]})},t.clientId||t.id)})]})})]})}function W(){let[e,t]=(0,P.useState)(`firehose`);return(0,F.jsxs)(`div`,{className:`space-y-4`,children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[e===`firehose`?(0,F.jsx)(r,{className:`w-5 h-5 text-muted-foreground`}):(0,F.jsx)(c,{className:`w-5 h-5 text-muted-foreground`}),(0,F.jsx)(`h2`,{className:`text-lg font-semibold`,children:e===`firehose`?`Firehose`:`Events`})]}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1 rounded-md border border-input overflow-hidden`,children:[(0,F.jsx)(`button`,{className:p(`px-3 py-1.5 text-sm transition-colors`,e===`firehose`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>t(`firehose`),children:`Firehose`}),(0,F.jsx)(`button`,{className:p(`px-3 py-1.5 text-sm transition-colors`,e===`events`?`bg-primary text-primary-foreground`:`hover:bg-accent`),onClick:()=>t(`events`),children:`Events`})]})]}),e===`firehose`?(0,F.jsx)(H,{}):(0,F.jsx)(U,{})]})}export{W as ActivityView};
2
+ //# sourceMappingURL=activity-BEm1BQB1.js.map