@proofhound/web-ui 0.1.6

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 (483) hide show
  1. package/LICENSE +190 -0
  2. package/dist/components/annotations/annotation-claim-dialog.d.ts +13 -0
  3. package/dist/components/annotations/annotation-claim-dialog.d.ts.map +1 -0
  4. package/dist/components/annotations/annotation-claim-dialog.js +60 -0
  5. package/dist/components/annotations/annotation-claim-dialog.js.map +1 -0
  6. package/dist/components/charts/source-legend.d.ts +10 -0
  7. package/dist/components/charts/source-legend.d.ts.map +1 -0
  8. package/dist/components/charts/source-legend.js +15 -0
  9. package/dist/components/charts/source-legend.js.map +1 -0
  10. package/dist/components/charts/source-stacked-bar.d.ts +22 -0
  11. package/dist/components/charts/source-stacked-bar.d.ts.map +1 -0
  12. package/dist/components/charts/source-stacked-bar.js +43 -0
  13. package/dist/components/charts/source-stacked-bar.js.map +1 -0
  14. package/dist/components/index.d.ts +15 -0
  15. package/dist/components/index.d.ts.map +1 -0
  16. package/dist/components/index.js +17 -0
  17. package/dist/components/index.js.map +1 -0
  18. package/dist/components/json-object-textarea.d.ts +11 -0
  19. package/dist/components/json-object-textarea.d.ts.map +1 -0
  20. package/dist/components/json-object-textarea.js +82 -0
  21. package/dist/components/json-object-textarea.js.map +1 -0
  22. package/dist/components/model-context-window-input.d.ts +15 -0
  23. package/dist/components/model-context-window-input.d.ts.map +1 -0
  24. package/dist/components/model-context-window-input.js +57 -0
  25. package/dist/components/model-context-window-input.js.map +1 -0
  26. package/dist/components/model-probe-status.d.ts +11 -0
  27. package/dist/components/model-probe-status.d.ts.map +1 -0
  28. package/dist/components/model-probe-status.js +34 -0
  29. package/dist/components/model-probe-status.js.map +1 -0
  30. package/dist/components/prompt-diff/prompt-diff-split-view.d.ts +28 -0
  31. package/dist/components/prompt-diff/prompt-diff-split-view.d.ts.map +1 -0
  32. package/dist/components/prompt-diff/prompt-diff-split-view.js +101 -0
  33. package/dist/components/prompt-diff/prompt-diff-split-view.js.map +1 -0
  34. package/dist/components/prompt-language-select.d.ts +13 -0
  35. package/dist/components/prompt-language-select.d.ts.map +1 -0
  36. package/dist/components/prompt-language-select.js +14 -0
  37. package/dist/components/prompt-language-select.js.map +1 -0
  38. package/dist/components/prompt-version-picker-row.d.ts +27 -0
  39. package/dist/components/prompt-version-picker-row.d.ts.map +1 -0
  40. package/dist/components/prompt-version-picker-row.js +50 -0
  41. package/dist/components/prompt-version-picker-row.js.map +1 -0
  42. package/dist/components/prompt-version-status-badge.d.ts +8 -0
  43. package/dist/components/prompt-version-status-badge.d.ts.map +1 -0
  44. package/dist/components/prompt-version-status-badge.js +31 -0
  45. package/dist/components/prompt-version-status-badge.js.map +1 -0
  46. package/dist/components/quick-fill/quick-fill-picker.d.ts +42 -0
  47. package/dist/components/quick-fill/quick-fill-picker.d.ts.map +1 -0
  48. package/dist/components/quick-fill/quick-fill-picker.js +46 -0
  49. package/dist/components/quick-fill/quick-fill-picker.js.map +1 -0
  50. package/dist/components/time-zone-preference-menu.d.ts +10 -0
  51. package/dist/components/time-zone-preference-menu.d.ts.map +1 -0
  52. package/dist/components/time-zone-preference-menu.js +33 -0
  53. package/dist/components/time-zone-preference-menu.js.map +1 -0
  54. package/dist/contracts/index.d.ts +13 -0
  55. package/dist/contracts/index.d.ts.map +1 -0
  56. package/dist/contracts/index.js +7 -0
  57. package/dist/contracts/index.js.map +1 -0
  58. package/dist/features/index.d.ts +3 -0
  59. package/dist/features/index.d.ts.map +1 -0
  60. package/dist/features/index.js +4 -0
  61. package/dist/features/index.js.map +1 -0
  62. package/dist/features/model-quick-fill/model-preset-draft.d.ts +19 -0
  63. package/dist/features/model-quick-fill/model-preset-draft.d.ts.map +1 -0
  64. package/dist/features/model-quick-fill/model-preset-draft.js +22 -0
  65. package/dist/features/model-quick-fill/model-preset-draft.js.map +1 -0
  66. package/dist/features/model-quick-fill/model-preset-quick-fill.d.ts +7 -0
  67. package/dist/features/model-quick-fill/model-preset-quick-fill.d.ts.map +1 -0
  68. package/dist/features/model-quick-fill/model-preset-quick-fill.js +73 -0
  69. package/dist/features/model-quick-fill/model-preset-quick-fill.js.map +1 -0
  70. package/dist/hooks/annotation.d.ts +253 -0
  71. package/dist/hooks/annotation.d.ts.map +1 -0
  72. package/dist/hooks/annotation.js +79 -0
  73. package/dist/hooks/annotation.js.map +1 -0
  74. package/dist/hooks/canary-release.d.ts +674 -0
  75. package/dist/hooks/canary-release.d.ts.map +1 -0
  76. package/dist/hooks/canary-release.js +142 -0
  77. package/dist/hooks/canary-release.js.map +1 -0
  78. package/dist/hooks/connector.d.ts +585 -0
  79. package/dist/hooks/connector.d.ts.map +1 -0
  80. package/dist/hooks/connector.js +157 -0
  81. package/dist/hooks/connector.js.map +1 -0
  82. package/dist/hooks/dataset.d.ts +138 -0
  83. package/dist/hooks/dataset.d.ts.map +1 -0
  84. package/dist/hooks/dataset.js +84 -0
  85. package/dist/hooks/dataset.js.map +1 -0
  86. package/dist/hooks/experiment.d.ts +376 -0
  87. package/dist/hooks/experiment.d.ts.map +1 -0
  88. package/dist/hooks/experiment.js +58 -0
  89. package/dist/hooks/experiment.js.map +1 -0
  90. package/dist/hooks/index.d.ts +18 -0
  91. package/dist/hooks/index.d.ts.map +1 -0
  92. package/dist/hooks/index.js +18 -0
  93. package/dist/hooks/index.js.map +1 -0
  94. package/dist/hooks/model.d.ts +327 -0
  95. package/dist/hooks/model.d.ts.map +1 -0
  96. package/dist/hooks/model.js +103 -0
  97. package/dist/hooks/model.js.map +1 -0
  98. package/dist/hooks/optimization.d.ts +557 -0
  99. package/dist/hooks/optimization.d.ts.map +1 -0
  100. package/dist/hooks/optimization.js +94 -0
  101. package/dist/hooks/optimization.js.map +1 -0
  102. package/dist/hooks/production-release.d.ts +141 -0
  103. package/dist/hooks/production-release.d.ts.map +1 -0
  104. package/dist/hooks/production-release.js +51 -0
  105. package/dist/hooks/production-release.js.map +1 -0
  106. package/dist/hooks/project-monitoring.d.ts +199 -0
  107. package/dist/hooks/project-monitoring.d.ts.map +1 -0
  108. package/dist/hooks/project-monitoring.js +55 -0
  109. package/dist/hooks/project-monitoring.js.map +1 -0
  110. package/dist/hooks/prompt.d.ts +509 -0
  111. package/dist/hooks/prompt.d.ts.map +1 -0
  112. package/dist/hooks/prompt.js +127 -0
  113. package/dist/hooks/prompt.js.map +1 -0
  114. package/dist/hooks/quick-start.d.ts +219 -0
  115. package/dist/hooks/quick-start.d.ts.map +1 -0
  116. package/dist/hooks/quick-start.js +35 -0
  117. package/dist/hooks/quick-start.js.map +1 -0
  118. package/dist/hooks/release-line.d.ts +819 -0
  119. package/dist/hooks/release-line.d.ts.map +1 -0
  120. package/dist/hooks/release-line.js +52 -0
  121. package/dist/hooks/release-line.js.map +1 -0
  122. package/dist/hooks/run-result.d.ts +118 -0
  123. package/dist/hooks/run-result.d.ts.map +1 -0
  124. package/dist/hooks/run-result.js +56 -0
  125. package/dist/hooks/run-result.js.map +1 -0
  126. package/dist/hooks/token.d.ts +58 -0
  127. package/dist/hooks/token.d.ts.map +1 -0
  128. package/dist/hooks/token.js +39 -0
  129. package/dist/hooks/token.js.map +1 -0
  130. package/dist/hooks/use-auto-refresh.d.ts +30 -0
  131. package/dist/hooks/use-auto-refresh.d.ts.map +1 -0
  132. package/dist/hooks/use-auto-refresh.js +175 -0
  133. package/dist/hooks/use-auto-refresh.js.map +1 -0
  134. package/dist/hooks/use-date-time-formatter.d.ts +11 -0
  135. package/dist/hooks/use-date-time-formatter.d.ts.map +1 -0
  136. package/dist/hooks/use-date-time-formatter.js +24 -0
  137. package/dist/hooks/use-date-time-formatter.js.map +1 -0
  138. package/dist/hooks/use-delayed-loading.d.ts +25 -0
  139. package/dist/hooks/use-delayed-loading.d.ts.map +1 -0
  140. package/dist/hooks/use-delayed-loading.js +95 -0
  141. package/dist/hooks/use-delayed-loading.js.map +1 -0
  142. package/dist/i18n/index.d.ts +5678 -0
  143. package/dist/i18n/index.d.ts.map +1 -0
  144. package/dist/i18n/index.js +5712 -0
  145. package/dist/i18n/index.js.map +1 -0
  146. package/dist/i18n/language.d.ts +17 -0
  147. package/dist/i18n/language.d.ts.map +1 -0
  148. package/dist/i18n/language.js +43 -0
  149. package/dist/i18n/language.js.map +1 -0
  150. package/dist/index.d.ts +2 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +2 -0
  153. package/dist/index.js.map +1 -0
  154. package/dist/lib/api-error.d.ts +2 -0
  155. package/dist/lib/api-error.d.ts.map +1 -0
  156. package/dist/lib/api-error.js +38 -0
  157. package/dist/lib/api-error.js.map +1 -0
  158. package/dist/lib/format.d.ts +30 -0
  159. package/dist/lib/format.d.ts.map +1 -0
  160. package/dist/lib/format.js +157 -0
  161. package/dist/lib/format.js.map +1 -0
  162. package/dist/lib/index.d.ts +8 -0
  163. package/dist/lib/index.d.ts.map +1 -0
  164. package/dist/lib/index.js +8 -0
  165. package/dist/lib/index.js.map +1 -0
  166. package/dist/lib/model-number.d.ts +2 -0
  167. package/dist/lib/model-number.d.ts.map +1 -0
  168. package/dist/lib/model-number.js +18 -0
  169. package/dist/lib/model-number.js.map +1 -0
  170. package/dist/lib/model-provider-type.d.ts +7 -0
  171. package/dist/lib/model-provider-type.d.ts.map +1 -0
  172. package/dist/lib/model-provider-type.js +28 -0
  173. package/dist/lib/model-provider-type.js.map +1 -0
  174. package/dist/lib/project-name.d.ts +7 -0
  175. package/dist/lib/project-name.d.ts.map +1 -0
  176. package/dist/lib/project-name.js +14 -0
  177. package/dist/lib/project-name.js.map +1 -0
  178. package/dist/lib/releases/release-line-model.d.ts +55 -0
  179. package/dist/lib/releases/release-line-model.d.ts.map +1 -0
  180. package/dist/lib/releases/release-line-model.js +476 -0
  181. package/dist/lib/releases/release-line-model.js.map +1 -0
  182. package/dist/lib/time-zone.d.ts +14 -0
  183. package/dist/lib/time-zone.d.ts.map +1 -0
  184. package/dist/lib/time-zone.js +118 -0
  185. package/dist/lib/time-zone.js.map +1 -0
  186. package/dist/lib/uuid.d.ts +2 -0
  187. package/dist/lib/uuid.d.ts.map +1 -0
  188. package/dist/lib/uuid.js +5 -0
  189. package/dist/lib/uuid.js.map +1 -0
  190. package/dist/providers/display-preferences-provider.d.ts +18 -0
  191. package/dist/providers/display-preferences-provider.d.ts.map +1 -0
  192. package/dist/providers/display-preferences-provider.js +46 -0
  193. package/dist/providers/display-preferences-provider.js.map +1 -0
  194. package/dist/providers/index.d.ts +5 -0
  195. package/dist/providers/index.d.ts.map +1 -0
  196. package/dist/providers/index.js +5 -0
  197. package/dist/providers/index.js.map +1 -0
  198. package/dist/providers/project-context-provider.d.ts +8 -0
  199. package/dist/providers/project-context-provider.d.ts.map +1 -0
  200. package/dist/providers/project-context-provider.js +15 -0
  201. package/dist/providers/project-context-provider.js.map +1 -0
  202. package/dist/providers/proofhound-web-provider.d.ts +9 -0
  203. package/dist/providers/proofhound-web-provider.d.ts.map +1 -0
  204. package/dist/providers/proofhound-web-provider.js +44 -0
  205. package/dist/providers/proofhound-web-provider.js.map +1 -0
  206. package/dist/providers/refine-provider.d.ts +5 -0
  207. package/dist/providers/refine-provider.d.ts.map +1 -0
  208. package/dist/providers/refine-provider.js +23 -0
  209. package/dist/providers/refine-provider.js.map +1 -0
  210. package/dist/screens/annotations/annotation-detail-page.d.ts +5 -0
  211. package/dist/screens/annotations/annotation-detail-page.d.ts.map +1 -0
  212. package/dist/screens/annotations/annotation-detail-page.js +379 -0
  213. package/dist/screens/annotations/annotation-detail-page.js.map +1 -0
  214. package/dist/screens/annotations/annotation-new-page.d.ts +4 -0
  215. package/dist/screens/annotations/annotation-new-page.d.ts.map +1 -0
  216. package/dist/screens/annotations/annotation-new-page.js +125 -0
  217. package/dist/screens/annotations/annotation-new-page.js.map +1 -0
  218. package/dist/screens/annotations/annotation-task-model.d.ts +39 -0
  219. package/dist/screens/annotations/annotation-task-model.d.ts.map +1 -0
  220. package/dist/screens/annotations/annotation-task-model.js +88 -0
  221. package/dist/screens/annotations/annotation-task-model.js.map +1 -0
  222. package/dist/screens/annotations/annotation-ui.d.ts +33 -0
  223. package/dist/screens/annotations/annotation-ui.d.ts.map +1 -0
  224. package/dist/screens/annotations/annotation-ui.js +92 -0
  225. package/dist/screens/annotations/annotation-ui.js.map +1 -0
  226. package/dist/screens/annotations/annotations-list-page.d.ts +4 -0
  227. package/dist/screens/annotations/annotations-list-page.d.ts.map +1 -0
  228. package/dist/screens/annotations/annotations-list-page.js +82 -0
  229. package/dist/screens/annotations/annotations-list-page.js.map +1 -0
  230. package/dist/screens/connectors/connector-detail-page.d.ts +5 -0
  231. package/dist/screens/connectors/connector-detail-page.d.ts.map +1 -0
  232. package/dist/screens/connectors/connector-detail-page.js +838 -0
  233. package/dist/screens/connectors/connector-detail-page.js.map +1 -0
  234. package/dist/screens/connectors/connector-form-page.d.ts +8 -0
  235. package/dist/screens/connectors/connector-form-page.d.ts.map +1 -0
  236. package/dist/screens/connectors/connector-form-page.js +328 -0
  237. package/dist/screens/connectors/connector-form-page.js.map +1 -0
  238. package/dist/screens/connectors/connector-peek-dialog.d.ts +8 -0
  239. package/dist/screens/connectors/connector-peek-dialog.d.ts.map +1 -0
  240. package/dist/screens/connectors/connector-peek-dialog.js +24 -0
  241. package/dist/screens/connectors/connector-peek-dialog.js.map +1 -0
  242. package/dist/screens/connectors/connector-types.d.ts +36 -0
  243. package/dist/screens/connectors/connector-types.d.ts.map +1 -0
  244. package/dist/screens/connectors/connector-types.js +48 -0
  245. package/dist/screens/connectors/connector-types.js.map +1 -0
  246. package/dist/screens/connectors/connector-ui.d.ts +14 -0
  247. package/dist/screens/connectors/connector-ui.d.ts.map +1 -0
  248. package/dist/screens/connectors/connector-ui.js +40 -0
  249. package/dist/screens/connectors/connector-ui.js.map +1 -0
  250. package/dist/screens/connectors/connectors-list-page.d.ts +4 -0
  251. package/dist/screens/connectors/connectors-list-page.d.ts.map +1 -0
  252. package/dist/screens/connectors/connectors-list-page.js +220 -0
  253. package/dist/screens/connectors/connectors-list-page.js.map +1 -0
  254. package/dist/screens/dashboard/dashboard-screen.d.ts +5 -0
  255. package/dist/screens/dashboard/dashboard-screen.d.ts.map +1 -0
  256. package/dist/screens/dashboard/dashboard-screen.js +524 -0
  257. package/dist/screens/dashboard/dashboard-screen.js.map +1 -0
  258. package/dist/screens/datasets/dataset-detail-helpers.d.ts +10 -0
  259. package/dist/screens/datasets/dataset-detail-helpers.d.ts.map +1 -0
  260. package/dist/screens/datasets/dataset-detail-helpers.js +84 -0
  261. package/dist/screens/datasets/dataset-detail-helpers.js.map +1 -0
  262. package/dist/screens/datasets/dataset-detail-page.d.ts +6 -0
  263. package/dist/screens/datasets/dataset-detail-page.d.ts.map +1 -0
  264. package/dist/screens/datasets/dataset-detail-page.js +379 -0
  265. package/dist/screens/datasets/dataset-detail-page.js.map +1 -0
  266. package/dist/screens/datasets/dataset-import-runner.d.ts +17 -0
  267. package/dist/screens/datasets/dataset-import-runner.d.ts.map +1 -0
  268. package/dist/screens/datasets/dataset-import-runner.js +33 -0
  269. package/dist/screens/datasets/dataset-import-runner.js.map +1 -0
  270. package/dist/screens/datasets/dataset-mappers.d.ts +5 -0
  271. package/dist/screens/datasets/dataset-mappers.d.ts.map +1 -0
  272. package/dist/screens/datasets/dataset-mappers.js +79 -0
  273. package/dist/screens/datasets/dataset-mappers.js.map +1 -0
  274. package/dist/screens/datasets/dataset-samples-table.d.ts +21 -0
  275. package/dist/screens/datasets/dataset-samples-table.d.ts.map +1 -0
  276. package/dist/screens/datasets/dataset-samples-table.js +59 -0
  277. package/dist/screens/datasets/dataset-samples-table.js.map +1 -0
  278. package/dist/screens/datasets/dataset-transfer-progress.d.ts +30 -0
  279. package/dist/screens/datasets/dataset-transfer-progress.d.ts.map +1 -0
  280. package/dist/screens/datasets/dataset-transfer-progress.js +157 -0
  281. package/dist/screens/datasets/dataset-transfer-progress.js.map +1 -0
  282. package/dist/screens/datasets/dataset-types.d.ts +59 -0
  283. package/dist/screens/datasets/dataset-types.d.ts.map +1 -0
  284. package/dist/screens/datasets/dataset-types.js +15 -0
  285. package/dist/screens/datasets/dataset-types.js.map +1 -0
  286. package/dist/screens/datasets/dataset-ui.d.ts +49 -0
  287. package/dist/screens/datasets/dataset-ui.d.ts.map +1 -0
  288. package/dist/screens/datasets/dataset-ui.js +163 -0
  289. package/dist/screens/datasets/dataset-ui.js.map +1 -0
  290. package/dist/screens/datasets/dataset-upload-page.d.ts +4 -0
  291. package/dist/screens/datasets/dataset-upload-page.d.ts.map +1 -0
  292. package/dist/screens/datasets/dataset-upload-page.js +425 -0
  293. package/dist/screens/datasets/dataset-upload-page.js.map +1 -0
  294. package/dist/screens/datasets/dataset-upload-parser.d.ts +21 -0
  295. package/dist/screens/datasets/dataset-upload-parser.d.ts.map +1 -0
  296. package/dist/screens/datasets/dataset-upload-parser.js +536 -0
  297. package/dist/screens/datasets/dataset-upload-parser.js.map +1 -0
  298. package/dist/screens/datasets/datasets-list-page.d.ts +4 -0
  299. package/dist/screens/datasets/datasets-list-page.d.ts.map +1 -0
  300. package/dist/screens/datasets/datasets-list-page.js +249 -0
  301. package/dist/screens/datasets/datasets-list-page.js.map +1 -0
  302. package/dist/screens/experiments/experiment-detail-page.d.ts +5 -0
  303. package/dist/screens/experiments/experiment-detail-page.d.ts.map +1 -0
  304. package/dist/screens/experiments/experiment-detail-page.js +460 -0
  305. package/dist/screens/experiments/experiment-detail-page.js.map +1 -0
  306. package/dist/screens/experiments/experiment-new-page.d.ts +21 -0
  307. package/dist/screens/experiments/experiment-new-page.d.ts.map +1 -0
  308. package/dist/screens/experiments/experiment-new-page.js +427 -0
  309. package/dist/screens/experiments/experiment-new-page.js.map +1 -0
  310. package/dist/screens/experiments/experiment-option-adapter.d.ts +68 -0
  311. package/dist/screens/experiments/experiment-option-adapter.d.ts.map +1 -0
  312. package/dist/screens/experiments/experiment-option-adapter.js +225 -0
  313. package/dist/screens/experiments/experiment-option-adapter.js.map +1 -0
  314. package/dist/screens/experiments/experiment-progress.d.ts +5 -0
  315. package/dist/screens/experiments/experiment-progress.d.ts.map +1 -0
  316. package/dist/screens/experiments/experiment-progress.js +15 -0
  317. package/dist/screens/experiments/experiment-progress.js.map +1 -0
  318. package/dist/screens/experiments/experiment-repeat-href.d.ts +17 -0
  319. package/dist/screens/experiments/experiment-repeat-href.d.ts.map +1 -0
  320. package/dist/screens/experiments/experiment-repeat-href.js +32 -0
  321. package/dist/screens/experiments/experiment-repeat-href.js.map +1 -0
  322. package/dist/screens/experiments/experiment-theme.d.ts +43 -0
  323. package/dist/screens/experiments/experiment-theme.d.ts.map +1 -0
  324. package/dist/screens/experiments/experiment-theme.js +43 -0
  325. package/dist/screens/experiments/experiment-theme.js.map +1 -0
  326. package/dist/screens/experiments/experiment-ui.d.ts +36 -0
  327. package/dist/screens/experiments/experiment-ui.d.ts.map +1 -0
  328. package/dist/screens/experiments/experiment-ui.js +46 -0
  329. package/dist/screens/experiments/experiment-ui.js.map +1 -0
  330. package/dist/screens/experiments/experiment-view-model.d.ts +118 -0
  331. package/dist/screens/experiments/experiment-view-model.d.ts.map +1 -0
  332. package/dist/screens/experiments/experiment-view-model.js +114 -0
  333. package/dist/screens/experiments/experiment-view-model.js.map +1 -0
  334. package/dist/screens/experiments/experiments-comparison-view.d.ts +10 -0
  335. package/dist/screens/experiments/experiments-comparison-view.d.ts.map +1 -0
  336. package/dist/screens/experiments/experiments-comparison-view.js +215 -0
  337. package/dist/screens/experiments/experiments-comparison-view.js.map +1 -0
  338. package/dist/screens/experiments/experiments-list-page.d.ts +4 -0
  339. package/dist/screens/experiments/experiments-list-page.d.ts.map +1 -0
  340. package/dist/screens/experiments/experiments-list-page.js +364 -0
  341. package/dist/screens/experiments/experiments-list-page.js.map +1 -0
  342. package/dist/screens/experiments/experiments-table.d.ts +18 -0
  343. package/dist/screens/experiments/experiments-table.d.ts.map +1 -0
  344. package/dist/screens/experiments/experiments-table.js +97 -0
  345. package/dist/screens/experiments/experiments-table.js.map +1 -0
  346. package/dist/screens/experiments/run-result-detail-sheet.d.ts +9 -0
  347. package/dist/screens/experiments/run-result-detail-sheet.d.ts.map +1 -0
  348. package/dist/screens/experiments/run-result-detail-sheet.js +89 -0
  349. package/dist/screens/experiments/run-result-detail-sheet.js.map +1 -0
  350. package/dist/screens/experiments/run-result-display.d.ts +28 -0
  351. package/dist/screens/experiments/run-result-display.d.ts.map +1 -0
  352. package/dist/screens/experiments/run-result-display.js +124 -0
  353. package/dist/screens/experiments/run-result-display.js.map +1 -0
  354. package/dist/screens/experiments/run-result-labels.d.ts +16 -0
  355. package/dist/screens/experiments/run-result-labels.d.ts.map +1 -0
  356. package/dist/screens/experiments/run-result-labels.js +52 -0
  357. package/dist/screens/experiments/run-result-labels.js.map +1 -0
  358. package/dist/screens/index.d.ts +29 -0
  359. package/dist/screens/index.d.ts.map +1 -0
  360. package/dist/screens/index.js +43 -0
  361. package/dist/screens/index.js.map +1 -0
  362. package/dist/screens/models/model-form-page.d.ts +9 -0
  363. package/dist/screens/models/model-form-page.d.ts.map +1 -0
  364. package/dist/screens/models/model-form-page.js +876 -0
  365. package/dist/screens/models/model-form-page.js.map +1 -0
  366. package/dist/screens/models/model-view-model.d.ts +66 -0
  367. package/dist/screens/models/model-view-model.d.ts.map +1 -0
  368. package/dist/screens/models/model-view-model.js +36 -0
  369. package/dist/screens/models/model-view-model.js.map +1 -0
  370. package/dist/screens/models/models-list-page.d.ts +4 -0
  371. package/dist/screens/models/models-list-page.d.ts.map +1 -0
  372. package/dist/screens/models/models-list-page.js +352 -0
  373. package/dist/screens/models/models-list-page.js.map +1 -0
  374. package/dist/screens/models/project-model-adapter.d.ts +5 -0
  375. package/dist/screens/models/project-model-adapter.d.ts.map +1 -0
  376. package/dist/screens/models/project-model-adapter.js +63 -0
  377. package/dist/screens/models/project-model-adapter.js.map +1 -0
  378. package/dist/screens/monitoring/big-chart-card.d.ts +36 -0
  379. package/dist/screens/monitoring/big-chart-card.d.ts.map +1 -0
  380. package/dist/screens/monitoring/big-chart-card.js +32 -0
  381. package/dist/screens/monitoring/big-chart-card.js.map +1 -0
  382. package/dist/screens/monitoring/monitoring-filter-strip.d.ts +14 -0
  383. package/dist/screens/monitoring/monitoring-filter-strip.d.ts.map +1 -0
  384. package/dist/screens/monitoring/monitoring-filter-strip.js +36 -0
  385. package/dist/screens/monitoring/monitoring-filter-strip.js.map +1 -0
  386. package/dist/screens/monitoring/project-monitoring-page.d.ts +9 -0
  387. package/dist/screens/monitoring/project-monitoring-page.d.ts.map +1 -0
  388. package/dist/screens/monitoring/project-monitoring-page.js +240 -0
  389. package/dist/screens/monitoring/project-monitoring-page.js.map +1 -0
  390. package/dist/screens/monitoring/ranking-cards.d.ts +23 -0
  391. package/dist/screens/monitoring/ranking-cards.d.ts.map +1 -0
  392. package/dist/screens/monitoring/ranking-cards.js +78 -0
  393. package/dist/screens/monitoring/ranking-cards.js.map +1 -0
  394. package/dist/screens/optimizations/optimization-detail-page.d.ts +5 -0
  395. package/dist/screens/optimizations/optimization-detail-page.d.ts.map +1 -0
  396. package/dist/screens/optimizations/optimization-detail-page.js +934 -0
  397. package/dist/screens/optimizations/optimization-detail-page.js.map +1 -0
  398. package/dist/screens/optimizations/optimization-mappers.d.ts +56 -0
  399. package/dist/screens/optimizations/optimization-mappers.d.ts.map +1 -0
  400. package/dist/screens/optimizations/optimization-mappers.js +142 -0
  401. package/dist/screens/optimizations/optimization-mappers.js.map +1 -0
  402. package/dist/screens/optimizations/optimization-new-page.d.ts +8 -0
  403. package/dist/screens/optimizations/optimization-new-page.d.ts.map +1 -0
  404. package/dist/screens/optimizations/optimization-new-page.js +732 -0
  405. package/dist/screens/optimizations/optimization-new-page.js.map +1 -0
  406. package/dist/screens/optimizations/optimization-theme.d.ts +43 -0
  407. package/dist/screens/optimizations/optimization-theme.d.ts.map +1 -0
  408. package/dist/screens/optimizations/optimization-theme.js +46 -0
  409. package/dist/screens/optimizations/optimization-theme.js.map +1 -0
  410. package/dist/screens/optimizations/optimization-ui.d.ts +60 -0
  411. package/dist/screens/optimizations/optimization-ui.d.ts.map +1 -0
  412. package/dist/screens/optimizations/optimization-ui.js +164 -0
  413. package/dist/screens/optimizations/optimization-ui.js.map +1 -0
  414. package/dist/screens/optimizations/optimizations-list-page.d.ts +4 -0
  415. package/dist/screens/optimizations/optimizations-list-page.d.ts.map +1 -0
  416. package/dist/screens/optimizations/optimizations-list-page.js +358 -0
  417. package/dist/screens/optimizations/optimizations-list-page.js.map +1 -0
  418. package/dist/screens/prompts/prompt-body-editor.d.ts +15 -0
  419. package/dist/screens/prompts/prompt-body-editor.d.ts.map +1 -0
  420. package/dist/screens/prompts/prompt-body-editor.js +165 -0
  421. package/dist/screens/prompts/prompt-body-editor.js.map +1 -0
  422. package/dist/screens/prompts/prompt-dataset-variables.d.ts +4 -0
  423. package/dist/screens/prompts/prompt-dataset-variables.d.ts.map +1 -0
  424. package/dist/screens/prompts/prompt-dataset-variables.js +27 -0
  425. package/dist/screens/prompts/prompt-dataset-variables.js.map +1 -0
  426. package/dist/screens/prompts/prompt-detail-page.d.ts +5 -0
  427. package/dist/screens/prompts/prompt-detail-page.d.ts.map +1 -0
  428. package/dist/screens/prompts/prompt-detail-page.js +1013 -0
  429. package/dist/screens/prompts/prompt-detail-page.js.map +1 -0
  430. package/dist/screens/prompts/prompt-model.d.ts +77 -0
  431. package/dist/screens/prompts/prompt-model.d.ts.map +1 -0
  432. package/dist/screens/prompts/prompt-model.js +152 -0
  433. package/dist/screens/prompts/prompt-model.js.map +1 -0
  434. package/dist/screens/prompts/prompt-preview-parts.d.ts +12 -0
  435. package/dist/screens/prompts/prompt-preview-parts.d.ts.map +1 -0
  436. package/dist/screens/prompts/prompt-preview-parts.js +32 -0
  437. package/dist/screens/prompts/prompt-preview-parts.js.map +1 -0
  438. package/dist/screens/prompts/prompt-preview.d.ts +10 -0
  439. package/dist/screens/prompts/prompt-preview.d.ts.map +1 -0
  440. package/dist/screens/prompts/prompt-preview.js +16 -0
  441. package/dist/screens/prompts/prompt-preview.js.map +1 -0
  442. package/dist/screens/prompts/prompt-ui.d.ts +30 -0
  443. package/dist/screens/prompts/prompt-ui.d.ts.map +1 -0
  444. package/dist/screens/prompts/prompt-ui.js +51 -0
  445. package/dist/screens/prompts/prompt-ui.js.map +1 -0
  446. package/dist/screens/prompts/prompts-list-page.d.ts +5 -0
  447. package/dist/screens/prompts/prompts-list-page.d.ts.map +1 -0
  448. package/dist/screens/prompts/prompts-list-page.js +208 -0
  449. package/dist/screens/prompts/prompts-list-page.js.map +1 -0
  450. package/dist/screens/quick-start/quick-start-screen.d.ts +2 -0
  451. package/dist/screens/quick-start/quick-start-screen.d.ts.map +1 -0
  452. package/dist/screens/quick-start/quick-start-screen.js +486 -0
  453. package/dist/screens/quick-start/quick-start-screen.js.map +1 -0
  454. package/dist/screens/releases/release-line-detail-page.d.ts +5 -0
  455. package/dist/screens/releases/release-line-detail-page.d.ts.map +1 -0
  456. package/dist/screens/releases/release-line-detail-page.js +973 -0
  457. package/dist/screens/releases/release-line-detail-page.js.map +1 -0
  458. package/dist/screens/releases/release-line-ui.d.ts +30 -0
  459. package/dist/screens/releases/release-line-ui.d.ts.map +1 -0
  460. package/dist/screens/releases/release-line-ui.js +197 -0
  461. package/dist/screens/releases/release-line-ui.js.map +1 -0
  462. package/dist/screens/releases/release-new-model.d.ts +5 -0
  463. package/dist/screens/releases/release-new-model.d.ts.map +1 -0
  464. package/dist/screens/releases/release-new-model.js +18 -0
  465. package/dist/screens/releases/release-new-model.js.map +1 -0
  466. package/dist/screens/releases/release-new-page.d.ts +6 -0
  467. package/dist/screens/releases/release-new-page.d.ts.map +1 -0
  468. package/dist/screens/releases/release-new-page.js +816 -0
  469. package/dist/screens/releases/release-new-page.js.map +1 -0
  470. package/dist/screens/releases/release-topology-canvas.d.ts +13 -0
  471. package/dist/screens/releases/release-topology-canvas.d.ts.map +1 -0
  472. package/dist/screens/releases/release-topology-canvas.js +856 -0
  473. package/dist/screens/releases/release-topology-canvas.js.map +1 -0
  474. package/dist/screens/releases/releases-list-page.d.ts +4 -0
  475. package/dist/screens/releases/releases-list-page.d.ts.map +1 -0
  476. package/dist/screens/releases/releases-list-page.js +86 -0
  477. package/dist/screens/releases/releases-list-page.js.map +1 -0
  478. package/dist/screens/settings/settings-page.d.ts +2 -0
  479. package/dist/screens/settings/settings-page.d.ts.map +1 -0
  480. package/dist/screens/settings/settings-page.js +250 -0
  481. package/dist/screens/settings/settings-page.js.map +1 -0
  482. package/dist/styles/globals.css +961 -0
  483. package/package.json +84 -0
@@ -0,0 +1,816 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import Link from 'next/link';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
5
+ import { useMemo, useState } from 'react';
6
+ import { CANARY_RELEASE_FILTER_MAX_DEPTH, CANARY_RELEASE_FILTER_OPS, DEFAULT_PROMPT_LANGUAGE, canaryReleaseFilterRulesSchema, } from '@proofhound/shared';
7
+ import { AlertCircle, Check, Filter, ImageIcon, Loader2, Plus, Search, X } from 'lucide-react';
8
+ import { Main } from '@proofhound/ui/layout';
9
+ import { ModalityIconGroup, Button, Input, Label, cn, } from '@proofhound/ui';
10
+ import { PromptVersionPickerRow, PromptVersionPickerTag } from '../../components';
11
+ import { useConnector, useConnectors } from '../../hooks';
12
+ import { useCreateCanaryRelease, useStartCanaryRelease } from '../../hooks';
13
+ import { useDateTimeFormatter } from '../../hooks';
14
+ import { useProjectModels } from '../../hooks';
15
+ import { useCreateProductionRelease } from '../../hooks';
16
+ import { usePrompt, usePrompts } from '../../hooks';
17
+ import { useDelayedLoading } from '../../hooks';
18
+ import { useReleaseLineList } from '../../hooks';
19
+ import { useI18n } from '../../i18n';
20
+ import { getApiErrorMessage, getReleaseLineId } from '../../lib';
21
+ import { composePromptPreview } from '../prompts/prompt-preview';
22
+ import { renderPromptPreviewParts } from '../prompts/prompt-preview-parts';
23
+ import { VARIABLE_TONE_CLASSES } from '../prompts/prompt-ui';
24
+ import { deriveRecordCategoryOptions, releaseRecordModeFromCategories } from './release-new-model';
25
+ const IMAGE_PROMPT_VARIABLE_TYPES = new Set(['image', 'image_url', 'image_base64']);
26
+ const DEFAULT_RPM = 60;
27
+ const DEFAULT_TPM = 120_000;
28
+ const DEFAULT_CONCURRENCY = 4;
29
+ const DEFAULT_QUEUE_TRAFFIC_PERCENT = 10;
30
+ const TRAFFIC_PERCENT_PRESETS = [1, 5, 20, 50, 100];
31
+ function buildDefaultReleaseName() {
32
+ const now = new Date();
33
+ const yyyy = now.getFullYear();
34
+ const mm = String(now.getMonth() + 1).padStart(2, '0');
35
+ const dd = String(now.getDate()).padStart(2, '0');
36
+ return `release-${yyyy}${mm}${dd}-`;
37
+ }
38
+ function formatTemplate(template, values) {
39
+ return template.replace(/\{(\w+)\}/g, (_, key) => {
40
+ const value = values[key];
41
+ return value === undefined ? `{${key}}` : String(value);
42
+ });
43
+ }
44
+ function formatThousand(value) {
45
+ return value.toLocaleString('en-US').replace(/,/g, ' ');
46
+ }
47
+ function formatPrice(value) {
48
+ if (!Number.isFinite(value))
49
+ return '0';
50
+ if (value === 0)
51
+ return '0';
52
+ if (value >= 1)
53
+ return value
54
+ .toFixed(2)
55
+ .replace(/\.00$/, '')
56
+ .replace(/(\.\d)0$/, '$1');
57
+ return value.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
58
+ }
59
+ function formatModelLimit(limit) {
60
+ return limit < 0 ? '∞' : formatThousand(limit);
61
+ }
62
+ function modelLimitDefaultValue(limit, fallback) {
63
+ return String(limit > 0 ? limit : fallback);
64
+ }
65
+ function formatContextWindow(tokens) {
66
+ if (!tokens || tokens <= 0)
67
+ return '—';
68
+ if (tokens >= 1_000_000) {
69
+ const value = tokens / 1_000_000;
70
+ return Number.isInteger(value) ? `${value}M` : `${value.toFixed(1)}M`;
71
+ }
72
+ if (tokens >= 1000)
73
+ return `${Math.round(tokens / 1000)}K`;
74
+ return String(tokens);
75
+ }
76
+ function positiveIntegerFromText(value) {
77
+ const parsed = Number(value.trim());
78
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
79
+ }
80
+ function numberTextWithinLimit(value, limit) {
81
+ const parsed = positiveIntegerFromText(value);
82
+ return parsed !== null && (!limit || limit < 0 || parsed <= limit);
83
+ }
84
+ function temperatureFromText(value) {
85
+ const parsed = Number(value.trim());
86
+ return Number.isFinite(parsed) && parsed >= 0 && parsed <= 2 ? parsed : null;
87
+ }
88
+ function trafficPercentFromText(value) {
89
+ const parsed = Number(value.trim());
90
+ return Number.isInteger(parsed) && parsed > 0 && parsed <= 100 ? parsed : null;
91
+ }
92
+ function initialTrafficPercentFromParams(params) {
93
+ const percent = params.get('trafficPercent');
94
+ if (percent && trafficPercentFromText(percent) !== null)
95
+ return percent;
96
+ const ratio = Number(params.get('trafficRatio'));
97
+ if (Number.isFinite(ratio) && ratio > 0 && ratio <= 1)
98
+ return String(Math.round(ratio * 100));
99
+ return String(DEFAULT_QUEUE_TRAFFIC_PERCENT);
100
+ }
101
+ function initialTrafficModeFromParams(params) {
102
+ return params.get('trafficMode') === 'dual_run' ? 'dual_run' : 'split';
103
+ }
104
+ function normalizeLineId(value) {
105
+ if (!value)
106
+ return '';
107
+ try {
108
+ return decodeURIComponent(value);
109
+ }
110
+ catch {
111
+ return value;
112
+ }
113
+ }
114
+ function isRecord(value) {
115
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
116
+ }
117
+ function normalizeFieldKey(value) {
118
+ return value
119
+ .trim()
120
+ .replace(/^\.+|\.+$/g, '')
121
+ .replace(/\.+/g, '.');
122
+ }
123
+ function fieldOptionFromUnknown(value) {
124
+ if (typeof value === 'string') {
125
+ const key = normalizeFieldKey(value);
126
+ return key ? { key, type: 'unknown', description: '' } : null;
127
+ }
128
+ if (!isRecord(value))
129
+ return null;
130
+ const rawKey = value.key ?? value.name ?? value.field;
131
+ const key = typeof rawKey === 'string' ? normalizeFieldKey(rawKey) : '';
132
+ if (!key)
133
+ return null;
134
+ return {
135
+ key,
136
+ type: typeof value.type === 'string' ? value.type : 'unknown',
137
+ description: typeof value.description === 'string' ? value.description : '',
138
+ };
139
+ }
140
+ function flattenSchemaProperties(properties, prefix = '') {
141
+ const skipKeys = new Set([
142
+ 'type',
143
+ 'properties',
144
+ 'required',
145
+ 'additionalProperties',
146
+ '$schema',
147
+ 'title',
148
+ 'description',
149
+ ]);
150
+ return Object.entries(properties).flatMap(([key, fieldValue]) => {
151
+ if (skipKeys.has(key))
152
+ return [];
153
+ const record = isRecord(fieldValue) ? fieldValue : {};
154
+ const rawType = typeof record.type === 'string' ? record.type : typeof fieldValue;
155
+ const fullKey = normalizeFieldKey(prefix ? `${prefix}.${key}` : key);
156
+ const current = {
157
+ key: fullKey,
158
+ type: rawType,
159
+ description: typeof record.description === 'string' ? record.description : '',
160
+ };
161
+ if (rawType === 'object' && isRecord(record.properties)) {
162
+ const children = flattenSchemaProperties(record.properties, fullKey);
163
+ return children.length > 0 ? [current, ...children] : [current];
164
+ }
165
+ return [current];
166
+ });
167
+ }
168
+ function schemaToFieldOptions(value) {
169
+ if (!isRecord(value))
170
+ return [];
171
+ const properties = isRecord(value.properties) ? value.properties : value;
172
+ return flattenSchemaProperties(properties);
173
+ }
174
+ function dedupeFieldOptions(fields) {
175
+ const seen = new Set();
176
+ const result = [];
177
+ for (const field of fields) {
178
+ const key = normalizeFieldKey(field.key);
179
+ if (!key || seen.has(key))
180
+ continue;
181
+ seen.add(key);
182
+ result.push({ ...field, key });
183
+ }
184
+ return result;
185
+ }
186
+ function extractFieldOptionsFromConnector(connector) {
187
+ if (!connector)
188
+ return [];
189
+ const config = isRecord(connector.config) ? connector.config : {};
190
+ const candidates = [
191
+ config.expectedPayloadSchema,
192
+ config.confirmedPayloadSchema,
193
+ config.lastPeekPayloadSchema,
194
+ config.peekPayloadSchema,
195
+ config.fieldSchema,
196
+ config.fields,
197
+ config.payloadFields,
198
+ ];
199
+ const options = [];
200
+ for (const candidate of candidates) {
201
+ if (!candidate)
202
+ continue;
203
+ if (Array.isArray(candidate)) {
204
+ options.push(...candidate.map(fieldOptionFromUnknown).filter((field) => Boolean(field)));
205
+ }
206
+ else {
207
+ options.push(...schemaToFieldOptions(candidate));
208
+ }
209
+ }
210
+ return dedupeFieldOptions(options);
211
+ }
212
+ function inferSourceForVariable(variable, fields) {
213
+ const fieldNames = new Set(fields.map((field) => field.key));
214
+ const candidates = [
215
+ variable.datasetField,
216
+ variable.name,
217
+ `payload.${variable.name}`,
218
+ `data.${variable.name}`,
219
+ `body.${variable.name}`,
220
+ ].filter((value) => Boolean(value));
221
+ for (const candidate of candidates) {
222
+ if (fieldNames.has(candidate))
223
+ return candidate;
224
+ }
225
+ const suffix = fields.find((field) => field.key.endsWith(`.${variable.name}`));
226
+ return suffix?.key ?? '';
227
+ }
228
+ function inferExternalIdField(fields) {
229
+ const candidates = ['id', 'external_id', 'externalId', 'payload.id', 'data.id', 'body.id'];
230
+ const fieldNames = new Set(fields.map((field) => field.key));
231
+ for (const candidate of candidates) {
232
+ if (fieldNames.has(candidate))
233
+ return candidate;
234
+ }
235
+ return fields.find((field) => /(^|\.)id$/iu.test(field.key))?.key ?? '';
236
+ }
237
+ function productionMappingToRecord(value) {
238
+ const mapping = {};
239
+ if (!isRecord(value))
240
+ return mapping;
241
+ for (const [target, source] of Object.entries(value)) {
242
+ if (typeof source === 'string')
243
+ mapping[target] = source;
244
+ }
245
+ return mapping;
246
+ }
247
+ function mergeIds(first, second) {
248
+ return Array.from(new Set([...first, ...second].filter(Boolean)));
249
+ }
250
+ function normalizeInheritedFilterRules(value) {
251
+ const parsed = canaryReleaseFilterRulesSchema.safeParse(value ?? null);
252
+ return parsed.success ? parsed.data : null;
253
+ }
254
+ function mapPromptVersionToOption(prompt, version, formatDateTime) {
255
+ const promptLanguage = version.promptLanguage ?? DEFAULT_PROMPT_LANGUAGE;
256
+ return {
257
+ id: version.id,
258
+ name: prompt.name,
259
+ version: `v${version.versionNumber}`,
260
+ isLatest: version.versionNumber === prompt.latestVersionNumber,
261
+ isOnline: version.versionNumber === prompt.currentOnlineVersionNumber,
262
+ status: version.status,
263
+ updatedAt: formatDateTime(version.createdAt),
264
+ variables: version.variables,
265
+ outputSchema: version.outputSchema,
266
+ promptPreview: composePromptPreview({
267
+ body: version.body,
268
+ outputSchema: version.outputSchema,
269
+ promptLanguage,
270
+ }),
271
+ };
272
+ }
273
+ function Radio({ checked }) {
274
+ return (_jsx("span", { "aria-hidden": "true", className: cn('mt-0.5 inline-flex size-4 flex-none items-center justify-center rounded-full border', checked ? 'border-primary bg-primary/10' : 'border-border bg-background'), children: checked ? _jsx("span", { className: "size-2 rounded-full bg-primary" }) : null }));
275
+ }
276
+ function CheckBox({ checked }) {
277
+ return (_jsx("span", { "aria-hidden": "true", className: cn('mt-0.5 inline-flex size-4 flex-none items-center justify-center rounded-[3px] border', checked ? 'border-primary bg-primary text-primary-foreground' : 'border-border bg-background'), children: checked ? _jsx(Check, { className: "size-2.5", strokeWidth: 3 }) : null }));
278
+ }
279
+ function Tag({ children, tone = 'neutral', }) {
280
+ return (_jsx("span", { className: cn('inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 font-mono text-[10.5px]', tone === 'info' &&
281
+ 'border-[var(--status-canary-bd)] bg-[var(--status-canary-bg)] text-[var(--status-canary-fg)]', tone === 'positive' &&
282
+ 'border-[var(--status-success-bd)] bg-[var(--status-success-bg)] text-[var(--status-success-fg)]', tone === 'warning' &&
283
+ 'border-[var(--status-pending-bd)] bg-[var(--status-pending-bg)] text-[var(--status-pending-fg)]', tone === 'neutral' && 'border-border bg-muted text-muted-foreground'), children: children }));
284
+ }
285
+ function MiniSearch({ value, onChange, placeholder, }) {
286
+ return (_jsxs("div", { className: "flex items-center gap-2 border-b bg-background px-2.5 py-2", children: [_jsx(Search, { className: "size-3.5 flex-none text-muted-foreground", "aria-hidden": "true" }), _jsx("input", { value: value, onChange: (event) => onChange(event.target.value), placeholder: placeholder, className: "h-5 w-full min-w-0 bg-transparent text-[12.5px] outline-none placeholder:text-muted-foreground" })] }));
287
+ }
288
+ function PickerEmpty({ children }) {
289
+ return _jsx("div", { className: "px-3 py-6 text-center text-[12px] text-muted-foreground", children: children });
290
+ }
291
+ function StepNumber({ index, done }) {
292
+ return (_jsx("span", { "aria-hidden": "true", className: cn('inline-flex size-5 items-center justify-center rounded-full border font-mono text-[11px] font-semibold', done
293
+ ? 'border-[var(--status-running-bd)] bg-[var(--status-running-bg)] text-[var(--status-running-fg)]'
294
+ : 'border-border bg-muted text-muted-foreground'), children: index }));
295
+ }
296
+ function StepCard({ index, done, title, detail, testId, children, }) {
297
+ return (_jsxs("section", { className: "rounded-lg border bg-card", "data-testid": testId, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b px-5 py-3", children: [_jsx(StepNumber, { index: index, done: done }), _jsx("h2", { className: "text-[14px] font-semibold", children: title }), _jsx("span", { className: "text-[12px] text-muted-foreground", children: detail })] }), _jsx("div", { className: "space-y-5 p-5", children: children })] }));
298
+ }
299
+ function SubSectionHead({ label }) {
300
+ return (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-[9.5px] font-bold uppercase tracking-[0.08em] text-muted-foreground", children: [_jsx("span", { "aria-hidden": "true", className: "inline-block h-2.5 w-[3px] rounded-[1.5px] bg-primary" }), label] }));
301
+ }
302
+ function RuntimeLimitField({ label, value, modelLimit, onChange, }) {
303
+ const { t } = useI18n();
304
+ return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "text-[12.5px]", children: [label, " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsxs("div", { className: "flex h-9 items-center rounded-md border bg-background pr-2", children: [_jsx("input", { value: value, onChange: (event) => onChange(event.target.value), inputMode: "numeric", "aria-label": label, className: "h-full min-w-0 flex-1 bg-transparent px-3 font-mono text-[13px] outline-none" }), _jsx("span", { className: "shrink-0 whitespace-nowrap font-mono text-[11px] text-muted-foreground", children: formatTemplate(t('canaryReleases.new.field.runConfig.limitSuffix'), { limit: modelLimit }) })] })] }));
305
+ }
306
+ function PromptRow({ prompt, selected, onSelect, }) {
307
+ const { t } = useI18n();
308
+ const { formatDateTime } = useDateTimeFormatter();
309
+ return (_jsxs("button", { type: "button", onClick: onSelect, "aria-pressed": selected, className: cn('flex w-full items-start gap-2.5 border-b px-3 py-2.5 text-left transition-colors last:border-b-0 hover:bg-muted/40', selected && 'bg-[color-mix(in_oklab,var(--status-canary-bg)_55%,var(--background))]'), children: [_jsx(Radio, { checked: selected }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx("span", { className: "font-mono text-[13px] font-semibold", children: prompt.name }), _jsx(Tag, { children: formatTemplate(t('canaryReleases.new.promptVersionCount'), { count: prompt.latestVersionNumber }) })] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [prompt.currentOnlineVersionNumber ? (_jsx(Tag, { tone: "positive", children: formatTemplate(t('optimizations.new.origin.promptOnlineVersion'), {
310
+ version: `v${prompt.currentOnlineVersionNumber}`,
311
+ }) })) : (_jsx(Tag, { children: t('optimizations.new.origin.promptNoOnlineVersion') })), _jsx(Tag, { children: prompt.latestVersionStatus })] }), _jsxs("div", { className: "mt-1 text-[11.5px] text-muted-foreground", children: [prompt.createdByDisplayName ? `@${prompt.createdByDisplayName}` : '@unknown', " \u00B7", ' ', formatDateTime(prompt.updatedAt)] })] })] }));
312
+ }
313
+ function PromptVersionRow({ option, selected, onSelect, }) {
314
+ const { t } = useI18n();
315
+ return (_jsx(PromptVersionPickerRow, { version: option.version, status: option.status, variables: option.variables, selected: selected, onSelect: onSelect, badges: option.isOnline ? (_jsx(PromptVersionPickerTag, { tone: "positive", children: t('canaryReleases.new.promptVersionOnline') })) : option.isLatest ? (_jsx(PromptVersionPickerTag, { tone: "info", children: t('canaryReleases.new.promptVersionLatest') })) : null, createdAt: option.updatedAt }));
316
+ }
317
+ function PromptVersionPreview({ option }) {
318
+ const { t } = useI18n();
319
+ const previewParts = useMemo(() => renderPromptPreviewParts(option?.promptPreview ?? '', option?.variables ?? []), [option]);
320
+ const imageVariables = useMemo(() => option?.variables.filter((variable) => IMAGE_PROMPT_VARIABLE_TYPES.has(variable.type)) ?? [], [option]);
321
+ if (!option)
322
+ return _jsx(PickerEmpty, { children: t('canaryReleases.new.promptPreviewEmpty') });
323
+ return (_jsxs("div", { className: "border-t px-4 py-3", "data-testid": "release-new-prompt-preview", children: [_jsxs("div", { className: "mb-2 flex flex-wrap items-center justify-between gap-2", children: [_jsx("span", { className: "font-mono text-[10.5px] uppercase tracking-wide text-muted-foreground", children: t('canaryReleases.new.promptPreviewTitle') }), _jsxs("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: [option.name, " \u00B7 ", option.version] })] }), _jsx("pre", { className: "max-h-[300px] overflow-auto whitespace-pre-wrap break-words rounded-md border bg-muted px-3 py-2 font-mono text-[11.5px] leading-relaxed text-foreground", children: previewParts.map((part, index) => {
324
+ if (part.kind === 'text' || !part.varType || IMAGE_PROMPT_VARIABLE_TYPES.has(part.varType)) {
325
+ return _jsx("span", { children: part.value }, index);
326
+ }
327
+ return (_jsx("span", { className: cn('inline rounded border px-1 font-mono text-[11px]', VARIABLE_TONE_CLASSES[part.varType]), "data-variable-name": part.name, children: part.value }, index));
328
+ }) }), imageVariables.length > 0 ? (_jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2 text-[11.5px] text-muted-foreground", children: [_jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [_jsx(ImageIcon, { className: "size-3.5", "aria-hidden": "true" }), t('canaryReleases.new.promptImageVariables')] }), imageVariables.map((variable) => (_jsx("span", { className: cn('inline-flex items-center rounded border px-1.5 py-0.5 font-mono text-[11px]', VARIABLE_TONE_CLASSES[variable.type]), children: `{{${variable.name}}}` }, variable.name)))] })) : null] }));
329
+ }
330
+ function ModelOptionRow({ model, selected, onSelect, }) {
331
+ const { t } = useI18n();
332
+ const imageCapability = model.capabilities.image;
333
+ const supportsImage = imageCapability !== 'none';
334
+ const modalityKinds = supportsImage ? ['text', 'image'] : ['text'];
335
+ const textCapabilityLabel = t('models.capability.text');
336
+ const imageCapabilityLabel = imageCapability === 'both'
337
+ ? t('models.capability.imageBoth')
338
+ : imageCapability === 'url'
339
+ ? t('models.capability.imageUrl')
340
+ : t('models.capability.imageBase64');
341
+ const modalityLabels = supportsImage
342
+ ? { text: textCapabilityLabel, image: imageCapabilityLabel }
343
+ : { text: textCapabilityLabel };
344
+ const ctx = model.contextWindowTokens
345
+ ? formatTemplate(t('canaryReleases.new.modelCap.ctx'), { value: formatContextWindow(model.contextWindowTokens) })
346
+ : null;
347
+ return (_jsxs("button", { type: "button", onClick: onSelect, "aria-pressed": selected, className: cn('flex w-full items-start gap-2.5 border-b px-3 py-2.5 text-left transition-colors last:border-b-0 hover:bg-muted/40', selected && 'bg-[color-mix(in_oklab,var(--status-canary-bg)_55%,var(--background))]'), children: [_jsx(Radio, { checked: selected }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx("span", { className: "font-mono text-[13px] font-semibold", children: model.name }), model.status === 'testing' ? _jsx(Tag, { tone: "warning", children: t('canaryReleases.new.modelTesting') }) : null] }), _jsx("div", { className: "mt-0.5 text-[12px] text-muted-foreground", children: model.providerType }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [ctx ? _jsx(Tag, { children: ctx }) : null, _jsx(ModalityIconGroup, { kinds: modalityKinds, size: "sm", tooltips: modalityLabels, ariaLabels: modalityLabels }), _jsx(Tag, { children: formatTemplate(t('canaryReleases.new.modelRpmTpm'), {
348
+ rpm: formatModelLimit(model.rpm.limit),
349
+ tpm: formatModelLimit(model.tpm.limit),
350
+ }) })] })] }), _jsxs("div", { className: "flex-none text-right font-mono text-[11.5px] text-muted-foreground", children: [_jsx("div", { className: "font-semibold text-foreground", children: formatTemplate(t('canaryReleases.new.modelPriceLabel'), {
351
+ input: formatPrice(model.pricing.inputPerMillion),
352
+ output: formatPrice(model.pricing.outputPerMillion),
353
+ }) }), _jsx("div", { className: "text-[10.5px] text-muted-foreground", children: t('canaryReleases.new.modelPriceUnit') })] })] }));
354
+ }
355
+ function ConnectorOptionRow({ connector, selected, multiple = false, onSelect, }) {
356
+ return (_jsxs("button", { type: "button", onClick: onSelect, "aria-pressed": selected, className: cn('grid w-full grid-cols-[minmax(140px,0.85fr)_minmax(0,1.15fr)] gap-3 border-b px-3 py-2.5 text-left transition-colors last:border-b-0 hover:bg-muted/40', selected && 'bg-[color-mix(in_oklab,var(--status-canary-bg)_55%,var(--background))]'), children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [multiple ? _jsx(CheckBox, { checked: selected }) : _jsx(Radio, { checked: selected }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-mono text-[13px] font-semibold", children: connector.name }), connector.description ? (_jsx("div", { className: "mt-0.5 truncate text-[11.5px] text-muted-foreground", children: connector.description })) : null] })] }), _jsxs("div", { className: "min-w-0 text-[11.5px] text-muted-foreground", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Tag, { children: connector.type }), _jsx(Tag, { children: connector.healthStatus })] }), _jsx("div", { className: "mt-1 truncate font-mono", children: connector.configSummary })] })] }));
357
+ }
358
+ function ReadOnlyConnectorRow({ connector, name, type, emptyLabel, }) {
359
+ return (_jsxs("div", { className: "grid w-full grid-cols-[minmax(140px,0.85fr)_minmax(0,1.15fr)] gap-3 rounded-md border bg-muted/35 px-3 py-2.5 text-left", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [_jsx(CheckBox, { checked: true }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-mono text-[13px] font-semibold", children: connector?.name ?? name ?? emptyLabel }), connector?.description ? (_jsx("div", { className: "mt-0.5 truncate text-[11.5px] text-muted-foreground", children: connector.description })) : null] })] }), _jsxs("div", { className: "min-w-0 text-[11.5px] text-muted-foreground", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Tag, { children: connector?.type ?? type ?? 'connector' }), connector ? _jsx(Tag, { children: connector.healthStatus }) : null] }), _jsx("div", { className: "mt-1 truncate font-mono", children: connector?.configSummary ?? '—' })] })] }));
360
+ }
361
+ function FieldMappingTable({ fields, promptVariables, externalIdField, mapping, readOnly = false, onExternalIdFieldChange, onMappingChange, }) {
362
+ const { t } = useI18n();
363
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "rounded-md border bg-background p-3", children: _jsxs("label", { className: "grid grid-cols-1 items-center gap-2 md:grid-cols-[minmax(180px,0.8fr)_minmax(0,1.2fr)]", children: [_jsxs("span", { className: "text-xs font-medium", children: [t('canaryReleases.new.field.externalIdField'), " ", _jsx("span", { className: "text-destructive", children: "*" })] }), readOnly ? (_jsx("div", { className: "min-h-9 rounded-md border bg-muted/40 px-3 py-2 font-mono text-xs", children: externalIdField || '—' })) : (_jsxs("select", { value: externalIdField, onChange: (event) => onExternalIdFieldChange(event.target.value), className: "h-9 rounded-md border border-input bg-background px-3 font-mono text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring", "data-testid": "release-new-mapping-external-id", children: [_jsx("option", { value: "", children: t('canaryReleases.new.fieldSelectPlaceholder') }), fields.map((field) => (_jsx("option", { value: field.key, children: field.key }, field.key)))] }))] }) }), _jsxs("div", { className: "overflow-hidden rounded-md border bg-background", children: [_jsxs("div", { className: "grid grid-cols-[minmax(180px,0.9fr)_minmax(0,1.1fr)_96px] gap-2 border-b bg-muted/40 px-3 py-2 text-xs font-medium text-muted-foreground", children: [_jsx("span", { children: t('canaryReleases.new.mapping.variable') }), _jsx("span", { children: t('canaryReleases.new.mapping.source') }), _jsx("span", { children: t('canaryReleases.new.mapping.type') })] }), promptVariables.length === 0 ? (_jsx("div", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: t('canaryReleases.new.promptVariablesEmpty') })) : (promptVariables.map((variable) => (_jsxs("div", { className: "grid grid-cols-[minmax(180px,0.9fr)_minmax(0,1.1fr)_96px] items-center gap-2 border-t px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-mono text-xs font-semibold", children: variable.name }), _jsx("div", { className: "text-[11px] text-muted-foreground", children: variable.required
364
+ ? t('canaryReleases.new.mapping.required')
365
+ : t('canaryReleases.new.mapping.optional') })] }), readOnly ? (_jsx("div", { className: "min-h-8 rounded-md border bg-muted/40 px-2 py-1.5 font-mono text-xs", children: mapping[variable.name] || t('canaryReleases.new.mapping.unmapped') })) : (_jsxs("select", { value: mapping[variable.name] ?? '', onChange: (event) => onMappingChange(variable.name, event.target.value), className: "h-8 rounded-md border border-input bg-background px-2 font-mono text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring", "data-testid": `release-new-mapping-source-${variable.name}`, children: [_jsx("option", { value: "", children: t('canaryReleases.new.mapping.unmapped') }), fields.map((field) => (_jsx("option", { value: field.key, children: field.key }, field.key)))] })), _jsx("span", { className: "font-mono text-[11px] text-muted-foreground", children: variable.type })] }, variable.name))))] })] }));
366
+ }
367
+ function ReadOnlyFilterRules({ value }) {
368
+ const { t } = useI18n();
369
+ if (!value) {
370
+ return (_jsx("div", { className: "rounded-md border bg-muted/35 px-3 py-2 text-[12px] text-muted-foreground", children: t('canaryReleases.new.filter.empty') }));
371
+ }
372
+ return (_jsx("pre", { className: "max-h-[220px] overflow-auto whitespace-pre-wrap break-words rounded-md border bg-muted px-3 py-2 font-mono text-[11.5px] leading-relaxed text-foreground", children: JSON.stringify(value, null, 2) }));
373
+ }
374
+ function FilterRulesBuilder({ value, fields, onChange, }) {
375
+ const { t } = useI18n();
376
+ const createAtom = () => ({
377
+ type: 'atom',
378
+ field: fields[0]?.key ?? '',
379
+ op: 'eq',
380
+ value: '',
381
+ });
382
+ if (!value) {
383
+ return (_jsx("div", { className: "rounded-md border border-dashed bg-background px-3 py-4", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: t('canaryReleases.new.filter.empty') }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onChange({ type: 'and', children: [createAtom()] }), children: [_jsx(Filter, { className: "size-3.5" }), t('canaryReleases.new.filter.enable')] })] }) }));
384
+ }
385
+ return (_jsx("div", { className: "space-y-2 rounded-md border bg-background p-3", children: _jsx(FilterNodeEditor, { node: value, depth: 1, fields: fields, onChange: onChange, onRemove: () => onChange(null) }) }));
386
+ }
387
+ function FilterNodeEditor({ node, depth, fields, onChange, onRemove, }) {
388
+ const { t } = useI18n();
389
+ const canNest = depth < CANARY_RELEASE_FILTER_MAX_DEPTH;
390
+ const createAtom = () => ({
391
+ type: 'atom',
392
+ field: fields[0]?.key ?? '',
393
+ op: 'eq',
394
+ value: '',
395
+ });
396
+ const createGroup = (type) => ({ type, children: [createAtom()] });
397
+ if (node.type === 'atom') {
398
+ const needsValue = node.op !== 'exists';
399
+ return (_jsxs("div", { className: "grid grid-cols-1 items-center gap-2 rounded-md border bg-card p-2 md:grid-cols-[minmax(0,1fr)_132px_minmax(0,1fr)_32px]", children: [_jsxs("select", { value: node.field, onChange: (event) => onChange({ ...node, field: event.target.value }), className: "h-8 rounded-md border border-input bg-background px-2 font-mono text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsx("option", { value: "", children: t('canaryReleases.new.fieldSelectPlaceholder') }), fields.map((field) => (_jsx("option", { value: field.key, children: field.key }, field.key)))] }), _jsx("select", { value: node.op, onChange: (event) => {
400
+ const op = event.target.value;
401
+ onChange(op === 'exists' ? { type: 'atom', field: node.field, op } : { ...node, op, value: node.value ?? '' });
402
+ }, className: "h-8 rounded-md border border-input bg-background px-2 font-mono text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring", children: CANARY_RELEASE_FILTER_OPS.map((op) => (_jsx("option", { value: op, children: t(`canaryReleases.new.filter.op.${op}`) }, op))) }), _jsx(Input, { value: needsValue ? String(node.value ?? '') : '', disabled: !needsValue, onChange: (event) => onChange({ ...node, value: event.target.value }), placeholder: needsValue ? t('canaryReleases.new.filter.valuePlaceholder') : t('canaryReleases.new.filter.valueDisabled'), className: "h-8 font-mono text-xs" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8", onClick: onRemove, "aria-label": t('canaryReleases.new.filter.remove'), children: _jsx(X, { className: "size-3.5" }) })] }));
403
+ }
404
+ if (node.type === 'not') {
405
+ return (_jsxs("div", { className: "space-y-2 rounded-md border bg-muted/25 p-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Tag, { tone: "warning", children: "NOT" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8", onClick: onRemove, "aria-label": t('canaryReleases.new.filter.remove'), children: _jsx(X, { className: "size-3.5" }) })] }), _jsx(FilterNodeEditor, { node: node.child, depth: depth + 1, fields: fields, onChange: (child) => onChange({ type: 'not', child }), onRemove: () => onChange({ type: 'not', child: createAtom() }) })] }));
406
+ }
407
+ return (_jsxs("div", { className: "space-y-2 rounded-md border bg-muted/25 p-2", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("select", { value: node.type, onChange: (event) => onChange({ type: event.target.value, children: node.children }), className: "h-8 rounded-md border border-input bg-background px-2 font-mono text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsx("option", { value: "and", children: "AND" }), _jsx("option", { value: "or", children: "OR" })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: formatTemplate(t('canaryReleases.new.filter.depth'), { depth }) })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: () => onChange({ ...node, children: [...node.children, createAtom()] }), children: [_jsx(Plus, { className: "size-3.5" }), t('canaryReleases.new.filter.addCondition')] }), canNest ? (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: () => onChange({ ...node, children: [...node.children, createGroup('and')] }), children: t('canaryReleases.new.filter.addGroup') }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: () => onChange({ ...node, children: [...node.children, { type: 'not', child: createAtom() }] }), children: "NOT" })] })) : null, _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8", onClick: onRemove, "aria-label": t('canaryReleases.new.filter.remove'), children: _jsx(X, { className: "size-3.5" }) })] })] }), _jsx("div", { className: "space-y-2 border-l pl-3", children: node.children.map((child, index) => (_jsx(FilterNodeEditor, { node: child, depth: depth + 1, fields: fields, onChange: (nextChild) => onChange({
408
+ ...node,
409
+ children: node.children.map((item, itemIndex) => (itemIndex === index ? nextChild : item)),
410
+ }), onRemove: () => {
411
+ const nextChildren = node.children.filter((_, itemIndex) => itemIndex !== index);
412
+ onChange({ ...node, children: nextChildren.length > 0 ? nextChildren : [createAtom()] });
413
+ } }, index))) })] }));
414
+ }
415
+ function RecordCategoriesField({ value, options, onChange, }) {
416
+ const { t } = useI18n();
417
+ const allSelected = options.length > 0 && options.every((option) => value.includes(option));
418
+ if (options.length === 0) {
419
+ return (_jsx("div", { className: "rounded-md border border-dashed bg-background px-3 py-4 text-center text-xs text-muted-foreground", children: t('canaryReleases.new.field.recordCategoriesEmpty') }));
420
+ }
421
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: t('canaryReleases.new.field.recordCategoriesSelected').replace('{count}', String(value.length)) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: () => onChange(allSelected ? [] : options), children: allSelected
422
+ ? t('canaryReleases.new.field.recordCategoriesClear')
423
+ : t('canaryReleases.new.field.recordCategoriesSelectAll') })] }), _jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-md border bg-background p-3", children: options.map((option) => {
424
+ const checked = value.includes(option);
425
+ return (_jsxs("button", { type: "button", onClick: () => onChange(checked ? value.filter((item) => item !== option) : [...value, option]), "aria-pressed": checked, className: cn('inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors', checked
426
+ ? 'border-primary bg-primary text-primary-foreground'
427
+ : 'border-border bg-muted text-muted-foreground hover:bg-muted/70'), children: [checked ? _jsx(Check, { className: "size-3", strokeWidth: 3 }) : null, _jsx("span", { className: "font-mono", children: option })] }, option));
428
+ }) })] }));
429
+ }
430
+ function TrafficSelectionField({ isQueueInput, value, trafficMode, showTrafficMode = false, onChange, onTrafficModeChange, }) {
431
+ const { t } = useI18n();
432
+ const parsed = trafficPercentFromText(value);
433
+ const sliderValue = parsed ?? DEFAULT_QUEUE_TRAFFIC_PERCENT;
434
+ if (!isQueueInput) {
435
+ return (_jsx("div", { className: "rounded-md border bg-background p-3", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[13px] font-semibold", children: t('releases.new.traffic.production100') }), _jsx("div", { className: "mt-1 text-[12px] text-muted-foreground", children: t('canaryReleases.new.field.trafficRatioWebhookHelp') })] }), _jsx(Tag, { tone: "positive", children: "100%" })] }) }));
436
+ }
437
+ return (_jsxs("div", { className: "space-y-3 rounded-md border bg-background p-3", children: [showTrafficMode ? (_jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [_jsx("button", { type: "button", onClick: () => onTrafficModeChange('split'), "aria-pressed": trafficMode === 'split', className: cn('rounded-md border px-3 py-2 text-left transition-colors', trafficMode === 'split' ? 'border-primary bg-primary/10' : 'border-border bg-muted/30 hover:bg-muted/50'), children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Radio, { checked: trafficMode === 'split' }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[13px] font-semibold", children: t('releases.new.trafficMode.split') }), _jsx("div", { className: "mt-1 text-[12px] text-muted-foreground", children: t('releases.new.trafficMode.splitHelp') })] })] }) }), _jsx("button", { type: "button", onClick: () => onTrafficModeChange('dual_run'), "aria-pressed": trafficMode === 'dual_run', className: cn('rounded-md border px-3 py-2 text-left transition-colors', trafficMode === 'dual_run'
438
+ ? 'border-primary bg-primary/10'
439
+ : 'border-border bg-muted/30 hover:bg-muted/50'), children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Radio, { checked: trafficMode === 'dual_run' }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[13px] font-semibold", children: t('releases.new.trafficMode.dualRun') }), _jsx("div", { className: "mt-1 text-[12px] text-muted-foreground", children: t('releases.new.trafficMode.dualRunHelp') })] })] }) })] })) : null, _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[13px] font-semibold", children: t('releases.new.traffic.queueTitle') }), _jsx("div", { className: "mt-1 max-w-2xl text-[12px] text-muted-foreground", children: t('releases.new.traffic.queueHint') })] }), _jsxs(Tag, { tone: sliderValue === 100 ? 'positive' : 'info', children: [sliderValue, "%"] })] }), _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center", children: [_jsx("input", { type: "range", min: 1, max: 100, step: 1, value: sliderValue, "aria-label": t('releases.new.traffic.percentAriaLabel'), onChange: (event) => onChange(event.target.value), className: "min-w-0 flex-1 accent-primary" }), _jsxs("label", { className: "flex w-full items-center gap-2 sm:w-32", children: [_jsx(Input, { type: "number", min: 1, max: 100, value: value, onChange: (event) => onChange(event.target.value), "aria-label": t('releases.new.traffic.percentInput'), className: "h-8 font-mono text-xs", "data-testid": "release-new-traffic" }), _jsx("span", { className: "font-mono text-xs text-muted-foreground", children: "%" })] })] }), _jsx("div", { className: "flex flex-wrap items-center gap-1.5", children: TRAFFIC_PERCENT_PRESETS.map((preset) => {
440
+ const selected = parsed === preset;
441
+ return (_jsxs("button", { type: "button", onClick: () => onChange(String(preset)), "aria-pressed": selected, className: cn('rounded-md border px-2.5 py-1 font-mono text-[11.5px] transition-colors', selected
442
+ ? 'border-primary bg-primary text-primary-foreground'
443
+ : 'border-border bg-muted text-muted-foreground hover:bg-muted/70'), children: [preset, "%"] }, preset));
444
+ }) })] }));
445
+ }
446
+ function SummaryRow({ label, value }) {
447
+ return (_jsxs("div", { children: [_jsx("div", { className: "text-[11.5px] text-muted-foreground", children: label }), _jsx("div", { className: "mt-0.5 min-h-5 break-words text-[12.5px] font-medium", children: value })] }));
448
+ }
449
+ function DeployButton({ canSubmit, isPending, label, pendingLabel, className, testId, }) {
450
+ return (_jsx(Button, { type: "submit", className: className, disabled: !canSubmit, "data-testid": testId, children: isPending ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "size-4 animate-spin" }), pendingLabel] })) : (label) }));
451
+ }
452
+ export function ReleaseNewPage({ projectId }) {
453
+ const router = useRouter();
454
+ const searchParams = useSearchParams();
455
+ const { t } = useI18n();
456
+ const { formatDateTime } = useDateTimeFormatter();
457
+ const initialPromptId = searchParams.get('promptId') ?? '';
458
+ const initialPromptVersionId = searchParams.get('promptVersionId') ?? '';
459
+ const initialModelId = searchParams.get('modelId') ?? '';
460
+ const initialSourceExperimentId = searchParams.get('eventType') === 'from_experiment' ? (searchParams.get('sourceExperimentId') ?? '') : '';
461
+ const requestedMode = searchParams.get('mode');
462
+ const requestedLineId = normalizeLineId(searchParams.get('line'));
463
+ const isCanaryLineMode = requestedMode === 'canary' && requestedLineId.length > 0;
464
+ const promptsQuery = usePrompts(projectId);
465
+ const modelsQuery = useProjectModels(projectId, { autoRefresh: false });
466
+ const inputConnectorsQuery = useConnectors(projectId, { direction: 'input' });
467
+ const outputConnectorsQuery = useConnectors(projectId, { direction: 'output' });
468
+ const releaseLinesQuery = useReleaseLineList(projectId);
469
+ const createRelease = useCreateProductionRelease(projectId);
470
+ const createCanaryRelease = useCreateCanaryRelease(projectId);
471
+ const startCanaryRelease = useStartCanaryRelease(projectId);
472
+ const prompts = useMemo(() => promptsQuery.data?.data ?? [], [promptsQuery.data]);
473
+ const models = useMemo(() => (modelsQuery.data?.data ?? []).filter((model) => model.status !== 'disabled'), [modelsQuery.data]);
474
+ const inputConnectors = useMemo(() => inputConnectorsQuery.data?.data ?? [], [inputConnectorsQuery.data]);
475
+ const outputConnectors = useMemo(() => outputConnectorsQuery.data?.data ?? [], [outputConnectorsQuery.data]);
476
+ const outputConnectorById = useMemo(() => new Map(outputConnectors.map((connector) => [connector.id, connector])), [outputConnectors]);
477
+ const selectedReleaseLine = useMemo(() => releaseLinesQuery.data.find((line) => line.id === requestedLineId) ?? null, [releaseLinesQuery.data, requestedLineId]);
478
+ const parentProductionEvent = selectedReleaseLine?.production?.currentEvent ?? null;
479
+ const isAddCanaryToProduction = isCanaryLineMode && Boolean(parentProductionEvent);
480
+ const inheritedVariableMapping = useMemo(() => productionMappingToRecord(parentProductionEvent?.variableMapping), [parentProductionEvent?.variableMapping]);
481
+ const inheritedFilterRules = useMemo(() => normalizeInheritedFilterRules(parentProductionEvent?.filterRules), [parentProductionEvent?.filterRules]);
482
+ const inheritedOutputConnectorIds = useMemo(() => parentProductionEvent?.outputConnectorIds ?? [], [parentProductionEvent?.outputConnectorIds]);
483
+ const inheritedOutputConnectorIdSet = useMemo(() => new Set(inheritedOutputConnectorIds), [inheritedOutputConnectorIds]);
484
+ const [releaseName, setReleaseName] = useState(buildDefaultReleaseName);
485
+ const [description, setDescription] = useState('');
486
+ const [promptSearch, setPromptSearch] = useState('');
487
+ const [modelSearch, setModelSearch] = useState('');
488
+ const [selectedPromptId, setSelectedPromptId] = useState(initialPromptId);
489
+ const [selectedVersionId, setSelectedVersionId] = useState(initialPromptVersionId);
490
+ const [selectedModelId, setSelectedModelId] = useState(initialModelId);
491
+ const [selectedInputConnectorId, setSelectedInputConnectorId] = useState('');
492
+ const [selectedOutputConnectorIds, setSelectedOutputConnectorIds] = useState([]);
493
+ const [mappingOverrides, setMappingOverrides] = useState({});
494
+ const [externalIdFieldOverride, setExternalIdFieldOverride] = useState('');
495
+ const [filterRules, setFilterRules] = useState(null);
496
+ const [trafficPercent, setTrafficPercent] = useState(() => initialTrafficPercentFromParams(searchParams));
497
+ const [trafficMode, setTrafficMode] = useState(() => initialTrafficModeFromParams(searchParams));
498
+ const [recordCategorySelection, setRecordCategorySelection] = useState(null);
499
+ const [rpm, setRpm] = useState(searchParams.get('rpmLimit') ?? '');
500
+ const [tpm, setTpm] = useState(searchParams.get('tpmLimit') ?? '');
501
+ const [concurrency, setConcurrency] = useState(searchParams.get('concurrency') ?? '');
502
+ const [temperature, setTemperature] = useState(searchParams.get('temperature') ?? '0.3');
503
+ const [runtimeDefaultsModelId, setRuntimeDefaultsModelId] = useState(null);
504
+ const [submitError, setSubmitError] = useState(null);
505
+ const lockedPromptId = isAddCanaryToProduction ? (selectedReleaseLine?.promptId ?? '') : '';
506
+ const effectivePromptId = lockedPromptId || selectedPromptId || prompts[0]?.id || '';
507
+ const selectedPrompt = useMemo(() => prompts.find((prompt) => prompt.id === effectivePromptId) ?? null, [effectivePromptId, prompts]);
508
+ const selectedPromptQuery = usePrompt(projectId, selectedPrompt?.id ?? '');
509
+ const selectedPromptLoading = useDelayedLoading(selectedPromptQuery.isLoading);
510
+ const promptVersions = useMemo(() => selectedPrompt && selectedPromptQuery.data
511
+ ? selectedPromptQuery.data.versions
512
+ .map((version) => mapPromptVersionToOption(selectedPrompt, version, formatDateTime))
513
+ .sort((left, right) => Number(right.version.slice(1)) - Number(left.version.slice(1)))
514
+ : [], [formatDateTime, selectedPrompt, selectedPromptQuery.data]);
515
+ const availablePromptVersions = useMemo(() => isAddCanaryToProduction && parentProductionEvent
516
+ ? promptVersions.filter((option) => option.id !== parentProductionEvent.promptVersionId)
517
+ : promptVersions, [isAddCanaryToProduction, parentProductionEvent, promptVersions]);
518
+ const preferredVersion = useMemo(() => {
519
+ const latest = availablePromptVersions.find((option) => option.isLatest);
520
+ const online = availablePromptVersions.find((option) => option.isOnline);
521
+ return latest ?? online ?? availablePromptVersions[0] ?? null;
522
+ }, [availablePromptVersions]);
523
+ const effectiveVersionId = selectedVersionId && availablePromptVersions.some((option) => option.id === selectedVersionId)
524
+ ? selectedVersionId
525
+ : (preferredVersion?.id ?? '');
526
+ const selectedVersion = useMemo(() => availablePromptVersions.find((option) => option.id === effectiveVersionId) ?? null, [availablePromptVersions, effectiveVersionId]);
527
+ const effectiveModelId = selectedModelId || models[0]?.id || '';
528
+ const selectedModel = useMemo(() => models.find((model) => model.id === effectiveModelId) ?? null, [effectiveModelId, models]);
529
+ if (selectedModel && runtimeDefaultsModelId !== selectedModel.id) {
530
+ setRuntimeDefaultsModelId(selectedModel.id);
531
+ setRpm((current) => current.trim().length > 0 ? current : modelLimitDefaultValue(selectedModel.rpm.limit, DEFAULT_RPM));
532
+ setTpm((current) => current.trim().length > 0 ? current : modelLimitDefaultValue(selectedModel.tpm.limit, DEFAULT_TPM));
533
+ setConcurrency((current) => current.trim().length > 0
534
+ ? current
535
+ : modelLimitDefaultValue(selectedModel.concurrency.limit, DEFAULT_CONCURRENCY));
536
+ }
537
+ const lockedInputConnectorId = isAddCanaryToProduction ? (parentProductionEvent?.inputConnectorId ?? '') : '';
538
+ const effectiveInputConnectorId = lockedInputConnectorId || selectedInputConnectorId || inputConnectors[0]?.id || '';
539
+ const selectedInputConnector = useMemo(() => inputConnectors.find((connector) => connector.id === effectiveInputConnectorId) ?? null, [effectiveInputConnectorId, inputConnectors]);
540
+ const inputConnectorDetailQuery = useConnector(projectId, effectiveInputConnectorId);
541
+ const inputFieldOptions = useMemo(() => extractFieldOptionsFromConnector(inputConnectorDetailQuery.data ?? null), [inputConnectorDetailQuery.data]);
542
+ const inputFieldKeySet = useMemo(() => new Set(inputFieldOptions.map((field) => field.key)), [inputFieldOptions]);
543
+ const effectiveExternalIdField = isAddCanaryToProduction
544
+ ? (parentProductionEvent?.externalIdField ?? '')
545
+ : externalIdFieldOverride && inputFieldKeySet.has(externalIdFieldOverride)
546
+ ? externalIdFieldOverride
547
+ : inferExternalIdField(inputFieldOptions);
548
+ const isQueueInput = selectedInputConnector?.type === 'redis' || selectedInputConnector?.type === 'kafka';
549
+ const trafficPercentValue = isQueueInput ? trafficPercentFromText(trafficPercent) : 100;
550
+ const trafficRatioValue = trafficPercentValue === null ? null : trafficPercentValue / 100;
551
+ const shouldCreateCanaryRelease = isAddCanaryToProduction || (isQueueInput && trafficRatioValue !== null && trafficRatioValue < 1);
552
+ const isSubmitting = createRelease.isPending || createCanaryRelease.isPending || startCanaryRelease.isPending;
553
+ const filteredPrompts = useMemo(() => {
554
+ const query = promptSearch.trim().toLowerCase();
555
+ if (!query)
556
+ return prompts;
557
+ return prompts.filter((prompt) => [prompt.name, prompt.createdByDisplayName ?? '', `v${prompt.latestVersionNumber}`]
558
+ .join(' ')
559
+ .toLowerCase()
560
+ .includes(query));
561
+ }, [promptSearch, prompts]);
562
+ const filteredModels = useMemo(() => {
563
+ const query = modelSearch.trim().toLowerCase();
564
+ if (!query)
565
+ return models;
566
+ return models.filter((model) => `${model.name} ${model.providerType} ${model.providerModelId}`.toLowerCase().includes(query));
567
+ }, [modelSearch, models]);
568
+ const validSelectedOutputConnectorIds = useMemo(() => selectedOutputConnectorIds.filter((id) => outputConnectors.some((connector) => connector.id === id)), [outputConnectors, selectedOutputConnectorIds]);
569
+ const extraOutputConnectorIds = useMemo(() => validSelectedOutputConnectorIds.filter((id) => !inheritedOutputConnectorIdSet.has(id)), [inheritedOutputConnectorIdSet, validSelectedOutputConnectorIds]);
570
+ const validOutputConnectorIds = useMemo(() => {
571
+ if (!isAddCanaryToProduction)
572
+ return validSelectedOutputConnectorIds;
573
+ return trafficMode === 'dual_run'
574
+ ? mergeIds(inheritedOutputConnectorIds, extraOutputConnectorIds)
575
+ : inheritedOutputConnectorIds;
576
+ }, [
577
+ extraOutputConnectorIds,
578
+ inheritedOutputConnectorIds,
579
+ isAddCanaryToProduction,
580
+ trafficMode,
581
+ validSelectedOutputConnectorIds,
582
+ ]);
583
+ const recordCategoryOptions = useMemo(() => deriveRecordCategoryOptions(selectedVersion?.outputSchema ?? null), [selectedVersion]);
584
+ const effectiveRecordCategories = useMemo(() => recordCategorySelection === null
585
+ ? recordCategoryOptions
586
+ : recordCategorySelection.filter((category) => recordCategoryOptions.includes(category)), [recordCategoryOptions, recordCategorySelection]);
587
+ const rpmValue = positiveIntegerFromText(rpm);
588
+ const tpmValue = positiveIntegerFromText(tpm);
589
+ const concurrencyValue = positiveIntegerFromText(concurrency);
590
+ const temperatureValue = temperatureFromText(temperature);
591
+ const sourceForVariable = (variable) => {
592
+ if (isAddCanaryToProduction)
593
+ return inheritedVariableMapping[variable.name] ?? '';
594
+ const override = mappingOverrides[variable.name];
595
+ if (override && inputFieldKeySet.has(override))
596
+ return override;
597
+ return inferSourceForVariable(variable, inputFieldOptions);
598
+ };
599
+ const requiredVariableMappingComplete = !selectedVersion ||
600
+ selectedVersion.variables.every((variable) => !variable.required || sourceForVariable(variable).trim().length > 0);
601
+ const effectiveFilterRules = isAddCanaryToProduction ? inheritedFilterRules : filterRules;
602
+ const filterRulesValid = canaryReleaseFilterRulesSchema.safeParse(effectiveFilterRules).success;
603
+ const runConfigValid = numberTextWithinLimit(rpm, selectedModel?.rpm.limit) &&
604
+ numberTextWithinLimit(tpm, selectedModel?.tpm.limit) &&
605
+ numberTextWithinLimit(concurrency, selectedModel?.concurrency.limit) &&
606
+ temperatureValue !== null;
607
+ const effectiveReleaseName = isAddCanaryToProduction ? '' : releaseName.trim();
608
+ const effectiveDescription = isAddCanaryToProduction ? '' : description.trim();
609
+ const basicComplete = Boolean((isAddCanaryToProduction || effectiveReleaseName) && selectedVersion && selectedModel);
610
+ const connectorComplete = (!isCanaryLineMode || isAddCanaryToProduction) &&
611
+ Boolean(selectedInputConnector) &&
612
+ (!isQueueInput || effectiveExternalIdField.trim().length > 0) &&
613
+ requiredVariableMappingComplete &&
614
+ filterRulesValid &&
615
+ trafficRatioValue !== null;
616
+ const runtimeComplete = runConfigValid && (recordCategoryOptions.length === 0 || effectiveRecordCategories.length > 0);
617
+ const canSubmit = basicComplete && connectorComplete && runtimeComplete && !isSubmitting;
618
+ const handlePromptSelect = (promptId) => {
619
+ if (isAddCanaryToProduction)
620
+ return;
621
+ setSelectedPromptId(promptId);
622
+ setSelectedVersionId('');
623
+ setMappingOverrides({});
624
+ setExternalIdFieldOverride('');
625
+ setRecordCategorySelection(null);
626
+ };
627
+ const handleVersionSelect = (versionId) => {
628
+ setSelectedVersionId(versionId);
629
+ setMappingOverrides({});
630
+ setRecordCategorySelection(null);
631
+ };
632
+ const handleModelSelect = (model) => {
633
+ setSelectedModelId(model.id);
634
+ setRuntimeDefaultsModelId(model.id);
635
+ setRpm(modelLimitDefaultValue(model.rpm.limit, DEFAULT_RPM));
636
+ setTpm(modelLimitDefaultValue(model.tpm.limit, DEFAULT_TPM));
637
+ setConcurrency(modelLimitDefaultValue(model.concurrency.limit, DEFAULT_CONCURRENCY));
638
+ };
639
+ const handleInputConnectorSelect = (connectorId) => {
640
+ if (isAddCanaryToProduction)
641
+ return;
642
+ setSelectedInputConnectorId(connectorId);
643
+ setMappingOverrides({});
644
+ setExternalIdFieldOverride('');
645
+ setFilterRules(null);
646
+ };
647
+ const handleOutputConnectorToggle = (connectorId, selected) => {
648
+ if (isAddCanaryToProduction) {
649
+ if (trafficMode !== 'dual_run' || inheritedOutputConnectorIdSet.has(connectorId))
650
+ return;
651
+ }
652
+ setSelectedOutputConnectorIds((current) => selected ? current.filter((id) => id !== connectorId) : [...current, connectorId]);
653
+ };
654
+ const formatCreateError = (error) => {
655
+ const message = getApiErrorMessage(error);
656
+ if (message === 'release_name_taken')
657
+ return t('common.formError.nameTaken');
658
+ return message ?? t('releases.new.error.createFailed');
659
+ };
660
+ const buildVariableMapping = () => {
661
+ const mapping = {};
662
+ for (const variable of selectedVersion?.variables ?? []) {
663
+ const source = sourceForVariable(variable).trim();
664
+ if (source)
665
+ mapping[variable.name] = source;
666
+ }
667
+ if (effectiveExternalIdField && !mapping.id)
668
+ mapping.id = effectiveExternalIdField;
669
+ return mapping;
670
+ };
671
+ const buildCanaryVariableMapping = (mapping) => {
672
+ const requiredByTarget = new Map((selectedVersion?.variables ?? []).map((variable) => [variable.name, variable.required]));
673
+ return Object.entries(mapping).map(([target, source]) => ({
674
+ source,
675
+ target,
676
+ required: target === 'id' || Boolean(requiredByTarget.get(target)),
677
+ }));
678
+ };
679
+ const handleSubmit = async (event) => {
680
+ event.preventDefault();
681
+ setSubmitError(null);
682
+ if (!canSubmit ||
683
+ !rpmValue ||
684
+ !tpmValue ||
685
+ !concurrencyValue ||
686
+ trafficRatioValue === null ||
687
+ temperatureValue === null ||
688
+ !selectedVersion ||
689
+ !selectedModel ||
690
+ !selectedInputConnector) {
691
+ setSubmitError(t('common.formError.requiredMissing'));
692
+ return;
693
+ }
694
+ const submitReason = effectiveDescription
695
+ ? `${effectiveReleaseName}\n${effectiveDescription}`
696
+ : effectiveReleaseName;
697
+ const variableMapping = buildVariableMapping();
698
+ const recordMode = releaseRecordModeFromCategories(effectiveRecordCategories, recordCategoryOptions);
699
+ const productionRunConfig = {
700
+ rpmLimit: rpmValue,
701
+ tpmLimit: tpmValue,
702
+ concurrency: concurrencyValue,
703
+ temperature: temperatureValue,
704
+ };
705
+ const canaryRunConfig = {
706
+ rpmLimit: rpmValue,
707
+ tpmLimit: tpmValue,
708
+ concurrency: concurrencyValue,
709
+ temperature: temperatureValue,
710
+ };
711
+ if (shouldCreateCanaryRelease) {
712
+ const payload = {
713
+ ...(isAddCanaryToProduction
714
+ ? {}
715
+ : {
716
+ name: effectiveReleaseName,
717
+ description: effectiveDescription,
718
+ }),
719
+ promptVersionId: selectedVersion.id,
720
+ modelId: selectedModel.id,
721
+ inputConnectorId: selectedInputConnector.id,
722
+ outputConnectorIds: validOutputConnectorIds,
723
+ trafficRatio: trafficRatioValue,
724
+ trafficMode,
725
+ runMode: 'manual',
726
+ recordMode,
727
+ variableMapping: buildCanaryVariableMapping(variableMapping),
728
+ outputMapping: [],
729
+ filterRules: effectiveFilterRules,
730
+ stopConditions: null,
731
+ externalIdField: effectiveExternalIdField,
732
+ annotationSchema: [],
733
+ storageCategories: effectiveRecordCategories.length === recordCategoryOptions.length ? [] : effectiveRecordCategories,
734
+ targetDatasetId: null,
735
+ runConfig: canaryRunConfig,
736
+ };
737
+ try {
738
+ const canary = await createCanaryRelease.mutateAsync(payload);
739
+ await startCanaryRelease.mutateAsync(canary.id);
740
+ router.push(`/releases/${getReleaseLineId(effectivePromptId, canary.inputConnectorId)}`);
741
+ }
742
+ catch (error) {
743
+ setSubmitError(formatCreateError(error));
744
+ }
745
+ return;
746
+ }
747
+ const payload = {
748
+ promptId: effectivePromptId,
749
+ promptVersionId: selectedVersion.id,
750
+ modelId: selectedModel.id,
751
+ inputConnectorId: selectedInputConnector.id,
752
+ outputConnectorIds: validOutputConnectorIds,
753
+ eventType: initialSourceExperimentId ? 'from_experiment' : 'from_prompt',
754
+ runConfig: productionRunConfig,
755
+ variableMapping,
756
+ filterRules: effectiveFilterRules,
757
+ recordMode,
758
+ externalIdField: effectiveExternalIdField || null,
759
+ retentionDays: null,
760
+ submitReason,
761
+ sourceExperimentId: initialSourceExperimentId || null,
762
+ sourceCanaryId: null,
763
+ sourceMetricsSnapshot: null,
764
+ rollbackTargetEventId: null,
765
+ };
766
+ createRelease.mutate(payload, {
767
+ onSuccess: (event) => {
768
+ router.push(`/releases/${getReleaseLineId(event.promptId, event.inputConnectorId)}`);
769
+ },
770
+ onError: (error) => {
771
+ setSubmitError(formatCreateError(error));
772
+ },
773
+ });
774
+ };
775
+ return (_jsx(Main, { fixed: true, className: "gap-5 overflow-auto bg-muted/35 pb-24", children: _jsxs("form", { onSubmit: handleSubmit, className: "mx-auto flex w-full max-w-[1280px] flex-col gap-5", "data-testid": "release-new-page", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-[22px] font-semibold leading-tight", children: isAddCanaryToProduction ? t('releases.new.canary.title') : t('releases.new.title') }), _jsx("p", { className: "mt-1 max-w-3xl text-[12.5px] text-muted-foreground", children: isAddCanaryToProduction ? t('releases.new.canary.subtitle') : t('releases.new.subtitle') })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { href: "/releases", children: t('common.cancel') }) }), _jsx(DeployButton, { canSubmit: canSubmit, isPending: isSubmitting, label: shouldCreateCanaryRelease
776
+ ? t('canaryReleases.new.action.submit')
777
+ : t('productionReleases.new.action.submit'), pendingLabel: shouldCreateCanaryRelease
778
+ ? t('canaryReleases.new.action.submitting')
779
+ : t('productionReleases.new.action.submitting') })] })] }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1fr)_320px]", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [_jsxs(StepCard, { index: 1, done: basicComplete, title: t('releases.new.steps.basic'), detail: isAddCanaryToProduction
780
+ ? t('releases.new.steps.basicCanaryDetail')
781
+ : t('releases.new.steps.basicDetail'), testId: "release-new-step-basic", children: [!isAddCanaryToProduction ? (_jsxs("div", { children: [_jsx(SubSectionHead, { label: t('releases.new.basic.section') }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "text-[12.5px]", children: [t('releases.new.field.name'), " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { value: releaseName, onChange: (event) => setReleaseName(event.target.value), placeholder: t('releases.new.field.namePlaceholder'), className: "font-mono text-[13px]", "data-testid": "release-new-name" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-[12.5px]", children: t('releases.new.field.description') }), _jsx(Input, { value: description, onChange: (event) => setDescription(event.target.value), placeholder: t('releases.new.field.descriptionPlaceholder') })] })] })] })) : null, _jsxs("div", { className: cn(!isAddCanaryToProduction && 'border-t border-dashed pt-5'), children: [_jsx(SubSectionHead, { label: t('canaryReleases.new.field.prompt') }), _jsxs("div", { className: "rounded-md border bg-background", children: [isAddCanaryToProduction ? (_jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1fr)]", children: [_jsxs("div", { className: "border-b lg:border-b-0 lg:border-r", children: [_jsx("div", { className: "border-b px-3 py-2 font-mono text-[10.5px] uppercase tracking-wide text-muted-foreground", children: t('canaryReleases.new.promptColumn') }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "rounded-md border bg-muted/35 p-3", children: [_jsx(SummaryRow, { label: t('releases.new.canary.lockedPrompt'), value: selectedPrompt?.name ?? selectedReleaseLine?.label ?? '—' }), _jsx("div", { className: "mt-3", children: _jsx(SummaryRow, { label: t('releases.new.summary.prompt'), value: selectedReleaseLine?.productionVersionLabel ?? '—' }) })] }), _jsx("p", { className: "text-[12px] text-muted-foreground", children: t('releases.new.canary.lockedPromptHelp') })] })] }), _jsxs("div", { children: [_jsx("div", { className: "border-b px-3 py-2 font-mono text-[10.5px] uppercase tracking-wide text-muted-foreground", children: t('canaryReleases.new.versionColumn') }), _jsx("div", { className: "max-h-[340px] overflow-y-auto overflow-x-hidden", children: selectedPromptLoading ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.versionLoading') })) : selectedPromptQuery.isError ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.versionEmpty') })) : availablePromptVersions.length === 0 ? (_jsx(PickerEmpty, { children: t('releases.new.canary.versionEmpty') })) : (availablePromptVersions.map((version) => (_jsx("div", { "data-testid": `release-new-version-row-${version.id}`, children: _jsx(PromptVersionRow, { option: version, selected: version.id === effectiveVersionId, onSelect: () => handleVersionSelect(version.id) }) }, version.id)))) })] })] })) : (_jsxs(_Fragment, { children: [_jsx(MiniSearch, { value: promptSearch, onChange: setPromptSearch, placeholder: t('canaryReleases.new.promptSearch') }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1fr)]", children: [_jsxs("div", { className: "border-b lg:border-b-0 lg:border-r", children: [_jsx("div", { className: "border-b px-3 py-2 font-mono text-[10.5px] uppercase tracking-wide text-muted-foreground", children: t('canaryReleases.new.promptColumn') }), _jsx("div", { className: "max-h-[340px] overflow-y-auto overflow-x-hidden", children: promptsQuery.isError ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.promptEmpty') })) : filteredPrompts.length === 0 ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.promptEmpty') })) : (filteredPrompts.map((prompt) => (_jsx("div", { "data-testid": `release-new-prompt-row-${prompt.id}`, children: _jsx(PromptRow, { prompt: prompt, selected: prompt.id === effectivePromptId, onSelect: () => handlePromptSelect(prompt.id) }) }, prompt.id)))) })] }), _jsxs("div", { children: [_jsx("div", { className: "border-b px-3 py-2 font-mono text-[10.5px] uppercase tracking-wide text-muted-foreground", children: t('canaryReleases.new.versionColumn') }), _jsx("div", { className: "max-h-[340px] overflow-y-auto overflow-x-hidden", children: selectedPromptLoading ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.versionLoading') })) : selectedPromptQuery.isError ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.versionEmpty') })) : availablePromptVersions.length === 0 ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.versionEmpty') })) : (availablePromptVersions.map((version) => (_jsx("div", { "data-testid": `release-new-version-row-${version.id}`, children: _jsx(PromptVersionRow, { option: version, selected: version.id === effectiveVersionId, onSelect: () => handleVersionSelect(version.id) }) }, version.id)))) })] })] })] })), _jsx(PromptVersionPreview, { option: selectedVersion })] })] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsx(SubSectionHead, { label: t('canaryReleases.new.field.model') }), _jsxs("div", { className: "max-h-[360px] overflow-y-auto overflow-x-hidden rounded-md border bg-background", children: [_jsx(MiniSearch, { value: modelSearch, onChange: setModelSearch, placeholder: t('canaryReleases.new.modelSearch') }), modelsQuery.isError ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.modelEmpty') })) : filteredModels.length === 0 ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.modelEmpty') })) : (filteredModels.map((model) => (_jsx("div", { "data-testid": `release-new-model-row-${model.id}`, children: _jsx(ModelOptionRow, { model: model, selected: model.id === effectiveModelId, onSelect: () => handleModelSelect(model) }) }, model.id))))] })] })] }), _jsxs(StepCard, { index: 2, done: connectorComplete, title: t('releases.new.steps.connectors'), detail: t('releases.new.steps.connectorsDetail'), testId: "release-new-step-connectors", children: [_jsxs("div", { children: [_jsxs("div", { className: "mb-2 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx(Label, { children: t('canaryReleases.new.field.inputConnector') }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: isAddCanaryToProduction
782
+ ? t('releases.new.canary.lockedInputConnectorHelp')
783
+ : t('canaryReleases.new.field.inputConnectorHelp') })] }), isAddCanaryToProduction ? null : (_jsx(Button, { variant: "outline", size: "sm", asChild: true, children: _jsxs(Link, { href: "/connectors/new?direction=input", children: [_jsx(Plus, { className: "size-3.5" }), t('releases.new.action.newInputConnector')] }) }))] }), isAddCanaryToProduction ? (_jsx(ReadOnlyConnectorRow, { connector: selectedInputConnector, name: parentProductionEvent?.inputConnectorId ?? null, emptyLabel: t('releases.new.placeholder.inputConnector') })) : (_jsx("div", { className: "max-h-[260px] overflow-y-auto overflow-x-hidden rounded-md border bg-background", children: inputConnectors.length === 0 ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.inputConnectorEmpty') })) : (inputConnectors.map((connector) => (_jsx("div", { "data-testid": `release-new-input-connector-${connector.id}`, children: _jsx(ConnectorOptionRow, { connector: connector, selected: connector.id === effectiveInputConnectorId, onSelect: () => handleInputConnectorSelect(connector.id) }) }, connector.id)))) }))] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsx(Label, { children: t('canaryReleases.new.field.variableMapping') }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: isAddCanaryToProduction
784
+ ? t('releases.new.canary.lockedMappingHelp')
785
+ : t('canaryReleases.new.field.variableMappingHelp') }), _jsx("div", { className: "mt-3", children: _jsx(FieldMappingTable, { fields: inputFieldOptions, promptVariables: selectedVersion?.variables ?? [], externalIdField: effectiveExternalIdField, mapping: Object.fromEntries((selectedVersion?.variables ?? []).map((variable) => [
786
+ variable.name,
787
+ sourceForVariable(variable),
788
+ ])), readOnly: isAddCanaryToProduction, onExternalIdFieldChange: setExternalIdFieldOverride, onMappingChange: (target, source) => setMappingOverrides((current) => ({ ...current, [target]: source })) }) }), effectiveInputConnectorId && inputFieldOptions.length === 0 ? (_jsx("div", { className: "mt-2 text-[11.5px] text-muted-foreground", children: selectedInputConnector?.type === 'webhook'
789
+ ? t('canaryReleases.new.fieldMappingEmptyWebhook')
790
+ : t('canaryReleases.new.fieldMappingEmptyQueue') })) : null] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsx(Label, { children: t('canaryReleases.new.field.filterRules') }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: isAddCanaryToProduction
791
+ ? t('releases.new.canary.lockedFilterHelp')
792
+ : t('canaryReleases.new.field.filterRulesHelp') }), _jsx("div", { className: "mt-3", children: isAddCanaryToProduction ? (_jsx(ReadOnlyFilterRules, { value: effectiveFilterRules })) : (_jsx(FilterRulesBuilder, { value: filterRules, fields: inputFieldOptions, onChange: setFilterRules })) })] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsx(Label, { children: t('releases.new.field.traffic') }), _jsx("div", { className: "mt-2", children: _jsx(TrafficSelectionField, { isQueueInput: isQueueInput, value: isQueueInput ? trafficPercent : '100', trafficMode: trafficMode, showTrafficMode: isAddCanaryToProduction, onChange: setTrafficPercent, onTrafficModeChange: setTrafficMode }) })] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsxs("div", { className: "mb-2 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx(Label, { children: t('canaryReleases.new.field.outputConnector') }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: isAddCanaryToProduction
793
+ ? trafficMode === 'dual_run'
794
+ ? t('releases.new.canary.outputDualRunHelp')
795
+ : t('releases.new.canary.outputSplitLocked')
796
+ : t('canaryReleases.new.field.outputConnectorHelp') })] }), isAddCanaryToProduction && trafficMode === 'split' ? null : (_jsx(Button, { variant: "outline", size: "sm", asChild: true, children: _jsxs(Link, { href: "/connectors/new?direction=output", children: [_jsx(Plus, { className: "size-3.5" }), t('releases.new.action.newOutputConnector')] }) }))] }), isAddCanaryToProduction && trafficMode === 'split' ? (_jsx("div", { className: "space-y-2", children: inheritedOutputConnectorIds.length === 0 ? (_jsx(PickerEmpty, { children: t('releases.new.canary.noInheritedOutput') })) : (inheritedOutputConnectorIds.map((connectorId) => (_jsx(ReadOnlyConnectorRow, { connector: outputConnectorById.get(connectorId), name: connectorId, emptyLabel: connectorId }, connectorId)))) })) : (_jsx("div", { className: "max-h-[260px] overflow-y-auto overflow-x-hidden rounded-md border bg-background", children: outputConnectors.length === 0 ? (_jsx(PickerEmpty, { children: t('canaryReleases.new.outputConnectorEmpty') })) : (outputConnectors.map((connector) => {
797
+ const selected = validOutputConnectorIds.includes(connector.id);
798
+ const locked = isAddCanaryToProduction && inheritedOutputConnectorIdSet.has(connector.id);
799
+ return (_jsx(ConnectorOptionRow, { connector: connector, multiple: true, selected: selected || locked, onSelect: () => handleOutputConnectorToggle(connector.id, selected) }, connector.id));
800
+ })) }))] })] }), _jsxs(StepCard, { index: 3, done: runtimeComplete, title: t('releases.new.steps.runtime'), detail: t('releases.new.steps.runtimeDetail'), testId: "release-new-step-runtime", children: [_jsxs("div", { children: [_jsx(SubSectionHead, { label: t('productionReleases.new.section.runtime') }), _jsxs("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-4", children: [_jsx(RuntimeLimitField, { label: t('productionReleases.new.field.rpm'), value: rpm, modelLimit: selectedModel ? formatModelLimit(selectedModel.rpm.limit) : '—', onChange: setRpm }), _jsx(RuntimeLimitField, { label: t('productionReleases.new.field.tpm'), value: tpm, modelLimit: selectedModel ? formatModelLimit(selectedModel.tpm.limit) : '—', onChange: setTpm }), _jsx(RuntimeLimitField, { label: t('productionReleases.new.field.concurrency'), value: concurrency, modelLimit: selectedModel ? formatModelLimit(selectedModel.concurrency.limit) : '—', onChange: setConcurrency }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "text-[12.5px]", children: [t('productionReleases.new.field.temperature'), " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { type: "number", min: 0, max: 2, step: 0.1, value: temperature, onChange: (event) => setTemperature(event.target.value), className: "h-9" })] })] })] }), _jsxs("div", { className: "border-t border-dashed pt-5", children: [_jsx(Label, { children: t('canaryReleases.new.field.recordCategories') }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t('canaryReleases.new.field.recordCategoriesHelp') }), _jsx("div", { className: "mt-3", children: _jsx(RecordCategoriesField, { value: effectiveRecordCategories, options: recordCategoryOptions, onChange: setRecordCategorySelection }) })] })] })] }), _jsx("aside", { className: "flex flex-col gap-3 xl:sticky xl:top-20 xl:self-start", children: _jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx("h2", { className: "text-[14px] font-semibold", children: t('releases.new.section.summary') }), _jsxs("div", { className: "mt-4 space-y-3", children: [!isAddCanaryToProduction ? (_jsx(SummaryRow, { label: t('releases.new.field.name'), value: effectiveReleaseName || '—' })) : null, _jsx(SummaryRow, { label: t('releases.new.summary.prompt'), value: selectedPrompt && selectedVersion ? `${selectedPrompt.name} · ${selectedVersion.version}` : '—' }), _jsx(SummaryRow, { label: t('productionReleases.new.field.model'), value: selectedModel ? `${selectedModel.name} · ${selectedModel.providerModelId}` : '—' }), _jsx(SummaryRow, { label: t('releases.new.summary.connector'), value: selectedInputConnector?.name ?? '—' }), _jsx(SummaryRow, { label: t('releases.new.field.traffic'), value: trafficRatioValue === null
801
+ ? '—'
802
+ : isQueueInput
803
+ ? `${Math.round(trafficRatioValue * 100)}%`
804
+ : t('releases.new.traffic.production100') }), shouldCreateCanaryRelease ? (_jsx(SummaryRow, { label: t('releases.new.summary.trafficMode'), value: trafficMode === 'dual_run'
805
+ ? t('releases.new.trafficMode.dualRun')
806
+ : t('releases.new.trafficMode.split') })) : null, _jsx(SummaryRow, { label: t('releases.new.summary.outputs'), value: validOutputConnectorIds.length > 0
807
+ ? String(validOutputConnectorIds.length)
808
+ : t('productionReleases.new.noOutputConnector') }), _jsx(SummaryRow, { label: t('releases.new.summary.runtime'), value: rpmValue && tpmValue && concurrencyValue
809
+ ? `${rpmValue} RPM / ${tpmValue} TPM / C${concurrencyValue}`
810
+ : '—' })] }), _jsx("div", { className: "mt-4 rounded-md border bg-muted/35 px-3 py-2 text-[12px] text-muted-foreground", children: canSubmit ? t('releases.new.submitReady') : t('releases.new.submitDisabled') }), submitError ? (_jsxs("div", { className: "mt-3 flex gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [_jsx(AlertCircle, { className: "mt-0.5 size-3.5 shrink-0" }), _jsx("span", { children: submitError })] })) : null, _jsx("p", { className: "mt-2 text-[11.5px] leading-5 text-muted-foreground", children: t('productionReleases.new.confirmIrreversible') })] }) })] }), _jsx("div", { className: "fixed bottom-0 left-0 right-0 z-30 border-t bg-background/95 px-4 py-3 shadow-[0_-8px_24px_rgb(15_23_42/0.08)] backdrop-blur supports-[backdrop-filter]:bg-background/80 md:left-[var(--sidebar-width)]", children: _jsxs("div", { className: "mx-auto flex w-full max-w-[1280px] flex-wrap items-center justify-between gap-3", children: [_jsx("p", { className: "text-[12px] text-muted-foreground", children: t('productionReleases.new.confirmIrreversible') }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { href: "/releases", children: t('common.cancel') }) }), _jsx(DeployButton, { canSubmit: canSubmit, isPending: isSubmitting, label: shouldCreateCanaryRelease
811
+ ? t('canaryReleases.new.action.submit')
812
+ : t('productionReleases.new.action.submit'), pendingLabel: shouldCreateCanaryRelease
813
+ ? t('canaryReleases.new.action.submitting')
814
+ : t('productionReleases.new.action.submitting'), className: "min-w-28", testId: "release-new-submit" })] })] }) })] }) }));
815
+ }
816
+ //# sourceMappingURL=release-new-page.js.map