@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,4 +2,4 @@
2
2
  * Touched by `taskmark dev` when board markdown changes so Next Fast Refresh
3
3
  * re-runs server components that import this module.
4
4
  */
5
- export const DEV_RELOAD_TOKEN = "0"
5
+ export const DEV_RELOAD_TOKEN = "1788031107000"
@@ -14,13 +14,10 @@ import {
14
14
  import type { DiscoveredProject } from "@/lib/taskmark/types"
15
15
 
16
16
  function hasBoardMarkers(boardPath: string): boolean {
17
- return (
18
- fs.existsSync(path.join(boardPath, "INDEX.md")) ||
19
- fs.existsSync(path.join(boardPath, "epics"))
20
- )
17
+ return fs.existsSync(path.join(boardPath, "epics"))
21
18
  }
22
19
 
23
- /** Single-project layout: `<project>/taskmark/` with INDEX.md or epics/. */
20
+ /** Single-project layout: `<project>/taskmark/` with epics/. */
24
21
  export function nestedBoardPath(candidateProjectRoot: string): string | null {
25
22
  const boardPath = path.join(candidateProjectRoot, "taskmark")
26
23
  try {
@@ -33,7 +30,7 @@ export function nestedBoardPath(candidateProjectRoot: string): string | null {
33
30
 
34
31
  /**
35
32
  * Multi-project layout: dedicated `<name>-taskmark` repo whose root is the board
36
- * (INDEX.md / epics/ at the folder root — no nested taskmark/).
33
+ * (`epics/` at the folder root — no nested taskmark/).
37
34
  */
38
35
  export function flatBoardPath(candidateRoot: string): string | null {
39
36
  if (!isDedicatedBoardRepoName(path.basename(candidateRoot))) return null
@@ -95,7 +92,7 @@ function shouldSkipDir(name: string): boolean {
95
92
  * Walk a master folder and find Taskmark boards in subfolders.
96
93
  *
97
94
  * Recognizes:
98
- * - Single-project: any directory with a nested `taskmark/` board (INDEX.md or epics/)
95
+ * - Single-project: any directory with a nested `taskmark/epics/` board
99
96
  * - Multi-project: dedicated `*-taskmark` folders with board files at the repo root
100
97
  *
101
98
  * Does not require a board at the master root, but includes the master itself if it
@@ -8,7 +8,6 @@ export type EpicSummary = {
8
8
  priority: string
9
9
  size: string
10
10
  points: number | null
11
- estimateMinutes: number | null
12
11
  actualMinutes: number | null
13
12
  actualMs: number | null
14
13
  tags: string[]
@@ -16,6 +15,8 @@ export type EpicSummary = {
16
15
  resolvers: ContributorIdentity[]
17
16
  /** Frontmatter created date (YYYY-MM-DD or ISO). */
18
17
  created: string
18
+ /** Frontmatter updated date (YYYY-MM-DD or ISO). */
19
+ updated: string
19
20
  /** Frontmatter completed_at (ISO) when solved; empty if open. */
20
21
  completedAt: string
21
22
  /** Stories + tasks + bugs under this epic (story children and epic-direct). */
@@ -12,7 +12,6 @@ export type FlatWorkItemRow = {
12
12
  priority: string
13
13
  size: string
14
14
  points: number | null
15
- estimateMinutes: number | null
16
15
  actualMinutes: number | null
17
16
  actualMs: number | null
18
17
  epicId: string | null
@@ -36,7 +35,6 @@ export type StoryWithEpicRow = {
36
35
  priority: string
37
36
  size: string
38
37
  points: number | null
39
- estimateMinutes: number | null
40
38
  actualMinutes: number | null
41
39
  actualMs: number | null
42
40
  epicId: string
@@ -60,7 +58,6 @@ export type TaskWithParentsRow = {
60
58
  priority: string
61
59
  size: string
62
60
  points: number | null
63
- estimateMinutes: number | null
64
61
  actualMinutes: number | null
65
62
  actualMs: number | null
66
63
  epicId: string
@@ -87,7 +84,6 @@ export type WorkItemsViewRow = {
87
84
  priority: string
88
85
  size: string
89
86
  points: number | null
90
- estimateMinutes: number | null
91
87
  actualMinutes: number | null
92
88
  actualMs: number | null
93
89
  epicId: string
@@ -96,6 +92,7 @@ export type WorkItemsViewRow = {
96
92
  reporters: ContributorIdentity[]
97
93
  resolvers: ContributorIdentity[]
98
94
  created: string
95
+ updated: string
99
96
  /** Frontmatter completed_at when solved; empty if open. */
100
97
  completedAt: string
101
98
  /** Child tasks/bugs under a story; 0 for epic-direct leaves. */
@@ -123,13 +123,13 @@ export function buildAttributionAvatars(
123
123
  const name = acc.identity.name || "Unknown"
124
124
  if (acc.created && acc.resolved) {
125
125
  role = "both"
126
- label = `Created and developed by ${name}`
126
+ label = `Reported and implemented by ${name}`
127
127
  } else if (acc.created) {
128
128
  role = "created"
129
- label = `Created by ${name}`
129
+ label = `Reported by ${name}`
130
130
  } else {
131
131
  role = "resolved"
132
- label = `Resolved by ${name}`
132
+ label = `Implemented by ${name}`
133
133
  }
134
134
  out.push({ identity: acc.identity, role, label })
135
135
  }
@@ -11,7 +11,6 @@ export type ItemSummary = {
11
11
  priority: string
12
12
  size: string
13
13
  points: number | null
14
- estimateMinutes: number | null
15
14
  actualMinutes: number | null
16
15
  actualMs: number | null
17
16
  tags: string[]
@@ -19,6 +18,8 @@ export type ItemSummary = {
19
18
  resolvers: ContributorIdentity[]
20
19
  /** Frontmatter created date (YYYY-MM-DD or ISO). */
21
20
  created: string
21
+ /** Frontmatter updated date (YYYY-MM-DD or ISO). */
22
+ updated: string
22
23
  /** Frontmatter completed_at (ISO) when solved; empty if open. */
23
24
  completedAt: string
24
25
  parent: string
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert/strict"
2
+ import test from "node:test"
3
+
4
+ import {
5
+ isCompletedStatus,
6
+ passesHideCompleted,
7
+ } from "./list-filters"
8
+
9
+ test("treats shelved as completed", () => {
10
+ assert.equal(isCompletedStatus("shelved"), true)
11
+ assert.equal(passesHideCompleted(true, "shelved"), false)
12
+ })
13
+
14
+ test("uses terminal child progress when filtering parents", () => {
15
+ assert.equal(
16
+ passesHideCompleted(true, "shelved", { done: 2, total: 2 }),
17
+ false
18
+ )
19
+ assert.equal(
20
+ passesHideCompleted(true, "shelved", { done: 1, total: 2 }),
21
+ true
22
+ )
23
+ })
@@ -87,10 +87,15 @@ export function matchesWorkItemSearch(
87
87
  }
88
88
 
89
89
  export function isCompletedStatus(status: string): boolean {
90
- return status.trim().toLowerCase() === "done"
90
+ const normalized = status.trim().toLowerCase()
91
+ return (
92
+ normalized === "done" ||
93
+ normalized === "shelved" ||
94
+ normalized === "cancelled"
95
+ )
91
96
  }
92
97
 
93
- /** True when every child is done (and there is at least one child). */
98
+ /** True when every child is terminal (and there is at least one child). */
94
99
  export function isChildProgressComplete(
95
100
  done?: number,
96
101
  total?: number
@@ -0,0 +1,45 @@
1
+ import assert from "node:assert/strict"
2
+ import test from "node:test"
3
+
4
+ import {
5
+ compareByStatusThenPriority,
6
+ listViewModes,
7
+ parseListViewMode,
8
+ statusSortRank,
9
+ } from "./list-view-mode"
10
+
11
+ test("omits changelog from the switcher when the file is absent", () => {
12
+ assert.deepEqual(listViewModes(false), ["overall", "workitems"])
13
+ assert.equal(parseListViewMode("changelog"), "overall")
14
+ assert.equal(parseListViewMode("changelog", { hasChangelog: false }), "overall")
15
+ })
16
+
17
+ test("keeps changelog when content is present", () => {
18
+ assert.deepEqual(listViewModes(true), ["overall", "workitems", "changelog"])
19
+ assert.equal(parseListViewMode("changelog", { hasChangelog: true }), "changelog")
20
+ })
21
+
22
+ test("sorts every incomplete status before shelved", () => {
23
+ assert.ok(statusSortRank("backlog") < statusSortRank("shelved"))
24
+ assert.ok(statusSortRank("in_progress") < statusSortRank("shelved"))
25
+ assert.ok(statusSortRank("blocked") < statusSortRank("shelved"))
26
+ })
27
+
28
+ test("sorts shelved rows by completion date instead of priority", () => {
29
+ const olderHighPriority = {
30
+ id: "T-1",
31
+ status: "shelved",
32
+ priority: "critical",
33
+ completedAt: "2026-08-01",
34
+ }
35
+ const newerLowPriority = {
36
+ id: "T-2",
37
+ status: "shelved",
38
+ priority: "low",
39
+ completedAt: "2026-08-29",
40
+ }
41
+
42
+ assert.ok(
43
+ compareByStatusThenPriority(olderHighPriority, newerLowPriority) > 0
44
+ )
45
+ })
@@ -1,4 +1,4 @@
1
- export const LIST_VIEW_MODES = ["overall", "workitems"] as const
1
+ export const LIST_VIEW_MODES = ["overall", "workitems", "changelog"] as const
2
2
 
3
3
  export type ListViewMode = (typeof LIST_VIEW_MODES)[number]
4
4
 
@@ -7,17 +7,27 @@ export const DEFAULT_LIST_VIEW_MODE: ListViewMode = "overall"
7
7
  export const LIST_VIEW_LABELS: Record<ListViewMode, string> = {
8
8
  overall: "Overall",
9
9
  workitems: "Work items",
10
+ changelog: "Changelog",
10
11
  }
11
12
 
12
13
  /** Legacy view query values that map to Work items. */
13
14
  const LEGACY_WORKITEMS_VIEWS = new Set(["all", "stories", "tasks", "workitems"])
14
15
 
16
+ export function listViewModes(hasChangelog: boolean): ListViewMode[] {
17
+ if (hasChangelog) return [...LIST_VIEW_MODES]
18
+ return LIST_VIEW_MODES.filter((mode) => mode !== "changelog")
19
+ }
20
+
15
21
  export function parseListViewMode(
16
- value: string | string[] | undefined
22
+ value: string | string[] | undefined,
23
+ options?: { hasChangelog?: boolean }
17
24
  ): ListViewMode {
18
25
  const raw = typeof value === "string" ? value.trim().toLowerCase() : ""
19
26
  if (raw === "overall") return "overall"
20
27
  if (LEGACY_WORKITEMS_VIEWS.has(raw)) return "workitems"
28
+ if (raw === "changelog") {
29
+ return options?.hasChangelog ? "changelog" : DEFAULT_LIST_VIEW_MODE
30
+ }
21
31
  return DEFAULT_LIST_VIEW_MODE
22
32
  }
23
33
 
@@ -45,9 +55,10 @@ export function boardHref(opts: {
45
55
  const STATUS_RANK: Record<string, number> = {
46
56
  backlog: 0,
47
57
  in_progress: 1,
48
- done: 2,
49
- blocked: 3,
50
- cancelled: 4,
58
+ blocked: 2,
59
+ done: 3,
60
+ shelved: 4,
61
+ cancelled: 5,
51
62
  }
52
63
 
53
64
  const PRIORITY_RANK: Record<string, number> = {
@@ -66,9 +77,9 @@ export function prioritySortRank(priority: string): number {
66
77
  }
67
78
 
68
79
  /**
69
- * Primary: status (backlog in_progress done);
80
+ * Primary: status, with every incomplete state before terminal outcomes;
70
81
  * For open statuses: priority (highest first), then created (newest first);
71
- * For done/cancelled: solved date only (completedAt newest first; no priority);
82
+ * For done/shelved/cancelled: solved date only (completedAt newest first; no priority);
72
83
  * then id.
73
84
  */
74
85
  export function compareByStatusThenPriority(
@@ -104,7 +115,7 @@ export function compareByStatusThenPriority(
104
115
 
105
116
  function usesCompletedDate(status: string): boolean {
106
117
  const s = status.trim().toLowerCase()
107
- return s === "done" || s === "cancelled"
118
+ return s === "done" || s === "shelved" || s === "cancelled"
108
119
  }
109
120
 
110
121
  function compareByDateThenId(
@@ -0,0 +1,32 @@
1
+ import assert from "node:assert/strict"
2
+ import fs from "node:fs"
3
+ import os from "node:os"
4
+ import path from "node:path"
5
+ import test from "node:test"
6
+
7
+ import { loadBoardChangelogMarkdown } from "./load-changelog"
8
+
9
+ function tempBoard(): string {
10
+ return fs.mkdtempSync(path.join(os.tmpdir(), "taskmark-changelog-"))
11
+ }
12
+
13
+ test("returns markdown when CHANGELOG.md has content", () => {
14
+ const dir = tempBoard()
15
+ fs.writeFileSync(
16
+ path.join(dir, "CHANGELOG.md"),
17
+ "# Changelog\n\n## Unreleased\n\n- A button was added.\n"
18
+ )
19
+ const loaded = loadBoardChangelogMarkdown(dir)
20
+ assert.ok(loaded)
21
+ assert.match(loaded, /A button was added/)
22
+ })
23
+
24
+ test("treats missing CHANGELOG.md as absent", () => {
25
+ assert.equal(loadBoardChangelogMarkdown(tempBoard()), null)
26
+ })
27
+
28
+ test("treats whitespace-only CHANGELOG.md as absent", () => {
29
+ const dir = tempBoard()
30
+ fs.writeFileSync(path.join(dir, "CHANGELOG.md"), " \n\t\n")
31
+ assert.equal(loadBoardChangelogMarkdown(dir), null)
32
+ })
@@ -0,0 +1,18 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+
4
+ /**
5
+ * Read board-root CHANGELOG.md for the UI. Never writes.
6
+ * Missing, unreadable, or whitespace-only files are absent.
7
+ */
8
+ export function loadBoardChangelogMarkdown(boardPath: string): string | null {
9
+ const filePath = path.join(boardPath, "CHANGELOG.md")
10
+ try {
11
+ const raw = fs.readFileSync(filePath, "utf8")
12
+ const trimmed = raw.trim()
13
+ if (!trimmed) return null
14
+ return raw
15
+ } catch {
16
+ return null
17
+ }
18
+ }
@@ -0,0 +1,196 @@
1
+ import assert from "node:assert/strict"
2
+ import test from "node:test"
3
+
4
+ import {
5
+ buildOverallTree,
6
+ compareOverallNodes,
7
+ filterOverallTree,
8
+ searchRevealIds,
9
+ type OverallTreeNode,
10
+ } from "./overall-tree"
11
+
12
+ function node(
13
+ id: string,
14
+ overrides: Partial<OverallTreeNode> = {}
15
+ ): OverallTreeNode {
16
+ return {
17
+ kind: "task",
18
+ id,
19
+ title: id,
20
+ status: "backlog",
21
+ size: "S",
22
+ points: 3,
23
+ tags: [],
24
+ reporters: [],
25
+ resolvers: [],
26
+ created: "2026-01-01",
27
+ updated: "2026-01-01",
28
+ completedAt: "",
29
+ filePath: `/${id}.md`,
30
+ children: [],
31
+ workItemCount: 0,
32
+ doneWorkItemCount: 0,
33
+ ...overrides,
34
+ }
35
+ }
36
+
37
+ test("sorts incomplete before done, shelved, and cancelled", () => {
38
+ const rows = [
39
+ node("T-4", { status: "done", created: "2026-08-29" }),
40
+ node("T-3", { status: "cancelled", created: "2026-08-30" }),
41
+ node("T-6", { status: "shelved", created: "2026-08-31" }),
42
+ node("T-2", { created: "2026-08-28", updated: "2026-08-29" }),
43
+ node("T-1", { created: "2026-08-28", updated: "2026-08-30" }),
44
+ node("T-0", { created: "2026-08-28", updated: "2026-08-30" }),
45
+ node("T-5", { created: "2026-08-29" }),
46
+ ]
47
+
48
+ assert.deepEqual(
49
+ rows.sort(compareOverallNodes).map((row) => row.id),
50
+ ["T-5", "T-0", "T-1", "T-2", "T-6", "T-3", "T-4"]
51
+ )
52
+ })
53
+
54
+ test("hides an empty General epic like the previous Overall list", () => {
55
+ const general = {
56
+ ...node("E-008", {
57
+ kind: "epic",
58
+ title: "General",
59
+ filePath: "/epics/E-008-general/epic.md",
60
+ }),
61
+ priority: "medium",
62
+ actualMinutes: null,
63
+ actualMs: null,
64
+ project: {},
65
+ }
66
+ const list = {
67
+ project: {},
68
+ epics: [{ ...general, workItemCount: 0 }],
69
+ errors: [],
70
+ } as unknown as Parameters<typeof buildOverallTree>[0]
71
+
72
+ assert.deepEqual(buildOverallTree(list, {}, {}), [])
73
+ })
74
+
75
+ test("search retains ancestors of matching descendants", () => {
76
+ const tree = [
77
+ node("E-1", {
78
+ kind: "epic",
79
+ title: "Parent",
80
+ children: [
81
+ node("S-1", {
82
+ kind: "story",
83
+ children: [node("T-1", { title: "Needle task" })],
84
+ }),
85
+ ],
86
+ }),
87
+ ]
88
+
89
+ const filtered = filterOverallTree(tree, {
90
+ query: "needle",
91
+ hideCompleted: false,
92
+ timeframe: { mode: "none" },
93
+ })
94
+
95
+ assert.equal(filtered[0]?.id, "E-1")
96
+ assert.equal(filtered[0]?.children[0]?.id, "S-1")
97
+ assert.equal(filtered[0]?.children[0]?.children[0]?.id, "T-1")
98
+ })
99
+
100
+ test("hide-completed retains completed ancestors with incomplete descendants", () => {
101
+ const tree = [
102
+ node("E-1", {
103
+ kind: "epic",
104
+ status: "done",
105
+ children: [
106
+ node("S-1", {
107
+ kind: "story",
108
+ status: "done",
109
+ children: [node("T-1", { status: "backlog" })],
110
+ }),
111
+ ],
112
+ }),
113
+ ]
114
+
115
+ const filtered = filterOverallTree(tree, {
116
+ query: "",
117
+ hideCompleted: true,
118
+ timeframe: { mode: "none" },
119
+ })
120
+
121
+ assert.equal(filtered[0]?.children[0]?.children[0]?.id, "T-1")
122
+ })
123
+
124
+ test("hide-completed removes shelved branches", () => {
125
+ const tree = [
126
+ node("E-1", {
127
+ kind: "epic",
128
+ status: "shelved",
129
+ children: [
130
+ node("S-1", {
131
+ kind: "story",
132
+ status: "shelved",
133
+ children: [node("T-1", { status: "shelved" })],
134
+ }),
135
+ ],
136
+ }),
137
+ ]
138
+
139
+ assert.deepEqual(
140
+ filterOverallTree(tree, {
141
+ query: "",
142
+ hideCompleted: true,
143
+ timeframe: { mode: "none" },
144
+ }),
145
+ []
146
+ )
147
+ })
148
+
149
+ test("only a search reveals ancestors, so toggles keep the expansion state", () => {
150
+ const tree = [
151
+ node("E-1", {
152
+ kind: "epic",
153
+ children: [
154
+ node("S-1", { kind: "story", children: [node("T-1")] }),
155
+ ],
156
+ }),
157
+ ]
158
+
159
+ assert.deepEqual(searchRevealIds(tree, ""), [])
160
+ assert.deepEqual(searchRevealIds(tree, " "), [])
161
+ assert.deepEqual(searchRevealIds(tree, "needle"), ["E-1", "S-1"])
162
+ })
163
+
164
+ test("timeframe retains ancestor context for matching completed leaves", () => {
165
+ const tree = [
166
+ node("E-1", {
167
+ kind: "epic",
168
+ children: [
169
+ node("S-1", {
170
+ kind: "story",
171
+ children: [
172
+ node("T-old", {
173
+ status: "done",
174
+ completedAt: "2026-01-01",
175
+ }),
176
+ node("T-match", {
177
+ status: "done",
178
+ completedAt: "2026-08-29",
179
+ }),
180
+ ],
181
+ }),
182
+ ],
183
+ }),
184
+ ]
185
+
186
+ const filtered = filterOverallTree(tree, {
187
+ query: "",
188
+ hideCompleted: false,
189
+ timeframe: { mode: "range", from: "2026-08-29", to: "2026-08-29" },
190
+ })
191
+
192
+ assert.deepEqual(
193
+ filtered[0]?.children[0]?.children.map((child) => child.id),
194
+ ["T-match"]
195
+ )
196
+ })