@code-yeongyu/senpi 2026.6.10 → 2026.6.13

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 (285) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/README.md +21 -21
  3. package/dist/cli/startup-ui.d.ts +10 -0
  4. package/dist/cli/startup-ui.d.ts.map +1 -1
  5. package/dist/cli/startup-ui.js +72 -1
  6. package/dist/cli/startup-ui.js.map +1 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +3 -2
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/bash-executor.d.ts.map +1 -1
  11. package/dist/core/bash-executor.js +31 -7
  12. package/dist/core/bash-executor.js.map +1 -1
  13. package/dist/core/extensions/builtin/compaction/degradation-monitor.d.ts +0 -1
  14. package/dist/core/extensions/builtin/compaction/degradation-monitor.d.ts.map +1 -1
  15. package/dist/core/extensions/builtin/compaction/degradation-monitor.js +0 -1
  16. package/dist/core/extensions/builtin/compaction/degradation-monitor.js.map +1 -1
  17. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  18. package/dist/core/extensions/builtin/compaction/index.js +4 -4
  19. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  20. package/dist/core/extensions/builtin/compaction/policy.d.ts +0 -5
  21. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  22. package/dist/core/extensions/builtin/compaction/policy.js +0 -4
  23. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/speculative.d.ts +0 -6
  25. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/compaction/speculative.js +0 -3
  27. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  28. package/dist/core/extensions/builtin/gpt-apply-patch/extension.d.ts +1 -1
  29. package/dist/core/extensions/builtin/gpt-apply-patch/extension.d.ts.map +1 -1
  30. package/dist/core/extensions/builtin/gpt-apply-patch/extension.js +10 -2
  31. package/dist/core/extensions/builtin/gpt-apply-patch/extension.js.map +1 -1
  32. package/dist/core/extensions/builtin/permission-system/cli.d.ts +0 -3
  33. package/dist/core/extensions/builtin/permission-system/cli.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/permission-system/cli.js +0 -13
  35. package/dist/core/extensions/builtin/permission-system/cli.js.map +1 -1
  36. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts +3 -0
  37. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts.map +1 -0
  38. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js +10 -0
  39. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js.map +1 -0
  40. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/prompt-preset/presets.js +12 -0
  42. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  43. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  44. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  46. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  47. package/dist/core/extensions/types.d.ts +1 -0
  48. package/dist/core/extensions/types.d.ts.map +1 -1
  49. package/dist/core/extensions/types.js.map +1 -1
  50. package/dist/core/model-registry.d.ts +0 -3
  51. package/dist/core/model-registry.d.ts.map +1 -1
  52. package/dist/core/model-registry.js +3 -3
  53. package/dist/core/model-registry.js.map +1 -1
  54. package/dist/core/model-resolver.d.ts +1 -0
  55. package/dist/core/model-resolver.d.ts.map +1 -1
  56. package/dist/core/model-resolver.js +43 -6
  57. package/dist/core/model-resolver.js.map +1 -1
  58. package/dist/core/project-trust.js +2 -2
  59. package/dist/core/project-trust.js.map +1 -1
  60. package/dist/core/resolve-config-value.d.ts +0 -4
  61. package/dist/core/resolve-config-value.d.ts.map +1 -1
  62. package/dist/core/resolve-config-value.js +0 -15
  63. package/dist/core/resolve-config-value.js.map +1 -1
  64. package/dist/core/session-manager.d.ts.map +1 -1
  65. package/dist/core/session-manager.js +11 -2
  66. package/dist/core/session-manager.js.map +1 -1
  67. package/dist/core/settings-manager.d.ts +6 -0
  68. package/dist/core/settings-manager.d.ts.map +1 -1
  69. package/dist/core/settings-manager.js +17 -0
  70. package/dist/core/settings-manager.js.map +1 -1
  71. package/dist/core/tools/edit-diff.d.ts +0 -5
  72. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  73. package/dist/core/tools/edit-diff.js +0 -7
  74. package/dist/core/tools/edit-diff.js.map +1 -1
  75. package/dist/core/tools/index.d.ts +0 -5
  76. package/dist/core/tools/index.d.ts.map +1 -1
  77. package/dist/core/tools/index.js +0 -67
  78. package/dist/core/tools/index.js.map +1 -1
  79. package/dist/core/tools/output-accumulator.d.ts +3 -5
  80. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  81. package/dist/core/tools/output-accumulator.js +38 -34
  82. package/dist/core/tools/output-accumulator.js.map +1 -1
  83. package/dist/core/tools/tail-window.d.ts +15 -0
  84. package/dist/core/tools/tail-window.d.ts.map +1 -0
  85. package/dist/core/tools/tail-window.js +75 -0
  86. package/dist/core/tools/tail-window.js.map +1 -0
  87. package/dist/core/trust-manager.d.ts +8 -3
  88. package/dist/core/trust-manager.d.ts.map +1 -1
  89. package/dist/core/trust-manager.js +26 -11
  90. package/dist/core/trust-manager.js.map +1 -1
  91. package/dist/index.d.ts +1 -1
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +1 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/main.d.ts.map +1 -1
  96. package/dist/main.js +17 -6
  97. package/dist/main.js.map +1 -1
  98. package/dist/modes/interactive/aborted-error-label.d.ts +2 -0
  99. package/dist/modes/interactive/aborted-error-label.d.ts.map +1 -0
  100. package/dist/modes/interactive/aborted-error-label.js +8 -0
  101. package/dist/modes/interactive/aborted-error-label.js.map +1 -0
  102. package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  103. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  104. package/dist/modes/interactive/components/first-time-setup.js +101 -0
  105. package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  106. package/dist/modes/interactive/components/index.d.ts +1 -0
  107. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  108. package/dist/modes/interactive/components/index.js +1 -0
  109. package/dist/modes/interactive/components/index.js.map +1 -1
  110. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  111. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  112. package/dist/modes/interactive/components/tool-execution.js +45 -0
  113. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  114. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/trust-selector.js +4 -4
  116. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  117. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  118. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  119. package/dist/modes/interactive/interactive-mode.js +18 -18
  120. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  121. package/dist/modes/interactive/theme/theme.d.ts +0 -4
  122. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  123. package/dist/modes/interactive/theme/theme.js +0 -7
  124. package/dist/modes/interactive/theme/theme.js.map +1 -1
  125. package/dist/modes/print-mode.d.ts.map +1 -1
  126. package/dist/modes/print-mode.js +1 -1
  127. package/dist/modes/print-mode.js.map +1 -1
  128. package/dist/modes/rpc/event-output-buffer.d.ts +10 -0
  129. package/dist/modes/rpc/event-output-buffer.d.ts.map +1 -0
  130. package/dist/modes/rpc/event-output-buffer.js +27 -0
  131. package/dist/modes/rpc/event-output-buffer.js.map +1 -0
  132. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  133. package/dist/modes/rpc/jsonl.js +9 -11
  134. package/dist/modes/rpc/jsonl.js.map +1 -1
  135. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  136. package/dist/modes/rpc/rpc-mode.js +17 -7
  137. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  138. package/dist/modes/rpc/rpc-types.d.ts +0 -1
  139. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-types.js.map +1 -1
  141. package/dist/package-manager-cli.d.ts.map +1 -1
  142. package/dist/package-manager-cli.js +10 -3
  143. package/dist/package-manager-cli.js.map +1 -1
  144. package/dist/utils/shell.d.ts.map +1 -1
  145. package/dist/utils/shell.js +14 -0
  146. package/dist/utils/shell.js.map +1 -1
  147. package/docs/compaction-guide.md +9 -9
  148. package/docs/compaction.md +2 -2
  149. package/docs/containerization.md +22 -22
  150. package/docs/custom-provider.md +13 -13
  151. package/docs/development.md +4 -2
  152. package/docs/extensions.md +18 -19
  153. package/docs/index.md +4 -4
  154. package/docs/json.md +21 -15
  155. package/docs/keybindings.md +6 -3
  156. package/docs/models.md +6 -6
  157. package/docs/packages.md +11 -11
  158. package/docs/prompt-templates.md +2 -2
  159. package/docs/providers.md +9 -9
  160. package/docs/rpc.md +14 -13
  161. package/docs/sdk.md +13 -9
  162. package/docs/security.md +14 -10
  163. package/docs/session-format.md +4 -4
  164. package/docs/sessions.md +14 -14
  165. package/docs/settings.md +14 -12
  166. package/docs/skills.md +8 -8
  167. package/docs/terminal-setup.md +2 -2
  168. package/docs/termux.md +3 -3
  169. package/docs/tui.md +4 -4
  170. package/docs/usage.md +52 -59
  171. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  172. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  173. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  174. package/examples/extensions/gondolin/package-lock.json +2 -2
  175. package/examples/extensions/gondolin/package.json +1 -1
  176. package/examples/extensions/sandbox/package-lock.json +2 -2
  177. package/examples/extensions/sandbox/package.json +1 -1
  178. package/examples/extensions/with-deps/package-lock.json +2 -2
  179. package/examples/extensions/with-deps/package.json +1 -1
  180. package/node_modules/@earendil-works/pi-agent-core/README.md +4 -3
  181. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +8 -0
  182. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +0 -2
  184. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js +0 -4
  186. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +7 -2
  188. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  189. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  190. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  191. package/node_modules/@earendil-works/pi-ai/README.md +2 -4
  192. package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts +0 -1
  193. package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts.map +1 -1
  194. package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +0 -3
  195. package/node_modules/@earendil-works/pi-ai/dist/api-registry.js.map +1 -1
  196. package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +252 -470
  198. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  199. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +533 -740
  200. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  201. package/node_modules/@earendil-works/pi-ai/dist/models.js +12 -5
  202. package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
  203. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  204. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  205. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  206. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  207. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +29 -16
  208. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  209. package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.d.ts +0 -4
  210. package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  211. package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.js +0 -13
  212. package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.js.map +1 -1
  213. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  214. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +3 -1
  215. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  216. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts +0 -6
  217. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  218. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +0 -14
  219. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
  220. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +7 -0
  221. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  222. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  223. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts +4 -1
  224. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts.map +1 -1
  225. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js +59 -19
  226. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js.map +1 -1
  227. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -5
  228. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  229. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -8
  230. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  231. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  232. package/node_modules/@earendil-works/pi-tui/README.md +1 -2
  233. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +2 -0
  234. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  235. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +93 -9
  236. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  237. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +3 -1
  238. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
  239. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +89 -2
  240. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
  241. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +1 -1
  242. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +2 -2
  243. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -1
  244. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +2 -2
  245. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  246. package/node_modules/@earendil-works/pi-tui/dist/index.js +2 -2
  247. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  248. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +1 -0
  249. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  250. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +15 -8
  251. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -1
  252. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -1
  253. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
  254. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +4 -7
  255. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
  256. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  257. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +0 -1
  258. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  259. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +2 -1
  260. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
  261. package/node_modules/@earendil-works/pi-tui/dist/tui.js +72 -16
  262. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +1 -4
  264. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-tui/dist/utils.js +1 -7
  266. package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  268. package/npm-shrinkwrap.json +12 -12
  269. package/package.json +5 -5
  270. package/dist/core/experimental.d.ts +0 -2
  271. package/dist/core/experimental.d.ts.map +0 -1
  272. package/dist/core/experimental.js +0 -4
  273. package/dist/core/experimental.js.map +0 -1
  274. package/dist/core/extensions/builtin/compaction/overflow-detection.d.ts +0 -11
  275. package/dist/core/extensions/builtin/compaction/overflow-detection.d.ts.map +0 -1
  276. package/dist/core/extensions/builtin/compaction/overflow-detection.js +0 -40
  277. package/dist/core/extensions/builtin/compaction/overflow-detection.js.map +0 -1
  278. package/dist/core/extensions/builtin/system-messages.d.ts +0 -47
  279. package/dist/core/extensions/builtin/system-messages.d.ts.map +0 -1
  280. package/dist/core/extensions/builtin/system-messages.js +0 -117
  281. package/dist/core/extensions/builtin/system-messages.js.map +0 -1
  282. package/dist/core/index.d.ts +0 -13
  283. package/dist/core/index.d.ts.map +0 -1
  284. package/dist/core/index.js +0 -13
  285. package/dist/core/index.js.map +0 -1
@@ -3,7 +3,7 @@ import { decodePrintableKey, matchesKey } from "../keys.js";
3
3
  import { KillRing } from "../kill-ring.js";
4
4
  import { CURSOR_MARKER } from "../tui.js";
5
5
  import { UndoStack } from "../undo-stack.js";
6
- import { getGraphemeSegmenter, getWordSegmenter, isWhitespaceChar, truncateToWidth, visibleWidth } from "../utils.js";
6
+ import { cjkBreakRegex, getGraphemeSegmenter, getWordSegmenter, isWhitespaceChar, truncateToWidth, visibleWidth, } from "../utils.js";
7
7
  import { findWordBackward, findWordForward } from "../word-navigation.js";
8
8
  import { SelectList } from "./select-list.js";
9
9
  const graphemeSegmenter = getGraphemeSegmenter();
@@ -68,6 +68,49 @@ function segmentWithMarkers(text, baseSegmenter, validIds) {
68
68
  }
69
69
  return result;
70
70
  }
71
+ function isPrintableAsciiText(text) {
72
+ for (let i = 0; i < text.length; i++) {
73
+ const code = text.charCodeAt(i);
74
+ if (code < 0x20 || code > 0x7e)
75
+ return false;
76
+ }
77
+ return true;
78
+ }
79
+ function wordWrapAsciiLine(line, maxWidth) {
80
+ if (line.length <= maxWidth) {
81
+ return [{ text: line, startIndex: 0, endIndex: line.length }];
82
+ }
83
+ const chunks = [];
84
+ let chunkStart = 0;
85
+ while (chunkStart < line.length) {
86
+ let chunkEnd = Math.min(line.length, chunkStart + maxWidth);
87
+ if (chunkEnd < line.length) {
88
+ let breakAt = -1;
89
+ for (let i = chunkEnd; i > chunkStart; i--) {
90
+ const code = line.charCodeAt(i - 1);
91
+ if (code === 0x20 || code === 0x09) {
92
+ breakAt = i - 1;
93
+ break;
94
+ }
95
+ }
96
+ if (breakAt > chunkStart)
97
+ chunkEnd = breakAt;
98
+ }
99
+ const raw = line.slice(chunkStart, chunkEnd);
100
+ const text = raw.trimEnd();
101
+ if (text || chunks.length === 0) {
102
+ chunks.push({ text, startIndex: chunkStart, endIndex: chunkStart + raw.length });
103
+ }
104
+ chunkStart = chunkEnd;
105
+ while (chunkStart < line.length) {
106
+ const code = line.charCodeAt(chunkStart);
107
+ if (code !== 0x20 && code !== 0x09)
108
+ break;
109
+ chunkStart++;
110
+ }
111
+ }
112
+ return chunks.length > 0 ? chunks : [{ text: "", startIndex: 0, endIndex: 0 }];
113
+ }
71
114
  /**
72
115
  * Split a line into word-wrapped chunks.
73
116
  * Wraps at word boundaries when possible, falling back to character-level
@@ -83,6 +126,9 @@ export function wordWrapLine(line, maxWidth, preSegmented) {
83
126
  if (!line || maxWidth <= 0) {
84
127
  return [{ text: "", startIndex: 0, endIndex: 0 }];
85
128
  }
129
+ if (preSegmented === undefined && !/\s/.test(line) && !line.includes("[paste #") && isPrintableAsciiText(line)) {
130
+ return wordWrapAsciiLine(line, maxWidth);
131
+ }
86
132
  const lineWidth = visibleWidth(line);
87
133
  if (lineWidth <= maxWidth) {
88
134
  return [{ text: line, startIndex: 0, endIndex: line.length }];
@@ -140,14 +186,23 @@ export function wordWrapLine(line, maxWidth, preSegmented) {
140
186
  }
141
187
  // Advance.
142
188
  currentWidth += gWidth;
143
- // Record wrap opportunity: whitespace followed by non-whitespace.
144
- // Multiple spaces join (no break between them); the break point is
145
- // after the last space before the next word.
189
+ // Record wrap opportunity: whitespace followed by non-whitespace
190
+ // (multiple spaces join; the break point is after the last space),
191
+ // or at a boundary where either side is CJK (CJK allows breaking
192
+ // between any adjacent characters).
146
193
  const next = segments[i + 1];
147
194
  if (isWs && next && (isPasteMarker(next.segment) || !isWhitespaceChar(next.segment))) {
148
195
  wrapOppIndex = next.index;
149
196
  wrapOppWidth = currentWidth;
150
197
  }
198
+ else if (!isWs && next && !isWhitespaceChar(next.segment)) {
199
+ const isCjk = !isPasteMarker(grapheme) && cjkBreakRegex.test(grapheme);
200
+ const nextIsCjk = !isPasteMarker(next.segment) && cjkBreakRegex.test(next.segment);
201
+ if (isCjk || nextIsCjk) {
202
+ wrapOppIndex = next.index;
203
+ wrapOppWidth = currentWidth;
204
+ }
205
+ }
151
206
  }
152
207
  // Push final chunk.
153
208
  chunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });
@@ -217,6 +272,7 @@ export class Editor {
217
272
  this.snappedFromCursorCol = null;
218
273
  // Undo support
219
274
  this.undoStack = new UndoStack();
275
+ this.wrappedLineCache = [];
220
276
  this.disableSubmit = false;
221
277
  this.tui = tui;
222
278
  this.theme = theme;
@@ -335,7 +391,33 @@ export class Editor {
335
391
  }
336
392
  }
337
393
  invalidate() {
338
- // No cached state to invalidate currently
394
+ this.wrappedLineCache.length = 0;
395
+ }
396
+ getWrappedLine(lineIndex, contentWidth) {
397
+ const line = this.state.lines[lineIndex] || "";
398
+ const cached = this.wrappedLineCache[lineIndex];
399
+ if (cached && cached.lineRef === line && cached.contentWidth === contentWidth) {
400
+ return cached;
401
+ }
402
+ const width = isPrintableAsciiText(line) ? line.length : visibleWidth(line);
403
+ if (line.includes("[paste #")) {
404
+ return {
405
+ width,
406
+ chunks: width <= contentWidth
407
+ ? [{ text: line, startIndex: 0, endIndex: line.length }]
408
+ : wordWrapLine(line, contentWidth, [...this.segment(line, "grapheme")]),
409
+ };
410
+ }
411
+ const wrapped = {
412
+ lineRef: line,
413
+ contentWidth,
414
+ width,
415
+ chunks: width <= contentWidth
416
+ ? [{ text: line, startIndex: 0, endIndex: line.length }]
417
+ : wordWrapLine(line, contentWidth),
418
+ };
419
+ this.wrappedLineCache[lineIndex] = wrapped;
420
+ return wrapped;
339
421
  }
340
422
  render(width) {
341
423
  const maxPadding = Math.max(0, Math.floor((width - 1) / 2));
@@ -706,6 +788,7 @@ export class Editor {
706
788
  const layoutLines = [];
707
789
  if (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === "")) {
708
790
  // Empty editor
791
+ this.wrappedLineCache.length = this.state.lines.length;
709
792
  layoutLines.push({
710
793
  text: "",
711
794
  hasCursor: true,
@@ -717,8 +800,8 @@ export class Editor {
717
800
  for (let i = 0; i < this.state.lines.length; i++) {
718
801
  const line = this.state.lines[i] || "";
719
802
  const isCurrentLine = i === this.state.cursorLine;
720
- const lineVisibleWidth = visibleWidth(line);
721
- if (lineVisibleWidth <= contentWidth) {
803
+ const wrappedLine = this.getWrappedLine(i, contentWidth);
804
+ if (wrappedLine.width <= contentWidth) {
722
805
  // Line fits in one layout line
723
806
  if (isCurrentLine) {
724
807
  layoutLines.push({
@@ -736,7 +819,7 @@ export class Editor {
736
819
  }
737
820
  else {
738
821
  // Line needs wrapping - use word-aware wrapping
739
- const chunks = wordWrapLine(line, contentWidth, [...this.segment(line, "grapheme")]);
822
+ const chunks = wrappedLine.chunks;
740
823
  for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
741
824
  const chunk = chunks[chunkIndex];
742
825
  if (!chunk)
@@ -783,6 +866,7 @@ export class Editor {
783
866
  }
784
867
  }
785
868
  }
869
+ this.wrappedLineCache.length = this.state.lines.length;
786
870
  return layoutLines;
787
871
  }
788
872
  getText() {
@@ -1408,7 +1492,7 @@ export class Editor {
1408
1492
  }
1409
1493
  else {
1410
1494
  // Line needs wrapping - use word-aware wrapping
1411
- const chunks = wordWrapLine(line, width, [...this.segment(line, "grapheme")]);
1495
+ const chunks = this.getWrappedLine(i, width).chunks;
1412
1496
  for (const chunk of chunks) {
1413
1497
  visualLines.push({
1414
1498
  logicalLine: i,