@effect-tui/core 0.1.0-alpha.1

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 (241) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/anim.d.ts +4 -0
  4. package/dist/anim.d.ts.map +1 -0
  5. package/dist/anim.js +5 -0
  6. package/dist/anim.js.map +1 -0
  7. package/dist/ansi.d.ts +69 -0
  8. package/dist/ansi.d.ts.map +1 -0
  9. package/dist/ansi.js +72 -0
  10. package/dist/ansi.js.map +1 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +16 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/keys.d.ts +18 -0
  16. package/dist/keys.d.ts.map +1 -0
  17. package/dist/keys.js +247 -0
  18. package/dist/keys.js.map +1 -0
  19. package/dist/layout/linearStack.d.ts +17 -0
  20. package/dist/layout/linearStack.d.ts.map +1 -0
  21. package/dist/layout/linearStack.js +86 -0
  22. package/dist/layout/linearStack.js.map +1 -0
  23. package/dist/motion-value.d.ts +58 -0
  24. package/dist/motion-value.d.ts.map +1 -0
  25. package/dist/motion-value.js +250 -0
  26. package/dist/motion-value.js.map +1 -0
  27. package/dist/present/display.d.ts +58 -0
  28. package/dist/present/display.d.ts.map +1 -0
  29. package/dist/present/display.js +168 -0
  30. package/dist/present/display.js.map +1 -0
  31. package/dist/present/writers/fullscreen.d.ts +19 -0
  32. package/dist/present/writers/fullscreen.d.ts.map +1 -0
  33. package/dist/present/writers/fullscreen.js +55 -0
  34. package/dist/present/writers/fullscreen.js.map +1 -0
  35. package/dist/present/writers/inline.d.ts +20 -0
  36. package/dist/present/writers/inline.d.ts.map +1 -0
  37. package/dist/present/writers/inline.js +92 -0
  38. package/dist/present/writers/inline.js.map +1 -0
  39. package/dist/render/buffer.d.ts +31 -0
  40. package/dist/render/buffer.d.ts.map +1 -0
  41. package/dist/render/buffer.js +183 -0
  42. package/dist/render/buffer.js.map +1 -0
  43. package/dist/render/color-utils.d.ts +18 -0
  44. package/dist/render/color-utils.d.ts.map +1 -0
  45. package/dist/render/color-utils.js +58 -0
  46. package/dist/render/color-utils.js.map +1 -0
  47. package/dist/render/diff.d.ts +30 -0
  48. package/dist/render/diff.d.ts.map +1 -0
  49. package/dist/render/diff.js +83 -0
  50. package/dist/render/diff.js.map +1 -0
  51. package/dist/render/measure.d.ts +15 -0
  52. package/dist/render/measure.d.ts.map +1 -0
  53. package/dist/render/measure.js +65 -0
  54. package/dist/render/measure.js.map +1 -0
  55. package/dist/render/palette.d.ts +46 -0
  56. package/dist/render/palette.d.ts.map +1 -0
  57. package/dist/render/palette.js +108 -0
  58. package/dist/render/palette.js.map +1 -0
  59. package/dist/render/surface.d.ts +77 -0
  60. package/dist/render/surface.d.ts.map +1 -0
  61. package/dist/render/surface.js +198 -0
  62. package/dist/render/surface.js.map +1 -0
  63. package/dist/runtime/backend_node.d.ts +36 -0
  64. package/dist/runtime/backend_node.d.ts.map +1 -0
  65. package/dist/runtime/backend_node.js +66 -0
  66. package/dist/runtime/backend_node.js.map +1 -0
  67. package/dist/spring-physics.d.ts +36 -0
  68. package/dist/spring-physics.d.ts.map +1 -0
  69. package/dist/spring-physics.js +113 -0
  70. package/dist/spring-physics.js.map +1 -0
  71. package/dist/spring.d.ts +73 -0
  72. package/dist/spring.d.ts.map +1 -0
  73. package/dist/spring.js +136 -0
  74. package/dist/spring.js.map +1 -0
  75. package/dist/ui/containers/canvas.d.ts +13 -0
  76. package/dist/ui/containers/canvas.d.ts.map +1 -0
  77. package/dist/ui/containers/canvas.js +16 -0
  78. package/dist/ui/containers/canvas.js.map +1 -0
  79. package/dist/ui/containers/geometry-reader.d.ts +17 -0
  80. package/dist/ui/containers/geometry-reader.d.ts.map +1 -0
  81. package/dist/ui/containers/geometry-reader.js +24 -0
  82. package/dist/ui/containers/geometry-reader.js.map +1 -0
  83. package/dist/ui/containers/hstack.d.ts +12 -0
  84. package/dist/ui/containers/hstack.d.ts.map +1 -0
  85. package/dist/ui/containers/hstack.js +28 -0
  86. package/dist/ui/containers/hstack.js.map +1 -0
  87. package/dist/ui/containers/scroll.d.ts +28 -0
  88. package/dist/ui/containers/scroll.d.ts.map +1 -0
  89. package/dist/ui/containers/scroll.js +97 -0
  90. package/dist/ui/containers/scroll.js.map +1 -0
  91. package/dist/ui/containers/shared.d.ts +12 -0
  92. package/dist/ui/containers/shared.d.ts.map +1 -0
  93. package/dist/ui/containers/shared.js +19 -0
  94. package/dist/ui/containers/shared.js.map +1 -0
  95. package/dist/ui/containers/vstack.d.ts +12 -0
  96. package/dist/ui/containers/vstack.d.ts.map +1 -0
  97. package/dist/ui/containers/vstack.js +28 -0
  98. package/dist/ui/containers/vstack.js.map +1 -0
  99. package/dist/ui/containers/zstack.d.ts +14 -0
  100. package/dist/ui/containers/zstack.d.ts.map +1 -0
  101. package/dist/ui/containers/zstack.js +36 -0
  102. package/dist/ui/containers/zstack.js.map +1 -0
  103. package/dist/ui/core/geometry-store.d.ts +22 -0
  104. package/dist/ui/core/geometry-store.d.ts.map +1 -0
  105. package/dist/ui/core/geometry-store.js +29 -0
  106. package/dist/ui/core/geometry-store.js.map +1 -0
  107. package/dist/ui/core/geometry.d.ts +34 -0
  108. package/dist/ui/core/geometry.d.ts.map +1 -0
  109. package/dist/ui/core/geometry.js +14 -0
  110. package/dist/ui/core/geometry.js.map +1 -0
  111. package/dist/ui/core/view.d.ts +25 -0
  112. package/dist/ui/core/view.d.ts.map +1 -0
  113. package/dist/ui/core/view.js +34 -0
  114. package/dist/ui/core/view.js.map +1 -0
  115. package/dist/ui/index.d.ts +44 -0
  116. package/dist/ui/index.d.ts.map +1 -0
  117. package/dist/ui/index.js +39 -0
  118. package/dist/ui/index.js.map +1 -0
  119. package/dist/ui/inlinetext.d.ts +24 -0
  120. package/dist/ui/inlinetext.d.ts.map +1 -0
  121. package/dist/ui/inlinetext.js +131 -0
  122. package/dist/ui/inlinetext.js.map +1 -0
  123. package/dist/ui/install.d.ts +22 -0
  124. package/dist/ui/install.d.ts.map +1 -0
  125. package/dist/ui/install.js +66 -0
  126. package/dist/ui/install.js.map +1 -0
  127. package/dist/ui/markdown.d.ts +40 -0
  128. package/dist/ui/markdown.d.ts.map +1 -0
  129. package/dist/ui/markdown.js +351 -0
  130. package/dist/ui/markdown.js.map +1 -0
  131. package/dist/ui/modifiers/border.d.ts +33 -0
  132. package/dist/ui/modifiers/border.d.ts.map +1 -0
  133. package/dist/ui/modifiers/border.js +82 -0
  134. package/dist/ui/modifiers/border.js.map +1 -0
  135. package/dist/ui/modifiers/fill.d.ts +14 -0
  136. package/dist/ui/modifiers/fill.d.ts.map +1 -0
  137. package/dist/ui/modifiers/fill.js +25 -0
  138. package/dist/ui/modifiers/fill.js.map +1 -0
  139. package/dist/ui/modifiers/frame.d.ts +23 -0
  140. package/dist/ui/modifiers/frame.d.ts.map +1 -0
  141. package/dist/ui/modifiers/frame.js +54 -0
  142. package/dist/ui/modifiers/frame.js.map +1 -0
  143. package/dist/ui/modifiers/offset.d.ts +15 -0
  144. package/dist/ui/modifiers/offset.d.ts.map +1 -0
  145. package/dist/ui/modifiers/offset.js +21 -0
  146. package/dist/ui/modifiers/offset.js.map +1 -0
  147. package/dist/ui/modifiers/opacity.d.ts +15 -0
  148. package/dist/ui/modifiers/opacity.d.ts.map +1 -0
  149. package/dist/ui/modifiers/opacity.js +95 -0
  150. package/dist/ui/modifiers/opacity.js.map +1 -0
  151. package/dist/ui/modifiers/padding.d.ts +20 -0
  152. package/dist/ui/modifiers/padding.d.ts.map +1 -0
  153. package/dist/ui/modifiers/padding.js +36 -0
  154. package/dist/ui/modifiers/padding.js.map +1 -0
  155. package/dist/ui/modifiers/styled.d.ts +14 -0
  156. package/dist/ui/modifiers/styled.d.ts.map +1 -0
  157. package/dist/ui/modifiers/styled.js +26 -0
  158. package/dist/ui/modifiers/styled.js.map +1 -0
  159. package/dist/ui/primitives/rectangle.d.ts +15 -0
  160. package/dist/ui/primitives/rectangle.d.ts.map +1 -0
  161. package/dist/ui/primitives/rectangle.js +23 -0
  162. package/dist/ui/primitives/rectangle.js.map +1 -0
  163. package/dist/ui/primitives/spacer.d.ts +13 -0
  164. package/dist/ui/primitives/spacer.d.ts.map +1 -0
  165. package/dist/ui/primitives/spacer.js +16 -0
  166. package/dist/ui/primitives/spacer.js.map +1 -0
  167. package/dist/ui/primitives/text.d.ts +15 -0
  168. package/dist/ui/primitives/text.d.ts.map +1 -0
  169. package/dist/ui/primitives/text.js +79 -0
  170. package/dist/ui/primitives/text.js.map +1 -0
  171. package/dist/ui/primitives/wrapped-text.d.ts +30 -0
  172. package/dist/ui/primitives/wrapped-text.d.ts.map +1 -0
  173. package/dist/ui/primitives/wrapped-text.js +117 -0
  174. package/dist/ui/primitives/wrapped-text.js.map +1 -0
  175. package/dist/ui/shinytext.d.ts +66 -0
  176. package/dist/ui/shinytext.d.ts.map +1 -0
  177. package/dist/ui/shinytext.js +99 -0
  178. package/dist/ui/shinytext.js.map +1 -0
  179. package/dist/ui/text/layout.d.ts +35 -0
  180. package/dist/ui/text/layout.d.ts.map +1 -0
  181. package/dist/ui/text/layout.js +102 -0
  182. package/dist/ui/text/layout.js.map +1 -0
  183. package/dist/ui/textinput.d.ts +140 -0
  184. package/dist/ui/textinput.d.ts.map +1 -0
  185. package/dist/ui/textinput.js +402 -0
  186. package/dist/ui/textinput.js.map +1 -0
  187. package/dist/ui/view-constructors.d.ts +72 -0
  188. package/dist/ui/view-constructors.d.ts.map +1 -0
  189. package/dist/ui/view-constructors.js +74 -0
  190. package/dist/ui/view-constructors.js.map +1 -0
  191. package/package.json +57 -0
  192. package/src/anim.ts +5 -0
  193. package/src/ansi.ts +83 -0
  194. package/src/index.ts +21 -0
  195. package/src/keys.ts +302 -0
  196. package/src/layout/linearStack.ts +115 -0
  197. package/src/motion-value.ts +335 -0
  198. package/src/present/display.ts +206 -0
  199. package/src/present/writers/fullscreen.ts +58 -0
  200. package/src/present/writers/inline.ts +101 -0
  201. package/src/render/buffer.ts +200 -0
  202. package/src/render/color-utils.ts +60 -0
  203. package/src/render/diff.ts +95 -0
  204. package/src/render/measure.ts +74 -0
  205. package/src/render/palette.ts +113 -0
  206. package/src/render/surface.ts +238 -0
  207. package/src/runtime/backend_node.ts +80 -0
  208. package/src/spring-physics.ts +151 -0
  209. package/src/spring.ts +234 -0
  210. package/src/ui/__snapshots__/wrappedtext.test.ts.snap +57 -0
  211. package/src/ui/containers/canvas.ts +18 -0
  212. package/src/ui/containers/geometry-reader.ts +32 -0
  213. package/src/ui/containers/hstack.ts +33 -0
  214. package/src/ui/containers/scroll.ts +106 -0
  215. package/src/ui/containers/shared.ts +27 -0
  216. package/src/ui/containers/vstack.ts +34 -0
  217. package/src/ui/containers/zstack.ts +37 -0
  218. package/src/ui/core/geometry-store.ts +42 -0
  219. package/src/ui/core/geometry.ts +30 -0
  220. package/src/ui/core/view.ts +49 -0
  221. package/src/ui/index.ts +84 -0
  222. package/src/ui/inlinetext.ts +135 -0
  223. package/src/ui/install.ts +110 -0
  224. package/src/ui/markdown.test.ts +74 -0
  225. package/src/ui/markdown.ts +388 -0
  226. package/src/ui/modifiers/border.ts +100 -0
  227. package/src/ui/modifiers/fill.ts +28 -0
  228. package/src/ui/modifiers/frame.ts +74 -0
  229. package/src/ui/modifiers/offset.ts +23 -0
  230. package/src/ui/modifiers/opacity.ts +93 -0
  231. package/src/ui/modifiers/padding.ts +53 -0
  232. package/src/ui/modifiers/styled.ts +31 -0
  233. package/src/ui/primitives/rectangle.ts +25 -0
  234. package/src/ui/primitives/spacer.ts +18 -0
  235. package/src/ui/primitives/text.ts +85 -0
  236. package/src/ui/primitives/wrapped-text.ts +131 -0
  237. package/src/ui/shinytext.ts +159 -0
  238. package/src/ui/text/layout.ts +119 -0
  239. package/src/ui/textinput.ts +496 -0
  240. package/src/ui/view-constructors.ts +96 -0
  241. package/src/ui/wrappedtext.test.ts +138 -0
@@ -0,0 +1,74 @@
1
+ /* view-constructors.ts — Static constructor methods for View
2
+ *
3
+ * This module contains all the static constructor methods that will be merged
4
+ * with the View class via namespace merging to avoid circular dependencies.
5
+ */
6
+ import { Colors } from "../render/surface.js";
7
+ // Import all component classes for builders
8
+ import { Text } from "./primitives/text.js";
9
+ import { Rectangle } from "./primitives/rectangle.js";
10
+ import { Spacer } from "./primitives/spacer.js";
11
+ import { WrappedText } from "./primitives/wrapped-text.js";
12
+ import { HStack } from "./containers/hstack.js";
13
+ import { VStack } from "./containers/vstack.js";
14
+ import { ZStack } from "./containers/zstack.js";
15
+ import { Scroll } from "./containers/scroll.js";
16
+ import { Canvas } from "./containers/canvas.js";
17
+ import { GeometryReader } from "./containers/geometry-reader.js";
18
+ import { TextInput } from "./textinput.js";
19
+ import { Markdown } from "./markdown.js";
20
+ // Constructor functions that will become static methods on View
21
+ export const ViewConstructors = {
22
+ // Primitives
23
+ text: (s, wrap) => {
24
+ return new Text(s, wrap);
25
+ },
26
+ wrappedText: (s, options) => {
27
+ return new WrappedText(s, options);
28
+ },
29
+ rect: (w, h, fill) => {
30
+ return new Rectangle(w, h, typeof fill === "number" ? fill : (fill?.codePointAt?.(0) ?? 32));
31
+ },
32
+ spacer: (minLength = 0) => {
33
+ return new Spacer(minLength);
34
+ },
35
+ // Containers (accept opts or legacy (gap, { alignment }))
36
+ hstack: (children, optsOrGap = {}, maybe = {}) => {
37
+ const isNum = typeof optsOrGap === "number";
38
+ const gap = isNum ? optsOrGap : (optsOrGap.gap ?? optsOrGap.spacing ?? 1);
39
+ const alignment = (isNum ? maybe.alignment : optsOrGap.alignment) ?? "center";
40
+ return new HStack(children, gap, alignment);
41
+ },
42
+ vstack: (children, optsOrGap = {}, maybe = {}) => {
43
+ const isNum = typeof optsOrGap === "number";
44
+ const gap = isNum ? optsOrGap : (optsOrGap.gap ?? optsOrGap.spacing ?? 0);
45
+ const alignment = (isNum ? maybe.alignment : optsOrGap.alignment) ?? "leading";
46
+ return new VStack(children, gap, alignment);
47
+ },
48
+ zstack: (children, alignment) => {
49
+ return new ZStack(children, alignment ?? { h: "center", v: "center" });
50
+ },
51
+ scroll: (child, opts) => {
52
+ return new Scroll(child, opts ?? {});
53
+ },
54
+ canvas: (paint) => {
55
+ return new Canvas(paint);
56
+ },
57
+ geometryReader: (reader) => {
58
+ return new GeometryReader(reader);
59
+ },
60
+ // Overlay method (moved from chainer to avoid circular dependency)
61
+ overlay: (base, overlay, alignment) => {
62
+ return new ZStack([base, overlay], alignment ?? { h: "center", v: "center" });
63
+ },
64
+ // Components
65
+ textInput: (state, opts) => {
66
+ return new TextInput(state, opts);
67
+ },
68
+ markdown: (text, opts) => {
69
+ return new Markdown(text, opts);
70
+ },
71
+ // Utilities
72
+ Colors,
73
+ };
74
+ //# sourceMappingURL=view-constructors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-constructors.js","sourceRoot":"","sources":["../../src/ui/view-constructors.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@effect-tui/core",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Terminal UI library built with Effect",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "src",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/kitlangton/effect-tui.git"
15
+ },
16
+ "homepage": "https://github.com/kitlangton/effect-tui",
17
+ "bugs": "https://github.com/kitlangton/effect-tui/issues",
18
+ "exports": {
19
+ ".": {
20
+ "import": "./dist/index.js",
21
+ "types": "./dist/index.d.ts"
22
+ }
23
+ },
24
+ "types": "./dist/index.d.ts",
25
+ "keywords": [
26
+ "terminal",
27
+ "tui",
28
+ "effect",
29
+ "cli",
30
+ "console"
31
+ ],
32
+ "author": "Kit Langton",
33
+ "license": "MIT",
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "tag": "alpha"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p .",
40
+ "typecheck": "tsc -p . --noEmit",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "format": "biome format --write .",
44
+ "format:check": "biome format .",
45
+ "prepublishOnly": "bun run typecheck && bun run build"
46
+ },
47
+ "peerDependencies": {
48
+ "effect": "^3.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@effect/vitest": "^0.27.0",
52
+ "@types/node": "^24.10.1",
53
+ "effect": "^3.19.8",
54
+ "typescript": "^5.9.3",
55
+ "vitest": "^4.0.14"
56
+ }
57
+ }
package/src/anim.ts ADDED
@@ -0,0 +1,5 @@
1
+ // anim.ts - Re-export all animation primitives for backwards compatibility
2
+
3
+ export * from "./spring-physics.js"
4
+ export * from "./spring.js"
5
+ export * from "./motion-value.js"
package/src/ansi.ts ADDED
@@ -0,0 +1,83 @@
1
+ // ANSI escape sequences and terminal control utilities
2
+
3
+ /** CSI prefix (ESC [) */
4
+ const CSI = "\x1b["
5
+
6
+ /** Basic ANSI codes and helpers */
7
+ export const ANSI = {
8
+ /** Reset all attributes */
9
+ reset: `${CSI}0m`,
10
+
11
+ /** Cursor control */
12
+ cursor: {
13
+ /** Hide cursor */
14
+ hide: `${CSI}?25l`,
15
+ /** Show cursor */
16
+ show: `${CSI}?25h`,
17
+ /** Move cursor to home position (1,1) */
18
+ home: `${CSI}H`,
19
+ /** Move cursor to 1-based row/column */
20
+ to: (row1: number, col1: number) => `${CSI}${row1};${col1}H`,
21
+ /** Move cursor to 1-based column on current row */
22
+ toCol: (col1: number) => `${CSI}${col1}G`,
23
+ },
24
+
25
+ /** Screen control */
26
+ screen: {
27
+ /** Enter alternate screen buffer */
28
+ altEnter: `${CSI}?1049h`,
29
+ /** Exit alternate screen buffer */
30
+ altExit: `${CSI}?1049l`,
31
+ /** Clear entire display */
32
+ clear: `${CSI}2J`,
33
+ /** Clear scrollback buffer */
34
+ clearScrollback: `${CSI}3J`,
35
+ },
36
+
37
+ /** Scrolling */
38
+ scroll: {
39
+ /** Scroll up by n lines */
40
+ up: (n: number) => `${CSI}${n}S`,
41
+ },
42
+
43
+ /** Device Status Reports / queries */
44
+ report: {
45
+ /** Report Cursor Position (response: ESC [ row ; col R) */
46
+ cursorPosition: `${CSI}6n`,
47
+ },
48
+
49
+ /** Bracketed paste mode */
50
+ paste: {
51
+ bracketed: {
52
+ on: `${CSI}?2004h`,
53
+ off: `${CSI}?2004l`,
54
+ },
55
+ },
56
+
57
+ /** Mouse reporting toggles */
58
+ mouse: {
59
+ /** Enable button tracking and SGR coordinates */
60
+ on: `${CSI}?1002h${CSI}?1006h`,
61
+ /** Disable SGR coords and button tracking */
62
+ off: `${CSI}?1006l${CSI}?1002l`,
63
+ },
64
+ } as const
65
+
66
+ /** Terminal session control utilities (compositions of ANSI primitives) */
67
+ export const Terminal = {
68
+ /** Enter full-screen mode: alternate screen + hide cursor */
69
+ enterFullscreen: ANSI.screen.altEnter + ANSI.cursor.hide,
70
+
71
+ /** Exit full-screen mode: reset + show cursor + exit alternate screen */
72
+ exitFullscreen: ANSI.reset + ANSI.cursor.show + ANSI.screen.altExit,
73
+
74
+ /** Clear screen and move cursor to home */
75
+ clearAndHome: ANSI.screen.clear + ANSI.cursor.home,
76
+
77
+ /** Convenience shorthands for common toggles */
78
+ bracketedPasteOn: ANSI.paste.bracketed.on,
79
+ bracketedPasteOff: ANSI.paste.bracketed.off,
80
+ mouseOn: ANSI.mouse.on,
81
+ mouseOff: ANSI.mouse.off,
82
+ reportCursorPosition: ANSI.report.cursorPosition,
83
+ } as const
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ // Re-export core modules
2
+ export * from "./render/surface.js"
3
+ export * from "./render/buffer.js"
4
+ export * from "./render/measure.js"
5
+ export * from "./anim.js"
6
+ export * from "./keys.js"
7
+
8
+ // UI (canonical View and curated exports)
9
+ export { View } from "./ui/index.js"
10
+ export { ShinyText } from "./ui/index.js"
11
+ export { TextInput, TextInputState, editTextInput } from "./ui/index.js"
12
+ export { Markdown } from "./ui/index.js"
13
+ export type { TextInputStateLike, TextInputStatePlain, TextInputGeom, TextInputOptions } from "./ui/index.js"
14
+ export type { MarkdownOptions } from "./ui/index.js"
15
+ // Geometry store (last-known layout)
16
+ export { geometryStore } from "./ui/core/geometry-store.js"
17
+ export type { ViewId, Rect as GeometryRect, InputGeom } from "./ui/core/geometry-store.js"
18
+
19
+ // Renderers (optional public API)
20
+ export * from "./present/display.js"
21
+ export type { TerminalBackend } from "./runtime/backend_node.js"
package/src/keys.ts ADDED
@@ -0,0 +1,302 @@
1
+ // Minimal key decoder for Node raw input → semantic key messages.
2
+
3
+ export type KeyName =
4
+ | "up"
5
+ | "down"
6
+ | "left"
7
+ | "right"
8
+ | "enter"
9
+ | "escape"
10
+ | "tab"
11
+ | "shift-tab"
12
+ | "backspace"
13
+ | "delete"
14
+ | "home"
15
+ | "end"
16
+ | "pageup"
17
+ | "pagedown"
18
+ | "insert"
19
+ | "return"
20
+ | "space"
21
+ | "char" // printable
22
+
23
+ export type KeyMsg = {
24
+ type: "key"
25
+ name: KeyName
26
+ text?: string // for printable chars
27
+ ctrl?: boolean
28
+ meta?: boolean
29
+ shift?: boolean
30
+ /** Optional phase; currently always "press" but left extensible. */
31
+ phase?: "press" | "repeat" | "release"
32
+ /** Set by renderer to allow handlers to stop further propagation. */
33
+ defaultPrevented?: boolean
34
+ preventDefault?: () => void
35
+ /** Source protocol (raw|csiu|modifyOther|kitty). */
36
+ source?: "raw" | "csiu" | "modify-other" | "kitty"
37
+ }
38
+
39
+ const ESC = "\x1b"
40
+
41
+ export function* decodeKeys(buf: Buffer): Iterable<KeyMsg> {
42
+ // Handle control characters (0x00-0x1F)
43
+ // These are generated by Ctrl+letter combinations
44
+ if (buf.length === 1 && buf[0] !== undefined && buf[0] < 0x20) {
45
+ const byte = buf[0]
46
+
47
+ // Special control characters with specific meanings
48
+ switch (byte) {
49
+ case 0x03: // Ctrl-C (ETX - End of Text)
50
+ yield { type: "key", name: "char", text: "c", ctrl: true }
51
+ return
52
+ case 0x04: // Ctrl-D (EOT - End of Transmission)
53
+ yield { type: "key", name: "char", text: "d", ctrl: true }
54
+ return
55
+ case 0x17: // Ctrl-W (ETB - End of Transmission Block)
56
+ yield { type: "key", name: "char", text: "w", ctrl: true }
57
+ return
58
+ case 0x1a: // Ctrl-Z (SUB - Substitute)
59
+ yield { type: "key", name: "char", text: "z", ctrl: true }
60
+ return
61
+ case 0x0c: // Ctrl-L (FF - Form Feed)
62
+ yield { type: "key", name: "char", text: "l", ctrl: true }
63
+ return
64
+ case 0x09: // Tab (HT - Horizontal Tab)
65
+ yield { type: "key", name: "tab" }
66
+ return
67
+ case 0x0d: // Enter (CR - Carriage Return)
68
+ yield { type: "key", name: "enter" }
69
+ return
70
+ case 0x0a: // Line Feed (LF)
71
+ yield { type: "key", name: "enter" }
72
+ return
73
+ case 0x08: // Backspace (BS)
74
+ yield { type: "key", name: "backspace" }
75
+ return
76
+ case 0x1b: // Escape (ESC)
77
+ yield { type: "key", name: "escape" }
78
+ return
79
+ default:
80
+ // Other control characters: convert back to letter
81
+ // Ctrl-A = 0x01, Ctrl-B = 0x02, etc.
82
+ if (byte >= 0x01 && byte <= 0x1a) {
83
+ const letter = String.fromCharCode(byte + 96) // 0x01 + 96 = 'a'
84
+ yield { type: "key", name: "char", text: letter, ctrl: true }
85
+ } else {
86
+ // Unknown control character, emit raw
87
+ yield { type: "key", name: "char", text: String.fromCharCode(byte), ctrl: true }
88
+ }
89
+ return
90
+ }
91
+ }
92
+
93
+ // Handle 0x7F (DEL) separately as it's outside the control character range
94
+ if (buf.length === 1 && buf[0] === 0x7f) {
95
+ yield { type: "key", name: "backspace" }
96
+ return
97
+ }
98
+
99
+ let s = buf.toString("utf8")
100
+
101
+ // Handle one or more SGR mouse reports possibly coalesced in a single chunk.
102
+ // Format: ESC [ < Cb ; Cx ; Cy (M|m) — terminals vary on the trailing letter; accept any A-Z.
103
+ const sgrRe = /\x1b\x5b<(?<cb>\d+);(?<cx>\d+);(?<cy>\d+)(?<updown>[A-Za-z])/g
104
+ let matchedAny = false
105
+ for (const m of s.matchAll(sgrRe)) {
106
+ matchedAny = true
107
+ const code = parseInt(m.groups?.cb ?? "0", 10)
108
+ if (code === 64) {
109
+ yield { type: "key", name: "pageup", meta: true }
110
+ continue
111
+ }
112
+ if (code === 65) {
113
+ yield { type: "key", name: "pagedown", meta: true }
114
+ }
115
+ // ignore other mouse events
116
+ }
117
+ if (matchedAny) {
118
+ // Strip SGR mouse sequences so they never leak into the input as text
119
+ // Create a fresh regex since the global one was consumed by matchAll()
120
+ s = s.replace(/\x1b\x5b<(?<cb>\d+);(?<cx>\d+);(?<cy>\d+)(?<updown>[A-Za-z])/g, "")
121
+ if (s.length === 0) return
122
+ }
123
+
124
+ // Legacy single SGR report path (when only one arrives)
125
+ const sgr = /^\x1b\[<(?<cb>\d+);(?<cx>\d+);(?<cy>\d+)(?<updown>[A-Za-z])$/.exec(s)
126
+ if (sgr?.groups) {
127
+ const code = parseInt(sgr.groups.cb ?? "0", 10)
128
+ if (code === 64) {
129
+ yield { type: "key", name: "pageup", meta: true }
130
+ return
131
+ }
132
+ if (code === 65) {
133
+ yield { type: "key", name: "pagedown", meta: true }
134
+ return
135
+ }
136
+ return
137
+ }
138
+
139
+ // Meta+Backspace (Option+Backspace on macOS terminals) — often ESC + DEL / BS
140
+ if (s === `${ESC}\x7f` || s === `${ESC}\b` || s === `${ESC}\x08`) {
141
+ yield { type: "key", name: "backspace", meta: true }
142
+ return
143
+ }
144
+
145
+ // Alt/Meta + Enter (many terminals send ESC + CR/LF)
146
+ if (s === `${ESC}\r` || s === `${ESC}\n`) {
147
+ yield { type: "key", name: "enter", meta: true }
148
+ return
149
+ }
150
+
151
+ // modifyOtherKeys (xterm / some terminals): CSI 27;modifier;code~
152
+ const moo = /^\x1b\[27;(?<mod>\d+);(?<code>\d+)~$/.exec(s)
153
+ if (moo?.groups) {
154
+ const mod = parseInt(moo.groups.mod ?? "1", 10) - 1
155
+ const code = parseInt(moo.groups.code ?? "0", 10)
156
+ const shift = !!(mod & 1)
157
+ const alt = !!(mod & 2)
158
+ const ctrl = !!(mod & 4)
159
+ const meta = !!(mod & 8) || alt
160
+ const key = fromCharCode(code, { shift, ctrl, meta, source: "modify-other" })
161
+ if (key) {
162
+ yield key
163
+ return
164
+ }
165
+ }
166
+
167
+ // CSI-u (modern modifyOtherKeys): CSI <code> ; <modifier> u
168
+ const csiu = /^\x1b\[(?<code>\d+);(?<mod>\d+)u$/.exec(s)
169
+ if (csiu?.groups) {
170
+ const mod = parseInt(csiu.groups.mod ?? "1", 10) - 1
171
+ const code = parseInt(csiu.groups.code ?? "0", 10)
172
+ const shift = !!(mod & 1)
173
+ const alt = !!(mod & 2)
174
+ const ctrl = !!(mod & 4)
175
+ const meta = !!(mod & 8) || alt
176
+ const key = fromCharCode(code, { shift, ctrl, meta, source: "csiu" })
177
+ if (key) {
178
+ yield key
179
+ return
180
+ }
181
+ }
182
+
183
+ // Escape (lone ESC) - handle after meta combinations
184
+ if (s === ESC) {
185
+ yield { type: "key", name: "escape" }
186
+ return
187
+ }
188
+
189
+ // Single printable
190
+ if (s.length === 1 && s !== ESC && s !== "\x7f") {
191
+ if (s === " ") {
192
+ yield { type: "key", name: "space", text: " " }
193
+ return
194
+ }
195
+ if (s >= " ") {
196
+ yield { type: "key", name: "char", text: s }
197
+ return
198
+ }
199
+ }
200
+
201
+ // Common escapes
202
+ switch (s) {
203
+ case `${ESC}[A`:
204
+ yield { type: "key", name: "up" }
205
+ return
206
+ case `${ESC}[B`:
207
+ yield { type: "key", name: "down" }
208
+ return
209
+ case `${ESC}[C`:
210
+ yield { type: "key", name: "right" }
211
+ return
212
+ case `${ESC}[D`:
213
+ yield { type: "key", name: "left" }
214
+ return
215
+ case `${ESC}[1;3C`:
216
+ yield { type: "key", name: "right", meta: true }
217
+ return
218
+ case `${ESC}[1;3D`:
219
+ yield { type: "key", name: "left", meta: true }
220
+ return
221
+ case `${ESC}OP`: // F1 (ignored)
222
+ return
223
+ case `${ESC}[H`:
224
+ yield { type: "key", name: "home" }
225
+ return
226
+ case `${ESC}[F`:
227
+ yield { type: "key", name: "end" }
228
+ return
229
+ case `${ESC}[5~`:
230
+ yield { type: "key", name: "pageup" }
231
+ return
232
+ case `${ESC}[6~`:
233
+ yield { type: "key", name: "pagedown" }
234
+ return
235
+ case `${ESC}[2~`:
236
+ yield { type: "key", name: "insert" }
237
+ return
238
+ case `${ESC}[3~`:
239
+ yield { type: "key", name: "delete" }
240
+ return
241
+ case `${ESC}[3;3~`: // Meta+Delete (best-effort for some terminals)
242
+ yield { type: "key", name: "delete", meta: true }
243
+ return
244
+ case `${ESC}[Z`:
245
+ yield { type: "key", name: "shift-tab", shift: true }
246
+ return
247
+ }
248
+
249
+ // Alt/Meta + char (ESC <char>)
250
+ if (s.length === 2 && s[0] === ESC && s[1] && s[1] >= " ") {
251
+ const ch = s[1]
252
+ if (ch === " ") {
253
+ yield { type: "key", name: "space", text: " ", meta: true }
254
+ return
255
+ }
256
+ yield { type: "key", name: "char", text: ch, meta: true }
257
+ return
258
+ }
259
+
260
+ // Fallback: emit printable chars individually
261
+ for (const ch of s) {
262
+ if (ch >= " ") yield { type: "key", name: "char", text: ch }
263
+ }
264
+ }
265
+
266
+ function fromCharCode(
267
+ code: number,
268
+ mods: { shift?: boolean; ctrl?: boolean; meta?: boolean; source: KeyMsg["source"] },
269
+ ): KeyMsg | null {
270
+ const { shift, ctrl, meta, source } = mods
271
+ const common: Omit<KeyMsg, "name"> = {
272
+ type: "key",
273
+ ctrl,
274
+ meta,
275
+ shift,
276
+ phase: "press",
277
+ source,
278
+ }
279
+
280
+ switch (code) {
281
+ case 13:
282
+ return { ...common, name: "enter" }
283
+ case 27:
284
+ return { ...common, name: "escape" }
285
+ case 9:
286
+ return { ...common, name: "tab" }
287
+ case 32:
288
+ return { ...common, name: "space", text: " " }
289
+ case 127:
290
+ case 8:
291
+ return { ...common, name: "backspace" }
292
+ }
293
+
294
+ if (code >= 0 && code < 32) {
295
+ // Control character -> map to letter when possible
296
+ const letter = String.fromCharCode(code + 96)
297
+ return { ...common, name: "char", text: letter, ctrl: true }
298
+ }
299
+
300
+ const ch = String.fromCharCode(code)
301
+ return { ...common, name: "char", text: ch }
302
+ }
@@ -0,0 +1,115 @@
1
+ // linearStack.ts — SwiftUI-like linear stacks with flexible children
2
+
3
+ export type Axis = "horizontal" | "vertical"
4
+ export type Size = { w: number; h: number }
5
+ export type Rect = { x: number; y: number; w: number; h: number }
6
+ export type HAlign = "leading" | "center" | "trailing"
7
+ export type VAlign = "top" | "center" | "bottom"
8
+
9
+ /** flexBase[i] = null for fixed, or the child's base main-size for flexible. */
10
+ export function measureLinear(
11
+ axis: Axis,
12
+ maxW: number,
13
+ maxH: number,
14
+ spacing: number,
15
+ sizes: Size[],
16
+ flexBase: (number | null)[],
17
+ ): Size {
18
+ const n = sizes.length
19
+ const gaps = Math.max(0, n - 1) * Math.max(0, spacing)
20
+
21
+ let mainSum = 0
22
+ let crossMax = 0
23
+ let hasFlex = false
24
+
25
+ for (let i = 0; i < n; i++) {
26
+ const s = sizes[i]
27
+ if (!s) continue
28
+ const main = axis === "horizontal" ? s.w : s.h
29
+ const cross = axis === "horizontal" ? s.h : s.w
30
+ crossMax = Math.max(crossMax, cross)
31
+ hasFlex ||= flexBase[i] != null
32
+ mainSum += flexBase[i] ?? main
33
+ }
34
+
35
+ const maxMain = axis === "horizontal" ? maxW : maxH
36
+ const totalMain = hasFlex ? maxMain : Math.min(maxMain, mainSum + gaps)
37
+
38
+ return axis === "horizontal"
39
+ ? { w: Math.min(maxW, totalMain), h: Math.min(maxH, crossMax) }
40
+ : { w: Math.min(maxW, crossMax), h: Math.min(maxH, totalMain) }
41
+ }
42
+
43
+ export function layoutLinear(
44
+ axis: Axis,
45
+ rect: Rect,
46
+ spacing: number,
47
+ crossAlign: HAlign | VAlign,
48
+ sizes: Size[],
49
+ flexBase: (number | null)[],
50
+ ): Rect[] {
51
+ const n = sizes.length
52
+ const out: Rect[] = new Array(n)
53
+ const gaps = Math.max(0, n - 1) * Math.max(0, spacing)
54
+
55
+ const mainAvail = axis === "horizontal" ? rect.w : rect.h
56
+ const crossAvail = axis === "horizontal" ? rect.h : rect.w
57
+
58
+ let baseSum = 0
59
+ let flexCount = 0
60
+ for (let i = 0; i < n; i++) {
61
+ if (flexBase[i] != null) {
62
+ baseSum += flexBase[i]!
63
+ flexCount++
64
+ } else {
65
+ const size = sizes[i]
66
+ if (size) {
67
+ baseSum += axis === "horizontal" ? size.w : size.h
68
+ }
69
+ }
70
+ }
71
+
72
+ const leftover = Math.max(0, mainAvail - baseSum - gaps)
73
+ const per = flexCount > 0 ? Math.floor(leftover / flexCount) : 0
74
+ let rem = flexCount > 0 ? leftover - per * flexCount : 0
75
+
76
+ const crossOffset = (childCross: number) => {
77
+ if (crossAlign === "center") return Math.floor((crossAvail - childCross) / 2)
78
+ if ((crossAlign === "trailing" && axis === "vertical") || (crossAlign === "bottom" && axis === "horizontal"))
79
+ return Math.max(0, crossAvail - childCross)
80
+ return 0
81
+ }
82
+
83
+ let pos = 0
84
+ for (let i = 0; i < n; i++) {
85
+ const s = sizes[i]
86
+ if (!s) continue
87
+ const baseMain = flexBase[i] ?? (axis === "horizontal" ? s.w : s.h)
88
+ const flexExtra = flexBase[i] != null ? per + (rem > 0 ? (rem--, 1) : 0) : 0
89
+ const wantMain = baseMain + flexExtra
90
+ const childCross = Math.min(crossAvail, axis === "horizontal" ? s.h : s.w)
91
+ const remaining = Math.max(0, mainAvail - pos)
92
+ const takeMain = Math.min(wantMain, remaining)
93
+ const cs = crossOffset(childCross)
94
+
95
+ out[i] =
96
+ axis === "horizontal"
97
+ ? { x: rect.x + pos, y: rect.y + cs, w: Math.max(0, takeMain), h: Math.max(0, childCross) }
98
+ : { x: rect.x + cs, y: rect.y + pos, w: Math.max(0, childCross), h: Math.max(0, takeMain) }
99
+
100
+ pos += takeMain
101
+ if (i < n - 1) pos += spacing
102
+
103
+ if (pos >= mainAvail) {
104
+ for (let j = i + 1; j < n; j++) {
105
+ out[j] =
106
+ axis === "horizontal"
107
+ ? { x: rect.x + mainAvail, y: rect.y, w: 0, h: 0 }
108
+ : { x: rect.x, y: rect.y + mainAvail, w: 0, h: 0 }
109
+ }
110
+ break
111
+ }
112
+ }
113
+
114
+ return out
115
+ }