@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,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import clsx from 'clsx';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Bookmark, Pencil, X } from 'lucide-react';
6
+ import { CHAT_BOOKMARK_LIMITS } from '@ruimte/agent-contracts';
7
+ import { nameBookmark, removeBookmark, useBookmarkNaming } from '../bookmarks';
8
+ import { bookmarkLabel } from '../logic/bookmarks';
9
+ import { useChatScope } from '../../scope';
10
+ import { BTN_GROUP } from '@ruimte/ui/classes';
11
+ import { Icon } from '@ruimte/ui/Icon';
12
+ import { Tooltip } from '@ruimte/ui/Tooltip';
13
+ /*
14
+ * A bookmark as a chapter line across the thread, drawn before the message it marks. Without a
15
+ * bookmark it only stands while the name field of a mark still on its way to the host is open.
16
+ */
17
+ export function BookmarkMarker({ chatId, itemId, bookmark }) {
18
+ const { t } = useTranslation('agent-chat');
19
+ const scope = useChatScope();
20
+ const chatKey = scope.keyOf(chatId);
21
+ const naming = useBookmarkNaming((s) => s.chatKey === chatKey && s.itemId === itemId);
22
+ if (bookmark === null && !naming) {
23
+ return null;
24
+ }
25
+ return (_jsxs("div", { className: "group/bookmark mb-2 flex h-7 min-w-0 items-center gap-2 text-xs select-none", children: [_jsx(Icon, { icon: Bookmark, size: 14, className: "shrink-0 text-accent" }), naming ? (_jsx(NameField, { initial: bookmark?.name ?? '', onDone: (name) => {
26
+ useBookmarkNaming.getState().close();
27
+ if (name !== null) {
28
+ void nameBookmark(scope, chatId, itemId, name, bookmark?.name);
29
+ }
30
+ } })) : (bookmark !== null && (_jsx("span", { className: clsx('min-w-0 truncate', bookmark.name === undefined ? 'text-text-muted' : 'text-text'), children: bookmarkLabel(bookmark) }))), _jsx("span", { className: "min-w-6 grow border-t border-border" }), bookmark !== null && !naming && (_jsxs("span", { className: clsx(BTN_GROUP, '-ml-2 max-w-0 shrink-0 overflow-hidden opacity-0 group-hover/bookmark:ml-0 group-hover/bookmark:max-w-none group-hover/bookmark:opacity-100 group-has-focus-visible/bookmark:ml-0 group-has-focus-visible/bookmark:max-w-none group-has-focus-visible/bookmark:opacity-100'), children: [_jsx(Tooltip, { label: t('bookmarks.rename'), name: true, children: _jsx("button", { type: "button", className: "icon-btn icon-btn-xs", onClick: () => useBookmarkNaming.getState().open(chatKey, itemId), children: _jsx(Icon, { icon: Pencil, size: 12 }) }) }), _jsx(Tooltip, { label: t('bookmarks.remove'), name: true, children: _jsx("button", { type: "button", className: "icon-btn icon-btn-xs", onClick: () => void removeBookmark(scope, chatId, bookmark), children: _jsx(Icon, { icon: X, size: 12 }) }) })] }))] }));
31
+ }
32
+ /* Settles with the name on Enter or when the focus leaves, and with null on Escape, which keeps the name as it was. */
33
+ function NameField({ initial, onDone }) {
34
+ const { t } = useTranslation('agent-chat');
35
+ const ref = useRef(null);
36
+ const settled = useRef(false);
37
+ // Again a frame later: the menu the field was opened from hands the focus back as it closes.
38
+ useEffect(() => {
39
+ ref.current?.focus();
40
+ const frame = requestAnimationFrame(() => ref.current?.focus());
41
+ return () => cancelAnimationFrame(frame);
42
+ }, []);
43
+ const settle = (name) => {
44
+ if (settled.current) {
45
+ return;
46
+ }
47
+ settled.current = true;
48
+ onDone(name);
49
+ };
50
+ return (_jsx("input", { ref: ref, defaultValue: initial, maxLength: CHAT_BOOKMARK_LIMITS.name, placeholder: t('bookmarks.namePlaceholder'), "aria-label": t('bookmarks.nameLabel'), className: "field field-sm w-64 max-w-full min-w-0 select-text", onPointerDown: (e) => e.stopPropagation(), onFocus: (e) => e.currentTarget.select(), onBlur: (e) => settle(e.currentTarget.value), onKeyDown: (e) => {
51
+ e.stopPropagation();
52
+ if (e.key === 'Enter') {
53
+ settle(e.currentTarget.value);
54
+ }
55
+ if (e.key === 'Escape') {
56
+ settle(null);
57
+ }
58
+ } }));
59
+ }
@@ -0,0 +1,3 @@
1
+ export declare function BookmarkSubmenu({ chatId }: {
2
+ chatId: string;
3
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import clsx from 'clsx';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Menu } from '@base-ui-components/react/menu';
6
+ import { Bookmark, ChevronRight, Pencil, X } from 'lucide-react';
7
+ import { goToBookmark, removeBookmark } from '../bookmarks';
8
+ import { bookmarkLabel, bookmarksInThreadOrder } from '../logic/bookmarks';
9
+ import { chatHost } from '../../host';
10
+ import { useChatScope } from '../../scope';
11
+ import { useChatRow } from '../../state/chats';
12
+ import { BTN_GROUP } from '@ruimte/ui/classes';
13
+ import { Icon } from '@ruimte/ui/Icon';
14
+ import { Tooltip } from '@ruimte/ui/Tooltip';
15
+ const actionOf = (e) => {
16
+ const action = e.target.closest('[data-bookmark-action]')?.dataset.bookmarkAction;
17
+ return action === 'rename' || action === 'remove' ? action : null;
18
+ };
19
+ /*
20
+ * "Bookmarks" in the menu of a chat node or a chat view: the chat's bookmarks in the order of the
21
+ * thread, each a jump to its message. Rename and remove sit on the row itself; the message's own
22
+ * menu offers both to a keyboard. A chat without bookmarks draws nothing.
23
+ */
24
+ export function BookmarkSubmenu({ chatId }) {
25
+ const { t } = useTranslation('agent-chat');
26
+ const scope = useChatScope();
27
+ const bookmarks = useChatRow(chatId, (row) => row?.bookmarks);
28
+ const order = useChatRow(chatId, (row) => row?.order);
29
+ const place = chatHost().useChatPlace(chatId);
30
+ const sorted = useMemo(() => bookmarksInThreadOrder(bookmarks ?? [], order ?? []), [bookmarks, order]);
31
+ if (sorted.length === 0) {
32
+ return null;
33
+ }
34
+ return (_jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { className: "menu-item", children: [_jsx(Icon, { icon: Bookmark, size: 14 }), " ", t('bookmarks.menu'), _jsx(Icon, { icon: ChevronRight, size: 14, className: "ml-auto text-text-faint" })] }), _jsx(Menu.Portal, { children: _jsx(Menu.Positioner, { className: "z-(--z-popup)", sideOffset: 4, alignOffset: -4, children: _jsx(Menu.Popup, { className: "menu-popup max-w-80 min-w-56", children: sorted.map((bookmark) => (_jsxs(Menu.Item, { className: "menu-item group/bookmark", onClick: (e) => {
35
+ const action = actionOf(e);
36
+ if (action === 'remove') {
37
+ void removeBookmark(scope, chatId, bookmark);
38
+ return;
39
+ }
40
+ if (!goToBookmark(scope, chatId, bookmark.itemId, action === 'rename')) {
41
+ place.go();
42
+ }
43
+ }, children: [_jsx(Icon, { icon: Bookmark, size: 14, className: "shrink-0 text-accent" }), _jsx("span", { className: clsx('min-w-0 grow truncate', bookmark.name === undefined && 'text-text-muted'), children: bookmarkLabel(bookmark) }), _jsxs("span", { className: `${BTN_GROUP} -my-1 ml-2 shrink-0 opacity-0 group-data-[highlighted]/bookmark:opacity-100`, children: [_jsx(Tooltip, { label: t('bookmarks.rename'), name: true, children: _jsx("button", { type: "button", tabIndex: -1, "data-bookmark-action": "rename", className: "icon-btn icon-btn-xs", children: _jsx(Icon, { icon: Pencil, size: 12 }) }) }), _jsx(Tooltip, { label: t('bookmarks.remove'), name: true, children: _jsx("button", { type: "button", tabIndex: -1, "data-bookmark-action": "remove", className: "icon-btn icon-btn-xs", children: _jsx(Icon, { icon: X, size: 12 }) }) })] })] }, bookmark.itemId))) }) }) })] }));
44
+ }
@@ -0,0 +1,3 @@
1
+ export declare function ChatActivity({ chatId }: {
2
+ chatId: string;
3
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,87 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { Popover } from '@base-ui-components/react/popover';
4
+ import clsx from 'clsx';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Activity, Square, SquareTerminal } from 'lucide-react';
7
+ import { backgroundCounts } from '../activity';
8
+ import { badgeCountOf, entryTimeOf, flyoutSubagents, statusWordOf, subagentTitle, summaryWordOf, taskIdOf } from '../subagent-list';
9
+ import { useSubagentSupport } from '../subagent-support';
10
+ import { canOpenSubagent, crumbOf, openFromMain, useSubagentTrail } from '../subagent-view';
11
+ import { SubagentStopButton } from './SubagentStopButton';
12
+ import { formatElapsedShort } from '@ruimte/ui/format/duration';
13
+ import { useChatRow } from '../../state/chats';
14
+ import { chatHost } from '../../host';
15
+ import { useChatScope } from '../../scope';
16
+ import { useChatActions } from '../actions';
17
+ import { FLOAT } from '@ruimte/ui/classes';
18
+ import { Icon } from '@ruimte/ui/Icon';
19
+ import { statusLookOf } from '../../agents/status-look';
20
+ import { Tooltip } from '@ruimte/ui/Tooltip';
21
+ import { useNow } from '@ruimte/ui/useNow';
22
+ const NO_TASKS = [];
23
+ const NO_ITEMS = [];
24
+ const CHIP = `${FLOAT} pointer-events-auto inline-flex h-7.5 items-center gap-1.5 rounded-full px-3 text-xs text-text-muted hover:text-text`;
25
+ const ROW_BODY = 'flex min-h-9 min-w-0 grow items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left';
26
+ /* What the chat keeps working on beside the thread, in one small line over the composer: its sub-agents and what runs in the background. */
27
+ export function ChatActivity({ chatId }) {
28
+ const { t } = useTranslation('agent-chat');
29
+ const order = useChatRow(chatId, (row) => row?.order) ?? NO_ITEMS;
30
+ const structure = useChatRow(chatId, (row) => row?.structure);
31
+ const subagents = useMemo(() => (structure === undefined ? [] : flyoutSubagents(order, structure)), [order, structure]);
32
+ const { id } = useChatScope();
33
+ const tasks = chatHost().tasks.useTasks(id);
34
+ const words = useMemo(() => subagents.map((item) => {
35
+ const taskId = taskIdOf(item);
36
+ return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
37
+ }), [subagents, tasks]);
38
+ const background = useChatRow(chatId, (row) => row?.info.background) ?? NO_TASKS;
39
+ if (subagents.length === 0 && background.length === 0) {
40
+ return null;
41
+ }
42
+ const { shells, monitors } = backgroundCounts(background);
43
+ return (_jsxs("div", { className: "mb-3 flex flex-wrap items-center gap-2", children: [subagents.length > 0 && (_jsx(Flyout, { icon: _jsx(StatusIcon, { word: summaryWordOf(words) }), label: t('activity.subagents', { count: badgeCountOf(words) }), heading: t('activity.subagents', { count: subagents.length }), children: _jsx(SubagentRows, { chatId: chatId, items: subagents }) })), background.length > 0 && (_jsx(Flyout, { icon: _jsx(Icon, { icon: shells > 0 ? SquareTerminal : Activity, size: 14 }), label: [shells > 0 && t('activity.shells', { count: shells }), monitors > 0 && t('activity.monitors', { count: monitors })]
44
+ .filter(Boolean)
45
+ .join(' · '), children: _jsx(BackgroundTaskRows, { chatId: chatId, tasks: background }) }))] }));
46
+ }
47
+ function StatusIcon({ word }) {
48
+ const look = statusLookOf(word);
49
+ return _jsx(Icon, { icon: look.icon, size: 14, className: clsx('shrink-0', look.tone, look.spins && 'animate-spin') });
50
+ }
51
+ /* A chip that opens a list over it, headed by what the chip says unless the list counts more than the chip does. */
52
+ function Flyout({ icon, label, heading = label, children }) {
53
+ return (_jsxs(Popover.Root, { children: [_jsxs(Popover.Trigger, { className: CHIP, children: [icon, label] }), _jsx(Popover.Portal, { children: _jsx(Popover.Positioner, { side: "top", sideOffset: 8, align: "start", className: "z-(--z-popup)", children: _jsxs(Popover.Popup, { className: "menu-popup w-80", children: [_jsx("div", { className: "px-2.5 pt-1.5 pb-1 text-xs text-text-faint", children: heading }), children] }) }) })] }));
54
+ }
55
+ /* One line of a flyout; with `onOpen` the line leads somewhere and closes the flyout on the way. */
56
+ function ActivityRow({ icon, title, detail, time, onOpen, children }) {
57
+ const body = (_jsxs(_Fragment, { children: [icon, _jsxs("span", { className: "flex min-w-0 grow flex-col", children: [_jsx("span", { className: "truncate text-sm text-text", children: title }), detail && _jsx("span", { className: "truncate font-mono text-xs text-text-faint", children: detail })] }), time !== '' && _jsx("span", { className: "shrink-0 text-xs text-text-faint tabular-nums", children: time })] }));
58
+ // The stop is a button of its own beside the line, since a button cannot hold another.
59
+ return (_jsxs("div", { className: clsx('flex items-center gap-1 rounded-md', onOpen && 'hover:bg-surface-hover'), children: [onOpen ? (_jsx(Popover.Close, { className: ROW_BODY, onClick: onOpen, children: body })) : (_jsx("div", { className: ROW_BODY, children: body })), children] }));
60
+ }
61
+ /* Only drawn while the flyout is open, so the clock ticks for nobody otherwise. */
62
+ function SubagentRows({ chatId, items }) {
63
+ const now = useNow(1000, items.some((item) => item.status === 'running'));
64
+ return items.map((item) => _jsx(SubagentEntry, { chatId: chatId, item: item, now: now }, item.id));
65
+ }
66
+ function SubagentEntry({ chatId, item, now }) {
67
+ const { t } = useTranslation('agent-chat');
68
+ const { id } = useChatScope();
69
+ const refused = useSubagentSupport((s) => s.unsupported[id] === true);
70
+ const task = chatHost().tasks.useTask(id, taskIdOf(item));
71
+ const { show } = useSubagentTrail(chatId);
72
+ const word = statusWordOf(item, task);
73
+ return (_jsx(ActivityRow, { icon: _jsx(StatusIcon, { word: word }), title: _jsxs(_Fragment, { children: [subagentTitle(item), _jsxs("span", { className: "sr-only", children: [", ", t(`common.status.${word}`)] })] }), time: entryTimeOf(item, task, now), onOpen: canOpenSubagent(item, refused) ? () => show(openFromMain(crumbOf(item))) : undefined, children: _jsx(SubagentStopButton, { chatId: chatId, item: item, className: "mr-1.5" }) }));
74
+ }
75
+ /* Only drawn while the flyout is open, so the clock ticks for nobody otherwise. */
76
+ function BackgroundTaskRows({ chatId, tasks }) {
77
+ const { t } = useTranslation('agent-chat');
78
+ const now = useNow(1000);
79
+ const actions = useChatActions();
80
+ function stop(taskId) {
81
+ void actions.stopTask(chatId, taskId).catch((e) => {
82
+ const description = e instanceof Error ? e.message : t('subagents.noAnswer');
83
+ chatHost().notify({ kind: 'error', title: t('activity.stopFailed'), description });
84
+ });
85
+ }
86
+ return tasks.map((task) => (_jsx(ActivityRow, { icon: _jsx(Icon, { icon: task.kind === 'shell' ? SquareTerminal : Activity, size: 14, className: "shrink-0 text-text-faint" }), title: task.description || task.command || t(`activity.${task.kind}`), detail: task.description ? task.command : null, time: formatElapsedShort(now - task.startedAt), children: _jsx(Tooltip, { label: t('activity.stop'), name: true, children: _jsx("button", { type: "button", className: "icon-btn icon-btn-xs mr-1.5", onClick: () => stop(task.id), children: _jsx(Icon, { icon: Square, size: 12 }) }) }) }, task.id)));
87
+ }
@@ -0,0 +1,4 @@
1
+ export declare function ChatReferenceChip({ chatId, onRemove }: {
2
+ chatId: string;
3
+ onRemove?: () => void;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { MessageSquare, X } from 'lucide-react';
5
+ import { chatHost } from '../../host';
6
+ import { CHIP_IN_MESSAGE, MENTION_TONE } from './chips';
7
+ import { Icon } from '@ruimte/ui/Icon';
8
+ import { Tooltip } from '@ruimte/ui/Tooltip';
9
+ /* A chat attached to a message, under the title it has now rather than the one it had when it was attached. */
10
+ export function ChatReferenceChip({ chatId, onRemove }) {
11
+ const { t } = useTranslation('agent-chat');
12
+ const chat = chatHost()
13
+ .useReferableChats()
14
+ .find((candidate) => candidate.id === chatId);
15
+ const title = chat === undefined ? t('chatReference.gone') : chat.title || t('chatReference.untitled');
16
+ return (_jsxs("span", { className: clsx(MENTION_TONE, CHIP_IN_MESSAGE), children: [_jsx(Icon, { icon: MessageSquare, size: 12, className: "shrink-0" }), _jsx("span", { className: "truncate", children: title }), onRemove && (_jsx(Tooltip, { label: t('chatReference.remove', { title }), name: true, children: _jsx("button", { className: "-mr-0.5 shrink-0 rounded-sm opacity-70 hover:opacity-100", onClick: onRemove, children: _jsx(Icon, { icon: X, size: 12 }) }) }))] }));
17
+ }
@@ -0,0 +1,4 @@
1
+ export declare function CodeBlock({ code, lang }: {
2
+ code: string;
3
+ lang: string;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,130 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment, memo, useContext, useEffect, useMemo, useState } from 'react';
3
+ import clsx from 'clsx';
4
+ import { IncrementalLines } from './code-lines';
5
+ import { CodeStreamingContext } from './code-streaming';
6
+ import { WHOLE_FADE_CLASS } from './rehype-fade';
7
+ import { shikiThemeOf, useCodeTheme } from './code-theme';
8
+ // A plain language needs no grammar; shiki draws it in the theme's own foreground.
9
+ const PLAIN = 'text';
10
+ let highlighter = null;
11
+ let highlighterLoad = null;
12
+ const loadedLanguages = new Set([PLAIN]);
13
+ const languageLoads = new Map();
14
+ const loadedThemes = new Set();
15
+ const themeLoads = new Map();
16
+ const tokenizers = new Map();
17
+ // Shiki loads on the first code block, not with the app; the web bundle covers the languages a coding agent writes.
18
+ const loadHighlighter = () => {
19
+ highlighterLoad ??= import('shiki/bundle/web').then(async ({ createHighlighter }) => {
20
+ highlighter = await createHighlighter({ themes: [], langs: [] });
21
+ return highlighter;
22
+ });
23
+ return highlighterLoad;
24
+ };
25
+ const resolveLanguage = (lang, known) => (known !== null && lang in known ? lang : PLAIN);
26
+ let bundled = null;
27
+ /* Loads the highlighter and the grammar of one language, once each. */
28
+ const loadLanguage = (lang) => {
29
+ let load = languageLoads.get(lang);
30
+ if (!load) {
31
+ load = Promise.all([loadHighlighter(), import('shiki/bundle/web')]).then(async ([loaded, { bundledLanguages }]) => {
32
+ bundled = bundledLanguages;
33
+ const language = resolveLanguage(lang, bundledLanguages);
34
+ if (!loadedLanguages.has(language)) {
35
+ await loaded.loadLanguage(language);
36
+ loadedLanguages.add(language);
37
+ }
38
+ });
39
+ languageLoads.set(lang, load);
40
+ }
41
+ return load;
42
+ };
43
+ /* Loads a theme into the highlighter, once. */
44
+ const loadTheme = (theme) => {
45
+ let load = themeLoads.get(theme);
46
+ if (!load) {
47
+ load = loadHighlighter().then(async (loaded) => {
48
+ await loaded.loadTheme(shikiThemeOf(theme));
49
+ loadedThemes.add(theme);
50
+ });
51
+ themeLoads.set(theme, load);
52
+ }
53
+ return load;
54
+ };
55
+ /* The tokenizer for a language in a theme, or null while shiki, the grammar or the theme is still on its way. */
56
+ const tokenizerFor = (lang, theme) => {
57
+ if (highlighter === null || bundled === null || !languageLoads.has(lang) || !loadedThemes.has(theme)) {
58
+ return null;
59
+ }
60
+ const language = resolveLanguage(lang, bundled);
61
+ if (!loadedLanguages.has(language)) {
62
+ return null;
63
+ }
64
+ const key = `${language}:${theme}`;
65
+ let entry = tokenizers.get(key);
66
+ if (!entry) {
67
+ const loaded = highlighter;
68
+ const options = { lang: language, theme };
69
+ entry = {
70
+ tokenize: (code, state) => {
71
+ const result = loaded.codeToTokens(code, state ? { ...options, grammarState: state } : options);
72
+ return { lines: result.tokens, state: result.grammarState };
73
+ },
74
+ fg: loaded.getTheme(theme).fg
75
+ };
76
+ tokenizers.set(key, entry);
77
+ }
78
+ return entry;
79
+ };
80
+ const styleOf = (token) => {
81
+ const style = token.fontStyle ?? 0;
82
+ if (token.color === undefined && style <= 0) {
83
+ return undefined;
84
+ }
85
+ const decorations = [style & 4 ? 'underline' : '', style & 8 ? 'line-through' : ''].filter(Boolean).join(' ');
86
+ return {
87
+ color: token.color,
88
+ ...(style & 1 ? { fontStyle: 'italic' } : {}),
89
+ ...(style & 2 ? { fontWeight: 'bold' } : {}),
90
+ ...(decorations ? { textDecoration: decorations } : {})
91
+ };
92
+ };
93
+ /* A line whose tokens did not change keeps its elements, so a selection in it survives the next delta. */
94
+ const CodeLine = memo(function CodeLine({ tokens }) {
95
+ return (_jsx("span", { className: "line", children: tokens.map((token, index) => (_jsx("span", { style: styleOf(token), children: token.content }, index))) }));
96
+ });
97
+ /*
98
+ * A fenced block, highlighted a line at a time. Until shiki and the grammar are there it holds its
99
+ * place invisibly in the same shape, so there is never a frame of bare code that turns colored. A
100
+ * fence that is still open is tokenized as it grows, and the same component carries on once it
101
+ * closes, so closing it does not draw it again.
102
+ */
103
+ export function CodeBlock({ code, lang }) {
104
+ const theme = useCodeTheme();
105
+ const streaming = useContext(CodeStreamingContext);
106
+ // What loaded last, so a block whose theme changes draws again once the new one is in.
107
+ const [loaded, setLoaded] = useState(null);
108
+ const tokenizer = tokenizerFor(lang, theme);
109
+ // Only a block that had to wait fades in; one drawn highlighted from its first frame just stands there.
110
+ const [waited] = useState(tokenizer === null);
111
+ const lines = useMemo(() => (tokenizer ? new IncrementalLines(tokenizer.tokenize) : null), [tokenizer]);
112
+ const tokens = useMemo(() => lines?.update(code, !streaming) ?? null, [lines, code, streaming]);
113
+ useEffect(() => {
114
+ if (tokenizer !== null) {
115
+ return;
116
+ }
117
+ let cancelled = false;
118
+ Promise.all([loadLanguage(lang), loadTheme(theme)])
119
+ .then(() => !cancelled && setLoaded(`${lang}:${theme}`))
120
+ .catch(() => !cancelled && setLoaded('failed'));
121
+ return () => {
122
+ cancelled = true;
123
+ };
124
+ }, [lang, theme, tokenizer]);
125
+ if (tokenizer === null || tokens === null) {
126
+ const failed = loaded === 'failed';
127
+ return (_jsx("div", { className: clsx('chat-code', !failed && 'invisible'), "aria-hidden": failed ? undefined : true, children: _jsx("pre", { children: _jsx("code", { children: code }) }) }));
128
+ }
129
+ return (_jsx("div", { className: clsx('chat-code', waited && WHOLE_FADE_CLASS), children: _jsx("pre", { style: { color: tokenizer.fg }, children: _jsx("code", { children: tokens.map((line, index) => (_jsxs(Fragment, { children: [_jsx(CodeLine, { tokens: line }), index < tokens.length - 1 && '\n'] }, index))) }) }) }));
130
+ }
@@ -0,0 +1,14 @@
1
+ import type { AgentKind, ChatInfo, ModelSelection } from '@ruimte/agent-contracts';
2
+ import type { ChatSendExtras } from '../chat-client';
3
+ interface ComposerProps {
4
+ chatId: string;
5
+ info: ChatInfo;
6
+ focused: boolean;
7
+ answerPromptsElsewhere?: () => void;
8
+ disabled: boolean;
9
+ providerFixed: boolean;
10
+ onSend(text: string, extras: ChatSendExtras): void;
11
+ onRetarget(provider: AgentKind, selection: ModelSelection): void;
12
+ }
13
+ export declare function Composer({ chatId, info, focused, answerPromptsElsewhere, disabled, providerFixed, onSend, onRetarget }: ComposerProps): import("react").JSX.Element;
14
+ export {};