@deepseek-harness-tui/dsh-tui 0.5.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 (638) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +235 -0
  3. package/bin/dsh-tui.js +95 -0
  4. package/cordis.patch.yml +218 -0
  5. package/cordis.yml +185 -0
  6. package/lib/invariant.js +28 -0
  7. package/lib/types/activityPrefs.d.ts +29 -0
  8. package/lib/types/activityPrefs.d.ts.map +1 -0
  9. package/lib/types/activityPrefs.js +60 -0
  10. package/lib/types/bootstrap/state.d.ts +13 -0
  11. package/lib/types/bootstrap/state.d.ts.map +1 -0
  12. package/lib/types/bootstrap/state.js +12 -0
  13. package/lib/types/cc/cliHighlight.d.ts +20 -0
  14. package/lib/types/cc/cliHighlight.d.ts.map +1 -0
  15. package/lib/types/cc/cliHighlight.js +53 -0
  16. package/lib/types/cc/figures.d.ts +59 -0
  17. package/lib/types/cc/figures.d.ts.map +1 -0
  18. package/lib/types/cc/figures.js +69 -0
  19. package/lib/types/cc/format.d.ts +26 -0
  20. package/lib/types/cc/format.d.ts.map +1 -0
  21. package/lib/types/cc/format.js +55 -0
  22. package/lib/types/cc/hash.d.ts +25 -0
  23. package/lib/types/cc/hash.d.ts.map +1 -0
  24. package/lib/types/cc/hash.js +41 -0
  25. package/lib/types/cc/hyperlink.d.ts +21 -0
  26. package/lib/types/cc/hyperlink.d.ts.map +1 -0
  27. package/lib/types/cc/hyperlink.js +31 -0
  28. package/lib/types/cc/markdown.d.ts +43 -0
  29. package/lib/types/cc/markdown.d.ts.map +1 -0
  30. package/lib/types/cc/markdown.js +345 -0
  31. package/lib/types/cc/spinnerVerbs.d.ts +3 -0
  32. package/lib/types/cc/spinnerVerbs.d.ts.map +1 -0
  33. package/lib/types/cc/spinnerVerbs.js +190 -0
  34. package/lib/types/cc/terminal.d.ts +15 -0
  35. package/lib/types/cc/terminal.d.ts.map +1 -0
  36. package/lib/types/cc/terminal.js +84 -0
  37. package/lib/types/channel.d.ts +672 -0
  38. package/lib/types/channel.d.ts.map +1 -0
  39. package/lib/types/channel.js +2427 -0
  40. package/lib/types/childStderr.d.ts +63 -0
  41. package/lib/types/childStderr.d.ts.map +1 -0
  42. package/lib/types/childStderr.js +161 -0
  43. package/lib/types/commands.d.ts +55 -0
  44. package/lib/types/commands.d.ts.map +1 -0
  45. package/lib/types/commands.js +101 -0
  46. package/lib/types/components/ActivityLine.d.ts +29 -0
  47. package/lib/types/components/ActivityLine.d.ts.map +1 -0
  48. package/lib/types/components/ActivityLine.js +47 -0
  49. package/lib/types/components/ActivityPicker.d.ts +13 -0
  50. package/lib/types/components/ActivityPicker.d.ts.map +1 -0
  51. package/lib/types/components/ActivityPicker.js +24 -0
  52. package/lib/types/components/CommandSuggestions.d.ts +14 -0
  53. package/lib/types/components/CommandSuggestions.d.ts.map +1 -0
  54. package/lib/types/components/CommandSuggestions.js +33 -0
  55. package/lib/types/components/FileSuggestions.d.ts +12 -0
  56. package/lib/types/components/FileSuggestions.d.ts.map +1 -0
  57. package/lib/types/components/FileSuggestions.js +26 -0
  58. package/lib/types/components/GoalTodoPanel.d.ts +13 -0
  59. package/lib/types/components/GoalTodoPanel.d.ts.map +1 -0
  60. package/lib/types/components/GoalTodoPanel.js +57 -0
  61. package/lib/types/components/HelpMenu.d.ts +12 -0
  62. package/lib/types/components/HelpMenu.d.ts.map +1 -0
  63. package/lib/types/components/HelpMenu.js +12 -0
  64. package/lib/types/components/HistorySearchDialog.d.ts +16 -0
  65. package/lib/types/components/HistorySearchDialog.d.ts.map +1 -0
  66. package/lib/types/components/HistorySearchDialog.js +31 -0
  67. package/lib/types/components/InterruptedByUser.d.ts +7 -0
  68. package/lib/types/components/InterruptedByUser.d.ts.map +1 -0
  69. package/lib/types/components/InterruptedByUser.js +9 -0
  70. package/lib/types/components/LoadedContextPanel.d.ts +20 -0
  71. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -0
  72. package/lib/types/components/LoadedContextPanel.js +30 -0
  73. package/lib/types/components/LogoV2.d.ts +23 -0
  74. package/lib/types/components/LogoV2.d.ts.map +1 -0
  75. package/lib/types/components/LogoV2.js +95 -0
  76. package/lib/types/components/Markdown.d.ts +24 -0
  77. package/lib/types/components/Markdown.d.ts.map +1 -0
  78. package/lib/types/components/Markdown.js +106 -0
  79. package/lib/types/components/MarkdownTable.d.ts +17 -0
  80. package/lib/types/components/MarkdownTable.d.ts.map +1 -0
  81. package/lib/types/components/MarkdownTable.js +272 -0
  82. package/lib/types/components/MessageList.d.ts +41 -0
  83. package/lib/types/components/MessageList.d.ts.map +1 -0
  84. package/lib/types/components/MessageList.js +303 -0
  85. package/lib/types/components/ModelPicker.d.ts +14 -0
  86. package/lib/types/components/ModelPicker.d.ts.map +1 -0
  87. package/lib/types/components/ModelPicker.js +15 -0
  88. package/lib/types/components/PresetPicker.d.ts +17 -0
  89. package/lib/types/components/PresetPicker.d.ts.map +1 -0
  90. package/lib/types/components/PresetPicker.js +25 -0
  91. package/lib/types/components/PromptInput.d.ts +67 -0
  92. package/lib/types/components/PromptInput.d.ts.map +1 -0
  93. package/lib/types/components/PromptInput.js +778 -0
  94. package/lib/types/components/ResumePicker.d.ts +15 -0
  95. package/lib/types/components/ResumePicker.d.ts.map +1 -0
  96. package/lib/types/components/ResumePicker.js +34 -0
  97. package/lib/types/components/RewindPicker.d.ts +14 -0
  98. package/lib/types/components/RewindPicker.d.ts.map +1 -0
  99. package/lib/types/components/RewindPicker.js +23 -0
  100. package/lib/types/components/SearchBox.d.ts +20 -0
  101. package/lib/types/components/SearchBox.d.ts.map +1 -0
  102. package/lib/types/components/SearchBox.js +33 -0
  103. package/lib/types/components/Select.d.ts +22 -0
  104. package/lib/types/components/Select.d.ts.map +1 -0
  105. package/lib/types/components/Select.js +19 -0
  106. package/lib/types/components/Spinner/GlimmerMessage.d.ts +19 -0
  107. package/lib/types/components/Spinner/GlimmerMessage.d.ts.map +1 -0
  108. package/lib/types/components/Spinner/GlimmerMessage.js +78 -0
  109. package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +31 -0
  110. package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -0
  111. package/lib/types/components/Spinner/SpinnerAnimationRow.js +162 -0
  112. package/lib/types/components/Spinner/SpinnerGlyph.d.ts +16 -0
  113. package/lib/types/components/Spinner/SpinnerGlyph.d.ts.map +1 -0
  114. package/lib/types/components/Spinner/SpinnerGlyph.js +36 -0
  115. package/lib/types/components/Spinner/spinnerMode.d.ts +6 -0
  116. package/lib/types/components/Spinner/spinnerMode.d.ts.map +1 -0
  117. package/lib/types/components/Spinner/spinnerMode.js +1 -0
  118. package/lib/types/components/Spinner/spinnerUtils.d.ts +41 -0
  119. package/lib/types/components/Spinner/spinnerUtils.d.ts.map +1 -0
  120. package/lib/types/components/Spinner/spinnerUtils.js +104 -0
  121. package/lib/types/components/Spinner/useStalledAnimation.d.ts +16 -0
  122. package/lib/types/components/Spinner/useStalledAnimation.d.ts.map +1 -0
  123. package/lib/types/components/Spinner/useStalledAnimation.js +71 -0
  124. package/lib/types/components/StreamingMarkdown.d.ts +12 -0
  125. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -0
  126. package/lib/types/components/StreamingMarkdown.js +39 -0
  127. package/lib/types/components/ThemePicker.d.ts +22 -0
  128. package/lib/types/components/ThemePicker.d.ts.map +1 -0
  129. package/lib/types/components/ThemePicker.js +51 -0
  130. package/lib/types/components/ThinkingToggle.d.ts +17 -0
  131. package/lib/types/components/ThinkingToggle.d.ts.map +1 -0
  132. package/lib/types/components/ThinkingToggle.js +30 -0
  133. package/lib/types/components/ToolUseLoader.d.ts +13 -0
  134. package/lib/types/components/ToolUseLoader.d.ts.map +1 -0
  135. package/lib/types/components/ToolUseLoader.js +17 -0
  136. package/lib/types/components/Whale.d.ts +22 -0
  137. package/lib/types/components/Whale.d.ts.map +1 -0
  138. package/lib/types/components/Whale.js +78 -0
  139. package/lib/types/components/WorkingSpinner.d.ts +26 -0
  140. package/lib/types/components/WorkingSpinner.d.ts.map +1 -0
  141. package/lib/types/components/WorkingSpinner.js +68 -0
  142. package/lib/types/components/activityFrames.d.ts +31 -0
  143. package/lib/types/components/activityFrames.d.ts.map +1 -0
  144. package/lib/types/components/activityFrames.js +83 -0
  145. package/lib/types/components/bigfont.d.ts +28 -0
  146. package/lib/types/components/bigfont.d.ts.map +1 -0
  147. package/lib/types/components/bigfont.js +91 -0
  148. package/lib/types/components/design-system/Byline.d.ts +14 -0
  149. package/lib/types/components/design-system/Byline.d.ts.map +1 -0
  150. package/lib/types/components/design-system/Byline.js +17 -0
  151. package/lib/types/components/design-system/Divider.d.ts +40 -0
  152. package/lib/types/components/design-system/Divider.d.ts.map +1 -0
  153. package/lib/types/components/design-system/Divider.js +24 -0
  154. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +19 -0
  155. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts.map +1 -0
  156. package/lib/types/components/design-system/KeyboardShortcutHint.js +14 -0
  157. package/lib/types/components/design-system/ListItem.d.ts +36 -0
  158. package/lib/types/components/design-system/ListItem.d.ts.map +1 -0
  159. package/lib/types/components/design-system/ListItem.js +48 -0
  160. package/lib/types/components/design-system/LoadingState.d.ts +19 -0
  161. package/lib/types/components/design-system/LoadingState.d.ts.map +1 -0
  162. package/lib/types/components/design-system/LoadingState.js +22 -0
  163. package/lib/types/components/design-system/Pane.d.ts +17 -0
  164. package/lib/types/components/design-system/Pane.d.ts.map +1 -0
  165. package/lib/types/components/design-system/Pane.js +15 -0
  166. package/lib/types/components/design-system/ProgressBar.d.ts +22 -0
  167. package/lib/types/components/design-system/ProgressBar.d.ts.map +1 -0
  168. package/lib/types/components/design-system/ProgressBar.js +27 -0
  169. package/lib/types/components/design-system/StatusIcon.d.ts +15 -0
  170. package/lib/types/components/design-system/StatusIcon.d.ts.map +1 -0
  171. package/lib/types/components/design-system/StatusIcon.js +20 -0
  172. package/lib/types/components/design-system/ThemeProvider.d.ts +11 -0
  173. package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -0
  174. package/lib/types/components/design-system/ThemeProvider.js +135 -0
  175. package/lib/types/components/design-system/ThemedBox.d.ts +37 -0
  176. package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -0
  177. package/lib/types/components/design-system/ThemedBox.js +26 -0
  178. package/lib/types/components/design-system/ThemedText.d.ts +56 -0
  179. package/lib/types/components/design-system/ThemedText.d.ts.map +1 -0
  180. package/lib/types/components/design-system/ThemedText.js +45 -0
  181. package/lib/types/components/messages/AssistantTextMessage.d.ts +18 -0
  182. package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -0
  183. package/lib/types/components/messages/AssistantTextMessage.js +15 -0
  184. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +23 -0
  185. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -0
  186. package/lib/types/components/messages/AssistantThinkingMessage.js +23 -0
  187. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +23 -0
  188. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -0
  189. package/lib/types/components/messages/AssistantToolUseMessage.js +220 -0
  190. package/lib/types/components/messages/MessageMetadata.d.ts +15 -0
  191. package/lib/types/components/messages/MessageMetadata.d.ts.map +1 -0
  192. package/lib/types/components/messages/MessageMetadata.js +20 -0
  193. package/lib/types/components/messages/UserPromptMessage.d.ts +19 -0
  194. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -0
  195. package/lib/types/components/messages/UserPromptMessage.js +15 -0
  196. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +39 -0
  197. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -0
  198. package/lib/types/components/questions/AskUserQuestionPanel.js +228 -0
  199. package/lib/types/components/shimmer.d.ts +35 -0
  200. package/lib/types/components/shimmer.d.ts.map +1 -0
  201. package/lib/types/components/shimmer.js +43 -0
  202. package/lib/types/components/whaleFrames.d.ts +25 -0
  203. package/lib/types/components/whaleFrames.d.ts.map +1 -0
  204. package/lib/types/components/whaleFrames.js +419 -0
  205. package/lib/types/customTheme.d.ts +104 -0
  206. package/lib/types/customTheme.d.ts.map +1 -0
  207. package/lib/types/customTheme.js +268 -0
  208. package/lib/types/effortPrefs.d.ts +22 -0
  209. package/lib/types/effortPrefs.d.ts.map +1 -0
  210. package/lib/types/effortPrefs.js +45 -0
  211. package/lib/types/history.d.ts +24 -0
  212. package/lib/types/history.d.ts.map +1 -0
  213. package/lib/types/history.js +75 -0
  214. package/lib/types/hooks/useBlink.d.ts +11 -0
  215. package/lib/types/hooks/useBlink.d.ts.map +1 -0
  216. package/lib/types/hooks/useBlink.js +20 -0
  217. package/lib/types/i18n.d.ts +926 -0
  218. package/lib/types/i18n.d.ts.map +1 -0
  219. package/lib/types/i18n.js +356 -0
  220. package/lib/types/index.d.ts +70 -0
  221. package/lib/types/index.d.ts.map +1 -0
  222. package/lib/types/index.js +31 -0
  223. package/lib/types/ink/Ansi.d.ts +11 -0
  224. package/lib/types/ink/Ansi.d.ts.map +1 -0
  225. package/lib/types/ink/Ansi.js +269 -0
  226. package/lib/types/ink/bidi.d.ts +18 -0
  227. package/lib/types/ink/bidi.d.ts.map +1 -0
  228. package/lib/types/ink/bidi.js +119 -0
  229. package/lib/types/ink/clearTerminal.d.ts +22 -0
  230. package/lib/types/ink/clearTerminal.d.ts.map +1 -0
  231. package/lib/types/ink/clearTerminal.js +72 -0
  232. package/lib/types/ink/colorize.d.ts +33 -0
  233. package/lib/types/ink/colorize.d.ts.map +1 -0
  234. package/lib/types/ink/colorize.js +213 -0
  235. package/lib/types/ink/components/AlternateScreen.d.ts +22 -0
  236. package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -0
  237. package/lib/types/ink/components/AlternateScreen.js +74 -0
  238. package/lib/types/ink/components/App.d.ts +73 -0
  239. package/lib/types/ink/components/App.d.ts.map +1 -0
  240. package/lib/types/ink/components/App.js +597 -0
  241. package/lib/types/ink/components/AppContext.d.ts +15 -0
  242. package/lib/types/ink/components/AppContext.d.ts.map +1 -0
  243. package/lib/types/ink/components/AppContext.js +11 -0
  244. package/lib/types/ink/components/Box.d.ts +49 -0
  245. package/lib/types/ink/components/Box.d.ts.map +1 -0
  246. package/lib/types/ink/components/Box.js +153 -0
  247. package/lib/types/ink/components/Button.d.ts +37 -0
  248. package/lib/types/ink/components/Button.d.ts.map +1 -0
  249. package/lib/types/ink/components/Button.js +166 -0
  250. package/lib/types/ink/components/ClockContext.d.ts +10 -0
  251. package/lib/types/ink/components/ClockContext.d.ts.map +1 -0
  252. package/lib/types/ink/components/ClockContext.js +108 -0
  253. package/lib/types/ink/components/CursorDeclarationContext.d.ts +30 -0
  254. package/lib/types/ink/components/CursorDeclarationContext.d.ts.map +1 -0
  255. package/lib/types/ink/components/CursorDeclarationContext.js +6 -0
  256. package/lib/types/ink/components/ErrorOverview.d.ts +7 -0
  257. package/lib/types/ink/components/ErrorOverview.d.ts.map +1 -0
  258. package/lib/types/ink/components/ErrorOverview.js +49 -0
  259. package/lib/types/ink/components/Link.d.ts +8 -0
  260. package/lib/types/ink/components/Link.d.ts.map +1 -0
  261. package/lib/types/ink/components/Link.js +33 -0
  262. package/lib/types/ink/components/Newline.d.ts +13 -0
  263. package/lib/types/ink/components/Newline.d.ts.map +1 -0
  264. package/lib/types/ink/components/Newline.js +29 -0
  265. package/lib/types/ink/components/NoSelect.d.ts +20 -0
  266. package/lib/types/ink/components/NoSelect.d.ts.map +1 -0
  267. package/lib/types/ink/components/NoSelect.js +56 -0
  268. package/lib/types/ink/components/RawAnsi.d.ts +17 -0
  269. package/lib/types/ink/components/RawAnsi.d.ts.map +1 -0
  270. package/lib/types/ink/components/RawAnsi.js +45 -0
  271. package/lib/types/ink/components/ScrollBox.d.ts +78 -0
  272. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -0
  273. package/lib/types/ink/components/ScrollBox.js +174 -0
  274. package/lib/types/ink/components/Spacer.d.ts +6 -0
  275. package/lib/types/ink/components/Spacer.d.ts.map +1 -0
  276. package/lib/types/ink/components/Spacer.js +19 -0
  277. package/lib/types/ink/components/StdinContext.d.ts +32 -0
  278. package/lib/types/ink/components/StdinContext.d.ts.map +1 -0
  279. package/lib/types/ink/components/StdinContext.js +16 -0
  280. package/lib/types/ink/components/TerminalFocusContext.d.ts +11 -0
  281. package/lib/types/ink/components/TerminalFocusContext.d.ts.map +1 -0
  282. package/lib/types/ink/components/TerminalFocusContext.js +45 -0
  283. package/lib/types/ink/components/TerminalSizeContext.d.ts +6 -0
  284. package/lib/types/ink/components/TerminalSizeContext.d.ts.map +1 -0
  285. package/lib/types/ink/components/TerminalSizeContext.js +3 -0
  286. package/lib/types/ink/components/Text.d.ts +56 -0
  287. package/lib/types/ink/components/Text.d.ts.map +1 -0
  288. package/lib/types/ink/components/Text.js +194 -0
  289. package/lib/types/ink/constants.d.ts +3 -0
  290. package/lib/types/ink/constants.d.ts.map +1 -0
  291. package/lib/types/ink/constants.js +2 -0
  292. package/lib/types/ink/cursor.d.ts +11 -0
  293. package/lib/types/ink/cursor.d.ts.map +1 -0
  294. package/lib/types/ink/cursor.js +1 -0
  295. package/lib/types/ink/devtools.d.ts +7 -0
  296. package/lib/types/ink/devtools.d.ts.map +1 -0
  297. package/lib/types/ink/devtools.js +1 -0
  298. package/lib/types/ink/dom.d.ts +178 -0
  299. package/lib/types/ink/dom.d.ts.map +1 -0
  300. package/lib/types/ink/dom.js +424 -0
  301. package/lib/types/ink/events/click-event.d.ts +32 -0
  302. package/lib/types/ink/events/click-event.d.ts.map +1 -0
  303. package/lib/types/ink/events/click-event.js +36 -0
  304. package/lib/types/ink/events/dispatcher.d.ts +63 -0
  305. package/lib/types/ink/events/dispatcher.d.ts.map +1 -0
  306. package/lib/types/ink/events/dispatcher.js +194 -0
  307. package/lib/types/ink/events/emitter.d.ts +20 -0
  308. package/lib/types/ink/events/emitter.d.ts.map +1 -0
  309. package/lib/types/ink/events/emitter.js +43 -0
  310. package/lib/types/ink/events/event-handlers.d.ts +47 -0
  311. package/lib/types/ink/events/event-handlers.d.ts.map +1 -0
  312. package/lib/types/ink/events/event-handlers.js +30 -0
  313. package/lib/types/ink/events/event.d.ts +19 -0
  314. package/lib/types/ink/events/event.d.ts.map +1 -0
  315. package/lib/types/ink/events/event.js +22 -0
  316. package/lib/types/ink/events/focus-event.d.ts +19 -0
  317. package/lib/types/ink/events/focus-event.d.ts.map +1 -0
  318. package/lib/types/ink/events/focus-event.js +21 -0
  319. package/lib/types/ink/events/input-event.d.ts +53 -0
  320. package/lib/types/ink/events/input-event.d.ts.map +1 -0
  321. package/lib/types/ink/events/input-event.js +180 -0
  322. package/lib/types/ink/events/keyboard-event.d.ts +40 -0
  323. package/lib/types/ink/events/keyboard-event.d.ts.map +1 -0
  324. package/lib/types/ink/events/keyboard-event.js +65 -0
  325. package/lib/types/ink/events/paste-event.d.ts +11 -0
  326. package/lib/types/ink/events/paste-event.d.ts.map +1 -0
  327. package/lib/types/ink/events/paste-event.js +13 -0
  328. package/lib/types/ink/events/resize-event.d.ts +13 -0
  329. package/lib/types/ink/events/resize-event.d.ts.map +1 -0
  330. package/lib/types/ink/events/resize-event.js +16 -0
  331. package/lib/types/ink/events/terminal-event.d.ts +112 -0
  332. package/lib/types/ink/events/terminal-event.d.ts.map +1 -0
  333. package/lib/types/ink/events/terminal-event.js +131 -0
  334. package/lib/types/ink/events/terminal-focus-event.d.ts +21 -0
  335. package/lib/types/ink/events/terminal-focus-event.d.ts.map +1 -0
  336. package/lib/types/ink/events/terminal-focus-event.js +18 -0
  337. package/lib/types/ink/focus.d.ts +74 -0
  338. package/lib/types/ink/focus.d.ts.map +1 -0
  339. package/lib/types/ink/focus.js +188 -0
  340. package/lib/types/ink/frame.d.ts +119 -0
  341. package/lib/types/ink/frame.d.ts.map +1 -0
  342. package/lib/types/ink/frame.js +43 -0
  343. package/lib/types/ink/get-max-width.d.ts +20 -0
  344. package/lib/types/ink/get-max-width.d.ts.map +1 -0
  345. package/lib/types/ink/get-max-width.js +25 -0
  346. package/lib/types/ink/hit-test.d.ts +47 -0
  347. package/lib/types/ink/hit-test.d.ts.map +1 -0
  348. package/lib/types/ink/hit-test.js +126 -0
  349. package/lib/types/ink/hooks/use-animation-frame.d.ts +27 -0
  350. package/lib/types/ink/hooks/use-animation-frame.d.ts.map +1 -0
  351. package/lib/types/ink/hooks/use-animation-frame.js +48 -0
  352. package/lib/types/ink/hooks/use-app.d.ts +8 -0
  353. package/lib/types/ink/hooks/use-app.d.ts.map +1 -0
  354. package/lib/types/ink/hooks/use-app.js +8 -0
  355. package/lib/types/ink/hooks/use-copy-on-select.d.ts +22 -0
  356. package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -0
  357. package/lib/types/ink/hooks/use-copy-on-select.js +40 -0
  358. package/lib/types/ink/hooks/use-declared-cursor.d.ts +31 -0
  359. package/lib/types/ink/hooks/use-declared-cursor.d.ts.map +1 -0
  360. package/lib/types/ink/hooks/use-declared-cursor.js +65 -0
  361. package/lib/types/ink/hooks/use-input.d.ts +41 -0
  362. package/lib/types/ink/hooks/use-input.d.ts.map +1 -0
  363. package/lib/types/ink/hooks/use-input.js +73 -0
  364. package/lib/types/ink/hooks/use-interval.d.ts +23 -0
  365. package/lib/types/ink/hooks/use-interval.d.ts.map +1 -0
  366. package/lib/types/ink/hooks/use-interval.js +58 -0
  367. package/lib/types/ink/hooks/use-search-highlight.d.ts +34 -0
  368. package/lib/types/ink/hooks/use-search-highlight.d.ts.map +1 -0
  369. package/lib/types/ink/hooks/use-search-highlight.js +35 -0
  370. package/lib/types/ink/hooks/use-selection.d.ts +44 -0
  371. package/lib/types/ink/hooks/use-selection.d.ts.map +1 -0
  372. package/lib/types/ink/hooks/use-selection.js +64 -0
  373. package/lib/types/ink/hooks/use-stdin.d.ts +8 -0
  374. package/lib/types/ink/hooks/use-stdin.d.ts.map +1 -0
  375. package/lib/types/ink/hooks/use-stdin.js +8 -0
  376. package/lib/types/ink/hooks/use-tab-status.d.ts +18 -0
  377. package/lib/types/ink/hooks/use-tab-status.d.ts.map +1 -0
  378. package/lib/types/ink/hooks/use-tab-status.js +58 -0
  379. package/lib/types/ink/hooks/use-terminal-focus.d.ts +11 -0
  380. package/lib/types/ink/hooks/use-terminal-focus.d.ts.map +1 -0
  381. package/lib/types/ink/hooks/use-terminal-focus.js +15 -0
  382. package/lib/types/ink/hooks/use-terminal-size.d.ts +6 -0
  383. package/lib/types/ink/hooks/use-terminal-size.d.ts.map +1 -0
  384. package/lib/types/ink/hooks/use-terminal-size.js +12 -0
  385. package/lib/types/ink/hooks/use-terminal-title.d.ts +14 -0
  386. package/lib/types/ink/hooks/use-terminal-title.d.ts.map +1 -0
  387. package/lib/types/ink/hooks/use-terminal-title.js +30 -0
  388. package/lib/types/ink/hooks/use-terminal-viewport.d.ts +32 -0
  389. package/lib/types/ink/hooks/use-terminal-viewport.d.ts.map +1 -0
  390. package/lib/types/ink/hooks/use-terminal-viewport.js +79 -0
  391. package/lib/types/ink/ink.d.ts +370 -0
  392. package/lib/types/ink/ink.d.ts.map +1 -0
  393. package/lib/types/ink/ink.js +1665 -0
  394. package/lib/types/ink/instances.d.ts +10 -0
  395. package/lib/types/ink/instances.d.ts.map +1 -0
  396. package/lib/types/ink/instances.js +13 -0
  397. package/lib/types/ink/layout/engine.d.ts +7 -0
  398. package/lib/types/ink/layout/engine.d.ts.map +1 -0
  399. package/lib/types/ink/layout/engine.js +8 -0
  400. package/lib/types/ink/layout/geometry.d.ts +86 -0
  401. package/lib/types/ink/layout/geometry.d.ts.map +1 -0
  402. package/lib/types/ink/layout/geometry.js +95 -0
  403. package/lib/types/ink/layout/node.d.ts +151 -0
  404. package/lib/types/ink/layout/node.d.ts.map +1 -0
  405. package/lib/types/ink/layout/node.js +72 -0
  406. package/lib/types/ink/layout/yoga.d.ts +69 -0
  407. package/lib/types/ink/layout/yoga.d.ts.map +1 -0
  408. package/lib/types/ink/layout/yoga.js +249 -0
  409. package/lib/types/ink/line-width-cache.d.ts +9 -0
  410. package/lib/types/ink/line-width-cache.d.ts.map +1 -0
  411. package/lib/types/ink/line-width-cache.js +45 -0
  412. package/lib/types/ink/log-update.d.ts +57 -0
  413. package/lib/types/ink/log-update.d.ts.map +1 -0
  414. package/lib/types/ink/log-update.js +710 -0
  415. package/lib/types/ink/measure-element.d.ts +19 -0
  416. package/lib/types/ink/measure-element.d.ts.map +1 -0
  417. package/lib/types/ink/measure-element.js +10 -0
  418. package/lib/types/ink/measure-text.d.ts +13 -0
  419. package/lib/types/ink/measure-text.d.ts.map +1 -0
  420. package/lib/types/ink/measure-text.js +38 -0
  421. package/lib/types/ink/node-cache.d.ts +33 -0
  422. package/lib/types/ink/node-cache.d.ts.map +1 -0
  423. package/lib/types/ink/node-cache.js +42 -0
  424. package/lib/types/ink/optimizer.d.ts +18 -0
  425. package/lib/types/ink/optimizer.d.ts.map +1 -0
  426. package/lib/types/ink/optimizer.js +83 -0
  427. package/lib/types/ink/output.d.ts +200 -0
  428. package/lib/types/ink/output.d.ts.map +1 -0
  429. package/lib/types/ink/output.js +663 -0
  430. package/lib/types/ink/parse-keypress.d.ts +132 -0
  431. package/lib/types/ink/parse-keypress.d.ts.map +1 -0
  432. package/lib/types/ink/parse-keypress.js +707 -0
  433. package/lib/types/ink/reconciler.d.ts +43 -0
  434. package/lib/types/ink/reconciler.d.ts.map +1 -0
  435. package/lib/types/ink/reconciler.js +413 -0
  436. package/lib/types/ink/render-border.d.ts +36 -0
  437. package/lib/types/ink/render-border.d.ts.map +1 -0
  438. package/lib/types/ink/render-border.js +143 -0
  439. package/lib/types/ink/render-node-to-output.d.ts +93 -0
  440. package/lib/types/ink/render-node-to-output.d.ts.map +1 -0
  441. package/lib/types/ink/render-node-to-output.js +1290 -0
  442. package/lib/types/ink/render-to-screen.d.ts +65 -0
  443. package/lib/types/ink/render-to-screen.d.ts.map +1 -0
  444. package/lib/types/ink/render-to-screen.js +189 -0
  445. package/lib/types/ink/renderer.d.ts +24 -0
  446. package/lib/types/ink/renderer.d.ts.map +1 -0
  447. package/lib/types/ink/renderer.js +136 -0
  448. package/lib/types/ink/root.d.ts +91 -0
  449. package/lib/types/ink/root.d.ts.map +1 -0
  450. package/lib/types/ink/root.js +93 -0
  451. package/lib/types/ink/screen.d.ts +443 -0
  452. package/lib/types/ink/screen.d.ts.map +1 -0
  453. package/lib/types/ink/screen.js +1295 -0
  454. package/lib/types/ink/searchHighlight.d.ts +25 -0
  455. package/lib/types/ink/searchHighlight.d.ts.map +1 -0
  456. package/lib/types/ink/searchHighlight.js +82 -0
  457. package/lib/types/ink/selection.d.ts +323 -0
  458. package/lib/types/ink/selection.d.ts.map +1 -0
  459. package/lib/types/ink/selection.js +881 -0
  460. package/lib/types/ink/squash-text-nodes.d.ts +30 -0
  461. package/lib/types/ink/squash-text-nodes.d.ts.map +1 -0
  462. package/lib/types/ink/squash-text-nodes.js +63 -0
  463. package/lib/types/ink/stringWidth.d.ts +11 -0
  464. package/lib/types/ink/stringWidth.d.ts.map +1 -0
  465. package/lib/types/ink/stringWidth.js +209 -0
  466. package/lib/types/ink/styles.d.ts +324 -0
  467. package/lib/types/ink/styles.d.ts.map +1 -0
  468. package/lib/types/ink/styles.js +307 -0
  469. package/lib/types/ink/supports-hyperlinks.d.ts +20 -0
  470. package/lib/types/ink/supports-hyperlinks.d.ts.map +1 -0
  471. package/lib/types/ink/supports-hyperlinks.js +44 -0
  472. package/lib/types/ink/tabstops.d.ts +9 -0
  473. package/lib/types/ink/tabstops.d.ts.map +1 -0
  474. package/lib/types/ink/tabstops.js +46 -0
  475. package/lib/types/ink/terminal-focus-state.d.ts +35 -0
  476. package/lib/types/ink/terminal-focus-state.d.ts.map +1 -0
  477. package/lib/types/ink/terminal-focus-state.js +57 -0
  478. package/lib/types/ink/terminal-querier.d.ts +161 -0
  479. package/lib/types/ink/terminal-querier.d.ts.map +1 -0
  480. package/lib/types/ink/terminal-querier.js +204 -0
  481. package/lib/types/ink/terminal.d.ts +80 -0
  482. package/lib/types/ink/terminal.d.ts.map +1 -0
  483. package/lib/types/ink/terminal.js +268 -0
  484. package/lib/types/ink/termio/ansi.d.ts +75 -0
  485. package/lib/types/ink/termio/ansi.d.ts.map +1 -0
  486. package/lib/types/ink/termio/ansi.js +79 -0
  487. package/lib/types/ink/termio/csi.d.ts +253 -0
  488. package/lib/types/ink/termio/csi.d.ts.map +1 -0
  489. package/lib/types/ink/termio/csi.js +347 -0
  490. package/lib/types/ink/termio/dec.d.ts +61 -0
  491. package/lib/types/ink/termio/dec.d.ts.map +1 -0
  492. package/lib/types/ink/termio/dec.js +76 -0
  493. package/lib/types/ink/termio/esc.d.ts +15 -0
  494. package/lib/types/ink/termio/esc.d.ts.map +1 -0
  495. package/lib/types/ink/termio/esc.js +57 -0
  496. package/lib/types/ink/termio/osc.d.ts +208 -0
  497. package/lib/types/ink/termio/osc.d.ts.map +1 -0
  498. package/lib/types/ink/termio/osc.js +466 -0
  499. package/lib/types/ink/termio/parser.d.ts +46 -0
  500. package/lib/types/ink/termio/parser.d.ts.map +1 -0
  501. package/lib/types/ink/termio/parser.js +366 -0
  502. package/lib/types/ink/termio/sgr.d.ts +17 -0
  503. package/lib/types/ink/termio/sgr.d.ts.map +1 -0
  504. package/lib/types/ink/termio/sgr.js +300 -0
  505. package/lib/types/ink/termio/tokenize.d.ts +50 -0
  506. package/lib/types/ink/termio/tokenize.d.ts.map +1 -0
  507. package/lib/types/ink/termio/tokenize.js +266 -0
  508. package/lib/types/ink/termio/types.d.ts +211 -0
  509. package/lib/types/ink/termio/types.d.ts.map +1 -0
  510. package/lib/types/ink/termio/types.js +68 -0
  511. package/lib/types/ink/termio.d.ts +25 -0
  512. package/lib/types/ink/termio.d.ts.map +1 -0
  513. package/lib/types/ink/termio.js +24 -0
  514. package/lib/types/ink/truncateToWidth.d.ts +7 -0
  515. package/lib/types/ink/truncateToWidth.d.ts.map +1 -0
  516. package/lib/types/ink/truncateToWidth.js +18 -0
  517. package/lib/types/ink/useTerminalNotification.d.ts +45 -0
  518. package/lib/types/ink/useTerminalNotification.d.ts.map +1 -0
  519. package/lib/types/ink/useTerminalNotification.js +70 -0
  520. package/lib/types/ink/warn.d.ts +7 -0
  521. package/lib/types/ink/warn.d.ts.map +1 -0
  522. package/lib/types/ink/warn.js +15 -0
  523. package/lib/types/ink/widest-line.d.ts +7 -0
  524. package/lib/types/ink/widest-line.d.ts.map +1 -0
  525. package/lib/types/ink/widest-line.js +19 -0
  526. package/lib/types/ink/wrap-text.d.ts +10 -0
  527. package/lib/types/ink/wrap-text.d.ts.map +1 -0
  528. package/lib/types/ink/wrap-text.js +61 -0
  529. package/lib/types/ink/wrapAnsi.d.ts +18 -0
  530. package/lib/types/ink/wrapAnsi.d.ts.map +1 -0
  531. package/lib/types/ink/wrapAnsi.js +16 -0
  532. package/lib/types/invariant.d.ts +20 -0
  533. package/lib/types/invariant.d.ts.map +1 -0
  534. package/lib/types/invariant.js +26 -0
  535. package/lib/types/modelPrefs.d.ts +37 -0
  536. package/lib/types/modelPrefs.d.ts.map +1 -0
  537. package/lib/types/modelPrefs.js +66 -0
  538. package/lib/types/modelRoute.d.ts +79 -0
  539. package/lib/types/modelRoute.d.ts.map +1 -0
  540. package/lib/types/modelRoute.js +96 -0
  541. package/lib/types/native-ts/yoga-layout/enums.d.ts +183 -0
  542. package/lib/types/native-ts/yoga-layout/enums.d.ts.map +1 -0
  543. package/lib/types/native-ts/yoga-layout/enums.js +150 -0
  544. package/lib/types/native-ts/yoga-layout/index.d.ts +788 -0
  545. package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -0
  546. package/lib/types/native-ts/yoga-layout/index.js +2571 -0
  547. package/lib/types/packaged-skills.d.ts +10 -0
  548. package/lib/types/packaged-skills.d.ts.map +1 -0
  549. package/lib/types/packaged-skills.js +63 -0
  550. package/lib/types/plugin.d.ts +33 -0
  551. package/lib/types/plugin.d.ts.map +1 -0
  552. package/lib/types/plugin.js +454 -0
  553. package/lib/types/presetPrefs.d.ts +29 -0
  554. package/lib/types/presetPrefs.d.ts.map +1 -0
  555. package/lib/types/presetPrefs.js +61 -0
  556. package/lib/types/presets.d.ts +110 -0
  557. package/lib/types/presets.d.ts.map +1 -0
  558. package/lib/types/presets.js +114 -0
  559. package/lib/types/questions.d.ts +97 -0
  560. package/lib/types/questions.d.ts.map +1 -0
  561. package/lib/types/questions.js +201 -0
  562. package/lib/types/screens/Chat.d.ts +23 -0
  563. package/lib/types/screens/Chat.d.ts.map +1 -0
  564. package/lib/types/screens/Chat.js +1263 -0
  565. package/lib/types/screens/StatusLine.d.ts +16 -0
  566. package/lib/types/screens/StatusLine.d.ts.map +1 -0
  567. package/lib/types/screens/StatusLine.js +105 -0
  568. package/lib/types/screens/StatusMetrics.d.ts +87 -0
  569. package/lib/types/screens/StatusMetrics.d.ts.map +1 -0
  570. package/lib/types/screens/StatusMetrics.js +252 -0
  571. package/lib/types/sessionHistory.d.ts +32 -0
  572. package/lib/types/sessionHistory.d.ts.map +1 -0
  573. package/lib/types/sessionHistory.js +87 -0
  574. package/lib/types/theme.d.ts +115 -0
  575. package/lib/types/theme.d.ts.map +1 -0
  576. package/lib/types/theme.js +300 -0
  577. package/lib/types/themePrefs.d.ts +28 -0
  578. package/lib/types/themePrefs.d.ts.map +1 -0
  579. package/lib/types/themePrefs.js +59 -0
  580. package/lib/types/ui.d.ts +28 -0
  581. package/lib/types/ui.d.ts.map +1 -0
  582. package/lib/types/ui.js +25 -0
  583. package/lib/types/update.d.ts +76 -0
  584. package/lib/types/update.d.ts.map +1 -0
  585. package/lib/types/update.js +203 -0
  586. package/lib/types/utils/clipboard.d.ts +43 -0
  587. package/lib/types/utils/clipboard.d.ts.map +1 -0
  588. package/lib/types/utils/clipboard.js +82 -0
  589. package/lib/types/utils/debug.d.ts +8 -0
  590. package/lib/types/utils/debug.d.ts.map +1 -0
  591. package/lib/types/utils/debug.js +12 -0
  592. package/lib/types/utils/earlyInput.d.ts +7 -0
  593. package/lib/types/utils/earlyInput.d.ts.map +1 -0
  594. package/lib/types/utils/earlyInput.js +6 -0
  595. package/lib/types/utils/env.d.ts +10 -0
  596. package/lib/types/utils/env.d.ts.map +1 -0
  597. package/lib/types/utils/env.js +9 -0
  598. package/lib/types/utils/envUtils.d.ts +8 -0
  599. package/lib/types/utils/envUtils.d.ts.map +1 -0
  600. package/lib/types/utils/envUtils.js +13 -0
  601. package/lib/types/utils/execFileNoThrow.d.ts +34 -0
  602. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -0
  603. package/lib/types/utils/execFileNoThrow.js +31 -0
  604. package/lib/types/utils/fullscreen.d.ts +8 -0
  605. package/lib/types/utils/fullscreen.d.ts.map +1 -0
  606. package/lib/types/utils/fullscreen.js +10 -0
  607. package/lib/types/utils/intl.d.ts +7 -0
  608. package/lib/types/utils/intl.d.ts.map +1 -0
  609. package/lib/types/utils/intl.js +9 -0
  610. package/lib/types/utils/loaded-context.d.ts +20 -0
  611. package/lib/types/utils/loaded-context.d.ts.map +1 -0
  612. package/lib/types/utils/loaded-context.js +36 -0
  613. package/lib/types/utils/log.d.ts +7 -0
  614. package/lib/types/utils/log.d.ts.map +1 -0
  615. package/lib/types/utils/log.js +9 -0
  616. package/lib/types/utils/mentions.d.ts +30 -0
  617. package/lib/types/utils/mentions.d.ts.map +1 -0
  618. package/lib/types/utils/mentions.js +72 -0
  619. package/lib/types/utils/narration.d.ts +11 -0
  620. package/lib/types/utils/narration.d.ts.map +1 -0
  621. package/lib/types/utils/narration.js +16 -0
  622. package/lib/types/utils/semver.d.ts +9 -0
  623. package/lib/types/utils/semver.d.ts.map +1 -0
  624. package/lib/types/utils/semver.js +24 -0
  625. package/lib/types/utils/sliceAnsi.d.ts +12 -0
  626. package/lib/types/utils/sliceAnsi.d.ts.map +1 -0
  627. package/lib/types/utils/sliceAnsi.js +79 -0
  628. package/lib/types/working-activity.d.ts +19 -0
  629. package/lib/types/working-activity.d.ts.map +1 -0
  630. package/lib/types/working-activity.js +18 -0
  631. package/package.json +141 -0
  632. package/skills/audit/SKILL.md +24 -0
  633. package/skills/bug/SKILL.md +27 -0
  634. package/skills/pr-comments/SKILL.md +20 -0
  635. package/skills/practice/SKILL.md +22 -0
  636. package/skills/release-notes/SKILL.md +25 -0
  637. package/skills/review/SKILL.md +25 -0
  638. package/skills/vuln-check/SKILL.md +25 -0
@@ -0,0 +1,881 @@
1
+ /**
2
+ * Text selection state for fullscreen mode.
3
+ *
4
+ * Tracks a linear selection in screen-buffer coordinates (0-indexed col/row).
5
+ * Selection is line-based: cells from (startCol, startRow) through
6
+ * (endCol, endRow) inclusive, wrapping across line boundaries. This matches
7
+ * terminal-native selection behavior (not rectangular/block).
8
+ *
9
+ * The selection is stored as ANCHOR (where the drag started) + FOCUS (where
10
+ * the cursor is now). The rendered highlight normalizes to start ≤ end.
11
+ */
12
+ import { clamp } from './layout/geometry.js';
13
+ import { cellAt, cellAtIndex, setCellStyleId } from './screen.js';
14
+ /**
15
+ * Create a new selection state with no active selection.
16
+ * @returns the empty selection state.
17
+ */
18
+ export function createSelectionState() {
19
+ return {
20
+ anchor: null,
21
+ focus: null,
22
+ isDragging: false,
23
+ anchorSpan: null,
24
+ scrolledOffAbove: [],
25
+ scrolledOffBelow: [],
26
+ scrolledOffAboveSW: [],
27
+ scrolledOffBelowSW: [],
28
+ lastPressHadAlt: false,
29
+ };
30
+ }
31
+ /**
32
+ * Begin a new drag selection at (col, row): set the anchor, mark the
33
+ * state as dragging, and clear every accumulator. Focus stays null until
34
+ * the first drag motion, so a bare click never highlights a cell.
35
+ * @param s - the selection state to mutate.
36
+ * @param col - anchor column in screen-buffer coordinates.
37
+ * @param row - anchor row in screen-buffer coordinates.
38
+ */
39
+ export function startSelection(s, col, row) {
40
+ s.anchor = { col, row };
41
+ // Focus is not set until the first drag motion. A click-release with no
42
+ // drag leaves focus null → hasSelection/selectionBounds return false/null
43
+ // via the `!s.focus` check, so a bare click never highlights a cell.
44
+ s.focus = null;
45
+ s.isDragging = true;
46
+ s.anchorSpan = null;
47
+ s.scrolledOffAbove = [];
48
+ s.scrolledOffBelow = [];
49
+ s.scrolledOffAboveSW = [];
50
+ s.scrolledOffBelowSW = [];
51
+ s.virtualAnchorRow = undefined;
52
+ s.virtualFocusRow = undefined;
53
+ s.lastPressHadAlt = false;
54
+ }
55
+ /**
56
+ * Track the drag position: update focus to (col, row). No-op while not
57
+ * dragging, and the first motion at the anchor cell is ignored so a bare
58
+ * click never becomes a one-cell selection.
59
+ * @param s - the selection state to mutate.
60
+ * @param col - current mouse column.
61
+ * @param row - current mouse row.
62
+ */
63
+ export function updateSelection(s, col, row) {
64
+ if (!s.isDragging)
65
+ return;
66
+ // First motion at the same cell as anchor is a no-op. Terminals in mode
67
+ // 1002 can fire a drag event at the anchor cell (sub-pixel tremor, or a
68
+ // motion-release pair). Setting focus here would turn a bare click into
69
+ // a 1-cell selection and clobber the clipboard via useCopyOnSelect. Once
70
+ // focus is set (real drag), we track normally including back to anchor.
71
+ if (!s.focus && s.anchor && s.anchor.col === col && s.anchor.row === row)
72
+ return;
73
+ s.focus = { col, row };
74
+ }
75
+ /**
76
+ * End a drag: clear the dragging flag while keeping anchor/focus so the
77
+ * highlight stays visible and the text can be copied. Call
78
+ * clearSelection to drop the selection after copy or on Esc.
79
+ * @param s - the selection state to mutate.
80
+ */
81
+ export function finishSelection(s) {
82
+ s.isDragging = false;
83
+ // Keep anchor/focus so highlight stays visible and text can be copied.
84
+ // Clear via clearSelection() on Esc or after copy.
85
+ }
86
+ /**
87
+ * Reset the selection to the empty state: no anchor, focus, span, or
88
+ * scrolled-off accumulators.
89
+ * @param s - the selection state to mutate.
90
+ */
91
+ export function clearSelection(s) {
92
+ s.anchor = null;
93
+ s.focus = null;
94
+ s.isDragging = false;
95
+ s.anchorSpan = null;
96
+ s.scrolledOffAbove = [];
97
+ s.scrolledOffBelow = [];
98
+ s.scrolledOffAboveSW = [];
99
+ s.scrolledOffBelowSW = [];
100
+ s.virtualAnchorRow = undefined;
101
+ s.virtualFocusRow = undefined;
102
+ s.lastPressHadAlt = false;
103
+ }
104
+ // Unicode-aware word character matcher: letters (any script), digits,
105
+ // and the punctuation set iTerm2 treats as word-part by default.
106
+ // Matching iTerm2's default means double-clicking a path like
107
+ // `/usr/bin/bash` or `~/.claude/config.json` selects the whole thing,
108
+ // which is the muscle memory most macOS terminal users have.
109
+ // iTerm2 default "characters considered part of a word": /-+\~_.
110
+ const WORD_CHAR = /[\p{L}\p{N}_/.\-+~\\]/u;
111
+ /**
112
+ * Character class for double-click word-expansion. Cells with the same
113
+ * class as the clicked cell are included in the selection; a class change
114
+ * is a boundary. Matches typical terminal-emulator behavior (iTerm2 etc.):
115
+ * double-click on `foo` selects `foo`, on `->` selects `->`, on spaces
116
+ * selects the whitespace run.
117
+ */
118
+ function charClass(c) {
119
+ if (c === ' ' || c === '')
120
+ return 0;
121
+ if (WORD_CHAR.test(c))
122
+ return 1;
123
+ return 2;
124
+ }
125
+ /**
126
+ * Find the bounds of the same-class character run at (col, row). Returns
127
+ * null if the click is out of bounds or lands on a noSelect cell. Used by
128
+ * selectWordAt (initial double-click) and extendWordSelection (drag).
129
+ */
130
+ function wordBoundsAt(screen, col, row) {
131
+ if (row < 0 || row >= screen.height)
132
+ return null;
133
+ const width = screen.width;
134
+ const noSelect = screen.noSelect;
135
+ const rowOff = row * width;
136
+ // If the click landed on the spacer tail of a wide char, step back to
137
+ // the head so the class check sees the actual grapheme.
138
+ let c = col;
139
+ if (c > 0) {
140
+ const cell = cellAt(screen, c, row);
141
+ if (cell && cell.width === 2 /* CellWidth.SpacerTail */)
142
+ c -= 1;
143
+ }
144
+ if (c < 0 || c >= width || noSelect[rowOff + c] === 1)
145
+ return null;
146
+ const startCell = cellAt(screen, c, row);
147
+ if (!startCell)
148
+ return null;
149
+ const cls = charClass(startCell.char);
150
+ // Expand left: include cells of the same class, stop at noSelect or
151
+ // class change. SpacerTail cells are stepped over (the wide-char head
152
+ // at the preceding column determines the class).
153
+ let lo = c;
154
+ while (lo > 0) {
155
+ const prev = lo - 1;
156
+ if (noSelect[rowOff + prev] === 1)
157
+ break;
158
+ const pc = cellAt(screen, prev, row);
159
+ if (!pc)
160
+ break;
161
+ if (pc.width === 2 /* CellWidth.SpacerTail */) {
162
+ // Step over the spacer to the wide-char head
163
+ if (prev === 0 || noSelect[rowOff + prev - 1] === 1)
164
+ break;
165
+ const head = cellAt(screen, prev - 1, row);
166
+ if (!head || charClass(head.char) !== cls)
167
+ break;
168
+ lo = prev - 1;
169
+ continue;
170
+ }
171
+ if (charClass(pc.char) !== cls)
172
+ break;
173
+ lo = prev;
174
+ }
175
+ // Expand right: same logic, skipping spacer tails.
176
+ let hi = c;
177
+ while (hi < width - 1) {
178
+ const next = hi + 1;
179
+ if (noSelect[rowOff + next] === 1)
180
+ break;
181
+ const nc = cellAt(screen, next, row);
182
+ if (!nc)
183
+ break;
184
+ if (nc.width === 2 /* CellWidth.SpacerTail */) {
185
+ // Include the spacer tail in the selection range (it belongs to
186
+ // the wide char at hi) and continue past it.
187
+ hi = next;
188
+ continue;
189
+ }
190
+ if (charClass(nc.char) !== cls)
191
+ break;
192
+ hi = next;
193
+ }
194
+ return { lo, hi };
195
+ }
196
+ /** -1 if a < b, 1 if a > b, 0 if equal (reading order: row then col). */
197
+ function comparePoints(a, b) {
198
+ if (a.row !== b.row)
199
+ return a.row < b.row ? -1 : 1;
200
+ if (a.col !== b.col)
201
+ return a.col < b.col ? -1 : 1;
202
+ return 0;
203
+ }
204
+ /**
205
+ * Select the word at (col, row) by scanning the screen buffer for the
206
+ * bounds of the same-class character run. Mutates the selection in place.
207
+ * No-op if the click is out of bounds or lands on a noSelect cell.
208
+ * Sets isDragging=true and anchorSpan so a subsequent drag extends the
209
+ * selection word-by-word (native macOS behavior).
210
+ * @param s - the selection state to mutate.
211
+ * @param screen - the screen buffer to scan for word bounds.
212
+ * @param col - click column.
213
+ * @param row - click row.
214
+ */
215
+ export function selectWordAt(s, screen, col, row) {
216
+ const b = wordBoundsAt(screen, col, row);
217
+ if (!b)
218
+ return;
219
+ const lo = { col: b.lo, row };
220
+ const hi = { col: b.hi, row };
221
+ s.anchor = lo;
222
+ s.focus = hi;
223
+ s.isDragging = true;
224
+ s.anchorSpan = { lo, hi, kind: 'word' };
225
+ }
226
+ // Printable ASCII minus terminal URL delimiters. Restricting to single-
227
+ // codeunit ASCII keeps cell-count === string-index, so the column-span
228
+ // check below is exact (no wide-char/grapheme drift).
229
+ const URL_BOUNDARY = new Set([...'<>"\'` ']);
230
+ function isUrlChar(c) {
231
+ if (c.length !== 1)
232
+ return false;
233
+ const code = c.charCodeAt(0);
234
+ return code >= 0x21 && code <= 0x7e && !URL_BOUNDARY.has(c);
235
+ }
236
+ /**
237
+ * Scan the screen buffer for a plain-text URL at (col, row). Mirrors the
238
+ * terminal's native Cmd+Click URL detection, which fullscreen mode's mouse
239
+ * tracking intercepts. Called from getHyperlinkAt as a fallback when the
240
+ * cell has no OSC 8 hyperlink.
241
+ * @param screen - the screen buffer to scan.
242
+ * @param col - click column.
243
+ * @param row - click row.
244
+ * @returns the URL at the click position, or undefined when the cell is
245
+ * not part of a plain-text URL.
246
+ */
247
+ export function findPlainTextUrlAt(screen, col, row) {
248
+ if (row < 0 || row >= screen.height)
249
+ return undefined;
250
+ const width = screen.width;
251
+ const noSelect = screen.noSelect;
252
+ const rowOff = row * width;
253
+ let c = col;
254
+ if (c > 0) {
255
+ const cell = cellAt(screen, c, row);
256
+ if (cell && cell.width === 2 /* CellWidth.SpacerTail */)
257
+ c -= 1;
258
+ }
259
+ if (c < 0 || c >= width || noSelect[rowOff + c] === 1)
260
+ return undefined;
261
+ const startCell = cellAt(screen, c, row);
262
+ if (!startCell || !isUrlChar(startCell.char))
263
+ return undefined;
264
+ // Expand left/right to the bounds of the URL-char run. URLs are ASCII
265
+ // (CellWidth.Narrow, 1 codeunit), so hitting a non-ASCII/wide/spacer
266
+ // cell is a boundary — no need to step over spacers like wordBoundsAt.
267
+ let lo = c;
268
+ while (lo > 0) {
269
+ const prev = lo - 1;
270
+ if (noSelect[rowOff + prev] === 1)
271
+ break;
272
+ const pc = cellAt(screen, prev, row);
273
+ if (!pc || pc.width !== 0 /* CellWidth.Narrow */ || !isUrlChar(pc.char))
274
+ break;
275
+ lo = prev;
276
+ }
277
+ let hi = c;
278
+ while (hi < width - 1) {
279
+ const next = hi + 1;
280
+ if (noSelect[rowOff + next] === 1)
281
+ break;
282
+ const nc = cellAt(screen, next, row);
283
+ if (!nc || nc.width !== 0 /* CellWidth.Narrow */ || !isUrlChar(nc.char))
284
+ break;
285
+ hi = next;
286
+ }
287
+ let token = '';
288
+ for (let i = lo; i <= hi; i++)
289
+ token += cellAt(screen, i, row).char;
290
+ // 1 cell = 1 char across [lo, hi] (ASCII-only run), so string index =
291
+ // column offset. Find the last scheme anchor at or before the click —
292
+ // a run like `https://a.com,https://b.com` has two, and clicking the
293
+ // second should return the second URL, not the greedy match of both.
294
+ const clickIdx = c - lo;
295
+ const schemeRe = /(?:https?|file):\/\//g;
296
+ let urlStart = -1;
297
+ let urlEnd = token.length;
298
+ for (let m; (m = schemeRe.exec(token));) {
299
+ if (m.index > clickIdx) {
300
+ urlEnd = m.index;
301
+ break;
302
+ }
303
+ urlStart = m.index;
304
+ }
305
+ if (urlStart < 0)
306
+ return undefined;
307
+ let url = token.slice(urlStart, urlEnd);
308
+ // Strip trailing sentence punctuation. For closers () ] }, only strip
309
+ // if unbalanced — `/wiki/Foo_(bar)` keeps `)`, `/arr[0]` keeps `]`.
310
+ const OPENER = { ')': '(', ']': '[', '}': '{' };
311
+ while (url.length > 0) {
312
+ const last = url.at(-1);
313
+ if ('.,;:!?'.includes(last)) {
314
+ url = url.slice(0, -1);
315
+ continue;
316
+ }
317
+ const opener = OPENER[last];
318
+ if (!opener)
319
+ break;
320
+ let opens = 0;
321
+ let closes = 0;
322
+ for (let i = 0; i < url.length; i++) {
323
+ const ch = url.charAt(i);
324
+ if (ch === opener)
325
+ opens++;
326
+ else if (ch === last)
327
+ closes++;
328
+ }
329
+ if (closes > opens)
330
+ url = url.slice(0, -1);
331
+ else
332
+ break;
333
+ }
334
+ // urlStart already guarantees click >= URL start; check right edge.
335
+ if (clickIdx >= urlStart + url.length)
336
+ return undefined;
337
+ return url;
338
+ }
339
+ /**
340
+ * Select the entire row. Sets isDragging=true and anchorSpan so a
341
+ * subsequent drag extends the selection line-by-line. The anchor/focus
342
+ * span from col 0 to width-1; getSelectedText handles noSelect skipping
343
+ * and trailing-whitespace trimming so the copied text is just the visible
344
+ * line content.
345
+ * @param s - the selection state to mutate.
346
+ * @param screen - the screen buffer providing the row width.
347
+ * @param row - the row to select.
348
+ */
349
+ export function selectLineAt(s, screen, row) {
350
+ if (row < 0 || row >= screen.height)
351
+ return;
352
+ const lo = { col: 0, row };
353
+ const hi = { col: screen.width - 1, row };
354
+ s.anchor = lo;
355
+ s.focus = hi;
356
+ s.isDragging = true;
357
+ s.anchorSpan = { lo, hi, kind: 'line' };
358
+ }
359
+ /**
360
+ * Extend a word/line-mode selection to the word/line at (col, row). The
361
+ * anchor span (the original multi-clicked word/line) stays selected; the
362
+ * selection grows from that span to the word/line at the current mouse
363
+ * position. Word mode falls back to the raw cell when the mouse is over a
364
+ * noSelect cell or out of bounds, so dragging into gutters still extends.
365
+ * @param s - the selection state to mutate.
366
+ * @param screen - the screen buffer to scan for word bounds.
367
+ * @param col - current mouse column.
368
+ * @param row - current mouse row.
369
+ */
370
+ export function extendSelection(s, screen, col, row) {
371
+ if (!s.isDragging || !s.anchorSpan)
372
+ return;
373
+ const span = s.anchorSpan;
374
+ let mLo;
375
+ let mHi;
376
+ if (span.kind === 'word') {
377
+ const b = wordBoundsAt(screen, col, row);
378
+ mLo = { col: b ? b.lo : col, row };
379
+ mHi = { col: b ? b.hi : col, row };
380
+ }
381
+ else {
382
+ const r = clamp(row, 0, screen.height - 1);
383
+ mLo = { col: 0, row: r };
384
+ mHi = { col: screen.width - 1, row: r };
385
+ }
386
+ if (comparePoints(mHi, span.lo) < 0) {
387
+ // Mouse target ends before anchor span: extend backward.
388
+ s.anchor = span.hi;
389
+ s.focus = mLo;
390
+ }
391
+ else if (comparePoints(mLo, span.hi) > 0) {
392
+ // Mouse target starts after anchor span: extend forward.
393
+ s.anchor = span.lo;
394
+ s.focus = mHi;
395
+ }
396
+ else {
397
+ // Mouse overlaps the anchor span: just select the anchor span.
398
+ s.anchor = span.lo;
399
+ s.focus = span.hi;
400
+ }
401
+ }
402
+ /**
403
+ * Set focus to (col, row) for keyboard selection extension (shift+arrow).
404
+ * Anchor stays fixed; selection grows or shrinks depending on where focus
405
+ * moves relative to anchor. Drops to char mode (clears anchorSpan) —
406
+ * native macOS does this too: shift+arrow after a double-click word-select
407
+ * extends char-by-char from the word edge, not word-by-word. Scrolled-off
408
+ * accumulators are preserved: keyboard-extending a drag-scrolled selection
409
+ * keeps the off-screen rows. Caller supplies coords already clamped/wrapped.
410
+ * @param s - the selection state to mutate.
411
+ * @param col - focus column.
412
+ * @param row - focus row.
413
+ */
414
+ export function moveFocus(s, col, row) {
415
+ if (!s.focus)
416
+ return;
417
+ s.anchorSpan = null;
418
+ s.focus = { col, row };
419
+ // Explicit user repositioning — any stale virtual focus (from a prior
420
+ // shiftSelection clamp) no longer reflects intent. Anchor stays put so
421
+ // virtualAnchorRow is still valid for its own round-trip.
422
+ s.virtualFocusRow = undefined;
423
+ }
424
+ /**
425
+ * Shift anchor AND focus by dRow, clamped to [minRow, maxRow]. Used for
426
+ * keyboard scroll (PgUp/PgDn/ctrl+u/d/b/f): the whole selection must track
427
+ * the content, unlike drag-to-scroll where focus stays at the mouse. Any
428
+ * point that hits a clamp bound gets its col reset to the full-width edge —
429
+ * its original content scrolled off-screen and was captured by
430
+ * captureScrolledRows, so the col constraint was already consumed. Keeping
431
+ * it would truncate the NEW content now at that screen row. Clamp col is 0
432
+ * for dRow<0 (scrolling down, top leaves, 'above' semantics) or width-1 for
433
+ * dRow>0 (scrolling up, bottom leaves, 'below' semantics).
434
+ *
435
+ * If both ends overshoot the SAME viewport edge (select text → Home/End/g/G
436
+ * jumps far enough that both are out of view), clear — otherwise both clamp
437
+ * to the same corner cell and a ghost 1-cell highlight lingers, and
438
+ * getSelectedText returns one unrelated char from that corner. Symmetric
439
+ * with shiftSelectionForFollow's top-edge check, but bidirectional: keyboard
440
+ * scroll can jump either way.
441
+ * @param s - the selection state to mutate.
442
+ * @param dRow - signed row offset to shift by.
443
+ * @param minRow - lowest allowed row (viewport top).
444
+ * @param maxRow - highest allowed row (viewport bottom).
445
+ * @param width - screen width, used for the clamp-edge column.
446
+ */
447
+ export function shiftSelection(s, dRow, minRow, maxRow, width) {
448
+ if (!s.anchor || !s.focus)
449
+ return;
450
+ // Virtual rows track pre-clamp positions so reverse scrolls restore
451
+ // correctly. Without this, clamp(5→0) + shift(+10) = 10, not the true 5,
452
+ // and scrolledOffAbove stays stale (highlight ≠ copy).
453
+ const vAnchor = (s.virtualAnchorRow ?? s.anchor.row) + dRow;
454
+ const vFocus = (s.virtualFocusRow ?? s.focus.row) + dRow;
455
+ if ((vAnchor < minRow && vFocus < minRow) ||
456
+ (vAnchor > maxRow && vFocus > maxRow)) {
457
+ clearSelection(s);
458
+ return;
459
+ }
460
+ // Debt = how far the nearer endpoint overshoots each edge. When debt
461
+ // shrinks (reverse scroll), those rows are back on-screen — pop from
462
+ // the accumulator so getSelectedText doesn't double-count them.
463
+ const oldMin = Math.min(s.virtualAnchorRow ?? s.anchor.row, s.virtualFocusRow ?? s.focus.row);
464
+ const oldMax = Math.max(s.virtualAnchorRow ?? s.anchor.row, s.virtualFocusRow ?? s.focus.row);
465
+ const oldAboveDebt = Math.max(0, minRow - oldMin);
466
+ const oldBelowDebt = Math.max(0, oldMax - maxRow);
467
+ const newAboveDebt = Math.max(0, minRow - Math.min(vAnchor, vFocus));
468
+ const newBelowDebt = Math.max(0, Math.max(vAnchor, vFocus) - maxRow);
469
+ if (newAboveDebt < oldAboveDebt) {
470
+ // scrolledOffAbove pushes newest at the end (closest to on-screen).
471
+ const drop = oldAboveDebt - newAboveDebt;
472
+ s.scrolledOffAbove.length -= drop;
473
+ s.scrolledOffAboveSW.length = s.scrolledOffAbove.length;
474
+ }
475
+ if (newBelowDebt < oldBelowDebt) {
476
+ // scrolledOffBelow unshifts newest at the front (closest to on-screen).
477
+ const drop = oldBelowDebt - newBelowDebt;
478
+ s.scrolledOffBelow.splice(0, drop);
479
+ s.scrolledOffBelowSW.splice(0, drop);
480
+ }
481
+ // Invariant: accumulator length ≤ debt. If the accumulator exceeds debt,
482
+ // the excess is stale — e.g., moveFocus cleared virtualFocusRow without
483
+ // trimming the accumulator, orphaning entries the pop above can never
484
+ // reach because oldDebt was ALREADY 0. Truncate to debt (keeping the
485
+ // newest = closest-to-on-screen entries). Check newDebt (not oldDebt):
486
+ // captureScrolledRows runs BEFORE this shift in the real flow (ink.tsx),
487
+ // so at entry the accumulator is populated but oldDebt is still 0 —
488
+ // that's the normal establish-debt path, not stale.
489
+ if (s.scrolledOffAbove.length > newAboveDebt) {
490
+ // Above pushes newest at END → keep END.
491
+ s.scrolledOffAbove =
492
+ newAboveDebt > 0 ? s.scrolledOffAbove.slice(-newAboveDebt) : [];
493
+ s.scrolledOffAboveSW =
494
+ newAboveDebt > 0 ? s.scrolledOffAboveSW.slice(-newAboveDebt) : [];
495
+ }
496
+ if (s.scrolledOffBelow.length > newBelowDebt) {
497
+ // Below unshifts newest at FRONT → keep FRONT.
498
+ s.scrolledOffBelow = s.scrolledOffBelow.slice(0, newBelowDebt);
499
+ s.scrolledOffBelowSW = s.scrolledOffBelowSW.slice(0, newBelowDebt);
500
+ }
501
+ // Clamp col depends on which EDGE (not dRow direction): virtual tracking
502
+ // means a top-clamped point can stay top-clamped during a dRow>0 reverse
503
+ // shift — dRow-based clampCol would give it the bottom col.
504
+ const shift = (p, vRow) => {
505
+ if (vRow < minRow)
506
+ return { col: 0, row: minRow };
507
+ if (vRow > maxRow)
508
+ return { col: width - 1, row: maxRow };
509
+ return { col: p.col, row: vRow };
510
+ };
511
+ s.anchor = shift(s.anchor, vAnchor);
512
+ s.focus = shift(s.focus, vFocus);
513
+ s.virtualAnchorRow =
514
+ vAnchor < minRow || vAnchor > maxRow ? vAnchor : undefined;
515
+ s.virtualFocusRow = vFocus < minRow || vFocus > maxRow ? vFocus : undefined;
516
+ // anchorSpan not virtual-tracked: it's for word/line extend-on-drag,
517
+ // irrelevant to the keyboard-scroll round-trip case.
518
+ if (s.anchorSpan) {
519
+ const sp = (p) => {
520
+ const r = p.row + dRow;
521
+ if (r < minRow)
522
+ return { col: 0, row: minRow };
523
+ if (r > maxRow)
524
+ return { col: width - 1, row: maxRow };
525
+ return { col: p.col, row: r };
526
+ };
527
+ s.anchorSpan = {
528
+ lo: sp(s.anchorSpan.lo),
529
+ hi: sp(s.anchorSpan.hi),
530
+ kind: s.anchorSpan.kind,
531
+ };
532
+ }
533
+ }
534
+ /**
535
+ * Shift the anchor row by dRow, clamped to [minRow, maxRow]. Used during
536
+ * drag-to-scroll: when the ScrollBox scrolls by N rows, the content that
537
+ * was under the anchor is now at a different viewport row, so the anchor
538
+ * must follow it. Focus is left unchanged (it stays at the mouse position).
539
+ * @param s - the selection state to mutate.
540
+ * @param dRow - signed row offset to shift by.
541
+ * @param minRow - lowest allowed row (viewport top).
542
+ * @param maxRow - highest allowed row (viewport bottom).
543
+ */
544
+ export function shiftAnchor(s, dRow, minRow, maxRow) {
545
+ if (!s.anchor)
546
+ return;
547
+ // Same virtual-row tracking as shiftSelection/shiftSelectionForFollow: the
548
+ // drag→follow transition hands off to shiftSelectionForFollow, which reads
549
+ // (virtualAnchorRow ?? anchor.row). Without this, drag-phase clamping
550
+ // leaves virtual undefined → follow initializes from the already-clamped
551
+ // row, under-counting total drift → shiftSelection's invariant-restore
552
+ // prematurely clears valid drag-phase accumulator entries.
553
+ const raw = (s.virtualAnchorRow ?? s.anchor.row) + dRow;
554
+ s.anchor = { col: s.anchor.col, row: clamp(raw, minRow, maxRow) };
555
+ s.virtualAnchorRow = raw < minRow || raw > maxRow ? raw : undefined;
556
+ // anchorSpan not virtual-tracked (word/line extend, irrelevant to
557
+ // keyboard-scroll round-trip) — plain clamp from current row.
558
+ if (s.anchorSpan) {
559
+ const shift = (p) => ({
560
+ col: p.col,
561
+ row: clamp(p.row + dRow, minRow, maxRow),
562
+ });
563
+ s.anchorSpan = {
564
+ lo: shift(s.anchorSpan.lo),
565
+ hi: shift(s.anchorSpan.hi),
566
+ kind: s.anchorSpan.kind,
567
+ };
568
+ }
569
+ }
570
+ /**
571
+ * Shift the whole selection (anchor + focus + anchorSpan) by dRow, clamped
572
+ * to [minRow, maxRow]. Used when sticky/auto-follow scrolls the ScrollBox
573
+ * while a selection is active — native terminal behavior is for the
574
+ * highlight to walk up the screen with the text (not stay at the same
575
+ * screen position).
576
+ *
577
+ * Differs from shiftAnchor: during drag-to-scroll, focus tracks the live
578
+ * mouse position and only anchor follows the text. During streaming-follow,
579
+ * the selection is text-anchored at both ends — both must move. The
580
+ * isDragging check in ink.tsx picks which shift to apply.
581
+ *
582
+ * If both ends would shift strictly BELOW minRow (unclamped), the selected
583
+ * text has scrolled entirely off the top. Clear it — otherwise a single
584
+ * inverted cell lingers at the viewport top as a ghost (native terminals
585
+ * drop the selection when it leaves scrollback). Landing AT minRow is
586
+ * still valid: that cell holds the correct text. Returns true if the
587
+ * selection was cleared so the caller can notify React-land subscribers
588
+ * (useHasSelection) — the caller is inside onRender so it can't use
589
+ * notifySelectionChange (recursion), must fire listeners directly.
590
+ * @param s - the selection state to mutate.
591
+ * @param dRow - signed row offset to shift by.
592
+ * @param minRow - lowest allowed row (viewport top).
593
+ * @param maxRow - highest allowed row (viewport bottom).
594
+ * @returns true when the selection was cleared because it scrolled
595
+ * entirely off the top, false otherwise.
596
+ */
597
+ export function shiftSelectionForFollow(s, dRow, minRow, maxRow) {
598
+ if (!s.anchor)
599
+ return false;
600
+ // Mirror shiftSelection: compute raw (unclamped) positions from virtual
601
+ // if set, else current. This handles BOTH the update path (virtual already
602
+ // set from a prior keyboard scroll) AND the initialize path (first clamp
603
+ // happens HERE via follow-scroll, no prior keyboard scroll). Without the
604
+ // initialize path, follow-scroll-first leaves virtual undefined even
605
+ // though the clamp below occurred → a later PgUp computes debt from the
606
+ // clamped row instead of the true pre-clamp row and never pops the
607
+ // accumulator — getSelectedText double-counts the off-screen rows.
608
+ const rawAnchor = (s.virtualAnchorRow ?? s.anchor.row) + dRow;
609
+ const rawFocus = s.focus
610
+ ? (s.virtualFocusRow ?? s.focus.row) + dRow
611
+ : undefined;
612
+ if (rawAnchor < minRow && rawFocus !== undefined && rawFocus < minRow) {
613
+ clearSelection(s);
614
+ return true;
615
+ }
616
+ // Clamp from raw, not p.row+dRow — so a virtual position coming back
617
+ // in-bounds lands at the TRUE position, not the stale clamped one.
618
+ s.anchor = { col: s.anchor.col, row: clamp(rawAnchor, minRow, maxRow) };
619
+ if (s.focus && rawFocus !== undefined) {
620
+ s.focus = { col: s.focus.col, row: clamp(rawFocus, minRow, maxRow) };
621
+ }
622
+ s.virtualAnchorRow =
623
+ rawAnchor < minRow || rawAnchor > maxRow ? rawAnchor : undefined;
624
+ s.virtualFocusRow =
625
+ rawFocus !== undefined && (rawFocus < minRow || rawFocus > maxRow)
626
+ ? rawFocus
627
+ : undefined;
628
+ // anchorSpan not virtual-tracked (word/line extend, irrelevant to
629
+ // keyboard-scroll round-trip) — plain clamp from current row.
630
+ if (s.anchorSpan) {
631
+ const shift = (p) => ({
632
+ col: p.col,
633
+ row: clamp(p.row + dRow, minRow, maxRow),
634
+ });
635
+ s.anchorSpan = {
636
+ lo: shift(s.anchorSpan.lo),
637
+ hi: shift(s.anchorSpan.hi),
638
+ kind: s.anchorSpan.kind,
639
+ };
640
+ }
641
+ return false;
642
+ }
643
+ /**
644
+ * True when a selection is active, meaning both anchor and focus are set.
645
+ * @param s - the selection state to inspect.
646
+ * @returns true when a selection is active.
647
+ */
648
+ export function hasSelection(s) {
649
+ return s.anchor !== null && s.focus !== null;
650
+ }
651
+ /**
652
+ * Normalized selection bounds: start is always before end in reading order.
653
+ * Returns null if no active selection.
654
+ * @param s - the selection state to inspect.
655
+ * @returns the normalized start/end cells, or null when there is no
656
+ * active selection.
657
+ */
658
+ export function selectionBounds(s) {
659
+ if (!s.anchor || !s.focus)
660
+ return null;
661
+ return comparePoints(s.anchor, s.focus) <= 0
662
+ ? { start: s.anchor, end: s.focus }
663
+ : { start: s.focus, end: s.anchor };
664
+ }
665
+ /**
666
+ * Check if a cell at (col, row) is within the current selection range.
667
+ * Used by the renderer to apply inverse style.
668
+ * @param s - the selection state to inspect.
669
+ * @param col - cell column.
670
+ * @param row - cell row.
671
+ * @returns true when the cell lies inside the selection range.
672
+ */
673
+ export function isCellSelected(s, col, row) {
674
+ const b = selectionBounds(s);
675
+ if (!b)
676
+ return false;
677
+ const { start, end } = b;
678
+ if (row < start.row || row > end.row)
679
+ return false;
680
+ if (row === start.row && col < start.col)
681
+ return false;
682
+ if (row === end.row && col > end.col)
683
+ return false;
684
+ return true;
685
+ }
686
+ /** Extract text from one screen row. When the next row is a soft-wrap
687
+ * continuation (screen.softWrap[row+1]>0), clamp to that content-end
688
+ * column and skip the trailing trim so the word-separator space survives
689
+ * the join. See Screen.softWrap for why the clamp is necessary. */
690
+ function extractRowText(screen, row, colStart, colEnd) {
691
+ const noSelect = screen.noSelect;
692
+ const rowOff = row * screen.width;
693
+ const contentEnd = row + 1 < screen.height ? screen.softWrap[row + 1] : 0;
694
+ const lastCol = contentEnd > 0 ? Math.min(colEnd, contentEnd - 1) : colEnd;
695
+ let line = '';
696
+ for (let col = colStart; col <= lastCol; col++) {
697
+ // Skip cells marked noSelect (gutters, line numbers, diff sigils).
698
+ // Check before cellAt to avoid the decode cost for excluded cells.
699
+ if (noSelect[rowOff + col] === 1)
700
+ continue;
701
+ const cell = cellAt(screen, col, row);
702
+ if (!cell)
703
+ continue;
704
+ // Skip spacer tails (second half of wide chars) — the head already
705
+ // contains the full grapheme. SpacerHead is a blank at line-end.
706
+ if (cell.width === 2 /* CellWidth.SpacerTail */ ||
707
+ cell.width === 3 /* CellWidth.SpacerHead */) {
708
+ continue;
709
+ }
710
+ line += cell.char;
711
+ }
712
+ return contentEnd > 0 ? line : line.replace(/\s+$/, '');
713
+ }
714
+ /** Accumulator for selected text that merges soft-wrapped rows back
715
+ * into logical lines. push(text, sw) appends a newline before text
716
+ * only when sw=false (i.e. the row starts a new logical line). Rows
717
+ * with sw=true are concatenated onto the previous row. */
718
+ function joinRows(lines, text, sw) {
719
+ if (sw && lines.length > 0) {
720
+ lines[lines.length - 1] += text;
721
+ }
722
+ else {
723
+ lines.push(text);
724
+ }
725
+ }
726
+ /**
727
+ * Extract text from the screen buffer within the selection range.
728
+ * Rows are joined with newlines unless the screen's softWrap bitmap
729
+ * marks a row as a word-wrap continuation — those rows are concatenated
730
+ * onto the previous row so the copied text matches the logical source
731
+ * line, not the visual wrapped layout. Trailing whitespace on the last
732
+ * fragment of each logical line is trimmed. Wide-char spacer cells are
733
+ * skipped. Rows that scrolled out of the viewport during drag-to-scroll
734
+ * are joined back in from the scrolledOffAbove/Below accumulators along
735
+ * with their captured softWrap bits.
736
+ * @param s - the selection state to read.
737
+ * @param screen - the screen buffer to extract from.
738
+ * @returns the selected text, or an empty string when no selection is
739
+ * active.
740
+ */
741
+ export function getSelectedText(s, screen) {
742
+ const b = selectionBounds(s);
743
+ if (!b)
744
+ return '';
745
+ const { start, end } = b;
746
+ const sw = screen.softWrap;
747
+ const lines = [];
748
+ for (let i = 0; i < s.scrolledOffAbove.length; i++) {
749
+ joinRows(lines, s.scrolledOffAbove[i], s.scrolledOffAboveSW[i]);
750
+ }
751
+ for (let row = start.row; row <= end.row; row++) {
752
+ const rowStart = row === start.row ? start.col : 0;
753
+ const rowEnd = row === end.row ? end.col : screen.width - 1;
754
+ joinRows(lines, extractRowText(screen, row, rowStart, rowEnd), sw[row] > 0);
755
+ }
756
+ for (let i = 0; i < s.scrolledOffBelow.length; i++) {
757
+ joinRows(lines, s.scrolledOffBelow[i], s.scrolledOffBelowSW[i]);
758
+ }
759
+ return lines.join('\n');
760
+ }
761
+ /**
762
+ * Capture text from rows about to scroll out of the viewport during
763
+ * drag-to-scroll, BEFORE scrollBy overwrites them. Only the rows that
764
+ * intersect the selection are captured, using the selection's col bounds
765
+ * for the anchor-side boundary row. After capturing the anchor row, the
766
+ * anchor.col AND anchorSpan cols are reset to the full-width boundary so
767
+ * subsequent captures and the final getSelectedText don't re-apply a stale
768
+ * col constraint to content that's no longer under the original anchor.
769
+ * Both span cols are reset (not just the near side): after a blocked
770
+ * reversal the drag can flip direction, and extendSelection then reads the
771
+ * OPPOSITE span side — which would otherwise still hold the original word
772
+ * boundary and truncate one subsequently-captured row.
773
+ *
774
+ * side='above': rows scrolling out the top (dragging down, anchor=start).
775
+ * side='below': rows scrolling out the bottom (dragging up, anchor=end).
776
+ * @param s - the selection state to update.
777
+ * @param screen - the screen buffer to read rows from.
778
+ * @param firstRow - first viewport row that is about to scroll out.
779
+ * @param lastRow - last viewport row that is about to scroll out.
780
+ * @param side - which viewport edge the rows scroll out of.
781
+ */
782
+ export function captureScrolledRows(s, screen, firstRow, lastRow, side) {
783
+ const b = selectionBounds(s);
784
+ if (!b || firstRow > lastRow)
785
+ return;
786
+ const { start, end } = b;
787
+ // Intersect [firstRow, lastRow] with [start.row, end.row]. Rows outside
788
+ // the selection aren't captured — they weren't selected.
789
+ const lo = Math.max(firstRow, start.row);
790
+ const hi = Math.min(lastRow, end.row);
791
+ if (lo > hi)
792
+ return;
793
+ const width = screen.width;
794
+ const sw = screen.softWrap;
795
+ const captured = [];
796
+ const capturedSW = [];
797
+ for (let row = lo; row <= hi; row++) {
798
+ const colStart = row === start.row ? start.col : 0;
799
+ const colEnd = row === end.row ? end.col : width - 1;
800
+ captured.push(extractRowText(screen, row, colStart, colEnd));
801
+ capturedSW.push(sw[row] > 0);
802
+ }
803
+ if (side === 'above') {
804
+ // Newest rows go at the bottom of the above-accumulator (closest to
805
+ // the on-screen content in reading order).
806
+ s.scrolledOffAbove.push(...captured);
807
+ s.scrolledOffAboveSW.push(...capturedSW);
808
+ // We just captured the top of the selection. The anchor (=start when
809
+ // dragging down) is now pointing at content that will scroll out; its
810
+ // col constraint was applied to the captured row. Reset to col 0 so
811
+ // the NEXT tick and the final getSelectedText read the full row.
812
+ if (s.anchor && s.anchor.row === start.row && lo === start.row) {
813
+ s.anchor = { col: 0, row: s.anchor.row };
814
+ if (s.anchorSpan) {
815
+ s.anchorSpan = {
816
+ kind: s.anchorSpan.kind,
817
+ lo: { col: 0, row: s.anchorSpan.lo.row },
818
+ hi: { col: width - 1, row: s.anchorSpan.hi.row },
819
+ };
820
+ }
821
+ }
822
+ }
823
+ else {
824
+ // Newest rows go at the TOP of the below-accumulator — they're
825
+ // closest to the on-screen content.
826
+ s.scrolledOffBelow.unshift(...captured);
827
+ s.scrolledOffBelowSW.unshift(...capturedSW);
828
+ if (s.anchor && s.anchor.row === end.row && hi === end.row) {
829
+ s.anchor = { col: width - 1, row: s.anchor.row };
830
+ if (s.anchorSpan) {
831
+ s.anchorSpan = {
832
+ kind: s.anchorSpan.kind,
833
+ lo: { col: 0, row: s.anchorSpan.lo.row },
834
+ hi: { col: width - 1, row: s.anchorSpan.hi.row },
835
+ };
836
+ }
837
+ }
838
+ }
839
+ }
840
+ /**
841
+ * Apply the selection overlay directly to the screen buffer by changing
842
+ * the style of every cell in the selection range. Called after the
843
+ * renderer produces the Frame but before the diff — the normal diffEach
844
+ * then picks up the restyled cells as ordinary changes, so LogUpdate
845
+ * stays a pure diff engine with no selection awareness.
846
+ *
847
+ * Uses a SOLID selection background (theme-provided via StylePool.
848
+ * setSelectionBg) that REPLACES each cell's bg while PRESERVING its fg —
849
+ * matches native terminal selection. Previously SGR-7 inverse (swapped
850
+ * fg/bg per cell), which fragmented badly over syntax-highlighted text:
851
+ * every distinct fg color became a different bg stripe.
852
+ *
853
+ * Uses StylePool caches so on drag the only work per cell is a Map
854
+ * lookup + packed-int write.
855
+ * @param screen - the screen buffer to restyle.
856
+ * @param selection - the selection whose range to highlight.
857
+ * @param stylePool - the style pool providing the selection background.
858
+ */
859
+ export function applySelectionOverlay(screen, selection, stylePool) {
860
+ const b = selectionBounds(selection);
861
+ if (!b)
862
+ return;
863
+ const { start, end } = b;
864
+ const width = screen.width;
865
+ const noSelect = screen.noSelect;
866
+ for (let row = start.row; row <= end.row && row < screen.height; row++) {
867
+ const colStart = row === start.row ? start.col : 0;
868
+ const colEnd = row === end.row ? Math.min(end.col, width - 1) : width - 1;
869
+ const rowOff = row * width;
870
+ for (let col = colStart; col <= colEnd; col++) {
871
+ const idx = rowOff + col;
872
+ // Skip noSelect cells — gutters stay visually unchanged so it's
873
+ // clear they're not part of the copy. Surrounding selectable cells
874
+ // still highlight so the selection extent remains visible.
875
+ if (noSelect[idx] === 1)
876
+ continue;
877
+ const cell = cellAtIndex(screen, idx);
878
+ setCellStyleId(screen, col, row, stylePool.withSelectionBg(cell.styleId));
879
+ }
880
+ }
881
+ }