tuimon 0.1.0 → 0.2.0
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/AI.md +191 -0
- package/README.md +223 -87
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +3 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +171 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +106 -0
- package/dist/config.js.map +1 -0
- package/dist/fkeybar.d.ts.map +1 -1
- package/dist/fkeybar.js +13 -0
- package/dist/fkeybar.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -18
- package/dist/index.js.map +1 -1
- package/dist/layout/generator-css.d.ts +3 -0
- package/dist/layout/generator-css.d.ts.map +1 -0
- package/dist/layout/generator-css.js +202 -0
- package/dist/layout/generator-css.js.map +1 -0
- package/dist/layout/generator-js.d.ts +3 -0
- package/dist/layout/generator-js.d.ts.map +1 -0
- package/dist/layout/generator-js.js +353 -0
- package/dist/layout/generator-js.js.map +1 -0
- package/dist/layout/generator.d.ts +4 -0
- package/dist/layout/generator.d.ts.map +1 -0
- package/dist/layout/generator.js +124 -0
- package/dist/layout/generator.js.map +1 -0
- package/dist/layout/theme.d.ts +4 -0
- package/dist/layout/theme.d.ts.map +1 -0
- package/dist/layout/theme.js +28 -0
- package/dist/layout/theme.js.map +1 -0
- package/dist/layout/types.d.ts +68 -0
- package/dist/layout/types.d.ts.map +1 -0
- package/dist/layout/types.js +3 -0
- package/dist/layout/types.js.map +1 -0
- package/dist/quick/auto-layout.d.ts +5 -0
- package/dist/quick/auto-layout.d.ts.map +1 -0
- package/dist/quick/auto-layout.js +262 -0
- package/dist/quick/auto-layout.js.map +1 -0
- package/dist/quick/db/detect.d.ts +16 -0
- package/dist/quick/db/detect.d.ts.map +1 -0
- package/dist/quick/db/detect.js +131 -0
- package/dist/quick/db/detect.js.map +1 -0
- package/dist/quick/db/mongo.d.ts +10 -0
- package/dist/quick/db/mongo.d.ts.map +1 -0
- package/dist/quick/db/mongo.js +81 -0
- package/dist/quick/db/mongo.js.map +1 -0
- package/dist/quick/db/mysql.d.ts +8 -0
- package/dist/quick/db/mysql.d.ts.map +1 -0
- package/dist/quick/db/mysql.js +43 -0
- package/dist/quick/db/mysql.js.map +1 -0
- package/dist/quick/db/postgres.d.ts +8 -0
- package/dist/quick/db/postgres.d.ts.map +1 -0
- package/dist/quick/db/postgres.js +47 -0
- package/dist/quick/db/postgres.js.map +1 -0
- package/dist/quick/db/sqlite.d.ts +8 -0
- package/dist/quick/db/sqlite.d.ts.map +1 -0
- package/dist/quick/db/sqlite.js +43 -0
- package/dist/quick/db/sqlite.js.map +1 -0
- package/dist/quick/db-mode.d.ts +13 -0
- package/dist/quick/db-mode.d.ts.map +1 -0
- package/dist/quick/db-mode.js +159 -0
- package/dist/quick/db-mode.js.map +1 -0
- package/dist/quick/detect.d.ts +4 -0
- package/dist/quick/detect.d.ts.map +1 -0
- package/dist/quick/detect.js +76 -0
- package/dist/quick/detect.js.map +1 -0
- package/dist/quick/file-mode.d.ts +5 -0
- package/dist/quick/file-mode.d.ts.map +1 -0
- package/dist/quick/file-mode.js +158 -0
- package/dist/quick/file-mode.js.map +1 -0
- package/dist/quick/parsers/csv.d.ts +3 -0
- package/dist/quick/parsers/csv.d.ts.map +1 -0
- package/dist/quick/parsers/csv.js +145 -0
- package/dist/quick/parsers/csv.js.map +1 -0
- package/dist/quick/parsers/detect-meta.d.ts +7 -0
- package/dist/quick/parsers/detect-meta.d.ts.map +1 -0
- package/dist/quick/parsers/detect-meta.js +35 -0
- package/dist/quick/parsers/detect-meta.js.map +1 -0
- package/dist/quick/parsers/json.d.ts +3 -0
- package/dist/quick/parsers/json.d.ts.map +1 -0
- package/dist/quick/parsers/json.js +74 -0
- package/dist/quick/parsers/json.js.map +1 -0
- package/dist/quick/parsers/log.d.ts +3 -0
- package/dist/quick/parsers/log.d.ts.map +1 -0
- package/dist/quick/parsers/log.js +185 -0
- package/dist/quick/parsers/log.js.map +1 -0
- package/dist/quick/parsers/modsec.d.ts +3 -0
- package/dist/quick/parsers/modsec.d.ts.map +1 -0
- package/dist/quick/parsers/modsec.js +338 -0
- package/dist/quick/parsers/modsec.js.map +1 -0
- package/dist/quick/types.d.ts +90 -0
- package/dist/quick/types.d.ts.map +1 -0
- package/dist/quick/types.js +3 -0
- package/dist/quick/types.js.map +1 -0
- package/dist/quick/watch-mode.d.ts +3 -0
- package/dist/quick/watch-mode.d.ts.map +1 -0
- package/dist/quick/watch-mode.js +156 -0
- package/dist/quick/watch-mode.js.map +1 -0
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/examples/demo.ts +134 -0
- package/examples/generate-access-log.ts +42 -0
- package/examples/screenshot-test.ts +105 -0
- package/examples/screenshot.png +0 -0
- package/frame-log.txt +830 -0
- package/package.json +10 -5
package/AI.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# TuiMon — AI Integration Guide
|
|
2
|
+
|
|
3
|
+
> Use this file when a user asks to display, monitor, or visualize data in a terminal.
|
|
4
|
+
> TuiMon renders beautiful dashboards with zero setup. Just install, send data, done.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install -g tuimon
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quickest Paths (pick one)
|
|
13
|
+
|
|
14
|
+
### Path 1: I have a data file
|
|
15
|
+
```bash
|
|
16
|
+
tuimon data.json # JSON array of objects → instant table + charts
|
|
17
|
+
tuimon users.csv # CSV → instant table + charts
|
|
18
|
+
tuimon access.log # Nginx log → request stats + browsable table
|
|
19
|
+
tuimon modsec.log # ModSecurity → security dashboard
|
|
20
|
+
```
|
|
21
|
+
That's it. No code needed. TuiMon auto-detects format and creates the dashboard.
|
|
22
|
+
|
|
23
|
+
### Path 2: I want live data from a function
|
|
24
|
+
Create one file:
|
|
25
|
+
```js
|
|
26
|
+
// monitor.js
|
|
27
|
+
module.exports = () => ({
|
|
28
|
+
cpu: require('os').cpus().reduce((a,c) => a + (100 - c.times.idle / Object.values(c.times).reduce((x,y)=>x+y,0) * 100), 0) / require('os').cpus().length | 0,
|
|
29
|
+
memory: ((1 - require('os').freemem() / require('os').totalmem()) * 100) | 0,
|
|
30
|
+
uptime: process.uptime() | 0,
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
```bash
|
|
34
|
+
tuimon watch monitor.js
|
|
35
|
+
```
|
|
36
|
+
TuiMon auto-detects data types and creates appropriate widgets. Refreshes every second.
|
|
37
|
+
|
|
38
|
+
### Path 3: I want to monitor an HTTP endpoint
|
|
39
|
+
```bash
|
|
40
|
+
tuimon watch --url http://localhost:3000/api/stats
|
|
41
|
+
```
|
|
42
|
+
The endpoint must return JSON. TuiMon polls it every second and auto-creates the dashboard.
|
|
43
|
+
|
|
44
|
+
### Path 4: I want a custom layout (still no HTML)
|
|
45
|
+
```js
|
|
46
|
+
// dashboard.js
|
|
47
|
+
const tuimon = require('tuimon')
|
|
48
|
+
|
|
49
|
+
tuimon.default.start({
|
|
50
|
+
pages: {
|
|
51
|
+
main: {
|
|
52
|
+
default: true,
|
|
53
|
+
layout: {
|
|
54
|
+
title: 'My Dashboard',
|
|
55
|
+
stats: [
|
|
56
|
+
{ id: 'users', label: 'Users', type: 'stat' },
|
|
57
|
+
{ id: 'cpu', label: 'CPU', type: 'gauge' },
|
|
58
|
+
],
|
|
59
|
+
panels: [
|
|
60
|
+
{ id: 'requests', label: 'Requests', type: 'line', span: 2 },
|
|
61
|
+
{ id: 'errors', label: 'Errors', type: 'event-log' },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
refresh: 1000,
|
|
67
|
+
data: () => ({
|
|
68
|
+
users: getUserCount(),
|
|
69
|
+
cpu: getCpuPercent(),
|
|
70
|
+
requests: { HTTP: getReqRate(), WS: getWsRate() },
|
|
71
|
+
errors: getRecentErrors(),
|
|
72
|
+
}),
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Data Format Rules
|
|
77
|
+
|
|
78
|
+
TuiMon accepts the **simplest possible data**. Just send what you have:
|
|
79
|
+
|
|
80
|
+
| You send | TuiMon shows |
|
|
81
|
+
|----------|-------------|
|
|
82
|
+
| `42` | Stat card with number |
|
|
83
|
+
| `73` (for id containing cpu/mem/disk) | Gauge (0-100%) |
|
|
84
|
+
| `"running"` | Stat card with text |
|
|
85
|
+
| `{ value: 42, trend: '+5', unit: 'req/s' }` | Stat card with trend |
|
|
86
|
+
| `{ Requests: 340, Errors: 12 }` | Line chart (auto-accumulates history over time) |
|
|
87
|
+
| `{ GET: 200, POST: 50 }` | Bar chart |
|
|
88
|
+
| `['Deploy completed', 'Scaled up']` | Event log (auto-timestamped) |
|
|
89
|
+
| `[{ text: 'Error', type: 'error' }]` | Event log (colored by type) |
|
|
90
|
+
| `['Node-1', 'Node-2']` | Status grid (all green) |
|
|
91
|
+
| `[{ label: 'DB', status: 'error' }]` | Status grid (colored dots) |
|
|
92
|
+
|
|
93
|
+
**Key insight for line charts:** You don't manage history. Each call to `render()` or each refresh sends CURRENT values. TuiMon accumulates the history automatically and builds the chart over time.
|
|
94
|
+
|
|
95
|
+
## Widget Types
|
|
96
|
+
|
|
97
|
+
| Type | Use for |
|
|
98
|
+
|------|---------|
|
|
99
|
+
| `stat` | Single number or text value |
|
|
100
|
+
| `gauge` | Percentage (0-100 with color bar) |
|
|
101
|
+
| `line` | Time series (auto-accumulates history) |
|
|
102
|
+
| `bar` | Categorical comparison |
|
|
103
|
+
| `doughnut` | Distribution/proportion |
|
|
104
|
+
| `event-log` | Scrolling list of events |
|
|
105
|
+
| `status-grid` | Health indicators (colored dots) |
|
|
106
|
+
| `table` | Tabular data with pagination |
|
|
107
|
+
|
|
108
|
+
## Per-Widget Throttle
|
|
109
|
+
|
|
110
|
+
Widgets can update at different speeds:
|
|
111
|
+
```js
|
|
112
|
+
panels: [
|
|
113
|
+
{ id: 'chart', label: 'Chart', type: 'line' }, // every frame
|
|
114
|
+
{ id: 'events', label: 'Events', type: 'event-log', throttle: 2000 }, // max every 2s
|
|
115
|
+
{ id: 'health', label: 'Health', type: 'status-grid', throttle: 5000 }, // max every 5s
|
|
116
|
+
]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## VSCode Terminal
|
|
120
|
+
|
|
121
|
+
TuiMon works in VSCode's integrated terminal. Ensure this setting is enabled:
|
|
122
|
+
```json
|
|
123
|
+
{ "terminal.integrated.enableImages": true }
|
|
124
|
+
```
|
|
125
|
+
Running `tuimon init` sets this automatically.
|
|
126
|
+
|
|
127
|
+
## Common AI Use Cases
|
|
128
|
+
|
|
129
|
+
### "Show me my server stats"
|
|
130
|
+
```js
|
|
131
|
+
module.exports = () => ({
|
|
132
|
+
cpu: os.loadavg()[0],
|
|
133
|
+
memory: ((1 - os.freemem()/os.totalmem()) * 100) | 0,
|
|
134
|
+
uptime: process.uptime() | 0,
|
|
135
|
+
processes: execSync('ps aux | wc -l').toString().trim(),
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
```bash
|
|
139
|
+
tuimon watch server-stats.js
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### "Monitor my API"
|
|
143
|
+
```bash
|
|
144
|
+
tuimon watch --url http://localhost:3000/health
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### "Visualize this JSON data"
|
|
148
|
+
```bash
|
|
149
|
+
tuimon data.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### "Show me my nginx traffic"
|
|
153
|
+
```bash
|
|
154
|
+
tuimon /var/log/nginx/access.log
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### "Monitor my database"
|
|
158
|
+
```js
|
|
159
|
+
module.exports = async () => {
|
|
160
|
+
const pool = require('./db')
|
|
161
|
+
const { rows } = await pool.query('SELECT count(*) as c FROM users WHERE active = true')
|
|
162
|
+
return {
|
|
163
|
+
activeUsers: rows[0].c,
|
|
164
|
+
connections: pool.totalCount,
|
|
165
|
+
idle: pool.idleCount,
|
|
166
|
+
waiting: pool.waitingCount,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
```bash
|
|
171
|
+
tuimon watch db-monitor.js
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### "Show security events"
|
|
175
|
+
```bash
|
|
176
|
+
tuimon /var/log/modsec_audit.log
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## CLI Reference
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
tuimon <file> # Visualize JSON/CSV/log file
|
|
183
|
+
tuimon <file> -c "col1,col2" # Show specific columns only
|
|
184
|
+
tuimon watch <file.js> # Live data from JS module
|
|
185
|
+
tuimon watch --url <url> # Poll JSON endpoint
|
|
186
|
+
tuimon watch --url <url> --interval 5000 # Custom poll interval
|
|
187
|
+
tuimon start # Full config mode
|
|
188
|
+
tuimon init # Scaffold project + enable VSCode
|
|
189
|
+
tuimon check # Verify terminal graphics support
|
|
190
|
+
tuimon ai # Print this guide
|
|
191
|
+
```
|
package/README.md
CHANGED
|
@@ -1,152 +1,288 @@
|
|
|
1
1
|
# TuiMon
|
|
2
2
|
|
|
3
|
-
> Render beautiful
|
|
3
|
+
> Render beautiful dashboards directly in your terminal. Zero config required.
|
|
4
4
|
|
|
5
|
-
## Terminal
|
|
5
|
+
## The Simplest Way to Visualize Data in Your Terminal
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
```bash
|
|
8
|
+
# Visualize any data file instantly
|
|
9
|
+
tuimon data.json
|
|
10
|
+
tuimon users.csv
|
|
11
|
+
tuimon /var/log/nginx/access.log
|
|
12
|
+
|
|
13
|
+
# Live monitoring from a data function
|
|
14
|
+
tuimon watch metrics.js
|
|
15
|
+
|
|
16
|
+
# Poll a JSON API
|
|
17
|
+
tuimon watch --url http://localhost:3000/metrics
|
|
18
|
+
|
|
19
|
+
# Full custom dashboard (HTML/CSS/JS)
|
|
20
|
+
tuimon start
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
No HTML. No config files. No charting libraries. Just point TuiMon at your data.
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
## Terminal Support
|
|
26
|
+
|
|
27
|
+
| Terminal | Protocol | Status |
|
|
28
|
+
|----------|----------|--------|
|
|
29
|
+
| Kitty | Kitty | Supported |
|
|
30
|
+
| Ghostty | Kitty | Supported |
|
|
31
|
+
| WezTerm | Kitty | Supported |
|
|
32
|
+
| iTerm2 | iTerm2 | Supported |
|
|
33
|
+
| **VSCode** | Sixel | **Supported** (requires one setting) |
|
|
34
|
+
| mlterm | Sixel | Supported |
|
|
35
|
+
|
|
36
|
+
### VSCode Setup
|
|
37
|
+
|
|
38
|
+
VSCode supports terminal images but it's **off by default**. Running `tuimon init` enables it automatically by creating `.vscode/settings.json` with:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{ "terminal.integrated.enableImages": true }
|
|
42
|
+
```
|
|
17
43
|
|
|
18
44
|
## Quick Start
|
|
19
45
|
|
|
20
46
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
npx tuimon start
|
|
47
|
+
npm install -g tuimon
|
|
48
|
+
tuimon check # verify your terminal supports graphics
|
|
24
49
|
```
|
|
25
50
|
|
|
26
|
-
|
|
51
|
+
## 1. Instant File Visualization
|
|
52
|
+
|
|
53
|
+
Point TuiMon at any data file — it auto-detects the format, picks the right charts, and renders a dashboard:
|
|
27
54
|
|
|
28
55
|
```bash
|
|
29
|
-
|
|
30
|
-
tuimon
|
|
56
|
+
# JSON array → table + charts
|
|
57
|
+
tuimon users.json
|
|
58
|
+
|
|
59
|
+
# CSV → table + charts
|
|
60
|
+
tuimon sales.csv
|
|
61
|
+
|
|
62
|
+
# Nginx access log → request stats + table
|
|
63
|
+
tuimon /var/log/nginx/access.log
|
|
64
|
+
|
|
65
|
+
# ModSecurity audit log → security dashboard
|
|
66
|
+
tuimon /var/log/modsec_audit.log
|
|
67
|
+
|
|
68
|
+
# Filter columns
|
|
69
|
+
tuimon access.log --columns "Timestamp,IP,Path,Status"
|
|
31
70
|
```
|
|
32
71
|
|
|
33
|
-
|
|
72
|
+
**Auto-detected formats:**
|
|
73
|
+
- **JSON/JSONL** — arrays of objects → table + stat cards + charts
|
|
74
|
+
- **CSV/TSV** — auto-detects delimiter, header row, column types
|
|
75
|
+
- **Nginx combined log** — request stats, status codes, top endpoints/IPs, browsable request table
|
|
76
|
+
- **ModSecurity audit log** — security events, severity distribution, attack categories, top attacker IPs
|
|
77
|
+
- **JSON logs** — level distribution, latest entries
|
|
78
|
+
- **Plain text** — live tail
|
|
79
|
+
|
|
80
|
+
**Features:**
|
|
81
|
+
- Press **D** to switch to full-screen data table view, **ESC** to go back
|
|
82
|
+
- Arrow keys / PgUp / PgDn to navigate table pages
|
|
83
|
+
- **F5** to reload file, **F10** to quit
|
|
84
|
+
- File is **watched** — dashboard updates when the file changes on disk
|
|
85
|
+
|
|
86
|
+
## 2. Live Data Monitoring
|
|
87
|
+
|
|
88
|
+
Create a JS file that exports a data function — TuiMon handles the rest:
|
|
34
89
|
|
|
35
|
-
|
|
90
|
+
```js
|
|
91
|
+
// metrics.js
|
|
92
|
+
const os = require('os')
|
|
36
93
|
|
|
37
|
-
|
|
94
|
+
module.exports = () => ({
|
|
95
|
+
cpu: Math.round(100 - (os.cpus().reduce((a,c) => a + c.times.idle, 0) / os.cpus().reduce((a,c) => a + Object.values(c.times).reduce((x,y) => x+y, 0), 0)) * 100),
|
|
96
|
+
memory: Math.round((1 - os.freemem() / os.totalmem()) * 100),
|
|
97
|
+
uptime: Math.round(process.uptime()) + 's',
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
tuimon watch metrics.js
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
TuiMon inspects the returned data and auto-creates the right widgets:
|
|
106
|
+
|
|
107
|
+
| Data type | Widget |
|
|
108
|
+
|-----------|--------|
|
|
109
|
+
| Number (0-100 + name like cpu/mem) | Gauge |
|
|
110
|
+
| Number (other) | Stat card |
|
|
111
|
+
| String | Stat card |
|
|
112
|
+
| `{ key: number, ... }` (2+ keys) | Line chart (auto-accumulates history) |
|
|
113
|
+
| `{ key: number }` | Bar chart |
|
|
114
|
+
| `['string', ...]` | Event log (auto-timestamped) |
|
|
115
|
+
| `[{ label, status }, ...]` | Status grid (colored dots) |
|
|
116
|
+
|
|
117
|
+
**Export options:**
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
module.exports = () => ({ ... }) // data function (required)
|
|
121
|
+
module.exports.refresh = 500 // refresh interval in ms (default: 1000)
|
|
122
|
+
module.exports.title = 'My App' // dashboard title
|
|
123
|
+
module.exports.layout = { ... } // override auto-detected layout
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Poll a JSON API
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
tuimon watch --url http://localhost:3000/metrics
|
|
130
|
+
tuimon watch --url http://localhost:3000/metrics --interval 2000
|
|
131
|
+
```
|
|
38
132
|
|
|
39
|
-
|
|
133
|
+
## 3. Zero-HTML Declarative Dashboard
|
|
134
|
+
|
|
135
|
+
For more control without writing HTML, define widgets in a layout config:
|
|
40
136
|
|
|
41
137
|
```typescript
|
|
138
|
+
import tuimon from 'tuimon'
|
|
139
|
+
|
|
42
140
|
const dash = await tuimon.start({
|
|
43
141
|
pages: {
|
|
44
142
|
overview: {
|
|
45
|
-
html: './pages/overview.html',
|
|
46
143
|
default: true,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
144
|
+
layout: {
|
|
145
|
+
title: 'My App',
|
|
146
|
+
stats: [
|
|
147
|
+
{ id: 'users', label: 'Users Online', type: 'stat' },
|
|
148
|
+
{ id: 'cpu', label: 'CPU', type: 'gauge' },
|
|
149
|
+
],
|
|
150
|
+
panels: [
|
|
151
|
+
{ id: 'traffic', label: 'Traffic', type: 'line', span: 2 },
|
|
152
|
+
{ id: 'services', label: 'Services', type: 'doughnut' },
|
|
153
|
+
{ id: 'events', label: 'Events', type: 'event-log', throttle: 2000 },
|
|
154
|
+
{ id: 'health', label: 'Health', type: 'status-grid', throttle: 5000 },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
58
157
|
},
|
|
59
158
|
},
|
|
159
|
+
refresh: 500,
|
|
160
|
+
data: () => ({
|
|
161
|
+
users: getOnlineCount(),
|
|
162
|
+
cpu: getCpuPercent(),
|
|
163
|
+
traffic: { Requests: 340, Errors: 12 },
|
|
164
|
+
services: { Web: 47, API: 27 },
|
|
165
|
+
events: ['Deploy completed'],
|
|
166
|
+
health: ['Node-1', 'Node-2'],
|
|
167
|
+
}),
|
|
60
168
|
})
|
|
61
169
|
```
|
|
62
170
|
|
|
63
|
-
|
|
64
|
-
- Press **ESC** on a detail page to return to overview
|
|
65
|
-
- Press **ESC** on overview to show quit confirmation
|
|
66
|
-
- Press **Ctrl+C** anywhere to exit immediately
|
|
171
|
+
**8 widget types:** `stat`, `gauge`, `line`, `doughnut`, `bar`, `event-log`, `status-grid`, `table`
|
|
67
172
|
|
|
68
|
-
|
|
173
|
+
**Per-widget throttle:** Each widget can have its own update rate — charts update every frame while event logs and status grids update less frequently.
|
|
69
174
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
175
|
+
**Lazy data format:** Every widget accepts the simplest possible data. Just send numbers — TuiMon figures out the rest:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
dash.render({
|
|
179
|
+
users: 42, // number → stat
|
|
180
|
+
cpu: 73, // number → gauge (0-100)
|
|
181
|
+
traffic: { Requests: 340, Errors: 12 }, // object → line chart (history auto-accumulated)
|
|
182
|
+
services: { Web: 47, API: 27 }, // object → doughnut
|
|
183
|
+
events: ['Deploy completed'], // string[] → event-log (auto-timestamped)
|
|
184
|
+
health: ['Node-1', 'Node-2'], // string[] → status-grid (all "ok")
|
|
185
|
+
})
|
|
74
186
|
```
|
|
75
187
|
|
|
76
|
-
##
|
|
188
|
+
## 4. Full Custom HTML Dashboard
|
|
77
189
|
|
|
78
|
-
|
|
190
|
+
For complete control, write your dashboard as normal HTML/CSS/JS:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
tuimon init # scaffolds starter project
|
|
194
|
+
tuimon start # runs it
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
TuiMon renders your HTML pages in a headless Chromium browser via Playwright, screenshots them as PNG, and streams the images to your terminal. Any charting library works — Chart.js, D3, ECharts, anything.
|
|
198
|
+
|
|
199
|
+
### Multi-Page Navigation
|
|
79
200
|
|
|
80
201
|
```typescript
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
202
|
+
const dash = await tuimon.start({
|
|
203
|
+
pages: {
|
|
204
|
+
overview: {
|
|
205
|
+
html: './pages/overview.html',
|
|
206
|
+
default: true,
|
|
207
|
+
label: 'Overview',
|
|
208
|
+
},
|
|
209
|
+
cpu: {
|
|
210
|
+
html: './pages/cpu-detail.html',
|
|
211
|
+
shortcut: 'g',
|
|
212
|
+
label: 'CPU Detail',
|
|
88
213
|
},
|
|
89
214
|
},
|
|
90
|
-
}
|
|
215
|
+
})
|
|
91
216
|
```
|
|
92
217
|
|
|
93
|
-
|
|
218
|
+
- Press a shortcut key to jump to a detail page
|
|
219
|
+
- **ESC** on detail page returns to overview
|
|
220
|
+
- **ESC** on overview shows quit confirmation
|
|
221
|
+
- **Ctrl+C** exits immediately from anywhere
|
|
94
222
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
### `tuimon.start(options: TuiMonOptions): Promise<TuiMonDashboard>`
|
|
223
|
+
### Per-Page F-Key Bindings
|
|
98
224
|
|
|
99
225
|
```typescript
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
refresh?: number // auto-render interval in ms
|
|
104
|
-
renderDelay?: number // delay after pushData before screenshot (default: 50)
|
|
226
|
+
keys: {
|
|
227
|
+
F5: { label: 'Refresh', action: async () => dash.render(await getData()) },
|
|
228
|
+
F10: { label: 'Quit', action: () => process.exit(0) },
|
|
105
229
|
}
|
|
230
|
+
```
|
|
106
231
|
|
|
107
|
-
|
|
108
|
-
html: string // path to HTML file
|
|
109
|
-
default?: boolean // exactly one page must be default
|
|
110
|
-
shortcut?: string // single lowercase letter
|
|
111
|
-
label?: string // human-readable name
|
|
112
|
-
keys?: Partial<Record<FKey, KeyBinding>>
|
|
113
|
-
}
|
|
232
|
+
### Client Library
|
|
114
233
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
234
|
+
In your HTML pages, the injected `TuiMon` object receives data:
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
TuiMon.onUpdate(function(data) {
|
|
238
|
+
TuiMon.set('#cpu', data.cpu)
|
|
239
|
+
TuiMon.set('#memory', data.memory + '%')
|
|
240
|
+
})
|
|
119
241
|
```
|
|
120
242
|
|
|
121
|
-
|
|
243
|
+
Add shortcut badges to panels with data attributes:
|
|
122
244
|
|
|
123
|
-
|
|
245
|
+
```html
|
|
246
|
+
<div class="panel" data-tm-key="g" data-tm-label="CPU Detail">
|
|
247
|
+
```
|
|
124
248
|
|
|
125
|
-
|
|
249
|
+
## CLI Reference
|
|
126
250
|
|
|
127
|
-
|
|
251
|
+
| Command | Description |
|
|
252
|
+
|---------|-------------|
|
|
253
|
+
| `tuimon <file>` | Visualize a JSON, CSV, or log file |
|
|
254
|
+
| `tuimon watch <file.js>` | Live dashboard from a data module |
|
|
255
|
+
| `tuimon watch --url <url>` | Poll a JSON endpoint |
|
|
256
|
+
| `tuimon start` | Run from tuimon.config.ts |
|
|
257
|
+
| `tuimon init` | Scaffold a starter project + enable VSCode |
|
|
258
|
+
| `tuimon check` | Check terminal graphics support |
|
|
128
259
|
|
|
129
|
-
|
|
260
|
+
**Options:**
|
|
261
|
+
- `-c, --columns <cols>` — comma-separated list of columns to display
|
|
262
|
+
- `--interval <ms>` — poll interval for URL watch mode (default: 1000)
|
|
130
263
|
|
|
131
|
-
|
|
264
|
+
**Environment:**
|
|
265
|
+
- `TUIMON_DEBUG=1` — print per-frame timing to stderr
|
|
132
266
|
|
|
133
|
-
|
|
134
|
-
// Listen for data updates
|
|
135
|
-
TuiMon.onUpdate(function(data) {
|
|
136
|
-
TuiMon.set('#cpu', data.cpu)
|
|
137
|
-
TuiMon.set('#memory', data.memory + '%')
|
|
138
|
-
})
|
|
267
|
+
## How It Works
|
|
139
268
|
|
|
140
|
-
// TuiMon.set(selector, value) — sets text content or applies styles
|
|
141
|
-
// TuiMon.notify(message, duration) — dispatches a notification event
|
|
142
269
|
```
|
|
270
|
+
Your data → TuiMon → Headless Chromium → Screenshot → Kitty/Sixel → Terminal
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
1. Your data is pushed to an HTML page running in headless Chromium
|
|
274
|
+
2. Playwright takes a PNG screenshot
|
|
275
|
+
3. The image is encoded as Kitty graphics protocol (or Sixel fallback)
|
|
276
|
+
4. Written directly to your terminal's stdout
|
|
277
|
+
|
|
278
|
+
Typical frame time: **~50ms** (at 250ms refresh = 4 FPS with headroom to spare).
|
|
143
279
|
|
|
144
280
|
## Contributing
|
|
145
281
|
|
|
146
282
|
- **TDD required** — write tests first, implementation second
|
|
147
283
|
- Coverage thresholds: 80% lines, 80% functions, 75% branches
|
|
148
284
|
- `npm test` must pass before any PR
|
|
149
|
-
- Strict TypeScript — `strict: true`, no `any`
|
|
285
|
+
- Strict TypeScript — `strict: true`, `noUncheckedIndexedAccess`, no `any`
|
|
150
286
|
|
|
151
287
|
## License
|
|
152
288
|
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,wBAAsB,aAAa,CAAC,EAClC,GAAG,EACH,KAAK,EACL,MAAM,GACP,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,wBAAsB,aAAa,CAAC,EAClC,GAAG,EACH,KAAK,EACL,MAAM,GACP,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,aAAa,CAAC,CAkEzB"}
|
package/dist/browser.js
CHANGED
|
@@ -49,6 +49,9 @@ export async function createBrowser({ url, width, height, }) {
|
|
|
49
49
|
async resize(w, h) {
|
|
50
50
|
await page.setViewportSize({ width: w, height: h });
|
|
51
51
|
},
|
|
52
|
+
async evaluate(expression) {
|
|
53
|
+
await page.evaluate(expression);
|
|
54
|
+
},
|
|
52
55
|
async close() {
|
|
53
56
|
await browser.close();
|
|
54
57
|
},
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGrC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,GAAG,EACH,KAAK,EACL,MAAM,GAKP;IACC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;IAC1C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IAEpC,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7C,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAA;IAEvD,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAC1B,UAAU,EAAE,CAAA;QACZ,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAA;YAChF,OAAM;QACR,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,UAAU,kBAAkB,CAAC,CAAA;QACpF,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,KAAK,CAAC,UAAU;YACd,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAClD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,IAA6B;YAC1C,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAA0B,EAAE,EAAE;gBACjD,MAAM,GAAG,GAAG,UAAqC,CAAA;gBACjD,IAAI,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,UAAU,EAAE,CAAC;oBACnD,CAAC;oBAAC,GAAG,CAAC,mBAAmB,CAA0C,CAAC,CAAC,CAAC,CAAA;gBACxE,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,CAAA;YACR,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,eAAe,CACxB,GAAG,EAAE,CAAE,UAAsC,CAAC,kBAAkB,CAAC,KAAK,IAAI,EAC1E,EAAE,OAAO,EAAE,IAAI,EAAE,CAClB,CAAA;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;YACpE,CAAC;QACH,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,MAAc;YAC3B,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,CAAS,EAAE,CAAS;YAC/B,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QACrD,CAAC;QAED,KAAK,CAAC,KAAK;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACvB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|