@nightkatana/kronosys-app 1.0.0-beta.4 → 1.0.0-beta.41

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 (542) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/standalone/.next/BUILD_ID +1 -1
  3. package/.next/standalone/.next/app-path-routes-manifest.json +6 -1
  4. package/.next/standalone/.next/build-manifest.json +3 -3
  5. package/.next/standalone/.next/prerender-manifest.json +3 -172
  6. package/.next/standalone/.next/required-server-files.json +7 -5
  7. package/.next/standalone/.next/routes-manifest.json +30 -0
  8. package/.next/standalone/.next/server/app/_global-error/page.js +3 -3
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  19. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/api/action/route.js +24 -6
  22. package/.next/standalone/.next/server/app/api/action/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/action-logs/route.js +45 -0
  24. package/.next/standalone/.next/server/app/api/action-logs/route.js.nft.json +1 -0
  25. package/.next/standalone/.next/server/app/api/action-logs/route_client-reference-manifest.js +1 -0
  26. package/.next/standalone/.next/server/app/api/backup/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/backup/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/health/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/health/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/restore/route.js +1 -0
  31. package/.next/standalone/.next/server/app/api/restore/route.js.nft.json +1 -0
  32. package/.next/standalone/.next/server/app/api/restore/route_client-reference-manifest.js +1 -0
  33. package/.next/standalone/.next/server/app/api/state/route.js +1 -1
  34. package/.next/standalone/.next/server/app/api/state/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/version-check/route.js +1 -0
  36. package/.next/standalone/.next/server/app/api/version-check/route.js.nft.json +1 -0
  37. package/.next/standalone/.next/server/app/api/version-check/route_client-reference-manifest.js +1 -0
  38. package/.next/standalone/.next/server/app/apple-icon.png/route.js +1 -1
  39. package/.next/standalone/.next/server/app/changelog/page.js +2 -2
  40. package/.next/standalone/.next/server/app/changelog/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/guide/page.js +2 -2
  43. package/.next/standalone/.next/server/app/guide/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/guide/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/icon.png/route.js +1 -1
  46. package/.next/standalone/.next/server/app/implementation/page.js +2 -0
  47. package/.next/standalone/.next/server/app/implementation/page.js.nft.json +1 -0
  48. package/.next/standalone/.next/server/app/implementation/page_client-reference-manifest.js +1 -0
  49. package/.next/standalone/.next/server/app/licenses/page.js +2 -22
  50. package/.next/standalone/.next/server/app/licenses/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/licenses/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/logs/page.js +2 -0
  53. package/.next/standalone/.next/server/app/logs/page.js.nft.json +1 -0
  54. package/.next/standalone/.next/server/app/logs/page_client-reference-manifest.js +1 -0
  55. package/.next/standalone/.next/server/app/manifest.webmanifest/route.js +2 -2
  56. package/.next/standalone/.next/server/app/manifest.webmanifest.body +1 -1
  57. package/.next/standalone/.next/server/app/page.js +7 -7
  58. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/reporting/page.js +2 -2
  61. package/.next/standalone/.next/server/app/reporting/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/reporting/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/settings/page.js +2 -2
  64. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app-paths-manifest.json +6 -1
  67. package/.next/standalone/.next/server/chunks/23.js +1 -0
  68. package/.next/standalone/.next/server/chunks/277.js +13 -7
  69. package/.next/standalone/.next/server/chunks/445.js +12 -12
  70. package/.next/standalone/.next/server/chunks/487.js +1 -0
  71. package/.next/standalone/.next/server/chunks/63.js +8 -8
  72. package/.next/standalone/.next/server/chunks/631.js +1 -0
  73. package/.next/standalone/.next/server/chunks/657.js +1 -0
  74. package/.next/standalone/.next/server/chunks/714.js +1 -0
  75. package/.next/standalone/.next/server/chunks/740.js +3 -0
  76. package/.next/standalone/.next/server/chunks/752.js +90 -0
  77. package/.next/standalone/.next/server/chunks/813.js +1 -1
  78. package/.next/standalone/.next/server/chunks/833.js +4 -0
  79. package/.next/standalone/.next/server/functions-config-manifest.json +2 -0
  80. package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  81. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  82. package/.next/standalone/.next/server/next-font-manifest.json +1 -1
  83. package/.next/standalone/.next/server/pages/500.html +1 -1
  84. package/.next/standalone/.next/server/pages-manifest.json +0 -1
  85. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  86. package/.next/standalone/.next/server/webpack-runtime.js +1 -1
  87. package/.next/standalone/.next/static/3cUpZ7Da9Ahhzdq0srbOV/_buildManifest.js +1 -0
  88. package/.next/standalone/.next/static/chunks/102-fd643497497a9b9e.js +1 -0
  89. package/.next/standalone/.next/static/chunks/227-ac9bbcd07955fca9.js +53 -0
  90. package/.next/standalone/.next/static/chunks/244-b2d8b651f4bbdea0.js +1 -0
  91. package/.next/standalone/.next/static/chunks/263-ac4001f38f81299c.js +4 -0
  92. package/.next/standalone/.next/static/chunks/317-edd92575d16974d9.js +1 -0
  93. package/.next/standalone/.next/static/chunks/400-ce1648f24df351a1.js +1 -0
  94. package/.next/standalone/.next/static/chunks/555-2cfd8090114d5ac8.js +1 -0
  95. package/.next/standalone/.next/static/chunks/558-8151f422e3fd565a.js +1 -0
  96. package/.next/standalone/.next/static/chunks/569-aafda3bfeb1cb4e5.js +1 -0
  97. package/.next/standalone/.next/static/chunks/702-033cf3571bb1add5.js +3 -0
  98. package/.next/standalone/.next/static/chunks/82-542b1f214e2e39ae.js +1 -0
  99. package/.next/standalone/.next/static/chunks/840-83aa65cc3eee5ac5.js +1 -0
  100. package/.next/standalone/.next/static/chunks/918-30897bc0b3f88e97.js +1 -0
  101. package/.next/standalone/.next/static/chunks/963-fef8714a78716b1a.js +1 -0
  102. package/.next/standalone/.next/static/chunks/app/_global-error/page-d4e4e10b259d5f0f.js +1 -0
  103. package/.next/standalone/.next/static/chunks/app/api/action/route-d4e4e10b259d5f0f.js +1 -0
  104. package/.next/standalone/.next/static/chunks/app/api/action-logs/route-d4e4e10b259d5f0f.js +1 -0
  105. package/.next/standalone/.next/static/chunks/app/api/backup/route-d4e4e10b259d5f0f.js +1 -0
  106. package/.next/standalone/.next/static/chunks/app/api/health/route-d4e4e10b259d5f0f.js +1 -0
  107. package/.next/standalone/.next/static/chunks/app/api/restore/route-d4e4e10b259d5f0f.js +1 -0
  108. package/.next/standalone/.next/static/chunks/app/api/state/route-d4e4e10b259d5f0f.js +1 -0
  109. package/.next/standalone/.next/static/chunks/app/api/version-check/route-d4e4e10b259d5f0f.js +1 -0
  110. package/.next/standalone/.next/static/chunks/app/changelog/page-7f780c1b36a2ddb9.js +1 -0
  111. package/.next/standalone/.next/static/chunks/app/guide/layout-d4e4e10b259d5f0f.js +1 -0
  112. package/.next/standalone/.next/static/chunks/app/guide/page-6e07594ac3175190.js +1 -0
  113. package/.next/standalone/.next/static/chunks/app/implementation/page-dfb5f75f238ecd34.js +1 -0
  114. package/.next/standalone/.next/static/chunks/app/layout-4cba44d778d24034.js +21 -0
  115. package/.next/standalone/.next/static/chunks/app/licenses/layout-d4e4e10b259d5f0f.js +1 -0
  116. package/.next/standalone/.next/static/chunks/app/licenses/page-9f61c749a03e5545.js +21 -0
  117. package/.next/standalone/.next/static/chunks/app/logs/page-d8640a587ea16957.js +1 -0
  118. package/.next/standalone/.next/static/chunks/app/manifest.webmanifest/route-d4e4e10b259d5f0f.js +1 -0
  119. package/.next/standalone/.next/static/chunks/app/page-de5cf8a8dee00974.js +22 -0
  120. package/.next/standalone/.next/static/chunks/app/reporting/page-b0c69796fc3e81f2.js +1 -0
  121. package/.next/standalone/.next/static/chunks/app/settings/layout-d4e4e10b259d5f0f.js +1 -0
  122. package/.next/standalone/.next/static/chunks/app/settings/page-2881390ef5c8c138.js +1 -0
  123. package/.next/standalone/.next/static/chunks/{main-934b539e9c7e4abe.js → main-c475a78be9d9d3c9.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d4e4e10b259d5f0f.js +1 -0
  125. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d4e4e10b259d5f0f.js +1 -0
  126. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d4e4e10b259d5f0f.js +1 -0
  127. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d4e4e10b259d5f0f.js +1 -0
  128. package/.next/standalone/.next/static/chunks/webpack-72874cc65370f1cd.js +1 -0
  129. package/.next/standalone/.next/static/css/{6fcf52f950db63c8.css → 46da805916c78992.css} +1 -1
  130. package/.next/standalone/.next/static/css/a07d69014ed91554.css +1 -0
  131. package/.next/standalone/.next/static/media/013b72fa676f92e0-s.woff2 +0 -0
  132. package/.next/standalone/.next/static/media/2b5b02fc7e511755-s.woff2 +0 -0
  133. package/.next/standalone/.next/static/media/65f03d54ccadf4a8-s.woff2 +0 -0
  134. package/.next/standalone/.next/static/media/7d4881bb7e1bf84d-s.p.woff2 +0 -0
  135. package/.next/standalone/.next/static/media/b9408752a0c24fb9-s.woff2 +0 -0
  136. package/.next/standalone/.next/static/media/e038a29029a234f2-s.woff2 +0 -0
  137. package/.next/standalone/kronosys-package-meta.json +1 -0
  138. package/.next/standalone/package.json +28 -33
  139. package/.next/standalone/server.js +1 -1
  140. package/README.md +49 -12
  141. package/bin/kronosys.mjs +211 -24
  142. package/package.json +28 -33
  143. package/.next/standalone/.next/server/app/_not-found.html +0 -9
  144. package/.next/standalone/.next/server/app/_not-found.meta +0 -16
  145. package/.next/standalone/.next/server/app/_not-found.rsc +0 -27
  146. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +0 -27
  147. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  148. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +0 -13
  149. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  150. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  151. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -6
  152. package/.next/standalone/.next/server/app/changelog.html +0 -9
  153. package/.next/standalone/.next/server/app/changelog.meta +0 -15
  154. package/.next/standalone/.next/server/app/changelog.rsc +0 -31
  155. package/.next/standalone/.next/server/app/changelog.segments/_full.segment.rsc +0 -31
  156. package/.next/standalone/.next/server/app/changelog.segments/_head.segment.rsc +0 -6
  157. package/.next/standalone/.next/server/app/changelog.segments/_index.segment.rsc +0 -13
  158. package/.next/standalone/.next/server/app/changelog.segments/_tree.segment.rsc +0 -6
  159. package/.next/standalone/.next/server/app/changelog.segments/changelog/__PAGE__.segment.rsc +0 -9
  160. package/.next/standalone/.next/server/app/changelog.segments/changelog.segment.rsc +0 -5
  161. package/.next/standalone/.next/server/app/guide.html +0 -9
  162. package/.next/standalone/.next/server/app/guide.meta +0 -15
  163. package/.next/standalone/.next/server/app/guide.rsc +0 -29
  164. package/.next/standalone/.next/server/app/guide.segments/_full.segment.rsc +0 -29
  165. package/.next/standalone/.next/server/app/guide.segments/_head.segment.rsc +0 -6
  166. package/.next/standalone/.next/server/app/guide.segments/_index.segment.rsc +0 -13
  167. package/.next/standalone/.next/server/app/guide.segments/_tree.segment.rsc +0 -6
  168. package/.next/standalone/.next/server/app/guide.segments/guide/__PAGE__.segment.rsc +0 -9
  169. package/.next/standalone/.next/server/app/guide.segments/guide.segment.rsc +0 -4
  170. package/.next/standalone/.next/server/app/index.html +0 -9
  171. package/.next/standalone/.next/server/app/index.meta +0 -14
  172. package/.next/standalone/.next/server/app/index.rsc +0 -30
  173. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -10
  174. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +0 -30
  175. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +0 -6
  176. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +0 -13
  177. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +0 -7
  178. package/.next/standalone/.next/server/app/licenses.html +0 -9
  179. package/.next/standalone/.next/server/app/licenses.meta +0 -15
  180. package/.next/standalone/.next/server/app/licenses.rsc +0 -29
  181. package/.next/standalone/.next/server/app/licenses.segments/_full.segment.rsc +0 -29
  182. package/.next/standalone/.next/server/app/licenses.segments/_head.segment.rsc +0 -6
  183. package/.next/standalone/.next/server/app/licenses.segments/_index.segment.rsc +0 -13
  184. package/.next/standalone/.next/server/app/licenses.segments/_tree.segment.rsc +0 -6
  185. package/.next/standalone/.next/server/app/licenses.segments/licenses/__PAGE__.segment.rsc +0 -9
  186. package/.next/standalone/.next/server/app/licenses.segments/licenses.segment.rsc +0 -4
  187. package/.next/standalone/.next/server/app/reporting.html +0 -9
  188. package/.next/standalone/.next/server/app/reporting.meta +0 -15
  189. package/.next/standalone/.next/server/app/reporting.rsc +0 -31
  190. package/.next/standalone/.next/server/app/reporting.segments/_full.segment.rsc +0 -31
  191. package/.next/standalone/.next/server/app/reporting.segments/_head.segment.rsc +0 -6
  192. package/.next/standalone/.next/server/app/reporting.segments/_index.segment.rsc +0 -13
  193. package/.next/standalone/.next/server/app/reporting.segments/_tree.segment.rsc +0 -6
  194. package/.next/standalone/.next/server/app/reporting.segments/reporting/__PAGE__.segment.rsc +0 -9
  195. package/.next/standalone/.next/server/app/reporting.segments/reporting.segment.rsc +0 -5
  196. package/.next/standalone/.next/server/app/settings.html +0 -9
  197. package/.next/standalone/.next/server/app/settings.meta +0 -15
  198. package/.next/standalone/.next/server/app/settings.rsc +0 -29
  199. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +0 -29
  200. package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +0 -6
  201. package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +0 -13
  202. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +0 -6
  203. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +0 -9
  204. package/.next/standalone/.next/server/app/settings.segments/settings.segment.rsc +0 -4
  205. package/.next/standalone/.next/server/chunks/104.js +0 -9
  206. package/.next/standalone/.next/server/chunks/16.js +0 -49
  207. package/.next/standalone/.next/server/chunks/177.js +0 -3
  208. package/.next/standalone/.next/server/chunks/590.js +0 -1
  209. package/.next/standalone/.next/server/chunks/658.js +0 -1
  210. package/.next/standalone/.next/server/chunks/942.js +0 -1
  211. package/.next/standalone/.next/server/pages/404.html +0 -9
  212. package/.next/standalone/.next/static/chunks/155-cdedad19f06ee45f.js +0 -49
  213. package/.next/standalone/.next/static/chunks/270-e7888e3c35848652.js +0 -1
  214. package/.next/standalone/.next/static/chunks/479-489c69d6eff752a2.js +0 -1
  215. package/.next/standalone/.next/static/chunks/498-1d9dbcc0c36b51e7.js +0 -1
  216. package/.next/standalone/.next/static/chunks/598-cc57a6fa6801c570.js +0 -1
  217. package/.next/standalone/.next/static/chunks/690-7c901031274462f8.js +0 -3
  218. package/.next/standalone/.next/static/chunks/889-3440fe146e529e67.js +0 -1
  219. package/.next/standalone/.next/static/chunks/993-1cb2e8683d684629.js +0 -1
  220. package/.next/standalone/.next/static/chunks/app/_global-error/page-53bc079dff893b85.js +0 -1
  221. package/.next/standalone/.next/static/chunks/app/api/action/route-53bc079dff893b85.js +0 -1
  222. package/.next/standalone/.next/static/chunks/app/api/backup/route-53bc079dff893b85.js +0 -1
  223. package/.next/standalone/.next/static/chunks/app/api/health/route-53bc079dff893b85.js +0 -1
  224. package/.next/standalone/.next/static/chunks/app/api/state/route-53bc079dff893b85.js +0 -1
  225. package/.next/standalone/.next/static/chunks/app/changelog/page-541aedca8e06f3e3.js +0 -1
  226. package/.next/standalone/.next/static/chunks/app/guide/layout-53bc079dff893b85.js +0 -1
  227. package/.next/standalone/.next/static/chunks/app/guide/page-b9347f93c8a9ebaa.js +0 -1
  228. package/.next/standalone/.next/static/chunks/app/layout-3c9b2ade2cd28d2c.js +0 -1
  229. package/.next/standalone/.next/static/chunks/app/licenses/layout-53bc079dff893b85.js +0 -1
  230. package/.next/standalone/.next/static/chunks/app/licenses/page-23a6137932aa9856.js +0 -21
  231. package/.next/standalone/.next/static/chunks/app/manifest.webmanifest/route-53bc079dff893b85.js +0 -1
  232. package/.next/standalone/.next/static/chunks/app/page-779637e187607866.js +0 -22
  233. package/.next/standalone/.next/static/chunks/app/reporting/page-bcb5a1897297e797.js +0 -1
  234. package/.next/standalone/.next/static/chunks/app/settings/layout-53bc079dff893b85.js +0 -1
  235. package/.next/standalone/.next/static/chunks/app/settings/page-b7eac8aeb092494d.js +0 -1
  236. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-53bc079dff893b85.js +0 -1
  237. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-53bc079dff893b85.js +0 -1
  238. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-53bc079dff893b85.js +0 -1
  239. package/.next/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-53bc079dff893b85.js +0 -1
  240. package/.next/standalone/.next/static/chunks/webpack-94545331cf4a1281.js +0 -1
  241. package/.next/standalone/.next/static/css/2cb439d7bd6673bc.css +0 -1
  242. package/.next/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  243. package/.next/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  244. package/.next/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  245. package/.next/standalone/.next/static/nzXLNr4JHdARH6TrNp6lD/_buildManifest.js +0 -1
  246. package/.next/standalone/node_modules/@img/colour/color.cjs +0 -1596
  247. package/.next/standalone/node_modules/@img/colour/index.cjs +0 -1
  248. package/.next/standalone/node_modules/@img/colour/package.json +0 -58
  249. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
  250. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  251. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +0 -42
  252. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/versions.json +0 -30
  253. package/.next/standalone/node_modules/@img/sharp-linux-x64/LICENSE +0 -191
  254. package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  255. package/.next/standalone/node_modules/@img/sharp-linux-x64/package.json +0 -46
  256. package/.next/standalone/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
  257. package/.next/standalone/node_modules/better-sqlite3/lib/database.js +0 -90
  258. package/.next/standalone/node_modules/better-sqlite3/lib/index.js +0 -3
  259. package/.next/standalone/node_modules/better-sqlite3/lib/methods/aggregate.js +0 -43
  260. package/.next/standalone/node_modules/better-sqlite3/lib/methods/backup.js +0 -67
  261. package/.next/standalone/node_modules/better-sqlite3/lib/methods/function.js +0 -31
  262. package/.next/standalone/node_modules/better-sqlite3/lib/methods/inspect.js +0 -7
  263. package/.next/standalone/node_modules/better-sqlite3/lib/methods/pragma.js +0 -12
  264. package/.next/standalone/node_modules/better-sqlite3/lib/methods/serialize.js +0 -16
  265. package/.next/standalone/node_modules/better-sqlite3/lib/methods/table.js +0 -189
  266. package/.next/standalone/node_modules/better-sqlite3/lib/methods/transaction.js +0 -78
  267. package/.next/standalone/node_modules/better-sqlite3/lib/methods/wrappers.js +0 -54
  268. package/.next/standalone/node_modules/better-sqlite3/lib/sqlite-error.js +0 -20
  269. package/.next/standalone/node_modules/better-sqlite3/lib/util.js +0 -12
  270. package/.next/standalone/node_modules/better-sqlite3/package.json +0 -59
  271. package/.next/standalone/node_modules/bindings/bindings.js +0 -221
  272. package/.next/standalone/node_modules/bindings/package.json +0 -28
  273. package/.next/standalone/node_modules/detect-libc/lib/detect-libc.js +0 -313
  274. package/.next/standalone/node_modules/detect-libc/lib/elf.js +0 -39
  275. package/.next/standalone/node_modules/detect-libc/lib/filesystem.js +0 -51
  276. package/.next/standalone/node_modules/detect-libc/lib/process.js +0 -24
  277. package/.next/standalone/node_modules/detect-libc/package.json +0 -44
  278. package/.next/standalone/node_modules/file-uri-to-path/index.js +0 -66
  279. package/.next/standalone/node_modules/file-uri-to-path/package.json +0 -32
  280. package/.next/standalone/node_modules/sharp/lib/channel.js +0 -177
  281. package/.next/standalone/node_modules/sharp/lib/colour.js +0 -195
  282. package/.next/standalone/node_modules/sharp/lib/composite.js +0 -212
  283. package/.next/standalone/node_modules/sharp/lib/constructor.js +0 -499
  284. package/.next/standalone/node_modules/sharp/lib/index.js +0 -16
  285. package/.next/standalone/node_modules/sharp/lib/input.js +0 -809
  286. package/.next/standalone/node_modules/sharp/lib/is.js +0 -143
  287. package/.next/standalone/node_modules/sharp/lib/libvips.js +0 -207
  288. package/.next/standalone/node_modules/sharp/lib/operation.js +0 -1016
  289. package/.next/standalone/node_modules/sharp/lib/output.js +0 -1666
  290. package/.next/standalone/node_modules/sharp/lib/resize.js +0 -595
  291. package/.next/standalone/node_modules/sharp/lib/sharp.js +0 -121
  292. package/.next/standalone/node_modules/sharp/lib/utility.js +0 -291
  293. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/comparator.js +0 -143
  294. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/range.js +0 -557
  295. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/semver.js +0 -333
  296. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/cmp.js +0 -54
  297. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/coerce.js +0 -62
  298. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/compare.js +0 -7
  299. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/eq.js +0 -5
  300. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gt.js +0 -5
  301. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gte.js +0 -5
  302. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lt.js +0 -5
  303. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lte.js +0 -5
  304. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/neq.js +0 -5
  305. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/parse.js +0 -18
  306. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/satisfies.js +0 -12
  307. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/constants.js +0 -37
  308. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/debug.js +0 -11
  309. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/identifiers.js +0 -29
  310. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/lrucache.js +0 -42
  311. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/parse-options.js +0 -17
  312. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/re.js +0 -223
  313. package/.next/standalone/node_modules/sharp/node_modules/semver/package.json +0 -78
  314. package/.next/standalone/node_modules/sharp/package.json +0 -202
  315. package/.next/static/chunks/155-cdedad19f06ee45f.js +0 -49
  316. package/.next/static/chunks/270-e7888e3c35848652.js +0 -1
  317. package/.next/static/chunks/479-489c69d6eff752a2.js +0 -1
  318. package/.next/static/chunks/498-1d9dbcc0c36b51e7.js +0 -1
  319. package/.next/static/chunks/4bd1b696-215e5051988c3dde.js +0 -1
  320. package/.next/static/chunks/598-cc57a6fa6801c570.js +0 -1
  321. package/.next/static/chunks/690-7c901031274462f8.js +0 -3
  322. package/.next/static/chunks/794-3bbf792b9526214d.js +0 -32
  323. package/.next/static/chunks/889-3440fe146e529e67.js +0 -1
  324. package/.next/static/chunks/993-1cb2e8683d684629.js +0 -1
  325. package/.next/static/chunks/app/_global-error/page-53bc079dff893b85.js +0 -1
  326. package/.next/static/chunks/app/_not-found/page-2acaadd098144869.js +0 -1
  327. package/.next/static/chunks/app/api/action/route-53bc079dff893b85.js +0 -1
  328. package/.next/static/chunks/app/api/backup/route-53bc079dff893b85.js +0 -1
  329. package/.next/static/chunks/app/api/health/route-53bc079dff893b85.js +0 -1
  330. package/.next/static/chunks/app/api/state/route-53bc079dff893b85.js +0 -1
  331. package/.next/static/chunks/app/changelog/page-541aedca8e06f3e3.js +0 -1
  332. package/.next/static/chunks/app/guide/layout-53bc079dff893b85.js +0 -1
  333. package/.next/static/chunks/app/guide/page-b9347f93c8a9ebaa.js +0 -1
  334. package/.next/static/chunks/app/layout-3c9b2ade2cd28d2c.js +0 -1
  335. package/.next/static/chunks/app/licenses/layout-53bc079dff893b85.js +0 -1
  336. package/.next/static/chunks/app/licenses/page-23a6137932aa9856.js +0 -21
  337. package/.next/static/chunks/app/manifest.webmanifest/route-53bc079dff893b85.js +0 -1
  338. package/.next/static/chunks/app/page-779637e187607866.js +0 -22
  339. package/.next/static/chunks/app/reporting/page-bcb5a1897297e797.js +0 -1
  340. package/.next/static/chunks/app/settings/layout-53bc079dff893b85.js +0 -1
  341. package/.next/static/chunks/app/settings/page-b7eac8aeb092494d.js +0 -1
  342. package/.next/static/chunks/framework-93cda6578f6c76ec.js +0 -1
  343. package/.next/static/chunks/main-934b539e9c7e4abe.js +0 -5
  344. package/.next/static/chunks/main-app-bdb688080b70ecf6.js +0 -1
  345. package/.next/static/chunks/next/dist/client/components/builtin/app-error-53bc079dff893b85.js +0 -1
  346. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-53bc079dff893b85.js +0 -1
  347. package/.next/static/chunks/next/dist/client/components/builtin/global-error-399b3c3c70060eef.js +0 -1
  348. package/.next/static/chunks/next/dist/client/components/builtin/not-found-53bc079dff893b85.js +0 -1
  349. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-53bc079dff893b85.js +0 -1
  350. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  351. package/.next/static/chunks/webpack-94545331cf4a1281.js +0 -1
  352. package/.next/static/css/2cb439d7bd6673bc.css +0 -1
  353. package/.next/static/css/6fcf52f950db63c8.css +0 -1
  354. package/.next/static/css/bdc8353fadd67def.css +0 -1
  355. package/.next/static/media/0a86735c6520d94f-s.p.woff2 +0 -0
  356. package/.next/static/media/32c80fb7588b7a2e-s.woff2 +0 -0
  357. package/.next/static/media/5b25d60b4670300e-s.p.woff2 +0 -0
  358. package/.next/static/media/60181e10ad16e354-s.woff2 +0 -0
  359. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  360. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  361. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  362. package/.next/static/media/99e31c27a1524300-s.woff2 +0 -0
  363. package/.next/static/media/c1b11e140b58cf5a-s.woff2 +0 -0
  364. package/.next/static/nzXLNr4JHdARH6TrNp6lD/_buildManifest.js +0 -1
  365. package/.next/static/nzXLNr4JHdARH6TrNp6lD/_ssgManifest.js +0 -1
  366. package/app/api/action/route.ts +0 -16
  367. package/app/api/backup/route.ts +0 -84
  368. package/app/api/health/route.ts +0 -22
  369. package/app/api/state/route.ts +0 -27
  370. package/app/apple-icon.png +0 -0
  371. package/app/changelog/page.tsx +0 -122
  372. package/app/globals.css +0 -210
  373. package/app/guide/layout.tsx +0 -11
  374. package/app/guide/page.tsx +0 -278
  375. package/app/icon.png +0 -0
  376. package/app/layout.tsx +0 -77
  377. package/app/licenses/layout.tsx +0 -11
  378. package/app/licenses/page.tsx +0 -246
  379. package/app/manifest.ts +0 -32
  380. package/app/page.tsx +0 -1610
  381. package/app/reporting/page.tsx +0 -2943
  382. package/app/settings/layout.tsx +0 -10
  383. package/app/settings/page.tsx +0 -3518
  384. package/components/KronosysPackageVersionProvider.tsx +0 -19
  385. package/components/KronosysPayloadProvider.tsx +0 -109
  386. package/components/PwaRegister.tsx +0 -25
  387. package/components/SiteLegalFooter.tsx +0 -21
  388. package/components/ThemeProvider.tsx +0 -78
  389. package/components/dashboard/AppShellLiveSessionDrawer.tsx +0 -394
  390. package/components/dashboard/AppShellRouteNav.tsx +0 -131
  391. package/components/dashboard/AppVersionStamp.tsx +0 -16
  392. package/components/dashboard/DashboardCollapsibleSection.tsx +0 -57
  393. package/components/dashboard/DashboardColumnHintsBanner.tsx +0 -159
  394. package/components/dashboard/DashboardCommandCenter.tsx +0 -470
  395. package/components/dashboard/DashboardLangGateModal.tsx +0 -118
  396. package/components/dashboard/DashboardLoadingOverlay.tsx +0 -42
  397. package/components/dashboard/DashboardSimpleModal.tsx +0 -337
  398. package/components/dashboard/DashboardSuspenseFallback.tsx +0 -52
  399. package/components/dashboard/DashboardToastProvider.tsx +0 -64
  400. package/components/dashboard/DashboardTour.tsx +0 -435
  401. package/components/dashboard/DeferredDescriptionPopoverWrap.tsx +0 -39
  402. package/components/dashboard/DeleteSessionModal.tsx +0 -130
  403. package/components/dashboard/DescriptionTooltipPortaled.tsx +0 -31
  404. package/components/dashboard/GitIdentityQuickSetupModal.tsx +0 -211
  405. package/components/dashboard/HeaderIntegrationBadges.tsx +0 -69
  406. package/components/dashboard/InlineMetricHelpTrigger.tsx +0 -102
  407. package/components/dashboard/IssuePickerModal.tsx +0 -168
  408. package/components/dashboard/KronoFocusPanel.tsx +0 -834
  409. package/components/dashboard/KronosysDatetimePopoverField.tsx +0 -357
  410. package/components/dashboard/KronosysTimePopoverField.tsx +0 -233
  411. package/components/dashboard/LanguageMenu.tsx +0 -123
  412. package/components/dashboard/MongoMirrorSyncLine.tsx +0 -57
  413. package/components/dashboard/NewSessionScopeModal.tsx +0 -410
  414. package/components/dashboard/PageRefreshButton.tsx +0 -130
  415. package/components/dashboard/PlainHelpPopover.tsx +0 -97
  416. package/components/dashboard/ReportingPageToc.tsx +0 -68
  417. package/components/dashboard/ReportingTour.tsx +0 -342
  418. package/components/dashboard/SavedProjectPicker.tsx +0 -92
  419. package/components/dashboard/SavedTagPicker.tsx +0 -115
  420. package/components/dashboard/ScrollToTopFab.tsx +0 -41
  421. package/components/dashboard/SelectedSessionSidebarBlock.tsx +0 -630
  422. package/components/dashboard/SessionEndReasonEditor.tsx +0 -114
  423. package/components/dashboard/SessionListPanel.tsx +0 -320
  424. package/components/dashboard/SessionLocMetricsSection.tsx +0 -128
  425. package/components/dashboard/SettingsTagsProjectsSection.tsx +0 -993
  426. package/components/dashboard/SettingsTour.tsx +0 -332
  427. package/components/dashboard/TagPills.tsx +0 -149
  428. package/components/dashboard/TagsHelpTrigger.tsx +0 -84
  429. package/components/dashboard/TaskFocusPanel.tsx +0 -1261
  430. package/components/dashboard/TaskSessionLiveCard.tsx +0 -832
  431. package/components/dashboard/TaskSubtasksBlock.tsx +0 -748
  432. package/components/dashboard/ThemeToggle.test.tsx +0 -26
  433. package/components/dashboard/ThemeToggle.tsx +0 -36
  434. package/components/dashboard/UserGuideBodyText.tsx +0 -62
  435. package/components/dashboard/WorkspaceGitRepoCard.tsx +0 -191
  436. package/components/dashboard/taskFieldStyles.ts +0 -139
  437. package/components/dashboard/useAnchoredFloatingPortalStyle.ts +0 -71
  438. package/components/dashboard/useDescriptionPopoverAfterMs.ts +0 -220
  439. package/components/dashboard/useKronoFocusLiveSeconds.ts +0 -36
  440. package/components/dashboard/useSmoothStopwatchMs.ts +0 -25
  441. package/lib/appShellHeaderClasses.ts +0 -12
  442. package/lib/backupCsvExport.test.ts +0 -149
  443. package/lib/backupCsvExport.ts +0 -392
  444. package/lib/changelogCopy.ts +0 -34
  445. package/lib/concurrentTaskStartPreference.ts +0 -29
  446. package/lib/dashboardClockFormat.ts +0 -13
  447. package/lib/dashboardColumnChrome.ts +0 -3
  448. package/lib/dashboardColumnHintsStorage.ts +0 -57
  449. package/lib/dashboardCopy.ts +0 -1831
  450. package/lib/dashboardDetachedUrlHintStorage.ts +0 -24
  451. package/lib/dashboardGitIdentityBannerStorage.ts +0 -36
  452. package/lib/dashboardLangStorage.ts +0 -72
  453. package/lib/dashboardQuickSearch.ts +0 -476
  454. package/lib/dashboardQuickSearchQuery.test.ts +0 -63
  455. package/lib/dashboardQuickSearchQuery.ts +0 -179
  456. package/lib/dashboardSessionNav.ts +0 -33
  457. package/lib/dashboardShortcuts.ts +0 -268
  458. package/lib/dashboardTimeZone.ts +0 -91
  459. package/lib/dashboardTourStorage.ts +0 -68
  460. package/lib/dataDir.test.ts +0 -87
  461. package/lib/dataDir.ts +0 -83
  462. package/lib/devDataPreferenceFile.ts +0 -55
  463. package/lib/devDataRuntimeInfo.ts +0 -34
  464. package/lib/formatIsoShort.test.ts +0 -46
  465. package/lib/formatIsoShort.ts +0 -29
  466. package/lib/generatedUserChangelog.ts +0 -34
  467. package/lib/gitlabIssueSearch.ts +0 -8
  468. package/lib/kronoFocusDurationHistory.ts +0 -71
  469. package/lib/kronoFocusRhythm.test.ts +0 -130
  470. package/lib/kronoFocusRhythm.ts +0 -46
  471. package/lib/kronoFocusTimerUrgency.test.ts +0 -74
  472. package/lib/kronoFocusTimerUrgency.ts +0 -24
  473. package/lib/kronosysApi.ts +0 -143
  474. package/lib/legacyEditorPayloadKeys.ts +0 -52
  475. package/lib/legacyKronoFocusStorageKeys.test.ts +0 -29
  476. package/lib/legacyKronoFocusStorageKeys.ts +0 -32
  477. package/lib/licensesCopy.ts +0 -128
  478. package/lib/openPlainTextInNewTab.ts +0 -49
  479. package/lib/readKronosysPackageVersion.ts +0 -10
  480. package/lib/reportingAggregate.test.ts +0 -325
  481. package/lib/reportingAggregate.ts +0 -819
  482. package/lib/reportingDatePresets.ts +0 -41
  483. package/lib/reportingMetricHelp.ts +0 -430
  484. package/lib/reportingNonFinalIndicators.test.ts +0 -157
  485. package/lib/reportingNonFinalIndicators.ts +0 -102
  486. package/lib/reportingStrings.ts +0 -491
  487. package/lib/reportingTagWeekBreakdown.test.ts +0 -141
  488. package/lib/reportingTagWeekBreakdown.ts +0 -181
  489. package/lib/reportingWeekLayout.test.ts +0 -239
  490. package/lib/reportingWeekLayout.ts +0 -313
  491. package/lib/sessionAssiduity.test.ts +0 -25
  492. package/lib/sessionAssiduity.ts +0 -33
  493. package/lib/sessionEndReason.ts +0 -55
  494. package/lib/sessionEndWarnings.test.ts +0 -200
  495. package/lib/sessionEndWarnings.ts +0 -125
  496. package/lib/sessionListMerge.test.ts +0 -101
  497. package/lib/sessionListMerge.ts +0 -70
  498. package/lib/sessionTaskSidebarStats.test.ts +0 -24
  499. package/lib/sessionTaskSidebarStats.ts +0 -54
  500. package/lib/settingsCopy.ts +0 -1276
  501. package/lib/taskParsing.test.ts +0 -153
  502. package/lib/taskParsing.ts +0 -737
  503. package/lib/theme.ts +0 -15
  504. package/lib/translucentButtonClasses.ts +0 -34
  505. package/lib/usageProfile.test.ts +0 -84
  506. package/lib/usageProfile.ts +0 -52
  507. package/lib/userGuideCopy.ts +0 -464
  508. package/lib/workspaceLocDefaults.ts +0 -21
  509. package/next-env.d.ts +0 -6
  510. package/next.config.ts +0 -16
  511. package/postcss.config.mjs +0 -12
  512. package/public/apple-icon.png +0 -0
  513. package/public/favicon.ico +0 -0
  514. package/public/file.svg +0 -1
  515. package/public/globe.svg +0 -1
  516. package/public/icon-192.png +0 -0
  517. package/public/icon-512.png +0 -0
  518. package/public/icon.png +0 -0
  519. package/public/next.svg +0 -1
  520. package/public/sw.js +0 -13
  521. package/public/traceback.png +0 -0
  522. package/public/vercel.svg +0 -1
  523. package/public/window.svg +0 -1
  524. package/server/actionDispatch.test.ts +0 -723
  525. package/server/actionDispatch.ts +0 -1476
  526. package/server/actionTaskSession.test.ts +0 -713
  527. package/server/actionTaskSession.ts +0 -717
  528. package/server/db.ts +0 -42
  529. package/server/defaultCfg.ts +0 -87
  530. package/server/gitlabTokenStore.ts +0 -34
  531. package/server/kronoFocusHydrate.test.ts +0 -142
  532. package/server/kronoFocusHydrate.ts +0 -69
  533. package/server/kronoFocusMigrate.test.ts +0 -53
  534. package/server/kronoFocusMigrate.ts +0 -78
  535. package/server/mainTimerHydrate.test.ts +0 -65
  536. package/server/mainTimerHydrate.ts +0 -53
  537. package/server/payloadStore.test.ts +0 -78
  538. package/server/payloadStore.ts +0 -83
  539. package/server/sessionWallHydrate.test.ts +0 -46
  540. package/server/sessionWallHydrate.ts +0 -88
  541. package/tsconfig.json +0 -41
  542. /package/.next/standalone/.next/static/{nzXLNr4JHdARH6TrNp6lD → 3cUpZ7Da9Ahhzdq0srbOV}/_ssgManifest.js +0 -0
@@ -1,737 +0,0 @@
1
- /**
2
- * Détection des `#token` dans un titre ou brouillon :
3
- * - après un espace ou en début : `… #release`, `#seul`
4
- * - collé au mot précédent : `mot#tag`, `issue#123`
5
- */
6
- const AUTO_TAG_COMBINED = /(?:^|\s)#([^\s#]+)|(?<=[^\s#])#([^\s#]+)/g;
7
-
8
- /**
9
- * Détection des `@projet` (mêmes idées que les `#tags`).
10
- */
11
- const AUTO_PROJECT_COMBINED = /(?:^|\s)@([^\s@]+)|(?<=[^\s@])@([^\s@]+)/g;
12
-
13
- /**
14
- * `@projet#suffixe` : étiquette liée à un projet (stockée comme `projet#suffixe`, sans `@`).
15
- * Traitée avant les `@projet` seuls pour ne pas confondre le corps `projet#…` avec un nom de projet.
16
- */
17
- const PROJECT_SCOPED_AT = /(?:^|\s)@([^\s@#]+)#([^\s#]+)/g;
18
-
19
- /** Extrait les jetons `@projet#suffixe` et retourne le texte restant. */
20
- export function stripScopedProjectAtTokens(raw: string): {
21
- core: string;
22
- scopedTags: string[];
23
- scopedProjectHint?: string;
24
- } {
25
- const scopedTags: string[] = [];
26
- let scopedProjectHint: string | undefined;
27
- const seen = new Set<string>();
28
- const core = raw
29
- .replace(PROJECT_SCOPED_AT, (_full, g1: string, g2: string) => {
30
- const proj = (g1 || "").trim();
31
- const loc = (g2 || "").trim();
32
- if (!proj || !loc) {
33
- return "";
34
- }
35
- if (!scopedProjectHint) {
36
- scopedProjectHint = proj;
37
- }
38
- const canon = `${proj}#${loc}`;
39
- const k = canon.toLowerCase();
40
- if (!seen.has(k)) {
41
- seen.add(k);
42
- scopedTags.push(canon);
43
- }
44
- return "";
45
- })
46
- .replace(/\s+/g, " ")
47
- .trim();
48
- return { core, scopedTags, scopedProjectHint };
49
- }
50
-
51
- export function parseProjectScopedTag(tag: string): { projectKey: string; localTag: string } | null {
52
- const t = tag.trim();
53
- const i = t.indexOf("#");
54
- if (i <= 0 || i >= t.length - 1) {
55
- return null;
56
- }
57
- const projectKey = t.slice(0, i).trim();
58
- const localTag = t.slice(i + 1).trim();
59
- if (!projectKey || !localTag || localTag.includes("#")) {
60
- return null;
61
- }
62
- return { projectKey, localTag };
63
- }
64
-
65
- export function isProjectScopedTag(tag: string): boolean {
66
- return parseProjectScopedTag(tag) !== null;
67
- }
68
-
69
- /** Étiquette « globale » (sans `#` de portée) ou étiquette `@projet#code` compatible avec le projet courant. */
70
- export function isTagAllowedForProject(tag: string, project: string | null | undefined): boolean {
71
- const scoped = parseProjectScopedTag(tag);
72
- if (!scoped) {
73
- return true;
74
- }
75
- const p = project?.trim();
76
- if (!p) {
77
- return false;
78
- }
79
- return scoped.projectKey.toLowerCase() === normalizeProjectKey(p).toLowerCase();
80
- }
81
-
82
- export function filterKnownTagsForProject(knownTags: string[], project: string | null | undefined): string[] {
83
- return knownTags.filter((t) => isTagAllowedForProject(t, project));
84
- }
85
-
86
- /** Retire les étiquettes `@projet#code` incompatibles avec le projet assigné (ou sans projet). */
87
- export function filterTaskTagsForProject(tags: string[], project: string | null | undefined): string[] {
88
- const p = project?.trim();
89
- if (!p) {
90
- return tags.filter((t) => !isProjectScopedTag(t));
91
- }
92
- return tags.filter((t) => isTagAllowedForProject(t, p));
93
- }
94
-
95
- /** Retire les `@projet` simples et retient le premier projet (une seule assignation par tâche). */
96
- function stripPlainProjectsAndTakeFirst(raw: string): { core: string; project?: string } {
97
- let first: string | undefined;
98
- const core = raw
99
- .replace(AUTO_PROJECT_COMBINED, (_full, g1: string, g2: string) => {
100
- const body = (g1 || g2 || "").trim();
101
- if (body && !body.includes("#") && !first) {
102
- first = normalizeProjectKey(body);
103
- }
104
- return "";
105
- })
106
- .replace(/\s+/g, " ")
107
- .trim();
108
- return { core, project: first };
109
- }
110
-
111
- /** Retire `@projet#suffixe` puis `@projet` ; fusionne l’indice de projet issu des jetons liés. */
112
- export function stripProjectsAndTakeFirst(raw: string): { core: string; project?: string; scopedTags: string[] } {
113
- const { core: c0, scopedTags, scopedProjectHint } = stripScopedProjectAtTokens(raw);
114
- const { core, project: plainProject } = stripPlainProjectsAndTakeFirst(c0);
115
- const project = plainProject ?? scopedProjectHint;
116
- return { core, project, scopedTags };
117
- }
118
-
119
- export function normalizeProjectKey(s: string): string {
120
- return s.replace(/^@+/, "").trim();
121
- }
122
-
123
- /**
124
- * Si le brouillon contient au moins un `@`, met à jour le projet (ou null si aucun jeton valide).
125
- * Sinon ne pas modifier le champ projet côté API (`undefined`).
126
- */
127
- export function resolveProjectForTaskUpdate(rawTitle: string): string | null | undefined {
128
- if (!/@/.test(rawTitle)) {
129
- return undefined;
130
- }
131
- const { project } = stripProjectsAndTakeFirst(rawTitle);
132
- return project ?? null;
133
- }
134
-
135
- /**
136
- * Projet à enregistrer à partir d’une ligne de tâche complète (champ unique avec `#`, `@`, `@p#t`).
137
- * Aucun `@` dans la chaîne ⇒ aucun projet (`null`). Sinon, premier projet déduit comme à la saisie.
138
- */
139
- export function resolveProjectFromFullTaskLine(rawTitle: string): string | null {
140
- const t = rawTitle.replace(/\s+/g, " ").trim();
141
- if (!/@/.test(t)) {
142
- return null;
143
- }
144
- const { project } = stripProjectsAndTakeFirst(t);
145
- return project ? normalizeProjectKey(project) : null;
146
- }
147
-
148
- /** Découpe le titre brut pour afficher le texte et les segments `#tag` comme pastilles (mêmes règles que {@link parseTaskWithAutoTags}). */
149
- export function splitTaskTitleSegments(raw: string): Array<{ kind: "text" | "tag"; value: string }> {
150
- const re = new RegExp(AUTO_TAG_COMBINED.source, "g");
151
- const out: Array<{ kind: "text" | "tag"; value: string }> = [];
152
- let last = 0;
153
- let m: RegExpExecArray | null;
154
- while ((m = re.exec(raw)) !== null) {
155
- if (m.index > last) {
156
- const text = raw.slice(last, m.index);
157
- if (text.length > 0) {
158
- out.push({ kind: "text", value: text });
159
- }
160
- }
161
- const body = (m[1] || m[2] || "").trim();
162
- if (body.length > 0) {
163
- out.push({ kind: "tag", value: `#${body}` });
164
- }
165
- last = m.index + m[0].length;
166
- }
167
- if (last < raw.length) {
168
- const text = raw.slice(last);
169
- if (text.length > 0) {
170
- out.push({ kind: "text", value: text });
171
- }
172
- }
173
- if (out.length === 0 && raw.trim().length > 0) {
174
- out.push({ kind: "text", value: raw });
175
- }
176
- return out;
177
- }
178
-
179
- /**
180
- * Étiquettes détectées dans `raw`, dans l’ordre d’apparition (gauche → droite),
181
- * sans doublon (insensible à la casse) — aligné sur {@link splitTaskTitleSegments}.
182
- */
183
- function mergeDedupedTagLists(primary: string[], secondary: string[]): string[] {
184
- const seen = new Set<string>();
185
- const out: string[] = [];
186
- const add = (t: string) => {
187
- const n = normalizeTagKey(t).trim();
188
- if (!n) {
189
- return;
190
- }
191
- const k = n.toLowerCase();
192
- if (seen.has(k)) {
193
- return;
194
- }
195
- seen.add(k);
196
- out.push(n);
197
- };
198
- for (const t of primary) {
199
- add(t);
200
- }
201
- for (const t of secondary) {
202
- add(t);
203
- }
204
- return out;
205
- }
206
-
207
- function collectAutoTagsFromRaw(raw: string): string[] {
208
- const { core: afterScoped, scopedTags } = stripScopedProjectAtTokens(raw);
209
- const fromHash: string[] = [];
210
- const seen = new Set<string>();
211
- for (const segment of splitTaskTitleSegments(afterScoped)) {
212
- if (segment.kind !== "tag") {
213
- continue;
214
- }
215
- const body = segment.value.replace(/^#/, "").trim();
216
- if (body.length === 0) {
217
- continue;
218
- }
219
- const key = body.toLowerCase();
220
- if (seen.has(key)) {
221
- continue;
222
- }
223
- seen.add(key);
224
- fromHash.push(body);
225
- }
226
- return mergeDedupedTagLists(scopedTags, fromHash);
227
- }
228
-
229
- function parseNameAndTagsFromCore(core: string): { name: string; tags: string[] } {
230
- const tags = collectAutoTagsFromRaw(core);
231
- const name = core
232
- .replace(new RegExp(AUTO_TAG_COMBINED.source, "g"), "")
233
- .replace(/\s+/g, " ")
234
- .trim();
235
- return { name, tags };
236
- }
237
-
238
- export function parseTaskWithAutoTags(raw: string): { name: string; tags: string[]; project?: string } {
239
- const { core, project, scopedTags } = stripProjectsAndTakeFirst(raw);
240
- const { name, tags } = parseNameAndTagsFromCore(core);
241
- const merged = mergeDedupedTagLists(scopedTags, tags);
242
- return { name, tags: filterTaskTagsForProject(merged, project ?? null), project };
243
- }
244
-
245
- /**
246
- * Interprète une ligne de saisie rapide sous une tâche (`#tag`, `@projet`, `@p#code`, texte seul comme étiquette).
247
- * Les étiquettes retournées sont déjà filtrées par projet si un `@projet` est détecté dans la même chaîne.
248
- */
249
- export function parseQuickAddTagOrProjectLine(input: string): { tags: string[]; project?: string | null } {
250
- const v = input.trim();
251
- if (!v) {
252
- return { tags: [] };
253
- }
254
- /** Seul `@` : retirer le projet (équivalent d’un clic « désactiver » sur la pastille projet enregistrée). */
255
- if (v === "@") {
256
- return { tags: [], project: null };
257
- }
258
- const parsed = parseTaskWithAutoTags(v);
259
- const name = parsed.name.replace(/\s+/g, "").trim();
260
- if (parsed.tags.length === 0 && parsed.project === undefined && name.length > 0) {
261
- return { tags: [normalizeTagKey(name)], project: undefined };
262
- }
263
- if (parsed.project === undefined) {
264
- return { tags: parsed.tags, project: undefined };
265
- }
266
- const proj = parsed.project ? normalizeProjectKey(parsed.project) : null;
267
- return { tags: parsed.tags, project: proj };
268
- }
269
-
270
- /**
271
- * Fusionne plusieurs lignes choisies (ex. valeurs d’un `<select multiple>` : `#tag`, `@projet`, `@`).
272
- * Les étiquettes sont dédupliquées (casse ignorée). Le dernier jeton qui fixe le projet l’emporte ; `@` seul retire le projet.
273
- */
274
- export function mergeQuickAddFromOptionValues(values: string[]): { tags: string[]; project?: string | null } {
275
- const seen = new Set<string>();
276
- const tags: string[] = [];
277
- let project: string | null | undefined = undefined;
278
- let projectTouched = false;
279
-
280
- for (const raw of values) {
281
- const r = parseQuickAddTagOrProjectLine(raw);
282
- for (const tg of r.tags) {
283
- const nk = normalizeTagKey(tg);
284
- if (!nk) {
285
- continue;
286
- }
287
- const k = nk.toLowerCase();
288
- if (seen.has(k)) {
289
- continue;
290
- }
291
- seen.add(k);
292
- tags.push(nk);
293
- }
294
- if (r.project !== undefined) {
295
- projectTouched = true;
296
- project = r.project;
297
- }
298
- }
299
-
300
- const out: { tags: string[]; project?: string | null } = { tags };
301
- if (projectTouched) {
302
- out.project = project;
303
- }
304
- return out;
305
- }
306
-
307
- /**
308
- * Texte de titre pour l’UI : sans segments `#tag` ni `@projet` (pastilles séparées).
309
- */
310
- export function taskTitleForDisplay(raw: string): string {
311
- const { name, tags } = parseTaskWithAutoTags(raw);
312
- const n = name.trim();
313
- if (n.length > 0) {
314
- return n;
315
- }
316
- if (tags.length > 0) {
317
- return "—";
318
- }
319
- return raw.trim() || "—";
320
- }
321
-
322
- /**
323
- * Valeur initiale du champ d’édition du titre (sans `#tags` ni `@projet` : pastilles en dessous).
324
- * Chaîne vide lorsqu’il n’y a pas de libellé textuel (équivalent du « — » en lecture seule).
325
- */
326
- export function taskTitleEditBaseline(storedName: string): string {
327
- const { name, tags } = parseTaskWithAutoTags(storedName);
328
- const n = name.trim();
329
- if (n.length > 0) {
330
- return n;
331
- }
332
- if (tags.length > 0) {
333
- return "";
334
- }
335
- return storedName.trim();
336
- }
337
-
338
- /**
339
- * Même extraction que {@link parseTaskWithAutoTags}, mais sans rogner le titre :
340
- * nécessaire pour le champ de saisie contrôlé.
341
- */
342
- export function parseTaskDraftParts(raw: string): { name: string; tags: string[]; project?: string } {
343
- const { core, project, scopedTags } = stripProjectsAndTakeFirst(raw);
344
- const fromCore = collectAutoTagsFromRaw(core);
345
- const tags = mergeDedupedTagLists(scopedTags, fromCore);
346
- const name = core.replace(new RegExp(AUTO_TAG_COMBINED.source, "g"), "");
347
- return { name, tags, project };
348
- }
349
-
350
- function appendTagTokenToDraft(s: string, tag: string): string {
351
- const t = normalizeTagKey(tag).trim();
352
- if (!t) {
353
- return s;
354
- }
355
- const scoped = parseProjectScopedTag(t);
356
- if (scoped) {
357
- const piece = `@${scoped.projectKey}#${scoped.localTag}`;
358
- const trimmed = s.replace(/\s+$/, "");
359
- return trimmed ? `${trimmed}${piece}` : piece;
360
- }
361
- const trimmedEnd = s.replace(/\s+$/, "");
362
- return trimmedEnd ? `${trimmedEnd}#${t}` : `#${t}`;
363
- }
364
-
365
- /** Reconstruit le brouillon (titre + #tags + `@projet#code` + @projet optionnel). */
366
- export function rebuildTaskDraftCore(name: string, tags: string[], project?: string): string {
367
- let s = name;
368
- for (const t of tags) {
369
- s = appendTagTokenToDraft(s, t);
370
- }
371
- const p = project?.trim();
372
- if (p) {
373
- const pk = normalizeProjectKey(p);
374
- const implied = tags.some((tg) => {
375
- const sc = parseProjectScopedTag(normalizeTagKey(tg));
376
- return sc && sc.projectKey.toLowerCase() === pk.toLowerCase();
377
- });
378
- if (!implied) {
379
- const trimmed = s.replace(/\s+$/, "");
380
- s = trimmed ? `${trimmed}@${pk}` : `@${pk}`;
381
- }
382
- }
383
- return s;
384
- }
385
-
386
- /** Reconstruit la chaîne « titre + #tags + `@p#t` [+ @projet] » alignée sur {@link parseTaskWithAutoTags}. */
387
- export function rebuildTaskRawString(name: string, tags: string[], project?: string | null): string {
388
- const n = name.replace(/\s+/g, " ").trim();
389
- let tagPart = n;
390
- for (const t of tags) {
391
- tagPart = appendTagTokenToDraft(tagPart, t);
392
- }
393
- const p = project?.trim();
394
- if (!p) {
395
- return tagPart;
396
- }
397
- const pk = normalizeProjectKey(p);
398
- const implied = tags.some((tg) => {
399
- const sc = parseProjectScopedTag(normalizeTagKey(tg));
400
- return sc && sc.projectKey.toLowerCase() === pk.toLowerCase();
401
- });
402
- if (implied) {
403
- return tagPart;
404
- }
405
- const trimmed = tagPart.replace(/\s+$/, "");
406
- return trimmed ? `${trimmed}@${pk}` : `@${pk}`;
407
- }
408
-
409
- /**
410
- * Chaîne pour le champ d’édition : nom stocké sans `#` / `@` + pastilles fusionnées + projet.
411
- */
412
- export function displayRawTaskTitle(
413
- storedName: string,
414
- mergedTags: string[],
415
- mergedProject?: string | null
416
- ): string {
417
- const { name: stripped } = parseTaskWithAutoTags(storedName);
418
- const base = stripped.replace(/\s+/g, " ").trim();
419
- const baseKey = base.toLowerCase();
420
- const suffixTags = mergedTags.filter((t) => normalizeTagKey(t).toLowerCase() !== baseKey);
421
- const suffix = suffixTags
422
- .map((t) => {
423
- const tk = normalizeTagKey(t);
424
- const sc = parseProjectScopedTag(tk);
425
- if (sc) {
426
- return `@${sc.projectKey}#${sc.localTag}`;
427
- }
428
- return `#${tk}`;
429
- })
430
- .join(" ");
431
- let out: string;
432
- if (!suffix) {
433
- out = base || storedName.trim();
434
- } else if (!base) {
435
- out = suffix;
436
- } else {
437
- out = `${base} ${suffix}`;
438
- }
439
- const p = mergedProject?.trim();
440
- if (p) {
441
- const pk = normalizeProjectKey(p);
442
- const implied = suffixTags.some((tg) => {
443
- const sc = parseProjectScopedTag(normalizeTagKey(tg));
444
- return sc && sc.projectKey.toLowerCase() === pk.toLowerCase();
445
- });
446
- if (!implied) {
447
- const trimmed = out.replace(/\s+$/, "");
448
- out = trimmed ? `${trimmed}@${pk}` : `@${pk}`;
449
- }
450
- }
451
- return out;
452
- }
453
-
454
- /** Ajoute un tag enregistré à la fin du brouillon (`#tag` ou `@projet#code`). */
455
- export function appendSavedTagToDraft(current: string, tag: string): string {
456
- const token = normalizeTagKey(tag);
457
- if (!token) {
458
- return current;
459
- }
460
- const parsed = parseTaskDraftParts(current);
461
- const nextTags = mergeDedupedTagLists(parsed.tags, [token]);
462
- const prevCore = rebuildTaskDraftCore(parsed.name, parsed.tags, parsed.project);
463
- const suff = current.startsWith(prevCore) ? current.slice(prevCore.length) : "";
464
- const core = rebuildTaskDraftCore(parsed.name, nextTags, parsed.project);
465
- return core + suff;
466
- }
467
-
468
- /** Retire une étiquette du brouillon (titre + jetons parsés), sans toucher au projet implicite. */
469
- export function removeSavedTagFromDraft(current: string, tag: string): string {
470
- const token = normalizeTagKey(tag);
471
- if (!token) {
472
- return current;
473
- }
474
- const key = token.toLowerCase();
475
- const parsed = parseTaskDraftParts(current);
476
- const nextTags = parsed.tags.filter((t) => normalizeTagKey(t).toLowerCase() !== key);
477
- return rebuildTaskRawString(parsed.name, nextTags, parsed.project ?? null);
478
- }
479
-
480
- /** Retire le projet (`@projet`) du brouillon sans modifier le libellé ni les étiquettes. */
481
- export function removeProjectFromDraft(current: string): string {
482
- const parsed = parseTaskDraftParts(current);
483
- return rebuildTaskRawString(parsed.name, parsed.tags, null);
484
- }
485
-
486
- /** Ajoute un projet connu à la fin du brouillon (`@nom`, sans espace avant `@` si le cœur se termine déjà par du texte). */
487
- export function appendSavedProjectToDraft(current: string, project: string): string {
488
- const token = normalizeProjectKey(project);
489
- if (!token) {
490
- return current;
491
- }
492
- const parsed = parseTaskDraftParts(current);
493
- if (parsed.project && parsed.project.toLowerCase() === token.toLowerCase()) {
494
- return current;
495
- }
496
- const core = rebuildTaskDraftCore(parsed.name, parsed.tags, token);
497
- const suff = current.startsWith(core) ? current.slice(core.length) : "";
498
- return core + suff;
499
- }
500
-
501
- export function buildStartTaskFromInput(raw: string): { name: string; tags: string[]; project?: string } {
502
- const trimmed = raw.trim();
503
- const { name, tags, project } = parseTaskWithAutoTags(trimmed);
504
- const displayName = name || tags[0] || trimmed;
505
- return { name: displayName, tags, project };
506
- }
507
-
508
- /**
509
- * Brouillon « en focus » : texte du champ + étiquettes / projet choisis via pastilles (sans les coller dans le titre).
510
- */
511
- export function buildStartTaskFromDraft(
512
- titleLine: string,
513
- pickerTags: string[],
514
- /** `undefined` : projet issu du titre ; chaîne vide : aucun projet (pastille désactivée). */
515
- pickerProject: string | undefined
516
- ): { name: string; tags: string[]; project?: string } {
517
- const trimmed = titleLine.trim();
518
- const parsed = parseTaskWithAutoTags(trimmed);
519
- const allTags = mergeTagsForDisplay(trimmed, pickerTags);
520
- const project =
521
- pickerProject === undefined ? parsed.project : pickerProject.trim() ? pickerProject : undefined;
522
- const tags = filterTaskTagsForProject(allTags, project ?? null);
523
- const displayName =
524
- parsed.name.trim() ||
525
- (tags.length > 0 ? tags[0] : "") ||
526
- trimmed ||
527
- (project ? normalizeProjectKey(project) : "");
528
- return {
529
- name: displayName,
530
- tags,
531
- ...(project ? { project } : {}),
532
- };
533
- }
534
-
535
- export function normalizeTagKey(s: string): string {
536
- return s.replace(/^#/, "");
537
- }
538
-
539
- /** Étiquette canonique pour une tâche sans aucune étiquette explicite (stable, indépendante de la langue d’affichage). */
540
- export const DEFAULT_FALLBACK_TASK_TAG = "default";
541
-
542
- /** Options pour {@link normalizeTaskTagsForStorage}. */
543
- export type TaskTagsStorageNormalizeOpts = {
544
- /**
545
- * Lorsque `false`, une tâche sans étiquette valide après nettoyage reste avec `tags: []`.
546
- * Lorsque `true` ou absent : comportement historique (`["default"]`).
547
- */
548
- assignDefaultTagBucket?: boolean;
549
- };
550
-
551
- /** Lit `cfg.taskDefaultTagBucketEnabled` du payload : `false` désactive le compartiment réservé ; défaut `true`. */
552
- export function readTaskDefaultTagBucketEnabled(cfg: unknown): boolean {
553
- const o =
554
- cfg && typeof cfg === "object" && !Array.isArray(cfg) ? (cfg as Record<string, unknown>) : undefined;
555
- if (o && o.taskDefaultTagBucketEnabled === false) {
556
- return false;
557
- }
558
- return true;
559
- }
560
-
561
- export function isFallbackTaskTagKey(tagKey: string): boolean {
562
- const t = tagKey.trim();
563
- if (t === "") {
564
- return true;
565
- }
566
- return normalizeTagKey(t).toLowerCase() === DEFAULT_FALLBACK_TASK_TAG;
567
- }
568
-
569
- /**
570
- * Déduplique les étiquettes ; si aucune ne subsiste après nettoyage, renvoie `[{@link DEFAULT_FALLBACK_TASK_TAG}]`
571
- * sauf si {@link TaskTagsStorageNormalizeOpts.assignDefaultTagBucket} vaut `false` (tableau vide).
572
- */
573
- export function normalizeTaskTagsForStorage(
574
- tags: string[] | null | undefined,
575
- opts?: TaskTagsStorageNormalizeOpts
576
- ): string[] {
577
- const raw = Array.isArray(tags) ? tags : [];
578
- const normalized: string[] = [];
579
- const seen = new Set<string>();
580
- for (const t of raw) {
581
- const token = normalizeTagKey(String(t)).trim();
582
- if (!token) {
583
- continue;
584
- }
585
- const k = token.toLowerCase();
586
- if (seen.has(k)) {
587
- continue;
588
- }
589
- seen.add(k);
590
- normalized.push(token);
591
- }
592
- if (normalized.length === 0) {
593
- if (opts?.assignDefaultTagBucket === false) {
594
- return [];
595
- }
596
- return [DEFAULT_FALLBACK_TASK_TAG];
597
- }
598
- return normalized;
599
- }
600
-
601
- /**
602
- * Fusionne les étiquettes du titre et celles du tableau `tags`, sans doublon (insensible à la casse).
603
- */
604
- export function mergeTagsForDisplay(rawName: string, storedTags?: string[] | null): string[] {
605
- const fromTitle = collectAutoTagsFromRaw(rawName);
606
- const fromArray = storedTags ?? [];
607
- const seen = new Set<string>();
608
- const out: string[] = [];
609
- const add = (t: string) => {
610
- const n = normalizeTagKey(t);
611
- const k = n.toLowerCase();
612
- if (!n || seen.has(k)) {
613
- return;
614
- }
615
- seen.add(k);
616
- out.push(n);
617
- };
618
- for (const t of fromTitle) {
619
- add(t);
620
- }
621
- for (const t of fromArray) {
622
- add(t);
623
- }
624
- return out;
625
- }
626
-
627
- export function formatTagDisplay(tag: string): string {
628
- const trimmed = tag.trim();
629
- if (!trimmed) {
630
- return trimmed;
631
- }
632
- const scoped = parseProjectScopedTag(trimmed.replace(/^#/, ""));
633
- if (scoped) {
634
- return `@${scoped.projectKey}#${scoped.localTag}`;
635
- }
636
- const noHash = trimmed.replace(/^#/, "");
637
- return trimmed.startsWith("#") ? trimmed : `#${noHash}`;
638
- }
639
-
640
- /** Libellé lisible pour une pastille d’étiquette ; `defaultBucketLabel` remplace l’affichage de l’étiquette réservée {@link DEFAULT_FALLBACK_TASK_TAG}. */
641
- export function formatTagDisplayForTask(tag: string, defaultBucketLabel?: string): string {
642
- const trimmed = normalizeTagKey(String(tag)).trim();
643
- if (!trimmed) {
644
- return "";
645
- }
646
- if (trimmed.toLowerCase() === DEFAULT_FALLBACK_TASK_TAG && defaultBucketLabel?.trim()) {
647
- return defaultBucketLabel.trim();
648
- }
649
- return formatTagDisplay(tag);
650
- }
651
-
652
- export function formatProjectDisplay(project: string): string {
653
- const trimmed = project.trim();
654
- if (!trimmed) {
655
- return trimmed;
656
- }
657
- return trimmed.startsWith("@") ? trimmed : `@${normalizeProjectKey(trimmed)}`;
658
- }
659
-
660
- /**
661
- * Durée lisible à partir d’une valeur en **minutes** (souvent fractionnaire, ex. agrégats reporting).
662
- * Les secondes non nulles sont affichées dès qu’elles apparaissent après arrondi à la seconde la plus proche.
663
- */
664
- export function formatDuration(minutes: number): string {
665
- const m = Math.max(0, minutes);
666
- if (m === 0) {
667
- return "0 min";
668
- }
669
- const totalSec = Math.round(m * 60);
670
- if (totalSec === 0) {
671
- return "1 s";
672
- }
673
- const h = Math.floor(totalSec / 3600);
674
- const remH = totalSec % 3600;
675
- const mi = Math.floor(remH / 60);
676
- const s = remH % 60;
677
-
678
- if (h > 0) {
679
- if (mi > 0 && s > 0) {
680
- return `${h}h ${mi}m ${s} s`;
681
- }
682
- if (mi > 0) {
683
- return `${h}h ${mi}m`;
684
- }
685
- if (s > 0) {
686
- return `${h}h ${s} s`;
687
- }
688
- return `${h}h`;
689
- }
690
- if (mi > 0) {
691
- return s > 0 ? `${mi} min ${s} s` : `${mi} min`;
692
- }
693
- return `${s} s`;
694
- }
695
-
696
- /**
697
- * Durée murale affichée à partir de **millisecondes** : secondes obtenues par `floor(ms/1000)` pour que
698
- * l’affichage progresse chaque seconde (ex. minuteur de session live entre deux rafraîchissements API).
699
- * Toujours inclure les secondes lorsqu’il y a au moins une minute (`5 min 0 s`, `5 min 1 s`, …).
700
- */
701
- export function formatWallDurationMs(ms: number): string {
702
- const m = Math.max(0, ms);
703
- const totalSec = Math.floor(m / 1000);
704
- if (totalSec === 0) {
705
- return "0 s";
706
- }
707
- const h = Math.floor(totalSec / 3600);
708
- const rem = totalSec % 3600;
709
- const mi = Math.floor(rem / 60);
710
- const s = rem % 60;
711
- if (h > 0) {
712
- return `${h}h ${mi}m ${s} s`;
713
- }
714
- if (mi > 0) {
715
- return `${mi} min ${s} s`;
716
- }
717
- return `${s} s`;
718
- }
719
-
720
- /**
721
- * Chronomètre à partir de millisecondes : minutes (non padées) : secondes . centièmes,
722
- * ou heures : minutes : secondes . centièmes au-delà d’une heure.
723
- */
724
- export function formatStopwatchMs(ms: number): string {
725
- const m = Math.max(0, Math.floor(ms));
726
- const hours = Math.floor(m / 3600000);
727
- const remH = m % 3600000;
728
- const minutes = Math.floor(remH / 60000);
729
- const remM = remH % 60000;
730
- const seconds = Math.floor(remM / 1000);
731
- const centis = Math.floor((remM % 1000) / 10);
732
- const pad2 = (n: number) => String(n).padStart(2, "0");
733
- if (hours > 0) {
734
- return `${hours}:${pad2(minutes)}:${pad2(seconds)}.${pad2(centis)}`;
735
- }
736
- return `${minutes}:${pad2(seconds)}.${pad2(centis)}`;
737
- }