termcast 1.3.47 → 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/apis/cache.d.ts.map +1 -1
- package/dist/apis/cache.js +1 -2
- package/dist/apis/cache.js.map +1 -1
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +1 -2
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/sqlite.d.ts +7 -0
- package/dist/apis/sqlite.d.ts.map +1 -0
- package/dist/apis/sqlite.js +13 -0
- package/dist/apis/sqlite.js.map +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +14 -5
- 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 +46 -27
- 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/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.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/swift-runtime.d.ts.map +1 -1
- package/dist/swift-runtime.js +20 -5
- package/dist/swift-runtime.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 +4 -1
- package/dist/utils.js.map +1 -1
- package/package.json +12 -4
- package/src/apis/cache.test.ts +1 -1
- package/src/apis/cache.tsx +1 -2
- package/src/apis/localstorage.tsx +1 -2
- package/src/apis/sqlite.ts +14 -0
- package/src/build.tsx +15 -5
- package/src/cli.tsx +5 -49
- package/src/colors.tsx +7 -7
- package/src/compile.tsx +53 -30
- 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 +22 -22
- 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 +73 -73
- 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 -18
- 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/logger.tsx +2 -1
- 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/swift-runtime.tsx +19 -5
- 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.test.tsx +1 -1
- package/src/utils.tsx +5 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Example: Comprehensive table edge cases for testing.
|
|
2
|
+
// Tests markdown tables with inline formatting, single column/row,
|
|
3
|
+
// empty cells, wide content, and various column counts.
|
|
4
|
+
|
|
5
|
+
import { Detail } from 'termcast'
|
|
6
|
+
import { renderWithProviders } from '../utils'
|
|
7
|
+
|
|
8
|
+
const markdown = `# Table Edge Cases
|
|
9
|
+
|
|
10
|
+
## Inline Formatting
|
|
11
|
+
|
|
12
|
+
| Feature | Syntax | Result |
|
|
13
|
+
|---------|--------|--------|
|
|
14
|
+
| Bold | **text** | bold text |
|
|
15
|
+
| Italic | *text* | italic text |
|
|
16
|
+
| Code | \`code\` | inline code |
|
|
17
|
+
| Link | [docs](https://example.com) | clickable link |
|
|
18
|
+
| Mixed | **bold** and *italic* | combined |
|
|
19
|
+
|
|
20
|
+
## Single Column
|
|
21
|
+
|
|
22
|
+
| Name |
|
|
23
|
+
|------|
|
|
24
|
+
| Alice |
|
|
25
|
+
| Bob |
|
|
26
|
+
| Charlie |
|
|
27
|
+
|
|
28
|
+
## Single Row
|
|
29
|
+
|
|
30
|
+
| A | B | C | D | E |
|
|
31
|
+
|---|---|---|---|---|
|
|
32
|
+
| 1 | 2 | 3 | 4 | 5 |
|
|
33
|
+
|
|
34
|
+
## Empty Cells
|
|
35
|
+
|
|
36
|
+
| Key | Value | Notes |
|
|
37
|
+
|-----|-------|-------|
|
|
38
|
+
| host | localhost | |
|
|
39
|
+
| | 8080 | default port |
|
|
40
|
+
| ssl | | not configured |
|
|
41
|
+
|
|
42
|
+
## Wide Table
|
|
43
|
+
|
|
44
|
+
| ID | Name | Email | Role | Department | Location |
|
|
45
|
+
|----|------|-------|------|------------|----------|
|
|
46
|
+
| 1 | Alice Johnson | alice@example.com | Engineer | Engineering | SF |
|
|
47
|
+
| 2 | Bob Smith | bob@example.com | Designer | Design | NYC |
|
|
48
|
+
|
|
49
|
+
## Two Columns
|
|
50
|
+
|
|
51
|
+
| Key | Value |
|
|
52
|
+
|-----|-------|
|
|
53
|
+
| version | 2.1.0 |
|
|
54
|
+
| license | MIT |
|
|
55
|
+
| author | termcast |
|
|
56
|
+
|
|
57
|
+
## Numeric Data
|
|
58
|
+
|
|
59
|
+
| Metric | Q1 | Q2 | Q3 | Q4 |
|
|
60
|
+
|--------|----|----|----|----|
|
|
61
|
+
| Revenue | 100 | 150 | 200 | 250 |
|
|
62
|
+
| Users | 1000 | 1500 | 2000 | 3000 |
|
|
63
|
+
| Churn | 5% | 4% | 3% | 2% |
|
|
64
|
+
|
|
65
|
+
Done.
|
|
66
|
+
`
|
|
67
|
+
|
|
68
|
+
function TableEdgeCases() {
|
|
69
|
+
return <Detail markdown={markdown} />
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
renderWithProviders(<TableEdgeCases />)
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// E2E tests for table edge cases: inline formatting, single column/row,
|
|
2
|
+
// empty cells, wide tables, various column counts, and numeric data.
|
|
3
|
+
|
|
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/table-edge-cases.tsx'],
|
|
13
|
+
cols: 80,
|
|
14
|
+
rows: 80,
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
session?.close()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('inline formatting table renders all rows', async () => {
|
|
23
|
+
const text = await session.text({
|
|
24
|
+
waitFor: (text) => {
|
|
25
|
+
return text.includes('Inline Formatting') && text.includes('bold text') && text.includes('combined')
|
|
26
|
+
},
|
|
27
|
+
timeout: 10000,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
expect(text).toMatchInlineSnapshot(`
|
|
31
|
+
"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Table Edge Cases
|
|
37
|
+
|
|
38
|
+
Inline Formatting
|
|
39
|
+
|
|
40
|
+
Feature Syntax Result
|
|
41
|
+
Bold text bold text
|
|
42
|
+
Italic text italic text
|
|
43
|
+
Code code inline code
|
|
44
|
+
Link docs clickable link
|
|
45
|
+
Mixed bold and italic combined
|
|
46
|
+
|
|
47
|
+
Single Column
|
|
48
|
+
|
|
49
|
+
Name
|
|
50
|
+
Alice
|
|
51
|
+
Bob
|
|
52
|
+
Charlie
|
|
53
|
+
|
|
54
|
+
Single Row
|
|
55
|
+
|
|
56
|
+
A B C D E
|
|
57
|
+
1 2 3 4 5
|
|
58
|
+
|
|
59
|
+
Empty Cells
|
|
60
|
+
|
|
61
|
+
Key Value Notes
|
|
62
|
+
host localhost
|
|
63
|
+
8080 default port
|
|
64
|
+
ssl not configured
|
|
65
|
+
|
|
66
|
+
Wide Table
|
|
67
|
+
|
|
68
|
+
ID Name Email Role Department Location
|
|
69
|
+
1 Alice Johnson alice@example.com Engineer Engineering SF
|
|
70
|
+
2 Bob Smith bob@example.com Designer Design NYC
|
|
71
|
+
|
|
72
|
+
Two Columns
|
|
73
|
+
|
|
74
|
+
Key Value
|
|
75
|
+
version 2.1.0
|
|
76
|
+
license MIT
|
|
77
|
+
author termcast
|
|
78
|
+
|
|
79
|
+
Numeric Data
|
|
80
|
+
|
|
81
|
+
Metric Q1 Q2 Q3 Q4
|
|
82
|
+
Revenue 100 150 200 250
|
|
83
|
+
Users 1000 1500 2000 3000
|
|
84
|
+
Churn 5% 4% 3% 2%
|
|
85
|
+
|
|
86
|
+
Done.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
esc go back ^k actions powered by termcast.app
|
|
110
|
+
|
|
111
|
+
"
|
|
112
|
+
`)
|
|
113
|
+
|
|
114
|
+
// All formatting rows present
|
|
115
|
+
expect(text).toContain('bold text')
|
|
116
|
+
expect(text).toContain('italic text')
|
|
117
|
+
expect(text).toContain('inline code')
|
|
118
|
+
expect(text).toContain('clickable link')
|
|
119
|
+
expect(text).toContain('combined')
|
|
120
|
+
// Headers
|
|
121
|
+
expect(text).toContain('Feature')
|
|
122
|
+
expect(text).toContain('Syntax')
|
|
123
|
+
expect(text).toContain('Result')
|
|
124
|
+
}, 30000)
|
|
125
|
+
|
|
126
|
+
test('single column table', async () => {
|
|
127
|
+
const text = await session.text({
|
|
128
|
+
waitFor: (text) => text.includes('Single Column') && text.includes('Charlie'),
|
|
129
|
+
timeout: 10000,
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
expect(text).toContain('Alice')
|
|
133
|
+
expect(text).toContain('Bob')
|
|
134
|
+
expect(text).toContain('Charlie')
|
|
135
|
+
}, 30000)
|
|
136
|
+
|
|
137
|
+
test('single row table', async () => {
|
|
138
|
+
const text = await session.text({
|
|
139
|
+
waitFor: (text) => text.includes('Single Row'),
|
|
140
|
+
timeout: 10000,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// 5 columns with single data row
|
|
144
|
+
expect(text).toContain('A')
|
|
145
|
+
expect(text).toContain('B')
|
|
146
|
+
expect(text).toContain('C')
|
|
147
|
+
expect(text).toContain('D')
|
|
148
|
+
expect(text).toContain('E')
|
|
149
|
+
}, 30000)
|
|
150
|
+
|
|
151
|
+
test('empty cells do not break layout', async () => {
|
|
152
|
+
const text = await session.text({
|
|
153
|
+
waitFor: (text) => text.includes('Empty Cells') && text.includes('not configured'),
|
|
154
|
+
timeout: 10000,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
expect(text).toContain('localhost')
|
|
158
|
+
expect(text).toContain('default port')
|
|
159
|
+
expect(text).toContain('not configured')
|
|
160
|
+
}, 30000)
|
|
161
|
+
|
|
162
|
+
test('wide table with 6 columns fits within terminal', async () => {
|
|
163
|
+
const text = await session.text({
|
|
164
|
+
waitFor: (text) => text.includes('Wide Table') && text.includes('Engineer'),
|
|
165
|
+
timeout: 10000,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// Headers
|
|
169
|
+
expect(text).toContain('ID')
|
|
170
|
+
expect(text).toContain('Name')
|
|
171
|
+
expect(text).toContain('Email')
|
|
172
|
+
expect(text).toContain('Role')
|
|
173
|
+
// Data - some fields may be truncated at 80 cols with 6 equal-width columns
|
|
174
|
+
expect(text).toContain('Alice Johnso')
|
|
175
|
+
expect(text).toContain('Bob Smith')
|
|
176
|
+
expect(text).toContain('Engineer')
|
|
177
|
+
expect(text).toContain('Designer')
|
|
178
|
+
}, 30000)
|
|
179
|
+
|
|
180
|
+
test('two column key-value table', async () => {
|
|
181
|
+
const text = await session.text({
|
|
182
|
+
waitFor: (text) => text.includes('Two Columns') && text.includes('2.1.0'),
|
|
183
|
+
timeout: 10000,
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
expect(text).toContain('version')
|
|
187
|
+
expect(text).toContain('2.1.0')
|
|
188
|
+
expect(text).toContain('license')
|
|
189
|
+
expect(text).toContain('MIT')
|
|
190
|
+
expect(text).toContain('author')
|
|
191
|
+
expect(text).toContain('termcast')
|
|
192
|
+
}, 30000)
|
|
193
|
+
|
|
194
|
+
test('numeric data table', async () => {
|
|
195
|
+
const text = await session.text({
|
|
196
|
+
waitFor: (text) => text.includes('Numeric Data') && text.includes('Revenue'),
|
|
197
|
+
timeout: 10000,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
expect(text).toContain('Revenue')
|
|
201
|
+
expect(text).toContain('Users')
|
|
202
|
+
expect(text).toContain('Churn')
|
|
203
|
+
expect(text).toContain('250')
|
|
204
|
+
expect(text).toContain('3000')
|
|
205
|
+
expect(text).toContain('2%')
|
|
206
|
+
}, 30000)
|
|
207
|
+
|
|
208
|
+
test('all tables render without crash - full page snapshot', async () => {
|
|
209
|
+
const text = await session.text({
|
|
210
|
+
waitFor: (text) => text.includes('Done.'),
|
|
211
|
+
timeout: 10000,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
expect(text).toMatchInlineSnapshot(`
|
|
215
|
+
"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
Table Edge Cases
|
|
221
|
+
|
|
222
|
+
Inline Formatting
|
|
223
|
+
|
|
224
|
+
Feature Syntax Result
|
|
225
|
+
Bold text bold text
|
|
226
|
+
Italic text italic text
|
|
227
|
+
Code code inline code
|
|
228
|
+
Link docs clickable link
|
|
229
|
+
Mixed bold and italic combined
|
|
230
|
+
|
|
231
|
+
Single Column
|
|
232
|
+
|
|
233
|
+
Name
|
|
234
|
+
Alice
|
|
235
|
+
Bob
|
|
236
|
+
Charlie
|
|
237
|
+
|
|
238
|
+
Single Row
|
|
239
|
+
|
|
240
|
+
A B C D E
|
|
241
|
+
1 2 3 4 5
|
|
242
|
+
|
|
243
|
+
Empty Cells
|
|
244
|
+
|
|
245
|
+
Key Value Notes
|
|
246
|
+
host localhost
|
|
247
|
+
8080 default port
|
|
248
|
+
ssl not configured
|
|
249
|
+
|
|
250
|
+
Wide Table
|
|
251
|
+
|
|
252
|
+
ID Name Email Role Department Location
|
|
253
|
+
1 Alice Johnson alice@example.com Engineer Engineering SF
|
|
254
|
+
2 Bob Smith bob@example.com Designer Design NYC
|
|
255
|
+
|
|
256
|
+
Two Columns
|
|
257
|
+
|
|
258
|
+
Key Value
|
|
259
|
+
version 2.1.0
|
|
260
|
+
license MIT
|
|
261
|
+
author termcast
|
|
262
|
+
|
|
263
|
+
Numeric Data
|
|
264
|
+
|
|
265
|
+
Metric Q1 Q2 Q3 Q4
|
|
266
|
+
Revenue 100 150 200 250
|
|
267
|
+
Users 1000 1500 2000 3000
|
|
268
|
+
Churn 5% 4% 3% 2%
|
|
269
|
+
|
|
270
|
+
Done.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
esc go back ^k actions powered by termcast.app
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
"
|
|
296
|
+
`)
|
|
297
|
+
|
|
298
|
+
// Verify all section headings present
|
|
299
|
+
expect(text).toContain('Inline Formatting')
|
|
300
|
+
expect(text).toContain('Single Column')
|
|
301
|
+
expect(text).toContain('Single Row')
|
|
302
|
+
expect(text).toContain('Empty Cells')
|
|
303
|
+
expect(text).toContain('Wide Table')
|
|
304
|
+
expect(text).toContain('Two Columns')
|
|
305
|
+
expect(text).toContain('Numeric Data')
|
|
306
|
+
expect(text).toContain('Done.')
|
|
307
|
+
}, 30000)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Example: Table with flexGrow to fill available width.
|
|
2
|
+
// Uses wrapText mode so columns distribute evenly across the full table width,
|
|
3
|
+
// making it visually obvious whether flexGrow stretches the table or not.
|
|
4
|
+
// The header background also reveals the actual table width.
|
|
5
|
+
|
|
6
|
+
import { renderWithProviders } from '../utils'
|
|
7
|
+
import { Table } from 'termcast/src/components/table'
|
|
8
|
+
|
|
9
|
+
function TableFlexGrow() {
|
|
10
|
+
const headers = ['Key', 'Value']
|
|
11
|
+
const rows = [
|
|
12
|
+
['version', '2.1.0'],
|
|
13
|
+
['license', 'MIT'],
|
|
14
|
+
['author', 'termcast'],
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<box flexDirection="column" paddingTop={1} paddingLeft={2} paddingRight={2}>
|
|
19
|
+
<text>With flexGrow=1 + wrapText (fills remaining space)</text>
|
|
20
|
+
<box height={1} />
|
|
21
|
+
<box flexDirection="row" width="100%">
|
|
22
|
+
<box width={12} flexShrink={0}>
|
|
23
|
+
<text>Config:</text>
|
|
24
|
+
</box>
|
|
25
|
+
<Table headers={headers} rows={rows} flexGrow={1} wrapText />
|
|
26
|
+
</box>
|
|
27
|
+
|
|
28
|
+
<box height={2} />
|
|
29
|
+
|
|
30
|
+
<text>Width=auto + wrapText (content-sized, no stretch)</text>
|
|
31
|
+
<box height={1} />
|
|
32
|
+
<box flexDirection="row" width="100%">
|
|
33
|
+
<box width={12} flexShrink={0}>
|
|
34
|
+
<text>Config:</text>
|
|
35
|
+
</box>
|
|
36
|
+
<Table headers={headers} rows={rows} width="auto" wrapText />
|
|
37
|
+
</box>
|
|
38
|
+
|
|
39
|
+
<box height={2} />
|
|
40
|
+
|
|
41
|
+
<text>With flexGrow=1 no wrapText (column-based)</text>
|
|
42
|
+
<box height={1} />
|
|
43
|
+
<box flexDirection="row" width="100%">
|
|
44
|
+
<box width={12} flexShrink={0}>
|
|
45
|
+
<text>Config:</text>
|
|
46
|
+
</box>
|
|
47
|
+
<Table headers={headers} rows={rows} flexGrow={1} />
|
|
48
|
+
</box>
|
|
49
|
+
</box>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
renderWithProviders(<TableFlexGrow />)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// E2E tests for Table with flexGrow prop.
|
|
2
|
+
// Uses wrapText mode to make the width difference visually obvious:
|
|
3
|
+
// in row-based layout, columns distribute evenly so a wider table
|
|
4
|
+
// means wider columns. Also checks header background extent.
|
|
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 = `table-flex-grow-${process.pid}-${Date.now()}`
|
|
13
|
+
session = await launchTerminal({
|
|
14
|
+
command: 'bun',
|
|
15
|
+
args: ['src/examples/table-flex-grow.tsx'],
|
|
16
|
+
cols: 80,
|
|
17
|
+
rows: 40,
|
|
18
|
+
env: {
|
|
19
|
+
TERMCAST_DB_SUFFIX: dbSuffix,
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
session?.close()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('flexGrow table fills remaining space next to fixed-width label', async () => {
|
|
29
|
+
const text = await session.text({
|
|
30
|
+
waitFor: (text) => {
|
|
31
|
+
return text.includes('flexGrow=1') && text.includes('version') && text.includes('Width=auto')
|
|
32
|
+
},
|
|
33
|
+
timeout: 10000,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
expect(text).toMatchInlineSnapshot(`
|
|
37
|
+
"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
With flexGrow=1 + wrapText (fills remaining space)
|
|
42
|
+
|
|
43
|
+
Config: Key Value
|
|
44
|
+
version 2.1.0
|
|
45
|
+
license MIT
|
|
46
|
+
author termcast
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
Width=auto + wrapText (content-sized, no stretch)
|
|
50
|
+
|
|
51
|
+
Config: KeVa
|
|
52
|
+
ve2.
|
|
53
|
+
liMI
|
|
54
|
+
aute
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
With flexGrow=1 no wrapText (column-based)
|
|
58
|
+
|
|
59
|
+
Config: Key Value
|
|
60
|
+
version 2.1.0
|
|
61
|
+
license MIT
|
|
62
|
+
author termcast
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
"
|
|
78
|
+
`)
|
|
79
|
+
|
|
80
|
+
// All three table sections render
|
|
81
|
+
expect(text).toContain('flexGrow=1')
|
|
82
|
+
expect(text).toContain('Width=auto')
|
|
83
|
+
// Table data is visible
|
|
84
|
+
expect(text).toContain('version')
|
|
85
|
+
expect(text).toContain('2.1.0')
|
|
86
|
+
expect(text).toContain('termcast')
|
|
87
|
+
// Fixed label is present
|
|
88
|
+
expect(text).toContain('Config:')
|
|
89
|
+
}, 30000)
|
|
90
|
+
|
|
91
|
+
test('flexGrow header background is wider than width=auto header', async () => {
|
|
92
|
+
await session.text({
|
|
93
|
+
waitFor: (text) => {
|
|
94
|
+
return text.includes('flexGrow=1') && text.includes('Width=auto')
|
|
95
|
+
},
|
|
96
|
+
timeout: 10000,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Header bg is orange (#e89500) from the default nerv theme.
|
|
100
|
+
// With flexGrow=1 the header row should span the full remaining width.
|
|
101
|
+
// With width=auto it should be content-sized (narrower).
|
|
102
|
+
const headerBgText = await session.text({
|
|
103
|
+
only: { background: '#e89500' },
|
|
104
|
+
timeout: 5000,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// The flexGrow header should have more trailing spaces (wider bg).
|
|
108
|
+
// Both tables have "Key" and "Value" headers, but the flexGrow one
|
|
109
|
+
// should have significantly more padding/whitespace in the bg.
|
|
110
|
+
expect(headerBgText).toContain('Key')
|
|
111
|
+
expect(headerBgText).toContain('Value')
|
|
112
|
+
|
|
113
|
+
// Extract the two header lines - flexGrow header should be longer
|
|
114
|
+
const lines = headerBgText.split('\n').filter((l) => {
|
|
115
|
+
return l.includes('Key')
|
|
116
|
+
})
|
|
117
|
+
// Should have at least 2 header lines (flexGrow + width=auto + column-based)
|
|
118
|
+
expect(lines.length).toBeGreaterThanOrEqual(2)
|
|
119
|
+
|
|
120
|
+
// The flexGrow line (first) should be wider than width=auto line (second)
|
|
121
|
+
const flexGrowLine = lines[0]!
|
|
122
|
+
const autoLine = lines[1]!
|
|
123
|
+
expect(flexGrowLine.length).toBeGreaterThan(autoLine.length)
|
|
124
|
+
}, 30000)
|
package/src/extensions/dev.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import { useNavigation } from 'termcast/src/internal/navigation'
|
|
|
14
14
|
import { TermcastProvider } from 'termcast/src/internal/providers'
|
|
15
15
|
import { showToast, Toast } from 'termcast/src/apis/toast'
|
|
16
16
|
import { Icon } from 'termcast'
|
|
17
|
-
import { useTheme } from 'termcast/src/theme'
|
|
17
|
+
import { useTheme, initializeTheme } from 'termcast/src/theme'
|
|
18
18
|
import { logger } from '../logger'
|
|
19
19
|
import { getCommandsWithFiles, CommandWithFile, RaycastPackageJson } from '../package-json'
|
|
20
20
|
import { buildExtensionCommands } from '../build'
|
|
@@ -226,6 +226,9 @@ export async function startDevMode({
|
|
|
226
226
|
devRebuildCount: 1,
|
|
227
227
|
})
|
|
228
228
|
|
|
229
|
+
// Load theme after state reset — extensionPath is now set so it reads from the correct DB
|
|
230
|
+
initializeTheme()
|
|
231
|
+
|
|
229
232
|
function App(): any {
|
|
230
233
|
const devElement = useStore((state) => state.devElement)
|
|
231
234
|
// REMOVED: key={devRebuildCount} - we want to preserve the React tree!
|
|
@@ -296,6 +299,9 @@ export async function startCompiledExtension({
|
|
|
296
299
|
devRebuildCount: 1,
|
|
297
300
|
})
|
|
298
301
|
|
|
302
|
+
// Load theme after state reset — extensionPath is now set so it reads from the correct DB
|
|
303
|
+
initializeTheme()
|
|
304
|
+
|
|
299
305
|
function App(): any {
|
|
300
306
|
const devElement = useStore((state) => state.devElement)
|
|
301
307
|
return <TermcastProvider>{devElement}</TermcastProvider>
|
package/src/globals.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as opentuiReact from '@opentui/react'
|
|
|
10
10
|
import * as react from 'react'
|
|
11
11
|
import * as reactJsxRuntime from 'react/jsx-runtime'
|
|
12
12
|
import * as termcastApi from './index'
|
|
13
|
+
import * as termcastOpentui from './opentui'
|
|
13
14
|
|
|
14
15
|
declare global {
|
|
15
16
|
var opentuiCore: typeof opentuiCore
|
|
@@ -17,6 +18,7 @@ declare global {
|
|
|
17
18
|
var react: typeof react
|
|
18
19
|
var reactJsxRuntime: typeof reactJsxRuntime
|
|
19
20
|
var termcastApi: typeof termcastApi
|
|
21
|
+
var termcastOpentui: typeof termcastOpentui
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
// Initialize globals
|
|
@@ -30,5 +32,6 @@ globalThis._Fragment = reactJsxRuntime.Fragment
|
|
|
30
32
|
|
|
31
33
|
globalThis.reactJsxRuntime = reactJsxRuntime
|
|
32
34
|
globalThis.termcastApi = termcastApi
|
|
35
|
+
globalThis.termcastOpentui = termcastOpentui
|
|
33
36
|
|
|
34
37
|
globalThis.logger = logger
|
package/src/index.tsx
CHANGED
|
@@ -52,6 +52,36 @@ export type {
|
|
|
52
52
|
DetailPropsWithLoading,
|
|
53
53
|
} from 'termcast/src/components/detail'
|
|
54
54
|
|
|
55
|
+
// Core UI Components - Graph
|
|
56
|
+
export { Graph } from 'termcast/src/components/graph'
|
|
57
|
+
export type {
|
|
58
|
+
GraphProps,
|
|
59
|
+
GraphLineProps,
|
|
60
|
+
GraphVariant,
|
|
61
|
+
} from 'termcast/src/components/graph'
|
|
62
|
+
|
|
63
|
+
// Core UI Components - Row
|
|
64
|
+
export { Row } from 'termcast/src/components/row'
|
|
65
|
+
export type { RowProps } from 'termcast/src/components/row'
|
|
66
|
+
|
|
67
|
+
// Core UI Components - Table
|
|
68
|
+
export { Table } from 'termcast/src/components/table'
|
|
69
|
+
export type { TableProps } from 'termcast/src/components/table'
|
|
70
|
+
|
|
71
|
+
// Core UI Components - BarChart
|
|
72
|
+
export { BarChart } from 'termcast/src/components/bar-chart'
|
|
73
|
+
export type {
|
|
74
|
+
BarChartProps,
|
|
75
|
+
BarChartSegmentProps,
|
|
76
|
+
} from 'termcast/src/components/bar-chart'
|
|
77
|
+
|
|
78
|
+
// Core UI Components - BarGraph
|
|
79
|
+
export { BarGraph } from 'termcast/src/components/bar-graph'
|
|
80
|
+
export type {
|
|
81
|
+
BarGraphProps,
|
|
82
|
+
BarGraphSeriesProps,
|
|
83
|
+
} from 'termcast/src/components/bar-graph'
|
|
84
|
+
|
|
55
85
|
// Form Components
|
|
56
86
|
import {
|
|
57
87
|
Form as FormComponent,
|
|
@@ -158,6 +188,7 @@ import type {
|
|
|
158
188
|
ImageFallback,
|
|
159
189
|
} from 'termcast/src/components/image'
|
|
160
190
|
|
|
191
|
+
|
|
161
192
|
export function Image(props: ImageProps): any {
|
|
162
193
|
return ImageComponent(props)
|
|
163
194
|
}
|
|
@@ -256,7 +256,9 @@ export function DatePickerWidget({
|
|
|
256
256
|
} else if (focus === 'year') {
|
|
257
257
|
// At top of widget, trigger callback or cycle to bottom
|
|
258
258
|
if (onFirstRowUpKey) {
|
|
259
|
+
key.stopPropagation()
|
|
259
260
|
onFirstRowUpKey()
|
|
261
|
+
return
|
|
260
262
|
} else {
|
|
261
263
|
// Cycle to grid if no callback
|
|
262
264
|
setFocus('grid')
|
|
@@ -274,7 +276,9 @@ export function DatePickerWidget({
|
|
|
274
276
|
if (remainingDays < 7) {
|
|
275
277
|
// At bottom of grid, trigger callback or cycle to top
|
|
276
278
|
if (onLastRowDownKey) {
|
|
279
|
+
key.stopPropagation()
|
|
277
280
|
onLastRowDownKey()
|
|
281
|
+
return
|
|
278
282
|
} else {
|
|
279
283
|
// Cycle to year if no callback
|
|
280
284
|
setFocus('year')
|
|
@@ -11,7 +11,7 @@ import { NavigationProvider } from 'termcast/src/internal/navigation'
|
|
|
11
11
|
import { CommonProps, termcastMaxContentWidth } from 'termcast/src/utils'
|
|
12
12
|
import { Cache } from 'termcast/src/apis/cache'
|
|
13
13
|
import { logger } from 'termcast/src/logger'
|
|
14
|
-
import { useTheme
|
|
14
|
+
import { useTheme } from 'termcast/src/theme'
|
|
15
15
|
import { useStore } from 'termcast/src/state'
|
|
16
16
|
import { useKeyboard, useRenderer } from '@opentui/react'
|
|
17
17
|
import { initializeErrorHandlers } from 'termcast/src/internal/error-handler'
|
|
@@ -22,9 +22,6 @@ import { Clipboard } from '../apis/clipboard'
|
|
|
22
22
|
// Initialize error handlers at module load time
|
|
23
23
|
initializeErrorHandlers()
|
|
24
24
|
|
|
25
|
-
// Initialize theme from persisted storage
|
|
26
|
-
initializeTheme()
|
|
27
|
-
|
|
28
25
|
const queryClient = new QueryClient({
|
|
29
26
|
defaultOptions: {
|
|
30
27
|
queries: {
|