@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,1013 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { promptVersionLabelNameSchema, } from '@proofhound/shared';
4
+ import Link from 'next/link';
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { usePathname, useRouter, useSearchParams } from 'next/navigation';
7
+ import { ArrowLeft, BarChart3, CheckCircle2, Copy, Database, Eye, FlaskConical, GitCompareArrows, Info, Lock, Pencil, Plus, Save, Search, Sparkles, Tags, Trash2, Upload, X, } from 'lucide-react';
8
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, PlatformLoaderOverlay, DetailPageSkeleton, Skeleton, TableActionRow, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, ModalityIcon, ModalityIconGroup, UnusedImagesBadge, cn, } from '@proofhound/ui';
9
+ import { Main } from '@proofhound/ui/layout';
10
+ import { PromptDiffSplitView, PromptVariableModalityBadges } from '../../components';
11
+ import { useDatasets } from '../../hooks';
12
+ import { useCreatePromptDraftVersion, useDeletePromptDraftVersion, useDateTimeFormatter, usePrompt, usePromptMetrics, usePromptVersionDeleteImpact, useUpdatePrompt, useUpdatePromptDraftVersion, useUpdatePromptVersionLabel, } from '../../hooks';
13
+ import { useDelayedLoading } from '../../hooks';
14
+ import { PromptLanguageSelect } from '../../components';
15
+ import { useI18n } from '../../i18n';
16
+ import { DATASET_MODALITY_LABEL_KEYS, } from '../datasets/dataset-types';
17
+ import { toProjectDataset } from '../datasets/dataset-mappers';
18
+ import { deriveJudgmentField, toProjectPrompt, upsertJudgmentField, } from './prompt-model';
19
+ import { toPromptVariablesFromDataset } from './prompt-dataset-variables';
20
+ import { PromptBodyEditor } from './prompt-body-editor';
21
+ import { composePromptPreview } from './prompt-preview';
22
+ import { countPromptVariableUsages, renderPromptPreviewParts } from './prompt-preview-parts';
23
+ import { StatusBadge, VARIABLE_TONE_CLASSES, VariableToken, hasImageVariable } from './prompt-ui';
24
+ const LABEL_ACTION_MESSAGE_DISMISS_MS = 3000;
25
+ const PROMPT_VERSION_SYSTEM_LABEL_KEYS = {
26
+ latest: 'prompts.labels.system.latest',
27
+ gray: 'prompts.labels.system.gray',
28
+ production: 'prompts.labels.system.production',
29
+ };
30
+ const TAB_LABEL_KEYS = {
31
+ versions: 'prompts.detail.tab.versions',
32
+ metrics: 'prompts.detail.tab.metrics',
33
+ };
34
+ const PROMPT_MAIN_TAB_LABEL_KEYS = {
35
+ prompt: 'prompts.detail.subtab.prompt',
36
+ config: 'prompts.detail.subtab.config',
37
+ };
38
+ function resolveDetailTab(value) {
39
+ return value === 'metrics' ? 'metrics' : 'versions';
40
+ }
41
+ function resolvePromptMainTab(value) {
42
+ return value === 'config' ? 'config' : 'prompt';
43
+ }
44
+ const OUTPUT_SCHEMA_PANEL_STRICT_JSON_LABEL = {
45
+ 'zh-CN': '请严格输出 JSON:',
46
+ 'en-US': 'Return strict JSON:',
47
+ };
48
+ const UNSAVED_HISTORY_GUARD_KEY = '__proofhoundPromptUnsavedGuard';
49
+ const PROMPT_MODALITY_LABEL_KEYS = {
50
+ text: 'prompts.variableType.text',
51
+ image: 'prompts.variableType.image',
52
+ number: 'prompts.variableType.number',
53
+ };
54
+ const IMPACT_LABEL_KEYS = {
55
+ experiment: 'prompts.deleteImpactExperiment',
56
+ optimization: 'prompts.deleteImpactOptimization',
57
+ canary_release: 'prompts.deleteImpactCanaryRelease',
58
+ production_release: 'prompts.deleteImpactProductionRelease',
59
+ };
60
+ function DeleteImpactPanel({ impact, loading }) {
61
+ const { t } = useI18n();
62
+ const items = impact
63
+ ? [...impact.experiments, ...impact.optimizations, ...impact.canaryReleases, ...impact.productionReleases]
64
+ : [];
65
+ if (loading) {
66
+ return (_jsx("div", { className: "rounded-md border bg-muted/35 p-3 text-sm text-muted-foreground", children: t('prompts.deleteImpactLoading') }));
67
+ }
68
+ if (items.length === 0) {
69
+ return (_jsx("div", { className: "rounded-md border bg-muted/35 p-3 text-sm text-muted-foreground", children: t('prompts.deleteImpactEmpty') }));
70
+ }
71
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "text-sm font-medium", children: [t('prompts.deleteImpactTitle'), " ", _jsx("span", { className: "font-mono", children: items.length })] }), _jsx("div", { className: "max-h-[260px] space-y-1 overflow-auto", children: items.map((item) => (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded border px-2.5 py-2 text-xs", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "truncate font-medium", children: [t(IMPACT_LABEL_KEYS[item.kind]), " \u00B7 ", item.name ?? item.id] }), _jsxs("div", { className: "mt-0.5 font-mono text-[11px] text-muted-foreground", children: [item.promptVersionNumber ? `v${item.promptVersionNumber}` : '-', " \u00B7 ", item.status ?? '-'] })] }), _jsx("span", { className: "shrink-0 font-mono text-[10.5px] text-muted-foreground", children: item.id.slice(0, 8) })] }, `${item.kind}-${item.id}`))) })] }));
72
+ }
73
+ function DatasetSelectionPanel({ variables, datasets, selectedDatasetId, onSelectDataset, readOnly = false, }) {
74
+ const { t } = useI18n();
75
+ const [datasetSearch, setDatasetSearch] = useState('');
76
+ const selectedCount = variables.filter((variable) => variable.selected).length;
77
+ const datasetOptions = useMemo(() => {
78
+ return datasets
79
+ .filter((dataset) => dataset.status === 'active')
80
+ .map((dataset) => ({
81
+ id: dataset.id,
82
+ name: dataset.name,
83
+ description: dataset.description,
84
+ modalities: dataset.modalities,
85
+ hasImages: dataset.hasImages,
86
+ sampleCount: dataset.sampleCount,
87
+ fieldCount: dataset.fieldCount,
88
+ updatedAt: dataset.updatedAt,
89
+ status: dataset.status,
90
+ }));
91
+ }, [datasets]);
92
+ const filteredDatasets = useMemo(() => {
93
+ const query = datasetSearch.trim().toLowerCase();
94
+ if (!query)
95
+ return datasetOptions;
96
+ return datasetOptions.filter((dataset) => `${dataset.name} ${dataset.description} ${dataset.id}`.toLowerCase().includes(query));
97
+ }, [datasetOptions, datasetSearch]);
98
+ const selectedDataset = datasetOptions.find((dataset) => dataset.id === selectedDatasetId) ?? null;
99
+ const isUnbound = !selectedDatasetId;
100
+ return (_jsxs(_Fragment, { children: [isUnbound && (_jsx("div", { className: "mb-3 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs font-medium text-destructive", role: "alert", "data-testid": "prompt-dataset-unbound-warning", children: t('prompts.detail.datasetUnboundWarning') })), _jsxs("section", { className: "mb-4 overflow-hidden rounded-lg border bg-card", "aria-label": t('prompts.detail.boundDataset'), "data-testid": "prompt-dataset-selector", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b px-4 py-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "inline-flex items-center gap-1.5 text-[12.5px] font-semibold", children: [_jsx(Database, { className: "size-4 text-[var(--status-pending-fg)]" }), t('prompts.detail.boundDataset'), selectedDataset?.hasImages && !hasImageVariable(variables) && (_jsx(UnusedImagesBadge, { size: "sm", tooltip: t('prompts.detail.unusedImagesTooltip'), "aria-label": t('prompts.detail.unusedImagesTooltip') }))] }), _jsxs("div", { className: "mt-1 truncate text-[11.5px] text-muted-foreground", children: [selectedDataset
101
+ ? t('prompts.detail.selectedDataset').replace('{dataset}', selectedDataset.name)
102
+ : t('prompts.detail.noDatasetSelected'), ' · ', t('prompts.detail.datasetFieldSummary')
103
+ .replace('{fieldCount}', String(selectedDataset?.fieldCount ?? 0))
104
+ .replace('{selectedCount}', String(selectedCount))] })] }), _jsxs("div", { className: "relative w-full sm:w-[260px]", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: datasetSearch, onChange: (event) => setDatasetSearch(event.target.value), placeholder: t('prompts.detail.datasetSearchPlaceholder'), className: "h-9 pl-8 text-xs" })] })] }), _jsx("div", { className: "max-h-[190px] overflow-auto p-2", children: filteredDatasets.length > 0 ? (_jsx("div", { className: "grid gap-1 sm:grid-cols-2 xl:grid-cols-3", children: filteredDatasets.map((dataset) => {
105
+ const selected = dataset.id === selectedDatasetId;
106
+ return (_jsxs("button", { type: "button", disabled: readOnly, onClick: () => onSelectDataset(dataset.id), className: cn('flex min-w-0 items-start gap-2 rounded-md border px-2.5 py-2 text-left transition-colors hover:bg-muted/40', selected ? 'border-primary bg-primary/5' : 'border-border bg-background', dataset.status === 'deleted' && 'opacity-70', readOnly && 'cursor-not-allowed opacity-60'), "aria-pressed": selected, children: [_jsx("span", { className: cn('mt-0.5 inline-flex size-3.5 flex-none items-center justify-center rounded-full border', selected ? 'border-primary bg-primary/10' : 'border-border bg-background'), "aria-hidden": "true", children: selected && _jsx("span", { className: "size-1.5 rounded-full bg-primary" }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate font-mono text-[12px] font-semibold", children: dataset.name }), _jsx("span", { className: "mt-1 block truncate text-[11px] text-muted-foreground", children: dataset.description }), _jsxs("span", { className: "mt-1 flex flex-wrap items-center gap-1", children: [_jsxs("span", { className: "rounded border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground", children: [dataset.sampleCount.toLocaleString(), " ", t('prompts.detail.samples')] }), _jsx("span", { className: "rounded border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground", children: t('prompts.detail.datasetFields').replace('{count}', String(dataset.fieldCount)) }), _jsx(ModalityIconGroup, { kinds: dataset.modalities, size: "sm", tooltips: dataset.modalities.reduce((acc, modality) => {
107
+ acc[modality] = t(DATASET_MODALITY_LABEL_KEYS[modality]);
108
+ return acc;
109
+ }, {}) })] })] })] }, dataset.id));
110
+ }) })) : (_jsx("div", { className: "px-3 py-8 text-center text-[12px] text-muted-foreground", children: t('prompts.detail.datasetNoMatch') })) })] })] }));
111
+ }
112
+ function getNextOutputFieldKey(fields) {
113
+ let index = fields.length + 1;
114
+ const existingKeys = new Set(fields.map((field) => field.key));
115
+ while (existingKeys.has(`field_${index}`))
116
+ index += 1;
117
+ return `field_${index}`;
118
+ }
119
+ function OutputSchemaPanel({ fields, promptLanguage, onFieldsChange, readOnly = false, }) {
120
+ const { t } = useI18n();
121
+ const updateField = (index, patch) => {
122
+ onFieldsChange(fields.map((field, fieldIndex) => (fieldIndex === index && !field.isJudgment ? { ...field, ...patch } : field)));
123
+ };
124
+ const addField = () => {
125
+ onFieldsChange([
126
+ ...fields,
127
+ {
128
+ key: getNextOutputFieldKey(fields),
129
+ value: '',
130
+ isJudgment: false,
131
+ },
132
+ ]);
133
+ };
134
+ const removeField = (index) => {
135
+ onFieldsChange(fields.filter((field, fieldIndex) => !(fieldIndex === index && !field.isJudgment)));
136
+ };
137
+ return (_jsxs("div", { className: "border-t bg-muted/55", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 px-4 py-2", children: [_jsx(CheckCircle2, { className: "size-3.5 text-[var(--status-canary-fg)]" }), _jsx("span", { className: "text-[12.5px] font-semibold text-muted-foreground", children: t('prompts.detail.outputSchema') }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: t('prompts.detail.outputSchemaHelp') }), _jsxs("span", { className: "ml-auto inline-flex items-center gap-1 font-mono text-[10.5px] text-[var(--status-running-fg)]", children: [_jsx("span", { className: "size-1 rounded-full bg-[var(--status-running-dot)]" }), t('prompts.detail.schemaLinked')] })] }), _jsxs("div", { className: "px-4 pb-3 font-mono text-[12px] leading-7 text-muted-foreground", children: [_jsx("div", { children: OUTPUT_SCHEMA_PANEL_STRICT_JSON_LABEL[promptLanguage] ?? OUTPUT_SCHEMA_PANEL_STRICT_JSON_LABEL['zh-CN'] }), _jsx("div", { children: '{' }), fields.map((field, index) => (_jsx("div", { className: cn('my-1 rounded-md border bg-background px-3 py-2', field.isJudgment &&
138
+ 'border-l-2 border-l-[var(--status-pending-bd)] bg-[color-mix(in_srgb,var(--status-pending-bg)_60%,transparent)]'), "data-testid": "prompt-output-field-row", "data-judgment": field.isJudgment ? 'true' : 'false', children: _jsxs("div", { className: "grid gap-2 lg:grid-cols-[minmax(140px,0.6fr)_minmax(200px,1fr)_32px] lg:items-center", children: [_jsx(Input, { value: field.key, onChange: (event) => updateField(index, { key: event.target.value }), disabled: field.isJudgment || readOnly, "aria-label": t('prompts.detail.outputFieldKey'), placeholder: t('prompts.detail.outputFieldKeyPlaceholder'), className: "h-8 font-mono text-xs", "data-testid": field.isJudgment ? 'prompt-output-judgment-key' : 'prompt-output-field-key' }), _jsx(Input, { value: field.value, onChange: (event) => updateField(index, { value: event.target.value }), disabled: field.isJudgment || readOnly, "aria-label": t('prompts.detail.outputFieldValue'), placeholder: t('prompts.detail.outputFieldValuePlaceholder'), className: "h-8 font-mono text-xs", "data-testid": field.isJudgment ? 'prompt-output-judgment-value' : 'prompt-output-field-value' }), field.isJudgment ? (_jsx("span", { className: "inline-flex h-8 items-center justify-center text-muted-foreground", "aria-label": t('prompts.detail.outputFieldJudgmentReadonly'), title: t('prompts.detail.outputFieldJudgmentReadonly'), children: _jsx(Lock, { className: "size-3.5" }) })) : (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8 text-destructive hover:text-destructive", "aria-label": t('prompts.detail.deleteOutputField'), onClick: () => removeField(index), disabled: readOnly, children: _jsx(Trash2, { className: "size-3.5" }) }))] }) }, index))), !readOnly && (_jsx("div", { className: "py-1 pl-4", children: _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-7 border-dashed px-2 text-xs", onClick: addField, children: [_jsx(Plus, { className: "size-3.5" }), t('prompts.detail.addOutputField')] }) })), _jsx("div", { children: '}' })] })] }));
139
+ }
140
+ const IMAGE_VARIABLE_TYPES = new Set(['image', 'image_url', 'image_base64']);
141
+ function toPromptVariableModalityKind(type) {
142
+ if (type === 'number')
143
+ return 'number';
144
+ if (IMAGE_VARIABLE_TYPES.has(type))
145
+ return 'image';
146
+ return 'text';
147
+ }
148
+ function VariableRow({ variable, usageCount, onInsertVariable, }) {
149
+ const { t } = useI18n();
150
+ const isImage = IMAGE_VARIABLE_TYPES.has(variable.type);
151
+ const canInsert = !isImage && onInsertVariable !== undefined;
152
+ const isUsed = usageCount > 0;
153
+ const modalityKind = toPromptVariableModalityKind(variable.type);
154
+ const modalityLabel = t(PROMPT_MODALITY_LABEL_KEYS[modalityKind]);
155
+ const usageLabel = t('prompts.detail.variables.usageCount').replace('{count}', String(usageCount));
156
+ const addToPromptLabel = t('prompts.detail.variables.addToPrompt');
157
+ const rowClassName = cn('group flex w-full items-center gap-2.5 border-b border-l-2 px-4 py-2.5 text-left transition-colors', isUsed ? 'border-l-primary bg-primary/5 hover:bg-primary/10' : 'border-l-transparent hover:bg-accent', canInsert &&
158
+ 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2');
159
+ const rowContent = (_jsxs(_Fragment, { children: [_jsx(VariableToken, { variable: variable, dimmed: !isUsed }), _jsx(ModalityIcon, { kind: modalityKind, size: "sm", tooltip: modalityLabel, "aria-label": modalityLabel }), _jsxs("span", { className: "ml-auto inline-flex min-w-[116px] items-center justify-end gap-1.5", children: [_jsx("span", { className: "truncate text-[10.5px] text-muted-foreground", children: usageLabel }), canInsert && (_jsx(TooltipProvider, { delayDuration: 160, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "inline-flex size-5 shrink-0 items-center justify-center rounded-full border bg-background text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100 group-focus:opacity-100", children: _jsx(Plus, { className: "size-3.5" }) }) }), _jsx(TooltipContent, { side: "left", children: addToPromptLabel })] }) }))] })] }));
160
+ if (canInsert) {
161
+ return (_jsx("button", { type: "button", className: rowClassName, "aria-label": `${addToPromptLabel} · ${variable.name} · ${usageLabel}`, onClick: () => onInsertVariable?.(variable.name), "data-testid": `prompt-variable-insert-${variable.name}`, children: rowContent }));
162
+ }
163
+ return _jsx("div", { className: rowClassName, children: rowContent });
164
+ }
165
+ function VariablesPanel({ variables, usageCounts, onInsertVariable, hasBoundDataset, hasDatasets, onRequestDatasetBinding, onRequestDatasetUpload, }) {
166
+ const { t } = useI18n();
167
+ const usedCount = variables.filter((variable) => (usageCounts.get(variable.name) ?? 0) > 0).length;
168
+ const textVars = variables.filter((variable) => !IMAGE_VARIABLE_TYPES.has(variable.type));
169
+ const imageVars = variables.filter((variable) => IMAGE_VARIABLE_TYPES.has(variable.type));
170
+ const datasetActionLabel = hasDatasets
171
+ ? t('prompts.detail.variables.bindDataset')
172
+ : t('prompts.detail.variables.uploadDataset');
173
+ const imageExample = `{
174
+ "role": "user",
175
+ "content": [
176
+ { "type": "text", "text": "..." },
177
+ { "type": "image_url", "image_url": { "url": "{{image_url}}" } }
178
+ ]
179
+ }`;
180
+ return (_jsxs("aside", { className: "flex min-w-0 flex-col lg:border-l", children: [_jsxs("div", { className: "flex items-center gap-2 border-b px-4 py-2", children: [_jsx(Pencil, { className: "size-3.5 text-muted-foreground" }), _jsx("span", { className: "text-[12.5px] font-semibold", children: t('prompts.detail.variables') }), _jsxs("span", { className: "text-[11px] text-muted-foreground", children: ["\u00B7 ", usedCount, " / ", variables.length] }), !hasBoundDataset && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "ml-auto h-7 px-2 text-xs", onClick: hasDatasets ? onRequestDatasetBinding : onRequestDatasetUpload, "data-testid": "prompt-variables-bind-dataset", children: [hasDatasets ? _jsx(Database, { className: "size-3.5" }) : _jsx(Upload, { className: "size-3.5" }), datasetActionLabel] }))] }), _jsxs("div", { className: "max-h-[520px] flex-1 overflow-auto", "data-testid": "prompt-variables-panel", children: [textVars.length > 0 && (_jsxs("section", { "data-testid": "prompt-variables-text-group", children: [_jsxs("header", { className: "flex items-center gap-2 bg-muted/35 px-4 py-1.5 text-[10.5px] font-medium text-muted-foreground", children: [_jsx("span", { children: t('prompts.detail.variables.textGroup') }), _jsxs("span", { className: "font-mono", children: ["\u00B7 ", textVars.length] })] }), textVars.map((variable) => (_jsx(VariableRow, { variable: variable, usageCount: usageCounts.get(variable.name) ?? 0, onInsertVariable: onInsertVariable }, variable.name)))] })), imageVars.length > 0 && (_jsxs("section", { "data-testid": "prompt-variables-image-group", children: [_jsxs("header", { className: "flex items-center gap-2 bg-muted/35 px-4 py-1.5 text-[10.5px] font-medium text-muted-foreground", children: [_jsx("span", { children: t('prompts.detail.variables.imageGroup') }), _jsxs("span", { className: "font-mono", children: ["\u00B7 ", imageVars.length] }), _jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground", "aria-label": t('prompts.detail.variables.imageHint'), "data-testid": "prompt-variables-image-info", children: _jsx(Info, { className: "size-3.5" }) }) }), _jsxs(TooltipContent, { side: "left", className: "max-w-[320px] text-left", children: [_jsx("p", { className: "mb-2 text-[11.5px] leading-relaxed", children: t('prompts.detail.variables.imageHint') }), _jsx("pre", { className: "overflow-auto whitespace-pre rounded bg-muted/40 p-2 font-mono text-[10.5px] text-foreground", children: imageExample })] })] }) })] }), imageVars.map((variable) => (_jsx(VariableRow, { variable: variable, usageCount: usageCounts.get(variable.name) ?? 0 }, variable.name)))] }))] }), _jsx("div", { className: "border-t bg-muted/25 px-4 py-3 text-[11.5px] text-muted-foreground", children: t('prompts.detail.variablesFooter') })] }));
181
+ }
182
+ function EditorTab({ body, promptLanguage, variables, outputFields, onBodyChange, onOutputFieldsChange, hasBoundDataset, hasDatasets, onRequestDatasetBinding, onRequestDatasetUpload, dirty, saveError, isSaving, onCancelChanges, onSaveChanges, readOnly, }) {
183
+ const { t } = useI18n();
184
+ const editorRef = useRef(null);
185
+ const lineCount = Math.max(1, body.split('\n').length);
186
+ const tokenEstimate = Math.max(1, Math.round(body.length / 4));
187
+ const fullPreview = useMemo(() => composePromptPreview({
188
+ body,
189
+ outputFields,
190
+ promptLanguage,
191
+ }), [body, outputFields, promptLanguage]);
192
+ const previewParts = useMemo(() => renderPromptPreviewParts(fullPreview, variables), [fullPreview, variables]);
193
+ const variableUsageCounts = useMemo(() => countPromptVariableUsages(body, variables), [body, variables]);
194
+ const insertVariable = useCallback((name) => {
195
+ editorRef.current?.insertVariable(name);
196
+ }, []);
197
+ return (_jsxs("div", { "data-testid": "prompt-editor-tab", children: [_jsxs("section", { className: "mb-4 overflow-hidden rounded-lg border bg-card", "aria-label": t('prompts.detail.editorSurface'), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b bg-muted/35 px-4 py-2.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: t('prompts.detail.editorSurface') }), !readOnly && (_jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [saveError && _jsx("span", { className: "text-xs text-destructive", children: saveError }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8", disabled: !dirty || isSaving, onClick: onCancelChanges, children: [_jsx(X, { className: "size-3.5" }), t('prompts.detail.cancelChanges')] }), _jsxs(Button, { type: "button", size: "sm", className: "h-8", disabled: !dirty || isSaving, onClick: () => void onSaveChanges(), "data-testid": "prompt-version-save", children: [_jsx(Save, { className: "size-3.5" }), isSaving ? t('common.savePending') : t('prompts.detail.saveChanges')] })] }))] }), _jsxs("div", { className: "grid lg:grid-cols-[minmax(0,1fr)_340px]", children: [_jsxs("div", { className: "min-w-0", "data-testid": "prompt-version-body", children: [_jsxs("div", { className: "flex items-center gap-2 border-b px-4 py-2", children: [_jsx(Pencil, { className: "size-3.5 text-muted-foreground" }), _jsx("span", { className: "text-[12.5px] font-semibold", children: t('prompts.detail.bodyTemplate') }), _jsx("span", { className: "text-[11.5px] text-muted-foreground", children: t('prompts.detail.bodyTemplateHelp') }), _jsxs("span", { className: "ml-auto font-mono text-[11px] text-muted-foreground", children: ["L ", lineCount, " \u00B7 token \u2248 ", tokenEstimate] })] }), _jsx(PromptBodyEditor, { ref: editorRef, value: body, onChange: onBodyChange, variables: variables, placeholder: t('prompts.detail.bodyTemplatePlaceholder'), readOnly: readOnly }), _jsx(OutputSchemaPanel, { fields: outputFields, promptLanguage: promptLanguage, onFieldsChange: onOutputFieldsChange, readOnly: readOnly })] }), _jsx(VariablesPanel, { variables: variables, usageCounts: variableUsageCounts, onInsertVariable: readOnly ? undefined : insertVariable, hasBoundDataset: hasBoundDataset, hasDatasets: hasDatasets, onRequestDatasetBinding: onRequestDatasetBinding, onRequestDatasetUpload: onRequestDatasetUpload })] })] }), _jsxs("section", { className: "mb-4 overflow-hidden rounded-lg border bg-card", "aria-label": t('prompts.detail.fullPromptPreview'), "data-testid": "prompt-full-preview", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b bg-muted/35 px-4 py-2.5", children: [_jsx(Eye, { className: "size-3.5 text-muted-foreground" }), _jsx("span", { className: "text-[12.5px] font-semibold", children: t('prompts.detail.fullPromptPreview') }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: t('prompts.detail.fullPromptPreviewHelp') })] }), _jsx("pre", { className: "overflow-auto whitespace-pre-wrap break-words px-4 py-3 font-mono text-[11.5px] leading-6 text-foreground", children: previewParts.map((part, index) => {
198
+ if (part.kind === 'text')
199
+ return _jsx("span", { children: part.value }, index);
200
+ const tone = part.varType
201
+ ? VARIABLE_TONE_CLASSES[part.varType]
202
+ : 'border-muted-foreground/30 bg-muted/40 text-muted-foreground';
203
+ return (_jsx("span", { className: cn('inline rounded border px-1 font-mono text-[11px]', tone), "data-variable-name": part.name, children: `{{${part.name}}}` }, index));
204
+ }) })] })] }));
205
+ }
206
+ function ConfigTab({ datasets, variables, promptLanguage, onPromptLanguageChange, selectedDatasetId, onSelectDataset, saveError, readOnly, }) {
207
+ const { t } = useI18n();
208
+ return (_jsxs("div", { className: "space-y-4", "data-testid": "prompt-config-tab", children: [saveError && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs font-medium text-destructive", role: "alert", children: saveError })), _jsxs("section", { className: "overflow-hidden rounded-lg border bg-card", "aria-label": t('prompts.detail.subtab.config'), children: [_jsx("div", { className: "border-b bg-muted/35 px-4 py-2.5 text-xs font-medium text-muted-foreground", children: t('promptLanguage.label') }), _jsx("div", { className: "px-4 py-3", children: _jsx(PromptLanguageSelect, { value: promptLanguage, onChange: onPromptLanguageChange, disabled: readOnly, helpKey: "prompts.detail.promptLanguageHelp", className: "max-w-[380px]", triggerClassName: "h-8" }) })] }), _jsx(DatasetSelectionPanel, { variables: variables, datasets: datasets, selectedDatasetId: selectedDatasetId, onSelectDataset: onSelectDataset, readOnly: readOnly })] }));
209
+ }
210
+ function getVersionLabel(version) {
211
+ return `v${version.version}`;
212
+ }
213
+ function renderVersionPromptPreview(version) {
214
+ if (!version)
215
+ return '';
216
+ return composePromptPreview({
217
+ body: version.body,
218
+ outputFields: version.outputFields,
219
+ promptLanguage: version.promptLanguage,
220
+ });
221
+ }
222
+ function serializePromptVariables(variables) {
223
+ return JSON.stringify(variables.map((variable) => ({
224
+ name: variable.name,
225
+ type: variable.type,
226
+ required: variable.required,
227
+ datasetField: variable.datasetField,
228
+ description: variable.description,
229
+ })));
230
+ }
231
+ function serializeOutputFields(fields) {
232
+ return JSON.stringify(fields.map((field) => ({
233
+ key: field.key,
234
+ value: field.value,
235
+ isJudgment: field.isJudgment,
236
+ })));
237
+ }
238
+ function getDatasetJudgmentField(dataset) {
239
+ const expectedField = dataset?.fields.find((field) => field.role === 'expected');
240
+ const labels = dataset?.categoryProfile.slices.map((slice) => slice.label) ?? [];
241
+ return deriveJudgmentField({
242
+ expectedOutputFieldName: expectedField?.name ?? null,
243
+ categoryLabels: labels,
244
+ });
245
+ }
246
+ function getPromptVersionSyncKey({ promptId, versionId, body, promptLanguage, variables, outputFields, }) {
247
+ return `${promptId}:${versionId}:${body}:${promptLanguage}:${serializePromptVariables(variables)}:${serializeOutputFields(outputFields)}`;
248
+ }
249
+ function VersionLabelPill({ label, onRemove }) {
250
+ const { t } = useI18n();
251
+ const system = label.type === 'system';
252
+ const displayName = formatPromptVersionLabel(label, t);
253
+ return (_jsxs("span", { className: cn('inline-flex items-center gap-1 rounded border px-1.5 py-0.5 font-mono text-[10px] font-medium', system
254
+ ? 'border-[var(--status-running-bd)] bg-[var(--status-running-bg)] text-[var(--status-running-fg)]'
255
+ : 'border-border bg-muted/50 text-muted-foreground'), children: [displayName, onRemove && (_jsx("button", { type: "button", className: "inline-flex size-3.5 items-center justify-center rounded-sm hover:bg-background/70", "aria-label": t('prompts.labels.remove').replace('{label}', displayName), onClick: (event) => {
256
+ event.stopPropagation();
257
+ onRemove();
258
+ }, children: _jsx(X, { className: "size-2.5" }) }))] }));
259
+ }
260
+ function formatPromptVersionLabel(label, t) {
261
+ const key = PROMPT_VERSION_SYSTEM_LABEL_KEYS[label.name];
262
+ return key ? t(key) : label.name;
263
+ }
264
+ function VersionSidebar({ prompt, activeVersionId, onActivateVersion, onRequestBlankVersion, onRequestCopy, onRequestDelete, onUpdateLabel, isCopying, isDeleting, isUpdatingLabel, initialVersionId, }) {
265
+ const { t } = useI18n();
266
+ const { formatDateTime } = useDateTimeFormatter();
267
+ const [selectedVersions, setSelectedVersions] = useState([]);
268
+ const [initialVersionAppliedId, setInitialVersionAppliedId] = useState(null);
269
+ const [diffDialogOpen, setDiffDialogOpen] = useState(false);
270
+ const [versionSearch, setVersionSearch] = useState('');
271
+ if (initialVersionId && initialVersionAppliedId !== initialVersionId) {
272
+ const targetVersion = prompt.versions.find((v) => v.id === initialVersionId);
273
+ if (targetVersion) {
274
+ setInitialVersionAppliedId(initialVersionId);
275
+ setSelectedVersions([targetVersion.version]);
276
+ }
277
+ }
278
+ const filteredVersions = useMemo(() => {
279
+ const query = versionSearch.trim().toLowerCase();
280
+ if (!query)
281
+ return prompt.versions;
282
+ return prompt.versions.filter((version) => {
283
+ const haystack = [
284
+ getVersionLabel(version),
285
+ version.status,
286
+ version.author,
287
+ version.createdAt,
288
+ ...version.labels.map((label) => label.name),
289
+ ...version.labels.map((label) => formatPromptVersionLabel(label, t)),
290
+ ]
291
+ .join(' ')
292
+ .toLowerCase();
293
+ return haystack.includes(query);
294
+ });
295
+ }, [prompt.versions, t, versionSearch]);
296
+ const stopRowClick = useCallback((event) => {
297
+ event.stopPropagation();
298
+ }, []);
299
+ const compareVersions = useMemo(() => {
300
+ const selected = prompt.versions.filter((version) => selectedVersions.includes(version.version));
301
+ return selected.slice(0, 2);
302
+ }, [prompt.versions, selectedVersions]);
303
+ const fromVersion = compareVersions[1];
304
+ const toVersion = compareVersions[0];
305
+ const fromPromptPreview = useMemo(() => renderVersionPromptPreview(fromVersion), [fromVersion]);
306
+ const toPromptPreview = useMemo(() => renderVersionPromptPreview(toVersion), [toVersion]);
307
+ const toggleVersion = (version) => {
308
+ setSelectedVersions((current) => {
309
+ if (current.includes(version))
310
+ return current.filter((item) => item !== version);
311
+ return [version, ...current].slice(0, 2);
312
+ });
313
+ };
314
+ return (_jsxs("aside", { className: "flex min-h-[640px] flex-col border-b bg-background lg:min-h-[calc(100vh-240px)] lg:border-b-0 lg:border-r", "data-testid": "prompt-version-sidebar", children: [_jsxs("div", { className: "border-b p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[13px] font-semibold", children: t('prompts.detail.tab.versions') }), _jsx("div", { className: "mt-0.5 text-[11.5px] text-muted-foreground", children: t('prompts.detail.versionShowing')
315
+ .replace('{visible}', String(filteredVersions.length))
316
+ .replace('{total}', String(prompt.versions.length)) })] }), _jsxs(Button, { type: "button", size: "sm", className: "h-8 shrink-0", disabled: isCopying, onClick: onRequestBlankVersion, children: [_jsx(Plus, { className: "size-4" }), t('prompts.detail.createDraftVersion')] })] }), _jsxs("div", { className: "relative mt-3", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: versionSearch, onChange: (event) => setVersionSearch(event.target.value), placeholder: t('prompts.detail.versionSearchPlaceholder'), className: "h-9 pl-8 text-xs" })] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto p-3", children: filteredVersions.length > 0 ? (_jsx("div", { className: "space-y-2", children: filteredVersions.map((version) => {
317
+ const selected = selectedVersions.includes(version.version);
318
+ const active = version.id === activeVersionId;
319
+ const frozen = version.frozen;
320
+ const editLabel = frozen ? t('prompts.detail.viewFrozenVersion') : t('prompts.detail.editVersion');
321
+ return (_jsx("div", { className: cn('cursor-pointer rounded-lg border bg-card p-3 transition-colors hover:bg-muted/35', active && 'border-primary bg-primary/5'), onClick: () => onActivateVersion(version.id), "data-testid": `prompt-version-row-${version.version}`, children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("input", { type: "checkbox", className: "mt-1 size-4 accent-primary", checked: selected, "aria-label": `${t('prompts.detail.compare')} ${getVersionLabel(version)}`, onClick: stopRowClick, onChange: () => toggleVersion(version.version) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [_jsx("span", { className: "font-mono text-[13px] font-semibold", children: getVersionLabel(version) }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx(StatusBadge, { status: version.status, compact: true }), _jsx(PromptVariableModalityBadges, { variables: version.variables })] }), version.version === prompt.onlineVersion && (_jsx("span", { className: "rounded bg-[var(--status-running-bg)] px-1.5 py-0.5 font-mono text-[10px] text-[var(--status-running-fg)]", children: t('prompts.badge.online') }))] }), version.labels.length > 0 && (_jsx("div", { className: "mt-2 flex flex-wrap items-center gap-1", onClick: stopRowClick, children: version.labels.map((label) => (_jsx(VersionLabelPill, { label: label, onRemove: label.name === 'latest' || isUpdatingLabel
322
+ ? undefined
323
+ : () => onUpdateLabel(label.name, null) }, label.name))) })), _jsx("div", { className: "mt-2 truncate text-[11.5px] text-muted-foreground", children: formatDateTime(version.createdAt) })] }), _jsx("div", { onClick: stopRowClick, children: _jsx(TableActionRow, { maxInline: 0, moreLabel: t('prompts.action.moreActions'), actions: [
324
+ {
325
+ key: 'edit',
326
+ label: editLabel,
327
+ icon: frozen ? Eye : Pencil,
328
+ onClick: () => onActivateVersion(version.id),
329
+ },
330
+ {
331
+ key: 'copy',
332
+ label: t('prompts.detail.copyVersion'),
333
+ icon: Copy,
334
+ disabled: isCopying,
335
+ onClick: () => onRequestCopy(version.id),
336
+ },
337
+ {
338
+ key: 'delete',
339
+ label: t('prompts.detail.deleteDraftVersion'),
340
+ icon: Trash2,
341
+ destructive: true,
342
+ disabled: isDeleting,
343
+ onClick: () => onRequestDelete(version.id),
344
+ },
345
+ ] }) })] }) }, version.id));
346
+ }) })) : (_jsx("div", { className: "rounded-lg border border-dashed px-3 py-8 text-center text-[12px] text-muted-foreground", children: t('prompts.detail.versionNoMatch') })) }), _jsx("div", { className: "border-t p-3", children: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-[12.5px] text-muted-foreground", children: t('prompts.detail.selectedForDiff').replace('{count}', String(selectedVersions.length)) }), compareVersions.map((version) => (_jsx("span", { className: "rounded border bg-background px-1.5 py-0.5 font-mono text-xs", children: getVersionLabel(version) }, version.version))), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: () => setSelectedVersions([]), children: t('prompts.detail.clearDiff') }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8", disabled: !fromVersion || !toVersion, onClick: () => setDiffDialogOpen(true), children: [_jsx(GitCompareArrows, { className: "size-3.5" }), t('prompts.detail.openDiff')] })] }) }), _jsx(Dialog, { open: diffDialogOpen, onOpenChange: setDiffDialogOpen, children: _jsxs(DialogContent, { className: "max-h-[86vh] max-w-[1100px] overflow-hidden p-0", children: [_jsxs(DialogHeader, { className: "border-b px-6 pt-6", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(GitCompareArrows, { className: "size-4" }), t('prompts.detail.diffRenderedPrompt')] }), _jsx(DialogDescription, { children: fromVersion && toVersion
347
+ ? `${getVersionLabel(fromVersion)} -> ${getVersionLabel(toVersion)}`
348
+ : t('prompts.detail.diffNeedsTwo') })] }), _jsx("div", { className: "max-h-[68vh] space-y-3 overflow-auto px-6 pb-6", children: fromVersion && toVersion && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground", children: [_jsxs("span", { className: "inline-flex items-center gap-1 rounded border bg-destructive/10 px-1.5 py-0.5 text-destructive", children: ["- ", t('prompts.detail.diffRemoved')] }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-[var(--status-running-bd)] bg-[var(--status-running-bg)] px-1.5 py-0.5 text-[var(--status-running-fg)]", children: ["+ ", t('prompts.detail.diffAdded')] })] }), _jsx(PromptDiffSplitView, { fromLabel: getVersionLabel(fromVersion), toLabel: getVersionLabel(toVersion), fromText: fromPromptPreview, toText: toPromptPreview })] })) })] }) })] }));
349
+ }
350
+ function ActiveVersionLabels({ activeVersion, onUpdateLabel, isUpdatingLabel, }) {
351
+ const { t } = useI18n();
352
+ const [editing, setEditing] = useState(false);
353
+ const [labelInput, setLabelInput] = useState('');
354
+ const cleanLabelInput = labelInput.trim();
355
+ const labelValidation = cleanLabelInput ? promptVersionLabelNameSchema.safeParse(cleanLabelInput) : null;
356
+ const labelError = labelValidation && !labelValidation.success ? t('prompts.labels.invalidFormat') : null;
357
+ const submitLabel = async () => {
358
+ if (!activeVersion || !cleanLabelInput || labelError || isUpdatingLabel)
359
+ return;
360
+ await onUpdateLabel(cleanLabelInput, activeVersion.id);
361
+ setLabelInput('');
362
+ setEditing(false);
363
+ };
364
+ return (_jsxs("div", { className: "mt-2 flex min-w-0 flex-wrap items-center gap-1.5", children: [activeVersion && activeVersion.labels.length > 0 ? (activeVersion.labels.map((label) => (_jsx(VersionLabelPill, { label: label, onRemove: label.name === 'latest' || isUpdatingLabel ? undefined : () => onUpdateLabel(label.name, null) }, label.name)))) : (_jsx("span", { className: "text-xs text-muted-foreground", children: t('prompts.labels.empty') })), !editing && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-7 px-2 text-xs", disabled: !activeVersion || isUpdatingLabel, onClick: () => setEditing(true), children: [_jsx(Plus, { className: "size-3.5" }), t('prompts.detail.addLabel')] })), editing && (_jsxs("form", { className: "flex flex-wrap items-center gap-1.5", onSubmit: (event) => {
365
+ event.preventDefault();
366
+ void submitLabel();
367
+ }, children: [_jsxs("div", { className: "flex min-w-[190px] flex-col gap-1", children: [_jsx(Input, { value: labelInput, onChange: (event) => setLabelInput(event.target.value), placeholder: t('prompts.detail.labelPlaceholder'), className: cn('h-7 w-[190px] font-mono text-xs', labelError && 'border-destructive'), "aria-invalid": Boolean(labelError), "data-testid": "prompt-version-label-input" }), labelError && _jsx("span", { className: "max-w-[260px] text-[11px] text-destructive", children: labelError })] }), _jsxs(Button, { type: "submit", variant: "outline", size: "sm", className: "h-7 px-2 text-xs", disabled: !activeVersion || !cleanLabelInput || Boolean(labelError) || isUpdatingLabel, children: [_jsx(Tags, { className: "size-3.5" }), t('prompts.detail.applyLabel')] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", onClick: () => {
368
+ setLabelInput('');
369
+ setEditing(false);
370
+ }, "aria-label": t('common.cancel'), children: _jsx(X, { className: "size-3.5" }) })] }))] }));
371
+ }
372
+ function formatMetricNumber(value) {
373
+ return value.toLocaleString();
374
+ }
375
+ function formatMetricMs(value) {
376
+ if (value === null)
377
+ return '-';
378
+ return `${Math.round(value).toLocaleString()} ms`;
379
+ }
380
+ function formatMetricCost(value) {
381
+ return `$${value.toFixed(4)}`;
382
+ }
383
+ function formatMetricPercent(value) {
384
+ if (value === null)
385
+ return '-';
386
+ return `${(value * 100).toFixed(1)}%`;
387
+ }
388
+ function MetricSummaryCard({ label, value, sub }) {
389
+ return (_jsxs("div", { className: "rounded-lg border bg-card px-4 py-3", children: [_jsx("div", { className: "text-[11.5px] font-medium text-muted-foreground", children: label }), _jsx("div", { className: "mt-2 font-mono text-xl font-semibold leading-none", children: value }), sub && _jsx("div", { className: "mt-2 text-[11px] text-muted-foreground", children: sub })] }));
390
+ }
391
+ function PromptMetricsTab({ projectId, promptId }) {
392
+ const { t } = useI18n();
393
+ const { formatDateTime } = useDateTimeFormatter();
394
+ const metricsQuery = usePromptMetrics(projectId, promptId);
395
+ const metrics = metricsQuery.data;
396
+ const metricsLoading = useDelayedLoading(metricsQuery.isLoading);
397
+ if (metricsLoading) {
398
+ return (_jsxs("div", { className: "relative min-h-[420px]", "data-testid": "prompt-metrics-tab", "aria-busy": "true", children: [_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-4", children: Array.from({ length: 4 }).map((_, index) => (_jsx(Skeleton, { className: "h-24 rounded-lg" }, index))) }), _jsx(Skeleton, { className: "h-64 rounded-lg" })] }), _jsx(PlatformLoaderOverlay, {})] }));
399
+ }
400
+ if (!metrics) {
401
+ return (_jsx("div", { className: "rounded-lg border bg-card p-8 text-center text-sm text-muted-foreground", children: t('prompts.metrics.empty') }));
402
+ }
403
+ return (_jsxs("div", { className: "space-y-4", "data-testid": "prompt-metrics-tab", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-semibold", children: [_jsx(BarChart3, { className: "size-4 text-muted-foreground" }), t('prompts.detail.tab.metrics')] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-4", children: [_jsx(MetricSummaryCard, { label: t('prompts.metrics.totalRuns'), value: formatMetricNumber(metrics.totals.runCount), sub: `${t('prompts.metrics.success')} ${formatMetricNumber(metrics.totals.successCount)} · ${t('prompts.metrics.errors')} ${formatMetricNumber(metrics.totals.errorCount)}` }), _jsx(MetricSummaryCard, { label: t('prompts.metrics.totalTokens'), value: formatMetricNumber(metrics.totals.totalInputTokens + metrics.totals.totalOutputTokens), sub: `${t('prompts.metrics.inputTokens')} ${formatMetricNumber(metrics.totals.totalInputTokens)} · ${t('prompts.metrics.outputTokens')} ${formatMetricNumber(metrics.totals.totalOutputTokens)}` }), _jsx(MetricSummaryCard, { label: t('prompts.metrics.totalCost'), value: formatMetricCost(metrics.totals.totalCostEstimate) }), _jsx(MetricSummaryCard, { label: t('prompts.metrics.versionsWithRuns'), value: formatMetricNumber(metrics.versions.filter((version) => version.runCount > 0).length), sub: `${formatMetricNumber(metrics.versions.length)} ${t('prompts.detail.versionTotalSuffix')}` })] }), _jsx("section", { className: "overflow-hidden rounded-lg border bg-card", "aria-label": t('prompts.detail.tab.metrics'), children: _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[1040px] text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b bg-muted/60 text-left text-xs font-medium text-muted-foreground", children: [_jsx("th", { className: "px-3 py-3", children: t('prompts.detail.version') }), _jsx("th", { className: "w-44 px-3 py-3", children: t('prompts.detail.labels') }), _jsx("th", { className: "w-28 px-3 py-3", children: t('prompts.table.status') }), _jsx("th", { className: "w-24 px-3 py-3 text-right", children: t('prompts.metrics.runs') }), _jsx("th", { className: "w-24 px-3 py-3 text-right", children: t('prompts.metrics.accuracy') }), _jsx("th", { className: "w-32 px-3 py-3 text-right", children: t('prompts.metrics.medianLatency') }), _jsx("th", { className: "w-32 px-3 py-3 text-right", children: t('prompts.metrics.medianTokens') }), _jsx("th", { className: "w-28 px-3 py-3 text-right", children: t('prompts.metrics.cost') }), _jsx("th", { className: "w-36 px-3 py-3", children: t('prompts.metrics.lastRun') })] }) }), _jsx("tbody", { children: metrics.versions.map((version) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "px-3 py-3 font-mono font-semibold", children: ["v", version.versionNumber] }), _jsx("td", { className: "px-3 py-3", children: _jsx("div", { className: "flex flex-wrap items-center gap-1", children: version.labels.length > 0 ? (version.labels.map((label) => _jsx(VersionLabelPill, { label: label }, label.name))) : (_jsx("span", { className: "text-xs text-muted-foreground", children: "-" })) }) }), _jsx("td", { className: "px-3 py-3", children: _jsx(StatusBadge, { status: version.status, compact: true }) }), _jsx("td", { className: "px-3 py-3 text-right font-mono", children: formatMetricNumber(version.runCount) }), _jsx("td", { className: "px-3 py-3 text-right font-mono", children: formatMetricPercent(version.accuracy) }), _jsx("td", { className: "px-3 py-3 text-right font-mono", children: formatMetricMs(version.medianLatencyMs) }), _jsx("td", { className: "px-3 py-3 text-right font-mono", children: version.medianInputTokens === null && version.medianOutputTokens === null
404
+ ? '-'
405
+ : `${Math.round(version.medianInputTokens ?? 0)} / ${Math.round(version.medianOutputTokens ?? 0)}` }), _jsx("td", { className: "px-3 py-3 text-right font-mono", children: formatMetricCost(version.totalCostEstimate) }), _jsx("td", { className: "px-3 py-3 font-mono text-[11.5px] text-muted-foreground", children: formatDateTime(version.lastRunAt) })] }, version.promptVersionId))) })] }) }) })] }));
406
+ }
407
+ export function PromptDetailPage({ projectId, promptId }) {
408
+ const { t } = useI18n();
409
+ const { formatDateTime } = useDateTimeFormatter();
410
+ const router = useRouter();
411
+ const pathname = usePathname();
412
+ const searchParams = useSearchParams();
413
+ const initialVersionParam = searchParams?.get('version') ?? null;
414
+ const urlDetailTab = resolveDetailTab(searchParams?.get('tab') ?? null);
415
+ const urlMainTab = resolvePromptMainTab(searchParams?.get('panel') ?? null);
416
+ const promptQuery = usePrompt(projectId, promptId);
417
+ const datasetsQuery = useDatasets(projectId);
418
+ const updateDraftVersionMutation = useUpdatePromptDraftVersion(projectId);
419
+ const updatePromptMutation = useUpdatePrompt(projectId);
420
+ const createDraftVersionMutation = useCreatePromptDraftVersion(projectId);
421
+ const deleteDraftVersionMutation = useDeletePromptDraftVersion(projectId);
422
+ const updateVersionLabelMutation = useUpdatePromptVersionLabel(projectId);
423
+ const activeTab = urlDetailTab;
424
+ const activeMainTab = urlMainTab;
425
+ const requestedVersionId = activeTab === 'versions' ? initialVersionParam : null;
426
+ const [body, setBody] = useState('');
427
+ const [savedBody, setSavedBody] = useState('');
428
+ const [promptLanguage, setPromptLanguage] = useState('zh-CN');
429
+ const [savedPromptLanguage, setSavedPromptLanguage] = useState('zh-CN');
430
+ const [variables, setVariables] = useState([]);
431
+ const [savedVariables, setSavedVariables] = useState([]);
432
+ const [customOutputFields, setCustomOutputFields] = useState([]);
433
+ const [savedCustomOutputFields, setSavedCustomOutputFields] = useState([]);
434
+ const [selectedDatasetId, setSelectedDatasetId] = useState(null);
435
+ const [activeSyncKey, setActiveSyncKey] = useState('');
436
+ const [pendingNavigation, setPendingNavigation] = useState(null);
437
+ const [unsavedDialogOpen, setUnsavedDialogOpen] = useState(false);
438
+ const [saveError, setSaveError] = useState(null);
439
+ const [deleteTargetVersionId, setDeleteTargetVersionId] = useState(null);
440
+ const [actionMessage, setActionMessage] = useState(null);
441
+ const allowNavigationRef = useRef(false);
442
+ const popGuardArmedRef = useRef(false);
443
+ const prompt = useMemo(() => (promptQuery.data ? toProjectPrompt(promptQuery.data) : null), [promptQuery.data]);
444
+ const deleteImpactQuery = usePromptVersionDeleteImpact(projectId, prompt?.id ?? '', deleteTargetVersionId ?? '');
445
+ const datasets = useMemo(() => (datasetsQuery.data?.data ?? []).map((dataset) => toProjectDataset(dataset)), [datasetsQuery.data?.data]);
446
+ const activeDatasets = useMemo(() => datasets.filter((dataset) => dataset.status === 'active'), [datasets]);
447
+ const editableVersions = useMemo(() => (prompt?.versions ?? []).filter((version) => !version.frozen), [prompt?.versions]);
448
+ const activeVersion = useMemo(() => {
449
+ if (!prompt)
450
+ return null;
451
+ if (requestedVersionId) {
452
+ const match = prompt.versions.find((version) => version.id === requestedVersionId);
453
+ if (match)
454
+ return match;
455
+ }
456
+ if (editableVersions.length > 0)
457
+ return editableVersions[0] ?? null;
458
+ return prompt.versions[0] ?? null;
459
+ }, [editableVersions, prompt, requestedVersionId]);
460
+ const isReadOnly = activeVersion ? activeVersion.frozen : false;
461
+ const activeVersionSyncKey = prompt && activeVersion
462
+ ? getPromptVersionSyncKey({
463
+ promptId: prompt.id,
464
+ versionId: activeVersion.id,
465
+ body: activeVersion.body ?? '',
466
+ promptLanguage: activeVersion.promptLanguage,
467
+ variables: activeVersion.variables ?? [],
468
+ outputFields: activeVersion.outputFields ?? [],
469
+ })
470
+ : '';
471
+ if (prompt && activeVersion && activeSyncKey !== activeVersionSyncKey) {
472
+ const sourceBody = activeVersion.body ?? prompt.body;
473
+ const sourcePromptLanguage = activeVersion.promptLanguage;
474
+ const sourceVariables = activeVersion.variables ?? prompt.variables;
475
+ const sourceFields = (activeVersion.outputFields ?? prompt.outputFields).filter((field) => !field.isJudgment);
476
+ setActiveSyncKey(activeVersionSyncKey);
477
+ setBody(sourceBody);
478
+ setSavedBody(sourceBody);
479
+ setPromptLanguage(sourcePromptLanguage);
480
+ setSavedPromptLanguage(sourcePromptLanguage);
481
+ setVariables(sourceVariables);
482
+ setSavedVariables(sourceVariables);
483
+ setCustomOutputFields(sourceFields);
484
+ setSavedCustomOutputFields(sourceFields);
485
+ setSelectedDatasetId(prompt.defaultDatasetId);
486
+ setSaveError(null);
487
+ }
488
+ const selectedDataset = useMemo(() => (selectedDatasetId ? (datasets.find((dataset) => dataset.id === selectedDatasetId) ?? null) : null), [datasets, selectedDatasetId]);
489
+ const derivedJudgmentField = useMemo(() => getDatasetJudgmentField(selectedDataset), [selectedDataset]);
490
+ const outputFields = useMemo(() => upsertJudgmentField(customOutputFields, derivedJudgmentField), [customOutputFields, derivedJudgmentField]);
491
+ const handleOutputFieldsChange = useCallback((next) => {
492
+ setCustomOutputFields(next.filter((field) => !field.isJudgment));
493
+ }, []);
494
+ const variablesDirty = serializePromptVariables(variables) !== serializePromptVariables(savedVariables);
495
+ const outputFieldsDirty = serializeOutputFields(customOutputFields) !== serializeOutputFields(savedCustomOutputFields);
496
+ const dirty = !isReadOnly &&
497
+ (body !== savedBody || promptLanguage !== savedPromptLanguage || variablesDirty || outputFieldsDirty);
498
+ const requestUnsavedNavigation = useCallback((navigation) => {
499
+ setPendingNavigation(navigation);
500
+ setUnsavedDialogOpen(true);
501
+ }, []);
502
+ const replaceDetailUrl = useCallback(({ nextTab = activeTab, nextMainTab = activeMainTab, versionId, }) => {
503
+ const params = new URLSearchParams(searchParams.toString());
504
+ if (nextTab === 'versions')
505
+ params.delete('tab');
506
+ else
507
+ params.set('tab', nextTab);
508
+ if (nextTab === 'versions' && nextMainTab === 'config')
509
+ params.set('panel', nextMainTab);
510
+ else
511
+ params.delete('panel');
512
+ if (versionId !== undefined) {
513
+ if (versionId)
514
+ params.set('version', versionId);
515
+ else
516
+ params.delete('version');
517
+ }
518
+ if (nextTab !== 'versions')
519
+ params.delete('version');
520
+ const query = params.toString();
521
+ router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false });
522
+ }, [activeMainTab, activeTab, pathname, router, searchParams]);
523
+ const selectDetailTab = useCallback((tab) => {
524
+ replaceDetailUrl({ nextTab: tab });
525
+ }, [replaceDetailUrl]);
526
+ const selectPromptMainTab = useCallback((tab) => {
527
+ replaceDetailUrl({ nextTab: 'versions', nextMainTab: tab });
528
+ }, [replaceDetailUrl]);
529
+ useEffect(() => {
530
+ if (!dirty)
531
+ return undefined;
532
+ const onBeforeUnload = (event) => {
533
+ if (allowNavigationRef.current)
534
+ return;
535
+ event.preventDefault();
536
+ event.returnValue = '';
537
+ };
538
+ window.addEventListener('beforeunload', onBeforeUnload);
539
+ return () => window.removeEventListener('beforeunload', onBeforeUnload);
540
+ }, [dirty]);
541
+ useEffect(() => {
542
+ if (!dirty || popGuardArmedRef.current)
543
+ return;
544
+ const currentState = typeof window.history.state === 'object' && window.history.state ? window.history.state : {};
545
+ window.history.pushState({ ...currentState, [UNSAVED_HISTORY_GUARD_KEY]: true }, '', window.location.href);
546
+ popGuardArmedRef.current = true;
547
+ }, [dirty]);
548
+ useEffect(() => {
549
+ const onPopState = () => {
550
+ if (allowNavigationRef.current || !dirty) {
551
+ allowNavigationRef.current = false;
552
+ return;
553
+ }
554
+ const currentState = typeof window.history.state === 'object' && window.history.state ? window.history.state : {};
555
+ window.history.pushState({ ...currentState, [UNSAVED_HISTORY_GUARD_KEY]: true }, '', window.location.href);
556
+ popGuardArmedRef.current = true;
557
+ requestUnsavedNavigation({ kind: 'back' });
558
+ };
559
+ window.addEventListener('popstate', onPopState);
560
+ return () => window.removeEventListener('popstate', onPopState);
561
+ }, [dirty, requestUnsavedNavigation]);
562
+ useEffect(() => {
563
+ if (!dirty)
564
+ return undefined;
565
+ const onDocumentClick = (event) => {
566
+ if (event.defaultPrevented ||
567
+ event.button !== 0 ||
568
+ event.metaKey ||
569
+ event.ctrlKey ||
570
+ event.shiftKey ||
571
+ event.altKey) {
572
+ return;
573
+ }
574
+ const target = event.target instanceof Element ? event.target.closest('a[href]') : null;
575
+ if (!(target instanceof HTMLAnchorElement))
576
+ return;
577
+ if (target.hasAttribute('download'))
578
+ return;
579
+ if (target.target && target.target !== '_self')
580
+ return;
581
+ const rawHref = target.getAttribute('href');
582
+ if (!rawHref || rawHref.startsWith('#'))
583
+ return;
584
+ const url = new URL(target.href, window.location.href);
585
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
586
+ return;
587
+ const currentPath = `${window.location.pathname}${window.location.search}${window.location.hash}`;
588
+ const nextPath = `${url.pathname}${url.search}${url.hash}`;
589
+ if (url.origin === window.location.origin && nextPath === currentPath)
590
+ return;
591
+ event.preventDefault();
592
+ event.stopPropagation();
593
+ requestUnsavedNavigation({
594
+ kind: 'href',
595
+ href: url.origin === window.location.origin ? nextPath : url.toString(),
596
+ external: url.origin !== window.location.origin,
597
+ });
598
+ };
599
+ document.addEventListener('click', onDocumentClick, true);
600
+ return () => document.removeEventListener('click', onDocumentClick, true);
601
+ }, [dirty, requestUnsavedNavigation]);
602
+ useEffect(() => {
603
+ if (!actionMessage?.autoDismiss)
604
+ return undefined;
605
+ const timer = window.setTimeout(() => {
606
+ setActionMessage((current) => (current === actionMessage ? null : current));
607
+ }, LABEL_ACTION_MESSAGE_DISMISS_MS);
608
+ return () => window.clearTimeout(timer);
609
+ }, [actionMessage]);
610
+ const saveDraft = useCallback(async () => {
611
+ if (!prompt || !activeVersion || isReadOnly) {
612
+ setSaveError(t('prompts.detail.noEditableDraft'));
613
+ return false;
614
+ }
615
+ try {
616
+ const nextPrompt = await updateDraftVersionMutation.mutateAsync({
617
+ promptId: prompt.id,
618
+ versionId: activeVersion.id,
619
+ body: {
620
+ body,
621
+ promptLanguage,
622
+ variables: variables.map(({ selected: _selected, ...variable }) => variable),
623
+ outputSchema: {
624
+ fields: outputFields.map((field) => ({
625
+ key: field.key,
626
+ value: field.value,
627
+ isJudgment: field.isJudgment,
628
+ })),
629
+ },
630
+ judgmentRules: { rules: activeVersion.judgmentRules },
631
+ changeReason: activeVersion.changeReason || null,
632
+ },
633
+ });
634
+ const nextProjectPrompt = toProjectPrompt(nextPrompt);
635
+ const nextActive = nextProjectPrompt.versions.find((version) => version.id === activeVersion.id) ?? null;
636
+ const nextBody = nextActive?.body ?? nextProjectPrompt.body ?? body;
637
+ const nextPromptLanguage = nextActive?.promptLanguage ?? promptLanguage;
638
+ const nextVariables = nextActive?.variables ?? nextProjectPrompt.variables;
639
+ const nextOutputFields = nextActive?.outputFields ?? nextProjectPrompt.outputFields;
640
+ const nextCustomFields = nextOutputFields.filter((field) => !field.isJudgment);
641
+ setBody(nextBody);
642
+ setSavedBody(nextBody);
643
+ setPromptLanguage(nextPromptLanguage);
644
+ setSavedPromptLanguage(nextPromptLanguage);
645
+ setVariables(nextVariables);
646
+ setSavedVariables(nextVariables);
647
+ setCustomOutputFields(nextCustomFields);
648
+ setSavedCustomOutputFields(nextCustomFields);
649
+ setActiveSyncKey(getPromptVersionSyncKey({
650
+ promptId: nextProjectPrompt.id,
651
+ versionId: activeVersion.id,
652
+ body: nextBody,
653
+ promptLanguage: nextPromptLanguage,
654
+ variables: nextVariables,
655
+ outputFields: nextOutputFields,
656
+ }));
657
+ setSaveError(null);
658
+ return true;
659
+ }
660
+ catch (error) {
661
+ const message = error?.response?.data?.message ??
662
+ error?.message ??
663
+ t('prompts.detail.saveFailed');
664
+ setSaveError(String(message));
665
+ return false;
666
+ }
667
+ }, [activeVersion, body, isReadOnly, outputFields, prompt, promptLanguage, t, updateDraftVersionMutation, variables]);
668
+ const autoSaveConfigVersion = useCallback(async ({ nextPromptLanguage = savedPromptLanguage, nextVariables = savedVariables, nextDataset = selectedDataset, }) => {
669
+ if (!prompt || !activeVersion || isReadOnly)
670
+ return false;
671
+ const nextOutputFields = upsertJudgmentField(savedCustomOutputFields, getDatasetJudgmentField(nextDataset));
672
+ try {
673
+ const nextPrompt = await updateDraftVersionMutation.mutateAsync({
674
+ promptId: prompt.id,
675
+ versionId: activeVersion.id,
676
+ body: {
677
+ body: savedBody,
678
+ promptLanguage: nextPromptLanguage,
679
+ variables: nextVariables.map(({ selected: _selected, ...variable }) => variable),
680
+ outputSchema: {
681
+ fields: nextOutputFields.map((field) => ({
682
+ key: field.key,
683
+ value: field.value,
684
+ isJudgment: field.isJudgment,
685
+ })),
686
+ },
687
+ judgmentRules: { rules: activeVersion.judgmentRules },
688
+ changeReason: activeVersion.changeReason || null,
689
+ },
690
+ });
691
+ const nextProjectPrompt = toProjectPrompt(nextPrompt);
692
+ const nextActive = nextProjectPrompt.versions.find((version) => version.id === activeVersion.id) ?? null;
693
+ const persistedBody = nextActive?.body ?? savedBody;
694
+ const persistedPromptLanguage = nextActive?.promptLanguage ?? nextPromptLanguage;
695
+ const persistedVariables = nextActive?.variables ?? nextVariables;
696
+ const persistedOutputFields = nextActive?.outputFields ?? nextOutputFields;
697
+ const persistedCustomFields = persistedOutputFields.filter((field) => !field.isJudgment);
698
+ setSavedBody(persistedBody);
699
+ setPromptLanguage(persistedPromptLanguage);
700
+ setSavedPromptLanguage(persistedPromptLanguage);
701
+ setVariables(persistedVariables);
702
+ setSavedVariables(persistedVariables);
703
+ setSavedCustomOutputFields(persistedCustomFields);
704
+ setActiveSyncKey(getPromptVersionSyncKey({
705
+ promptId: nextProjectPrompt.id,
706
+ versionId: activeVersion.id,
707
+ body: persistedBody,
708
+ promptLanguage: persistedPromptLanguage,
709
+ variables: persistedVariables,
710
+ outputFields: persistedOutputFields,
711
+ }));
712
+ setSaveError(null);
713
+ return true;
714
+ }
715
+ catch (error) {
716
+ const message = error?.response?.data?.message ??
717
+ error?.message ??
718
+ t('prompts.detail.saveFailed');
719
+ setSaveError(String(message));
720
+ return false;
721
+ }
722
+ }, [
723
+ activeVersion,
724
+ isReadOnly,
725
+ prompt,
726
+ savedBody,
727
+ savedCustomOutputFields,
728
+ savedPromptLanguage,
729
+ savedVariables,
730
+ selectedDataset,
731
+ t,
732
+ updateDraftVersionMutation,
733
+ ]);
734
+ const handlePromptLanguageChange = useCallback((nextLanguage) => {
735
+ if (nextLanguage === promptLanguage)
736
+ return;
737
+ const previousLanguage = promptLanguage;
738
+ setPromptLanguage(nextLanguage);
739
+ void autoSaveConfigVersion({ nextPromptLanguage: nextLanguage }).then((saved) => {
740
+ if (!saved)
741
+ setPromptLanguage(previousLanguage);
742
+ });
743
+ }, [autoSaveConfigVersion, promptLanguage]);
744
+ const selectDataset = useCallback((datasetId) => {
745
+ const nextDataset = datasets.find((dataset) => dataset.id === datasetId);
746
+ if (!nextDataset)
747
+ return;
748
+ const previousDatasetId = selectedDatasetId;
749
+ const previousVariables = variables;
750
+ const nextVariables = toPromptVariablesFromDataset(nextDataset);
751
+ setSelectedDatasetId(datasetId);
752
+ setVariables(nextVariables);
753
+ void (async () => {
754
+ try {
755
+ if (prompt && prompt.defaultDatasetId !== datasetId) {
756
+ await updatePromptMutation.mutateAsync({ promptId: prompt.id, body: { defaultDatasetId: datasetId } });
757
+ }
758
+ const saved = await autoSaveConfigVersion({
759
+ nextPromptLanguage: savedPromptLanguage,
760
+ nextVariables,
761
+ nextDataset,
762
+ });
763
+ if (!saved) {
764
+ setSelectedDatasetId(previousDatasetId);
765
+ setVariables(previousVariables);
766
+ }
767
+ }
768
+ catch (error) {
769
+ setSelectedDatasetId(previousDatasetId);
770
+ setVariables(previousVariables);
771
+ const message = error?.response?.data?.message ??
772
+ error?.message ??
773
+ t('prompts.detail.datasetBindFailed');
774
+ setSaveError(String(message));
775
+ }
776
+ })();
777
+ }, [
778
+ autoSaveConfigVersion,
779
+ datasets,
780
+ prompt,
781
+ savedPromptLanguage,
782
+ selectedDatasetId,
783
+ t,
784
+ updatePromptMutation,
785
+ variables,
786
+ ]);
787
+ const navigateWithGuard = useCallback((href) => {
788
+ if (!dirty) {
789
+ router.push(href);
790
+ return;
791
+ }
792
+ requestUnsavedNavigation({ kind: 'href', href, external: false });
793
+ }, [dirty, requestUnsavedNavigation, router]);
794
+ const closeUnsavedDialog = () => {
795
+ setUnsavedDialogOpen(false);
796
+ setPendingNavigation(null);
797
+ };
798
+ const discardAndLeave = () => {
799
+ const navigation = pendingNavigation;
800
+ setBody(savedBody);
801
+ setPromptLanguage(savedPromptLanguage);
802
+ setVariables(savedVariables);
803
+ setCustomOutputFields(savedCustomOutputFields);
804
+ setSelectedDatasetId(null);
805
+ setUnsavedDialogOpen(false);
806
+ setPendingNavigation(null);
807
+ runPendingNavigation(navigation);
808
+ };
809
+ const saveAndLeave = async () => {
810
+ const navigation = pendingNavigation;
811
+ const saved = await saveDraft();
812
+ if (!saved)
813
+ return;
814
+ setUnsavedDialogOpen(false);
815
+ setPendingNavigation(null);
816
+ runPendingNavigation(navigation);
817
+ };
818
+ const cancelDraftChanges = useCallback(() => {
819
+ if (!prompt)
820
+ return;
821
+ setBody(savedBody);
822
+ setPromptLanguage(savedPromptLanguage);
823
+ setVariables(savedVariables);
824
+ setCustomOutputFields(savedCustomOutputFields);
825
+ setSelectedDatasetId(prompt.defaultDatasetId);
826
+ setSaveError(null);
827
+ }, [prompt, savedBody, savedCustomOutputFields, savedPromptLanguage, savedVariables]);
828
+ const activateVersion = useCallback((versionId) => {
829
+ replaceDetailUrl({ nextTab: 'versions', versionId });
830
+ }, [replaceDetailUrl]);
831
+ const handleActivateVersion = useCallback((versionId) => {
832
+ if (dirty && versionId !== activeVersion?.id) {
833
+ requestUnsavedNavigation({ kind: 'version', versionId });
834
+ return;
835
+ }
836
+ activateVersion(versionId);
837
+ }, [activateVersion, activeVersion?.id, dirty, requestUnsavedNavigation]);
838
+ const createBlankVersion = useCallback(async () => {
839
+ if (!prompt)
840
+ return;
841
+ const inheritedDataset = selectedDataset;
842
+ try {
843
+ const next = await createDraftVersionMutation.mutateAsync({
844
+ promptId: prompt.id,
845
+ body: {},
846
+ });
847
+ let nextProjectPrompt = toProjectPrompt(next);
848
+ let created = nextProjectPrompt.versions[0] ?? null;
849
+ if (created && inheritedDataset) {
850
+ const createdVersionId = created.id;
851
+ const inheritedVariables = toPromptVariablesFromDataset(inheritedDataset);
852
+ const inheritedOutputFields = upsertJudgmentField([], getDatasetJudgmentField(inheritedDataset));
853
+ const updated = await updateDraftVersionMutation.mutateAsync({
854
+ promptId: prompt.id,
855
+ versionId: createdVersionId,
856
+ body: {
857
+ body: '',
858
+ promptLanguage: created.promptLanguage,
859
+ variables: inheritedVariables.map(({ selected: _selected, ...variable }) => variable),
860
+ outputSchema: {
861
+ fields: inheritedOutputFields.map((field) => ({
862
+ key: field.key,
863
+ value: field.value,
864
+ isJudgment: field.isJudgment,
865
+ })),
866
+ },
867
+ judgmentRules: { rules: created.judgmentRules },
868
+ changeReason: created.changeReason || null,
869
+ },
870
+ });
871
+ nextProjectPrompt = toProjectPrompt(updated);
872
+ created = nextProjectPrompt.versions.find((version) => version.id === createdVersionId) ?? created;
873
+ }
874
+ if (created) {
875
+ replaceDetailUrl({ nextTab: 'versions', nextMainTab: 'prompt', versionId: created.id });
876
+ }
877
+ setActionMessage({ kind: 'success', text: t('prompts.versions.createBlankSuccess') });
878
+ }
879
+ catch (error) {
880
+ const message = error?.response?.data?.message ??
881
+ error?.message ??
882
+ t('prompts.versions.createBlankFailed');
883
+ setActionMessage({ kind: 'error', text: String(message) });
884
+ }
885
+ }, [createDraftVersionMutation, prompt, replaceDetailUrl, selectedDataset, t, updateDraftVersionMutation]);
886
+ const handleRequestBlankVersion = useCallback(() => {
887
+ if (dirty) {
888
+ requestUnsavedNavigation({ kind: 'blankVersion' });
889
+ return;
890
+ }
891
+ void createBlankVersion();
892
+ }, [createBlankVersion, dirty, requestUnsavedNavigation]);
893
+ const copyVersion = useCallback(async (sourceVersionId) => {
894
+ if (!prompt)
895
+ return;
896
+ try {
897
+ const next = await createDraftVersionMutation.mutateAsync({
898
+ promptId: prompt.id,
899
+ body: { sourceVersionId },
900
+ });
901
+ const nextProjectPrompt = toProjectPrompt(next);
902
+ const created = nextProjectPrompt.versions[0] ?? null;
903
+ if (created) {
904
+ replaceDetailUrl({ nextTab: 'versions', nextMainTab: 'prompt', versionId: created.id });
905
+ }
906
+ setActionMessage({ kind: 'success', text: t('prompts.versions.copySuccess') });
907
+ }
908
+ catch (error) {
909
+ const message = error?.response?.data?.message ??
910
+ error?.message ??
911
+ t('prompts.versions.copyFailed');
912
+ setActionMessage({ kind: 'error', text: String(message) });
913
+ }
914
+ }, [createDraftVersionMutation, prompt, replaceDetailUrl, t]);
915
+ const handleRequestCopy = useCallback((sourceVersionId) => {
916
+ if (dirty) {
917
+ requestUnsavedNavigation({ kind: 'copyVersion', sourceVersionId });
918
+ return;
919
+ }
920
+ void copyVersion(sourceVersionId);
921
+ }, [copyVersion, dirty, requestUnsavedNavigation]);
922
+ function runPendingNavigation(navigation) {
923
+ if (!navigation)
924
+ return;
925
+ switch (navigation.kind) {
926
+ case 'href':
927
+ allowNavigationRef.current = true;
928
+ if (navigation.external)
929
+ window.location.assign(navigation.href);
930
+ else
931
+ router.push(navigation.href);
932
+ return;
933
+ case 'back':
934
+ allowNavigationRef.current = true;
935
+ window.history.go(popGuardArmedRef.current ? -2 : -1);
936
+ return;
937
+ case 'version':
938
+ activateVersion(navigation.versionId);
939
+ return;
940
+ case 'blankVersion':
941
+ void createBlankVersion();
942
+ return;
943
+ case 'copyVersion':
944
+ void copyVersion(navigation.sourceVersionId);
945
+ return;
946
+ }
947
+ }
948
+ const handleRequestDelete = useCallback((versionId) => {
949
+ setDeleteTargetVersionId(versionId);
950
+ }, []);
951
+ const handleUpdateLabel = useCallback(async (label, versionId) => {
952
+ if (!prompt)
953
+ return;
954
+ try {
955
+ await updateVersionLabelMutation.mutateAsync({
956
+ promptId: prompt.id,
957
+ body: { label, versionId },
958
+ });
959
+ setActionMessage({
960
+ kind: 'success',
961
+ text: versionId ? t('prompts.labels.updateSuccess') : t('prompts.labels.deleteSuccess'),
962
+ autoDismiss: true,
963
+ });
964
+ }
965
+ catch (error) {
966
+ const message = error?.response?.data?.message ??
967
+ error?.message ??
968
+ t('prompts.labels.updateFailed');
969
+ setActionMessage({ kind: 'error', text: String(message) });
970
+ }
971
+ }, [prompt, t, updateVersionLabelMutation]);
972
+ const confirmDelete = useCallback(async () => {
973
+ if (!prompt || !deleteTargetVersionId)
974
+ return;
975
+ try {
976
+ await deleteDraftVersionMutation.mutateAsync({
977
+ promptId: prompt.id,
978
+ versionId: deleteTargetVersionId,
979
+ });
980
+ if (requestedVersionId === deleteTargetVersionId) {
981
+ replaceDetailUrl({ nextTab: 'versions', versionId: null });
982
+ }
983
+ setDeleteTargetVersionId(null);
984
+ setActionMessage({ kind: 'success', text: t('prompts.versions.deleteSuccess') });
985
+ }
986
+ catch (error) {
987
+ const message = error?.response?.data?.message ??
988
+ error?.message ??
989
+ t('prompts.versions.deleteFailed');
990
+ setActionMessage({ kind: 'error', text: String(message) });
991
+ setDeleteTargetVersionId(null);
992
+ }
993
+ }, [deleteDraftVersionMutation, deleteTargetVersionId, prompt, replaceDetailUrl, requestedVersionId, t]);
994
+ const deleteTargetVersion = useMemo(() => prompt && deleteTargetVersionId ? (prompt.versions.find((v) => v.id === deleteTargetVersionId) ?? null) : null, [deleteTargetVersionId, prompt]);
995
+ const promptLoading = useDelayedLoading(promptQuery.isLoading);
996
+ if (promptLoading) {
997
+ return (_jsx(Main, { className: "gap-0 bg-muted/35 p-0", children: _jsx("div", { className: "mx-auto w-full max-w-[1680px] px-4 pb-10 pt-6 sm:px-6 lg:px-8", "data-testid": "prompt-detail-page", children: _jsx(DetailPageSkeleton, {}) }) }));
998
+ }
999
+ if (!prompt) {
1000
+ return (_jsx(Main, { className: "bg-muted/35", children: _jsxs("div", { className: "mx-auto w-full max-w-3xl rounded-lg border bg-card p-8 text-center", children: [_jsx("h1", { className: "text-xl font-semibold", children: t('prompts.detail.notFoundTitle') }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: t('prompts.detail.notFoundDescription') }), _jsx(Button, { asChild: true, className: "mt-4", children: _jsx(Link, { href: `/prompts`, children: t('prompts.detail.backToList') }) })] }) }));
1001
+ }
1002
+ const promptMetaParts = [
1003
+ t('prompts.detail.derivedFrom').replace('{version}', `v${prompt.versions[0]?.parentVersion ?? prompt.latestVersion - 1}`),
1004
+ prompt.owner,
1005
+ formatDateTime(prompt.updatedAt),
1006
+ ].filter(Boolean);
1007
+ return (_jsxs(Main, { className: "gap-0 bg-muted/35 p-0", children: [_jsxs("div", { className: "mx-auto w-full max-w-[1680px] px-4 pb-10 pt-6 sm:px-6 lg:px-8", "data-testid": "prompt-detail-page", children: [_jsxs("button", { type: "button", onClick: () => navigateWithGuard(`/prompts`), className: "mb-3 inline-flex items-center gap-1 text-[12.5px] text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "size-3.5" }), t('prompts.detail.backToList')] }), _jsx("div", { className: "mb-2", children: _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "mb-1 flex flex-wrap items-center gap-2.5", children: [_jsx("h1", { className: "text-[26px] font-semibold leading-tight", children: prompt.name }), _jsxs("span", { className: "inline-flex items-center rounded-full border bg-background px-2 py-0.5 font-mono text-[11.5px] font-medium text-muted-foreground", children: ["v", prompt.latestVersion] })] }), _jsx("div", { className: "text-[12.5px] text-muted-foreground", children: promptMetaParts.join(' · ') })] }) }), _jsx("div", { className: "mb-5 mt-3 flex items-end gap-1 border-b", children: Object.keys(TAB_LABEL_KEYS).map((tab) => (_jsx("button", { type: "button", onClick: () => selectDetailTab(tab), className: cn('border-b-2 px-4 py-2 text-[13.5px] font-medium transition-colors', activeTab === tab
1008
+ ? 'border-primary text-foreground'
1009
+ : 'border-transparent text-muted-foreground hover:text-foreground'), children: t(TAB_LABEL_KEYS[tab]) }, tab))) }), activeTab === 'versions' && (_jsxs("div", { className: "grid overflow-hidden border bg-background lg:grid-cols-[340px_minmax(0,1fr)]", "data-testid": "prompt-version-workspace", children: [_jsx(VersionSidebar, { prompt: prompt, activeVersionId: activeVersion?.id ?? null, onActivateVersion: handleActivateVersion, onRequestBlankVersion: handleRequestBlankVersion, onRequestCopy: handleRequestCopy, onRequestDelete: handleRequestDelete, onUpdateLabel: handleUpdateLabel, isCopying: createDraftVersionMutation.isPending, isDeleting: deleteDraftVersionMutation.isPending, isUpdatingLabel: updateVersionLabelMutation.isPending, initialVersionId: initialVersionParam }), _jsxs("section", { className: "min-w-0 bg-background", "data-testid": "prompt-version-main", children: [_jsx("div", { className: "border-b px-5 py-4", children: _jsxs("div", { className: "flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: activeVersion && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-mono text-sm font-semibold", children: ["v", activeVersion.version] }), _jsx("h2", { className: "truncate text-xl font-semibold leading-tight", children: prompt.name }), activeVersion.frozen ? (_jsx("span", { "data-testid": "prompt-version-frozen-badge", children: _jsx(StatusBadge, { status: activeVersion.status, compact: true }) })) : (_jsx(StatusBadge, { status: activeVersion.status, compact: true }))] })) }), _jsx(ActiveVersionLabels, { activeVersion: activeVersion, onUpdateLabel: handleUpdateLabel, isUpdatingLabel: updateVersionLabelMutation.isPending })] }), activeVersion && (_jsxs("div", { className: "flex max-w-full flex-wrap items-center justify-start gap-2 xl:justify-end", children: [actionMessage && (_jsx("span", { className: cn('text-xs', actionMessage.kind === 'success' ? 'text-[var(--status-running-fg)]' : 'text-destructive'), children: actionMessage.text })), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8", disabled: createDraftVersionMutation.isPending, onClick: () => handleRequestCopy(activeVersion.id), children: [_jsx(Copy, { className: "size-3.5" }), t('prompts.detail.copyAsNew')] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8", onClick: () => navigateWithGuard(`/experiments/new?promptId=${prompt.id}&promptVersionId=${activeVersion.id}`), children: [_jsx(FlaskConical, { className: "size-3.5" }), t('prompts.action.startExperiment')] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-8", onClick: () => navigateWithGuard(`/optimizations/new?promptId=${prompt.id}&promptVersionId=${activeVersion.id}`), children: [_jsx(Sparkles, { className: "size-3.5" }), t('prompts.action.startOptimization')] })] }))] }) }), _jsx("div", { className: "flex items-end gap-1 border-b px-5", children: Object.keys(PROMPT_MAIN_TAB_LABEL_KEYS).map((tab) => (_jsx("button", { type: "button", onClick: () => selectPromptMainTab(tab), className: cn('border-b-2 px-3 py-3 text-[13.5px] font-medium transition-colors', activeMainTab === tab
1010
+ ? 'border-primary text-foreground'
1011
+ : 'border-transparent text-muted-foreground hover:text-foreground'), children: t(PROMPT_MAIN_TAB_LABEL_KEYS[tab]) }, tab))) }), _jsxs("div", { className: "p-5", children: [activeMainTab === 'prompt' && (_jsx(EditorTab, { body: body, promptLanguage: promptLanguage, variables: variables, outputFields: outputFields, onBodyChange: setBody, onOutputFieldsChange: handleOutputFieldsChange, hasBoundDataset: Boolean(selectedDatasetId), hasDatasets: datasetsQuery.isLoading || activeDatasets.length > 0, onRequestDatasetBinding: () => selectPromptMainTab('config'), onRequestDatasetUpload: () => navigateWithGuard('/datasets/new'), dirty: dirty, saveError: saveError, isSaving: updateDraftVersionMutation.isPending, onCancelChanges: cancelDraftChanges, onSaveChanges: saveDraft, readOnly: isReadOnly })), activeMainTab === 'config' && (_jsx(ConfigTab, { datasets: datasets, variables: variables, promptLanguage: promptLanguage, onPromptLanguageChange: handlePromptLanguageChange, selectedDatasetId: selectedDatasetId, onSelectDataset: selectDataset, saveError: saveError, readOnly: isReadOnly || updateDraftVersionMutation.isPending || updatePromptMutation.isPending }))] })] })] })), activeTab === 'metrics' && _jsx(PromptMetricsTab, { projectId: projectId, promptId: prompt.id })] }), _jsx(Dialog, { open: unsavedDialogOpen, onOpenChange: (open) => !open && closeUnsavedDialog(), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t('prompts.detail.unsavedTitle') }), _jsx(DialogDescription, { children: t('prompts.detail.unsavedDescription') })] }), saveError && _jsx("p", { className: "text-sm text-destructive", children: saveError }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: closeUnsavedDialog, children: t('common.cancel') }), _jsx(Button, { type: "button", variant: "ghost", onClick: discardAndLeave, children: t('prompts.detail.leaveWithoutSaving') }), _jsxs(Button, { type: "button", onClick: () => void saveAndLeave(), disabled: updateDraftVersionMutation.isPending, children: [_jsx(Save, { className: "size-4" }), t('prompts.detail.saveAndLeave')] })] })] }) }), _jsx(Dialog, { open: deleteTargetVersionId !== null, onOpenChange: (open) => !open && setDeleteTargetVersionId(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t('prompts.versions.deleteConfirmTitle') }), _jsx(DialogDescription, { children: t('prompts.versions.deleteConfirmBody').replace('{version}', deleteTargetVersion ? `v${deleteTargetVersion.version}` : '') })] }), _jsx(DeleteImpactPanel, { impact: deleteImpactQuery.data, loading: deleteImpactQuery.isLoading }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => setDeleteTargetVersionId(null), children: t('common.cancel') }), _jsxs(Button, { type: "button", variant: "destructive", onClick: () => void confirmDelete(), disabled: deleteDraftVersionMutation.isPending || deleteImpactQuery.isLoading, children: [_jsx(Trash2, { className: "size-4" }), t('common.confirmDelete')] })] })] }) })] }));
1012
+ }
1013
+ //# sourceMappingURL=prompt-detail-page.js.map