@renxqoo/renx-code 0.0.3 → 0.0.4

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 (306) hide show
  1. package/README.md +58 -223
  2. package/bin/renx.cjs +34 -0
  3. package/package.json +27 -83
  4. package/src/App.tsx +297 -0
  5. package/src/agent/runtime/event-format.ts +258 -0
  6. package/src/agent/runtime/model-types.ts +13 -0
  7. package/src/agent/runtime/runtime.context-usage.test.ts +193 -0
  8. package/src/agent/runtime/runtime.error-handling.test.ts +236 -0
  9. package/src/agent/runtime/runtime.simple.test.ts +16 -0
  10. package/src/agent/runtime/runtime.test.ts +293 -0
  11. package/src/agent/runtime/runtime.ts +881 -0
  12. package/src/agent/runtime/runtime.usage-forwarding.test.ts +229 -0
  13. package/src/agent/runtime/source-modules.test.ts +57 -0
  14. package/src/agent/runtime/source-modules.ts +353 -0
  15. package/src/agent/runtime/tool-call-buffer.test.ts +65 -0
  16. package/src/agent/runtime/tool-call-buffer.ts +60 -0
  17. package/src/agent/runtime/tool-confirmation.test.ts +56 -0
  18. package/src/agent/runtime/tool-confirmation.ts +15 -0
  19. package/src/agent/runtime/types.ts +99 -0
  20. package/src/commands/slash-commands.test.ts +216 -0
  21. package/src/commands/slash-commands.ts +64 -0
  22. package/src/components/chat/assistant-reply.test.tsx +47 -0
  23. package/src/components/chat/assistant-reply.tsx +136 -0
  24. package/src/components/chat/assistant-segment.test.ts +99 -0
  25. package/src/components/chat/assistant-segment.tsx +125 -0
  26. package/src/components/chat/assistant-tool-group.tsx +900 -0
  27. package/src/components/chat/code-block.test.tsx +206 -0
  28. package/src/components/chat/code-block.tsx +313 -0
  29. package/src/components/chat/prompt-card.tsx +81 -0
  30. package/src/components/chat/segment-groups.test.ts +52 -0
  31. package/src/components/chat/segment-groups.ts +106 -0
  32. package/src/components/chat/turn-item.tsx +39 -0
  33. package/src/components/conversation-panel.tsx +43 -0
  34. package/src/components/file-mention-menu.tsx +77 -0
  35. package/src/components/file-picker-dialog.tsx +206 -0
  36. package/src/components/footer-hints.tsx +75 -0
  37. package/src/components/model-picker-dialog.tsx +248 -0
  38. package/src/components/prompt.tsx +233 -0
  39. package/src/components/slash-command-menu.tsx +65 -0
  40. package/src/components/tool-confirm-dialog-content.test.ts +103 -0
  41. package/src/components/tool-confirm-dialog-content.ts +186 -0
  42. package/src/components/tool-confirm-dialog.tsx +187 -0
  43. package/src/components/tool-display-config.ts +119 -0
  44. package/src/context-usage-regressions.test.ts +26 -0
  45. package/src/files/attachment-capabilities.test.ts +30 -0
  46. package/src/files/attachment-capabilities.ts +50 -0
  47. package/src/files/attachment-content.ts +153 -0
  48. package/src/files/file-mention-query.test.ts +34 -0
  49. package/src/files/file-mention-query.ts +32 -0
  50. package/src/files/prompt-display.ts +13 -0
  51. package/src/files/types.ts +5 -0
  52. package/src/files/workspace-files.ts +63 -0
  53. package/src/hooks/agent-event-handlers.test.ts +207 -0
  54. package/src/hooks/agent-event-handlers.ts +196 -0
  55. package/src/hooks/chat-local-replies.fixed.test.ts +119 -0
  56. package/src/hooks/chat-local-replies.test.ts +153 -0
  57. package/src/hooks/chat-local-replies.ts +63 -0
  58. package/src/hooks/turn-updater.test.ts +70 -0
  59. package/src/hooks/turn-updater.ts +166 -0
  60. package/src/hooks/use-agent-chat.context.test.ts +10 -0
  61. package/src/hooks/use-agent-chat.status.test.ts +14 -0
  62. package/src/hooks/use-agent-chat.test.ts +80 -0
  63. package/src/hooks/use-agent-chat.ts +621 -0
  64. package/src/hooks/use-file-mention-menu.ts +196 -0
  65. package/src/hooks/use-file-picker.ts +185 -0
  66. package/src/hooks/use-model-picker.ts +196 -0
  67. package/src/hooks/use-slash-command-menu.ts +154 -0
  68. package/src/index.tsx +55 -0
  69. package/src/runtime/clipboard.test.ts +43 -0
  70. package/src/runtime/clipboard.ts +89 -0
  71. package/src/runtime/exit.test.ts +177 -0
  72. package/src/runtime/exit.ts +98 -0
  73. package/src/runtime/runtime-support.test.ts +31 -0
  74. package/src/runtime/terminal-theme.test.ts +55 -0
  75. package/src/runtime/terminal-theme.ts +196 -0
  76. package/src/types/chat.ts +32 -0
  77. package/src/types/message-content.ts +48 -0
  78. package/src/ui/open-code-theme.ts +176 -0
  79. package/src/ui/opencode-markdown.ts +211 -0
  80. package/src/ui/theme.simple.test.ts +52 -0
  81. package/src/ui/theme.test.ts +151 -0
  82. package/src/ui/theme.ts +152 -0
  83. package/src/utils/time.test.ts +144 -0
  84. package/src/utils/time.ts +7 -0
  85. package/tsconfig.json +30 -0
  86. package/LICENSE +0 -21
  87. package/dist/App.d.ts +0 -2
  88. package/dist/App.d.ts.map +0 -1
  89. package/dist/App.js +0 -170
  90. package/dist/App.js.map +0 -1
  91. package/dist/agent/prompts/system.d.ts +0 -24
  92. package/dist/agent/prompts/system.d.ts.map +0 -1
  93. package/dist/agent/prompts/system.js +0 -222
  94. package/dist/agent/prompts/system.js.map +0 -1
  95. package/dist/agent/runtime/event-format.d.ts +0 -17
  96. package/dist/agent/runtime/event-format.d.ts.map +0 -1
  97. package/dist/agent/runtime/event-format.js +0 -194
  98. package/dist/agent/runtime/event-format.js.map +0 -1
  99. package/dist/agent/runtime/model-types.d.ts +0 -13
  100. package/dist/agent/runtime/model-types.d.ts.map +0 -1
  101. package/dist/agent/runtime/model-types.js +0 -1
  102. package/dist/agent/runtime/model-types.js.map +0 -1
  103. package/dist/agent/runtime/runtime.d.ts +0 -16
  104. package/dist/agent/runtime/runtime.d.ts.map +0 -1
  105. package/dist/agent/runtime/runtime.js +0 -691
  106. package/dist/agent/runtime/runtime.js.map +0 -1
  107. package/dist/agent/runtime/source-modules.d.ts +0 -176
  108. package/dist/agent/runtime/source-modules.d.ts.map +0 -1
  109. package/dist/agent/runtime/source-modules.js +0 -110
  110. package/dist/agent/runtime/source-modules.js.map +0 -1
  111. package/dist/agent/runtime/tool-call-buffer.d.ts +0 -12
  112. package/dist/agent/runtime/tool-call-buffer.d.ts.map +0 -1
  113. package/dist/agent/runtime/tool-call-buffer.js +0 -48
  114. package/dist/agent/runtime/tool-call-buffer.js.map +0 -1
  115. package/dist/agent/runtime/tool-confirmation.d.ts +0 -3
  116. package/dist/agent/runtime/tool-confirmation.d.ts.map +0 -1
  117. package/dist/agent/runtime/tool-confirmation.js +0 -9
  118. package/dist/agent/runtime/tool-confirmation.js.map +0 -1
  119. package/dist/agent/runtime/types.d.ts +0 -86
  120. package/dist/agent/runtime/types.d.ts.map +0 -1
  121. package/dist/agent/runtime/types.js +0 -1
  122. package/dist/agent/runtime/types.js.map +0 -1
  123. package/dist/cli.d.ts +0 -3
  124. package/dist/cli.d.ts.map +0 -1
  125. package/dist/cli.js +0 -12
  126. package/dist/cli.js.map +0 -1
  127. package/dist/commands/slash-commands.d.ts +0 -11
  128. package/dist/commands/slash-commands.d.ts.map +0 -1
  129. package/dist/commands/slash-commands.js +0 -48
  130. package/dist/commands/slash-commands.js.map +0 -1
  131. package/dist/components/chat/assistant-reply.d.ts +0 -13
  132. package/dist/components/chat/assistant-reply.d.ts.map +0 -1
  133. package/dist/components/chat/assistant-reply.js +0 -78
  134. package/dist/components/chat/assistant-reply.js.map +0 -1
  135. package/dist/components/chat/assistant-segment.d.ts +0 -8
  136. package/dist/components/chat/assistant-segment.d.ts.map +0 -1
  137. package/dist/components/chat/assistant-segment.js +0 -54
  138. package/dist/components/chat/assistant-segment.js.map +0 -1
  139. package/dist/components/chat/assistant-tool-group.d.ts +0 -7
  140. package/dist/components/chat/assistant-tool-group.d.ts.map +0 -1
  141. package/dist/components/chat/assistant-tool-group.js +0 -695
  142. package/dist/components/chat/assistant-tool-group.js.map +0 -1
  143. package/dist/components/chat/code-block.d.ts +0 -16
  144. package/dist/components/chat/code-block.d.ts.map +0 -1
  145. package/dist/components/chat/code-block.js +0 -194
  146. package/dist/components/chat/code-block.js.map +0 -1
  147. package/dist/components/chat/prompt-card.d.ts +0 -9
  148. package/dist/components/chat/prompt-card.d.ts.map +0 -1
  149. package/dist/components/chat/prompt-card.js +0 -18
  150. package/dist/components/chat/prompt-card.js.map +0 -1
  151. package/dist/components/chat/segment-groups.d.ts +0 -24
  152. package/dist/components/chat/segment-groups.d.ts.map +0 -1
  153. package/dist/components/chat/segment-groups.js +0 -69
  154. package/dist/components/chat/segment-groups.js.map +0 -1
  155. package/dist/components/chat/turn-item.d.ts +0 -9
  156. package/dist/components/chat/turn-item.d.ts.map +0 -1
  157. package/dist/components/chat/turn-item.js +0 -11
  158. package/dist/components/chat/turn-item.js.map +0 -1
  159. package/dist/components/conversation-panel.d.ts +0 -8
  160. package/dist/components/conversation-panel.d.ts.map +0 -1
  161. package/dist/components/conversation-panel.js +0 -8
  162. package/dist/components/conversation-panel.js.map +0 -1
  163. package/dist/components/file-mention-menu.d.ts +0 -11
  164. package/dist/components/file-mention-menu.d.ts.map +0 -1
  165. package/dist/components/file-mention-menu.js +0 -15
  166. package/dist/components/file-mention-menu.js.map +0 -1
  167. package/dist/components/file-picker-dialog.d.ts +0 -21
  168. package/dist/components/file-picker-dialog.d.ts.map +0 -1
  169. package/dist/components/file-picker-dialog.js +0 -48
  170. package/dist/components/file-picker-dialog.js.map +0 -1
  171. package/dist/components/footer-hints.d.ts +0 -7
  172. package/dist/components/footer-hints.d.ts.map +0 -1
  173. package/dist/components/footer-hints.js +0 -29
  174. package/dist/components/footer-hints.js.map +0 -1
  175. package/dist/components/model-picker-dialog.d.ts +0 -20
  176. package/dist/components/model-picker-dialog.d.ts.map +0 -1
  177. package/dist/components/model-picker-dialog.js +0 -72
  178. package/dist/components/model-picker-dialog.js.map +0 -1
  179. package/dist/components/prompt.d.ts +0 -18
  180. package/dist/components/prompt.d.ts.map +0 -1
  181. package/dist/components/prompt.js +0 -96
  182. package/dist/components/prompt.js.map +0 -1
  183. package/dist/components/slash-command-menu.d.ts +0 -9
  184. package/dist/components/slash-command-menu.d.ts.map +0 -1
  185. package/dist/components/slash-command-menu.js +0 -20
  186. package/dist/components/slash-command-menu.js.map +0 -1
  187. package/dist/components/tool-confirm-dialog-content.d.ts +0 -15
  188. package/dist/components/tool-confirm-dialog-content.d.ts.map +0 -1
  189. package/dist/components/tool-confirm-dialog-content.js +0 -143
  190. package/dist/components/tool-confirm-dialog-content.js.map +0 -1
  191. package/dist/components/tool-confirm-dialog.d.ts +0 -12
  192. package/dist/components/tool-confirm-dialog.d.ts.map +0 -1
  193. package/dist/components/tool-confirm-dialog.js +0 -21
  194. package/dist/components/tool-confirm-dialog.js.map +0 -1
  195. package/dist/components/tool-display-config.d.ts +0 -11
  196. package/dist/components/tool-display-config.d.ts.map +0 -1
  197. package/dist/components/tool-display-config.js +0 -94
  198. package/dist/components/tool-display-config.js.map +0 -1
  199. package/dist/config/paths.d.ts +0 -7
  200. package/dist/config/paths.d.ts.map +0 -1
  201. package/dist/config/paths.js +0 -24
  202. package/dist/config/paths.js.map +0 -1
  203. package/dist/files/attachment-capabilities.d.ts +0 -19
  204. package/dist/files/attachment-capabilities.d.ts.map +0 -1
  205. package/dist/files/attachment-capabilities.js +0 -26
  206. package/dist/files/attachment-capabilities.js.map +0 -1
  207. package/dist/files/attachment-content.d.ts +0 -5
  208. package/dist/files/attachment-content.d.ts.map +0 -1
  209. package/dist/files/attachment-content.js +0 -117
  210. package/dist/files/attachment-content.js.map +0 -1
  211. package/dist/files/file-mention-query.d.ts +0 -9
  212. package/dist/files/file-mention-query.d.ts.map +0 -1
  213. package/dist/files/file-mention-query.js +0 -23
  214. package/dist/files/file-mention-query.js.map +0 -1
  215. package/dist/files/prompt-display.d.ts +0 -3
  216. package/dist/files/prompt-display.d.ts.map +0 -1
  217. package/dist/files/prompt-display.js +0 -11
  218. package/dist/files/prompt-display.js.map +0 -1
  219. package/dist/files/types.d.ts +0 -6
  220. package/dist/files/types.d.ts.map +0 -1
  221. package/dist/files/types.js +0 -1
  222. package/dist/files/types.js.map +0 -1
  223. package/dist/files/workspace-files.d.ts +0 -3
  224. package/dist/files/workspace-files.d.ts.map +0 -1
  225. package/dist/files/workspace-files.js +0 -48
  226. package/dist/files/workspace-files.js.map +0 -1
  227. package/dist/hooks/agent-event-handlers.d.ts +0 -11
  228. package/dist/hooks/agent-event-handlers.d.ts.map +0 -1
  229. package/dist/hooks/agent-event-handlers.js +0 -137
  230. package/dist/hooks/agent-event-handlers.js.map +0 -1
  231. package/dist/hooks/chat-local-replies.d.ts +0 -9
  232. package/dist/hooks/chat-local-replies.d.ts.map +0 -1
  233. package/dist/hooks/chat-local-replies.js +0 -54
  234. package/dist/hooks/chat-local-replies.js.map +0 -1
  235. package/dist/hooks/turn-updater.d.ts +0 -9
  236. package/dist/hooks/turn-updater.d.ts.map +0 -1
  237. package/dist/hooks/turn-updater.js +0 -103
  238. package/dist/hooks/turn-updater.js.map +0 -1
  239. package/dist/hooks/use-agent-chat.d.ts +0 -29
  240. package/dist/hooks/use-agent-chat.d.ts.map +0 -1
  241. package/dist/hooks/use-agent-chat.js +0 -455
  242. package/dist/hooks/use-agent-chat.js.map +0 -1
  243. package/dist/hooks/use-file-mention-menu.d.ts +0 -22
  244. package/dist/hooks/use-file-mention-menu.d.ts.map +0 -1
  245. package/dist/hooks/use-file-mention-menu.js +0 -137
  246. package/dist/hooks/use-file-mention-menu.js.map +0 -1
  247. package/dist/hooks/use-file-picker.d.ts +0 -21
  248. package/dist/hooks/use-file-picker.d.ts.map +0 -1
  249. package/dist/hooks/use-file-picker.js +0 -145
  250. package/dist/hooks/use-file-picker.js.map +0 -1
  251. package/dist/hooks/use-model-picker.d.ts +0 -23
  252. package/dist/hooks/use-model-picker.d.ts.map +0 -1
  253. package/dist/hooks/use-model-picker.js +0 -151
  254. package/dist/hooks/use-model-picker.js.map +0 -1
  255. package/dist/hooks/use-slash-command-menu.d.ts +0 -19
  256. package/dist/hooks/use-slash-command-menu.d.ts.map +0 -1
  257. package/dist/hooks/use-slash-command-menu.js +0 -101
  258. package/dist/hooks/use-slash-command-menu.js.map +0 -1
  259. package/dist/index.d.ts +0 -2
  260. package/dist/index.d.ts.map +0 -1
  261. package/dist/index.js +0 -6
  262. package/dist/index.js.map +0 -1
  263. package/dist/run-cli-app.d.ts +0 -2
  264. package/dist/run-cli-app.d.ts.map +0 -1
  265. package/dist/run-cli-app.js +0 -41
  266. package/dist/run-cli-app.js.map +0 -1
  267. package/dist/runtime/clipboard.d.ts +0 -10
  268. package/dist/runtime/clipboard.d.ts.map +0 -1
  269. package/dist/runtime/clipboard.js +0 -64
  270. package/dist/runtime/clipboard.js.map +0 -1
  271. package/dist/runtime/exit.d.ts +0 -7
  272. package/dist/runtime/exit.d.ts.map +0 -1
  273. package/dist/runtime/exit.js +0 -85
  274. package/dist/runtime/exit.js.map +0 -1
  275. package/dist/runtime/runtime-support.d.ts +0 -4
  276. package/dist/runtime/runtime-support.d.ts.map +0 -1
  277. package/dist/runtime/runtime-support.js +0 -19
  278. package/dist/runtime/runtime-support.js.map +0 -1
  279. package/dist/runtime/terminal-theme.d.ts +0 -25
  280. package/dist/runtime/terminal-theme.d.ts.map +0 -1
  281. package/dist/runtime/terminal-theme.js +0 -148
  282. package/dist/runtime/terminal-theme.js.map +0 -1
  283. package/dist/types/chat.d.ts +0 -29
  284. package/dist/types/chat.d.ts.map +0 -1
  285. package/dist/types/chat.js +0 -1
  286. package/dist/types/chat.js.map +0 -1
  287. package/dist/types/message-content.d.ts +0 -38
  288. package/dist/types/message-content.d.ts.map +0 -1
  289. package/dist/types/message-content.js +0 -1
  290. package/dist/types/message-content.js.map +0 -1
  291. package/dist/ui/open-code-theme.d.ts +0 -58
  292. package/dist/ui/open-code-theme.d.ts.map +0 -1
  293. package/dist/ui/open-code-theme.js +0 -113
  294. package/dist/ui/open-code-theme.js.map +0 -1
  295. package/dist/ui/opencode-markdown.d.ts +0 -7
  296. package/dist/ui/opencode-markdown.d.ts.map +0 -1
  297. package/dist/ui/opencode-markdown.js +0 -169
  298. package/dist/ui/opencode-markdown.js.map +0 -1
  299. package/dist/ui/theme.d.ts +0 -68
  300. package/dist/ui/theme.d.ts.map +0 -1
  301. package/dist/ui/theme.js +0 -80
  302. package/dist/ui/theme.js.map +0 -1
  303. package/dist/utils/time.d.ts +0 -2
  304. package/dist/utils/time.d.ts.map +0 -1
  305. package/dist/utils/time.js +0 -7
  306. package/dist/utils/time.js.map +0 -1
@@ -0,0 +1,196 @@
1
+ /* global NodeJS */
2
+
3
+ export type RgbColor = {
4
+ r: number;
5
+ g: number;
6
+ b: number;
7
+ };
8
+
9
+ export type TerminalBackgroundMode = 'dark' | 'light';
10
+ export type TerminalBackgroundProbe = {
11
+ mode: TerminalBackgroundMode;
12
+ rawColor: string | null;
13
+ };
14
+ export type TerminalColorProbe = {
15
+ rawBackgroundColor: string | null;
16
+ rawForegroundColor: string | null;
17
+ mode: TerminalBackgroundMode;
18
+ };
19
+
20
+ // eslint-disable-next-line no-control-regex
21
+ const OSC10_PATTERN = /\x1b]10;([^\x07\x1b]+)/;
22
+ // eslint-disable-next-line no-control-regex
23
+ const OSC11_PATTERN = /\x1b]11;([^\x07\x1b]+)/;
24
+
25
+ const wrapOscForTmux = (osc: string) => {
26
+ if (!process.env['TMUX']) {
27
+ return osc;
28
+ }
29
+
30
+ return `\x1bPtmux;\x1b${osc}\x1b\\`;
31
+ };
32
+
33
+ const writeOsc = (osc: string) => {
34
+ if (!process.stdout.isTTY) {
35
+ return;
36
+ }
37
+
38
+ try {
39
+ process.stdout.write(wrapOscForTmux(osc));
40
+ } catch {
41
+ // Ignore errors when writing to stdout
42
+ }
43
+ };
44
+
45
+ export const parseTerminalColor = (value: string): RgbColor | null => {
46
+ if (value.startsWith('rgb:')) {
47
+ const parts = value.slice(4).split('/');
48
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
49
+ return null;
50
+ }
51
+
52
+ const [rHex, gHex, bHex] = parts;
53
+ const r16 = Number.parseInt(rHex, 16);
54
+ const g16 = Number.parseInt(gHex, 16);
55
+ const b16 = Number.parseInt(bHex, 16);
56
+ if (Number.isNaN(r16) || Number.isNaN(g16) || Number.isNaN(b16)) {
57
+ return null;
58
+ }
59
+
60
+ return {
61
+ r: r16 >> 8,
62
+ g: g16 >> 8,
63
+ b: b16 >> 8,
64
+ };
65
+ }
66
+
67
+ if (/^#[0-9a-fA-F]{6}$/.test(value)) {
68
+ return {
69
+ r: Number.parseInt(value.slice(1, 3), 16),
70
+ g: Number.parseInt(value.slice(3, 5), 16),
71
+ b: Number.parseInt(value.slice(5, 7), 16),
72
+ };
73
+ }
74
+
75
+ const rgbMatch = value.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/);
76
+ if (rgbMatch && rgbMatch[1] && rgbMatch[2] && rgbMatch[3]) {
77
+ const r = Number.parseInt(rgbMatch[1], 10);
78
+ const g = Number.parseInt(rgbMatch[2], 10);
79
+ const b = Number.parseInt(rgbMatch[3], 10);
80
+ if (r > 255 || g > 255 || b > 255) {
81
+ return null;
82
+ }
83
+
84
+ return { r, g, b };
85
+ }
86
+
87
+ return null;
88
+ };
89
+
90
+ export const modeFromColor = ({ r, g, b }: RgbColor): TerminalBackgroundMode => {
91
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
92
+ return luminance > 0.5 ? 'light' : 'dark';
93
+ };
94
+
95
+ export const extractOsc10Color = (chunk: string): string | null => {
96
+ const match = chunk.match(OSC10_PATTERN);
97
+ return match?.[1] ?? null;
98
+ };
99
+
100
+ export const extractOsc11Color = (chunk: string): string | null => {
101
+ const match = chunk.match(OSC11_PATTERN);
102
+ return match?.[1] ?? null;
103
+ };
104
+
105
+ export const setTerminalWindowForeground = (color: string) => {
106
+ writeOsc(`\x1b]10;${color}\x07`);
107
+ };
108
+
109
+ export const setTerminalWindowBackground = (color: string) => {
110
+ writeOsc(`\x1b]11;${color}\x07`);
111
+ };
112
+
113
+ export const probeTerminalColors = async (timeoutMs = 1000): Promise<TerminalColorProbe> => {
114
+ if (
115
+ !process.stdin.isTTY ||
116
+ !process.stdout.isTTY ||
117
+ typeof process.stdin.setRawMode !== 'function'
118
+ ) {
119
+ return { mode: 'dark', rawBackgroundColor: null, rawForegroundColor: null };
120
+ }
121
+
122
+ return new Promise(resolve => {
123
+ const stdin = process.stdin;
124
+ const previousRawMode = Boolean(stdin.isRaw);
125
+ let timer: NodeJS.Timeout | null = null;
126
+ let finished = false;
127
+ let rawBackgroundColor: string | null = null;
128
+ let rawForegroundColor: string | null = null;
129
+
130
+ const finish = () => {
131
+ if (finished) {
132
+ return;
133
+ }
134
+ finished = true;
135
+
136
+ if (timer) {
137
+ clearTimeout(timer);
138
+ }
139
+
140
+ stdin.removeListener('data', onData);
141
+ try {
142
+ stdin.setRawMode(previousRawMode);
143
+ } catch {
144
+ // Ignore errors when restoring raw mode
145
+ }
146
+
147
+ const color = rawBackgroundColor ? parseTerminalColor(rawBackgroundColor) : null;
148
+ resolve({
149
+ rawBackgroundColor,
150
+ rawForegroundColor,
151
+ mode: color ? modeFromColor(color) : 'dark',
152
+ });
153
+ };
154
+
155
+ const onData = (data: Buffer) => {
156
+ const chunk = data.toString();
157
+ rawForegroundColor ??= extractOsc10Color(chunk);
158
+ rawBackgroundColor ??= extractOsc11Color(chunk);
159
+
160
+ if (rawForegroundColor && rawBackgroundColor) {
161
+ finish();
162
+ }
163
+ };
164
+
165
+ try {
166
+ stdin.setRawMode(true);
167
+ stdin.on('data', onData);
168
+ writeOsc('\x1b]10;?\x07');
169
+ writeOsc('\x1b]11;?\x07');
170
+ } catch {
171
+ finish();
172
+ return;
173
+ }
174
+
175
+ timer = setTimeout(() => {
176
+ finish();
177
+ }, timeoutMs);
178
+ });
179
+ };
180
+
181
+ export const probeTerminalBackground = async (
182
+ timeoutMs = 1000
183
+ ): Promise<TerminalBackgroundProbe> => {
184
+ const result = await probeTerminalColors(timeoutMs);
185
+ return {
186
+ rawColor: result.rawBackgroundColor,
187
+ mode: result.mode,
188
+ };
189
+ };
190
+
191
+ export const detectTerminalBackgroundMode = async (
192
+ timeoutMs = 1000
193
+ ): Promise<TerminalBackgroundMode> => {
194
+ const result = await probeTerminalColors(timeoutMs);
195
+ return result.mode;
196
+ };
@@ -0,0 +1,32 @@
1
+ export type ReplySegmentType = 'thinking' | 'text' | 'code' | 'note';
2
+
3
+ export type ReplySegment = {
4
+ id: string;
5
+ type: ReplySegmentType;
6
+ content: string;
7
+ data?: unknown;
8
+ };
9
+
10
+ export type ReplyStatus = 'streaming' | 'done' | 'error';
11
+
12
+ export type AssistantReply = {
13
+ segments: ReplySegment[];
14
+ modelLabel: string;
15
+ agentLabel: string;
16
+ startedAtMs?: number;
17
+ durationSeconds: number;
18
+ usagePromptTokens?: number;
19
+ usageCompletionTokens?: number;
20
+ usageTotalTokens?: number;
21
+ status: ReplyStatus;
22
+ completionReason?: string;
23
+ completionMessage?: string;
24
+ };
25
+
26
+ export type ChatTurn = {
27
+ id: number;
28
+ prompt: string;
29
+ createdAtMs: number;
30
+ files?: string[];
31
+ reply?: AssistantReply;
32
+ };
@@ -0,0 +1,48 @@
1
+ export type TextContentPart = {
2
+ type: 'text';
3
+ text: string;
4
+ };
5
+
6
+ export type ImageUrlContentPart = {
7
+ type: 'image_url';
8
+ image_url: {
9
+ url: string;
10
+ detail?: 'auto' | 'low' | 'high';
11
+ };
12
+ };
13
+
14
+ export type InputAudioContentPart = {
15
+ type: 'input_audio';
16
+ input_audio: {
17
+ data: string;
18
+ format: 'wav' | 'mp3';
19
+ };
20
+ };
21
+
22
+ export type InputVideoContentPart = {
23
+ type: 'input_video';
24
+ input_video: {
25
+ url?: string;
26
+ file_id?: string;
27
+ data?: string;
28
+ format?: 'mp4' | 'mov' | 'webm';
29
+ };
30
+ };
31
+
32
+ export type FileContentPart = {
33
+ type: 'file';
34
+ file: {
35
+ file_id?: string;
36
+ file_data?: string;
37
+ filename?: string;
38
+ };
39
+ };
40
+
41
+ export type InputContentPart =
42
+ | TextContentPart
43
+ | ImageUrlContentPart
44
+ | InputAudioContentPart
45
+ | InputVideoContentPart
46
+ | FileContentPart;
47
+
48
+ export type MessageContent = string | InputContentPart[];
@@ -0,0 +1,176 @@
1
+ import { RGBA } from '@opentui/core';
2
+
3
+ export type OpenCodeThemeMode = 'dark' | 'light';
4
+ export type MarkdownThemePlatform = NodeJS.Platform;
5
+
6
+ export type OpenCodeTheme = {
7
+ primary: RGBA;
8
+ secondary: RGBA;
9
+ accent: RGBA;
10
+ error: RGBA;
11
+ warning: RGBA;
12
+ success: RGBA;
13
+ info: RGBA;
14
+ text: RGBA;
15
+ textMuted: RGBA;
16
+ background: RGBA;
17
+ backgroundPanel: RGBA;
18
+ backgroundElement: RGBA;
19
+ border: RGBA;
20
+ borderActive: RGBA;
21
+ borderSubtle: RGBA;
22
+ diffAdded: RGBA;
23
+ diffRemoved: RGBA;
24
+ diffContext: RGBA;
25
+ diffHunkHeader: RGBA;
26
+ diffHighlightAdded: RGBA;
27
+ diffHighlightRemoved: RGBA;
28
+ diffAddedBg: RGBA;
29
+ diffRemovedBg: RGBA;
30
+ diffContextBg: RGBA;
31
+ diffLineNumber: RGBA;
32
+ diffAddedLineNumberBg: RGBA;
33
+ diffRemovedLineNumberBg: RGBA;
34
+ markdownText: RGBA;
35
+ markdownHeading: RGBA;
36
+ markdownLink: RGBA;
37
+ markdownLinkText: RGBA;
38
+ markdownCode: RGBA;
39
+ markdownBlockQuote: RGBA;
40
+ markdownEmph: RGBA;
41
+ markdownStrong: RGBA;
42
+ markdownHorizontalRule: RGBA;
43
+ markdownListItem: RGBA;
44
+ markdownListEnumeration: RGBA;
45
+ markdownImage: RGBA;
46
+ markdownImageText: RGBA;
47
+ markdownCodeBlock: RGBA;
48
+ syntaxComment: RGBA;
49
+ syntaxKeyword: RGBA;
50
+ syntaxFunction: RGBA;
51
+ syntaxVariable: RGBA;
52
+ syntaxString: RGBA;
53
+ syntaxNumber: RGBA;
54
+ syntaxType: RGBA;
55
+ syntaxOperator: RGBA;
56
+ syntaxPunctuation: RGBA;
57
+ thinkingOpacity: number;
58
+ };
59
+
60
+ const color = (hex: string) => RGBA.fromHex(hex);
61
+
62
+ const OPEN_CODE_DARK_THEME: OpenCodeTheme = {
63
+ primary: color('#8e51ff'),
64
+ secondary: color('#27272a'),
65
+ accent: color('#8e51ff'),
66
+ error: color('#ff6467'),
67
+ warning: color('#c4b4ff'),
68
+ success: color('#c4b4ff'),
69
+ info: color('#a684ff'),
70
+ text: color('#fafafa'),
71
+ textMuted: color('#9f9fa9'),
72
+ background: color('#09090b'),
73
+ backgroundPanel: color('#18181b'),
74
+ backgroundElement: color('#27272a'),
75
+ border: color('#ffffff1a'),
76
+ borderActive: color('#4d179a'),
77
+ borderSubtle: color('#ffffff1a'),
78
+ diffAdded: color('#8e51ff'),
79
+ diffRemoved: color('#ff6467'),
80
+ diffContext: color('#9f9fa9'),
81
+ diffHunkHeader: color('#a684ff'),
82
+ diffHighlightAdded: color('#c4b4ff'),
83
+ diffHighlightRemoved: color('#ff8d8f'),
84
+ diffAddedBg: color('#8e51ff1f'),
85
+ diffRemovedBg: color('#ff646724'),
86
+ diffContextBg: color('#27272a'),
87
+ diffLineNumber: color('#9f9fa9'),
88
+ diffAddedLineNumberBg: color('#4d179a66'),
89
+ diffRemovedLineNumberBg: color('#ff646740'),
90
+ markdownText: color('#fafafa'),
91
+ markdownHeading: color('#c4b4ff'),
92
+ markdownLink: color('#8e51ff'),
93
+ markdownLinkText: color('#a684ff'),
94
+ markdownCode: color('#c4b4ff'),
95
+ markdownBlockQuote: color('#a684ff'),
96
+ markdownEmph: color('#c4b4ff'),
97
+ markdownStrong: color('#f5f3ff'),
98
+ markdownHorizontalRule: color('#9f9fa9'),
99
+ markdownListItem: color('#c4b4ff'),
100
+ markdownListEnumeration: color('#a684ff'),
101
+ markdownImage: color('#8e51ff'),
102
+ markdownImageText: color('#a684ff'),
103
+ markdownCodeBlock: color('#fafafa'),
104
+ syntaxComment: color('#9f9fa9'),
105
+ syntaxKeyword: color('#8e51ff'),
106
+ syntaxFunction: color('#c4b4ff'),
107
+ syntaxVariable: color('#f5f3ff'),
108
+ syntaxString: color('#c4b4ff'),
109
+ syntaxNumber: color('#a684ff'),
110
+ syntaxType: color('#8e51ff'),
111
+ syntaxOperator: color('#7008e7'),
112
+ syntaxPunctuation: color('#fafafa'),
113
+ thinkingOpacity: 0.74,
114
+ };
115
+
116
+ const OPEN_CODE_LIGHT_THEME: OpenCodeTheme = {
117
+ primary: color('#7f22fe'),
118
+ secondary: color('#f4f4f5'),
119
+ accent: color('#7f22fe'),
120
+ error: color('#e7000b'),
121
+ warning: color('#8e51ff'),
122
+ success: color('#8e51ff'),
123
+ info: color('#a684ff'),
124
+ text: color('#09090b'),
125
+ textMuted: color('#71717b'),
126
+ background: color('#ffffff'),
127
+ backgroundPanel: color('#ffffff'),
128
+ backgroundElement: color('#f4f4f5'),
129
+ border: color('#e4e4e7'),
130
+ borderActive: color('#a684ff'),
131
+ borderSubtle: color('#e4e4e7'),
132
+ diffAdded: color('#7f22fe'),
133
+ diffRemoved: color('#e7000b'),
134
+ diffContext: color('#71717b'),
135
+ diffHunkHeader: color('#a684ff'),
136
+ diffHighlightAdded: color('#5d0ec0'),
137
+ diffHighlightRemoved: color('#e7000b'),
138
+ diffAddedBg: color('#f5f3ff'),
139
+ diffRemovedBg: color('#e7000b14'),
140
+ diffContextBg: color('#f4f4f5'),
141
+ diffLineNumber: color('#71717b'),
142
+ diffAddedLineNumberBg: color('#c4b4ff80'),
143
+ diffRemovedLineNumberBg: color('#e7000b26'),
144
+ markdownText: color('#09090b'),
145
+ markdownHeading: color('#7f22fe'),
146
+ markdownLink: color('#7f22fe'),
147
+ markdownLinkText: color('#8e51ff'),
148
+ markdownCode: color('#5d0ec0'),
149
+ markdownBlockQuote: color('#8e51ff'),
150
+ markdownEmph: color('#7008e7'),
151
+ markdownStrong: color('#18181b'),
152
+ markdownHorizontalRule: color('#71717b'),
153
+ markdownListItem: color('#7f22fe'),
154
+ markdownListEnumeration: color('#8e51ff'),
155
+ markdownImage: color('#7f22fe'),
156
+ markdownImageText: color('#8e51ff'),
157
+ markdownCodeBlock: color('#09090b'),
158
+ syntaxComment: color('#71717b'),
159
+ syntaxKeyword: color('#7f22fe'),
160
+ syntaxFunction: color('#5d0ec0'),
161
+ syntaxVariable: color('#18181b'),
162
+ syntaxString: color('#8e51ff'),
163
+ syntaxNumber: color('#7008e7'),
164
+ syntaxType: color('#7f22fe'),
165
+ syntaxOperator: color('#7008e7'),
166
+ syntaxPunctuation: color('#09090b'),
167
+ thinkingOpacity: 0.9,
168
+ };
169
+
170
+ export const resolveOpenCodeTheme = (
171
+ mode: OpenCodeThemeMode,
172
+ platform: MarkdownThemePlatform
173
+ ): OpenCodeTheme => {
174
+ void platform;
175
+ return mode === 'light' ? OPEN_CODE_LIGHT_THEME : OPEN_CODE_DARK_THEME;
176
+ };
@@ -0,0 +1,211 @@
1
+ import { RGBA, SyntaxStyle } from '@opentui/core';
2
+ import {
3
+ resolveOpenCodeTheme,
4
+ type MarkdownThemePlatform,
5
+ type OpenCodeTheme,
6
+ } from './open-code-theme';
7
+ import type { UiThemeMode } from './theme';
8
+
9
+ type SyntaxRule = {
10
+ scope: string[];
11
+ style: {
12
+ foreground?: RGBA;
13
+ background?: RGBA;
14
+ bold?: boolean;
15
+ italic?: boolean;
16
+ underline?: boolean;
17
+ };
18
+ };
19
+
20
+ type SyntaxRenderOptions = {
21
+ platform: MarkdownThemePlatform;
22
+ };
23
+
24
+ const getSyntaxRules = (theme: OpenCodeTheme, options: SyntaxRenderOptions): SyntaxRule[] => {
25
+ const useItalic = options.platform !== 'darwin';
26
+ const useUnderline = options.platform !== 'darwin';
27
+
28
+ return [
29
+ { scope: ['default'], style: { foreground: theme.text } },
30
+ { scope: ['prompt'], style: { foreground: theme.accent } },
31
+ { scope: ['extmark.file'], style: { foreground: theme.warning, bold: true } },
32
+ { scope: ['extmark.agent'], style: { foreground: theme.secondary, bold: true } },
33
+ {
34
+ scope: ['extmark.paste'],
35
+ style: { foreground: theme.background, background: theme.warning, bold: true },
36
+ },
37
+ { scope: ['comment'], style: { foreground: theme.syntaxComment, italic: useItalic } },
38
+ {
39
+ scope: ['comment.documentation'],
40
+ style: { foreground: theme.syntaxComment, italic: useItalic },
41
+ },
42
+ { scope: ['string', 'symbol'], style: { foreground: theme.syntaxString } },
43
+ { scope: ['number', 'boolean'], style: { foreground: theme.syntaxNumber } },
44
+ { scope: ['character.special'], style: { foreground: theme.syntaxString } },
45
+ {
46
+ scope: ['keyword.return', 'keyword.conditional', 'keyword.repeat', 'keyword.coroutine'],
47
+ style: { foreground: theme.syntaxKeyword, italic: useItalic },
48
+ },
49
+ {
50
+ scope: ['keyword.type'],
51
+ style: { foreground: theme.syntaxType, bold: true, italic: useItalic },
52
+ },
53
+ { scope: ['keyword.function', 'function.method'], style: { foreground: theme.syntaxFunction } },
54
+ { scope: ['keyword'], style: { foreground: theme.syntaxKeyword, italic: useItalic } },
55
+ { scope: ['keyword.import'], style: { foreground: theme.syntaxKeyword } },
56
+ {
57
+ scope: ['operator', 'keyword.operator', 'punctuation.delimiter'],
58
+ style: { foreground: theme.syntaxOperator },
59
+ },
60
+ { scope: ['keyword.conditional.ternary'], style: { foreground: theme.syntaxOperator } },
61
+ {
62
+ scope: ['variable', 'variable.parameter', 'function.method.call', 'function.call'],
63
+ style: { foreground: theme.syntaxVariable },
64
+ },
65
+ {
66
+ scope: ['variable.member', 'function', 'constructor'],
67
+ style: { foreground: theme.syntaxFunction },
68
+ },
69
+ { scope: ['type', 'module'], style: { foreground: theme.syntaxType } },
70
+ { scope: ['constant'], style: { foreground: theme.syntaxNumber } },
71
+ { scope: ['property'], style: { foreground: theme.syntaxVariable } },
72
+ { scope: ['class'], style: { foreground: theme.syntaxType } },
73
+ { scope: ['parameter'], style: { foreground: theme.syntaxVariable } },
74
+ {
75
+ scope: ['punctuation', 'punctuation.bracket'],
76
+ style: { foreground: theme.syntaxPunctuation },
77
+ },
78
+ {
79
+ scope: [
80
+ 'variable.builtin',
81
+ 'type.builtin',
82
+ 'function.builtin',
83
+ 'module.builtin',
84
+ 'constant.builtin',
85
+ ],
86
+ style: { foreground: theme.error },
87
+ },
88
+ { scope: ['variable.super'], style: { foreground: theme.error } },
89
+ { scope: ['string.escape', 'string.regexp'], style: { foreground: theme.syntaxKeyword } },
90
+ { scope: ['keyword.directive'], style: { foreground: theme.syntaxKeyword, italic: useItalic } },
91
+ { scope: ['punctuation.special'], style: { foreground: theme.syntaxOperator } },
92
+ { scope: ['keyword.modifier'], style: { foreground: theme.syntaxKeyword, italic: useItalic } },
93
+ { scope: ['keyword.exception'], style: { foreground: theme.syntaxKeyword, italic: useItalic } },
94
+ { scope: ['markup.heading'], style: { foreground: theme.markdownHeading, bold: true } },
95
+ { scope: ['markup.heading.1'], style: { foreground: theme.markdownHeading, bold: true } },
96
+ { scope: ['markup.heading.2'], style: { foreground: theme.markdownHeading, bold: true } },
97
+ { scope: ['markup.heading.3'], style: { foreground: theme.markdownHeading, bold: true } },
98
+ { scope: ['markup.heading.4'], style: { foreground: theme.markdownHeading, bold: true } },
99
+ { scope: ['markup.heading.5'], style: { foreground: theme.markdownHeading, bold: true } },
100
+ { scope: ['markup.heading.6'], style: { foreground: theme.markdownHeading, bold: true } },
101
+ {
102
+ scope: ['markup.bold', 'markup.strong'],
103
+ style: { foreground: theme.markdownStrong, bold: true },
104
+ },
105
+ { scope: ['markup.italic'], style: { foreground: theme.markdownEmph, italic: useItalic } },
106
+ { scope: ['markup.list'], style: { foreground: theme.markdownListItem } },
107
+ { scope: ['markup.quote'], style: { foreground: theme.markdownBlockQuote, italic: useItalic } },
108
+ { scope: ['markup.raw', 'markup.raw.block'], style: { foreground: theme.markdownCode } },
109
+ {
110
+ scope: ['markup.raw.inline'],
111
+ style: { foreground: theme.markdownCode, background: theme.background },
112
+ },
113
+ { scope: ['markup.link'], style: { foreground: theme.markdownLink, underline: useUnderline } },
114
+ {
115
+ scope: ['markup.link.label'],
116
+ style: { foreground: theme.markdownLinkText, underline: useUnderline },
117
+ },
118
+ {
119
+ scope: ['markup.link.url'],
120
+ style: { foreground: theme.markdownLink, underline: useUnderline },
121
+ },
122
+ { scope: ['label'], style: { foreground: theme.markdownLinkText } },
123
+ { scope: ['spell', 'nospell'], style: { foreground: theme.text } },
124
+ { scope: ['conceal'], style: { foreground: theme.textMuted } },
125
+ {
126
+ scope: ['string.special', 'string.special.url'],
127
+ style: { foreground: theme.markdownLink, underline: useUnderline },
128
+ },
129
+ { scope: ['character'], style: { foreground: theme.syntaxString } },
130
+ { scope: ['float'], style: { foreground: theme.syntaxNumber } },
131
+ { scope: ['comment.error'], style: { foreground: theme.error, italic: useItalic, bold: true } },
132
+ {
133
+ scope: ['comment.warning'],
134
+ style: { foreground: theme.warning, italic: useItalic, bold: true },
135
+ },
136
+ {
137
+ scope: ['comment.todo', 'comment.note'],
138
+ style: { foreground: theme.info, italic: useItalic, bold: true },
139
+ },
140
+ { scope: ['namespace'], style: { foreground: theme.syntaxType } },
141
+ { scope: ['field'], style: { foreground: theme.syntaxVariable } },
142
+ { scope: ['type.definition'], style: { foreground: theme.syntaxType, bold: true } },
143
+ { scope: ['keyword.export'], style: { foreground: theme.syntaxKeyword } },
144
+ { scope: ['attribute', 'annotation'], style: { foreground: theme.warning } },
145
+ { scope: ['tag'], style: { foreground: theme.error } },
146
+ { scope: ['tag.attribute'], style: { foreground: theme.syntaxKeyword } },
147
+ { scope: ['tag.delimiter'], style: { foreground: theme.syntaxOperator } },
148
+ { scope: ['markup.strikethrough'], style: { foreground: theme.textMuted } },
149
+ { scope: ['markup.underline'], style: { foreground: theme.text, underline: useUnderline } },
150
+ { scope: ['markup.list.checked'], style: { foreground: theme.success } },
151
+ { scope: ['markup.list.unchecked'], style: { foreground: theme.textMuted } },
152
+ { scope: ['diff.plus'], style: { foreground: theme.diffAdded, background: theme.diffAddedBg } },
153
+ {
154
+ scope: ['diff.minus'],
155
+ style: { foreground: theme.diffRemoved, background: theme.diffRemovedBg },
156
+ },
157
+ {
158
+ scope: ['diff.delta'],
159
+ style: { foreground: theme.diffContext, background: theme.diffContextBg },
160
+ },
161
+ { scope: ['error'], style: { foreground: theme.error, bold: true } },
162
+ { scope: ['warning'], style: { foreground: theme.warning, bold: true } },
163
+ { scope: ['info'], style: { foreground: theme.info } },
164
+ { scope: ['debug'], style: { foreground: theme.textMuted } },
165
+ ];
166
+ };
167
+
168
+ const applyAlpha = (fg: RGBA, alpha: number) => {
169
+ return RGBA.fromInts(
170
+ Math.round(fg.r * 255),
171
+ Math.round(fg.g * 255),
172
+ Math.round(fg.b * 255),
173
+ Math.round(alpha * 255)
174
+ );
175
+ };
176
+
177
+ const createMarkdownSyntax = (theme: OpenCodeTheme, platform: MarkdownThemePlatform) =>
178
+ SyntaxStyle.fromTheme(getSyntaxRules(theme, { platform }));
179
+
180
+ const createSubtleMarkdownSyntax = (theme: OpenCodeTheme, platform: MarkdownThemePlatform) =>
181
+ SyntaxStyle.fromTheme(
182
+ getSyntaxRules(theme, { platform }).map(rule => {
183
+ if (!rule.style.foreground) {
184
+ return rule;
185
+ }
186
+ return {
187
+ ...rule,
188
+ style: {
189
+ ...rule.style,
190
+ foreground: applyAlpha(rule.style.foreground, theme.thinkingOpacity),
191
+ },
192
+ };
193
+ })
194
+ );
195
+
196
+ const defaultMarkdownTheme = resolveOpenCodeTheme('dark', process.platform);
197
+
198
+ export let opencodeMarkdownSyntax = createMarkdownSyntax(defaultMarkdownTheme, process.platform);
199
+ export let opencodeSubtleMarkdownSyntax = createSubtleMarkdownSyntax(
200
+ defaultMarkdownTheme,
201
+ process.platform
202
+ );
203
+
204
+ export const applyMarkdownThemeMode = (
205
+ mode: UiThemeMode,
206
+ platform: MarkdownThemePlatform = process.platform
207
+ ) => {
208
+ const theme = resolveOpenCodeTheme(mode, platform);
209
+ opencodeMarkdownSyntax = createMarkdownSyntax(theme, platform);
210
+ opencodeSubtleMarkdownSyntax = createSubtleMarkdownSyntax(theme, platform);
211
+ };