@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.
- package/README.md +42 -4
- package/dist/app-types.d.mts +2 -6
- package/dist/asset-manifest.mjs +14 -7
- package/dist/asset-manifest.mjs.map +1 -1
- package/dist/assets/{htmx.min.js → htmx.mjs} +9 -1
- package/dist/assets/htmx.mjs.map +1 -0
- package/dist/assets/mark.mjs +9 -0
- package/dist/assets/mark.mjs.map +1 -0
- package/dist/boot-tasks.mjs +79 -0
- package/dist/boot-tasks.mjs.map +1 -0
- package/dist/build-state-flags.mjs +17 -0
- package/dist/build-state-flags.mjs.map +1 -0
- package/dist/build-state-setup.mjs +74 -0
- package/dist/build-state-setup.mjs.map +1 -0
- package/dist/capture-alerts.mjs +45 -0
- package/dist/capture-alerts.mjs.map +1 -0
- package/dist/capture-process.mjs +58 -0
- package/dist/capture-process.mjs.map +1 -0
- package/dist/capture-setup.mjs +96 -119
- package/dist/capture-setup.mjs.map +1 -1
- package/dist/connections.mjs +33 -0
- package/dist/connections.mjs.map +1 -0
- package/dist/consumer-setup.mjs +57 -0
- package/dist/consumer-setup.mjs.map +1 -0
- package/dist/credential-keys.mjs +1 -2
- package/dist/credentials.mjs +1 -2
- package/dist/git-status.mjs +45 -0
- package/dist/git-status.mjs.map +1 -0
- package/dist/hidden-data.mjs +23 -0
- package/dist/hidden-data.mjs.map +1 -0
- package/dist/index.d.mts +3 -5
- package/dist/index.mjs +204 -127
- package/dist/index.mjs.map +1 -1
- package/dist/insights/budget.mjs +48 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/deps.mjs +20 -0
- package/dist/insights/deps.mjs.map +1 -0
- package/dist/insights/errors.mjs +21 -0
- package/dist/insights/errors.mjs.map +1 -0
- package/dist/insights/health-panel.mjs +39 -0
- package/dist/insights/health-panel.mjs.map +1 -0
- package/dist/insights/job.mjs +102 -0
- package/dist/insights/job.mjs.map +1 -0
- package/dist/insights/load-health.mjs +58 -0
- package/dist/insights/load-health.mjs.map +1 -0
- package/dist/insights/load-triage.mjs +97 -0
- package/dist/insights/load-triage.mjs.map +1 -0
- package/dist/insights/panel.mjs +27 -0
- package/dist/insights/panel.mjs.map +1 -0
- package/dist/insights/request.mjs +112 -0
- package/dist/insights/request.mjs.map +1 -0
- package/dist/insights/stale.mjs +20 -0
- package/dist/insights/stale.mjs.map +1 -0
- package/dist/insights/usage-report.mjs +59 -0
- package/dist/insights/usage-report.mjs.map +1 -0
- package/dist/insights/view.mjs +47 -0
- package/dist/insights/view.mjs.map +1 -0
- package/dist/job-canceller.mjs +13 -0
- package/dist/job-canceller.mjs.map +1 -0
- package/dist/job-queue.mjs +50 -0
- package/dist/job-queue.mjs.map +1 -0
- package/dist/jobs-setup.mjs +131 -0
- package/dist/jobs-setup.mjs.map +1 -0
- package/dist/lifecycle.mjs +93 -22
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +5 -4
- package/dist/middleware/auth-gate.mjs.map +1 -1
- package/dist/middleware/csrf.mjs +1 -2
- package/dist/middleware/rate-limit.mjs +42 -35
- package/dist/middleware/rate-limit.mjs.map +1 -1
- package/dist/middleware/request-log.mjs +4 -4
- package/dist/middleware/request-log.mjs.map +1 -1
- package/dist/middleware/require-review.mjs +50 -0
- package/dist/middleware/require-review.mjs.map +1 -0
- package/dist/middleware/require-testing.mjs +40 -0
- package/dist/middleware/require-testing.mjs.map +1 -0
- package/dist/middleware/server-timing.mjs +1 -2
- package/dist/middleware/setup-gate.mjs +1 -2
- package/dist/middleware/setup-gate.mjs.map +1 -1
- package/dist/middleware/store-scope.mjs +13 -4
- package/dist/middleware/store-scope.mjs.map +1 -1
- package/dist/notifications-pause.mjs +32 -0
- package/dist/notifications-pause.mjs.map +1 -0
- package/dist/notify.d.mts +3 -4
- package/dist/notify.mjs +79 -41
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +115 -0
- package/dist/pages/a11y-violations.mjs.map +1 -0
- package/dist/pages/admin-ai.mjs +77 -0
- package/dist/pages/admin-ai.mjs.map +1 -0
- package/dist/pages/admin-system-origin.mjs +132 -0
- package/dist/pages/admin-system-origin.mjs.map +1 -0
- package/dist/pages/admin-system-targets.mjs +52 -0
- package/dist/pages/admin-system-targets.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +64 -15
- package/dist/pages/admin-system.mjs.map +1 -1
- package/dist/pages/build-detail.mjs +105 -78
- package/dist/pages/build-detail.mjs.map +1 -1
- package/dist/pages/build-diff-actions.mjs +88 -35
- package/dist/pages/build-diff-actions.mjs.map +1 -1
- package/dist/pages/build-diff-comments.mjs +17 -16
- package/dist/pages/build-diff-comments.mjs.map +1 -1
- package/dist/pages/build-diff-default.mjs +53 -0
- package/dist/pages/build-diff-default.mjs.map +1 -0
- package/dist/pages/build-diff-environment.mjs +81 -0
- package/dist/pages/build-diff-environment.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +23 -6
- package/dist/pages/build-diff-header.mjs.map +1 -1
- package/dist/pages/build-diff-insight.mjs +112 -0
- package/dist/pages/build-diff-insight.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +74 -12
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +29 -14
- package/dist/pages/build-diff-nav.mjs.map +1 -1
- package/dist/pages/build-diff-reference.mjs +84 -0
- package/dist/pages/build-diff-reference.mjs.map +1 -0
- package/dist/pages/build-diff-shortcuts.mjs +6 -3
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
- package/dist/pages/build-diff-stale.mjs +1 -2
- package/dist/pages/build-diff-usage.mjs +62 -0
- package/dist/pages/build-diff-usage.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +97 -14
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +37 -22
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +73 -0
- package/dist/pages/build-tests.mjs.map +1 -0
- package/dist/pages/component-detail.mjs +248 -0
- package/dist/pages/component-detail.mjs.map +1 -0
- package/dist/pages/component-stories.mjs +167 -0
- package/dist/pages/component-stories.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +14 -28
- package/dist/pages/compute-jobs.mjs.map +1 -1
- package/dist/pages/design-links.mjs +181 -0
- package/dist/pages/design-links.mjs.map +1 -0
- package/dist/pages/environment-change.mjs +52 -0
- package/dist/pages/environment-change.mjs.map +1 -0
- package/dist/pages/health-panel.mjs +125 -0
- package/dist/pages/health-panel.mjs.map +1 -0
- package/dist/pages/invite.mjs +1 -2
- package/dist/pages/label-detail.mjs +1 -2
- package/dist/pages/library-grid.mjs +104 -0
- package/dist/pages/library-grid.mjs.map +1 -0
- package/dist/pages/library-model.mjs +46 -0
- package/dist/pages/library-model.mjs.map +1 -0
- package/dist/pages/library-snapshots.mjs +129 -0
- package/dist/pages/library-snapshots.mjs.map +1 -0
- package/dist/pages/library.mjs +113 -220
- package/dist/pages/library.mjs.map +1 -1
- package/dist/pages/login.mjs +1 -2
- package/dist/pages/notifications-paused.mjs +21 -0
- package/dist/pages/notifications-paused.mjs.map +1 -0
- package/dist/pages/passkey-ceremony.mjs +1 -2
- package/dist/pages/profile.mjs +54 -50
- package/dist/pages/profile.mjs.map +1 -1
- package/dist/pages/project-builds-table.mjs +3 -4
- package/dist/pages/project-builds-table.mjs.map +1 -1
- package/dist/pages/project-builds.mjs +52 -23
- package/dist/pages/project-builds.mjs.map +1 -1
- package/dist/pages/project-create.mjs +1 -2
- package/dist/pages/project-settings.mjs +52 -14
- package/dist/pages/project-settings.mjs.map +1 -1
- package/dist/pages/projects-card.mjs +15 -3
- package/dist/pages/projects-card.mjs.map +1 -1
- package/dist/pages/projects.mjs +7 -4
- package/dist/pages/projects.mjs.map +1 -1
- package/dist/pages/review-changeset.mjs +115 -0
- package/dist/pages/review-changeset.mjs.map +1 -0
- package/dist/pages/review-comments.mjs +102 -0
- package/dist/pages/review-comments.mjs.map +1 -0
- package/dist/pages/review-verdict.mjs +101 -0
- package/dist/pages/review-verdict.mjs.map +1 -0
- package/dist/pages/root.mjs +1 -2
- package/dist/pages/settings-ai.mjs +68 -0
- package/dist/pages/settings-ai.mjs.map +1 -0
- package/dist/pages/settings-capture.mjs +123 -0
- package/dist/pages/settings-capture.mjs.map +1 -0
- package/dist/pages/settings-design-credential.mjs +98 -0
- package/dist/pages/settings-design-credential.mjs.map +1 -0
- package/dist/pages/settings-design-links.mjs +178 -0
- package/dist/pages/settings-design-links.mjs.map +1 -0
- package/dist/pages/settings-design.mjs +47 -0
- package/dist/pages/settings-design.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +47 -23
- package/dist/pages/settings-general.mjs.map +1 -1
- package/dist/pages/settings-labels.mjs +44 -4
- package/dist/pages/settings-labels.mjs.map +1 -1
- package/dist/pages/settings-links.mjs +111 -0
- package/dist/pages/settings-links.mjs.map +1 -0
- package/dist/pages/settings-members.mjs +1 -2
- package/dist/pages/settings-notifications-git.mjs +127 -0
- package/dist/pages/settings-notifications-git.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +80 -44
- package/dist/pages/settings-notifications.mjs.map +1 -1
- package/dist/pages/settings-tests.mjs +34 -4
- package/dist/pages/settings-tests.mjs.map +1 -1
- package/dist/pages/settings-tokens.mjs +1 -2
- package/dist/pages/settings-webhooks.mjs +1 -2
- package/dist/pages/storybook.mjs +1 -2
- package/dist/pages/target-choices.mjs +38 -0
- package/dist/pages/target-choices.mjs.map +1 -0
- package/dist/publish-setup.mjs +88 -0
- package/dist/publish-setup.mjs.map +1 -0
- package/dist/retention-timer.mjs +12 -12
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +47 -0
- package/dist/review-events.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +46 -5
- package/dist/routers/admin-pages.mjs.map +1 -1
- package/dist/routers/admin-public-origin.mjs +92 -0
- package/dist/routers/admin-public-origin.mjs.map +1 -0
- package/dist/routers/admin.mjs +8 -4
- package/dist/routers/admin.mjs.map +1 -1
- package/dist/routers/assets.mjs +10 -4
- package/dist/routers/assets.mjs.map +1 -1
- package/dist/routers/attempts.mjs +1 -2
- package/dist/routers/auth-sync.mjs +1 -2
- package/dist/routers/auth.mjs +2 -3
- package/dist/routers/baseline-guard.mjs +4 -5
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baseline-reference.mjs +72 -0
- package/dist/routers/baseline-reference.mjs.map +1 -0
- package/dist/routers/baselines.handlers.mjs +63 -0
- package/dist/routers/baselines.handlers.mjs.map +1 -0
- package/dist/routers/baselines.mjs +124 -0
- package/dist/routers/baselines.mjs.map +1 -0
- package/dist/routers/build-cancel.mjs +67 -0
- package/dist/routers/build-cancel.mjs.map +1 -0
- package/dist/routers/build-changeset.mjs +69 -0
- package/dist/routers/build-changeset.mjs.map +1 -0
- package/dist/routers/build-reviews.handlers.mjs +85 -0
- package/dist/routers/build-reviews.handlers.mjs.map +1 -0
- package/dist/routers/build-reviews.mjs +73 -0
- package/dist/routers/build-reviews.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +65 -60
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +77 -19
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-cache.mjs +29 -0
- package/dist/routers/capture-cache.mjs.map +1 -0
- package/dist/routers/capture-config.mjs +34 -0
- package/dist/routers/capture-config.mjs.map +1 -0
- package/dist/routers/comments.mjs +42 -17
- package/dist/routers/comments.mjs.map +1 -1
- package/dist/routers/comparison-image.mjs +29 -0
- package/dist/routers/comparison-image.mjs.map +1 -0
- package/dist/routers/component-catalog.handlers.mjs +70 -0
- package/dist/routers/component-catalog.handlers.mjs.map +1 -0
- package/dist/routers/component.handlers.mjs +120 -0
- package/dist/routers/component.handlers.mjs.map +1 -0
- package/dist/routers/connections.mjs +117 -0
- package/dist/routers/connections.mjs.map +1 -0
- package/dist/routers/design-connect.handlers.mjs +43 -0
- package/dist/routers/design-connect.handlers.mjs.map +1 -0
- package/dist/routers/design.handlers.mjs +113 -0
- package/dist/routers/design.handlers.mjs.map +1 -0
- package/dist/routers/flash.mjs +1 -2
- package/dist/routers/health-report.mjs +3 -5
- package/dist/routers/health-report.mjs.map +1 -1
- package/dist/routers/health.mjs +5 -5
- package/dist/routers/health.mjs.map +1 -1
- package/dist/routers/helpers.mjs +20 -5
- package/dist/routers/helpers.mjs.map +1 -1
- package/dist/routers/htmx.mjs +1 -2
- package/dist/routers/insights-ui.mjs +63 -0
- package/dist/routers/insights-ui.mjs.map +1 -0
- package/dist/routers/insights.handlers.mjs +44 -0
- package/dist/routers/insights.handlers.mjs.map +1 -0
- package/dist/routers/insights.mjs +188 -0
- package/dist/routers/insights.mjs.map +1 -0
- package/dist/routers/labels.mjs +1 -2
- package/dist/routers/links.handlers.mjs +79 -0
- package/dist/routers/links.handlers.mjs.map +1 -0
- package/dist/routers/links.mjs +138 -0
- package/dist/routers/links.mjs.map +1 -0
- package/dist/routers/media.mjs +30 -7
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +3 -3
- package/dist/routers/members.mjs.map +1 -1
- package/dist/routers/notification-channels.mjs +18 -25
- package/dist/routers/notification-channels.mjs.map +1 -1
- package/dist/routers/notification-subscriptions.mjs +1 -2
- package/dist/routers/oauth-state.mjs +44 -0
- package/dist/routers/oauth-state.mjs.map +1 -0
- package/dist/routers/preview-base.mjs +37 -0
- package/dist/routers/preview-base.mjs.map +1 -0
- package/dist/routers/preview-token.mjs +50 -0
- package/dist/routers/preview-token.mjs.map +1 -0
- package/dist/routers/profile.mjs +4 -3
- package/dist/routers/profile.mjs.map +1 -1
- package/dist/routers/projects.mjs +18 -3
- package/dist/routers/projects.mjs.map +1 -1
- package/dist/routers/publish-upload.mjs +85 -0
- package/dist/routers/publish-upload.mjs.map +1 -0
- package/dist/routers/published-access.mjs +51 -0
- package/dist/routers/published-access.mjs.map +1 -0
- package/dist/routers/published-compression.mjs +109 -0
- package/dist/routers/published-compression.mjs.map +1 -0
- package/dist/routers/published-files.mjs +69 -0
- package/dist/routers/published-files.mjs.map +1 -0
- package/dist/routers/review-forms.mjs +71 -0
- package/dist/routers/review-forms.mjs.map +1 -0
- package/dist/routers/review-next.mjs +1 -2
- package/dist/routers/review-section.handlers.mjs +84 -0
- package/dist/routers/review-section.handlers.mjs.map +1 -0
- package/dist/routers/review-switch-off.mjs +28 -0
- package/dist/routers/review-switch-off.mjs.map +1 -0
- package/dist/routers/schemas.mjs +141 -24
- package/dist/routers/schemas.mjs.map +1 -1
- package/dist/routers/scoped-models.mjs +21 -0
- package/dist/routers/scoped-models.mjs.map +1 -0
- package/dist/routers/settings-ai.mjs +34 -0
- package/dist/routers/settings-ai.mjs.map +1 -0
- package/dist/routers/settings-capture.mjs +76 -0
- package/dist/routers/settings-capture.mjs.map +1 -0
- package/dist/routers/settings-design.mjs +102 -0
- package/dist/routers/settings-design.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +56 -12
- package/dist/routers/settings-general.mjs.map +1 -1
- package/dist/routers/settings-git-channel.mjs +76 -0
- package/dist/routers/settings-git-channel.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +28 -3
- package/dist/routers/settings-labels.mjs.map +1 -1
- package/dist/routers/settings-links.mjs +62 -0
- package/dist/routers/settings-links.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +5 -9
- package/dist/routers/settings-members.mjs.map +1 -1
- package/dist/routers/settings-notifications.mjs +16 -12
- package/dist/routers/settings-notifications.mjs.map +1 -1
- package/dist/routers/settings-tokens.mjs +3 -7
- package/dist/routers/settings-tokens.mjs.map +1 -1
- package/dist/routers/settings-webhooks.mjs +8 -26
- package/dist/routers/settings-webhooks.mjs.map +1 -1
- package/dist/routers/settings.handlers.mjs +108 -28
- package/dist/routers/settings.handlers.mjs.map +1 -1
- package/dist/routers/settings.mjs +17 -6
- package/dist/routers/settings.mjs.map +1 -1
- package/dist/routers/signed-storybook.mjs +55 -0
- package/dist/routers/signed-storybook.mjs.map +1 -0
- package/dist/routers/snapshot-groups.mjs +62 -0
- package/dist/routers/snapshot-groups.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +104 -7
- package/dist/routers/snapshots.mjs.map +1 -1
- package/dist/routers/storybook.mjs +57 -104
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +3 -7
- package/dist/routers/tokens.mjs.map +1 -1
- package/dist/routers/ui.mjs +38 -7
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +6 -13
- package/dist/routers/webhooks.mjs.map +1 -1
- package/dist/routers/well-known.mjs +12 -6
- package/dist/routers/well-known.mjs.map +1 -1
- package/dist/routes/api-routes.mjs +92 -0
- package/dist/routes/api-routes.mjs.map +1 -0
- package/dist/routes/classification.mjs +37 -0
- package/dist/routes/classification.mjs.map +1 -0
- package/dist/routes/openapi-classes.mjs +21 -0
- package/dist/routes/openapi-classes.mjs.map +1 -0
- package/dist/routes/page-routes.mjs +103 -0
- package/dist/routes/page-routes.mjs.map +1 -0
- package/dist/runtime.mjs +19 -15
- package/dist/runtime.mjs.map +1 -1
- package/dist/serverless.mjs +4 -0
- package/dist/store.mjs +20 -3
- package/dist/store.mjs.map +1 -1
- package/dist/targets.mjs +45 -0
- package/dist/targets.mjs.map +1 -0
- package/dist/ui/avatar.mjs +1 -2
- package/dist/ui/buttons.mjs +17 -5
- package/dist/ui/buttons.mjs.map +1 -1
- package/dist/ui/code-block.mjs +1 -2
- package/dist/ui/compare-stage.mjs +86 -54
- package/dist/ui/compare-stage.mjs.map +1 -1
- package/dist/ui/csrf-field.mjs +1 -2
- package/dist/ui/css.mjs +1 -2
- package/dist/ui/design-reference.mjs +96 -0
- package/dist/ui/design-reference.mjs.map +1 -0
- package/dist/ui/document.mjs +2 -4
- package/dist/ui/document.mjs.map +1 -1
- package/dist/ui/dropdown.mjs +1 -2
- package/dist/ui/feedback.mjs +1 -2
- package/dist/ui/filter-input.mjs +1 -2
- package/dist/ui/forms.mjs +7 -7
- package/dist/ui/forms.mjs.map +1 -1
- package/dist/ui/icons/icon.mjs +1 -2
- package/dist/ui/icons/paths.mjs +2 -2
- package/dist/ui/icons/paths.mjs.map +1 -1
- package/dist/ui/icons/sprite.mjs +1 -2
- package/dist/ui/kbd.mjs +1 -2
- package/dist/ui/layout.mjs +1 -2
- package/dist/ui/preview-embed.mjs +81 -0
- package/dist/ui/preview-embed.mjs.map +1 -0
- package/dist/ui/progress.mjs +1 -2
- package/dist/ui/segmented.mjs +1 -2
- package/dist/ui/shell/account.mjs +1 -2
- package/dist/ui/shell/bare.mjs +1 -2
- package/dist/ui/shell/boot-script.mjs +1 -2
- package/dist/ui/shell/client-script.mjs +86 -2
- package/dist/ui/shell/client-script.mjs.map +1 -1
- package/dist/ui/shell/nav.mjs +13 -10
- package/dist/ui/shell/nav.mjs.map +1 -1
- package/dist/ui/shell/review-script.mjs +25 -3
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +3 -4
- package/dist/ui/shell/sidebar.mjs.map +1 -1
- package/dist/ui/shell/topbar.mjs +1 -2
- package/dist/ui/stacks.mjs +1 -2
- package/dist/ui/styles/base.mjs +1 -2
- package/dist/ui/styles/behavior.mjs +1 -2
- package/dist/ui/styles/review-layout.mjs +1 -2
- package/dist/ui/styles/review.mjs +1 -2
- package/dist/ui/styles/tokens.mjs +1 -2
- package/dist/ui/styles.mjs +1 -2
- package/dist/ui/subnav.mjs +1 -2
- package/dist/ui/table.mjs +1 -2
- package/dist/ui/theme.mjs +1 -2
- package/dist/ui/thumbnail.mjs +1 -2
- package/dist/ui/time.mjs +1 -2
- package/package.json +14 -12
- package/dist/asset-paths.mjs +0 -23
- package/dist/asset-paths.mjs.map +0 -1
- package/dist/assets/mark.svg +0 -1
- package/dist/pages/settings-status.mjs +0 -137
- package/dist/pages/settings-status.mjs.map +0 -1
- package/dist/routers/settings-status.mjs +0 -77
- package/dist/routers/settings-status.mjs.map +0 -1
- package/dist/routers/status-configs.mjs +0 -108
- 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'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
|
-
|
|
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={
|
|
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":"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"}
|
|
@@ -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
|
-
|
|
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__ */
|
|
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,
|
|
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
|
-
|
|
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
|