@textui/core 0.1.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/adapters/index.d.ts +12 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +11 -0
  6. package/dist/app/app.d.ts +200 -0
  7. package/dist/app/app.d.ts.map +1 -0
  8. package/dist/app/app.js +980 -0
  9. package/dist/core/animation.d.ts +34 -0
  10. package/dist/core/animation.d.ts.map +1 -0
  11. package/dist/core/animation.js +125 -0
  12. package/dist/core/clipboard.d.ts +29 -0
  13. package/dist/core/clipboard.d.ts.map +1 -0
  14. package/dist/core/clipboard.js +30 -0
  15. package/dist/core/commands.d.ts +42 -0
  16. package/dist/core/commands.d.ts.map +1 -0
  17. package/dist/core/commands.js +146 -0
  18. package/dist/core/components.d.ts +27 -0
  19. package/dist/core/components.d.ts.map +1 -0
  20. package/dist/core/components.js +78 -0
  21. package/dist/core/events.d.ts +23 -0
  22. package/dist/core/events.d.ts.map +1 -0
  23. package/dist/core/events.js +77 -0
  24. package/dist/core/focus.d.ts +69 -0
  25. package/dist/core/focus.d.ts.map +1 -0
  26. package/dist/core/focus.js +336 -0
  27. package/dist/core/i18n.d.ts +27 -0
  28. package/dist/core/i18n.d.ts.map +1 -0
  29. package/dist/core/i18n.js +88 -0
  30. package/dist/core/keybindings.d.ts +53 -0
  31. package/dist/core/keybindings.d.ts.map +1 -0
  32. package/dist/core/keybindings.js +163 -0
  33. package/dist/core/layers.d.ts +28 -0
  34. package/dist/core/layers.d.ts.map +1 -0
  35. package/dist/core/layers.js +84 -0
  36. package/dist/core/manifest.d.ts +22 -0
  37. package/dist/core/manifest.d.ts.map +1 -0
  38. package/dist/core/manifest.js +85 -0
  39. package/dist/core/navigation.d.ts +47 -0
  40. package/dist/core/navigation.d.ts.map +1 -0
  41. package/dist/core/navigation.js +110 -0
  42. package/dist/core/resources.d.ts +115 -0
  43. package/dist/core/resources.d.ts.map +1 -0
  44. package/dist/core/resources.js +321 -0
  45. package/dist/core/services.d.ts +21 -0
  46. package/dist/core/services.d.ts.map +1 -0
  47. package/dist/core/services.js +63 -0
  48. package/dist/core/store.d.ts +60 -0
  49. package/dist/core/store.d.ts.map +1 -0
  50. package/dist/core/store.js +593 -0
  51. package/dist/core/surfaces.d.ts +81 -0
  52. package/dist/core/surfaces.d.ts.map +1 -0
  53. package/dist/core/surfaces.js +237 -0
  54. package/dist/core/syntax.d.ts +49 -0
  55. package/dist/core/syntax.d.ts.map +1 -0
  56. package/dist/core/syntax.js +172 -0
  57. package/dist/core/when.d.ts +15 -0
  58. package/dist/core/when.d.ts.map +1 -0
  59. package/dist/core/when.js +224 -0
  60. package/dist/index.d.ts +49 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +54 -0
  63. package/dist/jsx/factory.d.ts +27 -0
  64. package/dist/jsx/factory.d.ts.map +1 -0
  65. package/dist/jsx/factory.js +102 -0
  66. package/dist/jsx/intrinsics.d.ts +96 -0
  67. package/dist/jsx/intrinsics.d.ts.map +1 -0
  68. package/dist/jsx/intrinsics.js +1 -0
  69. package/dist/jsx/jsx-dev-runtime.d.ts +13 -0
  70. package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
  71. package/dist/jsx/jsx-dev-runtime.js +12 -0
  72. package/dist/jsx/jsx-runtime.d.ts +42 -0
  73. package/dist/jsx/jsx-runtime.d.ts.map +1 -0
  74. package/dist/jsx/jsx-runtime.js +16 -0
  75. package/dist/render/buffer.d.ts +77 -0
  76. package/dist/render/buffer.d.ts.map +1 -0
  77. package/dist/render/buffer.js +275 -0
  78. package/dist/render/color.d.ts +35 -0
  79. package/dist/render/color.d.ts.map +1 -0
  80. package/dist/render/color.js +160 -0
  81. package/dist/render/diff.d.ts +37 -0
  82. package/dist/render/diff.d.ts.map +1 -0
  83. package/dist/render/diff.js +66 -0
  84. package/dist/render/layout.d.ts +72 -0
  85. package/dist/render/layout.d.ts.map +1 -0
  86. package/dist/render/layout.js +633 -0
  87. package/dist/render/static.d.ts +63 -0
  88. package/dist/render/static.d.ts.map +1 -0
  89. package/dist/render/static.js +209 -0
  90. package/dist/runtime/bindings.d.ts +46 -0
  91. package/dist/runtime/bindings.d.ts.map +1 -0
  92. package/dist/runtime/bindings.js +116 -0
  93. package/dist/runtime/hooks.d.ts +284 -0
  94. package/dist/runtime/hooks.d.ts.map +1 -0
  95. package/dist/runtime/hooks.js +846 -0
  96. package/dist/runtime/instance.d.ts +106 -0
  97. package/dist/runtime/instance.d.ts.map +1 -0
  98. package/dist/runtime/instance.js +181 -0
  99. package/dist/runtime/paint.d.ts +27 -0
  100. package/dist/runtime/paint.d.ts.map +1 -0
  101. package/dist/runtime/paint.js +567 -0
  102. package/dist/runtime/reconcile.d.ts +23 -0
  103. package/dist/runtime/reconcile.d.ts.map +1 -0
  104. package/dist/runtime/reconcile.js +260 -0
  105. package/dist/runtime/runtime.d.ts +42 -0
  106. package/dist/runtime/runtime.d.ts.map +1 -0
  107. package/dist/runtime/runtime.js +1 -0
  108. package/dist/runtime/style.d.ts +55 -0
  109. package/dist/runtime/style.d.ts.map +1 -0
  110. package/dist/runtime/style.js +143 -0
  111. package/dist/themes/borders.d.ts +10 -0
  112. package/dist/themes/borders.d.ts.map +1 -0
  113. package/dist/themes/borders.js +88 -0
  114. package/dist/themes/builtin.d.ts +31 -0
  115. package/dist/themes/builtin.d.ts.map +1 -0
  116. package/dist/themes/builtin.js +291 -0
  117. package/dist/themes/glyphs.d.ts +15 -0
  118. package/dist/themes/glyphs.d.ts.map +1 -0
  119. package/dist/themes/glyphs.js +97 -0
  120. package/dist/themes/index.d.ts +5 -0
  121. package/dist/themes/index.d.ts.map +1 -0
  122. package/dist/themes/index.js +4 -0
  123. package/dist/themes/registry.d.ts +20 -0
  124. package/dist/themes/registry.d.ts.map +1 -0
  125. package/dist/themes/registry.js +204 -0
  126. package/dist/types/adapter.d.ts +38 -0
  127. package/dist/types/adapter.d.ts.map +1 -0
  128. package/dist/types/adapter.js +1 -0
  129. package/dist/types/animation.d.ts +31 -0
  130. package/dist/types/animation.d.ts.map +1 -0
  131. package/dist/types/animation.js +1 -0
  132. package/dist/types/app.d.ts +148 -0
  133. package/dist/types/app.d.ts.map +1 -0
  134. package/dist/types/app.js +1 -0
  135. package/dist/types/async.d.ts +30 -0
  136. package/dist/types/async.d.ts.map +1 -0
  137. package/dist/types/async.js +1 -0
  138. package/dist/types/capabilities.d.ts +44 -0
  139. package/dist/types/capabilities.d.ts.map +1 -0
  140. package/dist/types/capabilities.js +33 -0
  141. package/dist/types/cells.d.ts +72 -0
  142. package/dist/types/cells.d.ts.map +1 -0
  143. package/dist/types/cells.js +10 -0
  144. package/dist/types/command.d.ts +127 -0
  145. package/dist/types/command.d.ts.map +1 -0
  146. package/dist/types/command.js +1 -0
  147. package/dist/types/component-registry.d.ts +77 -0
  148. package/dist/types/component-registry.d.ts.map +1 -0
  149. package/dist/types/component-registry.js +1 -0
  150. package/dist/types/disposable.d.ts +8 -0
  151. package/dist/types/disposable.d.ts.map +1 -0
  152. package/dist/types/disposable.js +1 -0
  153. package/dist/types/focus.d.ts +72 -0
  154. package/dist/types/focus.d.ts.map +1 -0
  155. package/dist/types/focus.js +1 -0
  156. package/dist/types/geometry.d.ts +28 -0
  157. package/dist/types/geometry.d.ts.map +1 -0
  158. package/dist/types/geometry.js +14 -0
  159. package/dist/types/graph.d.ts +129 -0
  160. package/dist/types/graph.d.ts.map +1 -0
  161. package/dist/types/graph.js +31 -0
  162. package/dist/types/i18n.d.ts +23 -0
  163. package/dist/types/i18n.d.ts.map +1 -0
  164. package/dist/types/i18n.js +1 -0
  165. package/dist/types/index.d.ts +32 -0
  166. package/dist/types/index.d.ts.map +1 -0
  167. package/dist/types/index.js +31 -0
  168. package/dist/types/input.d.ts +65 -0
  169. package/dist/types/input.d.ts.map +1 -0
  170. package/dist/types/input.js +1 -0
  171. package/dist/types/keybinding.d.ts +49 -0
  172. package/dist/types/keybinding.d.ts.map +1 -0
  173. package/dist/types/keybinding.js +1 -0
  174. package/dist/types/layer.d.ts +54 -0
  175. package/dist/types/layer.d.ts.map +1 -0
  176. package/dist/types/layer.js +1 -0
  177. package/dist/types/manifest.d.ts +114 -0
  178. package/dist/types/manifest.d.ts.map +1 -0
  179. package/dist/types/manifest.js +1 -0
  180. package/dist/types/markdown.d.ts +58 -0
  181. package/dist/types/markdown.d.ts.map +1 -0
  182. package/dist/types/markdown.js +1 -0
  183. package/dist/types/navigation.d.ts +40 -0
  184. package/dist/types/navigation.d.ts.map +1 -0
  185. package/dist/types/navigation.js +1 -0
  186. package/dist/types/render.d.ts +72 -0
  187. package/dist/types/render.d.ts.map +1 -0
  188. package/dist/types/render.js +1 -0
  189. package/dist/types/resource.d.ts +170 -0
  190. package/dist/types/resource.d.ts.map +1 -0
  191. package/dist/types/resource.js +1 -0
  192. package/dist/types/services.d.ts +24 -0
  193. package/dist/types/services.d.ts.map +1 -0
  194. package/dist/types/services.js +3 -0
  195. package/dist/types/shell.d.ts +32 -0
  196. package/dist/types/shell.d.ts.map +1 -0
  197. package/dist/types/shell.js +1 -0
  198. package/dist/types/store.d.ts +124 -0
  199. package/dist/types/store.d.ts.map +1 -0
  200. package/dist/types/store.js +1 -0
  201. package/dist/types/stream.d.ts +18 -0
  202. package/dist/types/stream.d.ts.map +1 -0
  203. package/dist/types/stream.js +1 -0
  204. package/dist/types/style.d.ts +166 -0
  205. package/dist/types/style.d.ts.map +1 -0
  206. package/dist/types/style.js +1 -0
  207. package/dist/types/surface.d.ts +97 -0
  208. package/dist/types/surface.d.ts.map +1 -0
  209. package/dist/types/surface.js +1 -0
  210. package/dist/types/syntax.d.ts +68 -0
  211. package/dist/types/syntax.d.ts.map +1 -0
  212. package/dist/types/syntax.js +5 -0
  213. package/dist/types/terminal.d.ts +52 -0
  214. package/dist/types/terminal.d.ts.map +1 -0
  215. package/dist/types/terminal.js +1 -0
  216. package/dist/types/theme.d.ts +117 -0
  217. package/dist/types/theme.d.ts.map +1 -0
  218. package/dist/types/theme.js +1 -0
  219. package/dist/types/when.d.ts +19 -0
  220. package/dist/types/when.d.ts.map +1 -0
  221. package/dist/types/when.js +1 -0
  222. package/dist/ui/primitives.d.ts +56 -0
  223. package/dist/ui/primitives.d.ts.map +1 -0
  224. package/dist/ui/primitives.js +108 -0
  225. package/dist/ui/screen.d.ts +23 -0
  226. package/dist/ui/screen.d.ts.map +1 -0
  227. package/dist/ui/screen.js +18 -0
  228. package/dist/util/disposable.d.ts +6 -0
  229. package/dist/util/disposable.d.ts.map +1 -0
  230. package/dist/util/disposable.js +49 -0
  231. package/dist/util/markdown.d.ts +24 -0
  232. package/dist/util/markdown.d.ts.map +1 -0
  233. package/dist/util/markdown.js +220 -0
  234. package/dist/util/paths.d.ts +47 -0
  235. package/dist/util/paths.d.ts.map +1 -0
  236. package/dist/util/paths.js +134 -0
  237. package/dist/util/stream.d.ts +32 -0
  238. package/dist/util/stream.d.ts.map +1 -0
  239. package/dist/util/stream.js +206 -0
  240. package/dist/util/text.d.ts +65 -0
  241. package/dist/util/text.d.ts.map +1 -0
  242. package/dist/util/text.js +419 -0
  243. package/package.json +70 -0
  244. package/src/adapters/index.ts +11 -0
  245. package/src/app/app.ts +1096 -0
  246. package/src/core/animation.ts +144 -0
  247. package/src/core/clipboard.ts +40 -0
  248. package/src/core/commands.ts +169 -0
  249. package/src/core/components.ts +91 -0
  250. package/src/core/events.ts +95 -0
  251. package/src/core/focus.ts +358 -0
  252. package/src/core/i18n.ts +107 -0
  253. package/src/core/keybindings.ts +184 -0
  254. package/src/core/layers.ts +94 -0
  255. package/src/core/manifest.ts +84 -0
  256. package/src/core/navigation.ts +135 -0
  257. package/src/core/resources.ts +362 -0
  258. package/src/core/services.ts +69 -0
  259. package/src/core/store.ts +640 -0
  260. package/src/core/surfaces.ts +292 -0
  261. package/src/core/syntax.ts +200 -0
  262. package/src/core/when.ts +238 -0
  263. package/src/index.ts +76 -0
  264. package/src/jsx/factory.ts +124 -0
  265. package/src/jsx/intrinsics.ts +99 -0
  266. package/src/jsx/jsx-dev-runtime.ts +23 -0
  267. package/src/jsx/jsx-runtime.ts +76 -0
  268. package/src/render/buffer.ts +318 -0
  269. package/src/render/color.ts +180 -0
  270. package/src/render/diff.ts +99 -0
  271. package/src/render/layout.ts +764 -0
  272. package/src/render/static.ts +290 -0
  273. package/src/runtime/bindings.ts +150 -0
  274. package/src/runtime/hooks.ts +1057 -0
  275. package/src/runtime/instance.ts +284 -0
  276. package/src/runtime/paint.ts +737 -0
  277. package/src/runtime/reconcile.ts +335 -0
  278. package/src/runtime/runtime.ts +47 -0
  279. package/src/runtime/style.ts +205 -0
  280. package/src/themes/borders.ts +95 -0
  281. package/src/themes/builtin.ts +301 -0
  282. package/src/themes/glyphs.ts +101 -0
  283. package/src/themes/index.ts +4 -0
  284. package/src/themes/registry.ts +224 -0
  285. package/src/types/adapter.ts +41 -0
  286. package/src/types/animation.ts +34 -0
  287. package/src/types/app.ts +149 -0
  288. package/src/types/async.ts +35 -0
  289. package/src/types/capabilities.ts +79 -0
  290. package/src/types/cells.ts +79 -0
  291. package/src/types/command.ts +139 -0
  292. package/src/types/component-registry.ts +82 -0
  293. package/src/types/disposable.ts +8 -0
  294. package/src/types/focus.ts +77 -0
  295. package/src/types/geometry.ts +50 -0
  296. package/src/types/graph.ts +174 -0
  297. package/src/types/i18n.ts +25 -0
  298. package/src/types/index.ts +31 -0
  299. package/src/types/input.ts +81 -0
  300. package/src/types/keybinding.ts +51 -0
  301. package/src/types/layer.ts +46 -0
  302. package/src/types/manifest.ts +101 -0
  303. package/src/types/markdown.ts +47 -0
  304. package/src/types/navigation.ts +42 -0
  305. package/src/types/render.ts +86 -0
  306. package/src/types/resource.ts +185 -0
  307. package/src/types/services.ts +28 -0
  308. package/src/types/shell.ts +30 -0
  309. package/src/types/store.ts +148 -0
  310. package/src/types/stream.ts +24 -0
  311. package/src/types/style.ts +205 -0
  312. package/src/types/surface.ts +119 -0
  313. package/src/types/syntax.ts +93 -0
  314. package/src/types/terminal.ts +58 -0
  315. package/src/types/theme.ts +121 -0
  316. package/src/types/when.ts +21 -0
  317. package/src/ui/primitives.ts +118 -0
  318. package/src/ui/screen.ts +41 -0
  319. package/src/util/disposable.ts +49 -0
  320. package/src/util/markdown.ts +225 -0
  321. package/src/util/paths.ts +138 -0
  322. package/src/util/stream.ts +213 -0
  323. package/src/util/text.ts +428 -0
@@ -0,0 +1,65 @@
1
+ import type { TextWrap } from '../types/style.js';
2
+ /**
3
+ * Split into grapheme clusters. Falls back to code points without Intl.
4
+ *
5
+ * The ASCII path skips `Intl.Segmenter` entirely. It is not a micro-tweak:
6
+ * painting walks the graphemes of every string on screen every frame, and
7
+ * segmentation was measuring at about a sixth of the frame in a list of plain
8
+ * ASCII rows - all of it spent proving that `r`, `o` and `w` are one character
9
+ * each.
10
+ */
11
+ export declare function graphemes(text: string): string[];
12
+ /**
13
+ * Width of one grapheme cluster in cells. A cluster is measured by its base
14
+ * character; an emoji followed by VS16 counts as wide.
15
+ */
16
+ export declare function graphemeWidth(cluster: string): number;
17
+ /** Width of a string in terminal cells. */
18
+ export declare function stringWidth(text: string): number;
19
+ /** Take at most `width` cells from the front. Never splits a wide grapheme. */
20
+ export declare function sliceByWidth(text: string, width: number): string;
21
+ /**
22
+ * The slice of `text` covering display columns `[start, start + width)`.
23
+ *
24
+ * Cutting by index is wrong the moment a line contains a wide character: the
25
+ * horizontal scroll would drift against the gutter, one cell per CJK glyph.
26
+ * A wide grapheme straddling either edge is dropped rather than split, because
27
+ * half of one cannot be drawn.
28
+ */
29
+ export declare function sliceColumns(text: string, start: number, width: number): string;
30
+ /**
31
+ * Replace tabs with spaces to the next tab stop.
32
+ *
33
+ * A terminal cell is one column, so a raw tab is one blank cell and every
34
+ * indented line lands in a different place than it does in an editor. Doing
35
+ * this before highlighting keeps token offsets and screen columns the same
36
+ * number.
37
+ */
38
+ export declare function expandTabs(text: string, tabWidth?: number): string;
39
+ export type TruncateSide = 'end' | 'start' | 'middle';
40
+ /** Fit into `width` cells, marking the cut with `ellipsis`. */
41
+ export declare function truncate(text: string, width: number, ellipsis?: string, side?: TruncateSide): string;
42
+ /** Pad to exactly `width` cells. Over-long input is returned unchanged. */
43
+ export declare function padTo(text: string, width: number, align?: 'left' | 'center' | 'right', fill?: string): string;
44
+ /** Fit to exactly `width`: truncate when long, pad when short. */
45
+ export declare function fitTo(text: string, width: number, align?: 'left' | 'center' | 'right', ellipsis?: string): string;
46
+ export type WrapMode = 'none' | 'word' | 'char';
47
+ /**
48
+ * The end a `TextWrap` cuts at, or `undefined` when it wraps instead.
49
+ *
50
+ * The wrapping modes and the truncating ones live in one union because they
51
+ * answer one question - what happens at the edge - and a component that had to
52
+ * take both a `wrap` and a `truncate` prop could be handed the two answers at
53
+ * once. These two helpers are how the union is taken apart again.
54
+ */
55
+ export declare function truncateSideOf(wrap: TextWrap | undefined): TruncateSide | undefined;
56
+ /** The mode `wrapText` should run in. A truncating text does not wrap at all. */
57
+ export declare function wrapModeOf(wrap: TextWrap | undefined): WrapMode;
58
+ /** Break into lines of at most `width` cells. Honours existing newlines. */
59
+ export declare function wrapText(text: string, width: number, mode?: WrapMode): string[];
60
+ export declare function stripAnsi(text: string): string;
61
+ /** Remove control characters that would corrupt the frame. Keeps newlines. */
62
+ export declare function sanitize(text: string): string;
63
+ /** Repeat a grapheme to exactly `width` cells (wide chars land short). */
64
+ export declare function repeatToWidth(char: string, width: number): string;
65
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/util/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AA8BlD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAShD;AA8ED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAYhE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgC/E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAuB7D;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD,+DAA+D;AAC/D,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,SAAM,EACd,IAAI,GAAE,YAAoB,GACzB,MAAM,CAkCR;AAED,2EAA2E;AAC3E,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,GAAG,QAAQ,GAAG,OAAgB,EAC3C,IAAI,SAAM,GACT,MAAM,CAQR;AAED,kEAAkE;AAClE,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,GAAG,QAAQ,GAAG,OAAgB,EAC3C,QAAQ,SAAM,GACb,MAAM,CAER;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAOnF;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAE/D;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,QAAiB,GAAG,MAAM,EAAE,CAuDvF;AAOD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjE"}
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Terminal text measurement.
3
+ *
4
+ * Everything the layout engine knows about size comes from here, so the rules
5
+ * are worth stating: a cell holds one grapheme cluster, a grapheme is 0, 1 or
6
+ * 2 cells wide, and a string's width is the sum - never its `.length`.
7
+ */
8
+ const segmenter = typeof Intl !== 'undefined' && 'Segmenter' in Intl
9
+ ? new Intl.Segmenter(undefined, { granularity: 'grapheme' })
10
+ : null;
11
+ /**
12
+ * True for a string that is entirely printable ASCII.
13
+ *
14
+ * Worth asking, because the answer is yes for very nearly all of it: source
15
+ * code, file names, labels, key hints. Every character is then one grapheme
16
+ * one cell wide, and none of the Unicode machinery below has anything to do.
17
+ */
18
+ function isAscii(text) {
19
+ for (let i = 0; i < text.length; i++) {
20
+ const c = text.charCodeAt(i);
21
+ if (c < 0x20 || c > 0x7e)
22
+ return false;
23
+ }
24
+ return true;
25
+ }
26
+ /**
27
+ * Split into grapheme clusters. Falls back to code points without Intl.
28
+ *
29
+ * The ASCII path skips `Intl.Segmenter` entirely. It is not a micro-tweak:
30
+ * painting walks the graphemes of every string on screen every frame, and
31
+ * segmentation was measuring at about a sixth of the frame in a list of plain
32
+ * ASCII rows - all of it spent proving that `r`, `o` and `w` are one character
33
+ * each.
34
+ */
35
+ export function graphemes(text) {
36
+ if (text === '')
37
+ return [];
38
+ if (isAscii(text))
39
+ return text.split('');
40
+ if (segmenter) {
41
+ const out = [];
42
+ for (const { segment } of segmenter.segment(text))
43
+ out.push(segment);
44
+ return out;
45
+ }
46
+ return Array.from(text);
47
+ }
48
+ // Zero-width: combining marks, variation selectors, ZWJ, control chars.
49
+ function isZeroWidth(cp) {
50
+ return (cp === 0x200b || cp === 0x200c || cp === 0x200d || cp === 0xfeff ||
51
+ (cp >= 0x0300 && cp <= 0x036f) ||
52
+ (cp >= 0x0483 && cp <= 0x0489) ||
53
+ (cp >= 0x0591 && cp <= 0x05bd) ||
54
+ (cp >= 0x0610 && cp <= 0x061a) ||
55
+ (cp >= 0x064b && cp <= 0x065f) ||
56
+ (cp >= 0x0e31 && cp <= 0x0e3a) ||
57
+ (cp >= 0x1ab0 && cp <= 0x1aff) ||
58
+ (cp >= 0x1dc0 && cp <= 0x1dff) ||
59
+ (cp >= 0x20d0 && cp <= 0x20f0) ||
60
+ (cp >= 0xfe00 && cp <= 0xfe0f) ||
61
+ (cp >= 0xfe20 && cp <= 0xfe2f) ||
62
+ (cp >= 0xe0100 && cp <= 0xe01ef));
63
+ }
64
+ // East Asian Wide + Fullwidth, and the emoji ranges that render double-width.
65
+ function isWide(cp) {
66
+ return ((cp >= 0x1100 && cp <= 0x115f) ||
67
+ (cp >= 0x2e80 && cp <= 0x303e) ||
68
+ (cp >= 0x3041 && cp <= 0x33ff) ||
69
+ (cp >= 0x3400 && cp <= 0x4dbf) ||
70
+ (cp >= 0x4e00 && cp <= 0x9fff) ||
71
+ (cp >= 0xa000 && cp <= 0xa4cf) ||
72
+ (cp >= 0xac00 && cp <= 0xd7a3) ||
73
+ (cp >= 0xf900 && cp <= 0xfaff) ||
74
+ (cp >= 0xfe10 && cp <= 0xfe19) ||
75
+ (cp >= 0xfe30 && cp <= 0xfe6f) ||
76
+ (cp >= 0xff00 && cp <= 0xff60) ||
77
+ (cp >= 0xffe0 && cp <= 0xffe6) ||
78
+ // The pictograph blocks are *mostly* wide, and the exceptions matter.
79
+ // `1F300..1F64F` as one range called U+1F5C0 FOLDER two cells; Unicode
80
+ // says East Asian Width Neutral, and a terminal following wcwidth draws
81
+ // it in one. Those blocks are full of such symbols - the whole
82
+ // `1F5A5..1F5FA` run of them - and a glyph measured as two and drawn as
83
+ // one leaves the rest of the row shifted by a column.
84
+ //
85
+ // Generated from EastAsianWidth (Unicode 15.1), W and F only. A symbol
86
+ // that takes emoji presentation is handled by the VS16 rule below, which
87
+ // is the thing that actually makes one of these wide when it is wide.
88
+ (cp >= 0x1f300 && cp <= 0x1f320) ||
89
+ (cp >= 0x1f32d && cp <= 0x1f335) ||
90
+ (cp >= 0x1f337 && cp <= 0x1f37c) ||
91
+ (cp >= 0x1f37e && cp <= 0x1f393) ||
92
+ (cp >= 0x1f3a0 && cp <= 0x1f3ca) ||
93
+ (cp >= 0x1f3cf && cp <= 0x1f3d3) ||
94
+ (cp >= 0x1f3e0 && cp <= 0x1f3f0) ||
95
+ cp === 0x1f3f4 ||
96
+ (cp >= 0x1f3f8 && cp <= 0x1f43e) ||
97
+ cp === 0x1f440 ||
98
+ (cp >= 0x1f442 && cp <= 0x1f4fc) ||
99
+ (cp >= 0x1f4ff && cp <= 0x1f53d) ||
100
+ (cp >= 0x1f54b && cp <= 0x1f54e) ||
101
+ (cp >= 0x1f550 && cp <= 0x1f567) ||
102
+ cp === 0x1f57a ||
103
+ (cp >= 0x1f595 && cp <= 0x1f596) ||
104
+ cp === 0x1f5a4 ||
105
+ (cp >= 0x1f5fb && cp <= 0x1f64f) ||
106
+ (cp >= 0x1f90c && cp <= 0x1f93a) ||
107
+ (cp >= 0x1f93c && cp <= 0x1f945) ||
108
+ (cp >= 0x1f947 && cp <= 0x1f9ff) ||
109
+ (cp >= 0x1fa70 && cp <= 0x1fa7c) ||
110
+ (cp >= 0x1fa80 && cp <= 0x1fa88) ||
111
+ (cp >= 0x1fa90 && cp <= 0x1fabd) ||
112
+ (cp >= 0x1fabf && cp <= 0x1fac5) ||
113
+ (cp >= 0x1face && cp <= 0x1fadb) ||
114
+ (cp >= 0x1fae0 && cp <= 0x1fae8) ||
115
+ (cp >= 0x1faf0 && cp <= 0x1faf8) ||
116
+ (cp >= 0x20000 && cp <= 0x3fffd));
117
+ }
118
+ /**
119
+ * Width of one grapheme cluster in cells. A cluster is measured by its base
120
+ * character; an emoji followed by VS16 counts as wide.
121
+ */
122
+ export function graphemeWidth(cluster) {
123
+ if (cluster === '')
124
+ return 0;
125
+ /*
126
+ * One printable ASCII character is one cell, and that is the overwhelming
127
+ * majority of every cluster this is ever asked about.
128
+ *
129
+ * The general path below builds two arrays - `Array.from` then `.map` - for
130
+ * every cluster, which is two allocations per cell per frame. Answering the
131
+ * common case from the character code costs nothing and allocates nothing.
132
+ */
133
+ if (cluster.length === 1) {
134
+ const only = cluster.charCodeAt(0);
135
+ if (only >= 0x20 && only <= 0x7e)
136
+ return 1;
137
+ }
138
+ const cps = Array.from(cluster).map((c) => c.codePointAt(0));
139
+ const base = cps[0];
140
+ if (base < 0x20 || base === 0x7f)
141
+ return 0;
142
+ if (isZeroWidth(base))
143
+ return 0;
144
+ // VS16 promotes a text-presentation symbol to emoji presentation.
145
+ if (cps.includes(0xfe0f))
146
+ return 2;
147
+ if (isWide(base))
148
+ return 2;
149
+ return 1;
150
+ }
151
+ /** Width of a string in terminal cells. */
152
+ export function stringWidth(text) {
153
+ if (text === '')
154
+ return 0;
155
+ // Fast path: pure ASCII printable.
156
+ if (isAscii(text))
157
+ return text.length;
158
+ let w = 0;
159
+ for (const g of graphemes(text))
160
+ w += graphemeWidth(g);
161
+ return w;
162
+ }
163
+ /** Take at most `width` cells from the front. Never splits a wide grapheme. */
164
+ export function sliceByWidth(text, width) {
165
+ if (width <= 0)
166
+ return '';
167
+ if (stringWidth(text) <= width)
168
+ return text;
169
+ let out = '';
170
+ let w = 0;
171
+ for (const g of graphemes(text)) {
172
+ const gw = graphemeWidth(g);
173
+ if (w + gw > width)
174
+ break;
175
+ out += g;
176
+ w += gw;
177
+ }
178
+ return out;
179
+ }
180
+ /**
181
+ * The slice of `text` covering display columns `[start, start + width)`.
182
+ *
183
+ * Cutting by index is wrong the moment a line contains a wide character: the
184
+ * horizontal scroll would drift against the gutter, one cell per CJK glyph.
185
+ * A wide grapheme straddling either edge is dropped rather than split, because
186
+ * half of one cannot be drawn.
187
+ */
188
+ export function sliceColumns(text, start, width) {
189
+ if (width <= 0)
190
+ return '';
191
+ const end = start + width;
192
+ let column = 0;
193
+ let out = '';
194
+ for (const g of graphemes(text)) {
195
+ const gw = graphemeWidth(g);
196
+ if (gw === 0) {
197
+ // A combining mark belongs to the grapheme before it, if that survived.
198
+ if (out !== '')
199
+ out += g;
200
+ continue;
201
+ }
202
+ const next = column + gw;
203
+ if (next <= start) {
204
+ column = next;
205
+ continue;
206
+ }
207
+ if (column >= end)
208
+ break;
209
+ if (column >= start && next <= end) {
210
+ out += g;
211
+ }
212
+ else {
213
+ // A wide grapheme half inside the window becomes blanks for the half
214
+ // that shows. Splitting it is impossible and dropping it silently would
215
+ // slide the rest of the line one cell against the gutter.
216
+ out += ' '.repeat(Math.min(next, end) - Math.max(column, start));
217
+ }
218
+ column = next;
219
+ }
220
+ return out;
221
+ }
222
+ /**
223
+ * Replace tabs with spaces to the next tab stop.
224
+ *
225
+ * A terminal cell is one column, so a raw tab is one blank cell and every
226
+ * indented line lands in a different place than it does in an editor. Doing
227
+ * this before highlighting keeps token offsets and screen columns the same
228
+ * number.
229
+ */
230
+ export function expandTabs(text, tabWidth = 4) {
231
+ if (!text.includes('\t'))
232
+ return text;
233
+ const width = Math.max(1, tabWidth);
234
+ return text
235
+ .split('\n')
236
+ .map((line) => {
237
+ if (!line.includes('\t'))
238
+ return line;
239
+ let out = '';
240
+ let column = 0;
241
+ for (const g of graphemes(line)) {
242
+ if (g === '\t') {
243
+ const advance = width - (column % width);
244
+ out += ' '.repeat(advance);
245
+ column += advance;
246
+ continue;
247
+ }
248
+ out += g;
249
+ column += graphemeWidth(g);
250
+ }
251
+ return out;
252
+ })
253
+ .join('\n');
254
+ }
255
+ /** Fit into `width` cells, marking the cut with `ellipsis`. */
256
+ export function truncate(text, width, ellipsis = '…', side = 'end') {
257
+ const total = stringWidth(text);
258
+ if (total <= width)
259
+ return text;
260
+ if (width <= 0)
261
+ return '';
262
+ const ew = stringWidth(ellipsis);
263
+ if (width <= ew)
264
+ return sliceByWidth(ellipsis, width);
265
+ // A space either side of the ellipsis is a typographic mistake, not a cell
266
+ // of content, so the cut is tidied before the mark goes on. It also means a
267
+ // truncated string can come back narrower than `width`, which is correct:
268
+ // `width` is a limit, and nothing here is padding.
269
+ const keep = width - ew;
270
+ if (side === 'end')
271
+ return sliceByWidth(text, keep).trimEnd() + ellipsis;
272
+ const gs = graphemes(text);
273
+ if (side === 'start') {
274
+ let w = 0;
275
+ let i = gs.length;
276
+ while (i > 0 && w + graphemeWidth(gs[i - 1]) <= keep) {
277
+ i--;
278
+ w += graphemeWidth(gs[i]);
279
+ }
280
+ return ellipsis + gs.slice(i).join('').trimStart();
281
+ }
282
+ const left = Math.ceil(keep / 2);
283
+ const right = keep - left;
284
+ let w = 0;
285
+ let j = gs.length;
286
+ while (j > 0 && w + graphemeWidth(gs[j - 1]) <= right) {
287
+ j--;
288
+ w += graphemeWidth(gs[j]);
289
+ }
290
+ return sliceByWidth(text, left).trimEnd() + ellipsis + gs.slice(j).join('').trimStart();
291
+ }
292
+ /** Pad to exactly `width` cells. Over-long input is returned unchanged. */
293
+ export function padTo(text, width, align = 'left', fill = ' ') {
294
+ const w = stringWidth(text);
295
+ if (w >= width)
296
+ return text;
297
+ const pad = width - w;
298
+ if (align === 'left')
299
+ return text + fill.repeat(pad);
300
+ if (align === 'right')
301
+ return fill.repeat(pad) + text;
302
+ const left = Math.floor(pad / 2);
303
+ return fill.repeat(left) + text + fill.repeat(pad - left);
304
+ }
305
+ /** Fit to exactly `width`: truncate when long, pad when short. */
306
+ export function fitTo(text, width, align = 'left', ellipsis = '…') {
307
+ return padTo(truncate(text, width, ellipsis), width, align);
308
+ }
309
+ /**
310
+ * The end a `TextWrap` cuts at, or `undefined` when it wraps instead.
311
+ *
312
+ * The wrapping modes and the truncating ones live in one union because they
313
+ * answer one question - what happens at the edge - and a component that had to
314
+ * take both a `wrap` and a `truncate` prop could be handed the two answers at
315
+ * once. These two helpers are how the union is taken apart again.
316
+ */
317
+ export function truncateSideOf(wrap) {
318
+ switch (wrap) {
319
+ case 'truncate':
320
+ case 'truncate-end': return 'end';
321
+ case 'truncate-start': return 'start';
322
+ case 'truncate-middle': return 'middle';
323
+ default: return undefined;
324
+ }
325
+ }
326
+ /** The mode `wrapText` should run in. A truncating text does not wrap at all. */
327
+ export function wrapModeOf(wrap) {
328
+ return wrap === 'word' || wrap === 'char' ? wrap : 'none';
329
+ }
330
+ /** Break into lines of at most `width` cells. Honours existing newlines. */
331
+ export function wrapText(text, width, mode = 'word') {
332
+ if (width <= 0)
333
+ return [];
334
+ const paragraphs = text.split('\n');
335
+ if (mode === 'none')
336
+ return paragraphs;
337
+ const out = [];
338
+ for (const para of paragraphs) {
339
+ if (para === '') {
340
+ out.push('');
341
+ continue;
342
+ }
343
+ if (stringWidth(para) <= width) {
344
+ out.push(para);
345
+ continue;
346
+ }
347
+ if (mode === 'char') {
348
+ let rest = para;
349
+ while (stringWidth(rest) > width) {
350
+ const head = sliceByWidth(rest, width);
351
+ out.push(head);
352
+ rest = rest.slice(head.length);
353
+ }
354
+ if (rest !== '')
355
+ out.push(rest);
356
+ continue;
357
+ }
358
+ // word wrap; a word longer than the line falls back to char wrap
359
+ let line = '';
360
+ let lineW = 0;
361
+ for (const word of para.split(/(\s+)/)) {
362
+ if (word === '')
363
+ continue;
364
+ const ww = stringWidth(word);
365
+ const isSpace = /^\s+$/.test(word);
366
+ if (lineW + ww <= width) {
367
+ line += word;
368
+ lineW += ww;
369
+ continue;
370
+ }
371
+ if (isSpace) {
372
+ out.push(line.trimEnd());
373
+ line = '';
374
+ lineW = 0;
375
+ continue;
376
+ }
377
+ if (line !== '') {
378
+ out.push(line.trimEnd());
379
+ line = '';
380
+ lineW = 0;
381
+ }
382
+ if (ww <= width) {
383
+ line = word;
384
+ lineW = ww;
385
+ continue;
386
+ }
387
+ let rest = word;
388
+ while (stringWidth(rest) > width) {
389
+ const head = sliceByWidth(rest, width);
390
+ out.push(head);
391
+ rest = rest.slice(head.length);
392
+ }
393
+ line = rest;
394
+ lineW = stringWidth(rest);
395
+ }
396
+ if (line !== '')
397
+ out.push(line.trimEnd());
398
+ }
399
+ return out;
400
+ }
401
+ // CSI / OSC / SS2 / SS3 and friends. Input sanitation only, never output.
402
+ const ANSI_RE =
403
+ // eslint-disable-next-line no-control-regex
404
+ /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07\x1B]*(?:\x07|\x1B\\))/g;
405
+ export function stripAnsi(text) {
406
+ return text.replace(ANSI_RE, '');
407
+ }
408
+ /** Remove control characters that would corrupt the frame. Keeps newlines. */
409
+ export function sanitize(text) {
410
+ // eslint-disable-next-line no-control-regex
411
+ return stripAnsi(text).replace(/[\x00-\x08\x0B-\x1F\x7F]/g, '');
412
+ }
413
+ /** Repeat a grapheme to exactly `width` cells (wide chars land short). */
414
+ export function repeatToWidth(char, width) {
415
+ if (width <= 0)
416
+ return '';
417
+ const w = graphemeWidth(char) || 1;
418
+ return char.repeat(Math.max(0, Math.floor(width / w)));
419
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@textui/core",
3
+ "version": "0.1.0",
4
+ "description": "Graph-mounted terminal UI runtime - store, registries, renderer, host primitives",
5
+ "keywords": [
6
+ "terminal",
7
+ "tui",
8
+ "cli",
9
+ "jsx",
10
+ "ui",
11
+ "runtime",
12
+ "ansi",
13
+ "text-ui",
14
+ "console",
15
+ "no-dependencies"
16
+ ],
17
+ "homepage": "https://softov.github.io/textui/",
18
+ "bugs": {
19
+ "url": "https://github.com/softov/textui/issues"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/softov/textui.git",
24
+ "directory": "packages/core"
25
+ },
26
+ "license": "MIT",
27
+ "author": "Softov <softov@brbyte.com>",
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "src",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/index.js"
43
+ },
44
+ "./types": {
45
+ "types": "./dist/types/index.d.ts",
46
+ "import": "./dist/types/index.js"
47
+ },
48
+ "./jsx-runtime": {
49
+ "types": "./dist/jsx/jsx-runtime.d.ts",
50
+ "import": "./dist/jsx/jsx-runtime.js"
51
+ },
52
+ "./jsx-dev-runtime": {
53
+ "types": "./dist/jsx/jsx-dev-runtime.d.ts",
54
+ "import": "./dist/jsx/jsx-dev-runtime.js"
55
+ },
56
+ "./themes": {
57
+ "types": "./dist/themes/index.d.ts",
58
+ "import": "./dist/themes/index.js"
59
+ }
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "scripts": {
65
+ "build": "tsc -p tsconfig.json",
66
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
67
+ "test": "vitest run",
68
+ "test:watch": "vitest"
69
+ }
70
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Adapters.
3
+ *
4
+ * Nothing here is registered by default. An adapter is a choice - what a kind
5
+ * means, what opens it, what can be done to it - and choices belong to the
6
+ * application, not to the library that ships them.
7
+ *
8
+ * The adapters that have an opinion about *content* live in
9
+ * `@textui/documents`, beside the buffers they read and write.
10
+ */
11
+ export {};