@storyshelf/app 0.5.3

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/dist/app-types.d.mts +54 -0
  4. package/dist/asset-manifest.mjs +41 -0
  5. package/dist/asset-manifest.mjs.map +1 -0
  6. package/dist/asset-paths.mjs +23 -0
  7. package/dist/asset-paths.mjs.map +1 -0
  8. package/dist/assets/htmx.min.js +3304 -0
  9. package/dist/assets/mark.svg +1 -0
  10. package/dist/capture-setup.mjs +139 -0
  11. package/dist/capture-setup.mjs.map +1 -0
  12. package/dist/credential-keys.mjs +20 -0
  13. package/dist/credential-keys.mjs.map +1 -0
  14. package/dist/credentials.mjs +56 -0
  15. package/dist/credentials.mjs.map +1 -0
  16. package/dist/index.d.mts +18 -0
  17. package/dist/index.mjs +310 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/lifecycle.mjs +132 -0
  20. package/dist/lifecycle.mjs.map +1 -0
  21. package/dist/middleware/auth-gate.mjs +36 -0
  22. package/dist/middleware/auth-gate.mjs.map +1 -0
  23. package/dist/middleware/csrf.mjs +75 -0
  24. package/dist/middleware/csrf.mjs.map +1 -0
  25. package/dist/middleware/rate-limit.mjs +68 -0
  26. package/dist/middleware/rate-limit.mjs.map +1 -0
  27. package/dist/middleware/request-log.mjs +30 -0
  28. package/dist/middleware/request-log.mjs.map +1 -0
  29. package/dist/middleware/server-timing.mjs +39 -0
  30. package/dist/middleware/server-timing.mjs.map +1 -0
  31. package/dist/middleware/setup-gate.mjs +37 -0
  32. package/dist/middleware/setup-gate.mjs.map +1 -0
  33. package/dist/middleware/store-scope.mjs +33 -0
  34. package/dist/middleware/store-scope.mjs.map +1 -0
  35. package/dist/notify.d.mts +17 -0
  36. package/dist/notify.mjs +141 -0
  37. package/dist/notify.mjs.map +1 -0
  38. package/dist/pages/admin-system.mjs +243 -0
  39. package/dist/pages/admin-system.mjs.map +1 -0
  40. package/dist/pages/build-detail.mjs +392 -0
  41. package/dist/pages/build-detail.mjs.map +1 -0
  42. package/dist/pages/build-diff-actions.mjs +162 -0
  43. package/dist/pages/build-diff-actions.mjs.map +1 -0
  44. package/dist/pages/build-diff-comments.mjs +104 -0
  45. package/dist/pages/build-diff-comments.mjs.map +1 -0
  46. package/dist/pages/build-diff-header.mjs +117 -0
  47. package/dist/pages/build-diff-header.mjs.map +1 -0
  48. package/dist/pages/build-diff-model.mjs +48 -0
  49. package/dist/pages/build-diff-model.mjs.map +1 -0
  50. package/dist/pages/build-diff-nav.mjs +122 -0
  51. package/dist/pages/build-diff-nav.mjs.map +1 -0
  52. package/dist/pages/build-diff-shortcuts.mjs +78 -0
  53. package/dist/pages/build-diff-shortcuts.mjs.map +1 -0
  54. package/dist/pages/build-diff-stale.mjs +70 -0
  55. package/dist/pages/build-diff-stale.mjs.map +1 -0
  56. package/dist/pages/build-diff-viewer.mjs +70 -0
  57. package/dist/pages/build-diff-viewer.mjs.map +1 -0
  58. package/dist/pages/build-diff.mjs +110 -0
  59. package/dist/pages/build-diff.mjs.map +1 -0
  60. package/dist/pages/compute-jobs.mjs +165 -0
  61. package/dist/pages/compute-jobs.mjs.map +1 -0
  62. package/dist/pages/invite.mjs +91 -0
  63. package/dist/pages/invite.mjs.map +1 -0
  64. package/dist/pages/label-detail.mjs +154 -0
  65. package/dist/pages/label-detail.mjs.map +1 -0
  66. package/dist/pages/library.mjs +343 -0
  67. package/dist/pages/library.mjs.map +1 -0
  68. package/dist/pages/login.mjs +117 -0
  69. package/dist/pages/login.mjs.map +1 -0
  70. package/dist/pages/passkey-ceremony.mjs +97 -0
  71. package/dist/pages/passkey-ceremony.mjs.map +1 -0
  72. package/dist/pages/profile.mjs +364 -0
  73. package/dist/pages/profile.mjs.map +1 -0
  74. package/dist/pages/project-builds-table.mjs +151 -0
  75. package/dist/pages/project-builds-table.mjs.map +1 -0
  76. package/dist/pages/project-builds.mjs +143 -0
  77. package/dist/pages/project-builds.mjs.map +1 -0
  78. package/dist/pages/project-create.mjs +90 -0
  79. package/dist/pages/project-create.mjs.map +1 -0
  80. package/dist/pages/project-settings.mjs +95 -0
  81. package/dist/pages/project-settings.mjs.map +1 -0
  82. package/dist/pages/projects-card.mjs +194 -0
  83. package/dist/pages/projects-card.mjs.map +1 -0
  84. package/dist/pages/projects.mjs +129 -0
  85. package/dist/pages/projects.mjs.map +1 -0
  86. package/dist/pages/root.mjs +98 -0
  87. package/dist/pages/root.mjs.map +1 -0
  88. package/dist/pages/settings-general.mjs +138 -0
  89. package/dist/pages/settings-general.mjs.map +1 -0
  90. package/dist/pages/settings-labels.mjs +107 -0
  91. package/dist/pages/settings-labels.mjs.map +1 -0
  92. package/dist/pages/settings-members.mjs +192 -0
  93. package/dist/pages/settings-members.mjs.map +1 -0
  94. package/dist/pages/settings-notifications.mjs +240 -0
  95. package/dist/pages/settings-notifications.mjs.map +1 -0
  96. package/dist/pages/settings-status.mjs +137 -0
  97. package/dist/pages/settings-status.mjs.map +1 -0
  98. package/dist/pages/settings-tests.mjs +111 -0
  99. package/dist/pages/settings-tests.mjs.map +1 -0
  100. package/dist/pages/settings-tokens.mjs +90 -0
  101. package/dist/pages/settings-tokens.mjs.map +1 -0
  102. package/dist/pages/settings-webhooks.mjs +104 -0
  103. package/dist/pages/settings-webhooks.mjs.map +1 -0
  104. package/dist/pages/storybook.mjs +116 -0
  105. package/dist/pages/storybook.mjs.map +1 -0
  106. package/dist/retention-timer.mjs +65 -0
  107. package/dist/retention-timer.mjs.map +1 -0
  108. package/dist/routers/admin-pages.mjs +48 -0
  109. package/dist/routers/admin-pages.mjs.map +1 -0
  110. package/dist/routers/admin.mjs +113 -0
  111. package/dist/routers/admin.mjs.map +1 -0
  112. package/dist/routers/assets.mjs +64 -0
  113. package/dist/routers/assets.mjs.map +1 -0
  114. package/dist/routers/attempts.mjs +96 -0
  115. package/dist/routers/attempts.mjs.map +1 -0
  116. package/dist/routers/auth-sync.mjs +64 -0
  117. package/dist/routers/auth-sync.mjs.map +1 -0
  118. package/dist/routers/auth.mjs +236 -0
  119. package/dist/routers/auth.mjs.map +1 -0
  120. package/dist/routers/baseline-guard.mjs +83 -0
  121. package/dist/routers/baseline-guard.mjs.map +1 -0
  122. package/dist/routers/builds.handlers.mjs +217 -0
  123. package/dist/routers/builds.handlers.mjs.map +1 -0
  124. package/dist/routers/builds.mjs +381 -0
  125. package/dist/routers/builds.mjs.map +1 -0
  126. package/dist/routers/comments.mjs +109 -0
  127. package/dist/routers/comments.mjs.map +1 -0
  128. package/dist/routers/flash.mjs +35 -0
  129. package/dist/routers/flash.mjs.map +1 -0
  130. package/dist/routers/health-report.mjs +159 -0
  131. package/dist/routers/health-report.mjs.map +1 -0
  132. package/dist/routers/health.mjs +46 -0
  133. package/dist/routers/health.mjs.map +1 -0
  134. package/dist/routers/helpers.mjs +110 -0
  135. package/dist/routers/helpers.mjs.map +1 -0
  136. package/dist/routers/htmx.mjs +24 -0
  137. package/dist/routers/htmx.mjs.map +1 -0
  138. package/dist/routers/labels.mjs +118 -0
  139. package/dist/routers/labels.mjs.map +1 -0
  140. package/dist/routers/media.mjs +53 -0
  141. package/dist/routers/media.mjs.map +1 -0
  142. package/dist/routers/members.mjs +177 -0
  143. package/dist/routers/members.mjs.map +1 -0
  144. package/dist/routers/notification-channels.mjs +190 -0
  145. package/dist/routers/notification-channels.mjs.map +1 -0
  146. package/dist/routers/notification-subscriptions.mjs +120 -0
  147. package/dist/routers/notification-subscriptions.mjs.map +1 -0
  148. package/dist/routers/profile.mjs +276 -0
  149. package/dist/routers/profile.mjs.map +1 -0
  150. package/dist/routers/projects.mjs +133 -0
  151. package/dist/routers/projects.mjs.map +1 -0
  152. package/dist/routers/review-next.mjs +47 -0
  153. package/dist/routers/review-next.mjs.map +1 -0
  154. package/dist/routers/schemas.mjs +337 -0
  155. package/dist/routers/schemas.mjs.map +1 -0
  156. package/dist/routers/settings-general.mjs +104 -0
  157. package/dist/routers/settings-general.mjs.map +1 -0
  158. package/dist/routers/settings-labels.mjs +52 -0
  159. package/dist/routers/settings-labels.mjs.map +1 -0
  160. package/dist/routers/settings-members.mjs +58 -0
  161. package/dist/routers/settings-members.mjs.map +1 -0
  162. package/dist/routers/settings-notifications.mjs +108 -0
  163. package/dist/routers/settings-notifications.mjs.map +1 -0
  164. package/dist/routers/settings-status.mjs +77 -0
  165. package/dist/routers/settings-status.mjs.map +1 -0
  166. package/dist/routers/settings-tokens.mjs +40 -0
  167. package/dist/routers/settings-tokens.mjs.map +1 -0
  168. package/dist/routers/settings-webhooks.mjs +67 -0
  169. package/dist/routers/settings-webhooks.mjs.map +1 -0
  170. package/dist/routers/settings.handlers.mjs +149 -0
  171. package/dist/routers/settings.handlers.mjs.map +1 -0
  172. package/dist/routers/settings.mjs +25 -0
  173. package/dist/routers/settings.mjs.map +1 -0
  174. package/dist/routers/snapshots.mjs +240 -0
  175. package/dist/routers/snapshots.mjs.map +1 -0
  176. package/dist/routers/status-configs.mjs +108 -0
  177. package/dist/routers/status-configs.mjs.map +1 -0
  178. package/dist/routers/storybook.mjs +171 -0
  179. package/dist/routers/storybook.mjs.map +1 -0
  180. package/dist/routers/tokens.mjs +91 -0
  181. package/dist/routers/tokens.mjs.map +1 -0
  182. package/dist/routers/ui.mjs +166 -0
  183. package/dist/routers/ui.mjs.map +1 -0
  184. package/dist/routers/webhooks.mjs +97 -0
  185. package/dist/routers/webhooks.mjs.map +1 -0
  186. package/dist/routers/well-known.mjs +43 -0
  187. package/dist/routers/well-known.mjs.map +1 -0
  188. package/dist/runtime.mjs +52 -0
  189. package/dist/runtime.mjs.map +1 -0
  190. package/dist/store.mjs +20 -0
  191. package/dist/store.mjs.map +1 -0
  192. package/dist/ui/avatar.mjs +76 -0
  193. package/dist/ui/avatar.mjs.map +1 -0
  194. package/dist/ui/buttons.mjs +205 -0
  195. package/dist/ui/buttons.mjs.map +1 -0
  196. package/dist/ui/code-block.mjs +47 -0
  197. package/dist/ui/code-block.mjs.map +1 -0
  198. package/dist/ui/compare-stage.mjs +344 -0
  199. package/dist/ui/compare-stage.mjs.map +1 -0
  200. package/dist/ui/csrf-field.mjs +20 -0
  201. package/dist/ui/csrf-field.mjs.map +1 -0
  202. package/dist/ui/css.mjs +36 -0
  203. package/dist/ui/css.mjs.map +1 -0
  204. package/dist/ui/document.mjs +130 -0
  205. package/dist/ui/document.mjs.map +1 -0
  206. package/dist/ui/dropdown.mjs +181 -0
  207. package/dist/ui/dropdown.mjs.map +1 -0
  208. package/dist/ui/feedback.mjs +341 -0
  209. package/dist/ui/feedback.mjs.map +1 -0
  210. package/dist/ui/filter-input.mjs +58 -0
  211. package/dist/ui/filter-input.mjs.map +1 -0
  212. package/dist/ui/forms.mjs +291 -0
  213. package/dist/ui/forms.mjs.map +1 -0
  214. package/dist/ui/icons/icon.mjs +53 -0
  215. package/dist/ui/icons/icon.mjs.map +1 -0
  216. package/dist/ui/icons/paths.mjs +61 -0
  217. package/dist/ui/icons/paths.mjs.map +1 -0
  218. package/dist/ui/icons/sprite.mjs +26 -0
  219. package/dist/ui/icons/sprite.mjs.map +1 -0
  220. package/dist/ui/kbd.mjs +33 -0
  221. package/dist/ui/kbd.mjs.map +1 -0
  222. package/dist/ui/layout.mjs +173 -0
  223. package/dist/ui/layout.mjs.map +1 -0
  224. package/dist/ui/progress.mjs +44 -0
  225. package/dist/ui/progress.mjs.map +1 -0
  226. package/dist/ui/segmented.mjs +77 -0
  227. package/dist/ui/segmented.mjs.map +1 -0
  228. package/dist/ui/shell/account.mjs +161 -0
  229. package/dist/ui/shell/account.mjs.map +1 -0
  230. package/dist/ui/shell/bare.mjs +69 -0
  231. package/dist/ui/shell/bare.mjs.map +1 -0
  232. package/dist/ui/shell/boot-script.mjs +46 -0
  233. package/dist/ui/shell/boot-script.mjs.map +1 -0
  234. package/dist/ui/shell/client-script.mjs +259 -0
  235. package/dist/ui/shell/client-script.mjs.map +1 -0
  236. package/dist/ui/shell/nav.mjs +111 -0
  237. package/dist/ui/shell/nav.mjs.map +1 -0
  238. package/dist/ui/shell/review-script.mjs +195 -0
  239. package/dist/ui/shell/review-script.mjs.map +1 -0
  240. package/dist/ui/shell/sidebar.mjs +323 -0
  241. package/dist/ui/shell/sidebar.mjs.map +1 -0
  242. package/dist/ui/shell/topbar.mjs +268 -0
  243. package/dist/ui/shell/topbar.mjs.map +1 -0
  244. package/dist/ui/stacks.mjs +82 -0
  245. package/dist/ui/stacks.mjs.map +1 -0
  246. package/dist/ui/styles/base.mjs +41 -0
  247. package/dist/ui/styles/base.mjs.map +1 -0
  248. package/dist/ui/styles/behavior.mjs +43 -0
  249. package/dist/ui/styles/behavior.mjs.map +1 -0
  250. package/dist/ui/styles/review-layout.mjs +347 -0
  251. package/dist/ui/styles/review-layout.mjs.map +1 -0
  252. package/dist/ui/styles/review.mjs +86 -0
  253. package/dist/ui/styles/review.mjs.map +1 -0
  254. package/dist/ui/styles/tokens.mjs +132 -0
  255. package/dist/ui/styles/tokens.mjs.map +1 -0
  256. package/dist/ui/styles.mjs +19 -0
  257. package/dist/ui/styles.mjs.map +1 -0
  258. package/dist/ui/subnav.mjs +91 -0
  259. package/dist/ui/subnav.mjs.map +1 -0
  260. package/dist/ui/table.mjs +73 -0
  261. package/dist/ui/table.mjs.map +1 -0
  262. package/dist/ui/theme.mjs +64 -0
  263. package/dist/ui/theme.mjs.map +1 -0
  264. package/dist/ui/thumbnail.mjs +51 -0
  265. package/dist/ui/thumbnail.mjs.map +1 -0
  266. package/dist/ui/time.mjs +50 -0
  267. package/dist/ui/time.mjs.map +1 -0
  268. package/package.json +92 -0
@@ -0,0 +1,82 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { css } from "./css.mjs";
5
+ import { jsx } from "hono/jsx/jsx-runtime";
6
+ //#region src/ui/stacks.tsx
7
+ const hGaps = {
8
+ sm: "0.5rem",
9
+ md: "0.75rem",
10
+ lg: "1rem"
11
+ };
12
+ const hAligns = {
13
+ center: "center",
14
+ start: "flex-start",
15
+ end: "flex-end",
16
+ baseline: "baseline"
17
+ };
18
+ const hJustifies = {
19
+ start: "flex-start",
20
+ center: "center",
21
+ end: "flex-end",
22
+ between: "space-between"
23
+ };
24
+ const vGaps = hGaps;
25
+ const vAligns = {
26
+ stretch: "stretch",
27
+ start: "flex-start",
28
+ center: "center",
29
+ end: "flex-end"
30
+ };
31
+ function hstackClass(props) {
32
+ const { gap, align, justify, wrap } = props;
33
+ return css`
34
+ /* hstack */
35
+ display: flex;
36
+ gap: ${hGaps[gap]};
37
+ align-items: ${hAligns[align]};
38
+ justify-content: ${hJustifies[justify]};
39
+ flex-wrap: ${wrap ? "wrap" : "nowrap"};
40
+ `;
41
+ }
42
+ function vstackClass(props) {
43
+ const { gap, align } = props;
44
+ return css`
45
+ /* vstack */
46
+ display: grid;
47
+ gap: ${vGaps[gap]};
48
+ align-items: ${vAligns[align]};
49
+ `;
50
+ }
51
+ /**
52
+ * Horizontal stack (replaces `row-actions` divs and ad-hoc flex rows).
53
+ * Defaults match the old `row-actions` utility exactly.
54
+ */
55
+ const HStack = ({ gap = "sm", align = "center", justify = "start", wrap = true, children }) => {
56
+ return /* @__PURE__ */ jsx("div", {
57
+ class: hstackClass({
58
+ gap,
59
+ align,
60
+ justify,
61
+ wrap
62
+ }),
63
+ children
64
+ });
65
+ };
66
+ /**
67
+ * Vertical stack (replaces `stack` divs and ad-hoc grid rows).
68
+ * Defaults match the old `stack` utility exactly.
69
+ */
70
+ const VStack = ({ gap = "md", align = "stretch", children }) => {
71
+ return /* @__PURE__ */ jsx("div", {
72
+ class: vstackClass({
73
+ gap,
74
+ align
75
+ }),
76
+ children
77
+ });
78
+ };
79
+ //#endregion
80
+ export { HStack, VStack };
81
+
82
+ //# sourceMappingURL=stacks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stacks.mjs","names":[],"sources":["../../src/ui/stacks.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst hGaps = { sm: \"0.5rem\", md: \"0.75rem\", lg: \"1rem\" } as const;\nconst hAligns = {\n center: \"center\",\n start: \"flex-start\",\n end: \"flex-end\",\n baseline: \"baseline\",\n} as const;\nconst hJustifies = {\n start: \"flex-start\",\n center: \"center\",\n end: \"flex-end\",\n between: \"space-between\",\n} as const;\nconst vGaps = hGaps;\nconst vAligns = {\n stretch: \"stretch\",\n start: \"flex-start\",\n center: \"center\",\n end: \"flex-end\",\n} as const;\n\ntype HStackProps = {\n gap?: keyof typeof hGaps;\n align?: keyof typeof hAligns;\n justify?: keyof typeof hJustifies;\n wrap?: boolean;\n children?: unknown;\n};\n\ntype VStackProps = {\n gap?: keyof typeof vGaps;\n align?: keyof typeof vAligns;\n children?: unknown;\n};\n\nfunction hstackClass(props: Required<Omit<HStackProps, \"children\">>): Promise<string> {\n const { gap, align, justify, wrap } = props;\n return css`\n /* hstack */\n display: flex;\n gap: ${hGaps[gap]};\n align-items: ${hAligns[align]};\n justify-content: ${hJustifies[justify]};\n flex-wrap: ${wrap ? \"wrap\" : \"nowrap\"};\n `;\n}\n\nfunction vstackClass(props: Required<Omit<VStackProps, \"children\">>): Promise<string> {\n const { gap, align } = props;\n return css`\n /* vstack */\n display: grid;\n gap: ${vGaps[gap]};\n align-items: ${vAligns[align]};\n `;\n}\n\n/**\n * Horizontal stack (replaces `row-actions` divs and ad-hoc flex rows).\n * Defaults match the old `row-actions` utility exactly.\n */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const HStack: FC<HStackProps> = ({\n gap = \"sm\",\n align = \"center\",\n justify = \"start\",\n wrap = true,\n children,\n}) => {\n return <div class={hstackClass({ gap, align, justify, wrap })}>{children}</div>;\n};\n\n/**\n * Vertical stack (replaces `stack` divs and ad-hoc grid rows).\n * Defaults match the old `stack` utility exactly.\n */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const VStack: FC<VStackProps> = ({ gap = \"md\", align = \"stretch\", children }) => {\n return <div class={vstackClass({ gap, align })}>{children}</div>;\n};\n"],"mappings":";;;;;;AAKA,MAAM,QAAQ;CAAE,IAAI;CAAU,IAAI;CAAW,IAAI;AAAO;AACxD,MAAM,UAAU;CACd,QAAQ;CACR,OAAO;CACP,KAAK;CACL,UAAU;AACZ;AACA,MAAM,aAAa;CACjB,OAAO;CACP,QAAQ;CACR,KAAK;CACL,SAAS;AACX;AACA,MAAM,QAAQ;AACd,MAAM,UAAU;CACd,SAAS;CACT,OAAO;CACP,QAAQ;CACR,KAAK;AACP;AAgBA,SAAS,YAAY,OAAiE;CACpF,MAAM,EAAE,KAAK,OAAO,SAAS,SAAS;CACtC,OAAO,GAAG;;;WAGD,MAAM,KAAK;mBACH,QAAQ,OAAO;uBACX,WAAW,SAAS;iBAC1B,OAAO,SAAS,SAAS;;AAE1C;AAEA,SAAS,YAAY,OAAiE;CACpF,MAAM,EAAE,KAAK,UAAU;CACvB,OAAO,GAAG;;;WAGD,MAAM,KAAK;mBACH,QAAQ,OAAO;;AAElC;;;;;AAOA,MAAa,UAA2B,EACtC,MAAM,MACN,QAAQ,UACR,UAAU,SACV,OAAO,MACP,eACI;CACJ,OAAO,oBAAC,OAAD;EAAK,OAAO,YAAY;GAAE;GAAK;GAAO;GAAS;EAAK,CAAC;EAAI;CAAc,CAAA;AAChF;;;;;AAOA,MAAa,UAA2B,EAAE,MAAM,MAAM,QAAQ,WAAW,eAAe;CACtF,OAAO,oBAAC,OAAD;EAAK,OAAO,YAAY;GAAE;GAAK;EAAM,CAAC;EAAI;CAAc,CAAA;AACjE"}
@@ -0,0 +1,41 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/ui/styles/base.ts
5
+ /** Base reset, typography, focus ring, and shared utilities. */
6
+ function baseCss() {
7
+ return `${resetCss()}\n${utilityCss()}`;
8
+ }
9
+ function resetCss() {
10
+ return `
11
+ * { box-sizing: border-box; }
12
+ html { scroll-behavior: smooth; }
13
+ body { margin: 0; font-size: 14px; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background: var(--surface-base); color: var(--text-primary); line-height: 1.5; -webkit-font-smoothing: antialiased; }
14
+ a { color: var(--accent); text-decoration: none; }
15
+ a:hover { text-decoration: underline; }
16
+ a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }
17
+ code, kbd, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; }
18
+ kbd { display: inline-block; padding: .1rem .35rem; border: 1px solid var(--border); border-bottom-width: 2px; border-radius: var(--radius-sm); background: var(--surface-muted); color: var(--text-secondary); font-size: .75rem; line-height: 1.2; }
19
+ [hidden] { display: none !important; }
20
+ .skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
21
+ .skip-link:focus { left: 1rem; top: 1rem; width: auto; height: auto; background: var(--surface-card); color: var(--text-primary); padding: .5rem .75rem; border-radius: var(--radius-sm); box-shadow: var(--shadow); z-index: 100; }`;
22
+ }
23
+ function utilityCss() {
24
+ return `
25
+ .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
26
+ .min-w-0 { min-width: 0; }
27
+ .max-w-cell { max-width: 32ch; }
28
+ .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
29
+ .muted { color: var(--text-secondary); }
30
+ .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
31
+ .max-w-form { max-width: 880px; }
32
+ .max-w-prose { max-width: 640px; }
33
+ .table-gap { margin-top: .75rem; }
34
+ .mt-1 { margin-top: 1rem; }
35
+ .mb-1 { margin-bottom: 1rem; }
36
+ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }`;
37
+ }
38
+ //#endregion
39
+ export { baseCss };
40
+
41
+ //# sourceMappingURL=base.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.mjs","names":[],"sources":["../../../src/ui/styles/base.ts"],"sourcesContent":["/** Base reset, typography, focus ring, and shared utilities. */\nexport function baseCss(): string {\n return `${resetCss()}\\n${utilityCss()}`;\n}\n\nfunction resetCss(): string {\n return `\n * { box-sizing: border-box; }\n html { scroll-behavior: smooth; }\n body { margin: 0; font-size: 14px; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background: var(--surface-base); color: var(--text-primary); line-height: 1.5; -webkit-font-smoothing: antialiased; }\n a { color: var(--accent); text-decoration: none; }\n a:hover { text-decoration: underline; }\n a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }\n code, kbd, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; }\n kbd { display: inline-block; padding: .1rem .35rem; border: 1px solid var(--border); border-bottom-width: 2px; border-radius: var(--radius-sm); background: var(--surface-muted); color: var(--text-secondary); font-size: .75rem; line-height: 1.2; }\n [hidden] { display: none !important; }\n .skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }\n .skip-link:focus { left: 1rem; top: 1rem; width: auto; height: auto; background: var(--surface-card); color: var(--text-primary); padding: .5rem .75rem; border-radius: var(--radius-sm); box-shadow: var(--shadow); z-index: 100; }`;\n}\n\nfunction utilityCss(): string {\n return `\n .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n .min-w-0 { min-width: 0; }\n .max-w-cell { max-width: 32ch; }\n .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .muted { color: var(--text-secondary); }\n .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }\n .max-w-form { max-width: 880px; }\n .max-w-prose { max-width: 640px; }\n .table-gap { margin-top: .75rem; }\n .mt-1 { margin-top: 1rem; }\n .mb-1 { margin-bottom: 1rem; }\n @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }`;\n}\n"],"mappings":";;;;;AACA,SAAgB,UAAkB;CAChC,OAAO,GAAG,SAAS,EAAE,IAAI,WAAW;AACtC;AAEA,SAAS,WAAmB;CAC1B,OAAO;;;;;;;;;;;;AAYT;AAEA,SAAS,aAAqB;CAC5B,OAAO;;;;;;;;;;;;;AAaT"}
@@ -0,0 +1,43 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/ui/styles/behavior.ts
5
+ /** Behavioral CSS: HTMX indicators, skeletons, tables, stats. */
6
+ function behaviorCss() {
7
+ return `${htmxCss()}\n${dataCss()}\n${chromeCss()}`;
8
+ }
9
+ function htmxCss() {
10
+ return `
11
+ html.ss-loading::before { content: ""; position: fixed; top: 0; left: 0; z-index: 90; height: 2px; width: 100%; background: var(--accent); transform-origin: left; animation: ss-load 1.2s var(--ease) infinite; }
12
+ @keyframes ss-load { 0% { transform: scaleX(0); } 70% { transform: scaleX(0.85); } 100% { transform: scaleX(1); opacity: 0; } }
13
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }
14
+ .htmx-indicator { opacity: 0; transition: opacity 150ms ease; }
15
+ .htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
16
+ button.htmx-request, form.htmx-request button, [hx-post].htmx-request { pointer-events: none; opacity: .7; }
17
+ form.htmx-request { opacity: .9; }
18
+ form.htmx-request button[type="submit"]::after { content: ""; width: .85em; height: .85em; margin-left: .4rem; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: ss-spin .7s linear infinite; }
19
+ @keyframes ss-spin { to { transform: rotate(360deg); } }
20
+ .skeleton { position: relative; overflow: hidden; background: var(--surface-muted); border-radius: var(--radius-sm); min-height: 1rem; }
21
+ .skeleton::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent); animation: skeleton 1.4s infinite; }
22
+ [data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.08), transparent); }
23
+ @keyframes skeleton { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
24
+ @media (prefers-reduced-motion: reduce) { .htmx-indicator, .htmx-request { transition: none; } .skeleton::after { animation: none; } }`;
25
+ }
26
+ function dataCss() {
27
+ return `
28
+ .grid { display: grid; gap: 1rem; }
29
+ .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
30
+ .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
31
+ @media (max-width: 880px) { .grid--2, .grid--3 { grid-template-columns: 1fr; } }`;
32
+ }
33
+ /** Truly global chrome rules (depend on ancestors outside any one subtree). */
34
+ function chromeCss() {
35
+ return `
36
+ @media (min-width: 881px) { html[data-sidebar="rail"] { --sidebar-width: 68px; } }
37
+ [data-theme-icon] { display: none; }
38
+ [data-theme="light"] [data-theme-icon="light"], [data-theme="dark"] [data-theme-icon="dark"], [data-theme="system"] [data-theme-icon="system"] { display: inline-flex; }`;
39
+ }
40
+ //#endregion
41
+ export { behaviorCss };
42
+
43
+ //# sourceMappingURL=behavior.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behavior.mjs","names":[],"sources":["../../../src/ui/styles/behavior.ts"],"sourcesContent":["/** Behavioral CSS: HTMX indicators, skeletons, tables, stats. */\nexport function behaviorCss(): string {\n return `${htmxCss()}\\n${dataCss()}\\n${chromeCss()}`;\n}\n\nfunction htmxCss(): string {\n return `\n html.ss-loading::before { content: \"\"; position: fixed; top: 0; left: 0; z-index: 90; height: 2px; width: 100%; background: var(--accent); transform-origin: left; animation: ss-load 1.2s var(--ease) infinite; }\n @keyframes ss-load { 0% { transform: scaleX(0); } 70% { transform: scaleX(0.85); } 100% { transform: scaleX(1); opacity: 0; } }\n @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }\n .htmx-indicator { opacity: 0; transition: opacity 150ms ease; }\n .htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }\n button.htmx-request, form.htmx-request button, [hx-post].htmx-request { pointer-events: none; opacity: .7; }\n form.htmx-request { opacity: .9; }\n form.htmx-request button[type=\"submit\"]::after { content: \"\"; width: .85em; height: .85em; margin-left: .4rem; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: ss-spin .7s linear infinite; }\n @keyframes ss-spin { to { transform: rotate(360deg); } }\n .skeleton { position: relative; overflow: hidden; background: var(--surface-muted); border-radius: var(--radius-sm); min-height: 1rem; }\n .skeleton::after { content: \"\"; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent); animation: skeleton 1.4s infinite; }\n [data-theme=\"dark\"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.08), transparent); }\n @keyframes skeleton { from { transform: translateX(-100%); } to { transform: translateX(100%); } }\n @media (prefers-reduced-motion: reduce) { .htmx-indicator, .htmx-request { transition: none; } .skeleton::after { animation: none; } }`;\n}\n\nfunction dataCss(): string {\n return `\n .grid { display: grid; gap: 1rem; }\n .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n @media (max-width: 880px) { .grid--2, .grid--3 { grid-template-columns: 1fr; } }`;\n}\n\n/** Truly global chrome rules (depend on ancestors outside any one subtree). */\nfunction chromeCss(): string {\n return `\n @media (min-width: 881px) { html[data-sidebar=\"rail\"] { --sidebar-width: 68px; } }\n [data-theme-icon] { display: none; }\n [data-theme=\"light\"] [data-theme-icon=\"light\"], [data-theme=\"dark\"] [data-theme-icon=\"dark\"], [data-theme=\"system\"] [data-theme-icon=\"system\"] { display: inline-flex; }`;\n}\n"],"mappings":";;;;;AACA,SAAgB,cAAsB;CACpC,OAAO,GAAG,QAAQ,EAAE,IAAI,QAAQ,EAAE,IAAI,UAAU;AAClD;AAEA,SAAS,UAAkB;CACzB,OAAO;;;;;;;;;;;;;;;AAeT;AAEA,SAAS,UAAkB;CACzB,OAAO;;;;;AAKT;;AAGA,SAAS,YAAoB;CAC3B,OAAO;;;;AAIT"}
@@ -0,0 +1,347 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { css } from "../css.mjs";
5
+ //#region src/ui/styles/review-layout.ts
6
+ /**
7
+ * Review workspace layout styles: header, filmstrip, canvas column, action
8
+ * bar, and shortcuts dialog. Shared by the `build-diff-*` page sections, so
9
+ * they live here (the documented exception to colocation).
10
+ */
11
+ const workspace = css`
12
+ /* review-workspace */
13
+ display: grid;
14
+ grid-template-columns: 300px minmax(0, 1fr);
15
+ gap: var(--space-4);
16
+ align-items: start;
17
+ @media (max-width: 1024px) {
18
+ & {
19
+ grid-template-columns: minmax(0, 1fr);
20
+ }
21
+ }
22
+ `;
23
+ const canvasColumn = css`
24
+ /* review-canvas */
25
+ display: grid;
26
+ gap: var(--space-4);
27
+ min-width: 0;
28
+ `;
29
+ const reviewHeader = css`
30
+ /* review-header */
31
+ display: flex;
32
+ flex-wrap: wrap;
33
+ gap: var(--space-4);
34
+ align-items: flex-start;
35
+ justify-content: space-between;
36
+ margin-bottom: var(--space-4);
37
+ padding: var(--space-4);
38
+ background: linear-gradient(var(--accent-wash), var(--accent-wash)), var(--surface-card);
39
+ border: 1px solid var(--border);
40
+ border-radius: var(--radius-lg);
41
+ box-shadow: var(--shadow);
42
+ `;
43
+ const headerMain = css`
44
+ /* review-header-main */
45
+ display: grid;
46
+ gap: var(--space-1);
47
+ min-width: 0;
48
+ `;
49
+ const headerTitle = css`
50
+ /* review-header-title */
51
+ display: flex;
52
+ flex-wrap: wrap;
53
+ gap: var(--space-2);
54
+ align-items: center;
55
+ margin: 0;
56
+ font-size: var(--text-xl);
57
+ font-weight: 700;
58
+ letter-spacing: -0.02em;
59
+ line-height: var(--leading-tight);
60
+ `;
61
+ const headerMeta = css`
62
+ /* review-header-meta */
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ gap: var(--space-2);
66
+ align-items: center;
67
+ margin: 0;
68
+ color: var(--text-secondary);
69
+ font-size: var(--text-sm);
70
+ `;
71
+ const headerSide = css`
72
+ /* review-header-side */
73
+ display: grid;
74
+ gap: var(--space-3);
75
+ justify-items: end;
76
+ min-width: min(100%, 280px);
77
+ @media (max-width: 720px) {
78
+ & {
79
+ justify-items: stretch;
80
+ width: 100%;
81
+ }
82
+ }
83
+ `;
84
+ const headerProgress = css`
85
+ /* review-header-progress */
86
+ display: grid;
87
+ gap: var(--space-1);
88
+ width: 100%;
89
+ font-size: var(--text-sm);
90
+ color: var(--text-secondary);
91
+ font-variant-numeric: tabular-nums;
92
+ `;
93
+ const headerActions = css`
94
+ /* review-header-actions */
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ gap: var(--space-2);
98
+ align-items: center;
99
+ justify-content: flex-end;
100
+ `;
101
+ const filmstrip = css`
102
+ /* filmstrip */
103
+ position: sticky;
104
+ top: calc(var(--topbar-height) + var(--space-4));
105
+ display: flex;
106
+ flex-direction: column;
107
+ max-height: calc(100vh - var(--topbar-height) - var(--space-8) - var(--space-4));
108
+ overflow: hidden;
109
+ background: var(--surface-card);
110
+ border: 1px solid var(--border);
111
+ border-radius: var(--radius-lg);
112
+ box-shadow: var(--shadow);
113
+ @media (max-width: 1024px) {
114
+ & {
115
+ position: static;
116
+ max-height: none;
117
+ }
118
+ }
119
+ `;
120
+ const filmstripHead = css`
121
+ /* filmstrip-head */
122
+ display: grid;
123
+ gap: var(--space-2);
124
+ padding: var(--space-3);
125
+ border-bottom: 1px solid var(--border);
126
+ `;
127
+ const filmstripList = css`
128
+ /* filmstrip-list */
129
+ overflow: auto;
130
+ overscroll-behavior: contain;
131
+ @media (max-width: 1024px) {
132
+ & {
133
+ display: flex;
134
+ overflow-x: auto;
135
+ overflow-y: hidden;
136
+ }
137
+ }
138
+ `;
139
+ const filmItem = css`
140
+ /* film-item */
141
+ display: grid;
142
+ grid-template-columns: 72px minmax(0, 1fr);
143
+ gap: var(--space-3);
144
+ align-items: center;
145
+ padding: var(--space-2) var(--space-3);
146
+ border-bottom: 1px solid var(--border-subtle);
147
+ border-left: 3px solid transparent;
148
+ color: inherit;
149
+ text-decoration: none;
150
+ transition: background var(--dur-fast) var(--ease);
151
+ &:hover {
152
+ background: var(--surface-subtle);
153
+ text-decoration: none;
154
+ }
155
+ @media (max-width: 1024px) {
156
+ & {
157
+ flex: none;
158
+ width: 240px;
159
+ border-bottom: 0;
160
+ border-left: 0;
161
+ border-right: 1px solid var(--border-subtle);
162
+ border-top: 3px solid transparent;
163
+ }
164
+ }
165
+ `;
166
+ const filmItemActive = css`
167
+ /* film-item-active */
168
+ ${filmItem}
169
+ background: var(--accent-subtle);
170
+ border-left-color: var(--accent);
171
+ &:hover {
172
+ background: var(--accent-subtle);
173
+ }
174
+ @media (max-width: 1024px) {
175
+ & {
176
+ border-left-color: transparent;
177
+ border-top-color: var(--accent);
178
+ }
179
+ }
180
+ `;
181
+ const filmBody = css`
182
+ /* film-body */
183
+ display: grid;
184
+ gap: 2px;
185
+ min-width: 0;
186
+ `;
187
+ const filmTitle = css`
188
+ /* film-title */
189
+ overflow: hidden;
190
+ font-size: var(--text-base);
191
+ font-weight: 600;
192
+ text-overflow: ellipsis;
193
+ white-space: nowrap;
194
+ `;
195
+ const filmSub = css`
196
+ /* film-sub */
197
+ overflow: hidden;
198
+ color: var(--text-secondary);
199
+ font-size: var(--text-xs);
200
+ text-overflow: ellipsis;
201
+ white-space: nowrap;
202
+ `;
203
+ const filmStatus = css`
204
+ /* film-status */
205
+ display: inline-flex;
206
+ gap: var(--space-1);
207
+ align-items: center;
208
+ font-size: var(--text-xs);
209
+ font-weight: 600;
210
+ font-variant-numeric: tabular-nums;
211
+ `;
212
+ const dot = css`
213
+ /* status-dot */
214
+ display: inline-block;
215
+ width: 8px;
216
+ height: 8px;
217
+ border-radius: var(--radius-pill);
218
+ background: var(--text-muted);
219
+ `;
220
+ const statusDots = {
221
+ neutral: dot,
222
+ success: css`
223
+ /* status-dot-success */
224
+ ${dot}
225
+ background: var(--status-approved);
226
+ `,
227
+ warning: css`
228
+ /* status-dot-warning */
229
+ ${dot}
230
+ background: var(--status-new);
231
+ `,
232
+ danger: css`
233
+ /* status-dot-danger */
234
+ ${dot}
235
+ background: var(--status-rejected);
236
+ `,
237
+ info: css`
238
+ /* status-dot-info */
239
+ ${dot}
240
+ background: var(--status-info);
241
+ `
242
+ };
243
+ const viewerHead = css`
244
+ /* viewer-head */
245
+ display: flex;
246
+ flex-wrap: wrap;
247
+ gap: var(--space-2) var(--space-4);
248
+ align-items: baseline;
249
+ justify-content: space-between;
250
+ `;
251
+ const viewerTitle = css`
252
+ /* viewer-title */
253
+ margin: 0;
254
+ font-size: var(--text-lg);
255
+ font-weight: 650;
256
+ letter-spacing: -0.01em;
257
+ `;
258
+ const viewerMeta = css`
259
+ /* viewer-meta */
260
+ display: flex;
261
+ flex-wrap: wrap;
262
+ gap: var(--space-2);
263
+ align-items: center;
264
+ margin: 0;
265
+ color: var(--text-secondary);
266
+ font-size: var(--text-sm);
267
+ `;
268
+ const actionBar = css`
269
+ /* review-action-bar */
270
+ position: sticky;
271
+ bottom: var(--space-4);
272
+ z-index: 20;
273
+ display: flex;
274
+ flex-wrap: wrap;
275
+ gap: var(--space-3);
276
+ align-items: center;
277
+ justify-content: space-between;
278
+ padding: var(--space-2) var(--space-3);
279
+ background: color-mix(in srgb, var(--surface-card) 92%, transparent);
280
+ backdrop-filter: blur(10px);
281
+ border: 1px solid var(--border);
282
+ border-radius: var(--radius-lg);
283
+ box-shadow: var(--shadow-2);
284
+ `;
285
+ const actionGroup = css`
286
+ /* review-action-group */
287
+ display: flex;
288
+ gap: var(--space-2);
289
+ align-items: center;
290
+ `;
291
+ const actionPosition = css`
292
+ /* review-action-position */
293
+ min-width: 4.5rem;
294
+ color: var(--text-secondary);
295
+ font-size: var(--text-sm);
296
+ font-variant-numeric: tabular-nums;
297
+ text-align: center;
298
+ `;
299
+ const actionHints = css`
300
+ /* review-action-hints */
301
+ display: flex;
302
+ gap: var(--space-2);
303
+ align-items: center;
304
+ color: var(--text-secondary);
305
+ font-size: var(--text-xs);
306
+ @media (max-width: 720px) {
307
+ & {
308
+ display: none;
309
+ }
310
+ }
311
+ `;
312
+ const shortcutsDialog = css`
313
+ /* shortcuts-dialog */
314
+ width: min(440px, calc(100vw - 2rem));
315
+ padding: var(--space-5);
316
+ color: var(--text-primary);
317
+ background: var(--surface-card);
318
+ border: 1px solid var(--border);
319
+ border-radius: var(--radius-lg);
320
+ box-shadow: var(--shadow-3);
321
+ &::backdrop {
322
+ background: rgb(0 0 0 / 0.45);
323
+ }
324
+ h2 {
325
+ margin: 0 0 var(--space-3);
326
+ font-size: var(--text-lg);
327
+ }
328
+ dl {
329
+ display: grid;
330
+ grid-template-columns: auto 1fr;
331
+ gap: var(--space-2) var(--space-4);
332
+ margin: 0 0 var(--space-4);
333
+ font-size: var(--text-base);
334
+ }
335
+ dt {
336
+ display: flex;
337
+ gap: var(--space-1);
338
+ }
339
+ dd {
340
+ margin: 0;
341
+ color: var(--text-secondary);
342
+ }
343
+ `;
344
+ //#endregion
345
+ export { actionBar, actionGroup, actionHints, actionPosition, canvasColumn, filmBody, filmItem, filmItemActive, filmStatus, filmSub, filmTitle, filmstrip, filmstripHead, filmstripList, headerActions, headerMain, headerMeta, headerProgress, headerSide, headerTitle, reviewHeader, shortcutsDialog, statusDots, viewerHead, viewerMeta, viewerTitle, workspace };
346
+
347
+ //# sourceMappingURL=review-layout.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-layout.mjs","names":[],"sources":["../../../src/ui/styles/review-layout.ts"],"sourcesContent":["/**\n * Review workspace layout styles: header, filmstrip, canvas column, action\n * bar, and shortcuts dialog. Shared by the `build-diff-*` page sections, so\n * they live here (the documented exception to colocation).\n */\nimport { css } from \"../css.ts\";\n\nexport const workspace = css`\n /* review-workspace */\n display: grid;\n grid-template-columns: 300px minmax(0, 1fr);\n gap: var(--space-4);\n align-items: start;\n @media (max-width: 1024px) {\n & {\n grid-template-columns: minmax(0, 1fr);\n }\n }\n`;\n\nexport const canvasColumn = css`\n /* review-canvas */\n display: grid;\n gap: var(--space-4);\n min-width: 0;\n`;\n\n/* ---- header ---- */\n\nexport const reviewHeader = css`\n /* review-header */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-4);\n align-items: flex-start;\n justify-content: space-between;\n margin-bottom: var(--space-4);\n padding: var(--space-4);\n background: linear-gradient(var(--accent-wash), var(--accent-wash)), var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow);\n`;\n\nexport const headerMain = css`\n /* review-header-main */\n display: grid;\n gap: var(--space-1);\n min-width: 0;\n`;\n\nexport const headerTitle = css`\n /* review-header-title */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n align-items: center;\n margin: 0;\n font-size: var(--text-xl);\n font-weight: 700;\n letter-spacing: -0.02em;\n line-height: var(--leading-tight);\n`;\n\nexport const headerMeta = css`\n /* review-header-meta */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n align-items: center;\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-sm);\n`;\n\nexport const headerSide = css`\n /* review-header-side */\n display: grid;\n gap: var(--space-3);\n justify-items: end;\n min-width: min(100%, 280px);\n @media (max-width: 720px) {\n & {\n justify-items: stretch;\n width: 100%;\n }\n }\n`;\n\nexport const headerProgress = css`\n /* review-header-progress */\n display: grid;\n gap: var(--space-1);\n width: 100%;\n font-size: var(--text-sm);\n color: var(--text-secondary);\n font-variant-numeric: tabular-nums;\n`;\n\nexport const headerActions = css`\n /* review-header-actions */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n align-items: center;\n justify-content: flex-end;\n`;\n\n/* ---- filmstrip ---- */\n\nexport const filmstrip = css`\n /* filmstrip */\n position: sticky;\n top: calc(var(--topbar-height) + var(--space-4));\n display: flex;\n flex-direction: column;\n max-height: calc(100vh - var(--topbar-height) - var(--space-8) - var(--space-4));\n overflow: hidden;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow);\n @media (max-width: 1024px) {\n & {\n position: static;\n max-height: none;\n }\n }\n`;\n\nexport const filmstripHead = css`\n /* filmstrip-head */\n display: grid;\n gap: var(--space-2);\n padding: var(--space-3);\n border-bottom: 1px solid var(--border);\n`;\n\nexport const filmstripList = css`\n /* filmstrip-list */\n overflow: auto;\n overscroll-behavior: contain;\n @media (max-width: 1024px) {\n & {\n display: flex;\n overflow-x: auto;\n overflow-y: hidden;\n }\n }\n`;\n\nexport const filmItem = css`\n /* film-item */\n display: grid;\n grid-template-columns: 72px minmax(0, 1fr);\n gap: var(--space-3);\n align-items: center;\n padding: var(--space-2) var(--space-3);\n border-bottom: 1px solid var(--border-subtle);\n border-left: 3px solid transparent;\n color: inherit;\n text-decoration: none;\n transition: background var(--dur-fast) var(--ease);\n &:hover {\n background: var(--surface-subtle);\n text-decoration: none;\n }\n @media (max-width: 1024px) {\n & {\n flex: none;\n width: 240px;\n border-bottom: 0;\n border-left: 0;\n border-right: 1px solid var(--border-subtle);\n border-top: 3px solid transparent;\n }\n }\n`;\n\nexport const filmItemActive = css`\n /* film-item-active */\n ${filmItem}\n background: var(--accent-subtle);\n border-left-color: var(--accent);\n &:hover {\n background: var(--accent-subtle);\n }\n @media (max-width: 1024px) {\n & {\n border-left-color: transparent;\n border-top-color: var(--accent);\n }\n }\n`;\n\nexport const filmBody = css`\n /* film-body */\n display: grid;\n gap: 2px;\n min-width: 0;\n`;\n\nexport const filmTitle = css`\n /* film-title */\n overflow: hidden;\n font-size: var(--text-base);\n font-weight: 600;\n text-overflow: ellipsis;\n white-space: nowrap;\n`;\n\nexport const filmSub = css`\n /* film-sub */\n overflow: hidden;\n color: var(--text-secondary);\n font-size: var(--text-xs);\n text-overflow: ellipsis;\n white-space: nowrap;\n`;\n\nexport const filmStatus = css`\n /* film-status */\n display: inline-flex;\n gap: var(--space-1);\n align-items: center;\n font-size: var(--text-xs);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n`;\n\nconst dot = css`\n /* status-dot */\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: var(--radius-pill);\n background: var(--text-muted);\n`;\n\nexport const statusDots = {\n neutral: dot,\n success: css`\n /* status-dot-success */\n ${dot}\n background: var(--status-approved);\n `,\n warning: css`\n /* status-dot-warning */\n ${dot}\n background: var(--status-new);\n `,\n danger: css`\n /* status-dot-danger */\n ${dot}\n background: var(--status-rejected);\n `,\n info: css`\n /* status-dot-info */\n ${dot}\n background: var(--status-info);\n `,\n};\n\n/* ---- viewer ---- */\n\nexport const viewerHead = css`\n /* viewer-head */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2) var(--space-4);\n align-items: baseline;\n justify-content: space-between;\n`;\n\nexport const viewerTitle = css`\n /* viewer-title */\n margin: 0;\n font-size: var(--text-lg);\n font-weight: 650;\n letter-spacing: -0.01em;\n`;\n\nexport const viewerMeta = css`\n /* viewer-meta */\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n align-items: center;\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-sm);\n`;\n\n/* ---- action bar ---- */\n\nexport const actionBar = css`\n /* review-action-bar */\n position: sticky;\n bottom: var(--space-4);\n z-index: 20;\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n align-items: center;\n justify-content: space-between;\n padding: var(--space-2) var(--space-3);\n background: color-mix(in srgb, var(--surface-card) 92%, transparent);\n backdrop-filter: blur(10px);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow-2);\n`;\n\nexport const actionGroup = css`\n /* review-action-group */\n display: flex;\n gap: var(--space-2);\n align-items: center;\n`;\n\nexport const actionPosition = css`\n /* review-action-position */\n min-width: 4.5rem;\n color: var(--text-secondary);\n font-size: var(--text-sm);\n font-variant-numeric: tabular-nums;\n text-align: center;\n`;\n\nexport const actionHints = css`\n /* review-action-hints */\n display: flex;\n gap: var(--space-2);\n align-items: center;\n color: var(--text-secondary);\n font-size: var(--text-xs);\n @media (max-width: 720px) {\n & {\n display: none;\n }\n }\n`;\n\n/* ---- shortcuts dialog ---- */\n\nexport const shortcutsDialog = css`\n /* shortcuts-dialog */\n width: min(440px, calc(100vw - 2rem));\n padding: var(--space-5);\n color: var(--text-primary);\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow-3);\n &::backdrop {\n background: rgb(0 0 0 / 0.45);\n }\n h2 {\n margin: 0 0 var(--space-3);\n font-size: var(--text-lg);\n }\n dl {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: var(--space-2) var(--space-4);\n margin: 0 0 var(--space-4);\n font-size: var(--text-base);\n }\n dt {\n display: flex;\n gap: var(--space-1);\n }\n dd {\n margin: 0;\n color: var(--text-secondary);\n }\n`;\n"],"mappings":";;;;;;;;;;AAOA,MAAa,YAAY,GAAG;;;;;;;;;;;;AAa5B,MAAa,eAAe,GAAG;;;;;;AAS/B,MAAa,eAAe,GAAG;;;;;;;;;;;;;;AAe/B,MAAa,aAAa,GAAG;;;;;;AAO7B,MAAa,cAAc,GAAG;;;;;;;;;;;;AAa9B,MAAa,aAAa,GAAG;;;;;;;;;;AAW7B,MAAa,aAAa,GAAG;;;;;;;;;;;;;AAc7B,MAAa,iBAAiB,GAAG;;;;;;;;;AAUjC,MAAa,gBAAgB,GAAG;;;;;;;;AAWhC,MAAa,YAAY,GAAG;;;;;;;;;;;;;;;;;;;AAoB5B,MAAa,gBAAgB,GAAG;;;;;;;AAQhC,MAAa,gBAAgB,GAAG;;;;;;;;;;;;AAahC,MAAa,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B3B,MAAa,iBAAiB,GAAG;;IAE7B,SAAS;;;;;;;;;;;;;AAcb,MAAa,WAAW,GAAG;;;;;;AAO3B,MAAa,YAAY,GAAG;;;;;;;;AAS5B,MAAa,UAAU,GAAG;;;;;;;;AAS1B,MAAa,aAAa,GAAG;;;;;;;;;AAU7B,MAAM,MAAM,GAAG;;;;;;;;AASf,MAAa,aAAa;CACxB,SAAS;CACT,SAAS,GAAG;;MAER,IAAI;;;CAGR,SAAS,GAAG;;MAER,IAAI;;;CAGR,QAAQ,GAAG;;MAEP,IAAI;;;CAGR,MAAM,GAAG;;MAEL,IAAI;;;AAGV;AAIA,MAAa,aAAa,GAAG;;;;;;;;AAS7B,MAAa,cAAc,GAAG;;;;;;;AAQ9B,MAAa,aAAa,GAAG;;;;;;;;;;AAa7B,MAAa,YAAY,GAAG;;;;;;;;;;;;;;;;;AAkB5B,MAAa,cAAc,GAAG;;;;;;AAO9B,MAAa,iBAAiB,GAAG;;;;;;;;AASjC,MAAa,cAAc,GAAG;;;;;;;;;;;;;AAgB9B,MAAa,kBAAkB,GAAG"}
@@ -0,0 +1,86 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { css } from "../css.mjs";
5
+ //#region src/ui/styles/review.ts
6
+ /**
7
+ * Snapshot-card and comment styles shared by the build detail, library, and
8
+ * review pages (the review workspace layout lives in `review-layout.ts`).
9
+ * Shared across pages, so they live here rather than colocated in one file —
10
+ * the documented exception to colocation (see the `ui-building-blocks`
11
+ * skill). Labels keep class names readable.
12
+ */
13
+ const diffPaneImg = css`
14
+ /* diff-pane-img */
15
+ display: block;
16
+ width: 100%;
17
+ height: auto;
18
+ background: repeating-conic-gradient(var(--surface-muted) 0% 25%, var(--surface-subtle) 0% 50%) 0
19
+ 0 / 16px 16px;
20
+ `;
21
+ const snapshotGrid = css`
22
+ /* snapshot-grid */
23
+ display: grid;
24
+ gap: 1rem;
25
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
26
+ `;
27
+ const snapshotCard = css`
28
+ /* snapshot-card */
29
+ border: 1px solid var(--border);
30
+ border-radius: var(--radius);
31
+ overflow: hidden;
32
+ background: var(--surface-card);
33
+ display: flex;
34
+ flex-direction: column;
35
+ `;
36
+ const snapshotCardHead = css`
37
+ /* snapshot-card-head */
38
+ padding: 0.6rem 0.75rem;
39
+ border-bottom: 1px solid var(--border);
40
+ display: flex;
41
+ justify-content: space-between;
42
+ gap: 0.5rem;
43
+ align-items: center;
44
+ `;
45
+ const snapshotCardMeta = css`
46
+ /* snapshot-card-meta */
47
+ font-size: 0.78rem;
48
+ color: var(--text-secondary);
49
+ `;
50
+ const snapshotCardBody = css`
51
+ /* snapshot-card-body */
52
+ padding: 0.5rem;
53
+ display: grid;
54
+ gap: 0.5rem;
55
+ `;
56
+ const reviewComment = css`
57
+ /* review-comment */
58
+ border: 1px solid var(--border);
59
+ border-radius: var(--radius);
60
+ padding: 0.75rem;
61
+ background: var(--surface-card);
62
+ `;
63
+ const reviewCommentHead = css`
64
+ /* review-comment-head */
65
+ display: flex;
66
+ gap: 0.5rem;
67
+ align-items: center;
68
+ font-size: 0.82rem;
69
+ color: var(--text-secondary);
70
+ flex-wrap: wrap;
71
+ `;
72
+ const reviewCommentBody = css`
73
+ /* review-comment-body */
74
+ margin: 0.5rem 0 0;
75
+ white-space: pre-wrap;
76
+ word-break: break-word;
77
+ font-size: 0.875rem;
78
+ `;
79
+ const reviewCommentActions = css`
80
+ /* review-comment-actions */
81
+ margin-top: 0.5rem;
82
+ `;
83
+ //#endregion
84
+ export { diffPaneImg, reviewComment, reviewCommentActions, reviewCommentBody, reviewCommentHead, snapshotCard, snapshotCardBody, snapshotCardHead, snapshotCardMeta, snapshotGrid };
85
+
86
+ //# sourceMappingURL=review.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.mjs","names":[],"sources":["../../../src/ui/styles/review.ts"],"sourcesContent":["/**\n * Snapshot-card and comment styles shared by the build detail, library, and\n * review pages (the review workspace layout lives in `review-layout.ts`).\n * Shared across pages, so they live here rather than colocated in one file —\n * the documented exception to colocation (see the `ui-building-blocks`\n * skill). Labels keep class names readable.\n */\nimport { css } from \"../css.ts\";\n\nexport const diffPaneImg = css`\n /* diff-pane-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%) 0\n 0 / 16px 16px;\n`;\n\nexport const snapshotGrid = css`\n /* snapshot-grid */\n display: grid;\n gap: 1rem;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n`;\n\nexport const snapshotCard = css`\n /* snapshot-card */\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n background: var(--surface-card);\n display: flex;\n flex-direction: column;\n`;\n\nexport const snapshotCardHead = css`\n /* snapshot-card-head */\n padding: 0.6rem 0.75rem;\n border-bottom: 1px solid var(--border);\n display: flex;\n justify-content: space-between;\n gap: 0.5rem;\n align-items: center;\n`;\n\nexport const snapshotCardMeta = css`\n /* snapshot-card-meta */\n font-size: 0.78rem;\n color: var(--text-secondary);\n`;\n\nexport const snapshotCardBody = css`\n /* snapshot-card-body */\n padding: 0.5rem;\n display: grid;\n gap: 0.5rem;\n`;\n\nexport const reviewComment = css`\n /* review-comment */\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.75rem;\n background: var(--surface-card);\n`;\n\nexport const reviewCommentHead = css`\n /* review-comment-head */\n display: flex;\n gap: 0.5rem;\n align-items: center;\n font-size: 0.82rem;\n color: var(--text-secondary);\n flex-wrap: wrap;\n`;\n\nexport const reviewCommentBody = css`\n /* review-comment-body */\n margin: 0.5rem 0 0;\n white-space: pre-wrap;\n word-break: break-word;\n font-size: 0.875rem;\n`;\n\nexport const reviewCommentActions = css`\n /* review-comment-actions */\n margin-top: 0.5rem;\n`;\n"],"mappings":";;;;;;;;;;;;AASA,MAAa,cAAc,GAAG;;;;;;;;AAS9B,MAAa,eAAe,GAAG;;;;;;AAO/B,MAAa,eAAe,GAAG;;;;;;;;;AAU/B,MAAa,mBAAmB,GAAG;;;;;;;;;AAUnC,MAAa,mBAAmB,GAAG;;;;;AAMnC,MAAa,mBAAmB,GAAG;;;;;;AAOnC,MAAa,gBAAgB,GAAG;;;;;;;AAQhC,MAAa,oBAAoB,GAAG;;;;;;;;;AAUpC,MAAa,oBAAoB,GAAG;;;;;;;AAQpC,MAAa,uBAAuB,GAAG"}