plum-e2e 2.4.13 → 2.5.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 (55) hide show
  1. package/CLAUDE.md +1 -1
  2. package/backend/_scaffold/utils/browser.ts +47 -2
  3. package/backend/_scaffold/utils/hooks.ts +25 -2
  4. package/backend/config/scripts/run-tests.js +35 -13
  5. package/backend/logs/runner-cmr8o2x3n0000mg01410tpuck.log +20 -0
  6. package/backend/logs/runner-cmr90hvcx0000n2016edtmrtv.log +43 -0
  7. package/backend/logs/runner-cmr90i7h20001n201g2wg6v3n.log +43 -0
  8. package/backend/logs/runner-cmr90iilm0002n201mmisva95.log +43 -0
  9. package/backend/logs/runner-cmr90j70r0003n201xz8525dz.log +20 -0
  10. package/backend/logs/runner-cmr90yup70000qp017ianc2gp.log +43 -0
  11. package/backend/logs/runner-cmr90zbtm0001qp01u9tfdkh1.log +20 -0
  12. package/backend/logs/runner-cmr91a3mw0000tb01414rd1df.log +20 -0
  13. package/backend/logs/runner-cmr91dhla0001tb01jp7thjnl.log +20 -0
  14. package/backend/prisma/migrations/20260706000000_add_report_logs/migration.sql +2 -0
  15. package/backend/prisma/migrations/20260707032554_add_perf_indexes/migration.sql +44 -0
  16. package/backend/prisma/schema.prisma +20 -0
  17. package/backend/routes/node.routes.js +42 -4
  18. package/backend/routes/reports.routes.js +4 -2
  19. package/backend/services/reportService.js +58 -20
  20. package/backend/services/runnerService.js +11 -1
  21. package/backend/websockets/socketHandler.js +162 -16
  22. package/bin/plum.js +96 -68
  23. package/frontend/package.json +1 -1
  24. package/frontend/src/lib/api/reports.js +16 -5
  25. package/frontend/src/lib/components/layout/RunnerPanel.svelte +53 -55
  26. package/frontend/src/lib/components/ui/AutomatedBadge.svelte +56 -0
  27. package/frontend/src/lib/components/ui/BackLink.svelte +77 -0
  28. package/frontend/src/lib/components/ui/Badge.svelte +1 -1
  29. package/frontend/src/lib/components/ui/Button.svelte +1 -1
  30. package/frontend/src/lib/components/ui/CaseIdChip.svelte +59 -0
  31. package/frontend/src/lib/components/ui/ConfirmModal.svelte +1 -1
  32. package/frontend/src/lib/components/ui/Pagination.svelte +1 -1
  33. package/frontend/src/lib/components/ui/PriorityBadge.svelte +83 -0
  34. package/frontend/src/lib/components/ui/ResultChip.svelte +92 -0
  35. package/frontend/src/lib/components/ui/StatusDot.svelte +64 -0
  36. package/frontend/src/lib/components/ui/StepKeyword.svelte +79 -0
  37. package/frontend/src/lib/components/ui/StepStatusIcon.svelte +66 -0
  38. package/frontend/src/lib/components/ui/TagChip.svelte +51 -0
  39. package/frontend/src/lib/constants.js +14 -0
  40. package/frontend/src/lib/stores/runner.js +5 -3
  41. package/frontend/src/lib/styles/tokens.css +26 -0
  42. package/frontend/src/lib/utils/format.js +74 -0
  43. package/frontend/src/routes/+page.svelte +3 -3
  44. package/frontend/src/routes/login/+page.svelte +55 -40
  45. package/frontend/src/routes/reports/+page.svelte +35 -27
  46. package/frontend/src/routes/reports/[id]/+page.svelte +703 -215
  47. package/frontend/src/routes/reports/live/+page.svelte +281 -283
  48. package/frontend/src/routes/scheduled-tests/+page.svelte +3 -3
  49. package/frontend/src/routes/settings/+page.svelte +6 -6
  50. package/frontend/src/routes/setup/+page.svelte +2 -1
  51. package/frontend/src/routes/test-repository/+page.svelte +5 -5
  52. package/frontend/src/routes/test-repository/runs/[id]/+page.svelte +33 -152
  53. package/frontend/src/routes/test-repository/suites/[id]/+page.svelte +4 -4
  54. package/frontend/vite.config.js +7 -1
  55. package/package.json +1 -1
@@ -630,7 +630,7 @@
630
630
  width: 14px;
631
631
  height: 14px;
632
632
  border-radius: 50%;
633
- background: #fff;
633
+ background: var(--white);
634
634
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
635
635
  transition: left var(--duration-fast);
636
636
  }
@@ -666,7 +666,7 @@
666
666
 
667
667
  .run-icon-btn:hover {
668
668
  background: var(--accent);
669
- color: #fff;
669
+ color: var(--white);
670
670
  border-color: var(--accent);
671
671
  }
672
672
 
@@ -803,7 +803,7 @@
803
803
  }
804
804
  .seg-btn.active {
805
805
  background: var(--accent);
806
- color: #fff;
806
+ color: var(--white);
807
807
  }
808
808
  .seg-btn.active .seg-label {
809
809
  opacity: 0.85;
@@ -1828,7 +1828,7 @@
1828
1828
  flex-shrink: 0;
1829
1829
  width: 40px;
1830
1830
  height: 22px;
1831
- border-radius: 100px;
1831
+ border-radius: var(--radius-pill);
1832
1832
  border: none;
1833
1833
  background: var(--border);
1834
1834
  cursor: pointer;
@@ -1908,7 +1908,7 @@
1908
1908
  color: var(--text-muted);
1909
1909
  background: var(--bg-subtle);
1910
1910
  border: 1px solid var(--border);
1911
- border-radius: 100px;
1911
+ border-radius: var(--radius-pill);
1912
1912
  padding: 0.1rem 0.45rem;
1913
1913
  flex-shrink: 0;
1914
1914
  }
@@ -1935,7 +1935,7 @@
1935
1935
  font-size: 0.7rem;
1936
1936
  font-weight: 500;
1937
1937
  padding: 0.15rem 0.5rem;
1938
- border-radius: 100px;
1938
+ border-radius: var(--radius-pill);
1939
1939
  flex-shrink: 0;
1940
1940
  }
1941
1941
 
@@ -2084,7 +2084,7 @@
2084
2084
  flex-shrink: 0;
2085
2085
  width: 40px;
2086
2086
  height: 22px;
2087
- border-radius: 100px;
2087
+ border-radius: var(--radius-pill);
2088
2088
  border: none;
2089
2089
  background: var(--border);
2090
2090
  cursor: pointer;
@@ -2274,7 +2274,7 @@
2274
2274
  .role-chip {
2275
2275
  font-size: 0.7rem;
2276
2276
  font-weight: 500;
2277
- border-radius: 100px;
2277
+ border-radius: var(--radius-pill);
2278
2278
  padding: 0.15rem 0.55rem;
2279
2279
  flex-shrink: 0;
2280
2280
  }
@@ -2295,7 +2295,7 @@
2295
2295
  color: var(--text-muted);
2296
2296
  background: var(--bg-subtle);
2297
2297
  border: 1px solid var(--border);
2298
- border-radius: 100px;
2298
+ border-radius: var(--radius-pill);
2299
2299
  padding: 0.15rem 0.55rem;
2300
2300
  flex-shrink: 0;
2301
2301
  }
@@ -17,6 +17,7 @@
17
17
 
18
18
  <script>
19
19
  import { onMount } from 'svelte';
20
+ import { goto } from '$app/navigation';
20
21
  import { setup, checkNeedsSetup } from '$lib/api/auth';
21
22
  import { auth } from '$lib/stores/auth';
22
23
  import { login as apiLogin } from '$lib/api/auth';
@@ -228,7 +229,7 @@
228
229
  .submit-btn {
229
230
  height: 40px;
230
231
  background: var(--accent);
231
- color: #fff;
232
+ color: var(--white);
232
233
  border: none;
233
234
  border-radius: var(--radius-sm);
234
235
  font-family: var(--font-body);
@@ -890,7 +890,7 @@
890
890
  font-size: 0.7rem;
891
891
  background: var(--bg-subtle);
892
892
  border: 1px solid var(--border);
893
- border-radius: 100px;
893
+ border-radius: var(--radius-pill);
894
894
  padding: 0.05rem 0.4rem;
895
895
  color: var(--text-muted);
896
896
  }
@@ -991,7 +991,7 @@
991
991
  color: var(--text-muted);
992
992
  background: var(--bg-elevated);
993
993
  border: 1px solid var(--border);
994
- border-radius: 100px;
994
+ border-radius: var(--radius-pill);
995
995
  cursor: pointer;
996
996
  transition:
997
997
  color var(--duration-fast),
@@ -1181,7 +1181,7 @@
1181
1181
  text-transform: uppercase;
1182
1182
  color: var(--pass);
1183
1183
  background: var(--pass-soft);
1184
- border-radius: 100px;
1184
+ border-radius: var(--radius-pill);
1185
1185
  padding: 0.1rem 0.4rem;
1186
1186
  flex-shrink: 0;
1187
1187
  }
@@ -1190,7 +1190,7 @@
1190
1190
  .priority-badge {
1191
1191
  font-size: 0.68rem;
1192
1192
  font-weight: 500;
1193
- border-radius: 100px;
1193
+ border-radius: var(--radius-pill);
1194
1194
  padding: 0.15rem 0.5rem;
1195
1195
  flex-shrink: 0;
1196
1196
  }
@@ -1320,7 +1320,7 @@
1320
1320
  color: var(--text-muted);
1321
1321
  background: var(--bg-subtle);
1322
1322
  border: 1px solid var(--border);
1323
- border-radius: 100px;
1323
+ border-radius: var(--radius-pill);
1324
1324
  padding: 0.1rem 0.4rem;
1325
1325
  }
1326
1326
 
@@ -31,7 +31,12 @@
31
31
  import { auth } from '$lib/stores/auth';
32
32
  import Button from '$lib/components/ui/Button.svelte';
33
33
  import Toast from '$lib/components/ui/Toast.svelte';
34
- import { TOAST_TIMEOUT_MS } from '$lib/constants';
34
+ import EmptyState from '$lib/components/ui/EmptyState.svelte';
35
+ import AutomatedBadge from '$lib/components/ui/AutomatedBadge.svelte';
36
+ import PriorityBadge from '$lib/components/ui/PriorityBadge.svelte';
37
+ import CaseIdChip from '$lib/components/ui/CaseIdChip.svelte';
38
+ import ResultChip from '$lib/components/ui/ResultChip.svelte';
39
+ import { TOAST_TIMEOUT_MS, RUN_REFRESH_MS } from '$lib/constants';
35
40
 
36
41
  const runId = $page.params.id;
37
42
 
@@ -133,7 +138,7 @@
133
138
  const fresh = await fetchRun(runId);
134
139
  run = fresh;
135
140
  } catch {}
136
- }, 15000);
141
+ }, RUN_REFRESH_MS);
137
142
  });
138
143
 
139
144
  onDestroy(() => {
@@ -169,13 +174,18 @@
169
174
  (tc) =>
170
175
  !runCaseIds.has(tc.id) &&
171
176
  (!search ||
177
+ suite.displayId.toLowerCase().includes(search.toLowerCase()) ||
178
+ suite.name.toLowerCase().includes(search.toLowerCase()) ||
172
179
  tc.title.toLowerCase().includes(search.toLowerCase()) ||
173
180
  tc.displayId.toLowerCase().includes(search.toLowerCase()))
174
181
  )
175
182
  }))
176
183
  .filter(
177
184
  (suite) =>
178
- !search || suite.cases.length > 0 || suite.name.toLowerCase().includes(search.toLowerCase())
185
+ !search ||
186
+ suite.cases.length > 0 ||
187
+ suite.displayId.toLowerCase().includes(search.toLowerCase()) ||
188
+ suite.name.toLowerCase().includes(search.toLowerCase())
179
189
  );
180
190
 
181
191
  function addCase(tc) {
@@ -292,18 +302,6 @@
292
302
  dragOverEntryId = null;
293
303
  }
294
304
 
295
- function priorityClass(p) {
296
- return p?.toLowerCase() ?? 'medium';
297
- }
298
-
299
- function statusClass(s) {
300
- if (s === 'pass') return 'pass';
301
- if (s === 'fail') return 'fail';
302
- if (s === 'blocked') return 'warn';
303
- if (s === 'skip') return 'muted';
304
- return 'pending';
305
- }
306
-
307
305
  $: completedCount = run?.entries.filter((e) => e.status !== 'pending').length ?? 0;
308
306
  $: totalCount = run?.entries.length ?? 0;
309
307
  $: passCount = run?.entries.filter((e) => e.status === 'pass').length ?? 0;
@@ -378,7 +376,7 @@
378
376
  </div>
379
377
 
380
378
  {#if run.entries.length === 0}
381
- <div class="drop-hint">No cases in this run.</div>
379
+ <EmptyState message="No cases in this run." size="sm" />
382
380
  {:else}
383
381
  <div class="entry-sort-bar">
384
382
  {#each [['order', 'Order'], ['name', 'Name'], ['priority', 'Priority'], ['status', 'Status'], ['suite', 'Suite']] as [val, label]}
@@ -428,16 +426,14 @@
428
426
  {/if}
429
427
  <div class="entry-info">
430
428
  <div class="entry-badges">
431
- <span class="id-chip">{entry.case.displayId}</span>
432
- <span class="priority-badge {priorityClass(entry.case.priority)}"
433
- >{entry.case.priority}</span
434
- >
429
+ <CaseIdChip id={entry.case.displayId} />
430
+ <PriorityBadge priority={entry.case.priority} />
435
431
  </div>
436
432
  <p class="entry-title">{entry.case.title}</p>
437
433
  <span class="entry-suite">{entry.case.suite?.name ?? ''}</span>
438
434
  </div>
439
435
  {#if entry.case.isAutomated}
440
- <span class="assignee-auto-label">Automated</span>
436
+ <AutomatedBadge />
441
437
  {:else}
442
438
  <select
443
439
  class="assignee-select"
@@ -511,13 +507,11 @@
511
507
  <div class="browser-cases" transition:fly={{ y: -4, duration: 120 }}>
512
508
  {#each suite.cases as tc (tc.id)}
513
509
  <button class="browser-case" on:click={() => addCase(tc)}>
514
- <span class="id-chip small">{tc.displayId}</span>
510
+ <CaseIdChip id={tc.displayId} small />
515
511
  <span class="browser-case-title">{tc.title}</span>
516
- <span class="priority-badge small {priorityClass(tc.priority)}"
517
- >{tc.priority}</span
518
- >
512
+ <PriorityBadge priority={tc.priority} small />
519
513
  {#if tc.isAutomated}
520
- <span class="auto-pill">Auto</span>
514
+ <AutomatedBadge />
521
515
  {/if}
522
516
  </button>
523
517
  {/each}
@@ -586,8 +580,8 @@
586
580
  >
587
581
  </button>
588
582
  {/if}
589
- <span class="id-chip">{entry.case.displayId}</span>
590
- {#if entry.case.isAutomated}<span class="auto-pill">Automated</span>{/if}
583
+ <CaseIdChip id={entry.case.displayId} />
584
+ {#if entry.case.isAutomated}<AutomatedBadge />{/if}
591
585
  <div>
592
586
  <p class="exec-title">{entry.case.title}</p>
593
587
  <span class="exec-suite">{entry.case.suite?.name ?? ''}</span>
@@ -596,7 +590,7 @@
596
590
  <div class="exec-actions">
597
591
  {#if entry.case.isAutomated}
598
592
  <div class="exec-assignee-row">
599
- <span class="exec-assignee-name auto-label">Automated</span>
593
+ <AutomatedBadge />
600
594
  </div>
601
595
  <div class="exec-assignee-divider"></div>
602
596
  {:else if entry.assignedTo || (!isLocked && entry.assignedTo?.id !== currentUserId)}
@@ -617,7 +611,7 @@
617
611
  <div class="exec-assignee-divider"></div>
618
612
  {/if}
619
613
  {#if isLocked || run.status !== 'in-progress'}
620
- <span class="result-chip {statusClass(entry.status)}">{entry.status}</span>
614
+ <ResultChip status={entry.status} />
621
615
  {:else if entry.status === 'pending' || entry.status === 'in-progress'}
622
616
  <div class="exec-btns">
623
617
  <button
@@ -640,7 +634,7 @@
640
634
  </div>
641
635
  {:else}
642
636
  <div class="exec-btns">
643
- <span class="result-chip {statusClass(entry.status)}">{entry.status}</span>
637
+ <ResultChip status={entry.status} />
644
638
  <button
645
639
  class="exec-reset"
646
640
  on:click={() => handleMarkEntry(entry, 'pending')}
@@ -731,7 +725,7 @@
731
725
  font-size: 0.7rem;
732
726
  font-weight: 500;
733
727
  text-transform: capitalize;
734
- border-radius: 100px;
728
+ border-radius: var(--radius-pill);
735
729
  padding: 0.2rem 0.6rem;
736
730
  border: 1px solid var(--border);
737
731
  color: var(--text-muted);
@@ -764,7 +758,7 @@
764
758
  color: var(--text-muted);
765
759
  background: var(--bg-subtle);
766
760
  border: 1px solid var(--border);
767
- border-radius: 100px;
761
+ border-radius: var(--radius-pill);
768
762
  padding: 0.1rem 0.45rem;
769
763
  margin-left: auto;
770
764
  }
@@ -830,7 +824,7 @@
830
824
  font-size: 0.7rem;
831
825
  background: var(--bg-subtle);
832
826
  border: 1px solid var(--border);
833
- border-radius: 100px;
827
+ border-radius: var(--radius-pill);
834
828
  padding: 0.05rem 0.4rem;
835
829
  color: var(--text-muted);
836
830
  }
@@ -869,7 +863,7 @@
869
863
  font-size: 0.7rem;
870
864
  background: var(--bg-elevated);
871
865
  border: 1px solid var(--border);
872
- border-radius: 100px;
866
+ border-radius: var(--radius-pill);
873
867
  padding: 0.05rem 0.4rem;
874
868
  color: var(--text-muted);
875
869
  }
@@ -891,14 +885,6 @@
891
885
  border-color: var(--accent);
892
886
  }
893
887
 
894
- .drop-hint {
895
- padding: 2rem 1.25rem;
896
- font-size: 0.8125rem;
897
- color: var(--text-muted);
898
- text-align: center;
899
- line-height: 1.6;
900
- }
901
-
902
888
  .entry-sort-bar {
903
889
  display: flex;
904
890
  align-items: center;
@@ -911,7 +897,7 @@
911
897
  .entry-sort-chip {
912
898
  font-size: 0.75rem;
913
899
  padding: 0.2rem 0.55rem;
914
- border-radius: 100px;
900
+ border-radius: var(--radius-pill);
915
901
  border: 1px solid var(--border);
916
902
  background: var(--bg);
917
903
  color: var(--text-muted);
@@ -995,33 +981,6 @@
995
981
  color: var(--text-muted);
996
982
  }
997
983
 
998
- .assignee-auto-label {
999
- font-size: 0.72rem;
1000
- font-weight: 500;
1001
- color: var(--accent);
1002
- background: var(--accent-soft);
1003
- border-radius: 100px;
1004
- padding: 0.15rem 0.5rem;
1005
- flex-shrink: 0;
1006
- }
1007
-
1008
- .auto-pill {
1009
- font-size: 0.62rem;
1010
- font-weight: 600;
1011
- text-transform: uppercase;
1012
- letter-spacing: 0.05em;
1013
- color: var(--accent);
1014
- background: var(--accent-soft);
1015
- border-radius: 100px;
1016
- padding: 0.1rem 0.4rem;
1017
- flex-shrink: 0;
1018
- }
1019
-
1020
- .auto-label {
1021
- color: var(--accent);
1022
- font-weight: 500;
1023
- }
1024
-
1025
984
  .exec-locked-banner {
1026
985
  font-size: 0.8125rem;
1027
986
  color: var(--text-muted);
@@ -1165,9 +1124,6 @@
1165
1124
  }
1166
1125
 
1167
1126
  /* ── Execute ── */
1168
- .execute-workspace {
1169
- }
1170
-
1171
1127
  .progress-bar-wrap {
1172
1128
  margin-bottom: 1.25rem;
1173
1129
  }
@@ -1197,14 +1153,14 @@
1197
1153
  .progress-bar {
1198
1154
  height: 6px;
1199
1155
  background: var(--bg-subtle);
1200
- border-radius: 100px;
1156
+ border-radius: var(--radius-pill);
1201
1157
  overflow: hidden;
1202
1158
  display: flex;
1203
1159
  }
1204
1160
 
1205
1161
  .progress-fill {
1206
1162
  height: 100%;
1207
- border-radius: 100px;
1163
+ border-radius: var(--radius-pill);
1208
1164
  transition: width 0.3s var(--ease-out);
1209
1165
  }
1210
1166
 
@@ -1365,7 +1321,7 @@
1365
1321
  color: var(--accent);
1366
1322
  background: none;
1367
1323
  border: 1px solid var(--accent);
1368
- border-radius: 100px;
1324
+ border-radius: var(--radius-pill);
1369
1325
  padding: 0.1rem 0.5rem;
1370
1326
  cursor: pointer;
1371
1327
  transition: background var(--duration-fast);
@@ -1380,36 +1336,6 @@
1380
1336
  cursor: not-allowed;
1381
1337
  }
1382
1338
 
1383
- .result-chip {
1384
- font-size: 0.7rem;
1385
- font-weight: 600;
1386
- text-transform: uppercase;
1387
- letter-spacing: 0.05em;
1388
- padding: 0.2rem 0.6rem;
1389
- border-radius: 100px;
1390
- }
1391
-
1392
- .result-chip.pass {
1393
- background: var(--pass-soft);
1394
- color: var(--pass);
1395
- }
1396
- .result-chip.fail {
1397
- background: var(--fail-soft);
1398
- color: var(--fail);
1399
- }
1400
- .result-chip.warn {
1401
- background: var(--warn-soft);
1402
- color: var(--warn);
1403
- }
1404
- .result-chip.muted {
1405
- background: var(--bg-subtle);
1406
- color: var(--text-muted);
1407
- }
1408
- .result-chip.pending {
1409
- background: var(--bg-subtle);
1410
- color: var(--text-muted);
1411
- }
1412
-
1413
1339
  .exec-chevron {
1414
1340
  background: none;
1415
1341
  border: none;
@@ -1469,51 +1395,6 @@
1469
1395
  margin-top: 0.2rem;
1470
1396
  }
1471
1397
 
1472
- /* ── Shared chips ── */
1473
- .id-chip {
1474
- font-family: 'JetBrains Mono', monospace;
1475
- font-weight: 600;
1476
- color: var(--accent);
1477
- background: var(--accent-soft);
1478
- border-radius: 4px;
1479
- letter-spacing: 0.02em;
1480
- font-size: 0.7rem;
1481
- padding: 0.1rem 0.4rem;
1482
- flex-shrink: 0;
1483
- }
1484
-
1485
- .id-chip.small {
1486
- font-size: 0.63rem;
1487
- }
1488
-
1489
- .priority-badge {
1490
- font-size: 0.65rem;
1491
- font-weight: 500;
1492
- border-radius: 100px;
1493
- padding: 0.12rem 0.45rem;
1494
- flex-shrink: 0;
1495
- }
1496
-
1497
- .priority-badge.small {
1498
- font-size: 0.6rem;
1499
- }
1500
- .priority-badge.critical {
1501
- background: var(--fail-soft);
1502
- color: var(--fail);
1503
- }
1504
- .priority-badge.high {
1505
- background: var(--warn-soft);
1506
- color: var(--warn);
1507
- }
1508
- .priority-badge.medium {
1509
- background: var(--node-soft);
1510
- color: var(--node);
1511
- }
1512
- .priority-badge.low {
1513
- background: var(--bg-subtle);
1514
- color: var(--text-muted);
1515
- }
1516
-
1517
1398
  .icon-btn {
1518
1399
  display: flex;
1519
1400
  align-items: center;
@@ -980,7 +980,7 @@
980
980
  color: var(--text-muted);
981
981
  background: var(--bg);
982
982
  border: 1px solid var(--border);
983
- border-radius: 100px;
983
+ border-radius: var(--radius-pill);
984
984
  cursor: pointer;
985
985
  transition:
986
986
  color var(--duration-fast),
@@ -1321,7 +1321,7 @@
1321
1321
  text-transform: uppercase;
1322
1322
  letter-spacing: 0.05em;
1323
1323
  padding: 0.15rem 0.5rem;
1324
- border-radius: 100px;
1324
+ border-radius: var(--radius-pill);
1325
1325
  align-self: flex-start;
1326
1326
  }
1327
1327
 
@@ -1364,7 +1364,7 @@
1364
1364
  .priority-badge {
1365
1365
  font-size: 0.68rem;
1366
1366
  font-weight: 500;
1367
- border-radius: 100px;
1367
+ border-radius: var(--radius-pill);
1368
1368
  padding: 0.15rem 0.5rem;
1369
1369
  }
1370
1370
 
@@ -1393,7 +1393,7 @@
1393
1393
  font-weight: 500;
1394
1394
  background: var(--pass-soft);
1395
1395
  color: var(--pass);
1396
- border-radius: 100px;
1396
+ border-radius: var(--radius-pill);
1397
1397
  padding: 0.15rem 0.5rem;
1398
1398
  }
1399
1399
 
@@ -19,5 +19,11 @@ import { sveltekit } from '@sveltejs/kit/vite';
19
19
  import { defineConfig } from 'vite';
20
20
 
21
21
  export default defineConfig({
22
- plugins: [sveltekit()]
22
+ plugins: [sveltekit()],
23
+ server: {
24
+ // Plum is reverse-proxied behind whatever domain each self-hoster picks
25
+ // (see "Setting Up the Server" docs), so the Host header can't be known
26
+ // ahead of time. Disable Vite's dev-server host allowlist check entirely.
27
+ allowedHosts: true
28
+ }
23
29
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plum-e2e",
3
- "version": "2.4.13",
3
+ "version": "2.5.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/silverlunah/plum.git"