@taskmark/ui 0.3.0 → 0.3.1

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 (301) hide show
  1. package/README.md +1 -1
  2. package/app/globals.css +25 -0
  3. package/app/layout.tsx +15 -5
  4. package/app/setup/page.tsx +1 -1
  5. package/bin/lib/resolve-board.mjs +1 -4
  6. package/components/board/app-bar.tsx +3 -0
  7. package/components/board/board-screen.tsx +61 -117
  8. package/components/board/changelog-panel.tsx +26 -0
  9. package/components/board/epic-list.tsx +2 -0
  10. package/components/board/list-view-switcher.tsx +7 -3
  11. package/components/board/markdown-content.tsx +21 -1
  12. package/components/board/mermaid-diagram.tsx +88 -0
  13. package/components/board/metric-stat-card.tsx +4 -4
  14. package/components/board/overall-tree-list.tsx +467 -0
  15. package/components/board/overall-work-items-list.tsx +5 -1
  16. package/components/board/points-heatmap.tsx +183 -0
  17. package/components/board/project-status-metrics-strip.tsx +5 -22
  18. package/components/board/static-board-app.tsx +29 -94
  19. package/components/board/status-badge.tsx +8 -0
  20. package/components/board/story-list.tsx +2 -0
  21. package/components/board/task-list.tsx +5 -1
  22. package/components/board/work-item-detail-body.tsx +123 -8
  23. package/components/board/work-items-list.tsx +2 -0
  24. package/components/theme-provider.tsx +39 -0
  25. package/components/theme-toggle.tsx +27 -0
  26. package/components/ui/sonner.tsx +2 -2
  27. package/dist/prod-next/BUILD_ID +1 -1
  28. package/dist/prod-next/build-manifest.json +6 -6
  29. package/dist/prod-next/react-loadable-manifest.json +391 -1
  30. package/dist/prod-next/server/app/_global-error/page.js +2 -2
  31. package/dist/prod-next/server/app/_global-error/page.js.nft.json +1 -1
  32. package/dist/prod-next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  33. package/dist/prod-next/server/app/_global-error.html +1 -1
  34. package/dist/prod-next/server/app/_global-error.rsc +1 -1
  35. package/dist/prod-next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  36. package/dist/prod-next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  37. package/dist/prod-next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  38. package/dist/prod-next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  39. package/dist/prod-next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  40. package/dist/prod-next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  41. package/dist/prod-next/server/app/_not-found/page.js +2 -2
  42. package/dist/prod-next/server/app/_not-found/page.js.nft.json +1 -1
  43. package/dist/prod-next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  44. package/dist/prod-next/server/app/_not-found.html +1 -1
  45. package/dist/prod-next/server/app/_not-found.rsc +19 -17
  46. package/dist/prod-next/server/app/_not-found.segments/_full.segment.rsc +19 -17
  47. package/dist/prod-next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  48. package/dist/prod-next/server/app/_not-found.segments/_index.segment.rsc +8 -6
  49. package/dist/prod-next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  50. package/dist/prod-next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  51. package/dist/prod-next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  52. package/dist/prod-next/server/app/api/taskmark-reload/route.js +1 -1
  53. package/dist/prod-next/server/app/api/taskmark-reload/route.js.nft.json +1 -1
  54. package/dist/prod-next/server/app/board/page.js +2 -2
  55. package/dist/prod-next/server/app/board/page.js.nft.json +1 -1
  56. package/dist/prod-next/server/app/board/page_client-reference-manifest.js +1 -1
  57. package/dist/prod-next/server/app/favicon.ico/route.js +1 -1
  58. package/dist/prod-next/server/app/favicon.ico/route.js.nft.json +1 -1
  59. package/dist/prod-next/server/app/page.js +2 -2
  60. package/dist/prod-next/server/app/page.js.nft.json +1 -1
  61. package/dist/prod-next/server/app/page_client-reference-manifest.js +1 -1
  62. package/dist/prod-next/server/app/setup/page.js +2 -2
  63. package/dist/prod-next/server/app/setup/page.js.nft.json +1 -1
  64. package/dist/prod-next/server/app/setup/page_client-reference-manifest.js +1 -1
  65. package/dist/prod-next/server/chunks/102.js +90 -0
  66. package/dist/prod-next/server/chunks/11.js +1 -0
  67. package/dist/prod-next/server/chunks/1286.js +1 -0
  68. package/dist/prod-next/server/chunks/1412.js +38 -0
  69. package/dist/prod-next/server/chunks/1605.js +1 -0
  70. package/dist/prod-next/server/chunks/169.js +1 -0
  71. package/dist/prod-next/server/chunks/18.js +86 -0
  72. package/dist/prod-next/server/chunks/2.js +1 -0
  73. package/dist/prod-next/server/chunks/2022.js +1 -0
  74. package/dist/prod-next/server/chunks/2156.js +1 -0
  75. package/dist/prod-next/server/chunks/2157.js +1 -0
  76. package/dist/prod-next/server/chunks/2325.js +82 -0
  77. package/dist/prod-next/server/chunks/2838.js +1 -0
  78. package/dist/prod-next/server/chunks/2897.js +79 -0
  79. package/dist/prod-next/server/chunks/3227.js +1 -0
  80. package/dist/prod-next/server/chunks/3266.js +1 -0
  81. package/dist/prod-next/server/chunks/{192.js → 3307.js} +5 -5
  82. package/dist/prod-next/server/chunks/3387.js +19 -0
  83. package/dist/prod-next/server/chunks/3428.js +32 -0
  84. package/dist/prod-next/server/chunks/3479.js +1 -0
  85. package/dist/prod-next/server/chunks/367.js +115 -0
  86. package/dist/prod-next/server/chunks/3698.js +93 -0
  87. package/dist/prod-next/server/chunks/3709.js +148 -0
  88. package/dist/prod-next/server/chunks/41.js +1 -0
  89. package/dist/prod-next/server/chunks/4107.js +62 -0
  90. package/dist/prod-next/server/chunks/4569.js +34 -0
  91. package/dist/prod-next/server/chunks/{741.js → 4741.js} +1 -1
  92. package/dist/prod-next/server/chunks/4755.js +153 -0
  93. package/dist/prod-next/server/chunks/5002.js +1 -0
  94. package/dist/prod-next/server/chunks/5112.js +1 -0
  95. package/dist/prod-next/server/chunks/5289.js +8 -0
  96. package/dist/prod-next/server/chunks/5318.js +1 -0
  97. package/dist/prod-next/server/chunks/5452.js +23 -0
  98. package/dist/prod-next/server/chunks/5453.js +1 -0
  99. package/dist/prod-next/server/chunks/5749.js +27 -0
  100. package/dist/prod-next/server/chunks/5782.js +36 -0
  101. package/dist/prod-next/server/chunks/6163.js +104 -0
  102. package/dist/prod-next/server/chunks/6169.js +64 -0
  103. package/dist/prod-next/server/chunks/6208.js +172 -0
  104. package/dist/prod-next/server/chunks/6399.js +24 -0
  105. package/dist/prod-next/server/chunks/6496.js +1 -0
  106. package/dist/prod-next/server/chunks/6823.js +215 -0
  107. package/dist/prod-next/server/chunks/6888.js +1 -0
  108. package/dist/prod-next/server/chunks/6980.js +1 -0
  109. package/dist/prod-next/server/chunks/7245.js +77 -0
  110. package/dist/prod-next/server/chunks/7307.js +1 -0
  111. package/dist/prod-next/server/chunks/7372.js +287 -0
  112. package/dist/prod-next/server/chunks/7384.js +226 -0
  113. package/dist/prod-next/server/chunks/7516.js +1 -0
  114. package/dist/prod-next/server/chunks/7531.js +24 -0
  115. package/dist/prod-next/server/chunks/7591.js +1 -0
  116. package/dist/prod-next/server/chunks/7755.js +138 -0
  117. package/dist/prod-next/server/chunks/7916.js +1 -0
  118. package/dist/prod-next/server/chunks/8078.js +94 -0
  119. package/dist/prod-next/server/chunks/8253.js +41 -0
  120. package/dist/prod-next/server/chunks/8635.js +1 -0
  121. package/dist/prod-next/server/chunks/8688.js +1 -0
  122. package/dist/prod-next/server/chunks/8872.js +1 -0
  123. package/dist/prod-next/server/chunks/9006.js +1 -0
  124. package/dist/prod-next/server/chunks/9018.js +1 -0
  125. package/dist/prod-next/server/chunks/9047.js +1 -0
  126. package/dist/prod-next/server/chunks/9108.js +1 -0
  127. package/dist/prod-next/server/chunks/9280.js +256 -0
  128. package/dist/prod-next/server/chunks/9419.js +1 -0
  129. package/dist/prod-next/server/chunks/9430.js +1 -0
  130. package/dist/prod-next/server/chunks/9586.js +1 -0
  131. package/dist/prod-next/server/chunks/9775.js +1 -0
  132. package/dist/prod-next/server/chunks/992.js +1 -0
  133. package/dist/prod-next/server/chunks/9926.js +1 -0
  134. package/dist/prod-next/server/chunks/9984.js +8 -0
  135. package/dist/prod-next/server/middleware-build-manifest.js +1 -1
  136. package/dist/prod-next/server/middleware-react-loadable-manifest.js +1 -1
  137. package/dist/prod-next/server/pages/404.html +1 -1
  138. package/dist/prod-next/server/pages/500.html +1 -1
  139. package/dist/prod-next/server/server-reference-manifest.js +1 -1
  140. package/dist/prod-next/server/server-reference-manifest.json +1 -1
  141. package/dist/prod-next/server/webpack-runtime.js +1 -1
  142. package/dist/prod-next/static/chunks/1213.e5c6e70a79b5f7e6.js +24 -0
  143. package/dist/prod-next/static/chunks/1519.2f596e5f5af9ef57.js +62 -0
  144. package/dist/prod-next/static/chunks/1759-f5b8fb456f8fb420.js +1 -0
  145. package/dist/prod-next/static/chunks/1974.885c9a133b86d8a4.js +32 -0
  146. package/dist/prod-next/static/chunks/2280.6a7f540787704698.js +1 -0
  147. package/dist/prod-next/static/chunks/2521.7f745f50d30076e7.js +77 -0
  148. package/dist/prod-next/static/chunks/2585.4d6aa847c780c666.js +1 -0
  149. package/dist/prod-next/static/chunks/2589.1aad68c09021b1ac.js +1 -0
  150. package/dist/prod-next/static/chunks/2600.45ba17a0f756fc31.js +1 -0
  151. package/dist/prod-next/static/chunks/2619.9aac8983f30c7c8a.js +1 -0
  152. package/dist/prod-next/static/chunks/26d302b2.9f6383e42abdfe16.js +122 -0
  153. package/dist/prod-next/static/chunks/2768.305914f7900745b6.js +1 -0
  154. package/dist/prod-next/static/chunks/2931.c2acdbf72808b646.js +41 -0
  155. package/dist/prod-next/static/chunks/3012-838f69b72a0dc1b3.js +1 -0
  156. package/dist/prod-next/static/chunks/3016.406e568aa95ff867.js +1 -0
  157. package/dist/prod-next/static/chunks/3022.972047558a20553a.js +172 -0
  158. package/dist/prod-next/static/chunks/3064.3a2a1e7cd4fdca5f.js +1 -0
  159. package/dist/prod-next/static/chunks/3174.03fbed7b7d7a8256.js +1 -0
  160. package/dist/prod-next/static/chunks/3189.b048866768087750.js +1 -0
  161. package/dist/prod-next/static/chunks/3217.1a053843c58b70b5.js +24 -0
  162. package/dist/prod-next/static/chunks/3308.30b95d524f9c1daf.js +74 -0
  163. package/dist/prod-next/static/chunks/331.fc5d78e1e91daf43.js +1 -0
  164. package/dist/prod-next/static/chunks/3510.5d4d1c79533e080c.js +1 -0
  165. package/dist/prod-next/static/chunks/364.ce5400a9dd70b9dc.js +1 -0
  166. package/dist/prod-next/static/chunks/3661-1f08d73aaffb011d.js +1 -0
  167. package/dist/prod-next/static/chunks/370.57cfa27da8567918.js +1 -0
  168. package/dist/prod-next/static/chunks/3789.c418b69121761983.js +1 -0
  169. package/dist/prod-next/static/chunks/3794-c66b3d8325973459.js +32 -0
  170. package/dist/prod-next/static/chunks/3831.25ae62d10dccc27a.js +1 -0
  171. package/dist/prod-next/static/chunks/395.b41e9d76613d37b5.js +90 -0
  172. package/dist/prod-next/static/chunks/3a7aa434.93dbd20997cfd24f.js +82 -0
  173. package/dist/prod-next/static/chunks/4020.3cc19c463c49f969.js +23 -0
  174. package/dist/prod-next/static/chunks/4035.0acc428926d5c8dd.js +1 -0
  175. package/dist/prod-next/static/chunks/405.0902e4f6eb7bd9a2.js +215 -0
  176. package/dist/prod-next/static/chunks/4159.49ae537ff687bdc6.js +138 -0
  177. package/dist/prod-next/static/chunks/4208.384b381ca2757643.js +287 -0
  178. package/dist/prod-next/static/chunks/4263.ffa8d9bd8a0faaae.js +1 -0
  179. package/dist/prod-next/static/chunks/4561.d1a3aaca8bae3224.js +34 -0
  180. package/dist/prod-next/static/chunks/4592.d0f37bc216b9d988.js +93 -0
  181. package/dist/prod-next/static/chunks/4799.0dc7aa21bff2fd2a.js +1 -0
  182. package/dist/prod-next/static/chunks/4bd1b696-e356ca5ba0218e27.js +1 -0
  183. package/dist/prod-next/static/chunks/514.b804c51b26f5e126.js +1 -0
  184. package/dist/prod-next/static/chunks/5270.28592864d38e881c.js +1 -0
  185. package/dist/prod-next/static/chunks/52733464.3870f99cc446f9ff.js +1 -0
  186. package/dist/prod-next/static/chunks/5354.b559c717a607dc5c.js +1 -0
  187. package/dist/prod-next/static/chunks/5448.d3972d9fb464b54f.js +38 -0
  188. package/dist/prod-next/static/chunks/587.d52737d7288c11b4.js +1 -0
  189. package/dist/prod-next/static/chunks/5909.9e37f7b495b11995.js +115 -0
  190. package/dist/prod-next/static/chunks/5949.5504b3545fcc3d34.js +104 -0
  191. package/dist/prod-next/static/chunks/6322-4e97920c006f7933.js +1 -0
  192. package/dist/prod-next/static/chunks/6361.893d04ac34fa1b49.js +148 -0
  193. package/dist/prod-next/static/chunks/6774.8cd53427dbf32cac.js +86 -0
  194. package/dist/prod-next/static/chunks/6968.b80277c724dbc96e.js +1 -0
  195. package/dist/prod-next/static/chunks/7113.4b9e7ea870aa300a.js +8 -0
  196. package/dist/prod-next/static/chunks/7236.55a9728d35f5b8a6.js +1 -0
  197. package/dist/prod-next/static/chunks/7390.dde9b5bc2f6e24b8.js +1 -0
  198. package/dist/prod-next/static/chunks/7806.a903780fcfa3a30e.js +1 -0
  199. package/dist/prod-next/static/chunks/7997.be57e92e583fe635.js +1 -0
  200. package/dist/prod-next/static/chunks/8216.64a72d4d35ea7be0.js +1 -0
  201. package/dist/prod-next/static/chunks/830.5d5274d7ad1cb58e.js +1 -0
  202. package/dist/prod-next/static/chunks/8378.fa91de3406f554c9.js +226 -0
  203. package/dist/prod-next/static/chunks/8501.cd06aa1b0c7e1c6a.js +79 -0
  204. package/dist/prod-next/static/chunks/8663.6ec5792fb4952e45.js +27 -0
  205. package/dist/prod-next/static/chunks/872.96937e758ddce860.js +1 -0
  206. package/dist/prod-next/static/chunks/8803.75bde095fe3613f5.js +1 -0
  207. package/dist/prod-next/static/chunks/8863.72f5dbb820a0e4c3.js +1 -0
  208. package/dist/prod-next/static/chunks/90542734.f7a7b2972ef5a749.js +1 -0
  209. package/dist/prod-next/static/chunks/9379.5bc9c9343ec5a16c.js +64 -0
  210. package/dist/prod-next/static/chunks/9389.906fe7aa9cd6a170.js +1 -0
  211. package/dist/prod-next/static/chunks/9472.8818ee52cfa931a4.js +94 -0
  212. package/dist/prod-next/static/chunks/966.d6babbf8119162cc.js +1 -0
  213. package/dist/prod-next/static/chunks/9884.d4ac1a961b3366f1.js +36 -0
  214. package/dist/prod-next/static/chunks/9909.67cf9940b125243b.js +1 -0
  215. package/dist/prod-next/static/chunks/app/_global-error/page-8be5492b3b872a4c.js +1 -0
  216. package/dist/prod-next/static/chunks/app/_not-found/page-799e445a538b0024.js +1 -0
  217. package/dist/prod-next/static/chunks/app/api/taskmark-reload/route-8be5492b3b872a4c.js +1 -0
  218. package/dist/prod-next/static/chunks/app/board/page-5c181f45738daf1c.js +1 -0
  219. package/dist/prod-next/static/chunks/app/layout-21782e6d13a6f275.js +1 -0
  220. package/dist/prod-next/static/chunks/app/page-51bfe4356fe233e3.js +1 -0
  221. package/dist/prod-next/static/chunks/app/setup/page-3ccfba00c10f9b17.js +1 -0
  222. package/dist/prod-next/static/chunks/d3ac728e.7964f816a1ca64e5.js +1 -0
  223. package/dist/prod-next/static/chunks/e413bedd.3a12a8cb02e28fdf.js +153 -0
  224. package/dist/prod-next/static/chunks/ea233666.f00b64a7d99b4950.js +62 -0
  225. package/dist/prod-next/static/chunks/framework-711ef29bc66f648c.js +1 -0
  226. package/dist/prod-next/static/chunks/main-app-dd26d4f9d120646e.js +1 -0
  227. package/dist/prod-next/static/chunks/{main-bb33c46a9c25dee8.js → main-df4bcaed74f274eb.js} +2 -2
  228. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/app-error-8be5492b3b872a4c.js +1 -0
  229. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/forbidden-8be5492b3b872a4c.js +1 -0
  230. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/global-error-45a0cf77e03361ed.js +1 -0
  231. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/not-found-8be5492b3b872a4c.js +1 -0
  232. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/unauthorized-8be5492b3b872a4c.js +1 -0
  233. package/dist/prod-next/static/chunks/webpack-713747aaa9716c4c.js +1 -0
  234. package/dist/prod-next/static/css/cb78d4b0062d9a3f.css +1 -0
  235. package/dist/public/taskmark-snapshot.json +1 -1
  236. package/lib/taskmark/board-index.ts +101 -0
  237. package/lib/taskmark/build-snapshot.ts +28 -7
  238. package/lib/taskmark/derive-parents.test.ts +23 -0
  239. package/lib/taskmark/derive-parents.ts +323 -0
  240. package/lib/taskmark/detail-load.ts +55 -7
  241. package/lib/taskmark/detail-types.ts +10 -2
  242. package/lib/taskmark/dev-reload-token.ts +1 -1
  243. package/lib/taskmark/discover.ts +4 -7
  244. package/lib/taskmark/epic-types.ts +2 -1
  245. package/lib/taskmark/flat-work-item-types.ts +1 -4
  246. package/lib/taskmark/identity.ts +3 -3
  247. package/lib/taskmark/item-types.ts +2 -1
  248. package/lib/taskmark/list-filters.test.ts +23 -0
  249. package/lib/taskmark/list-filters.ts +7 -2
  250. package/lib/taskmark/list-view-mode.test.ts +45 -0
  251. package/lib/taskmark/list-view-mode.ts +19 -8
  252. package/lib/taskmark/load-changelog.test.ts +32 -0
  253. package/lib/taskmark/load-changelog.ts +18 -0
  254. package/lib/taskmark/overall-tree.test.ts +196 -0
  255. package/lib/taskmark/overall-tree.ts +194 -0
  256. package/lib/taskmark/parse-detail.ts +5 -5
  257. package/lib/taskmark/parse-epics.ts +19 -16
  258. package/lib/taskmark/parse-flat-lists.ts +32 -28
  259. package/lib/taskmark/parse-items.ts +47 -9
  260. package/lib/taskmark/parse-stories.ts +18 -15
  261. package/lib/taskmark/points-calendar.test.ts +72 -0
  262. package/lib/taskmark/points-calendar.ts +86 -0
  263. package/lib/taskmark/project-metrics.ts +11 -7
  264. package/lib/taskmark/snapshot-types.ts +2 -0
  265. package/lib/taskmark/story-types.ts +2 -1
  266. package/lib/taskmark/table-sort.ts +4 -3
  267. package/lib/taskmark/timing.ts +29 -18
  268. package/lib/taskmark/validate.ts +1 -1
  269. package/lib/theme-cookie.test.ts +27 -0
  270. package/lib/theme-cookie.ts +43 -0
  271. package/package.json +3 -1
  272. package/public/taskmark-snapshot.json +1 -1
  273. package/dist/prod-next/server/chunks/463.js +0 -1
  274. package/dist/prod-next/server/chunks/54.js +0 -19
  275. package/dist/prod-next/server/chunks/57.js +0 -8
  276. package/dist/prod-next/server/chunks/724.js +0 -1
  277. package/dist/prod-next/static/chunks/4bd1b696-deb4a0a1da1923b0.js +0 -1
  278. package/dist/prod-next/static/chunks/613-6f794154d28bb75a.js +0 -1
  279. package/dist/prod-next/static/chunks/636-626c380f021b95c8.js +0 -1
  280. package/dist/prod-next/static/chunks/669-8103441c67b185da.js +0 -1
  281. package/dist/prod-next/static/chunks/701-df3ee5f1cf837b1a.js +0 -1
  282. package/dist/prod-next/static/chunks/794-25668765560cb197.js +0 -32
  283. package/dist/prod-next/static/chunks/909.932f2af3be7f1e89.js +0 -1
  284. package/dist/prod-next/static/chunks/app/_global-error/page-a1e3320be3da9be1.js +0 -1
  285. package/dist/prod-next/static/chunks/app/_not-found/page-0b3785af737e7968.js +0 -1
  286. package/dist/prod-next/static/chunks/app/api/taskmark-reload/route-a1e3320be3da9be1.js +0 -1
  287. package/dist/prod-next/static/chunks/app/board/page-636f60191db62f52.js +0 -1
  288. package/dist/prod-next/static/chunks/app/layout-856e8a282e69e7d0.js +0 -1
  289. package/dist/prod-next/static/chunks/app/page-562a33b50da7665b.js +0 -1
  290. package/dist/prod-next/static/chunks/app/setup/page-4cf527b758b1d51e.js +0 -1
  291. package/dist/prod-next/static/chunks/framework-b29f9083d3cda3b2.js +0 -1
  292. package/dist/prod-next/static/chunks/main-app-bfb36455d24b3e5a.js +0 -1
  293. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/app-error-a1e3320be3da9be1.js +0 -1
  294. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/forbidden-a1e3320be3da9be1.js +0 -1
  295. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/global-error-dc53ab29e0491fe4.js +0 -1
  296. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/not-found-a1e3320be3da9be1.js +0 -1
  297. package/dist/prod-next/static/chunks/next/dist/client/components/builtin/unauthorized-a1e3320be3da9be1.js +0 -1
  298. package/dist/prod-next/static/chunks/webpack-d33b8a6298c94a64.js +0 -1
  299. package/dist/prod-next/static/css/2e6ec4c80894e3c0.css +0 -1
  300. /package/dist/prod-next/static/{yyRnYAFQrJjF5_b5ZIeqh → EI1zQaa_kQ7DqUSE4o3Qo}/_buildManifest.js +0 -0
  301. /package/dist/prod-next/static/{yyRnYAFQrJjF5_b5ZIeqh → EI1zQaa_kQ7DqUSE4o3Qo}/_ssgManifest.js +0 -0
@@ -2,16 +2,12 @@ import {
2
2
  CalendarDays,
3
3
  CheckSquare,
4
4
  Layers,
5
- TrendingUp,
6
5
  Users,
7
6
  } from "lucide-react"
8
7
 
9
8
  import { MetricStatCard } from "@/components/board/metric-stat-card"
10
9
  import { ProjectContributorsPanel } from "@/components/board/project-contributors-panel"
11
- import {
12
- formatSpeedPtsPerWeek,
13
- type ProjectStatusMetrics,
14
- } from "@/lib/taskmark/project-metrics-shared"
10
+ import type { ProjectStatusMetrics } from "@/lib/taskmark/project-metrics-shared"
15
11
 
16
12
  type ProjectStatusMetricsStripProps = {
17
13
  metrics: ProjectStatusMetrics
@@ -20,14 +16,12 @@ type ProjectStatusMetricsStripProps = {
20
16
  export function ProjectStatusMetricsStrip({
21
17
  metrics,
22
18
  }: ProjectStatusMetricsStripProps) {
23
- const speedLabel = formatSpeedPtsPerWeek(metrics.currentSpeedPtsPerWeek)
24
-
25
19
  return (
26
20
  <section
27
21
  className="flex flex-col gap-4"
28
22
  aria-label="Project status metrics"
29
23
  >
30
- <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-4">
24
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
31
25
  <MetricStatCard
32
26
  title="Total work items"
33
27
  value={metrics.totalWorkItems}
@@ -53,29 +47,18 @@ export function ProjectStatusMetricsStrip({
53
47
  accentClassName="bg-emerald-400"
54
48
  icon={<CalendarDays className="size-4" strokeWidth={2.5} />}
55
49
  />
56
- <MetricStatCard
57
- title="Current speed"
58
- value={speedLabel}
59
- subtitle={
60
- metrics.currentSpeedPtsPerWeek == null
61
- ? "No completed tasks yet"
62
- : `pts/week · ${metrics.speedWeekCount} active weeks (90d)`
63
- }
64
- accentClassName="bg-sky-400"
65
- icon={<TrendingUp className="size-4" strokeWidth={2.5} />}
66
- />
67
50
  </div>
68
51
 
69
- <div className="border-2 border-black bg-white p-4">
52
+ <div className="border-2 border-black bg-card p-4">
70
53
  <div className="mb-3 flex items-center gap-2">
71
54
  <div
72
- className="flex size-8 items-center justify-center border-2 border-black bg-amber-300 text-black shadow-[3px_3px_0_0_#000]"
55
+ className="flex size-8 items-center justify-center border-2 border-black bg-amber-300 text-black shadow-[3px_3px_0_0_var(--shadow-color)]"
73
56
  aria-hidden
74
57
  >
75
58
  <Users className="size-4" strokeWidth={2.5} />
76
59
  </div>
77
60
  <div>
78
- <h2 className="text-sm font-medium text-neutral-800">
61
+ <h2 className="text-sm font-medium text-card-foreground">
79
62
  Contributors
80
63
  </h2>
81
64
  <p className="text-xs text-muted-foreground">
@@ -1,31 +1,26 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import Link from "next/link"
5
4
  import { useSearchParams } from "next/navigation"
6
5
 
7
6
  import { AppBar } from "@/components/board/app-bar"
8
- import { EpicList } from "@/components/board/epic-list"
7
+ import { ChangelogPanel } from "@/components/board/changelog-panel"
9
8
  import { ListViewSwitcher } from "@/components/board/list-view-switcher"
10
- import { OverallWorkItemsList } from "@/components/board/overall-work-items-list"
9
+ import { OverallTreeList } from "@/components/board/overall-tree-list"
10
+ import { PointsHeatmap } from "@/components/board/points-heatmap"
11
11
  import { ProjectStatusMetricsStrip } from "@/components/board/project-status-metrics-strip"
12
- import { TaskList } from "@/components/board/task-list"
13
12
  import { WorkItemsList } from "@/components/board/work-items-list"
14
13
  import { WorkItemSheetProvider } from "@/components/board/work-item-sheet"
15
14
  import {
16
15
  LIST_VIEW_LABELS,
17
- boardHref,
18
16
  parseListViewMode,
19
17
  type ListViewMode,
20
18
  } from "@/lib/taskmark/list-view-mode"
21
19
  import { seedBoardSnapshot } from "@/lib/taskmark/snapshot-client"
22
- import {
23
- storyKey,
24
- type BoardSnapshot,
25
- } from "@/lib/taskmark/snapshot-types"
20
+ import type { BoardSnapshot } from "@/lib/taskmark/snapshot-types"
26
21
 
27
22
  function headingForView(view: ListViewMode): string {
28
- if (view === "overall") return "Epics"
23
+ if (view === "overall") return "Overall"
29
24
  return LIST_VIEW_LABELS[view]
30
25
  }
31
26
 
@@ -36,15 +31,19 @@ export function StaticBoardApp({ snapshot }: { snapshot: BoardSnapshot }) {
36
31
  }, [snapshot])
37
32
 
38
33
  const searchParams = useSearchParams()
39
- const activeView = parseListViewMode(searchParams.get("view") ?? undefined)
34
+ const changelogMarkdown = snapshot.changelogMarkdown?.trim()
35
+ ? snapshot.changelogMarkdown
36
+ : null
37
+ const hasChangelog = changelogMarkdown != null
38
+ const activeView = parseListViewMode(searchParams.get("view") ?? undefined, {
39
+ hasChangelog,
40
+ })
40
41
  const selectedEpicId =
41
42
  activeView === "overall" ? searchParams.get("epic")?.trim() || null : null
42
43
  const selectedStoryId =
43
44
  activeView === "overall" && selectedEpicId
44
45
  ? searchParams.get("story")?.trim() || null
45
46
  : null
46
- const selectedItemId = searchParams.get("item")?.trim() || null
47
-
48
47
  const activeProject = snapshot.project
49
48
  const list = snapshot.epics
50
49
  const epicCount = list.epics.length
@@ -58,27 +57,9 @@ export function StaticBoardApp({ snapshot }: { snapshot: BoardSnapshot }) {
58
57
  ? list.epics.find((e) => e.id === selectedEpicId) ?? null
59
58
  : null
60
59
 
61
- const epicWorkItems =
62
- selectedEpicId != null
63
- ? snapshot.workItemsByEpic[selectedEpicId] ?? null
64
- : null
65
-
66
- const selectedStory =
67
- epicWorkItems && selectedStoryId
68
- ? (epicWorkItems.rows.find(
69
- (row) => row.kind === "story" && row.id === selectedStoryId
70
- ) ?? null)
71
- : null
72
-
73
- const itemList =
74
- selectedEpicId && selectedStory
75
- ? snapshot.itemsByStory[storyKey(selectedEpicId, selectedStory.id)] ??
76
- null
77
- : null
78
-
79
60
  return (
80
61
  <WorkItemSheetProvider>
81
- <div className="min-h-svh bg-[linear-gradient(180deg,_#fff_0%,_#f7f4ff_100%)]">
62
+ <div className="tm-surface min-h-svh">
82
63
  <AppBar
83
64
  projects={snapshot.projects}
84
65
  activeProjectId={activeProject.id}
@@ -103,23 +84,20 @@ export function StaticBoardApp({ snapshot }: { snapshot: BoardSnapshot }) {
103
84
  ? ` · ${workItemsList.rows.length} item${workItemsList.rows.length === 1 ? "" : "s"}`
104
85
  : ""}
105
86
  {activeView === "overall" && selectedEpic
106
- ? ` · selected ${selectedEpic.id}`
87
+ ? ` · expanded ${selectedEpic.id}`
107
88
  : activeView === "overall" && selectedEpicId
108
89
  ? ` · epic ${selectedEpicId} not in list`
109
- : activeView === "overall"
110
- ? " · select an epic to view work items"
111
- : ""}
112
- {activeView === "overall" && selectedStory
113
- ? ` · story ${selectedStory.id}`
114
- : activeView === "overall" && selectedStoryId
115
- ? ` · story ${selectedStoryId} not in list`
116
90
  : ""}
91
+ {activeView === "overall" && selectedStoryId
92
+ ? ` · story ${selectedStoryId}`
93
+ : ""}
117
94
  </p>
118
95
  </div>
119
96
  <ListViewSwitcher
120
97
  activeView={activeView}
121
98
  selectedEpicId={selectedEpicId}
122
- selectedStoryId={selectedStory?.id ?? null}
99
+ selectedStoryId={selectedStoryId}
100
+ hasChangelog={hasChangelog}
123
101
  />
124
102
  </div>
125
103
 
@@ -127,63 +105,16 @@ export function StaticBoardApp({ snapshot }: { snapshot: BoardSnapshot }) {
127
105
 
128
106
  {activeView === "overall" ? (
129
107
  <>
130
- <EpicList
131
- lists={[list]}
108
+ <PointsHeatmap samples={countableCompletions} />
109
+ <OverallTreeList
110
+ list={list}
111
+ workItemsByEpic={snapshot.workItemsByEpic}
112
+ itemsByStory={snapshot.itemsByStory}
132
113
  selectedEpicId={selectedEpicId}
114
+ selectedStoryId={selectedStoryId}
133
115
  countableCompletions={countableCompletions}
134
116
  initialHideCompleted={hideCompletedPrefs.epics}
135
117
  />
136
-
137
- {epicWorkItems ? (
138
- <div className="flex flex-col gap-3">
139
- <div className="flex items-baseline justify-between gap-3">
140
- <h2 className="font-head text-2xl tracking-tight">
141
- Work items for{" "}
142
- {epicWorkItems.epicTitle ?? epicWorkItems.epicId}
143
- </h2>
144
- <Link
145
- href={boardHref({
146
- view: "overall",
147
- item: selectedItemId,
148
- })}
149
- className="text-sm text-muted-foreground underline-offset-2 hover:underline"
150
- >
151
- Clear selection
152
- </Link>
153
- </div>
154
- <OverallWorkItemsList
155
- list={epicWorkItems}
156
- selectedStoryId={selectedStory?.id ?? null}
157
- countableCompletions={countableCompletions}
158
- initialHideCompleted={hideCompletedPrefs.overallWorkItems}
159
- />
160
- </div>
161
- ) : null}
162
-
163
- {itemList && selectedStory ? (
164
- <div className="flex flex-col gap-3">
165
- <div className="flex items-baseline justify-between gap-3">
166
- <h2 className="font-head text-2xl tracking-tight">
167
- Sub Tasks for {itemList.storyTitle ?? itemList.storyId}
168
- </h2>
169
- <Link
170
- href={boardHref({
171
- view: "overall",
172
- epic: itemList.epicId,
173
- item: selectedItemId,
174
- })}
175
- className="text-sm text-muted-foreground underline-offset-2 hover:underline"
176
- >
177
- Clear story
178
- </Link>
179
- </div>
180
- <TaskList
181
- list={itemList}
182
- countableCompletions={countableCompletions}
183
- initialHideCompleted={hideCompletedPrefs.tasks}
184
- />
185
- </div>
186
- ) : null}
187
118
  </>
188
119
  ) : null}
189
120
 
@@ -194,6 +125,10 @@ export function StaticBoardApp({ snapshot }: { snapshot: BoardSnapshot }) {
194
125
  initialHideCompleted={hideCompletedPrefs.workItems}
195
126
  />
196
127
  ) : null}
128
+
129
+ {activeView === "changelog" && changelogMarkdown ? (
130
+ <ChangelogPanel markdown={changelogMarkdown} />
131
+ ) : null}
197
132
  </div>
198
133
  </div>
199
134
  </WorkItemSheetProvider>
@@ -9,6 +9,8 @@ export function statusBadgeClass(status: string): string {
9
9
  switch (status) {
10
10
  case "done":
11
11
  return "border-black bg-[var(--chart-4)] text-black"
12
+ case "shelved":
13
+ return "border-black border-dashed bg-[var(--chart-2)]/45 text-foreground"
12
14
  case "in_progress":
13
15
  return "border-black bg-[var(--chart-1)] text-black"
14
16
  case "blocked":
@@ -22,6 +24,12 @@ export function statusBadgeClass(status: string): string {
22
24
  }
23
25
  }
24
26
 
27
+ export function statusRowClass(status: string): string | undefined {
28
+ return status.trim().toLowerCase() === "shelved"
29
+ ? "bg-[var(--chart-2)]/10 text-muted-foreground"
30
+ : undefined
31
+ }
32
+
25
33
  export function typeBadgeClass(type: string): string {
26
34
  if (type === "bug") {
27
35
  return "border-black bg-destructive/15 text-destructive"
@@ -27,6 +27,7 @@ import { ChildProgressBar } from "@/components/board/child-progress-bar"
27
27
  import { ListFiltersBar } from "@/components/board/list-filters-bar"
28
28
  import { ListPagination } from "@/components/board/list-pagination"
29
29
  import { SortableTableHead } from "@/components/board/sortable-table-head"
30
+ import { statusRowClass } from "@/components/board/status-badge"
30
31
  import { TimeframeFilter } from "@/components/board/timeframe-filter"
31
32
  import { ViewWorkItemButton } from "@/components/board/work-item-sheet"
32
33
  import { AttributionAvatarGroup } from "@/components/board/attribution-avatars"
@@ -250,6 +251,7 @@ export function StoryList({
250
251
  key={`${project.id}:${story.id}:${story.filePath}`}
251
252
  data-state={selected ? "selected" : undefined}
252
253
  className={cn(
254
+ statusRowClass(story.status),
253
255
  selected && "bg-muted/60",
254
256
  "hover:bg-muted/40"
255
257
  )}
@@ -18,7 +18,10 @@ import {
18
18
  CardHeader,
19
19
  CardTitle,
20
20
  } from "@/components/ui/card"
21
- import { TypeBadge } from "@/components/board/status-badge"
21
+ import {
22
+ statusRowClass,
23
+ TypeBadge,
24
+ } from "@/components/board/status-badge"
22
25
  import {
23
26
  IdCreatedTooltip,
24
27
  StatusWithSolvedTooltip,
@@ -244,6 +247,7 @@ export function TaskList({
244
247
  {pageRows.map((item) => (
245
248
  <TableRow
246
249
  key={`${project.id}:${item.id}:${item.filePath}`}
250
+ className={statusRowClass(item.status)}
247
251
  >
248
252
  <TableCell className="w-10 pr-0">
249
253
  <ViewWorkItemButton
@@ -2,7 +2,7 @@
2
2
 
3
3
  import type { ReactNode } from "react"
4
4
 
5
- import { formatActualDuration, formatDurationMinutes } from "@/lib/format-duration"
5
+ import { formatActualDuration } from "@/lib/format-duration"
6
6
  import { formatTaskmarkDate } from "@/lib/format-date"
7
7
  import type {
8
8
  ChecklistItem,
@@ -10,12 +10,19 @@ import type {
10
10
  EpicDetail,
11
11
  ItemDetail,
12
12
  PromptFeedbackRow,
13
+ RowWorkItemRef,
13
14
  StoryDetail,
14
15
  WorkItemDetail,
15
16
  WorkItemMeta,
16
17
  WorkLogRow,
17
18
  } from "@/lib/taskmark/detail-types"
18
- import { StatusBadge, TypeBadge, PriorityBadge } from "@/components/board/status-badge"
19
+ import {
20
+ StatusBadge,
21
+ TypeBadge,
22
+ PriorityBadge,
23
+ typeBadgeClass,
24
+ } from "@/components/board/status-badge"
25
+ import { cn } from "@/lib/utils"
19
26
  import { AttributionPeopleList } from "@/components/board/attribution-avatars"
20
27
  import { DetailChildrenList } from "@/components/board/detail-children-list"
21
28
  import { MarkdownContent } from "@/components/board/markdown-content"
@@ -23,6 +30,12 @@ import { useWorkItemSheet } from "@/components/board/work-item-sheet"
23
30
  import { Badge } from "@/components/ui/badge"
24
31
  import { Button } from "@/components/ui/button"
25
32
  import { Separator } from "@/components/ui/separator"
33
+ import {
34
+ Tooltip,
35
+ TooltipContent,
36
+ TooltipProvider,
37
+ TooltipTrigger,
38
+ } from "@/components/ui/tooltip"
26
39
 
27
40
  function RelatedItemButton({ itemId }: { itemId: string }) {
28
41
  const { openDetailById } = useWorkItemSheet()
@@ -39,22 +52,101 @@ function RelatedItemButton({ itemId }: { itemId: string }) {
39
52
  )
40
53
  }
41
54
 
55
+ const MAX_VISIBLE_WORK_ITEMS = 2
56
+
57
+ function workItemKindFromId(id: string): "epic" | "story" | "task" | "bug" {
58
+ if (id.startsWith("B-")) return "bug"
59
+ if (id.startsWith("E-")) return "epic"
60
+ if (id.startsWith("S-")) return "story"
61
+ return "task"
62
+ }
63
+
64
+ function WorkItemTag({
65
+ item,
66
+ onOpen,
67
+ }: {
68
+ item: RowWorkItemRef
69
+ onOpen: (id: string) => void
70
+ }) {
71
+ return (
72
+ <TooltipProvider>
73
+ <Tooltip>
74
+ <TooltipTrigger
75
+ render={
76
+ <Badge
77
+ variant="outline"
78
+ title={item.title || item.id}
79
+ className={cn(
80
+ "cursor-pointer font-mono text-xs",
81
+ typeBadgeClass(workItemKindFromId(item.id))
82
+ )}
83
+ render={<button type="button" onClick={() => onOpen(item.id)} />}
84
+ />
85
+ }
86
+ >
87
+ {item.id}
88
+ </TooltipTrigger>
89
+ <TooltipContent>{item.title || item.id}</TooltipContent>
90
+ </Tooltip>
91
+ </TooltipProvider>
92
+ )
93
+ }
94
+
95
+ function WorkItemsCell({ items }: { items?: RowWorkItemRef[] }) {
96
+ const { openDetailById } = useWorkItemSheet()
97
+ if (!items || items.length === 0) return "—"
98
+
99
+ const visible = items.slice(0, MAX_VISIBLE_WORK_ITEMS)
100
+ const hidden = items.slice(MAX_VISIBLE_WORK_ITEMS)
101
+
102
+ return (
103
+ <span className="inline-flex flex-nowrap items-center gap-1 align-middle">
104
+ {visible.map((item) => (
105
+ <WorkItemTag key={item.id} item={item} onOpen={openDetailById} />
106
+ ))}
107
+ {hidden.length > 0 ? (
108
+ <TooltipProvider>
109
+ <Tooltip>
110
+ <TooltipTrigger
111
+ render={
112
+ <Badge
113
+ variant="outline"
114
+ className="cursor-default font-mono text-xs"
115
+ title={hidden.map((item) => item.id).join(", ")}
116
+ />
117
+ }
118
+ >
119
+ +{hidden.length}
120
+ </TooltipTrigger>
121
+ <TooltipContent>
122
+ <ul className="flex flex-col gap-0.5 text-left">
123
+ {hidden.map((item) => (
124
+ <li key={item.id}>
125
+ <span className="font-mono">{item.id}</span>
126
+ {item.title ? ` — ${item.title}` : null}
127
+ </li>
128
+ ))}
129
+ </ul>
130
+ </TooltipContent>
131
+ </Tooltip>
132
+ </TooltipProvider>
133
+ ) : null}
134
+ </span>
135
+ )
136
+ }
137
+
42
138
  function MetaGrid({ detail }: { detail: WorkItemMeta }) {
43
139
  const rows: { label: string; value: ReactNode }[] = [
44
140
  { label: "Status", value: <StatusBadge status={detail.status} /> },
45
141
  { label: "Priority", value: <PriorityBadge priority={detail.priority} /> },
46
142
  {
47
143
  label: "Size",
48
- value: detail.type === "epic" ? "—" : detail.size || "—",
144
+ value: detail.size || "—",
49
145
  },
50
146
  {
51
147
  label: "Points",
52
148
  value: detail.points == null ? "—" : String(detail.points),
53
149
  },
54
- {
55
- label: "Est",
56
- value: formatDurationMinutes(detail.estimateMinutes),
57
- },
58
150
  {
59
151
  label: "Actual",
60
152
  value: formatActualDuration(detail.actualMs, detail.actualMinutes),
@@ -67,7 +159,6 @@ function MetaGrid({ detail }: { detail: WorkItemMeta }) {
67
159
  label: "Epic",
68
160
  value: detail.epic ? <RelatedItemButton itemId={detail.epic} /> : "—",
69
161
  },
70
- { label: "Owner", value: detail.owner || "—" },
71
162
  { label: "Created", value: formatTaskmarkDate(detail.created) },
72
163
  { label: "Updated", value: formatTaskmarkDate(detail.updated) },
73
164
  { label: "Started", value: formatTaskmarkDate(detail.startedAt) },
@@ -162,6 +253,7 @@ function CommitsTable({ rows }: { rows: CommitRow[] }) {
162
253
  if (rows.length === 0) {
163
254
  return <p className="text-sm text-muted-foreground">No commits logged.</p>
164
255
  }
256
+ const showWorkItems = rows.some((row) => (row.workItems?.length ?? 0) > 0)
165
257
  return (
166
258
  <div className="w-full overflow-x-auto rounded border-2 border-border">
167
259
  <table className="w-full text-left text-xs">
@@ -172,6 +264,7 @@ function CommitsTable({ rows }: { rows: CommitRow[] }) {
172
264
  <th className="px-2 py-1.5">Date</th>
173
265
  <th className="px-2 py-1.5">Author</th>
174
266
  <th className="px-2 py-1.5">Message</th>
267
+ {showWorkItems ? <th className="px-2 py-1.5">Work items</th> : null}
175
268
  </tr>
176
269
  </thead>
177
270
  <tbody>
@@ -184,6 +277,11 @@ function CommitsTable({ rows }: { rows: CommitRow[] }) {
184
277
  </td>
185
278
  <td className="px-2 py-1.5">{row.author || "—"}</td>
186
279
  <td className="px-2 py-1.5">{row.message || "—"}</td>
280
+ {showWorkItems ? (
281
+ <td className="px-2 py-1.5 whitespace-nowrap">
282
+ <WorkItemsCell items={row.workItems} />
283
+ </td>
284
+ ) : null}
187
285
  </tr>
188
286
  ))}
189
287
  </tbody>
@@ -196,6 +294,7 @@ function WorkLogTable({ rows }: { rows: WorkLogRow[] }) {
196
294
  if (rows.length === 0) {
197
295
  return <p className="text-sm text-muted-foreground">No work sessions logged.</p>
198
296
  }
297
+ const showWorkItems = rows.some((row) => (row.workItems?.length ?? 0) > 0)
199
298
  return (
200
299
  <div className="w-full overflow-x-auto rounded border-2 border-border">
201
300
  <table className="w-full text-left text-xs">
@@ -206,6 +305,7 @@ function WorkLogTable({ rows }: { rows: WorkLogRow[] }) {
206
305
  <th className="px-2 py-1.5">Started</th>
207
306
  <th className="px-2 py-1.5">Ended</th>
208
307
  <th className="px-2 py-1.5">Summary</th>
308
+ {showWorkItems ? <th className="px-2 py-1.5">Work items</th> : null}
209
309
  </tr>
210
310
  </thead>
211
311
  <tbody>
@@ -220,6 +320,11 @@ function WorkLogTable({ rows }: { rows: WorkLogRow[] }) {
220
320
  {formatTaskmarkDate(row.ended)}
221
321
  </td>
222
322
  <td className="px-2 py-1.5">{row.summary || "—"}</td>
323
+ {showWorkItems ? (
324
+ <td className="px-2 py-1.5 whitespace-nowrap">
325
+ <WorkItemsCell items={row.workItems} />
326
+ </td>
327
+ ) : null}
223
328
  </tr>
224
329
  ))}
225
330
  </tbody>
@@ -232,6 +337,7 @@ function PromptTable({ rows }: { rows: PromptFeedbackRow[] }) {
232
337
  if (rows.length === 0) {
233
338
  return <p className="text-sm text-muted-foreground">No prompts logged.</p>
234
339
  }
340
+ const showWorkItems = rows.some((row) => (row.workItems?.length ?? 0) > 0)
235
341
  return (
236
342
  <div className="w-full overflow-x-auto rounded border-2 border-border">
237
343
  <table className="w-full text-left text-xs">
@@ -242,6 +348,7 @@ function PromptTable({ rows }: { rows: PromptFeedbackRow[] }) {
242
348
  <th className="px-2 py-1.5">Kind</th>
243
349
  <th className="px-2 py-1.5">Author</th>
244
350
  <th className="px-2 py-1.5">Summary</th>
351
+ {showWorkItems ? <th className="px-2 py-1.5">Work items</th> : null}
245
352
  </tr>
246
353
  </thead>
247
354
  <tbody>
@@ -254,6 +361,11 @@ function PromptTable({ rows }: { rows: PromptFeedbackRow[] }) {
254
361
  <td className="px-2 py-1.5">{row.kind || "—"}</td>
255
362
  <td className="px-2 py-1.5">{row.author || "—"}</td>
256
363
  <td className="px-2 py-1.5">{row.summary || "—"}</td>
364
+ {showWorkItems ? (
365
+ <td className="px-2 py-1.5 whitespace-nowrap">
366
+ <WorkItemsCell items={row.workItems} />
367
+ </td>
368
+ ) : null}
257
369
  </tr>
258
370
  ))}
259
371
  </tbody>
@@ -285,6 +397,9 @@ function EpicDetailBody({ detail }: { detail: EpicDetail }) {
285
397
  emptyLabel="No stories or epic-direct tasks yet."
286
398
  />
287
399
  </Section>
400
+ <Section title="Prompt & feedback">
401
+ <PromptTable rows={detail.promptFeedback} />
402
+ </Section>
288
403
  <Section title="Commits">
289
404
  <CommitsTable rows={detail.commits} />
290
405
  </Section>
@@ -22,6 +22,7 @@ import { ParentTagBadge } from "@/components/board/parent-tag-badge"
22
22
  import { ChildProgressBar } from "@/components/board/child-progress-bar"
23
23
  import {
24
24
  PriorityBadge,
25
+ statusRowClass,
25
26
  TypeBadge,
26
27
  } from "@/components/board/status-badge"
27
28
  import {
@@ -282,6 +283,7 @@ export function WorkItemsList({
282
283
  return (
283
284
  <TableRow
284
285
  key={`${project.id}:${row.kind}:${row.id}:${row.filePath}`}
286
+ className={statusRowClass(row.status)}
285
287
  >
286
288
  <TableCell className="w-10 pr-0">
287
289
  <ViewWorkItemButton
@@ -0,0 +1,39 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"
5
+
6
+ import {
7
+ parseThemePreference,
8
+ THEME_STORAGE_KEY,
9
+ writeThemeCookie,
10
+ } from "@/lib/theme-cookie"
11
+
12
+ type ThemeProviderProps = React.ComponentProps<typeof NextThemesProvider>
13
+
14
+ function ThemeCookieSync() {
15
+ const { theme } = useTheme()
16
+
17
+ React.useEffect(() => {
18
+ const preference = parseThemePreference(theme)
19
+ if (preference) writeThemeCookie(preference)
20
+ }, [theme])
21
+
22
+ return null
23
+ }
24
+
25
+ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
26
+ return (
27
+ <NextThemesProvider
28
+ attribute="class"
29
+ defaultTheme="system"
30
+ enableSystem
31
+ disableTransitionOnChange
32
+ storageKey={THEME_STORAGE_KEY}
33
+ {...props}
34
+ >
35
+ <ThemeCookieSync />
36
+ {children}
37
+ </NextThemesProvider>
38
+ )
39
+ }
@@ -0,0 +1,27 @@
1
+ "use client"
2
+
3
+ import { MoonIcon, SunIcon } from "lucide-react"
4
+ import { useTheme } from "next-themes"
5
+
6
+ import { Button } from "@/components/ui/button"
7
+
8
+ export function ThemeToggle() {
9
+ const { setTheme } = useTheme()
10
+
11
+ return (
12
+ <Button
13
+ type="button"
14
+ variant="outline"
15
+ size="icon"
16
+ aria-label="Switch between light and dark theme"
17
+ title="Switch between light and dark theme"
18
+ onClick={() => {
19
+ const isDark = document.documentElement.classList.contains("dark")
20
+ setTheme(isDark ? "light" : "dark")
21
+ }}
22
+ >
23
+ <SunIcon className="hidden size-4 dark:block" />
24
+ <MoonIcon className="size-4 dark:hidden" />
25
+ </Button>
26
+ )
27
+ }
@@ -11,11 +11,11 @@ import { useTheme } from "next-themes"
11
11
  import { Toaster as Sonner, type ToasterProps } from "sonner"
12
12
 
13
13
  const Toaster = ({ ...props }: ToasterProps) => {
14
- const { theme = "system" } = useTheme()
14
+ const { resolvedTheme } = useTheme()
15
15
 
16
16
  return (
17
17
  <Sonner
18
- theme={theme as ToasterProps["theme"]}
18
+ theme={(resolvedTheme === "dark" ? "dark" : "light") as ToasterProps["theme"]}
19
19
  className="toaster group"
20
20
  icons={{
21
21
  success: <CircleCheckIcon className="size-4" />,
@@ -1 +1 @@
1
- yyRnYAFQrJjF5_b5ZIeqh
1
+ EI1zQaa_kQ7DqUSE4o3Qo