@renxqoo/renx-code 0.0.2 → 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 (298) hide show
  1. package/README.md +59 -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 -43
  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 -50
  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 -39
  262. package/dist/index.js.map +0 -1
  263. package/dist/runtime/clipboard.d.ts +0 -10
  264. package/dist/runtime/clipboard.d.ts.map +0 -1
  265. package/dist/runtime/clipboard.js +0 -64
  266. package/dist/runtime/clipboard.js.map +0 -1
  267. package/dist/runtime/exit.d.ts +0 -7
  268. package/dist/runtime/exit.d.ts.map +0 -1
  269. package/dist/runtime/exit.js +0 -85
  270. package/dist/runtime/exit.js.map +0 -1
  271. package/dist/runtime/terminal-theme.d.ts +0 -25
  272. package/dist/runtime/terminal-theme.d.ts.map +0 -1
  273. package/dist/runtime/terminal-theme.js +0 -148
  274. package/dist/runtime/terminal-theme.js.map +0 -1
  275. package/dist/types/chat.d.ts +0 -29
  276. package/dist/types/chat.d.ts.map +0 -1
  277. package/dist/types/chat.js +0 -1
  278. package/dist/types/chat.js.map +0 -1
  279. package/dist/types/message-content.d.ts +0 -38
  280. package/dist/types/message-content.d.ts.map +0 -1
  281. package/dist/types/message-content.js +0 -1
  282. package/dist/types/message-content.js.map +0 -1
  283. package/dist/ui/open-code-theme.d.ts +0 -58
  284. package/dist/ui/open-code-theme.d.ts.map +0 -1
  285. package/dist/ui/open-code-theme.js +0 -113
  286. package/dist/ui/open-code-theme.js.map +0 -1
  287. package/dist/ui/opencode-markdown.d.ts +0 -7
  288. package/dist/ui/opencode-markdown.d.ts.map +0 -1
  289. package/dist/ui/opencode-markdown.js +0 -169
  290. package/dist/ui/opencode-markdown.js.map +0 -1
  291. package/dist/ui/theme.d.ts +0 -68
  292. package/dist/ui/theme.d.ts.map +0 -1
  293. package/dist/ui/theme.js +0 -80
  294. package/dist/ui/theme.js.map +0 -1
  295. package/dist/utils/time.d.ts +0 -2
  296. package/dist/utils/time.d.ts.map +0 -1
  297. package/dist/utils/time.js +0 -7
  298. package/dist/utils/time.js.map +0 -1
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import { applyUiThemeMode, uiTheme } from './theme';
4
+
5
+ describe('theme module', () => {
6
+ it('should have required properties', () => {
7
+ expect(uiTheme).toBeDefined();
8
+ expect(typeof uiTheme).toBe('object');
9
+
10
+ // 检查关键属性
11
+ expect(uiTheme.bg).toBeString();
12
+ expect(uiTheme.surface).toBeString();
13
+ expect(uiTheme.text).toBeString();
14
+ expect(uiTheme.accent).toBeString();
15
+ expect(uiTheme.layout).toBeObject();
16
+ expect(uiTheme.typography).toBeObject();
17
+ });
18
+
19
+ it('should switch to dark theme', () => {
20
+ applyUiThemeMode('dark');
21
+
22
+ expect(uiTheme.bg).toBe('#09090b');
23
+ expect(uiTheme.surface).toBe('#18181b');
24
+ expect(uiTheme.text).toBe('#fafafa');
25
+ expect(uiTheme.accent).toBe('#8e51ff');
26
+ });
27
+
28
+ it('should switch to light theme', () => {
29
+ applyUiThemeMode('light');
30
+
31
+ expect(uiTheme.bg).toBe('#ffffff');
32
+ expect(uiTheme.surface).toBe('#ffffff');
33
+ expect(uiTheme.text).toBe('#09090b');
34
+ expect(uiTheme.accent).toBe('#7f22fe');
35
+ });
36
+
37
+ it('should create independent theme objects', () => {
38
+ // 应用暗色主题
39
+ applyUiThemeMode('dark');
40
+ const darkThemeBg = uiTheme.bg;
41
+ const darkThemeText = uiTheme.text;
42
+
43
+ // 应用亮色主题
44
+ applyUiThemeMode('light');
45
+ const lightThemeBg = uiTheme.bg;
46
+ const lightThemeText = uiTheme.text;
47
+
48
+ // 颜色值应该不同
49
+ expect(darkThemeBg).not.toBe(lightThemeBg);
50
+ expect(darkThemeText).not.toBe(lightThemeText);
51
+ });
52
+ });
@@ -0,0 +1,151 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+
3
+ import { applyUiThemeMode, uiTheme } from './theme';
4
+
5
+ describe('theme module', () => {
6
+ beforeEach(() => {
7
+ applyUiThemeMode('dark');
8
+ });
9
+
10
+ afterEach(() => {
11
+ // 恢复原始主题
12
+ applyUiThemeMode('dark');
13
+ });
14
+
15
+ describe('uiTheme', () => {
16
+ it('should be defined', () => {
17
+ expect(uiTheme).toBeDefined();
18
+ expect(typeof uiTheme).toBe('object');
19
+ });
20
+
21
+ it('should have required properties', () => {
22
+ expect(uiTheme).toHaveProperty('bg');
23
+ expect(uiTheme).toHaveProperty('surface');
24
+ expect(uiTheme).toHaveProperty('panel');
25
+ expect(uiTheme).toHaveProperty('text');
26
+ expect(uiTheme).toHaveProperty('muted');
27
+ expect(uiTheme).toHaveProperty('subtle');
28
+ expect(uiTheme).toHaveProperty('accent');
29
+ expect(uiTheme).toHaveProperty('thinking');
30
+ expect(uiTheme).toHaveProperty('divider');
31
+ expect(uiTheme).toHaveProperty('inputCursor');
32
+ expect(uiTheme).toHaveProperty('inputBg');
33
+ expect(uiTheme).toHaveProperty('inputSelectionBg');
34
+ expect(uiTheme).toHaveProperty('inputSelectionText');
35
+ expect(uiTheme).toHaveProperty('codeBlock');
36
+ expect(uiTheme).toHaveProperty('diff');
37
+ expect(uiTheme).toHaveProperty('layout');
38
+ expect(uiTheme).toHaveProperty('typography');
39
+ });
40
+
41
+ it('should have layout properties', () => {
42
+ expect(uiTheme.layout).toHaveProperty('appPaddingTop');
43
+ expect(uiTheme.layout).toHaveProperty('appPaddingBottom');
44
+ expect(uiTheme.layout).toHaveProperty('appPaddingX');
45
+ expect(uiTheme.layout).toHaveProperty('conversationPaddingX');
46
+ expect(uiTheme.layout).toHaveProperty('conversationPaddingY');
47
+ expect(uiTheme.layout).toHaveProperty('conversationContentPaddingX');
48
+ expect(uiTheme.layout).toHaveProperty('conversationContentPaddingY');
49
+ expect(uiTheme.layout).toHaveProperty('promptPaddingX');
50
+ expect(uiTheme.layout).toHaveProperty('promptPaddingBottom');
51
+ expect(uiTheme.layout).toHaveProperty('footerMarginTop');
52
+ expect(uiTheme.layout).toHaveProperty('footerPaddingRight');
53
+ });
54
+
55
+ it('should expose code and diff theme tokens', () => {
56
+ expect(uiTheme.codeBlock).toHaveProperty('bg');
57
+ expect(uiTheme.codeBlock).toHaveProperty('language');
58
+ expect(uiTheme.diff).toHaveProperty('addedBg');
59
+ expect(uiTheme.diff).toHaveProperty('removedLineNumberBg');
60
+ });
61
+
62
+ it('should have typography properties', () => {
63
+ expect(uiTheme.typography).toHaveProperty('body');
64
+ expect(uiTheme.typography).toHaveProperty('code');
65
+ expect(uiTheme.typography).toHaveProperty('muted');
66
+ expect(uiTheme.typography).toHaveProperty('note');
67
+ expect(uiTheme.typography).toHaveProperty('heading');
68
+ });
69
+
70
+ it('should have correct default values for dark theme', () => {
71
+ // 确保是暗色主题
72
+ applyUiThemeMode('dark');
73
+
74
+ expect(uiTheme.bg).toBe('#09090b');
75
+ expect(uiTheme.surface).toBe('#18181b');
76
+ expect(uiTheme.text).toBe('#fafafa');
77
+ expect(uiTheme.accent).toBe('#8e51ff');
78
+ });
79
+ });
80
+
81
+ describe('applyUiThemeMode', () => {
82
+ it('should switch to dark theme', () => {
83
+ applyUiThemeMode('dark');
84
+
85
+ expect(uiTheme.bg).toBe('#09090b');
86
+ expect(uiTheme.surface).toBe('#18181b');
87
+ expect(uiTheme.text).toBe('#fafafa');
88
+ expect(uiTheme.accent).toBe('#8e51ff');
89
+ });
90
+
91
+ it('should switch to light theme', () => {
92
+ applyUiThemeMode('light');
93
+
94
+ expect(uiTheme.bg).toBe('#ffffff');
95
+ expect(uiTheme.surface).toBe('#ffffff');
96
+ expect(uiTheme.text).toBe('#09090b');
97
+ expect(uiTheme.accent).toBe('#7f22fe');
98
+ });
99
+
100
+ it('should create independent theme objects', () => {
101
+ // 应用暗色主题
102
+ applyUiThemeMode('dark');
103
+ const darkThemeRef = uiTheme;
104
+
105
+ // 应用亮色主题
106
+ applyUiThemeMode('light');
107
+ const lightThemeRef = uiTheme;
108
+
109
+ // 两个引用应该不同
110
+ expect(darkThemeRef).not.toBe(lightThemeRef);
111
+
112
+ // 颜色值应该不同
113
+ expect(darkThemeRef.bg).not.toBe(lightThemeRef.bg);
114
+ expect(darkThemeRef.text).not.toBe(lightThemeRef.text);
115
+ expect(darkThemeRef.accent).not.toBe(lightThemeRef.accent);
116
+ });
117
+
118
+ it('should clone layout and typography objects', () => {
119
+ // 应用暗色主题
120
+ applyUiThemeMode('dark');
121
+ const darkThemeLayout = uiTheme.layout;
122
+ const darkThemeTypography = uiTheme.typography;
123
+
124
+ // 应用亮色主题
125
+ applyUiThemeMode('light');
126
+ const lightThemeLayout = uiTheme.layout;
127
+ const lightThemeTypography = uiTheme.typography;
128
+
129
+ // layout和typography对象应该是独立的副本
130
+ expect(darkThemeLayout).not.toBe(lightThemeLayout);
131
+ expect(darkThemeTypography).not.toBe(lightThemeTypography);
132
+
133
+ // 但它们的值应该相同(因为两个主题共享相同的布局和排版)
134
+ expect(darkThemeLayout.appPaddingTop).toBe(lightThemeLayout.appPaddingTop);
135
+ expect(darkThemeLayout.appPaddingBottom).toBe(lightThemeLayout.appPaddingBottom);
136
+ expect(darkThemeTypography.body).toBe(lightThemeTypography.body);
137
+ expect(darkThemeTypography.code).toBe(lightThemeTypography.code);
138
+ });
139
+
140
+ it('should handle invalid mode by defaulting to dark', () => {
141
+ // 先设置为亮色主题
142
+ applyUiThemeMode('light');
143
+ expect(uiTheme.bg).toBe('#ffffff');
144
+
145
+ // 使用无效模式(TypeScript会阻止,但JavaScript可能允许)
146
+ // 这里测试默认行为
147
+ applyUiThemeMode('dark' as any); // 强制为暗色
148
+ expect(uiTheme.bg).toBe('#09090b');
149
+ });
150
+ });
151
+ });
@@ -0,0 +1,152 @@
1
+ import { TextAttributes, rgbToHex } from '@opentui/core';
2
+
3
+ import { resolveOpenCodeTheme, type OpenCodeThemeMode } from './open-code-theme';
4
+
5
+ export type UiThemeMode = OpenCodeThemeMode;
6
+ type TextAttributeValue = (typeof TextAttributes)[keyof typeof TextAttributes];
7
+
8
+ export type UiTheme = {
9
+ bg: string;
10
+ surface: string;
11
+ panel: string;
12
+ text: string;
13
+ muted: string;
14
+ subtle: string;
15
+ accent: string;
16
+ thinking: string;
17
+ divider: string;
18
+ userPromptBg: string;
19
+ userPromptText: string;
20
+ inputBg: string;
21
+ inputCursor: string;
22
+ inputSelectionBg: string;
23
+ inputSelectionText: string;
24
+ codeBlock: {
25
+ bg: string;
26
+ border: string;
27
+ header: string;
28
+ language: string;
29
+ text: string;
30
+ selectionBg: string;
31
+ selectionText: string;
32
+ };
33
+ diff: {
34
+ lineNumberFg: string;
35
+ lineNumberBg: string;
36
+ addedBg: string;
37
+ removedBg: string;
38
+ contextBg: string;
39
+ addedContentBg: string;
40
+ removedContentBg: string;
41
+ contextContentBg: string;
42
+ addedSign: string;
43
+ removedSign: string;
44
+ addedLineNumberBg: string;
45
+ removedLineNumberBg: string;
46
+ };
47
+ layout: {
48
+ appPaddingTop: number;
49
+ appPaddingBottom: number;
50
+ appPaddingX: number;
51
+ conversationPaddingX: number;
52
+ conversationPaddingY: number;
53
+ conversationContentPaddingX: number;
54
+ conversationContentPaddingY: number;
55
+ promptPaddingX: number;
56
+ promptPaddingBottom: number;
57
+ footerMarginTop: number;
58
+ footerPaddingRight: number;
59
+ };
60
+ typography: {
61
+ body: TextAttributeValue;
62
+ code: TextAttributeValue;
63
+ muted: TextAttributeValue;
64
+ note: TextAttributeValue;
65
+ heading: TextAttributeValue;
66
+ };
67
+ };
68
+
69
+ const baseLayout: UiTheme['layout'] = {
70
+ appPaddingTop: 0,
71
+ appPaddingBottom: 1,
72
+ appPaddingX: 0,
73
+ conversationPaddingX: 0,
74
+ conversationPaddingY: 0,
75
+ conversationContentPaddingX: 2,
76
+ conversationContentPaddingY: 1,
77
+ promptPaddingX: 0,
78
+ promptPaddingBottom: 1,
79
+ footerMarginTop: 1,
80
+ footerPaddingRight: 0,
81
+ };
82
+
83
+ const baseTypography: UiTheme['typography'] = {
84
+ body: TextAttributes.BOLD,
85
+ code: TextAttributes.BOLD,
86
+ muted: TextAttributes.BOLD,
87
+ note: TextAttributes.NONE,
88
+ heading: TextAttributes.BOLD,
89
+ };
90
+
91
+ const toHex = (value: Parameters<typeof rgbToHex>[0]) => rgbToHex(value).toLowerCase();
92
+
93
+ const createTheme = (mode: UiThemeMode, platform: NodeJS.Platform): UiTheme => {
94
+ const theme = resolveOpenCodeTheme(mode, platform);
95
+
96
+ return {
97
+ bg: toHex(theme.background),
98
+ surface: toHex(theme.backgroundPanel),
99
+ panel: toHex(theme.backgroundPanel),
100
+ text: toHex(theme.text),
101
+ muted: toHex(theme.textMuted),
102
+ subtle: toHex(theme.textMuted),
103
+ accent: toHex(theme.primary),
104
+ thinking: toHex(theme.textMuted),
105
+ divider: toHex(theme.borderSubtle),
106
+ userPromptBg: toHex(theme.backgroundElement),
107
+ userPromptText: toHex(theme.text),
108
+ inputBg: mode === 'light' ? '#e4e4e7' : '#27272a',
109
+ inputCursor: toHex(theme.primary),
110
+ inputSelectionBg: toHex(theme.borderActive),
111
+ inputSelectionText: toHex(theme.text),
112
+ codeBlock: {
113
+ bg: toHex(theme.backgroundElement),
114
+ border: toHex(theme.border),
115
+ header: toHex(theme.textMuted),
116
+ language: toHex(theme.accent),
117
+ text: toHex(theme.text),
118
+ selectionBg: toHex(theme.borderActive),
119
+ selectionText: toHex(theme.text),
120
+ },
121
+ diff: {
122
+ lineNumberFg: toHex(theme.diffLineNumber),
123
+ lineNumberBg: toHex(theme.backgroundElement),
124
+ addedBg: toHex(theme.diffAddedBg),
125
+ removedBg: toHex(theme.diffRemovedBg),
126
+ contextBg: toHex(theme.diffContextBg),
127
+ addedContentBg: toHex(theme.diffAddedBg),
128
+ removedContentBg: toHex(theme.diffRemovedBg),
129
+ contextContentBg: toHex(theme.diffContextBg),
130
+ addedSign: toHex(theme.diffAdded),
131
+ removedSign: toHex(theme.diffRemoved),
132
+ addedLineNumberBg: toHex(theme.diffAddedLineNumberBg),
133
+ removedLineNumberBg: toHex(theme.diffRemovedLineNumberBg),
134
+ },
135
+ layout: baseLayout,
136
+ typography: baseTypography,
137
+ };
138
+ };
139
+
140
+ const cloneTheme = (theme: UiTheme): UiTheme => ({
141
+ ...theme,
142
+ codeBlock: { ...theme.codeBlock },
143
+ diff: { ...theme.diff },
144
+ layout: { ...theme.layout },
145
+ typography: { ...theme.typography },
146
+ });
147
+
148
+ export let uiTheme: UiTheme = cloneTheme(createTheme('dark', process.platform));
149
+
150
+ export const applyUiThemeMode = (mode: UiThemeMode) => {
151
+ uiTheme = cloneTheme(createTheme(mode, process.platform));
152
+ };
@@ -0,0 +1,144 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { createTimeLabel } from './time';
4
+
5
+ class MockDate extends Date {
6
+ constructor(private readonly value: Date) {
7
+ super(value.getTime());
8
+ }
9
+
10
+ override toLocaleTimeString(
11
+ locales?: Intl.LocalesArgument,
12
+ options?: Intl.DateTimeFormatOptions
13
+ ): string {
14
+ return this.value.toLocaleTimeString(locales, options);
15
+ }
16
+ }
17
+
18
+ describe('createTimeLabel', () => {
19
+ it('should return time in HH:MM:SS format', () => {
20
+ // 模拟一个固定的日期来测试格式
21
+ const mockDate = new Date('2024-01-15T14:30:45Z');
22
+ const originalDate = global.Date;
23
+
24
+ // 临时替换Date构造函数
25
+ global.Date = class extends originalDate {
26
+ constructor() {
27
+ super();
28
+ return new MockDate(mockDate);
29
+ }
30
+
31
+ static override now() {
32
+ return mockDate.getTime();
33
+ }
34
+ } as unknown as DateConstructor;
35
+
36
+ try {
37
+ const timeLabel = createTimeLabel();
38
+ // 24小时格式,前导零
39
+ expect(timeLabel).toBe('14:30:45');
40
+ } finally {
41
+ // 恢复原始的Date
42
+ global.Date = originalDate;
43
+ }
44
+ });
45
+
46
+ it('should use 24-hour format', () => {
47
+ // 测试下午时间
48
+ const mockDate = new Date('2024-01-15T22:15:30Z');
49
+ const originalDate = global.Date;
50
+
51
+ global.Date = class extends originalDate {
52
+ constructor() {
53
+ super();
54
+ return new MockDate(mockDate);
55
+ }
56
+
57
+ static override now() {
58
+ return mockDate.getTime();
59
+ }
60
+ } as unknown as DateConstructor;
61
+
62
+ try {
63
+ const timeLabel = createTimeLabel();
64
+ // 应该是22:15:30,不是10:15:30 PM
65
+ expect(timeLabel).toBe('22:15:30');
66
+ } finally {
67
+ global.Date = originalDate;
68
+ }
69
+ });
70
+
71
+ it('should pad single-digit hours, minutes, and seconds', () => {
72
+ // 测试单数字时间
73
+ const mockDate = new Date('2024-01-15T09:05:07Z');
74
+ const originalDate = global.Date;
75
+
76
+ global.Date = class extends originalDate {
77
+ constructor() {
78
+ super();
79
+ return new MockDate(mockDate);
80
+ }
81
+
82
+ static override now() {
83
+ return mockDate.getTime();
84
+ }
85
+ } as unknown as DateConstructor;
86
+
87
+ try {
88
+ const timeLabel = createTimeLabel();
89
+ // 应该是09:05:07,不是9:5:7
90
+ expect(timeLabel).toBe('09:05:07');
91
+ } finally {
92
+ global.Date = originalDate;
93
+ }
94
+ });
95
+
96
+ it('should use en-US locale', () => {
97
+ // 保存原始locale
98
+ const originalToLocaleTimeString = Date.prototype.toLocaleTimeString;
99
+
100
+ let calledWithLocale = '';
101
+ Date.prototype.toLocaleTimeString = function (
102
+ locales?: Intl.LocalesArgument,
103
+ options?: Intl.DateTimeFormatOptions
104
+ ): string {
105
+ calledWithLocale = Array.isArray(locales) ? (locales[0] ?? '') : (locales ?? '');
106
+ return originalToLocaleTimeString.call(this, locales, options);
107
+ };
108
+
109
+ try {
110
+ createTimeLabel();
111
+ expect(calledWithLocale).toBe('en-US');
112
+ } finally {
113
+ // 恢复原始方法
114
+ Date.prototype.toLocaleTimeString = originalToLocaleTimeString;
115
+ }
116
+ });
117
+
118
+ it('should use correct options', () => {
119
+ // 保存原始方法
120
+ const originalToLocaleTimeString = Date.prototype.toLocaleTimeString;
121
+
122
+ let calledWithOptions: Intl.DateTimeFormatOptions = {};
123
+ Date.prototype.toLocaleTimeString = function (
124
+ _locales?: Intl.LocalesArgument,
125
+ options?: Intl.DateTimeFormatOptions
126
+ ): string {
127
+ calledWithOptions = options ?? {};
128
+ return '00:00:00'; // 返回模拟值
129
+ };
130
+
131
+ try {
132
+ createTimeLabel();
133
+ expect(calledWithOptions).toEqual({
134
+ hour: '2-digit',
135
+ minute: '2-digit',
136
+ second: '2-digit',
137
+ hour12: false,
138
+ });
139
+ } finally {
140
+ // 恢复原始方法
141
+ Date.prototype.toLocaleTimeString = originalToLocaleTimeString;
142
+ }
143
+ });
144
+ });
@@ -0,0 +1,7 @@
1
+ export const createTimeLabel = () =>
2
+ new Date().toLocaleTimeString('en-US', {
3
+ hour: '2-digit',
4
+ minute: '2-digit',
5
+ second: '2-digit',
6
+ hour12: false,
7
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "jsxImportSource": "@opentui/react",
10
+ "allowJs": true,
11
+
12
+ // Bundler mode
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "verbatimModuleSyntax": true,
16
+ "noEmit": true,
17
+
18
+ // Best practices
19
+ "strict": true,
20
+ "skipLibCheck": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedIndexedAccess": true,
23
+ "noImplicitOverride": true,
24
+
25
+ // Some stricter flags (disabled by default)
26
+ "noUnusedLocals": false,
27
+ "noUnusedParameters": false,
28
+ "noPropertyAccessFromIndexSignature": false
29
+ }
30
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 coding-agent-v2 contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/dist/App.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare const App: () => import("react").ReactNode;
2
- //# sourceMappingURL=App.d.ts.map
package/dist/App.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAuCA,eAAO,MAAM,GAAG,iCAiQf,CAAC"}