@storyshelf/app 0.7.0 → 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/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +1 -2
- package/dist/assets/htmx.mjs +1 -2
- package/dist/assets/mark.mjs +1 -2
- package/dist/boot-tasks.mjs +1 -2
- package/dist/build-state-flags.mjs +1 -2
- package/dist/build-state-setup.mjs +1 -2
- package/dist/capture-alerts.mjs +1 -2
- package/dist/capture-process.mjs +1 -2
- package/dist/capture-setup.mjs +1 -2
- package/dist/connections.mjs +1 -2
- package/dist/consumer-setup.mjs +1 -2
- package/dist/credential-keys.mjs +1 -2
- package/dist/credentials.mjs +1 -2
- package/dist/git-status.mjs +1 -2
- package/dist/hidden-data.mjs +1 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +2 -3
- package/dist/insights/budget.mjs +1 -2
- package/dist/insights/deps.mjs +1 -2
- package/dist/insights/errors.mjs +1 -2
- package/dist/insights/health-panel.mjs +0 -1
- package/dist/insights/job.mjs +1 -2
- package/dist/insights/load-health.mjs +1 -2
- package/dist/insights/load-triage.mjs +1 -2
- package/dist/insights/panel.mjs +1 -2
- package/dist/insights/request.mjs +1 -2
- package/dist/insights/stale.mjs +1 -2
- package/dist/insights/usage-report.mjs +1 -2
- package/dist/insights/view.mjs +1 -2
- package/dist/job-canceller.mjs +1 -2
- package/dist/job-queue.mjs +1 -2
- package/dist/jobs-setup.mjs +1 -2
- package/dist/lifecycle.mjs +2 -3
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +1 -2
- package/dist/middleware/csrf.mjs +1 -2
- package/dist/middleware/rate-limit.mjs +1 -2
- package/dist/middleware/request-log.mjs +1 -2
- package/dist/middleware/require-review.mjs +1 -2
- package/dist/middleware/require-testing.mjs +1 -2
- package/dist/middleware/server-timing.mjs +1 -2
- package/dist/middleware/setup-gate.mjs +1 -2
- package/dist/middleware/store-scope.mjs +1 -2
- package/dist/notifications-pause.mjs +1 -2
- package/dist/notify.d.mts +1 -3
- package/dist/notify.mjs +1 -2
- package/dist/pages/a11y-violations.mjs +1 -2
- package/dist/pages/admin-ai.mjs +1 -2
- package/dist/pages/admin-system-origin.mjs +19 -10
- package/dist/pages/admin-system-origin.mjs.map +1 -1
- package/dist/pages/admin-system-targets.mjs +1 -2
- package/dist/pages/admin-system.mjs +1 -2
- package/dist/pages/build-detail.mjs +1 -2
- package/dist/pages/build-diff-actions.mjs +1 -2
- package/dist/pages/build-diff-comments.mjs +1 -2
- 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 +1 -2
- package/dist/pages/build-diff-header.mjs +1 -2
- package/dist/pages/build-diff-insight.mjs +1 -2
- package/dist/pages/build-diff-model.mjs +20 -3
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +1 -2
- 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 +1 -2
- package/dist/pages/build-diff-stale.mjs +1 -2
- package/dist/pages/build-diff-usage.mjs +1 -2
- package/dist/pages/build-diff-viewer.mjs +38 -12
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +6 -12
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +1 -2
- package/dist/pages/component-detail.mjs +1 -2
- package/dist/pages/component-stories.mjs +1 -2
- package/dist/pages/compute-jobs.mjs +1 -2
- package/dist/pages/design-links.mjs +1 -2
- package/dist/pages/environment-change.mjs +1 -2
- package/dist/pages/health-panel.mjs +1 -2
- package/dist/pages/invite.mjs +1 -2
- package/dist/pages/label-detail.mjs +1 -2
- package/dist/pages/library-grid.mjs +1 -2
- package/dist/pages/library-model.mjs +1 -2
- package/dist/pages/library-snapshots.mjs +1 -2
- package/dist/pages/library.mjs +1 -2
- package/dist/pages/login.mjs +1 -2
- package/dist/pages/notifications-paused.mjs +1 -2
- package/dist/pages/passkey-ceremony.mjs +1 -2
- package/dist/pages/profile.mjs +1 -2
- package/dist/pages/project-builds-table.mjs +1 -2
- package/dist/pages/project-builds.mjs +1 -2
- package/dist/pages/project-create.mjs +1 -2
- package/dist/pages/project-settings.mjs +1 -2
- package/dist/pages/projects-card.mjs +1 -2
- package/dist/pages/projects.mjs +1 -2
- package/dist/pages/review-changeset.mjs +1 -2
- package/dist/pages/review-comments.mjs +1 -2
- package/dist/pages/review-verdict.mjs +1 -2
- package/dist/pages/root.mjs +1 -2
- package/dist/pages/settings-ai.mjs +1 -2
- package/dist/pages/settings-capture.mjs +1 -2
- package/dist/pages/settings-design-credential.mjs +1 -2
- package/dist/pages/settings-design-links.mjs +1 -2
- package/dist/pages/settings-design.mjs +1 -2
- package/dist/pages/settings-general.mjs +1 -2
- package/dist/pages/settings-labels.mjs +1 -2
- package/dist/pages/settings-links.mjs +1 -2
- package/dist/pages/settings-members.mjs +1 -2
- package/dist/pages/settings-notifications-git.mjs +1 -2
- package/dist/pages/settings-notifications.mjs +1 -2
- package/dist/pages/settings-tests.mjs +1 -2
- 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 +1 -2
- package/dist/publish-setup.mjs +1 -2
- package/dist/retention-timer.mjs +10 -11
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +1 -2
- package/dist/routers/admin-pages.mjs +1 -2
- package/dist/routers/admin-public-origin.mjs +10 -3
- package/dist/routers/admin-public-origin.mjs.map +1 -1
- package/dist/routers/admin.mjs +8 -4
- package/dist/routers/admin.mjs.map +1 -1
- package/dist/routers/assets.mjs +1 -2
- package/dist/routers/attempts.mjs +1 -2
- package/dist/routers/auth-sync.mjs +1 -2
- package/dist/routers/auth.mjs +1 -2
- package/dist/routers/baseline-guard.mjs +2 -3
- 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 +1 -2
- package/dist/routers/baselines.mjs +1 -2
- package/dist/routers/build-cancel.mjs +1 -2
- package/dist/routers/build-changeset.mjs +1 -2
- package/dist/routers/build-reviews.handlers.mjs +1 -2
- package/dist/routers/build-reviews.mjs +1 -2
- package/dist/routers/builds.handlers.mjs +23 -9
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +7 -3
- 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 +1 -2
- package/dist/routers/comments.mjs +1 -2
- package/dist/routers/comparison-image.mjs +29 -0
- package/dist/routers/comparison-image.mjs.map +1 -0
- package/dist/routers/component-catalog.handlers.mjs +1 -2
- package/dist/routers/component.handlers.mjs +1 -2
- package/dist/routers/connections.mjs +1 -2
- package/dist/routers/design-connect.handlers.mjs +1 -2
- package/dist/routers/design.handlers.mjs +1 -2
- package/dist/routers/flash.mjs +1 -2
- package/dist/routers/health-report.mjs +1 -2
- package/dist/routers/health.mjs +1 -2
- package/dist/routers/helpers.mjs +1 -2
- package/dist/routers/htmx.mjs +1 -2
- package/dist/routers/insights-ui.mjs +1 -2
- package/dist/routers/insights.handlers.mjs +1 -2
- package/dist/routers/insights.mjs +1 -2
- package/dist/routers/labels.mjs +1 -2
- package/dist/routers/links.handlers.mjs +1 -2
- package/dist/routers/links.mjs +1 -2
- package/dist/routers/media.mjs +14 -5
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +1 -2
- package/dist/routers/notification-channels.mjs +1 -2
- package/dist/routers/notification-subscriptions.mjs +1 -2
- package/dist/routers/oauth-state.mjs +1 -2
- package/dist/routers/preview-base.mjs +1 -2
- package/dist/routers/preview-token.mjs +1 -2
- package/dist/routers/profile.mjs +1 -2
- package/dist/routers/projects.mjs +1 -2
- package/dist/routers/publish-upload.mjs +19 -12
- package/dist/routers/publish-upload.mjs.map +1 -1
- package/dist/routers/published-access.mjs +1 -2
- package/dist/routers/published-compression.mjs +109 -0
- package/dist/routers/published-compression.mjs.map +1 -0
- package/dist/routers/published-files.mjs +26 -9
- package/dist/routers/published-files.mjs.map +1 -1
- package/dist/routers/review-forms.mjs +1 -2
- package/dist/routers/review-next.mjs +1 -2
- package/dist/routers/review-section.handlers.mjs +1 -2
- package/dist/routers/review-switch-off.mjs +1 -2
- package/dist/routers/schemas.mjs +1 -2
- package/dist/routers/scoped-models.mjs +1 -2
- package/dist/routers/settings-ai.mjs +1 -2
- package/dist/routers/settings-capture.mjs +1 -2
- package/dist/routers/settings-design.mjs +1 -2
- package/dist/routers/settings-general.mjs +1 -2
- package/dist/routers/settings-git-channel.mjs +1 -2
- package/dist/routers/settings-labels.mjs +1 -2
- package/dist/routers/settings-links.mjs +1 -2
- package/dist/routers/settings-members.mjs +1 -2
- package/dist/routers/settings-notifications.mjs +1 -2
- package/dist/routers/settings-tokens.mjs +1 -2
- package/dist/routers/settings-webhooks.mjs +1 -2
- package/dist/routers/settings.handlers.mjs +1 -2
- package/dist/routers/settings.mjs +1 -2
- package/dist/routers/signed-storybook.mjs +5 -3
- package/dist/routers/signed-storybook.mjs.map +1 -1
- package/dist/routers/snapshot-groups.mjs +1 -2
- package/dist/routers/snapshots.mjs +1 -2
- package/dist/routers/storybook.mjs +21 -17
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +1 -2
- package/dist/routers/ui.mjs +4 -2
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +1 -2
- package/dist/routers/well-known.mjs +1 -2
- package/dist/routes/api-routes.mjs +1 -2
- package/dist/routes/classification.mjs +1 -2
- package/dist/routes/openapi-classes.mjs +1 -2
- package/dist/routes/page-routes.mjs +2 -3
- package/dist/routes/page-routes.mjs.map +1 -1
- package/dist/runtime.mjs +1 -2
- package/dist/serverless.mjs +1 -2
- package/dist/store.mjs +1 -2
- package/dist/targets.mjs +1 -2
- package/dist/ui/avatar.mjs +1 -2
- package/dist/ui/buttons.mjs +1 -2
- 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 +1 -2
- package/dist/ui/document.mjs +1 -2
- 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 +1 -2
- package/dist/ui/icons/icon.mjs +1 -2
- package/dist/ui/icons/paths.mjs +1 -2
- 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 +1 -2
- 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 +1 -2
- package/dist/ui/shell/nav.mjs +1 -2
- package/dist/ui/shell/review-script.mjs +17 -3
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +1 -2
- 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 +9 -9
|
@@ -7,7 +7,7 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
|
7
7
|
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
8
8
|
import { csrfField } from "../ui/csrf-field.mjs";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
10
|
-
|
|
10
|
+
// src/pages/build-diff-insight.tsx
|
|
11
11
|
function itemsOf(output) {
|
|
12
12
|
const items = output?.items;
|
|
13
13
|
return Array.isArray(items) ? items : [];
|
|
@@ -107,7 +107,6 @@ function InsightSlot(props) {
|
|
|
107
107
|
data: props.data
|
|
108
108
|
}) : null;
|
|
109
109
|
}
|
|
110
|
-
//#endregion
|
|
111
110
|
export { InsightPanel, InsightSlot };
|
|
112
111
|
|
|
113
112
|
//# sourceMappingURL=build-diff-insight.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/pages/build-diff-model.ts
|
|
5
5
|
const OPEN = /* @__PURE__ */ new Set(["new", "changed"]);
|
|
6
6
|
const DECIDED = /* @__PURE__ */ new Set(["approved", "rejected"]);
|
|
7
7
|
const browserOf = (snap) => snap.browser ?? "chromium";
|
|
@@ -87,7 +87,24 @@ function snapshotVariant(snap) {
|
|
|
87
87
|
const browser = snap.browser ?? "chromium";
|
|
88
88
|
return browser === "chromium" ? snap.viewportName : `${snap.viewportName} · ${browser}`;
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
/**
|
|
91
|
+
* The design links of a component that have a cached render, as comparison sources.
|
|
92
|
+
* A link without a render is not offered (the design strip still lists it).
|
|
93
|
+
*/
|
|
94
|
+
function designSources(items) {
|
|
95
|
+
const rendered = items.flatMap((item) => item.renderUrl === null ? [] : [{
|
|
96
|
+
item,
|
|
97
|
+
src: item.renderUrl
|
|
98
|
+
}]);
|
|
99
|
+
const several = rendered.length > 1;
|
|
100
|
+
return rendered.map(({ item, src }, index) => ({
|
|
101
|
+
value: several ? `design-${index + 1}` : "design",
|
|
102
|
+
label: several ? `${item.providerName} ${index + 1}` : "Design",
|
|
103
|
+
src,
|
|
104
|
+
name: item.name,
|
|
105
|
+
providerName: item.providerName
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
export { designSources, diffPercent, groupOf, groupSnapshots, isOpenGroup, neighbours, reviewProgress, snapshotImageUrl, snapshotPageUrl, snapshotVariant };
|
|
92
109
|
|
|
93
110
|
//# sourceMappingURL=build-diff-model.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-diff-model.mjs","names":[],"sources":["../../src/pages/build-diff-model.ts"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\n\n/** How far a build's review has progressed, counted per story/mode (not per browser). */\nexport interface ReviewProgress {\n /** Groups a human has to decide on (any open or decided member). */\n reviewable: number;\n /** Reviewable groups with no open member left. */\n done: number;\n /** Groups still waiting for a decision. */\n pending: number;\n}\n\nconst OPEN = new Set([\"new\", \"changed\"]);\nconst DECIDED = new Set([\"approved\", \"rejected\"]);\n\n/**\n * One story in one mode, with its snapshot on every engine. Approval is shared:\n * a single decision covers all members.\n */\nexport interface SnapshotGroup {\n /** Stable identity (story + mode). */\n key: string;\n storyId: string;\n /** The mode key (`viewportName`). */\n modeKey: string;\n /** Member snapshots, chromium first, then by engine name. */\n members: Snapshot[];\n /** The primary engine's snapshot (chromium when present). */\n primary: Snapshot;\n /** Snapshot the filmstrip links to: first open member, else the first member. */\n representative: Snapshot;\n}\n\nconst browserOf = (snap: Pick<Snapshot, \"browser\">): string => snap.browser ?? \"chromium\";\n\nfunction memberOrder(a: Snapshot, b: Snapshot): number {\n const left = browserOf(a);\n const right = browserOf(b);\n if (left === right) return 0;\n if (left === \"chromium\") return -1;\n if (right === \"chromium\") return 1;\n return left < right ? -1 : 1;\n}\n\n/** Group snapshots by story and mode, keeping first-appearance order. */\nexport function groupSnapshots(snapshots: Snapshot[]): SnapshotGroup[] {\n const groups = new Map<string, Snapshot[]>();\n for (const snap of snapshots) {\n const key = `${snap.storyId}\\u0000${snap.viewportName}`;\n groups.set(key, [...(groups.get(key) ?? []), snap]);\n }\n return [...groups.entries()].flatMap(([key, list]): SnapshotGroup[] => {\n const members = list.toSorted(memberOrder);\n const [primary] = members;\n if (!primary) return [];\n return [\n {\n key,\n storyId: primary.storyId,\n modeKey: primary.viewportName,\n members,\n primary,\n representative: members.find((snap) => OPEN.has(snap.status)) ?? primary,\n },\n ];\n });\n}\n\n/** Whether a group still has a snapshot waiting for a decision. */\nexport function isOpenGroup(group: SnapshotGroup): boolean {\n return group.members.some((snap) => OPEN.has(snap.status));\n}\n\n/** Count review progress per group; groups of unchanged snapshots need no decision. */\nexport function reviewProgress(snapshots: Snapshot[]): ReviewProgress {\n const groups = groupSnapshots(snapshots);\n const pending = groups.filter((group) => isOpenGroup(group)).length;\n const done = groups.filter(\n (group) => !isOpenGroup(group) && group.members.some((snap) => DECIDED.has(snap.status)),\n ).length;\n return { reviewable: pending + done, done, pending };\n}\n\n/** Position of the selected group plus its neighbours in list order. */\nexport interface Neighbours {\n /** 1-based group position, or 0 when nothing is selected. */\n position: number;\n /** Number of groups. */\n total: number;\n prev: Snapshot | null;\n next: Snapshot | null;\n}\n\n/** Previous/next group (as its representative snapshot) around the selected snapshot. */\nexport function neighbours(snapshots: Snapshot[], selectedId?: string): Neighbours {\n const groups = groupSnapshots(snapshots);\n const index = groups.findIndex((group) => group.members.some((snap) => snap.id === selectedId));\n if (index === -1) {\n return { position: 0, total: groups.length, prev: null, next: null };\n }\n return {\n position: index + 1,\n total: groups.length,\n prev: groups[index - 1]?.representative ?? null,\n next: groups[index + 1]?.representative ?? null,\n };\n}\n\n/** The group a snapshot belongs to. */\nexport function groupOf(snapshots: Snapshot[], snapshotId: string): SnapshotGroup | undefined {\n return groupSnapshots(snapshots).find((group) =>\n group.members.some((snap) => snap.id === snapshotId),\n );\n}\n\n/** Diff size as a percentage string (\"3.1%\"), or null when not computed. */\nexport function diffPercent(snapshot: Pick<Snapshot, \"diffRatio\">): string | null {\n return snapshot.diffRatio === null || snapshot.diffRatio === undefined\n ? null\n : `${(snapshot.diffRatio * 100).toFixed(1)}%`;\n}\n\n/** Review-page URL for one snapshot. */\nexport function snapshotPageUrl(project: Project, build: Build, snapshotId: string): string {\n return `/projects/${project.slug}/builds/${build.id}/diff?snapshot=${snapshotId}`;\n}\n\n/** API URL of a snapshot image (`image`), its diff overlay, or its baseline. */\nexport function snapshotImageUrl(\n project: Project,\n build: Build,\n snapshot: Snapshot,\n kind: \"image\" | \"diff\" | \"baseline\",\n): string {\n return `/api/v1/projects/${project.slug}/builds/${build.id}/snapshots/${snapshot.id}/${kind}`;\n}\n\n/** Mode and engine of a snapshot for labels: `dark`, `dark · firefox` (chromium is implicit). */\nexport function snapshotVariant(snap: Pick<Snapshot, \"viewportName\" | \"browser\">): string {\n const browser = snap.browser ?? \"chromium\";\n return browser === \"chromium\" ? snap.viewportName : `${snap.viewportName} · ${browser}`;\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"build-diff-model.mjs","names":[],"sources":["../../src/pages/build-diff-model.ts"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport type { DesignItem } from \"./design-links.tsx\";\n\n/** How far a build's review has progressed, counted per story/mode (not per browser). */\nexport interface ReviewProgress {\n /** Groups a human has to decide on (any open or decided member). */\n reviewable: number;\n /** Reviewable groups with no open member left. */\n done: number;\n /** Groups still waiting for a decision. */\n pending: number;\n}\n\nconst OPEN = new Set([\"new\", \"changed\"]);\nconst DECIDED = new Set([\"approved\", \"rejected\"]);\n\n/**\n * One story in one mode, with its snapshot on every engine. Approval is shared:\n * a single decision covers all members.\n */\nexport interface SnapshotGroup {\n /** Stable identity (story + mode). */\n key: string;\n storyId: string;\n /** The mode key (`viewportName`). */\n modeKey: string;\n /** Member snapshots, chromium first, then by engine name. */\n members: Snapshot[];\n /** The primary engine's snapshot (chromium when present). */\n primary: Snapshot;\n /** Snapshot the filmstrip links to: first open member, else the first member. */\n representative: Snapshot;\n}\n\nconst browserOf = (snap: Pick<Snapshot, \"browser\">): string => snap.browser ?? \"chromium\";\n\nfunction memberOrder(a: Snapshot, b: Snapshot): number {\n const left = browserOf(a);\n const right = browserOf(b);\n if (left === right) return 0;\n if (left === \"chromium\") return -1;\n if (right === \"chromium\") return 1;\n return left < right ? -1 : 1;\n}\n\n/** Group snapshots by story and mode, keeping first-appearance order. */\nexport function groupSnapshots(snapshots: Snapshot[]): SnapshotGroup[] {\n const groups = new Map<string, Snapshot[]>();\n for (const snap of snapshots) {\n const key = `${snap.storyId}\\u0000${snap.viewportName}`;\n groups.set(key, [...(groups.get(key) ?? []), snap]);\n }\n return [...groups.entries()].flatMap(([key, list]): SnapshotGroup[] => {\n const members = list.toSorted(memberOrder);\n const [primary] = members;\n if (!primary) return [];\n return [\n {\n key,\n storyId: primary.storyId,\n modeKey: primary.viewportName,\n members,\n primary,\n representative: members.find((snap) => OPEN.has(snap.status)) ?? primary,\n },\n ];\n });\n}\n\n/** Whether a group still has a snapshot waiting for a decision. */\nexport function isOpenGroup(group: SnapshotGroup): boolean {\n return group.members.some((snap) => OPEN.has(snap.status));\n}\n\n/** Count review progress per group; groups of unchanged snapshots need no decision. */\nexport function reviewProgress(snapshots: Snapshot[]): ReviewProgress {\n const groups = groupSnapshots(snapshots);\n const pending = groups.filter((group) => isOpenGroup(group)).length;\n const done = groups.filter(\n (group) => !isOpenGroup(group) && group.members.some((snap) => DECIDED.has(snap.status)),\n ).length;\n return { reviewable: pending + done, done, pending };\n}\n\n/** Position of the selected group plus its neighbours in list order. */\nexport interface Neighbours {\n /** 1-based group position, or 0 when nothing is selected. */\n position: number;\n /** Number of groups. */\n total: number;\n prev: Snapshot | null;\n next: Snapshot | null;\n}\n\n/** Previous/next group (as its representative snapshot) around the selected snapshot. */\nexport function neighbours(snapshots: Snapshot[], selectedId?: string): Neighbours {\n const groups = groupSnapshots(snapshots);\n const index = groups.findIndex((group) => group.members.some((snap) => snap.id === selectedId));\n if (index === -1) {\n return { position: 0, total: groups.length, prev: null, next: null };\n }\n return {\n position: index + 1,\n total: groups.length,\n prev: groups[index - 1]?.representative ?? null,\n next: groups[index + 1]?.representative ?? null,\n };\n}\n\n/** The group a snapshot belongs to. */\nexport function groupOf(snapshots: Snapshot[], snapshotId: string): SnapshotGroup | undefined {\n return groupSnapshots(snapshots).find((group) =>\n group.members.some((snap) => snap.id === snapshotId),\n );\n}\n\n/** Diff size as a percentage string (\"3.1%\"), or null when not computed. */\nexport function diffPercent(snapshot: Pick<Snapshot, \"diffRatio\">): string | null {\n return snapshot.diffRatio === null || snapshot.diffRatio === undefined\n ? null\n : `${(snapshot.diffRatio * 100).toFixed(1)}%`;\n}\n\n/** Review-page URL for one snapshot. */\nexport function snapshotPageUrl(project: Project, build: Build, snapshotId: string): string {\n return `/projects/${project.slug}/builds/${build.id}/diff?snapshot=${snapshotId}`;\n}\n\n/** API URL of a snapshot image (`image`), its diff overlay, or its baseline. */\nexport function snapshotImageUrl(\n project: Project,\n build: Build,\n snapshot: Snapshot,\n kind: \"image\" | \"diff\" | \"baseline\",\n): string {\n return `/api/v1/projects/${project.slug}/builds/${build.id}/snapshots/${snapshot.id}/${kind}`;\n}\n\n/** Mode and engine of a snapshot for labels: `dark`, `dark · firefox` (chromium is implicit). */\nexport function snapshotVariant(snap: Pick<Snapshot, \"viewportName\" | \"browser\">): string {\n const browser = snap.browser ?? \"chromium\";\n return browser === \"chromium\" ? snap.viewportName : `${snap.viewportName} · ${browser}`;\n}\n\n/** A cached design render the review stage can compare the screenshot with. */\nexport interface DesignSource {\n /** Stage key: `design` for a lone render, `design-<n>` when the component links several. */\n value: string;\n /** Source switch label. */\n label: string;\n /** API URL of the cached render. */\n src: string;\n name: string | null;\n providerName: string;\n}\n\n/**\n * The design links of a component that have a cached render, as comparison sources.\n * A link without a render is not offered (the design strip still lists it).\n */\nexport function designSources(items: readonly DesignItem[]): DesignSource[] {\n const rendered = items.flatMap((item) =>\n item.renderUrl === null ? [] : [{ item, src: item.renderUrl }],\n );\n const several = rendered.length > 1;\n return rendered.map(({ item, src }, index) => ({\n value: several ? `design-${index + 1}` : \"design\",\n label: several ? `${item.providerName} ${index + 1}` : \"Design\",\n src,\n name: item.name,\n providerName: item.providerName,\n }));\n}\n"],"mappings":";;;;AAaA,MAAM,uBAAO,IAAI,IAAI,CAAC,OAAO,SAAS,CAAC;AACvC,MAAM,0BAAU,IAAI,IAAI,CAAC,YAAY,UAAU,CAAC;AAoBhD,MAAM,aAAa,SAA4C,KAAK,WAAW;AAE/E,SAAS,YAAY,GAAa,GAAqB;CACrD,MAAM,OAAO,UAAU,CAAC;CACxB,MAAM,QAAQ,UAAU,CAAC;CACzB,IAAI,SAAS,OAAO,OAAO;CAC3B,IAAI,SAAS,YAAY,OAAO;CAChC,IAAI,UAAU,YAAY,OAAO;CACjC,OAAO,OAAO,QAAQ,KAAK;AAC7B;;AAGA,SAAgB,eAAe,WAAwC;CACrE,MAAM,yBAAS,IAAI,IAAwB;CAC3C,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,KAAK;EACzC,OAAO,IAAI,KAAK,CAAC,GAAI,OAAO,IAAI,GAAG,KAAK,CAAC,GAAI,IAAI,CAAC;CACpD;CACA,OAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,UAA2B;EACrE,MAAM,UAAU,KAAK,SAAS,WAAW;EACzC,MAAM,CAAC,WAAW;EAClB,IAAI,CAAC,SAAS,OAAO,CAAC;EACtB,OAAO,CACL;GACE;GACA,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB;GACA;GACA,gBAAgB,QAAQ,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,CAAC,KAAK;EACnE,CACF;CACF,CAAC;AACH;;AAGA,SAAgB,YAAY,OAA+B;CACzD,OAAO,MAAM,QAAQ,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,CAAC;AAC3D;;AAGA,SAAgB,eAAe,WAAuC;CACpE,MAAM,SAAS,eAAe,SAAS;CACvC,MAAM,UAAU,OAAO,QAAQ,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC;CAC7D,MAAM,OAAO,OAAO,QACjB,UAAU,CAAC,YAAY,KAAK,KAAK,MAAM,QAAQ,MAAM,SAAS,QAAQ,IAAI,KAAK,MAAM,CAAC,CACzF,CAAC,CAAC;CACF,OAAO;EAAE,YAAY,UAAU;EAAM;EAAM;CAAQ;AACrD;;AAaA,SAAgB,WAAW,WAAuB,YAAiC;CACjF,MAAM,SAAS,eAAe,SAAS;CACvC,MAAM,QAAQ,OAAO,WAAW,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAK,OAAO,UAAU,CAAC;CAC9F,IAAI,UAAU,IACZ,OAAO;EAAE,UAAU;EAAG,OAAO,OAAO;EAAQ,MAAM;EAAM,MAAM;CAAK;CAErE,OAAO;EACL,UAAU,QAAQ;EAClB,OAAO,OAAO;EACd,MAAM,OAAO,QAAQ,EAAE,EAAE,kBAAkB;EAC3C,MAAM,OAAO,QAAQ,EAAE,EAAE,kBAAkB;CAC7C;AACF;;AAGA,SAAgB,QAAQ,WAAuB,YAA+C;CAC5F,OAAO,eAAe,SAAS,CAAC,CAAC,MAAM,UACrC,MAAM,QAAQ,MAAM,SAAS,KAAK,OAAO,UAAU,CACrD;AACF;;AAGA,SAAgB,YAAY,UAAsD;CAChF,OAAO,SAAS,cAAc,QAAQ,SAAS,cAAc,KAAA,IACzD,OACA,IAAI,SAAS,YAAY,IAAA,CAAK,QAAQ,CAAC,EAAE;AAC/C;;AAGA,SAAgB,gBAAgB,SAAkB,OAAc,YAA4B;CAC1F,OAAO,aAAa,QAAQ,KAAK,UAAU,MAAM,GAAG,iBAAiB;AACvE;;AAGA,SAAgB,iBACd,SACA,OACA,UACA,MACQ;CACR,OAAO,oBAAoB,QAAQ,KAAK,UAAU,MAAM,GAAG,aAAa,SAAS,GAAG,GAAG;AACzF;;AAGA,SAAgB,gBAAgB,MAA0D;CACxF,MAAM,UAAU,KAAK,WAAW;CAChC,OAAO,YAAY,aAAa,KAAK,eAAe,GAAG,KAAK,aAAa,KAAK;AAChF;;;;;AAkBA,SAAgB,cAAc,OAA8C;CAC1E,MAAM,WAAW,MAAM,SAAS,SAC9B,KAAK,cAAc,OAAO,CAAC,IAAI,CAAC;EAAE;EAAM,KAAK,KAAK;CAAU,CAAC,CAC/D;CACA,MAAM,UAAU,SAAS,SAAS;CAClC,OAAO,SAAS,KAAK,EAAE,MAAM,OAAO,WAAW;EAC7C,OAAO,UAAU,UAAU,QAAQ,MAAM;EACzC,OAAO,UAAU,GAAG,KAAK,aAAa,GAAG,QAAQ,MAAM;EACvD;EACA,MAAM,KAAK;EACX,cAAc,KAAK;CACrB,EAAE;AACJ"}
|
|
@@ -9,7 +9,7 @@ import { a11yCarrierOf, a11yFindingsOf } from "./a11y-violations.mjs";
|
|
|
9
9
|
import { diffPercent, groupSnapshots, isOpenGroup, reviewProgress, snapshotImageUrl, snapshotPageUrl } from "./build-diff-model.mjs";
|
|
10
10
|
import { filmBody, filmItem, filmItemActive, filmStatus, filmSub, filmTitle, filmstrip, filmstripHead, filmstripList, statusDots } from "../ui/styles/review-layout.mjs";
|
|
11
11
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
12
|
-
|
|
12
|
+
// src/pages/build-diff-nav.tsx
|
|
13
13
|
/**
|
|
14
14
|
* The list filters purely in CSS off `data-filter`, so it works before the
|
|
15
15
|
* review script runs. The selected snapshot always stays visible.
|
|
@@ -132,7 +132,6 @@ function DiffNav(props) {
|
|
|
132
132
|
})]
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
-
//#endregion
|
|
136
135
|
export { DiffNav };
|
|
137
136
|
|
|
138
137
|
//# sourceMappingURL=build-diff-nav.mjs.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { Alert } from "../ui/feedback.mjs";
|
|
5
|
+
import { Segmented } from "../ui/segmented.mjs";
|
|
6
|
+
import { CompareStage } from "../ui/compare-stage.mjs";
|
|
7
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
+
// src/pages/build-diff-reference.tsx
|
|
9
|
+
function BaselineReferenceNotice(props) {
|
|
10
|
+
const reference = props.reference;
|
|
11
|
+
if (reference.kind === "none") return /* @__PURE__ */ jsx(Alert, {
|
|
12
|
+
tone: "info",
|
|
13
|
+
title: "No baseline at capture",
|
|
14
|
+
children: "This was a new comparison; no baseline existed when it was captured."
|
|
15
|
+
});
|
|
16
|
+
if (reference.kind === "unavailable") return /* @__PURE__ */ jsx(Alert, {
|
|
17
|
+
tone: "warning",
|
|
18
|
+
title: "Baseline reference unavailable",
|
|
19
|
+
children: "The original baseline details or image are unavailable or have changed since this diff was computed. For older records, any image shown may be the current baseline rather than the one used for the diff."
|
|
20
|
+
});
|
|
21
|
+
return /* @__PURE__ */ jsxs(Alert, {
|
|
22
|
+
tone: "info",
|
|
23
|
+
title: "Comparison baseline",
|
|
24
|
+
children: [
|
|
25
|
+
reference.defaultBranch ? "Default branch" : "Accepted branch",
|
|
26
|
+
": ",
|
|
27
|
+
reference.branch,
|
|
28
|
+
". Baseline updated ",
|
|
29
|
+
reference.updatedAt,
|
|
30
|
+
".",
|
|
31
|
+
reference.imageAvailable ? "" : " The baseline image is unavailable.",
|
|
32
|
+
reference.build ? ` Reference build ${reference.build.id}, commit ${reference.build.gitSha.slice(0, 7)}.` : " Reference build and commit are unavailable."
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/** What the Design source is, shown under its controls. */
|
|
37
|
+
const DESIGN_NOTE = "Design reference: not a test. Scaled to the screenshot’s width.";
|
|
38
|
+
/**
|
|
39
|
+
* The comparison stage with a source switch. The baseline stage is always there; each cached design
|
|
40
|
+
* render adds a stage (hidden until chosen) that compares the screenshot with the design using the
|
|
41
|
+
* same side-by-side, swipe and onion tools. A reference only: no diff, score or verdict. Without a
|
|
42
|
+
* render, only the baseline stage is rendered.
|
|
43
|
+
*/
|
|
44
|
+
function ComparisonSources(props) {
|
|
45
|
+
const { baseline, sources, currentSrc, subject } = props;
|
|
46
|
+
if (sources.length === 0) return baseline;
|
|
47
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
48
|
+
"data-compare-sources": true,
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsx(Segmented, {
|
|
51
|
+
label: "Compare against",
|
|
52
|
+
"data-compare-source-switch": true,
|
|
53
|
+
items: [{
|
|
54
|
+
label: "Baseline",
|
|
55
|
+
value: "baseline",
|
|
56
|
+
active: true
|
|
57
|
+
}, ...sources.map((source) => ({
|
|
58
|
+
label: source.label,
|
|
59
|
+
value: source.value
|
|
60
|
+
}))]
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ jsx("div", {
|
|
63
|
+
"data-compare-source": "baseline",
|
|
64
|
+
children: baseline
|
|
65
|
+
}),
|
|
66
|
+
sources.map((source) => /* @__PURE__ */ jsx("div", {
|
|
67
|
+
"data-compare-source": source.value,
|
|
68
|
+
hidden: true,
|
|
69
|
+
children: /* @__PURE__ */ jsx(CompareStage, {
|
|
70
|
+
variant: "design",
|
|
71
|
+
baselineLabel: source.name ? `${source.providerName} · ${source.name}` : "Design",
|
|
72
|
+
baselineSrc: source.src,
|
|
73
|
+
currentSrc,
|
|
74
|
+
diffSrc: null,
|
|
75
|
+
subject,
|
|
76
|
+
note: DESIGN_NOTE
|
|
77
|
+
})
|
|
78
|
+
}, source.value))
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export { BaselineReferenceNotice, ComparisonSources };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=build-diff-reference.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-diff-reference.mjs","names":[],"sources":["../../src/pages/build-diff-reference.tsx"],"sourcesContent":["import type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { BaselineReference } from \"../routers/baseline-reference.ts\";\nimport { Alert, CompareStage, Segmented } from \"../ui/components.tsx\";\nimport type { DesignSource } from \"./build-diff-model.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HTML or a promise */\nexport function BaselineReferenceNotice(props: {\n reference: BaselineReference;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const reference = props.reference;\n if (reference.kind === \"none\")\n return (\n <Alert tone=\"info\" title=\"No baseline at capture\">\n This was a new comparison; no baseline existed when it was captured.\n </Alert>\n );\n if (reference.kind === \"unavailable\")\n return (\n <Alert tone=\"warning\" title=\"Baseline reference unavailable\">\n The original baseline details or image are unavailable or have changed since this diff was\n computed. For older records, any image shown may be the current baseline rather than the one\n used for the diff.\n </Alert>\n );\n return (\n <Alert tone=\"info\" title=\"Comparison baseline\">\n {reference.defaultBranch ? \"Default branch\" : \"Accepted branch\"}: {reference.branch}. Baseline\n updated {reference.updatedAt}.\n {reference.imageAvailable ? \"\" : \" The baseline image is unavailable.\"}\n {reference.build\n ? ` Reference build ${reference.build.id}, commit ${reference.build.gitSha.slice(0, 7)}.`\n : \" Reference build and commit are unavailable.\"}\n </Alert>\n );\n}\n\n/** What the Design source is, shown under its controls. */\nconst DESIGN_NOTE = \"Design reference: not a test. Scaled to the screenshot’s width.\";\n\n/**\n * The comparison stage with a source switch. The baseline stage is always there; each cached design\n * render adds a stage (hidden until chosen) that compares the screenshot with the design using the\n * same side-by-side, swipe and onion tools. A reference only: no diff, score or verdict. Without a\n * render, only the baseline stage is rendered.\n */\nexport function ComparisonSources(props: {\n /** The baseline comparison stage. */\n baseline: HtmlEscapedString | Promise<HtmlEscapedString>;\n sources: DesignSource[];\n currentSrc: string;\n /** Accessible description shared by the images, e.g. \"Button / Primary\". */\n subject: string;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { baseline, sources, currentSrc, subject } = props;\n if (sources.length === 0) return baseline;\n return (\n <div data-compare-sources>\n <Segmented\n label=\"Compare against\"\n data-compare-source-switch\n items={[\n { label: \"Baseline\", value: \"baseline\", active: true },\n ...sources.map((source) => ({ label: source.label, value: source.value })),\n ]}\n />\n <div data-compare-source=\"baseline\">{baseline}</div>\n {sources.map((source) => (\n <div key={source.value} data-compare-source={source.value} hidden>\n <CompareStage\n variant=\"design\"\n baselineLabel={source.name ? `${source.providerName} · ${source.name}` : \"Design\"}\n baselineSrc={source.src}\n currentSrc={currentSrc}\n diffSrc={null}\n subject={subject}\n note={DESIGN_NOTE}\n />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAMA,SAAgB,wBAAwB,OAEW;CACjD,MAAM,YAAY,MAAM;CACxB,IAAI,UAAU,SAAS,QACrB,OACE,oBAAC,OAAD;EAAO,MAAK;EAAO,OAAM;EAAyB,UAAA;CAE3C,CAAA;CAEX,IAAI,UAAU,SAAS,eACrB,OACE,oBAAC,OAAD;EAAO,MAAK;EAAU,OAAM;EAAiC,UAAA;CAItD,CAAA;CAEX,OACE,qBAAC,OAAD;EAAO,MAAK;EAAO,OAAM;EAAzB,UAAA;GACG,UAAU,gBAAgB,mBAAmB;GAAkB;GAAG,UAAU;GAAO;GAC3E,UAAU;GAAU;GAC5B,UAAU,iBAAiB,KAAK;GAChC,UAAU,QACP,oBAAoB,UAAU,MAAM,GAAG,WAAW,UAAU,MAAM,OAAO,MAAM,GAAG,CAAC,EAAE,KACrF;EACC;;AAEX;;AAGA,MAAM,cAAc;;;;;;;AAQpB,SAAgB,kBAAkB,OAOiB;CACjD,MAAM,EAAE,UAAU,SAAS,YAAY,YAAY;CACnD,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,OACE,qBAAC,OAAD;EAAK,wBAAA;EAAL,UAAA;GACE,oBAAC,WAAD;IACE,OAAM;IACN,8BAAA;IACA,OAAO,CACL;KAAE,OAAO;KAAY,OAAO;KAAY,QAAQ;IAAK,GACrD,GAAG,QAAQ,KAAK,YAAY;KAAE,OAAO,OAAO;KAAO,OAAO,OAAO;IAAM,EAAE,CAC3E;GACD,CAAA;GACD,oBAAC,OAAD;IAAK,uBAAoB;IAAY,UAAA;GAAc,CAAA;GAClD,QAAQ,KAAK,WACZ,oBAAC,OAAD;IAAwB,uBAAqB,OAAO;IAAO,QAAA;IACzD,UAAA,oBAAC,cAAD;KACE,SAAQ;KACR,eAAe,OAAO,OAAO,GAAG,OAAO,aAAa,KAAK,OAAO,SAAS;KACzE,aAAa,OAAO;KACR;KACZ,SAAS;KACA;KACT,MAAM;IACP,CAAA;GACE,GAVK,OAAO,KAUZ,CACN;EACE;;AAET"}
|
|
@@ -5,7 +5,7 @@ import { Button } from "../ui/buttons.mjs";
|
|
|
5
5
|
import { Kbd } from "../ui/kbd.mjs";
|
|
6
6
|
import { shortcutsDialog } from "../ui/styles/review-layout.mjs";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
-
|
|
8
|
+
// src/pages/build-diff-shortcuts.tsx
|
|
9
9
|
const SHORTCUTS = [
|
|
10
10
|
{
|
|
11
11
|
keys: ["j", "→"],
|
|
@@ -76,7 +76,6 @@ function ShortcutsDialog() {
|
|
|
76
76
|
]
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
//#endregion
|
|
80
79
|
export { ShortcutsDialog };
|
|
81
80
|
|
|
82
81
|
//# sourceMappingURL=build-diff-shortcuts.mjs.map
|
|
@@ -5,7 +5,7 @@ import { Button } from "../ui/buttons.mjs";
|
|
|
5
5
|
import { Alert, Meta } from "../ui/feedback.mjs";
|
|
6
6
|
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
7
7
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
-
|
|
8
|
+
// src/pages/build-diff-stale.tsx
|
|
9
9
|
/**
|
|
10
10
|
* Warns that undecided snapshots were compared with a baseline that has since changed.
|
|
11
11
|
* Builds are never recaptured automatically, so the reviewer chooses how to refresh.
|
|
@@ -64,7 +64,6 @@ function StaleSnapshotNotice(props) {
|
|
|
64
64
|
]
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
-
//#endregion
|
|
68
67
|
export { StaleBaselineNotice, StaleSnapshotNotice };
|
|
69
68
|
|
|
70
69
|
//# sourceMappingURL=build-diff-stale.mjs.map
|
|
@@ -6,7 +6,7 @@ import { Badge } from "../ui/feedback.mjs";
|
|
|
6
6
|
import { HStack } from "../ui/stacks.mjs";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
8
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
9
|
-
|
|
9
|
+
// src/pages/build-diff-usage.tsx
|
|
10
10
|
/** Deep link to the upstream component's published story (or its Storybook root). */
|
|
11
11
|
function upstreamPreviewUrl(item, publishedBaseDomain) {
|
|
12
12
|
if (!item.upstreamBuildId) return null;
|
|
@@ -57,7 +57,6 @@ function LinkedUsage(props) {
|
|
|
57
57
|
}, item.package);
|
|
58
58
|
}) });
|
|
59
59
|
}
|
|
60
|
-
//#endregion
|
|
61
60
|
export { LinkedUsage, upstreamPreviewUrl };
|
|
62
61
|
|
|
63
62
|
//# sourceMappingURL=build-diff-usage.mjs.map
|
|
@@ -7,15 +7,17 @@ import { VStack } from "../ui/stacks.mjs";
|
|
|
7
7
|
import { Segmented } from "../ui/segmented.mjs";
|
|
8
8
|
import { CompareStage } from "../ui/compare-stage.mjs";
|
|
9
9
|
import { A11yViolationsNotice, a11yCarrierOf } from "./a11y-violations.mjs";
|
|
10
|
-
import { diffPercent, snapshotImageUrl, snapshotPageUrl, snapshotVariant } from "./build-diff-model.mjs";
|
|
10
|
+
import { designSources, diffPercent, snapshotImageUrl, snapshotPageUrl, snapshotVariant } from "./build-diff-model.mjs";
|
|
11
11
|
import { StaleSnapshotNotice } from "./build-diff-stale.mjs";
|
|
12
12
|
import { LinkedUsage } from "./build-diff-usage.mjs";
|
|
13
13
|
import { DesignReferences, DesignStrip } from "./design-links.mjs";
|
|
14
14
|
import { viewerHead, viewerMeta, viewerTitle } from "../ui/styles/review-layout.mjs";
|
|
15
15
|
import { EnvironmentChangeSnapshotNotice } from "./build-diff-environment.mjs";
|
|
16
|
+
import { DefaultComparisonView } from "./build-diff-default.mjs";
|
|
17
|
+
import { BaselineReferenceNotice, ComparisonSources } from "./build-diff-reference.mjs";
|
|
16
18
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
17
19
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
18
|
-
|
|
20
|
+
// src/pages/build-diff-viewer.tsx
|
|
19
21
|
/** Why a snapshot has no diff image, phrased for the empty pane. */
|
|
20
22
|
function diffEmptyText(status) {
|
|
21
23
|
return status === "unchanged" || status === "approved" ? "No diff — within threshold" : "No diff yet";
|
|
@@ -49,7 +51,7 @@ function ViewComponentButton(props) {
|
|
|
49
51
|
}
|
|
50
52
|
/** Story heading plus the comparison stage for the selected snapshot. */
|
|
51
53
|
function DiffViewer(props) {
|
|
52
|
-
const { project, build, selected,
|
|
54
|
+
const { project, build, selected, drifted, usage, design, publishedBaseDomain } = props;
|
|
53
55
|
const siblings = props.siblings ?? [selected];
|
|
54
56
|
const percent = diffPercent(selected);
|
|
55
57
|
return /* @__PURE__ */ jsxs(VStack, { children: [
|
|
@@ -93,6 +95,7 @@ function DiffViewer(props) {
|
|
|
93
95
|
]
|
|
94
96
|
})]
|
|
95
97
|
}),
|
|
98
|
+
props.reference ? /* @__PURE__ */ jsx(BaselineReferenceNotice, { reference: props.reference }) : null,
|
|
96
99
|
/* @__PURE__ */ jsx(BrowserTabs, {
|
|
97
100
|
project,
|
|
98
101
|
build,
|
|
@@ -110,18 +113,41 @@ function DiffViewer(props) {
|
|
|
110
113
|
publishedBaseDomain
|
|
111
114
|
}) : null,
|
|
112
115
|
design && design.length > 0 ? /* @__PURE__ */ jsx(DesignStrip, { items: design }) : null,
|
|
113
|
-
/* @__PURE__ */ jsx(
|
|
114
|
-
|
|
115
|
-
currentSrc: snapshotImageUrl(project, build, selected, "image"),
|
|
116
|
-
diffSrc: selected.diffPath ? snapshotImageUrl(project, build, selected, "diff") : null,
|
|
117
|
-
subject: `${selected.storyTitle} / ${selected.storyName}`,
|
|
118
|
-
diffEmpty: diffEmptyText(selected.status),
|
|
119
|
-
diffMeta: percent ?? void 0
|
|
120
|
-
}),
|
|
116
|
+
/* @__PURE__ */ jsx(PrimaryComparison, { ...props }),
|
|
117
|
+
/* @__PURE__ */ jsx(SecondaryComparison, { ...props }),
|
|
121
118
|
design && design.length > 0 ? /* @__PURE__ */ jsx(DesignReferences, { items: design }) : null
|
|
122
119
|
] });
|
|
123
120
|
}
|
|
124
|
-
|
|
121
|
+
function SecondaryComparison(props) {
|
|
122
|
+
if (!props.defaultComparison) return null;
|
|
123
|
+
return /* @__PURE__ */ jsx(DefaultComparisonView, {
|
|
124
|
+
...props,
|
|
125
|
+
snapshot: props.selected,
|
|
126
|
+
comparison: props.defaultComparison
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function baselineUrl(props) {
|
|
130
|
+
const { hasBaseline, selected, reference, project, build } = props;
|
|
131
|
+
return hasBaseline[selected.id] && reference?.kind !== "none" && !(reference?.kind === "known" && !reference.imageAvailable) && !(reference?.kind === "unavailable" && selected.baselineVersion !== null) ? snapshotImageUrl(project, build, selected, "baseline") : null;
|
|
132
|
+
}
|
|
133
|
+
function PrimaryComparison(props) {
|
|
134
|
+
const { project, build, selected, reference } = props;
|
|
135
|
+
const stage = /* @__PURE__ */ jsx(CompareStage, {
|
|
136
|
+
baselineSrc: baselineUrl(props),
|
|
137
|
+
baselineEmpty: reference?.kind === "unavailable" || reference?.kind === "known" && !reference.imageAvailable ? "The original baseline image is unavailable." : void 0,
|
|
138
|
+
currentSrc: snapshotImageUrl(project, build, selected, "image"),
|
|
139
|
+
diffSrc: selected.diffPath ? snapshotImageUrl(project, build, selected, "diff") : null,
|
|
140
|
+
subject: `${selected.storyTitle} / ${selected.storyName}`,
|
|
141
|
+
diffEmpty: diffEmptyText(selected.status),
|
|
142
|
+
diffMeta: diffPercent(selected) ?? void 0
|
|
143
|
+
});
|
|
144
|
+
return /* @__PURE__ */ jsx(ComparisonSources, {
|
|
145
|
+
baseline: stage,
|
|
146
|
+
sources: designSources(props.design ?? []),
|
|
147
|
+
currentSrc: snapshotImageUrl(project, build, selected, "image"),
|
|
148
|
+
subject: `${selected.storyTitle} / ${selected.storyName}`
|
|
149
|
+
});
|
|
150
|
+
}
|
|
125
151
|
export { DiffViewer };
|
|
126
152
|
|
|
127
153
|
//# sourceMappingURL=build-diff-viewer.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-diff-viewer.mjs","names":[],"sources":["../../src/pages/build-diff-viewer.tsx"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Badge, Button, CompareStage, Segmented, VStack, statusTone } from \"../ui/components.tsx\";\nimport { viewerHead, viewerMeta, viewerTitle } from \"../ui/styles/review-layout.ts\";\nimport { A11yViolationsNotice, a11yCarrierOf } from \"./a11y-violations.tsx\";\nimport { EnvironmentChangeSnapshotNotice } from \"./build-diff-environment.tsx\";\nimport {\n diffPercent,\n snapshotImageUrl,\n snapshotPageUrl,\n snapshotVariant,\n} from \"./build-diff-model.ts\";\nimport { StaleSnapshotNotice } from \"./build-diff-stale.tsx\";\nimport { LinkedUsage, type LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DesignReferences, DesignStrip, type DesignItem } from \"./design-links.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** Viewer props for the selected snapshot. */\nexport interface DiffViewerProps {\n project: Project;\n build: Build;\n selected: Snapshot;\n /** The selected story/mode's snapshots on every engine (browser tabs when more than one). */\n siblings?: Snapshot[];\n hasBaseline: Record<string, boolean>;\n /** Set when the baseline changed after this snapshot's diff was computed. */\n drifted?: \"stale\" | \"removed\";\n /** Linked packages the story uses (provenance badges + upstream deep links). */\n usage?: LinkedUsageItem[];\n /** Resolved design links of the story's component (open-in-Figma + stale/deleted notices). */\n design?: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** Why a snapshot has no diff image, phrased for the empty pane. */\nfunction diffEmptyText(status: string): string {\n return status === \"unchanged\" || status === \"approved\"\n ? \"No diff — within threshold\"\n : \"No diff yet\";\n}\n\n/** Browser tabs (one link per engine) when the story/mode was captured on several. */\nfunction BrowserTabs(props: {\n project: Project;\n build: Build;\n selected: Snapshot;\n siblings: Snapshot[];\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, build, selected, siblings } = props;\n if (siblings.length < 2) return null;\n return (\n <Segmented\n label=\"Browser\"\n data-browser-tabs\n items={siblings.map((snap) => ({\n label: `${snap.browser ?? \"chromium\"} · ${snap.status}`,\n value: snap.id,\n href: snapshotPageUrl(project, build, snap.id),\n active: snap.id === selected.id,\n }))}\n />\n );\n}\n\n/** Link to the story file's component page, anchored at the story. */\nfunction ViewComponentButton(props: {\n project: Project;\n selected: Snapshot;\n base?: string;\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, selected, base } = props;\n if (!selected.storyImportPath) return null;\n const urls = createUrlBuilder(\"/\", base);\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n href={`${urls.component(project.slug, selected.storyImportPath)}#${selected.storyId}`}\n >\n View component\n </Button>\n );\n}\n\n/** Story heading plus the comparison stage for the selected snapshot. */\nexport function DiffViewer(props: DiffViewerProps): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, selected, hasBaseline, drifted, usage, design, publishedBaseDomain } =\n props;\n const siblings = props.siblings ?? [selected];\n const percent = diffPercent(selected);\n return (\n <VStack>\n <div class={viewerHead}>\n <h2 class={viewerTitle}>\n {selected.storyTitle} / {selected.storyName}\n </h2>\n <p class={viewerMeta}>\n <Badge tone={statusTone(selected.status)} icon>\n {selected.status}\n </Badge>\n <span>\n {snapshotVariant(selected)} · {selected.viewportWidth}×{selected.viewportHeight}\n </span>\n {selected.diffPixels === null || selected.diffPixels === undefined ? null : (\n <span class=\"mono\">{selected.diffPixels} px</span>\n )}\n {percent ? <span class=\"mono\">{percent}</span> : null}\n <ViewComponentButton project={project} selected={selected} base={publishedBaseDomain} />\n </p>\n </div>\n <BrowserTabs project={project} build={build} selected={selected} siblings={siblings} />\n {drifted ? <StaleSnapshotNotice status={drifted} /> : null}\n <EnvironmentChangeSnapshotNotice snapshot={selected} migrated={build.isDefault} />\n <A11yViolationsNotice snapshot={a11yCarrierOf(selected, siblings)} />\n {usage && usage.length > 0 ? (\n <LinkedUsage usage={usage} publishedBaseDomain={publishedBaseDomain} />\n ) : null}\n {design && design.length > 0 ? <DesignStrip items={design} /> : null}\n <CompareStage\n baselineSrc={\n hasBaseline[selected.id] ? snapshotImageUrl(project, build, selected, \"baseline\") : null\n }\n currentSrc={snapshotImageUrl(project, build, selected, \"image\")}\n diffSrc={selected.diffPath ? snapshotImageUrl(project, build, selected, \"diff\") : null}\n subject={`${selected.storyTitle} / ${selected.storyName}`}\n diffEmpty={diffEmptyText(selected.status)}\n diffMeta={percent ?? undefined}\n />\n {design && design.length > 0 ? <DesignReferences items={design} /> : null}\n </VStack>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,SAAS,cAAc,QAAwB;CAC7C,OAAO,WAAW,eAAe,WAAW,aACxC,+BACA;AACN;;AAGA,SAAS,YAAY,OAKqC;CACxD,MAAM,EAAE,SAAS,OAAO,UAAU,aAAa;CAC/C,IAAI,SAAS,SAAS,GAAG,OAAO;CAChC,OACE,oBAAC,WAAD;EACE,OAAM;EACN,qBAAA;EACA,OAAO,SAAS,KAAK,UAAU;GAC7B,OAAO,GAAG,KAAK,WAAW,WAAW,KAAK,KAAK;GAC/C,OAAO,KAAK;GACZ,MAAM,gBAAgB,SAAS,OAAO,KAAK,EAAE;GAC7C,QAAQ,KAAK,OAAO,SAAS;EAC/B,EAAE;CACH,CAAA;AAEL;;AAGA,SAAS,oBAAoB,OAI6B;CACxD,MAAM,EAAE,SAAS,UAAU,SAAS;CACpC,IAAI,CAAC,SAAS,iBAAiB,OAAO;CACtC,MAAM,OAAO,iBAAiB,KAAK,IAAI;CACvC,OACE,oBAAC,QAAD;EACE,SAAQ;EACR,MAAK;EACL,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,SAAS,eAAe,EAAE,GAAG,SAAS;EAC7E,UAAA;CAEO,CAAA;AAEZ;;AAGA,SAAgB,WAAW,OAAwE;CACjG,MAAM,EAAE,SAAS,OAAO,UAAU,aAAa,SAAS,OAAO,QAAQ,wBACrE;CACF,MAAM,WAAW,MAAM,YAAY,CAAC,QAAQ;CAC5C,MAAM,UAAU,YAAY,QAAQ;CACpC,OACE,qBAAC,QAAD,EAAA,UAAA;EACE,qBAAC,OAAD;GAAK,OAAO;GAAZ,UAAA,CACE,qBAAC,MAAD;IAAI,OAAO;IAAX,UAAA;KACG,SAAS;KAAW;KAAI,SAAS;IAChC;GACJ,CAAA,GAAA,qBAAC,KAAD;IAAG,OAAO;IAAV,UAAA;KACE,oBAAC,OAAD;MAAO,MAAM,WAAW,SAAS,MAAM;MAAG,MAAA;MACvC,UAAA,SAAS;KACL,CAAA;KACP,qBAAC,QAAD,EAAA,UAAA;MACG,gBAAgB,QAAQ;MAAE;MAAI,SAAS;MAAc;MAAE,SAAS;KAC7D,EAAA,CAAA;KACL,SAAS,eAAe,QAAQ,SAAS,eAAe,KAAA,IAAY,OACnE,qBAAC,QAAD;MAAM,OAAM;MAAZ,UAAA,CAAoB,SAAS,YAAW,KAAS;;KAElD,UAAU,oBAAC,QAAD;MAAM,OAAM;MAAQ,UAAA;KAAc,CAAA,IAAI;KACjD,oBAAC,qBAAD;MAA8B;MAAmB;MAAU,MAAM;KAAsB,CAAA;IACtF;GACA,CAAA,CAAA;;EACL,oBAAC,aAAD;GAAsB;GAAgB;GAAiB;GAAoB;EAAW,CAAA;EACrF,UAAU,oBAAC,qBAAD,EAAqB,QAAQ,QAAU,CAAA,IAAI;EACtD,oBAAC,iCAAD;GAAiC,UAAU;GAAU,UAAU,MAAM;EAAY,CAAA;EACjF,oBAAC,sBAAD,EAAsB,UAAU,cAAc,UAAU,QAAQ,EAAI,CAAA;EACnE,SAAS,MAAM,SAAS,IACvB,oBAAC,aAAD;GAAoB;GAA4B;EAAsB,CAAA,IACpE;EACH,UAAU,OAAO,SAAS,IAAI,oBAAC,aAAD,EAAa,OAAO,OAAS,CAAA,IAAI;EAChE,oBAAC,cAAD;GACE,aACE,YAAY,SAAS,MAAM,iBAAiB,SAAS,OAAO,UAAU,UAAU,IAAI;GAEtF,YAAY,iBAAiB,SAAS,OAAO,UAAU,OAAO;GAC9D,SAAS,SAAS,WAAW,iBAAiB,SAAS,OAAO,UAAU,MAAM,IAAI;GAClF,SAAS,GAAG,SAAS,WAAW,KAAK,SAAS;GAC9C,WAAW,cAAc,SAAS,MAAM;GACxC,UAAU,WAAW,KAAA;EACtB,CAAA;EACA,UAAU,OAAO,SAAS,IAAI,oBAAC,kBAAD,EAAkB,OAAO,OAAS,CAAA,IAAI;CAC/D,EAAA,CAAA;AAEZ"}
|
|
1
|
+
{"version":3,"file":"build-diff-viewer.mjs","names":[],"sources":["../../src/pages/build-diff-viewer.tsx"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { BaselineReference, DefaultComparison } from \"../routers/baseline-reference.ts\";\nimport { Badge, Button, CompareStage, Segmented, VStack, statusTone } from \"../ui/components.tsx\";\nimport { viewerHead, viewerMeta, viewerTitle } from \"../ui/styles/review-layout.ts\";\nimport { A11yViolationsNotice, a11yCarrierOf } from \"./a11y-violations.tsx\";\nimport { DefaultComparisonView } from \"./build-diff-default.tsx\";\nimport { EnvironmentChangeSnapshotNotice } from \"./build-diff-environment.tsx\";\nimport {\n designSources,\n diffPercent,\n snapshotImageUrl,\n snapshotPageUrl,\n snapshotVariant,\n} from \"./build-diff-model.ts\";\nimport { BaselineReferenceNotice, ComparisonSources } from \"./build-diff-reference.tsx\";\nimport { StaleSnapshotNotice } from \"./build-diff-stale.tsx\";\nimport { LinkedUsage, type LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DesignReferences, DesignStrip, type DesignItem } from \"./design-links.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** Viewer props for the selected snapshot. */\nexport interface DiffViewerProps {\n project: Project;\n build: Build;\n selected: Snapshot;\n /** The selected story/mode's snapshots on every engine (browser tabs when more than one). */\n siblings?: Snapshot[];\n hasBaseline: Record<string, boolean>;\n reference?: BaselineReference;\n defaultComparison?: DefaultComparison | null;\n /** Set when the baseline changed after this snapshot's diff was computed. */\n drifted?: \"stale\" | \"removed\";\n /** Linked packages the story uses (provenance badges + upstream deep links). */\n usage?: LinkedUsageItem[];\n /** Resolved design links of the story's component (open-in-Figma + stale/deleted notices). */\n design?: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** Why a snapshot has no diff image, phrased for the empty pane. */\nfunction diffEmptyText(status: string): string {\n return status === \"unchanged\" || status === \"approved\"\n ? \"No diff — within threshold\"\n : \"No diff yet\";\n}\n\n/** Browser tabs (one link per engine) when the story/mode was captured on several. */\nfunction BrowserTabs(props: {\n project: Project;\n build: Build;\n selected: Snapshot;\n siblings: Snapshot[];\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, build, selected, siblings } = props;\n if (siblings.length < 2) return null;\n return (\n <Segmented\n label=\"Browser\"\n data-browser-tabs\n items={siblings.map((snap) => ({\n label: `${snap.browser ?? \"chromium\"} · ${snap.status}`,\n value: snap.id,\n href: snapshotPageUrl(project, build, snap.id),\n active: snap.id === selected.id,\n }))}\n />\n );\n}\n\n/** Link to the story file's component page, anchored at the story. */\nfunction ViewComponentButton(props: {\n project: Project;\n selected: Snapshot;\n base?: string;\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, selected, base } = props;\n if (!selected.storyImportPath) return null;\n const urls = createUrlBuilder(\"/\", base);\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n href={`${urls.component(project.slug, selected.storyImportPath)}#${selected.storyId}`}\n >\n View component\n </Button>\n );\n}\n\n/** Story heading plus the comparison stage for the selected snapshot. */\nexport function DiffViewer(props: DiffViewerProps): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, selected, drifted, usage, design, publishedBaseDomain } = props;\n const siblings = props.siblings ?? [selected];\n const percent = diffPercent(selected);\n return (\n <VStack>\n <div class={viewerHead}>\n <h2 class={viewerTitle}>\n {selected.storyTitle} / {selected.storyName}\n </h2>\n <p class={viewerMeta}>\n <Badge tone={statusTone(selected.status)} icon>\n {selected.status}\n </Badge>\n <span>\n {snapshotVariant(selected)} · {selected.viewportWidth}×{selected.viewportHeight}\n </span>\n {selected.diffPixels === null || selected.diffPixels === undefined ? null : (\n <span class=\"mono\">{selected.diffPixels} px</span>\n )}\n {percent ? <span class=\"mono\">{percent}</span> : null}\n <ViewComponentButton project={project} selected={selected} base={publishedBaseDomain} />\n </p>\n </div>\n {props.reference ? <BaselineReferenceNotice reference={props.reference} /> : null}\n <BrowserTabs project={project} build={build} selected={selected} siblings={siblings} />\n {drifted ? <StaleSnapshotNotice status={drifted} /> : null}\n <EnvironmentChangeSnapshotNotice snapshot={selected} migrated={build.isDefault} />\n <A11yViolationsNotice snapshot={a11yCarrierOf(selected, siblings)} />\n {usage && usage.length > 0 ? (\n <LinkedUsage usage={usage} publishedBaseDomain={publishedBaseDomain} />\n ) : null}\n {design && design.length > 0 ? <DesignStrip items={design} /> : null}\n <PrimaryComparison {...props} />\n <SecondaryComparison {...props} />\n {design && design.length > 0 ? <DesignReferences items={design} /> : null}\n </VStack>\n );\n}\n\nfunction SecondaryComparison(\n props: DiffViewerProps,\n): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n if (!props.defaultComparison) return null;\n return (\n <DefaultComparisonView\n {...props}\n snapshot={props.selected}\n comparison={props.defaultComparison}\n />\n );\n}\n\nfunction baselineUrl(props: DiffViewerProps): string | null {\n const { hasBaseline, selected, reference, project, build } = props;\n return hasBaseline[selected.id] &&\n reference?.kind !== \"none\" &&\n !(reference?.kind === \"known\" && !reference.imageAvailable) &&\n !(reference?.kind === \"unavailable\" && selected.baselineVersion !== null)\n ? snapshotImageUrl(project, build, selected, \"baseline\")\n : null;\n}\n\nfunction PrimaryComparison(props: DiffViewerProps): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, selected, reference } = props;\n const stage = (\n <CompareStage\n baselineSrc={baselineUrl(props)}\n baselineEmpty={\n reference?.kind === \"unavailable\" ||\n (reference?.kind === \"known\" && !reference.imageAvailable)\n ? \"The original baseline image is unavailable.\"\n : undefined\n }\n currentSrc={snapshotImageUrl(project, build, selected, \"image\")}\n diffSrc={selected.diffPath ? snapshotImageUrl(project, build, selected, \"diff\") : null}\n subject={`${selected.storyTitle} / ${selected.storyName}`}\n diffEmpty={diffEmptyText(selected.status)}\n diffMeta={diffPercent(selected) ?? undefined}\n />\n );\n return (\n <ComparisonSources\n baseline={stage}\n sources={designSources(props.design ?? [])}\n currentSrc={snapshotImageUrl(project, build, selected, \"image\")}\n subject={`${selected.storyTitle} / ${selected.storyName}`}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@ import { DiffNav } from "./build-diff-nav.mjs";
|
|
|
14
14
|
import { ShortcutsDialog } from "./build-diff-shortcuts.mjs";
|
|
15
15
|
import { DiffViewer } from "./build-diff-viewer.mjs";
|
|
16
16
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
17
|
-
|
|
17
|
+
// src/pages/build-diff.tsx
|
|
18
18
|
/** The snapshot the review page shows: the requested one, else the first needing review. */
|
|
19
19
|
function selectedSnapshot(snapshots, selectedId) {
|
|
20
20
|
return snapshots.find((s) => s.id === selectedId) ?? snapshots.find((s) => s.status === "changed" || s.status === "new") ?? snapshots[0];
|
|
@@ -39,6 +39,8 @@ function DiffReviewGrid(props) {
|
|
|
39
39
|
selected,
|
|
40
40
|
siblings: groupOf(snapshots, selected.id)?.members,
|
|
41
41
|
hasBaseline,
|
|
42
|
+
reference: props.reference,
|
|
43
|
+
defaultComparison: props.defaultComparison,
|
|
42
44
|
drifted: drifted[selected.id],
|
|
43
45
|
usage,
|
|
44
46
|
design,
|
|
@@ -78,7 +80,7 @@ function reviewNav(project, build) {
|
|
|
78
80
|
}
|
|
79
81
|
/** Three-up diff review page: baseline, current, and diff with keyboard review. */
|
|
80
82
|
function renderBuildDiffPage(data) {
|
|
81
|
-
const { project, build, snapshots,
|
|
83
|
+
const { project, build, snapshots, selectedId, canReview, drifted } = data;
|
|
82
84
|
const selected = selectedSnapshot(snapshots, selectedId);
|
|
83
85
|
const pendingGroups = reviewProgress(snapshots).pending;
|
|
84
86
|
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
@@ -109,23 +111,15 @@ function renderBuildDiffPage(data) {
|
|
|
109
111
|
title: "No snapshots",
|
|
110
112
|
description: "This build has no snapshots yet. Capture may still be running."
|
|
111
113
|
}) : /* @__PURE__ */ jsx(DiffReviewGrid, {
|
|
112
|
-
|
|
113
|
-
build,
|
|
114
|
-
snapshots,
|
|
115
|
-
comments,
|
|
114
|
+
...data,
|
|
116
115
|
selected,
|
|
117
|
-
canReview,
|
|
118
|
-
hasBaseline,
|
|
119
|
-
drifted,
|
|
120
116
|
usage: data.usage ?? [],
|
|
121
|
-
design: data.design ?? []
|
|
122
|
-
publishedBaseDomain: data.publishedBaseDomain
|
|
117
|
+
design: data.design ?? []
|
|
123
118
|
}),
|
|
124
119
|
/* @__PURE__ */ jsx(ShortcutsDialog, {})
|
|
125
120
|
]
|
|
126
121
|
});
|
|
127
122
|
}
|
|
128
|
-
//#endregion
|
|
129
123
|
export { renderBuildDiffPage, selectedSnapshot };
|
|
130
124
|
|
|
131
125
|
//# sourceMappingURL=build-diff.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-diff.mjs","names":[],"sources":["../../src/pages/build-diff.tsx"],"sourcesContent":["import type { Build } from \"@storyshelf/core/schema\";\nimport type { Comment } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { InsightPanelData } from \"../insights/panel.ts\";\nimport { EmptyState } from \"../ui/components.tsx\";\nimport { DocumentLayout, type NavConfig, type RenderedContent } from \"../ui/document.tsx\";\nimport { canvasColumn, workspace } from \"../ui/styles/review-layout.ts\";\nimport { DiffActionBar } from \"./build-diff-actions.tsx\";\nimport { DiffComments } from \"./build-diff-comments.tsx\";\nimport { ReviewNotices } from \"./build-diff-environment.tsx\";\nimport { DiffHeader } from \"./build-diff-header.tsx\";\nimport { InsightSlot } from \"./build-diff-insight.tsx\";\nimport { groupOf, reviewProgress } from \"./build-diff-model.ts\";\nimport { DiffNav } from \"./build-diff-nav.tsx\";\nimport { ShortcutsDialog } from \"./build-diff-shortcuts.tsx\";\nimport type { LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DiffViewer } from \"./build-diff-viewer.tsx\";\nimport type { DesignItem } from \"./design-links.tsx\";\n\n/** Data required to render the three-up build diff review page. */\nexport interface BuildDiffData {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selectedId?: string;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n /** Open snapshots whose baseline changed after their diff was computed. */\n drifted: Record<string, \"stale\" | \"removed\">;\n /** AI triage panel data; omitted/null hides the panel. */\n insight?: InsightPanelData | null;\n /** Linked packages the selected snapshot's story uses; omitted/empty hides the badges. */\n usage?: LinkedUsageItem[];\n /** Design links the selected snapshot's component resolves to; omitted/empty hides the strip. */\n design?: DesignItem[];\n /** Base domain for published Storybook deep links. */\n publishedBaseDomain?: string;\n}\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ninterface DiffReviewGridProps {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selected?: Snapshot;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n drifted: Record<string, \"stale\" | \"removed\">;\n usage: LinkedUsageItem[];\n design: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** The snapshot the review page shows: the requested one, else the first needing review. */\nexport function selectedSnapshot(snapshots: Snapshot[], selectedId?: string): Snapshot | undefined {\n return (\n snapshots.find((s) => s.id === selectedId) ??\n snapshots.find((s) => s.status === \"changed\" || s.status === \"new\") ??\n snapshots[0]\n );\n}\n\n/** Snapshot nav plus the viewer/comments column for the selected snapshot. */\nfunction DiffReviewGrid(\n props: DiffReviewGridProps,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, snapshots, comments, selected, canReview, hasBaseline, drifted } = props;\n const { usage, design, publishedBaseDomain } = props;\n return (\n <div class={workspace}>\n <DiffNav project={project} build={build} snapshots={snapshots} selectedId={selected?.id} />\n <div class={canvasColumn}>\n {selected ? (\n <>\n <DiffViewer\n project={project}\n build={build}\n selected={selected}\n siblings={groupOf(snapshots, selected.id)?.members}\n hasBaseline={hasBaseline}\n drifted={drifted[selected.id]}\n usage={usage}\n design={design}\n publishedBaseDomain={publishedBaseDomain}\n />\n {project.reviewEnabled === false ? null : (\n <DiffComments\n project={project}\n build={build}\n selectedId={selected.id}\n selectedStoryId={selected.storyId}\n comments={comments}\n canReview={canReview}\n />\n )}\n <DiffActionBar\n project={project}\n build={build}\n snapshots={snapshots}\n selected={selected}\n canReview={canReview}\n drifted={drifted}\n />\n </>\n ) : (\n <EmptyState description=\"Select a snapshot to review.\" />\n )}\n </div>\n </div>\n );\n}\n\n/** Sidebar/breadcrumb context for the review page: Builds / <build> / Review. */\nfunction reviewNav(project: Project, build: Build): NavConfig {\n return {\n active: \"builds\",\n projectSlug: project.slug,\n projectName: project.name,\n trail: [\n {\n label: `${build.gitBranch} · ${build.gitSha.slice(0, 7)}`,\n href: `/projects/${project.slug}/builds/${build.id}`,\n },\n { label: \"Review\" },\n ],\n };\n}\n\n/** Three-up diff review page: baseline, current, and diff with keyboard review. */\nexport function renderBuildDiffPage(data: BuildDiffData): RenderedContent {\n const { project, build, snapshots,
|
|
1
|
+
{"version":3,"file":"build-diff.mjs","names":[],"sources":["../../src/pages/build-diff.tsx"],"sourcesContent":["import type { Build } from \"@storyshelf/core/schema\";\nimport type { Comment } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { InsightPanelData } from \"../insights/panel.ts\";\nimport type { BaselineReference, DefaultComparison } from \"../routers/baseline-reference.ts\";\nimport { EmptyState } from \"../ui/components.tsx\";\nimport { DocumentLayout, type NavConfig, type RenderedContent } from \"../ui/document.tsx\";\nimport { canvasColumn, workspace } from \"../ui/styles/review-layout.ts\";\nimport { DiffActionBar } from \"./build-diff-actions.tsx\";\nimport { DiffComments } from \"./build-diff-comments.tsx\";\nimport { ReviewNotices } from \"./build-diff-environment.tsx\";\nimport { DiffHeader } from \"./build-diff-header.tsx\";\nimport { InsightSlot } from \"./build-diff-insight.tsx\";\nimport { groupOf, reviewProgress } from \"./build-diff-model.ts\";\nimport { DiffNav } from \"./build-diff-nav.tsx\";\nimport { ShortcutsDialog } from \"./build-diff-shortcuts.tsx\";\nimport type { LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DiffViewer } from \"./build-diff-viewer.tsx\";\nimport type { DesignItem } from \"./design-links.tsx\";\n\n/** Data required to render the three-up build diff review page. */\nexport interface BuildDiffData {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selectedId?: string;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n reference?: BaselineReference;\n defaultComparison?: DefaultComparison | null;\n /** Open snapshots whose baseline changed after their diff was computed. */\n drifted: Record<string, \"stale\" | \"removed\">;\n /** AI triage panel data; omitted/null hides the panel. */\n insight?: InsightPanelData | null;\n /** Linked packages the selected snapshot's story uses; omitted/empty hides the badges. */\n usage?: LinkedUsageItem[];\n /** Design links the selected snapshot's component resolves to; omitted/empty hides the strip. */\n design?: DesignItem[];\n /** Base domain for published Storybook deep links. */\n publishedBaseDomain?: string;\n}\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ninterface DiffReviewGridProps {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selected?: Snapshot;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n reference?: BaselineReference;\n defaultComparison?: DefaultComparison | null;\n drifted: Record<string, \"stale\" | \"removed\">;\n usage: LinkedUsageItem[];\n design: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** The snapshot the review page shows: the requested one, else the first needing review. */\nexport function selectedSnapshot(snapshots: Snapshot[], selectedId?: string): Snapshot | undefined {\n return (\n snapshots.find((s) => s.id === selectedId) ??\n snapshots.find((s) => s.status === \"changed\" || s.status === \"new\") ??\n snapshots[0]\n );\n}\n\n/** Snapshot nav plus the viewer/comments column for the selected snapshot. */\nfunction DiffReviewGrid(\n props: DiffReviewGridProps,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, snapshots, comments, selected, canReview, hasBaseline, drifted } = props;\n const { usage, design, publishedBaseDomain } = props;\n return (\n <div class={workspace}>\n <DiffNav project={project} build={build} snapshots={snapshots} selectedId={selected?.id} />\n <div class={canvasColumn}>\n {selected ? (\n <>\n <DiffViewer\n project={project}\n build={build}\n selected={selected}\n siblings={groupOf(snapshots, selected.id)?.members}\n hasBaseline={hasBaseline}\n reference={props.reference}\n defaultComparison={props.defaultComparison}\n drifted={drifted[selected.id]}\n usage={usage}\n design={design}\n publishedBaseDomain={publishedBaseDomain}\n />\n {project.reviewEnabled === false ? null : (\n <DiffComments\n project={project}\n build={build}\n selectedId={selected.id}\n selectedStoryId={selected.storyId}\n comments={comments}\n canReview={canReview}\n />\n )}\n <DiffActionBar\n project={project}\n build={build}\n snapshots={snapshots}\n selected={selected}\n canReview={canReview}\n drifted={drifted}\n />\n </>\n ) : (\n <EmptyState description=\"Select a snapshot to review.\" />\n )}\n </div>\n </div>\n );\n}\n\n/** Sidebar/breadcrumb context for the review page: Builds / <build> / Review. */\nfunction reviewNav(project: Project, build: Build): NavConfig {\n return {\n active: \"builds\",\n projectSlug: project.slug,\n projectName: project.name,\n trail: [\n {\n label: `${build.gitBranch} · ${build.gitSha.slice(0, 7)}`,\n href: `/projects/${project.slug}/builds/${build.id}`,\n },\n { label: \"Review\" },\n ],\n };\n}\n\n/** Three-up diff review page: baseline, current, and diff with keyboard review. */\nexport function renderBuildDiffPage(data: BuildDiffData): RenderedContent {\n const { project, build, snapshots, selectedId, canReview, drifted } = data;\n const selected = selectedSnapshot(snapshots, selectedId);\n const pendingGroups = reviewProgress(snapshots).pending;\n return (\n <DocumentLayout\n title={`Review · ${build.gitBranch}`}\n nav={reviewNav(project, build)}\n layout=\"wide\"\n >\n <DiffHeader\n project={project}\n build={build}\n snapshots={snapshots}\n pendingCount={pendingGroups}\n canReview={canReview}\n />\n <ReviewNotices\n project={project}\n build={build}\n snapshots={snapshots}\n drifted={drifted}\n canReview={canReview}\n />\n <InsightSlot project={project} build={build} data={data.insight} />\n {snapshots.length === 0 ? (\n <EmptyState\n title=\"No snapshots\"\n description=\"This build has no snapshots yet. Capture may still be running.\"\n />\n ) : (\n <DiffReviewGrid\n {...data}\n selected={selected}\n usage={data.usage ?? []}\n design={data.design ?? []}\n />\n )}\n <ShortcutsDialog />\n </DocumentLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgEA,SAAgB,iBAAiB,WAAuB,YAA2C;CACjG,OACE,UAAU,MAAM,MAAM,EAAE,OAAO,UAAU,KACzC,UAAU,MAAM,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,KAAK,KAClE,UAAU;AAEd;;AAGA,SAAS,eACP,OACgD;CAChD,MAAM,EAAE,SAAS,OAAO,WAAW,UAAU,UAAU,WAAW,aAAa,YAAY;CAC3F,MAAM,EAAE,OAAO,QAAQ,wBAAwB;CAC/C,OACE,qBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA,CACE,oBAAC,SAAD;GAAkB;GAAgB;GAAkB;GAAW,YAAY,UAAU;EAAK,CAAA,GAC1F,oBAAC,OAAD;GAAK,OAAO;GACT,UAAA,WACC,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,YAAD;KACW;KACF;KACG;KACV,UAAU,QAAQ,WAAW,SAAS,EAAE,CAAC,EAAE;KAC9B;KACb,WAAW,MAAM;KACjB,mBAAmB,MAAM;KACzB,SAAS,QAAQ,SAAS;KACnB;KACC;KACa;IACtB,CAAA;IACA,QAAQ,kBAAkB,QAAQ,OACjC,oBAAC,cAAD;KACW;KACF;KACP,YAAY,SAAS;KACrB,iBAAiB,SAAS;KAChB;KACC;IACZ,CAAA;IAEH,oBAAC,eAAD;KACW;KACF;KACI;KACD;KACC;KACF;IACV,CAAA;GACD,EAAA,CAAA,IAEF,oBAAC,YAAD,EAAY,aAAY,+BAAgC,CAAA;EAEvD,CAAA,CACF;;AAET;;AAGA,SAAS,UAAU,SAAkB,OAAyB;CAC5D,OAAO;EACL,QAAQ;EACR,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,OAAO,CACL;GACE,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,OAAO,MAAM,GAAG,CAAC;GACtD,MAAM,aAAa,QAAQ,KAAK,UAAU,MAAM;EAClD,GACA,EAAE,OAAO,SAAS,CACpB;CACF;AACF;;AAGA,SAAgB,oBAAoB,MAAsC;CACxE,MAAM,EAAE,SAAS,OAAO,WAAW,YAAY,WAAW,YAAY;CACtE,MAAM,WAAW,iBAAiB,WAAW,UAAU;CACvD,MAAM,gBAAgB,eAAe,SAAS,CAAC,CAAC;CAChD,OACE,qBAAC,gBAAD;EACE,OAAO,YAAY,MAAM;EACzB,KAAK,UAAU,SAAS,KAAK;EAC7B,QAAO;EAHT,UAAA;GAKE,oBAAC,YAAD;IACW;IACF;IACI;IACX,cAAc;IACH;GACZ,CAAA;GACD,oBAAC,eAAD;IACW;IACF;IACI;IACF;IACE;GACZ,CAAA;GACD,oBAAC,aAAD;IAAsB;IAAgB;IAAO,MAAM,KAAK;GAAU,CAAA;GACjE,UAAU,WAAW,IACpB,oBAAC,YAAD;IACE,OAAM;IACN,aAAY;GACb,CAAA,IAED,oBAAC,gBAAD;IACE,GAAI;IACM;IACV,OAAO,KAAK,SAAS,CAAC;IACtB,QAAQ,KAAK,UAAU,CAAC;GACzB,CAAA;GAEH,oBAAC,iBAAD,CAAkB,CAAA;EACJ;;AAEpB"}
|
|
@@ -6,7 +6,7 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
|
6
6
|
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
7
7
|
import { summarizeTests } from "@storyshelf/core/capture";
|
|
8
8
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
9
|
-
|
|
9
|
+
// src/pages/build-tests.tsx
|
|
10
10
|
/** The line under the card title: counts, and what a failure means for the build. */
|
|
11
11
|
function testsSummaryNote(results) {
|
|
12
12
|
const { total, passed, failed } = summarizeTests(results);
|
|
@@ -68,7 +68,6 @@ function TestRow(props) {
|
|
|
68
68
|
}) : null]
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
//#endregion
|
|
72
71
|
export { TestsCard, testsSummaryNote };
|
|
73
72
|
|
|
74
73
|
//# sourceMappingURL=build-tests.mjs.map
|
|
@@ -16,7 +16,7 @@ import { ComponentBuilds, StoriesTab } from "./component-stories.mjs";
|
|
|
16
16
|
import { worstStatus } from "@storyshelf/core/models";
|
|
17
17
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
18
18
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
19
|
-
|
|
19
|
+
// src/pages/component-detail.tsx
|
|
20
20
|
const TAB_LABELS = {
|
|
21
21
|
stories: "Stories",
|
|
22
22
|
states: "States",
|
|
@@ -243,7 +243,6 @@ function LinkedTab(props) {
|
|
|
243
243
|
publishedBaseDomain: data.publishedBaseDomain
|
|
244
244
|
})] });
|
|
245
245
|
}
|
|
246
|
-
//#endregion
|
|
247
246
|
export { componentTabs, renderComponentPage };
|
|
248
247
|
|
|
249
248
|
//# sourceMappingURL=component-detail.mjs.map
|
|
@@ -11,7 +11,7 @@ import { PREVIEW_TOKEN_REFRESH, isSignedPreviewBase } from "../routers/preview-b
|
|
|
11
11
|
import { StoryComments } from "./review-comments.mjs";
|
|
12
12
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
13
13
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
14
|
-
|
|
14
|
+
// src/pages/component-stories.tsx
|
|
15
15
|
/** Stories tab: the selected story in a sandboxed preview, and every story and docs entry. */
|
|
16
16
|
function StoriesTab(props) {
|
|
17
17
|
const { data } = props;
|
|
@@ -162,7 +162,6 @@ function changeParts(row) {
|
|
|
162
162
|
function storybookBase(data) {
|
|
163
163
|
return createUrlBuilder("/", data.publishedBaseDomain).storybookBuild(data.project.slug, data.catalog.build.id);
|
|
164
164
|
}
|
|
165
|
-
//#endregion
|
|
166
165
|
export { ComponentBuilds, StoriesTab };
|
|
167
166
|
|
|
168
167
|
//# sourceMappingURL=component-stories.mjs.map
|
|
@@ -10,7 +10,7 @@ import { Table } from "../ui/table.mjs";
|
|
|
10
10
|
import { DocumentLayout } from "../ui/document.mjs";
|
|
11
11
|
import { BuildModel, ProjectModel } from "@storyshelf/core/models";
|
|
12
12
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
13
|
-
|
|
13
|
+
// src/pages/compute-jobs.tsx
|
|
14
14
|
/** Build statuses the retry button of the jobs table offers. */
|
|
15
15
|
const RETRYABLE = /* @__PURE__ */ new Set([
|
|
16
16
|
"failed",
|
|
@@ -146,7 +146,6 @@ async function renderComputeJobsPage(slug, queueView, canRetry) {
|
|
|
146
146
|
]
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
|
-
//#endregion
|
|
150
149
|
export { renderActiveQueue, renderComputeJobsPage };
|
|
151
150
|
|
|
152
151
|
//# sourceMappingURL=compute-jobs.mjs.map
|
|
@@ -8,7 +8,7 @@ import { Dropdown, DropdownItem } from "../ui/dropdown.mjs";
|
|
|
8
8
|
import { DesignPreview, DesignReference } from "../ui/design-reference.mjs";
|
|
9
9
|
import { csrfField } from "../ui/csrf-field.mjs";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
11
|
-
|
|
11
|
+
// src/pages/design-links.tsx
|
|
12
12
|
/**
|
|
13
13
|
* Design actions for a component's link(s). A link with a cached render opens a
|
|
14
14
|
* click popover (preview + `Open in <provider>`); others are plain deep links.
|
|
@@ -176,7 +176,6 @@ function ProjectDesignLinks(props) {
|
|
|
176
176
|
children: link.label
|
|
177
177
|
}, link.url)) });
|
|
178
178
|
}
|
|
179
|
-
//#endregion
|
|
180
179
|
export { DesignButtons, DesignReferences, DesignStrip, ProjectDesignLinks };
|
|
181
180
|
|
|
182
181
|
//# sourceMappingURL=design-links.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/pages/environment-change.ts
|
|
5
5
|
/** What changed, in words a reviewer recognises. */
|
|
6
6
|
const FIELD_LABELS = {
|
|
7
7
|
engine: "browser engine",
|
|
@@ -47,7 +47,6 @@ function browserVersionOf(changes) {
|
|
|
47
47
|
to
|
|
48
48
|
} } : {};
|
|
49
49
|
}
|
|
50
|
-
//#endregion
|
|
51
50
|
export { environmentChangeOf, labelOf, summariseEnvironmentChanges };
|
|
52
51
|
|
|
53
52
|
//# sourceMappingURL=environment-change.mjs.map
|
|
@@ -7,7 +7,7 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
|
7
7
|
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
8
8
|
import { csrfField } from "../ui/csrf-field.mjs";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
10
|
-
|
|
10
|
+
// src/pages/health-panel.tsx
|
|
11
11
|
/** Badge tone for a health verdict. */
|
|
12
12
|
function healthTone(verdict) {
|
|
13
13
|
if (verdict === "healthy") return "success";
|
|
@@ -120,7 +120,6 @@ function HealthSlot(props) {
|
|
|
120
120
|
data: props.data
|
|
121
121
|
}) : null;
|
|
122
122
|
}
|
|
123
|
-
//#endregion
|
|
124
123
|
export { HealthPanel, HealthSlot, healthTone };
|
|
125
124
|
|
|
126
125
|
//# sourceMappingURL=health-panel.mjs.map
|