tuimon 0.1.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/CLAUDE.md +80 -0
- package/README.md +153 -0
- package/client/tuimon-client.js +44 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +57 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +87 -0
- package/dist/cli.js.map +1 -0
- package/dist/detect.d.ts +8 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +141 -0
- package/dist/detect.js.map +1 -0
- package/dist/encoder.d.ts +4 -0
- package/dist/encoder.d.ts.map +1 -0
- package/dist/encoder.js +115 -0
- package/dist/encoder.js.map +1 -0
- package/dist/fkeybar.d.ts +5 -0
- package/dist/fkeybar.d.ts.map +1 -0
- package/dist/fkeybar.js +89 -0
- package/dist/fkeybar.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/keyhandler.d.ts +5 -0
- package/dist/keyhandler.d.ts.map +1 -0
- package/dist/keyhandler.js +20 -0
- package/dist/keyhandler.js.map +1 -0
- package/dist/router.d.ts +10 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +154 -0
- package/dist/router.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +142 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
- package/templates/internal/confirm-quit.html +94 -0
- package/templates/starter/pages/cpu-detail.html +176 -0
- package/templates/starter/pages/memory-detail.html +166 -0
- package/templates/starter/pages/overview.html +186 -0
- package/templates/starter/tuimon.config.ts +87 -0
- package/tsconfig.json +23 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Build & Development Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run build # tsc → dist/
|
|
9
|
+
npm run dev # tsc --watch
|
|
10
|
+
npm test # vitest run (single pass)
|
|
11
|
+
npm run test:watch # vitest (watch mode)
|
|
12
|
+
npm run test:coverage # vitest run --coverage
|
|
13
|
+
npm run typecheck # tsc --noEmit
|
|
14
|
+
npm run lint # eslint src --ext .ts
|
|
15
|
+
npm run format # prettier --write src
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run a single test file: `npx vitest run src/__tests__/router.test.ts`
|
|
19
|
+
Run tests matching a pattern: `npx vitest run --grep "shortcut navigation"`
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
TuiMon renders HTML pages in a headless Chromium browser, screenshots them as PNG, and streams images to the terminal via Kitty/Sixel graphics protocols. Developers write dashboards as plain HTML/CSS/JS.
|
|
24
|
+
|
|
25
|
+
### Startup Sequence (src/index.ts)
|
|
26
|
+
|
|
27
|
+
`tuimon.start(options)` orchestrates everything in this order:
|
|
28
|
+
|
|
29
|
+
1. **detect.ts** — probe terminal for Kitty/Sixel/iTerm2 support and pixel dimensions
|
|
30
|
+
2. **server.ts** — spin up an HTTP server on localhost:7337+ serving HTML pages from a common root directory
|
|
31
|
+
3. **browser.ts** — launch headless Chromium via Playwright, set viewport to terminal pixel dimensions
|
|
32
|
+
4. **router.ts** — create navigation state machine wired to navigate/render/setKeys callbacks
|
|
33
|
+
5. **keyhandler.ts** — enable stdin raw mode, forward raw key strings to router.handleKey
|
|
34
|
+
6. **fkeybar.ts** — render F-key status bar on the terminal's last row
|
|
35
|
+
|
|
36
|
+
### Render Pipeline
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
dash.render(data) → browser.pushData(data) → page.__tuimon_update__(data)
|
|
40
|
+
→ setTimeout(renderDelay) → browser.screenshot() → encodeAndRender(png, protocol)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The encoder writes directly to stdout: Kitty uses base64 PNG chunked at 4096 bytes; Sixel uses sharp to convert to raw pixels then builds sixel bands.
|
|
44
|
+
|
|
45
|
+
### Router State Machine (src/router.ts)
|
|
46
|
+
|
|
47
|
+
Three states: `overview` → `detail` → `confirm-quit`. This is the navigation core:
|
|
48
|
+
- **overview**: shortcut keys navigate to detail pages; ESC opens confirm-quit
|
|
49
|
+
- **detail**: ESC returns to overview; shortcuts ignored
|
|
50
|
+
- **confirm-quit**: Y exits, N/ESC returns to previous state; all F-keys ignored
|
|
51
|
+
- **Ctrl+C**: immediate exit from any state
|
|
52
|
+
|
|
53
|
+
F-key escape sequences are mapped in a lookup table (e.g., `\x1bOP` → F1, `\x1b[15~` → F5).
|
|
54
|
+
|
|
55
|
+
### Client Bridge (client/tuimon-client.js)
|
|
56
|
+
|
|
57
|
+
Auto-injected into served HTML pages by the server. Exposes `window.TuiMon.onUpdate(cb)` and `window.TuiMon.set(selector, value)`. Data attributes `data-tm-key` and `data-tm-label` on HTML elements get automatic shortcut badges.
|
|
58
|
+
|
|
59
|
+
## TypeScript Rules
|
|
60
|
+
|
|
61
|
+
- **Strict mode** with `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitReturns`
|
|
62
|
+
- **ESM only** — module: NodeNext, all imports use `.js` extension
|
|
63
|
+
- All shared interfaces live in `src/types.ts` — modules import types from there
|
|
64
|
+
- iterm2 protocol maps to kitty for encoding (only 'kitty' | 'sixel' reach the encoder)
|
|
65
|
+
|
|
66
|
+
## Testing Patterns
|
|
67
|
+
|
|
68
|
+
Tests live in `src/__tests__/`. Setup file (`setup.ts`) converts `process.stdout.columns/rows` to getter properties for `vi.spyOn` compatibility.
|
|
69
|
+
|
|
70
|
+
- **Module mocking**: `vi.mock('../module.js', () => ({ ... }))` at file top
|
|
71
|
+
- **Dynamic imports**: tests use `await import('../module.js')` after `vi.resetModules()` for env-dependent modules (detect, router)
|
|
72
|
+
- **stdout capture**: `vi.spyOn(process.stdout, 'write').mockImplementation(() => true)` then collect calls
|
|
73
|
+
- **stdin simulation**: create `EventEmitter` with `setRawMode`/`resume` stubs, mock `process.stdin`
|
|
74
|
+
- **Server tests**: create real temp directories with `mkdirSync`, track handles for cleanup in `afterEach`
|
|
75
|
+
|
|
76
|
+
Coverage thresholds: 80% lines/functions/statements, 75% branches. cli.ts is excluded.
|
|
77
|
+
|
|
78
|
+
## Formatting
|
|
79
|
+
|
|
80
|
+
No semicolons, single quotes, trailing commas, 100 char width, 2-space indent (see .prettierrc).
|
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# TuiMon
|
|
2
|
+
|
|
3
|
+
> Render beautiful HTML dashboards directly in your terminal.
|
|
4
|
+
|
|
5
|
+
## Terminal Requirements
|
|
6
|
+
|
|
7
|
+
| Terminal | Protocol | Status |
|
|
8
|
+
|-----------|----------|------------|
|
|
9
|
+
| Kitty | Kitty | Supported |
|
|
10
|
+
| Ghostty | Kitty | Supported |
|
|
11
|
+
| WezTerm | Kitty | Supported |
|
|
12
|
+
| iTerm2 | iTerm2 | Supported |
|
|
13
|
+
| VSCode | Sixel | Supported* |
|
|
14
|
+
| mlterm | Sixel | Supported |
|
|
15
|
+
|
|
16
|
+
\* Sixel support may vary by terminal version.
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx tuimon init
|
|
22
|
+
npx tuimon check
|
|
23
|
+
npx tuimon start
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or install globally:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g tuimon
|
|
30
|
+
tuimon init && tuimon start
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How It Works
|
|
34
|
+
|
|
35
|
+
TuiMon renders your HTML pages in a headless Chromium browser via Playwright, screenshots them as PNG, and streams the images to your terminal using the Kitty graphics protocol (or Sixel as fallback). You write dashboards as normal HTML/CSS/JS — any charting library works.
|
|
36
|
+
|
|
37
|
+
## Multi-Page Navigation
|
|
38
|
+
|
|
39
|
+
Define multiple pages with keyboard shortcuts:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const dash = await tuimon.start({
|
|
43
|
+
pages: {
|
|
44
|
+
overview: {
|
|
45
|
+
html: './pages/overview.html',
|
|
46
|
+
default: true,
|
|
47
|
+
label: 'Overview',
|
|
48
|
+
},
|
|
49
|
+
cpu: {
|
|
50
|
+
html: './pages/cpu-detail.html',
|
|
51
|
+
shortcut: 'g',
|
|
52
|
+
label: 'CPU Detail',
|
|
53
|
+
},
|
|
54
|
+
memory: {
|
|
55
|
+
html: './pages/memory-detail.html',
|
|
56
|
+
shortcut: 'm',
|
|
57
|
+
label: 'Memory',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Press a shortcut key from the overview to jump to a detail page
|
|
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
|
|
67
|
+
|
|
68
|
+
Add shortcut badges to HTML panels with data attributes:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<div class="panel" data-tm-key="g" data-tm-label="CPU Detail">
|
|
72
|
+
<!-- panel content -->
|
|
73
|
+
</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Per-Page F-Key Bindings
|
|
77
|
+
|
|
78
|
+
Each page defines its own F-key actions:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
pages: {
|
|
82
|
+
overview: {
|
|
83
|
+
html: './pages/overview.html',
|
|
84
|
+
default: true,
|
|
85
|
+
keys: {
|
|
86
|
+
F5: { label: 'Refresh', action: async () => dash.render(await getData()) },
|
|
87
|
+
F10: { label: 'Quit', action: () => process.exit(0) },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The F-key bar at the bottom always reflects the active page's bindings.
|
|
94
|
+
|
|
95
|
+
## API Reference
|
|
96
|
+
|
|
97
|
+
### `tuimon.start(options: TuiMonOptions): Promise<TuiMonDashboard>`
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface TuiMonOptions {
|
|
101
|
+
pages: Record<string, PageConfig>
|
|
102
|
+
data?: () => Record<string, unknown> | Promise<Record<string, unknown>>
|
|
103
|
+
refresh?: number // auto-render interval in ms
|
|
104
|
+
renderDelay?: number // delay after pushData before screenshot (default: 50)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface PageConfig {
|
|
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
|
+
}
|
|
114
|
+
|
|
115
|
+
interface KeyBinding {
|
|
116
|
+
label: string
|
|
117
|
+
action: () => void | Promise<void>
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `dash.render(data): Promise<void>`
|
|
122
|
+
|
|
123
|
+
Push data to the current page and re-render.
|
|
124
|
+
|
|
125
|
+
### `dash.stop(): Promise<void>`
|
|
126
|
+
|
|
127
|
+
Gracefully shut down — restores terminal state.
|
|
128
|
+
|
|
129
|
+
## Client Library
|
|
130
|
+
|
|
131
|
+
In your HTML pages, use the injected `TuiMon` object:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// Listen for data updates
|
|
135
|
+
TuiMon.onUpdate(function(data) {
|
|
136
|
+
TuiMon.set('#cpu', data.cpu)
|
|
137
|
+
TuiMon.set('#memory', data.memory + '%')
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// TuiMon.set(selector, value) — sets text content or applies styles
|
|
141
|
+
// TuiMon.notify(message, duration) — dispatches a notification event
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
- **TDD required** — write tests first, implementation second
|
|
147
|
+
- Coverage thresholds: 80% lines, 80% functions, 75% branches
|
|
148
|
+
- `npm test` must pass before any PR
|
|
149
|
+
- Strict TypeScript — `strict: true`, no `any`
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
;(function () {
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
window.__tuimon_ready__ = false
|
|
5
|
+
|
|
6
|
+
window.__tuimon_update__ = function (data) {
|
|
7
|
+
window.__tuimon_ready__ = false
|
|
8
|
+
window.dispatchEvent(new CustomEvent('tuimon:update', { detail: data }))
|
|
9
|
+
window.__tuimon_ready__ = true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
window.TuiMon = {
|
|
13
|
+
onUpdate(callback) {
|
|
14
|
+
window.addEventListener('tuimon:update', function (e) { callback(e.detail) })
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
set(selector, value) {
|
|
18
|
+
var el = document.querySelector(selector)
|
|
19
|
+
if (!el) return
|
|
20
|
+
if (typeof value === 'string') el.textContent = value
|
|
21
|
+
else if (typeof value === 'number') el.textContent = value.toLocaleString()
|
|
22
|
+
else if (typeof value === 'object' && value !== null) Object.assign(el.style, value)
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
notify(message, duration) {
|
|
26
|
+
duration = duration || 2000
|
|
27
|
+
window.dispatchEvent(new CustomEvent('tuimon:notify', { detail: { message: message, duration: duration } }))
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Auto-add shortcut badges to panels with data-tm-key
|
|
32
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
33
|
+
document.querySelectorAll('[data-tm-key]').forEach(function (panel) {
|
|
34
|
+
var key = panel.getAttribute('data-tm-key')
|
|
35
|
+
var label = panel.getAttribute('data-tm-label') || ''
|
|
36
|
+
if (!key) return
|
|
37
|
+
var badge = document.createElement('div')
|
|
38
|
+
badge.style.cssText = 'position:absolute;top:8px;right:8px;background:rgba(0,0,0,0.6);color:#58a6ff;padding:2px 8px;border-radius:4px;font-size:12px;font-family:monospace;pointer-events:none;z-index:10;'
|
|
39
|
+
badge.textContent = '[' + key.toUpperCase() + ']' + (label ? ' ' + label : '')
|
|
40
|
+
panel.style.position = panel.style.position || 'relative'
|
|
41
|
+
panel.appendChild(badge)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
})()
|
|
@@ -0,0 +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,CA8DzB"}
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
export async function createBrowser({ url, width, height, }) {
|
|
3
|
+
const browser = await chromium.launch({ headless: true });
|
|
4
|
+
const context = await browser.newContext();
|
|
5
|
+
const page = await context.newPage();
|
|
6
|
+
await page.setViewportSize({ width, height });
|
|
7
|
+
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
8
|
+
let crashCount = 0;
|
|
9
|
+
page.on('crash', async () => {
|
|
10
|
+
crashCount++;
|
|
11
|
+
if (crashCount > 3) {
|
|
12
|
+
process.stderr.write('[tuimon] page crashed repeatedly — giving up on reload\n');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
process.stderr.write(`[tuimon] page crashed (attempt ${crashCount}/3), reloading\n`);
|
|
16
|
+
try {
|
|
17
|
+
await page.reload();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// ignore reload failures
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
page.on('pageerror', (err) => {
|
|
24
|
+
process.stderr.write(`tuimon: page error: ${err.message}\n`);
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
async screenshot() {
|
|
28
|
+
const buf = await page.screenshot({ type: 'png' });
|
|
29
|
+
return Buffer.from(buf);
|
|
30
|
+
},
|
|
31
|
+
async pushData(data) {
|
|
32
|
+
await page.evaluate((d) => {
|
|
33
|
+
const win = globalThis;
|
|
34
|
+
if (typeof win['__tuimon_update__'] === 'function') {
|
|
35
|
+
;
|
|
36
|
+
win['__tuimon_update__'](d);
|
|
37
|
+
}
|
|
38
|
+
}, data);
|
|
39
|
+
try {
|
|
40
|
+
await page.waitForFunction(() => globalThis['__tuimon_ready__'] === true, { timeout: 2000 });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// timeout is acceptable — page may not have the client script yet
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
async navigate(newUrl) {
|
|
47
|
+
await page.goto(newUrl, { waitUntil: 'domcontentloaded' });
|
|
48
|
+
},
|
|
49
|
+
async resize(w, h) {
|
|
50
|
+
await page.setViewportSize({ width: w, height: h });
|
|
51
|
+
},
|
|
52
|
+
async close() {
|
|
53
|
+
await browser.close();
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { existsSync, mkdirSync, cpSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { detectGraphicsSupport } from './detect.js';
|
|
7
|
+
process.on('unhandledRejection', (reason) => {
|
|
8
|
+
console.error('[tuimon] Unhandled rejection:', reason);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
11
|
+
process.on('uncaughtException', (error) => {
|
|
12
|
+
console.error('[tuimon] Uncaught exception:', error);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
});
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
17
|
+
const program = new Command();
|
|
18
|
+
program
|
|
19
|
+
.name('tuimon')
|
|
20
|
+
.description('Render beautiful HTML dashboards directly in your terminal.')
|
|
21
|
+
.version('0.1.0');
|
|
22
|
+
program
|
|
23
|
+
.command('start')
|
|
24
|
+
.description('Start a TuiMon dashboard')
|
|
25
|
+
.option('-c, --config <path>', 'Path to config file', 'tuimon.config.ts')
|
|
26
|
+
.action(async (opts) => {
|
|
27
|
+
const configPath = path.resolve(process.cwd(), opts.config);
|
|
28
|
+
if (!existsSync(configPath)) {
|
|
29
|
+
console.error(`[tuimon] Config file not found: ${configPath}`);
|
|
30
|
+
console.error('[tuimon] Run "tuimon init" to create a starter project.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
// Dynamic import of the config — it self-starts
|
|
34
|
+
await import(configPath);
|
|
35
|
+
});
|
|
36
|
+
program
|
|
37
|
+
.command('init')
|
|
38
|
+
.description('Scaffold a starter TuiMon project in the current directory')
|
|
39
|
+
.action(() => {
|
|
40
|
+
const starterDir = path.resolve(pkgRoot, 'templates', 'starter');
|
|
41
|
+
const targetDir = process.cwd();
|
|
42
|
+
const pagesDir = path.join(targetDir, 'pages');
|
|
43
|
+
const configFile = path.join(targetDir, 'tuimon.config.ts');
|
|
44
|
+
if (existsSync(configFile)) {
|
|
45
|
+
console.error('[tuimon] tuimon.config.ts already exists. Aborting.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
mkdirSync(pagesDir, { recursive: true });
|
|
49
|
+
cpSync(path.join(starterDir, 'pages'), pagesDir, { recursive: true });
|
|
50
|
+
cpSync(path.join(starterDir, 'tuimon.config.ts'), configFile);
|
|
51
|
+
console.log('TuiMon project initialized!');
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(' Files created:');
|
|
54
|
+
console.log(' pages/overview.html');
|
|
55
|
+
console.log(' pages/cpu-detail.html');
|
|
56
|
+
console.log(' pages/memory-detail.html');
|
|
57
|
+
console.log(' tuimon.config.ts');
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(' Next steps:');
|
|
60
|
+
console.log(' npx tuimon start');
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command('check')
|
|
64
|
+
.description('Check terminal graphics support')
|
|
65
|
+
.action(async () => {
|
|
66
|
+
const support = await detectGraphicsSupport();
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log('TuiMon Terminal Check');
|
|
69
|
+
console.log('\u2500'.repeat(21));
|
|
70
|
+
console.log(`Kitty protocol: ${support.kitty ? '\u2713 supported' : '\u2717 not detected'}`);
|
|
71
|
+
console.log(`Sixel protocol: ${support.sixel ? '\u2713 supported' : '\u2717 not detected'}`);
|
|
72
|
+
console.log(`iTerm2 protocol: ${support.iterm2 ? '\u2713 supported' : '\u2717 not detected'}`);
|
|
73
|
+
console.log('');
|
|
74
|
+
if (support.protocol) {
|
|
75
|
+
console.log(`\u2713 Will use: ${support.protocol}`);
|
|
76
|
+
const term = process.env['TERM_PROGRAM'] ?? process.env['TERM'] ?? 'unknown';
|
|
77
|
+
console.log(` ${term}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log('\u2717 No supported graphics protocol detected.');
|
|
81
|
+
console.log(' TuiMon requires Kitty, iTerm2, or Sixel graphics support.');
|
|
82
|
+
}
|
|
83
|
+
console.log('');
|
|
84
|
+
process.exit(support.protocol ? 0 : 1);
|
|
85
|
+
});
|
|
86
|
+
program.parse();
|
|
87
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GraphicsSupport, TerminalDimensions } from './types.js';
|
|
2
|
+
export declare function detectGraphicsSupport(opts?: {
|
|
3
|
+
skipQuery?: boolean;
|
|
4
|
+
}): Promise<GraphicsSupport>;
|
|
5
|
+
export declare function getTerminalDimensions(opts?: {
|
|
6
|
+
skipQuery?: boolean;
|
|
7
|
+
}): Promise<TerminalDimensions>;
|
|
8
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAwGrE,wBAAsB,qBAAqB,CACzC,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAED,wBAAsB,qBAAqB,CACzC,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAqB7B"}
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// ─── Env-based detection helpers ─────────────────────────────────────────────
|
|
2
|
+
const KITTY_TERM_PROGRAMS = ['WezTerm', 'ghostty', 'Ghostty'];
|
|
3
|
+
const ITERM2_TERM_PROGRAMS = ['iTerm.app', 'iTerm2'];
|
|
4
|
+
function detectKittyEnv() {
|
|
5
|
+
const term = process.env.TERM ?? '';
|
|
6
|
+
const termProgram = process.env.TERM_PROGRAM ?? '';
|
|
7
|
+
if (term === 'xterm-kitty')
|
|
8
|
+
return true;
|
|
9
|
+
if (KITTY_TERM_PROGRAMS.includes(termProgram))
|
|
10
|
+
return true;
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
function detectIterm2Env() {
|
|
14
|
+
const termProgram = process.env.TERM_PROGRAM ?? '';
|
|
15
|
+
return ITERM2_TERM_PROGRAMS.includes(termProgram);
|
|
16
|
+
}
|
|
17
|
+
function detectSixelEnv() {
|
|
18
|
+
const term = process.env.TERM ?? '';
|
|
19
|
+
const termProgram = process.env.TERM_PROGRAM ?? '';
|
|
20
|
+
if (term === 'mlterm')
|
|
21
|
+
return true;
|
|
22
|
+
if (termProgram === 'vscode')
|
|
23
|
+
return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
// ─── Terminal query helpers ──────────────────────────────────────────────────
|
|
27
|
+
let queryLock = Promise.resolve();
|
|
28
|
+
function queryKittySupport() {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const timeout = setTimeout(() => {
|
|
31
|
+
cleanup();
|
|
32
|
+
resolve(false);
|
|
33
|
+
}, 500);
|
|
34
|
+
const onData = (data) => {
|
|
35
|
+
const str = data.toString();
|
|
36
|
+
if (str.includes('_G')) {
|
|
37
|
+
cleanup();
|
|
38
|
+
resolve(true);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
process.stdin.removeListener('data', onData);
|
|
44
|
+
if (wasRaw !== undefined) {
|
|
45
|
+
try {
|
|
46
|
+
process.stdin.setRawMode(wasRaw);
|
|
47
|
+
}
|
|
48
|
+
catch { }
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
let wasRaw;
|
|
52
|
+
try {
|
|
53
|
+
wasRaw = process.stdin.isRaw;
|
|
54
|
+
process.stdin.setRawMode(true);
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
process.stdin.on('data', onData);
|
|
58
|
+
process.stdout.write('\x1b_Ga=q;\x1b\\');
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function queryPixelDimensions() {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
const timeout = setTimeout(() => {
|
|
64
|
+
cleanup();
|
|
65
|
+
resolve(null);
|
|
66
|
+
}, 500);
|
|
67
|
+
const onData = (data) => {
|
|
68
|
+
const str = data.toString();
|
|
69
|
+
// Response format: \x1b[4;<height>;<width>t
|
|
70
|
+
const match = str.match(/\x1b\[4;(\d+);(\d+)t/);
|
|
71
|
+
if (match) {
|
|
72
|
+
cleanup();
|
|
73
|
+
resolve({ width: parseInt(match[2] ?? '0', 10), height: parseInt(match[1] ?? '0', 10) });
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const cleanup = () => {
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
process.stdin.removeListener('data', onData);
|
|
79
|
+
if (wasRaw !== undefined) {
|
|
80
|
+
try {
|
|
81
|
+
process.stdin.setRawMode(wasRaw);
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
let wasRaw;
|
|
87
|
+
try {
|
|
88
|
+
wasRaw = process.stdin.isRaw;
|
|
89
|
+
process.stdin.setRawMode(true);
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
process.stdin.on('data', onData);
|
|
93
|
+
process.stdout.write('\x1b[14t');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
97
|
+
export async function detectGraphicsSupport(opts) {
|
|
98
|
+
const skipQuery = opts?.skipQuery ?? false;
|
|
99
|
+
// Start with environment-based detection
|
|
100
|
+
let kitty = detectKittyEnv();
|
|
101
|
+
const iterm2 = detectIterm2Env();
|
|
102
|
+
const sixel = detectSixelEnv();
|
|
103
|
+
// If not skipping queries and env didn't detect kitty, try querying (serialized)
|
|
104
|
+
if (!skipQuery && !kitty) {
|
|
105
|
+
await queryLock;
|
|
106
|
+
const p = queryKittySupport();
|
|
107
|
+
queryLock = p.catch(() => { });
|
|
108
|
+
kitty = await p;
|
|
109
|
+
}
|
|
110
|
+
// Determine protocol with priority: kitty > iterm2 > sixel
|
|
111
|
+
let protocol = null;
|
|
112
|
+
if (kitty) {
|
|
113
|
+
protocol = 'kitty';
|
|
114
|
+
}
|
|
115
|
+
else if (iterm2) {
|
|
116
|
+
protocol = 'iterm2';
|
|
117
|
+
}
|
|
118
|
+
else if (sixel) {
|
|
119
|
+
protocol = 'sixel';
|
|
120
|
+
}
|
|
121
|
+
return { kitty, sixel, iterm2, protocol };
|
|
122
|
+
}
|
|
123
|
+
export async function getTerminalDimensions(opts) {
|
|
124
|
+
const skipQuery = opts?.skipQuery ?? false;
|
|
125
|
+
const cols = process.stdout.columns ?? 80;
|
|
126
|
+
const rows = process.stdout.rows ?? 24;
|
|
127
|
+
let pixelWidth = 1600;
|
|
128
|
+
let pixelHeight = 900;
|
|
129
|
+
if (!skipQuery) {
|
|
130
|
+
await queryLock;
|
|
131
|
+
const p = queryPixelDimensions();
|
|
132
|
+
queryLock = p.catch(() => { });
|
|
133
|
+
const queried = await p;
|
|
134
|
+
if (queried) {
|
|
135
|
+
pixelWidth = queried.width;
|
|
136
|
+
pixelHeight = queried.height;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { cols, rows, pixelWidth, pixelHeight };
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../src/encoder.ts"],"names":[],"mappings":"AAKA,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,GACpC,OAAO,CAAC,IAAI,CAAC,CAMf"}
|