@ruimte/agents-react 0.0.0 → 0.7.0

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 (312) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +142 -1
  3. package/dist/agents/AccountDot.d.ts +4 -0
  4. package/dist/agents/AccountDot.js +8 -0
  5. package/dist/agents/AgentIcon.d.ts +6 -0
  6. package/dist/agents/AgentIcon.js +20 -0
  7. package/dist/agents/ProviderLogo.d.ts +8 -0
  8. package/dist/agents/ProviderLogo.js +8 -0
  9. package/dist/agents/account-limits.d.ts +7 -0
  10. package/dist/agents/account-limits.js +26 -0
  11. package/dist/agents/accounts.d.ts +22 -0
  12. package/dist/agents/accounts.js +103 -0
  13. package/dist/agents/model-name.d.ts +6 -0
  14. package/dist/agents/model-name.js +44 -0
  15. package/dist/agents/provider-paths.d.ts +2 -0
  16. package/dist/agents/provider-paths.js +8 -0
  17. package/dist/agents/status-look.d.ts +13 -0
  18. package/dist/agents/status-look.js +22 -0
  19. package/dist/chat/account-choice.d.ts +11 -0
  20. package/dist/chat/account-choice.js +30 -0
  21. package/dist/chat/actions.d.ts +6 -0
  22. package/dist/chat/actions.js +64 -0
  23. package/dist/chat/activity.d.ts +5 -0
  24. package/dist/chat/activity.js +4 -0
  25. package/dist/chat/attachments.d.ts +23 -0
  26. package/dist/chat/attachments.js +70 -0
  27. package/dist/chat/bookmarks.d.ts +14 -0
  28. package/dist/chat/bookmarks.js +73 -0
  29. package/dist/chat/chat-client.d.ts +53 -0
  30. package/dist/chat/chat-client.js +232 -0
  31. package/dist/chat/chat-references.d.ts +5 -0
  32. package/dist/chat/chat-references.js +5 -0
  33. package/dist/chat/drafts.d.ts +29 -0
  34. package/dist/chat/drafts.js +104 -0
  35. package/dist/chat/forks.d.ts +2 -0
  36. package/dist/chat/forks.js +26 -0
  37. package/dist/chat/guards.d.ts +12 -0
  38. package/dist/chat/guards.js +63 -0
  39. package/dist/chat/logic/answers.d.ts +3 -0
  40. package/dist/chat/logic/answers.js +14 -0
  41. package/dist/chat/logic/bookmarks.d.ts +5 -0
  42. package/dist/chat/logic/bookmarks.js +31 -0
  43. package/dist/chat/logic/context-usage.d.ts +12 -0
  44. package/dist/chat/logic/context-usage.js +20 -0
  45. package/dist/chat/logic/fork.d.ts +44 -0
  46. package/dist/chat/logic/fork.js +138 -0
  47. package/dist/chat/logic/handback.d.ts +2 -0
  48. package/dist/chat/logic/handback.js +8 -0
  49. package/dist/chat/logic/json.d.ts +1 -0
  50. package/dist/chat/logic/json.js +2 -0
  51. package/dist/chat/logic/limit.d.ts +7 -0
  52. package/dist/chat/logic/limit.js +34 -0
  53. package/dist/chat/logic/resume-compaction.d.ts +14 -0
  54. package/dist/chat/logic/resume-compaction.js +47 -0
  55. package/dist/chat/logic/scrubber.d.ts +50 -0
  56. package/dist/chat/logic/scrubber.js +216 -0
  57. package/dist/chat/logic/timeline-copy.d.ts +4 -0
  58. package/dist/chat/logic/timeline-copy.js +51 -0
  59. package/dist/chat/logic/timeline-target.d.ts +12 -0
  60. package/dist/chat/logic/timeline-target.js +34 -0
  61. package/dist/chat/logic/timeline.d.ts +110 -0
  62. package/dist/chat/logic/timeline.js +339 -0
  63. package/dist/chat/logic/tool-catalog.d.ts +10 -0
  64. package/dist/chat/logic/tool-catalog.js +40 -0
  65. package/dist/chat/logic/tools.d.ts +15 -0
  66. package/dist/chat/logic/tools.js +79 -0
  67. package/dist/chat/logic/workflow.d.ts +14 -0
  68. package/dist/chat/logic/workflow.js +47 -0
  69. package/dist/chat/mentions.d.ts +46 -0
  70. package/dist/chat/mentions.js +128 -0
  71. package/dist/chat/persisted-json.d.ts +5 -0
  72. package/dist/chat/persisted-json.js +24 -0
  73. package/dist/chat/preferences.d.ts +23 -0
  74. package/dist/chat/preferences.js +110 -0
  75. package/dist/chat/quote.d.ts +2 -0
  76. package/dist/chat/quote.js +25 -0
  77. package/dist/chat/recent-messages.d.ts +17 -0
  78. package/dist/chat/recent-messages.js +28 -0
  79. package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
  80. package/dist/chat/resume-compaction-dismissals.js +14 -0
  81. package/dist/chat/runtime-modes.d.ts +4 -0
  82. package/dist/chat/runtime-modes.js +11 -0
  83. package/dist/chat/stash.d.ts +24 -0
  84. package/dist/chat/stash.js +88 -0
  85. package/dist/chat/subagent-conversation.d.ts +38 -0
  86. package/dist/chat/subagent-conversation.js +150 -0
  87. package/dist/chat/subagent-list.d.ts +16 -0
  88. package/dist/chat/subagent-list.js +76 -0
  89. package/dist/chat/subagent-support.d.ts +6 -0
  90. package/dist/chat/subagent-support.js +14 -0
  91. package/dist/chat/subagent-view.d.ts +33 -0
  92. package/dist/chat/subagent-view.js +65 -0
  93. package/dist/chat/thumbnails.d.ts +10 -0
  94. package/dist/chat/thumbnails.js +87 -0
  95. package/dist/chat/timeline-scroll.d.ts +16 -0
  96. package/dist/chat/timeline-scroll.js +100 -0
  97. package/dist/chat/ui/AccountPill.d.ts +3 -0
  98. package/dist/chat/ui/AccountPill.js +24 -0
  99. package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
  100. package/dist/chat/ui/BookmarkMarker.js +59 -0
  101. package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
  102. package/dist/chat/ui/BookmarkSubmenu.js +44 -0
  103. package/dist/chat/ui/ChatActivity.d.ts +3 -0
  104. package/dist/chat/ui/ChatActivity.js +87 -0
  105. package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
  106. package/dist/chat/ui/ChatReferenceChip.js +17 -0
  107. package/dist/chat/ui/CodeBlock.d.ts +4 -0
  108. package/dist/chat/ui/CodeBlock.js +130 -0
  109. package/dist/chat/ui/Composer.d.ts +14 -0
  110. package/dist/chat/ui/Composer.js +821 -0
  111. package/dist/chat/ui/ComposerInput.d.ts +28 -0
  112. package/dist/chat/ui/ComposerInput.js +118 -0
  113. package/dist/chat/ui/DiffPool.d.ts +5 -0
  114. package/dist/chat/ui/DiffPool.js +26 -0
  115. package/dist/chat/ui/EditDiff.d.ts +4 -0
  116. package/dist/chat/ui/EditDiff.js +21 -0
  117. package/dist/chat/ui/FadingWords.d.ts +3 -0
  118. package/dist/chat/ui/FadingWords.js +5 -0
  119. package/dist/chat/ui/ImageView.d.ts +6 -0
  120. package/dist/chat/ui/ImageView.js +85 -0
  121. package/dist/chat/ui/LimitState.d.ts +8 -0
  122. package/dist/chat/ui/LimitState.js +117 -0
  123. package/dist/chat/ui/Markdown.d.ts +18 -0
  124. package/dist/chat/ui/Markdown.js +158 -0
  125. package/dist/chat/ui/MessageActions.d.ts +9 -0
  126. package/dist/chat/ui/MessageActions.js +56 -0
  127. package/dist/chat/ui/Pickers.d.ts +15 -0
  128. package/dist/chat/ui/Pickers.js +110 -0
  129. package/dist/chat/ui/PromptComposer.d.ts +13 -0
  130. package/dist/chat/ui/PromptComposer.js +48 -0
  131. package/dist/chat/ui/QuoteButton.d.ts +7 -0
  132. package/dist/chat/ui/QuoteButton.js +76 -0
  133. package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
  134. package/dist/chat/ui/ResumeCompactionDock.js +15 -0
  135. package/dist/chat/ui/RunSettings.d.ts +25 -0
  136. package/dist/chat/ui/RunSettings.js +148 -0
  137. package/dist/chat/ui/Scrubber.d.ts +17 -0
  138. package/dist/chat/ui/Scrubber.js +121 -0
  139. package/dist/chat/ui/SubagentControls.d.ts +11 -0
  140. package/dist/chat/ui/SubagentControls.js +37 -0
  141. package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
  142. package/dist/chat/ui/SubagentStopButton.js +36 -0
  143. package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
  144. package/dist/chat/ui/SubagentTimeline.js +92 -0
  145. package/dist/chat/ui/Timeline.d.ts +5 -0
  146. package/dist/chat/ui/Timeline.js +318 -0
  147. package/dist/chat/ui/TimelineMenu.d.ts +12 -0
  148. package/dist/chat/ui/TimelineMenu.js +61 -0
  149. package/dist/chat/ui/UnifiedDiff.d.ts +8 -0
  150. package/dist/chat/ui/UnifiedDiff.js +51 -0
  151. package/dist/chat/ui/UploadThumb.d.ts +4 -0
  152. package/dist/chat/ui/UploadThumb.js +11 -0
  153. package/dist/chat/ui/chips.d.ts +4 -0
  154. package/dist/chat/ui/chips.js +11 -0
  155. package/dist/chat/ui/code-lines.d.ts +18 -0
  156. package/dist/chat/ui/code-lines.js +46 -0
  157. package/dist/chat/ui/code-streaming.d.ts +1 -0
  158. package/dist/chat/ui/code-streaming.js +3 -0
  159. package/dist/chat/ui/code-theme.d.ts +3 -0
  160. package/dist/chat/ui/code-theme.js +10 -0
  161. package/dist/chat/ui/composer/chips.d.ts +6 -0
  162. package/dist/chat/ui/composer/chips.js +29 -0
  163. package/dist/chat/ui/composer/editor.d.ts +11 -0
  164. package/dist/chat/ui/composer/editor.js +94 -0
  165. package/dist/chat/ui/composer/keys.d.ts +30 -0
  166. package/dist/chat/ui/composer/keys.js +98 -0
  167. package/dist/chat/ui/diff-theme.d.ts +2 -0
  168. package/dist/chat/ui/diff-theme.js +20 -0
  169. package/dist/chat/ui/file-links.d.ts +5 -0
  170. package/dist/chat/ui/file-links.js +18 -0
  171. package/dist/chat/ui/find-reveal.d.ts +7 -0
  172. package/dist/chat/ui/find-reveal.js +13 -0
  173. package/dist/chat/ui/icons.d.ts +4 -0
  174. package/dist/chat/ui/icons.js +20 -0
  175. package/dist/chat/ui/markdown-blocks.d.ts +6 -0
  176. package/dist/chat/ui/markdown-blocks.js +101 -0
  177. package/dist/chat/ui/quote-selection.d.ts +3 -0
  178. package/dist/chat/ui/quote-selection.js +44 -0
  179. package/dist/chat/ui/rehype-chips.d.ts +23 -0
  180. package/dist/chat/ui/rehype-chips.js +28 -0
  181. package/dist/chat/ui/rehype-fade.d.ts +23 -0
  182. package/dist/chat/ui/rehype-fade.js +30 -0
  183. package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
  184. package/dist/chat/ui/remark-html-as-text.js +24 -0
  185. package/dist/chat/ui/reveal.d.ts +13 -0
  186. package/dist/chat/ui/reveal.js +112 -0
  187. package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
  188. package/dist/chat/ui/rows/ForksRow.js +40 -0
  189. package/dist/chat/ui/rows/MessageRows.d.ts +36 -0
  190. package/dist/chat/ui/rows/MessageRows.js +190 -0
  191. package/dist/chat/ui/rows/Rows.d.ts +12 -0
  192. package/dist/chat/ui/rows/Rows.js +49 -0
  193. package/dist/chat/ui/rows/SubagentRow.d.ts +15 -0
  194. package/dist/chat/ui/rows/SubagentRow.js +98 -0
  195. package/dist/chat/ui/rows/WorkRows.d.ts +46 -0
  196. package/dist/chat/ui/rows/WorkRows.js +143 -0
  197. package/dist/chat/ui/rows/WorkflowRow.d.ts +7 -0
  198. package/dist/chat/ui/rows/WorkflowRow.js +37 -0
  199. package/dist/chat/ui/rows/row-rhythm.d.ts +3 -0
  200. package/dist/chat/ui/rows/row-rhythm.js +14 -0
  201. package/dist/chat/ui/useToggleSet.d.ts +6 -0
  202. package/dist/chat/ui/useToggleSet.js +25 -0
  203. package/dist/host.d.ts +167 -0
  204. package/dist/host.js +41 -0
  205. package/dist/lazy.d.ts +5 -0
  206. package/dist/lazy.js +35 -0
  207. package/dist/locales/en/agent-chat.json +417 -0
  208. package/dist/locales/en/agent-prompts.json +42 -0
  209. package/dist/locales/en/agent-providers.json +131 -0
  210. package/dist/locales/en/agent-usage.json +131 -0
  211. package/dist/locales/nl/agent-chat.json +417 -0
  212. package/dist/locales/nl/agent-prompts.json +42 -0
  213. package/dist/locales/nl/agent-providers.json +131 -0
  214. package/dist/locales/nl/agent-usage.json +131 -0
  215. package/dist/locales.d.ts +5 -0
  216. package/dist/locales.js +18 -0
  217. package/dist/mounted-registry.d.ts +7 -0
  218. package/dist/mounted-registry.js +39 -0
  219. package/dist/port-transport.d.ts +6 -0
  220. package/dist/port-transport.js +120 -0
  221. package/dist/prompts/logic/focus.d.ts +1 -0
  222. package/dist/prompts/logic/focus.js +17 -0
  223. package/dist/prompts/logic/keys.d.ts +42 -0
  224. package/dist/prompts/logic/keys.js +84 -0
  225. package/dist/prompts/logic/prompts.d.ts +34 -0
  226. package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
  227. package/dist/prompts/logic/prompts.fixtures.js +73 -0
  228. package/dist/prompts/logic/prompts.js +22 -0
  229. package/dist/prompts/logic/subjects.d.ts +49 -0
  230. package/dist/prompts/logic/subjects.js +56 -0
  231. package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
  232. package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
  233. package/dist/prompts/logic/usePromptSession.d.ts +24 -0
  234. package/dist/prompts/logic/usePromptSession.js +87 -0
  235. package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
  236. package/dist/prompts/ui/ApprovalBody.js +35 -0
  237. package/dist/prompts/ui/PromptActions.d.ts +26 -0
  238. package/dist/prompts/ui/PromptActions.js +19 -0
  239. package/dist/prompts/ui/PromptCard.d.ts +16 -0
  240. package/dist/prompts/ui/PromptCard.js +50 -0
  241. package/dist/prompts/ui/PromptView.d.ts +2 -0
  242. package/dist/prompts/ui/PromptView.js +47 -0
  243. package/dist/prompts/ui/QuestionBody.d.ts +9 -0
  244. package/dist/prompts/ui/QuestionBody.js +87 -0
  245. package/dist/providers/AccountColors.d.ts +5 -0
  246. package/dist/providers/AccountColors.js +8 -0
  247. package/dist/providers/AccountDetail.d.ts +12 -0
  248. package/dist/providers/AccountDetail.js +76 -0
  249. package/dist/providers/AccountVariables.d.ts +8 -0
  250. package/dist/providers/AccountVariables.js +48 -0
  251. package/dist/providers/AddAccountForm.d.ts +10 -0
  252. package/dist/providers/AddAccountForm.js +58 -0
  253. package/dist/providers/CliDetail.d.ts +11 -0
  254. package/dist/providers/CliDetail.js +62 -0
  255. package/dist/providers/ProvidersPane.d.ts +8 -0
  256. package/dist/providers/ProvidersPane.js +90 -0
  257. package/dist/providers/account-actions.d.ts +7 -0
  258. package/dist/providers/account-actions.js +53 -0
  259. package/dist/providers/parts.d.ts +9 -0
  260. package/dist/providers/parts.js +18 -0
  261. package/dist/providers/provider-abilities.d.ts +2 -0
  262. package/dist/providers/provider-abilities.js +7 -0
  263. package/dist/providers/variables.d.ts +13 -0
  264. package/dist/providers/variables.js +53 -0
  265. package/dist/scope.d.ts +11 -0
  266. package/dist/scope.js +10 -0
  267. package/dist/settings/sections.d.ts +13 -0
  268. package/dist/settings/sections.js +30 -0
  269. package/dist/state/chats.d.ts +32 -0
  270. package/dist/state/chats.js +124 -0
  271. package/dist/state/provider-accounts.d.ts +17 -0
  272. package/dist/state/provider-accounts.js +63 -0
  273. package/dist/state/providers.d.ts +17 -0
  274. package/dist/state/providers.js +29 -0
  275. package/dist/state/usage.d.ts +41 -0
  276. package/dist/state/usage.js +100 -0
  277. package/dist/theme.css +186 -0
  278. package/dist/transport.d.ts +24 -0
  279. package/dist/transport.js +16 -0
  280. package/dist/ui/PlainTextarea.d.ts +2 -0
  281. package/dist/ui/PlainTextarea.js +5 -0
  282. package/dist/usage/LimitsList.d.ts +18 -0
  283. package/dist/usage/LimitsList.js +69 -0
  284. package/dist/usage/UsageBreakdown.d.ts +9 -0
  285. package/dist/usage/UsageBreakdown.js +69 -0
  286. package/dist/usage/UsageChart.d.ts +10 -0
  287. package/dist/usage/UsageChart.js +49 -0
  288. package/dist/usage/UsageDialog.d.ts +6 -0
  289. package/dist/usage/UsageDialog.js +15 -0
  290. package/dist/usage/UsageLimits.d.ts +1 -0
  291. package/dist/usage/UsageLimits.js +62 -0
  292. package/dist/usage/UsageLimitsCard.d.ts +4 -0
  293. package/dist/usage/UsageLimitsCard.js +27 -0
  294. package/dist/usage/UsagePage.d.ts +7 -0
  295. package/dist/usage/UsagePage.js +161 -0
  296. package/dist/usage/UsagePane.d.ts +3 -0
  297. package/dist/usage/UsagePane.js +24 -0
  298. package/dist/usage/UsageSummary.d.ts +12 -0
  299. package/dist/usage/UsageSummary.js +15 -0
  300. package/dist/usage/UsageTiles.d.ts +7 -0
  301. package/dist/usage/UsageTiles.js +19 -0
  302. package/dist/usage/format.d.ts +14 -0
  303. package/dist/usage/format.js +50 -0
  304. package/dist/usage/limit-groups.d.ts +22 -0
  305. package/dist/usage/limit-groups.js +78 -0
  306. package/dist/usage/limits.d.ts +4 -0
  307. package/dist/usage/limits.js +58 -0
  308. package/dist/usage/money.d.ts +1 -0
  309. package/dist/usage/money.js +13 -0
  310. package/dist/usage/summary.d.ts +34 -0
  311. package/dist/usage/summary.js +90 -0
  312. package/package.json +639 -2
@@ -0,0 +1,50 @@
1
+ import type { ChatBookmark } from '@ruimte/agent-contracts';
2
+ import type { TimelineRow } from './timeline';
3
+ export type TickKind = 'person' | 'wake' | 'bookmark';
4
+ export interface ScrubberTick {
5
+ id: string;
6
+ rowIndex: number;
7
+ kind: TickKind;
8
+ text: string;
9
+ createdAt: number;
10
+ turnId: string | null;
11
+ bookmark: ChatBookmark | null;
12
+ }
13
+ export declare const SCRUBBER_MIN_TICKS = 3;
14
+ export declare const TICK_HEIGHT_PX = 2;
15
+ export declare const STRIP_WIDTH_PX = 24;
16
+ export declare const STRIP_INSET_PX = 8;
17
+ export declare const ticksOf: (rows: readonly TimelineRow[], marks?: ReadonlyMap<string, ChatBookmark>) => ScrubberTick[];
18
+ export interface TickSlot {
19
+ first: number;
20
+ last: number;
21
+ kind: TickKind;
22
+ marked: boolean;
23
+ found: boolean;
24
+ y: number;
25
+ }
26
+ export interface ScrubberLayout {
27
+ top: number;
28
+ pitch: number;
29
+ count: number;
30
+ slots: TickSlot[];
31
+ }
32
+ export declare const layoutTicks: (kinds: readonly TickKind[], height: number, marked?: readonly boolean[], found?: readonly boolean[]) => ScrubberLayout;
33
+ export declare const tickOfRow: (ticks: readonly ScrubberTick[], rowIndex: number) => number | null;
34
+ export declare const ticksWithHits: (ticks: readonly ScrubberTick[], hitRows: readonly number[]) => boolean[];
35
+ export declare const messageAt: (layout: ScrubberLayout, y: number) => number | null;
36
+ export declare const slotOf: (layout: ScrubberLayout, message: number) => number | null;
37
+ export declare const tickWidth: (kind: TickKind, distanceFromPointer: number | null) => number;
38
+ export interface ReadingPosition {
39
+ starts: readonly number[];
40
+ scrollTop: number;
41
+ visibleHeight: number;
42
+ }
43
+ export interface MessageRange {
44
+ first: number;
45
+ last: number;
46
+ }
47
+ export declare const messagesInView: ({ starts, scrollTop, visibleHeight }: ReadingPosition) => MessageRange | null;
48
+ export declare const slotInView: (slot: TickSlot, range: MessageRange | null) => boolean;
49
+ export declare const stepMessage: (starts: readonly number[], eligible: (index: number) => boolean, scrollTop: number, direction: -1 | 1) => number | null;
50
+ export declare const threadPaddingLeft: (width: number, strip: boolean) => number;
@@ -0,0 +1,216 @@
1
+ /* With fewer messages than this the thread is short enough to scroll through, and the strip is noise. */
2
+ export const SCRUBBER_MIN_TICKS = 3;
3
+ export const TICK_HEIGHT_PX = 2;
4
+ const PITCH_PX = 8;
5
+ const MIN_PITCH_PX = 4;
6
+ /* The thread's content column in a view of its own (`.chat-column-content`) and the scroller's padding around it (`px-4`). */
7
+ const CONTENT_MAX_WIDTH_PX = 768;
8
+ const THREAD_PADDING_PX = 16;
9
+ export const STRIP_WIDTH_PX = 24;
10
+ /* The strip's distance from the view's left edge, and the air it keeps between itself and the thread's text. */
11
+ export const STRIP_INSET_PX = 8;
12
+ const STRIP_CLEARANCE_PX = STRIP_INSET_PX + STRIP_WIDTH_PX + STRIP_INSET_PX;
13
+ const WIDTH_HOVERED_PX = 16;
14
+ const WIDTH_NEAR_PERSON_PX = 12;
15
+ const WIDTH_NEAR_WAKE_PX = 8;
16
+ const WIDTH_PERSON_PX = 8;
17
+ const WIDTH_WAKE_PX = 5;
18
+ /* `marks` is what `bookmarkRows` answers: the bookmark drawn at a row, by row id. */
19
+ export const ticksOf = (rows, marks = new Map()) => rows.flatMap((row, rowIndex) => {
20
+ const bookmark = marks.get(row.id) ?? null;
21
+ if (row.kind === 'user') {
22
+ // A message of only attachments still has a place in the thread; its names stand in for the text.
23
+ const text = row.item.text !== '' ? row.item.text : (row.item.attachments ?? []).map((attachment) => attachment.name).join(', ');
24
+ return [{ id: row.id, rowIndex, kind: 'person', text, createdAt: row.item.createdAt, turnId: row.item.turnId, bookmark }];
25
+ }
26
+ // The other turns nobody asked for (a sub-agent that finished, the CLI going on) are no place to return to.
27
+ if (row.kind === 'turn-start' && ((row.turn.taskIds?.length ?? 0) > 0 || (row.turn.messageFrom?.length ?? 0) > 0)) {
28
+ return [{ id: row.id, rowIndex, kind: 'wake', text: row.label, createdAt: row.turn.createdAt, turnId: row.turn.id, bookmark: null }];
29
+ }
30
+ if (bookmark === null) {
31
+ return [];
32
+ }
33
+ if (row.kind === 'assistant') {
34
+ return [{ id: row.id, rowIndex, kind: 'bookmark', text: row.item.text, createdAt: row.item.createdAt, turnId: row.item.turnId, bookmark }];
35
+ }
36
+ // The fold of a turn a marked reply is hidden in; the card reads the start the bookmark kept.
37
+ const turnId = row.kind === 'turn-fold' ? row.turn.id : null;
38
+ return [{ id: row.id, rowIndex, kind: 'bookmark', text: bookmark.excerpt, createdAt: bookmark.createdAt, turnId, bookmark }];
39
+ });
40
+ /* `marked` and `found` say per message whether it has a bookmark and whether a find hit falls under it; absent, none has. */
41
+ export const layoutTicks = (kinds, height, marked = [], found = []) => {
42
+ const count = kinds.length;
43
+ const room = Math.max(0, Math.floor(height));
44
+ const slotCount = Math.min(count, Math.floor(room / MIN_PITCH_PX));
45
+ if (slotCount === 0) {
46
+ return { top: 0, pitch: MIN_PITCH_PX, count, slots: [] };
47
+ }
48
+ const pitch = Math.min(PITCH_PX, Math.floor(room / slotCount));
49
+ const top = Math.floor((room - slotCount * pitch) / 2);
50
+ const inset = Math.floor((pitch - TICK_HEIGHT_PX) / 2);
51
+ const slots = [];
52
+ for (let i = 0; i < slotCount; i++) {
53
+ const first = Math.floor((i * count) / slotCount);
54
+ const last = Math.floor(((i + 1) * count) / slotCount) - 1;
55
+ // A merged tick is the person's as soon as one message in it is, since those are what the strip is for.
56
+ const run = kinds.slice(first, last + 1);
57
+ const kind = run.includes('person') ? 'person' : run.includes('bookmark') ? 'bookmark' : 'wake';
58
+ slots.push({
59
+ first,
60
+ last,
61
+ kind,
62
+ marked: marked.slice(first, last + 1).includes(true),
63
+ found: found.slice(first, last + 1).includes(true),
64
+ y: top + i * pitch + inset
65
+ });
66
+ }
67
+ return { top, pitch, count, slots };
68
+ };
69
+ /*
70
+ * The tick whose stretch of the thread a row falls in: a tick stands for its row and every row up to
71
+ * the next tick's, and a row above the first tick counts under the first. Null without ticks.
72
+ */
73
+ export const tickOfRow = (ticks, rowIndex) => {
74
+ if (ticks.length === 0) {
75
+ return null;
76
+ }
77
+ let low = 0;
78
+ let high = ticks.length - 1;
79
+ let found = 0;
80
+ while (low <= high) {
81
+ const middle = (low + high) >> 1;
82
+ if (ticks[middle].rowIndex <= rowIndex) {
83
+ found = middle;
84
+ low = middle + 1;
85
+ }
86
+ else {
87
+ high = middle - 1;
88
+ }
89
+ }
90
+ return found;
91
+ };
92
+ /* Per tick, whether any of the rows has a find hit in its stretch. */
93
+ export const ticksWithHits = (ticks, hitRows) => {
94
+ const found = ticks.map(() => false);
95
+ for (const row of hitRows) {
96
+ const tick = tickOfRow(ticks, row);
97
+ if (tick !== null) {
98
+ found[tick] = true;
99
+ }
100
+ }
101
+ return found;
102
+ };
103
+ /* The message under a height in the strip; inside a merged tick the height picks among its messages. */
104
+ export const messageAt = (layout, y) => {
105
+ const { slots, top, pitch } = layout;
106
+ if (slots.length === 0) {
107
+ return null;
108
+ }
109
+ const offset = y - top;
110
+ const index = Math.min(slots.length - 1, Math.max(0, Math.floor(offset / pitch)));
111
+ const slot = slots[index];
112
+ const fraction = Math.min(1, Math.max(0, (offset - index * pitch) / pitch));
113
+ return slot.first + Math.min(slot.last - slot.first, Math.floor(fraction * (slot.last - slot.first + 1)));
114
+ };
115
+ export const slotOf = (layout, message) => {
116
+ const { slots } = layout;
117
+ let low = 0;
118
+ let high = slots.length - 1;
119
+ while (low <= high) {
120
+ const middle = (low + high) >> 1;
121
+ const slot = slots[middle];
122
+ if (message < slot.first) {
123
+ high = middle - 1;
124
+ }
125
+ else if (message > slot.last) {
126
+ low = middle + 1;
127
+ }
128
+ else {
129
+ return middle;
130
+ }
131
+ }
132
+ return null;
133
+ };
134
+ /*
135
+ * At rest every tick of a kind is as long as the next, so length never competes with the brightness
136
+ * that says what is on screen. Only a pointer on the strip magnifies, the way a dock does, one step out.
137
+ */
138
+ export const tickWidth = (kind, distanceFromPointer) => {
139
+ if (distanceFromPointer === 0) {
140
+ return WIDTH_HOVERED_PX;
141
+ }
142
+ if (distanceFromPointer === 1) {
143
+ return kind === 'wake' ? WIDTH_NEAR_WAKE_PX : WIDTH_NEAR_PERSON_PX;
144
+ }
145
+ return kind === 'wake' ? WIDTH_WAKE_PX : WIDTH_PERSON_PX;
146
+ };
147
+ /* The last index whose start lies at or above a line, or -1. The starts only grow. */
148
+ const lastAtOrAbove = (starts, line) => {
149
+ let low = 0;
150
+ let high = starts.length - 1;
151
+ let found = -1;
152
+ while (low <= high) {
153
+ const middle = (low + high) >> 1;
154
+ if (starts[middle] <= line) {
155
+ found = middle;
156
+ low = middle + 1;
157
+ }
158
+ else {
159
+ high = middle - 1;
160
+ }
161
+ }
162
+ return found;
163
+ };
164
+ /*
165
+ * The messages on screen. A message counts from its own start to the start of the next one, so the
166
+ * question whose answer fills the screen is in view even after it scrolled out at the top.
167
+ */
168
+ export const messagesInView = ({ starts, scrollTop, visibleHeight }) => {
169
+ const bottom = scrollTop + visibleHeight;
170
+ if (starts.length === 0 || visibleHeight <= 0 || starts[0] >= bottom) {
171
+ return null;
172
+ }
173
+ const first = Math.max(0, lastAtOrAbove(starts, scrollTop));
174
+ let last = lastAtOrAbove(starts, bottom);
175
+ // A row that starts on the very last line has not a pixel on screen yet.
176
+ if (starts[last] >= bottom) {
177
+ last--;
178
+ }
179
+ return { first, last: Math.max(first, last) };
180
+ };
181
+ /* Whether a drawn tick covers any of the messages on screen. */
182
+ export const slotInView = (slot, range) => range !== null && slot.last >= range.first && slot.first <= range.last;
183
+ /*
184
+ * The message a step back or forward lands on, among the ones `eligible` allows. A jump puts a
185
+ * message's start at the top, so back from there is the one before it, and back from the middle of
186
+ * a long answer is the question it belongs to. A pixel of slack absorbs rounding in the scroller.
187
+ */
188
+ export const stepMessage = (starts, eligible, scrollTop, direction) => {
189
+ if (direction === -1) {
190
+ for (let i = lastAtOrAbove(starts, scrollTop - 1.5); i >= 0; i--) {
191
+ if (eligible(i)) {
192
+ return i;
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ for (let i = lastAtOrAbove(starts, scrollTop + 1) + 1; i < starts.length; i++) {
198
+ if (eligible(i)) {
199
+ return i;
200
+ }
201
+ }
202
+ return null;
203
+ };
204
+ /*
205
+ * The left padding of the thread in a view that draws the strip at its left edge. A wide view keeps
206
+ * the usual padding, since its centered column already clears the strip; a narrower one pads until
207
+ * the column's text starts clear of it, so nothing is ever drawn under the strip.
208
+ */
209
+ export const threadPaddingLeft = (width, strip) => {
210
+ if (!strip) {
211
+ return THREAD_PADDING_PX;
212
+ }
213
+ // The column's margin is half of what the padding leaves, so the padding counts the clearance twice.
214
+ const padding = 2 * STRIP_CLEARANCE_PX + CONTENT_MAX_WIDTH_PX + THREAD_PADDING_PX - Math.floor(width);
215
+ return Math.min(STRIP_CLEARANCE_PX, Math.max(THREAD_PADDING_PX, padding));
216
+ };
@@ -0,0 +1,4 @@
1
+ import type { TimelineRow } from './timeline';
2
+ export declare const stripMarkdown: (text: string) => string;
3
+ export declare const messageTextOf: (row: TimelineRow) => string | null;
4
+ export declare const markdownOf: (row: TimelineRow) => string | null;
@@ -0,0 +1,51 @@
1
+ /*
2
+ * What a row of the thread puts on the clipboard. An answer is markdown on the wire and rendered
3
+ * text on the screen, so copying it as text has to undo the marks the renderer swallowed; the
4
+ * source itself stays available as "Copy as markdown".
5
+ */
6
+ /*
7
+ * The marks that only exist to be rendered come off, what they marked stays. `_` is left alone on
8
+ * purpose: it is a letter in half the identifiers an agent writes, and no italic is worth mangling
9
+ * `file_path`.
10
+ */
11
+ export const stripMarkdown = (text) => text
12
+ .replace(/^ *```[^\n]*\n?/gm, '')
13
+ .replace(/^ *(#{1,6}) +/gm, '')
14
+ .replace(/^ *> ?/gm, '')
15
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
16
+ .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
17
+ .replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2')
18
+ .replace(/\*(?=\S)([^*\n]*\S)\*/g, '$1')
19
+ .replace(/`+([^`]+)`+/g, '$1')
20
+ .trim();
21
+ /* What a tool call has to show for itself: what it answered, or what it has answered so far. */
22
+ const outputOf = (tool) => tool.output ?? tool.progress?.output ?? null;
23
+ /* The whole item under the pointer as plain text, or null for a row that carries no message. */
24
+ export const messageTextOf = (row) => {
25
+ switch (row.kind) {
26
+ case 'user':
27
+ return row.item.text || null;
28
+ case 'assistant':
29
+ return stripMarkdown(row.item.text) || null;
30
+ case 'report':
31
+ return stripMarkdown(row.text) || null;
32
+ case 'thinking':
33
+ return row.item.text || null;
34
+ case 'work':
35
+ case 'work-live':
36
+ case 'workflow':
37
+ return outputOf(row.tool);
38
+ case 'work-group': {
39
+ const outputs = row.tools.map(outputOf).filter((output) => output !== null);
40
+ return outputs.length > 0 ? outputs.join('\n\n') : null;
41
+ }
42
+ case 'subagent':
43
+ return row.item.result ?? row.item.summary ?? null;
44
+ case 'note':
45
+ return row.text;
46
+ default:
47
+ return null;
48
+ }
49
+ };
50
+ /* The markdown an answer was written in; every other row has none to copy. */
51
+ export const markdownOf = (row) => row.kind === 'assistant' ? row.item.text || null : row.kind === 'report' ? row.text || null : null;
@@ -0,0 +1,12 @@
1
+ import type { ChatItem } from '@ruimte/agent-contracts';
2
+ import type { TimelineRow } from './timeline';
3
+ export interface TimelineTarget {
4
+ selection: string;
5
+ row: TimelineRow | null;
6
+ code: string | null;
7
+ path: string | null;
8
+ line: number | null;
9
+ }
10
+ export declare const EMPTY_TARGET: TimelineTarget;
11
+ export declare const withCurrentText: (rows: TimelineRow[], items: Record<string, ChatItem> | undefined) => TimelineRow[];
12
+ export declare const readTimelineTarget: (element: HTMLElement, scroller: HTMLElement | null, rows: TimelineRow[]) => TimelineTarget;
@@ -0,0 +1,34 @@
1
+ import { selectionWithin } from '@ruimte/ui/selection';
2
+ export const EMPTY_TARGET = { selection: '', row: null, code: null, path: null, line: null };
3
+ /*
4
+ * The rows with the text a reply or a thought holds now. Rows are derived from the structure of a
5
+ * thread, which a delta leaves alone, so a row still carries the text it was derived with.
6
+ */
7
+ export const withCurrentText = (rows, items) => {
8
+ if (!items) {
9
+ return rows;
10
+ }
11
+ return rows.map((row) => {
12
+ const item = items[row.id];
13
+ if (row.kind === 'assistant' && item?.kind === 'assistant') {
14
+ return { ...row, item };
15
+ }
16
+ if (row.kind === 'thinking' && item?.kind === 'thinking') {
17
+ return { ...row, item };
18
+ }
19
+ return row;
20
+ });
21
+ };
22
+ /* The row a click landed in is the one whose id it sits under; the rest comes from the same walk up. */
23
+ export const readTimelineTarget = (element, scroller, rows) => {
24
+ const id = element.closest('[data-item-id]')?.dataset.itemId;
25
+ const file = element.closest('[data-file-path]');
26
+ const line = Number(file?.dataset.fileLine);
27
+ return {
28
+ selection: selectionWithin(scroller),
29
+ row: rows.find((row) => row.id === id) ?? null,
30
+ code: element.closest('pre')?.textContent ?? null,
31
+ path: file?.dataset.filePath ?? null,
32
+ line: Number.isFinite(line) && line > 0 ? line : null
33
+ };
34
+ };
@@ -0,0 +1,110 @@
1
+ import type { ChatApprovalItem, ChatAssistantItem, ChatCheckpointDiff, ChatItem, ChatQuestionItem, ChatSubagentItem, ChatThinkingItem, ChatToolItem, ChatTurnItem, ChatUserItem, ChatWorkflow } from '@ruimte/agent-contracts';
2
+ export interface SubagentBranch {
3
+ id: string;
4
+ item: ChatSubagentItem;
5
+ children: ChatItem[];
6
+ nested: SubagentBranch[];
7
+ expanded: boolean;
8
+ }
9
+ export type TimelineRow = {
10
+ kind: 'user';
11
+ id: string;
12
+ item: ChatUserItem;
13
+ } | {
14
+ kind: 'turn-start';
15
+ id: string;
16
+ turn: ChatTurnItem;
17
+ label: string;
18
+ } | {
19
+ kind: 'assistant';
20
+ id: string;
21
+ item: ChatAssistantItem;
22
+ } | {
23
+ kind: 'report';
24
+ id: string;
25
+ text: string;
26
+ } | {
27
+ kind: 'thinking';
28
+ id: string;
29
+ item: ChatThinkingItem;
30
+ } | {
31
+ kind: 'work';
32
+ id: string;
33
+ tool: ChatToolItem;
34
+ } | {
35
+ kind: 'work-group';
36
+ id: string;
37
+ tools: ChatToolItem[];
38
+ summary: string;
39
+ expanded: boolean;
40
+ } | {
41
+ kind: 'work-live';
42
+ id: string;
43
+ tool: ChatToolItem;
44
+ } | {
45
+ kind: 'workflow';
46
+ id: string;
47
+ tool: ChatToolItem;
48
+ workflow: ChatWorkflow;
49
+ } | ({
50
+ kind: 'subagent';
51
+ } & SubagentBranch) | {
52
+ kind: 'approval';
53
+ id: string;
54
+ item: ChatApprovalItem;
55
+ } | {
56
+ kind: 'question';
57
+ id: string;
58
+ item: ChatQuestionItem;
59
+ } | {
60
+ kind: 'note';
61
+ id: string;
62
+ level: 'info' | 'warning' | 'error';
63
+ text: string;
64
+ from?: string;
65
+ } | {
66
+ kind: 'compaction';
67
+ id: string;
68
+ preTokens: number | null;
69
+ } | {
70
+ kind: 'changed-files';
71
+ id: string;
72
+ turnId: string;
73
+ tools: ChatToolItem[];
74
+ diff: ChatCheckpointDiff | null;
75
+ checkpoint: boolean;
76
+ } | {
77
+ kind: 'turn-fold';
78
+ id: string;
79
+ turn: ChatTurnItem;
80
+ label: string;
81
+ work: string[];
82
+ hiddenCount: number;
83
+ expanded: boolean;
84
+ } | {
85
+ kind: 'forks';
86
+ id: string;
87
+ turnId: string;
88
+ } | {
89
+ kind: 'working';
90
+ id: string;
91
+ startedAt: number;
92
+ };
93
+ interface TimelineOptions {
94
+ expandedGroups: ReadonlySet<string>;
95
+ expandedTurns: ReadonlySet<string>;
96
+ expandedSubagents: ReadonlySet<string>;
97
+ activeTurnId: string | null;
98
+ forkedTurns?: ReadonlySet<string>;
99
+ }
100
+ export declare const isBlock: (row: TimelineRow) => boolean;
101
+ export declare const summarizeGroup: (tools: ChatToolItem[]) => string;
102
+ export declare const summarizeTurn: (tools: readonly ChatToolItem[]) => string[];
103
+ export declare const agentTurnLabel: (turn: ChatTurnItem) => string;
104
+ export declare const turnLabel: (turn: ChatTurnItem, items?: readonly ChatItem[]) => string;
105
+ export declare const findSubagentBranch: (rows: readonly TimelineRow[], toolUseId: string) => {
106
+ index: number;
107
+ branch: SubagentBranch;
108
+ } | null;
109
+ export declare const deriveTimelineRows: (items: ChatItem[], options: TimelineOptions) => TimelineRow[];
110
+ export {};