fleetlens 0.13.0 → 0.13.2

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 (211) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/build-manifest.json +8 -8
  3. package/app/apps/web/.next/prerender-manifest.json +3 -3
  4. package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +5 -5
  5. package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +7 -7
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page/build-manifest.json +5 -5
  14. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  15. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  18. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +1 -1
  24. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  26. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  27. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  28. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  29. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  30. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  31. package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
  32. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  33. package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +1 -1
  34. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/changelog/page/build-manifest.json +5 -5
  36. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  37. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  38. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  39. package/app/apps/web/.next/server/app/day/[date]/page/build-manifest.json +5 -5
  40. package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
  41. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  42. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  43. package/app/apps/web/.next/server/app/day/page/build-manifest.json +5 -5
  44. package/app/apps/web/.next/server/app/day/page.js +1 -1
  45. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  46. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  47. package/app/apps/web/.next/server/app/digest/[date]/page/build-manifest.json +5 -5
  48. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  49. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  50. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  51. package/app/apps/web/.next/server/app/digest/page/build-manifest.json +5 -5
  52. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  53. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  54. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  55. package/app/apps/web/.next/server/app/insights/[key]/page/build-manifest.json +5 -5
  56. package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
  57. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  58. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  59. package/app/apps/web/.next/server/app/insights/page/build-manifest.json +5 -5
  60. package/app/apps/web/.next/server/app/insights/page.js +3 -2
  61. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  62. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  63. package/app/apps/web/.next/server/app/page/build-manifest.json +5 -5
  64. package/app/apps/web/.next/server/app/page.js +1 -1
  65. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  66. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  67. package/app/apps/web/.next/server/app/parallelism/page/build-manifest.json +5 -5
  68. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  69. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  70. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  71. package/app/apps/web/.next/server/app/print/[key]/page/build-manifest.json +5 -5
  72. package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
  73. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  74. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  75. package/app/apps/web/.next/server/app/projects/[slug]/page/build-manifest.json +5 -5
  76. package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
  77. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  78. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  79. package/app/apps/web/.next/server/app/projects/page/build-manifest.json +5 -5
  80. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  81. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  82. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  83. package/app/apps/web/.next/server/app/runs/page/build-manifest.json +5 -5
  84. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  85. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  86. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  87. package/app/apps/web/.next/server/app/sessions/[id]/page/build-manifest.json +5 -5
  88. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  89. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  90. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  91. package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +5 -5
  92. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  93. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  94. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  95. package/app/apps/web/.next/server/app/settings/page/build-manifest.json +5 -5
  96. package/app/apps/web/.next/server/app/settings/page.js +1 -1
  97. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  98. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  99. package/app/apps/web/.next/server/app/team/page/build-manifest.json +5 -5
  100. package/app/apps/web/.next/server/app/team/page.js +1 -1
  101. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  102. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  103. package/app/apps/web/.next/server/app/usage/page/build-manifest.json +5 -5
  104. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  105. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  106. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  107. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gril0u._.js +9 -0
  108. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0n79hpm._.js +9 -0
  109. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~utv~v._.js +9 -0
  110. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  111. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  112. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +1 -1
  113. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0-1_njm._.js → [root-of-the-server]__0k1x-c9._.js} +2 -2
  114. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lhwp-k._.js +8 -0
  115. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +1 -1
  116. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +3 -3
  117. package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +1 -1
  118. package/app/apps/web/.next/server/chunks/ssr/_0s3n5mz._.js +8 -0
  119. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  120. package/app/apps/web/.next/server/chunks/ssr/{_0jw2sm7._.js → _12mbgs5._.js} +2 -2
  121. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
  122. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_insights_page_tsx_071kwyw._.js +3 -0
  123. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_sessions-grid_tsx_0bri5x.._.js +3 -0
  124. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0l6hsch._.js +6 -0
  125. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  126. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  127. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0cepfi8._.js +3 -0
  128. package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
  129. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
  130. package/app/apps/web/.next/server/middleware-build-manifest.js +8 -8
  131. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  132. package/app/apps/web/.next/server/pages/500.html +1 -1
  133. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  134. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  135. package/app/apps/web/.next/static/chunks/02xxpexzk3rf-.js +1 -0
  136. package/app/apps/web/.next/static/chunks/{07lbm-sdh9c2j.js → 0389wb_f0j78j.js} +1 -1
  137. package/app/apps/web/.next/static/chunks/03gy1evbn94qu.js +6 -0
  138. package/app/apps/web/.next/static/chunks/03rd.3cc_t~yf.js +4 -0
  139. package/app/apps/web/.next/static/chunks/{0l4e3as18eexy.js → 0g223u9amib0q.js} +1 -1
  140. package/app/apps/web/.next/static/chunks/{0h3.-_s-k0mj3.js → 0i.hc~2bj0_3d.js} +1 -1
  141. package/app/apps/web/.next/static/chunks/0m7dd-2zd9vl2.js +4 -0
  142. package/app/apps/web/.next/static/chunks/0nqklmbs5s3ax.js +6 -0
  143. package/app/apps/web/.next/static/chunks/{073081qv4uyn8.js → 0p--hzrway8~3.js} +1 -1
  144. package/app/apps/web/.next/static/chunks/{0gblp_v.kiu-..js → 0qv-gw888_5nu.js} +1 -1
  145. package/app/apps/web/.next/static/chunks/{14bjditmmrn7l.js → 0tirv_vo6q8uc.js} +1 -1
  146. package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +1 -0
  147. package/app/apps/web/.next/static/chunks/0~qdw1b2uz._5.js +10 -0
  148. package/app/apps/web/.next/static/chunks/{0n0n.0279ot6c.js → 14vx702c32kn6.js} +1 -1
  149. package/app/apps/web/.next/static/chunks/{turbopack-0lb9w090w5ezx.js → turbopack-0mxu3~9vk.brh.js} +1 -1
  150. package/app/apps/web/app/api/digest/_shared/digest-route-helpers.ts +178 -0
  151. package/app/apps/web/app/api/digest/day/[date]/route.ts +34 -128
  152. package/app/apps/web/app/api/digest/month/[yearMonth]/route.ts +36 -121
  153. package/app/apps/web/app/api/digest/week/[startDate]/route.ts +36 -123
  154. package/app/apps/web/app/insights/page.tsx +20 -5
  155. package/app/apps/web/app/parallelism/burst-detail-modal.tsx +637 -0
  156. package/app/apps/web/app/parallelism/concurrency-info-modal.tsx +228 -0
  157. package/app/apps/web/app/parallelism/gantt-chart-utils.ts +49 -0
  158. package/app/apps/web/app/parallelism/gantt-chart.tsx +24 -903
  159. package/app/apps/web/app/sessions/[id]/session-view/colors.ts +31 -0
  160. package/app/apps/web/app/sessions/[id]/session-view/debug-list.tsx +78 -0
  161. package/app/apps/web/app/sessions/[id]/session-view/drawer.tsx +414 -0
  162. package/app/apps/web/app/sessions/[id]/session-view/entry-day-strip.tsx +124 -0
  163. package/app/apps/web/app/sessions/[id]/session-view/header-stats.tsx +217 -0
  164. package/app/apps/web/app/sessions/[id]/session-view/helpers.ts +135 -0
  165. package/app/apps/web/app/sessions/[id]/session-view/minimap.tsx +1587 -0
  166. package/app/apps/web/app/sessions/[id]/session-view/subagent-drawer.tsx +339 -0
  167. package/app/apps/web/app/sessions/[id]/session-view/token-stats.tsx +111 -0
  168. package/app/apps/web/app/sessions/[id]/session-view/tooltip.tsx +63 -0
  169. package/app/apps/web/app/sessions/[id]/session-view/types.ts +46 -0
  170. package/app/apps/web/app/sessions/[id]/session-view/workflows-panel.tsx +944 -0
  171. package/app/apps/web/app/sessions/[id]/session-view/workflows-shared.tsx +104 -0
  172. package/app/apps/web/app/sessions/[id]/session-view.tsx +1513 -5018
  173. package/app/apps/web/app/sessions/[id]/team-tab/minimap-hover-card.tsx +201 -0
  174. package/app/apps/web/app/sessions/[id]/team-tab/minimap-idle-band.tsx +53 -0
  175. package/app/apps/web/app/sessions/[id]/team-tab/minimap-shared.ts +20 -0
  176. package/app/apps/web/app/sessions/[id]/team-tab/team-minimap.tsx +10 -265
  177. package/app/apps/web/app/sessions/sessions-grid.tsx +10 -10
  178. package/app/apps/web/components/digest-actions.tsx +103 -0
  179. package/app/apps/web/components/job-queue-widget.tsx +1 -1
  180. package/app/apps/web/components/month-digest-view.tsx +19 -64
  181. package/app/apps/web/components/searchable-select.tsx +215 -0
  182. package/app/apps/web/components/tail-mode.tsx +46 -9
  183. package/app/apps/web/components/week-digest-view.tsx +16 -71
  184. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  185. package/app/apps/web/lib/entries-index.ts +7 -8
  186. package/app/apps/web/lib/entries.ts +0 -9
  187. package/app/apps/web/lib/format.test.ts +221 -0
  188. package/app/apps/web/lib/format.ts +17 -0
  189. package/app/apps/web/lib/utilization-tone.test.ts +56 -0
  190. package/app/apps/web/package.json +1 -1
  191. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  192. package/dist/daemon-worker.js +35 -43
  193. package/dist/index.js +37 -49
  194. package/package.json +1 -1
  195. package/app/apps/web/.next/server/chunks/[root-of-the-server]__04j3tng._.js +0 -9
  196. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0cnh3k4._.js +0 -9
  197. package/app/apps/web/.next/server/chunks/[root-of-the-server]__132eosx._.js +0 -9
  198. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +0 -8
  199. package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +0 -8
  200. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +0 -6
  201. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0-lmm4p._.js +0 -3
  202. package/app/apps/web/.next/static/chunks/05k84gif3ucxm.js +0 -6
  203. package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +0 -4
  204. package/app/apps/web/.next/static/chunks/0okg13phslr1..js +0 -4
  205. package/app/apps/web/.next/static/chunks/0pzt9vemtzct8.js +0 -10
  206. package/app/apps/web/.next/static/chunks/0qmh5da6gfm56.js +0 -1
  207. package/app/apps/web/.next/static/chunks/0rnay-a.-jr8-.js +0 -1
  208. package/app/apps/web/.next/static/chunks/0snv3-2ux2xvv.js +0 -6
  209. /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_buildManifest.js +0 -0
  210. /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_clientMiddlewareManifest.js +0 -0
  211. /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_ssgManifest.js +0 -0
@@ -0,0 +1,217 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Header stats + token stat with anchored tooltip.
5
+ *
6
+ * - InlineStat / InlineStatDivider — compact icon+value chips used in the
7
+ * single-line session header, modeled on Claude's Sessions page.
8
+ * - EntrypointBadge — colors `cli` / `claude-desktop` (green, human-driven)
9
+ * vs `sdk-*` (amber, programmatic).
10
+ * - InlineTokenStat — session-header token chip with a tooltip that
11
+ * escapes the header's overflow:hidden ancestor via getBoundingClientRect
12
+ * + position:fixed (`useAnchoredTooltip` + `AnchoredTooltip`).
13
+ *
14
+ * `useAnchoredTooltip` is a hook — it must be defined and consumed inside
15
+ * a component (no hook calls at module load).
16
+ */
17
+ import React, { useRef, useState } from "react";
18
+ import type { SessionEvent } from "@claude-lens/parser";
19
+ import { formatTokens } from "@/lib/format";
20
+ import { TooltipRow } from "./tooltip";
21
+
22
+ export function InlineStat({
23
+ icon,
24
+ value,
25
+ mono,
26
+ truncate,
27
+ }: {
28
+ icon?: React.ReactNode;
29
+ value: string;
30
+ mono?: boolean;
31
+ truncate?: boolean;
32
+ }) {
33
+ return (
34
+ <span
35
+ style={{
36
+ display: "inline-flex",
37
+ alignItems: "center",
38
+ gap: 5,
39
+ fontSize: 12,
40
+ color: "var(--af-text-secondary)",
41
+ maxWidth: truncate ? 260 : undefined,
42
+ overflow: "hidden",
43
+ textOverflow: "ellipsis",
44
+ whiteSpace: "nowrap",
45
+ }}
46
+ >
47
+ {icon && (
48
+ <span
49
+ style={{
50
+ display: "inline-flex",
51
+ color: "var(--af-text-tertiary)",
52
+ }}
53
+ >
54
+ {icon}
55
+ </span>
56
+ )}
57
+ <span
58
+ style={{
59
+ fontFamily: mono ? "var(--font-mono)" : "inherit",
60
+ overflow: "hidden",
61
+ textOverflow: "ellipsis",
62
+ whiteSpace: "nowrap",
63
+ }}
64
+ >
65
+ {value}
66
+ </span>
67
+ </span>
68
+ );
69
+ }
70
+
71
+ export function InlineStatDivider() {
72
+ return (
73
+ <span
74
+ style={{
75
+ color: "var(--af-text-tertiary)",
76
+ fontSize: 11,
77
+ opacity: 0.6,
78
+ }}
79
+ >
80
+ ·
81
+ </span>
82
+ );
83
+ }
84
+
85
+ export function EntrypointBadge({ entrypoint }: { entrypoint: string }) {
86
+ const isSdk = entrypoint.startsWith("sdk-");
87
+ const tone = isSdk
88
+ ? { bg: "rgba(245, 158, 11, 0.16)", fg: "#b45309" }
89
+ : { bg: "rgba(16, 185, 129, 0.16)", fg: "#047857" };
90
+ return (
91
+ <span
92
+ title={`entrypoint: ${entrypoint}`}
93
+ style={{
94
+ fontSize: 10.5,
95
+ padding: "2px 7px",
96
+ borderRadius: 4,
97
+ background: tone.bg,
98
+ color: tone.fg,
99
+ fontWeight: 600,
100
+ fontFamily: "var(--font-mono)",
101
+ letterSpacing: 0.2,
102
+ }}
103
+ >
104
+ {entrypoint}
105
+ </span>
106
+ );
107
+ }
108
+
109
+ export function useAnchoredTooltip() {
110
+ const ref = useRef<HTMLSpanElement>(null);
111
+ const [anchor, setAnchor] = useState<{ top: number; left: number } | null>(null);
112
+ const open = () => {
113
+ if (!ref.current) return;
114
+ const r = ref.current.getBoundingClientRect();
115
+ setAnchor({ top: r.bottom + 6, left: r.left });
116
+ };
117
+ const close = () => setAnchor(null);
118
+ return { ref, anchor, open, close };
119
+ }
120
+
121
+ export function AnchoredTooltip({
122
+ anchor,
123
+ width,
124
+ children,
125
+ }: {
126
+ anchor: { top: number; left: number };
127
+ width: number;
128
+ children: React.ReactNode;
129
+ }) {
130
+ return (
131
+ <div
132
+ style={{
133
+ position: "fixed",
134
+ top: anchor.top,
135
+ left: anchor.left,
136
+ zIndex: 1000,
137
+ background: "#1A1A1A",
138
+ color: "#F5F1EC",
139
+ padding: "8px 12px",
140
+ borderRadius: 6,
141
+ fontSize: 11,
142
+ fontFamily: "var(--font-mono)",
143
+ lineHeight: 1.5,
144
+ pointerEvents: "none",
145
+ boxShadow: "0 4px 16px rgba(15,23,42,0.24)",
146
+ width,
147
+ }}
148
+ >
149
+ {children}
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export function InlineTokenStat({ usage }: { usage: SessionEvent["usage"] }) {
155
+ const { ref, anchor, open, close } = useAnchoredTooltip();
156
+ const u = usage ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
157
+ const totalIn = u.input + u.cacheRead + u.cacheWrite;
158
+ const pctRead = totalIn > 0 ? Math.round((u.cacheRead / totalIn) * 100) : 0;
159
+ const cached = u.cacheRead + u.cacheWrite;
160
+
161
+ return (
162
+ <span
163
+ ref={ref}
164
+ style={{
165
+ display: "inline-flex",
166
+ alignItems: "center",
167
+ gap: 6,
168
+ fontSize: 12,
169
+ color: "var(--af-text-secondary)",
170
+ cursor: "default",
171
+ fontFamily: "var(--font-mono)",
172
+ }}
173
+ onMouseEnter={open}
174
+ onMouseLeave={close}
175
+ >
176
+ <span>
177
+ {formatTokens(u.input)}
178
+ <span style={{ color: "var(--af-text-tertiary)", margin: "0 3px" }}>in</span>
179
+ {formatTokens(u.output)}
180
+ <span style={{ color: "var(--af-text-tertiary)", marginLeft: 3 }}>out</span>
181
+ </span>
182
+ {cached > 0 && (
183
+ <span
184
+ style={{
185
+ fontSize: 10.5,
186
+ color: "var(--af-text-tertiary)",
187
+ paddingLeft: 6,
188
+ borderLeft: "1px solid var(--af-border-subtle)",
189
+ }}
190
+ >
191
+ +{formatTokens(cached)} cached
192
+ </span>
193
+ )}
194
+ {anchor && (
195
+ <AnchoredTooltip anchor={anchor} width={280}>
196
+ <TooltipRow label="Input (fresh)" value={u.input.toLocaleString()} />
197
+ <TooltipRow label="Output" value={u.output.toLocaleString()} />
198
+ <TooltipRow label="Cache read" value={`${u.cacheRead.toLocaleString()} (${pctRead}%)`} />
199
+ <TooltipRow label="Cache write" value={u.cacheWrite.toLocaleString()} />
200
+ <div
201
+ style={{
202
+ marginTop: 6,
203
+ paddingTop: 6,
204
+ borderTop: "1px solid rgba(241,245,249,0.12)",
205
+ opacity: 0.65,
206
+ fontSize: 10,
207
+ whiteSpace: "normal",
208
+ lineHeight: 1.4,
209
+ }}
210
+ >
211
+ Cache reads are cumulative across all API requests and billed at ~10% of regular input.
212
+ </div>
213
+ </AnchoredTooltip>
214
+ )}
215
+ </span>
216
+ );
217
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Pure (no-React) helpers for the session-view module.
3
+ *
4
+ * - `formatDayKey` — render a `YYYY-MM-DD` day key as a short, locale-free
5
+ * label (e.g. "Jun 16"). Formatted straight from the key — not a Date —
6
+ * so SSR and client output match regardless of timezone.
7
+ * - `flattenMegaRows` — turn the parser's MegaRow stream into a flat
8
+ * DisplayRow list, expanding turns whose firstPrimaryIndex is in the
9
+ * `expanded` set.
10
+ * - `allRowsAsRawRows` — fallback for "All events" filter mode. Wraps each
11
+ * raw event in a synthetic PresentationRow so TranscriptList can render
12
+ * attachments/meta/thinking/tool_result without going through the
13
+ * meaningful-transformation logic.
14
+ * - `formatDurationHeader` — concise ms→string for header stats; rolls up
15
+ * to "h m" once agent time exceeds an hour so workflow-fold totals stay
16
+ * readable.
17
+ */
18
+ import type {
19
+ MegaRow,
20
+ PresentationRow,
21
+ PresentationRowKind,
22
+ SessionEvent,
23
+ } from "@claude-lens/parser";
24
+ import type { DisplayRow } from "./types";
25
+
26
+ const DAY_MONTHS = [
27
+ "Jan",
28
+ "Feb",
29
+ "Mar",
30
+ "Apr",
31
+ "May",
32
+ "Jun",
33
+ "Jul",
34
+ "Aug",
35
+ "Sep",
36
+ "Oct",
37
+ "Nov",
38
+ "Dec",
39
+ ];
40
+
41
+ export function formatDayKey(key: string): string {
42
+ const [, m, d] = key.split("-").map(Number);
43
+ if (!m || !d) return key;
44
+ return `${DAY_MONTHS[m - 1]} ${d}`;
45
+ }
46
+
47
+ export function flattenMegaRows(
48
+ megaRows: MegaRow[],
49
+ expanded: Set<number>,
50
+ ): DisplayRow[] {
51
+ const out: DisplayRow[] = [];
52
+ for (const m of megaRows) {
53
+ if (m.kind === "turn") {
54
+ if (expanded.has(m.firstPrimaryIndex)) {
55
+ out.push({ kind: "turn-expanded-header", turn: m });
56
+ for (const r of m.rows) {
57
+ out.push({ kind: "presentation", row: r, indented: true });
58
+ }
59
+ out.push({ kind: "turn-expanded-footer", turn: m });
60
+ } else {
61
+ out.push({ kind: "turn-collapsed", turn: m });
62
+ }
63
+ } else {
64
+ out.push({ kind: "presentation", row: m });
65
+ }
66
+ }
67
+ return out;
68
+ }
69
+
70
+ export function allRowsAsRawRows(events: SessionEvent[]): PresentationRow[] {
71
+ const out: PresentationRow[] = [];
72
+ for (const e of events) {
73
+ let kind: PresentationRowKind;
74
+ switch (e.role) {
75
+ case "user":
76
+ kind = "user";
77
+ break;
78
+ case "agent":
79
+ case "agent-thinking":
80
+ kind = "agent";
81
+ break;
82
+ case "tool-call":
83
+ case "tool-result":
84
+ kind = "tool-group";
85
+ break;
86
+ case "system":
87
+ case "meta":
88
+ kind = "model";
89
+ break;
90
+ }
91
+ if (kind === "tool-group") {
92
+ out.push({
93
+ kind: "tool-group",
94
+ toolNames: [{ name: e.toolName ?? e.rawType, count: 1 }],
95
+ count: 1,
96
+ events: [e],
97
+ tOffsetMs: e.tOffsetMs,
98
+ gapMs: e.gapMs,
99
+ });
100
+ } else if (kind === "agent") {
101
+ out.push({
102
+ kind: "agent",
103
+ event: e,
104
+ groupedEvents: [e],
105
+ tOffsetMs: e.tOffsetMs,
106
+ gapMs: e.gapMs,
107
+ });
108
+ } else if (kind === "user") {
109
+ out.push({
110
+ kind: "user",
111
+ event: e,
112
+ tOffsetMs: e.tOffsetMs,
113
+ gapMs: e.gapMs,
114
+ });
115
+ } else {
116
+ out.push({
117
+ kind: "model",
118
+ event: e,
119
+ tOffsetMs: e.tOffsetMs,
120
+ gapMs: e.gapMs,
121
+ });
122
+ }
123
+ }
124
+ return out;
125
+ }
126
+
127
+ export function formatDurationHeader(ms: number): string {
128
+ if (ms < 1000) return `${ms}ms`;
129
+ const s = Math.round(ms / 1000);
130
+ if (s < 60) return `${s}s`;
131
+ const m = Math.floor(s / 60);
132
+ if (m < 60) return `${m}m ${s % 60}s`;
133
+ const h = Math.floor(m / 60);
134
+ return `${h}h ${m % 60}m`;
135
+ }