@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
@@ -5,7 +5,7 @@ import '@/comps/ui/toast.css';
5
5
  import { useEffect, useRef, useState } from 'preact/hooks';
6
6
 
7
7
  import { serverEvents } from '@coder/client';
8
- import type { TaskRow, TaskState } from '@coder/client/types';
8
+ import type { TaskEvent, TaskRow, TaskState } from '@coder/client/types';
9
9
  import { client } from '@/utils/client';
10
10
  import { Link } from '@wular/pnext/link';
11
11
  import { usePathname, useRouter } from '@wular/pnext/navigation/client';
@@ -13,7 +13,7 @@ import { Status, STATUS_LABEL } from '@/app/dash/tasks/list/status';
13
13
 
14
14
  type Watched = { title: string; status: TaskState; at: number };
15
15
  type Live = Pick<TaskRow, 'status' | 'result' | 'error' | 'approval'>;
16
- type Toast = { key: string; id?: string; title: string; status?: TaskState; line: string };
16
+ type Toast = { key: string; id?: string; title: string; status?: TaskState; line: string; err?: boolean };
17
17
 
18
18
  const WATCHED = 'coder:watched';
19
19
  export const NOTIFY = 'coder:notify';
@@ -21,11 +21,18 @@ const TOAST = 'coder:toast';
21
21
  const DONE = new Set<TaskState>(['completed', 'failed', 'cancelled']);
22
22
  const SHOWN = new Set<TaskState>([...DONE, 'waiting']);
23
23
  const KEEP = 20;
24
- const STREAMS = 3;
25
24
  const MAX = 3;
26
25
  const TTL = 8000;
27
- // Past any log line, so a stream sends status alone; log seqs are PostgreSQL integers.
28
- const NO_LINES = 2 ** 31 - 1;
26
+ // The Web Lock that picks the one tab holding the events stream, and the channel it shares them on.
27
+ const EVENTS = 'coder:events';
28
+ const TASK = 'coder:task';
29
+ const REPLAY = 'coder:task-replay';
30
+ const RESET = 'coder:task-reset';
31
+ const RESYNC = 'coder:task-resync';
32
+ // A write that commits past the server's look-back still reaches shown pages within this, through a fresh read.
33
+ const RESYNC_MS = 60_000;
34
+ // Latest events a tab keeps, so a page that mounts later starts from them.
35
+ const LATEST = 200;
29
36
 
30
37
  const read = (): Record<string, Watched> => {
31
38
  try {
@@ -43,9 +50,11 @@ const save = (all: Record<string, Watched>) => {
43
50
  dispatchEvent(new Event(WATCHED));
44
51
  };
45
52
 
46
- /** An error toast for an action whose page has no room for it. */
47
- export const toastError = (title: string, line: string) =>
48
- dispatchEvent(new CustomEvent(TOAST, { detail: { title, line } }));
53
+ /** A toast for an action whose page has no room for its outcome. */
54
+ export const showToast = (title: string, line: string, err?: boolean) =>
55
+ dispatchEvent(new CustomEvent(TOAST, { detail: { title, line, err } }));
56
+
57
+ export const toastError = (title: string, line: string) => showToast(title, line, true);
49
58
 
50
59
  /** Remembers a task this browser started or opened, so its finish shows a toast on any page. */
51
60
  export function watchTask(id: string, status: TaskState, title?: string) {
@@ -74,34 +83,151 @@ function lineOf(live: Live): string {
74
83
  return text(live.error) || (live.status === 'failed' ? 'It failed.' : 'It was stopped.');
75
84
  }
76
85
 
77
- /** Follows one watched task's event stream for its status alone. */
78
- function Follow({ id, onStatus }: { id: string; onStatus: (id: string, live: Live) => void }) {
86
+ /** Hears every task change from this browser's one events stream; `reset` when some may have been missed, `resync` once a minute to read again. */
87
+ export function useTaskEvents(
88
+ on: (event: TaskEvent) => void,
89
+ reset?: () => void,
90
+ resync?: () => void,
91
+ ) {
92
+ const handler = useRef({ on, reset, resync });
93
+ handler.current = { on, reset, resync };
94
+ useEffect(() => {
95
+ const listener = (event: Event) => handler.current.on((event as CustomEvent<TaskEvent>).detail);
96
+ const again = () => handler.current.reset?.();
97
+ const read = () => handler.current.resync?.();
98
+ addEventListener(TASK, listener);
99
+ addEventListener(RESET, again);
100
+ addEventListener(RESYNC, read);
101
+ dispatchEvent(
102
+ new CustomEvent(REPLAY, { detail: (event: TaskEvent) => handler.current.on(event) }),
103
+ );
104
+ return () => {
105
+ removeEventListener(TASK, listener);
106
+ removeEventListener(RESET, again);
107
+ removeEventListener(RESYNC, read);
108
+ };
109
+ }, []);
110
+ }
111
+
112
+ type Message = { hello?: true; id?: string; event?: TaskEvent; reset?: true; resync?: true };
113
+
114
+ /** One events stream per browser: the tab holding the lock reads it, shares it on a channel, and a tab that takes over resumes from the last event id. */
115
+ function useEventsStream(ids: string[], leading: { current: boolean }) {
116
+ const asked = useRef(ids);
117
+ asked.current = ids;
118
+ const current = useRef<{ key: string; abort: AbortController }>();
119
+ const key = [...ids].sort().join(',');
120
+
79
121
  useEffect(() => {
122
+ const latest = new Map<string, TaskEvent>();
123
+ const deliver = (event: TaskEvent) => {
124
+ latest.delete(event.id);
125
+ latest.set(event.id, event);
126
+ if (latest.size > LATEST) latest.delete(latest.keys().next().value!);
127
+ dispatchEvent(new CustomEvent(TASK, { detail: event }));
128
+ };
129
+ const replay = (event: Event) => {
130
+ for (const each of latest.values())
131
+ (event as CustomEvent<(e: TaskEvent) => void>).detail(each);
132
+ };
133
+ addEventListener(REPLAY, replay);
134
+ const channel =
135
+ typeof BroadcastChannel !== 'undefined' && navigator.locks
136
+ ? new BroadcastChannel(EVENTS)
137
+ : undefined;
80
138
  const abort = new AbortController();
139
+ let last: string | undefined;
140
+ let wake = () => {};
141
+ let soon: ReturnType<typeof setTimeout> | undefined;
142
+ const receive = (message: Message) => {
143
+ last = message.id ?? last;
144
+ if (message.event) deliver(message.event);
145
+ if (message.resync) dispatchEvent(new Event(RESYNC));
146
+ if (!message.reset) return;
147
+ latest.clear();
148
+ dispatchEvent(new Event(RESET));
149
+ };
150
+ channel?.addEventListener('message', ({ data }: MessageEvent<Message>) => {
151
+ if (!data.hello) return receive(data);
152
+ // A tab that just loaded, maybe into another workspace: the next stream resumes for it too.
153
+ soon ??= setTimeout(() => {
154
+ soon = undefined;
155
+ current.current?.abort.abort();
156
+ wake();
157
+ }, 300);
158
+ });
159
+ channel?.postMessage({ hello: true });
160
+
161
+ const lead = async () => {
162
+ leading.current = true;
163
+ const resync = setInterval(() => {
164
+ channel?.postMessage({ resync: true });
165
+ receive({ resync: true });
166
+ }, RESYNC_MS);
167
+ try {
168
+ await follow();
169
+ } finally {
170
+ clearInterval(resync);
171
+ }
172
+ };
173
+
81
174
  const follow = async () => {
82
175
  while (!abort.signal.aborted) {
176
+ const once = new AbortController();
177
+ const stop = () => once.abort();
178
+ abort.signal.addEventListener('abort', stop);
179
+ const ids = asked.current;
180
+ current.current = { key: [...ids].sort().join(','), abort: once };
181
+ let wait = 1000;
83
182
  try {
84
- const response = await client.tasks.stream(id, NO_LINES, abort.signal);
85
- if (response.status === 404) {
86
- const all = read();
87
- delete all[id];
88
- return save(all);
89
- }
90
- if (!response.ok || !response.body) return;
91
- for await (const event of serverEvents(response.body)) {
92
- if (event.event === 'status') onStatus(id, JSON.parse(event.data));
93
- else if (event.event === 'end') return;
94
- }
95
- } catch {
96
- if (abort.signal.aborted) return;
97
- }
98
- await new Promise(done => setTimeout(done, 1000));
183
+ const response = await client.tasks.events(ids, last, once.signal);
184
+ // A server without the events stream: nothing to follow.
185
+ if (response.status === 404) return void (leading.current = false);
186
+ // Signed out: a tab that signs in again wakes the next try.
187
+ if (!response.ok || !response.body)
188
+ wait = [401, 403].includes(response.status) ? 60_000 : 5000;
189
+ else
190
+ for await (const { event, data, id } of serverEvents(response.body)) {
191
+ // A stream replaced by the next one says nothing more.
192
+ if (once.signal.aborted) break;
193
+ const message: Message = {
194
+ ...(id ? { id } : {}),
195
+ ...(event === 'task' ? { event: JSON.parse(data) as TaskEvent } : {}),
196
+ ...(event === 'reset' ? { reset: true as const } : {}),
197
+ };
198
+ channel?.postMessage(message);
199
+ receive(message);
200
+ }
201
+ } catch {}
202
+ abort.signal.removeEventListener('abort', stop);
203
+ // A new task set opens the next stream at once.
204
+ if (once.signal.aborted) continue;
205
+ await new Promise<void>(done => {
206
+ const timer = setTimeout(done, wait);
207
+ wake = () => {
208
+ clearTimeout(timer);
209
+ done();
210
+ };
211
+ });
212
+ wake = () => {};
99
213
  }
100
214
  };
101
- void follow();
102
- return () => abort.abort();
103
- }, [id]);
104
- return null;
215
+
216
+ if (channel)
217
+ void navigator.locks.request(EVENTS, { signal: abort.signal }, lead).catch(() => {});
218
+ else void lead();
219
+ return () => {
220
+ abort.abort();
221
+ wake();
222
+ clearTimeout(soon);
223
+ channel?.close();
224
+ removeEventListener(REPLAY, replay);
225
+ };
226
+ }, []);
227
+
228
+ useEffect(() => {
229
+ if (current.current && current.current.key !== key) current.current.abort.abort();
230
+ }, [key]);
105
231
  }
106
232
 
107
233
  function Item({ toast, dismiss }: { toast: Toast; dismiss: (key: string) => void }) {
@@ -110,7 +236,7 @@ function Item({ toast, dismiss }: { toast: Toast; dismiss: (key: string) => void
110
236
  return () => clearTimeout(timer);
111
237
  }, []);
112
238
  return (
113
- <div class={toast.id ? 'toast' : 'toast err'} onClick={() => dismiss(toast.key)}>
239
+ <div class={toast.err ? 'toast err' : 'toast'} onClick={() => dismiss(toast.key)}>
114
240
  <p class="toast-head">
115
241
  {toast.id ? (
116
242
  <Link class="cover" href="/dash/tasks/[id]" params={{ id: toast.id }}>
@@ -134,9 +260,16 @@ export function TaskToasts() {
134
260
  const [toasts, setToasts] = useState<Toast[]>([]);
135
261
  const open = useRef(openTask(path));
136
262
  open.current = openTask(path);
263
+ // Each tab's own last status per task, so every tab toasts a change once.
264
+ const known = useRef<Record<string, TaskState>>({});
265
+ const leading = useRef(false);
137
266
 
138
267
  useEffect(() => {
139
- const sync = () => setWatched(read());
268
+ const sync = () => {
269
+ const all = read();
270
+ for (const [id, entry] of Object.entries(all)) known.current[id] ??= entry.status;
271
+ setWatched(all);
272
+ };
140
273
  sync();
141
274
  addEventListener(WATCHED, sync);
142
275
  addEventListener('storage', sync);
@@ -148,8 +281,8 @@ export function TaskToasts() {
148
281
 
149
282
  useEffect(() => {
150
283
  const onToast = (event: Event) => {
151
- const { title, line } = (event as CustomEvent<{ title: string; line: string }>).detail;
152
- setToasts(list => [{ key: `${TOAST}:${Date.now()}`, title, line }, ...list].slice(0, MAX));
284
+ const detail = (event as CustomEvent<Omit<Toast, 'key'>>).detail;
285
+ setToasts(list => [{ key: `${TOAST}:${Date.now()}`, ...detail }, ...list].slice(0, MAX));
153
286
  };
154
287
  addEventListener(TOAST, onToast);
155
288
  return () => removeEventListener(TOAST, onToast);
@@ -164,9 +297,25 @@ export function TaskToasts() {
164
297
 
165
298
  const dismiss = (key: string) => setToasts(list => list.filter(toast => toast.key !== key));
166
299
 
167
- const onStatus = (id: string, live: Live) => {
168
- const seen = read()[id];
169
- if (!seen || seen.status === live.status) return;
300
+ useTaskEvents(event => {
301
+ const all = read();
302
+ const seen = all[event.id];
303
+ if (!seen) return;
304
+ if (event.deleted) {
305
+ delete all[event.id];
306
+ return save(all);
307
+ }
308
+ const before = known.current[event.id] ?? seen.status;
309
+ known.current[event.id] = event.status;
310
+ if (before !== event.status)
311
+ onStatus(
312
+ event.id,
313
+ { ...event, result: event.result ?? undefined, error: event.error ?? undefined },
314
+ seen,
315
+ );
316
+ });
317
+
318
+ const onStatus = (id: string, live: Live, seen: Watched) => {
170
319
  watchTask(id, live.status);
171
320
  if (!SHOWN.has(live.status) || open.current === id) return;
172
321
  const toast = {
@@ -178,6 +327,7 @@ export function TaskToasts() {
178
327
  };
179
328
  setToasts(list => [toast, ...list].slice(0, MAX));
180
329
  if (
330
+ !leading.current ||
181
331
  localStorage.getItem(NOTIFY) !== '1' ||
182
332
  typeof Notification === 'undefined' ||
183
333
  Notification.permission !== 'granted'
@@ -193,22 +343,18 @@ export function TaskToasts() {
193
343
  };
194
344
  };
195
345
 
196
- // Its own page follows an open task; the rest share a few streams, newest first.
197
- const follow = Object.entries(watched)
198
- .filter(([id, entry]) => !DONE.has(entry.status) && id !== open.current)
199
- .sort(([, a], [, b]) => b.at - a.at)
200
- .slice(0, STREAMS);
346
+ useEventsStream(
347
+ Object.entries(watched)
348
+ .filter(([, entry]) => !DONE.has(entry.status))
349
+ .map(([id]) => id),
350
+ leading,
351
+ );
201
352
 
202
353
  return (
203
- <>
204
- {follow.map(([id]) => (
205
- <Follow key={id} id={id} onStatus={onStatus} />
354
+ <section class="toasts" aria-label="Task updates" aria-live="polite">
355
+ {toasts.map(toast => (
356
+ <Item key={toast.key} toast={toast} dismiss={dismiss} />
206
357
  ))}
207
- <section class="toasts" aria-label="Task updates" aria-live="polite">
208
- {toasts.map(toast => (
209
- <Item key={toast.key} toast={toast} dismiss={dismiss} />
210
- ))}
211
- </section>
212
- </>
358
+ </section>
213
359
  );
214
360
  }
@@ -1,20 +1,31 @@
1
1
  'use client';
2
2
 
3
- import { useState } from 'preact/hooks';
3
+ import { useEffect, useState } from 'preact/hooks';
4
4
 
5
5
  import { iLeft, iRight } from '@/comps/ui/icons';
6
6
  import { Icon } from '@/comps/ui/icon';
7
7
 
8
8
  const COOKIE = 'side';
9
9
 
10
- /** Collapses the sidebar to its icons; the choice lives in a cookie so the next page renders that way at once. */
10
+ /** Collapses the sidebar to its icons; a click is saved in a cookie so the next page renders that way at once. */
11
11
  export function SideToggle({ collapsed: initial }: { collapsed: boolean }) {
12
12
  const [collapsed, setCollapsed] = useState(initial);
13
+
14
+ // Without a saved choice the layout script collapses mid-width screens, so the label follows the class.
15
+ useEffect(() => {
16
+ const app = document.querySelector('.app');
17
+ if (!app) return;
18
+ const sync = () => setCollapsed(app.classList.contains('side-min'));
19
+ sync();
20
+ const watch = new MutationObserver(sync);
21
+ watch.observe(app, { attributes: true, attributeFilter: ['class'] });
22
+ return () => watch.disconnect();
23
+ }, []);
24
+
13
25
  const toggle = () => {
14
26
  const next = !collapsed;
15
- setCollapsed(next);
16
27
  document.querySelector('.app')?.classList.toggle('side-min', next);
17
- document.cookie = `${COOKIE}=${next ? 'min' : ''}; path=/; max-age=${next ? 31536000 : 0}; samesite=lax`;
28
+ document.cookie = `${COOKIE}=${next ? 'min' : 'max'}; path=/; max-age=31536000; samesite=lax`;
18
29
  };
19
30
  return (
20
31
  <button
@@ -8,6 +8,7 @@
8
8
  }
9
9
  .card.accent {
10
10
  border-color: var(--warn);
11
+ background: var(--warnbg);
11
12
  }
12
13
  .card.danger {
13
14
  border-color: var(--bad);
@@ -0,0 +1,64 @@
1
+ /* applied filters sit in the toolbar's row and scroll sideways instead of wrapping it */
2
+ .filter-chips {
3
+ display: flex;
4
+ flex: 1 1 0;
5
+ gap: 4px;
6
+ min-width: 0;
7
+ overflow-x: auto;
8
+ scrollbar-width: none;
9
+ }
10
+ .filters:has(> .filter-chips) > .search {
11
+ flex-grow: 1000;
12
+ }
13
+ /* in a page head the filters keep to the right, so chips fill in from the search's left */
14
+ .head.tools .filter-chips {
15
+ order: -1;
16
+ }
17
+ .head.tools .filter-chips > :first-child {
18
+ margin-left: auto;
19
+ }
20
+ .filter-chips .chip {
21
+ display: inline-flex;
22
+ flex: none;
23
+ align-items: center;
24
+ gap: 4px;
25
+ max-width: 200px;
26
+ }
27
+ .filter-chips .chip .grow {
28
+ overflow: hidden;
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ }
32
+ .filter-chips .chip .ic {
33
+ width: 12px;
34
+ height: 12px;
35
+ }
36
+ /* brand art takes the same box as the other icons, so labels line up */
37
+ .filter-panel .pop-item > .brand-icon {
38
+ width: 16px;
39
+ height: 16px;
40
+ }
41
+ .filter-panel [data-facet] {
42
+ color: var(--muted);
43
+ }
44
+ .filter-panel .search {
45
+ margin: 4px 0 6px;
46
+ }
47
+ .filter-panel .search input {
48
+ width: 100%;
49
+ }
50
+
51
+ @media (max-width: 760px) {
52
+ /* the button sits at the left edge here, so its menu opens rightward */
53
+ @supports (anchor-scope: --menu) {
54
+ .filters .menu.filter-more > .pop {
55
+ inset: anchor(bottom) auto auto anchor(left);
56
+ }
57
+ }
58
+ .head.tools .filter-chips {
59
+ order: 0;
60
+ }
61
+ .head.tools .filter-chips > :first-child {
62
+ margin-left: 0;
63
+ }
64
+ }