@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
package/dist/pages/library.mjs
CHANGED
|
@@ -1,91 +1,111 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { getStore } from "../store.mjs";
|
|
5
|
-
import { css } from "../ui/css.mjs";
|
|
4
|
+
import { catalogOf, getStore, testingEnabled } from "../store.mjs";
|
|
6
5
|
import { Button } from "../ui/buttons.mjs";
|
|
7
6
|
import { Badge, EmptyState, Meta, statusTone } from "../ui/feedback.mjs";
|
|
8
7
|
import { SelectField } from "../ui/forms.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import { HStack
|
|
8
|
+
import { PageHeader } from "../ui/layout.mjs";
|
|
9
|
+
import { HStack } from "../ui/stacks.mjs";
|
|
11
10
|
import { FilterInput } from "../ui/filter-input.mjs";
|
|
12
11
|
import { DocumentLayout } from "../ui/document.mjs";
|
|
13
|
-
import {
|
|
12
|
+
import { loadHealthPanel } from "../insights/health-panel.mjs";
|
|
13
|
+
import { HealthSlot } from "./health-panel.mjs";
|
|
14
|
+
import { ProjectDesignLinks } from "./design-links.mjs";
|
|
15
|
+
import { loadSnapshotLayer } from "./library-snapshots.mjs";
|
|
16
|
+
import { LibraryGrid } from "./library-grid.mjs";
|
|
17
|
+
import { entriesFromSnapshots, groupLibrary } from "./library-model.mjs";
|
|
14
18
|
import { BuildModel, ProjectModel, SnapshotModel } from "@storyshelf/core/models";
|
|
15
|
-
import { storybookDir } from "@storyshelf/core/utils";
|
|
16
19
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
17
20
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
max-height: 240px;
|
|
25
|
-
object-fit: contain;
|
|
26
|
-
`;
|
|
27
|
-
/** Library page: gallery grouped by title from latest build on selected branch. */
|
|
21
|
+
// src/pages/library.tsx
|
|
22
|
+
/**
|
|
23
|
+
* Library page (ADR 0006, decision 9): the components of the latest build of the selected branch,
|
|
24
|
+
* whatever its status, from the build's Storybook catalog. With `testing` the cards gain
|
|
25
|
+
* screenshots, statuses and design links, and the page the health panel.
|
|
26
|
+
*/
|
|
28
27
|
async function renderLibraryPage(slug, branch) {
|
|
29
|
-
const
|
|
30
|
-
const project = await new ProjectModel(db).getBySlug(slug);
|
|
28
|
+
const project = await new ProjectModel(getStore().db).getBySlug(slug);
|
|
31
29
|
if (!project) return null;
|
|
32
|
-
const build = await getLibraryBuild(
|
|
30
|
+
const build = await getLibraryBuild(project, branch);
|
|
33
31
|
if (!build) return renderEmptyLibrary(project);
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
return
|
|
32
|
+
const data = await loadLibraryData(project, build);
|
|
33
|
+
if (data.groups.length === 0) return renderNoStories(project, build);
|
|
34
|
+
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
35
|
+
title: "Library",
|
|
36
|
+
nav: {
|
|
37
|
+
active: "library",
|
|
38
|
+
projectSlug: project.slug,
|
|
39
|
+
projectName: project.name
|
|
40
|
+
},
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx(LibraryHeader, {
|
|
43
|
+
project,
|
|
44
|
+
build,
|
|
45
|
+
components: data.groups.length,
|
|
46
|
+
stories: data.groups.reduce((total, group) => total + group.stories.length, 0),
|
|
47
|
+
branches: data.branches
|
|
48
|
+
}),
|
|
49
|
+
data.layer && data.layer.design.projectLinks.length > 0 ? /* @__PURE__ */ jsx(ProjectDesignLinks, { links: data.layer.design.projectLinks }) : null,
|
|
50
|
+
/* @__PURE__ */ jsx(HealthSlot, {
|
|
51
|
+
project,
|
|
52
|
+
data: data.health
|
|
53
|
+
}),
|
|
54
|
+
/* @__PURE__ */ jsx(LibraryGrid, {
|
|
55
|
+
groups: data.groups,
|
|
56
|
+
project,
|
|
57
|
+
build,
|
|
58
|
+
layer: data.layer
|
|
59
|
+
}),
|
|
60
|
+
/* @__PURE__ */ jsx("div", {
|
|
61
|
+
"data-filter-empty": true,
|
|
62
|
+
hidden: true,
|
|
63
|
+
children: /* @__PURE__ */ jsx(Meta, {
|
|
64
|
+
center: true,
|
|
65
|
+
children: "No stories match your filter."
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
});
|
|
37
70
|
}
|
|
38
|
-
async function
|
|
39
|
-
const
|
|
71
|
+
async function loadLibraryData(project, build) {
|
|
72
|
+
const testing = testingEnabled();
|
|
73
|
+
const [catalog, snapshots, branches] = await Promise.all([
|
|
74
|
+
catalogOf().load(project.id, build.id),
|
|
75
|
+
testing ? new SnapshotModel(getStore().db).listByBuild(build.id) : Promise.resolve([]),
|
|
76
|
+
distinctBranches(project.id)
|
|
77
|
+
]);
|
|
78
|
+
const entries = catalog?.entries ?? entriesFromSnapshots(snapshots);
|
|
79
|
+
return {
|
|
80
|
+
groups: groupLibrary(entries),
|
|
81
|
+
branches,
|
|
82
|
+
layer: snapshots.length > 0 ? await loadSnapshotLayer(project, snapshots) : null,
|
|
83
|
+
health: testing ? await loadHealthPanel(project) : null
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Latest build of the branch (default branch when none is chosen), whatever its status. */
|
|
87
|
+
async function getLibraryBuild(project, branch) {
|
|
88
|
+
const buildModel = new BuildModel(getStore().db);
|
|
40
89
|
const trimmed = branch?.trim();
|
|
41
90
|
const target = trimmed === void 0 || trimmed === "" ? project.gitDefaultBranch : trimmed;
|
|
42
|
-
const byBranch = await buildModel.list(project.id, {
|
|
91
|
+
const byBranch = await buildModel.list(project.id, {
|
|
92
|
+
branch: target,
|
|
93
|
+
limit: 1
|
|
94
|
+
});
|
|
43
95
|
if (byBranch[0]) return byBranch[0];
|
|
44
96
|
if (target !== project.gitDefaultBranch) {
|
|
45
|
-
const defaults = await buildModel.list(project.id, {
|
|
97
|
+
const defaults = await buildModel.list(project.id, {
|
|
98
|
+
branch: project.gitDefaultBranch,
|
|
99
|
+
limit: 1
|
|
100
|
+
});
|
|
46
101
|
if (defaults[0]) return defaults[0];
|
|
47
102
|
}
|
|
48
|
-
return (await buildModel.list(project.id))[0] ?? null;
|
|
103
|
+
return (await buildModel.list(project.id, { limit: 1 }))[0] ?? null;
|
|
49
104
|
}
|
|
50
|
-
async function distinctBranches(
|
|
51
|
-
const buildsList = await new BuildModel(db).list(projectId);
|
|
105
|
+
async function distinctBranches(projectId) {
|
|
106
|
+
const buildsList = await new BuildModel(getStore().db).list(projectId);
|
|
52
107
|
return [...new Set(buildsList.map((b) => b.gitBranch))].toSorted((a, b) => a.localeCompare(b));
|
|
53
108
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Map snapshot story ids to their component's docs entry id. Reads the build's
|
|
56
|
-
* Storybook index from storage and returns an empty map when statics are
|
|
57
|
-
* purged or the build has no docs entries, so the Library never links to a
|
|
58
|
-
* docs page that does not exist.
|
|
59
|
-
*/
|
|
60
|
-
async function docsEntriesByStory(storage, projectId, buildId, snapshots) {
|
|
61
|
-
const prefixes = await readDocsPrefixes(storage, projectId, buildId);
|
|
62
|
-
const byStory = /* @__PURE__ */ new Map();
|
|
63
|
-
for (const snap of snapshots) {
|
|
64
|
-
const docsId = docsEntryId(prefixes, snap.storyId);
|
|
65
|
-
if (docsId) byStory.set(snap.storyId, docsId);
|
|
66
|
-
}
|
|
67
|
-
return byStory;
|
|
68
|
-
}
|
|
69
|
-
async function readDocsPrefixes(storage, projectId, buildId) {
|
|
70
|
-
const candidates = await Promise.all(["index.json", "stories.json"].map((name) => readIndexEntries(storage, projectId, buildId, name)));
|
|
71
|
-
const prefixes = /* @__PURE__ */ new Set();
|
|
72
|
-
for (const entries of candidates) if (entries) {
|
|
73
|
-
for (const entry of Object.values(entries)) if (entry?.type === "docs" && typeof entry.id === "string" && entry.id.endsWith("--docs")) prefixes.add(entry.id.slice(0, -6));
|
|
74
|
-
return prefixes;
|
|
75
|
-
}
|
|
76
|
-
return prefixes;
|
|
77
|
-
}
|
|
78
|
-
async function readIndexEntries(storage, projectId, buildId, name) {
|
|
79
|
-
try {
|
|
80
|
-
const raw = await storage.read(posix.join(storybookDir(projectId, buildId), name));
|
|
81
|
-
return JSON.parse(raw.toString("utf8")).entries ?? null;
|
|
82
|
-
} catch {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function docsEntryId(prefixes, storyId) {
|
|
87
|
-
for (const prefix of prefixes) if (storyId.startsWith(`${prefix}--`) || storyId === prefix) return `${prefix}--docs`;
|
|
88
|
-
}
|
|
89
109
|
function renderEmptyLibrary(project) {
|
|
90
110
|
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
91
111
|
title: "Library",
|
|
@@ -107,8 +127,10 @@ function renderEmptyLibrary(project) {
|
|
|
107
127
|
})]
|
|
108
128
|
});
|
|
109
129
|
}
|
|
110
|
-
|
|
130
|
+
/** A build with nothing to list: still publishing, failed, or its Storybook is gone. */
|
|
131
|
+
function renderNoStories(project, build) {
|
|
111
132
|
const urls = createUrlBuilder("/", getStore().config.publishedBaseDomain);
|
|
133
|
+
const preparing = build.status === "pending";
|
|
112
134
|
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
113
135
|
title: "Library",
|
|
114
136
|
nav: {
|
|
@@ -127,8 +149,8 @@ function renderEmptySnapshots(project, build) {
|
|
|
127
149
|
build.status
|
|
128
150
|
] })
|
|
129
151
|
}), /* @__PURE__ */ jsx(EmptyState, {
|
|
130
|
-
title: "No stories in latest build",
|
|
131
|
-
description: "The latest build has no
|
|
152
|
+
title: preparing ? "Preparing the library" : "No stories in the latest build",
|
|
153
|
+
description: preparing ? "The latest build is still being published. Reload in a moment." : "The latest build has no readable Storybook index, so there is nothing to list.",
|
|
132
154
|
action: /* @__PURE__ */ jsx(Button, {
|
|
133
155
|
variant: "secondary",
|
|
134
156
|
href: urls.build(project.slug, build.id),
|
|
@@ -137,55 +159,20 @@ function renderEmptySnapshots(project, build) {
|
|
|
137
159
|
})]
|
|
138
160
|
});
|
|
139
161
|
}
|
|
140
|
-
function
|
|
141
|
-
const
|
|
142
|
-
for (const snap of snapshots) {
|
|
143
|
-
const key = snap.storyTitle || "Untitled";
|
|
144
|
-
const list = map.get(key) ?? [];
|
|
145
|
-
list.push(snap);
|
|
146
|
-
map.set(key, list);
|
|
147
|
-
}
|
|
148
|
-
return map;
|
|
149
|
-
}
|
|
150
|
-
function distinctViewports(snapshots) {
|
|
151
|
-
return [...new Set(snapshots.map((s) => s.viewportName))].toSorted();
|
|
152
|
-
}
|
|
153
|
-
function renderLibraryGrid(project, build, snapshots, branches, docsByStory) {
|
|
154
|
-
const byTitle = groupByTitle(snapshots);
|
|
155
|
-
const viewportNames = distinctViewports(snapshots);
|
|
156
|
-
const multi = viewportNames.length > 1;
|
|
157
|
-
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
158
|
-
title: "Library",
|
|
159
|
-
nav: {
|
|
160
|
-
active: "library",
|
|
161
|
-
projectSlug: project.slug,
|
|
162
|
-
projectName: project.name
|
|
163
|
-
},
|
|
164
|
-
children: [
|
|
165
|
-
renderLibraryHeader(project, build, snapshots.length, branches),
|
|
166
|
-
multi ? /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Meta, {
|
|
167
|
-
as: "span",
|
|
168
|
-
children: ["Viewports: ", viewportNames.join(" · ")]
|
|
169
|
-
}) }) : null,
|
|
170
|
-
renderTitleList(byTitle, project, build, multi, docsByStory),
|
|
171
|
-
/* @__PURE__ */ jsx("div", {
|
|
172
|
-
"data-filter-empty": true,
|
|
173
|
-
hidden: true,
|
|
174
|
-
children: /* @__PURE__ */ jsx(Meta, {
|
|
175
|
-
center: true,
|
|
176
|
-
children: "No stories match your filter."
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
]
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
function renderLibraryHeader(project, build, storyCount, branches) {
|
|
162
|
+
function LibraryHeader(props) {
|
|
163
|
+
const { project, build } = props;
|
|
183
164
|
const urls = createUrlBuilder("/", getStore().config.publishedBaseDomain);
|
|
184
165
|
return /* @__PURE__ */ jsx(PageHeader, {
|
|
185
166
|
title: "Library",
|
|
186
167
|
description: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
187
|
-
|
|
188
|
-
"
|
|
168
|
+
props.components,
|
|
169
|
+
" ",
|
|
170
|
+
props.components === 1 ? "component" : "components",
|
|
171
|
+
" · ",
|
|
172
|
+
props.stories,
|
|
173
|
+
" ",
|
|
174
|
+
props.stories === 1 ? "story" : "stories",
|
|
175
|
+
" · Latest build",
|
|
189
176
|
" ",
|
|
190
177
|
/* @__PURE__ */ jsxs("a", {
|
|
191
178
|
href: urls.build(project.slug, build.id),
|
|
@@ -198,8 +185,11 @@ function renderLibraryHeader(project, build, storyCount, branches) {
|
|
|
198
185
|
" ",
|
|
199
186
|
"· ",
|
|
200
187
|
new Date(build.createdAt).toLocaleString(),
|
|
201
|
-
"
|
|
202
|
-
|
|
188
|
+
" ",
|
|
189
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
190
|
+
tone: statusTone(build.status),
|
|
191
|
+
children: build.status
|
|
192
|
+
})
|
|
203
193
|
] }),
|
|
204
194
|
actions: /* @__PURE__ */ jsxs(HStack, { children: [
|
|
205
195
|
/* @__PURE__ */ jsx(FilterInput, {
|
|
@@ -213,20 +203,24 @@ function renderLibraryHeader(project, build, storyCount, branches) {
|
|
|
213
203
|
href: urls.short(build.id),
|
|
214
204
|
children: "View Storybook"
|
|
215
205
|
}),
|
|
216
|
-
|
|
206
|
+
/* @__PURE__ */ jsx(BranchPicker, {
|
|
207
|
+
project,
|
|
208
|
+
build,
|
|
209
|
+
branches: props.branches
|
|
210
|
+
})
|
|
217
211
|
] })
|
|
218
212
|
});
|
|
219
213
|
}
|
|
220
|
-
function
|
|
214
|
+
function BranchPicker(props) {
|
|
221
215
|
return /* @__PURE__ */ jsx("form", {
|
|
222
216
|
method: "get",
|
|
223
|
-
action: `/projects/${project.slug}/library`,
|
|
217
|
+
action: `/projects/${props.project.slug}/library`,
|
|
224
218
|
children: /* @__PURE__ */ jsxs(HStack, { children: [/* @__PURE__ */ jsx(SelectField, {
|
|
225
219
|
label: "Branch",
|
|
226
220
|
name: "branch",
|
|
227
221
|
layout: "inline",
|
|
228
|
-
value: build.gitBranch,
|
|
229
|
-
options: branches.map((b) => ({
|
|
222
|
+
value: props.build.gitBranch,
|
|
223
|
+
options: props.branches.map((b) => ({
|
|
230
224
|
value: b,
|
|
231
225
|
label: b
|
|
232
226
|
}))
|
|
@@ -237,107 +231,6 @@ function renderBranchPicker(project, build, branches) {
|
|
|
237
231
|
})] })
|
|
238
232
|
});
|
|
239
233
|
}
|
|
240
|
-
function renderTitleList(byTitle, project, build, multi, docsByStory) {
|
|
241
|
-
const titles = [...byTitle.keys()].toSorted((a, b) => a.localeCompare(b));
|
|
242
|
-
return /* @__PURE__ */ jsx(VStack, { children: titles.map((title) => renderTitleGroup(title, byTitle.get(title) ?? [], project, build, multi, docsByStory)) });
|
|
243
|
-
}
|
|
244
|
-
function renderTitleGroup(title, group, project, build, hasMultipleViewports, docsByStory) {
|
|
245
|
-
const byViewport = groupByViewport(group);
|
|
246
|
-
const vNames = [...byViewport.keys()].toSorted();
|
|
247
|
-
return /* @__PURE__ */ jsxs(Card, {
|
|
248
|
-
padded: false,
|
|
249
|
-
"data-filter-item": true,
|
|
250
|
-
"data-filter-text": `${title} ${group.map((snap) => snap.storyName).join(" ")}`,
|
|
251
|
-
children: [/* @__PURE__ */ jsxs(CardSection, {
|
|
252
|
-
divider: true,
|
|
253
|
-
children: [/* @__PURE__ */ jsx(SectionTitle, { children: title }), /* @__PURE__ */ jsxs(Meta, {
|
|
254
|
-
as: "span",
|
|
255
|
-
children: [group.length, " stories"]
|
|
256
|
-
})]
|
|
257
|
-
}), /* @__PURE__ */ jsx(CardSection, { children: /* @__PURE__ */ jsx(VStack, { children: vNames.map((vName) => renderViewportGroup(vName, byViewport.get(vName) ?? [], project, build, hasMultipleViewports, docsByStory)) }) })]
|
|
258
|
-
}, title);
|
|
259
|
-
}
|
|
260
|
-
function groupByViewport(group) {
|
|
261
|
-
const map = /* @__PURE__ */ new Map();
|
|
262
|
-
for (const snap of group) {
|
|
263
|
-
const list = map.get(snap.viewportName) ?? [];
|
|
264
|
-
list.push(snap);
|
|
265
|
-
map.set(snap.viewportName, list);
|
|
266
|
-
}
|
|
267
|
-
return map;
|
|
268
|
-
}
|
|
269
|
-
function renderViewportGroup(vName, snaps, project, build, hasMultipleViewports, docsByStory) {
|
|
270
|
-
return /* @__PURE__ */ jsxs("div", { children: [hasMultipleViewports ? /* @__PURE__ */ jsx(SectionTitle, {
|
|
271
|
-
level: 3,
|
|
272
|
-
children: vName
|
|
273
|
-
}) : null, /* @__PURE__ */ jsx("div", {
|
|
274
|
-
class: snapshotGrid,
|
|
275
|
-
children: snaps.toSorted((a, b) => a.storyName.localeCompare(b.storyName)).map((snap) => renderSnapshotCard(snap, project, build, docsByStory))
|
|
276
|
-
})] }, vName);
|
|
277
|
-
}
|
|
278
|
-
function renderSnapshotCard(snap, project, build, docsByStory) {
|
|
279
|
-
const docsId = docsByStory.get(snap.storyId);
|
|
280
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
281
|
-
class: snapshotCard,
|
|
282
|
-
"data-filter-item": true,
|
|
283
|
-
"data-filter-text": `${snap.storyTitle} ${snap.storyName}`,
|
|
284
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
285
|
-
class: snapshotCardHead,
|
|
286
|
-
children: [/* @__PURE__ */ jsx("strong", { children: snap.storyName }), /* @__PURE__ */ jsxs("span", {
|
|
287
|
-
class: snapshotCardMeta,
|
|
288
|
-
children: [
|
|
289
|
-
/* @__PURE__ */ jsx(Badge, {
|
|
290
|
-
tone: statusTone(snap.status),
|
|
291
|
-
children: snap.status
|
|
292
|
-
}),
|
|
293
|
-
" ",
|
|
294
|
-
snap.viewportWidth,
|
|
295
|
-
"×",
|
|
296
|
-
snap.viewportHeight
|
|
297
|
-
]
|
|
298
|
-
})]
|
|
299
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
300
|
-
class: snapshotCardBody,
|
|
301
|
-
children: [/* @__PURE__ */ jsx("img", {
|
|
302
|
-
class: snapshotImg,
|
|
303
|
-
src: `/api/v1/projects/${project.slug}/builds/${build.id}/snapshots/${snap.id}/image`,
|
|
304
|
-
alt: `${snap.storyTitle} / ${snap.storyName}`,
|
|
305
|
-
loading: "lazy"
|
|
306
|
-
}), /* @__PURE__ */ jsx(HStack, { children: renderCardActions(snap, project, build, docsId) })]
|
|
307
|
-
})]
|
|
308
|
-
}, snap.id);
|
|
309
|
-
}
|
|
310
|
-
function renderCardActions(snap, project, build, docsId) {
|
|
311
|
-
const urls = createUrlBuilder("/", getStore().config.publishedBaseDomain);
|
|
312
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
313
|
-
/* @__PURE__ */ jsx(Button, {
|
|
314
|
-
variant: "secondary",
|
|
315
|
-
size: "sm",
|
|
316
|
-
icon: "columns",
|
|
317
|
-
href: `${urls.buildDiff(project.slug, build.id)}?snapshot=${snap.id}`,
|
|
318
|
-
children: "Review"
|
|
319
|
-
}),
|
|
320
|
-
/* @__PURE__ */ jsx(Button, {
|
|
321
|
-
variant: "ghost",
|
|
322
|
-
size: "sm",
|
|
323
|
-
icon: "external-link",
|
|
324
|
-
href: `${urls.storybookBuild(project.slug, build.id)}/?storyId=${encodeURIComponent(snap.storyId)}`,
|
|
325
|
-
target: "_blank",
|
|
326
|
-
rel: "noopener",
|
|
327
|
-
children: "Preview"
|
|
328
|
-
}),
|
|
329
|
-
docsId ? /* @__PURE__ */ jsx(Button, {
|
|
330
|
-
variant: "ghost",
|
|
331
|
-
size: "sm",
|
|
332
|
-
icon: "book",
|
|
333
|
-
href: `/projects/${project.slug}/storybook/build/${build.id}/?storyId=${encodeURIComponent(docsId)}&viewMode=docs`,
|
|
334
|
-
target: "_blank",
|
|
335
|
-
rel: "noopener",
|
|
336
|
-
children: "Docs"
|
|
337
|
-
}) : null
|
|
338
|
-
] });
|
|
339
|
-
}
|
|
340
|
-
//#endregion
|
|
341
234
|
export { renderLibraryPage };
|
|
342
235
|
|
|
343
236
|
//# sourceMappingURL=library.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library.mjs","names":[],"sources":["../../src/pages/library.tsx"],"sourcesContent":["// oxlint-disable typescript/promise-function-async -- Hono JSX may return Promise<HtmlEscapedString>\nimport { BuildModel } from \"@storyshelf/core/models\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport { SnapshotModel } from \"@storyshelf/core/models\";\nimport type { Build } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { Snapshot } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport { storybookDir } from \"@storyshelf/core/utils\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { posix } from \"node:path\";\nimport { getStore } from \"../store.ts\";\nimport {\n Badge,\n Button,\n Card,\n CardSection,\n EmptyState,\n FilterInput,\n HStack,\n Meta,\n PageHeader,\n SectionTitle,\n SelectField,\n VStack,\n statusTone,\n} from \"../ui/components.tsx\";\nimport { css } from \"../ui/css.ts\";\nimport { DocumentLayout, type RenderedContent } from \"../ui/document.tsx\";\nimport {\n diffPaneImg,\n snapshotCard,\n snapshotCardBody,\n snapshotCardHead,\n snapshotCardMeta,\n snapshotGrid,\n} from \"../ui/styles/review.ts\";\n\n/** Snapshot image with library height cap, extending the shared diff pane image. */\nconst snapshotImg = css`\n /* snapshot-img */\n ${diffPaneImg}\n max-height: 240px;\n object-fit: contain;\n`;\n\n/** Library page: gallery grouped by title from latest build on selected branch. */\nexport async function renderLibraryPage(\n slug: string,\n branch?: string,\n): Promise<RenderedContent | null> {\n const { db, storage } = getStore();\n const project = await new ProjectModel(db).getBySlug(slug);\n if (!project) return null;\n const build = await getLibraryBuild(db, project, branch);\n if (!build) return renderEmptyLibrary(project);\n const snapshots = await new SnapshotModel(db).listByBuild(build.id);\n if (snapshots.length === 0) return renderEmptySnapshots(project, build);\n const branches = await distinctBranches(db, project.id);\n const docsByStory = await docsEntriesByStory(storage, project.id, build.id, snapshots);\n return renderLibraryGrid(project, build, snapshots, branches, docsByStory);\n}\n\nasync function getLibraryBuild(\n db: ReturnType<typeof getStore>[\"db\"],\n project: Project,\n branch?: string,\n): Promise<Build | null> {\n const buildModel = new BuildModel(db);\n const trimmed = branch?.trim();\n const target = trimmed === undefined || trimmed === \"\" ? project.gitDefaultBranch : trimmed;\n const byBranch = await buildModel.list(project.id, { branch: target });\n if (byBranch[0]) return byBranch[0];\n if (target !== project.gitDefaultBranch) {\n const defaults = await buildModel.list(project.id, { branch: project.gitDefaultBranch });\n if (defaults[0]) return defaults[0];\n }\n const all = await buildModel.list(project.id);\n return all[0] ?? null;\n}\n\nasync function distinctBranches(\n db: ReturnType<typeof getStore>[\"db\"],\n projectId: string,\n): Promise<string[]> {\n const buildsList = await new BuildModel(db).list(projectId);\n return [...new Set(buildsList.map((b) => b.gitBranch))].toSorted((a, b) => a.localeCompare(b));\n}\n\n/** Storybook index entries (only the docs subset we need). */\ninterface LibraryIndexEntry {\n id?: string;\n type?: string;\n}\n\n/**\n * Map snapshot story ids to their component's docs entry id. Reads the build's\n * Storybook index from storage and returns an empty map when statics are\n * purged or the build has no docs entries, so the Library never links to a\n * docs page that does not exist.\n */\nasync function docsEntriesByStory(\n storage: ReturnType<typeof getStore>[\"storage\"],\n projectId: string,\n buildId: string,\n snapshots: Snapshot[],\n): Promise<Map<string, string>> {\n const prefixes = await readDocsPrefixes(storage, projectId, buildId);\n const byStory = new Map<string, string>();\n for (const snap of snapshots) {\n const docsId = docsEntryId(prefixes, snap.storyId);\n if (docsId) byStory.set(snap.storyId, docsId);\n }\n return byStory;\n}\n\nasync function readDocsPrefixes(\n storage: ReturnType<typeof getStore>[\"storage\"],\n projectId: string,\n buildId: string,\n): Promise<Set<string>> {\n const candidates = await Promise.all(\n [\"index.json\", \"stories.json\"].map((name) =>\n readIndexEntries(storage, projectId, buildId, name),\n ),\n );\n const prefixes = new Set<string>();\n for (const entries of candidates) {\n if (entries) {\n for (const entry of Object.values(entries)) {\n if (entry?.type === \"docs\" && typeof entry.id === \"string\" && entry.id.endsWith(\"--docs\")) {\n prefixes.add(entry.id.slice(0, -\"--docs\".length));\n }\n }\n return prefixes;\n }\n }\n return prefixes;\n}\n\nasync function readIndexEntries(\n storage: ReturnType<typeof getStore>[\"storage\"],\n projectId: string,\n buildId: string,\n name: string,\n): Promise<Record<string, LibraryIndexEntry> | null> {\n try {\n const raw = await storage.read(posix.join(storybookDir(projectId, buildId), name));\n const parsed = JSON.parse(raw.toString(\"utf8\")) as {\n entries?: Record<string, LibraryIndexEntry>;\n };\n return parsed.entries ?? null;\n } catch {\n return null;\n }\n}\n\nfunction docsEntryId(prefixes: Set<string>, storyId: string): string | undefined {\n for (const prefix of prefixes) {\n if (storyId.startsWith(`${prefix}--`) || storyId === prefix) {\n return `${prefix}--docs`;\n }\n }\n return undefined;\n}\n\nfunction renderEmptyLibrary(project: Project): RenderedContent {\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n <PageHeader\n title=\"Library\"\n description={\n <>All stories/components from the latest build on {project.gitDefaultBranch}.</>\n }\n />\n <EmptyState title=\"No builds yet\" description=\"Upload a build to populate the library.\" />\n </DocumentLayout>\n );\n}\n\nfunction renderEmptySnapshots(project: Project, build: Build): RenderedContent {\n const urls = createUrlBuilder(\"/\", getStore().config.publishedBaseDomain);\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n <PageHeader\n title=\"Library\"\n description={\n <>\n Latest build {build.gitBranch} · {build.gitSha.slice(0, 7)} · {build.status}\n </>\n }\n />\n <EmptyState\n title=\"No stories in latest build\"\n description=\"The latest build has no snapshots yet.\"\n action={\n <Button variant=\"secondary\" href={urls.build(project.slug, build.id)}>\n View build\n </Button>\n }\n />\n </DocumentLayout>\n );\n}\n\nfunction groupByTitle(snapshots: Snapshot[]): Map<string, Snapshot[]> {\n const map = new Map<string, Snapshot[]>();\n for (const snap of snapshots) {\n const key = snap.storyTitle || \"Untitled\";\n const list = map.get(key) ?? [];\n list.push(snap);\n map.set(key, list);\n }\n return map;\n}\n\nfunction distinctViewports(snapshots: Snapshot[]): string[] {\n return [...new Set(snapshots.map((s) => s.viewportName))].toSorted();\n}\n\nfunction renderLibraryGrid(\n project: Project,\n build: Build,\n snapshots: Snapshot[],\n branches: string[],\n docsByStory: Map<string, string>,\n): RenderedContent {\n const byTitle = groupByTitle(snapshots);\n const viewportNames = distinctViewports(snapshots);\n const multi = viewportNames.length > 1;\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n {renderLibraryHeader(project, build, snapshots.length, branches)}\n {multi ? (\n <Card>\n <Meta as=\"span\">Viewports: {viewportNames.join(\" · \")}</Meta>\n </Card>\n ) : null}\n {renderTitleList(byTitle, project, build, multi, docsByStory)}\n <div data-filter-empty hidden>\n <Meta center>No stories match your filter.</Meta>\n </div>\n </DocumentLayout>\n );\n}\n\nfunction renderLibraryHeader(\n project: Project,\n build: Build,\n storyCount: number,\n branches: string[],\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const urls = createUrlBuilder(\"/\", getStore().config.publishedBaseDomain);\n return (\n <PageHeader\n title=\"Library\"\n description={\n <>\n {storyCount} stories · Latest build{\" \"}\n <a href={urls.build(project.slug, build.id)}>\n {build.gitBranch} · {build.gitSha.slice(0, 7)}\n </a>{\" \"}\n · {new Date(build.createdAt).toLocaleString()} · {build.status}\n </>\n }\n actions={\n <HStack>\n <FilterInput label=\"Filter stories\" placeholder=\"Filter stories…\" />\n <Button variant=\"ghost\" size=\"sm\" icon=\"external-link\" href={urls.short(build.id)}>\n View Storybook\n </Button>\n {renderBranchPicker(project, build, branches)}\n </HStack>\n }\n />\n );\n}\n\nfunction renderBranchPicker(\n project: Project,\n build: Build,\n branches: string[],\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n return (\n <form method=\"get\" action={`/projects/${project.slug}/library`}>\n <HStack>\n <SelectField\n label=\"Branch\"\n name=\"branch\"\n layout=\"inline\"\n value={build.gitBranch}\n options={branches.map((b) => ({ value: b, label: b }))}\n />\n <Button variant=\"secondary\" type=\"submit\">\n View\n </Button>\n </HStack>\n </form>\n );\n}\n\nfunction renderTitleList(\n byTitle: Map<string, Snapshot[]>,\n project: Project,\n build: Build,\n multi: boolean,\n docsByStory: Map<string, string>,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const titles = [...byTitle.keys()].toSorted((a, b) => a.localeCompare(b));\n return (\n <VStack>\n {titles.map((title) =>\n renderTitleGroup(title, byTitle.get(title) ?? [], project, build, multi, docsByStory),\n )}\n </VStack>\n );\n}\n\nfunction renderTitleGroup(\n title: string,\n group: Snapshot[],\n project: Project,\n build: Build,\n hasMultipleViewports: boolean,\n docsByStory: Map<string, string>,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const byViewport = groupByViewport(group);\n const vNames = [...byViewport.keys()].toSorted();\n return (\n <Card\n key={title}\n padded={false}\n data-filter-item\n data-filter-text={`${title} ${group.map((snap) => snap.storyName).join(\" \")}`}\n >\n <CardSection divider>\n <SectionTitle>{title}</SectionTitle>\n <Meta as=\"span\">{group.length} stories</Meta>\n </CardSection>\n <CardSection>\n <VStack>\n {vNames.map((vName) =>\n renderViewportGroup(\n vName,\n byViewport.get(vName) ?? [],\n project,\n build,\n hasMultipleViewports,\n docsByStory,\n ),\n )}\n </VStack>\n </CardSection>\n </Card>\n );\n}\n\nfunction groupByViewport(group: Snapshot[]): Map<string, Snapshot[]> {\n const map = new Map<string, Snapshot[]>();\n for (const snap of group) {\n const list = map.get(snap.viewportName) ?? [];\n list.push(snap);\n map.set(snap.viewportName, list);\n }\n return map;\n}\n\nfunction renderViewportGroup(\n vName: string,\n snaps: Snapshot[],\n project: Project,\n build: Build,\n hasMultipleViewports: boolean,\n docsByStory: Map<string, string>,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n return (\n <div key={vName}>\n {hasMultipleViewports ? <SectionTitle level={3}>{vName}</SectionTitle> : null}\n <div class={snapshotGrid}>\n {snaps\n .toSorted((a, b) => a.storyName.localeCompare(b.storyName))\n .map((snap) => renderSnapshotCard(snap, project, build, docsByStory))}\n </div>\n </div>\n );\n}\n\nfunction renderSnapshotCard(\n snap: Snapshot,\n project: Project,\n build: Build,\n docsByStory: Map<string, string>,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const docsId = docsByStory.get(snap.storyId);\n return (\n <div\n key={snap.id}\n class={snapshotCard}\n data-filter-item\n data-filter-text={`${snap.storyTitle} ${snap.storyName}`}\n >\n <div class={snapshotCardHead}>\n <strong>{snap.storyName}</strong>\n <span class={snapshotCardMeta}>\n <Badge tone={statusTone(snap.status)}>{snap.status}</Badge> {snap.viewportWidth}×\n {snap.viewportHeight}\n </span>\n </div>\n <div class={snapshotCardBody}>\n <img\n class={snapshotImg}\n src={`/api/v1/projects/${project.slug}/builds/${build.id}/snapshots/${snap.id}/image`}\n alt={`${snap.storyTitle} / ${snap.storyName}`}\n loading=\"lazy\"\n />\n <HStack>{renderCardActions(snap, project, build, docsId)}</HStack>\n </div>\n </div>\n );\n}\n\nfunction renderCardActions(\n snap: Snapshot,\n project: Project,\n build: Build,\n docsId: string | undefined,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const urls = createUrlBuilder(\"/\", getStore().config.publishedBaseDomain);\n return (\n <>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n icon=\"columns\"\n href={`${urls.buildDiff(project.slug, build.id)}?snapshot=${snap.id}`}\n >\n Review\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon=\"external-link\"\n href={`${urls.storybookBuild(project.slug, build.id)}/?storyId=${encodeURIComponent(snap.storyId)}`}\n target=\"_blank\"\n rel=\"noopener\"\n >\n Preview\n </Button>\n {docsId ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon=\"book\"\n href={`/projects/${project.slug}/storybook/build/${build.id}/?storyId=${encodeURIComponent(docsId)}&viewMode=docs`}\n target=\"_blank\"\n rel=\"noopener\"\n >\n Docs\n </Button>\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"library.mjs","names":[],"sources":["../../src/pages/library.tsx"],"sourcesContent":["// oxlint-disable typescript/promise-function-async -- Hono JSX may return Promise<HtmlEscapedString>\nimport { BuildModel, ProjectModel, SnapshotModel } from \"@storyshelf/core/models\";\nimport type { Build, Project } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { loadHealthPanel, type HealthPanelData } from \"../insights/health-panel.ts\";\nimport { catalogOf, getStore, testingEnabled } from \"../store.ts\";\nimport {\n Button,\n EmptyState,\n FilterInput,\n HStack,\n Meta,\n PageHeader,\n SelectField,\n statusTone,\n Badge,\n} from \"../ui/components.tsx\";\nimport { DocumentLayout, type RenderedContent } from \"../ui/document.tsx\";\nimport { ProjectDesignLinks } from \"./design-links.tsx\";\nimport { HealthSlot } from \"./health-panel.tsx\";\nimport { LibraryGrid } from \"./library-grid.tsx\";\nimport { entriesFromSnapshots, groupLibrary, type LibraryGroup } from \"./library-model.ts\";\nimport { loadSnapshotLayer, type SnapshotLayer } from \"./library-snapshots.tsx\";\n\ntype Rendered = HtmlEscapedString | Promise<HtmlEscapedString>;\n\n/**\n * Library page (ADR 0006, decision 9): the components of the latest build of the selected branch,\n * whatever its status, from the build's Storybook catalog. With `testing` the cards gain\n * screenshots, statuses and design links, and the page the health panel.\n */\nexport async function renderLibraryPage(\n slug: string,\n branch?: string,\n): Promise<RenderedContent | null> {\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) return null;\n const build = await getLibraryBuild(project, branch);\n if (!build) return renderEmptyLibrary(project);\n const data = await loadLibraryData(project, build);\n if (data.groups.length === 0) return renderNoStories(project, build);\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n <LibraryHeader\n project={project}\n build={build}\n components={data.groups.length}\n stories={data.groups.reduce((total, group) => total + group.stories.length, 0)}\n branches={data.branches}\n />\n {data.layer && data.layer.design.projectLinks.length > 0 ? (\n <ProjectDesignLinks links={data.layer.design.projectLinks} />\n ) : null}\n <HealthSlot project={project} data={data.health} />\n <LibraryGrid groups={data.groups} project={project} build={build} layer={data.layer} />\n <div data-filter-empty hidden>\n <Meta center>No stories match your filter.</Meta>\n </div>\n </DocumentLayout>\n );\n}\n\n/** Everything the library renders of a build: catalog groups, branches, and the `testing` layer. */\ninterface LibraryData {\n groups: LibraryGroup[];\n branches: string[];\n layer: SnapshotLayer | null;\n health: HealthPanelData | null;\n}\n\nasync function loadLibraryData(project: Project, build: Build): Promise<LibraryData> {\n const testing = testingEnabled();\n const [catalog, snapshots, branches] = await Promise.all([\n catalogOf().load(project.id, build.id),\n testing ? new SnapshotModel(getStore().db).listByBuild(build.id) : Promise.resolve([]),\n distinctBranches(project.id),\n ]);\n const entries = catalog?.entries ?? entriesFromSnapshots(snapshots);\n return {\n groups: groupLibrary(entries),\n branches,\n layer: snapshots.length > 0 ? await loadSnapshotLayer(project, snapshots) : null,\n health: testing ? await loadHealthPanel(project) : null,\n };\n}\n\n/** Latest build of the branch (default branch when none is chosen), whatever its status. */\nasync function getLibraryBuild(project: Project, branch?: string): Promise<Build | null> {\n const buildModel = new BuildModel(getStore().db);\n const trimmed = branch?.trim();\n const target = trimmed === undefined || trimmed === \"\" ? project.gitDefaultBranch : trimmed;\n const byBranch = await buildModel.list(project.id, { branch: target, limit: 1 });\n if (byBranch[0]) return byBranch[0];\n if (target !== project.gitDefaultBranch) {\n const defaults = await buildModel.list(project.id, {\n branch: project.gitDefaultBranch,\n limit: 1,\n });\n if (defaults[0]) return defaults[0];\n }\n return (await buildModel.list(project.id, { limit: 1 }))[0] ?? null;\n}\n\nasync function distinctBranches(projectId: string): Promise<string[]> {\n const buildsList = await new BuildModel(getStore().db).list(projectId);\n return [...new Set(buildsList.map((b) => b.gitBranch))].toSorted((a, b) => a.localeCompare(b));\n}\n\nfunction renderEmptyLibrary(project: Project): RenderedContent {\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n <PageHeader\n title=\"Library\"\n description={\n <>All stories/components from the latest build on {project.gitDefaultBranch}.</>\n }\n />\n <EmptyState title=\"No builds yet\" description=\"Upload a build to populate the library.\" />\n </DocumentLayout>\n );\n}\n\n/** A build with nothing to list: still publishing, failed, or its Storybook is gone. */\nfunction renderNoStories(project: Project, build: Build): RenderedContent {\n const urls = createUrlBuilder(\"/\", getStore().config.publishedBaseDomain);\n const preparing = build.status === \"pending\";\n return (\n <DocumentLayout\n title=\"Library\"\n nav={{ active: \"library\", projectSlug: project.slug, projectName: project.name }}\n >\n <PageHeader\n title=\"Library\"\n description={\n <>\n Latest build {build.gitBranch} · {build.gitSha.slice(0, 7)} · {build.status}\n </>\n }\n />\n <EmptyState\n title={preparing ? \"Preparing the library\" : \"No stories in the latest build\"}\n description={\n preparing\n ? \"The latest build is still being published. Reload in a moment.\"\n : \"The latest build has no readable Storybook index, so there is nothing to list.\"\n }\n action={\n <Button variant=\"secondary\" href={urls.build(project.slug, build.id)}>\n View build\n </Button>\n }\n />\n </DocumentLayout>\n );\n}\n\nfunction LibraryHeader(props: {\n project: Project;\n build: Build;\n components: number;\n stories: number;\n branches: string[];\n}): Rendered {\n const { project, build } = props;\n const urls = createUrlBuilder(\"/\", getStore().config.publishedBaseDomain);\n return (\n <PageHeader\n title=\"Library\"\n description={\n <>\n {props.components} {props.components === 1 ? \"component\" : \"components\"} · {props.stories}{\" \"}\n {props.stories === 1 ? \"story\" : \"stories\"} · Latest build{\" \"}\n <a href={urls.build(project.slug, build.id)}>\n {build.gitBranch} · {build.gitSha.slice(0, 7)}\n </a>{\" \"}\n · {new Date(build.createdAt).toLocaleString()}{\" \"}\n <Badge tone={statusTone(build.status)}>{build.status}</Badge>\n </>\n }\n actions={\n <HStack>\n <FilterInput label=\"Filter stories\" placeholder=\"Filter stories…\" />\n <Button variant=\"ghost\" size=\"sm\" icon=\"external-link\" href={urls.short(build.id)}>\n View Storybook\n </Button>\n <BranchPicker project={project} build={build} branches={props.branches} />\n </HStack>\n }\n />\n );\n}\n\nfunction BranchPicker(props: { project: Project; build: Build; branches: string[] }): Rendered {\n return (\n <form method=\"get\" action={`/projects/${props.project.slug}/library`}>\n <HStack>\n <SelectField\n label=\"Branch\"\n name=\"branch\"\n layout=\"inline\"\n value={props.build.gitBranch}\n options={props.branches.map((b) => ({ value: b, label: b }))}\n />\n <Button variant=\"secondary\" type=\"submit\">\n View\n </Button>\n </HStack>\n </form>\n );\n}\n"],"mappings":"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"}
|
package/dist/pages/login.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { VStack } from "../ui/stacks.mjs";
|
|
|
10
10
|
import { DocumentLayout } from "../ui/document.mjs";
|
|
11
11
|
import { passkeyLoginScript } from "./passkey-ceremony.mjs";
|
|
12
12
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
13
|
-
|
|
13
|
+
// src/pages/login.tsx
|
|
14
14
|
/** Resolve auth UI text with defaults. */
|
|
15
15
|
function authUi() {
|
|
16
16
|
try {
|
|
@@ -111,7 +111,6 @@ function renderLoginPage(state = {}) {
|
|
|
111
111
|
}) })
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
|
-
//#endregion
|
|
115
114
|
export { renderLoginPage };
|
|
116
115
|
|
|
117
116
|
//# sourceMappingURL=login.mjs.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { Alert } from "../ui/feedback.mjs";
|
|
5
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
+
// src/pages/notifications-paused.tsx
|
|
7
|
+
/** Notifications need a public URL, so without one nothing is sent (ADR 0014, decision 11). */
|
|
8
|
+
function NotificationsPausedNotice(props) {
|
|
9
|
+
return props.paused ? /* @__PURE__ */ jsxs(Alert, {
|
|
10
|
+
tone: "warning",
|
|
11
|
+
title: "Notifications are paused",
|
|
12
|
+
children: [
|
|
13
|
+
"Notifications are paused until a public URL is set; set ",
|
|
14
|
+
/* @__PURE__ */ jsx("code", { children: "PUBLIC_BASE_URL" }),
|
|
15
|
+
" or confirm the detected origin on the System page. Nothing is sent meanwhile, and no channel is marked as failing."
|
|
16
|
+
]
|
|
17
|
+
}) : null;
|
|
18
|
+
}
|
|
19
|
+
export { NotificationsPausedNotice };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=notifications-paused.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications-paused.mjs","names":[],"sources":["../../src/pages/notifications-paused.tsx"],"sourcesContent":["import type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Alert } from \"../ui/components.tsx\";\n\n/** Notifications need a public URL, so without one nothing is sent (ADR 0014, decision 11). */\nexport function NotificationsPausedNotice(props: {\n paused: boolean | undefined;\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n return props.paused ? (\n <Alert tone=\"warning\" title=\"Notifications are paused\">\n Notifications are paused until a public URL is set; set <code>PUBLIC_BASE_URL</code> or\n confirm the detected origin on the System page. Nothing is sent meanwhile, and no channel is\n marked as failing.\n </Alert>\n ) : null;\n}\n"],"mappings":";;;;;;;AAIA,SAAgB,0BAA0B,OAEgB;CACxD,OAAO,MAAM,SACX,qBAAC,OAAD;EAAO,MAAK;EAAU,OAAM;EAA5B,UAAA;GAAuD;GACG,oBAAC,QAAD,EAAA,UAAM,kBAAqB,CAAA;GAAC;EAG/E;CACL,CAAA,IAAA;AACN"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/pages/passkey-ceremony.ts
|
|
5
5
|
/**
|
|
6
6
|
* Page-local WebAuthn ceremony scripts (vanilla JS, no dependencies).
|
|
7
7
|
*
|
|
@@ -91,7 +91,6 @@ ${SUPPORT_CHECK}
|
|
|
91
91
|
})();
|
|
92
92
|
`.trim();
|
|
93
93
|
}
|
|
94
|
-
//#endregion
|
|
95
94
|
export { passkeyLoginScript, passkeyRegisterScript };
|
|
96
95
|
|
|
97
96
|
//# sourceMappingURL=passkey-ceremony.mjs.map
|