ethagent 3.3.4 → 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 -260
  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
@@ -1,247 +0,0 @@
1
- import {
2
- cursorColumn,
3
- cursorOnLastLineAtColumn,
4
- getVisualLines,
5
- moveVerticalVisualCursor,
6
- moveVerticalCursor,
7
- normalizeCursor,
8
- } from './textCursor.js'
9
-
10
- export type ChatBuffer = {
11
- value: string
12
- cursor: number
13
- }
14
-
15
- export type FileMentionToken = {
16
- start: number
17
- end: number
18
- query: string
19
- }
20
-
21
- export type HistoryPreviewState = {
22
- historyIndex: number | null
23
- historyPreviewActive: boolean
24
- draftBuffer: ChatBuffer
25
- preferredColumn: number | null
26
- }
27
-
28
- export type VerticalMoveResult =
29
- | { kind: 'moved'; cursor: number; preferredColumn: number }
30
- | { kind: 'boundary-top'; cursor: number; preferredColumn: number }
31
- | { kind: 'boundary-bottom'; cursor: number; preferredColumn: number }
32
-
33
- export function emptyBuffer(): ChatBuffer {
34
- return { value: '', cursor: 0 }
35
- }
36
-
37
- export function bufferFromValue(value: string, cursor = value.length): ChatBuffer {
38
- const next = normalizeCursor(value, cursor)
39
- return { value: next.value, cursor: next.offset }
40
- }
41
-
42
- export function canNavigateHistory(
43
- _buffer: ChatBuffer,
44
- historyLength: number,
45
- _historyIndex: number | null,
46
- _historyPreviewActive: boolean,
47
- ): boolean {
48
- return historyLength > 0
49
- }
50
-
51
- export function beginHistoryPreview(
52
- buffer: ChatBuffer,
53
- history: string[],
54
- direction: 1 | -1,
55
- _preferredColumn: number | null,
56
- ): { preview: HistoryPreviewState; buffer: ChatBuffer } | null {
57
- if (history.length === 0) return null
58
- const nextIndex = direction === -1 ? history.length - 1 : 0
59
- const chosen = history[nextIndex] ?? ''
60
- return {
61
- preview: {
62
- historyIndex: nextIndex,
63
- historyPreviewActive: true,
64
- draftBuffer: buffer,
65
- preferredColumn: null,
66
- },
67
- buffer: bufferFromValue(chosen),
68
- }
69
- }
70
-
71
- export function moveThroughHistory(
72
- history: string[],
73
- historyIndex: number,
74
- direction: 1 | -1,
75
- draftBuffer: ChatBuffer,
76
- _preferredColumn: number | null,
77
- ): { preview: HistoryPreviewState; buffer: ChatBuffer } {
78
- const next = historyIndex + direction
79
- if (next < 0) {
80
- const chosen = history[0] ?? ''
81
- return {
82
- preview: {
83
- historyIndex: 0,
84
- historyPreviewActive: true,
85
- draftBuffer,
86
- preferredColumn: null,
87
- },
88
- buffer: bufferFromValue(chosen),
89
- }
90
- }
91
- if (next >= history.length) {
92
- return {
93
- preview: {
94
- historyIndex: null,
95
- historyPreviewActive: false,
96
- draftBuffer,
97
- preferredColumn: null,
98
- },
99
- buffer: bufferFromValue(draftBuffer.value),
100
- }
101
- }
102
- const chosen = history[next] ?? ''
103
- return {
104
- preview: {
105
- historyIndex: next,
106
- historyPreviewActive: true,
107
- draftBuffer,
108
- preferredColumn: null,
109
- },
110
- buffer: bufferFromValue(chosen),
111
- }
112
- }
113
-
114
- export function exitHistoryPreview(
115
- buffer: ChatBuffer,
116
- ): { historyIndex: null; historyPreviewActive: false; draftBuffer: ChatBuffer; preferredColumn: null } {
117
- return {
118
- historyIndex: null,
119
- historyPreviewActive: false,
120
- draftBuffer: buffer,
121
- preferredColumn: null,
122
- }
123
- }
124
-
125
- export function bufferFromLastLine(value: string, preferredColumn: number | null = null): ChatBuffer {
126
- const next = cursorOnLastLineAtColumn(value, preferredColumn ?? 0)
127
- return { value: next.value, cursor: next.offset }
128
- }
129
-
130
- export function deleteToLineStart(buffer: ChatBuffer, wrapWidth: number): ChatBuffer {
131
- const normalized = normalizeCursor(buffer.value, buffer.cursor)
132
- const { value, offset } = normalized
133
- if (offset <= 0) return { value, cursor: 0 }
134
-
135
- if (value[offset - 1] === '\n') {
136
- return {
137
- value: value.slice(0, offset - 1) + value.slice(offset),
138
- cursor: offset - 1,
139
- }
140
- }
141
-
142
- const lineStart = visualLineStart(value, offset, wrapWidth)
143
- return {
144
- value: value.slice(0, lineStart) + value.slice(offset),
145
- cursor: lineStart,
146
- }
147
- }
148
-
149
- export function moveVertical(
150
- text: string,
151
- cursor: number,
152
- direction: 1 | -1,
153
- preferredColumn: number | null = null,
154
- ): VerticalMoveResult {
155
- const normalized = normalizeCursor(text, cursor)
156
- const nextColumn = preferredColumn ?? cursorColumn(normalized.value, normalized.offset)
157
- const moved = moveVerticalCursor(normalized, direction, nextColumn)
158
- if (moved.moved) {
159
- return { kind: 'moved', cursor: moved.cursor.offset, preferredColumn: nextColumn }
160
- }
161
- return {
162
- kind: direction === -1 ? 'boundary-top' : 'boundary-bottom',
163
- cursor: normalized.offset,
164
- preferredColumn: nextColumn,
165
- }
166
- }
167
-
168
- export function moveVerticalVisual(
169
- text: string,
170
- cursor: number,
171
- direction: 1 | -1,
172
- wrapWidth: number,
173
- preferredColumn: number | null = null,
174
- ): VerticalMoveResult {
175
- const normalized = normalizeCursor(text, cursor)
176
- const position = visualPosition(normalized.value, normalized.offset, wrapWidth)
177
- const nextColumn = preferredColumn ?? position.column
178
- const moved = moveVerticalVisualCursor(normalized, direction, wrapWidth, nextColumn)
179
- if (moved.moved) {
180
- return { kind: 'moved', cursor: moved.cursor.offset, preferredColumn: nextColumn }
181
- }
182
- return {
183
- kind: direction === -1 ? 'boundary-top' : 'boundary-bottom',
184
- cursor: normalized.offset,
185
- preferredColumn: nextColumn,
186
- }
187
- }
188
-
189
- export function detectActiveFileMention(value: string, cursor: number): FileMentionToken | undefined {
190
- const safeCursor = Math.max(0, Math.min(cursor, value.length))
191
- const left = value.slice(0, safeCursor)
192
- const atIndex = left.lastIndexOf('@')
193
- if (atIndex === -1) return undefined
194
- const before = atIndex === 0 ? '' : value[atIndex - 1]
195
- if (before && !/\s|\(|\[|\{/.test(before)) return undefined
196
- const between = value.slice(atIndex + 1, safeCursor)
197
- if (/\s/.test(between)) return undefined
198
- let end = safeCursor
199
- while (end < value.length && !/\s/.test(value[end]!)) end += 1
200
- return { start: atIndex, end, query: between }
201
- }
202
-
203
- export function replaceActiveFileMention(buffer: ChatBuffer, replacementPath: string): ChatBuffer {
204
- const mention = detectActiveFileMention(buffer.value, buffer.cursor)
205
- if (!mention) return buffer
206
- const replacement = `@${replacementPath}`
207
- const value = buffer.value.slice(0, mention.start) + replacement + buffer.value.slice(mention.end)
208
- return { value, cursor: mention.start + replacement.length }
209
- }
210
-
211
- function visualPosition(value: string, offset: number, wrapWidth: number): { column: number } {
212
- const lines = getVisualLines(value, wrapWidth)
213
- for (const entry of lines) {
214
- if (entry.start === entry.end && offset === entry.start) {
215
- return { column: 0 }
216
- }
217
- if (offset >= entry.start && offset < entry.end) {
218
- return { column: offset - entry.start }
219
- }
220
- }
221
-
222
- for (let index = lines.length - 1; index >= 0; index -= 1) {
223
- const entry = lines[index]!
224
- if (offset === entry.end) {
225
- return { column: entry.end - entry.start }
226
- }
227
- }
228
-
229
- return { column: 0 }
230
- }
231
-
232
- function visualLineStart(value: string, offset: number, wrapWidth: number): number {
233
- const lines = getVisualLines(value, wrapWidth)
234
- for (let index = 0; index < lines.length; index += 1) {
235
- const entry = lines[index]!
236
- if (entry.start === entry.end && offset === entry.start) return entry.start
237
- if (offset > entry.start && offset <= entry.end) return entry.start
238
- if (offset === entry.start) {
239
- const previous = lines[index - 1]
240
- if (previous && previous.end === entry.start) return previous.start
241
- return entry.start
242
- }
243
- }
244
-
245
- const last = lines[lines.length - 1]
246
- return last ? last.start : 0
247
- }
@@ -1,49 +0,0 @@
1
- export const LARGE_PASTE_THRESHOLD = 800
2
-
3
- export type PastedTextRef = {
4
- id: number
5
- content: string
6
- }
7
-
8
- export function normalizePastedText(text: string): string {
9
- return text
10
- .replaceAll('\x1b[200~', '')
11
- .replaceAll('\x1b[201~', '')
12
- .replace(/(^|\n)(?:\[?200~|\[?201~)(?=\n|$)/g, '$1')
13
- .replace(/\r\n/g, '\n')
14
- .replace(/\r/g, '\n')
15
- }
16
-
17
- export function shouldCollapsePastedText(
18
- text: string,
19
- maxInlineLines: number,
20
- threshold = LARGE_PASTE_THRESHOLD,
21
- ): boolean {
22
- return text.length > threshold || countPastedTextLineBreaks(text) > maxInlineLines
23
- }
24
-
25
- export function countPastedTextLineBreaks(text: string): number {
26
- return (text.match(/\n/g) ?? []).length
27
- }
28
-
29
- export function formatPastedTextRef(id: number, chars: number): string {
30
- return `[Pasted Content ${chars} chars #${id}]`
31
- }
32
-
33
- export function expandPastedTextRefs(input: string, refs: Map<number, PastedTextRef>): string {
34
- const matches = [...input.matchAll(/\[Pasted Content (\d+) chars #(\d+)\]/g)]
35
- let expanded = input
36
-
37
- for (let index = matches.length - 1; index >= 0; index -= 1) {
38
- const match = matches[index]!
39
- const id = Number(match[2])
40
- const ref = refs.get(id)
41
- if (!ref) continue
42
- expanded =
43
- expanded.slice(0, match.index) +
44
- ref.content +
45
- expanded.slice((match.index ?? 0) + match[0].length)
46
- }
47
-
48
- return expanded
49
- }
@@ -1,30 +0,0 @@
1
- export type ImageRef = { path: string; mimeType?: string }
2
-
3
- const IMAGE_REF_MARKER_RE = /\[Image\s+#(\d+)\]/g
4
-
5
- export function expandImageRefs(text: string, refs: Map<number, ImageRef>): string {
6
- return text.replace(IMAGE_REF_MARKER_RE, (full, raw: string) => {
7
- const ref = refs.get(Number(raw))
8
- return ref ? `[image: ${ref.path}]` : full
9
- })
10
- }
11
-
12
- export function referencedImageIds(text: string): Set<number> {
13
- const out = new Set<number>()
14
- for (const match of text.matchAll(IMAGE_REF_MARKER_RE)) {
15
- const id = Number(match[1])
16
- if (Number.isFinite(id)) out.add(id)
17
- }
18
- return out
19
- }
20
-
21
- export function pruneImageRefs(refs: Map<number, ImageRef>, text: string): void {
22
- const referenced = referencedImageIds(text)
23
- for (const id of [...refs.keys()]) {
24
- if (!referenced.has(id)) refs.delete(id)
25
- }
26
- }
27
-
28
- export function formatImageRefMarker(id: number): string {
29
- return `[Image #${id}]`
30
- }
@@ -1,93 +0,0 @@
1
- import React from 'react'
2
- import { Text } from 'ink'
3
- import { theme } from '../../ui/theme.js'
4
- import {
5
- getVisibleVisualLineWindow,
6
- getVisualLineIndex,
7
- getVisualLines,
8
- } from './textCursor.js'
9
-
10
- const STACK_HORIZONTAL_PADDING = 2
11
- const INPUT_BORDER_WIDTH = 2
12
- const INPUT_HORIZONTAL_PADDING = 4
13
- const PROMPT_PREFIX_WIDTH = 2
14
-
15
- type RenderedVisualLine = {
16
- visualLineIndex: number
17
- node: React.ReactNode
18
- }
19
-
20
- type RenderedInputViewport = {
21
- lines: RenderedVisualLine[]
22
- hiddenAbove: number
23
- hiddenBelow: number
24
- visibleLineCount: number
25
- }
26
-
27
- export function renderWithCursor(
28
- value: string,
29
- cursor: number,
30
- showCursor: boolean,
31
- wrapWidth: number,
32
- maxVisibleLines: number,
33
- ): RenderedInputViewport {
34
- const lines = getVisualLines(value, wrapWidth)
35
- const cursorLine = getVisualLineIndex(lines, cursor)
36
- const window = getVisibleVisualLineWindow(lines.length, cursorLine, maxVisibleLines)
37
- const visibleLines = lines.slice(window.start, window.end)
38
-
39
- if (!showCursor) {
40
- return {
41
- lines: visibleLines.map((line, i) => ({
42
- visualLineIndex: window.start + i,
43
- node: (
44
- <Text color={theme.text} wrap="wrap">
45
- {value.slice(line.start, line.end) || ' '}
46
- </Text>
47
- ),
48
- })),
49
- hiddenAbove: window.start,
50
- hiddenBelow: lines.length - window.end,
51
- visibleLineCount: Math.max(1, visibleLines.length),
52
- }
53
- }
54
-
55
- return {
56
- lines: visibleLines.map((line, i) => {
57
- const visualLineIndex = window.start + i
58
- const text = value.slice(line.start, line.end)
59
- if (visualLineIndex !== cursorLine) {
60
- return {
61
- visualLineIndex,
62
- node: <Text color={theme.text} wrap="wrap">{text || ' '}</Text>,
63
- }
64
- }
65
- const column = Math.max(0, Math.min(cursor - line.start, text.length))
66
- const before = text.slice(0, column)
67
- const atChar = text[column] ?? ' '
68
- const after = text.slice(column + 1)
69
- return {
70
- visualLineIndex,
71
- node: (
72
- <Text color={theme.text} wrap="wrap">
73
- {before}
74
- <Text backgroundColor={theme.accentPeriwinkle} color="#0c0c1f">{atChar}</Text>
75
- {after}
76
- </Text>
77
- ),
78
- }
79
- }),
80
- hiddenAbove: window.start,
81
- hiddenBelow: lines.length - window.end,
82
- visibleLineCount: Math.max(1, visibleLines.length),
83
- }
84
- }
85
-
86
- export function inputWrapWidth(columns: number): number {
87
- const fixedChromeWidth =
88
- STACK_HORIZONTAL_PADDING
89
- + INPUT_BORDER_WIDTH
90
- + INPUT_HORIZONTAL_PADDING
91
- + PROMPT_PREFIX_WIDTH
92
- return Math.max(1, Math.floor(columns) - fixedChromeWidth)
93
- }
@@ -1,212 +0,0 @@
1
- import wrapAnsi from 'wrap-ansi'
2
-
3
- export type TextCursor = {
4
- value: string
5
- offset: number
6
- }
7
-
8
- export type CursorMoveResult = {
9
- cursor: TextCursor
10
- moved: boolean
11
- }
12
-
13
- export type VisualLine = {
14
- start: number
15
- end: number
16
- }
17
-
18
- export type VisualLineWindow = {
19
- start: number
20
- end: number
21
- }
22
-
23
- export function normalizeCursor(value: string, offset: number): TextCursor {
24
- return { value, offset: clampOffset(value, offset) }
25
- }
26
-
27
- export function moveLeft(cursor: TextCursor): TextCursor {
28
- return normalizeCursor(cursor.value, cursor.offset - 1)
29
- }
30
-
31
- export function moveRight(cursor: TextCursor): TextCursor {
32
- return normalizeCursor(cursor.value, cursor.offset + 1)
33
- }
34
-
35
- export function moveVerticalCursor(
36
- cursor: TextCursor,
37
- direction: 1 | -1,
38
- preferredColumn?: number,
39
- ): CursorMoveResult {
40
- const lines = getLogicalLines(cursor.value)
41
- const position = positionFromOffset(lines, cursor.offset)
42
- const targetLine = position.line + direction
43
- if (targetLine < 0 || targetLine >= lines.length) {
44
- return { cursor, moved: false }
45
- }
46
- const targetOffset = offsetFromPosition(lines, targetLine, preferredColumn ?? position.column)
47
- return {
48
- cursor: normalizeCursor(cursor.value, targetOffset),
49
- moved: targetOffset !== cursor.offset,
50
- }
51
- }
52
-
53
- export function getVisualLines(value: string, wrapWidth: number): VisualLine[] {
54
- const safeWrapWidth = Math.max(1, Math.floor(wrapWidth))
55
- const lines: VisualLine[] = []
56
- let start = 0
57
-
58
- for (let index = 0; index <= value.length; index += 1) {
59
- if (index !== value.length && value[index] !== '\n') continue
60
-
61
- const end = index
62
- if (start === end) {
63
- lines.push({ start, end })
64
- } else {
65
- const logical = value.slice(start, end)
66
- const wrapped = wrapAnsi(logical, safeWrapWidth, { trim: false, hard: true, wordWrap: true })
67
- const wrappedLines = wrapped.split('\n')
68
- let cursor = start
69
- let lastEnd = start
70
- for (const line of wrappedLines) {
71
- const lineEnd = cursor + line.length
72
- lines.push({ start: cursor, end: lineEnd })
73
- cursor = lineEnd
74
- lastEnd = lineEnd
75
- }
76
- if (end === value.length && lastEnd === end && wrappedLines[wrappedLines.length - 1]!.length === safeWrapWidth) {
77
- lines.push({ start: end, end })
78
- }
79
- }
80
- start = index + 1
81
- }
82
-
83
- return lines.length > 0 ? lines : [{ start: 0, end: 0 }]
84
- }
85
-
86
- export function getVisualLineIndex(lines: VisualLine[], offset: number): number {
87
- return visualPositionFromOffset(lines, offset).line
88
- }
89
-
90
- export function getVisibleVisualLineWindow(
91
- totalLines: number,
92
- cursorLine: number,
93
- maxVisibleLines: number,
94
- ): VisualLineWindow {
95
- const safeTotal = Math.max(0, Math.floor(totalLines))
96
- if (safeTotal === 0) return { start: 0, end: 0 }
97
-
98
- const safeMaxVisible = Math.max(1, Math.floor(maxVisibleLines))
99
- if (safeTotal <= safeMaxVisible) return { start: 0, end: safeTotal }
100
-
101
- const safeCursorLine = Math.max(0, Math.min(Math.floor(cursorLine), safeTotal - 1))
102
- const half = Math.floor(safeMaxVisible / 2)
103
- const start = Math.max(0, Math.min(
104
- safeCursorLine - half,
105
- safeTotal - safeMaxVisible,
106
- ))
107
-
108
- return { start, end: start + safeMaxVisible }
109
- }
110
-
111
- export function moveVerticalVisualCursor(
112
- cursor: TextCursor,
113
- direction: 1 | -1,
114
- wrapWidth: number,
115
- preferredColumn?: number,
116
- ): CursorMoveResult {
117
- const lines = getVisualLines(cursor.value, wrapWidth)
118
- const position = visualPositionFromOffset(lines, cursor.offset)
119
- const targetLine = position.line + direction
120
- if (targetLine < 0 || targetLine >= lines.length) {
121
- return { cursor, moved: false }
122
- }
123
- const target = lines[targetLine]!
124
- const targetColumn = Math.min(preferredColumn ?? position.column, target.end - target.start)
125
- const targetOffset = target.start + Math.max(0, targetColumn)
126
- return {
127
- cursor: normalizeCursor(cursor.value, targetOffset),
128
- moved: targetOffset !== cursor.offset,
129
- }
130
- }
131
-
132
- export function cursorOnLastLine(value: string, _offset: number): TextCursor {
133
- const lines = getLogicalLines(value)
134
- const lastLine = Math.max(0, lines.length - 1)
135
- return normalizeCursor(value, offsetFromPosition(lines, lastLine, 0))
136
- }
137
-
138
- export function cursorOnLastLineAtColumn(value: string, column: number): TextCursor {
139
- const lines = getLogicalLines(value)
140
- const lastLine = Math.max(0, lines.length - 1)
141
- return normalizeCursor(value, offsetFromPosition(lines, lastLine, column))
142
- }
143
-
144
- export function cursorColumn(value: string, offset: number): number {
145
- return positionFromOffset(getLogicalLines(value), clampOffset(value, offset)).column
146
- }
147
-
148
- type LogicalLine = {
149
- start: number
150
- end: number
151
- }
152
-
153
- function getLogicalLines(value: string): LogicalLine[] {
154
- const lines: LogicalLine[] = []
155
- let start = 0
156
- for (let index = 0; index < value.length; index += 1) {
157
- if (value[index] === '\n') {
158
- lines.push({ start, end: index })
159
- start = index + 1
160
- }
161
- }
162
- lines.push({ start, end: value.length })
163
- return lines
164
- }
165
-
166
- function positionFromOffset(lines: LogicalLine[], rawOffset: number): { line: number; column: number } {
167
- const offset = Math.max(0, rawOffset)
168
- for (let line = 0; line < lines.length; line += 1) {
169
- const entry = lines[line]!
170
- const next = lines[line + 1]
171
- if (!next || offset < next.start) {
172
- return {
173
- line,
174
- column: Math.max(0, Math.min(offset, entry.end) - entry.start),
175
- }
176
- }
177
- }
178
- const last = lines[lines.length - 1]!
179
- return { line: lines.length - 1, column: last.end - last.start }
180
- }
181
-
182
- function offsetFromPosition(lines: LogicalLine[], line: number, column: number): number {
183
- const entry = lines[Math.max(0, Math.min(line, lines.length - 1))]!
184
- return entry.start + Math.min(Math.max(0, column), entry.end - entry.start)
185
- }
186
-
187
- function visualPositionFromOffset(lines: VisualLine[], rawOffset: number): { line: number; column: number } {
188
- const offset = Math.max(0, rawOffset)
189
- for (let line = 0; line < lines.length; line += 1) {
190
- const entry = lines[line]!
191
- if (entry.start === entry.end && offset === entry.start) {
192
- return { line, column: 0 }
193
- }
194
- if (offset >= entry.start && offset < entry.end) {
195
- return { line, column: offset - entry.start }
196
- }
197
- }
198
-
199
- for (let line = lines.length - 1; line >= 0; line -= 1) {
200
- const entry = lines[line]!
201
- if (offset === entry.end) {
202
- return { line, column: entry.end - entry.start }
203
- }
204
- }
205
-
206
- const last = lines[lines.length - 1]!
207
- return { line: lines.length - 1, column: last.end - last.start }
208
- }
209
-
210
- function clampOffset(value: string, offset: number): number {
211
- return Math.max(0, Math.min(value.length, offset))
212
- }