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.
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +12 -0
- package/dist/build.js.map +1 -1
- package/dist/cli.js +5 -40
- package/dist/cli.js.map +1 -1
- package/dist/colors.d.ts +7 -7
- package/dist/colors.js +7 -7
- package/dist/compile.d.ts +6 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +45 -26
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.js +1 -1
- package/dist/components/actions.js.map +1 -1
- package/dist/components/bar-chart.d.ts +38 -0
- package/dist/components/bar-chart.d.ts.map +1 -0
- package/dist/components/bar-chart.js +158 -0
- package/dist/components/bar-chart.js.map +1 -0
- package/dist/components/bar-graph.d.ts +41 -0
- package/dist/components/bar-graph.d.ts.map +1 -0
- package/dist/components/bar-graph.js +95 -0
- package/dist/components/bar-graph.js.map +1 -0
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +5 -7
- package/dist/components/detail.js.map +1 -1
- package/dist/components/footer.d.ts.map +1 -1
- package/dist/components/footer.js +8 -9
- package/dist/components/footer.js.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +7 -1
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +10 -2
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -5
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/use-form-navigation.d.ts.map +1 -1
- package/dist/components/form/use-form-navigation.js +6 -0
- package/dist/components/form/use-form-navigation.js.map +1 -1
- package/dist/components/graph.d.ts +111 -0
- package/dist/components/graph.d.ts.map +1 -0
- package/dist/components/graph.js +392 -0
- package/dist/components/graph.js.map +1 -0
- package/dist/components/icon.js +5 -5
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +53 -5
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +125 -71
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.js +3 -3
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts +1 -1
- package/dist/components/loading-text.d.ts.map +1 -1
- package/dist/components/loading-text.js +3 -1
- package/dist/components/loading-text.js.map +1 -1
- package/dist/components/metadata.js +2 -2
- package/dist/components/metadata.js.map +1 -1
- package/dist/components/row.d.ts +10 -0
- package/dist/components/row.d.ts.map +1 -0
- package/dist/components/row.js +12 -0
- package/dist/components/row.js.map +1 -0
- package/dist/components/table.d.ts +57 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.js +365 -0
- package/dist/components/table.js.map +1 -0
- package/dist/descendants.js +13 -13
- package/dist/descendants.js.map +1 -1
- package/dist/examples/bar-graph-weekly.d.ts +2 -0
- package/dist/examples/bar-graph-weekly.d.ts.map +1 -0
- package/dist/examples/bar-graph-weekly.js +95 -0
- package/dist/examples/bar-graph-weekly.js.map +1 -0
- package/dist/examples/components-weird-places.d.ts +2 -0
- package/dist/examples/components-weird-places.d.ts.map +1 -0
- package/dist/examples/components-weird-places.js +46 -0
- package/dist/examples/components-weird-places.js.map +1 -0
- package/dist/examples/graph-bar-chart.d.ts +2 -0
- package/dist/examples/graph-bar-chart.d.ts.map +1 -0
- package/dist/examples/graph-bar-chart.js +270 -0
- package/dist/examples/graph-bar-chart.js.map +1 -0
- package/dist/examples/graph-multi-series.d.ts +2 -0
- package/dist/examples/graph-multi-series.d.ts.map +1 -0
- package/dist/examples/graph-multi-series.js +23 -0
- package/dist/examples/graph-multi-series.js.map +1 -0
- package/dist/examples/graph-polymarket.d.ts +2 -0
- package/dist/examples/graph-polymarket.d.ts.map +1 -0
- package/dist/examples/graph-polymarket.js +109 -0
- package/dist/examples/graph-polymarket.js.map +1 -0
- package/dist/examples/graph-row.d.ts +2 -0
- package/dist/examples/graph-row.d.ts.map +1 -0
- package/dist/examples/graph-row.js +226 -0
- package/dist/examples/graph-row.js.map +1 -0
- package/dist/examples/graph-styles.d.ts +2 -0
- package/dist/examples/graph-styles.d.ts.map +1 -0
- package/dist/examples/graph-styles.js +316 -0
- package/dist/examples/graph-styles.js.map +1 -0
- package/dist/examples/list-accessory-table.d.ts +2 -0
- package/dist/examples/list-accessory-table.d.ts.map +1 -0
- package/dist/examples/list-accessory-table.js +46 -0
- package/dist/examples/list-accessory-table.js.map +1 -0
- package/dist/examples/list-item-accessories.d.ts +2 -0
- package/dist/examples/list-item-accessories.d.ts.map +1 -0
- package/dist/examples/list-item-accessories.js +27 -0
- package/dist/examples/list-item-accessories.js.map +1 -0
- package/dist/examples/list-no-actions.d.ts +2 -0
- package/dist/examples/list-no-actions.d.ts.map +1 -0
- package/dist/examples/list-no-actions.js +7 -0
- package/dist/examples/list-no-actions.js.map +1 -0
- package/dist/examples/simple-detail-table.d.ts +2 -0
- package/dist/examples/simple-detail-table.d.ts.map +1 -0
- package/dist/examples/simple-detail-table.js +45 -0
- package/dist/examples/simple-detail-table.js.map +1 -0
- package/dist/examples/simple-graph.d.ts +2 -0
- package/dist/examples/simple-graph.d.ts.map +1 -0
- package/dist/examples/simple-graph.js +32 -0
- package/dist/examples/simple-graph.js.map +1 -0
- package/dist/examples/simple-table-wrap.d.ts +2 -0
- package/dist/examples/simple-table-wrap.d.ts.map +1 -0
- package/dist/examples/simple-table-wrap.js +37 -0
- package/dist/examples/simple-table-wrap.js.map +1 -0
- package/dist/examples/table-edge-cases.d.ts +2 -0
- package/dist/examples/table-edge-cases.d.ts.map +1 -0
- package/dist/examples/table-edge-cases.js +70 -0
- package/dist/examples/table-edge-cases.js.map +1 -0
- package/dist/examples/table-flex-grow.d.ts +2 -0
- package/dist/examples/table-flex-grow.d.ts.map +1 -0
- package/dist/examples/table-flex-grow.js +18 -0
- package/dist/examples/table-flex-grow.js.map +1 -0
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +5 -1
- package/dist/extensions/dev.js.map +1 -1
- package/dist/globals.d.ts +1 -0
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +2 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/date-picker-widget.d.ts.map +1 -1
- package/dist/internal/date-picker-widget.js +4 -0
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +1 -3
- package/dist/internal/providers.js.map +1 -1
- package/dist/markdown-utils.d.ts +22 -1
- package/dist/markdown-utils.d.ts.map +1 -1
- package/dist/markdown-utils.js +66 -1
- package/dist/markdown-utils.js.map +1 -1
- package/dist/opentui.d.ts +4 -0
- package/dist/opentui.d.ts.map +1 -0
- package/dist/opentui.js +3 -0
- package/dist/opentui.js.map +1 -0
- package/dist/release.d.ts +2 -1
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +2 -1
- package/dist/release.js.map +1 -1
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/theme.d.ts +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +13 -0
- package/dist/theme.js.map +1 -1
- package/dist/themes/nerv.json +227 -0
- package/dist/themes/termcast.json +72 -71
- package/dist/themes.d.ts +2 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +7 -5
- package/dist/themes.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/package.json +12 -4
- package/src/build.tsx +13 -0
- package/src/cli.tsx +5 -49
- package/src/colors.tsx +7 -7
- package/src/compile.tsx +52 -29
- package/src/components/actions.tsx +1 -1
- package/src/components/bar-chart.tsx +271 -0
- package/src/components/bar-graph.tsx +214 -0
- package/src/components/detail.tsx +7 -8
- package/src/components/footer.tsx +14 -15
- package/src/components/form/date-picker.tsx +9 -0
- package/src/components/form/dropdown.tsx +13 -3
- package/src/components/form/index.tsx +4 -6
- package/src/components/form/use-form-navigation.tsx +6 -0
- package/src/components/graph.tsx +506 -0
- package/src/components/icon.tsx +5 -5
- package/src/components/list.tsx +210 -102
- package/src/components/loading-bar.tsx +3 -3
- package/src/components/loading-text.tsx +4 -2
- package/src/components/metadata.tsx +2 -2
- package/src/components/row.tsx +31 -0
- package/src/components/table.tsx +511 -0
- package/src/descendants.tsx +13 -13
- package/src/examples/action-shortcut.vitest.tsx +1 -1
- package/src/examples/actions-context.vitest.tsx +1 -1
- package/src/examples/bar-graph-weekly.tsx +264 -0
- package/src/examples/bar-graph-weekly.vitest.tsx +275 -0
- package/src/examples/detail-metadata-showcase.vitest.tsx +8 -8
- package/src/examples/form-basic.vitest.tsx +239 -0
- package/src/examples/form-dropdown.vitest.tsx +29 -29
- package/src/examples/form-tagpicker.vitest.tsx +27 -27
- package/src/examples/github.vitest.tsx +4 -4
- package/src/examples/graph-bar-chart.tsx +408 -0
- package/src/examples/graph-bar-chart.vitest.tsx +283 -0
- package/src/examples/graph-multi-series.tsx +36 -0
- package/src/examples/graph-multi-series.vitest.tsx +89 -0
- package/src/examples/graph-polymarket.tsx +182 -0
- package/src/examples/graph-polymarket.vitest.tsx +130 -0
- package/src/examples/graph-row.tsx +347 -0
- package/src/examples/graph-row.vitest.tsx +295 -0
- package/src/examples/graph-styles.tsx +457 -0
- package/src/examples/graph-styles.vitest.tsx +322 -0
- package/src/examples/list-accessory-table.tsx +77 -0
- package/src/examples/list-detail-metadata.vitest.tsx +21 -21
- package/src/examples/list-dropdown-default.vitest.tsx +12 -12
- package/src/examples/list-item-accessories.tsx +106 -0
- package/src/examples/list-item-accessories.vitest.tsx +115 -0
- package/src/examples/list-no-actions.tsx +18 -0
- package/src/examples/list-no-actions.vitest.tsx +97 -0
- package/src/examples/list-spacing-mode.vitest.tsx +6 -6
- package/src/examples/list-with-detail.vitest.tsx +92 -92
- package/src/examples/list-with-dropdown.vitest.tsx +49 -6
- package/src/examples/list-with-sections.vitest.tsx +61 -56
- package/src/examples/simple-detail-markdown.vitest.tsx +21 -17
- package/src/examples/simple-detail-table.tsx +65 -0
- package/src/examples/simple-detail-table.vitest.tsx +200 -0
- package/src/examples/simple-graph.tsx +51 -0
- package/src/examples/simple-graph.vitest.tsx +124 -0
- package/src/examples/simple-grid.vitest.tsx +3 -3
- package/src/examples/simple-list-search.vitest.tsx +65 -0
- package/src/examples/simple-navigation.vitest.tsx +3 -3
- package/src/examples/simple-table-wrap.tsx +55 -0
- package/src/examples/simple-table-wrap.vitest.tsx +91 -0
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/table-edge-cases.tsx +72 -0
- package/src/examples/table-edge-cases.vitest.tsx +307 -0
- package/src/examples/table-flex-grow.tsx +53 -0
- package/src/examples/table-flex-grow.vitest.tsx +124 -0
- package/src/extensions/dev.tsx +7 -1
- package/src/globals.ts +3 -0
- package/src/index.tsx +31 -0
- package/src/internal/date-picker-widget.tsx +4 -0
- package/src/internal/providers.tsx +1 -4
- package/src/markdown-utils.tsx +82 -1
- package/src/opentui.tsx +5 -0
- package/src/release.tsx +3 -0
- package/src/state.tsx +2 -1
- package/src/theme.tsx +14 -0
- package/src/themes/nerv.json +231 -0
- package/src/themes/termcast.json +75 -71
- package/src/themes.ts +8 -5
- 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
|
-
│
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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)
|