@effect-tui/core 0.1.1 → 0.1.5

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 (271) hide show
  1. package/README.md +31 -11
  2. package/dist/ansi.d.ts +127 -32
  3. package/dist/ansi.d.ts.map +1 -1
  4. package/dist/ansi.js +159 -37
  5. package/dist/ansi.js.map +1 -1
  6. package/dist/colors.d.ts +139 -0
  7. package/dist/colors.d.ts.map +1 -0
  8. package/dist/colors.js +339 -0
  9. package/dist/colors.js.map +1 -0
  10. package/dist/index.d.ts +6 -10
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +13 -11
  13. package/dist/index.js.map +1 -1
  14. package/dist/keys.d.ts +21 -0
  15. package/dist/keys.d.ts.map +1 -1
  16. package/dist/keys.js +199 -58
  17. package/dist/keys.js.map +1 -1
  18. package/dist/layout/axis-helpers.d.ts +19 -0
  19. package/dist/layout/axis-helpers.d.ts.map +1 -0
  20. package/dist/layout/axis-helpers.js +19 -0
  21. package/dist/layout/axis-helpers.js.map +1 -0
  22. package/dist/output.d.ts +59 -0
  23. package/dist/output.d.ts.map +1 -0
  24. package/dist/output.js +142 -0
  25. package/dist/output.js.map +1 -0
  26. package/dist/render/buffer.d.ts.map +1 -1
  27. package/dist/render/buffer.js +6 -25
  28. package/dist/render/buffer.js.map +1 -1
  29. package/dist/render/graphemes.d.ts +15 -0
  30. package/dist/render/graphemes.d.ts.map +1 -0
  31. package/dist/render/graphemes.js +28 -0
  32. package/dist/render/graphemes.js.map +1 -0
  33. package/dist/render/measure.d.ts +1 -0
  34. package/dist/render/measure.d.ts.map +1 -1
  35. package/dist/render/measure.js +14 -36
  36. package/dist/render/measure.js.map +1 -1
  37. package/dist/render/palette.d.ts.map +1 -1
  38. package/dist/render/palette.js +26 -1
  39. package/dist/render/palette.js.map +1 -1
  40. package/dist/render/segmenter.d.ts +8 -0
  41. package/dist/render/segmenter.d.ts.map +1 -0
  42. package/dist/render/segmenter.js +23 -0
  43. package/dist/render/segmenter.js.map +1 -0
  44. package/dist/render/surface.d.ts +6 -32
  45. package/dist/render/surface.d.ts.map +1 -1
  46. package/dist/render/surface.js +11 -80
  47. package/dist/render/surface.js.map +1 -1
  48. package/dist/runtime/backend_node.d.ts.map +1 -1
  49. package/dist/runtime/backend_node.js.map +1 -1
  50. package/dist/tailwind-colors.d.ts +291 -0
  51. package/dist/tailwind-colors.d.ts.map +1 -0
  52. package/dist/tailwind-colors.js +291 -0
  53. package/dist/tailwind-colors.js.map +1 -0
  54. package/dist/types.d.ts +15 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +3 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +55 -55
  59. package/src/ansi.ts +201 -73
  60. package/src/colors.ts +468 -0
  61. package/src/index.ts +28 -14
  62. package/src/keys.ts +467 -287
  63. package/src/layout/axis-helpers.ts +33 -0
  64. package/src/output.ts +175 -0
  65. package/src/render/buffer.ts +161 -184
  66. package/src/render/graphemes.ts +34 -0
  67. package/src/render/measure.ts +15 -38
  68. package/src/render/palette.ts +98 -77
  69. package/src/render/segmenter.ts +27 -0
  70. package/src/render/surface.ts +139 -225
  71. package/src/runtime/backend_node.ts +71 -71
  72. package/src/tailwind-colors.ts +295 -0
  73. package/src/types.ts +18 -0
  74. package/dist/anim.d.ts +0 -4
  75. package/dist/anim.d.ts.map +0 -1
  76. package/dist/anim.js +0 -5
  77. package/dist/anim.js.map +0 -1
  78. package/dist/layout/linearStack.d.ts +0 -17
  79. package/dist/layout/linearStack.d.ts.map +0 -1
  80. package/dist/layout/linearStack.js +0 -86
  81. package/dist/layout/linearStack.js.map +0 -1
  82. package/dist/motion-value.d.ts +0 -58
  83. package/dist/motion-value.d.ts.map +0 -1
  84. package/dist/motion-value.js +0 -250
  85. package/dist/motion-value.js.map +0 -1
  86. package/dist/present/display.d.ts +0 -58
  87. package/dist/present/display.d.ts.map +0 -1
  88. package/dist/present/display.js +0 -168
  89. package/dist/present/display.js.map +0 -1
  90. package/dist/present/writers/fullscreen.d.ts +0 -19
  91. package/dist/present/writers/fullscreen.d.ts.map +0 -1
  92. package/dist/present/writers/fullscreen.js +0 -55
  93. package/dist/present/writers/fullscreen.js.map +0 -1
  94. package/dist/present/writers/inline.d.ts +0 -20
  95. package/dist/present/writers/inline.d.ts.map +0 -1
  96. package/dist/present/writers/inline.js +0 -92
  97. package/dist/present/writers/inline.js.map +0 -1
  98. package/dist/render/color-utils.d.ts +0 -18
  99. package/dist/render/color-utils.d.ts.map +0 -1
  100. package/dist/render/color-utils.js +0 -58
  101. package/dist/render/color-utils.js.map +0 -1
  102. package/dist/render/diff.d.ts +0 -30
  103. package/dist/render/diff.d.ts.map +0 -1
  104. package/dist/render/diff.js +0 -83
  105. package/dist/render/diff.js.map +0 -1
  106. package/dist/spring-physics.d.ts +0 -36
  107. package/dist/spring-physics.d.ts.map +0 -1
  108. package/dist/spring-physics.js +0 -113
  109. package/dist/spring-physics.js.map +0 -1
  110. package/dist/spring.d.ts +0 -73
  111. package/dist/spring.d.ts.map +0 -1
  112. package/dist/spring.js +0 -136
  113. package/dist/spring.js.map +0 -1
  114. package/dist/ui/containers/canvas.d.ts +0 -13
  115. package/dist/ui/containers/canvas.d.ts.map +0 -1
  116. package/dist/ui/containers/canvas.js +0 -16
  117. package/dist/ui/containers/canvas.js.map +0 -1
  118. package/dist/ui/containers/geometry-reader.d.ts +0 -17
  119. package/dist/ui/containers/geometry-reader.d.ts.map +0 -1
  120. package/dist/ui/containers/geometry-reader.js +0 -24
  121. package/dist/ui/containers/geometry-reader.js.map +0 -1
  122. package/dist/ui/containers/hstack.d.ts +0 -12
  123. package/dist/ui/containers/hstack.d.ts.map +0 -1
  124. package/dist/ui/containers/hstack.js +0 -28
  125. package/dist/ui/containers/hstack.js.map +0 -1
  126. package/dist/ui/containers/scroll.d.ts +0 -28
  127. package/dist/ui/containers/scroll.d.ts.map +0 -1
  128. package/dist/ui/containers/scroll.js +0 -97
  129. package/dist/ui/containers/scroll.js.map +0 -1
  130. package/dist/ui/containers/shared.d.ts +0 -12
  131. package/dist/ui/containers/shared.d.ts.map +0 -1
  132. package/dist/ui/containers/shared.js +0 -19
  133. package/dist/ui/containers/shared.js.map +0 -1
  134. package/dist/ui/containers/vstack.d.ts +0 -12
  135. package/dist/ui/containers/vstack.d.ts.map +0 -1
  136. package/dist/ui/containers/vstack.js +0 -28
  137. package/dist/ui/containers/vstack.js.map +0 -1
  138. package/dist/ui/containers/zstack.d.ts +0 -14
  139. package/dist/ui/containers/zstack.d.ts.map +0 -1
  140. package/dist/ui/containers/zstack.js +0 -36
  141. package/dist/ui/containers/zstack.js.map +0 -1
  142. package/dist/ui/core/geometry-store.d.ts +0 -22
  143. package/dist/ui/core/geometry-store.d.ts.map +0 -1
  144. package/dist/ui/core/geometry-store.js +0 -29
  145. package/dist/ui/core/geometry-store.js.map +0 -1
  146. package/dist/ui/core/geometry.d.ts +0 -34
  147. package/dist/ui/core/geometry.d.ts.map +0 -1
  148. package/dist/ui/core/geometry.js +0 -14
  149. package/dist/ui/core/geometry.js.map +0 -1
  150. package/dist/ui/core/view.d.ts +0 -25
  151. package/dist/ui/core/view.d.ts.map +0 -1
  152. package/dist/ui/core/view.js +0 -34
  153. package/dist/ui/core/view.js.map +0 -1
  154. package/dist/ui/index.d.ts +0 -44
  155. package/dist/ui/index.d.ts.map +0 -1
  156. package/dist/ui/index.js +0 -39
  157. package/dist/ui/index.js.map +0 -1
  158. package/dist/ui/inlinetext.d.ts +0 -24
  159. package/dist/ui/inlinetext.d.ts.map +0 -1
  160. package/dist/ui/inlinetext.js +0 -131
  161. package/dist/ui/inlinetext.js.map +0 -1
  162. package/dist/ui/install.d.ts +0 -22
  163. package/dist/ui/install.d.ts.map +0 -1
  164. package/dist/ui/install.js +0 -66
  165. package/dist/ui/install.js.map +0 -1
  166. package/dist/ui/markdown.d.ts +0 -40
  167. package/dist/ui/markdown.d.ts.map +0 -1
  168. package/dist/ui/markdown.js +0 -351
  169. package/dist/ui/markdown.js.map +0 -1
  170. package/dist/ui/modifiers/border.d.ts +0 -33
  171. package/dist/ui/modifiers/border.d.ts.map +0 -1
  172. package/dist/ui/modifiers/border.js +0 -82
  173. package/dist/ui/modifiers/border.js.map +0 -1
  174. package/dist/ui/modifiers/fill.d.ts +0 -14
  175. package/dist/ui/modifiers/fill.d.ts.map +0 -1
  176. package/dist/ui/modifiers/fill.js +0 -25
  177. package/dist/ui/modifiers/fill.js.map +0 -1
  178. package/dist/ui/modifiers/frame.d.ts +0 -23
  179. package/dist/ui/modifiers/frame.d.ts.map +0 -1
  180. package/dist/ui/modifiers/frame.js +0 -54
  181. package/dist/ui/modifiers/frame.js.map +0 -1
  182. package/dist/ui/modifiers/offset.d.ts +0 -15
  183. package/dist/ui/modifiers/offset.d.ts.map +0 -1
  184. package/dist/ui/modifiers/offset.js +0 -21
  185. package/dist/ui/modifiers/offset.js.map +0 -1
  186. package/dist/ui/modifiers/opacity.d.ts +0 -15
  187. package/dist/ui/modifiers/opacity.d.ts.map +0 -1
  188. package/dist/ui/modifiers/opacity.js +0 -95
  189. package/dist/ui/modifiers/opacity.js.map +0 -1
  190. package/dist/ui/modifiers/padding.d.ts +0 -20
  191. package/dist/ui/modifiers/padding.d.ts.map +0 -1
  192. package/dist/ui/modifiers/padding.js +0 -36
  193. package/dist/ui/modifiers/padding.js.map +0 -1
  194. package/dist/ui/modifiers/styled.d.ts +0 -14
  195. package/dist/ui/modifiers/styled.d.ts.map +0 -1
  196. package/dist/ui/modifiers/styled.js +0 -26
  197. package/dist/ui/modifiers/styled.js.map +0 -1
  198. package/dist/ui/primitives/rectangle.d.ts +0 -15
  199. package/dist/ui/primitives/rectangle.d.ts.map +0 -1
  200. package/dist/ui/primitives/rectangle.js +0 -23
  201. package/dist/ui/primitives/rectangle.js.map +0 -1
  202. package/dist/ui/primitives/spacer.d.ts +0 -13
  203. package/dist/ui/primitives/spacer.d.ts.map +0 -1
  204. package/dist/ui/primitives/spacer.js +0 -16
  205. package/dist/ui/primitives/spacer.js.map +0 -1
  206. package/dist/ui/primitives/text.d.ts +0 -15
  207. package/dist/ui/primitives/text.d.ts.map +0 -1
  208. package/dist/ui/primitives/text.js +0 -79
  209. package/dist/ui/primitives/text.js.map +0 -1
  210. package/dist/ui/primitives/wrapped-text.d.ts +0 -30
  211. package/dist/ui/primitives/wrapped-text.d.ts.map +0 -1
  212. package/dist/ui/primitives/wrapped-text.js +0 -117
  213. package/dist/ui/primitives/wrapped-text.js.map +0 -1
  214. package/dist/ui/shinytext.d.ts +0 -66
  215. package/dist/ui/shinytext.d.ts.map +0 -1
  216. package/dist/ui/shinytext.js +0 -99
  217. package/dist/ui/shinytext.js.map +0 -1
  218. package/dist/ui/text/layout.d.ts +0 -35
  219. package/dist/ui/text/layout.d.ts.map +0 -1
  220. package/dist/ui/text/layout.js +0 -102
  221. package/dist/ui/text/layout.js.map +0 -1
  222. package/dist/ui/textinput.d.ts +0 -140
  223. package/dist/ui/textinput.d.ts.map +0 -1
  224. package/dist/ui/textinput.js +0 -402
  225. package/dist/ui/textinput.js.map +0 -1
  226. package/dist/ui/view-constructors.d.ts +0 -72
  227. package/dist/ui/view-constructors.d.ts.map +0 -1
  228. package/dist/ui/view-constructors.js +0 -74
  229. package/dist/ui/view-constructors.js.map +0 -1
  230. package/src/anim.ts +0 -5
  231. package/src/layout/linearStack.ts +0 -115
  232. package/src/motion-value.ts +0 -335
  233. package/src/present/display.ts +0 -206
  234. package/src/present/writers/fullscreen.ts +0 -58
  235. package/src/present/writers/inline.ts +0 -101
  236. package/src/render/color-utils.ts +0 -60
  237. package/src/render/diff.ts +0 -95
  238. package/src/spring-physics.ts +0 -151
  239. package/src/spring.ts +0 -234
  240. package/src/ui/__snapshots__/wrappedtext.test.ts.snap +0 -57
  241. package/src/ui/containers/canvas.ts +0 -18
  242. package/src/ui/containers/geometry-reader.ts +0 -32
  243. package/src/ui/containers/hstack.ts +0 -33
  244. package/src/ui/containers/scroll.ts +0 -106
  245. package/src/ui/containers/shared.ts +0 -27
  246. package/src/ui/containers/vstack.ts +0 -34
  247. package/src/ui/containers/zstack.ts +0 -37
  248. package/src/ui/core/geometry-store.ts +0 -42
  249. package/src/ui/core/geometry.ts +0 -30
  250. package/src/ui/core/view.ts +0 -49
  251. package/src/ui/index.ts +0 -84
  252. package/src/ui/inlinetext.ts +0 -135
  253. package/src/ui/install.ts +0 -110
  254. package/src/ui/markdown.test.ts +0 -74
  255. package/src/ui/markdown.ts +0 -388
  256. package/src/ui/modifiers/border.ts +0 -100
  257. package/src/ui/modifiers/fill.ts +0 -28
  258. package/src/ui/modifiers/frame.ts +0 -74
  259. package/src/ui/modifiers/offset.ts +0 -23
  260. package/src/ui/modifiers/opacity.ts +0 -93
  261. package/src/ui/modifiers/padding.ts +0 -53
  262. package/src/ui/modifiers/styled.ts +0 -31
  263. package/src/ui/primitives/rectangle.ts +0 -25
  264. package/src/ui/primitives/spacer.ts +0 -18
  265. package/src/ui/primitives/text.ts +0 -85
  266. package/src/ui/primitives/wrapped-text.ts +0 -131
  267. package/src/ui/shinytext.ts +0 -159
  268. package/src/ui/text/layout.ts +0 -119
  269. package/src/ui/textinput.ts +0 -496
  270. package/src/ui/view-constructors.ts +0 -96
  271. package/src/ui/wrappedtext.test.ts +0 -138
@@ -1,18 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect } from "../core/geometry.js"
4
-
5
- export class Canvas extends View {
6
- constructor(readonly paint: (s: Surface, pal: Palette, rect: Rect) => void) {
7
- super()
8
- }
9
-
10
- protected measureContent(maxW: number, maxH: number) {
11
- // Greedy: occupy the full available area (caller can constrain via frame())
12
- return { w: Math.max(0, maxW), h: Math.max(0, maxH) }
13
- }
14
-
15
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
16
- this.paint(s, pal, rect)
17
- }
18
- }
@@ -1,32 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect } from "../core/geometry.js"
4
-
5
- // GeometryReader - provides size information to child builder
6
- export type GeometryProxy = {
7
- width: number
8
- height: number
9
- }
10
-
11
- export class GeometryReader extends View {
12
- constructor(readonly reader: (proxy: GeometryProxy) => View) {
13
- super()
14
- }
15
-
16
- protected measureContent(maxW: number, maxH: number) {
17
- // GeometryReader is greedy - takes all available space
18
- return { w: maxW, h: maxH }
19
- }
20
-
21
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
22
- // Create the child with the actual geometry
23
- const proxy: GeometryProxy = {
24
- width: rect.w,
25
- height: rect.h,
26
- }
27
- const child = this.reader(proxy)
28
- // Measure the child first, then render
29
- child.measure(rect.w, rect.h)
30
- child.render(s, pal, rect)
31
- }
32
- }
@@ -1,33 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect, VAlign } from "../core/geometry.js"
4
- import { measureLinear, layoutLinear } from "../../layout/linearStack.js"
5
- import { Spacer } from "../primitives/spacer.js"
6
- import { flexBaseForAxis } from "./shared.js"
7
-
8
- export class HStack extends View {
9
- constructor(
10
- readonly children: View[],
11
- readonly spacing = 1,
12
- readonly align: VAlign = "center",
13
- ) {
14
- super()
15
- }
16
-
17
- protected measureContent(maxW: number, maxH: number) {
18
- const sizes = this.children.map((c) => c.measure(maxW, maxH))
19
- const flexBase = this.children.map((c, i) =>
20
- c instanceof Spacer ? c.minLength | 0 : flexBaseForAxis(c, "horizontal", sizes[i]),
21
- )
22
- return measureLinear("horizontal", maxW, maxH, this.spacing, sizes, flexBase)
23
- }
24
-
25
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
26
- const sizes = this.children.map((c) => c.measure(rect.w, rect.h))
27
- const flexBase = this.children.map((c, i) =>
28
- c instanceof Spacer ? c.minLength | 0 : flexBaseForAxis(c, "horizontal", sizes[i]),
29
- )
30
- const rects = layoutLinear("horizontal", rect, this.spacing, this.align, sizes, flexBase)
31
- for (let i = 0; i < this.children.length; i++) this.children[i].render(s, pal, rects[i])
32
- }
33
- }
@@ -1,106 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect, VAlign, HAlign } from "../core/geometry.js"
4
-
5
- // Scroll wrapper: viewport with clean SwiftUI-like semantics
6
- export class Scroll extends View {
7
- /** Duck-typed brand so helpers can detect Scroll without imports/cycles */
8
- public readonly __isScroll = true
9
-
10
- private _fullW = 0
11
- private _fullH = 0
12
-
13
- constructor(
14
- readonly child: View,
15
- readonly opts: {
16
- axis?: "vertical" | "horizontal"
17
- offset?: number
18
- align?: VAlign | HAlign
19
- } = {},
20
- ) {
21
- super()
22
- }
23
-
24
- /** Get the maximum valid scroll offset for this content */
25
- getMaxOffset(viewportW: number, viewportH: number): number {
26
- const axis = this.opts.axis ?? "vertical"
27
- if (axis === "vertical") {
28
- const full = this.child.measure(viewportW, Number.MAX_SAFE_INTEGER)
29
- return Math.max(0, full.h - viewportH)
30
- } else {
31
- const full = this.child.measure(Number.MAX_SAFE_INTEGER, viewportH)
32
- return Math.max(0, full.w - viewportW)
33
- }
34
- }
35
-
36
- protected measureContent(maxW: number, maxH: number) {
37
- const axis = this.opts.axis ?? "vertical"
38
- if (axis === "vertical") {
39
- const full = this.child.measure(maxW, Number.MAX_SAFE_INTEGER)
40
- this._fullW = full.w
41
- this._fullH = full.h
42
- // Greedy: take all available space (viewport semantics)
43
- return { w: Math.min(maxW, full.w), h: maxH }
44
- } else {
45
- const full = this.child.measure(Number.MAX_SAFE_INTEGER, maxH)
46
- this._fullW = full.w
47
- this._fullH = full.h
48
- return { w: maxW, h: Math.min(maxH, full.h) }
49
- }
50
- }
51
-
52
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
53
- const axis = this.opts.axis ?? "vertical"
54
- const align = this.opts.align ?? "top"
55
- const rawOffset = this.opts.offset ?? 0
56
-
57
- // Ensure we have measured the child content
58
- if (this._fullH === 0 && this._fullW === 0) {
59
- this.measureContent(rect.w, rect.h)
60
- // measureContent sets _fullH and _fullW as side effects
61
- }
62
-
63
- if (axis === "vertical") {
64
- const overflow = Math.max(0, this._fullH - rect.h)
65
- // Clamp offset to valid range [0, overflow]
66
- const offset = Math.max(0, Math.min(overflow, rawOffset))
67
- let start: number
68
-
69
- if (align === "bottom") {
70
- // Bottom-pinned: offset 0 shows last lines, positive offset scrolls up from bottom
71
- if (overflow === 0) {
72
- // Content fits: bottom-align by pushing content down
73
- start = -(rect.h - this._fullH)
74
- } else {
75
- // Content overflows: start from bottom minus offset
76
- start = overflow - offset
77
- }
78
- } else {
79
- // Top-aligned: traditional scrolling
80
- start = offset
81
- }
82
-
83
- const childRect = { x: rect.x, y: rect.y - start, w: rect.w, h: this._fullH }
84
- s.withClip(rect.x, rect.y, rect.w, rect.h, () => this.child.render(s, pal, childRect))
85
- } else {
86
- const overflow = Math.max(0, this._fullW - rect.w)
87
- // Clamp offset to valid range [0, overflow]
88
- const offset = Math.max(0, Math.min(overflow, rawOffset))
89
- let start: number
90
-
91
- if (align === "trailing") {
92
- // Right-pinned: similar logic for horizontal
93
- if (overflow === 0) {
94
- start = -(rect.w - this._fullW)
95
- } else {
96
- start = overflow - offset
97
- }
98
- } else {
99
- start = offset
100
- }
101
-
102
- const childRect = { x: rect.x - start, y: rect.y, w: this._fullW, h: rect.h }
103
- s.withClip(rect.x, rect.y, rect.w, rect.h, () => this.child.render(s, pal, childRect))
104
- }
105
- }
106
- }
@@ -1,27 +0,0 @@
1
- // Shared utilities for containers
2
- import type { View } from "../core/view.js"
3
-
4
- // Check if a node is a Fill wrapper for a given axis
5
- export function isFillForAxis(node: View, axis: "horizontal" | "vertical"): boolean {
6
- return node.constructor.name === "Fill" && (node as any).axis === axis
7
- }
8
-
9
- // Get the main-axis size from a measured size
10
- export function mainSize(axis: "horizontal" | "vertical", size: { w: number; h: number }): number {
11
- return axis === "horizontal" ? size.w : size.h
12
- }
13
-
14
- /** Return the flex base size for a node on an axis, or null if fixed. */
15
- export function flexBaseForAxis(
16
- node: View,
17
- axis: "horizontal" | "vertical",
18
- measured: { w: number; h: number },
19
- ): number | null {
20
- // Scroll should be flexible but with a *zero* base so siblings get their fixed space first.
21
- if ((node as any).__isScroll) return 0
22
-
23
- // Fill is flexible with intrinsic base equal to its measured main size.
24
- if (isFillForAxis(node, axis)) return mainSize(axis, measured)
25
-
26
- return null
27
- }
@@ -1,34 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect, HAlign } from "../core/geometry.js"
4
- import { measureLinear, layoutLinear } from "../../layout/linearStack.js"
5
- import { Spacer } from "../primitives/spacer.js"
6
- import { flexBaseForAxis } from "./shared.js"
7
-
8
- export class VStack extends View {
9
- constructor(
10
- readonly children: View[],
11
- readonly spacing = 0,
12
- readonly align: HAlign = "leading",
13
- ) {
14
- super()
15
- }
16
-
17
- protected measureContent(maxW: number, maxH: number) {
18
- const sizes = this.children.map((c) => c.measure(maxW, maxH))
19
- const flexBase = this.children.map((c, i) =>
20
- c instanceof Spacer ? c.minLength | 0 : flexBaseForAxis(c, "vertical", sizes[i]),
21
- )
22
- return measureLinear("vertical", maxW, maxH, this.spacing, sizes, flexBase)
23
- }
24
-
25
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
26
- const sizes = this.children.map((c) => c.measure(rect.w, rect.h))
27
- const flexBase = this.children.map((c, i) =>
28
- c instanceof Spacer ? c.minLength | 0 : flexBaseForAxis(c, "vertical", sizes[i]),
29
- )
30
- const rects = layoutLinear("vertical", rect, this.spacing, this.align, sizes, flexBase)
31
-
32
- for (let i = 0; i < this.children.length; i++) this.children[i].render(s, pal, rects[i])
33
- }
34
- }
@@ -1,37 +0,0 @@
1
- import type { Palette, Surface } from "../../render/surface.js"
2
- import { View } from "../core/view.js"
3
- import type { Rect, Align2D } from "../core/geometry.js"
4
-
5
- // ZStack (overlay)
6
- export class ZStack extends View {
7
- constructor(
8
- readonly children: View[],
9
- readonly alignment: Align2D = { h: "center", v: "center" },
10
- ) {
11
- super()
12
- }
13
-
14
- protected measureContent(maxW: number, maxH: number) {
15
- let w = 0,
16
- h = 0
17
- for (const c of this.children) {
18
- const m = c.measure(maxW, maxH)
19
- w = Math.max(w, m.w)
20
- h = Math.max(h, m.h)
21
- }
22
- return { w: Math.min(maxW, w), h: Math.min(maxH, h) }
23
- }
24
-
25
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
26
- for (const c of this.children) {
27
- const m = c.measure(rect.w, rect.h)
28
- let x = rect.x,
29
- y = rect.y
30
- if (this.alignment.h === "center") x += Math.floor((rect.w - m.w) / 2)
31
- else if (this.alignment.h === "trailing") x += Math.max(0, rect.w - m.w)
32
- if (this.alignment.v === "center") y += Math.floor((rect.h - m.h) / 2)
33
- else if (this.alignment.v === "bottom") y += Math.max(0, rect.h - m.h)
34
- c.render(s, pal, { x, y, w: Math.min(rect.w, m.w), h: Math.min(rect.h, m.h) })
35
- }
36
- }
37
- }
@@ -1,42 +0,0 @@
1
- /* geometry-store.ts — last-known layout/geometry store keyed by ViewId */
2
-
3
- import type { Rect } from "./geometry.js"
4
-
5
- export type ViewId = string
6
- export type { Rect }
7
- export type InputGeom = { firstW: number; wrapW: number; xFirst: number; xOther: number }
8
-
9
- class GeometryStoreImpl {
10
- private frameNo = 0
11
- private rects = new Map<ViewId, Rect>()
12
- private inputs = new Map<ViewId, InputGeom>()
13
-
14
- beginFrame(frameNo?: number) {
15
- // Clear for the new render pass; consumers will read after endFrame
16
- this.frameNo = frameNo ?? this.frameNo + 1
17
- this.rects.clear()
18
- this.inputs.clear()
19
- }
20
-
21
- endFrame() {
22
- // no-op for now; snapshot already in maps
23
- }
24
-
25
- setRect(id: ViewId, rect: Rect) {
26
- this.rects.set(id, rect)
27
- }
28
-
29
- setInputGeom(id: ViewId, g: InputGeom) {
30
- this.inputs.set(id, g)
31
- }
32
-
33
- getRect(id: ViewId): Rect | undefined {
34
- return this.rects.get(id)
35
- }
36
-
37
- getInputGeom(id: ViewId): InputGeom | undefined {
38
- return this.inputs.get(id)
39
- }
40
- }
41
-
42
- export const geometryStore = new GeometryStoreImpl()
@@ -1,30 +0,0 @@
1
- // Basic geometry types and alignments
2
-
3
- export type Rect = { x: number; y: number; w: number; h: number }
4
-
5
- export type HAlign = "leading" | "center" | "trailing"
6
- export type VAlign = "top" | "center" | "bottom"
7
- export type Align2D = { h: HAlign; v: VAlign }
8
-
9
- export const Align = {
10
- leading: "leading" as HAlign,
11
- center: "center" as HAlign,
12
- trailing: "trailing" as HAlign,
13
- top: "top" as VAlign,
14
- bottom: "bottom" as VAlign,
15
- topLeading: { h: "leading", v: "top" } as Align2D,
16
- topTrailing: { h: "trailing", v: "top" } as Align2D,
17
- bottomLeading: { h: "leading", v: "bottom" } as Align2D,
18
- bottomTrailing: { h: "trailing", v: "bottom" } as Align2D,
19
- center2D: { h: "center", v: "center" } as Align2D,
20
- }
21
-
22
- export type FrameSpec = {
23
- width?: number // exact width; use Infinity to fill
24
- height?: number
25
- minWidth?: number
26
- maxWidth?: number
27
- minHeight?: number
28
- maxHeight?: number
29
- alignment?: Align2D // child placement within surplus
30
- }
@@ -1,49 +0,0 @@
1
- /* view.ts — Core View class (renamed from CoreViewBase)
2
- *
3
- * This module defines the fundamental UI View class with minimal base functionality.
4
- * It has NO imports of concrete modifiers to avoid circular dependencies.
5
- */
6
-
7
- import type { Palette, Surface } from "../../render/surface.js"
8
- import type { Rect } from "./geometry.js"
9
- import { geometryStore } from "./geometry-store.js"
10
-
11
- export interface View {
12
- measure(maxW: number, maxH: number): { w: number; h: number }
13
- render(s: Surface, pal: Palette, rect: Rect): void
14
- }
15
-
16
- /**
17
- * Abstract base class providing a minimal View foundation.
18
- * Subclasses implement measureContent/renderContent for their specific behavior.
19
- * All styling, padding, and frame logic has been moved to modifier classes.
20
- */
21
- export abstract class View implements View {
22
- protected _id?: string
23
-
24
- /** Attach a stable id to this view for geometry tracking. */
25
- id(id: string): View {
26
- const c = Object.create(Object.getPrototypeOf(this)) as this
27
- // copy own fields
28
- for (const k of Object.keys(this) as Array<keyof this>) (c as any)[k] = (this as any)[k]
29
- c._id = id
30
- return c
31
- }
32
-
33
- measure(maxW: number, maxH: number) {
34
- return this.measureContent(maxW, maxH)
35
- }
36
-
37
- render(s: Surface, pal: Palette, rect: Rect) {
38
- // Record geometry if id is present
39
- if (this._id) {
40
- geometryStore.setRect(this._id, rect)
41
- }
42
-
43
- this.renderContent(s, pal, rect)
44
- }
45
-
46
- // ---- Hooks for subclasses ----
47
- protected abstract measureContent(maxW: number, maxH: number): { w: number; h: number }
48
- protected abstract renderContent(s: Surface, pal: Palette, rect: Rect): void
49
- }
package/src/ui/index.ts DELETED
@@ -1,84 +0,0 @@
1
- // Single barrel export for the View-centric UI system
2
-
3
- // Install chainable modifiers (side-effect import)
4
- import "./install.js"
5
-
6
- // Core geometry (types + helpers)
7
- export * from "./core/geometry.js"
8
-
9
- // View class will be exported at bottom after enhancement
10
-
11
- // Primitives
12
- export * from "./primitives/text.js"
13
- export * from "./primitives/rectangle.js"
14
- export * from "./primitives/spacer.js"
15
- export * from "./primitives/wrapped-text.js"
16
-
17
- // Containers
18
- export * from "./containers/hstack.js"
19
- export * from "./containers/vstack.js"
20
- export * from "./containers/zstack.js"
21
- export * from "./containers/scroll.js"
22
- export * from "./containers/canvas.js"
23
- export * from "./containers/geometry-reader.js"
24
-
25
- // Modifiers (classes exported, chainers are installed via side effects above)
26
- export * from "./modifiers/border.js"
27
- export * from "./modifiers/offset.js"
28
- export * from "./modifiers/fill.js"
29
- export * from "./modifiers/padding.js"
30
- export { Frame, type FrameSpec as ModifierFrameSpec } from "./modifiers/frame.js"
31
- export * from "./modifiers/styled.js"
32
-
33
- // Components & utilities
34
- export { ShinyText } from "./shinytext.js"
35
- export {
36
- TextInput,
37
- TextInputState,
38
- editTextInput,
39
- type TextInputStateLike,
40
- type TextInputStatePlain,
41
- type TextInputGeom,
42
- type TextInputOptions,
43
- } from "./textinput.js"
44
- export { Markdown, type MarkdownOptions } from "./markdown.js"
45
-
46
- // Import View class and constructors
47
- import { View as ViewClass } from "./core/view.js"
48
- import { ViewConstructors } from "./view-constructors.js"
49
-
50
- // Use namespace merging to add static methods to View class
51
- declare module "./core/view.js" {
52
- namespace View {
53
- // Primitives
54
- const text: typeof ViewConstructors.text
55
- const wrappedText: typeof ViewConstructors.wrappedText
56
- const rect: typeof ViewConstructors.rect
57
- const spacer: typeof ViewConstructors.spacer
58
-
59
- // Containers
60
- const hstack: typeof ViewConstructors.hstack
61
- const vstack: typeof ViewConstructors.vstack
62
- const zstack: typeof ViewConstructors.zstack
63
- const scroll: typeof ViewConstructors.scroll
64
- const canvas: typeof ViewConstructors.canvas
65
- const geometryReader: typeof ViewConstructors.geometryReader
66
- const overlay: typeof ViewConstructors.overlay
67
-
68
- // Components
69
- const textInput: typeof ViewConstructors.textInput
70
- const markdown: typeof ViewConstructors.markdown
71
-
72
- // Utilities
73
- const Colors: typeof ViewConstructors.Colors
74
- }
75
- }
76
-
77
- // Install all the constructor methods on the View class
78
- const View = ViewClass as typeof ViewClass & typeof ViewConstructors
79
-
80
- // Install all constructors
81
- Object.assign(View, ViewConstructors)
82
-
83
- // Export the enhanced View
84
- export { View }
@@ -1,135 +0,0 @@
1
- import type { Palette, Surface, StyleSpec } from "../render/surface.js"
2
- import { View } from "./core/view.js"
3
- import type { Rect } from "./core/geometry.js"
4
- import type { WrappingOptions } from "./primitives/wrapped-text.js"
5
-
6
- export type TextFragment = {
7
- text: string
8
- style?: StyleSpec
9
- }
10
-
11
- type Token = { text: string; style?: StyleSpec; kind: "word" | "space" }
12
-
13
- function toTokens(fragments: TextFragment[]): Token[] {
14
- const tokens: Token[] = []
15
- for (const frag of fragments) {
16
- const parts = frag.text.split(/(\s+)/)
17
- for (const p of parts) {
18
- if (p.length === 0) continue
19
- if (/^\s+$/.test(p)) tokens.push({ text: p.replace(/\s+/g, " "), style: frag.style, kind: "space" })
20
- else tokens.push({ text: p, style: frag.style, kind: "word" })
21
- }
22
- }
23
- return tokens
24
- }
25
-
26
- function strlen(s: string): number {
27
- // Best-effort: count code points. Rendering will use wcwidth-aware clipping.
28
- return [...s].length
29
- }
30
-
31
- export class InlineText extends View {
32
- constructor(
33
- readonly fragments: TextFragment[],
34
- readonly options: WrappingOptions = {},
35
- ) {
36
- super()
37
- }
38
-
39
- private layout(maxW: number): Array<Array<Token>> {
40
- const { wordWrap = true, breakWords = true } = this.options
41
- const toks = toTokens(this.fragments)
42
- const lines: Array<Array<Token>> = []
43
- let line: Token[] = []
44
- let used = 0
45
- const flush = () => {
46
- if (line.length === 0) return
47
- lines.push(line)
48
- line = []
49
- used = 0
50
- }
51
- for (const t of toks) {
52
- if (t.kind === "space") {
53
- if (used === 0) continue // skip leading spaces
54
- const w = 1
55
- if (used + w <= maxW) {
56
- line.push({ ...t, text: " " })
57
- used += w
58
- } else {
59
- flush()
60
- }
61
- continue
62
- }
63
- const w = strlen(t.text)
64
- if (!wordWrap) {
65
- // No wrapping: push to current line; if overflow, start new line first
66
- if (used + w > maxW && used > 0) flush()
67
- line.push(t)
68
- used = Math.min(maxW, used + w)
69
- continue
70
- }
71
- if (w > maxW) {
72
- if (breakWords) {
73
- const remaining = [...t.text]
74
- while (remaining.length > 0) {
75
- const room = Math.max(0, maxW - used)
76
- if (room === 0) flush()
77
- const take = Math.min(remaining.length, Math.max(1, maxW - used))
78
- const chunk = remaining.splice(0, take).join("")
79
- line.push({ ...t, text: chunk })
80
- used += strlen(chunk)
81
- if (remaining.length > 0) flush()
82
- }
83
- } else {
84
- // Place as its own line (will clip visually)
85
- if (used > 0) flush()
86
- line.push(t)
87
- flush()
88
- }
89
- } else if (used + w <= maxW) {
90
- line.push(t)
91
- used += w
92
- } else {
93
- flush()
94
- line.push(t)
95
- used = w
96
- }
97
- }
98
- flush()
99
- if (lines.length === 0) lines.push([])
100
- return lines
101
- }
102
-
103
- protected measureContent(maxW: number, maxH: number) {
104
- const lines = this.layout(maxW)
105
- const h = Math.min(maxH, Math.max(1, lines.length))
106
- return { w: maxW, h }
107
- }
108
-
109
- protected renderContent(s: Surface, pal: Palette, rect: Rect) {
110
- const lines = this.layout(rect.w)
111
- const h = Math.min(rect.h, lines.length)
112
- for (let y = 0; y < h; y++) {
113
- let x = 0
114
- for (const seg of lines[y]) {
115
- if (x >= rect.w) break
116
- const id = pal.id(seg.style)
117
- const max = Math.max(0, rect.w - x)
118
- if (max <= 0) break
119
- s.drawText(rect.x + x, rect.y + y, seg.text, id, max)
120
- x += strlen(seg.text)
121
- }
122
- }
123
- }
124
- }
125
-
126
- // Builder contribution (optional if needed later)
127
- export type ViewInlineTextExt = {
128
- inline(fragments: TextFragment[], opts?: WrappingOptions): InlineText
129
- }
130
-
131
- export const viewInlineText: ViewInlineTextExt = {
132
- inline(fragments: TextFragment[], opts?: WrappingOptions): InlineText {
133
- return new InlineText(fragments, opts)
134
- },
135
- }