termcast 1.3.48 → 1.3.50

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 +13 -5
  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,36 @@
1
+ // Example: Graph with multiple series - stock price and moving average.
2
+ // Demonstrates multi-series rendering with different colors.
3
+
4
+ import { Detail, Graph, Color } from 'termcast'
5
+ import { renderWithProviders } from '../utils'
6
+
7
+ // 20 days of price data
8
+ const prices = [
9
+ 150, 155, 148, 162, 158, 165, 170, 168, 175, 180,
10
+ 178, 185, 190, 188, 195, 192, 198, 202, 200, 208,
11
+ ]
12
+
13
+ // Simple moving average (5-day window)
14
+ const sma: number[] = prices.map((_, i) => {
15
+ if (i < 4) return prices[i]!
16
+ const window = prices.slice(i - 4, i + 1)
17
+ return window.reduce((sum, v) => sum + v, 0) / window.length
18
+ })
19
+
20
+ const xLabels = ['Day 1', 'Day 5', 'Day 10', 'Day 15', 'Day 20']
21
+
22
+ function GraphMultiSeries() {
23
+ return (
24
+ <Detail
25
+ markdown="# Multi-Series Chart"
26
+ metadata={
27
+ <Graph height={12} xLabels={xLabels} yTicks={5}>
28
+ <Graph.Line data={prices} color={Color.Orange} title="Price" />
29
+ <Graph.Line data={sma} color={Color.Blue} title="SMA(5)" />
30
+ </Graph>
31
+ }
32
+ />
33
+ )
34
+ }
35
+
36
+ renderWithProviders(<GraphMultiSeries />)
@@ -0,0 +1,89 @@
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/graph-multi-series.tsx'],
10
+ cols: 70,
11
+ rows: 24,
12
+ })
13
+ })
14
+
15
+ afterEach(() => {
16
+ session?.close()
17
+ })
18
+
19
+ test('multi-series graph renders both lines', async () => {
20
+ const text = await session.text({
21
+ waitFor: (text) => {
22
+ return text.includes('Multi-Series') && text.includes('│')
23
+ },
24
+ timeout: 10000,
25
+ })
26
+
27
+ expect(text).toMatchInlineSnapshot(`
28
+ "
29
+
30
+
31
+
32
+
33
+ Multi-Series Chart
34
+ 211.0│ ⣠
35
+ │ ⢀⣠⣄⣀ ⣠⣾⣿
36
+ │ ⢀⡀ ⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿
37
+ 194.5│ ⢀⣀⡀ ⣠⣴⣿⣿⣷⣦⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
38
+ │ ⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
39
+ │ ⣀⣤⣀ ⣠⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
40
+ 178.0│ ⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
41
+ │ ⢀⣤⣶⣤⣄⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
42
+ 161.5│ ⣀ ⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
43
+ │ ⣼⣿⣿⣶⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
44
+ │⣀⣤⣶⣷⣤⡀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
45
+ 145.0│⣿⣿⣿⣿⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
46
+ Day 1 Day 5 Day 10 Day 15 Day 20
47
+
48
+
49
+ esc go back ^k actions
50
+
51
+
52
+ "
53
+ `)
54
+
55
+ // Should contain braille characters
56
+ expect(text).toMatch(/[\u2800-\u28FF]/)
57
+ // Y-axis separator
58
+ expect(text).toContain('│')
59
+ // Title
60
+ expect(text).toContain('Multi-Series')
61
+ // X-axis labels
62
+ expect(text).toContain('Day 1')
63
+ expect(text).toContain('Day 20')
64
+ }, 30000)
65
+
66
+ test('multi-series uses different colors per series', async () => {
67
+ const text = await session.text({
68
+ waitFor: (text) => {
69
+ return text.includes('Multi-Series') && text.includes('│')
70
+ },
71
+ timeout: 10000,
72
+ })
73
+
74
+ // Check that orange-colored braille chars exist (price series uses Color.Orange = #FF9F43)
75
+ const orangeText = await session.text({
76
+ only: { foreground: '#ff9f43' },
77
+ timeout: 5000,
78
+ })
79
+
80
+ // Check that blue-colored braille chars exist (SMA series uses Color.Blue = #5CB8FF)
81
+ const blueText = await session.text({
82
+ only: { foreground: '#5cb8ff' },
83
+ timeout: 5000,
84
+ })
85
+
86
+ // Both series should produce braille output
87
+ expect(orangeText).toMatch(/[\u2800-\u28FF]/)
88
+ expect(blueText).toMatch(/[\u2800-\u28FF]/)
89
+ }, 30000)
@@ -0,0 +1,182 @@
1
+ // Example: Polymarket-style prediction market UI.
2
+ // List of markets with odds graphs in the side detail panel.
3
+ // Each market shows a probability line chart over time.
4
+
5
+ import React from 'react'
6
+ import { Action, ActionPanel, List, Color, Graph } from 'termcast'
7
+ import { renderWithProviders } from '../utils'
8
+
9
+ interface Market {
10
+ id: string
11
+ title: string
12
+ category: string
13
+ currentOdds: number // 0-100 percentage for "Yes"
14
+ volume: string
15
+ endDate: string
16
+ // 30 data points of odds history (0-100)
17
+ oddsHistory: number[]
18
+ }
19
+
20
+ // Generate slightly noisy trend data
21
+ function generateOdds({ start, end, volatility = 5 }: { start: number; end: number; volatility?: number }): number[] {
22
+ const points = 30
23
+ const result: number[] = []
24
+ let current = start
25
+ const step = (end - start) / (points - 1)
26
+ for (let i = 0; i < points; i++) {
27
+ const noise = (Math.sin(i * 1.7) + Math.cos(i * 0.8)) * volatility
28
+ current = start + step * i + noise
29
+ result.push(Math.max(1, Math.min(99, Math.round(current * 10) / 10)))
30
+ }
31
+ // Ensure last point matches target
32
+ result[points - 1] = end
33
+ return result
34
+ }
35
+
36
+ const markets: Market[] = [
37
+ {
38
+ id: 'btc-100k',
39
+ title: 'Bitcoin above $100k by EOY',
40
+ category: 'Crypto',
41
+ currentOdds: 72,
42
+ volume: '$4.2M',
43
+ endDate: 'Dec 31, 2026',
44
+ oddsHistory: generateOdds({ start: 45, end: 72, volatility: 8 }),
45
+ },
46
+ {
47
+ id: 'eth-merge',
48
+ title: 'ETH above $5k by Q2 2026',
49
+ category: 'Crypto',
50
+ currentOdds: 38,
51
+ volume: '$1.8M',
52
+ endDate: 'Jun 30, 2026',
53
+ oddsHistory: generateOdds({ start: 55, end: 38, volatility: 6 }),
54
+ },
55
+ {
56
+ id: 'fed-rate',
57
+ title: 'Fed cuts rates below 4%',
58
+ category: 'Economics',
59
+ currentOdds: 61,
60
+ volume: '$8.1M',
61
+ endDate: 'Dec 31, 2026',
62
+ oddsHistory: generateOdds({ start: 40, end: 61, volatility: 4 }),
63
+ },
64
+ {
65
+ id: 'sp500-6k',
66
+ title: 'S&P 500 reaches 6,000',
67
+ category: 'Markets',
68
+ currentOdds: 55,
69
+ volume: '$3.5M',
70
+ endDate: 'Dec 31, 2026',
71
+ oddsHistory: generateOdds({ start: 35, end: 55, volatility: 7 }),
72
+ },
73
+ {
74
+ id: 'trump-pardon',
75
+ title: 'Presidential pardon for SBF',
76
+ category: 'Politics',
77
+ currentOdds: 8,
78
+ volume: '$920K',
79
+ endDate: 'Jan 20, 2027',
80
+ oddsHistory: generateOdds({ start: 15, end: 8, volatility: 3 }),
81
+ },
82
+ {
83
+ id: 'ai-agi',
84
+ title: 'AGI announced by major lab',
85
+ category: 'Tech',
86
+ currentOdds: 12,
87
+ volume: '$2.1M',
88
+ endDate: 'Dec 31, 2026',
89
+ oddsHistory: generateOdds({ start: 5, end: 12, volatility: 3 }),
90
+ },
91
+ {
92
+ id: 'sol-flip-eth',
93
+ title: 'Solana flips Ethereum market cap',
94
+ category: 'Crypto',
95
+ currentOdds: 18,
96
+ volume: '$1.4M',
97
+ endDate: 'Dec 31, 2026',
98
+ oddsHistory: generateOdds({ start: 8, end: 18, volatility: 5 }),
99
+ },
100
+ {
101
+ id: 'recession-2026',
102
+ title: 'US enters recession in 2026',
103
+ category: 'Economics',
104
+ currentOdds: 28,
105
+ volume: '$5.7M',
106
+ endDate: 'Dec 31, 2026',
107
+ oddsHistory: generateOdds({ start: 20, end: 28, volatility: 4 }),
108
+ },
109
+ ]
110
+
111
+ const xLabels = ['30d', '20d', '10d', 'Now']
112
+
113
+ function oddsColor(odds: number): Color.ColorLike {
114
+ if (odds >= 65) return Color.Green
115
+ if (odds >= 40) return Color.Yellow
116
+ return Color.Red
117
+ }
118
+
119
+ function PolymarketExample() {
120
+ return (
121
+ <List
122
+ navigationTitle="Polymarket"
123
+ searchBarPlaceholder="Search markets..."
124
+ isShowingDetail={true}
125
+ >
126
+ <List.Section title="Trending Markets">
127
+ {markets.map((market) => {
128
+ return (
129
+ <List.Item
130
+ key={market.id}
131
+ id={market.id}
132
+ title={market.title}
133
+ subtitle={market.category}
134
+ accessories={[
135
+ { text: { value: `${market.currentOdds}%`, color: oddsColor(market.currentOdds) } },
136
+ ]}
137
+ detail={
138
+ <List.Item.Detail
139
+ metadata={
140
+ <List.Item.Detail.Metadata>
141
+ <Graph
142
+ height={8}
143
+ xLabels={xLabels}
144
+ yRange={[0, 100]}
145
+ yTicks={3}
146
+ variant="area"
147
+ yFormat={(v) => `${v.toFixed(0)}%`}
148
+ >
149
+ <Graph.Line
150
+ data={market.oddsHistory}
151
+ color={oddsColor(market.currentOdds)}
152
+ title="Yes"
153
+ />
154
+ </Graph>
155
+ <List.Item.Detail.Metadata.Label
156
+ title="Odds"
157
+ text={{ value: `${market.currentOdds}% Yes`, color: oddsColor(market.currentOdds) }}
158
+ />
159
+ <List.Item.Detail.Metadata.Label title="Volume" text={market.volume} />
160
+ <List.Item.Detail.Metadata.Label title="Ends" text={market.endDate} />
161
+ <List.Item.Detail.Metadata.Separator />
162
+ <List.Item.Detail.Metadata.Label title="Probability (30d)" />
163
+
164
+ </List.Item.Detail.Metadata>
165
+ }
166
+ />
167
+ }
168
+ actions={
169
+ <ActionPanel>
170
+ <Action title="Buy Yes" onAction={() => {}} />
171
+ <Action title="Buy No" onAction={() => {}} />
172
+ </ActionPanel>
173
+ }
174
+ />
175
+ )
176
+ })}
177
+ </List.Section>
178
+ </List>
179
+ )
180
+ }
181
+
182
+ renderWithProviders(<PolymarketExample />)
@@ -0,0 +1,130 @@
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/graph-polymarket.tsx'],
10
+ cols: 100,
11
+ rows: 30,
12
+ })
13
+ })
14
+
15
+ afterEach(() => {
16
+ session?.close()
17
+ })
18
+
19
+ test('polymarket list with graph detail renders correctly', async () => {
20
+ const text = await session.text({
21
+ waitFor: (text) => {
22
+ return text.includes('Polymarket') && text.includes('│') && text.includes('Bitcoin')
23
+ },
24
+ timeout: 10000,
25
+ })
26
+
27
+ expect(text).toMatchInlineSnapshot(`
28
+ "
29
+
30
+
31
+ Polymarket ───────────────────────────────────────────────────────────────────────────────────
32
+
33
+ > Search markets...
34
+
35
+ Trending Markets │ 100%│
36
+ ›Bitcoin above $100k by EOY Crypto │ │ ⣠⣀
37
+ ETH above $5k by Q2 2026 Crypto │ │ ⢠⣀ ⢀⣴⣷⡀ ⣰⣿⣿⣷⣀⣤⣴⣦⢀⣴
38
+ Fed cuts rates below 4% Economics │ │⣠⣴⡀ ⢀ ⢠⣿⣿⣷⡀ ⣀⣤⣀⣀⣾⣿⣿⣿⣶⣴⣿⣿⣶⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
39
+ S&P 500 reaches 6,000 Markets │ 50%│⣿⣿⣷⣄⢀⣴⣿⣿⣾⣿⣿⣿⣿⣷⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
40
+ Presidential pardon for SBF Politics │ │⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
41
+ AGI announced by major lab Tech │ │⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
42
+ Solana flips Ethereum market cap Crypto │ 0%│⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
43
+ US enters recession in 2026 Economics │ 30d 20d 10d Now
44
+
45
+ │ Odds: 72% Yes
46
+
47
+ │ Volume: $4.2M
48
+
49
+ │ Ends: Dec 31, 2026
50
+
51
+ │ ────────────────────────────────────────────
52
+
53
+ ↵ buy yes ↑↓ navigate ^k actions │ Probability (30d)
54
+
55
+
56
+
57
+
58
+ "
59
+ `)
60
+
61
+ // Market list visible
62
+ expect(text).toContain('Bitcoin')
63
+ expect(text).toContain('Polymarket')
64
+ // Detail panel with graph
65
+ expect(text).toContain('│')
66
+ expect(text).toMatch(/[\u2800-\u28FF]/)
67
+ // Metadata labels
68
+ expect(text).toContain('Odds')
69
+ expect(text).toContain('Volume')
70
+ }, 30000)
71
+
72
+ test('navigating to different market updates the graph', async () => {
73
+ await session.text({
74
+ waitFor: (text) => {
75
+ return text.includes('Bitcoin') && text.includes('│')
76
+ },
77
+ timeout: 10000,
78
+ })
79
+
80
+ // Navigate down to second market (ETH)
81
+ await session.press('down')
82
+
83
+ const text = await session.text({
84
+ waitFor: (text) => {
85
+ return text.includes('›ETH')
86
+ },
87
+ timeout: 5000,
88
+ })
89
+
90
+ expect(text).toMatchInlineSnapshot(`
91
+ "
92
+
93
+
94
+ Polymarket ───────────────────────────────────────────────────────────────────────────────────
95
+
96
+ > Search markets...
97
+
98
+ Trending Markets │ 100%│
99
+ Bitcoin above $100k by EOY Crypto │ │
100
+ ›ETH above $5k by Q2 2026 Crypto │ │ ⣀
101
+ Fed cuts rates below 4% Economics │ │⣿⣿⣆ ⢀⡀ ⣀⣾⣶⣄ ⢀⣤⣦ ⢀⡀
102
+ S&P 500 reaches 6,000 Markets │ 50%│⣿⣿⣿⣷⣴⣾⣿⣿⣿⣿⣿⣿⣿⣦⣤⣶⣷⣦⣤⣿⣿⣿⣷⣤⣀⣤⣤⣀⢀⣴⣿⣿⣧ ⣀⣀⡀ ⢀
103
+ Presidential pardon for SBF Politics │ │⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣴⣿
104
+ AGI announced by major lab Tech │ │⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
105
+ Solana flips Ethereum market cap Crypto │ 0%│⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
106
+ US enters recession in 2026 Economics │ 30d 20d 10d Now
107
+
108
+ │ Odds: 38% Yes
109
+
110
+ │ Volume: $1.8M
111
+
112
+ │ Ends: Jun 30, 2026
113
+
114
+ │ ────────────────────────────────────────────
115
+
116
+ ↵ buy yes ↑↓ navigate ^k actions │ Probability (30d)
117
+
118
+
119
+
120
+
121
+ "
122
+ `)
123
+
124
+ // Second market selected
125
+ expect(text).toContain('›ETH')
126
+ // Detail should update
127
+ expect(text).toContain('Volume')
128
+ // Graph still renders
129
+ expect(text).toMatch(/[\u2800-\u28FF]/)
130
+ }, 30000)