plum-e2e 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +7 -7
- package/backend/app.js +1 -1
- package/backend/lib/csv.js +21 -0
- package/backend/lib/exportResponse.js +37 -0
- package/backend/lib/serverBootstrap.js +2 -2
- package/backend/lib/serverConfig.js +5 -3
- package/backend/mcp/server.js +44 -0
- package/backend/middleware/runnerOrAdmin.js +1 -1
- package/backend/package.json +1 -1
- package/backend/routes/reports.routes.js +19 -0
- package/backend/routes/runners.routes.js +1 -1
- package/backend/routes/test-cases.routes.js +12 -0
- package/backend/routes/test-runs.routes.js +17 -0
- package/backend/routes/test-suites.routes.js +34 -0
- package/backend/scripts/{manage-runners.mjs → manage-nodes.mjs} +67 -52
- package/backend/services/exportService.js +339 -0
- package/backend/services/testImportService.js +87 -0
- package/bin/plum.js +174 -68
- package/docker-compose.yml +1 -1
- package/frontend/.svelte-kit/adapter-node/.vite/manifest.json +48 -40
- package/frontend/.svelte-kit/{output/server/_app/immutable/assets/Toast.DENwYn3g.css → adapter-node/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/{build/client/_app/immutable/assets/_layout.HkPnpRl9.css → .svelte-kit/adapter-node/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/adapter-node/chunks/{Toast.js → Button.js} +2 -17
- package/frontend/.svelte-kit/adapter-node/chunks/ConfirmModal.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/EmptyState.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/common.js +11 -4
- package/frontend/.svelte-kit/adapter-node/chunks/constants.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/repository.js +21 -17
- package/frontend/.svelte-kit/adapter-node/entries/pages/_layout.svelte.js +2 -0
- package/frontend/.svelte-kit/adapter-node/entries/pages/reports/_id_/_page.svelte.js +11 -2
- package/frontend/.svelte-kit/adapter-node/entries/pages/scheduled-tests/_page.svelte.js +2 -5
- package/frontend/.svelte-kit/adapter-node/entries/pages/settings/_page.svelte.js +300 -253
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/_page.svelte.js +55 -36
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/runs/_id_/_page.svelte.js +3 -6
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/suites/_id_/_page.svelte.js +15 -17
- package/frontend/.svelte-kit/adapter-node/manifest-full.js +1 -1
- package/frontend/.svelte-kit/adapter-node/manifest.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/0.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/1.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/10.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/11.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/12.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/2.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/3.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/4.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/5.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/6.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/7.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/8.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/9.js +1 -1
- package/frontend/.svelte-kit/generated/server/internal.js +1 -1
- package/frontend/.svelte-kit/output/client/.vite/manifest.json +459 -412
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/{0.CmJTG5mP.css → 0.BEqlRAtD.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/10.DC0wD6w_.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/12.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/6.DdQVxyZK.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/{adapter-node/_app/immutable/assets/Toast.DENwYn3g.css → output/client/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/{_layout.HkPnpRl9.css → _layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{C2_rQ4aY.js → 3MBVX4Yb.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/CfkUW1vA.js → .svelte-kit/output/client/_app/immutable/chunks/6VDMVB5U.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/B0_HKFXV.js → .svelte-kit/output/client/_app/immutable/chunks/B2lVGNml.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/CboYYxrf.js → .svelte-kit/output/client/_app/immutable/chunks/BCh31ecr.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BDb8hZHu.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{CeH-RgMb.js → BIf7lHIm.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BIjh3jiU.js +8 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BTV7EXZb.js +11 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DAmOMhaf.js → BWGoOYU0.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BdBJfKUi.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/BAvTKHxT.js → .svelte-kit/output/client/_app/immutable/chunks/BnXsGCy3.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BtTNggoY.js +8 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C4Z3p6jE.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CNpD-yNH.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CUPWZLIN.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{Ck6C3Fl0.js → CXQhSK1T.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/C00jnSU2.js → .svelte-kit/output/client/_app/immutable/chunks/CtgCYzu5.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/D72giPTu.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{GDjpV3r7.js → DDx3no2z.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/D_thT6pZ.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/C8S7qQjQ.js → .svelte-kit/output/client/_app/immutable/chunks/DeiVAbyn.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DiexacSB.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/d2K3c8Kx.js → .svelte-kit/output/client/_app/immutable/chunks/Dlymnwnf.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/Cgw8TOOy.js → .svelte-kit/output/client/_app/immutable/chunks/gCgJ3Cwu.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/BJ_ypKl3.js → .svelte-kit/output/client/_app/immutable/chunks/htkwR9b-.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/C7uEQ-LH.js → .svelte-kit/output/client/_app/immutable/chunks/iL4SCrvY.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{CoTEzvLa.js → kFFd6Oo5.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/pNZogwkM.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DWzEJYIF.js → prk8PJfA.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/qvxlSGpD.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/r_d1Iu0W.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{B00vxw57.js → tsOANt55.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/zXSLr47H.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.DgdSwPuN.js +7 -0
- package/frontend/{build/client/_app/immutable/entry/start.BXOLyf9R.js → .svelte-kit/output/client/_app/immutable/entry/start.D_lhI48n.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.D2F-i1I5.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{1.BI97il3r.js → 1.JIF2GfHq.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.A0AHLfOt.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.CVSPxlqY.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.CnAR0AQb.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{2.Dr6yugXz.js → 2.Bno00fr8.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3.B6KwaCsM.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{4.DDADylfF.js → 4.BX70m3v6.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.UIFh06yr.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.DZ1OV24V.js +10 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.B_XntBZ1.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.CVHgJw-E.js +9 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{9.BOK0GNF_.js → 9.DDDQPmMj.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/version.json +1 -1
- package/frontend/.svelte-kit/output/server/.vite/manifest.json +48 -40
- package/frontend/{build/client/_app/immutable/assets/Toast.DENwYn3g.css → .svelte-kit/output/server/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/.svelte-kit/{adapter-node/_app/immutable/assets/_layout.HkPnpRl9.css → output/server/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/server/chunks/{Toast.js → Button.js} +2 -17
- package/frontend/.svelte-kit/output/server/chunks/ConfirmModal.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/EmptyState.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/common.js +11 -4
- package/frontend/.svelte-kit/output/server/chunks/constants.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/repository.js +21 -17
- package/frontend/.svelte-kit/output/server/entries/pages/_layout.svelte.js +2 -0
- package/frontend/.svelte-kit/output/server/entries/pages/reports/_id_/_page.svelte.js +11 -2
- package/frontend/.svelte-kit/output/server/entries/pages/scheduled-tests/_page.svelte.js +2 -5
- package/frontend/.svelte-kit/output/server/entries/pages/settings/_page.svelte.js +300 -253
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/_page.svelte.js +55 -36
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/runs/_id_/_page.svelte.js +3 -6
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/suites/_id_/_page.svelte.js +15 -17
- package/frontend/.svelte-kit/output/server/manifest-full.js +1 -1
- package/frontend/.svelte-kit/output/server/manifest.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/0.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/1.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/10.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/11.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/12.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/2.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/3.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/4.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/5.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/6.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/7.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/8.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/9.js +1 -1
- package/frontend/build/client/_app/immutable/assets/{0.CmJTG5mP.css → 0.BEqlRAtD.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/0.BEqlRAtD.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/0.BEqlRAtD.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css +6 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css +6 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css +6 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css +6 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/assets/Toast.DENwYn3g.css → build/client/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/Button.C1zsJAUG.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.C1zsJAUG.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css.gz +0 -0
- package/frontend/{.svelte-kit/output/server/_app/immutable/assets/_layout.HkPnpRl9.css → build/client/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/_layout.B4TT3qNx.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.B4TT3qNx.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{C2_rQ4aY.js → 3MBVX4Yb.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/3MBVX4Yb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/3MBVX4Yb.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/CfkUW1vA.js → build/client/_app/immutable/chunks/6VDMVB5U.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/6VDMVB5U.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/6VDMVB5U.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/B0_HKFXV.js → build/client/_app/immutable/chunks/B2lVGNml.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/B2lVGNml.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B2lVGNml.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/CboYYxrf.js → build/client/_app/immutable/chunks/BCh31ecr.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BCh31ecr.js.br +6 -0
- package/frontend/build/client/_app/immutable/chunks/BCh31ecr.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CeH-RgMb.js → BIf7lHIm.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BIf7lHIm.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CeH-RgMb.js.gz → BIf7lHIm.js.gz} +0 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js +8 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js +11 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{DAmOMhaf.js → BWGoOYU0.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BWGoOYU0.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BWGoOYU0.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/BAvTKHxT.js → build/client/_app/immutable/chunks/BnXsGCy3.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BnXsGCy3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BnXsGCy3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js +8 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{Ck6C3Fl0.js → CXQhSK1T.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/CXQhSK1T.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CXQhSK1T.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C00jnSU2.js → build/client/_app/immutable/chunks/CtgCYzu5.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/CtgCYzu5.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CtgCYzu5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{GDjpV3r7.js → DDx3no2z.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/DDx3no2z.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DDx3no2z.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C8S7qQjQ.js → build/client/_app/immutable/chunks/DeiVAbyn.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/DeiVAbyn.js.br +6 -0
- package/frontend/build/client/_app/immutable/chunks/DeiVAbyn.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/d2K3c8Kx.js → build/client/_app/immutable/chunks/Dlymnwnf.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/Dlymnwnf.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dlymnwnf.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/Cgw8TOOy.js → build/client/_app/immutable/chunks/gCgJ3Cwu.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/gCgJ3Cwu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/gCgJ3Cwu.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/BJ_ypKl3.js → build/client/_app/immutable/chunks/htkwR9b-.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/htkwR9b-.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/htkwR9b-.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C7uEQ-LH.js → build/client/_app/immutable/chunks/iL4SCrvY.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/iL4SCrvY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/iL4SCrvY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CoTEzvLa.js → kFFd6Oo5.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/kFFd6Oo5.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/kFFd6Oo5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{DWzEJYIF.js → prk8PJfA.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/prk8PJfA.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/prk8PJfA.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{B00vxw57.js → tsOANt55.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/tsOANt55.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/tsOANt55.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js.gz +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js +7 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/entry/start.BXOLyf9R.js → build/client/_app/immutable/entry/start.D_lhI48n.js} +1 -1
- package/frontend/build/client/_app/immutable/entry/{start.BXOLyf9R.js.br → start.D_lhI48n.js.br} +1 -1
- package/frontend/build/client/_app/immutable/entry/{start.BXOLyf9R.js.gz → start.D_lhI48n.js.gz} +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{1.BI97il3r.js → 1.JIF2GfHq.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/1.JIF2GfHq.js.br +5 -0
- package/frontend/build/client/_app/immutable/nodes/1.JIF2GfHq.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{2.Dr6yugXz.js → 2.Bno00fr8.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/2.Bno00fr8.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Bno00fr8.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{4.DDADylfF.js → 4.BX70m3v6.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/4.BX70m3v6.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.BX70m3v6.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js +10 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js +9 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{9.BOK0GNF_.js → 9.DDDQPmMj.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/9.DDDQPmMj.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.DDDQPmMj.js.gz +0 -0
- package/frontend/build/client/_app/version.json +1 -1
- package/frontend/build/client/_app/version.json.br +0 -0
- package/frontend/build/client/_app/version.json.gz +0 -0
- package/frontend/build/server/chunks/0-38cvTs44.js +22 -0
- package/frontend/build/server/chunks/0-38cvTs44.js.map +5 -0
- package/frontend/build/server/chunks/{1-B3HpigzL.js → 1-CcYsajvR.js} +2 -2
- package/frontend/build/server/chunks/{1-B3HpigzL.js.map → 1-CcYsajvR.js.map} +1 -1
- package/frontend/build/server/chunks/10-CFR2MUJt.js +14 -0
- package/frontend/build/server/chunks/10-CFR2MUJt.js.map +5 -0
- package/frontend/build/server/chunks/11-BmkiRQ9D.js +14 -0
- package/frontend/build/server/chunks/11-BmkiRQ9D.js.map +5 -0
- package/frontend/build/server/chunks/12-BphJAwnL.js +14 -0
- package/frontend/build/server/chunks/12-BphJAwnL.js.map +5 -0
- package/frontend/build/server/chunks/2-Dalgd2D4.js +14 -0
- package/frontend/build/server/chunks/{2-DioMbKbC.js.map → 2-Dalgd2D4.js.map} +1 -1
- package/frontend/build/server/chunks/3-BvsmdXCj.js +14 -0
- package/frontend/build/server/chunks/{3-CgiZg_9v.js.map → 3-BvsmdXCj.js.map} +1 -1
- package/frontend/build/server/chunks/4-CN18WCEn.js +14 -0
- package/frontend/build/server/chunks/{4-BM-49dbE.js.map → 4-CN18WCEn.js.map} +1 -1
- package/frontend/build/server/chunks/5-BwLYF3xL.js +14 -0
- package/frontend/build/server/chunks/{5-Brx_KOzy.js.map → 5-BwLYF3xL.js.map} +1 -1
- package/frontend/build/server/chunks/6-DTM2N_k3.js +14 -0
- package/frontend/build/server/chunks/6-DTM2N_k3.js.map +5 -0
- package/frontend/build/server/chunks/7-DTNbErSe.js +14 -0
- package/frontend/build/server/chunks/7-DTNbErSe.js.map +5 -0
- package/frontend/build/server/chunks/8-BfCbb7pl.js +14 -0
- package/frontend/build/server/chunks/8-BfCbb7pl.js.map +5 -0
- package/frontend/build/server/chunks/9-CQKepnMw.js +14 -0
- package/frontend/build/server/chunks/{9-BMAKiAem.js.map → 9-CQKepnMw.js.map} +1 -1
- package/frontend/build/server/chunks/{Toast-CHB87F8k.js → Button-B9h9viZX.js} +3 -17
- package/frontend/build/server/chunks/Button-B9h9viZX.js.map +5 -0
- package/frontend/build/server/chunks/{ConfirmModal-CtGWHl0v.js → ConfirmModal-C6Cz898n.js} +2 -2
- package/frontend/build/server/chunks/{ConfirmModal-CtGWHl0v.js.map → ConfirmModal-C6Cz898n.js.map} +1 -1
- package/frontend/build/server/chunks/{EmptyState-Zpm9tJe1.js → EmptyState-CRtsJIv6.js} +2 -2
- package/frontend/build/server/chunks/{EmptyState-Zpm9tJe1.js.map → EmptyState-CRtsJIv6.js.map} +1 -1
- package/frontend/build/server/chunks/{_layout.svelte-C_6nsIpT.js → _layout.svelte-B8vZGefg.js} +6 -7
- package/frontend/build/server/chunks/_layout.svelte-B8vZGefg.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-CTebwzwy.js → _page.svelte-BrnXHxJ4.js} +14 -7
- package/frontend/build/server/chunks/_page.svelte-BrnXHxJ4.js.map +5 -0
- package/frontend/build/server/chunks/_page.svelte-C2WqvInJ.js +898 -0
- package/frontend/build/server/chunks/_page.svelte-C2WqvInJ.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-Bamoq11m.js → _page.svelte-CP6jA1ex.js} +6 -6
- package/frontend/build/server/chunks/{_page.svelte-Bamoq11m.js.map → _page.svelte-CP6jA1ex.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CbTrCeeX.js → _page.svelte-D6b-klLK.js} +9 -9
- package/frontend/build/server/chunks/{_page.svelte-CbTrCeeX.js.map → _page.svelte-D6b-klLK.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CmJKt3-H.js → _page.svelte-DBjTpquQ.js} +18 -21
- package/frontend/build/server/chunks/_page.svelte-DBjTpquQ.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-DHZhNTPg.js → _page.svelte-DM1_y4Oq.js} +60 -42
- package/frontend/build/server/chunks/_page.svelte-DM1_y4Oq.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-DObTHQe2.js → _page.svelte-Mo-O3HJ5.js} +7 -7
- package/frontend/build/server/chunks/{_page.svelte-DObTHQe2.js.map → _page.svelte-Mo-O3HJ5.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CAlhaCzS.js → _page.svelte-mZF4NrfU.js} +9 -12
- package/frontend/build/server/chunks/_page.svelte-mZF4NrfU.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-BNwU9zza.js → _page.svelte-nrlgIFaU.js} +8 -11
- package/frontend/build/server/chunks/_page.svelte-nrlgIFaU.js.map +5 -0
- package/frontend/build/server/chunks/{common-DYYTVHPw.js → common-DS-Z0UFe.js} +7 -2
- package/frontend/build/server/chunks/common-DS-Z0UFe.js.map +5 -0
- package/frontend/build/server/chunks/{constants-BYVkTZ-O.js → constants-OmHysD51.js} +2 -2
- package/frontend/build/server/chunks/{constants-BYVkTZ-O.js.map → constants-OmHysD51.js.map} +1 -1
- package/frontend/build/server/chunks/{format-2pqy61MU.js → format-Bqb0as15.js} +2 -2
- package/frontend/build/server/chunks/{format-2pqy61MU.js.map → format-Bqb0as15.js.map} +1 -1
- package/frontend/build/server/chunks/{reports-WuXG6tj3.js → reports-chCHCDzB.js} +2 -2
- package/frontend/build/server/chunks/{reports-WuXG6tj3.js.map → reports-chCHCDzB.js.map} +1 -1
- package/frontend/build/server/chunks/{reports2-CCpI0xkQ.js → reports2-Dr3zu9CD.js} +2 -2
- package/frontend/build/server/chunks/{reports2-CCpI0xkQ.js.map → reports2-Dr3zu9CD.js.map} +1 -1
- package/frontend/build/server/chunks/{repository-TYWcFuWP.js → repository-DSsWVKEb.js} +5 -3
- package/frontend/build/server/chunks/repository-DSsWVKEb.js.map +5 -0
- package/frontend/build/server/chunks/{runner-D5yCWOsy.js → runner-aaUtOqck.js} +3 -3
- package/frontend/build/server/chunks/{runner-D5yCWOsy.js.map → runner-aaUtOqck.js.map} +1 -1
- package/frontend/build/server/index.js +1 -1
- package/frontend/build/server/index.js.map +1 -1
- package/frontend/build/server/manifest.js +14 -14
- package/frontend/build/server/manifest.js.map +1 -1
- package/frontend/package-lock.json +14 -0
- package/frontend/package.json +2 -0
- package/frontend/src/lib/api/reports.js +7 -0
- package/frontend/src/lib/api/repository.js +28 -0
- package/frontend/src/lib/components/layout/RunnerPanel.svelte +2 -1
- package/frontend/src/lib/components/reports/RecordingPlayer.svelte +172 -2
- package/frontend/src/lib/components/ui/ExportMenu.svelte +154 -0
- package/frontend/src/lib/components/ui/NotificationStack.svelte +177 -0
- package/frontend/src/lib/constants.js +1 -1
- package/frontend/src/lib/copy/common.js +9 -0
- package/frontend/src/lib/copy/reports.js +20 -0
- package/frontend/src/lib/copy/repository.js +4 -0
- package/frontend/src/lib/copy/settings.js +15 -0
- package/frontend/src/lib/stores/notifications.js +23 -0
- package/frontend/src/lib/stores/replayVideo.js +51 -0
- package/frontend/src/lib/utils/download.js +28 -0
- package/frontend/src/lib/utils/replayCapture.js +91 -0
- package/frontend/src/lib/utils/replayVideo.js +111 -0
- package/frontend/src/routes/+layout.svelte +2 -0
- package/frontend/src/routes/reports/[id]/+page.svelte +38 -3
- package/frontend/src/routes/scheduled-tests/+page.svelte +9 -17
- package/frontend/src/routes/settings/+page.svelte +140 -56
- package/frontend/src/routes/test-repository/+page.svelte +44 -16
- package/frontend/src/routes/test-repository/runs/[id]/+page.svelte +14 -23
- package/frontend/src/routes/test-repository/suites/[id]/+page.svelte +38 -22
- package/package.json +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/.svelte-kit/generated/client/nodes/13.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/10.CdaQUS6U.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/12.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/6.BN5XbLUX.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/2-KCkWZO.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BbYY_TzB.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Beula-TH.js +0 -11
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CZWchtW8.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CegSvdD-.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Cfmt4_tJ.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DB3YXVFP.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DgQy0SB2.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DiVmSEJa.js +0 -8
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Dl-F6VCo.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DuAVKaL3.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/K1h4wprj.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.DRjRA71t.js +0 -7
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.CRb8Nyce.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.kyazROhw.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.B3vMgT2v.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.DkPcIrgW.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3.DUiL7fb5.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.N4CeDeoD.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.DWd93Bv5.js +0 -10
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.c8d1cQQM.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.Cvd3MauU.js +0 -9
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/0.CmJTG5mP.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/0.CmJTG5mP.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css +0 -6
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css +0 -6
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css +0 -6
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css +0 -6
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -6
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.HkPnpRl9.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.HkPnpRl9.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/B00vxw57.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B00vxw57.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/B0_HKFXV.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B0_HKFXV.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BAvTKHxT.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BAvTKHxT.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BJ_ypKl3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BJ_ypKl3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js +0 -11
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C00jnSU2.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C00jnSU2.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C2_rQ4aY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C2_rQ4aY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C7uEQ-LH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C7uEQ-LH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C8S7qQjQ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C8S7qQjQ.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CboYYxrf.js.br +0 -6
- package/frontend/build/client/_app/immutable/chunks/CboYYxrf.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CeH-RgMb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CfkUW1vA.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CfkUW1vA.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cgw8TOOy.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cgw8TOOy.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Ck6C3Fl0.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Ck6C3Fl0.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CoTEzvLa.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CoTEzvLa.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DAmOMhaf.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DAmOMhaf.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DWzEJYIF.js.br +0 -7
- package/frontend/build/client/_app/immutable/chunks/DWzEJYIF.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js +0 -8
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/GDjpV3r7.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/GDjpV3r7.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/d2K3c8Kx.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/d2K3c8Kx.js.gz +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js +0 -7
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.BI97il3r.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.BI97il3r.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Dr6yugXz.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Dr6yugXz.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.DDADylfF.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.DDADylfF.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js +0 -10
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js +0 -9
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.BOK0GNF_.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.BOK0GNF_.js.gz +0 -0
- package/frontend/build/server/chunks/0-_rljpEay.js +0 -22
- package/frontend/build/server/chunks/0-_rljpEay.js.map +0 -5
- package/frontend/build/server/chunks/10-BQBBYpxV.js +0 -14
- package/frontend/build/server/chunks/10-BQBBYpxV.js.map +0 -5
- package/frontend/build/server/chunks/11-10xw0TIP.js +0 -14
- package/frontend/build/server/chunks/11-10xw0TIP.js.map +0 -5
- package/frontend/build/server/chunks/12-CZ3_Uw3c.js +0 -14
- package/frontend/build/server/chunks/12-CZ3_Uw3c.js.map +0 -5
- package/frontend/build/server/chunks/2-DioMbKbC.js +0 -14
- package/frontend/build/server/chunks/3-CgiZg_9v.js +0 -14
- package/frontend/build/server/chunks/4-BM-49dbE.js +0 -14
- package/frontend/build/server/chunks/5-Brx_KOzy.js +0 -14
- package/frontend/build/server/chunks/6-C5swINND.js +0 -14
- package/frontend/build/server/chunks/6-C5swINND.js.map +0 -5
- package/frontend/build/server/chunks/7-B8e4y3CB.js +0 -14
- package/frontend/build/server/chunks/7-B8e4y3CB.js.map +0 -5
- package/frontend/build/server/chunks/8-BZtkZMIZ.js +0 -14
- package/frontend/build/server/chunks/8-BZtkZMIZ.js.map +0 -5
- package/frontend/build/server/chunks/9-BMAKiAem.js +0 -14
- package/frontend/build/server/chunks/Toast-CHB87F8k.js.map +0 -5
- package/frontend/build/server/chunks/_layout.svelte-C_6nsIpT.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-BNwU9zza.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CAlhaCzS.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CTebwzwy.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CmJKt3-H.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-DHZhNTPg.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-ywHy9KCh.js +0 -852
- package/frontend/build/server/chunks/_page.svelte-ywHy9KCh.js.map +0 -5
- package/frontend/build/server/chunks/common-DYYTVHPw.js.map +0 -5
- package/frontend/build/server/chunks/repository-TYWcFuWP.js.map +0 -5
- package/frontend/src/lib/components/ui/Toast.svelte +0 -24
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<script>
|
|
7
|
+
import { fly } from 'svelte/transition';
|
|
8
|
+
import { flip } from 'svelte/animate';
|
|
9
|
+
import { notifications, dismissNotification } from '$lib/stores/notifications';
|
|
10
|
+
import { replayVideoJob, cancelReplayVideo, dismissReplayVideo } from '$lib/stores/replayVideo';
|
|
11
|
+
import { CANCEL_LABEL } from '$lib/copy/common';
|
|
12
|
+
import {
|
|
13
|
+
DISMISS_LABEL,
|
|
14
|
+
VIDEO_TASK_RENDERING,
|
|
15
|
+
VIDEO_TASK_DONE,
|
|
16
|
+
VIDEO_TASK_FAILED,
|
|
17
|
+
VIDEO_TASK_EXPERIMENTAL
|
|
18
|
+
} from '$lib/copy/reports';
|
|
19
|
+
|
|
20
|
+
$: job = $replayVideoJob;
|
|
21
|
+
$: pct = Math.round((job?.progress ?? 0) * 100);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class="stack">
|
|
25
|
+
{#if job}
|
|
26
|
+
<div class="card" transition:fly={{ y: 12, duration: 200 }}>
|
|
27
|
+
{#if job.status === 'rendering'}
|
|
28
|
+
<div class="row">
|
|
29
|
+
<span class="spinner" aria-hidden="true"></span>
|
|
30
|
+
<span class="msg">{VIDEO_TASK_RENDERING} {pct}%</span>
|
|
31
|
+
<button class="link text" on:click={cancelReplayVideo}>{CANCEL_LABEL}</button>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="bar"><span class="fill" style="width:{pct}%"></span></div>
|
|
34
|
+
<p class="sub">{VIDEO_TASK_EXPERIMENTAL}</p>
|
|
35
|
+
{:else if job.status === 'done'}
|
|
36
|
+
<div class="row">
|
|
37
|
+
<span class="dot success"></span>
|
|
38
|
+
<span class="msg">{VIDEO_TASK_DONE}</span>
|
|
39
|
+
</div>
|
|
40
|
+
{:else}
|
|
41
|
+
<div class="row">
|
|
42
|
+
<span class="dot error"></span>
|
|
43
|
+
<span class="msg">{VIDEO_TASK_FAILED}</span>
|
|
44
|
+
<button class="link text" on:click={dismissReplayVideo}>{DISMISS_LABEL}</button>
|
|
45
|
+
</div>
|
|
46
|
+
{#if job.error}<p class="sub">{job.error}</p>{/if}
|
|
47
|
+
{/if}
|
|
48
|
+
</div>
|
|
49
|
+
{/if}
|
|
50
|
+
|
|
51
|
+
{#each $notifications as n (n.id)}
|
|
52
|
+
<div class="card" animate:flip={{ duration: 180 }} transition:fly={{ y: 12, duration: 200 }}>
|
|
53
|
+
<div class="row">
|
|
54
|
+
<span class="dot {n.type}"></span>
|
|
55
|
+
<span class="msg">{n.message}</span>
|
|
56
|
+
<button class="link" on:click={() => dismissNotification(n.id)} aria-label={DISMISS_LABEL}>
|
|
57
|
+
<svg width="12" height="12" viewBox="0 0 14 14" fill="none">
|
|
58
|
+
<path
|
|
59
|
+
d="M1 1l12 12M13 1L1 13"
|
|
60
|
+
stroke="currentColor"
|
|
61
|
+
stroke-width="1.6"
|
|
62
|
+
stroke-linecap="round"
|
|
63
|
+
/>
|
|
64
|
+
</svg>
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
{/each}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
.stack {
|
|
73
|
+
position: fixed;
|
|
74
|
+
right: 1rem;
|
|
75
|
+
bottom: 3.25rem;
|
|
76
|
+
/* Above the replay modal (500) so feedback stays visible over it. */
|
|
77
|
+
z-index: 600;
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column-reverse;
|
|
80
|
+
gap: 0.5rem;
|
|
81
|
+
max-width: min(20rem, calc(100vw - 2rem));
|
|
82
|
+
pointer-events: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.card {
|
|
86
|
+
pointer-events: auto;
|
|
87
|
+
padding: 0.7rem 0.85rem;
|
|
88
|
+
background: var(--bg-elevated);
|
|
89
|
+
border: 1px solid var(--border);
|
|
90
|
+
border-radius: var(--radius-md);
|
|
91
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);
|
|
92
|
+
font-size: 0.82rem;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.row {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: 0.55rem;
|
|
99
|
+
}
|
|
100
|
+
.msg {
|
|
101
|
+
flex: 1;
|
|
102
|
+
color: var(--text);
|
|
103
|
+
line-height: 1.35;
|
|
104
|
+
}
|
|
105
|
+
.sub {
|
|
106
|
+
margin-top: 0.4rem;
|
|
107
|
+
color: var(--text-muted);
|
|
108
|
+
font-size: 0.72rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.dot {
|
|
112
|
+
width: 7px;
|
|
113
|
+
height: 7px;
|
|
114
|
+
border-radius: 50%;
|
|
115
|
+
flex-shrink: 0;
|
|
116
|
+
background: var(--text-muted);
|
|
117
|
+
}
|
|
118
|
+
.dot.success {
|
|
119
|
+
background: var(--pass);
|
|
120
|
+
}
|
|
121
|
+
.dot.error {
|
|
122
|
+
background: var(--fail);
|
|
123
|
+
}
|
|
124
|
+
.dot.info {
|
|
125
|
+
background: var(--accent);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.link {
|
|
129
|
+
display: inline-flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
background: none;
|
|
132
|
+
border: none;
|
|
133
|
+
color: var(--text-muted);
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
padding: 0;
|
|
136
|
+
flex-shrink: 0;
|
|
137
|
+
}
|
|
138
|
+
.link.text {
|
|
139
|
+
font-size: 0.75rem;
|
|
140
|
+
}
|
|
141
|
+
.link:hover {
|
|
142
|
+
color: var(--accent);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.bar {
|
|
146
|
+
margin-top: 0.55rem;
|
|
147
|
+
height: 4px;
|
|
148
|
+
border-radius: var(--radius-pill);
|
|
149
|
+
background: var(--bg-subtle);
|
|
150
|
+
overflow: hidden;
|
|
151
|
+
}
|
|
152
|
+
.fill {
|
|
153
|
+
display: block;
|
|
154
|
+
height: 100%;
|
|
155
|
+
background: var(--accent);
|
|
156
|
+
transition: width 0.2s var(--ease-out);
|
|
157
|
+
}
|
|
158
|
+
.spinner {
|
|
159
|
+
width: 12px;
|
|
160
|
+
height: 12px;
|
|
161
|
+
flex-shrink: 0;
|
|
162
|
+
border: 2px solid var(--border);
|
|
163
|
+
border-top-color: var(--accent);
|
|
164
|
+
border-radius: 50%;
|
|
165
|
+
animation: spin 0.7s linear infinite;
|
|
166
|
+
}
|
|
167
|
+
@keyframes spin {
|
|
168
|
+
to {
|
|
169
|
+
transform: rotate(360deg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.spinner {
|
|
174
|
+
animation-duration: 2s;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
@@ -26,6 +26,15 @@ export const SLACK_LABEL = 'Slack';
|
|
|
26
26
|
export const EMAIL_LABEL = 'Email';
|
|
27
27
|
export const PASSWORD_LABEL = 'Password';
|
|
28
28
|
|
|
29
|
+
export const EXPORT_LABEL = 'Export';
|
|
30
|
+
export const EXPORTING_LABEL = 'Exporting…';
|
|
31
|
+
export const EXPORT_MENU_ITEMS = [
|
|
32
|
+
{ id: 'csv', label: 'CSV', hint: 'Download report' },
|
|
33
|
+
{ id: 'json', label: 'JSON', hint: 'Download JSON. Re-importable' }
|
|
34
|
+
];
|
|
35
|
+
export const exportFailedToast = (what) => `Could not export ${what}.`;
|
|
36
|
+
export const exportedToast = (what) => `${what} exported.`;
|
|
37
|
+
|
|
29
38
|
export const CLEAR_SEARCH_LABEL = 'Clear search';
|
|
30
39
|
export const SORT_BY_LABEL = 'Sort by';
|
|
31
40
|
export const LOADING_LABEL = 'Loading…';
|
|
@@ -55,6 +55,26 @@ export const caseLabel = (index) => `Case ${index}`;
|
|
|
55
55
|
// ── Recording replay ──
|
|
56
56
|
export const PLAYER_LOAD_ERROR = 'Could not load this recording.';
|
|
57
57
|
export const INSPECT_TOGGLE_LABEL = 'Inspect element';
|
|
58
|
+
|
|
59
|
+
export const REPORT_EXPORT_MENU_ITEMS = [
|
|
60
|
+
{ id: 'csv', label: 'CSV', hint: 'Download report' },
|
|
61
|
+
{ id: 'json', label: 'JSON', hint: 'Structured summary' }
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// ── Replay download (screenshot / video) ──
|
|
65
|
+
export const REPLAY_DOWNLOAD_LABEL = 'Download';
|
|
66
|
+
export const SCREENSHOT_LABEL = 'Screenshot';
|
|
67
|
+
export const SCREENSHOT_HINT = 'PNG of the current frame';
|
|
68
|
+
export const RECORD_VIDEO_LABEL = 'Record video';
|
|
69
|
+
export const RECORD_VIDEO_HINT = 'WebM — experimental, stays in your browser';
|
|
70
|
+
export const VIDEO_MULTITAB_UNSUPPORTED = 'Video export is only available for single-tab replays.';
|
|
71
|
+
export const SCREENSHOT_SAVED_TOAST = 'Screenshot saved.';
|
|
72
|
+
export const SCREENSHOT_FAILED_TOAST = 'Could not capture this frame.';
|
|
73
|
+
export const VIDEO_TASK_RENDERING = 'Rendering replay video…';
|
|
74
|
+
export const VIDEO_TASK_DONE = 'Replay video downloaded';
|
|
75
|
+
export const VIDEO_TASK_FAILED = 'Replay video render failed';
|
|
76
|
+
export const VIDEO_TASK_EXPERIMENTAL = 'Experimental — quality varies. Nothing is uploaded.';
|
|
77
|
+
export const DISMISS_LABEL = 'Dismiss';
|
|
58
78
|
export const RESTART_LABEL = 'Restart replay';
|
|
59
79
|
export const STEPS_RAIL_HEADING = 'Steps';
|
|
60
80
|
export const INSPECTOR_HEADING = 'Inspector';
|
|
@@ -19,6 +19,10 @@ export const HEADING = 'Test Repository';
|
|
|
19
19
|
export const HEADER_DESC = 'Manage test suites, cases, and track manual test runs.';
|
|
20
20
|
export const NEW_SUITE_LABEL = '+ New Suite';
|
|
21
21
|
export const NEW_RUN_LABEL = '+ New Run';
|
|
22
|
+
export const IMPORT_TEST_CASES_LINK = 'Import from file';
|
|
23
|
+
export const IMPORT_TEST_CASES_HREF = '/settings?section=testcases';
|
|
24
|
+
export const EXPORT_TEST_CASES_WHAT = 'Test cases';
|
|
25
|
+
export const EXPORT_SUITE_WHAT = 'Suite';
|
|
22
26
|
export const SUITES_TAB_LABEL = 'Suites';
|
|
23
27
|
export const RUNS_TAB_LABEL = 'Test Runs';
|
|
24
28
|
export const SEARCH_PLACEHOLDER = 'Search by ID or name…';
|
|
@@ -19,6 +19,21 @@ export const PROJECT_LABEL = 'Project';
|
|
|
19
19
|
export const RUNNERS_LABEL = 'Runners';
|
|
20
20
|
export const REPOSITORY_NAV_LABEL = 'Repository';
|
|
21
21
|
export const REPOSITORY_HEADING = 'Test Repository';
|
|
22
|
+
export const TEST_CASES_NAV_LABEL = 'Test Cases';
|
|
23
|
+
export const TEST_CASES_HEADING = 'Test Cases';
|
|
24
|
+
export const TEST_CASES_DESC = 'Bring test cases in from a Plum export file.';
|
|
25
|
+
export const TC_IMPORT_CARD_TITLE = 'Import';
|
|
26
|
+
export const TC_IMPORT_DESC =
|
|
27
|
+
'Upload a JSON file exported from Test Repository. Suites and cases are added as new records.';
|
|
28
|
+
export const TC_IMPORT_HINT =
|
|
29
|
+
'A case is skipped when its ID already exists here. Cases with a different prefix or no ID are imported with a fresh ID.';
|
|
30
|
+
export const TC_IMPORT_FAILED_FALLBACK = 'Could not read that file.';
|
|
31
|
+
export const tcImportLabel = (importing) => (importing ? 'Importing…' : 'Import');
|
|
32
|
+
export const tcImportSummary = ({ importedCases, importedSuites, skippedCases, skippedSuites }) => {
|
|
33
|
+
const imported = `Imported ${importedCases} ${importedCases === 1 ? 'case' : 'cases'} in ${importedSuites} ${importedSuites === 1 ? 'suite' : 'suites'}`;
|
|
34
|
+
const skipped = skippedCases + skippedSuites;
|
|
35
|
+
return skipped > 0 ? `${imported} · skipped ${skippedCases} already here` : imported + '.';
|
|
36
|
+
};
|
|
22
37
|
export const INTEGRATIONS_LABEL = 'Integrations';
|
|
23
38
|
export const MCP_NAV_LABEL = 'MCP';
|
|
24
39
|
export const MCP_HEADING = 'MCP Integration';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { writable } from 'svelte/store';
|
|
7
|
+
import { TOAST_TIMEOUT_MS } from '$lib/constants';
|
|
8
|
+
|
|
9
|
+
// { id, type: 'success' | 'error' | 'info', message, sticky }
|
|
10
|
+
export const notifications = writable([]);
|
|
11
|
+
|
|
12
|
+
let seq = 0;
|
|
13
|
+
|
|
14
|
+
export function notify(type, message, { sticky = false, timeout = TOAST_TIMEOUT_MS } = {}) {
|
|
15
|
+
const id = ++seq;
|
|
16
|
+
notifications.update((list) => [...list, { id, type, message, sticky }]);
|
|
17
|
+
if (!sticky) setTimeout(() => dismissNotification(id), timeout);
|
|
18
|
+
return id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function dismissNotification(id) {
|
|
22
|
+
notifications.update((list) => list.filter((n) => n.id !== id));
|
|
23
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { writable, get } from 'svelte/store';
|
|
7
|
+
import { renderReplayToWebM } from '$lib/utils/replayVideo';
|
|
8
|
+
import { downloadBlob } from '$lib/utils/download';
|
|
9
|
+
|
|
10
|
+
// null when idle; otherwise { status, progress, filename, error }.
|
|
11
|
+
// status: 'rendering' | 'done' | 'error'
|
|
12
|
+
export const replayVideoJob = writable(null);
|
|
13
|
+
|
|
14
|
+
let cancelled = false;
|
|
15
|
+
|
|
16
|
+
export function cancelReplayVideo() {
|
|
17
|
+
cancelled = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function dismissReplayVideo() {
|
|
21
|
+
replayVideoJob.set(null);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** `replayer` is the live rrweb replayer to record; it is not retained. */
|
|
25
|
+
export async function startReplayVideo({ replayer, durationMs, filename }) {
|
|
26
|
+
if (get(replayVideoJob)?.status === 'rendering') return;
|
|
27
|
+
cancelled = false;
|
|
28
|
+
replayVideoJob.set({ status: 'rendering', progress: 0, filename });
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const blob = await renderReplayToWebM({
|
|
32
|
+
replayer,
|
|
33
|
+
durationMs,
|
|
34
|
+
onProgress: (p) => replayVideoJob.update((j) => (j ? { ...j, progress: p } : j)),
|
|
35
|
+
shouldCancel: () => cancelled
|
|
36
|
+
});
|
|
37
|
+
if (cancelled) {
|
|
38
|
+
replayVideoJob.set(null);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
downloadBlob(blob, filename);
|
|
42
|
+
replayVideoJob.set({ status: 'done', progress: 1, filename });
|
|
43
|
+
setTimeout(() => replayVideoJob.update((j) => (j?.status === 'done' ? null : j)), 6000);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
if (cancelled || e.message === 'Cancelled.') {
|
|
46
|
+
replayVideoJob.set(null);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
replayVideoJob.set({ status: 'error', progress: 0, filename, error: e.message });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function downloadBlob(blob, filename) {
|
|
7
|
+
const url = URL.createObjectURL(blob);
|
|
8
|
+
const a = document.createElement('a');
|
|
9
|
+
a.href = url;
|
|
10
|
+
a.download = filename;
|
|
11
|
+
document.body.appendChild(a);
|
|
12
|
+
a.click();
|
|
13
|
+
a.remove();
|
|
14
|
+
// Revoking synchronously can cancel the download in some browsers.
|
|
15
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function filenameFromDisposition(header, fallback) {
|
|
19
|
+
const match = /filename="?([^"]+)"?/.exec(header ?? '');
|
|
20
|
+
return match ? match[1] : fallback;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function downloadFromEndpoint(url, { headers = {}, fallbackName = 'export' } = {}) {
|
|
24
|
+
const res = await fetch(url, { headers });
|
|
25
|
+
if (!res.ok) throw new Error('Export failed');
|
|
26
|
+
const blob = await res.blob();
|
|
27
|
+
downloadBlob(blob, filenameFromDisposition(res.headers.get('Content-Disposition'), fallbackName));
|
|
28
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as htmlToImage from 'html-to-image';
|
|
7
|
+
|
|
8
|
+
function toBase64(buffer) {
|
|
9
|
+
const bytes = new Uint8Array(buffer);
|
|
10
|
+
let binary = '';
|
|
11
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
12
|
+
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
|
|
13
|
+
}
|
|
14
|
+
return btoa(binary);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const fontMime = (url) =>
|
|
18
|
+
url.includes('woff2') ? 'font/woff2' : url.includes('woff') ? 'font/woff' : 'font/ttf';
|
|
19
|
+
|
|
20
|
+
const FONT_FACE_RE = /@font-face\s*{[^}]*}/g;
|
|
21
|
+
|
|
22
|
+
async function faceTextsFromHref(href) {
|
|
23
|
+
if (!href) return [];
|
|
24
|
+
try {
|
|
25
|
+
const css = await fetch(href).then((r) => r.text());
|
|
26
|
+
return css.match(FONT_FACE_RE) ?? [];
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// html-to-image's own font embedding misses @font-face rules from a cross-origin
|
|
33
|
+
// stylesheet (the tested page's Google Fonts <link>) — the capture then falls
|
|
34
|
+
// back to a default face. Re-fetch the CORS-blocked sheets by URL ourselves.
|
|
35
|
+
export async function collectFontEmbedCSS(doc) {
|
|
36
|
+
const rules = [];
|
|
37
|
+
for (const sheet of doc.styleSheets) {
|
|
38
|
+
let cssRules;
|
|
39
|
+
try {
|
|
40
|
+
cssRules = sheet.cssRules;
|
|
41
|
+
} catch {
|
|
42
|
+
rules.push(...(await faceTextsFromHref(sheet.href)));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
for (const rule of cssRules) {
|
|
46
|
+
if (rule.type === 5 || rule.constructor?.name === 'CSSFontFaceRule') {
|
|
47
|
+
rules.push(rule.cssText);
|
|
48
|
+
} else if (rule.type === 3 && rule.href) {
|
|
49
|
+
let reachable = true;
|
|
50
|
+
try {
|
|
51
|
+
void rule.styleSheet.cssRules;
|
|
52
|
+
} catch {
|
|
53
|
+
reachable = false;
|
|
54
|
+
}
|
|
55
|
+
if (!reachable) rules.push(...(await faceTextsFromHref(rule.href)));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cache = new Map();
|
|
61
|
+
const out = [];
|
|
62
|
+
for (let text of rules) {
|
|
63
|
+
const urls = [...text.matchAll(/url\((['"]?)(https?:\/\/[^'")]+)\1\)/g)].map((m) => m[2]);
|
|
64
|
+
for (const url of urls) {
|
|
65
|
+
try {
|
|
66
|
+
if (!cache.has(url)) {
|
|
67
|
+
const buf = await fetch(url).then((r) => r.arrayBuffer());
|
|
68
|
+
cache.set(url, `data:${fontMime(url)};base64,${toBase64(buf)}`);
|
|
69
|
+
}
|
|
70
|
+
text = text.split(url).join(cache.get(url));
|
|
71
|
+
} catch {
|
|
72
|
+
// unreachable font — leave the original url, the face just won't embed
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
out.push(text);
|
|
76
|
+
}
|
|
77
|
+
return out.join('\n');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** PNG blob of whatever the given rrweb replayer is currently showing. */
|
|
81
|
+
export async function captureReplayFrame(replayer) {
|
|
82
|
+
const doc = replayer?.iframe?.contentDocument;
|
|
83
|
+
if (!doc?.documentElement) throw new Error('Nothing to capture yet — let the replay load first.');
|
|
84
|
+
const fontEmbedCSS = await collectFontEmbedCSS(doc).catch(() => '');
|
|
85
|
+
const blob = await htmlToImage.toBlob(doc.documentElement, {
|
|
86
|
+
backgroundColor: '#ffffff',
|
|
87
|
+
fontEmbedCSS
|
|
88
|
+
});
|
|
89
|
+
if (!blob) throw new Error('Could not capture this frame.');
|
|
90
|
+
return blob;
|
|
91
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as htmlToImage from 'html-to-image';
|
|
7
|
+
import fixWebmDuration from 'fix-webm-duration';
|
|
8
|
+
import { collectFontEmbedCSS } from './replayCapture';
|
|
9
|
+
|
|
10
|
+
const FPS = 12;
|
|
11
|
+
const TAIL_MS = 400;
|
|
12
|
+
|
|
13
|
+
const supportedMime = () =>
|
|
14
|
+
['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm'].find((m) =>
|
|
15
|
+
MediaRecorder.isTypeSupported(m)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Records the already-loaded replayer to a WebM blob in the browser — nothing is
|
|
20
|
+
* uploaded. Plays it through in real time (so a mid-recording navigation applies
|
|
21
|
+
* the same as normal playback) while html-to-image snapshots each frame onto a
|
|
22
|
+
* canvas the MediaRecorder samples at FPS; a slow snapshot just repeats the
|
|
23
|
+
* previous frame, so the clip stays the right length.
|
|
24
|
+
*
|
|
25
|
+
* A second rrweb player can't be spun up off-screen for this (two instances
|
|
26
|
+
* fight over shared module state and the hidden one renders blank), so it drives
|
|
27
|
+
* the visible replayer and the caller restores its position afterwards.
|
|
28
|
+
*/
|
|
29
|
+
export async function renderReplayToWebM({ replayer, durationMs, onProgress, shouldCancel }) {
|
|
30
|
+
const iframe = replayer?.iframe;
|
|
31
|
+
if (!iframe?.contentDocument?.documentElement) throw new Error('The replay is not ready yet.');
|
|
32
|
+
if (!durationMs || durationMs < 200) throw new Error('This recording is too short to record.');
|
|
33
|
+
if (typeof MediaRecorder === 'undefined') throw new Error("This browser can't record video.");
|
|
34
|
+
const mime = supportedMime();
|
|
35
|
+
if (!mime) throw new Error("This browser can't record WebM video.");
|
|
36
|
+
|
|
37
|
+
const w = Math.max(320, iframe.offsetWidth || 1280);
|
|
38
|
+
const h = Math.max(240, iframe.offsetHeight || 720);
|
|
39
|
+
const canvas = document.createElement('canvas');
|
|
40
|
+
canvas.width = w;
|
|
41
|
+
canvas.height = h;
|
|
42
|
+
const ctx = canvas.getContext('2d');
|
|
43
|
+
const paintWhite = () => {
|
|
44
|
+
ctx.fillStyle = '#ffffff';
|
|
45
|
+
ctx.fillRect(0, 0, w, h);
|
|
46
|
+
};
|
|
47
|
+
paintWhite();
|
|
48
|
+
|
|
49
|
+
const fontEmbedCSS = await collectFontEmbedCSS(iframe.contentDocument).catch(() => '');
|
|
50
|
+
|
|
51
|
+
const recorder = new MediaRecorder(canvas.captureStream(FPS), {
|
|
52
|
+
mimeType: mime,
|
|
53
|
+
videoBitsPerSecond: 3_500_000
|
|
54
|
+
});
|
|
55
|
+
const chunks = [];
|
|
56
|
+
recorder.ondataavailable = (e) => e.data.size && chunks.push(e.data);
|
|
57
|
+
const stopped = new Promise((res) => (recorder.onstop = res));
|
|
58
|
+
recorder.start(250);
|
|
59
|
+
|
|
60
|
+
replayer.play(0);
|
|
61
|
+
const start = performance.now();
|
|
62
|
+
let capturing = false;
|
|
63
|
+
let renderedMs = 0;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
function tick() {
|
|
68
|
+
if (shouldCancel?.()) return reject(new Error('Cancelled.'));
|
|
69
|
+
renderedMs = performance.now() - start;
|
|
70
|
+
onProgress?.(Math.min(1, renderedMs / durationMs));
|
|
71
|
+
|
|
72
|
+
if (!capturing) {
|
|
73
|
+
capturing = true;
|
|
74
|
+
htmlToImage
|
|
75
|
+
.toCanvas(iframe.contentDocument.documentElement, {
|
|
76
|
+
width: w,
|
|
77
|
+
height: h,
|
|
78
|
+
backgroundColor: '#ffffff',
|
|
79
|
+
fontEmbedCSS
|
|
80
|
+
})
|
|
81
|
+
.then((frame) => {
|
|
82
|
+
paintWhite();
|
|
83
|
+
ctx.drawImage(frame, 0, 0, w, h);
|
|
84
|
+
})
|
|
85
|
+
.catch(() => {})
|
|
86
|
+
.finally(() => {
|
|
87
|
+
capturing = false;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (renderedMs >= durationMs + TAIL_MS) resolve();
|
|
92
|
+
else requestAnimationFrame(tick);
|
|
93
|
+
}
|
|
94
|
+
requestAnimationFrame(tick);
|
|
95
|
+
});
|
|
96
|
+
} finally {
|
|
97
|
+
recorder.stop();
|
|
98
|
+
await stopped;
|
|
99
|
+
try {
|
|
100
|
+
replayer.pause(0);
|
|
101
|
+
} catch {
|
|
102
|
+
// caller rebuilds the player anyway
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onProgress?.(1);
|
|
107
|
+
const raw = new Blob(chunks, { type: mime });
|
|
108
|
+
// MediaRecorder WebM carries no duration in its header — players show it as
|
|
109
|
+
// unseekable / 0:00 until playback ends. Patch the EBML so the file behaves.
|
|
110
|
+
return fixWebmDuration(raw, Math.round(renderedMs), { logger: false }).catch(() => raw);
|
|
111
|
+
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import Nav from '$lib/components/layout/Nav.svelte';
|
|
12
12
|
import PageShell from '$lib/components/layout/PageShell.svelte';
|
|
13
13
|
import RunnerPanel from '$lib/components/layout/RunnerPanel.svelte';
|
|
14
|
+
import NotificationStack from '$lib/components/ui/NotificationStack.svelte';
|
|
14
15
|
import { auth } from '$lib/stores/auth';
|
|
15
16
|
import { checkNeedsSetup } from '$lib/api/auth';
|
|
16
17
|
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
<slot />
|
|
52
53
|
</PageShell>
|
|
53
54
|
<RunnerPanel />
|
|
55
|
+
<NotificationStack />
|
|
54
56
|
{/if}
|
|
55
57
|
{:else}
|
|
56
58
|
<div class="boot-loading">Loading…</div>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { page } from '$app/stores';
|
|
8
8
|
import { onMount } from 'svelte';
|
|
9
9
|
import { slide } from 'svelte/transition';
|
|
10
|
-
import { fetchReportDetail, fetchRecordings } from '$lib/api/reports';
|
|
10
|
+
import { fetchReportDetail, fetchRecordings, downloadReportExport } from '$lib/api/reports';
|
|
11
11
|
import {
|
|
12
12
|
isScheduled,
|
|
13
13
|
triggerLabel,
|
|
@@ -39,10 +39,14 @@
|
|
|
39
39
|
attemptsLabel,
|
|
40
40
|
caseLabel,
|
|
41
41
|
UNKNOWN_RUNNER_LABEL,
|
|
42
|
-
workerLabel
|
|
42
|
+
workerLabel,
|
|
43
|
+
REPORT_EXPORT_MENU_ITEMS
|
|
43
44
|
} from '$lib/copy/reports';
|
|
45
|
+
import { exportFailedToast, exportedToast } from '$lib/copy/common';
|
|
46
|
+
import { notify } from '$lib/stores/notifications';
|
|
44
47
|
import Badge from '$lib/components/ui/Badge.svelte';
|
|
45
48
|
import BackLink from '$lib/components/ui/BackLink.svelte';
|
|
49
|
+
import ExportMenu from '$lib/components/ui/ExportMenu.svelte';
|
|
46
50
|
import StatusDot from '$lib/components/ui/StatusDot.svelte';
|
|
47
51
|
import TagChip from '$lib/components/ui/TagChip.svelte';
|
|
48
52
|
import StepKeyword from '$lib/components/ui/StepKeyword.svelte';
|
|
@@ -63,6 +67,19 @@
|
|
|
63
67
|
let scenarioRecordings = [];
|
|
64
68
|
let replayInspecting = false;
|
|
65
69
|
|
|
70
|
+
let exporting = false;
|
|
71
|
+
async function handleExport(format) {
|
|
72
|
+
exporting = true;
|
|
73
|
+
try {
|
|
74
|
+
await downloadReportExport(reportId, format);
|
|
75
|
+
notify('success', exportedToast('Report'));
|
|
76
|
+
} catch {
|
|
77
|
+
notify('error', exportFailedToast('this report'));
|
|
78
|
+
} finally {
|
|
79
|
+
exporting = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
function scenarioHasRecording(scenario) {
|
|
67
84
|
return allRecordings.some((r) => r.scenarioId === scenario.id);
|
|
68
85
|
}
|
|
@@ -128,7 +145,16 @@
|
|
|
128
145
|
|
|
129
146
|
<svelte:head><title>{DETAIL_PAGE_TITLE}</title></svelte:head>
|
|
130
147
|
|
|
131
|
-
<
|
|
148
|
+
<div class="detail-top">
|
|
149
|
+
<BackLink href="/reports" label={REPORTS_BACK_LABEL} />
|
|
150
|
+
{#if detail}
|
|
151
|
+
<ExportMenu
|
|
152
|
+
items={REPORT_EXPORT_MENU_ITEMS}
|
|
153
|
+
busy={exporting}
|
|
154
|
+
on:select={(e) => handleExport(e.detail)}
|
|
155
|
+
/>
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
132
158
|
|
|
133
159
|
{#if error}
|
|
134
160
|
<div class="error-state">{error}</div>
|
|
@@ -612,6 +638,7 @@
|
|
|
612
638
|
{reportId}
|
|
613
639
|
recordings={scenarioRecordings}
|
|
614
640
|
steps={replayScenario.steps}
|
|
641
|
+
scenarioName={replayScenario.name}
|
|
615
642
|
bind:inspecting={replayInspecting}
|
|
616
643
|
/>
|
|
617
644
|
{/if}
|
|
@@ -621,6 +648,14 @@
|
|
|
621
648
|
{/if}
|
|
622
649
|
|
|
623
650
|
<style>
|
|
651
|
+
.detail-top {
|
|
652
|
+
display: flex;
|
|
653
|
+
align-items: center;
|
|
654
|
+
justify-content: space-between;
|
|
655
|
+
gap: 1rem;
|
|
656
|
+
margin-bottom: 1rem;
|
|
657
|
+
}
|
|
658
|
+
|
|
624
659
|
.report-header {
|
|
625
660
|
border-radius: var(--radius-lg);
|
|
626
661
|
border: 1px solid var(--border);
|