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,31 @@
1
+ /**
2
+ * Color-mapping utilities for the session-view minimap and workflow cards.
3
+ *
4
+ * - `subagentColor` — color a sub-agent lane by its declared type.
5
+ * `background=true` returns a brighter variant used for parallel
6
+ * (background) runs so they read as more "alive" than blocking
7
+ * subagents.
8
+ * - `workflowColor` — orange base for workflow lanes, modulated to red
9
+ * for failures and green for in-flight runs. Deliberately warm so
10
+ * workflow lanes are visually distinct from the cool subagent palette.
11
+ */
12
+
13
+ export function subagentColor(agentType: string, background?: boolean): string {
14
+ const palette: Record<string, [string, string]> = {
15
+ "general-purpose": ["#5C84C3", "#7BA3DC"],
16
+ Explore: ["#A855F7", "#C57BFF"],
17
+ Plan: ["#F59E0B", "#FFBD3D"],
18
+ "code-reviewer": ["#34D399", "#5EE5B0"],
19
+ "playwright-qa-verifier": ["#22D3EE", "#67E8F9"],
20
+ "claude-code-guide": ["#EC4899", "#F472B6"],
21
+ };
22
+ const [base, bright] = palette[agentType] ?? ["#8A8580", "#A8A19A"];
23
+ return background ? bright : base;
24
+ }
25
+
26
+ export function workflowColor(status: string): string {
27
+ const s = status.toLowerCase();
28
+ if (s === "failed" || s === "aborted" || s === "error") return "#EF4444";
29
+ if (s === "running" || s === "in_progress" || s === "active") return "#10B981";
30
+ return "#EA580C";
31
+ }
@@ -0,0 +1,78 @@
1
+ "use client";
2
+
3
+ /**
4
+ * DebugList — renders the Log tab's raw event stream as a list of
5
+ * collapsible JSON entries.
6
+ *
7
+ * Notes:
8
+ * - We rebuild a JSON view from the structured fields on the event.
9
+ * The original `raw` field is stripped server-side before
10
+ * serialization (see app/sessions/[id]/page.tsx) because including
11
+ * the full JSONL line per event doubles the RSC payload on large
12
+ * sessions. All the useful data is already on the structured event;
13
+ * this view surfaces it in the same shape you'd get from the raw
14
+ * JSONL.
15
+ */
16
+ import React from "react";
17
+ import type { SessionEvent } from "@claude-lens/parser";
18
+
19
+ export function DebugList({ events }: { events: SessionEvent[] }) {
20
+ const shapeForDebug = (e: SessionEvent) => ({
21
+ type: e.rawType,
22
+ index: e.index,
23
+ uuid: e.uuid,
24
+ parentUuid: e.parentUuid,
25
+ timestamp: e.timestamp,
26
+ role: e.role,
27
+ messageId: e.messageId,
28
+ stopReason: e.stopReason,
29
+ model: e.model,
30
+ requestId: e.requestId,
31
+ toolName: e.toolName,
32
+ toolUseId: e.toolUseId,
33
+ attachmentType: e.attachmentType,
34
+ usage: e.usage,
35
+ blocks: e.blocks,
36
+ });
37
+
38
+ return (
39
+ <div style={{ padding: "8px 0" }}>
40
+ {events.map((e) => (
41
+ <details
42
+ key={e.index}
43
+ style={{
44
+ borderBottom: "1px solid var(--af-border-subtle)",
45
+ padding: "8px 12px",
46
+ }}
47
+ >
48
+ <summary
49
+ style={{
50
+ cursor: "pointer",
51
+ fontFamily: "var(--font-mono)",
52
+ fontSize: 12,
53
+ color: "var(--af-text-secondary)",
54
+ }}
55
+ >
56
+ #{e.index} · {e.rawType}
57
+ {e.attachmentType ? `/${e.attachmentType}` : ""} · {e.timestamp ?? "(no ts)"}
58
+ </summary>
59
+ <pre
60
+ style={{
61
+ marginTop: 8,
62
+ padding: 12,
63
+ background: "var(--background)",
64
+ border: "1px solid var(--af-border-subtle)",
65
+ borderRadius: 6,
66
+ fontSize: 11,
67
+ overflow: "auto",
68
+ maxHeight: 400,
69
+ color: "var(--af-text-secondary)",
70
+ }}
71
+ >
72
+ {JSON.stringify(shapeForDebug(e), null, 2)}
73
+ </pre>
74
+ </details>
75
+ ))}
76
+ </div>
77
+ );
78
+ }
@@ -0,0 +1,414 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Drawer + DrawerContent + BlockView — right-side event-inspector panel
5
+ * that opens when a transcript row is clicked. Renders the event's
6
+ * title bar, compact meta line, optional developer panel (model, request
7
+ * id, full usage breakdown), and the event's content blocks via
8
+ * BlockView.
9
+ *
10
+ * DrawerContent dispatches on row.kind so user rows can substitute their
11
+ * raw blocks for a cleaner `displayBlocks` (e.g. slash commands render
12
+ * "/implement AGE-8" instead of the raw XML), task-notification rows
13
+ * get a parsed-fields layout, and multi-call tool-groups list each call
14
+ * individually with offset + tool-use-id headers.
15
+ *
16
+ * BlockView is the leaf renderer: markdown text via ReactMarkdown +
17
+ * remarkGfm, thinking blocks as collapsible details, tool_use as
18
+ * ToolUseCard, tool_result as a pre-formatted code block.
19
+ */
20
+ import React, { useState } from "react";
21
+ import { X } from "lucide-react";
22
+ import ReactMarkdown from "react-markdown";
23
+ import remarkGfm from "remark-gfm";
24
+ import type {
25
+ ContentBlock,
26
+ PresentationRow,
27
+ PresentationRowKind,
28
+ SessionEvent,
29
+ } from "@claude-lens/parser";
30
+ import { formatGap, formatOffset, formatTokens } from "@/lib/format";
31
+ import { ROLE_THEMES, formatToolSummary } from "../turn-steps";
32
+ import { ToolUseCard } from "../tool-cards";
33
+
34
+ export function Drawer({
35
+ event,
36
+ row,
37
+ onClose,
38
+ }: {
39
+ event: SessionEvent;
40
+ row: PresentationRow | null;
41
+ onClose: () => void;
42
+ }) {
43
+ const [showDev, setShowDev] = useState(false);
44
+ const kind: PresentationRowKind = row?.kind ?? "agent";
45
+ const theme = ROLE_THEMES[kind];
46
+ const title = drawerTitle(row, event);
47
+ const hasUsage = !!event.usage && (event.usage.input > 0 || event.usage.output > 0);
48
+
49
+ return (
50
+ <div>
51
+ {/* Sticky title bar */}
52
+ <div
53
+ style={{
54
+ padding: "14px 20px",
55
+ borderBottom: "1px solid var(--af-border-subtle)",
56
+ display: "flex",
57
+ alignItems: "center",
58
+ gap: 10,
59
+ background: "var(--af-surface)",
60
+ position: "sticky",
61
+ top: 0,
62
+ zIndex: 1,
63
+ }}
64
+ >
65
+ <span
66
+ style={{
67
+ fontSize: 11,
68
+ fontWeight: 600,
69
+ padding: "3px 10px",
70
+ borderRadius: 4,
71
+ background: theme.bg,
72
+ color: theme.fg,
73
+ }}
74
+ >
75
+ {theme.label}
76
+ </span>
77
+ <span
78
+ style={{
79
+ fontSize: 14,
80
+ fontWeight: 600,
81
+ color: "var(--af-text)",
82
+ }}
83
+ >
84
+ {title}
85
+ </span>
86
+ <button
87
+ onClick={onClose}
88
+ style={{
89
+ marginLeft: "auto",
90
+ background: "transparent",
91
+ border: "none",
92
+ cursor: "pointer",
93
+ color: "var(--af-text-tertiary)",
94
+ padding: 4,
95
+ borderRadius: 4,
96
+ }}
97
+ >
98
+ <X size={16} />
99
+ </button>
100
+ </div>
101
+
102
+ {/* Meta line — compact */}
103
+ <div
104
+ style={{
105
+ padding: "8px 20px",
106
+ fontFamily: "var(--font-mono)",
107
+ fontSize: 11,
108
+ color: "var(--af-text-tertiary)",
109
+ display: "flex",
110
+ gap: 10,
111
+ alignItems: "center",
112
+ flexWrap: "wrap",
113
+ borderBottom: "1px solid var(--af-border-subtle)",
114
+ }}
115
+ >
116
+ <span>{formatOffset(event.tOffsetMs)}</span>
117
+ {event.gapMs !== undefined && event.gapMs > 0 && <span>· {formatGap(event.gapMs)}</span>}
118
+ {hasUsage && event.usage && (
119
+ <span style={{ color: "var(--af-text-secondary)" }}>
120
+ · {formatTokens(event.usage.input + event.usage.cacheRead + event.usage.cacheWrite)}/
121
+ {formatTokens(event.usage.output)} tokens
122
+ </span>
123
+ )}
124
+ <button
125
+ onClick={() => setShowDev((s) => !s)}
126
+ style={{
127
+ marginLeft: "auto",
128
+ background: "transparent",
129
+ border: "none",
130
+ fontSize: 10,
131
+ color: "var(--af-text-tertiary)",
132
+ cursor: "pointer",
133
+ padding: 0,
134
+ fontFamily: "inherit",
135
+ textDecoration: "underline",
136
+ textUnderlineOffset: 2,
137
+ }}
138
+ >
139
+ {showDev ? "hide details" : "details"}
140
+ </button>
141
+ </div>
142
+
143
+ {/* Developer panel — collapsed by default */}
144
+ {showDev && (
145
+ <div
146
+ style={{
147
+ padding: "10px 20px 14px",
148
+ borderBottom: "1px solid var(--af-border-subtle)",
149
+ fontSize: 11,
150
+ color: "var(--af-text-secondary)",
151
+ fontFamily: "var(--font-mono)",
152
+ display: "flex",
153
+ flexDirection: "column",
154
+ gap: 4,
155
+ }}
156
+ >
157
+ {event.model && <div>model: {event.model}</div>}
158
+ {event.requestId && <div>request: {event.requestId}</div>}
159
+ {event.messageId && <div>message: {event.messageId}</div>}
160
+ {event.stopReason && <div>stop_reason: {event.stopReason}</div>}
161
+ {event.usage && (
162
+ <>
163
+ <div style={{ marginTop: 6, opacity: 0.7 }}>tokens</div>
164
+ <div> input: {event.usage.input.toLocaleString()}</div>
165
+ <div> output: {event.usage.output.toLocaleString()}</div>
166
+ <div> cache read: {event.usage.cacheRead.toLocaleString()}</div>
167
+ <div>
168
+ {" "}cache write: {event.usage.cacheWrite.toLocaleString()}
169
+ </div>
170
+ </>
171
+ )}
172
+ </div>
173
+ )}
174
+
175
+ {/* Content */}
176
+ <div style={{ padding: "18px 22px" }}>
177
+ <DrawerContent event={event} row={row} />
178
+ </div>
179
+ </div>
180
+ );
181
+ }
182
+
183
+ export function drawerTitle(row: PresentationRow | null, event: SessionEvent): string {
184
+ if (row) {
185
+ switch (row.kind) {
186
+ case "user":
187
+ return "Message";
188
+ case "agent":
189
+ return "Message";
190
+ case "tool-group":
191
+ return `Tool use · ${formatToolSummary(row.toolNames)}`;
192
+ case "interrupt":
193
+ return "Interrupted";
194
+ case "model":
195
+ return "Model (zero-usage)";
196
+ case "error":
197
+ return "API error";
198
+ case "task-notification":
199
+ return `Background task · ${row.status}`;
200
+ }
201
+ }
202
+ return event.rawType;
203
+ }
204
+
205
+ export function DrawerContent({
206
+ event,
207
+ row,
208
+ }: {
209
+ event: SessionEvent;
210
+ row: PresentationRow | null;
211
+ }) {
212
+ // User rows can override their blocks (e.g. slash commands get a cleaned
213
+ // "/implement AGE-8" block instead of the raw XML).
214
+ if (row?.kind === "user" && row.displayBlocks) {
215
+ return (
216
+ <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
217
+ {row.displayBlocks.map((b, i) => (
218
+ <BlockView key={i} block={b} />
219
+ ))}
220
+ </div>
221
+ );
222
+ }
223
+
224
+ // Task notifications: parsed fields in a clean key-value layout,
225
+ // not the raw <task-notification> XML blob.
226
+ if (row?.kind === "task-notification") {
227
+ const statusColor =
228
+ row.status === "success"
229
+ ? "var(--af-success)"
230
+ : row.status === "failed"
231
+ ? "var(--af-danger)"
232
+ : "var(--af-text-secondary)";
233
+ return (
234
+ <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
235
+ <div
236
+ style={{
237
+ display: "flex",
238
+ alignItems: "center",
239
+ gap: 8,
240
+ }}
241
+ >
242
+ <span
243
+ style={{
244
+ fontSize: 11,
245
+ fontWeight: 600,
246
+ padding: "3px 10px",
247
+ borderRadius: 4,
248
+ background:
249
+ row.status === "success"
250
+ ? "var(--af-success-subtle)"
251
+ : row.status === "failed"
252
+ ? "var(--af-danger-subtle)"
253
+ : "var(--af-border-subtle)",
254
+ color: statusColor,
255
+ textTransform: "uppercase",
256
+ letterSpacing: "0.03em",
257
+ }}
258
+ >
259
+ {row.status}
260
+ </span>
261
+ </div>
262
+ <div style={{ fontSize: 13, lineHeight: 1.55, color: "var(--af-text)" }}>{row.summary}</div>
263
+ {(row.taskId || row.toolUseId || row.outputFile) && (
264
+ <div
265
+ style={{
266
+ fontSize: 11,
267
+ color: "var(--af-text-secondary)",
268
+ fontFamily: "var(--font-mono)",
269
+ display: "flex",
270
+ flexDirection: "column",
271
+ gap: 4,
272
+ paddingTop: 10,
273
+ borderTop: "1px solid var(--af-border-subtle)",
274
+ }}
275
+ >
276
+ {row.taskId && <div>task id: {row.taskId}</div>}
277
+ {row.toolUseId && <div>tool use: {row.toolUseId.slice(0, 24)}…</div>}
278
+ {row.outputFile && (
279
+ <div style={{ wordBreak: "break-all" }}>output: {row.outputFile}</div>
280
+ )}
281
+ </div>
282
+ )}
283
+ </div>
284
+ );
285
+ }
286
+
287
+ // Tool group: list all individual tool calls with their inputs
288
+ if (row?.kind === "tool-group" && row.count > 1) {
289
+ return (
290
+ <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
291
+ {row.events.map((e, i) => (
292
+ <div key={e.index}>
293
+ <div
294
+ style={{
295
+ fontSize: 11,
296
+ color: "var(--af-text-tertiary)",
297
+ marginBottom: 4,
298
+ fontFamily: "var(--font-mono)",
299
+ }}
300
+ >
301
+ #{i + 1} · {formatOffset(e.tOffsetMs)} · {e.toolUseId?.slice(0, 14)}…
302
+ </div>
303
+ {e.blocks.map((b, bi) => (
304
+ <BlockView key={bi} block={b} />
305
+ ))}
306
+ </div>
307
+ ))}
308
+ </div>
309
+ );
310
+ }
311
+
312
+ // Agent row: include thinking blocks from the same message.id if present
313
+ if (row?.kind === "agent" && row.groupedEvents.length > 1) {
314
+ return (
315
+ <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
316
+ {row.groupedEvents.flatMap((e, i) =>
317
+ e.blocks.map((b, bi) => <BlockView key={`${i}-${bi}`} block={b} />),
318
+ )}
319
+ </div>
320
+ );
321
+ }
322
+
323
+ return (
324
+ <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
325
+ {event.blocks.map((b, i) => (
326
+ <BlockView key={i} block={b} />
327
+ ))}
328
+ </div>
329
+ );
330
+ }
331
+
332
+ export function BlockView({ block }: { block: ContentBlock }) {
333
+ if (block.type === "text") {
334
+ return (
335
+ <div className="sl-prose">
336
+ <ReactMarkdown
337
+ remarkPlugins={[remarkGfm]}
338
+ components={{
339
+ a: (props) => (
340
+ <a
341
+ {...props}
342
+ target="_blank"
343
+ rel="noopener noreferrer"
344
+ style={{
345
+ color: "var(--af-accent)",
346
+ textDecoration: "underline",
347
+ textUnderlineOffset: 2,
348
+ }}
349
+ />
350
+ ),
351
+ }}
352
+ >
353
+ {block.text}
354
+ </ReactMarkdown>
355
+ </div>
356
+ );
357
+ }
358
+ if (block.type === "thinking") {
359
+ return (
360
+ <details>
361
+ <summary
362
+ style={{
363
+ cursor: "pointer",
364
+ fontSize: 11,
365
+ color: "var(--af-text-tertiary)",
366
+ marginBottom: 6,
367
+ }}
368
+ >
369
+ Thinking · {block.thinking.length} chars
370
+ </summary>
371
+ <div
372
+ style={{
373
+ fontSize: 12,
374
+ lineHeight: 1.6,
375
+ color: "var(--af-text-secondary)",
376
+ whiteSpace: "pre-wrap",
377
+ fontStyle: "italic",
378
+ borderLeft: "3px solid var(--af-border-subtle)",
379
+ paddingLeft: 12,
380
+ marginTop: 6,
381
+ }}
382
+ >
383
+ {block.thinking}
384
+ </div>
385
+ </details>
386
+ );
387
+ }
388
+ if (block.type === "tool_use") {
389
+ return <ToolUseCard name={block.name} input={block.input} />;
390
+ }
391
+ if (block.type === "tool_result") {
392
+ const text =
393
+ typeof block.content === "string" ? block.content : JSON.stringify(block.content, null, 2);
394
+ return (
395
+ <pre
396
+ style={{
397
+ fontSize: 12,
398
+ padding: 12,
399
+ background: "var(--background)",
400
+ border: "1px solid var(--af-border-subtle)",
401
+ borderRadius: 6,
402
+ overflow: "auto",
403
+ whiteSpace: "pre-wrap",
404
+ color: "var(--af-text)",
405
+ fontFamily: "var(--font-mono)",
406
+ maxHeight: 500,
407
+ }}
408
+ >
409
+ {text}
410
+ </pre>
411
+ );
412
+ }
413
+ return null;
414
+ }
@@ -0,0 +1,124 @@
1
+ "use client";
2
+
3
+ /**
4
+ * EntryDayStrip — interleaved daily digest card shown at the top of each
5
+ * day's slice of the transcript. Summarizes outcome, active minutes,
6
+ * brief summary, friction detail, and top user instructions for that
7
+ * local day. Trivial/pending states render simpler placeholders with a
8
+ * call-to-action.
9
+ */
10
+ import Link from "next/link";
11
+ import React from "react";
12
+ import type { Entry } from "@claude-lens/entries";
13
+ import { OutcomePill } from "@/components/outcome-pill";
14
+
15
+ export function EntryDayStrip({ entry }: { entry: Entry }) {
16
+ const fmtDate = new Date(`${entry.local_day}T12:00:00`).toLocaleDateString("en-US", {
17
+ weekday: "short", month: "short", day: "numeric",
18
+ });
19
+ const durMin = Math.round(entry.numbers.active_min);
20
+ const enr = entry.enrichment;
21
+ const isPending = enr.status === "pending" || enr.status === "error";
22
+ const isTrivial = enr.status === "skipped_trivial";
23
+
24
+ return (
25
+ <div
26
+ style={{
27
+ border: "1px solid var(--af-border-subtle)",
28
+ borderRadius: 10,
29
+ padding: "12px 16px",
30
+ background: "var(--af-surface)",
31
+ fontSize: 13,
32
+ lineHeight: 1.5,
33
+ }}
34
+ >
35
+ <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
36
+ <span
37
+ style={{
38
+ fontSize: 12,
39
+ color: "var(--af-text-tertiary)",
40
+ display: "inline-flex",
41
+ alignItems: "center",
42
+ gap: 4,
43
+ }}
44
+ >
45
+ 📅 {fmtDate}
46
+ </span>
47
+ {isTrivial ? (
48
+ <OutcomePill outcome="trivial" size="md" agent={entry.agent} />
49
+ ) : isPending ? (
50
+ <OutcomePill
51
+ outcome={null}
52
+ pending
53
+ sessionId={entry.session_id}
54
+ localDay={entry.local_day}
55
+ size="md"
56
+ agent={entry.agent}
57
+ />
58
+ ) : enr.outcome ? (
59
+ <OutcomePill outcome={enr.outcome} size="md" agent={entry.agent} />
60
+ ) : null}
61
+ <span
62
+ style={{
63
+ fontFamily: "var(--font-mono)",
64
+ fontSize: 11,
65
+ color: "var(--af-text-tertiary)",
66
+ marginLeft: "auto",
67
+ }}
68
+ >
69
+ {durMin}m active
70
+ </span>
71
+ </div>
72
+
73
+ {isTrivial && (
74
+ <div style={{ fontSize: 12, color: "var(--af-text-tertiary)", fontStyle: "italic", marginTop: 8 }}>
75
+ Warmup — no enrichment generated.
76
+ </div>
77
+ )}
78
+
79
+ {isPending && (
80
+ <div style={{ fontSize: 12, color: "var(--af-text-secondary)", marginTop: 8 }}>
81
+ Click the pending pill to generate this day's digest →
82
+ </div>
83
+ )}
84
+
85
+ {enr.status === "done" && (
86
+ <>
87
+ {enr.brief_summary && (
88
+ <p style={{ margin: "10px 0 0", color: "var(--af-text)" }}>{enr.brief_summary}</p>
89
+ )}
90
+ {(enr.friction_detail || enr.user_instructions.length > 0) && (
91
+ <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 6 }}>
92
+ {enr.friction_detail && (
93
+ <div style={{ fontSize: 12, color: "var(--af-text-secondary)" }}>
94
+ <span style={{ color: "#ed8936" }}>⚠ </span>
95
+ {enr.friction_detail}
96
+ </div>
97
+ )}
98
+ {enr.user_instructions.length > 0 && (
99
+ <details style={{ fontSize: 12, color: "var(--af-text-secondary)" }}>
100
+ <summary style={{ cursor: "pointer", color: "var(--af-text-tertiary)" }}>
101
+ Top user instructions ({enr.user_instructions.length})
102
+ </summary>
103
+ <ul style={{ margin: "6px 0 0", paddingLeft: 18 }}>
104
+ {enr.user_instructions.slice(0, 5).map((u, i) => (
105
+ <li key={i} style={{ marginBottom: 2 }}>{u}</li>
106
+ ))}
107
+ </ul>
108
+ </details>
109
+ )}
110
+ </div>
111
+ )}
112
+ <div style={{ marginTop: 10, fontSize: 11 }}>
113
+ <Link
114
+ href={`/digest/${entry.local_day}`}
115
+ style={{ color: "var(--af-accent)", textDecoration: "none" }}
116
+ >
117
+ Open day digest →
118
+ </Link>
119
+ </div>
120
+ </>
121
+ )}
122
+ </div>
123
+ );
124
+ }