@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.
Files changed (264) hide show
  1. package/dist/app-types.d.mts +1 -3
  2. package/dist/asset-manifest.mjs +1 -2
  3. package/dist/assets/htmx.mjs +1 -2
  4. package/dist/assets/mark.mjs +1 -2
  5. package/dist/boot-tasks.mjs +1 -2
  6. package/dist/build-state-flags.mjs +1 -2
  7. package/dist/build-state-setup.mjs +1 -2
  8. package/dist/capture-alerts.mjs +1 -2
  9. package/dist/capture-process.mjs +1 -2
  10. package/dist/capture-setup.mjs +1 -2
  11. package/dist/connections.mjs +1 -2
  12. package/dist/consumer-setup.mjs +1 -2
  13. package/dist/credential-keys.mjs +1 -2
  14. package/dist/credentials.mjs +1 -2
  15. package/dist/git-status.mjs +1 -2
  16. package/dist/hidden-data.mjs +1 -2
  17. package/dist/index.d.mts +0 -2
  18. package/dist/index.mjs +2 -3
  19. package/dist/insights/budget.mjs +1 -2
  20. package/dist/insights/deps.mjs +1 -2
  21. package/dist/insights/errors.mjs +1 -2
  22. package/dist/insights/health-panel.mjs +0 -1
  23. package/dist/insights/job.mjs +1 -2
  24. package/dist/insights/load-health.mjs +1 -2
  25. package/dist/insights/load-triage.mjs +1 -2
  26. package/dist/insights/panel.mjs +1 -2
  27. package/dist/insights/request.mjs +1 -2
  28. package/dist/insights/stale.mjs +1 -2
  29. package/dist/insights/usage-report.mjs +1 -2
  30. package/dist/insights/view.mjs +1 -2
  31. package/dist/job-canceller.mjs +1 -2
  32. package/dist/job-queue.mjs +1 -2
  33. package/dist/jobs-setup.mjs +1 -2
  34. package/dist/lifecycle.mjs +2 -3
  35. package/dist/lifecycle.mjs.map +1 -1
  36. package/dist/middleware/auth-gate.mjs +1 -2
  37. package/dist/middleware/csrf.mjs +1 -2
  38. package/dist/middleware/rate-limit.mjs +1 -2
  39. package/dist/middleware/request-log.mjs +1 -2
  40. package/dist/middleware/require-review.mjs +1 -2
  41. package/dist/middleware/require-testing.mjs +1 -2
  42. package/dist/middleware/server-timing.mjs +1 -2
  43. package/dist/middleware/setup-gate.mjs +1 -2
  44. package/dist/middleware/store-scope.mjs +1 -2
  45. package/dist/notifications-pause.mjs +1 -2
  46. package/dist/notify.d.mts +1 -3
  47. package/dist/notify.mjs +1 -2
  48. package/dist/pages/a11y-violations.mjs +1 -2
  49. package/dist/pages/admin-ai.mjs +1 -2
  50. package/dist/pages/admin-system-origin.mjs +19 -10
  51. package/dist/pages/admin-system-origin.mjs.map +1 -1
  52. package/dist/pages/admin-system-targets.mjs +1 -2
  53. package/dist/pages/admin-system.mjs +1 -2
  54. package/dist/pages/build-detail.mjs +1 -2
  55. package/dist/pages/build-diff-actions.mjs +1 -2
  56. package/dist/pages/build-diff-comments.mjs +1 -2
  57. package/dist/pages/build-diff-default.mjs +53 -0
  58. package/dist/pages/build-diff-default.mjs.map +1 -0
  59. package/dist/pages/build-diff-environment.mjs +1 -2
  60. package/dist/pages/build-diff-header.mjs +1 -2
  61. package/dist/pages/build-diff-insight.mjs +1 -2
  62. package/dist/pages/build-diff-model.mjs +20 -3
  63. package/dist/pages/build-diff-model.mjs.map +1 -1
  64. package/dist/pages/build-diff-nav.mjs +1 -2
  65. package/dist/pages/build-diff-reference.mjs +84 -0
  66. package/dist/pages/build-diff-reference.mjs.map +1 -0
  67. package/dist/pages/build-diff-shortcuts.mjs +1 -2
  68. package/dist/pages/build-diff-stale.mjs +1 -2
  69. package/dist/pages/build-diff-usage.mjs +1 -2
  70. package/dist/pages/build-diff-viewer.mjs +38 -12
  71. package/dist/pages/build-diff-viewer.mjs.map +1 -1
  72. package/dist/pages/build-diff.mjs +6 -12
  73. package/dist/pages/build-diff.mjs.map +1 -1
  74. package/dist/pages/build-tests.mjs +1 -2
  75. package/dist/pages/component-detail.mjs +1 -2
  76. package/dist/pages/component-stories.mjs +1 -2
  77. package/dist/pages/compute-jobs.mjs +1 -2
  78. package/dist/pages/design-links.mjs +1 -2
  79. package/dist/pages/environment-change.mjs +1 -2
  80. package/dist/pages/health-panel.mjs +1 -2
  81. package/dist/pages/invite.mjs +1 -2
  82. package/dist/pages/label-detail.mjs +1 -2
  83. package/dist/pages/library-grid.mjs +1 -2
  84. package/dist/pages/library-model.mjs +1 -2
  85. package/dist/pages/library-snapshots.mjs +1 -2
  86. package/dist/pages/library.mjs +1 -2
  87. package/dist/pages/login.mjs +1 -2
  88. package/dist/pages/notifications-paused.mjs +1 -2
  89. package/dist/pages/passkey-ceremony.mjs +1 -2
  90. package/dist/pages/profile.mjs +1 -2
  91. package/dist/pages/project-builds-table.mjs +1 -2
  92. package/dist/pages/project-builds.mjs +1 -2
  93. package/dist/pages/project-create.mjs +1 -2
  94. package/dist/pages/project-settings.mjs +1 -2
  95. package/dist/pages/projects-card.mjs +1 -2
  96. package/dist/pages/projects.mjs +1 -2
  97. package/dist/pages/review-changeset.mjs +1 -2
  98. package/dist/pages/review-comments.mjs +1 -2
  99. package/dist/pages/review-verdict.mjs +1 -2
  100. package/dist/pages/root.mjs +1 -2
  101. package/dist/pages/settings-ai.mjs +1 -2
  102. package/dist/pages/settings-capture.mjs +1 -2
  103. package/dist/pages/settings-design-credential.mjs +1 -2
  104. package/dist/pages/settings-design-links.mjs +1 -2
  105. package/dist/pages/settings-design.mjs +1 -2
  106. package/dist/pages/settings-general.mjs +1 -2
  107. package/dist/pages/settings-labels.mjs +1 -2
  108. package/dist/pages/settings-links.mjs +1 -2
  109. package/dist/pages/settings-members.mjs +1 -2
  110. package/dist/pages/settings-notifications-git.mjs +1 -2
  111. package/dist/pages/settings-notifications.mjs +1 -2
  112. package/dist/pages/settings-tests.mjs +1 -2
  113. package/dist/pages/settings-tokens.mjs +1 -2
  114. package/dist/pages/settings-webhooks.mjs +1 -2
  115. package/dist/pages/storybook.mjs +1 -2
  116. package/dist/pages/target-choices.mjs +1 -2
  117. package/dist/publish-setup.mjs +1 -2
  118. package/dist/retention-timer.mjs +10 -11
  119. package/dist/retention-timer.mjs.map +1 -1
  120. package/dist/review-events.mjs +1 -2
  121. package/dist/routers/admin-pages.mjs +1 -2
  122. package/dist/routers/admin-public-origin.mjs +10 -3
  123. package/dist/routers/admin-public-origin.mjs.map +1 -1
  124. package/dist/routers/admin.mjs +8 -4
  125. package/dist/routers/admin.mjs.map +1 -1
  126. package/dist/routers/assets.mjs +1 -2
  127. package/dist/routers/attempts.mjs +1 -2
  128. package/dist/routers/auth-sync.mjs +1 -2
  129. package/dist/routers/auth.mjs +1 -2
  130. package/dist/routers/baseline-guard.mjs +2 -3
  131. package/dist/routers/baseline-guard.mjs.map +1 -1
  132. package/dist/routers/baseline-reference.mjs +72 -0
  133. package/dist/routers/baseline-reference.mjs.map +1 -0
  134. package/dist/routers/baselines.handlers.mjs +1 -2
  135. package/dist/routers/baselines.mjs +1 -2
  136. package/dist/routers/build-cancel.mjs +1 -2
  137. package/dist/routers/build-changeset.mjs +1 -2
  138. package/dist/routers/build-reviews.handlers.mjs +1 -2
  139. package/dist/routers/build-reviews.mjs +1 -2
  140. package/dist/routers/builds.handlers.mjs +23 -9
  141. package/dist/routers/builds.handlers.mjs.map +1 -1
  142. package/dist/routers/builds.mjs +7 -3
  143. package/dist/routers/builds.mjs.map +1 -1
  144. package/dist/routers/capture-cache.mjs +29 -0
  145. package/dist/routers/capture-cache.mjs.map +1 -0
  146. package/dist/routers/capture-config.mjs +1 -2
  147. package/dist/routers/comments.mjs +1 -2
  148. package/dist/routers/comparison-image.mjs +29 -0
  149. package/dist/routers/comparison-image.mjs.map +1 -0
  150. package/dist/routers/component-catalog.handlers.mjs +1 -2
  151. package/dist/routers/component.handlers.mjs +1 -2
  152. package/dist/routers/connections.mjs +1 -2
  153. package/dist/routers/design-connect.handlers.mjs +1 -2
  154. package/dist/routers/design.handlers.mjs +1 -2
  155. package/dist/routers/flash.mjs +1 -2
  156. package/dist/routers/health-report.mjs +1 -2
  157. package/dist/routers/health.mjs +1 -2
  158. package/dist/routers/helpers.mjs +1 -2
  159. package/dist/routers/htmx.mjs +1 -2
  160. package/dist/routers/insights-ui.mjs +1 -2
  161. package/dist/routers/insights.handlers.mjs +1 -2
  162. package/dist/routers/insights.mjs +1 -2
  163. package/dist/routers/labels.mjs +1 -2
  164. package/dist/routers/links.handlers.mjs +1 -2
  165. package/dist/routers/links.mjs +1 -2
  166. package/dist/routers/media.mjs +14 -5
  167. package/dist/routers/media.mjs.map +1 -1
  168. package/dist/routers/members.mjs +1 -2
  169. package/dist/routers/notification-channels.mjs +1 -2
  170. package/dist/routers/notification-subscriptions.mjs +1 -2
  171. package/dist/routers/oauth-state.mjs +1 -2
  172. package/dist/routers/preview-base.mjs +1 -2
  173. package/dist/routers/preview-token.mjs +1 -2
  174. package/dist/routers/profile.mjs +1 -2
  175. package/dist/routers/projects.mjs +1 -2
  176. package/dist/routers/publish-upload.mjs +19 -12
  177. package/dist/routers/publish-upload.mjs.map +1 -1
  178. package/dist/routers/published-access.mjs +1 -2
  179. package/dist/routers/published-compression.mjs +109 -0
  180. package/dist/routers/published-compression.mjs.map +1 -0
  181. package/dist/routers/published-files.mjs +26 -9
  182. package/dist/routers/published-files.mjs.map +1 -1
  183. package/dist/routers/review-forms.mjs +1 -2
  184. package/dist/routers/review-next.mjs +1 -2
  185. package/dist/routers/review-section.handlers.mjs +1 -2
  186. package/dist/routers/review-switch-off.mjs +1 -2
  187. package/dist/routers/schemas.mjs +1 -2
  188. package/dist/routers/scoped-models.mjs +1 -2
  189. package/dist/routers/settings-ai.mjs +1 -2
  190. package/dist/routers/settings-capture.mjs +1 -2
  191. package/dist/routers/settings-design.mjs +1 -2
  192. package/dist/routers/settings-general.mjs +1 -2
  193. package/dist/routers/settings-git-channel.mjs +1 -2
  194. package/dist/routers/settings-labels.mjs +1 -2
  195. package/dist/routers/settings-links.mjs +1 -2
  196. package/dist/routers/settings-members.mjs +1 -2
  197. package/dist/routers/settings-notifications.mjs +1 -2
  198. package/dist/routers/settings-tokens.mjs +1 -2
  199. package/dist/routers/settings-webhooks.mjs +1 -2
  200. package/dist/routers/settings.handlers.mjs +1 -2
  201. package/dist/routers/settings.mjs +1 -2
  202. package/dist/routers/signed-storybook.mjs +5 -3
  203. package/dist/routers/signed-storybook.mjs.map +1 -1
  204. package/dist/routers/snapshot-groups.mjs +1 -2
  205. package/dist/routers/snapshots.mjs +1 -2
  206. package/dist/routers/storybook.mjs +21 -17
  207. package/dist/routers/storybook.mjs.map +1 -1
  208. package/dist/routers/tokens.mjs +1 -2
  209. package/dist/routers/ui.mjs +4 -2
  210. package/dist/routers/ui.mjs.map +1 -1
  211. package/dist/routers/webhooks.mjs +1 -2
  212. package/dist/routers/well-known.mjs +1 -2
  213. package/dist/routes/api-routes.mjs +1 -2
  214. package/dist/routes/classification.mjs +1 -2
  215. package/dist/routes/openapi-classes.mjs +1 -2
  216. package/dist/routes/page-routes.mjs +2 -3
  217. package/dist/routes/page-routes.mjs.map +1 -1
  218. package/dist/runtime.mjs +1 -2
  219. package/dist/serverless.mjs +1 -2
  220. package/dist/store.mjs +1 -2
  221. package/dist/targets.mjs +1 -2
  222. package/dist/ui/avatar.mjs +1 -2
  223. package/dist/ui/buttons.mjs +1 -2
  224. package/dist/ui/code-block.mjs +1 -2
  225. package/dist/ui/compare-stage.mjs +86 -54
  226. package/dist/ui/compare-stage.mjs.map +1 -1
  227. package/dist/ui/csrf-field.mjs +1 -2
  228. package/dist/ui/css.mjs +1 -2
  229. package/dist/ui/design-reference.mjs +1 -2
  230. package/dist/ui/document.mjs +1 -2
  231. package/dist/ui/dropdown.mjs +1 -2
  232. package/dist/ui/feedback.mjs +1 -2
  233. package/dist/ui/filter-input.mjs +1 -2
  234. package/dist/ui/forms.mjs +1 -2
  235. package/dist/ui/icons/icon.mjs +1 -2
  236. package/dist/ui/icons/paths.mjs +1 -2
  237. package/dist/ui/icons/sprite.mjs +1 -2
  238. package/dist/ui/kbd.mjs +1 -2
  239. package/dist/ui/layout.mjs +1 -2
  240. package/dist/ui/preview-embed.mjs +1 -2
  241. package/dist/ui/progress.mjs +1 -2
  242. package/dist/ui/segmented.mjs +1 -2
  243. package/dist/ui/shell/account.mjs +1 -2
  244. package/dist/ui/shell/bare.mjs +1 -2
  245. package/dist/ui/shell/boot-script.mjs +1 -2
  246. package/dist/ui/shell/client-script.mjs +1 -2
  247. package/dist/ui/shell/nav.mjs +1 -2
  248. package/dist/ui/shell/review-script.mjs +17 -3
  249. package/dist/ui/shell/review-script.mjs.map +1 -1
  250. package/dist/ui/shell/sidebar.mjs +1 -2
  251. package/dist/ui/shell/topbar.mjs +1 -2
  252. package/dist/ui/stacks.mjs +1 -2
  253. package/dist/ui/styles/base.mjs +1 -2
  254. package/dist/ui/styles/behavior.mjs +1 -2
  255. package/dist/ui/styles/review-layout.mjs +1 -2
  256. package/dist/ui/styles/review.mjs +1 -2
  257. package/dist/ui/styles/tokens.mjs +1 -2
  258. package/dist/ui/styles.mjs +1 -2
  259. package/dist/ui/subnav.mjs +1 -2
  260. package/dist/ui/table.mjs +1 -2
  261. package/dist/ui/theme.mjs +1 -2
  262. package/dist/ui/thumbnail.mjs +1 -2
  263. package/dist/ui/time.mjs +1 -2
  264. package/package.json +9 -9
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Segmented } from "./segmented.mjs";
6
6
  import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/compare-stage.tsx
7
+ // src/ui/compare-stage.tsx
8
8
  /**
9
9
  * Screenshot comparison surface. All modes share one set of images; the
10
10
  * `data-view` / `data-zoom` / `data-flip` attributes on the root (driven by
@@ -70,6 +70,10 @@ const stageRoot = css`
70
70
  font-size: var(--text-sm);
71
71
  text-align: center;
72
72
  }
73
+ .stage__note {
74
+ color: var(--text-secondary);
75
+ font-size: var(--text-sm);
76
+ }
73
77
  .stage__divider,
74
78
  .stage__controls {
75
79
  display: none;
@@ -103,6 +107,10 @@ const stageRoot = css`
103
107
  &[data-zoom="400"] .stage__img {
104
108
  image-rendering: pixelated;
105
109
  }
110
+ /* Design variant: screenshot against a render, two panes and no diff. */
111
+ &[data-variant="design"][data-view="split"] .stage__grid {
112
+ grid-template-columns: repeat(2, minmax(0, 1fr));
113
+ }
106
114
  &[data-view="diff"] .stage__grid,
107
115
  &[data-view="flip"] .stage__grid,
108
116
  &[data-view="swipe"] .stage__grid,
@@ -218,6 +226,12 @@ const ZOOMS = [
218
226
  value: "400"
219
227
  }
220
228
  ];
229
+ /** Modes and zoom a design comparison offers: no pixel diff, and the render always fits the stage. */
230
+ const DESIGN_MODES = [
231
+ "split",
232
+ "swipe",
233
+ "onion"
234
+ ];
221
235
  function withActive(items, active) {
222
236
  return items.map((item) => ({
223
237
  ...item,
@@ -244,12 +258,12 @@ const Pane = ({ kind, label, src, alt, empty, meta }) => {
244
258
  });
245
259
  };
246
260
  /** Swipe and onion-skin sliders; each is shown only in its own mode (CSS). */
247
- const RangeControls = () => {
261
+ const RangeControls = ({ baselineLabel }) => {
248
262
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("label", {
249
263
  class: "stage__controls",
250
264
  "data-for": "swipe",
251
265
  children: [
252
- /* @__PURE__ */ jsx("span", { children: "Baseline" }),
266
+ /* @__PURE__ */ jsx("span", { children: baselineLabel }),
253
267
  /* @__PURE__ */ jsx("input", {
254
268
  type: "range",
255
269
  min: "0",
@@ -264,7 +278,7 @@ const RangeControls = () => {
264
278
  class: "stage__controls",
265
279
  "data-for": "onion",
266
280
  children: [
267
- /* @__PURE__ */ jsx("span", { children: "Baseline" }),
281
+ /* @__PURE__ */ jsx("span", { children: baselineLabel }),
268
282
  /* @__PURE__ */ jsx("input", {
269
283
  type: "range",
270
284
  min: "0",
@@ -277,68 +291,86 @@ const RangeControls = () => {
277
291
  ]
278
292
  })] });
279
293
  };
294
+ /** Mode switch, plus the zoom switch (a design comparison always fits, so it has none). */
295
+ const StageBar = ({ design }) => {
296
+ const modes = design ? MODES.filter((mode) => DESIGN_MODES.includes(mode.value)) : MODES;
297
+ return /* @__PURE__ */ jsxs("div", {
298
+ class: "stage__bar",
299
+ children: [/* @__PURE__ */ jsx(Segmented, {
300
+ label: "Comparison mode",
301
+ "data-compare-mode": true,
302
+ items: withActive(modes, "split")
303
+ }), design ? null : /* @__PURE__ */ jsx(Segmented, {
304
+ label: "Zoom",
305
+ "data-compare-zoom": true,
306
+ items: withActive(ZOOMS, "fit")
307
+ })]
308
+ });
309
+ };
310
+ /** The panes (baseline or design, current, diff) and the swipe divider. */
311
+ const StageGrid = ({ baselineSrc, currentSrc, diffSrc, subject, baselineEmpty = "New story — no baseline yet. Approve to set the baseline.", diffEmpty = "No diff", diffMeta, design, baselineLabel }) => /* @__PURE__ */ jsxs("div", {
312
+ class: "stage__grid",
313
+ children: [
314
+ /* @__PURE__ */ jsx(Pane, {
315
+ kind: "baseline",
316
+ label: baselineLabel,
317
+ src: baselineSrc,
318
+ alt: `${baselineLabel} for ${subject}`,
319
+ empty: baselineEmpty
320
+ }),
321
+ /* @__PURE__ */ jsx(Pane, {
322
+ kind: "current",
323
+ label: "Current",
324
+ src: currentSrc,
325
+ alt: `Current for ${subject}`,
326
+ empty: "No screenshot"
327
+ }),
328
+ design ? null : /* @__PURE__ */ jsx(Pane, {
329
+ kind: "diff",
330
+ label: "Diff",
331
+ src: diffSrc,
332
+ alt: `Diff for ${subject}`,
333
+ empty: diffEmpty,
334
+ meta: diffMeta
335
+ }),
336
+ /* @__PURE__ */ jsx("span", {
337
+ class: "stage__divider",
338
+ "aria-hidden": "true",
339
+ children: /* @__PURE__ */ jsx("span", {
340
+ class: "stage__knob",
341
+ "data-compare-knob": true,
342
+ children: "↔"
343
+ })
344
+ })
345
+ ]
346
+ });
280
347
  /** Baseline / current / diff comparison with swipe, onion-skin, flip, and zoom. */
281
- const CompareStage = ({ baselineSrc, currentSrc, diffSrc, subject, baselineEmpty = "New story — no baseline yet. Approve to set the baseline.", diffEmpty = "No diff", diffMeta }) => {
348
+ const CompareStage = ({ variant = "baseline", baselineLabel = "Baseline", note, ...images }) => {
349
+ const design = variant === "design";
282
350
  return /* @__PURE__ */ jsxs("div", {
283
351
  class: stageRoot,
284
352
  "data-compare": true,
285
353
  "data-view": "split",
286
354
  "data-zoom": "fit",
287
355
  "data-flip": "current",
356
+ "data-variant": variant,
357
+ "data-modes": design ? DESIGN_MODES.join(",") : void 0,
358
+ "data-zooms": design ? "fit" : void 0,
288
359
  children: [
289
- /* @__PURE__ */ jsxs("div", {
290
- class: "stage__bar",
291
- children: [/* @__PURE__ */ jsx(Segmented, {
292
- label: "Comparison mode",
293
- "data-compare-mode": true,
294
- items: withActive(MODES, "split")
295
- }), /* @__PURE__ */ jsx(Segmented, {
296
- label: "Zoom",
297
- "data-compare-zoom": true,
298
- items: withActive(ZOOMS, "fit")
299
- })]
300
- }),
301
- /* @__PURE__ */ jsxs("div", {
302
- class: "stage__grid",
303
- children: [
304
- /* @__PURE__ */ jsx(Pane, {
305
- kind: "baseline",
306
- label: "Baseline",
307
- src: baselineSrc,
308
- alt: `Baseline for ${subject}`,
309
- empty: baselineEmpty
310
- }),
311
- /* @__PURE__ */ jsx(Pane, {
312
- kind: "current",
313
- label: "Current",
314
- src: currentSrc,
315
- alt: `Current for ${subject}`,
316
- empty: "No screenshot"
317
- }),
318
- /* @__PURE__ */ jsx(Pane, {
319
- kind: "diff",
320
- label: "Diff",
321
- src: diffSrc,
322
- alt: `Diff for ${subject}`,
323
- empty: diffEmpty,
324
- meta: diffMeta
325
- }),
326
- /* @__PURE__ */ jsx("span", {
327
- class: "stage__divider",
328
- "aria-hidden": "true",
329
- children: /* @__PURE__ */ jsx("span", {
330
- class: "stage__knob",
331
- "data-compare-knob": true,
332
- children: "↔"
333
- })
334
- })
335
- ]
360
+ /* @__PURE__ */ jsx(StageBar, { design }),
361
+ note ? /* @__PURE__ */ jsx("p", {
362
+ class: "stage__note",
363
+ children: note
364
+ }) : null,
365
+ /* @__PURE__ */ jsx(StageGrid, {
366
+ ...images,
367
+ design,
368
+ baselineLabel
336
369
  }),
337
- /* @__PURE__ */ jsx(RangeControls, {})
370
+ /* @__PURE__ */ jsx(RangeControls, { baselineLabel })
338
371
  ]
339
372
  });
340
373
  };
341
- //#endregion
342
374
  export { CompareStage };
343
375
 
344
376
  //# sourceMappingURL=compare-stage.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare-stage.mjs","names":[],"sources":["../../src/ui/compare-stage.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\nimport { Segmented } from \"./segmented.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/**\n * Screenshot comparison surface. All modes share one set of images; the\n * `data-view` / `data-zoom` / `data-flip` attributes on the root (driven by\n * the review script) decide what is shown. Without JS it stays side-by-side.\n */\nconst stageRoot = css`\n /* compare-stage */\n display: grid;\n gap: var(--space-3);\n .stage__bar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n align-items: center;\n justify-content: space-between;\n }\n .stage__grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: var(--space-3);\n align-items: start;\n }\n .stage__pane {\n position: relative;\n min-width: 0;\n max-height: 78vh;\n overflow: auto;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n .stage__label {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n background: var(--surface-subtle);\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-size: var(--text-xs);\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n .stage__img {\n display: block;\n width: 100%;\n height: auto;\n background: repeating-conic-gradient(var(--surface-muted) 0% 25%, var(--surface-subtle) 0% 50%)\n 0 0 / 16px 16px;\n }\n .stage__empty {\n display: grid;\n place-items: center;\n aspect-ratio: 16 / 9;\n padding: var(--space-4);\n background: var(--surface-subtle);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n text-align: center;\n }\n .stage__divider,\n .stage__controls {\n display: none;\n }\n .stage__controls {\n align-items: center;\n gap: var(--space-3);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n }\n .stage__controls input {\n flex: 1;\n accent-color: var(--accent);\n }\n &[data-zoom=\"100\"] .stage__img {\n width: auto;\n max-width: none;\n }\n &[data-zoom=\"200\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 2;\n }\n &[data-zoom=\"400\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 4;\n }\n /* Zoomed in, show real pixels instead of a smoothed blur. */\n &[data-zoom=\"200\"] .stage__img,\n &[data-zoom=\"400\"] .stage__img {\n image-rendering: pixelated;\n }\n &[data-view=\"diff\"] .stage__grid,\n &[data-view=\"flip\"] .stage__grid,\n &[data-view=\"swipe\"] .stage__grid,\n &[data-view=\"onion\"] .stage__grid {\n grid-template-columns: minmax(0, 1fr);\n }\n &[data-view=\"diff\"] .stage__pane:not([data-pane=\"diff\"]) {\n display: none;\n }\n &[data-view=\"flip\"][data-flip=\"baseline\"] .stage__pane:not([data-pane=\"baseline\"]),\n &[data-view=\"flip\"]:not([data-flip=\"baseline\"]) .stage__pane:not([data-pane=\"current\"]) {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"diff\"],\n &[data-view=\"onion\"] .stage__pane[data-pane=\"diff\"] {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane,\n &[data-view=\"onion\"] .stage__pane,\n &[data-view=\"swipe\"] .stage__divider {\n grid-area: 1 / 1;\n }\n &[data-view=\"swipe\"] .stage__label,\n &[data-view=\"onion\"] .stage__label {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"current\"] {\n clip-path: inset(0 0 0 var(--swipe, 50%));\n background: transparent;\n }\n &[data-view=\"onion\"] .stage__pane[data-pane=\"current\"] {\n opacity: var(--onion, 0.5);\n background: transparent;\n }\n &[data-view=\"swipe\"] .stage__divider {\n position: relative;\n display: block;\n z-index: 3;\n justify-self: start;\n width: 2px;\n height: 100%;\n margin-left: var(--swipe, 50%);\n background: var(--accent);\n pointer-events: none;\n }\n /* The only part of the divider that takes pointer input: drag it to wipe. */\n .stage__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n transform: translate(-50%, -50%);\n background: var(--accent);\n color: var(--accent-contrast);\n border: 2px solid var(--surface-card);\n border-radius: var(--radius-pill);\n box-shadow: var(--shadow-2);\n font-size: var(--text-lg);\n line-height: 1;\n cursor: ew-resize;\n pointer-events: auto;\n touch-action: none;\n user-select: none;\n }\n &[data-view=\"flip\"] .stage__grid {\n cursor: pointer;\n }\n &[data-view=\"swipe\"] .stage__controls[data-for=\"swipe\"],\n &[data-view=\"onion\"] .stage__controls[data-for=\"onion\"] {\n display: flex;\n }\n`;\n\nconst MODES = [\n { label: \"Side by side\", value: \"split\" },\n { label: \"Swipe\", value: \"swipe\" },\n { label: \"Onion skin\", value: \"onion\" },\n { label: \"Diff\", value: \"diff\" },\n { label: \"Flip\", value: \"flip\" },\n];\n\nconst ZOOMS = [\n { label: \"Fit\", value: \"fit\" },\n { label: \"100%\", value: \"100\" },\n { label: \"200%\", value: \"200\" },\n { label: \"400%\", value: \"400\" },\n];\n\nfunction withActive(\n items: { label: string; value: string }[],\n active: string,\n): { label: string; value: string; active: boolean }[] {\n return items.map((item) => ({ ...item, active: item.value === active }));\n}\n\ninterface PaneProps {\n kind: \"baseline\" | \"current\" | \"diff\";\n label: string;\n src: string | null;\n alt: string;\n empty: unknown;\n meta?: string;\n}\n\nconst Pane: FC<PaneProps> = ({ kind, label, src, alt, empty, meta }) => {\n return (\n <div class=\"stage__pane\" data-pane={kind}>\n <div class=\"stage__label\">\n <span>{label}</span>\n {meta ? <span>{meta}</span> : null}\n </div>\n {src ? (\n <img class=\"stage__img\" src={src} alt={alt} loading=\"lazy\" decoding=\"async\" />\n ) : (\n <div class=\"stage__empty\">{empty}</div>\n )}\n </div>\n );\n};\n\n/** Swipe and onion-skin sliders; each is shown only in its own mode (CSS). */\nconst RangeControls: FC = () => {\n return (\n <>\n <label class=\"stage__controls\" data-for=\"swipe\">\n <span>Baseline</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-swipe aria-label=\"Swipe\" />\n <span>Current</span>\n </label>\n <label class=\"stage__controls\" data-for=\"onion\">\n <span>Baseline</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-onion aria-label=\"Opacity\" />\n <span>Current</span>\n </label>\n </>\n );\n};\n\n/** Props for the screenshot comparison surface. */\nexport interface CompareStageProps {\n /** Baseline image URL, or null when the story has no baseline yet. */\n baselineSrc: string | null;\n currentSrc: string;\n /** Diff overlay URL, or null when none was produced. */\n diffSrc: string | null;\n /** Accessible description shared by the images, e.g. \"Button / Primary\". */\n subject: string;\n baselineEmpty?: unknown;\n diffEmpty?: unknown;\n diffMeta?: string;\n}\n\n/** Baseline / current / diff comparison with swipe, onion-skin, flip, and zoom. */\nexport const CompareStage: FC<CompareStageProps> = ({\n baselineSrc,\n currentSrc,\n diffSrc,\n subject,\n baselineEmpty = \"New story — no baseline yet. Approve to set the baseline.\",\n diffEmpty = \"No diff\",\n diffMeta,\n}) => {\n return (\n <div class={stageRoot} data-compare data-view=\"split\" data-zoom=\"fit\" data-flip=\"current\">\n <div class=\"stage__bar\">\n <Segmented label=\"Comparison mode\" data-compare-mode items={withActive(MODES, \"split\")} />\n <Segmented label=\"Zoom\" data-compare-zoom items={withActive(ZOOMS, \"fit\")} />\n </div>\n <div class=\"stage__grid\">\n <Pane\n kind=\"baseline\"\n label=\"Baseline\"\n src={baselineSrc}\n alt={`Baseline for ${subject}`}\n empty={baselineEmpty}\n />\n <Pane\n kind=\"current\"\n label=\"Current\"\n src={currentSrc}\n alt={`Current for ${subject}`}\n empty=\"No screenshot\"\n />\n <Pane\n kind=\"diff\"\n label=\"Diff\"\n src={diffSrc}\n alt={`Diff for ${subject}`}\n empty={diffEmpty}\n meta={diffMeta}\n />\n <span class=\"stage__divider\" aria-hidden=\"true\">\n <span class=\"stage__knob\" data-compare-knob>\n ↔\n </span>\n </span>\n </div>\n <RangeControls />\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"compare-stage.mjs","names":[],"sources":["../../src/ui/compare-stage.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\nimport { Segmented } from \"./segmented.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/**\n * Screenshot comparison surface. All modes share one set of images; the\n * `data-view` / `data-zoom` / `data-flip` attributes on the root (driven by\n * the review script) decide what is shown. Without JS it stays side-by-side.\n */\nconst stageRoot = css`\n /* compare-stage */\n display: grid;\n gap: var(--space-3);\n .stage__bar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n align-items: center;\n justify-content: space-between;\n }\n .stage__grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: var(--space-3);\n align-items: start;\n }\n .stage__pane {\n position: relative;\n min-width: 0;\n max-height: 78vh;\n overflow: auto;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n .stage__label {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n background: var(--surface-subtle);\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-size: var(--text-xs);\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n .stage__img {\n display: block;\n width: 100%;\n height: auto;\n background: repeating-conic-gradient(var(--surface-muted) 0% 25%, var(--surface-subtle) 0% 50%)\n 0 0 / 16px 16px;\n }\n .stage__empty {\n display: grid;\n place-items: center;\n aspect-ratio: 16 / 9;\n padding: var(--space-4);\n background: var(--surface-subtle);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n text-align: center;\n }\n .stage__note {\n color: var(--text-secondary);\n font-size: var(--text-sm);\n }\n .stage__divider,\n .stage__controls {\n display: none;\n }\n .stage__controls {\n align-items: center;\n gap: var(--space-3);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n }\n .stage__controls input {\n flex: 1;\n accent-color: var(--accent);\n }\n &[data-zoom=\"100\"] .stage__img {\n width: auto;\n max-width: none;\n }\n &[data-zoom=\"200\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 2;\n }\n &[data-zoom=\"400\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 4;\n }\n /* Zoomed in, show real pixels instead of a smoothed blur. */\n &[data-zoom=\"200\"] .stage__img,\n &[data-zoom=\"400\"] .stage__img {\n image-rendering: pixelated;\n }\n /* Design variant: screenshot against a render, two panes and no diff. */\n &[data-variant=\"design\"][data-view=\"split\"] .stage__grid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n &[data-view=\"diff\"] .stage__grid,\n &[data-view=\"flip\"] .stage__grid,\n &[data-view=\"swipe\"] .stage__grid,\n &[data-view=\"onion\"] .stage__grid {\n grid-template-columns: minmax(0, 1fr);\n }\n &[data-view=\"diff\"] .stage__pane:not([data-pane=\"diff\"]) {\n display: none;\n }\n &[data-view=\"flip\"][data-flip=\"baseline\"] .stage__pane:not([data-pane=\"baseline\"]),\n &[data-view=\"flip\"]:not([data-flip=\"baseline\"]) .stage__pane:not([data-pane=\"current\"]) {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"diff\"],\n &[data-view=\"onion\"] .stage__pane[data-pane=\"diff\"] {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane,\n &[data-view=\"onion\"] .stage__pane,\n &[data-view=\"swipe\"] .stage__divider {\n grid-area: 1 / 1;\n }\n &[data-view=\"swipe\"] .stage__label,\n &[data-view=\"onion\"] .stage__label {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"current\"] {\n clip-path: inset(0 0 0 var(--swipe, 50%));\n background: transparent;\n }\n &[data-view=\"onion\"] .stage__pane[data-pane=\"current\"] {\n opacity: var(--onion, 0.5);\n background: transparent;\n }\n &[data-view=\"swipe\"] .stage__divider {\n position: relative;\n display: block;\n z-index: 3;\n justify-self: start;\n width: 2px;\n height: 100%;\n margin-left: var(--swipe, 50%);\n background: var(--accent);\n pointer-events: none;\n }\n /* The only part of the divider that takes pointer input: drag it to wipe. */\n .stage__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n transform: translate(-50%, -50%);\n background: var(--accent);\n color: var(--accent-contrast);\n border: 2px solid var(--surface-card);\n border-radius: var(--radius-pill);\n box-shadow: var(--shadow-2);\n font-size: var(--text-lg);\n line-height: 1;\n cursor: ew-resize;\n pointer-events: auto;\n touch-action: none;\n user-select: none;\n }\n &[data-view=\"flip\"] .stage__grid {\n cursor: pointer;\n }\n &[data-view=\"swipe\"] .stage__controls[data-for=\"swipe\"],\n &[data-view=\"onion\"] .stage__controls[data-for=\"onion\"] {\n display: flex;\n }\n`;\n\nconst MODES = [\n { label: \"Side by side\", value: \"split\" },\n { label: \"Swipe\", value: \"swipe\" },\n { label: \"Onion skin\", value: \"onion\" },\n { label: \"Diff\", value: \"diff\" },\n { label: \"Flip\", value: \"flip\" },\n];\n\nconst ZOOMS = [\n { label: \"Fit\", value: \"fit\" },\n { label: \"100%\", value: \"100\" },\n { label: \"200%\", value: \"200\" },\n { label: \"400%\", value: \"400\" },\n];\n\n/** Modes and zoom a design comparison offers: no pixel diff, and the render always fits the stage. */\nconst DESIGN_MODES = [\"split\", \"swipe\", \"onion\"];\n\nfunction withActive(\n items: { label: string; value: string }[],\n active: string,\n): { label: string; value: string; active: boolean }[] {\n return items.map((item) => ({ ...item, active: item.value === active }));\n}\n\ninterface PaneProps {\n kind: \"baseline\" | \"current\" | \"diff\";\n label: string;\n src: string | null;\n alt: string;\n empty: unknown;\n meta?: string;\n}\n\nconst Pane: FC<PaneProps> = ({ kind, label, src, alt, empty, meta }) => {\n return (\n <div class=\"stage__pane\" data-pane={kind}>\n <div class=\"stage__label\">\n <span>{label}</span>\n {meta ? <span>{meta}</span> : null}\n </div>\n {src ? (\n <img class=\"stage__img\" src={src} alt={alt} loading=\"lazy\" decoding=\"async\" />\n ) : (\n <div class=\"stage__empty\">{empty}</div>\n )}\n </div>\n );\n};\n\n/** Swipe and onion-skin sliders; each is shown only in its own mode (CSS). */\nconst RangeControls: FC<{ baselineLabel: string }> = ({ baselineLabel }) => {\n return (\n <>\n <label class=\"stage__controls\" data-for=\"swipe\">\n <span>{baselineLabel}</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-swipe aria-label=\"Swipe\" />\n <span>Current</span>\n </label>\n <label class=\"stage__controls\" data-for=\"onion\">\n <span>{baselineLabel}</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-onion aria-label=\"Opacity\" />\n <span>Current</span>\n </label>\n </>\n );\n};\n\n/** Props for the screenshot comparison surface. */\nexport interface CompareStageProps {\n /** Baseline image URL, or null when the story has no baseline yet. */\n baselineSrc: string | null;\n currentSrc: string;\n /** Diff overlay URL, or null when none was produced. */\n diffSrc: string | null;\n /** Accessible description shared by the images, e.g. \"Button / Primary\". */\n subject: string;\n baselineEmpty?: unknown;\n diffEmpty?: unknown;\n diffMeta?: string;\n /**\n * `design` compares the screenshot with a design render instead of a baseline: side by side,\n * swipe and onion only (no diff, no flip), fit zoom only.\n */\n variant?: \"baseline\" | \"design\";\n /** Name of the left-hand image in labels and sliders (default \"Baseline\"). */\n baselineLabel?: string;\n /** A line under the controls, e.g. what the left-hand image is. */\n note?: string;\n}\n\n/** Mode switch, plus the zoom switch (a design comparison always fits, so it has none). */\nconst StageBar: FC<{ design: boolean }> = ({ design }) => {\n const modes = design ? MODES.filter((mode) => DESIGN_MODES.includes(mode.value)) : MODES;\n return (\n <div class=\"stage__bar\">\n <Segmented label=\"Comparison mode\" data-compare-mode items={withActive(modes, \"split\")} />\n {design ? null : (\n <Segmented label=\"Zoom\" data-compare-zoom items={withActive(ZOOMS, \"fit\")} />\n )}\n </div>\n );\n};\n\n/** The panes (baseline or design, current, diff) and the swipe divider. */\nconst StageGrid: FC<CompareStageProps & { design: boolean; baselineLabel: string }> = ({\n baselineSrc,\n currentSrc,\n diffSrc,\n subject,\n baselineEmpty = \"New story — no baseline yet. Approve to set the baseline.\",\n diffEmpty = \"No diff\",\n diffMeta,\n design,\n baselineLabel,\n}) => (\n <div class=\"stage__grid\">\n <Pane\n kind=\"baseline\"\n label={baselineLabel}\n src={baselineSrc}\n alt={`${baselineLabel} for ${subject}`}\n empty={baselineEmpty}\n />\n <Pane\n kind=\"current\"\n label=\"Current\"\n src={currentSrc}\n alt={`Current for ${subject}`}\n empty=\"No screenshot\"\n />\n {design ? null : (\n <Pane\n kind=\"diff\"\n label=\"Diff\"\n src={diffSrc}\n alt={`Diff for ${subject}`}\n empty={diffEmpty}\n meta={diffMeta}\n />\n )}\n <span class=\"stage__divider\" aria-hidden=\"true\">\n <span class=\"stage__knob\" data-compare-knob>\n ↔\n </span>\n </span>\n </div>\n);\n\n/** Baseline / current / diff comparison with swipe, onion-skin, flip, and zoom. */\nexport const CompareStage: FC<CompareStageProps> = ({\n variant = \"baseline\",\n baselineLabel = \"Baseline\",\n note,\n ...images\n}) => {\n const design = variant === \"design\";\n return (\n <div\n class={stageRoot}\n data-compare\n data-view=\"split\"\n data-zoom=\"fit\"\n data-flip=\"current\"\n data-variant={variant}\n data-modes={design ? DESIGN_MODES.join(\",\") : undefined}\n data-zooms={design ? \"fit\" : undefined}\n >\n <StageBar design={design} />\n {note ? <p class=\"stage__note\">{note}</p> : null}\n <StageGrid {...images} design={design} baselineLabel={baselineLabel} />\n <RangeControls baselineLabel={baselineLabel} />\n </div>\n );\n};\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { getStore } from "../store.mjs";
5
5
  import { getCsrfToken } from "../middleware/csrf.mjs";
6
6
  import { jsx } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/csrf-field.tsx
7
+ // src/ui/csrf-field.tsx
8
8
  /** Hidden CSRF token so native (non-HTMX) form posts pass the csrf middleware. */
9
9
  function csrfField() {
10
10
  const { config, sessionId } = getStore();
@@ -14,7 +14,6 @@ function csrfField() {
14
14
  value: getCsrfToken(config.secret, sessionId)
15
15
  });
16
16
  }
17
- //#endregion
18
17
  export { csrfField };
19
18
 
20
19
  //# sourceMappingURL=csrf-field.mjs.map
package/dist/ui/css.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { createCssContext } from "hono/css";
5
- //#region src/ui/css.ts
5
+ // src/ui/css.ts
6
6
  /**
7
7
  * hono/css building blocks for the server-rendered UI.
8
8
  *
@@ -30,7 +30,6 @@ const context = createCssContext({
30
30
  const css = (strings, ...values) => context.css(strings, ...values);
31
31
  /** Collects every used class into `<style id="storyshelf-css">`. */
32
32
  const Style = (args) => context.Style(args);
33
- //#endregion
34
33
  export { Style, css };
35
34
 
36
35
  //# sourceMappingURL=css.mjs.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Icon } from "./icons/icon.mjs";
6
6
  import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/design-reference.tsx
7
+ // src/ui/design-reference.tsx
8
8
  const referenceRoot = css`
9
9
  /* design-reference */
10
10
  display: grid;
@@ -91,7 +91,6 @@ const DesignPreview = ({ src, name, providerName }) => /* @__PURE__ */ jsxs(Frag
91
91
  class: previewCaption,
92
92
  children: name
93
93
  }) : null] });
94
- //#endregion
95
94
  export { DesignPreview, DesignReference };
96
95
 
97
96
  //# sourceMappingURL=design-reference.mjs.map
@@ -13,7 +13,7 @@ import { baseStyle } from "./styles.mjs";
13
13
  import { DARK_THEME, LIGHT_THEME } from "./theme.mjs";
14
14
  import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
15
15
  import { raw } from "hono/html";
16
- //#region src/ui/document.tsx
16
+ // src/ui/document.tsx
17
17
  const shellRoot = css`
18
18
  /* shell */
19
19
  display: grid;
@@ -123,7 +123,6 @@ const DocumentLayout = ({ title, nav, layout = "default", children }) => {
123
123
  ] })]
124
124
  })] });
125
125
  };
126
- //#endregion
127
126
  export { DocumentLayout };
128
127
 
129
128
  //# sourceMappingURL=document.mjs.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Icon } from "./icons/icon.mjs";
6
6
  import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/dropdown.tsx
7
+ // src/ui/dropdown.tsx
8
8
  const dropdownRoot = css`
9
9
  /* dropdown */
10
10
  position: relative;
@@ -175,7 +175,6 @@ const DropdownDivider = () => {
175
175
  role: "separator"
176
176
  });
177
177
  };
178
- //#endregion
179
178
  export { Dropdown, DropdownDivider, DropdownItem };
180
179
 
181
180
  //# sourceMappingURL=dropdown.mjs.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Icon } from "./icons/icon.mjs";
6
6
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/feedback.tsx
7
+ // src/ui/feedback.tsx
8
8
  const badgeBase = css`
9
9
  /* badge */
10
10
  display: inline-flex;
@@ -335,7 +335,6 @@ const Meta = ({ as = "p", mono = false, tone = "neutral", center = false, childr
335
335
  children
336
336
  });
337
337
  };
338
- //#endregion
339
338
  export { Alert, Badge, EmptyState, Meta, Stat, statusTone };
340
339
 
341
340
  //# sourceMappingURL=feedback.mjs.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Icon } from "./icons/icon.mjs";
6
6
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/filter-input.tsx
7
+ // src/ui/filter-input.tsx
8
8
  const filterWrap = css`
9
9
  /* filter-input */
10
10
  position: relative;
@@ -52,7 +52,6 @@ const FilterInput = ({ label, placeholder = "Filter…" }) => {
52
52
  })]
53
53
  });
54
54
  };
55
- //#endregion
56
55
  export { FilterInput };
57
56
 
58
57
  //# sourceMappingURL=filter-input.mjs.map
package/dist/ui/forms.mjs CHANGED
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Icon } from "./icons/icon.mjs";
6
6
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/forms.tsx
7
+ // src/ui/forms.tsx
8
8
  const fieldWrap = css`
9
9
  /* field */
10
10
  display: grid;
@@ -286,7 +286,6 @@ const SelectField = ({ label, name, value, options, hint, disabled, required, la
286
286
  ]
287
287
  });
288
288
  };
289
- //#endregion
290
289
  export { CheckField, Field, FormActions, SelectField, TextareaField };
291
290
 
292
291
  //# sourceMappingURL=forms.mjs.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { css } from "../css.mjs";
5
5
  import { iconHref } from "./sprite.mjs";
6
6
  import { jsx } from "hono/jsx/jsx-runtime";
7
- //#region src/ui/icons/icon.tsx
7
+ // src/ui/icons/icon.tsx
8
8
  const iconBase = css`
9
9
  /* icon */
10
10
  flex: none;
@@ -47,7 +47,6 @@ const Icon = ({ name, size = "md", label }) => {
47
47
  children: /* @__PURE__ */ jsx("use", { href: iconHref(name) })
48
48
  });
49
49
  };
50
- //#endregion
51
50
  export { Icon };
52
51
 
53
52
  //# sourceMappingURL=icon.mjs.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/ui/icons/paths.ts
4
+ // src/ui/icons/paths.ts
5
5
  /**
6
6
  * Icon set: inner SVG markup per icon on a 24x24 grid, stroke-based
7
7
  * (Lucide-style, ISC). Add an icon here and it is in the sprite and the
@@ -56,7 +56,6 @@ const ICON_PATHS = {
56
56
  book: "<path d=\"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20\"/>",
57
57
  server: "<rect width=\"20\" height=\"8\" x=\"2\" y=\"2\" rx=\"2\"/><rect width=\"20\" height=\"8\" x=\"2\" y=\"14\" rx=\"2\"/><path d=\"M6 6h.01M6 18h.01\"/>"
58
58
  };
59
- //#endregion
60
59
  export { ICON_PATHS };
61
60
 
62
61
  //# sourceMappingURL=paths.mjs.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { ICON_PATHS } from "./paths.mjs";
5
5
  import { createHash } from "node:crypto";
6
- //#region src/ui/icons/sprite.ts
6
+ // src/ui/icons/sprite.ts
7
7
  /** Assembled `<svg>` sprite of `<symbol id="i-<name>">` entries. */
8
8
  function buildSprite(paths = ICON_PATHS) {
9
9
  return `<svg xmlns="http://www.w3.org/2000/svg">${Object.entries(paths).toSorted(([a], [b]) => a.localeCompare(b)).map(([name, body]) => `<symbol id="i-${name}" viewBox="0 0 24 24">${body}</symbol>`).join("")}</svg>`;
@@ -20,7 +20,6 @@ const iconSpriteHref = `/assets/icons-${iconSpriteHash}.svg`;
20
20
  function iconHref(name) {
21
21
  return `${iconSpriteHref}#i-${name}`;
22
22
  }
23
- //#endregion
24
23
  export { buildSprite, contentHash, iconHref, iconSprite, iconSpriteHash, iconSpriteHref };
25
24
 
26
25
  //# sourceMappingURL=sprite.mjs.map
package/dist/ui/kbd.mjs CHANGED
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { Fragment, jsx } from "hono/jsx/jsx-runtime";
6
- //#region src/ui/kbd.tsx
6
+ // src/ui/kbd.tsx
7
7
  const kbdKey = css`
8
8
  /* kbd */
9
9
  display: inline-flex;
@@ -27,7 +27,6 @@ const Kbd = ({ keys }) => {
27
27
  children: key
28
28
  }, String(index))) });
29
29
  };
30
- //#endregion
31
30
  export { Kbd };
32
31
 
33
32
  //# sourceMappingURL=kbd.mjs.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
6
- //#region src/ui/layout.tsx
6
+ // src/ui/layout.tsx
7
7
  const cardBase = css`
8
8
  /* card */
9
9
  background: var(--surface-card);
@@ -167,7 +167,6 @@ const SectionTitle = ({ level = 2, tone = "neutral", children }) => {
167
167
  children
168
168
  });
169
169
  };
170
- //#endregion
171
170
  export { Card, CardSection, PageHeader, SectionTitle };
172
171
 
173
172
  //# sourceMappingURL=layout.mjs.map
@@ -5,7 +5,7 @@ import { css } from "./css.mjs";
5
5
  import { Button } from "./buttons.mjs";
6
6
  import { Meta } from "./feedback.mjs";
7
7
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
8
- //#region src/ui/preview-embed.tsx
8
+ // src/ui/preview-embed.tsx
9
9
  /**
10
10
  * Sandbox of the embedded story frame (ADR 0006, decision 9). Scripts only: no `allow-same-origin`,
11
11
  * so a customer's Storybook never runs with the app's origin and its cookies, and no top navigation,
@@ -76,7 +76,6 @@ const PreviewEmbed = ({ src, title, refresh }) => /* @__PURE__ */ jsxs("div", {
76
76
  })
77
77
  })]
78
78
  });
79
- //#endregion
80
79
  export { PREVIEW_SANDBOX, PreviewEmbed };
81
80
 
82
81
  //# sourceMappingURL=preview-embed.mjs.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { jsx } from "hono/jsx/jsx-runtime";
6
- //#region src/ui/progress.tsx
6
+ // src/ui/progress.tsx
7
7
  const track = css`
8
8
  /* progress */
9
9
  height: 6px;
@@ -38,7 +38,6 @@ const Progress = ({ value, max, label }) => {
38
38
  children: /* @__PURE__ */ jsx("span", { style: `width:${percent}%` })
39
39
  });
40
40
  };
41
- //#endregion
42
41
  export { Progress };
43
42
 
44
43
  //# sourceMappingURL=progress.mjs.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { css } from "./css.mjs";
5
5
  import { jsx } from "hono/jsx/jsx-runtime";
6
- //#region src/ui/segmented.tsx
6
+ // src/ui/segmented.tsx
7
7
  const segmentedRoot = css`
8
8
  /* segmented */
9
9
  display: inline-flex;
@@ -71,7 +71,6 @@ const Segmented = ({ items, label, ...rest }) => {
71
71
  }, item.value))
72
72
  });
73
73
  };
74
- //#endregion
75
74
  export { Segmented };
76
75
 
77
76
  //# sourceMappingURL=segmented.mjs.map
@@ -10,7 +10,7 @@ import { Dropdown, DropdownDivider, DropdownItem } from "../dropdown.mjs";
10
10
  import { csrfField } from "../csrf-field.mjs";
11
11
  import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
12
12
  import { safeImageUrl } from "@storyshelf/core/urls";
13
- //#region src/ui/shell/account.tsx
13
+ // src/ui/shell/account.tsx
14
14
  /** Styles for the account widgets that live in the sidebar footer. */
15
15
  const accountRoot = css`
16
16
  /* account */
@@ -155,7 +155,6 @@ const UserMenu = () => {
155
155
  })
156
156
  });
157
157
  };
158
- //#endregion
159
158
  export { ThemeMenu, UserMenu };
160
159
 
161
160
  //# sourceMappingURL=account.mjs.map
@@ -5,7 +5,7 @@ import { css } from "../css.mjs";
5
5
  import { Icon } from "../icons/icon.mjs";
6
6
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
7
7
  import { safeImageUrl } from "@storyshelf/core/urls";
8
- //#region src/ui/shell/bare.tsx
8
+ // src/ui/shell/bare.tsx
9
9
  /** Chrome-free page for signed-out flows: brand over a centered column. */
10
10
  const bareRoot = css`
11
11
  /* bare */
@@ -63,7 +63,6 @@ const BareShell = ({ name, logo, children }) => {
63
63
  })]
64
64
  });
65
65
  };
66
- //#endregion
67
66
  export { BareShell };
68
67
 
69
68
  //# sourceMappingURL=bare.mjs.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/ui/shell/boot-script.ts
4
+ // src/ui/shell/boot-script.ts
5
5
  /**
6
6
  * Runs in `<head>` before first paint so the saved theme and sidebar state
7
7
  * apply without a flash. Exposes cookie helpers to the client script.
@@ -40,7 +40,6 @@ function bootScript() {
40
40
  })();
41
41
  `.trim();
42
42
  }
43
- //#endregion
44
43
  export { bootScript };
45
44
 
46
45
  //# sourceMappingURL=boot-script.mjs.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { iconSpriteHref } from "../icons/sprite.mjs";
5
5
  import { reviewScript } from "./review-script.mjs";
6
- //#region src/ui/shell/client-script.ts
6
+ // src/ui/shell/client-script.ts
7
7
  /** Attach the CSRF token to every HTMX request. */
8
8
  function csrfPart() {
9
9
  return `
@@ -338,7 +338,6 @@ function clientScript() {
338
338
  })();
339
339
  `.trim();
340
340
  }
341
- //#endregion
342
341
  export { clientScript };
343
342
 
344
343
  //# sourceMappingURL=client-script.mjs.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/ui/shell/nav.ts
4
+ // src/ui/shell/nav.ts
5
5
  /**
6
6
  * Entries shown for the current project. Entries for `testing` routes (Jobs) drop out on a
7
7
  * hosting-only instance, where those routes answer 404.
@@ -109,7 +109,6 @@ function sectionCrumb(nav, urls) {
109
109
  }
110
110
  }[nav?.active ?? ""] ?? null;
111
111
  }
112
- //#endregion
113
112
  export { adminNavItems, breadcrumbs, developerNavItems, projectNavItems };
114
113
 
115
114
  //# sourceMappingURL=nav.mjs.map