ethagent 3.3.3 → 4.0.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 (322) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +35 -0
  3. package/LICENSE +1 -1
  4. package/README.md +64 -104
  5. package/commands/ethagent.md +40 -0
  6. package/package.json +16 -16
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -6
  8. package/src/app/keybindings/types.ts +1 -6
  9. package/src/cli/ResetConfirmView.tsx +54 -53
  10. package/src/cli/demo.ts +86 -0
  11. package/src/cli/hookIo.ts +45 -0
  12. package/src/cli/main.tsx +94 -123
  13. package/src/cli/memoryGuard.ts +49 -0
  14. package/src/cli/reset.ts +28 -70
  15. package/src/cli/sessionStart.ts +33 -0
  16. package/src/cli/status.ts +46 -0
  17. package/src/cli/sync.ts +167 -0
  18. package/src/cli/syncAdapters/claude-code.ts +86 -0
  19. package/src/cli/syncAdapters/codex.ts +66 -0
  20. package/src/cli/syncAdapters/index.ts +45 -0
  21. package/src/cli/syncAdapters/managedBlock.ts +175 -0
  22. package/src/cli/syncAdapters/shared.ts +63 -0
  23. package/src/identity/continuity/envelopeParse.ts +20 -1
  24. package/src/identity/continuity/publicSkills.ts +3 -1
  25. package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
  26. package/src/identity/continuity/skills/scaffold.ts +5 -2
  27. package/src/identity/continuity/snapshots.ts +12 -5
  28. package/src/identity/continuity/storage/defaults.ts +20 -19
  29. package/src/identity/continuity/storage/status.ts +1 -1
  30. package/src/identity/ens/ensLookup/constants.ts +1 -1
  31. package/src/identity/manager/IdentityManager.tsx +33 -0
  32. package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
  33. package/src/identity/{hub → manager}/Routes.tsx +48 -34
  34. package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
  35. package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
  36. package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
  37. package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
  38. package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
  39. package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
  40. package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
  41. package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
  42. package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
  43. package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
  44. package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
  45. package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
  46. package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
  47. package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
  48. package/src/identity/{hub → manager}/create/effects.ts +19 -10
  49. package/src/identity/manager/create/importScan.ts +122 -0
  50. package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
  51. package/src/identity/{hub → manager}/custody/actions.ts +1 -15
  52. package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
  53. package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
  54. package/src/identity/{hub → manager}/custody/types.ts +1 -2
  55. package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
  56. package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
  57. package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
  58. package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
  59. package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
  60. package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
  61. package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
  62. package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
  63. package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
  64. package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
  65. package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
  66. package/src/identity/{hub → manager}/profile/effects.ts +1 -3
  67. package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
  68. package/src/identity/{hub → manager}/profile/state.ts +1 -1
  69. package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
  70. package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
  71. package/src/identity/{hub → manager}/restore/apply.ts +1 -1
  72. package/src/identity/{hub → manager}/restore/auth.ts +1 -1
  73. package/src/identity/{hub → manager}/restore/discover.ts +1 -1
  74. package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
  75. package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
  76. package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
  77. package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
  78. package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
  79. package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
  80. package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
  81. package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
  82. package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
  83. package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
  84. package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
  85. package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
  86. package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
  87. package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
  88. package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
  89. package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
  90. package/src/identity/manager/shared/effects/types.ts +30 -0
  91. package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
  92. package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
  93. package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
  94. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
  95. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
  96. package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
  97. package/src/identity/{hub → manager}/shared/utils.ts +6 -10
  98. package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
  99. package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
  100. package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
  101. package/src/identity/{hub → manager}/types.ts +5 -6
  102. package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
  103. package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
  104. package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
  105. package/src/identity/registry/erc8004/discovery.ts +3 -17
  106. package/src/identity/registry/erc8004/utils.ts +1 -1
  107. package/src/identity/storage/ipfs.ts +21 -1
  108. package/src/identity/wallet/browserWallet/html.ts +10 -2
  109. package/src/identity/wallet/browserWallet/http.ts +18 -0
  110. package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
  111. package/src/identity/wallet/browserWallet/requests.ts +10 -28
  112. package/src/identity/wallet/browserWallet/session.ts +26 -33
  113. package/src/identity/wallet/browserWallet/validation.ts +14 -0
  114. package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
  115. package/src/identity/wallet/page/boot.ts +43 -0
  116. package/src/identity/wallet/page/config.ts +59 -0
  117. package/src/identity/wallet/page/constants.ts +12 -0
  118. package/src/identity/wallet/page/copy.ts +47 -68
  119. package/src/identity/wallet/page/css.ts +638 -0
  120. package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
  121. package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
  122. package/src/identity/wallet/page/markup.ts +44 -34
  123. package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
  124. package/src/identity/wallet/page/resize.ts +95 -0
  125. package/src/identity/wallet/page/state.ts +135 -8
  126. package/src/identity/wallet/page/timeline.ts +161 -0
  127. package/src/identity/wallet/page/view.ts +22 -302
  128. package/src/storage/config.ts +30 -80
  129. package/src/storage/reset.ts +31 -0
  130. package/src/storage/secrets.ts +1 -16
  131. package/src/ui/Select.tsx +27 -5
  132. package/src/ui/Spinner.tsx +16 -15
  133. package/src/ui/Surface.tsx +21 -17
  134. package/src/ui/TextArea.tsx +173 -0
  135. package/src/ui/TextInput.tsx +31 -133
  136. package/src/ui/theme.ts +22 -13
  137. package/src/utils/clipboard.ts +0 -140
  138. package/src/app/FirstRun.tsx +0 -577
  139. package/src/app/FirstRunTimeline.tsx +0 -51
  140. package/src/app/firstRunConfig.ts +0 -26
  141. package/src/app/hooks/useCancelRequest.ts +0 -22
  142. package/src/app/hooks/useDoublePress.ts +0 -46
  143. package/src/app/hooks/useExitOnCtrlC.ts +0 -36
  144. package/src/auth/openaiOAuth/credentials.ts +0 -47
  145. package/src/auth/openaiOAuth/crypto.ts +0 -23
  146. package/src/auth/openaiOAuth/index.ts +0 -238
  147. package/src/auth/openaiOAuth/landingPage.ts +0 -116
  148. package/src/auth/openaiOAuth/listener.ts +0 -151
  149. package/src/auth/openaiOAuth/refresh.ts +0 -70
  150. package/src/auth/openaiOAuth/shared.ts +0 -115
  151. package/src/chat/ChatBottomPane.tsx +0 -296
  152. package/src/chat/ChatScreen.tsx +0 -1685
  153. package/src/chat/ConversationStack.tsx +0 -56
  154. package/src/chat/MessageList.tsx +0 -638
  155. package/src/chat/SessionStatus.tsx +0 -53
  156. package/src/chat/chatEnvironment.ts +0 -16
  157. package/src/chat/chatScreenUtils.ts +0 -194
  158. package/src/chat/chatSessionState.ts +0 -146
  159. package/src/chat/chatTurnContext.ts +0 -50
  160. package/src/chat/chatTurnOrchestrator.ts +0 -603
  161. package/src/chat/chatTurnRows.ts +0 -64
  162. package/src/chat/commands.ts +0 -494
  163. package/src/chat/continuityEditReview.ts +0 -42
  164. package/src/chat/display/DiffView.tsx +0 -193
  165. package/src/chat/display/SyntaxText.tsx +0 -192
  166. package/src/chat/display/toolCallDisplay.ts +0 -103
  167. package/src/chat/display/toolResultDisplay.ts +0 -19
  168. package/src/chat/input/ChatInput.tsx +0 -625
  169. package/src/chat/input/chatInputHelpers.ts +0 -62
  170. package/src/chat/input/chatInputState.ts +0 -247
  171. package/src/chat/input/chatPaste.ts +0 -49
  172. package/src/chat/input/imageRefs.ts +0 -30
  173. package/src/chat/input/inputRendering.tsx +0 -93
  174. package/src/chat/input/textCursor.ts +0 -212
  175. package/src/chat/messageMarkdown.ts +0 -220
  176. package/src/chat/messageRows.ts +0 -43
  177. package/src/chat/planImplementation.ts +0 -62
  178. package/src/chat/slashCommandHandlers.ts +0 -122
  179. package/src/chat/slashCommandViews.ts +0 -120
  180. package/src/chat/transcript/TranscriptView.tsx +0 -184
  181. package/src/chat/transcript/transcriptViewport.ts +0 -295
  182. package/src/chat/views/ContextLimitView.tsx +0 -95
  183. package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
  184. package/src/chat/views/CopyPicker.tsx +0 -50
  185. package/src/chat/views/PermissionPrompt.tsx +0 -156
  186. package/src/chat/views/PermissionsView.tsx +0 -165
  187. package/src/chat/views/PlanApprovalView.tsx +0 -91
  188. package/src/chat/views/ResumeView.tsx +0 -273
  189. package/src/chat/views/RewindView.tsx +0 -412
  190. package/src/cli/preview.tsx +0 -14
  191. package/src/cli/updateNotice.ts +0 -54
  192. package/src/identity/continuity/privateEdit/apply.ts +0 -170
  193. package/src/identity/continuity/privateEdit/diff.ts +0 -6
  194. package/src/identity/continuity/privateEdit/files.ts +0 -23
  195. package/src/identity/continuity/privateEdit/types.ts +0 -28
  196. package/src/identity/continuity/privateEdit.ts +0 -46
  197. package/src/identity/hub/IdentityHub.tsx +0 -14
  198. package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
  199. package/src/identity/hub/ens/effects.ts +0 -218
  200. package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
  201. package/src/identity/hub/shared/effects/types.ts +0 -53
  202. package/src/identity/hub/shared/reconciliation/index.ts +0 -14
  203. package/src/identity/wallet/page/grainient.ts +0 -278
  204. package/src/identity/wallet/page/html.ts +0 -28
  205. package/src/identity/wallet/page/styles/base.ts +0 -259
  206. package/src/identity/wallet/page/styles/components.ts +0 -262
  207. package/src/identity/wallet/page/styles/index.ts +0 -5
  208. package/src/identity/wallet/page/styles/responsive.ts +0 -247
  209. package/src/identity/wallet/page.tsx +0 -38
  210. package/src/mcp/approvals.ts +0 -113
  211. package/src/mcp/config.ts +0 -235
  212. package/src/mcp/manager.ts +0 -482
  213. package/src/mcp/managerHelpers.ts +0 -70
  214. package/src/mcp/names.ts +0 -19
  215. package/src/mcp/output.ts +0 -96
  216. package/src/models/ModelPicker.tsx +0 -1009
  217. package/src/models/catalog.ts +0 -327
  218. package/src/models/huggingface.ts +0 -712
  219. package/src/models/huggingfaceStorage.ts +0 -136
  220. package/src/models/llamacpp.ts +0 -848
  221. package/src/models/llamacppCommands.ts +0 -44
  222. package/src/models/llamacppConfig.ts +0 -34
  223. package/src/models/llamacppDiscovery.ts +0 -176
  224. package/src/models/llamacppOutput.ts +0 -65
  225. package/src/models/llamacppPreflight.ts +0 -158
  226. package/src/models/modelDisplay.ts +0 -180
  227. package/src/models/modelPickerCatalogFlow.ts +0 -56
  228. package/src/models/modelPickerCredentials.ts +0 -166
  229. package/src/models/modelPickerData.ts +0 -41
  230. package/src/models/modelPickerDisplay.tsx +0 -132
  231. package/src/models/modelPickerHfFlow.ts +0 -192
  232. package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
  233. package/src/models/modelPickerOptions.ts +0 -457
  234. package/src/models/modelPickerTypes.ts +0 -69
  235. package/src/models/modelPickerUninstallFlow.ts +0 -48
  236. package/src/models/modelPickerViewHelpers.ts +0 -174
  237. package/src/models/modelRecommendation.ts +0 -139
  238. package/src/models/providerDisplay.ts +0 -16
  239. package/src/models/runtimeDetection.ts +0 -81
  240. package/src/models/uncensoredCatalog.ts +0 -86
  241. package/src/providers/anthropic.ts +0 -290
  242. package/src/providers/contracts.ts +0 -71
  243. package/src/providers/errors.ts +0 -80
  244. package/src/providers/gemini.ts +0 -391
  245. package/src/providers/openai-chat.ts +0 -474
  246. package/src/providers/openai-responses-format.ts +0 -177
  247. package/src/providers/openai-responses.ts +0 -306
  248. package/src/providers/openaiChatWire.ts +0 -124
  249. package/src/providers/registry.ts +0 -120
  250. package/src/providers/retry.ts +0 -58
  251. package/src/providers/sse.ts +0 -93
  252. package/src/runtime/compaction.ts +0 -395
  253. package/src/runtime/cwd.ts +0 -43
  254. package/src/runtime/providerTurn.ts +0 -38
  255. package/src/runtime/sessionMode.ts +0 -55
  256. package/src/runtime/systemPrompt.ts +0 -213
  257. package/src/runtime/textToolParser.ts +0 -161
  258. package/src/runtime/toolClaimGuards.ts +0 -143
  259. package/src/runtime/toolExecution.ts +0 -304
  260. package/src/runtime/toolIntent.ts +0 -143
  261. package/src/runtime/turn.ts +0 -369
  262. package/src/runtime/turnNudges.ts +0 -223
  263. package/src/runtime/turnTypes.ts +0 -86
  264. package/src/storage/factoryReset.ts +0 -127
  265. package/src/storage/history.ts +0 -58
  266. package/src/storage/permissions.ts +0 -76
  267. package/src/storage/rewind.ts +0 -266
  268. package/src/storage/sessionExport.ts +0 -49
  269. package/src/storage/sessions.ts +0 -495
  270. package/src/tools/bashSafety.ts +0 -186
  271. package/src/tools/bashTool.ts +0 -140
  272. package/src/tools/changeDirectoryTool.ts +0 -213
  273. package/src/tools/contracts.ts +0 -192
  274. package/src/tools/deleteFileTool.ts +0 -116
  275. package/src/tools/editTool.ts +0 -165
  276. package/src/tools/editUtils.ts +0 -170
  277. package/src/tools/fileDiff.ts +0 -261
  278. package/src/tools/listDirectoryTool.ts +0 -55
  279. package/src/tools/listSkillFilesTool.ts +0 -77
  280. package/src/tools/listSkillsTool.ts +0 -68
  281. package/src/tools/mcpResourceTools.ts +0 -95
  282. package/src/tools/permissionRules.ts +0 -85
  283. package/src/tools/privateContinuityEditTool.ts +0 -187
  284. package/src/tools/privateContinuityReadTool.ts +0 -106
  285. package/src/tools/readSkillTool.ts +0 -107
  286. package/src/tools/readTool.ts +0 -85
  287. package/src/tools/registry.ts +0 -103
  288. package/src/tools/writeFileTool.ts +0 -167
  289. package/src/ui/BrandSplash.tsx +0 -133
  290. package/src/ui/terminalTitle.ts +0 -30
  291. package/src/utils/images.ts +0 -140
  292. package/src/utils/markdownSegments.ts +0 -51
  293. package/src/utils/messages.ts +0 -37
  294. package/src/utils/withRetry.ts +0 -324
  295. /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
  296. /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
  297. /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
  298. /package/src/identity/{hub → manager}/custody/state.ts +0 -0
  299. /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
  300. /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
  301. /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
  302. /package/src/identity/{hub → manager}/ens/state.ts +0 -0
  303. /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
  304. /package/src/identity/{hub → manager}/ens/types.ts +0 -0
  305. /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
  306. /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
  307. /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
  308. /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
  309. /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
  310. /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
  311. /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
  312. /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
  313. /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
  314. /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
  315. /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
  316. /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
  317. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
  318. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
  319. /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
  320. /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
  321. /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
  322. /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
package/src/ui/Select.tsx CHANGED
@@ -1,8 +1,16 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react'
2
2
  import { Box, Text } from 'ink'
3
- import { theme } from './theme.js'
3
+ import { theme, PANEL_WIDTH } from './theme.js'
4
4
  import { useAppInput } from '../app/input/AppInputProvider.js'
5
5
 
6
+ const CONTENT_WIDTH = PANEL_WIDTH - 4
7
+
8
+ function fitHint(hint: string, budget: number): string {
9
+ if (budget < 8) return ''
10
+ if (hint.length <= budget) return hint
11
+ return `${hint.slice(0, budget - 1)}…`
12
+ }
13
+
6
14
  export type SelectOption<T> = {
7
15
  value: T
8
16
  label: string
@@ -121,22 +129,36 @@ export function Select<T>({
121
129
  const bold = option.bold ?? (isSection || (isActive && selectable))
122
130
  const inlineHint = Boolean(option.hint && hintLayout === 'inline' && !isSection)
123
131
  const belowHint = Boolean(option.hint && (!inlineHint || isSection))
132
+ const inlineHintText = inlineHint
133
+ ? fitHint(option.hint ?? '', CONTENT_WIDTH - rowIndent - prefix.length - option.label.length - 4)
134
+ : ''
135
+ const belowHintText = belowHint
136
+ ? fitHint(option.hint ?? '', CONTENT_WIDTH - rowIndent - 2)
137
+ : ''
138
+ const showHeadHighlight = isActive && selectable && !isSection && option.label.length > 0
124
139
  return (
125
140
  <Box key={absoluteIndex} flexDirection="column">
126
141
  <Box flexDirection="row" marginLeft={rowIndent}>
127
142
  <Text color={prefixColor}>{cursor} </Text>
128
143
  {prefix ? <Text color={prefixColor}>{prefix}</Text> : null}
129
- <Text color={labelColor} bold={bold}>{option.label}</Text>
130
- {inlineHint ? <Text color={hintColor}> {option.hint}</Text> : null}
144
+ {showHeadHighlight ? (
145
+ <>
146
+ <Text color={theme.accentHighlight} bold>{option.label[0]}</Text>
147
+ <Text color={labelColor} bold={bold}>{option.label.slice(1)}</Text>
148
+ </>
149
+ ) : (
150
+ <Text color={labelColor} bold={bold}>{option.label}</Text>
151
+ )}
152
+ {inlineHint && inlineHintText ? <Text color={hintColor}> {inlineHintText}</Text> : null}
131
153
  </Box>
132
154
  {option.subtext ? (
133
155
  <Box marginLeft={2 + rowIndent}>
134
156
  <Text color={subtextColor}>{option.subtext}</Text>
135
157
  </Box>
136
158
  ) : null}
137
- {belowHint ? (
159
+ {belowHint && belowHintText ? (
138
160
  <Box marginLeft={2 + rowIndent}>
139
- <Text color={hintColor}>{option.hint}</Text>
161
+ <Text color={hintColor}>{belowHintText}</Text>
140
162
  </Box>
141
163
  ) : null}
142
164
  </Box>
@@ -1,6 +1,8 @@
1
1
  import React, { useEffect, useRef, useState } from 'react'
2
2
  import { Text } from 'ink'
3
- import { theme } from './theme.js'
3
+ import { theme, PANEL_WIDTH } from './theme.js'
4
+
5
+ const CONTENT_WIDTH = PANEL_WIDTH - 4
4
6
 
5
7
  export const SPINNER_VERBS: string[] = [
6
8
  'accomplishing',
@@ -212,7 +214,7 @@ type SpinnerProps = {
212
214
  showElapsed?: boolean
213
215
  }
214
216
 
215
- const FRAMES = ['.', 'o', 'O', 'o']
217
+ const FRAMES = ['', '', '', '', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
216
218
 
217
219
  export const Spinner: React.FC<SpinnerProps> = ({
218
220
  active = true,
@@ -249,12 +251,21 @@ export const Spinner: React.FC<SpinnerProps> = ({
249
251
  if (!active) return
250
252
  const timer = setInterval(() => {
251
253
  setFrame(prev => (prev + 1) % FRAMES.length)
252
- }, 120)
254
+ }, 80)
253
255
  return () => clearInterval(timer)
254
256
  }, [active])
255
257
 
256
258
  const autoLabel = stickyVerbRef.current ?? verb ?? 'thinking'
257
- const text = spinnerText(label ?? `${autoLabel}…`)
259
+ const elapsed = showElapsed ? formatElapsedSeconds(Date.now() - (startedAt ?? internalStartedAtRef.current)) : null
260
+ const renderedHint = [rawHint, elapsed].filter(Boolean).join(' · ')
261
+ const hint = renderedHint ? spinnerHintText(renderedHint) : ''
262
+
263
+ const hintReserve = hint ? hint.length + 3 : 0
264
+ const labelBudget = Math.max(0, CONTENT_WIDTH - 2 - hintReserve)
265
+ let text = spinnerText(label ?? `${autoLabel}…`)
266
+ if (text.length > labelBudget) {
267
+ text = text.slice(0, Math.max(0, labelBudget - 1)) + '…'
268
+ }
258
269
 
259
270
  useEffect(() => {
260
271
  if (!active) return
@@ -267,10 +278,7 @@ export const Spinner: React.FC<SpinnerProps> = ({
267
278
 
268
279
  if (!active) return null
269
280
 
270
- const glyph = FRAMES[frame] ?? 'o'
271
- const elapsed = showElapsed ? formatElapsedSeconds(Date.now() - (startedAt ?? internalStartedAtRef.current)) : null
272
- const renderedHint = [rawHint, elapsed].filter(Boolean).join(' · ')
273
- const hint = renderedHint ? spinnerHintText(renderedHint) : ''
281
+ const glyph = FRAMES[frame] ?? ''
274
282
 
275
283
  const shimmerStart = shimmerPos - 1
276
284
  const shimmerEnd = shimmerPos + 1
@@ -316,15 +324,10 @@ function restoreSpinnerTerms(value: string): string {
316
324
  .replace(/\bapi\b/g, 'API')
317
325
  .replace(/\bens\b/g, 'ENS')
318
326
  .replace(/\berc-8004\b/g, 'ERC-8004')
319
- .replace(/\bgguf\b/g, 'GGUF')
320
- .replace(/\bhugging face\b/g, 'Hugging Face')
321
327
  .replace(/\bipfs\b/g, 'IPFS')
322
328
  .replace(/\bjson\b/g, 'JSON')
323
329
  .replace(/\bjwt\b/g, 'JWT')
324
330
  .replace(/\bmemory\.md\b/g, 'MEMORY.md')
325
- .replace(/\bopenai\b/g, 'OpenAI')
326
- .replace(/\banthropic\b/g, 'Anthropic')
327
- .replace(/\bgemini\b/g, 'Gemini')
328
331
  .replace(/\bos\b/g, 'OS')
329
332
  .replace(/\brpc\b/g, 'RPC')
330
333
  .replace(/\bsoul\.md\b/g, 'SOUL.md')
@@ -333,6 +336,4 @@ function restoreSpinnerTerms(value: string): string {
333
336
  .replace(/\bbase\b/g, 'Base')
334
337
  .replace(/\bethereum mainnet\b/g, 'Ethereum Mainnet')
335
338
  .replace(/\bethereum\b/g, 'Ethereum')
336
- .replace(/\bsepolia\b/g, 'Sepolia')
337
- .replace(/\bbase sepolia\b/g, 'Base Sepolia')
338
339
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
- import { theme } from './theme.js'
3
+ import { theme, PANEL_WIDTH } from './theme.js'
4
4
 
5
5
  type SurfaceTone = 'primary' | 'muted' | 'error'
6
6
 
@@ -13,8 +13,8 @@ type SurfaceProps = {
13
13
  }
14
14
 
15
15
  const toneColor: Record<SurfaceTone, string> = {
16
- primary: theme.accentPeriwinkle,
17
- muted: theme.border,
16
+ primary: theme.accentWhite,
17
+ muted: theme.dim,
18
18
  error: theme.accentError,
19
19
  }
20
20
 
@@ -25,23 +25,27 @@ export const Surface: React.FC<SurfaceProps> = ({
25
25
  tone = 'primary',
26
26
  children,
27
27
  }) => {
28
- const borderColor = toneColor[tone]
28
+ const titleColor = toneColor[tone]
29
29
  return (
30
- <Box flexDirection="column" borderStyle="round" borderColor={borderColor} paddingX={2} paddingY={0} width="100%">
31
- <Box flexDirection="column">
32
- <Text color={borderColor} bold>{title}</Text>
33
- {subtitle ? (
34
- typeof subtitle === 'string'
35
- ? <Text color={theme.dim}>{subtitle}</Text>
36
- : subtitle
30
+ <Box flexDirection="column" alignItems="center" paddingY={1} width="100%">
31
+ <Box flexDirection="column" paddingX={2} width={PANEL_WIDTH}>
32
+ <Box flexDirection="column">
33
+ <Text color={titleColor} bold>{title}</Text>
34
+ {subtitle ? (
35
+ typeof subtitle === 'string'
36
+ ? <Text color={theme.menuStatus}>{subtitle}</Text>
37
+ : subtitle
38
+ ) : null}
39
+ </Box>
40
+ {children ? <Box flexDirection="column" marginTop={1}>{children}</Box> : null}
41
+ {footer ? (
42
+ <Box marginTop={1}>
43
+ {typeof footer === 'string'
44
+ ? <Text color={theme.menuStatus}>{footer}</Text>
45
+ : footer}
46
+ </Box>
37
47
  ) : null}
38
48
  </Box>
39
- {children ? <Box flexDirection="column" marginTop={1}>{children}</Box> : null}
40
- {footer ? (
41
- <Box marginTop={1} borderTop={false}>
42
- <Text color={theme.dim}>{footer}</Text>
43
- </Box>
44
- ) : null}
45
49
  </Box>
46
50
  )
47
51
  }
@@ -0,0 +1,173 @@
1
+ import React, { useState, useRef, useEffect } from 'react'
2
+ import { Box, Text, useStdout } from 'ink'
3
+ import { theme, PANEL_WIDTH } from './theme.js'
4
+ import { useAppInput } from '../app/input/AppInputProvider.js'
5
+
6
+ type TextAreaProps = {
7
+ initialValue?: string
8
+ placeholder?: string
9
+ maxLength?: number
10
+ onSubmit: (value: string) => void
11
+ onCancel?: () => void
12
+ }
13
+
14
+ export function TextArea({
15
+ initialValue = '',
16
+ placeholder,
17
+ maxLength = 4096,
18
+ onSubmit,
19
+ onCancel,
20
+ }: TextAreaProps) {
21
+ const [value, setValue] = useState(initialValue)
22
+ const [cursor, setCursor] = useState(initialValue.length)
23
+ const { stdout } = useStdout()
24
+ const [columns, setColumns] = useState<number>(() => Math.floor(stdout?.columns ?? 80))
25
+
26
+ useEffect(() => {
27
+ if (!stdout) return
28
+ const handleResize = () => setColumns(Math.floor(stdout.columns ?? 80))
29
+ stdout.on('resize', handleResize)
30
+ return () => { stdout.off('resize', handleResize) }
31
+ }, [stdout])
32
+
33
+ const stateRef = useRef({ value, cursor })
34
+ stateRef.current = { value, cursor }
35
+
36
+ useAppInput((input, key) => {
37
+ const { value: val, cursor: cur } = stateRef.current
38
+
39
+ if (key.escape || (key.ctrl && input === 'c')) {
40
+ onCancel?.()
41
+ return
42
+ }
43
+ if (key.ctrl && input === 's') {
44
+ onSubmit(val)
45
+ return
46
+ }
47
+ if (key.return) {
48
+ const next = (val.slice(0, cur) + '\n' + val.slice(cur)).slice(0, maxLength)
49
+ setValue(next)
50
+ setCursor(cur + 1)
51
+ return
52
+ }
53
+ if (key.backspace || key.delete) {
54
+ if (cur === 0) return
55
+ setValue(val.slice(0, cur - 1) + val.slice(cur))
56
+ setCursor(cur - 1)
57
+ return
58
+ }
59
+ if (key.leftArrow) {
60
+ setCursor(Math.max(0, cur - 1))
61
+ return
62
+ }
63
+ if (key.rightArrow) {
64
+ setCursor(Math.min(val.length, cur + 1))
65
+ return
66
+ }
67
+ if (key.upArrow) {
68
+ setCursor(moveCursorUp(val, cur))
69
+ return
70
+ }
71
+ if (key.downArrow) {
72
+ setCursor(moveCursorDown(val, cur))
73
+ return
74
+ }
75
+ if (key.ctrl && input === 'u') {
76
+ const lineStart = Math.max(0, val.lastIndexOf('\n', cur - 1) + 1)
77
+ setValue(val.slice(0, lineStart) + val.slice(cur))
78
+ setCursor(lineStart)
79
+ return
80
+ }
81
+ if (key.ctrl || key.meta || key.tab) return
82
+ if (input) {
83
+ const clean = input.replace(/\r/g, '')
84
+ if (clean) {
85
+ const next = (val.slice(0, cur) + clean + val.slice(cur)).slice(0, maxLength)
86
+ setValue(next)
87
+ setCursor(cur + clean.length)
88
+ }
89
+ }
90
+ })
91
+
92
+ const displayWidth = Math.min(PANEL_WIDTH - 6, Math.max(20, columns - 6))
93
+ const [cursorLine, cursorCol] = cursorToLineCol(value, cursor)
94
+ const lines = value.split('\n')
95
+ const showPlaceholder = value.length === 0
96
+
97
+ return (
98
+ <Box flexDirection="column">
99
+ {showPlaceholder ? (
100
+ <Box flexDirection="row">
101
+ <Text color={theme.accentPeriwinkle}>{'> '}</Text>
102
+ <Box width={displayWidth}>
103
+ <Text>
104
+ <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{' '}</Text>
105
+ <Text color={theme.dim}>{placeholder ?? ''}</Text>
106
+ </Text>
107
+ </Box>
108
+ </Box>
109
+ ) : (
110
+ <Box flexDirection="column">
111
+ {lines.map((line, i) => {
112
+ const active = i === cursorLine
113
+ return (
114
+ <Box key={i} flexDirection="row">
115
+ <Text color={theme.accentPeriwinkle}>{active ? '> ' : ' '}</Text>
116
+ <Box width={displayWidth}>
117
+ {active ? (
118
+ <Text wrap="wrap">
119
+ <Text color={theme.text}>{line.slice(0, cursorCol)}</Text>
120
+ <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">
121
+ {line[cursorCol] ?? ' '}
122
+ </Text>
123
+ <Text color={theme.text}>{line.slice(cursorCol + 1)}</Text>
124
+ </Text>
125
+ ) : (
126
+ <Text color={theme.text} wrap="wrap">{line || ' '}</Text>
127
+ )}
128
+ </Box>
129
+ </Box>
130
+ )
131
+ })}
132
+ </Box>
133
+ )}
134
+ <Box marginTop={1}>
135
+ <Text color={theme.dim}>ctrl+s save · enter newline · esc back</Text>
136
+ </Box>
137
+ </Box>
138
+ )
139
+ }
140
+
141
+ function cursorToLineCol(val: string, cur: number): [number, number] {
142
+ const lines = val.split('\n')
143
+ let remaining = cur
144
+ for (let i = 0; i < lines.length; i++) {
145
+ const len = lines[i]!.length
146
+ if (remaining <= len) return [i, remaining]
147
+ remaining -= len + 1
148
+ }
149
+ const last = lines.length - 1
150
+ return [last, lines[last]!.length]
151
+ }
152
+
153
+ function moveCursorUp(val: string, cur: number): number {
154
+ const lines = val.split('\n')
155
+ const [lineIdx, colIdx] = cursorToLineCol(val, cur)
156
+ if (lineIdx === 0) return cur
157
+ const prevLine = lines[lineIdx - 1]!
158
+ const newCol = Math.min(colIdx, prevLine.length)
159
+ let pos = 0
160
+ for (let i = 0; i < lineIdx - 1; i++) pos += lines[i]!.length + 1
161
+ return pos + newCol
162
+ }
163
+
164
+ function moveCursorDown(val: string, cur: number): number {
165
+ const lines = val.split('\n')
166
+ const [lineIdx, colIdx] = cursorToLineCol(val, cur)
167
+ if (lineIdx >= lines.length - 1) return cur
168
+ const nextLine = lines[lineIdx + 1]!
169
+ const newCol = Math.min(colIdx, nextLine.length)
170
+ let pos = 0
171
+ for (let i = 0; i <= lineIdx; i++) pos += lines[i]!.length + 1
172
+ return pos + newCol
173
+ }
@@ -2,11 +2,6 @@ import React, { useState, useRef, useEffect } from 'react'
2
2
  import { Box, Text, useStdout } from 'ink'
3
3
  import { theme } from './theme.js'
4
4
  import { useAppInput } from '../app/input/AppInputProvider.js'
5
- import { moveVerticalVisual } from '../chat/input/chatInputState.js'
6
- import {
7
- getVisualLineIndex,
8
- getVisualLines,
9
- } from '../chat/input/textCursor.js'
10
5
 
11
6
  const DEFAULT_CHROME_WIDTH = 10
12
7
 
@@ -16,8 +11,8 @@ type TextInputProps = {
16
11
  isSecret?: boolean
17
12
  initialValue?: string
18
13
  allowEmpty?: boolean
19
- multiline?: boolean
20
14
  chromeWidth?: number
15
+ maxWidth?: number
21
16
  maxLength?: number
22
17
  validate?: (value: string) => string | null
23
18
  onSubmit: (value: string) => void
@@ -26,19 +21,14 @@ type TextInputProps = {
26
21
  onNavigateRight?: (value: string) => void
27
22
  }
28
23
 
29
- type RenderedTextInputLine = {
30
- visualLineIndex: number
31
- node: React.ReactNode
32
- }
33
-
34
24
  export function TextInput({
35
25
  label,
36
26
  placeholder,
37
27
  isSecret,
38
28
  initialValue = '',
39
29
  allowEmpty = false,
40
- multiline = false,
41
30
  chromeWidth = DEFAULT_CHROME_WIDTH,
31
+ maxWidth,
42
32
  maxLength = 4096,
43
33
  validate,
44
34
  onSubmit,
@@ -49,7 +39,6 @@ export function TextInput({
49
39
  const { stdout } = useStdout()
50
40
  const [value, setValue] = useState(initialValue)
51
41
  const [cursor, setCursor] = useState(initialValue.length)
52
- const [preferredColumn, setPreferredColumn] = useState<number | null>(null)
53
42
  const [error, setError] = useState<string | null>(null)
54
43
 
55
44
  const [columns, setColumns] = useState<number>(() => Math.floor(stdout?.columns ?? 80))
@@ -60,13 +49,13 @@ export function TextInput({
60
49
  return () => { stdout.off('resize', handleResize) }
61
50
  }, [stdout])
62
51
 
63
- const wrapWidth = textInputWrapWidth(columns, chromeWidth)
52
+ const wrapWidth = textInputWrapWidth(columns, chromeWidth, maxWidth)
64
53
 
65
- const stateRef = useRef({ value, cursor, preferredColumn, wrapWidth })
66
- stateRef.current = { value, cursor, preferredColumn, wrapWidth }
54
+ const stateRef = useRef({ value, cursor })
55
+ stateRef.current = { value, cursor }
67
56
 
68
57
  useAppInput((input, key) => {
69
- const { value: val, cursor: cur, preferredColumn: prefCol, wrapWidth: ww } = stateRef.current
58
+ const { value: val, cursor: cur } = stateRef.current
70
59
 
71
60
  const submitValue = (submit: (value: string) => void) => {
72
61
  if (!allowEmpty && val.trim().length === 0) {
@@ -83,14 +72,6 @@ export function TextInput({
83
72
  return true
84
73
  }
85
74
 
86
- if (multiline && isTextInputSoftBreak(key)) {
87
- const next = insertTextInputText(val, cur, '\n', maxLength)
88
- setValue(next.value)
89
- setCursor(next.cursor)
90
- setPreferredColumn(null)
91
- if (error) setError(null)
92
- return
93
- }
94
75
  if (key.return) {
95
76
  submitValue(onSubmit)
96
77
  return
@@ -105,7 +86,6 @@ export function TextInput({
105
86
  return
106
87
  }
107
88
  setCursor(Math.max(0, cur - 1))
108
- setPreferredColumn(null)
109
89
  return
110
90
  }
111
91
  if (key.rightArrow) {
@@ -114,34 +94,18 @@ export function TextInput({
114
94
  return
115
95
  }
116
96
  setCursor(Math.min(val.length, cur + 1))
117
- setPreferredColumn(null)
118
- return
119
- }
120
- if (multiline && (key.upArrow || key.downArrow)) {
121
- const result = moveVerticalVisual(val, cur, key.upArrow ? -1 : 1, ww, prefCol)
122
- if (result.kind === 'moved') setCursor(result.cursor)
123
- setPreferredColumn(result.preferredColumn)
124
97
  return
125
98
  }
126
99
  if (key.backspace || key.delete) {
127
100
  if (cur === 0) return
128
101
  setValue(val.slice(0, cur - 1) + val.slice(cur))
129
102
  setCursor(cur - 1)
130
- setPreferredColumn(null)
131
103
  if (error) setError(null)
132
104
  return
133
105
  }
134
106
  if (key.ctrl && input === 'u') {
135
- const lineStart = val.lastIndexOf('\n', cur - 1) + 1
136
- if (lineStart === cur) {
137
- if (!multiline || cur === 0) return
138
- setValue(val.slice(0, cur - 1) + val.slice(cur))
139
- setCursor(cur - 1)
140
- } else {
141
- setValue(val.slice(0, lineStart) + val.slice(cur))
142
- setCursor(lineStart)
143
- }
144
- setPreferredColumn(null)
107
+ setValue(val.slice(cur))
108
+ setCursor(0)
145
109
  if (error) setError(null)
146
110
  return
147
111
  }
@@ -149,14 +113,12 @@ export function TextInput({
149
113
  return
150
114
  }
151
115
  if (input) {
152
- const clean = multiline
153
- ? input.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
154
- : input.replace(/[\r\n]/g, '')
116
+ const clean = input.replace(/[\r\n]/g, '')
155
117
  if (clean) {
156
- const next = insertTextInputText(val, cur, clean, maxLength)
157
- setValue(next.value)
158
- setCursor(next.cursor)
159
- setPreferredColumn(null)
118
+ const cleanCursor = Math.max(0, Math.min(cur, val.length))
119
+ const next = (val.slice(0, cleanCursor) + clean + val.slice(cleanCursor)).slice(0, maxLength)
120
+ setValue(next)
121
+ setCursor(Math.min(cleanCursor + clean.length, next.length))
160
122
  if (error) setError(null)
161
123
  }
162
124
  }
@@ -164,96 +126,32 @@ export function TextInput({
164
126
 
165
127
  const display = isSecret ? '*'.repeat(value.length) : value
166
128
  const showPlaceholder = value.length === 0 && placeholder
167
- const renderedLines = multiline
168
- ? renderTextInputLines(display, cursor, true, wrapWidth)
169
- : []
170
129
 
171
130
  return (
172
131
  <Box flexDirection="column">
173
132
  {label ? <Text color={theme.dim}>{label}</Text> : null}
174
- {multiline && !showPlaceholder ? (
175
- <Box flexDirection="column">
176
- {renderedLines.map(line => (
177
- <Box key={line.visualLineIndex} flexDirection="row">
178
- <Text color={line.visualLineIndex === 0 ? theme.accentPeriwinkle : theme.dim}>
179
- {line.visualLineIndex === 0 ? '> ' : ' '}
180
- </Text>
181
- <Box width={wrapWidth}>{line.node}</Box>
182
- </Box>
183
- ))}
184
- </Box>
185
- ) : (
186
- <Box flexDirection="row">
187
- <Text color={theme.accentPeriwinkle}>{'> '}</Text>
188
- <Box width={wrapWidth}>
189
- {showPlaceholder ? (
190
- <Text wrap={multiline ? 'wrap' : 'truncate-end'}>
191
- <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{' '}</Text>
192
- <Text color={theme.dim}>{placeholder}</Text>
193
- </Text>
194
- ) : (
195
- <Text color={theme.text} wrap="truncate-end">
196
- {display.slice(0, cursor)}
197
- <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{display[cursor] ?? ' '}</Text>
198
- {display.slice(cursor + 1)}
199
- </Text>
200
- )}
201
- </Box>
133
+ <Box flexDirection="row">
134
+ <Text color={theme.accentPeriwinkle}>{'> '}</Text>
135
+ <Box width={wrapWidth}>
136
+ {showPlaceholder ? (
137
+ <Text wrap="truncate-end">
138
+ <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{' '}</Text>
139
+ <Text color={theme.dim}>{placeholder}</Text>
140
+ </Text>
141
+ ) : (
142
+ <Text color={theme.text} wrap="truncate-end">
143
+ {display.slice(0, cursor)}
144
+ <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{display[cursor] ?? ' '}</Text>
145
+ {display.slice(cursor + 1)}
146
+ </Text>
147
+ )}
202
148
  </Box>
203
- )}
149
+ </Box>
204
150
  {error ? <Text color={theme.accentError}>{error}</Text> : null}
205
151
  </Box>
206
152
  )
207
153
  }
208
154
 
209
- export function textInputWrapWidth(columns: number, chromeWidth = DEFAULT_CHROME_WIDTH): number {
210
- return Math.max(1, Math.floor(columns) - Math.max(0, Math.floor(chromeWidth)))
211
- }
212
-
213
- export function insertTextInputText(value: string, cursor: number, input: string, maxLength = 4096): { value: string; cursor: number } {
214
- const cleanCursor = Math.max(0, Math.min(cursor, value.length))
215
- const next = (value.slice(0, cleanCursor) + input + value.slice(cleanCursor)).slice(0, maxLength)
216
- return {
217
- value: next,
218
- cursor: Math.min(cleanCursor + input.length, next.length),
219
- }
220
- }
221
-
222
- export function isTextInputSoftBreak(key: { return: boolean; shift?: boolean; meta?: boolean }): boolean {
223
- return key.return && Boolean(key.shift || key.meta)
224
- }
225
-
226
- export function renderTextInputLines(
227
- value: string,
228
- cursor: number,
229
- showCursor: boolean,
230
- wrapWidth: number,
231
- ): RenderedTextInputLine[] {
232
- const lines = getVisualLines(value, wrapWidth)
233
- const cursorLine = getVisualLineIndex(lines, cursor)
234
-
235
- return lines.map((line, visualLineIndex) => {
236
- const text = value.slice(line.start, line.end)
237
- if (!showCursor || visualLineIndex !== cursorLine) {
238
- return {
239
- visualLineIndex,
240
- node: <Text color={theme.text} wrap="wrap">{text || ' '}</Text>,
241
- }
242
- }
243
-
244
- const column = Math.max(0, Math.min(cursor - line.start, text.length))
245
- const before = text.slice(0, column)
246
- const atChar = text[column] ?? ' '
247
- const after = text.slice(column + 1)
248
- return {
249
- visualLineIndex,
250
- node: (
251
- <Text color={theme.text} wrap="wrap">
252
- {before}
253
- <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{atChar}</Text>
254
- {after}
255
- </Text>
256
- ),
257
- }
258
- })
155
+ export function textInputWrapWidth(columns: number, chromeWidth = DEFAULT_CHROME_WIDTH, maxWidth = 42): number {
156
+ return Math.min(maxWidth, Math.max(1, Math.floor(columns) - Math.max(0, Math.floor(chromeWidth))))
259
157
  }
package/src/ui/theme.ts CHANGED
@@ -1,16 +1,23 @@
1
1
  export const palette: Array<[number, number, number]> = [
2
- [0xff, 0xff, 0xff],
3
- [0xd8, 0xdc, 0xfa],
4
- [0xe8, 0xee, 0xfd],
5
- [0xd8, 0xdc, 0xfa],
6
- [0xff, 0xff, 0xff],
2
+ [0xf3, 0xd8, 0xe0],
3
+ [0xf3, 0xdf, 0xce],
4
+ [0xf2, 0xec, 0xca],
5
+ [0xd7, 0xed, 0xda],
6
+ [0xd2, 0xe5, 0xf3],
7
+ [0xe2, 0xd6, 0xf0],
8
+ [0xf3, 0xd8, 0xe0],
7
9
  ]
8
10
 
9
11
  export const theme = {
10
- accentPeriwinkle: '#d8dcfa',
11
- accentBlue: '#e8eefd',
12
- accentWhite: '#f5f8ff',
13
- accentError: '#d99898',
12
+ accentPeriwinkle: '#f0eee8',
13
+ accentBlue: '#dde4f0',
14
+ accentWhite: '#f8f6f2',
15
+ accentError: '#e8b8b8',
16
+ accentHighlight: '#f0c8c8',
17
+ wordmark: '#fafafa',
18
+ wordmarkEth: '#9aa0b0',
19
+ menuShortcut: '#8a8a9c',
20
+ menuStatus: '#7a8090',
14
21
  modePlan: '#f0c7a8',
15
22
  modeAcceptEdits: '#c7b6f2',
16
23
  diffAdded: '#8fd49d',
@@ -30,12 +37,14 @@ export const theme = {
30
37
  codePunctuation: '#aeb4c8',
31
38
  codeTag: '#ffb3b3',
32
39
  codeAttribute: '#f2d087',
33
- border: '#555555',
34
- dim: '#909090',
35
- text: '#f1f1f1',
36
- textSubtle: '#9b9b9b',
40
+ border: '#3a3f4f',
41
+ dim: '#7a8090',
42
+ text: '#dadce6',
43
+ textSubtle: '#9aa0b0',
37
44
  } as const
38
45
 
46
+ export const PANEL_WIDTH = 48
47
+
39
48
  export function gradientColor(t: number): string {
40
49
  const s = Math.max(0, Math.min(1, t)) * (palette.length - 1)
41
50
  const i = Math.min(Math.floor(s), palette.length - 2)