@react-x11/components 0.1.0 → 0.2.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 (783) hide show
  1. package/README.md +979 -21
  2. package/dist/ansi/cast.d.ts +48 -0
  3. package/dist/ansi/cast.d.ts.map +1 -0
  4. package/dist/ansi/cast.js +135 -0
  5. package/dist/ansi/cast.js.map +1 -0
  6. package/dist/ansi/index.d.ts +10 -0
  7. package/dist/ansi/index.d.ts.map +1 -0
  8. package/dist/ansi/index.js +27 -0
  9. package/dist/ansi/index.js.map +1 -0
  10. package/dist/ansi/palette.d.ts +79 -0
  11. package/dist/ansi/palette.d.ts.map +1 -0
  12. package/dist/ansi/palette.js +155 -0
  13. package/dist/ansi/palette.js.map +1 -0
  14. package/dist/ansi/parse.d.ts +114 -0
  15. package/dist/ansi/parse.d.ts.map +1 -0
  16. package/dist/ansi/parse.js +687 -0
  17. package/dist/ansi/parse.js.map +1 -0
  18. package/dist/ansi/sgr.d.ts +87 -0
  19. package/dist/ansi/sgr.d.ts.map +1 -0
  20. package/dist/ansi/sgr.js +302 -0
  21. package/dist/ansi/sgr.js.map +1 -0
  22. package/dist/ansi/utf8.d.ts +15 -0
  23. package/dist/ansi/utf8.d.ts.map +1 -0
  24. package/dist/ansi/utf8.js +82 -0
  25. package/dist/ansi/utf8.js.map +1 -0
  26. package/dist/calendar/Calendar.d.ts +136 -0
  27. package/dist/calendar/Calendar.d.ts.map +1 -0
  28. package/dist/calendar/Calendar.js +460 -0
  29. package/dist/calendar/Calendar.js.map +1 -0
  30. package/dist/calendar/DatePicker.d.ts +52 -0
  31. package/dist/calendar/DatePicker.d.ts.map +1 -0
  32. package/dist/calendar/DatePicker.js +257 -0
  33. package/dist/calendar/DatePicker.js.map +1 -0
  34. package/dist/calendar/dates.d.ts +90 -0
  35. package/dist/calendar/dates.d.ts.map +1 -0
  36. package/dist/calendar/dates.js +236 -0
  37. package/dist/calendar/dates.js.map +1 -0
  38. package/dist/calendar/index.d.ts +8 -0
  39. package/dist/calendar/index.d.ts.map +1 -0
  40. package/dist/calendar/index.js +15 -0
  41. package/dist/calendar/index.js.map +1 -0
  42. package/dist/charts/components.d.ts +141 -0
  43. package/dist/charts/components.d.ts.map +1 -0
  44. package/dist/charts/components.js +580 -0
  45. package/dist/charts/components.js.map +1 -0
  46. package/dist/charts/data.d.ts +198 -0
  47. package/dist/charts/data.d.ts.map +1 -0
  48. package/dist/charts/data.js +616 -0
  49. package/dist/charts/data.js.map +1 -0
  50. package/dist/charts/hx.d.ts +21 -0
  51. package/dist/charts/hx.d.ts.map +1 -0
  52. package/dist/charts/hx.js +15 -0
  53. package/dist/charts/hx.js.map +1 -0
  54. package/dist/charts/index.d.ts +27 -0
  55. package/dist/charts/index.d.ts.map +1 -0
  56. package/dist/charts/index.js +38 -0
  57. package/dist/charts/index.js.map +1 -0
  58. package/dist/charts/node.d.ts +96 -0
  59. package/dist/charts/node.d.ts.map +1 -0
  60. package/dist/charts/node.js +720 -0
  61. package/dist/charts/node.js.map +1 -0
  62. package/dist/charts/render.d.ts +153 -0
  63. package/dist/charts/render.d.ts.map +1 -0
  64. package/dist/charts/render.js +762 -0
  65. package/dist/charts/render.js.map +1 -0
  66. package/dist/charts/scale.d.ts +42 -0
  67. package/dist/charts/scale.d.ts.map +1 -0
  68. package/dist/charts/scale.js +173 -0
  69. package/dist/charts/scale.js.map +1 -0
  70. package/dist/charts/spec.d.ts +91 -0
  71. package/dist/charts/spec.d.ts.map +1 -0
  72. package/dist/charts/spec.js +33 -0
  73. package/dist/charts/spec.js.map +1 -0
  74. package/dist/charts/timers.d.ts +6 -0
  75. package/dist/charts/timers.d.ts.map +1 -0
  76. package/dist/charts/timers.js +14 -0
  77. package/dist/charts/timers.js.map +1 -0
  78. package/dist/code/hx.d.ts +24 -0
  79. package/dist/code/hx.d.ts.map +1 -0
  80. package/dist/code/hx.js +25 -0
  81. package/dist/code/hx.js.map +1 -0
  82. package/dist/code/index.d.ts +45 -0
  83. package/dist/code/index.d.ts.map +1 -0
  84. package/dist/code/index.js +93 -0
  85. package/dist/code/index.js.map +1 -0
  86. package/dist/code-editor/completion.d.ts +26 -0
  87. package/dist/code-editor/completion.d.ts.map +1 -0
  88. package/dist/code-editor/completion.js +129 -0
  89. package/dist/code-editor/completion.js.map +1 -0
  90. package/dist/code-editor/doc.d.ts +35 -0
  91. package/dist/code-editor/doc.d.ts.map +1 -0
  92. package/dist/code-editor/doc.js +162 -0
  93. package/dist/code-editor/doc.js.map +1 -0
  94. package/dist/code-editor/hx.d.ts +13 -0
  95. package/dist/code-editor/hx.d.ts.map +1 -0
  96. package/dist/code-editor/hx.js +10 -0
  97. package/dist/code-editor/hx.js.map +1 -0
  98. package/dist/code-editor/index.d.ts +88 -0
  99. package/dist/code-editor/index.d.ts.map +1 -0
  100. package/dist/code-editor/index.js +401 -0
  101. package/dist/code-editor/index.js.map +1 -0
  102. package/dist/code-editor/node.d.ts +343 -0
  103. package/dist/code-editor/node.d.ts.map +1 -0
  104. package/dist/code-editor/node.js +1521 -0
  105. package/dist/code-editor/node.js.map +1 -0
  106. package/dist/code-language/hljs.d.ts +47 -0
  107. package/dist/code-language/hljs.d.ts.map +1 -0
  108. package/dist/code-language/hljs.js +211 -0
  109. package/dist/code-language/hljs.js.map +1 -0
  110. package/dist/code-language/index.d.ts +22 -0
  111. package/dist/code-language/index.d.ts.map +1 -0
  112. package/dist/code-language/index.js +20 -0
  113. package/dist/code-language/index.js.map +1 -0
  114. package/dist/code-language/languages/glsl.d.ts +4 -0
  115. package/dist/code-language/languages/glsl.d.ts.map +1 -0
  116. package/dist/code-language/languages/glsl.js +99 -0
  117. package/dist/code-language/languages/glsl.js.map +1 -0
  118. package/dist/code-language/languages/javascript.d.ts +10 -0
  119. package/dist/code-language/languages/javascript.d.ts.map +1 -0
  120. package/dist/code-language/languages/javascript.js +360 -0
  121. package/dist/code-language/languages/javascript.js.map +1 -0
  122. package/dist/code-language/languages/json.d.ts +4 -0
  123. package/dist/code-language/languages/json.d.ts.map +1 -0
  124. package/dist/code-language/languages/json.js +68 -0
  125. package/dist/code-language/languages/json.js.map +1 -0
  126. package/dist/code-language/languages/shell.d.ts +4 -0
  127. package/dist/code-language/languages/shell.d.ts.map +1 -0
  128. package/dist/code-language/languages/shell.js +189 -0
  129. package/dist/code-language/languages/shell.js.map +1 -0
  130. package/dist/code-language/languages/sql.d.ts +11 -0
  131. package/dist/code-language/languages/sql.d.ts.map +1 -0
  132. package/dist/code-language/languages/sql.js +133 -0
  133. package/dist/code-language/languages/sql.js.map +1 -0
  134. package/dist/code-language/lezer.d.ts +24 -0
  135. package/dist/code-language/lezer.d.ts.map +1 -0
  136. package/dist/code-language/lezer.js +148 -0
  137. package/dist/code-language/lezer.js.map +1 -0
  138. package/dist/code-language/registry.d.ts +19 -0
  139. package/dist/code-language/registry.d.ts.map +1 -0
  140. package/dist/code-language/registry.js +56 -0
  141. package/dist/code-language/registry.js.map +1 -0
  142. package/dist/code-language/runs.d.ts +35 -0
  143. package/dist/code-language/runs.d.ts.map +1 -0
  144. package/dist/code-language/runs.js +88 -0
  145. package/dist/code-language/runs.js.map +1 -0
  146. package/dist/code-language/stream.d.ts +70 -0
  147. package/dist/code-language/stream.d.ts.map +1 -0
  148. package/dist/code-language/stream.js +243 -0
  149. package/dist/code-language/stream.js.map +1 -0
  150. package/dist/code-language/textmate.d.ts +27 -0
  151. package/dist/code-language/textmate.d.ts.map +1 -0
  152. package/dist/code-language/textmate.js +86 -0
  153. package/dist/code-language/textmate.js.map +1 -0
  154. package/dist/code-language/theme.d.ts +26 -0
  155. package/dist/code-language/theme.d.ts.map +1 -0
  156. package/dist/code-language/theme.js +108 -0
  157. package/dist/code-language/theme.js.map +1 -0
  158. package/dist/code-language/timers.d.ts +7 -0
  159. package/dist/code-language/timers.d.ts.map +1 -0
  160. package/dist/code-language/timers.js +22 -0
  161. package/dist/code-language/timers.js.map +1 -0
  162. package/dist/code-language/types.d.ts +173 -0
  163. package/dist/code-language/types.d.ts.map +1 -0
  164. package/dist/code-language/types.js +41 -0
  165. package/dist/code-language/types.js.map +1 -0
  166. package/dist/codeblock/index.d.ts +69 -0
  167. package/dist/codeblock/index.d.ts.map +1 -0
  168. package/dist/codeblock/index.js +85 -0
  169. package/dist/codeblock/index.js.map +1 -0
  170. package/dist/color-picker/ColorField.d.ts +39 -0
  171. package/dist/color-picker/ColorField.d.ts.map +1 -0
  172. package/dist/color-picker/ColorField.js +239 -0
  173. package/dist/color-picker/ColorField.js.map +1 -0
  174. package/dist/color-picker/ColorPicker.d.ts +109 -0
  175. package/dist/color-picker/ColorPicker.d.ts.map +1 -0
  176. package/dist/color-picker/ColorPicker.js +757 -0
  177. package/dist/color-picker/ColorPicker.js.map +1 -0
  178. package/dist/color-picker/color.d.ts +105 -0
  179. package/dist/color-picker/color.d.ts.map +1 -0
  180. package/dist/color-picker/color.js +313 -0
  181. package/dist/color-picker/color.js.map +1 -0
  182. package/dist/color-picker/index.d.ts +8 -0
  183. package/dist/color-picker/index.d.ts.map +1 -0
  184. package/dist/color-picker/index.js +19 -0
  185. package/dist/color-picker/index.js.map +1 -0
  186. package/dist/desktop-calendar/eds.d.ts +102 -0
  187. package/dist/desktop-calendar/eds.d.ts.map +1 -0
  188. package/dist/desktop-calendar/eds.js +356 -0
  189. package/dist/desktop-calendar/eds.js.map +1 -0
  190. package/dist/desktop-calendar/ical.d.ts +63 -0
  191. package/dist/desktop-calendar/ical.d.ts.map +1 -0
  192. package/dist/desktop-calendar/ical.js +57 -0
  193. package/dist/desktop-calendar/ical.js.map +1 -0
  194. package/dist/desktop-calendar/index.d.ts +72 -0
  195. package/dist/desktop-calendar/index.d.ts.map +1 -0
  196. package/dist/desktop-calendar/index.js +213 -0
  197. package/dist/desktop-calendar/index.js.map +1 -0
  198. package/dist/embed/client.d.ts +121 -0
  199. package/dist/embed/client.d.ts.map +1 -0
  200. package/dist/embed/client.js +203 -0
  201. package/dist/embed/client.js.map +1 -0
  202. package/dist/embed/host.d.ts +96 -0
  203. package/dist/embed/host.d.ts.map +1 -0
  204. package/dist/embed/host.js +266 -0
  205. package/dist/embed/host.js.map +1 -0
  206. package/dist/embed/index.d.ts +5 -0
  207. package/dist/embed/index.d.ts.map +1 -0
  208. package/dist/embed/index.js +13 -0
  209. package/dist/embed/index.js.map +1 -0
  210. package/dist/embed/timers.d.ts +6 -0
  211. package/dist/embed/timers.d.ts.map +1 -0
  212. package/dist/embed/timers.js +16 -0
  213. package/dist/embed/timers.js.map +1 -0
  214. package/dist/flow/draw.d.ts +45 -0
  215. package/dist/flow/draw.d.ts.map +1 -0
  216. package/dist/flow/draw.js +295 -0
  217. package/dist/flow/draw.js.map +1 -0
  218. package/dist/flow/index.d.ts +64 -0
  219. package/dist/flow/index.d.ts.map +1 -0
  220. package/dist/flow/index.js +306 -0
  221. package/dist/flow/index.js.map +1 -0
  222. package/dist/flow/model.d.ts +158 -0
  223. package/dist/flow/model.d.ts.map +1 -0
  224. package/dist/flow/model.js +566 -0
  225. package/dist/flow/model.js.map +1 -0
  226. package/dist/flow/node.d.ts +429 -0
  227. package/dist/flow/node.d.ts.map +1 -0
  228. package/dist/flow/node.js +3025 -0
  229. package/dist/flow/node.js.map +1 -0
  230. package/dist/flow/paths.d.ts +43 -0
  231. package/dist/flow/paths.d.ts.map +1 -0
  232. package/dist/flow/paths.js +327 -0
  233. package/dist/flow/paths.js.map +1 -0
  234. package/dist/flow/types.d.ts +561 -0
  235. package/dist/flow/types.d.ts.map +1 -0
  236. package/dist/flow/types.js +2 -0
  237. package/dist/flow/types.js.map +1 -0
  238. package/dist/formula/hx.d.ts +24 -0
  239. package/dist/formula/hx.d.ts.map +1 -0
  240. package/dist/formula/hx.js +25 -0
  241. package/dist/formula/hx.js.map +1 -0
  242. package/dist/formula/index.d.ts +74 -0
  243. package/dist/formula/index.d.ts.map +1 -0
  244. package/dist/formula/index.js +155 -0
  245. package/dist/formula/index.js.map +1 -0
  246. package/dist/formula/katex.d.ts +78 -0
  247. package/dist/formula/katex.d.ts.map +1 -0
  248. package/dist/formula/katex.js +130 -0
  249. package/dist/formula/katex.js.map +1 -0
  250. package/dist/formula/layout.d.ts +103 -0
  251. package/dist/formula/layout.d.ts.map +1 -0
  252. package/dist/formula/layout.js +716 -0
  253. package/dist/formula/layout.js.map +1 -0
  254. package/dist/formula/node.d.ts +50 -0
  255. package/dist/formula/node.d.ts.map +1 -0
  256. package/dist/formula/node.js +351 -0
  257. package/dist/formula/node.js.map +1 -0
  258. package/dist/html/controls.d.ts +48 -0
  259. package/dist/html/controls.d.ts.map +1 -0
  260. package/dist/html/controls.js +195 -0
  261. package/dist/html/controls.js.map +1 -0
  262. package/dist/html/css/cascade.d.ts +51 -0
  263. package/dist/html/css/cascade.d.ts.map +1 -0
  264. package/dist/html/css/cascade.js +552 -0
  265. package/dist/html/css/cascade.js.map +1 -0
  266. package/dist/html/css/parse.d.ts +74 -0
  267. package/dist/html/css/parse.d.ts.map +1 -0
  268. package/dist/html/css/parse.js +464 -0
  269. package/dist/html/css/parse.js.map +1 -0
  270. package/dist/html/css/style.d.ts +142 -0
  271. package/dist/html/css/style.d.ts.map +1 -0
  272. package/dist/html/css/style.js +1145 -0
  273. package/dist/html/css/style.js.map +1 -0
  274. package/dist/html/css/ua.d.ts +5 -0
  275. package/dist/html/css/ua.d.ts.map +1 -0
  276. package/dist/html/css/ua.js +171 -0
  277. package/dist/html/css/ua.js.map +1 -0
  278. package/dist/html/css/values.d.ts +78 -0
  279. package/dist/html/css/values.d.ts.map +1 -0
  280. package/dist/html/css/values.js +330 -0
  281. package/dist/html/css/values.js.map +1 -0
  282. package/dist/html/dom.d.ts +112 -0
  283. package/dist/html/dom.d.ts.map +1 -0
  284. package/dist/html/dom.js +374 -0
  285. package/dist/html/dom.js.map +1 -0
  286. package/dist/html/hx.d.ts +24 -0
  287. package/dist/html/hx.d.ts.map +1 -0
  288. package/dist/html/hx.js +29 -0
  289. package/dist/html/hx.js.map +1 -0
  290. package/dist/html/index.d.ts +137 -0
  291. package/dist/html/index.d.ts.map +1 -0
  292. package/dist/html/index.js +339 -0
  293. package/dist/html/index.js.map +1 -0
  294. package/dist/html/layout/block.d.ts +58 -0
  295. package/dist/html/layout/block.d.ts.map +1 -0
  296. package/dist/html/layout/block.js +724 -0
  297. package/dist/html/layout/block.js.map +1 -0
  298. package/dist/html/layout/boxes.d.ts +261 -0
  299. package/dist/html/layout/boxes.d.ts.map +1 -0
  300. package/dist/html/layout/boxes.js +725 -0
  301. package/dist/html/layout/boxes.js.map +1 -0
  302. package/dist/html/layout/flex.d.ts +16 -0
  303. package/dist/html/layout/flex.d.ts.map +1 -0
  304. package/dist/html/layout/flex.js +242 -0
  305. package/dist/html/layout/flex.js.map +1 -0
  306. package/dist/html/layout/floats.d.ts +50 -0
  307. package/dist/html/layout/floats.d.ts.map +1 -0
  308. package/dist/html/layout/floats.js +110 -0
  309. package/dist/html/layout/floats.js.map +1 -0
  310. package/dist/html/layout/inline.d.ts +44 -0
  311. package/dist/html/layout/inline.d.ts.map +1 -0
  312. package/dist/html/layout/inline.js +700 -0
  313. package/dist/html/layout/inline.js.map +1 -0
  314. package/dist/html/layout/table.d.ts +5 -0
  315. package/dist/html/layout/table.d.ts.map +1 -0
  316. package/dist/html/layout/table.js +340 -0
  317. package/dist/html/layout/table.js.map +1 -0
  318. package/dist/html/node.d.ts +149 -0
  319. package/dist/html/node.d.ts.map +1 -0
  320. package/dist/html/node.js +861 -0
  321. package/dist/html/node.js.map +1 -0
  322. package/dist/html/paint.d.ts +48 -0
  323. package/dist/html/paint.d.ts.map +1 -0
  324. package/dist/html/paint.js +495 -0
  325. package/dist/html/paint.js.map +1 -0
  326. package/dist/html/resources.d.ts +48 -0
  327. package/dist/html/resources.d.ts.map +1 -0
  328. package/dist/html/resources.js +156 -0
  329. package/dist/html/resources.js.map +1 -0
  330. package/dist/index.d.ts +48 -0
  331. package/dist/index.d.ts.map +1 -0
  332. package/dist/index.js +92 -0
  333. package/dist/index.js.map +1 -0
  334. package/dist/internal/heights.d.ts +81 -0
  335. package/dist/internal/heights.d.ts.map +1 -0
  336. package/dist/internal/heights.js +193 -0
  337. package/dist/internal/heights.js.map +1 -0
  338. package/dist/internal/hx.d.ts +24 -0
  339. package/dist/internal/hx.d.ts.map +1 -0
  340. package/dist/internal/hx.js +27 -0
  341. package/dist/internal/hx.js.map +1 -0
  342. package/dist/internal/text.d.ts +6 -0
  343. package/dist/internal/text.d.ts.map +1 -0
  344. package/dist/internal/text.js +44 -0
  345. package/dist/internal/text.js.map +1 -0
  346. package/dist/internal/timers.d.ts +8 -0
  347. package/dist/internal/timers.d.ts.map +1 -0
  348. package/dist/internal/timers.js +22 -0
  349. package/dist/internal/timers.js.map +1 -0
  350. package/dist/internal/widget.d.ts +40 -0
  351. package/dist/internal/widget.d.ts.map +1 -0
  352. package/dist/internal/widget.js +44 -0
  353. package/dist/internal/widget.js.map +1 -0
  354. package/dist/markdown/ast.d.ts +126 -0
  355. package/dist/markdown/ast.d.ts.map +1 -0
  356. package/dist/markdown/ast.js +8 -0
  357. package/dist/markdown/ast.js.map +1 -0
  358. package/dist/markdown/hx.d.ts +24 -0
  359. package/dist/markdown/hx.d.ts.map +1 -0
  360. package/dist/markdown/hx.js +25 -0
  361. package/dist/markdown/hx.js.map +1 -0
  362. package/dist/markdown/index.d.ts +90 -0
  363. package/dist/markdown/index.d.ts.map +1 -0
  364. package/dist/markdown/index.js +413 -0
  365. package/dist/markdown/index.js.map +1 -0
  366. package/dist/markdown/parse.d.ts +8 -0
  367. package/dist/markdown/parse.d.ts.map +1 -0
  368. package/dist/markdown/parse.js +911 -0
  369. package/dist/markdown/parse.js.map +1 -0
  370. package/dist/markdown/spans.d.ts +20 -0
  371. package/dist/markdown/spans.d.ts.map +1 -0
  372. package/dist/markdown/spans.js +96 -0
  373. package/dist/markdown/spans.js.map +1 -0
  374. package/dist/media-player/backends.d.ts +64 -0
  375. package/dist/media-player/backends.d.ts.map +1 -0
  376. package/dist/media-player/backends.js +110 -0
  377. package/dist/media-player/backends.js.map +1 -0
  378. package/dist/media-player/control.d.ts +40 -0
  379. package/dist/media-player/control.d.ts.map +1 -0
  380. package/dist/media-player/control.js +44 -0
  381. package/dist/media-player/control.js.map +1 -0
  382. package/dist/media-player/hx.d.ts +24 -0
  383. package/dist/media-player/hx.d.ts.map +1 -0
  384. package/dist/media-player/hx.js +25 -0
  385. package/dist/media-player/hx.js.map +1 -0
  386. package/dist/media-player/index.d.ts +107 -0
  387. package/dist/media-player/index.d.ts.map +1 -0
  388. package/dist/media-player/index.js +219 -0
  389. package/dist/media-player/index.js.map +1 -0
  390. package/dist/media-player/mpv.d.ts +4 -0
  391. package/dist/media-player/mpv.d.ts.map +1 -0
  392. package/dist/media-player/mpv.js +120 -0
  393. package/dist/media-player/mpv.js.map +1 -0
  394. package/dist/media-player/vlc.d.ts +8 -0
  395. package/dist/media-player/vlc.d.ts.map +1 -0
  396. package/dist/media-player/vlc.js +83 -0
  397. package/dist/media-player/vlc.js.map +1 -0
  398. package/dist/richtext/index.d.ts +19 -0
  399. package/dist/richtext/index.d.ts.map +1 -0
  400. package/dist/richtext/index.js +4 -0
  401. package/dist/richtext/index.js.map +1 -0
  402. package/dist/richtext/links.d.ts +12 -0
  403. package/dist/richtext/links.d.ts.map +1 -0
  404. package/dist/richtext/links.js +64 -0
  405. package/dist/richtext/links.js.map +1 -0
  406. package/dist/richtext/menu.d.ts +11 -0
  407. package/dist/richtext/menu.d.ts.map +1 -0
  408. package/dist/richtext/menu.js +43 -0
  409. package/dist/richtext/menu.js.map +1 -0
  410. package/dist/richtext/node.d.ts +151 -0
  411. package/dist/richtext/node.d.ts.map +1 -0
  412. package/dist/richtext/node.js +255 -0
  413. package/dist/richtext/node.js.map +1 -0
  414. package/dist/richtext/runs.d.ts +92 -0
  415. package/dist/richtext/runs.d.ts.map +1 -0
  416. package/dist/richtext/runs.js +158 -0
  417. package/dist/richtext/runs.js.map +1 -0
  418. package/dist/table/hx.d.ts +24 -0
  419. package/dist/table/hx.d.ts.map +1 -0
  420. package/dist/table/hx.js +25 -0
  421. package/dist/table/hx.js.map +1 -0
  422. package/dist/table/index.d.ts +169 -0
  423. package/dist/table/index.d.ts.map +1 -0
  424. package/dist/table/index.js +844 -0
  425. package/dist/table/index.js.map +1 -0
  426. package/dist/table/rows.d.ts +136 -0
  427. package/dist/table/rows.d.ts.map +1 -0
  428. package/dist/table/rows.js +146 -0
  429. package/dist/table/rows.js.map +1 -0
  430. package/dist/terminal/backends.d.ts +105 -0
  431. package/dist/terminal/backends.d.ts.map +1 -0
  432. package/dist/terminal/backends.js +185 -0
  433. package/dist/terminal/backends.js.map +1 -0
  434. package/dist/terminal/hx.d.ts +24 -0
  435. package/dist/terminal/hx.d.ts.map +1 -0
  436. package/dist/terminal/hx.js +25 -0
  437. package/dist/terminal/hx.js.map +1 -0
  438. package/dist/terminal/index.d.ts +160 -0
  439. package/dist/terminal/index.d.ts.map +1 -0
  440. package/dist/terminal/index.js +344 -0
  441. package/dist/terminal/index.js.map +1 -0
  442. package/dist/terminal/title.d.ts +24 -0
  443. package/dist/terminal/title.d.ts.map +1 -0
  444. package/dist/terminal/title.js +129 -0
  445. package/dist/terminal/title.js.map +1 -0
  446. package/dist/terminal/vt/colors.d.ts +63 -0
  447. package/dist/terminal/vt/colors.d.ts.map +1 -0
  448. package/dist/terminal/vt/colors.js +203 -0
  449. package/dist/terminal/vt/colors.js.map +1 -0
  450. package/dist/terminal/vt/diff.d.ts +117 -0
  451. package/dist/terminal/vt/diff.d.ts.map +1 -0
  452. package/dist/terminal/vt/diff.js +358 -0
  453. package/dist/terminal/vt/diff.js.map +1 -0
  454. package/dist/terminal/vt/fonts.d.ts +92 -0
  455. package/dist/terminal/vt/fonts.d.ts.map +1 -0
  456. package/dist/terminal/vt/fonts.js +178 -0
  457. package/dist/terminal/vt/fonts.js.map +1 -0
  458. package/dist/terminal/vt/index.d.ts +78 -0
  459. package/dist/terminal/vt/index.d.ts.map +1 -0
  460. package/dist/terminal/vt/index.js +312 -0
  461. package/dist/terminal/vt/index.js.map +1 -0
  462. package/dist/terminal/vt/keys.d.ts +74 -0
  463. package/dist/terminal/vt/keys.d.ts.map +1 -0
  464. package/dist/terminal/vt/keys.js +309 -0
  465. package/dist/terminal/vt/keys.js.map +1 -0
  466. package/dist/terminal/vt/mouse.d.ts +44 -0
  467. package/dist/terminal/vt/mouse.d.ts.map +1 -0
  468. package/dist/terminal/vt/mouse.js +106 -0
  469. package/dist/terminal/vt/mouse.js.map +1 -0
  470. package/dist/terminal/vt/node.d.ts +246 -0
  471. package/dist/terminal/vt/node.d.ts.map +1 -0
  472. package/dist/terminal/vt/node.js +1068 -0
  473. package/dist/terminal/vt/node.js.map +1 -0
  474. package/dist/terminal/vt/pty.d.ts +81 -0
  475. package/dist/terminal/vt/pty.d.ts.map +1 -0
  476. package/dist/terminal/vt/pty.js +201 -0
  477. package/dist/terminal/vt/pty.js.map +1 -0
  478. package/dist/terminal/vt/renderer.d.ts +179 -0
  479. package/dist/terminal/vt/renderer.d.ts.map +1 -0
  480. package/dist/terminal/vt/renderer.js +357 -0
  481. package/dist/terminal/vt/renderer.js.map +1 -0
  482. package/dist/terminal/vt/timers.d.ts +6 -0
  483. package/dist/terminal/vt/timers.d.ts.map +1 -0
  484. package/dist/terminal/vt/timers.js +16 -0
  485. package/dist/terminal/vt/timers.js.map +1 -0
  486. package/dist/terminal/vt/xterm.d.ts +142 -0
  487. package/dist/terminal/vt/xterm.d.ts.map +1 -0
  488. package/dist/terminal/vt/xterm.js +32 -0
  489. package/dist/terminal/vt/xterm.js.map +1 -0
  490. package/dist/terminal-output/hx.d.ts +24 -0
  491. package/dist/terminal-output/hx.d.ts.map +1 -0
  492. package/dist/terminal-output/hx.js +25 -0
  493. package/dist/terminal-output/hx.js.map +1 -0
  494. package/dist/terminal-output/index.d.ts +92 -0
  495. package/dist/terminal-output/index.d.ts.map +1 -0
  496. package/dist/terminal-output/index.js +179 -0
  497. package/dist/terminal-output/index.js.map +1 -0
  498. package/dist/terminal-output/runs.d.ts +27 -0
  499. package/dist/terminal-output/runs.d.ts.map +1 -0
  500. package/dist/terminal-output/runs.js +66 -0
  501. package/dist/terminal-output/runs.js.map +1 -0
  502. package/dist/three/canvas.d.ts +46 -0
  503. package/dist/three/canvas.d.ts.map +1 -0
  504. package/dist/three/canvas.js +305 -0
  505. package/dist/three/canvas.js.map +1 -0
  506. package/dist/three/events.d.ts +64 -0
  507. package/dist/three/events.d.ts.map +1 -0
  508. package/dist/three/events.js +146 -0
  509. package/dist/three/events.js.map +1 -0
  510. package/dist/three/geometries.d.ts +89 -0
  511. package/dist/three/geometries.d.ts.map +1 -0
  512. package/dist/three/geometries.js +345 -0
  513. package/dist/three/geometries.js.map +1 -0
  514. package/dist/three/globals.d.ts +7 -0
  515. package/dist/three/globals.d.ts.map +1 -0
  516. package/dist/three/globals.js +20 -0
  517. package/dist/three/globals.js.map +1 -0
  518. package/dist/three/hx.d.ts +9 -0
  519. package/dist/three/hx.d.ts.map +1 -0
  520. package/dist/three/hx.js +10 -0
  521. package/dist/three/hx.js.map +1 -0
  522. package/dist/three/index.d.ts +22 -0
  523. package/dist/three/index.d.ts.map +1 -0
  524. package/dist/three/index.js +24 -0
  525. package/dist/three/index.js.map +1 -0
  526. package/dist/three/jsx-dev-runtime.d.ts +19 -0
  527. package/dist/three/jsx-dev-runtime.d.ts.map +1 -0
  528. package/dist/three/jsx-dev-runtime.js +2 -0
  529. package/dist/three/jsx-dev-runtime.js.map +1 -0
  530. package/dist/three/jsx-runtime.d.ts +19 -0
  531. package/dist/three/jsx-runtime.d.ts.map +1 -0
  532. package/dist/three/jsx-runtime.js +2 -0
  533. package/dist/three/jsx-runtime.js.map +1 -0
  534. package/dist/three/jsx.d.ts +198 -0
  535. package/dist/three/jsx.d.ts.map +1 -0
  536. package/dist/three/jsx.js +2 -0
  537. package/dist/three/jsx.js.map +1 -0
  538. package/dist/three/materials.d.ts +125 -0
  539. package/dist/three/materials.d.ts.map +1 -0
  540. package/dist/three/materials.js +229 -0
  541. package/dist/three/materials.js.map +1 -0
  542. package/dist/three/math.d.ts +146 -0
  543. package/dist/three/math.d.ts.map +1 -0
  544. package/dist/three/math.js +464 -0
  545. package/dist/three/math.js.map +1 -0
  546. package/dist/three/objects.d.ts +185 -0
  547. package/dist/three/objects.d.ts.map +1 -0
  548. package/dist/three/objects.js +309 -0
  549. package/dist/three/objects.js.map +1 -0
  550. package/dist/three/passes.d.ts +54 -0
  551. package/dist/three/passes.d.ts.map +1 -0
  552. package/dist/three/passes.js +78 -0
  553. package/dist/three/passes.js.map +1 -0
  554. package/dist/three/postprocess.d.ts +85 -0
  555. package/dist/three/postprocess.d.ts.map +1 -0
  556. package/dist/three/postprocess.js +503 -0
  557. package/dist/three/postprocess.js.map +1 -0
  558. package/dist/three/raycast.d.ts +24 -0
  559. package/dist/three/raycast.d.ts.map +1 -0
  560. package/dist/three/raycast.js +138 -0
  561. package/dist/three/raycast.js.map +1 -0
  562. package/dist/three/reconciler.d.ts +48 -0
  563. package/dist/three/reconciler.d.ts.map +1 -0
  564. package/dist/three/reconciler.js +416 -0
  565. package/dist/three/reconciler.js.map +1 -0
  566. package/dist/three/renderer-direct.d.ts +102 -0
  567. package/dist/three/renderer-direct.d.ts.map +1 -0
  568. package/dist/three/renderer-direct.js +782 -0
  569. package/dist/three/renderer-direct.js.map +1 -0
  570. package/dist/three/renderer-indirect.d.ts +68 -0
  571. package/dist/three/renderer-indirect.d.ts.map +1 -0
  572. package/dist/three/renderer-indirect.js +438 -0
  573. package/dist/three/renderer-indirect.js.map +1 -0
  574. package/dist/three/store.d.ts +81 -0
  575. package/dist/three/store.d.ts.map +1 -0
  576. package/dist/three/store.js +110 -0
  577. package/dist/three/store.js.map +1 -0
  578. package/dist/timeline/hx.d.ts +18 -0
  579. package/dist/timeline/hx.d.ts.map +1 -0
  580. package/dist/timeline/hx.js +10 -0
  581. package/dist/timeline/hx.js.map +1 -0
  582. package/dist/timeline/index.d.ts +152 -0
  583. package/dist/timeline/index.d.ts.map +1 -0
  584. package/dist/timeline/index.js +383 -0
  585. package/dist/timeline/index.js.map +1 -0
  586. package/dist/tray-host/hx.d.ts +24 -0
  587. package/dist/tray-host/hx.d.ts.map +1 -0
  588. package/dist/tray-host/hx.js +25 -0
  589. package/dist/tray-host/hx.js.map +1 -0
  590. package/dist/tray-host/index.d.ts +110 -0
  591. package/dist/tray-host/index.d.ts.map +1 -0
  592. package/dist/tray-host/index.js +191 -0
  593. package/dist/tray-host/index.js.map +1 -0
  594. package/dist/tray-host/manager.d.ts +137 -0
  595. package/dist/tray-host/manager.d.ts.map +1 -0
  596. package/dist/tray-host/manager.js +462 -0
  597. package/dist/tray-host/manager.js.map +1 -0
  598. package/dist/tray-host/notify.d.ts +22 -0
  599. package/dist/tray-host/notify.d.ts.map +1 -0
  600. package/dist/tray-host/notify.js +74 -0
  601. package/dist/tray-host/notify.js.map +1 -0
  602. package/dist/tray-host/protocol.d.ts +106 -0
  603. package/dist/tray-host/protocol.d.ts.map +1 -0
  604. package/dist/tray-host/protocol.js +191 -0
  605. package/dist/tray-host/protocol.js.map +1 -0
  606. package/dist/tree/hx.d.ts +24 -0
  607. package/dist/tree/hx.d.ts.map +1 -0
  608. package/dist/tree/hx.js +25 -0
  609. package/dist/tree/hx.js.map +1 -0
  610. package/dist/tree/index.d.ts +221 -0
  611. package/dist/tree/index.d.ts.map +1 -0
  612. package/dist/tree/index.js +716 -0
  613. package/dist/tree/index.js.map +1 -0
  614. package/dist/tree/internal.d.ts +18 -0
  615. package/dist/tree/internal.d.ts.map +1 -0
  616. package/dist/tree/internal.js +66 -0
  617. package/dist/tree/internal.js.map +1 -0
  618. package/dist/tree/rows.d.ts +177 -0
  619. package/dist/tree/rows.d.ts.map +1 -0
  620. package/dist/tree/rows.js +179 -0
  621. package/dist/tree/rows.js.map +1 -0
  622. package/package.json +166 -12
  623. package/src/ansi/cast.ts +159 -0
  624. package/src/ansi/index.ts +53 -0
  625. package/src/ansi/palette.ts +226 -0
  626. package/src/ansi/parse.ts +780 -0
  627. package/src/ansi/sgr.ts +380 -0
  628. package/src/ansi/utf8.ts +84 -0
  629. package/src/calendar/Calendar.ts +744 -0
  630. package/src/calendar/DatePicker.ts +368 -0
  631. package/src/calendar/dates.ts +307 -0
  632. package/src/calendar/index.ts +51 -0
  633. package/src/charts/components.ts +864 -0
  634. package/src/charts/data.ts +776 -0
  635. package/src/charts/hx.ts +39 -0
  636. package/src/charts/index.ts +99 -0
  637. package/src/charts/node.ts +932 -0
  638. package/src/charts/render.ts +982 -0
  639. package/src/charts/scale.ts +214 -0
  640. package/src/charts/spec.ts +122 -0
  641. package/src/charts/timers.ts +24 -0
  642. package/src/code/hx.ts +51 -0
  643. package/src/code/index.ts +174 -0
  644. package/src/code-editor/completion.ts +161 -0
  645. package/src/code-editor/doc.ts +198 -0
  646. package/src/code-editor/hx.ts +26 -0
  647. package/src/code-editor/index.ts +615 -0
  648. package/src/code-editor/node.ts +1926 -0
  649. package/src/code-language/hljs.ts +302 -0
  650. package/src/code-language/index.ts +51 -0
  651. package/src/code-language/languages/glsl.ts +120 -0
  652. package/src/code-language/languages/javascript.ts +419 -0
  653. package/src/code-language/languages/json.ts +72 -0
  654. package/src/code-language/languages/shell.ts +221 -0
  655. package/src/code-language/languages/sql.ts +165 -0
  656. package/src/code-language/lezer.ts +190 -0
  657. package/src/code-language/registry.ts +66 -0
  658. package/src/code-language/runs.ts +128 -0
  659. package/src/code-language/stream.ts +306 -0
  660. package/src/code-language/textmate.ts +134 -0
  661. package/src/code-language/theme.ts +124 -0
  662. package/src/code-language/timers.ts +36 -0
  663. package/src/code-language/types.ts +252 -0
  664. package/src/codeblock/index.ts +146 -0
  665. package/src/color-picker/ColorField.ts +333 -0
  666. package/src/color-picker/ColorPicker.ts +1069 -0
  667. package/src/color-picker/color.ts +399 -0
  668. package/src/color-picker/index.ts +50 -0
  669. package/src/desktop-calendar/eds.ts +484 -0
  670. package/src/desktop-calendar/ical.ts +119 -0
  671. package/src/desktop-calendar/index.ts +287 -0
  672. package/src/embed/client.ts +311 -0
  673. package/src/embed/host.ts +432 -0
  674. package/src/embed/index.ts +33 -0
  675. package/src/embed/timers.ts +32 -0
  676. package/src/flow/draw.ts +418 -0
  677. package/src/flow/index.ts +473 -0
  678. package/src/flow/model.ts +687 -0
  679. package/src/flow/node.ts +3694 -0
  680. package/src/flow/paths.ts +403 -0
  681. package/src/flow/types.ts +634 -0
  682. package/src/formula/hx.ts +51 -0
  683. package/src/formula/index.ts +243 -0
  684. package/src/formula/katex.ts +209 -0
  685. package/src/formula/layout.ts +876 -0
  686. package/src/formula/node.ts +467 -0
  687. package/src/html/controls.ts +244 -0
  688. package/src/html/css/cascade.ts +606 -0
  689. package/src/html/css/parse.ts +500 -0
  690. package/src/html/css/style.ts +1371 -0
  691. package/src/html/css/ua.ts +174 -0
  692. package/src/html/css/values.ts +358 -0
  693. package/src/html/dom.ts +410 -0
  694. package/src/html/hx.ts +55 -0
  695. package/src/html/index.ts +545 -0
  696. package/src/html/layout/block.ts +876 -0
  697. package/src/html/layout/boxes.ts +981 -0
  698. package/src/html/layout/flex.ts +297 -0
  699. package/src/html/layout/floats.ts +125 -0
  700. package/src/html/layout/inline.ts +870 -0
  701. package/src/html/layout/table.ts +380 -0
  702. package/src/html/node.ts +967 -0
  703. package/src/html/paint.ts +605 -0
  704. package/src/html/resources.ts +214 -0
  705. package/src/index.ts +638 -0
  706. package/src/internal/heights.ts +211 -0
  707. package/src/internal/hx.ts +53 -0
  708. package/src/internal/text.ts +44 -0
  709. package/src/internal/timers.ts +44 -0
  710. package/src/internal/widget.ts +92 -0
  711. package/src/markdown/ast.ts +163 -0
  712. package/src/markdown/hx.ts +51 -0
  713. package/src/markdown/index.ts +686 -0
  714. package/src/markdown/parse.ts +1037 -0
  715. package/src/markdown/spans.ts +143 -0
  716. package/src/media-player/backends.ts +155 -0
  717. package/src/media-player/control.ts +70 -0
  718. package/src/media-player/hx.ts +51 -0
  719. package/src/media-player/index.ts +378 -0
  720. package/src/media-player/mpv.ts +146 -0
  721. package/src/media-player/vlc.ts +97 -0
  722. package/src/richtext/index.ts +42 -0
  723. package/src/richtext/links.ts +78 -0
  724. package/src/richtext/menu.ts +49 -0
  725. package/src/richtext/node.ts +406 -0
  726. package/src/richtext/runs.ts +253 -0
  727. package/src/table/hx.ts +51 -0
  728. package/src/table/index.ts +1219 -0
  729. package/src/table/rows.ts +275 -0
  730. package/src/terminal/backends.ts +242 -0
  731. package/src/terminal/hx.ts +51 -0
  732. package/src/terminal/index.ts +574 -0
  733. package/src/terminal/title.ts +172 -0
  734. package/src/terminal/vt/colors.ts +247 -0
  735. package/src/terminal/vt/diff.ts +467 -0
  736. package/src/terminal/vt/fonts.ts +233 -0
  737. package/src/terminal/vt/index.ts +443 -0
  738. package/src/terminal/vt/keys.ts +369 -0
  739. package/src/terminal/vt/mouse.ts +144 -0
  740. package/src/terminal/vt/node.ts +1242 -0
  741. package/src/terminal/vt/pty.ts +334 -0
  742. package/src/terminal/vt/renderer.ts +555 -0
  743. package/src/terminal/vt/timers.ts +31 -0
  744. package/src/terminal/vt/xterm.ts +193 -0
  745. package/src/terminal-output/hx.ts +51 -0
  746. package/src/terminal-output/index.ts +340 -0
  747. package/src/terminal-output/runs.ts +79 -0
  748. package/src/three/canvas.ts +451 -0
  749. package/src/three/events.ts +197 -0
  750. package/src/three/geometries.ts +429 -0
  751. package/src/three/globals.ts +34 -0
  752. package/src/three/hx.ts +22 -0
  753. package/src/three/index.ts +98 -0
  754. package/src/three/jsx-dev-runtime.ts +27 -0
  755. package/src/three/jsx-runtime.ts +38 -0
  756. package/src/three/jsx.ts +275 -0
  757. package/src/three/materials.ts +270 -0
  758. package/src/three/math.ts +585 -0
  759. package/src/three/objects.ts +397 -0
  760. package/src/three/passes.ts +90 -0
  761. package/src/three/postprocess.ts +677 -0
  762. package/src/three/raycast.ts +189 -0
  763. package/src/three/react-reconciler.d.ts +50 -0
  764. package/src/three/reconciler.ts +624 -0
  765. package/src/three/renderer-direct.ts +1055 -0
  766. package/src/three/renderer-indirect.ts +570 -0
  767. package/src/three/store.ts +207 -0
  768. package/src/timeline/hx.ts +31 -0
  769. package/src/timeline/index.ts +603 -0
  770. package/src/tray-host/hx.ts +51 -0
  771. package/src/tray-host/index.ts +331 -0
  772. package/src/tray-host/manager.ts +639 -0
  773. package/src/tray-host/notify.ts +102 -0
  774. package/src/tray-host/protocol.ts +235 -0
  775. package/src/tree/hx.ts +51 -0
  776. package/src/tree/index.ts +1087 -0
  777. package/src/tree/internal.ts +87 -0
  778. package/src/tree/rows.ts +334 -0
  779. package/src/index.d.ts +0 -2
  780. package/src/index.js +0 -9
  781. package/src/sparkline/index.d.ts +0 -29
  782. package/src/sparkline/index.js +0 -50
  783. package/src/sparkline/node.js +0 -72
@@ -0,0 +1,1521 @@
1
+ // The retained node behind `<codeeditor>`: the text model (lines, caret,
2
+ // selection, undo), scrolling, and all painting (gutter, tokens, selection,
3
+ // caret, bracket match, diagnostics). `./index.ts` registers it and wires
4
+ // input to it.
5
+ //
6
+ // **How input reaches this node.** Through the default-action seam
7
+ // react-x11#266 made public: `defaultKeyDown`, `defaultMouseDown`,
8
+ // `defaultMouseDrag`, `defaultMouseUp`, `defaultFocus` and `defaultBlur`
9
+ // are methods the EventManager calls on the focused or pressed node, after
10
+ // the application's own `onKeyDown`/`onMouseDown` handlers and not at all
11
+ // if one of them called `preventDefault()`. So a bare `<codeeditor>` in
12
+ // JSX edits, selects and blinks the way `<textinput>` does, with no
13
+ // wrapping component required — and an app still vetoes or extends any of
14
+ // it without knowing how the element is built.
15
+ //
16
+ // `<CodeEditor>` is now only the completion popup: it is an ordinary
17
+ // `onKeyDown` handler that consumes the keys the popup owns (arrows,
18
+ // Return, Tab-accept, Escape-close) and, by consuming them, keeps this
19
+ // node's default action from also acting on them.
20
+ import { CARET_BLINK_MS, Node } from 'react-x11/node';
21
+ import { ctrlChordLetter, XK_BACKSPACE, XK_DELETE, XK_DOWN, XK_END, XK_ESCAPE, XK_HOME, XK_KP_ENTER, XK_LEFT, XK_PAGE_DOWN, XK_PAGE_UP, XK_RETURN, XK_RIGHT, XK_TAB, XK_UP, } from 'react-x11/keysyms';
22
+ import { startInterval, stopInterval } from '../code-language/timers.js';
23
+ import { clampPos, comparePos, maxPos, minPos, movePos, posEqual, tabMap, wordBoundary, wordRangeAt, } from './doc.js';
24
+ import { DARK_TOKEN_STYLES, isDarkBackground, LIGHT_TOKEN_STYLES, tokenStyleFor, } from '../code-language/theme.js';
25
+ /**
26
+ * The element name — registration key, `kind`, and JSX tag, one string.
27
+ * react-x11 rejects a node whose `kind` is not the name it was registered
28
+ * under, because `kind` is what paint order, the test queries and the DEV
29
+ * assertion all match on. One constant, used in all three places.
30
+ */
31
+ export const ELEMENT = 'codeeditor';
32
+ const UNDO_LIMIT = 200;
33
+ const DEFAULT_ROWS = 6;
34
+ const PREFERRED_WIDTH = 360;
35
+ const GUTTER_PAD = 8; // each side of the line numbers
36
+ const CARET_MARGIN = 24; // keep this many px visible beside the caret
37
+ const MATCH_SCAN_LIMIT = 20000; // chars examined looking for a bracket match
38
+ const BRACKETS = {
39
+ '(': { match: ')', dir: 1 },
40
+ '[': { match: ']', dir: 1 },
41
+ '{': { match: '}', dir: 1 },
42
+ ')': { match: '(', dir: -1 },
43
+ ']': { match: '[', dir: -1 },
44
+ '}': { match: '{', dir: -1 },
45
+ };
46
+ /** Token types the bracket matcher must not look inside. */
47
+ const OPAQUE_TOKENS = new Set(['string', 'string2', 'comment', 'docComment']);
48
+ const SEVERITY_COLORS = {
49
+ error: '#e5484d',
50
+ warning: '#d5a021',
51
+ information: '#3b82f6',
52
+ hint: '#8b8f98',
53
+ };
54
+ function cpToUtf16(text, cp) {
55
+ let i = 0;
56
+ for (let n = 0; n < cp && i < text.length; n++) {
57
+ const code = text.codePointAt(i);
58
+ i += code > 0xffff ? 2 : 1;
59
+ }
60
+ return i;
61
+ }
62
+ function utf16ToCp(text, index) {
63
+ let cp = 0;
64
+ for (let i = 0; i < index && i < text.length;) {
65
+ const code = text.codePointAt(i);
66
+ i += code > 0xffff ? 2 : 1;
67
+ cp++;
68
+ }
69
+ return cp;
70
+ }
71
+ export class CodeEditorNode extends Node {
72
+ _lines;
73
+ /** What `_lines` was last built from — how a controlled `value` that the
74
+ * parent refused to update snaps back on the next read (see `_syncProps`). */
75
+ _synced;
76
+ _caret = { line: 0, ch: 0 };
77
+ _anchor = { line: 0, ch: 0 };
78
+ _scrollX = 0;
79
+ _scrollY = 0;
80
+ _focused = false;
81
+ _caretOn = false;
82
+ _blinkTimer = null;
83
+ _goalX = null;
84
+ _drag = null;
85
+ _dragOrigin = { line: 0, ch: 0 };
86
+ /** Escape was the last key, so the next Tab leaves instead of indenting —
87
+ * the editor's way of not being a keyboard trap (see `defaultKeyDown`). */
88
+ _tabEscapes = false;
89
+ _history;
90
+ _historyIndex = 0;
91
+ _undoRun = null;
92
+ _pendingValue = null;
93
+ _keyNative = null;
94
+ _tok = null;
95
+ _tokLanguage;
96
+ _lineCache = new Map();
97
+ _metricsKey = '';
98
+ _lineH = 0;
99
+ _charW = 0;
100
+ _widest = 0;
101
+ constructor(props, app) {
102
+ super(ELEMENT, props, app);
103
+ const initial = props.value != null
104
+ ? String(props.value)
105
+ : props.defaultValue != null
106
+ ? String(props.defaultValue)
107
+ : '';
108
+ this._lines = initial.split('\n');
109
+ this._synced = initial;
110
+ this._history = [
111
+ { value: initial, caret: this._caret, anchor: this._anchor },
112
+ ];
113
+ // Without this nothing focuses the editor and no key ever reaches it —
114
+ // an app's `focusable`/`tabIndex` prop still overrides either way, which
115
+ // is how `<CodeEditor disabled>` stays out of the tab order.
116
+ this.focusableByDefault = true;
117
+ this.defaultCursor = 'text';
118
+ // The editor's selection is its own (react-x11#291). A `selectable`
119
+ // document around it — a `<Markdown>` with a live editor in it, a form
120
+ // on a selectable pane — skips this subtree whole rather than lighting
121
+ // up half the text somebody is typing in, and a press here never
122
+ // reaches the surface above. That is the same rule `<textinput>` keeps,
123
+ // and the reason the `default*` handlers below can own the gesture
124
+ // without calling `super`.
125
+ this.hasOwnSelection = true;
126
+ }
127
+ /**
128
+ * The editor's own size: a comfortable width, never wider than what is on
129
+ * offer, and `rows` text lines tall. An unbounded axis arrives as
130
+ * `Infinity`, so the `Math.min` is the whole rule. The height comes from a
131
+ * prop rather than from the style, which is what makes the
132
+ * `invalidateMeasure` in `applyProps` necessary.
133
+ */
134
+ measureContent({ width }) {
135
+ const rows = Math.max(1, Number(this.props.rows ?? DEFAULT_ROWS));
136
+ return {
137
+ width: Math.min(PREFERRED_WIDTH, width),
138
+ height: Math.ceil(this._lineHeight() * rows),
139
+ };
140
+ }
141
+ // --- value & props sync --------------------------------------------------
142
+ get value() {
143
+ if (this._pendingValue !== null)
144
+ return this._pendingValue;
145
+ if (this.props.value != null)
146
+ return String(this.props.value);
147
+ return this._lines.join('\n');
148
+ }
149
+ /** Programmatic set, no `onChange` — the DOM-input contract form libraries
150
+ * rely on. On a controlled editor `props.value` wins the next render. */
151
+ set value(next) {
152
+ const text = next == null ? '' : String(next);
153
+ if (text === this._lines.join('\n'))
154
+ return;
155
+ this._setText(text);
156
+ this._noteExternal();
157
+ this._repaint();
158
+ }
159
+ get name() {
160
+ return this.props.name;
161
+ }
162
+ get selection() {
163
+ return { anchor: { ...this._anchor }, head: { ...this._caret } };
164
+ }
165
+ get lines() {
166
+ this._syncProps();
167
+ return this._lines;
168
+ }
169
+ get language() {
170
+ return this.props.language ?? null;
171
+ }
172
+ /**
173
+ * Pull a controlled `value` into the model. Called at the top of every
174
+ * command and paint: a parent that ignored an `onChange` never re-renders,
175
+ * so there is no `applyProps` moment to catch the divergence — the next
176
+ * read is that moment.
177
+ */
178
+ _syncProps() {
179
+ const v = this.props.value;
180
+ if (v == null)
181
+ return;
182
+ const text = String(v);
183
+ if (text === this._synced)
184
+ return;
185
+ this._setText(text);
186
+ this._noteExternal();
187
+ }
188
+ _setText(text) {
189
+ this._lines = text.split('\n');
190
+ this._synced = text;
191
+ this._caret = clampPos(this._lines, this._caret);
192
+ this._anchor = clampPos(this._lines, this._anchor);
193
+ this._lineCache.clear();
194
+ this._widest = 0;
195
+ this._tokenizer()?.setLines(this._lines);
196
+ }
197
+ // --- tokenizer -----------------------------------------------------------
198
+ _tokenizer() {
199
+ const lang = this.language;
200
+ if (lang !== this._tokLanguage) {
201
+ this._tok?.dispose?.();
202
+ this._tokLanguage = lang;
203
+ this._tok = lang
204
+ ? lang.createTokenizer({
205
+ invalidate: (fromLine) => {
206
+ for (const key of this._lineCache.keys()) {
207
+ if (key >= fromLine)
208
+ this._lineCache.delete(key);
209
+ }
210
+ this._repaint();
211
+ },
212
+ })
213
+ : null;
214
+ this._tok?.setLines(this._lines);
215
+ this._lineCache.clear();
216
+ }
217
+ return this._tok;
218
+ }
219
+ // --- metrics & style -----------------------------------------------------
220
+ _textStyle() {
221
+ const s = this.style;
222
+ return {
223
+ family: s.fontFamily ?? 'monospace',
224
+ size: s.fontSize ?? 13,
225
+ weight: s.fontWeight ?? 400,
226
+ style: s.fontStyle ?? 'normal',
227
+ // theme text before hardcoded ink: on a dark desktop the palette's
228
+ // `text` is light, and a near-black fallback would vanish — the
229
+ // component's default style also asks for `color: '$text'`, so this
230
+ // branch is for a bare <codeeditor> under a themeless tree
231
+ color: s.color ?? this._themeColor('text') ?? '#24292f',
232
+ };
233
+ }
234
+ _themeColor(token) {
235
+ const theme = this.theme;
236
+ const v = theme?.[token];
237
+ return typeof v === 'string' ? v : undefined;
238
+ }
239
+ _fonts() {
240
+ return (this.app?.fonts ?? null);
241
+ }
242
+ _metrics() {
243
+ const s = this._textStyle();
244
+ const key = `${s.family}|${s.size}|${s.weight}`;
245
+ if (key === this._metricsKey && this._lineH > 0)
246
+ return;
247
+ const fonts = this._fonts();
248
+ this._metricsKey = key;
249
+ if (!fonts) {
250
+ this._lineH = Number(s.size) * 1.4;
251
+ this._charW = Number(s.size) * 0.6;
252
+ return;
253
+ }
254
+ const probe = fonts.layout('Mg', s);
255
+ this._lineH = probe.height || Number(s.size) * 1.4;
256
+ this._charW = fonts.layout('0', s).width || Number(s.size) * 0.6;
257
+ }
258
+ _lineHeight() {
259
+ this._metrics();
260
+ return this._lineH;
261
+ }
262
+ /**
263
+ * The content box: `abs` inset by border and padding, computed from the
264
+ * flattened style. [react-x11 gap] built-in fields read an internal
265
+ * `contentBox()`; a public equivalent would remove this arithmetic —
266
+ * filed as react-x11#254.
267
+ */
268
+ _contentRect() {
269
+ const s = this.style;
270
+ const num = (v) => (typeof v === 'number' ? v : 0);
271
+ const border = num(s.borderWidth);
272
+ const pad = num(s.padding);
273
+ const top = num(s.borderTopWidth ?? border) + num(s.paddingTop ?? pad);
274
+ const right = num(s.borderRightWidth ?? border) + num(s.paddingRight ?? pad);
275
+ const bottom = num(s.borderBottomWidth ?? border) + num(s.paddingBottom ?? pad);
276
+ const left = num(s.borderLeftWidth ?? border) + num(s.paddingLeft ?? pad);
277
+ const { x, y, width, height } = this.abs;
278
+ return {
279
+ x: x + left,
280
+ y: y + top,
281
+ width: Math.max(0, width - left - right),
282
+ height: Math.max(0, height - top - bottom),
283
+ };
284
+ }
285
+ _gutterWidth() {
286
+ if (!this.props.lineNumbers)
287
+ return 0;
288
+ this._metrics();
289
+ const digits = Math.max(2, String(this._lines.length).length);
290
+ return Math.ceil(digits * this._charW) + GUTTER_PAD * 2;
291
+ }
292
+ _tabSize() {
293
+ const n = Number(this.props.tabSize ?? 4);
294
+ return Number.isFinite(n) && n >= 1 ? Math.floor(n) : 4;
295
+ }
296
+ // --- per-line layout -----------------------------------------------------
297
+ _resolveColor(color) {
298
+ if (!color)
299
+ return color;
300
+ if (color.startsWith('$'))
301
+ return this._themeColor(color.slice(1));
302
+ return color;
303
+ }
304
+ /**
305
+ * The token palette in force: the prop when given, else whichever
306
+ * built-in set reads on this editor's background — the resolved
307
+ * `backgroundColor` when it is a judgeable colour, the theme's
308
+ * `background` otherwise. This is what makes one `<CodeEditor>` legible
309
+ * on a light *and* a dark desktop with no props at all.
310
+ */
311
+ _tokenStyles() {
312
+ const explicit = this.props.tokenStyles;
313
+ if (explicit)
314
+ return explicit;
315
+ const s = this.style;
316
+ const own = isDarkBackground(s.backgroundColor);
317
+ const dark = own ?? isDarkBackground(this._themeColor('background'));
318
+ return dark ? DARK_TOKEN_STYLES : LIGHT_TOKEN_STYLES;
319
+ }
320
+ /** Shared by the paint defaults that need a light/dark answer. */
321
+ _onDark() {
322
+ const s = this.style;
323
+ return ((isDarkBackground(s.backgroundColor) ??
324
+ isDarkBackground(this._themeColor('background'))) === true);
325
+ }
326
+ _lineEntry(line) {
327
+ const raw = this._lines[line];
328
+ const map = tabMap(raw, this._tabSize());
329
+ const tokens = this._tokenizer()?.lineTokens(line) ?? [];
330
+ const styleKey = this._metricsKey;
331
+ const cached = this._lineCache.get(line);
332
+ if (cached &&
333
+ cached.display === map.display &&
334
+ cached.tokens === tokens &&
335
+ cached.styleKey === styleKey) {
336
+ return cached;
337
+ }
338
+ const fonts = this._fonts();
339
+ const base = this._textStyle();
340
+ let layout = null;
341
+ if (fonts) {
342
+ const styles = this._tokenStyles();
343
+ const spans = [];
344
+ let cursor = 0;
345
+ for (const t of tokens) {
346
+ const from = map.toDisplay(t.from);
347
+ const to = map.toDisplay(t.to);
348
+ if (from > cursor) {
349
+ spans.push({ text: map.display.slice(cursor, from) });
350
+ }
351
+ if (to > from) {
352
+ const st = tokenStyleFor(styles, t.type);
353
+ const span = {
354
+ text: map.display.slice(from, to),
355
+ };
356
+ if (st) {
357
+ const color = this._resolveColor(st.color);
358
+ if (color)
359
+ span.color = color;
360
+ if (st.weight)
361
+ span.weight = st.weight;
362
+ if (st.fontStyle)
363
+ span.style = st.fontStyle;
364
+ }
365
+ spans.push(span);
366
+ }
367
+ cursor = Math.max(cursor, to);
368
+ }
369
+ if (cursor < map.display.length) {
370
+ spans.push({ text: map.display.slice(cursor) });
371
+ }
372
+ layout = fonts.layout(spans.length > 0 ? spans : [{ text: '' }], base);
373
+ }
374
+ const entry = {
375
+ display: map.display,
376
+ tokens,
377
+ styleKey,
378
+ layout,
379
+ map,
380
+ };
381
+ this._lineCache.set(line, entry);
382
+ if (layout && layout.width > this._widest)
383
+ this._widest = layout.width;
384
+ return entry;
385
+ }
386
+ /** Caret x (display pixels from the text origin) for a position. */
387
+ _caretX(pos) {
388
+ const entry = this._lineEntry(pos.line);
389
+ if (!entry.layout)
390
+ return 0;
391
+ const disp = entry.map.toDisplay(pos.ch);
392
+ return entry.layout.caretPosition(utf16ToCp(entry.display, disp)).x;
393
+ }
394
+ /** Window coordinates → document position. */
395
+ _posAt(x, y) {
396
+ this._metrics();
397
+ const content = this._contentRect();
398
+ const textX = content.x + this._gutterWidth();
399
+ const line = Math.max(0, Math.min(this._lines.length - 1, Math.floor((y - content.y + this._scrollY) / this._lineH)));
400
+ const entry = this._lineEntry(line);
401
+ if (!entry.layout)
402
+ return { line, ch: 0 };
403
+ const cp = entry.layout.indexAt(x - textX + this._scrollX, 0);
404
+ const disp = cpToUtf16(entry.display, cp);
405
+ return clampPos(this._lines, { line, ch: entry.map.toRaw(disp) });
406
+ }
407
+ /** Font facts the completion popup sizes itself with. */
408
+ metrics() {
409
+ this._metrics();
410
+ const s = this._textStyle();
411
+ return {
412
+ lineHeight: this._lineH,
413
+ charWidth: this._charW,
414
+ family: String(s.family),
415
+ size: Number(s.size),
416
+ };
417
+ }
418
+ /** Text width in this editor's font. The built-in completion popup no
419
+ * longer needs it — a `<popup anchor>` measures its own rows — but an app
420
+ * laying out its own overlay against the text still does. */
421
+ measureText(text) {
422
+ const fonts = this._fonts();
423
+ if (!fonts)
424
+ return text.length * this._charW;
425
+ return fonts.layout(text, this._textStyle()).width;
426
+ }
427
+ /** Caret geometry relative to this node's origin — node coordinates, which
428
+ * is exactly what a `<popup anchor={{at}}>` takes, so the completion popup
429
+ * hands this straight through and nothing here has to know where the
430
+ * editor sits on screen. */
431
+ caretRect() {
432
+ this._syncProps();
433
+ this._metrics();
434
+ const content = this._contentRect();
435
+ const textX = content.x + this._gutterWidth() - this.abs.x;
436
+ return {
437
+ x: textX + this._caretX(this._caret) - this._scrollX,
438
+ y: content.y - this.abs.y + this._caret.line * this._lineH - this._scrollY,
439
+ height: this._lineH,
440
+ };
441
+ }
442
+ // --- scrolling -----------------------------------------------------------
443
+ _maxScrollY() {
444
+ const content = this._contentRect();
445
+ return Math.max(0, this._lines.length * this._lineHeight() - content.height);
446
+ }
447
+ _maxScrollX() {
448
+ const content = this._contentRect();
449
+ const textW = content.width - this._gutterWidth();
450
+ return Math.max(0, this._widest + CARET_MARGIN - textW);
451
+ }
452
+ /** Wheel notches (deltaX/deltaY arrive as ±1 per notch) → pixels. */
453
+ handleWheel(ev) {
454
+ const step = this._lineHeight() * 3;
455
+ return this.scrollBy(ev.deltaX * step, ev.deltaY * step);
456
+ }
457
+ /** Returns true when the offset moved (the component consumes the wheel
458
+ * event exactly then, so an unscrollable editor lets the page scroll). */
459
+ scrollBy(dx, dy) {
460
+ const nx = Math.max(0, Math.min(this._scrollX + dx, this._maxScrollX()));
461
+ const ny = Math.max(0, Math.min(this._scrollY + dy, this._maxScrollY()));
462
+ if (nx === this._scrollX && ny === this._scrollY)
463
+ return false;
464
+ this._scrollX = nx;
465
+ this._scrollY = ny;
466
+ this.root?.invalidate(false, this.abs, 'scroll');
467
+ return true;
468
+ }
469
+ _ensureCaretVisible() {
470
+ this._metrics();
471
+ const content = this._contentRect();
472
+ const lineTop = this._caret.line * this._lineH;
473
+ if (lineTop < this._scrollY)
474
+ this._scrollY = lineTop;
475
+ const lineBottom = lineTop + this._lineH;
476
+ if (lineBottom > this._scrollY + content.height) {
477
+ this._scrollY = lineBottom - content.height;
478
+ }
479
+ const textW = content.width - this._gutterWidth();
480
+ const x = this._caretX(this._caret);
481
+ if (x < this._scrollX + CARET_MARGIN) {
482
+ this._scrollX = Math.max(0, x - CARET_MARGIN);
483
+ }
484
+ if (x > this._scrollX + textW - CARET_MARGIN) {
485
+ this._scrollX = x - textW + CARET_MARGIN;
486
+ }
487
+ this._scrollY = Math.max(0, Math.min(this._scrollY, this._maxScrollY()));
488
+ this._scrollX = Math.max(0, this._scrollX);
489
+ }
490
+ // --- events out ----------------------------------------------------------
491
+ _makeEvent(type) {
492
+ const self = this;
493
+ const ev = {
494
+ type,
495
+ value: this.value,
496
+ name: this.name,
497
+ selection: this.selection,
498
+ target: self,
499
+ currentTarget: self,
500
+ nativeEvent: this._keyNative ?? null,
501
+ defaultPrevented: false,
502
+ preventDefault() {
503
+ ev.defaultPrevented = true;
504
+ },
505
+ stopPropagation() { },
506
+ };
507
+ return ev;
508
+ }
509
+ _fire(prop, value) {
510
+ const handler = this.props[prop];
511
+ if (!handler)
512
+ return;
513
+ const previous = this._pendingValue;
514
+ this._pendingValue = value;
515
+ try {
516
+ handler(this._makeEvent(prop === 'onSubmit'
517
+ ? 'submit'
518
+ : prop === 'onSelectionChange'
519
+ ? 'selectionchange'
520
+ : 'change'));
521
+ }
522
+ finally {
523
+ this._pendingValue = previous;
524
+ }
525
+ }
526
+ _repaint() {
527
+ this._caretOn = true;
528
+ this.root?.invalidate(false, this.abs, 'text');
529
+ }
530
+ // --- history -------------------------------------------------------------
531
+ _noteExternal() {
532
+ this._undoRun = null;
533
+ const value = this._lines.join('\n');
534
+ const top = this._history[this._historyIndex];
535
+ if (top && top.value === value)
536
+ return;
537
+ this._pushHistory({ value, caret: this._caret, anchor: this._anchor });
538
+ }
539
+ _pushHistory(entry) {
540
+ this._history.length = this._historyIndex + 1;
541
+ this._history.push(entry);
542
+ if (this._history.length > UNDO_LIMIT) {
543
+ this._history.splice(0, this._history.length - UNDO_LIMIT);
544
+ }
545
+ this._historyIndex = this._history.length - 1;
546
+ }
547
+ breakUndoRun() {
548
+ this._undoRun = null;
549
+ }
550
+ get canUndo() {
551
+ return this._historyIndex > 0;
552
+ }
553
+ get canRedo() {
554
+ return this._historyIndex < this._history.length - 1;
555
+ }
556
+ undo() {
557
+ this._syncProps();
558
+ if (!this.canUndo)
559
+ return;
560
+ this._historyIndex--;
561
+ this._applyHistory(this._history[this._historyIndex]);
562
+ }
563
+ redo() {
564
+ this._syncProps();
565
+ if (!this.canRedo)
566
+ return;
567
+ this._historyIndex++;
568
+ this._applyHistory(this._history[this._historyIndex]);
569
+ }
570
+ _applyHistory(entry) {
571
+ this._undoRun = null;
572
+ this._goalX = null;
573
+ this._applyLines(entry.value.split('\n'), null);
574
+ this._caret = clampPos(this._lines, entry.caret);
575
+ this._anchor = clampPos(this._lines, entry.anchor);
576
+ this._afterEdit(entry.value);
577
+ }
578
+ // --- editing core --------------------------------------------------------
579
+ _selectionRange() {
580
+ return [
581
+ minPos(this._anchor, this._caret),
582
+ maxPos(this._anchor, this._caret),
583
+ ];
584
+ }
585
+ _hasSelection() {
586
+ return !posEqual(this._anchor, this._caret);
587
+ }
588
+ selectedText() {
589
+ this._syncProps();
590
+ const [a, b] = this._selectionRange();
591
+ if (posEqual(a, b))
592
+ return '';
593
+ if (a.line === b.line)
594
+ return this._lines[a.line].slice(a.ch, b.ch);
595
+ const parts = [this._lines[a.line].slice(a.ch)];
596
+ for (let i = a.line + 1; i < b.line; i++)
597
+ parts.push(this._lines[i]);
598
+ parts.push(this._lines[b.line].slice(0, b.ch));
599
+ return parts.join('\n');
600
+ }
601
+ /**
602
+ * The one place text changes. Replaces `[from, to)` with `text`, moves the
603
+ * caret to the end of the insertion, fires `onChange`, and manages undo —
604
+ * `run` names a coalescable edit kind (`'type'`, `'delete-back'`,
605
+ * `'delete-forward'`); anything else is its own undo step.
606
+ */
607
+ replaceRange(from, to, text, run = null) {
608
+ this._syncProps();
609
+ if (this.props.readOnly)
610
+ return;
611
+ const a = clampPos(this._lines, minPos(from, to));
612
+ const b = clampPos(this._lines, maxPos(from, to));
613
+ const insert = String(text).replace(/\r\n?/g, '\n');
614
+ const before = this._lines[a.line].slice(0, a.ch);
615
+ const after = this._lines[b.line].slice(b.ch);
616
+ const inserted = insert.split('\n');
617
+ inserted[0] = before + inserted[0];
618
+ const caretCh = inserted[inserted.length - 1].length;
619
+ inserted[inserted.length - 1] += after;
620
+ const newLines = this._lines.slice();
621
+ newLines.splice(a.line, b.line - a.line + 1, ...inserted);
622
+ const coalesce = run !== null &&
623
+ run === this._undoRun &&
624
+ this._historyIndex === this._history.length - 1;
625
+ this._applyLines(newLines, {
626
+ fromLine: a.line,
627
+ removed: b.line - a.line + 1,
628
+ inserted: inserted.length,
629
+ });
630
+ this._caret = clampPos(this._lines, {
631
+ line: a.line + inserted.length - 1,
632
+ ch: caretCh,
633
+ });
634
+ this._anchor = { ...this._caret };
635
+ this._goalX = null;
636
+ const value = this._lines.join('\n');
637
+ if (coalesce) {
638
+ this._history[this._historyIndex] = {
639
+ value,
640
+ caret: this._caret,
641
+ anchor: this._anchor,
642
+ };
643
+ }
644
+ else {
645
+ this._pushHistory({ value, caret: this._caret, anchor: this._anchor });
646
+ }
647
+ this._undoRun = run;
648
+ this._afterEdit(value);
649
+ }
650
+ _applyLines(newLines, edit) {
651
+ if (edit) {
652
+ // A tokenizer keeps the array `setLines` handed it (that is the
653
+ // contract in `Tokenizer`) and is told about changes through `edit()`
654
+ // alone, so an incremental edit has to *mutate* the live array —
655
+ // swapping in a fresh one would leave every engine tokenizing the
656
+ // pre-edit text at the new line numbers, painting a split line with
657
+ // its neighbour's colours. Callers only touch the edited span, so
658
+ // splicing that span across reproduces `newLines` exactly.
659
+ this._lines.splice(edit.fromLine, edit.removed, ...newLines.slice(edit.fromLine, edit.fromLine + edit.inserted));
660
+ }
661
+ else {
662
+ this._lines = newLines;
663
+ }
664
+ const tok = this._tokenizer();
665
+ if (edit && tok) {
666
+ tok.edit(edit);
667
+ // line indices at and below the edit moved or changed; drop them
668
+ for (const key of [...this._lineCache.keys()]) {
669
+ if (key >= edit.fromLine)
670
+ this._lineCache.delete(key);
671
+ }
672
+ if (edit.removed !== edit.inserted)
673
+ this._lineCache.clear();
674
+ }
675
+ else {
676
+ tok?.setLines(this._lines);
677
+ this._lineCache.clear();
678
+ }
679
+ }
680
+ _afterEdit(value) {
681
+ this._synced = value;
682
+ this._ensureCaretVisible();
683
+ this._repaint();
684
+ this._fire('onChange', value);
685
+ this._fire('onSelectionChange', value);
686
+ }
687
+ insertText(text, run = null) {
688
+ const [a, b] = this._selectionRange();
689
+ this.replaceRange(a, b, text, this._hasSelection() ? null : run);
690
+ }
691
+ moveCaret(pos, extend) {
692
+ this._syncProps();
693
+ this.breakUndoRun();
694
+ this._caret = clampPos(this._lines, pos);
695
+ if (!extend)
696
+ this._anchor = { ...this._caret };
697
+ this._ensureCaretVisible();
698
+ this._repaint();
699
+ this._fire('onSelectionChange', this.value);
700
+ }
701
+ select(anchor, head) {
702
+ this._syncProps();
703
+ this.breakUndoRun();
704
+ this._anchor = clampPos(this._lines, anchor);
705
+ this._caret = clampPos(this._lines, head);
706
+ this._ensureCaretVisible();
707
+ this._repaint();
708
+ this._fire('onSelectionChange', this.value);
709
+ }
710
+ // `: this`, matching the base declaration core grew in #294 — a subclass
711
+ // narrowing it to void stops being structurally a DrawnNode.
712
+ selectAll() {
713
+ const last = this._lines.length - 1;
714
+ this.select({ line: 0, ch: 0 }, { line: last, ch: this._lines[last].length });
715
+ this._ownPrimary();
716
+ return this;
717
+ }
718
+ // --- clipboard -----------------------------------------------------------
719
+ _clipboard() {
720
+ const app = this.app;
721
+ return app?.clipboard ?? null;
722
+ }
723
+ copySelection(selection = 'CLIPBOARD') {
724
+ const text = this.selectedText();
725
+ if (!text)
726
+ return;
727
+ this._clipboard()
728
+ ?.write(text, { selection })
729
+ .catch(() => { });
730
+ }
731
+ _ownPrimary() {
732
+ if (this._hasSelection())
733
+ this.copySelection('PRIMARY');
734
+ }
735
+ pasteFrom(selection = 'CLIPBOARD') {
736
+ this._clipboard()
737
+ ?.read({ selection })
738
+ .then((text) => {
739
+ if (!this.destroyed && typeof text === 'string' && text) {
740
+ this.insertText(text);
741
+ }
742
+ })
743
+ .catch(() => { });
744
+ }
745
+ // --- indentation & comments ---------------------------------------------
746
+ _indentUnit() {
747
+ return this.props.insertSpaces === false
748
+ ? '\t'
749
+ : ' '.repeat(this._tabSize());
750
+ }
751
+ /** Tab with a multi-line selection (or dir < 0): shift the lines. */
752
+ indentSelection(dir) {
753
+ this._syncProps();
754
+ if (this.props.readOnly)
755
+ return;
756
+ const [a, b] = this._selectionRange();
757
+ const endLine = b.line > a.line && b.ch === 0 ? b.line - 1 : b.line;
758
+ const unit = this._indentUnit();
759
+ const size = this._tabSize();
760
+ const newLines = this._lines.slice();
761
+ for (let i = a.line; i <= endLine; i++) {
762
+ const line = newLines[i];
763
+ if (dir > 0) {
764
+ if (line.length > 0)
765
+ newLines[i] = unit + line;
766
+ }
767
+ else {
768
+ if (line.startsWith('\t'))
769
+ newLines[i] = line.slice(1);
770
+ else {
771
+ let strip = 0;
772
+ while (strip < size && line.charAt(strip) === ' ')
773
+ strip++;
774
+ newLines[i] = line.slice(strip);
775
+ }
776
+ }
777
+ }
778
+ this._replaceAllPreservingSelection(newLines, a.line, endLine);
779
+ }
780
+ toggleLineComment() {
781
+ this._syncProps();
782
+ if (this.props.readOnly)
783
+ return;
784
+ const marker = this.language?.data?.lineComment;
785
+ if (!marker)
786
+ return;
787
+ const [a, b] = this._selectionRange();
788
+ const endLine = b.line > a.line && b.ch === 0 ? b.line - 1 : b.line;
789
+ const targets = [];
790
+ for (let i = a.line; i <= endLine; i++) {
791
+ if (this._lines[i].trim().length > 0)
792
+ targets.push(i);
793
+ }
794
+ if (targets.length === 0)
795
+ return;
796
+ const allCommented = targets.every((i) => this._lines[i].trimStart().startsWith(marker));
797
+ const newLines = this._lines.slice();
798
+ for (const i of targets) {
799
+ const line = newLines[i];
800
+ const indent = line.length - line.trimStart().length;
801
+ if (allCommented) {
802
+ const rest = line.slice(indent + marker.length);
803
+ newLines[i] =
804
+ line.slice(0, indent) + (rest.startsWith(' ') ? rest.slice(1) : rest);
805
+ }
806
+ else {
807
+ newLines[i] = line.slice(0, indent) + marker + ' ' + line.slice(indent);
808
+ }
809
+ }
810
+ this._replaceAllPreservingSelection(newLines, a.line, endLine);
811
+ }
812
+ /** Line-rewriting commands share this: one undo step, selection kept on
813
+ * the same lines (clamped to their new lengths). */
814
+ _replaceAllPreservingSelection(newLines, fromLine, toLine) {
815
+ const caret = this._caret;
816
+ const anchor = this._anchor;
817
+ this._applyLines(newLines, {
818
+ fromLine,
819
+ removed: toLine - fromLine + 1,
820
+ inserted: toLine - fromLine + 1,
821
+ });
822
+ this._caret = clampPos(this._lines, caret);
823
+ this._anchor = clampPos(this._lines, anchor);
824
+ this._undoRun = null;
825
+ const value = this._lines.join('\n');
826
+ this._pushHistory({ value, caret: this._caret, anchor: this._anchor });
827
+ this._afterEdit(value);
828
+ }
829
+ // --- input: keyboard -----------------------------------------------------
830
+ /**
831
+ * The editor's own key behaviour, run by the EventManager after the app's
832
+ * `onKeyDown` handlers and not at all if one of them called
833
+ * `preventDefault()` — so `<CodeEditor>`'s completion popup vetoes this
834
+ * simply by consuming the key first, and so does an application handler.
835
+ *
836
+ * Tab is an ordinary key here, ahead of focus traversal, which is what
837
+ * lets a bare `<codeeditor>` indent with it. Owning Tab means owning the
838
+ * keyboard user's way out, hence the convention `docs/extending.md` asks
839
+ * elements to converge on: **Escape arms one pass-through Tab**. Escape on
840
+ * its own does nothing visible, the next Tab leaves, and the one after
841
+ * that indents again.
842
+ */
843
+ defaultKeyDown(ev) {
844
+ if (this.props.disabled)
845
+ return;
846
+ const k = ev.keysym;
847
+ if (k === XK_ESCAPE) {
848
+ this._tabEscapes = true;
849
+ return;
850
+ }
851
+ if (k === XK_TAB && this._tabEscapes) {
852
+ this._tabEscapes = false;
853
+ return; // this one belongs to the focus cycle
854
+ }
855
+ this._tabEscapes = false;
856
+ if (this.handleKeyDown(ev))
857
+ ev.preventDefault?.();
858
+ }
859
+ /**
860
+ * The editor's key behaviour as a predicate — true when the key was
861
+ * consumed. Kept apart from {@link defaultKeyDown} because the editing
862
+ * model is also driven directly in tests and by an app that wires the raw
863
+ * element itself; the default action is this plus the Tab/Escape bargain.
864
+ */
865
+ handleKeyDown(ev) {
866
+ this._syncProps();
867
+ const previousNative = this._keyNative;
868
+ this._keyNative = ev.nativeEvent ?? null;
869
+ try {
870
+ return this._editKey(ev);
871
+ }
872
+ finally {
873
+ this._keyNative = previousNative;
874
+ }
875
+ }
876
+ _editKey(ev) {
877
+ const k = ev.keysym;
878
+ const readOnly = Boolean(this.props.readOnly);
879
+ const [a, b] = this._selectionRange();
880
+ const hasSelection = this._hasSelection();
881
+ const wordChars = this.language?.data?.wordChars ?? '';
882
+ if (k === XK_RETURN || k === XK_KP_ENTER) {
883
+ if (ev.ctrlKey) {
884
+ this._fire('onSubmit', this.value);
885
+ return true;
886
+ }
887
+ if (readOnly)
888
+ return false;
889
+ const lineText = this._lines[a.line];
890
+ const indentMatch = /^[\t ]*/.exec(lineText)?.[0] ?? '';
891
+ const indent = indentMatch.slice(0, Math.min(indentMatch.length, a.ch));
892
+ const before = lineText.slice(0, a.ch);
893
+ const rule = this.language?.data?.indentAfter;
894
+ const deeper = rule ? rule.test(before) : false;
895
+ const after = this._lines[b.line].slice(b.ch);
896
+ if (deeper && /^[)\]}]/.test(after)) {
897
+ // the caret sits between a fresh pair — `{|}` — so Enter opens it
898
+ // the way every editor does: an indented line for the caret, the
899
+ // closer on its own line at the old depth
900
+ const unit = this._indentUnit();
901
+ this.replaceRange(a, b, '\n' + indent + unit + '\n' + indent);
902
+ this.moveCaret({ line: a.line + 1, ch: indent.length + unit.length }, false);
903
+ return true;
904
+ }
905
+ this.insertText('\n' + indent + (deeper ? this._indentUnit() : ''));
906
+ return true;
907
+ }
908
+ if (k === XK_BACKSPACE) {
909
+ if (readOnly)
910
+ return true;
911
+ if (hasSelection)
912
+ this.replaceRange(a, b, '');
913
+ else if (ev.ctrlKey) {
914
+ this.replaceRange(wordBoundary(this._lines, a, -1, wordChars), a, '');
915
+ }
916
+ else {
917
+ const from = movePos(this._lines, a, -1);
918
+ if (!posEqual(from, a))
919
+ this.replaceRange(from, a, '', 'delete-back');
920
+ }
921
+ return true;
922
+ }
923
+ if (k === XK_DELETE) {
924
+ if (readOnly)
925
+ return true;
926
+ if (hasSelection)
927
+ this.replaceRange(a, b, '');
928
+ else if (ev.ctrlKey) {
929
+ this.replaceRange(a, wordBoundary(this._lines, a, 1, wordChars), '');
930
+ }
931
+ else {
932
+ const to = movePos(this._lines, a, 1);
933
+ if (!posEqual(to, a))
934
+ this.replaceRange(a, to, '', 'delete-forward');
935
+ }
936
+ return true;
937
+ }
938
+ if (k === XK_LEFT || k === XK_RIGHT) {
939
+ const dir = k === XK_LEFT ? -1 : 1;
940
+ let target;
941
+ if (ev.ctrlKey) {
942
+ target = wordBoundary(this._lines, this._caret, dir, wordChars);
943
+ }
944
+ else if (!ev.shiftKey && hasSelection) {
945
+ target = dir < 0 ? a : b;
946
+ }
947
+ else {
948
+ target = movePos(this._lines, this._caret, dir);
949
+ }
950
+ this.moveCaret(target, ev.shiftKey);
951
+ if (ev.shiftKey)
952
+ this._ownPrimary();
953
+ return true;
954
+ }
955
+ if (k === XK_UP || k === XK_DOWN) {
956
+ this._verticalMove(k === XK_UP ? -1 : 1, ev.shiftKey);
957
+ return true;
958
+ }
959
+ if (k === XK_PAGE_UP || k === XK_PAGE_DOWN) {
960
+ const content = this._contentRect();
961
+ const page = Math.max(1, Math.floor(content.height / this._lineHeight()) - 1);
962
+ this._verticalMove((k === XK_PAGE_UP ? -1 : 1) * page, ev.shiftKey);
963
+ return true;
964
+ }
965
+ if (k === XK_HOME) {
966
+ if (ev.ctrlKey) {
967
+ this.moveCaret({ line: 0, ch: 0 }, ev.shiftKey);
968
+ return true;
969
+ }
970
+ const text = this._lines[this._caret.line];
971
+ const first = text.length - text.trimStart().length;
972
+ const ch = this._caret.ch === first ? 0 : first;
973
+ this.moveCaret({ line: this._caret.line, ch }, ev.shiftKey);
974
+ return true;
975
+ }
976
+ if (k === XK_END) {
977
+ if (ev.ctrlKey) {
978
+ const last = this._lines.length - 1;
979
+ this.moveCaret({ line: last, ch: this._lines[last].length }, ev.shiftKey);
980
+ return true;
981
+ }
982
+ this.moveCaret({ line: this._caret.line, ch: this._lines[this._caret.line].length }, ev.shiftKey);
983
+ return true;
984
+ }
985
+ if (k === XK_TAB) {
986
+ if (readOnly)
987
+ return false;
988
+ if (ev.shiftKey) {
989
+ this.indentSelection(-1);
990
+ return true;
991
+ }
992
+ if (a.line !== b.line) {
993
+ this.indentSelection(1);
994
+ return true;
995
+ }
996
+ if (this.props.insertSpaces === false) {
997
+ this.insertText('\t', 'type');
998
+ return true;
999
+ }
1000
+ // spaces to the next tab stop, measured in display columns
1001
+ const map = tabMap(this._lines[a.line].slice(0, a.ch), this._tabSize());
1002
+ const col = map.display.length;
1003
+ const size = this._tabSize();
1004
+ this.insertText(' '.repeat(size - (col % size)), 'type');
1005
+ return true;
1006
+ }
1007
+ if (k === XK_ESCAPE) {
1008
+ return false; // the component owns Escape (completion, then Tab-out)
1009
+ }
1010
+ if (ev.ctrlKey) {
1011
+ const letter = ctrlChordLetter(ev);
1012
+ if (letter === 0x61 /* a */) {
1013
+ this.selectAll();
1014
+ return true;
1015
+ }
1016
+ if (letter === 0x63 /* c */) {
1017
+ this.copySelection();
1018
+ return true;
1019
+ }
1020
+ if (letter === 0x78 /* x */) {
1021
+ this.copySelection();
1022
+ if (!readOnly && hasSelection)
1023
+ this.replaceRange(a, b, '');
1024
+ return true;
1025
+ }
1026
+ if (letter === 0x76 /* v */) {
1027
+ if (!readOnly)
1028
+ this.pasteFrom();
1029
+ return true;
1030
+ }
1031
+ if (letter === 0x7a /* z */) {
1032
+ if (!readOnly) {
1033
+ if (ev.shiftKey)
1034
+ this.redo();
1035
+ else
1036
+ this.undo();
1037
+ }
1038
+ return true;
1039
+ }
1040
+ if (letter === 0x79 /* y */) {
1041
+ if (!readOnly)
1042
+ this.redo();
1043
+ return true;
1044
+ }
1045
+ if (letter === 0x2f /* / */) {
1046
+ this.toggleLineComment();
1047
+ return true;
1048
+ }
1049
+ return false;
1050
+ }
1051
+ if (!readOnly &&
1052
+ ev.codepoint != null &&
1053
+ ev.codepoint >= 0x20 &&
1054
+ ev.codepoint !== 0x7f) {
1055
+ const ch = String.fromCodePoint(ev.codepoint);
1056
+ this.insertText(ch, 'type');
1057
+ if (/\s/.test(ch))
1058
+ this.breakUndoRun();
1059
+ return true;
1060
+ }
1061
+ return false;
1062
+ }
1063
+ _verticalMove(deltaLines, extend) {
1064
+ if (this._goalX == null)
1065
+ this._goalX = this._caretX(this._caret);
1066
+ const line = Math.max(0, Math.min(this._lines.length - 1, this._caret.line + deltaLines));
1067
+ if (line === this._caret.line) {
1068
+ // pressing against the document's edge: land on its corner, the way
1069
+ // every editor does
1070
+ const ch = deltaLines < 0 ? 0 : this._lines[line].length;
1071
+ this.moveCaret({ line, ch }, extend);
1072
+ if (extend)
1073
+ this._ownPrimary();
1074
+ return;
1075
+ }
1076
+ const entry = this._lineEntry(line);
1077
+ let ch = 0;
1078
+ if (entry.layout) {
1079
+ const cp = entry.layout.indexAt(this._goalX, 0);
1080
+ ch = entry.map.toRaw(cpToUtf16(entry.display, cp));
1081
+ }
1082
+ const goal = this._goalX; // moveCaret clears it; this move keeps it
1083
+ this.moveCaret({ line, ch }, extend);
1084
+ this._goalX = goal;
1085
+ if (extend)
1086
+ this._ownPrimary();
1087
+ }
1088
+ // --- input: mouse --------------------------------------------------------
1089
+ /**
1090
+ * A press: place the caret, start a drag-selection, or paste PRIMARY on
1091
+ * the middle button. The drag and release below are the continuation of
1092
+ * *this* press — core vetoes a gesture once, at its press, so an editor
1093
+ * whose `defaultMouseDown` never ran hears neither, and needs no
1094
+ * `dragging` flag to discover that.
1095
+ */
1096
+ defaultMouseDown(ev) {
1097
+ if (this.props.disabled)
1098
+ return;
1099
+ this.focus();
1100
+ if (this.handleMouseDown(ev))
1101
+ ev.preventDefault?.();
1102
+ }
1103
+ defaultMouseDrag(ev) {
1104
+ if (this.handleMouseMove(ev))
1105
+ ev.preventDefault?.();
1106
+ }
1107
+ defaultMouseUp() {
1108
+ this.handleMouseUp();
1109
+ }
1110
+ handleMouseDown(ev) {
1111
+ this._syncProps();
1112
+ this.breakUndoRun();
1113
+ const pos = this._posAt(ev.x, ev.y);
1114
+ if (ev.button === 3) {
1115
+ const [a, b] = this._selectionRange();
1116
+ if (comparePos(pos, a) < 0 || comparePos(pos, b) > 0) {
1117
+ this.moveCaret(pos, false);
1118
+ }
1119
+ return true;
1120
+ }
1121
+ if (ev.button === 2) {
1122
+ this.moveCaret(pos, false);
1123
+ if (!this.props.readOnly)
1124
+ this.pasteFrom('PRIMARY');
1125
+ return true;
1126
+ }
1127
+ const detail = ev.detail ?? 1;
1128
+ if (detail >= 3) {
1129
+ this._drag = 'line';
1130
+ this._dragOrigin = pos;
1131
+ this._selectLineRange(pos, pos);
1132
+ }
1133
+ else if (detail === 2) {
1134
+ this._drag = 'word';
1135
+ this._dragOrigin = pos;
1136
+ this._selectWordRange(pos, pos);
1137
+ }
1138
+ else {
1139
+ this._drag = 'char';
1140
+ this._dragOrigin = pos;
1141
+ this.moveCaret(pos, ev.shiftKey);
1142
+ }
1143
+ ev.capturePointer?.();
1144
+ return true;
1145
+ }
1146
+ handleMouseMove(ev) {
1147
+ if (!this._drag)
1148
+ return false;
1149
+ const pos = this._posAt(ev.x, ev.y);
1150
+ if (this._drag === 'char')
1151
+ this.moveCaret(pos, true);
1152
+ else if (this._drag === 'word')
1153
+ this._selectWordRange(this._dragOrigin, pos);
1154
+ else
1155
+ this._selectLineRange(this._dragOrigin, pos);
1156
+ return true;
1157
+ }
1158
+ handleMouseUp() {
1159
+ if (!this._drag)
1160
+ return false;
1161
+ this._drag = null;
1162
+ this._ownPrimary();
1163
+ return true;
1164
+ }
1165
+ _selectWordRange(origin, to) {
1166
+ const extra = this.language?.data?.wordChars ?? '';
1167
+ const w1 = wordRangeAt(this._lines[origin.line], origin.ch, extra);
1168
+ const w2 = wordRangeAt(this._lines[to.line], to.ch, extra);
1169
+ const start = { line: origin.line, ch: w1.from };
1170
+ const end = { line: to.line, ch: w2.to };
1171
+ if (comparePos(to, origin) < 0) {
1172
+ this.select({ line: origin.line, ch: w1.to }, { line: to.line, ch: w2.from });
1173
+ }
1174
+ else {
1175
+ this.select(start, end);
1176
+ }
1177
+ }
1178
+ _selectLineRange(origin, to) {
1179
+ const [first, last] = to.line < origin.line ? [to.line, origin.line] : [origin.line, to.line];
1180
+ if (last + 1 < this._lines.length) {
1181
+ this.select({ line: first, ch: 0 }, { line: last + 1, ch: 0 });
1182
+ }
1183
+ else {
1184
+ this.select({ line: first, ch: 0 }, { line: last, ch: this._lines[last].length });
1185
+ }
1186
+ }
1187
+ // --- focus ---------------------------------------------------------------
1188
+ // `focus()`/`blur()` come from `Node` — declared as well as implemented
1189
+ // since react-x11#267, so the forwarding shims this file used to carry are
1190
+ // gone. `CodeEditorHandle` keeps promising `void`: the base returns `this`,
1191
+ // and widening the handle to the node type would drag the class's nominal
1192
+ // type into the exported props (see the note on `CodeEditorHandle`).
1193
+ /** Focus is a state, not an event: `onFocus` is where the app hears about
1194
+ * it, and this is where the caret starts blinking. The timer is also
1195
+ * released in `destroySubtree`, because a node that unmounts while focused
1196
+ * is forgotten rather than blurred and this would never run. */
1197
+ defaultFocus() {
1198
+ this.handleFocus();
1199
+ }
1200
+ defaultBlur() {
1201
+ this.handleBlur();
1202
+ }
1203
+ handleFocus() {
1204
+ this._focused = true;
1205
+ this._caretOn = true;
1206
+ if (this._blinkTimer == null) {
1207
+ this._blinkTimer = startInterval(() => {
1208
+ this._caretOn = !this._caretOn;
1209
+ this.root?.invalidate(false, this.abs, 'text');
1210
+ }, CARET_BLINK_MS);
1211
+ }
1212
+ this._repaint();
1213
+ }
1214
+ handleBlur() {
1215
+ this._focused = false;
1216
+ this.breakUndoRun();
1217
+ stopInterval(this._blinkTimer);
1218
+ this._blinkTimer = null;
1219
+ this._repaint();
1220
+ }
1221
+ // --- lifecycle -----------------------------------------------------------
1222
+ applyProps(nextProps, prevProps) {
1223
+ const before = prevProps ?? this.props;
1224
+ super.applyProps(nextProps, prevProps);
1225
+ if (nextProps.value != null)
1226
+ this._syncProps();
1227
+ if (nextProps.language !== before.language) {
1228
+ this._tokenizer(); // re-resolves against the new props
1229
+ this._repaint();
1230
+ }
1231
+ if (nextProps.rows !== before.rows)
1232
+ this.invalidateMeasure('props');
1233
+ if (nextProps.tokenStyles !== before.tokenStyles ||
1234
+ nextProps.diagnostics !== before.diagnostics ||
1235
+ nextProps.tabSize !== before.tabSize ||
1236
+ nextProps.lineNumbers !== before.lineNumbers) {
1237
+ this._lineCache.clear();
1238
+ this._repaint();
1239
+ }
1240
+ }
1241
+ destroySubtree() {
1242
+ stopInterval(this._blinkTimer);
1243
+ this._blinkTimer = null;
1244
+ this._tok?.dispose?.();
1245
+ this._tok = null;
1246
+ super.destroySubtree();
1247
+ }
1248
+ // --- painting ------------------------------------------------------------
1249
+ paint(ctx) {
1250
+ super.paint(ctx);
1251
+ this._syncProps();
1252
+ this._metrics();
1253
+ const c = ctx;
1254
+ if (typeof c.fillRect !== 'function')
1255
+ return; // mock backend: geometry only
1256
+ const content = this._contentRect();
1257
+ if (content.width <= 0 || content.height <= 0)
1258
+ return;
1259
+ const lineH = this._lineH;
1260
+ const gutterW = this._gutterWidth();
1261
+ const textX = content.x + gutterW - this._scrollX;
1262
+ const props = this.props;
1263
+ const styles = this._textStyle();
1264
+ const baseColor = styles.color;
1265
+ const canClip = typeof c.save === 'function' &&
1266
+ typeof c.beginPath === 'function' &&
1267
+ typeof c.rect === 'function' &&
1268
+ typeof c.clip === 'function';
1269
+ const clipTo = (x, y, w, h) => {
1270
+ if (!canClip)
1271
+ return false;
1272
+ c.save();
1273
+ c.beginPath();
1274
+ c.rect(x, y, w, h);
1275
+ c.clip();
1276
+ return true;
1277
+ };
1278
+ const dark = this._onDark();
1279
+ const first = Math.max(0, Math.floor(this._scrollY / lineH));
1280
+ const last = Math.min(this._lines.length - 1, Math.ceil((this._scrollY + content.height) / lineH));
1281
+ const lineY = (i) => content.y + i * lineH - this._scrollY;
1282
+ // Everything the editor draws — gutter included — stays inside the
1283
+ // content box. The first and last visible rows are partial when
1284
+ // scrolled, and without this their overhang (up to a line each way)
1285
+ // walks into the padding and past it.
1286
+ const outerClip = clipTo(content.x, content.y, content.width, content.height);
1287
+ // gutter background
1288
+ if (gutterW > 0 && props.gutterBackground) {
1289
+ c.fillStyle = this._resolveColor(props.gutterBackground) ?? '#00000008';
1290
+ c.fillRect(content.x, content.y, gutterW, content.height);
1291
+ }
1292
+ // the text region clips one step narrower, so a horizontally scrolled
1293
+ // line cannot run underneath the line numbers
1294
+ const textClip = clipTo(content.x + gutterW, content.y, content.width - gutterW, content.height);
1295
+ // active line
1296
+ if (props.activeLine &&
1297
+ this._focused &&
1298
+ !this._hasSelection() &&
1299
+ this._caret.line >= first &&
1300
+ this._caret.line <= last) {
1301
+ c.fillStyle =
1302
+ this._resolveColor(props.activeLineColor) ??
1303
+ (dark ? 'rgba(255,255,255,0.06)' : 'rgba(120,140,180,0.10)');
1304
+ c.fillRect(content.x + gutterW, lineY(this._caret.line), content.width - gutterW, lineH);
1305
+ }
1306
+ // selection bands
1307
+ if (this._hasSelection()) {
1308
+ const [a, b] = this._selectionRange();
1309
+ c.fillStyle =
1310
+ this._resolveColor(props.selectionColor) ??
1311
+ (dark ? 'rgba(82,139,255,0.35)' : 'rgba(80,140,255,0.28)');
1312
+ for (let i = Math.max(a.line, first); i <= Math.min(b.line, last); i++) {
1313
+ const entry = this._lineEntry(i);
1314
+ const startX = i === a.line && entry.layout
1315
+ ? this._caretX({ line: i, ch: a.ch })
1316
+ : 0;
1317
+ const endX = i === b.line
1318
+ ? this._caretX({ line: i, ch: b.ch })
1319
+ : (entry.layout?.width ?? 0) + this._charW * 0.5; // the newline
1320
+ c.fillRect(textX + startX, lineY(i), Math.max(2, endX - startX), lineH);
1321
+ }
1322
+ }
1323
+ // matching brackets
1324
+ if (props.matchBrackets !== false && this._focused) {
1325
+ const match = this._findBracketMatch();
1326
+ if (match) {
1327
+ c.fillStyle =
1328
+ this._resolveColor(props.matchingBracketColor) ??
1329
+ (dark ? 'rgba(90,190,130,0.35)' : 'rgba(80,180,120,0.30)');
1330
+ for (const pos of match) {
1331
+ if (pos.line < first || pos.line > last)
1332
+ continue;
1333
+ const x0 = this._caretX(pos);
1334
+ const x1 = this._caretX({ line: pos.line, ch: pos.ch + 1 });
1335
+ c.fillRect(textX + x0, lineY(pos.line), Math.max(1, x1 - x0), lineH);
1336
+ }
1337
+ }
1338
+ }
1339
+ // text
1340
+ const empty = this._lines.length === 1 &&
1341
+ this._lines[0].length === 0 &&
1342
+ props.placeholder;
1343
+ if (empty) {
1344
+ const fonts = this._fonts();
1345
+ if (fonts) {
1346
+ const layout = fonts.layout([
1347
+ {
1348
+ text: String(props.placeholder),
1349
+ color: this._resolveColor(props.placeholderColor) ??
1350
+ this._themeColor('dim') ??
1351
+ '#9aa0a6',
1352
+ },
1353
+ ], styles);
1354
+ layout.draw(ctx, textX, lineY(0) + (lineH - layout.height) / 2);
1355
+ }
1356
+ }
1357
+ else {
1358
+ for (let i = first; i <= last; i++) {
1359
+ const entry = this._lineEntry(i);
1360
+ entry.layout?.draw(ctx, textX, lineY(i) + (lineH - (entry.layout.height || lineH)) / 2);
1361
+ }
1362
+ }
1363
+ // diagnostics
1364
+ const diagnostics = props.diagnostics ?? [];
1365
+ for (const d of diagnostics) {
1366
+ const color = SEVERITY_COLORS[d.severity ?? 'error'] ?? '#e5484d';
1367
+ const from = clampPos(this._lines, d.from);
1368
+ const to = clampPos(this._lines, maxPos(d.from, d.to));
1369
+ for (let i = Math.max(from.line, first); i <= Math.min(to.line, last); i++) {
1370
+ const x0 = i === from.line ? this._caretX({ line: i, ch: from.ch }) : 0;
1371
+ const lineText = this._lines[i];
1372
+ const x1 = i === to.line
1373
+ ? this._caretX({ line: i, ch: to.ch })
1374
+ : this._caretX({ line: i, ch: lineText.length });
1375
+ // hug the glyphs, not the line box: the text is centred in its
1376
+ // band, so a squiggle at the band's bottom floats visibly below
1377
+ // short text — measure where the drawn layout actually ends
1378
+ const entry = this._lineEntry(i);
1379
+ const glyphBottom = lineY(i) +
1380
+ (lineH - (entry.layout?.height || lineH)) / 2 +
1381
+ (entry.layout?.height || lineH);
1382
+ const y = Math.min(lineY(i) + lineH - 2, glyphBottom - 1);
1383
+ const width = Math.max(this._charW * 0.6, x1 - x0);
1384
+ if (typeof c.beginPath === 'function' &&
1385
+ typeof c.moveTo === 'function' &&
1386
+ typeof c.lineTo === 'function' &&
1387
+ typeof c.stroke === 'function') {
1388
+ c.beginPath();
1389
+ const step = 4;
1390
+ let up = true;
1391
+ for (let x = 0; x <= width; x += step) {
1392
+ const px = textX + x0 + x;
1393
+ const py = y + (up ? 0 : 2);
1394
+ if (x === 0)
1395
+ c.moveTo(px, py);
1396
+ else
1397
+ c.lineTo(px, py);
1398
+ up = !up;
1399
+ }
1400
+ c.strokeStyle = color;
1401
+ c.lineWidth = 1;
1402
+ c.stroke();
1403
+ }
1404
+ else {
1405
+ c.fillStyle = color;
1406
+ c.fillRect(textX + x0, y + 1, width, 1);
1407
+ }
1408
+ }
1409
+ }
1410
+ // caret — 2px, in the (theme-resolved) text colour
1411
+ if (this._focused && this._caretOn) {
1412
+ const x = this._caretX(this._caret);
1413
+ c.fillStyle = this._resolveColor(props.caretColor) ?? baseColor;
1414
+ c.fillRect(Math.round(textX + x), lineY(this._caret.line) + 1, 2, lineH - 2);
1415
+ }
1416
+ if (textClip)
1417
+ c.restore();
1418
+ // gutter numbers: inside the content clip, outside the text clip
1419
+ if (gutterW > 0) {
1420
+ const fonts = this._fonts();
1421
+ if (fonts) {
1422
+ const dim = this._resolveColor(props.gutterColor) ??
1423
+ this._themeColor('dim') ??
1424
+ '#9aa0a6';
1425
+ for (let i = first; i <= last; i++) {
1426
+ const active = i === this._caret.line;
1427
+ const layout = fonts.layout([
1428
+ {
1429
+ text: String(i + 1),
1430
+ color: active ? baseColor : dim,
1431
+ },
1432
+ ], styles);
1433
+ layout.draw(ctx, content.x + gutterW - GUTTER_PAD - layout.width, lineY(i) + (lineH - layout.height) / 2);
1434
+ }
1435
+ }
1436
+ }
1437
+ // scroll thumbs — minimal, panel-style
1438
+ const maxY = this._maxScrollY();
1439
+ if (maxY > 0) {
1440
+ const trackH = content.height;
1441
+ const thumbH = Math.max(24, (trackH * trackH) / (this._lines.length * lineH));
1442
+ const yOff = (this._scrollY / maxY) * (trackH - thumbH);
1443
+ c.fillStyle = 'rgba(128,128,128,0.35)';
1444
+ c.fillRect(content.x + content.width - 4, content.y + yOff, 3, thumbH);
1445
+ }
1446
+ const maxX = this._maxScrollX();
1447
+ if (maxX > 0) {
1448
+ const textW = content.width - gutterW;
1449
+ const trackW = textW;
1450
+ const thumbW = Math.max(24, (trackW * trackW) / (this._widest + CARET_MARGIN));
1451
+ const xOff = (this._scrollX / maxX) * (trackW - thumbW);
1452
+ c.fillStyle = 'rgba(128,128,128,0.35)';
1453
+ c.fillRect(content.x + gutterW + xOff, content.y + content.height - 4, thumbW, 3);
1454
+ }
1455
+ if (outerClip)
1456
+ c.restore();
1457
+ }
1458
+ /** The bracket pair to highlight: the caret sits beside one of them. */
1459
+ _findBracketMatch() {
1460
+ const pos = this._caret;
1461
+ const line = this._lines[pos.line];
1462
+ const before = pos.ch > 0 ? line.charAt(pos.ch - 1) : '';
1463
+ const after = pos.ch < line.length ? line.charAt(pos.ch) : '';
1464
+ let at;
1465
+ let ch;
1466
+ if (after in BRACKETS) {
1467
+ at = { line: pos.line, ch: pos.ch };
1468
+ ch = after;
1469
+ }
1470
+ else if (before in BRACKETS) {
1471
+ at = { line: pos.line, ch: pos.ch - 1 };
1472
+ ch = before;
1473
+ }
1474
+ else {
1475
+ return null;
1476
+ }
1477
+ if (this._isOpaqueAt(at))
1478
+ return null;
1479
+ const { match, dir } = BRACKETS[ch];
1480
+ let depth = 0;
1481
+ let scanned = 0;
1482
+ let l = at.line;
1483
+ let i = at.ch;
1484
+ while (l >= 0 && l < this._lines.length && scanned < MATCH_SCAN_LIMIT) {
1485
+ const text = this._lines[l];
1486
+ while (dir > 0 ? i < text.length : i >= 0) {
1487
+ const cur = text.charAt(i);
1488
+ if ((cur === ch || cur === match) &&
1489
+ !this._isOpaqueAt({ line: l, ch: i })) {
1490
+ if (cur === ch)
1491
+ depth++;
1492
+ else {
1493
+ depth--;
1494
+ if (depth === 0)
1495
+ return [at, { line: l, ch: i }];
1496
+ }
1497
+ }
1498
+ i += dir;
1499
+ scanned++;
1500
+ }
1501
+ l += dir;
1502
+ if (l >= 0 && l < this._lines.length) {
1503
+ i = dir > 0 ? 0 : this._lines[l].length - 1;
1504
+ }
1505
+ }
1506
+ return null;
1507
+ }
1508
+ _isOpaqueAt(pos) {
1509
+ const tokens = this._tokenizer()?.lineTokens(pos.line);
1510
+ if (!tokens)
1511
+ return false;
1512
+ for (const t of tokens) {
1513
+ if (t.from <= pos.ch && pos.ch < t.to)
1514
+ return OPAQUE_TOKENS.has(t.type);
1515
+ if (t.from > pos.ch)
1516
+ break;
1517
+ }
1518
+ return false;
1519
+ }
1520
+ }
1521
+ //# sourceMappingURL=node.js.map