@schlenkr/ragents 0.1.19 → 0.1.20

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 (206) hide show
  1. package/apps/server/src/host-api.json +6 -2
  2. package/apps/server/src/host-api.ts +3 -2
  3. package/apps/server/src/host-web.ts +3 -0
  4. package/apps/server/src/plugin-support/actor-programs/contract.ts +0 -3
  5. package/apps/server/src/plugin-support/removed-layout.ts +94 -0
  6. package/apps/server/src/ragents/engine.ts +2 -1
  7. package/apps/server/src/title-compactor.ts +5 -7
  8. package/apps/web/dist/assets/highlighted-body-KPVGNVTW-k67VKdbw.js +1 -0
  9. package/apps/web/dist/assets/main-CVjIz7BS.js +674 -0
  10. package/apps/web/dist/help/guide-clients.html +22 -42
  11. package/apps/web/dist/help/guide-clients.md +24 -45
  12. package/apps/web/dist/help/guide-functions.html +1 -1
  13. package/apps/web/dist/help/guide-functions.md +1 -1
  14. package/apps/web/dist/help/guide-getting-started.html +14 -22
  15. package/apps/web/dist/help/guide-getting-started.md +17 -24
  16. package/apps/web/dist/help/guide-ideas.html +1 -1
  17. package/apps/web/dist/help/guide-ideas.md +1 -1
  18. package/apps/web/dist/help/guide-plugins.html +1 -1
  19. package/apps/web/dist/help/guide-plugins.md +1 -1
  20. package/apps/web/dist/help/guide-programs.html +1 -1
  21. package/apps/web/dist/help/guide-programs.md +1 -1
  22. package/apps/web/dist/help/index.html +12 -79
  23. package/apps/web/dist/help/mini-app.css +8 -502
  24. package/apps/web/dist/host-web.json +28 -23
  25. package/apps/web/dist/index.html +2 -3
  26. package/apps/web/dist/run-panel.html +3 -4
  27. package/apps/web/src/Overview.tsx +4 -143
  28. package/apps/web/src/PluginChat.tsx +22 -116
  29. package/apps/web/src/PluginRegistry.tsx +4 -9
  30. package/apps/web/src/RunPreparationChat.tsx +4 -1
  31. package/apps/web/src/host-modules.ts +2 -0
  32. package/apps/web/src/main.tsx +23 -3
  33. package/apps/web/src/panel/RunLine.tsx +5 -2
  34. package/apps/web/src/panel/RunsPage.tsx +6 -6
  35. package/apps/web/src/panel/StartPage.tsx +6 -5
  36. package/apps/web/src/panel/contract.ts +3 -0
  37. package/apps/web/src/panel/page-props.ts +2 -0
  38. package/apps/web/src/run-apps.ts +34 -0
  39. package/apps/web/src/run-overview.ts +0 -27
  40. package/apps/web/src/run-panel/PanelContributions.tsx +41 -0
  41. package/apps/web/src/run-panel/RunPanelApp.tsx +172 -91
  42. package/apps/web/src/run-panel/RunPanelMenu.tsx +4 -3
  43. package/apps/web/src/run-panel/RunPanelWorkspace.tsx +8 -2
  44. package/apps/web/src/run-panel/clipboard.ts +17 -14
  45. package/apps/web/src/run-panel/host-contract.ts +8 -9
  46. package/apps/web/src/run-panel/host.ts +5 -31
  47. package/apps/web/src/run-panel/input-bridge.ts +15 -7
  48. package/bundles/ragents.activity/ragents-bundle.json +2 -2
  49. package/bundles/ragents.actor-programs/prompt.hbs +4 -5
  50. package/bundles/ragents.actor-programs/ragents-bundle.json +4 -14
  51. package/bundles/ragents.actor-programs/server/index.js +2 -14
  52. package/bundles/ragents.actor-programs/server/index.js.map +2 -2
  53. package/bundles/ragents.actor-programs/web/classes.json +1 -1
  54. package/bundles/ragents.actor-programs/web/index.js +1 -3
  55. package/bundles/ragents.actor-programs/web/index.js.map +4 -4
  56. package/bundles/ragents.ask/ragents-bundle.json +2 -2
  57. package/bundles/ragents.browser/ragents-bundle.json +2 -2
  58. package/bundles/ragents.documents/ragents-bundle.json +2 -2
  59. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +2 -2
  60. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +2 -2
  61. package/bundles/ragents.lsp-typescript/ragents-bundle.json +2 -2
  62. package/bundles/ragents.model-relay/ragents-bundle.json +2 -2
  63. package/bundles/ragents.orchestration/ragents-bundle.json +15 -41
  64. package/bundles/ragents.orchestration/server/index.js +10 -249
  65. package/bundles/ragents.orchestration/server/index.js.map +4 -4
  66. package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js +2 -0
  67. package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js.map +7 -0
  68. package/bundles/ragents.orchestration/web/classes.json +1 -1
  69. package/bundles/ragents.orchestration/web/exports/web/program-slot.js +1 -1
  70. package/bundles/ragents.orchestration/web/index.js +10 -18
  71. package/bundles/ragents.orchestration/web/index.js.map +4 -4
  72. package/bundles/ragents.overseer/ragents-bundle.json +3 -3
  73. package/bundles/ragents.overseer/server/index.js +0 -1
  74. package/bundles/ragents.overseer/server/index.js.map +2 -2
  75. package/bundles/ragents.processes/ragents-bundle.json +2 -2
  76. package/bundles/ragents.product/ragents-bundle.json +2 -2
  77. package/bundles/ragents.profile-distribution/ragents-bundle.json +2 -2
  78. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  79. package/bundles/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
  80. package/bundles/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
  81. package/bundles/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
  82. package/bundles/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
  83. package/bundles/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
  84. package/bundles/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
  85. package/bundles/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
  86. package/bundles/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
  87. package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  88. package/bundles/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
  89. package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
  90. package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
  91. package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
  92. package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
  93. package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
  94. package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
  95. package/bundles/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
  96. package/bundles/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
  97. package/bundles/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
  98. package/bundles/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
  99. package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
  100. package/bundles/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
  101. package/bundles/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
  102. package/bundles/ragents.todo/ragents-bundle.json +2 -2
  103. package/bundles/ragents.transcript/ragents-bundle.json +2 -2
  104. package/bundles/ragents.watch/ragents-bundle.json +2 -2
  105. package/bundles/ragents.workspace/ragents-bundle.json +2 -2
  106. package/package.json +8 -8
  107. package/packages/agent/dist/core/agent-settings.d.ts +1 -1
  108. package/packages/ai/dist/types.d.ts +2 -1
  109. package/packages/ragents/src/runtime/journal.ts +4 -0
  110. package/plugins/ragents.actor-programs/prompt.hbs +4 -5
  111. package/plugins/ragents.actor-programs/server/access-projections.ts +1 -1
  112. package/plugins/ragents.actor-programs/server/index.ts +0 -2
  113. package/plugins/ragents.actor-programs/server/runtime.ts +2 -10
  114. package/plugins/ragents.actor-programs/web/ActorViewFrame.tsx +5 -20
  115. package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -79
  116. package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +0 -9
  117. package/plugins/ragents.actor-programs/web/api.ts +0 -20
  118. package/plugins/ragents.actor-programs/web/context.ts +0 -3
  119. package/plugins/ragents.actor-programs/web/index.tsx +0 -9
  120. package/plugins/ragents.orchestration/ragents-plugin.json +1 -2
  121. package/plugins/ragents.orchestration/server/index.ts +1 -11
  122. package/plugins/ragents.orchestration/web/ActorChat.tsx +1 -1
  123. package/plugins/ragents.orchestration/web/ActorChatControls.tsx +1 -1
  124. package/plugins/ragents.orchestration/web/FlowInspector.tsx +2 -4
  125. package/plugins/ragents.orchestration/web/InspectionPanel.tsx +21 -0
  126. package/plugins/ragents.orchestration/web/index.tsx +13 -9
  127. package/plugins/ragents.orchestration/web/program-slot.ts +0 -3
  128. package/plugins/ragents.orchestration/web/run-panel/AddresseeControl.tsx +15 -36
  129. package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +76 -451
  130. package/plugins/ragents.orchestration/web/run-panel/run-panel-actors.ts +0 -27
  131. package/plugins/ragents.orchestration/web/run-panel/run-panel-state.ts +7 -40
  132. package/plugins/ragents.overseer/server/coordinator-prompt.ts +0 -1
  133. package/plugins/ragents.reference/examples.ts +1 -2
  134. package/plugins/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
  135. package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
  136. package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
  137. package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
  138. package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
  139. package/plugins/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
  140. package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
  141. package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
  142. package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  143. package/plugins/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
  144. package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
  145. package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
  146. package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
  147. package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
  148. package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
  149. package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
  150. package/plugins/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
  151. package/plugins/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
  152. package/plugins/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
  153. package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
  154. package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
  155. package/plugins/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
  156. package/plugins/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
  157. package/plugins/ragents.reference/walkthroughs.ts +13 -14
  158. package/apps/web/dist/assets/highlighted-body-KPVGNVTW-BwD4uYEe.js +0 -1
  159. package/apps/web/dist/assets/main-KBrc5Q77.js +0 -11
  160. package/apps/web/dist/assets/mermaid-HWGCJPDP-CSCkaArr.js +0 -608
  161. package/apps/web/dist/assets/runPanel-BjG5NBKm.js +0 -26
  162. package/apps/web/src/App.tsx +0 -318
  163. package/apps/web/src/SessionList.tsx +0 -157
  164. package/apps/web/src/SurfaceShortcuts.tsx +0 -60
  165. package/apps/web/src/WorkspacePanel.tsx +0 -309
  166. package/apps/web/src/run-panel.tsx +0 -52
  167. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +0 -11
  168. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +0 -7
  169. package/bundles/ragents.orchestration/server/exports/server/contract.js +0 -9
  170. package/bundles/ragents.orchestration/server/exports/server/contract.js.map +0 -7
  171. package/bundles/ragents.orchestration/surface.hbs +0 -39
  172. package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js +0 -2
  173. package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js.map +0 -7
  174. package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js +0 -2
  175. package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js.map +0 -7
  176. package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js +0 -2
  177. package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js.map +0 -7
  178. package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js +0 -2
  179. package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js.map +0 -7
  180. package/bundles/ragents.orchestration/web/exports/web/surface-entities.js +0 -2
  181. package/bundles/ragents.orchestration/web/exports/web/surface-entities.js.map +0 -7
  182. package/bundles/ragents.orchestration/web/exports/web/tile-docking.js +0 -2
  183. package/bundles/ragents.orchestration/web/exports/web/tile-docking.js.map +0 -7
  184. package/plugins/ragents.actor-programs/web/ExpandIcon.tsx +0 -5
  185. package/plugins/ragents.actor-programs/web/FullscreenHost.tsx +0 -31
  186. package/plugins/ragents.orchestration/contract.ts +0 -51
  187. package/plugins/ragents.orchestration/server/contract.ts +0 -14
  188. package/plugins/ragents.orchestration/server/surface-tool.ts +0 -198
  189. package/plugins/ragents.orchestration/surface.hbs +0 -39
  190. package/plugins/ragents.orchestration/tiled-layout.ts +0 -55
  191. package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +0 -29
  192. package/plugins/ragents.orchestration/web/ActorShortcut.tsx +0 -74
  193. package/plugins/ragents.orchestration/web/ActorSurfaceControls.tsx +0 -130
  194. package/plugins/ragents.orchestration/web/ActorTile.tsx +0 -23
  195. package/plugins/ragents.orchestration/web/Center.tsx +0 -133
  196. package/plugins/ragents.orchestration/web/SurfaceTiles.tsx +0 -47
  197. package/plugins/ragents.orchestration/web/TiledSurface.tsx +0 -280
  198. package/plugins/ragents.orchestration/web/actor-header-settings.ts +0 -41
  199. package/plugins/ragents.orchestration/web/run-panel/RunPanelSettings.tsx +0 -50
  200. package/plugins/ragents.orchestration/web/run-panel/run-panel-settings.ts +0 -47
  201. package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +0 -30
  202. package/plugins/ragents.orchestration/web/surface-entities.ts +0 -21
  203. package/plugins/ragents.orchestration/web/surface-view-settings.ts +0 -42
  204. package/plugins/ragents.orchestration/web/tile-docking.ts +0 -105
  205. package/plugins/ragents.orchestration/web/tiled-view-settings.ts +0 -74
  206. /package/apps/web/dist/assets/{mermaid-HWGCJPDP-BPn4aV2r.css → main-BPn4aV2r.css} +0 -0
@@ -1,26 +0,0 @@
1
- import{a as _,H as B,u as X,r as o,j as e,B as E,I as Fe,J as Me,K as De,v as Ue,l as W,m as Y,t as He,k as $e,P as Be,G as fe,L as Ve,p as Z,q as pe,i as qe,N as xe,O as le,T as Je,V as Ge,A as ce,Y as Ke,Z as Qe,_ as We,$ as Ye,s as Ze,a0 as Xe,e as et,a1 as tt,a2 as nt,x as st,y as rt,a3 as ot,a4 as at,a5 as it,z as lt,Q as ct,a6 as dt,a7 as ut,F as ht}from"./mermaid-HWGCJPDP-CSCkaArr.js";/**
2
- * @license lucide-react v1.46.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const me={name:"ellipsis-vertical",size:24,node:[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["circle",{cx:"12",cy:"5",r:"1",key:"gxeob9"}],["circle",{cx:"12",cy:"19",r:"1",key:"lyex9k"}]],aliases:["more-vertical"]};me.node;const ft=_(me);/**
7
- * @license lucide-react v1.46.0 - ISC
8
- *
9
- * This source code is licensed under the ISC license.
10
- * See the LICENSE file in the root directory of this source tree.
11
- */const ve={name:"external-link",size:24,node:[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]};ve.node;const pt=_(ve);/**
12
- * @license lucide-react v1.46.0 - ISC
13
- *
14
- * This source code is licensed under the ISC license.
15
- * See the LICENSE file in the root directory of this source tree.
16
- */const ge={name:"layout-grid",size:24,node:[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]]};ge.node;const xt=_(ge);/**
17
- * @license lucide-react v1.46.0 - ISC
18
- *
19
- * This source code is licensed under the ISC license.
20
- * See the LICENSE file in the root directory of this source tree.
21
- */const we={name:"log-out",size:24,node:[["path",{d:"m16 17 5-5-5-5",key:"1bji2h"}],["path",{d:"M21 12H9",key:"dn1m92"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}]]};we.node;const mt=_(we);/**
22
- * @license lucide-react v1.46.0 - ISC
23
- *
24
- * This source code is licensed under the ISC license.
25
- * See the LICENSE file in the root directory of this source tree.
26
- */const be={name:"play",size:24,node:[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]]};be.node;const vt=_(be),O="flex w-full cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-2 disabled:cursor-default disabled:opacity-50 disabled:hover:bg-transparent [&>svg]:size-4 [&>svg]:flex-none [&>svg]:text-muted-foreground",K=t=>t instanceof Error?t.message:String(t);function Q({onOpenSettings:t,runId:a}){const r=B(),n=X(),[l,c]=o.useState(!1),[u,f]=o.useState(!1),[m,x]=o.useState(),[d,b]=o.useState(),k=o.useRef(null),R=o.useId(),P=i=>{var p;return i.reason==="outside-press"&&i.event.target instanceof Node&&((p=k.current)==null?void 0:p.contains(i.event.target))===!0},v=()=>{c(!1),b(void 0),x(void 0)},g=()=>{if(r.kind==="vscode"){r.requestLogout(),c(!1);return}f(!0),x(void 0),n.logout().then(()=>c(!1)).catch(i=>x(K(i))).finally(()=>f(!1))},V=i=>{b({kind:"loading"}),x(void 0),W.call(Y.runs.scripts,{runId:i}).then(p=>b({kind:"ready",scripts:p})).catch(p=>{b({kind:"ready",scripts:[]}),x(K(p))})},z=(i,p)=>{f(!0),x(void 0),W.call(Y.runs.startScript,{runId:i,entry:p,input:null}).then(v).catch(F=>x(K(F))).finally(()=>f(!1))},h=a!==void 0&&n.can("runs.write")?a:void 0;return e.jsxs(e.Fragment,{children:[e.jsx(E,{"aria-controls":l?R:void 0,"aria-expanded":l,"aria-haspopup":"dialog","aria-label":"Panel menu",className:"self-center",onClick:()=>l?v():c(!0),ref:k,size:"icon-lg",title:"Menu",variant:"ghost",children:e.jsx(ft,{})}),e.jsx(Fe,{onOpenChange:(i,p)=>{!i&&P(p)||(i?c(!0):v())},open:l,children:e.jsxs(Me,{align:"end",anchor:k,"aria-label":"Panel menu",className:"w-[260px] gap-0 p-1 text-[0.8rem]",collisionPadding:8,dim:!0,id:R,role:"dialog",side:"bottom",children:[d&&h!==void 0?e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:O,onClick:()=>{b(void 0),x(void 0)},type:"button",children:[e.jsx(De,{}),"Run script"]}),e.jsxs("div",{"aria-label":"Run scripts",className:"grid max-h-[50vh] overflow-auto border-t border-border-soft pt-1",role:"list",children:[d.kind==="loading"&&e.jsx("p",{className:"px-2 py-1.5 text-muted-foreground",children:"Loading run scripts ..."}),d.kind==="ready"&&d.scripts.length===0&&!m&&e.jsx("p",{className:"px-2 py-1.5 text-muted-foreground",children:"This profile has no run scripts for you."}),d.kind==="ready"&&d.scripts.map(i=>e.jsx("button",{className:O,disabled:u||!i.available,onClick:()=>z(h,i.id),role:"listitem",title:i.reason??i.description,type:"button",children:e.jsxs("span",{className:"grid min-w-0",children:[e.jsx("span",{className:"truncate",children:i.title}),e.jsx("span",{className:"line-clamp-2 text-[0.68rem] text-muted-foreground",children:i.available?i.description:i.reason})]})},i.id))]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:O,onClick:()=>{v(),t()},type:"button",children:[e.jsx(Ue,{}),"Settings"]}),h!==void 0&&e.jsxs("button",{className:O,onClick:()=>V(h),type:"button",children:[e.jsx(vt,{}),"Run script"]}),r.kind==="vscode"&&e.jsxs("button",{className:O,onClick:()=>{v(),r.openExternal(new URL("/",window.location.href).toString())},type:"button",children:[e.jsx(pt,{}),"Open in browser"]}),n.enabled&&n.user&&e.jsxs("button",{className:O,disabled:u,onClick:g,title:n.user.id,type:"button",children:[e.jsx(mt,{}),e.jsxs("span",{className:"grid min-w-0",children:[e.jsx("span",{children:"Sign out"}),e.jsx("span",{className:"truncate text-[0.68rem] text-muted-foreground",children:n.user.label})]})]})]}),m&&e.jsx("p",{className:"px-2 py-1.5 text-[0.72rem] text-destructive",role:"alert",children:m})]})})]})}const gt=5e3;function je(t){const[a,r]=o.useState([]),[n,l]=o.useState(!1),c=o.useRef(void 0),u=o.useRef(!1),f=o.useCallback(()=>{if(!t)return Promise.resolve();if(c.current)return u.current=!0,c.current;const m=async()=>{do{u.current=!1;try{r(await He()),l(!1)}catch{l(!0)}}while(u.current)};return c.current=m().finally(()=>{c.current=void 0}),c.current},[t]);return o.useEffect(()=>{if(!t)return;f();const m=W.subscribe(Y.channels.runs,{},()=>void f()),x=setInterval(()=>void f(),gt);return()=>{m(),clearInterval(x)}},[t,f]),{sessions:a,unreachable:n,refresh:f}}const Se="relative z-[80] flex h-header flex-none items-stretch border-b border-border bg-shell shadow-bar",de="max-w-[120px] self-center truncate rounded-full bg-secondary px-2 py-0.5 text-[0.66rem] font-semibold text-muted-foreground",wt="min-w-0 flex-1 self-center truncate px-1.5 text-[0.82rem] font-semibold",bt="Stopped in the run panel",jt=5e3,S="New run",St="relative z-[80] flex h-header flex-none items-center gap-2 border-b border-border bg-shell px-3 shadow-bar",ye="m-auto max-w-[420px] p-6 text-center text-muted-foreground",yt=new Set,kt=()=>({id:crypto.randomUUID(),title:S,updatedAt:Date.now()}),ke=(t,a="Run")=>({id:t,title:a,updatedAt:Date.now()}),Nt={kind:"working",title:"Loading profile",detail:"Loading the server's interface."},Rt={kind:"working",title:"Starting run",detail:"Creating the new run."},ue={kind:"working",title:"Starting run",detail:"Starting the template."},Ct={kind:"waiting",title:"No run selected",detail:"On the Start page you start a new run or open an existing one."},Et={kind:"error",title:"No runs enabled",detail:"No runs are enabled for this user account."};function It({location:t}){const a=$e(),r=t.layout==="app";if(a.status==="loading")return e.jsx(y,{headless:r,state:Nt});if(a.status==="failed")return e.jsx(y,{headless:r,state:{kind:"error",title:"The profile could not be loaded",detail:a.error},children:e.jsx(E,{onClick:()=>window.location.reload(),size:"sm",variant:"outline",children:"Reload"})});const n=t.layout==="app"?e.jsx(Tt,{elementId:t.elementId,registry:a.registry,runId:t.runId}):e.jsx(Ot,{connection:t.connection,initialRunId:t.runId,registry:a.registry});return a.failures.length===0?n:e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsx(Be,{failures:a.failures}),e.jsx("div",{className:"flex min-h-0 flex-1 flex-col",children:n})]})}function Ot({connection:t,initialRunId:a,registry:r}){var ie;const n=B(),l=X(),c=l.can("runs.read"),u=c&&l.can("runs.write"),f=u&&l.can("runs.create"),m=f||u&&r.scriptEntries.some(s=>pe(l,s.id)),x=qe((ie=l.user)==null?void 0:ie.id),[d,b]=o.useState(a),[k,R]=o.useState(),[P,v]=o.useState(),g=o.useCallback(()=>v(s=>s===d?void 0:s),[d]),[V,z]=o.useState(),[h,i]=o.useState(),[p,F]=o.useState(),q=o.useRef(void 0),j=o.useCallback(s=>{q.current=s==null?void 0:s.runId,F(s)},[]),[ee,J]=o.useState(),[L,te]=o.useState(!1),[ne,se]=o.useState(),G=o.useCallback(()=>te(!0),[]),{sessions:re,unreachable:Re,refresh:Ce}=je(c&&!h&&!p),[Ee,Ie]=o.useState(null),[Oe,Pe]=o.useState(null),T=o.useCallback((s,w)=>{var $;const N=($=r.startEntries.find(ze=>ze.id===w))==null?void 0:$.fixedStartOptions;i({session:kt(),startOptions:s&&xe(s,N),entryId:w})},[r]),Le=o.useCallback(()=>T(),[T]),A=o.useCallback((s,w)=>{i(void 0),j(void 0),z(w),b(s),v(n.kind==="vscode"?s:void 0)},[n.kind,j]),oe=o.useCallback(s=>{i(void 0),j(s),Pt(s,r,l).then(()=>{q.current===s.runId&&A(s.runId)},w=>{q.current===s.runId&&F({...s,error:w instanceof Error?w.message:String(w)})})},[l,A,r,j]),Te=o.useCallback(s=>{v(void 0),R(void 0),b(s)},[]),M=o.useCallback(()=>{v(void 0),i(void 0),j(void 0),J(void 0),n.kind==="vscode"?n.notify({type:"showStart"}):b(void 0)},[n,j]);o.useLayoutEffect(()=>{if(P){if(L||h){g();return}return window.addEventListener("pointerdown",g,!0),window.addEventListener("keydown",g,!0),window.addEventListener("blur",g),()=>{window.removeEventListener("pointerdown",g,!0),window.removeEventListener("keydown",g,!0),window.removeEventListener("blur",g)}}},[h,P,L,g]),o.useEffect(()=>n.onCommand(s=>{if(s.type==="selectRun"&&(v(void 0),i(void 0),j(void 0),J(void 0),z(void 0),R(void 0),b(s.runId??void 0)),s.type!=="newRun")return;if(n.kind!=="vscode"){m&&T(s.startOptions,s.entryId);return}const w=m?s.entryId===void 0&&!f?"Free runs are not enabled for this user account.":void 0:"New runs are not enabled for this user account.";if(J(w),w!==void 0)return;const N=r.startEntries.find($=>$.id===s.entryId);if(R((N==null?void 0:N.title)??S),s.entryId===void 0){A(crypto.randomUUID(),s.startOptions);return}if(N!==void 0&&r.guideFor(N)!==void 0){j(void 0),T(s.startOptions,N.id);return}oe({runId:crypto.randomUUID(),entryId:s.entryId,startOptions:s.startOptions})}),[m,f,n,A,r,j,T,oe]),o.useEffect(()=>{n.notify({type:"ready"})},[n]),o.useEffect(()=>{n.notify({type:"runChanged",runId:d??null})},[n,d]);const D=L&&e.jsx(Ze,{onClose:()=>te(!1),registry:r}),I=s=>e.jsxs(e.Fragment,{children:[e.jsx(E,{"aria-label":"Back to Start",className:"self-center",onClick:M,size:"icon-lg",title:"Back to Start",variant:"ghost",children:e.jsx(le,{})}),e.jsx("strong",{className:wt,children:s}),t&&e.jsx("span",{className:de,title:`Server ${t}`,children:t}),e.jsx(Q,{onOpenSettings:G})]}),U=e.jsx(E,{onClick:M,size:"sm",variant:"outline",children:"Back to Start"}),ae=h&&e.jsx(Z,{initialStartOptions:h.startOptions,onStarted:()=>{A(h.session.id),R(s=>s??S),Ce()},registry:r,session:h.session,startDialog:{onClose:n.kind==="vscode"?M:()=>i(void 0),initialEntryId:h.entryId}},h.session.id);if(p)return e.jsxs(e.Fragment,{children:[p.error===void 0?e.jsx(y,{header:I(k??S),state:ue}):e.jsx(y,{header:I(k??S),state:{kind:"error",title:"The run could not be started",detail:p.error},children:U}),D]});if(n.kind==="vscode"&&h)return e.jsxs(e.Fragment,{children:[e.jsx(y,{header:I(k??S),state:ue}),ae,D]});if(n.kind==="vscode"&&!(c&&d))return e.jsxs(e.Fragment,{children:[c?ee!==void 0?e.jsx(y,{header:I(S),state:{kind:"error",title:"No new run possible",detail:ee},children:U}):e.jsx(Lt,{header:I(S),children:U}):e.jsx(y,{header:I(S),state:Et,children:U}),D]});const H=re.find(s=>s.id===d),Ae=()=>{d!==void 0&&(se(void 0),Xe(d,bt).catch(s=>se(`Stop failed: ${s instanceof Error?s.message:String(s)}`)))},_e=c?e.jsx(et,{activeId:d,onCreate:m?Le:void 0,onSelect:Te,onToggleSelected:()=>{},registry:r,seenRevisions:x.revisions,selectMode:!1,selectedIds:yt,sessions:re}):e.jsx("p",{className:ye,children:"No runs are enabled for this user account."});return e.jsxs("div",{className:"flex h-full flex-col bg-[image:var(--surface-backdrop)]",children:[c&&d?e.jsxs(e.Fragment,{children:[e.jsxs("header",{className:Se,children:[e.jsx(E,{"aria-label":"Back to Start",className:"self-center",onClick:M,size:"icon-lg",title:"Back to Start",variant:"ghost",children:e.jsx(le,{})}),e.jsx("div",{"aria-label":"Run title bar",className:"flex min-w-0 flex-1 items-stretch overflow-x-auto no-scrollbar",ref:Ie,role:"region"}),t&&e.jsx("span",{className:de,title:`Server ${t}`,children:t}),e.jsx(Je,{className:"self-center px-1.5",state:H!=null&&H.running?"running":"idle"}),e.jsx(Ge,{className:"self-center",disabled:!u,label:"Stop run",onClick:Ae,size:"icon-lg",title:"Stop the run with all agents and flows"}),e.jsx("div",{"aria-label":"Panel tools",className:"flex flex-none items-center empty:hidden",ref:Pe,role:"toolbar"}),e.jsx(Q,{onOpenSettings:G,runId:d})]}),ne&&e.jsx(ce,{variant:"destructive",children:ne}),e.jsx("main",{className:"relative z-1 flex min-h-0 min-w-0 flex-1 @container/chat-content",children:e.jsx(Z,{autoFocusChat:P===d&&!L&&!h,onAutoFocusChatSettled:g,toolbarContainer:Oe,headerContainer:Ee,initialStartOptions:V,layout:"panel",onViewed:x.markViewed,registry:r,session:H??ke(d,k),viewing:!h&&!L},d)})]}):e.jsxs(e.Fragment,{children:[e.jsxs("header",{className:St,children:[e.jsx(xt,{"aria-hidden":!0,className:"size-4 flex-none text-muted-foreground"}),e.jsx("strong",{className:"min-w-0 flex-1 truncate text-[0.82rem] font-semibold",children:r.brand.title}),e.jsx(Q,{onOpenSettings:G})]}),e.jsxs("div",{className:"min-h-0 flex-1 overflow-auto p-3",children:[Re&&e.jsx(ce,{className:"mb-3",variant:"destructive",children:"The server is unreachable."}),_e]})]}),D,ae]})}async function Pt(t,a,r){const n=a.startEntries.find(l=>l.id===t.entryId);if(!n)throw new Error("This template does not exist in this profile.");if(!pe(r,n.id))throw new Error(`${n.title} is not enabled for this user account.`);if(t.startOptions){const l=await Ke(t.runId),c=xe(t.startOptions,n.fixedStartOptions);for(const[u,f]of Qe(l,c))await We(t.runId,u,f)}await Ye(t.runId,n,null)}function Lt({children:t,header:a}){const[r,n]=o.useState(!1);return o.useEffect(()=>{const l=window.setTimeout(()=>n(!0),jt);return()=>window.clearTimeout(l)},[]),r?e.jsx(y,{header:a,state:Ct,children:t}):e.jsx(y,{header:a,state:Rt})}function y({children:t,header:a,headless:r=!1,state:n}){return e.jsxs("div",{className:"flex h-full flex-col bg-[image:var(--surface-backdrop)]",children:[!r&&e.jsx("header",{className:Se,children:a}),e.jsx("main",{className:"grid min-h-0 flex-1 place-items-center overflow-hidden p-6",children:e.jsx(Ve,{state:n,children:t})})]})}function Tt({elementId:t,registry:a,runId:r}){const n=B(),l=X().can("runs.read"),{sessions:c}=je(l);return o.useEffect(()=>{n.notify({type:"ready"})},[n]),l?e.jsx("main",{className:"relative flex h-full min-h-0 min-w-0 flex-1 bg-background @container/chat-content",children:e.jsx(Z,{layout:{element:t},registry:a,session:c.find(u=>u.id===r)??ke(r)},r)}):e.jsx("p",{className:ye,children:"No runs are enabled for this user account."})}function At(){const t=B();return e.jsxs(fe,{children:[e.jsx("h1",{className:"my-3 text-[1.5rem]",children:"Sign-in required"}),e.jsx("p",{className:"mb-6 leading-normal text-muted-foreground",children:"Sign-in goes through VS Code. The panel then reloads."}),e.jsx(E,{className:"w-full",onClick:()=>t.requestLogin(),children:"Sign in with VS Code"})]})}const C=t=>t!=null&&t.trim()?t.trim():void 0,_t=t=>{if(t==="browser"||t==="vscode")return t;throw new Error(`Unknown host ${JSON.stringify(t)}. Allowed are browser and vscode.`)};function zt(t){const a=new URLSearchParams(t),r=C(a.get("layout"))??"panel";if(r!=="panel"&&r!=="app")throw new Error(`Unknown layout ${JSON.stringify(r)}. Allowed are panel and app.`);const n=C(a.get("theme"));if(n!==void 0&&!tt(n))throw new Error(`Unknown theme ${JSON.stringify(n)}. Allowed are light and dark.`);const l={host:_t(C(a.get("host"))??"browser"),connection:C(a.get("connection")),theme:n,access:C(a.get(nt))},c=C(a.get("run"));if(r==="panel")return{layout:r,runId:c,...l};const u=C(a.get("element"));if(!c||!u)throw new Error("The app layout needs run and element.");return{layout:r,runId:c,elementId:u,...l}}st();const Ne=document.getElementById("root");if(!Ne)throw new Error("Root element is missing");const he=rt.createRoot(Ne);try{const t=zt(window.location.search);t.access!==void 0&&ot(t.access);const a=at(t.host,window);if(t.host==="vscode"){const n=it(window)}const r=lt(window);t.theme!==void 0&&r.setPreference(t.theme),a.onCommand(n=>{n.type==="theme"&&r.setPreference(n.theme)}),he.render(e.jsx(o.StrictMode,{children:e.jsx(ct,{children:e.jsx(dt,{value:a,children:e.jsx(ut,{value:t.host==="vscode"?{open:a.openPage}:void 0,children:e.jsx(ht,{Login:t.host==="vscode"?At:void 0,children:e.jsx(It,{location:t})})})})})}))}catch(t){he.render(e.jsxs(fe,{children:[e.jsx("h1",{className:"my-3 text-[1.5rem]",children:"The RAgents panel could not be loaded"}),e.jsx("p",{className:"mb-6 leading-normal",role:"alert",children:t instanceof Error?t.message:String(t)}),e.jsx(E,{onClick:()=>window.location.reload(),variant:"outline",children:"Reload"})]}))}
@@ -1,318 +0,0 @@
1
- import { canStartEntry } from "../../../packages/ragents/src/access";
2
- import { useAccess, UserMenu } from "./AccessContext";
3
- import { Alert, Button, Spinner } from "./ui";
4
- import { RunModalContext, WorkspaceModalContext } from "./ui/dialog";
5
- import { useCallback, useEffect, useRef, useState, type ComponentType } from "react";
6
- import { deleteSession, listSessions, type SessionInfo } from "./api";
7
- import { CircleHelpIcon, SettingsIcon, SparklesIcon } from "lucide-react";
8
- import { HelpDialog } from "./HelpDialog";
9
- import { Overview } from "./Overview";
10
- import type { OverviewPanelContext } from "./PluginRegistry";
11
- import { PluginChat } from "./PluginChat";
12
- import { chatUserLocation, type ChatRunLocation } from "./chat/user-location";
13
- import { coreContracts } from "@ragents/host/api/contracts";
14
- import { rpc } from "./rpc";
15
- import { SettingsModal } from "./SettingsModal";
16
- import { usePluginActivation } from "./PluginActivation";
17
- import { PluginFailureNotice } from "./PluginFailureNotice";
18
- import { useRunReadState } from "./run-read-state";
19
-
20
- const chatWorkspaceClass = "flex min-h-0 min-w-0 flex-1 flex-col";
21
- const headerSquareClass = "inline-flex w-header min-h-header items-center justify-center border-r border-border text-primary transition-colors duration-100 not-disabled:cursor-pointer hover:not-disabled:bg-accent focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-3 aria-expanded:bg-primary aria-expanded:text-primary-foreground aria-pressed:bg-primary aria-pressed:text-primary-foreground disabled:opacity-45";
22
-
23
- export function App() {
24
- const access = useAccess();
25
- const runReadState = useRunReadState(access.user?.id);
26
- const readRuns = access.can("runs.read");
27
- const writeRuns = readRuns && access.can("runs.write");
28
- const inspectRuns = access.can("runs.inspect");
29
- const deleteRuns = readRuns && access.can("runs.delete");
30
- const pluginActivation = usePluginActivation();
31
- const overviewButtonRef = useRef<HTMLButtonElement>(null);
32
- const settingsButtonRef = useRef<HTMLButtonElement>(null);
33
- const [runHeaderContainer, setRunHeaderContainer] = useState<HTMLDivElement | null>(null);
34
- const [workspaceHeaderContainer, setWorkspaceHeaderContainer] = useState<HTMLDivElement | null>(null);
35
- const [statusContainer, setStatusContainer] = useState<HTMLElement | null>(null);
36
- const [workspaceModalContainer, setWorkspaceModalContainer] = useState<HTMLDivElement | null>(null);
37
- const [runModalContainer, setRunModalContainer] = useState<HTMLDivElement | null>(null);
38
- const [serverSessions, setServerSessions] = useState<SessionInfo[]>([]);
39
- const [draft, setDraft] = useState<(SessionInfo & { startEntryId?: string }) | undefined>();
40
- const [activeId, setActiveId] = useState<string | undefined>();
41
- const [runLocation, setRunLocation] = useState<ChatRunLocation>();
42
- const [settingsOpen, setSettingsOpen] = useState(false);
43
- const [helpOpen, setHelpOpen] = useState(false);
44
- const [overviewOpen, setOverviewOpen] = useState(false);
45
- const [toolbarPanel, setToolbarPanel] = useState<string>();
46
- const [overviewActivated, setOverviewActivated] = useState(false);
47
- const [busyPanels, setBusyPanels] = useState<ReadonlySet<string>>(() => new Set());
48
- const [sessionsUnreachable, setSessionsUnreachable] = useState(false);
49
- const refreshing = useRef<Promise<void> | undefined>(undefined);
50
- const refreshRequested = useRef(false);
51
- const refresh = useCallback((): Promise<void> => {
52
- if (!readRuns) return Promise.resolve();
53
- if (refreshing.current) {
54
- refreshRequested.current = true;
55
- return refreshing.current;
56
- }
57
- const update = async () => {
58
- do {
59
- refreshRequested.current = false;
60
- try {
61
- setServerSessions(await listSessions());
62
- setSessionsUnreachable(false);
63
- } catch { setSessionsUnreachable(true); }
64
- } while (refreshRequested.current);
65
- };
66
- refreshing.current = update().finally(() => { refreshing.current = undefined; });
67
- return refreshing.current;
68
- }, [readRuns]);
69
-
70
- useEffect(() => {
71
- if (!readRuns || draft) return;
72
- void refresh();
73
- const unsubscribe = rpc.subscribe(coreContracts.channels.runs, {}, () => void refresh());
74
- const timer = setInterval(() => void refresh(), 5000);
75
- return () => { unsubscribe(); clearInterval(timer); };
76
- }, [draft, readRuns, refresh]);
77
-
78
- const closeOverview = useCallback((restoreFocus = false) => {
79
- setOverviewOpen(false);
80
- if (restoreFocus) overviewButtonRef.current?.focus({ preventScroll: true });
81
- }, []);
82
-
83
- // Overview contributions keep their state after the first opening.
84
- const openOverview = useCallback(() => {
85
- setToolbarPanel(undefined);
86
- setOverviewActivated(true);
87
- setOverviewOpen(true);
88
- }, []);
89
-
90
- const toggleOverview = useCallback(() => {
91
- if (overviewOpen) closeOverview(true);
92
- else openOverview();
93
- }, [closeOverview, openOverview, overviewOpen]);
94
-
95
- useEffect(() => {
96
- const handleShortcut = (event: KeyboardEvent) => {
97
- if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey
98
- || event.key.toLowerCase() !== "i" || event.metaKey === event.ctrlKey) return;
99
- const button = overviewButtonRef.current;
100
- if (!button || button.closest("[inert]")) return;
101
- event.preventDefault();
102
- toggleOverview();
103
- };
104
- window.addEventListener("keydown", handleShortcut);
105
- return () => window.removeEventListener("keydown", handleShortcut);
106
- }, [toggleOverview]);
107
-
108
- const setPanelBusy = useCallback((id: string, busy: boolean) => setBusyPanels((current) => {
109
- if (current.has(id) === busy) return current;
110
- const next = new Set(current);
111
- if (busy) next.add(id);
112
- else next.delete(id);
113
- return next;
114
- }), []);
115
-
116
- const sessions = serverSessions;
117
- const activeSession = sessions.find((session) => session.id === activeId);
118
- const openSettings = useCallback(() => { setToolbarPanel(undefined); setSettingsOpen(true); }, []);
119
- const closeHelp = useCallback(() => setHelpOpen(false), []);
120
- const closeSettings = useCallback(() => {
121
- setSettingsOpen(false);
122
- requestAnimationFrame(() => settingsButtonRef.current?.focus());
123
- }, []);
124
- const settingsAction = (
125
- <Button aria-label="Open settings" onClick={openSettings} ref={settingsButtonRef} size="icon-lg" title="Settings" variant="ghost">
126
- <SettingsIcon />
127
- </Button>
128
- );
129
-
130
- const handleOpen = (id: string) => {
131
- setActiveId(id);
132
- closeOverview(true);
133
- };
134
-
135
- const handleCreate = () => {
136
- setToolbarPanel(undefined);
137
- closeOverview(true);
138
- setDraft({ id: crypto.randomUUID(), title: "New run", updatedAt: Date.now() });
139
- };
140
-
141
- const handleDelete = async (ids: readonly string[]) => {
142
- const results = await Promise.allSettled(ids.map((id) => deleteSession(id)));
143
- const deleted = ids.filter((_, index) => results[index]!.status === "fulfilled");
144
- if (activeId !== undefined && deleted.includes(activeId)) setActiveId(undefined);
145
- await refresh();
146
- const failure = results.find((result): result is PromiseRejectedResult => result.status === "rejected");
147
- if (failure) throw failure.reason;
148
- };
149
-
150
- if (pluginActivation.status === "loading") {
151
- return <div className="m-auto p-6 text-muted-foreground">Loading profile.</div>;
152
- }
153
- if (pluginActivation.status === "failed") {
154
- return (
155
- <div className="flex h-full">
156
- <main className="relative z-1 flex min-h-0 min-w-0 flex-1 bg-[image:var(--surface-backdrop)]">
157
- <div className={chatWorkspaceClass}>
158
- <header className="flex min-h-[42px] flex-none items-center gap-3 border-b border-border-soft pr-0.5 pb-2 pl-2.5">
159
- <div className="flex min-w-0 flex-1 items-center gap-3 text-[0.82rem] font-semibold text-foreground">Profile unavailable</div>
160
- </header>
161
- <Alert className="m-auto max-w-[560px] gap-3 p-6" variant="destructive">
162
- <p>{pluginActivation.error}</p>
163
- <Button variant="outline" onClick={() => window.location.reload()}>Reload</Button>
164
- </Alert>
165
- </div>
166
- </main>
167
- </div>
168
- );
169
- }
170
-
171
- const registry = pluginActivation.registry;
172
- const availablePanels = registry.overviewPanels.filter((panel) => panel.readRight === undefined || access.can(panel.readRight));
173
- const panels = availablePanels.filter((panel) => (panel.placement ?? "overview") === "overview");
174
- const toolbarPanels = availablePanels.filter((panel) => panel.placement === "toolbar");
175
- const idlePanel = readRuns ? availablePanels.find((panel) => panel.placement === "idle") : undefined;
176
- const overviewAvailable = readRuns || panels.length > 0;
177
- const busy = panels.some((panel) => busyPanels.has(panel.id));
178
- const userLocation = chatUserLocation(readRuns ? activeId : undefined, overviewOpen, runLocation);
179
-
180
- return (
181
- <div className="flex h-full flex-col">
182
- <header className="relative z-[80] flex h-header flex-none items-stretch border-b border-border bg-shell shadow-bar">
183
- {overviewAvailable && <div className="relative flex flex-none self-stretch">
184
- <button
185
- aria-controls="app-overview"
186
- aria-expanded={overviewOpen}
187
- aria-haspopup="dialog"
188
- aria-keyshortcuts="Meta+I Control+I"
189
- aria-label={overviewOpen ? "Close overview" : "Open overview"}
190
- className={headerSquareClass}
191
- onClick={toggleOverview}
192
- ref={overviewButtonRef}
193
- title={`Overview (Cmd+I / Ctrl+I)${busy ? ": processing" : ""}`}
194
- type="button"
195
- >
196
- {busy ? <Spinner aria-hidden className="size-[18px]" /> : <SparklesIcon size={22} />}
197
- </button>
198
- </div>}
199
- {toolbarPanels.map(({ id, Panel }) => <ToolbarPanelHost
200
- id={id} key={id} onBusy={setPanelBusy}
201
- onClose={() => setToolbarPanel(undefined)}
202
- onOpen={() => { if (!settingsOpen && !helpOpen && !draft) { closeOverview(); setToolbarPanel(id); } }}
203
- open={toolbarPanel === id} Panel={Panel} registry={registry}
204
- userLocation={userLocation}
205
- />)}
206
- <div aria-label="Run title bar" className="flex min-w-0 flex-1 items-stretch overflow-x-auto no-scrollbar focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-3 max-md:in-[header:has([data-slot=overseer-toolbar][data-open=true])]:flex-[0_1_0]" ref={setRunHeaderContainer} role="region" tabIndex={0} />
207
- <UserMenu />
208
- <div className="flex min-w-0 flex-[0_1_auto] items-stretch bg-shell has-data-[workspace-state=expanded]:w-[var(--workspace-panel-width,560px)] has-data-[workspace-state=expanded]:border-l has-data-[workspace-state=expanded]:border-border">
209
- <div className="flex min-w-0 flex-1 empty:hidden" ref={setWorkspaceHeaderContainer} />
210
- <div className="flex flex-none items-center gap-1.5 px-2 max-md:gap-0.5 max-md:px-1">
211
- {access.can("settings.read") && settingsAction}
212
- {inspectRuns && <Button aria-haspopup="dialog" aria-label="Open help" onClick={() => { setToolbarPanel(undefined); setHelpOpen(true); }} size="icon-lg" title="Help" variant="ghost">
213
- <CircleHelpIcon />
214
- </Button>}
215
- </div>
216
- </div>
217
- </header>
218
- <PluginFailureNotice failures={pluginActivation.failures} />
219
- <WorkspaceModalContext.Provider value={workspaceModalContainer}>
220
- <div className="relative flex min-h-0 flex-1 flex-col" ref={setWorkspaceModalContainer}>
221
- <RunModalContext.Provider value={runModalContainer}>
222
- <div className="relative flex h-auto min-h-0 flex-1" ref={setRunModalContainer}>
223
- <main className="relative z-1 flex min-h-0 min-w-0 flex-1 bg-[image:var(--surface-backdrop)] @container/chat-content">
224
- {readRuns && activeId
225
- ? (
226
- <PluginChat
227
- headerContainer={runHeaderContainer}
228
- workspaceHeaderContainer={workspaceHeaderContainer}
229
- statusContainer={statusContainer}
230
- key={activeId}
231
- onLocationChange={setRunLocation}
232
- onViewed={runReadState.markViewed}
233
- viewing={!overviewOpen && !settingsOpen && !helpOpen && !draft && !toolbarPanel}
234
- registry={registry}
235
- session={activeSession ?? { id: activeId, title: "New run", updatedAt: Date.now() }}
236
- />
237
- )
238
- : idlePanel
239
- ? (
240
- <div className={chatWorkspaceClass}>
241
- <ToolbarPanelHost
242
- id={idlePanel.id} key={idlePanel.id} onBusy={setPanelBusy} onClose={() => {}} onOpen={() => {}}
243
- open={!overviewOpen && !settingsOpen && !helpOpen && !draft && !toolbarPanel} Panel={idlePanel.Panel} registry={registry}
244
- userLocation={userLocation}
245
- />
246
- </div>
247
- )
248
- : (
249
- <div className={chatWorkspaceClass}>
250
- <div className="m-auto text-center text-muted-foreground">{readRuns ? writeRuns ? "Open the overview at the top left to choose a run or start a new one." : "Open the overview at the top left to choose a run. You have read access." : "No runs are shared with this user account."}</div>
251
- </div>
252
- )}
253
- </main>
254
- {overviewActivated && (
255
- <Overview
256
- onOpen={openOverview}
257
- onBusy={setPanelBusy}
258
- onClose={() => closeOverview(true)}
259
- open={overviewOpen}
260
- panels={panels}
261
- registry={registry}
262
- runs={readRuns
263
- ? {
264
- activeId,
265
- canCreate: writeRuns && (access.can("runs.create") || registry.scriptEntries.some((entry) => canStartEntry(access, entry.id))),
266
- canDelete: deleteRuns,
267
- onCreate: handleCreate,
268
- onDelete: handleDelete,
269
- onOpen: handleOpen,
270
- registry,
271
- sessions,
272
- seenRevisions: runReadState.revisions,
273
- unreachable: sessionsUnreachable,
274
- }
275
- : undefined}
276
- />
277
- )}
278
- {draft && (
279
- <PluginChat
280
- key={draft.id}
281
- onStarted={() => { setActiveId(draft.id); setDraft(undefined); void refresh(); }}
282
- registry={registry}
283
- session={draft}
284
- startDialog={{ onClose: () => setDraft(undefined), initialEntryId: draft.startEntryId }}
285
- />
286
- )}
287
- {settingsOpen && <SettingsModal onClose={closeSettings} registry={registry} />}
288
- {inspectRuns && helpOpen && <HelpDialog onClose={closeHelp}
289
- sampleEntries={registry.scriptEntries.filter((entry) => canStartEntry(access, entry.id)).map((entry) => entry.id)}
290
- onStartSample={(entryId) => {
291
- if (!canStartEntry(access, entryId) || !registry.scriptEntries.some((entry) => entry.id === entryId)) return;
292
- setHelpOpen(false);
293
- setToolbarPanel(undefined);
294
- closeOverview();
295
- setDraft({ id: crypto.randomUUID(), title: "New run", updatedAt: Date.now(), startEntryId: entryId });
296
- }} />}
297
- </div>
298
- <footer
299
- aria-label="Run status bar"
300
- className="relative flex h-statusbar w-full flex-none items-stretch pl-3 before:pointer-events-none before:absolute before:inset-0 before:z-[2] before:border-t before:border-border before:bg-shell before:shadow-status before:backdrop-blur-[16px] before:content-['']"
301
- ref={setStatusContainer}
302
- />
303
- </RunModalContext.Provider>
304
- </div>
305
- </WorkspaceModalContext.Provider>
306
- </div>
307
- );
308
- }
309
-
310
- function ToolbarPanelHost({ id, onBusy, Panel, ...context }: Omit<OverviewPanelContext, "onBusy"> & {
311
- id: string;
312
- onBusy: (id: string, busy: boolean) => void;
313
- Panel: ComponentType<OverviewPanelContext>;
314
- }) {
315
- const reportBusy = useCallback((busy: boolean) => onBusy(id, busy), [id, onBusy]);
316
- useEffect(() => () => onBusy(id, false), [id, onBusy]);
317
- return <Panel {...context} onBusy={reportBusy} />;
318
- }
@@ -1,157 +0,0 @@
1
- import { PlusIcon, Trash2Icon } from "lucide-react";
2
- import { cn } from "cn";
3
- import { Button, Empty, Spinner } from "./ui";
4
- import type { SessionInfo } from "./api";
5
- import { groupRunsByActivity, runActivityNotice } from "./run-overview";
6
- import type { RunReadRevisions } from "./run-read-state";
7
- import type { PluginRegistry } from "./PluginRegistry";
8
-
9
- interface SessionListProps {
10
- sessions: SessionInfo[];
11
- seenRevisions?: RunReadRevisions;
12
- activeId: string | undefined;
13
- onSelect: (id: string) => void;
14
- onDelete?: (id: string) => void;
15
- registry: PluginRegistry;
16
- selectMode: boolean;
17
- selectedIds: ReadonlySet<string>;
18
- onToggleSelected: (id: string) => void;
19
- /** With a handler, "New run" is the first card in the list instead of a button next to it. */
20
- onCreate?: () => void;
21
- }
22
-
23
- const createCardClass = "flex min-h-[148px] min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-2 rounded-[12px] border border-dashed border-border-strong text-[0.9rem] font-semibold text-muted-foreground hover:border-primary hover:bg-[color-mix(in_srgb,var(--primary)_6%,var(--background))] hover:text-primary focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2";
24
- const gridClass = "grid list-none grid-cols-[repeat(auto-fill,minmax(min(100%,240px),1fr))] items-stretch gap-3";
25
-
26
- const formatTimestamp = (time: number): string => {
27
- const date = new Date(time);
28
- const today = new Date();
29
- const sameDay = date.toDateString() === today.toDateString();
30
- return sameDay
31
- ? date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })
32
- : date.toLocaleDateString(undefined, { day: "2-digit", month: "2-digit" });
33
- };
34
-
35
- /** Running, unread, locked and resting runs differ only in their accent color; the card layout stays the same. */
36
- const tones = {
37
- locked: {
38
- card: "border-[color-mix(in_srgb,var(--destructive)_55%,var(--border))]",
39
- head: "border-t-destructive bg-destructive/8",
40
- text: "text-destructive",
41
- },
42
- running: {
43
- card: "border-[color-mix(in_srgb,var(--info)_55%,var(--border))]",
44
- head: "border-t-info bg-info/8",
45
- text: "text-info",
46
- },
47
- unread: {
48
- card: "border-[color-mix(in_srgb,var(--primary)_55%,var(--border))]",
49
- head: "border-t-primary bg-primary/8",
50
- text: "text-primary",
51
- },
52
- idle: {
53
- card: "border-border",
54
- head: "border-t-muted-foreground bg-muted-foreground/8",
55
- text: "text-muted-foreground",
56
- },
57
- } as const;
58
-
59
- export function SessionList({
60
- sessions,
61
- seenRevisions = {},
62
- activeId,
63
- onSelect,
64
- onDelete,
65
- registry,
66
- selectMode,
67
- selectedIds,
68
- onToggleSelected,
69
- onCreate,
70
- }: SessionListProps) {
71
- const createCard = onCreate && !selectMode && <li className="flex min-w-0">
72
- <button className={createCardClass} onClick={onCreate} type="button"><PlusIcon aria-hidden className="size-5" />New run</button>
73
- </li>;
74
- if (sessions.length === 0) {
75
- return createCard
76
- ? <ul className={gridClass}>{createCard}</ul>
77
- : <Empty className="py-16 text-[0.9rem] text-muted-foreground">No runs yet.</Empty>;
78
- }
79
- return (
80
- <div className="grid gap-6.5">
81
- {groupRunsByActivity(sessions).map((group, index) => <section key={group.date} aria-label={group.label}>
82
- <h3 className="mb-3 flex items-center gap-2.5 text-[0.85rem] after:flex-1 after:border-t after:border-border-soft after:content-['']">
83
- <time dateTime={group.date}>{group.label}</time><span className="text-[0.7rem] font-normal text-muted-foreground">{group.sessions.length}</span>
84
- </h3>
85
- <ul className={gridClass}>
86
- {index === 0 && createCard}
87
- {group.sessions.map((session) => {
88
- const notice = session.locked === undefined ? runActivityNotice(session, seenRevisions[session.id]) : undefined;
89
- const tone = tones[session.locked !== undefined ? "locked" : session.running ? "running" : notice ? "unread" : "idle"];
90
- const picked = session.id === activeId || (selectMode && selectedIds.has(session.id));
91
- return (
92
- <li
93
- className={cn(
94
- "relative flex min-w-0 rounded-[12px] border bg-background",
95
- picked
96
- ? "border-primary bg-[color-mix(in_srgb,var(--primary)_8%,var(--background))]"
97
- : cn(tone.card, "hover:border-muted-foreground hover:bg-[color-mix(in_srgb,var(--primary)_3%,var(--background))]"),
98
- )}
99
- key={session.id}
100
- >
101
- {selectMode && (
102
- <input
103
- aria-label={`Select ${session.title} for deletion`}
104
- checked={selectedIds.has(session.id)}
105
- className="absolute top-[17px] left-3.5 z-1 flex-none cursor-pointer accent-primary"
106
- onChange={() => onToggleSelected(session.id)}
107
- type="checkbox"
108
- />
109
- )}
110
- <button
111
- aria-current={session.id === activeId ? "true" : undefined}
112
- aria-pressed={selectMode ? selectedIds.has(session.id) : undefined}
113
- className={cn(
114
- "flex max-h-[240px] min-h-[148px] min-w-0 flex-1 cursor-pointer flex-col items-stretch gap-2.5 overflow-hidden rounded-[11px] px-4 pb-4 text-left focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2 disabled:cursor-default",
115
- selectMode && "pl-9",
116
- )}
117
- disabled={session.locked !== undefined && !selectMode}
118
- onClick={() => (selectMode ? onToggleSelected(session.id) : onSelect(session.id))}
119
- type="button"
120
- >
121
- <span className={cn("-mx-4 flex flex-col gap-2.5 rounded-t-[11px] border-t-[3px] px-4 pt-3.5 pb-3", tone.head, selectMode && "-ml-9 pl-9")}>
122
- <span className="line-clamp-2 pr-6 text-[0.9rem] leading-[1.45] font-semibold [overflow-wrap:anywhere]" title={session.title}>{session.title}</span>
123
- <span className="flex min-h-[18px] flex-wrap items-center gap-x-3.5 gap-y-1.5">
124
- <span className={cn("flex items-center gap-[7px] text-[0.75rem]", tone.text)}>
125
- {session.running ? <Spinner aria-hidden className="size-3" /> : <span aria-hidden className="size-[7px] flex-none rounded-full bg-current" />}
126
- {session.locked !== undefined ? "Locked" : session.running ? "Running" : session.id === activeId ? "Open" : "Idle"}
127
- </span>
128
- {notice && <span className="inline-flex items-center gap-1.5 text-[0.7rem] font-semibold text-primary" title={notice === "updated" ? "New journal activity since you last viewed this run" : "Not viewed in this browser yet"}>
129
- <span aria-hidden className="size-[7px] flex-none rounded-full bg-current" />{notice === "updated" ? "New activity" : "Not viewed"}
130
- </span>}
131
- </span>
132
- </span>
133
- {session.locked !== undefined
134
- ? <span className="line-clamp-3 text-[0.75rem] leading-[1.45] text-destructive [overflow-wrap:anywhere]" title={session.locked}>{session.locked}</span>
135
- : registry.sessionMetadata.map(({ id, Metadata }) => (
136
- <Metadata key={id} placement="list" session={session} />
137
- ))}
138
- <span className="mt-auto grid gap-0.5 text-muted-foreground">
139
- {session.createdAt !== undefined && <span className="text-[0.7rem] tabular-nums">Created <time dateTime={new Date(session.createdAt).toISOString()}>
140
- {new Date(session.createdAt).toLocaleString(undefined, { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" })}
141
- </time></span>}
142
- {session.ownerLabel !== undefined && <span className="text-[0.7rem] [overflow-wrap:anywhere]">Created by {session.ownerLabel}</span>}
143
- <span className="text-[0.7rem] tabular-nums">Last activity {formatTimestamp(session.updatedAt)}</span>
144
- </span>
145
- </button>
146
- {!selectMode && onDelete && (
147
- <Button aria-label={`Delete ${session.title}`} className="absolute top-2.5 right-2.5 text-destructive hover:bg-destructive/12 hover:text-destructive" onClick={() => onDelete(session.id)} size="icon-sm" title={`Delete ${session.title}`} variant="ghost">
148
- <Trash2Icon />
149
- </Button>
150
- )}
151
- </li>
152
- ); })}
153
- </ul>
154
- </section>)}
155
- </div>
156
- );
157
- }
@@ -1,60 +0,0 @@
1
- import { useEffect, useId, useRef, useState, type ReactNode } from "react";
2
- import { ChevronLeftIcon } from "lucide-react";
3
- import { ToolbarItem } from "./Toolbar";
4
-
5
- export function SurfaceShortcuts({ children }: { children: ReactNode }) {
6
- const containerRef = useRef<HTMLDivElement>(null);
7
- const viewportRef = useRef<HTMLDivElement>(null);
8
- const contentRef = useRef<HTMLDivElement>(null);
9
- const id = useId();
10
- const [scroll, setScroll] = useState({ overflow: false, left: false, right: false });
11
-
12
- useEffect(() => {
13
- const container = containerRef.current!;
14
- const viewport = viewportRef.current!;
15
- const content = contentRef.current!;
16
- const measure = () => {
17
- const next = {
18
- overflow: content.scrollWidth > container.clientWidth + 1,
19
- left: viewport.scrollLeft > 1,
20
- right: viewport.scrollLeft + viewport.clientWidth < viewport.scrollWidth - 1,
21
- };
22
- setScroll((current) => current.overflow === next.overflow && current.left === next.left && current.right === next.right ? current : next);
23
- };
24
- const wheel = (event: WheelEvent) => {
25
- if (event.defaultPrevented || event.ctrlKey || viewport.scrollWidth <= viewport.clientWidth + 1) return;
26
- if (event.target instanceof Element && event.target.closest("[data-surface-scroll]")) return;
27
- event.preventDefault();
28
- const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
29
- const unit = event.deltaMode === WheelEvent.DOM_DELTA_LINE ? 16 : event.deltaMode === WheelEvent.DOM_DELTA_PAGE ? viewport.clientWidth : 1;
30
- viewport.scrollLeft += delta * unit;
31
- };
32
- const observer = new ResizeObserver(measure);
33
- observer.observe(container);
34
- observer.observe(viewport);
35
- observer.observe(content);
36
- viewport.addEventListener("scroll", measure);
37
- viewport.addEventListener("wheel", wheel, { passive: false });
38
- measure();
39
- return () => {
40
- observer.disconnect();
41
- viewport.removeEventListener("scroll", measure);
42
- viewport.removeEventListener("wheel", wheel);
43
- };
44
- }, []);
45
-
46
- const move = (direction: number) => {
47
- const viewport = viewportRef.current!;
48
- const item = Array.from(contentRef.current!.children).find((child) => child.getBoundingClientRect().width > 0);
49
- const step = item ? item.getBoundingClientRect().width : viewport.clientWidth / 3;
50
- viewport.scrollBy({ left: direction * step, behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "instant" : "smooth" });
51
- };
52
-
53
- return <div className="flex min-w-0 flex-1" ref={containerRef}>
54
- {scroll.overflow && <ToolbarItem as="button" aria-controls={id} aria-label="Scroll apps and actors left" className="w-8 justify-center px-0" disabled={!scroll.left} onClick={() => move(-1)} title="Scroll left" type="button"><ChevronLeftIcon size={18} /></ToolbarItem>}
55
- <div aria-label="Browse apps and actors" className="flex min-w-0 flex-1 items-stretch overflow-x-auto no-scrollbar focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-3" id={id} ref={viewportRef} role="group" tabIndex={0}>
56
- <div className="flex w-max flex-none items-stretch" ref={contentRef}>{children}</div>
57
- </div>
58
- {scroll.overflow && <ToolbarItem as="button" aria-controls={id} aria-label="Scroll apps and actors right" className="w-8 justify-center border-r-0 border-l border-border px-0 [&>svg]:rotate-180" disabled={!scroll.right} onClick={() => move(1)} title="Scroll right" type="button"><ChevronLeftIcon size={18} /></ToolbarItem>}
59
- </div>;
60
- }