termcast 1.3.48 → 1.3.49

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 (255) hide show
  1. package/dist/build.d.ts.map +1 -1
  2. package/dist/build.js +12 -0
  3. package/dist/build.js.map +1 -1
  4. package/dist/cli.js +5 -40
  5. package/dist/cli.js.map +1 -1
  6. package/dist/colors.d.ts +7 -7
  7. package/dist/colors.js +7 -7
  8. package/dist/compile.d.ts +6 -1
  9. package/dist/compile.d.ts.map +1 -1
  10. package/dist/compile.js +45 -26
  11. package/dist/compile.js.map +1 -1
  12. package/dist/components/actions.js +1 -1
  13. package/dist/components/actions.js.map +1 -1
  14. package/dist/components/bar-chart.d.ts +38 -0
  15. package/dist/components/bar-chart.d.ts.map +1 -0
  16. package/dist/components/bar-chart.js +158 -0
  17. package/dist/components/bar-chart.js.map +1 -0
  18. package/dist/components/bar-graph.d.ts +41 -0
  19. package/dist/components/bar-graph.d.ts.map +1 -0
  20. package/dist/components/bar-graph.js +95 -0
  21. package/dist/components/bar-graph.js.map +1 -0
  22. package/dist/components/detail.d.ts.map +1 -1
  23. package/dist/components/detail.js +5 -7
  24. package/dist/components/detail.js.map +1 -1
  25. package/dist/components/footer.d.ts.map +1 -1
  26. package/dist/components/footer.js +8 -9
  27. package/dist/components/footer.js.map +1 -1
  28. package/dist/components/form/date-picker.d.ts.map +1 -1
  29. package/dist/components/form/date-picker.js +7 -1
  30. package/dist/components/form/date-picker.js.map +1 -1
  31. package/dist/components/form/dropdown.d.ts.map +1 -1
  32. package/dist/components/form/dropdown.js +10 -2
  33. package/dist/components/form/dropdown.js.map +1 -1
  34. package/dist/components/form/index.d.ts.map +1 -1
  35. package/dist/components/form/index.js +4 -5
  36. package/dist/components/form/index.js.map +1 -1
  37. package/dist/components/form/use-form-navigation.d.ts.map +1 -1
  38. package/dist/components/form/use-form-navigation.js +6 -0
  39. package/dist/components/form/use-form-navigation.js.map +1 -1
  40. package/dist/components/graph.d.ts +111 -0
  41. package/dist/components/graph.d.ts.map +1 -0
  42. package/dist/components/graph.js +392 -0
  43. package/dist/components/graph.js.map +1 -0
  44. package/dist/components/icon.js +5 -5
  45. package/dist/components/icon.js.map +1 -1
  46. package/dist/components/list.d.ts +53 -5
  47. package/dist/components/list.d.ts.map +1 -1
  48. package/dist/components/list.js +125 -71
  49. package/dist/components/list.js.map +1 -1
  50. package/dist/components/loading-bar.js +3 -3
  51. package/dist/components/loading-bar.js.map +1 -1
  52. package/dist/components/loading-text.d.ts +1 -1
  53. package/dist/components/loading-text.d.ts.map +1 -1
  54. package/dist/components/loading-text.js +3 -1
  55. package/dist/components/loading-text.js.map +1 -1
  56. package/dist/components/metadata.js +2 -2
  57. package/dist/components/metadata.js.map +1 -1
  58. package/dist/components/row.d.ts +10 -0
  59. package/dist/components/row.d.ts.map +1 -0
  60. package/dist/components/row.js +12 -0
  61. package/dist/components/row.js.map +1 -0
  62. package/dist/components/table.d.ts +57 -0
  63. package/dist/components/table.d.ts.map +1 -0
  64. package/dist/components/table.js +365 -0
  65. package/dist/components/table.js.map +1 -0
  66. package/dist/descendants.js +13 -13
  67. package/dist/descendants.js.map +1 -1
  68. package/dist/examples/bar-graph-weekly.d.ts +2 -0
  69. package/dist/examples/bar-graph-weekly.d.ts.map +1 -0
  70. package/dist/examples/bar-graph-weekly.js +95 -0
  71. package/dist/examples/bar-graph-weekly.js.map +1 -0
  72. package/dist/examples/components-weird-places.d.ts +2 -0
  73. package/dist/examples/components-weird-places.d.ts.map +1 -0
  74. package/dist/examples/components-weird-places.js +46 -0
  75. package/dist/examples/components-weird-places.js.map +1 -0
  76. package/dist/examples/graph-bar-chart.d.ts +2 -0
  77. package/dist/examples/graph-bar-chart.d.ts.map +1 -0
  78. package/dist/examples/graph-bar-chart.js +270 -0
  79. package/dist/examples/graph-bar-chart.js.map +1 -0
  80. package/dist/examples/graph-multi-series.d.ts +2 -0
  81. package/dist/examples/graph-multi-series.d.ts.map +1 -0
  82. package/dist/examples/graph-multi-series.js +23 -0
  83. package/dist/examples/graph-multi-series.js.map +1 -0
  84. package/dist/examples/graph-polymarket.d.ts +2 -0
  85. package/dist/examples/graph-polymarket.d.ts.map +1 -0
  86. package/dist/examples/graph-polymarket.js +109 -0
  87. package/dist/examples/graph-polymarket.js.map +1 -0
  88. package/dist/examples/graph-row.d.ts +2 -0
  89. package/dist/examples/graph-row.d.ts.map +1 -0
  90. package/dist/examples/graph-row.js +226 -0
  91. package/dist/examples/graph-row.js.map +1 -0
  92. package/dist/examples/graph-styles.d.ts +2 -0
  93. package/dist/examples/graph-styles.d.ts.map +1 -0
  94. package/dist/examples/graph-styles.js +316 -0
  95. package/dist/examples/graph-styles.js.map +1 -0
  96. package/dist/examples/list-accessory-table.d.ts +2 -0
  97. package/dist/examples/list-accessory-table.d.ts.map +1 -0
  98. package/dist/examples/list-accessory-table.js +46 -0
  99. package/dist/examples/list-accessory-table.js.map +1 -0
  100. package/dist/examples/list-item-accessories.d.ts +2 -0
  101. package/dist/examples/list-item-accessories.d.ts.map +1 -0
  102. package/dist/examples/list-item-accessories.js +27 -0
  103. package/dist/examples/list-item-accessories.js.map +1 -0
  104. package/dist/examples/list-no-actions.d.ts +2 -0
  105. package/dist/examples/list-no-actions.d.ts.map +1 -0
  106. package/dist/examples/list-no-actions.js +7 -0
  107. package/dist/examples/list-no-actions.js.map +1 -0
  108. package/dist/examples/simple-detail-table.d.ts +2 -0
  109. package/dist/examples/simple-detail-table.d.ts.map +1 -0
  110. package/dist/examples/simple-detail-table.js +45 -0
  111. package/dist/examples/simple-detail-table.js.map +1 -0
  112. package/dist/examples/simple-graph.d.ts +2 -0
  113. package/dist/examples/simple-graph.d.ts.map +1 -0
  114. package/dist/examples/simple-graph.js +32 -0
  115. package/dist/examples/simple-graph.js.map +1 -0
  116. package/dist/examples/simple-table-wrap.d.ts +2 -0
  117. package/dist/examples/simple-table-wrap.d.ts.map +1 -0
  118. package/dist/examples/simple-table-wrap.js +37 -0
  119. package/dist/examples/simple-table-wrap.js.map +1 -0
  120. package/dist/examples/table-edge-cases.d.ts +2 -0
  121. package/dist/examples/table-edge-cases.d.ts.map +1 -0
  122. package/dist/examples/table-edge-cases.js +70 -0
  123. package/dist/examples/table-edge-cases.js.map +1 -0
  124. package/dist/examples/table-flex-grow.d.ts +2 -0
  125. package/dist/examples/table-flex-grow.d.ts.map +1 -0
  126. package/dist/examples/table-flex-grow.js +18 -0
  127. package/dist/examples/table-flex-grow.js.map +1 -0
  128. package/dist/extensions/dev.d.ts.map +1 -1
  129. package/dist/extensions/dev.js +5 -1
  130. package/dist/extensions/dev.js.map +1 -1
  131. package/dist/globals.d.ts +1 -0
  132. package/dist/globals.d.ts.map +1 -1
  133. package/dist/globals.js +2 -0
  134. package/dist/globals.js.map +1 -1
  135. package/dist/index.d.ts +10 -0
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/index.js +10 -0
  138. package/dist/index.js.map +1 -1
  139. package/dist/internal/date-picker-widget.d.ts.map +1 -1
  140. package/dist/internal/date-picker-widget.js +4 -0
  141. package/dist/internal/date-picker-widget.js.map +1 -1
  142. package/dist/internal/providers.d.ts.map +1 -1
  143. package/dist/internal/providers.js +1 -3
  144. package/dist/internal/providers.js.map +1 -1
  145. package/dist/markdown-utils.d.ts +22 -1
  146. package/dist/markdown-utils.d.ts.map +1 -1
  147. package/dist/markdown-utils.js +66 -1
  148. package/dist/markdown-utils.js.map +1 -1
  149. package/dist/opentui.d.ts +4 -0
  150. package/dist/opentui.d.ts.map +1 -0
  151. package/dist/opentui.js +3 -0
  152. package/dist/opentui.js.map +1 -0
  153. package/dist/release.d.ts +2 -1
  154. package/dist/release.d.ts.map +1 -1
  155. package/dist/release.js +2 -1
  156. package/dist/release.js.map +1 -1
  157. package/dist/state.d.ts +1 -0
  158. package/dist/state.d.ts.map +1 -1
  159. package/dist/state.js +1 -1
  160. package/dist/state.js.map +1 -1
  161. package/dist/theme.d.ts +1 -0
  162. package/dist/theme.d.ts.map +1 -1
  163. package/dist/theme.js +13 -0
  164. package/dist/theme.js.map +1 -1
  165. package/dist/themes/nerv.json +227 -0
  166. package/dist/themes/termcast.json +72 -71
  167. package/dist/themes.d.ts +2 -1
  168. package/dist/themes.d.ts.map +1 -1
  169. package/dist/themes.js +7 -5
  170. package/dist/themes.js.map +1 -1
  171. package/dist/utils.d.ts.map +1 -1
  172. package/dist/utils.js +3 -0
  173. package/dist/utils.js.map +1 -1
  174. package/package.json +12 -4
  175. package/src/build.tsx +13 -0
  176. package/src/cli.tsx +5 -49
  177. package/src/colors.tsx +7 -7
  178. package/src/compile.tsx +52 -29
  179. package/src/components/actions.tsx +1 -1
  180. package/src/components/bar-chart.tsx +271 -0
  181. package/src/components/bar-graph.tsx +214 -0
  182. package/src/components/detail.tsx +7 -8
  183. package/src/components/footer.tsx +14 -15
  184. package/src/components/form/date-picker.tsx +9 -0
  185. package/src/components/form/dropdown.tsx +13 -3
  186. package/src/components/form/index.tsx +4 -6
  187. package/src/components/form/use-form-navigation.tsx +6 -0
  188. package/src/components/graph.tsx +506 -0
  189. package/src/components/icon.tsx +5 -5
  190. package/src/components/list.tsx +210 -102
  191. package/src/components/loading-bar.tsx +3 -3
  192. package/src/components/loading-text.tsx +4 -2
  193. package/src/components/metadata.tsx +2 -2
  194. package/src/components/row.tsx +31 -0
  195. package/src/components/table.tsx +511 -0
  196. package/src/descendants.tsx +13 -13
  197. package/src/examples/action-shortcut.vitest.tsx +1 -1
  198. package/src/examples/actions-context.vitest.tsx +1 -1
  199. package/src/examples/bar-graph-weekly.tsx +264 -0
  200. package/src/examples/bar-graph-weekly.vitest.tsx +275 -0
  201. package/src/examples/detail-metadata-showcase.vitest.tsx +8 -8
  202. package/src/examples/form-basic.vitest.tsx +239 -0
  203. package/src/examples/form-dropdown.vitest.tsx +29 -29
  204. package/src/examples/form-tagpicker.vitest.tsx +27 -27
  205. package/src/examples/github.vitest.tsx +4 -4
  206. package/src/examples/graph-bar-chart.tsx +408 -0
  207. package/src/examples/graph-bar-chart.vitest.tsx +283 -0
  208. package/src/examples/graph-multi-series.tsx +36 -0
  209. package/src/examples/graph-multi-series.vitest.tsx +89 -0
  210. package/src/examples/graph-polymarket.tsx +182 -0
  211. package/src/examples/graph-polymarket.vitest.tsx +130 -0
  212. package/src/examples/graph-row.tsx +347 -0
  213. package/src/examples/graph-row.vitest.tsx +295 -0
  214. package/src/examples/graph-styles.tsx +457 -0
  215. package/src/examples/graph-styles.vitest.tsx +322 -0
  216. package/src/examples/list-accessory-table.tsx +77 -0
  217. package/src/examples/list-detail-metadata.vitest.tsx +21 -21
  218. package/src/examples/list-dropdown-default.vitest.tsx +12 -12
  219. package/src/examples/list-item-accessories.tsx +106 -0
  220. package/src/examples/list-item-accessories.vitest.tsx +115 -0
  221. package/src/examples/list-no-actions.tsx +18 -0
  222. package/src/examples/list-no-actions.vitest.tsx +97 -0
  223. package/src/examples/list-spacing-mode.vitest.tsx +6 -6
  224. package/src/examples/list-with-detail.vitest.tsx +92 -92
  225. package/src/examples/list-with-dropdown.vitest.tsx +49 -6
  226. package/src/examples/list-with-sections.vitest.tsx +61 -56
  227. package/src/examples/simple-detail-markdown.vitest.tsx +21 -17
  228. package/src/examples/simple-detail-table.tsx +65 -0
  229. package/src/examples/simple-detail-table.vitest.tsx +200 -0
  230. package/src/examples/simple-graph.tsx +51 -0
  231. package/src/examples/simple-graph.vitest.tsx +124 -0
  232. package/src/examples/simple-grid.vitest.tsx +3 -3
  233. package/src/examples/simple-list-search.vitest.tsx +65 -0
  234. package/src/examples/simple-navigation.vitest.tsx +3 -3
  235. package/src/examples/simple-table-wrap.tsx +55 -0
  236. package/src/examples/simple-table-wrap.vitest.tsx +91 -0
  237. package/src/examples/store.vitest.tsx +1 -1
  238. package/src/examples/table-edge-cases.tsx +72 -0
  239. package/src/examples/table-edge-cases.vitest.tsx +307 -0
  240. package/src/examples/table-flex-grow.tsx +53 -0
  241. package/src/examples/table-flex-grow.vitest.tsx +124 -0
  242. package/src/extensions/dev.tsx +7 -1
  243. package/src/globals.ts +3 -0
  244. package/src/index.tsx +31 -0
  245. package/src/internal/date-picker-widget.tsx +4 -0
  246. package/src/internal/providers.tsx +1 -4
  247. package/src/markdown-utils.tsx +82 -1
  248. package/src/opentui.tsx +5 -0
  249. package/src/release.tsx +3 -0
  250. package/src/state.tsx +2 -1
  251. package/src/theme.tsx +14 -0
  252. package/src/themes/nerv.json +231 -0
  253. package/src/themes/termcast.json +75 -71
  254. package/src/themes.ts +8 -5
  255. package/src/utils.tsx +4 -0
@@ -0,0 +1,200 @@
1
+ // E2E tests for Detail view with markdown tables.
2
+ // Verifies our custom TableRenderable renders borderless tables
3
+ // with header background and alternating row stripes.
4
+ // Also tests two tables side by side in a Row component.
5
+
6
+ import { test, expect, afterEach, beforeEach } from 'vitest'
7
+ import { launchTerminal, Session } from 'tuistory/src'
8
+
9
+ let session: Session
10
+
11
+ beforeEach(async () => {
12
+ const dbSuffix = `simple-detail-table-${process.pid}-${Date.now()}`
13
+ session = await launchTerminal({
14
+ command: 'bun',
15
+ args: ['src/examples/simple-detail-table.tsx'],
16
+ cols: 80,
17
+ rows: 50,
18
+ env: {
19
+ TERMCAST_DB_SUFFIX: dbSuffix,
20
+ },
21
+ })
22
+ })
23
+
24
+ afterEach(() => {
25
+ session?.close()
26
+ })
27
+
28
+ test('markdown tables render with borderless layout', async () => {
29
+ const text = await session.text({
30
+ waitFor: (text) => {
31
+ return text.includes('Server Status') && text.includes('API Gateway') && text.includes('max_connections')
32
+ },
33
+ timeout: 10000,
34
+ })
35
+
36
+ expect(text).toMatchInlineSnapshot(`
37
+ "
38
+
39
+
40
+
41
+
42
+ Server Status
43
+
44
+ Active Services
45
+
46
+ Service Status Uptime Memory
47
+ API Gateway Running 14d 3h 256MB
48
+ Auth Server Running 14d 3h 128MB
49
+ Worker Pool Running 7d 12h 512MB
50
+ Cache Layer Stopped - 0MB
51
+
52
+ Configuration
53
+
54
+ Key Value Description
55
+ max_connections 1000 Maximum concurrent connections
56
+ timeout_ms 5000 Request timeout in ms
57
+ retry_count 3 Number of retry attempts
58
+ log_level info Logging verbosity
59
+ region us-east-1 Deployment region
60
+
61
+ The system is operating normally.
62
+
63
+ Region Latency Endpoint RPS
64
+ us-east-1 12ms /api/auth 1200
65
+ eu-west-1 45ms /api/data 3400
66
+ ap-south-1 89ms /api/health 500
67
+
68
+
69
+ esc go back ^k actions powered by termcast.app
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+ "
88
+ `)
89
+
90
+ // Headers
91
+ expect(text).toContain('Server Status')
92
+ expect(text).toContain('Active Services')
93
+ expect(text).toContain('Configuration')
94
+ // Table header cells
95
+ expect(text).toContain('Service')
96
+ expect(text).toContain('Status')
97
+ expect(text).toContain('Uptime')
98
+ // Table data cells
99
+ expect(text).toContain('API Gateway')
100
+ expect(text).toContain('Running')
101
+ expect(text).toContain('Worker Pool')
102
+ expect(text).toContain('Cache Layer')
103
+ // Second table
104
+ expect(text).toContain('max_connections')
105
+ expect(text).toContain('timeout_ms')
106
+ expect(text).toContain('us-east-1')
107
+ // Prose below tables
108
+ expect(text).toContain('operating normally')
109
+ }, 30000)
110
+
111
+ test('table headers have distinct background color', async () => {
112
+ await session.text({
113
+ waitFor: (text) => text.includes('Service') && text.includes('API Gateway'),
114
+ timeout: 10000,
115
+ })
116
+
117
+ // Header cells should use inverted heading colors (heading fg becomes bg)
118
+ // The default nerv theme heading fg is orange (#e89500), so header bg should be orange
119
+ const headerBgText = await session.text({
120
+ only: { background: '#e89500' },
121
+ timeout: 5000,
122
+ })
123
+
124
+ expect(headerBgText).toContain('Service')
125
+ expect(headerBgText).toContain('Status')
126
+ }, 30000)
127
+
128
+ test('two tables render side by side in a Row', async () => {
129
+ const text = await session.text({
130
+ waitFor: (text) => {
131
+ return text.includes('us-east-1') && text.includes('/api/auth')
132
+ },
133
+ timeout: 10000,
134
+ })
135
+
136
+ expect(text).toMatchInlineSnapshot(`
137
+ "
138
+
139
+
140
+
141
+
142
+ Server Status
143
+
144
+ Active Services
145
+
146
+ Service Status Uptime Memory
147
+ API Gateway Running 14d 3h 256MB
148
+ Auth Server Running 14d 3h 128MB
149
+ Worker Pool Running 7d 12h 512MB
150
+ Cache Layer Stopped - 0MB
151
+
152
+ Configuration
153
+
154
+ Key Value Description
155
+ max_connections 1000 Maximum concurrent connections
156
+ timeout_ms 5000 Request timeout in ms
157
+ retry_count 3 Number of retry attempts
158
+ log_level info Logging verbosity
159
+ region us-east-1 Deployment region
160
+
161
+ The system is operating normally.
162
+
163
+ Region Latency Endpoint RPS
164
+ us-east-1 12ms /api/auth 1200
165
+ eu-west-1 45ms /api/data 3400
166
+ ap-south-1 89ms /api/health 500
167
+
168
+
169
+ esc go back ^k actions powered by termcast.app
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+ "
188
+ `)
189
+
190
+ // Left table
191
+ expect(text).toContain('Region')
192
+ expect(text).toContain('Latency')
193
+ expect(text).toContain('us-east-1')
194
+ expect(text).toContain('12ms')
195
+ // Right table
196
+ expect(text).toContain('Endpoint')
197
+ expect(text).toContain('RPS')
198
+ expect(text).toContain('/api/auth')
199
+ expect(text).toContain('3400')
200
+ }, 30000)
@@ -0,0 +1,51 @@
1
+ // Example: Graph component showing stock price data in a Detail view.
2
+ // Demonstrates line chart with braille rendering, multiple series,
3
+ // Y-axis labels, and X-axis date labels.
4
+
5
+ import { Detail, Graph, Color } from 'termcast'
6
+ import { renderWithProviders } from '../utils'
7
+
8
+ // 30 days of AAPL stock price (simulated)
9
+ const prices = [
10
+ 150.2, 152.1, 148.7, 155.3, 162.8, 158.4, 156.1,
11
+ 160.5, 163.2, 167.8, 165.4, 170.1, 172.5, 169.3,
12
+ 174.8, 178.2, 175.6, 180.1, 183.4, 179.8, 185.2,
13
+ 188.6, 186.3, 190.1, 187.5, 192.8, 195.3, 193.7,
14
+ 198.1, 201.4,
15
+ ]
16
+
17
+ // Volume data (in hundreds, to keep scale reasonable)
18
+ const volume = [
19
+ 12, 8, 15, 22, 45, 18, 10,
20
+ 14, 16, 25, 12, 20, 18, 11,
21
+ 30, 35, 15, 28, 32, 14, 38,
22
+ 42, 20, 35, 18, 40, 45, 22,
23
+ 48, 55,
24
+ ]
25
+
26
+ const days = Array.from({ length: 30 }, (_, i) => {
27
+ return String(i + 1).padStart(2, '0')
28
+ })
29
+
30
+ // Show labels every 5 days
31
+ const xLabels = days.filter((_, i) => i % 5 === 0)
32
+
33
+ function SimpleGraph() {
34
+ return (
35
+ <Detail
36
+ markdown={`# AAPL Stock - 30 Day\n\nPrice range: $${Math.min(...prices).toFixed(2)} - $${Math.max(...prices).toFixed(2)}`}
37
+ metadata={
38
+ <Graph
39
+ height={15}
40
+ xLabels={xLabels}
41
+ yTicks={6}
42
+ yFormat={(v) => v.toFixed(1)}
43
+ >
44
+ <Graph.Line data={prices} color={Color.Orange} title="Price" />
45
+ </Graph>
46
+ }
47
+ />
48
+ )
49
+ }
50
+
51
+ renderWithProviders(<SimpleGraph />)
@@ -0,0 +1,124 @@
1
+ import { test, expect, afterEach, beforeEach } from 'vitest'
2
+ import { launchTerminal, Session } from 'tuistory/src'
3
+
4
+ let session: Session
5
+
6
+ beforeEach(async () => {
7
+ session = await launchTerminal({
8
+ command: 'bun',
9
+ args: ['src/examples/simple-graph.tsx'],
10
+ cols: 80,
11
+ rows: 30,
12
+ })
13
+ })
14
+
15
+ afterEach(() => {
16
+ session?.close()
17
+ })
18
+
19
+ test('graph renders with braille characters and axis labels', async () => {
20
+ const text = await session.text({
21
+ waitFor: (text) => {
22
+ // Wait for graph to render - should have braille chars and axis labels
23
+ return text.includes('AAPL') && text.includes('│')
24
+ },
25
+ timeout: 10000,
26
+ })
27
+
28
+ expect(text).toMatchInlineSnapshot(`
29
+ "
30
+
31
+
32
+
33
+
34
+ AAPL Stock - 30 Day
35
+
36
+ Price range: $148.70 - $201.40
37
+ 204.0│ ⢀
38
+ │ ⢀⣴⣾⣿
39
+ │ ⢀⣠⣴⣦⣄⣠⣿⣿⣿⣿
40
+ 192.4│ ⣠⣀ ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
41
+ │ ⣠⣴⣿⣶⣤⣾⣿⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
42
+ │ ⢀⣠⣴⣄ ⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
43
+ 180.8│ ⣠⣀ ⢀⣴⣿⣿⣿⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
44
+ │ ⢀⣴⣿⣿⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
45
+ 169.3│ ⣠⣴⣾⣶⣤⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
46
+ │ ⢀⣴⣦⣄⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
47
+ │ ⢀⣄ ⣀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
48
+ 157.7│ ⢀⣾⣿⣿⣦⣀ ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
49
+ │ ⢀⣾⣿⣿⣿⣿⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
50
+ │⣀⣤⣶⣤⡀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
51
+ 146.1│⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
52
+ 01 06 11 16 21 26
53
+
54
+
55
+ esc go back ^k actions powered by termcast.app
56
+
57
+
58
+
59
+ "
60
+ `)
61
+
62
+ // Should contain the markdown title
63
+ expect(text).toContain('AAPL')
64
+ // Should contain Y-axis separator
65
+ expect(text).toContain('│')
66
+ // Should contain braille characters (U+2800 block)
67
+ expect(text).toMatch(/[\u2800-\u28FF]/)
68
+ // Should contain X-axis labels
69
+ expect(text).toContain('01')
70
+ expect(text).toContain('26')
71
+ // Should contain Y-axis values
72
+ expect(text).toContain('204.0')
73
+ expect(text).toContain('146.1')
74
+ }, 30000)
75
+
76
+ test('graph renders at different terminal sizes', async () => {
77
+ // Close existing session and start a smaller one
78
+ session?.close()
79
+ session = await launchTerminal({
80
+ command: 'bun',
81
+ args: ['src/examples/simple-graph.tsx'],
82
+ cols: 50,
83
+ rows: 24,
84
+ })
85
+
86
+ const text = await session.text({
87
+ waitFor: (text) => {
88
+ return text.includes('AAPL') && text.includes('│')
89
+ },
90
+ timeout: 10000,
91
+ })
92
+
93
+ expect(text).toMatchInlineSnapshot(`
94
+ "
95
+
96
+
97
+
98
+
99
+ AAPL Stock - 30 Day █
100
+
101
+ Price range: $148.70 - $201.40
102
+ 204.0│ ⢀
103
+ │ ⣠⣿
104
+ │ ⢀⣤⣦⣰⣿⣿
105
+ 192.4│ ⢀⣄ ⣾⣿⣿⣿⣿⣿
106
+ │ ⢀⣾⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿
107
+ │ ⢠⣦ ⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
108
+ 180.8│ ⣠⡀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
109
+ │ ⣼⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
110
+ 169.3│ ⢀⣾⣦⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
111
+ │ ⢀⣦⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
112
+ │ ⡄ ⣀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
113
+ 157.7│ ⣼⣿⣄ ⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
114
+
115
+
116
+ esc go back ^k actions
117
+
118
+ "
119
+ `)
120
+
121
+ // Graph should still render with braille
122
+ expect(text).toMatch(/[\u2800-\u28FF]/)
123
+ expect(text).toContain('│')
124
+ }, 30000)
@@ -148,7 +148,7 @@ test('grid navigation and display', async () => {
148
148
  │ │
149
149
  │ Settings │
150
150
  │ Change Theme... │
151
- See Console Logs
151
+ Toggle Console Logs
152
152
  │ │
153
153
  │ │
154
154
  │ │
@@ -533,7 +533,7 @@ test('grid mouse interaction', async () => {
533
533
  │ │
534
534
  │ Settings │
535
535
  │ Change Theme... │
536
- See Console Logs
536
+ Toggle Console Logs
537
537
  │ │
538
538
  │ │
539
539
  │ │
@@ -584,7 +584,7 @@ test('grid mouse interaction', async () => {
584
584
  │ │
585
585
  │ Settings │
586
586
  │ Change Theme... │
587
- See Console Logs
587
+ Toggle Console Logs
588
588
  │ │
589
589
  │ │
590
590
  │ │
@@ -0,0 +1,65 @@
1
+ // E2E test: search text persists across push/pop navigation.
2
+ // When user types a search query, pushes a detail view (Enter), then pops back (Esc),
3
+ // the search text and filtered results should be restored.
4
+ import { test, expect, afterEach, beforeEach } from 'vitest'
5
+ import { launchTerminal, Session } from 'tuistory/src'
6
+
7
+ let session: Session
8
+
9
+ beforeEach(async () => {
10
+ session = await launchTerminal({
11
+ command: 'bun',
12
+ args: ['src/examples/simple-list-search.tsx'],
13
+ cols: 70,
14
+ rows: 20,
15
+ })
16
+ })
17
+
18
+ afterEach(() => {
19
+ session?.close()
20
+ })
21
+
22
+ test('search text persists after push and pop', async () => {
23
+ // Wait for initial render
24
+ await session.text({
25
+ waitFor: (text) => {
26
+ return /search items/i.test(text) && text.includes('First Item')
27
+ },
28
+ })
29
+
30
+ // Type "apple" to filter to the Apple item
31
+ await session.type('apple')
32
+
33
+ const afterSearch = await session.text({
34
+ waitFor: (text) => {
35
+ return text.includes('apple') && text.includes('Apple')
36
+ },
37
+ })
38
+ expect(afterSearch).toContain('apple')
39
+ expect(afterSearch).toContain('›Apple')
40
+
41
+ // Press Enter to push the Detail view (Apple has a "View Details" action)
42
+ await session.press('enter')
43
+
44
+ const detailView = await session.text({
45
+ waitFor: (text) => {
46
+ return text.includes('First Item') || text.includes('Apple')
47
+ },
48
+ })
49
+ // Should be in the detail view now
50
+ expect(detailView).toContain('go back')
51
+
52
+ // Press Esc to pop back to the list
53
+ await session.press('escape')
54
+
55
+ const afterPop = await session.text({
56
+ waitFor: (text) => {
57
+ return text.includes('apple')
58
+ },
59
+ })
60
+
61
+ // Search text "apple" should be restored
62
+ expect(afterPop).toContain('apple')
63
+ // The filtered result should still show Apple
64
+ expect(afterPop).toContain('Apple')
65
+ }, 15000)
@@ -306,7 +306,7 @@ test('navigation with actions panel', async () => {
306
306
  │ │
307
307
  │ Settings │
308
308
  │ Change Theme... │
309
- See Console Logs
309
+ Toggle Console Logs
310
310
  │ │
311
311
  │ │
312
312
  │ │
@@ -339,7 +339,7 @@ test('navigation with actions panel', async () => {
339
339
  │ │
340
340
  │ Settings │
341
341
  │ Change Theme... │
342
- See Console Logs
342
+ Toggle Console Logs
343
343
  │ │
344
344
  │ │
345
345
  │ │
@@ -450,7 +450,7 @@ test('navigation with actions panel', async () => {
450
450
  │ │
451
451
  │ Settings │
452
452
  │ Change Theme... │
453
- See Console Logs
453
+ Toggle Console Logs
454
454
  │ │
455
455
  │ │
456
456
  │ │
@@ -0,0 +1,55 @@
1
+ // Example: Table component with wrapText enabled for long prose content.
2
+ // Demonstrates that cells can wrap to multiple lines when wrapText is true,
3
+ // versus the default single-line truncation.
4
+
5
+ import { renderWithProviders } from '../utils'
6
+ import { Table } from 'termcast/src/components/table'
7
+
8
+ function SimpleTableWrap() {
9
+ return (
10
+ <box flexDirection="column" paddingTop={1} paddingLeft={2} paddingRight={2}>
11
+ <text>Wrapping Table</text>
12
+ <box height={1} />
13
+ <Table
14
+ wrapText
15
+ headers={['Feature', 'Description']}
16
+ rows={[
17
+ [
18
+ 'OAuth Proxy',
19
+ 'Handles the full OAuth authorization code flow including PKCE verification, token exchange, and refresh. The proxy keeps client secrets server-side so CLI tools never need to store them locally.',
20
+ ],
21
+ [
22
+ 'Hot Reload',
23
+ 'Watches source files for changes and rebuilds the extension bundle automatically. The TUI updates in place without losing navigation state or scroll position.',
24
+ ],
25
+ [
26
+ 'Compiled Mode',
27
+ 'Extensions can be compiled into standalone binaries that embed the package.json and all command components. No filesystem paths are hardcoded, making the binary fully portable.',
28
+ ],
29
+ ]}
30
+ />
31
+ <box height={1} />
32
+ <text>Non-wrapping Table (default)</text>
33
+ <box height={1} />
34
+ <Table
35
+ headers={['Feature', 'Description']}
36
+ rows={[
37
+ [
38
+ 'OAuth Proxy',
39
+ 'Handles the full OAuth authorization code flow including PKCE verification, token exchange, and refresh. The proxy keeps client secrets server-side so CLI tools never need to store them locally.',
40
+ ],
41
+ [
42
+ 'Hot Reload',
43
+ 'Watches source files for changes and rebuilds the extension bundle automatically. The TUI updates in place without losing navigation state or scroll position.',
44
+ ],
45
+ [
46
+ 'Compiled Mode',
47
+ 'Extensions can be compiled into standalone binaries that embed the package.json and all command components. No filesystem paths are hardcoded, making the binary fully portable.',
48
+ ],
49
+ ]}
50
+ />
51
+ </box>
52
+ )
53
+ }
54
+
55
+ renderWithProviders(<SimpleTableWrap />)
@@ -0,0 +1,91 @@
1
+ // E2E tests for Table component with wrapText.
2
+ // Verifies that long cell text wraps to multiple lines when wrapText is true
3
+ // and truncates to single lines when false (default).
4
+
5
+ import { test, expect, afterEach, beforeEach } from 'vitest'
6
+ import { launchTerminal, Session } from 'tuistory/src'
7
+
8
+ let session: Session
9
+
10
+ beforeEach(async () => {
11
+ session = await launchTerminal({
12
+ command: 'bun',
13
+ args: ['src/examples/simple-table-wrap.tsx'],
14
+ cols: 80,
15
+ rows: 50,
16
+ })
17
+ })
18
+
19
+ afterEach(() => {
20
+ session?.close()
21
+ })
22
+
23
+ test('wrapText table shows full prose across multiple lines', async () => {
24
+ const text = await session.text({
25
+ waitFor: (text) => {
26
+ return text.includes('Wrapping Table') && text.includes('OAuth Proxy') && text.includes('Non-wrapping')
27
+ },
28
+ timeout: 10000,
29
+ })
30
+
31
+ expect(text).toMatchInlineSnapshot(`
32
+ "
33
+
34
+
35
+
36
+ Wrapping Table
37
+
38
+ Feature Description
39
+ OAuth Proxy Handles the full OAuth
40
+ authorization code flow including
41
+ PKCE verification, token exchange,
42
+ and refresh. The proxy keeps client
43
+ secrets server-side so CLI tools
44
+ never need to store them locally.
45
+
46
+ Hot Reload Watches source files for changes
47
+ and rebuilds the extension bundle
48
+ automatically. The TUI updates in
49
+ place without losing navigation
50
+ state or scroll position.
51
+ Compiled Mode Extensions can be compiled into
52
+ standalone binaries that embed the
53
+ package.json and all command
54
+ components. No filesystem paths are
55
+ hardcoded, making the binary fully
56
+ portable.
57
+
58
+ Non-wrapping Table (default)
59
+
60
+ Feature Description
61
+ OAuth Proxy Handles the full OAuth authorization code flow including
62
+ Hot Reload Watches source files for changes and rebuilds the extension
63
+ Compiled ModeExtensions can be compiled into standalone binaries that
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+ "
83
+ `)
84
+
85
+ // Wrapping table should show the full long text
86
+ expect(text).toContain('OAuth Proxy')
87
+ expect(text).toContain('PKCE verification')
88
+ expect(text).toContain('Hot Reload')
89
+ expect(text).toContain('Compiled Mode')
90
+ expect(text).toContain('portable')
91
+ }, 30000)
@@ -58,7 +58,7 @@ test('Store extension - searching for spiceblow shows Database', async () => {
58
58
 
59
59
 
60
60
 
61
- ↵ view details ↑↓ navigate ^k actions powered by termcast
61
+ ↵ view details ↑↓ navigate ^k actions powered by termcast.app
62
62
 
63
63
 
64
64