@wular/coder 0.4.2 → 0.4.3

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 (320) hide show
  1. package/dist/actions-FzDNxMFf.js +20 -0
  2. package/dist/{adapter-BKVzTDnn.js → adapter-7SEiVsmp.js} +1 -1
  3. package/dist/{adapter-B0oYCI0S.js → adapter-BLEfUqoQ.js} +1 -1
  4. package/dist/{adapter-DPInwbJG.js → adapter-H4GlcKQz.js} +1 -1
  5. package/dist/{add-DRsPpQFl.js → add-B50dIJGd.js} +1 -1
  6. package/dist/{add-DkpUwhtO.js → add-D7X3gxap.js} +1 -1
  7. package/dist/{add-C2y-mVzl.js → add-DOEUHIe7.js} +1 -1
  8. package/dist/{add-CLYTF_up.js → add-QExQhTzi.js} +1 -1
  9. package/dist/{add-json-5uPnWPoD.js → add-json-Ck4KIZrT.js} +1 -1
  10. package/dist/{agent-r5mMcehf.js → agent-BPtnEf1q.js} +1 -1
  11. package/dist/{agent-hw3zKw8y.js → agent-BlQ_dNNm.js} +2 -2
  12. package/dist/{alias-_0Rwf2cA.js → alias-BwQBovke.js} +1 -1
  13. package/dist/{app-oE8G1-bi.js → app-y6BYm2eH.js} +1 -1
  14. package/dist/{approvals-DbVrsxec.js → approvals-Cc9cq5T1.js} +2 -2
  15. package/dist/{approve-DeAhFL-m.js → approve-oO9k2fnK.js} +2 -2
  16. package/dist/{apps-eujHiNca.js → apps-CU2YalX8.js} +1 -1
  17. package/dist/{archive-BCUfymIB.js → archive-C5c_HQS1.js} +2 -2
  18. package/dist/{archive-DZ6vpMHb.js → archive-CemlWlVY.js} +2 -2
  19. package/dist/archive-sweep-ehg-fWIt.js +1 -0
  20. package/dist/{args-BOEEdpWk.js → args-DcBCtXhz.js} +1 -1
  21. package/dist/{ask-BU2WVxVM.js → ask-Chu7Twu_.js} +2 -2
  22. package/dist/auth-BOyXPOtu.js +1 -0
  23. package/dist/auth-XbqKSbcD.js +1 -0
  24. package/dist/{chat-bridge-BS9WtP3X.js → chat-bridge-DXgZItGP.js} +1 -1
  25. package/dist/{chat-B_yhNTo0.js → chat-cknlIhVh.js} +1 -1
  26. package/dist/{cli-DWCR7iqa.js → cli-B1ObVrL4.js} +5 -5
  27. package/dist/cli.d.ts +1 -1
  28. package/dist/cli.js +1 -1
  29. package/dist/{client-D9IoAuDD.js → client-31YzOj-i.js} +1 -1
  30. package/dist/{config-12pK4qW7.js → config-DAE_qHYA.js} +1 -1
  31. package/dist/{create-D3f6q0Yq.js → create-CM8B8JxD.js} +1 -1
  32. package/dist/credentials-BXD4Qptk.js +1 -0
  33. package/dist/dash/manifest.json +949 -915
  34. package/dist/dash/public/assets/chunks/{agent-pills-dd0b7643e125bbf1.js → agent-pills-b6a6da91c9559d9f.js} +1 -1
  35. package/dist/dash/public/assets/chunks/{agent-settings-form-45ae6aeaaed2b568.js → agent-settings-form-b615c677046cd6f2.js} +1 -1
  36. package/dist/dash/public/assets/chunks/agents-grid-f2e7b4ff9e11b6e3.js +1 -0
  37. package/dist/dash/public/assets/chunks/{badge-676b01e0c615bf7b.js → badge-215c7b2631dafc69.js} +1 -1
  38. package/dist/dash/public/assets/chunks/{card-e0901d68b79dfa93.js → card-e86dcc81326d2b14.js} +1 -1
  39. package/dist/dash/public/assets/chunks/{combo-952a457d52360d88.js → combo-09e246dc9a517fa8.js} +1 -1
  40. package/dist/dash/public/assets/chunks/composer-417166b029f48c47.js +1 -0
  41. package/dist/dash/public/assets/chunks/composer-645ae8fa97a35280.js +1 -0
  42. package/dist/dash/public/assets/chunks/{confirm-f2fe4ba9905a89c6.js → confirm-5ae484241ad888fd.js} +1 -1
  43. package/dist/dash/public/assets/chunks/{connect-apps-661f3b3a85f1776a.js → connect-apps-4147c45f45fde2b1.js} +1 -1
  44. package/dist/dash/public/assets/chunks/{connect-repositories-5389ec8701a3deac.js → connect-repositories-77591110836bdf03.js} +1 -1
  45. package/dist/dash/public/assets/chunks/credentials-list-e005a0783ec93465.js +2 -0
  46. package/dist/dash/public/assets/chunks/{custom-range-03ba6fb2ade7d282.js → custom-range-463e119af4681c61.js} +1 -1
  47. package/dist/dash/public/assets/chunks/{editor-34cb529d6d8a4b09.js → editor-804b84f4fe6539cd.js} +4 -4
  48. package/dist/dash/public/assets/chunks/{engines-list-2f1acee3ecc8461a.js → engines-list-ceab51f13c690e8c.js} +1 -1
  49. package/dist/dash/public/assets/chunks/{field-901f1f35c18c39c9.js → field-aa0a4b98a976781e.js} +1 -1
  50. package/dist/dash/public/assets/chunks/filter-91144ac2eec6297f.js +1 -0
  51. package/dist/dash/public/assets/chunks/format-105c706345987fb2.js +1 -0
  52. package/dist/dash/public/assets/chunks/heads-a42817b32d2d7f08.js +1 -0
  53. package/dist/dash/public/assets/chunks/{instructions-052fa45a9daf6db6.js → instructions-3a1dcaff2d868aa5.js} +1 -1
  54. package/dist/dash/public/assets/chunks/instructions-6c6f981fffaa31e4.js +1 -0
  55. package/dist/dash/public/assets/chunks/{list-87b359bebd26986a.js → list-7270361464fc3c04.js} +1 -1
  56. package/dist/dash/public/assets/chunks/{mcp-list-b42f0d2ee463b37d.js → mcp-list-2e715d48666b54ad.js} +1 -1
  57. package/dist/dash/public/assets/chunks/{models-list-0669d26fd662aec3.js → models-list-d3c40f94c761a6e5.js} +1 -1
  58. package/dist/dash/public/assets/chunks/paged-3ddb6077bef42752.js +1 -0
  59. package/dist/dash/public/assets/chunks/{platform-actions-c92ca3c6198a9387.js → platform-actions-271ec3b99a64280b.js} +1 -1
  60. package/dist/dash/public/assets/chunks/runners-list-767e49c7d0ec833a.js +1 -0
  61. package/dist/dash/public/assets/chunks/{segmented-358ff2b4992b8384.js → segmented-de4386ccaac7304d.js} +1 -1
  62. package/dist/dash/public/assets/chunks/{select-1013702bdfb1ade4.js → select-a90307706cffa6e0.js} +1 -1
  63. package/dist/dash/public/assets/chunks/{sentinel-c972a33dc69a5135.js → sentinel-b5e032b38604dfd3.js} +1 -1
  64. package/dist/dash/public/assets/chunks/shared-init-7f81ae7bc8c82438.js +2 -0
  65. package/dist/dash/public/assets/chunks/shared-init-933c7f9ee85be766.js +5 -0
  66. package/dist/dash/public/assets/chunks/{tail-7c27aab8ba268bd9.js → tail-4891e727ee526792.js} +1 -1
  67. package/dist/dash/public/assets/chunks/task-actions-ce854925a2dc78fd.js +1 -0
  68. package/dist/dash/public/assets/chunks/task-ddfddeaf07fb684c.js +1 -0
  69. package/dist/dash/public/assets/chunks/task-view-c37dcee08f21a279.js +5 -0
  70. package/dist/dash/public/assets/chunks/tasks-list-baef587999f322d8.js +1 -0
  71. package/dist/dash/public/assets/chunks/{toolbar-f964fa864699f095.js → toolbar-8d98313606ad8a2d.js} +1 -1
  72. package/dist/dash/public/assets/chunks/{versions-list-fc2e0d2a7382e049.js → versions-list-532690efd9f1b0ea.js} +1 -1
  73. package/dist/dash/public/assets/{dash-account-96fc41be318839fa.css → dash-account-edc56e85dbaaabe0.css} +1 -1
  74. package/dist/dash/public/assets/dash-agents-edba57b67e4005d0.css +1 -0
  75. package/dist/dash/public/assets/dash-agents-new-fe22253fbada2fbf.css +1 -0
  76. package/dist/dash/public/assets/dash-agents-param-slug-adf52e9dacb2d825.css +1 -0
  77. package/dist/dash/public/assets/dash-agents-param-slug-edit-a2937a347689ef29.css +1 -0
  78. package/dist/dash/public/assets/dash-agents-param-slug-platforms-265ba13db32a460d.css +1 -0
  79. package/dist/dash/public/assets/dash-agents-param-slug-settings-8afc52eb0372bc4b.css +1 -0
  80. package/dist/dash/public/assets/dash-agents-param-slug-tasks-6193e646be68dbd2.css +1 -0
  81. package/dist/dash/public/assets/dash-agents-param-slug-usage-ceadb3a1c911c402.css +1 -0
  82. package/dist/dash/public/assets/{dash-settings-53b8e6a8f656fa1a.css → dash-settings-cf1997cd7f72e518.css} +1 -1
  83. package/dist/dash/public/assets/dash-settings-param-tab-b44dac8e79a560e6.css +1 -0
  84. package/dist/dash/public/assets/dash-tasks-916c7100a8058aa3.css +1 -0
  85. package/dist/dash/public/assets/dash-tasks-param-id-194a6ffc20e872f2.css +1 -0
  86. package/dist/dash/public/assets/{dash-usage-884943221f76ad93.css → dash-usage-40940b289f85e0b6.css} +1 -1
  87. package/dist/dash/public/assets/pnext-client-13aee92bef5c9e3f.js +1 -0
  88. package/dist/dash/public/assets/pnext-client-1f2686b2044f0df6.js +1 -0
  89. package/dist/dash/public/assets/pnext-client-26130234ebad7067.js +1 -0
  90. package/dist/dash/public/assets/pnext-client-311e35b0076bf406.js +1 -0
  91. package/dist/dash/public/assets/pnext-client-4ade1bf75c9c3d87.js +1 -0
  92. package/dist/dash/public/assets/pnext-client-61cd31b687ede1da.js +1 -0
  93. package/dist/dash/public/assets/{pnext-client-2693db10b197ade1.js → pnext-client-61db65dc4223217a.js} +1 -1
  94. package/dist/dash/public/assets/pnext-client-6ea656e7705e3f2a.js +1 -0
  95. package/dist/dash/public/assets/pnext-client-7fd0f793f0322cb9.js +1 -0
  96. package/dist/dash/public/assets/pnext-client-88159c0840fcfa4a.js +1 -0
  97. package/dist/dash/public/assets/pnext-client-b03f0c47c066b4f3.js +1 -0
  98. package/dist/dash/public/assets/{pnext-client-1afa4148bea69ad2.js → pnext-client-d6478835f543f901.js} +1 -1
  99. package/dist/dash/public/assets/pnext-client-dadb8359021ed590.js +1 -0
  100. package/dist/dash/public/assets/pnext-client-de07532d6aaebf2c.js +1 -0
  101. package/dist/dash/public/assets/pnext-client-f87a31890c8ff224.js +1 -0
  102. package/dist/dash/public/assets/pnext-client-ffe1c123a8e2a350.js +1 -0
  103. package/dist/{default-CwQXCtoB.js → default-BMvUMTFp.js} +1 -1
  104. package/dist/default-B_nKaX0-.js +1 -0
  105. package/dist/{delete-cS01oOIb.js → delete-Bjteibld.js} +2 -2
  106. package/dist/{delete-BD6AnCLg.js → delete-riCIKZwj.js} +2 -2
  107. package/dist/{disable-BMWWeYFQ.js → disable-DQC56YFy.js} +1 -1
  108. package/dist/{discover-Dvr_3_Xr.js → discover-Qanr5TEO.js} +1 -1
  109. package/dist/{discover-Cfx5RPU7.js → discover-lgTYwoQp.js} +2 -2
  110. package/dist/dispatch-BsW_1m7e.js +14 -0
  111. package/dist/{docs-DWegDRiu.js → docs-DFs7lOJp.js} +1 -1
  112. package/dist/{docs-BX65z2Iu.js → docs-DKROso-4.js} +2 -2
  113. package/dist/{enable-Cfzeau2x.js → enable-D2DqZvcU.js} +1 -1
  114. package/dist/{env-CRf6wkM2.js → env-YRY3I60r.js} +1 -1
  115. package/dist/exec-DuUkLMqg.js +11 -0
  116. package/dist/executor-vMppVLB6.js +10 -0
  117. package/dist/flow/builtin/review.js +1 -1
  118. package/dist/flow/index.d.ts +1 -1
  119. package/dist/flow/index.js +1 -1
  120. package/dist/{flow-kAxxVOiR.js → flow-DmERAnR8.js} +1 -1
  121. package/dist/{github-actions-DwUzm4hC.js → github-actions-C_NkHVJq.js} +1 -1
  122. package/dist/http-D01Udu4u.js +1 -0
  123. package/dist/{index-CZjFX5zV.d.ts → index-B1L4zXZH.d.ts} +2 -1
  124. package/dist/{init-C4tDFH4G.js → init-Bd6JHzBc.js} +1 -1
  125. package/dist/integrations-B2CavXR1.js +5 -0
  126. package/dist/integrations-D1Dwy1kt.js +1 -0
  127. package/dist/lib/broker.js +2 -2
  128. package/dist/{list-DuPqU3fk.js → list-4Uo-0v9s.js} +1 -1
  129. package/dist/{list-COAo7eZd.js → list-B0ujEZhd.js} +3 -3
  130. package/dist/{list-5Ljf9TxF.js → list-B5oa1Bhy.js} +2 -2
  131. package/dist/{list-vOvIpbOH.js → list-BjE_l6la.js} +1 -1
  132. package/dist/{list-J8d_zh-o.js → list-Ck9ibCs1.js} +1 -1
  133. package/dist/{list-vpyS-hv8.js → list-DhgK4Zc4.js} +1 -1
  134. package/dist/{list-way5oLO0.js → list-KvL7AUhD.js} +2 -2
  135. package/dist/{list-DzGuMkQy.js → list-byYLBl1U.js} +1 -1
  136. package/dist/{load-BpEsAn51.js → load-SnZd7xG1.js} +1 -1
  137. package/dist/{local-H3uvqEbq.js → local-BRy1EUq2.js} +1 -1
  138. package/dist/log-view-DeJstSkw.js +3 -0
  139. package/dist/{login-BBw95-S3.js → login-C-ahtDKV.js} +1 -1
  140. package/dist/{login-CNmeRAL7.js → login-zpwke8g1.js} +1 -1
  141. package/dist/{logout-j0dTo_Ib.js → logout-CI6Kf-T6.js} +1 -1
  142. package/dist/{maintenance-DkLVtqM7.js → maintenance-BAzr9Jlr.js} +1 -1
  143. package/dist/{match-CXAjDhg7.js → match-DxV1d49D.js} +1 -1
  144. package/dist/{mcp-BgKb2Cdk.js → mcp-CmcL1MCG.js} +1 -1
  145. package/dist/{mcp-CsmBtlW9.js → mcp-mhXwtGQT.js} +1 -1
  146. package/dist/{migrate-N-85_N7J.js → migrate-Czh3TEK5.js} +1 -1
  147. package/dist/{migrate-D4nzYhk8.js → migrate-w7KfzUAR.js} +1 -1
  148. package/dist/{model-B79MFtRV.js → model-JRoTH9QK.js} +1 -1
  149. package/dist/models-C-S7HqSb.js +1 -0
  150. package/dist/{prompt-CEuBMNhk.js → prompt-CMXmt2l9.js} +4 -4
  151. package/dist/{push-DbdnbZYt.js → push-B8BhTyZs.js} +1 -1
  152. package/dist/queue-CirjEskI.js +117 -0
  153. package/dist/refresh-Bmy2r1Ee.js +1 -0
  154. package/dist/{remote-miQNTVRa.js → remote-CxFnEjZl.js} +1 -1
  155. package/dist/{remove-BNzbUhaK.js → remove-C15WLaXT.js} +1 -1
  156. package/dist/{remove-D-QeeZQL.js → remove-CTZYjjSr.js} +1 -1
  157. package/dist/{remove-CLpL12Ya.js → remove-CTl-H0f0.js} +1 -1
  158. package/dist/{remove-CaYH9Cna.js → remove-DX2FVo5b.js} +1 -1
  159. package/dist/rename-D1g4TnHQ.js +1 -0
  160. package/dist/{result-BfTfJayl.js → result-D49VN8PM.js} +3 -3
  161. package/dist/result-DGWyUHYU.js +18 -0
  162. package/dist/{resume-aZ8yWDBh.js → resume-ywzc7z6A.js} +2 -2
  163. package/dist/{review-BEhDcX4b.js → review-ZC0xG8XY.js} +2 -2
  164. package/dist/{rotate-key-BqnKhTK8.js → rotate-key-Bf8_99ck.js} +1 -1
  165. package/dist/{run-BWbabvub.js → run-D7SJ-ieT.js} +2 -2
  166. package/dist/run-DoRQSC_K.js +2 -0
  167. package/dist/{run-BdPjxaZU.js → run-ukCf2Mdy.js} +1 -1
  168. package/dist/runner-D-REfOUe.js +1 -0
  169. package/dist/{runner-sUpWJ_k0.js → runner-wDYhkVsM.js} +1 -1
  170. package/dist/schema-DoNO0dPh.js +1 -0
  171. package/dist/sdk.d.ts +4 -2
  172. package/dist/sdk.js +1 -1
  173. package/dist/serve-BJ_Mbm2m.js +8 -0
  174. package/dist/{serve-C0WC-QrV.js → serve-C1MMhM1F.js} +2 -2
  175. package/dist/{serve-CcObDKKQ.js → serve-DtrOHaoS.js} +2 -2
  176. package/dist/{serve-Ck95047c.js → serve-P9pFkEnE.js} +1 -1
  177. package/dist/{serve-BTeomYxW.js → serve-yjSw0B1a.js} +1 -1
  178. package/dist/{server-BxLFZL80.js → server-DDWTnmbx.js} +1 -1
  179. package/dist/{setup-host-DLd_j2Uw.js → setup-host-L6Rnn5Cz.js} +1 -1
  180. package/dist/{shared-BLkGRKty.js → shared-XsUX_ZZb.js} +1 -1
  181. package/dist/{show-DzoQIs75.js → show-CU-jqJ90.js} +1 -1
  182. package/dist/{show-FC3dJcD-.js → show-DVJmB2u-.js} +1 -1
  183. package/dist/{status-BU3DR1XU.js → status-CBydR7LE.js} +1 -1
  184. package/dist/{steer-P_38hRRD.js → steer-Cm_sHZqs.js} +2 -2
  185. package/dist/{stop-BJGDRPsp.js → stop-DJS7DAPJ.js} +1 -1
  186. package/dist/{stop-BWY6k6v5.js → stop-TrebIIuv.js} +2 -2
  187. package/dist/store-BrNMqJgk.js +42 -0
  188. package/dist/store-j8icrG0v.js +19 -0
  189. package/dist/task-C8lmcNsf.js +1 -0
  190. package/dist/{task-DeglY79z.js → task-CpUI19o2.js} +4 -4
  191. package/dist/task-CvSqiwxo.js +1 -0
  192. package/dist/{test-BrSrhop9.js → test-CDKz4jY_.js} +1 -1
  193. package/dist/{tools-CgswhJzh.js → tools-DBFeoosk.js} +1 -1
  194. package/dist/{types-I5Cwtl_K.d.ts → types-DMGBYB_K.d.ts} +3 -1
  195. package/dist/{unalias-mMHXqok7.js → unalias-BfQFBnuK.js} +1 -1
  196. package/dist/{update-DbOWToPy.js → update-C4FFOie8.js} +1 -1
  197. package/dist/{upgrade-D6oC9HQI.js → upgrade-DLJqMnUQ.js} +2 -2
  198. package/dist/{usage-Cb8Dtv9V.js → usage-CSh-xwQ2.js} +1 -1
  199. package/dist/{usage-DMs-IIJC.js → usage-D8mc4qXK.js} +1 -1
  200. package/dist/{watch-BcMQXGYO.js → watch-CQu-tJKZ.js} +2 -2
  201. package/dist/{watch-eE2XiwsC.js → watch-DmwaEvoi.js} +2 -2
  202. package/dist/{wire-BeT1Axeo.js → wire-DzGLZ3-O.js} +1 -1
  203. package/dist/worker-BLKTMLqa.js +1 -0
  204. package/dist/worker-nHemduM_.js +9 -0
  205. package/dist/{workflow-Cshghoas.js → workflow-IrouAus_.js} +1 -1
  206. package/docs/mcp.md +1 -1
  207. package/migrations/0000_initial.sql +8 -0
  208. package/migrations/meta/0000_snapshot.json +75 -1
  209. package/migrations/meta/_journal.json +1 -1
  210. package/package.json +1 -1
  211. package/plugins/agents/skills/coder/SKILL.md +1 -0
  212. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  213. package/plugins/claude/skills/dispatch/SKILL.md +1 -1
  214. package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
  215. package/plugins/cursor/skills/coder/SKILL.md +1 -0
  216. package/src/client/index.ts +10 -2
  217. package/src/client/types.ts +18 -0
  218. package/src/core/defaults.ts +11 -0
  219. package/src/core/task/log-view.ts +120 -6
  220. package/src/server/dash/app/dash/agents/(list)/agent-menu.tsx +4 -0
  221. package/src/server/dash/app/dash/agents/(list)/agents-grid.tsx +18 -35
  222. package/src/server/dash/app/dash/agents/(list)/loading.tsx +7 -1
  223. package/src/server/dash/app/dash/agents/[slug]/(tabs)/layout.tsx +2 -1
  224. package/src/server/dash/app/dash/layout.tsx +13 -3
  225. package/src/server/dash/app/dash/settings/[tab]/credentials-list.tsx +20 -13
  226. package/src/server/dash/app/dash/settings/[tab]/runners-list.tsx +13 -12
  227. package/src/server/dash/app/dash/tasks/[id]/activity.tsx +49 -4
  228. package/src/server/dash/app/dash/tasks/[id]/task-actions.tsx +43 -6
  229. package/src/server/dash/app/dash/tasks/[id]/task-view.css +52 -31
  230. package/src/server/dash/app/dash/tasks/[id]/task-view.tsx +143 -51
  231. package/src/server/dash/app/dash/tasks/composer.css +4 -0
  232. package/src/server/dash/app/dash/tasks/composer.tsx +6 -5
  233. package/src/server/dash/app/dash/tasks/list/task.ts +29 -1
  234. package/src/server/dash/app/dash/tasks/list/tasks-list.css +20 -7
  235. package/src/server/dash/app/dash/tasks/list/tasks-list.tsx +256 -109
  236. package/src/server/dash/app/dash/tasks/list/tasks-section.tsx +9 -13
  237. package/src/server/dash/app/dash/tasks/page.tsx +3 -35
  238. package/src/server/dash/comps/frame/loading-card.tsx +4 -3
  239. package/src/server/dash/comps/frame/page-head.css +10 -0
  240. package/src/server/dash/comps/frame/page-head.tsx +3 -2
  241. package/src/server/dash/comps/frame/task-toasts.tsx +196 -50
  242. package/src/server/dash/comps/nav/side-toggle.tsx +15 -4
  243. package/src/server/dash/comps/ui/card.css +1 -0
  244. package/src/server/dash/comps/ui/filter.css +64 -0
  245. package/src/server/dash/comps/ui/filter.tsx +336 -0
  246. package/src/server/dash/comps/ui/icons.ts +2 -0
  247. package/src/server/dash/comps/ui/menu.tsx +4 -0
  248. package/src/server/dash/comps/ui/select.tsx +2 -2
  249. package/src/server/dash/comps/ui/toolbar.css +14 -0
  250. package/src/server/dash/utils/client.ts +21 -0
  251. package/src/server/dash/utils/format.ts +12 -4
  252. package/src/server/dash/utils/paged.ts +14 -6
  253. package/dist/actions-Mk3FSlpJ.js +0 -20
  254. package/dist/archive-sweep-Cxbh8yAI.js +0 -1
  255. package/dist/auth-CBWTmAPA.js +0 -1
  256. package/dist/auth-Dmn2U7rr.js +0 -1
  257. package/dist/credentials-l6dsrZLL.js +0 -1
  258. package/dist/dash/public/assets/chunks/agents-grid-b906da80c31fc035.js +0 -1
  259. package/dist/dash/public/assets/chunks/composer-49f9e7c5c4dfda3b.js +0 -1
  260. package/dist/dash/public/assets/chunks/composer-f331d00837143aee.js +0 -1
  261. package/dist/dash/public/assets/chunks/credentials-list-e6177d0943517929.js +0 -2
  262. package/dist/dash/public/assets/chunks/format-eb03fc9d9744d960.js +0 -1
  263. package/dist/dash/public/assets/chunks/heads-1570a3a6db331979.js +0 -1
  264. package/dist/dash/public/assets/chunks/instructions-d0a4b0d95009c262.js +0 -1
  265. package/dist/dash/public/assets/chunks/paged-bc6fa0c2b9d52ed7.js +0 -1
  266. package/dist/dash/public/assets/chunks/runners-list-9fffa50432c470c9.js +0 -1
  267. package/dist/dash/public/assets/chunks/shared-init-20e199043ff5d00f.js +0 -2
  268. package/dist/dash/public/assets/chunks/shared-init-88cce66bb3cfe1e5.js +0 -5
  269. package/dist/dash/public/assets/chunks/task-actions-a9603d520434baab.js +0 -1
  270. package/dist/dash/public/assets/chunks/task-fa8b82e95e771c4a.js +0 -1
  271. package/dist/dash/public/assets/chunks/task-view-f7039cb330bcb0b5.js +0 -3
  272. package/dist/dash/public/assets/chunks/tasks-list-2a905b4a0c3a9042.js +0 -1
  273. package/dist/dash/public/assets/dash-agents-290a7b21c7dc0c92.css +0 -1
  274. package/dist/dash/public/assets/dash-agents-new-a7a07bf4ebda24c2.css +0 -1
  275. package/dist/dash/public/assets/dash-agents-param-slug-69a72fb0bbd14cc2.css +0 -1
  276. package/dist/dash/public/assets/dash-agents-param-slug-edit-397bdfb2f0ca328c.css +0 -1
  277. package/dist/dash/public/assets/dash-agents-param-slug-platforms-a7b3b7247c27f1d2.css +0 -1
  278. package/dist/dash/public/assets/dash-agents-param-slug-settings-8389b7d0555e8ccf.css +0 -1
  279. package/dist/dash/public/assets/dash-agents-param-slug-tasks-5987b8aea5e8f30d.css +0 -1
  280. package/dist/dash/public/assets/dash-agents-param-slug-usage-19abc69e1d4e6b0a.css +0 -1
  281. package/dist/dash/public/assets/dash-settings-param-tab-ac61fdfdd6fd533f.css +0 -1
  282. package/dist/dash/public/assets/dash-tasks-cc0b1d32874b731c.css +0 -1
  283. package/dist/dash/public/assets/dash-tasks-param-id-02b8c7bd99cc1090.css +0 -1
  284. package/dist/dash/public/assets/pnext-client-17ba55bb57a1f4da.js +0 -1
  285. package/dist/dash/public/assets/pnext-client-43f52a07f8067b1b.js +0 -1
  286. package/dist/dash/public/assets/pnext-client-4594b693fe612835.js +0 -1
  287. package/dist/dash/public/assets/pnext-client-73f73e24f8141fb6.js +0 -1
  288. package/dist/dash/public/assets/pnext-client-7574da4651b40eae.js +0 -1
  289. package/dist/dash/public/assets/pnext-client-7b3a2c1092cbbb02.js +0 -1
  290. package/dist/dash/public/assets/pnext-client-816fe5fc96d3f768.js +0 -1
  291. package/dist/dash/public/assets/pnext-client-86cb956b448b0887.js +0 -1
  292. package/dist/dash/public/assets/pnext-client-8b64e3c030164288.js +0 -1
  293. package/dist/dash/public/assets/pnext-client-8f6161084c595a22.js +0 -1
  294. package/dist/dash/public/assets/pnext-client-9203ec11926e5d04.js +0 -1
  295. package/dist/dash/public/assets/pnext-client-a38b446f941d44df.js +0 -1
  296. package/dist/dash/public/assets/pnext-client-bd8d51aaa095e8a4.js +0 -1
  297. package/dist/dash/public/assets/pnext-client-e9821d1f1bdb281d.js +0 -1
  298. package/dist/default-De8FXiWz.js +0 -1
  299. package/dist/dispatch-BKeAch9E.js +0 -14
  300. package/dist/exec-RZFLclKX.js +0 -11
  301. package/dist/executor-BvAA7Cgw.js +0 -10
  302. package/dist/http-Bkg_OQ6r.js +0 -1
  303. package/dist/integrations-BAgdVDhj.js +0 -5
  304. package/dist/integrations-D-L3uQRj.js +0 -1
  305. package/dist/log-view-dg0TlKLA.js +0 -1
  306. package/dist/models-_5LVGC3m.js +0 -1
  307. package/dist/queue-C9fSdE_X.js +0 -116
  308. package/dist/refresh-BDmap1RA.js +0 -1
  309. package/dist/rename-Bhcb4t9F.js +0 -1
  310. package/dist/result-D8DsvJDu.js +0 -15
  311. package/dist/run-Dqe38jX8.js +0 -2
  312. package/dist/runner-BPpiJvHx.js +0 -1
  313. package/dist/schema-CU2iFkp8.js +0 -1
  314. package/dist/serve-DA5I2qXj.js +0 -4
  315. package/dist/store-B3ZHoBJY.js +0 -8
  316. package/dist/store-BLi19UDE.js +0 -42
  317. package/dist/task-C6VAg5WU.js +0 -1
  318. package/dist/task-b2BK481M.js +0 -1
  319. package/dist/worker-CXndxmGF.js +0 -9
  320. package/dist/worker-DrucI22w.js +0 -1
@@ -2,15 +2,33 @@
2
2
 
3
3
  import './tasks-list.css';
4
4
 
5
- import { useEffect, useState } from 'preact/hooks';
5
+ import { dynamic } from '@wular/pnext/dynamic';
6
+ import { useEffect, useRef, useState } from 'preact/hooks';
6
7
 
7
8
  import { client } from '@/utils/client';
8
9
  import { formatCount, formatDate, formatDay, formatDuration } from '@/utils/format';
9
- import { iClock, iDots, iMonitor, iPlug, iTerminal } from '@/comps/ui/icons';
10
- import { Menu, MenuItem, MenuRadio } from '@/comps/ui/menu';
10
+ import {
11
+ iAgent,
12
+ iClock,
13
+ iDots,
14
+ iFolder,
15
+ iMonitor,
16
+ iPlug,
17
+ iPlus,
18
+ iTerminal,
19
+ } from '@/comps/ui/icons';
20
+ import { Menu, MenuItem } from '@/comps/ui/menu';
11
21
  import { Link } from '@wular/pnext/link';
22
+ import { useRouter } from '@wular/pnext/navigation/client';
12
23
  import { usePaged } from '@/utils/paged';
13
- import type { TaskCounts, TaskRow, TasksPage } from '@coder/client/types';
24
+ import type {
25
+ IntegrationInfo,
26
+ TaskCounts,
27
+ TaskEvent,
28
+ TaskRow,
29
+ TasksPage,
30
+ } from '@coder/client/types';
31
+ import { BrandIcon } from '@/app/dash/agents/agent/agent-pills';
14
32
  import { Card, Empty } from '@/comps/ui/card';
15
33
  import { Id } from '@/comps/ui/badge';
16
34
  import { ConfirmDialog } from '@/comps/ui/confirm';
@@ -19,8 +37,8 @@ import { approvalParts } from '@/app/dash/tasks/[id]/task-actions';
19
37
  import { reasonText } from '@/utils/format';
20
38
  import { Status } from './status';
21
39
  import { Icon } from '@/comps/ui/icon';
22
- import { Select } from '@/comps/ui/select';
23
- import { taskActive } from '@/app/dash/tasks/list/task';
40
+ import { Filter, type Facet } from '@/comps/ui/filter';
41
+ import { addRows, listOrder, taskActive, withAdded } from '@/app/dash/tasks/list/task';
24
42
  import { taskTitle } from '@/app/dash/tasks/list/task';
25
43
  import { LIST_PAGE } from '@/utils/paged';
26
44
  import { Search, Toolbar, queryOf, useFilters } from '@/comps/ui/toolbar';
@@ -33,6 +51,20 @@ import {
33
51
  sourceLabel,
34
52
  } from '@/app/dash/tasks/list/task';
35
53
  import { NeedsCredential } from '@/app/dash/tasks/composer';
54
+ import { useTaskEvents } from '@/comps/frame/task-toasts';
55
+
56
+ const NewTaskButton = dynamic(
57
+ () => import('@/app/dash/tasks/composer').then(m => m.NewTaskButton),
58
+ {
59
+ // TODO: stays disabled after client navigation until pnext SSRs nested dynamic() like Next.js.
60
+ loading: () => (
61
+ <button type="button" class="btn new-task" disabled>
62
+ <Icon d={iPlus} />
63
+ New task
64
+ </button>
65
+ ),
66
+ },
67
+ );
36
68
 
37
69
  type Keys = 'q' | 'status' | 'source' | 'agent';
38
70
 
@@ -41,9 +73,7 @@ const SOURCE_ICONS: Record<string, string> = {
41
73
  cli: iTerminal,
42
74
  schedule: iClock,
43
75
  };
44
-
45
- /** How often a list with running tasks refreshes their status. */
46
- const LIVE_MS = 4000;
76
+ const sourceIcon = (source: string) => SOURCE_ICONS[source] ?? iPlug;
47
77
 
48
78
  /** How long ago, under a day; the date after that. */
49
79
  function ago(value: number, now: number, tz: string) {
@@ -56,6 +86,7 @@ function ago(value: number, now: number, tz: string) {
56
86
 
57
87
  function TaskRowView({
58
88
  task,
89
+ brand,
59
90
  back,
60
91
  scoped,
61
92
  tz,
@@ -64,6 +95,8 @@ function TaskRowView({
64
95
  onError,
65
96
  }: {
66
97
  task: TaskRow;
98
+ /** The source platform's brand art, when it has one. */
99
+ brand?: IntegrationInfo['brand'];
67
100
  onDone: (what: RowAction) => void;
68
101
  onError: (message: string) => void;
69
102
  /** The list to return to, when it is not the plain Tasks page. */
@@ -90,28 +123,37 @@ function TaskRowView({
90
123
  {taskTitle(task)}
91
124
  </Link>
92
125
  <span class="sub">
93
- {scoped ? null : `${task.task.agent} · `}
126
+ {scoped ? null : (
127
+ <>
128
+ <Icon d={iAgent} />
129
+ {`${task.task.agent} · `}
130
+ </>
131
+ )}
94
132
  <Id value={task.task.id} />
95
133
  {kind ? ` · ${kind}` : null}
96
134
  {task.task.cwd ? (
97
- <span title={task.task.cwd}>{` · ${folderName(task.task.cwd)}`}</span>
135
+ <>
136
+ {' · '}
137
+ <span title={task.task.cwd}>
138
+ <Icon d={iFolder} />
139
+ {folderName(task.task.cwd)}
140
+ </span>
141
+ </>
98
142
  ) : null}
99
143
  </span>
100
144
  </td>
101
145
  <td>
102
146
  <Status status={task.status} />
103
147
  </td>
148
+ <td class="when" title={formatDate(task.createdAt, tz)}>
149
+ {ago(task.createdAt, now, tz)}
150
+ </td>
151
+ <td class="num">{formatDuration(elapsed)}</td>
104
152
  <td>
105
153
  <span class="source" title={sourceLabel(source)}>
106
- <Icon d={SOURCE_ICONS[source] ?? iPlug} />
154
+ {brand ? <BrandIcon brand={brand} /> : <Icon d={sourceIcon(source)} />}
107
155
  {sourceLabel(source)}
108
156
  </span>
109
- </td>
110
- <td class="when" title={formatDate(task.createdAt, tz)}>
111
- {ago(task.createdAt, now, tz)}
112
- </td>
113
- <td class="num">
114
- {formatDuration(elapsed)}
115
157
  <span class="row-acts">
116
158
  <RowActions task={task} onDone={onDone} onError={onError} />
117
159
  </span>
@@ -184,6 +226,7 @@ function RowActions({
184
226
  export function TasksList({
185
227
  first,
186
228
  platforms,
229
+ brands = {},
187
230
  agents = [],
188
231
  filters,
189
232
  agent,
@@ -195,7 +238,9 @@ export function TasksList({
195
238
  first: TasksPage;
196
239
  /** Platform ids, the sources besides the dashboard, CLI and schedule. */
197
240
  platforms: [string, string][];
198
- /** Every agent's id, for the agent filter. */
241
+ /** Each platform's brand art, as the Agents page shows it. */
242
+ brands?: Record<string, IntegrationInfo['brand']>;
243
+ /** The first agents, for the agent filter before typing. */
199
244
  agents?: [string, string][];
200
245
  filters: Record<Keys, string>;
201
246
  agent?: string;
@@ -218,6 +263,26 @@ export function TasksList({
218
263
  ...listFilters(values),
219
264
  agent: agent ?? values.agent,
220
265
  };
266
+ const facets: Facet<Keys>[] = [
267
+ { key: 'status', label: 'Status', options: STATUSES, inline: true },
268
+ {
269
+ key: 'source',
270
+ label: 'Source',
271
+ options: [...SOURCES, ...platforms],
272
+ icon: value => (brands[value] ? <BrandIcon brand={brands[value]} /> : sourceIcon(value)),
273
+ },
274
+ ];
275
+ if (!agent)
276
+ facets.push({
277
+ key: 'agent',
278
+ label: 'Agent',
279
+ options: agents,
280
+ icon: () => iAgent,
281
+ search: (q, limit) =>
282
+ client.agents
283
+ .list({ cursor: '', limit, q })
284
+ .then(page => page.items.map((row): [string, string] => [row.id, row.name])),
285
+ });
221
286
  const list = usePaged<TaskRow>({
222
287
  name: `tasks${scope}`,
223
288
  fetchPage: cursor => client.tasks.list({ ...options, cursor }),
@@ -227,97 +292,190 @@ export function TasksList({
227
292
  });
228
293
  const [fresh, setFresh] = useState<{
229
294
  rows: Record<string, TaskRow>;
295
+ /** Tasks newer than the loaded pages, on top. */
296
+ added: TaskRow[];
230
297
  counts?: TaskCounts;
231
298
  query?: string;
232
- }>({ rows: {} });
299
+ }>({ rows: {}, added: [] });
233
300
  const [gone, setGone] = useState<string[]>([]);
234
301
  // Rows hidden after an archive or delete come back into view when the filters change.
235
302
  useEffect(() => setGone([]), [query]);
236
303
  const [actionError, setActionError] = useState('');
237
- const rows = list.rows
238
- .filter(task => !gone.includes(task.task.id))
239
- .map(task => fresh.rows[task.task.id] ?? task);
304
+ const rows = withAdded(list.rows, fresh.query === query ? fresh.added : [], fresh.rows).filter(
305
+ task => !gone.includes(task.task.id),
306
+ );
240
307
  const counts =
241
308
  (fresh.query === query ? fresh.counts : undefined) ??
242
309
  (list.meta.counts as TaskCounts | undefined);
243
- const live = rows.some(taskActive) || !!counts?.active;
244
310
 
245
- // Running tasks' status follows while they run; the rows themselves stay where they are.
311
+ // One first-page refetch at a time and a second apart, longer after failures; a change meanwhile asks for one more, with the filters then; a hidden tab waits to show.
312
+ const refetching = useRef<number>();
313
+ const again = useRef(false);
314
+ const hidden = useRef(false);
315
+ const spaced = useRef<ReturnType<typeof setTimeout>>();
316
+ const left = useRef(false);
317
+ const failures = useRef(0);
318
+ // Bumped by a reset, so a fetch started before it never writes over the page that follows.
319
+ const era = useRef(0);
320
+ // When each row last took an event, so an older page never undoes it.
321
+ const touched = useRef<Record<string, number>>({});
322
+ // The minute's resync reads every loaded row, not just the first page.
323
+ const deep = useRef(false);
324
+ const refresh = (all = false) => {
325
+ deep.current ||= all;
326
+ if (document.visibilityState === 'hidden') return void (hidden.current = true);
327
+ if (refetching.current) return void (again.current = true);
328
+ const started = (refetching.current = Date.now());
329
+ const before = era.current;
330
+ const loaded = deep.current ? rows : [];
331
+ deep.current = false;
332
+ // Pages of up to 200 until every loaded row is covered.
333
+ const read = async () => {
334
+ const first = await client.tasks.list({
335
+ ...options,
336
+ limit: Math.min(200, Math.max(LIST_PAGE, loaded.length)),
337
+ cursor: '',
338
+ });
339
+ let page = first;
340
+ const items = [...first.items];
341
+ while (page.next && items.length < loaded.length) {
342
+ page = await client.tasks.list({ ...options, limit: 200, cursor: page.next });
343
+ items.push(...page.items);
344
+ }
345
+
346
+ return { ...page, items, counts: first.counts };
347
+ };
348
+ void read()
349
+ .then(page => {
350
+ if (era.current !== before) return;
351
+ // A loaded row the read covers but no longer lists has left this view.
352
+ const last = page.items.at(-1);
353
+ const fetched = new Set(page.items.map(row => row.task.id));
354
+ const left = loaded.filter(
355
+ row =>
356
+ !fetched.has(row.task.id) &&
357
+ (touched.current[row.task.id] ?? 0) <= started &&
358
+ (!page.next || (last && listOrder(row, last) < 0)),
359
+ );
360
+ if (left.length) setGone(current => [...current, ...left.map(row => row.task.id)]);
361
+ setFresh(current => ({
362
+ rows: {
363
+ ...current.rows,
364
+ // A page row is a summary, so what events added to the row stays.
365
+ ...Object.fromEntries(
366
+ page.items
367
+ .filter(row => (touched.current[row.task.id] ?? 0) <= started)
368
+ .map(row => [row.task.id, { ...current.rows[row.task.id], ...row }]),
369
+ ),
370
+ },
371
+ added: addRows(current.query === query ? current.added : [], page.items),
372
+ counts: page.counts,
373
+ query,
374
+ }));
375
+ failures.current = 0;
376
+ })
377
+ // A failed read stays wanted, and tries again later each time.
378
+ .catch(() => {
379
+ failures.current++;
380
+ again.current = true;
381
+ })
382
+ .finally(() => {
383
+ if (left.current) return;
384
+ if (!again.current) return void (refetching.current = undefined);
385
+ again.current = false;
386
+ spaced.current = setTimeout(
387
+ () => {
388
+ refetching.current = undefined;
389
+ latest.current();
390
+ },
391
+ Math.min(30_000, 1000 * 2 ** failures.current),
392
+ );
393
+ });
394
+ };
395
+ const latest = useRef(refresh);
396
+ latest.current = refresh;
246
397
  useEffect(() => {
247
- if (!live) return;
398
+ const show = () => {
399
+ if (document.visibilityState !== 'visible' || !hidden.current) return;
400
+ hidden.current = false;
401
+ latest.current();
402
+ };
403
+ addEventListener('visibilitychange', show);
404
+ return () => {
405
+ removeEventListener('visibilitychange', show);
406
+ clearTimeout(spaced.current);
407
+ left.current = true;
408
+ };
409
+ }, []);
410
+
411
+ // Changes were missed, so every loaded page goes: the page renders again from the server.
412
+ const nav = useRouter();
413
+ const reset = () => {
414
+ era.current++;
415
+ touched.current = {};
416
+ setFresh({ rows: {}, added: [] });
417
+ setGone([]);
418
+ nav.refresh();
419
+ };
248
420
 
249
- const byId = (items: TaskRow[]) => Object.fromEntries(items.map(row => [row.task.id, row]));
250
- const timer = setInterval(() => {
251
- void Promise.all([
252
- client.tasks.list({ ...options, cursor: '' }),
253
- // A row whose status leaves the filter still takes its new status.
254
- options.status
255
- ? client.tasks.list({ ...options, status: undefined, counts: false, cursor: '' })
256
- : undefined,
257
- ])
258
- .then(([page, recent]) =>
259
- setFresh(current => ({
260
- rows: { ...current.rows, ...byId(recent?.items ?? []), ...byId(page.items) },
261
- counts: page.counts,
262
- query,
263
- })),
264
- )
265
- .catch(() => {});
266
- }, LIVE_MS);
267
- return () => clearInterval(timer);
268
- }, [live, query, agent]);
421
+ // Rows follow the events stream at once; counts, and tasks the list lacks, come with its first page again.
422
+ useTaskEvents(
423
+ (event: TaskEvent) => {
424
+ const row = rows.find(task => task.task.id === event.id);
425
+ if (!row) {
426
+ // A deleted or archived task stays out even if a read from before it brings it; an unarchived one comes back.
427
+ if (event.deleted || (event.archivedAt && values.status !== 'archived'))
428
+ setGone(current => [...current, event.id]);
429
+ else
430
+ setGone(current =>
431
+ current.includes(event.id) ? current.filter(id => id !== event.id) : current,
432
+ );
433
+ // A row kept from another view would outlive this change, so it goes.
434
+ setFresh(current => {
435
+ const { [event.id]: _kept, ...rows } = current.rows;
436
+ return _kept ? { ...current, rows } : current;
437
+ });
438
+ if (event.deleted || (agent || values.agent || event.agent) === event.agent) refresh();
439
+ return;
440
+ }
441
+ touched.current[event.id] = Date.now();
442
+ if (refetching.current) again.current = true;
443
+ // A change that moves the counts asks the server; a continued task also brings its new prompt.
444
+ if (
445
+ event.deleted ||
446
+ event.status !== row.status ||
447
+ Boolean(event.archivedAt) !== Boolean(row.archivedAt)
448
+ )
449
+ refresh();
450
+ if (event.deleted || (event.archivedAt && values.status !== 'archived'))
451
+ return setGone(current => [...current, event.id]);
452
+ setFresh(current => {
453
+ const was = current.rows[event.id] ?? row;
454
+ return {
455
+ ...current,
456
+ rows: {
457
+ ...current.rows,
458
+ [event.id]: {
459
+ ...was,
460
+ status: event.status,
461
+ approval: event.approval ?? undefined,
462
+ archivedAt: event.archivedAt,
463
+ startedAt: event.startedAt,
464
+ finishedAt: event.finishedAt,
465
+ },
466
+ },
467
+ };
468
+ });
469
+ },
470
+ reset,
471
+ () => refresh(true),
472
+ );
269
473
 
270
474
  return (
271
475
  <>
272
- <Toolbar head={head}>
476
+ <Toolbar head={head} action={head ? <NewTaskButton /> : undefined}>
273
477
  <Search value={values.q} label="Search tasks" onInput={value => set('q', value, 250)} />
274
- <Select
275
- label="Status"
276
- value={values.status}
277
- options={STATUSES}
278
- onChange={value => set('status', value)}
279
- />
280
- <Menu
281
- class="filter-more"
282
- label="More filters"
283
- summaryClass={`field-btn${values.source || values.agent ? ' on' : ''}`}
284
- summary="Filter"
285
- >
286
- <p class="pop-label">Source</p>
287
- <div role="group" aria-label="Source">
288
- {[...SOURCES, ...platforms].map(([value, label]) => (
289
- <MenuRadio
290
- key={value}
291
- on={values.source === value}
292
- onPick={() => set('source', value)}
293
- >
294
- {label}
295
- </MenuRadio>
296
- ))}
297
- </div>
298
- {agent ? null : (
299
- <>
300
- <p class="pop-label">Agent</p>
301
- <div role="group" aria-label="Agent">
302
- {[
303
- ['', 'All agents'] as [string, string],
304
- ...(values.agent && !agents.some(([id]) => id === values.agent)
305
- ? [[values.agent, values.agent] as [string, string]]
306
- : []),
307
- ...agents,
308
- ].map(([value, label]) => (
309
- <MenuRadio
310
- key={value}
311
- on={values.agent === value}
312
- onPick={() => set('agent', value)}
313
- >
314
- {label}
315
- </MenuRadio>
316
- ))}
317
- </div>
318
- </>
319
- )}
320
- </Menu>
478
+ <Filter name="tasks" facets={facets} values={values} onChange={set} />
321
479
  </Toolbar>
322
480
  {missing ? <NeedsCredential /> : null}
323
481
  <Card
@@ -348,9 +506,9 @@ export function TasksList({
348
506
  <tr>
349
507
  <th>Task</th>
350
508
  <th>Status</th>
351
- <th>Source</th>
352
509
  <th>Created</th>
353
510
  <th class="num">Duration</th>
511
+ <th>Source</th>
354
512
  </tr>
355
513
  </thead>
356
514
  <tbody>
@@ -358,6 +516,7 @@ export function TasksList({
358
516
  <TaskRowView
359
517
  key={task.task.id}
360
518
  task={task}
519
+ brand={brands[task.task.event?.integration ?? task.task.source]}
361
520
  back={back}
362
521
  scoped={Boolean(agent)}
363
522
  tz={tz}
@@ -367,19 +526,7 @@ export function TasksList({
367
526
  // An archived row leaves every view but the archive; a stopped one just refreshes.
368
527
  if (what === 'delete' || (what === 'archive' && values.status !== 'archived'))
369
528
  setGone(current => [...current, task.task.id]);
370
- void client.tasks
371
- .list({ ...options, cursor: '' })
372
- .then(page =>
373
- setFresh(current => ({
374
- rows: {
375
- ...current.rows,
376
- ...Object.fromEntries(page.items.map(row => [row.task.id, row])),
377
- },
378
- counts: page.counts,
379
- query,
380
- })),
381
- )
382
- .catch(() => {});
529
+ refresh();
383
530
  }}
384
531
  />
385
532
  ))}
@@ -392,7 +539,7 @@ export function TasksList({
392
539
  <Empty title="No tasks yet">
393
540
  {agent
394
541
  ? 'Tasks appear here as this agent runs.'
395
- : 'Type what you want done in the box above and press Enter. Tasks from the dashboard, the CLI and platforms like GitHub show up here, newest first.'}
542
+ : 'Start one with New task. Tasks from the dashboard, the CLI and platforms like GitHub show up here, newest first.'}
396
543
  </Empty>
397
544
  )}
398
545
  <Tail noun="tasks" {...list} />
@@ -2,11 +2,11 @@ import './tasks-list.css';
2
2
  import { dynamic } from '@wular/pnext/dynamic';
3
3
 
4
4
  import { zoneOf } from '@/utils/format';
5
- import { LIST_PAGE } from '@/utils/paged';
5
+ import { FILTER_ROWS, LIST_PAGE } from '@/utils/paged';
6
6
  import { load } from '@/api/load';
7
7
  import { Settle } from '@/comps/frame/stream';
8
8
  import { LoadingCard, LoadingToolbar } from '@/comps/frame/loading-card';
9
- import { SOURCES, STATUSES, listFilters } from '@/app/dash/tasks/list/task';
9
+ import { listFilters } from '@/app/dash/tasks/list/task';
10
10
 
11
11
  // Rows arrive rendered; paging and filters hydrate once the list is in view.
12
12
  const TasksList = dynamic(() => import('./tasks-list').then(m => m.TasksList), {
@@ -18,18 +18,14 @@ const TasksList = dynamic(() => import('./tasks-list').then(m => m.TasksList), {
18
18
  export function TasksLoading({ head, agent }: { head?: { title: string }; agent?: boolean }) {
19
19
  return (
20
20
  <>
21
- <LoadingToolbar head={head} search="Search tasks" selects={[STATUSES[0]![1]]} more />
21
+ <LoadingToolbar head={head} search="Search tasks" more />
22
22
  <LoadingCard title="Tasks" label="Loading tasks" />
23
23
  </>
24
24
  );
25
25
  }
26
26
 
27
27
  /** The tasks the address bar's filters select; the page starts these loads before it renders. */
28
- export function loadTasks(
29
- request: Request,
30
- agent?: string,
31
- agentChoices?: Promise<[string, string][]>,
32
- ) {
28
+ export function loadTasks(request: Request, agent?: string) {
33
29
  const params = new URL(request.url).searchParams;
34
30
  const filters = {
35
31
  q: params.get('q') ?? '',
@@ -47,16 +43,15 @@ export function loadTasks(
47
43
  agent: agent ?? filters.agent,
48
44
  });
49
45
  const catalog = load(request).integrations.list();
50
- // The agent filter's choices; an agent's own tab has none.
46
+ // The agent filter's first rows, one past what it shows so it knows to offer typing; an agent's own tab has none.
51
47
  const agents = agent
52
48
  ? undefined
53
- : (agentChoices ??
54
- load(request)
55
- .agents.list({ cursor: '', limit: 100 })
49
+ : load(request)
50
+ .agents.list({ cursor: '', limit: FILTER_ROWS + 1 })
56
51
  .then(
57
52
  page => page.items.map((row): [string, string] => [row.id, row.name]),
58
53
  () => [],
59
- ));
54
+ );
60
55
  return { filters, first, catalog, agents, tz: zoneOf(request) };
61
56
  }
62
57
 
@@ -80,6 +75,7 @@ export function TasksSection({
80
75
  <TasksList
81
76
  first={page}
82
77
  platforms={integrations.map((entry): [string, string] => [entry.id, entry.name])}
78
+ brands={Object.fromEntries(integrations.map(entry => [entry.id, entry.brand]))}
83
79
  agents={ids}
84
80
  filters={filters}
85
81
  agent={agent}
@@ -1,50 +1,18 @@
1
- import { dynamic } from '@wular/pnext/dynamic';
2
-
3
- import { zoneOf } from '@/utils/format';
4
1
  import { load } from '@/api/load';
5
2
  import { HEADS } from '@/comps/frame/heads';
6
3
  import { Page } from '@/comps/frame/page-head';
7
- import { Stream } from '@/comps/frame/stream';
8
4
  import { TasksSection, loadTasks } from '@/app/dash/tasks/list/tasks-section';
9
- import {
10
- agentChoices,
11
- credentialMissing,
12
- loadComposer,
13
- savedChoice,
14
- } from '@/app/dash/tasks/composer-data';
15
- import { LoadingComposer } from '@/app/dash/tasks/composer';
16
-
17
- // Hydrates with the page, so typing and Enter work at once.
18
- const Composer = dynamic(() => import('@/app/dash/tasks/composer').then(m => m.Composer));
5
+ import { credentialMissing } from '@/app/dash/tasks/composer-data';
19
6
 
20
- /** One box to start anything, then every task; each streams in on its own. */
7
+ /** Every task, with New task in the head. */
21
8
  export default function TasksPage({ request }: { request?: Request }) {
22
9
  if (!request) return null;
23
10
  const client = load(request);
24
- const agent = new URL(request.url).searchParams.get('agent');
25
11
 
26
12
  return (
27
13
  <Page>
28
- <Stream
29
- fallback={<LoadingComposer />}
30
- load={() =>
31
- Promise.all([loadComposer(client), credentialMissing(client)]).then(
32
- ([setup, missing]) => (
33
- <Composer
34
- {...setup}
35
- initial={{
36
- ...savedChoice(request.headers.get('cookie') ?? ''),
37
- ...(agent ? { agent } : {}),
38
- }}
39
- missing={missing}
40
- tz={zoneOf(request)}
41
- />
42
- ),
43
- )
44
- }
45
- />
46
14
  <TasksSection
47
- tasks={loadTasks(request, undefined, agentChoices(client))}
15
+ tasks={loadTasks(request)}
48
16
  head={HEADS.tasks}
49
17
  missing={credentialMissing(client)}
50
18
  />
@@ -27,8 +27,8 @@ export function LoadingToolbar({
27
27
  selects?: string[];
28
28
  /** A disabled more-filters button after the selects. */
29
29
  more?: boolean;
30
- /** The primary button's label. */
31
- action?: string;
30
+ /** The primary button. */
31
+ action?: { label: string; icon: string };
32
32
  }) {
33
33
  return (
34
34
  <Toolbar
@@ -36,7 +36,8 @@ export function LoadingToolbar({
36
36
  action={
37
37
  action ? (
38
38
  <button type="button" class="btn" disabled>
39
- {action}
39
+ <Icon d={action.icon} />
40
+ {action.label}
40
41
  </button>
41
42
  ) : undefined
42
43
  }
@@ -36,7 +36,17 @@
36
36
  font-size: 12px;
37
37
  color: var(--muted);
38
38
  }
39
+ /* stuck: the back link shrinks to its arrow before the title */
40
+ .stuck > div:first-child:has(> .back) {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: 8px;
44
+ }
39
45
  .stuck .back {
46
+ flex: none;
47
+ margin: 0;
48
+ }
49
+ .stuck .back > span {
40
50
  display: none;
41
51
  }
42
52
  .back:hover {
@@ -29,10 +29,11 @@ function backTo(back: string | null | undefined): { to: To; label: string } | un
29
29
  /** Back to the logical parent, wherever the visitor came from; a `back` search param wins. */
30
30
  export function Back({ label, back, ...to }: To & { label: string; back?: string | null }) {
31
31
  const param = backTo(back);
32
+ const name = param?.label ?? label;
32
33
  return (
33
- <Link class="back" {...(param?.to ?? to)}>
34
+ <Link class="back" aria-label={name} {...(param?.to ?? to)}>
34
35
  <Icon d={iLeft} />
35
- {param?.label ?? label}
36
+ <span>{name}</span>
36
37
  </Link>
37
38
  );
38
39
  }