indusagi 0.12.13 → 0.12.16

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 (344) hide show
  1. package/dist/agent/agent-session.d.ts +352 -0
  2. package/dist/agent/agent-session.d.ts.map +1 -0
  3. package/dist/agent/agent-session.js +870 -0
  4. package/dist/agent/agent-session.js.map +1 -0
  5. package/dist/agent/agent-session.test.d.ts +5 -0
  6. package/dist/agent/agent-session.test.d.ts.map +1 -0
  7. package/dist/agent/agent-session.test.js +129 -0
  8. package/dist/agent/agent-session.test.js.map +1 -0
  9. package/dist/agent/agent.d.ts +108 -2
  10. package/dist/agent/agent.d.ts.map +1 -1
  11. package/dist/agent/agent.js +210 -17
  12. package/dist/agent/agent.js.map +1 -1
  13. package/dist/agent/bash-executor.d.ts +33 -0
  14. package/dist/agent/bash-executor.d.ts.map +1 -0
  15. package/dist/agent/bash-executor.js +197 -0
  16. package/dist/agent/bash-executor.js.map +1 -0
  17. package/dist/agent/compaction/compaction.d.ts +87 -0
  18. package/dist/agent/compaction/compaction.d.ts.map +1 -0
  19. package/dist/agent/compaction/compaction.js +598 -0
  20. package/dist/agent/compaction/compaction.js.map +1 -0
  21. package/dist/agent/compaction/index.d.ts +6 -0
  22. package/dist/agent/compaction/index.d.ts.map +1 -0
  23. package/dist/agent/compaction/index.js +6 -0
  24. package/dist/agent/compaction/index.js.map +1 -0
  25. package/dist/agent/compaction/utils.d.ts +41 -0
  26. package/dist/agent/compaction/utils.d.ts.map +1 -0
  27. package/dist/agent/compaction/utils.js +240 -0
  28. package/dist/agent/compaction/utils.js.map +1 -0
  29. package/dist/agent/event-bus.js +1 -3
  30. package/dist/agent/event-bus.js.map +1 -1
  31. package/dist/agent/index.d.ts +11 -5
  32. package/dist/agent/index.d.ts.map +1 -1
  33. package/dist/agent/index.js +19 -8
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/messages.d.ts +2 -48
  36. package/dist/agent/messages.d.ts.map +1 -1
  37. package/dist/agent/messages.js.map +1 -1
  38. package/dist/agent/proxy.js.map +1 -1
  39. package/dist/agent/session-manager.d.ts +2 -3
  40. package/dist/agent/session-manager.d.ts.map +1 -1
  41. package/dist/agent/session-manager.js +10 -6
  42. package/dist/agent/session-manager.js.map +1 -1
  43. package/dist/agent/settings-manager.d.ts +204 -0
  44. package/dist/agent/settings-manager.d.ts.map +1 -0
  45. package/dist/agent/settings-manager.js +594 -0
  46. package/dist/agent/settings-manager.js.map +1 -0
  47. package/dist/agent/state-manager.js +1 -0
  48. package/dist/agent/state-manager.js.map +1 -1
  49. package/dist/agent/telemetry.js +1 -3
  50. package/dist/agent/telemetry.js.map +1 -1
  51. package/dist/agent/tools/bash.js +1 -0
  52. package/dist/agent/tools/bash.js.map +1 -1
  53. package/dist/agent/tools/edit.test.d.ts +6 -0
  54. package/dist/agent/tools/edit.test.d.ts.map +1 -0
  55. package/dist/agent/tools/edit.test.js +196 -0
  56. package/dist/agent/tools/edit.test.js.map +1 -0
  57. package/dist/agent/tools/index.d.ts +26 -26
  58. package/dist/agent/tools/registry.d.ts +1 -1
  59. package/dist/agent/tools/registry.d.ts.map +1 -1
  60. package/dist/agent/tools/registry.js +2 -4
  61. package/dist/agent/tools/registry.js.map +1 -1
  62. package/dist/agent/tools/todo-store.js +2 -1
  63. package/dist/agent/tools/todo-store.js.map +1 -1
  64. package/dist/agent/tools/utils/hook-runner.js +1 -3
  65. package/dist/agent/tools/utils/hook-runner.js.map +1 -1
  66. package/dist/agent/types.d.ts +139 -1
  67. package/dist/agent/types.d.ts.map +1 -1
  68. package/dist/agent/types.js.map +1 -1
  69. package/dist/ai/api-registry.js +1 -3
  70. package/dist/ai/api-registry.js.map +1 -1
  71. package/dist/ai/models.generated.d.ts +621 -53
  72. package/dist/ai/models.generated.d.ts.map +1 -1
  73. package/dist/ai/models.generated.js +548 -0
  74. package/dist/ai/models.generated.js.map +1 -1
  75. package/dist/ai/models.js +1 -0
  76. package/dist/ai/models.js.map +1 -1
  77. package/dist/ai/providers/amazon-bedrock.d.ts +6 -0
  78. package/dist/ai/providers/amazon-bedrock.d.ts.map +1 -1
  79. package/dist/ai/providers/amazon-bedrock.js +47 -9
  80. package/dist/ai/providers/amazon-bedrock.js.map +1 -1
  81. package/dist/ai/providers/anthropic.d.ts +8 -0
  82. package/dist/ai/providers/anthropic.d.ts.map +1 -1
  83. package/dist/ai/providers/anthropic.js +28 -12
  84. package/dist/ai/providers/anthropic.js.map +1 -1
  85. package/dist/ai/providers/azure-openai-responses.js +1 -0
  86. package/dist/ai/providers/azure-openai-responses.js.map +1 -1
  87. package/dist/ai/providers/github-copilot-headers.d.ts +33 -0
  88. package/dist/ai/providers/github-copilot-headers.d.ts.map +1 -0
  89. package/dist/ai/providers/github-copilot-headers.js +51 -0
  90. package/dist/ai/providers/github-copilot-headers.js.map +1 -0
  91. package/dist/ai/providers/google-shared.d.ts.map +1 -1
  92. package/dist/ai/providers/google-shared.js +16 -22
  93. package/dist/ai/providers/google-shared.js.map +1 -1
  94. package/dist/ai/providers/google-vertex.d.ts.map +1 -1
  95. package/dist/ai/providers/google-vertex.js +28 -5
  96. package/dist/ai/providers/google-vertex.js.map +1 -1
  97. package/dist/ai/providers/google.js +1 -0
  98. package/dist/ai/providers/google.js.map +1 -1
  99. package/dist/ai/providers/kimi.js +1 -0
  100. package/dist/ai/providers/kimi.js.map +1 -1
  101. package/dist/ai/providers/mistral.d.ts +22 -0
  102. package/dist/ai/providers/mistral.d.ts.map +1 -0
  103. package/dist/ai/providers/mistral.js +497 -0
  104. package/dist/ai/providers/mistral.js.map +1 -0
  105. package/dist/ai/providers/openai-codex-responses.js +1 -0
  106. package/dist/ai/providers/openai-codex-responses.js.map +1 -1
  107. package/dist/ai/providers/openai-completions.js +2 -0
  108. package/dist/ai/providers/openai-completions.js.map +1 -1
  109. package/dist/ai/providers/openai-responses.d.ts.map +1 -1
  110. package/dist/ai/providers/openai-responses.js +30 -0
  111. package/dist/ai/providers/openai-responses.js.map +1 -1
  112. package/dist/ai/providers/register-builtins.d.ts.map +1 -1
  113. package/dist/ai/providers/register-builtins.js +6 -0
  114. package/dist/ai/providers/register-builtins.js.map +1 -1
  115. package/dist/ai/providers/simple-options.js +2 -0
  116. package/dist/ai/providers/simple-options.js.map +1 -1
  117. package/dist/ai/providers/transform-messages.js +3 -9
  118. package/dist/ai/providers/transform-messages.js.map +1 -1
  119. package/dist/ai/stream.js +1 -3
  120. package/dist/ai/stream.js.map +1 -1
  121. package/dist/ai/types.d.ts +34 -0
  122. package/dist/ai/types.d.ts.map +1 -1
  123. package/dist/ai/types.js.map +1 -1
  124. package/dist/ai/utils/base-stream-handler.js +1 -0
  125. package/dist/ai/utils/base-stream-handler.js.map +1 -1
  126. package/dist/ai/utils/event-stream.js +9 -4
  127. package/dist/ai/utils/event-stream.js.map +1 -1
  128. package/dist/ai/utils/hash.d.ts +6 -0
  129. package/dist/ai/utils/hash.d.ts.map +1 -0
  130. package/dist/ai/utils/hash.js +17 -0
  131. package/dist/ai/utils/hash.js.map +1 -0
  132. package/dist/ai/utils/index.d.ts +1 -0
  133. package/dist/ai/utils/index.d.ts.map +1 -1
  134. package/dist/ai/utils/index.js +1 -0
  135. package/dist/ai/utils/index.js.map +1 -1
  136. package/dist/ai/utils/oauth/github-copilot.d.ts.map +1 -1
  137. package/dist/ai/utils/oauth/github-copilot.js +19 -8
  138. package/dist/ai/utils/oauth/github-copilot.js.map +1 -1
  139. package/dist/ai/utils/overflow.d.ts.map +1 -1
  140. package/dist/ai/utils/overflow.js +4 -0
  141. package/dist/ai/utils/overflow.js.map +1 -1
  142. package/dist/ai/utils/provider-adapter.js +9 -0
  143. package/dist/ai/utils/provider-adapter.js.map +1 -1
  144. package/dist/ai/utils/provider-client-builder.js +1 -1
  145. package/dist/ai/utils/provider-client-builder.js.map +1 -1
  146. package/dist/ai/utils/provider-errors.js +2 -0
  147. package/dist/ai/utils/provider-errors.js.map +1 -1
  148. package/dist/ai/utils/stream-event-helper.js +3 -0
  149. package/dist/ai/utils/stream-event-helper.js.map +1 -1
  150. package/dist/ai/utils/stream-handler-types.js +5 -0
  151. package/dist/ai/utils/stream-handler-types.js.map +1 -1
  152. package/dist/ai/utils/streaming-state-manager.js +4 -2
  153. package/dist/ai/utils/streaming-state-manager.js.map +1 -1
  154. package/dist/index.d.ts +1 -0
  155. package/dist/index.d.ts.map +1 -1
  156. package/dist/index.js +1 -0
  157. package/dist/index.js.map +1 -1
  158. package/dist/mcp/client-pool.d.ts +106 -0
  159. package/dist/mcp/client-pool.d.ts.map +1 -0
  160. package/dist/mcp/client-pool.js +234 -0
  161. package/dist/mcp/client-pool.js.map +1 -0
  162. package/dist/mcp/client.d.ts +158 -0
  163. package/dist/mcp/client.d.ts.map +1 -0
  164. package/dist/mcp/client.js +599 -0
  165. package/dist/mcp/client.js.map +1 -0
  166. package/dist/mcp/config.d.ts +61 -0
  167. package/dist/mcp/config.d.ts.map +1 -0
  168. package/dist/mcp/config.js +250 -0
  169. package/dist/mcp/config.js.map +1 -0
  170. package/dist/mcp/errors.d.ts +104 -0
  171. package/dist/mcp/errors.d.ts.map +1 -0
  172. package/dist/mcp/errors.js +146 -0
  173. package/dist/mcp/errors.js.map +1 -0
  174. package/dist/mcp/index.d.ts +56 -0
  175. package/dist/mcp/index.d.ts.map +1 -0
  176. package/dist/mcp/index.js +83 -0
  177. package/dist/mcp/index.js.map +1 -0
  178. package/dist/mcp/schema-converter.d.ts +68 -0
  179. package/dist/mcp/schema-converter.d.ts.map +1 -0
  180. package/dist/mcp/schema-converter.js +230 -0
  181. package/dist/mcp/schema-converter.js.map +1 -0
  182. package/dist/mcp/server.d.ts +111 -0
  183. package/dist/mcp/server.d.ts.map +1 -0
  184. package/dist/mcp/server.js +305 -0
  185. package/dist/mcp/server.js.map +1 -0
  186. package/dist/mcp/tool-factory.d.ts +63 -0
  187. package/dist/mcp/tool-factory.d.ts.map +1 -0
  188. package/dist/mcp/tool-factory.js +228 -0
  189. package/dist/mcp/tool-factory.js.map +1 -0
  190. package/dist/mcp/types.d.ts +289 -0
  191. package/dist/mcp/types.d.ts.map +1 -0
  192. package/dist/mcp/types.js +8 -0
  193. package/dist/mcp/types.js.map +1 -0
  194. package/dist/mcp.d.ts +6 -0
  195. package/dist/mcp.d.ts.map +1 -0
  196. package/dist/mcp.js +6 -0
  197. package/dist/mcp.js.map +1 -0
  198. package/dist/memory/embedder/base.d.ts +41 -0
  199. package/dist/memory/embedder/base.d.ts.map +1 -0
  200. package/dist/memory/embedder/base.js +10 -0
  201. package/dist/memory/embedder/base.js.map +1 -0
  202. package/dist/memory/embedder/index.d.ts +8 -0
  203. package/dist/memory/embedder/index.d.ts.map +1 -0
  204. package/dist/memory/embedder/index.js +6 -0
  205. package/dist/memory/embedder/index.js.map +1 -0
  206. package/dist/memory/embedder/openai.d.ts +35 -0
  207. package/dist/memory/embedder/openai.d.ts.map +1 -0
  208. package/dist/memory/embedder/openai.js +109 -0
  209. package/dist/memory/embedder/openai.js.map +1 -0
  210. package/dist/memory/index.d.ts +33 -0
  211. package/dist/memory/index.d.ts.map +1 -0
  212. package/dist/memory/index.js +31 -0
  213. package/dist/memory/index.js.map +1 -0
  214. package/dist/memory/memory.d.ts +126 -0
  215. package/dist/memory/memory.d.ts.map +1 -0
  216. package/dist/memory/memory.js +285 -0
  217. package/dist/memory/memory.js.map +1 -0
  218. package/dist/memory/processors/base.d.ts +42 -0
  219. package/dist/memory/processors/base.d.ts.map +1 -0
  220. package/dist/memory/processors/base.js +6 -0
  221. package/dist/memory/processors/base.js.map +1 -0
  222. package/dist/memory/processors/index.d.ts +16 -0
  223. package/dist/memory/processors/index.d.ts.map +1 -0
  224. package/dist/memory/processors/index.js +18 -0
  225. package/dist/memory/processors/index.js.map +1 -0
  226. package/dist/memory/processors/message-history.d.ts +35 -0
  227. package/dist/memory/processors/message-history.d.ts.map +1 -0
  228. package/dist/memory/processors/message-history.js +53 -0
  229. package/dist/memory/processors/message-history.js.map +1 -0
  230. package/dist/memory/processors/observational-memory/index.d.ts +82 -0
  231. package/dist/memory/processors/observational-memory/index.d.ts.map +1 -0
  232. package/dist/memory/processors/observational-memory/index.js +239 -0
  233. package/dist/memory/processors/observational-memory/index.js.map +1 -0
  234. package/dist/memory/processors/observational-memory/observer-agent.d.ts +64 -0
  235. package/dist/memory/processors/observational-memory/observer-agent.d.ts.map +1 -0
  236. package/dist/memory/processors/observational-memory/observer-agent.js +362 -0
  237. package/dist/memory/processors/observational-memory/observer-agent.js.map +1 -0
  238. package/dist/memory/processors/observational-memory/reflector-agent.d.ts +38 -0
  239. package/dist/memory/processors/observational-memory/reflector-agent.d.ts.map +1 -0
  240. package/dist/memory/processors/observational-memory/reflector-agent.js +213 -0
  241. package/dist/memory/processors/observational-memory/reflector-agent.js.map +1 -0
  242. package/dist/memory/processors/observational-memory/token-counter.d.ts +35 -0
  243. package/dist/memory/processors/observational-memory/token-counter.d.ts.map +1 -0
  244. package/dist/memory/processors/observational-memory/token-counter.js +90 -0
  245. package/dist/memory/processors/observational-memory/token-counter.js.map +1 -0
  246. package/dist/memory/processors/semantic-recall.d.ts +55 -0
  247. package/dist/memory/processors/semantic-recall.d.ts.map +1 -0
  248. package/dist/memory/processors/semantic-recall.js +152 -0
  249. package/dist/memory/processors/semantic-recall.js.map +1 -0
  250. package/dist/memory/processors/working-memory.d.ts +41 -0
  251. package/dist/memory/processors/working-memory.d.ts.map +1 -0
  252. package/dist/memory/processors/working-memory.js +87 -0
  253. package/dist/memory/processors/working-memory.js.map +1 -0
  254. package/dist/memory/storage/base.d.ts +288 -0
  255. package/dist/memory/storage/base.d.ts.map +1 -0
  256. package/dist/memory/storage/base.js +209 -0
  257. package/dist/memory/storage/base.js.map +1 -0
  258. package/dist/memory/storage/index.d.ts +9 -0
  259. package/dist/memory/storage/index.d.ts.map +1 -0
  260. package/dist/memory/storage/index.js +7 -0
  261. package/dist/memory/storage/index.js.map +1 -0
  262. package/dist/memory/storage/inmemory.d.ts +93 -0
  263. package/dist/memory/storage/inmemory.d.ts.map +1 -0
  264. package/dist/memory/storage/inmemory.js +647 -0
  265. package/dist/memory/storage/inmemory.js.map +1 -0
  266. package/dist/memory/tools/working-memory.d.ts +100 -0
  267. package/dist/memory/tools/working-memory.d.ts.map +1 -0
  268. package/dist/memory/tools/working-memory.js +237 -0
  269. package/dist/memory/tools/working-memory.js.map +1 -0
  270. package/dist/memory/types.d.ts +386 -0
  271. package/dist/memory/types.d.ts.map +1 -0
  272. package/dist/memory/types.js +58 -0
  273. package/dist/memory/types.js.map +1 -0
  274. package/dist/memory/vector/base.d.ts +145 -0
  275. package/dist/memory/vector/base.d.ts.map +1 -0
  276. package/dist/memory/vector/base.js +81 -0
  277. package/dist/memory/vector/base.js.map +1 -0
  278. package/dist/memory/vector/index.d.ts +8 -0
  279. package/dist/memory/vector/index.d.ts.map +1 -0
  280. package/dist/memory/vector/index.js +7 -0
  281. package/dist/memory/vector/index.js.map +1 -0
  282. package/dist/memory/vector/inmemory.d.ts +47 -0
  283. package/dist/memory/vector/inmemory.d.ts.map +1 -0
  284. package/dist/memory/vector/inmemory.js +231 -0
  285. package/dist/memory/vector/inmemory.js.map +1 -0
  286. package/dist/tui/autocomplete.js +5 -1
  287. package/dist/tui/autocomplete.js.map +1 -1
  288. package/dist/tui/components/box.js +9 -1
  289. package/dist/tui/components/box.js.map +1 -1
  290. package/dist/tui/components/cancellable-loader.js +3 -4
  291. package/dist/tui/components/cancellable-loader.js.map +1 -1
  292. package/dist/tui/components/editor.d.ts +39 -3
  293. package/dist/tui/components/editor.d.ts.map +1 -1
  294. package/dist/tui/components/editor.js +162 -133
  295. package/dist/tui/components/editor.js.map +1 -1
  296. package/dist/tui/components/image.js +8 -0
  297. package/dist/tui/components/image.js.map +1 -1
  298. package/dist/tui/components/input.js +10 -11
  299. package/dist/tui/components/input.js.map +1 -1
  300. package/dist/tui/components/loader.js +7 -4
  301. package/dist/tui/components/loader.js.map +1 -1
  302. package/dist/tui/components/markdown.js +10 -0
  303. package/dist/tui/components/markdown.js.map +1 -1
  304. package/dist/tui/components/select-list.js +8 -4
  305. package/dist/tui/components/select-list.js.map +1 -1
  306. package/dist/tui/components/settings-list.js +12 -4
  307. package/dist/tui/components/settings-list.js.map +1 -1
  308. package/dist/tui/components/spacer.js +1 -0
  309. package/dist/tui/components/spacer.js.map +1 -1
  310. package/dist/tui/components/text.js +5 -0
  311. package/dist/tui/components/text.js.map +1 -1
  312. package/dist/tui/components/truncated-text.js +3 -0
  313. package/dist/tui/components/truncated-text.js.map +1 -1
  314. package/dist/tui/fuzzy.js +4 -1
  315. package/dist/tui/fuzzy.js.map +1 -1
  316. package/dist/tui/keybindings.js +3 -2
  317. package/dist/tui/keybindings.js.map +1 -1
  318. package/dist/tui/keys.d.ts +17 -1
  319. package/dist/tui/keys.d.ts.map +1 -1
  320. package/dist/tui/keys.js +56 -0
  321. package/dist/tui/keys.js.map +1 -1
  322. package/dist/tui/kill-ring.d.ts +28 -0
  323. package/dist/tui/kill-ring.d.ts.map +1 -0
  324. package/dist/tui/kill-ring.js +44 -0
  325. package/dist/tui/kill-ring.js.map +1 -0
  326. package/dist/tui/stdin-buffer.js +6 -5
  327. package/dist/tui/stdin-buffer.js.map +1 -1
  328. package/dist/tui/terminal-image.js +1 -3
  329. package/dist/tui/terminal-image.js.map +1 -1
  330. package/dist/tui/terminal.d.ts +13 -0
  331. package/dist/tui/terminal.d.ts.map +1 -1
  332. package/dist/tui/terminal.js +70 -7
  333. package/dist/tui/terminal.js.map +1 -1
  334. package/dist/tui/tui.d.ts +36 -0
  335. package/dist/tui/tui.d.ts.map +1 -1
  336. package/dist/tui/tui.js +101 -29
  337. package/dist/tui/tui.js.map +1 -1
  338. package/dist/tui/undo-stack.d.ts +17 -0
  339. package/dist/tui/undo-stack.d.ts.map +1 -0
  340. package/dist/tui/undo-stack.js +25 -0
  341. package/dist/tui/undo-stack.js.map +1 -0
  342. package/dist/tui/utils.js +12 -16
  343. package/dist/tui/utils.js.map +1 -1
  344. package/package.json +32 -2
@@ -1,6 +1,8 @@
1
1
  import { getEditorKeybindings } from "../keybindings.js";
2
- import { matchesKey } from "../keys.js";
2
+ import { decodeKittyPrintable, matchesKey } from "../keys.js";
3
+ import { KillRing } from "../kill-ring.js";
3
4
  import { ComponentBase, CURSOR_MARKER } from "../tui.js";
5
+ import { UndoStack } from "../undo-stack.js";
4
6
  import { getSegmenter, isPunctuationChar, isWhitespaceChar, visibleWidth } from "../utils.js";
5
7
  import { SelectList } from "./select-list.js";
6
8
  const segmenter = getSegmenter();
@@ -66,81 +68,60 @@ export function wordWrapLine(line, maxWidth) {
66
68
  chunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });
67
69
  return chunks;
68
70
  }
69
- // Kitty CSI-u sequences for printable keys, including optional shifted/base codepoints.
70
- const KITTY_CSI_U_REGEX = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/;
71
- const KITTY_MOD_SHIFT = 1;
72
- const KITTY_MOD_ALT = 2;
73
- const KITTY_MOD_CTRL = 4;
74
- // Decode a printable CSI-u sequence, preferring the shifted key when present.
75
- function decodeKittyPrintable(data) {
76
- const match = data.match(KITTY_CSI_U_REGEX);
77
- if (!match)
78
- return undefined;
79
- // CSI-u groups: <codepoint>[:<shifted>[:<base>]];<mod>u
80
- const codepoint = Number.parseInt(match[1] ?? "", 10);
81
- if (!Number.isFinite(codepoint))
82
- return undefined;
83
- const shiftedKey = match[2] && match[2].length > 0 ? Number.parseInt(match[2], 10) : undefined;
84
- const modValue = match[4] ? Number.parseInt(match[4], 10) : 1;
85
- // Modifiers are 1-indexed in CSI-u; normalize to our bitmask.
86
- const modifier = Number.isFinite(modValue) ? modValue - 1 : 0;
87
- // Ignore CSI-u sequences used for Alt/Ctrl shortcuts.
88
- if (modifier & (KITTY_MOD_ALT | KITTY_MOD_CTRL))
89
- return undefined;
90
- // Prefer the shifted keycode when Shift is held.
91
- let effectiveCodepoint = codepoint;
92
- if (modifier & KITTY_MOD_SHIFT && typeof shiftedKey === "number") {
93
- effectiveCodepoint = shiftedKey;
94
- }
95
- // Drop control characters or invalid codepoints.
96
- if (!Number.isFinite(effectiveCodepoint) || effectiveCodepoint < 32)
97
- return undefined;
98
- try {
99
- return String.fromCodePoint(effectiveCodepoint);
100
- }
101
- catch {
102
- return undefined;
103
- }
104
- }
105
71
  export class Editor extends ComponentBase {
72
+ state = {
73
+ lines: [""],
74
+ cursorLine: 0,
75
+ cursorCol: 0,
76
+ };
77
+ /** Focusable interface - set by TUI when focus changes */
78
+ focused = false;
79
+ tui;
80
+ theme;
81
+ paddingX = 0;
82
+ // Store last render width for cursor navigation
83
+ lastWidth = 80;
84
+ // Vertical scrolling support
85
+ scrollOffset = 0;
86
+ // Border color (can be changed dynamically)
87
+ borderColor;
88
+ // Autocomplete support
89
+ autocompleteProvider;
90
+ autocompleteList;
91
+ isAutocompleting = false;
92
+ autocompletePrefix = "";
93
+ autocompleteMaxVisible = 5;
94
+ // Paste tracking for large pastes
95
+ pastes = new Map();
96
+ pasteCounter = 0;
97
+ // Bracketed paste mode buffering
98
+ pasteBuffer = "";
99
+ isInPaste = false;
100
+ pendingShiftEnter = false;
101
+ // Prompt history for up/down navigation
102
+ history = [];
103
+ historyIndex = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
104
+ // Kill ring for Emacs-style kill/yank operations
105
+ // Also tracks undo coalescing: "type-word" means we're mid-word (coalescing)
106
+ killRing = new KillRing();
107
+ lastAction = null;
108
+ // Undo support
109
+ undoStack = new UndoStack();
110
+ // Sticky column navigation for wrapped lines
111
+ // Maintains cursor column when moving up/down through lines of different lengths
112
+ preferredVisualCol = null;
113
+ onSubmit;
114
+ onChange;
115
+ disableSubmit = false;
106
116
  constructor(tui, theme, options = {}) {
107
117
  super();
108
- this.state = {
109
- lines: [""],
110
- cursorLine: 0,
111
- cursorCol: 0,
112
- };
113
- /** Focusable interface - set by TUI when focus changes */
114
- this.focused = false;
115
- this.paddingX = 0;
116
- // Store last render width for cursor navigation
117
- this.lastWidth = 80;
118
- // Vertical scrolling support
119
- this.scrollOffset = 0;
120
- this.isAutocompleting = false;
121
- this.autocompletePrefix = "";
122
- // Paste tracking for large pastes
123
- this.pastes = new Map();
124
- this.pasteCounter = 0;
125
- // Bracketed paste mode buffering
126
- this.pasteBuffer = "";
127
- this.isInPaste = false;
128
- this.pendingShiftEnter = false;
129
- // Prompt history for up/down navigation
130
- this.history = [];
131
- this.historyIndex = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
132
- // Kill ring for Emacs-style kill/yank operations
133
- // Also tracks undo coalescing: "type-word" means we're mid-word (coalescing)
134
- this.killRing = [];
135
- this.lastAction = null;
136
- // Undo support
137
- this.undoStack = [];
138
- this.disableSubmit = false;
139
118
  this.tui = tui;
140
119
  this.theme = theme;
141
120
  this.borderColor = theme.borderColor;
142
121
  const paddingX = options.paddingX ?? 0;
143
122
  this.paddingX = Number.isFinite(paddingX) ? Math.max(0, Math.floor(paddingX)) : 0;
123
+ const maxVisible = options.autocompleteMaxVisible ?? 5;
124
+ this.autocompleteMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
144
125
  }
145
126
  getPaddingX() {
146
127
  return this.paddingX;
@@ -152,6 +133,16 @@ export class Editor extends ComponentBase {
152
133
  this.tui.requestRender();
153
134
  }
154
135
  }
136
+ getAutocompleteMaxVisible() {
137
+ return this.autocompleteMaxVisible;
138
+ }
139
+ setAutocompleteMaxVisible(maxVisible) {
140
+ const newMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
141
+ if (this.autocompleteMaxVisible !== newMaxVisible) {
142
+ this.autocompleteMaxVisible = newMaxVisible;
143
+ this.tui.requestRender();
144
+ }
145
+ }
155
146
  setAutocompleteProvider(provider) {
156
147
  this.autocompleteProvider = provider;
157
148
  this.invalidate();
@@ -502,7 +493,7 @@ export class Editor extends ComponentBase {
502
493
  this.pasteCounter = 0;
503
494
  this.historyIndex = -1;
504
495
  this.scrollOffset = 0;
505
- this.undoStack.length = 0;
496
+ this.undoStack.clear();
506
497
  this.lastAction = null;
507
498
  if (this.onChange)
508
499
  this.onChange("");
@@ -899,14 +890,93 @@ export class Editor extends ComponentBase {
899
890
  }
900
891
  }
901
892
  }
893
+ /**
894
+ * Set cursor column and clear sticky column preference.
895
+ * Call this for any direct column positioning or horizontal movement.
896
+ */
897
+ setCursorCol(col) {
898
+ this.state.cursorCol = col;
899
+ this.preferredVisualCol = null;
900
+ }
901
+ /**
902
+ * Move cursor to a target visual line, applying sticky column logic.
903
+ * Preserves the cursor column when moving between wrapped lines of different widths.
904
+ */
905
+ moveToVisualLine(visualLines, currentVisualLine, targetVisualLine) {
906
+ const currentVL = visualLines[currentVisualLine];
907
+ const targetVL = visualLines[targetVisualLine];
908
+ if (currentVL && targetVL) {
909
+ const currentVisualCol = this.state.cursorCol - currentVL.startCol;
910
+ // For non-last segments, clamp to length-1 to stay within the segment
911
+ const isLastSourceSegment = currentVisualLine === visualLines.length - 1 ||
912
+ visualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;
913
+ const sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);
914
+ const isLastTargetSegment = targetVisualLine === visualLines.length - 1 ||
915
+ visualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;
916
+ const targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);
917
+ const moveToVisualCol = this.computeVerticalMoveColumn(currentVisualCol, sourceMaxVisualCol, targetMaxVisualCol);
918
+ // Set cursor position
919
+ this.state.cursorLine = targetVL.logicalLine;
920
+ const targetCol = targetVL.startCol + moveToVisualCol;
921
+ const logicalLine = this.state.lines[targetVL.logicalLine] || "";
922
+ this.state.cursorCol = Math.min(targetCol, logicalLine.length);
923
+ }
924
+ }
925
+ /**
926
+ * Compute the target visual column for vertical cursor movement.
927
+ * Implements sticky column logic: maintains the cursor column when moving between
928
+ * wrapped lines of different lengths.
929
+ *
930
+ * Decision table:
931
+ * | P | S | T | U | Scenario | Set Preferred | Move To |
932
+ * |---|---|---|---| ---------------------------------------------------- |---------------|-------------|
933
+ * | 0 | * | 0 | - | Start nav, target fits | null | current |
934
+ * | 0 | * | 1 | - | Start nav, target shorter | current | target end |
935
+ * | 1 | 0 | 0 | 0 | Clamped, target fits preferred | null | preferred |
936
+ * | 1 | 0 | 0 | 1 | Clamped, target longer but still can't fit preferred | keep | target end |
937
+ * | 1 | 0 | 1 | - | Clamped, target even shorter | keep | target end |
938
+ * | 1 | 1 | 0 | - | Rewrapped, target fits current | null | current |
939
+ * | 1 | 1 | 1 | - | Rewrapped, target shorter than current | current | target end |
940
+ *
941
+ * Where:
942
+ * - P = preferred col is set
943
+ * - S = cursor in middle of source line (not clamped to end)
944
+ * - T = target line shorter than current visual col
945
+ * - U = target line shorter than preferred col
946
+ */
947
+ computeVerticalMoveColumn(currentVisualCol, sourceMaxVisualCol, targetMaxVisualCol) {
948
+ const hasPreferred = this.preferredVisualCol !== null; // P
949
+ const cursorInMiddle = currentVisualCol < sourceMaxVisualCol; // S
950
+ const targetTooShort = targetMaxVisualCol < currentVisualCol; // T
951
+ if (!hasPreferred || cursorInMiddle) {
952
+ if (targetTooShort) {
953
+ // Cases 2 and 7: Start of navigation with target shorter than current
954
+ // Save current column as preferred for later restoration
955
+ this.preferredVisualCol = currentVisualCol;
956
+ return targetMaxVisualCol;
957
+ }
958
+ // Cases 1 and 6: Target fits current column
959
+ this.preferredVisualCol = null;
960
+ return currentVisualCol;
961
+ }
962
+ const targetCantFitPreferred = targetMaxVisualCol < this.preferredVisualCol; // U
963
+ if (targetTooShort || targetCantFitPreferred) {
964
+ // Cases 4 and 5: Target can't fit preferred, clamp to target end
965
+ return targetMaxVisualCol;
966
+ }
967
+ // Case 3: Target can fit preferred, restore it and clear preference
968
+ const result = this.preferredVisualCol;
969
+ this.preferredVisualCol = null;
970
+ return result;
971
+ }
902
972
  moveToLineStart() {
903
973
  this.lastAction = null;
904
- this.state.cursorCol = 0;
974
+ this.setCursorCol(0);
905
975
  }
906
976
  moveToLineEnd() {
907
977
  this.lastAction = null;
908
978
  const currentLine = this.state.lines[this.state.cursorLine] || "";
909
- this.state.cursorCol = currentLine.length;
979
+ this.setCursorCol(currentLine.length);
910
980
  }
911
981
  deleteToStartOfLine() {
912
982
  this.historyIndex = -1; // Exit history browsing mode
@@ -1138,20 +1208,10 @@ export class Editor extends ComponentBase {
1138
1208
  // Build visual line map for navigation
1139
1209
  const visualLines = this.buildVisualLineMap(width);
1140
1210
  const currentVisualLine = this.findCurrentVisualLine(visualLines);
1141
- // Calculate column position within current visual line
1142
- const currentVL = visualLines[currentVisualLine];
1143
- const visualCol = currentVL ? this.state.cursorCol - currentVL.startCol : 0;
1144
- // Move to target visual line
1211
+ // Move to target visual line with sticky column logic
1145
1212
  const targetVisualLine = currentVisualLine + deltaLine;
1146
1213
  if (targetVisualLine >= 0 && targetVisualLine < visualLines.length) {
1147
- const targetVL = visualLines[targetVisualLine];
1148
- if (targetVL) {
1149
- this.state.cursorLine = targetVL.logicalLine;
1150
- // Try to maintain visual column position, clamped to line length
1151
- const targetCol = targetVL.startCol + Math.min(visualCol, targetVL.length);
1152
- const logicalLine = this.state.lines[targetVL.logicalLine] || "";
1153
- this.state.cursorCol = Math.min(targetCol, logicalLine.length);
1154
- }
1214
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1155
1215
  }
1156
1216
  }
1157
1217
  if (deltaCol !== 0) {
@@ -1162,12 +1222,12 @@ export class Editor extends ComponentBase {
1162
1222
  const afterCursor = currentLine.slice(this.state.cursorCol);
1163
1223
  const graphemes = [...segmenter.segment(afterCursor)];
1164
1224
  const firstGrapheme = graphemes[0];
1165
- this.state.cursorCol += firstGrapheme ? firstGrapheme.segment.length : 1;
1225
+ this.setCursorCol(this.state.cursorCol + (firstGrapheme ? firstGrapheme.segment.length : 1));
1166
1226
  }
1167
1227
  else if (this.state.cursorLine < this.state.lines.length - 1) {
1168
1228
  // Wrap to start of next logical line
1169
1229
  this.state.cursorLine++;
1170
- this.state.cursorCol = 0;
1230
+ this.setCursorCol(0);
1171
1231
  }
1172
1232
  }
1173
1233
  else {
@@ -1176,13 +1236,13 @@ export class Editor extends ComponentBase {
1176
1236
  const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1177
1237
  const graphemes = [...segmenter.segment(beforeCursor)];
1178
1238
  const lastGrapheme = graphemes[graphemes.length - 1];
1179
- this.state.cursorCol -= lastGrapheme ? lastGrapheme.segment.length : 1;
1239
+ this.setCursorCol(this.state.cursorCol - (lastGrapheme ? lastGrapheme.segment.length : 1));
1180
1240
  }
1181
1241
  else if (this.state.cursorLine > 0) {
1182
1242
  // Wrap to end of previous logical line
1183
1243
  this.state.cursorLine--;
1184
1244
  const prevLine = this.state.lines[this.state.cursorLine] || "";
1185
- this.state.cursorCol = prevLine.length;
1245
+ this.setCursorCol(prevLine.length);
1186
1246
  }
1187
1247
  }
1188
1248
  }
@@ -1201,17 +1261,8 @@ export class Editor extends ComponentBase {
1201
1261
  const currentVisualLine = this.findCurrentVisualLine(visualLines);
1202
1262
  // Calculate target visual line
1203
1263
  const targetVisualLine = Math.max(0, Math.min(visualLines.length - 1, currentVisualLine + direction * pageSize));
1204
- // Move cursor to target visual line
1205
- const targetVL = visualLines[targetVisualLine];
1206
- if (targetVL) {
1207
- // Preserve column position within the line
1208
- const currentVL = visualLines[currentVisualLine];
1209
- const visualCol = currentVL ? this.state.cursorCol - currentVL.startCol : 0;
1210
- this.state.cursorLine = targetVL.logicalLine;
1211
- const targetCol = targetVL.startCol + Math.min(visualCol, targetVL.length);
1212
- const logicalLine = this.state.lines[targetVL.logicalLine] || "";
1213
- this.state.cursorCol = Math.min(targetCol, logicalLine.length);
1214
- }
1264
+ // Move cursor to target visual line with sticky column logic
1265
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1215
1266
  }
1216
1267
  moveWordBackwards() {
1217
1268
  this.lastAction = null;
@@ -1258,7 +1309,7 @@ export class Editor extends ComponentBase {
1258
1309
  if (this.killRing.length === 0)
1259
1310
  return;
1260
1311
  this.pushUndoSnapshot();
1261
- const text = this.killRing[this.killRing.length - 1] || "";
1312
+ const text = this.killRing.peek();
1262
1313
  this.insertYankedText(text);
1263
1314
  this.lastAction = "yank";
1264
1315
  }
@@ -1274,10 +1325,9 @@ export class Editor extends ComponentBase {
1274
1325
  // Delete the previously yanked text (still at end of ring before rotation)
1275
1326
  this.deleteYankedText();
1276
1327
  // Rotate the ring: move end to front
1277
- const lastEntry = this.killRing.pop();
1278
- this.killRing.unshift(lastEntry);
1328
+ this.killRing.rotate();
1279
1329
  // Insert the new most recent entry (now at end after rotation)
1280
- const text = this.killRing[this.killRing.length - 1];
1330
+ const text = this.killRing.peek();
1281
1331
  this.insertYankedText(text);
1282
1332
  this.lastAction = "yank";
1283
1333
  }
@@ -1319,10 +1369,10 @@ export class Editor extends ComponentBase {
1319
1369
  }
1320
1370
  /**
1321
1371
  * Delete the previously yanked text (used by yank-pop).
1322
- * The yanked text is derived from killRing[end] since it hasn't been rotated yet.
1372
+ * The yanked text is derived from killRing.peek() since it hasn't been rotated yet.
1323
1373
  */
1324
1374
  deleteYankedText() {
1325
- const yankedText = this.killRing[this.killRing.length - 1] || "";
1375
+ const yankedText = this.killRing.peek();
1326
1376
  if (!yankedText)
1327
1377
  return;
1328
1378
  const yankLines = yankedText.split("\n");
@@ -1360,38 +1410,17 @@ export class Editor extends ComponentBase {
1360
1410
  * @param prepend - If accumulating, prepend (true) or append (false) to existing entry
1361
1411
  */
1362
1412
  addToKillRing(text, prepend) {
1363
- if (!text)
1364
- return;
1365
- if (this.lastAction === "kill" && this.killRing.length > 0) {
1366
- // Accumulate with the most recent entry (at end of array)
1367
- const lastEntry = this.killRing.pop();
1368
- if (prepend) {
1369
- this.killRing.push(text + lastEntry);
1370
- }
1371
- else {
1372
- this.killRing.push(lastEntry + text);
1373
- }
1374
- }
1375
- else {
1376
- // Add new entry to end of ring
1377
- this.killRing.push(text);
1378
- }
1379
- }
1380
- captureUndoSnapshot() {
1381
- return structuredClone(this.state);
1382
- }
1383
- restoreUndoSnapshot(snapshot) {
1384
- Object.assign(this.state, structuredClone(snapshot));
1413
+ this.killRing.push(text, { prepend, accumulate: this.lastAction === "kill" });
1385
1414
  }
1386
1415
  pushUndoSnapshot() {
1387
- this.undoStack.push(this.captureUndoSnapshot());
1416
+ this.undoStack.push(this.state);
1388
1417
  }
1389
1418
  undo() {
1390
1419
  this.historyIndex = -1; // Exit history browsing mode
1391
- if (this.undoStack.length === 0)
1392
- return;
1393
1420
  const snapshot = this.undoStack.pop();
1394
- this.restoreUndoSnapshot(snapshot);
1421
+ if (!snapshot)
1422
+ return;
1423
+ Object.assign(this.state, snapshot);
1395
1424
  this.lastAction = null;
1396
1425
  if (this.onChange) {
1397
1426
  this.onChange(this.getText());
@@ -1472,7 +1501,7 @@ export class Editor extends ComponentBase {
1472
1501
  const suggestions = this.autocompleteProvider.getSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol);
1473
1502
  if (suggestions && suggestions.items.length > 0) {
1474
1503
  this.autocompletePrefix = suggestions.prefix;
1475
- this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
1504
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
1476
1505
  this.isAutocompleting = true;
1477
1506
  }
1478
1507
  else {
@@ -1512,7 +1541,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
1512
1541
  const suggestions = provider.getForceFileSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol);
1513
1542
  if (suggestions && suggestions.items.length > 0) {
1514
1543
  this.autocompletePrefix = suggestions.prefix;
1515
- this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
1544
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
1516
1545
  this.isAutocompleting = true;
1517
1546
  }
1518
1547
  else {
@@ -1534,7 +1563,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
1534
1563
  if (suggestions && suggestions.items.length > 0) {
1535
1564
  this.autocompletePrefix = suggestions.prefix;
1536
1565
  // Always create new SelectList to ensure update
1537
- this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
1566
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
1538
1567
  }
1539
1568
  else {
1540
1569
  this.cancelAutocomplete();