@delmaredigital/payload-experiments 0.2.0-rc.6 → 0.3.0-rc.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 (291) hide show
  1. package/README.md +129 -11
  2. package/dist/admin/PreviewLinks.d.ts +2 -1
  3. package/dist/admin/PreviewLinks.js +24 -16
  4. package/dist/admin/actions/ConfirmDialog.d.ts +7 -4
  5. package/dist/admin/actions/ConfirmDialog.js +37 -82
  6. package/dist/admin/actions/DecideDialog.d.ts +3 -1
  7. package/dist/admin/actions/DecideDialog.js +30 -13
  8. package/dist/admin/actions/MakeContentResult.d.ts +13 -2
  9. package/dist/admin/actions/MakeContentResult.js +115 -16
  10. package/dist/admin/actions/actions.model.d.ts +2 -4
  11. package/dist/admin/actions/actions.model.js +2 -3
  12. package/dist/admin/allocation.model.js +3 -5
  13. package/dist/admin/api.d.ts +10 -4
  14. package/dist/admin/api.js +11 -7
  15. package/dist/admin/copy/actions.d.ts +39 -3
  16. package/dist/admin/copy/actions.js +40 -4
  17. package/dist/admin/copy/armSelect.js +5 -5
  18. package/dist/admin/copy/arms.d.ts +1 -7
  19. package/dist/admin/copy/arms.js +1 -7
  20. package/dist/admin/copy/audience.d.ts +2 -1
  21. package/dist/admin/copy/audience.js +3 -2
  22. package/dist/admin/copy/common.d.ts +13 -6
  23. package/dist/admin/copy/common.js +14 -8
  24. package/dist/admin/copy/goals.d.ts +21 -6
  25. package/dist/admin/copy/goals.js +27 -6
  26. package/dist/admin/copy/readiness.d.ts +19 -0
  27. package/dist/admin/copy/readiness.js +19 -0
  28. package/dist/admin/copy/recommendation.d.ts +0 -3
  29. package/dist/admin/copy/recommendation.js +0 -3
  30. package/dist/admin/copy/results.d.ts +41 -2
  31. package/dist/admin/copy/results.js +42 -3
  32. package/dist/admin/copy/review.d.ts +1 -1
  33. package/dist/admin/copy/review.js +1 -1
  34. package/dist/admin/copy/setting.d.ts +2 -0
  35. package/dist/admin/copy/setting.js +2 -0
  36. package/dist/admin/copy/surface.d.ts +7 -4
  37. package/dist/admin/copy/surface.js +9 -4
  38. package/dist/admin/copy/testPage.d.ts +12 -0
  39. package/dist/admin/copy/testPage.js +12 -0
  40. package/dist/admin/format/date.d.ts +2 -0
  41. package/dist/admin/format/date.js +10 -0
  42. package/dist/admin/labels.d.ts +2 -7
  43. package/dist/admin/labels.js +2 -7
  44. package/dist/admin/previewLinks.model.d.ts +2 -2
  45. package/dist/admin/previewLinks.model.js +5 -7
  46. package/dist/admin/results/CiChart.js +11 -21
  47. package/dist/admin/results/DataQualityPanel.d.ts +4 -2
  48. package/dist/admin/results/DataQualityPanel.js +30 -17
  49. package/dist/admin/results/DataTable.d.ts +16 -0
  50. package/dist/admin/results/DataTable.js +152 -0
  51. package/dist/admin/results/StatTile.d.ts +7 -0
  52. package/dist/admin/results/StatTile.js +22 -0
  53. package/dist/admin/results/VerdictHero.d.ts +13 -0
  54. package/dist/admin/results/{VerdictCard.js → VerdictHero.js} +15 -12
  55. package/dist/admin/results/releasePages.model.d.ts +17 -0
  56. package/dist/admin/results/releasePages.model.js +10 -0
  57. package/dist/admin/results/results.css +64 -0
  58. package/dist/admin/results/results.model.d.ts +6 -2
  59. package/dist/admin/results/results.model.js +12 -5
  60. package/dist/admin/surface/ResultsRoute.d.ts +1 -1
  61. package/dist/admin/surface/ResultsRoute.js +60 -31
  62. package/dist/admin/surface/SurfaceShell.d.ts +11 -2
  63. package/dist/admin/surface/SurfaceShell.js +49 -14
  64. package/dist/admin/surface/TestsHome.d.ts +6 -1
  65. package/dist/admin/surface/TestsHome.js +183 -112
  66. package/dist/admin/surface/TestsSurface.js +56 -16
  67. package/dist/admin/surface/contracts.d.ts +1 -0
  68. package/dist/admin/surface/goals/GoalForm.js +341 -308
  69. package/dist/admin/surface/goals/GoalsList.js +123 -89
  70. package/dist/admin/surface/goals/data.d.ts +8 -2
  71. package/dist/admin/surface/goals/data.js +11 -3
  72. package/dist/admin/surface/goals/goals.css +10 -13
  73. package/dist/admin/surface/home.model.d.ts +7 -8
  74. package/dist/admin/surface/home.model.js +23 -12
  75. package/dist/admin/surface/newTest/NewTest.js +5 -5
  76. package/dist/admin/surface/pageBlock/parts.js +31 -26
  77. package/dist/admin/surface/readiness/ActionDialogs.js +43 -14
  78. package/dist/admin/surface/readiness/Banner.d.ts +3 -0
  79. package/dist/admin/surface/readiness/Banner.js +63 -36
  80. package/dist/admin/surface/readiness/SaveStatus.d.ts +1 -0
  81. package/dist/admin/surface/readiness/SaveStatus.js +6 -3
  82. package/dist/admin/surface/readiness/readiness.model.d.ts +2 -0
  83. package/dist/admin/surface/readiness/readiness.model.js +18 -9
  84. package/dist/admin/surface/readiness/useTransition.d.ts +1 -0
  85. package/dist/admin/surface/readiness/useTransition.js +2 -1
  86. package/dist/admin/surface/results/ReleasePages.d.ts +14 -0
  87. package/dist/admin/surface/results/ReleasePages.js +116 -0
  88. package/dist/admin/surface/results/ResultsBody.d.ts +15 -2
  89. package/dist/admin/surface/results/ResultsBody.js +281 -107
  90. package/dist/admin/surface/results/ResultsHeader.d.ts +10 -0
  91. package/dist/admin/surface/results/ResultsHeader.js +177 -0
  92. package/dist/admin/surface/results/recommendation.model.js +2 -6
  93. package/dist/admin/surface/results/useResults.d.ts +21 -3
  94. package/dist/admin/surface/results/useResults.js +2 -1
  95. package/dist/admin/surface/setting/parts.js +75 -88
  96. package/dist/admin/surface/setting/setting.css +3 -15
  97. package/dist/admin/surface/surface.css +62 -47
  98. package/dist/admin/surface/test/AdvancedSection.js +55 -48
  99. package/dist/admin/surface/test/GoalSection.js +39 -32
  100. package/dist/admin/surface/test/LengthSection.js +46 -34
  101. package/dist/admin/surface/test/Rail.d.ts +4 -0
  102. package/dist/admin/surface/test/Rail.js +83 -0
  103. package/dist/admin/surface/test/ReadinessBar.d.ts +5 -0
  104. package/dist/admin/surface/test/ReadinessBar.js +33 -0
  105. package/dist/admin/surface/test/Section.d.ts +1 -1
  106. package/dist/admin/surface/test/Section.js +36 -16
  107. package/dist/admin/surface/test/TargetingSection.js +168 -142
  108. package/dist/admin/surface/test/TestPage.d.ts +2 -1
  109. package/dist/admin/surface/test/TestPage.js +146 -59
  110. package/dist/admin/surface/test/VersionsSection.js +151 -120
  111. package/dist/admin/surface/test/fields.d.ts +11 -4
  112. package/dist/admin/surface/test/fields.js +84 -85
  113. package/dist/admin/surface/test/jumpToSection.d.ts +3 -0
  114. package/dist/admin/surface/test/jumpToSection.js +10 -0
  115. package/dist/admin/surface/test/pageParts.js +9 -3
  116. package/dist/admin/surface/test/testPage.css +81 -14
  117. package/dist/admin/surface/useTestDocument.d.ts +1 -0
  118. package/dist/admin/surface/useTestDocument.js +40 -9
  119. package/dist/admin/targeting/targeting.model.d.ts +1 -3
  120. package/dist/admin/targeting/targeting.model.js +3 -8
  121. package/dist/admin/theme.css +115 -36
  122. package/dist/admin/theme.d.ts +38 -0
  123. package/dist/admin/theme.js +38 -1
  124. package/dist/admin/useApi.d.ts +17 -2
  125. package/dist/admin/views/ExperimentsRootView.js +12 -2
  126. package/dist/collections/experiments/config.js +44 -1
  127. package/dist/collections/experiments/lockHook.js +16 -2
  128. package/dist/collections/identities.js +6 -1
  129. package/dist/collections/metrics.js +7 -0
  130. package/dist/collections/metricsGuards.d.ts +9 -0
  131. package/dist/collections/metricsGuards.js +33 -0
  132. package/dist/constants.d.ts +1 -0
  133. package/dist/constants.js +1 -0
  134. package/dist/contentDone.d.ts +4 -0
  135. package/dist/contentDone.js +5 -0
  136. package/dist/db/identities.d.ts +8 -7
  137. package/dist/db/identities.js +9 -6
  138. package/dist/db/metricUsage.d.ts +19 -0
  139. package/dist/db/metricUsage.js +54 -0
  140. package/dist/db/names.d.ts +5 -0
  141. package/dist/db/names.js +5 -0
  142. package/dist/db/publishedContent.d.ts +10 -0
  143. package/dist/db/publishedContent.js +43 -0
  144. package/dist/db/results.d.ts +2 -0
  145. package/dist/db/results.js +6 -2
  146. package/dist/db/snapshots.d.ts +18 -6
  147. package/dist/db/snapshots.js +23 -7
  148. package/dist/db/staleEdit.d.ts +6 -0
  149. package/dist/db/staleEdit.js +10 -0
  150. package/dist/db/sweep.js +11 -6
  151. package/dist/endpoints/events.js +3 -3
  152. package/dist/endpoints/index.js +19 -1
  153. package/dist/endpoints/recompute.js +2 -1
  154. package/dist/endpoints/results.js +5 -0
  155. package/dist/endpoints/sanitizeReadinessDetail.d.ts +1 -0
  156. package/dist/endpoints/sanitizeReadinessDetail.js +2 -0
  157. package/dist/endpoints/summary.js +20 -2
  158. package/dist/endpoints/transitions.d.ts +15 -0
  159. package/dist/endpoints/transitions.js +36 -0
  160. package/dist/errors.d.ts +8 -0
  161. package/dist/errors.js +28 -0
  162. package/dist/exports/adminClient.d.ts +1 -0
  163. package/dist/exports/adminClient.js +1 -0
  164. package/dist/exports/index.d.ts +2 -1
  165. package/dist/exports/index.js +1 -0
  166. package/dist/exports/puckEditor.d.ts +1 -0
  167. package/dist/exports/puckEditor.js +1 -0
  168. package/dist/exports/rsc.d.ts +5 -1
  169. package/dist/exports/rsc.js +2 -0
  170. package/dist/hooks/hostHook.js +3 -2
  171. package/dist/hooks/pageGuard.d.ts +5 -0
  172. package/dist/hooks/pageGuard.js +121 -0
  173. package/dist/labels/arms.d.ts +7 -0
  174. package/dist/labels/arms.js +7 -0
  175. package/dist/log.d.ts +7 -0
  176. package/dist/log.js +38 -0
  177. package/dist/manifest/compile.d.ts +5 -0
  178. package/dist/manifest/compile.js +5 -1
  179. package/dist/options.js +23 -0
  180. package/dist/plugin.js +26 -2
  181. package/dist/puck/copy.d.ts +1 -1
  182. package/dist/puck/copy.js +6 -6
  183. package/dist/puck/editor/editor.chrome.css +235 -0
  184. package/dist/puck/editor/editor.css +241 -43
  185. package/dist/results/aggregate.js +4 -3
  186. package/dist/results/analyse.d.ts +4 -1
  187. package/dist/results/analyse.js +27 -11
  188. package/dist/results/cuped.js +3 -2
  189. package/dist/results/identityCredit.d.ts +15 -7
  190. package/dist/results/identityCredit.js +32 -33
  191. package/dist/results/rolloutUnits.js +2 -1
  192. package/dist/results/snapshot.js +8 -2
  193. package/dist/results/srm.js +2 -1
  194. package/dist/results/verdict.d.ts +2 -0
  195. package/dist/results/verdict.js +18 -1
  196. package/dist/sanitizeReadinessDetail.d.ts +7 -0
  197. package/dist/sanitizeReadinessDetail.js +54 -0
  198. package/dist/server/linkIdentity.d.ts +10 -1
  199. package/dist/server/linkIdentity.js +20 -18
  200. package/dist/server/metadata.d.ts +5 -0
  201. package/dist/server/metadata.js +9 -1
  202. package/dist/server/recordContentChange.js +2 -1
  203. package/dist/server/recordExposures.js +3 -3
  204. package/dist/server/runResults.d.ts +3 -2
  205. package/dist/server/runResults.js +125 -34
  206. package/dist/server/trackConversion.js +5 -6
  207. package/dist/targeting/values.d.ts +4 -0
  208. package/dist/targeting/values.js +10 -0
  209. package/dist/transitions/addMetric.js +8 -0
  210. package/dist/transitions/archive.js +2 -3
  211. package/dist/transitions/checkStart.d.ts +6 -3
  212. package/dist/transitions/checkStart.js +15 -0
  213. package/dist/transitions/contentFields.d.ts +7 -0
  214. package/dist/transitions/contentFields.js +20 -0
  215. package/dist/transitions/decide.js +6 -1
  216. package/dist/transitions/deleteExperiment.js +2 -1
  217. package/dist/transitions/dismissContentEntry.d.ts +8 -0
  218. package/dist/transitions/dismissContentEntry.js +45 -0
  219. package/dist/transitions/makeContent.d.ts +5 -2
  220. package/dist/transitions/makeContent.js +217 -118
  221. package/dist/transitions/phaseRecord.d.ts +8 -2
  222. package/dist/transitions/placements.d.ts +1 -0
  223. package/dist/transitions/placements.js +4 -0
  224. package/dist/transitions/preflightData.d.ts +1 -1
  225. package/dist/transitions/preflightData.js +42 -4
  226. package/dist/transitions/releasePages.d.ts +8 -0
  227. package/dist/transitions/releasePages.js +26 -0
  228. package/dist/transitions/restoreContentEntry.d.ts +8 -0
  229. package/dist/transitions/restoreContentEntry.js +47 -0
  230. package/dist/transitions/start.js +30 -5
  231. package/dist/types/contracts.d.ts +14 -0
  232. package/dist/types/endpoints.d.ts +34 -3
  233. package/dist/types/index.d.ts +1 -0
  234. package/dist/types/index.js +1 -0
  235. package/dist/types/reasons.d.ts +3 -0
  236. package/dist/types/reasons.js +1 -0
  237. package/dist/types/snapshot.d.ts +6 -1
  238. package/dist/ui/Banner.d.ts +2 -0
  239. package/dist/ui/Banner.js +2 -0
  240. package/dist/ui/Breadcrumb.d.ts +7 -0
  241. package/dist/ui/Breadcrumb.js +25 -0
  242. package/dist/ui/Button.d.ts +15 -0
  243. package/dist/ui/Button.js +32 -0
  244. package/dist/ui/Card.d.ts +12 -0
  245. package/dist/ui/Card.js +40 -0
  246. package/dist/ui/Checkbox.d.ts +2 -0
  247. package/dist/ui/Checkbox.js +2 -0
  248. package/dist/{admin/components → ui}/ChipsInput.d.ts +4 -3
  249. package/dist/{admin/components → ui}/ChipsInput.js +20 -12
  250. package/dist/ui/Dialog.d.ts +13 -0
  251. package/dist/ui/Dialog.js +85 -0
  252. package/dist/ui/EmptyState.d.ts +2 -0
  253. package/dist/ui/EmptyState.js +2 -0
  254. package/dist/ui/Feedback.d.ts +24 -0
  255. package/dist/ui/Feedback.js +40 -0
  256. package/dist/ui/Inputs.d.ts +34 -0
  257. package/dist/ui/Inputs.js +225 -0
  258. package/dist/ui/NumberInput.d.ts +2 -0
  259. package/dist/ui/NumberInput.js +2 -0
  260. package/dist/ui/Range.d.ts +2 -0
  261. package/dist/ui/Range.js +2 -0
  262. package/dist/ui/SearchInput.d.ts +2 -0
  263. package/dist/ui/SearchInput.js +2 -0
  264. package/dist/ui/SectionCard.d.ts +2 -0
  265. package/dist/ui/SectionCard.js +2 -0
  266. package/dist/ui/Select.d.ts +2 -0
  267. package/dist/ui/Select.js +2 -0
  268. package/dist/ui/StatusChip.d.ts +2 -0
  269. package/dist/ui/StatusChip.js +2 -0
  270. package/dist/ui/Tabs.d.ts +14 -0
  271. package/dist/ui/Tabs.js +54 -0
  272. package/dist/ui/TextInput.d.ts +2 -0
  273. package/dist/ui/TextInput.js +2 -0
  274. package/dist/ui/Textarea.d.ts +2 -0
  275. package/dist/ui/Textarea.js +2 -0
  276. package/dist/ui/Toggle.d.ts +2 -0
  277. package/dist/ui/Toggle.js +2 -0
  278. package/dist/ui/accent.d.ts +21 -0
  279. package/dist/ui/accent.js +81 -0
  280. package/dist/ui/index.d.ts +9 -0
  281. package/dist/ui/index.js +9 -0
  282. package/dist/ui/ui.css +92 -0
  283. package/package.json +1 -1
  284. package/dist/admin/components/chipsInput.css +0 -11
  285. package/dist/admin/results/ArmTable.d.ts +0 -8
  286. package/dist/admin/results/ArmTable.js +0 -113
  287. package/dist/admin/results/VerdictCard.d.ts +0 -9
  288. package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +0 -5
  289. package/dist/admin/surface/goals/GoalsBreadcrumb.js +0 -47
  290. package/dist/admin/surface/test/TestHeader.d.ts +0 -4
  291. package/dist/admin/surface/test/TestHeader.js +0 -69
@@ -1,23 +1,90 @@
1
1
  .xp-test-page { color: var(--xp-text); background: var(--xp-bg); max-width: 90rem; margin: 0 auto; padding: 1.5rem; }
2
+ .xp-test-page--setup { min-width: 0; padding: 0 0 160px; }
2
3
  .xp-test-header { position: sticky; top: 0; z-index: 10; background: var(--xp-bg); padding: 1rem 0; border-bottom: 1px solid var(--xp-border); }
3
4
  .xp-header-meta, .xp-section-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; margin-block: 1rem; }
4
- .xp-kind-chip, .xp-status-pill { border: 1px solid var(--xp-border-control); border-radius: 1rem; padding: .25rem .75rem; }
5
+ .xp-kind-chip, .xp-status-pill { border: 1px solid var(--xp-border-strong); border-radius: 1rem; padding: .25rem .75rem; }
5
6
  .xp-status-pill--success, .xp-content-ready, .xp-section-check--done { color: var(--xp-success-text); }
6
7
  .xp-content-empty, .xp-status-pill--warning { color: var(--xp-warning-text); }
7
- .xp-test-section { padding-block: 1.5rem; border-bottom: 1px solid var(--xp-border); scroll-margin-top: 15rem; }
8
- .xp-version-rows { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
9
- .xp-version-row { border: 1px solid var(--xp-border); border-radius: var(--xp-radius); padding: 1rem; }
10
- .xp-version-colour { display: inline-block; width: .75rem; height: .75rem; margin-right: .5rem; border-radius: 50%; }
11
- .xp-traffic-bar { display: flex; height: 1.25rem; background: var(--xp-surface-raised); margin-block: 1.5rem; overflow: hidden; border-radius: var(--xp-radius); }
8
+ .xp-test-section, .xp-test-advanced { scroll-margin-top: var(--xp-space-6); }
9
+ .xp-version-rows { display: grid; grid-template-columns: minmax(0, 1.3fr) var(--xp-version-share-width) minmax(var(--xp-version-state-width), 1fr) var(--xp-version-remove-width); gap: var(--xp-space-3); align-items: center; }
10
+ .xp-version-row, .xp-version-grid-head { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; gap: var(--xp-space-3); align-items: center; }
11
+ .xp-version-grid-head { margin-bottom: var(--xp-space-2); font: var(--xp-text-small); color: var(--xp-text-2); }
12
+ .xp-version-row > .xp-field-error, .xp-version-row > [role=alert] { grid-column: 1 / -1; }
13
+ .xp-version-remove { grid-column: 4; }
14
+ .xp-grid-field .xp-ui-field { margin: 0; }
15
+ .xp-grid-field .xp-ui-field > label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
16
+ .xp-version-content { display: flex; flex-wrap: nowrap; gap: var(--xp-space-2); align-items: center; min-width: 0; }
17
+ .xp-version-content .xp-ui-button { white-space: nowrap; }
18
+ .xp-content-chip { flex-shrink: 0; display: inline-flex; width: fit-content; border-radius: var(--xp-radius-chip); padding: var(--xp-space-half) var(--xp-space-2); font: var(--xp-text-small); background: var(--xp-surface-2); color: var(--xp-text-2); }
19
+ .xp-content-chip.xp-content-ready { background: var(--xp-success-bg); color: var(--xp-success); }
20
+ .xp-content-chip.xp-content-empty { background: var(--xp-warning-bg); color: var(--xp-warning); }
21
+ .xp-traffic-bar { display: flex; height: var(--xp-space-2); gap: var(--xp-space-half); margin-block: var(--xp-space-5) var(--xp-space-3); overflow: hidden; border-radius: var(--xp-space-1); }
12
22
  .xp-traffic-bar > span { display: block; }
13
- .xp-field { display: flex; flex-direction: column; gap: .5rem; margin-block: 1rem; }
14
- .xp-field input, .xp-field textarea, .xp-field select { font: inherit; color: var(--xp-text); background: var(--xp-input-bg); border: 1px solid var(--xp-border-control); border-radius: var(--xp-radius); padding: .65rem; }
15
- .xp-field textarea { min-height: 6rem; resize: vertical; }
16
- .xp-field input[type=range] { padding: 0; }
23
+ .xp-split-legend { display: flex; flex-wrap: wrap; gap: var(--xp-space-4); font: var(--xp-text-small); color: var(--xp-text-2); }
24
+ .xp-split-legend i { display: inline-block; width: var(--xp-legend-key-size); height: var(--xp-legend-key-size); border-radius: var(--xp-legend-key-radius); margin-right: var(--xp-space-one-half); }
25
+ .xp-field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--xp-space-4); }
26
+ .xp-test-advanced .xp-field-grid { padding-top: var(--xp-space-4); }
27
+ .xp-field-grid > div { min-width: 0; }
28
+ .xp-field-grid p, .xp-page-binding p { color: var(--xp-text-2); font: var(--xp-text-small); }
29
+ .xp-read-only code { font-family: var(--xp-mono); overflow-wrap: anywhere; }
30
+ .xp-ui-checkbox-list { display: flex; flex-wrap: wrap; gap: var(--xp-space-2) var(--xp-space-4); }
31
+ .xp-duration-suggestion { align-items: center; margin-top: var(--xp-space-4); }
17
32
  .xp-read-only { padding-block: .5rem; }
18
33
  .xp-read-only p { margin-block: .5rem; }
19
34
  .xp-field-error { color: var(--xp-error-text); }
20
- .xp-test-page button { cursor: pointer; color: var(--xp-text); background: var(--xp-surface-raised); border: 1px solid var(--xp-border-control); padding: .65rem 1rem; border-radius: var(--xp-radius); font: inherit; }
21
- .xp-test-page button:disabled { opacity: .5; cursor: default; }
22
- .xp-test-page summary { cursor: pointer; padding-block: .75rem; }
23
- .xp-targeting-row { border-bottom: 1px solid var(--xp-border); padding-bottom: 1rem; }
35
+
36
+ .xp-targeting-row { display: grid; grid-template-columns: var(--xp-targeting-attribute-width) var(--xp-targeting-comparison-width) minmax(0, 1fr); align-items: start; gap: var(--xp-space-3); border: 1px solid var(--xp-border); border-radius: var(--xp-radius-control); padding: var(--xp-space-4); margin-bottom: var(--xp-space-4); }
37
+ .xp-targeting-row > .xp-ui-button-group, .xp-targeting-row > p { grid-column: 1 / -1; }
38
+ @media (max-width: 720px) {
39
+ .xp-version-rows { grid-template-columns: minmax(0, 1fr) var(--xp-version-share-width-mobile); }
40
+ .xp-version-content { flex-wrap: wrap; }
41
+ .xp-version-remove:empty { display: none; }
42
+ .xp-version-content, .xp-setting-values, .xp-version-remove { grid-column: 1 / -1; }
43
+ .xp-version-grid-head > :nth-child(n+3) { display: none; }
44
+ .xp-targeting-row, .xp-field-grid { grid-template-columns: 1fr; }
45
+ }
46
+
47
+ .xp-test-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); align-items: start; gap: var(--xp-space-8); }
48
+ .xp-test-content { display: grid; gap: var(--xp-space-8); min-width: 0; max-width: 760px; }
49
+ .xp-test-rail { position: sticky; top: var(--xp-space-6); min-width: 0; }
50
+ .xp-test-rail-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--xp-space-1); }
51
+ .xp-test-rail-item { display: flex; align-items: center; gap: var(--xp-space-2); padding: var(--xp-space-2); border-radius: var(--xp-radius-control); color: var(--xp-text-2); text-decoration: none; font: var(--xp-text-small); }
52
+ .xp-test-rail-item:hover, .xp-test-rail-item.is-active { background: var(--xp-surface-2); color: var(--xp-text); }
53
+ .xp-test-rail-item.is-active { font-weight: 600; }
54
+ .xp-test-rail-item--advanced { margin-top: var(--xp-space-3); color: var(--xp-text-3); }
55
+ .xp-test-rail-mark { flex: none; color: var(--xp-text-3); }
56
+ .xp-test-rail-mark--done { color: var(--xp-success); }
57
+ .xp-test-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
58
+ .xp-test-rail-item:focus-visible, .xp-test-advanced > summary:focus-visible, .xp-test-content h2:focus { outline: 2px solid var(--xp-focus); outline-offset: 2px; }
59
+ .xp-test-advanced { border: 1px solid var(--xp-border); border-radius: var(--xp-radius-card); background: var(--xp-surface); }
60
+ .xp-test-advanced > summary { cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: var(--xp-space-3); padding: var(--xp-space-4) var(--xp-space-5); color: var(--xp-text-2); }
61
+ .xp-test-advanced > summary h2 { margin: 0; font: var(--xp-text-body); }
62
+ .xp-test-advanced[open] > summary { border-bottom: 1px solid var(--xp-border); }
63
+ .xp-test-readiness-bar { position: fixed; bottom: 0; inset-inline: 0; z-index: 20; background: var(--xp-surface); border-top: 1px solid var(--xp-border); }
64
+ .xp-readiness { color: var(--xp-text); background: var(--xp-surface); padding: var(--xp-space-4); border: 1px solid var(--xp-border-strong); border-radius: var(--xp-radius-control); }
65
+ .xp-readiness--ready:not(.xp-readiness--bar) { background: var(--xp-success-bg); }
66
+ .xp-readiness--bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--xp-space-2) var(--xp-space-4); padding-block: var(--xp-space-3); min-height: 64px; border: 0; border-radius: 0; }
67
+ .xp-readiness__state { display: flex; align-items: center; flex-wrap: wrap; gap: var(--xp-space-2); min-width: 0; }
68
+ .xp-readiness__state p, .xp-readiness--bar > p { margin: 0; }
69
+ .xp-readiness__state > p { font-weight: 500; }
70
+ .xp-readiness--bar .xp-save-status, .xp-readiness__separator { color: var(--xp-text-3); font: var(--xp-text-small); }
71
+ .xp-readiness__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--xp-space-2); }
72
+ .xp-readiness--bar > .xp-readiness__actions { grid-column: 2; grid-row: 1 / span 3; justify-content: flex-end; }
73
+ @media (max-width: 960px) {
74
+ .xp-test-layout { grid-template-columns: minmax(0, 1fr); gap: var(--xp-space-5); }
75
+ .xp-test-rail { top: 0; z-index: 2; background: var(--xp-bg); padding-block: var(--xp-space-2); border-bottom: 1px solid var(--xp-border); }
76
+ .xp-test-rail-list { flex-direction: row; overflow-x: auto; }
77
+ .xp-test-rail-item { white-space: nowrap; }
78
+ .xp-test-rail-item--advanced { margin-top: 0; }
79
+ .xp-test-content { max-width: none; }
80
+ .xp-test-section, .xp-test-advanced { scroll-margin-top: 64px; }
81
+ }
82
+ @media (max-width: 720px) {
83
+ .xp-readiness--bar { grid-template-columns: minmax(0, 1fr); }
84
+ /* The sentence wraps under the save state here, so a trailing separator would dangle. */
85
+ .xp-readiness--bar .xp-readiness__separator { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
86
+ .xp-readiness--bar > .xp-readiness__actions { grid-column: 1; grid-row: auto; justify-content: flex-start; }
87
+ }
88
+
89
+ .xp-ui-checkbox-list > .xp-ui-field:has(input[type="search"]) { flex-basis: 100%; }
90
+ .xp-targeting-row .xp-ui-chips { margin-block: 0; }
@@ -17,6 +17,7 @@ export declare function useTestDocument(id: DocId, { apiRoute }: {
17
17
  hasUnsaved: boolean;
18
18
  loadState: "loading" | "ready" | "forbidden" | "missing" | "error";
19
19
  saving: "idle" | "saving" | "saved" | "error";
20
+ stale: boolean;
20
21
  /** Path-specific validation errors; _section is the fallback for a section-wide refusal. */
21
22
  errors: Record<string, string>;
22
23
  };
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import { API_MESSAGES, refusalMessage } from '../copy/common.js';
4
+ import { XP_IF_UPDATED_AT_HEADER } from '../../constants.js';
5
+ import { TEST_PAGE } from '../copy/testPage.js';
4
6
  import { changedPatch, editablePatch, payloadFieldErrors } from './testDocument.model.js';
5
7
  // Saves outlive their mounted reader. Reopening the same document must wait for its queue.
6
8
  const drainingSaves = new Map();
@@ -12,6 +14,7 @@ const initialView = ()=>({
12
14
  hasUnsaved: false,
13
15
  loadState: 'loading',
14
16
  saving: 'idle',
17
+ stale: false,
15
18
  errors: {}
16
19
  });
17
20
  async function waitForDrain(draining, signal) {
@@ -49,7 +52,7 @@ function clearErrors(errors, patch) {
49
52
  return Object.fromEntries(Object.entries(errors).filter(([path])=>!Object.hasOwn(patch, path.split('.')[0])));
50
53
  }
51
54
  function stage(session, partial) {
52
- if (!session.saved || session.reloading || session.view.loadState !== 'ready' || session.controller.signal.aborted) return;
55
+ if (!session.saved || session.view.stale || session.reloading || session.view.loadState !== 'ready' || session.controller.signal.aborted) return;
53
56
  const patch = editablePatch(session.saved.status, partial);
54
57
  if (!Object.keys(patch).length) return;
55
58
  session.pending = {
@@ -152,11 +155,13 @@ function stage(session, partial) {
152
155
  }
153
156
  session.publish();
154
157
  }
155
- async function readDocument() {
158
+ async function readDocument(forSave = false) {
156
159
  const response = await fetch(url, {
157
160
  method: 'GET',
158
161
  credentials: 'include',
159
- signal: controller.signal
162
+ ...forSave ? {} : {
163
+ signal: controller.signal
164
+ }
160
165
  });
161
166
  if (!response.ok) throw new Error(API_MESSAGES.unreadable);
162
167
  const doc = await response.json();
@@ -165,32 +170,55 @@ function stage(session, partial) {
165
170
  }
166
171
  // Capture each commit now, then diff against the latest server document when its turn arrives.
167
172
  function commit() {
168
- if (!session.saved || session.reloading || controller.signal.aborted || !Object.keys(session.pending).length) return session.tail;
173
+ if (!session.saved || session.view.stale || session.reloading || controller.signal.aborted || !Object.keys(session.pending).length) return session.tail;
169
174
  const queued = session.pending;
170
175
  session.pending = {};
171
176
  session.queue.push(queued);
172
177
  session.view.saving = 'saving';
173
178
  session.publish();
174
179
  const step = async ()=>{
180
+ if (session.view.stale) return;
175
181
  let saved = false;
176
182
  let changed = false;
177
183
  let failed = false;
178
184
  try {
179
185
  if (!session.saved) return;
186
+ if (typeof session.saved.updatedAt !== 'string' || !session.saved.updatedAt) {
187
+ session.saved = await readDocument(true);
188
+ if (typeof session.saved.updatedAt !== 'string' || !session.saved.updatedAt) throw new Error(API_MESSAGES.unreadable);
189
+ }
180
190
  const patch = changedPatch(session.saved, queued);
181
191
  changed = Object.keys(patch).length > 0;
182
192
  if (changed) {
193
+ const headers = {
194
+ 'Content-Type': 'application/json'
195
+ };
196
+ if (typeof session.saved.updatedAt === 'string') headers[XP_IF_UPDATED_AT_HEADER] = session.saved.updatedAt;
183
197
  const response = await fetch(url, {
184
198
  // Submitted saves belong to the queue, not the mounted reader's lifetime.
185
199
  method: 'PATCH',
186
200
  credentials: 'include',
187
- headers: {
188
- 'Content-Type': 'application/json'
189
- },
201
+ headers,
190
202
  body: JSON.stringify(patch)
191
203
  });
192
204
  const body = await response.json().catch(()=>undefined);
193
- if (!response.ok) session.view.errors = {
205
+ const errors = body?.errors;
206
+ const staleError = response.status === 409 && Array.isArray(errors) ? errors.find((error)=>error?.code === 'stale-edit') : undefined;
207
+ if (staleError) {
208
+ session.view.stale = true;
209
+ // Keep every typed value visible, but discard all queued sends until Reload.
210
+ session.pending = [
211
+ ...session.queue,
212
+ session.pending
213
+ ].reduce((draft, edit)=>({
214
+ ...draft,
215
+ ...edit
216
+ }), {});
217
+ session.queue = [];
218
+ session.view.errors = {
219
+ _section: staleError.message || TEST_PAGE.staleEdit
220
+ };
221
+ } else if (!response.ok) session.view.errors = {
194
222
  ...session.view.errors,
195
223
  ...payloadFieldErrors(body)
196
224
  };
@@ -199,9 +227,12 @@ function stage(session, partial) {
199
227
  const doc = body?.doc;
200
228
  const readable = doc && doc.id != null && doc.status;
201
229
  // A 2xx has applied even if its response body was lost. Preserve it while re-reading.
230
+ // The old timestamp is no longer a valid baseline. The next queued save
231
+ // must recover it with a GET before sending another PATCH.
202
232
  session.saved = readable ? doc : {
203
233
  ...session.saved,
204
- ...patch
234
+ ...patch,
235
+ updatedAt: undefined
205
236
  };
206
237
  saved = true;
207
238
  // Keep readiness pending even while recovering an unreadable successful PATCH body.
@@ -1,6 +1,7 @@
1
1
  import type { TargetingRule } from '../../types/contracts.js';
2
2
  import type { SerializedAttributeKeys } from '../../types/admin.js';
3
3
  import type { TargetingOp } from '../../targeting/ops.js';
4
+ export { readListValue } from '../../targeting/values.js';
4
5
  type Attribute = SerializedAttributeKeys[string];
5
6
  export type AttributeType = Attribute['type'];
6
7
  export declare function opsFor(type?: AttributeType): readonly TargetingOp[];
@@ -13,11 +14,8 @@ export declare function valueInput(attribute?: Attribute, op?: TargetingOp): {
13
14
  }[];
14
15
  };
15
16
  export declare function coerceValue(type: AttributeType, op: TargetingOp, raw: unknown): string | number | boolean | string[] | null;
16
- /** Only the read boundary accepts legacy comma-separated document values. */
17
- export declare function readListValue(raw: unknown): string[];
18
17
  /** Normalize the read boundary before any whole-group edit. */
19
18
  export declare function readTargetingRules(rules: TargetingRule[]): TargetingRule[];
20
19
  export declare function rowPath(path: string): string;
21
20
  export declare function emptyListHint(op: TargetingOp, value: unknown): string | undefined;
22
21
  export declare function humaniseAttribute(key: string): string;
23
- export {};
@@ -1,5 +1,7 @@
1
1
  import { TARGETING_OPS, isOpValid } from '../../targeting/ops.js';
2
+ import { coerceListValue, readListValue } from '../../targeting/values.js';
2
3
  import { NUMBER_OP_LABELS, STRING_OP_LABELS, AUDIENCE_COPY } from '../copy/audience.js';
4
+ export { readListValue } from '../../targeting/values.js';
3
5
  export function opsFor(type) {
4
6
  return type ? TARGETING_OPS[type] : [];
5
7
  }
@@ -27,11 +29,7 @@ export function valueInput(attribute, op = 'eq') {
27
29
  export function coerceValue(type, op, raw) {
28
30
  if (!isOpValid(type, op)) return null;
29
31
  if (type === 'string') {
30
- if (op === 'in' || op === 'nin') return [
31
- ...new Set((Array.isArray(raw) ? raw.map(String) : [
32
- String(raw ?? '')
33
- ]).map((value)=>value.trim()).filter(Boolean))
34
- ];
32
+ if (op === 'in' || op === 'nin') return coerceListValue(raw);
35
33
  return raw == null ? '' : String(raw);
36
34
  }
37
35
  if (type === 'number') {
@@ -41,9 +39,6 @@ export function coerceValue(type, op, raw) {
41
39
  }
42
40
  return raw === true || raw === 'true' ? true : raw === false || raw === 'false' ? false : null;
43
41
  }
44
- /** Only the read boundary accepts legacy comma-separated document values. */ export function readListValue(raw) {
45
- return coerceValue('string', 'in', typeof raw === 'string' ? raw.split(',') : raw);
46
- }
47
42
  /** Normalize the read boundary before any whole-group edit. */ export function readTargetingRules(rules) {
48
43
  return rules.map((rule)=>rule.op === 'in' || rule.op === 'nin' ? {
49
44
  ...rule,
@@ -1,44 +1,123 @@
1
- /* The Payload token boundary. Recompute aliases for either admin theme. */
1
+ /* The only Payload token boundary. Reference: Payload 3.90.2; nearest neutral
2
+ elevation by RGB distance to the approved mockup, advancing toward the mockup
3
+ on collision so all five surface steps stay distinct (light / dark):
4
+ bg 50/0; surface 0/50; surface-2 100/100; surface-3 150/150;
5
+ accent-subtle 200/200;
6
+ border 100/100; border-strong 250/200; text 850/800;
7
+ text-2 650/550; text-3 450/350; accent 800/800; hover 900/900;
8
+ accent-fg 0/0; focus 800/800; series-muted 300/250.
9
+ Semantic and series hues are fixed mockup literals; only neutrals use Payload. */
2
10
  :root, [data-theme] {
3
- --xp-bg: var(--theme-bg);
4
- --xp-text: var(--theme-text);
5
- --xp-surface: var(--theme-elevation-50);
6
- --xp-surface-raised: var(--theme-elevation-100);
7
- --xp-border: var(--theme-elevation-150);
8
- --xp-border-strong: var(--theme-elevation-200);
11
+ --xp-bg: var(--theme-elevation-50);
12
+ --xp-surface: var(--theme-elevation-0);
13
+ --xp-surface-2: var(--theme-elevation-100);
14
+ --xp-surface-3: var(--theme-elevation-150);
15
+ --xp-border: var(--theme-elevation-100);
16
+ --xp-border-strong: var(--theme-elevation-250);
17
+ --xp-text: var(--theme-elevation-850);
18
+ --xp-text-2: var(--theme-elevation-650);
19
+ --xp-text-3: var(--theme-elevation-450);
20
+ --xp-accent: var(--theme-elevation-800);
21
+ --xp-accent-hover: var(--theme-elevation-900);
22
+ --xp-accent-active: var(--theme-elevation-950);
23
+ --xp-accent-subtle: var(--theme-elevation-200);
24
+ --xp-accent-fg: var(--theme-elevation-0);
25
+ --xp-accent-text: var(--xp-text);
26
+ --xp-accent-text-decoration: none;
27
+ --xp-focus: var(--theme-elevation-800);
28
+ --xp-series-muted: var(--theme-elevation-300);
29
+ --xp-success: #1a6a3a;
30
+ --xp-success-bg: #e8f4ec;
31
+ --xp-success-border: #bddfc9;
32
+ --xp-warning: #8a5a0b;
33
+ --xp-warning-bg: #fbf3e1;
34
+ --xp-warning-border: #ecd6a2;
35
+ --xp-error: #a63030;
36
+ --xp-error-bg: #fbeaea;
37
+ --xp-error-border: #efc3c3;
38
+ --xp-series-a: #2a78d6;
39
+ --xp-series-b: #eb6834;
40
+ --xp-series-c: #8056b3;
41
+ --xp-radius-control: 8px;
42
+ --xp-radius-card: 12px;
43
+ --xp-radius-chip: 6px;
44
+ --xp-font: var(--font-body);
45
+ --xp-mono: var(--font-mono);
46
+ --xp-text-display: 600 28px/32px var(--xp-font);
47
+ --xp-text-title: 600 20px/28px var(--xp-font);
48
+ --xp-text-body: 400 15px/22px var(--xp-font);
49
+ --xp-text-small: 400 13px/18px var(--xp-font);
50
+ --xp-motion: 120ms;
51
+ --xp-space-half: 2px;
52
+ --xp-space-one-half: 6px;
53
+ --xp-version-share-width: 96px;
54
+ --xp-version-share-width-mobile: 84px;
55
+ --xp-version-state-width: 192px;
56
+ --xp-version-remove-width: 128px;
57
+ --xp-targeting-attribute-width: 150px;
58
+ --xp-targeting-comparison-width: 140px;
59
+ --xp-legend-key-size: 10px;
60
+ --xp-legend-key-radius: 3px;
61
+ --xp-space-1: 4px;
62
+ --xp-space-2: 8px;
63
+ --xp-space-3: 12px;
64
+ --xp-space-4: 16px;
65
+ --xp-space-5: 20px;
66
+ --xp-space-6: 24px;
67
+ --xp-space-7: 28px;
68
+ --xp-space-8: 32px;
69
+ --xp-surface-raised: var(--xp-surface-2);
9
70
  --xp-border-control: var(--theme-elevation-250);
10
71
  --xp-input-border: var(--theme-elevation-300);
11
72
  --xp-border-accent: var(--theme-elevation-400);
12
- --xp-accent: var(--theme-elevation-500);
13
- --xp-text-muted: var(--theme-elevation-600);
14
- --xp-text-strong: var(--theme-elevation-800);
15
- --xp-text-emphasis: var(--theme-elevation-900);
73
+ --xp-text-muted: var(--xp-text-2);
74
+ --xp-text-strong: var(--xp-text);
75
+ --xp-text-emphasis: var(--xp-text);
16
76
  --xp-input-bg: var(--theme-input-bg);
17
77
  --xp-overlay: var(--theme-overlay);
18
- --xp-success-surface: var(--theme-success-100);
19
- --xp-success-bg: var(--theme-success-150);
20
- --xp-success: var(--theme-success-500);
21
- --xp-success-text: var(--theme-success-600);
22
- --xp-success-text-strong: var(--theme-success-850);
23
- --xp-warning-surface: var(--theme-warning-100);
24
- --xp-warning-bg: var(--theme-warning-150);
25
- --xp-warning: var(--theme-warning-500);
26
- --xp-warning-text: var(--theme-warning-600);
27
- --xp-warning-text-strong: var(--theme-warning-850);
28
- --xp-error-surface: var(--theme-error-100);
29
- --xp-error: var(--theme-error-500);
30
- --xp-error-text: var(--theme-error-600);
31
- --xp-radius: var(--style-radius-s);
32
- --xp-radius-large: var(--style-radius-m);
33
- --xp-space-1: calc(var(--base) * 0.25);
34
- --xp-space-2: calc(var(--base) * 0.5);
35
- --xp-space-3: calc(var(--base) * 0.75);
36
- --xp-space-4: calc(var(--base) * 1);
37
- --xp-space-5: calc(var(--base) * 1.5);
38
- --xp-space-6: calc(var(--base) * 2);
78
+ --xp-success-surface: var(--xp-success-bg);
79
+ --xp-success-text: var(--xp-success);
80
+ --xp-success-text-strong: var(--xp-success);
81
+ --xp-warning-surface: var(--xp-warning-bg);
82
+ --xp-warning-text: var(--xp-warning);
83
+ --xp-warning-text-strong: var(--xp-warning);
84
+ --xp-error-surface: var(--xp-error-bg);
85
+ --xp-error-text: var(--xp-error);
86
+ --xp-radius: var(--xp-radius-control);
87
+ --xp-radius-large: var(--xp-radius-card);
39
88
  --xp-dash-card-gap: 12px;
40
- --xp-dash-card-gap-small: calc(var(--base) * 0.4);
41
- --xp-font-body: var(--font-body);
42
- --xp-font-size-small: calc(var(--base) * 0.65);
43
- --xp-font-size-body: calc(var(--base) * 0.75);
89
+ --xp-dash-card-gap-small: 8px;
90
+ --xp-font-body: var(--xp-font);
91
+ --xp-font-size-small: 13px;
92
+ --xp-font-size-body: 15px;
93
+ }
94
+ [data-theme="dark"] {
95
+ --xp-bg: var(--theme-elevation-0);
96
+ --xp-surface: var(--theme-elevation-50);
97
+ --xp-surface-2: var(--theme-elevation-100);
98
+ --xp-surface-3: var(--theme-elevation-150);
99
+ --xp-border: var(--theme-elevation-100);
100
+ --xp-border-strong: var(--theme-elevation-200);
101
+ --xp-text: var(--theme-elevation-800);
102
+ --xp-text-2: var(--theme-elevation-550);
103
+ --xp-text-3: var(--theme-elevation-350);
104
+ --xp-accent: var(--theme-elevation-800);
105
+ --xp-accent-hover: var(--theme-elevation-900);
106
+ --xp-accent-active: var(--theme-elevation-1000);
107
+ --xp-accent-subtle: var(--theme-elevation-200);
108
+ --xp-accent-fg: var(--theme-elevation-0);
109
+ --xp-focus: var(--theme-elevation-800);
110
+ --xp-series-muted: var(--theme-elevation-250);
111
+ --xp-success: #7fd49c;
112
+ --xp-success-bg: #142b1d;
113
+ --xp-success-border: #23492f;
114
+ --xp-warning: #e7bd5c;
115
+ --xp-warning-bg: #2b2312;
116
+ --xp-warning-border: #4d3d17;
117
+ --xp-error: #f09494;
118
+ --xp-error-bg: #2f1717;
119
+ --xp-error-border: #5a2626;
120
+ --xp-series-a: #3987e5;
121
+ --xp-series-b: #d95926;
122
+ --xp-series-c: #b48ade;
44
123
  }
@@ -1,9 +1,33 @@
1
+ /** Host accent rules (reference surfaces are defined by theme.css):
2
+ * (i) Foreground on accent, hover and active fills reaches 4.5:1 in both themes.
3
+ * (ii) Accent text moves toward the surface's opposite to reach 4.5:1; if the
4
+ * bounded search fails, use the neutral text token and underline it.
5
+ * (iii) Focus reaches 3:1 against the surface: darken on light, lighten on dark.
6
+ */
7
+ export { deriveAccent } from '../ui/accent.js';
1
8
  /** every Payload token reference lives in theme.css; porting to Payload 4 edits that file only */
2
9
  export declare const theme: {
3
10
  readonly color: {
4
11
  readonly bg: "var(--xp-bg)";
5
12
  readonly text: "var(--xp-text)";
6
13
  readonly surface: "var(--xp-surface)";
14
+ readonly surface2: "var(--xp-surface-2)";
15
+ readonly surface3: "var(--xp-surface-3)";
16
+ readonly text2: "var(--xp-text-2)";
17
+ readonly text3: "var(--xp-text-3)";
18
+ readonly accentHover: "var(--xp-accent-hover)";
19
+ readonly accentActive: "var(--xp-accent-active)";
20
+ readonly accentText: "var(--xp-accent-text)";
21
+ readonly accentSubtle: "var(--xp-accent-subtle)";
22
+ readonly accentFg: "var(--xp-accent-fg)";
23
+ readonly focus: "var(--xp-focus)";
24
+ readonly successBorder: "var(--xp-success-border)";
25
+ readonly warningBorder: "var(--xp-warning-border)";
26
+ readonly errorBg: "var(--xp-error-bg)";
27
+ readonly errorBorder: "var(--xp-error-border)";
28
+ readonly seriesA: "var(--xp-series-a)";
29
+ readonly seriesB: "var(--xp-series-b)";
30
+ readonly seriesMuted: "var(--xp-series-muted)";
7
31
  readonly surfaceRaised: "var(--xp-surface-raised)";
8
32
  readonly border: "var(--xp-border)";
9
33
  readonly borderStrong: "var(--xp-border-strong)";
@@ -30,7 +54,17 @@ export declare const theme: {
30
54
  readonly error: "var(--xp-error)";
31
55
  readonly errorText: "var(--xp-error-text)";
32
56
  };
57
+ readonly motion: "var(--xp-motion)";
58
+ readonly type: {
59
+ readonly display: "var(--xp-text-display)";
60
+ readonly title: "var(--xp-text-title)";
61
+ readonly body: "var(--xp-text-body)";
62
+ readonly small: "var(--xp-text-small)";
63
+ };
33
64
  readonly radius: {
65
+ readonly control: "var(--xp-radius-control)";
66
+ readonly card: "var(--xp-radius-card)";
67
+ readonly chip: "var(--xp-radius-chip)";
34
68
  readonly small: "var(--xp-radius)";
35
69
  readonly large: "var(--xp-radius-large)";
36
70
  };
@@ -41,8 +75,12 @@ export declare const theme: {
41
75
  readonly 4: "var(--xp-space-4)";
42
76
  readonly 5: "var(--xp-space-5)";
43
77
  readonly 6: "var(--xp-space-6)";
78
+ readonly 7: "var(--xp-space-7)";
79
+ readonly 8: "var(--xp-space-8)";
44
80
  };
45
81
  readonly font: {
82
+ readonly mono: "var(--xp-mono)";
83
+ readonly family: "var(--xp-font)";
46
84
  readonly body: "var(--xp-font-body)";
47
85
  readonly small: "var(--xp-font-size-small)";
48
86
  readonly size: "var(--xp-font-size-body)";
@@ -1,8 +1,31 @@
1
+ /** Host accent rules (reference surfaces are defined by theme.css):
2
+ * (i) Foreground on accent, hover and active fills reaches 4.5:1 in both themes.
3
+ * (ii) Accent text moves toward the surface's opposite to reach 4.5:1; if the
4
+ * bounded search fails, use the neutral text token and underline it.
5
+ * (iii) Focus reaches 3:1 against the surface: darken on light, lighten on dark.
6
+ */ export { deriveAccent } from '../ui/accent.js';
1
7
  /** every Payload token reference lives in theme.css; porting to Payload 4 edits that file only */ export const theme = {
2
8
  color: {
3
9
  bg: 'var(--xp-bg)',
4
10
  text: 'var(--xp-text)',
5
11
  surface: 'var(--xp-surface)',
12
+ surface2: 'var(--xp-surface-2)',
13
+ surface3: 'var(--xp-surface-3)',
14
+ text2: 'var(--xp-text-2)',
15
+ text3: 'var(--xp-text-3)',
16
+ accentHover: 'var(--xp-accent-hover)',
17
+ accentActive: 'var(--xp-accent-active)',
18
+ accentText: 'var(--xp-accent-text)',
19
+ accentSubtle: 'var(--xp-accent-subtle)',
20
+ accentFg: 'var(--xp-accent-fg)',
21
+ focus: 'var(--xp-focus)',
22
+ successBorder: 'var(--xp-success-border)',
23
+ warningBorder: 'var(--xp-warning-border)',
24
+ errorBg: 'var(--xp-error-bg)',
25
+ errorBorder: 'var(--xp-error-border)',
26
+ seriesA: 'var(--xp-series-a)',
27
+ seriesB: 'var(--xp-series-b)',
28
+ seriesMuted: 'var(--xp-series-muted)',
6
29
  surfaceRaised: 'var(--xp-surface-raised)',
7
30
  border: 'var(--xp-border)',
8
31
  borderStrong: 'var(--xp-border-strong)',
@@ -29,7 +52,17 @@
29
52
  error: 'var(--xp-error)',
30
53
  errorText: 'var(--xp-error-text)'
31
54
  },
55
+ motion: 'var(--xp-motion)',
56
+ type: {
57
+ display: 'var(--xp-text-display)',
58
+ title: 'var(--xp-text-title)',
59
+ body: 'var(--xp-text-body)',
60
+ small: 'var(--xp-text-small)'
61
+ },
32
62
  radius: {
63
+ control: 'var(--xp-radius-control)',
64
+ card: 'var(--xp-radius-card)',
65
+ chip: 'var(--xp-radius-chip)',
33
66
  small: 'var(--xp-radius)',
34
67
  large: 'var(--xp-radius-large)'
35
68
  },
@@ -39,9 +72,13 @@
39
72
  3: 'var(--xp-space-3)',
40
73
  4: 'var(--xp-space-4)',
41
74
  5: 'var(--xp-space-5)',
42
- 6: 'var(--xp-space-6)'
75
+ 6: 'var(--xp-space-6)',
76
+ 7: 'var(--xp-space-7)',
77
+ 8: 'var(--xp-space-8)'
43
78
  },
44
79
  font: {
80
+ mono: 'var(--xp-mono)',
81
+ family: 'var(--xp-font)',
45
82
  body: 'var(--xp-font-body)',
46
83
  small: 'var(--xp-font-size-small)',
47
84
  size: 'var(--xp-font-size-body)'
@@ -1,17 +1,32 @@
1
1
  export declare function useApi(): {
2
2
  start: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
3
- allocate: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AllocateBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
4
- addMetric: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AddMetricBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
3
+ allocate: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AllocateBody, options?: {
4
+ notFound?: string;
5
+ codes?: Readonly<Record<string, string>>;
6
+ fallback?: string;
7
+ }) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
8
+ addMetric: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AddMetricBody, options?: {
9
+ notFound?: string;
10
+ codes?: Readonly<Record<string, string>>;
11
+ fallback?: string;
12
+ }) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
5
13
  stop: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
6
14
  decide: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").DecideBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
7
15
  releaseHoldout: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
16
+ releasePages: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
8
17
  makeContent: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").MakeContentResult>>;
18
+ dismissContentEntry: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").DismissContentEntryBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").DismissContentEntryResult>>;
19
+ restoreContentEntry: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").RestoreContentEntryBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").RestoreContentEntryResult>>;
9
20
  archive: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").ArchiveBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
10
21
  preflight: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").PreflightResult>>;
11
22
  previewToken: (id: import("../types/contracts.js").DocId, arm: string) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").PreviewTokenResult>>;
12
23
  results: (id: import("../types/contracts.js").DocId, options?: {
13
24
  phase?: number;
14
25
  series?: boolean;
26
+ } & {
27
+ notFound?: string;
28
+ codes?: Readonly<Record<string, string>>;
29
+ fallback?: string;
15
30
  }) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").ResultsResponse>>;
16
31
  recompute: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<{
17
32
  snapshotId: string;
@@ -1,4 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { accentStyles } from '../../ui/accent.js';
2
3
  import { formatAdminURL } from 'payload/shared';
3
4
  import { notFound, redirect } from 'next/navigation.js';
4
5
  import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
@@ -166,10 +167,19 @@ export async function ExperimentsRootView({ initPageResult, params, searchParams
166
167
  children: SURFACE_COPY.notInstalled
167
168
  });
168
169
  }
169
- return RenderServerComponent({
170
+ const css = accentStyles(options.theme?.accent);
171
+ const surface = RenderServerComponent({
170
172
  Component: XP_TESTS_SURFACE,
171
173
  Fallback: TestsSurfaceFallback,
172
174
  importMap: req.payload.importMap,
173
175
  clientProps
174
176
  });
177
+ return css ? /*#__PURE__*/ _jsxs(_Fragment, {
178
+ children: [
179
+ /*#__PURE__*/ _jsx("style", {
180
+ children: css
181
+ }),
182
+ surface
183
+ ]
184
+ }) : surface;
175
185
  }