plum-e2e 2.6.8 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1040) hide show
  1. package/.env +5 -0
  2. package/CLAUDE.md +17 -2
  3. package/LICENSE +21 -677
  4. package/README.md +1 -1
  5. package/backend/Dockerfile +1 -13
  6. package/backend/app.js +4 -14
  7. package/backend/config/scripts/create-step.mjs +1 -13
  8. package/backend/config/scripts/create-test.mjs +1 -13
  9. package/backend/config/scripts/generate-report.js +5 -15
  10. package/backend/config/scripts/run-tests.js +5 -16
  11. package/backend/constants/defaults.js +8 -0
  12. package/backend/constants/env.js +16 -0
  13. package/backend/constants/jobStatus.js +21 -0
  14. package/backend/constants/socketEvents.js +40 -0
  15. package/backend/constants/triggers.js +6 -14
  16. package/backend/entrypoint.sh +1 -13
  17. package/backend/lib/authHeader.js +16 -0
  18. package/backend/lib/globalRegistry.js +1 -13
  19. package/backend/lib/nodeRegister.js +1 -13
  20. package/backend/lib/reportFilename.js +1 -13
  21. package/backend/lib/retryRunner.js +1 -13
  22. package/backend/lib/runnerProcess.js +34 -31
  23. package/backend/lib/screenshotPoller.js +1 -13
  24. package/backend/lib/serverBootstrap.js +189 -0
  25. package/backend/lib/serverConfig.js +1 -13
  26. package/backend/lib/testChunker.js +1 -13
  27. package/backend/lib/testEnv.js +1 -13
  28. package/backend/logs/runner-cmqneqerz0000qq01i5ap2rvl.log +4 -0
  29. package/backend/logs/runner-cmqnfv7kr0000r101aeocm8eu.log +4 -0
  30. package/backend/logs/runner-cmqnfvb560001r101qoi0phau.log +4 -0
  31. package/backend/logs/runner-cmqnfvlm20002r101gsyqb837.log +4 -0
  32. package/backend/logs/runner-cmqnfvqfy0003r101fh41pzx3.log +4 -0
  33. package/backend/logs/runner-cmqnfvvwo0004r101q4dtqxd2.log +4 -0
  34. package/backend/logs/runner-cmr8o2x3n0000mg01410tpuck.log +4 -0
  35. package/backend/logs/runner-cmr90hvcx0000n2016edtmrtv.log +4 -0
  36. package/backend/logs/runner-cmr90i7h20001n201g2wg6v3n.log +4 -0
  37. package/backend/logs/runner-cmr90iilm0002n201mmisva95.log +4 -0
  38. package/backend/logs/runner-cmr90j70r0003n201xz8525dz.log +4 -0
  39. package/backend/logs/runner-cmr90yup70000qp017ianc2gp.log +4 -0
  40. package/backend/logs/runner-cmr90zbtm0001qp01u9tfdkh1.log +4 -0
  41. package/backend/logs/runner-cmr91a3mw0000tb01414rd1df.log +4 -0
  42. package/backend/logs/runner-cmr91dhla0001tb01jp7thjnl.log +4 -0
  43. package/backend/logs/runner-cmrkk6fcl0000qj012kve79f4.log +4 -0
  44. package/backend/logs/runner-cmrkl8sym0000qy01euz4270n.log +4 -0
  45. package/backend/logs/runner-cmrkl9oyx0001qy0137nzr94d.log +4 -0
  46. package/backend/mcp/server.js +5 -15
  47. package/backend/middleware/auth.js +4 -14
  48. package/backend/middleware/jwtAuth.js +5 -16
  49. package/backend/middleware/requireAdmin.js +1 -13
  50. package/backend/package.json +3 -3
  51. package/backend/playwright.config.js +1 -13
  52. package/backend/routes/active-runs.routes.js +18 -0
  53. package/backend/routes/auth.routes.js +1 -13
  54. package/backend/routes/backup.routes.js +10 -21
  55. package/backend/routes/cron.routes.js +1 -13
  56. package/backend/routes/node.routes.js +11 -148
  57. package/backend/routes/reports.routes.js +1 -13
  58. package/backend/routes/runners.routes.js +11 -21
  59. package/backend/routes/settings.routes.js +6 -17
  60. package/backend/routes/test-cases.routes.js +1 -13
  61. package/backend/routes/test-runs.routes.js +1 -13
  62. package/backend/routes/test-suites.routes.js +1 -13
  63. package/backend/routes/tests.routes.js +1 -13
  64. package/backend/routes/trigger.routes.js +23 -165
  65. package/backend/routes/users.routes.js +1 -13
  66. package/backend/scripts/manage-runners.mjs +1 -13
  67. package/backend/server.js +34 -151
  68. package/backend/services/activeRunsService.js +55 -0
  69. package/backend/services/backupCronService.js +1 -13
  70. package/backend/services/backupService.js +7 -17
  71. package/backend/services/cronService.js +57 -39
  72. package/backend/services/envService.js +1 -13
  73. package/backend/services/nodeExecutionService.js +140 -0
  74. package/backend/services/notificationService.js +7 -17
  75. package/backend/services/prisma.js +1 -13
  76. package/backend/services/reportService.js +21 -17
  77. package/backend/services/runnerService.js +46 -27
  78. package/backend/services/settingsService.js +19 -19
  79. package/backend/services/testCaseService.js +1 -13
  80. package/backend/services/testRunService.js +1 -13
  81. package/backend/services/testService.js +1 -13
  82. package/backend/services/testSuiteService.js +1 -13
  83. package/backend/services/triggerService.js +246 -0
  84. package/backend/services/userService.js +1 -13
  85. package/backend/websockets/socketHandler.js +99 -48
  86. package/bin/plum.js +14 -13
  87. package/bin/postinstall.js +34 -0
  88. package/bin/scaffold-tests.js +1 -13
  89. package/docker-compose.node.yml +1 -13
  90. package/docker-compose.yml +1 -13
  91. package/frontend/.svelte-kit/adapter-node/.vite/manifest.json +97 -41
  92. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/BackLink.CvWWuO3h.css +0 -17
  93. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Badge.D1SV0Gv7.css +0 -17
  94. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css +1 -0
  95. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/EmptyState.DUDM4P19.css +1 -0
  96. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Modal.BuaCfdLj.css +1 -0
  97. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Pagination.DZmi2Po-.css +0 -17
  98. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Toast.DENwYn3g.css +0 -17
  99. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_layout.BuhMvMUj.css +0 -17
  100. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.0Eln51hV.css +0 -17
  101. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.5IJAEAjc.css +0 -17
  102. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BR3wgXcZ.css +0 -17
  103. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BWAH0Beh.css +1 -0
  104. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BZfe6nWe.css +0 -17
  105. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Bj_a_5lv.css +0 -17
  106. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BwgOqeMo.css +1 -0
  107. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.CBHi6pzT.css +0 -17
  108. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.CFnbmY-D.css +0 -17
  109. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.DvMkmYA-.css +1 -0
  110. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page._h2o67kg.css +0 -17
  111. package/frontend/.svelte-kit/adapter-node/chunks/BackLink.js +0 -17
  112. package/frontend/.svelte-kit/adapter-node/chunks/Badge.js +1 -18
  113. package/frontend/.svelte-kit/adapter-node/chunks/ConfirmModal.js +7 -37
  114. package/frontend/.svelte-kit/adapter-node/chunks/EmptyState.js +25 -25
  115. package/frontend/.svelte-kit/adapter-node/chunks/Modal.js +21 -0
  116. package/frontend/.svelte-kit/adapter-node/chunks/Toast.js +12 -18
  117. package/frontend/.svelte-kit/adapter-node/chunks/attributes.js +0 -17
  118. package/frontend/.svelte-kit/adapter-node/chunks/auth.js +0 -17
  119. package/frontend/.svelte-kit/adapter-node/chunks/auth2.js +8 -0
  120. package/frontend/.svelte-kit/adapter-node/chunks/client.js +0 -17
  121. package/frontend/.svelte-kit/adapter-node/chunks/common.js +36 -0
  122. package/frontend/.svelte-kit/adapter-node/chunks/constants.js +8 -20
  123. package/frontend/.svelte-kit/adapter-node/chunks/escaping.js +0 -17
  124. package/frontend/.svelte-kit/adapter-node/chunks/exports.js +0 -17
  125. package/frontend/.svelte-kit/adapter-node/chunks/format.js +0 -17
  126. package/frontend/.svelte-kit/adapter-node/chunks/index-server.js +0 -17
  127. package/frontend/.svelte-kit/adapter-node/chunks/index.js +4 -21
  128. package/frontend/.svelte-kit/adapter-node/chunks/index2.js +0 -17
  129. package/frontend/.svelte-kit/adapter-node/chunks/internal.js +2 -19
  130. package/frontend/.svelte-kit/adapter-node/chunks/reports.js +0 -17
  131. package/frontend/.svelte-kit/adapter-node/chunks/reports2.js +87 -0
  132. package/frontend/.svelte-kit/adapter-node/chunks/repository.js +87 -0
  133. package/frontend/.svelte-kit/adapter-node/chunks/runner.js +9 -20
  134. package/frontend/.svelte-kit/adapter-node/chunks/stores.js +0 -17
  135. package/frontend/.svelte-kit/adapter-node/chunks/theme.js +0 -17
  136. package/frontend/.svelte-kit/adapter-node/entries/fallbacks/error.svelte.js +0 -17
  137. package/frontend/.svelte-kit/adapter-node/entries/pages/_layout.js +0 -17
  138. package/frontend/.svelte-kit/adapter-node/entries/pages/_layout.svelte.js +2 -17
  139. package/frontend/.svelte-kit/adapter-node/entries/pages/_page.svelte.js +135 -101
  140. package/frontend/.svelte-kit/adapter-node/entries/pages/login/_page.svelte.js +5 -20
  141. package/frontend/.svelte-kit/adapter-node/entries/pages/reports/_id_/_page.svelte.js +5 -20
  142. package/frontend/.svelte-kit/adapter-node/entries/pages/reports/_page.svelte.js +17 -43
  143. package/frontend/.svelte-kit/adapter-node/entries/pages/reports/live/_page.svelte.js +27 -42
  144. package/frontend/.svelte-kit/adapter-node/entries/pages/scheduled-tests/_page.svelte.js +68 -50
  145. package/frontend/.svelte-kit/adapter-node/entries/pages/settings/_page.svelte.js +272 -113
  146. package/frontend/.svelte-kit/adapter-node/entries/pages/setup/_page.svelte.js +5 -20
  147. package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/_page.svelte.js +38 -53
  148. package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/runs/_id_/_page.svelte.js +61 -30
  149. package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/suites/_id_/_page.svelte.js +19 -35
  150. package/frontend/.svelte-kit/adapter-node/index.js +0 -17
  151. package/frontend/.svelte-kit/adapter-node/internal.js +0 -17
  152. package/frontend/.svelte-kit/adapter-node/manifest-full.js +1 -18
  153. package/frontend/.svelte-kit/adapter-node/manifest.js +1 -18
  154. package/frontend/.svelte-kit/adapter-node/nodes/0.js +2 -19
  155. package/frontend/.svelte-kit/adapter-node/nodes/1.js +1 -18
  156. package/frontend/.svelte-kit/adapter-node/nodes/10.js +2 -19
  157. package/frontend/.svelte-kit/adapter-node/nodes/11.js +2 -19
  158. package/frontend/.svelte-kit/adapter-node/nodes/12.js +2 -19
  159. package/frontend/.svelte-kit/adapter-node/nodes/2.js +2 -19
  160. package/frontend/.svelte-kit/adapter-node/nodes/3.js +1 -18
  161. package/frontend/.svelte-kit/adapter-node/nodes/4.js +2 -19
  162. package/frontend/.svelte-kit/adapter-node/nodes/5.js +1 -18
  163. package/frontend/.svelte-kit/adapter-node/nodes/6.js +1 -18
  164. package/frontend/.svelte-kit/adapter-node/nodes/7.js +2 -19
  165. package/frontend/.svelte-kit/adapter-node/nodes/8.js +2 -19
  166. package/frontend/.svelte-kit/adapter-node/nodes/9.js +1 -18
  167. package/frontend/.svelte-kit/ambient.d.ts +2 -21
  168. package/frontend/.svelte-kit/generated/client/app.js +0 -17
  169. package/frontend/.svelte-kit/generated/client/matchers.js +0 -17
  170. package/frontend/.svelte-kit/generated/client/nodes/0.js +0 -17
  171. package/frontend/.svelte-kit/generated/client/nodes/1.js +0 -17
  172. package/frontend/.svelte-kit/generated/client/nodes/10.js +0 -17
  173. package/frontend/.svelte-kit/generated/client/nodes/11.js +0 -17
  174. package/frontend/.svelte-kit/generated/client/nodes/12.js +0 -17
  175. package/frontend/.svelte-kit/generated/client/nodes/2.js +0 -17
  176. package/frontend/.svelte-kit/generated/client/nodes/3.js +0 -17
  177. package/frontend/.svelte-kit/generated/client/nodes/4.js +0 -17
  178. package/frontend/.svelte-kit/generated/client/nodes/5.js +0 -17
  179. package/frontend/.svelte-kit/generated/client/nodes/6.js +0 -17
  180. package/frontend/.svelte-kit/generated/client/nodes/7.js +0 -17
  181. package/frontend/.svelte-kit/generated/client/nodes/8.js +0 -17
  182. package/frontend/.svelte-kit/generated/client/nodes/9.js +0 -17
  183. package/frontend/.svelte-kit/generated/client-optimized/app.js +0 -17
  184. package/frontend/.svelte-kit/generated/client-optimized/matchers.js +0 -17
  185. package/frontend/.svelte-kit/generated/client-optimized/nodes/0.js +0 -17
  186. package/frontend/.svelte-kit/generated/client-optimized/nodes/1.js +0 -17
  187. package/frontend/.svelte-kit/generated/client-optimized/nodes/10.js +0 -17
  188. package/frontend/.svelte-kit/generated/client-optimized/nodes/11.js +0 -17
  189. package/frontend/.svelte-kit/generated/client-optimized/nodes/12.js +0 -17
  190. package/frontend/.svelte-kit/generated/client-optimized/nodes/2.js +0 -17
  191. package/frontend/.svelte-kit/generated/client-optimized/nodes/3.js +0 -17
  192. package/frontend/.svelte-kit/generated/client-optimized/nodes/4.js +0 -17
  193. package/frontend/.svelte-kit/generated/client-optimized/nodes/5.js +0 -17
  194. package/frontend/.svelte-kit/generated/client-optimized/nodes/6.js +0 -17
  195. package/frontend/.svelte-kit/generated/client-optimized/nodes/7.js +0 -17
  196. package/frontend/.svelte-kit/generated/client-optimized/nodes/8.js +0 -17
  197. package/frontend/.svelte-kit/generated/client-optimized/nodes/9.js +0 -17
  198. package/frontend/.svelte-kit/generated/root.js +0 -17
  199. package/frontend/.svelte-kit/generated/root.svelte +0 -17
  200. package/frontend/.svelte-kit/generated/server/internal.js +2 -19
  201. package/frontend/.svelte-kit/non-ambient.d.ts +0 -17
  202. package/frontend/.svelte-kit/output/client/.vite/manifest.json +406 -352
  203. package/frontend/.svelte-kit/output/client/_app/immutable/assets/0.BhSG-jZH.css +0 -17
  204. package/frontend/.svelte-kit/output/client/_app/immutable/assets/10.CdaQUS6U.css +0 -17
  205. package/frontend/.svelte-kit/output/client/_app/immutable/assets/11.BWAH0Beh.css +1 -0
  206. package/frontend/.svelte-kit/output/client/_app/immutable/assets/12.Bj_a_5lv.css +0 -17
  207. package/frontend/.svelte-kit/output/client/_app/immutable/assets/2.DvMkmYA-.css +1 -0
  208. package/frontend/.svelte-kit/output/client/_app/immutable/assets/3.BR3wgXcZ.css +0 -17
  209. package/frontend/.svelte-kit/output/client/_app/immutable/assets/4.0Eln51hV.css +0 -17
  210. package/frontend/.svelte-kit/output/client/_app/immutable/assets/5.DFV9OgAu.css +0 -17
  211. package/frontend/.svelte-kit/output/client/_app/immutable/assets/6.CFnbmY-D.css +0 -17
  212. package/frontend/.svelte-kit/output/client/_app/immutable/assets/7.CBHi6pzT.css +0 -17
  213. package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.BwgOqeMo.css +1 -0
  214. package/frontend/.svelte-kit/output/client/_app/immutable/assets/9._h2o67kg.css +0 -17
  215. package/frontend/.svelte-kit/output/client/_app/immutable/assets/BackLink.CvWWuO3h.css +0 -17
  216. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Badge.D1SV0Gv7.css +0 -17
  217. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -17
  218. package/frontend/.svelte-kit/output/client/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css +1 -0
  219. package/frontend/.svelte-kit/output/client/_app/immutable/assets/EmptyState.DUDM4P19.css +1 -0
  220. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Modal.BZpm0HP8.css +1 -0
  221. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Modal.BuaCfdLj.css +1 -0
  222. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Pagination.DZmi2Po-.css +0 -17
  223. package/frontend/.svelte-kit/output/client/_app/immutable/assets/Toast.DENwYn3g.css +0 -17
  224. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_layout.BuhMvMUj.css +0 -17
  225. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.0Eln51hV.css +0 -17
  226. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -17
  227. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BR3wgXcZ.css +0 -17
  228. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BWAH0Beh.css +1 -0
  229. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BZfe6nWe.css +0 -17
  230. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Bj_a_5lv.css +0 -17
  231. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BwgOqeMo.css +1 -0
  232. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.CBHi6pzT.css +0 -17
  233. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.CFnbmY-D.css +0 -17
  234. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.DvMkmYA-.css +1 -0
  235. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page._h2o67kg.css +0 -17
  236. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/2zMvi6fR.js +1 -0
  237. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/3InFPLvE.js +1 -0
  238. package/frontend/{build/client/_app/immutable/chunks/C67QNx7Z.js → .svelte-kit/output/client/_app/immutable/chunks/BAACMRH5.js} +1 -18
  239. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BAMUGSh7.js +1 -0
  240. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BKuh57WU.js +1 -0
  241. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{BFEiffAL.js → BUwxRmHf.js} +1 -18
  242. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Bfc47y5P.js +0 -17
  243. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{BVMec-1T.js → BmZ9upwL.js} +1 -18
  244. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C1Vid6wz.js +1 -0
  245. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C8OURRSs.js +1 -0
  246. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CGeZlCaI.js +1 -0
  247. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CKDtmZUQ.js +1 -0
  248. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CMBQQ9RZ.js +1 -0
  249. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CMEtqUfQ.js +1 -0
  250. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CNdcuGR0.js +1 -0
  251. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CUC98kMZ.js +1 -0
  252. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CgnwuKKO.js +1 -0
  253. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Co3xKhEp.js +1 -0
  254. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CuqnZUex.js +1 -0
  255. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{BkKmeUbA.js → CzlPaHY9.js} +1 -18
  256. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DCgHuuvs.js +1 -0
  257. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DHM6H-es.js +2 -0
  258. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DMZwI8Cj.js +1 -0
  259. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DMyTUV-Y.js +3 -0
  260. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{B0pefNHY.js → DSLBMEpr.js} +1 -18
  261. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{CxTX_lpP.js → DbgD7JLa.js} +1 -18
  262. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Dlnxgs49.js +1 -0
  263. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/TOCyHP97.js +6 -0
  264. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/nURnIknV.js +1 -0
  265. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/t-La0hgu.js +1 -0
  266. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/w0D4Uzlq.js +1 -0
  267. package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.BkBD0hgA.js +2 -0
  268. package/frontend/.svelte-kit/output/client/_app/immutable/entry/start.BxeX1Uxs.js +1 -0
  269. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.ChjDHxSJ.js +6 -0
  270. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/1.DBSWa36f.js +1 -0
  271. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.DEbnxfND.js +1 -0
  272. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.O4cjXCg4.js +1 -0
  273. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.BWQdeRdX.js +1 -0
  274. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/2.BnHalSCi.js +1 -0
  275. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3._S1vrLVF.js +1 -0
  276. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/4.D-YT_JJe.js +1 -0
  277. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.BiFBiWcW.js +1 -0
  278. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.BNS-W0R_.js +1 -0
  279. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.C-pKvVZy.js +1 -0
  280. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.BEUdazrw.js +4 -0
  281. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/9.CrLHYPEJ.js +1 -0
  282. package/frontend/.svelte-kit/output/client/_app/version.json +1 -1
  283. package/frontend/.svelte-kit/output/server/.vite/manifest.json +97 -41
  284. package/frontend/.svelte-kit/output/server/_app/immutable/assets/BackLink.CvWWuO3h.css +0 -17
  285. package/frontend/.svelte-kit/output/server/_app/immutable/assets/Badge.D1SV0Gv7.css +0 -17
  286. package/frontend/.svelte-kit/output/server/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css +1 -0
  287. package/frontend/.svelte-kit/output/server/_app/immutable/assets/EmptyState.DUDM4P19.css +1 -0
  288. package/frontend/.svelte-kit/output/server/_app/immutable/assets/Modal.BuaCfdLj.css +1 -0
  289. package/frontend/.svelte-kit/output/server/_app/immutable/assets/Pagination.DZmi2Po-.css +0 -17
  290. package/frontend/.svelte-kit/output/server/_app/immutable/assets/Toast.DENwYn3g.css +0 -17
  291. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_layout.BuhMvMUj.css +0 -17
  292. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.0Eln51hV.css +0 -17
  293. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.5IJAEAjc.css +0 -17
  294. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BR3wgXcZ.css +0 -17
  295. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BWAH0Beh.css +1 -0
  296. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BZfe6nWe.css +0 -17
  297. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Bj_a_5lv.css +0 -17
  298. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BwgOqeMo.css +1 -0
  299. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.CBHi6pzT.css +0 -17
  300. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.CFnbmY-D.css +0 -17
  301. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.DvMkmYA-.css +1 -0
  302. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page._h2o67kg.css +0 -17
  303. package/frontend/.svelte-kit/output/server/chunks/BackLink.js +0 -17
  304. package/frontend/.svelte-kit/output/server/chunks/Badge.js +1 -18
  305. package/frontend/.svelte-kit/output/server/chunks/ConfirmModal.js +7 -37
  306. package/frontend/.svelte-kit/output/server/chunks/EmptyState.js +25 -25
  307. package/frontend/.svelte-kit/output/server/chunks/Modal.js +21 -0
  308. package/frontend/.svelte-kit/output/server/chunks/Toast.js +12 -18
  309. package/frontend/.svelte-kit/output/server/chunks/attributes.js +0 -17
  310. package/frontend/.svelte-kit/output/server/chunks/auth.js +0 -17
  311. package/frontend/.svelte-kit/output/server/chunks/auth2.js +8 -0
  312. package/frontend/.svelte-kit/output/server/chunks/client.js +0 -17
  313. package/frontend/.svelte-kit/output/server/chunks/common.js +36 -0
  314. package/frontend/.svelte-kit/output/server/chunks/constants.js +8 -20
  315. package/frontend/.svelte-kit/output/server/chunks/escaping.js +0 -17
  316. package/frontend/.svelte-kit/output/server/chunks/exports.js +0 -17
  317. package/frontend/.svelte-kit/output/server/chunks/format.js +0 -17
  318. package/frontend/.svelte-kit/output/server/chunks/index-server.js +0 -17
  319. package/frontend/.svelte-kit/output/server/chunks/index.js +4 -21
  320. package/frontend/.svelte-kit/output/server/chunks/index2.js +0 -17
  321. package/frontend/.svelte-kit/output/server/chunks/internal.js +2 -19
  322. package/frontend/.svelte-kit/output/server/chunks/reports.js +0 -17
  323. package/frontend/.svelte-kit/output/server/chunks/reports2.js +87 -0
  324. package/frontend/.svelte-kit/output/server/chunks/repository.js +87 -0
  325. package/frontend/.svelte-kit/output/server/chunks/runner.js +9 -20
  326. package/frontend/.svelte-kit/output/server/chunks/stores.js +0 -17
  327. package/frontend/.svelte-kit/output/server/chunks/theme.js +0 -17
  328. package/frontend/.svelte-kit/output/server/entries/fallbacks/error.svelte.js +0 -17
  329. package/frontend/.svelte-kit/output/server/entries/pages/_layout.js +0 -17
  330. package/frontend/.svelte-kit/output/server/entries/pages/_layout.svelte.js +2 -17
  331. package/frontend/.svelte-kit/output/server/entries/pages/_page.svelte.js +135 -101
  332. package/frontend/.svelte-kit/output/server/entries/pages/login/_page.svelte.js +5 -20
  333. package/frontend/.svelte-kit/output/server/entries/pages/reports/_id_/_page.svelte.js +5 -20
  334. package/frontend/.svelte-kit/output/server/entries/pages/reports/_page.svelte.js +17 -43
  335. package/frontend/.svelte-kit/output/server/entries/pages/reports/live/_page.svelte.js +27 -42
  336. package/frontend/.svelte-kit/output/server/entries/pages/scheduled-tests/_page.svelte.js +68 -50
  337. package/frontend/.svelte-kit/output/server/entries/pages/settings/_page.svelte.js +272 -113
  338. package/frontend/.svelte-kit/output/server/entries/pages/setup/_page.svelte.js +5 -20
  339. package/frontend/.svelte-kit/output/server/entries/pages/test-repository/_page.svelte.js +38 -53
  340. package/frontend/.svelte-kit/output/server/entries/pages/test-repository/runs/_id_/_page.svelte.js +61 -30
  341. package/frontend/.svelte-kit/output/server/entries/pages/test-repository/suites/_id_/_page.svelte.js +19 -35
  342. package/frontend/.svelte-kit/output/server/index.js +0 -17
  343. package/frontend/.svelte-kit/output/server/internal.js +0 -17
  344. package/frontend/.svelte-kit/output/server/manifest-full.js +1 -18
  345. package/frontend/.svelte-kit/output/server/manifest.js +1 -18
  346. package/frontend/.svelte-kit/output/server/nodes/0.js +2 -19
  347. package/frontend/.svelte-kit/output/server/nodes/1.js +1 -18
  348. package/frontend/.svelte-kit/output/server/nodes/10.js +2 -19
  349. package/frontend/.svelte-kit/output/server/nodes/11.js +2 -19
  350. package/frontend/.svelte-kit/output/server/nodes/12.js +2 -19
  351. package/frontend/.svelte-kit/output/server/nodes/2.js +2 -19
  352. package/frontend/.svelte-kit/output/server/nodes/3.js +1 -18
  353. package/frontend/.svelte-kit/output/server/nodes/4.js +2 -19
  354. package/frontend/.svelte-kit/output/server/nodes/5.js +1 -18
  355. package/frontend/.svelte-kit/output/server/nodes/6.js +1 -18
  356. package/frontend/.svelte-kit/output/server/nodes/7.js +2 -19
  357. package/frontend/.svelte-kit/output/server/nodes/8.js +2 -19
  358. package/frontend/.svelte-kit/output/server/nodes/9.js +1 -18
  359. package/frontend/Dockerfile +1 -13
  360. package/frontend/build/client/_app/immutable/assets/0.BhSG-jZH.css +0 -17
  361. package/frontend/build/client/_app/immutable/assets/0.BhSG-jZH.css.br +0 -0
  362. package/frontend/build/client/_app/immutable/assets/0.BhSG-jZH.css.gz +0 -0
  363. package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css +0 -17
  364. package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.br +0 -0
  365. package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.gz +0 -0
  366. package/frontend/build/client/_app/immutable/assets/11.BWAH0Beh.css +1 -0
  367. package/frontend/build/client/_app/immutable/assets/11.BWAH0Beh.css.br +0 -0
  368. package/frontend/build/client/_app/immutable/assets/11.BWAH0Beh.css.gz +0 -0
  369. package/frontend/build/client/_app/immutable/assets/12.Bj_a_5lv.css +0 -17
  370. package/frontend/build/client/_app/immutable/assets/12.Bj_a_5lv.css.br +0 -0
  371. package/frontend/build/client/_app/immutable/assets/12.Bj_a_5lv.css.gz +0 -0
  372. package/frontend/build/client/_app/immutable/assets/2.DvMkmYA-.css +1 -0
  373. package/frontend/build/client/_app/immutable/assets/2.DvMkmYA-.css.br +0 -0
  374. package/frontend/build/client/_app/immutable/assets/2.DvMkmYA-.css.gz +0 -0
  375. package/frontend/build/client/_app/immutable/assets/3.BR3wgXcZ.css +0 -17
  376. package/frontend/build/client/_app/immutable/assets/3.BR3wgXcZ.css.br +0 -0
  377. package/frontend/build/client/_app/immutable/assets/3.BR3wgXcZ.css.gz +0 -0
  378. package/frontend/build/client/_app/immutable/assets/4.0Eln51hV.css +0 -17
  379. package/frontend/build/client/_app/immutable/assets/4.0Eln51hV.css.br +0 -0
  380. package/frontend/build/client/_app/immutable/assets/4.0Eln51hV.css.gz +0 -0
  381. package/frontend/build/client/_app/immutable/assets/5.DFV9OgAu.css +0 -17
  382. package/frontend/build/client/_app/immutable/assets/5.DFV9OgAu.css.br +0 -0
  383. package/frontend/build/client/_app/immutable/assets/5.DFV9OgAu.css.gz +0 -0
  384. package/frontend/build/client/_app/immutable/assets/6.CFnbmY-D.css +0 -17
  385. package/frontend/build/client/_app/immutable/assets/6.CFnbmY-D.css.br +0 -0
  386. package/frontend/build/client/_app/immutable/assets/6.CFnbmY-D.css.gz +0 -0
  387. package/frontend/build/client/_app/immutable/assets/7.CBHi6pzT.css +0 -17
  388. package/frontend/build/client/_app/immutable/assets/7.CBHi6pzT.css.br +0 -0
  389. package/frontend/build/client/_app/immutable/assets/7.CBHi6pzT.css.gz +0 -0
  390. package/frontend/build/client/_app/immutable/assets/8.BwgOqeMo.css +1 -0
  391. package/frontend/build/client/_app/immutable/assets/8.BwgOqeMo.css.br +0 -0
  392. package/frontend/build/client/_app/immutable/assets/8.BwgOqeMo.css.gz +0 -0
  393. package/frontend/build/client/_app/immutable/assets/9._h2o67kg.css +0 -17
  394. package/frontend/build/client/_app/immutable/assets/9._h2o67kg.css.br +0 -0
  395. package/frontend/build/client/_app/immutable/assets/9._h2o67kg.css.gz +0 -0
  396. package/frontend/build/client/_app/immutable/assets/BackLink.CvWWuO3h.css +0 -17
  397. package/frontend/build/client/_app/immutable/assets/BackLink.CvWWuO3h.css.br +0 -0
  398. package/frontend/build/client/_app/immutable/assets/BackLink.CvWWuO3h.css.gz +0 -0
  399. package/frontend/build/client/_app/immutable/assets/Badge.D1SV0Gv7.css +0 -17
  400. package/frontend/build/client/_app/immutable/assets/Badge.D1SV0Gv7.css.br +0 -0
  401. package/frontend/build/client/_app/immutable/assets/Badge.D1SV0Gv7.css.gz +0 -0
  402. package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -17
  403. package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.br +0 -0
  404. package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.gz +0 -0
  405. package/frontend/build/client/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css +1 -0
  406. package/frontend/build/client/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css.br +0 -0
  407. package/frontend/build/client/_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css.gz +0 -0
  408. package/frontend/build/client/_app/immutable/assets/EmptyState.DUDM4P19.css +1 -0
  409. package/frontend/build/client/_app/immutable/assets/EmptyState.DUDM4P19.css.br +0 -0
  410. package/frontend/build/client/_app/immutable/assets/EmptyState.DUDM4P19.css.gz +0 -0
  411. package/frontend/build/client/_app/immutable/assets/Modal.BZpm0HP8.css +1 -0
  412. package/frontend/build/client/_app/immutable/assets/Modal.BZpm0HP8.css.br +0 -0
  413. package/frontend/build/client/_app/immutable/assets/Modal.BZpm0HP8.css.gz +0 -0
  414. package/frontend/build/client/_app/immutable/assets/Modal.BuaCfdLj.css +1 -0
  415. package/frontend/build/client/_app/immutable/assets/Modal.BuaCfdLj.css.br +0 -0
  416. package/frontend/build/client/_app/immutable/assets/Modal.BuaCfdLj.css.gz +0 -0
  417. package/frontend/build/client/_app/immutable/assets/Pagination.DZmi2Po-.css +0 -17
  418. package/frontend/build/client/_app/immutable/assets/Pagination.DZmi2Po-.css.br +0 -0
  419. package/frontend/build/client/_app/immutable/assets/Pagination.DZmi2Po-.css.gz +0 -0
  420. package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css +0 -17
  421. package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.br +0 -0
  422. package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.gz +0 -0
  423. package/frontend/build/client/_app/immutable/assets/_layout.BuhMvMUj.css +0 -17
  424. package/frontend/build/client/_app/immutable/assets/_layout.BuhMvMUj.css.br +0 -0
  425. package/frontend/build/client/_app/immutable/assets/_layout.BuhMvMUj.css.gz +0 -0
  426. package/frontend/build/client/_app/immutable/assets/_page.0Eln51hV.css +0 -17
  427. package/frontend/build/client/_app/immutable/assets/_page.0Eln51hV.css.br +0 -0
  428. package/frontend/build/client/_app/immutable/assets/_page.0Eln51hV.css.gz +0 -0
  429. package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -17
  430. package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.br +0 -0
  431. package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.gz +0 -0
  432. package/frontend/build/client/_app/immutable/assets/_page.BR3wgXcZ.css +0 -17
  433. package/frontend/build/client/_app/immutable/assets/_page.BR3wgXcZ.css.br +0 -0
  434. package/frontend/build/client/_app/immutable/assets/_page.BR3wgXcZ.css.gz +0 -0
  435. package/frontend/build/client/_app/immutable/assets/_page.BWAH0Beh.css +1 -0
  436. package/frontend/build/client/_app/immutable/assets/_page.BWAH0Beh.css.br +0 -0
  437. package/frontend/build/client/_app/immutable/assets/_page.BWAH0Beh.css.gz +0 -0
  438. package/frontend/build/client/_app/immutable/assets/_page.BZfe6nWe.css +0 -17
  439. package/frontend/build/client/_app/immutable/assets/_page.BZfe6nWe.css.br +0 -0
  440. package/frontend/build/client/_app/immutable/assets/_page.BZfe6nWe.css.gz +0 -0
  441. package/frontend/build/client/_app/immutable/assets/_page.Bj_a_5lv.css +0 -17
  442. package/frontend/build/client/_app/immutable/assets/_page.Bj_a_5lv.css.br +0 -0
  443. package/frontend/build/client/_app/immutable/assets/_page.Bj_a_5lv.css.gz +0 -0
  444. package/frontend/build/client/_app/immutable/assets/_page.BwgOqeMo.css +1 -0
  445. package/frontend/build/client/_app/immutable/assets/_page.BwgOqeMo.css.br +0 -0
  446. package/frontend/build/client/_app/immutable/assets/_page.BwgOqeMo.css.gz +0 -0
  447. package/frontend/build/client/_app/immutable/assets/_page.CBHi6pzT.css +0 -17
  448. package/frontend/build/client/_app/immutable/assets/_page.CBHi6pzT.css.br +0 -0
  449. package/frontend/build/client/_app/immutable/assets/_page.CBHi6pzT.css.gz +0 -0
  450. package/frontend/build/client/_app/immutable/assets/_page.CFnbmY-D.css +0 -17
  451. package/frontend/build/client/_app/immutable/assets/_page.CFnbmY-D.css.br +0 -0
  452. package/frontend/build/client/_app/immutable/assets/_page.CFnbmY-D.css.gz +0 -0
  453. package/frontend/build/client/_app/immutable/assets/_page.DvMkmYA-.css +1 -0
  454. package/frontend/build/client/_app/immutable/assets/_page.DvMkmYA-.css.br +0 -0
  455. package/frontend/build/client/_app/immutable/assets/_page.DvMkmYA-.css.gz +0 -0
  456. package/frontend/build/client/_app/immutable/assets/_page._h2o67kg.css +0 -17
  457. package/frontend/build/client/_app/immutable/assets/_page._h2o67kg.css.br +0 -0
  458. package/frontend/build/client/_app/immutable/assets/_page._h2o67kg.css.gz +0 -0
  459. package/frontend/build/client/_app/immutable/chunks/2zMvi6fR.js +1 -0
  460. package/frontend/build/client/_app/immutable/chunks/2zMvi6fR.js.br +0 -0
  461. package/frontend/build/client/_app/immutable/chunks/2zMvi6fR.js.gz +0 -0
  462. package/frontend/build/client/_app/immutable/chunks/3InFPLvE.js +1 -0
  463. package/frontend/build/client/_app/immutable/chunks/3InFPLvE.js.br +0 -0
  464. package/frontend/build/client/_app/immutable/chunks/3InFPLvE.js.gz +0 -0
  465. package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C67QNx7Z.js → build/client/_app/immutable/chunks/BAACMRH5.js} +1 -18
  466. package/frontend/build/client/_app/immutable/chunks/BAACMRH5.js.br +0 -0
  467. package/frontend/build/client/_app/immutable/chunks/BAACMRH5.js.gz +0 -0
  468. package/frontend/build/client/_app/immutable/chunks/BAMUGSh7.js +1 -0
  469. package/frontend/build/client/_app/immutable/chunks/BAMUGSh7.js.br +0 -0
  470. package/frontend/build/client/_app/immutable/chunks/BAMUGSh7.js.gz +0 -0
  471. package/frontend/build/client/_app/immutable/chunks/BKuh57WU.js +1 -0
  472. package/frontend/build/client/_app/immutable/chunks/BKuh57WU.js.br +0 -0
  473. package/frontend/build/client/_app/immutable/chunks/BKuh57WU.js.gz +0 -0
  474. package/frontend/build/client/_app/immutable/chunks/{BFEiffAL.js → BUwxRmHf.js} +1 -18
  475. package/frontend/build/client/_app/immutable/chunks/BUwxRmHf.js.br +0 -0
  476. package/frontend/build/client/_app/immutable/chunks/BUwxRmHf.js.gz +0 -0
  477. package/frontend/build/client/_app/immutable/chunks/Bfc47y5P.js +0 -17
  478. package/frontend/build/client/_app/immutable/chunks/Bfc47y5P.js.br +0 -0
  479. package/frontend/build/client/_app/immutable/chunks/Bfc47y5P.js.gz +0 -0
  480. package/frontend/build/client/_app/immutable/chunks/{BVMec-1T.js → BmZ9upwL.js} +1 -18
  481. package/frontend/build/client/_app/immutable/chunks/BmZ9upwL.js.br +0 -0
  482. package/frontend/build/client/_app/immutable/chunks/BmZ9upwL.js.gz +0 -0
  483. package/frontend/build/client/_app/immutable/chunks/C1Vid6wz.js +1 -0
  484. package/frontend/build/client/_app/immutable/chunks/C1Vid6wz.js.br +0 -0
  485. package/frontend/build/client/_app/immutable/chunks/C1Vid6wz.js.gz +0 -0
  486. package/frontend/build/client/_app/immutable/chunks/C8OURRSs.js +1 -0
  487. package/frontend/build/client/_app/immutable/chunks/C8OURRSs.js.br +0 -0
  488. package/frontend/build/client/_app/immutable/chunks/C8OURRSs.js.gz +0 -0
  489. package/frontend/build/client/_app/immutable/chunks/CGeZlCaI.js +1 -0
  490. package/frontend/build/client/_app/immutable/chunks/CGeZlCaI.js.br +0 -0
  491. package/frontend/build/client/_app/immutable/chunks/CGeZlCaI.js.gz +0 -0
  492. package/frontend/build/client/_app/immutable/chunks/CKDtmZUQ.js +1 -0
  493. package/frontend/build/client/_app/immutable/chunks/CKDtmZUQ.js.br +0 -0
  494. package/frontend/build/client/_app/immutable/chunks/CKDtmZUQ.js.gz +0 -0
  495. package/frontend/build/client/_app/immutable/chunks/CMBQQ9RZ.js +1 -0
  496. package/frontend/build/client/_app/immutable/chunks/CMBQQ9RZ.js.br +0 -0
  497. package/frontend/build/client/_app/immutable/chunks/CMBQQ9RZ.js.gz +0 -0
  498. package/frontend/build/client/_app/immutable/chunks/CMEtqUfQ.js +1 -0
  499. package/frontend/build/client/_app/immutable/chunks/CMEtqUfQ.js.br +0 -0
  500. package/frontend/build/client/_app/immutable/chunks/CMEtqUfQ.js.gz +0 -0
  501. package/frontend/build/client/_app/immutable/chunks/CNdcuGR0.js +1 -0
  502. package/frontend/build/client/_app/immutable/chunks/CNdcuGR0.js.br +0 -0
  503. package/frontend/build/client/_app/immutable/chunks/CNdcuGR0.js.gz +0 -0
  504. package/frontend/build/client/_app/immutable/chunks/CUC98kMZ.js +1 -0
  505. package/frontend/build/client/_app/immutable/chunks/CUC98kMZ.js.br +0 -0
  506. package/frontend/build/client/_app/immutable/chunks/CUC98kMZ.js.gz +0 -0
  507. package/frontend/build/client/_app/immutable/chunks/CgnwuKKO.js +1 -0
  508. package/frontend/build/client/_app/immutable/chunks/CgnwuKKO.js.br +0 -0
  509. package/frontend/build/client/_app/immutable/chunks/CgnwuKKO.js.gz +0 -0
  510. package/frontend/build/client/_app/immutable/chunks/Co3xKhEp.js +1 -0
  511. package/frontend/build/client/_app/immutable/chunks/Co3xKhEp.js.br +0 -0
  512. package/frontend/build/client/_app/immutable/chunks/Co3xKhEp.js.gz +0 -0
  513. package/frontend/build/client/_app/immutable/chunks/CuqnZUex.js +1 -0
  514. package/frontend/build/client/_app/immutable/chunks/CuqnZUex.js.br +1 -0
  515. package/frontend/build/client/_app/immutable/chunks/CuqnZUex.js.gz +0 -0
  516. package/frontend/build/client/_app/immutable/chunks/{BkKmeUbA.js → CzlPaHY9.js} +1 -18
  517. package/frontend/build/client/_app/immutable/chunks/CzlPaHY9.js.br +0 -0
  518. package/frontend/build/client/_app/immutable/chunks/CzlPaHY9.js.gz +0 -0
  519. package/frontend/build/client/_app/immutable/chunks/DCgHuuvs.js +1 -0
  520. package/frontend/build/client/_app/immutable/chunks/DCgHuuvs.js.br +0 -0
  521. package/frontend/build/client/_app/immutable/chunks/DCgHuuvs.js.gz +0 -0
  522. package/frontend/build/client/_app/immutable/chunks/DHM6H-es.js +2 -0
  523. package/frontend/build/client/_app/immutable/chunks/DHM6H-es.js.br +0 -0
  524. package/frontend/build/client/_app/immutable/chunks/DHM6H-es.js.gz +0 -0
  525. package/frontend/build/client/_app/immutable/chunks/DMZwI8Cj.js +1 -0
  526. package/frontend/build/client/_app/immutable/chunks/DMZwI8Cj.js.br +0 -0
  527. package/frontend/build/client/_app/immutable/chunks/DMZwI8Cj.js.gz +0 -0
  528. package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js +3 -0
  529. package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.br +0 -0
  530. package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.gz +0 -0
  531. package/frontend/build/client/_app/immutable/chunks/{B0pefNHY.js → DSLBMEpr.js} +1 -18
  532. package/frontend/build/client/_app/immutable/chunks/DSLBMEpr.js.br +0 -0
  533. package/frontend/build/client/_app/immutable/chunks/DSLBMEpr.js.gz +0 -0
  534. package/frontend/build/client/_app/immutable/chunks/{CxTX_lpP.js → DbgD7JLa.js} +1 -18
  535. package/frontend/build/client/_app/immutable/chunks/DbgD7JLa.js.br +0 -0
  536. package/frontend/build/client/_app/immutable/chunks/DbgD7JLa.js.gz +0 -0
  537. package/frontend/build/client/_app/immutable/chunks/Dlnxgs49.js +1 -0
  538. package/frontend/build/client/_app/immutable/chunks/Dlnxgs49.js.br +0 -0
  539. package/frontend/build/client/_app/immutable/chunks/Dlnxgs49.js.gz +0 -0
  540. package/frontend/build/client/_app/immutable/chunks/TOCyHP97.js +6 -0
  541. package/frontend/build/client/_app/immutable/chunks/TOCyHP97.js.br +0 -0
  542. package/frontend/build/client/_app/immutable/chunks/TOCyHP97.js.gz +0 -0
  543. package/frontend/build/client/_app/immutable/chunks/nURnIknV.js +1 -0
  544. package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.br +0 -0
  545. package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.gz +0 -0
  546. package/frontend/build/client/_app/immutable/chunks/t-La0hgu.js +1 -0
  547. package/frontend/build/client/_app/immutable/chunks/t-La0hgu.js.br +0 -0
  548. package/frontend/build/client/_app/immutable/chunks/t-La0hgu.js.gz +0 -0
  549. package/frontend/build/client/_app/immutable/chunks/w0D4Uzlq.js +1 -0
  550. package/frontend/build/client/_app/immutable/chunks/w0D4Uzlq.js.br +0 -0
  551. package/frontend/build/client/_app/immutable/chunks/w0D4Uzlq.js.gz +0 -0
  552. package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js +2 -0
  553. package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.br +0 -0
  554. package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.gz +0 -0
  555. package/frontend/build/client/_app/immutable/entry/start.BxeX1Uxs.js +1 -0
  556. package/frontend/build/client/_app/immutable/entry/start.BxeX1Uxs.js.br +2 -0
  557. package/frontend/build/client/_app/immutable/entry/start.BxeX1Uxs.js.gz +0 -0
  558. package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js +6 -0
  559. package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.br +0 -0
  560. package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.gz +0 -0
  561. package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js +1 -0
  562. package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.br +0 -0
  563. package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.gz +0 -0
  564. package/frontend/build/client/_app/immutable/nodes/10.DEbnxfND.js +1 -0
  565. package/frontend/build/client/_app/immutable/nodes/10.DEbnxfND.js.br +0 -0
  566. package/frontend/build/client/_app/immutable/nodes/10.DEbnxfND.js.gz +0 -0
  567. package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js +1 -0
  568. package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.br +0 -0
  569. package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.gz +0 -0
  570. package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js +1 -0
  571. package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.br +0 -0
  572. package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.gz +0 -0
  573. package/frontend/build/client/_app/immutable/nodes/2.BnHalSCi.js +1 -0
  574. package/frontend/build/client/_app/immutable/nodes/2.BnHalSCi.js.br +0 -0
  575. package/frontend/build/client/_app/immutable/nodes/2.BnHalSCi.js.gz +0 -0
  576. package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js +1 -0
  577. package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.br +0 -0
  578. package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.gz +0 -0
  579. package/frontend/build/client/_app/immutable/nodes/4.D-YT_JJe.js +1 -0
  580. package/frontend/build/client/_app/immutable/nodes/4.D-YT_JJe.js.br +0 -0
  581. package/frontend/build/client/_app/immutable/nodes/4.D-YT_JJe.js.gz +0 -0
  582. package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js +1 -0
  583. package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.br +0 -0
  584. package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.gz +0 -0
  585. package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js +1 -0
  586. package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.br +0 -0
  587. package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.gz +0 -0
  588. package/frontend/build/client/_app/immutable/nodes/7.C-pKvVZy.js +1 -0
  589. package/frontend/build/client/_app/immutable/nodes/7.C-pKvVZy.js.br +0 -0
  590. package/frontend/build/client/_app/immutable/nodes/7.C-pKvVZy.js.gz +0 -0
  591. package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js +4 -0
  592. package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.br +0 -0
  593. package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.gz +0 -0
  594. package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js +1 -0
  595. package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js.br +0 -0
  596. package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js.gz +0 -0
  597. package/frontend/build/client/_app/version.json +1 -1
  598. package/frontend/build/client/_app/version.json.br +0 -0
  599. package/frontend/build/client/_app/version.json.gz +0 -0
  600. package/frontend/build/env.js +0 -17
  601. package/frontend/build/handler.js +0 -17
  602. package/frontend/build/index.js +0 -17
  603. package/frontend/build/server/chunks/0-Cn_2hzqk.js +17 -0
  604. package/frontend/build/server/chunks/0-Cn_2hzqk.js.map +1 -0
  605. package/frontend/build/server/chunks/1-BrRGJFSB.js +9 -0
  606. package/frontend/build/server/chunks/1-BrRGJFSB.js.map +1 -0
  607. package/frontend/build/server/chunks/10-l6_ikL_D.js +9 -0
  608. package/frontend/build/server/chunks/10-l6_ikL_D.js.map +1 -0
  609. package/frontend/build/server/chunks/11-BLPbHJqB.js +9 -0
  610. package/frontend/build/server/chunks/11-BLPbHJqB.js.map +1 -0
  611. package/frontend/build/server/chunks/12-S1ujUrHy.js +9 -0
  612. package/frontend/build/server/chunks/12-S1ujUrHy.js.map +1 -0
  613. package/frontend/build/server/chunks/2-Cte30Dgy.js +9 -0
  614. package/frontend/build/server/chunks/2-Cte30Dgy.js.map +1 -0
  615. package/frontend/build/server/chunks/3-BHkXVoNI.js +9 -0
  616. package/frontend/build/server/chunks/3-BHkXVoNI.js.map +1 -0
  617. package/frontend/build/server/chunks/4-Bo04DC5Z.js +9 -0
  618. package/frontend/build/server/chunks/4-Bo04DC5Z.js.map +1 -0
  619. package/frontend/build/server/chunks/5-F1ZsXMt2.js +9 -0
  620. package/frontend/build/server/chunks/5-F1ZsXMt2.js.map +1 -0
  621. package/frontend/build/server/chunks/6-FlZLnYJ6.js +9 -0
  622. package/frontend/build/server/chunks/6-FlZLnYJ6.js.map +1 -0
  623. package/frontend/build/server/chunks/7-DiQJZEF_.js +9 -0
  624. package/frontend/build/server/chunks/7-DiQJZEF_.js.map +1 -0
  625. package/frontend/build/server/chunks/8-DXReUGxi.js +9 -0
  626. package/frontend/build/server/chunks/8-DXReUGxi.js.map +1 -0
  627. package/frontend/build/server/chunks/9-Cy_mGmt4.js +9 -0
  628. package/frontend/build/server/chunks/9-Cy_mGmt4.js.map +1 -0
  629. package/frontend/build/server/chunks/BackLink-BJ8NaemM.js +13 -0
  630. package/frontend/build/server/chunks/{BackLink-KxZNqsaq.js.map → BackLink-BJ8NaemM.js.map} +1 -17
  631. package/frontend/build/server/chunks/Badge-44ZTDx0V.js +13 -0
  632. package/frontend/build/server/chunks/{Badge-B5V-z6pI.js.map → Badge-44ZTDx0V.js.map} +1 -17
  633. package/frontend/build/server/chunks/ConfirmModal-C0EAXcq_.js +44 -0
  634. package/frontend/build/server/chunks/ConfirmModal-C0EAXcq_.js.map +1 -0
  635. package/frontend/build/server/chunks/EmptyState-BA_eXaeR.js +34 -0
  636. package/frontend/build/server/chunks/EmptyState-BA_eXaeR.js.map +1 -0
  637. package/frontend/build/server/chunks/Modal-DChG187n.js +21 -0
  638. package/frontend/build/server/chunks/Modal-DChG187n.js.map +1 -0
  639. package/frontend/build/server/chunks/Toast-v-q4ZnYq.js +30 -0
  640. package/frontend/build/server/chunks/Toast-v-q4ZnYq.js.map +1 -0
  641. package/frontend/build/server/chunks/_layout.svelte-CHVI3Prt.js +24 -0
  642. package/frontend/build/server/chunks/_layout.svelte-CHVI3Prt.js.map +1 -0
  643. package/frontend/build/server/chunks/{_page.svelte-LcJmR5du.js → _page.svelte-1as75nPc.js} +28 -51
  644. package/frontend/build/server/chunks/_page.svelte-1as75nPc.js.map +1 -0
  645. package/frontend/build/server/chunks/_page.svelte-1jo8MapI.js +96 -0
  646. package/frontend/build/server/chunks/_page.svelte-1jo8MapI.js.map +1 -0
  647. package/frontend/build/server/chunks/{_page.svelte-C0vstY93.js → _page.svelte-BuFusRK1.js} +74 -56
  648. package/frontend/build/server/chunks/_page.svelte-BuFusRK1.js.map +1 -0
  649. package/frontend/build/server/chunks/{_page.svelte-gnktnXKP.js → _page.svelte-CAVyp70V.js} +37 -51
  650. package/frontend/build/server/chunks/_page.svelte-CAVyp70V.js.map +1 -0
  651. package/frontend/build/server/chunks/{_page.svelte-CC27sZag.js → _page.svelte-CB8-QBNg.js} +42 -57
  652. package/frontend/build/server/chunks/_page.svelte-CB8-QBNg.js.map +1 -0
  653. package/frontend/build/server/chunks/{_page.svelte-DqWsUJRp.js → _page.svelte-CWWeZuid.js} +24 -40
  654. package/frontend/build/server/chunks/_page.svelte-CWWeZuid.js.map +1 -0
  655. package/frontend/build/server/chunks/_page.svelte-CpB-fgJl.js +219 -0
  656. package/frontend/build/server/chunks/_page.svelte-CpB-fgJl.js.map +1 -0
  657. package/frontend/build/server/chunks/_page.svelte-D3E6Eojd.js +27 -0
  658. package/frontend/build/server/chunks/_page.svelte-D3E6Eojd.js.map +1 -0
  659. package/frontend/build/server/chunks/_page.svelte-Kg69DDaM.js +27 -0
  660. package/frontend/build/server/chunks/_page.svelte-Kg69DDaM.js.map +1 -0
  661. package/frontend/build/server/chunks/{_page.svelte-BETENC2e.js → _page.svelte-L9fuiN_q.js} +13 -28
  662. package/frontend/build/server/chunks/_page.svelte-L9fuiN_q.js.map +1 -0
  663. package/frontend/build/server/chunks/_page.svelte-wKyUmA2e.js +724 -0
  664. package/frontend/build/server/chunks/_page.svelte-wKyUmA2e.js.map +1 -0
  665. package/frontend/build/server/chunks/attributes-BeaNKpgU.js +0 -17
  666. package/frontend/build/server/chunks/attributes-BeaNKpgU.js.map +0 -16
  667. package/frontend/build/server/chunks/{auth-rlH-cIzn.js → auth-Chw-wBpI.js} +2 -19
  668. package/frontend/build/server/chunks/{auth-rlH-cIzn.js.map → auth-Chw-wBpI.js.map} +1 -17
  669. package/frontend/build/server/chunks/auth2-C02yYDpz.js +6 -0
  670. package/frontend/build/server/chunks/auth2-C02yYDpz.js.map +1 -0
  671. package/frontend/build/server/chunks/{client-DyII78e0.js → client-DEmxR_a-.js} +2 -19
  672. package/frontend/build/server/chunks/{client-DyII78e0.js.map → client-DEmxR_a-.js.map} +1 -17
  673. package/frontend/build/server/chunks/common-DYYTVHPw.js +21 -0
  674. package/frontend/build/server/chunks/common-DYYTVHPw.js.map +1 -0
  675. package/frontend/build/server/chunks/constants-CXTaQaB6.js +22 -0
  676. package/frontend/build/server/chunks/constants-CXTaQaB6.js.map +1 -0
  677. package/frontend/build/server/chunks/error.svelte-BD3xNemk.js +28 -0
  678. package/frontend/build/server/chunks/{error.svelte-BDW3iP1O.js.map → error.svelte-BD3xNemk.js.map} +1 -17
  679. package/frontend/build/server/chunks/escaping-CqgfEcN3.js +0 -17
  680. package/frontend/build/server/chunks/escaping-CqgfEcN3.js.map +0 -16
  681. package/frontend/build/server/chunks/exports-D0QHgeSL.js +0 -17
  682. package/frontend/build/server/chunks/exports-D0QHgeSL.js.map +0 -16
  683. package/frontend/build/server/chunks/{format-D26ZQccb.js → format-CiNRMfHu.js} +2 -19
  684. package/frontend/build/server/chunks/{format-D26ZQccb.js.map → format-CiNRMfHu.js.map} +1 -17
  685. package/frontend/build/server/chunks/index-server-B3M9vyh2.js +14 -0
  686. package/frontend/build/server/chunks/index-server-B3M9vyh2.js.map +1 -0
  687. package/frontend/build/server/chunks/{index2-DG8XHU5D.js → index2-5R1tbrep.js} +2 -19
  688. package/frontend/build/server/chunks/{index2-DG8XHU5D.js.map → index2-5R1tbrep.js.map} +1 -17
  689. package/frontend/build/server/chunks/reports-D_ApeeNh.js +23 -0
  690. package/frontend/build/server/chunks/{reports-Bgi7V5N7.js.map → reports-D_ApeeNh.js.map} +1 -17
  691. package/frontend/build/server/chunks/reports2-BtElN_53.js +47 -0
  692. package/frontend/build/server/chunks/reports2-BtElN_53.js.map +1 -0
  693. package/frontend/build/server/chunks/repository-FYYV_vKE.js +47 -0
  694. package/frontend/build/server/chunks/repository-FYYV_vKE.js.map +1 -0
  695. package/frontend/build/server/chunks/runner-DC7hXbnY.js +32 -0
  696. package/frontend/build/server/chunks/runner-DC7hXbnY.js.map +1 -0
  697. package/frontend/build/server/chunks/stores-BLenR3c1.js +27 -0
  698. package/frontend/build/server/chunks/{stores-DlkXV308.js.map → stores-BLenR3c1.js.map} +1 -17
  699. package/frontend/build/server/chunks/theme-nAx-GvX_.js +10 -0
  700. package/frontend/build/server/chunks/theme-nAx-GvX_.js.map +1 -0
  701. package/frontend/build/server/index.js +3 -20
  702. package/frontend/build/server/index.js.map +1 -17
  703. package/frontend/build/server/manifest.js +14 -31
  704. package/frontend/build/server/manifest.js.map +1 -17
  705. package/frontend/build/shims.js +0 -17
  706. package/frontend/package.json +1 -0
  707. package/frontend/src/app.css +1 -13
  708. package/frontend/src/app.html +1 -13
  709. package/frontend/src/lib/api/activeRuns.js +18 -0
  710. package/frontend/src/lib/api/auth.js +1 -13
  711. package/frontend/src/lib/api/reports.js +1 -13
  712. package/frontend/src/lib/api/repository.js +1 -13
  713. package/frontend/src/lib/api/runners.js +26 -21
  714. package/frontend/src/lib/api/schedules.js +1 -13
  715. package/frontend/src/lib/api/settings.js +11 -23
  716. package/frontend/src/lib/api/tests.js +1 -13
  717. package/frontend/src/lib/api/users.js +1 -13
  718. package/frontend/src/lib/components/icons/BrowserIcon.svelte +1 -13
  719. package/frontend/src/lib/components/layout/Nav.svelte +1 -13
  720. package/frontend/src/lib/components/layout/PageShell.svelte +1 -13
  721. package/frontend/src/lib/components/layout/RunnerPanel.svelte +146 -105
  722. package/frontend/src/lib/components/ui/AutomatedBadge.svelte +2 -31
  723. package/frontend/src/lib/components/ui/BackLink.svelte +1 -30
  724. package/frontend/src/lib/components/ui/Badge.svelte +1 -13
  725. package/frontend/src/lib/components/ui/Button.svelte +1 -13
  726. package/frontend/src/lib/components/ui/CaseIdChip.svelte +1 -30
  727. package/frontend/src/lib/components/ui/ConfirmModal.svelte +8 -17
  728. package/frontend/src/lib/components/ui/EmptyState.svelte +26 -15
  729. package/frontend/src/lib/components/ui/Modal.svelte +1 -13
  730. package/frontend/src/lib/components/ui/Pagination.svelte +1 -13
  731. package/frontend/src/lib/components/ui/PriorityBadge.svelte +1 -30
  732. package/frontend/src/lib/components/ui/ResultChip.svelte +1 -30
  733. package/frontend/src/lib/components/ui/StatusDot.svelte +1 -30
  734. package/frontend/src/lib/components/ui/StepKeyword.svelte +1 -30
  735. package/frontend/src/lib/components/ui/StepStatusIcon.svelte +1 -30
  736. package/frontend/src/lib/components/ui/TagChip.svelte +1 -30
  737. package/frontend/src/lib/components/ui/Terminal.svelte +1 -13
  738. package/frontend/src/lib/components/ui/Toast.svelte +1 -13
  739. package/frontend/src/lib/constants.js +5 -17
  740. package/frontend/src/lib/copy/auth.js +29 -0
  741. package/frontend/src/lib/copy/common.js +39 -0
  742. package/frontend/src/lib/copy/dashboard.js +28 -0
  743. package/frontend/src/lib/copy/reports.js +83 -0
  744. package/frontend/src/lib/copy/repository.js +153 -0
  745. package/frontend/src/lib/copy/runners.js +59 -0
  746. package/frontend/src/lib/copy/schedules.js +96 -0
  747. package/frontend/src/lib/copy/settings.js +295 -0
  748. package/frontend/src/lib/index.js +1 -13
  749. package/frontend/src/lib/socketEvents.js +36 -0
  750. package/frontend/src/lib/stores/auth.js +1 -13
  751. package/frontend/src/lib/stores/runner.js +10 -18
  752. package/frontend/src/lib/stores/theme.js +1 -13
  753. package/frontend/src/lib/styles/global.css +1 -13
  754. package/frontend/src/lib/styles/reset.css +1 -13
  755. package/frontend/src/lib/styles/tokens.css +1 -13
  756. package/frontend/src/lib/utils/format.js +1 -13
  757. package/frontend/src/routes/+layout.js +1 -13
  758. package/frontend/src/routes/+layout.svelte +1 -13
  759. package/frontend/src/routes/+page.svelte +296 -115
  760. package/frontend/src/routes/login/+page.svelte +22 -23
  761. package/frontend/src/routes/reports/+page.svelte +37 -38
  762. package/frontend/src/routes/reports/[id]/+page.svelte +54 -43
  763. package/frontend/src/routes/reports/live/+page.svelte +57 -42
  764. package/frontend/src/routes/scheduled-tests/+page.svelte +134 -117
  765. package/frontend/src/routes/settings/+page.svelte +490 -267
  766. package/frontend/src/routes/setup/+page.svelte +30 -27
  767. package/frontend/src/routes/test-repository/+page.svelte +120 -91
  768. package/frontend/src/routes/test-repository/runs/[id]/+page.svelte +95 -60
  769. package/frontend/src/routes/test-repository/suites/[id]/+page.svelte +145 -91
  770. package/frontend/svelte.config.js +1 -13
  771. package/frontend/vite.config.js +1 -13
  772. package/package.json +3 -2
  773. package/resources/mit-license.txt +2 -0
  774. package/backend/scripts/create-test.js +0 -149
  775. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/ConfirmModal.CdhOYVBb.css +0 -18
  776. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/EmptyState.BdO1q9cJ.css +0 -18
  777. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BJ-I1gHO.css +0 -18
  778. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Bhof07Eg.css +0 -18
  779. package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.CrXpMrqh.css +0 -18
  780. package/frontend/.svelte-kit/adapter-node/chunks/Button.js +0 -33
  781. package/frontend/.svelte-kit/output/client/_app/immutable/assets/11.BJ-I1gHO.css +0 -18
  782. package/frontend/.svelte-kit/output/client/_app/immutable/assets/2.CrXpMrqh.css +0 -18
  783. package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.Bhof07Eg.css +0 -18
  784. package/frontend/.svelte-kit/output/client/_app/immutable/assets/ConfirmModal.CdhOYVBb.css +0 -18
  785. package/frontend/.svelte-kit/output/client/_app/immutable/assets/ConfirmModal.DmxkcwXv.css +0 -18
  786. package/frontend/.svelte-kit/output/client/_app/immutable/assets/EmptyState.BdO1q9cJ.css +0 -18
  787. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BJ-I1gHO.css +0 -18
  788. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Bhof07Eg.css +0 -18
  789. package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.CrXpMrqh.css +0 -18
  790. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BFgtfHj2.js +0 -18
  791. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BIRVeCLU.js +0 -18
  792. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BKCNyxZ2.js +0 -18
  793. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BLAiBE__.js +0 -18
  794. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BeO8veBd.js +0 -18
  795. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BgNaeXAp.js +0 -18
  796. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C3SY6-sX.js +0 -19
  797. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C4eeChIU.js +0 -18
  798. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CVV6HrFb.js +0 -18
  799. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CstKKb9b.js +0 -18
  800. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CyQ_oBw2.js +0 -18
  801. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DGryW1Vp.js +0 -18
  802. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DIx44H9N.js +0 -18
  803. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DP6QP4L6.js +0 -18
  804. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DmRbbcS3.js +0 -23
  805. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/IFIFk6zi.js +0 -18
  806. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/KJdjlibc.js +0 -20
  807. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/ll-VsEGv.js +0 -18
  808. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/raHbXn_9.js +0 -18
  809. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/uFRI_bmR.js +0 -18
  810. package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.Dz_CBZcR.js +0 -19
  811. package/frontend/.svelte-kit/output/client/_app/immutable/entry/start.MMlC62np.js +0 -18
  812. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.CKD9Qja6.js +0 -23
  813. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/1.Dbck5ct8.js +0 -18
  814. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.BfmGpmC1.js +0 -19
  815. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.C7l9HPom.js +0 -18
  816. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.Iu9bcGdC.js +0 -19
  817. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/2.BqQhRvYh.js +0 -18
  818. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3.BZRTKadS.js +0 -18
  819. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/4.BfKoPKdM.js +0 -18
  820. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.B4a2uCWn.js +0 -18
  821. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.CI508CMS.js +0 -18
  822. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.3x_eiof1.js +0 -18
  823. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.BfUuob4H.js +0 -29
  824. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/9.BZhCKRJe.js +0 -18
  825. package/frontend/.svelte-kit/output/server/_app/immutable/assets/ConfirmModal.CdhOYVBb.css +0 -18
  826. package/frontend/.svelte-kit/output/server/_app/immutable/assets/EmptyState.BdO1q9cJ.css +0 -18
  827. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BJ-I1gHO.css +0 -18
  828. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Bhof07Eg.css +0 -18
  829. package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.CrXpMrqh.css +0 -18
  830. package/frontend/.svelte-kit/output/server/chunks/Button.js +0 -33
  831. package/frontend/build/client/_app/immutable/assets/11.BJ-I1gHO.css +0 -18
  832. package/frontend/build/client/_app/immutable/assets/11.BJ-I1gHO.css.br +0 -0
  833. package/frontend/build/client/_app/immutable/assets/11.BJ-I1gHO.css.gz +0 -0
  834. package/frontend/build/client/_app/immutable/assets/2.CrXpMrqh.css +0 -18
  835. package/frontend/build/client/_app/immutable/assets/2.CrXpMrqh.css.br +0 -0
  836. package/frontend/build/client/_app/immutable/assets/2.CrXpMrqh.css.gz +0 -0
  837. package/frontend/build/client/_app/immutable/assets/8.Bhof07Eg.css +0 -18
  838. package/frontend/build/client/_app/immutable/assets/8.Bhof07Eg.css.br +0 -0
  839. package/frontend/build/client/_app/immutable/assets/8.Bhof07Eg.css.gz +0 -0
  840. package/frontend/build/client/_app/immutable/assets/ConfirmModal.CdhOYVBb.css +0 -18
  841. package/frontend/build/client/_app/immutable/assets/ConfirmModal.CdhOYVBb.css.br +0 -0
  842. package/frontend/build/client/_app/immutable/assets/ConfirmModal.CdhOYVBb.css.gz +0 -0
  843. package/frontend/build/client/_app/immutable/assets/ConfirmModal.DmxkcwXv.css +0 -18
  844. package/frontend/build/client/_app/immutable/assets/ConfirmModal.DmxkcwXv.css.br +0 -0
  845. package/frontend/build/client/_app/immutable/assets/ConfirmModal.DmxkcwXv.css.gz +0 -0
  846. package/frontend/build/client/_app/immutable/assets/EmptyState.BdO1q9cJ.css +0 -18
  847. package/frontend/build/client/_app/immutable/assets/EmptyState.BdO1q9cJ.css.br +0 -0
  848. package/frontend/build/client/_app/immutable/assets/EmptyState.BdO1q9cJ.css.gz +0 -0
  849. package/frontend/build/client/_app/immutable/assets/_page.BJ-I1gHO.css +0 -18
  850. package/frontend/build/client/_app/immutable/assets/_page.BJ-I1gHO.css.br +0 -0
  851. package/frontend/build/client/_app/immutable/assets/_page.BJ-I1gHO.css.gz +0 -0
  852. package/frontend/build/client/_app/immutable/assets/_page.Bhof07Eg.css +0 -18
  853. package/frontend/build/client/_app/immutable/assets/_page.Bhof07Eg.css.br +0 -0
  854. package/frontend/build/client/_app/immutable/assets/_page.Bhof07Eg.css.gz +0 -0
  855. package/frontend/build/client/_app/immutable/assets/_page.CrXpMrqh.css +0 -18
  856. package/frontend/build/client/_app/immutable/assets/_page.CrXpMrqh.css.br +0 -0
  857. package/frontend/build/client/_app/immutable/assets/_page.CrXpMrqh.css.gz +0 -0
  858. package/frontend/build/client/_app/immutable/chunks/B0pefNHY.js.br +0 -0
  859. package/frontend/build/client/_app/immutable/chunks/B0pefNHY.js.gz +0 -0
  860. package/frontend/build/client/_app/immutable/chunks/BFEiffAL.js.br +0 -0
  861. package/frontend/build/client/_app/immutable/chunks/BFEiffAL.js.gz +0 -0
  862. package/frontend/build/client/_app/immutable/chunks/BFgtfHj2.js +0 -18
  863. package/frontend/build/client/_app/immutable/chunks/BFgtfHj2.js.br +0 -0
  864. package/frontend/build/client/_app/immutable/chunks/BFgtfHj2.js.gz +0 -0
  865. package/frontend/build/client/_app/immutable/chunks/BIRVeCLU.js +0 -18
  866. package/frontend/build/client/_app/immutable/chunks/BIRVeCLU.js.br +0 -20
  867. package/frontend/build/client/_app/immutable/chunks/BIRVeCLU.js.gz +0 -0
  868. package/frontend/build/client/_app/immutable/chunks/BKCNyxZ2.js +0 -18
  869. package/frontend/build/client/_app/immutable/chunks/BKCNyxZ2.js.br +0 -0
  870. package/frontend/build/client/_app/immutable/chunks/BKCNyxZ2.js.gz +0 -0
  871. package/frontend/build/client/_app/immutable/chunks/BLAiBE__.js +0 -18
  872. package/frontend/build/client/_app/immutable/chunks/BLAiBE__.js.br +0 -18
  873. package/frontend/build/client/_app/immutable/chunks/BLAiBE__.js.gz +0 -0
  874. package/frontend/build/client/_app/immutable/chunks/BVMec-1T.js.br +0 -0
  875. package/frontend/build/client/_app/immutable/chunks/BVMec-1T.js.gz +0 -0
  876. package/frontend/build/client/_app/immutable/chunks/BeO8veBd.js +0 -18
  877. package/frontend/build/client/_app/immutable/chunks/BeO8veBd.js.br +0 -0
  878. package/frontend/build/client/_app/immutable/chunks/BeO8veBd.js.gz +0 -0
  879. package/frontend/build/client/_app/immutable/chunks/BgNaeXAp.js +0 -18
  880. package/frontend/build/client/_app/immutable/chunks/BgNaeXAp.js.br +0 -0
  881. package/frontend/build/client/_app/immutable/chunks/BgNaeXAp.js.gz +0 -0
  882. package/frontend/build/client/_app/immutable/chunks/BkKmeUbA.js.br +0 -0
  883. package/frontend/build/client/_app/immutable/chunks/BkKmeUbA.js.gz +0 -0
  884. package/frontend/build/client/_app/immutable/chunks/C3SY6-sX.js +0 -19
  885. package/frontend/build/client/_app/immutable/chunks/C3SY6-sX.js.br +0 -0
  886. package/frontend/build/client/_app/immutable/chunks/C3SY6-sX.js.gz +0 -0
  887. package/frontend/build/client/_app/immutable/chunks/C4eeChIU.js +0 -18
  888. package/frontend/build/client/_app/immutable/chunks/C4eeChIU.js.br +0 -0
  889. package/frontend/build/client/_app/immutable/chunks/C4eeChIU.js.gz +0 -0
  890. package/frontend/build/client/_app/immutable/chunks/C67QNx7Z.js.br +0 -0
  891. package/frontend/build/client/_app/immutable/chunks/C67QNx7Z.js.gz +0 -0
  892. package/frontend/build/client/_app/immutable/chunks/CVV6HrFb.js +0 -18
  893. package/frontend/build/client/_app/immutable/chunks/CVV6HrFb.js.br +0 -0
  894. package/frontend/build/client/_app/immutable/chunks/CVV6HrFb.js.gz +0 -0
  895. package/frontend/build/client/_app/immutable/chunks/CstKKb9b.js +0 -18
  896. package/frontend/build/client/_app/immutable/chunks/CstKKb9b.js.br +0 -0
  897. package/frontend/build/client/_app/immutable/chunks/CstKKb9b.js.gz +0 -0
  898. package/frontend/build/client/_app/immutable/chunks/CxTX_lpP.js.br +0 -0
  899. package/frontend/build/client/_app/immutable/chunks/CxTX_lpP.js.gz +0 -0
  900. package/frontend/build/client/_app/immutable/chunks/CyQ_oBw2.js +0 -18
  901. package/frontend/build/client/_app/immutable/chunks/CyQ_oBw2.js.br +0 -0
  902. package/frontend/build/client/_app/immutable/chunks/CyQ_oBw2.js.gz +0 -0
  903. package/frontend/build/client/_app/immutable/chunks/DGryW1Vp.js +0 -18
  904. package/frontend/build/client/_app/immutable/chunks/DGryW1Vp.js.br +0 -0
  905. package/frontend/build/client/_app/immutable/chunks/DGryW1Vp.js.gz +0 -0
  906. package/frontend/build/client/_app/immutable/chunks/DIx44H9N.js +0 -18
  907. package/frontend/build/client/_app/immutable/chunks/DIx44H9N.js.br +0 -0
  908. package/frontend/build/client/_app/immutable/chunks/DIx44H9N.js.gz +0 -0
  909. package/frontend/build/client/_app/immutable/chunks/DP6QP4L6.js +0 -18
  910. package/frontend/build/client/_app/immutable/chunks/DP6QP4L6.js.br +0 -0
  911. package/frontend/build/client/_app/immutable/chunks/DP6QP4L6.js.gz +0 -0
  912. package/frontend/build/client/_app/immutable/chunks/DmRbbcS3.js +0 -23
  913. package/frontend/build/client/_app/immutable/chunks/DmRbbcS3.js.br +0 -0
  914. package/frontend/build/client/_app/immutable/chunks/DmRbbcS3.js.gz +0 -0
  915. package/frontend/build/client/_app/immutable/chunks/IFIFk6zi.js +0 -18
  916. package/frontend/build/client/_app/immutable/chunks/IFIFk6zi.js.br +0 -0
  917. package/frontend/build/client/_app/immutable/chunks/IFIFk6zi.js.gz +0 -0
  918. package/frontend/build/client/_app/immutable/chunks/KJdjlibc.js +0 -20
  919. package/frontend/build/client/_app/immutable/chunks/KJdjlibc.js.br +0 -0
  920. package/frontend/build/client/_app/immutable/chunks/KJdjlibc.js.gz +0 -0
  921. package/frontend/build/client/_app/immutable/chunks/ll-VsEGv.js +0 -18
  922. package/frontend/build/client/_app/immutable/chunks/ll-VsEGv.js.br +0 -0
  923. package/frontend/build/client/_app/immutable/chunks/ll-VsEGv.js.gz +0 -0
  924. package/frontend/build/client/_app/immutable/chunks/raHbXn_9.js +0 -18
  925. package/frontend/build/client/_app/immutable/chunks/raHbXn_9.js.br +0 -0
  926. package/frontend/build/client/_app/immutable/chunks/raHbXn_9.js.gz +0 -0
  927. package/frontend/build/client/_app/immutable/chunks/uFRI_bmR.js +0 -18
  928. package/frontend/build/client/_app/immutable/chunks/uFRI_bmR.js.br +0 -0
  929. package/frontend/build/client/_app/immutable/chunks/uFRI_bmR.js.gz +0 -0
  930. package/frontend/build/client/_app/immutable/entry/app.Dz_CBZcR.js +0 -19
  931. package/frontend/build/client/_app/immutable/entry/app.Dz_CBZcR.js.br +0 -0
  932. package/frontend/build/client/_app/immutable/entry/app.Dz_CBZcR.js.gz +0 -0
  933. package/frontend/build/client/_app/immutable/entry/start.MMlC62np.js +0 -18
  934. package/frontend/build/client/_app/immutable/entry/start.MMlC62np.js.br +0 -18
  935. package/frontend/build/client/_app/immutable/entry/start.MMlC62np.js.gz +0 -0
  936. package/frontend/build/client/_app/immutable/nodes/0.CKD9Qja6.js +0 -23
  937. package/frontend/build/client/_app/immutable/nodes/0.CKD9Qja6.js.br +0 -0
  938. package/frontend/build/client/_app/immutable/nodes/0.CKD9Qja6.js.gz +0 -0
  939. package/frontend/build/client/_app/immutable/nodes/1.Dbck5ct8.js +0 -18
  940. package/frontend/build/client/_app/immutable/nodes/1.Dbck5ct8.js.br +0 -0
  941. package/frontend/build/client/_app/immutable/nodes/1.Dbck5ct8.js.gz +0 -0
  942. package/frontend/build/client/_app/immutable/nodes/10.BfmGpmC1.js +0 -19
  943. package/frontend/build/client/_app/immutable/nodes/10.BfmGpmC1.js.br +0 -0
  944. package/frontend/build/client/_app/immutable/nodes/10.BfmGpmC1.js.gz +0 -0
  945. package/frontend/build/client/_app/immutable/nodes/11.C7l9HPom.js +0 -18
  946. package/frontend/build/client/_app/immutable/nodes/11.C7l9HPom.js.br +0 -0
  947. package/frontend/build/client/_app/immutable/nodes/11.C7l9HPom.js.gz +0 -0
  948. package/frontend/build/client/_app/immutable/nodes/12.Iu9bcGdC.js +0 -19
  949. package/frontend/build/client/_app/immutable/nodes/12.Iu9bcGdC.js.br +0 -0
  950. package/frontend/build/client/_app/immutable/nodes/12.Iu9bcGdC.js.gz +0 -0
  951. package/frontend/build/client/_app/immutable/nodes/2.BqQhRvYh.js +0 -18
  952. package/frontend/build/client/_app/immutable/nodes/2.BqQhRvYh.js.br +0 -0
  953. package/frontend/build/client/_app/immutable/nodes/2.BqQhRvYh.js.gz +0 -0
  954. package/frontend/build/client/_app/immutable/nodes/3.BZRTKadS.js +0 -18
  955. package/frontend/build/client/_app/immutable/nodes/3.BZRTKadS.js.br +0 -0
  956. package/frontend/build/client/_app/immutable/nodes/3.BZRTKadS.js.gz +0 -0
  957. package/frontend/build/client/_app/immutable/nodes/4.BfKoPKdM.js +0 -18
  958. package/frontend/build/client/_app/immutable/nodes/4.BfKoPKdM.js.br +0 -0
  959. package/frontend/build/client/_app/immutable/nodes/4.BfKoPKdM.js.gz +0 -0
  960. package/frontend/build/client/_app/immutable/nodes/5.B4a2uCWn.js +0 -18
  961. package/frontend/build/client/_app/immutable/nodes/5.B4a2uCWn.js.br +0 -0
  962. package/frontend/build/client/_app/immutable/nodes/5.B4a2uCWn.js.gz +0 -0
  963. package/frontend/build/client/_app/immutable/nodes/6.CI508CMS.js +0 -18
  964. package/frontend/build/client/_app/immutable/nodes/6.CI508CMS.js.br +0 -0
  965. package/frontend/build/client/_app/immutable/nodes/6.CI508CMS.js.gz +0 -0
  966. package/frontend/build/client/_app/immutable/nodes/7.3x_eiof1.js +0 -18
  967. package/frontend/build/client/_app/immutable/nodes/7.3x_eiof1.js.br +0 -0
  968. package/frontend/build/client/_app/immutable/nodes/7.3x_eiof1.js.gz +0 -0
  969. package/frontend/build/client/_app/immutable/nodes/8.BfUuob4H.js +0 -29
  970. package/frontend/build/client/_app/immutable/nodes/8.BfUuob4H.js.br +0 -0
  971. package/frontend/build/client/_app/immutable/nodes/8.BfUuob4H.js.gz +0 -0
  972. package/frontend/build/client/_app/immutable/nodes/9.BZhCKRJe.js +0 -18
  973. package/frontend/build/client/_app/immutable/nodes/9.BZhCKRJe.js.br +0 -0
  974. package/frontend/build/client/_app/immutable/nodes/9.BZhCKRJe.js.gz +0 -0
  975. package/frontend/build/server/chunks/0-Dzas6zkl.js +0 -34
  976. package/frontend/build/server/chunks/0-Dzas6zkl.js.map +0 -17
  977. package/frontend/build/server/chunks/1-B4EsP8yr.js +0 -26
  978. package/frontend/build/server/chunks/1-B4EsP8yr.js.map +0 -17
  979. package/frontend/build/server/chunks/10-UhDQZ5BY.js +0 -26
  980. package/frontend/build/server/chunks/10-UhDQZ5BY.js.map +0 -17
  981. package/frontend/build/server/chunks/11-mTH3nQlX.js +0 -26
  982. package/frontend/build/server/chunks/11-mTH3nQlX.js.map +0 -17
  983. package/frontend/build/server/chunks/12-9mg6kp53.js +0 -26
  984. package/frontend/build/server/chunks/12-9mg6kp53.js.map +0 -17
  985. package/frontend/build/server/chunks/2-Bkz8H7CT.js +0 -26
  986. package/frontend/build/server/chunks/2-Bkz8H7CT.js.map +0 -17
  987. package/frontend/build/server/chunks/3-DDtaiqu7.js +0 -26
  988. package/frontend/build/server/chunks/3-DDtaiqu7.js.map +0 -17
  989. package/frontend/build/server/chunks/4-DbdkTAvS.js +0 -26
  990. package/frontend/build/server/chunks/4-DbdkTAvS.js.map +0 -17
  991. package/frontend/build/server/chunks/5-CK5hY2WF.js +0 -26
  992. package/frontend/build/server/chunks/5-CK5hY2WF.js.map +0 -17
  993. package/frontend/build/server/chunks/6-Cps9fL3Z.js +0 -26
  994. package/frontend/build/server/chunks/6-Cps9fL3Z.js.map +0 -17
  995. package/frontend/build/server/chunks/7-CgUpbaJD.js +0 -26
  996. package/frontend/build/server/chunks/7-CgUpbaJD.js.map +0 -17
  997. package/frontend/build/server/chunks/8-dilaVKYo.js +0 -26
  998. package/frontend/build/server/chunks/8-dilaVKYo.js.map +0 -17
  999. package/frontend/build/server/chunks/9-CUJxgoIN.js +0 -26
  1000. package/frontend/build/server/chunks/9-CUJxgoIN.js.map +0 -17
  1001. package/frontend/build/server/chunks/BackLink-KxZNqsaq.js +0 -30
  1002. package/frontend/build/server/chunks/Badge-B5V-z6pI.js +0 -30
  1003. package/frontend/build/server/chunks/Button-CuacyA7Y.js +0 -33
  1004. package/frontend/build/server/chunks/Button-CuacyA7Y.js.map +0 -17
  1005. package/frontend/build/server/chunks/ConfirmModal-DBtnLLsU.js +0 -73
  1006. package/frontend/build/server/chunks/ConfirmModal-DBtnLLsU.js.map +0 -17
  1007. package/frontend/build/server/chunks/EmptyState-B74-VZ15.js +0 -34
  1008. package/frontend/build/server/chunks/EmptyState-B74-VZ15.js.map +0 -17
  1009. package/frontend/build/server/chunks/Toast-DDF2wi_5.js +0 -37
  1010. package/frontend/build/server/chunks/Toast-DDF2wi_5.js.map +0 -17
  1011. package/frontend/build/server/chunks/_layout.svelte-DoJXDbP1.js +0 -39
  1012. package/frontend/build/server/chunks/_layout.svelte-DoJXDbP1.js.map +0 -17
  1013. package/frontend/build/server/chunks/_page.svelte-BETENC2e.js.map +0 -17
  1014. package/frontend/build/server/chunks/_page.svelte-C0vstY93.js.map +0 -17
  1015. package/frontend/build/server/chunks/_page.svelte-CC27sZag.js.map +0 -17
  1016. package/frontend/build/server/chunks/_page.svelte-CYkOPxn2.js +0 -183
  1017. package/frontend/build/server/chunks/_page.svelte-CYkOPxn2.js.map +0 -17
  1018. package/frontend/build/server/chunks/_page.svelte-CperUuP9.js +0 -564
  1019. package/frontend/build/server/chunks/_page.svelte-CperUuP9.js.map +0 -17
  1020. package/frontend/build/server/chunks/_page.svelte-CwrCf0C8.js +0 -65
  1021. package/frontend/build/server/chunks/_page.svelte-CwrCf0C8.js.map +0 -17
  1022. package/frontend/build/server/chunks/_page.svelte-DALHRFEX.js +0 -43
  1023. package/frontend/build/server/chunks/_page.svelte-DALHRFEX.js.map +0 -17
  1024. package/frontend/build/server/chunks/_page.svelte-DNNyBx4y.js +0 -43
  1025. package/frontend/build/server/chunks/_page.svelte-DNNyBx4y.js.map +0 -17
  1026. package/frontend/build/server/chunks/_page.svelte-DqWsUJRp.js.map +0 -17
  1027. package/frontend/build/server/chunks/_page.svelte-LcJmR5du.js.map +0 -17
  1028. package/frontend/build/server/chunks/_page.svelte-gnktnXKP.js.map +0 -17
  1029. package/frontend/build/server/chunks/constants-Drz-Vo39.js +0 -35
  1030. package/frontend/build/server/chunks/constants-Drz-Vo39.js.map +0 -17
  1031. package/frontend/build/server/chunks/error.svelte-BDW3iP1O.js +0 -45
  1032. package/frontend/build/server/chunks/index-server-Dr2Nvcmu.js +0 -31
  1033. package/frontend/build/server/chunks/index-server-Dr2Nvcmu.js.map +0 -17
  1034. package/frontend/build/server/chunks/reports-Bgi7V5N7.js +0 -40
  1035. package/frontend/build/server/chunks/runner-D18Ml_vF.js +0 -42
  1036. package/frontend/build/server/chunks/runner-D18Ml_vF.js.map +0 -17
  1037. package/frontend/build/server/chunks/stores-DlkXV308.js +0 -44
  1038. package/frontend/build/server/chunks/theme-D_NKm6iI.js +0 -27
  1039. package/frontend/build/server/chunks/theme-D_NKm6iI.js.map +0 -17
  1040. package/resources/gpl-3.0-license.txt +0 -14
@@ -1,18 +1,6 @@
1
1
  <!--
2
2
  * This file is part of Plum.
3
- *
4
- * Plum is free software: you can redistribute it and/or modify
5
- * it under the terms of the GNU General Public License as published by
6
- * the Free Software Foundation, either version 3 of the License, or
7
- * (at your option) any later version.
8
- *
9
- * Plum is distributed in the hope that it will be useful,
10
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- * GNU General Public License for more details.
13
- *
14
- * You should have received a copy of the GNU General Public License
15
- * along with Plum. If not, see https://www.gnu.org/licenses/.
3
+ * Licensed under the MIT License. See LICENSE file in the project root for details.
16
4
  -->
17
5
 
18
6
  <script>
@@ -53,11 +41,231 @@
53
41
  import { builtInEnabled } from '$lib/stores/runner';
54
42
  import { auth } from '$lib/stores/auth';
55
43
  import { theme } from '$lib/stores/theme';
56
- import { API_BASE, BROWSERS, TOAST_TIMEOUT_MS, MAX_TEST_RETRIES } from '$lib/constants';
44
+ import {
45
+ API_BASE,
46
+ BROWSERS,
47
+ TOAST_TIMEOUT_MS,
48
+ MAX_TEST_RETRIES,
49
+ COPY_TIMEOUT_MS
50
+ } from '$lib/constants';
57
51
  import Button from '$lib/components/ui/Button.svelte';
58
52
  import Toast from '$lib/components/ui/Toast.svelte';
59
53
  import ConfirmModal from '$lib/components/ui/ConfirmModal.svelte';
60
54
  import Badge from '$lib/components/ui/Badge.svelte';
55
+ import { CANCEL_LABEL, EDIT_LABEL, SAVE_LABEL, EMAIL_LABEL } from '$lib/copy/common';
56
+ import {
57
+ PAGE_TITLE,
58
+ HEADING,
59
+ NAME_LABEL,
60
+ NETWORK_ERROR,
61
+ PROJECT_LABEL,
62
+ RUNNERS_LABEL,
63
+ REPOSITORY_NAV_LABEL,
64
+ REPOSITORY_HEADING,
65
+ INTEGRATIONS_LABEL,
66
+ MCP_NAV_LABEL,
67
+ MCP_HEADING,
68
+ ACCOUNT_LABEL,
69
+ USERS_LABEL,
70
+ BACKUP_LABEL,
71
+ PROJECT_DESC,
72
+ RUNNERS_DESC,
73
+ REPOSITORY_DESC,
74
+ INTEGRATIONS_DESC,
75
+ MCP_DESC,
76
+ ACCOUNT_DESC,
77
+ USERS_DESC,
78
+ BACKUP_DESC,
79
+ RUNNER_FIELDS_REQUIRED_ERROR,
80
+ ADD_RUNNER_FAILED,
81
+ REMOVE_RUNNER_FAILED,
82
+ UPDATE_RUNNER_FAILED,
83
+ cannotReachRunnerError,
84
+ runnerAddedToast,
85
+ runnerRemovedToast,
86
+ runnerStoppedToast,
87
+ runnerStopFailedToast,
88
+ runnerStopFailedGenericToast,
89
+ runnerRestartingToast,
90
+ runnerRestartFailedToast,
91
+ runnerRestartFailedGenericToast,
92
+ runnerUpdatedToast,
93
+ PROJECT_NAME_LABEL,
94
+ PROJECT_NAME_PLACEHOLDER,
95
+ LOGO_URL_LABEL,
96
+ LOGO_URL_HINT,
97
+ LOGO_URL_PLACEHOLDER,
98
+ PREVIEW_LABEL,
99
+ LOGO_PREVIEW_ALT,
100
+ TIMEZONE_LABEL,
101
+ TIMEZONE_HINT,
102
+ RETRY_FAILED_TESTS_LABEL,
103
+ RETRY_FAILED_TESTS_HINT,
104
+ DARK_MODE_LABEL,
105
+ DARK_MODE_DESC,
106
+ PROJECT_SAVED_TOAST,
107
+ PROJECT_SAVE_FAILED,
108
+ saveProjectLabel,
109
+ BUILTIN_RUNNER_TOGGLE_LABEL,
110
+ BUILTIN_RUNNER_TOGGLE_DESC,
111
+ RUNNER_URL_LABEL,
112
+ RUNNER_URL_HINT_PREFIX,
113
+ RUNNER_URL_HINT_SUFFIX,
114
+ RUNNER_URL_PLACEHOLDER,
115
+ TOKEN_LABEL,
116
+ TOKEN_PLACEHOLDER,
117
+ KEEP_TOKEN_PLACEHOLDER,
118
+ BROWSER_LABEL,
119
+ RUNNER_NAME_PLACEHOLDER,
120
+ RUNNER_UNREACHABLE_LABEL,
121
+ RUNNER_PINGING_LABEL,
122
+ REMOVE_LABEL,
123
+ ADD_RUNNER_FORM_TITLE,
124
+ OPEN_ADD_RUNNER_LABEL,
125
+ editRunnerSubmitLabel,
126
+ addRunnerSubmitLabel,
127
+ restartRunnerLabel,
128
+ stopRunnerLabel,
129
+ CASE_PREFIX_LABEL,
130
+ CASE_PREFIX_PLACEHOLDER,
131
+ SUITE_PREFIX_LABEL,
132
+ SUITE_PREFIX_PLACEHOLDER,
133
+ EXAMPLES_LABEL,
134
+ PREFIXES_SAVED_TOAST,
135
+ PREFIXES_SAVE_FAILED,
136
+ MIGRATE_IDS_HEADING,
137
+ MIGRATE_DESC_PREFIX,
138
+ MIGRATE_DESC_STRONG,
139
+ MIGRATE_DESC_SUFFIX,
140
+ NEW_CASE_PREFIX_LABEL,
141
+ NEW_SUITE_PREFIX_LABEL,
142
+ MIGRATION_COMPLETE_TOAST,
143
+ MIGRATION_FAILED_TOAST,
144
+ savePrefixesLabel,
145
+ runMigrationLabel,
146
+ WEBHOOKS_CARD_TITLE,
147
+ DISCORD_WEBHOOK_LABEL,
148
+ DISCORD_WEBHOOK_HINT,
149
+ DISCORD_WEBHOOK_PLACEHOLDER,
150
+ SLACK_WEBHOOK_LABEL,
151
+ SLACK_WEBHOOK_HINT,
152
+ SLACK_WEBHOOK_PLACEHOLDER,
153
+ PUBLIC_URL_LABEL,
154
+ PUBLIC_URL_HINT,
155
+ PUBLIC_URL_PLACEHOLDER,
156
+ INTEGRATIONS_SAVED_TOAST,
157
+ INTEGRATIONS_SAVE_FAILED,
158
+ CI_TRIGGERS_CARD_TITLE,
159
+ CI_DESC_PART1,
160
+ CI_DESC_PART2,
161
+ CI_DESC_PART3,
162
+ MCP_TAB_LINK_LABEL,
163
+ CI_DESC_PART4,
164
+ EXTERNAL_BADGE_LABEL,
165
+ saveIntegrationsLabel,
166
+ copyCiSnippetLabel,
167
+ API_KEY_CARD_TITLE,
168
+ NO_KEY_GENERATED_MESSAGE,
169
+ HIDE_KEY_TITLE,
170
+ SHOW_KEY_TITLE,
171
+ COPY_KEY_TITLE,
172
+ MCP_REGEN_NOTE,
173
+ CONFIG_SNIPPET_CARD_TITLE,
174
+ CONFIG_SNIPPET_DESC_PREFIX,
175
+ CONFIG_SNIPPET_DESC_SUFFIX,
176
+ MCP_KEY_GENERATED_TOAST,
177
+ MCP_KEY_GENERATE_FAILED,
178
+ generateKeyLabel,
179
+ regenerateKeyLabel,
180
+ copyMcpSnippetLabel,
181
+ PROFILE_CARD_TITLE,
182
+ CHANGE_PASSWORD_CARD_TITLE,
183
+ CURRENT_PASSWORD_LABEL,
184
+ NEW_PASSWORD_LABEL,
185
+ CONFIRM_NEW_PASSWORD_LABEL,
186
+ SIGN_OUT_LABEL,
187
+ PROFILE_UPDATED_TOAST,
188
+ PASSWORDS_NO_MATCH_ERROR,
189
+ PASSWORD_TOO_SHORT_ERROR,
190
+ PASSWORD_CHANGED_TOAST,
191
+ saveProfileLabel,
192
+ changePasswordLabel,
193
+ REMOVE_USER_MODAL_TITLE,
194
+ REMOVE_USER_LABEL,
195
+ REMOVE_USER_BODY_PREFIX,
196
+ REMOVE_USER_BODY_SUFFIX,
197
+ ADD_USER_CARD_TITLE,
198
+ USER_NAME_PLACEHOLDER,
199
+ USER_EMAIL_PLACEHOLDER,
200
+ PASSWORD_LABEL,
201
+ ROLE_LABEL,
202
+ USER_ROLE_OPTION,
203
+ ADMIN_ROLE_OPTION,
204
+ REMOVE_USER_ICON_TITLE,
205
+ YOU_CHIP_LABEL,
206
+ USER_FORM_REQUIRED_ERROR,
207
+ addUserLabel,
208
+ userAddedToast,
209
+ userRemovedToast,
210
+ MANUAL_BACKUP_CARD_TITLE,
211
+ EXPORT_BLOCK_TITLE,
212
+ EXPORT_BLOCK_DESC_PREFIX,
213
+ EXPORT_BLOCK_DESC_SUFFIX,
214
+ IMPORT_BLOCK_TITLE,
215
+ IMPORT_BLOCK_DESC,
216
+ CHOOSE_FILE_LABEL,
217
+ BACKUP_DISCLAIMER_PREFIX,
218
+ BACKUP_DISCLAIMER_SUFFIX,
219
+ S3_STORAGE_CARD_TITLE,
220
+ S3_STORAGE_DESC_PREFIX,
221
+ ENDPOINT_URL_LABEL,
222
+ S3_STORAGE_DESC_SUFFIX,
223
+ ENDPOINT_URL_HINT,
224
+ ENDPOINT_URL_PLACEHOLDER,
225
+ REGION_LABEL,
226
+ REGION_HINT_PREFIX,
227
+ REGION_HINT_SUFFIX,
228
+ REGION_PLACEHOLDER,
229
+ BUCKET_LABEL,
230
+ BUCKET_PLACEHOLDER,
231
+ PATH_PREFIX_LABEL,
232
+ PATH_PREFIX_HINT,
233
+ PATH_PREFIX_PLACEHOLDER,
234
+ ACCESS_KEY_LABEL,
235
+ ACCESS_KEY_PLACEHOLDER,
236
+ SECRET_KEY_LABEL,
237
+ TEST_CONNECTION_LABEL,
238
+ SAVE_S3_CONFIG_LABEL,
239
+ S3_CONNECTION_SUCCESS,
240
+ S3_CONNECTION_FAILED,
241
+ BACKUP_CONFIG_SAVED_TOAST,
242
+ BACKUP_CONFIG_SAVE_FAILED,
243
+ SCHEDULED_BACKUP_CARD_TITLE,
244
+ CONFIGURE_S3_FIRST_MESSAGE,
245
+ ENABLE_SCHEDULED_BACKUP_LABEL,
246
+ CRON_EXPRESSION_LABEL,
247
+ CRON_HINT_PREFIX,
248
+ CRON_HINT_SUFFIX,
249
+ CRONTAB_LINK_LABEL,
250
+ CRON_PLACEHOLDER,
251
+ BACKUP_LAST_RUN_PREFIX,
252
+ BACKUP_DOWNLOADED_TOAST,
253
+ EXPORT_FAILED_TOAST,
254
+ IMPORT_SUCCESS_TOAST,
255
+ IMPORT_FAILED_FALLBACK,
256
+ BACKUP_UPLOAD_SUCCESS_TOAST,
257
+ BACKUP_UPLOAD_FAILED_FALLBACK,
258
+ backupFilename,
259
+ exportLabel,
260
+ importLabel,
261
+ secretKeyHint,
262
+ secretKeyPlaceholder,
263
+ testConnectionLabel,
264
+ saveS3ConfigLabel,
265
+ uploadedToLabel,
266
+ uploadS3NowLabel,
267
+ saveScheduleLabel
268
+ } from '$lib/copy/settings';
61
269
 
62
270
  /** @type {'project' | 'runners' | 'repository' | 'integrations' | 'mcp' | 'account' | 'users' | 'backup'} */
63
271
  let section =
@@ -224,7 +432,7 @@
224
432
  } catch {
225
433
  pingResults = {
226
434
  ...pingResults,
227
- [r.id]: { ok: false, error: 'Network error', loading: false }
435
+ [r.id]: { ok: false, error: NETWORK_ERROR, loading: false }
228
436
  };
229
437
  }
230
438
  })
@@ -247,7 +455,7 @@
247
455
 
248
456
  async function handleAddRunner() {
249
457
  if (!runnerForm.name || !runnerForm.url || !runnerForm.token) {
250
- runnerFormError = 'Name, URL and token are required.';
458
+ runnerFormError = RUNNER_FIELDS_REQUIRED_ERROR;
251
459
  return;
252
460
  }
253
461
  runnerFormError = '';
@@ -255,7 +463,7 @@
255
463
  try {
256
464
  const probe = await probeRunner(runnerForm.url, runnerForm.token);
257
465
  if (!probe.ok) {
258
- runnerFormError = `Cannot reach this runner — ${probe.error ?? 'check the URL and token'}.`;
466
+ runnerFormError = cannotReachRunnerError(probe.error);
259
467
  return;
260
468
  }
261
469
  const { runner } = await createRunner(runnerForm);
@@ -266,9 +474,9 @@
266
474
  };
267
475
  runnerForm = { name: '', url: '', token: '', browser: 'chromium' };
268
476
  runnerFormOpen = false;
269
- showToast('success', `Runner "${runner.name}" added.`);
477
+ showToast('success', runnerAddedToast(runner.name));
270
478
  } catch {
271
- runnerFormError = 'Failed to add runner.';
479
+ runnerFormError = ADD_RUNNER_FAILED;
272
480
  } finally {
273
481
  runnerFormSaving = false;
274
482
  }
@@ -278,9 +486,9 @@
278
486
  try {
279
487
  await deleteRunner(id);
280
488
  runners = runners.filter((r) => r.id !== id);
281
- showToast('success', `Runner "${name}" removed.`);
489
+ showToast('success', runnerRemovedToast(name));
282
490
  } catch {
283
- showToast('error', 'Failed to remove runner.');
491
+ showToast('error', REMOVE_RUNNER_FAILED);
284
492
  }
285
493
  }
286
494
 
@@ -290,7 +498,7 @@
290
498
  const result = await pingRunner(id);
291
499
  pingResults = { ...pingResults, [id]: { ...result, loading: false } };
292
500
  } catch {
293
- pingResults = { ...pingResults, [id]: { ok: false, error: 'Network error', loading: false } };
501
+ pingResults = { ...pingResults, [id]: { ok: false, error: NETWORK_ERROR, loading: false } };
294
502
  }
295
503
  }
296
504
 
@@ -299,12 +507,12 @@
299
507
  try {
300
508
  const result = await stopRunner(id);
301
509
  if (result.ok) {
302
- showToast('success', `Runner "${name}" stopped.`);
510
+ showToast('success', runnerStoppedToast(name));
303
511
  } else {
304
- showToast('error', `Could not stop "${name}": ${result.error ?? 'unknown error'}`);
512
+ showToast('error', runnerStopFailedToast(name, result.error));
305
513
  }
306
514
  } catch {
307
- showToast('error', `Could not stop "${name}".`);
515
+ showToast('error', runnerStopFailedGenericToast(name));
308
516
  } finally {
309
517
  stoppingId = null;
310
518
  refreshPing(id);
@@ -316,12 +524,12 @@
316
524
  try {
317
525
  const result = await restartRunner(id);
318
526
  if (result.ok) {
319
- showToast('success', `Runner "${name}" restarting…`);
527
+ showToast('success', runnerRestartingToast(name));
320
528
  } else {
321
- showToast('error', `Could not restart "${name}": ${result.error ?? 'unknown error'}`);
529
+ showToast('error', runnerRestartFailedToast(name, result.error));
322
530
  }
323
531
  } catch {
324
- showToast('error', `Could not restart "${name}".`);
532
+ showToast('error', runnerRestartFailedGenericToast(name));
325
533
  } finally {
326
534
  restartingId = null;
327
535
  // Give the replacement process a moment to bind before checking on it.
@@ -331,30 +539,40 @@
331
539
 
332
540
  function startEdit(r) {
333
541
  editingId = r.id;
334
- editForm = { name: r.name, url: r.url, token: r.token, browser: r.browser };
542
+ // Token is never sent back by the server leave blank; the update
543
+ // only replaces it if the admin types a new one (see handleUpdateRunner).
544
+ editForm = { name: r.name, url: r.url, token: '', browser: r.browser };
335
545
  editFormError = '';
336
546
  }
337
547
 
338
548
  async function handleUpdateRunner(id) {
339
- if (!editForm.name || !editForm.url || !editForm.token) {
340
- editFormError = 'Name, URL and token are required.';
549
+ if (!editForm.name || !editForm.url) {
550
+ editFormError = RUNNER_FIELDS_REQUIRED_ERROR;
341
551
  return;
342
552
  }
343
553
  editFormError = '';
344
554
  editFormSaving = true;
345
555
  try {
346
- const probe = await probeRunner(editForm.url, editForm.token);
347
- if (!probe.ok) {
348
- editFormError = `Cannot reach this runner — ${probe.error ?? 'check the URL and token'}.`;
349
- return;
556
+ // Only re-probe (and require a reachable token) when the admin actually
557
+ // typed a new one — an empty token here means "keep the existing one",
558
+ // which the still-running node already accepts.
559
+ if (editForm.token) {
560
+ const probe = await probeRunner(editForm.url, editForm.token);
561
+ if (!probe.ok) {
562
+ editFormError = cannotReachRunnerError(probe.error);
563
+ return;
564
+ }
565
+ pingResults = {
566
+ ...pingResults,
567
+ [id]: { ok: true, latency: probe.latency, loading: false }
568
+ };
350
569
  }
351
570
  const { runner } = await updateRunner(id, editForm);
352
571
  runners = runners.map((r) => (r.id === id ? runner : r));
353
- pingResults = { ...pingResults, [id]: { ok: true, latency: probe.latency, loading: false } };
354
572
  editingId = null;
355
- showToast('success', `Runner "${runner.name}" updated.`);
573
+ showToast('success', runnerUpdatedToast(runner.name));
356
574
  } catch {
357
- editFormError = 'Failed to update runner.';
575
+ editFormError = UPDATE_RUNNER_FAILED;
358
576
  } finally {
359
577
  editFormSaving = false;
360
578
  }
@@ -364,9 +582,9 @@
364
582
  projectSaving = true;
365
583
  try {
366
584
  await saveProject(project);
367
- showToast('success', 'Project settings saved.');
585
+ showToast('success', PROJECT_SAVED_TOAST);
368
586
  } catch {
369
- showToast('error', 'Failed to save project settings.');
587
+ showToast('error', PROJECT_SAVE_FAILED);
370
588
  } finally {
371
589
  projectSaving = false;
372
590
  }
@@ -380,12 +598,12 @@
380
598
  const url = URL.createObjectURL(blob);
381
599
  const a = document.createElement('a');
382
600
  a.href = url;
383
- a.download = `plum-backup-${new Date().toISOString().slice(0, 10)}.json`;
601
+ a.download = backupFilename(new Date().toISOString().slice(0, 10));
384
602
  a.click();
385
603
  URL.revokeObjectURL(url);
386
- showToast('success', 'Backup downloaded.');
604
+ showToast('success', BACKUP_DOWNLOADED_TOAST);
387
605
  } catch {
388
- showToast('error', 'Export failed.');
606
+ showToast('error', EXPORT_FAILED_TOAST);
389
607
  } finally {
390
608
  exporting = false;
391
609
  }
@@ -399,12 +617,12 @@
399
617
  const data = JSON.parse(text);
400
618
  const result = await importBackup(data);
401
619
  if (result.error) throw new Error(result.error);
402
- showToast('success', 'Import successful. Cron jobs have been re-scheduled.');
620
+ showToast('success', IMPORT_SUCCESS_TOAST);
403
621
  project = await fetchProject();
404
622
  importFile = null;
405
623
  if (fileInput) fileInput.value = '';
406
624
  } catch (e) {
407
- showToast('error', e.message || 'Import failed. Check the file format.');
625
+ showToast('error', e.message || IMPORT_FAILED_FALLBACK);
408
626
  } finally {
409
627
  importing = false;
410
628
  }
@@ -418,9 +636,9 @@
418
636
  prefixesSaving = true;
419
637
  try {
420
638
  prefixes = await savePrefixes(prefixes);
421
- showToast('success', 'Prefixes saved.');
639
+ showToast('success', PREFIXES_SAVED_TOAST);
422
640
  } catch {
423
- showToast('error', 'Failed to save prefixes.');
641
+ showToast('error', PREFIXES_SAVE_FAILED);
424
642
  } finally {
425
643
  prefixesSaving = false;
426
644
  }
@@ -431,9 +649,9 @@
431
649
  try {
432
650
  await migratePrefixes(migrateForm);
433
651
  prefixes = { ...prefixes, ...migrateForm };
434
- showToast('success', 'Prefix migration complete. All IDs updated.');
652
+ showToast('success', MIGRATION_COMPLETE_TOAST);
435
653
  } catch {
436
- showToast('error', 'Migration failed.');
654
+ showToast('error', MIGRATION_FAILED_TOAST);
437
655
  } finally {
438
656
  migrating = false;
439
657
  }
@@ -449,7 +667,7 @@
449
667
  email: profileForm.email
450
668
  });
451
669
  auth.login($auth.token, { ...$auth.user, ...user });
452
- showToast('success', 'Profile updated.');
670
+ showToast('success', PROFILE_UPDATED_TOAST);
453
671
  } catch (e) {
454
672
  profileError = e.message;
455
673
  } finally {
@@ -460,7 +678,7 @@
460
678
  async function handleCreateUser() {
461
679
  userFormError = '';
462
680
  if (!userForm.name || !userForm.email || !userForm.password) {
463
- userFormError = 'Name, email and password are required.';
681
+ userFormError = USER_FORM_REQUIRED_ERROR;
464
682
  return;
465
683
  }
466
684
  userFormSaving = true;
@@ -468,7 +686,7 @@
468
686
  const user = await createUserApi(userForm);
469
687
  allUsers = [...allUsers, user];
470
688
  userForm = { name: '', email: '', password: '', role: 'user' };
471
- showToast('success', `User "${user.name}" added.`);
689
+ showToast('success', userAddedToast(user.name));
472
690
  } catch (e) {
473
691
  userFormError = e.message;
474
692
  } finally {
@@ -480,7 +698,7 @@
480
698
  try {
481
699
  await deleteUserApi(id);
482
700
  allUsers = allUsers.filter((u) => u.id !== id);
483
- showToast('success', `User "${name}" removed.`);
701
+ showToast('success', userRemovedToast(name));
484
702
  } catch (e) {
485
703
  showToast('error', e.message);
486
704
  }
@@ -491,11 +709,11 @@
491
709
  async function handleChangePassword() {
492
710
  pwError = '';
493
711
  if (pwForm.newPassword !== pwForm.confirmPassword) {
494
- pwError = 'Passwords do not match.';
712
+ pwError = PASSWORDS_NO_MATCH_ERROR;
495
713
  return;
496
714
  }
497
715
  if (pwForm.newPassword.length < 8) {
498
- pwError = 'Password must be at least 8 characters.';
716
+ pwError = PASSWORD_TOO_SHORT_ERROR;
499
717
  return;
500
718
  }
501
719
  pwSaving = true;
@@ -506,7 +724,7 @@
506
724
  newPassword: pwForm.newPassword
507
725
  });
508
726
  pwForm = { currentPassword: '', newPassword: '', confirmPassword: '' };
509
- showToast('success', 'Password changed.');
727
+ showToast('success', PASSWORD_CHANGED_TOAST);
510
728
  } catch (e) {
511
729
  pwError = e.message;
512
730
  } finally {
@@ -526,9 +744,9 @@
526
744
  mcpKey = result.mcpKey;
527
745
  mcpKeySet = true;
528
746
  mcpShowKey = true;
529
- showToast('success', 'MCP key generated.');
747
+ showToast('success', MCP_KEY_GENERATED_TOAST);
530
748
  } catch {
531
- showToast('error', 'Failed to generate MCP key.');
749
+ showToast('error', MCP_KEY_GENERATE_FAILED);
532
750
  } finally {
533
751
  mcpGenerating = false;
534
752
  }
@@ -537,21 +755,21 @@
537
755
  function handleCopyMcpKey() {
538
756
  navigator.clipboard.writeText(mcpKey).then(() => {
539
757
  mcpKeyCopied = true;
540
- setTimeout(() => (mcpKeyCopied = false), 1500);
758
+ setTimeout(() => (mcpKeyCopied = false), COPY_TIMEOUT_MS);
541
759
  });
542
760
  }
543
761
 
544
762
  function handleCopyMcpSnippet() {
545
763
  navigator.clipboard.writeText(mcpConfigSnippet).then(() => {
546
764
  mcpSnippetCopied = true;
547
- setTimeout(() => (mcpSnippetCopied = false), 1500);
765
+ setTimeout(() => (mcpSnippetCopied = false), COPY_TIMEOUT_MS);
548
766
  });
549
767
  }
550
768
 
551
769
  function handleCopyCiSnippet() {
552
770
  navigator.clipboard.writeText(ciWorkflowSnippet).then(() => {
553
771
  ciSnippetCopied = true;
554
- setTimeout(() => (ciSnippetCopied = false), 1500);
772
+ setTimeout(() => (ciSnippetCopied = false), COPY_TIMEOUT_MS);
555
773
  });
556
774
  }
557
775
 
@@ -559,9 +777,9 @@
559
777
  integrationsSaving = true;
560
778
  try {
561
779
  integrations = await saveIntegrations(integrations);
562
- showToast('success', 'Integration settings saved.');
780
+ showToast('success', INTEGRATIONS_SAVED_TOAST);
563
781
  } catch {
564
- showToast('error', 'Failed to save integration settings.');
782
+ showToast('error', INTEGRATIONS_SAVE_FAILED);
565
783
  } finally {
566
784
  integrationsSaving = false;
567
785
  }
@@ -576,9 +794,9 @@
576
794
  if (result.error) throw new Error(result.error);
577
795
  if (backupConfig.backupS3SecretKey) backupS3SecretKeySet = true;
578
796
  backupConfig = { ...backupConfig, backupS3SecretKey: '' };
579
- showToast('success', 'Backup configuration saved.');
797
+ showToast('success', BACKUP_CONFIG_SAVED_TOAST);
580
798
  } catch (e) {
581
- showToast('error', e.message || 'Failed to save backup configuration.');
799
+ showToast('error', e.message || BACKUP_CONFIG_SAVE_FAILED);
582
800
  } finally {
583
801
  backupConfigSaving = false;
584
802
  }
@@ -592,10 +810,10 @@
592
810
  const result = await testBackupS3(backupConfig);
593
811
  if (result.error) throw new Error(result.error);
594
812
  backupS3TestResult = 'success';
595
- backupS3TestMessage = 'Connection successful.';
813
+ backupS3TestMessage = S3_CONNECTION_SUCCESS;
596
814
  } catch (e) {
597
815
  backupS3TestResult = 'error';
598
- backupS3TestMessage = e.message || 'Connection failed.';
816
+ backupS3TestMessage = e.message || S3_CONNECTION_FAILED;
599
817
  } finally {
600
818
  backupTestingS3 = false;
601
819
  }
@@ -608,9 +826,9 @@
608
826
  if (result.error) throw new Error(result.error);
609
827
  backupLastRunAt = result.lastRunAt;
610
828
  backupLastStatus = result.lastStatus ?? '';
611
- showToast('success', 'Backup uploaded to S3 successfully.');
829
+ showToast('success', BACKUP_UPLOAD_SUCCESS_TOAST);
612
830
  } catch (e) {
613
- showToast('error', e.message || 'Backup failed. Check your S3 configuration.');
831
+ showToast('error', e.message || BACKUP_UPLOAD_FAILED_FALLBACK);
614
832
  const bc = await fetchBackupConfig().catch(() => null);
615
833
  if (bc) {
616
834
  backupLastRunAt = bc.backupLastRunAt;
@@ -634,7 +852,7 @@
634
852
  command: 'plum',
635
853
  args: ['mcp'],
636
854
  env: {
637
- PLUM_API_URL: 'http://localhost:3001',
855
+ PLUM_API_URL: API_BASE,
638
856
  PLUM_API_KEY: mcpKey
639
857
  }
640
858
  }
@@ -670,24 +888,24 @@
670
888
  $: navItems =
671
889
  $auth.user?.role === 'admin'
672
890
  ? [
673
- { id: 'project', label: 'Project' },
674
- { id: 'runners', label: 'Runners' },
675
- { id: 'repository', label: 'Repository' },
676
- { id: 'integrations', label: 'Integrations' },
677
- { id: 'mcp', label: 'MCP' },
678
- { id: 'account', label: 'Account' },
679
- { id: 'users', label: 'Users' },
680
- { id: 'backup', label: 'Backup' }
891
+ { id: 'project', label: PROJECT_LABEL },
892
+ { id: 'runners', label: RUNNERS_LABEL },
893
+ { id: 'repository', label: REPOSITORY_NAV_LABEL },
894
+ { id: 'integrations', label: INTEGRATIONS_LABEL },
895
+ { id: 'mcp', label: MCP_NAV_LABEL },
896
+ { id: 'account', label: ACCOUNT_LABEL },
897
+ { id: 'users', label: USERS_LABEL },
898
+ { id: 'backup', label: BACKUP_LABEL }
681
899
  ]
682
- : [{ id: 'account', label: 'Account' }];
900
+ : [{ id: 'account', label: ACCOUNT_LABEL }];
683
901
  </script>
684
902
 
685
- <svelte:head><title>Settings — Plum</title></svelte:head>
903
+ <svelte:head><title>{PAGE_TITLE}</title></svelte:head>
686
904
 
687
905
  <Toast {toast} />
688
906
 
689
907
  <div class="page-header">
690
- <h1>Settings</h1>
908
+ <h1>{HEADING}</h1>
691
909
  </div>
692
910
 
693
911
  <div class="settings-layout">
@@ -712,42 +930,42 @@
712
930
  {#if section === 'project'}
713
931
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
714
932
  <div class="content-header">
715
- <h2>Project</h2>
716
- <p class="content-desc">Identity information shown across the UI</p>
933
+ <h2>{PROJECT_LABEL}</h2>
934
+ <p class="content-desc">{PROJECT_DESC}</p>
717
935
  </div>
718
936
 
719
937
  <div class="card settings-card">
720
938
  <div class="field">
721
- <label class="field-label" for="project-name">Project Name</label>
939
+ <label class="field-label" for="project-name">{PROJECT_NAME_LABEL}</label>
722
940
  <input
723
941
  id="project-name"
724
942
  type="text"
725
943
  class="field-input"
726
944
  bind:value={project.name}
727
- placeholder="My Test Suite"
945
+ placeholder={PROJECT_NAME_PLACEHOLDER}
728
946
  />
729
947
  </div>
730
948
 
731
949
  <div class="field">
732
950
  <label class="field-label" for="project-logo">
733
- <span>Logo URL</span>
734
- <span class="field-hint">Direct link to an image (PNG, SVG, JPG)</span>
951
+ <span>{LOGO_URL_LABEL}</span>
952
+ <span class="field-hint">{LOGO_URL_HINT}</span>
735
953
  </label>
736
954
  <input
737
955
  id="project-logo"
738
956
  type="url"
739
957
  class="field-input"
740
958
  bind:value={project.logoUrl}
741
- placeholder="https://example.com/logo.png"
959
+ placeholder={LOGO_URL_PLACEHOLDER}
742
960
  />
743
961
  </div>
744
962
 
745
963
  {#if project.logoUrl}
746
964
  <div class="logo-preview">
747
- <span class="preview-label">Preview</span>
965
+ <span class="preview-label">{PREVIEW_LABEL}</span>
748
966
  <img
749
967
  src={project.logoUrl}
750
- alt="Project logo preview"
968
+ alt={LOGO_PREVIEW_ALT}
751
969
  class="logo-img"
752
970
  on:error={(e) => (e.target.style.display = 'none')}
753
971
  />
@@ -756,8 +974,8 @@
756
974
 
757
975
  <div class="field">
758
976
  <label class="field-label" for="project-timezone">
759
- <span>Timezone</span>
760
- <span class="field-hint">Used to schedule cron jobs and backups</span>
977
+ <span>{TIMEZONE_LABEL}</span>
978
+ <span class="field-hint">{TIMEZONE_HINT}</span>
761
979
  </label>
762
980
  <select id="project-timezone" class="field-input" bind:value={project.timezone}>
763
981
  {#each TIMEZONES as tz}
@@ -768,10 +986,9 @@
768
986
 
769
987
  <div class="field">
770
988
  <label class="field-label" for="project-max-retries">
771
- <span>Retry failed tests</span>
989
+ <span>{RETRY_FAILED_TESTS_LABEL}</span>
772
990
  <span class="field-hint">
773
- Automatically re-run failed scenarios up to this many times before finalizing the
774
- report. 0 disables retries.
991
+ {RETRY_FAILED_TESTS_HINT}
775
992
  </span>
776
993
  </label>
777
994
  <input
@@ -787,8 +1004,8 @@
787
1004
  <!-- Dark mode toggle -->
788
1005
  <div class="toggle-row">
789
1006
  <div class="toggle-info">
790
- <span class="toggle-label">Dark mode</span>
791
- <span class="toggle-desc">Switch between light and dark appearance</span>
1007
+ <span class="toggle-label">{DARK_MODE_LABEL}</span>
1008
+ <span class="toggle-desc">{DARK_MODE_DESC}</span>
792
1009
  </div>
793
1010
  <button
794
1011
  class="toggle-switch"
@@ -803,7 +1020,7 @@
803
1020
 
804
1021
  <div class="card-footer">
805
1022
  <Button on:click={handleSaveProject} disabled={projectSaving}>
806
- {projectSaving ? 'Saving…' : 'Save Project'}
1023
+ {saveProjectLabel(projectSaving)}
807
1024
  </Button>
808
1025
  </div>
809
1026
  </div>
@@ -813,9 +1030,9 @@
813
1030
  {:else if section === 'runners'}
814
1031
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
815
1032
  <div class="content-header">
816
- <h2>Runners</h2>
1033
+ <h2>{RUNNERS_LABEL}</h2>
817
1034
  <p class="content-desc">
818
- Register self-hosted runner nodes to distribute tests across machines.
1035
+ {RUNNERS_DESC}
819
1036
  </p>
820
1037
  </div>
821
1038
 
@@ -823,9 +1040,9 @@
823
1040
  <!-- Built-in runner toggle -->
824
1041
  <div class="toggle-row">
825
1042
  <div class="toggle-info">
826
- <span class="toggle-label">Built-in runner</span>
1043
+ <span class="toggle-label">{BUILTIN_RUNNER_TOGGLE_LABEL}</span>
827
1044
  <span class="toggle-desc">
828
- Use this server to run tests locally. Disable to route all runs to external nodes.
1045
+ {BUILTIN_RUNNER_TOGGLE_DESC}
829
1046
  </span>
830
1047
  </div>
831
1048
  <button
@@ -848,20 +1065,21 @@
848
1065
  <div class="runner-card editing" transition:fly={{ y: -4, duration: 180 }}>
849
1066
  <div class="runner-form-fields">
850
1067
  <div class="field">
851
- <label class="field-label" for="edit-name-{r.id}">Name</label>
1068
+ <label class="field-label" for="edit-name-{r.id}">{NAME_LABEL}</label>
852
1069
  <input
853
1070
  id="edit-name-{r.id}"
854
1071
  type="text"
855
1072
  class="field-input"
856
1073
  bind:value={editForm.name}
857
- placeholder="staging-node"
1074
+ placeholder={RUNNER_NAME_PLACEHOLDER}
858
1075
  />
859
1076
  </div>
860
1077
  <div class="field">
861
1078
  <label class="field-label" for="edit-url-{r.id}">
862
- URL
1079
+ {RUNNER_URL_LABEL}
863
1080
  <span class="field-hint"
864
- >Use <code>host.docker.internal</code> for local nodes</span
1081
+ >{RUNNER_URL_HINT_PREFIX} <code>host.docker.internal</code>
1082
+ {RUNNER_URL_HINT_SUFFIX}</span
865
1083
  >
866
1084
  </label>
867
1085
  <input
@@ -869,23 +1087,23 @@
869
1087
  type="url"
870
1088
  class="field-input"
871
1089
  bind:value={editForm.url}
872
- placeholder="http://host.docker.internal:3002"
1090
+ placeholder={RUNNER_URL_PLACEHOLDER}
873
1091
  />
874
1092
  </div>
875
1093
  <div class="field">
876
- <label class="field-label" for="edit-token-{r.id}">Token</label>
1094
+ <label class="field-label" for="edit-token-{r.id}">{TOKEN_LABEL}</label>
877
1095
  <input
878
1096
  id="edit-token-{r.id}"
879
1097
  type="text"
880
1098
  class="field-input"
881
1099
  bind:value={editForm.token}
882
- placeholder="secret-token"
1100
+ placeholder={KEEP_TOKEN_PLACEHOLDER}
883
1101
  spellcheck="false"
884
1102
  autocomplete="off"
885
1103
  />
886
1104
  </div>
887
1105
  <div class="field">
888
- <label class="field-label" for="edit-browser-{r.id}">Browser</label>
1106
+ <label class="field-label" for="edit-browser-{r.id}">{BROWSER_LABEL}</label>
889
1107
  <select
890
1108
  id="edit-browser-{r.id}"
891
1109
  class="field-input"
@@ -902,7 +1120,7 @@
902
1120
  {/if}
903
1121
  <div class="runner-form-actions">
904
1122
  <Button on:click={() => handleUpdateRunner(r.id)} disabled={editFormSaving}>
905
- {editFormSaving ? 'Checking…' : 'Save'}
1123
+ {editRunnerSubmitLabel(editFormSaving)}
906
1124
  </Button>
907
1125
  <Button
908
1126
  variant="ghost"
@@ -910,7 +1128,7 @@
910
1128
  editingId = null;
911
1129
  editFormError = '';
912
1130
  }}
913
- disabled={editFormSaving}>Cancel</Button
1131
+ disabled={editFormSaving}>{CANCEL_LABEL}</Button
914
1132
  >
915
1133
  </div>
916
1134
  </div>
@@ -936,33 +1154,37 @@
936
1154
  {#if ping.ok}
937
1155
  <span class="ping-badge ok">{ping.latency}ms</span>
938
1156
  {:else}
939
- <span class="ping-badge fail" title={ping.error}>unreachable</span>
1157
+ <span class="ping-badge fail" title={ping.error}
1158
+ >{RUNNER_UNREACHABLE_LABEL}</span
1159
+ >
940
1160
  {/if}
941
1161
  {:else if ping?.loading}
942
- <span class="ping-badge pinging">pinging…</span>
1162
+ <span class="ping-badge pinging">{RUNNER_PINGING_LABEL}</span>
943
1163
  {/if}
944
1164
  </div>
945
1165
  <p class="runner-card-url">{r.url}</p>
946
1166
  <div class="runner-card-actions">
947
- <Button variant="ghost" size="sm" on:click={() => startEdit(r)}>Edit</Button>
1167
+ <Button variant="ghost" size="sm" on:click={() => startEdit(r)}
1168
+ >{EDIT_LABEL}</Button
1169
+ >
948
1170
  <Button
949
1171
  variant="ghost"
950
1172
  size="sm"
951
1173
  disabled={restartingId === r.id}
952
1174
  on:click={() => handleRestartRunner(r.id, r.name)}
953
- >{restartingId === r.id ? 'Restarting…' : 'Restart'}</Button
1175
+ >{restartRunnerLabel(restartingId === r.id)}</Button
954
1176
  >
955
1177
  <Button
956
1178
  variant="ghost"
957
1179
  size="sm"
958
1180
  disabled={stoppingId === r.id}
959
1181
  on:click={() => handleStopRunner(r.id, r.name)}
960
- >{stoppingId === r.id ? 'Stopping…' : 'Stop'}</Button
1182
+ >{stopRunnerLabel(stoppingId === r.id)}</Button
961
1183
  >
962
1184
  <Button
963
1185
  variant="danger"
964
1186
  size="sm"
965
- on:click={() => handleDeleteRunner(r.id, r.name)}>Remove</Button
1187
+ on:click={() => handleDeleteRunner(r.id, r.name)}>{REMOVE_LABEL}</Button
966
1188
  >
967
1189
  </div>
968
1190
  </div>
@@ -974,23 +1196,24 @@
974
1196
  <!-- Add runner form / button -->
975
1197
  {#if runnerFormOpen}
976
1198
  <div class="runner-form" transition:fly={{ y: -6, duration: 200 }}>
977
- <p class="runner-form-title">Add runner</p>
1199
+ <p class="runner-form-title">{ADD_RUNNER_FORM_TITLE}</p>
978
1200
  <div class="runner-form-fields">
979
1201
  <div class="field">
980
- <label class="field-label" for="rn-name">Name</label>
1202
+ <label class="field-label" for="rn-name">{NAME_LABEL}</label>
981
1203
  <input
982
1204
  id="rn-name"
983
1205
  type="text"
984
1206
  class="field-input"
985
1207
  bind:value={runnerForm.name}
986
- placeholder="staging-node"
1208
+ placeholder={RUNNER_NAME_PLACEHOLDER}
987
1209
  />
988
1210
  </div>
989
1211
  <div class="field">
990
1212
  <label class="field-label" for="rn-url">
991
- URL
1213
+ {RUNNER_URL_LABEL}
992
1214
  <span class="field-hint"
993
- >Use <code>host.docker.internal</code> for local nodes</span
1215
+ >{RUNNER_URL_HINT_PREFIX} <code>host.docker.internal</code>
1216
+ {RUNNER_URL_HINT_SUFFIX}</span
994
1217
  >
995
1218
  </label>
996
1219
  <input
@@ -998,23 +1221,23 @@
998
1221
  type="url"
999
1222
  class="field-input"
1000
1223
  bind:value={runnerForm.url}
1001
- placeholder="http://host.docker.internal:3002"
1224
+ placeholder={RUNNER_URL_PLACEHOLDER}
1002
1225
  />
1003
1226
  </div>
1004
1227
  <div class="field">
1005
- <label class="field-label" for="rn-token">Token</label>
1228
+ <label class="field-label" for="rn-token">{TOKEN_LABEL}</label>
1006
1229
  <input
1007
1230
  id="rn-token"
1008
1231
  type="text"
1009
1232
  class="field-input"
1010
1233
  bind:value={runnerForm.token}
1011
- placeholder="secret-token"
1234
+ placeholder={TOKEN_PLACEHOLDER}
1012
1235
  spellcheck="false"
1013
1236
  autocomplete="off"
1014
1237
  />
1015
1238
  </div>
1016
1239
  <div class="field">
1017
- <label class="field-label" for="rn-browser">Browser</label>
1240
+ <label class="field-label" for="rn-browser">{BROWSER_LABEL}</label>
1018
1241
  <select id="rn-browser" class="field-input" bind:value={runnerForm.browser}>
1019
1242
  {#each BROWSERS as b}
1020
1243
  <option value={b.id}>{b.label}</option>
@@ -1027,7 +1250,7 @@
1027
1250
  {/if}
1028
1251
  <div class="runner-form-actions">
1029
1252
  <Button on:click={handleAddRunner} disabled={runnerFormSaving}>
1030
- {runnerFormSaving ? 'Checking…' : 'Add Runner'}
1253
+ {addRunnerSubmitLabel(runnerFormSaving)}
1031
1254
  </Button>
1032
1255
  <Button
1033
1256
  variant="ghost"
@@ -1035,13 +1258,15 @@
1035
1258
  runnerFormOpen = false;
1036
1259
  runnerFormError = '';
1037
1260
  }}
1038
- disabled={runnerFormSaving}>Cancel</Button
1261
+ disabled={runnerFormSaving}>{CANCEL_LABEL}</Button
1039
1262
  >
1040
1263
  </div>
1041
1264
  </div>
1042
1265
  {:else}
1043
1266
  <div class="card-footer">
1044
- <Button variant="ghost" on:click={() => (runnerFormOpen = true)}>+ Add Runner</Button>
1267
+ <Button variant="ghost" on:click={() => (runnerFormOpen = true)}
1268
+ >{OPEN_ADD_RUNNER_LABEL}</Button
1269
+ >
1045
1270
  </div>
1046
1271
  {/if}
1047
1272
  </div>
@@ -1051,59 +1276,62 @@
1051
1276
  {:else if section === 'repository'}
1052
1277
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1053
1278
  <div class="content-header">
1054
- <h2>Test Repository</h2>
1055
- <p class="content-desc">Configure ID prefixes for test suites and cases.</p>
1279
+ <h2>{REPOSITORY_HEADING}</h2>
1280
+ <p class="content-desc">{REPOSITORY_DESC}</p>
1056
1281
  </div>
1057
1282
 
1058
1283
  <div class="card settings-card">
1059
1284
  <div class="field-row">
1060
1285
  <div class="field">
1061
- <label class="field-label" for="tc-prefix">Test Case prefix</label>
1286
+ <label class="field-label" for="tc-prefix">{CASE_PREFIX_LABEL}</label>
1062
1287
  <input
1063
1288
  id="tc-prefix"
1064
1289
  type="text"
1065
1290
  class="field-input mono"
1066
1291
  bind:value={prefixes.testCasePrefix}
1067
- placeholder="TC"
1292
+ placeholder={CASE_PREFIX_PLACEHOLDER}
1068
1293
  maxlength="10"
1069
1294
  />
1070
1295
  </div>
1071
1296
  <div class="field">
1072
- <label class="field-label" for="ts-prefix">Test Suite prefix</label>
1297
+ <label class="field-label" for="ts-prefix">{SUITE_PREFIX_LABEL}</label>
1073
1298
  <input
1074
1299
  id="ts-prefix"
1075
1300
  type="text"
1076
1301
  class="field-input mono"
1077
1302
  bind:value={prefixes.testSuitePrefix}
1078
- placeholder="TS"
1303
+ placeholder={SUITE_PREFIX_PLACEHOLDER}
1079
1304
  maxlength="10"
1080
1305
  />
1081
1306
  </div>
1082
1307
  </div>
1083
1308
  <p class="content-desc">
1084
- Examples: <code class="code-sample">{prefixes.testCasePrefix || 'TC'}-001</code>,
1085
- <code class="code-sample">{prefixes.testSuitePrefix || 'TS'}-001</code>
1309
+ {EXAMPLES_LABEL}
1310
+ <code class="code-sample">{prefixes.testCasePrefix || CASE_PREFIX_PLACEHOLDER}-001</code
1311
+ >,
1312
+ <code class="code-sample"
1313
+ >{prefixes.testSuitePrefix || SUITE_PREFIX_PLACEHOLDER}-001</code
1314
+ >
1086
1315
  </p>
1087
1316
  <div class="card-footer">
1088
1317
  <Button on:click={handleSavePrefixes} disabled={prefixesSaving}>
1089
- {prefixesSaving ? 'Saving…' : 'Save Prefixes'}
1318
+ {savePrefixesLabel(prefixesSaving)}
1090
1319
  </Button>
1091
1320
  </div>
1092
1321
  </div>
1093
1322
 
1094
1323
  <div class="content-header" style="margin-top: 1rem">
1095
- <h2>Migrate IDs</h2>
1324
+ <h2>{MIGRATE_IDS_HEADING}</h2>
1096
1325
  <p class="content-desc">
1097
- Rename all existing test IDs to use a new prefix. Cucumber tags in code are <strong
1098
- >not</strong
1099
- > affected — you manage those separately.
1326
+ {MIGRATE_DESC_PREFIX} <strong>{MIGRATE_DESC_STRONG}</strong>
1327
+ {MIGRATE_DESC_SUFFIX}
1100
1328
  </p>
1101
1329
  </div>
1102
1330
 
1103
1331
  <div class="card settings-card">
1104
1332
  <div class="field-row">
1105
1333
  <div class="field">
1106
- <label class="field-label" for="mig-tc">New case prefix</label>
1334
+ <label class="field-label" for="mig-tc">{NEW_CASE_PREFIX_LABEL}</label>
1107
1335
  <input
1108
1336
  id="mig-tc"
1109
1337
  type="text"
@@ -1114,7 +1342,7 @@
1114
1342
  />
1115
1343
  </div>
1116
1344
  <div class="field">
1117
- <label class="field-label" for="mig-ts">New suite prefix</label>
1345
+ <label class="field-label" for="mig-ts">{NEW_SUITE_PREFIX_LABEL}</label>
1118
1346
  <input
1119
1347
  id="mig-ts"
1120
1348
  type="text"
@@ -1127,7 +1355,7 @@
1127
1355
  </div>
1128
1356
  <div class="card-footer">
1129
1357
  <Button variant="ghost" on:click={handleMigratePrefixes} disabled={migrating}>
1130
- {migrating ? 'Migrating…' : 'Run Migration'}
1358
+ {runMigrationLabel(migrating)}
1131
1359
  </Button>
1132
1360
  </div>
1133
1361
  </div>
@@ -1137,82 +1365,78 @@
1137
1365
  {:else if section === 'integrations'}
1138
1366
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1139
1367
  <div class="content-header">
1140
- <h2>Integrations</h2>
1368
+ <h2>{INTEGRATIONS_LABEL}</h2>
1141
1369
  <p class="content-desc">
1142
- Connect Discord and Slack to receive run notifications with pass/fail results and report
1143
- links.
1370
+ {INTEGRATIONS_DESC}
1144
1371
  </p>
1145
1372
  </div>
1146
1373
 
1147
1374
  <div class="card settings-card">
1148
- <p class="card-title">Webhooks</p>
1375
+ <p class="card-title">{WEBHOOKS_CARD_TITLE}</p>
1149
1376
 
1150
1377
  <div class="field">
1151
1378
  <label class="field-label" for="discord-url">
1152
- <span>Discord Webhook URL</span>
1153
- <span class="field-hint">Leave blank to disable Discord notifications</span>
1379
+ <span>{DISCORD_WEBHOOK_LABEL}</span>
1380
+ <span class="field-hint">{DISCORD_WEBHOOK_HINT}</span>
1154
1381
  </label>
1155
1382
  <input
1156
1383
  id="discord-url"
1157
1384
  type="url"
1158
1385
  class="field-input"
1159
1386
  bind:value={integrations.discordWebhookUrl}
1160
- placeholder="https://discord.com/api/webhooks/…"
1387
+ placeholder={DISCORD_WEBHOOK_PLACEHOLDER}
1161
1388
  />
1162
1389
  </div>
1163
1390
 
1164
1391
  <div class="field">
1165
1392
  <label class="field-label" for="slack-url">
1166
- <span>Slack Webhook URL</span>
1167
- <span class="field-hint">Leave blank to disable Slack notifications</span>
1393
+ <span>{SLACK_WEBHOOK_LABEL}</span>
1394
+ <span class="field-hint">{SLACK_WEBHOOK_HINT}</span>
1168
1395
  </label>
1169
1396
  <input
1170
1397
  id="slack-url"
1171
1398
  type="url"
1172
1399
  class="field-input"
1173
1400
  bind:value={integrations.slackWebhookUrl}
1174
- placeholder="https://hooks.slack.com/services/…"
1401
+ placeholder={SLACK_WEBHOOK_PLACEHOLDER}
1175
1402
  />
1176
1403
  </div>
1177
1404
 
1178
1405
  <div class="field">
1179
1406
  <label class="field-label" for="public-url">
1180
- <span>Public URL</span>
1181
- <span class="field-hint"
1182
- >Base URL of this Plum instance, used to link reports in notifications</span
1183
- >
1407
+ <span>{PUBLIC_URL_LABEL}</span>
1408
+ <span class="field-hint">{PUBLIC_URL_HINT}</span>
1184
1409
  </label>
1185
1410
  <input
1186
1411
  id="public-url"
1187
1412
  type="url"
1188
1413
  class="field-input"
1189
1414
  bind:value={integrations.notifyPublicUrl}
1190
- placeholder="https://plum.yourcompany.com"
1415
+ placeholder={PUBLIC_URL_PLACEHOLDER}
1191
1416
  />
1192
1417
  </div>
1193
1418
 
1194
1419
  <Button on:click={handleSaveIntegrations} disabled={integrationsSaving}>
1195
- {integrationsSaving ? 'Saving…' : 'Save Integrations'}
1420
+ {saveIntegrationsLabel(integrationsSaving)}
1196
1421
  </Button>
1197
1422
  </div>
1198
1423
 
1199
1424
  <div class="card settings-card">
1200
- <p class="card-title">CI / External Triggers</p>
1425
+ <p class="card-title">{CI_TRIGGERS_CARD_TITLE}</p>
1201
1426
  <p class="content-desc">
1202
- Trigger a Plum run from GitHub Actions (e.g. on a pull request, against its preview
1203
- deployment) or any other external script by calling <code class="code-sample"
1204
- >POST {API_BASE}/trigger</code
1427
+ {CI_DESC_PART1} <code class="code-sample">POST {API_BASE}/trigger</code>
1428
+ {CI_DESC_PART2}
1429
+ <code class="code-sample">Authorization: ApiKey …</code>
1430
+ {CI_DESC_PART3}
1431
+ <button class="link-btn" on:click={() => setSection('mcp')}>{MCP_TAB_LINK_LABEL}</button
1205
1432
  >
1206
- with an
1207
- <code class="code-sample">Authorization: ApiKey …</code> header. Generate a key on the
1208
- <button class="link-btn" on:click={() => setSection('mcp')}>MCP tab</button>
1209
- and store it as a repo secret — never commit it directly. Runs triggered this way show up
1210
- in Reports tagged <Badge variant="external">External</Badge>.
1433
+ {CI_DESC_PART4}
1434
+ <Badge variant="external">{EXTERNAL_BADGE_LABEL}</Badge>.
1211
1435
  </p>
1212
1436
  <pre class="mcp-snippet">{ciWorkflowSnippet}</pre>
1213
1437
  <div class="card-footer">
1214
1438
  <Button variant="ghost" on:click={handleCopyCiSnippet}>
1215
- {ciSnippetCopied ? 'Copied!' : 'Copy Workflow Step'}
1439
+ {copyCiSnippetLabel(ciSnippetCopied)}
1216
1440
  </Button>
1217
1441
  </div>
1218
1442
  </div>
@@ -1222,21 +1446,20 @@
1222
1446
  {:else if section === 'mcp'}
1223
1447
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1224
1448
  <div class="content-header">
1225
- <h2>MCP Integration</h2>
1449
+ <h2>{MCP_HEADING}</h2>
1226
1450
  <p class="content-desc">
1227
- Generate an API key for any MCP-compatible AI client — Claude, Cursor, Windsurf, and
1228
- others.
1451
+ {MCP_DESC}
1229
1452
  </p>
1230
1453
  </div>
1231
1454
 
1232
1455
  <div class="card settings-card">
1233
- <p class="card-title">API Key</p>
1456
+ <p class="card-title">{API_KEY_CARD_TITLE}</p>
1234
1457
 
1235
1458
  {#if !mcpKeySet}
1236
- <p class="content-desc">No key generated yet.</p>
1459
+ <p class="content-desc">{NO_KEY_GENERATED_MESSAGE}</p>
1237
1460
  <div class="card-footer">
1238
1461
  <Button on:click={handleGenerateMcpKey} disabled={mcpGenerating}>
1239
- {mcpGenerating ? 'Generating…' : 'Generate Key'}
1462
+ {generateKeyLabel(mcpGenerating)}
1240
1463
  </Button>
1241
1464
  </div>
1242
1465
  {:else}
@@ -1251,7 +1474,7 @@
1251
1474
  />
1252
1475
  <button
1253
1476
  class="icon-btn"
1254
- title={mcpShowKey ? 'Hide key' : 'Show key'}
1477
+ title={mcpShowKey ? HIDE_KEY_TITLE : SHOW_KEY_TITLE}
1255
1478
  on:click={() => (mcpShowKey = !mcpShowKey)}
1256
1479
  >
1257
1480
  {#if mcpShowKey}
@@ -1290,7 +1513,7 @@
1290
1513
  </svg>
1291
1514
  {/if}
1292
1515
  </button>
1293
- <button class="icon-btn" title="Copy key" on:click={handleCopyMcpKey}>
1516
+ <button class="icon-btn" title={COPY_KEY_TITLE} on:click={handleCopyMcpKey}>
1294
1517
  {#if mcpKeyCopied}
1295
1518
  <svg
1296
1519
  width="14"
@@ -1322,10 +1545,10 @@
1322
1545
  {/if}
1323
1546
  </button>
1324
1547
  </div>
1325
- <p class="mcp-regen-note">Regenerating invalidates the existing key immediately.</p>
1548
+ <p class="mcp-regen-note">{MCP_REGEN_NOTE}</p>
1326
1549
  <div class="card-footer">
1327
1550
  <Button variant="ghost" on:click={handleGenerateMcpKey} disabled={mcpGenerating}>
1328
- {mcpGenerating ? 'Generating…' : 'Regenerate Key'}
1551
+ {regenerateKeyLabel(mcpGenerating)}
1329
1552
  </Button>
1330
1553
  </div>
1331
1554
  {/if}
@@ -1333,16 +1556,16 @@
1333
1556
 
1334
1557
  {#if mcpKeySet}
1335
1558
  <div class="card settings-card">
1336
- <p class="card-title">Config Snippet</p>
1559
+ <p class="card-title">{CONFIG_SNIPPET_CARD_TITLE}</p>
1337
1560
  <p class="content-desc">
1338
- Add this to your MCP client's config file (e.g.
1339
- <code class="code-sample">claude_desktop_config.json</code>, Cursor MCP settings,
1340
- etc.).
1561
+ {CONFIG_SNIPPET_DESC_PREFIX}
1562
+ <code class="code-sample">claude_desktop_config.json</code
1563
+ >{CONFIG_SNIPPET_DESC_SUFFIX}
1341
1564
  </p>
1342
1565
  <pre class="mcp-snippet">{mcpConfigSnippet}</pre>
1343
1566
  <div class="card-footer">
1344
1567
  <Button variant="ghost" on:click={handleCopyMcpSnippet}>
1345
- {mcpSnippetCopied ? 'Copied!' : 'Copy Config'}
1568
+ {copyMcpSnippetLabel(mcpSnippetCopied)}
1346
1569
  </Button>
1347
1570
  </div>
1348
1571
  </div>
@@ -1353,14 +1576,14 @@
1353
1576
  {:else if section === 'account'}
1354
1577
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1355
1578
  <div class="content-header">
1356
- <h2>Account</h2>
1357
- <p class="content-desc">Manage your profile, credentials and session.</p>
1579
+ <h2>{ACCOUNT_LABEL}</h2>
1580
+ <p class="content-desc">{ACCOUNT_DESC}</p>
1358
1581
  </div>
1359
1582
 
1360
1583
  <div class="card settings-card">
1361
- <p class="card-title">Profile</p>
1584
+ <p class="card-title">{PROFILE_CARD_TITLE}</p>
1362
1585
  <div class="field">
1363
- <label class="field-label" for="profile-name">Name</label>
1586
+ <label class="field-label" for="profile-name">{NAME_LABEL}</label>
1364
1587
  <input
1365
1588
  id="profile-name"
1366
1589
  type="text"
@@ -1369,7 +1592,7 @@
1369
1592
  />
1370
1593
  </div>
1371
1594
  <div class="field">
1372
- <label class="field-label" for="profile-email">Email</label>
1595
+ <label class="field-label" for="profile-email">{EMAIL_LABEL}</label>
1373
1596
  <input
1374
1597
  id="profile-email"
1375
1598
  type="email"
@@ -1383,15 +1606,15 @@
1383
1606
  on:click={handleUpdateProfile}
1384
1607
  disabled={profileSaving || !profileForm.name || !profileForm.email}
1385
1608
  >
1386
- {profileSaving ? 'Saving…' : 'Save Profile'}
1609
+ {saveProfileLabel(profileSaving)}
1387
1610
  </Button>
1388
1611
  </div>
1389
1612
  </div>
1390
1613
 
1391
1614
  <div class="card settings-card">
1392
- <p class="card-title">Change password</p>
1615
+ <p class="card-title">{CHANGE_PASSWORD_CARD_TITLE}</p>
1393
1616
  <div class="field">
1394
- <label class="field-label" for="pw-current">Current password</label>
1617
+ <label class="field-label" for="pw-current">{CURRENT_PASSWORD_LABEL}</label>
1395
1618
  <input
1396
1619
  id="pw-current"
1397
1620
  type="password"
@@ -1401,7 +1624,7 @@
1401
1624
  />
1402
1625
  </div>
1403
1626
  <div class="field">
1404
- <label class="field-label" for="pw-new">New password</label>
1627
+ <label class="field-label" for="pw-new">{NEW_PASSWORD_LABEL}</label>
1405
1628
  <input
1406
1629
  id="pw-new"
1407
1630
  type="password"
@@ -1411,7 +1634,7 @@
1411
1634
  />
1412
1635
  </div>
1413
1636
  <div class="field">
1414
- <label class="field-label" for="pw-confirm">Confirm new password</label>
1637
+ <label class="field-label" for="pw-confirm">{CONFIRM_NEW_PASSWORD_LABEL}</label>
1415
1638
  <input
1416
1639
  id="pw-confirm"
1417
1640
  type="password"
@@ -1426,14 +1649,14 @@
1426
1649
  on:click={handleChangePassword}
1427
1650
  disabled={pwSaving || !pwForm.currentPassword || !pwForm.newPassword}
1428
1651
  >
1429
- {pwSaving ? 'Saving…' : 'Change Password'}
1652
+ {changePasswordLabel(pwSaving)}
1430
1653
  </Button>
1431
1654
  </div>
1432
1655
  </div>
1433
1656
 
1434
1657
  <div class="card settings-card">
1435
1658
  <div class="card-footer">
1436
- <Button variant="danger" size="sm" on:click={handleLogout}>Sign out</Button>
1659
+ <Button variant="danger" size="sm" on:click={handleLogout}>{SIGN_OUT_LABEL}</Button>
1437
1660
  </div>
1438
1661
  </div>
1439
1662
  </div>
@@ -1442,48 +1665,49 @@
1442
1665
  {:else if section === 'users'}
1443
1666
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1444
1667
  <div class="content-header">
1445
- <h2>Users</h2>
1446
- <p class="content-desc">Add and manage who can access Plum.</p>
1668
+ <h2>{USERS_LABEL}</h2>
1669
+ <p class="content-desc">{USERS_DESC}</p>
1447
1670
  </div>
1448
1671
 
1449
1672
  <ConfirmModal
1450
1673
  bind:open={confirmDeleteUserOpen}
1451
- title="Remove User"
1452
- confirmLabel="Remove"
1674
+ title={REMOVE_USER_MODAL_TITLE}
1675
+ confirmLabel={REMOVE_USER_LABEL}
1453
1676
  on:confirm={() => handleDeleteUser(confirmDeleteUser?.id, confirmDeleteUser?.name)}
1454
1677
  >
1455
1678
  {#if confirmDeleteUser}
1456
- Remove <strong>{confirmDeleteUser.name}</strong>? They will lose access immediately.
1679
+ {REMOVE_USER_BODY_PREFIX}
1680
+ <strong>{confirmDeleteUser.name}</strong>{REMOVE_USER_BODY_SUFFIX}
1457
1681
  {/if}
1458
1682
  </ConfirmModal>
1459
1683
 
1460
1684
  <div class="card settings-card">
1461
- <p class="card-title">Add User</p>
1685
+ <p class="card-title">{ADD_USER_CARD_TITLE}</p>
1462
1686
  <div class="field-row">
1463
1687
  <div class="field">
1464
- <label class="field-label" for="u-name">Name</label>
1688
+ <label class="field-label" for="u-name">{NAME_LABEL}</label>
1465
1689
  <input
1466
1690
  id="u-name"
1467
1691
  type="text"
1468
1692
  class="field-input"
1469
1693
  bind:value={userForm.name}
1470
- placeholder="Jane Smith"
1694
+ placeholder={USER_NAME_PLACEHOLDER}
1471
1695
  />
1472
1696
  </div>
1473
1697
  <div class="field">
1474
- <label class="field-label" for="u-email">Email</label>
1698
+ <label class="field-label" for="u-email">{EMAIL_LABEL}</label>
1475
1699
  <input
1476
1700
  id="u-email"
1477
1701
  type="email"
1478
1702
  class="field-input"
1479
1703
  bind:value={userForm.email}
1480
- placeholder="jane@example.com"
1704
+ placeholder={USER_EMAIL_PLACEHOLDER}
1481
1705
  />
1482
1706
  </div>
1483
1707
  </div>
1484
1708
  <div class="field-row">
1485
1709
  <div class="field">
1486
- <label class="field-label" for="u-pw">Password</label>
1710
+ <label class="field-label" for="u-pw">{PASSWORD_LABEL}</label>
1487
1711
  <input
1488
1712
  id="u-pw"
1489
1713
  type="password"
@@ -1493,17 +1717,17 @@
1493
1717
  />
1494
1718
  </div>
1495
1719
  <div class="field">
1496
- <label class="field-label" for="u-role">Role</label>
1720
+ <label class="field-label" for="u-role">{ROLE_LABEL}</label>
1497
1721
  <select id="u-role" class="field-input" bind:value={userForm.role}>
1498
- <option value="user">User</option>
1499
- <option value="admin">Admin</option>
1722
+ <option value="user">{USER_ROLE_OPTION}</option>
1723
+ <option value="admin">{ADMIN_ROLE_OPTION}</option>
1500
1724
  </select>
1501
1725
  </div>
1502
1726
  </div>
1503
1727
  {#if userFormError}<p class="form-error">{userFormError}</p>{/if}
1504
1728
  <div class="card-footer">
1505
1729
  <Button on:click={handleCreateUser} disabled={userFormSaving}>
1506
- {userFormSaving ? 'Adding…' : 'Add User'}
1730
+ {addUserLabel(userFormSaving)}
1507
1731
  </Button>
1508
1732
  </div>
1509
1733
  </div>
@@ -1520,7 +1744,7 @@
1520
1744
  {#if u.id !== $auth.user?.id}
1521
1745
  <button
1522
1746
  class="icon-btn danger"
1523
- title="Remove user"
1747
+ title={REMOVE_USER_ICON_TITLE}
1524
1748
  on:click={() => {
1525
1749
  confirmDeleteUser = { id: u.id, name: u.name };
1526
1750
  confirmDeleteUserOpen = true;
@@ -1542,7 +1766,7 @@
1542
1766
  </svg>
1543
1767
  </button>
1544
1768
  {:else}
1545
- <span class="you-chip">you</span>
1769
+ <span class="you-chip">{YOU_CHIP_LABEL}</span>
1546
1770
  {/if}
1547
1771
  </div>
1548
1772
  {/each}
@@ -1554,35 +1778,33 @@
1554
1778
  {:else if section === 'backup'}
1555
1779
  <div class="content-section" transition:fly={{ y: 6, duration: 180 }}>
1556
1780
  <div class="content-header">
1557
- <h2>Backup</h2>
1781
+ <h2>{BACKUP_LABEL}</h2>
1558
1782
  <p class="content-desc">
1559
- Export your test cases, schedules, users, and project settings. Automate uploads to any
1560
- S3-compatible storage — Cloudflare R2, Backblaze B2, AWS S3, or MinIO.
1783
+ {BACKUP_DESC}
1561
1784
  </p>
1562
1785
  </div>
1563
1786
 
1564
1787
  <!-- Manual export / import -->
1565
1788
  <div class="card settings-card">
1566
- <p class="card-title">Manual Backup</p>
1789
+ <p class="card-title">{MANUAL_BACKUP_CARD_TITLE}</p>
1567
1790
  <div class="backup-row">
1568
1791
  <div class="backup-block">
1569
- <p class="backup-block-title">Export</p>
1792
+ <p class="backup-block-title">{EXPORT_BLOCK_TITLE}</p>
1570
1793
  <p class="backup-block-desc">
1571
- Downloads a <code>.json</code> file with all cron jobs, test cases, test runs, users,
1572
- runners, and project settings.
1794
+ {EXPORT_BLOCK_DESC_PREFIX} <code>.json</code>
1795
+ {EXPORT_BLOCK_DESC_SUFFIX}
1573
1796
  </p>
1574
1797
  <Button on:click={handleExport} disabled={exporting}>
1575
- {exporting ? 'Exporting…' : 'Export JSON'}
1798
+ {exportLabel(exporting)}
1576
1799
  </Button>
1577
1800
  </div>
1578
1801
 
1579
1802
  <div class="backup-divider"></div>
1580
1803
 
1581
1804
  <div class="backup-block">
1582
- <p class="backup-block-title">Import</p>
1805
+ <p class="backup-block-title">{IMPORT_BLOCK_TITLE}</p>
1583
1806
  <p class="backup-block-desc">
1584
- Restores all data from a previously exported backup. Existing records are
1585
- overwritten. Cron jobs are re-scheduled after import.
1807
+ {IMPORT_BLOCK_DESC}
1586
1808
  </p>
1587
1809
  <div class="import-row">
1588
1810
  <label class="file-label">
@@ -1593,53 +1815,56 @@
1593
1815
  class="file-input-hidden"
1594
1816
  on:change={handleFileChange}
1595
1817
  />
1596
- <span class="file-btn">{importFile ? importFile.name : 'Choose file…'}</span>
1818
+ <span class="file-btn">{importFile ? importFile.name : CHOOSE_FILE_LABEL}</span>
1597
1819
  </label>
1598
1820
  <Button on:click={handleImport} disabled={!importFile || importing}>
1599
- {importing ? 'Importing…' : 'Import'}
1821
+ {importLabel(importing)}
1600
1822
  </Button>
1601
1823
  </div>
1602
1824
  </div>
1603
1825
  </div>
1604
1826
  <p class="backup-disclaimer">
1605
- Reports are not included in backups. To back up report history, run
1606
- <code>pg_dump</code> directly on the PostgreSQL volume.
1827
+ {BACKUP_DISCLAIMER_PREFIX}
1828
+ <code>pg_dump</code>
1829
+ {BACKUP_DISCLAIMER_SUFFIX}
1607
1830
  </p>
1608
1831
  </div>
1609
1832
 
1610
1833
  <!-- S3 cloud backup -->
1611
1834
  <div class="card settings-card">
1612
- <p class="card-title">S3 Storage</p>
1835
+ <p class="card-title">{S3_STORAGE_CARD_TITLE}</p>
1613
1836
  <p class="backup-block-desc" style="margin-bottom: 1.25rem;">
1614
- Works with any S3-compatible provider — Cloudflare R2, Backblaze B2, AWS S3, or MinIO.
1615
- Leave <strong>Endpoint URL</strong> empty for AWS S3.
1837
+ {S3_STORAGE_DESC_PREFIX} <strong>{ENDPOINT_URL_LABEL}</strong>
1838
+ {S3_STORAGE_DESC_SUFFIX}
1616
1839
  </p>
1617
1840
 
1618
1841
  <div class="field-row">
1619
1842
  <div class="field">
1620
1843
  <label class="field-label" for="s3-endpoint">
1621
- <span>Endpoint URL</span>
1622
- <span class="field-hint">Leave blank for AWS S3</span>
1844
+ <span>{ENDPOINT_URL_LABEL}</span>
1845
+ <span class="field-hint">{ENDPOINT_URL_HINT}</span>
1623
1846
  </label>
1624
1847
  <input
1625
1848
  id="s3-endpoint"
1626
1849
  type="url"
1627
1850
  class="field-input"
1628
1851
  bind:value={backupConfig.backupS3Endpoint}
1629
- placeholder="https://account.r2.cloudflarestorage.com"
1852
+ placeholder={ENDPOINT_URL_PLACEHOLDER}
1630
1853
  />
1631
1854
  </div>
1632
1855
  <div class="field">
1633
1856
  <label class="field-label" for="s3-region">
1634
- <span>Region</span>
1635
- <span class="field-hint">Use <code>auto</code> for Cloudflare R2</span>
1857
+ <span>{REGION_LABEL}</span>
1858
+ <span class="field-hint"
1859
+ >{REGION_HINT_PREFIX} <code>auto</code> {REGION_HINT_SUFFIX}</span
1860
+ >
1636
1861
  </label>
1637
1862
  <input
1638
1863
  id="s3-region"
1639
1864
  type="text"
1640
1865
  class="field-input"
1641
1866
  bind:value={backupConfig.backupS3Region}
1642
- placeholder="us-east-1"
1867
+ placeholder={REGION_PLACEHOLDER}
1643
1868
  />
1644
1869
  </div>
1645
1870
  </div>
@@ -1647,27 +1872,27 @@
1647
1872
  <div class="field-row">
1648
1873
  <div class="field">
1649
1874
  <label class="field-label" for="s3-bucket">
1650
- <span>Bucket</span>
1875
+ <span>{BUCKET_LABEL}</span>
1651
1876
  </label>
1652
1877
  <input
1653
1878
  id="s3-bucket"
1654
1879
  type="text"
1655
1880
  class="field-input"
1656
1881
  bind:value={backupConfig.backupS3Bucket}
1657
- placeholder="my-plum-backups"
1882
+ placeholder={BUCKET_PLACEHOLDER}
1658
1883
  />
1659
1884
  </div>
1660
1885
  <div class="field">
1661
1886
  <label class="field-label" for="s3-prefix">
1662
- <span>Path Prefix</span>
1663
- <span class="field-hint">Optional folder inside the bucket</span>
1887
+ <span>{PATH_PREFIX_LABEL}</span>
1888
+ <span class="field-hint">{PATH_PREFIX_HINT}</span>
1664
1889
  </label>
1665
1890
  <input
1666
1891
  id="s3-prefix"
1667
1892
  type="text"
1668
1893
  class="field-input"
1669
1894
  bind:value={backupConfig.backupS3Prefix}
1670
- placeholder="plum/"
1895
+ placeholder={PATH_PREFIX_PLACEHOLDER}
1671
1896
  />
1672
1897
  </div>
1673
1898
  </div>
@@ -1675,32 +1900,28 @@
1675
1900
  <div class="field-row">
1676
1901
  <div class="field">
1677
1902
  <label class="field-label" for="s3-access-key">
1678
- <span>Access Key ID</span>
1903
+ <span>{ACCESS_KEY_LABEL}</span>
1679
1904
  </label>
1680
1905
  <input
1681
1906
  id="s3-access-key"
1682
1907
  type="text"
1683
1908
  class="field-input"
1684
1909
  bind:value={backupConfig.backupS3AccessKey}
1685
- placeholder="AKIAIOSFODNN7EXAMPLE"
1910
+ placeholder={ACCESS_KEY_PLACEHOLDER}
1686
1911
  autocomplete="off"
1687
1912
  />
1688
1913
  </div>
1689
1914
  <div class="field">
1690
1915
  <label class="field-label" for="s3-secret-key">
1691
- <span>Secret Access Key</span>
1692
- <span class="field-hint"
1693
- >{backupS3SecretKeySet
1694
- ? 'A key is already saved — leave blank to keep it'
1695
- : 'Required'}</span
1696
- >
1916
+ <span>{SECRET_KEY_LABEL}</span>
1917
+ <span class="field-hint">{secretKeyHint(backupS3SecretKeySet)}</span>
1697
1918
  </label>
1698
1919
  <input
1699
1920
  id="s3-secret-key"
1700
1921
  type="password"
1701
1922
  class="field-input"
1702
1923
  bind:value={backupConfig.backupS3SecretKey}
1703
- placeholder={backupS3SecretKeySet ? '••••••••' : 'Enter secret key'}
1924
+ placeholder={secretKeyPlaceholder(backupS3SecretKeySet)}
1704
1925
  autocomplete="new-password"
1705
1926
  />
1706
1927
  </div>
@@ -1708,10 +1929,10 @@
1708
1929
 
1709
1930
  <div class="backup-actions">
1710
1931
  <Button variant="ghost" on:click={handleTestS3} disabled={backupTestingS3}>
1711
- {backupTestingS3 ? 'Testing…' : 'Test Connection'}
1932
+ {testConnectionLabel(backupTestingS3)}
1712
1933
  </Button>
1713
1934
  <Button on:click={handleSaveBackupConfig} disabled={backupConfigSaving}>
1714
- {backupConfigSaving ? 'Saving…' : 'Save S3 Config'}
1935
+ {saveS3ConfigLabel(backupConfigSaving)}
1715
1936
  </Button>
1716
1937
  </div>
1717
1938
 
@@ -1729,14 +1950,14 @@
1729
1950
 
1730
1951
  <!-- Scheduled backup -->
1731
1952
  <div class="card settings-card" class:card-disabled={!s3Configured}>
1732
- <p class="card-title">Scheduled Backup</p>
1953
+ <p class="card-title">{SCHEDULED_BACKUP_CARD_TITLE}</p>
1733
1954
 
1734
1955
  {#if !s3Configured}
1735
- <p class="backup-block-desc">Configure S3 storage above to enable scheduled backups.</p>
1956
+ <p class="backup-block-desc">{CONFIGURE_S3_FIRST_MESSAGE}</p>
1736
1957
  {:else}
1737
1958
  <div class="field">
1738
1959
  <label class="field-label backup-toggle-label" for="backup-enabled">
1739
- <span>Enable scheduled backup</span>
1960
+ <span>{ENABLE_SCHEDULED_BACKUP_LABEL}</span>
1740
1961
  <button
1741
1962
  id="backup-enabled"
1742
1963
  class="toggle-btn"
@@ -1752,11 +1973,12 @@
1752
1973
 
1753
1974
  <div class="field">
1754
1975
  <label class="field-label" for="backup-cron">
1755
- <span>Cron Expression</span>
1976
+ <span>{CRON_EXPRESSION_LABEL}</span>
1756
1977
  <span class="field-hint">
1757
- 5-field cron — e.g. <code>0 2 * * *</code> = daily at 2 AM.
1978
+ {CRON_HINT_PREFIX} <code>0 2 * * *</code>
1979
+ {CRON_HINT_SUFFIX}
1758
1980
  <a href="https://crontab.guru" target="_blank" rel="noopener noreferrer"
1759
- >Test at crontab.guru ↗</a
1981
+ >{CRONTAB_LINK_LABEL}</a
1760
1982
  >
1761
1983
  </span>
1762
1984
  </label>
@@ -1765,16 +1987,17 @@
1765
1987
  type="text"
1766
1988
  class="field-input field-input-mono"
1767
1989
  bind:value={backupConfig.backupCron}
1768
- placeholder="0 2 * * *"
1990
+ placeholder={CRON_PLACEHOLDER}
1769
1991
  />
1770
1992
  </div>
1771
1993
 
1772
1994
  {#if backupLastRunAt}
1773
1995
  <p class="backup-last-run">
1774
- Last backup: {new Date(backupLastRunAt).toLocaleString()}
1996
+ {BACKUP_LAST_RUN_PREFIX}
1997
+ {new Date(backupLastRunAt).toLocaleString()} —
1775
1998
  {#if backupLastStatus?.startsWith('success:')}
1776
1999
  <span class="status-success"
1777
- >uploaded to {backupLastStatus.replace('success:', '')}</span
2000
+ >{uploadedToLabel(backupLastStatus.replace('success:', ''))}</span
1778
2001
  >
1779
2002
  {:else if backupLastStatus?.startsWith('error:')}
1780
2003
  <span class="status-error">{backupLastStatus.replace('error:', '')}</span>
@@ -1786,10 +2009,10 @@
1786
2009
 
1787
2010
  <div class="backup-actions">
1788
2011
  <Button variant="ghost" on:click={handleRunBackupNow} disabled={backupRunningNow}>
1789
- {backupRunningNow ? 'Uploading…' : 'Upload to S3 Now'}
2012
+ {uploadS3NowLabel(backupRunningNow)}
1790
2013
  </Button>
1791
2014
  <Button on:click={handleSaveBackupConfig} disabled={backupConfigSaving}>
1792
- {backupConfigSaving ? 'Saving…' : 'Save Schedule'}
2015
+ {saveScheduleLabel(backupConfigSaving)}
1793
2016
  </Button>
1794
2017
  </div>
1795
2018
  {/if}