@wular/coder 0.4.1 → 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.
- package/README.md +69 -70
- package/dist/actions-FzDNxMFf.js +20 -0
- package/dist/{adapter-BKVzTDnn.js → adapter-7SEiVsmp.js} +1 -1
- package/dist/{adapter-B0oYCI0S.js → adapter-BLEfUqoQ.js} +1 -1
- package/dist/{adapter-DPInwbJG.js → adapter-H4GlcKQz.js} +1 -1
- package/dist/{add-CE-70LFs.js → add-B50dIJGd.js} +1 -1
- package/dist/{add-BxpLHnvD.js → add-D7X3gxap.js} +1 -1
- package/dist/{add-ej0X9e3a.js → add-DOEUHIe7.js} +1 -1
- package/dist/{add-U-pty9qJ.js → add-QExQhTzi.js} +1 -1
- package/dist/{add-json-CEa-6luW.js → add-json-Ck4KIZrT.js} +1 -1
- package/dist/{agent-JdtZozvP.js → agent-BPtnEf1q.js} +1 -1
- package/dist/{agent-BsYx3Odz.js → agent-BlQ_dNNm.js} +2 -2
- package/dist/{alias-DX-Bm86x.js → alias-BwQBovke.js} +1 -1
- package/dist/{app-BT5f7zL5.js → app-y6BYm2eH.js} +1 -1
- package/dist/{approvals-C-AvFt6s.js → approvals-Cc9cq5T1.js} +2 -2
- package/dist/{approve-DOmpQRNd.js → approve-oO9k2fnK.js} +2 -2
- package/dist/{apps-CjfrZyM5.js → apps-CU2YalX8.js} +1 -1
- package/dist/{archive-BziJ1rQ-.js → archive-C5c_HQS1.js} +2 -2
- package/dist/{archive-BUB8gFKD.js → archive-CemlWlVY.js} +2 -2
- package/dist/archive-sweep-ehg-fWIt.js +1 -0
- package/dist/{args-BOEEdpWk.js → args-DcBCtXhz.js} +1 -1
- package/dist/{ask-dE3U3ss2.js → ask-Chu7Twu_.js} +2 -2
- package/dist/auth-BOyXPOtu.js +1 -0
- package/dist/auth-XbqKSbcD.js +1 -0
- package/dist/{chat-bridge-BS9WtP3X.js → chat-bridge-DXgZItGP.js} +1 -1
- package/dist/{chat-B_yhNTo0.js → chat-cknlIhVh.js} +1 -1
- package/dist/{cli-BilSDkvE.js → cli-B1ObVrL4.js} +5 -5
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/{client-D9IoAuDD.js → client-31YzOj-i.js} +1 -1
- package/dist/{config-Da_cwoxI.js → config-DAE_qHYA.js} +1 -1
- package/dist/{create-C_iMJV4Y.js → create-CM8B8JxD.js} +1 -1
- package/dist/credentials-BXD4Qptk.js +1 -0
- package/dist/dash/manifest.json +934 -1014
- package/dist/dash/public/assets/chunks/{agent-pills-c9c4ac4d4a10df83.js → agent-pills-b6a6da91c9559d9f.js} +1 -1
- package/dist/dash/public/assets/chunks/{agent-settings-form-8be2ddab074aba6d.js → agent-settings-form-b615c677046cd6f2.js} +1 -1
- package/dist/dash/public/assets/chunks/agents-grid-f2e7b4ff9e11b6e3.js +1 -0
- package/dist/dash/public/assets/chunks/{badge-676b01e0c615bf7b.js → badge-215c7b2631dafc69.js} +1 -1
- package/dist/dash/public/assets/chunks/{card-e0901d68b79dfa93.js → card-e86dcc81326d2b14.js} +1 -1
- package/dist/dash/public/assets/chunks/{combo-41234c24668b1db1.js → combo-09e246dc9a517fa8.js} +1 -1
- package/dist/dash/public/assets/chunks/composer-417166b029f48c47.js +1 -0
- package/dist/dash/public/assets/chunks/composer-645ae8fa97a35280.js +1 -0
- package/dist/dash/public/assets/chunks/{confirm-f2fe4ba9905a89c6.js → confirm-5ae484241ad888fd.js} +1 -1
- package/dist/dash/public/assets/chunks/{connect-apps-aa2a3d82e48ed8c3.js → connect-apps-4147c45f45fde2b1.js} +1 -1
- package/dist/dash/public/assets/chunks/{connect-repositories-5389ec8701a3deac.js → connect-repositories-77591110836bdf03.js} +1 -1
- package/dist/dash/public/assets/chunks/credentials-list-e005a0783ec93465.js +2 -0
- package/dist/dash/public/assets/chunks/{custom-range-4761ab14f05149df.js → custom-range-463e119af4681c61.js} +1 -1
- package/dist/dash/public/assets/chunks/{editor-bfe8cd854f8001be.js → editor-804b84f4fe6539cd.js} +4 -4
- package/dist/dash/public/assets/chunks/{engines-list-f1191390f5715b39.js → engines-list-ceab51f13c690e8c.js} +1 -1
- package/dist/dash/public/assets/chunks/{field-901f1f35c18c39c9.js → field-aa0a4b98a976781e.js} +1 -1
- package/dist/dash/public/assets/chunks/filter-91144ac2eec6297f.js +1 -0
- package/dist/dash/public/assets/chunks/format-105c706345987fb2.js +1 -0
- package/dist/dash/public/assets/chunks/heads-a42817b32d2d7f08.js +1 -0
- package/dist/dash/public/assets/chunks/{instructions-052fa45a9daf6db6.js → instructions-3a1dcaff2d868aa5.js} +1 -1
- package/dist/dash/public/assets/chunks/instructions-6c6f981fffaa31e4.js +1 -0
- package/dist/dash/public/assets/chunks/{list-1d259660d7612c5f.js → list-7270361464fc3c04.js} +1 -1
- package/dist/dash/public/assets/chunks/{mcp-list-e3bebbbe831393b8.js → mcp-list-2e715d48666b54ad.js} +1 -1
- package/dist/dash/public/assets/chunks/{models-list-44f857a469ca04d5.js → models-list-d3c40f94c761a6e5.js} +1 -1
- package/dist/dash/public/assets/chunks/paged-3ddb6077bef42752.js +1 -0
- package/dist/dash/public/assets/chunks/{platform-actions-d1426602049926d2.js → platform-actions-271ec3b99a64280b.js} +1 -1
- package/dist/dash/public/assets/chunks/runners-list-767e49c7d0ec833a.js +1 -0
- package/dist/dash/public/assets/chunks/{segmented-358ff2b4992b8384.js → segmented-de4386ccaac7304d.js} +1 -1
- package/dist/dash/public/assets/chunks/{select-7ae25fd8a7d6c338.js → select-a90307706cffa6e0.js} +1 -1
- package/dist/dash/public/assets/chunks/{sentinel-c972a33dc69a5135.js → sentinel-b5e032b38604dfd3.js} +1 -1
- package/dist/dash/public/assets/chunks/shared-init-7f81ae7bc8c82438.js +2 -0
- package/dist/dash/public/assets/chunks/shared-init-933c7f9ee85be766.js +5 -0
- package/dist/dash/public/assets/chunks/{tail-7c27aab8ba268bd9.js → tail-4891e727ee526792.js} +1 -1
- package/dist/dash/public/assets/chunks/task-actions-ce854925a2dc78fd.js +1 -0
- package/dist/dash/public/assets/chunks/task-ddfddeaf07fb684c.js +1 -0
- package/dist/dash/public/assets/chunks/task-view-c37dcee08f21a279.js +5 -0
- package/dist/dash/public/assets/chunks/tasks-list-baef587999f322d8.js +1 -0
- package/dist/dash/public/assets/chunks/{toolbar-a75d2570f07cbb70.js → toolbar-8d98313606ad8a2d.js} +1 -1
- package/dist/dash/public/assets/chunks/{versions-list-fc2e0d2a7382e049.js → versions-list-532690efd9f1b0ea.js} +1 -1
- package/dist/dash/public/assets/dash-49ca85b59ac65c20.css +1 -0
- package/dist/dash/public/assets/dash-account-edc56e85dbaaabe0.css +1 -0
- package/dist/dash/public/assets/dash-agents-edba57b67e4005d0.css +1 -0
- package/dist/dash/public/assets/dash-agents-new-fe22253fbada2fbf.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-adf52e9dacb2d825.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-edit-a2937a347689ef29.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-platforms-265ba13db32a460d.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-settings-8afc52eb0372bc4b.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-tasks-6193e646be68dbd2.css +1 -0
- package/dist/dash/public/assets/dash-agents-param-slug-usage-ceadb3a1c911c402.css +1 -0
- package/dist/dash/public/assets/dash-settings-cf1997cd7f72e518.css +1 -0
- package/dist/dash/public/assets/dash-settings-param-tab-b44dac8e79a560e6.css +1 -0
- package/dist/dash/public/assets/dash-tasks-916c7100a8058aa3.css +1 -0
- package/dist/dash/public/assets/dash-tasks-param-id-194a6ffc20e872f2.css +1 -0
- package/dist/dash/public/assets/dash-usage-40940b289f85e0b6.css +1 -0
- package/dist/dash/public/assets/pnext-client-13aee92bef5c9e3f.js +1 -0
- package/dist/dash/public/assets/pnext-client-1f2686b2044f0df6.js +1 -0
- package/dist/dash/public/assets/pnext-client-26130234ebad7067.js +1 -0
- package/dist/dash/public/assets/pnext-client-311e35b0076bf406.js +1 -0
- package/dist/dash/public/assets/pnext-client-4ade1bf75c9c3d87.js +1 -0
- package/dist/dash/public/assets/pnext-client-61cd31b687ede1da.js +1 -0
- package/dist/dash/public/assets/pnext-client-61db65dc4223217a.js +1 -0
- package/dist/dash/public/assets/pnext-client-6ea656e7705e3f2a.js +1 -0
- package/dist/dash/public/assets/pnext-client-7fd0f793f0322cb9.js +1 -0
- package/dist/dash/public/assets/pnext-client-88159c0840fcfa4a.js +1 -0
- package/dist/dash/public/assets/pnext-client-b03f0c47c066b4f3.js +1 -0
- package/dist/dash/public/assets/{pnext-client-1b9ab0963a8ff77b.js → pnext-client-d6478835f543f901.js} +1 -1
- package/dist/dash/public/assets/pnext-client-dadb8359021ed590.js +1 -0
- package/dist/dash/public/assets/pnext-client-de07532d6aaebf2c.js +1 -0
- package/dist/dash/public/assets/pnext-client-f87a31890c8ff224.js +1 -0
- package/dist/dash/public/assets/pnext-client-ffe1c123a8e2a350.js +1 -0
- package/dist/dash/server/bundle-0.1.6-b29ec45cb963d47f/entry.js +2 -0
- package/dist/{default-vYPT26DR.js → default-BMvUMTFp.js} +1 -1
- package/dist/default-B_nKaX0-.js +1 -0
- package/dist/{delete-D8gEwoOj.js → delete-Bjteibld.js} +2 -2
- package/dist/{delete-z1_86nf6.js → delete-riCIKZwj.js} +2 -2
- package/dist/{disable-DVKd-0Xb.js → disable-DQC56YFy.js} +1 -1
- package/dist/{discover-Dvr_3_Xr.js → discover-Qanr5TEO.js} +1 -1
- package/dist/{discover-Df-a8K4R.js → discover-lgTYwoQp.js} +2 -2
- package/dist/dispatch-BsW_1m7e.js +14 -0
- package/dist/{docs-DKJnqZqt.js → docs-DFs7lOJp.js} +1 -1
- package/dist/{docs-BX65z2Iu.js → docs-DKROso-4.js} +2 -2
- package/dist/{enable-C8Zf_wpN.js → enable-D2DqZvcU.js} +1 -1
- package/dist/{env-CRf6wkM2.js → env-YRY3I60r.js} +1 -1
- package/dist/exec-DuUkLMqg.js +11 -0
- package/dist/executor-vMppVLB6.js +10 -0
- package/dist/flow/builtin/review.js +1 -1
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.js +1 -1
- package/dist/{flow-17k25MHt.js → flow-DmERAnR8.js} +1 -1
- package/dist/{github-actions-DwUzm4hC.js → github-actions-C_NkHVJq.js} +1 -1
- package/dist/http-D01Udu4u.js +1 -0
- package/dist/{index-CZjFX5zV.d.ts → index-B1L4zXZH.d.ts} +2 -1
- package/dist/{init-CoMfFxbG.js → init-Bd6JHzBc.js} +1 -1
- package/dist/integrations-B2CavXR1.js +5 -0
- package/dist/integrations-D1Dwy1kt.js +1 -0
- package/dist/lib/broker.js +2 -2
- package/dist/{list-DX-z3SBp.js → list-4Uo-0v9s.js} +1 -1
- package/dist/{list-BAKvxSbh.js → list-B0ujEZhd.js} +3 -3
- package/dist/{list-BYiG8Zav.js → list-B5oa1Bhy.js} +2 -2
- package/dist/{list-BxEywhHU.js → list-BjE_l6la.js} +1 -1
- package/dist/{list-VToARh3C.js → list-Ck9ibCs1.js} +1 -1
- package/dist/{list-CIW2joV1.js → list-DhgK4Zc4.js} +1 -1
- package/dist/{list-DpCf4G-H.js → list-KvL7AUhD.js} +2 -2
- package/dist/{list-C5w01-8q.js → list-byYLBl1U.js} +1 -1
- package/dist/{load-BpEsAn51.js → load-SnZd7xG1.js} +1 -1
- package/dist/{local-H3uvqEbq.js → local-BRy1EUq2.js} +1 -1
- package/dist/log-view-DeJstSkw.js +3 -0
- package/dist/{login-DkkslfBx.js → login-C-ahtDKV.js} +1 -1
- package/dist/{login-BFxX4jJy.js → login-zpwke8g1.js} +1 -1
- package/dist/{logout-BHAtw4Z8.js → logout-CI6Kf-T6.js} +1 -1
- package/dist/{maintenance-C5FklUw3.js → maintenance-BAzr9Jlr.js} +1 -1
- package/dist/{match-CXAjDhg7.js → match-DxV1d49D.js} +1 -1
- package/dist/{mcp-BgKb2Cdk.js → mcp-CmcL1MCG.js} +1 -1
- package/dist/{mcp-CCZOAo06.js → mcp-mhXwtGQT.js} +1 -1
- package/dist/{migrate-N-85_N7J.js → migrate-Czh3TEK5.js} +1 -1
- package/dist/{migrate-C4vM7DQN.js → migrate-w7KfzUAR.js} +1 -1
- package/dist/{model-Bs1StDo3.js → model-JRoTH9QK.js} +1 -1
- package/dist/models-C-S7HqSb.js +1 -0
- package/dist/{prompt-CEuBMNhk.js → prompt-CMXmt2l9.js} +4 -4
- package/dist/{push-CUC55Bqd.js → push-B8BhTyZs.js} +1 -1
- package/dist/queue-CirjEskI.js +117 -0
- package/dist/refresh-Bmy2r1Ee.js +1 -0
- package/dist/{remote-miQNTVRa.js → remote-CxFnEjZl.js} +1 -1
- package/dist/{remove-B93LNkZI.js → remove-C15WLaXT.js} +1 -1
- package/dist/{remove-C5_xggx7.js → remove-CTZYjjSr.js} +1 -1
- package/dist/{remove-eQ8y5C5p.js → remove-CTl-H0f0.js} +1 -1
- package/dist/{remove-CuH8a3L9.js → remove-DX2FVo5b.js} +1 -1
- package/dist/rename-D1g4TnHQ.js +1 -0
- package/dist/{result-BEtQZHaY.js → result-D49VN8PM.js} +3 -3
- package/dist/result-DGWyUHYU.js +18 -0
- package/dist/{resume-DYzKvyEw.js → resume-ywzc7z6A.js} +2 -2
- package/dist/{review-oIBeECZE.js → review-ZC0xG8XY.js} +2 -2
- package/dist/{rotate-key-IdMYH7gg.js → rotate-key-Bf8_99ck.js} +1 -1
- package/dist/{run-CTtd0Znc.js → run-D7SJ-ieT.js} +2 -2
- package/dist/run-DoRQSC_K.js +2 -0
- package/dist/{run-CawRGnOg.js → run-ukCf2Mdy.js} +1 -1
- package/dist/runner-D-REfOUe.js +1 -0
- package/dist/{runner-sUpWJ_k0.js → runner-wDYhkVsM.js} +1 -1
- package/dist/schema-DoNO0dPh.js +1 -0
- package/dist/sdk.d.ts +4 -2
- package/dist/sdk.js +1 -1
- package/dist/serve-BJ_Mbm2m.js +8 -0
- package/dist/{serve-CPnmrKyf.js → serve-C1MMhM1F.js} +2 -2
- package/dist/{serve-GmRO89s5.js → serve-DtrOHaoS.js} +2 -2
- package/dist/{serve-CE8YV2e9.js → serve-P9pFkEnE.js} +1 -1
- package/dist/{serve-BTeomYxW.js → serve-yjSw0B1a.js} +1 -1
- package/dist/{server-Kb22cWfF.js → server-DDWTnmbx.js} +1 -1
- package/dist/{setup-host-GFTUzzZJ.js → setup-host-L6Rnn5Cz.js} +1 -1
- package/dist/{shared-DVtzWhUT.js → shared-XsUX_ZZb.js} +1 -1
- package/dist/{show-COTZEYgc.js → show-CU-jqJ90.js} +1 -1
- package/dist/{show-CMUg8SJu.js → show-DVJmB2u-.js} +1 -1
- package/dist/{status-D26U--p8.js → status-CBydR7LE.js} +1 -1
- package/dist/{steer-C6uARLp5.js → steer-Cm_sHZqs.js} +2 -2
- package/dist/{stop-BrGI3hSS.js → stop-DJS7DAPJ.js} +1 -1
- package/dist/{stop-ByCAlbmN.js → stop-TrebIIuv.js} +2 -2
- package/dist/store-BrNMqJgk.js +42 -0
- package/dist/store-j8icrG0v.js +19 -0
- package/dist/task-C8lmcNsf.js +1 -0
- package/dist/{task-DeglY79z.js → task-CpUI19o2.js} +4 -4
- package/dist/task-CvSqiwxo.js +1 -0
- package/dist/{test-NEbhlxvo.js → test-CDKz4jY_.js} +1 -1
- package/dist/{tools-D0Zfs8Hx.js → tools-DBFeoosk.js} +1 -1
- package/dist/{types-I5Cwtl_K.d.ts → types-DMGBYB_K.d.ts} +3 -1
- package/dist/{unalias-C9NqmSDq.js → unalias-BfQFBnuK.js} +1 -1
- package/dist/{update-BTxYQj7Z.js → update-C4FFOie8.js} +1 -1
- package/dist/{upgrade-hA0yWBvx.js → upgrade-DLJqMnUQ.js} +2 -2
- package/dist/{usage-BhqoCjvE.js → usage-CSh-xwQ2.js} +1 -1
- package/dist/{usage-C4d9X2Uq.js → usage-D8mc4qXK.js} +1 -1
- package/dist/{watch-DtRuC0Wc.js → watch-CQu-tJKZ.js} +2 -2
- package/dist/{watch-CSdj24ct.js → watch-DmwaEvoi.js} +2 -2
- package/dist/{wire-BeT1Axeo.js → wire-DzGLZ3-O.js} +1 -1
- package/dist/worker-BLKTMLqa.js +1 -0
- package/dist/worker-nHemduM_.js +9 -0
- package/dist/{workflow-Dcmdooi8.js → workflow-IrouAus_.js} +1 -1
- package/docs/cli.md +145 -0
- package/docs/mcp.md +1 -1
- package/migrations/0000_initial.sql +8 -0
- package/migrations/meta/0000_snapshot.json +75 -1
- package/migrations/meta/_journal.json +1 -1
- package/package.json +3 -2
- package/plugins/agents/skills/coder/SKILL.md +1 -0
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/dispatch/SKILL.md +1 -1
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/skills/coder/SKILL.md +1 -0
- package/src/client/index.ts +10 -2
- package/src/client/types.ts +18 -0
- package/src/core/defaults.ts +36 -0
- package/src/core/task/log-view.ts +120 -6
- package/src/server/dash/app/dash/agents/(list)/agent-menu.tsx +4 -0
- package/src/server/dash/app/dash/agents/(list)/agents-grid.tsx +18 -35
- package/src/server/dash/app/dash/agents/(list)/loading.tsx +7 -1
- package/src/server/dash/app/dash/agents/[slug]/(tabs)/layout.tsx +2 -1
- package/src/server/dash/app/dash/layout.css +58 -1
- package/src/server/dash/app/dash/layout.tsx +29 -7
- package/src/server/dash/app/dash/settings/[tab]/credentials-list.tsx +20 -13
- package/src/server/dash/app/dash/settings/[tab]/page.tsx +1 -1
- package/src/server/dash/app/dash/settings/[tab]/runners-list.tsx +13 -12
- package/src/server/dash/app/dash/tasks/[id]/activity.tsx +49 -4
- package/src/server/dash/app/dash/tasks/[id]/task-actions.tsx +43 -6
- package/src/server/dash/app/dash/tasks/[id]/task-view.css +52 -31
- package/src/server/dash/app/dash/tasks/[id]/task-view.tsx +143 -51
- package/src/server/dash/app/dash/tasks/composer.css +4 -0
- package/src/server/dash/app/dash/tasks/composer.tsx +6 -5
- package/src/server/dash/app/dash/tasks/list/task.ts +29 -1
- package/src/server/dash/app/dash/tasks/list/tasks-list.css +20 -7
- package/src/server/dash/app/dash/tasks/list/tasks-list.tsx +256 -109
- package/src/server/dash/app/dash/tasks/list/tasks-section.tsx +9 -13
- package/src/server/dash/app/dash/tasks/page.tsx +3 -35
- package/src/server/dash/comps/frame/loading-card.tsx +4 -3
- package/src/server/dash/comps/frame/page-head.css +10 -0
- package/src/server/dash/comps/frame/page-head.tsx +3 -2
- package/src/server/dash/comps/frame/task-toasts.tsx +196 -50
- package/src/server/dash/comps/nav/side-toggle.tsx +16 -5
- package/src/server/dash/comps/ui/card.css +1 -0
- package/src/server/dash/comps/ui/filter.css +64 -0
- package/src/server/dash/comps/ui/filter.tsx +336 -0
- package/src/server/dash/comps/ui/icons.ts +2 -0
- package/src/server/dash/comps/ui/menu.tsx +9 -1
- package/src/server/dash/comps/ui/select.tsx +2 -2
- package/src/server/dash/comps/ui/toast.css +3 -2
- package/src/server/dash/comps/ui/toolbar.css +14 -0
- package/src/server/dash/utils/client.ts +21 -0
- package/src/server/dash/utils/format.ts +12 -4
- package/src/server/dash/utils/paged.ts +14 -6
- package/dist/actions-Mk3FSlpJ.js +0 -20
- package/dist/archive-sweep-CtBcARgN.js +0 -1
- package/dist/auth-BFny--17.js +0 -1
- package/dist/auth-DWHIzqbA.js +0 -1
- package/dist/credentials-B1jv6dkg.js +0 -1
- package/dist/dash/public/assets/chunks/agents-grid-951584b6f4c4314e.js +0 -1
- package/dist/dash/public/assets/chunks/composer-5534be50bc12385e.js +0 -1
- package/dist/dash/public/assets/chunks/composer-d10d4b1d54c4f751.js +0 -1
- package/dist/dash/public/assets/chunks/credentials-list-bc5bfcab058176c5.js +0 -2
- package/dist/dash/public/assets/chunks/format-eb03fc9d9744d960.js +0 -1
- package/dist/dash/public/assets/chunks/heads-f3bcd289663d48fa.js +0 -1
- package/dist/dash/public/assets/chunks/instructions-d0a4b0d95009c262.js +0 -1
- package/dist/dash/public/assets/chunks/paged-bc6fa0c2b9d52ed7.js +0 -1
- package/dist/dash/public/assets/chunks/runners-list-1901af8a5dea1f09.js +0 -1
- package/dist/dash/public/assets/chunks/shared-init-88cce66bb3cfe1e5.js +0 -5
- package/dist/dash/public/assets/chunks/shared-init-ffb3397e494137c9.js +0 -2
- package/dist/dash/public/assets/chunks/task-actions-8e1d93c788d001df.js +0 -1
- package/dist/dash/public/assets/chunks/task-fa8b82e95e771c4a.js +0 -1
- package/dist/dash/public/assets/chunks/task-view-6d7618d2820a5cb4.js +0 -3
- package/dist/dash/public/assets/chunks/tasks-list-f4784e5cbd0ed785.js +0 -1
- package/dist/dash/public/assets/dash-9e02d5d1a6d42ee3.css +0 -1
- package/dist/dash/public/assets/dash-account-04859899c1838ada.css +0 -1
- package/dist/dash/public/assets/dash-agents-9e678137267dec07.css +0 -1
- package/dist/dash/public/assets/dash-agents-new-26fa27b17ef9b8d8.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-b975c48f8abf3444.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-edit-e13fda599bb04843.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-platforms-70292932cb27bc96.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-settings-71f9a86251723fcc.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-tasks-fb8eb2ed71006601.css +0 -1
- package/dist/dash/public/assets/dash-agents-param-slug-usage-7c8740f1cdd52c71.css +0 -1
- package/dist/dash/public/assets/dash-settings-8eb818da462227e8.css +0 -1
- package/dist/dash/public/assets/dash-settings-param-tab-849a1a9542e273f5.css +0 -1
- package/dist/dash/public/assets/dash-tasks-81e510e0b6931b42.css +0 -1
- package/dist/dash/public/assets/dash-tasks-param-id-bbd2a83342f2ec10.css +0 -1
- package/dist/dash/public/assets/dash-usage-8e0259e03eaed1bd.css +0 -1
- package/dist/dash/public/assets/pnext-client-03b03b254e525e82.js +0 -1
- package/dist/dash/public/assets/pnext-client-1c7859bb888f4ef0.js +0 -1
- package/dist/dash/public/assets/pnext-client-3c748afe963fb05d.js +0 -1
- package/dist/dash/public/assets/pnext-client-4f47c2cae220f107.js +0 -1
- package/dist/dash/public/assets/pnext-client-6ab9fd9b53b794d2.js +0 -1
- package/dist/dash/public/assets/pnext-client-6c241786342498e4.js +0 -1
- package/dist/dash/public/assets/pnext-client-80b56d420f02b808.js +0 -1
- package/dist/dash/public/assets/pnext-client-83135c17bae850a3.js +0 -1
- package/dist/dash/public/assets/pnext-client-8e6fa4302ae0396f.js +0 -1
- package/dist/dash/public/assets/pnext-client-932c9d8cc0717fc8.js +0 -1
- package/dist/dash/public/assets/pnext-client-a42ae63d4de113f9.js +0 -1
- package/dist/dash/public/assets/pnext-client-d2ee6f8c1e337032.js +0 -1
- package/dist/dash/public/assets/pnext-client-d513f65ec23b7f20.js +0 -1
- package/dist/dash/public/assets/pnext-client-ecb88e2a8a109954.js +0 -1
- package/dist/dash/public/assets/pnext-client-f1545ee6270bbf9a.js +0 -1
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/boot-B2MFTRO2.js +0 -30
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/build-tier-XJJSL5FR.js +0 -2704
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2HFY3DQT.js +0 -208
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2ICEVELW.js +0 -834
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-3UPROT2C.js +0 -35
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4P5CMIYT.js +0 -1582
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4TKZKZ3Q.js +0 -131
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-5RDEILQB.js +0 -437
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-62KEQM43.js +0 -25
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-6KQSWT6D.js +0 -831
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-7DCNCW6O.js +0 -724
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-B4THCTQP.js +0 -22
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-C2LIV2MM.js +0 -172
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-CCATPVTW.js +0 -378
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-COXJAPTU.js +0 -89
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-E6IXL2NJ.js +0 -6270
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FLGRBFAH.js +0 -1198
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FTJVHFWD.js +0 -61
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-H523KSIH.js +0 -5151
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-HT6KKX6A.js +0 -20
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JESFYECC.js +0 -438
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JRPJM2XY.js +0 -17
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L2IWDUXG.js +0 -107
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L5P4Y4UC.js +0 -183
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-NQE46AZN.js +0 -647
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PA4QEARJ.js +0 -129
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PZ5AXOLW.js +0 -7146
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-SNVPTULL.js +0 -2397
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-T3QZT3PG.js +0 -220
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-VC5O2SDP.js +0 -72
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-WZX4V6CM.js +0 -231
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-XUUIDXY5.js +0 -627
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-ZHFDO3PR.js +0 -265
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/compile-CE2DN7ZA.js +0 -18
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-UREY7XJT.js +0 -16
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-loader-UI2XE3PZ.js +0 -17
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/entry.js +0 -219
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/generate-I2PPTDFD.js +0 -16
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/modules-UVOCE4C2.js +0 -48
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/pipeline-UW47K537.js +0 -963
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/proxy-ITGOVJBW.js +0 -26
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/register-4SA55SWT.js +0 -12579
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/router-JNKHLBCZ.js +0 -505
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/routes-PD6J2NXW.js +0 -54
- package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/tsconfig-defaults-ZXEH7RQK.js +0 -15
- package/dist/default-Bobztoxf.js +0 -1
- package/dist/dispatch-BKeAch9E.js +0 -14
- package/dist/exec-RZFLclKX.js +0 -11
- package/dist/executor-BvAA7Cgw.js +0 -10
- package/dist/http-Bkg_OQ6r.js +0 -1
- package/dist/integrations-5xCaXSd-.js +0 -1
- package/dist/integrations-BAgdVDhj.js +0 -5
- package/dist/log-view-dg0TlKLA.js +0 -1
- package/dist/models-_5LVGC3m.js +0 -1
- package/dist/queue-6io_NOP3.js +0 -116
- package/dist/refresh-BIrmrWYq.js +0 -1
- package/dist/rename-CYfy03FE.js +0 -1
- package/dist/result-BzaM3inj.js +0 -15
- package/dist/run-BDYl0CKl.js +0 -2
- package/dist/runner-D7ScbIHJ.js +0 -1
- package/dist/schema-CU2iFkp8.js +0 -1
- package/dist/serve-CQKZxzFE.js +0 -4
- package/dist/store-B94tSate.js +0 -42
- package/dist/store-DzqXS2wT.js +0 -8
- package/dist/task-C6VAg5WU.js +0 -1
- package/dist/task-CgOKgkEG.js +0 -1
- package/dist/worker-CXndxmGF.js +0 -9
- package/dist/worker-DX7P9kWl.js +0 -1
- package/src/server/dash/app/dash/token-from-hash.tsx +0 -17
|
@@ -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
|
-
/**
|
|
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
|
|
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
|
|
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
|
-
{
|
|
36
|
+
<span>{name}</span>
|
|
36
37
|
</Link>
|
|
37
38
|
);
|
|
38
39
|
}
|
|
@@ -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
|
-
//
|
|
28
|
-
const
|
|
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
|
-
/**
|
|
47
|
-
export const
|
|
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
|
-
/**
|
|
78
|
-
function
|
|
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.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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.
|
|
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 = () =>
|
|
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
|
|
152
|
-
setToasts(list => [{ key: `${TOAST}:${Date.now()}`,
|
|
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
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
{
|
|
205
|
-
<
|
|
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
|
-
|
|
208
|
-
{toasts.map(toast => (
|
|
209
|
-
<Item key={toast.key} toast={toast} dismiss={dismiss} />
|
|
210
|
-
))}
|
|
211
|
-
</section>
|
|
212
|
-
</>
|
|
358
|
+
</section>
|
|
213
359
|
);
|
|
214
360
|
}
|
|
@@ -1,27 +1,38 @@
|
|
|
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;
|
|
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
|
|
28
|
+
document.cookie = `${COOKIE}=${next ? 'min' : 'max'}; path=/; max-age=31536000; samesite=lax`;
|
|
18
29
|
};
|
|
19
30
|
return (
|
|
20
31
|
<button
|
|
21
32
|
type="button"
|
|
22
33
|
class="icon-btn side-toggle"
|
|
23
34
|
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
24
|
-
|
|
35
|
+
data-tip={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
25
36
|
onClick={toggle}
|
|
26
37
|
>
|
|
27
38
|
<Icon d={collapsed ? iRight : iLeft} />
|
|
@@ -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
|
+
}
|