@delmaredigital/payload-experiments 0.3.0-rc.1 → 0.4.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 (338) hide show
  1. package/README.md +172 -21
  2. package/dist/admin/MetricSourceSelect.js +3 -3
  3. package/dist/admin/PlacementStatus.d.ts +3 -0
  4. package/dist/admin/PlacementStatus.js +54 -0
  5. package/dist/admin/PreviewLinks.d.ts +2 -1
  6. package/dist/admin/PreviewLinks.js +47 -26
  7. package/dist/admin/TestsNavLink.d.ts +3 -1
  8. package/dist/admin/TestsNavLink.js +2 -2
  9. package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
  10. package/dist/admin/actions/ConfirmDialog.js +2 -2
  11. package/dist/admin/actions/DecideDialog.d.ts +2 -1
  12. package/dist/admin/actions/DecideDialog.js +2 -2
  13. package/dist/admin/api.d.ts +11 -2
  14. package/dist/admin/api.js +20 -2
  15. package/dist/admin/armSelect/ArmSelectField.js +14 -22
  16. package/dist/admin/arms/arms.model.d.ts +1 -21
  17. package/dist/admin/arms/arms.model.js +1 -153
  18. package/dist/admin/blocks/BlockPanel.d.ts +4 -0
  19. package/dist/admin/blocks/BlockPanel.js +153 -0
  20. package/dist/admin/blocks/TestBlockField.d.ts +5 -0
  21. package/dist/admin/blocks/TestBlockField.js +163 -0
  22. package/dist/admin/blocks/formApi.d.ts +43 -0
  23. package/dist/admin/blocks/formApi.js +153 -0
  24. package/dist/admin/blocks/inject.d.ts +7 -0
  25. package/dist/admin/blocks/inject.js +41 -0
  26. package/dist/admin/blocks/model.d.ts +17 -0
  27. package/dist/admin/blocks/model.js +45 -0
  28. package/dist/admin/blocks/statusCache.d.ts +7 -0
  29. package/dist/admin/blocks/statusCache.js +83 -0
  30. package/dist/admin/blocks/useBlockForm.d.ts +17 -0
  31. package/dist/admin/blocks/useBlockForm.js +46 -0
  32. package/dist/admin/copy/actions.d.ts +2 -0
  33. package/dist/admin/copy/actions.js +9 -0
  34. package/dist/admin/copy/arms.d.ts +1 -6
  35. package/dist/admin/copy/arms.js +1 -16
  36. package/dist/admin/copy/blocks.d.ts +27 -0
  37. package/dist/admin/copy/blocks.js +38 -0
  38. package/dist/admin/copy/goals.d.ts +2 -0
  39. package/dist/admin/copy/goals.js +4 -1
  40. package/dist/admin/copy/newTest.d.ts +10 -0
  41. package/dist/admin/copy/newTest.js +10 -0
  42. package/dist/admin/copy/pages.d.ts +26 -0
  43. package/dist/admin/copy/pages.js +26 -0
  44. package/dist/admin/copy/readiness.d.ts +25 -3
  45. package/dist/admin/copy/readiness.js +25 -3
  46. package/dist/admin/copy/results.d.ts +3 -0
  47. package/dist/admin/copy/results.js +4 -1
  48. package/dist/admin/copy/review.d.ts +1 -1
  49. package/dist/admin/copy/review.js +1 -1
  50. package/dist/admin/copy/surface.d.ts +0 -1
  51. package/dist/admin/copy/surface.js +0 -1
  52. package/dist/admin/copy/testPage.d.ts +4 -0
  53. package/dist/admin/copy/testPage.js +4 -0
  54. package/dist/admin/goals/XpGoalField.d.ts +2 -0
  55. package/dist/admin/goals/XpGoalField.js +152 -0
  56. package/dist/admin/metricSource.model.d.ts +2 -0
  57. package/dist/admin/metricSource.model.js +4 -0
  58. package/dist/admin/pageCollections.d.ts +10 -0
  59. package/dist/admin/pageCollections.js +20 -0
  60. package/dist/admin/paths.d.ts +8 -1
  61. package/dist/admin/paths.js +8 -1
  62. package/dist/admin/placementStatus.model.d.ts +2 -0
  63. package/dist/admin/placementStatus.model.js +19 -0
  64. package/dist/admin/placements/placementLabels.d.ts +11 -0
  65. package/dist/admin/placements/placementLabels.js +21 -0
  66. package/dist/admin/placements/placementLabels.model.d.ts +13 -0
  67. package/dist/admin/placements/placementLabels.model.js +12 -0
  68. package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
  69. package/dist/admin/placements/usePlacementLabels.js +102 -0
  70. package/dist/admin/previewLinks.model.d.ts +2 -0
  71. package/dist/admin/previewLinks.model.js +3 -0
  72. package/dist/admin/results/DataQualityPanel.js +4 -1
  73. package/dist/admin/results/legacy.d.ts +16 -0
  74. package/dist/admin/results/legacy.js +27 -0
  75. package/dist/admin/results/results.model.d.ts +9 -6
  76. package/dist/admin/results/results.model.js +22 -9
  77. package/dist/admin/results/snapshot.d.ts +14 -0
  78. package/dist/admin/results/snapshot.js +3 -0
  79. package/dist/admin/surface/TestsHome.js +43 -15
  80. package/dist/admin/surface/TestsSurface.js +5 -1
  81. package/dist/admin/surface/contracts.d.ts +6 -5
  82. package/dist/admin/surface/goals/GoalForm.js +67 -34
  83. package/dist/admin/surface/goals/GoalsList.js +3 -3
  84. package/dist/admin/surface/goals/data.js +19 -16
  85. package/dist/admin/surface/goals/goals.model.d.ts +1 -2
  86. package/dist/admin/surface/goals/goals.model.js +1 -3
  87. package/dist/admin/surface/home.model.d.ts +2 -1
  88. package/dist/admin/surface/home.model.js +8 -4
  89. package/dist/admin/surface/newTest/NewTest.js +58 -17
  90. package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
  91. package/dist/admin/surface/newTest/PageWizard.js +209 -0
  92. package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
  93. package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
  94. package/dist/admin/surface/pagePicker/PagePicker.d.ts +10 -0
  95. package/dist/admin/surface/pagePicker/PagePicker.js +172 -0
  96. package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +25 -0
  97. package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
  98. package/dist/admin/surface/readiness/ActionDialogs.js +103 -69
  99. package/dist/admin/surface/readiness/Banner.js +30 -6
  100. package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
  101. package/dist/admin/surface/readiness/readiness.model.js +95 -6
  102. package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
  103. package/dist/admin/surface/results/ReleasePages.js +3 -7
  104. package/dist/admin/surface/results/ResultsBody.js +23 -3
  105. package/dist/admin/surface/results/ResultsHeader.js +2 -5
  106. package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
  107. package/dist/admin/surface/results/recommendation.model.js +10 -3
  108. package/dist/admin/surface/results/useResults.d.ts +10 -1
  109. package/dist/admin/surface/surface.css +0 -1
  110. package/dist/admin/surface/test/AdvancedSection.js +111 -104
  111. package/dist/admin/surface/test/GoalSection.js +97 -22
  112. package/dist/admin/surface/test/LengthSection.js +1 -1
  113. package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
  114. package/dist/admin/surface/test/PageHoldout.js +67 -0
  115. package/dist/admin/surface/test/TestPage.js +2 -1
  116. package/dist/admin/surface/test/VersionsSection.js +12 -5
  117. package/dist/admin/surface/test/fields.d.ts +13 -4
  118. package/dist/admin/surface/test/fields.js +46 -14
  119. package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
  120. package/dist/admin/surface/test/jumpToSection.js +4 -3
  121. package/dist/admin/surface/test/pageParts.d.ts +4 -1
  122. package/dist/admin/surface/test/pageParts.js +79 -2
  123. package/dist/admin/surface/test/testPage.css +8 -1
  124. package/dist/admin/surface/test/testPage.model.d.ts +2 -3
  125. package/dist/admin/surface/test/testPage.model.js +1 -21
  126. package/dist/admin/surface/testDocument.model.d.ts +1 -0
  127. package/dist/admin/surface/testDocument.model.js +2 -1
  128. package/dist/admin/surface/useTestDocument.d.ts +11 -2
  129. package/dist/admin/surface/useTestDocument.js +55 -16
  130. package/dist/admin/useApi.d.ts +11 -2
  131. package/dist/admin/useApi.js +3 -5
  132. package/dist/admin/useLayerUsage.js +14 -23
  133. package/dist/admin/views/ExperimentsRootView.js +58 -16
  134. package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
  135. package/dist/admin/views/TestsNavLinkServer.js +43 -0
  136. package/dist/arms/copy.d.ts +6 -0
  137. package/dist/arms/copy.js +16 -0
  138. package/dist/arms/model.d.ts +23 -0
  139. package/dist/arms/model.js +173 -0
  140. package/dist/blocks/bindings.d.ts +3 -0
  141. package/dist/blocks/bindings.js +34 -0
  142. package/dist/blocks/createExperimentBlock.d.ts +6 -0
  143. package/dist/blocks/createExperimentBlock.js +77 -0
  144. package/dist/blocks/formState.d.ts +31 -0
  145. package/dist/blocks/formState.js +146 -0
  146. package/dist/blocks/model.d.ts +41 -0
  147. package/dist/blocks/model.js +34 -0
  148. package/dist/client/ExperimentGroup.d.ts +6 -0
  149. package/dist/client/ExperimentGroup.js +9 -0
  150. package/dist/client/ExperimentsProvider.js +20 -4
  151. package/dist/client/clickGoals.d.ts +4 -0
  152. package/dist/client/clickGoals.js +43 -0
  153. package/dist/collections/experiments/config.js +12 -2
  154. package/dist/collections/experiments/deleteGuard.d.ts +2 -1
  155. package/dist/collections/experiments/deleteGuard.js +12 -1
  156. package/dist/collections/experiments/editability.js +4 -2
  157. package/dist/collections/experiments/pickSlug.d.ts +2 -0
  158. package/dist/collections/experiments/pickSlug.js +8 -0
  159. package/dist/collections/labels.d.ts +3 -0
  160. package/dist/collections/labels.js +20 -0
  161. package/dist/collections/metrics.js +12 -2
  162. package/dist/collections/phases.js +1 -0
  163. package/dist/collections/serialize.js +7 -0
  164. package/dist/db/appends.d.ts +6 -2
  165. package/dist/db/appends.js +10 -4
  166. package/dist/db/clickGoals.d.ts +9 -0
  167. package/dist/db/clickGoals.js +22 -0
  168. package/dist/db/delete.d.ts +1 -0
  169. package/dist/db/delete.js +1 -0
  170. package/dist/db/locks.d.ts +14 -0
  171. package/dist/db/locks.js +36 -1
  172. package/dist/db/names.d.ts +4 -0
  173. package/dist/db/names.js +4 -0
  174. package/dist/db/pageBindings.d.ts +4 -0
  175. package/dist/db/pageBindings.js +17 -0
  176. package/dist/db/pageBusy.d.ts +2 -0
  177. package/dist/db/pageBusy.js +10 -0
  178. package/dist/db/pageContent.d.ts +10 -0
  179. package/dist/db/pageContent.js +79 -0
  180. package/dist/db/pageOccupancy.d.ts +12 -0
  181. package/dist/db/pageOccupancy.js +45 -0
  182. package/dist/db/placements.d.ts +33 -0
  183. package/dist/db/placements.js +54 -0
  184. package/dist/db/slugs.d.ts +8 -0
  185. package/dist/db/slugs.js +40 -0
  186. package/dist/db/snapshots.d.ts +6 -0
  187. package/dist/db/snapshots.js +9 -5
  188. package/dist/endpoints/clickKeys.d.ts +5 -0
  189. package/dist/endpoints/clickKeys.js +17 -0
  190. package/dist/endpoints/events.js +4 -1
  191. package/dist/endpoints/index.js +28 -0
  192. package/dist/endpoints/pages.d.ts +5 -0
  193. package/dist/endpoints/pages.js +120 -0
  194. package/dist/endpoints/placementStatus.d.ts +6 -0
  195. package/dist/endpoints/placementStatus.js +238 -0
  196. package/dist/endpoints/readBody.d.ts +2 -0
  197. package/dist/endpoints/readBody.js +24 -0
  198. package/dist/endpoints/results.js +5 -1
  199. package/dist/endpoints/summary.js +11 -4
  200. package/dist/endpoints/wrapBlock.d.ts +6 -0
  201. package/dist/endpoints/wrapBlock.js +76 -0
  202. package/dist/errors.d.ts +3 -3
  203. package/dist/errors.js +4 -4
  204. package/dist/events/rows.d.ts +1 -1
  205. package/dist/events/rows.js +5 -3
  206. package/dist/exports/adminClient.d.ts +5 -0
  207. package/dist/exports/adminClient.js +5 -0
  208. package/dist/exports/adminRsc.d.ts +1 -0
  209. package/dist/exports/adminRsc.js +1 -0
  210. package/dist/exports/blocks.d.ts +2 -0
  211. package/dist/exports/blocks.js +1 -0
  212. package/dist/exports/client.d.ts +1 -0
  213. package/dist/exports/client.js +1 -0
  214. package/dist/exports/index.d.ts +6 -1
  215. package/dist/exports/index.js +3 -0
  216. package/dist/exports/puck.d.ts +5 -2
  217. package/dist/exports/puck.js +3 -1
  218. package/dist/exports/puckEditor.d.ts +6 -1
  219. package/dist/exports/puckEditor.js +4 -1
  220. package/dist/exports/rsc.d.ts +4 -0
  221. package/dist/exports/rsc.js +2 -0
  222. package/dist/fields/xpGoal.d.ts +3 -0
  223. package/dist/fields/xpGoal.js +28 -0
  224. package/dist/globals/state.js +10 -0
  225. package/dist/goals/cache.d.ts +3 -0
  226. package/dist/goals/cache.js +35 -0
  227. package/dist/goals/fairness.d.ts +40 -0
  228. package/dist/goals/fairness.js +200 -0
  229. package/dist/goals/key.d.ts +4 -0
  230. package/dist/goals/key.js +3 -0
  231. package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
  232. package/dist/goals/picker/clickGoalPicker.model.js +45 -0
  233. package/dist/goals/picker/copy.d.ts +16 -0
  234. package/dist/goals/picker/copy.js +15 -0
  235. package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
  236. package/dist/goals/picker/useClickGoalPicker.js +220 -0
  237. package/dist/hooks/pageGuard.d.ts +5 -1
  238. package/dist/hooks/pageGuard.js +160 -80
  239. package/dist/lib/fetchAllDocs.d.ts +16 -0
  240. package/dist/lib/fetchAllDocs.js +56 -0
  241. package/dist/onInit.js +29 -0
  242. package/dist/options.d.ts +2 -0
  243. package/dist/options.js +43 -10
  244. package/dist/plugin.js +45 -28
  245. package/dist/puck/ExperimentPicker.js +47 -16
  246. package/dist/puck/ExperimentSlot.d.ts +51 -1
  247. package/dist/puck/ExperimentSlot.js +23 -12
  248. package/dist/puck/copy.d.ts +6 -4
  249. package/dist/puck/copy.js +6 -4
  250. package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
  251. package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
  252. package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
  253. package/dist/puck/editor/ResultsReadout.d.ts +6 -0
  254. package/dist/puck/editor/ResultsReadout.js +99 -0
  255. package/dist/puck/editor/VersionBuilder.d.ts +34 -0
  256. package/dist/puck/editor/VersionBuilder.js +317 -0
  257. package/dist/puck/editor/armWrites.d.ts +7 -0
  258. package/dist/puck/editor/armWrites.js +71 -0
  259. package/dist/puck/editor/armsCache.d.ts +14 -0
  260. package/dist/puck/editor/armsCache.js +78 -0
  261. package/dist/puck/editor/copy.d.ts +33 -1
  262. package/dist/puck/editor/copy.js +45 -8
  263. package/dist/puck/editor/createTest.d.ts +1 -1
  264. package/dist/puck/editor/createTest.js +25 -14
  265. package/dist/puck/editor/editor.chrome.css +48 -161
  266. package/dist/puck/editor/editor.css +48 -161
  267. package/dist/puck/editor/editor.model.d.ts +8 -3
  268. package/dist/puck/editor/editor.model.js +16 -10
  269. package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
  270. package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
  271. package/dist/puck/editor/integration.d.ts +10 -0
  272. package/dist/puck/editor/integration.js +30 -0
  273. package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
  274. package/dist/puck/editor/xpGoalPuckEditorField.js +134 -0
  275. package/dist/puck/experimentSlotField.d.ts +2 -0
  276. package/dist/puck/experimentSlotField.js +11 -3
  277. package/dist/puck/hostContext.d.ts +10 -0
  278. package/dist/puck/hostContext.js +12 -0
  279. package/dist/puck/integration.d.ts +6 -0
  280. package/dist/puck/integration.js +21 -0
  281. package/dist/puck/routes.d.ts +12 -0
  282. package/dist/puck/routes.js +11 -0
  283. package/dist/puck/slotConfig.d.ts +1 -1
  284. package/dist/puck/slotConfig.js +2 -2
  285. package/dist/puck/xpGoalField.d.ts +3 -0
  286. package/dist/puck/xpGoalField.js +6 -0
  287. package/dist/results/health.d.ts +4 -0
  288. package/dist/results/health.js +27 -0
  289. package/dist/results/snapshot.js +15 -2
  290. package/dist/sanitizeReadinessDetail.js +51 -1
  291. package/dist/server/metadata.js +9 -2
  292. package/dist/server/normaliseContentRelationships.d.ts +7 -0
  293. package/dist/server/normaliseContentRelationships.js +62 -0
  294. package/dist/server/pageKindSeoContract.d.ts +27 -0
  295. package/dist/server/pageKindSeoContract.js +80 -0
  296. package/dist/server/recordContentChange.d.ts +1 -1
  297. package/dist/server/recordContentChange.js +72 -13
  298. package/dist/server/resolveExperimentBlocks.d.ts +5 -0
  299. package/dist/server/resolveExperimentBlocks.js +54 -0
  300. package/dist/server/runResults.js +92 -19
  301. package/dist/server/toClientAssignments.js +2 -1
  302. package/dist/transitions/addMetric.d.ts +2 -1
  303. package/dist/transitions/addMetric.js +28 -1
  304. package/dist/transitions/checkStart.d.ts +17 -1
  305. package/dist/transitions/checkStart.js +99 -7
  306. package/dist/transitions/decide.js +40 -1
  307. package/dist/transitions/deleteExperiment.d.ts +3 -1
  308. package/dist/transitions/deleteExperiment.js +27 -1
  309. package/dist/transitions/kernel.js +4 -0
  310. package/dist/transitions/makeContent.d.ts +5 -0
  311. package/dist/transitions/makeContent.js +39 -11
  312. package/dist/transitions/pageCollection.d.ts +10 -0
  313. package/dist/transitions/pageCollection.js +20 -0
  314. package/dist/transitions/pageVersions.d.ts +10 -0
  315. package/dist/transitions/pageVersions.js +44 -0
  316. package/dist/transitions/phaseRecord.d.ts +1 -0
  317. package/dist/transitions/placementInteractions.d.ts +40 -0
  318. package/dist/transitions/placementInteractions.js +94 -0
  319. package/dist/transitions/placements.d.ts +10 -6
  320. package/dist/transitions/placements.js +156 -2
  321. package/dist/transitions/preflightData.d.ts +8 -0
  322. package/dist/transitions/preflightData.js +248 -36
  323. package/dist/transitions/resolveCaps.d.ts +1 -0
  324. package/dist/transitions/resolveCaps.js +6 -2
  325. package/dist/transitions/start.js +77 -3
  326. package/dist/types/admin.d.ts +1 -1
  327. package/dist/types/assignments.d.ts +1 -0
  328. package/dist/types/contracts.d.ts +31 -10
  329. package/dist/types/endpoints.d.ts +92 -5
  330. package/dist/types/index.d.ts +3 -0
  331. package/dist/types/puck.d.ts +7 -1
  332. package/dist/types/snapshot.d.ts +17 -4
  333. package/dist/types/surface.d.ts +13 -0
  334. package/dist/ui/Tabs.d.ts +4 -2
  335. package/dist/ui/Tabs.js +33 -26
  336. package/dist/ui/accent.d.ts +22 -3
  337. package/dist/ui/accent.js +74 -16
  338. package/package.json +12 -2
@@ -1,5 +1,5 @@
1
1
  import { editorCopy } from './copy.js';
2
- import { isSlugCollision, newTestBody, slugify, uniqueSlug } from './editor.model.js';
2
+ import { isSlugCollision, newTestBody, slugify, slugCandidate } from './editor.model.js';
3
3
  /** Only a server refusal may supply editor-facing error text. */ export class CreateRefused extends Error {
4
4
  }
5
5
  function refusalMessage(body) {
@@ -7,20 +7,31 @@ function refusalMessage(body) {
7
7
  const message = error?.errors?.[0]?.data?.errors?.[0]?.message;
8
8
  return typeof message === 'string' && message.trim() ? message : editorCopy.createError;
9
9
  }
10
- /** Shared lookup, complete draft creation and one retry for a racing slug collision. */ export async function createTest(apiRoute, input) {
11
- const api = apiRoute.replace(/\/$/, '');
10
+ /** Shared lookup, complete draft creation and one retry for a racing slug collision. */ export async function createTest(api, input) {
12
11
  const base = slugify(input.name) || 'test';
12
+ let next = 1;
13
13
  for(let attempt = 0;; attempt++){
14
- // Suffixes shorten a 64-character base; include those neighbouring slugs in the lookup.
15
- const lookup = await fetch(`${api}/xp-experiments?where[slug][like]=${encodeURIComponent(base.slice(0, 60))}&depth=0&limit=200&select[slug]=true`, {
16
- credentials: 'include'
17
- });
18
- const found = await lookup.json();
19
- if (!lookup.ok) throw new CreateRefused(refusalMessage(found));
20
- if (!Array.isArray(found?.docs)) throw new Error(editorCopy.createError);
21
- const taken = found.docs.flatMap((doc)=>typeof doc.slug === 'string' ? [
22
- doc.slug
23
- ] : []);
14
+ let slug;
15
+ while(slug === undefined){
16
+ const candidates = Array.from({
17
+ length: 50
18
+ }, (_, offset)=>slugCandidate(base, next + offset));
19
+ const lookup = await fetch(`${api}/xp-experiments?where[slug][in]=${encodeURIComponent(candidates.join(','))}&limit=50&depth=0&select[slug]=true`, {
20
+ credentials: 'include'
21
+ });
22
+ const found = await lookup.json();
23
+ if (!lookup.ok) throw new CreateRefused(refusalMessage(found));
24
+ if (!Array.isArray(found?.docs)) throw new Error(editorCopy.createError);
25
+ const taken = new Set(found.docs.flatMap((doc)=>typeof doc.slug === 'string' ? [
26
+ doc.slug
27
+ ] : []));
28
+ const free = candidates.findIndex((candidate)=>!taken.has(candidate));
29
+ if (free === -1) next += candidates.length;
30
+ else {
31
+ slug = candidates[free];
32
+ next += free + 1;
33
+ }
34
+ }
24
35
  const response = await fetch(`${api}/xp-experiments`, {
25
36
  method: 'POST',
26
37
  credentials: 'include',
@@ -29,7 +40,7 @@ function refusalMessage(body) {
29
40
  },
30
41
  body: JSON.stringify(newTestBody({
31
42
  ...input,
32
- slug: uniqueSlug(base, taken)
43
+ slug
33
44
  }))
34
45
  });
35
46
  const body = await response.json();
@@ -1,35 +1,28 @@
1
- /* These controls also render in the host's preflight-reset editor iframe. */
1
+ /* Shared UI controls are assembled into editor.css for the isolated canvas. */
2
2
  .xp-slot-editor {
3
3
  min-width: 0;
4
- }
5
-
6
- .xp-slot-chrome,
7
- .xp-slot-drawer {
8
- box-sizing: border-box;
9
- background: var(--xp-bg);
10
4
  color: var(--xp-text);
11
- border: 1px solid var(--xp-border);
12
- border-radius: var(--xp-radius-large);
13
- padding: var(--xp-space-3);
14
5
  font-family: var(--xp-font-body);
15
6
  font-size: var(--xp-font-size-body);
16
7
  line-height: 1.5;
17
8
  }
18
9
 
19
- .xp-slot-header,
20
- .xp-slot-toolbar,
21
- .xp-slot-actions {
10
+ .xp-slot-chrome {
11
+ display: grid;
12
+ gap: var(--xp-space-3);
13
+ padding: var(--xp-space-4);
14
+ background: var(--xp-surface);
15
+ border: 1px solid var(--xp-border);
16
+ border-radius: var(--xp-radius-card) var(--xp-radius-card) 0 0;
17
+ }
18
+
19
+ .xp-slot-header {
22
20
  display: flex;
23
21
  flex-wrap: wrap;
24
22
  align-items: center;
25
23
  gap: var(--xp-space-2);
26
24
  }
27
25
 
28
- .xp-slot-header > .xp-slot-actions,
29
- .xp-slot-toolbar > .xp-slot-actions {
30
- margin-inline-start: auto;
31
- }
32
-
33
26
  .xp-slot-title {
34
27
  margin: 0;
35
28
  color: var(--xp-text);
@@ -38,125 +31,14 @@
38
31
  overflow-wrap: anywhere;
39
32
  }
40
33
 
41
- .xp-slot-status {
42
- display: inline-flex;
43
- padding: var(--xp-space-1) var(--xp-space-2);
44
- border: 1px solid var(--xp-border-strong);
45
- border-radius: var(--xp-radius-large);
34
+ .xp-slot-editor > .xp-ui-tabs > .xp-ui-tabs-header {
35
+ padding: var(--xp-space-2) var(--xp-space-4);
46
36
  background: var(--xp-surface);
47
- color: var(--xp-text-strong);
48
- font-size: var(--xp-font-size-small);
49
- line-height: 1.2;
50
- }
51
-
52
- .xp-slot-status[data-status='running'] {
53
- background: var(--xp-success-surface);
54
- border-color: var(--xp-success);
55
- color: var(--xp-success-text-strong);
56
- }
57
-
58
- .xp-slot-notice {
59
- margin: var(--xp-space-3) 0;
60
- color: var(--xp-text-muted);
61
- }
62
-
63
- .xp-slot-warning {
64
- display: flex;
65
- align-items: flex-start;
66
- gap: var(--xp-space-2);
67
- border: 1px solid var(--xp-warning);
68
- border-radius: var(--xp-radius);
69
- padding: var(--xp-space-2) var(--xp-space-3);
70
- background: var(--xp-warning-surface);
71
- color: var(--xp-warning-text-strong);
72
- }
73
-
74
- .xp-slot-warning-icon {
75
- flex: 0 0 auto;
76
- width: var(--xp-space-4);
77
- height: var(--xp-space-4);
78
- }
79
-
80
- .xp-slot-button,
81
- .xp-slot-tab,
82
- .xp-slot-input {
83
- box-sizing: border-box;
84
- appearance: none;
85
- background: var(--xp-bg);
86
- border: 1px solid var(--xp-input-border);
87
- padding: var(--xp-space-2) var(--xp-space-3);
88
- border-radius: var(--xp-radius);
89
- font: inherit;
90
- font-size: var(--xp-font-size-body);
91
- color: var(--xp-text);
92
- cursor: pointer;
93
- line-height: 1.4;
94
- }
95
-
96
- .xp-slot-button {
97
- display: inline-flex;
98
- align-items: center;
99
- justify-content: center;
100
- gap: var(--xp-space-1);
101
- text-decoration: none;
102
- text-align: center;
37
+ border-inline: 1px solid var(--xp-border);
103
38
  }
104
39
 
105
- .xp-slot-button:hover:not(:disabled),
106
- .xp-slot-tab:hover:not(:disabled) {
107
- background: var(--xp-surface-raised);
108
- border-color: var(--xp-border-accent);
109
- }
110
-
111
- .xp-slot-button-primary {
112
- background: var(--xp-text-emphasis);
113
- border-color: var(--xp-text-emphasis);
114
- color: var(--xp-bg);
115
- font-weight: 600;
116
- }
117
-
118
- .xp-slot-button-primary:hover:not(:disabled) {
119
- background: var(--xp-text-strong);
120
- border-color: var(--xp-text-strong);
121
- }
122
-
123
- .xp-slot-button:focus-visible,
124
- .xp-slot-tab:focus-visible,
125
- .xp-slot-input:focus-visible {
126
- outline: 2px solid var(--xp-text);
127
- outline-offset: 2px;
128
- }
129
-
130
- .xp-slot-button:disabled,
131
- .xp-slot-tab:disabled,
132
- .xp-slot-input:disabled {
133
- background: var(--xp-surface);
134
- border-color: var(--xp-border);
135
- color: var(--xp-text-muted);
136
- cursor: not-allowed;
137
- opacity: 0.65;
138
- }
139
-
140
- .xp-slot-tabs {
141
- display: flex;
142
- flex-wrap: wrap;
143
- gap: var(--xp-space-1);
144
- padding: var(--xp-space-1);
145
- border-radius: var(--xp-radius-large);
146
- background: var(--xp-surface);
147
- }
148
-
149
- .xp-slot-tab {
150
- background: transparent;
151
- border-color: transparent;
152
- }
153
-
154
- .xp-slot-tab[aria-selected='true'] {
155
- background: var(--xp-bg);
156
- border-color: var(--xp-border-accent);
157
- color: var(--xp-text-emphasis);
158
- font-weight: 600;
159
- box-shadow: 0 2px 0 var(--xp-text-emphasis);
40
+ .xp-slot-editor .xp-ui-tabpanel {
41
+ padding: 0;
160
42
  }
161
43
 
162
44
  .xp-slot-panel {
@@ -198,38 +80,43 @@
198
80
 
199
81
  .xp-slot-drawer {
200
82
  display: grid;
201
- gap: var(--xp-space-3);
202
- margin-block: var(--xp-space-3);
203
- padding: var(--xp-space-4);
204
- border-color: var(--xp-border-accent);
205
- background: var(--xp-surface);
206
- }
207
-
208
- .xp-slot-field {
209
- display: grid;
210
- gap: var(--xp-space-1);
211
- }
212
-
213
- .xp-slot-label {
214
- font-weight: 600;
215
- }
216
-
217
- .xp-slot-input {
218
- width: 100%;
219
- min-width: 0;
220
- background: var(--xp-input-bg);
221
- cursor: text;
222
- }
223
-
224
- .xp-slot-error {
225
- margin: 0;
226
- color: var(--xp-error-text);
83
+ gap: var(--xp-space-4);
227
84
  }
228
85
 
229
86
  .xp-slot-confirm {
230
87
  padding: var(--xp-space-2) var(--xp-space-3);
231
- margin-block: var(--xp-space-3);
232
88
  border: 1px solid var(--xp-warning);
233
89
  border-radius: var(--xp-radius);
234
90
  background: var(--xp-warning-surface);
235
91
  }
92
+
93
+ /* Tailwind's preflight runs before these controls in the canvas document. */
94
+ .xp-slot-editor .xp-ui-button,
95
+ .xp-slot-editor .xp-ui-tab,
96
+ .xp-slot-editor .xp-ui-input {
97
+ appearance: none;
98
+ }
99
+
100
+ /* Restore native modal centering after the host's universal margin reset. */
101
+ .xp-slot-editor .xp-ui-dialog {
102
+ margin: auto;
103
+ }
104
+
105
+ .xp-slot-editor .xp-ui-tabs-header { display: flex; flex-wrap: wrap; align-items: stretch; }
106
+ .xp-slot-version-actions { display: contents; }
107
+ .xp-slot-version-anchor { position: relative; display: flex; align-items: center; border-bottom: 1px solid var(--xp-border); }
108
+ .xp-slot-version-rename { flex: 1 0 100%; min-width: 0; padding-top: var(--xp-space-2); }
109
+ .xp-slot-version-menu {
110
+ position: absolute; top: 100%; inset-inline-end: 0; z-index: 5;
111
+ display: grid; min-width: 180px; padding: var(--xp-space-2);
112
+ box-shadow: 0 8px 24px var(--xp-overlay);
113
+ background: var(--xp-surface); border: 1px solid var(--xp-border); border-radius: var(--xp-radius);
114
+ }
115
+ .xp-slot-version-menu .xp-ui-button { justify-content: start; }
116
+ .xp-slot-weight-inputs, .xp-slot-people { display: flex; flex-wrap: wrap; gap: var(--xp-space-3); }
117
+ .xp-slot-weight-inputs { align-items: flex-start; }
118
+ .xp-slot-weight-inputs .xp-ui-field { flex: 1 1 120px; max-width: 200px; }
119
+ .xp-slot-readout { display: grid; gap: var(--xp-space-2); }
120
+ .xp-slot-readout > .xp-ui-button { justify-self: start; }
121
+ .xp-slot-editor .xp-ui-tablist { flex: 1 1 auto; min-width: 0; flex-wrap: wrap; }
122
+ .xp-slot-root.xp-puck-field { display: grid; gap: var(--xp-space-3); min-width: 0; }
@@ -263,38 +263,31 @@
263
263
  .xp-ui-field-label { display: flex; align-items: center; justify-content: space-between; gap: var(--xp-space-3); }
264
264
  .xp-ui-field-label > label { font-weight: 500; }
265
265
 
266
- /* These controls also render in the host's preflight-reset editor iframe. */
266
+ /* Shared UI controls are assembled into editor.css for the isolated canvas. */
267
267
  .xp-slot-editor {
268
268
  min-width: 0;
269
- }
270
-
271
- .xp-slot-chrome,
272
- .xp-slot-drawer {
273
- box-sizing: border-box;
274
- background: var(--xp-bg);
275
269
  color: var(--xp-text);
276
- border: 1px solid var(--xp-border);
277
- border-radius: var(--xp-radius-large);
278
- padding: var(--xp-space-3);
279
270
  font-family: var(--xp-font-body);
280
271
  font-size: var(--xp-font-size-body);
281
272
  line-height: 1.5;
282
273
  }
283
274
 
284
- .xp-slot-header,
285
- .xp-slot-toolbar,
286
- .xp-slot-actions {
275
+ .xp-slot-chrome {
276
+ display: grid;
277
+ gap: var(--xp-space-3);
278
+ padding: var(--xp-space-4);
279
+ background: var(--xp-surface);
280
+ border: 1px solid var(--xp-border);
281
+ border-radius: var(--xp-radius-card) var(--xp-radius-card) 0 0;
282
+ }
283
+
284
+ .xp-slot-header {
287
285
  display: flex;
288
286
  flex-wrap: wrap;
289
287
  align-items: center;
290
288
  gap: var(--xp-space-2);
291
289
  }
292
290
 
293
- .xp-slot-header > .xp-slot-actions,
294
- .xp-slot-toolbar > .xp-slot-actions {
295
- margin-inline-start: auto;
296
- }
297
-
298
291
  .xp-slot-title {
299
292
  margin: 0;
300
293
  color: var(--xp-text);
@@ -303,125 +296,14 @@
303
296
  overflow-wrap: anywhere;
304
297
  }
305
298
 
306
- .xp-slot-status {
307
- display: inline-flex;
308
- padding: var(--xp-space-1) var(--xp-space-2);
309
- border: 1px solid var(--xp-border-strong);
310
- border-radius: var(--xp-radius-large);
299
+ .xp-slot-editor > .xp-ui-tabs > .xp-ui-tabs-header {
300
+ padding: var(--xp-space-2) var(--xp-space-4);
311
301
  background: var(--xp-surface);
312
- color: var(--xp-text-strong);
313
- font-size: var(--xp-font-size-small);
314
- line-height: 1.2;
315
- }
316
-
317
- .xp-slot-status[data-status='running'] {
318
- background: var(--xp-success-surface);
319
- border-color: var(--xp-success);
320
- color: var(--xp-success-text-strong);
321
- }
322
-
323
- .xp-slot-notice {
324
- margin: var(--xp-space-3) 0;
325
- color: var(--xp-text-muted);
326
- }
327
-
328
- .xp-slot-warning {
329
- display: flex;
330
- align-items: flex-start;
331
- gap: var(--xp-space-2);
332
- border: 1px solid var(--xp-warning);
333
- border-radius: var(--xp-radius);
334
- padding: var(--xp-space-2) var(--xp-space-3);
335
- background: var(--xp-warning-surface);
336
- color: var(--xp-warning-text-strong);
337
- }
338
-
339
- .xp-slot-warning-icon {
340
- flex: 0 0 auto;
341
- width: var(--xp-space-4);
342
- height: var(--xp-space-4);
343
- }
344
-
345
- .xp-slot-button,
346
- .xp-slot-tab,
347
- .xp-slot-input {
348
- box-sizing: border-box;
349
- appearance: none;
350
- background: var(--xp-bg);
351
- border: 1px solid var(--xp-input-border);
352
- padding: var(--xp-space-2) var(--xp-space-3);
353
- border-radius: var(--xp-radius);
354
- font: inherit;
355
- font-size: var(--xp-font-size-body);
356
- color: var(--xp-text);
357
- cursor: pointer;
358
- line-height: 1.4;
359
- }
360
-
361
- .xp-slot-button {
362
- display: inline-flex;
363
- align-items: center;
364
- justify-content: center;
365
- gap: var(--xp-space-1);
366
- text-decoration: none;
367
- text-align: center;
302
+ border-inline: 1px solid var(--xp-border);
368
303
  }
369
304
 
370
- .xp-slot-button:hover:not(:disabled),
371
- .xp-slot-tab:hover:not(:disabled) {
372
- background: var(--xp-surface-raised);
373
- border-color: var(--xp-border-accent);
374
- }
375
-
376
- .xp-slot-button-primary {
377
- background: var(--xp-text-emphasis);
378
- border-color: var(--xp-text-emphasis);
379
- color: var(--xp-bg);
380
- font-weight: 600;
381
- }
382
-
383
- .xp-slot-button-primary:hover:not(:disabled) {
384
- background: var(--xp-text-strong);
385
- border-color: var(--xp-text-strong);
386
- }
387
-
388
- .xp-slot-button:focus-visible,
389
- .xp-slot-tab:focus-visible,
390
- .xp-slot-input:focus-visible {
391
- outline: 2px solid var(--xp-text);
392
- outline-offset: 2px;
393
- }
394
-
395
- .xp-slot-button:disabled,
396
- .xp-slot-tab:disabled,
397
- .xp-slot-input:disabled {
398
- background: var(--xp-surface);
399
- border-color: var(--xp-border);
400
- color: var(--xp-text-muted);
401
- cursor: not-allowed;
402
- opacity: 0.65;
403
- }
404
-
405
- .xp-slot-tabs {
406
- display: flex;
407
- flex-wrap: wrap;
408
- gap: var(--xp-space-1);
409
- padding: var(--xp-space-1);
410
- border-radius: var(--xp-radius-large);
411
- background: var(--xp-surface);
412
- }
413
-
414
- .xp-slot-tab {
415
- background: transparent;
416
- border-color: transparent;
417
- }
418
-
419
- .xp-slot-tab[aria-selected='true'] {
420
- background: var(--xp-bg);
421
- border-color: var(--xp-border-accent);
422
- color: var(--xp-text-emphasis);
423
- font-weight: 600;
424
- box-shadow: 0 2px 0 var(--xp-text-emphasis);
305
+ .xp-slot-editor .xp-ui-tabpanel {
306
+ padding: 0;
425
307
  }
426
308
 
427
309
  .xp-slot-panel {
@@ -463,38 +345,43 @@
463
345
 
464
346
  .xp-slot-drawer {
465
347
  display: grid;
466
- gap: var(--xp-space-3);
467
- margin-block: var(--xp-space-3);
468
- padding: var(--xp-space-4);
469
- border-color: var(--xp-border-accent);
470
- background: var(--xp-surface);
471
- }
472
-
473
- .xp-slot-field {
474
- display: grid;
475
- gap: var(--xp-space-1);
476
- }
477
-
478
- .xp-slot-label {
479
- font-weight: 600;
480
- }
481
-
482
- .xp-slot-input {
483
- width: 100%;
484
- min-width: 0;
485
- background: var(--xp-input-bg);
486
- cursor: text;
487
- }
488
-
489
- .xp-slot-error {
490
- margin: 0;
491
- color: var(--xp-error-text);
348
+ gap: var(--xp-space-4);
492
349
  }
493
350
 
494
351
  .xp-slot-confirm {
495
352
  padding: var(--xp-space-2) var(--xp-space-3);
496
- margin-block: var(--xp-space-3);
497
353
  border: 1px solid var(--xp-warning);
498
354
  border-radius: var(--xp-radius);
499
355
  background: var(--xp-warning-surface);
500
356
  }
357
+
358
+ /* Tailwind's preflight runs before these controls in the canvas document. */
359
+ .xp-slot-editor .xp-ui-button,
360
+ .xp-slot-editor .xp-ui-tab,
361
+ .xp-slot-editor .xp-ui-input {
362
+ appearance: none;
363
+ }
364
+
365
+ /* Restore native modal centering after the host's universal margin reset. */
366
+ .xp-slot-editor .xp-ui-dialog {
367
+ margin: auto;
368
+ }
369
+
370
+ .xp-slot-editor .xp-ui-tabs-header { display: flex; flex-wrap: wrap; align-items: stretch; }
371
+ .xp-slot-version-actions { display: contents; }
372
+ .xp-slot-version-anchor { position: relative; display: flex; align-items: center; border-bottom: 1px solid var(--xp-border); }
373
+ .xp-slot-version-rename { flex: 1 0 100%; min-width: 0; padding-top: var(--xp-space-2); }
374
+ .xp-slot-version-menu {
375
+ position: absolute; top: 100%; inset-inline-end: 0; z-index: 5;
376
+ display: grid; min-width: 180px; padding: var(--xp-space-2);
377
+ box-shadow: 0 8px 24px var(--xp-overlay);
378
+ background: var(--xp-surface); border: 1px solid var(--xp-border); border-radius: var(--xp-radius);
379
+ }
380
+ .xp-slot-version-menu .xp-ui-button { justify-content: start; }
381
+ .xp-slot-weight-inputs, .xp-slot-people { display: flex; flex-wrap: wrap; gap: var(--xp-space-3); }
382
+ .xp-slot-weight-inputs { align-items: flex-start; }
383
+ .xp-slot-weight-inputs .xp-ui-field { flex: 1 1 120px; max-width: 200px; }
384
+ .xp-slot-readout { display: grid; gap: var(--xp-space-2); }
385
+ .xp-slot-readout > .xp-ui-button { justify-self: start; }
386
+ .xp-slot-editor .xp-ui-tablist { flex: 1 1 auto; min-width: 0; flex-wrap: wrap; }
387
+ .xp-slot-root.xp-puck-field { display: grid; gap: var(--xp-space-3); min-width: 0; }
@@ -2,8 +2,9 @@ import type { Arm, DocId, Status, UnitType } from '../../types/contracts.js';
2
2
  import type { ArmSlot } from '../../types/puck.js';
3
3
  export type EditorExperiment = {
4
4
  name: string;
5
+ updatedAt?: string;
5
6
  status: Status;
6
- arms: Array<Pick<Arm, 'key' | 'kind' | 'label'>>;
7
+ arms: Array<Pick<Arm, 'key' | 'kind' | 'label'> & Partial<Arm>>;
7
8
  decision?: {
8
9
  winnerArm?: string;
9
10
  } | null;
@@ -16,10 +17,11 @@ export declare function bannerFor(status?: Status | null, winner?: string): stri
16
17
  export declare function slugify(name: string): string;
17
18
  /** Complete draft defaults, shared by the page hand-off and setting creation. */
18
19
  export declare function newTestBody(input: {
19
- kind: 'block' | 'flag';
20
+ kind: 'block' | 'flag' | 'page';
20
21
  name: string;
21
22
  slug: string;
22
23
  unitType: UnitType;
24
+ pageCollection?: string;
23
25
  flagPoint?: string;
24
26
  layer?: DocId;
25
27
  hypothesis?: string;
@@ -27,9 +29,10 @@ export declare function newTestBody(input: {
27
29
  hypothesis?: string | undefined;
28
30
  layer?: DocId | undefined;
29
31
  flagPoint?: string | undefined;
32
+ pageCollection?: string | undefined;
30
33
  name: string;
31
34
  slug: string;
32
- kind: "block" | "flag";
35
+ kind: "block" | "page" | "flag";
33
36
  unitType: UnitType;
34
37
  exposurePolicy: string;
35
38
  arms: Arm[];
@@ -38,6 +41,8 @@ export declare function newTestBody(input: {
38
41
  plannedDurationDays: number;
39
42
  };
40
43
  };
44
+ /** Candidate order shared by local naming and the bounded REST slug probes. */
45
+ export declare function slugCandidate(base: string, index: number): string;
41
46
  export declare function uniqueSlug(base: string, taken: readonly string[]): string;
42
47
  /** Payload nests field validation errors under data.errors. Only a unique slug refusal is retryable. */
43
48
  export declare function isSlugCollision(status: number, body: unknown): boolean;
@@ -9,9 +9,9 @@ export function tabsFor(experiment) {
9
9
  return experiment.arms.flatMap((arm)=>arm.kind === 'holdout' || arm.key === 'holdout' ? [] : [
10
10
  {
11
11
  slot: arm.key,
12
- label: arm.label?.trim() ? arm.label : editorCopy.labels[arm.key]
12
+ label: arm.label.trim() || editorCopy.labels[arm.key]
13
13
  }
14
- ]);
14
+ ]).sort((a, b)=>Number(b.slot === 'control') - Number(a.slot === 'control'));
15
15
  }
16
16
  export function bannerFor(status, winner = editorCopy.labels.control) {
17
17
  switch(status){
@@ -32,7 +32,7 @@ export function slugify(name) {
32
32
  return name.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 64).replace(/-+$/g, '');
33
33
  }
34
34
  /** Complete draft defaults, shared by the page hand-off and setting creation. */ export function newTestBody(input) {
35
- const { kind, name, slug, unitType, flagPoint, layer, hypothesis } = input;
35
+ const { kind, name, slug, unitType, flagPoint, layer, hypothesis, pageCollection } = input;
36
36
  const arms = [
37
37
  {
38
38
  key: 'control',
@@ -57,13 +57,16 @@ export function slugify(name) {
57
57
  name,
58
58
  slug,
59
59
  kind,
60
- unitType,
61
- exposurePolicy: kind === 'block' ? 'render' : 'manual',
60
+ unitType: kind === 'page' ? 'visitor' : unitType,
61
+ exposurePolicy: kind === 'block' ? 'render' : kind === 'page' ? 'pageview' : 'manual',
62
62
  arms,
63
63
  allocation: 100,
64
64
  analysis: {
65
65
  plannedDurationDays: 28
66
66
  },
67
+ ...pageCollection === undefined ? {} : {
68
+ pageCollection
69
+ },
67
70
  ...flagPoint === undefined ? {} : {
68
71
  flagPoint
69
72
  },
@@ -75,13 +78,16 @@ export function slugify(name) {
75
78
  }
76
79
  };
77
80
  }
78
- export function uniqueSlug(base, taken) {
81
+ /** Candidate order shared by local naming and the bounded REST slug probes. */ export function slugCandidate(base, index) {
79
82
  const root = base.slice(0, 64).replace(/-+$/g, '') || 'test';
83
+ if (index === 1) return root;
84
+ const suffix = `-${index}`;
85
+ return root.slice(0, 64 - suffix.length).replace(/-+$/g, '') + suffix;
86
+ }
87
+ export function uniqueSlug(base, taken) {
80
88
  const used = new Set(taken);
81
- if (!used.has(root)) return root;
82
- for(let index = 2;; index++){
83
- const suffix = `-${index}`;
84
- const candidate = root.slice(0, 64 - suffix.length).replace(/-+$/g, '') + suffix;
89
+ for(let index = 1;; index++){
90
+ const candidate = slugCandidate(base, index);
85
91
  if (!used.has(candidate)) return candidate;
86
92
  }
87
93
  }
@@ -0,0 +1,3 @@
1
+ import { type Plugin } from '@puckeditor/core';
2
+ /** Add to Puck's plugins so its outline wrapper composes with the host's overrides. */
3
+ export declare function experimentOutlinePlugin(): Plugin;