@storyshelf/app 0.6.2 → 0.8.0

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 (429) hide show
  1. package/README.md +42 -4
  2. package/dist/app-types.d.mts +2 -6
  3. package/dist/asset-manifest.mjs +14 -7
  4. package/dist/asset-manifest.mjs.map +1 -1
  5. package/dist/assets/{htmx.min.js → htmx.mjs} +9 -1
  6. package/dist/assets/htmx.mjs.map +1 -0
  7. package/dist/assets/mark.mjs +9 -0
  8. package/dist/assets/mark.mjs.map +1 -0
  9. package/dist/boot-tasks.mjs +79 -0
  10. package/dist/boot-tasks.mjs.map +1 -0
  11. package/dist/build-state-flags.mjs +17 -0
  12. package/dist/build-state-flags.mjs.map +1 -0
  13. package/dist/build-state-setup.mjs +74 -0
  14. package/dist/build-state-setup.mjs.map +1 -0
  15. package/dist/capture-alerts.mjs +45 -0
  16. package/dist/capture-alerts.mjs.map +1 -0
  17. package/dist/capture-process.mjs +58 -0
  18. package/dist/capture-process.mjs.map +1 -0
  19. package/dist/capture-setup.mjs +96 -119
  20. package/dist/capture-setup.mjs.map +1 -1
  21. package/dist/connections.mjs +33 -0
  22. package/dist/connections.mjs.map +1 -0
  23. package/dist/consumer-setup.mjs +57 -0
  24. package/dist/consumer-setup.mjs.map +1 -0
  25. package/dist/credential-keys.mjs +1 -2
  26. package/dist/credentials.mjs +1 -2
  27. package/dist/git-status.mjs +45 -0
  28. package/dist/git-status.mjs.map +1 -0
  29. package/dist/hidden-data.mjs +23 -0
  30. package/dist/hidden-data.mjs.map +1 -0
  31. package/dist/index.d.mts +3 -5
  32. package/dist/index.mjs +204 -127
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/insights/budget.mjs +48 -0
  35. package/dist/insights/budget.mjs.map +1 -0
  36. package/dist/insights/deps.mjs +20 -0
  37. package/dist/insights/deps.mjs.map +1 -0
  38. package/dist/insights/errors.mjs +21 -0
  39. package/dist/insights/errors.mjs.map +1 -0
  40. package/dist/insights/health-panel.mjs +39 -0
  41. package/dist/insights/health-panel.mjs.map +1 -0
  42. package/dist/insights/job.mjs +102 -0
  43. package/dist/insights/job.mjs.map +1 -0
  44. package/dist/insights/load-health.mjs +58 -0
  45. package/dist/insights/load-health.mjs.map +1 -0
  46. package/dist/insights/load-triage.mjs +97 -0
  47. package/dist/insights/load-triage.mjs.map +1 -0
  48. package/dist/insights/panel.mjs +27 -0
  49. package/dist/insights/panel.mjs.map +1 -0
  50. package/dist/insights/request.mjs +112 -0
  51. package/dist/insights/request.mjs.map +1 -0
  52. package/dist/insights/stale.mjs +20 -0
  53. package/dist/insights/stale.mjs.map +1 -0
  54. package/dist/insights/usage-report.mjs +59 -0
  55. package/dist/insights/usage-report.mjs.map +1 -0
  56. package/dist/insights/view.mjs +47 -0
  57. package/dist/insights/view.mjs.map +1 -0
  58. package/dist/job-canceller.mjs +13 -0
  59. package/dist/job-canceller.mjs.map +1 -0
  60. package/dist/job-queue.mjs +50 -0
  61. package/dist/job-queue.mjs.map +1 -0
  62. package/dist/jobs-setup.mjs +131 -0
  63. package/dist/jobs-setup.mjs.map +1 -0
  64. package/dist/lifecycle.mjs +93 -22
  65. package/dist/lifecycle.mjs.map +1 -1
  66. package/dist/middleware/auth-gate.mjs +5 -4
  67. package/dist/middleware/auth-gate.mjs.map +1 -1
  68. package/dist/middleware/csrf.mjs +1 -2
  69. package/dist/middleware/rate-limit.mjs +42 -35
  70. package/dist/middleware/rate-limit.mjs.map +1 -1
  71. package/dist/middleware/request-log.mjs +4 -4
  72. package/dist/middleware/request-log.mjs.map +1 -1
  73. package/dist/middleware/require-review.mjs +50 -0
  74. package/dist/middleware/require-review.mjs.map +1 -0
  75. package/dist/middleware/require-testing.mjs +40 -0
  76. package/dist/middleware/require-testing.mjs.map +1 -0
  77. package/dist/middleware/server-timing.mjs +1 -2
  78. package/dist/middleware/setup-gate.mjs +1 -2
  79. package/dist/middleware/setup-gate.mjs.map +1 -1
  80. package/dist/middleware/store-scope.mjs +13 -4
  81. package/dist/middleware/store-scope.mjs.map +1 -1
  82. package/dist/notifications-pause.mjs +32 -0
  83. package/dist/notifications-pause.mjs.map +1 -0
  84. package/dist/notify.d.mts +3 -4
  85. package/dist/notify.mjs +79 -41
  86. package/dist/notify.mjs.map +1 -1
  87. package/dist/pages/a11y-violations.mjs +115 -0
  88. package/dist/pages/a11y-violations.mjs.map +1 -0
  89. package/dist/pages/admin-ai.mjs +77 -0
  90. package/dist/pages/admin-ai.mjs.map +1 -0
  91. package/dist/pages/admin-system-origin.mjs +132 -0
  92. package/dist/pages/admin-system-origin.mjs.map +1 -0
  93. package/dist/pages/admin-system-targets.mjs +52 -0
  94. package/dist/pages/admin-system-targets.mjs.map +1 -0
  95. package/dist/pages/admin-system.mjs +64 -15
  96. package/dist/pages/admin-system.mjs.map +1 -1
  97. package/dist/pages/build-detail.mjs +105 -78
  98. package/dist/pages/build-detail.mjs.map +1 -1
  99. package/dist/pages/build-diff-actions.mjs +88 -35
  100. package/dist/pages/build-diff-actions.mjs.map +1 -1
  101. package/dist/pages/build-diff-comments.mjs +17 -16
  102. package/dist/pages/build-diff-comments.mjs.map +1 -1
  103. package/dist/pages/build-diff-default.mjs +53 -0
  104. package/dist/pages/build-diff-default.mjs.map +1 -0
  105. package/dist/pages/build-diff-environment.mjs +81 -0
  106. package/dist/pages/build-diff-environment.mjs.map +1 -0
  107. package/dist/pages/build-diff-header.mjs +23 -6
  108. package/dist/pages/build-diff-header.mjs.map +1 -1
  109. package/dist/pages/build-diff-insight.mjs +112 -0
  110. package/dist/pages/build-diff-insight.mjs.map +1 -0
  111. package/dist/pages/build-diff-model.mjs +74 -12
  112. package/dist/pages/build-diff-model.mjs.map +1 -1
  113. package/dist/pages/build-diff-nav.mjs +29 -14
  114. package/dist/pages/build-diff-nav.mjs.map +1 -1
  115. package/dist/pages/build-diff-reference.mjs +84 -0
  116. package/dist/pages/build-diff-reference.mjs.map +1 -0
  117. package/dist/pages/build-diff-shortcuts.mjs +6 -3
  118. package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
  119. package/dist/pages/build-diff-stale.mjs +1 -2
  120. package/dist/pages/build-diff-usage.mjs +62 -0
  121. package/dist/pages/build-diff-usage.mjs.map +1 -0
  122. package/dist/pages/build-diff-viewer.mjs +97 -14
  123. package/dist/pages/build-diff-viewer.mjs.map +1 -1
  124. package/dist/pages/build-diff.mjs +37 -22
  125. package/dist/pages/build-diff.mjs.map +1 -1
  126. package/dist/pages/build-tests.mjs +73 -0
  127. package/dist/pages/build-tests.mjs.map +1 -0
  128. package/dist/pages/component-detail.mjs +248 -0
  129. package/dist/pages/component-detail.mjs.map +1 -0
  130. package/dist/pages/component-stories.mjs +167 -0
  131. package/dist/pages/component-stories.mjs.map +1 -0
  132. package/dist/pages/compute-jobs.mjs +14 -28
  133. package/dist/pages/compute-jobs.mjs.map +1 -1
  134. package/dist/pages/design-links.mjs +181 -0
  135. package/dist/pages/design-links.mjs.map +1 -0
  136. package/dist/pages/environment-change.mjs +52 -0
  137. package/dist/pages/environment-change.mjs.map +1 -0
  138. package/dist/pages/health-panel.mjs +125 -0
  139. package/dist/pages/health-panel.mjs.map +1 -0
  140. package/dist/pages/invite.mjs +1 -2
  141. package/dist/pages/label-detail.mjs +1 -2
  142. package/dist/pages/library-grid.mjs +104 -0
  143. package/dist/pages/library-grid.mjs.map +1 -0
  144. package/dist/pages/library-model.mjs +46 -0
  145. package/dist/pages/library-model.mjs.map +1 -0
  146. package/dist/pages/library-snapshots.mjs +129 -0
  147. package/dist/pages/library-snapshots.mjs.map +1 -0
  148. package/dist/pages/library.mjs +113 -220
  149. package/dist/pages/library.mjs.map +1 -1
  150. package/dist/pages/login.mjs +1 -2
  151. package/dist/pages/notifications-paused.mjs +21 -0
  152. package/dist/pages/notifications-paused.mjs.map +1 -0
  153. package/dist/pages/passkey-ceremony.mjs +1 -2
  154. package/dist/pages/profile.mjs +54 -50
  155. package/dist/pages/profile.mjs.map +1 -1
  156. package/dist/pages/project-builds-table.mjs +3 -4
  157. package/dist/pages/project-builds-table.mjs.map +1 -1
  158. package/dist/pages/project-builds.mjs +52 -23
  159. package/dist/pages/project-builds.mjs.map +1 -1
  160. package/dist/pages/project-create.mjs +1 -2
  161. package/dist/pages/project-settings.mjs +52 -14
  162. package/dist/pages/project-settings.mjs.map +1 -1
  163. package/dist/pages/projects-card.mjs +15 -3
  164. package/dist/pages/projects-card.mjs.map +1 -1
  165. package/dist/pages/projects.mjs +7 -4
  166. package/dist/pages/projects.mjs.map +1 -1
  167. package/dist/pages/review-changeset.mjs +115 -0
  168. package/dist/pages/review-changeset.mjs.map +1 -0
  169. package/dist/pages/review-comments.mjs +102 -0
  170. package/dist/pages/review-comments.mjs.map +1 -0
  171. package/dist/pages/review-verdict.mjs +101 -0
  172. package/dist/pages/review-verdict.mjs.map +1 -0
  173. package/dist/pages/root.mjs +1 -2
  174. package/dist/pages/settings-ai.mjs +68 -0
  175. package/dist/pages/settings-ai.mjs.map +1 -0
  176. package/dist/pages/settings-capture.mjs +123 -0
  177. package/dist/pages/settings-capture.mjs.map +1 -0
  178. package/dist/pages/settings-design-credential.mjs +98 -0
  179. package/dist/pages/settings-design-credential.mjs.map +1 -0
  180. package/dist/pages/settings-design-links.mjs +178 -0
  181. package/dist/pages/settings-design-links.mjs.map +1 -0
  182. package/dist/pages/settings-design.mjs +47 -0
  183. package/dist/pages/settings-design.mjs.map +1 -0
  184. package/dist/pages/settings-general.mjs +47 -23
  185. package/dist/pages/settings-general.mjs.map +1 -1
  186. package/dist/pages/settings-labels.mjs +44 -4
  187. package/dist/pages/settings-labels.mjs.map +1 -1
  188. package/dist/pages/settings-links.mjs +111 -0
  189. package/dist/pages/settings-links.mjs.map +1 -0
  190. package/dist/pages/settings-members.mjs +1 -2
  191. package/dist/pages/settings-notifications-git.mjs +127 -0
  192. package/dist/pages/settings-notifications-git.mjs.map +1 -0
  193. package/dist/pages/settings-notifications.mjs +80 -44
  194. package/dist/pages/settings-notifications.mjs.map +1 -1
  195. package/dist/pages/settings-tests.mjs +34 -4
  196. package/dist/pages/settings-tests.mjs.map +1 -1
  197. package/dist/pages/settings-tokens.mjs +1 -2
  198. package/dist/pages/settings-webhooks.mjs +1 -2
  199. package/dist/pages/storybook.mjs +1 -2
  200. package/dist/pages/target-choices.mjs +38 -0
  201. package/dist/pages/target-choices.mjs.map +1 -0
  202. package/dist/publish-setup.mjs +88 -0
  203. package/dist/publish-setup.mjs.map +1 -0
  204. package/dist/retention-timer.mjs +12 -12
  205. package/dist/retention-timer.mjs.map +1 -1
  206. package/dist/review-events.mjs +47 -0
  207. package/dist/review-events.mjs.map +1 -0
  208. package/dist/routers/admin-pages.mjs +46 -5
  209. package/dist/routers/admin-pages.mjs.map +1 -1
  210. package/dist/routers/admin-public-origin.mjs +92 -0
  211. package/dist/routers/admin-public-origin.mjs.map +1 -0
  212. package/dist/routers/admin.mjs +8 -4
  213. package/dist/routers/admin.mjs.map +1 -1
  214. package/dist/routers/assets.mjs +10 -4
  215. package/dist/routers/assets.mjs.map +1 -1
  216. package/dist/routers/attempts.mjs +1 -2
  217. package/dist/routers/auth-sync.mjs +1 -2
  218. package/dist/routers/auth.mjs +2 -3
  219. package/dist/routers/baseline-guard.mjs +4 -5
  220. package/dist/routers/baseline-guard.mjs.map +1 -1
  221. package/dist/routers/baseline-reference.mjs +72 -0
  222. package/dist/routers/baseline-reference.mjs.map +1 -0
  223. package/dist/routers/baselines.handlers.mjs +63 -0
  224. package/dist/routers/baselines.handlers.mjs.map +1 -0
  225. package/dist/routers/baselines.mjs +124 -0
  226. package/dist/routers/baselines.mjs.map +1 -0
  227. package/dist/routers/build-cancel.mjs +67 -0
  228. package/dist/routers/build-cancel.mjs.map +1 -0
  229. package/dist/routers/build-changeset.mjs +69 -0
  230. package/dist/routers/build-changeset.mjs.map +1 -0
  231. package/dist/routers/build-reviews.handlers.mjs +85 -0
  232. package/dist/routers/build-reviews.handlers.mjs.map +1 -0
  233. package/dist/routers/build-reviews.mjs +73 -0
  234. package/dist/routers/build-reviews.mjs.map +1 -0
  235. package/dist/routers/builds.handlers.mjs +65 -60
  236. package/dist/routers/builds.handlers.mjs.map +1 -1
  237. package/dist/routers/builds.mjs +77 -19
  238. package/dist/routers/builds.mjs.map +1 -1
  239. package/dist/routers/capture-cache.mjs +29 -0
  240. package/dist/routers/capture-cache.mjs.map +1 -0
  241. package/dist/routers/capture-config.mjs +34 -0
  242. package/dist/routers/capture-config.mjs.map +1 -0
  243. package/dist/routers/comments.mjs +42 -17
  244. package/dist/routers/comments.mjs.map +1 -1
  245. package/dist/routers/comparison-image.mjs +29 -0
  246. package/dist/routers/comparison-image.mjs.map +1 -0
  247. package/dist/routers/component-catalog.handlers.mjs +70 -0
  248. package/dist/routers/component-catalog.handlers.mjs.map +1 -0
  249. package/dist/routers/component.handlers.mjs +120 -0
  250. package/dist/routers/component.handlers.mjs.map +1 -0
  251. package/dist/routers/connections.mjs +117 -0
  252. package/dist/routers/connections.mjs.map +1 -0
  253. package/dist/routers/design-connect.handlers.mjs +43 -0
  254. package/dist/routers/design-connect.handlers.mjs.map +1 -0
  255. package/dist/routers/design.handlers.mjs +113 -0
  256. package/dist/routers/design.handlers.mjs.map +1 -0
  257. package/dist/routers/flash.mjs +1 -2
  258. package/dist/routers/health-report.mjs +3 -5
  259. package/dist/routers/health-report.mjs.map +1 -1
  260. package/dist/routers/health.mjs +5 -5
  261. package/dist/routers/health.mjs.map +1 -1
  262. package/dist/routers/helpers.mjs +20 -5
  263. package/dist/routers/helpers.mjs.map +1 -1
  264. package/dist/routers/htmx.mjs +1 -2
  265. package/dist/routers/insights-ui.mjs +63 -0
  266. package/dist/routers/insights-ui.mjs.map +1 -0
  267. package/dist/routers/insights.handlers.mjs +44 -0
  268. package/dist/routers/insights.handlers.mjs.map +1 -0
  269. package/dist/routers/insights.mjs +188 -0
  270. package/dist/routers/insights.mjs.map +1 -0
  271. package/dist/routers/labels.mjs +1 -2
  272. package/dist/routers/links.handlers.mjs +79 -0
  273. package/dist/routers/links.handlers.mjs.map +1 -0
  274. package/dist/routers/links.mjs +138 -0
  275. package/dist/routers/links.mjs.map +1 -0
  276. package/dist/routers/media.mjs +30 -7
  277. package/dist/routers/media.mjs.map +1 -1
  278. package/dist/routers/members.mjs +3 -3
  279. package/dist/routers/members.mjs.map +1 -1
  280. package/dist/routers/notification-channels.mjs +18 -25
  281. package/dist/routers/notification-channels.mjs.map +1 -1
  282. package/dist/routers/notification-subscriptions.mjs +1 -2
  283. package/dist/routers/oauth-state.mjs +44 -0
  284. package/dist/routers/oauth-state.mjs.map +1 -0
  285. package/dist/routers/preview-base.mjs +37 -0
  286. package/dist/routers/preview-base.mjs.map +1 -0
  287. package/dist/routers/preview-token.mjs +50 -0
  288. package/dist/routers/preview-token.mjs.map +1 -0
  289. package/dist/routers/profile.mjs +4 -3
  290. package/dist/routers/profile.mjs.map +1 -1
  291. package/dist/routers/projects.mjs +18 -3
  292. package/dist/routers/projects.mjs.map +1 -1
  293. package/dist/routers/publish-upload.mjs +85 -0
  294. package/dist/routers/publish-upload.mjs.map +1 -0
  295. package/dist/routers/published-access.mjs +51 -0
  296. package/dist/routers/published-access.mjs.map +1 -0
  297. package/dist/routers/published-compression.mjs +109 -0
  298. package/dist/routers/published-compression.mjs.map +1 -0
  299. package/dist/routers/published-files.mjs +69 -0
  300. package/dist/routers/published-files.mjs.map +1 -0
  301. package/dist/routers/review-forms.mjs +71 -0
  302. package/dist/routers/review-forms.mjs.map +1 -0
  303. package/dist/routers/review-next.mjs +1 -2
  304. package/dist/routers/review-section.handlers.mjs +84 -0
  305. package/dist/routers/review-section.handlers.mjs.map +1 -0
  306. package/dist/routers/review-switch-off.mjs +28 -0
  307. package/dist/routers/review-switch-off.mjs.map +1 -0
  308. package/dist/routers/schemas.mjs +141 -24
  309. package/dist/routers/schemas.mjs.map +1 -1
  310. package/dist/routers/scoped-models.mjs +21 -0
  311. package/dist/routers/scoped-models.mjs.map +1 -0
  312. package/dist/routers/settings-ai.mjs +34 -0
  313. package/dist/routers/settings-ai.mjs.map +1 -0
  314. package/dist/routers/settings-capture.mjs +76 -0
  315. package/dist/routers/settings-capture.mjs.map +1 -0
  316. package/dist/routers/settings-design.mjs +102 -0
  317. package/dist/routers/settings-design.mjs.map +1 -0
  318. package/dist/routers/settings-general.mjs +56 -12
  319. package/dist/routers/settings-general.mjs.map +1 -1
  320. package/dist/routers/settings-git-channel.mjs +76 -0
  321. package/dist/routers/settings-git-channel.mjs.map +1 -0
  322. package/dist/routers/settings-labels.mjs +28 -3
  323. package/dist/routers/settings-labels.mjs.map +1 -1
  324. package/dist/routers/settings-links.mjs +62 -0
  325. package/dist/routers/settings-links.mjs.map +1 -0
  326. package/dist/routers/settings-members.mjs +5 -9
  327. package/dist/routers/settings-members.mjs.map +1 -1
  328. package/dist/routers/settings-notifications.mjs +16 -12
  329. package/dist/routers/settings-notifications.mjs.map +1 -1
  330. package/dist/routers/settings-tokens.mjs +3 -7
  331. package/dist/routers/settings-tokens.mjs.map +1 -1
  332. package/dist/routers/settings-webhooks.mjs +8 -26
  333. package/dist/routers/settings-webhooks.mjs.map +1 -1
  334. package/dist/routers/settings.handlers.mjs +108 -28
  335. package/dist/routers/settings.handlers.mjs.map +1 -1
  336. package/dist/routers/settings.mjs +17 -6
  337. package/dist/routers/settings.mjs.map +1 -1
  338. package/dist/routers/signed-storybook.mjs +55 -0
  339. package/dist/routers/signed-storybook.mjs.map +1 -0
  340. package/dist/routers/snapshot-groups.mjs +62 -0
  341. package/dist/routers/snapshot-groups.mjs.map +1 -0
  342. package/dist/routers/snapshots.mjs +104 -7
  343. package/dist/routers/snapshots.mjs.map +1 -1
  344. package/dist/routers/storybook.mjs +57 -104
  345. package/dist/routers/storybook.mjs.map +1 -1
  346. package/dist/routers/tokens.mjs +3 -7
  347. package/dist/routers/tokens.mjs.map +1 -1
  348. package/dist/routers/ui.mjs +38 -7
  349. package/dist/routers/ui.mjs.map +1 -1
  350. package/dist/routers/webhooks.mjs +6 -13
  351. package/dist/routers/webhooks.mjs.map +1 -1
  352. package/dist/routers/well-known.mjs +12 -6
  353. package/dist/routers/well-known.mjs.map +1 -1
  354. package/dist/routes/api-routes.mjs +92 -0
  355. package/dist/routes/api-routes.mjs.map +1 -0
  356. package/dist/routes/classification.mjs +37 -0
  357. package/dist/routes/classification.mjs.map +1 -0
  358. package/dist/routes/openapi-classes.mjs +21 -0
  359. package/dist/routes/openapi-classes.mjs.map +1 -0
  360. package/dist/routes/page-routes.mjs +103 -0
  361. package/dist/routes/page-routes.mjs.map +1 -0
  362. package/dist/runtime.mjs +19 -15
  363. package/dist/runtime.mjs.map +1 -1
  364. package/dist/serverless.mjs +4 -0
  365. package/dist/store.mjs +20 -3
  366. package/dist/store.mjs.map +1 -1
  367. package/dist/targets.mjs +45 -0
  368. package/dist/targets.mjs.map +1 -0
  369. package/dist/ui/avatar.mjs +1 -2
  370. package/dist/ui/buttons.mjs +17 -5
  371. package/dist/ui/buttons.mjs.map +1 -1
  372. package/dist/ui/code-block.mjs +1 -2
  373. package/dist/ui/compare-stage.mjs +86 -54
  374. package/dist/ui/compare-stage.mjs.map +1 -1
  375. package/dist/ui/csrf-field.mjs +1 -2
  376. package/dist/ui/css.mjs +1 -2
  377. package/dist/ui/design-reference.mjs +96 -0
  378. package/dist/ui/design-reference.mjs.map +1 -0
  379. package/dist/ui/document.mjs +2 -4
  380. package/dist/ui/document.mjs.map +1 -1
  381. package/dist/ui/dropdown.mjs +1 -2
  382. package/dist/ui/feedback.mjs +1 -2
  383. package/dist/ui/filter-input.mjs +1 -2
  384. package/dist/ui/forms.mjs +7 -7
  385. package/dist/ui/forms.mjs.map +1 -1
  386. package/dist/ui/icons/icon.mjs +1 -2
  387. package/dist/ui/icons/paths.mjs +2 -2
  388. package/dist/ui/icons/paths.mjs.map +1 -1
  389. package/dist/ui/icons/sprite.mjs +1 -2
  390. package/dist/ui/kbd.mjs +1 -2
  391. package/dist/ui/layout.mjs +1 -2
  392. package/dist/ui/preview-embed.mjs +81 -0
  393. package/dist/ui/preview-embed.mjs.map +1 -0
  394. package/dist/ui/progress.mjs +1 -2
  395. package/dist/ui/segmented.mjs +1 -2
  396. package/dist/ui/shell/account.mjs +1 -2
  397. package/dist/ui/shell/bare.mjs +1 -2
  398. package/dist/ui/shell/boot-script.mjs +1 -2
  399. package/dist/ui/shell/client-script.mjs +86 -2
  400. package/dist/ui/shell/client-script.mjs.map +1 -1
  401. package/dist/ui/shell/nav.mjs +13 -10
  402. package/dist/ui/shell/nav.mjs.map +1 -1
  403. package/dist/ui/shell/review-script.mjs +25 -3
  404. package/dist/ui/shell/review-script.mjs.map +1 -1
  405. package/dist/ui/shell/sidebar.mjs +3 -4
  406. package/dist/ui/shell/sidebar.mjs.map +1 -1
  407. package/dist/ui/shell/topbar.mjs +1 -2
  408. package/dist/ui/stacks.mjs +1 -2
  409. package/dist/ui/styles/base.mjs +1 -2
  410. package/dist/ui/styles/behavior.mjs +1 -2
  411. package/dist/ui/styles/review-layout.mjs +1 -2
  412. package/dist/ui/styles/review.mjs +1 -2
  413. package/dist/ui/styles/tokens.mjs +1 -2
  414. package/dist/ui/styles.mjs +1 -2
  415. package/dist/ui/subnav.mjs +1 -2
  416. package/dist/ui/table.mjs +1 -2
  417. package/dist/ui/theme.mjs +1 -2
  418. package/dist/ui/thumbnail.mjs +1 -2
  419. package/dist/ui/time.mjs +1 -2
  420. package/package.json +14 -12
  421. package/dist/asset-paths.mjs +0 -23
  422. package/dist/asset-paths.mjs.map +0 -1
  423. package/dist/assets/mark.svg +0 -1
  424. package/dist/pages/settings-status.mjs +0 -137
  425. package/dist/pages/settings-status.mjs.map +0 -1
  426. package/dist/routers/settings-status.mjs +0 -77
  427. package/dist/routers/settings-status.mjs.map +0 -1
  428. package/dist/routers/status-configs.mjs +0 -108
  429. package/dist/routers/status-configs.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-design-links.mjs","names":[],"sources":["../../src/pages/settings-design-links.tsx"],"sourcesContent":["import type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Button, Card, Field, FormActions, Meta, SectionTitle, Table } from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ntype Rendered = HtmlEscapedString | Promise<HtmlEscapedString>;\n\n/** Field-level and page-level errors of the Design settings forms. */\nexport type DesignFormState = { errors?: Record<string, string>; globalError?: string };\n\n/** A project-level design file link (shown in the library header). */\nexport interface SettingsDesignFile {\n id: string;\n label: string;\n url: string;\n}\n\n/** A manual `import_path → URL` component mapping. */\nexport interface SettingsDesignComponent {\n id: string;\n importPath: string;\n url: string;\n}\n\nexport function FilesCard(props: {\n base: string;\n files: SettingsDesignFile[];\n isAdmin: boolean;\n formState?: DesignFormState;\n urlHint: string;\n}): Rendered {\n const { base, files, isAdmin, formState, urlHint } = props;\n return (\n <Card>\n <SectionTitle>Design files</SectionTitle>\n <Meta>\n Design files for this project. They appear in the library header; individual stories link to\n a design through their component (see below) or their story parameters.\n </Meta>\n <LinkTable\n rows={files.map((file) => ({\n id: file.id,\n key: file.label,\n url: file.url,\n removeAction: isAdmin ? `${base}/files/${file.id}/delete` : undefined,\n }))}\n keyHeading=\"Label\"\n confirm=\"Remove this design file?\"\n />\n {files.length === 0 ? <Meta>No design files linked.</Meta> : null}\n {isAdmin ? (\n <AddFileForm base={base} urlHint={urlHint} error={formState?.errors?.[\"file\"]} />\n ) : null}\n </Card>\n );\n}\n\n/** Rows of `key → design link` with an optional remove action. */\nfunction LinkTable(props: {\n rows: { id: string; key: string; url: string; removeAction?: string }[];\n keyHeading: string;\n confirm: string;\n}): Rendered {\n return (\n <Table>\n <table>\n <thead>\n <tr>\n <th>{props.keyHeading}</th>\n <th>Design</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {props.rows.map((row): Rendered => (\n <tr key={row.id}>\n <td>{row.key}</td>\n <td class=\"truncate max-w-cell\" title={row.url}>\n <a href={row.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {row.url}\n </a>\n </td>\n <td>\n {row.removeAction ? (\n <RemoveForm\n action={row.removeAction}\n confirm={props.confirm}\n label={`Remove ${row.key}`}\n />\n ) : null}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Table>\n );\n}\n\nfunction AddFileForm(props: { base: string; urlHint: string; error?: string }): Rendered {\n const action = `${props.base}/files`;\n return (\n <form method=\"post\" action={action} hx-post={action} hx-target=\"body\">\n {csrfField()}\n <div class=\"grid grid--2\">\n <Field label=\"Label\" name=\"label\" required placeholder=\"Design system\" />\n <Field\n label=\"Design URL\"\n name=\"url\"\n type=\"url\"\n required\n placeholder={props.urlHint}\n error={props.error}\n />\n </div>\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"plus\">\n Add design file\n </Button>\n </FormActions>\n </form>\n );\n}\n\nexport function ComponentsCard(props: {\n base: string;\n components: SettingsDesignComponent[];\n formState?: DesignFormState;\n urlHint: string;\n}): Rendered {\n const { base, components, formState, urlHint } = props;\n return (\n <Card>\n <SectionTitle>Component mapping</SectionTitle>\n <Meta>\n Map a story file (its import path) to a design. A manual mapping overrides the story&apos;s\n own <code>storyshelf.figma</code> and <code>design</code> parameters, and survives new\n builds. Mappings are keyed by the story file path, so update one after a file is renamed or\n moved.\n </Meta>\n <LinkTable\n rows={components.map((component) => ({\n id: component.id,\n key: component.importPath,\n url: component.url,\n removeAction: `${base}/components/${component.id}/delete`,\n }))}\n keyHeading=\"Story file\"\n confirm=\"Remove this mapping?\"\n />\n {components.length === 0 ? <Meta>No component mappings.</Meta> : null}\n <MapComponentForm base={base} urlHint={urlHint} error={formState?.errors?.[\"component\"]} />\n </Card>\n );\n}\n\nfunction MapComponentForm(props: { base: string; urlHint: string; error?: string }): Rendered {\n const action = `${props.base}/components`;\n return (\n <form method=\"post\" action={action} hx-post={action} hx-target=\"body\">\n {csrfField()}\n <div class=\"grid grid--2\">\n <Field\n label=\"Story file\"\n name=\"importPath\"\n required\n placeholder=\"./src/Button.stories.tsx\"\n />\n <Field\n label=\"Design URL\"\n name=\"url\"\n type=\"url\"\n required\n placeholder={props.urlHint}\n error={props.error}\n />\n </div>\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"plus\">\n Map component\n </Button>\n </FormActions>\n </form>\n );\n}\n\nexport function RemoveForm(props: { action: string; confirm: string; label: string }): Rendered {\n return (\n <form\n method=\"post\"\n action={props.action}\n hx-post={props.action}\n hx-target=\"body\"\n hx-confirm={props.confirm}\n >\n {csrfField()}\n <Button variant=\"ghost\" size=\"sm\" type=\"submit\" aria-label={props.label}>\n Remove\n </Button>\n </form>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Alert, EmptyState } from "../ui/feedback.mjs";
5
+ import { ComponentsCard, FilesCard } from "./settings-design-links.mjs";
6
+ import { CredentialCard } from "./settings-design-credential.mjs";
7
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
8
+ // src/pages/settings-design.tsx
9
+ /** Design settings tab: project file links, component mapping and provider credentials (admin-only parts gated). */
10
+ function renderSettingsDesign(project, design, isAdmin, formState) {
11
+ const base = `/projects/${project.slug}/settings/design`;
12
+ const urlHint = design.providers.map((provider) => provider.urlHint).join(" or ");
13
+ if (design.providers.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
14
+ title: "No design provider configured",
15
+ description: "Add a design provider (for example @storyshelf/design-figma) to createShelfApp({ design: [...] }) to link components to design files."
16
+ });
17
+ return /* @__PURE__ */ jsxs("div", {
18
+ class: "grid max-w-form",
19
+ children: [
20
+ formState?.globalError ? /* @__PURE__ */ jsx(Alert, {
21
+ tone: "danger",
22
+ children: formState.globalError
23
+ }) : null,
24
+ /* @__PURE__ */ jsx(FilesCard, {
25
+ base,
26
+ files: design.files,
27
+ isAdmin,
28
+ formState,
29
+ urlHint
30
+ }),
31
+ isAdmin ? /* @__PURE__ */ jsx(ComponentsCard, {
32
+ base,
33
+ components: design.components,
34
+ formState,
35
+ urlHint
36
+ }) : null,
37
+ isAdmin ? design.providers.map((provider) => /* @__PURE__ */ jsx(CredentialCard, {
38
+ base,
39
+ provider,
40
+ formState
41
+ }, provider.kind)) : null
42
+ ]
43
+ });
44
+ }
45
+ export { renderSettingsDesign };
46
+
47
+ //# sourceMappingURL=settings-design.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-design.mjs","names":[],"sources":["../../src/pages/settings-design.tsx"],"sourcesContent":["import type { Project } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Alert, EmptyState } from \"../ui/components.tsx\";\nimport { CredentialCard, type SettingsDesignProvider } from \"./settings-design-credential.tsx\";\nimport {\n ComponentsCard,\n FilesCard,\n type DesignFormState,\n type SettingsDesignComponent,\n type SettingsDesignFile,\n} from \"./settings-design-links.tsx\";\n\nexport type { SettingsDesignComponent, SettingsDesignFile } from \"./settings-design-links.tsx\";\nexport type {\n DesignCredentialState,\n SettingsDesignCredential,\n SettingsDesignProvider,\n} from \"./settings-design-credential.tsx\";\n\n/** Data behind the Design settings tab. */\nexport interface SettingsDesignData {\n files: SettingsDesignFile[];\n components: SettingsDesignComponent[];\n /** Configured design providers; empty when the server wires none. */\n providers: SettingsDesignProvider[];\n}\n\n/** Design settings tab: project file links, component mapping and provider credentials (admin-only parts gated). */\nexport function renderSettingsDesign(\n project: Project,\n design: SettingsDesignData,\n isAdmin: boolean,\n formState?: DesignFormState,\n): unknown {\n const base = `/projects/${project.slug}/settings/design`;\n const urlHint = design.providers.map((provider) => provider.urlHint).join(\" or \");\n if (design.providers.length === 0) {\n return (\n <EmptyState\n title=\"No design provider configured\"\n description=\"Add a design provider (for example @storyshelf/design-figma) to createShelfApp({ design: [...] }) to link components to design files.\"\n />\n );\n }\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <FilesCard\n base={base}\n files={design.files}\n isAdmin={isAdmin}\n formState={formState}\n urlHint={urlHint}\n />\n {isAdmin ? (\n <ComponentsCard\n base={base}\n components={design.components}\n formState={formState}\n urlHint={urlHint}\n />\n ) : null}\n {isAdmin\n ? design.providers.map((provider): HtmlEscapedString | Promise<HtmlEscapedString> => (\n <CredentialCard\n key={provider.kind}\n base={base}\n provider={provider}\n formState={formState}\n />\n ))\n : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AA4BA,SAAgB,qBACd,SACA,QACA,SACA,WACS;CACT,MAAM,OAAO,aAAa,QAAQ,KAAK;CACvC,MAAM,UAAU,OAAO,UAAU,KAAK,aAAa,SAAS,OAAO,CAAC,CAAC,KAAK,MAAM;CAChF,IAAI,OAAO,UAAU,WAAW,GAC9B,OACE,oBAAC,YAAD;EACE,OAAM;EACN,aAAY;CACb,CAAA;CAGL,OACE,qBAAC,OAAD;EAAK,OAAM;EAAX,UAAA;GACG,WAAW,cAAc,oBAAC,OAAD;IAAO,MAAK;IAAU,UAAA,UAAU;GAAmB,CAAA,IAAI;GACjF,oBAAC,WAAD;IACQ;IACN,OAAO,OAAO;IACL;IACE;IACF;GACV,CAAA;GACA,UACC,oBAAC,gBAAD;IACQ;IACN,YAAY,OAAO;IACR;IACF;GACV,CAAA,IACC;GACH,UACG,OAAO,UAAU,KAAK,aACpB,oBAAC,gBAAD;IAEQ;IACI;IACC;GACZ,GAJM,SAAS,IAIf,CACF,IACD;EACD;;AAET"}
@@ -3,15 +3,18 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { Button } from "../ui/buttons.mjs";
5
5
  import { Alert, Meta } from "../ui/feedback.mjs";
6
- import { Field, FormActions, SelectField } from "../ui/forms.mjs";
6
+ import { CheckField, Field, FormActions, SelectField } from "../ui/forms.mjs";
7
7
  import { Card, SectionTitle } from "../ui/layout.mjs";
8
8
  import { csrfField } from "../ui/csrf-field.mjs";
9
+ import { targetChoices } from "./target-choices.mjs";
9
10
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
10
- //#region src/pages/settings-general.tsx
11
+ import { isCaptureTarget } from "@storyshelf/core/adapter/capture-runner";
12
+ // src/pages/settings-general.tsx
11
13
  /** General settings tab: project metadata, diff thresholds, and danger zone. */
12
- function renderSettingsGeneral(project, formState, isAdmin) {
14
+ function renderSettingsGeneral(project, formState, isAdmin, offered) {
13
15
  const errors = formState?.errors ?? {};
14
16
  const browser = project.browser ?? "chromium";
17
+ const targets = targetChoices(offered, isCaptureTarget(browser) ? [browser] : []);
15
18
  return /* @__PURE__ */ jsxs("div", {
16
19
  class: "grid max-w-form",
17
20
  children: [
@@ -71,25 +74,8 @@ function renderSettingsGeneral(project, formState, isAdmin) {
71
74
  name: "browser",
72
75
  value: browser,
73
76
  disabled: !isAdmin,
74
- options: [
75
- {
76
- value: "chromium",
77
- label: "Chromium (default)"
78
- },
79
- {
80
- value: "firefox",
81
- label: "Firefox"
82
- },
83
- {
84
- value: "webkit",
85
- label: "WebKit (Safari engine)"
86
- },
87
- {
88
- value: "chrome",
89
- label: "Chrome (Chromium engine)"
90
- }
91
- ],
92
- hint: "Rendering engine for captures. Baselines track the browser, so switching starts a fresh baseline. Firefox and WebKit need Playwright system dependencies installed on the server."
77
+ options: targets.choices,
78
+ hint: `Where captures render. Baselines are kept per target, so switching starts a fresh baseline for the new one.${targets.unavailable.length > 0 ? ` Not selectable on this server: ${targets.unavailable.join("; ")}.` : ""}`
93
79
  }),
94
80
  /* @__PURE__ */ jsx(Field, {
95
81
  label: "Public branch regex",
@@ -106,6 +92,45 @@ function renderSettingsGeneral(project, formState, isAdmin) {
106
92
  }) }) : /* @__PURE__ */ jsx(Meta, { children: "You need admin access to edit settings." })
107
93
  ]
108
94
  })] }),
95
+ /* @__PURE__ */ jsxs(Card, { children: [
96
+ /* @__PURE__ */ jsx(SectionTitle, { children: "Review" }),
97
+ /* @__PURE__ */ jsx(Meta, { children: "Comments on builds and stories, and a build-level verdict from approvers and admins. The default branch is never held for review." }),
98
+ /* @__PURE__ */ jsxs("form", {
99
+ method: "post",
100
+ action: `/projects/${project.slug}/settings/review`,
101
+ "hx-post": `/projects/${project.slug}/settings/review`,
102
+ "hx-target": "body",
103
+ "hx-swap": "outerHTML",
104
+ children: [
105
+ csrfField(),
106
+ /* @__PURE__ */ jsx(CheckField, {
107
+ label: "Review builds before they are accepted",
108
+ name: "reviewEnabled",
109
+ value: "true",
110
+ checked: project.reviewEnabled !== false,
111
+ disabled: !isAdmin,
112
+ hint: "With review off, comments and verdicts are hidden, not deleted, and the project's builds waiting for a verdict are accepted when you save."
113
+ }),
114
+ /* @__PURE__ */ jsx(Field, {
115
+ label: "Approvals required",
116
+ name: "reviewApprovals",
117
+ type: "number",
118
+ step: "1",
119
+ min: "1",
120
+ max: "20",
121
+ value: String(project.reviewApprovals ?? 1),
122
+ error: errors["reviewApprovals"],
123
+ hint: "Approvals a build needs when screenshots are not captured. A rejection always rejects the build."
124
+ }),
125
+ isAdmin ? /* @__PURE__ */ jsx(FormActions, { children: /* @__PURE__ */ jsx(Button, {
126
+ variant: "primary",
127
+ type: "submit",
128
+ icon: "check",
129
+ children: "Save review settings"
130
+ }) }) : null
131
+ ]
132
+ })
133
+ ] }),
109
134
  isAdmin ? /* @__PURE__ */ jsxs(Card, {
110
135
  tone: "danger",
111
136
  children: [
@@ -132,7 +157,6 @@ function renderSettingsGeneral(project, formState, isAdmin) {
132
157
  ]
133
158
  });
134
159
  }
135
- //#endregion
136
160
  export { renderSettingsGeneral };
137
161
 
138
162
  //# sourceMappingURL=settings-general.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-general.mjs","names":[],"sources":["../../src/pages/settings-general.tsx"],"sourcesContent":["import type { Project } from \"@storyshelf/core/schema\";\nimport {\n Alert,\n Button,\n Card,\n Field,\n FormActions,\n Meta,\n SectionTitle,\n SelectField,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/** Form state for the settings tabs (field errors and global error). */\nexport interface SettingsFormState {\n errors?: Record<string, string>;\n globalError?: string;\n}\n\n/** General settings tab: project metadata, diff thresholds, and danger zone. */\nexport function renderSettingsGeneral(\n project: Project,\n formState: SettingsFormState | undefined,\n isAdmin: boolean,\n): unknown {\n const errors = formState?.errors ?? {};\n const browser = project.browser ?? \"chromium\";\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <Card>\n <SectionTitle>General</SectionTitle>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings`}\n hx-post={`/projects/${project.slug}/settings`}\n hx-target=\"body\"\n hx-swap=\"outerHTML\"\n >\n {csrfField()}\n <Field label=\"Name\" name=\"name\" value={project.name} error={errors[\"name\"]} />\n <Field\n label=\"Git repository\"\n name=\"gitRepository\"\n value={project.gitRepository ?? \"\"}\n placeholder=\"owner/repo\"\n />\n <Field label=\"Default branch\" name=\"gitDefaultBranch\" value={project.gitDefaultBranch} />\n <div class=\"grid grid--2\">\n <Field\n label=\"Pixel threshold\"\n name=\"pixelThreshold\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n max=\"1\"\n value={String(project.pixelThreshold)}\n hint=\"Per-pixel color distance 0–1\"\n />\n <Field\n label=\"Max diff ratio\"\n name=\"maxDiffRatio\"\n type=\"number\"\n step=\"0.001\"\n min=\"0\"\n max=\"1\"\n value={String(project.maxDiffRatio)}\n hint=\"Allowed diff ratio 0–1\"\n />\n </div>\n <SelectField\n label=\"Capture browser\"\n name=\"browser\"\n value={browser}\n disabled={!isAdmin}\n options={[\n { value: \"chromium\", label: \"Chromium (default)\" },\n { value: \"firefox\", label: \"Firefox\" },\n { value: \"webkit\", label: \"WebKit (Safari engine)\" },\n { value: \"chrome\", label: \"Chrome (Chromium engine)\" },\n ]}\n hint=\"Rendering engine for captures. Baselines track the browser, so switching starts a fresh baseline. Firefox and WebKit need Playwright system dependencies installed on the server.\"\n />\n <Field\n label=\"Public branch regex\"\n name=\"publicBranchRegex\"\n value={project.publicBranchRegex ?? \"\"}\n placeholder=\"^main$\"\n hint=\"Branches matching this regex are publicly viewable.\"\n />\n {isAdmin ? (\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"check\">\n Save changes\n </Button>\n </FormActions>\n ) : (\n <Meta>You need admin access to edit settings.</Meta>\n )}\n </form>\n </Card>\n\n {isAdmin ? (\n <Card tone=\"danger\">\n <SectionTitle level={3} tone=\"danger\">\n Danger zone\n </SectionTitle>\n <Meta>\n Deleting a project removes all builds, snapshots, baselines and tokens. This cannot be\n undone.\n </Meta>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/delete`}\n hx-post={`/projects/${project.slug}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Delete this project? This cannot be undone.\"\n >\n <Button variant=\"danger\" type=\"submit\">\n Delete project\n </Button>\n </form>\n </Card>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAoBA,SAAgB,sBACd,SACA,WACA,SACS;CACT,MAAM,SAAS,WAAW,UAAU,CAAC;CACrC,MAAM,UAAU,QAAQ,WAAW;CACnC,OACE,qBAAC,OAAD;EAAK,OAAM;EAAX,UAAA;GACG,WAAW,cAAc,oBAAC,OAAD;IAAO,MAAK;IAAU,UAAA,UAAU;GAAmB,CAAA,IAAI;GACjF,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAA,UAAc,UAAqB,CAAA,GACnC,qBAAC,QAAD;IACE,QAAO;IACP,QAAQ,aAAa,QAAQ,KAAK;IAClC,WAAS,aAAa,QAAQ,KAAK;IACnC,aAAU;IACV,WAAQ;IALV,UAAA;KAOG,UAAU;KACX,oBAAC,OAAD;MAAO,OAAM;MAAO,MAAK;MAAO,OAAO,QAAQ;MAAM,OAAO,OAAO;KAAU,CAAA;KAC7E,oBAAC,OAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO,QAAQ,iBAAiB;MAChC,aAAY;KACb,CAAA;KACD,oBAAC,OAAD;MAAO,OAAM;MAAiB,MAAK;MAAmB,OAAO,QAAQ;KAAmB,CAAA;KACxF,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,MAAK;OACL,MAAK;OACL,KAAI;OACJ,KAAI;OACJ,OAAO,OAAO,QAAQ,cAAc;OACpC,MAAK;MACN,CAAA,GACD,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,MAAK;OACL,MAAK;OACL,KAAI;OACJ,KAAI;OACJ,OAAO,OAAO,QAAQ,YAAY;OAClC,MAAK;MACN,CAAA,CACE;;KACL,oBAAC,aAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO;MACP,UAAU,CAAC;MACX,SAAS;OACP;QAAE,OAAO;QAAY,OAAO;OAAqB;OACjD;QAAE,OAAO;QAAW,OAAO;OAAU;OACrC;QAAE,OAAO;QAAU,OAAO;OAAyB;OACnD;QAAE,OAAO;QAAU,OAAO;OAA2B;MACvD;MACA,MAAK;KACN,CAAA;KACD,oBAAC,OAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO,QAAQ,qBAAqB;MACpC,aAAY;MACZ,MAAK;KACN,CAAA;KACA,UACC,oBAAC,aAAD,EAAA,UACE,oBAAC,QAAD;MAAQ,SAAQ;MAAU,MAAK;MAAS,MAAK;MAAQ,UAAA;KAE7C,CAAA,EACG,CAAA,IAEb,oBAAC,MAAD,EAAA,UAAM,0CAA6C,CAAA;IAEjD;GACF,CAAA,CAAA,EAAA,CAAA;GAEL,UACC,qBAAC,MAAD;IAAM,MAAK;IAAX,UAAA;KACE,oBAAC,cAAD;MAAc,OAAO;MAAG,MAAK;MAAS,UAAA;KAExB,CAAA;KACd,oBAAC,MAAD,EAAA,UAAM,iGAGA,CAAA;KACN,oBAAC,QAAD;MACE,QAAO;MACP,QAAQ,aAAa,QAAQ,KAAK;MAClC,WAAS,aAAa,QAAQ,KAAK;MACnC,aAAU;MACV,cAAW;MAEX,UAAA,oBAAC,QAAD;OAAQ,SAAQ;OAAS,MAAK;OAAS,UAAA;MAE/B,CAAA;KACJ,CAAA;IACF;GACJ,CAAA,IAAA;EACD;;AAET"}
1
+ {"version":3,"file":"settings-general.mjs","names":[],"sources":["../../src/pages/settings-general.tsx"],"sourcesContent":["import { isCaptureTarget, type RunnerTarget } from \"@storyshelf/core/adapter/capture-runner\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport {\n Alert,\n Button,\n Card,\n CheckField,\n Field,\n FormActions,\n Meta,\n SectionTitle,\n SelectField,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\nimport { targetChoices } from \"./target-choices.ts\";\n\n/** Form state for the settings tabs (field errors and global error). */\nexport interface SettingsFormState {\n errors?: Record<string, string>;\n globalError?: string;\n}\n\n/** General settings tab: project metadata, diff thresholds, and danger zone. */\nexport function renderSettingsGeneral(\n project: Project,\n formState: SettingsFormState | undefined,\n isAdmin: boolean,\n /** What the runner offers; omitted when it publishes no list. */\n offered?: readonly RunnerTarget[],\n): unknown {\n const errors = formState?.errors ?? {};\n const browser = project.browser ?? \"chromium\";\n const targets = targetChoices(offered, isCaptureTarget(browser) ? [browser] : []);\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <Card>\n <SectionTitle>General</SectionTitle>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings`}\n hx-post={`/projects/${project.slug}/settings`}\n hx-target=\"body\"\n hx-swap=\"outerHTML\"\n >\n {csrfField()}\n <Field label=\"Name\" name=\"name\" value={project.name} error={errors[\"name\"]} />\n <Field\n label=\"Git repository\"\n name=\"gitRepository\"\n value={project.gitRepository ?? \"\"}\n placeholder=\"owner/repo\"\n />\n <Field label=\"Default branch\" name=\"gitDefaultBranch\" value={project.gitDefaultBranch} />\n <div class=\"grid grid--2\">\n <Field\n label=\"Pixel threshold\"\n name=\"pixelThreshold\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n max=\"1\"\n value={String(project.pixelThreshold)}\n hint=\"Per-pixel color distance 0–1\"\n />\n <Field\n label=\"Max diff ratio\"\n name=\"maxDiffRatio\"\n type=\"number\"\n step=\"0.001\"\n min=\"0\"\n max=\"1\"\n value={String(project.maxDiffRatio)}\n hint=\"Allowed diff ratio 0–1\"\n />\n </div>\n <SelectField\n label=\"Capture browser\"\n name=\"browser\"\n value={browser}\n disabled={!isAdmin}\n options={targets.choices}\n hint={`Where captures render. Baselines are kept per target, so switching starts a fresh baseline for the new one.${\n targets.unavailable.length > 0\n ? ` Not selectable on this server: ${targets.unavailable.join(\"; \")}.`\n : \"\"\n }`}\n />\n <Field\n label=\"Public branch regex\"\n name=\"publicBranchRegex\"\n value={project.publicBranchRegex ?? \"\"}\n placeholder=\"^main$\"\n hint=\"Branches matching this regex are publicly viewable.\"\n />\n {isAdmin ? (\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"check\">\n Save changes\n </Button>\n </FormActions>\n ) : (\n <Meta>You need admin access to edit settings.</Meta>\n )}\n </form>\n </Card>\n\n <Card>\n <SectionTitle>Review</SectionTitle>\n <Meta>\n Comments on builds and stories, and a build-level verdict from approvers and admins. The\n default branch is never held for review.\n </Meta>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/review`}\n hx-post={`/projects/${project.slug}/settings/review`}\n hx-target=\"body\"\n hx-swap=\"outerHTML\"\n >\n {csrfField()}\n <CheckField\n label=\"Review builds before they are accepted\"\n name=\"reviewEnabled\"\n value=\"true\"\n checked={project.reviewEnabled !== false}\n disabled={!isAdmin}\n hint=\"With review off, comments and verdicts are hidden, not deleted, and the project's builds waiting for a verdict are accepted when you save.\"\n />\n <Field\n label=\"Approvals required\"\n name=\"reviewApprovals\"\n type=\"number\"\n step=\"1\"\n min=\"1\"\n max=\"20\"\n value={String(project.reviewApprovals ?? 1)}\n error={errors[\"reviewApprovals\"]}\n hint=\"Approvals a build needs when screenshots are not captured. A rejection always rejects the build.\"\n />\n {isAdmin ? (\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"check\">\n Save review settings\n </Button>\n </FormActions>\n ) : null}\n </form>\n </Card>\n\n {isAdmin ? (\n <Card tone=\"danger\">\n <SectionTitle level={3} tone=\"danger\">\n Danger zone\n </SectionTitle>\n <Meta>\n Deleting a project removes all builds, snapshots, baselines and tokens. This cannot be\n undone.\n </Meta>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/delete`}\n hx-post={`/projects/${project.slug}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Delete this project? This cannot be undone.\"\n >\n <Button variant=\"danger\" type=\"submit\">\n Delete project\n </Button>\n </form>\n </Card>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,SAAgB,sBACd,SACA,WACA,SAEA,SACS;CACT,MAAM,SAAS,WAAW,UAAU,CAAC;CACrC,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,UAAU,cAAc,SAAS,gBAAgB,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;CAChF,OACE,qBAAC,OAAD;EAAK,OAAM;EAAX,UAAA;GACG,WAAW,cAAc,oBAAC,OAAD;IAAO,MAAK;IAAU,UAAA,UAAU;GAAmB,CAAA,IAAI;GACjF,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAA,UAAc,UAAqB,CAAA,GACnC,qBAAC,QAAD;IACE,QAAO;IACP,QAAQ,aAAa,QAAQ,KAAK;IAClC,WAAS,aAAa,QAAQ,KAAK;IACnC,aAAU;IACV,WAAQ;IALV,UAAA;KAOG,UAAU;KACX,oBAAC,OAAD;MAAO,OAAM;MAAO,MAAK;MAAO,OAAO,QAAQ;MAAM,OAAO,OAAO;KAAU,CAAA;KAC7E,oBAAC,OAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO,QAAQ,iBAAiB;MAChC,aAAY;KACb,CAAA;KACD,oBAAC,OAAD;MAAO,OAAM;MAAiB,MAAK;MAAmB,OAAO,QAAQ;KAAmB,CAAA;KACxF,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,MAAK;OACL,MAAK;OACL,KAAI;OACJ,KAAI;OACJ,OAAO,OAAO,QAAQ,cAAc;OACpC,MAAK;MACN,CAAA,GACD,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,MAAK;OACL,MAAK;OACL,KAAI;OACJ,KAAI;OACJ,OAAO,OAAO,QAAQ,YAAY;OAClC,MAAK;MACN,CAAA,CACE;;KACL,oBAAC,aAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO;MACP,UAAU,CAAC;MACX,SAAS,QAAQ;MACjB,MAAM,8GACJ,QAAQ,YAAY,SAAS,IACzB,mCAAmC,QAAQ,YAAY,KAAK,IAAI,EAAE,KAClE;KAEP,CAAA;KACD,oBAAC,OAAD;MACE,OAAM;MACN,MAAK;MACL,OAAO,QAAQ,qBAAqB;MACpC,aAAY;MACZ,MAAK;KACN,CAAA;KACA,UACC,oBAAC,aAAD,EAAA,UACE,oBAAC,QAAD;MAAQ,SAAQ;MAAU,MAAK;MAAS,MAAK;MAAQ,UAAA;KAE7C,CAAA,EACG,CAAA,IAEb,oBAAC,MAAD,EAAA,UAAM,0CAA6C,CAAA;IAEjD;GACF,CAAA,CAAA,EAAA,CAAA;GAEN,qBAAC,MAAD,EAAA,UAAA;IACE,oBAAC,cAAD,EAAA,UAAc,SAAoB,CAAA;IAClC,oBAAC,MAAD,EAAA,UAAM,oIAGA,CAAA;IACN,qBAAC,QAAD;KACE,QAAO;KACP,QAAQ,aAAa,QAAQ,KAAK;KAClC,WAAS,aAAa,QAAQ,KAAK;KACnC,aAAU;KACV,WAAQ;KALV,UAAA;MAOG,UAAU;MACX,oBAAC,YAAD;OACE,OAAM;OACN,MAAK;OACL,OAAM;OACN,SAAS,QAAQ,kBAAkB;OACnC,UAAU,CAAC;OACX,MAAK;MACN,CAAA;MACD,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,MAAK;OACL,MAAK;OACL,KAAI;OACJ,KAAI;OACJ,OAAO,OAAO,QAAQ,mBAAmB,CAAC;OAC1C,OAAO,OAAO;OACd,MAAK;MACN,CAAA;MACA,UACC,oBAAC,aAAD,EAAA,UACE,oBAAC,QAAD;OAAQ,SAAQ;OAAU,MAAK;OAAS,MAAK;OAAQ,UAAA;MAE7C,CAAA,EACG,CAAA,IACX;KACA;;GACF,EAAA,CAAA;GAEL,UACC,qBAAC,MAAD;IAAM,MAAK;IAAX,UAAA;KACE,oBAAC,cAAD;MAAc,OAAO;MAAG,MAAK;MAAS,UAAA;KAExB,CAAA;KACd,oBAAC,MAAD,EAAA,UAAM,iGAGA,CAAA;KACN,oBAAC,QAAD;MACE,QAAO;MACP,QAAQ,aAAa,QAAQ,KAAK;MAClC,WAAS,aAAa,QAAQ,KAAK;MACnC,aAAU;MACV,cAAW;MAEX,UAAA,oBAAC,QAAD;OAAQ,SAAQ;OAAS,MAAK;OAAS,UAAA;MAE/B,CAAA;KACJ,CAAA;IACF;GACJ,CAAA,IAAA;EACD;;AAET"}
@@ -8,9 +8,10 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
8
8
  import { Table } from "../ui/table.mjs";
9
9
  import { csrfField } from "../ui/csrf-field.mjs";
10
10
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
11
- //#region src/pages/settings-labels.tsx
11
+ // src/pages/settings-labels.tsx
12
12
  /** Labels settings tab: label-type table plus the create-type form. */
13
13
  function renderSettingsLabels(project, labelTypes, isAdmin, formState) {
14
+ const editing = isAdmin ? labelTypes.find((item) => item.key === formState?.editKey) : void 0;
14
15
  return /* @__PURE__ */ jsxs("div", {
15
16
  class: "grid max-w-form",
16
17
  children: [
@@ -39,7 +40,13 @@ function renderSettingsLabels(project, labelTypes, isAdmin, formState) {
39
40
  class: "truncate max-w-cell",
40
41
  children: labelType.linkTemplate ?? "—"
41
42
  }),
42
- /* @__PURE__ */ jsx("td", { children: isAdmin && labelType.key !== "persistent" && labelType.key !== "branch" ? /* @__PURE__ */ jsxs("form", {
43
+ /* @__PURE__ */ jsxs("td", { children: [isAdmin && labelType.key !== "persistent" ? /* @__PURE__ */ jsx(Button, {
44
+ variant: "ghost",
45
+ size: "sm",
46
+ href: `/projects/${project.slug}/settings/labels?edit=${labelType.key}`,
47
+ "aria-label": `Edit ${labelType.key}`,
48
+ children: "Edit"
49
+ }) : null, isAdmin && labelType.key !== "persistent" && labelType.key !== "branch" ? /* @__PURE__ */ jsxs("form", {
43
50
  method: "post",
44
51
  action: `/projects/${project.slug}/settings/labels/${labelType.key}/delete`,
45
52
  "hx-post": `/projects/${project.slug}/settings/labels/${labelType.key}/delete`,
@@ -54,10 +61,44 @@ function renderSettingsLabels(project, labelTypes, isAdmin, formState) {
54
61
  }) : /* @__PURE__ */ jsx(Meta, {
55
62
  as: "span",
56
63
  children: "built-in"
57
- }) })
64
+ })] })
58
65
  ] }, labelType.id)) })] }) }),
59
66
  labelTypes.length === 0 ? /* @__PURE__ */ jsx(Meta, { children: "No label types configured." }) : null
60
67
  ] }),
68
+ editing ? /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(SectionTitle, {
69
+ level: 3,
70
+ children: ["Edit label type: ", editing.key]
71
+ }), /* @__PURE__ */ jsxs("form", {
72
+ method: "post",
73
+ action: `/projects/${project.slug}/settings/labels/${editing.key}`,
74
+ "hx-post": `/projects/${project.slug}/settings/labels/${editing.key}`,
75
+ "hx-target": "body",
76
+ children: [
77
+ csrfField(),
78
+ /* @__PURE__ */ jsx(Field, {
79
+ label: "Name",
80
+ name: "labelName",
81
+ required: true,
82
+ value: editing.name
83
+ }),
84
+ /* @__PURE__ */ jsx(Field, {
85
+ label: "Link template",
86
+ name: "linkTemplate",
87
+ value: editing.linkTemplate ?? "",
88
+ placeholder: "https://jira.example.com/browse/{value}",
89
+ hint: "Placeholders: {value}, {repo}, {branch}. Leave empty for no link."
90
+ }),
91
+ /* @__PURE__ */ jsxs(FormActions, { children: [/* @__PURE__ */ jsx(Button, {
92
+ variant: "primary",
93
+ type: "submit",
94
+ children: "Save label type"
95
+ }), /* @__PURE__ */ jsx(Button, {
96
+ variant: "ghost",
97
+ href: `/projects/${project.slug}/settings/labels`,
98
+ children: "Cancel"
99
+ })] })
100
+ ]
101
+ })] }) : null,
61
102
  isAdmin ? /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(SectionTitle, {
62
103
  level: 3,
63
104
  children: "Create label type"
@@ -101,7 +142,6 @@ function renderSettingsLabels(project, labelTypes, isAdmin, formState) {
101
142
  ]
102
143
  });
103
144
  }
104
- //#endregion
105
145
  export { renderSettingsLabels };
106
146
 
107
147
  //# sourceMappingURL=settings-labels.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-labels.mjs","names":[],"sources":["../../src/pages/settings-labels.tsx"],"sourcesContent":["import type { LabelType } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n Field,\n FormActions,\n Meta,\n SectionTitle,\n Table,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/** Labels settings tab: label-type table plus the create-type form. */\nexport function renderSettingsLabels(\n project: Project,\n labelTypes: LabelType[],\n isAdmin: boolean,\n formState?: { globalError?: string },\n): unknown {\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <Card>\n <SectionTitle>Label types</SectionTitle>\n <Meta>\n Labels attach typed values to builds (e.g. pr=123, jira=ABC-123). Values link out via the\n template.\n </Meta>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Key</th>\n <th>Name</th>\n <th>Template</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {labelTypes.map((labelType): HtmlEscapedString | Promise<HtmlEscapedString> => (\n <tr key={labelType.id}>\n <td>\n <Badge tone=\"neutral\">{labelType.key}</Badge>\n </td>\n <td>{labelType.name}</td>\n <td class=\"truncate max-w-cell\">{labelType.linkTemplate ?? \"—\"}</td>\n <td>\n {isAdmin && labelType.key !== \"persistent\" && labelType.key !== \"branch\" ? (\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`}\n hx-post={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Delete this label type?\"\n >\n {csrfField()}\n <Button\n variant=\"ghost\"\n type=\"submit\"\n aria-label={`Delete ${labelType.key}`}\n >\n Delete\n </Button>\n </form>\n ) : (\n <Meta as=\"span\">built-in</Meta>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Table>\n {labelTypes.length === 0 ? <Meta>No label types configured.</Meta> : null}\n </Card>\n\n {isAdmin ? (\n <Card>\n <SectionTitle level={3}>Create label type</SectionTitle>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/labels`}\n hx-post={`/projects/${project.slug}/settings/labels`}\n hx-target=\"body\"\n >\n {csrfField()}\n <div class=\"grid grid--2\">\n <Field\n label=\"Key\"\n name=\"key\"\n required\n placeholder=\"jira\"\n pattern=\"^[a-z0-9_-]+$\"\n hint=\"Lowercase, no spaces.\"\n />\n <Field label=\"Name\" name=\"labelName\" required placeholder=\"Jira issue\" />\n </div>\n <Field\n label=\"Link template\"\n name=\"linkTemplate\"\n placeholder=\"https://jira.example.com/browse/{value}\"\n hint=\"Use {value} placeholder. Optional.\"\n />\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"plus\">\n Add label type\n </Button>\n </FormActions>\n </form>\n </Card>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAgB,qBACd,SACA,YACA,SACA,WACS;CACT,OACE,qBAAC,OAAD;EAAK,OAAM;EAAX,UAAA;GACG,WAAW,cAAc,oBAAC,OAAD;IAAO,MAAK;IAAU,UAAA,UAAU;GAAmB,CAAA,IAAI;GACjF,qBAAC,MAAD,EAAA,UAAA;IACE,oBAAC,cAAD,EAAA,UAAc,cAAyB,CAAA;IACvC,oBAAC,MAAD,EAAA,UAAM,sGAGA,CAAA;IACN,oBAAC,OAAD,EAAA,UACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,SAAD,EAAA,UACE,qBAAC,MAAD,EAAA,UAAA;KACE,oBAAC,MAAD,EAAA,UAAI,MAAO,CAAA;KACX,oBAAC,MAAD,EAAA,UAAI,OAAQ,CAAA;KACZ,oBAAC,MAAD,EAAA,UAAI,WAAY,CAAA;KAChB,oBAAC,MAAD,EAAA,UACE,oBAAC,QAAD;MAAM,OAAM;MAAkB,UAAA;KAAa,CAAA,EACzC,CAAA;IACF,EAAA,CAAA,EACC,CAAA,GACP,oBAAC,SAAD,EAAA,UACG,WAAW,KAAK,cACf,qBAAC,MAAD,EAAA,UAAA;KACE,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;MAAO,MAAK;MAAW,UAAA,UAAU;KAAW,CAAA,EAC1C,CAAA;KACJ,oBAAC,MAAD,EAAA,UAAK,UAAU,KAAS,CAAA;KACxB,oBAAC,MAAD;MAAI,OAAM;MAAuB,UAAA,UAAU,gBAAgB;KAAQ,CAAA;KACnE,oBAAC,MAAD,EAAA,UACG,WAAW,UAAU,QAAQ,gBAAgB,UAAU,QAAQ,WAC9D,qBAAC,QAAD;MACE,QAAO;MACP,QAAQ,aAAa,QAAQ,KAAK,mBAAmB,UAAU,IAAI;MACnE,WAAS,aAAa,QAAQ,KAAK,mBAAmB,UAAU,IAAI;MACpE,aAAU;MACV,cAAW;MALb,UAAA,CAOG,UAAU,GACX,oBAAC,QAAD;OACE,SAAQ;OACR,MAAK;OACL,cAAY,UAAU,UAAU;OACjC,UAAA;MAEO,CAAA,CACJ;KAEN,CAAA,IAAA,oBAAC,MAAD;MAAM,IAAG;MAAO,UAAA;KAAc,CAAA,EAE9B,CAAA;IACF,EAAA,GA5BK,UAAU,EA4Bf,CACL,EACI,CAAA,CACF,EAAA,CAAA,EACF,CAAA;IACN,WAAW,WAAW,IAAI,oBAAC,MAAD,EAAA,UAAM,6BAAgC,CAAA,IAAI;GACjE,EAAA,CAAA;GAEL,UACC,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,cAAD;IAAc,OAAO;IAAG,UAAA;GAA+B,CAAA,GACvD,qBAAC,QAAD;IACE,QAAO;IACP,QAAQ,aAAa,QAAQ,KAAK;IAClC,WAAS,aAAa,QAAQ,KAAK;IACnC,aAAU;IAJZ,UAAA;KAMG,UAAU;KACX,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,oBAAC,OAAD;OACE,OAAM;OACN,MAAK;OACL,UAAA;OACA,aAAY;OACZ,SAAQ;OACR,MAAK;MACN,CAAA,GACD,oBAAC,OAAD;OAAO,OAAM;OAAO,MAAK;OAAY,UAAA;OAAS,aAAY;MAAc,CAAA,CACrE;;KACL,oBAAC,OAAD;MACE,OAAM;MACN,MAAK;MACL,aAAY;MACZ,MAAK;KACN,CAAA;KACD,oBAAC,aAAD,EAAA,UACE,oBAAC,QAAD;MAAQ,SAAQ;MAAU,MAAK;MAAS,MAAK;MAAO,UAAA;KAE5C,CAAA,EACG,CAAA;IACT;GACF,CAAA,CAAA,EAAA,CAAA,IACJ;EACD;;AAET"}
1
+ {"version":3,"file":"settings-labels.mjs","names":[],"sources":["../../src/pages/settings-labels.tsx"],"sourcesContent":["import type { LabelType } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n Field,\n FormActions,\n Meta,\n SectionTitle,\n Table,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/** Labels settings tab: label-type table plus the create-type form. */\nexport function renderSettingsLabels(\n project: Project,\n labelTypes: LabelType[],\n isAdmin: boolean,\n formState?: { globalError?: string; editKey?: string },\n): unknown {\n const editing = isAdmin ? labelTypes.find((item) => item.key === formState?.editKey) : undefined;\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <Card>\n <SectionTitle>Label types</SectionTitle>\n <Meta>\n Labels attach typed values to builds (e.g. pr=123, jira=ABC-123). Values link out via the\n template.\n </Meta>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Key</th>\n <th>Name</th>\n <th>Template</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {labelTypes.map((labelType): HtmlEscapedString | Promise<HtmlEscapedString> => (\n <tr key={labelType.id}>\n <td>\n <Badge tone=\"neutral\">{labelType.key}</Badge>\n </td>\n <td>{labelType.name}</td>\n <td class=\"truncate max-w-cell\">{labelType.linkTemplate ?? \"—\"}</td>\n <td>\n {isAdmin && labelType.key !== \"persistent\" ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n href={`/projects/${project.slug}/settings/labels?edit=${labelType.key}`}\n aria-label={`Edit ${labelType.key}`}\n >\n Edit\n </Button>\n ) : null}\n {isAdmin && labelType.key !== \"persistent\" && labelType.key !== \"branch\" ? (\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`}\n hx-post={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Delete this label type?\"\n >\n {csrfField()}\n <Button\n variant=\"ghost\"\n type=\"submit\"\n aria-label={`Delete ${labelType.key}`}\n >\n Delete\n </Button>\n </form>\n ) : (\n <Meta as=\"span\">built-in</Meta>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Table>\n {labelTypes.length === 0 ? <Meta>No label types configured.</Meta> : null}\n </Card>\n\n {editing ? (\n <Card>\n <SectionTitle level={3}>Edit label type: {editing.key}</SectionTitle>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/labels/${editing.key}`}\n hx-post={`/projects/${project.slug}/settings/labels/${editing.key}`}\n hx-target=\"body\"\n >\n {csrfField()}\n <Field label=\"Name\" name=\"labelName\" required value={editing.name} />\n <Field\n label=\"Link template\"\n name=\"linkTemplate\"\n value={editing.linkTemplate ?? \"\"}\n placeholder=\"https://jira.example.com/browse/{value}\"\n hint=\"Placeholders: {value}, {repo}, {branch}. Leave empty for no link.\"\n />\n <FormActions>\n <Button variant=\"primary\" type=\"submit\">\n Save label type\n </Button>\n <Button variant=\"ghost\" href={`/projects/${project.slug}/settings/labels`}>\n Cancel\n </Button>\n </FormActions>\n </form>\n </Card>\n ) : null}\n\n {isAdmin ? (\n <Card>\n <SectionTitle level={3}>Create label type</SectionTitle>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings/labels`}\n hx-post={`/projects/${project.slug}/settings/labels`}\n hx-target=\"body\"\n >\n {csrfField()}\n <div class=\"grid grid--2\">\n <Field\n label=\"Key\"\n name=\"key\"\n required\n placeholder=\"jira\"\n pattern=\"^[a-z0-9_-]+$\"\n hint=\"Lowercase, no spaces.\"\n />\n <Field label=\"Name\" name=\"labelName\" required placeholder=\"Jira issue\" />\n </div>\n <Field\n label=\"Link template\"\n name=\"linkTemplate\"\n placeholder=\"https://jira.example.com/browse/{value}\"\n hint=\"Use {value} placeholder. Optional.\"\n />\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"plus\">\n Add label type\n </Button>\n </FormActions>\n </form>\n </Card>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Button } from "../ui/buttons.mjs";
5
+ import { Alert, Badge, Meta } from "../ui/feedback.mjs";
6
+ import { Field, FormActions, SelectField } from "../ui/forms.mjs";
7
+ import { Card, SectionTitle } from "../ui/layout.mjs";
8
+ import { Table } from "../ui/table.mjs";
9
+ import { csrfField } from "../ui/csrf-field.mjs";
10
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
11
+ // src/pages/settings-links.tsx
12
+ const linkRow = (base, link, isAdmin) => /* @__PURE__ */ jsxs("tr", { children: [
13
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Badge, {
14
+ tone: "neutral",
15
+ children: link.packageName
16
+ }) }),
17
+ /* @__PURE__ */ jsx("td", { children: link.upstreamName }),
18
+ /* @__PURE__ */ jsx("td", { children: isAdmin ? /* @__PURE__ */ jsxs("form", {
19
+ method: "post",
20
+ action: `${base}/delete`,
21
+ "hx-post": `${base}/delete`,
22
+ "hx-target": "body",
23
+ "hx-confirm": "Remove this link?",
24
+ children: [
25
+ csrfField(),
26
+ /* @__PURE__ */ jsx("input", {
27
+ type: "hidden",
28
+ name: "package",
29
+ value: link.packageName
30
+ }),
31
+ /* @__PURE__ */ jsx(Button, {
32
+ variant: "ghost",
33
+ type: "submit",
34
+ "aria-label": `Remove ${link.packageName}`,
35
+ children: "Remove"
36
+ })
37
+ ]
38
+ }) : null })
39
+ ] }, link.packageName);
40
+ /** Links settings tab: linked packages plus the add-link form. */
41
+ function renderSettingsLinks(project, links, candidates, isAdmin, formState) {
42
+ const base = `/projects/${project.slug}/settings/links`;
43
+ return /* @__PURE__ */ jsxs("div", {
44
+ class: "grid max-w-form",
45
+ children: [
46
+ formState?.globalError ? /* @__PURE__ */ jsx(Alert, {
47
+ tone: "danger",
48
+ children: formState.globalError
49
+ }) : null,
50
+ /* @__PURE__ */ jsxs(Card, { children: [
51
+ /* @__PURE__ */ jsx(SectionTitle, { children: "Linked packages" }),
52
+ /* @__PURE__ */ jsx(Meta, { children: "Link a package this project consumes (e.g. @acme/design-system) to the project that publishes it. Review pages then show where a story uses it, with links to the upstream component." }),
53
+ /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
54
+ /* @__PURE__ */ jsx("th", { children: "Package" }),
55
+ /* @__PURE__ */ jsx("th", { children: "Upstream project" }),
56
+ /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx("span", {
57
+ class: "visually-hidden",
58
+ children: "Actions"
59
+ }) })
60
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: links.map((link) => linkRow(base, link, isAdmin)) })] }) }),
61
+ links.length === 0 ? /* @__PURE__ */ jsx(Meta, { children: "No packages linked." }) : null
62
+ ] }),
63
+ isAdmin ? /* @__PURE__ */ jsx(AddLinkCard, {
64
+ base,
65
+ candidates
66
+ }) : null
67
+ ]
68
+ });
69
+ }
70
+ /** Form card linking a package to an upstream project. */
71
+ function AddLinkCard(props) {
72
+ const { base, candidates } = props;
73
+ return /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(SectionTitle, {
74
+ level: 3,
75
+ children: "Link a package"
76
+ }), /* @__PURE__ */ jsxs("form", {
77
+ method: "post",
78
+ action: base,
79
+ "hx-post": base,
80
+ "hx-target": "body",
81
+ children: [
82
+ csrfField(),
83
+ /* @__PURE__ */ jsxs("div", {
84
+ class: "grid grid--2",
85
+ children: [/* @__PURE__ */ jsx(Field, {
86
+ label: "Package",
87
+ name: "package",
88
+ required: true,
89
+ placeholder: "@acme/design-system"
90
+ }), /* @__PURE__ */ jsx(SelectField, {
91
+ label: "Upstream project",
92
+ name: "upstreamSlug",
93
+ required: true,
94
+ options: candidates.map((candidate) => ({
95
+ value: candidate.slug,
96
+ label: candidate.name
97
+ }))
98
+ })]
99
+ }),
100
+ /* @__PURE__ */ jsx(FormActions, { children: /* @__PURE__ */ jsx(Button, {
101
+ variant: "primary",
102
+ type: "submit",
103
+ icon: "plus",
104
+ children: "Link package"
105
+ }) })
106
+ ]
107
+ })] });
108
+ }
109
+ export { renderSettingsLinks };
110
+
111
+ //# sourceMappingURL=settings-links.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-links.mjs","names":[],"sources":["../../src/pages/settings-links.tsx"],"sourcesContent":["import type { Project } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n Field,\n FormActions,\n Meta,\n SectionTitle,\n SelectField,\n Table,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** A package linked to the upstream project that publishes it. */\nexport interface SettingsLink {\n packageName: string;\n upstreamSlug: string;\n upstreamName: string;\n}\n\n/** Another project that can be chosen as an upstream. */\nexport interface SettingsLinkCandidate {\n slug: string;\n name: string;\n}\n\nconst linkRow = (\n base: string,\n link: SettingsLink,\n isAdmin: boolean,\n): HtmlEscapedString | Promise<HtmlEscapedString> => (\n <tr key={link.packageName}>\n <td>\n <Badge tone=\"neutral\">{link.packageName}</Badge>\n </td>\n <td>{link.upstreamName}</td>\n <td>\n {isAdmin ? (\n <form\n method=\"post\"\n action={`${base}/delete`}\n hx-post={`${base}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Remove this link?\"\n >\n {csrfField()}\n <input type=\"hidden\" name=\"package\" value={link.packageName} />\n <Button variant=\"ghost\" type=\"submit\" aria-label={`Remove ${link.packageName}`}>\n Remove\n </Button>\n </form>\n ) : null}\n </td>\n </tr>\n);\n\n/** Links settings tab: linked packages plus the add-link form. */\nexport function renderSettingsLinks(\n project: Project,\n links: SettingsLink[],\n candidates: SettingsLinkCandidate[],\n isAdmin: boolean,\n formState?: { globalError?: string },\n): unknown {\n const base = `/projects/${project.slug}/settings/links`;\n return (\n <div class=\"grid max-w-form\">\n {formState?.globalError ? <Alert tone=\"danger\">{formState.globalError}</Alert> : null}\n <Card>\n <SectionTitle>Linked packages</SectionTitle>\n <Meta>\n Link a package this project consumes (e.g. @acme/design-system) to the project that\n publishes it. Review pages then show where a story uses it, with links to the upstream\n component.\n </Meta>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Package</th>\n <th>Upstream project</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>{links.map((link) => linkRow(base, link, isAdmin))}</tbody>\n </table>\n </Table>\n {links.length === 0 ? <Meta>No packages linked.</Meta> : null}\n </Card>\n {isAdmin ? <AddLinkCard base={base} candidates={candidates} /> : null}\n </div>\n );\n}\n\n/** Form card linking a package to an upstream project. */\nfunction AddLinkCard(props: {\n base: string;\n candidates: SettingsLinkCandidate[];\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { base, candidates } = props;\n return (\n <Card>\n <SectionTitle level={3}>Link a package</SectionTitle>\n <form method=\"post\" action={base} hx-post={base} hx-target=\"body\">\n {csrfField()}\n <div class=\"grid grid--2\">\n <Field label=\"Package\" name=\"package\" required placeholder=\"@acme/design-system\" />\n <SelectField\n label=\"Upstream project\"\n name=\"upstreamSlug\"\n required\n options={candidates.map((candidate) => ({\n value: candidate.slug,\n label: candidate.name,\n }))}\n />\n </div>\n <FormActions>\n <Button variant=\"primary\" type=\"submit\" icon=\"plus\">\n Link package\n </Button>\n </FormActions>\n </form>\n </Card>\n );\n}\n"],"mappings":";;;;;;;;;;;AA+BA,MAAM,WACJ,MACA,MACA,YAEA,qBAAC,MAAD,EAAA,UAAA;CACE,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;EAAO,MAAK;EAAW,UAAA,KAAK;CAAmB,CAAA,EAC7C,CAAA;CACJ,oBAAC,MAAD,EAAA,UAAK,KAAK,aAAiB,CAAA;CAC3B,oBAAC,MAAD,EAAA,UACG,UACC,qBAAC,QAAD;EACE,QAAO;EACP,QAAQ,GAAG,KAAK;EAChB,WAAS,GAAG,KAAK;EACjB,aAAU;EACV,cAAW;EALb,UAAA;GAOG,UAAU;GACX,oBAAC,SAAD;IAAO,MAAK;IAAS,MAAK;IAAU,OAAO,KAAK;GAAc,CAAA;GAC9D,oBAAC,QAAD;IAAQ,SAAQ;IAAQ,MAAK;IAAS,cAAY,UAAU,KAAK;IAAe,UAAA;GAExE,CAAA;EACJ;CACJ,CAAA,IAAA,KACF,CAAA;AACF,EAAA,GAtBK,KAAK,WAsBV;;AAIN,SAAgB,oBACd,SACA,OACA,YACA,SACA,WACS;CACT,MAAM,OAAO,aAAa,QAAQ,KAAK;CACvC,OACE,qBAAC,OAAD;EAAK,OAAM;EAAX,UAAA;GACG,WAAW,cAAc,oBAAC,OAAD;IAAO,MAAK;IAAU,UAAA,UAAU;GAAmB,CAAA,IAAI;GACjF,qBAAC,MAAD,EAAA,UAAA;IACE,oBAAC,cAAD,EAAA,UAAc,kBAA6B,CAAA;IAC3C,oBAAC,MAAD,EAAA,UAAM,wLAIA,CAAA;IACN,oBAAC,OAAD,EAAA,UACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,SAAD,EAAA,UACE,qBAAC,MAAD,EAAA,UAAA;KACE,oBAAC,MAAD,EAAA,UAAI,UAAW,CAAA;KACf,oBAAC,MAAD,EAAA,UAAI,mBAAoB,CAAA;KACxB,oBAAC,MAAD,EAAA,UACE,oBAAC,QAAD;MAAM,OAAM;MAAkB,UAAA;KAAa,CAAA,EACzC,CAAA;IACF,EAAA,CAAA,EACC,CAAA,GACP,oBAAC,SAAD,EAAA,UAAQ,MAAM,KAAK,SAAS,QAAQ,MAAM,MAAM,OAAO,CAAC,EAAS,CAAA,CAC5D,EAAA,CAAA,EACF,CAAA;IACN,MAAM,WAAW,IAAI,oBAAC,MAAD,EAAA,UAAM,sBAAyB,CAAA,IAAI;GACrD,EAAA,CAAA;GACL,UAAU,oBAAC,aAAD;IAAmB;IAAkB;GAAa,CAAA,IAAI;EAC9D;;AAET;;AAGA,SAAS,YAAY,OAG8B;CACjD,MAAM,EAAE,MAAM,eAAe;CAC7B,OACE,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,cAAD;EAAc,OAAO;EAAG,UAAA;CAA4B,CAAA,GACpD,qBAAC,QAAD;EAAM,QAAO;EAAO,QAAQ;EAAM,WAAS;EAAM,aAAU;EAA3D,UAAA;GACG,UAAU;GACX,qBAAC,OAAD;IAAK,OAAM;IAAX,UAAA,CACE,oBAAC,OAAD;KAAO,OAAM;KAAU,MAAK;KAAU,UAAA;KAAS,aAAY;IAAuB,CAAA,GAClF,oBAAC,aAAD;KACE,OAAM;KACN,MAAK;KACL,UAAA;KACA,SAAS,WAAW,KAAK,eAAe;MACtC,OAAO,UAAU;MACjB,OAAO,UAAU;KACnB,EAAE;IACH,CAAA,CACE;;GACL,oBAAC,aAAD,EAAA,UACE,oBAAC,QAAD;IAAQ,SAAQ;IAAU,MAAK;IAAS,MAAK;IAAO,UAAA;GAE5C,CAAA,EACG,CAAA;EACT;CACF,CAAA,CAAA,EAAA,CAAA;AAEV"}
@@ -8,7 +8,7 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
8
8
  import { Table } from "../ui/table.mjs";
9
9
  import { csrfField } from "../ui/csrf-field.mjs";
10
10
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
11
- //#region src/pages/settings-members.tsx
11
+ // src/pages/settings-members.tsx
12
12
  /** Members settings tab: member roster plus the add-member form. */
13
13
  function renderSettingsMembers(project, members, groupMappings, isAdmin, formState) {
14
14
  return /* @__PURE__ */ jsxs("div", {
@@ -186,7 +186,6 @@ function renderSettingsMembers(project, members, groupMappings, isAdmin, formSta
186
186
  ]
187
187
  });
188
188
  }
189
- //#endregion
190
189
  export { renderSettingsMembers };
191
190
 
192
191
  //# sourceMappingURL=settings-members.mjs.map
@@ -0,0 +1,127 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Button } from "../ui/buttons.mjs";
5
+ import { Alert } from "../ui/feedback.mjs";
6
+ import { Field, FormActions, SelectField, TextareaField } from "../ui/forms.mjs";
7
+ import { Card, SectionTitle } from "../ui/layout.mjs";
8
+ import { csrfField } from "../ui/csrf-field.mjs";
9
+ import { isServerlessHost } from "@storyshelf/core/config";
10
+ import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
11
+ // src/pages/settings-notifications-git.tsx
12
+ /** GitHub only: show builds as check runs posted by a GitHub App, not as commit statuses. */
13
+ const GitHubAppFields = ({ errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
14
+ /* @__PURE__ */ jsx(SelectField, {
15
+ label: "GitHub mode",
16
+ name: "mode",
17
+ options: [{
18
+ value: "statuses",
19
+ label: "Commit statuses (token)"
20
+ }, {
21
+ value: "checks",
22
+ label: "Check runs (GitHub App)"
23
+ }],
24
+ hint: errors?.["mode"] ?? "GitHub only. Check runs list each changed snapshot and need a GitHub App installed on the repository; GitLab always uses statuses."
25
+ }),
26
+ /* @__PURE__ */ jsx(Field, {
27
+ label: "App ID",
28
+ name: "appId",
29
+ placeholder: "123456",
30
+ error: errors?.["appId"]
31
+ }),
32
+ /* @__PURE__ */ jsx(Field, {
33
+ label: "Installation ID",
34
+ name: "installationId",
35
+ placeholder: "7654321",
36
+ error: errors?.["installationId"]
37
+ }),
38
+ /* @__PURE__ */ jsx(TextareaField, {
39
+ label: "App private key",
40
+ name: "privateKey",
41
+ rows: 4,
42
+ placeholder: "-----BEGIN RSA PRIVATE KEY-----",
43
+ hint: "The PEM key generated for the App. It is stored encrypted and never shown again.",
44
+ error: errors?.["privateKey"]
45
+ })
46
+ ] });
47
+ /** The repository, prefix and token fields of the git channel form. */
48
+ const GitChannelFields = ({ errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
49
+ /* @__PURE__ */ jsx(Field, {
50
+ label: "Owner",
51
+ name: "owner",
52
+ required: true,
53
+ placeholder: "my-org",
54
+ hint: "Organization, user or group that owns the repository.",
55
+ error: errors?.["owner"]
56
+ }),
57
+ /* @__PURE__ */ jsx(Field, {
58
+ label: "Repository",
59
+ name: "repo",
60
+ required: true,
61
+ placeholder: "my-repo",
62
+ error: errors?.["repo"]
63
+ }),
64
+ /* @__PURE__ */ jsx(Field, {
65
+ label: "Host",
66
+ name: "host",
67
+ placeholder: "https://gitlab.example.com",
68
+ hint: "GitLab only: a self-managed host. Leave blank for gitlab.com.",
69
+ error: errors?.["host"]
70
+ }),
71
+ /* @__PURE__ */ jsx(Field, {
72
+ label: "Status prefix",
73
+ name: "prefix",
74
+ placeholder: "storyshelf",
75
+ hint: "Statuses (or check runs) are named <prefix>/testing and <prefix>/storybook; require <prefix>/testing in branch protection (`storyshelf protect` prints the rule). Give each project in a shared repository its own prefix.",
76
+ error: errors?.["prefix"]
77
+ }),
78
+ /* @__PURE__ */ jsx(Field, {
79
+ label: "Token",
80
+ name: "token",
81
+ type: "password",
82
+ placeholder: "ghp_…",
83
+ hint: "A token that can write commit statuses (GitHub: repo:status; GitLab: api). Not needed for GitHub checks.",
84
+ error: errors?.["token"]
85
+ }),
86
+ /* @__PURE__ */ jsx(GitHubAppFields, { errors })
87
+ ] });
88
+ /** Git channel creation card (admins only): repository, status prefix and token. */
89
+ const CreateGitChannelCard = ({ project, providers, formState }) => /* @__PURE__ */ jsxs(Card, { children: [
90
+ /* @__PURE__ */ jsx(SectionTitle, {
91
+ level: 3,
92
+ children: "Add a git channel"
93
+ }),
94
+ isServerlessHost() ? /* @__PURE__ */ jsx(Alert, {
95
+ tone: "info",
96
+ title: "Statuses are delivered inline",
97
+ children: "This host has no job consumer, so a build's state is written in the request that changes it, without retries. A failure shows on the channel and the next change writes the current state again."
98
+ }) : null,
99
+ /* @__PURE__ */ jsxs("form", {
100
+ method: "post",
101
+ action: `/projects/${project.slug}/settings/notifications`,
102
+ "hx-post": `/projects/${project.slug}/settings/notifications`,
103
+ "hx-target": "body",
104
+ children: [
105
+ csrfField(),
106
+ /* @__PURE__ */ jsx(SelectField, {
107
+ label: "Git provider",
108
+ name: "gitProvider",
109
+ options: providers.map((provider) => ({
110
+ value: provider.kind,
111
+ label: provider.name
112
+ })),
113
+ hint: formState?.errors?.["gitProvider"]
114
+ }),
115
+ /* @__PURE__ */ jsx(GitChannelFields, { errors: formState?.errors }),
116
+ /* @__PURE__ */ jsx(FormActions, { children: /* @__PURE__ */ jsx(Button, {
117
+ variant: "primary",
118
+ type: "submit",
119
+ icon: "plus",
120
+ children: "Add git channel"
121
+ }) })
122
+ ]
123
+ })
124
+ ] });
125
+ export { CreateGitChannelCard };
126
+
127
+ //# sourceMappingURL=settings-notifications-git.mjs.map